]> git.sesse.net Git - betaftpd/blobdiff - cmds.c
Updated the version number to 0.0.8pre18 (forgot to do so upon the release of 0.0...
[betaftpd] / cmds.c
diff --git a/cmds.c b/cmds.c
index a8453def6aacbc4f01c3076a0fae9611cb0382ea..500d6c28e4d20e900fc5afafbcfd3ec34b90d29d 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
-    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,
 #include <config.h>
 #endif
 
-#if HAVE_SYS_TYPES_H
-#include <sys/types.h>
-#endif
-
 #if HAVE_STROPTS_H
 #include <stropts.h>
 #endif
 #include <fcntl.h>
 #endif
 
-#if HAVE_PWD_H
-#include <pwd.h>
-#endif
-
 #if HAVE_GRP_H
 #include <grp.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
 #include <sys/conf.h>
 #endif
 
-#if HAVE_NETINET_IN_H
-#include <netinet/in.h>
-#endif
-
 #if HAVE_SHADOW_H
 #include <shadow.h>
 #endif
 #include <cmds.h>
 #include <nonroot.h>
 
+#if WANT_DCACHE
+#include <dcache.h>
+#endif
+
 #define lstat stat
 
 extern struct conn *first_conn;
@@ -985,9 +973,14 @@ int _mwrite(const char * const buf, const struct ftran * const f,
 
 /*
  * 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));
+#else
+#define mwrite(buf, count) write(f->local_file, buf, count);
+#endif
 
 /*
  * long_listing():
@@ -1058,9 +1051,9 @@ int long_listing(char * const retbuf, const char * const pathname, const int do_
                        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
-               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
@@ -1078,7 +1071,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);
-
+               i = strlen(temp);
+       
 #if 0
                /*
                 * vim needs this extra character for some reason... It's too 
@@ -1109,7 +1103,7 @@ int cmd_list(struct conn * const c)
 {
        struct list_options lo;
 
-/*     lo.recursive = 0; */
+       lo.recursive = 0;
        lo.long_listing = 1;
        lo.classify = 0;
 
@@ -1128,7 +1122,7 @@ int cmd_nlst(struct conn * const c)
 {
        struct list_options lo;
 
-/*     lo.recursive = 0; */
+       lo.recursive = 0;
        lo.long_listing = 0;
        lo.classify = 0;
 
@@ -1177,30 +1171,17 @@ void do_listing(struct conn * const c, struct list_options * const lo)
 
 #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
@@ -1213,30 +1194,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);
-               list_core(c, ptr, lo, size);
+               list_core(c, ptr, "", lo, size, 0);
        }
 #else
-       list_core(c, ptr, lo);
+       list_core(c, ptr, "", lo);
 #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
@@ -1273,8 +1238,8 @@ int get_num_files(struct conn * const c, const char * const pathname,
                return -1;
        }
 
-#if 0  /* the rest of the code doesn't support recursion yet */
        if (lo->recursive) {
+               int i;
                        for (i = 0; i < pglob.gl_pathc; i++) {
                        char *temp = pglob.gl_pathv[i];
                        struct stat buf;
@@ -1287,7 +1252,8 @@ int get_num_files(struct conn * const c, const char * const pathname,
                        }
                }
        }
-#endif
+
+       globfree(&pglob);
 
        return num_files;
 }
@@ -1302,20 +1268,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...
  *
+ *             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.
  */
-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
-               , const int size
+               , const int size, int pos
 #endif
                )
 {
        int i;
        glob_t pglob;
-#if HAVE_MMAP
-       int pos = 0;
-#endif
        struct ftran * const f = c->transfer;
 
         /*
@@ -1327,12 +1293,28 @@ void list_core(struct conn * const c, const char * const pathname,
 #ifdef GLOB_NOMATCH
        case GLOB_NOMATCH:
 #endif
-                break;
+                break;         /* note: break, not return */
         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;
@@ -1344,12 +1326,9 @@ void list_core(struct conn * const c, const char * const pathname,
                                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);
-#else
-               write(f->local_file, temp, i);
-#endif
        }
 
        for (i = 0; i < pglob.gl_pathc; i++) {
@@ -1380,15 +1359,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);
-#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
+                               chdir("..");
+                       }
+               }
        }
 
 #if HAVE_MMAP
@@ -1398,6 +1405,11 @@ void list_core(struct conn * const c, const char * const pathname,
 #endif
 
        globfree(&pglob);
+#if HAVE_MMAP
+       return pos;
+#else
+       return 0;
+#endif
 }
 
 /*
@@ -1442,22 +1454,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)
 {
-       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;
 }
 
 /*
- * 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)
 {
-       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;
 }
 
@@ -1803,11 +1823,9 @@ int prepare_for_listing(struct conn * const c, char ** const ptr,
        if (optr != NULL) {
                while (*++optr) {
                        switch (*optr & (255-32)) {     /* uppercase */
-#if 0
                        case 'R':       /* actually case sensitive... */
                                lo->recursive = 1;
                                break;
-#endif
                        case 'L':
                                lo->long_listing = 1;
                                break;
@@ -1838,7 +1856,16 @@ int prepare_for_listing(struct conn * const c, char ** const ptr,
        }
 
        /* 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