/* Internet FTP client (interactive user)
 * Copyright 1991 Phil Karn, KA9Q
 */
 /* Mods by G1EMM and PA0GRI */
/* modifications for encrypted password by ik1che 900419 */
/* added "resume" and "rput" commands for interrupted file transfers
 * by iw0cnb 15 Feb 92 */

 /* VIEW command added by Simon G1FHY. Mod by Paul@wolf.demon.co.uk */
#include <stdio.h>
#ifdef MSDOS
#include <dir.h>
#endif
#ifdef UNIX
#include <sys/types.h>
#include <sys/stat.h>
#endif
#include "global.h"
#include "config.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 "help.h"
#ifdef LZW
#include "lzw.h"
#endif
#include "files.h"
#include "config.h"

#define	DIRBUF	256

extern char *FTPHelp;           /* FTP Help file */

#ifdef ALLSESSIONS
static int compsub __ARGS((struct ftpcli *ftp,char *localname,char *remotename));
static int txlate __ARGS((int argc,char *argv[],void *p,char *substr,int parm));
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 docompare __ARGS((int argc,char *argv[],void *p));
static int doftpcd __ARGS((int argc,char *argv[],void *p));
static int doftpcdup __ARGS((int argc,char *argv[],void *p));
static int doftpdel __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 domd5 __ARGS((int argc,char *argv[],void *p));
static int doldir  __ARGS((int argc,char *argv[],void *p));
static int dolcd  __ARGS((int argc,char *argv[],void *p));
static int dolmkdir __ARGS((int argc,char *argv[],void *p));
static int dolrename __ARGS((int argc,char *argv[],void *p));
static int dolrmdir __ARGS((int argc,char *argv[],void *p));
#ifdef LZW
static int doftplzw __ARGS((int argc,char *argv[],void *p));
#endif
static int domcompare __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 dopwd __ARGS((int argc,char *argv[],void *p));
static int doftphelp __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 doftprename __ARGS((int argc,char *argv[],void *p));
static int doresume __ARGS((int argc,char *argv[],void *p));
static int dorput __ARGS((int argc,char *argv[],void *p));
static int dotype __ARGS((int argc,char *argv[],void *p));
static int doftpview __ARGS((int argc,char *argv[],void *p));
static int ftpgetline __ARGS((struct session *sp,char *prompt,char *buf,int n));
static int getresp __ARGS((struct ftpcli *ftp,int mincode));
static int doupdate __ARGS((int argc,char *argv[],void *p));
static long getsub __ARGS((struct ftpcli *ftp,char *command,char *remotename,
	char *localname));
static long putsub __ARGS((struct ftpcli *ftp,char *remotename,char *localname,int putr));
static void sendport __ARGS((int s,struct sockaddr_in *socket));
static char *ftpcli_login __ARGS((struct ftpcli *ftp,char *host)); 

static char Notsess[] = "Not an FTP session!\n";
static int Ftp_type = ASCII_TYPE;
static int Ftp_logbsize = 8;

