]> git.sesse.net Git - betaftpd/blobdiff - cmds.c
Fixed the order of a leftover seteuid()/setegid() pair.
[betaftpd] / cmds.c
diff --git a/cmds.c b/cmds.c
index 131df3403bf9bf32e6050da978da4e55dab1baf2..cd8cfbc610aeff1b4755832f696a4065516b0b94 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
 #include <cmds.h>
 #include <nonroot.h>
 
 #include <cmds.h>
 #include <nonroot.h>
 
+#if WANT_DCACHE
+#include <dcache.h>
+#endif
+
 #define lstat stat
 
 extern struct conn *first_conn;
 #define lstat stat
 
 extern struct conn *first_conn;
@@ -317,6 +309,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;
        }
@@ -336,11 +329,15 @@ 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 */
 
+       /* root should not be allowed to FTP */
+       if (c->uid == 0) {
+               c->auth = 0;
+       }
        if (c->auth == 0) {
                numeric(c, 530, "Login incorrect.");
        } else {
        if (c->auth == 0) {
                numeric(c, 530, "Login incorrect.");
        } else {
@@ -348,6 +345,7 @@ 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.");
        }
        return 1;
                numeric(c, 230, "User logged in.");
        }
        return 1;
@@ -414,9 +412,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
 
@@ -802,7 +802,7 @@ int cmd_rnfr(struct conn * const c)
        snprintf(c->rename_from, 256, "%s/%s", cwd, fname);
 
        /* Just check that the file exists. */
        snprintf(c->rename_from, 256, "%s/%s", cwd, fname);
 
        /* Just check that the file exists. */
-       TRAP_ERROR(lstat(c->rename_from, &buf) == -1, 550, return 1);
+       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;
 
        numeric(c, 350, "File exists, send RNTO.");
        return 1;
@@ -821,9 +821,10 @@ int cmd_rnto(struct conn * const c)
                return 1;
        }
 
                return 1;
        }
 
-       TRAP_ERROR(rename(c->rename_from, fname) == -1, 550, return 1);
+       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 successfulyy.");
+       numeric(c, 250, "File renamed successfully.");
        return 1;
 }
 
        return 1;
 }
 
@@ -908,7 +909,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] = {
@@ -932,11 +933,19 @@ int cmd_stat(struct conn * const c)
                            "     BetaFTPD version " VERSION " (http://members.xoom.com/sneeze/betaftpd.html)\r\n"
                            "     Connected to %s\r\n"
                            "     Control connection state: %s\r\n"
                            "     BetaFTPD version " VERSION " (http://members.xoom.com/sneeze/betaftpd.html)\r\n"
                            "     Connected to %s\r\n"
                            "     Control connection state: %s\r\n"
+#if WANT_ASCII
+                           "     TYPE: %s; STRUcture: File; transfer MODE: Stream\r\n"
+#else
                            "     TYPE: Image; STRUcture: File; transfer MODE: Stream\r\n"
                            "     TYPE: Image; STRUcture: File; transfer MODE: Stream\r\n"
+#endif
                            "     Data connection state: %s\r\n"
                            "211 End of status\r\n",
                                inet_ntoa(((struct sockaddr_in *)(&(c->addr)))->sin_addr),
                            "     Data connection state: %s\r\n"
                            "211 End of status\r\n",
                                inet_ntoa(((struct sockaddr_in *)(&(c->addr)))->sin_addr),
-                               conn_state[c->auth], (f) ? ftran_state[f->state] : ftran_state[0]);
+                               conn_state[c->auth],
+#if WANT_ASCII
+                               (c->ascii_mode == 1) ? "ASCII, FORM: Nonprint" : "Image",
+#endif
+                               (f) ? ftran_state[f->state] : ftran_state[0]);
 
        i = strlen(buf);
 
 
        i = strlen(buf);
 
