]> git.sesse.net Git - betaftpd/blobdiff - cmds.c
Fixed a few problems reported by Valgrind (still not sure if BetaFTPD is totally...
[betaftpd] / cmds.c
diff --git a/cmds.c b/cmds.c
index a2e047bc51eef0a54ffc3d8d46ea4f74c74108ba..2e38823ab9d4716ae5c2f6aae5805e513c819fca 100644 (file)
--- a/cmds.c
+++ b/cmds.c
@@ -2,7 +2,7 @@
     Copyright (C) 1999-2000 Steinar H. Gunderson
 
     This program is is free software; you can redistribute it and/or modify
     Copyright (C) 1999-2000 Steinar H. Gunderson
 
     This program is is free software; you can redistribute it and/or modify
-    it under the terms of the GNU General Public License, version 2 if the
+    it under the terms of the GNU General Public License, version 2 of the
     License as published by the Free Software Foundation.
 
     This program is distributed in the hope that it will be useful,
     License as published by the Free Software Foundation.
 
     This program is distributed in the hope that it will be useful,
 #include <config.h>
 #endif
 
 #include <config.h>
 #endif
 
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
 #if HAVE_STROPTS_H
 #include <stropts.h>
 #endif
 
 #if HAVE_STROPTS_H
 #include <stropts.h>
 #endif
 
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
 #if HAVE_SYS_CONF_H
 #include <sys/conf.h>
 #endif
 #if HAVE_SYS_CONF_H
 #include <sys/conf.h>
 #endif
 #include <fcntl.h>
 #endif
 
 #include <fcntl.h>
 #endif
 
-#if HAVE_PWD_H
-#include <pwd.h>
-#endif
-
 #if HAVE_GRP_H
 #include <grp.h>
 #endif
 #if HAVE_GRP_H
 #include <grp.h>
 #endif
 #include <sys/ioctl.h>
 #endif
 
 #include <sys/ioctl.h>
 #endif
 
-#if HAVE_SYS_SOCKET_H
-#include <sys/socket.h>
-#endif
-
 #if HAVE_SYS_STAT_H
 #include <sys/stat.h>
 #endif
 #if HAVE_SYS_STAT_H
 #include <sys/stat.h>
 #endif
 #include <sys/conf.h>
 #endif
 
 #include <sys/conf.h>
 #endif
 
-#if HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-
 #if HAVE_SHADOW_H
 #include <shadow.h>
 #endif
 #if HAVE_SHADOW_H
 #include <shadow.h>
 #endif
@@ -161,7 +149,7 @@ extern fd_set master_fds, master_send_fds;
 struct handler {
        char cmd_name[6];
        char add_cmlen;         /* =1 if the command takes an argument */
 struct handler {
        char cmd_name[6];
        char add_cmlen;         /* =1 if the command takes an argument */
-       int (*callback)(struct conn * const);
+       void (*callback)(struct conn * const);
        char min_auth;
 #if !WANT_NONROOT
        char do_setuid;         /* =1 if root is not *really* needed */
        char min_auth;
 #if !WANT_NONROOT
        char do_setuid;         /* =1 if root is not *really* needed */
@@ -275,7 +263,7 @@ int do_chdir(struct conn * const c, const char * const newd)
  *             authentication work. User names are limited to 16
  *             characters, by force...
  */
  *             authentication work. User names are limited to 16
  *             characters, by force...
  */
-int cmd_user(struct conn * const c)
+void cmd_user(struct conn * const c)
 {
        strncpy(c->username, c->recv_buf, 16);
        c->username[16] = 0;
 {
        strncpy(c->username, c->recv_buf, 16);
        c->username[16] = 0;
@@ -290,7 +278,6 @@ int cmd_user(struct conn * const c)
                numeric(c, 331, "Password required for %s.", c->username);
                c->auth = 2;
        }
                numeric(c, 331, "Password required for %s.", c->username);
                c->auth = 2;
        }
-       return 1;
 }
 
 /*
 }
 
 /*
@@ -301,7 +288,7 @@ int cmd_user(struct conn * const c)
  *             don't even support PAM or real shadow passwords (with
  *             expiry etc) yet...
  */
  *             don't even support PAM or real shadow passwords (with
  *             expiry etc) yet...
  */
-int cmd_pass(struct conn * const c)
+void cmd_pass(struct conn * const c)
 {
 #if WANT_NONROOT
        c->auth = nr_userinfo(c->username, &c->uid, c->curr_dir, c->root_dir,
 {
 #if WANT_NONROOT
        c->auth = nr_userinfo(c->username, &c->uid, c->curr_dir, c->root_dir,
@@ -321,6 +308,7 @@ int cmd_pass(struct conn * const c)
                c->auth = 0;
        } else {
                c->uid = p->pw_uid;
                c->auth = 0;
        } else {
                c->uid = p->pw_uid;
+               c->gid = p->pw_gid;
                strncpy(c->curr_dir, p->pw_dir, 254);
                c->curr_dir[254] = 0;
        }
                strncpy(c->curr_dir, p->pw_dir, 254);
                c->curr_dir[254] = 0;
        }
@@ -340,7 +328,7 @@ int cmd_pass(struct conn * const c)
                ) {
                        c->auth = 0;
                } else {
                ) {
                        c->auth = 0;
                } else {
-                       c->auth = 3;
+                       c->auth = 4;
                }
        }
 #endif /* !WANT_NONROOT */
                }
        }
 #endif /* !WANT_NONROOT */