static struct cmds DFAR Ftpcmds[] = {
	"",		donothing,	0, 0, NULLCHAR,
	"?",		doftphelp,	0, 0, NULLCHAR,
	"append",	doput,		0, 2, "append <localfile> <remotefile>",
	"ascii",	doascii,	0, 0, NULLCHAR,
	"batch",	dobatch,	0, 0, NULLCHAR,
	"binary",	dobinary,	0, 0, NULLCHAR,
	"bye",		doquit,		0, 0, NULLCHAR,
	"cd",		doftpcd,	0, 2, "cd <directory>",
	"cdup",		doftpcdup,	0, 0, NULLCHAR,
	"compare",	docompare,	0, 2, "compare <remotefile> [<localfile>]",
	"del",		doftpdel,	0, 2, "del <remotefile>",
	"dir",		dolist,		0, 0, NULLCHAR,
	"exit",		doquit,		0, 0, NULLCHAR,
	"get",		doget,		0, 2, "get <remotefile> <localfile>",
	"hash",		dohash,		0, 0, NULLCHAR,
	"help",		doftphelp,	0, 0, NULLCHAR,
	"lcd",          dolcd,          0, 1, NULLCHAR,
	"ldir",         doldir,         0, 1, NULLCHAR,
	"list",		dolist,		0, 0, NULLCHAR,
	"lmkdir",       dolmkdir,       0, 2, "lmkdir <local Directory>",
	"lrename",	dolrename,	0, 3, "lrename <oldname> <newname>",
	"lrmdir",       dolrmdir,       0, 2, "lrmdir <local Directory>",
	"ls",		dols,		0, 0, NULLCHAR,
#ifdef LZW
	"lzw",		doftplzw,		0, 0, NULLCHAR,
#endif
	"mcompare",	domcompare,	0, 2, "mcompare <file> [<file> ...]",
	"md",		domkdir,	0, 2, "md <directory>",
	"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> ...]",
	"nlist",	dols,		0, 0, NULLCHAR,
	"nlst",		dols,		0, 0, NULLCHAR,
	"put",		doput,		0, 2, "put <localfile> <remotefile>",
	"pwd",		dopwd,		0, 0, NULLCHAR,
	"quit",		doquit,		0, 0, NULLCHAR,
	"rename",	doftprename,	0, 3, "rename <oldname> <newname>",
	"resume",       doresume,       0, 2, "resume <remotefile> <localfile>",
	"rmdir",	dormdir,	0, 2, "rmdir <directory>",
	"rput",         dorput,         0, 2, "rput <localfile> <remotefile>",
	"type",		dotype,		0, 0, NULLCHAR,
	"update",	doupdate,	0, 0, NULLCHAR,
	"verbose",	doverbose,	0, 0, NULLCHAR,
	"view",         doftpview,         0, 2,"view <remotefile>",
	NULLCHAR,	NULLFP((int,char**,void*)),	0, 0, NULLCHAR,
};


int
doftphelp(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	dohelper ("FTP commands:\n", &Ftpcmds[1], NULLCHAR, (argv[0][0] == '?' && argc == 1) ? NULLCHAR : FTPHelp, (argc == 2) ? argv[1] : NULLCHAR);
	return 0;
}

/* 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];
#ifdef notyet
	char l[17];
#endif
	int control, rmt;
#ifdef __GNUC__
	static
#endif
	char *ftpcli_login __ARGS((struct ftpcli *, char *));
	FILE *fp1 = NULLFILE;
	struct  cur_dirs dirs;

	/*Make sure this comes from console - WG7J*/
    if(Curproc->input != Command->input)
		return 0;

    /* Allocate a session control block */
	if((sp = newsession(argv[1],FTP,0)) == NULLSESSION){
        tputs(TooManySessions);
		return 1;
	}
	memset((char *)&ftp,0,sizeof(ftp));
	ftp.control = ftp.data = -1;
	ftp.verbose = V_BYTE;	/* changed to ver 4 default - KO4KS */
	ftp.type = Ftp_type;
	ftp.logbsize = Ftp_logbsize;

	sp->cb.ftp = &ftp;	/* Downward link */
	ftp.session = sp;	/* Upward link */

	ftp.curdirs = &dirs;      

	fsocket.sin_family = AF_INET;
	fsocket.sin_port = IPPORT_FTP;

	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){
        tputs(Nosock);
		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));
	tprintf("Local Directory - %s\n",init_dirs(&dirs));
	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;
#ifdef UNIX
	if (!(un = getenv("LOGNAME")))
