diff --git a/src/sbbs3/ftpsrvr.c b/src/sbbs3/ftpsrvr.c
index 1fbf6ffbc493abf6fd0fa54c6307e85537fbe097..7f68e802acc970bbc8896fabf5e812056a30e168 100644
--- a/src/sbbs3/ftpsrvr.c
+++ b/src/sbbs3/ftpsrvr.c
@@ -2208,13 +2208,14 @@ char* dotname(char* in, char* out)
 	return(out);
 }
 
-void parsepath(char** pp, user_t* user, client_t* client, int* curlib, int* curdir)
+static int parsepath(char** pp, user_t* user, client_t* client, int* curlib, int* curdir)
 {
 	char*	p;
 	char*	tp;
 	char	path[MAX_PATH+1];
 	int		dir=*curdir;
 	int		lib=*curlib;
+	int	ret = 0;
 
 	SAFECOPY(path,*pp);
 	p=path;
@@ -2232,6 +2233,8 @@ void parsepath(char** pp, user_t* user, client_t* client, int* curlib, int* curd
 			dir=-1;
 		else if(lib>=0)
 			lib=-1;
+		else
+			ret = -1;
 		if(*p=='/')
 			p++;
 	}
@@ -2239,7 +2242,7 @@ void parsepath(char** pp, user_t* user, client_t* client, int* curlib, int* curd
 	if(*p==0) {
 		*curlib=lib;
 		*curdir=dir;
-		return;
+		return ret;
 	}
 
 	if(lib<0) { /* root */
@@ -2256,14 +2259,14 @@ void parsepath(char** pp, user_t* user, client_t* client, int* curlib, int* curd
 		}
 		if(lib>=scfg.total_libs) { /* not found */
 			*curlib=-1;
-			return;
+			return -1;
 		}
 		*curlib=lib;
 
 		if(*(tp)==0) {
 			*curdir=-1;
 			*pp+=tp-path;	/* skip "lib" or "lib/" */
-			return;
+			return ret;
 		}
 
 		p=tp;
@@ -2288,12 +2291,13 @@ void parsepath(char** pp, user_t* user, client_t* client, int* curlib, int* curd
 
 	if(dir>=scfg.total_dirs) {  /* not found */
 		*pp+=p-path;			/* skip /lib/filespec */
-		return;
+		return -1;
 	}
 
 	*curdir=dir;
 
 	*pp+=tp-path;	/* skip "lib/dir/" */
+	return ret;
 }
 
 static BOOL ftpalias(char* fullalias, char* filename, user_t* user, client_t* client, int* curdir)
@@ -2482,6 +2486,239 @@ static char* ftp_tmpfname(char* fname, char* ext, SOCKET sock)
 	return(fname);
 }
 
+static BOOL send_mlsx(FILE *fp, SOCKET sock, CRYPT_SESSION sess, const char *format, ...)
+{
+	va_list va;
+	char *str;
+
+	if (fp == NULL && sock == INVALID_SOCKET)
+		return FALSE;
+	va_start(va, format);
+	if (vasprintf(&str, format, va) == -1)
+		return FALSE;
+	if (fp != NULL)
+		fprintf(fp, "%s\r\n", str);
+	else
+		sockprintf(sock, sess, " %s", str);
+	free(str);
+	return TRUE;
+}
+
+static BOOL write_local_mlsx(FILE *fp, SOCKET sock, CRYPT_SESSION sess, const char *path)
+{
+	const char *type;
+	char permstr[11];
+	char *p;
+	BOOL is_file = FALSE;
+
+	if (!strcmp(path, "."))
+		type="cdir";
+	else if (!strcmp(path, ".."))
+		type="pdir";
+	else if (isdir(path))
+		type="dir";
+	else {
+		is_file = TRUE;
+		type="file";
+	}
+	// TODO: Check for deletability 'd'
+	// TODO: Check for renamability 'f'
+	p = permstr;
+	if (is_file) {
+		if (access(path, W_OK) == 0) {
+			// Can append ('a') and write ('w')
+			*(p++)='a';
+			*(p++)='w';
+		}
+		if (access(path, R_OK) == 0) {
+			// Can read ('r')
+			*(p++)='r';
+		}
+	}
+	else {
+		// TODO: Check these on Windows...
+		if (access(path, W_OK) == 0) {
+			// Can create files ('c'), directories ('m') and delete files ('p')
+			*(p++)='c';
+			*(p++)='m';
+			*(p++)='p';
+		}
+		if (access(path, R_OK) == 0) {
+			// Can change to the directory ('e'), and list files ('l')
+			*(p++)='e';
+			*(p++)='l';
+		}
+	}
+	*p=0;
+	return send_mlsx(fp, sock, sess, "Type=%s;Perm=%s;Size=%" PRIu64 ";Modify=%" PRIu64 "; %s", type, permstr, (uint64_t)flength(path), (uint64_t)fdate(path), path);
+}
+
+/*
+ * Nobody can do anything but list files and change to dirs.
+ */
+static void get_libperm(lib_t *lib, user_t *user, client_t *client, char *permstr)
+{
+	char *p = permstr;
+
+	if (chk_ar(&scfg,lib->ar,user,client)) {
+		//*(p++) = 'a';	// File may be appended to
+		//*(p++) = 'c';	// Files may be created in dir
+		//*(p++) = 'd';	// Item may be depeted (dir or file)
+		*(p++) = 'e';	// Can change to the dir
+		//*(p++) = 'f';	// Item may be renamed
+		*(p++) = 'l';	// Directory contents can be listed
+		//*(p++) = 'm';	// New subdirectories may be created
+		//*(p++) = 'p';	// Files/Dirs in directory may be deleted
+		//*(p++) = 'r';	// File may be retrieved
+		//*(p++) = 'w';	// File may be overwritten
+	}
+	*p=0;
+}
+
+static BOOL can_upload(lib_t *lib, dir_t *dir, user_t *user, client_t *client)
+{
+	if (!chk_ar(&scfg,lib->ar,user,client))
+		return FALSE;
+	if (user->rest & FLAG('U'))
+		return FALSE;
+	if (dir_op(&scfg, user, client, dir->dirnum))
+		return TRUE;
+	// The rest can only upload if there's room
+	if(dir->maxfiles && getfiles(&scfg,dir->dirnum)>=dir->maxfiles)
+		return FALSE;
+	if (dir->dirnum == scfg.sysop_dir)
+		return TRUE;
+	if (dir->dirnum == scfg.upload_dir)
+		return TRUE;
+	if (chk_ar(&scfg, dir->ul_ar,user,client))
+		return TRUE;
+	if ((user->exempt & FLAG('U')))
+		return TRUE;
+	return FALSE;
+}
+
+static BOOL can_list(lib_t *lib, dir_t *dir, user_t *user, client_t *client)
+{
+	if (!chk_ar(&scfg,lib->ar,user,client))
+		return FALSE;
+	if (dir->dirnum == scfg.sysop_dir)
+		return TRUE;
+	if (dir->dirnum == scfg.upload_dir)
+		return TRUE;
+	if (chk_ar(&scfg, dir->ar, user, client))
+		return TRUE;
+	return FALSE;
+}
+
+static BOOL can_delete_files(lib_t *lib, dir_t *dir, user_t *user, client_t *client)
+{
+	if (!chk_ar(&scfg,lib->ar,user,client))
+		return FALSE;
+	if (user->rest&FLAG('D'))
+		return FALSE;
+	if (!chk_ar(&scfg,dir->ar,user,client))
+		return FALSE;
+	if (dir_op(&scfg,user,client,dir->dirnum))
+		return TRUE;
+	if (user->exempt&FLAG('R'))
+		return TRUE;
+	return FALSE;
+}
+
+static void get_dirperm(lib_t *lib, dir_t *dir, user_t *user, client_t *client, char *permstr)
+{
+	char *p = permstr;
+
+	//*(p++) = 'a';	// File may be appended to
+	if (can_upload(lib, dir, user, client))
+		*(p++) = 'c';	// Files may be created in dir
+	//*(p++) = 'd';	// Item may be depeted (dir or file)
+	if (can_list(lib, dir, user, client)) {
+		*(p++) = 'e';	// Can change to the dir
+		//*(p++) = 'f';	// Item may be renamed
+		*(p++) = 'l';	// Directory contents can be listed
+	}
+	//*(p++) = 'm';	// New subdirectories may be created
+	if (can_delete_files(lib, dir, user, client))
+		*(p++) = 'p';	// Files/Dirs in directory may be deleted
+	//*(p++) = 'r';	// File may be retrieved
+	//*(p++) = 'w';	// File may be overwritten
+	*p=0;
+}
+
+static BOOL can_append(lib_t *lib, dir_t *dir, user_t *user, client_t *client, file_t *file)
+{
+	if (!chk_ar(&scfg,lib->ar,user,client))
+		return FALSE;
+	if (user->rest&FLAG('U'))
+		return FALSE;
+	if (dir->dirnum != scfg.sysop_dir && dir->dirnum != scfg.upload_dir && !chk_ar(&scfg,dir->ar,user,client))
+		return FALSE;
+	if(!dir_op(&scfg,user,client,dir->dirnum) && !(user->exempt&FLAG('U'))) {
+		if(!chk_ar(&scfg,dir->ul_ar,user,client))
+			return FALSE;
+	}
+	if(!getfileixb(&scfg,file) || !getfiledat(&scfg,file))
+		return FALSE;
+	if (stricmp(file->uler,user->alias))
+		return FALSE;
+	// Check credits?
+	return TRUE;
+}
+
+static BOOL can_delete(lib_t *lib, dir_t *dir, user_t *user, client_t *client, file_t *file)
+{
+	if (user->rest&FLAG('D'))
+		return FALSE;
+	if (!chk_ar(&scfg,lib->ar,user,client))
+		return FALSE;
+	if (!chk_ar(&scfg,dir->ar,user,client))
+		return FALSE;
+	if (!dir_op(&scfg, user, client, dir->dirnum))
+		return FALSE;
+	if (!(user->exempt&FLAG('R')))
+		return FALSE;
+	if(!getfileixb(&scfg,file) && !(startup->options&FTP_OPT_DIR_FILES) && !(dir->misc&DIR_FILES))
+		return FALSE;
+	return TRUE;
+}
+
+static BOOL can_download(lib_t *lib, dir_t *dir, user_t *user, client_t *client, file_t *file)
+{
+	if (user->rest&FLAG('D'))
+		return FALSE;
+	if (!chk_ar(&scfg,lib->ar,user,client))
+		return FALSE;
+	if (!chk_ar(&scfg,dir->ar,user,client))
+		return FALSE;
+	if (!chk_ar(&scfg,dir->dl_ar,user,client))
+		return FALSE;
+	if(!getfileixb(&scfg,file) && !(startup->options&FTP_OPT_DIR_FILES) && !(dir->misc&DIR_FILES))
+		return FALSE;
+	// TODO: Verify credits
+	return TRUE;
+}
+
+static void get_fileperm(lib_t *lib, dir_t *dir, user_t *user, client_t *client, file_t *file, char *permstr)
+{
+	char *p = permstr;
+
+	if (can_append(lib, dir, user, client, file))
+		*(p++) = 'a';	// File may be appended to
+	//*(p++) = 'c';	// Files may be created in dir
+	if (can_delete(lib, dir, user, client, file))
+		*(p++) = 'd';	// Item may be depeted (dir or file)
+	//*(p++) = 'e';	// Can change to the dir
+	//*(p++) = 'f';	// Item may be renamed
+	//*(p++) = 'l';	// Directory contents can be listed
+	//*(p++) = 'm';	// New subdirectories may be created
+	//*(p++) = 'p';	// Files/Dirs in directory may be deleted
+	if (can_download(lib, dir, user, client, file))
+		*(p++) = 'r';	// File may be retrieved
+	//*(p++) = 'w';	// File may be overwritten
+	*p = 0;
+}
+
 static void ctrl_thread(void* arg)
 {
 	char		buf[512];
@@ -2499,6 +2736,9 @@ static void ctrl_thread(void* arg)
 	char		fname[MAX_PATH+1];
 	char		qwkfile[MAX_PATH+1];
 	char		aliasfile[MAX_PATH+1];
+	char		mls_path[MAX_PATH+1];
+	char		*mls_fname;
+	char		permstr[11];
 	char		aliasline[512];
 	char		desc[501]="";
 	char		sys_pass[128];
@@ -2761,6 +3001,7 @@ static void ctrl_thread(void* arg)
 			sockprintf(sock,sess," SIZE    MDTM    RETR    STOR    REST    ALLO    ABOR    SYST");
 			sockprintf(sock,sess," TYPE    STRU    MODE    SITE    RNFR*   RNTO*   DELE*   DESC#");
 			sockprintf(sock,sess," FEAT#   OPTS#   EPRT    EPSV    AUTH#   PBSZ#   PROT#   CCC#");
+			sockprintf(sock,sess," MLSD#");
 			sockprintf(sock,sess,"214 Direct comments to sysop@%s.",scfg.sys_inetaddr);
 			continue;
 		}
@@ -2773,6 +3014,7 @@ static void ctrl_thread(void* arg)
 			sockprintf(sock,sess," AUTH TLS");
 			sockprintf(sock,sess," PBSZ");
 			sockprintf(sock,sess," PROT");
+			sockprintf(sock,sess," MLST");
 			sockprintf(sock,sess,"211 End");
 			continue;
 		}
@@ -3495,17 +3737,85 @@ static void ctrl_thread(void* arg)
 				&& local_dir[strlen(local_dir)-1]!='/')
 				strcat(local_dir,"/");
 