@@ -972,9 +981,14 @@ int _mwrite(const char * const buf, const struct ftran * const f,
 
 /*
  * mwrite:     This is a short_hand define, making calls to _mwrite() very
 
 /*
  * mwrite:     This is a short_hand define, making calls to _mwrite() very
- *             similiar to calls to write().
+ *             similiar to calls to write(). It works both with and without
+ *             mmap().
  */
  */
+#if HAVE_MMAP
 #define mwrite(buf, count) pos = _mwrite((buf), (f), (pos), (count), (size));
 #define mwrite(buf, count) pos = _mwrite((buf), (f), (pos), (count), (size));
+#else
+#define mwrite(buf, count) write(f->local_file, buf, count);
+#endif
 
 /*
  * long_listing():
 
 /*
  * long_listing():
@@ -1040,14 +1054,14 @@ int long_listing(char * const retbuf, const char * const pathname, const int do_
 #if WANT_NONROOT
                char rights[16];
 
 #if WANT_NONROOT
                char rights[16];
 
-               if (nr_check_permission(0, pathname, 0, (buf.st_mode & S_IFDIR), rights) == -1) {
+               if (nr_check_permission(0, pathname, 0, (S_ISDIR(buf.st_mode)), rights) == -1) {
                        /* no permission to even see this file */
                        return 0;
                }
 
                        /* no permission to even see this file */
                        return 0;
                }
 
-               i = snprintf(temp, 1024, "%c%s %3u %-8s %-8s %8lu %12s %s\r\n",
+               snprintf(temp, 1024, "%c%s %3u %-8s %-8s %8lu %12s %s\r\n",
 #else
 #else
-               i = snprintf(temp, 1024, "%c%c%c%c%c%c%c%c%c%c %3u %-8s %-8s %8lu %12s %s",
+               snprintf(temp, 1024, "%c%c%c%c%c%c%c%c%c%c %3u %-8s %-8s %8lu %12s %s",
 #endif
                        decode_mode(buf.st_mode),
 #if WANT_NONROOT
 #endif
                        decode_mode(buf.st_mode),
 #if WANT_NONROOT
@@ -1065,7 +1079,8 @@ int long_listing(char * const retbuf, const char * const pathname, const int do_
 #endif
                        buf.st_nlink, username, groupname,
                        (unsigned long)(buf.st_size), newd, pathname);
 #endif
                        buf.st_nlink, username, groupname,
                        (unsigned long)(buf.st_size), newd, pathname);
-
+               i = strlen(temp);
+       
 #if 0
                /*
                 * vim needs this extra character for some reason... It's too 
 #if 0
                /*
                 * vim needs this extra character for some reason... It's too 
@@ -1096,7 +1111,7 @@ int cmd_list(struct conn * const c)
 {
        struct list_options lo;
 
 {
        struct list_options lo;
 
-/*     lo.recursive = 0; */
+       lo.recursive = 0;
        lo.long_listing = 1;
        lo.classify = 0;
 
        lo.long_listing = 1;
        lo.classify = 0;
 
@@ -1115,7 +1130,7 @@ int cmd_nlst(struct conn * const c)
 {
        struct list_options lo;
 
 {
        struct list_options lo;
 
-/*     lo.recursive = 0; */
+       lo.recursive = 0;
        lo.long_listing = 0;
        lo.classify = 0;
 
        lo.long_listing = 0;
        lo.classify = 0;
 
@@ -1164,30 +1179,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;
+               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;
 
 
-               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;
-                               }
-                       }
+                       prepare_for_transfer(f);
+                       return;
                }
        }
 #endif
                }
        }
 #endif
@@ -1200,30 +1202,14 @@ void do_listing(struct conn * const c, struct list_options * const lo)
                size = num_files * 160;
                f->file_data = malloc(size + 1);
                TRAP_ERROR(f->file_data == NULL, 550, return);
                size = num_files * 160;
                f->file_data = malloc(size + 1);
                TRAP_ERROR(f->file_data == NULL, 550, return);
-               list_core(c, ptr, lo, size);
+               list_core(c, ptr, "", lo, size, 0);
        }
 #else
        }
 #else
-       list_core(c, ptr, lo);
+       list_core(c, ptr, "", lo);
 #endif
 
 #if WANT_DCACHE
 #endif
 
 #if WANT_DCACHE
