You are viewing a plain text version of this content. The canonical link for it is here.
Posted to mod_ftp-commits@incubator.apache.org by ji...@apache.org on 2005/10/06 13:17:04 UTC

svn commit: r306631 [6/8] - in /incubator/mod_ftp/trunk: ./ conf/ docs/ include/ modules/ patches/ src/ tests/ tests/conf/ tests/logs/ tests/tests/ utils/ utils/ftp_proxy/ utils/static_build/ utils/stresstest/

Added: incubator/mod_ftp/trunk/src/ftp_protocol.c
URL: http://svn.apache.org/viewcvs/incubator/mod_ftp/trunk/src/ftp_protocol.c?rev=306631&view=auto
==============================================================================
--- incubator/mod_ftp/trunk/src/ftp_protocol.c (added)
+++ incubator/mod_ftp/trunk/src/ftp_protocol.c Thu Oct  6 06:16:28 2005
@@ -0,0 +1,864 @@
+/* Copyright 1999-2005 The Apache Software Foundation or its licensors, as
+ * applicable.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Original Copyright (c) Covalent Technologies 2001-2005
+ *
+ * FTP Protocol module for Apache 2.0
+ */
+
+#include "mod_ftp.h"
+#include "ftp_config.h"
+
+/* for socket_atmark - move to apr with apr_socket_atmark */
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_SOCKIO_H
+#include <sys/sockio.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+/* Min # of bytes to allocate when reading a request line */
+#define MIN_LINE_ALLOC 512
+
+
+/* 
+ * Moving to apr shortly, this function returns APR_SUCCESS and
+ * sets atmark to true if at the mark, or false when not.
+ */
+apr_status_t socket_atmark(apr_socket_t *sock, int *atmark)
+{
+    apr_os_sock_t sd;
+    apr_status_t  rv;
+#ifdef WIN32
+    u_long oobmark;
+#else
+    int oobmark;
+#endif
+
+    rv = apr_os_sock_get(&sd, sock);
+
+    if (rv != APR_SUCCESS) {
+        return rv;
+    }
+
+#ifndef SIOCATMARK
+#error FAILED TO COMPILE, SIOCATMARK must be supported!
+#else
+    /* if (apr_socket_atmark(sd->socketdes)) */
+#ifdef WIN32
+    if (ioctlsocket(sd, SIOCATMARK, (void*) &oobmark) < 0)
+#else
+    if (ioctl(sd, SIOCATMARK, (void*) &oobmark) < 0)
+#endif
+        return apr_get_netos_error();
+
+    *atmark = (oobmark != 0);
+#endif
+
+    return APR_SUCCESS;
+}
+
+/*
+ * ftp_read_line reads ahead one line from the control channel.
+ *
+ */
+static apr_status_t ftp_read_line(char **result, apr_size_t *bytes_read,
+                                  apr_pool_t *pool,
+                                  apr_bucket_brigade *bb,
+                                  ap_filter_t *input_filters,
+                                  int block, ftp_connection *fc)
+{
+    char *last_char = NULL;
+    apr_status_t rv;
+    apr_bucket_pool *pb;
+    apr_bucket *pe;
+    apr_bucket *e;
+    char *pbuf;
+    char *pos;
+
+    /* We manage a leading (intially empty) pool bucket that 
+     * we will use to concatinate the line over (possibly)
+     * multiple non-blocking invocations of ftp_read_line()
+     */
+
+    if (APR_BRIGADE_EMPTY(bb)) {
+        pe = apr_bucket_pool_create(apr_palloc(pool, MIN_LINE_ALLOC), 0,
+                                    pool, input_filters->c->bucket_alloc);
+        pb = pe->data;
+        pb->heap.alloc_len = MIN_LINE_ALLOC;
+        pbuf = (char*)pb->base;
+#ifdef FTP_TRACE
+        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0, 
+                     fc->connection->base_server, "FTP[%d] frl: empty bb",
+                     (int) getpid());
+#endif
+    }
+    else {
+        pe = APR_BRIGADE_FIRST(bb);
+        pb = pe->data;
+        if (APR_BUCKET_IS_POOL(pe) && pb->pool)
+            pbuf = (char*)pb->base;
+        else if (APR_BUCKET_IS_HEAP(pe) || APR_BUCKET_IS_POOL(pe))
+            pbuf = pb->heap.base;
+        else
+            return APR_EGENERAL;
+        /* We hope to keep things simple! */
+        if (pe->start != 0)
+            return APR_EGENERAL;
+        /* Remove pe so we have a clean brigade for the loop below */
+        APR_BUCKET_REMOVE(pe);
+#ifdef FTP_TRACE
+        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0, 
+                     fc->connection->base_server, "FTP[%d] frl: using previous bb",
+                     (int) getpid());
+#endif
+    }
+
+    *result = NULL;
+
+    do {
+
+        for (;;) {
+
+            apr_brigade_cleanup(bb);
+            rv = ap_get_brigade(input_filters, bb, 
+                                AP_MODE_GETLINE, block, 0);
+
+            if (rv != APR_SUCCESS) {
+                APR_BRIGADE_INSERT_HEAD(bb, pe);
+                return rv;
+            }
+
+            if (APR_BRIGADE_EMPTY(bb)) {
+                APR_BRIGADE_INSERT_HEAD(bb, pe);
+#ifdef FTP_TRACE
+                ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0, 
+                             fc->connection->base_server, "FTP[%d] frl: got empty brigade",
+                             (int) getpid());
+#endif
+                return (block == APR_BLOCK_READ) ? APR_EGENERAL 
+                                                 : APR_EAGAIN;
+            }
+
+            while ((e = APR_BRIGADE_FIRST(bb)) != APR_BRIGADE_SENTINEL(bb)) {
+                apr_bucket *e_next;
+                const char *str;
+                apr_size_t len;
+                int mark;
+
+                /* If we see an EOS, don't bother doing anything more. */
+                if (APR_BUCKET_IS_EOS(e)) {
+                    break;
+                }
+
+                rv = apr_bucket_read(e, &str, &len, block);
+
+                /*
+                 * Upon discovering that the next bucket is a socket,
+                 * and that socket is at the OOB mark, we will dump
+                 * our current buffer and continue to read the priority
+                 * command beyond the OOB mark.
+                 */
+                e_next = APR_BUCKET_NEXT(e);
+                if (rv == APR_SUCCESS && APR_BUCKET_IS_SOCKET(e_next)) {
+                    apr_socket_t *sock = e_next->data;
+                    rv = socket_atmark(sock, &mark);
+                    ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0, 
+                      fc->connection->base_server, "FTP[%d] frl: atmark: %x %d",
+		      (int) getpid(), (int) rv, mark);
+                    if (rv == APR_SUCCESS && mark) {
+                        pe->length = 0;
+                        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0, 
+                         fc->connection->base_server, "FTP[%d] frl: Saw OOB",
+			 (int) getpid());
+                        continue;
+                    }
+                }
+
+                if (rv != APR_SUCCESS) {
+                    APR_BRIGADE_INSERT_HEAD(bb, pe);
+                    return rv;
+                }
+
+                if (len == 0) {
+                    /* no use attempting a zero-byte alloc (hurts when
+                     * using --with-efence --enable-pool-debug) or
+                     * doing any of the other logic either
+                     */
+                    apr_bucket_delete(e);
+                    continue;
+                }
+
+                /* Exceeding limits?  If so, we'll die. */
+                if (pe->length + len > DEFAULT_LIMIT_REQUEST_LINE + 2) {
+                    APR_BRIGADE_INSERT_HEAD(bb, pe);
+                    return APR_ENOSPC;
+                }
+
+                /* If exceeding our limit, increase the buffer size */
+                if (pe->length + len > pb->heap.alloc_len) {
+                    apr_size_t new_size = pb->heap.alloc_len * 2;
+                    char *new_buffer;
+
+                    if (pe->length + len > new_size) {
+                        new_size = (pe->length + len) * 2;
+                    }
+
+                    if (new_size > DEFAULT_LIMIT_REQUEST_LINE + 2) {
+                        new_size = DEFAULT_LIMIT_REQUEST_LINE + 2;
+                    }
+
+                    if (pb->pool) {
+                        new_buffer = apr_palloc(pb->pool, new_size);
+                        memcpy(new_buffer, pb->base, pe->length);
+                        pb->base = new_buffer;
+                        pbuf = (char*)pb->base;
+                    }
+                    else {
+                        new_buffer = malloc(new_size);
+                        if (!new_buffer)
+                            return APR_ENOSPC;
+                        memcpy(new_buffer, pb->heap.base, pe->length);
+                        free (pb->heap.base);
+                        pb->heap.base = new_buffer;
+                        pbuf = (char*) pb->heap.base;
+                    }
+                    pb->heap.alloc_len = new_size;
+                }
+    
+                /* Just copy the rest of the data to the end of the buffer. */
+                pos = pbuf + pe->length;
+                memcpy(pos, str, len);
+                pe->length += len;
+
+                last_char = pos + len - 1;
+
+                /* Destroy the now-consumed bucket */
+                apr_bucket_delete(e);
+            }
+
+            /* If we got a full line of input, stop reading */
+            if (last_char && (*last_char == APR_ASCII_LF)) {
+
+                /* Trim the trailing spaces or tabs, LF and CR*/
+                while ((last_char > pbuf)
+                       && apr_isspace(*(last_char - 1))) {
+                    --last_char;
+                }
+
+                /* Since we want to remove the LF from the line, we'll go ahead
+                 * and set this last character to be the term NULL
+                 */
+                *last_char = '\0';
+
+                /* Return the result string, and the actual bytes 
+                 * read from the network (before we truncated 
+                 * trailing whitespace and newlines)
+                 *
+                 * We may have moved from a pool to another pool, 
+                 * or to a heap bucket.  Reallocate from the current
+                 * pool in these cases.
+                 */
+                if (pb->pool && pb->pool == pool) {
+                    *result = pbuf;
+                }
+                else {
+                    *result = apr_palloc(pool, last_char - pbuf + 1);
+                    memcpy(*result, pbuf, last_char - pbuf + 1);
+                }
+                *bytes_read = pe->length;
+                
+                /* Finally destroy the working bucket - if it is heap 
+                 * the heap data will also be free()'d.
+                 */
+                apr_bucket_destroy(pe);
+#ifdef FTP_TRACE
+                ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0, 
+                             fc->connection->base_server, "FTP[%d] frl: got full line",
+                             (int) getpid());
+#endif
+                return APR_SUCCESS;
+            }
+        }
+
+    } while (pe->length <= 0);
+#ifdef FTP_TRACE
+    ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0, 
+                 fc->connection->base_server, "FTP[%d] frl: fall through success",
+                 (int) getpid());
+#endif
+    return APR_SUCCESS;
+}
+
+/*
+ * ftp_read_request_line: Read the request from the client, and set the
+ *                        correct values in the request record.
+ *
+ * Arguments: r - The request to read from.
+ * Arguments: bb - The brigade to retrieve.
+ *
+ * Returns: Returns 0 on success, 1 on error. 
+ */
+static apr_status_t ftp_read_request_line(ftp_connection *fc,
+                                          request_rec *r,
+                                          apr_bucket_brigade *bb)
+{
+    apr_size_t bytes_read;
+    apr_status_t rv;
+    const char *ll;
+
+    if (fc->next_request && *fc->next_request) {
+        r->the_request = apr_pstrdup(r->pool, fc->next_request);
+        bytes_read = fc->next_reqsize;
+        fc->next_request = NULL;
+        fc->next_reqsize = 0;
+#ifdef FTP_TRACE
+        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0, 
+                     fc->connection->base_server, "FTP[%d] frrl: using read-ahead request",
+                     (int) getpid());
+#endif
+    }
+    else if ((rv = ftp_read_line(&r->the_request, &bytes_read,
+                                 fc->connection->pool, bb, r->input_filters, 
+                                 APR_BLOCK_READ, fc)) != APR_SUCCESS) {
+        return rv;
+    }
+
+    r->read_length = bytes_read;
+    r->request_time = apr_time_now();
+    ll = r->the_request;
+#ifdef FTP_TRACE
+    ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0, 
+                 fc->connection->base_server, "FTP[%d] frrl: raw command: %s",
+		 (int) getpid(), ll);
+#endif
+    r->method = ftp_toupper(r->pool, ap_getword_white(r->pool, &ll));
+    r->method = ftp_get_cmd_alias(r->method);    
+    r->method_number = ap_method_number_of(r->method);
+
+    return APR_SUCCESS;
+}
+
+apr_status_t ftp_read_ahead_request(ftp_connection *fc) 
+{
+    apr_status_t rv;
+    const char *ll;
+    const char *method;
+
+#ifdef FTP_TRACE
+    ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0, 
+                 fc->connection->base_server, "FTP[%d] frar: entering",
+		 (int) getpid());
+#endif
+
+    /* Review one command, only once */
+    if (fc->next_request && *fc->next_request) {
+#ifdef FTP_TRACE
+        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0, 
+                     fc->connection->base_server, "FTP[%d] frar: previously read-ahead",
+                     (int) getpid());
+#endif
+        return APR_SUCCESS;
+    }
+
+    if (!fc->next_pool) {
+        apr_pool_create(&fc->next_pool, fc->connection->pool);
+        fc->next_bb = apr_brigade_create(fc->next_pool, 
+                                         fc->connection->bucket_alloc);
+#ifdef FTP_TRACE
+        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0, 
+                     fc->connection->base_server, "FTP[%d] frar: created next_pool",
+                     (int) getpid());
+#endif
+    }
+
+    rv = ftp_read_line(&fc->next_request, &fc->next_reqsize,
+                       fc->next_pool, fc->next_bb, 
+                       fc->connection->input_filters, 
+                       APR_NONBLOCK_READ, fc);
+    if (APR_STATUS_IS_EAGAIN(rv)) {
+#ifdef FTP_TRACE
+        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0, 
+                     fc->connection->base_server, "FTP[%d] frar: not ready - read again",
+                     (int) getpid());
+#endif
+        /* We actually like some failures here */
+        return APR_SUCCESS;
+    }
+    else if (rv != APR_SUCCESS) {
+        return rv;
+    }
+
+    /* The entire line is read - we no longer need this brigade */
+    apr_brigade_destroy(fc->next_bb);
+    fc->next_bb = NULL;
+
+    ll = fc->next_request;
+
+#ifdef FTP_TRACE
+    ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0, 
+                 fc->connection->base_server, "FTP[%d] frar: raw command: %s",
+		 (int) getpid(), ll);
+#endif
+
+    /*
+     * FIXME: For some reason we miss seeing the OOB mark,
+     * at which point the various telnet sequences are
+     * still in the command stream. We assume that we
+     * should strip out anything before, and including,
+     * the DM (value xf2). Because of strangeness with
+     * how some clients do this, we also strip out
+     * the IAC (xff) and IP (xf4) chars.
+     */
+    if (method = strrchr(ll, '\xff'))
+        ll = ++method;
+    if (method = strrchr(ll, '\xf4'))
+        ll = ++method;
+    if (method = strrchr(ll, '\xf2'))
+        ll = ++method;
+
+    /* XXX gross hack till we straighten out constness */
+    fc->next_request = (char *)ll;  /* store away "cleaned" command */
+
+    method = ftp_toupper(fc->next_pool, ap_getword_white(fc->next_pool, &ll));
+
+#ifdef FTP_TRACE
+    ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0, 
+                 fc->connection->base_server, "FTP[%d] frar: method: %s",
+		 (int) getpid(), method);
+#endif
+    /* Can we ignore this command for a while? */
+    if (ftp_cmd_abort_data(method)) {
+        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0, 
+                     fc->connection->base_server, "FTP[%d] frar: I see ABOR",
+                     (int) getpid());
+        return APR_ECONNRESET;
+    }
+
+    /* Wait to consider this command till the data transfer is complete */
+    return APR_SUCCESS;
+}
+
+/*
+ * ftp_read_request: Called from the connection handler.  Used to
+ *                   read the request and fill out the request record.
+ *                       
+ * Arguments: fc - The ftp_connection rec associated with this request.
+ *
+ * Returns: Returns an initialized request_rec 
+ */
+request_rec *ftp_read_request(ftp_connection *fc)
+{
+    conn_rec *c = fc->connection;
+    apr_status_t rv;
+    request_rec *r;
+    apr_pool_t *p;
+    int access_status;
+    apr_bucket_brigade *tmp_bb;
+    ap_filter_t *f;
+
+    apr_pool_create(&p, c->pool);
+    r = apr_pcalloc(p, sizeof(request_rec));
+
+    r->pool = p;
+    r->connection = c;
+    r->server = c->base_server;
+
+    r->user = NULL;
+    r->ap_auth_type = NULL;
+
+    r->allowed_methods = ap_make_method_list(p, 2);
+
+    r->headers_in = apr_table_make(r->pool, 50);
+    r->subprocess_env = apr_table_make(r->pool, 50);
+    r->headers_out = apr_table_make(r->pool, 12);
+    r->err_headers_out = apr_table_make(r->pool, 5);
+    r->notes = apr_table_make(r->pool, 5);
+
+    r->request_config = ap_create_request_config(r->pool);
+
+    /* Must be set before we run create request hook */
+    r->proto_output_filters = c->output_filters;
+    r->output_filters = r->proto_output_filters;
+    r->proto_input_filters = c->input_filters;
+    r->input_filters = r->proto_input_filters;
+    ap_run_create_request(r);
+
+    /*
+     * We now need to remove the NET_TIME filter to allow
+     * use to control timeouts ourselves. 
+     */
+    for (f = c->input_filters; f; f = f->next) {
+        if (strcasecmp(f->frec->name, "NET_TIME") == 0) {
+            ap_remove_input_filter(f);
+            break;
+        }
+    }
+    for (f = r->input_filters; f; f = f->next) {
+        if (strcasecmp(f->frec->name, "NET_TIME") == 0) {
+            ap_remove_input_filter(f);
+            break;
+        }
+    }
+    for (f = r->proto_input_filters; f; f = f->next) {
+        if (strcasecmp(f->frec->name, "NET_TIME") == 0) {
+            ap_remove_input_filter(f);
+            break;
+        }
+    }
+
+    ftp_set_module_config(r->request_config, fc);
+
+    r->per_dir_config = r->server->lookup_defaults;
+
+    r->the_request = NULL;
+    r->protocol = "FTP";
+    r->method = NULL;
+    r->read_length = 0;
+    
+    /* We don't use r->uri for every request, but some modules (SSL)
+     * require r->uri to not be NULL in the post_read_request hook 
+     *
+     * The canonical (http) form of "Any location" is *, e.g. the
+     * http OPTIONS * request.  It's not a bad pattern to keep with
+     * module author's expectations.
+     */
+    r->uri = "*";
+
+    /* Resume any partial request line from fc->next_bb */
+    if (fc->next_bb) {
+        tmp_bb = fc->next_bb;
+#ifdef FTP_TRACE
+        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0, 
+                     fc->connection->base_server, "FTP[%d] frr: using next_bb",
+                     (int) getpid());
+#endif
+    } else {
+        tmp_bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+#ifdef FTP_TRACE
+        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0, 
+                     fc->connection->base_server, "FTP[%d] frr: using tmp_bb",
+                     (int) getpid());
+#endif
+    }
+    if ((rv = ftp_read_request_line(fc, r, tmp_bb)) != APR_SUCCESS)
+    {
+        apr_time_t timeout;
+        apr_bucket_brigade *bb;
+        apr_bucket *b;
+        apr_socket_t *client_socket = ap_get_module_config(c->conn_config,
+                                                           &core_module);
+        char *err;
+        apr_size_t len;
+
+        apr_brigade_destroy(tmp_bb);
+
+        if (rv == APR_TIMEUP) {
+            /*
+             * Handle client timeouts here.  The idle timeout for the 
+             * control connection is set in the process_connection 
+             * handler, if the timeout is reached, ftp_read_request_line
+             * will return with an error.  Here we send the client a 
+             * friendly error message, and close the connection.
+             */
+            apr_socket_timeout_get(client_socket, &timeout);
+
+            ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0, 
+                         c->base_server,
+                         "User %s timed out after %d seconds", fc->user,
+                         (int) (timeout / APR_USEC_PER_SEC));
+
+            err = apr_psprintf(r->pool,
+                               "%d Idle Timeout (%d seconds): "
+                               "Closing control connection"
+                               CRLF, FTP_REPLY_SERVICE_NOT_AVAILABLE,
+                               (int) (timeout / APR_USEC_PER_SEC));
+            len = strlen(err);
+
+            bb = apr_brigade_create(r->pool, c->bucket_alloc);
+            rv = apr_brigade_write(bb, ap_filter_flush,
+                                   c->output_filters, err, len);
+
+            /* Flush the brigade down the filter chain */
+            b = apr_bucket_flush_create(c->bucket_alloc);
+            APR_BRIGADE_INSERT_TAIL(bb, b);
+            ap_pass_brigade(c->output_filters, bb);
+           
+            apr_brigade_destroy(bb);
+        }
+        else {
+            /* Remote client suddenly disconnected, don't bother sending
+             * an error since the client is long gone.  Just log the error.
+             */
+            ap_log_error(APLOG_MARK, APLOG_INFO, rv, 
+                         c->base_server, "User %s disconnected", fc->user);
+        }
+        /* Return NULL to the connection handler, causing the connection
+         * to be dropped.
+         */
+        return NULL;
+    }
+
+    apr_brigade_destroy(tmp_bb);
+    fc->next_bb = NULL;
+
+    /* ftp_read_line returns the_request always allocated from the
+     * correct pool.  If not, we have a bug.  No need to clean up
+     * next_pool above in the failure case, because it is allocated 
+     * from the connection (soon to be destroyed.)
+     */
+    if (fc->next_pool) {
+        apr_pool_destroy(fc->next_pool);
+        fc->next_pool = NULL;
+#ifdef FTP_TRACE
+        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0, 
+                     fc->connection->base_server, "FTP[%d] frr: clearing next_pool",
+                     (int) getpid());
+#endif
+    }
+
+    /* PHP does initializations of important data structures in the
+     * post_read_request phase.  */
+    if ((access_status = ap_run_post_read_request(r))) {
+
+        ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0,
+                     c->base_server, "Post read request failed, dropping "
+                     "client connection.");
+        return NULL;
+    }
+
+    return r;
+}
+
+/*
+ * ftp_reply: This function is used for sending command responses to the
+ *            client over the control connection.  All responses should 
+ *            be sent through this function.
+ *
+ * Arguments: out_filter - The output filter chain.
+ *            p - The pool to allocate from.
+ *            n - The ftp response code.
+ *            l - Flag that determines if this is a long response or not.  Long
+ *                responses have the format %d-%s%s, short responses use a
+ *                space in place of the dash.
+ *                
+ *                    1 - this is long response.
+ *                    0 - this is a short (the last) response.
+ *            fmt - Format string to be sent to the client.
+ *
+ * Returns: apr_status_t
+ */
+apr_status_t ftp_reply(ftp_connection *fc, ap_filter_t *out_filter, 
+                       apr_pool_t *p, int n, int l, const char *fmt, ...)
+{
+    char buf[BUFSIZ], reply[BUFSIZ];
+    int len;
+    va_list ap;
+    apr_bucket_brigade *bb;
+    apr_bucket *b;
+
+    va_start(ap, fmt);
+    apr_vsnprintf(buf, sizeof(buf), fmt, ap);
+    len = apr_snprintf(reply, sizeof(reply), "%d%s%s%s", n,
+                       l == 1 ? "-" : " ", buf, CRLF);
+    va_end(ap);
+
+    bb = apr_brigade_create(p, out_filter->c->bucket_alloc);
+    b = apr_bucket_pool_create(reply, len, p, 
+                               out_filter->c->bucket_alloc);
+    APR_BRIGADE_INSERT_HEAD(bb, b);
+    b = apr_bucket_flush_create(out_filter->c->bucket_alloc);
+    APR_BRIGADE_INSERT_TAIL(bb, b);
+    
+    fc->traffic += len;
+
+    return ap_pass_brigade(out_filter, bb);
+}
+
+/* ftp_show_file: Test if a file exists, and echo it to the control
+ *                connection if it exists.  This is primarily for
+ *                displaying MOTD messages and .message files.
+ *
+ * Arguments: out_filter - The output filter chain.
+ *            p - The pool to allocate from.
+ *            code - Integer value representing the response code to use.
+ *            fc   - The ftp connection.
+ *            file - Absolute path to the file to be displayed.
+ *
+ * Returns: apr_status_t
+ */
+apr_status_t ftp_show_file(ap_filter_t *out_filter, apr_pool_t *p, int code, 
+                           ftp_connection *fc, const char *path)
+{
+    apr_status_t rv;
+    apr_file_t *file;
+    char *pos;
+    char buf[BUFSIZ];
+    char reply[BUFSIZ];
+
+    rv = apr_file_open(&file, path, APR_READ, APR_OS_DEFAULT, p);
+    if (rv != APR_SUCCESS) {
+        return rv;
+    }
+
+    while(apr_file_gets(buf, sizeof(buf), file) == APR_SUCCESS) {
+        /* Strip off trailing space/cr/lf, ftp_reply does not expect them */
+        pos = buf + strlen(buf) - 1;
+        while ((pos >= buf) && apr_isspace(*pos)) 
+            --pos;
+        pos[1] = '\0';
+
+        ftp_message_generate(fc, buf, reply, sizeof(reply));
+
+        rv = ftp_reply(fc, out_filter, p, code, 1, "%s", reply);
+        if (rv != APR_SUCCESS) {
+            return rv;
+        }
+    }
+
+    return apr_file_close(file);
+}
+
+/* ftp_send_response: Send response to the client based on the status code
+ *                    These are currently listed in numerical order.
+ *                    For responses such as 503's where the error message
+ *                    can change, the caller is requried to fill out
+ *                    the respose_notes in the ftp_connection structure.
+ *
+ * Arguments: r - The request.
+ *            status - FTP status code.
+ *
+ * Returns: nothing
+ */
+void ftp_send_response(request_rec *r, int status)
+{
+    ftp_connection *fc = ftp_get_module_config(r->request_config);
+    ftp_server_config *fsc;
+    conn_rec *c = r->connection;
+
+    /* We are done checking subrequest values for r->status, so we can
+     * place our ftp reply code there so it can be logged */
+    r->status = status;
+
+    /* In general, status codes below 400 will be considered success.
+     * Specific exceptions are toggled, below.
+     */
+    if (status >= 400) {
+        apr_table_setn(r->subprocess_env, "ftp_transfer_ok", "0");
+    }
+
+    switch(status) {
+    case FTP_REPLY_SYSTEM_STATUS:
+        ftp_reply(fc, c->output_filters, r->pool, FTP_REPLY_SYSTEM_STATUS, 1, 
+                  "Extensions supported:\n"
+                  " AUTH TLS\n"
+                  " PBSZ\n"
+                  " PROT\n"
+                  " SIZE\n"
+                  " MDTM");
+        ftp_reply(fc, c->output_filters, r->pool, FTP_REPLY_SYSTEM_STATUS, 0, 
+                  "END");
+        break;
+    case FTP_REPLY_SYSTEM_TYPE:
+#ifdef WIN32
+        ftp_reply(fc, c->output_filters, r->pool, FTP_REPLY_SYSTEM_TYPE, 0, 
+                  "Win32 Type");
+#else
+        ftp_reply(fc, c->output_filters, r->pool, FTP_REPLY_SYSTEM_TYPE, 0, 
+                  "UNIX Type");
+#endif /* WIN32 */
+        break;
+    case FTP_REPLY_CONTROL_CLOSE:
+        fsc = ftp_get_module_config(c->base_server->module_config);
+        if (fsc->exit_message) {
+            if (fsc->exit_message_isfile) {
+                ftp_show_file(c->output_filters, r->pool,
+                              FTP_REPLY_CONTROL_CLOSE, fc, fsc->exit_message);
+            }
+            else {
+                char reply[BUFSIZ];
+                ftp_message_generate(fc, fsc->exit_message, reply,
+                                     sizeof(reply));
+                ftp_reply(fc, c->output_filters, r->pool, 
+                          FTP_REPLY_CONTROL_CLOSE, 1, reply);
+            }
+        }
+
+        ftp_reply(fc, c->output_filters, r->pool, FTP_REPLY_CONTROL_CLOSE, 0,
+                  "Goodbye.");
+        break;
+    case FTP_REPLY_DATA_CLOSE:
+        ftp_reply(fc, c->output_filters, r->pool, FTP_REPLY_DATA_CLOSE, 0, 
+                  "Transfer complete.");
+        break;
+    case FTP_REPLY_USER_LOGGED_IN:
+        ftp_reply(fc, c->output_filters, r->pool, FTP_REPLY_USER_LOGGED_IN, 0, 
+                  "User %s logged in", fc->user);
+        break;
+    case FTP_REPLY_SECURITY_EXCHANGE_DONE:
+        ftp_reply(fc, c->output_filters, r->pool, 
+                  FTP_REPLY_SECURITY_EXCHANGE_DONE, 0,
+                  "Security exchange completed");
+        break;
+    case FTP_REPLY_COMPLETED:
+        ftp_reply(fc, c->output_filters, r->pool, FTP_REPLY_COMPLETED, 0, 
+                  "%s command successful.", r->method);
+        break;
+    case FTP_REPLY_CANNOT_OPEN_DATACONN:
+        ftp_reply(fc, c->output_filters, r->pool, 
+                  FTP_REPLY_CANNOT_OPEN_DATACONN, 
+                  0, "Can't open data connection.");
+        break;
+    case FTP_REPLY_TRANSFER_ABORTED:
+        ftp_reply(fc, c->output_filters, r->pool, FTP_REPLY_TRANSFER_ABORTED,
+                  0, "Transfer aborted");
+        break;
+    case FTP_REPLY_COMMAND_UNRECOGNIZED:
+        ftp_reply(fc, c->output_filters, r->pool, 
+                  FTP_REPLY_COMMAND_UNRECOGNIZED, 0, 
+                  "%s: Command not recognized", r->method);
+        break;
+    case FTP_REPLY_BAD_SEQUENCE:
+        ftp_reply(fc, c->output_filters, r->pool, FTP_REPLY_BAD_SEQUENCE, 0, 
+                  "Bad sequence of commands");
+        break;
+    case FTP_REPLY_PROT_NOT_SUPPORTED:
+        ftp_reply(fc, c->output_filters, r->pool, 
+                  FTP_REPLY_PROT_NOT_SUPPORTED, 0,
+                  "Requested PROT level not supported by mechanism");
+        break;
+
+    /* Exception cases, failure codes that fall before the 400's: */
+    case FTP_REPLY_SERVICE_READY_IN_N_MIN:
+    case FTP_REPLY_NOT_IMPLEMENTED:
+    case FTP_REPLY_NEED_ACCOUNT:
+        apr_table_setn(r->subprocess_env, "ftp_transfer_ok", "0");
+        /* failure is flagged for status < 400
+         * now fall through...
+         */
+
+    default:
+        ftp_reply(fc, c->output_filters, r->pool, status, 0, 
+                  "%s", fc->response_notes ? fc->response_notes
+                                           : "Undefined Internal Error.");
+    }
+
+    return;
+}