@@ -356,9 +344,9 @@ int cmd_pass(struct conn * const c)
                chdir(c->curr_dir);
                dump_file(c, 230, "welcome.msg");
 #endif
                chdir(c->curr_dir);
                dump_file(c, 230, "welcome.msg");
 #endif
+               /* Have a different message for anonymous users? */
                numeric(c, 230, "User logged in.");
        }
                numeric(c, 230, "User logged in.");
        }
-       return 1;
 }
 
 /*
 }
 
 /*
@@ -372,10 +360,9 @@ int cmd_pass(struct conn * const c)
  *             I feel that the RFC959 intention is having it _before_
  *             USER/PASS. Therefore, this one runs with root privilegies :-)
  */
  *             I feel that the RFC959 intention is having it _before_
  *             USER/PASS. Therefore, this one runs with root privilegies :-)
  */
-int cmd_acct(struct conn * const c)
+void cmd_acct(struct conn * const c)
 {
        numeric(c, 202, "ACCT ignored OK -- not applicable on this system.");
 {
        numeric(c, 202, "ACCT ignored OK -- not applicable on this system.");
-       return 1;
 }
 
 /*
 }
 
 /*
@@ -385,7 +372,7 @@ int cmd_acct(struct conn * const c)
  *             the whole way), in case there are some weird overflows
  *             somewhere.
  */
  *             the whole way), in case there are some weird overflows
  *             somewhere.
  */
-int cmd_port(struct conn * const c)
+void cmd_port(struct conn * const c)
 {
        short int a0, a1, a2, a3, p0, p1;
        int i, sock, err;
 {
        short int a0, a1, a2, a3, p0, p1;
        int i, sock, err;
@@ -394,11 +381,11 @@ int cmd_port(struct conn * const c)
     
        if ((c->transfer != NULL) && (c->transfer->state >= 4)) {
                numeric(c, 500, "Sorry, only one transfer at a time.");
     
        if ((c->transfer != NULL) && (c->transfer->state >= 4)) {
                numeric(c, 500, "Sorry, only one transfer at a time.");
-               return 1;
+               return;
        }
 
        sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        }
 
        sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
-       TRAP_ERROR(sock == -1, 500, return 1);
+       TRAP_ERROR(sock == -1, 500, return);
 
        destroy_ftran(c->transfer);
        c->transfer = f = alloc_new_ftran(sock, c);
 
        destroy_ftran(c->transfer);
        c->transfer = f = alloc_new_ftran(sock, c);
@@ -413,7 +400,7 @@ int cmd_port(struct conn * const c)
                /* bind to own address, port 20 (FTP data) */
                tmp = sizeof(sin);
                err = getsockname(c->sock, (struct sockaddr *)&sin, &tmp);
                /* bind to own address, port 20 (FTP data) */
                tmp = sizeof(sin);
                err = getsockname(c->sock, (struct sockaddr *)&sin, &tmp);
-               TRAP_ERROR(err == -1, 500, return 1);
+               TRAP_ERROR(err == -1, 500, return);
                sin.sin_port = FTP_PORT - 1;
 
                numeric(c, 200, "PORT command OK.");
                sin.sin_port = FTP_PORT - 1;
 
                numeric(c, 200, "PORT command OK.");
@@ -422,9 +409,11 @@ int cmd_port(struct conn * const c)
 #if !WANT_NONROOT
                /* need root privilegies for a short while */
                seteuid(getuid());
 #if !WANT_NONROOT
                /* need root privilegies for a short while */
                seteuid(getuid());
+               setegid(getgid());
 #endif
                bind(sock, (struct sockaddr *)&sin, sizeof(sin));
 #if !WANT_NONROOT
 #endif
                bind(sock, (struct sockaddr *)&sin, sizeof(sin));
 #if !WANT_NONROOT
+               setegid(c->gid);
                seteuid(c->uid);
 #endif
 
                seteuid(c->uid);
 #endif
 
@@ -443,14 +432,13 @@ int cmd_port(struct conn * const c)
                i = 1;          
                ioctl(f->sock, FIONBIO, &one);
        }
                i = 1;          
                ioctl(f->sock, FIONBIO, &one);
        }
-       return 1;
 }
 
 /*
  * cmd_pasv(): Handles the PASV command, and sets up the data socket.
  *             Uses port numbers > 1024, since it doesn't run as root.
  */
 }
 
 /*
  * cmd_pasv(): Handles the PASV command, and sets up the data socket.
  *             Uses port numbers > 1024, since it doesn't run as root.
  */
