Skip to content
Snippets Groups Projects
ftpsrvr.c 136 KiB
Newer Older
			continue;
		}

		if(!strnicmp(cmd,"SMNT ",5) && sysop && !(startup->options&FTP_OPT_NO_LOCAL_FSYS)) {
			p=cmd+5;
			if(!stricmp(p,BBS_FSYS_DIR)) 
				local_fsys=FALSE;
			else {
				if(!direxist(p)) {
					sockprintf(sock,"550 Directory does not exist.");
					lprintf(LOG_WARNING,"%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(LOG_INFO,"%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,"lst",sock),"w+b"))==NULL) {
					lprintf(LOG_ERR,"%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;

				if(*p=='-') {	/* -Letc */
					FIND_WHITESPACE(p);
					SKIP_WHITESPACE(p);
				filespec=p;
				if(*filespec==0)
					filespec="*";

				SAFEPRINTF2(path,"%s%s",local_dir, filespec);
				lprintf(LOG_INFO,"%04d %s listing: %s in %s mode", sock, user.alias, path, mode);
				sockprintf(sock, "150 Directory of %s%s", local_dir, filespec);
					memset(&cur_tm,0,sizeof(cur_tm));
			
				glob(path,0,NULL,&g);
				for(i=0;i<(int)g.gl_pathc;i++) {
						f.size=flength(g.gl_pathv[i]);
						t=fdate(g.gl_pathv[i]);
							memset(&tm,0,sizeof(tm));
						fprintf(fp,"%crw-r--r--   1 %-8s local %9ld %s %2d "
							,scfg.sys_id
							,f.size
						if(tm.tm_year==cur_tm.tm_year)
							fprintf(fp,"%02d:%02d %s\r\n"
								,tm.tm_hour,tm.tm_min
						else
							fprintf(fp,"%5d %s\r\n"
								,1900+tm.tm_year
				fclose(fp);
				filexfer(&data_addr,sock,pasv_sock,&data_sock,fname,0L
					,&transfer_inprogress,&transfer_aborted
					,TRUE	/* delfile */
					,TRUE	/* tmpfile */
					,&lastactive,&user,-1,FALSE,FALSE,FALSE,NULL);
				continue;
			} /* Local LIST/NLST */
				
			if(!strnicmp(cmd, "CWD ", 4) || !strnicmp(cmd,"XCWD ",5)) {
			    if(!strnicmp(cmd,"CWD ",4))
					p=cmd+4;
				else
					p=cmd+5;
				tp=p;
				if(*tp=='/' || *tp=='\\') /* /local: and /bbs: are valid */
					tp++;
				if(!strnicmp(tp,BBS_FSYS_DIR,strlen(BBS_FSYS_DIR))) {
					local_fsys=FALSE;
					sockprintf(sock,"250 CWD command successful (BBS file system mounted).");
					lprintf(LOG_INFO,"%04d %s mounted BBS file system", sock, user.alias);
					continue;
				}
				if(!strnicmp(tp,LOCAL_FSYS_DIR,strlen(LOCAL_FSYS_DIR))) {
					tp+=strlen(LOCAL_FSYS_DIR);	/* already mounted */
					p=tp;
				}

				if(p[1]==':' || !strncmp(p,"\\\\",2))
					SAFECOPY(path,p);
				else if(*p=='/' || *p=='\\')
					SAFEPRINTF2(path,"%s%s",root_dir(local_dir),p);
					SAFEPRINTF2(fname,"%s%s",local_dir,p);
					FULLPATH(path,fname,sizeof(path));
				}

				if(!direxist(path)) {
					sockprintf(sock,"550 Directory does not exist (%s).",path);
					lprintf(LOG_WARNING,"%04d !%s attempted to change to an invalid directory: %s"
						,sock, user.alias, path);
				} else {
					SAFECOPY(local_dir,path);
					sockprintf(sock,"250 CWD command successful (%s).", local_dir);
				}
				continue;
			} /* Local CWD */

			if(!stricmp(cmd,"CDUP") || !stricmp(cmd,"XCUP")) {
				SAFEPRINTF(path,"%s..",local_dir);
				if(FULLPATH(local_dir,path,sizeof(local_dir))==NULL)
					sockprintf(sock,"550 Directory does not exist.");
				else
					sockprintf(sock,"200 CDUP command successful.");
				continue;
			}

			if(!stricmp(cmd, "PWD") || !stricmp(cmd,"XPWD")) {
				if(strlen(local_dir)>3)
					local_dir[strlen(local_dir)-1]=0;	/* truncate '/' */

				sockprintf(sock,"257 \"%s\" is current directory."
					,local_dir);
				continue;
			} /* Local PWD */

			if(!strnicmp(cmd, "MKD ", 4) || !strnicmp(cmd,"XMKD",4)) {
				p=cmd+4;
				if(*p=='/')	/* absolute */
					SAFEPRINTF2(fname,"%s%s",root_dir(local_dir),p+1);
				else		/* relative */
					SAFEPRINTF2(fname,"%s%s",local_dir,p);
rswindell's avatar
rswindell committed
				if((i=MKDIR(fname))==0) {
					sockprintf(sock,"257 \"%s\" directory created",fname);
					lprintf(LOG_NOTICE,"%04d %s created directory: %s",sock,user.alias,fname);
				} else {
					sockprintf(sock,"521 Error %d creating directory: %s",i,fname);
					lprintf(LOG_WARNING,"%04d !%s attempted to create directory: %s (Error %d)"
						,sock,user.alias,fname,i);
				}
				continue;
			}

			if(!strnicmp(cmd, "RMD ", 4) || !strnicmp(cmd,"XRMD",4)) {
				p=cmd+4;
				if(*p=='/')	/* absolute */
					SAFEPRINTF2(fname,"%s%s",root_dir(local_dir),p+1);
				else		/* relative */
					SAFEPRINTF2(fname,"%s%s",local_dir,p);
				if((i=rmdir(fname))==0) {
					sockprintf(sock,"250 \"%s\" directory removed",fname);
					lprintf(LOG_NOTICE,"%04d %s removed directory: %s",sock,user.alias,fname);
				} else {
					sockprintf(sock,"450 Error %d removing directory: %s",i,fname);
					lprintf(LOG_WARNING,"%04d !%s attempted to remove directory: %s (Error %d)"
						,sock,user.alias,fname,i);
				}
				continue;
			}

			if(!strnicmp(cmd, "RNFR ",5)) {
				p=cmd+5;
				if(*p=='/')	/* absolute */
					SAFEPRINTF2(ren_from,"%s%s",root_dir(local_dir),p+1);
				else		/* relative */
					SAFEPRINTF2(ren_from,"%s%s",local_dir,p);
				if(!fexist(ren_from)) {
					sockprintf(sock,"550 File not found: %s",ren_from);
					lprintf(LOG_WARNING,"%04d !%s attempted to rename %s (not found)"
						,sock,user.alias,ren_from);
				} else
					sockprintf(sock,"350 File exists, ready for destination name");
				continue;
			}

			if(!strnicmp(cmd, "RNTO ",5)) {
				p=cmd+5;
				if(*p=='/')	/* absolute */
					SAFEPRINTF2(fname,"%s%s",root_dir(local_dir),p+1);
				else		/* relative */
					SAFEPRINTF2(fname,"%s%s",local_dir,p);

				if((i=rename(ren_from, fname))==0) {
					sockprintf(sock,"250 \"%s\" renamed to \"%s\"",ren_from,fname);
					lprintf(LOG_NOTICE,"%04d %s renamed %s to %s",sock,user.alias,ren_from,fname);
				} else {
					sockprintf(sock,"450 Error %d renaming file: %s",i,ren_from);
					lprintf(LOG_WARNING,"%04d !%s attempted to rename file: %s (Error %d)"
						,sock,user.alias,ren_from,i);
				}
				continue;
			}


			if(!strnicmp(cmd, "RETR ", 5) || !strnicmp(cmd,"SIZE ",5) 
				|| !strnicmp(cmd, "MDTM ",5) || !strnicmp(cmd, "DELE ",5)) {
				p=cmd+5;

				if(!strnicmp(p,LOCAL_FSYS_DIR,strlen(LOCAL_FSYS_DIR))) 
					p+=strlen(LOCAL_FSYS_DIR);	/* already mounted */

				if(p[1]==':')		/* drive specified */
					SAFECOPY(fname,p);
				else if(*p=='/')	/* absolute, current drive */
					SAFEPRINTF2(fname,"%s%s",root_dir(local_dir),p+1);
				else		/* relative */
					SAFEPRINTF2(fname,"%s%s",local_dir,p);
				if(!fexist(fname)) {
					lprintf(LOG_WARNING,"%04d !%s file not found: %s",sock,user.alias,fname);
					sockprintf(sock,"550 File not found: %s",fname);
					continue;
				}
				if(!strnicmp(cmd,"SIZE ",5)) {
					sockprintf(sock,"213 %lu",flength(fname));
					continue;
				}
				if(!strnicmp(cmd,"MDTM ",5)) {
					t=fdate(fname);
					if(gmtime_r(&t,&tm)==NULL) /* specifically use GMT/UTC representation */
						memset(&tm,0,sizeof(tm));
					sockprintf(sock,"213 %u%02u%02u%02u%02u%02u"
						,1900+tm.tm_year,tm.tm_mon+1,tm.tm_mday
						,tm.tm_hour,tm.tm_min,tm.tm_sec);					
					continue;
				}
				if(!strnicmp(cmd,"DELE ",5)) {
					if((i=remove(fname))==0) {
						sockprintf(sock,"250 \"%s\" removed successfully.",fname);
						lprintf(LOG_NOTICE,"%04d %s deleted file: %s",sock,user.alias,fname);
					} else {
						sockprintf(sock,"450 Error %d removing file: %s",i,fname);
						lprintf(LOG_WARNING,"%04d !%s attempted to delete file: %s (Error %d)"
							,sock,user.alias,fname,i);
					}
					continue;
				}
				/* RETR */
				lprintf(LOG_INFO,"%04d %s downloading: %s (%lu bytes) in %s mode"
					,sock,user.alias,fname,flength(fname)
				sockprintf(sock,"150 Opening BINARY mode data connection for file transfer.");
				filexfer(&data_addr,sock,pasv_sock,&data_sock,fname,filepos
					,&transfer_inprogress,&transfer_aborted,FALSE,FALSE
					,&lastactive,&user,-1,FALSE,FALSE,FALSE,NULL);
				continue;
			} /* Local RETR/SIZE/MDTM */

			if(!strnicmp(cmd, "STOR ", 5) || !strnicmp(cmd, "APPE ", 5)) {
				p=cmd+5;

				if(!strnicmp(p,LOCAL_FSYS_DIR,strlen(LOCAL_FSYS_DIR))) 
					p+=strlen(LOCAL_FSYS_DIR);	/* already mounted */

				if(p[1]==':')		/* drive specified */
					SAFECOPY(fname,p);
				else if(*p=='/')	/* absolute, current drive */
					SAFEPRINTF2(fname,"%s%s",root_dir(local_dir),p+1);
				else				/* relative */
					SAFEPRINTF2(fname,"%s%s",local_dir,p);
				lprintf(LOG_INFO,"%04d %s uploading: %s in %s mode", sock,user.alias,fname
				sockprintf(sock,"150 Opening BINARY mode data connection for file transfer.");
				filexfer(&data_addr,sock,pasv_sock,&data_sock,fname,filepos
					,&transfer_inprogress,&transfer_aborted,FALSE,FALSE
					,&lastactive
					,&user
					,-1		/* dir */
					,TRUE	/* uploading */
					,FALSE	/* credits */
					,!strnicmp(cmd,"APPE",4) ? TRUE : FALSE	/* append */
					,NULL	/* desc */
					);
				filepos=0;
				continue;
			} /* Local STOR */
		}

		if(!strnicmp(cmd, "LIST", 4) || !strnicmp(cmd, "NLST", 4)) {	
			dir=curdir;
			lib=curlib;

			if(cmd[4]!=0) 
				lprintf(LOG_DEBUG,"%04d LIST/NLST: %s",sock,cmd);

			/* path specified? */
			p=cmd+4;

			if(*p=='-') {	/* -Letc */
				FIND_WHITESPACE(p);
				SKIP_WHITESPACE(p);
			}

			parsepath(&p,&user,&lib,&dir);
			if((fp=fopen(ftp_tmpfname(fname,"lst",sock),"w+b"))==NULL) {
				lprintf(LOG_ERR,"%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;
			sockprintf(sock,"150 Opening ASCII mode data connection for /bin/ls.");
			now=time(NULL);
				memset(&cur_tm,0,sizeof(cur_tm));

			/* ASCII Index File */
			if(startup->options&FTP_OPT_INDEX_FILE && startup->index_file_name[0]
				&& wildmatchi(startup->index_file_name, filespec, FALSE)) {
					fprintf(fp,"-r--r--r--   1 %-*s %-8s %9ld %s %2d %02d:%02d %s\r\n"
						,NAME_LEN
						,scfg.sys_id
						,lib<0 ? scfg.sys_id : dir<0 
							? scfg.lib[lib]->sname : scfg.dir[dir]->code_suffix
						,ftp_mon[cur_tm.tm_mon],cur_tm.tm_mday,cur_tm.tm_hour,cur_tm.tm_min
						,startup->index_file_name);
				else
					fprintf(fp,"%s\r\n",startup->index_file_name);
			} 
			/* HTML Index File */
			if(startup->options&FTP_OPT_HTML_INDEX_FILE && startup->html_index_file[0]
				&& wildmatchi(startup->html_index_file, filespec, FALSE)) {
				if(detail)
					fprintf(fp,"-r--r--r--   1 %-*s %-8s %9ld %s %2d %02d:%02d %s\r\n"
						,NAME_LEN
						,scfg.sys_id
						,lib<0 ? scfg.sys_id : dir<0 
							? scfg.lib[lib]->sname : scfg.dir[dir]->code_suffix
						,ftp_mon[cur_tm.tm_mon],cur_tm.tm_mday,cur_tm.tm_hour,cur_tm.tm_min
						,startup->html_index_file);
				else
					fprintf(fp,"%s\r\n",startup->html_index_file);
			} 

			if(lib<0) { /* Root dir */
				lprintf(LOG_INFO,"%04d %s listing: root in %s mode",sock,user.alias, mode);
				if(startup->options&FTP_OPT_ALLOW_QWK) {
					SAFEPRINTF(str,"%s.qwk",scfg.sys_id);
					if(wildmatchi(str, filespec, FALSE)) {
						if(detail) {
							if(fexistcase(qwkfile)) {
								t=fdate(qwkfile);
								l=flength(qwkfile);
							} else {
								t=time(NULL);
								l=10240;
							};
							if(localtime_r(&t,&tm)==NULL) 
								memset(&tm,0,sizeof(tm));
							fprintf(fp,"-r--r--r--   1 %-*s %-8s %9ld %s %2d %02d:%02d %s\r\n"
								,NAME_LEN
								,scfg.sys_id
								,scfg.sys_id
								,l
								,ftp_mon[tm.tm_mon],tm.tm_mday,tm.tm_hour,tm.tm_min
								,str);
						} else
							fprintf(fp,"%s\r\n",str);
					}
				sprintf(aliasfile,"%sftpalias.cfg",scfg.ctrl_dir);
				if((alias_fp=fopen(aliasfile,"r"))!=NULL) {

					while(!feof(alias_fp)) {
						if(!fgets(aliasline,sizeof(aliasline),alias_fp))
							break;

						alias_dir=FALSE;

						p=aliasline;		/* alias pointer */

						if(*p==';')	/* comment */
							continue;

						tp=p;		/* terminator pointer */
						if(*tp) *tp=0;

						np=tp+1;	/* filename pointer */

						tp=np;		/* terminator pointer */
						truncsp(dp);

						if(stricmp(dp,BBS_HIDDEN_ALIAS)==0)
							continue;

						if(!wildmatchi(p, filespec, FALSE))
							continue;

						/* Virtual Path? */
						if(!strnicmp(np,BBS_VIRTUAL_PATH,strlen(BBS_VIRTUAL_PATH))) {
							if((dir=getdir(np+strlen(BBS_VIRTUAL_PATH),&user))<0) {
								lprintf(LOG_WARNING,"0000 !Invalid virtual path (%s) for %s",np,user.alias);
								continue; /* No access or invalid virtual path */
							tp=strrchr(np,'/');
							if(tp==NULL) 
								continue;
							tp++;
							if(*tp) {
								SAFEPRINTF2(aliasfile,"%s%s",scfg.dir[dir]->path,tp);
						if(!alias_dir && !fexist(np)) {
							lprintf(LOG_WARNING,"0000 !Missing aliased file (%s) for %s",np,user.alias);

						if(detail) {

							if(alias_dir==TRUE) {
								fprintf(fp,"drwxrwxrwx   1 %-*s %-8s %9ld %s %2d %02d:%02d %s\r\n"
									,NAME_LEN
									,scfg.sys_id
									,scfg.lib[scfg.dir[dir]->lib]->sname
									,ftp_mon[cur_tm.tm_mon],cur_tm.tm_mday,cur_tm.tm_hour,cur_tm.tm_min
									memset(&tm,0,sizeof(tm));
								fprintf(fp,"-r--r--r--   1 %-*s %-8s %9ld %s %2d %02d:%02d %s\r\n"
									,NAME_LEN
									,scfg.sys_id
									,scfg.sys_id
									,flength(np)
									,ftp_mon[tm.tm_mon],tm.tm_mday,tm.tm_hour,tm.tm_min
									,p);
							}
						} else
							fprintf(fp,"%s\r\n",p);

					}

					fclose(alias_fp);
				}

				/* Library folders */
				for(i=0;i<scfg.total_libs;i++) {
					if(!chk_ar(&scfg,scfg.lib[i]->ar,&user))
						continue;
					if(!wildmatchi(scfg.lib[i]->sname, filespec, FALSE))
						continue;
						fprintf(fp,"dr-xr-xr-x   1 %-*s %-8s %9ld %s %2d %02d:%02d %s\r\n"
							,NAME_LEN
							,scfg.sys_id
							,scfg.sys_id
							,ftp_mon[cur_tm.tm_mon],cur_tm.tm_mday,cur_tm.tm_hour,cur_tm.tm_min
							,scfg.lib[i]->sname);
					else
						fprintf(fp,"%s\r\n",scfg.lib[i]->sname);
				}
			} else if(dir<0) {
				lprintf(LOG_INFO,"%04d %s listing: %s library in %s mode"
					,sock,user.alias,scfg.lib[lib]->sname,mode);
				for(i=0;i<scfg.total_dirs;i++) {
					if(scfg.dir[i]->lib!=lib)
						continue;
					if(i!=scfg.sysop_dir && i!=scfg.upload_dir 
						&& !chk_ar(&scfg,scfg.dir[i]->ar,&user))
						continue;
					if(!wildmatchi(scfg.dir[i]->code_suffix, filespec, FALSE))
						continue;
						fprintf(fp,"drwxrwxrwx   1 %-*s %-8s %9ld %s %2d %02d:%02d %s\r\n"
							,NAME_LEN
							,scfg.sys_id
							,scfg.lib[lib]->sname
							,ftp_mon[cur_tm.tm_mon],cur_tm.tm_mday,cur_tm.tm_hour,cur_tm.tm_min
						fprintf(fp,"%s\r\n",scfg.dir[i]->code_suffix);
				}
			} else if(chk_ar(&scfg,scfg.dir[dir]->ar,&user)) {
				lprintf(LOG_INFO,"%04d %s listing: %s/%s directory in %s mode"
					,sock,user.alias,scfg.lib[lib]->sname,scfg.dir[dir]->code_suffix,mode);
				SAFEPRINTF2(path,"%s%s",scfg.dir[dir]->path,filespec);
				glob(path,0,NULL,&g);
				for(i=0;i<(int)g.gl_pathc;i++) {
					if(isdir(g.gl_pathv[i]))
						continue;
#ifdef _WIN32
					GetShortPathName(g.gl_pathv[i], str, sizeof(str));
#else
					SAFECOPY(str,g.gl_pathv[i]);
					if((filedat=getfileixb(&scfg,&f))==FALSE
						&& !(startup->options&FTP_OPT_DIR_FILES))
						continue;
					if(detail) {
						f.size=flength(g.gl_pathv[i]);
						getfiledat(&scfg,&f);
						t=fdate(g.gl_pathv[i]);
						if(filedat) {
							if(f.misc&FM_ANON)
								SAFECOPY(str,ANONYMOUS);
							SAFECOPY(str,scfg.sys_id);
						fprintf(fp,"-r--r--r--   1 %-*s %-8s %9ld %s %2d "
						if(tm.tm_year==cur_tm.tm_year)
							fprintf(fp,"%02d:%02d %s\r\n"
								,tm.tm_hour,tm.tm_min
								,getfname(g.gl_pathv[i]));
						else
							fprintf(fp,"%5d %s\r\n"
								,1900+tm.tm_year
								,getfname(g.gl_pathv[i]));
					} else
						fprintf(fp,"%s\r\n",getfname(g.gl_pathv[i]));
				lprintf(LOG_INFO,"%04d %s listing: %s/%s directory in %s mode (empty - no access)"
					,sock,user.alias,scfg.lib[lib]->sname,scfg.dir[dir]->code_suffix,mode);

			fclose(fp);
			filexfer(&data_addr,sock,pasv_sock,&data_sock,fname,0L
				,&transfer_inprogress,&transfer_aborted
				,TRUE /* delfile */
				,TRUE /* tmpfile */
				,&lastactive,&user,dir,FALSE,FALSE,FALSE,NULL);
			continue;
		}

		if(!strnicmp(cmd, "RETR ", 5) 
			|| !strnicmp(cmd, "SIZE ",5) 
			|| !strnicmp(cmd, "MDTM ",5)
			|| !strnicmp(cmd, "DELE ",5)) {
			getdate=FALSE;
			getsize=FALSE;
			if(!strnicmp(cmd,"SIZE ",5))
				getsize=TRUE;
			else if(!strnicmp(cmd,"MDTM ",5))
				getdate=TRUE;
			else if(!strnicmp(cmd,"DELE ",5))
				delecmd=TRUE;

			if(!getsize && !getdate && user.rest&FLAG('D')) {
				sockprintf(sock,"550 Insufficient access.");
				filepos=0;
				continue;
			}
			credits=TRUE;
			success=FALSE;
			delfile=FALSE;
			tmpfile=FALSE;
			lib=curlib;
			dir=curdir;

			p=cmd+5;

			if(!strnicmp(p,BBS_FSYS_DIR,strlen(BBS_FSYS_DIR))) 
				p+=strlen(BBS_FSYS_DIR);	/* already mounted */

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

			if(lib<0 && ftpalias(p, fname, &user, &dir)==TRUE) {
				success=TRUE;
				credits=TRUE;	/* include in d/l stats */
				tmpfile=FALSE;
				delfile=FALSE;
				lprintf(LOG_INFO,"%04d %s %.4s by alias: %s"
					,sock,user.alias,cmd,p);
				p=getfname(fname);
			}
			if(!success && lib<0 && (tp=strchr(p,'/'))!=NULL) {
				dir=-1;
				*tp=0;
				for(i=0;i<scfg.total_libs;i++) {
					if(!chk_ar(&scfg,scfg.lib[i]->ar,&user))
						continue;
					if(!stricmp(scfg.lib[i]->sname,p))
						break;
				}
				if(i<scfg.total_libs) 
					lib=i;
				p=tp+1;
			}
			if(!success && dir<0 && (tp=strchr(p,'/'))!=NULL) {
				*tp=0;
				for(i=0;i<scfg.total_dirs;i++) {
					if(scfg.dir[i]->lib!=lib)
						continue;
					if(!chk_ar(&scfg,scfg.dir[i]->ar,&user))
						continue;
					if(!stricmp(scfg.dir[i]->code_suffix,p))
						break;
				}
				if(i<scfg.total_dirs) 
					dir=i;
				p=tp+1;
			}

			sprintf(html_index_ext,"%s?",startup->html_index_file);

			sprintf(str,"%s.qwk",scfg.sys_id);
			if(lib<0 && startup->options&FTP_OPT_ALLOW_QWK 
				if(!fexistcase(qwkfile)) {
					lprintf(LOG_INFO,"%04d %s creating QWK packet...",sock,user.alias);
					sprintf(str,"%spack%04u.now",scfg.data_dir,user.number);
					if(!ftouch(str))
						lprintf(LOG_ERR,"%04d !ERROR creating semaphore file: %s"
							,sock, str);
					t=time(NULL);
					while(fexist(str)) {
						if(time(NULL)-t>startup->qwk_timeout)
							break;
						mswait(1000);
					}
					if(fexist(str)) {
						lprintf(LOG_WARNING,"%04d !TIMEOUT waiting for QWK packet creation",sock);
						sockprintf(sock,"451 Time-out waiting for packet creation.");
						remove(str);
						filepos=0;
						continue;
					}
					if(!fexistcase(qwkfile)) {
						lprintf(LOG_INFO,"%04d No QWK Packet created (no new messages)",sock);
						sockprintf(sock,"550 No QWK packet created (no new messages)");
						filepos=0;
						continue;
					}
				SAFECOPY(fname,qwkfile);
				success=TRUE;
				delfile=TRUE;
				credits=FALSE;
				if(!getsize && !getdate)
					lprintf(LOG_INFO,"%04d %s downloading QWK packet (%lu bytes) in %s mode"
						,sock,user.alias,flength(fname)
			/* ASCII Index File */
			} else if(startup->options&FTP_OPT_INDEX_FILE 
				&& !stricmp(p,startup->index_file_name)
				&& !delecmd) {
				if(getsize) {
					sockprintf(sock, "500 Size not available for dynamically generated files");
					continue;
				}
				if((fp=fopen(ftp_tmpfname(fname,"ndx",sock),"w+b"))==NULL) {
					lprintf(LOG_ERR,"%04d !ERROR %d opening %s",sock,errno,fname);
					sockprintf(sock, "451 Insufficient system storage");
					filepos=0;
					continue;
				}
					lprintf(LOG_INFO,"%04d %s downloading index for %s in %s mode"
						,sock,user.alias,genvpath(lib,dir,str)
					credits=FALSE;
					tmpfile=TRUE;
					delfile=TRUE;
					fprintf(fp,"%-*s File/Folder Descriptions\r\n"
						,INDEX_FNAME_LEN,startup->index_file_name);
					if(startup->options&FTP_OPT_HTML_INDEX_FILE)
						fprintf(fp,"%-*s File/Folder Descriptions (HTML)\r\n"
							,INDEX_FNAME_LEN,startup->html_index_file);
					if(lib<0) {

						/* File Aliases */
						sprintf(aliasfile,"%sftpalias.cfg",scfg.ctrl_dir);
						if((alias_fp=fopen(aliasfile,"r"))!=NULL) {

							while(!feof(alias_fp)) {
								if(!fgets(aliasline,sizeof(aliasline),alias_fp))
									break;
								fprintf(fp,"%-*s %s\r\n",INDEX_FNAME_LEN,p,np);
							}
						/* QWK Packet */
						if(startup->options&FTP_OPT_ALLOW_QWK /* && fexist(qwkfile) */) {
							sprintf(str,"%s.qwk",scfg.sys_id);
							fprintf(fp,"%-*s QWK Message Packet\r\n"
								,INDEX_FNAME_LEN,str);
						}
						/* Library Folders */
						for(i=0;i<scfg.total_libs;i++) {
							if(!chk_ar(&scfg,scfg.lib[i]->ar,&user))
								continue;
							fprintf(fp,"%-*s %s\r\n"
								,INDEX_FNAME_LEN,scfg.lib[i]->sname,scfg.lib[i]->lname);
						}
					} else if(dir<0) {
						for(i=0;i<scfg.total_dirs;i++) {
							if(scfg.dir[i]->lib!=lib)
								continue;
							if(i!=scfg.sysop_dir && i!=scfg.upload_dir
								&& !chk_ar(&scfg,scfg.dir[i]->ar,&user))
								continue;
							fprintf(fp,"%-*s %s\r\n"
								,INDEX_FNAME_LEN,scfg.dir[i]->code_suffix,scfg.dir[i]->lname);
						}
					} else if(chk_ar(&scfg,scfg.dir[dir]->ar,&user)){
						sprintf(cmd,"%s*",scfg.dir[dir]->path);
						glob(cmd,0,NULL,&g);
						for(i=0;i<(int)g.gl_pathc;i++) {
							if(isdir(g.gl_pathv[i]))
								continue;
	#ifdef _WIN32
							GetShortPathName(g.gl_pathv[i], str, sizeof(str));
	#else
							SAFECOPY(str,g.gl_pathv[i]);
	#endif
							padfname(getfname(str),f.name);
							f.dir=dir;
							if(getfileixb(&scfg,&f)) {
								f.size=flength(g.gl_pathv[i]);
								getfiledat(&scfg,&f);
								fprintf(fp,"%-*s %s\r\n",INDEX_FNAME_LEN
									,getfname(g.gl_pathv[i]),f.desc);
							}
			/* HTML Index File */
			} else if(startup->options&FTP_OPT_HTML_INDEX_FILE 
				&& (!stricmp(p,startup->html_index_file) 
				|| !strnicmp(p,html_index_ext,strlen(html_index_ext)))
				&& !delecmd) {
				success=TRUE;
				if(getsize) {
					sockprintf(sock, "500 Size not available for dynamically generated files");
				else if(getdate)
					file_date=time(NULL);
				else {
#ifdef JAVASCRIPT
					if(startup->options&FTP_OPT_NO_JAVASCRIPT) {
						lprintf(LOG_ERR,"%04d !JavaScript disabled, cannot generate %s",sock,fname);
						sockprintf(sock, "451 JavaScript disabled");
					if(js_runtime == NULL) {
						lprintf(LOG_DEBUG,"%04d JavaScript: Creating runtime: %lu bytes"
							,sock,startup->js.max_bytes);

						if((js_runtime = JS_NewRuntime(startup->js.max_bytes))==NULL) {
							lprintf(LOG_ERR,"%04d !ERROR creating JavaScript runtime",sock);
							sockprintf(sock,"451 Error creating JavaScript runtime");
							filepos=0;
							continue;
						}
					if(js_cx==NULL) {	/* Context not yet created, create it now */
						if(((js_cx=js_initcx(js_runtime, sock,&js_glob,&js_ftp))==NULL)) {
							lprintf(LOG_ERR,"%04d !ERROR initializing JavaScript context",sock);
							sockprintf(sock,"451 Error initializing JavaScript context");
							filepos=0;
							continue;
						}
						if(js_CreateUserClass(js_cx, js_glob, &scfg)==NULL) 
							lprintf(LOG_ERR,"%04d !JavaScript ERROR creating user class",sock);
						if(js_CreateFileClass(js_cx, js_glob)==NULL) 
							lprintf(LOG_ERR,"%04d !JavaScript ERROR creating file class",sock);
						if(js_CreateUserObject(js_cx, js_glob, &scfg, "user", user.number)==NULL) 
							lprintf(LOG_ERR,"%04d !JavaScript ERROR creating user object",sock);
						if(js_CreateClientObject(js_cx, js_glob, "client", &client, sock)==NULL) 
							lprintf(LOG_ERR,"%04d !JavaScript ERROR creating client object",sock);
						if(js_CreateFileAreaObject(js_cx, js_glob, &scfg, &user
							,startup->html_index_file)==NULL) 
							lprintf(LOG_ERR,"%04d !JavaScript ERROR creating file area object",sock);
					}

					if((js_str=JS_NewStringCopyZ(js_cx, "name"))!=NULL) {
						js_val=STRING_TO_JSVAL(js_str);
						JS_SetProperty(js_cx, js_ftp, "sort", &js_val);
					}
					js_val=BOOLEAN_TO_JSVAL(FALSE);
					JS_SetProperty(js_cx, js_ftp, "reverse", &js_val);

					if(!strnicmp(p,html_index_ext,strlen(html_index_ext))) {
						p+=strlen(html_index_ext);
						tp=strrchr(p,'$');
						if(tp!=NULL)
							*tp=0;
						if(!strnicmp(p,"ext=",4)) {
							p+=4;
							if(!strcmp(p,"on"))
								user.misc|=EXTDESC;
							else
								user.misc&=~EXTDESC;
							if(!(user.rest&FLAG('G')))
								putuserrec(&scfg,user.number,U_MISC,8,ultoa(user.misc,str,16));
						} 
						else if(!strnicmp(p,"sort=",5)) {
							p+=5;
							tp=strchr(p,'&');
							if(tp!=NULL) {
								*tp=0;
								tp++;
								if(!stricmp(tp,"reverse")) {
									js_val=BOOLEAN_TO_JSVAL(TRUE);
									JS_SetProperty(js_cx, js_ftp, "reverse", &js_val);
								}
							}
							if((js_str=JS_NewStringCopyZ(js_cx, p))!=NULL) {
								js_val=STRING_TO_JSVAL(js_str);
								JS_SetProperty(js_cx, js_ftp, "sort", &js_val);
					js_val=BOOLEAN_TO_JSVAL(INT_TO_BOOL(user.misc&EXTDESC));
					JS_SetProperty(js_cx, js_ftp, "extended_descriptions", &js_val);
#endif
					if((fp=fopen(ftp_tmpfname(fname,"html",sock),"w+b"))==NULL) {
						lprintf(LOG_ERR,"%04d !ERROR %d opening %s",sock,errno,fname);
						sockprintf(sock, "451 Insufficient system storage");
						filepos=0;
						continue;
					}
					lprintf(LOG_INFO,"%04d %s downloading HTML index for %s in %s mode"
						,sock,user.alias,genvpath(lib,dir,str)
					js_val=INT_TO_JSVAL(timeleft);
					if(!JS_SetProperty(js_cx, js_ftp, "time_left", &js_val))
						lprintf(LOG_ERR,"%04d !JavaScript ERROR setting user.time_left",sock);
					js_generate_index(js_cx, js_ftp, sock, fp, lib, dir, &user);
			} else if(dir>=0) {

				if(!chk_ar(&scfg,scfg.dir[dir]->ar,&user)) {
					lprintf(LOG_WARNING,"%04d !%s has insufficient access to /%s/%s"
						,sock,user.alias
						,scfg.lib[scfg.dir[dir]->lib]->sname
						,scfg.dir[dir]->code_suffix);
					sockprintf(sock,"550 Insufficient access.");
					filepos=0;
					continue;
				}

					&& !chk_ar(&scfg,scfg.dir[dir]->dl_ar,&user)) {
					lprintf(LOG_WARNING,"%04d !%s has insufficient access to download from /%s/%s"
						,sock,user.alias
						,scfg.lib[scfg.dir[dir]->lib]->sname
						,scfg.dir[dir]->code_suffix);
					sockprintf(sock,"550 Insufficient access.");
					filepos=0;
					continue;
				}
					lprintf(LOG_WARNING,"%04d !%s has insufficient access to delete files in /%s/%s"
						,sock,user.alias
						,scfg.lib[scfg.dir[dir]->lib]->sname
						,scfg.dir[dir]->code_suffix);
					sockprintf(sock,"550 Insufficient access.");
					filepos=0;
					continue;
				}
				SAFEPRINTF2(fname,"%s%s",scfg.dir[dir]->path,p);
				GetShortPathName(fname, str, sizeof(str));
				SAFECOPY(str,fname);
#endif
				padfname(getfname(str),f.name);
				f.dir=dir;
				f.cdt=0;
				f.size=-1;
				filedat=getfileixb(&scfg,&f);
				if(!filedat && !(startup->options&FTP_OPT_DIR_FILES)) {
					sockprintf(sock,"550 File not found: %s",p);
					lprintf(LOG_WARNING,"%04d !%s file (%s%s) not in database for %.4s command"