mod_proxy_ftp.c revision 6f4dadd94220f9bdfe34fc4530527e4482319541
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny/* Licensed to the Apache Software Foundation (ASF) under one or more
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * contributor license agreements. See the NOTICE file distributed with
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * this work for additional information regarding copyright ownership.
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * The ASF licenses this file to You under the Apache License, Version 2.0
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * (the "License"); you may not use this file except in compliance with
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * the License. You may obtain a copy of the License at
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * Unless required by applicable law or agreed to in writing, software
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * distributed under the License is distributed on an "AS IS" BASIS,
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * See the License for the specific language governing permissions and
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * limitations under the License.
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny/* FTP routines for Apache proxy */
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny/* Automatic timestamping (Last-Modified header) based on MDTM is used if:
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * 1) the FTP server supports the MDTM command and
204cfc89a076fd32bf34f2abb3f809304aaa88abSimo Sorce * 2) HAVE_TIMEGM (preferred) or HAVE_GMTOFF is available at compile time
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * Decodes a '%' escaped string, and returns the number of characters
9822d4d468ec74e4e173f5adf0db12d02974cd18Sumit Bosestatic int decodeenc(char *x)
9822d4d468ec74e4e173f5adf0db12d02974cd18Sumit Bose if (x[0] == '\0')
a6cca9c284724fafd670a3163812f248ba53ad97Jakub Hrozek return 0; /* special case for no characters */
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny for (i = 0, j = 0; x[i] != '\0'; i++, j++) {
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny /* decode it if not already done */
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny if (ch == '%' && apr_isxdigit(x[i + 1]) && apr_isxdigit(x[i + 2])) {
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny x[j] = '\0';
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * Escape the globbing characters in a path used as argument to
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * the FTP commands (SIZE, CWD, RETR, MDTM, ...).
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * ftpd assumes '\\' as a quoting character to escape special characters.
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny * Returns: escaped string
bba1a5fd62cffcae076d1351df5a83fbc4a6ec17Simo Sorcestatic char *ftp_escape_globbingchars(apr_pool_t *p, const char *path)
bba1a5fd62cffcae076d1351df5a83fbc4a6ec17Simo Sorce char *ret = apr_palloc(p, 2*strlen(path)+sizeof(""));
bba1a5fd62cffcae076d1351df5a83fbc4a6ec17Simo Sorce *d++ = '\\';
aab938c5975f0e3b85c7c79a5d718e5fefed7217Simo Sorce * Check for globbing characters in a path used as argument to
7fe69bb6ec70bce439c6b975a9a0044c98ff502bSimo Sorce * the FTP commands (SIZE, CWD, RETR, MDTM, ...).
7fe69bb6ec70bce439c6b975a9a0044c98ff502bSimo Sorce * ftpd assumes '\\' as a quoting character to escape special characters.
7fe69bb6ec70bce439c6b975a9a0044c98ff502bSimo Sorce * Returns: 0 (no globbing chars, or all globbing chars escaped), 1 (globbing chars)
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zelenystatic int ftp_check_globbingchars(const char *path)
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny if (*path != '\0' && strchr(FTP_GLOBBING_CHARS, *path) != NULL)
9822d4d468ec74e4e173f5adf0db12d02974cd18Sumit Bose * checks an encoded ftp string for bad characters, namely, CR, LF or
9822d4d468ec74e4e173f5adf0db12d02974cd18Sumit Bose * non-ascii character
a6cca9c284724fafd670a3163812f248ba53ad97Jakub Hrozekstatic int ftp_check_string(const char *x)
bba1a5fd62cffcae076d1351df5a83fbc4a6ec17Simo Sorce for (i = 0; x[i] != '\0'; i++) {
bba1a5fd62cffcae076d1351df5a83fbc4a6ec17Simo Sorce if (ch == '%' && apr_isxdigit(x[i + 1]) && apr_isxdigit(x[i + 2])) {
bba1a5fd62cffcae076d1351df5a83fbc4a6ec17Simo Sorce#else /* APR_CHARSET_EBCDIC */
bba1a5fd62cffcae076d1351df5a83fbc4a6ec17Simo Sorce#endif /* APR_CHARSET_EBCDIC */
bba1a5fd62cffcae076d1351df5a83fbc4a6ec17Simo Sorce * Canonicalise ftp URLs.
bba1a5fd62cffcae076d1351df5a83fbc4a6ec17Simo Sorcestatic int proxy_ftp_canon(request_rec *r, char *url)
bba1a5fd62cffcae076d1351df5a83fbc4a6ec17Simo Sorce char *user, *password, *host, *path, *parms, *strp, sport[7];
bba1a5fd62cffcae076d1351df5a83fbc4a6ec17Simo Sorce const char *err;
9822d4d468ec74e4e173f5adf0db12d02974cd18Sumit Bose ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
9822d4d468ec74e4e173f5adf0db12d02974cd18Sumit Bose err = ap_proxy_canon_netloc(p, &url, &user, &password, &host, &port);
a6cca9c284724fafd670a3163812f248ba53ad97Jakub Hrozek if (password != NULL && !ftp_check_string(password))
a6cca9c284724fafd670a3163812f248ba53ad97Jakub Hrozek /* now parse path/parameters args, according to rfc1738 */
a6cca9c284724fafd670a3163812f248ba53ad97Jakub Hrozek * N.B. if this isn't a true proxy request, then the URL path (but not
c5711b0279ea85d69fe3c77dfb194360c346e1d7Sumit Bose * query args) has already been decoded. This gives rise to the problem
c5711b0279ea85d69fe3c77dfb194360c346e1d7Sumit Bose * of a ; being decoded into the path.
c5711b0279ea85d69fe3c77dfb194360c346e1d7Sumit Bose parms = ap_proxy_canonenc(p, strp, strlen(strp), enc_parm, 0,
bba1a5fd62cffcae076d1351df5a83fbc4a6ec17Simo Sorce path = ap_proxy_canonenc(p, url, strlen(url), enc_path, 0, r->proxyreq);
bba1a5fd62cffcae076d1351df5a83fbc4a6ec17Simo Sorce strp = ap_proxy_canonenc(p, r->args, strlen(r->args), enc_parm, 1, r->proxyreq);
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny strp = ap_proxy_canonenc(p, r->args, strlen(r->args), enc_fpath, 1, r->proxyreq);
65393a294e635822c1d7a15fe5853dc457ad8a2aSimo Sorce/* now, rebuild URL */
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny if (ap_strchr_c(host, ':')) { /* if literal IPv6 address */
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny r->filename = apr_pstrcat(p, "proxy:ftp://", (user != NULL) ? user : "",
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny (user != NULL) ? "@" : "", host, sport, "/", path,
65393a294e635822c1d7a15fe5853dc457ad8a2aSimo Sorce/* we chop lines longer than 80 characters */
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny * Reads response lines, returns both the ftp status code and
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny * remembers the response message in the supplied buffer
65393a294e635822c1d7a15fe5853dc457ad8a2aSimo Sorcestatic int ftp_getrc_msg(conn_rec *ftp_ctrl, apr_bucket_brigade *bb, char *msgbuf, int msglen)
cb388d52f49f54963379cc20a25e14d17fe6e9a3Simo Sorce if (APR_SUCCESS != (rv = ap_proxy_string_read(ftp_ctrl, bb, response, sizeof(response), &eos))) {
cb388d52f49f54963379cc20a25e14d17fe6e9a3Simo Sorce ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, NULL,
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny "proxy: <FTP: %s", response);
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny if (!apr_isdigit(response[0]) || !apr_isdigit(response[1]) ||
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny !apr_isdigit(response[2]) || (response[3] != ' ' && response[3] != '-'))
65393a294e635822c1d7a15fe5853dc457ad8a2aSimo Sorce status = 100 * response[0] + 10 * response[1] + response[2] - 111 * '0';
204cfc89a076fd32bf34f2abb3f809304aaa88abSimo Sorce if (APR_SUCCESS != (rv = ap_proxy_string_read(ftp_ctrl, bb, response, sizeof(response), &eos))) {
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny mb = apr_cpystrn(mb, response + (' ' == response[0] ? 1 : 4), me - mb);
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny/* this is a filter that turns a raw ASCII directory listing into pretty HTML */
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny/* ideally, mod_proxy should simply send the raw directory list up the filter
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny * stack to mod_autoindex, which in theory should turn the raw ascii into
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny * pretty html along with all the bells and whistles it provides...
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny * all in good time...! :)
65393a294e635822c1d7a15fe5853dc457ad8a2aSimo Sorcetypedef struct {
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny/* fallback regex for ls -s1; ($0..$2) == 3 */
17195241500e46272018d7897d6e87249870caf2Pavel Reichlstatic apr_status_t proxy_send_dir_filter(ap_filter_t *f,
17195241500e46272018d7897d6e87249870caf2Pavel Reichl apr_bucket_brigade *out = apr_brigade_create(p, c->bucket_alloc);
17195241500e46272018d7897d6e87249870caf2Pavel Reichl register int n;
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny const char *pwd = apr_table_get(r->notes, "Directory-PWD");
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny const char *readme = apr_table_get(r->notes, "Directory-README");
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny ctx->in = apr_brigade_create(p, c->bucket_alloc);
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny /* combine the stored and the new */
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny /* basedir is either "", or "/%2f" for the "squid %2f hack" */
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny const char *basedir = ""; /* By default, path is relative to the $HOME dir */
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny /* Save "scheme://site" prefix without password */
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny site = apr_uri_unparse(p, &f->r->parsed_uri, APR_URI_UNP_OMITPASSWORD | APR_URI_UNP_OMITPATHINFO);
3912262270a6449ebe1d3e92c27c217b4044f894Simo Sorce /* ... and path without query args */
0754ff886f909f0404038eb9c99dd61be1acf5b9Simo Sorce path = apr_uri_unparse(p, &f->r->parsed_uri, APR_URI_UNP_OMITSITEPART | APR_URI_UNP_OMITQUERY);
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny /* If path began with /%2f, change the basedir */
3912262270a6449ebe1d3e92c27c217b4044f894Simo Sorce /* Strip off a type qualifier. It is ignored for dir listings */
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny while (path[1] == '/') /* collapse multiple leading slashes to one */
c03b28a38b14fdb59f74864ae4dc56affe256508Simo Sorce if (reldir != NULL && ftp_check_globbingchars(reldir)) {
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny reldir[0] = '\0'; /* strip off the wildcard suffix */
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny /* Copy path, strip (all except the last) trailing slashes */
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny /* (the trailing slash is needed for the dir component loop below) */
3912262270a6449ebe1d3e92c27c217b4044f894Simo Sorce for (n = strlen(path); n > 1 && path[n - 1] == '/' && path[n - 2] == '/'; --n)
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny /* Add a link to the root directory (if %2f hack was used) */
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny str = (basedir[0] != '\0') ? "<a href=\"/%2f/\">%2f</a>/" : "";
3912262270a6449ebe1d3e92c27c217b4044f894Simo Sorce "<html>\n <head>\n <title>%s%s%s</title>\n"
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny " </head>\n"
c03b28a38b14fdb59f74864ae4dc56affe256508Simo Sorce " <body>\n <h2>Directory of "
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny "<a href=\"/\">%s</a>/%s",
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(str, strlen(str),
17195241500e46272018d7897d6e87249870caf2Pavel Reichl for (dir = path+1; (dir = strchr(dir, '/')) != NULL; )
17195241500e46272018d7897d6e87249870caf2Pavel Reichl /* print "path/" component */
17195241500e46272018d7897d6e87249870caf2Pavel Reichl str = apr_psprintf(p, "<a href=\"%s%s/\">%s</a>/", basedir,
17195241500e46272018d7897d6e87249870caf2Pavel Reichl APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(str,
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(wildcard,
3912262270a6449ebe1d3e92c27c217b4044f894Simo Sorce /* If the caller has determined the current directory, and it differs */
3912262270a6449ebe1d3e92c27c217b4044f894Simo Sorce /* from what the client requested, then show the real name */
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny if (pwd == NULL || strncmp(pwd, path, strlen(pwd)) == 0) {
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny str = apr_psprintf(p, "</h2>\n\n <hr />\n\n<pre>");
3b0e0352d8076909608d04750d3ea6b0d9ba33f6Jakub Hrozek str = apr_psprintf(p, "</h2>\n\n(%s)\n\n <hr />\n\n<pre>",
3b0e0352d8076909608d04750d3ea6b0d9ba33f6Jakub Hrozek APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(str, strlen(str),
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny /* print README */
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny str = apr_psprintf(p, "%s\n</pre>\n\n<hr />\n\n<pre>\n",
84c611c1b7c04cc7735ab54d4e5f48284b79e6fbJan Zeleny APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(str,
c5711b0279ea85d69fe3c77dfb194360c346e1d7Sumit Bose /* make sure page intro gets sent out */
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny APR_BRIGADE_INSERT_TAIL(out, apr_bucket_flush_create(c->bucket_alloc));
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny if (APR_SUCCESS != (rv = ap_pass_brigade(f->next, out))) {
09d7c105839bfc7447ea0f766413ed86675ca075Sumit Bose /* loop through each line of directory */
1187a07ed4207c1c326fdf83915dddfe472b8620Simo Sorce /* Compile the output format of "ls -s1" as a fallback for non-unix ftp listings */
09d7c105839bfc7447ea0f766413ed86675ca075Sumit Bose re = ap_pregcomp(p, LS_REG_PATTERN, AP_REG_EXTENDED);
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny /* get a complete line */
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny /* if the buffer overruns - throw data away */
1187a07ed4207c1c326fdf83915dddfe472b8620Simo Sorce if (APR_SUCCESS != (rv = apr_bucket_read(e, (const char **)&response, &len, APR_BLOCK_READ))) {
1187a07ed4207c1c326fdf83915dddfe472b8620Simo Sorce max = sizeof(ctx->buffer) - strlen(ctx->buffer) - 1;
09d7c105839bfc7447ea0f766413ed86675ca075Sumit Bose /* len+1 to leave space for the trailing nil char */
b3458bbb5315b05d7ac1abc58f1c380761756603Jakub Hrozek apr_cpystrn(ctx->buffer+strlen(ctx->buffer), response, len+1);
1187a07ed4207c1c326fdf83915dddfe472b8620Simo Sorce /* EOS? jump to footer */
1187a07ed4207c1c326fdf83915dddfe472b8620Simo Sorce /* not complete? leave and try get some more */
1187a07ed4207c1c326fdf83915dddfe472b8620Simo Sorce if (ctx->buffer[n-1] == CRLF[1]) /* strip trailing '\n' */
1187a07ed4207c1c326fdf83915dddfe472b8620Simo Sorce if (ctx->buffer[n-1] == CRLF[0]) /* strip trailing '\r' if present */
1187a07ed4207c1c326fdf83915dddfe472b8620Simo Sorce /* a symlink? */
1187a07ed4207c1c326fdf83915dddfe472b8620Simo Sorce if (ctx->buffer[0] == 'l' && (filename = strstr(ctx->buffer, " -> ")) != NULL) {
09d7c105839bfc7447ea0f766413ed86675ca075Sumit Bose } while (filename[0] != ' ' && filename > ctx->buffer);
b3458bbb5315b05d7ac1abc58f1c380761756603Jakub Hrozek str = apr_psprintf(p, "%s <a href=\"%s\">%s %s</a>\n",
c5711b0279ea85d69fe3c77dfb194360c346e1d7Sumit Bose else if (ctx->buffer[0] == 'd' || ctx->buffer[0] == '-' || ctx->buffer[0] == 'l' || apr_isdigit(ctx->buffer[0])) {
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny if (apr_isdigit(ctx->buffer[0])) { /* handle DOS dir */
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny /* Line is broken. Ignore it. */
1187a07ed4207c1c326fdf83915dddfe472b8620Simo Sorce ap_log_error(APLOG_MARK, APLOG_WARNING, 0, r->server,
1187a07ed4207c1c326fdf83915dddfe472b8620Simo Sorce "proxy_ftp: could not parse line %s", ctx->buffer);
09d7c105839bfc7447ea0f766413ed86675ca075Sumit Bose /* erase buffer for next time around */
1187a07ed4207c1c326fdf83915dddfe472b8620Simo Sorce continue; /* while state is BODY */
1187a07ed4207c1c326fdf83915dddfe472b8620Simo Sorce /* handle filenames with spaces in 'em */
1187a07ed4207c1c326fdf83915dddfe472b8620Simo Sorce if (!strcmp(filename, ".") || !strcmp(filename, "..") || firstfile) {
1187a07ed4207c1c326fdf83915dddfe472b8620Simo Sorce else if (searchidx != 0 && ctx->buffer[searchidx] != 0) {
204cfc89a076fd32bf34f2abb3f809304aaa88abSimo Sorce /* Append a slash to the HREF link for directories */
204cfc89a076fd32bf34f2abb3f809304aaa88abSimo Sorce if (!strcmp(filename, ".") || !strcmp(filename, "..") || ctx->buffer[0] == 'd') {
204cfc89a076fd32bf34f2abb3f809304aaa88abSimo Sorce str = apr_psprintf(p, "%s <a href=\"%s/\">%s</a>\n",
204cfc89a076fd32bf34f2abb3f809304aaa88abSimo Sorce str = apr_psprintf(p, "%s <a href=\"%s\">%s</a>\n",
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny /* Try a fallback for listings in the format of "ls -s1" */
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny else if (0 == ap_regexec(re, ctx->buffer, LS_REG_MATCH, re_result, 0)) {
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny filename = apr_pstrndup(p, &ctx->buffer[re_result[2].rm_so], re_result[2].rm_eo - re_result[2].rm_so);
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny str = apr_pstrcat(p, ap_escape_html(p, apr_pstrndup(p, ctx->buffer, re_result[2].rm_so)),
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny strcat(ctx->buffer, "\n"); /* re-append the newline */
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny /* erase buffer for next time around */
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(str, strlen(str), p,
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny APR_BRIGADE_INSERT_TAIL(out, apr_bucket_flush_create(c->bucket_alloc));
c03b28a38b14fdb59f74864ae4dc56affe256508Simo Sorce if (APR_SUCCESS != (rv = ap_pass_brigade(f->next, out))) {
09d7c105839bfc7447ea0f766413ed86675ca075Sumit Bose str = apr_psprintf(p, "</pre>\n\n <hr />\n\n %s\n\n </body>\n</html>\n", ap_psignature("", r));
09d7c105839bfc7447ea0f766413ed86675ca075Sumit Bose APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(str, strlen(str), p,
09d7c105839bfc7447ea0f766413ed86675ca075Sumit Bose APR_BRIGADE_INSERT_TAIL(out, apr_bucket_flush_create(c->bucket_alloc));
09d7c105839bfc7447ea0f766413ed86675ca075Sumit Bose APR_BRIGADE_INSERT_TAIL(out, apr_bucket_eos_create(c->bucket_alloc));
09d7c105839bfc7447ea0f766413ed86675ca075Sumit Bose if (APR_SUCCESS != (rv = ap_pass_brigade(f->next, out))) {
b3458bbb5315b05d7ac1abc58f1c380761756603Jakub Hrozek * Generic "send FTP command to server" routine, using the control socket.
b3458bbb5315b05d7ac1abc58f1c380761756603Jakub Hrozek * Returns the FTP returncode (3 digit code)
b3458bbb5315b05d7ac1abc58f1c380761756603Jakub Hrozek * Allows for tracing the FTP protocol (in LogLevel debug)
b3458bbb5315b05d7ac1abc58f1c380761756603Jakub Hrozekproxy_ftp_command(const char *cmd, request_rec *r, conn_rec *ftp_ctrl,
c5711b0279ea85d69fe3c77dfb194360c346e1d7Sumit Bose /* If cmd == NULL, we retrieve the next ftp response line */
c5711b0279ea85d69fe3c77dfb194360c346e1d7Sumit Bose APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_pool_create(cmd, strlen(cmd), r->pool, c->bucket_alloc));
c5711b0279ea85d69fe3c77dfb194360c346e1d7Sumit Bose APR_BRIGADE_INSERT_TAIL(bb, apr_bucket_flush_create(c->bucket_alloc));
c5711b0279ea85d69fe3c77dfb194360c346e1d7Sumit Bose /* strip off the CRLF for logging */
c5711b0279ea85d69fe3c77dfb194360c346e1d7Sumit Bose ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
1187a07ed4207c1c326fdf83915dddfe472b8620Simo Sorce rc = ftp_getrc_msg(ftp_ctrl, bb, message, sizeof message);
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server,
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny/* Set ftp server to TYPE {A,I,E} before transfer of a directory or file */
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zelenystatic int ftp_set_TYPE(char xfer_type, request_rec *r, conn_rec *ftp_ctrl,
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny char old_type[2] = { 'A', '\0' }; /* After logon, mode is ASCII */
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny /* set desired type */
bba1a5fd62cffcae076d1351df5a83fbc4a6ec17Simo Sorce rc = proxy_ftp_command(apr_pstrcat(r->pool, "TYPE ", old_type, CRLF, NULL),
bba1a5fd62cffcae076d1351df5a83fbc4a6ec17Simo Sorce/* responses: 200, 421, 500, 501, 504, 530 */
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny /* 200 Command okay. */
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny /* 421 Service not available, closing control connection. */
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny /* 500 Syntax error, command unrecognized. */
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny /* 501 Syntax error in parameters or arguments. */
bba1a5fd62cffcae076d1351df5a83fbc4a6ec17Simo Sorce /* 504 Command not implemented for that parameter. */
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny /* 530 Not logged in. */
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny "Error reading from remote server");
e76d78338026fa47dca32eaf7f5c15eabb1b951aJan Zeleny "Unable to set transfer type");
return ret;
return cwd;
* ftp://user@host part of the reqest (sans password -if supplied but invalid-)
if (log_it)
return HTTP_UNAUTHORIZED;
return OK;
int dirlisting = 0;
int status;
if (proxyhost) {
return HTTP_NOT_IMPLEMENTED;
if (connectport == 0) {
return HTTP_INTERNAL_SERVER_ERROR;
if (!connect_addr)
connectport, 0,
if (!backend) {
if (backend) {
return status;
backend->r = r;
return HTTP_SERVICE_UNAVAILABLE;
return status;
while (*secs_str)
return ftp_unauthorized(r, 0);
++path;
* We could also have extended gen_test_char.c with a special T_ESCAPE_FTP_PATH
char *data_ip;
char *pstr;
char *tok_cntx;
if (pstr) {
if ((rv = apr_socket_create(&data_sock, connect_addr->family, SOCK_STREAM, 0, r->pool)) != APR_SUCCESS) {
return HTTP_INTERNAL_SERVER_ERROR;
if (!connect) {
char *pstr;
char *tok_cntx;
if ((rv = apr_socket_create(&data_sock, connect_addr->family, SOCK_STREAM, 0, r->pool)) != APR_SUCCESS) {
return HTTP_INTERNAL_SERVER_ERROR;
apr_sockaddr_info_get(&pasv_addr, apr_psprintf(p, "%d.%d.%d.%d", h3, h2, h1, h0), connect_addr->family, pasvport, 0, p);
if (!connect) {
char *local_ip;
if ((rv = apr_socket_create(&local_sock, connect_addr->family, SOCK_STREAM, 0, r->pool)) != APR_SUCCESS) {
return HTTP_INTERNAL_SERVER_ERROR;
!= APR_SUCCESS) {
return HTTP_INTERNAL_SERVER_ERROR;
return HTTP_INTERNAL_SERVER_ERROR;
return HTTP_INTERNAL_SERVER_ERROR;
/* from draft-ietf-ftpext-mlst-14.txt:
len = 0;
if (dirlisting) {
* queries like: ftp://user@host/apache/src/server/http_*.c
if (len != 0)
/* from draft-ietf-ftpext-mlst-14.txt:
* YYYYMMDDHHMMSS.sss
} time_val;
mtime = 0L;
/* rc is an intermediate response for the LIST command (125 transfer starting, 150 opening data connection) */
if (dirlisting) {
if (r->content_type) {
if (mtime != 0L) {
* @@@ FIXME (e.g., for ftp://user@host/file*.tar.gz,
if (use_port) {
return HTTP_BAD_GATEWAY;
if (!data) {
return HTTP_INTERNAL_SERVER_ERROR;
rc);
return rc;
if (dirlisting) {
if (!r->header_only) {
apr_bucket *e;
bb,
#if DEBUGGING
|| c->aborted) {
if (data_sock) {
return OK;