-       /* populate the directory listing cache */
-       {
-               struct stat buf;
-               struct dcache *d = alloc_new_dcache();
-               if (d != NULL && stat(cwd, &buf) > -1) {
-                       d->use_count++;
-                       f->dir_cache = d;
-                       d->dir_data = f->file_data;
-                       d->dir_size = f->size;
-                       d->generated = buf.st_mtime;
-
-                       strcpy(d->dir_name, cwd);
-                       strncpy(d->pattern, ptr, 255);
-                       d->pattern[255] = 0;
-                       d->lo = *lo;
-               }
-       }
+       populate_dcache(f, cwd, ptr, lo);
 #endif
 
 #if HAVE_MMAP
 #endif
 
 #if HAVE_MMAP
@@ -1260,8 +1246,8 @@ int get_num_files(struct conn * const c, const char * const pathname,
                return -1;
        }
 
                return -1;
        }
 
-#if 0  /* the rest of the code doesn't support recursion yet */
        if (lo->recursive) {
        if (lo->recursive) {
+               int i;
                        for (i = 0; i < pglob.gl_pathc; i++) {
                        char *temp = pglob.gl_pathv[i];
                        struct stat buf;
                        for (i = 0; i < pglob.gl_pathc; i++) {
                        char *temp = pglob.gl_pathv[i];
                        struct stat buf;
@@ -1274,7 +1260,8 @@ int get_num_files(struct conn * const c, const char * const pathname,
                        }
                }
        }
                        }
                }
        }
-#endif
+
+       globfree(&pglob);
 
        return num_files;
 }
 
        return num_files;
 }
@@ -1289,20 +1276,20 @@ int get_num_files(struct conn * const c, const char * const pathname,
  *             under 80 for long listings, and a little under 160 for
  *             short listings), the list will be truncated. Fix...
  *
  *             under 80 for long listings, and a little under 160 for
  *             short listings), the list will be truncated. Fix...
  *
+ *             The return value only makes sense if mmap()'ing, since it
+ *             returns the number of bytes written into the buffer.
+ *
  *             This function is rather long.
  */
  *             This function is rather long.
  */
-void list_core(struct conn * const c, const char * const pathname,
-              struct list_options * const lo
+int list_core(struct conn * const c, const char * const pathname,
+             char * const disp_pathname, struct list_options * const lo
 #if HAVE_MMAP
 #if HAVE_MMAP
-               , const int size
+               , const int size, int pos
 #endif
                )
 {
        int i;
        glob_t pglob;
 #endif
                )
 {
        int i;
        glob_t pglob;
-#if HAVE_MMAP
-       int pos = 0;
-#endif
        struct ftran * const f = c->transfer;
 
         /*
        struct ftran * const f = c->transfer;
 
         /*
@@ -1314,12 +1301,28 @@ void list_core(struct conn * const c, const char * const pathname,
 #ifdef GLOB_NOMATCH
        case GLOB_NOMATCH:
 #endif
 #ifdef GLOB_NOMATCH
        case GLOB_NOMATCH:
 #endif
-                break;
+                break;         /* note: break, not return */
         default:
                 numeric(c, 550, strerror(EACCES));
         default:
                 numeric(c, 550, strerror(EACCES));
-                return;
+#if HAVE_MMAP
+               return pos;
+#else
+                return 0;
+#endif
         }
 
         }
 
+       if (lo->recursive) {
+               if (disp_pathname[0] == '\0') {
+                       mwrite(".:\r\n", 4);
+               } else {
+                       char temp[1024];
+                       int i;
+
+                       snprintf(temp, 1024, "%s:\r\n", disp_pathname);
+                       i = strlen(temp);
+                       mwrite(temp, i);
+               }
+       }
        if (lo->long_listing) {
                /* FIX: we may get too high total number if we are running nonroot! */
                struct stat buf;
        if (lo->long_listing) {
                /* FIX: we may get too high total number if we are running nonroot! */
                struct stat buf;
@@ -1331,12 +1334,9 @@ void list_core(struct conn * const c, const char * const pathname,
                                total += buf.st_blocks;
                        }
                }
                                total += buf.st_blocks;
                        }
                }
