/* Internet FTP Server server machine - see RFC 959
 * Copyright 1991 Phil Karn, KA9Q
 *
 * Mods by KO4KS
 */
#include <stdio.h>
#include <ctype.h>
#include <time.h>
#ifdef	__TURBOC__
#include <io.h>
#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 "socket.h"
#include "ftp.h"
#include "ftpserv.h"
#include "proc.h"
#include "dirutil.h"
#include "files.h"
#include "commands.h"
#include "session.h"
#include "md5.h"
#include "smtp.h"
#ifdef CALLSERVER
#include <string.h>
/* CD-ROM code by Fred Peachman KB7YW */
extern char *CDROM; /* buckbook.c: defines CDROM drive letter e.g. "s:"  */
#endif                  /* #ifdef CALLSERVER  */
#ifdef LZW
#include "lzw.h"
#endif

static int isanonymous __ARGS((char *name));
static void ftpserv __ARGS((int s,void *unused,void *p));
static int pport __ARGS((struct sockaddr_in *sock,char *arg));
static void ftplogin __ARGS((struct ftpserv *ftp,char *pass));
static int sendit __ARGS((struct ftpserv *ftp,char *command,char *file));
static int recvit __ARGS((struct ftpserv *ftp,char *command,char *file));
static void sendmsgfile __ARGS((int s,int num,char *buf,int size,FILE *fp));
int doftptdisc __ARGS((int argc, char *argv[], void *p));
extern char *addroot __ARGS((char *root, char *name));
extern int dir_ok __ARGS((char * newpath,struct cur_dirs * dirs));
extern char * defpath __ARGS((struct cur_dirs *curdirs, char *path));

#ifdef __cplusplus
extern "C" {
#endif
extern int access __ARGS((const char *,int));
extern int unlink __ARGS((const char *));
extern int rmdir __ARGS((const char *));
#ifdef __cplusplus
}
#endif

#ifdef UNIX
extern unsigned long filelength __ARGS((int));
#endif

/* Command table */
static char *commands[] = {
	"user",
	"acct",
	"pass",
	"type",
	"list",
	"cwd",
	"dele",
	"name",
	"quit",
	"retr",
	"stor",
	"port",
	"nlst",
	"pwd",
	"xpwd",			/* For compatibility with 4.2BSD */
	"mkd ",
	"xmkd",			/* For compatibility with 4.2BSD */
	"xrmd",			/* For compatibility with 4.2BSD */
	"rmd ",
	"stru",
	"mode",
	"syst",
	"xmd5",
	"rsme",                 /* Added by IW0CNB, for resuming interrupted trasnfers */
	"rput",
#ifdef LZW
	"xlzw",
#endif
	"rnfr",
	"rnto",
	"cdup",
	"appe",
	NULLCHAR
};

/* Response messages */
static char banner[] = "220 %s, KA9Q-NOS FTP version %s\n";
static char banner1[] = "230- Ready on %s";
static char banner2[] = "230- Total active FTP sessions at %s: %d out of %d maximum\n";
static char badcmd[] = "500 Unknown command\n";
static char binwarn[] = "100 Warning: type is ASCII and %s appears to be binary\n";
static char unsupp[] = "500 Unsupported command or option\n";
static char givepass[] = "331 Enter PASS command\n";
static char anonokay[] = "331 Anonymous access, give email address as password\n";
#ifdef notdef
static char challenge[] = "399 PASS challenge : %016lx\n";
#endif
static char logged[] = "230 Logged in\n";
static char loggeda[] = "230 Logged in as anonymous, restrictions apply\n";
static char typeok[] = "200 Type %s OK\n";
static char only8[] = "501 Only logical bytesize 8 supported\n";
static char deleok[] = "250 File deleted\n";
static char mkdok[] = "200 MKD ok\n";
static char delefail[] = "550 Delete failed: %s\n";
static char pwdmsg[] = "257 \"%s\" is current directory\n";
static char badtype[] = "501 Unknown type \"%s\"\n";
static char badport[] = "501 Bad port syntax\n";
static char unimp[] = "502 Command not yet implemented\n";
static char bye[] = "221 Goodbye!\n";
static char nodir[] = "553 Can't read directory \"%s\": %s\n";
static char cantopen[] = "550 Can't read file \"%s\": %s\n";
static char sending[] = "150 Opening data connection for %s %s %s\n"; /*N1BEE*/
static char cantmake[] = "553 Can't create \"%s\": %s\n";
static char writerr[] = "552 Write error: %s\n";
static char portok[] = "200 Port command okay\n";
static char rxok[] = "226 File received OK\n";
static char txok[] = "226 File sent OK\n";
static char noperm[] = "550 Permission denied\n";
static char noconn[] = "425 Data connection reset\n";
static char badcheck[] = "425 Bad checksum\n";
#ifdef notdef
static char lowmem[] = "421 System overloaded, try again later\n";
#endif
static char notlog[] = "530 Please log in with USER and PASS\n";
static char userfirst[] = "503 Login with USER first.\n";
static char okay[] = "200 Ok\n";
static char syst[] = "215 %s Type: L%d Version: %s\n";
static char pendingto[] = "350 Rename awaiting new name.\n";
static char badseq[] = "503 No prior RNFR received - RNTO ignored\n";
static char norename[] = "550 Can't rename: %s\n";

