Newer
Older

rswindell
committed
delecmd=FALSE;
file_date=0;
file_size=-1;
if(!strnicmp(cmd,"SIZE ",5))
getsize=TRUE;
else if(!strnicmp(cmd,"MDTM ",5))
getdate=TRUE;

rswindell
committed
else if(!strnicmp(cmd,"DELE ",5))
delecmd=TRUE;
if(!getsize && !getdate && user.rest&FLAG('D')) {
filepos=0;
continue;
}
credits=TRUE;
success=FALSE;
delfile=FALSE;
tmpfile=FALSE;
lib=curlib;
dir=curdir;
p=cmd+5;
SKIP_WHITESPACE(p);
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, &client, &dir)==TRUE) {
success=TRUE;
credits=TRUE; /* include in d/l stats */
tmpfile=FALSE;
delfile=FALSE;
lprintf(LOG_INFO,"%04d <%s> %.4s by alias: %s"
if(dir>=0)
lib=scfg.dir[dir]->lib;
}
if(!success && lib<0 && (tp=strchr(p,'/'))!=NULL) {
dir=-1;
*tp=0;
for(i=0;i<scfg.total_libs;i++) {
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(!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

rswindell
committed
&& !stricmp(p,str) && !delecmd) {
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 <%s> !ERROR creating semaphore file: %s"
,sock, user.alias, str);
t=time(NULL);
while(fexist(str) && !terminate_server) {
if(!socket_check(sock,NULL,NULL,0))
break;
if(time(NULL)-t>startup->qwk_timeout)
break;
mswait(1000);
}
if(!socket_check(sock,NULL,NULL,0)) {
lprintf(LOG_NOTICE,"%04d <%s> disconnected while waiting for QWK packet creation"
,sock, user.alias);
ftp_remove(sock, __LINE__, str, user.alias);
continue;
}
if(fexist(str)) {
lprintf(LOG_WARNING,"%04d <%s> !TIMEOUT waiting for QWK packet creation", sock, user.alias);
sockprintf(sock,sess,"451 Time-out waiting for packet creation.");
ftp_remove(sock, __LINE__, str, user.alias);
filepos=0;
continue;
}
if(!fexistcase(qwkfile)) {
lprintf(LOG_INFO,"%04d <%s> No QWK Packet created (no new messages)", sock, user.alias);
sockprintf(sock,sess,"550 No QWK packet created (no new messages)");
filepos=0;
continue;
}
file_size = flength(fname);
if(file_size < 1) {
lprintf(LOG_WARNING, "%04d <%s> Invalid QWK packet file size (%"PRIuOFF" bytes): %s"
,sock, user.alias, file_size, fname);
sockprintf(sock,sess,"550 Invalid QWK packet file size: %"PRIuOFF" bytes", file_size);
filepos=0;
continue;
}
success=TRUE;
delfile=TRUE;
credits=FALSE;
if(!getsize && !getdate)
lprintf(LOG_INFO,"%04d <%s> downloading QWK packet (%"PRIuOFF" bytes) in %s mode"
,sock,user.alias,file_size
,mode);
} else if(startup->options&FTP_OPT_INDEX_FILE

rswindell
committed
&& !stricmp(p,startup->index_file_name)
&& !delecmd) {
if(getsize) {
sockprintf(sock,sess, "550 Size not available for dynamically generated files");
continue;
}
if((fp=fopen(ftp_tmpfname(fname,"ndx",sock),"wb"))==NULL) {
lprintf(LOG_ERR,"%04d <%s> !ERROR %d (%s) line %d opening %s"
,sock, user.alias, errno, strerror(errno), __LINE__, fname);
filepos=0;
continue;
}
success=TRUE;
if(getdate)
file_date=time(NULL);
else {
lprintf(LOG_INFO,"%04d <%s> downloading %s for %s in %s mode"
,sock, user.alias, startup->index_file_name, genvpath(lib,dir,str)
,mode);
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;
p=aliasline; /* alias pointer */
SKIP_WHITESPACE(p);
if(*p==';') /* comment */
continue;
tp=p; /* terminator pointer */
FIND_WHITESPACE(tp);
if(*tp) *tp=0;
np=tp+1; /* filename pointer */
SKIP_WHITESPACE(np);
np++; /* description pointer */
FIND_WHITESPACE(np);
while(*np && *np<' ') np++;
truncsp(np);
fprintf(fp,"%-*s %s\r\n",INDEX_FNAME_LEN,p,np);
}
fclose(alias_fp);
}
/* 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++) {
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!=(int)scfg.sysop_dir && i!=(int)scfg.upload_dir
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,&client)){
sprintf(cmd,"%s*",scfg.dir[dir]->path);
time_t start = time(NULL);
glob(cmd, GLOB_MARK, NULL, &g);
for(i=0;i<(int)g.gl_pathc;i++) {
if(*lastchar(g.gl_pathv[i]) == '/') /* is directory */
continue;
#ifdef _WIN32
GetShortPathName(g.gl_pathv[i], str, sizeof(str));
#else
SAFECOPY(str,g.gl_pathv[i]);
#endif
memset(&f, 0, sizeof(f));
padfname(getfname(str),f.name);
f.dir=dir;
if((filedat=getfileixb(&scfg,&f))==FALSE
&& !(startup->options&FTP_OPT_DIR_FILES)
&& !(scfg.dir[dir]->misc&DIR_FILES))
continue;
f.size = -1; // Not used, don't query
if(filedat && !getfiledat(&scfg,&f))
continue;
fprintf(fp,"%-*s %s\r\n",INDEX_FNAME_LEN
,getfname(g.gl_pathv[i]),f.desc);
lprintf(LOG_INFO, "%04d <%s> index (%ld bytes) of /%s/%s (%lu files) created in %ld seconds"
,sock, user.alias, ftell(fp), scfg.lib[lib]->sname, scfg.dir[dir]->code_suffix
,(ulong)g.gl_pathc, (long)time(NULL) - start);
globfree(&g);
fclose(fp);
/* 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)))
success=TRUE;
if(getsize) {
sockprintf(sock,sess, "550 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 <%s> !JavaScript disabled, cannot generate %s",sock, user.alias, fname);

rswindell
committed
filepos=0;
continue;
}
if(js_runtime == NULL) {
lprintf(LOG_DEBUG,"%04d <%s> JavaScript: Creating runtime: %lu bytes"
,sock, user.alias,startup->js.max_bytes);
if((js_runtime = jsrt_GetNew(startup->js.max_bytes, 1000, __FILE__, __LINE__))==NULL) {
lprintf(LOG_ERR,"%04d <%s> !ERROR creating JavaScript runtime",sock, user.alias);
sockprintf(sock,sess,"451 Error creating JavaScript runtime");
filepos=0;
continue;
}
}
if(js_cx==NULL) { /* Context not yet created, create it now */
/* js_initcx() starts a request */
if(((js_cx=js_initcx(js_runtime, sock,&js_glob,&js_ftp,&js_callback))==NULL)) {
lprintf(LOG_ERR,"%04d <%s> !ERROR initializing JavaScript context",sock, user.alias);
sockprintf(sock,sess,"451 Error initializing JavaScript context");
filepos=0;
continue;
}
if(js_CreateUserClass(js_cx, js_glob, &scfg)==NULL)
lprintf(LOG_ERR,"%04d <%s> !JavaScript ERROR creating user class",sock, user.alias);
if(js_CreateFileClass(js_cx, js_glob)==NULL)
lprintf(LOG_ERR,"%04d <%s> !JavaScript ERROR creating file class",sock, user.alias);
if(js_CreateUserObject(js_cx, js_glob, &scfg, "user", &user, &client, /* global_user: */TRUE)==NULL)
lprintf(LOG_ERR,"%04d <%s> !JavaScript ERROR creating user object",sock, user.alias);
if(js_CreateClientObject(js_cx, js_glob, "client", &client, sock, -1)==NULL)
lprintf(LOG_ERR,"%04d <%s> !JavaScript ERROR creating client object",sock, user.alias);
if(js_CreateFileAreaObject(js_cx, js_glob, &scfg, &user, &client
,startup->html_index_file)==NULL)
lprintf(LOG_ERR,"%04d <%s> !JavaScript ERROR creating file area object",sock, user.alias);
}
JS_BEGINREQUEST(js_cx);
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)
5323
5324
5325
5326
5327
5328
5329
5330
5331
5332
5333
5334
5335
5336
5337
5338
5339
5340
5341
5342
5343
5344
5345
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);
JS_ENDREQUEST(js_cx);
if((fp=fopen(ftp_tmpfname(fname,"html",sock),"w+b"))==NULL) {
lprintf(LOG_ERR,"%04d <%s> !ERROR %d (%s) line %d opening %s"
,sock, user.alias, errno, strerror(errno), __LINE__, fname);
filepos=0;
continue;
}
lprintf(LOG_INFO,"%04d <%s> downloading %s for %s in %s mode"
,sock, user.alias, startup->html_index_file, genvpath(lib,dir,str)
,mode);
credits=FALSE;
tmpfile=TRUE;
delfile=TRUE;
JS_BEGINREQUEST(js_cx);
js_val=INT_TO_JSVAL(timeleft);
if(!JS_SetProperty(js_cx, js_ftp, "time_left", &js_val))
lprintf(LOG_ERR,"%04d <%s> !JavaScript ERROR setting user.time_left",sock, user.alias);
js_generate_index(js_cx, js_ftp, sock, fp, lib, dir, &user, &client);
JS_ENDREQUEST(js_cx);
fclose(fp);
}
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);
filepos=0;
continue;
}

