#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <error.h>
#include <stat.h>
#include <dos.h>
#include <sys/ioctl.h>
#include <sys/dir.h>
#include <lineread.h>
#include <sys/socket.h>
#include <exec/memory.h>
#include <dos/dostags.h>
#include <libraries/multiuser.h>

#include <proto/exec.h>
#include <proto/socket.h>
#include <proto/multiuser.h>
#include <proto/dos.h>

#include "ftp.h"

struct muBase *muBase;
extern LONG server_socket;

char VersionTag[]="\0$VER: JoranFtpd 2.0 (14.12.93)";

char Hostname[40]="unknown";
char Version[]="Joran 2.0";

char buf2[512];
int tt1, tt2, tnw;
int pdata=-1;				// For passive mode
struct FileInfoBlock *PermChkFib;
struct muUserInfo *UserInfo;

long gethostname(char *,long len);

struct LineRead *LineRead;

/* 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",
	"pasv",			/* Pasive mode */
	NULL
};

/* Response messages */
static char binwarn[]   = "100 Warning: File %s appears to be BINARY\n";
static char sending[]   = "150 Open %s for %s %s (%ld Bytes)\n";
static char recving[]   = "150 Open %s for %s %s\n";

static char typeok[]    = "200 Type %s OK\n";
static char mkdok[]     = "200 MKD ok\n";
static char portok[]    = "200 Port command okay\n";
static char okay[]      = "200 Ok\n";
static char banner[]    = "220 %s FTP Ready - v%s\n";
static char bye[]       = "221 Goodbye!\n";
static char rxok[]      = "226 File received OK\n";
static char txok[]      = "226 File sent OK\n";
static char logged[]    = "230 %s Logged in, %s\n";
static char loggeda[]   = "230 %s Logged in as anonymous, %s\n";
static char deleok[]    = "250 File deleted\n";
static char pwdmsg[]    = "257 \"%s\" is current directory\n";

static char givepass[]  = "331 Enter Password\n";
static char givepasa[]  = "331 Please enter your email address '<user-id>@<host-name>'\n";

static char lowmem[]    = "421 System overloaded, try again later\n";
static char noconn[]    = "425 Data connection reset\n";

static char unsupp[]    = "500 Unsupported command or option\n";
static char badcmd[]    = "500 Unknown command\n";
static char only8[]     = "501 Only logical bytesize 8 supported\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 userfirst[] = "503 Login with USER first.\n";
static char notlog[]    = "530 Please log in with USER and PASS\n";
static char noperm[]    = "550 Permission denied\n";
static char cantopen[]  = "550 Can't read file \"%s\": %s\n";
static char delefail[]  = "550 Delete failed: %s\n";
static char writerr[]   = "552 Write error: %s\n";
static char nodir[]     = "553 Can't read directory \"%s\": %s\n";
static char cantmake[]  = "553 Can't create \"%s\": %s\n";
static char notyet[]    = "555 Try Later : Access '%2d:00-%2d:59 %s' only\n";
static char nopasv[]		= "425 Can't open passive connection\n";
static char pasvcon[]		=	"227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\n";

void CloseAll(void)
{
	if(UserInfo) muFreeUserInfo(UserInfo);
	if(PermChkFib) FreeDosObject(DOS_FIB,PermChkFib);
	if(LineRead) FreeMem(LineRead,sizeof(struct LineRead));
	if(muBase) CloseLibrary((struct Library *)muBase);
	
	if(LogLevel>=20) AddLog("FTPDeamon Stopped",FindTask(NULL));
}

void InitAll(void)
{
	atexit(CloseAll);
	
	SetProgramName("JoranFtpd");
	muBase=(struct muBase *)OpenLibrary(MULTIUSERNAME,MULTIUSERVERSION);
	if(muBase && !(UserInfo=muAllocUserInfo())) exit(20);
	gethostname(Hostname,sizeof(Hostname));
	if(!(LineRead=AllocMem(sizeof(struct LineRead),MEMF_ANY))) exit(20);
	initLineRead(LineRead,server_socket,RL_LFREQLF,RL_BUFSIZE);
	if(!(PermChkFib=AllocDosObject(DOS_FIB,NULL))) exit(20);
}

