diff -ur betaftpd-0.0.8pre17/cmds.c betaftpd-new/cmds.c
--- betaftpd-0.0.8pre17/cmds.c	Sat Sep 30 15:42:50 2000
+++ betaftpd-new/cmds.c	Sat Jun 21 08:57:42 2003
@@ -234,8 +234,7 @@
 
 #if WANT_NONROOT
 	if (nr_check_permission(c->uid, temp, 1, 1, NULL) == -1) {
-	        numeric(c, 550, "Permission denied");
-	        return -1;
+	        return numeric(c, 550, "Permission denied");
 	}
 #endif
 
@@ -247,11 +246,10 @@
 	}
 
 	if (strncmp(chd, c->root_dir, strlen(c->root_dir)) != 0) {
-		numeric(c, 550, "No such file or directory.");
-		return -1;
+		return numeric(c, 550, "No such file or directory.");
 	}
 
-	return 0;
+	return 1;
 }
 
 /*
@@ -268,12 +266,13 @@
 		strcpy(c->username, "ftp");
 	}
        	if (strcasecmp(c->username, "ftp") == 0) {
-	       	numeric(c, 331, "Login OK, send password (your e-mail).");
 		c->auth = 1;
+	       	return numeric(c, 331, "Login OK, send password (your e-mail).");
 	} else {
-		numeric(c, 331, "Password required for %s.", c->username);
 		c->auth = 2;
+		return numeric(c, 331, "Password required for %s.", c->username);
 	}
+	/*notreached*/
 	return 1;
 }
 
@@ -334,14 +333,15 @@
 		c->auth = 0;
 	}
 	if (c->auth == 0) {
-		numeric(c, 530, "Login incorrect.");
+		return numeric(c, 530, "Login incorrect.");
 	} else {
 #if WANT_MESSAGE
 		chdir(c->curr_dir);
 		dump_file(c, 230, "welcome.msg");
 #endif
-		numeric(c, 230, "User logged in.");
+		return numeric(c, 230, "User logged in.");
 	}
+	/*notreached*/
 	return 1;
 }
 
@@ -358,8 +358,7 @@
  */
 int cmd_acct(struct conn * const c)
 {
-	numeric(c, 202, "ACCT ignored OK -- not applicable on this system.");
-	return 1;
+	return numeric(c, 202, "ACCT ignored OK -- not applicable on this system.");
 }
 
 /*
@@ -377,8 +376,7 @@
 	struct sockaddr_in sin;
     
 	if ((c->transfer != NULL) && (c->transfer->state >= 4)) {
-		numeric(c, 500, "Sorry, only one transfer at a time.");
-		return 1;
+		return numeric(c, 500, "Sorry, only one transfer at a time.");
 	}
 
 	sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
@@ -389,7 +387,7 @@
 
 	i = sscanf(c->recv_buf, "%3hu,%3hu,%3hu,%3hu,%3hu,%3hu", &a0, &a1, &a2, &a3, &p0, &p1);
 	if (i < 6) {
-		numeric(c, 501, "Parse error.");
+		return numeric(c, 501, "Parse error.");
 	} else {
 		const int one = 1;
 		int tmp;
@@ -400,7 +398,8 @@
 		TRAP_ERROR(err == -1, 500, return 1);
 		sin.sin_port = FTP_PORT - 1;
 
-		numeric(c, 200, "PORT command OK.");
+		if (!numeric(c, 200, "PORT command OK."))
+			return 0;
 
 		/* note that bind() might well fail, so we don't error check */
 #if !WANT_NONROOT
@@ -442,8 +441,7 @@
 	struct sockaddr_in addr;
 
 	if ((c->transfer != NULL) && (c->transfer->state >= 4)) {
-		numeric(c, 503, "Sorry, only one transfer at once.");
-		return 1;
+		return numeric(c, 503, "Sorry, only one transfer at once.");
 	}
 	destroy_ftran(c->transfer);
 
@@ -473,14 +471,13 @@
 	TRAP_ERROR(err == -1, 500, return 1);
 	f->state = 1;
 
