Newer
Older
/* Library folders */
for(i=0;i<scfg.total_libs;i++) {
if(!wildmatchi(scfg.lib[i]->vdir, filespec, FALSE))
continue;
fprintf(fp,"dr-xr-xr-x 1 %-*s %-8s %9ld %s %2d %02d:%02d %s\r\n"
,NAME_LEN
,scfg.sys_id
,scfg.sys_id
,ftp_mon[cur_tm.tm_mon],cur_tm.tm_mday,cur_tm.tm_hour,cur_tm.tm_min
,scfg.lib[i]->vdir);
fprintf(fp,"%s\r\n",scfg.lib[i]->vdir);
lprintf(LOG_INFO,"%04d <%s> %slisting: %s library in %s mode"
,sock, user.alias, detail ? "detailed ":"", scfg.lib[lib]->vdir, mode);
for(i=0;i<scfg.total_dirs;i++) {
if(scfg.dir[i]->lib!=lib)
continue;
if(i!=(int)scfg.sysop_dir && i!=(int)scfg.upload_dir
if(!wildmatchi(scfg.dir[i]->vdir, filespec, FALSE))
continue;
fprintf(fp,"drwxrwxrwx 1 %-*s %-8s %9ld %s %2d %02d:%02d %s\r\n"
,scfg.lib[lib]->vdir
,ftp_mon[cur_tm.tm_mon],cur_tm.tm_mday,cur_tm.tm_hour,cur_tm.tm_min
,scfg.dir[i]->vdir);
fprintf(fp,"%s\r\n",scfg.dir[i]->vdir);
} else if(chk_ar(&scfg,scfg.dir[dir]->ar,&user,&client)) {
lprintf(LOG_INFO,"%04d <%s> %slisting: /%s/%s directory in %s mode"
,sock, user.alias, detail ? "detailed ":""
,scfg.lib[lib]->vdir, scfg.dir[dir]->vdir, mode);
smb_t smb;
if((result = smb_open_dir(&scfg, &smb, dir)) != SMB_SUCCESS) {
lprintf(LOG_ERR, "ERROR %d (%s) opening %s", result, smb.last_error, smb.file);
continue;
}
time_t start = time(NULL);
size_t file_count = 0;
file_t* file_list = loadfiles(&smb
,filespec, /* time: */0, file_detail_normal, scfg.dir[dir]->sort, &file_count);
for(size_t i = 0; i < file_count; i++) {
file_t* f = &file_list[i];

rswindell
committed
if(detail) {
f->size = f->cost;
t = f->hdr.when_imported.time;
if(scfg.dir[dir]->misc&DIR_FCHK) {
struct stat st;
if(stat(getfilepath(&scfg, f, path), &st) != 0)
continue;
f->size = st.st_size;
t = st.st_mtime;
}
if(localtime_r(&t,&tm)==NULL)

rswindell
committed
memset(&tm,0,sizeof(tm));
if(f->hdr.attr & MSG_ANONYMOUS)
SAFECOPY(str,ANONYMOUS);
else
dotname(f->from,str);
fprintf(fp,"-r--r--r-- 1 %-*s %-8s %9"PRId64" %s %2d "

rswindell
committed
,NAME_LEN
,scfg.dir[dir]->vdir
,(int64_t)f->size
,ftp_mon[tm.tm_mon],tm.tm_mday);

rswindell
committed
if(tm.tm_year==cur_tm.tm_year)
fprintf(fp,"%02d:%02d %s\r\n"
,tm.tm_hour,tm.tm_min
,f->name);

rswindell
committed
else
fprintf(fp,"%5d %s\r\n"
,1900+tm.tm_year
,f->name);

rswindell
committed
} else
fprintf(fp,"%s\r\n", f->name);
lprintf(LOG_INFO, "%04d <%s> %slisting (%ld bytes) of /%s/%s (%lu files) created in %ld seconds"
,sock, user.alias, detail ? "detailed ":"", ftell(fp), scfg.lib[lib]->vdir, scfg.dir[dir]->vdir
,(ulong)file_count, (long)time(NULL) - start);
freefiles(file_list, file_count);
smb_close(&smb);
lprintf(LOG_INFO,"%04d <%s> %slisting: /%s/%s directory in %s mode (empty - no access)"
,sock, user.alias, detail ? "detailed ":"", scfg.lib[lib]->vdir, scfg.dir[dir]->vdir, mode);
filexfer(&data_addr,sock,sess,pasv_sock,pasv_sess,&data_sock,&data_sess,fname,0L
,&transfer_inprogress,&transfer_aborted
,TRUE /* delfile */
,TRUE /* tmpfile */
,&lastactive,&user,&client,dir,FALSE,FALSE,FALSE,NULL,protection);
if(!strnicmp(cmd, "RETR ", 5)
|| !strnicmp(cmd, "SIZE ",5)

rswindell
committed
|| !strnicmp(cmd, "MDTM ",5)
|| !strnicmp(cmd, "DELE ",5)) {
getdate=FALSE;
getsize=FALSE;

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

rswindell
committed
else if(!strnicmp(cmd,"DELE ",5))
delecmd=TRUE;
if(!getsize && !getdate && user.rest&FLAG('D')) {
filepos=0;
continue;
}
credits=TRUE;
success=FALSE;
delfile=FALSE;
tmpfile=FALSE;
lib=curlib;
dir=curdir;
p=cmd+5;
SKIP_WHITESPACE(p);
if(!strnicmp(p,BBS_FSYS_DIR,strlen(BBS_FSYS_DIR)))
p+=strlen(BBS_FSYS_DIR); /* already mounted */
if(*p=='/') {
lib=-1;
p++;
}
if(!strncmp(p,"./",2))
if(lib<0 && ftpalias(p, fname, &user, &client, &dir)==TRUE) {
success=TRUE;
credits=TRUE; /* include in d/l stats */
tmpfile=FALSE;
delfile=FALSE;
lprintf(LOG_INFO,"%04d <%s> %.4s by alias: %s"
if(dir>=0)
lib=scfg.dir[dir]->lib;
}
if(!success && lib<0 && (tp=strchr(p,'/'))!=NULL) {
dir=-1;
*tp=0;
for(i=0;i<scfg.total_libs;i++) {
if(!stricmp(scfg.lib[i]->vdir,p))
if(i<scfg.total_libs)
lib=i;
p=tp+1;
}
if(!success && dir<0 && (tp=strchr(p,'/'))!=NULL) {
*tp=0;
for(i=0;i<scfg.total_dirs;i++) {
if(scfg.dir[i]->lib!=lib)
continue;
if(!stricmp(scfg.dir[i]->vdir,p))
if(i<scfg.total_dirs)
dir=i;
p=tp+1;
}
sprintf(str,"%s.qwk",scfg.sys_id);
if(lib<0 && startup->options&FTP_OPT_ALLOW_QWK

rswindell
committed
&& !stricmp(p,str) && !delecmd) {
if(!fexistcase(qwkfile)) {
lprintf(LOG_INFO,"%04d <%s> creating QWK packet...",sock,user.alias);
sprintf(str,"%spack%04u.now",scfg.data_dir,user.number);
if(!fmutex(str, startup->host_name, /* max_age: */60 * 60)) {
lprintf(LOG_WARNING, "%04d <%s> !ERROR %d (%s) creating mutex-semaphore file: %s"
,sock, user.alias, errno, strerror(errno), str);
sockprintf(sock,sess,"451 Packet creation already in progress (are you logged-in concurrently?)");
filepos=0;
continue;
}
t=time(NULL);
while(fexist(str) && !terminate_server) {
if(!socket_check(sock,NULL,NULL,0))
break;
if(time(NULL)-t>startup->qwk_timeout)
break;
mswait(1000);
}
if(!socket_check(sock,NULL,NULL,0)) {
lprintf(LOG_NOTICE,"%04d <%s> disconnected while waiting for QWK packet creation"
,sock, user.alias);
(void)ftp_remove(sock, __LINE__, str, user.alias);
continue;
}
if(fexist(str)) {
lprintf(LOG_WARNING,"%04d <%s> !TIMEOUT waiting for QWK packet creation", sock, user.alias);
sockprintf(sock,sess,"451 Time-out waiting for packet creation.");
(void)ftp_remove(sock, __LINE__, str, user.alias);
filepos=0;
continue;
}
if(!fexistcase(qwkfile)) {
lprintf(LOG_INFO,"%04d <%s> No QWK Packet created (no new messages)", sock, user.alias);
sockprintf(sock,sess,"550 No QWK packet created (no new messages)");
filepos=0;
continue;
}
file_size = flength(fname);
if(file_size < 1) {
lprintf(LOG_WARNING, "%04d <%s> Invalid QWK packet file size (%"PRIuOFF" bytes): %s"
,sock, user.alias, file_size, fname);
sockprintf(sock,sess,"550 Invalid QWK packet file size: %"PRIuOFF" bytes", file_size);
filepos=0;
continue;
}
success=TRUE;
delfile=TRUE;
credits=FALSE;
if(!getsize && !getdate)
lprintf(LOG_INFO,"%04d <%s> downloading QWK packet (%"PRIuOFF" bytes) in %s mode"
,sock,user.alias,file_size
,mode);
} else if(startup->options&FTP_OPT_INDEX_FILE

rswindell
committed
&& !stricmp(p,startup->index_file_name)
&& !delecmd) {
if(getsize) {
sockprintf(sock,sess, "550 Size not available for dynamically generated files");
continue;
}
if((fp=fopen(ftp_tmpfname(fname,"ndx",sock),"wb"))==NULL) {
lprintf(LOG_ERR,"%04d <%s> !ERROR %d (%s) line %d opening %s"
,sock, user.alias, errno, strerror(errno), __LINE__, fname);
filepos=0;
continue;
}
success=TRUE;
if(getdate)
file_date=time(NULL);
else {
lprintf(LOG_INFO,"%04d <%s> downloading %s for %s in %s mode"
,sock, user.alias, startup->index_file_name, genvpath(lib,dir,str)
,mode);
credits=FALSE;
tmpfile=TRUE;
delfile=TRUE;
fprintf(fp,"%-*s File/Folder Descriptions\r\n"
,INDEX_FNAME_LEN,startup->index_file_name);
if(lib<0) {
/* File Aliases */
sprintf(aliasfile,"%sftpalias.cfg",scfg.ctrl_dir);
if((alias_fp=fopen(aliasfile,"r"))!=NULL) {
while(!feof(alias_fp)) {
if(!fgets(aliasline,sizeof(aliasline),alias_fp))
break;
p=aliasline; /* alias pointer */
SKIP_WHITESPACE(p);
if(*p==';') /* comment */
continue;
tp=p; /* terminator pointer */
FIND_WHITESPACE(tp);
if(*tp) *tp=0;
np=tp+1; /* filename pointer */
SKIP_WHITESPACE(np);
np++; /* description pointer */
FIND_WHITESPACE(np);
while(*np && *np<' ') np++;
truncsp(np);
fprintf(fp,"%-*s %s\r\n",INDEX_FNAME_LEN,p,np);
}
fclose(alias_fp);
}
/* QWK Packet */
if(startup->options&FTP_OPT_ALLOW_QWK /* && fexist(qwkfile) */) {
sprintf(str,"%s.qwk",scfg.sys_id);
fprintf(fp,"%-*s QWK Message Packet\r\n"
,INDEX_FNAME_LEN,str);
}
/* Library Folders */
for(i=0;i<scfg.total_libs;i++) {
continue;
fprintf(fp,"%-*s %s\r\n"
,INDEX_FNAME_LEN,scfg.lib[i]->vdir,scfg.lib[i]->lname);
}
} else if(dir<0) {
for(i=0;i<scfg.total_dirs;i++) {
if(scfg.dir[i]->lib!=lib)
continue;
if(i!=(int)scfg.sysop_dir && i!=(int)scfg.upload_dir
continue;
fprintf(fp,"%-*s %s\r\n"
,INDEX_FNAME_LEN,scfg.dir[i]->vdir,scfg.dir[i]->lname);
}
} else if(chk_ar(&scfg,scfg.dir[dir]->ar,&user,&client)){
smb_t smb;
if((result = smb_open_dir(&scfg, &smb, dir)) != SMB_SUCCESS) {
lprintf(LOG_ERR, "ERROR %d (%s) opening %s", result, smb.last_error, smb.file);
continue;
}
time_t start = time(NULL);
size_t file_count = 0;
file_t* file_list = loadfiles(&smb
,/* filespec */NULL, /* time: */0, file_detail_normal, scfg.dir[dir]->sort, &file_count);
for(size_t i = 0; i < file_count; i++) {
file_t* f = &file_list[i];
fprintf(fp,"%-*s %s\r\n",INDEX_FNAME_LEN
,f->name, f->desc);
lprintf(LOG_INFO, "%04d <%s> index (%ld bytes) of /%s/%s (%lu files) created in %ld seconds"
,sock, user.alias, ftell(fp), scfg.lib[lib]->vdir, scfg.dir[dir]->vdir
,(ulong)file_count, (long)time(NULL) - start);
freefiles(file_list, file_count);
smb_close(&smb);
fclose(fp);
}
} else if(dir>=0) {
lprintf(LOG_WARNING,"%04d <%s> has insufficient access to /%s/%s"
,sock,user.alias
,scfg.lib[scfg.dir[dir]->lib]->vdir
,scfg.dir[dir]->vdir);
filepos=0;
continue;
}

rswindell
committed
if(!getsize && !getdate && !delecmd
&& !can_user_download(&scfg, dir, &user, &client, /* reason */NULL)) {
lprintf(LOG_WARNING,"%04d <%s> has insufficient access to download from /%s/%s"
,sock,user.alias
,scfg.lib[scfg.dir[dir]->lib]->vdir
,scfg.dir[dir]->vdir);
filepos=0;
continue;
}

rswindell
committed
if(delecmd && !dir_op(&scfg,&user,&client,dir) && !(user.exempt&FLAG('R'))) {
lprintf(LOG_WARNING,"%04d <%s> has insufficient access to delete files in /%s/%s"
,sock,user.alias
,scfg.lib[scfg.dir[dir]->lib]->vdir
,scfg.dir[dir]->vdir);

rswindell
committed
filepos=0;
continue;
}
SAFEPRINTF2(fname,"%s%s",scfg.dir[dir]->path,p);
filedat=findfile(&scfg, dir, p, NULL);
if(!filedat) {
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
loadfile(&scfg, dir, p, &f, file_detail_normal);
f.cost=(uint32_t)flength(fname);
if(f.cost>user_available_credits(&user)) {
lprintf(LOG_WARNING,"%04d <%s> has insufficient credit to download /%s/%s/%s (%lu credits)"
,sock,user.alias,scfg.lib[scfg.dir[dir]->lib]->vdir
,scfg.dir[dir]->vdir
,(ulong)f.cost);
sockprintf(sock,sess,"550 Insufficient credit (%lu required).", (ulong)f.cost);
smb_freefilemem(&f);
smb_freefilemem(&f);
}
if(strcspn(p,ILLEGAL_FILENAME_CHARS)!=strlen(p)) {
success=FALSE;
lprintf(LOG_WARNING,"%04d <%s> !ILLEGAL FILENAME ATTEMPT by %s [%s]: '%s'"
,sock, user.alias, host_name, host_ip, p);
ftp_hacklog("FTP FILENAME", user.alias, cmd, host_name, &ftp.client_addr);
if(fexistcase(fname)) {

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

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

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

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

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);
freespace = getfreediskspace(scfg.data_dir, 1);
append=(strnicmp(cmd,"APPE",4)==0);
if(!dir_op(&scfg,&user,&client,dir) && !(user.exempt&FLAG('U'))) {
if(!chk_ar(&scfg,scfg.dir[dir]->ul_ar,&user,&client)
|| !chk_ar(&scfg, scfg.lib[scfg.dir[dir]->lib]->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]->vdir
,scfg.dir[dir]->vdir);
continue;
}
if(!append && scfg.dir[dir]->maxfiles && getfiles(&scfg,dir)>=scfg.dir[dir]->maxfiles) {
lprintf(LOG_WARNING,"%04d <%s> cannot upload to /%s/%s (directory full: %d files)"
,sock,user.alias
,scfg.lib[scfg.dir[dir]->lib]->vdir
,scfg.dir[dir]->vdir
,getfiles(&scfg,dir));
continue;
}
if(illegal_filename(p)
|| trashcan(&scfg,p,"file")) {
lprintf(LOG_WARNING,"%04d <%s> !ILLEGAL FILENAME ATTEMPT by %s [%s]: '%s'"
,sock, user.alias, host_name, host_ip, p);
ftp_hacklog("FTP FILENAME", user.alias, cmd, host_name, &ftp.client_addr);
if(!allowed_filename(&scfg, p)) {
lprintf(LOG_WARNING,"%04d <%s> !UNALLOWED FILENAME ATTEMPT by %s [%s]: '%s'"
,sock, user.alias, host_name, host_ip, p);
sockprintf(sock,sess,"553 Unallowed filename attempt");
continue;
}
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))
) {
lprintf(LOG_WARNING,"%04d <%s> attempted to overwrite existing file: '%s'"
if(append || filepos) { /* RESUME */
file_t f;
if(!loadfile(&scfg, dir, p, &f, file_detail_normal)) {
lprintf(LOG_WARNING,"%04d <%s> file (%s) not in database for %.4s command"
,sock,user.alias,fname,cmd);
continue;
}
append=FALSE;
}
/* Verify user is original uploader */
if((append || filepos) && stricmp(f.from, user.alias)) {
lprintf(LOG_WARNING,"%04d <%s> !cannot resume upload of %s, uploaded by %s"
,sock,user.alias,fname,f.from);
sockprintf(sock,sess,"553 Insufficient access (can't resume upload from different user).");
smb_freefilemem(&f);
smb_freefilemem(&f);
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);
freespace = getfreediskspace(scfg.dir[dir]->path, 1);
}
if(freespace < scfg.min_dspace) {
lprintf(LOG_ERR, "%04d <%s> !Insufficient free disk space (%s bytes) to allow upload"
,sock, user.alias, byte_estimate_to_str(freespace, str, sizeof(str), 1,1));
sockprintf(sock, sess, "452 Insufficient free disk space, try again later");
sockprintf(sock,sess,"150 Opening BINARY mode data connection for file transfer.");
filexfer(&data_addr,sock,sess,pasv_sock,pasv_sess,&data_sock,&data_sess,fname,filepos
,&transfer_inprogress,&transfer_aborted,FALSE,FALSE
,&lastactive
,&user
,dir
,TRUE /* uploading */
,TRUE /* credits */
);
filepos=0;
continue;
}
if(!stricmp(cmd,"CDUP") || !stricmp(cmd,"XCUP")) {
if(curdir<0)
curlib=-1;
else
curdir=-1;
continue;
}
if(!strnicmp(cmd, "CWD ", 4) || !strnicmp(cmd,"XCWD ",5)) {
p=cmd+4;
SKIP_WHITESPACE(p);
if(!strnicmp(p,BBS_FSYS_DIR,strlen(BBS_FSYS_DIR)))
p+=strlen(BBS_FSYS_DIR); /* already mounted */
if(*p=='/') {
curlib=-1;
curdir=-1;
p++;
}
/* Local File System? */
if(sysop && !(startup->options&FTP_OPT_NO_LOCAL_FSYS)
&& !strnicmp(p,LOCAL_FSYS_DIR,strlen(LOCAL_FSYS_DIR))) {
p+=strlen(LOCAL_FSYS_DIR);
if(!direxist(p)) {
lprintf(LOG_WARNING,"%04d <%s> attempted to mount invalid directory: '%s'"
,sock, user.alias, p);
continue;
}
sockprintf(sock,sess,"250 CWD command successful (local file system mounted).");
lprintf(LOG_INFO,"%04d <%s> mounted local file system", sock, user.alias);
continue;
}
success=FALSE;
/* Directory Alias? */
if(curlib<0 && ftpalias(p,NULL,&user,&client,&curdir)==TRUE) {
4712
4713
4714
4715
4716
4717
4718
4719
4720
4721
4722
4723
4724
4725
4726
4727
4728
4729
4730
4731
4732
4733
4734
4735
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++) {
if(!stricmp(scfg.lib[i]->vdir,p))
break;
}
if(i<scfg.total_libs) {
curlib=i;
success=TRUE;
}
}
if((!success && curdir<0) || (success && tp && *(tp+1))) {
if(tp)
p=tp+1;
tp=lastchar(p);
if(tp && *tp=='/') *tp=0;
for(i=0;i<scfg.total_dirs;i++) {
if(scfg.dir[i]->lib!=curlib)
continue;
if(i!=(int)scfg.sysop_dir && i!=(int)scfg.upload_dir
if(!stricmp(scfg.dir[i]->vdir,p))
break;
}
if(i<scfg.total_dirs) {
curdir=i;
success=TRUE;
} else
success=FALSE;
}
if(success)
sockprintf(sock,sess,"550 %s: No such directory.",p);
curlib=orglib;
curdir=orgdir;
}
continue;
}
if(!stricmp(cmd, "PWD") || !stricmp(cmd,"XPWD")) {
if(curlib<0)
,scfg.lib[curlib]->vdir);
,scfg.lib[curlib]->vdir
,scfg.dir[curdir]->vdir);
if(!strnicmp(cmd, "MKD", 3) ||
!strnicmp(cmd,"XMKD",4) ||
!strnicmp(cmd,"SITE EXEC",9)) {
lprintf(LOG_WARNING,"%04d <%s> !SUSPECTED HACK ATTEMPT: %s"
,sock,user.alias,cmd);
ftp_hacklog("FTP", user.alias, cmd, host_name, &ftp.client_addr);
// TODO: STAT is mandatory
lprintf(LOG_WARNING,"%04d <%s> !UNSUPPORTED COMMAND: %s"
,sock,user.alias,cmd);
} /* while(1) */
#if defined(SOCKET_DEBUG_TERMINATE)
socket_debug[sock]|=SOCKET_DEBUG_TERMINATE;
#endif
if(transfer_inprogress==TRUE) {
lprintf(LOG_DEBUG,"%04d Waiting for transfer to complete...",sock);
while(transfer_inprogress==TRUE) {
mswait(2000); /* allow xfer threads to terminate */
break;
}

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

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

rswindell
committed
,startup->max_inactivity);

rswindell
committed
transfer_aborted=TRUE;
}
if(count && (count%60)==0)
lprintf(LOG_WARNING,"%04d Still waiting for transfer to complete "
"(count=%lu, aborted=%d, lastactive=%lX) ..."
,sock,count,transfer_aborted,lastactive);
count++;
mswait(1000);
lprintf(LOG_DEBUG,"%04d Done waiting for transfer to complete",sock);
if(user.number) {
/* Update User Statistics */
if(!logoutuserdat(&scfg, &user, time(NULL), logintime))
lprintf(LOG_ERR,"%04d <%s> !ERROR in logoutuserdat", sock, user.alias);
mqtt_user_logout(&mqtt, &client, logintime);

Rob Swindell
committed
lprintf(LOG_INFO,"%04d <%s> logged-out", sock, user.alias);
#ifdef _WIN32
if(startup->sound.logout[0] && !sound_muted(&scfg))
PlaySound(startup->sound.logout, NULL, SND_ASYNC|SND_FILENAME);
#endif
if(startup->sound.hangup[0] && !sound_muted(&scfg))
PlaySound(startup->sound.hangup, NULL, SND_ASYNC|SND_FILENAME);
if(pasv_sock!=INVALID_SOCKET)
if(data_sock!=INVALID_SOCKET)
client_off(sock);
socket_debug[sock]&=~SOCKET_DEBUG_CTRL;
#if defined(SOCKET_DEBUG_TERMINATE)
socket_debug[sock]&=~SOCKET_DEBUG_TERMINATE;
#endif
tmp_sock=sock;
int32_t clients = protected_uint32_adjust_fetch(&active_clients, -1);
int32_t threads = thread_down();
update_clients();
lprintf(LOG_INFO,"%04d CTRL thread terminated (%d clients and %d threads remain, %lu served)"
,sock, clients, threads, served);
lprintf(LOG_DEBUG,"0000 cleanup called from line %d",line);
if(protected_uint32_value(thread_count) > 1) {
lprintf(LOG_INFO, "0000 Waiting for %d child threads to terminate", protected_uint32_value(thread_count)-1);
while(protected_uint32_value(thread_count) > 1) {
mswait(100);
}
lprintf(LOG_INFO, "0000 Done waiting for child threads to terminate");
}
free_text(text);
semfile_list_free(&recycle_semfiles);
semfile_list_free(&shutdown_semfiles);
if(ftp_set != NULL) {
xpms_destroy(ftp_set, ftp_close_socket_cb, NULL);
ftp_set = NULL;
}
update_clients(); /* active_clients is destroyed below */
listFree(¤t_connections);
if(protected_uint32_value(active_clients))
lprintf(LOG_WARNING,"!!!! Terminating with %d active clients", protected_uint32_value(active_clients));
protected_uint32_destroy(active_clients);
#ifdef _WINSOCKAPI_
if(WSAInitialized && WSACleanup()!=0)
lprintf(LOG_ERR,"0000 !WSACleanup ERROR %d",ERROR_VALUE);
if(terminate_server || code)
lprintf(LOG_INFO,"#### FTP Server thread terminated (%lu clients served)", served);
set_state(SERVER_STOPPED);
mqtt_shutdown(&mqtt);
if(startup!=NULL && startup->terminated!=NULL)
startup->terminated(startup->cbdata,code);
{
static char ver[256];
char compiler[32];
safe_snprintf(ver, sizeof(ver), "%s %s%c%s "
"Compiled %s/%s %s %s with %s"
,VERSION, REVISION
#ifdef _DEBUG
," Debug"
#else
,""
#endif
,GIT_BRANCH, GIT_HASH
,__DATE__, __TIME__, compiler);
return(ver);
}
char* p;
char path[MAX_PATH+1];
char error[256];
char str[256];
socklen_t client_addr_len;
SOCKET client_socket;
int i;
time_t t;
time_t start;
time_t initialized=0;
startup=(ftp_startup_t*)arg;
SetThreadName("sbbs/ftpServer");
#ifdef _THREAD_SUID_BROKEN
if(thread_suid_broken)
startup->seteuid(TRUE);
#endif
sbbs_beep(100,500);
fprintf(stderr, "No startup structure passed!\n");
return;
}
if(startup->size!=sizeof(ftp_startup_t)) { /* verify size */
sbbs_beep(100,500);
sbbs_beep(300,500);
sbbs_beep(100,500);
fprintf(stderr, "Invalid startup structure!\n");
return;