Propchange: incubator/mod_ftp/trunk/src/ftp_protocol.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/mod_ftp/trunk/src/ftp_protocol.c
------------------------------------------------------------------------------
    svn:executable = 

Added: incubator/mod_ftp/trunk/src/ftp_request.c
URL: http://svn.apache.org/viewcvs/incubator/mod_ftp/trunk/src/ftp_request.c?rev=306631&view=auto
==============================================================================
--- incubator/mod_ftp/trunk/src/ftp_request.c (added)
+++ incubator/mod_ftp/trunk/src/ftp_request.c Thu Oct  6 06:16:28 2005
@@ -0,0 +1,82 @@
+/* Copyright 1999-2005 The Apache Software Foundation or its licensors, as
+ * applicable.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Original Copyright (c) Covalent Technologies 2001-2005
+ *
+ * FTP Protocol module for Apache 2.0
+ */
+
+#include "mod_ftp.h"
+
+#ifndef apr_time_from_sec
+#define apr_time_from_sec(sec) ((apr_time_t)(sec) * APR_USEC_PER_SEC)
+#endif
+
+/*
+ * ftp_process_request: Called after the request is read.  This function
+ *                      will process the request, and call the correct
+ *                      command handler, send the response and log the
+ *                      transaction.
+ *
+ * Arguments: r - The request to be processed.
+ *            fc - The ftp connection record associated with this request.
+ *
+ * Returns: Nothing. 
+ */
+void ftp_process_request(request_rec *r)
+{
+    ftp_connection *fc = ftp_get_module_config(r->request_config);
+    ftp_server_config *fsc =
+        ftp_get_module_config(r->server->module_config);
+
+    int res;
+
+    fc->traffic += r->read_length;
+    fc->response_notes = "";
+
+    apr_table_setn(r->subprocess_env, "ftp_transfer_ok", "1");
+    res = ftp_run_cmd(r, r->method);
+
+    /* If the passive connection has been open too long, close it */
+    if ((fc->passive_created != -1) && fc->csock
+            && (res != FTP_REPLY_DATA_CLOSE)
+            && (res != FTP_REPLY_CONTROL_CLOSE) 
+            && (apr_time_now() - fc->passive_created
+                    > apr_time_from_sec(fsc->timeout_data))) {
+        ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
+                     "Timeout waiting to use passive port (closing data connection).");
+        apr_socket_close(fc->csock);
+        fc->csock = NULL;
+        fc->passive_created = -1;
+    }
+
+    ftp_send_response(r, res);
+
+    ap_run_log_transaction(r);
+}
+
+apr_status_t ftp_protocol_filter(ap_filter_t *f, apr_bucket_brigade *b,
+                                 ap_input_mode_t mode, apr_read_type_e block,
+                                 apr_off_t readbytes)
+{
+    apr_bucket *eos;
+
+    eos = apr_bucket_eos_create(b->bucket_alloc);
+    APR_BRIGADE_INSERT_HEAD(b, eos);
+
+    return APR_SUCCESS;
+}

