Newer
Older
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)) {
ftp_remove(sock, __LINE__, str);
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);
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;
}
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,flength(fname)
,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),"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;
}
success=TRUE;
if(getdate)
file_date=time(NULL);
else {
lprintf(LOG_INFO,"%04d <%s> downloading index for %s in %s mode"
,sock,user.alias,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);
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);
}
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)
5248
5249
5250
5251
5252
5253
5254
5255
5256
5257
5258
5259
5260
5261
5262
5263
5264
5265
5266
5267
5268
5269
5270
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 HTML index for %s in %s mode"
,sock,user.alias,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) {
5658
5659
5660
5661
5662
5663
5664
5665
5666
5667
5668
5669
5670
5671
5672
5673
5674
5675
5676
5677
5678
5679
5680
5681
5682
5683
5684
5685
5686
5687
5688
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);
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$", "%*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];
char error[256];
char str[256];
socklen_t client_addr_len;
SOCKET client_socket;
int i;
time_t t;
time_t start;
time_t initialized=0;
SetThreadName("sbbs/ftpServer");
#ifdef _THREAD_SUID_BROKEN
if(thread_suid_broken)
startup->seteuid(TRUE);
#endif
sbbs_beep(100,500);
fprintf(stderr, "No startup structure passed!\n");
return;
}
if(startup->size!=sizeof(ftp_startup_t)) { /* verify size */
sbbs_beep(100,500);
sbbs_beep(300,500);
sbbs_beep(100,500);
fprintf(stderr, "Invalid startup structure!\n");
return;
}
ZERO_VAR(js_server_props);
SAFEPRINTF2(js_server_props.version,"%s %s",FTP_SERVER,revision);
js_server_props.version_detail=ftp_ver();
js_server_props.clients=&active_clients.value;
js_server_props.options=&startup->options;
js_server_props.interfaces=&startup->interfaces;
uptime=0;
served=0;
startup->recycle_now=FALSE;
startup->shutdown_now=FALSE;
terminate_server=FALSE;
protected_uint32_init(&thread_count, 0);
/* Setup intelligent defaults */
if(startup->port==0) startup->port=IPPORT_FTP;
if(startup->qwk_timeout==0) startup->qwk_timeout=FTP_DEFAULT_QWK_TIMEOUT; /* seconds */
if(startup->max_inactivity==0) startup->max_inactivity=FTP_DEFAULT_MAX_INACTIVITY; /* seconds */
if(startup->sem_chk_freq==0) startup->sem_chk_freq=DEFAULT_SEM_CHK_FREQ; /* seconds */
if(startup->index_file_name[0]==0) SAFECOPY(startup->index_file_name,"00index");
if(startup->html_index_file[0]==0) SAFECOPY(startup->html_index_file,"00index.html");
if(startup->html_index_script[0]==0) SAFECOPY(startup->html_index_script,"ftp-html.js");
if(startup->options&FTP_OPT_HTML_INDEX_FILE)
startup->options&=~FTP_OPT_NO_JAVASCRIPT;
else
startup->options|=FTP_OPT_NO_JAVASCRIPT;
if(startup->js.max_bytes==0) startup->js.max_bytes=JAVASCRIPT_MAX_BYTES;
if(startup->js.cx_stack==0) startup->js.cx_stack=JAVASCRIPT_CONTEXT_STACK;
protected_uint32_adjust(&thread_count,1);
thread_up(FALSE /* setuid */);
status("Initializing");
memset(&scfg, 0, sizeof(scfg));