Newer
Older
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 !JavaScript ERROR setting user.time_left",sock);
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);
sockprintf(sock,"550 Insufficient access.");
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);
sockprintf(sock,"550 Insufficient access.");
filepos=0;
continue;
}

rswindell
committed
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
sockprintf(sock,"550 Insufficient access.");
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)) {
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,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
,p
,f.cdt);
sockprintf(sock,"550 Insufficient credit (%lu required).",f.cdt);
filepos=0;
continue;
}
}
if(strcspn(p,ILLEGAL_FILENAME_CHARS)!=strlen(p)) {
success=FALSE;
lprintf(LOG_WARNING,"%04d !ILLEGAL FILENAME ATTEMPT by %s: %s"
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
if(fexistcase(fname)) {

rswindell
committed
if(!getsize && !getdate && !delecmd)
lprintf(LOG_INFO,"%04d %s downloading: %s (%lu 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)
sockprintf(sock,"213 %lu", flength(fname));
else if(getdate && success) {
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);

rswindell
committed
} else if(delecmd && success) {
if(removecase(fname)!=0) {
lprintf(LOG_ERR,"%04d !ERROR %d deleting %s",sock,errno,fname);

rswindell
committed
sockprintf(sock,"450 %s could not be deleted (error: %d)"
,fname,errno);
} else {
lprintf(LOG_NOTICE,"%04d %s deleted %s",sock,user.alias,fname);

rswindell
committed
if(filedat)
removefiledat(&scfg,&f);
sockprintf(sock,"250 %s deleted.",fname);
}
} else if(success) {
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,&client,dir,FALSE,credits,FALSE,NULL);
sockprintf(sock,"550 File not found: %s",p);
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')) {
sockprintf(sock,"553 Insufficient access.");
continue;
}
p=cmd+4;
SKIP_WHITESPACE(p);
if(*p==0)
sockprintf(sock,"501 No file description given.");
else {
sockprintf(sock,"200 File description set. Ready to STOR file.");
}
continue;
}
if(!strnicmp(cmd, "STOR ", 5) || !strnicmp(cmd, "APPE ", 5)) {
if(user.rest&FLAG('U')) {
sockprintf(sock,"553 Insufficient access.");
continue;
}
if(transfer_inprogress==TRUE) {
lprintf(LOG_WARNING,"%04d !TRANSFER already in progress (%s)",sock,cmd);
sockprintf(sock,"425 Transfer already in progress.");
continue;
}
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!=scfg.sysop_dir && i!=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"
,sock,user.alias);
sockprintf(sock,"553 Invalid directory.");
continue;
}

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);
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(*p=='-'
|| strcspn(p,ILLEGAL_FILENAME_CHARS)!=strlen(p)
|| trashcan(&scfg,p,"file")) {
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
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"
,sock,user.alias,fname);
sockprintf(sock,"553 File already exists.");
continue;
}
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);
sockprintf(sock,"550 File not found: %s",p);
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,"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,"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
,dir
,TRUE /* uploading */
,TRUE /* credits */
,desc
);
filepos=0;
continue;
}
if(!stricmp(cmd,"CDUP") || !stricmp(cmd,"XCUP")) {
if(curdir<0)
curlib=-1;
else
curdir=-1;
sockprintf(sock,"200 CDUP command successful.");
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)) {
sockprintf(sock,"550 Directory does not exist.");
lprintf(LOG_WARNING,"%04d !%s attempted to mount invalid directory: %s"
,sock, user.alias, p);
continue;
}
local_fsys=TRUE;
sockprintf(sock,"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) {
4367
4368
4369
4370
4371
4372
4373
4374
4375
4376
4377
4378
4379
4380
4381
4382
4383
4384
4385
4386
4387
4388
4389
4390
4391
4392
4393
4394
4395
4396
4397
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!=scfg.sysop_dir && i!=scfg.upload_dir
if(!stricmp(scfg.dir[i]->code_suffix,p))
4420
4421
4422
4423
4424
4425
4426
4427
4428
4429
4430
4431
4432
4433
4434
4435
4436
4437
4438
4439
4440
4441
4442
4443
4444
4445
4446
break;
}
if(i<scfg.total_dirs) {
curdir=i;
success=TRUE;
} else
success=FALSE;
}
if(success)
sockprintf(sock,"250 CWD command successful.");
else {
sockprintf(sock,"550 %s: No such file or directory.",p);
curlib=orglib;
curdir=orgdir;
}
continue;
}
if(!stricmp(cmd, "PWD") || !stricmp(cmd,"XPWD")) {
if(curlib<0)
sockprintf(sock,"257 \"/\" is current directory.");
else if(curdir<0)
sockprintf(sock,"257 \"/%s\" is current directory."
,scfg.lib[curlib]->sname);
else
sockprintf(sock,"257 \"/%s/%s\" is current directory."
,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 !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
}
sockprintf(sock,"500 Syntax error: '%s'",cmd);
lprintf(LOG_WARNING,"%04d !UNSUPPORTED COMMAND from %s: '%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) {
if(server_socket==INVALID_SOCKET || terminate_server) {
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);

rswindell
committed
sockprintf(sock,"421 Sorry, you've run out of time.");
ftp_close_socket(&data_sock,__LINE__);
transfer_aborted=TRUE;
}
if((time(NULL)-lastactive)>startup->max_inactivity) {
lprintf(LOG_WARNING,"%04d Disconnecting due to to inactivity",sock);

rswindell
committed
sockprintf(sock,"421 Disconnecting due to inactivity (%u seconds)."
,startup->max_inactivity);
ftp_close_socket(&data_sock,__LINE__);
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 !ERROR in logoutuserdat",sock);
/* Remove QWK-pack semaphore file (if left behind) */
sprintf(str,"%spack%04u.now",scfg.data_dir,user.number);
remove(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_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)
ftp_close_socket(&pasv_sock,__LINE__);
if(data_sock!=INVALID_SOCKET)
ftp_close_socket(&data_sock,__LINE__);
client_off(sock);
socket_debug[sock]&=~SOCKET_DEBUG_CTRL;
#if defined(SOCKET_DEBUG_TERMINATE)
socket_debug[sock]&=~SOCKET_DEBUG_TERMINATE;
#endif
tmp_sock=sock;
ftp_close_socket(&tmp_sock,__LINE__);
if(active_clients)
active_clients--, update_clients();
thread_down();
lprintf(LOG_INFO,"%04d CTRL thread terminated (%d clients, %u threads remain, %lu served)"
,sock, active_clients, thread_count, served);
lprintf(LOG_DEBUG,"0000 cleanup called from line %d",line);
free_text(text);
semfile_list_free(&recycle_semfiles);
semfile_list_free(&shutdown_semfiles);
if(server_socket!=INVALID_SOCKET)

rswindell
committed
ftp_close_socket(&server_socket,__LINE__);
#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(thread_count)
lprintf(LOG_WARNING,"#### !FTP Server threads (%u) remain after termination", thread_count);
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];
SOCKADDR_IN server_addr;
SOCKADDR_IN client_addr;
socklen_t client_addr_len;
SOCKET client_socket;
int i;
int result;
time_t t;
time_t start;
time_t initialized=0;

rswindell
committed
fd_set socket_set;

rswindell
committed
struct timeval tv;
#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;
}
/* Setup intelligent defaults */
if(startup->port==0) startup->port=IPPORT_FTP;
if(startup->qwk_timeout==0) startup->qwk_timeout=600; /* seconds */
if(startup->max_inactivity==0) startup->max_inactivity=300; /* seconds */
if(startup->sem_chk_freq==0) startup->sem_chk_freq=2; /* 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");
startup->options|=FTP_OPT_HTML_INDEX_FILE;
}
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;
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.options=&startup->options;
js_server_props.interface_addr=&startup->interface_addr;
uptime=0;
served=0;
startup->recycle_now=FALSE;
startup->shutdown_now=FALSE;
terminate_server=FALSE;
thread_up(FALSE /* setuid */);
status("Initializing");
memset(&scfg, 0, sizeof(scfg));
lprintf(LOG_INFO,"Synchronet FTP Server Revision %s%s"
#ifdef _DEBUG
," Debug"
lprintf(LOG_INFO,"Compiled %s %s with %s", __DATE__, __TIME__, compiler);
sbbs_srand(); /* Seed random number generator */

rswindell
committed
if(!winsock_startup()) {

rswindell
committed
return;
}
t=time(NULL);
lprintf(LOG_INFO,"Initializing on %.24s with options: %lx"
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);
lprintf(LOG_INFO,"Loading configuration files from %s", scfg.ctrl_dir);
scfg.size=sizeof(scfg);
SAFECOPY(error,UNKNOWN_LOAD_ERROR);
if(!load_cfg(&scfg, text, TRUE, error)) {
lprintf(LOG_CRIT,"!ERROR %s",error);
lprintf(LOG_CRIT,"!Failed to load configuration files");
return;
}
if(startup->host_name[0]==0)
SAFECOPY(startup->host_name,scfg.sys_inetaddr);
if((t=checktime())!=0) { /* Check binary time */
lprintf(LOG_ERR,"!TIME PROBLEM (%ld)",t);
}
if(uptime==0)
uptime=time(NULL); /* this must be done *after* setting the timezone */
if(startup->temp_dir[0])
SAFECOPY(scfg.temp_dir,startup->temp_dir);
else
SAFECOPY(scfg.temp_dir,"../temp");
prep_dir(scfg.ctrl_dir, scfg.temp_dir, sizeof(scfg.temp_dir));
MKDIR(scfg.temp_dir);
lprintf(LOG_DEBUG,"Temporary file directory: %s", scfg.temp_dir);
if(!isdir(scfg.temp_dir)) {
lprintf(LOG_CRIT,"!Invalid temp directory: %s", scfg.temp_dir);
cleanup(1,__LINE__);
return;
}
if(!startup->max_clients) {
startup->max_clients=scfg.sys_nodes;
if(startup->max_clients<10)
startup->max_clients=10;
}
lprintf(LOG_DEBUG,"Maximum clients: %d",startup->max_clients);
/* Sanity-check the passive port range */
if(startup->pasv_port_low || startup->pasv_port_high) {
if(startup->pasv_port_low > startup->pasv_port_high
|| startup->pasv_port_high-startup->pasv_port_low < (startup->max_clients-1)) {
lprintf(LOG_WARNING,"!Correcting Passive Port Range (Low: %u, High: %u)"
,startup->pasv_port_low,startup->pasv_port_high);
if(startup->pasv_port_low)
startup->pasv_port_high = startup->pasv_port_low+(startup->max_clients-1);
else
startup->pasv_port_low = startup->pasv_port_high-(startup->max_clients-1);
}
lprintf(LOG_DEBUG,"Passive Port Low: %u",startup->pasv_port_low);
lprintf(LOG_DEBUG,"Passive Port High: %u",startup->pasv_port_high);
}
lprintf(LOG_DEBUG,"Maximum inactivity: %d seconds",startup->max_inactivity);
active_clients=0, update_clients();
strlwr(scfg.sys_id); /* Use lower-case unix-looking System ID for group name */
for(i=0;i<scfg.total_libs;i++) {
strlwr(scfg.lib[i]->sname);
dotname(scfg.lib[i]->sname,scfg.lib[i]->sname);
}
/* open a socket and wait for a client */
if((server_socket=ftp_open_socket(SOCK_STREAM))==INVALID_SOCKET) {
lprintf(LOG_CRIT,"!ERROR %d opening socket", ERROR_VALUE);
return;
}
lprintf(LOG_DEBUG,"%04d FTP Server socket opened",server_socket);
/*****************************/
/* Listen for incoming calls */
/*****************************/
memset(&server_addr, 0, sizeof(server_addr));
server_addr.sin_addr.s_addr = htonl(startup->interface_addr);
server_addr.sin_family = AF_INET;
server_addr.sin_port = htons(startup->port);
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->port < IPPORT_RESERVED) {
if(startup->seteuid!=NULL)
startup->seteuid(TRUE);
}
lprintf(LOG_CRIT,"%04d %s", server_socket, BIND_FAILURE_HELP);
return;
}
if((result=listen(server_socket, 1))!= 0) {
lprintf(LOG_CRIT,"%04d !ERROR %d (%d) listening on socket"
,server_socket, result, ERROR_VALUE);
return;
}
lprintf(LOG_INFO,"%04d FTP Server listening on port %u",server_socket,startup->port);
status(STATUS_WFC);
/* Setup recycle/shutdown semaphore file lists */
shutdown_semfiles=semfile_list_init(scfg.ctrl_dir,"shutdown","ftp");
recycle_semfiles=semfile_list_init(scfg.ctrl_dir,"recycle","ftp");
SAFEPRINTF(path,"%sftpsrvr.rec",scfg.ctrl_dir); /* legacy */
semfile_list_add(&recycle_semfiles,path);
if(!initialized) {
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(!(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(startup->recycle_now==TRUE) {
lprintf(LOG_NOTICE,"0000 Recycle semaphore signaled");
startup->recycle_now=FALSE;
break;
}
if(((p=semfile_list_check(&initialized,shutdown_semfiles))!=NULL
&& lprintf(LOG_INFO,"0000 Shutdown semaphore file (%s) detected",p))
|| (startup->shutdown_now==TRUE
&& lprintf(LOG_INFO,"0000 Shutdown semaphore signaled"))) {
startup->shutdown_now=FALSE;
terminate_server=TRUE;
break;
}
/* now wait for connection */
tv.tv_sec=startup->sem_chk_freq;
tv.tv_usec=0;

rswindell
committed
FD_ZERO(&socket_set);
FD_SET(server_socket,&socket_set);

rswindell
committed
if((i=select(server_socket+1,&socket_set,NULL,NULL,&tv))<1) {
continue;
if(ERROR_VALUE==EINTR)
lprintf(LOG_DEBUG,"%04d FTP Server listening interrupted", server_socket);
else if(ERROR_VALUE == ENOTSOCK)
lprintf(LOG_NOTICE,"%04d FTP Server socket closed", server_socket);
lprintf(LOG_WARNING,"%04d !ERROR %d selecting socket",server_socket, ERROR_VALUE);
continue;

rswindell
committed
}
if(server_socket==INVALID_SOCKET || terminate_server) /* terminated */
client_addr_len = sizeof(client_addr);
client_socket = accept(server_socket, (struct sockaddr *)&client_addr
,&client_addr_len);
if(client_socket == INVALID_SOCKET)
{
#if 0 /* is this necessary still? */
if(ERROR_VALUE == ENOTSOCK || ERROR_VALUE == EINTR || ERROR_VALUE == EINVAL) {
lprintf(LOG_NOTICE,"0000 FTP socket closed while listening");
break;
}
#endif
lprintf(LOG_WARNING,"%04d !ERROR %d accepting connection"
,server_socket, ERROR_VALUE);
#ifdef _WIN32
if(WSAGetLastError()==WSAENOBUFS) /* recycle (re-init WinSock) on this error */
break;
#endif
continue;
}
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) {
,client_socket, startup->max_clients);
sockprintf(client_socket,"421 Maximum active clients reached, please try again later.");
mswait(3000);
ftp_close_socket(&client_socket,__LINE__);
continue;
}
if((ftp=malloc(sizeof(ftp_t)))==NULL) {
lprintf(LOG_CRIT,"%04d !ERROR allocating %d bytes of memory for ftp_t"
,client_socket,sizeof(ftp_t));
sockprintf(client_socket,"421 System error, please try again later.");
mswait(3000);
ftp_close_socket(&client_socket,__LINE__);
continue;
}
ftp->socket=client_socket;
ftp->client_addr=client_addr;
_beginthread (ctrl_thread, 0, ftp);
served++;
lprintf(LOG_DEBUG,"0000 server_socket: %d",server_socket);
lprintf(LOG_DEBUG,"0000 terminate_server: %d",terminate_server);
#endif
if(active_clients) {
lprintf(LOG_DEBUG,"%04d Waiting for %d active clients to disconnect..."
,server_socket, active_clients);
start=time(NULL);
while(active_clients) {
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);
break;
}
mswait(100);
if(!terminate_server) {
lprintf(LOG_INFO,"Recycling server...");
if(startup->recycle!=NULL)
startup->recycle(startup->cbdata);
} while(!terminate_server);