-	numeric(c, 227, "Entering passive mode (%u,%u,%u,%u,%u,%u)",
+	return numeric(c, 227, "Entering passive mode (%u,%u,%u,%u,%u,%u)",
 		(htonl(addr.sin_addr.s_addr) & 0xff000000) >> 24,
 		(htonl(addr.sin_addr.s_addr) & 0x00ff0000) >> 16,
 		(htonl(addr.sin_addr.s_addr) & 0x0000ff00) >>  8,
 		(htonl(addr.sin_addr.s_addr) & 0x000000ff),
 		(htons(addr.sin_port) & 0xff00) >> 8,
 		(htons(addr.sin_port) & 0x00ff));
-	return 1;
 }
 
 /*
@@ -497,7 +494,7 @@
 
 	cdir = do_pwd(c, temp, c->curr_dir);
 	if (cdir != NULL) {
-		numeric(c, 257, "\"%s\" is current working directory.", cdir);
+		return numeric(c, 257, "\"%s\" is current working directory.", cdir);
 	}
 	return 1;
 }
@@ -535,8 +532,7 @@
  */
 int cmd_cwd(struct conn * const c)
 {
-	cmd_cwd_internal(c, c->recv_buf);
-	return 1;
+	return cmd_cwd_internal(c, c->recv_buf);
 }
 
 /*
@@ -551,8 +547,7 @@
  */
 int cmd_cdup(struct conn * const c)
 {
-	cmd_cwd_internal(c, "..");
-	return 1;
+	return cmd_cwd_internal(c, "..");
 }
 
 /*
@@ -561,25 +556,28 @@
  *		space and have clearer code). Mostly, it just uses do_chdir(),
  *		and sees where that takes us. It adds a trailing slash if needed.
  */
-void cmd_cwd_internal(struct conn * const c, const char * const newd)
+int cmd_cwd_internal(struct conn * const c, const char * const newd)
 {
-	if (do_chdir(c, newd) != -1) {
-		int i;
+	int i;
+	int status;
 
-		getcwd(c->curr_dir, 254);
-		i = strlen(c->curr_dir);
-		if (c->curr_dir[i - 1] != '/') {
-			c->curr_dir[i++] = '/';
-			c->curr_dir[i] = '\0';
-		}
+	status = do_chdir(c, newd);
+	if (status != 1)
+		return status;
+
+	getcwd(c->curr_dir, 254);
+	i = strlen(c->curr_dir);
+	if (c->curr_dir[i - 1] != '/') {
+		c->curr_dir[i++] = '/';
+		c->curr_dir[i] = '\0';
+	}
 
 #if WANT_MESSAGE
-		dump_file(c, 250, ".message");
-		list_readmes(c);
+	dump_file(c, 250, ".message");
+	list_readmes(c);
 #endif
 
-		numeric(c, 250, "CWD successful.");
-	}
+	return numeric(c, 250, "CWD successful.");
 }
 
 /*
@@ -590,8 +588,7 @@
 int cmd_rest(struct conn * const c)
 {
 	c->rest_pos = abs(atoi(c->recv_buf));
-	numeric(c, 350, "Setting resume at %u bytes.", c->rest_pos);
-	return 1;
+	return numeric(c, 350, "Setting resume at %u bytes.", c->rest_pos);
 }
 
 /*
@@ -603,17 +600,16 @@
  */
 int cmd_retr(struct conn * const c)
 {
+	int ok=1;
 	struct ftran *f = c->transfer;
 
 	if ((f == NULL) || ((f->state != 1) && (f->state != 3))) {
-		numeric(c, 425, "No data connection set up; please use PASV or PORT.");
-		return 1;
+		return numeric(c, 425, "No data connection set up; please use PASV or PORT.");
 	}
 
 #if WANT_ASCII
 	if ((c->rest_pos > 0) && (c->ascii_mode == 1)) {
-		numeric(c, 500, "Cannot resume while in ASCII mode.");
-		return 1;
+		return numeric(c, 500, "Cannot resume while in ASCII mode.");
 	}
 #endif
 
@@ -625,7 +621,7 @@
 	f->dir_listing = 0;
 
 	if (f->local_file == -1) {
-		numeric(f->owner, 550, strerror(errno));
+		ok = numeric(f->owner, 550, strerror(errno));
 		destroy_ftran(f);
 	} else if (f->local_file == -2) {
 		f->local_file = -1;
@@ -636,7 +632,7 @@
 #endif
 		prepare_for_transfer(f);
 	}
-	return 1;
+	return ok;
 }
 
 #if WANT_UPLOAD
@@ -646,8 +642,7 @@
  */
 int cmd_stor(struct conn * const c)
 {
-	do_store(c, 0);
-	return 1;
+	return do_store(c, 0);
 }
 
 /*
@@ -656,27 +651,24 @@
  */
 int cmd_appe(struct conn * const c)
 {
-	do_store(c, 1);
-	return 1;
+	return do_store(c, 1);
 }
 
 /*
  * do_store():	Initiate an upload. Most of the comments to do_retr()
  *		(above) apply to this one as well.
  */
