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 [5/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/

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

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

Added: incubator/mod_ftp/trunk/src/ftp_connection.c
URL: http://svn.apache.org/viewcvs/incubator/mod_ftp/trunk/src/ftp_connection.c?rev=306631&view=auto
==============================================================================
--- incubator/mod_ftp/trunk/src/ftp_connection.c (added)
+++ incubator/mod_ftp/trunk/src/ftp_connection.c Thu Oct  6 06:16:28 2005
@@ -0,0 +1,277 @@
+/* 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"
+
+/*
+ * initialize_ftp_connection: Used to initialize the ftp_connection structure
+ *                            when accepting an incoming FTP connection.
+ *
+ * Arguments: c - The connection associated with the session
+ *            fc - The ftp connection to initialize
+ *
+ * Returns: nothing 
+ */
+static void initialize_ftp_connection(conn_rec *c, ftp_connection *fc)
+{
+    ftp_server_config *fsc =
+      ftp_get_module_config(c->base_server->module_config);
+
+    /* The ftp_connection structure is calloc'ed so only initalize
+     * the members that we need to. */
+
+    fc->connection       = c;
+    fc->user             = "unknown";
+    fc->auth             = FTP_AUTH_NONE;
+    fc->prot             = FTP_PROT_CLEAR;
+    fc->cwd              = "/";
+    fc->timeout          = fsc->timeout_idle;
+    fc->type             = TYPE_A;
+    fc->passive_created  = -1;
+}
+
+/* ftp_ssl_init: Fakes a read on the SSL filters to force initialization.
+ *
+ * Arguments: cdata - The data connection
+ *
+ * Return: apr_status_t
+ */
+apr_status_t ftp_ssl_init(conn_rec *cdata)
+{
+    apr_bucket_brigade *bb;
+    apr_status_t rv;
+    apr_socket_t *client_socket;
+
+    /* This is handled in the NET_TIME filter, which unfortunately
+     * ignores the timeout for the purpose of AP_MODE_INIT.
+     * Fix a timeout so the core read filter will behave.
+     */
+    client_socket = ap_get_module_config(cdata->conn_config,
+                                         &core_module);
+    apr_socket_timeout_set(client_socket, cdata->base_server->timeout);
+
+    bb = apr_brigade_create(cdata->pool, cdata->bucket_alloc);
+    rv = ap_get_brigade(cdata->input_filters, bb, AP_MODE_INIT,
+                        APR_BLOCK_READ, 0);
+    apr_brigade_destroy(bb);
+
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, cdata->base_server,
+                     "Couldn't set SO_NONBLOCK socket option");
+    }
+    
+    return rv;
+}
+
+/*
+ * ftp_send_welcome: Send the server welcome string to the client 
+ * 
+ * Arguments: fc - The ftp connection to send the welcome message to 
+ * 
+ * Returns: apr_status_t
+ */
+static apr_status_t ftp_send_welcome(ftp_connection *fc)
+{
+    conn_rec *c = fc->connection;
+    ftp_server_config *fsc = 
+        ftp_get_module_config(c->base_server->module_config);
+    apr_status_t rv;
+
+    /* Check if a login message has been configured */
+    if (fsc->banner_message) {
+        if (fsc->banner_message_isfile) {
+            rv = ftp_show_file(c->output_filters, c->pool, 
+                               FTP_REPLY_SERVICE_READY, fc, 
+                               fsc->banner_message);
+            if (rv != APR_SUCCESS) {
+                return rv;
+            }
+        }
+        else {
+            char outbuf[BUFSIZ];
+
+            ftp_message_generate(fc, fsc->banner_message,
+                                 outbuf, sizeof(outbuf));
+
+            rv = ftp_reply(fc, c->output_filters, c->pool,
+                           FTP_REPLY_SERVICE_READY, 1, outbuf); 
+
+            if (rv != APR_SUCCESS) {
+                return rv;
+            }
+        }
+    }
+
+    rv = ftp_reply(fc, c->output_filters, c->pool, FTP_REPLY_SERVICE_READY, 0,
+                   "%s FTP Server (%s %s) ready.",
+                   c->base_server->server_hostname,
+                   ap_get_server_version(),
+                   ap_get_server_built());
+    return rv;
+}
+
+/*
+ * ftp_process_connection: Connection handler for the FTP module. 
+ * 
+ * Arguments: c - The incoming connection
+ *
+ * Returns: Returns OK when the connection is to be closed. 
+ */
+int ftp_process_connection(conn_rec *c)
+{
+    ap_filter_t *f;
+    request_rec *r;
+    ftp_connection *fc;
+    apr_status_t rv;
+    int idle_timeout_set = 0;
+    extern ap_filter_rec_t *ftp_ssl_input_filter_handle;
+    extern ap_filter_rec_t *ftp_ssl_output_filter_handle;
+    
+    apr_socket_t *client_socket = ap_get_module_config(c->conn_config,
+                                                       &core_module);
+
+    ftp_server_config *fsc =
+        ftp_get_module_config(c->base_server->module_config);
+
+    if (!fsc->enabled) {
+        return DECLINED;
+    }
+
+    /*
+     * Allocate for FTP connection structure, and initialize 
+     */
+    fc = apr_pcalloc(c->pool, sizeof(*fc));
+    initialize_ftp_connection(c, fc);
+    fc->orig_server = c->base_server;
+    fc->cntlsock = client_socket;
+
+    /* Check for implicit security on the control connection */
+    if (!fsc->implicit_ssl) {
+        for (f = c->output_filters; f; f = f->next) {
+            if (strcasecmp(f->frec->name, FTP_SSL_FILTER) == 0) {
+                fc->ssl_output_ctx = f->ctx;
+                ap_remove_output_filter(f);
+            }
+        }
+        for (f = c->input_filters; f; f = f->next) {
+            if (strcasecmp(f->frec->name, FTP_SSL_FILTER) == 0) {
+                fc->ssl_input_ctx = f->ctx;
+                ap_remove_input_filter(f);
+            }
+        }
+    } else {
+        /* Initialize control connection */
+        if ((rv = ftp_ssl_init(c)) != APR_SUCCESS) {
+            /* Error initializing the connection, most likely from a
+             * client attempting to connect w/o SSL.  Just drop the 
+             * connection */
+            ap_log_error(APLOG_MARK, APLOG_ERR, rv, c->base_server,
+                         "Error initializing SSL connection.  Client "
+                         "connecting without SSL?");
+            return OK;
+        }
+
+        /** 
+         * From draft-murray-auth-ftp-ssl-06.txt:
+         *
+         * For implicit SSL the data connection should be implicitly
+         * protected (i.e. the PBSZ 0, PROT P command sequence is not 
+         * required but the client and server will protect the data channel 
+         * as if it had).
+         *
+         * Support for Implicit SSL has been declared deprecated as of
+         * April 5, 2001 in draft-murray-auth-ftp-ssl-07.txt.
+         */
+        fc->prot = FTP_PROT_PRIVATE;
+        fc->is_secure = 1;
+    }
+
+    rv = ftp_send_welcome(fc);
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, c->base_server,
+                     "Error sending server welcome string");
+        return OK;
+    }
+
+    /*
+     * Begin processing requests 
+     */
+
+    /* Set initial login timeout value */
+    rv = apr_socket_timeout_set(client_socket,
+                          fsc->timeout_login * APR_USEC_PER_SEC);
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, c->base_server,
+                     "Couldn't set SO_TIMEOUT socket option");
+    }
+
+    ap_update_child_status(c->sbh,
+                           SERVER_BUSY_READ, NULL);
+
+    while ((r = ftp_read_request(fc)) != NULL) {
+
+        /* Set socket timeout values.  A user can use the SITE command
+         * to change the idle timeout during an FTP session, but since
+         * we do not support the SITE command, it doesn't make sense to
+         * be setting the socket timeout on every single request.
+         */
+        if (!idle_timeout_set) {
+            rv = apr_socket_timeout_set(client_socket,
+                                  fsc->timeout_idle * APR_USEC_PER_SEC);
+            if (rv != APR_SUCCESS) {
+                ap_log_error(APLOG_MARK, APLOG_ERR, rv, c->base_server,
+                             "Couldn't set socket timeout");
+            }
+            idle_timeout_set = 1;
+        }
+
+        ap_update_child_status(c->sbh,
+                               SERVER_BUSY_WRITE, NULL);
+        
+        ftp_process_request(r);
+        
+        if (ap_extended_status) {
+            ap_increment_counts(c->sbh, r);
+        }
+
+        apr_pool_destroy(r->pool);
+
+        /* Check if the connection should be closed  */
+        if (fc->close_connection) {
+            break;
+        }
+        
+        /* Check if the client has requested a secure connection */
+        if ((fc->auth == FTP_AUTH_TLS || fc->auth == FTP_AUTH_SSL) && 
+            fc->is_secure == 0) {
+            ap_add_output_filter_handle(ftp_ssl_output_filter_handle, 
+                                        fc->ssl_output_ctx, NULL ,c);
+            ap_add_input_filter_handle(ftp_ssl_input_filter_handle, 
+                                       fc->ssl_input_ctx, NULL, c);
+            fc->is_secure = 1;
+        }
+    }
+    if (fc->logged_in) {
+        ftp_limitlogin_loggedout(fc->user, c);
+    }
+    return OK;
+}

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

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

