home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
HAM Radio 3
/
hamradioversion3.0examsandprograms1992.iso
/
packet
/
n17jsrc
/
ftpcli.c
< prev
next >
Wrap
C/C++ Source or Header
|
1991-08-22
|
27KB
|
1,141 lines
/* Internet FTP client (interactive user)
* Copyright 1991 Phil Karn, KA9Q
*/
/* Mods by G1EMM and PA0GRI */
#include <stdio.h>
#include "global.h"
#include "mbuf.h"
#include "session.h"
#include "cmdparse.h"
#include "proc.h"
#include "tty.h"
#include "socket.h"
#include "ftp.h"
#include "ftpcli.h"
#include "commands.h"
#include "netuser.h"
#include "dirutil.h"
#define DIRBUF 256
static int doascii __ARGS((int argc,char *argv[],void *p));
static int dobatch __ARGS((int argc,char *argv[],void *p));
static int dobinary __ARGS((int argc,char *argv[],void *p));
static int doftpcd __ARGS((int argc,char *argv[],void *p));
static int doget __ARGS((int argc,char *argv[],void *p));
static int dohash __ARGS((int argc,char *argv[],void *p));
static int doverbose __ARGS((int argc,char *argv[],void *p));
static int dolist __ARGS((int argc,char *argv[],void *p));
static int dols __ARGS((int argc,char *argv[],void *p));
static int domkdir __ARGS((int argc,char *argv[],void *p));
static int domget __ARGS((int argc,char *argv[],void *p));
static int domput __ARGS((int argc,char *argv[],void *p));
static int doput __ARGS((int argc,char *argv[],void *p));
static int doquit __ARGS((int argc,char *argv[],void *p));
static int dormdir __ARGS((int argc,char *argv[],void *p));
static int dotype __ARGS((int argc,char *argv[],void *p));
static int getline __ARGS((struct session *sp,char *prompt,char *buf,int n));
static int getresp __ARGS((struct ftpcli *ftp,int mincode));
static long getsub __ARGS((struct ftpcli *ftp,char *command,char *remotename,
char *localname));
static long putsub __ARGS((struct ftpcli *ftp,char *remotename,char *localname));
static void sendport __ARGS((int s,struct sockaddr_in *socket));
static char Notsess[] = "Not an FTP session!\n";
static int Ftp_type = ASCII_TYPE;
static int Ftp_logbsize = 8;
static struct cmds Ftpcmds[] = {
"", donothing, 0, 0, NULLCHAR,
"ascii", doascii, 0, 0, NULLCHAR,
"batch", dobatch, 0, 0, NULLCHAR,
"binary", dobinary, 0, 0, NULLCHAR,
"cd", doftpcd, 0, 2, "cd <directory>",
"dir", dolist, 0, 0, NULLCHAR,
"list", dolist, 0, 0, NULLCHAR,
"get", doget, 0, 2, "get <remotefile> <localfile>",
"hash", dohash, 0, 0, NULLCHAR,
"ls", dols, 0, 0, NULLCHAR,
"mget", domget, 0, 2, "mget <file> [<file> ...]",
"mkdir", domkdir, 0, 2, "mkdir <directory>",
"mput", domput, 0, 2, "mput <file> [<file> ...]",
"nlst", dols, 0, 0, NULLCHAR,
"quit", doquit, 0, 0, NULLCHAR,
"rmdir", dormdir, 0, 2, "rmdir <directory>",
"put", doput, 0, 2, "put <localfile> <remotefile>",
"type", dotype, 0, 0, NULLCHAR,
"verbose", doverbose, 0, 0, NULLCHAR,
NULLCHAR, NULLFP, 0, 0, NULLCHAR,
};
/* Handle top-level FTP command */
int
doftp(argc,argv,p)
int argc;
char *argv[];
void *p;
{
struct session *sp;
struct ftpcli ftp;
struct sockaddr_in fsocket;
int resp,vsave;
char *buf,*bufsav,*cp,*un;
char prmt[40];
int control;
char *ftpcli_login();
/* Allocate a session control block */
if((sp = newsession(argv[1],FTP,0)) == NULLSESSION){
tprintf("Too many sessions\n");
return 1;
}
memset((char *)&ftp,0,sizeof(ftp));
ftp.control = ftp.data = -1;
ftp.verbose = V_NORMAL;
ftp.type = Ftp_type;
ftp.logbsize = Ftp_logbsize;
sp->cb.ftp = &ftp; /* Downward link */
ftp.session = sp; /* Upward link */
fsocket.sin_family = AF_INET;
if(argc < 3)
fsocket.sin_port = IPPORT_FTP;
else
fsocket.sin_port = atoi(argv[2]);
tprintf("Resolving %s... ",sp->name);
if((fsocket.sin_addr.s_addr = resolve(sp->name)) == 0){
tprintf(Badhost,sp->name);
keywait(NULLCHAR,1);
freesession(sp);
return 1;
}
/* Open the control connection */
if((control = sp->s = ftp.control = socket(AF_INET,SOCK_STREAM,0)) == -1){
tprintf("Can't create socket\n");
keywait(NULLCHAR,1);
freesession(sp);
return 1;
}
sockmode(sp->s,SOCK_ASCII);
setflush(sp->s,-1); /* Flush output only when we call getresp() */
tprintf("Trying %s...\n",psocket((struct sockaddr *)&fsocket));
if(connect(control,(char *)&fsocket,sizeof(fsocket)) == -1)
goto quit;
tprintf("FTP session %u connected to %s\n",(unsigned)(sp-Sessions),
sp->name);
/* Wait for greeting from server */
resp = getresp(&ftp,200);
if(resp >= 400)
goto quit;
un = getenv("USER");
if(un != NULLCHAR)
sprintf(prmt,"Enter user name (%s): ",un);
else
sprintf(prmt,"Enter user name: ");
/* Now process responses and commands */
buf = mallocw(LINELEN);
while(resp != -1){
if(resp == 220){
/* Sign-on banner; prompt for and send USER command */
if((cp = ftpcli_login(&ftp, sp->name)) == NULLCHAR){
#ifndef notdef
getline(sp,prmt,buf,LINELEN);
#else
getline(sp,"Enter user name: ",buf,LINELEN);
#endif
/* Send the command only if the user response
* was non-null
*/
if(buf[0] != '\n'){
usprintf(control,"USER %s",buf);
resp = getresp(&ftp,200);
} else {
if(un != NULLCHAR){
usprintf(control,"USER %s\n",un);
resp = getresp(&ftp,200);
} else {
tprintf("No username sent\n");
resp = 200; /* dummy */
}
}
} else {
usprintf(control,"USER %s\n",cp);
free(cp);
resp = getresp(&ftp,200);
}
} else if(resp == 331){
if(ftp.password == NULLCHAR){
/* turn off echo */
sp->ttystate.echo = 0;
getline(sp,"Password: ",buf,LINELEN);
tprintf("\n");
/* Turn echo back on */
sp->ttystate.echo = 1;
/* Send the command only if the user response
* was non-null
*/
if(buf[0] != '\n'){
usprintf(control,"PASS %s",buf);
resp = getresp(&ftp,200);
} else {
tprintf("Password must be provided.\nLoggin failed.\n");
resp = 200; /* dummy */
}
} else {
usprintf(control,"PASS %s\n",ftp.password);
resp = getresp(&ftp,200);
free(ftp.password);
ftp.password = NULLCHAR; /* clean up */
}
} else {
/* Test the control channel first */
if(sockstate(control) == NULLCHAR)
break;
getline(sp,"ftp> ",buf,LINELEN);
/* Copy because cmdparse modifies the original */
bufsav = strdup(buf);
if((resp = cmdparse(Ftpcmds,buf,&ftp)) != -1){
/* Valid command, free buffer and get another */
free(bufsav);
} else {
/* Not a local cmd, send to remote server */
usputs(control,bufsav);
free(bufsav);
/* Enable display of server response */
vsave = ftp.verbose;
ftp.verbose = V_NORMAL;
resp = getresp(&ftp,200);
ftp.verbose = vsave;
}
}
}
free(buf);
quit: cp = sockerr(control);
tprintf("FTP session %u closed: %s\n",(unsigned)(sp - Sessions),
cp != NULLCHAR ? cp : "EOF");
if(ftp.fp != NULLFILE && ftp.fp != stdout)
fclose(ftp.fp);
if(ftp.data != -1)
close_s(ftp.data);
if(ftp.control != -1)
close_s(ftp.control);
keywait(NULLCHAR,1);
if(ftp.session != NULLSESSION)
freesession(ftp.session);
return 0;
}
/* Control verbosity level */
static int
doverbose(argc,argv,p)
int argc;
char *argv[];
void *p;
{
register struct ftpcli *ftp;
if((ftp = (struct ftpcli *)p) == NULLFTP)
return -1;
return setshort(&ftp->verbose,"Verbose",argc,argv);
}
/* Enable/disable command batching */
static int
dobatch(argc,argv,p)
int argc;
char *argv[];
void *p;
{
register struct ftpcli *ftp;
if((ftp = (struct ftpcli *)p) == NULLFTP)
return -1;
return setbool(&ftp->batch,"Command batching",argc,argv);
}
/* Set verbosity to high (convenience command) */
static int
dohash(argc,argv,p)
int argc;
char *argv[];
void *p;
{
register struct ftpcli *ftp;
if((ftp = (struct ftpcli *)p) == NULLFTP)
return -1;
ftp->verbose = V_HASH;
return 0;
}
/* Close session */
static int
doquit(argc,argv,p)
int argc;
char *argv[];
void *p;
{
register struct ftpcli *ftp;
ftp = (struct ftpcli *)p;
if(ftp == NULLFTP)
return -1;
usprintf(ftp->control,"QUIT\n");
getresp(ftp,200); /* Get the closing message */
getresp(ftp,200); /* Wait for the server to close */
return -1;
}
/* Translate 'cd' to 'cwd' for convenience */
static int
doftpcd(argc,argv,p)
int argc;
char *argv[];
void *p;
{
register struct ftpcli *ftp;
ftp = (struct ftpcli *)p;
if(ftp == NULLFTP)
return -1;
usprintf(ftp->control,"CWD %s\n",argv[1]);
return getresp(ftp,200);
}
/* Translate 'mkdir' to 'xmkd' for convenience */
static int
domkdir(argc,argv,p)
int argc;
char *argv[];
void *p;
{
register struct ftpcli *ftp;
ftp = (struct ftpcli *)p;
if(ftp == NULLFTP)
return -1;
usprintf(ftp->control,"XMKD %s\n",argv[1]);
return getresp(ftp,200);
}
/* Translate 'rmdir' to 'xrmd' for convenience */
static int
dormdir(argc,argv,p)
int argc;
char *argv[];
void *p;
{
register struct ftpcli *ftp;
ftp = (struct ftpcli *)p;
if(ftp == NULLFTP)
return -1;
usprintf(ftp->control,"XRMD %s\n",argv[1]);
return getresp(ftp,200);
}
static int
dobinary(argc,argv,p)
int argc;
char *argv[];
void *p;
{
char *args[2];
args[1] = "I";
return dotype(2,args,p);
}
static int
doascii(argc,argv,p)
int argc;
char *argv[];
void *p;
{
char *args[2];
args[1] = "A";
return dotype(2,args,p);
}
/* Handle "type" command from user */
static int
dotype(argc,argv,p)
int argc;
char *argv[];
void *p;
{
register struct ftpcli *ftp;
ftp = (struct ftpcli *)p;
if(ftp == NULLFTP)
return -1;
if(argc < 2){
switch(ftp->type){
case IMAGE_TYPE:
tprintf("Image\n");
break;
case ASCII_TYPE:
tprintf("Ascii\n");
break;
case LOGICAL_TYPE:
tprintf("Logical bytesize %u\n",ftp->logbsize);
break;
}
return 0;
}
switch(*argv[1]){
case 'i':
case 'I':
case 'b':
case 'B':
ftp->type = IMAGE_TYPE;
break;
case 'a':
case 'A':
ftp->type = ASCII_TYPE;
break;
case 'L':
case 'l':
ftp->type = LOGICAL_TYPE;
ftp->logbsize = atoi(argv[2]);
break;
default:
tprintf("Invalid type %s\n",argv[1]);
return 1;
}
return 0;
}
/* Handle "ftype" command */
int
doftype(argc,argv,p)
int argc;
char *argv[];
void *p;
{
if(argc < 2){
tprintf("Ftp initial TYPE is ");
switch(Ftp_type){
case IMAGE_TYPE:
tprintf("Image\n");
break;
case ASCII_TYPE:
tprintf("Ascii\n");
break;
case LOGICAL_TYPE:
tprintf("Logical bytesize %u\n",Ftp_logbsize);
break;
}
return 0;
}
switch(*argv[1]){
case 'i':
case 'I':
case 'b':
case 'B':
Ftp_type = IMAGE_TYPE;
break;
case 'a':
case 'A':
Ftp_type = ASCII_TYPE;
break;
case 'L':
case 'l':
Ftp_type = LOGICAL_TYPE;
Ftp_logbsize = atoi(argv[2]);
break;
default:
tprintf("Invalid type %s\n",argv[1]);
return 1;
}
return 0;
}
/* Start receive transfer. Syntax: get <remote name> [<local name>] */
static int
doget(argc,argv,p)
int argc;
char *argv[];
void *p;
{
char *remotename,*localname;
register struct ftpcli *ftp;
ftp = (struct ftpcli *)p;
if(ftp == NULLFTP){
tprintf(Notsess);
return 1;
}
remotename = argv[1];
if(argc < 3)
localname = remotename;
else
localname = argv[2];
getsub(ftp,"RETR",remotename,localname);
return 0;
}
/* Get a collection of files */
static int
domget(argc,argv,p)
int argc;
char *argv[];
void *p;
{
register struct ftpcli *ftp;
FILE *files, *filel;
char tmpname[80];
char *buf, *local, *c;
int i, inlist;
long r;
if((ftp = (struct ftpcli *)p) == NULLFTP){
tprintf(Notsess);
return 1;
}
tmpnam(tmpname);
buf = mallocw(DIRBUF);
ftp->state = RECEIVING_STATE;
for(i=1;i<argc;i++){
if(argv[i][0] == '@'){
inlist = 1;
if((filel = fopen(&argv[i][1], "r")) == NULLFILE){
tprintf("Can't open listfile: %s\n", &argv[i][1]);
continue;
}
if((files = fopen(tmpname, "w")) == NULLFILE){
tprintf("Can't open tempfile: %s\n", tmpname);
continue;
}
while(fgets(buf,DIRBUF,filel) != NULLCHAR){
fputs(buf,files);
}
fclose(files);
fclose(filel);
if((files = fopen(tmpname, "r")) == NULLFILE){
tprintf("Can't open tempfile: %s\n", tmpname);
continue;
}
} else {
inlist = 0;
r = getsub(ftp,"NLST",argv[i],tmpname);
if(ftp->abort)
break; /* Aborted */
if(r == -1 || (files = fopen(tmpname,"r")) == NULLFILE){
tprintf("Can't NLST %s\n",argv[i]);
unlink(tmpname);
continue;
}
}
/* The tmp file now contains a list of the remote files, so
* go get 'em. Break out if the user signals an abort.
*/
while(fgets(buf,DIRBUF,files) != NULLCHAR){
rip(buf);
local = strdup(buf);
#ifdef MSDOS
if(inlist){
strrev(local);
strtok(local, "\\/[]<>,?#~()&%");
strrev(local);
}
if((c = strstr(local, ".")) != NULLCHAR) {
c++;
c = strtok(c, "."); /* remove 2nd period if any*/
}
#endif
getsub(ftp,"RETR",buf,local);
free(local);
if(ftp->abort){
/* User abort */
ftp->abort = 0;
fclose(files);
unlink(tmpname);
free(buf);
ftp->state = COMMAND_STATE;
return 1;
}
}
fclose(files);
unlink(tmpname);
}
free(buf);
ftp->state = COMMAND_STATE;
ftp->abort = 0;
return 0;
}
/* List remote directory. Syntax: dir <remote files> [<local name>] */
static int
dolist(argc,argv,p)
int argc;
char *argv[];
void *p;
{
char *remotename,*localname;
register struct ftpcli *ftp;
ftp = (struct ftpcli *)p;
if(ftp == NULLFTP){
tprintf(Notsess);
return 1;
}
remotename = argv[1];
if(argc > 2)
localname = argv[2];
else
localname = NULLCHAR;
getsub(ftp,"LIST",remotename,localname);
return 0;
}
/* Remote directory list, short form. Syntax: ls <remote files> [<local name>] */
static int
dols(argc,argv,p)
int argc;
char *argv[];
void *p;
{
char *remotename,*localname;
register struct ftpcli *ftp;
ftp = (struct ftpcli *)p;
if(ftp == NULLFTP){
tprintf(Notsess);
return 1;
}
remotename = argv[1];
if(argc > 2)
localname = argv[2];
else
localname = NULLCHAR;
getsub(ftp,"NLST",remotename,localname);
return 0;
}
/* Common code to LIST/NLST/RETR and mget
* Returns number of bytes received if successful
* Returns -1 on error
*/
static long
getsub(ftp,command,remotename,localname)
register struct ftpcli *ftp;
char *command,*remotename,*localname;
{
unsigned long total;
FILE *fp;
int cnt,resp,i,control,savmode;
char *mode;
struct sockaddr_in lsocket;
struct sockaddr_in lcsocket;
int32 startclk,rate;
int vsave;
int typewait = 0;
int prevstate;
if(ftp == NULLFTP)
return -1;
control = ftp->control;
savmode = ftp->type;
switch(ftp->type){
case IMAGE_TYPE:
case LOGICAL_TYPE:
mode = WRITE_BINARY;
break;
case ASCII_TYPE:
mode = WRITE_TEXT;
break;
}
/* Open the file */
if(localname == NULLCHAR){
fp = NULLFILE;
} else if((fp = fopen(localname,mode)) == NULLFILE){
tprintf("Can't write %s: %s\n",localname,sys_errlist[errno]);
return -1;
}
/* Open the data connection */
ftp->data = socket(AF_INET,SOCK_STREAM,0);
listen(ftp->data,0); /* Accept only one connection */
prevstate = ftp->state;
ftp->state = RECEIVING_STATE;
/* Send TYPE message, if necessary */
if(strcmp(command,"LIST") == 0 || strcmp(command,"NLST") == 0){
/* Directory listings are always in ASCII */
ftp->type = ASCII_TYPE;
}
if(ftp->typesent != ftp->type){
switch(ftp->type){
case ASCII_TYPE:
usprintf(control,"TYPE A\n");
break;
case IMAGE_TYPE:
usprintf(control,"TYPE I\n");
break;
case LOGICAL_TYPE:
usprintf(control,"TYPE L %d\n",ftp->logbsize);
break;
}
ftp->typesent = ftp->type;
if(!ftp->batch){
resp = getresp(ftp,200);
if(resp == -1 || resp > 299)
goto failure;
} else
typewait = 1;
}
/* Send the PORT message. Use the IP address
* on the local end of our control connection.
*/
i = SOCKSIZE;
getsockname(ftp->data,(char *)&lsocket,&i); /* Get port number */
i = SOCKSIZE;
getsockname(ftp->control,(char *)&lcsocket,&i);
lsocket.sin_addr.s_addr = lcsocket.sin_addr.s_addr;
sendport(control,&lsocket);
if(!ftp->batch){
/* Get response to PORT command */
resp = getresp(ftp,200);
if(resp == -1 || resp > 299)
goto failure;
}
/* Generate the command to start the transfer */
if(remotename != NULLCHAR)
usprintf(control,"%s %s\n",command,remotename);
else
usprintf(control,"%s\n",command);
if(ftp->batch){
/* Get response to TYPE command, if sent */
if(typewait){
resp = getresp(ftp,200);
if(resp == -1 || resp > 299)
goto failure;
}
/* Get response to PORT command */
resp = getresp(ftp,200);
if(resp == -1 || resp > 299)
goto failure;
}
/* Get the intermediate "150" response */
resp = getresp(ftp,100);
if(resp == -1 || resp >= 400)
goto failure;
/* Wait for the server to open the data connection */
cnt = 0;
ftp->data = accept(ftp->data,NULLCHAR,&cnt);
startclk = msclock();
/* If output is to the screen, temporarily disable hash marking */
vsave = ftp->verbose;
if(vsave >= V_HASH && fp == NULLFILE)
ftp->verbose = V_NORMAL;
total = recvfile(fp,ftp->data,ftp->type,(ftp->verbose >= V_HASH) ? ftp->verbose : 0);
/* Immediately close the data connection; some servers (e.g., TOPS-10)
* wait for the data connection to close completely before returning
* the completion message on the control channel
*/
close_s(ftp->data);
ftp->data = -1;
#ifdef CPM
if(fp != NULLFILE && ftp->type == ASCII_TYPE)
putc(CTLZ,fp);
#endif
if(fp != NULLFILE && fp != stdout)
fclose(fp);
startclk = msclock() - startclk;
if(startclk != 0)
rate = (total*1000)/startclk;
else
rate = 0;
if(remotename == NULLCHAR)
remotename = "";
if(total == -1){
tprintf("%s %s: Error/abort during data transfer\n",command,remotename);
} else if(ftp->verbose >= V_SHORT){
tprintf("%s %s: %lu bytes in %lu sec (%lu/sec)\n",
command,remotename, total,startclk/1000,rate);
}
/* Get the "Sent" message */
getresp(ftp,200);
ftp->state = prevstate;
ftp->verbose = vsave;
ftp->type = savmode;
return total;
failure:
/* Error, quit */
if(fp != NULLFILE && fp != stdout)
fclose(fp);
close_s(ftp->data);
ftp->data = -1;
ftp->state = prevstate;
ftp->type = savmode;
return -1;
}
/* Send a file. Syntax: put <local name> [<remote name>] */
static int
doput(argc,argv,p)
int argc;
char *argv[];
void *p;
{
register struct ftpcli *ftp;
char *remotename,*localname;
if((ftp = (struct ftpcli *)p) == NULLFTP){
tprintf(Notsess);
return 1;
}
localname = argv[1];
if(argc < 3)
remotename = localname;
else
remotename = argv[2];
putsub(ftp,remotename,localname);
return 0;
}
/* Put a collection of files */
static int
domput(argc,argv,p)
int argc;
char *argv[];
void *p;
{
register struct ftpcli *ftp;
FILE *files;
int i;
char tmpname[80];
char *buf;
if((ftp = (struct ftpcli *)p) == NULLFTP){
tprintf(Notsess);
return 1;
}
tmpnam(tmpname);
if((files = fopen(tmpname,"w+")) == NULLFILE){
tprintf("Can't list local files\n");
unlink(tmpname);
return 1;
}
for(i=1;i<argc;i++)
getdir(argv[i],0,files);
rewind(files);
buf = mallocw(DIRBUF);
ftp->state = SENDING_STATE;
while(fgets(buf,DIRBUF,files) != NULLCHAR){
rip(buf);
putsub(ftp,buf,buf);
if(ftp->abort)
break; /* User abort */
}
fclose(files);
unlink(tmpname);
free(buf);
ftp->state = COMMAND_STATE;
ftp->abort = 0;
return 0;
}
/* Common code to put, mput.
* Returns number of bytes sent if successful
* Returns -1 on error
*/
static long
putsub(ftp,remotename,localname)
register struct ftpcli *ftp;
char *remotename,*localname;
{
char *mode;
int i,resp,control;
unsigned long total;
FILE *fp;
struct sockaddr_in lsocket,lcsocket;
int32 startclk,rate;
int typewait = 0;
int prevstate;
control = ftp->control;
if(ftp->type == IMAGE_TYPE)
mode = READ_BINARY;
else
mode = READ_TEXT;
/* Open the file */
if((fp = fopen(localname,mode)) == NULLFILE){
tprintf("Can't read %s: %s\n",localname,sys_errlist[errno]);
return -1;
}
if(ftp->type == ASCII_TYPE && isbinary(fp)){
tprintf("Warning: type is ASCII and %s appears to be binary\n",localname);
}
/* Open the data connection */
ftp->data = socket(AF_INET,SOCK_STREAM,0);
listen(ftp->data,0);
prevstate = ftp->state;
ftp->state = SENDING_STATE;
/* Send TYPE message, if necessary */
if(ftp->typesent != ftp->type){
switch(ftp->type){
case ASCII_TYPE:
usprintf(control,"TYPE A\n");
break;
case IMAGE_TYPE:
usprintf(control,"TYPE I\n");
break;
case LOGICAL_TYPE:
usprintf(control,"TYPE L %d\n",ftp->logbsize);
break;
}
ftp->typesent = ftp->type;
/* Get response to TYPE command */
if(!ftp->batch){
resp = getresp(ftp,200);
if(resp == -1 || resp > 299){
goto failure;
}
} else
typewait = 1;
}
/* Send the PORT message. Use the IP address
* on the local end of our control connection.
*/
i = SOCKSIZE;
getsockname(ftp->data,(char *)&lsocket,&i);
i = SOCKSIZE;
getsockname(ftp->control,(char *)&lcsocket,&i);
lsocket.sin_addr.s_addr = lcsocket.sin_addr.s_addr;
sendport(control,&lsocket);
if(!ftp->batch){
/* Get response to PORT command */
resp = getresp(ftp,200);
if(resp == -1 || resp > 299){
goto failure;
}
}
/* Generate the command to start the transfer */
usprintf(control,"STOR %s\n",remotename);
if(ftp->batch){
/* Get response to TYPE command, if sent */
if(typewait){
resp = getresp(ftp,200);
if(resp == -1 || resp > 299){
goto failure;
}
}
/* Get response to PORT command */
resp = getresp(ftp,200);
if(resp == -1 || resp > 299){
goto failure;
}
}
/* Get the intermediate "150" response */
resp = getresp(ftp,100);
if(resp == -1 || resp >= 400){
goto failure;
}
/* Wait for the data connection to open. Otherwise the first
* block of data would go out with the SYN, and this may confuse
* some other TCPs
*/
accept(ftp->data,NULLCHAR,(int *)NULL);
startclk = msclock();
total = sendfile(fp,ftp->data,ftp->type,(ftp->verbose >= V_HASH) ? ftp->verbose : 0);
close_s(ftp->data);
ftp->data = -1;
fclose(fp);
startclk = msclock() - startclk;
if(startclk != 0)
rate = (total*1000)/startclk;
else
rate = 0;
if(total == -1){
tprintf("STOR %s: Error/abort during data transfer\n",remotename);
} else if(ftp->verbose >= V_SHORT){
tprintf("STOR %s: %lu bytes in %lu sec (%lu/sec)\n",
remotename,total,startclk/1000,rate);
}
getresp(ftp,200);
ftp->state = prevstate;
return total;
failure:
/* Error, quit */
fclose(fp);
close_s(ftp->data);
ftp->data = -1;
ftp->state = prevstate;
return -1;
}
/* Abort a GET or PUT operation in progress. Note: this will leave
* the partial file on the local or remote system
*/
int
doabort(argc,argv,p)
int argc;
char *argv[];
void *p;
{
register struct session *sp;
register struct ftpcli *ftp;
sp = (struct session *)p;
if(sp == NULLSESSION)
return -1;
/* Default is the current session, but it can be overridden with
* an argument.
*/
if(argc > 1)
sp = sessptr(argv[1]);
if(sp == NULLSESSION || sp->type != FTP){
tprintf("Not an active FTP session\n");
return 1;
}
ftp = sp->cb.ftp;
switch(ftp->state){
case COMMAND_STATE:
tprintf("No active transfer\n");
return 1;
case SENDING_STATE:
/* Send a premature EOF.
* Unfortunately we can't just reset the connection
* since the remote side might end up waiting forever
* for us to send something.
*/
shutdown(ftp->data,1); /* Note fall-thru */
ftp->abort = 1;
break;
case RECEIVING_STATE:
/* Just blow away the receive socket */
shutdown(ftp->data,2); /* Note fall-thru */
ftp->abort = 1;
break;
}
return 0;
}
/* send PORT message */
static void
sendport(s,socket)
int s;
struct sockaddr_in *socket;
{
/* Send PORT a,a,a,a,p,p message */
usprintf(s,"PORT %u,%u,%u,%u,%u,%u\n",
hibyte(hiword(socket->sin_addr.s_addr)),
lobyte(hiword(socket->sin_addr.s_addr)),
hibyte(loword(socket->sin_addr.s_addr)),
lobyte(loword(socket->sin_addr.s_addr)),
hibyte(socket->sin_port),
lobyte(socket->sin_port));
}
/* Wait for, read and display response from FTP server. Return the result code.
*/
static int
getresp(ftp,mincode)
struct ftpcli *ftp;
int mincode; /* Keep reading until at least this code comes back */
{
register char *line;
int rval;
usflush(ftp->control);
line = mallocw(LINELEN);
for(;;){
/* Get line */
if(recvline(ftp->control,line,LINELEN) == -1){
rval = -1;
break;
}
rip(line); /* Remove cr/lf */
rval = atoi(line);
if(rval >= 400 || ftp->verbose >= V_NORMAL)
tprintf("%s\n",line); /* Display to user */
/* Messages with dashes are continued */
if(line[3] != '-' && rval >= mincode)
break;
}
free(line);
return rval;
}
/* Issue a prompt and read a line from the user */
static int
getline(sp,prompt,buf,n)
struct session *sp;
char *prompt;
char *buf;
int n;
{
/* If there's something already there, don't issue prompt */
if(socklen(sp->input,0) == 0)
tprintf(prompt);
usflush(sp->output);
return recvline(sp->input,buf,n);
}
/* Attempt to log in the user whose name is in ftp->username and password
* in pass
*/
static char *
ftpcli_login(ftp,host)
struct ftpcli *ftp;
char *host; {
char buf[80],*cp,*cp1;
FILE *fp;
extern char *Hostfile; /* List of user names and permissions */
if((fp = fopen(Hostfile,"r")) == NULLFILE){
return NULLCHAR;
}
while(fgets(buf,sizeof(buf),fp),!feof(fp)){
buf[strlen(buf)-1] = '\0'; /* Nuke the newline */
if(buf[0] == '#')
continue; /* Comment */
if((cp = strchr(buf,' ')) == NULLCHAR)
/* Bogus entry */
continue;
*cp++ = '\0'; /* Now points to user name */
if(strcmp(host,buf) == 0)
break; /* Found host name */
}
if(feof(fp)){
/* User name not found in file */
fclose(fp);
return NULLCHAR;
}
fclose(fp);
/* Look for space after user field in file */
if((cp1 = strchr(cp,' ')) == NULLCHAR)
/* if not there then we'll prompt */
ftp->password = NULLCHAR;
else
*cp1++ = '\0'; /* Now points to password */
if(strcmp(cp,"*") == 0)
cp1 = "anonymous";
ftp->password = strdup(cp1);
return strdup(cp);
}