#endif
	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);

	rmt = 0;
	if(argc > 2)
		if((fp1 = fopen(argv[2],READ_TEXT)) == NULLFILE) goto quit;
	while(resp != -1){
		switch (resp)	{
			case 220:	/* Sign-on banner; prompt for and send USER command */
					if((cp = ftpcli_login(&ftp, sp->name)) == NULLCHAR)	{
						if(argc > 2)	{
							if(fgets(buf,LINELEN,fp1) == NULLCHAR)
								goto quit;
						} else
							ftpgetline(sp,prmt,buf,LINELEN);
						/* 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);
					}
					break;
			case 331:	if(ftp.password == NULLCHAR)	{
						/* turn off echo */
						if(argc > 2)	{
							if(fgets(buf,LINELEN,fp1) == NULLCHAR)
								goto quit;
						} else	{
							sp->ttystate.echo = 0;
							ftpgetline(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 */
					}
					break;
			case 230:	/* Successful login */
					/* Find out what type of system we're talking to */
					tprintf("ftp> syst\n");
					usprintf(control,"SYST\n");
					resp = getresp(&ftp,200);
					break;
			case 215:	/* Response to SYST command */
					cp = strchr(ftp.line,' ');
					if(cp != NULLCHAR && strnicmp(cp+1,System,strlen(System)) == 0){
						ftp.type = IMAGE_TYPE;
						tprintf("Defaulting to binary mode\n");
					}
					resp = 200;	/* dummy */
					break;
#ifdef notyet
			case 399:	/* Encrypted password login */
					if(ftp.password == NULLCHAR)	{
						ftpgetline(sp,"Key ? --> ",buf,LINELEN);
						/* Send the command only if the user response
						 * was non-null
						 */
						if(buf[0] != '\n')	{
							cp = strchr(ftp.line,':');
							cp += 2;
						      /*  
							epass(htol(cp),buf,l);
						      */  
							l[16] = '\0';
							free(ftp.line);
							ftp.line = NULLCHAR;
							usprintf(control,"PASS %s\n",l);
							resp = getresp(&ftp,200);
						} else {
							tprintf("Password must be provided.\nLogin failed.\n");
							resp = 200;     /* dummy */
						}
					} else {
						cp = strchr(ftp.line,':');
						cp += 2;
						/*
						epass(htol(cp),ftp.password,l);
						*/
						l[16] = '\0';
						free(ftp.line);
						ftp.line = NULLCHAR;
						usprintf(control,"PASS %s\n",l);
						resp = getresp(&ftp,200);
						free(ftp.password);
						ftp.password = NULLCHAR;        /* clean up */
					}
					break;
#endif
			default:	/* Test the control channel first */
					if(sockstate(control) == NULLCHAR)
						break;
					if(argc > 2)	{
						if(fgets(buf,LINELEN,fp1) == NULLCHAR)
							goto quit;
					} else
						ftpgetline(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);
	if(argc < 3)
		keywait(NULLCHAR,1);
	else
		fclose(fp1);
	if(ftp.session != NULLSESSION)
		freesession(ftp.session);
	free_dirs(&dirs);         
	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);
}
/* 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) == NULLFTP)
		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 = (struct ftpcli *)p) == NULLFTP)
		return -1;
	tputs("Hash Printing ");
	if (ftp->verbose==V_HASH){
		ftp->verbose = V_HASH+1;
		tputs("Off\n");
	} else {
		tputs("On\n");
		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 */
	/* prevent spurious retry caused by "error" return */
	ftp->state = EXITING_STATE;
	return -1;
}

#ifdef LZW
/* Toggle LZW compressed streams mode on */
static int
doftplzw(argc,argv,p)
int argc;
char *argv[];
void *p;
{
register struct ftpcli *ftp;
int retval;

	ftp = (struct ftpcli *)p;
	if(ftp == NULLFTP)
		return -1;
	if (ftp->lzw)	{
		tprintf ("550 Already using LZW compression\n");
		return (550);
	}
	usprintf(ftp->control,"XLZW %d %d\n", Lzwbits, Lzwmode);
	retval = getresp(ftp,200);
	if (retval >= 200 && retval < 300) 	{
		ftp->lzw = 1;
		ftp->lzwbits = Lzwbits;
		ftp->lzwmode = Lzwmode;
/*		lzwinit (ftp->control, Lzwbits, Lzwmode);	*/
	}
	return (retval);
}
#endif

/* Rename remote file */
static int
doftprename(argc,argv,p)
int argc;
char *argv[];
void *p;
{
register struct ftpcli *ftp;
int retval;

	ftp = (struct ftpcli *)p;
	if(ftp == NULLFTP)
		return -1;
	usprintf(ftp->control,"RNFR %s\n", argv[1]);
	retval = getresp(ftp,350);

	if (retval != 350)
		return (retval);
	usprintf(ftp->control,"RNTO %s\n", argv[2]);
	return (getresp(ftp,200));
}

/* Rename local file */
static int
dolrename(argc,argv,p)
int argc;
char *argv[];
void *p;
{
register struct ftpcli *ftp;
char fname1[128];
char fname2[128];

	ftp = (struct ftpcli *)p;
	if(ftp == NULLFTP)
		return -1;
	strcpy(fname1,make_fname(ftp->curdirs->dir,argv[1]));
	strcpy(fname2,make_fname(ftp->curdirs->dir,argv[2]));
	if(rename(fname1,fname2) == -1)
		tprintf("Can't rename: %s\n",sys_errlist[errno]);
	else
		tprintf("Local file renamed to '%s'\n",fname2);
	return 0;

}

static int
txlate (argc,argv,p,substr, parm)
int argc;
char *argv[];
void *p;
char *substr;
int parm;
{
register struct ftpcli *ftp;

	ftp = (struct ftpcli *)p;
	if(ftp == NULLFTP)
		return -1;
	usprintf(ftp->control,"%s %s\n",substr, (parm) ? argv[1] : "");
	return getresp(ftp,200);
}

/* Pass PWD to server - included here to allow cmd to be viewed in help list */
static int
dopwd(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return (txlate (argc, argv, p, "PWD", 0));
}

/* Translate 'cd' to 'cwd' for convenience */
static int
doftpcd(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	if (argc == 1)
		return (dopwd(argc, argv, p));
	return (txlate (argc, argv, p, "CWD", 1));
}

/* Pass CDUP to server - included here to allow cmd to be viewed in help list */
static int
doftpcdup(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return (txlate (argc, argv, p, "CDUP", 0));
}

/* Translate 'del' to 'dele' for convenience */
static int
doftpdel(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return (txlate (argc, argv, p, "DELE", 1));
}

/* Translate 'mkdir' to 'xmkd' for convenience */
static int
domkdir(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return (txlate (argc, argv, p, "XMKD", 1));
}
/* Translate 'rmdir' to 'xrmd' for convenience */
static int
dormdir(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return (txlate (argc, argv, p, "XRMD", 1));
}
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;
}