Added: incubator/mod_ftp/trunk/src/ftp_data_connection.c
URL: http://svn.apache.org/viewcvs/incubator/mod_ftp/trunk/src/ftp_data_connection.c?rev=306631&view=auto
==============================================================================
--- incubator/mod_ftp/trunk/src/ftp_data_connection.c (added)
+++ incubator/mod_ftp/trunk/src/ftp_data_connection.c Thu Oct  6 06:16:28 2005
@@ -0,0 +1,289 @@
+/* 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"
+
+/*
+ * ftp_open_datasock: If we are in passive mode we accept and return a 
+ *                    socket.  If we are in active mode, a socket is
+ *                    created based on the fc->clientsa sockaddr and 
+ *                    then returned.
+ * 
+ * Arguments: r - The request.
+ *
+ * Returns: apr_status_t
+ */
+static apr_status_t ftp_open_datasock(request_rec *r)
+{
+    ftp_connection *fc = ftp_get_module_config(r->request_config);
+    conn_rec *c = r->connection;
+    ftp_server_config *fsc;
+    apr_interval_time_t timeout;
+    apr_pollfd_t pollset[2];
+    apr_socket_t *s;
+    apr_status_t rv, res;
+    int n;
+
+    fsc = ftp_get_module_config(c->base_server->module_config);
+
+    /* handle err condition when the creation of the socket had failed,
+     * this will occur if a PORT command has failed */
+    if (!fc->csock) {
+       ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0, c->base_server,
+                    "Nonexistant connection");
+       return APR_EGENERAL;
+    }
+
+    if (fc->passive_created != -1)
+    {
+        pollset[0].desc_type = APR_POLL_SOCKET;
+        pollset[0].desc.s = fc->csock;
+        pollset[0].reqevents = APR_POLLIN;
+        pollset[1].desc_type = APR_POLL_SOCKET;
+        pollset[1].desc.s = fc->cntlsock;
+        pollset[1].reqevents = (APR_POLLIN | APR_POLLPRI);
+
+        /* since it is possible to hang in accept(), poll both
+         * the control and client sockets waiting for activity. */
+        apr_socket_timeout_get(fc->csock, &timeout); /* likely to be -1 */
+        res = apr_poll(pollset, 2, &n, timeout);
+                
+        if (res == APR_SUCCESS) {
+            if (pollset[0].rtnevents & APR_POLLIN) {
+                /* activity on client socket, attempt an accept() */
+                rv = apr_accept(&s, fc->csock, c->pool);
+            } else if (pollset[1].rtnevents & (APR_POLLIN | APR_POLLPRI)) {
+                /* command channel has activity. since we can only do
+                 * a single read ahead operation, no use in looping here
+                 * if the command is not an ABOR.  bail out and let
+                 * command processing occur as normal. */
+                ap_log_error(APLOG_MARK, APLOG_ERR, res, c->base_server,
+                             "Activity on control channel while waiting "
+                             "for client connect, processing command");
+                
+                /* manual cleanup, no need to close the socket */
+                fc->csock = NULL;
+                fc->passive_created = -1;
+                return APR_ECONNRESET;
+            }
+        } else {
+            /* not much we can do, one of our sockets was likely
+             * disconnected */
+            fc->csock = NULL;
+            fc->passive_created = -1;
+            return APR_EGENERAL;
+        }
+
+        /* fc->clientsa used for PORT, so assign it NULL for the PASV command. */
+        fc->clientsa = NULL;
+
+        res = apr_socket_close(fc->csock);
+        fc->csock = NULL;
+        fc->passive_created = -1;
+
+        /* check csock closure first */
+        if (res != APR_SUCCESS) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, res, c->base_server,
+                         "Couldn't close passive connection");
+        }
+        
+        /* retest the apr_accept result from above */
+        if (rv != APR_SUCCESS) {
+            return rv;
+        }
+        fc->datasock = s;
+    }
+    else {
+        if (fc->clientsa) {
+            int tries;
+            for (tries = 0;; tries++) {
+                rv = apr_connect(fc->csock, fc->clientsa);
+                if (rv == APR_SUCCESS) {
+                    break;
+                }
+                if (!APR_STATUS_IS_EAGAIN(rv) || tries > FTP_MAX_TRIES) {
+                    ap_log_error(APLOG_MARK, APLOG_ERR, rv, c->base_server,
+                                 "Couldn't connect to client");
+                    apr_socket_close(fc->csock);
+                    fc->csock = NULL;
+                    fc->passive_created = -1;
+                    return rv;
+                }
+                apr_sleep(tries * APR_USEC_PER_SEC);
+            }
+        }
+        else {
+            return APR_EGENERAL;
+        }
+        fc->datasock = fc->csock;
+        fc->csock = NULL;
+        fc->passive_created = -1;
+    }
+
+    rv = apr_socket_opt_set(fc->datasock, APR_SO_LINGER,
+                          MAX_SECS_TO_LINGER);
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, c->base_server,
+                     "Couldn't set APR_SO_LINGER socket option");
+    }
+    rv = apr_socket_opt_set(fc->datasock, APR_SO_REUSEADDR, 1);
+    if (rv != APR_SUCCESS && rv != APR_ENOTIMPL) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, c->base_server,
+                     "Couldn't set APR_SO_REUSEADDR socket option");
+    }
+    
+    /* Set the default data connection timeout value */
+    rv = apr_socket_timeout_set(fc->datasock,
+                          fsc->timeout_data * APR_USEC_PER_SEC);
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, c->base_server,
+                     "Couldn't set socket timeout");
+    }
+
+    return APR_SUCCESS;
+}
+
+/*
+ * ftp_open_dataconn: Creates the appropriate data channel
+ *                    and initializes the 
+ * 
+ * Arguments: r - The request.
+ *            write - Open for write-to-client only
+ *
+ * Returns: apr_status_t
+ */
+conn_rec *ftp_open_dataconn(request_rec *r, int write_not_read)
+{
+    ftp_server_config *fsc = ftp_get_module_config(r->server->module_config);
+    ftp_connection *fc = ftp_get_module_config(r->request_config);
+    conn_rec *cdata;
+    ap_filter_t *f; 
+
+    if (ftp_open_datasock(r) != APR_SUCCESS) {
+        return NULL;
+    }
+
+    cdata = ap_run_create_connection(r->pool, r->server, fc->datasock, 
+                                     r->connection->id, r->connection->sbh, 
+                                     r->connection->bucket_alloc);
+
+    ap_run_pre_connection(cdata, fc->datasock);
+
+    /* Open data connection only if the connection is from the client's IP address.
+     * All other PASV connection attempts are denied, unless disabled using:
+     *
+     *   FTPOptions AllowProxyPASV
+     */
+    if (fc->clientsa == NULL) { /* Only check PASV, never PORT connections */ 
+        if (!(fsc->options & FTP_OPT_ALLOWPROXYPASV)) {
+            if (strcmp(fc->connection->remote_ip, cdata->remote_ip) != 0) {
+                ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, fc->connection->base_server,
+                             "PASV data connection attempt from %s "
+                             "doesn't match the client IP %s",
+                             cdata->remote_ip, fc->connection->remote_ip);
+                ap_log_error(APLOG_MARK, APLOG_ERR, 0, fc->connection->base_server,
+                             "PASV data connection attempt denied, "
+                             "not configured to AllowProxyPASV");
+                apr_socket_close(fc->datasock);
+                fc->datasock = NULL;
+                return NULL;
+            }
+        }
+        fc->passive_created = 0;
+    }
+
+    if ( write_not_read ) {
+        /* We need the network-level poll filter to watch both the
+         * control incoming command and writable data port conditions
+         */
+        fc->filter_mask += FTP_NEED_DATA_OUT;
+    }
+    else {
+        /* Once Apache has inserted the core i/o filters, we must
+         * insert our idea of a core socket, based on our own
+         * ftp_datasock bucket, instead of the socket_bucket.
+         * This will capture any abort command on the control
+         * socket while actually reading from the data socket.
+         *
+         * Insert this bucket type only for read connections
+         */
+        for (f = cdata->input_filters; f; f = f->next) {
+            if (strcasecmp(f->frec->name, "CORE_IN") == 0) 
+            {
+                core_net_rec *net = f->ctx;
+                apr_bucket *e;
+
+                net->in_ctx = apr_pcalloc(f->c->pool, sizeof(*net->in_ctx));
+                net->in_ctx->b = apr_brigade_create(f->c->pool, f->c->bucket_alloc);
+                net->in_ctx->tmpbb = 
+                    apr_brigade_create(net->in_ctx->b->p,
+                                       net->in_ctx->b->bucket_alloc);
+
+                /* seed the brigade with our client data+control sockets */
+                e = ftp_bucket_datasock_create(fc, f->c->bucket_alloc);
+                APR_BRIGADE_INSERT_TAIL(net->in_ctx->b, e);
+                break;
+            }
+        }
+    }
+
+    /* We initalize the data connection here by adding/removing
+     * the SSL/TLS filters.  We then invoke ftp_ssl_init immediately
+     * for writers, since negotation would normally occur on the
+     * first socket read (and we won't be reading from that socket.)
+     */
+    if (fc->prot == FTP_PROT_CLEAR) {
+        for (f = cdata->output_filters; f; f = f->next) {
+            if (strcasecmp(f->frec->name, FTP_SSL_FILTER) == 0) {
+                ap_remove_output_filter(f);
+            }
+        }
+        for (f = cdata->input_filters; f; f = f->next) {
+            if (strcasecmp(f->frec->name, FTP_SSL_FILTER) == 0) {
+                ap_remove_input_filter(f);
+            }
+        }
+    }
+    else if ((fc->prot == FTP_PROT_PRIVATE) && write_not_read) {
+        if (ftp_ssl_init(cdata) != APR_SUCCESS) {
+            /* In case of failure within ssl_init, 
+             * return no data connection 
+             */
+            apr_socket_close(fc->datasock);
+            fc->datasock = NULL;
+            return NULL;
+        }
+    }
+
+    /*
+     * We now need to remove the NET_TIME filter to allow
+     * use to control timeouts ourselves.
+     */
+     for (f = cdata->input_filters; f; f = f->next) {
+         if (strcasecmp(f->frec->name, "NET_TIME") == 0) {
+             ap_remove_input_filter(f);
+         }
+     }
+
+    return cdata;
+}
+

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