-void do_store(struct conn * const c, const int append)
+int do_store(struct conn * const c, const int append)
 {
 	struct ftran *f = c->transfer;
 
 	if ((f == NULL) || ((f->state != 1) && (f->state != 3))) {
-		numeric(c, 425, "No data connection set up; please use PASV or PORT.");
-		return;
+		return numeric(c, 425, "No data connection set up; please use PASV or PORT.");
 	}
 
 #if WANT_ASCII
 	if ((c->rest_pos > 0) && (c->ascii_mode == 1)) {
-		numeric(c, 500, "Cannot resume while in ASCII mode.");
-		return;
+		return numeric(c, 500, "Cannot resume while in ASCII mode.");
 	}
 #endif
 
@@ -689,7 +681,7 @@
 	f->dir_listing = 0;
 
 	if (f->local_file == -1) {
-	        numeric(f->owner, 550, strerror(errno));
+	        return numeric(f->owner, 550, strerror(errno));
 	} else if (f->local_file == -2) {
 		f->local_file = -1;
 	} else {
@@ -700,6 +692,7 @@
 #endif
 	        prepare_for_transfer(f);
 	}
+	return 1;
 }
 #endif /* WANT_UPLOAD */
 
@@ -718,18 +711,19 @@
 {
 #if WANT_ASCII
 	if (c->ascii_mode) {
-		numeric(c, 550, "SIZE not available in ASCII mode.");
-		return 1;
+		return numeric(c, 550, "SIZE not available in ASCII mode.");
 	}
 #endif
 	{
-		const char * const fname = translate_path(c, c->recv_buf);
 		struct stat buf;
+		int status;
+		const char * const fname = translate_path(c, c->recv_buf, &status);
+		if (status != 1)
+			return status;
 	
 		TRAP_ERROR(fname == NULL || lstat(fname, &buf) == -1, 550, return 1);
 	
-		numeric(c, 213, "%lu", (unsigned long)(buf.st_size));
-		return 1;
+		return numeric(c, 213, "%lu", (unsigned long)(buf.st_size));
 	}
 }
 
@@ -740,16 +734,18 @@
  */
 int cmd_mdtm(struct conn * const c)
 {
-	const char * const fname = translate_path(c, c->recv_buf);
 	struct stat buf;
 	struct tm *m;
+	int status;
+	const char * const fname = translate_path(c, c->recv_buf, &status);
+	if (status != 1)
+		return status;
 
 	TRAP_ERROR(fname == NULL || lstat(fname, &buf) == -1, 550, return 1);
 
 	m = gmtime(&(buf.st_mtime));	/* at least wu-ftpd does it in GMT */
-	numeric(c, 213, "%u%02u%02u%02u%02u%02u", m->tm_year + 1900,
+	return numeric(c, 213, "%u%02u%02u%02u%02u%02u", m->tm_year + 1900,
 		m->tm_mon + 1, m->tm_mday, m->tm_hour, m->tm_min, m->tm_sec);
-	return 1;
 }
 
 /*
@@ -759,11 +755,11 @@
 int cmd_abor(struct conn * const c)
 {
 	if (c->transfer != NULL) {
-		numeric(c, 426, "File transfer aborted.");
+		if (!numeric(c, 426, "File transfer aborted."))
+			return 0;
 		destroy_ftran(c->transfer);
 	}
-	numeric(c, 226, "ABOR command processed OK.");
-	return 1;
+	return numeric(c, 226, "ABOR command processed OK.");
 }
 
 /*
@@ -771,11 +767,13 @@
  */
 int cmd_dele(struct conn * const c)
 {
-	const char * const fname = translate_path(c, c->recv_buf);
+	int status;
+	const char * const fname = translate_path(c, c->recv_buf, &status);
+	if (status != 1)
+		return status;
 	
 	TRAP_ERROR(fname == NULL || unlink(fname) == -1, 550, return 1);
-	numeric(c, 250, "File deleted OK.");
-	return 1;
+	return numeric(c, 250, "File deleted OK.");
 }
 
 /*
@@ -783,9 +781,12 @@
  */
 int cmd_rnfr(struct conn * const c)
 {
-	const char * const fname = translate_path(c, c->recv_buf);
 	char cwd[256];
 	struct stat buf;
+	int status;
+	const char * const fname = translate_path(c, c->recv_buf, &status);
+	if (status != 1)
+		return status;
 
 	c->rename_from[0] = '\0';
 	if (fname == NULL) return 1;
@@ -796,8 +797,7 @@
 	/* Just check that the file exists. */
 	TRAP_ERROR(lstat(c->rename_from, &buf) == -1, 550, c->rename_from[0] = '\0'; return 1);
 
-	numeric(c, 350, "File exists, send RNTO.");
-	return 1;
+	return numeric(c, 350, "File exists, send RNTO.");
 }
 
 /*
@@ -805,19 +805,20 @@
  */
 int cmd_rnto(struct conn * const c)
 {
-	const char * const fname = translate_path(c, c->recv_buf);
+	int status;
+	const char * const fname = translate_path(c, c->recv_buf, &status);
+	if (status != 1)
+		return status;
 
 	if (fname == NULL) return 1;
 	if (c->rename_from[0] == '\0') {
-		numeric(c, 503, "Please send RNFR first.");
-		return 1;
+		return numeric(c, 503, "Please send RNFR first.");
 	}
 
 	TRAP_ERROR(rename(c->rename_from, fname) == -1, 550, c->rename_from[0] = '\0'; return 1);
 	c->rename_from[0] = '\0';
 
-	numeric(c, 250, "File renamed successfully.");
-	return 1;
+	return numeric(c, 250, "File renamed successfully.");
 }
 
 /*
@@ -836,9 +837,12 @@
  */
 int cmd_mkd(struct conn * const c)
 {
-	const char * const fname = translate_path(c, c->recv_buf);
 	char temp[512], temp2[1024], *cdir;
 	int i, j;
+	int status;
+	const char * const fname = translate_path(c, c->recv_buf, &status);
+	if (status != 1)
+		return status;
 
 	TRAP_ERROR(fname == NULL || mkdir(fname, 0755) == -1, 550, return 1);
 
@@ -853,8 +857,7 @@
 			temp2[++j] = '"';
 		}
 	}