-			if(!strnicmp(cmd, "LIST", 4) || !strnicmp(cmd, "NLST", 4)) {	
-				if((fp=fopen(ftp_tmpfname(fname,"lst",sock),"w+b"))==NULL) {
-					lprintf(LOG_ERR,"%04d !ERROR %d opening %s",sock,errno,fname);
-					sockprintf(sock,sess, "451 Insufficient system storage");
+			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 !ERROR %d opening %s",sock,errno,fname);
+							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: %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, path);
+						sockprintf(sock, sess, "250 End");
+					}
+					else {
+						glob(path,0,NULL,&g);
+						for(i=0;i<(int)g.gl_pathc;i++)
+							write_local_mlsx(fp, INVALID_SOCKET, -1, g.gl_pathv[i]);
+						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);
+					}
 					continue;
 				}
+			}
+
+			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 !ERROR %d opening %s",sock,errno,fname);
+					sockprintf(sock,sess, "451 Insufficient system storage");
+					continue;
+				}
+
 				p=cmd+4;
 				SKIP_WHITESPACE(p);
 
@@ -3558,7 +3868,7 @@ static void ctrl_thread(void* arg)
 					,&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;
@@ -3779,6 +4089,256 @@ static void ctrl_thread(void* arg)
 			} /* 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 MLSx: %s",sock,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);
+				}
+				mls_fname = getfname(mls_path);
+
+				fp = NULL;
+				if (cmd[3] == 'D') {
+					if((fp=fopen(ftp_tmpfname(fname,"lst",sock),"w+b"))==NULL) {
+						lprintf(LOG_ERR,"%04d !ERROR %d opening %s",sock,errno,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);
+					send_mlsx(fp, sock, sess, "Type=file;Perm=r; %s",cmd[3] == 'T' ? mls_path : startup->index_file_name);
+					l++;
+				}
+				/* HTML Index File */
+				if(startup->options&FTP_OPT_INDEX_FILE && startup->html_index_file[0]
+						&& (cmd[3] == 'D' || strcmp(startup->html_index_file, mls_fname) == 0)) {
+					if (cmd[3] == 'T')
+						sockprintf(sock,sess, "250- Listing %s", startup->html_index_file);
+					send_mlsx(fp, sock, sess, "Type=file;Perm=r; %s",cmd[3] == 'T' ? mls_path : startup->html_index_file);
+					l++;
+				}
+
+				if(lib<0) { /* Root dir */
+					if (cmd[3] == 'T' && !*mls_path) {
+						sockprintf(sock,sess, "250- Listing root");
+						send_mlsx(fp, sock, sess, "Type=dir;Perm=el%s %s", (startup->options&FTP_OPT_ALLOW_QWK) ? "c" : "", mls_path);
+						l++;
+					}
+					lprintf(LOG_INFO,"%04d %s listing: root in %s mode",sock,user.alias, mode);
+
+					// TODO: Can upload files to root dir if QWK REPs are allowed.
+					/* 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);
+							send_mlsx(fp, sock, sess, "Type=file;Perm=r; %s", 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;
+
+							alias_dir=FALSE;
+
+							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? */
+							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);
+									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;
+								}
+								else 
+									alias_dir=TRUE;
+							}
+
+							if(!alias_dir && !fexist(np)) {
+								lprintf(LOG_WARNING,"0000 !Missing aliased file (%s) for %s",np,user.alias);
+								continue;
+							}
+
+							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(fp, sock, sess, "Type=dir;Perm=el; %s",cmd[3] == 'T' ? mls_path : p);
+								else
+									send_mlsx(fp, sock, sess, "Type=file;Perm=r;Size=%" PRIu64 ";Modify=%" PRIu64 "; %s",(uint64_t)flength(np), (uint64_t)fdate(np),cmd[3] == 'T' ? mls_path : p);
+								l++;
+							}
+						}
+
+						fclose(alias_fp);
+					}
+
+					/* 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]->sname, mls_fname) != 0)
+							continue;
+						if (cmd[3] == 'T')
+							sockprintf(sock,sess, "250- Listing %s", scfg.lib[i]->sname);
+						get_libperm(scfg.lib[i], &user, &client, permstr);
+						send_mlsx(fp, sock, sess, "Type=dir;Perm=%s; %s", permstr, cmd[3] == 'T' ? mls_path : scfg.lib[i]->sname);
+						l++;
+					}
+				} else if(dir<0) {
+					if (cmd[3] == 'T' && !*mls_path) {
+						sockprintf(sock,sess, "250- Listing %s", scfg.lib[lib]->sname);
+						send_mlsx(fp, sock, sess, "Type=dir;Perm=el %s", mls_path);
+						l++;
+					}
+					lprintf(LOG_INFO,"%04d %s listing: %s library in %s mode"
+						,sock,user.alias,scfg.lib[lib]->sname,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]->code_suffix, mls_fname) != 0)
+							continue;
+						if (cmd[3] == 'T')
+							sockprintf(sock,sess, "250- Listing %s", scfg.dir[i]->code_suffix);
+						get_dirperm(scfg.lib[lib], scfg.dir[i], &user, &client, permstr);
+						send_mlsx(fp, sock, sess, "Type=dir;Perm=%s; %s", permstr, cmd[3] == 'T' ? mls_path : scfg.dir[i]->code_suffix);
+						l++;
+					}
+				} 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);
+
+					if (cmd[3] == 'T' && !*mls_path) {
+						sockprintf(sock,sess, "250- Listing %s/%s",scfg.lib[lib]->sname,scfg.dir[dir]->code_suffix);
+						send_mlsx(fp, sock, sess, "Type=dir;Perm=el%s %s", (startup->options&FTP_OPT_ALLOW_QWK) ? "c" : "", mls_path);
+						l++;
+					}
+
+					SAFEPRINTF2(path,"%s%s",scfg.dir[dir]->path,"*");
+					glob(path,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((filedat=getfileixb(&scfg,&f))==FALSE
+							&& !(startup->options&FTP_OPT_DIR_FILES)
+							&& !(scfg.dir[dir]->misc&DIR_FILES))
+							continue;
+						if (cmd[3] != 'D' && strcmp(getfname(g.gl_pathv[i]), 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);
+						send_mlsx(fp, sock, sess, "Type=file;Perm=%s;Size=%" PRIu64 ";Modify=%" PRIu64 "; %s", permstr, (uint64_t)flength(g.gl_pathv[i]), (uint64_t)fdate(g.gl_pathv[i]), cmd[3] == 'T' ? mls_path : getfname(g.gl_pathv[i]));
+						l++;
+					}
+					globfree(&g);
+				} else 
+					lprintf(LOG_INFO,"%04d %s listing: %s/%s directory in %s mode (empty - no access)"
+						,sock,user.alias,scfg.lib[lib]->sname,scfg.dir[dir]->code_suffix,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;
@@ -4662,7 +5222,7 @@ static void ctrl_thread(void* arg)
 						continue;
 					}
 
-					if(scfg.dir[dir]->maxfiles && getfiles(&scfg,dir)>=scfg.dir[dir]->maxfiles) {
+					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: %u files)"
 							,sock,user.alias
 							,scfg.lib[scfg.dir[dir]->lib]->sname