/* replace terminating end of line marker(s) with null */
void rip(char *s)
{
	char *cp;
	if((cp = strchr(s,'\n')) != NULL) *cp = '\0';
	if((cp = strchr(s,'\r')) != NULL) *cp = '\0';
}

/* This checks the current time is in a preset Time-Window before
   allowing a user to login */
int timewind(int argc,char *argv[],void *p)
{
	char *now;
	long t;
	char *t1, *t2;

	t1 = strdup(curftwin);
	*(t1 + 2) = '\0';
	tt1 = atoi(t1);

	t2 = strdup(curftwin+2);
	*(t2 + 2) = '\0';
	tt2 = atoi(t2);

	time(&t);
	now = ctime(&t) + 11;
	*(now + 2) = '\0';
	tnw = atoi(now);

	if(tt1 > tt2) {
		if(((tnw >= tt1) && (tnw <= 23)) ||
		   ((tnw <= tt2) && (tnw >= 0)))
			return 0;		/* right in time */
	} else {
		if((tnw >= tt1) && (tnw <= tt2))
			return 0;		/* right in time */
	}

	return 1;
}

/* Return 1 if the file operation is allowed, 0 otherwise */
int permcheck(char *path,int perms,int op,char *file)
{
	BPTR l;
	BYTE prot;
	
	if(file == NULL || path == NULL)
		return 0;	/* Probably hasn't logged in yet */

	/* The target file must be under the user's allowed search path */
	if(strncmp(file,path,strlen(path)) != 0)
		return 0;

	prot=-1;
//	if(muBase & perms&FTP_MULTIUSER)
//	{
		// Check default protection
//		if(l=Lock(file,ACCESS_READ))
//		{
//			if(Examine(l,PermChkFib))
//			{
//				if(UserInfo->uid==muNOBODYUID)
//				{
//					
//				}
//				if(UserInfo->uid==PermChkFib->fib_OwnerUID)
//				{
//				}
//				if(UserInfo->gid==PermChkFib->fib_GroupUID)
//				{
//				}
//				
//			}
//			UnLock(l);
//		}
//	}
	
	if(l=Lock(file,ACCESS_READ)) UnLock(l);
	
	switch(op){
		case RETR_CMD:
			/* User must have permission to read files */
			if(perms&FTP_READ && prot&FIBF_READ)
				return 1;
			return 0;
		case DELE_CMD:
		case RMD_CMD:
			/* User must have permission to (over)write files */
			if(perms&FTP_WRITE && prot&FIBF_DELETE)
				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(!l && perms&FTP_CREATE) return 1;
			if(l && perms&FTP_WRITE && prot&FIBF_DELETE)
				return 1;
			return 0;
	}
	return 0;	/* "can't happen" -- keep lint happy */
}

/* Subroutine for logging in the user whose name is name and
   password is pass.  The buffer path should be long enough to
   keep a line from the userfile.  If pwdignore is true, the
   password check will be overridden.  The return value is the
   permissions field or -1 if the login failed.  Path is set to
   point at the path field, and pwdignore will be true if no
   particular password was needed for this user.
 */