Added: incubator/mod_ftp/trunk/src/ftp_data_filters.c
URL: http://svn.apache.org/viewcvs/incubator/mod_ftp/trunk/src/ftp_data_filters.c?rev=306631&view=auto
==============================================================================
--- incubator/mod_ftp/trunk/src/ftp_data_filters.c (added)
+++ incubator/mod_ftp/trunk/src/ftp_data_filters.c Thu Oct  6 06:16:28 2005
@@ -0,0 +1,309 @@
+/* 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"
+
+/* Reimplement the core i/o filters to insert two-channel
+ * data socket buckets for send or retrieve, monitoring 
+ * the control channel for sideband ABOR-like commands.
+ *
+ * These filters sit below
+ */
+
+static apr_status_t datasock_bucket_read(apr_bucket *a, const char **str,
+                                         apr_size_t *len, apr_read_type_e block)
+{
+    ftp_connection *fc = a->data;
+    apr_socket_t *sd = fc->datasock;
+    conn_rec *c = fc->connection;
+    char *buf;
+    apr_status_t rv;
+    apr_interval_time_t timeout;
+    apr_interval_time_t polltimeout;
+    apr_pollfd_t pollset[2];
+    int n;
+
+    /* command socket heard already?  (e.g. already polled) */
+    rv = ftp_read_ahead_request(fc);
+
+    if ((rv != APR_SUCCESS) &&
+        !(APR_STATUS_IS_EINTR(rv) || (APR_STATUS_IS_EAGAIN(rv)) ||
+          APR_STATUS_IS_EOF(rv))) {
+        return rv;
+    }
+
+    apr_socket_timeout_get(sd, &timeout);
+
+    if (block == APR_NONBLOCK_READ) {
+        apr_socket_timeout_set(sd, 0);
+        polltimeout = 0;
+    }
+    else {
+        polltimeout = timeout;
+    }
+
+    *str = NULL;
+    /* buf allocation must mirror *len initalization below */
+    buf = apr_bucket_alloc(APR_BUCKET_BUFF_SIZE, a->list);
+
+    pollset[0].desc_type = APR_POLL_SOCKET;
+    pollset[0].desc.s = fc->datasock;
+    pollset[0].reqevents = APR_POLLIN; /* APR_POLLOUT for write */
+    pollset[1].desc_type = APR_POLL_SOCKET;
+    pollset[1].desc.s = fc->cntlsock;
+    pollset[1].reqevents = (APR_POLLIN | APR_POLLPRI);
+    pollset[1].rtnevents = 0; /* For poll(1), pretend poll answered this */
+
+    /* XXX: evil, no apr_socket_pool_get accessor available, though */
+    pollset[1].p = pollset[0].p = *((apr_pool_t **)fc->datasock);
+    
+    do {
+        /* Unset so we don't trip over the len when we don't call recv */
+        *len = 0;
+
+        /* If we have already assembled our next_request, don't
+        * bother polling the control connection - we won't read
+        * ahead two commands
+        */
+        rv = apr_poll(pollset, fc->next_request ? 1 : 2,
+                      &n, polltimeout);
+        /* pedantic sanity check, this should never happen */
+        if ((rv == APR_SUCCESS) && (n < 0)) {
+            rv = APR_EGENERAL;
+        }
+
+        if (rv != APR_SUCCESS) {
+            /* The loss of either socket here means
+             * the death of the data connection.
+             * Unset the length to avoid looping
+             */
+            break;
+        }
+        if (pollset[1].rtnevents & (APR_POLLIN | APR_POLLPRI)) {
+            /* command socket heard - but with a full line yet? */
+            rv = ftp_read_ahead_request(fc);
+
+            if ((rv != APR_SUCCESS) &&
+                !(APR_STATUS_IS_EINTR(rv) || (APR_STATUS_IS_EAGAIN(rv)) ||
+                  APR_STATUS_IS_EOF(rv))) {
+                apr_bucket_free(buf);
+                return rv;
+            }
+        }
+
+        if (pollset[0].rtnevents & APR_POLLIN) {
+            /* *len must mirror the apr_bucket_alloc above. */
+            *len = APR_BUCKET_BUFF_SIZE;
+            rv = apr_socket_recv(sd, buf, len);
+        }
+    } while (APR_STATUS_IS_EINTR(rv) 
+                 || (APR_STATUS_IS_EAGAIN(rv) && (block == APR_BLOCK_READ)));
+    /* EINTR, above, is obvious, EAGAIN is less so - win32 (perhaps others)
+     * can trigger POLLIN a little too early, before the recieved packet
+     * has actually been disassembled - so loop again.
+     */
+
+    if (block == APR_NONBLOCK_READ) {
+        apr_socket_timeout_set(sd, timeout);
+    }
+
+    if (rv != APR_SUCCESS && !APR_STATUS_IS_EOF(rv)) {
+        apr_bucket_free(buf);
+        return rv;
+    }
+    /*
+     * If there's more to read we have to keep the rest of the socket
+     * for later. XXX: Note that more complicated bucket types that
+     * refer to data not in memory and must therefore have a read()
+     * function similar to this one should be wary of copying this
+     * code because if they have a destroy function they probably
+     * want to migrate the bucket's subordinate structure from the
+     * old bucket to a raw new one and adjust it as appropriate,
+     * rather than destroying the old one and creating a completely
+     * new bucket.
+     *
+     * Even if there is nothing more to read, don't close the socket here
+     * as we have to use it to send any response :)  We could shut it 
+     * down for reading, but there is no benefit to doing so.
+     */
+    if (*len > 0) {
+        apr_bucket_heap *h;
+        /* Change the current bucket to refer to what we read */
+        a = apr_bucket_heap_make(a, buf, *len, apr_bucket_free);
+        h = a->data;
+        h->alloc_len = APR_BUCKET_BUFF_SIZE; /* note the real buffer size */
+        *str = buf;
+        APR_BUCKET_INSERT_AFTER(a, ftp_bucket_datasock_create(fc, a->list));
+    }
+    else {
+        apr_bucket_free(buf);
+        a = apr_bucket_immortal_make(a, "", 0);
+        *str = a->data;
+    }
+    return APR_SUCCESS;
+}
+
+static apr_bucket *ftp_bucket_datasock_make(apr_bucket *b, ftp_connection *fc)
+{
+    /*
+     * Note that typically the socket is allocated from the connection pool
+     * so it will disappear when the connection is finished. 
+     */
+    b->type        = &ftp_bucket_type_datasock;
+    b->length      = (apr_size_t)(-1);
+    b->start       = -1;
+    b->data        = fc;
+
+    return b;
+}
+
+apr_bucket *ftp_bucket_datasock_create(ftp_connection *fc,
+                                       apr_bucket_alloc_t *list)
+{
+    apr_bucket *b = apr_bucket_alloc(sizeof(*b), list);
+
+    APR_BUCKET_INIT(b);
+    b->free = apr_bucket_free;
+    b->list = list;
+    return ftp_bucket_datasock_make(b, fc);
+}
+
+const apr_bucket_type_t ftp_bucket_type_datasock = {
+    "DATASOCK",
+    5,
+    APR_BUCKET_DATA,
+    apr_bucket_destroy_noop,
+    datasock_bucket_read,
+    apr_bucket_setaside_notimpl, 
+    apr_bucket_split_notimpl,
+    apr_bucket_copy_notimpl
+};
+ 
+ 
+apr_status_t ftp_data_out_filter(ap_filter_t *f, apr_bucket_brigade *bb)
+{
+    ftp_connection *fc = f->ctx;
+    ftp_server_config *fsc;
+    conn_rec *c = fc->connection;
+    apr_status_t rv;
+    apr_interval_time_t polltimeout;
+    apr_pollfd_t pollset[2];
+    apr_bucket_brigade *next_bb;
+    apr_bucket *e;
+    int n;
+
+    /* command socket heard already?  (e.g. already polled) */
+    rv = ftp_read_ahead_request(fc);
+
+    if ((rv != APR_SUCCESS) &&
+        !(APR_STATUS_IS_EINTR(rv) || (APR_STATUS_IS_EAGAIN(rv)) ||
+          APR_STATUS_IS_EOF(rv))) {
+        f->c->aborted = 1;
+        return rv;
+    }
+
+    fsc = ftp_get_module_config(f->r->server->module_config);
+
+    while (!APR_BRIGADE_EMPTY(bb)) {
+
+        /* The brigade can be far to 'chunky' to respect the ABOR
+         * tests we perform below.  Dice up the raw response stream
+         * into chuncks (48000 bytes by default) and and check between
+         * each chunk for a control channel command.  At 48000 this is 
+         * at least 1/minute on a very slow 9600 baud line.
+         */
+        rv = apr_brigade_partition(bb, fsc->data_block_size, &e);
+        if (rv != APR_SUCCESS && rv != APR_INCOMPLETE) {
+            return rv;
+        }
+        next_bb = apr_brigade_split(bb, e);
+
+        /* Poll to see if we are are prepared to pass on the brigade
+         * and the client hasn't ABORted us yet.
+         */
+        apr_socket_timeout_get(fc->datasock, &polltimeout);
+
+        pollset[0].desc_type = APR_POLL_SOCKET;
+        pollset[0].desc.s = fc->datasock;
+        pollset[0].reqevents = APR_POLLOUT;
+        pollset[1].desc_type = APR_POLL_SOCKET;
+        pollset[1].desc.s = fc->cntlsock;
+        pollset[1].reqevents = (APR_POLLIN | APR_POLLPRI);
+        pollset[1].rtnevents = 0; /* For poll(1), pretend poll answered this */
+
+        /* XXX: evil, no apr_socket_pool_get accessor available, though */
+        pollset[1].p = pollset[0].p = *((apr_pool_t **)fc->datasock);
+
+        do {
+            /* If we have already assembled our next_request, don't
+             * bother polling the control connection - we won't read
+             * ahead two commands
+             */
+            rv = apr_poll(pollset, fc->next_request ? 1 : 2,
+                          &n, polltimeout);
+            /* pedantic sanity check, this should never happen */
+            if ((rv == APR_SUCCESS) && (n < 0)) {
+                rv = APR_EGENERAL;
+            }
+
+            if (rv != APR_SUCCESS) {
+                /* The loss of either socket here means
+                 * the death of the data connection.
+                 */
+                break;
+            }
+
+            if (pollset[1].rtnevents & (APR_POLLIN | APR_POLLPRI)) {
+                /* command socket heard - but with a full line yet? */
+                rv = ftp_read_ahead_request(fc);
+
+                if ((rv != APR_SUCCESS) &&
+                    !(APR_STATUS_IS_EINTR(rv) || (APR_STATUS_IS_EAGAIN(rv)) ||
+                      APR_STATUS_IS_EOF(rv))) {
+                    f->c->aborted = 1;
+                    return rv;
+                }
+            }
+
+            if ((rv == APR_SUCCESS) && (pollset[0].rtnevents & APR_POLLOUT)) {
+                break;
+            }
+        } while (APR_STATUS_IS_EINTR(rv) || APR_STATUS_IS_EAGAIN(rv));
+        /* EINTR, above, is obvious, EAGAIN is less so - win32 (perhaps others)
+         * can trigger POLLIN a little too early, before the recieved packet
+         * has actually been disassembled - so loop again.
+         */
+
+        if (c->aborted || f->c->aborted) {
+            return AP_FILTER_ERROR;
+        }
+
+        rv = ap_pass_brigade(f->next, bb);
+        if (rv != APR_SUCCESS) {
+            f->c->aborted = 1;
+            return rv;
+        }
+        bb = next_bb;
+    }
+
+    return APR_SUCCESS;
+}

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

Added: incubator/mod_ftp/trunk/src/ftp_filters.c
URL: http://svn.apache.org/viewcvs/incubator/mod_ftp/trunk/src/ftp_filters.c?rev=306631&view=auto
==============================================================================
--- incubator/mod_ftp/trunk/src/ftp_filters.c (added)
+++ incubator/mod_ftp/trunk/src/ftp_filters.c Thu Oct  6 06:16:28 2005
@@ -0,0 +1,138 @@
+/* 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"
+
+/* ftp_crlf_filter: Filter for sending ASCII files.  RFC 959 states that
+ *                  CRLF should be use where necessary to denote the end
+ *                  of line.  This filter replaces all LF's with CRLF's.
+ *
+ *                  I believe most clients strip the CR's anyway, but we
+ *                  need them to transfer the file properly.
+ *
+ * Arguments: f - The current filter.
+ *            bb - The bucket brigade sent down the filter stack. 
+ *
+ * Returns:  APR_SUCCESS on success, otherwise the status code is returned.
+ */
+
+apr_status_t ftp_crlf_filter(ap_filter_t * f, apr_bucket_brigade * bb)
+{
+    apr_bucket *e;
+    apr_bucket *b;
+    apr_status_t rv;
+    apr_off_t this_size = 0;
+    int *seen_cr = (int *)f->ctx;
+
+    e = APR_BRIGADE_FIRST(bb);
+    while (e != APR_BRIGADE_SENTINEL(bb)) {
+        const char *buf;
+        apr_size_t len;
+        const char *pos;
+
+        if (e->length == 0) {
+            e = APR_BUCKET_NEXT(e);  /* onwards */
+            continue;
+        }
+
+        rv = apr_bucket_read(e, &buf, &len, APR_BLOCK_READ);
+        if (rv != APR_SUCCESS) {
+            return rv;
+        }
+
+        /* Skip all of the extra effort below for empty buckets,
+         * if this is EOS we will catch it at the while condition
+         * looking for the trailing sentinal.
+         */
+        if (len == 0) {
+            e = APR_BUCKET_NEXT(e);  /* onwards */
+            continue;
+        }
+
+        /* Handle case where we have a leading LF */
+        if (!*seen_cr) {
+            if (buf[0] == '\n') {
+                b = apr_bucket_immortal_create("\r", 1, 
+                                               f->c->bucket_alloc);
+                APR_BUCKET_INSERT_BEFORE(e, b);
+                this_size += 1;
+            }
+        }
+        else {
+            *seen_cr = 0;
+        }
+
+        /* We search the data for a LF without a preceeding CR.
+         * If we find one, we split the bucket so that the LF 
+         * is the first character in the new bucket, and then
+         * insert a new bucket with a CR and insert it before 
+         * the LF bucket (ignored on the next loop by seen_cr.)
+         * As long as we keep seeing CRLF pairs, keep looking
+         * forward through the buffer.
+         */
+        pos = buf;
+        while (++pos, pos = memchr(pos, APR_ASCII_LF, len - (pos-buf))) {
+            /* We found a bare linefeed, insert a CR and advance
+             * to the LF-remainder (we skip that LF above) */
+            if (*(pos - 1) != APR_ASCII_CR) {
+                apr_bucket_split(e, pos - buf);
+                e = APR_BUCKET_NEXT(e);  /* second half */
+                b = apr_bucket_immortal_create("\r", 1, 
+                                               f->c->bucket_alloc);
+                APR_BUCKET_INSERT_BEFORE(e, b);
+                this_size += (pos - buf) + 1; /* including the CR */
+                *seen_cr = 1;
+                break;
+            }
+        }
+
+        /* If we just split, we will 'reread' this current bucket... 
+         * But otherwise we note if the trailing character is a CR,
+         * tally this bucket, and advance to the next bucket */
+        if (!pos) {
+            *seen_cr = (buf[len - 1] == '\r');
+            this_size += len;
+            e = APR_BUCKET_NEXT(e);  /* onwards */
+        }
+
+        /* We got too big and chunky, let's spill this out to the
+         * client data connection and resume processing
+         */
+        if (this_size >= 9000) {
+            apr_bucket_brigade *bb_split = bb;
+            bb = apr_brigade_split(bb_split, e);
+            rv = ap_pass_brigade(f->next, bb_split);
+            if (rv != APR_SUCCESS)
+                return rv;
+            this_size = 0;
+        }
+    }
+
+    if (APR_BRIGADE_EMPTY(bb))
+        rv = APR_SUCCESS;
+    else {
+        rv = ap_pass_brigade(f->next, bb);
+        if (rv == APR_SUCCESS && f->c->aborted)
+            rv = AP_FILTER_ERROR;
+    }
+    return rv;
+}

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

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

