Blob Blame History Raw
--- proftpd-1.3.2a/modules/mod_facts.c.mlsd	2009-04-28 22:17:45.000000000 +0100
+++ proftpd-1.3.2a/modules/mod_facts.c	2009-09-07 14:17:39.000000000 +0100
@@ -22,7 +22,7 @@
  * resulting executable, without including the source code for OpenSSL in the
  * source distribution.
  *
- * $Id: mod_facts.c,v 1.13.2.1 2009/04/28 21:17:45 castaglia Exp $
+ * $Id: mod_facts.c,v 1.22 2009/04/09 15:59:38 castaglia Exp $
  */
 
 #include "conf.h"
@@ -227,7 +227,17 @@
     ptr = buf + buflen;
   }
 
-  snprintf(ptr, bufsz - buflen, " %s\n", info->path);
+  /* MLST entries are not sent via pr_data_xfer(), and thus we do not need
+   * to include an LF at the end; it is appended by pr_response_send_raw().
+   * But MLSD entries DO need the trailing LF, so that it can be converted
+   * into a CRLF sequence by pr_data_xfer().
+   */
+  if (strcmp(session.curr_cmd, C_MLSD) == 0) {
+    snprintf(ptr, bufsz - buflen, " %s\n", info->path);
+
+  } else {
+    snprintf(ptr, bufsz - buflen, " %s", info->path);
+  }
 
   buf[bufsz-1] = '\0';
   buflen = strlen(buf);
@@ -244,11 +254,17 @@
  * channel, wherease MLSD's output is sent via a data transfer, much like
  * LIST or NLST.
  */
-static char mlinfo_buf[PR_TUNABLE_BUFFER_SIZE];
+static char *mlinfo_buf = NULL;
+static size_t mlinfo_bufsz = 0;
 static size_t mlinfo_buflen = 0;
 
 static void facts_mlinfobuf_init(void) {
-  memset(mlinfo_buf, '\0', sizeof(mlinfo_buf));
+  if (mlinfo_buf == NULL) {
+    mlinfo_bufsz = pr_config_get_xfer_bufsz();
+    mlinfo_buf = palloc(session.pool, mlinfo_bufsz);
+  }
+
+  memset(mlinfo_buf, '\0', mlinfo_bufsz);
   mlinfo_buflen = 0;
 }
 
@@ -261,11 +277,11 @@
   /* If this buffer will exceed the capacity of mlinfo_buf, then flush
    * mlinfo_buf.
    */
-  if (buflen >= (sizeof(mlinfo_buf) - mlinfo_buflen)) {
+  if (buflen >= (mlinfo_bufsz - mlinfo_buflen)) {
     (void) facts_mlinfobuf_flush();
   }
 
-  sstrcat(mlinfo_buf, buf, sizeof(mlinfo_buf));
+  sstrcat(mlinfo_buf, buf, mlinfo_bufsz);
   mlinfo_buflen += buflen;
 }
 
@@ -358,6 +374,8 @@
   char buf[PR_TUNABLE_BUFFER_SIZE];
 
   (void) facts_mlinfo_fmt(info, buf, sizeof(buf));
+
+  /* The trailing CRLF will be added by pr_response_send_raw(). */
   pr_response_send_raw("%s", buf);
 }
 