int userlogin(char *name,char *pass,char **path,int len,int *pwdignore)
{
	char *cp,*cp1;
	FILE *fp;
	char *rest = NULL;
	int anony, perm;
	int unknown=FALSE;

	*pwdignore=!strcmp("ftp",name);
	anony = *pwdignore;

	if((fp = fopen(Userfile,"r")) == NULL) return -1; /* Userfile doesn't exist */
		
	while(fgets(*path,len,fp),!feof(fp))
	{
		if(*path[0] == '#') continue;	/* Comment */
		if((cp=strchr(*path,' '))==NULL) continue; /* Bogus entry */
		*cp++ = '\0'; /* Now points to password */
		
		if(stricmp(name,*path)==0) break; /* Found user name */
		if(stricmp("rest",*path)==0) rest=strdup(cp); /* remember default entry */
	}
	unknown=feof(fp);
	if((rest==NULL) && unknown) 
	{
		/* User not in file, nor was anonymous */
		fclose(fp);
		return -1;
	}
 	if(unknown)
	{
		/* restore default entry */
		strcpy(cp = *path, rest);
	}
	fclose(fp);
	
	/* Look for space after password field in file */
	if((cp1 = strchr(cp,' ')) == NULL) return -1; /* Invalid file entry */
		
	*cp1++ = '\0';	/* Now points to path field */
	if(strcmp(cp,"*")!=0)
	{
		if(strcmp(cp,pass)!=0) return -1; /* Password required, but wrong one given */
	}
	if((cp = strchr(cp1,' ')) == NULL) return -1; /* Permission field missing */
		
	*cp++ = '\0';	/* now points to permission field */
	perm = atoi(cp);
	
	if(unknown && (!muBase || !perm&FTP_MULTIUSER)) return -1;
	
	if(muBase && perm&FTP_MULTIUSER)
	{
		strncpy(UserInfo->UserID,name,muUSERIDSIZE);
		if(!muGetUserInfo(UserInfo,muKeyType_UserID))
		{
			if(LogLevel>=10) AddLog("Unknown User \"%s\"",name);
			return -1;
		}
		if(!muLogin(
			muT_UserID,UserInfo->UserID,
			muT_Password,anony?"":pass,
			TAG_DONE
		))
		{
			if(LogLevel>=10) AddLog("Wrong password for \"%s\"",UserInfo->UserID);
			return -1;
		}
	}
	
/*
 *  Well, on the Amiga, a file can be referenced by many names:
 *  device names (DF0:) or volume names (My_Disk:).  This hunk of code
 *  passed the pathname specified in the ftpusers file, and gets the
 *  absolute path copied into the user's buffer.  We really should just
 *  allocate the buffer and return a pointer to it, since the caller
 *  really doesn't have a good idea how long the path string is..
 */
	if(cp1[0]=='*') cp1=strdup("");
	else cp1=pathname("",cp1);
	
	if (cp1) strcpy(*path, cp1);
	else **path = '\0';
	free(cp1);
	
	/* Finally return the permission bits */
	return perm;
}

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

	path = malloc(200);
	if((ftp->perms=userlogin(ftp->username,pass,&path,200,&anony)) == -1)
	{
		usprintf(ftp->control,noperm);
		free(path);
		return;
	}
	/* Set up current directory and path prefix */
	if(*path) ftp->cd=strdup(path);
	else
	{
		if(muBase) ftp->cd=pathname(UserInfo->HomeDir,"");
		else ftp->cd=pathname(":","");
	}
	ftp->path = strdup(path);
	free(path);

	sprintf(buf2,"%sFTP.After",SignOn);
	if((fp = fopen(buf2,"r")) != NULL)
	{
		sendfile(fp,ftp->control,ASCII_TYPE,0);
		fclose(fp);
	}

	time(&t);
	cp = ctime(&t);
	if((cp1 = strchr(cp,'\n')) != NULL) *cp1 = '\0';

	if(!anony)
	{
		usprintf(ftp->control,logged,ftp->username,cp);
		if(LogLevel>=15) AddLog("%s logged in",ftp->username);
	} 
	else 
	{
		usprintf(ftp->control,loggeda,
			strcmp(ftp->username,"ftp") ? 
			ftp->username : "",cp);
		if(LogLevel>=15) AddLog("%s logged in, ID %s",ftp->username,pass);
	}
}		

static int sendit(struct ftpserv *ftp,char *command,char *file)
{
	long total;
	struct sockaddr_in dport;
	struct stat stb;

	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,(struct sockaddr *)&dport,sizeof(struct sockaddr_in));
	
	stat(file,&stb);
	usprintf(ftp->control,sending,file,ftp->type ? "BINARY" : "ASCII",command,stb.st_size);
	if(connect(ftp->data,(struct sockaddr *)&ftp->port,sizeof(struct sockaddr_in)) == -1){
		fclose(ftp->fp);
		ftp->fp = NULL;
		CloseSocket(ftp->data);
		ftp->data = -1;
		ftp->pdata = -1;
		usprintf(ftp->control,noconn);
		return -1;
	}