-               i = snprintf(temp, 1024, "total %lu\r\n", total >> 1); 
-#if HAVE_MMAP
+               snprintf(temp, 1024, "total %lu\r\n", total >> 1);
+               i = strlen(temp);
                mwrite(temp, i);
                mwrite(temp, i);
-#else
-               write(f->local_file, temp, i);
-#endif
        }
 
        for (i = 0; i < pglob.gl_pathc; i++) {
        }
 
        for (i = 0; i < pglob.gl_pathc; i++) {
@@ -1367,15 +1367,43 @@ void list_core(struct conn * const c, const char * const pathname,
                        }
                }
 
                        }
                }
 
-               /* support recursion here some day... */
-
-#if HAVE_MMAP
                mwrite(buf, strlen(buf));
                mwrite("\r\n", 2);
                mwrite(buf, strlen(buf));
                mwrite("\r\n", 2);
-#else
-               write(f->local_file, buf, strlen(buf));
-               write(f->local_file, "\r\n", 2);
+       }
+
+       /*
+        * If recursion is on, dive into any subdirectories now -- note
+        * that each entry is stat()'ed twice, hopefully the OS will manage,
+        * and we've got our own dcache anyways -- this could be fixed at
+        * the expense of some memory, consider for later inclusion.
+        */
+       if (lo->recursive) {
+               for (i = 0; i < pglob.gl_pathc; i++) {
+                       struct stat buf;
+                       const char * const temp = pglob.gl_pathv[i];
+
+                       /* don't dive into `.' or `..' */
+                        if (lstat(temp, &buf) != -1 && S_ISDIR(buf.st_mode) &&
+                               (temp[0] != '.' || (temp[1] != '.' && temp[1] != '\0'))) {
+                               char tmp2[1024];
+
+                               mwrite("\r\n", 2);
+
+                               /* attach the pathname to the end of the displayed path */
+                               if (disp_pathname[0] == '\0') {
+                                       snprintf(tmp2, 1024, "%s", temp);
+                               } else {
+                                       snprintf(tmp2, 1024, "%s/%s", disp_pathname, temp);
+                               }
+
+                               chdir(temp);
+                               pos = list_core(c, "*", tmp2, lo, 
+#if HAVE_MMAP
+                                       size, pos);
 #endif
 #endif
+                               chdir("..");
+                       }
+               }
        }
 
 #if HAVE_MMAP
        }
 
 #if HAVE_MMAP