Added: incubator/mod_ftp/trunk/src/ftp_glob.c
URL: http://svn.apache.org/viewcvs/incubator/mod_ftp/trunk/src/ftp_glob.c?rev=306631&view=auto
==============================================================================
--- incubator/mod_ftp/trunk/src/ftp_glob.c (added)
+++ incubator/mod_ftp/trunk/src/ftp_glob.c Thu Oct  6 06:16:28 2005
@@ -0,0 +1,876 @@
+/*
+ * Original: /repoman/r/ncvs/src/lib/libc/gen/glob.c,v 1.11.6.6 2002/09/18 14:13:31 mikeh Exp $
+ */
+
+/*
+ * Copyright (c) 1989, 1993
+ *	The Regents of the University of California.  All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Guido van Rossum.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by the University of
+ *	California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ *    may be used to endorse or promote products derived from this software
+ *    without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD: /repoman/r/ncvs/src/lib/libc/gen/glob.c,v 1.11.6.6 2002/09/18 14:13:31 mikeh Exp $
+ */
+
+#if defined(LIBC_SCCS) && !defined(lint)
+static char sccsid[] = "@(#)glob.c	8.3 (Berkeley) 10/13/93";
+#endif /* LIBC_SCCS and not lint */
+
+/*
+ * glob(3) -- a superset of the one defined in POSIX 1003.2.
+ *
+ * The [!...] convention to negate a range is supported (SysV, Posix, ksh).
+ *
+ * Optional extra services, controlled by flags not defined by POSIX:
+ *
+ * FTP_GLOB_QUOTE:
+ *	Escaping convention: \ inhibits any special meaning the following
+ *	character might have (except \ at end of string is retained).
+ * FTP_GLOB_MAGCHAR:
+ *	Set in gl_flags if pattern contained a globbing character.
+ * FTP_GLOB_NOMAGIC:
+ *	Same as FTP_GLOB_NOCHECK, but it will only append pattern if it did
+ *	not contain any magic characters.  [Used in csh style globbing]
+ * FTP_GLOB_ALTDIRFUNC:
+ *	Use alternately specified directory access functions.
+ * FTP_GLOB_TILDE:
+ *	expand ~user/foo to the /home/dir/of/user/foo
+ * FTP_GLOB_BRACE:
+ *	expand {1,2}{a,b} to 1a 1b 2a 2b
+ * gl_matchc:
+ *	Number of matches in the current invocation of glob.
+ */
+
+#include "ftp_glob.h"
+
+#include <sys/param.h>
+#include <sys/stat.h>
+
+#include <ctype.h>
+#include <dirent.h>
+#include <errno.h>
+#include <pwd.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#define	DOLLAR		'$'
+#define	DOT		'.'
+#define	EOS		'\0'
+#define	LBRACKET	'['
+#define	NOT		'!'
+#define	QUESTION	'?'
+#define	QUOTE		'\\'
+#define	RANGE		'-'
+#define	RBRACKET	']'
+#define	SEP		'/'
+#define	STAR		'*'
+#define	TILDE		'~'
+#define	UNDERSCORE	'_'
+#define	LBRACE		'{'
+#define	RBRACE		'}'
+#define	SLASH		'/'
+#define	COMMA		','
+
+#ifndef DEBUG
+
+#define	M_QUOTE		0x8000
+#define	M_PROTECT	0x4000
+#define	M_MASK		0xffff
+#define	M_ASCII		0x00ff
+
+typedef u_short Char;
+
+#else
+
+#define	M_QUOTE		0x80
+#define	M_PROTECT	0x40
+#define	M_MASK		0xff
+#define	M_ASCII		0x7f
+
+typedef char Char;
+
+#endif
+
+
+#define	CHAR(c)		((Char)((c)&M_ASCII))
+#define	META(c)		((Char)((c)|M_QUOTE))
+#define	M_ALL		META('*')
+#define	M_END		META(']')
+#define	M_NOT		META('!')
+#define	M_ONE		META('?')
+#define	M_RNG		META('-')
+#define	M_SET		META('[')
+#define	ismeta(c)	(((c)&M_QUOTE) != 0)
+
+#ifndef ARG_MAX
+#define ARG_MAX 4096
+#endif
+
+static int	 compare(const void *, const void *);
+static int	 g_Ctoc(const Char *, char *, u_int);
+static int	 g_lstat(Char *, struct stat *, ftp_glob_t *);
+static DIR	*g_opendir(Char *, ftp_glob_t *);
+static Char	*g_strchr(Char *, int);
+#ifdef notdef
+static Char	*g_strcat(Char *, const Char *);
+#endif
+static int	 g_stat(Char *, struct stat *, ftp_glob_t *);
+static int	 glob0(const Char *, ftp_glob_t *, int *);
+static int	 glob1(Char *, ftp_glob_t *, int *);
+static int	 glob2(Char *, Char *, Char *, Char *, ftp_glob_t *, int *);
+static int	 glob3(Char *, Char *, Char *, Char *, Char *, ftp_glob_t *, int *);
+static int	 globextend(const Char *, ftp_glob_t *, int *);
+static const Char *	
+		 globtilde(const Char *, Char *, size_t, ftp_glob_t *);
+static int	 globexp1(const Char *, ftp_glob_t *, int *);
+static int	 globexp2(const Char *, const Char *, ftp_glob_t *, int *, int *);
+static int	 match(Char *, Char *, Char *);
+
+int
+ftp_glob(pattern, flags, errfunc, pglob)
+	const char *pattern;
+	int flags, (*errfunc)(const char *, int);
+	ftp_glob_t *pglob;
+{
+	const u_char *patnext;
+	int c, limit;
+	Char *bufnext, *bufend, patbuf[MAXPATHLEN];
+
+	patnext = (u_char *) pattern;
+	if (!(flags & FTP_GLOB_APPEND)) {
+		pglob->gl_pathc = 0;
+		pglob->gl_pathv = NULL;
+		if (!(flags & FTP_GLOB_DOOFFS))
+			pglob->gl_offs = 0;
+	}
+	if (flags & FTP_GLOB_LIMIT) {
+		limit = pglob->gl_matchc;
+		if (limit == 0)
+			limit = ARG_MAX;
+	} else
+		limit = 0;
+	pglob->gl_flags = flags & ~FTP_GLOB_MAGCHAR;
+	pglob->gl_errfunc = errfunc;
+	pglob->gl_matchc = 0;
+
+	bufnext = patbuf;
+	bufend = bufnext + MAXPATHLEN - 1;
+	if (flags & FTP_GLOB_NOESCAPE)
+	    while (bufnext < bufend && (c = *patnext++) != EOS)
+		    *bufnext++ = c;
+	else {
+		/* Protect the quoted characters. */
+		while (bufnext < bufend && (c = *patnext++) != EOS)
+			if (c == QUOTE) {
+				if ((c = *patnext++) == EOS) {
+					c = QUOTE;
+					--patnext;
+				}
+				*bufnext++ = c | M_PROTECT;
+			}
+			else
+				*bufnext++ = c;
+	}
+	*bufnext = EOS;
+
+	if (flags & FTP_GLOB_BRACE)
+	    return globexp1(patbuf, pglob, &limit);
+	else
+	    return glob0(patbuf, pglob, &limit);
+}
+
+/*
+ * Expand recursively a glob {} pattern. When there is no more expansion
+ * invoke the standard globbing routine to glob the rest of the magic
+ * characters
+ */
+static int
+globexp1(pattern, pglob, limit)
+	const Char *pattern;
+	ftp_glob_t *pglob;
+	int *limit;
+{
+	const Char* ptr = pattern;
+	int rv;
+
+	/* Protect a single {}, for find(1), like csh */
+	if (pattern[0] == LBRACE && pattern[1] == RBRACE && pattern[2] == EOS)
+		return glob0(pattern, pglob, limit);
+
+	while ((ptr = (const Char *) g_strchr((Char *) ptr, LBRACE)) != NULL)
+		if (!globexp2(ptr, pattern, pglob, &rv, limit))
+			return rv;
+
+	return glob0(pattern, pglob, limit);
+}
+
+
+/*
+ * Recursive brace globbing helper. Tries to expand a single brace.
+ * If it succeeds then it invokes globexp1 with the new pattern.
+ * If it fails then it tries to glob the rest of the pattern and returns.
+ */
+static int
+globexp2(ptr, pattern, pglob, rv, limit)
+	const Char *ptr, *pattern;
+	ftp_glob_t *pglob;
+	int *rv, *limit;
+{
+	int     i;
+	Char   *lm, *ls;
+	const Char *pe, *pm, *pl;
+	Char    patbuf[MAXPATHLEN];
+
+	/* copy part up to the brace */
+	for (lm = patbuf, pm = pattern; pm != ptr; *lm++ = *pm++)
+		continue;
+	*lm = EOS;
+	ls = lm;
+
+	/* Find the balanced brace */
+	for (i = 0, pe = ++ptr; *pe; pe++)
+		if (*pe == LBRACKET) {
+			/* Ignore everything between [] */
+			for (pm = pe++; *pe != RBRACKET && *pe != EOS; pe++)
+				continue;
+			if (*pe == EOS) {
+				/*
+				 * We could not find a matching RBRACKET.
+				 * Ignore and just look for RBRACE
+				 */
+				pe = pm;
+			}
+		}
+		else if (*pe == LBRACE)
+			i++;
+		else if (*pe == RBRACE) {
+			if (i == 0)
+				break;
+			i--;
+		}
+
+	/* Non matching braces; just glob the pattern */
+	if (i != 0 || *pe == EOS) {
+		*rv = glob0(patbuf, pglob, limit);
+		return 0;
+	}
+
+	for (i = 0, pl = pm = ptr; pm <= pe; pm++)
+		switch (*pm) {
+		case LBRACKET:
+			/* Ignore everything between [] */
+			for (pl = pm++; *pm != RBRACKET && *pm != EOS; pm++)
+				continue;
+			if (*pm == EOS) {
+				/*
+				 * We could not find a matching RBRACKET.
+				 * Ignore and just look for RBRACE
+				 */
+				pm = pl;
+			}
+			break;
+
+		case LBRACE:
+			i++;
+			break;
+
+		case RBRACE:
+			if (i) {
+			    i--;
+			    break;
+			}
+			/* FALLTHROUGH */
+		case COMMA:
+			if (i && *pm == COMMA)
+				break;
+			else {
+				/* Append the current string */
+				for (lm = ls; (pl < pm); *lm++ = *pl++)
+					continue;
+				/*
+				 * Append the rest of the pattern after the
+				 * closing brace
+				 */
+				for (pl = pe + 1; (*lm++ = *pl++) != EOS;)
+					continue;
+
+				/* Expand the current pattern */
+				*rv = globexp1(patbuf, pglob, limit);
+
+				/* move after the comma, to the next string */
+				pl = pm + 1;
+			}
+			break;
+
+		default:
+			break;
+		}
+	*rv = 0;
+	return 0;
+}
+
+
+
+/*
+ * expand tilde from the passwd file.
+ */
+static const Char *
+globtilde(pattern, patbuf, patbuf_len, pglob)
+	const Char *pattern;
+	Char *patbuf;
+	size_t patbuf_len;
+	ftp_glob_t *pglob;
+{
+	struct passwd *pwd;
+	char *h;
+	const Char *p;
+	Char *b, *eb;
+
+	if (*pattern != TILDE || !(pglob->gl_flags & FTP_GLOB_TILDE))
+		return pattern;
+
+	/* 
+	 * Copy up to the end of the string or / 
+	 */
+	eb = &patbuf[patbuf_len - 1];
+	for (p = pattern + 1, h = (char *) patbuf;
+	    h < (char *)eb && *p && *p != SLASH; *h++ = *p++)
+		continue;
+
+	*h = EOS;
+
+	if (((char *) patbuf)[0] == EOS) {
+		/*
+		 * handle a plain ~ or ~/ by expanding $HOME first (iff
+		 * we're not running setuid or setgid) and then trying
+		 * the password file
+		 */
+		if ((h = getenv("HOME")) == NULL) {
+			if (((h = getlogin()) != NULL &&
+			     (pwd = getpwnam(h)) != NULL) ||
+			    (pwd = getpwuid(getuid())) != NULL)
+				h = pwd->pw_dir;
+			else
+				return pattern;
+		}
+	}
+	else {
+		/*
+		 * Expand a ~user
+		 */
+		if ((pwd = getpwnam((char*) patbuf)) == NULL)
+			return pattern;
+		else
+			h = pwd->pw_dir;
+	}
+
+	/* Copy the home directory */
+	for (b = patbuf; b < eb && *h; *b++ = *h++)
+		continue;
+
+	/* Append the rest of the pattern */
+	while (b < eb && (*b++ = *p++) != EOS)
+		continue;
+	*b = EOS;
+
+	return patbuf;
+}
+
+
+/*
+ * The main glob() routine: compiles the pattern (optionally processing
+ * quotes), calls glob1() to do the real pattern matching, and finally
+ * sorts the list (unless unsorted operation is requested).  Returns 0
+ * if things went well, nonzero if errors occurred.
+ */
+static int
+glob0(pattern, pglob, limit)
+	const Char *pattern;
+	ftp_glob_t *pglob;
+	int *limit;
+{
+	const Char *qpatnext;
+	int c, err, oldpathc;
+	Char *bufnext, patbuf[MAXPATHLEN];
+
+	qpatnext = globtilde(pattern, patbuf, MAXPATHLEN, pglob);
+	oldpathc = pglob->gl_pathc;
+	bufnext = patbuf;
+
+	/* We don't need to check for buffer overflow any more. */
+	while ((c = *qpatnext++) != EOS) {
+		switch (c) {
+		case LBRACKET:
+			c = *qpatnext;
+			if (c == NOT)
+				++qpatnext;
+			if (*qpatnext == EOS ||
+			    g_strchr((Char *) qpatnext+1, RBRACKET) == NULL) {
+				*bufnext++ = LBRACKET;
+				if (c == NOT)
+					--qpatnext;
+				break;
+			}
+			*bufnext++ = M_SET;
+			if (c == NOT)
+				*bufnext++ = M_NOT;
+			c = *qpatnext++;
+			do {
+				*bufnext++ = CHAR(c);
+				if (*qpatnext == RANGE &&
+				    (c = qpatnext[1]) != RBRACKET) {
+					*bufnext++ = M_RNG;
+					*bufnext++ = CHAR(c);
+					qpatnext += 2;
+				}
+			} while ((c = *qpatnext++) != RBRACKET);
+			pglob->gl_flags |= FTP_GLOB_MAGCHAR;
+			*bufnext++ = M_END;
+			break;
+		case QUESTION:
+			pglob->gl_flags |= FTP_GLOB_MAGCHAR;
+			*bufnext++ = M_ONE;
+			break;
+		case STAR:
+			pglob->gl_flags |= FTP_GLOB_MAGCHAR;
+			/* collapse adjacent stars to one,
+			 * to avoid exponential behavior
+			 */
+			if (bufnext == patbuf || bufnext[-1] != M_ALL)
+			    *bufnext++ = M_ALL;
+			break;
+		default:
+			*bufnext++ = CHAR(c);
+			break;
+		}
+	}
+	*bufnext = EOS;
+
+	if ((err = glob1(patbuf, pglob, limit)) != 0)
+		return(err);
+
+	/*
+	 * If there was no match we are going to append the pattern
+	 * if FTP_GLOB_NOCHECK was specified or if FTP_GLOB_NOMAGIC was specified
+	 * and the pattern did not contain any magic characters
+	 * FTP_GLOB_NOMAGIC is there just for compatibility with csh.
+	 */
+	if (pglob->gl_pathc == oldpathc) {
+		if (((pglob->gl_flags & FTP_GLOB_NOCHECK) ||
+		    ((pglob->gl_flags & FTP_GLOB_NOMAGIC) &&
+			!(pglob->gl_flags & FTP_GLOB_MAGCHAR))))
+			return(globextend(pattern, pglob, limit));
+		else
+			return(FTP_GLOB_NOMATCH);
+	}
+	if (!(pglob->gl_flags & FTP_GLOB_NOSORT))
+		qsort(pglob->gl_pathv + pglob->gl_offs + oldpathc,
+		    pglob->gl_pathc - oldpathc, sizeof(char *), compare);
+	return(0);
+}
+
+static int
+compare(p, q)
+	const void *p, *q;
+{
+	return(strcmp(*(char **)p, *(char **)q));
+}
+
+static int
+glob1(pattern, pglob, limit)
+	Char *pattern;
+	ftp_glob_t *pglob;
+	int *limit;
+{
+	Char pathbuf[MAXPATHLEN];
+
+	/* A null pathname is invalid -- POSIX 1003.1 sect. 2.4. */
+	if (*pattern == EOS)
+		return(0);
+	return(glob2(pathbuf, pathbuf, pathbuf + MAXPATHLEN - 1,
+	    pattern, pglob, limit));
+}
+
+/*
+ * The functions glob2 and glob3 are mutually recursive; there is one level
+ * of recursion for each segment in the pattern that contains one or more
+ * meta characters.
+ */
+static int
+glob2(pathbuf, pathend, pathend_last, pattern, pglob, limit)
+	Char *pathbuf, *pathend, *pathend_last, *pattern;
+	ftp_glob_t *pglob;
+	int *limit;
+{
+	struct stat sb;
+	Char *p, *q;
+	int anymeta;
+
+	/*
+	 * Loop over pattern segments until end of pattern or until
+	 * segment with meta character found.
+	 */
+	for (anymeta = 0;;) {
+		if (*pattern == EOS) {		/* End of pattern? */
+			*pathend = EOS;
+			if (g_lstat(pathbuf, &sb, pglob))
+				return(0);
+
+			if (((pglob->gl_flags & FTP_GLOB_MARK) &&
+			    pathend[-1] != SEP) && (S_ISDIR(sb.st_mode)
+			    || (S_ISLNK(sb.st_mode) &&
+			    (g_stat(pathbuf, &sb, pglob) == 0) &&
+			    S_ISDIR(sb.st_mode)))) {
+				if (pathend + 1 > pathend_last)
+					return (FTP_GLOB_ABORTED);
+				*pathend++ = SEP;
+				*pathend = EOS;
+			}
+			++pglob->gl_matchc;
+			return(globextend(pathbuf, pglob, limit));
+		}
+
+		/* Find end of next segment, copy tentatively to pathend. */
+		q = pathend;
+		p = pattern;
+		while (*p != EOS && *p != SEP) {
+			if (ismeta(*p))
+				anymeta = 1;
+			if (q + 1 > pathend_last)
+				return (FTP_GLOB_ABORTED);
+			*q++ = *p++;
+		}
+
+		if (!anymeta) {		/* No expansion, do next segment. */
+			pathend = q;
+			pattern = p;
+			while (*pattern == SEP) {
+				if (pathend + 1 > pathend_last)
+					return (FTP_GLOB_ABORTED);
+				*pathend++ = *pattern++;
+			}
+		} else			/* Need expansion, recurse. */
+			return(glob3(pathbuf, pathend, pathend_last, pattern, p,
+			    pglob, limit));
+	}
+	/* NOTREACHED */
+}
+
+static int
+glob3(pathbuf, pathend, pathend_last, pattern, restpattern, pglob, limit)
+	Char *pathbuf, *pathend, *pathend_last, *pattern, *restpattern;
+	ftp_glob_t *pglob;
+	int *limit;
+{
+	struct dirent *dp;
+	DIR *dirp;
+	int err;
+	char buf[MAXPATHLEN];
+
+	/*
+	 * The readdirfunc declaration can't be prototyped, because it is
+	 * assigned, below, to two functions which are prototyped in glob.h
+	 * and dirent.h as taking pointers to differently typed opaque
+	 * structures.
+	 */
+	struct dirent *(*readdirfunc)();
+
+	if (pathend > pathend_last)
+		return (FTP_GLOB_ABORTED);
+	*pathend = EOS;
+	errno = 0;
+
+	if ((dirp = g_opendir(pathbuf, pglob)) == NULL) {
+		/* TODO: don't call for ENOENT or ENOTDIR? */
+		if (pglob->gl_errfunc) {
+			if (g_Ctoc(pathbuf, buf, sizeof(buf)))
+				return (FTP_GLOB_ABORTED);
+			if (pglob->gl_errfunc(buf, errno) ||
+			    pglob->gl_flags & FTP_GLOB_ERR)
+				return (FTP_GLOB_ABORTED);
+		}
+		return(0);
+	}
+
+	err = 0;
+
+	/* Search directory for matching names. */
+	if (pglob->gl_flags & FTP_GLOB_ALTDIRFUNC)
+		readdirfunc = pglob->gl_readdir;
+	else
+		readdirfunc = readdir;
+	while ((dp = (*readdirfunc)(dirp))) {
+		u_char *sc;
+		Char *dc;
+
+                if (!(pglob->gl_flags & FTP_GLOB_PERIOD)) {
+                        /* Initial DOT must be matched literally. */
+                        if (dp->d_name[0] == DOT && *pattern != DOT)
+                                continue;
+                }
+
+		dc = pathend;
+		sc = (u_char *) dp->d_name;
+		while (dc < pathend_last && (*dc++ = *sc++) != EOS)
+			;
+		if (!match(pathend, pattern, restpattern)) {
+			*pathend = EOS;
+			continue;
+		}
+		err = glob2(pathbuf, --dc, pathend_last, restpattern,
+		    pglob, limit);
+		if (err)
+			break;
+	}
+
+	if (pglob->gl_flags & FTP_GLOB_ALTDIRFUNC)
+		(*pglob->gl_closedir)(dirp);
+	else
+		closedir(dirp);
+	return(err);
+}
+
+
+/*
+ * Extend the gl_pathv member of a ftp_glob_t structure to accomodate a new item,
+ * add the new item, and update gl_pathc.
+ *
+ * This assumes the BSD realloc, which only copies the block when its size
+ * crosses a power-of-two boundary; for v7 realloc, this would cause quadratic
+ * behavior.
+ *
+ * Return 0 if new item added, error code if memory couldn't be allocated.
+ *
+ * Invariant of the ftp_glob_t structure:
+ *	Either gl_pathc is zero and gl_pathv is NULL; or gl_pathc > 0 and
+ *	gl_pathv points to (gl_offs + gl_pathc + 1) items.
+ */
+static int
+globextend(path, pglob, limit)
+	const Char *path;
+	ftp_glob_t *pglob;
+	int *limit;
+{
+	char **pathv;
+	int i;
+	u_int newsize, len;
+	char *copy;
+	const Char *p;
+
+	if (*limit && pglob->gl_pathc > *limit) {
+		errno = 0;
+		return (FTP_GLOB_NOSPACE);
+	}
+
+	newsize = sizeof(*pathv) * (2 + pglob->gl_pathc + pglob->gl_offs);
+	pathv = pglob->gl_pathv ?
+		    realloc((char *)pglob->gl_pathv, newsize) :
+		    malloc(newsize);
+	if (pathv == NULL) {
+		if (pglob->gl_pathv) {
+			free(pglob->gl_pathv);
+			pglob->gl_pathv = NULL;
+		}
+		return(FTP_GLOB_NOSPACE);
+	}
+
+	if (pglob->gl_pathv == NULL && pglob->gl_offs > 0) {
+		/* first time around -- clear initial gl_offs items */
+		pathv += pglob->gl_offs;
+		for (i = pglob->gl_offs; --i >= 0; )
+			*--pathv = NULL;
+	}
+	pglob->gl_pathv = pathv;
+
+	for (p = path; *p++;)
+		continue;
+	len = (size_t)(p - path);
+	if ((copy = malloc(len)) != NULL) {
+		if (g_Ctoc(path, copy, len)) {
+			free(copy);
+			return (FTP_GLOB_NOSPACE);
+		}
+		pathv[pglob->gl_offs + pglob->gl_pathc++] = copy;
+	}
+	pathv[pglob->gl_offs + pglob->gl_pathc] = NULL;
+	return(copy == NULL ? FTP_GLOB_NOSPACE : 0);
+}
+
+/*
+ * pattern matching function for filenames.  Each occurrence of the *
+ * pattern causes a recursion level.
+ */
+static int
+match(name, pat, patend)
+	Char *name, *pat, *patend;
+{
+	int ok, negate_range;
+	Char c, k;
+
+	while (pat < patend) {
+		c = *pat++;
+		switch (c & M_MASK) {
+		case M_ALL:
+			if (pat == patend)
+				return(1);
+			do
+			    if (match(name, pat, patend))
+				    return(1);
+			while (*name++ != EOS);
+			return(0);
+		case M_ONE:
+			if (*name++ == EOS)
+				return(0);
+			break;
+		case M_SET:
+			ok = 0;
+			if ((k = *name++) == EOS)
+				return(0);
+			if ((negate_range = ((*pat & M_MASK) == M_NOT)) != EOS)
+				++pat;
+			while (((c = *pat++) & M_MASK) != M_END)
+				if ((*pat & M_MASK) == M_RNG) {
+						ok = 1;
+					pat += 2;
+				} else if (c == k)
+					ok = 1;
+			if (ok == negate_range)
+				return(0);
+			break;
+		default:
+			if (*name++ != c)
+				return(0);
+			break;
+		}
+	}
+	return(*name == EOS);
+}
+
+/* Free allocated data belonging to a ftp_glob_t structure. */
+void
+ftp_globfree(pglob)
+	ftp_glob_t *pglob;
+{
+	int i;
+	char **pp;
+
+	if (pglob->gl_pathv != NULL) {
+		pp = pglob->gl_pathv + pglob->gl_offs;
+		for (i = pglob->gl_pathc; i--; ++pp)
+			if (*pp)
+				free(*pp);
+		free(pglob->gl_pathv);
+		pglob->gl_pathv = NULL;
+	}
+}
+
+static DIR *
+g_opendir(str, pglob)
+	Char *str;
+	ftp_glob_t *pglob;
+{
+	char buf[MAXPATHLEN];
+
+	if (!*str)
+		strcpy(buf, ".");
+	else {
+		if (g_Ctoc(str, buf, sizeof(buf)))
+			return (NULL);
+	}
+
+	if (pglob->gl_flags & FTP_GLOB_ALTDIRFUNC)
+		return((*pglob->gl_opendir)(buf));
+
+	return(opendir(buf));
+}
+
+static int
+g_lstat(fn, sb, pglob)
+	Char *fn;
+	struct stat *sb;
+	ftp_glob_t *pglob;
+{
+	char buf[MAXPATHLEN];
+
+	if (g_Ctoc(fn, buf, sizeof(buf))) {
+		errno = ENAMETOOLONG;
+		return (-1);
+	}
+	if (pglob->gl_flags & FTP_GLOB_ALTDIRFUNC)
+		return((*pglob->gl_lstat)(buf, sb));
+	return(lstat(buf, sb));
+}
+
+static int
+g_stat(fn, sb, pglob)
+	Char *fn;
+	struct stat *sb;
+	ftp_glob_t *pglob;
+{
+	char buf[MAXPATHLEN];
+
+	if (g_Ctoc(fn, buf, sizeof(buf))) {
+		errno = ENAMETOOLONG;
+		return (-1);
+	}
+	if (pglob->gl_flags & FTP_GLOB_ALTDIRFUNC)
+		return((*pglob->gl_stat)(buf, sb));
+	return(stat(buf, sb));
+}
+
+static Char *
+g_strchr(str, ch)
+	Char *str;
+	int ch;
+{
+	do {
+		if (*str == ch)
+			return (str);
+	} while (*str++);
+	return (NULL);
+}
+
+static int
+g_Ctoc(str, buf, len)
+	const Char *str;
+	char *buf;
+	u_int len;
+{
+
+	while (len--) {
+		if ((*buf++ = *str++) == '\0')
+			return (0);
+	}
+	return (1);
+}
+

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

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

