--- 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);