Newer
Older
reuseaddr=FALSE;
if((result=setsockopt(pasv_sock,SOL_SOCKET,SO_REUSEADDR,(char*)&reuseaddr,sizeof(reuseaddr)))!=0) {
lprintf(LOG_WARNING,"%04d <%s> !PASV ERROR %d disabling REUSEADDR socket option"
,sock, user.alias, ERROR_VALUE);
sockprintf(sock,sess,"425 Error %d disabling REUSEADDR socket option", ERROR_VALUE);
continue;
}
if(startup->options&FTP_OPT_DEBUG_DATA)
lprintf(LOG_DEBUG,"%04d <%s> PASV DATA socket %d opened",sock, user.alias, pasv_sock);
for(port=startup->pasv_port_low; port<=startup->pasv_port_high; port++) {
if(startup->options&FTP_OPT_DEBUG_DATA)
lprintf(LOG_DEBUG,"%04d <%s> PASV DATA trying to bind socket to port %u"
,sock, user.alias, port);
if((result=bind(pasv_sock, &pasv_addr.addr,xp_sockaddr_len(&pasv_addr)))==0)
break;
if(port==startup->pasv_port_high)
break;
lprintf(LOG_ERR,"%04d <%s> !PASV ERROR %d (%d) binding socket to port %u"
,sock, user.alias, result, ERROR_VALUE, port);
sockprintf(sock,sess,"425 Error %d binding data socket",ERROR_VALUE);
ftp_close_socket(&pasv_sock,&pasv_sess,__LINE__);
if(startup->options&FTP_OPT_DEBUG_DATA)
lprintf(LOG_DEBUG,"%04d <%s> PASV DATA socket %d bound to port %u",sock, user.alias, pasv_sock, port);
lprintf(LOG_CRIT,"%04d <%s> !PASV ERROR %d (%d) getting address/port of socket"
,sock, user.alias, result, ERROR_VALUE);
sockprintf(sock,sess,"425 Error %d getting address/port",ERROR_VALUE);
ftp_close_socket(&pasv_sock,&pasv_sess,__LINE__);
if((result=listen(pasv_sock, 1))!= 0) {
lprintf(LOG_ERR,"%04d <%s> !PASV ERROR %d (%d) listening on port %u"
,sock, user.alias, result, ERROR_VALUE,port);
sockprintf(sock,sess,"425 Error %d listening on data socket",ERROR_VALUE);
ftp_close_socket(&pasv_sock,&pasv_sess,__LINE__);
if(strnicmp(cmd, "EPSV", 4)==0)
sockprintf(sock,sess,"229 Entering Extended Passive Mode (|||%hu|)", port);
else if (stricmp(cmd,"LPSV")==0) {
switch(addr.addr.sa_family) {
case AF_INET:
sockprintf(sock,sess, "228 Entering Long Passive Mode (4, 4, %d, %d, %d, %d, 2, %d, %d)"
,((unsigned char *)&(addr.in.sin_addr))[0]
,((unsigned char *)&(addr.in.sin_addr))[1]
,((unsigned char *)&(addr.in.sin_addr))[2]
,((unsigned char *)&(addr.in.sin_addr))[3]
,((unsigned char *)&(addr.in.sin_port))[0]
,((unsigned char *)&(addr.in.sin_port))[1]);
break;
case AF_INET6:
sockprintf(sock,sess, "228 Entering Long Passive Mode (6, 16, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, %d, 2, %d, %d)"
3070
3071
3072
3073
3074
3075
3076
3077
3078
3079
3080
3081
3082
3083
3084
3085
3086
3087
3088
3089
3090
3091
3092
3093
3094
3095
,((unsigned char *)&(addr.in6.sin6_addr))[0]
,((unsigned char *)&(addr.in6.sin6_addr))[1]
,((unsigned char *)&(addr.in6.sin6_addr))[2]
,((unsigned char *)&(addr.in6.sin6_addr))[3]
,((unsigned char *)&(addr.in6.sin6_addr))[4]
,((unsigned char *)&(addr.in6.sin6_addr))[5]
,((unsigned char *)&(addr.in6.sin6_addr))[6]
,((unsigned char *)&(addr.in6.sin6_addr))[7]
,((unsigned char *)&(addr.in6.sin6_addr))[8]
,((unsigned char *)&(addr.in6.sin6_addr))[9]
,((unsigned char *)&(addr.in6.sin6_addr))[10]
,((unsigned char *)&(addr.in6.sin6_addr))[11]
,((unsigned char *)&(addr.in6.sin6_addr))[12]
,((unsigned char *)&(addr.in6.sin6_addr))[13]
,((unsigned char *)&(addr.in6.sin6_addr))[14]
,((unsigned char *)&(addr.in6.sin6_addr))[15]
,((unsigned char *)&(addr.in6.sin6_port))[0]
,((unsigned char *)&(addr.in6.sin6_port))[1]);
break;
}
}
else {
/* Choose IP address to use in passive response */
ip_addr=0;
/* TODO: IPv6 this here lookup */
if(startup->options&FTP_OPT_LOOKUP_PASV_IP
&& (host=gethostbyname(server_host_name()))!=NULL
&& host->h_addr_list[0] != NULL)
ip_addr=ntohl(*((in_addr_t*)host->h_addr_list[0]));
if(ip_addr==0 && (ip_addr=startup->pasv_ip_addr.s_addr)==0)
ip_addr=ntohl(pasv_addr.in.sin_addr.s_addr);
if(startup->options&FTP_OPT_DEBUG_DATA)
lprintf(LOG_INFO,"%04d <%s> PASV DATA IP address in response: %u.%u.%u.%u (subject to NAT)"
,user.alias
,(ip_addr>>24)&0xff
,(ip_addr>>16)&0xff
,(ip_addr>>8)&0xff
,ip_addr&0xff
);
sockprintf(sock,sess,"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
,(ushort)((port>>8)&0xff)
,(ushort)(port&0xff)
mode="passive";
continue;
}
if(!strnicmp(cmd, "TYPE ",5)) {
continue;
}
if(!strnicmp(cmd, "ALLO",4)) {
p=cmd+5;
SKIP_WHITESPACE(p);
else
l=0;
if(local_fsys)
avail=getfreediskspace(local_dir,0);
avail=getfreediskspace(scfg.data_dir,0); /* Change to temp_dir? */
sockprintf(sock,sess,"504 Only %" PRIu64 " bytes available.",avail);
sockprintf(sock,sess,"200 %" PRIu64 " bytes available.",avail);
continue;
}
if(!strnicmp(cmd, "REST",4)) {
p=cmd+4;
SKIP_WHITESPACE(p);
if(*p)
filepos=atol(p);
else
filepos=0;
sockprintf(sock,sess,"350 Restarting at %ld. Send STORE or RETRIEVE to initiate transfer."
continue;
}
if(!strnicmp(cmd, "MODE ",5)) {
p=cmd+5;
SKIP_WHITESPACE(p);
continue;
}
if(!strnicmp(cmd, "STRU ",5)) {
p=cmd+5;
SKIP_WHITESPACE(p);
continue;
}
if(!stricmp(cmd, "SYST")) {
continue;
}
if(!stricmp(cmd, "ABOR")) {
if(!transfer_inprogress)
sockprintf(sock,sess,"226 No transfer in progress.");
lprintf(LOG_WARNING,"%04d <%s> aborting transfer"
,sock,user.alias);
YIELD(); /* give send thread time to abort */
}
continue;
}
if(!strnicmp(cmd,"SMNT ",5) && sysop && !(startup->options&FTP_OPT_NO_LOCAL_FSYS)) {
p=cmd+5;
SKIP_WHITESPACE(p);
if(!stricmp(p,BBS_FSYS_DIR))
local_fsys=FALSE;
else {
if(!direxist(p)) {
lprintf(LOG_WARNING,"%04d <%s> !attempted to mount invalid directory: '%s'"
,sock, user.alias, p);
continue;
}
local_fsys=TRUE;
,local_fsys ? "Local" : "BBS");
lprintf(LOG_INFO,"%04d <%s> mounted %s file system"
,sock, user.alias, local_fsys ? "local" : "BBS");
continue;
}
/****************************/
/* Local File System Access */
/****************************/
if(sysop && local_fsys && !(startup->options&FTP_OPT_NO_LOCAL_FSYS)) {
if(local_dir[0]
&& local_dir[strlen(local_dir)-1]!='\\'
&& local_dir[strlen(local_dir)-1]!='/')
strcat(local_dir,"/");
if(!strnicmp(cmd, "MLS", 3)) {
if (cmd[3] == 'T' || cmd[3] == 'D') {
if (cmd[3] == 'D') {
if((fp=fopen(ftp_tmpfname(fname,"lst",sock),"w+b"))==NULL) {
lprintf(LOG_ERR,"%04d <%s> !ERROR %d (%s) line %d opening %s"
,sock, user.alias, errno, safe_strerror(errno, error, sizeof error), __LINE__, fname);
3234
3235
3236
3237
3238
3239
3240
3241
3242
3243
3244
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256
3257
3258
3259
3260
3261
3262
3263
3264
3265
3266
3267
3268
3269
sockprintf(sock,sess, "451 Insufficient system storage");
continue;
}
}
p=cmd+4;
SKIP_WHITESPACE(p);
filespec=p;
if (!local_dir[0])
strcpy(local_dir, "/");
SAFEPRINTF2(path,"%s%s",local_dir, filespec);
p=FULLPATH(NULL, path, 0);
strcpy(path, p);
free(p);
if (cmd[3] == 'T') {
if (access(path, 0) == -1) {
sockprintf(sock,sess, "550 No such path %s", path);
continue;
}
sockprintf(sock,sess, "250- Listing %s", path);
}
else {
if (access(path, 0) == -1) {
sockprintf(sock,sess, "550 No such path %s", path);
continue;
}
if (!isdir(path)) {
sockprintf(sock,sess, "501 Not a directory");
continue;
}
sockprintf(sock,sess, "150 Directory of %s", path);
backslash(path);
strcat(path, "*");
}
lprintf(LOG_INFO,"%04d <%s> MLSx listing: local %s in %s mode", sock, user.alias, path, mode);
now=time(NULL);
if(localtime_r(&now,&cur_tm)==NULL)
memset(&cur_tm,0,sizeof(cur_tm));
if (cmd[3] == 'T') {
write_local_mlsx(NULL, sock, sess, mlsx_feats, path, TRUE);
sockprintf(sock, sess, "250 End");
}
else {
time_t start = time(NULL);
glob(path, GLOB_MARK, NULL, &g);
for(i=0;i<(int)g.gl_pathc;i++) {
char fpath[MAX_PATH + 1];
SAFECOPY(fpath, g.gl_pathv[i]);
if(*lastchar(fpath) == '/')
*lastchar(fpath) = 0;
write_local_mlsx(fp, INVALID_SOCKET, -1, mlsx_feats, fpath, FALSE);
}
lprintf(LOG_INFO, "%04d <%s> %s-listing (%ld bytes) of local %s (%lu files) created in %" PRId64 " seconds"
,sock, user.alias, cmd, ftell(fp), path
,(ulong)g.gl_pathc, (int64_t)(time(NULL) - start));
globfree(&g);
fclose(fp);
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,-1,FALSE,FALSE,FALSE,NULL,protection);
}
}
if(!strnicmp(cmd, "LIST", 4) || !strnicmp(cmd, "NLST", 4)) {
if(!strnicmp(cmd, "LIST", 4))
detail=TRUE;
else
detail=FALSE;
if((fp=fopen(ftp_tmpfname(fname,"lst",sock),"w+b"))==NULL) {
lprintf(LOG_ERR,"%04d <%s> !ERROR %d (%s) line %d opening %s"
,sock, user.alias, errno, safe_strerror(errno, error, sizeof error), __LINE__, fname);
sockprintf(sock,sess, "451 Insufficient system storage");
continue;
}
SKIP_WHITESPACE(p);
if(*p=='-') { /* -Letc */
FIND_WHITESPACE(p);
SKIP_WHITESPACE(p);
filespec=p;
if(*filespec==0)
filespec="*";
SAFEPRINTF2(path,"%s%s",local_dir, filespec);
lprintf(LOG_INFO,"%04d <%s> %slisting: local %s in %s mode"
,sock, user.alias, detail ? "detailed ":"", path, mode);
sockprintf(sock,sess, "150 Directory of %s%s", local_dir, filespec);
now=time(NULL);
if(localtime_r(&now,&cur_tm)==NULL)
memset(&cur_tm,0,sizeof(cur_tm));
time_t start = time(NULL);
glob(path, GLOB_MARK, NULL, &g);

rswindell
committed
for(i=0;i<(int)g.gl_pathc;i++) {
char fpath[MAX_PATH + 1];
SAFECOPY(fpath, g.gl_pathv[i]);
if(*lastchar(fpath) == '/')
*lastchar(fpath) = 0;
if(localtime_r(&st.st_mtime,&tm)==NULL)
fprintf(fp,"%crw-r--r-- 1 %-8s local %9"PRId64" %s %2d "
,*lastchar(g.gl_pathv[i]) == '/' ? 'd':'-'
,(int64_t)st.st_size
,ftp_mon[tm.tm_mon],tm.tm_mday);
if(tm.tm_year==cur_tm.tm_year)
fprintf(fp,"%02d:%02d %s\r\n"
,tm.tm_hour,tm.tm_min
else
fprintf(fp,"%5d %s\r\n"
,1900+tm.tm_year
fprintf(fp,"%s\r\n", getfname(fpath));
lprintf(LOG_INFO, "%04d <%s> %slisting (%ld bytes) of local %s (%lu files) created in %" PRId64 " seconds"
,sock, user.alias, detail ? "detailed ":"", ftell(fp), path
,(ulong)g.gl_pathc, (int64_t)(time(NULL) - start));

rswindell
committed
globfree(&g);
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,-1,FALSE,FALSE,FALSE,NULL,protection);
continue;
} /* Local LIST/NLST */
if(!strnicmp(cmd, "CWD ", 4) || !strnicmp(cmd,"XCWD ",5)) {
if(!strnicmp(cmd,"CWD ",4))
p=cmd+4;
else
p=cmd+5;
SKIP_WHITESPACE(p);
tp=p;
if(*tp=='/' || *tp=='\\') /* /local: and /bbs: are valid */
tp++;
if(!strnicmp(tp,BBS_FSYS_DIR,strlen(BBS_FSYS_DIR))) {
local_fsys=FALSE;
sockprintf(sock,sess,"250 CWD command successful (BBS file system mounted).");
lprintf(LOG_INFO,"%04d <%s> mounted BBS file system", sock, user.alias);
continue;
}
if(!strnicmp(tp,LOCAL_FSYS_DIR,strlen(LOCAL_FSYS_DIR))) {
tp+=strlen(LOCAL_FSYS_DIR); /* already mounted */
p=tp;
}
if(p[1]==':' || !strncmp(p,"\\\\",2))
else if(*p=='/' || *p=='\\') {
SAFEPRINTF2(path,"%s%s",root_dir(local_dir),p+1);
p = FULLPATH(NULL, path, 0);
SAFECOPY(path, p);
free(p);
}
SAFEPRINTF2(fname,"%s%s",local_dir,p);
}
if(!direxist(path)) {
sockprintf(sock,sess,"550 Directory does not exist (%s).",path);
lprintf(LOG_WARNING,"%04d <%s> !attempted to change to an invalid directory: '%s'"
,sock, user.alias, path);
} else {
sockprintf(sock,sess,"250 CWD command successful (%s).", local_dir);
}
continue;
} /* Local CWD */
if(!stricmp(cmd,"CDUP") || !stricmp(cmd,"XCUP")) {
SAFEPRINTF(path,"%s..",local_dir);
if(FULLPATH(local_dir,path,sizeof(local_dir))==NULL)
continue;
}
if(!stricmp(cmd, "PWD") || !stricmp(cmd,"XPWD")) {
if(strlen(local_dir)>3)
local_dir[strlen(local_dir)-1]=0; /* truncate '/' */
,local_dir);
continue;
} /* Local PWD */
if(!strnicmp(cmd, "MKD ", 4) || !strnicmp(cmd,"XMKD",4)) {
p=cmd+4;
SKIP_WHITESPACE(p);
SAFEPRINTF2(fname,"%s%s",root_dir(local_dir),p+1);
SAFEPRINTF2(fname,"%s%s",local_dir,p);
if(MKDIR(fname) == 0) {
lprintf(LOG_NOTICE,"%04d <%s> created directory: %s",sock,user.alias,fname);
sockprintf(sock,sess,"521 Error %d creating directory: %s",errno,fname);
lprintf(LOG_WARNING,"%04d <%s> !ERROR %d (%s) attempting to create directory: %s"
,sock,user.alias,errno,safe_strerror(errno, error, sizeof error),fname);
}
continue;
}
if(!strnicmp(cmd, "RMD ", 4) || !strnicmp(cmd,"XRMD",4)) {
p=cmd+4;
SKIP_WHITESPACE(p);
SAFEPRINTF2(fname,"%s%s",root_dir(local_dir),p+1);
SAFEPRINTF2(fname,"%s%s",local_dir,p);
if(rmdir(fname) == 0) {
lprintf(LOG_NOTICE,"%04d <%s> removed directory: %s",sock,user.alias,fname);
sockprintf(sock,sess,"450 Error %d removing directory: %s", errno, fname);
lprintf(LOG_WARNING,"%04d <%s> !ERROR %d (%s) removing directory: %s"
,sock, user.alias, errno, safe_strerror(errno, error, sizeof error), fname);
}
continue;
}
if(!strnicmp(cmd, "RNFR ",5)) {
p=cmd+5;
SKIP_WHITESPACE(p);
SAFEPRINTF2(ren_from,"%s%s",root_dir(local_dir),p+1);
SAFEPRINTF2(ren_from,"%s%s",local_dir,p);
lprintf(LOG_WARNING,"%04d <%s> !ERROR renaming %s (not found)"
,sock,user.alias,ren_from);
} else
sockprintf(sock,sess,"350 File exists, ready for destination name");
continue;
}
if(!strnicmp(cmd, "RNTO ",5)) {
p=cmd+5;
SKIP_WHITESPACE(p);
SAFEPRINTF2(fname,"%s%s",root_dir(local_dir),p+1);
SAFEPRINTF2(fname,"%s%s",local_dir,p);
if(rename(ren_from, fname) == 0) {
sockprintf(sock,sess,"250 \"%s\" renamed to \"%s\"",ren_from,fname);
lprintf(LOG_NOTICE,"%04d <%s> renamed %s to %s",sock,user.alias,ren_from,fname);
sockprintf(sock,sess,"450 Error %d renaming file: %s", errno, ren_from);
lprintf(LOG_WARNING,"%04d <%s> !ERRROR %d (%s) renaming file: %s"
,sock, user.alias, errno, safe_strerror(errno, error, sizeof error), ren_from);
}
continue;
}
if(!strnicmp(cmd, "RETR ", 5) || !strnicmp(cmd,"SIZE ",5)
|| !strnicmp(cmd, "MDTM ",5) || !strnicmp(cmd, "DELE ",5)) {
p=cmd+5;
SKIP_WHITESPACE(p);
if(!strnicmp(p,LOCAL_FSYS_DIR,strlen(LOCAL_FSYS_DIR)))
p+=strlen(LOCAL_FSYS_DIR); /* already mounted */
if(p[1]==':') /* drive specified */
else if(*p=='/') /* absolute, current drive */
SAFEPRINTF2(fname,"%s%s",root_dir(local_dir),p+1);
SAFEPRINTF2(fname,"%s%s",local_dir,p);
lprintf(LOG_WARNING,"%04d <%s> !File not found: %s",sock,user.alias,fname);
continue;
}
if(!strnicmp(cmd,"SIZE ",5)) {
continue;
}
if(!strnicmp(cmd,"MDTM ",5)) {
t=fdate(fname);
if(gmtime_r(&t,&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);
continue;
}
if(!strnicmp(cmd,"DELE ",5)) {
if(ftp_remove(sock, __LINE__, fname, user.alias, LOG_ERR) == true) {
sockprintf(sock,sess,"250 \"%s\" removed successfully.",fname);
lprintf(LOG_NOTICE,"%04d <%s> deleted file: %s",sock,user.alias,fname);
sockprintf(sock,sess,"450 Error %d removing file: %s", errno, fname);
lprintf(LOG_WARNING,"%04d <%s> !ERROR %d (%s) deleting file: %s"
,sock, user.alias, errno, safe_strerror(errno, error, sizeof error), fname);
}
continue;
}
/* RETR */
lprintf(LOG_INFO,"%04d <%s> downloading: %s (%"PRIuOFF" bytes) in %s mode"
,sock,user.alias,fname,flength(fname)
,mode);
sockprintf(sock,sess,"150 Opening BINARY mode data connection for file transfer.");
filexfer(&data_addr,sock,sess,pasv_sock,pasv_sess,&data_sock,&data_sess,fname,filepos
,&transfer_inprogress,&transfer_aborted,FALSE,FALSE
,&lastactive,&user,&client,-1,FALSE,FALSE,FALSE,NULL,protection);
continue;
} /* Local RETR/SIZE/MDTM */
if(!strnicmp(cmd, "STOR ", 5) || !strnicmp(cmd, "APPE ", 5)) {
p=cmd+5;
SKIP_WHITESPACE(p);
if(!strnicmp(p,LOCAL_FSYS_DIR,strlen(LOCAL_FSYS_DIR)))
p+=strlen(LOCAL_FSYS_DIR); /* already mounted */
if(p[1]==':') /* drive specified */
else if(*p=='/') /* absolute, current drive */
SAFEPRINTF2(fname,"%s%s",root_dir(local_dir),p+1);
SAFEPRINTF2(fname,"%s%s",local_dir,p);
lprintf(LOG_INFO,"%04d <%s> uploading: %s in %s mode", sock,user.alias,fname
,mode);
sockprintf(sock,sess,"150 Opening BINARY mode data connection for file transfer.");
filexfer(&data_addr,sock,sess,pasv_sock,pasv_sess,&data_sock,&data_sess,fname,filepos
,&transfer_inprogress,&transfer_aborted,FALSE,FALSE
,&lastactive
,&user
,-1 /* dir */
,TRUE /* uploading */
,FALSE /* credits */
,!strnicmp(cmd,"APPE",4) ? TRUE : FALSE /* append */
,NULL /* desc */
);
filepos=0;
continue;
} /* Local STOR */
}
if (!strnicmp(cmd, "MLS", 3)) {
if (cmd[3] == 'D' || cmd[3] == 'T') {
dir=curdir;
lib=curlib;
l = 0;
if(cmd[4]!=0)
lprintf(LOG_DEBUG,"%04d <%s> MLSx: %s",sock, user.alias, cmd);
/* path specified? */
p=cmd+4;
if (*p == ' ')
p++;
if (parsepath(&p,&user,&client,&lib,&dir) == -1) {
sockprintf(sock,sess, "550 No such path");
continue;
}
if (strchr(p, '/')) {
sockprintf(sock,sess, "550 No such path");
continue;
}
if (cmd[3] == 'T') {
if (cmd[4])
strcpy(mls_path, cmd+5);
else
strcpy(mls_path, p);
}
else {
if (*p) {
sockprintf(sock,sess, "501 Not a directory");
continue;
}
strcpy(mls_path, p);
}
fp = NULL;
if (cmd[3] == 'D') {
if((fp=fopen(ftp_tmpfname(fname,"lst",sock),"w+b"))==NULL) {
lprintf(LOG_ERR,"%04d <%s> !ERROR %d (%s) line %d opening %s"
,sock, user.alias, errno, safe_strerror(errno, error, sizeof error), __LINE__, fname);
sockprintf(sock,sess, "451 Insufficient system storage");
continue;
}
sockprintf(sock,sess,"150 Opening ASCII mode data connection for MLSD.");
}
now=time(NULL);
if(localtime_r(&now,&cur_tm)==NULL)
memset(&cur_tm,0,sizeof(cur_tm));
/* ASCII Index File */
if(startup->options&FTP_OPT_INDEX_FILE && startup->index_file_name[0]
&& (cmd[3] == 'D' || strcmp(startup->index_file_name, mls_fname) == 0)) {
if (cmd[3] == 'T')
sockprintf(sock,sess, "250- Listing %s", startup->index_file_name);
get_owner_name(NULL, str, sizeof str);
send_mlsx_entry(fp, sock, sess, mlsx_feats, "file", "r", UINT64_MAX, 0, str, NULL, 0, cmd[3] == 'T' ? mls_path : startup->index_file_name);
l++;
}
if(lib<0) { /* Root dir */
if (cmd[3] == 'T' && !*mls_fname) {
sockprintf(sock,sess, "250- Listing root");
get_owner_name(NULL, str, sizeof str);
strcpy(aliaspath, "/");
send_mlsx_entry(fp, sock, sess, mlsx_feats, "dir", (startup->options&FTP_OPT_ALLOW_QWK) ? "elc" : "el", UINT64_MAX, 0, str, NULL, 0, aliaspath);
l++;
/* RFC 3659: "if no object is named ... MLST to send a one-line response, describing the current directory itself" */
else {
send_mlsx_entry(fp, sock, sess, mlsx_feats, "cdir", (startup->options&FTP_OPT_ALLOW_QWK) ? "elc" : "el", UINT64_MAX, 0, str, NULL, 0, "/");
lprintf(LOG_INFO,"%04d <%s> %s listing: root in %s mode", sock, user.alias, cmd, mode);
/* QWK Packet */
if(startup->options&FTP_OPT_ALLOW_QWK) {
SAFEPRINTF(str,"%s.qwk",scfg.sys_id);
if (cmd[3] == 'D' || strcmp(str, mls_fname) == 0) {
if (cmd[3] == 'T')
sockprintf(sock,sess, "250- Listing %s", str);
get_owner_name(NULL, owner, sizeof owner);
send_mlsx_entry(fp, sock, sess, mlsx_feats, "file", "r", flength(qwkfile), fdate(qwkfile), owner, NULL, 0, cmd[3] == 'T' ? mls_path : str);
l++;
}
}
/* 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);
tp=np; /* terminator pointer */
FIND_WHITESPACE(tp);
if(*tp) *tp=0;
dp=tp+1; /* description pointer */
SKIP_WHITESPACE(dp);
truncsp(dp);
if(stricmp(dp,BBS_HIDDEN_ALIAS)==0)
continue;
/* Virtual Path? */
aliaspath[0]=0;
if(!strnicmp(np,BBS_VIRTUAL_PATH,strlen(BBS_VIRTUAL_PATH))) {
if((dir=getdir_from_vpath(&scfg, np+strlen(BBS_VIRTUAL_PATH), &user, &client, true))<0) {
lprintf(LOG_WARNING,"%04d <%s> !Invalid virtual path:%s",sock,user.alias,np);
continue; /* No access or invalid virtual path */
}
tp=strrchr(np,'/');
if(tp==NULL)
continue;
tp++;
if(*tp) {
SAFEPRINTF2(aliasfile,"%s%s",scfg.dir[dir]->path,tp);
np=aliasfile;
SAFEPRINTF3(aliaspath,"/%s/%s/%s", scfg.lib[scfg.dir[dir]->lib]->vdir, scfg.dir[dir]->vdir, tp);
}
else {
alias_dir=TRUE;
SAFEPRINTF2(aliaspath,"/%s/%s", scfg.lib[scfg.dir[dir]->lib]->vdir, scfg.dir[dir]->vdir);
}
if(!alias_dir && !fexist(np)) {
lprintf(LOG_WARNING,"%04d <%s> !Missing aliased file: %s",sock, user.alias, np);
continue;
}
get_unique(aliaspath, uniq);
if (cmd[3] == 'D') {
if (alias_dir==TRUE)
send_mlsx_entry(fp, sock, sess, mlsx_feats, "dir", "el", UINT64_MAX, /* modify_date: */0, /* owner: */scfg.lib[scfg.dir[dir]->lib]->vdir, uniq, 0, p);
else
send_mlsx_entry(fp, sock, sess, mlsx_feats, "file", "r", (uint64_t)flength(np), fdate(np), get_owner_name(NULL, owner, sizeof owner), uniq, 0, p);
if(strcmp(mls_fname, p) != 0)
continue;
if (alias_dir==TRUE)
send_mlsx_entry(fp, sock, sess, mlsx_feats, "dir", "el", UINT64_MAX, /* modify_date: */0, /* owner: */scfg.lib[scfg.dir[dir]->lib]->vdir, uniq, 0, aliaspath[0] ? aliaspath : mls_path);
else
send_mlsx_entry(fp, sock, sess, mlsx_feats, "file", "r", (uint64_t)flength(np), fdate(np), get_owner_name(NULL, owner, sizeof owner), uniq, 0, mls_path);
}
}
}
/* Library folders */
for(i=0;i<scfg.total_libs;i++) {
if(!chk_ar(&scfg,scfg.lib[i]->ar,&user,&client))
continue;
if (cmd[3] != 'D' && strcmp(scfg.lib[i]->vdir, mls_fname) != 0)
continue;
if (cmd[3] == 'T')
sockprintf(sock,sess, "250- Listing %s", scfg.lib[i]->vdir);
get_libperm(scfg.lib[i], &user, &client, permstr);
get_owner_name(NULL, str, sizeof str);
send_mlsx_entry(fp, sock, sess, mlsx_feats, "dir", permstr, UINT64_MAX, 0, str, NULL, 0, cmd[3] == 'T' ? mls_path : scfg.lib[i]->vdir);
l++;
}
} else if(dir<0) {
if (cmd[3] == 'T' && !*mls_fname) {
sockprintf(sock,sess, "250- Listing %s", scfg.lib[lib]->vdir);
get_owner_name(NULL, str, sizeof str);
SAFEPRINTF(aliaspath, "/%s", scfg.lib[lib]->vdir);
send_mlsx_entry(fp, sock, sess, mlsx_feats, "dir", "el", UINT64_MAX, 0, str, NULL, 0, aliaspath);
l++;
}
get_owner_name(NULL, str, sizeof str);
send_mlsx_entry(fp, sock, sess, mlsx_feats, "pdir", (startup->options&FTP_OPT_ALLOW_QWK) ? "elc" : "el", UINT64_MAX, 0, str, NULL, 0, "/");
SAFEPRINTF(aliaspath, "/%s", scfg.lib[lib]->vdir);
send_mlsx_entry(fp, sock, sess, mlsx_feats, "cdir", (startup->options&FTP_OPT_ALLOW_QWK) ? "elc" : "el", UINT64_MAX, 0, str, NULL, 0, aliaspath);
lprintf(LOG_INFO,"%04d <%s> %s listing: %s library in %s mode"
,sock, user.alias, cmd, 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
&& !chk_ar(&scfg,scfg.dir[i]->ar,&user,&client))
continue;
if (cmd[3] != 'D' && strcmp(scfg.dir[i]->vdir, mls_fname) != 0)
continue;
if (cmd[3] == 'T')
sockprintf(sock,sess, "250- Listing %s", scfg.dir[i]->vdir);
get_dirperm(scfg.lib[lib], scfg.dir[i], &user, &client, permstr);
get_owner_name(NULL, str, sizeof str);
SAFEPRINTF2(aliaspath, "/%s/%s", scfg.lib[lib]->vdir, scfg.dir[i]->vdir);
get_unique(aliaspath, uniq);
send_mlsx_entry(fp, sock, sess, mlsx_feats, "dir", permstr, UINT64_MAX, 0, str, uniq, 0, cmd[3] == 'T' ? mls_path : scfg.dir[i]->vdir);
l++;
}
} else if(chk_ar(&scfg,scfg.dir[dir]->ar,&user,&client)) {
lprintf(LOG_INFO,"%04d <%s> %s listing: /%s/%s directory in %s mode"
,sock, user.alias, cmd, scfg.lib[lib]->vdir, scfg.dir[dir]->vdir,mode);
if (cmd[3] == 'T' && !*mls_fname) {
sockprintf(sock,sess, "250- Listing %s/%s",scfg.lib[lib]->vdir,scfg.dir[dir]->vdir);
get_owner_name(NULL, str, sizeof str);
SAFEPRINTF2(aliaspath, "/%s/%s", scfg.lib[lib]->vdir, scfg.dir[dir]->vdir);
get_unique(aliaspath, uniq);
send_mlsx_entry(fp, sock, sess, mlsx_feats, "dir", (startup->options&FTP_OPT_ALLOW_QWK) ? "elc" : "el", UINT64_MAX, 0, str, uniq, 0, aliaspath);
l++;
}
if (cmd[3] == 'D') {
get_libperm(scfg.lib[lib], &user, &client, permstr);
get_owner_name(NULL, str, sizeof str);
SAFEPRINTF(aliaspath, "/%s", scfg.lib[lib]->vdir);
send_mlsx_entry(fp, sock, sess, mlsx_feats, "pdir", permstr, UINT64_MAX, 0, str, NULL, 0, aliaspath);
SAFEPRINTF2(aliaspath, "/%s/%s", scfg.lib[lib]->vdir, scfg.dir[dir]->vdir);
get_unique(aliaspath, uniq);
send_mlsx_entry(fp, sock, sess, mlsx_feats, "cdir", permstr, UINT64_MAX, 0, str, NULL, 0, aliaspath);
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];
if (cmd[3] != 'D' && strcmp(f->name, mls_fname) != 0)
continue;
if (cmd[3] == 'T')
sockprintf(sock,sess, "250- Listing %s", p);
get_fileperm(scfg.lib[lib], scfg.dir[dir], &user, &client, f, permstr);
get_owner_name(f, str, sizeof str);
SAFEPRINTF3(aliaspath, "/%s/%s/%s", scfg.lib[lib]->vdir, scfg.dir[dir]->vdir, f->name);
get_unique(aliaspath, uniq);
f->size = f->cost;
f->time = 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;
f->time = st.st_mtime;
f->hdr.when_imported.time = (uint32_t)st.st_ctime;
}
send_mlsx_entry(fp, sock, sess, mlsx_feats, "file", permstr, f->size, f->time, str, uniq, f->hdr.when_imported.time, cmd[3] == 'T' ? mls_path : f->name);
l++;
}
lprintf(LOG_INFO, "%04d <%s> %s listing (%ld bytes) of /%s/%s (%lu files) created in %" PRId64 " seconds"
,sock, user.alias, cmd, ftell(fp), scfg.lib[lib]->vdir, scfg.dir[dir]->vdir
,(ulong)file_count, (int64_t)(time(NULL) - start));
freefiles(file_list, file_count);
smb_close(&smb);
lprintf(LOG_INFO,"%04d <%s> %s listing: /%s/%s directory in %s mode (empty - no access)"
,sock, user.alias, cmd, scfg.lib[lib]->vdir, scfg.dir[dir]->vdir, mode);
if (cmd[3] == 'D') {
fclose(fp);
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);
}
else {
if (l==0)
sockprintf(sock,sess, "550 No such path");
else
sockprintf(sock, sess, "250 End");
}
continue;
}
}
if(!strnicmp(cmd, "LIST", 4) || !strnicmp(cmd, "NLST", 4)) {
dir=curdir;
lib=curlib;
if(cmd[4]!=0)
lprintf(LOG_DEBUG,"%04d <%s> LIST/NLST: %s", sock, user.alias, cmd);
/* path specified? */
p=cmd+4;
SKIP_WHITESPACE(p);
if(*p=='-') { /* -Letc */
FIND_WHITESPACE(p);
SKIP_WHITESPACE(p);
if((fp=fopen(ftp_tmpfname(fname,"lst",sock),"w+b"))==NULL) {
lprintf(LOG_ERR,"%04d <%s> !ERROR %d (%s) line %d opening %s"
,sock, user.alias, errno, safe_strerror(errno, error, sizeof error), __LINE__, fname);
sockprintf(sock,sess,"150 Opening ASCII mode data connection for /bin/ls.");
if (parsepath(&p,&user,&client,&lib,&dir) == -1) {
/* Empty list */
fclose(fp);
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);
continue;
}
filespec=p;
if(*filespec==0)
filespec="*";
if(!strnicmp(cmd, "LIST", 4))
detail=TRUE;
else
detail=FALSE;
now=time(NULL);
if(localtime_r(&now,&cur_tm)==NULL)
memset(&cur_tm,0,sizeof(cur_tm));
if(startup->options&FTP_OPT_INDEX_FILE && startup->index_file_name[0]
&& wildmatchi(startup->index_file_name, filespec, FALSE)) {
fprintf(fp,"-r--r--r-- 1 %-*s %-8s %9ld %s %2d %02d:%02d %s\r\n"
,lib<0 ? scfg.sys_id : dir<0
? scfg.lib[lib]->vdir : scfg.dir[dir]->vdir
,ftp_mon[cur_tm.tm_mon],cur_tm.tm_mday,cur_tm.tm_hour,cur_tm.tm_min
,startup->index_file_name);
else
fprintf(fp,"%s\r\n",startup->index_file_name);
lprintf(LOG_INFO,"%04d <%s> %slisting: root in %s mode", sock, user.alias, detail ? "detailed ":"", mode);
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=(ulong)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);
}

rswindell
committed
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))