Added: incubator/mod_ftp/trunk/src/ftp_inet_pton.c
URL: http://svn.apache.org/viewcvs/incubator/mod_ftp/trunk/src/ftp_inet_pton.c?rev=306631&view=auto
==============================================================================
--- incubator/mod_ftp/trunk/src/ftp_inet_pton.c (added)
+++ incubator/mod_ftp/trunk/src/ftp_inet_pton.c Thu Oct  6 06:16:28 2005
@@ -0,0 +1,240 @@
+/* Copyright (c) 1996 by Internet Software Consortium.
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM DISCLAIMS
+ * ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL INTERNET SOFTWARE
+ * CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
+ * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR
+ * PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
+ * ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
+ * SOFTWARE.
+ */
+
+#include "apr_network_io.h"
+#include "mod_ftp.h"
+
+#if APR_HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if APR_HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#if APR_HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#if APR_HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#include <string.h>
+#if APR_HAVE_ERRNO_H
+#include <errno.h>
+#endif
+
+#ifndef IN6ADDRSZ
+#define IN6ADDRSZ   16
+#endif
+
+#ifndef INT16SZ
+#define INT16SZ sizeof(apr_int16_t)
+#endif
+
+#ifndef INADDRSZ
+#define INADDRSZ    4
+#endif
+
+#ifndef __P
+#define __P(x) x
+#endif
+
+#if !defined(EAFNOSUPPORT) && defined(WSAEAFNOSUPPORT)
+#define EAFNOSUPPORT WSAEAFNOSUPPORT
+#endif
+
+/*
+ * WARNING: Don't even consider trying to compile this on a system where
+ * sizeof(int) < 4.  sizeof(int) > 4 is fine; all the world's not a VAX.
+ */
+
+static int	inet_pton4 __P((const char *src, unsigned char *dst));
+#if APR_HAVE_IPV6
+static int	inet_pton6 __P((const char *src, unsigned char *dst));
+#endif
+
+/* int
+ * inet_pton(af, src, dst)
+ *	convert from presentation format (which usually means ASCII printable)
+ *	to network format (which is usually some kind of binary format).
+ * return:
+ *	1 if the address was valid for the specified address family
+ *	0 if the address wasn't valid (`dst' is untouched in this case)
+ *	-1 if some other error occurred (`dst' is untouched in this case, too)
+ * author:
+ *	Paul Vixie, 1996.
+ */
+int
+ftp_inet_pton(int af, const char *src, void *dst)
+{
+	switch (af) {
+	case AF_INET:
+		return (inet_pton4(src, dst));
+#if APR_HAVE_IPV6
+	case AF_INET6:
+		return (inet_pton6(src, dst));
+#endif
+	default:
+		errno = EAFNOSUPPORT;
+		return (-1);
+	}
+	/* NOTREACHED */
+}
+
+/* int
+ * inet_pton4(src, dst)
+ *	like inet_aton() but without all the hexadecimal and shorthand.
+ * return:
+ *	1 if `src' is a valid dotted quad, else 0.
+ * notice:
+ *	does not touch `dst' unless it's returning 1.
+ * author:
+ *	Paul Vixie, 1996.
+ */
+static int
+inet_pton4(const char *src, unsigned char *dst)
+{
+	static const char digits[] = "0123456789";
+	int saw_digit, octets, ch;
+	unsigned char tmp[INADDRSZ], *tp;
+
+	saw_digit = 0;
+	octets = 0;
+	*(tp = tmp) = 0;
+	while ((ch = *src++) != '\0') {
+		const char *pch;
+
+		if ((pch = ap_strchr_c(digits, ch)) != NULL) {
+			unsigned int new = *tp * 10 + (pch - digits);
+
+			if (new > 255)
+				return (0);
+			*tp = new;
+			if (! saw_digit) {
+				if (++octets > 4)
+					return (0);
+				saw_digit = 1;
+			}
+		} else if (ch == '.' && saw_digit) {
+			if (octets == 4)
+				return (0);
+			*++tp = 0;
+			saw_digit = 0;
+		} else
+			return (0);
+	}
+	if (octets < 4)
+		return (0);
+
+	memcpy(dst, tmp, INADDRSZ);
+	return (1);
+}
+
+#if APR_HAVE_IPV6
+/* int
+ * inet_pton6(src, dst)
+ *	convert presentation level address to network order binary form.
+ * return:
+ *	1 if `src' is a valid [RFC1884 2.2] address, else 0.
+ * notice:
+ *	(1) does not touch `dst' unless it's returning 1.
+ *	(2) :: in a full address is silently ignored.
+ * credit:
+ *	inspired by Mark Andrews.
+ * author:
+ *	Paul Vixie, 1996.
+ */
+static int
+inet_pton6(const char *src, unsigned char *dst)
+{
+	static const char xdigits_l[] = "0123456789abcdef",
+			  xdigits_u[] = "0123456789ABCDEF";
+	unsigned char tmp[IN6ADDRSZ], *tp, *endp, *colonp;
+	const char *xdigits, *curtok;
+	int ch, saw_xdigit;
+	unsigned int val;
+
+	memset((tp = tmp), '\0', IN6ADDRSZ);
+	endp = tp + IN6ADDRSZ;
+	colonp = NULL;
+	/* Leading :: requires some special handling. */
+	if (*src == ':')
+		if (*++src != ':')
+			return (0);
+	curtok = src;
+	saw_xdigit = 0;
+	val = 0;
+	while ((ch = *src++) != '\0') {
+		const char *pch;
+
+		if ((pch = ap_strchr_c((xdigits = xdigits_l), ch)) == NULL)
+			pch = ap_strchr_c((xdigits = xdigits_u), ch);
+		if (pch != NULL) {
+			val <<= 4;
+			val |= (pch - xdigits);
+			if (val > 0xffff)
+				return (0);
+			saw_xdigit = 1;
+			continue;
+		}
+		if (ch == ':') {
+			curtok = src;
+			if (!saw_xdigit) {
+				if (colonp)
+					return (0);
+				colonp = tp;
+				continue;
+			}
+			if (tp + INT16SZ > endp)
+				return (0);
+			*tp++ = (unsigned char) (val >> 8) & 0xff;
+			*tp++ = (unsigned char) val & 0xff;
+			saw_xdigit = 0;
+			val = 0;
+			continue;
+		}
+		if (ch == '.' && ((tp + INADDRSZ) <= endp) &&
+		    inet_pton4(curtok, tp) > 0) {
+			tp += INADDRSZ;
+			saw_xdigit = 0;
+			break;	/* '\0' was seen by inet_pton4(). */
+		}
+		return (0);
+	}
+	if (saw_xdigit) {
+		if (tp + INT16SZ > endp)
+			return (0);
+		*tp++ = (unsigned char) (val >> 8) & 0xff;
+		*tp++ = (unsigned char) val & 0xff;
+	}
+	if (colonp != NULL) {
+		/*
+		 * Since some memmove()'s erroneously fail to handle
+		 * overlapping regions, we'll do the shift by hand.
+		 */
+		const int n = tp - colonp;
+		int i;
+
+		for (i = 1; i <= n; i++) {
+			endp[- i] = colonp[n - i];
+			colonp[n - i] = 0;
+		}
+		tp = endp;
+	}
+	if (tp != endp)
+		return (0);
+	memcpy(dst, tmp, IN6ADDRSZ);
+	return (1);
+}
+#endif

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

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

