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