mod_ftp  0.9.6
About: mod_ftp is a FTP (File Transfer) Protocol module to serve httpd content over the FTP protocol. It allows you to combine Apache's powerful authentication, SSL encryption, dynamic content and filtering capabilities with the venerable FTP protocol. Beta version.
  Fossies Dox: mod_ftp-0.9.6-beta.tar.gz  ("inofficial" and yet experimental doxygen-generated source code documentation)  

 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros
ftp_protocol.c
Go to the documentation of this file.
1 /* Licensed to the Apache Software Foundation (ASF) under one or more
2  * contributor license agreements. See the NOTICE file distributed with
3  * this work for additional information regarding copyright ownership.
4  * The ASF licenses this file to You under the Apache License, Version 2.0
5  * (the "License"); you may not use this file except in compliance with
6  * the License. You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 /*
18  * Original Copyright (c) 2005 Covalent Technologies
19  *
20  * FTP Protocol module for Apache 2.0
21  */
22 
23 #define CORE_PRIVATE
24 #include "mod_ftp.h"
25 #include "ftp_internal.h"
26 
27 /* Min # of bytes to allocate when reading a request line */
28 #define MIN_LINE_ALLOC 512
29 
30 
31 /*
32  * ftp_read_line reads ahead one line from the control channel.
33  *
34  */
35 static apr_status_t ftp_read_line(char **result, apr_size_t *bytes_read,
36  apr_pool_t *pool,
37  apr_bucket_brigade *bb,
38  ap_filter_t *input_filters,
39  int block, ftp_connection *fc)
40 {
41  char *last_char = NULL;
42  apr_status_t rv;
43  apr_bucket_pool *pb;
44  apr_bucket *pe;
45  apr_bucket *e;
46  char *pbuf;
47  char *pos;
48 
49  /*
50  * We manage a leading (intially empty) pool bucket that we will use to
51  * concatinate the line over (possibly) multiple non-blocking invocations
52  * of ftp_read_line()
53  */
54 
55  if (APR_BRIGADE_EMPTY(bb)) {
56  pe = apr_bucket_pool_create(apr_palloc(pool, MIN_LINE_ALLOC), 0,
57  pool, input_filters->c->bucket_alloc);
58  pb = pe->data;
59  pb->heap.alloc_len = MIN_LINE_ALLOC;
60  pbuf = (char *) pb->base;
61 #ifdef FTP_TRACE
62  ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0,
63  fc->orig_server, "FTP frl: empty bb");
64 #endif
65  }
66  else {
67  pe = APR_BRIGADE_FIRST(bb);
68  pb = pe->data;
69  if (APR_BUCKET_IS_POOL(pe) && pb->pool)
70  pbuf = (char *) pb->base;
71  else if (APR_BUCKET_IS_HEAP(pe) || APR_BUCKET_IS_POOL(pe))
72  pbuf = pb->heap.base;
73  else
74  return APR_EGENERAL;
75  /* We hope to keep things simple! */
76  if (pe->start != 0)
77  return APR_EGENERAL;
78  /* Remove pe so we have a clean brigade for the loop below */
79  APR_BUCKET_REMOVE(pe);
80 #ifdef FTP_TRACE
81  ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0,
82  fc->orig_server, "FTP frl: using previous bb");
83 #endif
84  }
85 
86  *result = NULL;
87 
88  do {
89 
90  for (;;) {
91 
92  apr_brigade_cleanup(bb);
93  rv = ap_get_brigade(input_filters, bb,
94  AP_MODE_GETLINE, block, 0);
95 
96  if (rv != APR_SUCCESS) {
97  APR_BRIGADE_INSERT_HEAD(bb, pe);
98  return rv;
99  }
100 
101  if (APR_BRIGADE_EMPTY(bb)) {
102  APR_BRIGADE_INSERT_HEAD(bb, pe);
103 #ifdef FTP_TRACE
104  ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0,
105  fc->orig_server, "FTP frl: got empty brigade");
106 #endif
107  return (block == APR_BLOCK_READ) ? APR_EGENERAL
108  : APR_EAGAIN;
109  }
110 
111  while ((e = APR_BRIGADE_FIRST(bb)) != APR_BRIGADE_SENTINEL(bb)) {
112  apr_bucket *e_next;
113  const char *str;
114  apr_size_t len;
115  int mark;
116 
117  /* If we see an EOS, don't bother doing anything more. */
118  if (APR_BUCKET_IS_EOS(e)) {
119  break;
120  }
121 
122  rv = apr_bucket_read(e, &str, &len, block);
123 
124  /*
125  * Upon discovering that the next bucket is a socket,
126  * and that socket is at the OOB mark, we will dump
127  * our current buffer and continue to read the priority
128  * command beyond the OOB mark.
129  *
130  * XXX: Note that some bytes of the IAC IP IAC DM sequence
131  * may fall back in band, or the leading 'A' of "ABOR" may
132  * fall out of band, due to poor understanding of telnet
133  * by many client authors. We need to take this a step
134  * further and add logic to correct these cases.
135  */
136  e_next = APR_BUCKET_NEXT(e);
137  if (rv == APR_SUCCESS && APR_BUCKET_IS_SOCKET(e_next)) {
138  apr_socket_t *sock = e_next->data;
139  rv = apr_socket_atmark(sock, &mark);
140  ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0,
141  fc->orig_server, "FTP frl: atmark: %x %d",
142  (int) rv, mark);
143  if (rv == APR_SUCCESS && mark) {
144  pe->length = 0;
145  ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0,
146  fc->orig_server, "FTP frl: Saw OOB");
147  continue;
148  }
149  }
150 
151  if (rv != APR_SUCCESS) {
152  APR_BRIGADE_INSERT_HEAD(bb, pe);
153  return rv;
154  }
155 
156  if (len == 0) {
157  /*
158  * no use attempting a zero-byte alloc (hurts when using
159  * --with-efence --enable-pool-debug) or doing any of the
160  * other logic either
161  */
162  apr_bucket_delete(e);
163  continue;
164  }
165 
166  /* Exceeding limits? If so, we'll die. */
167  if (pe->length + len > DEFAULT_LIMIT_REQUEST_LINE + 2) {
168  APR_BRIGADE_INSERT_HEAD(bb, pe);
169  return APR_ENOSPC;
170  }
171 
172  /* If exceeding our limit, increase the buffer size */
173  if (pe->length + len > pb->heap.alloc_len) {
174  apr_size_t new_size = pb->heap.alloc_len * 2;
175  char *new_buffer;
176 
177  if (pe->length + len > new_size) {
178  new_size = (pe->length + len) * 2;
179  }
180 
181  if (new_size > DEFAULT_LIMIT_REQUEST_LINE + 2) {
182  new_size = DEFAULT_LIMIT_REQUEST_LINE + 2;
183  }
184 
185  if (pb->pool) {
186  new_buffer = apr_palloc(pb->pool, new_size);
187  memcpy(new_buffer, pb->base, pe->length);
188  pb->base = new_buffer;
189  pbuf = (char *) pb->base;
190  }
191  else {
192  new_buffer = malloc(new_size);
193  if (!new_buffer)
194  return APR_ENOSPC;
195  memcpy(new_buffer, pb->heap.base, pe->length);
196  free(pb->heap.base);
197  pb->heap.base = new_buffer;
198  pbuf = (char *) pb->heap.base;
199  }
200  pb->heap.alloc_len = new_size;
201  }
202 
203  /* Just copy the rest of the data to the end of the buffer. */
204  pos = pbuf + pe->length;
205  memcpy(pos, str, len);
206  pe->length += len;
207 
208  last_char = pos + len - 1;
209 
210  /* Destroy the now-consumed bucket */
211  apr_bucket_delete(e);
212  }
213 
214  /* If we got a full line of input, stop reading */
215  if ((last_char > pbuf) && (*(last_char - 1) == APR_ASCII_CR)
216  && (*last_char == APR_ASCII_LF))
217  {
218  char *ssrc, *sdst;
219 
220  /* Since we want to remove the CRLF from the line, we'll go
221  * ahead and NULL term the string;
222  */
223  *(--last_char) = '\0';
224 
225  /*
226  * We ignore claims in draft-ietf-ftpext-utf-8-option-00 which
227  * suggested RFC2640 is wrong with respect to RFC1123. RFC1123
228  * is quite clear in stating;
229  *
230  * The Telnet end-of-line sequence CR LF MUST be used to send
231  * Telnet data that is not terminal-to-computer (e.g., for Server
232  * Telnet sending output, or the Telnet protocol incorporated
233  * another application protocol)
234  *
235  * ergo CR NUL is not a valid command completion sequence, as
236  * FTP is not a terminal protocol, but an application protocol.
237  *
238  * Collapse <CR><NULL> to <CR> and <IAC><IAC> to a single 0xFF
239  * as documented in RFC854 and clarified by RFC2640 and RFC3659,
240  * discarding all extranious <IAC> sequences. As negotiation
241  * is explicitly not allowed, we make no effort to catch such.
242  */
243  for (ssrc = sdst = pbuf; ssrc < last_char; ++ssrc, ++sdst) {
244  if ((ssrc[0] == '\xFF')
245  || (ssrc[0] == APR_ASCII_CR && ssrc[1] == 0)) {
246  if (ssrc[0] == '\xFF' && ssrc[1] != '\xFF')
247  --sdst;
248  break;
249  }
250  }
251  /* Jumping from the parse-only loop above, into this parse w/copy
252  */
253  for (ssrc += 2, ++sdst; ssrc < last_char; ++ssrc) {
254  *(sdst++) = *ssrc;
255  if ((ssrc[0] == '\xFF')
256  || (ssrc[0] == APR_ASCII_CR && ssrc[1] == 0)) {
257  ++ssrc;
258  if (ssrc[0] == '\xFF' && ssrc[1] != '\xFF')
259  --sdst;
260  }
261  }
262 
263  /*
264  * Return the result string, and the actual bytes read from
265  * the network (before we truncated characters)
266  *
267  * We may have moved from a pool to another pool, or to a heap
268  * bucket. Reallocate from the current pool in these cases.
269  */
270  if (pb->pool &&pb->pool == pool) {
271  *result = pbuf;
272  }
273  else {
274  *result = apr_palloc(pool, last_char - pbuf + 1);
275  memcpy(*result, pbuf, last_char - pbuf + 1);
276  }
277  *bytes_read = pe->length;
278 
279  /*
280  * Finally destroy the working bucket - if it is heap the
281  * heap data will also be free()'d.
282  */
283  apr_bucket_destroy(pe);
284 #ifdef FTP_TRACE
285  ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0,
286  fc->orig_server, "FTP frl: got full line");
287 #endif
288  return APR_SUCCESS;
289  }
290  }
291 
292  } while (pe->length <= 0);
293 #ifdef FTP_TRACE
294  ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0,
295  fc->orig_server, "FTP frl: fall through success");
296 #endif
297  return APR_SUCCESS;
298 }
299 
300 /*
301  * ftp_read_request_line: Read the request from the client, and set the
302  * correct values in the request record.
303  *
304  * Arguments: r - The request to read from.
305  * Arguments: bb - The brigade to retrieve.
306  *
307  * Returns: Returns 0 on success, 1 on error.
308  */
309 static apr_status_t ftp_read_request_line(ftp_connection *fc,
310  request_rec *r,
311  apr_bucket_brigade *bb)
312 {
313  apr_size_t bytes_read;
314  apr_status_t rv;
315  const char *ll;
316 
317  if (fc->next_request && *fc->next_request) {
318  r->the_request = apr_pstrdup(r->pool, fc->next_request);
319  bytes_read = fc->next_reqsize;
320  fc->next_request = NULL;
321  fc->next_reqsize = 0;
322 #ifdef FTP_TRACE
323  ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0,
324  r->server, "FTP frrl: using read-ahead request");
325 #endif
326  }
327  else if ((rv = ftp_read_line(&r->the_request, &bytes_read,
328  fc->connection->pool, bb, r->input_filters,
329  APR_BLOCK_READ, fc)) != APR_SUCCESS) {
330  return rv;
331  }
332 
333  r->read_length = bytes_read;
334  r->request_time = apr_time_now();
335  ll = r->the_request;
336 #ifdef FTP_TRACE
337  ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0,
338  r->server, "FTP frrl: raw command: %s", ll);
339 #endif
340  r->method = ftp_toupper(r->pool, ap_getword_white(r->pool, &ll));
341  r->method = ftp_get_cmd_alias(r->method);
342  r->method_number = ap_method_number_of(r->method);
343 
344  return APR_SUCCESS;
345 }
346 
348 {
349  apr_status_t rv;
350  const char *ll;
351  const char *method;
352 
353 #ifdef FTP_TRACE
354  ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0,
355  fc->orig_server, "FTP frar: entering");
356 #endif
357 
358  /* Review one command, only once */
359  if (fc->next_request && *fc->next_request) {
360 #ifdef FTP_TRACE
361  ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0,
362  fc->orig_server, "FTP frar: previously read-ahead");
363 #endif
364  return APR_SUCCESS;
365  }
366 
367  if (!fc->next_pool) {
368  apr_pool_create(&fc->next_pool, fc->connection->pool);
369  apr_pool_tag(fc->next_pool, "next_cmd");
370  fc->next_bb = apr_brigade_create(fc->next_pool,
371  fc->connection->bucket_alloc);
372 #ifdef FTP_TRACE
373  ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0,
374  fc->orig_server, "FTP frar: created next_pool");
375 #endif
376  }
377 
378  rv = ftp_read_line(&fc->next_request, &fc->next_reqsize,
379  fc->next_pool, fc->next_bb,
380  fc->connection->input_filters,
381  APR_NONBLOCK_READ, fc);
382  if (APR_STATUS_IS_EAGAIN(rv)) {
383 #ifdef FTP_TRACE
384  ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0,
385  fc->orig_server, "FTP frar: not ready - read again");
386 #endif
387  /* We actually like some failures here */
388  return APR_SUCCESS;
389  }
390  else if (rv != APR_SUCCESS) {
391  return rv;
392  }
393 
394  /* The entire line is read - we no longer need this brigade */
395  apr_brigade_destroy(fc->next_bb);
396  fc->next_bb = NULL;
397 
398  method = ftp_toupper(fc->next_pool, ap_getword_white(fc->next_pool, &ll));
399 
400 #ifdef FTP_TRACE
401  ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0,
402  fc->orig_server, "FTP frar: method: %s", method);
403 #endif
404  /* Can we ignore this command for a while? */
405  if (ftp_cmd_abort_data(method)) {
406  ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0,
407  fc->orig_server, "FTP frar: I see ABOR");
408  return APR_ECONNRESET;
409  }
410 
411  /* Wait to consider this command till the data transfer is complete */
412  return APR_SUCCESS;
413 }
414 
415 /*
416  * ftp_read_request: Called from the connection handler. Used to
417  * read the request and fill out the request record.
418  *
419  * Arguments: fc - The ftp_connection rec associated with this request.
420  *
421  * Returns: Returns an initialized request_rec
422  */
424 {
425  conn_rec *c = fc->connection;
426  apr_status_t rv;
427  request_rec *r;
428  apr_pool_t *p;
429  int access_status;
430  apr_bucket_brigade *tmp_bb;
431  ap_filter_t *f;
432 
433  apr_pool_create(&p, c->pool);
434  apr_pool_tag(p, "request");
435  r = apr_pcalloc(p, sizeof(request_rec));
436 
437  r->pool = p;
438  r->connection = c;
439  r->server = fc->orig_server;
440 
441  r->user = NULL;
442  r->ap_auth_type = NULL;
443 
444  r->allowed_methods = ap_make_method_list(p, 2);
445 
446  r->headers_in = apr_table_make(r->pool, 50);
447  r->subprocess_env = apr_table_make(r->pool, 50);
448  r->headers_out = apr_table_make(r->pool, 12);
449  r->err_headers_out = apr_table_make(r->pool, 5);
450  r->notes = apr_table_make(r->pool, 5);
451 
452  r->request_config = ap_create_request_config(r->pool);
453 
454  /* Must be set before we run create request hook */
455  r->proto_output_filters = c->output_filters;
456  r->output_filters = r->proto_output_filters;
457  r->proto_input_filters = c->input_filters;
458  r->input_filters = r->proto_input_filters;
459  ap_run_create_request(r);
460 
461  /*
462  * We now need to remove the NET_TIME filter to allow
463  * use to control timeouts ourselves.
464  */
465  for (f = c->input_filters; f; f = f->next) {
466  if (strcasecmp(f->frec->name, "NET_TIME") == 0) {
467  ap_remove_input_filter(f);
468  break;
469  }
470  }
471  for (f = r->input_filters; f; f = f->next) {
472  if (strcasecmp(f->frec->name, "NET_TIME") == 0) {
473  ap_remove_input_filter(f);
474  break;
475  }
476  }
477  for (f = r->proto_input_filters; f; f = f->next) {
478  if (strcasecmp(f->frec->name, "NET_TIME") == 0) {
479  ap_remove_input_filter(f);
480  break;
481  }
482  }
483 
484  r->per_dir_config = r->server->lookup_defaults;
485 
486  r->sent_bodyct = 0; /* bytect isn't for body */
487 
488  r->read_length = 0;
489  r->read_body = REQUEST_NO_BODY;
490 
491  r->status = HTTP_OK; /* Until we get a request */
492  r->the_request = NULL;
493 
494  /*
495  * Begin by presuming any module can make its own path_info assumptions,
496  * until some module interjects and changes the value.
497  */
498  r->used_path_info = AP_REQ_DEFAULT_PATH_INFO;
499 
500  r->protocol = "FTP";
501  r->method = NULL;
502 
503  /*
504  * We don't use r->uri for every request, but some modules (SSL) require
505  * r->uri to not be NULL in the post_read_request hook
506  *
507  * The canonical (http) form of "Any location" is *, e.g. the http OPTIONS *
508  * request. It's not a bad pattern to keep with module author's
509  * expectations.
510  */
511  r->uri = "*";
512 
513  /* Resume any partial request line from fc->next_bb */
514  if (fc->next_bb) {
515  tmp_bb = fc->next_bb;
516 #ifdef FTP_TRACE
517  ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0,
518  r->server, "FTP frr: using next_bb");
519 #endif
520  }
521  else {
522  tmp_bb = apr_brigade_create(r->pool, r->connection->bucket_alloc);
523 #ifdef FTP_TRACE
524  ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0,
525  r->server, "FTP frr: using tmp_bb");
526 #endif
527  }
528  if ((rv = ftp_read_request_line(fc, r, tmp_bb)) != APR_SUCCESS) {
529  apr_time_t timeout;
530  apr_bucket_brigade *bb;
531  apr_bucket *b;
532  char *err;
533  apr_size_t len;
534 
535  apr_brigade_destroy(tmp_bb);
536 
537  if (rv == APR_TIMEUP) {
538  /*
539  * Handle client timeouts here. The idle timeout for the
540  * control connection is set in the process_connection
541  * handler, if the timeout is reached, ftp_read_request_line
542  * will return with an error. Here we send the client a
543  * friendly error message, and close the connection.
544  */
545  apr_socket_timeout_get(fc->cntlsock, &timeout);
546 
547  ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, 0,
548  r->server,
549  "User %s timed out after %d seconds", fc->user,
550  (int) (timeout / APR_USEC_PER_SEC));
551 
552  err = apr_psprintf(r->pool,
553  "%d Idle Timeout (%d seconds): "
554  "Closing control connection"
556  (int) (timeout / APR_USEC_PER_SEC));
557  len = strlen(err);
558 
559  bb = apr_brigade_create(r->pool, c->bucket_alloc);
560  rv = apr_brigade_write(bb, ap_filter_flush,
561  c->output_filters, err, len);
562 
563  /* Flush the brigade down the filter chain */
564  b = apr_bucket_flush_create(c->bucket_alloc);
565  APR_BRIGADE_INSERT_TAIL(bb, b);
566  ap_pass_brigade(c->output_filters, bb);
567 
568  apr_brigade_destroy(bb);
569  }
570  else {
571  /*
572  * Remote client suddenly disconnected, don't bother sending an
573  * error since the client is long gone. Just log the error.
574  */
575  ap_log_error(APLOG_MARK, APLOG_INFO, rv,
576  r->server, "User %s disconnected", fc->user);
577  }
578  /*
579  * Return NULL to the connection handler, causing the connection to
580  * be dropped.
581  */
582  return NULL;
583  }
584 
585  apr_brigade_destroy(tmp_bb);
586  fc->next_bb = NULL;
587 
588  /*
589  * ftp_read_line returns the_request always allocated from the correct
590  * pool. If not, we have a bug. No need to clean up next_pool above in
591  * the failure case, because it is allocated from the connection (soon to
592  * be destroyed.)
593  */
594  if (fc->next_pool) {
595  apr_pool_destroy(fc->next_pool);
596  fc->next_pool = NULL;
597 #ifdef FTP_TRACE
598  ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_DEBUG, 0,
599  r->server, "FTP frr: clearing next_pool");
600 #endif
601  }
602 
603  /*
604  * PHP does initializations of important data structures in the
605  * post_read_request phase.
606  */
607  if ((access_status = ap_run_post_read_request(r))) {
608 
609  ap_log_error(APLOG_MARK, APLOG_NOERRNO | APLOG_INFO, 0,
610  r->server, "Post read request failed, dropping "
611  "client connection.");
612  return NULL;
613  }
614 
615  return r;
616 }
617 
618 /*
619  * ftp_reply: This function is used for sending command responses to the
620  * client over the control connection. All responses should
621  * be sent through this function.
622  *
623  * Arguments: out_filter - The output filter chain.
624  * p - The pool to allocate from.
625  * n - The ftp response code.
626  * l - Flag that determines if this is a long response or not. Long
627  * responses have the format %d-%s%s, short responses use a
628  * space in place of the dash.
629  *
630  * 1 - this is long response.
631  * 0 - this is a short (the last) response.
632  * fmt - Format string to be sent to the client.
633  *
634  * Returns: apr_status_t
635  */
636 apr_status_t ftp_reply(ftp_connection *fc, ap_filter_t *out_filter,
637  apr_pool_t *p, int n, int l, const char *fmt,...)
638 {
639  char buf[BUFSIZ], reply[BUFSIZ];
640  int len;
641  va_list ap;
642  apr_bucket_brigade *bb;
643  apr_bucket *b;
644 
645  va_start(ap, fmt);
646  apr_vsnprintf(buf, sizeof(buf), fmt, ap);
647  len = apr_snprintf(reply, sizeof(reply), "%d%s%s%s", n,
648  l == 1 ? "-" : " ", buf, CRLF);
649  va_end(ap);
650 
651  bb = apr_brigade_create(p, out_filter->c->bucket_alloc);
652  b = apr_bucket_pool_create(reply, len, p,
653  out_filter->c->bucket_alloc);
654  APR_BRIGADE_INSERT_HEAD(bb, b);
655  b = apr_bucket_flush_create(out_filter->c->bucket_alloc);
656  APR_BRIGADE_INSERT_TAIL(bb, b);
657 
658  fc->traffic += len;
659 
660  return ap_pass_brigade(out_filter, bb);
661 }
662 
663 /* ftp_show_file: Test if a file exists, and echo it to the control
664  * connection if it exists. This is primarily for
665  * displaying MOTD messages and .message files.
666  *
667  * Arguments: out_filter - The output filter chain.
668  * p - The pool to allocate from.
669  * code - Integer value representing the response code to use.
670  * fc - The ftp connection.
671  * file - Absolute path to the file to be displayed.
672  *
673  * Returns: apr_status_t
674  */
675 apr_status_t ftp_show_file(ap_filter_t *out_filter, apr_pool_t *p, int code,
676  ftp_connection *fc, const char *path)
677 {
678  apr_status_t rv;
679  apr_file_t *file;
680  char *pos;
681  char buf[BUFSIZ];
682  char reply[BUFSIZ];
683 
684  rv = apr_file_open(&file, path, APR_READ, APR_OS_DEFAULT, p);
685  if (rv != APR_SUCCESS) {
686  return rv;
687  }
688 
689  while (apr_file_gets(buf, sizeof(buf), file) == APR_SUCCESS) {
690  /* Strip off trailing space/cr/lf, ftp_reply does not expect them */
691  pos = buf + strlen(buf) - 1;
692  while ((pos >= buf) && apr_isspace(*pos))
693  --pos;
694  pos[1] = '\0';
695 
696  ftp_message_generate(fc, buf, reply, sizeof(reply));
697 
698  rv = ftp_reply(fc, out_filter, p, code, 1, "%s", reply);
699  if (rv != APR_SUCCESS) {
700  return rv;
701  }
702  }
703 
704  return apr_file_close(file);
705 }
706 
707 /* ftp_send_response: Send response to the client based on the status code
708  * These are currently listed in numerical order.
709  * For responses such as 503's where the error message
710  * can change, the caller is requried to fill out
711  * the respose_notes in the ftp_connection structure.
712  *
713  * Arguments: r - The request.
714  * status - FTP status code.
715  *
716  * Returns: nothing
717  */
718 void ftp_send_response(request_rec *r, int status)
719 {
720  ftp_connection *fc = ftp_get_module_config(r->connection->conn_config);
721  ftp_server_config *fsc;
722  conn_rec *c = r->connection;
723 
724  /*
725  * We are done checking subrequest values for r->status, so we can place
726  * our ftp reply code there so it can be logged
727  */
728  r->status = status;
729 
730  /*
731  * In general, status codes below 400 will be considered success.
732  * Specific exceptions are toggled, below.
733  */
734  if (status >= 400) {
735  apr_table_setn(r->subprocess_env, "ftp_transfer_ok", "0");
736  }
737 
738  switch (status) {
740  ftp_reply(fc, c->output_filters, r->pool, FTP_REPLY_SYSTEM_TYPE, 0,
741  apr_pstrcat(r->pool, "UNIX Type: L8 System: \"",
742 #if AP_MODULE_MAGIC_AT_LEAST(20060905,0)
743  ap_get_server_banner(),
744 
745 #else
746  ap_get_server_version(),
747 #endif
748  "\"", NULL));
749  break;
751  fsc = ftp_get_module_config(r->server->module_config);
752  if (fsc->exit_message) {
753  if (fsc->exit_message_isfile) {
754  ftp_show_file(c->output_filters, r->pool,
756  }
757  else {
758  char reply[BUFSIZ];
759  ftp_message_generate(fc, fsc->exit_message, reply,
760  sizeof(reply));
761  ftp_reply(fc, c->output_filters, r->pool,
762  FTP_REPLY_CONTROL_CLOSE, 1, reply);
763  }
764  }
765 
766  ftp_reply(fc, c->output_filters, r->pool, FTP_REPLY_CONTROL_CLOSE, 0,
767  "Goodbye.");
768  break;
770  ftp_reply(fc, c->output_filters, r->pool, FTP_REPLY_DATA_CLOSE, 0,
771  "Transfer complete.");
772  break;
774  ftp_reply(fc, c->output_filters, r->pool, FTP_REPLY_USER_LOGGED_IN, 0,
775  "User %s logged in", fc->user);
776  break;
778  ftp_reply(fc, c->output_filters, r->pool,
780  "Security exchange completed");
781  break;
782  case FTP_REPLY_COMPLETED:
783  ftp_reply(fc, c->output_filters, r->pool, FTP_REPLY_COMPLETED, 0,
784  "%s command successful.", r->method);
785  break;
787  ftp_reply(fc, c->output_filters, r->pool,
789  0, "Cannot open data connection.");
790  break;
792  ftp_reply(fc, c->output_filters, r->pool, FTP_REPLY_TRANSFER_ABORTED,
793  0, "Transfer aborted");
794  break;
796  ftp_reply(fc, c->output_filters, r->pool,
798  "%s: Command not recognized", r->method);
799  break;
801  ftp_reply(fc, c->output_filters, r->pool,
803  "Syntax error in '%s'", r->the_request);
804  break;
806  ftp_reply(fc, c->output_filters, r->pool, FTP_REPLY_BAD_SEQUENCE, 0,
807  "Bad sequence of commands");
808  break;
810  /*
811  * XXX This is really crufty, the given server may be configured to
812  * support only an IPv4 or IPv6 binding
813  */
814  ftp_reply(fc, c->output_filters, r->pool, FTP_REPLY_BAD_PROTOCOL, 0,
815 #if APR_HAVE_IPV6
816  "Network protocol not supported, use (1,2)");
817 #else
818  "Network protocol not supported, use (1)");
819 #endif
820  break;
822  ftp_reply(fc, c->output_filters, r->pool,
824  "Requested PROT level not supported by mechanism");
825  break;
826 
827  /* Exception cases, failure codes that fall before the 400's: */
831  apr_table_setn(r->subprocess_env, "ftp_transfer_ok", "0");
832  /*
833  * failure is flagged for status < 400 now fall through...
834  */
835 
836  default:
837  ftp_reply(fc, c->output_filters, r->pool, status, 0,
838  "%s", (fc->response_notes && *fc->response_notes)
839  ? fc->response_notes
840  : "Error (no message)");
841  }
842 
843  return;
844 }