/* View added to jnos1.08 by Simon G1FHY _ mod by Paul@wolf.demon.co.uk */
/* Start view transfer. Syntax: view <remote name> */
static int
doftpview(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    char *remotename;
	register struct ftpcli  *ftp;

    ftp = (struct ftpcli *)p;
	if(ftp == NULLFTP){
		tprintf(Notsess);
		return 1;
	}
	remotename = argv[1];

    getsub(ftp,"RETR",remotename,NULLCHAR);
	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];

	if(!ftp->update || compsub(ftp,localname,remotename) != 0)
		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;
#ifdef MSDOS
	char *c;
#endif
	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(make_fname(ftp->curdirs->dir,&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
			if(!ftp->update || compsub(ftp,buf,buf) != 0)
				getsub(ftp,"RETR",buf,local);
			usflush (ftp->control);
			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;
}
/* Resume interrupted file transfer. Syntax: resume <remote name> [<local name>] */
static int
doresume(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,"RSME",remotename,localname);
	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;
}
static int
domd5(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	char *remotename;
	register struct ftpcli *ftp;
	int control;
	int resp;
	int typewait = 0;

	ftp = (struct ftpcli *)p;
	if(ftp == NULLFTP){
		tprintf(Notsess);
		return 1;
	}
	control = ftp->control;
	remotename = argv[1];
	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;

	}
	usprintf(control,"XMD5 %s\n",remotename);
	if(typewait)
		(void)getresp(ftp,200);
	(void)getresp(ftp,200);
