You are viewing a plain text version of this content. The canonical link for it is here.
Posted to mod_ftp-commits@incubator.apache.org by ji...@apache.org on 2005/10/06 13:17:04 UTC
svn commit: r306631 [6/8] - in /incubator/mod_ftp/trunk: ./ conf/ docs/
include/ modules/ patches/ src/ tests/ tests/conf/ tests/logs/ tests/tests/
utils/ utils/ftp_proxy/ utils/static_build/ utils/stresstest/
Added: incubator/mod_ftp/trunk/src/ftp_protocol.c
URL: http://svn.apache.org/viewcvs/incubator/mod_ftp/trunk/src/ftp_protocol.c?rev=306631&view=auto
==============================================================================
--- incubator/mod_ftp/trunk/src/ftp_protocol.c (added)
+++ incubator/mod_ftp/trunk/src/ftp_protocol.c Thu Oct 6 06:16:28 2005
@@ -0,0 +1,864 @@
+/* Copyright 1999-2005 The Apache Software Foundation or its licensors, as
+ * applicable.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Original Copyright (c) Covalent Technologies 2001-2005
+ *
+ * FTP Protocol module for Apache 2.0
+ */
+
+#include "mod_ftp.h"
+#include "ftp_config.h"
+
+/* for socket_atmark - move to apr with apr_socket_atmark */
+#ifdef HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#ifdef HAVE_SYS_SOCKIO_H
+#include <sys/sockio.h>
+#endif
+#ifdef HAVE_SYS_IOCTL_H
+#include <sys/ioctl.h>
+#endif
+
+/* Min # of bytes to allocate when reading a request line */
+#define MIN_LINE_ALLOC 512
+
+
+/*
+ * Moving to apr shortly, this function returns APR_SUCCESS and
+ * sets atmark to true if at the mark, or false when not.
+ */
+apr_status_t socket_atmark(apr_socket_t *sock, int *atmark)
+{
+ apr_os_sock_t sd;
+ apr_status_t rv;
+#ifdef WIN32
+ u_long oobmark;
+#else
+ int oobmark;
+#endif
+
+ rv = apr_os_sock_get(&sd, sock);
+
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+
+#ifndef SIOCATMARK
+#error FAILED TO COMPILE, SIOCATMARK must be supported!
+#else
+ /* if (apr_socket_atmark(sd->socketdes)) */
+#ifdef WIN32
+ if (ioctlsocket(sd, SIOCATMARK, (void*) &oobmark) < 0)
+#else
+ if (ioctl(sd, SIOCATMARK, (void*) &oobmark) < 0)
+#endif
+ return apr_get_netos_error();
+
+ *atmark = (oobmark != 0);
+#endif
+
+ return APR_SUCCESS;
+}
+
+/*
+ * ftp_read_line reads ahead one line from the control channel.
+ *
+ */
+static apr_status_t ftp_read_line(char **result, apr_size_t *bytes_read,
+ apr_pool_t *pool,
+ apr_bucket_brigade *bb,
+ ap_filter_t *input_filters,
+ int block, ftp_connection *fc)
+{
+ char *last_char = NULL;
+ apr_status_t rv;
+ apr_bucket_pool *pb;
+ apr_bucket *pe;
+ apr_bucket *e;
+ char *pbuf;
+ char *pos;
+
+ /* We manage a leading (intially empty) pool bucket that
+ * we will use to concatinate the line over (possibly)
+ * multiple non-blocking invocations of ftp_read_line()
+ */
+
+ if (APR_BRIGADE_EMPTY(bb)) {
+ pe = apr_bucket_pool_create(apr_palloc(pool, MIN_LINE_ALLOC), 0,
+ pool, input_filters->c->bucket_alloc);
+ pb = pe->data;
+ pb->heap.alloc_len = MIN_LINE_ALLOC;
+ pbuf = (char*)pb->base;
+#ifdef FTP_TRACE
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0,
+ fc->connection->base_server, "FTP[%d] frl: empty bb",
+ (int) getpid());
+#endif
+ }
+ else {
+ pe = APR_BRIGADE_FIRST(bb);
+ pb = pe->data;
+ if (APR_BUCKET_IS_POOL(pe) && pb->pool)
+ pbuf = (char*)pb->base;
+ else if (APR_BUCKET_IS_HEAP(pe) || APR_BUCKET_IS_POOL(pe))
+ pbuf = pb->heap.base;
+ else
+ return APR_EGENERAL;
+ /* We hope to keep things simple! */
+ if (pe->start != 0)
+ return APR_EGENERAL;
+ /* Remove pe so we have a clean brigade for the loop below */
+ APR_BUCKET_REMOVE(pe);
+#ifdef FTP_TRACE
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0,
+ fc->connection->base_server, "FTP[%d] frl: using previous bb",
+ (int) getpid());
+#endif
+ }
+
+ *result = NULL;
+
+ do {
+
+ for (;;) {
+
+ apr_brigade_cleanup(bb);
+ rv = ap_get_brigade(input_filters, bb,
+ AP_MODE_GETLINE, block, 0);
+
+ if (rv != APR_SUCCESS) {
+ APR_BRIGADE_INSERT_HEAD(bb, pe);
+ return rv;
+ }
+
+ if (APR_BRIGADE_EMPTY(bb)) {
+ APR_BRIGADE_INSERT_HEAD(bb, pe);
+#ifdef FTP_TRACE
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0,
+ fc->connection->base_server, "FTP[%d] frl: got empty brigade",
+ (int) getpid());
+#endif
+ return (block == APR_BLOCK_READ) ? APR_EGENERAL
+ : APR_EAGAIN;
+ }
+
+ while ((e = APR_BRIGADE_FIRST(bb)) != APR_BRIGADE_SENTINEL(bb)) {
+ apr_bucket *e_next;
+ const char *str;
+ apr_size_t len;
+ int mark;
+
+ /* If we see an EOS, don't bother doing anything more. */
+ if (APR_BUCKET_IS_EOS(e)) {
+ break;
+ }
+
+ rv = apr_bucket_read(e, &str, &len, block);
+
+ /*
+ * Upon discovering that the next bucket is a socket,
+ * and that socket is at the OOB mark, we will dump
+ * our current buffer and continue to read the priority
+ * command beyond the OOB mark.
+ */
+ e_next = APR_BUCKET_NEXT(e);
+ if (rv == APR_SUCCESS && APR_BUCKET_IS_SOCKET(e_next)) {
+ apr_socket_t *sock = e_next->data;
+ rv = socket_atmark(sock, &mark);
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0,
+ fc->connection->base_server, "FTP[%d] frl: atmark: %x %d",
+ (int) getpid(), (int) rv, mark);
+ if (rv == APR_SUCCESS && mark) {
+ pe->length = 0;
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0,
+ fc->connection->base_server, "FTP[%d] frl: Saw OOB",
+ (int) getpid());
+ continue;
+ }
+ }
+
+ if (rv != APR_SUCCESS) {
+ APR_BRIGADE_INSERT_HEAD(bb, pe);
+ return rv;
+ }
+
+ if (len == 0) {
+ /* no use attempting a zero-byte alloc (hurts when
+ * using --with-efence --enable-pool-debug) or
+ * doing any of the other logic either
+ */
+ apr_bucket_delete(e);
+ continue;
+ }
+
+ /* Exceeding limits? If so, we'll die. */
+ if (pe->length + len > DEFAULT_LIMIT_REQUEST_LINE + 2) {
+ APR_BRIGADE_INSERT_HEAD(bb, pe);
+ return APR_ENOSPC;
+ }
+
+ /* If exceeding our limit, increase the buffer size */
+ if (pe->length + len > pb->heap.alloc_len) {
+ apr_size_t new_size = pb->heap.alloc_len * 2;
+ char *new_buffer;
+
+ if (pe->length + len > new_size) {
+ new_size = (pe->length + len) * 2;
+ }
+
+ if (new_size > DEFAULT_LIMIT_REQUEST_LINE + 2) {
+ new_size = DEFAULT_LIMIT_REQUEST_LINE + 2;
+ }
+
+ if (pb->pool) {
+ new_buffer = apr_palloc(pb->pool, new_size);
+ memcpy(new_buffer, pb->base, pe->length);
+ pb->base = new_buffer;
+ pbuf = (char*)pb->base;
+ }
+ else {
+ new_buffer = malloc(new_size);
+ if (!new_buffer)
+ return APR_ENOSPC;
+ memcpy(new_buffer, pb->heap.base, pe->length);
+ free (pb->heap.base);
+ pb->heap.base = new_buffer;
+ pbuf = (char*) pb->heap.base;
+ }
+ pb->heap.alloc_len = new_size;
+ }
+
+ /* Just copy the rest of the data to the end of the buffer. */
+ pos = pbuf + pe->length;
+ memcpy(pos, str, len);
+ pe->length += len;
+
+ last_char = pos + len - 1;
+
+ /* Destroy the now-consumed bucket */
+ apr_bucket_delete(e);
+ }
+
+ /* If we got a full line of input, stop reading */
+ if (last_char && (*last_char == APR_ASCII_LF)) {
+
+ /* Trim the trailing spaces or tabs, LF and CR*/
+ while ((last_char > pbuf)
+ && apr_isspace(*(last_char - 1))) {
+ --last_char;
+ }
+
+ /* Since we want to remove the LF from the line, we'll go ahead
+ * and set this last character to be the term NULL
+ */
+ *last_char = '\0';
+
+ /* Return the result string, and the actual bytes
+ * read from the network (before we truncated
+ * trailing whitespace and newlines)
+ *
+ * We may have moved from a pool to another pool,
+ * or to a heap bucket. Reallocate from the current
+ * pool in these cases.
+ */
+ if (pb->pool && pb->pool == pool) {
+ *result = pbuf;
+ }
+ else {
+ *result = apr_palloc(pool, last_char - pbuf + 1);
+ memcpy(*result, pbuf, last_char - pbuf + 1);
+ }
+ *bytes_read = pe->length;
+
+ /* Finally destroy the working bucket - if it is heap
+ * the heap data will also be free()'d.
+ */
+ apr_bucket_destroy(pe);
+#ifdef FTP_TRACE
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0,
+ fc->connection->base_server, "FTP[%d] frl: got full line",
+ (int) getpid());
+#endif
+ return APR_SUCCESS;
+ }
+ }
+
+ } while (pe->length <= 0);
+#ifdef FTP_TRACE
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0,
+ fc->connection->base_server, "FTP[%d] frl: fall through success",
+ (int) getpid());
+#endif
+ return APR_SUCCESS;
+}
+
+/*
+ * ftp_read_request_line: Read the request from the client, and set the
+ * correct values in the request record.
+ *
+ * Arguments: r - The request to read from.
+ * Arguments: bb - The brigade to retrieve.
+ *
+ * Returns: Returns 0 on success, 1 on error.
+ */
+static apr_status_t ftp_read_request_line(ftp_connection *fc,
+ request_rec *r,
+ apr_bucket_brigade *bb)
+{
+ apr_size_t bytes_read;
+ apr_status_t rv;
+ const char *ll;
+
+ if (fc->next_request && *fc->next_request) {
+ r->the_request = apr_pstrdup(r->pool, fc->next_request);
+ bytes_read = fc->next_reqsize;
+ fc->next_request = NULL;
+ fc->next_reqsize = 0;
+#ifdef FTP_TRACE
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0,
+ fc->connection->base_server, "FTP[%d] frrl: using read-ahead request",
+ (int) getpid());
+#endif
+ }
+ else if ((rv = ftp_read_line(&r->the_request, &bytes_read,
+ fc->connection->pool, bb, r->input_filters,
+ APR_BLOCK_READ, fc)) != APR_SUCCESS) {
+ return rv;
+ }
+
+ r->read_length = bytes_read;
+ r->request_time = apr_time_now();
+ ll = r->the_request;
+#ifdef FTP_TRACE
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0,
+ fc->connection->base_server, "FTP[%d] frrl: raw command: %s",
+ (int) getpid(), ll);
+#endif
+ r->method = ftp_toupper(r->pool, ap_getword_white(r->pool, &ll));
+ r->method = ftp_get_cmd_alias(r->method);
+ r->method_number = ap_method_number_of(r->method);
+
+ return APR_SUCCESS;
+}
+
+apr_status_t ftp_read_ahead_request(ftp_connection *fc)
+{
+ apr_status_t rv;
+ const char *ll;
+ const char *method;
+
+#ifdef FTP_TRACE
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0,
+ fc->connection->base_server, "FTP[%d] frar: entering",
+ (int) getpid());
+#endif
+
+ /* Review one command, only once */
+ if (fc->next_request && *fc->next_request) {
+#ifdef FTP_TRACE
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0,
+ fc->connection->base_server, "FTP[%d] frar: previously read-ahead",
+ (int) getpid());
+#endif
+ return APR_SUCCESS;
+ }
+
+ if (!fc->next_pool) {
+ apr_pool_create(&fc->next_pool, fc->connection->pool);
+ fc->next_bb = apr_brigade_create(fc->next_pool,
+ fc->connection->bucket_alloc);
+#ifdef FTP_TRACE
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0,
+ fc->connection->base_server, "FTP[%d] frar: created next_pool",
+ (int) getpid());
+#endif
+ }
+
+ rv = ftp_read_line(&fc->next_request, &fc->next_reqsize,
+ fc->next_pool, fc->next_bb,
+ fc->connection->input_filters,
+ APR_NONBLOCK_READ, fc);
+ if (APR_STATUS_IS_EAGAIN(rv)) {
+#ifdef FTP_TRACE
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0,
+ fc->connection->base_server, "FTP[%d] frar: not ready - read again",
+ (int) getpid());
+#endif
+ /* We actually like some failures here */
+ return APR_SUCCESS;
+ }
+ else if (rv != APR_SUCCESS) {
+ return rv;
+ }
+
+ /* The entire line is read - we no longer need this brigade */
+ apr_brigade_destroy(fc->next_bb);
+ fc->next_bb = NULL;
+
+ ll = fc->next_request;
+
+#ifdef FTP_TRACE
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0,
+ fc->connection->base_server, "FTP[%d] frar: raw command: %s",
+ (int) getpid(), ll);
+#endif
+
+ /*
+ * FIXME: For some reason we miss seeing the OOB mark,
+ * at which point the various telnet sequences are
+ * still in the command stream. We assume that we
+ * should strip out anything before, and including,
+ * the DM (value xf2). Because of strangeness with
+ * how some clients do this, we also strip out
+ * the IAC (xff) and IP (xf4) chars.
+ */
+ if (method = strrchr(ll, '\xff'))
+ ll = ++method;
+ if (method = strrchr(ll, '\xf4'))
+ ll = ++method;
+ if (method = strrchr(ll, '\xf2'))
+ ll = ++method;
+
+ /* XXX gross hack till we straighten out constness */
+ fc->next_request = (char *)ll; /* store away "cleaned" command */
+
+ method = ftp_toupper(fc->next_pool, ap_getword_white(fc->next_pool, &ll));
+
+#ifdef FTP_TRACE
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0,
+ fc->connection->base_server, "FTP[%d] frar: method: %s",
+ (int) getpid(), method);
+#endif
+ /* Can we ignore this command for a while? */
+ if (ftp_cmd_abort_data(method)) {
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0,
+ fc->connection->base_server, "FTP[%d] frar: I see ABOR",
+ (int) getpid());
+ return APR_ECONNRESET;
+ }
+
+ /* Wait to consider this command till the data transfer is complete */
+ return APR_SUCCESS;
+}
+
+/*
+ * ftp_read_request: Called from the connection handler. Used to
+ * read the request and fill out the request record.
+ *
+ * Arguments: fc - The ftp_connection rec associated with this request.
+ *
+ * Returns: Returns an initialized request_rec
+ */
+request_rec *ftp_read_request(ftp_connection *fc)
+{
+ conn_rec *c = fc->connection;
+ apr_status_t rv;
+ request_rec *r;
+ apr_pool_t *p;
+ int access_status;
+ apr_bucket_brigade *tmp_bb;
+ ap_filter_t *f;
+
+ apr_pool_create(&p, c->pool);
+ r = apr_pcalloc(p, sizeof(request_rec));
+
+ r->pool = p;
+ r->connection = c;
+ r->server = c->base_server;
+
+ r->user = NULL;
+ r->ap_auth_type = NULL;
+
+ r->allowed_methods = ap_make_method_list(p, 2);
+
+ r->headers_in = apr_table_make(r->pool, 50);
+ r->subprocess_env = apr_table_make(r->pool, 50);
+ r->headers_out = apr_table_make(r->pool, 12);
+ r->err_headers_out = apr_table_make(r->pool, 5);
+ r->notes = apr_table_make(r->pool, 5);
+
+ r->request_config = ap_create_request_config(r->pool);
+
+ /* Must be set before we run create request hook */
+ r->proto_output_filters = c->output_filters;
+ r->output_filters = r->proto_output_filters;
+ r->proto_input_filters = c->input_filters;
+ r->input_filters = r->proto_input_filters;
+ ap_run_create_request(r);
+
+ /*
+ * We now need to remove the NET_TIME filter to allow
+ * use to control timeouts ourselves.
+ */
+ for (f = c->input_filters; f; f = f->next) {
+ if (strcasecmp(f->frec->name, "NET_TIME") == 0) {
+ ap_remove_input_filter(f);
+ break;
+ }
+ }
+ for (f = r->input_filters; f; f = f->next) {
+ if (strcasecmp(f->frec->name, "NET_TIME") == 0) {
+ ap_remove_input_filter(f);
+ break;
+ }
+ }
+ for (f = r->proto_input_filters; f; f = f->next) {
+ if (strcasecmp(f->frec->name, "NET_TIME") == 0) {
+ ap_remove_input_filter(f);
+ break;
+ }
+ }
+
+ ftp_set_module_config(r->request_config, fc);
+
+ r->per_dir_config = r->server->lookup_defaults;
+
+ r->the_request = NULL;
+ r->protocol = "FTP";
+ r->method = NULL;
+ r->read_length = 0;
+
+ /* We don't use r->uri for every request, but some modules (SSL)
+ * require r->uri to not be NULL in the post_read_request hook
+ *
+ * The canonical (http) form of "Any location" is *, e.g. the
+ * http OPTIONS * request. It's not a bad pattern to keep with
+ * module author's expectations.
+ */
+ r->uri = "*";
+
+ /* Resume any partial request line from fc->next_bb */
+ if (fc->next_bb) {
+ tmp_bb = fc->next_bb;
+#ifdef FTP_TRACE
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0,
+ fc->connection->base_server, "FTP[%d] frr: using next_bb",
+ (int) getpid());
+#endif
+ } else {
+ tmp_bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
+#ifdef FTP_TRACE
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0,
+ fc->connection->base_server, "FTP[%d] frr: using tmp_bb",
+ (int) getpid());
+#endif
+ }
+ if ((rv = ftp_read_request_line(fc, r, tmp_bb)) != APR_SUCCESS)
+ {
+ apr_time_t timeout;
+ apr_bucket_brigade *bb;
+ apr_bucket *b;
+ apr_socket_t *client_socket = ap_get_module_config(c->conn_config,
+ &core_module);
+ char *err;
+ apr_size_t len;
+
+ apr_brigade_destroy(tmp_bb);
+
+ if (rv == APR_TIMEUP) {
+ /*
+ * Handle client timeouts here. The idle timeout for the
+ * control connection is set in the process_connection
+ * handler, if the timeout is reached, ftp_read_request_line
+ * will return with an error. Here we send the client a
+ * friendly error message, and close the connection.
+ */
+ apr_socket_timeout_get(client_socket, &timeout);
+
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0,
+ c->base_server,
+ "User %s timed out after %d seconds", fc->user,
+ (int) (timeout / APR_USEC_PER_SEC));
+
+ err = apr_psprintf(r->pool,
+ "%d Idle Timeout (%d seconds): "
+ "Closing control connection"
+ CRLF, FTP_REPLY_SERVICE_NOT_AVAILABLE,
+ (int) (timeout / APR_USEC_PER_SEC));
+ len = strlen(err);
+
+ bb = apr_brigade_create(r->pool, c->bucket_alloc);
+ rv = apr_brigade_write(bb, ap_filter_flush,
+ c->output_filters, err, len);
+
+ /* Flush the brigade down the filter chain */
+ b = apr_bucket_flush_create(c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(bb, b);
+ ap_pass_brigade(c->output_filters, bb);
+
+ apr_brigade_destroy(bb);
+ }
+ else {
+ /* Remote client suddenly disconnected, don't bother sending
+ * an error since the client is long gone. Just log the error.
+ */
+ ap_log_error(APLOG_MARK, APLOG_INFO, rv,
+ c->base_server, "User %s disconnected", fc->user);
+ }
+ /* Return NULL to the connection handler, causing the connection
+ * to be dropped.
+ */
+ return NULL;
+ }
+
+ apr_brigade_destroy(tmp_bb);
+ fc->next_bb = NULL;
+
+ /* ftp_read_line returns the_request always allocated from the
+ * correct pool. If not, we have a bug. No need to clean up
+ * next_pool above in the failure case, because it is allocated
+ * from the connection (soon to be destroyed.)
+ */
+ if (fc->next_pool) {
+ apr_pool_destroy(fc->next_pool);
+ fc->next_pool = NULL;
+#ifdef FTP_TRACE
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_DEBUG, 0,
+ fc->connection->base_server, "FTP[%d] frr: clearing next_pool",
+ (int) getpid());
+#endif
+ }
+
+ /* PHP does initializations of important data structures in the
+ * post_read_request phase. */
+ if ((access_status = ap_run_post_read_request(r))) {
+
+ ap_log_error(APLOG_MARK, APLOG_NOERRNO|APLOG_INFO, 0,
+ c->base_server, "Post read request failed, dropping "
+ "client connection.");
+ return NULL;
+ }
+
+ return r;
+}
+
+/*
+ * ftp_reply: This function is used for sending command responses to the
+ * client over the control connection. All responses should
+ * be sent through this function.
+ *
+ * Arguments: out_filter - The output filter chain.
+ * p - The pool to allocate from.
+ * n - The ftp response code.
+ * l - Flag that determines if this is a long response or not. Long
+ * responses have the format %d-%s%s, short responses use a
+ * space in place of the dash.
+ *
+ * 1 - this is long response.
+ * 0 - this is a short (the last) response.
+ * fmt - Format string to be sent to the client.
+ *
+ * Returns: apr_status_t
+ */
+apr_status_t ftp_reply(ftp_connection *fc, ap_filter_t *out_filter,
+ apr_pool_t *p, int n, int l, const char *fmt, ...)
+{
+ char buf[BUFSIZ], reply[BUFSIZ];
+ int len;
+ va_list ap;
+ apr_bucket_brigade *bb;
+ apr_bucket *b;
+
+ va_start(ap, fmt);
+ apr_vsnprintf(buf, sizeof(buf), fmt, ap);
+ len = apr_snprintf(reply, sizeof(reply), "%d%s%s%s", n,
+ l == 1 ? "-" : " ", buf, CRLF);
+ va_end(ap);
+
+ bb = apr_brigade_create(p, out_filter->c->bucket_alloc);
+ b = apr_bucket_pool_create(reply, len, p,
+ out_filter->c->bucket_alloc);
+ APR_BRIGADE_INSERT_HEAD(bb, b);
+ b = apr_bucket_flush_create(out_filter->c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(bb, b);
+
+ fc->traffic += len;
+
+ return ap_pass_brigade(out_filter, bb);
+}
+
+/* ftp_show_file: Test if a file exists, and echo it to the control
+ * connection if it exists. This is primarily for
+ * displaying MOTD messages and .message files.
+ *
+ * Arguments: out_filter - The output filter chain.
+ * p - The pool to allocate from.
+ * code - Integer value representing the response code to use.
+ * fc - The ftp connection.
+ * file - Absolute path to the file to be displayed.
+ *
+ * Returns: apr_status_t
+ */
+apr_status_t ftp_show_file(ap_filter_t *out_filter, apr_pool_t *p, int code,
+ ftp_connection *fc, const char *path)
+{
+ apr_status_t rv;
+ apr_file_t *file;
+ char *pos;
+ char buf[BUFSIZ];
+ char reply[BUFSIZ];
+
+ rv = apr_file_open(&file, path, APR_READ, APR_OS_DEFAULT, p);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+
+ while(apr_file_gets(buf, sizeof(buf), file) == APR_SUCCESS) {
+ /* Strip off trailing space/cr/lf, ftp_reply does not expect them */
+ pos = buf + strlen(buf) - 1;
+ while ((pos >= buf) && apr_isspace(*pos))
+ --pos;
+ pos[1] = '\0';
+
+ ftp_message_generate(fc, buf, reply, sizeof(reply));
+
+ rv = ftp_reply(fc, out_filter, p, code, 1, "%s", reply);
+ if (rv != APR_SUCCESS) {
+ return rv;
+ }
+ }
+
+ return apr_file_close(file);
+}
+
+/* ftp_send_response: Send response to the client based on the status code
+ * These are currently listed in numerical order.
+ * For responses such as 503's where the error message
+ * can change, the caller is requried to fill out
+ * the respose_notes in the ftp_connection structure.
+ *
+ * Arguments: r - The request.
+ * status - FTP status code.
+ *
+ * Returns: nothing
+ */
+void ftp_send_response(request_rec *r, int status)
+{
+ ftp_connection *fc = ftp_get_module_config(r->request_config);
+ ftp_server_config *fsc;
+ conn_rec *c = r->connection;
+
+ /* We are done checking subrequest values for r->status, so we can
+ * place our ftp reply code there so it can be logged */
+ r->status = status;
+
+ /* In general, status codes below 400 will be considered success.
+ * Specific exceptions are toggled, below.
+ */
+ if (status >= 400) {
+ apr_table_setn(r->subprocess_env, "ftp_transfer_ok", "0");
+ }
+
+ switch(status) {
+ case FTP_REPLY_SYSTEM_STATUS:
+ ftp_reply(fc, c->output_filters, r->pool, FTP_REPLY_SYSTEM_STATUS, 1,
+ "Extensions supported:\n"
+ " AUTH TLS\n"
+ " PBSZ\n"
+ " PROT\n"
+ " SIZE\n"
+ " MDTM");
+ ftp_reply(fc, c->output_filters, r->pool, FTP_REPLY_SYSTEM_STATUS, 0,
+ "END");
+ break;
+ case FTP_REPLY_SYSTEM_TYPE:
+#ifdef WIN32
+ ftp_reply(fc, c->output_filters, r->pool, FTP_REPLY_SYSTEM_TYPE, 0,
+ "Win32 Type");
+#else
+ ftp_reply(fc, c->output_filters, r->pool, FTP_REPLY_SYSTEM_TYPE, 0,
+ "UNIX Type");
+#endif /* WIN32 */
+ break;
+ case FTP_REPLY_CONTROL_CLOSE:
+ fsc = ftp_get_module_config(c->base_server->module_config);
+ if (fsc->exit_message) {
+ if (fsc->exit_message_isfile) {
+ ftp_show_file(c->output_filters, r->pool,
+ FTP_REPLY_CONTROL_CLOSE, fc, fsc->exit_message);
+ }
+ else {
+ char reply[BUFSIZ];
+ ftp_message_generate(fc, fsc->exit_message, reply,
+ sizeof(reply));
+ ftp_reply(fc, c->output_filters, r->pool,
+ FTP_REPLY_CONTROL_CLOSE, 1, reply);
+ }
+ }
+
+ ftp_reply(fc, c->output_filters, r->pool, FTP_REPLY_CONTROL_CLOSE, 0,
+ "Goodbye.");
+ break;
+ case FTP_REPLY_DATA_CLOSE:
+ ftp_reply(fc, c->output_filters, r->pool, FTP_REPLY_DATA_CLOSE, 0,
+ "Transfer complete.");
+ break;
+ case FTP_REPLY_USER_LOGGED_IN:
+ ftp_reply(fc, c->output_filters, r->pool, FTP_REPLY_USER_LOGGED_IN, 0,
+ "User %s logged in", fc->user);
+ break;
+ case FTP_REPLY_SECURITY_EXCHANGE_DONE:
+ ftp_reply(fc, c->output_filters, r->pool,
+ FTP_REPLY_SECURITY_EXCHANGE_DONE, 0,
+ "Security exchange completed");
+ break;
+ case FTP_REPLY_COMPLETED:
+ ftp_reply(fc, c->output_filters, r->pool, FTP_REPLY_COMPLETED, 0,
+ "%s command successful.", r->method);
+ break;
+ case FTP_REPLY_CANNOT_OPEN_DATACONN:
+ ftp_reply(fc, c->output_filters, r->pool,
+ FTP_REPLY_CANNOT_OPEN_DATACONN,
+ 0, "Can't open data connection.");
+ break;
+ case FTP_REPLY_TRANSFER_ABORTED:
+ ftp_reply(fc, c->output_filters, r->pool, FTP_REPLY_TRANSFER_ABORTED,
+ 0, "Transfer aborted");
+ break;
+ case FTP_REPLY_COMMAND_UNRECOGNIZED:
+ ftp_reply(fc, c->output_filters, r->pool,
+ FTP_REPLY_COMMAND_UNRECOGNIZED, 0,
+ "%s: Command not recognized", r->method);
+ break;
+ case FTP_REPLY_BAD_SEQUENCE:
+ ftp_reply(fc, c->output_filters, r->pool, FTP_REPLY_BAD_SEQUENCE, 0,
+ "Bad sequence of commands");
+ break;
+ case FTP_REPLY_PROT_NOT_SUPPORTED:
+ ftp_reply(fc, c->output_filters, r->pool,
+ FTP_REPLY_PROT_NOT_SUPPORTED, 0,
+ "Requested PROT level not supported by mechanism");
+ break;
+
+ /* Exception cases, failure codes that fall before the 400's: */
+ case FTP_REPLY_SERVICE_READY_IN_N_MIN:
+ case FTP_REPLY_NOT_IMPLEMENTED:
+ case FTP_REPLY_NEED_ACCOUNT:
+ apr_table_setn(r->subprocess_env, "ftp_transfer_ok", "0");
+ /* failure is flagged for status < 400
+ * now fall through...
+ */
+
+ default:
+ ftp_reply(fc, c->output_filters, r->pool, status, 0,
+ "%s", fc->response_notes ? fc->response_notes
+ : "Undefined Internal Error.");
+ }
+
+ return;
+}
Propchange: incubator/mod_ftp/trunk/src/ftp_protocol.c
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/mod_ftp/trunk/src/ftp_protocol.c
------------------------------------------------------------------------------
svn:executable =
Added: incubator/mod_ftp/trunk/src/ftp_request.c
URL: http://svn.apache.org/viewcvs/incubator/mod_ftp/trunk/src/ftp_request.c?rev=306631&view=auto
==============================================================================
--- incubator/mod_ftp/trunk/src/ftp_request.c (added)
+++ incubator/mod_ftp/trunk/src/ftp_request.c Thu Oct 6 06:16:28 2005
@@ -0,0 +1,82 @@
+/* Copyright 1999-2005 The Apache Software Foundation or its licensors, as
+ * applicable.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Original Copyright (c) Covalent Technologies 2001-2005
+ *
+ * FTP Protocol module for Apache 2.0
+ */
+
+#include "mod_ftp.h"
+
+#ifndef apr_time_from_sec
+#define apr_time_from_sec(sec) ((apr_time_t)(sec) * APR_USEC_PER_SEC)
+#endif
+
+/*
+ * ftp_process_request: Called after the request is read. This function
+ * will process the request, and call the correct
+ * command handler, send the response and log the
+ * transaction.
+ *
+ * Arguments: r - The request to be processed.
+ * fc - The ftp connection record associated with this request.
+ *
+ * Returns: Nothing.
+ */
+void ftp_process_request(request_rec *r)
+{
+ ftp_connection *fc = ftp_get_module_config(r->request_config);
+ ftp_server_config *fsc =
+ ftp_get_module_config(r->server->module_config);
+
+ int res;
+
+ fc->traffic += r->read_length;
+ fc->response_notes = "";
+
+ apr_table_setn(r->subprocess_env, "ftp_transfer_ok", "1");
+ res = ftp_run_cmd(r, r->method);
+
+ /* If the passive connection has been open too long, close it */
+ if ((fc->passive_created != -1) && fc->csock
+ && (res != FTP_REPLY_DATA_CLOSE)
+ && (res != FTP_REPLY_CONTROL_CLOSE)
+ && (apr_time_now() - fc->passive_created
+ > apr_time_from_sec(fsc->timeout_data))) {
+ ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
+ "Timeout waiting to use passive port (closing data connection).");
+ apr_socket_close(fc->csock);
+ fc->csock = NULL;
+ fc->passive_created = -1;
+ }
+
+ ftp_send_response(r, res);
+
+ ap_run_log_transaction(r);
+}
+
+apr_status_t ftp_protocol_filter(ap_filter_t *f, apr_bucket_brigade *b,
+ ap_input_mode_t mode, apr_read_type_e block,
+ apr_off_t readbytes)
+{
+ apr_bucket *eos;
+
+ eos = apr_bucket_eos_create(b->bucket_alloc);
+ APR_BRIGADE_INSERT_HEAD(b, eos);
+
+ return APR_SUCCESS;
+}
Propchange: incubator/mod_ftp/trunk/src/ftp_request.c
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/mod_ftp/trunk/src/ftp_request.c
------------------------------------------------------------------------------
svn:executable =
Added: incubator/mod_ftp/trunk/src/ftp_util.c
URL: http://svn.apache.org/viewcvs/incubator/mod_ftp/trunk/src/ftp_util.c?rev=306631&view=auto
==============================================================================
--- incubator/mod_ftp/trunk/src/ftp_util.c (added)
+++ incubator/mod_ftp/trunk/src/ftp_util.c Thu Oct 6 06:16:28 2005
@@ -0,0 +1,592 @@
+/* Copyright 1999-2005 The Apache Software Foundation or its licensors, as
+ * applicable.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Original Copyright (c) Covalent Technologies 2001-2005
+ *
+ * FTP Protocol module for Apache 2.0
+ */
+
+#include <sys/stat.h> /* For file perms */
+
+#include "mod_ftp.h"
+#ifndef FTP_NO_GLOB
+#include "ftp_glob.h" /* Custom globber */
+#endif /* FTP_NO_GLOB */
+
+#include "ap_mpm.h" /* For MPM query interface */
+
+static char *ftp_modestring_get(char *mode, apr_filetype_e typ,
+ apr_fileperms_t perms)
+{
+
+#ifdef WIN32
+ perms = ftp_unix_mode2perms(0);
+#endif /* WIN32 */
+
+ if(perms < 0 || perms >= FTP_MAX_MODESTRING) {
+ return FTP_UNKNOWN_MODESTRING; /* see mod_ftp.h */
+ }
+
+ if (typ == APR_DIR) {
+ mode[0] = 'd';
+ }
+ if(perms & APR_UREAD) {
+ mode[1] = 'r';
+ }
+ if(perms & APR_UWRITE) {
+ mode[2] = 'w';
+ }
+ if(perms & APR_UEXECUTE) {
+ mode[3] = 'x';
+ }
+ if(perms & APR_USETID) {
+ mode[3] = 's';
+ }
+ if(perms & APR_GREAD) {
+ mode[4] = 'r';
+ }
+ if(perms & APR_GWRITE) {
+ mode[5] = 'w';
+ }
+ if(perms & APR_GEXECUTE) {
+ mode[6] = 'x';
+ }
+ if(perms & APR_GSETID) {
+ mode[6] = 's';
+ }
+ if(perms & APR_WREAD) {
+ mode[7] = 'r';
+ }
+ if(perms & APR_WWRITE) {
+ mode[8] = 'w';
+ }
+ if(perms & APR_WEXECUTE) {
+ mode[9] = 'x';
+ }
+ if(perms & APR_WSTICKY) {
+ mode[9] = 't';
+ }
+ return mode;
+}
+
+
+/* ftp_make_direntry: Fill out a directory entry structure from the
+ * directory being requested and a filename.
+ *
+ * Arguments: r - The request record for the directory index
+ * name - The filename to be checked.
+ *
+ * Returns: ftp_direntry structure on success, NULL otherwise.
+ */
+static struct ftp_direntry *ftp_direntry_make(request_rec *r,
+ const char *name)
+{
+ ftp_server_config *fsc = ftp_get_module_config(r->server->module_config);
+ struct ftp_direntry *dirent;
+ request_rec *rr;
+ const char *test, *sl;
+ char mode[FTP_MODESTRING_LEN] = "----------";
+
+ for (test = name; sl = strchr(test, '/'); test = sl + 1)
+ /* noop */ ;
+
+ if (!strcmp("..", test)) {
+ return NULL;
+ }
+
+ rr = ap_sub_req_lookup_file(name, r, NULL);
+
+ /* Another hack. With BSD glob it is possible to return a file
+ * outside the current directory. (or document root for that matter).
+ * This was an oversight made when adding the GLOB_NOPERIOD option
+ * to BSD glob. Parsing the uri and checking if NULL is returned
+ * will allow us to check if the current file is outside the docroot.
+ */
+ ap_parse_uri(rr, name);
+ ap_getparents(rr->uri);
+
+ if ((rr->finfo.filetype != 0) &&
+ ((rr->status == HTTP_OK) || (rr->status == HTTP_MOVED_PERMANENTLY) ||
+ (rr->status == HTTP_UNAUTHORIZED &&
+ fsc->options & FTP_OPT_SHOWUNAUTH)) &&
+ (rr->uri != NULL))
+ {
+ apr_time_exp_t xt;
+ apr_size_t retcode;
+ char *lasts;
+
+ /* We don't mess with char returned here, so its fine to cast */
+ lasts = ap_strrchr((char *)name, '/');
+
+ dirent = apr_pcalloc(r->pool, sizeof(ftp_direntry));
+ dirent->next = NULL;
+ dirent->name = apr_pstrdup(r->pool, lasts + 1);
+ dirent->nlink = rr->finfo.nlink;
+ dirent->size = rr->finfo.size;
+ dirent->csize = rr->finfo.csize;
+ dirent->modestring = apr_pstrdup(r->pool,
+ ftp_modestring_get(
+ mode,
+ rr->finfo.filetype,
+ rr->finfo.protection)
+ );
+
+ /* If FTPOptions RemoveUserGroup is set, we don't bother looking
+ * up user and group information for each file */
+ if (fsc->options & FTP_OPT_REMOVEUSERGROUP) {
+ dirent->username = apr_psprintf(r->pool, "%d", rr->finfo.user);
+ dirent->groupname = apr_psprintf(r->pool, "%d", rr->finfo.group);
+ }
+ else {
+ if ((apr_get_username(&dirent->username, rr->finfo.user, r->pool)
+ != APR_SUCCESS) || (!dirent->username)
+ || (!dirent->username[0])) {
+ dirent->username = apr_psprintf(r->pool, "%d", rr->finfo.user);
+ }
+ if ((apr_get_groupname(&dirent->groupname, rr->finfo.group, r->pool)
+ != APR_SUCCESS) || (!dirent->groupname)
+ || (!dirent->groupname[0])) {
+ dirent->groupname = apr_psprintf(r->pool, "%d", rr->finfo.group);
+ }
+ }
+
+ apr_explode_localtime(&xt, rr->finfo.mtime);
+
+ if (r->request_time - rr->finfo.mtime >
+ 180 * 24 * 60 * 60 * APR_USEC_PER_SEC) {
+ apr_strftime(dirent->datestring, &retcode,
+ sizeof(dirent->datestring), "%b %e %Y", &xt);
+ }
+ else {
+ apr_strftime(dirent->datestring, &retcode,
+ sizeof(dirent->datestring), "%b %e %H:%M", &xt);
+ }
+ }
+ else {
+ dirent = NULL;
+ }
+
+ ap_destroy_sub_req(rr);
+ return dirent;
+}
+
+#ifdef FTP_NO_GLOB
+/* ftp_dsortf: Used for sorting directory entries when globbing is
+ * not enabled. Called by qsort()
+ *
+ * Arguments: d1 - The first directory entry
+ * d2 - The second directory entry
+ *
+ * Returns: An integer less than, equal to or greater than zero if
+ * d1 is found, respectively, to be less than, match or
+ * be greater than d2.
+ */
+ static int ftp_dsortf(struct ftp_direntry **d1,
+ struct ftp_direntry **d2)
+{
+ /* Simple sort based on filename */
+ return strcmp((*d1)->name, (*d2)->name);
+}
+
+/* ftp_direntry_get: Identical to ftp_direntry_get_glob, but we do not
+ * support globbing.
+ *
+ * Arguments r - The request rec for the directory listing
+ *
+ * Returns: The sorted array of directory entries on success, NULL otherwise
+ */
+struct ftp_direntry *ftp_direntry_get(request_rec *r)
+{
+ struct ftp_direntry *p, *head, *current, **a;
+ apr_dir_t *dir;
+ apr_finfo_t finfo;
+ apr_status_t rv;
+ int num, i;
+
+#ifdef WIN32
+ /* FIXME: A nasty hack. If the last char is a '*' then
+ * we silently delete it and press on. This allows for
+ * things like 'NLST *' to work */
+ num = strlen(r->filename) - 1;
+ if ((num >= 0) && (r->filename[num] == '*')) {
+ r->filename[num] = '\0';
+ num = strlen(r->filename) - 1;
+ if ((num >= 0) && (r->filename[num] == '/')) {
+ r->filename[num] = '\0';
+ }
+ }
+
+ /* Win32 will always return sucess on apr_dir_open, which means
+ * we have to stat the file to see if the request was made for
+ * a directory or file.
+ */
+ rv = apr_stat(&finfo, r->filename, APR_FINFO_MIN, r->pool);
+ if (finfo.filetype != APR_DIR) {
+ /* Must be for a single file */
+ return ftp_direntry_make(r, r->filename);
+ }
+#endif /* WIN32 */
+
+ rv = apr_dir_open(&dir, r->filename, r->pool);
+
+ if (rv != APR_SUCCESS) {
+ if (APR_STATUS_IS_ENOTDIR(rv)) {
+ /* Must be for a single file */
+ return ftp_direntry_make(r, r->filename);
+ }
+ else {
+ return NULL;
+ }
+ }
+
+ num = 0;
+ head = current = NULL;
+ while ((rv = apr_dir_read(&finfo, APR_FINFO_DIRENT, dir))
+ == APR_SUCCESS) {
+
+ p = ftp_direntry_make(r, ap_make_full_path(r->pool, r->filename,
+ finfo.name));
+ if (!p) {
+ continue;
+ }
+
+ /* Add this entry to the linked list */
+ if (head == NULL) {
+ head = p;
+ p->next = NULL;
+ current = p;
+ }
+ else {
+ current->next = p;
+ current = p;
+ }
+ p->child = NULL;
+ num++;
+ }
+
+ apr_dir_close(dir);
+
+ /* Sort this mess */
+ if (num > 0) {
+ a = (struct ftp_direntry **) apr_pcalloc(r->pool,
+ num *
+ sizeof (ftp_direntry));
+ p = head;
+ i = 0;
+ while (p) {
+ a[i++] = p;
+ p = p->next;
+ }
+ qsort((void *) a, num, sizeof (struct ftp_direntry *),
+ (int(*)(const void *, const void *))ftp_dsortf);
+
+ /* Re-construct the list from the sorted list */
+ head = a[0];
+ current = head;
+ for (i = 1; i < num; i++) {
+ current->next = a[i];
+ current = current->next;
+ }
+ current->next = NULL;
+ }
+
+ return head;
+}
+
+#else
+/* ftp_direntry_get_glob: Return an array of ftp_direntry structures based
+ * on the uri stored in the request rec. An extra
+ * argument may be passed for pattern matching. Note
+ * that this uses glob, which is only available on
+ * Unix type systems.
+ *
+ * Arguments: r - The request rec for the directory listing
+ * pattern - Pattern to get a directory listing for
+ *
+ * Returns: The sorted array of directory entries on success, NULL otherwise
+ */
+
+struct ftp_direntry *ftp_direntry_get_glob(request_rec *r,
+ const char *pattern)
+{
+ struct ftp_direntry *p, *head, *current;
+ int i;
+ ftp_glob_t gb;
+ /* The actual search pattern, used to determine if we should recurse
+ * into a directory or not. If the search pattern is just *, we should
+ * not decend. For search patterns like 'm*', we should.
+ */
+ const char *search = strrchr((char *) pattern, '/') + 1;
+
+ if(ftp_glob(pattern, FTP_GLOB_PERIOD, NULL, &gb)) {
+ return NULL;
+ }
+
+ /* BSD glob does not consider finding no matches an error. */
+ if (gb.gl_pathc == 0 && gb.gl_matchc == 0) {
+ return NULL;
+ }
+
+ head = NULL;
+ current = NULL;
+ for(i=0; gb.gl_pathv[i] != NULL; i++) {
+
+ /* Run the subreq and fill out most information */
+ p = ftp_direntry_make(r, gb.gl_pathv[i]);
+ if (!p) {
+ continue;
+ }
+
+ /* Add this entry to the linked list */
+ if (head == NULL) {
+ head = p;
+ p->next = NULL;
+ current = p;
+ }
+ else {
+ current->next = p;
+ current = p;
+ }
+
+ /* We are only going to support single recursive listings
+ * this means that reqeusts such as 'ls m*' will print out
+ * all files that match, and recurse a single level into
+ * directories.
+ */
+
+ if ((search[0] != '*') && (p->modestring[0] == 'd')) {
+ const char *newpattern = apr_pstrcat(r->pool, gb.gl_pathv[i],
+ "/*", NULL);
+ p->child = ftp_direntry_get_glob(r, newpattern);
+ }
+ else {
+ p->child = NULL;
+ }
+ }
+
+ ftp_globfree(&gb);
+ return head;
+}
+#endif /* FTP_NO_GLOB */
+
+/* ftp_set_authorization: set the r->headers_in Authorization header
+ *
+ * Arguments: r - The request
+ *
+ * Returns: nada
+ */
+
+void ftp_set_authorization(request_rec *r)
+{
+ ftp_connection *fc = ftp_get_module_config(r->request_config);
+ r->user = apr_pstrdup(r->pool, fc->user);
+ apr_table_setn(r->headers_in, "Authorization", fc->authorization);
+}
+
+/* ftp_set_uri: Setup r->uri based on a file argument and user's
+ * current working directory. We also run the translate
+ * name phase here to set r->filename.
+ *
+ * Arguments: r - The request
+ * arg - The file argument
+ *
+ * Returns: nothing
+ */
+int ftp_set_uri(request_rec *r, const char *arg)
+{
+ ftp_connection *fc = ftp_get_module_config(r->request_config);
+ apr_status_t res;
+
+ if (arg[0] == '/') {
+ ap_parse_uri(r, arg);
+ } else {
+ ap_parse_uri(r, ap_make_full_path(r->pool, fc->cwd, arg));
+ }
+ ap_getparents(r->uri);
+
+ /* If the path ended in /., ap_getparents converts it to /, but we really
+ * don't want the trailing / there, so remove it.
+ */
+ if (r->uri[strlen(r->uri) - 1] == '/') {
+ r->uri[strlen(r->uri) - 1] = '\0';
+ }
+
+ /*
+ * Parsed uri is empty if we try a path outside the document
+ * root. For now, just set the uri to /, but I'm sure there
+ * is a better way around this.
+ *
+ * - rpm
+ */
+ if (r->uri[0] == '\0') {
+ r->uri = apr_pstrdup(r->pool, "/");
+ }
+ res = ap_run_translate_name(r);
+ if (res) {
+ fc->response_notes = apr_psprintf(r->pool, FTP_MSG_PERM_DENIED,
+ r->parsed_uri.path);
+ return FTP_REPLY_LOCAL_ERROR;
+ }
+ r->uri = ap_escape_uri(r->pool, r->uri);
+
+ return OK;
+}
+
+/* ftp_unix_perms2mode: Translate apr_fileperms_t to mode_t.
+ *
+ * Arguments: perms - apr_fileperms_t
+ *
+ * Returns: mode_t
+ */
+mode_t ftp_unix_perms2mode(apr_fileperms_t perms)
+{
+ mode_t mode = 0;
+
+#ifndef WIN32
+ if (perms & APR_UREAD) {
+ mode |= S_IRUSR;
+ }
+ if (perms & APR_UWRITE) {
+ mode |= S_IWUSR;
+ }
+ if (perms & APR_UEXECUTE) {
+ mode |= S_IXUSR;
+ }
+ if (perms & APR_GREAD) {
+ mode |= S_IRGRP;
+ }
+ if (perms & APR_GWRITE) {
+ mode |= S_IWGRP;
+ }
+ if (perms & APR_GEXECUTE) {
+ mode |= S_IXGRP;
+ }
+ if (perms & APR_WREAD) {
+ mode |= S_IROTH;
+ }
+ if (perms & APR_WWRITE) {
+ mode |= S_IWOTH;
+ }
+ if (perms & APR_WEXECUTE) {
+ mode |= S_IXOTH;
+ }
+#endif /* WIN32 */
+ return mode;
+}
+
+/* ftp_unix_mode2perms: Translate mode_t to apr_fileperms_t.
+ *
+ * Arguments: mode - mode_t
+ *
+ * Returns: apr_fileperms_t
+ */
+apr_fileperms_t ftp_unix_mode2perms(mode_t mode)
+{
+ apr_fileperms_t perms = 0;
+#ifdef WIN32
+ perms |= APR_UREAD;
+ perms |= APR_UWRITE;
+ perms |= APR_UEXECUTE;
+ perms |= APR_GREAD;
+ perms |= APR_GEXECUTE;
+ perms |= APR_WREAD;
+ perms |= APR_WEXECUTE;
+
+#else
+ if (mode & S_IRUSR) {
+ perms |= APR_UREAD;
+ }
+ if (mode & S_IWUSR) {
+ perms |= APR_UWRITE;
+ }
+ if (mode & S_IXUSR) {
+ perms |= APR_UEXECUTE;
+ }
+ if (mode & S_IRGRP) {
+ perms |= APR_GREAD;
+ }
+ if (mode & S_IWGRP) {
+ perms |= APR_GWRITE;
+ }
+ if (mode & S_IXGRP) {
+ perms |= APR_GEXECUTE;
+ }
+ if (mode & S_IROTH) {
+ perms |= APR_WREAD;
+ }
+ if (mode & S_IWOTH) {
+ perms |= APR_WWRITE;
+ }
+ if (mode & S_IXOTH) {
+ perms |= APR_WEXECUTE;
+ }
+#endif /* WIN32 */
+ return perms;
+}
+
+/* ftp_toupper: Convert a string to uppercase
+ *
+ * Arguments: s - The string
+ *
+ * Returns: The capitialized string.
+ */
+char *ftp_toupper(apr_pool_t *p, const char *s)
+{
+ char *upper = apr_pstrdup(p, s);
+ char *pos = upper;
+
+ while (*pos != '\0') {
+ *pos = apr_toupper(*pos);
+ pos++;
+ }
+
+ return upper;
+}
+
+/* ftp_check_maxclients: Check the scoreboard for other available servers.
+ *
+ * Arguments: r - The current request
+ *
+ * Returns: 0 if we find a server, 1 otherwise.
+ */
+int ftp_check_maxclients(request_rec *r)
+{
+ int hard_server_limit, hard_thread_limit;
+ int i, j;
+ worker_score *scoreboard;
+
+ ap_mpm_query(AP_MPMQ_HARD_LIMIT_DAEMONS, &hard_server_limit);
+ ap_mpm_query(AP_MPMQ_HARD_LIMIT_THREADS, &hard_thread_limit);
+
+ for (i = 0; i < hard_server_limit; i++) {
+ for (j = 0; j < hard_thread_limit; j++) {
+ scoreboard = ap_get_scoreboard_worker(i, j);
+ if (scoreboard->status == SERVER_READY)
+ return 0;
+ }
+ }
+
+ /* We are the only available server, so go ahead. Maybe this should
+ * be optimized out so it's only in debug builds? */
+ if (ap_exists_config_define("ONE_PROCESS")) {
+ return 0;
+ }
+
+ ap_log_rerror(APLOG_MARK, APLOG_NOERRNO | APLOG_ERR, 0, r,
+ "Maximum number of FTP sessions reached.");
+ return 1;
+}
Propchange: incubator/mod_ftp/trunk/src/ftp_util.c
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/mod_ftp/trunk/src/ftp_util.c
------------------------------------------------------------------------------
svn:executable =
Added: incubator/mod_ftp/trunk/src/mod_ftp.c
URL: http://svn.apache.org/viewcvs/incubator/mod_ftp/trunk/src/mod_ftp.c?rev=306631&view=auto
==============================================================================
--- incubator/mod_ftp/trunk/src/mod_ftp.c (added)
+++ incubator/mod_ftp/trunk/src/mod_ftp.c Thu Oct 6 06:16:28 2005
@@ -0,0 +1,917 @@
+/* Copyright 1999-2005 The Apache Software Foundation or its licensors, as
+ * applicable.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+ * Original Copyright (c) Covalent Technologies 2001-2005
+ *
+ * FTP Protocol module for Apache 2.0
+ */
+
+#include "mod_ftp.h"
+#include "ftp_config.h"
+
+#define FTP_SERVER_STRING PACKAGE "/" VERSION
+
+
+static ap_filter_rec_t *ftp_crlf_filter_handle;
+static ap_filter_rec_t *ftp_data_out_filter_handle;
+static ap_filter_rec_t *ftp_byterange_filter_handle;
+ap_filter_rec_t *ftp_input_filter_handle;
+ap_filter_rec_t *ftp_content_length_filter_handle;
+ap_filter_rec_t *ftp_ssl_input_filter_handle;
+ap_filter_rec_t *ftp_ssl_output_filter_handle;
+
+/* Register callbacks for mod_log_config. */
+static int ftp_pre_config(apr_pool_t *p, apr_pool_t *plog,
+ apr_pool_t *ptemp)
+{
+ APR_OPTIONAL_FN_TYPE(ap_register_log_handler) *log_pfn_register;
+
+ /* Find the handles of non-ftp filters we will be using
+ *
+ * This must be done after register_hooks since SSL
+ * may not be loaded before FTP, but before we actually
+ * process the config because we test those filter handles
+ * to determine if SSL-related directives are valid.
+ */
+ ftp_byterange_filter_handle
+ = ap_get_output_filter_handle("BYTERANGE");
+ ftp_content_length_filter_handle
+ = ap_get_output_filter_handle("CONTENT_LENGTH");
+ ftp_ssl_input_filter_handle
+ = ap_get_input_filter_handle(FTP_SSL_FILTER);
+ ftp_ssl_output_filter_handle
+ = ap_get_output_filter_handle(FTP_SSL_FILTER);
+
+ /* Register our custom log format flags
+ */
+ log_pfn_register = APR_RETRIEVE_OPTIONAL_FN(ap_register_log_handler);
+
+ if (log_pfn_register) {
+ log_pfn_register(p, "M", ftp_log_transfer_mode, 0);
+ log_pfn_register(p, "F", ftp_log_action_flags, 0);
+ log_pfn_register(p, "d", ftp_log_transfer_direction, 0);
+ log_pfn_register(p, "W", ftp_log_accessed_anonymously, 0);
+ log_pfn_register(p, "S", ftp_log_service_name, 0);
+ log_pfn_register(p, "Z", ftp_log_auth_method, 0);
+ log_pfn_register(p, "Y", ftp_log_auth_user_id, 0);
+ }
+
+ return OK;
+}
+
+
+static int ftp_post_config(apr_pool_t *p, apr_pool_t *plog,
+ apr_pool_t *ptemp, server_rec *s)
+{
+ server_rec *base = s;
+
+ ap_add_version_component(p, FTP_SERVER_STRING);
+
+ /* Fixup base server and virtual host default values
+ */
+ for (; s; s = s->next) {
+ ftp_server_config *fsc = ftp_get_module_config(s->module_config);
+
+ if (fsc->fileperms == APR_OS_DEFAULT)
+ fsc->fileperms = FTP_DEFAULT_UMASK;
+
+ /* In the config phase, ->fileperms was a negative umask.
+ * for operation, exchange this with a positive protections
+ * to pass to the apr_file_open protection flag.
+ */
+ fsc->fileperms = (APR_UREAD | APR_UWRITE |
+ APR_GREAD | APR_GWRITE |
+ APR_WREAD | APR_WWRITE)
+ & ~fsc->fileperms;
+
+ if (fsc->timeout_login == FTP_UNSPEC)
+ fsc->timeout_login = FTP_TIMEOUT_LOGIN;
+ if (fsc->timeout_idle == FTP_UNSPEC)
+ fsc->timeout_idle = FTP_TIMEOUT_IDLE;
+ if (fsc->timeout_data == FTP_UNSPEC)
+ fsc->timeout_data = FTP_TIMEOUT_DATA;
+
+ if (fsc->max_login_attempts == FTP_UNSPEC)
+ fsc->max_login_attempts = FTP_MAX_LOGINS;
+
+ if (fsc->active_min == FTP_UNSPEC)
+ fsc->active_min = fsc->active_max = -1;
+
+ if (fsc->pasv_min == FTP_UNSPEC)
+ fsc->pasv_min = fsc->pasv_max = 0;
+
+ if (fsc->data_block_size == FTP_UNSPEC)
+ fsc->data_block_size = FTP_DATA_BLOCK_SIZE;
+
+ if (fsc->limit_peruser == FTP_UNSPEC)
+ fsc->limit_peruser = 0;
+ if (fsc->limit_perip == FTP_UNSPEC)
+ fsc->limit_perip = 0;
+ if (fsc->limit_perserver == FTP_UNSPEC)
+ fsc->limit_perserver = 0;
+
+ fsc->limitdbfile = ap_server_root_relative(p, fsc->limitdbfile);
+
+ }
+
+ if (ftp_mutexdb_init(base, p) != APR_SUCCESS) {
+ ap_log_error(APLOG_MARK, APLOG_CRIT, 0, base,
+ "Could not initialize FTP mutex");
+ return HTTP_INTERNAL_SERVER_ERROR;
+ }
+
+ apr_pool_cleanup_register(p, base, ftp_mutexdb_cleanup,
+ apr_pool_cleanup_null);
+ return OK;
+}
+
+static void ftp_child_init(apr_pool_t *p, server_rec *s)
+{
+ ftp_mutexdb_child_init(s, p);
+}
+
+static void *create_ftp_server_config(apr_pool_t *p, server_rec *s)
+{
+ ftp_server_config *fsc = apr_pcalloc(p, sizeof(*fsc));
+
+ fsc->fileperms = APR_OS_DEFAULT;
+ fsc->timeout_login = FTP_UNSPEC;
+ fsc->timeout_idle = FTP_UNSPEC;
+ fsc->timeout_data = FTP_UNSPEC;
+ fsc->max_login_attempts = FTP_UNSPEC;
+
+ fsc->active_min = FTP_UNSPEC;
+ fsc->active_max = FTP_UNSPEC;
+ fsc->pasv_min = FTP_UNSPEC;
+ fsc->pasv_max = FTP_UNSPEC;
+
+ fsc->data_block_size = FTP_UNSPEC;
+
+ fsc->limit_peruser = FTP_UNSPEC;
+ fsc->limit_perip = FTP_UNSPEC;
+ fsc->limit_perserver = FTP_UNSPEC;
+
+ fsc->limitdbfile = FTP_DEFAULT_DBFILE;
+ return fsc;
+}
+
+void *merge_ftp_server_config(apr_pool_t *p, void *basev, void *addv)
+{
+ ftp_server_config *base = (ftp_server_config *)basev;
+ ftp_server_config *add = (ftp_server_config *)addv;
+ ftp_server_config *fsc = apr_palloc(p, sizeof(*fsc));
+
+ /* We default to the add config, so any directive not
+ * handled here won't be inherited from the base server.
+ */
+ memcpy (fsc, add, sizeof(*fsc));
+
+ if (fsc->fileperms == APR_OS_DEFAULT)
+ fsc->fileperms = base->fileperms;
+
+ if (fsc->timeout_login == FTP_UNSPEC)
+ fsc->timeout_login = base->timeout_login;
+ if (fsc->timeout_idle == FTP_UNSPEC)
+ fsc->timeout_idle = base->timeout_idle;
+ if (fsc->timeout_data == FTP_UNSPEC)
+ fsc->timeout_data = base->timeout_data;
+
+ if (fsc->max_login_attempts == FTP_UNSPEC)
+ fsc->max_login_attempts = FTP_MAX_LOGINS;
+
+ if (fsc->active_min == FTP_UNSPEC) {
+ fsc->active_min = base->active_min;
+ fsc->active_max = base->active_max;
+ }
+ if (fsc->pasv_min == FTP_UNSPEC) {
+ fsc->pasv_min = base->pasv_min;
+ fsc->pasv_max = base->pasv_max;
+ }
+
+ if (fsc->data_block_size == FTP_UNSPEC) {
+ fsc->data_block_size = base->data_block_size;
+ }
+
+ if (fsc->limit_peruser == FTP_UNSPEC)
+ fsc->limit_peruser = base->limit_peruser;
+ if (fsc->limit_perip == FTP_UNSPEC)
+ fsc->limit_perip = base->limit_perip;
+ if (fsc->limit_perserver == FTP_UNSPEC)
+ fsc->limit_perserver = base->limit_perserver;
+
+ fsc->limitdbfile = ap_server_root_relative(p, base->limitdbfile);
+
+ if (!fsc->banner_message) {
+ fsc->banner_message = base->banner_message;
+ fsc->banner_message_isfile = base->banner_message_isfile;
+ }
+ if (!fsc->exit_message) {
+ fsc->exit_message = base->exit_message;
+ fsc->exit_message_isfile = base->exit_message_isfile;
+ }
+
+ return fsc;
+}
+
+
+static void *create_ftp_dir_config(apr_pool_t *p, char *dir)
+{
+ ftp_dir_config *conf = apr_pcalloc(p, sizeof(*conf));
+
+ return conf;
+}
+
+void *merge_ftp_dir_config(apr_pool_t *p, void *basev, void *addv)
+{
+ ftp_dir_config *base = (ftp_dir_config *)basev;
+ ftp_dir_config *add = (ftp_dir_config *)addv;
+ ftp_dir_config *conf = apr_palloc(p, sizeof(*conf));
+
+ /* We default to the add config, so any directive not
+ * handled here won't be inherited through directories.
+ */
+ memcpy (conf, add, sizeof(*conf));
+
+ /* TODO:
+ * if we adopt FTPUMask as a per-dir, we must blend it
+ * with FTPReadmeMessage inheritence here.
+ * since today we don't inherit FTPReadmeMessage this
+ * merger is a no-op.
+ */
+
+ return conf;
+}
+
+int ftp_have_ssl(void)
+{
+ return (ftp_ssl_input_filter_handle
+ && ftp_ssl_output_filter_handle);
+}
+
+static void ftp_insert_filter(request_rec *r)
+{
+ ftp_connection *fc = ftp_get_module_config(r->request_config);
+
+ /* XXX: mod_negotiation does not run the create_request hook, so
+ * we must dig deeper looking for our request_config. Look
+ * at this later.
+ */
+ if (!fc) {
+ if (r->main) {
+ fc = ftp_get_module_config(r->main->request_config);
+ }
+
+ if (!fc) {
+ return;
+ }
+ }
+
+ if (fc->datasock && (fc->filter_mask & FTP_NEED_DATA_OUT)) {
+ ap_add_output_filter_handle(ftp_data_out_filter_handle, fc, r,
+ r->connection);
+ }
+ if (fc->filter_mask & FTP_NEED_BYTERANGE) {
+ ap_add_output_filter_handle(ftp_byterange_filter_handle, NULL,
+ r, r->connection);
+ }
+ if (fc->filter_mask & FTP_NEED_CRLF) {
+ /* CRLF filter has a single int context, "did we last have a CR?" */
+ ap_add_output_filter_handle(ftp_crlf_filter_handle,
+ apr_pcalloc(r->pool, sizeof(int*)),
+ r, r->connection);
+ }
+}
+
+/*
+ * The create request hook. Used when Apache does an internal redirect
+ */
+static int ftp_create_request(request_rec *r)
+{
+ if (r->main) {
+ ftp_set_module_config(r->request_config,
+ ftp_get_module_config(r->main->request_config));
+ }
+
+ return OK;
+}
+
+/* This is something of a hack. The idea is that we want to be able to look
+ * at a string with a '*' wildcard, and a string that matches the pattern,
+ * and determine which part of the second string matches the wildcard. This
+ * allows us to extract that information, which is later used to setup
+ * the require line properly if we are ensuring that users can not switch
+ * to other user's home directories.
+ */
+static char *find_dir(apr_pool_t *p, const char *wildcard,
+ const char *curr_file)
+{
+ char *dirname;
+ size_t i;
+
+ for (i = 0; i < strlen(wildcard); i++) {
+ if (wildcard[i] == curr_file[i]) {
+ /* So far the strings are equal */
+ continue;
+ }
+ if (wildcard[i] == '*' && wildcard[i + 1] == '/') {
+ /* Now we have the portion we want to match against. Find the
+ * corresponding string in the current file.
+ */
+ size_t j = i;
+ while (curr_file[j] && curr_file[j] != '/') {
+ j++;
+ }
+ dirname = apr_pstrmemdup(p, curr_file + i, j - i);
+ return dirname;
+ }
+ return NULL;
+ }
+ return NULL;
+}
+
+/*
+ * The check_user_id hook. This is a hack that allows us to ensure that only
+ * the owner of the directory can get access to the current directory. If the
+ * user has configured:
+ * Require dir-name
+ * for the current directory, then we require that the logged in user match
+ * the name of the directory.
+ */
+static int ftp_check_user_id(request_rec *r)
+{
+ apr_array_header_t *newrequire;
+ const apr_array_header_t *reqs_arr = ap_requires(r);
+ require_line *reqs;
+ const char *t, *w;
+ core_dir_config *conf;
+ int x;
+
+ conf = (core_dir_config *)ap_get_module_config(r->per_dir_config,
+ &core_module);
+
+ if (!reqs_arr) {
+ return DECLINED;
+ }
+
+ newrequire = apr_array_make(r->pool, 2, sizeof(require_line));
+
+ reqs = (require_line *)reqs_arr->elts;
+ for (x = 0; x < reqs_arr->nelts; x++) {
+ require_line *rl;
+
+ rl = (require_line *)apr_array_push(newrequire);
+ rl->method_mask = reqs[x].method_mask;
+
+ t = reqs[x].requirement;
+ w = ap_getword_white(r->pool, &t);
+ if (!strcmp(w, "user")) {
+ rl->requirement = apr_pstrdup(r->pool, w);
+ while (strcmp(w = ap_getword_white(r->pool, &t), "")) {
+ if (!strcmp(w, "dir-name")) {
+ char *dirname = find_dir(r->pool, conf->d, r->filename);
+ rl->requirement = apr_pstrcat(r->pool, rl->requirement,
+ " ", dirname, NULL);
+ }
+ else {
+ rl->requirement = apr_pstrcat(r->pool, rl->requirement,
+ " ", w, NULL);
+ }
+ }
+ }
+ else {
+ rl->requirement = apr_pstrdup(r->pool, reqs[x].requirement);
+ }
+ }
+ conf->ap_requires = newrequire;
+
+ return DECLINED;
+}
+
+/*
+ * Handle FTP module directives
+ */
+
+/*
+ * Generic function used for setting values in the server config.
+ * Adapted from ap_set_int_slot, since it only works with per directory
+ * configurations.
+ */
+static const char *ftp_set_int_slot(cmd_parms *cmd, void *dummy,
+ const char *arg)
+{
+ char *endptr;
+ char *error_str = NULL;
+ int offset = (int) (long) cmd->info;
+ ftp_server_config *fsc =
+ ftp_get_module_config(cmd->server->module_config);
+
+ *(int *) ((char*)fsc + offset) = strtol(arg, &endptr, 10);
+
+ if ((*arg == '\0') || (*endptr != '\0')) {
+ error_str = apr_psprintf(cmd->pool,
+ "Invalid value for directive %s, expected integer",
+ cmd->directive->directive);
+ }
+
+ return error_str;
+}
+
+
+static const char *ftp_enable(cmd_parms *cmd, void *dummy, int arg)
+{
+ ftp_server_config *fsc =
+ ftp_get_module_config(cmd->server->module_config);
+
+ fsc->enabled = arg;
+
+ return NULL;
+}
+
+static const char *ftp_umask(cmd_parms *cmd, void *dummy, const char *arg)
+{
+#ifdef HAVE_FCHMOD
+ int umask, mode;
+ char *endp;
+ char *error_str = NULL;
+
+ ftp_server_config *fsc =
+ ftp_get_module_config(cmd->server->module_config);
+
+ umask = strtol(arg, &endp, 8);
+ mode = umask & 0666;
+
+ if ((*arg == '\0') || (*endp != '\0')) {
+ error_str = apr_psprintf(cmd->pool,
+ "%s is not valid for %s", arg,
+ cmd->directive->directive);
+ }
+ else {
+ fsc->fileperms = ftp_unix_mode2perms(mode);
+ }
+ return error_str;
+#else
+ return "The FTPUmask directive is not supported on this platform";
+#endif
+}
+
+static const char *ftp_implicit_ssl(cmd_parms *cmd, void *dummy, int arg)
+{
+ ftp_server_config *fsc =
+ ftp_get_module_config(cmd->server->module_config);
+
+ if (!ftp_have_ssl()) {
+ return "No SSL module found, cannot enable implicit SSL";
+ }
+
+ fsc->implicit_ssl = arg;
+ return NULL;
+}
+
+static const char *ftp_set_jail(cmd_parms *cmd, void *dummy, int arg)
+{
+ ftp_server_config *fsc =
+ ftp_get_module_config(cmd->server->module_config);
+
+ fsc->jailuser = arg;
+ return NULL;
+}
+
+static const char *ftp_options(cmd_parms *cmd, void *dummy,
+ const char *raw)
+{
+ ftp_server_config *fsc =
+ ftp_get_module_config(cmd->server->module_config);
+
+ while (raw[0]) {
+ int opt_mask = 0;
+ char *op = ap_getword_conf(cmd->pool, &raw);
+
+ if (!strcasecmp(op, "RequireSSL")) {
+ opt_mask = FTP_OPT_REQUIRESSL;
+ }
+ else if (!strcasecmp(op, "CheckMaxClients")) {
+ opt_mask = FTP_OPT_CHECKMAXCLIENTS;
+ }
+ else if (!strcasecmp(op, "RemoveUserGroup")) {
+ opt_mask = FTP_OPT_REMOVEUSERGROUP;
+ }
+ else if (!strcasecmp(op, "NLSTShowDirs")) {
+ opt_mask = FTP_OPT_NLSTSHOWDIRS;
+ }
+ else if (!strcasecmp(op, "NLSTIsLIST")) {
+ opt_mask = FTP_OPT_NLSTISLIST;
+ }
+ else if (!strcasecmp(op, "LISTIsNLST")) {
+ opt_mask = FTP_OPT_LISTISNLST;
+ }
+ else if (!strcasecmp(op, "CreateHomeDirs")) {
+ opt_mask = FTP_OPT_CREATEHOMEDIRS;
+ }
+ else if (!strcasecmp(op, "ShowUnAuthorizedFiles")) {
+ opt_mask = FTP_OPT_SHOWUNAUTH;
+ }
+ else if (!strcasecmp(op, "AllowProxyPORT")) {
+ opt_mask = FTP_OPT_ALLOWPROXYPORT;
+ }
+ else if (!strcasecmp(op, "AllowProxyPASV")) {
+ opt_mask = FTP_OPT_ALLOWPROXYPASV;
+ }
+ else {
+ return apr_pstrcat(cmd->pool, "Illegal FTPOption ", op, NULL);
+ }
+ fsc->options |= opt_mask;
+ }
+ if ((fsc->options & FTP_OPT_LISTISNLST) && (fsc->options & FTP_OPT_NLSTISLIST)) {
+ return "LISTISNLST and NLSTISLIST are mutually exclusive options";
+ }
+ return NULL;
+}
+
+static const char *ftp_set_pasv_addr(cmd_parms *cmd, void *dummy,
+ const char *addr)
+{
+ apr_uint32_t ipaddr;
+ ftp_server_config *fsc =
+ ftp_get_module_config(cmd->server->module_config);
+
+ if ((ftp_inet_pton(AF_INET, addr, &ipaddr)) != 1) {
+ return apr_pstrcat(cmd->pool, "Invalid IP address for ",
+ cmd->directive->directive, " (", addr, ")", NULL);
+ }
+
+ fsc->pasv_addr = apr_pstrdup(cmd->pool, addr);
+
+ return NULL;
+}
+
+static const char *ftp_set_pasv_bindaddr(cmd_parms *cmd, void *dummy,
+ const char *addr)
+{
+ apr_uint32_t ipaddr;
+ ftp_server_config *fsc =
+ ftp_get_module_config(cmd->server->module_config);
+
+ if ((ftp_inet_pton(AF_INET, addr, &ipaddr)) != 1) {
+ return apr_pstrcat(cmd->pool, "Invalid IP address for ",
+ cmd->directive->directive, " (", addr, ")", NULL);
+ }
+
+ fsc->pasv_bindaddr = apr_pstrdup(cmd->pool, addr);
+
+ return NULL;
+}
+
+static const char *ftp_set_message_generic(cmd_parms *cmd, const char *arg,
+ const char **dest, int *file_flag)
+{
+ apr_finfo_t finfo;
+ apr_status_t rv;
+
+ if (*dest != NULL) {
+ ap_log_error(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, 0,
+ cmd->server, "Ignoring duplicate message file: %s", arg);
+ return NULL;
+ }
+
+ if (!strncmp(arg, "file:", 5)) {
+ rv = apr_stat(&finfo,
+ ap_server_root_relative(cmd->temp_pool, arg + 5),
+ APR_FINFO_TYPE, cmd->temp_pool);
+ if (rv != APR_SUCCESS || finfo.filetype != APR_REG) {
+ return apr_pstrcat(cmd->pool, "Invalid message file: ",
+ arg + 5, NULL);
+ }
+
+ *(file_flag) = 1;
+ *(dest) = ap_server_root_relative(cmd->pool, arg + 5);
+ }
+ else {
+ *(dest) = apr_pstrdup(cmd->pool, arg);
+ }
+
+ return NULL;
+}
+
+static const char *ftp_set_banner_message(cmd_parms *cmd, void *dummy,
+ const char *arg)
+{
+ ftp_server_config *fsc =
+ ftp_get_module_config(cmd->server->module_config);
+
+ return ftp_set_message_generic(cmd, arg, &fsc->banner_message,
+ &fsc->banner_message_isfile);
+}
+
+static const char *ftp_set_exit_message(cmd_parms *cmd, void *dummy,
+ const char *arg)
+{
+ ftp_server_config *fsc =
+ ftp_get_module_config(cmd->server->module_config);
+
+ return ftp_set_message_generic(cmd, arg, &fsc->exit_message,
+ &fsc->exit_message_isfile);
+}
+
+static const char *ftp_set_readme_message(cmd_parms *cmd, void *dconf,
+ const char *arg)
+{
+ ftp_dir_config *d = dconf;
+
+ d->path = apr_pstrdup(cmd->pool, cmd->path);
+ return ftp_set_message_generic(cmd, arg, &d->readme, &d->readme_isfile);
+}
+
+static const char *ftp_set_pasv_range(cmd_parms *cmd, void *dummy,
+ const char *min, const char *max)
+{
+ char *error_str = NULL;
+
+ ftp_server_config *fsc =
+ ftp_get_module_config(cmd->server->module_config);
+
+ /* XXX: better error handling */
+ fsc->pasv_min = (apr_port_t) atoi(min);
+ fsc->pasv_max = (apr_port_t) atoi(max);
+
+ if (fsc->pasv_min > fsc->pasv_max) {
+ error_str = apr_psprintf(cmd->pool,
+ "Invalid range for %s (%s > %s)",
+ cmd->directive->directive, min, max);
+ }
+
+ return error_str;
+}
+
+static const char *ftp_set_active_ports(cmd_parms *cmd, void *dummy,
+ const char *min, const char *max)
+{
+ char *error_str = NULL;
+
+ ftp_server_config *fsc =
+ ftp_get_module_config(cmd->server->module_config);
+
+ /* XXX: better error handling */
+ fsc->active_min = atoi(min);
+ if (!max) {
+ fsc->active_max = fsc->active_min;
+ }
+ else {
+ fsc->active_max = atoi(max);
+ }
+
+ if (fsc->active_min > fsc->active_max) {
+ error_str = apr_psprintf(cmd->pool,
+ "Invalid range for %s (%s > %s)",
+ cmd->directive->directive, min, max);
+ }
+
+ return error_str;
+}
+
+static const char *ftp_set_homedir(cmd_parms *cmd, void *dummy,
+ const char *dir)
+{
+ ftp_server_config *fsc =
+ ftp_get_module_config(cmd->server->module_config);
+
+ if (*dir != '/') {
+ return apr_pstrcat(cmd->pool, "Path for ", cmd->directive->directive,
+ " must be absolute (", dir, ")", NULL);
+ }
+
+ fsc->homedir = apr_pstrdup(cmd->pool, dir);
+
+ return NULL;
+}
+
+static const char *ftp_set_docrootenv(cmd_parms *cmd, void *dummy,
+ const char *var)
+{
+ ftp_server_config *fsc =
+ ftp_get_module_config(cmd->server->module_config);
+
+ fsc->docrootenv = apr_pstrdup(cmd->pool, var);
+
+ return NULL;
+}
+
+static const char *ftp_set_limit_peruser(cmd_parms *cmd, void *dummy,
+ const char *limit)
+{
+ char *error_str = NULL;
+ ftp_server_config *fsc;
+
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ fsc = ftp_get_module_config(cmd->server->module_config);
+
+ /* XXX: better error handling */
+ fsc->limit_peruser = (apr_port_t) atoi(limit);
+
+ if (fsc->limit_peruser < 0) {
+ error_str = apr_psprintf(cmd->pool,
+ "%s value must be 0 or greater (%d)",
+ cmd->directive->directive, limit);
+ }
+
+ return error_str;
+}
+
+static const char *ftp_set_limit_perip(cmd_parms *cmd, void *dummy,
+ const char *limit)
+{
+ char *error_str = NULL;
+ ftp_server_config *fsc;
+
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ fsc = ftp_get_module_config(cmd->server->module_config);
+
+ /* XXX: better error handling */
+ fsc->limit_perip = (apr_port_t) atoi(limit);
+
+ if (fsc->limit_perip < 0) {
+ error_str = apr_psprintf(cmd->pool,
+ "%s value must be 0 or greater (%d)",
+ cmd->directive->directive, limit);
+ }
+
+ return error_str;
+}
+
+static const char *ftp_set_limit_perserver(cmd_parms *cmd, void *dummy,
+ const char *limit)
+{
+ char *error_str = NULL;
+ ftp_server_config *fsc;
+
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ fsc = ftp_get_module_config(cmd->server->module_config);
+
+ /* XXX: better error handling */
+ fsc->limit_perserver = (apr_port_t) atoi(limit);
+
+ if (fsc->limit_perserver < 0) {
+ error_str = apr_psprintf(cmd->pool,
+ "%s value must be 0 or greater (%d)",
+ cmd->directive->directive, limit);
+ }
+
+ return error_str;
+}
+
+static const char *ftp_set_dbfile(cmd_parms *cmd, void *dummy,
+ const char *dbfile)
+{
+ ftp_server_config *fsc;
+
+ const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+ if (err != NULL) {
+ return err;
+ }
+
+ fsc = ftp_get_module_config(cmd->server->module_config);
+
+ fsc->limitdbfile = ap_server_root_relative(cmd->pool, dbfile);
+ if (!fsc->limitdbfile) {
+ return apr_pstrcat(cmd->pool, "Invalid FTPLimitDBFile value: ",
+ dbfile, NULL);
+ }
+
+ return NULL;
+}
+
+/*
+ * Setup command table
+ */
+static const command_rec ftp_cmds[] = {
+ AP_INIT_FLAG("FTP", ftp_enable, NULL, RSRC_CONF,
+ "Run an FTP server on this host"),
+ AP_INIT_TAKE1("FTPUmask", ftp_umask, NULL, RSRC_CONF,
+ "Set the umask for created files"),
+ AP_INIT_TAKE1("FTPTimeoutLogin", ftp_set_int_slot,
+ (void *)APR_XtOffsetOf(ftp_server_config,
+ timeout_login), RSRC_CONF,
+ "Idle time allowed when logging in"),
+ AP_INIT_TAKE1("FTPTimeoutIdle", ftp_set_int_slot,
+ (void *)APR_XtOffsetOf(ftp_server_config,
+ timeout_idle), RSRC_CONF,
+ "Idle time allowed during a FTP session"),
+ AP_INIT_TAKE1("FTPTimeoutData", ftp_set_int_slot,
+ (void *)APR_XtOffsetOf(ftp_server_config,
+ timeout_data), RSRC_CONF,
+ "Idle time allowed during a data transfer"),
+ AP_INIT_TAKE1("FTPMaxLoginAttempts", ftp_set_int_slot,
+ (void *)APR_XtOffsetOf(ftp_server_config,
+ max_login_attempts), RSRC_CONF,
+ "Maximum number of login attempts"),
+ AP_INIT_FLAG("FTPImplicitSSL", ftp_implicit_ssl, NULL, RSRC_CONF,
+ "Use SSL implicitly."),
+ AP_INIT_RAW_ARGS("FTPOptions", ftp_options, NULL, RSRC_CONF,
+ "Set options for this server"),
+ AP_INIT_TAKE1("FTPPASVaddr", ftp_set_pasv_addr, NULL, RSRC_CONF,
+ "Set the allowed PASV server IP address for the data "
+ "channel"),
+ AP_INIT_TAKE1("FTPPASVbindaddr", ftp_set_pasv_bindaddr, NULL, RSRC_CONF,
+ "Set and bind the allowed PASV server IP "
+ "address for the data channel"),
+ AP_INIT_TAKE2("FTPPASVrange", ftp_set_pasv_range, NULL, RSRC_CONF,
+ "Set the allowed PASV port range"),
+ AP_INIT_TAKE1("FTPBannerMessage", ftp_set_banner_message, NULL, RSRC_CONF,
+ "Set initial login message"),
+ AP_INIT_TAKE1("FTPExitMessage", ftp_set_exit_message, NULL, RSRC_CONF,
+ "Set logout message"),
+ AP_INIT_TAKE1("FTPHomeDir", ftp_set_homedir, NULL, RSRC_CONF,
+ "Set the path to directory containing user's "
+ "home directories"),
+ AP_INIT_TAKE1("FTPDocRootEnv", ftp_set_docrootenv, NULL, RSRC_CONF,
+ "Set the DocumentRoot based on the given environment "
+ "variable, such as a per-user LDAP property"),
+ AP_INIT_FLAG("FTPJailUser", ftp_set_jail, NULL, RSRC_CONF,
+ "Users are not allowed to leave their home directories"),
+ AP_INIT_TAKE12("FTPActiveRange", ftp_set_active_ports, NULL, RSRC_CONF,
+ "Ports the server will use for connecting to the client."),
+ AP_INIT_TAKE1("FTPReadmeMessage", ftp_set_readme_message, NULL, OR_ALL,
+ "Set per-directory Readme file"),
+ AP_INIT_TAKE1("FTPLimitLoginUser", ftp_set_limit_peruser, NULL, RSRC_CONF,
+ "Set the maximum number of concurrent logins per user"),
+ AP_INIT_TAKE1("FTPLimitLoginIP", ftp_set_limit_perip, NULL, RSRC_CONF,
+ "Set the maximum number of concurrent logins per IP address"),
+ AP_INIT_TAKE1("FTPLimitLoginServer", ftp_set_limit_perserver, NULL, RSRC_CONF,
+ "Set the maximum number of concurrent logins per server"),
+ AP_INIT_TAKE1("FTPLimitDBFile", ftp_set_dbfile, NULL, RSRC_CONF,
+ "Set the location for the Login Limit DB file"),
+ AP_INIT_TAKE1("FTPDataBlockSize", ftp_set_int_slot,
+ (void *)APR_XtOffsetOf(ftp_server_config,
+ data_block_size), RSRC_CONF,
+ "Block size in bytes to use during data transfers"),
+ {NULL}
+};
+
+static void register_hooks(apr_pool_t *p)
+{
+ static const char *const pred[] = { "mod_log_config.c",
+ NULL };
+
+ static const char *const post[] = { NULL };
+
+ ap_hook_pre_config(ftp_pre_config, pred, NULL, APR_HOOK_MIDDLE);
+ ap_hook_post_config(ftp_post_config, post, NULL, APR_HOOK_MIDDLE);
+ ap_hook_child_init(ftp_child_init, NULL, NULL, APR_HOOK_MIDDLE);
+ ap_hook_process_connection(ftp_process_connection, NULL, NULL,
+ APR_HOOK_MIDDLE);
+ ap_hook_create_request(ftp_create_request,NULL,NULL,APR_HOOK_MIDDLE);
+ ap_hook_check_user_id(ftp_check_user_id, NULL, NULL, APR_HOOK_REALLY_FIRST);
+ ap_hook_insert_filter(ftp_insert_filter,NULL,NULL,APR_HOOK_MIDDLE);
+
+ /* FTP filters */
+ ftp_input_filter_handle = ap_register_input_filter("FTP_PROTOCOL",
+ ftp_protocol_filter,
+ NULL,
+ AP_FTYPE_PROTOCOL);
+ ftp_crlf_filter_handle = ap_register_output_filter("FTP_CRLF",
+ ftp_crlf_filter,
+ NULL,
+ AP_FTYPE_RESOURCE - 1);
+ ftp_data_out_filter_handle = ap_register_output_filter("FTP_DATA_OUT",
+ ftp_data_out_filter,
+ NULL,
+ AP_FTYPE_NETWORK - 2);
+
+ /* Register all core command handlers */
+ ftp_register_core_cmds(p);
+}
+
+FTP_DECLARE_DATA module ftp_module = {
+ STANDARD20_MODULE_STUFF,
+ create_ftp_dir_config, /* create per-directory config structure */
+ merge_ftp_dir_config, /* merge per-directory config structures */
+ create_ftp_server_config, /* create per-server config structure */
+ merge_ftp_server_config, /* merge per-server config structures */
+ ftp_cmds, /* command apr_table_t */
+ register_hooks /* register hooks */
+};
Propchange: incubator/mod_ftp/trunk/src/mod_ftp.c
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: incubator/mod_ftp/trunk/src/mod_ftp.c
------------------------------------------------------------------------------
svn:executable =