--- sbbs/src/sbbs3/ftpsrvr.c 2018/04/24 16:41:24 1.1.1.1 +++ sbbs/src/sbbs3/ftpsrvr.c 2018/04/24 16:43:52 1.1.1.2 @@ -2,13 +2,13 @@ /* Synchronet FTP server */ -/* $Id: ftpsrvr.c,v 1.1.1.1 2018/04/24 16:41:24 root Exp $ */ +/* $Id: ftpsrvr.c,v 1.1.1.2 2018/04/24 16:43:52 root Exp $ */ /**************************************************************************** * @format.tab-size 4 (Plain Text/Source Code File Header) * * @format.use-tabs true (see http://www.synchro.net/ptsc_hdr.html) * * * - * Copyright 2006 Rob Swindell - http://www.synchro.net/copyright.html * + * Copyright 2011 Rob Swindell - http://www.synchro.net/copyright.html * * * * This program is free software; you can redistribute it and/or * * modify it under the terms of the GNU General Public License * @@ -51,6 +51,8 @@ #include "text.h" /* TOTAL_TEXT */ #include "ftpsrvr.h" #include "telnet.h" +#include "js_rtpool.h" +#include "js_request.h" /* Constants */ @@ -77,18 +79,16 @@ static ftp_startup_t* startup=NULL; static scfg_t scfg; static SOCKET server_socket=INVALID_SOCKET; -static DWORD active_clients=0; -static DWORD sockets=0; -static DWORD thread_count=0; -static time_t uptime=0; -static DWORD served=0; -static BOOL terminate_server=FALSE; +static protected_int32_t active_clients; +static volatile time_t uptime=0; +static volatile ulong served=0; +static volatile BOOL terminate_server=FALSE; static char revision[16]; static char *text[TOTAL_TEXT]; static str_list_t recycle_semfiles; static str_list_t shutdown_semfiles; -#ifdef _DEBUG +#ifdef SOCKET_DEBUG static BYTE socket_debug[0x10000]={0}; #define SOCKET_DEBUG_CTRL (1<<0) /* 0x01 */ @@ -101,6 +101,7 @@ static str_list_t shutdown_semfiles; #define SOCKET_DEBUG_FILEXFER (1<<7) /* 0x80 */ #endif +char* genvpath(int lib, int dir, char* str); typedef struct { SOCKET socket; @@ -119,28 +120,37 @@ BOOL direxist(char *dir) return(FALSE); } -BOOL dir_op(scfg_t* cfg, user_t* user, uint dirnum) +BOOL dir_op(scfg_t* cfg, user_t* user, client_t* client, uint dirnum) { - return(user->level>=SYSOP_LEVEL || user->exempt&FLAG('R') - || (cfg->dir[dirnum]->op_ar[0] && chk_ar(cfg,cfg->dir[dirnum]->op_ar,user))); + return(user->level>=SYSOP_LEVEL + || (cfg->dir[dirnum]->op_ar[0] && chk_ar(cfg,cfg->dir[dirnum]->op_ar,user,client))); } -static int lprintf(int level, char *fmt, ...) +static int lprintf(int level, const char *fmt, ...) { - int result; va_list argptr; char sbuf[1024]; - if(startup==NULL || startup->lputs==NULL) - return(0); - va_start(argptr,fmt); vsnprintf(sbuf,sizeof(sbuf),fmt,argptr); sbuf[sizeof(sbuf)-1]=0; va_end(argptr); - result=startup->lputs(startup->cbdata,level,sbuf); - return(result); + if(level <= LOG_ERR) { + errorlog(&scfg, startup==NULL ? NULL:startup->host_name, sbuf); + if(startup!=NULL && startup->errormsg!=NULL) + startup->errormsg(startup->cbdata,level,sbuf); + } + + if(startup==NULL || startup->lputs==NULL || level > startup->log_level) + return(0); + +#if defined(_WIN32) + if(IsBadCodePtr((FARPROC)startup->lputs)) + return(0); +#endif + + return startup->lputs(startup->cbdata,level,sbuf); } #ifdef _WINSOCKAPI_ @@ -155,12 +165,12 @@ static BOOL winsock_startup(void) int status; /* Status Code */ if((status = WSAStartup(MAKEWORD(1,1), &WSAData))==0) { - lprintf(LOG_INFO,"%s %s",WSAData.szDescription, WSAData.szSystemStatus); + lprintf(LOG_DEBUG,"%s %s",WSAData.szDescription, WSAData.szSystemStatus); WSAInitialized=TRUE; return (TRUE); } - lprintf(LOG_ERR,"!WinSock startup ERROR %d", status); + lprintf(LOG_CRIT,"!WinSock startup ERROR %d", status); return (FALSE); } @@ -180,7 +190,7 @@ static void status(char* str) static void update_clients(void) { if(startup!=NULL && startup->clients!=NULL) - startup->clients(startup->cbdata,active_clients); + startup->clients(startup->cbdata,active_clients.value); } static void client_on(SOCKET sock, client_t* client, BOOL update) @@ -197,15 +207,12 @@ static void client_off(SOCKET sock) static void thread_up(BOOL setuid) { - thread_count++; if(startup!=NULL && startup->thread_up!=NULL) startup->thread_up(startup->cbdata,TRUE, setuid); } static void thread_down(void) { - if(thread_count>0) - thread_count--; if(startup!=NULL && startup->thread_up!=NULL) startup->thread_up(startup->cbdata,FALSE, FALSE); } @@ -221,10 +228,6 @@ static SOCKET ftp_open_socket(int type) if(sock!=INVALID_SOCKET) { if(set_socket_options(&scfg, sock, "FTP", error, sizeof(error))) lprintf(LOG_ERR,"%04d !ERROR %s",sock, error); - sockets++; -#ifdef _DEBUG - lprintf(LOG_DEBUG,"%04d Socket opened (%u sockets in use)",sock,sockets); -#endif } return(sock); } @@ -247,17 +250,10 @@ static int ftp_close_socket(SOCKET* sock if(startup!=NULL && startup->socket_open!=NULL) startup->socket_open(startup->cbdata,FALSE); - sockets--; - if(result!=0) { if(ERROR_VALUE!=ENOTSOCK) lprintf(LOG_WARNING,"%04d !ERROR %d closing socket from line %u",*sock,ERROR_VALUE,line); - } else if(sock==&server_socket || *sock==server_socket) - lprintf(LOG_DEBUG,"%04d Server socket closed (%u sockets in use) from line %u",*sock,sockets,line); -#ifdef _DEBUG - else - lprintf(LOG_DEBUG,"%04d Socket closed (%u sockets in use) from line %u",*sock,sockets,line); -#endif + } *sock=INVALID_SOCKET; return(result); @@ -326,12 +322,12 @@ static int sockprintf(SOCKET sock, char /* Returns the directory index of a virtual lib/dir path (e.g. main/games/filename) */ -int getdir(char* p, user_t* user) +int getdir(char* p, user_t* user, client_t* client) { char* tp; char path[MAX_PATH+1]; - int dir; - int lib; + uint dir; + uint lib; SAFECOPY(path,p); p=path; @@ -344,7 +340,7 @@ int getdir(char* p, user_t* user) tp=strchr(p,'/'); if(tp) *tp=0; for(lib=0;libar,user)) + if(!chk_ar(&scfg,scfg.lib[lib]->ar,user,client)) continue; if(!stricmp(scfg.lib[lib]->sname,p)) break; @@ -361,7 +357,7 @@ int getdir(char* p, user_t* user) if(scfg.dir[dir]->lib!=lib) continue; if(dir!=scfg.sysop_dir && dir!=scfg.upload_dir - && !chk_ar(&scfg,scfg.dir[dir]->ar,user)) + && !chk_ar(&scfg,scfg.dir[dir]->ar,user,client)) continue; if(!stricmp(scfg.dir[dir]->code_suffix,p)) break; @@ -385,6 +381,7 @@ js_write(JSContext *cx, JSObject *obj, u uintN i; JSString* str=NULL; FILE* fp; + jsrefcount rc; if((fp=(FILE*)JS_GetContextPrivate(cx))==NULL) return(JS_FALSE); @@ -393,7 +390,9 @@ js_write(JSContext *cx, JSObject *obj, u str = JS_ValueToString(cx, argv[i]); if (!str) return JS_FALSE; + rc=JS_SUSPENDREQUEST(cx); fprintf(fp,"%s",JS_GetStringBytes(str)); + JS_RESUMEREQUEST(cx, rc); } if(str==NULL) @@ -407,12 +406,15 @@ static JSBool js_writeln(JSContext *cx, JSObject *obj, uintN argc, jsval *argv, jsval *rval) { FILE* fp; + jsrefcount rc; if((fp=(FILE*)JS_GetContextPrivate(cx))==NULL) return(JS_FALSE); js_write(cx,obj,argc,argv,rval); + rc=JS_SUSPENDREQUEST(cx); fprintf(fp,"\r\n"); + JS_RESUMEREQUEST(cx, rc); return(JS_TRUE); } @@ -430,6 +432,7 @@ js_ErrorReporter(JSContext *cx, const ch char file[MAX_PATH+1]; char* warning; FILE* fp; + int log_level; fp=(FILE*)JS_GetContextPrivate(cx); @@ -455,16 +458,19 @@ js_ErrorReporter(JSContext *cx, const ch warning="strict warning"; else warning="warning"; - } else + log_level=LOG_WARNING; + } else { + log_level=LOG_ERR; warning=""; + } - lprintf(LOG_ERR,"!JavaScript %s%s%s: %s",warning,file,line,message); + lprintf(log_level,"!JavaScript %s%s%s: %s",warning,file,line,message); if(fp!=NULL) fprintf(fp,"!JavaScript %s%s%s: %s",warning,file,line,message); } static JSContext* -js_initcx(JSRuntime* runtime, SOCKET sock, JSObject** glob, JSObject** ftp) +js_initcx(JSRuntime* runtime, SOCKET sock, JSObject** glob, JSObject** ftp, js_branch_t* branch) { JSContext* js_cx; JSObject* js_glob; @@ -475,18 +481,27 @@ js_initcx(JSRuntime* runtime, SOCKET soc if((js_cx = JS_NewContext(runtime, startup->js.cx_stack))==NULL) return(NULL); + JS_BEGINREQUEST(js_cx); lprintf(LOG_DEBUG,"%04d JavaScript: Context created",sock); JS_SetErrorReporter(js_cx, js_ErrorReporter); + memset(branch, 0, sizeof(js_branch_t)); + + /* ToDo: call js_CreateCommonObjects() instead */ + do { lprintf(LOG_DEBUG,"%04d JavaScript: Initializing Global object",sock); - if((js_glob=js_CreateGlobalObject(js_cx, &scfg, NULL))==NULL) + if((js_glob=js_CreateGlobalObject(js_cx, &scfg, NULL, &startup->js))==NULL) break; - if (!JS_DefineFunctions(js_cx, js_glob, js_global_functions)) + if(!JS_DefineFunctions(js_cx, js_glob, js_global_functions)) + break; + + /* Internal JS Object */ + if(js_CreateInternalJsObject(js_cx, js_glob, branch, &startup->js)==NULL) break; lprintf(LOG_DEBUG,"%04d JavaScript: Initializing System object",sock); @@ -508,6 +523,7 @@ js_initcx(JSRuntime* runtime, SOCKET soc } while(0); if(!success) { + JS_ENDREQUEST(js_cx); JS_DestroyContext(js_cx); return(NULL); } @@ -556,7 +572,7 @@ BOOL js_add_file(JSContext* js_cx, JSObj if(!JS_SetProperty(js_cx, file, "credits", &val)) return(FALSE); - JS_NewNumberValue(js_cx,time,&val); + JS_NewNumberValue(js_cx,(jsdouble)time,&val); if(!JS_SetProperty(js_cx, file, "time", &val)) return(FALSE); @@ -596,7 +612,7 @@ BOOL js_add_file(JSContext* js_cx, JSObj } BOOL js_generate_index(JSContext* js_cx, JSObject* parent, - SOCKET sock, FILE* fp, int lib, int dir, user_t* user) + SOCKET sock, FILE* fp, int lib, int dir, user_t* user, client_t* client) { char str[256]; char path[MAX_PATH+1]; @@ -623,6 +639,11 @@ BOOL js_generate_index(JSContext* js_cx, JSObject* dir_array=NULL; JSScript* js_script=NULL; JSString* js_str; + long double start=xp_timer(); + jsrefcount rc; + + lprintf(LOG_DEBUG,"%04d JavaScript: Generating HTML Index for %s" + ,sock, genvpath(lib,dir,str)); JS_SetContextPrivate(js_cx, fp); @@ -652,6 +673,7 @@ BOOL js_generate_index(JSContext* js_cx, break; } + rc=JS_SUSPENDREQUEST(js_cx); if(strcspn(startup->html_index_script,"/\\")==strlen(startup->html_index_script)) { sprintf(spath,"%s%s",scfg.mods_dir,startup->html_index_script); if(scfg.mods_dir[0]==0 || !fexist(spath)) @@ -663,9 +685,11 @@ BOOL js_generate_index(JSContext* js_cx, strcat(spath,".js"); if(!fexist(spath)) { + JS_RESUMEREQUEST(js_cx, rc); lprintf(LOG_ERR,"%04d !HTML JavaScript (%s) doesn't exist",sock,spath); break; } + JS_RESUMEREQUEST(js_cx, rc); if((js_str=JS_NewStringCopyZ(js_cx, startup->html_index_file))==NULL) break; @@ -768,6 +792,7 @@ BOOL js_generate_index(JSContext* js_cx, if(lib<0) { /* root dir */ + rc=JS_SUSPENDREQUEST(js_cx); /* File Aliases */ sprintf(path,"%sftpalias.cfg",scfg.ctrl_dir); if((alias_fp=fopen(path,"r"))!=NULL) { @@ -777,24 +802,24 @@ BOOL js_generate_index(JSContext* js_cx, break; p=aliasline; /* alias pointer */ - while(*p && *p<=' ') p++; + SKIP_WHITESPACE(p); if(*p==';') /* comment */ continue; tp=p; /* terminator pointer */ - while(*tp && *tp>' ') tp++; + FIND_WHITESPACE(tp); if(*tp) *tp=0; np=tp+1; /* filename pointer */ - while(*np && *np<=' ') np++; + SKIP_WHITESPACE(np); tp=np; /* terminator pointer */ - while(*tp && *tp>' ') tp++; + FIND_WHITESPACE(tp); if(*tp) *tp=0; dp=tp+1; /* description pointer */ - while(*dp && *dp<=' ') dp++; + SKIP_WHITESPACE(dp); truncsp(dp); if(stricmp(dp,BBS_HIDDEN_ALIAS)==0) @@ -804,7 +829,7 @@ BOOL js_generate_index(JSContext* js_cx, /* Virtual Path? */ if(!strnicmp(np,BBS_VIRTUAL_PATH,strlen(BBS_VIRTUAL_PATH))) { - if((dir=getdir(np+strlen(BBS_VIRTUAL_PATH),user))<0) + if((dir=getdir(np+strlen(BBS_VIRTUAL_PATH),user,client))<0) continue; /* No access or invalid virtual path */ tp=strrchr(np,'/'); if(tp==NULL) @@ -822,11 +847,12 @@ BOOL js_generate_index(JSContext* js_cx, continue; if(alias_dir) { - if(!chk_ar(&scfg,scfg.dir[dir]->ar,user)) + if(!chk_ar(&scfg,scfg.dir[dir]->ar,user,client)) continue; SAFEPRINTF2(vpath,"/%s/%s",p,startup->html_index_file); } else SAFECOPY(vpath,p); + JS_RESUMEREQUEST(js_cx, rc); js_add_file(js_cx ,alias_dir ? dir_array : file_array ,p /* filename */ @@ -842,10 +868,12 @@ BOOL js_generate_index(JSContext* js_cx, ,scfg.sys_id /* uploader */ ,vpath /* link */ ); + rc=JS_SUSPENDREQUEST(js_cx); } fclose(alias_fp); } + JS_RESUMEREQUEST(js_cx, rc); /* QWK Packet */ if(startup->options&FTP_OPT_ALLOW_QWK /* && fexist(qwkfile) */) { @@ -870,7 +898,7 @@ BOOL js_generate_index(JSContext* js_cx, /* Library Folders */ for(i=0;iar,user)) + if(!chk_ar(&scfg,scfg.lib[i]->ar,user,client)) continue; SAFEPRINTF2(vpath,"/%s/%s",scfg.lib[i]->sname,startup->html_index_file); js_add_file(js_cx @@ -889,7 +917,7 @@ BOOL js_generate_index(JSContext* js_cx, if(scfg.dir[i]->lib!=lib) continue; if(/* i!=scfg.sysop_dir && i!=scfg.upload_dir && */ - !chk_ar(&scfg,scfg.dir[i]->ar,user)) + !chk_ar(&scfg,scfg.dir[i]->ar,user,client)) continue; SAFEPRINTF3(vpath,"/%s/%s/%s" ,scfg.lib[scfg.dir[i]->lib]->sname @@ -908,8 +936,9 @@ BOOL js_generate_index(JSContext* js_cx, ); } - } else if(chk_ar(&scfg,scfg.dir[dir]->ar,user)){ + } else if(chk_ar(&scfg,scfg.dir[dir]->ar,user,client)){ SAFEPRINTF(path,"%s*",scfg.dir[dir]->path); + rc=JS_SUSPENDREQUEST(js_cx); glob(path,0,NULL,&g); for(i=0;i<(int)g.gl_pathc;i++) { if(isdir(g.gl_pathv[i])) @@ -928,12 +957,13 @@ BOOL js_generate_index(JSContext* js_cx, extdesc[0]=0; getextdesc(&scfg, dir, f.datoffset, extdesc); /* Remove Ctrl-A Codes and Ex-ASCII code */ - remove_ctrl_a(extdesc,NULL); + remove_ctrl_a(extdesc,extdesc); } SAFEPRINTF3(vpath,"/%s/%s/%s" ,scfg.lib[scfg.dir[dir]->lib]->sname ,scfg.dir[dir]->code_suffix ,getfname(g.gl_pathv[i])); + JS_RESUMEREQUEST(js_cx, rc); js_add_file(js_cx ,file_array ,getfname(g.gl_pathv[i]) /* filename */ @@ -949,9 +979,11 @@ BOOL js_generate_index(JSContext* js_cx, ,f.uler /* uploader */ ,getfname(g.gl_pathv[i]) /* link */ ); + rc=JS_SUSPENDREQUEST(js_cx); } } globfree(&g); + JS_RESUMEREQUEST(js_cx, rc); } @@ -963,11 +995,15 @@ BOOL js_generate_index(JSContext* js_cx, break; } + js_PrepareToExecute(js_cx, parent, spath, /* startup_dir: */NULL); if((success=JS_ExecuteScript(js_cx, parent, js_script, &rval))!=TRUE) { lprintf(LOG_ERR,"%04d !JavaScript FAILED to execute script (%s)",sock,spath); break; } + lprintf(LOG_DEBUG,"%04d JavaScript: Done executing script: %s (%.2Lf seconds)" + ,sock,spath,xp_timer()-start); + } while(0); @@ -989,50 +1025,11 @@ BOOL js_generate_index(JSContext* js_cx, #endif /* ifdef JAVASCRIPT */ - -time_t gettimeleft(scfg_t* cfg, user_t* user, time_t starttime) -{ - time_t now; - long tleft; - time_t timeleft; - - now=time(NULL); - - if(user->exempt&FLAG('T')) { /* Time online exemption */ - timeleft=cfg->level_timepercall[user->level]*60; - if(timeleft<10) /* never get below 10 for exempt users */ - timeleft=10; } - else { - tleft=(((long)cfg->level_timeperday[user->level]-user->ttoday) - +user->textra)*60L; - if(tleft<0) tleft=0; - if(tleft>cfg->level_timepercall[user->level]*60) - tleft=cfg->level_timepercall[user->level]*60; - tleft+=user->min*60L; - tleft-=now-starttime; - if(tleft>0x7fffL) - timeleft=0x7fff; - else - timeleft=tleft; } - - return(timeleft); -} - -static time_t checktime(void) -{ - struct tm tm; - - memset(&tm,0,sizeof(tm)); - tm.tm_year=94; - tm.tm_mday=1; - return(mktime(&tm)-0x2D24BD00L); -} - BOOL upload_stats(ulong bytes) { char str[MAX_PATH+1]; int file; - ulong val; + uint32_t val; sprintf(str,"%sdsts.dab",scfg.ctrl_dir); if((file=nopen(str,O_RDWR))==-1) @@ -1055,7 +1052,7 @@ BOOL download_stats(ulong bytes) { char str[MAX_PATH+1]; int file; - ulong val; + uint32_t val; sprintf(str,"%sdsts.dab",scfg.ctrl_dir); if((file=nopen(str,O_RDWR))==-1) @@ -1296,26 +1293,27 @@ static char* cmdstr(user_t* user, char * void DLLCALL ftp_terminate(void) { - lprintf(LOG_DEBUG,"%04d FTP Server terminate",server_socket); + lprintf(LOG_INFO,"%04d FTP Server terminate",server_socket); terminate_server=TRUE; } typedef struct { - SOCKET ctrl_sock; - SOCKET* data_sock; - BOOL* inprogress; - BOOL* aborted; - BOOL delfile; - BOOL tmpfile; - BOOL credits; - BOOL append; - long filepos; - char filename[MAX_PATH+1]; - time_t* lastactive; - user_t* user; - int dir; - char* desc; + SOCKET ctrl_sock; + SOCKET* data_sock; + BOOL* inprogress; + BOOL* aborted; + BOOL delfile; + BOOL tmpfile; + BOOL credits; + BOOL append; + long filepos; + char filename[MAX_PATH+1]; + time_t* lastactive; + user_t* user; + client_t* client; + int dir; + char* desc; } xfer_t; static void send_thread(void* arg) @@ -1351,6 +1349,7 @@ static void send_thread(void* arg) xfer=*(xfer_t*)arg; free(arg); + SetThreadName("FTP Send"); thread_up(TRUE /* setuid */); length=flength(xfer.filename); @@ -1390,7 +1389,7 @@ static void send_thread(void* arg) str[0]=0; lprintf(LOG_INFO,"%04d Sent %lu bytes (%lu total) of %s (%lu cps)%s" ,xfer.ctrl_sock,total,length,xfer.filename - ,(total-last_total)/(now-last_report) + ,(ulong)((total-last_total)/(now-last_report)) ,str); last_total=total; last_report=now; @@ -1486,7 +1485,7 @@ static void send_thread(void* arg) lprintf(LOG_DEBUG,"%04d DATA socket closed",xfer.ctrl_sock); if(!error) { - dur=time(NULL)-start; + dur=(long)(time(NULL)-start); cps=dur ? total/dur : total*2; lprintf(LOG_INFO,"%04d Transfer successful: %lu bytes sent in %lu seconds (%lu cps)" ,xfer.ctrl_sock @@ -1556,7 +1555,7 @@ static void send_thread(void* arg) if(xfer.credits) { user_downloaded(&scfg, xfer.user, 1, total); - if(xfer.dir>=0 && !is_download_free(&scfg,xfer.dir,xfer.user)) + if(xfer.dir>=0 && !is_download_free(&scfg,xfer.dir,xfer.user,xfer.client)) subtract_cdt(&scfg, xfer.user, xfer.credits); } } @@ -1609,6 +1608,7 @@ static void receive_thread(void* arg) xfer=*(xfer_t*)arg; free(arg); + SetThreadName("FTP RECV"); thread_up(TRUE /* setuid */); if((fp=fopen(xfer.filename,xfer.append ? "ab" : "wb"))==NULL) { @@ -1642,7 +1642,7 @@ static void receive_thread(void* arg) str[0]=0; lprintf(LOG_INFO,"%04d Received %lu bytes of %s (%lu cps)%s" ,xfer.ctrl_sock,total,xfer.filename - ,(total-last_total)/(now-last_report) + ,(ulong)((total-last_total)/(now-last_report)) ,str); last_total=total; last_report=now; @@ -1728,8 +1728,6 @@ static void receive_thread(void* arg) YIELD(); } - if(server_socket!=INVALID_SOCKET && !terminate_server) - *xfer.inprogress=FALSE; fclose(fp); ftp_close_socket(xfer.data_sock,__LINE__); @@ -1737,7 +1735,7 @@ static void receive_thread(void* arg) lprintf(LOG_DEBUG,"%04d DATA socket %d closed",xfer.ctrl_sock,*xfer.data_sock); if(!error) { - dur=time(NULL)-start; + dur=(long)(time(NULL)-start); cps=dur ? total/dur : total*2; lprintf(LOG_INFO,"%04d Transfer successful: %lu bytes received in %lu seconds (%lu cps)" ,xfer.ctrl_sock @@ -1770,21 +1768,26 @@ static void receive_thread(void* arg) if(p!=NULL && scfg.dir[f.dir]->misc&DIR_DIZ) { for(i=0;iext,p+1) - && chk_ar(&scfg,scfg.fextr[i]->ar,xfer.user)) + && chk_ar(&scfg,scfg.fextr[i]->ar,xfer.user,xfer.client)) break; if(icmd,fname,"FILE_ID.DIZ",cmd)); + cmdstr(&scfg,xfer.user,scfg.fextr[i]->cmd,fname,"FILE_ID.DIZ",cmd); + lprintf(LOG_DEBUG,"%04d Extracting DIZ: %s",xfer.ctrl_sock,cmd); + system(cmd); if(!fexistcase(tmp)) { sprintf(tmp,"%sDESC.SDI",scfg.temp_dir); if(fexistcase(tmp)) remove(tmp); - system(cmdstr(&scfg,xfer.user,scfg.fextr[i]->cmd,fname,"DESC.SDI",cmd)); + cmdstr(&scfg,xfer.user,scfg.fextr[i]->cmd,fname,"DESC.SDI",cmd); + lprintf(LOG_DEBUG,"%04d Extracting DIZ: %s",xfer.ctrl_sock,cmd); + system(cmd); fexistcase(tmp); /* fixes filename case */ } if((file=nopen(tmp,O_RDONLY))!=-1) { + lprintf(LOG_DEBUG,"%04d Parsing DIZ: %s",xfer.ctrl_sock,tmp); memset(ext,0,sizeof(ext)); read(file,ext,sizeof(ext)-1); for(i=sizeof(ext)-1;i;i--) /* trim trailing spaces */ @@ -1793,8 +1796,8 @@ static void receive_thread(void* arg) ext[i]=0; if(!f.desc[0]) { /* use for normal description */ SAFECOPY(desc,ext); - strip_exascii(desc); /* strip extended ASCII chars */ - prep_file_desc(desc); /* strip control chars and dupe chars */ + strip_exascii(desc, desc); /* strip extended ASCII chars */ + prep_file_desc(desc, desc); /* strip control chars and dupe chars */ for(i=0;desc[i];i++) /* find approprate first char */ if(isalnum(desc[i])) break; @@ -1803,7 +1806,8 @@ static void receive_thread(void* arg) close(file); remove(tmp); f.misc|=FM_EXTDESC; - } + } else + lprintf(LOG_DEBUG,"%04d DIZ Does not exist: %s",xfer.ctrl_sock,tmp); } } /* FILE_ID.DIZ support */ @@ -1844,6 +1848,9 @@ static void receive_thread(void* arg) sockprintf(xfer.ctrl_sock,"226 Upload complete (%lu cps).",cps); } + if(server_socket!=INVALID_SOCKET && !terminate_server) + *xfer.inprogress=FALSE; + thread_down(); } @@ -1854,6 +1861,7 @@ static void filexfer(SOCKADDR_IN* addr, ,BOOL delfile, BOOL tmpfile ,time_t* lastactive ,user_t* user + ,client_t* client ,int dir ,BOOL receiving ,BOOL credits @@ -1872,6 +1880,8 @@ static void filexfer(SOCKADDR_IN* addr, if((*inprogress)==TRUE) { lprintf(LOG_WARNING,"%04d !TRANSFER already in progress",ctrl_sock); sockprintf(ctrl_sock,"425 Transfer already in progress."); + if(tmpfile && !(startup->options&FTP_OPT_KEEP_TEMP_FILES)) + remove(filename); return; } *inprogress=TRUE; @@ -1884,14 +1894,13 @@ static void filexfer(SOCKADDR_IN* addr, if((*data_sock=socket(AF_INET, SOCK_STREAM, IPPROTO_IP)) == INVALID_SOCKET) { lprintf(LOG_ERR,"%04d !DATA ERROR %d opening socket", ctrl_sock, ERROR_VALUE); sockprintf(ctrl_sock,"425 Error %d opening socket",ERROR_VALUE); - if(tmpfile) + if(tmpfile && !(startup->options&FTP_OPT_KEEP_TEMP_FILES)) remove(filename); *inprogress=FALSE; return; } if(startup->socket_open!=NULL) startup->socket_open(startup->cbdata,TRUE); - sockets++; if(startup->options&FTP_OPT_DEBUG_DATA) lprintf(LOG_DEBUG,"%04d DATA socket %d opened",ctrl_sock,*data_sock); @@ -1914,7 +1923,7 @@ static void filexfer(SOCKADDR_IN* addr, lprintf(LOG_ERR,"%04d !DATA ERROR %d (%d) binding socket %d" ,ctrl_sock, result, ERROR_VALUE, *data_sock); sockprintf(ctrl_sock,"425 Error %d binding socket",ERROR_VALUE); - if(tmpfile) + if(tmpfile && !(startup->options&FTP_OPT_KEEP_TEMP_FILES)) remove(filename); *inprogress=FALSE; ftp_close_socket(data_sock,__LINE__); @@ -1927,7 +1936,7 @@ static void filexfer(SOCKADDR_IN* addr, ,ctrl_sock,result,ERROR_VALUE ,inet_ntoa(addr->sin_addr),ntohs(addr->sin_port),*data_sock); sockprintf(ctrl_sock,"425 Error %d connecting to socket",ERROR_VALUE); - if(tmpfile) + if(tmpfile && !(startup->options&FTP_OPT_KEEP_TEMP_FILES)) remove(filename); *inprogress=FALSE; ftp_close_socket(data_sock,__LINE__); @@ -1966,7 +1975,7 @@ static void filexfer(SOCKADDR_IN* addr, if(result<1) { lprintf(LOG_WARNING,"%04d !PASV select returned %d (error: %d)",ctrl_sock,result,ERROR_VALUE); sockprintf(ctrl_sock,"425 Error %d selecting socket for connection",ERROR_VALUE); - if(tmpfile) + if(tmpfile && !(startup->options&FTP_OPT_KEEP_TEMP_FILES)) remove(filename); *inprogress=FALSE; return; @@ -1984,14 +1993,13 @@ static void filexfer(SOCKADDR_IN* addr, lprintf(LOG_WARNING,"%04d !PASV DATA ERROR %d accepting connection on socket %d" ,ctrl_sock,ERROR_VALUE,pasv_sock); sockprintf(ctrl_sock,"425 Error %d accepting connection",ERROR_VALUE); - if(tmpfile) + if(tmpfile && !(startup->options&FTP_OPT_KEEP_TEMP_FILES)) remove(filename); *inprogress=FALSE; return; } if(startup->socket_open!=NULL) startup->socket_open(startup->cbdata,TRUE); - sockets++; if(startup->options&FTP_OPT_DEBUG_DATA) lprintf(LOG_DEBUG,"%04d PASV DATA socket %d connected to %s port %u" ,ctrl_sock,*data_sock,inet_ntoa(addr->sin_addr),ntohs(addr->sin_port)); @@ -2026,6 +2034,7 @@ static void filexfer(SOCKADDR_IN* addr, xfer->credits=credits; xfer->lastactive=lastactive; xfer->user=user; + xfer->client=client; xfer->dir=dir; xfer->desc=desc; SAFECOPY(xfer->filename,filename); @@ -2040,7 +2049,7 @@ static void filexfer(SOCKADDR_IN* addr, } while(0); /* failure */ - if(tmpfile) + if(tmpfile && !(startup->options&FTP_OPT_KEEP_TEMP_FILES)) remove(filename); *inprogress=FALSE; } @@ -2064,7 +2073,7 @@ char* dotname(char* in, char* out) return(out); } -void parsepath(char** pp, user_t* user, int* curlib, int* curdir) +void parsepath(char** pp, user_t* user, client_t* client, int* curlib, int* curdir) { char* p; char* tp; @@ -2100,9 +2109,12 @@ void parsepath(char** pp, user_t* user, if(lib<0) { /* root */ tp=strchr(p,'/'); - if(tp) *tp=0; + if(tp) + *(tp++)=0; + else + tp=p+strlen(p); for(lib=0;libar,user)) + if(!chk_ar(&scfg,scfg.lib[lib]->ar,user,client)) continue; if(!stricmp(scfg.lib[lib]->sname,p)) break; @@ -2113,12 +2125,13 @@ void parsepath(char** pp, user_t* user, } *curlib=lib; - if(tp==NULL) { + if(*(tp)==0) { *curdir=-1; + *pp+=tp-path; /* skip "lib" or "lib/" */ return; } - p=tp+1; + p=tp; } tp=strchr(p,'/'); @@ -2132,21 +2145,23 @@ void parsepath(char** pp, user_t* user, if(scfg.dir[dir]->lib!=lib) continue; if(dir!=scfg.sysop_dir && dir!=scfg.upload_dir - && !chk_ar(&scfg,scfg.dir[dir]->ar,user)) + && !chk_ar(&scfg,scfg.dir[dir]->ar,user,client)) continue; if(!stricmp(scfg.dir[dir]->code_suffix,p)) break; } - if(dir>=scfg.total_dirs) /* not found */ + if(dir>=scfg.total_dirs) { /* not found */ + *pp+=p-path; /* skip /lib/filespec */ return; + } *curdir=dir; *pp+=tp-path; /* skip "lib/dir/" */ } -static BOOL ftpalias(char* fullalias, char* filename, user_t* user, int* curdir) +static BOOL ftpalias(char* fullalias, char* filename, user_t* user, client_t* client, int* curdir) { char* p; char* tp; @@ -2179,26 +2194,26 @@ static BOOL ftpalias(char* fullalias, ch break; p=line; /* alias */ - while(*p && *p<=' ') p++; + SKIP_WHITESPACE(p); if(*p==';') /* comment */ continue; tp=p; /* terminator */ - while(*tp && *tp>' ') tp++; + FIND_WHITESPACE(tp); if(*tp) *tp=0; if(stricmp(p,alias)) /* Not a match */ continue; p=tp+1; /* filename */ - while(*p && *p<=' ') p++; + SKIP_WHITESPACE(p); tp=p; /* terminator */ - while(*tp && *tp>' ') tp++; + FIND_WHITESPACE(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) { + if((dir=getdir(p+strlen(BBS_VIRTUAL_PATH),user,client))<0) { lprintf(LOG_WARNING,"0000 !Invalid virtual path (%s) for %s",p,user->alias); /* invalid or no access */ continue; @@ -2243,7 +2258,7 @@ char* root_dir(char* path) return(root); } -char* vpath(int lib, int dir, char* str) +char* genvpath(int lib, int dir, char* str) { strcpy(str,"/"); if(lib<0) @@ -2281,10 +2296,45 @@ void ftp_printfile(SOCKET sock, const ch } } -static BOOL badlogin(SOCKET sock, ulong* login_attempts) +static BOOL ftp_hacklog(char* prot, char* user, char* text, char* host, SOCKADDR_IN* addr) { - mswait(5000); /* As recommended by RFC2577 */ - if(++(*login_attempts)>=3) { +#ifdef _WIN32 + if(startup->hack_sound[0] && !(startup->options&FTP_OPT_MUTE)) + PlaySound(startup->hack_sound, NULL, SND_ASYNC|SND_FILENAME); +#endif + + return hacklog(&scfg, prot, user, text, host, addr); +} + +/****************************************************************************/ +/* Consecutive failed login (possible password hack) attempt tracking */ +/****************************************************************************/ +/* Counter is global so it is tracked between multiple connections. */ +/* Failed consecutive login attempts > 10 will generate a hacklog entry and */ +/* immediately disconnect (after the usual failed-login delay). */ +/* A failed login from a different host resets the counter. */ +/* A successful login from the same host resets the counter. */ +/****************************************************************************/ + +static BOOL badlogin(SOCKET sock, ulong* login_attempts, char* user, char* passwd, char* host, SOCKADDR_IN* addr) +{ + ulong count; + + if(addr!=NULL) { + count=loginFailure(startup->login_attempt_list, addr, "FTP", user, passwd); + if(startup->login_attempt_hack_threshold && count>=startup->login_attempt_hack_threshold) + ftp_hacklog("FTP LOGIN", user, passwd, host, addr); + if(startup->login_attempt_filter_threshold && count>=startup->login_attempt_filter_threshold) + filter_ip(&scfg, "FTP", "- TOO MANY CONSECUTIVE FAILED LOGIN ATTEMPTS" + ,host, inet_ntoa(addr->sin_addr), user, /* fname: */NULL); + if(count > *login_attempts) + *login_attempts=count; + } else + (*login_attempts)++; + + mswait(startup->login_attempt_delay); /* As recommended by RFC2577 */ + + if((*login_attempts)>=3) { sockprintf(sock,"421 Too many failed login attempts."); return(TRUE); } @@ -2293,10 +2343,11 @@ static BOOL badlogin(SOCKET sock, ulong* return(FALSE); } -static char* ftp_tmpfname(char* str, SOCKET sock) +static char* ftp_tmpfname(char* fname, char* ext, SOCKET sock) { - safe_snprintf(str,MAX_PATH,"%sSBBS_FTP.%u.%u.tx",scfg.temp_dir,getpid(),sock); - return(str); + safe_snprintf(fname,MAX_PATH,"%sSBBS_FTP.%x%x%x%lx.%s" + ,scfg.temp_dir,getpid(),sock,rand(),clock(),ext); + return(fname); } static void ctrl_thread(void* arg) @@ -2308,6 +2359,7 @@ static void ctrl_thread(void* arg) char* np; char* tp; char* dp; + char* filespec; char* mode="active"; char password[64]; char fname[MAX_PATH+1]; @@ -2323,9 +2375,9 @@ static void ctrl_thread(void* arg) char ren_from[MAX_PATH+1]=""; char html_index_ext[MAX_PATH+1]; WORD port; - ulong ip_addr; + uint32_t ip_addr; socklen_t addr_len; - DWORD h1,h2,h3,h4; + unsigned h1,h2,h3,h4; u_short p1,p2; /* For PORT command */ int i; int rd; @@ -2374,6 +2426,7 @@ static void ctrl_thread(void* arg) time_t now; time_t logintime=0; time_t lastactive; + time_t file_date; file_t f; glob_t g; node_t node; @@ -2387,8 +2440,10 @@ static void ctrl_thread(void* arg) JSObject* js_glob; JSObject* js_ftp; JSString* js_str; + js_branch_t js_branch; #endif + SetThreadName("FTP CTRL"); thread_up(TRUE /* setuid */); lastactive=time(NULL); @@ -2401,7 +2456,7 @@ static void ctrl_thread(void* arg) lprintf(LOG_DEBUG,"%04d CTRL thread started", sock); - free(arg); /* unexplicable assertion here on July 26, 2001 */ + free(arg); #ifdef _WIN32 if(startup->answer_sound[0] && !(startup->options&FTP_OPT_MUTE)) @@ -2427,7 +2482,7 @@ static void ctrl_thread(void* arg) SAFECOPY(host_ip,inet_ntoa(ftp.client_addr.sin_addr)); - lprintf(LOG_DEBUG,"%04d CTRL connection accepted from: %s port %u" + lprintf(LOG_INFO,"%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) @@ -2441,11 +2496,8 @@ static void ctrl_thread(void* arg) else host_name=""; - if(!(startup->options&FTP_OPT_NO_HOST_LOOKUP)) { + if(!(startup->options&FTP_OPT_NO_HOST_LOOKUP)) lprintf(LOG_INFO,"%04d Hostname: %s", sock, host_name); - for(i=0;host!=NULL && host->h_aliases!=NULL && host->h_aliases[i]!=NULL;i++) - lprintf(LOG_INFO,"%04d HostAlias: %s", sock, host->h_aliases[i]); - } if(trashcan(&scfg,host_ip,"ip")) { lprintf(LOG_NOTICE,"%04d !CLIENT BLOCKED in ip.can: %s", sock, host_ip); @@ -2473,7 +2525,8 @@ static void ctrl_thread(void* arg) return; } - active_clients++, update_clients(); + protected_int32_adjust(&active_clients, 1), + update_clients(); /* Initialize client display */ client.size=sizeof(client); @@ -2485,6 +2538,13 @@ static void ctrl_thread(void* arg) client.user=""; client_on(sock,&client,FALSE /* update */); + if(startup->login_attempt_throttle + && (login_attempts=loginAttempts(startup->login_attempt_list, &ftp.client_addr)) > 1) { + lprintf(LOG_DEBUG,"%04d Throttling suspicious connection from: %s (%u login attempts)" + ,sock, inet_ntoa(ftp.client_addr.sin_addr), login_attempts); + mswait(login_attempts*startup->login_attempt_throttle); + } + sockprintf(sock,"220-%s (%s)",scfg.sys_name, startup->host_name); sockprintf(sock," Synchronet FTP Server %s-%s Ready" ,revision,PLATFORM_DESC); @@ -2557,7 +2617,7 @@ static void ctrl_thread(void* arg) 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," FEAT# OPTS# EPRT EPSV"); sockprintf(sock,"214 Direct comments to sysop@%s.",scfg.sys_inetaddr); continue; } @@ -2583,7 +2643,7 @@ static void ctrl_thread(void* arg) sysop=FALSE; user.number=0; p=cmd+5; - while(*p && *p<=' ') p++; + SKIP_WHITESPACE(p); truncsp(p); SAFECOPY(user.alias,p); user.number=matchuser(&scfg,user.alias,FALSE /*sysop_alias*/); @@ -2599,7 +2659,7 @@ static void ctrl_thread(void* arg) if(!strnicmp(cmd, "PASS ",5) && user.alias[0]) { user.number=0; p=cmd+5; - while(*p && *p<=' ') p++; + SKIP_WHITESPACE(p); SAFECOPY(password,p); user.number=matchuser(&scfg,user.alias,FALSE /*sysop_alias*/); @@ -2608,7 +2668,7 @@ static void ctrl_thread(void* arg) lprintf(LOG_WARNING,"%04d !UNKNOWN USER: %s, Password: %s",sock,user.alias,p); else lprintf(LOG_WARNING,"%04d !UNKNOWN USER: %s",sock,user.alias); - if(badlogin(sock,&login_attempts)) + if(badlogin(sock, &login_attempts, user.alias, p, host_name, &ftp.client_addr)) break; continue; } @@ -2623,7 +2683,7 @@ static void ctrl_thread(void* arg) lprintf(LOG_WARNING,"%04d !DELETED or INACTIVE user #%d (%s)" ,sock,user.number,user.alias); user.number=0; - if(badlogin(sock,&login_attempts)) + if(badlogin(sock, &login_attempts, NULL, NULL, NULL, NULL)) break; continue; } @@ -2631,22 +2691,22 @@ static void ctrl_thread(void* arg) lprintf(LOG_WARNING,"%04d !T RESTRICTED user #%d (%s)" ,sock,user.number,user.alias); user.number=0; - if(badlogin(sock,&login_attempts)) + if(badlogin(sock, &login_attempts, NULL, NULL, NULL, NULL)) break; continue; } - if(user.ltoday>scfg.level_callsperday[user.level] + if(user.ltoday>=scfg.level_callsperday[user.level] && !(user.exempt&FLAG('L'))) { lprintf(LOG_WARNING,"%04d !MAXIMUM LOGONS (%d) reached for %s" ,sock,scfg.level_callsperday[user.level],user.alias); - sockprintf(sock,"530 Maximum logons reached."); + sockprintf(sock,"530 Maximum logons per day reached."); user.number=0; continue; } - if(user.rest&FLAG('L') && user.ltoday>1) { + if(user.rest&FLAG('L') && user.ltoday>=1) { lprintf(LOG_WARNING,"%04d !L RESTRICTED user #%d (%s) already on today" ,sock,user.number,user.alias); - sockprintf(sock,"530 Maximum logons reached."); + sockprintf(sock,"530 Maximum logons per day reached."); user.number=0; continue; } @@ -2656,7 +2716,7 @@ static void ctrl_thread(void* arg) if(trashcan(&scfg,password,"email")) { lprintf(LOG_NOTICE,"%04d !BLOCKED e-mail address: %s",sock,password); user.number=0; - if(badlogin(sock,&login_attempts)) + if(badlogin(sock, &login_attempts, NULL, NULL, NULL, NULL)) break; continue; } @@ -2675,22 +2735,23 @@ static void ctrl_thread(void* arg) lprintf(LOG_WARNING,"%04d !FAILED Password attempt for user %s" ,sock, user.alias); user.number=0; - if(badlogin(sock,&login_attempts)) + if(badlogin(sock, &login_attempts, user.alias, password, host_name, &ftp.client_addr)) break; continue; } /* Update client display */ - if(user.pass[0]) + if(user.pass[0]) { client.user=user.alias; - else { /* anonymous */ + loginSuccess(startup->login_attempt_list, &ftp.client_addr); + } else { /* anonymous */ sprintf(str,"%s <%.32s>",user.alias,password); client.user=str; } client_on(sock,&client,TRUE /* update */); - - lprintf(LOG_INFO,"%04d %s logged in",sock,user.alias); + lprintf(LOG_INFO,"%04d %s logged in (%u today, %u total)" + ,sock,user.alias,user.ltoday+1, user.logons+1); logintime=time(NULL); timeleft=gettimeleft(&scfg,&user,logintime); ftp_printfile(sock,"hello",230); @@ -2702,7 +2763,7 @@ static void ctrl_thread(void* arg) if(js_CreateUserClass(js_cx, js_glob, &scfg)==NULL) lprintf(LOG_ERR,"%04d !JavaScript ERROR creating user class",sock); - if(js_CreateUserObject(js_cx, js_glob, &scfg, "user", user.number)==NULL) + if(js_CreateUserObject(js_cx, js_glob, &scfg, "user", user.number, &client)==NULL) lprintf(LOG_ERR,"%04d !JavaScript ERROR creating user object",sock); if(js_CreateClientObject(js_cx, js_glob, "client", &client, sock)==NULL) @@ -2775,7 +2836,7 @@ static void ctrl_thread(void* arg) 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); + sockprintf(sock,"211 End (%d active FTP clients)", active_clients.value); continue; } if(!stricmp(cmd, "SITE VER")) { @@ -2798,7 +2859,7 @@ static void ctrl_thread(void* arg) } if(!strnicmp(cmd,"SITE EXEC ",10) && sysop) { p=cmd+10; - while(*p && *p<=' ') p++; + SKIP_WHITESPACE(p); #ifdef __unix__ fp=popen(p,"r"); if(fp==NULL) @@ -2829,26 +2890,41 @@ static void ctrl_thread(void* arg) } #endif - if(!strnicmp(cmd, "PORT ",5)) { + if(strnicmp(cmd, "PORT ",5)==0 || strnicmp(cmd, "EPRT ",5)==0) { if(pasv_sock!=INVALID_SOCKET) ftp_close_socket(&pasv_sock,__LINE__); 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); + SKIP_WHITESPACE(p); + if(strnicmp(cmd, "PORT ",5)==0) { + sscanf(p,"%u,%u,%u,%u,%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); + } else { /* EPRT */ + char delim = *p; + int prot; + + if(*p) p++; + prot=strtol(p,NULL,/* base: */10); + if(prot!=1) { + lprintf(LOG_WARNING,"%04d UNSUPPORTED protocol: %d", sock, prot); + sockprintf(sock,"522 Network protocol not supported, use (1)"); + continue; + } + FIND_CHAR(p,delim); + if(*p) p++; + data_addr.sin_addr.s_addr=inet_addr(p); + FIND_CHAR(p,delim); + if(*p) p++; + data_addr.sin_port=atoi(p); + } if(data_addr.sin_port< IPPORT_RESERVED) { lprintf(LOG_WARNING,"%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); + ftp_hacklog("FTP BOUNCE", 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); @@ -2857,8 +2933,8 @@ static void ctrl_thread(void* arg) continue; } - if(!stricmp(cmd, "PASV") - || !stricmp(cmd, "P@SW")) { /* Kludge required for SMC Barricade V1.2 */ + if(stricmp(cmd, "PASV")==0 || stricmp(cmd, "P@SW")==0 /* Kludge required for SMC Barricade V1.2 */ + || stricmp(cmd, "EPSV")==0) { if(pasv_sock!=INVALID_SOCKET) ftp_close_socket(&pasv_sock,__LINE__); @@ -2935,14 +3011,17 @@ static void ctrl_thread(void* arg) ,ip_addr&0xff ); port=ntohs(addr.sin_port); - sockprintf(sock,"227 Entering Passive Mode (%u,%u,%u,%u,%hu,%hu)" - ,(ip_addr>>24)&0xff - ,(ip_addr>>16)&0xff - ,(ip_addr>>8)&0xff - ,ip_addr&0xff - ,(port>>8)&0xff - ,port&0xff - ); + if(stricmp(cmd, "EPSV")==0) + sockprintf(sock,"229 Entering Extended Passive Mode (|||%hu|)", port); + else + sockprintf(sock,"227 Entering Passive Mode (%u,%u,%u,%u,%hu,%hu)" + ,(ip_addr>>24)&0xff + ,(ip_addr>>16)&0xff + ,(ip_addr>>8)&0xff + ,ip_addr&0xff + ,(port>>8)&0xff + ,port&0xff + ); mode="passive"; continue; } @@ -2954,7 +3033,7 @@ static void ctrl_thread(void* arg) if(!strnicmp(cmd, "ALLO",4)) { p=cmd+5; - while(*p && *p<=' ') p++; + SKIP_WHITESPACE(p); if(*p) l=atol(p); else @@ -2972,7 +3051,7 @@ static void ctrl_thread(void* arg) if(!strnicmp(cmd, "REST",4)) { p=cmd+4; - while(*p && *p<=' ') p++; + SKIP_WHITESPACE(p); if(*p) filepos=atol(p); else @@ -2984,7 +3063,7 @@ static void ctrl_thread(void* arg) if(!strnicmp(cmd, "MODE ",5)) { p=cmd+5; - while(*p && *p<=' ') p++; + SKIP_WHITESPACE(p); if(toupper(*p)!='S') sockprintf(sock,"504 Only STREAM mode supported."); else @@ -2994,7 +3073,7 @@ static void ctrl_thread(void* arg) if(!strnicmp(cmd, "STRU ",5)) { p=cmd+5; - while(*p && *p<=' ') p++; + SKIP_WHITESPACE(p); if(toupper(*p)!='F') sockprintf(sock,"504 Only FILE structure supported."); else @@ -3022,7 +3101,7 @@ static void ctrl_thread(void* arg) if(!strnicmp(cmd,"SMNT ",5) && sysop && !(startup->options&FTP_OPT_NO_LOCAL_FSYS)) { p=cmd+5; - while(*p && *p<=' ') p++; + SKIP_WHITESPACE(p); if(!stricmp(p,BBS_FSYS_DIR)) local_fsys=FALSE; else { @@ -3052,7 +3131,7 @@ static void ctrl_thread(void* arg) strcat(local_dir,"/"); if(!strnicmp(cmd, "LIST", 4) || !strnicmp(cmd, "NLST", 4)) { - if((fp=fopen(ftp_tmpfname(fname,sock),"w+b"))==NULL) { + 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; @@ -3063,16 +3142,20 @@ static void ctrl_thread(void* arg) detail=FALSE; p=cmd+4; - while(*p && *p<=' ') p++; + SKIP_WHITESPACE(p); if(*p=='-') { /* -Letc */ - while(*p && *p>' ') p++; - while(*p && *p<=' ') p++; + FIND_WHITESPACE(p); + SKIP_WHITESPACE(p); } - SAFEPRINTF2(path,"%s%s",local_dir, *p ? 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, p); + sockprintf(sock, "150 Directory of %s%s", local_dir, filespec); now=time(NULL); if(localtime_r(&now,&cur_tm)==NULL) @@ -3107,7 +3190,7 @@ static void ctrl_thread(void* arg) ,&transfer_inprogress,&transfer_aborted ,TRUE /* delfile */ ,TRUE /* tmpfile */ - ,&lastactive,&user,-1,FALSE,FALSE,FALSE,NULL); + ,&lastactive,&user,&client,-1,FALSE,FALSE,FALSE,NULL); continue; } /* Local LIST/NLST */ @@ -3116,7 +3199,7 @@ static void ctrl_thread(void* arg) p=cmd+4; else p=cmd+5; - while(*p && *p<=' ') p++; + SKIP_WHITESPACE(p); tp=p; if(*tp=='/' || *tp=='\\') /* /local: and /bbs: are valid */ tp++; @@ -3171,7 +3254,7 @@ static void ctrl_thread(void* arg) if(!strnicmp(cmd, "MKD ", 4) || !strnicmp(cmd,"XMKD",4)) { p=cmd+4; - while(*p && *p<=' ') p++; + SKIP_WHITESPACE(p); if(*p=='/') /* absolute */ SAFEPRINTF2(fname,"%s%s",root_dir(local_dir),p+1); else /* relative */ @@ -3190,7 +3273,7 @@ static void ctrl_thread(void* arg) if(!strnicmp(cmd, "RMD ", 4) || !strnicmp(cmd,"XRMD",4)) { p=cmd+4; - while(*p && *p<=' ') p++; + SKIP_WHITESPACE(p); if(*p=='/') /* absolute */ SAFEPRINTF2(fname,"%s%s",root_dir(local_dir),p+1); else /* relative */ @@ -3209,7 +3292,7 @@ static void ctrl_thread(void* arg) if(!strnicmp(cmd, "RNFR ",5)) { p=cmd+5; - while(*p && *p<=' ') p++; + SKIP_WHITESPACE(p); if(*p=='/') /* absolute */ SAFEPRINTF2(ren_from,"%s%s",root_dir(local_dir),p+1); else /* relative */ @@ -3225,7 +3308,7 @@ static void ctrl_thread(void* arg) if(!strnicmp(cmd, "RNTO ",5)) { p=cmd+5; - while(*p && *p<=' ') p++; + SKIP_WHITESPACE(p); if(*p=='/') /* absolute */ SAFEPRINTF2(fname,"%s%s",root_dir(local_dir),p+1); else /* relative */ @@ -3246,7 +3329,7 @@ static void ctrl_thread(void* arg) if(!strnicmp(cmd, "RETR ", 5) || !strnicmp(cmd,"SIZE ",5) || !strnicmp(cmd, "MDTM ",5) || !strnicmp(cmd, "DELE ",5)) { p=cmd+5; - while(*p && *p<=' ') p++; + SKIP_WHITESPACE(p); if(!strnicmp(p,LOCAL_FSYS_DIR,strlen(LOCAL_FSYS_DIR))) p+=strlen(LOCAL_FSYS_DIR); /* already mounted */ @@ -3263,7 +3346,7 @@ static void ctrl_thread(void* arg) continue; } if(!strnicmp(cmd,"SIZE ",5)) { - sockprintf(sock,"213 %lu",flength(fname)); + sockprintf(sock,"213 %"PRIuOFF,flength(fname)); continue; } if(!strnicmp(cmd,"MDTM ",5)) { @@ -3287,19 +3370,19 @@ static void ctrl_thread(void* arg) continue; } /* RETR */ - lprintf(LOG_INFO,"%04d %s downloading: %s (%lu bytes) in %s mode" + lprintf(LOG_INFO,"%04d %s downloading: %s (%"PRIuOFF" bytes) in %s mode" ,sock,user.alias,fname,flength(fname) ,mode); 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); + ,&lastactive,&user,&client,-1,FALSE,FALSE,FALSE,NULL); continue; } /* Local RETR/SIZE/MDTM */ if(!strnicmp(cmd, "STOR ", 5) || !strnicmp(cmd, "APPE ", 5)) { p=cmd+5; - while(*p && *p<=' ') p++; + SKIP_WHITESPACE(p); if(!strnicmp(p,LOCAL_FSYS_DIR,strlen(LOCAL_FSYS_DIR))) p+=strlen(LOCAL_FSYS_DIR); /* already mounted */ @@ -3318,6 +3401,7 @@ static void ctrl_thread(void* arg) ,&transfer_inprogress,&transfer_aborted,FALSE,FALSE ,&lastactive ,&user + ,&client ,-1 /* dir */ ,TRUE /* uploading */ ,FALSE /* credits */ @@ -3338,16 +3422,19 @@ static void ctrl_thread(void* arg) /* path specified? */ p=cmd+4; - while(*p && *p<=' ') p++; + SKIP_WHITESPACE(p); if(*p=='-') { /* -Letc */ - while(*p && *p>' ') p++; - while(*p && *p<=' ') p++; + FIND_WHITESPACE(p); + SKIP_WHITESPACE(p); } - parsepath(&p,&user,&lib,&dir); + parsepath(&p,&user,&client,&lib,&dir); + filespec=p; + if(*filespec==0) + filespec="*"; - if((fp=fopen(ftp_tmpfname(fname,sock),"w+b"))==NULL) { + 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; @@ -3363,7 +3450,7 @@ static void ctrl_thread(void* arg) /* ASCII Index File */ if(startup->options&FTP_OPT_INDEX_FILE && startup->index_file_name[0] - && (!stricmp(p,startup->index_file_name) || *p==0 || *p=='*')) { + && wildmatchi(startup->index_file_name, filespec, FALSE)) { if(detail) fprintf(fp,"-r--r--r-- 1 %-*s %-8s %9ld %s %2d %02d:%02d %s\r\n" ,NAME_LEN @@ -3378,7 +3465,7 @@ static void ctrl_thread(void* arg) } /* HTML Index File */ if(startup->options&FTP_OPT_HTML_INDEX_FILE && startup->html_index_file[0] - && (!stricmp(p,startup->html_index_file) || *p==0 || *p=='*')) { + && 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 @@ -3396,26 +3483,29 @@ static void ctrl_thread(void* arg) lprintf(LOG_INFO,"%04d %s listing: root in %s mode",sock,user.alias, mode); /* QWK Packet */ - if(startup->options&FTP_OPT_ALLOW_QWK/* && fexist(qwkfile)*/) { - 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.qwk\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 - ,scfg.sys_id); - } else - fprintf(fp,"%s.qwk\r\n",scfg.sys_id); + 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); + } } /* File Aliases */ @@ -3429,33 +3519,38 @@ static void ctrl_thread(void* arg) alias_dir=FALSE; p=aliasline; /* alias pointer */ - while(*p && *p<=' ') p++; + SKIP_WHITESPACE(p); if(*p==';') /* comment */ continue; tp=p; /* terminator pointer */ - while(*tp && *tp>' ') tp++; + FIND_WHITESPACE(tp); if(*tp) *tp=0; np=tp+1; /* filename pointer */ - while(*np && *np<=' ') np++; + SKIP_WHITESPACE(np); tp=np; /* terminator pointer */ - while(*tp && *tp>' ') tp++; + FIND_WHITESPACE(tp); if(*tp) *tp=0; dp=tp+1; /* description pointer */ - while(*dp && *dp<=' ') dp++; + SKIP_WHITESPACE(dp); 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) + if((dir=getdir(np+strlen(BBS_VIRTUAL_PATH),&user,&client))<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; @@ -3468,8 +3563,10 @@ static void ctrl_thread(void* arg) alias_dir=TRUE; } - if(!alias_dir && !fexist(np)) + if(!alias_dir && !fexist(np)) { + lprintf(LOG_WARNING,"0000 !Missing aliased file (%s) for %s",np,user.alias); continue; + } if(detail) { @@ -3486,7 +3583,7 @@ static void ctrl_thread(void* arg) t=fdate(np); 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" + fprintf(fp,"-r--r--r-- 1 %-*s %-8s %9"PRIdOFF" %s %2d %02d:%02d %s\r\n" ,NAME_LEN ,scfg.sys_id ,scfg.sys_id @@ -3504,7 +3601,9 @@ static void ctrl_thread(void* arg) /* Library folders */ for(i=0;iar,&user)) + if(!chk_ar(&scfg,scfg.lib[i]->ar,&user,&client)) + continue; + if(!wildmatchi(scfg.lib[i]->sname, filespec, FALSE)) continue; if(detail) fprintf(fp,"dr-xr-xr-x 1 %-*s %-8s %9ld %s %2d %02d:%02d %s\r\n" @@ -3523,8 +3622,10 @@ static void ctrl_thread(void* arg) for(i=0;ilib!=lib) continue; - if(i!=scfg.sysop_dir && i!=scfg.upload_dir - && !chk_ar(&scfg,scfg.dir[i]->ar,&user)) + if(i!=(int)scfg.sysop_dir && i!=(int)scfg.upload_dir + && !chk_ar(&scfg,scfg.dir[i]->ar,&user,&client)) + continue; + if(!wildmatchi(scfg.dir[i]->code_suffix, filespec, FALSE)) continue; if(detail) fprintf(fp,"drwxrwxrwx 1 %-*s %-8s %9ld %s %2d %02d:%02d %s\r\n" @@ -3537,11 +3638,11 @@ static void ctrl_thread(void* arg) else fprintf(fp,"%s\r\n",scfg.dir[i]->code_suffix); } - } else if(chk_ar(&scfg,scfg.dir[dir]->ar,&user)) { + } else if(chk_ar(&scfg,scfg.dir[dir]->ar,&user,&client)) { 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,*p ? p : "*"); + 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])) @@ -3596,7 +3697,7 @@ static void ctrl_thread(void* arg) ,&transfer_inprogress,&transfer_aborted ,TRUE /* delfile */ ,TRUE /* tmpfile */ - ,&lastactive,&user,dir,FALSE,FALSE,FALSE,NULL); + ,&lastactive,&user,&client,dir,FALSE,FALSE,FALSE,NULL); continue; } @@ -3607,6 +3708,7 @@ static void ctrl_thread(void* arg) getdate=FALSE; getsize=FALSE; delecmd=FALSE; + file_date=0; if(!strnicmp(cmd,"SIZE ",5)) getsize=TRUE; else if(!strnicmp(cmd,"MDTM ",5)) @@ -3627,7 +3729,7 @@ static void ctrl_thread(void* arg) dir=curdir; p=cmd+5; - while(*p && *p<=' ') p++; + SKIP_WHITESPACE(p); if(!strnicmp(p,BBS_FSYS_DIR,strlen(BBS_FSYS_DIR))) p+=strlen(BBS_FSYS_DIR); /* already mounted */ @@ -3639,7 +3741,7 @@ static void ctrl_thread(void* arg) else if(!strncmp(p,"./",2)) p+=2; - if(lib<0 && ftpalias(p, fname, &user, &dir)==TRUE) { + if(lib<0 && ftpalias(p, fname, &user, &client, &dir)==TRUE) { success=TRUE; credits=TRUE; /* include in d/l stats */ tmpfile=FALSE; @@ -3654,7 +3756,7 @@ static void ctrl_thread(void* arg) dir=-1; *tp=0; for(i=0;iar,&user)) + if(!chk_ar(&scfg,scfg.lib[i]->ar,&user,&client)) continue; if(!stricmp(scfg.lib[i]->sname,p)) break; @@ -3668,7 +3770,7 @@ static void ctrl_thread(void* arg) for(i=0;ilib!=lib) continue; - if(!chk_ar(&scfg,scfg.dir[i]->ar,&user)) + if(!chk_ar(&scfg,scfg.dir[i]->ar,&user,&client)) continue; if(!stricmp(scfg.dir[i]->code_suffix,p)) break; @@ -3691,10 +3793,16 @@ static void ctrl_thread(void* arg) ,sock, str); t=time(NULL); while(fexist(str)) { + 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)) { + remove(str); + continue; + } if(fexist(str)) { lprintf(LOG_WARNING,"%04d !TIMEOUT waiting for QWK packet creation",sock); sockprintf(sock,"451 Time-out waiting for packet creation."); @@ -3714,230 +3822,252 @@ static void ctrl_thread(void* arg) delfile=TRUE; credits=FALSE; if(!getsize && !getdate) - lprintf(LOG_INFO,"%04d %s downloading QWK packet (%lu bytes) in %s mode" + lprintf(LOG_INFO,"%04d %s downloading QWK packet (%"PRIuOFF" bytes) in %s mode" ,sock,user.alias,flength(fname) ,mode); /* ASCII Index File */ } else if(startup->options&FTP_OPT_INDEX_FILE && !stricmp(p,startup->index_file_name) && !delecmd) { - if((fp=fopen(ftp_tmpfname(fname,sock),"w+b"))==NULL) { + if(getsize) { + /* Size not available for dynamically generated files */ + sockprintf(sock, "213 0"); /* report size of 0 to be make Google Chrome happy */ + 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; } - if(!getsize && !getdate) + success=TRUE; + if(getdate) + file_date=time(NULL); + else { lprintf(LOG_INFO,"%04d %s downloading index for %s in %s mode" - ,sock,user.alias,vpath(lib,dir,str) + ,sock,user.alias,genvpath(lib,dir,str) ,mode); - success=TRUE; - 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) { + 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; + while(!feof(alias_fp)) { + if(!fgets(aliasline,sizeof(aliasline),alias_fp)) + break; - p=aliasline; /* alias pointer */ - while(*p && *p<=' ') p++; + p=aliasline; /* alias pointer */ + SKIP_WHITESPACE(p); - if(*p==';') /* comment */ - continue; + if(*p==';') /* comment */ + continue; - tp=p; /* terminator pointer */ - while(*tp && *tp>' ') tp++; - if(*tp) *tp=0; + tp=p; /* terminator pointer */ + FIND_WHITESPACE(tp); + if(*tp) *tp=0; - np=tp+1; /* filename pointer */ - while(*np && *np<=' ') np++; + np=tp+1; /* filename pointer */ + SKIP_WHITESPACE(np); - np++; /* description pointer */ - while(*np && *np>' ') np++; + np++; /* description pointer */ + FIND_WHITESPACE(np); - while(*np && *np<' ') np++; + while(*np && *np<' ') np++; - truncsp(np); + truncsp(np); - fprintf(fp,"%-*s %s\r\n",INDEX_FNAME_LEN,p,np); - } + fprintf(fp,"%-*s %s\r\n",INDEX_FNAME_LEN,p,np); + } - fclose(alias_fp); - } + 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); - } + /* 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;iar,&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;ilib!=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); + /* Library Folders */ + for(i=0;iar,&user,&client)) + 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;ilib!=lib) + continue; + if(i!=(int)scfg.sysop_dir && i!=(int)scfg.upload_dir + && !chk_ar(&scfg,scfg.dir[i]->ar,&user,&client)) + 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); } - globfree(&g); + fclose(fp); } - 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))) && !delecmd) { -#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"); - filepos=0; + success=TRUE; + if(getsize) { + /* Size not available for dynamically generated files */ + sockprintf(sock, "213 0"); /* report size of 0 to be make Google Chrome happy */ continue; } - 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"); + 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"); 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_runtime == NULL) { + lprintf(LOG_DEBUG,"%04d JavaScript: Creating runtime: %lu bytes" + ,sock,startup->js.max_bytes); + + if((js_runtime = jsrt_GetNew(startup->js.max_bytes, 1000, __FILE__, __LINE__))==NULL) { + lprintf(LOG_ERR,"%04d !ERROR creating JavaScript runtime",sock); + sockprintf(sock,"451 Error creating JavaScript runtime"); + 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_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_branch))==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_CreateUserObject(js_cx, js_glob, &scfg, "user", user.number)==NULL) - lprintf(LOG_ERR,"%04d !JavaScript ERROR creating user object",sock); + if(js_CreateFileClass(js_cx, js_glob)==NULL) + lprintf(LOG_ERR,"%04d !JavaScript ERROR creating file class",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) { + if(js_CreateUserObject(js_cx, js_glob, &scfg, "user", &user, &client, /* global_user: */TRUE)==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, &client + ,startup->html_index_file)==NULL) + lprintf(LOG_ERR,"%04d !JavaScript ERROR creating file area object",sock); + } + else + 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) *tp=0; - tp++; - if(!stricmp(tp,"reverse")) { - js_val=BOOLEAN_TO_JSVAL(TRUE); - JS_SetProperty(js_cx, js_ftp, "reverse", &js_val); + 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); } - } - 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_val=BOOLEAN_TO_JSVAL(INT_TO_BOOL(user.misc&EXTDESC)); + JS_SetProperty(js_cx, js_ftp, "extended_descriptions", &js_val); + JS_ENDREQUEST(js_cx); #endif - if((fp=fopen(ftp_tmpfname(fname,sock),"w+b"))==NULL) { - lprintf(LOG_ERR,"%04d !ERROR %d opening %s",sock,errno,fname); - sockprintf(sock, "451 Insufficient system storage"); - filepos=0; - continue; - } - if(!getsize && !getdate) + 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,vpath(lib,dir,str) + ,sock,user.alias,genvpath(lib,dir,str) ,mode); - success=TRUE; - credits=FALSE; - tmpfile=TRUE; - delfile=TRUE; + credits=FALSE; + tmpfile=TRUE; + delfile=TRUE; #ifdef JAVASCRIPT - 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); + 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 !JavaScript ERROR setting user.time_left",sock); + js_generate_index(js_cx, js_ftp, sock, fp, lib, dir, &user, &client); + JS_ENDREQUEST(js_cx); #endif - fclose(fp); + fclose(fp); + } } else if(dir>=0) { - if(!chk_ar(&scfg,scfg.dir[dir]->ar,&user)) { + if(!chk_ar(&scfg,scfg.dir[dir]->ar,&user,&client)) { lprintf(LOG_WARNING,"%04d !%s has insufficient access to /%s/%s" ,sock,user.alias ,scfg.lib[scfg.dir[dir]->lib]->sname @@ -3948,7 +4078,7 @@ static void ctrl_thread(void* arg) } if(!getsize && !getdate && !delecmd - && !chk_ar(&scfg,scfg.dir[dir]->dl_ar,&user)) { + && !chk_ar(&scfg,scfg.dir[dir]->dl_ar,&user,&client)) { lprintf(LOG_WARNING,"%04d !%s has insufficient access to download from /%s/%s" ,sock,user.alias ,scfg.lib[scfg.dir[dir]->lib]->sname @@ -3958,7 +4088,7 @@ static void ctrl_thread(void* arg) continue; } - if(delecmd && !dir_op(&scfg,&user,dir)) { + 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 @@ -3981,14 +4111,14 @@ static void ctrl_thread(void* arg) 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" - ,sock,user.alias,vpath(lib,dir,str),p,cmd); + ,sock,user.alias,genvpath(lib,dir,str),p,cmd); filepos=0; continue; } /* Verify credits */ if(!getsize && !getdate && !delecmd - && !is_download_free(&scfg,dir,&user)) { + && !is_download_free(&scfg,dir,&user,&client)) { if(filedat) getfiledat(&scfg,&f); else @@ -4009,16 +4139,12 @@ static void ctrl_thread(void* arg) success=FALSE; lprintf(LOG_WARNING,"%04d !ILLEGAL FILENAME ATTEMPT by %s: %s" ,sock,user.alias,p); - hacklog(&scfg, "FTP", user.alias, cmd, host_name, &ftp.client_addr); -#ifdef _WIN32 - if(startup->hack_sound[0] && !(startup->options&FTP_OPT_MUTE)) - PlaySound(startup->hack_sound, NULL, SND_ASYNC|SND_FILENAME); -#endif + ftp_hacklog("FTP FILENAME", user.alias, cmd, host_name, &ftp.client_addr); } else { - if(fexist(fname)) { + if(fexistcase(fname)) { success=TRUE; if(!getsize && !getdate && !delecmd) - lprintf(LOG_INFO,"%04d %s downloading: %s (%lu bytes) in %s mode" + lprintf(LOG_INFO,"%04d %s downloading: %s (%"PRIuOFF" bytes) in %s mode" ,sock,user.alias,fname,flength(fname) ,mode); } @@ -4028,17 +4154,18 @@ static void ctrl_thread(void* arg) socket_debug[sock]|=SOCKET_DEBUG_DOWNLOAD; #endif - if(getsize && success) - sockprintf(sock,"213 %lu",flength(fname)); + if(getsize && success) + sockprintf(sock,"213 %"PRIuOFF, flength(fname)); else if(getdate && success) { - t=fdate(fname); - if(gmtime_r(&t,&tm)==NULL) /* specifically use GMT/UTC representation */ + if(file_date==0) + file_date = fdate(fname); + if(gmtime_r(&file_date,&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); } else if(delecmd && success) { - if(remove(fname)!=0) { + if(removecase(fname)!=0) { lprintf(LOG_ERR,"%04d !ERROR %d deleting %s",sock,errno,fname); sockprintf(sock,"450 %s could not be deleted (error: %d)" ,fname,errno); @@ -4052,12 +4179,12 @@ static void ctrl_thread(void* arg) 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,delfile,tmpfile - ,&lastactive,&user,dir,FALSE,credits,FALSE,NULL); + ,&lastactive,&user,&client,dir,FALSE,credits,FALSE,NULL); } else { sockprintf(sock,"550 File not found: %s",p); lprintf(LOG_WARNING,"%04d !%s file (%s%s) not found for %.4s command" - ,sock,user.alias,vpath(lib,dir,str),p,cmd); + ,sock,user.alias,genvpath(lib,dir,str),p,cmd); } filepos=0; #if defined(SOCKET_DEBUG_DOWNLOAD) @@ -4074,7 +4201,7 @@ static void ctrl_thread(void* arg) } p=cmd+4; - while(*p && *p<=' ') p++; + SKIP_WHITESPACE(p); if(*p==0) sockprintf(sock,"501 No file description given."); @@ -4103,7 +4230,7 @@ static void ctrl_thread(void* arg) dir=curdir; p=cmd+5; - while(*p && *p<=' ') p++; + SKIP_WHITESPACE(p); if(!strnicmp(p,BBS_FSYS_DIR,strlen(BBS_FSYS_DIR))) p+=strlen(BBS_FSYS_DIR); /* already mounted */ @@ -4119,7 +4246,7 @@ static void ctrl_thread(void* arg) dir=-1; *tp=0; for(i=0;iar,&user)) + if(!chk_ar(&scfg,scfg.lib[i]->ar,&user,&client)) continue; if(!stricmp(scfg.lib[i]->sname,p)) break; @@ -4133,8 +4260,8 @@ static void ctrl_thread(void* arg) for(i=0;ilib!=lib) continue; - if(i!=scfg.sysop_dir && i!=scfg.upload_dir - && !chk_ar(&scfg,scfg.dir[i]->ar,&user)) + if(i!=(int)scfg.sysop_dir && i!=(int)scfg.upload_dir + && !chk_ar(&scfg,scfg.dir[i]->ar,&user,&client)) continue; if(!stricmp(scfg.dir[i]->code_suffix,p)) break; @@ -4160,13 +4287,25 @@ static void ctrl_thread(void* arg) append=(strnicmp(cmd,"APPE",4)==0); - if(!chk_ar(&scfg,scfg.dir[dir]->ul_ar,&user)) { - lprintf(LOG_WARNING,"%04d !%s has insufficient access to upload to /%s/%s" - ,sock,user.alias - ,scfg.lib[scfg.dir[dir]->lib]->sname - ,scfg.dir[dir]->code_suffix); - sockprintf(sock,"553 Insufficient access."); - continue; + 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); + sockprintf(sock,"553 Insufficient access."); + continue; + } + + if(scfg.dir[dir]->maxfiles && getfiles(&scfg,dir)>=scfg.dir[dir]->maxfiles) { + lprintf(LOG_WARNING,"%04d !%s cannot upload to /%s/%s (directory full: %u files)" + ,sock,user.alias + ,scfg.lib[scfg.dir[dir]->lib]->sname + ,scfg.dir[dir]->code_suffix + ,getfiles(&scfg,dir)); + sockprintf(sock,"553 Directory full."); + continue; + } } if(*p=='-' || strcspn(p,ILLEGAL_FILENAME_CHARS)!=strlen(p) @@ -4174,11 +4313,7 @@ static void ctrl_thread(void* arg) lprintf(LOG_WARNING,"%04d !ILLEGAL FILENAME ATTEMPT by %s: %s" ,sock,user.alias,p); sockprintf(sock,"553 Illegal filename attempt"); - hacklog(&scfg, "FTP", user.alias, cmd, host_name, &ftp.client_addr); -#ifdef _WIN32 - if(startup->hack_sound[0] && !(startup->options&FTP_OPT_MUTE)) - PlaySound(startup->hack_sound, NULL, SND_ASYNC|SND_FILENAME); -#endif + ftp_hacklog("FTP FILENAME", user.alias, cmd, host_name, &ftp.client_addr); continue; } SAFEPRINTF2(fname,"%s%s",scfg.dir[dir]->path,p); @@ -4223,7 +4358,7 @@ static void ctrl_thread(void* arg) lprintf(LOG_INFO,"%04d %s uploading: %s to %s (%s) in %s mode" ,sock,user.alias ,p /* filename */ - ,vpath(lib,dir,str) /* virtual path */ + ,genvpath(lib,dir,str) /* virtual path */ ,scfg.dir[dir]->path /* actual path */ ,mode); } @@ -4232,6 +4367,7 @@ static void ctrl_thread(void* arg) ,&transfer_inprogress,&transfer_aborted,FALSE,FALSE ,&lastactive ,&user + ,&client ,dir ,TRUE /* uploading */ ,TRUE /* credits */ @@ -4253,7 +4389,7 @@ static void ctrl_thread(void* arg) if(!strnicmp(cmd, "CWD ", 4) || !strnicmp(cmd,"XCWD ",5)) { p=cmd+4; - while(*p && *p<=' ') p++; + SKIP_WHITESPACE(p); if(!strnicmp(p,BBS_FSYS_DIR,strlen(BBS_FSYS_DIR))) p+=strlen(BBS_FSYS_DIR); /* already mounted */ @@ -4282,7 +4418,7 @@ static void ctrl_thread(void* arg) success=FALSE; /* Directory Alias? */ - if(curlib<0 && ftpalias(p,NULL,&user,&curdir)==TRUE) { + if(curlib<0 && ftpalias(p,NULL,&user,&client,&curdir)==TRUE) { if(curdir>=0) curlib=scfg.dir[curdir]->lib; success=TRUE; @@ -4314,7 +4450,7 @@ static void ctrl_thread(void* arg) tp=strchr(p,'/'); if(tp) *tp=0; for(i=0;iar,&user)) + if(!chk_ar(&scfg,scfg.lib[i]->ar,&user,&client)) continue; if(!stricmp(scfg.lib[i]->sname,p)) break; @@ -4332,8 +4468,8 @@ static void ctrl_thread(void* arg) for(i=0;ilib!=curlib) continue; - if(i!=scfg.sysop_dir && i!=scfg.upload_dir - && !chk_ar(&scfg,scfg.dir[i]->ar,&user)) + if(i!=(int)scfg.sysop_dir && i!=(int)scfg.upload_dir + && !chk_ar(&scfg,scfg.dir[i]->ar,&user,&client)) continue; if(!stricmp(scfg.dir[i]->code_suffix,p)) break; @@ -4373,11 +4509,7 @@ static void ctrl_thread(void* arg) !strnicmp(cmd,"SITE EXEC",9)) { lprintf(LOG_WARNING,"%04d !SUSPECTED HACK ATTEMPT by %s: '%s'" ,sock,user.alias,cmd); - hacklog(&scfg, "FTP", user.alias, cmd, host_name, &ftp.client_addr); -#ifdef _WIN32 - if(startup->hack_sound[0] && !(startup->options&FTP_OPT_MUTE)) - PlaySound(startup->hack_sound, NULL, SND_ASYNC|SND_FILENAME); -#endif + ftp_hacklog("FTP", user.alias, cmd, host_name, &ftp.client_addr); } sockprintf(sock,"500 Syntax error: '%s'",cmd); lprintf(LOG_WARNING,"%04d !UNSUPPORTED COMMAND from %s: '%s'" @@ -4423,7 +4555,8 @@ static void ctrl_thread(void* arg) if(user.number) { /* Update User Statistics */ - logoutuserdat(&scfg, &user, time(NULL), logintime); + if(!logoutuserdat(&scfg, &user, time(NULL), logintime)) + lprintf(LOG_ERR,"%04d !ERROR in logoutuserdat",sock); /* Remove QWK-pack semaphore file (if left behind) */ sprintf(str,"%spack%04u.now",scfg.data_dir,user.number); remove(str); @@ -4443,7 +4576,7 @@ static void ctrl_thread(void* arg) if(js_runtime!=NULL) { lprintf(LOG_DEBUG,"%04d JavaScript: Destroying runtime",sock); - JS_DestroyRuntime(js_runtime); + jsrt_Release(js_runtime); } #endif @@ -4468,19 +4601,22 @@ static void ctrl_thread(void* arg) tmp_sock=sock; ftp_close_socket(&tmp_sock,__LINE__); - if(active_clients) - active_clients--, update_clients(); + { + int32_t remain = protected_int32_adjust(&active_clients, -1); + update_clients(); - thread_down(); - lprintf(LOG_DEBUG,"%04d CTRL thread terminated (%d clients, %u threads remain, %lu served)" - ,sock, active_clients, thread_count, served); + thread_down(); + lprintf(LOG_INFO,"%04d CTRL thread terminated (%ld clients remain, %lu served)" + ,sock, remain, served); + } } static void cleanup(int code, int line) { #ifdef _DEBUG - lprintf(LOG_INFO,"0000 cleanup called from line %d",line); + lprintf(LOG_DEBUG,"0000 cleanup called from line %d",line); #endif + free_cfg(&scfg); free_text(text); @@ -4490,6 +4626,11 @@ static void cleanup(int code, int line) if(server_socket!=INVALID_SOCKET) ftp_close_socket(&server_socket,__LINE__); + if(active_clients.value) + lprintf(LOG_WARNING,"#### !FTP Server terminating with %ld active clients", active_clients.value); + else + protected_int32_destroy(active_clients); + update_clients(); #ifdef _WINSOCKAPI_ @@ -4500,8 +4641,7 @@ static void cleanup(int code, int line) thread_down(); status("Down"); if(terminate_server || code) - lprintf(LOG_INFO,"#### FTP Server thread terminated (%u threads remain, %lu clients served)" - ,thread_count, served); + lprintf(LOG_INFO,"#### FTP Server thread terminated (%lu clients served)", served); if(startup!=NULL && startup->terminated!=NULL) startup->terminated(startup->cbdata,code); } @@ -4513,7 +4653,7 @@ const char* DLLCALL ftp_ver(void) DESCRIBE_COMPILER(compiler); - sscanf("$Revision: 1.1.1.1 $", "%*s %s", revision); + sscanf("$Revision: 1.1.1.2 $", "%*s %s", revision); sprintf(ver,"%s %s%s " "Compiled %s %s with %s" @@ -4552,6 +4692,7 @@ void DLLCALL ftp_server(void* arg) ftp_ver(); startup=(ftp_startup_t*)arg; + SetThreadName("FTP Server"); #ifdef _THREAD_SUID_BROKEN if(thread_suid_broken) @@ -4593,12 +4734,11 @@ void DLLCALL ftp_server(void* arg) 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; + js_server_props.clients=&active_clients.value; js_server_props.options=&startup->options; js_server_props.interface_addr=&startup->interface_addr; #endif - uptime=0; served=0; startup->recycle_now=FALSE; @@ -4635,7 +4775,10 @@ void DLLCALL ftp_server(void* arg) t=time(NULL); lprintf(LOG_INFO,"Initializing on %.24s with options: %lx" - ,CTIME_R(&t,str),startup->options); + ,ctime_r(&t,str),startup->options); + + if(chdir(startup->ctrl_dir)!=0) + lprintf(LOG_ERR,"!ERROR %d changing directory to: %s", errno, startup->ctrl_dir); /* Initial configuration and load from CNF files */ SAFECOPY(scfg.ctrl_dir, startup->ctrl_dir); @@ -4643,8 +4786,8 @@ void DLLCALL ftp_server(void* arg) scfg.size=sizeof(scfg); SAFECOPY(error,UNKNOWN_LOAD_ERROR); if(!load_cfg(&scfg, text, TRUE, error)) { - lprintf(LOG_ERR,"!ERROR %s",error); - lprintf(LOG_ERR,"!Failed to load configuration files"); + lprintf(LOG_CRIT,"!ERROR %s",error); + lprintf(LOG_CRIT,"!Failed to load configuration files"); cleanup(1,__LINE__); return; } @@ -4652,16 +4795,8 @@ void DLLCALL ftp_server(void* arg) if(startup->host_name[0]==0) SAFECOPY(startup->host_name,scfg.sys_inetaddr); - if(!(scfg.sys_misc&SM_LOCAL_TZ) && !(startup->options&FTP_OPT_LOCAL_TIMEZONE)) { - if(putenv("TZ=UTC0")) - lprintf(LOG_ERR,"!putenv() FAILED"); - tzset(); - - if((t=checktime())!=0) { /* Check binary time */ - lprintf(LOG_ERR,"!TIME PROBLEM (%ld)",t); - cleanup(1,__LINE__); - return; - } + if((t=checktime())!=0) { /* Check binary time */ + lprintf(LOG_ERR,"!TIME PROBLEM (%ld)",t); } if(uptime==0) @@ -4675,7 +4810,7 @@ void DLLCALL ftp_server(void* arg) MKDIR(scfg.temp_dir); lprintf(LOG_DEBUG,"Temporary file directory: %s", scfg.temp_dir); if(!isdir(scfg.temp_dir)) { - lprintf(LOG_ERR,"!Invalid temp directory: %s", scfg.temp_dir); + lprintf(LOG_CRIT,"!Invalid temp directory: %s", scfg.temp_dir); cleanup(1,__LINE__); return; } @@ -4704,7 +4839,8 @@ void DLLCALL ftp_server(void* arg) lprintf(LOG_DEBUG,"Maximum inactivity: %d seconds",startup->max_inactivity); - active_clients=0, update_clients(); + protected_int32_init(&active_clients, 0); + update_clients(); strlwr(scfg.sys_id); /* Use lower-case unix-looking System ID for group name */ @@ -4712,14 +4848,10 @@ void DLLCALL ftp_server(void* arg) strlwr(scfg.lib[i]->sname); dotname(scfg.lib[i]->sname,scfg.lib[i]->sname); } -#if 0 /* this is now handled by load_cfg()->prep_cfg() */ - for(i=0;icode_suffix); -#endif /* open a socket and wait for a client */ if((server_socket=ftp_open_socket(SOCK_STREAM))==INVALID_SOCKET) { - lprintf(LOG_ERR,"!ERROR %d opening socket", ERROR_VALUE); + lprintf(LOG_CRIT,"!ERROR %d opening socket", ERROR_VALUE); cleanup(1,__LINE__); return; } @@ -4735,26 +4867,30 @@ void DLLCALL ftp_server(void* arg) server_addr.sin_family = AF_INET; server_addr.sin_port = htons(startup->port); - if(startup->seteuid!=NULL) - startup->seteuid(FALSE); + if(startup->port < IPPORT_RESERVED) { + if(startup->seteuid!=NULL) + startup->seteuid(FALSE); + } result=retry_bind(server_socket, (struct sockaddr *) &server_addr,sizeof(server_addr) ,startup->bind_retry_count,startup->bind_retry_delay,"FTP Server",lprintf); - if(startup->seteuid!=NULL) - startup->seteuid(TRUE); + if(startup->port < IPPORT_RESERVED) { + if(startup->seteuid!=NULL) + startup->seteuid(TRUE); + } if(result!=0) { - lprintf(LOG_ERR,"%04d %s", server_socket, BIND_FAILURE_HELP); + lprintf(LOG_CRIT,"%04d %s", server_socket, BIND_FAILURE_HELP); cleanup(1,__LINE__); return; } if((result=listen(server_socket, 1))!= 0) { - lprintf(LOG_ERR,"%04d !ERROR %d (%d) listening on socket" + lprintf(LOG_CRIT,"%04d !ERROR %d (%d) listening on socket" ,server_socket, result, ERROR_VALUE); cleanup(1,__LINE__); return; } - lprintf(LOG_NOTICE,"%04d FTP Server thread started on port %d",server_socket,startup->port); + lprintf(LOG_INFO,"%04d FTP Server listening on port %u",server_socket,startup->port); status(STATUS_WFC); /* Setup recycle/shutdown semaphore file lists */ @@ -4763,28 +4899,24 @@ void DLLCALL ftp_server(void* arg) SAFEPRINTF(path,"%sftpsrvr.rec",scfg.ctrl_dir); /* legacy */ semfile_list_add(&recycle_semfiles,path); if(!initialized) { - initialized=time(NULL); semfile_list_check(&initialized,recycle_semfiles); semfile_list_check(&initialized,shutdown_semfiles); } - /* signal caller that we've started up successfully */ if(startup->started!=NULL) startup->started(startup->cbdata); + lprintf(LOG_INFO,"%04d FTP Server thread started",server_socket); + while(server_socket!=INVALID_SOCKET && !terminate_server) { - if(active_clients==0) { + if(active_clients.value==0) { if(!(startup->options&FTP_OPT_NO_RECYCLE)) { if((p=semfile_list_check(&initialized,recycle_semfiles))!=NULL) { lprintf(LOG_INFO,"0000 Recycle semaphore file (%s) detected",p); break; } -#if 0 /* unused */ - if(startup->recycle_sem!=NULL && sem_trywait(&startup->recycle_sem)==0) - startup->recycle_now=TRUE; -#endif if(startup->recycle_now==TRUE) { lprintf(LOG_NOTICE,"0000 Recycle semaphore signaled"); startup->recycle_now=FALSE; @@ -4845,14 +4977,13 @@ void DLLCALL ftp_server(void* arg) } if(startup->socket_open!=NULL) startup->socket_open(startup->cbdata,TRUE); - sockets++; if(trashcan(&scfg,inet_ntoa(client_addr.sin_addr),"ip-silent")) { ftp_close_socket(&client_socket,__LINE__); continue; } - - if(active_clients>=startup->max_clients) { + + if(active_clients.value>=startup->max_clients) { lprintf(LOG_WARNING,"%04d !MAXIMUM CLIENTS (%d) reached, access denied" ,client_socket, startup->max_clients); sockprintf(client_socket,"421 Maximum active clients reached, please try again later."); @@ -4873,36 +5004,22 @@ void DLLCALL ftp_server(void* arg) ftp->socket=client_socket; ftp->client_addr=client_addr; - _beginthread (ctrl_thread, 0, ftp); + _beginthread(ctrl_thread, 0, ftp); served++; } -#ifdef _DEBUG +#if 0 /* def _DEBUG */ lprintf(LOG_DEBUG,"0000 server_socket: %d",server_socket); lprintf(LOG_DEBUG,"0000 terminate_server: %d",terminate_server); #endif - if(active_clients) { + if(active_clients.value) { lprintf(LOG_DEBUG,"%04d Waiting for %d active clients to disconnect..." - ,server_socket, active_clients); + ,server_socket, active_clients.value); start=time(NULL); - while(active_clients) { + while(active_clients.value) { if(time(NULL)-start>startup->max_inactivity) { lprintf(LOG_WARNING,"%04d !TIMEOUT waiting for %d active clients" - ,server_socket, active_clients); - break; - } - mswait(100); - } - } - - if(thread_count>1) { - lprintf(LOG_DEBUG,"%04d Waiting for %d threads to terminate..." - ,server_socket, thread_count-1); - start=time(NULL); - while(thread_count>1) { - if(time(NULL)-start>TIMEOUT_THREAD_WAIT) { - lprintf(LOG_WARNING,"%04d !TIMEOUT waiting for %d threads" - ,server_socket, thread_count-1); + ,server_socket, active_clients.value); break; } mswait(100);