failure:;
	return 0;
}
static int
docompare(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 = remotename;

	if(compsub(ftp,localname,remotename) == 0)
		tprintf("Same\n");
	else
		tprintf("Different\n");
	return 0;
}
/* Compare a collection of files */
static int
domcompare(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	register struct ftpcli *ftp;
	FILE *files;
	char *buf;
	int i;
	long r;
	char tmpname[80];

	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++){
		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);
			if(compsub(ftp,buf,buf) == 0)
				tprintf("%s - Same\n",buf);
			else
				tprintf("%s - Different\n",buf);

			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;
}
/* Common subroutine to compare a local with a remote file
 * Return 1 if files are different, 0 if they are the same
 */
static int
compsub(ftp,localname,remotename)
struct ftpcli *ftp;
char *localname;
char *remotename;
{
	char *mode = NULLCHAR,*cp;
	FILE *fp;
	int control;
	int resp,i;
	int typewait = 0;
	char remhash[16];
	char lochash[16];

	control = ftp->control;

	switch(ftp->type){
	case IMAGE_TYPE:
	case LOGICAL_TYPE:
		mode = READ_BINARY;
		break;
	case ASCII_TYPE:
		mode = READ_TEXT;
		break;
	}
	if((fp = fopen(make_fname(ftp->curdirs->dir,localname),mode)) == NULLFILE){
		tprintf("Can't read local file %s\n",make_fname(ftp->curdirs->dir,localname));
		return 1;
	}
	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;
	}
	usprintf(control,"XMD5 %s\n",remotename);
	/* Try to overlap the two MD5 operations */
	md5hash(fp,lochash,ftp->type == ASCII_TYPE);
	fclose(fp);
	if(typewait && (resp = getresp(ftp,200)) > 299)
		goto failure;
	if((resp = getresp(ftp,200)) > 299){
		if(resp == 500)
			ftp->update = 0;	/* XMD5 not supported */
		goto failure;
	}	
	if((cp = strchr(ftp->line,' ')) == NULLCHAR){
		tprintf("Error in response\n");
		goto failure;
	}
	/* Convert ascii/hex back to binary */
	readhex(remhash,cp,sizeof(remhash));
	if(ftp->verbose > 1){
		tprintf("Loc ");
		for(i=0;i<sizeof(lochash);i++)
			tprintf("%02x",lochash[i] & 0xff);
		tprintf(" %s\n",make_fname(ftp->curdirs->dir,localname));
	}
	if(memcmp(lochash,remhash,sizeof(remhash)) == 0)
		return 0;
	else
		return 1;
failure:;
	return 1;
}


/* Common code to LIST/NLST/RETR/RSME 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;
	unsigned long starting;

	if(ftp == NULLFTP)
		return -1;
	control = ftp->control;
	savmode = ftp->type;
#ifdef __GNUC__
	mode = 0;		/* semi-spurious warning */