-	numeric(c, 257, "\"%s\" created.", temp2);
-	return 1;
+	return numeric(c, 257, "\"%s\" created.", temp2);
 }
 
 /*
@@ -863,11 +866,13 @@
  */
 int cmd_rmd(struct conn * const c)
 {
-	const char * const fname = translate_path(c, c->recv_buf);
+	int status;
+	const char * const fname = translate_path(c, c->recv_buf, &status);
+	if (status != 1)
+		return status;
 
 	TRAP_ERROR(fname == NULL || rmdir(fname) == -1, 550, return 1);
-	numeric(c, 250, "Directory deleted.");
-	return 1;
+	return numeric(c, 250, "Directory deleted.");
 }
 
 /*
@@ -883,8 +888,7 @@
  */
 int cmd_allo(struct conn * const c)
 {
-	numeric(c, 202, "No storage allocation necessary.");
-	return 1;
+	return numeric(c, 202, "No storage allocation necessary.");
 }
 
 /*
@@ -947,7 +951,7 @@
 		return 0;
 	}
 #else
-	numeric(c, 502, "STAT command disabled for security reasons.");
+	return numeric(c, 502, "STAT command disabled for security reasons.");
 #endif
 	return 1;
 }
@@ -1107,8 +1111,7 @@
 	lo.long_listing = 1;
 	lo.classify = 0;
 
-	do_listing(c, &lo);
-	return 1;
+	return do_listing(c, &lo);
 }
 
 /*
@@ -1126,8 +1129,7 @@
 	lo.long_listing = 0;
 	lo.classify = 0;
 
-	do_listing(c, &lo);
-	return 1;
+	return do_listing(c, &lo);
 }
 
 /*
@@ -1138,9 +1140,9 @@
  *		If the directory listing cache is enabled, the cache
  *		is checked first, to see if we still have a valid entry.
  */
-void do_listing(struct conn * const c, struct list_options * const lo)
+int do_listing(struct conn * const c, struct list_options * const lo)
 {
-	int i;
+	int status;
 	char *ptr;
 #if HAVE_MMAP
 	int size;
@@ -1155,11 +1157,11 @@
 #warning No nonroot checking for list_core() yet
 #endif
 
-	i = prepare_for_listing(c, &ptr, lo);
-	if (i == -1) {
+	status = prepare_for_listing(c, &ptr, lo);
+	if (status == -1)
 		destroy_ftran(c->transfer);
-		return;
-	}
+	if (status != 1)
+		return status;
 
 #if WANT_DCACHE
 	getcwd(cwd, 256);
@@ -1181,24 +1183,30 @@
 			f->pos = 0;
 
 			prepare_for_transfer(f);
-			return;
+			return 1;
 		}
 	}
 #endif
 
 #if HAVE_MMAP
 	{
-		int num_files = get_num_files(c, ptr, lo);
-		if (num_files == -1) return;
+		int num_files = get_num_files(c, ptr, lo, &status);
+		if (status != 1) 
+			return status;
 
 		size = num_files * 160;
 		f->file_data = malloc(size + 1);
-		TRAP_ERROR(f->file_data == NULL, 550, return);
-		list_core(c, ptr, "", lo, size, 0);
+		TRAP_ERROR(f->file_data == NULL, 550, return -1);
+		list_core(c, ptr, "", lo, &status, size, 0);
+		/* FIXME: say, weren't we supposed to use the return value of list_core? */
 	}
 #else
-	list_core(c, ptr, "", lo);
+	list_core(c, ptr, "", lo, &status);
 #endif
