diff --git a/src/sbbs3/ftpsrvr.c b/src/sbbs3/ftpsrvr.c index 8ec44a27c1f574b3fd33a1d78586740ed9056e53..bb0dfba0ff7bb4d770f832477b4c9ad0874bed97 100644 --- a/src/sbbs3/ftpsrvr.c +++ b/src/sbbs3/ftpsrvr.c @@ -56,6 +56,7 @@ #include "ssl.h" #include "cryptlib.h" #include "xpprintf.h" // vasprintf +#include "md5.h" /* Constants */ @@ -84,6 +85,7 @@ #define MLSX_SIZE (1<<2) #define MLSX_MODIFY (1<<3) #define MLSX_OWNER (1<<4) +#define MLSX_UNIQUE (1<<5) static ftp_startup_t* startup=NULL; static scfg_t scfg; @@ -2566,7 +2568,19 @@ static BOOL send_mlsx(FILE *fp, SOCKET sock, CRYPT_SESSION sess, const char *for return TRUE; } -static BOOL send_mlsx_entry(FILE *fp, SOCKET sock, CRYPT_SESSION sess, unsigned feats, const char *type, const char *perm, uint64_t size, time_t modify, const char *owner, const char *fname) +static char *get_unique(const char *path, char *uniq) +{ + BYTE digest[MD5_DIGEST_SIZE]; + + if (path == NULL) + return NULL; + + MD5_calc(digest, path, strlen(path)); + MD5_hex((BYTE*)uniq, digest); + return uniq; +} + +static BOOL send_mlsx_entry(FILE *fp, SOCKET sock, CRYPT_SESSION sess, unsigned feats, const char *type, const char *perm, uint64_t size, time_t modify, const char *owner, const char *unique, const char *fname) { char line[1024]; char *end; @@ -2587,6 +2601,8 @@ static BOOL send_mlsx_entry(FILE *fp, SOCKET sock, CRYPT_SESSION sess, unsigned mtime.tm_year+1900, mtime.tm_mon+1, mtime.tm_mday, mtime.tm_hour, mtime.tm_min, mtime.tm_sec); } + if (unique != NULL && (feats & MLSX_UNIQUE)) + end += sprintf(end, "Unique=%s;", unique); // Owner can contain percents, so let send_mlsx() deal with it if (owner != NULL && (feats & MLSX_OWNER)) { strcat(end, "UNIX.ownername=%s;"); @@ -2644,7 +2660,7 @@ static BOOL write_local_mlsx(FILE *fp, SOCKET sock, CRYPT_SESSION sess, unsigned } } *p=0; - return send_mlsx_entry(fp, sock, sess, feats, type, permstr, (uint64_t)flength(path), fdate(path), NULL, path); + return send_mlsx_entry(fp, sock, sess, feats, type, permstr, (uint64_t)flength(path), fdate(path), NULL, NULL, path); } /* @@ -2838,9 +2854,10 @@ static void get_owner_name(file_t *file, char *namestr) static void ctrl_thread(void* arg) { - unsigned mlsx_feats = (MLSX_TYPE | MLSX_PERM | MLSX_SIZE | MLSX_MODIFY | MLSX_OWNER); + unsigned mlsx_feats = (MLSX_TYPE | MLSX_PERM | MLSX_SIZE | MLSX_MODIFY | MLSX_OWNER | MLSX_UNIQUE); char buf[512]; char str[128]; + char uniq[33]; char* cmd; char* p; char* np; @@ -2854,6 +2871,7 @@ static void ctrl_thread(void* arg) char fname[MAX_PATH+1]; char qwkfile[MAX_PATH+1]; char aliasfile[MAX_PATH+1]; + char aliaspath[MAX_PATH+1]; char mls_path[MAX_PATH+1]; char *mls_fname; char permstr[11]; @@ -3137,7 +3155,8 @@ static void ctrl_thread(void* arg) (mlsx_feats & MLSX_PERM) ? "*" : "", (mlsx_feats & MLSX_SIZE) ? "*" : "", (mlsx_feats & MLSX_MODIFY) ? "*" : "", - (mlsx_feats & MLSX_OWNER) ? "*" : "" + (mlsx_feats & MLSX_OWNER) ? "*" : "", + (mlsx_feats & MLSX_UNIQUE) ? "*" : "" ); sockprintf(sock,sess," TVFS"); sockprintf(sock,sess,"211 End"); @@ -3165,12 +3184,15 @@ static void ctrl_thread(void* arg) mlsx_feats |= MLSX_MODIFY; if (strstr(cmd, "UNIX.OWNERNAME;")) mlsx_feats |= MLSX_OWNER; + if (strstr(cmd, "UNIQUE;")) + mlsx_feats |= MLSX_OWNER; sockprintf(sock,sess,"200 %s%s%s%s%s", (mlsx_feats & MLSX_TYPE) ? "Type;" : "", (mlsx_feats & MLSX_PERM) ? "Perm;" : "", (mlsx_feats & MLSX_SIZE) ? "Size;" : "", (mlsx_feats & MLSX_MODIFY) ? "Modify;" : "", - (mlsx_feats & MLSX_OWNER) ? "UNIX.ownername;" : "" + (mlsx_feats & MLSX_OWNER) ? "UNIX.ownername;" : "", + (mlsx_feats & MLSX_UNIQUE) ? "Unique;" : "" ); continue; } @@ -4263,7 +4285,7 @@ static void ctrl_thread(void* arg) sockprintf(sock,sess, "550 No such path"); continue; } - + if (strchr(p, '/')) { sockprintf(sock,sess, "550 No such path"); continue; @@ -4302,7 +4324,7 @@ static void ctrl_thread(void* arg) if (cmd[3] == 'T') sockprintf(sock,sess, "250- Listing %s", startup->index_file_name); get_owner_name(NULL, str); - send_mlsx_entry(fp, sock, sess, mlsx_feats, "file", "r", UINT64_MAX, 0, str, cmd[3] == 'T' ? mls_path : startup->index_file_name); + send_mlsx_entry(fp, sock, sess, mlsx_feats, "file", "r", UINT64_MAX, 0, str, NULL, cmd[3] == 'T' ? mls_path : startup->index_file_name); l++; } /* HTML Index File */ @@ -4311,7 +4333,7 @@ static void ctrl_thread(void* arg) if (cmd[3] == 'T') sockprintf(sock,sess, "250- Listing %s", startup->html_index_file); get_owner_name(NULL, str); - send_mlsx_entry(fp, sock, sess, mlsx_feats, "file", "r", UINT64_MAX, 0, str, cmd[3] == 'T' ? mls_path : startup->html_index_file); + send_mlsx_entry(fp, sock, sess, mlsx_feats, "file", "r", UINT64_MAX, 0, str, NULL, cmd[3] == 'T' ? mls_path : startup->html_index_file); l++; } @@ -4319,7 +4341,8 @@ static void ctrl_thread(void* arg) if (cmd[3] == 'T' && !*mls_fname) { sockprintf(sock,sess, "250- Listing root"); get_owner_name(NULL, str); - send_mlsx_entry(fp, sock, sess, mlsx_feats, "dir", (startup->options&FTP_OPT_ALLOW_QWK) ? "elc" : "el", UINT64_MAX, 0, str, mls_path); + strcpy(aliaspath, "/"); + send_mlsx_entry(fp, sock, sess, mlsx_feats, "dir", (startup->options&FTP_OPT_ALLOW_QWK) ? "elc" : "el", UINT64_MAX, 0, str, NULL, aliaspath); l++; } lprintf(LOG_INFO,"%04d %s listing: root in %s mode",sock,user.alias, mode); @@ -4331,7 +4354,7 @@ static void ctrl_thread(void* arg) if (cmd[3] == 'T') sockprintf(sock,sess, "250- Listing %s", str); get_owner_name(NULL, str); - send_mlsx_entry(fp, sock, sess, mlsx_feats, "file", "r", UINT64_MAX, 0, str, cmd[3] == 'T' ? mls_path : str); + send_mlsx_entry(fp, sock, sess, mlsx_feats, "file", "r", UINT64_MAX, 0, str, NULL, cmd[3] == 'T' ? mls_path : str); l++; } } @@ -4371,6 +4394,7 @@ static void ctrl_thread(void* arg) continue; /* Virtual Path? */ + aliaspath[0]=0; if(!strnicmp(np,BBS_VIRTUAL_PATH,strlen(BBS_VIRTUAL_PATH))) { if((dir=getdir(np+strlen(BBS_VIRTUAL_PATH),&user,&client))<0) { lprintf(LOG_WARNING,"0000 !Invalid virtual path (%s) for %s",np,user.alias); @@ -4383,9 +4407,12 @@ static void ctrl_thread(void* arg) if(*tp) { SAFEPRINTF2(aliasfile,"%s%s",scfg.dir[dir]->path,tp); np=aliasfile; + SAFEPRINTF3(aliaspath,"/%s/%s/%s", scfg.lib[scfg.dir[dir]->lib]->sname, scfg.dir[dir]->code_suffix, tp); } - else + else { alias_dir=TRUE; + SAFEPRINTF2(aliaspath,"/%s/%s", scfg.lib[scfg.dir[dir]->lib]->sname, scfg.dir[dir]->code_suffix); + } } if(!alias_dir && !fexist(np)) { @@ -4396,11 +4423,18 @@ static void ctrl_thread(void* arg) if(cmd[3] == 'D' || strcmp(startup->html_index_file, mls_fname) == 0) { if (cmd[3] == 'T') sockprintf(sock,sess, "250- Listing %s", p); - if (alias_dir==TRUE) { - send_mlsx_entry(fp, sock, sess, mlsx_feats, "dir", "el", UINT64_MAX, 0, NULL, cmd[3] == 'T' ? mls_path : p); + get_unique(aliaspath, uniq); + if (cmd[3] == 'D') { + if (alias_dir==TRUE) + send_mlsx_entry(fp, sock, sess, mlsx_feats, "dir", "el", UINT64_MAX, 0, NULL, uniq, p); + else + send_mlsx_entry(fp, sock, sess, mlsx_feats, "file", "r", (uint64_t)flength(np), fdate(np), NULL, uniq, p); } else { - send_mlsx_entry(fp, sock, sess, mlsx_feats, "file", "r", (uint64_t)flength(np), fdate(np), NULL, cmd[3] == 'T' ? mls_path : p); + if (alias_dir==TRUE) + send_mlsx_entry(fp, sock, sess, mlsx_feats, "dir", "el", UINT64_MAX, 0, NULL, uniq, aliaspath[0] ? aliaspath : mls_path); + else + send_mlsx_entry(fp, sock, sess, mlsx_feats, "file", "r", (uint64_t)flength(np), fdate(np), NULL, uniq, mls_path); } l++; } @@ -4419,19 +4453,20 @@ static void ctrl_thread(void* arg) sockprintf(sock,sess, "250- Listing %s", scfg.lib[i]->sname); get_libperm(scfg.lib[i], &user, &client, permstr); get_owner_name(NULL, str); - send_mlsx_entry(fp, sock, sess, mlsx_feats, "dir", permstr, UINT64_MAX, 0, str, cmd[3] == 'T' ? mls_path : scfg.lib[i]->sname); + send_mlsx_entry(fp, sock, sess, mlsx_feats, "dir", permstr, UINT64_MAX, 0, str, NULL, cmd[3] == 'T' ? mls_path : scfg.lib[i]->sname); l++; } } else if(dir<0) { if (cmd[3] == 'T' && !*mls_fname) { sockprintf(sock,sess, "250- Listing %s", scfg.lib[lib]->sname); get_owner_name(NULL, str); - send_mlsx_entry(fp, sock, sess, mlsx_feats, "dir", "el", UINT64_MAX, 0, str, mls_path); + SAFEPRINTF(aliaspath, "/%s", scfg.lib[lib]->sname); + send_mlsx_entry(fp, sock, sess, mlsx_feats, "dir", "el", UINT64_MAX, 0, str, NULL, aliaspath); l++; } if (cmd[3] == 'D') { get_owner_name(NULL, str); - send_mlsx_entry(fp, sock, sess, mlsx_feats, "pdir", (startup->options&FTP_OPT_ALLOW_QWK) ? "elc" : "el", UINT64_MAX, 0, str, ".."); + send_mlsx_entry(fp, sock, sess, mlsx_feats, "pdir", (startup->options&FTP_OPT_ALLOW_QWK) ? "elc" : "el", UINT64_MAX, 0, str, NULL, ".."); } lprintf(LOG_INFO,"%04d %s listing: %s library in %s mode" ,sock,user.alias,scfg.lib[lib]->sname,mode); @@ -4447,7 +4482,9 @@ static void ctrl_thread(void* arg) sockprintf(sock,sess, "250- Listing %s", scfg.dir[i]->code_suffix); get_dirperm(scfg.lib[lib], scfg.dir[i], &user, &client, permstr); get_owner_name(NULL, str); - send_mlsx_entry(fp, sock, sess, mlsx_feats, "dir", permstr, UINT64_MAX, 0, str, cmd[3] == 'T' ? mls_path : scfg.dir[i]->code_suffix); + SAFEPRINTF2(aliaspath, "/%s/%s", scfg.lib[lib]->sname, scfg.dir[i]->code_suffix); + get_unique(aliaspath, uniq); + send_mlsx_entry(fp, sock, sess, mlsx_feats, "dir", permstr, UINT64_MAX, 0, str, uniq, cmd[3] == 'T' ? mls_path : scfg.dir[i]->code_suffix); l++; } } else if(chk_ar(&scfg,scfg.dir[dir]->ar,&user,&client)) { @@ -4457,13 +4494,15 @@ static void ctrl_thread(void* arg) if (cmd[3] == 'T' && !*mls_fname) { sockprintf(sock,sess, "250- Listing %s/%s",scfg.lib[lib]->sname,scfg.dir[dir]->code_suffix); get_owner_name(NULL, str); - send_mlsx_entry(fp, sock, sess, mlsx_feats, "dir", (startup->options&FTP_OPT_ALLOW_QWK) ? "elc" : "el", UINT64_MAX, 0, str, mls_path); + SAFEPRINTF2(aliaspath, "/%s/%s", scfg.lib[lib]->sname, scfg.dir[dir]->code_suffix); + 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, aliaspath); l++; } if (cmd[3] == 'D') { get_libperm(scfg.lib[lib], &user, &client, permstr); get_owner_name(NULL, str); - send_mlsx_entry(fp, sock, sess, mlsx_feats, "pdir", permstr, UINT64_MAX, 0, str, ".."); + send_mlsx_entry(fp, sock, sess, mlsx_feats, "pdir", permstr, UINT64_MAX, 0, str, NULL, ".."); } SAFEPRINTF2(path,"%s%s",scfg.dir[dir]->path,"*"); @@ -4488,7 +4527,9 @@ static void ctrl_thread(void* arg) sockprintf(sock,sess, "250- Listing %s", p); get_fileperm(scfg.lib[lib], scfg.dir[dir], &user, &client, &f, permstr); get_owner_name(&f, str); - send_mlsx_entry(fp, sock, sess, mlsx_feats, "file", permstr, (uint64_t)flength(g.gl_pathv[i]), fdate(g.gl_pathv[i]), str, cmd[3] == 'T' ? mls_path : getfname(g.gl_pathv[i])); + SAFEPRINTF3(aliaspath, "/%s/%s/%s", scfg.lib[lib]->sname, scfg.dir[dir]->code_suffix, getfname(g.gl_pathv[i])); + get_unique(aliaspath, uniq); + send_mlsx_entry(fp, sock, sess, mlsx_feats, "file", permstr, (uint64_t)flength(g.gl_pathv[i]), fdate(g.gl_pathv[i]), str, uniq, cmd[3] == 'T' ? mls_path : getfname(g.gl_pathv[i])); l++; } globfree(&g);