//#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
	/* 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);

	fclose(ftp->fp);
	ftp->fp = NULL;
	CloseSocket(ftp->data);
	ftp->data = -1;
	ftp->pdata = -1;
	if(total == -1) return -1;
	else return 0;
}

int recvit(struct ftpserv *ftp,char *command,char *file)
{
	struct sockaddr_in dport;
	long total;

	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,(struct sockaddr *)&dport,sizeof(struct sockaddr_in));
	usprintf(ftp->control,recving,file,ftp->type?"BINARY":"ASCII",command);
	if(connect(ftp->data,(struct sockaddr *)&ftp->port,sizeof(struct sockaddr_in)) == -1)
	{
		fclose(ftp->fp);
		ftp->fp = NULL;
		CloseSocket(ftp->data);
		ftp->data = -1;
		ftp->pdata = -1;
		usprintf(ftp->control,noconn);
		return -1;
	}
	
//#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

    /* 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

	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);
		CloseSocket(ftp->data);
	}
	ftp->data = -1;
	ftp->pdata = -1;
	fclose(ftp->fp);
	ftp->fp = NULL;
	if(total == -1) return -1;
	else return 0;
}

static int pport(struct sockaddr_in *sock,char *arg)
{
	int n;
	int i;

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

FILE *dir(char *path,int full)
{
   int ftotal=0, fnumbr=0;
   BPTR FrameLock_p;
	struct FileInfoBlock *fib_p;
   FILE *f;
	char wildcard[40],*p=NULL;
	char parsecard[82];

	if(strchr(path,'*') || strchr(path,'?')) p=FilePart(path);
	if(p)
	{
		strncpy(wildcard,p,sizeof(wildcard));
		wildcard[sizeof(wildcard)-1]='\0';
		p=PathPart(path);
		if(p) *p='\0';
	} else strcpy(wildcard,"*");
	
	if(ParsePatternNoCase(wildcard,parsecard,sizeof(parsecard))<=0) return NULL;

   if((f=tmpfile())==NULL) return NULL;
	
	if(FrameLock_p=Lock(path,ACCESS_READ))
	{
		if (fib_p=(struct FileInfoBlock *)AllocMem(sizeof(struct FileInfoBlock),MEMF_PUBLIC))
		{
			if(Examine(FrameLock_p,fib_p))
			{
				if(fib_p->fib_DirEntryType<0)
					fprintf(f,"You can only use this command with a Directory Name\n");
				else
				{
					if(full)	fprintf(f,"    Size  File/Dir Name      Comment\n");
					while(ExNext(FrameLock_p,fib_p))
					{
						if(MatchPatternNoCase(parsecard,fib_p->fib_FileName))
						{
							if(full)
							{
								if(fib_p->fib_DirEntryType>0)
								{
									fprintf(f,"    (Dir) ");
								}
								else if(fib_p->fib_Size == 0)
								{
									fprintf(f,"  (empty) ");
								}
								else
								{
									ftotal=ftotal+fib_p->fib_Size;
									fnumbr++; 
									fprintf(f,"%8d  ",fib_p->fib_Size);
								}
							
								fprintf(f,"%-16s", fib_p->fib_FileName);
								fprintf(f,strlen(fib_p->fib_Comment) ? " : %-30s\n" : "\n",
									fib_p->fib_Comment);
							}
							else if(fib_p->fib_DirEntryType<0) fprintf(f,"%s\n", fib_p->fib_FileName);
						}
					}
					if(full)
						fprintf(f, "Directory %s : %d Bytes : %d Files\n", path, ftotal, fnumbr);
				}
			}
			FreeMem(fib_p,sizeof(struct FileInfoBlock));
		}
		UnLock(FrameLock_p);
   }
	
	rewind(f);

	return f;	
}

FILE *extdir(char *arg)
{
	BPTR f;
	FILE *fp;
	char fn[30];
	
	sprintf(fn,"T:JoranFtpd%08X",FindTask(NULL));
	
	if(*arg) sprintf(buf2,ArgDirCommand,arg);
	else sprintf(buf2,DefDirCommand,arg);
	if(LogLevel>=30) AddLog("%s",buf2);
	if(f=Open(fn,MODE_NEWFILE))
	{
		SystemTags(buf2,
			SYS_Output,f,
			TAG_DONE
		);
		Close(f);
		if(fp=fopen(fn,"r")) return fp;
	}
	
	return NULL;
}

/* Note: a response of 425 is not mentioned as a possible response to the
 * PASV command in RFC959. However, it has been blessed as a legitimate
 * response by Jon Postel in a telephone conversation with Rick Adams on 25
 * Jan 89. */