-int cmd_pasv(struct conn * const c)
+void cmd_pasv(struct conn * const c)
 {
        struct ftran *f;
        int tmp, sock, err;
 {
        struct ftran *f;
        int tmp, sock, err;
@@ -459,14 +447,14 @@ int cmd_pasv(struct conn * const c)
 
        if ((c->transfer != NULL) && (c->transfer->state >= 4)) {
                numeric(c, 503, "Sorry, only one transfer at once.");
 
        if ((c->transfer != NULL) && (c->transfer->state >= 4)) {
                numeric(c, 503, "Sorry, only one transfer at once.");
-               return 1;
+               return;
        }
        destroy_ftran(c->transfer);
 
        sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
        }
        destroy_ftran(c->transfer);
 
        sock = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
-       TRAP_ERROR(sock == -1, 500, return 1);
+       TRAP_ERROR(sock == -1, 500, return);
        err = add_fd(sock, POLLIN);
        err = add_fd(sock, POLLIN);
-       TRAP_ERROR(err != 0, 501, return 1);
+       TRAP_ERROR(err != 0, 501, return);
 
        c->transfer = f = alloc_new_ftran(sock, c);
 
 
        c->transfer = f = alloc_new_ftran(sock, c);
 
@@ -475,18 +463,18 @@ int cmd_pasv(struct conn * const c)
        /* setup socket */
        tmp = sizeof(addr);
        err = getsockname(c->sock, (struct sockaddr *)&addr, &tmp);
        /* setup socket */
        tmp = sizeof(addr);
        err = getsockname(c->sock, (struct sockaddr *)&addr, &tmp);
-       TRAP_ERROR(err == -1, 500, return 1);
+       TRAP_ERROR(err == -1, 500, return);
 
        addr.sin_port = 0;      /* let the system choose */
        err = bind(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr));
 
        addr.sin_port = 0;      /* let the system choose */
        err = bind(sock, (struct sockaddr *)&addr, sizeof(struct sockaddr));
-       TRAP_ERROR(err == -1, 500, return 1);
+       TRAP_ERROR(err == -1, 500, return);
 
        tmp = sizeof(addr);
        err = getsockname(sock, (struct sockaddr *)&addr, &tmp);
 
        tmp = sizeof(addr);
        err = getsockname(sock, (struct sockaddr *)&addr, &tmp);
-       TRAP_ERROR(err == -1, 500, return 1);
+       TRAP_ERROR(err == -1, 500, return);
 
        err = listen(f->sock, 1);
 
        err = listen(f->sock, 1);
-       TRAP_ERROR(err == -1, 500, return 1);
+       TRAP_ERROR(err == -1, 500, return);
        f->state = 1;
 
        numeric(c, 227, "Entering passive mode (%u,%u,%u,%u,%u,%u)",
        f->state = 1;
 
        numeric(c, 227, "Entering passive mode (%u,%u,%u,%u,%u,%u)",
@@ -496,7 +484,6 @@ int cmd_pasv(struct conn * const c)
                (htonl(addr.sin_addr.s_addr) & 0x000000ff),
                (htons(addr.sin_port) & 0xff00) >> 8,
                (htons(addr.sin_port) & 0x00ff));
                (htonl(addr.sin_addr.s_addr) & 0x000000ff),
                (htons(addr.sin_port) & 0xff00) >> 8,
                (htons(addr.sin_port) & 0x00ff));
-       return 1;
 }
 
 /*
 }
 
 /*
@@ -507,7 +494,7 @@ int cmd_pasv(struct conn * const c)
  *             /betaftpd.users file, if you use nonroot. If not, it's a bug.
  *             Try to get it _reproducible_, and mail it to me.
  */
  *             /betaftpd.users file, if you use nonroot. If not, it's a bug.
  *             Try to get it _reproducible_, and mail it to me.
  */
-int cmd_pwd(struct conn * const c)
+void cmd_pwd(struct conn * const c)
 {
        char temp[512], *cdir = NULL;
 
 {
        char temp[512], *cdir = NULL;
 
@@ -515,7 +502,6 @@ int cmd_pwd(struct conn * const c)
        if (cdir != NULL) {
                numeric(c, 257, "\"%s\" is current working directory.", cdir);
        }
        if (cdir != NULL) {
                numeric(c, 257, "\"%s\" is current working directory.", cdir);
        }
-       return 1;
 }
 
 /*
 }
 
 /*
@@ -530,6 +516,7 @@ char *do_pwd(struct conn * const c, char * const retbuf, const char * const dir)
        char *cdir = NULL;
 
        strcpy(retbuf, dir);
        char *cdir = NULL;
 
        strcpy(retbuf, dir);
+
        if (strncmp(retbuf, c->root_dir, strlen(c->root_dir)) != 0) {
                numeric(c, 550, "curr_dir is outside root_dir, please contact site administrator.");
                return NULL;
        if (strncmp(retbuf, c->root_dir, strlen(c->root_dir)) != 0) {
                numeric(c, 550, "curr_dir is outside root_dir, please contact site administrator.");
                return NULL;
@@ -549,10 +536,9 @@ char *do_pwd(struct conn * const c, char * const retbuf, const char * const dir)
  * cmd_cwd():  Handles CWD command (change working directory). Uses
  *             cmd_cwd_internal() (see below).
  */
  * cmd_cwd():  Handles CWD command (change working directory). Uses
  *             cmd_cwd_internal() (see below).
  */
-int cmd_cwd(struct conn * const c)
+void cmd_cwd(struct conn * const c)
 {
        cmd_cwd_internal(c, c->recv_buf);
 {
        cmd_cwd_internal(c, c->recv_buf);
-       return 1;
 }
 
 /*
 }
 
 /*
@@ -565,10 +551,9 @@ int cmd_cwd(struct conn * const c)
  *             an error, instead of just staying in the root directory (as
  *             the OS and thus wu-ftpd does).
  */
  *             an error, instead of just staying in the root directory (as
  *             the OS and thus wu-ftpd does).
  */
-int cmd_cdup(struct conn * const c)
+void cmd_cdup(struct conn * const c)
 {
        cmd_cwd_internal(c, "..");
 {
        cmd_cwd_internal(c, "..");
-       return 1;
 }
 
 /*
 }
 
 /*
@@ -603,11 +588,10 @@ void cmd_cwd_internal(struct conn * const c, const char * const newd)
  *             sending functions to start at the correct number. We should
  *             perhaps add some better error checking to this?
  */
  *             sending functions to start at the correct number. We should
  *             perhaps add some better error checking to this?
  */
-int cmd_rest(struct conn * const c)
+void 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);
 {
        c->rest_pos = abs(atoi(c->recv_buf));
        numeric(c, 350, "Setting resume at %u bytes.", c->rest_pos);
-       return 1;
 }
 
 /*
 }
 
 /*
@@ -617,19 +601,19 @@ int cmd_rest(struct conn * const c)
  *             connection occurs (or succeeds, if we're using PORT mode),
  *             the actual file transfer begins.
  */
  *             connection occurs (or succeeds, if we're using PORT mode),
  *             the actual file transfer begins.
  */
-int cmd_retr(struct conn * const c)
+void cmd_retr(struct conn * const c)
 {
        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.");
 {
        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;
        }
 
 #if WANT_ASCII
        if ((c->rest_pos > 0) && (c->ascii_mode == 1)) {
                numeric(c, 500, "Cannot resume while in ASCII mode.");
        }
 
 #if WANT_ASCII
        if ((c->rest_pos > 0) && (c->ascii_mode == 1)) {
                numeric(c, 500, "Cannot resume while in ASCII mode.");
-               return 1;
+               return;
        }
 #endif
 
        }
 #endif
 
@@ -652,7 +636,6 @@ int cmd_retr(struct conn * const c)
 #endif
                prepare_for_transfer(f);
        }
 #endif
                prepare_for_transfer(f);
        }
-       return 1;
 }
 
 #if WANT_UPLOAD
 }
 
 #if WANT_UPLOAD
