Skip to content
Snippets Groups Projects
ftpsrvr.c 127 KiB
Newer Older

		if((xfer=malloc(sizeof(xfer_t)))==NULL) {
			lprintf("%04d !MALLOC FAILURE LINE %d",ctrl_sock,__LINE__);
			sockprintf(ctrl_sock,"425 MALLOC FAILURE");
			break;
		}
		memset(xfer,0,sizeof(xfer_t));
		xfer->ctrl_sock=ctrl_sock;
		xfer->data_sock=data_sock;
		xfer->inprogress=inprogress;
		xfer->aborted=aborted;
		xfer->delfile=delfile;
		xfer->tmpfile=tmpfile;
		xfer->append=append;
		xfer->filepos=filepos;
		xfer->credits=credits;
		xfer->lastactive=lastactive;
		xfer->user=user;
		xfer->dir=dir;
		xfer->desc=desc;
		SAFECOPY(xfer->filename,filename);
		if(receiving)
			result=_beginthread(receive_thread,0,(void*)xfer);
		else
			result=_beginthread(send_thread,0,(void*)xfer);

		if(result!=-1)
			return;	/* success */

	} while(0);

	/* failure */
	if(tmpfile)
		remove(filename);
	*inprogress=FALSE;
/* convert "user name" to "user.name" or "mr. user" to "mr._user" */
char* dotname(char* in, char* out)
{
	if(strchr(in,'.')==NULL)
		ch='.';
	else
		ch='_';
	for(i=0;in[i];i++)
		if(in[i]<=' ')
		else
			out[i]=in[i];
	out[i]=0;
	return(out);
}

void parsepath(char** pp, user_t* user, int* curlib, int* curdir)
{
	char*	p;
	char*	tp;
	int		dir=*curdir;
	int		lib=*curlib;

	SAFECOPY(path,*pp);
	p=path;

	if(*p=='/') {
		p++;
		lib=-1;
	}
	else if(!strncmp(p,"./",2))
		p+=2;

	if(!strncmp(p,"..",2)) {
		p+=2;
		if(dir>=0)
			dir=-1;
		else if(lib>=0)
			lib=-1;
		if(*p=='/')
			p++;
	}

	if(*p==0) {
		*curlib=lib;
		*curdir=dir;
		return;
	}

	if(lib<0) { /* root */
		tp=strchr(p,'/');
		if(tp) *tp=0;
		for(lib=0;lib<scfg.total_libs;lib++) {
			if(!chk_ar(&scfg,scfg.lib[lib]->ar,user))
				continue;
			if(!stricmp(scfg.lib[lib]->sname,p))
				break;
		}
		if(lib>=scfg.total_libs) { /* not found */
			*curlib=-1;
			return;
		}
		*curlib=lib;

		if(tp==NULL) {
			*curdir=-1;
			return;
		}

		p=tp+1;
	}

	tp=strchr(p,'/');
	if(tp!=NULL) {
		*tp=0;
		tp++;
	} else 
		tp=p+strlen(p);

	for(dir=0;dir<scfg.total_dirs;dir++) {
		if(scfg.dir[dir]->lib!=lib)
			continue;
		if(dir!=scfg.sysop_dir && dir!=scfg.upload_dir 
			&& !chk_ar(&scfg,scfg.dir[dir]->ar,user))
		if(!stricmp(scfg.dir[dir]->code_suffix,p))
			break;
	}

	if(dir>=scfg.total_dirs)  /* not found */
		return;

	*curdir=dir;

	*pp+=tp-path;	/* skip "lib/dir/" */
}