+	if (status == -1)
+		destroy_ftran(c->transfer);
+	if (status != 1)
+		return status;
 
 #if WANT_DCACHE
 	populate_dcache(f, cwd, ptr, lo);
@@ -1208,6 +1216,7 @@
 	f->pos = 0;
 #endif
 	prepare_for_transfer(f);
+	return 1;
 }
 
 /*
@@ -1216,7 +1225,7 @@
  *		a pattern). Note that c is needed for TRAP_ERROR.
  */
 int get_num_files(struct conn * const c, const char * const pathname,
-	       	   struct list_options * const lo)
+	       	   struct list_options * const lo, int *pstatus)
 {
 	int num_files;
 	glob_t pglob;
@@ -1228,14 +1237,16 @@
 	switch (glob(pathname, 0, NULL, &pglob)) {
 #ifdef GLOB_NOMATCH
 	case GLOB_NOMATCH:
+		*pstatus = -1;
 		return 0;
 #endif
 	case 0:
+		*pstatus = 1;
 		num_files = pglob.gl_pathc;
 		break;
 	default:
-		numeric(c, 550, strerror(EACCES));
-		return -1;
+		*pstatus = numeric(c, 550, strerror(EACCES));
+		return 0;
 	}
 
 	if (lo->recursive) {
@@ -1247,8 +1258,10 @@
 			lstat(temp, &buf);
 			if (S_ISDIR(buf.st_mode)) {
 				chdir(temp);
-				num_files += get_num_files(c, "*", lo);
+				num_files += get_num_files(c, "*", lo, pstatus);
 				chdir("..");
+				if (*pstatus != 1)
+					break;
 			}
 		}
 	}
@@ -1274,7 +1287,8 @@
  *		This function is rather long.
  */
 int list_core(struct conn * const c, const char * const pathname,
-	      char * const disp_pathname, struct list_options * const lo
+	      char * const disp_pathname, struct list_options * const lo,
+	      int *pstatus
 #if HAVE_MMAP
 		, const int size, int pos
 #endif
@@ -1284,6 +1298,9 @@
 	glob_t pglob;
 	struct ftran * const f = c->transfer;
 
+	/* ok status until we hit an error */
+	*pstatus = 1;
+
         /*
          * glob() fails to set errno correctly, so we simply guess on
          * `permission denied'... The others are far less likely to happen.
@@ -1295,7 +1312,7 @@
 #endif
                 break;		/* note: break, not return */
         default:
-                numeric(c, 550, strerror(EACCES));
+                *pstatus = numeric(c, 550, strerror(EACCES));
 #if HAVE_MMAP
 		return pos;
 #else
@@ -1389,11 +1406,13 @@
 				}
 
 				chdir(temp);
-				pos = list_core(c, "*", tmp2, lo, 
+				pos = list_core(c, "*", tmp2, lo, pstatus,
 #if HAVE_MMAP
 					size, pos);
 #endif
 				chdir("..");
+				if (*pstatus != 1)
+					return 0;	/* caller will destroy_ftran if *pstatus is -1 */
 			}
 		}
 	}
@@ -1418,8 +1437,7 @@
  */
 int cmd_noop(struct conn * const c)
 {
-	numeric(c, 200, "NOOP command successful.");
-	return 1;
+	return numeric(c, 200, "NOOP command successful.");
 }
 
 /*
@@ -1427,8 +1445,7 @@
  */
 int cmd_syst(struct conn * const c)
 {
-	numeric(c, 215, "UNIX Type: L%u", NBBY);
-	return 1;
+	return numeric(c, 215, "UNIX Type: L%u", NBBY);
 }
 
 /*
@@ -1440,16 +1457,17 @@
 	c->recv_buf[0] &= (255-32);	/* convert to upper case */
 	if (c->recv_buf[0] == 'A') {
 		c->ascii_mode = 1;
-		numeric(c, 200, "Type is ASCII.");
+		return numeric(c, 200, "Type is ASCII.");
 	} else if (c->recv_buf[0] == 'I') {
 		c->ascii_mode = 0;
-		numeric(c, 200, "Type is IMAGE.");
+		return numeric(c, 200, "Type is IMAGE.");
 	} else {
-		numeric(c, 504, "Unknown type.");
+		return numeric(c, 504, "Unknown type.");
 	}
 #else
-	numeric(c, 200, "TYPE ignored (always I)");
+	return numeric(c, 200, "TYPE ignored (always I)");
 #endif
+	/*notreached*/
 	return 1;
 }
 
@@ -1460,10 +1478,11 @@
 {
  	c->recv_buf[0] &= (255-32);	/* convert to upper case */
 	if (c->recv_buf[0] == 'S') {
-		numeric(c, 200, "Mode is STREAM.");
+		return numeric(c, 200, "Mode is STREAM.");
 	} else {
-		numeric(c, 504, "Unknown mode.");
+		return numeric(c, 504, "Unknown mode.");
 	}
+	/*notreached*/
 	return 1;
 }
 