#endif

	switch(ftp->type){
	case IMAGE_TYPE:
	case LOGICAL_TYPE:
		if(strcmp(command,"RSME") == 0)
			mode = APPEND_BINARY;
		else
			mode = WRITE_BINARY;
		break;
	case ASCII_TYPE:
		if(strcmp(command,"RSME") == 0)
			mode = APPEND_TEXT;
		else
			mode = WRITE_TEXT;
		break;
	}
	/* Open the file */
	if(localname == NULLCHAR){
		fp = NULLFILE;
	} else if((fp = fopen(make_fname(ftp->curdirs->dir,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;
#ifdef LZW
	if (ftp->lzw)
		lzwinit (ftp->data, ftp->lzwbits, ftp->lzwmode);	
#endif

	/* 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;
	accept(ftp->data,NULLCHAR,(int *)NULL);
	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;
	if(strcmp(command,"RSME") == 0){
		if((starting = getsize(fp)) == -1)
			starting = 0L;
		usprintf(control,"%lu %lu\n",starting,checksum(fp,(long)starting));
		usflush(control);
		fseek(fp,(long)starting,SEEK_SET);
	}
	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);
	if(remotename == NULLCHAR)
		remotename = "";
	if(total == -1){
		tprintf("%s %s: Error/abort during data transfer\n",command,remotename);
	} else if(ftp->verbose >= V_SHORT){
		startclk = msclock() - startclk;
		rate = 0;
		if(startclk != 0){	/* Avoid divide-by-zero */
			if(total < 4294967L){
				rate = (total*1000)/startclk;
			} else {	/* Avoid overflow */
				rate = total/(startclk/1000);
			}
		}
		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];

	if(!ftp->update || compsub(ftp,localname,remotename) != 0)
		putsub(ftp,remotename,localname,(*argv[0] == 'a') ? 2 : 0);
	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;
#if 0
	int j;
#endif
	char tmpname[80];	
	char *buf, *file;

	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++)	{
        /* Use the path in the ftp client struct, since user may have done
         * a lcd command to change dir !
         */
        file = pathname(ftp->curdirs->dir,argv[i]);
#if 0		/* was #ifdef MSDOS */
        /* Shift everything back one byte, pathname returns with a leading '/'! */
        for(j=1;j<=strlen(file);j++)
            file[j-1] = file[j];
#endif
        getdir(file,0,files);
        free(file);
    }

	rewind(files);
	buf = mallocw(DIRBUF);
	ftp->state = SENDING_STATE;
	while(fgets(buf,DIRBUF,files) != NULLCHAR){
		rip(buf);
		if(!ftp->update || compsub(ftp,buf,buf) != 0)
			putsub(ftp,buf,buf,0);
		if(ftp->abort)
			break;		/* User abort */
		usflush (ftp->control);
	}
	fclose(files);
	unlink(tmpname);
	free(buf);
	ftp->state = COMMAND_STATE;
	ftp->abort = 0;
	return 0;
}
/* Put a file, appending data to it - iw0cnb */
static int
dorput(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,1);
	return 0;
}
/* Common code to put, mput.
 * Returns number of bytes sent if successful
 * Returns -1 on error
 */