int Sftp = -1;	/* Prototype socket for service */
int FtpUsers = 0;
int FtpMaxUsers = 255;

#ifdef FTPTDISC
int32 Ftptdiscinit = 0;

/* Set ftp redundancy timer */
int
doftptdisc(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    return setlong(&Ftptdiscinit,"Ftp redundancy timer (sec)",argc,argv);
}

static void
#ifdef PROTOTYPES
ftp_redundant(struct ftpserv *ftp)
#else
ftp_redundant(ftp)
struct ftpserv *ftp;
#endif
{
    /* Clean up */
    shutdown(ftp->control,2);
    close_s(ftp->control);
    if(ftp->data != -1){
        shutdown(ftp->data,2);
        close_s(ftp->data);
        ftp->data = -1;
    }
    return;
}
#endif

/* Set ftp Maximum number of connections */
int
doftpmaxclients(argc,argv,p)
int argc;
char *argv[];
void *p;
{
    return setint(&FtpMaxUsers,"Maximum Ftp incoming clients",argc,argv);
}

/* Start up FTP service */
int
ftpstart(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return (installserver (argc, argv, &Sftp, "FTP listener", IPPORT_FTP,
		"ftpserv", ftpserv, 2048, NULL));
}

static void sendmsgfile(s, num, buf, size, fp)
int s, num;
char *buf;
int size;
FILE *fp;
{

    while(fgets(buf,size,fp)) {
        rip(buf);
        usprintf(s,"%d- %s\n",num,buf);
    }
}

static int
isanonymous (name)
char *name;
{
FILE *fp;
int retval = 1;
char buf[128], *cp;

	if((fp = fopen(Userfile,READ_TEXT)) != NULLFILE)	{
		while ( fgets(buf,128,fp) != NULLCHAR ){
			if((cp = strpbrk(buf," \t")) == NULLCHAR)
				/* Bogus entry */
				continue;
			*cp++ = '\0';

			if(!stricmp(name,buf))	{
				retval = 0;
				break;		/* Found user */
			}
		}
		fclose (fp);
	}
	return (retval);
}