@@ -595,7 +613,6 @@
  */
 
 MODRET facts_mff(cmd_rec *cmd) {
-  register unsigned int i;
   const char *path, *decoded_path;
   char *facts, *ptr;
 
@@ -606,18 +623,16 @@
 
   facts = cmd->argv[1];
 
-  /* The path can contain spaces; it is thus the concatenation of all of the
-   * arguments after the timestamp.
+  /* The path can contain spaces.  Thus we need to use cmd->arg, not cmd->argv,
+   * to find the path.  But cmd->arg contains the facts as well.  Thus we
+   * find the FIRST space in cmd->arg; the path is everything past that space.
    */
-  path = pstrdup(cmd->tmp_pool, cmd->argv[2]);
-  for (i = 3; i < cmd->argc; i++) {
-    path = pstrcat(cmd->tmp_pool, path, " ", cmd->argv[i], NULL);
-  }
+  ptr = strchr(cmd->arg, ' ');
+  path = pstrdup(cmd->tmp_pool, ptr + 1);
 
   decoded_path = pr_fs_decode_path(cmd->tmp_pool, path);
 
-  if (!dir_check(cmd->tmp_pool, cmd->argv[0], cmd->group, (char *) decoded_path,
-      NULL)) {
+  if (!dir_check(cmd->tmp_pool, cmd, cmd->group, (char *) decoded_path, NULL)) {
     pr_log_debug(DEBUG4, MOD_FACTS_VERSION ": %s command denied by <Limit>",
       cmd->argv[0]);
     pr_response_add_err(R_550, _("Unable to handle command"));
@@ -739,7 +754,6 @@
 }
 
 MODRET facts_mfmt(cmd_rec *cmd) {
-  register unsigned int i;
   const char *path, *decoded_path;
   char *timestamp, *ptr;
   int res;
@@ -751,18 +765,16 @@
 
   timestamp = cmd->argv[1];
 
-  /* The path can contain spaces; it is thus the concatenation of all of the
-   * arguments after the timestamp.
+  /* The path can contain spaces.  Thus we need to use cmd->arg, not cmd->argv,
+   * to find the path.  But cmd->arg contains the facts as well.  Thus we
+   * find the FIRST space in cmd->arg; the path is everything past that space.
    */
-  path = pstrdup(cmd->tmp_pool, cmd->argv[2]);
-  for (i = 3; i < cmd->argc; i++) {
-    path = pstrcat(cmd->tmp_pool, path, " ", cmd->argv[i], NULL);
-  }
+  ptr = strchr(cmd->arg, ' ');
+  path = pstrdup(cmd->tmp_pool, ptr + 1);
 
   decoded_path = pr_fs_decode_path(cmd->tmp_pool, path);
 
-  if (!dir_check(cmd->tmp_pool, cmd->argv[0], cmd->group, (char *) decoded_path,
-      NULL)) {
+  if (!dir_check(cmd->tmp_pool, cmd, cmd->group, (char *) decoded_path, NULL)) {
     pr_log_debug(DEBUG4, MOD_FACTS_VERSION ": %s command denied by <Limit>",
       cmd->argv[0]);
     pr_response_add_err(R_550, _("Unable to handle command"));
@@ -820,33 +832,28 @@
   DIR *dirh;
   struct dirent *dent;
 
-  if (cmd->argc > 2) {
-    pr_response_add_err(R_501, _("Invalid number of arguments"));
-    return PR_ERROR(cmd);
-  }
-
   if (cmd->argc != 1) {
-    path = cmd->argv[1];
+    path = pstrdup(cmd->tmp_pool, cmd->arg);
     decoded_path = pr_fs_decode_path(cmd->tmp_pool, path);
 
   } else {
     decoded_path = path = pr_fs_getcwd();
   }
 
-  if (!dir_check(cmd->tmp_pool, cmd->argv[0], cmd->group, (char *) decoded_path,
-      NULL)) {
+  if (!dir_check(cmd->tmp_pool, cmd, cmd->group, (char *) decoded_path, NULL)) {
     pr_log_debug(DEBUG4, MOD_FACTS_VERSION ": %s command denied by <Limit>",
       cmd->argv[0]);
     pr_response_add_err(R_550, _("Unable to handle command"));
     return PR_ERROR(cmd);
   }
 
-  /* RFC3659 explicitly does NOT support glob characters. */
+  /* RFC3659 explicitly does NOT support glob characters.  So warn about
+   * this, but let the command continue as is.  We don't actually call
+   * glob(3) here, so no expansion will occur.
+   */
   if (strpbrk(decoded_path, "{[*?") != NULL) {
-    pr_log_debug(DEBUG2, MOD_FACTS_VERSION ": unable to handle MLSD command: "
-      "target '%s' contains glob characters", decoded_path);
-    pr_response_add_err(R_550, _("Unable to handle command"));
-    return PR_ERROR(cmd);
+    pr_log_debug(DEBUG9, MOD_FACTS_VERSION ": glob characters in MLSD ('%s') "
+      "ignored", decoded_path);
   }
 
   /* Make sure that the given path is actually a directory. */
@@ -870,11 +877,11 @@
     int xerrno = errno;
 
     pr_trace_msg("fileperms", 1, "MLSD, user '%s' (UID %lu, GID %lu): "
-      "error reading directory '%s': %s", session.user,
+      "error opening directory '%s': %s", session.user,
       (unsigned long) session.uid, (unsigned long) session.gid,
       decoded_path, strerror(xerrno));
 
-    pr_response_add_err(R_550, _("'%s' cannot be listed"), path);
+    pr_response_add_err(R_550, "%s: %s", path, strerror(xerrno));
     return PR_ERROR(cmd);
   }
 
@@ -899,16 +906,14 @@
     /* Check that the file can be listed. */
     abs_path = dir_realpath(cmd->tmp_pool, rel_path);
     if (abs_path) {
-      res = dir_check(cmd->tmp_pool, cmd->argv[0], cmd->group, abs_path,
-        &hidden);
+      res = dir_check(cmd->tmp_pool, cmd, cmd->group, abs_path, &hidden);
       
     } else {
       abs_path = dir_canonical_path(cmd->tmp_pool, rel_path);
       if (abs_path == NULL)
         abs_path = rel_path;
 
-      res = dir_check_canon(cmd->tmp_pool, cmd->argv[0], cmd->group, abs_path,
-        &hidden);
+      res = dir_check_canon(cmd->tmp_pool, cmd, cmd->group, abs_path, &hidden);
     }
 
     if (!res || hidden) {
@@ -955,20 +960,15 @@
   const char *path, *decoded_path;
   struct mlinfo info;
 
-  if (cmd->argc > 2) {
-    pr_response_add_err(R_501, _("Invalid number of arguments"));
-    return PR_ERROR(cmd);
-  }
-
   if (cmd->argc != 1) {
-    path = cmd->argv[1];
+    path = pstrdup(cmd->tmp_pool, cmd->arg);
     decoded_path = pr_fs_decode_path(cmd->tmp_pool, path);
 
   } else {
     decoded_path = path = pr_fs_getcwd();
   }
 
-  if (!dir_check(cmd->tmp_pool, cmd->argv[0], cmd->group, (char *) decoded_path,
+  if (!dir_check(cmd->tmp_pool, cmd, cmd->group, (char *) decoded_path,
       &hidden)) {
     pr_log_debug(DEBUG4, MOD_FACTS_VERSION ": %s command denied by <Limit>",
       cmd->argv[0]);
@@ -1155,6 +1155,7 @@
 
   pr_feat_add("MFF modify;UNIX.group;UNIX.mode;");
   pr_feat_add("MFMT");
+  pr_feat_add("TVFS");
 
   facts_mlst_feat_add(session.pool);