@@ -660,20 +643,18 @@ int cmd_retr(struct conn * const c)
  * cmd_stor(): Handles the STOR command (upload file). Pushes the
  *             work down to do_store(), below.
  */
  * cmd_stor(): Handles the STOR command (upload file). Pushes the
  *             work down to do_store(), below.
  */
-int cmd_stor(struct conn * const c)
+void cmd_stor(struct conn * const c)
 {
        do_store(c, 0);
 {
        do_store(c, 0);
-       return 1;
 }
 
 /*
  * cmd_appe(): Handles the APPE command (append to file). Pushes
  *             the work down to do_store(), below.
  */
 }
 
 /*
  * cmd_appe(): Handles the APPE command (append to file). Pushes
  *             the work down to do_store(), below.
  */
-int cmd_appe(struct conn * const c)
+void cmd_appe(struct conn * const c)
 {
        do_store(c, 1);
 {
        do_store(c, 1);
-       return 1;
 }
 
 /*
 }
 
 /*
@@ -730,22 +711,21 @@ void do_store(struct conn * const c, const int append)
  *             size of all the files in the directory, rather how
  *             much space the directory inode uses.
  */
  *             size of all the files in the directory, rather how
  *             much space the directory inode uses.
  */
-int cmd_size(struct conn * const c)
+void cmd_size(struct conn * const c)
 {
 #if WANT_ASCII
        if (c->ascii_mode) {
                numeric(c, 550, "SIZE not available in ASCII mode.");
 {
 #if WANT_ASCII
        if (c->ascii_mode) {
                numeric(c, 550, "SIZE not available in ASCII mode.");
-               return 1;
+               return;
        }
 #endif
        {
                const char * const fname = translate_path(c, c->recv_buf);
                struct stat buf;
        
        }
 #endif
        {
                const char * const fname = translate_path(c, c->recv_buf);
                struct stat buf;
        
-               TRAP_ERROR(fname == NULL || lstat(fname, &buf) == -1, 550, return 1);
+               TRAP_ERROR(fname == NULL || lstat(fname, &buf) == -1, 550, return);
        
                numeric(c, 213, "%lu", (unsigned long)(buf.st_size));
        
                numeric(c, 213, "%lu", (unsigned long)(buf.st_size));
-               return 1;
        }
 }
 
        }
 }
 
@@ -754,86 +734,81 @@ int cmd_size(struct conn * const c)
  *             date/time of a file. See the comments on cmd_size(),
  *             above.
  */
  *             date/time of a file. See the comments on cmd_size(),
  *             above.
  */
-int cmd_mdtm(struct conn * const c)
+void cmd_mdtm(struct conn * const c)
 {
        const char * const fname = translate_path(c, c->recv_buf);
        struct stat buf;
        struct tm *m;
 
 {
        const char * const fname = translate_path(c, c->recv_buf);
        struct stat buf;
        struct tm *m;
 
-       TRAP_ERROR(fname == NULL || lstat(fname, &buf) == -1, 550, return 1);
+       TRAP_ERROR(fname == NULL || lstat(fname, &buf) == -1, 550, return);
 
        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,
                m->tm_mon + 1, m->tm_mday, m->tm_hour, m->tm_min, m->tm_sec);
 
        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,
                m->tm_mon + 1, m->tm_mday, m->tm_hour, m->tm_min, m->tm_sec);
-       return 1;
 }
 
 /*
  * cmd_abor(): Handle the ABOR command (abort a file transfer). This should
  *             be clean enough, but isn't tested extensively.
  */
 }
 
 /*
  * cmd_abor(): Handle the ABOR command (abort a file transfer). This should
  *             be clean enough, but isn't tested extensively.
  */