Added: incubator/mod_ftp/trunk/src/ftp_limitlogin.c
URL: http://svn.apache.org/viewcvs/incubator/mod_ftp/trunk/src/ftp_limitlogin.c?rev=306631&view=auto
==============================================================================
--- incubator/mod_ftp/trunk/src/ftp_limitlogin.c (added)
+++ incubator/mod_ftp/trunk/src/ftp_limitlogin.c Thu Oct  6 06:16:28 2005
@@ -0,0 +1,331 @@
+/* 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 FTP_NO_GLOB
+#include "ftp_glob.h" /* Custom globber */
+#endif /* FTP_NO_GLOB */
+
+#include "ap_mpm.h" /* For MPM query interface */
+#include "apr_dbm.h"
+#ifndef WIN32
+#include "unixd.h"
+#endif
+
+#define FTP_SERVER_LIMIT_KEY "FireballXL5OnDVD"
+#define FTP_DB_FILE_MODE ( APR_UREAD | APR_UWRITE | APR_GREAD | APR_WREAD )
+
+/*
+ * We also use the below as a state variable. Ugly.
+ */
+static apr_global_mutex_t   *ftp_lock = NULL;
+
+static apr_status_t ftp_mutex_init(server_rec *s, apr_pool_t *p)
+{
+    ftp_server_config *fsc = ftp_get_module_config(s->module_config);
+
+    if (fsc->limit_perip || fsc->limit_peruser || fsc->limit_perserver)
+        return apr_global_mutex_create(&ftp_lock,
+                                   apr_pstrcat(p, fsc->limitdbfile, ".LoCK", NULL),
+                                   APR_LOCK_DEFAULT, p);
+     else
+         return APR_SUCCESS;
+}
+
+static apr_status_t ftp_mutex_on(void)
+{
+    return apr_global_mutex_lock(ftp_lock);
+}
+
+static apr_status_t ftp_mutex_off(void)
+{
+    return apr_global_mutex_unlock(ftp_lock);
+}
+
+static apr_status_t ftp_db_init(server_rec *s, apr_pool_t *p)
+{
+    apr_status_t rv;
+    apr_dbm_t *dbf;
+    ftp_server_config *fsc = ftp_get_module_config(s->module_config);
+
+    /*
+     * Noop in cases where we have not
+     * setup or enabled the actual mutex. We know
+     * that this is (still) NULL if ftp_mutex_init() determined
+     * it didn't need to bother with login limits.
+     */
+    if (!ftp_lock)
+        return APR_SUCCESS;
+
+    ftp_mutex_on();
+    if ((rv = apr_dbm_open(&dbf, fsc->limitdbfile,
+                           APR_DBM_RWCREATE, FTP_DB_FILE_MODE, p)) != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, s,
+                     "Cannot create FTPLimitDBFile file `%s'",
+                     fsc->limitdbfile);
+        ftp_mutex_off();
+        return rv;
+    }
+    apr_dbm_close(dbf);
+#if !defined(OS2) && !defined(WIN32) && !defined(BEOS) && !defined(NETWARE)
+    if (geteuid() == 0) {
+        chown(fsc->limitdbfile, unixd_config.user_id, -1);
+        chown(apr_pstrcat(p, fsc->limitdbfile, ".LoCK", NULL), unixd_config.user_id, -1);
+        chown(apr_pstrcat(p, fsc->limitdbfile, ".db", NULL), unixd_config.user_id, -1);
+        chown(apr_pstrcat(p, fsc->limitdbfile, ".dir", NULL), unixd_config.user_id, -1);
+        chown(apr_pstrcat(p, fsc->limitdbfile, ".pag", NULL), unixd_config.user_id, -1);
+    }
+#endif
+    ftp_mutex_off();
+
+    return APR_SUCCESS;
+}
+
+apr_status_t ftp_mutexdb_init(server_rec *s, apr_pool_t *p)
+{
+    apr_status_t rv;
+    if ((rv = ftp_mutex_init(s, p)) != APR_SUCCESS)
+        return rv;
+    return ftp_db_init(s, p);
+}
+
+apr_status_t ftp_mutexdb_child_init(server_rec *s, apr_pool_t *p)
+{
+    ftp_server_config *fsc = ftp_get_module_config(s->module_config);
+
+    if (!ftp_lock)
+        return APR_SUCCESS;
+    else
+        return apr_global_mutex_child_init(&ftp_lock, fsc->limitdbfile, p);
+}
+
+apr_status_t ftp_mutexdb_cleanup (void *data)
+{
+    server_rec *s = data;
+    ftp_server_config *fsc = ftp_get_module_config(s->module_config);
+    apr_pool_t *p;
+
+    if (ftp_lock) {
+        apr_global_mutex_destroy(ftp_lock);
+        apr_pool_sub_make(&p, s->process->pool, NULL);
+        if (p) {
+            unlink(apr_pstrcat(p, fsc->limitdbfile, ".db", NULL));
+            unlink(apr_pstrcat(p, fsc->limitdbfile, ".dir", NULL));
+            unlink(apr_pstrcat(p, fsc->limitdbfile, ".pag", NULL));
+            unlink(apr_pstrcat(p, fsc->limitdbfile, ".LoCK", NULL));
+            unlink(fsc->limitdbfile);
+            apr_pool_destroy(p);
+        }
+        ftp_lock = NULL;
+    }
+
+    return APR_SUCCESS;
+}
+
+#define MYMIN(a,b) ( (a) < (b) ? (a) : (b) )
+
+ftp_loginlimit_t ftp_limitlogin_check(const char *user, request_rec *r)
+{
+    apr_status_t rv;
+    conn_rec *c = r->connection;
+    apr_datum_t ukey;
+    apr_datum_t ikey;
+    apr_datum_t skey;
+    apr_datum_t val;
+    apr_dbm_t *dbf;
+    char temp[10];   /* Note: This means only values <= 999,999,999 */
+    int uval = 0;
+    int ival = 0;
+    int sval = 0;
+    ftp_server_config *fsc =
+        ftp_get_module_config(r->connection->base_server->module_config);
+
+    if (!ftp_lock)
+        return FTP_LIMIT_OK;
+
+    ftp_mutex_on();
+    if ((rv = apr_dbm_open(&dbf, fsc->limitdbfile,
+                           APR_DBM_RWCREATE, FTP_DB_FILE_MODE, r->pool)) != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, c->base_server,
+                     "Cannot open FTPLimitDBFile file `%s' for login check",
+                     fsc->limitdbfile);
+        ftp_mutex_off();
+        return FTP_LIMIT_ERROR;
+    }
+    /*
+     * First we check the user settings.
+     * This is a safe cast, this is a lookup key.
+     */
+    ukey.dptr = (char *)user;
+    ukey.dsize = strlen(user);
+    rv = apr_dbm_fetch(dbf, ukey, &val);  /* error for non-existant? */
+    if (val.dptr != NULL && val.dsize > 0) {
+        apr_cpystrn(temp, val.dptr, MYMIN(sizeof(temp), val.dsize+1));
+        uval = atoi(temp);
+    }
+    if (fsc->limit_peruser && uval >= fsc->limit_peruser) {
+        ftp_mutex_off();
+        return FTP_LIMIT_HIT_PERUSER;
+    }
+    /*
+     * Now we check the IP settings.
+     * This is a safe cast, this is a lookup key.
+     */
+    ikey.dptr = (char *)c->remote_ip;
+    ikey.dsize = strlen(c->remote_ip);
+    rv = apr_dbm_fetch(dbf, ikey, &val);  /* error for non-existant? */
+    if (val.dptr != NULL && val.dsize > 0) {
+        apr_cpystrn(temp, val.dptr, MYMIN(sizeof(temp), val.dsize+1));
+        ival = atoi(temp);
+    }
+    if (fsc->limit_perip && ival >= fsc->limit_perip) {
+        ftp_mutex_off();
+        return FTP_LIMIT_HIT_PERIP;
+    }
+    /*
+     * OK, so we're not up against the per user or IP limit,
+     * we need to check the perserver limit then
+     */
+    skey.dptr = FTP_SERVER_LIMIT_KEY;
+    skey.dsize = strlen(FTP_SERVER_LIMIT_KEY);
+    rv = apr_dbm_fetch(dbf, skey, &val);  /* error for non-existant? */
+    if (val.dptr != NULL && val.dsize > 0) {
+        apr_cpystrn(temp, val.dptr, MYMIN(sizeof(temp), val.dsize+1));
+        sval = atoi(temp);
+    }
+    if (fsc->limit_perserver && sval >= fsc->limit_perserver) {
+        ftp_mutex_off();
+        return FTP_LIMIT_HIT_PERSERVER;
+    }
+
+    /*
+     * Oh joy. Oh rapture. We have room for this person. We
+     * now go ahead and update these values in the DB "atomically".
+     * If not (that is, if we check and then, if OK, we *then*
+     * update), we hit a race condition.
+     */
+    sval++; uval++; ival++;
+    apr_snprintf(temp, sizeof(temp), "%d", uval);
+    val.dptr = temp;
+    val.dsize = strlen(temp);
+    rv = apr_dbm_store(dbf, ukey, val);
+
+    apr_snprintf(temp, sizeof(temp), "%d", ival);
+    val.dptr = temp;
+    val.dsize = strlen(temp);
+    rv = apr_dbm_store(dbf, ikey, val);
+
+    apr_snprintf(temp, sizeof(temp), "%d", sval);
+    val.dptr = temp;
+    val.dsize = strlen(temp);
+    rv = apr_dbm_store(dbf, skey, val);
+    
+    apr_dbm_close(dbf);
+
+    ftp_mutex_off();
+
+    return FTP_LIMIT_OK;
+}
+
+int ftp_limitlogin_loggedout(const char *user, conn_rec *c)
+{
+    apr_status_t rv;
+    apr_datum_t ukey;
+    apr_datum_t ikey;
+    apr_datum_t skey;
+    apr_datum_t val;
+    apr_dbm_t *dbf;
+    char temp[10];   /* Note: This means only values <= 999,999,999 */
+    int uval = 0;
+    int ival = 0;
+    int sval = 0;
+    ftp_server_config *fsc =
+        ftp_get_module_config(c->base_server->module_config);
+
+    if (!ftp_lock)
+        return 0;
+    
+    ftp_mutex_on();
+    if ((rv = apr_dbm_open(&dbf, fsc->limitdbfile,
+                           APR_DBM_RWCREATE, FTP_DB_FILE_MODE, c->pool)) != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, rv, c->base_server,
+                     "Cannot open FTPLimitDBFile file `%s' for logged out update",
+                     fsc->limitdbfile);
+        ftp_mutex_off();
+        return rv;
+    }
+    /* This is a safe cast, it's a lookup key
+     */
+    ukey.dptr = (char *)user;
+    ukey.dsize = strlen(user);
+    rv = apr_dbm_fetch(dbf, ukey, &val);  /* error for non-existant? */
+    if (val.dptr != NULL && val.dsize > 0) {
+        apr_cpystrn(temp, val.dptr, MYMIN(sizeof(temp), val.dsize+1));
+        uval = atoi(temp);
+    }
+
+    ikey.dptr = (char *)c->remote_ip;
+    ikey.dsize = strlen(c->remote_ip);
+    rv = apr_dbm_fetch(dbf, ikey, &val);  /* error for non-existant? */
+    if (val.dptr != NULL && val.dsize > 0) {
+        apr_cpystrn(temp, val.dptr, MYMIN(sizeof(temp), val.dsize+1));
+        ival = atoi(temp);
+    }
+    
+    skey.dptr = FTP_SERVER_LIMIT_KEY;
+    skey.dsize = strlen(FTP_SERVER_LIMIT_KEY);
+    rv = apr_dbm_fetch(dbf, skey, &val);  /* error for non-existant? */
+    if (val.dptr != NULL && val.dsize > 0) {
+        apr_cpystrn(temp, val.dptr, MYMIN(sizeof(temp), val.dsize+1));
+        sval = atoi(temp);
+    }
+    sval--; uval--; ival--;
+    if (sval < 0)
+        sval = 0;
+    if (uval < 0)
+        uval = 0;
+    if (ival < 0)
+        ival = 0;
+
+    apr_snprintf(temp, sizeof(temp), "%d", uval);
+    val.dptr = temp;
+    val.dsize = strlen(temp);
+    rv = apr_dbm_store(dbf, ukey, val);
+
+    apr_snprintf(temp, sizeof(temp), "%d", ival);
+    val.dptr = temp;
+    val.dsize = strlen(temp);
+    rv = apr_dbm_store(dbf, ikey, val);
+    
+    apr_snprintf(temp, sizeof(temp), "%d", sval);
+    val.dptr = temp;
+    val.dsize = strlen(temp);
+    rv = apr_dbm_store(dbf, skey, val);
+
+    apr_dbm_close(dbf);
+
+    ftp_mutex_off();
+
+    return 0;
+}
+

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

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