@@ -1385,6 +1413,11 @@ void list_core(struct conn * const c, const char * const pathname,
 #endif
 
        globfree(&pglob);
 #endif
 
        globfree(&pglob);
+#if HAVE_MMAP
+       return pos;
+#else
+       return 0;
+#endif
 }
 
 /*
 }
 
 /*
@@ -1429,22 +1462,30 @@ int cmd_type(struct conn * const c)
 }
 
 /*
 }
 
 /*
- * cmd_mode(): Handles the MODE command. We always use stream mode,
- *             so the argument is ignored.
+ * cmd_mode(): Handles the MODE command. Only stream mode is supported.
  */
 int cmd_mode(struct conn * const c)
 {
  */
 int cmd_mode(struct conn * const c)
 {
-       numeric(c, 200, "MODE ignored (always S)");
+       c->recv_buf[0] &= (255-32);     /* convert to upper case */
+       if (c->recv_buf[0] == 'S') {
+               numeric(c, 200, "Mode is STREAM.");
+       } else {
+               numeric(c, 504, "Unknown mode.");
+       }
        return 1;
 }
 
 /*
        return 1;
 }
 
 /*
- * cmd_stru(): Handles the STRU command. We always use file mode,
- *             so the argument is ignored.
+ * cmd_stru(): Handles the STRU command. Only file mode is supported.
  */
 int cmd_stru(struct conn * const c)
 {
  */
 int cmd_stru(struct conn * const c)
 {
-       numeric(c, 200, "STRU ignored (always F)");
+       c->recv_buf[0] &= (255-32);     /* convert to upper case */
+       if (c->recv_buf[0] == 'F') {
+               numeric(c, 200, "Structure is FILE.");
+       } else {
+               numeric(c, 504, "Unknown structure.");
+       }
        return 1;
 }
 
        return 1;
 }
 
@@ -1518,7 +1559,7 @@ int cmd_rein(struct conn * const c)
  *             down without clearing any sockets etc. In other words:
  *             Don't use it on a production site.
  */
  *             down without clearing any sockets etc. In other words:
  *             Don't use it on a production site.
  */
-void cmd_exit(struct conn * const c)
+int cmd_exit(struct conn * const c)
 {
        while (first_conn->next_conn)
                destroy_conn(first_conn->next_conn);
 {
        while (first_conn->next_conn)
                destroy_conn(first_conn->next_conn);
@@ -1569,8 +1610,10 @@ void parse_command(struct conn *c)
 #if !WANT_NONROOT
                                if (h->do_setuid) {
                                        seteuid(c->uid);
 #if !WANT_NONROOT
                                if (h->do_setuid) {
                                        seteuid(c->uid);
+                                       setegid(c->gid);
                                } else {
                                } else {
-                                       seteuid(0);
+                                       seteuid(getuid());
+                                       setegid(getgid());
                                }
 #endif
 
                                }
 #endif
 
@@ -1588,7 +1631,10 @@ void parse_command(struct conn *c)
                                if (h->callback(c)) {
                                        c->recv_buf[cmlen] = schar;
 #if !WANT_NONROOT
                                if (h->callback(c)) {
                                        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);
                                }
@@ -1652,13 +1698,13 @@ void prepare_for_transfer(struct ftran *f)
  *             The most common cases are put first, for speed :-)
  */
 char decode_mode(mode_t mode) {
  *             The most common cases are put first, for speed :-)
  */
 char decode_mode(mode_t mode) {
-       if (mode & S_IFREG)  return '-';
-       if (mode & S_IFDIR)  return 'd';
-       if (mode & S_IFLNK)  return 'l';
-       if (mode & S_IFBLK)  return 'b';
-       if (mode & S_IFCHR)  return 'c';
-       if (mode & S_IFSOCK) return 's';
-       if (mode & S_IFIFO)  return 'f';
+       if (S_ISREG(mode))  return '-';
+       if (S_ISDIR(mode))  return 'd';
+       if (S_ISLNK(mode))  return 'l';
+       if (S_ISBLK(mode))  return 'b';
+       if (S_ISCHR(mode))  return 'c';
+       if (S_ISSOCK(mode)) return 's';
+       if (S_ISFIFO(mode))  return 'f';
 
        return '-';
 }
 
        return '-';
 }
@@ -1733,9 +1779,9 @@ int do_openfile(struct conn * const c, char * const path,
 #if WANT_UPLOAD
        if ((flags & O_CREAT) == 0) {
 #endif
 #if WANT_UPLOAD
        if ((flags & O_CREAT) == 0) {
 #endif
-               stat(ptr, &buf);
+               TRAP_ERROR(stat(ptr, &buf) == -1, 550, return -2);
                if (!S_ISREG(buf.st_mode)) {
                if (!S_ISREG(buf.st_mode)) {
-                       numeric(c, 550, "%s: Not a plain file.", ptr);
+                       numeric(c, 550, "Not a plain file.", ptr);
                        return -2;
                }
 #if WANT_UPLOAD
                        return -2;
                }
 #if WANT_UPLOAD
@@ -1790,11 +1836,9 @@ int prepare_for_listing(struct conn * const c, char ** const ptr,
        if (optr != NULL) {
                while (*++optr) {
                        switch (*optr & (255-32)) {     /* uppercase */
        if (optr != NULL) {
                while (*++optr) {
                        switch (*optr & (255-32)) {     /* uppercase */
-#if 0
                        case 'R':       /* actually case sensitive... */
                                lo->recursive = 1;
                                break;
                        case 'R':       /* actually case sensitive... */
                                lo->recursive = 1;
                                break;
-#endif
                        case 'L':
                                lo->long_listing = 1;
                                break;
                        case 'L':
                                lo->long_listing = 1;
                                break;
@@ -1825,7 +1869,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