-int cmd_abor(struct conn * const c)
+void cmd_abor(struct conn * const c)
 {
        if (c->transfer != NULL) {
                numeric(c, 426, "File transfer aborted.");
                destroy_ftran(c->transfer);
        }
        numeric(c, 226, "ABOR command processed OK.");
 {
        if (c->transfer != NULL) {
                numeric(c, 426, "File transfer aborted.");
                destroy_ftran(c->transfer);
        }
        numeric(c, 226, "ABOR command processed OK.");
-       return 1;
 }
 
 /*
  * cmd_dele(): Handle the DELE command (delete a file).
  */
 }
 
 /*
  * cmd_dele(): Handle the DELE command (delete a file).
  */
-int cmd_dele(struct conn * const c)
+void cmd_dele(struct conn * const c)
 {
        const char * const fname = translate_path(c, c->recv_buf);
        
 {
        const char * const fname = translate_path(c, c->recv_buf);
        
-       TRAP_ERROR(fname == NULL || unlink(fname) == -1, 550, return 1);
+       TRAP_ERROR(fname == NULL || unlink(fname) == -1, 550, return);
        numeric(c, 250, "File deleted OK.");
        numeric(c, 250, "File deleted OK.");
-       return 1;
 }
 
 /*
  * cmd_rnfr(): Handle the RNFR command (take a filename to rename from).
  */
 }
 
 /*
  * cmd_rnfr(): Handle the RNFR command (take a filename to rename from).
  */
-int cmd_rnfr(struct conn * const c)
+void cmd_rnfr(struct conn * const c)
 {
        const char * const fname = translate_path(c, c->recv_buf);
        char cwd[256];
        struct stat buf;
 
        c->rename_from[0] = '\0';
 {
        const char * const fname = translate_path(c, c->recv_buf);
        char cwd[256];
        struct stat buf;
 
        c->rename_from[0] = '\0';
-       if (fname == NULL) return 1;
+       if (fname == NULL) return;
        
        getcwd(cwd, 256);
        snprintf(c->rename_from, 256, "%s/%s", cwd, fname);
 
        /* Just check that the file exists. */
        
        getcwd(cwd, 256);
        snprintf(c->rename_from, 256, "%s/%s", cwd, fname);
 
        /* Just check that the file exists. */
-       TRAP_ERROR(lstat(c->rename_from, &buf) == -1, 550, c->rename_from[0] = '\0'; return 1);
+       TRAP_ERROR(lstat(c->rename_from, &buf) == -1, 550, c->rename_from[0] = '\0'; return);
 
        numeric(c, 350, "File exists, send RNTO.");
 
        numeric(c, 350, "File exists, send RNTO.");
-       return 1;
 }
 
 /*
  * cmd_rnto(): Handle the RNTO command (do the actual renaming).
  */
 }
 
 /*
  * cmd_rnto(): Handle the RNTO command (do the actual renaming).
  */
-int cmd_rnto(struct conn * const c)
+void cmd_rnto(struct conn * const c)
 {
        const char * const fname = translate_path(c, c->recv_buf);
 
 {
        const char * const fname = translate_path(c, c->recv_buf);
 
-       if (fname == NULL) return 1;
+       if (fname == NULL) return;
        if (c->rename_from[0] == '\0') {
                numeric(c, 503, "Please send RNFR first.");
        if (c->rename_from[0] == '\0') {
                numeric(c, 503, "Please send RNFR first.");
-               return 1;
+               return;
        }
 
        }
 
-       TRAP_ERROR(rename(c->rename_from, fname) == -1, 550, c->rename_from[0] = '\0'; return 1);
+       TRAP_ERROR(rename(c->rename_from, fname) == -1, 550, c->rename_from[0] = '\0'; return);
        c->rename_from[0] = '\0';
 
        numeric(c, 250, "File renamed successfully.");
        c->rename_from[0] = '\0';
 
        numeric(c, 250, "File renamed successfully.");
-       return 1;
 }
 
 /*
 }
 
 /*
@@ -850,18 +825,20 @@ int cmd_rnto(struct conn * const c)
  *             easy :-) (This code isn't quite easy to understand, because
  *             temp2 is used twice, in two different roles.)
  */
  *             easy :-) (This code isn't quite easy to understand, because
  *             temp2 is used twice, in two different roles.)
  */
-int cmd_mkd(struct conn * const c)
+void 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;
 
 {
        const char * const fname = translate_path(c, c->recv_buf);
        char temp[512], temp2[1024], *cdir;
        int i, j;
 
-       TRAP_ERROR(fname == NULL || mkdir(fname, 0755) == -1, 550, return 1);
+       TRAP_ERROR(fname == NULL || mkdir(fname, 0755) == -1, 550, return);
 
        chdir(fname);
        getcwd(temp2, 512);
        cdir = do_pwd(c, temp, temp2);
 
 
        chdir(fname);
        getcwd(temp2, 512);
        cdir = do_pwd(c, temp, temp2);
 
+       if (do_pwd == NULL) return;
+       
        /* double the quotes in the output */ 
        for (i = 0, j = 0; i <= strlen(cdir); i++, j++) {
                temp2[j] = cdir[i];
        /* double the quotes in the output */ 
        for (i = 0, j = 0; i <= strlen(cdir); i++, j++) {
                temp2[j] = cdir[i];
@@ -870,20 +847,18 @@ int cmd_mkd(struct conn * const c)
                }
        }
        numeric(c, 257, "\"%s\" created.", temp2);
                }
        }
        numeric(c, 257, "\"%s\" created.", temp2);
