/* Internet FTP client (interactive user)
*/
#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"
#include "internet.h"
#define POLLRATE 500 /* 500ms between more file polls */
#define DIRBUF 256
static int doascii(int argc,char *argv[],void *p);
static int dobatch(int argc,char *argv[],void *p);
static int dobinary(int argc,char *argv[],void *p);
static int docompare(int argc,char *argv[],void *p);
static int doftpcd(int argc,char *argv[],void *p);
static int doget(int argc,char *argv[],void *p);
static int dohash(int argc,char *argv[],void *p);
static int doverbose(int argc,char *argv[],void *p);
static int dolist(int argc,char *argv[],void *p);
static int dols(int argc,char *argv[],void *p);
static int domd5(int argc,char *argv[],void *p);
static int domkdir(int argc,char *argv[],void *p);
static int domcompare(int argc,char *argv[],void *p);
static int domget(int argc,char *argv[],void *p);
static int domput(int argc,char *argv[],void *p);
static int doput(int argc,char *argv[],void *p);
static int doquit(int argc,char *argv[],void *p);
static int doread(int argc,char *argv[],void *p);
static int dormdir(int argc,char *argv[],void *p);
static int dotype(int argc,char *argv[],void *p);
static int doupdate(int argc,char *argv[],void *p);
static int getline(struct session *sp,char *prompt,char *buf,int n);
static int getresp(struct ftpcli *ftp,int mincode);
static long getsub(struct ftpcli *ftp,char *command,char *remotename,
FILE *fp);
static long putsub(struct ftpcli *ftp,char *remotename,char *localname);
static int compsub(struct ftpcli *ftp,char *localname,char *remotename);
static void sendport(FILE *fp,struct sockaddr_in *socket);
static int keychar(int c);
static char Notsess[] = "Not an FTP session!\n";
static struct cmds Ftpcmds[] = {
"", donothing, 0, 0, NULL,
"ascii", doascii, 0, 0, NULL,
"batch", dobatch, 0, 0, NULL,
"binary", dobinary, 0, 0, NULL,
"cd", doftpcd, 0, 2, "cd <directory>",
"compare", docompare, 0, 2, "compare <remotefile> [<localfile>]",
"dir", dolist, 0, 0, NULL,
"list", dolist, 0, 0, NULL,
"get", doget, 0, 2, "get <remotefile> <localfile>",
"hash", dohash, 0, 0, NULL,
"ls", dols, 0, 0, NULL,
"mcompare", domcompare, 0, 2, "mcompare <file> [<file> ...]",
"md5", domd5, 0, 2, "md5 <file>",
"mget", domget, 0, 2, "mget <file> [<file> ...]",
"mkdir", domkdir, 0, 2, "mkdir <directory>",
"mput", domput, 0, 2, "mput <file> [<file> ...]",
"nlst", dols, 0, 0, NULL,
"quit", doquit, 0, 0, NULL,
"read", doread, 0, 2, "read <remotefile>",
"rmdir", dormdir, 0, 2, "rmdir <directory>",
"put", doput, 0, 2, "put <localfile> <remotefile>",
"type", dotype, 0, 0, NULL,
"update", doupdate, 0, 0, NULL,
"verbose", doverbose, 0, 0, NULL,
NULL, NULL, 0, 0, NULL,
};
/* 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 *bufsav,*cp;
FILE *control;
int s;
/* Allocate a session control block */
if((sp = newsession(Cmdline,FTP,1)) == NULL){
printf("Too many sessions\n");
return 1;
}
sp->inproc = keychar;
memset(&ftp,0,sizeof(ftp));
ftp.control = ftp.data = NULL;
ftp.verbose = V_NORMAL;
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]);
if(SETSIG(EABORT)){
keywait(NULL,1);
freesession(sp);
return 1;
}
printf("Resolving %s...\n",argv[1]);
if((fsocket.sin_addr.s_addr = resolve(argv[1])) == 0){
printf(Badhost,argv[1]);
keywait(NULL,1);
freesession(sp);
return 1;
}
/* Open the control connection */
if((s = socket(AF_INET,SOCK_STREAM,0)) == -1){
printf("Can't create socket\n");
keywait(NULL,1);
freesession(sp);
return 1;
}
if(SETSIG(EABORT)){
goto quit;
}
sp->network = control = ftp.control = fdopen(s,"r+t");
settos(s,LOW_DELAY);
printf("Trying %s...\n",psocket(&fsocket));
if(connect(s,(struct sockaddr *)&fsocket,sizeof(fsocket)) == -1){
perror("Connect failed");
goto quit;
}
printf("Connected\n");
/* Wait for greeting from server */
resp = getresp(&ftp,200);
if(resp >= 400)
goto quit;
/* Now process responses and commands */
if(SETSIG(EABORT)){
/* Come back here after a ^C in command state */
resp = 200;
}
while(resp != -1){
switch(resp){
case 220:
/* Sign-on banner; prompt for and send USER command */
getline(sp,"Enter user name: ",ftp.buf,LINELEN);
/* Send the command only if the user response
* was non-null
*/
if(ftp.buf[0] != '\n'){
fprintf(control,"USER %s",ftp.buf);
resp = getresp(&ftp,200);
} else
resp = 200; /* dummy */
break;
case 331:
/* turn off echo */
sp->ttystate.echo = 0;
getline(sp,"Password: ",ftp.buf,LINELEN);
printf("\n");
/* Turn echo back on */
sp->ttystate.echo = 1;
/* Send the command only if the user response
* was non-null
*/
if(ftp.buf[0] != '\n'){
fprintf(control,"PASS %s",ftp.buf);
resp = getresp(&ftp,200);
} else
resp = 200; /* dummy */
break;
case 230: /* Successful login */
/* Find out what type of system we're talking to */
printf("ftp> syst\n");
fprintf(control,"SYST\n");
resp = getresp(&ftp,200);
break;
case 215:
/* Response to SYST command */
cp = strchr(ftp.line,' ');
if(cp != NULL && strnicmp(cp+1,System,strlen(System)) == 0){
ftp.type = IMAGE_TYPE;
printf("Defaulting to binary mode\n");
}
resp = 200; /* dummy */
break;
default:
/* Test the control channel first */
if(sockstate(fileno(control)) == NULL){
resp = -1;
break;
}
getline(sp,"ftp> ",ftp.buf,LINELEN);
/* Copy because cmdparse modifies the original */
bufsav = strdup(ftp.buf);
if((resp = cmdparse(Ftpcmds,ftp.buf,&ftp)) != -1){
/* Valid command, free buffer and get another */
FREE(bufsav);
} else {
/* Not a local cmd, send to remote server */
fputs(bufsav,control);
FREE(bufsav);
/* Enable display of server response */
vsave = ftp.verbose;
ftp.verbose = V_NORMAL;
resp = getresp(&ftp,200);
ftp.verbose = vsave;
}
}
}
quit: cp = sockerr(fileno(control));
printf("Closed: %s\n",cp != NULL ? cp : "EOF");
if(ftp.fp != NULL && ftp.fp != stdout)
fclose(ftp.fp);
if(ftp.data != NULL)
fclose(ftp.data);
if(ftp.control != NULL){
fclose(ftp.control);
ftp.control = NULL;
sp->network = NULL;
}
keywait(NULL,1);
if(ftp.session != NULL)
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) == NULL)
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) == NULL)
return -1;
return setbool(&ftp->batch,"Command batching",argc,argv);
}
/* Enable/disable update flag */
static int
doupdate(argc,argv,p)
int argc;
char *argv[];
void *p;
{
register struct ftpcli *ftp;
if((ftp = (struct ftpcli *)p) == NULL)
return -1;
return setbool(&ftp->update,"Update with MD5",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 = (str