static void
ftpserv(s,unused,p)
int s;	/* Socket with user connection */
void *unused;
void *p;
{
	struct ftpserv ftp;
	char **cmdp,buf[512],*arg,*cp,*file,*mode = NULLCHAR;
#ifdef notdef
	struct stat cwdstat;
	char *cp1;
#endif
	long t;
	int cnt,i;
	struct sockaddr_in socket;
	struct  cur_dirs dirs;
	char *rnfrom = NULLCHAR;
	FILE *fpm;

	sockmode(s,SOCK_ASCII);
	memset((char *)&ftp,0,sizeof(ftp));	/* Start with clear slate */
	ftp.data = -1;

	sockowner(s,Curproc);		/* We own it now */
	ftp.control = s;
	/* Set default data port */
	i = SOCKSIZE;
	getpeername(s,(char *)&socket,&i);
	socket.sin_port = IPPORT_FTPD;
	ASSIGN(ftp.port,socket);

#ifdef FTPTDISC
    /* Set the timeout timer - WG7J */
    set_timer(&ftp.tdisc,Ftptdiscinit * 1000L);
    ftp.tdisc.func = (void(*)__FARGS((void*)))ftp_redundant;
    ftp.tdisc.arg = &ftp;
    start_timer(&ftp.tdisc);
#endif

	if (++FtpUsers > FtpMaxUsers)	{
		usprintf (s, "\n200- Sorry, too many FTP users at %s this time. Try again later!\n",  Hostname);
/*		usflush (s);	*/
		FtpUsers--;
		return;
	}
	log(s,"open FTP");
    usprintf(s,banner,Hostname,Version);
	init_dirs(&dirs);
	ftp.curdirs = &dirs;      
    time(&t);
    cp = ctime(&t);
#ifdef notdef
    if((cp1 = strchr(cp,'\n')) != NULLCHAR)
        *cp1 = '\0';
#endif

    /* Command interpreting loop */
loop:
    if((cnt = recvline(s,buf,sizeof(buf))) == -1){
		/* He closed on us */
		goto finish;
	}
#ifdef FTPTDISC
    /* Reset the timeout timer - WG7J */
    start_timer(&ftp.tdisc);
#endif
    if(cnt == 0){
		/* Can't be a legal FTP command */
		usprintf(ftp.control,badcmd);
		goto loop;
	}	
	rip(buf);
	/* Translate first word to lower case */
	for(cp = buf;*cp != ' ' && *cp != '\0';cp++)
		*cp = tolower(*cp);
	/* Find command in table; if not present, return syntax error */
	for(cmdp = commands;*cmdp != NULLCHAR;cmdp++)
		if(strnicmp(*cmdp,buf,strlen(*cmdp)) == 0)
			break;
	if(*cmdp == NULLCHAR){
		usprintf(ftp.control,badcmd);
		goto loop;
	}
	/* Allow only USER, PASS and QUIT before logging in */
	if(ftp.path == NULLCHAR){
		switch(cmdp-commands){
		case USER_CMD:
		case PASS_CMD:
		case QUIT_CMD:
			break;
		default:
			usprintf(ftp.control,notlog);
			goto loop;
		}
	}
	arg = &buf[strlen(*cmdp)];
	while(*arg == ' ')
		arg++;

	/* Execute specific command */
	switch(cmdp-commands){
	case USER_CMD:
		free(ftp.username);
		ftp.username = strdup(arg);
	     /*   if(sp_user(ftp.username)) {
			time(&ftp.ttim);
			usprintf(ftp.control,challenge,ftp.ttim);
		}
		else
	     */
	        if (isanonymous (arg))
			usprintf(ftp.control,anonokay);
	        else
			usprintf(ftp.control,givepass);
		break;
	case TYPE_CMD:
		switch(arg[0]){
		case 'A':
		case 'a':	/* Ascii */
			ftp.type = ASCII_TYPE;
			usprintf(ftp.control,typeok,arg);
			break;
		case 'l':
		case 'L':
			while(*arg != ' ' && *arg != '\0')
				arg++;
			if(*arg == '\0' || *++arg != '8'){
				usprintf(ftp.control,only8);
				break;
			}
			ftp.type = LOGICAL_TYPE;
			ftp.logbsize = 8;
			usprintf(ftp.control,typeok,arg);
			break;
		case 'B':
		case 'b':	/* Binary */
		case 'I':
		case 'i':	/* Image */
			ftp.type = IMAGE_TYPE;
			usprintf(ftp.control,typeok,arg);
			break;
		default:	/* Invalid */
			usprintf(ftp.control,badtype,arg);
			break;
		}
		break;
	case QUIT_CMD:
		usprintf(ftp.control,bye);
		goto finish;
	case RNFR_CMD:
		file = addroot(ftp.curdirs->dir,arg);
		if(!permcheck(ftp.path,ftp.perms,RNFR_CMD,file))
		 	usprintf(ftp.control,noperm);
		else if (access (file, 6))
			usprintf(ftp.control,cantopen,file,sys_errlist[errno]);
		else	{
			usprintf(ftp.control,pendingto);
			rnfrom = strdup (file);
		}
		free (file);
		break;
	case RNTO_CMD:
		file = addroot(ftp.curdirs->dir,arg);
		if(rnfrom == NULLCHAR)
			usprintf(ftp.control,badseq);
		else	{
			if(!permcheck(ftp.path,ftp.perms,RNTO_CMD,file))
			 	usprintf(ftp.control,noperm);
			else	{
				if(rename(rnfrom,file) == -1)
					usprintf(ftp.control,norename,sys_errlist[errno]);
				else
					usprintf(ftp.control,okay);
			}
			free (rnfrom);
			rnfrom = NULLCHAR;
		}
		free (file);
		break;
	case RETR_CMD:
	case RSME_CMD:
		file = addroot(ftp.curdirs->dir,arg);
		switch(ftp.type){
		case IMAGE_TYPE:
		case LOGICAL_TYPE:
			mode = READ_BINARY;
			break;
#ifdef __GNUC__
		default:
#endif
		case ASCII_TYPE:
			mode = READ_TEXT;
			break;
		}
		if(!permcheck(ftp.path,ftp.perms,RETR_CMD,file)){
		 	usprintf(ftp.control,noperm);
		} else if((ftp.fp = fopen(file,mode)) == NULLFILE){
			usprintf(ftp.control,cantopen,file,sys_errlist[errno]);
		} else {
			if((cmdp-commands) == RSME_CMD) {
				log(ftp.control,"RSME %s",file);
				if(ftp.type == ASCII_TYPE && isbinary(ftp.fp)){
					usprintf(ftp.control,binwarn,file);
				}
				sendit(&ftp,"RSME",file);
			} else {
				log(ftp.control,"RETR %s",file);
				if(ftp.type == ASCII_TYPE && isbinary(ftp.fp)){
					usprintf(ftp.control,binwarn,file);
				}
				sendit(&ftp,"RETR",file);
			}	
		}
		free(file);
		break;
	case STOR_CMD:
		cp = "STOR";
		goto store2;
	case APPE_CMD:
		cp = "APPE";
		goto store2;
	case RPUT_CMD:
		cp = "RPUT";
store2:		file = addroot(ftp.curdirs->dir,arg);
		switch(ftp.type){
		case IMAGE_TYPE:
		case LOGICAL_TYPE:
			if(cmdp-commands != STOR_CMD)
				mode = APPEND_BINARY;
			else
				mode = WRITE_BINARY;
			break;
#ifdef __GNUC__
		default:
#endif
		case ASCII_TYPE:
			if(cmdp-commands != STOR_CMD)
				mode = APPEND_TEXT;
			else
				mode = WRITE_TEXT;
			break;
		}
		if(!permcheck(ftp.path,ftp.perms,cmdp-commands,file)){
			usprintf(ftp.control,noperm);
		} else if((ftp.fp = fopen(file,mode)) == NULLFILE){
			usprintf(ftp.control,cantmake,file,sys_errlist[errno]);
		} else {
			log(ftp.control,"%s %s",cp, file);
			recvit(&ftp,cp,file);
		}
		free(file);
		break;
	case PORT_CMD:
		if(pport(&ftp.port,arg) == -1){
			usprintf(ftp.control,badport);
		} else {
			usprintf(ftp.control,portok);
		}
		break;
#ifndef CPM
	case LIST_CMD:
	case NLST_CMD:
		file = addroot(ftp.curdirs->dir,defpath (ftp.curdirs, arg));
		if(!permcheck(ftp.path,ftp.perms,RETR_CMD,file))
		 	usprintf(ftp.control,noperm);
		else 	{
			cp = Command->curdirs->dir;
			Command->curdirs->dir = ftp.curdirs->dir;
			if((ftp.fp = dir(file,((cmdp-commands) == LIST_CMD) ? 1 : 0)) == NULLFILE)
				usprintf(ftp.control,nodir,file,sys_errlist[errno]);
			else
				sendit(&ftp,((cmdp-commands) == LIST_CMD) ? "LIST" : "NLST",file);
			Command->curdirs->dir = cp;
		}
		free(file);
		break;
	case CDUP_CMD:
		sprintf (arg, "..");	/* and fall through */
	case CWD_CMD:
#ifdef old_CALLSERVER
		/* if the requested path contains the CROM drive letter:  */
		if (CDROM != NULLCHAR && strnicmp(CDROM, arg, 2) == 0)  {
			if (strchr(arg, '/') == NULLCHAR) {
				file = (char *)malloc(strlen(arg) + 2);
				sprintf(file,"%s/", arg);
			} else  file = strdup(arg);
			if(!permcheck(ftp.path,ftp.perms,RETR_CMD,file)){
				usprintf(ftp.control,noperm);
				free(file);
#ifdef MSDOS
			/* Don'tcha just LOVE %%$#@!! MS-DOS? - which is what we are running */
			} else if(file[2] == '/' || access(file,0) == 0){
#else
			} else if(access(file,0) == 0){ /* See if it exists */
#endif
				/* Succeeded, record in control block */
				free(ftp.cd);
				ftp.cd = file;
				usprintf(ftp.control,"You may return to your default drive & directory by entering:\n\t\t\"cd %s\"\n\n", ftp.path);
				usprintf(ftp.control,pwdmsg,file);
			} else {
				/* Failed, don't change anything */
				usprintf(ftp.control,nodir,file,sys_errlist[errno]);
				free(file);
			}
			break;
		}
		/* requested path does not contain CDROM drive letter:                      */
		/* if current dir is in CDROM - and a "off-root" is requested:
		   go back to default path.		*/
		if ((CDROM != NULLCHAR && strnicmp(ftp.cd, CDROM, 2) == 0) && (arg[0] == '/'))  {
			free(ftp.cd);
			ftp.cd = strdup(ftp.path);      /* go back to default path            */
		}
#endif  /* #ifdef CALLSERVER  */
		file = addroot(ftp.curdirs->dir,arg);
		if(!permcheck(ftp.path,ftp.perms,RETR_CMD,file))
		 	usprintf(ftp.control,noperm);
		else if (dir_ok(file,ftp.curdirs))	{
			/* Succeeded */
	            /* If exists, send the contents of 'desc.ftp' in the new
        	     * directory...
	             */
        	    strcpy(buf,file);
	            if((fpm = fopen(strcat(buf,"/desc.ftp"),"r")) != NULL) {
        	        sendmsgfile(ftp.control,257,buf,sizeof(buf),fpm);
	                fclose(fpm);
        	    }

			usprintf(ftp.control,pwdmsg,ftp.curdirs->dir);
		} else
			/* Failed, nothing changed */
			usprintf(ftp.control,nodir,arg,sys_errlist[errno]);
		free(file);
		break;
	case XPWD_CMD:
	case PWD_CMD:
		usprintf(ftp.control,pwdmsg,ftp.curdirs->dir);
		break;
#else
	case LIST_CMD:
	case NLST_CMD:
	case CWD_CMD:
	case XPWD_CMD:
	case PWD_CMD:
#endif
	case ACCT_CMD:		
		usprintf(ftp.control,unimp);
		break;
	case DELE_CMD:
		file = addroot(ftp.curdirs->dir,arg);
		if(!permcheck(ftp.path,ftp.perms,DELE_CMD,file)){
		 	usprintf(ftp.control,noperm);
		} else if(unlink(file) == 0){
			log(ftp.control,"DELE %s",file);
			usprintf(ftp.control,deleok);
		} else {
			usprintf(ftp.control,delefail,sys_errlist[errno]);
		}
		free(file);
		break;
	case PASS_CMD:
		if(ftp.username == NULLCHAR)
			usprintf(ftp.control,userfirst);
		else
			ftplogin(&ftp,arg);
		break;
#ifndef	CPM
	case XMKD_CMD:
	case MKD_CMD:
		file = addroot(ftp.curdirs->dir,arg);
		if(!permcheck(ftp.path,ftp.perms,MKD_CMD,file)){
			usprintf(ftp.control,noperm);
#ifdef	UNIX
		} else if(mkdir(file,0777) == 0){
#else
		} else if(mkdir(file) == 0){
#endif
			log(ftp.control,"MKD %s",file);
			usprintf(ftp.control,mkdok);
		} else {
			usprintf(ftp.control,cantmake,file,sys_errlist[errno]);
		}
		free(file);
		break;
	case XRMD_CMD:
	case RMD_CMD:
		file = addroot(ftp.curdirs->dir,arg);
		if(!permcheck(ftp.path,ftp.perms,RMD_CMD,file)){
		 	usprintf(ftp.control,noperm);
		} else if(rmdir(file) == 0){
			log(ftp.control,"RMD %s",file);
			usprintf(ftp.control,deleok);
		} else {
			usprintf(ftp.control,delefail,sys_errlist[errno]);
		}
		free(file);
		break;
	case STRU_CMD:
		if(tolower(arg[0]) != 'f')
			usprintf(ftp.control,unsupp);
		else
			usprintf(ftp.control,okay);
		break;
	case MODE_CMD:
		if(tolower(arg[0]) != 's')
			usprintf(ftp.control,unsupp);
		else
			usprintf(ftp.control,okay);
		break;
	case SYST_CMD:
		usprintf(ftp.control,syst,System,NBBY,Version);
		break;
	case XMD5_CMD:
		file = addroot(ftp.curdirs->dir,arg);
		switch(ftp.type){
		case IMAGE_TYPE:
		case LOGICAL_TYPE:
			mode = READ_BINARY;
			break;
		case ASCII_TYPE:
			mode = READ_TEXT;
			break;
		}
		if(!permcheck(ftp.path,ftp.perms,RETR_CMD,file)){
		 	usprintf(ftp.control,noperm);
		} else if((ftp.fp = fopen(file,mode)) == NULLFILE){
			usprintf(ftp.control,cantopen,file,sys_errlist[errno]);
		} else {
			char hash[16];

			log(ftp.control,"XMD5 %s",file);
			if(ftp.type == ASCII_TYPE && isbinary(ftp.fp))
				usprintf(ftp.control,binwarn,file);

			md5hash(ftp.fp,hash,ftp.type == ASCII_TYPE);
			fclose(ftp.fp);
			ftp.fp = NULLFILE;
			usprintf(ftp.control,"200 ");
			for(i=0;i<16;i++)
				usprintf(ftp.control,"%02x",hash[i] & 0xff);
			usprintf(ftp.control," %s\n",file);
		}
		free(file);
		break;
#ifdef LZW
	case XLZW_CMD:
		if (ftp.lzw)
			usprintf (ftp.control, "550 Already using LZW compression\n");
		else	{
			usprintf (ftp.control, okay);
			sscanf(&buf[5],"%d %d",&ftp.lzwbits,&ftp.lzwmode);
/*			lzwinit (ftp.control, ftp.lzwbits, ftp.lzwmode);	*/
			ftp.lzw = 1;
		}
		break;
#endif
	}
#endif
	goto loop;
finish:

#ifdef FTPTDISC
    stop_timer(&ftp.tdisc);
#endif

    log(ftp.control,"close FTP from '%s'", ftp.username);
    FtpUsers--;
	/* Clean up */
	close_s(ftp.control);
	if(ftp.data != -1)
		close_s(ftp.data);
	if(ftp.fp != NULLFILE)
		fclose(ftp.fp);
	free(ftp.username);
	free(ftp.path);
	free_dirs(&dirs);
	free(rnfrom);
}