Propchange: incubator/mod_ftp/trunk/src/ftp_request.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/mod_ftp/trunk/src/ftp_request.c
------------------------------------------------------------------------------
    svn:executable = 

Added: incubator/mod_ftp/trunk/src/ftp_util.c
URL: http://svn.apache.org/viewcvs/incubator/mod_ftp/trunk/src/ftp_util.c?rev=306631&view=auto
==============================================================================
--- incubator/mod_ftp/trunk/src/ftp_util.c (added)
+++ incubator/mod_ftp/trunk/src/ftp_util.c Thu Oct  6 06:16:28 2005
@@ -0,0 +1,592 @@
+/* Copyright 1999-2005 The Apache Software Foundation or its licensors, as
+ * applicable.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Original Copyright (c) Covalent Technologies 2001-2005
+ *
+ * FTP Protocol module for Apache 2.0
+ */
+
+#include <sys/stat.h> /* For file perms */
+
+#include "mod_ftp.h"
+#ifndef FTP_NO_GLOB
+#include "ftp_glob.h" /* Custom globber */
+#endif /* FTP_NO_GLOB */
+
+#include "ap_mpm.h" /* For MPM query interface */
+
+static char *ftp_modestring_get(char *mode, apr_filetype_e typ, 
+                                      apr_fileperms_t perms)
+{
+
+#ifdef WIN32
+    perms = ftp_unix_mode2perms(0);
+#endif /* WIN32 */
+
+    if(perms < 0 || perms >= FTP_MAX_MODESTRING) {
+        return FTP_UNKNOWN_MODESTRING;  /* see mod_ftp.h */
+    }
+
+    if (typ == APR_DIR) {
+        mode[0] = 'd';
+    }
+    if(perms & APR_UREAD) {
+        mode[1] = 'r';
+    }
+    if(perms & APR_UWRITE) {
+        mode[2] = 'w';
+    }
+    if(perms & APR_UEXECUTE) {
+        mode[3] = 'x';
+    }
+    if(perms & APR_USETID) {
+        mode[3] = 's';
+    }
+    if(perms & APR_GREAD) {
+        mode[4] = 'r';
+    }
+    if(perms & APR_GWRITE) {
+        mode[5] = 'w';
+    }
+    if(perms & APR_GEXECUTE) {
+        mode[6] = 'x';
+    }
+    if(perms & APR_GSETID) {
+        mode[6] = 's';
+    }
+    if(perms & APR_WREAD) {
+        mode[7] = 'r';
+    }
+    if(perms & APR_WWRITE) {
+        mode[8] = 'w';
+    }
+    if(perms & APR_WEXECUTE) {
+        mode[9] = 'x';
+    }
+    if(perms & APR_WSTICKY) {
+        mode[9] = 't';
+    }
+    return mode;
+}
+
+
+/* ftp_make_direntry: Fill out a directory entry structure from the
+ *                    directory being requested and a filename.
+ *
+ * Arguments: r    - The request record for the directory index
+ *            name - The filename to be checked.
+ *
+ * Returns: ftp_direntry structure on success, NULL otherwise.
+ */
+static struct ftp_direntry *ftp_direntry_make(request_rec *r,
+                                              const char *name)
+{
+    ftp_server_config *fsc = ftp_get_module_config(r->server->module_config);
+    struct ftp_direntry *dirent;
+    request_rec *rr;
+    const char *test, *sl;
+    char mode[FTP_MODESTRING_LEN] = "----------";
+
+    for (test = name; sl = strchr(test, '/'); test = sl + 1)
+        /* noop */ ;
+
+    if (!strcmp("..", test)) {
+        return NULL;
+    }
+
+    rr = ap_sub_req_lookup_file(name, r, NULL);
+
+    /* Another hack.  With BSD glob it is possible to return a file
+     * outside the current directory. (or document root for that matter).
+     * This was an oversight made when adding the GLOB_NOPERIOD option
+     * to BSD glob.  Parsing the uri and checking if NULL is returned
+     * will allow us to check if the current file is outside the docroot.
+     */
+    ap_parse_uri(rr, name);
+    ap_getparents(rr->uri);
+
+    if ((rr->finfo.filetype != 0) && 
+        ((rr->status == HTTP_OK) || (rr->status == HTTP_MOVED_PERMANENTLY) ||
+         (rr->status == HTTP_UNAUTHORIZED && 
+          fsc->options & FTP_OPT_SHOWUNAUTH)) &&
+        (rr->uri != NULL)) 
+    {
+        apr_time_exp_t xt;
+        apr_size_t retcode;
+        char *lasts;
+
+        /* We don't mess with char returned here, so its fine to cast */
+        lasts = ap_strrchr((char *)name, '/');
+
+        dirent             = apr_pcalloc(r->pool, sizeof(ftp_direntry));
+        dirent->next       = NULL;
+        dirent->name       = apr_pstrdup(r->pool, lasts + 1);
+        dirent->nlink      = rr->finfo.nlink;
+        dirent->size       = rr->finfo.size;
+        dirent->csize      = rr->finfo.csize;
+        dirent->modestring = apr_pstrdup(r->pool,
+                                         ftp_modestring_get(
+                                                mode,
+                                                rr->finfo.filetype,
+                                                rr->finfo.protection)
+                                         );
+
+        /* If FTPOptions RemoveUserGroup is set, we don't bother looking
+         * up user and group information for each file */
+        if (fsc->options & FTP_OPT_REMOVEUSERGROUP) {
+            dirent->username = apr_psprintf(r->pool, "%d", rr->finfo.user);
+            dirent->groupname = apr_psprintf(r->pool, "%d", rr->finfo.group);
+        }
+        else {
+            if ((apr_get_username(&dirent->username, rr->finfo.user, r->pool)
+                    != APR_SUCCESS) || (!dirent->username) 
+                                    || (!dirent->username[0])) {
+                dirent->username = apr_psprintf(r->pool, "%d", rr->finfo.user);
+            }
+            if ((apr_get_groupname(&dirent->groupname, rr->finfo.group, r->pool)
+                    != APR_SUCCESS) || (!dirent->groupname) 
+                                    || (!dirent->groupname[0])) {
+                dirent->groupname = apr_psprintf(r->pool, "%d", rr->finfo.group);
+            }
+        }
+
+        apr_explode_localtime(&xt, rr->finfo.mtime);
+        
+        if (r->request_time - rr->finfo.mtime > 
+            180 * 24 * 60 * 60 * APR_USEC_PER_SEC) {
+            apr_strftime(dirent->datestring, &retcode, 
+                         sizeof(dirent->datestring), "%b %e  %Y", &xt);
+        }
+        else {
+            apr_strftime(dirent->datestring, &retcode, 
+                         sizeof(dirent->datestring), "%b %e %H:%M", &xt);
+        }
+    } 
+    else {
+        dirent = NULL;
+    }
+
+    ap_destroy_sub_req(rr);
+    return dirent;
+}
+
+#ifdef FTP_NO_GLOB
+/* ftp_dsortf: Used for sorting directory entries when globbing is
+ *             not enabled.  Called by qsort()
+ *
+ * Arguments: d1 - The first directory entry
+ *            d2 - The second directory entry
+ *
+ * Returns: An integer less than, equal to or greater than zero if
+ *          d1 is found, respectively, to be less than, match or
+ *          be greater than d2.
+ */
+ static int ftp_dsortf(struct ftp_direntry **d1,
+                       struct ftp_direntry **d2)
+{
+    /* Simple sort based on filename */
+    return strcmp((*d1)->name, (*d2)->name);
+}
+
+/* ftp_direntry_get: Identical to ftp_direntry_get_glob, but we do not
+ *                   support globbing.
+ *
+ * Arguments r - The request rec for the directory listing
+ *
+ * Returns: The sorted array of directory entries on success, NULL otherwise
+ */
+struct ftp_direntry *ftp_direntry_get(request_rec *r)
+{
+    struct ftp_direntry *p, *head, *current, **a;
+    apr_dir_t *dir;
+    apr_finfo_t finfo;
+    apr_status_t rv;
+    int num, i;
+
+#ifdef WIN32
+    /* FIXME: A nasty hack. If the last char is a '*' then
+     * we silently delete it and press on. This allows for
+     * things like 'NLST *' to work */
+    num = strlen(r->filename) - 1;
+    if ((num >= 0) && (r->filename[num] == '*')) {
+        r->filename[num] = '\0';
+        num = strlen(r->filename) - 1;
+        if ((num >= 0) && (r->filename[num] == '/')) {
+            r->filename[num] = '\0';
+        }
+    }
+
+    /* Win32 will always return sucess on apr_dir_open, which means
+     * we have to stat the file to see if the request was made for
+     * a directory or file.
+     */
+    rv = apr_stat(&finfo, r->filename, APR_FINFO_MIN, r->pool);
+    if (finfo.filetype != APR_DIR) {
+        /* Must be for a single file */
+        return ftp_direntry_make(r, r->filename);    
+    }
+#endif /* WIN32 */
+
+    rv = apr_dir_open(&dir, r->filename, r->pool);
+
+    if (rv != APR_SUCCESS) {
+        if (APR_STATUS_IS_ENOTDIR(rv)) {
+            /* Must be for a single file */
+            return ftp_direntry_make(r, r->filename);
+        }
+        else {
+            return NULL;
+        }
+    }
+
+    num = 0;
+    head = current = NULL;
+    while ((rv = apr_dir_read(&finfo, APR_FINFO_DIRENT, dir)) 
+           == APR_SUCCESS) {
+
+        p = ftp_direntry_make(r, ap_make_full_path(r->pool, r->filename,
+                                                   finfo.name));
+        if (!p) {
+            continue;
+        }
+        
+        /* Add this entry to the linked list */
+        if (head == NULL) {
+            head = p;
+            p->next = NULL;
+            current = p;
+        }
+        else {
+            current->next = p;
+            current = p;
+        }
+        p->child = NULL;
+        num++;
+    }
+    
+    apr_dir_close(dir);
+    
+    /* Sort this mess */
+    if (num > 0) {
+        a = (struct ftp_direntry **) apr_pcalloc(r->pool,
+                                                 num *
+                                                 sizeof (ftp_direntry));
+        p = head;
+        i = 0;
+        while (p) {
+            a[i++] = p;
+            p = p->next;
+        }
+        qsort((void *) a, num, sizeof (struct ftp_direntry *),
+              (int(*)(const void *, const void *))ftp_dsortf);
+
+        /* Re-construct the list from the sorted list */
+        head = a[0];
+        current = head;
+        for (i = 1; i < num; i++) {
+            current->next = a[i];
+            current = current->next;
+        }
+        current->next = NULL;
+    }
+        
+    return head;
+}
+
+#else
+/* ftp_direntry_get_glob: Return an array of ftp_direntry structures based
+ *                        on the uri stored in the request rec.  An extra
+ *                        argument may be passed for pattern matching. Note 
+ *                        that this uses glob, which is only available on 
+ *                        Unix type systems.
+ *
+ * Arguments: r       - The request rec for the directory listing
+ *            pattern - Pattern to get a directory listing for
+ *
+ * Returns: The sorted array of directory entries on success, NULL otherwise
+ */
+
+struct ftp_direntry *ftp_direntry_get_glob(request_rec *r,  
+                                           const char *pattern)
+{
+    struct ftp_direntry *p, *head, *current;
+    int i;
+    ftp_glob_t gb;
+    /* The actual search pattern, used to determine if we should recurse
+     * into a directory or not.  If the search pattern is just *, we should
+     * not decend.  For search patterns like 'm*', we should.
+     */
+    const char *search = strrchr((char *) pattern, '/') + 1;
+
+    if(ftp_glob(pattern, FTP_GLOB_PERIOD, NULL, &gb)) {
+        return NULL;
+    }
+
+    /* BSD glob does not consider finding no matches an error. */
+    if (gb.gl_pathc == 0 && gb.gl_matchc == 0) {
+        return NULL;
+    }
+
+    head = NULL;
+    current = NULL;
+    for(i=0; gb.gl_pathv[i] != NULL; i++) {
+
+        /* Run the subreq and fill out most information */
+        p = ftp_direntry_make(r, gb.gl_pathv[i]);
+        if (!p) {
+            continue;
+        }
+
+        /* Add this entry to the linked list */
+        if (head == NULL) {
+            head = p;
+            p->next = NULL;
+            current = p;
+        }
+        else {
+            current->next = p;
+            current = p;
+        }
+
+        /* We are only going to support single recursive listings
+         * this means that reqeusts such as 'ls m*' will print out
+         * all files that match, and recurse a single level into
+         * directories.
+         */
+        
+        if ((search[0] != '*') && (p->modestring[0] == 'd')) {
+            const char *newpattern = apr_pstrcat(r->pool, gb.gl_pathv[i],
+                                                 "/*", NULL);
+            p->child = ftp_direntry_get_glob(r, newpattern);
+        }
+        else {
+            p->child = NULL;
+        }
+    }
+
+    ftp_globfree(&gb);
+    return head;
+}
+#endif /* FTP_NO_GLOB */
+
+/* ftp_set_authorization: set the r->headers_in Authorization header
+ *
+ * Arguments: r - The request
+ *
+ * Returns: nada
+ */
+
+void ftp_set_authorization(request_rec *r)
+{
+    ftp_connection *fc = ftp_get_module_config(r->request_config);
+    r->user = apr_pstrdup(r->pool, fc->user);
+    apr_table_setn(r->headers_in, "Authorization", fc->authorization);
+}
+
+/* ftp_set_uri: Setup r->uri based on a file argument and user's 
+ *              current working directory.  We also run the translate
+ *              name phase here to set r->filename.
+ *
+ * Arguments: r - The request
+ *            arg - The file argument
+ *
+ * Returns: nothing
+ */
+int ftp_set_uri(request_rec *r, const char *arg)
+{
+    ftp_connection *fc = ftp_get_module_config(r->request_config);
+    apr_status_t res;
+
+    if (arg[0] == '/') {
+        ap_parse_uri(r, arg);
+    } else {
+        ap_parse_uri(r, ap_make_full_path(r->pool, fc->cwd, arg));
+    }
+    ap_getparents(r->uri);
+
+    /* If the path ended in /., ap_getparents converts it to /, but we really
+     * don't want the trailing / there, so remove it.
+     */
+    if (r->uri[strlen(r->uri) - 1] == '/') {
+        r->uri[strlen(r->uri) - 1] = '\0';
+    }
+
+    /*
+     * Parsed uri is empty if we try a path outside the document
+     * root.  For now, just set the uri to /, but I'm sure there
+     * is a better way around this.
+     *
+     * - rpm
+     */
+    if (r->uri[0] == '\0') {
+        r->uri = apr_pstrdup(r->pool, "/");
+    }
+    res = ap_run_translate_name(r);
+    if (res) {
+        fc->response_notes = apr_psprintf(r->pool, FTP_MSG_PERM_DENIED,
+                                          r->parsed_uri.path);
+        return FTP_REPLY_LOCAL_ERROR;    
+    }
+    r->uri = ap_escape_uri(r->pool, r->uri);
+
+    return OK;
+}
+
+/* ftp_unix_perms2mode: Translate apr_fileperms_t to mode_t.
+ *
+ * Arguments: perms - apr_fileperms_t
+ *
+ * Returns: mode_t
+ */ 
+mode_t ftp_unix_perms2mode(apr_fileperms_t perms)
+{
+    mode_t mode = 0;
+
+#ifndef WIN32
+    if (perms & APR_UREAD) {
+        mode |= S_IRUSR;
+    }
+    if (perms & APR_UWRITE) {
+        mode |= S_IWUSR;
+    }
+    if (perms & APR_UEXECUTE) {
+        mode |= S_IXUSR;
+    }
+    if (perms & APR_GREAD) {
+        mode |= S_IRGRP;
+    }
+    if (perms & APR_GWRITE) {
+        mode |= S_IWGRP;
+    }
+    if (perms & APR_GEXECUTE) {
+        mode |= S_IXGRP;
+    }
+    if (perms & APR_WREAD) {
+        mode |= S_IROTH;
+    }
+    if (perms & APR_WWRITE) {
+        mode |= S_IWOTH;
+    }
+    if (perms & APR_WEXECUTE) {
+        mode |= S_IXOTH;
+    }
+#endif /* WIN32 */
+    return mode;
+}
+
+/* ftp_unix_mode2perms: Translate mode_t to apr_fileperms_t.
+ *
+ * Arguments: mode - mode_t
+ *
+ * Returns: apr_fileperms_t
+ */ 
+apr_fileperms_t ftp_unix_mode2perms(mode_t mode)
+{
+    apr_fileperms_t perms = 0;
+#ifdef WIN32
+    perms |= APR_UREAD;
+    perms |= APR_UWRITE;
+    perms |= APR_UEXECUTE;
+    perms |= APR_GREAD;
+    perms |= APR_GEXECUTE;
+    perms |= APR_WREAD;
+    perms |= APR_WEXECUTE;
+
+#else
+    if (mode & S_IRUSR) {
+        perms |= APR_UREAD;
+    }
+    if (mode & S_IWUSR) {
+        perms |= APR_UWRITE;
+    }
+    if (mode & S_IXUSR) {
+        perms |= APR_UEXECUTE;
+    }
+    if (mode & S_IRGRP) {
+        perms |= APR_GREAD;
+    }
+    if (mode & S_IWGRP) {
+        perms |= APR_GWRITE;
+    }
+    if (mode & S_IXGRP) {
+        perms |= APR_GEXECUTE;
+    }
+    if (mode & S_IROTH) {
+        perms |= APR_WREAD;
+    }
+    if (mode & S_IWOTH) {
+        perms |= APR_WWRITE;
+    }
+    if (mode & S_IXOTH) {
+        perms |= APR_WEXECUTE;
+    }
+#endif /* WIN32 */
+    return perms;
+}
+
+/* ftp_toupper: Convert a string to uppercase
+ *
+ * Arguments: s - The string
+ *
+ * Returns: The capitialized string. 
+ */ 
+char *ftp_toupper(apr_pool_t *p, const char *s)
+{
+    char *upper = apr_pstrdup(p, s);
+    char *pos = upper;
+
+    while (*pos != '\0') {
+        *pos = apr_toupper(*pos);
+        pos++;
+    }
+    
+    return upper;
+}
+
+/* ftp_check_maxclients: Check the scoreboard for other available servers.
+ *
+ * Arguments: r - The current request
+ *
+ * Returns: 0 if we find a server, 1 otherwise.
+ */
+int ftp_check_maxclients(request_rec *r)
+{
+    int hard_server_limit, hard_thread_limit;
+    int i, j;
+    worker_score *scoreboard;
+
+    ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &hard_server_limit);
+    ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &hard_thread_limit);
+
+    for (i = 0; i < hard_server_limit; i++) {
+        for (j = 0; j < hard_thread_limit; j++) {
+            scoreboard = ap_get_scoreboard_worker(i, j);
+            if (scoreboard->status == SERVER_READY)
+                return 0;
+        }
+    }
+
+    /* We are the only available server, so go ahead.  Maybe this should
+     * be optimized out so it's only in debug builds? */
+    if (ap_exists_config_define("ONE_PROCESS")) {
+        return 0;
+    }
+
+    ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r,
+                  "Maximum number of FTP sessions reached.");
+    return 1;
+}

