head	1.1;
access;
symbols
	RELEASE_8_3_0:1.1;
locks; strict;
comment	@# @;


1.1
date	2012.03.13.17.02.32;	author mi;	state Exp;
branches;
next	;


desc
@@


1.1
log
@mod_ftp is a FTP Protocol module to serve httpd content over the
FTP protocol (whereever the HTTP protocol could also be used). It
provides both RETR/REST retrieval and STOR/APPE upload, using the
same user/permissions model as httpd (so it shares the same security
considerations as mod_dav plus mod_dav_fs).

WWW: http://httpd.apache.org/mod_ftp/

Feature safe:	yes
@
text
@Support real ls-like sorting options. See:
	https://issues.apache.org/bugzilla/show_bug.cgi?id=51409

--- modules/ftp/ftp_commands.c	2009-09-21 15:09:19.000000000 -0400
+++ modules/ftp/ftp_commands.c	2011-06-22 03:00:31.000000000 -0400
@@@@ -24,6 +24,8 @@@@
 #include "mod_ftp.h"
 #include "ftp_internal.h"
-#include "apr_version.h"
-#include "apr_network_io.h"
+#include <apr_version.h>
+#include <apr_getopt.h>
+#include <apr_network_io.h>
+#include <apr_strings.h>
 #include "http_vhost.h"
 
@@@@ -665,22 +667,66 @@@@ static int common_list(request_rec *r, c
     apr_status_t rv;
     apr_size_t nbytes;
-    char *varg = apr_pstrdup(r->pool, arg);
     const char *test, *sl;
     int res;
     int decend = 0;
+    apr_getopt_t *options;
+    char **argv, option;
+    const char *optval;
+    char * const *p;
+    int argc;
+    /* Sort by name ascending by default */
+    int reverse = 0;
+    enum ftp_sort sortby = SORT_BY_NAME; /* Why not SORT_NONE? */
+
+    /* TODO: improve error reporting with detailed status explanation */
+    if ((res = apr_tokenize_to_argv(arg, &argv, r->pool))) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
+	    "Attempt to tokenize string `%s' failed", arg);
+	return res;
+    }
+    for (p = argv, argc = 0; *p; p++)
+	argc++;
 
-    while (*varg == '-')
-    {
-        if (ftp_parse2(r->pool, varg, &word, &varg, FTP_KEEP_WHITESPACE)) {
-            varg = word;
-            break;
-        }
-        /* More Cowbell!  TODO: expand the accepted dash patterns */
-        if (ap_strchr(word, 'l')) {
+    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Split `%s' into %d word%s",
+        arg, argc, argc == 1 ? "" : "s");
+
+    /*
+     * apr_getopt ignores argv[0] thinking, it is the program's name,
+     * but we want it to start with it, so we shift argv and argc by one:
+     */
+    if ((res = apr_getopt_init(&options, r->pool, argc + 1, (const char **)argv - 1))) {
+        ap_log_rerror(APLOG_MARK, APLOG_ERR | APLOG_NOERRNO, 0, r,
+	    "apr_getopt_init failed");
+	return res;
+    }
+
+    /* Disable apr_getopt's own error-reporting: */
+    options->errfn = NULL;
+
+    while (apr_getopt(options, "Slrtcu", &option, &optval) != APR_EOF) {
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Checking option `%c'", option);
+        switch(option) {
+        case 'l':
             dashl = 1;
-        }
-        /* -- 'end of dash-opts' by convention allows patterns like '-*' */
-        if (ap_strchr(word + 1, '-')) {
-            break;
+            continue;
+        case 'r':
+            reverse = 1;
+            continue;
+        case 't':
+	    sortby = SORT_BY_MTIME;
+            continue;
+        case 'c':
+	    sortby = SORT_BY_CTIME;
+            continue;
+        case 'u':
+	    sortby = SORT_BY_ATIME;
+            continue;
+        case 'S':
+            sortby = SORT_BY_SIZE;
+            continue;
+        default:
+            /* TODO: communicate this back to the client too */
+            ap_log_rerror(APLOG_MARK, APLOG_WARNING | APLOG_NOERRNO, 0, r,
+                "Ignoring unrecognized listing option `%c'", option);
         }
     }
@@@@ -696,5 +748,9 @@@@ static int common_list(request_rec *r, c
     }
 
-    arg = varg;
+    arg = argv[options->ind - 1] ? argv[options->ind - 1] : "";
+
+    ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "%s-listing `%s', "
+        "sorting by %d (%sscending)", is_list ? "Long" : "Short",
+         arg ? arg : "nill!", (int)sortby, reverse ? "de" : "a");
 
     if (is_list && (ap_strchr_c(arg, '*') != NULL))