@@ -1474,10 +1493,11 @@
 {
 	c->recv_buf[0] &= (255-32);	/* convert to upper case */
 	if (c->recv_buf[0] == 'F') {
-		numeric(c, 200, "Structure is FILE.");
+		return numeric(c, 200, "Structure is FILE.");
 	} else {
-		numeric(c, 504, "Unknown structure.");
+		return numeric(c, 504, "Unknown structure.");
 	}
+	/*notreached*/
 	return 1;
 }
 
@@ -1500,8 +1520,7 @@
  */
 int cmd_help(struct conn * const c)
 {
-	numeric(c, 414, "Sorry, no detailed help; use standard FTP commands.");
-	return 1;
+	return numeric(c, 414, "Sorry, no detailed help; use standard FTP commands.");
 }
 
 /*
@@ -1510,8 +1529,8 @@
  */
 int cmd_quit(struct conn * const c)
 {
-	numeric(c, 221, "Have a nice day!");
-	destroy_conn(c);
+	if (numeric(c, 221, "Have a nice day!"))
+		destroy_conn(c);
 	return 0;
 }
 
@@ -1535,9 +1554,7 @@
 #endif
 
 	time(&(c->last_transfer));
-	numeric(c, 220, "BetaFTPD " VERSION " ready.");
-
-	return 1;
+	return numeric(c, 220, "BetaFTPD " VERSION " ready.");
 }
 
 #if DOING_PROFILING
@@ -1594,7 +1611,8 @@
 		if ((cmlen >= (strlen(h->cmd_name) + h->add_cmlen)) &&
 		    (strncasecmp(c->recv_buf, h->cmd_name, strlen(h->cmd_name)) == 0)) {
 			if (c->auth < h->min_auth) {
-				numeric(c, 503, "Please login with USER and PASS.");
+				if (!numeric(c, 503, "Please login with USER and PASS."))
+					return;
 				while (c->recv_buf[0] != '\n') remove_bytes(c, 1);
 			} else {
 				char schar;
@@ -1630,8 +1648,8 @@
 		}
 	} while ((++h)->callback != NULL);
 
-	numeric(c, 500, "Sorry, no such command.");
-	remove_bytes(c, cmlen); 
+	if (numeric(c, 500, "Sorry, no such command."))
+		remove_bytes(c, cmlen); 
 }
 
 /*
@@ -1707,8 +1725,12 @@
  *		Note that `path' will be _changed_, and used as a return pointer
  *		base. Do not attempt to free the result from this function --
  *		if you need to, free path instead.
+ *
+ *		Since this calls do_chdir, it might destroy c.
+ *		On exit, it will set *pstatus to the return value of do_chdir,
+ *		i.e. -1 if path bad, 0 if c destroyed, 1 on success.
  */