static BOOL ftpalias(char* fullalias, char* filename, user_t* user, int* curdir)
{
	char*	p;
	char*	tp;
	char*	fname="";
	char	line[512];
	char	alias[512];
	char	aliasfile[MAX_PATH+1];
	int		dir=-1;
	FILE*	fp;
	BOOL	result=FALSE;

	sprintf(aliasfile,"%sftpalias.cfg",scfg.ctrl_dir);
	if((fp=fopen(aliasfile,"r"))==NULL) 
	SAFECOPY(alias,fullalias);
	p=strrchr(alias+1,'/');
	if(p) {
		*p=0;
		fname=p+1;
	}

	if(filename==NULL /* directory */ && *fname /* filename specified */)
		return(FALSE);

	while(!feof(fp)) {
		if(!fgets(line,sizeof(line),fp))
			break;

		p=line;	/* alias */
		while(*p && *p<=' ') p++;
		if(*p==';')	/* comment */
			continue;

		tp=p;		/* terminator */
		while(*tp && *tp>' ') tp++;
		if(*tp) *tp=0;

		if(stricmp(p,alias))	/* Not a match */
			continue;

		p=tp+1;		/* filename */
		while(*p && *p<=' ') p++;

		tp=p;		/* terminator */
		while(*tp && *tp>' ') tp++;
		if(*tp) *tp=0;

		if(!strnicmp(p,BBS_VIRTUAL_PATH,strlen(BBS_VIRTUAL_PATH))) {
			if((dir=getdir(p+strlen(BBS_VIRTUAL_PATH),user))<0)	{
				lprintf("0000 !Invalid virtual path (%s) for %s",p,user->alias);
				/* invalid or no access */
				continue;
			}
			p=strrchr(p,'/');
			if(p!=NULL) p++;
			if(p!=NULL && filename!=NULL) {
				if(*p)
					sprintf(filename,"%s%s",scfg.dir[dir]->path,p);
				else
					sprintf(filename,"%s%s",scfg.dir[dir]->path,fname);
			}
		} else if(filename!=NULL)
			strcpy(filename,p);

		result=TRUE;	/* success */
		break;
	}
	fclose(fp);
	if(curdir!=NULL)
		*curdir=dir;
	return(result);
}

char* root_dir(char* path)
{
	char*	p;
	static char	root[MAX_PATH+1];
	SAFECOPY(root,path);

	if(!strncmp(root,"\\\\",2)) {	/* network path */
		p=strchr(root+2,'\\');
		if(p) p=strchr(p+1,'\\');
		if(p) *(p+1)=0;				/* truncate at \\computer\sharename\ */
	} 
	else if(!strncmp(root+1,":/",2) || !strncmp(root+1,":\\",2))
		root[3]=0;
	else if(*root=='/' || *root=='\\')
		root[1]=0;

	return(root);
}