/* Shut down FTP server */
int
ftp0(argc,argv,p)
int argc;
char *argv[];
void *p;
{
	return (deleteserver (&Sftp));
}
static
int
pport(sock,arg)
struct sockaddr_in *sock;
char *arg;
{
	int32 n;
	int i;

	n = 0;
	for(i=0;i<4;i++){
		n = atoi(arg) + (n << 8);
		if((arg = strchr(arg,',')) == NULLCHAR)
			return -1;
		arg++;
	}
	sock->sin_addr.s_addr = n;
	n = atoi(arg);
	if((arg = strchr(arg,',')) == NULLCHAR)
		return -1;
	arg++;
	n = atoi(arg) + (n << 8);
	sock->sin_port = n;
	return 0;
}

/* Attempt to log in the user whose name is in ftp->username and password
 * in pass
 */
static void
ftplogin(ftp,pass)
struct ftpserv *ftp;
char *pass;
{
	char *path, buf[128], *cp;
	char *p;
	long t;
	FILE *fp;
	int anony = 0;

	path = mallocw(200);
	if((ftp->perms = userlogin(ftp->username,pass,&path,200,&anony))
	   == -1){
		log(ftp->control,"FTP login refused - '%s'", ftp->username);
		usprintf(ftp->control,noperm);
		free(path);
		return;
	}
	/* Set up current directory and path prefix */
#if	defined(AMIGAGONE)
	ftp->path = strdup(ftp->cd);
	free(path);
#else
	ftp->path = strdup(path);
#endif

	{
	FILE *out;
		sprintf(buf,"%s/ftp.log",Spool);
		if((out = fopen(buf,APPEND_TEXT)) != NULLFILE)	{
			time_t t;
			time(&t);
			fprintf (out, "FTP from %s (%s) on %s", ftp->username, pass, ptime(&t));
			fclose (out);
		}
	}

       time(&t);
       cp = ctime(&t);
       usprintf(ftp->control,banner1,cp);
       usprintf(ftp->control,banner2,Hostname,FtpUsers, FtpMaxUsers);
 
	/* everyone gets the ftpmotd file, if it exists */
     if((fp = fopen(Ftpmotd,"r")) != NULL) {
	 sendmsgfile(ftp->control,230,buf,sizeof(buf),fp);
         fclose(fp);
     }

	/* if there is a message.ftp file in the login dir, send it too */
    sprintf(buf,"%s%s%s", path, (path[strlen(path)-1] == '/') ? "" : "/", "message.ftp");
    if((fp = fopen(buf,"r")) != NULL) {
        sendmsgfile(ftp->control,230,buf,sizeof(buf),fp);
        fclose(fp);
    }

	path = strdup (ftp->path);
	if ((p = strpbrk (path, "=;")) != NULLCHAR)
		*p = 0;
	if (dir_ok(path,ftp->curdirs))		{
		/* Succeeded */
		/* If exists, send the contents of 'desc.ftp' in the new directory... */
		sprintf (buf, "%s/desc.ftp", path);
		if((fp = fopen(buf,"r")) != NULL) {
        	        sendmsgfile(ftp->control,230,buf,sizeof(buf),fp);
       		        fclose(fp);
		}
	}

	free (path);
	if(!anony){
		usprintf(ftp->control,logged);
		log(ftp->control,"FTP login - '%s'",ftp->username);
	} else {
		usprintf(ftp->control,loggeda);
		log(ftp->control,"FTP anonymous login - '%s' (%s)",ftp->username,pass);
	}
	usflush (ftp->control);
	pwait (NULL);
}