@@@@ -768,5 +824,5 @@@@ static int common_list(request_rec *r, c
 
     /* Construct the sorted array of directory contents */
-    if ((direntry = ftp_direntry_get(r, pattern)) == NULL) {
+    if ((direntry = ftp_direntry_get(r, pattern, sortby, reverse)) == NULL) {
         fc->response_notes = apr_psprintf(r->pool, FTP_MSG_NOSUCHFILE,
                                  ftp_escape_control_text(arg, r->pool));
--- modules/ftp/ftp_internal.h	2009-09-17 14:06:43.000000000 -0400
+++ modules/ftp/ftp_internal.h	2011-06-22 02:59:53.000000000 -0400
@@@@ -160,4 +160,13 @@@@ typedef enum {
 } ftp_loginlimit_t;
 
+enum ftp_sort {
+	SORT_BY_NAME,
+	SORT_BY_MTIME,
+	SORT_BY_ATIME,
+	SORT_BY_CTIME,
+	SORT_BY_SIZE,
+	SORT_NONE
+};
+
 /* Directory entry structure.  Used for directory listings */
 typedef struct ftp_direntry
@@@@ -170,4 +179,5 @@@@ typedef struct ftp_direntry
     apr_off_t size; 
     apr_off_t csize;
+    apr_time_t atime, mtime, ctime;
     apr_int32_t  nlink;
     struct ftp_direntry *child; /* For descending */
@@@@ -254,5 +264,6 @@@@ int ftp_limitlogin_loggedout(conn_rec *c
 int ftp_eprt_decode(apr_int32_t *family, char **addr, apr_port_t *port,
                     char *arg);
-struct ftp_direntry *ftp_direntry_get(request_rec *r, const char *pattern);
+struct ftp_direntry *ftp_direntry_get(request_rec *r, const char *pattern,
+                                      enum ftp_sort sortby, int reverse);
 
 void ftp_set_authorization(request_rec *r);
--- modules/ftp/ftp_util.c	2009-09-17 17:36:36.000000000 -0400
+++ modules/ftp/ftp_util.c	2011-06-22 03:05:05.000000000 -0400
@@@@ -210,4 +210,7 @@@@ static struct ftp_direntry *ftp_direntry
         dirent->size = rr->finfo.size;
         dirent->csize = rr->finfo.csize;
+        dirent->ctime = rr->finfo.ctime;
+        dirent->atime = rr->finfo.atime;
+        dirent->mtime = rr->finfo.mtime;
         dirent->modestring = apr_pstrdup(r->pool,
                                          ftp_modestring_get(
@@@@ -267,6 +270,6 @@@@ static struct ftp_direntry *ftp_direntry
  *          be greater than d2.
  */
-static int ftp_dsortf(struct ftp_direntry ** d1,
-                          struct ftp_direntry ** d2)
+static int ftp_dsortf(const struct ftp_direntry ** d1,
+                      const struct ftp_direntry ** d2)
 {
     /* Simple sort based on filename */
@@@@ -274,4 +277,32 @@@@ static int ftp_dsortf(struct ftp_direntr
 }
 
+static int ftp_dsortf_desc(const struct ftp_direntry ** d1,
+                           const struct ftp_direntry ** d2)
+{
+    /* Simple sort based on filename, descending */
+    return strcmp((*d2)->name, (*d1)->name);
+}
+
+#define FTP_DSORT_NUM(field)	\
+static int ftp_dsort_##field(const struct ftp_direntry ** d1,	\
+                             const struct ftp_direntry ** d2)	\
+{	\
+    if ((*d1)->field == (*d2)->field)	\
+        return 0;	\
+    return (*d1)->field < (*d2)->field ? 1 : -1;	\
+}	\
+static int ftp_dsort_desc_##field(const struct ftp_direntry ** d1,	\
+                                  const struct ftp_direntry ** d2)	\
+{	\
+    if ((*d1)->field == (*d2)->field)	\
+        return 0;	\
+    return (*d1)->field > (*d2)->field ? 1 : -1;	\
+}
+
+FTP_DSORT_NUM(mtime)
+FTP_DSORT_NUM(ctime)
+FTP_DSORT_NUM(atime)
+FTP_DSORT_NUM(size)
+
 /* ftp_direntry_get: Return an array of ftp_direntry structures based
  *                   on the uri stored in the request rec.  An extra
@@@@ -283,5 +310,6 @@@@ static int ftp_dsortf(struct ftp_direntr
  * Returns: The sorted array of directory entries on success, NULL otherwise
  */
-struct ftp_direntry *ftp_direntry_get(request_rec *r, const char *pattern)
+struct ftp_direntry *ftp_direntry_get(request_rec *r, const char *pattern,
+                                      enum ftp_sort sortby, int reverse)
 {
     struct ftp_direntry *p, *head, *current, **a;
@@@@ -293,4 +321,14 @@@@ struct ftp_direntry *ftp_direntry_get(re
     const char *path, *search;
 
+    int (*compar[])(const struct ftp_direntry **,
+                    const struct ftp_direntry **) = {
+        /* The order here must match the enum ftp_sort! */
+        ftp_dsortf, ftp_dsortf_desc,
+	ftp_dsort_mtime, ftp_dsort_desc_mtime,
+	ftp_dsort_atime, ftp_dsort_desc_atime,
+	ftp_dsort_ctime, ftp_dsort_desc_ctime,
+	ftp_dsort_size, ftp_dsort_desc_size
+    };
+
     /*
      * The actual search pattern, used to determine if we should recurse into
@@@@ -374,5 +412,5 @@@@ struct ftp_direntry *ftp_direntry_get(re
             const char *newpattern = apr_pstrcat(r->pool, fname,
                                                  "/*", NULL);
-            p->child = ftp_direntry_get(r, newpattern);
+            p->child = ftp_direntry_get(r, newpattern, sortby, reverse);
         }
         else {
@@@@ -396,6 +434,9 @@@@ struct ftp_direntry *ftp_direntry_get(re
         }
         num = i;
-        qsort((void *) a, num, sizeof(struct ftp_direntry *),
-              (int (*) (const void *, const void *)) ftp_dsortf);
+	if (sortby != SORT_NONE) {
+            qsort((void *) a, num, sizeof(struct ftp_direntry *),
+                  (int (*)(const void *,
+                           const void *))compar[sortby * 2 + reverse]);
+        }
 
         /* Re-construct the list from the sorted list */
@