-       return 1;
 }
 
 /*
  * cmd_rmd():  Handle the RMD/XRMD command. Works just like DELE, only for
  *             directories.
  */
 }
 
 /*
  * cmd_rmd():  Handle the RMD/XRMD command. Works just like DELE, only for
  *             directories.
  */
-int cmd_rmd(struct conn * const c)
+void cmd_rmd(struct conn * const c)
 {
        const char * const fname = translate_path(c, c->recv_buf);
 
 {
        const char * const fname = translate_path(c, c->recv_buf);
 
-       TRAP_ERROR(fname == NULL || rmdir(fname) == -1, 550, return 1);
+       TRAP_ERROR(fname == NULL || rmdir(fname) == -1, 550, return);
        numeric(c, 250, "Directory deleted.");
        numeric(c, 250, "Directory deleted.");
-       return 1;
 }
 
 /*
 }
 
 /*
@@ -897,10 +872,9 @@ int cmd_rmd(struct conn * const c)
  *             to the full-screen mode, but close to no FTP clients send this
  *             command, and it would touch too much code.
  */
  *             to the full-screen mode, but close to no FTP clients send this
  *             command, and it would touch too much code.
  */
-int cmd_allo(struct conn * const c)
+void cmd_allo(struct conn * const c)
 {
        numeric(c, 202, "No storage allocation necessary.");
 {
        numeric(c, 202, "No storage allocation necessary.");
-       return 1;
 }
 
 /*
 }
 
 /*
@@ -917,7 +891,7 @@ char conn_state[5][27] = {
        "Waiting for e-mail address",
        "Waiting for password",
        "Logged in",
        "Waiting for e-mail address",
        "Waiting for password",
        "Logged in",
-       "Waiting for password",         /* actually non-existant user */
+       "Logged in",            /* non-anonymous */
 };
 
 char ftran_state[6][42] = {
 };
 
 char ftran_state[6][42] = {
@@ -930,7 +904,7 @@ char ftran_state[6][42] = {
 };
 #endif
 
 };
 #endif
 
-int cmd_stat(struct conn * const c)
+void cmd_stat(struct conn * const c)
 { 
 #if WANT_STAT
        char buf[1024];
 { 
 #if WANT_STAT
        char buf[1024];
@@ -959,13 +933,11 @@ int cmd_stat(struct conn * const c)
 
        err = send(c->sock, buf, i, 0);
                if (err == -1 && errno == EPIPE) {
 
        err = send(c->sock, buf, i, 0);
                if (err == -1 && errno == EPIPE) {
-                       destroy_conn(c);
-               return 0;
+               this->free_me = 1;
        }
 #else
        numeric(c, 502, "STAT command disabled for security reasons.");
 #endif
        }
 #else
        numeric(c, 502, "STAT command disabled for security reasons.");
 #endif
-       return 1;
 }
 
 #if HAVE_MMAP
 }
 
 #if HAVE_MMAP
@@ -1115,7 +1087,7 @@ int long_listing(char * const retbuf, const char * const pathname, const int do_
  *             long listing (of type `ls -l'). The listing work is
  *             done by do_listing(), below.
  */
  *             long listing (of type `ls -l'). The listing work is
  *             done by do_listing(), below.
  */
-int cmd_list(struct conn * const c)
+void cmd_list(struct conn * const c)
 {
        struct list_options lo;
 
 {
        struct list_options lo;
 
@@ -1124,7 +1096,6 @@ int cmd_list(struct conn * const c)
        lo.classify = 0;
 
        do_listing(c, &lo);
        lo.classify = 0;
 
        do_listing(c, &lo);
-       return 1;
 }
 
 /*
 }
 
 /*
@@ -1134,7 +1105,7 @@ int cmd_list(struct conn * const c)
  *             FTP clients don't have a clue about what they send out). 
  *             The listing work is done by do_listing(), below.
  */    
  *             FTP clients don't have a clue about what they send out). 
  *             The listing work is done by do_listing(), below.
  */    
-int cmd_nlst(struct conn * const c)
+void cmd_nlst(struct conn * const c)
 {
        struct list_options lo;
 
 {
        struct list_options lo;
 
@@ -1143,7 +1114,6 @@ int cmd_nlst(struct conn * const c)
        lo.classify = 0;
 
        do_listing(c, &lo);
        lo.classify = 0;
 
        do_listing(c, &lo);
-       return 1;
 }
 
 /*
 }
 
 /*
@@ -1187,30 +1157,17 @@ void do_listing(struct conn * const c, struct list_options * const lo)
 
 #if WANT_DCACHE
        {
 
 #if WANT_DCACHE
        {
-               struct dcache *d = NULL, *next = first_dcache->next_dcache;
-               struct stat buf;
-
-               if (stat(cwd, &buf) > -1) {
-                       /* run through the linked list */
-                       while (next != NULL) {
-                               d = next;
-                               next = d->next_dcache;
-       
-                               if (buf.st_mtime <= d->generated &&
-                                   strcmp(d->dir_name, cwd) == 0 &&
-                                   strcmp(d->pattern, ptr) == 0 &&
-                                   memcmp(&(d->lo), lo,
-                                          sizeof(struct list_options)) == 0) {
-                                       d->use_count++;
-                                       f->dir_cache = d;
-                                       f->file_data = d->dir_data;
-                                       f->size = d->dir_size;
-                                       f->dir_listing = 1;
-                                       f->pos = 0;
-                                       prepare_for_transfer(f);
-                                       return;
-                               }
-                       }
+               struct dcache *d = find_dcache(cwd, ptr, lo);
+               if (d != NULL) {
+                       d->use_count++;
+                       f->dir_cache = d;
+                       f->file_data = d->dir_data;
+                       f->size = d->dir_size;
+                       f->dir_listing = 1;
+                       f->pos = 0;
+
+                       prepare_for_transfer(f);
+                       return;
                }
        }
 #endif
                }
        }
 #endif
@@ -1445,25 +1402,23 @@ int list_core(struct conn * const c, const char * const pathname,
  * cmd_noop(): Handles the NOOP command. Does nothing, doesn't even
  *             reset the timeout.
  */
  * cmd_noop(): Handles the NOOP command. Does nothing, doesn't even
  *             reset the timeout.
  */
-int cmd_noop(struct conn * const c)
+void cmd_noop(struct conn * const c)
 {
        numeric(c, 200, "NOOP command successful.");
 {
        numeric(c, 200, "NOOP command successful.");
-       return 1;
 }
 
 /*
  * cmd_syst(): Handles the SYST command. Returns the system identification.
  */
 }
 
 /*
  * cmd_syst(): Handles the SYST command. Returns the system identification.
  */
-int cmd_syst(struct conn * const c)
+void cmd_syst(struct conn * const c)
 {
        numeric(c, 215, "UNIX Type: L%u", NBBY);
 {
        numeric(c, 215, "UNIX Type: L%u", NBBY);
-       return 1;
 }
 
 /*
  * cmd_type(): Handles the TYPE command.
  */
 }
 
 /*
  * cmd_type(): Handles the TYPE command.
  */
-int cmd_type(struct conn * const c)
+void cmd_type(struct conn * const c)
 {
 #if WANT_ASCII
        c->recv_buf[0] &= (255-32);     /* convert to upper case */
 {
 #if WANT_ASCII
        c->recv_buf[0] &= (255-32);     /* convert to upper case */
@@ -1479,13 +1434,12 @@ int cmd_type(struct conn * const c)
 #else
        numeric(c, 200, "TYPE ignored (always I)");
 #endif
 #else
        numeric(c, 200, "TYPE ignored (always I)");
 #endif
-       return 1;
 }
 
 /*
  * cmd_mode(): Handles the MODE command. Only stream mode is supported.
  */
 }
 
 /*
  * cmd_mode(): Handles the MODE command. Only stream mode is supported.
  */
-int cmd_mode(struct conn * const c)
+void cmd_mode(struct conn * const c)
 {
        c->recv_buf[0] &= (255-32);     /* convert to upper case */
        if (c->recv_buf[0] == 'S') {
 {
        c->recv_buf[0] &= (255-32);     /* convert to upper case */
        if (c->recv_buf[0] == 'S') {
@@ -1493,13 +1447,12 @@ int cmd_mode(struct conn * const c)
        } else {
                numeric(c, 504, "Unknown mode.");
        }
        } else {
                numeric(c, 504, "Unknown mode.");
        }
-       return 1;
 }
 
 /*
  * cmd_stru(): Handles the STRU command. Only file mode is supported.
  */
 }
 
 /*
  * cmd_stru(): Handles the STRU command. Only file mode is supported.
  */
-int cmd_stru(struct conn * const c)
+void cmd_stru(struct conn * const c)
 {
        c->recv_buf[0] &= (255-32);     /* convert to upper case */
        if (c->recv_buf[0] == 'F') {
 {
        c->recv_buf[0] &= (255-32);     /* convert to upper case */
        if (c->recv_buf[0] == 'F') {
@@ -1507,7 +1460,6 @@ int cmd_stru(struct conn * const c)
        } else {
                numeric(c, 504, "Unknown structure.");
        }
        } else {
                numeric(c, 504, "Unknown structure.");
        }
-       return 1;
 }
 
 /*
 }
 
 /*
@@ -1527,21 +1479,19 @@ int cmd_stru(struct conn * const c)
  *             like an error, since the error code is intended for helpful
  *             messages? :-)
  */
  *             like an error, since the error code is intended for helpful
  *             messages? :-)
  */
-int cmd_help(struct conn * const c)
+void cmd_help(struct conn * const c)
 {
        numeric(c, 414, "Sorry, no detailed help; use standard FTP commands.");
 {
        numeric(c, 414, "Sorry, no detailed help; use standard FTP commands.");
-       return 1;
 }
 
 /*
  * cmd_quit(): Handles the QUIT command, which shuts down the control
  *             and data sockets.
  */
 }
 
 /*
  * cmd_quit(): Handles the QUIT command, which shuts down the control
  *             and data sockets.
  */
-int cmd_quit(struct conn * const c)
+void cmd_quit(struct conn * const c)
 {
        numeric(c, 221, "Have a nice day!");
 {
        numeric(c, 221, "Have a nice day!");
-       destroy_conn(c);
-       return 0;
+       c->free_me = 1;
 }
 
 /*
 }
 
 /*
@@ -1550,7 +1500,7 @@ int cmd_quit(struct conn * const c)
  *             copied directly from alloc_new_conn() -- perhaps we should
  *             modularize this?
  */
  *             copied directly from alloc_new_conn() -- perhaps we should
  *             modularize this?
  */
-int cmd_rein(struct conn * const c)
+void cmd_rein(struct conn * const c)
 {
        destroy_ftran(c->transfer);
        c->buf_len = c->auth = c->rest_pos = 0;
 {
        destroy_ftran(c->transfer);
        c->buf_len = c->auth = c->rest_pos = 0;
@@ -1565,8 +1515,6 @@ int cmd_rein(struct conn * const c)
 
        time(&(c->last_transfer));
        numeric(c, 220, "BetaFTPD " VERSION " ready.");
 
        time(&(c->last_transfer));
        numeric(c, 220, "BetaFTPD " VERSION " ready.");
-
-       return 1;
 }
 
 #if DOING_PROFILING
 }
 
 #if DOING_PROFILING
@@ -1630,9 +1578,11 @@ void parse_command(struct conn *c)
 
 #if !WANT_NONROOT
                                if (h->do_setuid) {
 
 #if !WANT_NONROOT
                                if (h->do_setuid) {
+                                       setegid(c->gid);
                                        seteuid(c->uid);
                                } else {
                                        seteuid(c->uid);
                                } else {
-                                       seteuid(0);
+                                       seteuid(getuid());
+                                       setegid(getgid());
                                }
 #endif
 
                                }
 #endif
 
@@ -1645,12 +1595,24 @@ void parse_command(struct conn *c)
 
                                schar = c->recv_buf[cmlen];
                                c->recv_buf[cmlen] = 0;
 
                                schar = c->recv_buf[cmlen];
                                c->recv_buf[cmlen] = 0;
-
-                               /* result of zero means the connection is freed */
-                               if (h->callback(c)) {
+                               
+                               message_buf[0] = '\0';
+                               h->callback(c);
+                               if (message_buf[0] != '\0') {
+                                       /* send any feedback we might have */
+                                       int err = send(c->sock, message_buf, strlen(message_buf), 0);
+                                       if (err == -1 && errno == EPIPE) {
+                                               c->free_me = 1;
+                                       }
+                               }       
+                               
+                               if (!c->free_me) {
                                        c->recv_buf[cmlen] = schar;
 #if !WANT_NONROOT
                                        c->recv_buf[cmlen] = schar;
 #if !WANT_NONROOT
-                                       if (h->do_setuid) seteuid(getuid());
+                                       if (h->do_setuid) {
+                                               seteuid(getuid());
+                                               setegid(getgid());
+                                       }
 #endif
                                        remove_bytes(c, cmlen);
                                }
 #endif
                                        remove_bytes(c, cmlen);
                                }
@@ -1659,8 +1621,12 @@ void parse_command(struct conn *c)
                }
        } while ((++h)->callback != NULL);
 
                }
        } while ((++h)->callback != NULL);
 