#ifdef	MSDOS
/* Illegal characters in a DOS filename */
static char badchars[] = "\"[]|<>+=;,";
#endif

/* Return 1 if the file operation is allowed, 0 otherwise */
int
permcheck(path,perms,op,file)
char *path;
int perms;
int op;
char *file;
{
	char *cp, *cp1;
	int newperms;

	if(file == NULLCHAR || path == NULLCHAR)
		return 0;	/* Probably hasn't logged in yet */

/* To get to the CDROM - EVERYBODY gets read privs, regardless of what
  /ftpusers has to say about it!!! - kb7yw */

#ifdef CALLSERVER
      if (CDROM != NULLCHAR && strnicmp(file, CDROM ,2) == 0) {
      /* Check for characters illegal in MS-DOS file names */
      for(cp = badchars;*cp != '\0';cp++){
        if(strchr(&file[2],*cp) != NULLCHAR)
        return 0;
      }

        switch(op){ /* What to do when the user is on the cd-rom drive      */
        case RETR_CMD: /* Everybody gets read privs regardless of ftpusers  */
          /* User has permission to read files */
            return 1;
        case DELE_CMD:
        case RMD_CMD:
          /* User must not have permission to (over)write files */
        case STOR_CMD:
        case MKD_CMD:
          /* User must NOT have permission to (over)write files
           */
          return 0;
        }   /* switch(op) */
      }     /* if strncmp(....  */
#endif    /* #ifdef CALLSERVER  */
#ifndef MAC
	/* The target file must be under the user's allowed search path */
	/* We let them specify multiple paths using path;path... -russ */
	for(cp = path;;cp = cp1+1){
		char *cp2;
		newperms = perms;
		if((cp1 = strchr(cp,';')) == NULLCHAR)	cp1 = strchr(cp,'\0');
		if (((cp2 = strchr(cp, '=')) == NULLCHAR) || (cp2 > cp1))
			cp2 = cp1;
		else
			newperms = atoi (cp2+1);
		if(!strnicmp(file,cp,(unsigned)(cp2 - cp))
#ifdef MSDOS	/* start past the X: drive letter */
			|| !strnicmp(file+2,cp,cp2 - cp)
#endif
			)	{
			perms = newperms;
			break;
		}
		if(*cp1 == '\0')	return 0;
	}
#endif

#ifdef	MSDOS
	/* Check for characters illegal in MS-DOS file names */
	for(cp = badchars;*cp != '\0';cp++){
		if(strchr(file,*cp) != NULLCHAR)
			return 0;	
	}
#endif

	switch(op){
	case RNFR_CMD:
	case RNTO_CMD:
	case RETR_CMD:
		/* User must have permission to read files */
		if(perms & FTP_READ)
			return 1;
		return 0;
	case DELE_CMD:
	case RMD_CMD:
	case RPUT_CMD:
	case APPE_CMD:
		/* User must have permission to (over)write files */
		if(perms & FTP_WRITE)
			return 1;
		return 0;
	case STOR_CMD:
	case MKD_CMD:
		/* User must have permission to (over)write files, or permission
		 * to create them if the file doesn't already exist
		 */
		if(perms & FTP_WRITE)
			return 1;
		if(access(file,2) == -1 && (perms & FTP_CREATE))
			return 1;
		return 0;
	}
	return 0;	/* "can't happen" -- keep lint happy */
}
static int
sendit(ftp,command,file)
struct ftpserv *ftp;
char *command;
char *file;
{
	long total, starting;
	unsigned long check;
	struct sockaddr_in dport;
	char *cp;
	char fsizetext[20];                /* N1BEE */

	fsizetext[0] = 0;
	ftp->data = socket(AF_INET,SOCK_STREAM,0);
	dport.sin_family = AF_INET;
	dport.sin_addr.s_addr = INADDR_ANY;
	dport.sin_port = IPPORT_FTPD;
	bind(ftp->data,(char *)&dport,SOCKSIZE);
	sprintf(fsizetext, "(%lu bytes)", filelength(fileno(ftp->fp)));
	usprintf(ftp->control,sending,command,file,fsizetext);  /* N1BEE */
	if(connect(ftp->data,(char *)&ftp->port,SOCKSIZE) == -1){
		fclose(ftp->fp);
		ftp->fp = NULLFILE;
		close_s(ftp->data);
		ftp->data = -1;
		usprintf(ftp->control,noconn);
		return -1;
	}
	if(strcmp(command,"RSME") == 0) {
#ifdef __GNUC__
	    total = -1;
#endif
		cp = mallocw(40);
		recvline(ftp->control,cp,40);
		starting = atol(cp);
		/* If checksum field is not present go on anyway, for compatibility
		 * with previous scheme. If present check it and barf if wrong.
		 */
		if(strchr(cp,' ') != NULL){
			check = (unsigned long)atol(strchr(cp,' '));
			check -= checksum(ftp->fp,starting);
			if(check != 0){
				free(cp);
				usprintf(ftp->control,badcheck);
				shutdown(ftp->data,1);  /* Blow away data connection */
				goto send_err;
			}
		} else if(fseek(ftp->fp,starting,SEEK_SET) != 0) {
			free(cp);
			usprintf(ftp->control,noconn);
			shutdown(ftp->data,2);  /* Blow away data connection */
			goto send_err;
		}
	}

#ifdef FTPTDISC
    /* Turn off the timeout timer here, some ftp's could
     * take a long time with sloooow packet channels - WG7J
     */
    stop_timer(&ftp->tdisc);
#endif
	
#ifdef LZW
	if (ftp->lzw)
		lzwinit (ftp->data, ftp->lzwbits, ftp->lzwmode);	
#endif

	/* Do the actual transfer */
	total = sendfile(ftp->fp,ftp->data,ftp->type,0);

#ifdef FTPTDISC
    /* And turn it back on now */
    start_timer(&ftp->tdisc);
#endif

	if(total == -1){
		/* An error occurred on the data connection */
		usprintf(ftp->control,noconn);
		shutdown(ftp->data,2);  /* Blow away data connection */
	} else {
		usprintf(ftp->control,txok);
	}
send_err:       fclose(ftp->fp);
	ftp->fp = NULLFILE;
	close_s(ftp->data);
	ftp->data = -1;
	if(total == -1)
		return -1;
	else
		return 0;
}
static int
recvit(ftp,command,file)
struct ftpserv *ftp;
char *command;
char *file;
{
	struct sockaddr_in dport;
	long total, starting;

	ftp->data = socket(AF_INET,SOCK_STREAM,0);
	dport.sin_family = AF_INET;
	dport.sin_addr.s_addr = INADDR_ANY;
	dport.sin_port = IPPORT_FTPD;
	bind(ftp->data,(char *)&dport,SOCKSIZE);
	usprintf(ftp->control,sending,command,file,"");

	if(connect(ftp->data,(char *)&ftp->port,SOCKSIZE) == -1){
		fclose(ftp->fp);
		ftp->fp = NULLFILE;
		close_s(ftp->data);
		ftp->data = -1;
		usprintf(ftp->control,noconn);
		return -1;
	}
	if (strcmp(command, "APPE") == 0)
		fseek(ftp->fp,0,SEEK_END);
	if(strcmp(command,"RPUT") == 0){
		if((starting = getsize(ftp->fp)) == -1)
			starting = 0L;
		usprintf(ftp->control,"%lu %lu\n",starting,checksum(ftp->fp,starting));
		fseek(ftp->fp,starting,SEEK_SET);
	}

#ifdef FTPTDISC
    /* Turn of the timeout timer here; some ftp's could
     * take a long time with sloooow packet channels - WG7J
     */
    stop_timer(&ftp->tdisc);
#endif

#ifdef LZW
	if (ftp->lzw)
		lzwinit (ftp->data, ftp->lzwbits, ftp->lzwmode);	
#endif

    /* Do the actual transfer */
    total = recvfile(ftp->fp,ftp->data,ftp->type,0);

#ifdef FTPTDISC
    /* And turn it back on now */
    start_timer(&ftp->tdisc);
#endif

#ifdef	CPM
	if(ftp->type == ASCII_TYPE)
		putc(CTLZ,ftp->fp);
#endif
	if(total == -1) {
		/* An error occurred while writing the file */
		usprintf(ftp->control,writerr,sys_errlist[errno]);
		shutdown(ftp->data,2);	/* Blow it away */
	} else {
		usprintf(ftp->control,rxok);
		close_s(ftp->data);
	}
	ftp->data = -1;
	fclose(ftp->fp);
	ftp->fp = NULLFILE;
	if(total == -1)
		return -1;
	else
		return 0;
}