void passive(struct ftpserv *ftp)
{
	LONG len;
	register char *p,*a;

	ftp->pdata = socket(AF_INET, SOCK_STREAM, 0);
	if (ftp->pdata < 0) {
		usprintf(ftp->control,nopasv);
		return;
	}
	ftp->pasv = ftp->port;
	ftp->pasv.sin_port = 0;
	// (void) seteuid((uid_t) 0);
	if (bind(ftp->pdata, (struct sockaddr *) &ftp->pasv, sizeof(ftp->pasv)) < 0) {
		// (void) seteuid((uid_t) pw->pw_uid);
		goto pasv_error;
	}
	//(void) seteuid((uid_t) pw->pw_uid);
	len = sizeof(ftp->pasv);
	if (getsockname(ftp->pdata, (struct sockaddr *) &ftp->pasv, &len) < 0)
		goto pasv_error;
	if (listen(ftp->pdata, 1) < 0)
		goto pasv_error;
	a = (char *) &ftp->pasv.sin_addr;
	p = (char *) &ftp->pasv.sin_port;

#define UC(b) (((int) b) & 0xff)

	usprintf(ftp->control,pasvcon,
		UC(a[0]),UC(a[1]), UC(a[2]), UC(a[3]), UC(p[0]), UC(p[1]));
	return;

	pasv_error:
		CloseSocket(ftp->pdata);
		ftp->pdata = -1;
		usprintf(ftp->control,nopasv);
	return;
}