Added: incubator/mod_ftp/trunk/src/ftp_log.c
URL: http://svn.apache.org/viewcvs/incubator/mod_ftp/trunk/src/ftp_log.c?rev=306631&view=auto
==============================================================================
--- incubator/mod_ftp/trunk/src/ftp_log.c (added)
+++ incubator/mod_ftp/trunk/src/ftp_log.c Thu Oct  6 06:16:28 2005
@@ -0,0 +1,111 @@
+/* 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"
+
+/*
+ * callbacks for mod_log_config.
+ *
+ * These callbacks extend mod_log_config by adding additional
+ * % directives as follows:
+ *
+ * %..M  The mode that was used to transfer the file.
+ *       A single character is printed, a (ascii) or b (binary)
+ * %..F  Any action that was taken on the file (concationated as needed)
+ *         C - file was compressed.
+ *         U - file was uncompressed.
+ *         T - file was tarred.
+ *         _ - no action taken.
+ * %..d  Direction the file was sent.
+ *         o - outgoing
+ *         i - incoming
+ * %..W  How the file was accessed.
+ *         r - real
+ *         a - anonymous
+ *         g - guest
+ * %..S    Service name, usually 'ftp'
+ * %..Z    Authentication method
+ *         0 - no auth
+ *         1 - rfc931 auth
+ * %..Y    Authenticated user id
+ *         * - if not available
+ */
+
+const char *ftp_log_transfer_mode(request_rec *r, char *a)
+{
+    ftp_connection *fc = ftp_get_module_config(r->request_config);
+    if (fc->type == TYPE_A) {
+        return "a";
+    }
+    else if (fc->type == TYPE_I) {
+        return "b";
+    }
+    else {
+        return "-";
+    }
+}
+
+const char *ftp_log_action_flags(request_rec *r, char *a)
+{
+    return "_";
+}
+
+const char *ftp_log_transfer_direction(request_rec *r, char *a)
+{
+    if (strcmp(r->method, "RETR") == 0) {
+        return "o";
+    }
+    else if (strcmp(r->method, "STOR") == 0 || strcmp(r->method, "APPE") == 0) {
+        return "i";
+    }
+    else {
+        return "-";
+    }
+}
+
+const char *ftp_log_accessed_anonymously(request_rec *r, char *a)
+{
+    if (strcasecmp(r->user, "anonymous") == 0) {
+        return "a";
+    }
+    else if (strcasecmp(r->user, "guest") == 0) {
+        return "g";
+    }
+    else {
+        return "r";
+    }
+}
+
+const char *ftp_log_service_name(request_rec *r, char *a)
+{
+    return apr_pstrdup(r->pool, "ftp");
+}
+
+const char *ftp_log_auth_method(request_rec *r, char *a)
+{
+    return "0";
+}
+
+const char *ftp_log_auth_user_id(request_rec *r, char *a)
+{
+    return "*";
+}

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

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