Propchange: incubator/mod_ftp/trunk/src/ftp_util.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/mod_ftp/trunk/src/ftp_util.c
------------------------------------------------------------------------------
    svn:executable = 

Added: incubator/mod_ftp/trunk/src/mod_ftp.c
URL: http://svn.apache.org/viewcvs/incubator/mod_ftp/trunk/src/mod_ftp.c?rev=306631&view=auto
==============================================================================
--- incubator/mod_ftp/trunk/src/mod_ftp.c (added)
+++ incubator/mod_ftp/trunk/src/mod_ftp.c Thu Oct  6 06:16:28 2005
@@ -0,0 +1,917 @@
+/* Copyright 1999-2005 The Apache Software Foundation or its licensors, as
+ * applicable.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Original Copyright (c) Covalent Technologies 2001-2005
+ *
+ * FTP Protocol module for Apache 2.0
+ */
+
+#include "mod_ftp.h"
+#include "ftp_config.h"
+
+#define FTP_SERVER_STRING PACKAGE "/" VERSION
+
+
+static ap_filter_rec_t *ftp_crlf_filter_handle;
+static ap_filter_rec_t *ftp_data_out_filter_handle;
+static ap_filter_rec_t *ftp_byterange_filter_handle;
+ap_filter_rec_t *ftp_input_filter_handle;
+ap_filter_rec_t *ftp_content_length_filter_handle;
+ap_filter_rec_t *ftp_ssl_input_filter_handle;
+ap_filter_rec_t *ftp_ssl_output_filter_handle;
+
+/* Register callbacks for mod_log_config. */
+static int ftp_pre_config(apr_pool_t *p, apr_pool_t *plog,
+                          apr_pool_t *ptemp)
+{
+    APR_OPTIONAL_FN_TYPE(ap_register_log_handler) *log_pfn_register;
+
+    /* Find the handles of non-ftp filters we will be using
+     *
+     * This must be done after register_hooks since SSL 
+     * may not be loaded before FTP, but before we actually
+     * process the config because we test those filter handles
+     * to determine if SSL-related directives are valid.
+     */
+    ftp_byterange_filter_handle 
+        = ap_get_output_filter_handle("BYTERANGE");
+    ftp_content_length_filter_handle 
+	= ap_get_output_filter_handle("CONTENT_LENGTH"); 
+    ftp_ssl_input_filter_handle 
+        = ap_get_input_filter_handle(FTP_SSL_FILTER);
+    ftp_ssl_output_filter_handle 
+        = ap_get_output_filter_handle(FTP_SSL_FILTER);
+
+    /* Register our custom log format flags
+     */
+    log_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler);
+
+    if (log_pfn_register) {
+        log_pfn_register(p, "M", ftp_log_transfer_mode, 0);
+        log_pfn_register(p, "F", ftp_log_action_flags, 0);
+        log_pfn_register(p, "d", ftp_log_transfer_direction, 0);
+        log_pfn_register(p, "W", ftp_log_accessed_anonymously, 0);
+        log_pfn_register(p, "S", ftp_log_service_name, 0);
+        log_pfn_register(p, "Z", ftp_log_auth_method, 0);
+        log_pfn_register(p, "Y", ftp_log_auth_user_id, 0);
+    }
+
+    return OK;
+}
+
+
+static int ftp_post_config(apr_pool_t *p, apr_pool_t *plog,
+                           apr_pool_t *ptemp, server_rec *s)
+{
+    server_rec *base = s;
+
+    ap_add_version_component(p, FTP_SERVER_STRING); 
+
+    /* Fixup base server and virtual host default values
+     */
+    for (; s; s = s->next) {
+        ftp_server_config *fsc = ftp_get_module_config(s->module_config);
+
+        if (fsc->fileperms == APR_OS_DEFAULT)
+            fsc->fileperms = FTP_DEFAULT_UMASK;
+
+        /* In the config phase, ->fileperms was a negative umask.
+         * for operation, exchange this with a positive protections
+         * to pass to the apr_file_open protection flag.
+         */
+        fsc->fileperms = (APR_UREAD | APR_UWRITE | 
+                          APR_GREAD | APR_GWRITE | 
+                          APR_WREAD | APR_WWRITE)                          
+                       & ~fsc->fileperms;
+
+        if (fsc->timeout_login == FTP_UNSPEC)
+            fsc->timeout_login = FTP_TIMEOUT_LOGIN;
+        if (fsc->timeout_idle  == FTP_UNSPEC)
+            fsc->timeout_idle  = FTP_TIMEOUT_IDLE;
+        if (fsc->timeout_data  == FTP_UNSPEC)
+            fsc->timeout_data  = FTP_TIMEOUT_DATA;
+
+        if (fsc->max_login_attempts == FTP_UNSPEC)
+            fsc->max_login_attempts = FTP_MAX_LOGINS;
+
+        if (fsc->active_min == FTP_UNSPEC)
+            fsc->active_min = fsc->active_max = -1;
+
+        if (fsc->pasv_min == FTP_UNSPEC)
+            fsc->pasv_min = fsc->pasv_max = 0;
+
+        if (fsc->data_block_size == FTP_UNSPEC)
+            fsc->data_block_size = FTP_DATA_BLOCK_SIZE;
+
+        if (fsc->limit_peruser == FTP_UNSPEC)
+            fsc->limit_peruser = 0;
+        if (fsc->limit_perip == FTP_UNSPEC)
+            fsc->limit_perip = 0;
+        if (fsc->limit_perserver == FTP_UNSPEC)
+            fsc->limit_perserver = 0;
+
+        fsc->limitdbfile = ap_server_root_relative(p, fsc->limitdbfile);
+
+    }
+
+    if (ftp_mutexdb_init(base, p) != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_CRIT, 0, base,
+                     "Could not initialize FTP mutex");
+        return HTTP_INTERNAL_SERVER_ERROR;
+    }
+
+    apr_pool_cleanup_register(p, base, ftp_mutexdb_cleanup,
+                              apr_pool_cleanup_null);
+    return OK;
+}
+
+static void ftp_child_init(apr_pool_t *p, server_rec *s)
+{
+    ftp_mutexdb_child_init(s, p);
+}
+
+static void *create_ftp_server_config(apr_pool_t *p, server_rec *s)
+{
+    ftp_server_config *fsc  = apr_pcalloc(p, sizeof(*fsc));
+
+    fsc->fileperms          = APR_OS_DEFAULT;
+    fsc->timeout_login      = FTP_UNSPEC;
+    fsc->timeout_idle       = FTP_UNSPEC;
+    fsc->timeout_data       = FTP_UNSPEC;
+    fsc->max_login_attempts = FTP_UNSPEC;
+    
+    fsc->active_min         = FTP_UNSPEC;
+    fsc->active_max         = FTP_UNSPEC;
+    fsc->pasv_min           = FTP_UNSPEC;
+    fsc->pasv_max           = FTP_UNSPEC;
+
+    fsc->data_block_size    = FTP_UNSPEC;
+
+    fsc->limit_peruser      = FTP_UNSPEC;
+    fsc->limit_perip        = FTP_UNSPEC;
+    fsc->limit_perserver    = FTP_UNSPEC;
+
+    fsc->limitdbfile        = FTP_DEFAULT_DBFILE;
+    return fsc;
+}
+
+void *merge_ftp_server_config(apr_pool_t *p, void *basev, void *addv)
+{
+    ftp_server_config *base = (ftp_server_config *)basev;
+    ftp_server_config *add  = (ftp_server_config *)addv;
+    ftp_server_config *fsc  = apr_palloc(p, sizeof(*fsc));
+
+    /* We default to the add config, so any directive not
+     * handled here won't be inherited from the base server.
+     */
+    memcpy (fsc, add, sizeof(*fsc));
+
+    if (fsc->fileperms == APR_OS_DEFAULT)
+         fsc->fileperms = base->fileperms;
+
+    if (fsc->timeout_login == FTP_UNSPEC)
+        fsc->timeout_login = base->timeout_login;
+    if (fsc->timeout_idle  == FTP_UNSPEC)
+        fsc->timeout_idle  = base->timeout_idle;
+    if (fsc->timeout_data  == FTP_UNSPEC)
+        fsc->timeout_data  = base->timeout_data;
+    
+    if (fsc->max_login_attempts == FTP_UNSPEC)
+        fsc->max_login_attempts = FTP_MAX_LOGINS;
+
+    if (fsc->active_min == FTP_UNSPEC) {
+        fsc->active_min = base->active_min;
+        fsc->active_max = base->active_max;
+    }
+    if (fsc->pasv_min == FTP_UNSPEC) {
+        fsc->pasv_min = base->pasv_min;
+        fsc->pasv_max = base->pasv_max;
+    }
+
+    if (fsc->data_block_size == FTP_UNSPEC) {
+        fsc->data_block_size = base->data_block_size;
+    }
+
+    if (fsc->limit_peruser == FTP_UNSPEC)
+        fsc->limit_peruser = base->limit_peruser;
+    if (fsc->limit_perip == FTP_UNSPEC)
+        fsc->limit_perip = base->limit_perip;
+    if (fsc->limit_perserver == FTP_UNSPEC)
+        fsc->limit_perserver = base->limit_perserver;
+
+    fsc->limitdbfile = ap_server_root_relative(p, base->limitdbfile);
+    
+    if (!fsc->banner_message) {
+        fsc->banner_message = base->banner_message;
+        fsc->banner_message_isfile = base->banner_message_isfile;
+    }
+    if (!fsc->exit_message) {
+        fsc->exit_message = base->exit_message;
+        fsc->exit_message_isfile = base->exit_message_isfile;
+    }
+
+    return fsc;
+}
+
+
+static void *create_ftp_dir_config(apr_pool_t *p, char *dir)
+{
+    ftp_dir_config *conf = apr_pcalloc(p, sizeof(*conf));
+
+    return conf;
+}
+
+void *merge_ftp_dir_config(apr_pool_t *p, void *basev, void *addv)
+{
+    ftp_dir_config *base = (ftp_dir_config *)basev;
+    ftp_dir_config *add  = (ftp_dir_config *)addv;
+    ftp_dir_config *conf  = apr_palloc(p, sizeof(*conf));
+
+    /* We default to the add config, so any directive not
+     * handled here won't be inherited through directories.
+     */
+    memcpy (conf, add, sizeof(*conf));
+
+    /* TODO:
+     *   if we adopt FTPUMask as a per-dir, we must blend it
+     *   with FTPReadmeMessage inheritence here.
+     *   since today we don't inherit FTPReadmeMessage this
+     *   merger is a no-op.
+     */
+
+    return conf;
+}
+
+int ftp_have_ssl(void)
+{   
+    return (ftp_ssl_input_filter_handle 
+                && ftp_ssl_output_filter_handle);
+}
+
+static void ftp_insert_filter(request_rec *r)
+{
+    ftp_connection *fc = ftp_get_module_config(r->request_config);
+    
+    /* XXX: mod_negotiation does not run the create_request hook, so
+     *      we must dig deeper looking for our request_config.  Look
+     *      at this later.
+     */
+    if (!fc) {
+        if (r->main) {
+            fc = ftp_get_module_config(r->main->request_config);
+        }
+        
+        if (!fc) {
+            return;
+        }
+    }
+
+    if (fc->datasock && (fc->filter_mask & FTP_NEED_DATA_OUT)) {
+        ap_add_output_filter_handle(ftp_data_out_filter_handle, fc, r, 
+                                    r->connection);
+    }
+    if (fc->filter_mask & FTP_NEED_BYTERANGE) {
+        ap_add_output_filter_handle(ftp_byterange_filter_handle, NULL, 
+                                    r, r->connection);
+    }
+    if (fc->filter_mask & FTP_NEED_CRLF) {
+        /* CRLF filter has a single int context, "did we last have a CR?" */
+        ap_add_output_filter_handle(ftp_crlf_filter_handle, 
+                                    apr_pcalloc(r->pool, sizeof(int*)),
+                                    r, r->connection);
+    }
+}
+
+/*
+ * The create request hook.  Used when Apache does an internal redirect
+ */
+static int ftp_create_request(request_rec *r)
+{
+    if (r->main) {
+        ftp_set_module_config(r->request_config,
+                              ftp_get_module_config(r->main->request_config));
+    }
+
+    return OK;
+}
+
+/* This is something of a hack.  The idea is that we want to be able to look
+ * at a string with a '*' wildcard, and a string that matches the pattern,
+ * and determine which part of the second string matches the wildcard.  This
+ * allows us to extract that information, which is later used to setup
+ * the require line properly if we are ensuring that users can not switch
+ * to other user's home directories.
+ */
+static char *find_dir(apr_pool_t *p, const char *wildcard, 
+                      const char *curr_file)
+{
+    char *dirname;
+    size_t i;
+
+    for (i = 0; i < strlen(wildcard); i++) {
+        if (wildcard[i] == curr_file[i]) {
+            /* So far the strings are equal */
+            continue;
+        }
+        if (wildcard[i] == '*' && wildcard[i + 1] == '/') {
+            /* Now we have the portion we want to match against.  Find the
+             * corresponding string in the current file.
+             */
+            size_t j = i;
+            while (curr_file[j] && curr_file[j] != '/') {
+                j++;
+            }
+            dirname = apr_pstrmemdup(p, curr_file + i, j - i);
+            return dirname;
+        }
+        return NULL;
+    }
+    return NULL;
+} 
+
+/*
+ * The check_user_id hook.  This is a hack that allows us to ensure that only
+ * the owner of the directory can get access to the current directory.  If the
+ * user has configured:
+ *         Require dir-name
+ * for the current directory, then we require that the logged in user match
+ * the name of the directory.
+ */
+static int ftp_check_user_id(request_rec *r)
+{
+    apr_array_header_t *newrequire;
+    const apr_array_header_t *reqs_arr = ap_requires(r);
+    require_line *reqs;
+    const char *t, *w;
+    core_dir_config *conf;
+    int x;
+
+    conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+                                                   &core_module);
+
+    if (!reqs_arr) {
+        return DECLINED;
+    }
+
+    newrequire = apr_array_make(r->pool, 2, sizeof(require_line));
+
+    reqs = (require_line *)reqs_arr->elts;
+    for (x = 0; x < reqs_arr->nelts; x++) {
+        require_line *rl;
+
+        rl = (require_line *)apr_array_push(newrequire);
+        rl->method_mask = reqs[x].method_mask;
+
+        t = reqs[x].requirement;
+        w = ap_getword_white(r->pool, &t);
+        if (!strcmp(w, "user")) {
+            rl->requirement = apr_pstrdup(r->pool, w);
+            while (strcmp(w = ap_getword_white(r->pool, &t), "")) {
+                if (!strcmp(w, "dir-name")) {
+                    char *dirname = find_dir(r->pool, conf->d, r->filename);
+                    rl->requirement = apr_pstrcat(r->pool, rl->requirement, 
+                                                  " ", dirname, NULL);
+                }
+                else {
+                    rl->requirement = apr_pstrcat(r->pool, rl->requirement,
+                                                  " ", w, NULL);
+                }
+            }
+        }
+        else {
+            rl->requirement = apr_pstrdup(r->pool, reqs[x].requirement);
+        }
+    }
+    conf->ap_requires = newrequire;
+
+    return DECLINED;
+}
+
+/*
+ * Handle FTP module directives
+ */
+
+/*
+ * Generic function used for setting values in the server config.
+ * Adapted from ap_set_int_slot, since it only works with per directory
+ * configurations.
+ */
+static const char *ftp_set_int_slot(cmd_parms *cmd, void *dummy,
+                                    const char *arg)
+{
+    char *endptr;
+    char *error_str = NULL;
+    int offset = (int) (long) cmd->info;
+    ftp_server_config *fsc =
+        ftp_get_module_config(cmd->server->module_config);
+
+    *(int *) ((char*)fsc + offset) = strtol(arg, &endptr, 10);
+
+    if ((*arg == '\0') || (*endptr != '\0')) {
+        error_str = apr_psprintf(cmd->pool,
+                     "Invalid value for directive %s, expected integer",
+                     cmd->directive->directive);
+    }
+
+    return error_str;
+}
+
+
+static const char *ftp_enable(cmd_parms *cmd, void *dummy, int arg)
+{
+    ftp_server_config *fsc =
+        ftp_get_module_config(cmd->server->module_config);
+
+    fsc->enabled = arg;
+
+    return NULL;
+}
+
+static const char *ftp_umask(cmd_parms *cmd, void *dummy, const char *arg)
+{
+#ifdef HAVE_FCHMOD
+    int umask, mode;
+    char *endp;
+    char *error_str = NULL;
+
+    ftp_server_config *fsc =
+      ftp_get_module_config(cmd->server->module_config);
+  
+    umask = strtol(arg, &endp, 8);
+    mode = umask & 0666;
+
+    if ((*arg == '\0') || (*endp != '\0')) {
+        error_str = apr_psprintf(cmd->pool,
+                                 "%s is not valid for %s", arg,
+                                 cmd->directive->directive);
+    }
+    else {
+        fsc->fileperms = ftp_unix_mode2perms(mode);
+    }
+    return error_str;
+#else
+    return "The FTPUmask directive is not supported on this platform";
+#endif
+}
+
+static const char *ftp_implicit_ssl(cmd_parms *cmd, void *dummy, int arg)
+{
+    ftp_server_config *fsc =
+	ftp_get_module_config(cmd->server->module_config);
+
+    if (!ftp_have_ssl()) {
+        return "No SSL module found, cannot enable implicit SSL";
+    }
+
+    fsc->implicit_ssl = arg;
+    return NULL;
+}
+
+static const char *ftp_set_jail(cmd_parms *cmd, void *dummy, int arg)
+{
+    ftp_server_config *fsc =
+	ftp_get_module_config(cmd->server->module_config);
+
+    fsc->jailuser = arg;
+    return NULL;
+}
+
+static const char *ftp_options(cmd_parms *cmd, void *dummy,
+                               const char *raw)
+{
+    ftp_server_config *fsc =
+	ftp_get_module_config(cmd->server->module_config);
+
+    while (raw[0]) {
+        int opt_mask = 0;
+        char *op = ap_getword_conf(cmd->pool, &raw);
+        
+        if (!strcasecmp(op, "RequireSSL")) {
+            opt_mask = FTP_OPT_REQUIRESSL;
+        }
+        else if (!strcasecmp(op, "CheckMaxClients")) {
+            opt_mask = FTP_OPT_CHECKMAXCLIENTS;
+        }
+        else if (!strcasecmp(op, "RemoveUserGroup")) {
+            opt_mask = FTP_OPT_REMOVEUSERGROUP;
+        }
+        else if (!strcasecmp(op, "NLSTShowDirs")) {
+            opt_mask = FTP_OPT_NLSTSHOWDIRS;
+        }
+        else if (!strcasecmp(op, "NLSTIsLIST")) {
+            opt_mask = FTP_OPT_NLSTISLIST;
+        }
+        else if (!strcasecmp(op, "LISTIsNLST")) {
+            opt_mask = FTP_OPT_LISTISNLST;
+        }
+        else if (!strcasecmp(op, "CreateHomeDirs")) {
+            opt_mask = FTP_OPT_CREATEHOMEDIRS;
+        }
+        else if (!strcasecmp(op, "ShowUnAuthorizedFiles")) {
+            opt_mask = FTP_OPT_SHOWUNAUTH;
+        }
+        else if (!strcasecmp(op, "AllowProxyPORT")) {
+            opt_mask = FTP_OPT_ALLOWPROXYPORT;
+        }
+        else if (!strcasecmp(op, "AllowProxyPASV")) {
+            opt_mask = FTP_OPT_ALLOWPROXYPASV;
+        }
+        else {
+            return apr_pstrcat(cmd->pool, "Illegal FTPOption ", op, NULL);
+        }
+        fsc->options |= opt_mask;
+    }
+    if ((fsc->options & FTP_OPT_LISTISNLST) && (fsc->options & FTP_OPT_NLSTISLIST)) {
+        return "LISTISNLST and NLSTISLIST are mutually exclusive options";
+    }
+    return NULL;
+}
+
+static const char *ftp_set_pasv_addr(cmd_parms *cmd, void *dummy,
+                                     const char *addr)
+{
+    apr_uint32_t ipaddr;
+    ftp_server_config *fsc =
+        ftp_get_module_config(cmd->server->module_config);
+
+    if ((ftp_inet_pton(AF_INET, addr, &ipaddr)) != 1) {
+        return apr_pstrcat(cmd->pool, "Invalid IP address for ",
+                           cmd->directive->directive, " (", addr, ")", NULL);
+    }
+    
+    fsc->pasv_addr = apr_pstrdup(cmd->pool, addr);
+
+    return NULL;
+}
+
+static const char *ftp_set_pasv_bindaddr(cmd_parms *cmd, void *dummy,
+                                         const char *addr)
+{
+    apr_uint32_t ipaddr;
+    ftp_server_config *fsc =
+        ftp_get_module_config(cmd->server->module_config);
+
+    if ((ftp_inet_pton(AF_INET, addr, &ipaddr)) != 1) {
+        return apr_pstrcat(cmd->pool, "Invalid IP address for ",
+                           cmd->directive->directive, " (", addr, ")", NULL);
+    }
+
+    fsc->pasv_bindaddr = apr_pstrdup(cmd->pool, addr);
+
+    return NULL;
+}
+
+static const char *ftp_set_message_generic(cmd_parms *cmd, const char *arg,
+                                           const char **dest, int *file_flag)
+{
+    apr_finfo_t finfo;
+    apr_status_t rv;
+
+    if (*dest != NULL) {
+        ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0,
+                     cmd->server, "Ignoring duplicate message file: %s", arg);
+        return NULL;
+    }
+
+    if (!strncmp(arg, "file:", 5)) {
+        rv = apr_stat(&finfo, 
+                      ap_server_root_relative(cmd->temp_pool, arg + 5),
+                      APR_FINFO_TYPE, cmd->temp_pool);
+        if (rv != APR_SUCCESS || finfo.filetype != APR_REG) {
+            return apr_pstrcat(cmd->pool, "Invalid message file: ",
+                               arg + 5, NULL);
+        }
+        
+        *(file_flag) = 1;
+        *(dest) = ap_server_root_relative(cmd->pool, arg + 5);
+    }
+    else {
+        *(dest) = apr_pstrdup(cmd->pool, arg);
+    }
+
+    return NULL;
+}
+
+static const char *ftp_set_banner_message(cmd_parms *cmd, void *dummy,
+                                          const char *arg)
+{
+    ftp_server_config *fsc =
+        ftp_get_module_config(cmd->server->module_config);
+
+    return ftp_set_message_generic(cmd, arg, &fsc->banner_message,
+                                   &fsc->banner_message_isfile);
+}
+
+static const char *ftp_set_exit_message(cmd_parms *cmd, void *dummy,
+                                        const char *arg)
+{
+    ftp_server_config *fsc =
+        ftp_get_module_config(cmd->server->module_config);
+
+    return ftp_set_message_generic(cmd, arg, &fsc->exit_message,
+                                   &fsc->exit_message_isfile);
+}
+
+static const char *ftp_set_readme_message(cmd_parms *cmd, void *dconf,
+                                          const char *arg)
+{
+    ftp_dir_config *d = dconf;
+
+    d->path = apr_pstrdup(cmd->pool, cmd->path);
+    return ftp_set_message_generic(cmd, arg, &d->readme, &d->readme_isfile);
+}
+
+static const char *ftp_set_pasv_range(cmd_parms *cmd, void *dummy,
+                                      const char *min, const char *max)
+{
+    char *error_str = NULL;
+    
+    ftp_server_config *fsc = 
+        ftp_get_module_config(cmd->server->module_config);
+    
+    /* XXX: better error handling */
+    fsc->pasv_min = (apr_port_t) atoi(min);
+    fsc->pasv_max = (apr_port_t) atoi(max);
+
+    if (fsc->pasv_min > fsc->pasv_max) {
+        error_str = apr_psprintf(cmd->pool,
+                                 "Invalid range for %s (%s > %s)",
+                                 cmd->directive->directive, min, max);
+    }
+    
+    return error_str;
+}
+
+static const char *ftp_set_active_ports(cmd_parms *cmd, void *dummy,
+                                        const char *min, const char *max)
+{
+    char *error_str = NULL;
+    
+    ftp_server_config *fsc = 
+        ftp_get_module_config(cmd->server->module_config);
+    
+    /* XXX: better error handling */
+    fsc->active_min = atoi(min);
+    if (!max) {
+        fsc->active_max = fsc->active_min;
+    }
+    else {
+        fsc->active_max = atoi(max);
+    }
+
+    if (fsc->active_min > fsc->active_max) {
+        error_str = apr_psprintf(cmd->pool,
+                                 "Invalid range for %s (%s > %s)",
+                                 cmd->directive->directive, min, max);
+    }
+    
+    return error_str;
+}
+
+static const char *ftp_set_homedir(cmd_parms *cmd, void *dummy,
+                                   const char *dir)
+{
+    ftp_server_config *fsc =
+        ftp_get_module_config(cmd->server->module_config);
+
+    if (*dir != '/') {
+        return apr_pstrcat(cmd->pool, "Path for ", cmd->directive->directive,
+                           " must be absolute (", dir, ")", NULL);
+    }
+
+    fsc->homedir = apr_pstrdup(cmd->pool, dir);
+
+    return NULL;
+}
+
+static const char *ftp_set_docrootenv(cmd_parms *cmd, void *dummy,
+                                      const char *var)
+{
+    ftp_server_config *fsc =
+        ftp_get_module_config(cmd->server->module_config);
+
+    fsc->docrootenv = apr_pstrdup(cmd->pool, var);
+
+    return NULL;
+}
+
+static const char *ftp_set_limit_peruser(cmd_parms *cmd, void *dummy,
+                                      const char *limit)
+{
+    char *error_str = NULL;
+    ftp_server_config *fsc;
+
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+    
+    fsc = ftp_get_module_config(cmd->server->module_config);
+
+    /* XXX: better error handling */
+    fsc->limit_peruser = (apr_port_t) atoi(limit);
+
+    if (fsc->limit_peruser < 0) {
+        error_str = apr_psprintf(cmd->pool,
+                                 "%s value must be 0 or greater (%d)",
+                                 cmd->directive->directive, limit);
+    }
+
+    return error_str;
+}
+
+static const char *ftp_set_limit_perip(cmd_parms *cmd, void *dummy,
+                                         const char *limit)
+{
+    char *error_str = NULL;
+    ftp_server_config *fsc;
+    
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+    
+    fsc = ftp_get_module_config(cmd->server->module_config);
+    
+    /* XXX: better error handling */
+    fsc->limit_perip = (apr_port_t) atoi(limit);
+    
+    if (fsc->limit_perip < 0) {
+        error_str = apr_psprintf(cmd->pool,
+                                 "%s value must be 0 or greater (%d)",
+                                 cmd->directive->directive, limit);
+    }
+    
+    return error_str;
+}
+
+static const char *ftp_set_limit_perserver(cmd_parms *cmd, void *dummy,
+                                         const char *limit)
+{
+    char *error_str = NULL;
+    ftp_server_config *fsc;
+
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+
+    fsc = ftp_get_module_config(cmd->server->module_config);
+    
+    /* XXX: better error handling */
+    fsc->limit_perserver = (apr_port_t) atoi(limit);
+
+    if (fsc->limit_perserver < 0) {
+        error_str = apr_psprintf(cmd->pool,
+                                 "%s value must be 0 or greater (%d)",
+                                 cmd->directive->directive, limit);
+    }
+
+    return error_str;
+}
+
+static const char *ftp_set_dbfile(cmd_parms *cmd, void *dummy,
+                                  const char *dbfile)
+{
+    ftp_server_config *fsc;
+
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+
+    fsc = ftp_get_module_config(cmd->server->module_config);
+    
+    fsc->limitdbfile = ap_server_root_relative(cmd->pool, dbfile);
+    if (!fsc->limitdbfile) {
+        return apr_pstrcat(cmd->pool, "Invalid FTPLimitDBFile value: ",
+                           dbfile, NULL);
+    }
+
+    return NULL;
+}
+
+/*
+ * Setup command table
+ */
+static const command_rec ftp_cmds[] = {
+    AP_INIT_FLAG("FTP", ftp_enable, NULL, RSRC_CONF,
+		 "Run an FTP server on this host"),
+    AP_INIT_TAKE1("FTPUmask", ftp_umask, NULL, RSRC_CONF,
+		  "Set the umask for created files"),
+    AP_INIT_TAKE1("FTPTimeoutLogin", ftp_set_int_slot,
+                  (void *)APR_XtOffsetOf(ftp_server_config, 
+                                         timeout_login), RSRC_CONF, 
+                  "Idle time allowed when logging in"),
+    AP_INIT_TAKE1("FTPTimeoutIdle", ftp_set_int_slot, 
+                  (void *)APR_XtOffsetOf(ftp_server_config, 
+                                         timeout_idle), RSRC_CONF, 
+                  "Idle time allowed during a FTP session"),
+    AP_INIT_TAKE1("FTPTimeoutData", ftp_set_int_slot, 
+                  (void *)APR_XtOffsetOf(ftp_server_config, 
+                                         timeout_data), RSRC_CONF, 
+                  "Idle time allowed during a data transfer"),
+    AP_INIT_TAKE1("FTPMaxLoginAttempts", ftp_set_int_slot,
+                  (void *)APR_XtOffsetOf(ftp_server_config,
+                                         max_login_attempts), RSRC_CONF, 
+		  "Maximum number of login attempts"),
+    AP_INIT_FLAG("FTPImplicitSSL", ftp_implicit_ssl, NULL, RSRC_CONF, 
+                 "Use SSL implicitly."),
+    AP_INIT_RAW_ARGS("FTPOptions", ftp_options, NULL, RSRC_CONF, 
+                     "Set options for this server"),
+    AP_INIT_TAKE1("FTPPASVaddr", ftp_set_pasv_addr, NULL, RSRC_CONF,
+                  "Set the allowed PASV server IP address for the data "
+                  "channel"),
+    AP_INIT_TAKE1("FTPPASVbindaddr", ftp_set_pasv_bindaddr, NULL, RSRC_CONF, 
+                  "Set and bind the allowed PASV server IP "
+                  "address for the data channel"),
+    AP_INIT_TAKE2("FTPPASVrange", ftp_set_pasv_range, NULL, RSRC_CONF, 
+                  "Set the allowed PASV port range"),
+    AP_INIT_TAKE1("FTPBannerMessage", ftp_set_banner_message, NULL, RSRC_CONF, 
+                  "Set initial login message"),
+    AP_INIT_TAKE1("FTPExitMessage", ftp_set_exit_message, NULL, RSRC_CONF, 
+                  "Set logout message"),
+    AP_INIT_TAKE1("FTPHomeDir", ftp_set_homedir, NULL, RSRC_CONF, 
+                  "Set the path to directory containing user's "
+                  "home directories"),
+    AP_INIT_TAKE1("FTPDocRootEnv", ftp_set_docrootenv, NULL, RSRC_CONF, 
+                  "Set the DocumentRoot based on the given environment "
+                  "variable, such as a per-user LDAP property"),
+    AP_INIT_FLAG("FTPJailUser", ftp_set_jail, NULL, RSRC_CONF,
+                 "Users are not allowed to leave their home directories"),
+    AP_INIT_TAKE12("FTPActiveRange", ftp_set_active_ports, NULL, RSRC_CONF,
+                   "Ports the server will use for connecting to the client."),
+    AP_INIT_TAKE1("FTPReadmeMessage", ftp_set_readme_message, NULL, OR_ALL, 
+                  "Set per-directory Readme file"),
+    AP_INIT_TAKE1("FTPLimitLoginUser", ftp_set_limit_peruser, NULL, RSRC_CONF,
+                  "Set the maximum number of concurrent logins per user"),
+    AP_INIT_TAKE1("FTPLimitLoginIP", ftp_set_limit_perip, NULL, RSRC_CONF,
+                  "Set the maximum number of concurrent logins per IP address"),
+    AP_INIT_TAKE1("FTPLimitLoginServer", ftp_set_limit_perserver, NULL, RSRC_CONF,
+                  "Set the maximum number of concurrent logins per server"),
+    AP_INIT_TAKE1("FTPLimitDBFile", ftp_set_dbfile, NULL, RSRC_CONF,
+                  "Set the location for the Login Limit DB file"),
+    AP_INIT_TAKE1("FTPDataBlockSize", ftp_set_int_slot, 
+                  (void *)APR_XtOffsetOf(ftp_server_config, 
+                                         data_block_size), RSRC_CONF, 
+                  "Block size in bytes to use during data transfers"),
+    {NULL}
+};
+
+static void register_hooks(apr_pool_t *p)
+{
+    static const char *const pred[] = { "mod_log_config.c",
+					NULL };
+
+    static const char *const post[] = { NULL };
+
+    ap_hook_pre_config(ftp_pre_config, pred, NULL, APR_HOOK_MIDDLE);
+    ap_hook_post_config(ftp_post_config, post, NULL, APR_HOOK_MIDDLE);
+    ap_hook_child_init(ftp_child_init, NULL, NULL, APR_HOOK_MIDDLE);
+    ap_hook_process_connection(ftp_process_connection, NULL, NULL,
+			       APR_HOOK_MIDDLE);
+    ap_hook_create_request(ftp_create_request,NULL,NULL,APR_HOOK_MIDDLE);
+    ap_hook_check_user_id(ftp_check_user_id, NULL, NULL, APR_HOOK_REALLY_FIRST);
+    ap_hook_insert_filter(ftp_insert_filter,NULL,NULL,APR_HOOK_MIDDLE);
+
+    /* FTP filters */
+    ftp_input_filter_handle = ap_register_input_filter("FTP_PROTOCOL",
+                                                       ftp_protocol_filter,
+                                                       NULL,
+                                                       AP_FTYPE_PROTOCOL);
+    ftp_crlf_filter_handle = ap_register_output_filter("FTP_CRLF",   
+						       ftp_crlf_filter,
+						       NULL,
+						       AP_FTYPE_RESOURCE - 1);
+    ftp_data_out_filter_handle = ap_register_output_filter("FTP_DATA_OUT",   
+						           ftp_data_out_filter,
+						           NULL,
+						           AP_FTYPE_NETWORK - 2);
+
+    /* Register all core command handlers */
+    ftp_register_core_cmds(p);
+}
+
+FTP_DECLARE_DATA module ftp_module = {
+    STANDARD20_MODULE_STUFF,
+    create_ftp_dir_config,      /* create per-directory config structure */
+    merge_ftp_dir_config,        /* merge per-directory config structures */
+    create_ftp_server_config,   /* create per-server config structure */
+    merge_ftp_server_config,    /* merge per-server config structures */
+    ftp_cmds,                   /* command apr_table_t */
+    register_hooks              /* register hooks */
+};

Propchange: incubator/mod_ftp/trunk/src/mod_ftp.c
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: incubator/mod_ftp/trunk/src/mod_ftp.c
------------------------------------------------------------------------------
    svn:executable =