main()
{
	struct ftpserv ftp;
	FILE *fp;
	long l,cnt;
	char *buf,*arg;
	char **cmdp,*p;
	char *file,*mode;
	fd_set readfds;
	
	AddLog("FTPDeamon Initialising");
	
	if(server_socket==-1) exit(20);
	
	InitAll();
	ReadConfig();
	
	memset((char *)&ftp,0,sizeof(ftp));	/* Start with clear slate */
	ftp.data=-1;
	ftp.pdata=-1;
	ftp.control=server_socket;
	l=sizeof(struct sockaddr_in);
	getpeername(server_socket,(struct sockaddr *)&ftp.port,&l);
	ftp.port.sin_port=IPPORT_FTPD;
	
	if(LogLevel>=20) AddLog("FTPDeamon started",FindTask(NULL));
	
	sprintf(buf2,"%sFTP.Before",SignOn);
	if((fp=fopen(buf2,"r"))!=NULL)
	{
		sendfile(fp,server_socket,ASCII_TYPE,0);
		fclose(fp);
	}

	usprintf(server_socket,banner,Hostname,Version);

	FD_ZERO(&readfds);
	
	for(;;)
	{
   	FD_SET(server_socket,&readfds);
   	if (select(server_socket+1,&readfds,NULL,NULL,NULL)<0) break;
   	if(!FD_ISSET(server_socket,&readfds)) continue;
   
   	cnt=lineRead(LineRead);
   	
   	if(cnt==0) continue;		// EOF
   	if(cnt==-1)
   	{
   		if(LineRead->rl_Line==NULL) goto finish;
   		continue;
   	}
	
   	buf=LineRead->rl_Line;
   	rip(buf);
		
		AddLog("%s",buf);
	
   	/* Find command in table; if not present, return syntax error */
   	for(cmdp=commands;*cmdp != NULL;cmdp++)
   		if(strnicmp(*cmdp,buf,strlen(*cmdp)) == 0)
   			break;
   	if(*cmdp == NULL){
   		usprintf(ftp.control,badcmd);
   		continue;
   	}
   	/* Allow only USER, PASS, QUIT before logging in */
   	if(ftp.cd == NULL || ftp.path == NULL){
   		switch(cmdp-commands){
   		case USER_CMD:
   		case PASS_CMD:
   		case QUIT_CMD:
   			break;
   		default:
   			usprintf(ftp.control,notlog);
   			continue;
   		}
   	}
   	arg = &buf[strlen(*cmdp)];
   	while(*arg == ' ') arg++;
   		
   	switch(cmdp-commands)
   	{
   		case USER_CMD:
   			if(timewind(NULL,NULL,NULL)) {
   				usprintf(ftp.control,notyet,tt1,tt2,
   					((p = getenv("TZ")) == NULL) ? "GMT" : p);
   				goto finish;
   			}
   			free(ftp.username);
   			if(!strcmp("anonymous",arg)) ftp.username=strdup("ftp");
   			else ftp.username=strdup(arg);
   			if(!stricmp("ftp",ftp.username))	usprintf(ftp.control,givepasa);
   			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 RETR_CMD:
   			if(timewind(NULL,NULL,NULL))
   			{
   				usprintf(ftp.control,notyet,tt1,tt2,
   					((p = getenv("TZ")) == NULL) ? "GMT" : p);
   				goto finish;
   			}
   			file=pathname(ftp.cd,arg);
   			switch(ftp.type) {
   				case IMAGE_TYPE:
   				case LOGICAL_TYPE:
   					mode = "rb";
   					break;
   				case ASCII_TYPE:
   					mode = "r";
   					break;
   			}
   			if(!permcheck(ftp.path,ftp.perms,RETR_CMD,file))
   			 	usprintf(ftp.control,noperm);
   			else if((ftp.fp = fopen(file,mode)) == NULL)
   				usprintf(ftp.control,cantopen,file,sys_errlist[errno]);
   			else
   			{
   				if(LogLevel>=10) AddLog("RETR %s by %s",file,ftp.username);
   				if(ftp.type == ASCII_TYPE && isbinary(ftp.fp)){
   					usprintf(ftp.control,binwarn,file);
   				}
   				sendit(&ftp,"RETR",file);
   			}
   			free(file);
   			break;
   		case STOR_CMD:
   			if(timewind(NULL,NULL,NULL)) {
   				usprintf(ftp.control,notyet,tt1,tt2,
   					((p = getenv("TZ")) == NULL) ? "GMT" : p);
   				goto finish;
   			}
   			file = pathname(ftp.cd,arg);
   			switch(ftp.type){
   				case IMAGE_TYPE:
   				case LOGICAL_TYPE:
   					mode = "wb";
   					break;
   				case ASCII_TYPE:
   					mode = "w";
   					break;
   			}
   			if(!permcheck(ftp.path,ftp.perms,STOR_CMD,file))
   			 	usprintf(ftp.control,noperm);
   			else if((ftp.fp = fopen(file,mode)) == NULL)
   				usprintf(ftp.control,cantmake,file,sys_errlist[errno]);
   			else 
   			{
   				if(LogLevel>=5) AddLog("STOR %s by %s",file,ftp.username);
   				recvit(&ftp,"STOR",file);
   			}
   			free(file);
   			break;
   		case PORT_CMD:
   			if(pport(&ftp.port,arg) == -1) usprintf(ftp.control,badport);
   			else usprintf(ftp.control,portok);
   			break;
   		case LIST_CMD:
   			if(DefDirCommand && ArgDirCommand && !(ftp.perms&FTP_NOEXTDIR))
   			{
   				/* User external dir-command, you lose some protection when
   				   using this option */
   				if(chdir(ftp.cd)) usprintf(ftp.control,nodir,file,sys_errlist[errno]);
   				else if((ftp.fp=extdir(arg))==NULL) usprintf(ftp.control,nodir,file,sys_errlist[errno]);
   				else 	sendit(&ftp,"LIST",DefDirCommand);
   			}
   			else
   			{
   				file = pathname(ftp.cd,arg);
   				if(!permcheck(ftp.path,ftp.perms,RETR_CMD,file)) usprintf(ftp.control,noperm);
   				else if((ftp.fp = dir(file,1)) == NULL) usprintf(ftp.control,nodir,file,sys_errlist[errno]);
   				else sendit(&ftp,"LIST",file);
   				free(file);
   			}
   			break;
   		case NLST_CMD:
   			file = pathname(ftp.cd,arg);
   			if(!permcheck(ftp.path,ftp.perms,RETR_CMD,file))
   				usprintf(ftp.control,noperm);
   			else if((ftp.fp = dir(file,0)) == NULL)
   				usprintf(ftp.control,nodir,file,sys_errlist[errno]);
   			else sendit(&ftp,"NLST",file);
   			free(file);
   			break;
   		case PASS_CMD:
   			if(ftp.username == NULL) usprintf(ftp.control,userfirst);
   			else
   			{
   				if(!strcmp(arg,"anonymous")) arg="ftp";
   				ftplogin(&ftp,arg);
   			}
   			break;
   		case CWD_CMD:
   			if(!strncmp(arg,"..",2)) arg="/";
   			file = pathname(ftp.cd,arg);
   			if(!permcheck(ftp.path,ftp.perms,RETR_CMD,file))
   			{
   			 	usprintf(ftp.control,noperm);
   				free(file);
   			} 
   			else if(access(file,0) == 0)
   			{	/* See if it exists */
   				/* Succeeded, record in control block */
   				free(ftp.cd);
   				ftp.cd = file;
   				sprintf(buf2,"%s/FTP.Dir",file);
   				if((fp=fopen(buf2,"r"))!=NULL) 
   				{
   					sendfile(fp,server_socket,ASCII_TYPE,0);
   					fclose(fp);
   				}
   				usprintf(ftp.control,pwdmsg,file);
   			} 
   			else 
   			{
   				/* Failed, don't change anything */
   				usprintf(ftp.control,nodir,file,sys_errlist[errno]);
   				free(file);
   			}
   			break;
   		case XPWD_CMD:
   		case PWD_CMD:
   			usprintf(ftp.control,pwdmsg,ftp.cd);
   			break;
   		case XMKD_CMD:
   		case MKD_CMD:
   			file = pathname(ftp.cd,arg);
   			if(!permcheck(ftp.path,ftp.perms,MKD_CMD,file))
   				usprintf(ftp.control,noperm);
   			else if(mkdir(file) == 0)
   			{
   				if(LogLevel>=5) AddLog("MKD %s by %s",file,ftp.username);
   				usprintf(ftp.control,mkdok);
   			} 
   			else usprintf(ftp.control,cantmake,file,sys_errlist[errno]);
   			free(file);
   			break;
   		case XRMD_CMD:
   		case RMD_CMD:
   			file = pathname(ftp.cd,arg);
   			if(!permcheck(ftp.path,ftp.perms,RMD_CMD,file))
   			 	usprintf(ftp.control,noperm);
   			else if(rmdir(file) == 0)
   			{
   				if(LogLevel>=5) AddLog("RMD %s by %s",file,ftp.username);
   				usprintf(ftp.control,deleok);
   			} 
   			else usprintf(ftp.control,delefail,sys_errlist[errno]);
   			free(file);
   			break;
   		case ACCT_CMD:		
   			usprintf(ftp.control,unimp);
   			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 DELE_CMD:
   			file = pathname(ftp.cd,arg);
   			if(!permcheck(ftp.path,ftp.perms,DELE_CMD,file))
   			 	usprintf(ftp.control,noperm);
   			else if(unlink(file) == 0)
   			{
   				if(LogLevel>=5) AddLog("DELE %s by %s",file,ftp.username);
   				usprintf(ftp.control,deleok);
   			} 
   			else usprintf(ftp.control,delefail,sys_errlist[errno]);
   			free(file);
   			break;
//			case PASV_CMD:
//				passive(&ftp);
//				break;
   	}
	}
	   	
finish:
	free(ftp.username);
	free(ftp.path);
	free(ftp.cd);
	
	exit(0);
}