Added: incubator/mod_ftp/trunk/src/ftp_message.c
URL: http://svn.apache.org/viewcvs/incubator/mod_ftp/trunk/src/ftp_message.c?rev=306631&view=auto
==============================================================================
--- incubator/mod_ftp/trunk/src/ftp_message.c (added)
+++ incubator/mod_ftp/trunk/src/ftp_message.c Thu Oct  6 06:16:28 2005
@@ -0,0 +1,119 @@
+/* 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"
+
+/* ftp_message_generate: Function to transform meta-characters
+ *
+ * Arguments: fc - The ftp connection
+ *            inbuf - The input buffer
+ *            outbuf - The output buffer
+ *            outlen - The length of the output buffer
+ *
+ * Returns: nadda
+ */
+void ftp_message_generate(ftp_connection *fc, const char *inbuf, 
+                          char *outbuf, size_t outlen)
+{
+    conn_rec *c = fc->connection;
+    const char *inptr = inbuf;
+    char *outptr = outbuf;
+    char time_str[APR_CTIME_LEN];
+    char *remote_host, *local_host;
+
+    while ((outlen > 1) && (*inptr != '\0')) {
+        if (*inptr != '%') {
+            *outptr++ = *inptr;
+            outlen -= 1;
+        }
+        else {
+            switch(*++inptr) {
+              case 'T':
+                apr_ctime(time_str, apr_time_now());
+                strncpy(outptr, time_str, outlen);
+                if (outlen > APR_CTIME_LEN - 1) {
+                    *(outptr + APR_CTIME_LEN - 1) = '\0';
+                }
+                break;
+              case 'C':
+                apr_snprintf(outptr, outlen, "%s", fc->cwd);
+                break;
+              case 'h':
+                apr_getnameinfo(&remote_host, c->remote_addr, 0);
+                apr_snprintf(outptr, outlen, "%s", remote_host);
+                break;
+              case 'L':
+                apr_getnameinfo(&local_host, c->local_addr, 0);
+                apr_snprintf(outptr, outlen, "%s", local_host);
+                break;
+              case 'E':
+                apr_snprintf(outptr, outlen, "%s", 
+                             c->base_server->server_admin);
+                break;
+              case 'a':
+                apr_snprintf(outptr, outlen, "%s", c->local_ip);
+                break;
+              case 'A':
+                apr_snprintf(outptr, outlen, "%s", c->remote_ip);
+                break;
+              case 'u':
+                apr_snprintf(outptr, outlen, "%s", fc->user);
+                break;
+              case 'f':
+                apr_snprintf(outptr, outlen, "%d", fc->files);
+                break;
+              case 't':
+                apr_snprintf(outptr, outlen, "%" APR_OFF_T_FMT, fc->bytes);
+                break;
+              case 'b':
+                apr_snprintf(outptr, outlen, "%" APR_OFF_T_FMT, fc->traffic);
+                break;
+              case 'x':
+                apr_snprintf(outptr, outlen, "%d", fc->transfers);
+                break;
+              case '%':
+                *outptr++ = '%';
+                outlen -= 1;
+                *outptr = '\0';
+                break;
+              default:
+                *outptr++ = '%';
+                outlen -= 1;
+                if (outlen > 1) {
+                    *outptr++ = *inptr;
+                    outlen -= 1;
+                }
+                *outptr = '\0';
+                break;
+            }
+            outptr[outlen - 1] = '\0';
+            while (*outptr) {
+                outptr++;
+                outlen -= 1;
+            }
+        }
+        inptr++;
+    }
+    if (outlen > 0) {
+        *outptr = '\0';
+    }
+}

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