-       numeric(c, 500, "Sorry, no such command.");
        remove_bytes(c, cmlen); 
        remove_bytes(c, cmlen); 
+       {
+               char error[] = "500 Sorry, no such command.\r\n";
+               if (send(c->sock, error, strlen(error), 0) == -1 && errno == EPIPE)
+                       destroy_conn(c);
+       }
 }
 
 /*
 }
 
 /*
@@ -1861,7 +1827,7 @@ int prepare_for_listing(struct conn * const c, char ** const ptr,
                        case 'F':
                                lo->classify = 1;
                                break;
                        case 'F':
                                lo->classify = 1;
                                break;
-                       case ' ':
+                       case '\0':
                                fptr = optr + 1;
                                *(optr--) = 0;
                                break;
                                fptr = optr + 1;
                                *(optr--) = 0;
                                break;
@@ -1872,7 +1838,7 @@ int prepare_for_listing(struct conn * const c, char ** const ptr,
        } else {
                fptr = c->recv_buf;
        }
        } else {
                fptr = c->recv_buf;
        }
-       
+
        /* then we chdir to the dir in fptr (if any) */
        tmp = fptr ? strrchr(fptr, '/') : NULL;
        if (tmp != NULL) {
        /* then we chdir to the dir in fptr (if any) */
        tmp = fptr ? strrchr(fptr, '/') : NULL;
        if (tmp != NULL) {
@@ -1885,7 +1851,16 @@ int prepare_for_listing(struct conn * const c, char ** const ptr,
        }
 
        /* if no argument, choose all files */
        }
 
        /* if no argument, choose all files */
-       if (fptr == NULL || fptr[0] == 0) fptr = "*";
+       if (fptr == NULL || fptr[0] == 0) {
+               fptr = "*";
+       } else {
+               /* we need to check if the last part is a directory (no -d switch) */
+               struct stat buf;
+               if (stat(fptr, &buf) == 0 && S_ISDIR(buf.st_mode)) {
+                       TRAP_ERROR(chdir(fptr) == -1, 550, return -1);
+                       fptr = "*";
+               }
+       }
        *ptr = fptr;
 
 #if WANT_NONROOT
        *ptr = fptr;
 
 #if WANT_NONROOT