char* vpath(int lib, int dir, char* str)
{
	strcpy(str,"/");
	if(lib<0)
		return(str);
	strcat(str,scfg.lib[lib]->sname);
	strcat(str,scfg.dir[dir]->code_suffix);
static BOOL badlogin(SOCKET sock, ulong* login_attempts)
{
	mswait(5000);	/* As recommended by RFC2577 */
	if(++(*login_attempts)>=3) {
		sockprintf(sock,"421 Too many failed login attempts.");
		return(TRUE);
	}
	sockprintf(sock,"530 Invalid login.");
	return(FALSE);
static char* ftp_tmpfname(char* str, SOCKET sock)
{
	sprintf(str,"%sftp%u%u.tx",scfg.data_dir,getpid(),sock);
	return(str);
}

static void ctrl_thread(void* arg)
{
	char		buf[512];
	char		str[128];
	char*		cmd;
	char*		p;
	char*		np;
	char*		tp;
	char		password[64];
	char		fname[MAX_PATH+1];
	char		qwkfile[MAX_PATH+1];
	char		aliasfile[MAX_PATH+1];
	char		aliasline[512];
	char		desc[501]="";
	char		sys_pass[128];
	char*		host_name;
	char		host_ip[64];
	char		path[MAX_PATH+1];
	char		local_dir[MAX_PATH+1];
	char		ren_from[MAX_PATH+1]="";
	char		html_index_ext[MAX_PATH+1];
	WORD		port;
	ulong		ip_addr;
	socklen_t	addr_len;
	DWORD		h1,h2,h3,h4;
	u_short		p1,p2;	/* For PORT command */
	int			i;
	int			rd;
	int			file;
	int			result;
	int			lib;
	int			dir;
	int			curlib=-1;
	int			curdir=-1;
	int			orglib;
	int			orgdir;
	long		filepos=0L;
	long		timeleft;
	ulong		l;
	ulong		login_attempts=0;
	ulong		avail;	/* disk space */
	BOOL		detail;
	BOOL		success;
	BOOL		getdate;
	BOOL		getsize;
	BOOL		delfile;
	BOOL		tmpfile;
	BOOL		credits;
	BOOL		transfer_inprogress;
	BOOL		transfer_aborted;
	BOOL		sysop=FALSE;
	BOOL		local_fsys=FALSE;
	BOOL		alias_dir;
	FILE*		fp;
	FILE*		alias_fp;
	SOCKET		sock;
	SOCKET		pasv_sock=INVALID_SOCKET;
	SOCKET		data_sock=INVALID_SOCKET;
	HOSTENT*	host;
	SOCKADDR_IN	addr;
	SOCKADDR_IN	data_addr;
	SOCKADDR_IN	pasv_addr;
	ftp_t		ftp=*(ftp_t*)arg;
	user_t		user;
	time_t		t;
	time_t		now;
	time_t		lastactive;
	file_t		f;
	node_t		node;
	client_t	client;
	struct tm	tm;
	struct tm 	cur_tm;
#ifdef JAVASCRIPT
	JSContext*	js_cx=NULL;
	JSObject*	js_glob;
	JSString*	js_str;

	lastactive=time(NULL);

	sock=ftp.socket;
	data_addr=ftp.client_addr;
	/* Default data port is ctrl port-1 */
	data_addr.sin_port=ntohs(data_addr.sin_port)-1;
	data_addr.sin_port=htons(data_addr.sin_port);
	
	lprintf("%04d CTRL thread started", sock);

	free(arg);	/* unexplicable assertion here on July 26, 2001 */
rswindell's avatar
rswindell committed
#ifdef _WIN32
	if(startup->answer_sound[0] && !(startup->options&FTP_OPT_MUTE)) 
		PlaySound(startup->answer_sound, NULL, SND_ASYNC|SND_FILENAME);
rswindell's avatar
rswindell committed
#endif
	l=1;

	if((i=ioctlsocket(sock, FIONBIO, &l))!=0) {
		lprintf("%04d !ERROR %d (%d) disabling socket blocking"
			,sock, i, ERROR_VALUE);
		sockprintf(sock,"425 Error %d disabling socket blocking"
			,ERROR_VALUE);
		thread_down();
		return;
	}

	memset(&user,0,sizeof(user));

	SAFECOPY(host_ip,inet_ntoa(ftp.client_addr.sin_addr));
	lprintf ("%04d CTRL connection accepted from: %s port %u"
		,sock, host_ip, ntohs(ftp.client_addr.sin_port));

	if(startup->options&FTP_OPT_NO_HOST_LOOKUP)
		host=NULL;
	else
		host=gethostbyaddr ((char *)&ftp.client_addr.sin_addr
			,sizeof(ftp.client_addr.sin_addr),AF_INET);

	if(host!=NULL && host->h_name!=NULL)
		host_name=host->h_name;
	else
		host_name="<no name>";

	if(!(startup->options&FTP_OPT_NO_HOST_LOOKUP)) {
		lprintf("%04d Hostname: %s", sock, host_name);
		for(i=0;host!=NULL && host->h_aliases!=NULL && host->h_aliases[i]!=NULL;i++)
			lprintf("%04d HostAlias: %s", sock, host->h_aliases[i]);
	}
		lprintf("%04d !CLIENT BLOCKED in ip.can: %s", sock, host_ip);
		sockprintf(sock,"550 Access denied.");
	if(trashcan(&scfg,host_name,"host")) {
		lprintf("%04d !CLIENT BLOCKED in host.can: %s", sock, host_name);
		sockprintf(sock,"550 Access denied.");
		thread_down();
		return;
	}

	/* For PASV mode */
	addr_len=sizeof(pasv_addr);
	if((result=getsockname(sock, (struct sockaddr *)&pasv_addr,&addr_len))!=0) {
		lprintf("%04d !ERROR %d (%d) getting address/port", sock, result, ERROR_VALUE);
		sockprintf(sock,"425 Error %d getting address/port",ERROR_VALUE);
		thread_down();
		return;
	} 

	active_clients++;
	update_clients();

	/* Initialize client display */
	client.size=sizeof(client);
	client.time=time(NULL);
	SAFECOPY(client.addr,host_ip);
	SAFECOPY(client.host,host_name);
	client.port=ntohs(ftp.client_addr.sin_port);
	client.protocol="FTP";
	client.user="<unknown>";
	client_on(sock,&client,FALSE /* update */);
	sockprintf(sock,"220-%s (%s)",scfg.sys_name, startup->host_name);
	sockprintf(sock," Synchronet FTP Server %s-%s Ready"
		,revision,PLATFORM_DESC);
	if((fp=fopen(str,"rb"))!=NULL) {
		while(!feof(fp)) {
			if(!fgets(buf,sizeof(buf),fp))
				break;
			truncsp(buf);
			sockprintf(sock," %s",buf);
		}
		fclose(fp);
	}
	sockprintf(sock,"220 Please enter your user name.");

#ifdef SOCKET_DEBUG_CTRL
	socket_debug[sock]|=SOCKET_DEBUG_CTRL;
#ifdef SOCKET_DEBUG_READLINE
		socket_debug[sock]|=SOCKET_DEBUG_READLINE;
		rd = sockreadline(sock, buf, sizeof(buf), &lastactive);
#ifdef SOCKET_DEBUG_READLINE
		socket_debug[sock]&=~SOCKET_DEBUG_READLINE;
			if(transfer_inprogress==TRUE) {
				lprintf("%04d Aborting transfer due to receive error",sock);
				transfer_aborted=TRUE;
		lastactive=time(NULL);
		cmd=buf;
		while(((BYTE)*cmd)==TELNET_IAC) {
			cmd++;
			lprintf("%04d RX: Telnet cmd: %s",sock,telnet_cmd_desc(*cmd));
			cmd++;
		}
		while(*cmd && *cmd<' ') {
			lprintf("%04d RX: %d (0x%02X)",sock,(BYTE)*cmd,(BYTE)*cmd);
			cmd++;
		}
		if(!(*cmd))
			continue;
		if(startup->options&FTP_OPT_DEBUG_RX)
			lprintf("%04d RX: %s", sock, cmd);
		if(!stricmp(cmd, "NOOP")) {
			sockprintf(sock,"200 NOOP command successful.");
			continue;
		}
		if(!stricmp(cmd, "HELP SITE") || !stricmp(cmd, "SITE HELP")) {
			sockprintf(sock,"214-The following SITE commands are recognized (* => unimplemented):");
			sockprintf(sock," HELP    VER     WHO     UPTIME");
			if(user.level>=SYSOP_LEVEL)
				sockprintf(sock,
							" RECYCLE [ALL]");
			sockprintf(sock,"214 Direct comments to sysop@%s.",scfg.sys_inetaddr);
			continue;
		}
		if(!strnicmp(cmd, "HELP",4)) {
			sockprintf(sock,"214-The following commands are recognized (* => unimplemented, # => extension):");
			sockprintf(sock," USER    PASS    CWD     XCWD    CDUP    XCUP    PWD     XPWD");
			sockprintf(sock," QUIT    REIN    PORT    PASV    LIST    NLST    NOOP    HELP");
			sockprintf(sock," SIZE    MDTM    RETR    STOR    REST    ALLO    ABOR    SYST");
			sockprintf(sock," TYPE    STRU    MODE    SITE    RNFR*   RNTO*   DELE*   DESC#");
			sockprintf(sock," FEAT#   OPTS#");
			sockprintf(sock,"214 Direct comments to sysop@%s.",scfg.sys_inetaddr);
			continue;
		}
		if(!stricmp(cmd, "FEAT")) {
			sockprintf(sock,"211-The following additional (post-RFC949) features are supported:");
			sockprintf(sock," DESC");
			sockprintf(sock," MDTM");
			sockprintf(sock," SIZE");
			sockprintf(sock," REST STREAM");
			sockprintf(sock,"211 End");
			continue;
		}
		if(!strnicmp(cmd, "OPTS",4)) {
			sockprintf(sock,"501 No options supported.");
			continue;
		}
		if(!stricmp(cmd, "QUIT")) {
			if((fp=fopen(str,"rb"))!=NULL) {
				i=0;
				while(!feof(fp)) {
					if(!fgets(buf,sizeof(buf),fp))
						break;
					truncsp(buf);
					if(!i)
						sockprintf(sock,"221-%s",buf);
					else
						sockprintf(sock," %s",buf);
					i++;
				}
				fclose(fp);
			}
			sockprintf(sock,"221 Goodbye. Closing control connection.");
			break;
		}
		if(!strnicmp(cmd, "USER ",5)) {
			sysop=FALSE;
			user.number=0;
			p=cmd+5;
			while(*p && *p<=' ') p++;
			truncsp(p);
			SAFECOPY(user.alias,p);
			user.number=matchuser(&scfg,user.alias,FALSE /*sysop_alias*/);
			if(!user.number && !stricmp(user.alias,"anonymous"))	
				user.number=matchuser(&scfg,"guest",FALSE);
			if(user.number && getuserdat(&scfg, &user)==0 && user.pass[0]==0) 
				sockprintf(sock,"331 User name okay, give your full e-mail address as password.");
			else
				sockprintf(sock,"331 User name okay, need password.");
			user.number=0;
			continue;
		}
		if(!strnicmp(cmd, "PASS ",5) && user.alias[0]) {
			user.number=0;
			p=cmd+5;
			while(*p && *p<=' ') p++;

			SAFECOPY(password,p);
			user.number=matchuser(&scfg,user.alias,FALSE /*sysop_alias*/);
			if(!user.number) {
				if(scfg.sys_misc&SM_ECHO_PW)
					lprintf("%04d !UNKNOWN USER: %s, Password: %s",sock,user.alias,p);
				else
					lprintf("%04d !UNKNOWN USER: %s",sock,user.alias);
				if(badlogin(sock,&login_attempts))
					break;
				continue;
			}
			if((i=getuserdat(&scfg, &user))!=0) {
				lprintf("%04d !ERROR %d getting data for user #%d (%s)"
					,sock,i,user.number,user.alias);
				sockprintf(sock,"530 Database error %d",i);
				user.number=0;
				continue;
			}
			if(user.misc&(DELETED|INACTIVE)) {
				lprintf("%04d !DELETED or INACTIVE user #%d (%s)"
					,sock,user.number,user.alias);
				user.number=0;
				if(badlogin(sock,&login_attempts))
					break;
				continue;
			}
			if(user.rest&FLAG('T')) {
				lprintf("%04d !T RESTRICTED user #%d (%s)"
					,sock,user.number,user.alias);
				user.number=0;
				if(badlogin(sock,&login_attempts))
					break;
				continue;
			}
			if(user.ltoday>scfg.level_callsperday[user.level]
				&& !(user.exempt&FLAG('L'))) {
				lprintf("%04d !MAXIMUM LOGONS (%d) reached for %s"
					,sock,scfg.level_callsperday[user.level],user.alias);
				sockprintf(sock,"530 Maximum logons reached.");
				user.number=0;
				continue;
			}
			if(user.rest&FLAG('L') && user.ltoday>1) {
				lprintf("%04d !L RESTRICTED user #%d (%s) already on today"
					,sock,user.number,user.alias);
				sockprintf(sock,"530 Maximum logons reached.");
				user.number=0;
				continue;
			}

			sprintf(sys_pass,"%s:%s",user.pass,scfg.sys_pass);
			if(!user.pass[0]) {	/* Guest/Anonymous */
				if(trashcan(&scfg,password,"email")) {
					lprintf("%04d !BLOCKED e-mail address: %s",sock,password);
					if(badlogin(sock,&login_attempts))
						break;
				lprintf("%04d %s: <%s>",sock,user.alias,password);
				putuserrec(&scfg,user.number,U_NETMAIL,LEN_NETMAIL,password);
			}
			else if(user.level>=SYSOP_LEVEL && !stricmp(password,sys_pass)) {
				lprintf("%04d Sysop access granted to %s", sock, user.alias);
				sysop=TRUE;
			}
			else if(stricmp(password,user.pass)) {
					lprintf("%04d !FAILED Password attempt for user %s: '%s' expected '%s'"
						,sock, user.alias, password, user.pass);
				else
					lprintf("%04d !FAILED Password attempt for user %s"
				user.number=0;
				if(badlogin(sock,&login_attempts))
					break;
				continue;
			}

			/* Update client display */
			if(user.pass[0])
				client.user=user.alias;
			else {	/* anonymous */
				sprintf(str,"%s <%.32s>",user.alias,password);
				client.user=str;
			}
			client_on(sock,&client,TRUE /* update */);
rswindell's avatar
rswindell committed
			lprintf("%04d %s logged in",sock,user.alias);
			logintime=time(NULL);
			timeleft=gettimeleft(&scfg,&user,logintime);
			if((fp=fopen(str,"rb"))!=NULL) {
				i=0;
				while(!feof(fp)) {
					if(!fgets(buf,sizeof(buf),fp))
						break;
					truncsp(buf);
					if(!i)
						sockprintf(sock,"230-%s",buf);
					else
						sockprintf(sock," %s",buf);
					i++;
				}
				fclose(fp);
			}
			if(js_cx!=NULL) {
				if(js_CreateUserClass(js_cx, js_glob, &scfg)==NULL) 
					lprintf("%04d !JavaScript ERROR creating user class",sock);
				if(js_CreateUserObject(js_cx, js_glob, &scfg, "user", user.number)==NULL) 
					lprintf("%04d !JavaScript ERROR creating user object",sock);
				if(js_CreateClientObject(js_cx, js_glob, "client", &client, sock)==NULL) 
					lprintf("%04d !JavaScript ERROR creating client object",sock);
				if(js_CreateFileAreaObject(js_cx, js_glob, &scfg, &user
					,startup->html_index_file)==NULL) 
					lprintf("%04d !JavaScript ERROR creating file area object",sock);
			if(sysop)
				sockprintf(sock,"230-Sysop access granted.");
			sockprintf(sock,"230-%s logged in.",user.alias);
			if(!(user.exempt&FLAG('D')) && (user.cdt+user.freecdt)>0)
				sockprintf(sock,"230-You have %lu download credits."
					,user.cdt+user.freecdt);
			sockprintf(sock,"230 You are allowed %lu minutes of use for this session."
				,timeleft/60);
			sprintf(qwkfile,"%sfile/%04d.qwk",scfg.data_dir,user.number);

			/* Adjust User Total Logons/Logons Today */
			adjustuserrec(&scfg,user.number,U_LOGONS,5,1);
			putuserrec(&scfg,user.number,U_LTODAY,5,ultoa(user.ltoday+1,str,10));
			putuserrec(&scfg,user.number,U_MODEM,LEN_MODEM,"FTP");
			putuserrec(&scfg,user.number,U_COMP,LEN_COMP,host_name);
			putuserrec(&scfg,user.number,U_NOTE,LEN_NOTE,host_ip);
			getuserdat(&scfg, &user);	/* make user current */
			continue;
		}

		if(!user.number) {
			sockprintf(sock,"530 Please login with USER and PASS.");
			continue;
		}

		getuserdat(&scfg, &user);	/* get current user data */

		if((timeleft=gettimeleft(&scfg,&user,logintime))<1L) {
			sockprintf(sock,"421 Sorry, you've run out of time.");
			lprintf("%04d Out of time, disconnecting",sock);
			break;
		}

		/********************************/
		/* These commands require login */
		/********************************/

		if(!stricmp(cmd, "REIN")) {
			lprintf("%04d %s reinitialized control session",sock,user.alias);
			user.number=0;
			sysop=FALSE;
			filepos=0;
			sockprintf(sock,"220 Control session re-initialized. Ready for re-login.");
			continue;
		}

		if(!stricmp(cmd, "SITE WHO")) {
			sockprintf(sock,"211-Active Telnet Nodes:");
			for(i=0;i<scfg.sys_nodes && i<scfg.sys_lastnode;i++) {
				if((result=getnodedat(&scfg, i+1, &node, 0))!=0) {
					sockprintf(sock," Error %d getting data for Telnet Node %d",result,i+1);
					continue;
				}
				if(node.status==NODE_INUSE)
					sockprintf(sock," Node %3d: %s",i+1, username(&scfg,node.useron,str));
			sockprintf(sock,"211 End (%d active FTP clients)", active_clients);
			continue;
		}
		if(!stricmp(cmd, "SITE VER")) {
			sockprintf(sock,"211 %s",ftp_ver());
		if(!stricmp(cmd, "SITE UPTIME")) {
			sockprintf(sock,"211 %s (%lu served)",sectostr(time(NULL)-uptime,str),served);
			continue;
		}
		if(!stricmp(cmd, "SITE RECYCLE") && user.level>=SYSOP_LEVEL) {
			startup->recycle_now=TRUE;
			sockprintf(sock,"211 server will recycle when not in-use");
			continue;
		}
		if(!stricmp(cmd, "SITE RECYCLE ALL") && user.level>=SYSOP_LEVEL) {
			refresh_cfg(&scfg);
			sockprintf(sock,"211 ALL servers/nodes will recycle when not in-use");
			continue;
		}
		if(!strnicmp(cmd,"SITE EXEC ",10) && sysop) {
			p=cmd+10;
			while(*p && *p<=' ') p++;
			sockprintf(sock,"211 system(%s) returned %d",p,system(p));
			continue;
		}
#ifdef SOCKET_DEBUG_CTRL
		if(!stricmp(cmd, "SITE DEBUG")) {
			sockprintf(sock,"211-Debug");
			for(i=0;i<sizeof(socket_debug);i++) 
				if(socket_debug[i]!=0)
					sockprintf(sock,"211-socket %d = 0x%X",i,socket_debug[i]);

		if(!strnicmp(cmd, "PORT ",5)) {
			p=cmd+5;
			while(*p && *p<=' ') p++;
			sscanf(p,"%ld,%ld,%ld,%ld,%hd,%hd",&h1,&h2,&h3,&h4,&p1,&p2);
			data_addr.sin_addr.s_addr=htonl((h1<<24)|(h2<<16)|(h3<<8)|h4);
			data_addr.sin_port=(u_short)((p1<<8)|p2);
			if(data_addr.sin_port<1024) {	
				lprintf("%04d !SUSPECTED BOUNCE ATTACK ATTEMPT by %s to %s port %u"
					,sock,user.alias
					,inet_ntoa(data_addr.sin_addr),data_addr.sin_port);
				hacklog(&scfg, "FTP", user.alias, cmd, host_name, &ftp.client_addr);
				sockprintf(sock,"504 Bad port number.");	
#ifdef _WIN32
				if(startup->hack_sound[0] && !(startup->options&FTP_OPT_MUTE)) 
					PlaySound(startup->hack_sound, NULL, SND_ASYNC|SND_FILENAME);
#endif
				continue; /* As recommended by RFC2577 */
			}
			data_addr.sin_port=htons(data_addr.sin_port);
			sockprintf(sock,"200 PORT Command successful.");
			continue;
		}


			if(pasv_sock!=INVALID_SOCKET) 
			if((pasv_sock=ftp_open_socket(SOCK_STREAM))==INVALID_SOCKET) {
				lprintf("%04d !PASV ERROR %d opening socket", sock,ERROR_VALUE);
				sockprintf(sock,"425 Error %d opening PASV data socket", ERROR_VALUE);
				continue;
			}

			if(startup->options&FTP_OPT_DEBUG_DATA)
				lprintf("%04d PASV DATA socket %d opened",sock,pasv_sock);

			pasv_addr.sin_port = 0;

			result=bind(pasv_sock, (struct sockaddr *) &pasv_addr,sizeof(pasv_addr));
			if(result!= 0) {
				lprintf("%04d !PASV ERROR %d (%d) binding socket", sock, result, ERROR_VALUE);
				sockprintf(sock,"425 Error %d binding data socket",ERROR_VALUE);
				continue;
			}

			addr_len=sizeof(addr);
			if((result=getsockname(pasv_sock, (struct sockaddr *)&addr,&addr_len))!=0) {
				lprintf("%04d !PASV ERROR %d (%d) getting address/port", sock, result, ERROR_VALUE);
				sockprintf(sock,"425 Error %d getting address/port",ERROR_VALUE);
				continue;
			} 

			if((result=listen(pasv_sock, 1))!= 0) {
				lprintf("%04d !PASV ERROR %d (%d) listening on socket", sock, result, ERROR_VALUE);
				sockprintf(sock,"425 Error %d listening on data socket",ERROR_VALUE);
			ip_addr=ntohl(pasv_addr.sin_addr.s_addr);
			port=ntohs(addr.sin_port);
			sockprintf(sock,"227 Entering Passive Mode (%d,%d,%d,%d,%hd,%hd)"
				,(ip_addr>>24)&0xff
				,(ip_addr>>16)&0xff
				,(ip_addr>>8)&0xff
				,ip_addr&0xff
				,(port>>8)&0xff
				,port&0xff
				);
			continue;
		}

		if(!strnicmp(cmd, "TYPE ",5)) {
			sockprintf(sock,"200 All files sent in BINARY mode.");
			continue;
		}

		if(!strnicmp(cmd, "ALLO",4)) {
			p=cmd+5;
			while(*p && *p<=' ') p++;
			if(*p)
				l=atol(p);	
			else
				l=0;
			if(local_fsys)
				avail=getfreediskspace(local_dir,0);
				avail=getfreediskspace(scfg.data_dir,0);	/* Change to temp_dir? */
			if(l && l>avail)
				sockprintf(sock,"504 Only %lu bytes available.",avail);
				sockprintf(sock,"200 %lu bytes available.",avail);
			continue;
		}

		if(!strnicmp(cmd, "REST",4)) {
			p=cmd+4;
			while(*p && *p<=' ') p++;
			if(*p)
				filepos=atol(p);
			else
				filepos=0;
			sockprintf(sock,"350 Restarting at %lu. Send STORE or RETRIEVE to initiate transfer."
			continue;
		}

		if(!strnicmp(cmd, "MODE ",5)) {
			p=cmd+5;
			while(*p && *p<=' ') p++;
			if(toupper(*p)!='S')
				sockprintf(sock,"504 Only STREAM mode supported.");
			else
				sockprintf(sock,"200 STREAM mode.");
			continue;
		}

		if(!strnicmp(cmd, "STRU ",5)) {
			p=cmd+5;
			while(*p && *p<=' ') p++;
			if(toupper(*p)!='F')
				sockprintf(sock,"504 Only FILE structure supported.");
			else
				sockprintf(sock,"200 FILE structure.");
			continue;
		}

		if(!stricmp(cmd, "SYST")) {
			sockprintf(sock,"215 UNIX Type: L8");
			continue;
		}

		if(!stricmp(cmd, "ABOR")) {
			if(!transfer_inprogress)
				sockprintf(sock,"226 No tranfer in progress.");
			else {
				lprintf("%04d %s aborting transfer"
					,sock,user.alias);
				transfer_aborted=TRUE;
				YIELD(); /* give send thread time to abort */
				sockprintf(sock,"226 Transfer aborted.");
			}
			continue;
		}

		if(!strnicmp(cmd,"SMNT ",5) && sysop && !(startup->options&FTP_OPT_NO_LOCAL_FSYS)) {
			p=cmd+5;
			while(*p && *p<=' ') p++;
			if(!stricmp(p,BBS_FSYS_DIR)) 
				local_fsys=FALSE;
			else {
				if(!direxist(p)) {
					sockprintf(sock,"550 Directory does not exist.");
					lprintf("%04d !%s attempted to mount invalid directory: %s"
						,sock, user.alias, p);
					continue;
				}
				local_fsys=TRUE;
				SAFECOPY(local_dir,p);
			}
			sockprintf(sock,"250 %s file system mounted."
				,local_fsys ? "Local" : "BBS");
			lprintf("%04d %s mounted %s file system"
				,sock, user.alias, local_fsys ? "local" : "BBS");
			continue;
		}

		/****************************/
		/* Local File System Access */
		/****************************/
		if(sysop && local_fsys && !(startup->options&FTP_OPT_NO_LOCAL_FSYS)) {
			if(local_dir[0] 
				&& local_dir[strlen(local_dir)-1]!='\\'
				&& local_dir[strlen(local_dir)-1]!='/')
				strcat(local_dir,"/");

			if(!strnicmp(cmd, "LIST", 4) || !strnicmp(cmd, "NLST", 4)) {	
				if((fp=fopen(ftp_tmpfname(fname,sock),"w+b"))==NULL) {
					lprintf("%04d !ERROR %d opening %s",sock,errno,fname);
					sockprintf(sock, "451 Insufficient system storage");
					continue;
				}
				if(!strnicmp(cmd, "LIST", 4))
					detail=TRUE;
				else
					detail=FALSE;

				p=cmd+4;
				while(*p && *p<=' ') p++;
				lprintf("%04d %s listing: %s", sock, user.alias, path);
				sockprintf(sock, "150 Directory of %s%s", local_dir, p);