static long
putsub(ftp,remotename,localname,putr)
register struct ftpcli *ftp;
char *remotename,*localname;
int putr;       /* Flag: 0 if standard put, 1 if put with resume, if 2 append */
{
	char *mode;
	int i,resp,control;
	unsigned long total;
	FILE *fp;
	struct sockaddr_in lsocket,lcsocket;
	int32 startclk,rate;
	int typewait = 0;
	int prevstate;
	char *line;
	unsigned long starting;
	unsigned long check, local_check;

	control = ftp->control;
	if(ftp->type == IMAGE_TYPE)
		mode = READ_BINARY;
	else
		mode = READ_TEXT;

	/* Open the file */
	if((fp = fopen(make_fname(ftp->curdirs->dir,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;
#ifdef LZW
	if (ftp->lzw)
		lzwinit (ftp->data, ftp->lzwbits, ftp->lzwmode);	
#endif
 
	/* 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 */
	if(putr == 1)
		usprintf(control,"RPUT %s\n",remotename);
	else if (putr == 2)
		usprintf(control,"APPE %s\n",remotename);
	else
		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();

	if(putr == 1){          /* Wait for file offset and checksum */
		line=mallocw(40);
		recvline(control,line,40);
		starting=atol(line);
		check = (unsigned long)atol(strchr(line,' '));
		free(line);
		local_check = checksum(fp,(long)starting);
		if(ftp->verbose >= V_HASH)
			tprintf("Remote checksum: %lu - Local checksum: %lu - Offset: %lu\n",check,local_check,starting);
		check -= local_check;
		if(check != 0){
			tprintf("Can't send %s: files are different\n",localname);
			shutdown(ftp->data,1);
			getresp(ftp,200);
			goto failure;
		}
	}

	total = sendfile(fp,ftp->data,ftp->type,(ftp->verbose >= V_HASH) ? ftp->verbose : 0);
	close_s(ftp->data);
	ftp->data = -1;
	fclose(fp);

	/* Wait for control channel ack before calculating transfer time;
	 * this accounts for transmitted data in the pipe.
	 */
	getresp(ftp,200);

	if(total == -1){
		tprintf("STOR %s: Error/abort during data transfer\n",remotename);
	} else if(ftp->verbose >= V_SHORT){
		startclk = msclock() - startclk;
		rate = 0;
		if(startclk != 0){	/* Avoid divide-by-zero */
			if(total < 4294967L){
				rate = (total*1000)/startclk;
			} else {	/* Avoid overflow */
				rate = total/(startclk/1000);
			}
		}
		tprintf("STOR %s: %lu bytes in %lu sec (%lu/sec)\n",
		 remotename,total,startclk/1000,rate);
	}
	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 */
{
	int rval;

	usflush(ftp->control);
	for(;;){
		/* Get line */
		if(recvline(ftp->control,ftp->line,LINELEN) == -1){
			rval = -1;
			break;
		}
		rip(ftp->line);		/* Remove cr/lf */
		rval = atoi(ftp->line);
		if(rval >= 400 || ftp->verbose >= V_NORMAL)
			tprintf("%s\n",ftp->line);	/* Display to user */

		/* Messages with dashes are continued */
		if(ftp->line[3] != '-' && rval >= mincode)
			break;
	}
	return rval;
}

/* Issue a prompt and read a line from the user */
static int
ftpgetline(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,(unsigned)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 = NULLCHAR,*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);
}

int
dolcd(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	register struct ftpcli *ftp;

	ftp = (struct ftpcli *)p;
	if(ftp == NULLFTP){
		tprintf(Notsess);
		return 1;
	}

	if(argc > 1){
		if (!dir_ok(argv[1],ftp->curdirs)) {
		     tprintf("Invalid Drive/Directory - %s\n",argv[1]);
		     return 1;
		}
		   
	}
	tprintf("Local Directory - %s\n",ftp->curdirs->dir);
	
	return 0;
}


int
doldir(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	register struct ftpcli *ftp;
	char ** margv;

	margv=(char **)callocw(2,sizeof(char*));

	ftp = (struct ftpcli *)p;
	if(ftp == NULLFTP){
		tprintf(Notsess);
		return 1;
	}
	
	tprintf("\n");
	if (argc == 1)
		margv[1]=strdup(ftp->curdirs->dir);
	else
		margv[1]=strdup(make_dir_path(argc,argv[1],ftp->curdirs->dir));
	dodir(2,margv,p);
	free(margv[1]);
	free(margv);
	tprintf("\n");
	return 0;
}

int
dolmkdir(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	register struct ftpcli *ftp;
	char *buf;

	ftp = (struct ftpcli *)p;
	if(ftp == NULLFTP){
		tprintf(Notsess);
		return 1;
	}
/*	undosify(argv[1]);	done in make_fname	*/
	buf=strdup(make_fname(ftp->curdirs->dir,argv[1]));
#ifdef UNIX
	if (mkdir(buf, 0777) == -1)
#else
	if (mkdir(buf) == -1)
#endif
		tprintf("Can't make %s: %s\n",buf,sys_errlist[errno]);
	else
		tprintf("Directory %s Created\n",buf);
	free(buf);
	return 0;
}                

int
dolrmdir(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	register struct ftpcli *ftp;
	char *buf;

	ftp = (struct ftpcli *)p;
	if(ftp == NULLFTP){
		tprintf(Notsess);
		return 1;
	}
	buf=strdup(make_fname(ftp->curdirs->dir,argv[1]));
	if (rmdir(buf) == -1)
		tprintf("Can't remove %s: %s\n",buf,sys_errlist[errno]);
	else
		tprintf("Directory %s Removed\n",buf);
	free(buf);
	return 0;
}                

#endif