-char *translate_path(struct conn * const c, char * const path)
+char *translate_path(struct conn * const c, char * const path, int *pstatus)
 {
 	char *ptr = NULL;
 
@@ -1719,13 +1741,13 @@
 	if (ptr != NULL) {
 		char save_char = ptr[0];
 		ptr[0] = 0;
-
-		if (do_chdir(c, path) == -1) {
-			return NULL;
-		}
+		*pstatus = do_chdir(c, path);
 		ptr[0] = save_char;
+		if (*pstatus != 1)
+			return NULL;
 		ptr++;
 	} else {
+		*pstatus = 1;
 		ptr = path;
 	}
 	return ptr;
@@ -1753,6 +1775,7 @@
 {
 	char *ptr;
 	struct stat buf;
+	int status;
 
 #if WANT_NONROOT
 	if (nr_check_permission(c->uid, path, check_permission, 0, NULL) == -1) {
@@ -1760,14 +1783,16 @@
 	}
 #endif
 
-	ptr = translate_path(c, c->recv_buf);
-	if (ptr == NULL) return -1;
+	ptr = translate_path(c, c->recv_buf, &status);
+	if (status != 1)
+		return status;
 
 #if WANT_UPLOAD
 	if ((flags & O_CREAT) == 0) {
 #endif
 		TRAP_ERROR(stat(ptr, &buf) == -1, 550, return -2);
  		if (!S_ISREG(buf.st_mode)) {
+			/*FIXME: could destroy c*/
 			numeric(c, 550, "Not a plain file.", ptr);
 			return -2;
  		}
@@ -1804,8 +1829,7 @@
 #endif
 
 	if ((f == NULL) || ((f->state != 1) && (f->state != 3))) {
-		numeric(c, 425, "No data connection set up; please use PASV or PORT.");
-		return -1;
+		return numeric(c, 425, "No data connection set up; please use PASV or PORT.");
 	}
 
 	/*
@@ -1847,8 +1871,11 @@
 	/* then we chdir to the dir in fptr (if any) */
 	tmp = fptr ? strrchr(fptr, '/') : NULL;
 	if (tmp != NULL) {
+		int status;
 		tmp[0] = 0;
-		if (do_chdir(c, fptr) == -1) return -1;
+		status = do_chdir(c, fptr);
+		if (status != 1) 
+			return status;
 		fptr = tmp + 1;
 	} else {
 		/* current directory */
@@ -1871,8 +1898,7 @@
 #if WANT_NONROOT
 	getcwd(chd, 512);
 	if (nr_check_permission(c->uid, chd, 4, 1, NULL) == -1) {
-	 	numeric(c, 550, "Permission denied");
-		return -1;
+	 	return numeric(c, 550, "Permission denied");
 	}
 #endif
 
@@ -1895,7 +1921,7 @@
 	f->upload = 0;
 #endif
 
-	return 0;
+	return 1;
 }
 
 /*
diff -ur betaftpd-0.0.8pre17/cmds.h betaftpd-new/cmds.h
--- betaftpd-0.0.8pre17/cmds.h	Sat Sep 30 15:42:50 2000
+++ betaftpd-new/cmds.h	Sat Jun 21 08:33:10 2003
@@ -97,11 +97,11 @@
 CMD_PROTO(exit);	
 #endif
 
-void cmd_cwd_internal(struct conn * const c, const char * const newd);
+int cmd_cwd_internal(struct conn * const c, const char * const newd);
 void parse_command(struct conn *c);
 void prepare_for_transfer(struct ftran *f);
 char decode_mode(mode_t mode);
-char *translate_path(struct conn * const c, char * const path);
+char *translate_path(struct conn * const c, char * const path, int *pstatus);
 int do_openfile(struct conn * const c, char * const path,
 		char * const filename, const int flags
 #if WANT_NONROOT
@@ -110,11 +110,12 @@
 );
 int prepare_for_listing(struct conn * const c, char ** const ptr,
 			struct list_options * const lo);
-void do_listing(struct conn * const c, struct list_options * const lo);
+int do_listing(struct conn * const c, struct list_options * const lo);
 int get_num_files(struct conn * const c, const char * const pathname,
-                   struct list_options * const lo);
+                   struct list_options * const lo, int *pstatus);
 int list_core(struct conn * const c, const char * const pathname,
-   		   char * const disp_pathname, struct list_options * const lo
+   		   char * const disp_pathname, struct list_options * const lo,
+		   int *pstatus
 #if HAVE_MMAP
 		, const int size, int pos
 #endif
diff -ur betaftpd-0.0.8pre17/ftpd.c betaftpd-new/ftpd.c
--- betaftpd-0.0.8pre17/ftpd.c	Sat Sep 30 15:42:50 2000
+++ betaftpd-new/ftpd.c	Fri Jun 20 16:53:23 2003
@@ -359,6 +359,9 @@
 
 	if (c == NULL) return c;
 
+	c->prev_conn = NULL;
+	c->next_conn = NULL;
+
 	if (sock != -1) {
 		ioctl(sock, FIONBIO, &one);
 		if (add_fd(sock, POLLIN) != 0) {
@@ -414,11 +417,9 @@
 	struct ftran *f = (struct ftran *)(malloc(sizeof(struct ftran)));
 
 	if (f == NULL) return f;
-	if (c == NULL) {
-		/* this is the bogus head of the list */
-		f->next_ftran = NULL;
-		f->prev_ftran = NULL;
-	} else {
+	f->next_ftran = NULL;
+	f->prev_ftran = NULL;
+	if (c != NULL) {
 		add_to_linked_list((struct list_element *)first_ftran,
 				   (struct list_element *)f);
 	}
@@ -565,17 +566,19 @@
 
 		/* overrun = disconnect */
 		if (c->buf_len + bytes_avail > 254) {
-			numeric(c, 503, "Buffer overrun; disconnecting.");
-			destroy_conn(c);
+			if (numeric(c, 503, "Buffer overrun; disconnecting."))
+				destroy_conn(c);
 			continue;
 		}
 
 		c->buf_len += bytes_avail;
 		parse_command(c);
 
+		/* FIXME: c could be invalid here, as parse_command can destroy it 
 		if (fds[c->sock].revents & (POLLERR|POLLHUP|POLLNVAL)) {
                         destroy_conn(c);
                 }
+		*/
 	}
 	return checked_through;
 }
@@ -660,7 +663,8 @@
 			if (do_download(f)) continue;
 
 		/* do_{upload,download} returned 0, the transfer is complete */
-                numeric(f->owner, 226, "Transfer complete.");
+                if (!numeric(f->owner, 226, "Transfer complete."))
+			continue;
                 time(&(f->owner->last_transfer));
 
 #if WANT_XFERLOG
@@ -1090,10 +1094,12 @@
 		struct conn * const c = alloc_new_conn(tempsock);
 		num_err = 0;
 		if (c != NULL) {
-			numeric(c, 220, "BetaFTPD " VERSION " ready.");
+			if (numeric(c, 220, "BetaFTPD " VERSION " ready.")) {
 #if WANT_STAT
-			memcpy(&(c->addr), &tempaddr, sizeof(struct sockaddr));
+				memcpy(&(c->addr), &tempaddr, sizeof(struct sockaddr));
 #endif
+				;
+			}
 		}
 	}
 }