rswindell
committed
if(!getsize && !getdate && !delecmd
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);
filepos=0;
continue;
}

rswindell
committed
if(delecmd && !dir_op(&scfg,&user,&client,dir) && !(user.exempt&FLAG('R'))) {
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);

rswindell
committed
filepos=0;
continue;
}
SAFEPRINTF2(fname,"%s%s",scfg.dir[dir]->path,p);
GetShortPathName(fname, str, sizeof(str));
#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) && !(scfg.dir[dir]->misc&DIR_FILES)) {
lprintf(LOG_WARNING,"%04d <%s> file (%s%s) not in database for %.4s command"
,sock,user.alias,genvpath(lib,dir,str),p,cmd);
filepos=0;
continue;
}

rswindell
committed
/* Verify credits */
if(!getsize && !getdate && !delecmd
if(filedat)
getfiledat(&scfg,&f);
else
f.cdt=flength(fname);
if(f.cdt>(user.cdt+user.freecdt)) {
lprintf(LOG_WARNING,"%04d <%s> has insufficient credit to download /%s/%s/%s (%lu credits)"
,sock,user.alias,scfg.lib[scfg.dir[dir]->lib]->sname
,scfg.dir[dir]->code_suffix
,(ulong)f.cdt);
sockprintf(sock,sess,"550 Insufficient credit (%lu required).", (ulong)f.cdt);
filepos=0;
continue;
}
}
if(strcspn(p,ILLEGAL_FILENAME_CHARS)!=strlen(p)) {
success=FALSE;
lprintf(LOG_WARNING,"%04d <%s> !ILLEGAL FILENAME ATTEMPT by %s [%s]: %s"
,sock, user.alias, host_name, host_ip, p);
ftp_hacklog("FTP FILENAME", user.alias, cmd, host_name, &ftp.client_addr);
if(fexistcase(fname)) {

rswindell
committed
if(!getsize && !getdate && !delecmd)
lprintf(LOG_INFO,"%04d <%s> downloading: %s (%"PRIuOFF" bytes) in %s mode"
,sock,user.alias,fname,flength(fname)
,mode);
#if defined(SOCKET_DEBUG_DOWNLOAD)
socket_debug[sock]|=SOCKET_DEBUG_DOWNLOAD;
if(getsize && success)
else if(getdate && success) {
if(file_date==0)
file_date = fdate(fname);
if(gmtime_r(&file_date,&tm)==NULL) /* specifically use GMT/UTC representation */
,1900+tm.tm_year,tm.tm_mon+1,tm.tm_mday
,tm.tm_hour,tm.tm_min,tm.tm_sec);

rswindell
committed
} else if(delecmd && success) {
if(removecase(fname)!=0) {
lprintf(LOG_ERR,"%04d <%s> !ERROR %d (%s) deleting %s", sock, user.alias, errno, strerror(errno), fname);
sockprintf(sock,sess,"450 %s could not be deleted (error: %d)"

rswindell
committed
,fname,errno);
} else {
lprintf(LOG_NOTICE,"%04d <%s> deleted %s",sock,user.alias,fname);

rswindell
committed
if(filedat)
removefiledat(&scfg,&f);

rswindell
committed
}
sockprintf(sock,sess,"150 Opening BINARY mode data connection for file transfer.");
filexfer(&data_addr,sock,sess,pasv_sock,pasv_sess,&data_sock,&data_sess,fname,filepos
,&transfer_inprogress,&transfer_aborted,delfile,tmpfile
,&lastactive,&user,&client,dir,FALSE,credits,FALSE,NULL,protection);
lprintf(LOG_WARNING,"%04d <%s> file (%s%s) not found for %.4s command"
,sock,user.alias,genvpath(lib,dir,str),p,cmd);
#if defined(SOCKET_DEBUG_DOWNLOAD)
socket_debug[sock]&=~SOCKET_DEBUG_DOWNLOAD;
continue;
}
if(!strnicmp(cmd, "DESC", 4)) {
if(user.rest&FLAG('U')) {
continue;
}
p=cmd+4;
SKIP_WHITESPACE(p);
sockprintf(sock,sess,"200 File description set. Ready to STOR file.");
if(!strnicmp(cmd, "STOR ", 5) || !strnicmp(cmd, "APPE ", 5)) {
if(user.rest&FLAG('U')) {
continue;
}
if(transfer_inprogress==TRUE) {
lprintf(LOG_WARNING,"%04d <%s> !TRANSFER already in progress (%s)",sock, user.alias, cmd);
lib=curlib;
dir=curdir;
p=cmd+5;
SKIP_WHITESPACE(p);
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;
/* Need to add support for uploading to aliased directories */
if(lib<0 && (tp=strchr(p,'/'))!=NULL) {
dir=-1;
*tp=0;
for(i=0;i<scfg.total_libs;i++) {
continue;
if(!stricmp(scfg.lib[i]->sname,p))
break;
}
if(i<scfg.total_libs)
lib=i;
p=tp+1;
}
if(dir<0 && (tp=strchr(p,'/'))!=NULL) {
*tp=0;
for(i=0;i<scfg.total_dirs;i++) {
if(scfg.dir[i]->lib!=lib)
continue;
if(i!=(int)scfg.sysop_dir && i!=(int)scfg.upload_dir
if(!stricmp(scfg.dir[i]->code_suffix,p))
break;
}
if(i<scfg.total_dirs)
dir=i;
p=tp+1;
}
if(dir<0) {
sprintf(str,"%s.rep",scfg.sys_id);
if(!(startup->options&FTP_OPT_ALLOW_QWK)
|| stricmp(p,str)) {
lprintf(LOG_WARNING,"%04d <%s> !attempted to upload to invalid directory"

rswindell
committed
sprintf(fname,"%sfile/%04d.rep",scfg.data_dir,user.number);
lprintf(LOG_INFO,"%04d <%s> uploading: %s in %s mode"
,sock,user.alias,fname
,mode);
append=(strnicmp(cmd,"APPE",4)==0);
if(!dir_op(&scfg,&user,&client,dir) && !(user.exempt&FLAG('U'))) {
if(!chk_ar(&scfg,scfg.dir[dir]->ul_ar,&user,&client)) {
lprintf(LOG_WARNING,"%04d <%s> cannot upload to /%s/%s (insufficient access)"
,sock,user.alias
,scfg.lib[scfg.dir[dir]->lib]->sname
,scfg.dir[dir]->code_suffix);
continue;
}
if(!append && scfg.dir[dir]->maxfiles && getfiles(&scfg,dir)>=scfg.dir[dir]->maxfiles) {
lprintf(LOG_WARNING,"%04d <%s> cannot upload to /%s/%s (directory full: %ld files)"
,sock,user.alias
,scfg.lib[scfg.dir[dir]->lib]->sname
,scfg.dir[dir]->code_suffix
,getfiles(&scfg,dir));
continue;
}
if(*p=='-'
|| strcspn(p,ILLEGAL_FILENAME_CHARS)!=strlen(p)
|| trashcan(&scfg,p,"file")) {
lprintf(LOG_WARNING,"%04d <%s> !ILLEGAL FILENAME ATTEMPT by %s [%s]: %s"
,sock, user.alias, host_name, host_ip, p);
ftp_hacklog("FTP FILENAME", user.alias, cmd, host_name, &ftp.client_addr);
SAFEPRINTF2(fname,"%s%s",scfg.dir[dir]->path,p);
if((!append && filepos==0 && fexist(fname))
|| (startup->options&FTP_OPT_INDEX_FILE
&& !stricmp(p,startup->index_file_name))
|| (startup->options&FTP_OPT_HTML_INDEX_FILE
&& !stricmp(p,startup->html_index_file))
) {
lprintf(LOG_WARNING,"%04d <%s> attempted to overwrite existing file: %s"
if(append || filepos) { /* RESUME */
#ifdef _WIN32
GetShortPathName(fname, str, sizeof(str));
#else
SAFECOPY(str,fname);
#endif
padfname(getfname(str),f.name);
f.dir=dir;
f.cdt=0;
f.size=-1;
if(!getfileixb(&scfg,&f) || !getfiledat(&scfg,&f)) {
lprintf(LOG_WARNING,"%04d <%s> file (%s) not in database for %.4s command"
,sock,user.alias,fname,cmd);
continue;
}
append=FALSE;
}
/* Verify user is original uploader */
if((append || filepos) && stricmp(f.uler,user.alias)) {
lprintf(LOG_WARNING,"%04d <%s> !cannot resume upload of %s, uploaded by %s"
,sock,user.alias,fname,f.uler);
sockprintf(sock,sess,"553 Insufficient access (can't resume upload from different user).");
continue;
}
}
lprintf(LOG_INFO,"%04d <%s> uploading: %s to %s (%s) in %s mode"
,sock,user.alias
,p /* filename */
,genvpath(lib,dir,str) /* virtual path */
,scfg.dir[dir]->path /* actual path */
,mode);
sockprintf(sock,sess,"150 Opening BINARY mode data connection for file transfer.");
filexfer(&data_addr,sock,sess,pasv_sock,pasv_sess,&data_sock,&data_sess,fname,filepos
,&transfer_inprogress,&transfer_aborted,FALSE,FALSE
,&lastactive
,&user
,dir
,TRUE /* uploading */
,TRUE /* credits */
);
filepos=0;
continue;
}
if(!stricmp(cmd,"CDUP") || !stricmp(cmd,"XCUP")) {
if(curdir<0)
curlib=-1;
else
curdir=-1;
continue;
}
if(!strnicmp(cmd, "CWD ", 4) || !strnicmp(cmd,"XCWD ",5)) {
p=cmd+4;
SKIP_WHITESPACE(p);
if(!strnicmp(p,BBS_FSYS_DIR,strlen(BBS_FSYS_DIR)))
p+=strlen(BBS_FSYS_DIR); /* already mounted */
if(*p=='/') {
curlib=-1;
curdir=-1;
p++;
}
/* Local File System? */
if(sysop && !(startup->options&FTP_OPT_NO_LOCAL_FSYS)
&& !strnicmp(p,LOCAL_FSYS_DIR,strlen(LOCAL_FSYS_DIR))) {
p+=strlen(LOCAL_FSYS_DIR);
if(!direxist(p)) {
lprintf(LOG_WARNING,"%04d <%s> attempted to mount invalid directory: %s"
,sock, user.alias, p);
continue;
}
sockprintf(sock,sess,"250 CWD command successful (local file system mounted).");
lprintf(LOG_INFO,"%04d <%s> mounted local file system", sock, user.alias);
continue;
}
success=FALSE;
/* Directory Alias? */
if(curlib<0 && ftpalias(p,NULL,&user,&client,&curdir)==TRUE) {
5733
5734
5735
5736
5737
5738
5739
5740
5741
5742
5743
5744
5745
5746
5747
5748
5749
5750
5751
5752
5753
5754
5755
5756
5757
5758
5759
5760
5761
5762
5763
if(curdir>=0)
curlib=scfg.dir[curdir]->lib;
success=TRUE;
}
orglib=curlib;
orgdir=curdir;
tp=0;
if(!strncmp(p,"...",3)) {
curlib=-1;
curdir=-1;
p+=3;
}
if(!strncmp(p,"./",2))
p+=2;
else if(!strncmp(p,"..",2)) {
if(curdir<0)
curlib=-1;
else
curdir=-1;
p+=2;
}
if(*p==0)
success=TRUE;
else if(!strcmp(p,"."))
success=TRUE;
if(!success && (curlib<0 || *p=='/')) { /* Root dir */
if(*p=='/') p++;
tp=strchr(p,'/');
if(tp) *tp=0;
for(i=0;i<scfg.total_libs;i++) {
continue;
if(!stricmp(scfg.lib[i]->sname,p))
break;
}
if(i<scfg.total_libs) {
curlib=i;
success=TRUE;
}
}
if((!success && curdir<0) || (success && tp && *(tp+1))) {
if(tp)
p=tp+1;
tp=lastchar(p);
if(tp && *tp=='/') *tp=0;
for(i=0;i<scfg.total_dirs;i++) {
if(scfg.dir[i]->lib!=curlib)
continue;
if(i!=(int)scfg.sysop_dir && i!=(int)scfg.upload_dir
if(!stricmp(scfg.dir[i]->code_suffix,p))
break;
}
if(i<scfg.total_dirs) {
curdir=i;
success=TRUE;
} else
success=FALSE;
}
if(success)
sockprintf(sock,sess,"550 %s: No such directory.",p);
curlib=orglib;
curdir=orgdir;
}
continue;
}
if(!stricmp(cmd, "PWD") || !stricmp(cmd,"XPWD")) {
if(curlib<0)
,scfg.lib[curlib]->sname);
else
,scfg.lib[curlib]->sname
,scfg.dir[curdir]->code_suffix);
if(!strnicmp(cmd, "MKD", 3) ||
!strnicmp(cmd,"XMKD",4) ||
!strnicmp(cmd,"SITE EXEC",9)) {
lprintf(LOG_WARNING,"%04d <%s> !SUSPECTED HACK ATTEMPT: %s"
,sock,user.alias,cmd);
ftp_hacklog("FTP", user.alias, cmd, host_name, &ftp.client_addr);
// TODO: STAT is mandatory
lprintf(LOG_WARNING,"%04d <%s> !UNSUPPORTED COMMAND: %s"
,sock,user.alias,cmd);
} /* while(1) */
#if defined(SOCKET_DEBUG_TERMINATE)
socket_debug[sock]|=SOCKET_DEBUG_TERMINATE;
#endif
if(transfer_inprogress==TRUE) {
lprintf(LOG_DEBUG,"%04d Waiting for transfer to complete...",sock);
while(transfer_inprogress==TRUE) {
mswait(2000); /* allow xfer threads to terminate */
break;
}

rswindell
committed
if(!transfer_aborted) {
if(gettimeleft(&scfg,&user,logintime)<1) {
lprintf(LOG_WARNING,"%04d Out of time, disconnecting",sock);
sockprintf(sock,sess,"421 Sorry, you've run out of time.");
ftp_close_socket(&data_sock,&data_sess,__LINE__);

rswindell
committed
transfer_aborted=TRUE;
}
if((time(NULL)-lastactive)>startup->max_inactivity) {
lprintf(LOG_WARNING,"%04d Disconnecting due to to inactivity",sock);
sockprintf(sock,sess,"421 Disconnecting due to inactivity (%u seconds)."

rswindell
committed
,startup->max_inactivity);

rswindell
committed
transfer_aborted=TRUE;
}
if(count && (count%60)==0)
lprintf(LOG_WARNING,"%04d Still waiting for transfer to complete "
"(count=%lu, aborted=%d, lastactive=%lX) ..."
,sock,count,transfer_aborted,lastactive);
count++;
mswait(1000);
lprintf(LOG_DEBUG,"%04d Done waiting for transfer to complete",sock);
if(user.number) {
/* Update User Statistics */
if(!logoutuserdat(&scfg, &user, time(NULL), logintime))
lprintf(LOG_ERR,"%04d <%s> !ERROR in logoutuserdat", sock, user.alias);
/* Remove QWK-pack semaphore file (if left behind) */
sprintf(str,"%spack%04u.now",scfg.data_dir,user.number);
ftp_remove(sock, __LINE__, str, user.alias);
lprintf(LOG_INFO,"%04d <%s> logged off", sock, user.alias);
if(startup->hangup_sound[0] && !(startup->options&FTP_OPT_MUTE))
PlaySound(startup->hangup_sound, NULL, SND_ASYNC|SND_FILENAME);
lprintf(LOG_DEBUG,"%04d JavaScript: Destroying context",sock);
JS_BEGINREQUEST(js_cx);
JS_RemoveObjectRoot(js_cx, &js_glob);
JS_ENDREQUEST(js_cx);
JS_DestroyContext(js_cx); /* Free Context */
}

rswindell
committed
if(js_runtime!=NULL) {
lprintf(LOG_DEBUG,"%04d JavaScript: Destroying runtime",sock);
jsrt_Release(js_runtime);

rswindell
committed
}
/* status(STATUS_WFC); server thread should control status display */
if(pasv_sock!=INVALID_SOCKET)
if(data_sock!=INVALID_SOCKET)
client_off(sock);
socket_debug[sock]&=~SOCKET_DEBUG_CTRL;
#if defined(SOCKET_DEBUG_TERMINATE)
socket_debug[sock]&=~SOCKET_DEBUG_TERMINATE;
#endif
tmp_sock=sock;
int32_t clients = protected_uint32_adjust(&active_clients, -1);
int32_t threads = thread_down();
update_clients();
lprintf(LOG_INFO,"%04d CTRL thread terminated (%d clients and %d threads remain, %lu served)"
,sock, clients, threads, served);
lprintf(LOG_DEBUG,"0000 cleanup called from line %d",line);
if(protected_uint32_value(thread_count) > 1) {
lprintf(LOG_INFO, "0000 Waiting for %d child threads to terminate", protected_uint32_value(thread_count)-1);
while(protected_uint32_value(thread_count) > 1) {
mswait(100);
}
lprintf(LOG_INFO, "0000 Done waiting for child threads to terminate");
}
free_text(text);
semfile_list_free(&recycle_semfiles);
semfile_list_free(&shutdown_semfiles);
if(ftp_set != NULL) {
xpms_destroy(ftp_set, ftp_close_socket_cb, NULL);
ftp_set = NULL;
}
update_clients(); /* active_clients is destroyed below */
if(protected_uint32_value(active_clients))
lprintf(LOG_WARNING,"!!!! Terminating with %d active clients", protected_uint32_value(active_clients));
protected_uint32_destroy(active_clients);
#ifdef _WINSOCKAPI_
if(WSAInitialized && WSACleanup()!=0)
lprintf(LOG_ERR,"0000 !WSACleanup ERROR %d",ERROR_VALUE);
if(terminate_server || code)
lprintf(LOG_INFO,"#### FTP Server thread terminated (%lu clients served)", served);
if(startup!=NULL && startup->terminated!=NULL)
startup->terminated(startup->cbdata,code);
const char* DLLCALL ftp_ver(void)
{
static char ver[256];
char compiler[32];
sscanf("$Revision: 1.501 $", "%*s %s", revision);
sprintf(ver,"%s %s%s "
#ifdef _DEBUG
," Debug"
#else
,""
#endif
,__DATE__, __TIME__, compiler);
return(ver);
}
void DLLCALL ftp_server(void* arg)
char* p;
char path[MAX_PATH+1];