@@ -1136,8 +1142,8 @@
 		if ((c->transfer == NULL || c->transfer->state != 5) &&
 		    (now - c->last_transfer > TIMEOUT_SECS)) {
 			/* RFC violation? */
-			numeric(c, 421, "Timeout (%u minutes): Closing control connection.", TIMEOUT_SECS/60);
-			destroy_conn(c);
+			if (numeric(c, 421, "Timeout (%u minutes): Closing control connection.", TIMEOUT_SECS/60))
+				destroy_conn(c);
 		}
 	}
 }
@@ -1165,8 +1171,11 @@
  *		you can use this command much the same way as you
  *		would use a printf() (with all the normal %s, %d,
  *		etc.), since it actually uses printf() internally.
+ *
+ *		Returns 0 if connection was destroyed, 1 on success.
+ *		This is so cmd_* callers can just do "return numeric(...)" on error.
  */
-void numeric(struct conn * const c, const int numeric, const char * const format, ...)
+int numeric(struct conn * const c, const int numeric, const char * const format, ...)
 {
 	char buf[256], fmt[256];
 	va_list args;
@@ -1181,7 +1190,9 @@
 	err = send(c->sock, buf, i, 0);
 	if (err == -1 && errno == EPIPE) {
 		destroy_conn(c);
+		return 0;
 	}
+	return 1;
 }
 
 /*
@@ -1277,7 +1288,8 @@
 	
 	if (f->dir_listing) {
 		/* include size? */
-		numeric(f->owner, 150, "Opening ASCII mode data connection for directory listing.");
+		if (!numeric(f->owner, 150, "Opening ASCII mode data connection for directory listing."))
+			return;
 	} else {
 		/*
 		 * slightly kludged -- perhaps we should kill the second arm,
@@ -1290,20 +1302,24 @@
 			|| f->upload
 #endif /* WANT_UPLOAD */
 		) {
-			numeric(f->owner, 150, "Opening %s mode data connection for '%s'",
-				(f->ascii_mode) ? "ASCII" : "BINARY", f->filename);
+			if (!numeric(f->owner, 150, "Opening %s mode data connection for '%s'",
+				(f->ascii_mode) ? "ASCII" : "BINARY", f->filename))
+					return;
 		} else {
-			numeric(f->owner, 150, "Opening %s mode data connection for '%s' (%u bytes)",
+			if (!numeric(f->owner, 150, "Opening %s mode data connection for '%s' (%u bytes)",
 				(f->ascii_mode) ? "ASCII" : "BINARY", f->filename,
-				f->size); 
+				f->size))
+					return; 
 		}
 #else /* !WANT_ASCII */
 #if WANT_UPLOAD
 		if (f->upload) {
-			numeric(f->owner, 150, "Opening BINARY mode data connection for '%s'", f->filename);
+			if (!numeric(f->owner, 150, "Opening BINARY mode data connection for '%s'", f->filename))
+				return;
 		} else
 #endif /* WANT_UPLOAD */
-			numeric(f->owner, 150, "Opening BINARY mode data connection for '%s' (%u bytes)", f->filename, f->size);
+			if (!numeric(f->owner, 150, "Opening BINARY mode data connection for '%s' (%u bytes)", f->filename, f->size))
+				return;
 #endif /* !WANT_ASCII */
 	}
 
diff -ur betaftpd-0.0.8pre17/ftpd.h betaftpd-new/ftpd.h
--- betaftpd-0.0.8pre17/ftpd.h	Sat Jun 21 08:40:24 2003
+++ betaftpd-new/ftpd.h	Fri Jun 20 16:25:27 2003
@@ -203,7 +203,7 @@
 void time_out_sockets();
 
 void remove_bytes(struct conn * const c, const int i);
-void numeric(struct conn * const c, const int numeric, const char * const format, ...);
+int numeric(struct conn * const c, const int numeric, const char * const format, ...);
 void init_file_transfer(struct ftran * const f);
 int create_server_socket();
 
