httpd-cvs mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mar...@apache.org
Subject cvs commit: httpd-proxy/module-1.0 proxy_ftp.c
Date Mon, 28 Jan 2002 16:29:40 GMT
martin      02/01/28 08:29:40

  Modified:    module-1.0 proxy_ftp.c
  Log:
  ftp proxy: various cosmetic and functional improvements
  (also integrated into apache-1.3.24-dev):
   - Allow for /%2f hack (to access the root directory / )
   - properly escape generated links in dir listing
   - do directory listings in ASCII, to avoid problems with EBCDIC servers
   - close data & control channels to server properly
   - Rename "BUFF f" to "BUFF data" or "BUFF ctrl", depending on ftp use
     (cosmetics only)
   - Make output look more like xhtml (cosmetics only)
   - Properly escape file names from ftp directory listing (as HTML or URI)
   - Treat ftp rc 421 (closing connection) like -1 (connection was closed)
   - fix a possible pointer underrun
  
  Revision  Changes    Path
  1.6       +429 -323  httpd-proxy/module-1.0/proxy_ftp.c
  
  Index: proxy_ftp.c
  ===================================================================
  RCS file: /home/cvs/httpd-proxy/module-1.0/proxy_ftp.c,v
  retrieving revision 1.5
  retrieving revision 1.6
  diff -u -r1.5 -r1.6
  --- proxy_ftp.c	11 Jan 2002 14:45:35 -0000	1.5
  +++ proxy_ftp.c	28 Jan 2002 16:29:40 -0000	1.6
  @@ -185,12 +185,12 @@
    * Returns the ftp status code;
    *  or -1 on I/O error, 0 on data error
    */
  -static int ftp_getrc(BUFF *f)
  +static int ftp_getrc(BUFF *ctrl)
   {
       int len, status;
       char linebuff[100], buff[5];
   
  -    len = ap_bgets(linebuff, sizeof linebuff, f);
  +    len = ap_bgets(linebuff, sizeof linebuff, ctrl);
       if (len == -1)
           return -1;
   /* check format */
  @@ -201,7 +201,7 @@
           status = 100 * linebuff[0] + 10 * linebuff[1] + linebuff[2] - 111 * '0';
   
       if (linebuff[len - 1] != '\n') {
  -        (void)ap_bskiplf(f);
  +        (void)ap_bskiplf(ctrl);
       }
   
   /* skip continuation lines */
  @@ -209,11 +209,11 @@
           memcpy(buff, linebuff, 3);
           buff[3] = ' ';
           do {
  -            len = ap_bgets(linebuff, sizeof linebuff, f);
  +            len = ap_bgets(linebuff, sizeof linebuff, ctrl);
               if (len == -1)
                   return -1;
               if (linebuff[len - 1] != '\n') {
  -                (void)ap_bskiplf(f);
  +                (void)ap_bskiplf(ctrl);
               }
           } while (memcmp(linebuff, buff, 4) != 0);
       }
  @@ -225,14 +225,14 @@
    * Like ftp_getrc but returns both the ftp status code and 
    * remembers the response message in the supplied buffer
    */
  -static int ftp_getrc_msg(BUFF *f, char *msgbuf, int msglen)
  +static int ftp_getrc_msg(BUFF *ctrl, char *msgbuf, int msglen)
   {
       int len, status;
       char linebuff[100], buff[5];
       char *mb = msgbuf,
            *me = &msgbuf[msglen];
   
  -    len = ap_bgets(linebuff, sizeof linebuff, f);
  +    len = ap_bgets(linebuff, sizeof linebuff, ctrl);
       if (len == -1)
           return -1;
       if (len < 5 || !ap_isdigit(linebuff[0]) || !ap_isdigit(linebuff[1]) ||
  @@ -244,17 +244,17 @@
       mb = ap_cpystrn(mb, linebuff+4, me - mb);
   
       if (linebuff[len - 1] != '\n')
  -        (void)ap_bskiplf(f);
  +        (void)ap_bskiplf(ctrl);
   
       if (linebuff[3] == '-') {
           memcpy(buff, linebuff, 3);
           buff[3] = ' ';
           do {
  -            len = ap_bgets(linebuff, sizeof linebuff, f);
  +            len = ap_bgets(linebuff, sizeof linebuff, ctrl);
               if (len == -1)
                   return -1;
               if (linebuff[len - 1] != '\n') {
  -                (void)ap_bskiplf(f);
  +                (void)ap_bskiplf(ctrl);
               }
               mb = ap_cpystrn(mb, linebuff+4, me - mb);
           } while (memcmp(linebuff, buff, 4) != 0);
  @@ -262,7 +262,7 @@
       return status;
   }
   
  -static long int send_dir(BUFF *f, request_rec *r, cache_req *c, char *cwd)
  +static long int send_dir(BUFF *data, request_rec *r, cache_req *c, char *cwd)
   {
       char buf[IOBUFSIZE];
       char buf2[IOBUFSIZE];
  @@ -273,51 +273,82 @@
       unsigned long total_bytes_sent = 0;
       register int n, o, w;
       conn_rec *con = r->connection;
  -    char *dir, *path, *reldir, *site;
  +    pool *p = r->pool;
  +    char *dir, *path, *reldir, *site, *type = NULL;
  +    char *basedir = ""; /* By default, path is relative to the $HOME dir */
   
       /* Save "scheme://site" prefix without password */
  -    site = ap_unparse_uri_components(r->pool, &r->parsed_uri, UNP_OMITPASSWORD|UNP_OMITPATHINFO);
  +    site = ap_unparse_uri_components(p, &r->parsed_uri, UNP_OMITPASSWORD|UNP_OMITPATHINFO);
       /* ... and path without query args */
  -    path = ap_unparse_uri_components(r->pool, &r->parsed_uri, UNP_OMITSITEPART|UNP_OMITQUERY);
  +    path = ap_unparse_uri_components(p, &r->parsed_uri, UNP_OMITSITEPART|UNP_OMITQUERY);
  +
  +    /* If path began with /%2f, change the basedir */
  +    if (strncasecmp(path, "/%2f", 4) == 0) {
  +        basedir = "/%2f";
  +    }
  +
  +    /* Strip off a type qualifier. It is ignored for dir listings */
  +    if ((type = strstr(path, ";type=")) != NULL)
  +        *type++ = '\0';
  +
       (void)decodeenc(path);
   
  +    while (path[1] == '/') /* collapse multiple leading slashes to one */
  +        ++path;
  +
       /* Copy path, strip (all except the last) trailing slashes */
  +    /* (the trailing slash is needed for the dir component loop below) */
       path = dir = ap_pstrcat(r->pool, path, "/", NULL);
  -    while ((n = strlen(path)) > 1 && path[n-1] == '/' && path[n-2] == '/')
  +    for (n = strlen(path); n > 1 && path[n-1] == '/' && path[n-2] == '/'; --n)
           path[n-1] = '\0';
   
       /* print "ftp://host/" */
       n = ap_snprintf(buf, sizeof(buf), DOCTYPE_HTML_3_2
  -                "<HTML><HEAD><TITLE>%s%s</TITLE>\n"
  -                "<BASE HREF=\"%s%s\"></HEAD>\n"
  -                "<BODY><H2>Directory of "
  -                "<A HREF=\"/\">%s</A>/",
  -                site, path, site, path, site);
  +                "<html><head><title>%s%s%s</title>\n"
  +                "<base href=\"%s%s%s\"></head>\n"
  +                "<body><h2>Directory of "
  +                "<a href=\"/\">%s</a>/",
  +                site, basedir, ap_escape_html(p,path),
  +                site, basedir, ap_escape_uri(p,path),
  +                site);
       total_bytes_sent += ap_proxy_bputs2(buf, con->client, c);
   
  -    while ((dir = strchr(dir+1, '/')) != NULL)
  +    /* Add a link to the root directory (if %2f hack was used) */
  +    if (basedir[0] != '\0') {
  +        total_bytes_sent += ap_proxy_bputs2("<a href=\"/%2f/\">%2f</a>/", con->client, c);
  +    }
  +
  +    for (dir = path+1; (dir = strchr(dir, '/')) != NULL; )
       {
           *dir = '\0';
  -        if ((reldir = strrchr(path+1, '/'))==NULL)
  +        if ((reldir = strrchr(path+1, '/'))==NULL) {
               reldir = path+1;
  +        }
           else
               ++reldir;
           /* print "path/" component */
  -        ap_snprintf(buf, sizeof(buf), "<A HREF=\"/%s/\">%s</A>/", path+1, reldir);
  +        ap_snprintf(buf, sizeof(buf), "<a href=\"%s%s/\">%s</a>/",
  +                    basedir,
  +                    ap_escape_uri(p, path),
  +                    ap_escape_html(p, reldir));
           total_bytes_sent += ap_proxy_bputs2(buf, con->client, c);
           *dir = '/';
  +        while (*dir == '/')
  +          ++dir;
       }
  +
       /* If the caller has determined the current directory, and it differs */
       /* from what the client requested, then show the real name */
       if (cwd == NULL || strncmp (cwd, path, strlen(cwd)) == 0) {
  -        ap_snprintf(buf, sizeof(buf), "</H2>\n<HR><PRE>");
  +        ap_snprintf(buf, sizeof(buf), "</h2>\n<hr /><pre>");
       } else {
  -        ap_snprintf(buf, sizeof(buf), "</H2>\n(%s)\n<HR><PRE>", cwd);
  +        ap_snprintf(buf, sizeof(buf), "</h2>\n(%s)\n<hr /><pre>",
  +                    ap_escape_html(p, cwd));
       }
       total_bytes_sent += ap_proxy_bputs2(buf, con->client, c);
   
       while (!con->aborted) {
  -        n = ap_bgets(buf, sizeof buf, f);
  +        n = ap_bgets(buf, sizeof buf, data);
           if (n == -1) {          /* input error */
               if (c != NULL) {
                   ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
  @@ -328,20 +359,31 @@
           }
           if (n == 0)
               break;                /* EOF */
  +
  +        if (buf[n-1] == '\n')  /* strip trailing '\n' */
  +            buf[--n] = '\0';
  +        if (buf[n-1] == '\r')  /* strip trailing '\r' if present */
  +            buf[--n] = '\0';
  +
  +        /* Handle unix-style symbolic link */
           if (buf[0] == 'l' && (filename=strstr(buf, " -> ")) != NULL) {
               char *link_ptr = filename;
   
               do {
                   filename--;
  -            } while (filename[0] != ' ');
  -            *(filename++) = '\0';
  +            } while (filename[0] != ' ' && filename > buf);
  +            if (filename != buf)
  +                *(filename++) = '\0';
               *(link_ptr++) = '\0';
  -            if ((n = strlen(link_ptr)) > 1 && link_ptr[n - 1] == '\n')
  -              link_ptr[n - 1] = '\0';
  -            ap_snprintf(buf2, sizeof(buf2), "%s <A HREF=\"%s\">%s %s</A>\n", buf, filename, filename, link_ptr);
  +            ap_snprintf(buf2, sizeof(buf2), "%s <a href=\"%s\">%s %s</a>\n",
  +                        ap_escape_html(p, buf),
  +                        ap_escape_uri(p,filename),
  +                        ap_escape_html(p, filename),
  +                        ap_escape_html(p, link_ptr));
               ap_cpystrn(buf, buf2, sizeof(buf));
               n = strlen(buf);
           }
  +        /* Handle unix style or DOS style directory  */
           else if (buf[0] == 'd' || buf[0] == '-' || buf[0] == 'l' || ap_isdigit(buf[0])) {
               if (ap_isdigit(buf[0])) {   /* handle DOS dir */
                   searchptr = strchr(buf, '<');
  @@ -354,7 +396,6 @@
   
               filename = strrchr(buf, ' ');
               *(filename++) = 0;
  -            filename[strlen(filename) - 1] = 0;
   
               /* handle filenames with spaces in 'em */
               if (!strcmp(filename, ".") || !strcmp(filename, "..") || firstfile) {
  @@ -367,40 +408,35 @@
                   filename = &buf[searchidx];
               }
   
  -            /* Special handling for '.' and '..' */
  +            /* Special handling for '.' and '..': append slash to link */
               if (!strcmp(filename, ".") || !strcmp(filename, "..") || buf[0] == 'd') {
  -                ap_snprintf(buf2, sizeof(buf2), "%s <A HREF=\"%s/\">%s</A>\n",
  -                    buf, filename, filename);
  +                ap_snprintf(buf2, sizeof(buf2), "%s <a href=\"%s/\">%s</a>\n",
  +                            ap_escape_html(p, buf), ap_escape_uri(p,filename),
  +                            ap_escape_html(p, filename));
               }
               else {
  -                ap_snprintf(buf2, sizeof(buf2), "%s <A HREF=\"%s\">%s</A>\n", buf, filename, filename);
  +                ap_snprintf(buf2, sizeof(buf2), "%s <a href=\"%s\">%s</a>\n",
  +                            ap_escape_html(p, buf),
  +                            ap_escape_uri(p,filename),
  +                            ap_escape_html(p, filename));
               }
               ap_cpystrn(buf, buf2, sizeof(buf));
               n = strlen(buf);
           }
  -
  -        o = 0;
  -        total_bytes_sent += n;
  -
  -        if (c != NULL && c->fp && ap_bwrite(c->fp, buf, n) != n) {
  -            ap_log_rerror(APLOG_MARK, APLOG_ERR, c->req,
  -                "proxy: error writing to %s", c->tempfile);
  -            c = ap_proxy_cache_error(c);
  +        /* else??? What about other OS's output formats? */
  +        else {
  +            strcat(buf, "\n"); /* re-append the newline char */
  +            ap_cpystrn(buf, ap_escape_html(p, buf), sizeof(buf));
           }
   
  -        while (n && !r->connection->aborted) {
  -            w = ap_bwrite(con->client, &buf[o], n);
  -            if (w <= 0)
  -                break;
  -            ap_reset_timeout(r); /* reset timeout after successfule write */
  -            n -= w;
  -            o += w;
  -        }
  +        total_bytes_sent += ap_proxy_bputs2(buf, con->client, c);
  +
  +        ap_reset_timeout(r);        /* reset timeout after successfule write */
       }
   
  -    total_bytes_sent += ap_proxy_bputs2("</PRE><HR>\n", con->client, c);
  +    total_bytes_sent += ap_proxy_bputs2("</pre><hr />\n", con->client, c);
       total_bytes_sent += ap_proxy_bputs2(ap_psignature("", r), con->client, c);
  -    total_bytes_sent += ap_proxy_bputs2("</BODY></HTML>\n", con->client, c);
  +    total_bytes_sent += ap_proxy_bputs2("</body></html>\n", con->client, c);
   
       ap_bflush(con->client);
   
  @@ -435,6 +471,67 @@
       return HTTP_UNAUTHORIZED;
   }
   
  +/* Set ftp server to TYPE {A,I,E} before transfer of a directory or file */
  +static int ftp_set_TYPE(request_rec *r, BUFF *ctrl, char xfer_type)
  +{
  +    static char old_type[2] = { 'A', '\0' }; /* After logon, mode is ASCII */
  +    int ret = HTTP_OK;
  +    int rc;
  +
  +    if (xfer_type == old_type[0])
  +        return ret;
  +
  +    /* set desired type */
  +    old_type[0] = xfer_type;
  +    ap_bvputs(ctrl, "TYPE ", old_type, CRLF, NULL);
  +    ap_bflush(ctrl);
  +    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: TYPE %s", old_type);
  +
  +/* responses: 200, 421, 500, 501, 504, 530 */
  +    /* 200 Command okay. */
  +    /* 421 Service not available, closing control connection. */
  +    /* 500 Syntax error, command unrecognized. */
  +    /* 501 Syntax error in parameters or arguments. */
  +    /* 504 Command not implemented for that parameter. */
  +    /* 530 Not logged in. */
  +    rc = ftp_getrc(ctrl);
  +    ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", rc);
  +    if (rc == -1 || rc == 421) {
  +        ap_kill_timeout(r);
  +        ret = ap_proxyerror(r, HTTP_BAD_GATEWAY,
  +                             "Error reading from remote server");
  +    }
  +    else if (rc != 200 && rc != 504) {
  +        ap_kill_timeout(r);
  +        ret = ap_proxyerror(r, HTTP_BAD_GATEWAY,
  +                             "Unable to set transfer type");
  +    }
  +/* Allow not implemented */
  +    else if (rc == 504)
  +        /* ignore it silently */;
  +
  +    return ret;
  +}
  +
  +/* Common cleanup routine: close open BUFFers or sockets, and return an error */
  +static int
  +ftp_cleanup_and_return(request_rec *r, BUFF *ctrl, BUFF *data, int csock, int dsock, int rc)
  +{
  +    if (ctrl != NULL)
  +      ap_bclose(ctrl);
  +    else if (csock != -1)
  +      ap_pclosesocket(r->pool, csock);
  +
  +    if (data != NULL)
  +      ap_bclose(data);
  +    else if (dsock != -1)
  +      ap_pclosesocket(r->pool, dsock);
  +
  +    ap_kill_timeout(r);
  +
  +    return rc;
  +}
  +
   /*
    * Handles direct access of ftp:// URLs
    * Original (Non-PASV) version from
  @@ -449,17 +546,19 @@
   /*    char *account = NULL; how to supply an account in a URL? */
       const char *password = NULL;
       const char *err;
  -    int port, i, j, len, sock, dsock, rc, nocache = 0;
  -    int csd = 0;
  +    int port, i, j, len, rc, nocache = 0;
  +    int csd = 0, sock = -1, dsock = -1;
       struct sockaddr_in server;
       struct hostent server_hp;
       struct in_addr destaddr;
       table *resp_hdrs;
  -    BUFF *f;
  +    BUFF *ctrl = NULL;
       BUFF *data = NULL;
       pool *p = r->pool;
       int one = 1;
       NET_SIZE_T clen;
  +    char xfer_type = 'A'; /* after ftp login, the default is ASCII */
  +    int get_dirlisting = 0;
   
       void *sconf = r->server->module_config;
       proxy_server_conf *conf =
  @@ -492,7 +591,11 @@
               ? r->parsed_uri.port
               : ap_default_port_for_request(r);
       path = ap_pstrdup(p, r->parsed_uri.path);
  -    path = (path != NULL && path[0] != '\0') ? &path[1] : "";
  +    if (path == NULL)
  +        path = "";
  +    else
  +        while (*path == '/')
  +            ++path;
   
       /* The "Authorization:" header must be checked first.
        * We allow the user to "override" the URL-coded user [ & password ]
  @@ -547,7 +650,7 @@
   
       memset(&server, 0, sizeof(struct sockaddr_in));
       server.sin_family = AF_INET;
  -    server.sin_port = htons(port);
  +    server.sin_port = htons((unsigned short)port);
       err = ap_proxy_host2addr(host, &server_hp);
       if (err != NULL)
           return ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, err);
  @@ -602,21 +705,21 @@
       }
   #endif
       if (i == -1) {
  -        ap_pclosesocket(p, sock);
  -        return ap_proxyerror(r, HTTP_BAD_GATEWAY, ap_pstrcat(r->pool,
  -                                "Could not connect to remote machine: ",
  -                                strerror(errno), NULL));
  +        return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                      ap_proxyerror(r, HTTP_BAD_GATEWAY, ap_pstrcat(r->pool,
  +                                    "Could not connect to remote machine: ",
  +                                    strerror(errno), NULL)));
       }
   
       /* record request_time for HTTP/1.1 age calculation */
       c->req_time = time(NULL);
   
  -    f = ap_bcreate(p, B_RDWR | B_SOCKET);
  -    ap_bpushfd(f, sock, sock);
  +    ctrl = ap_bcreate(p, B_RDWR | B_SOCKET);
  +    ap_bpushfd(ctrl, sock, sock);
   /* shouldn't we implement telnet control options here? */
   
   #ifdef CHARSET_EBCDIC
  -    ap_bsetflag(f, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 1);
  +    ap_bsetflag(ctrl, B_ASCII2EBCDIC|B_EBCDIC2ASCII, 1);
   #endif /*CHARSET_EBCDIC*/
   
       /* possible results: */
  @@ -624,16 +727,15 @@
       /* 220 Service ready for new user. */
       /* 421 Service not available, closing control connection. */
       ap_hard_timeout("proxy ftp", r);
  -    i = ftp_getrc_msg(f, resp, sizeof resp);
  +    i = ftp_getrc_msg(ctrl, resp, sizeof resp);
       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", i);
  -    if (i == -1) {
  -        ap_kill_timeout(r);
  -        return ap_proxyerror(r, HTTP_BAD_GATEWAY,
  -                             "Error reading from remote server");
  +    if (i == -1 || i == 421) {
  +        return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                      ap_proxyerror(r, HTTP_BAD_GATEWAY,
  +                                    "Error reading from remote server"));
       }
   #if 0
       if (i == 120) {
  -        ap_kill_timeout(r);
           /* RFC2068 states:
            * 14.38 Retry-After
            * 
  @@ -645,18 +747,19 @@
            *     Retry-After  = "Retry-After" ":" ( HTTP-date | delta-seconds )
            */
           ap_set_header("Retry-After", ap_psprintf(p, "%u", 60*wait_mins);
  -        return ap_proxyerror(r, HTTP_SERVICE_UNAVAILABLE, resp);
  +        return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                      ap_proxyerror(r, HTTP_SERVICE_UNAVAILABLE, resp));
       }
   #endif
       if (i != 220) {
  -        ap_kill_timeout(r);
  -        return ap_proxyerror(r, HTTP_BAD_GATEWAY, resp);
  +        return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                      ap_proxyerror(r, HTTP_BAD_GATEWAY, resp));
       }
   
       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: connected.");
   
  -    ap_bvputs(f, "USER ", user, CRLF, NULL);
  -    ap_bflush(f);                        /* capture any errors */
  +    ap_bvputs(ctrl, "USER ", user, CRLF, NULL);
  +    ap_bflush(ctrl);                        /* capture any errors */
       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: USER %s", user);
   
       /* possible results; 230, 331, 332, 421, 500, 501, 530 */
  @@ -669,28 +772,29 @@
       /*     (This may include errors such as command line too long.) */
       /* 501 Syntax error in parameters or arguments. */
       /* 530 Not logged in. */
  -    i = ftp_getrc(f);
  +    i = ftp_getrc(ctrl);
       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", i);
  -    if (i == -1) {
  -        ap_kill_timeout(r);
  -        return ap_proxyerror(r, HTTP_BAD_GATEWAY,
  -                             "Error reading from remote server");
  +    if (i == -1 || i == 421) {
  +        return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                      ap_proxyerror(r, HTTP_BAD_GATEWAY,
  +                                    "Error reading from remote server"));
       }
       if (i == 530) {
  -        ap_kill_timeout(r);
  -        return ftp_unauthorized (r, 1);        /* log it: user name guessing attempt? */
  +        return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                                      ftp_unauthorized (r, 1));
       }
       if (i != 230 && i != 331) {
  -        ap_kill_timeout(r);
  -        return HTTP_BAD_GATEWAY;
  +        return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                                      HTTP_BAD_GATEWAY);
       }
   
       if (i == 331) {             /* send password */
           if (password == NULL) {
  -            return ftp_unauthorized (r, 0);
  +            return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                                          ftp_unauthorized (r, 0));
           }
  -        ap_bvputs(f, "PASS ", password, CRLF, NULL);
  -        ap_bflush(f);
  +        ap_bvputs(ctrl, "PASS ", password, CRLF, NULL);
  +        ap_bflush(ctrl);
           ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: PASS %s", password);
       /* possible results 202, 230, 332, 421, 500, 501, 503, 530 */
       /* 230 User logged in, proceed. */
  @@ -700,44 +804,86 @@
       /* 501 Syntax error in parameters or arguments. */
       /* 503 Bad sequence of commands. */
       /* 530 Not logged in. */
  -        i = ftp_getrc(f);
  +        i = ftp_getrc(ctrl);
           ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", i);
  -        if (i == -1) {
  -            ap_kill_timeout(r);
  -            return ap_proxyerror(r, HTTP_BAD_GATEWAY,
  -                                 "Error reading from remote server");
  +        if (i == -1 || i == 421) {
  +            return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                      ap_proxyerror(r, HTTP_BAD_GATEWAY,
  +                                    "Error reading from remote server"));
           }
           if (i == 332) {
  -            ap_kill_timeout(r);
  -            return ap_proxyerror(r, HTTP_UNAUTHORIZED,
  -                                 "Need account for login");
  +            return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                      ap_proxyerror(r, HTTP_UNAUTHORIZED,
  +                                 "Need account for login"));
           }
           /* @@@ questionable -- we might as well return a 403 Forbidden here */
  -        if (i == 530) {
  -            ap_kill_timeout(r);
  -            return ftp_unauthorized (r, 1); /* log it: passwd guessing attempt? */
  -        }
  -        if (i != 230 && i != 202) {
  -            ap_kill_timeout(r);
  -            return HTTP_BAD_GATEWAY;
  -        }
  +        if (i == 530) /* log it: passwd guessing attempt? */
  +            return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                                          ftp_unauthorized (r, 1));
  +        if (i != 230 && i != 202)
  +            return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                                          HTTP_BAD_GATEWAY);
  +    }
  +
  +    /* Special handling for leading "%2f": this enforces a "cwd /"
  +     * out of the $HOME directory which was the starting point after login
  +     */
  +    if (strncasecmp(path, "%2f", 3) == 0) {
  +        path += 3;
  +        while (*path == '/') /* skip leading '/' (after root %2f) */
  +            ++path;
  +        ap_bputs("CWD /" CRLF, ctrl);
  +        ap_bflush(ctrl);
  +        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: CWD /");
  +
  +        /* possible results: 250, 421, 500, 501, 502, 530, 550 */
  +        /* 250 Requested file action okay, completed. */
  +        /* 421 Service not available, closing control connection. */
  +        /* 500 Syntax error, command unrecognized. */
  +        /* 501 Syntax error in parameters or arguments. */
  +        /* 502 Command not implemented. */
  +        /* 530 Not logged in. */
  +        /* 550 Requested action not taken. */
  +        i = ftp_getrc(ctrl);
  +        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", i);
  +        if (i == -1 || i == 421)
  +            return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                        ap_proxyerror(r, HTTP_BAD_GATEWAY,
  +                                      "Error reading from remote server"));
  +        else if (i == 550)
  +            return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                                          HTTP_NOT_FOUND);
  +        else if (i != 250)
  +            return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                                          HTTP_BAD_GATEWAY);
       }
   
   /* set the directory (walk directory component by component):
    * this is what we must do if we don't know the OS type of the remote
    * machine
    */
  -    for (;;) {
  -        strp = strchr(path, '/');
  -        if (strp == NULL)
  +    for ( ; (strp = strchr(path, '/')) != NULL ; path = strp + 1) {
  +        char *slash = strp;
  +
  +        *slash = '\0';
  +
  +        /* Skip multiple '/' (or trailing '/') to avoid 500 errors */
  +        while (strp[1] == '/')
  +            ++strp;
  +        if (strp[1] == '\0')
               break;
  -        *strp = '\0';
   
  -        len = decodeenc(path);
  -        ap_bvputs(f, "CWD ", path, CRLF, NULL);
  -        ap_bflush(f);
  +        len = decodeenc(path); /* Note! This decodes a %2f -> "/" */
  +        if (strchr(path, '/')) /* were there any '/' characters? */
  +            return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                        ap_proxyerror(r, HTTP_BAD_REQUEST,
  +                                      "Use of %2F is only allowed at the base directory"));
  +
  +        ap_bvputs(ctrl, "CWD ", path, CRLF, NULL);
  +        ap_bflush(ctrl);
           ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: CWD %s", path);
  -        *strp = '/';
  +        *slash = '/';
  +
   /* responses: 250, 421, 500, 501, 502, 530, 550 */
       /* 250 Requested file action okay, completed. */
       /* 421 Service not available, closing control connection. */
  @@ -746,74 +892,50 @@
       /* 502 Command not implemented. */
       /* 530 Not logged in. */
       /* 550 Requested action not taken. */
  -        i = ftp_getrc(f);
  +        i = ftp_getrc(ctrl);
           ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", i);
  -        if (i == -1) {
  -            ap_kill_timeout(r);
  -            return ap_proxyerror(r, HTTP_BAD_GATEWAY,
  -                                 "Error reading from remote server");
  -        }
  -        if (i == 550) {
  -            ap_kill_timeout(r);
  -            return HTTP_NOT_FOUND;
  -        }
  -        if (i != 250) {
  -            ap_kill_timeout(r);
  -            return HTTP_BAD_GATEWAY;
  -        }
  -
  -        path = strp + 1;
  -    }
  +        if (i == -1 || i == 421)
  +            return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                      ap_proxyerror(r, HTTP_BAD_GATEWAY,
  +                                    "Error reading from remote server"));
  +        if (i == 550)
  +            return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                                          HTTP_NOT_FOUND);
  +        if (i == 500 || i == 501)
  +            return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                      ap_proxyerror(r, HTTP_BAD_REQUEST,
  +                                    "Syntax error in filename (reported by ftp server)"));
  +        if (i != 250)
  +            return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                                          HTTP_BAD_GATEWAY);
  +    }
  +
  +    if (parms != NULL && strncmp(parms, "type=", 5) == 0
  +        && ap_isalpha(parms[5])) {
  +        /* "type=d" forces a dir listing.
  +         * The other types (i|a|e) are directly used for the ftp TYPE command
  +         */
  +        if ( ! (get_dirlisting = (parms[5] == 'd')))
  +            xfer_type = ap_toupper(parms[5]);
   
  -    if (parms != NULL && strncmp(parms, "type=", 5) == 0) {
  -        parms += 5;
  -        if ((parms[0] != 'd' && parms[0] != 'a' && parms[0] != 'i') ||
  -            parms[1] != '\0')
  -            parms = "";
  +        /* Check valid types, rather than ignoring invalid types silently: */
  +        if (strchr("AEI", xfer_type) == NULL)
  +            return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                      ap_proxyerror(r, HTTP_BAD_REQUEST, ap_pstrcat(r->pool,
  +                                    "ftp proxy supports only types 'a', 'i', or 'e': \"",
  +                                    parms, "\" is invalid.", NULL)));
       }
  -    else
  -        parms = "";
  -
  -    /* changed to make binary transfers the default */
  -
  -    if (parms[0] != 'a') {
  -        /* set type to image */
  -        /* TM - Added CRLF to the end of TYPE I, otherwise it hangs the
  -           connection */
  -        ap_bputs("TYPE I" CRLF, f);
  -        ap_bflush(f);
  -        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: TYPE I");
  -/* responses: 200, 421, 500, 501, 504, 530 */
  -    /* 200 Command okay. */
  -    /* 421 Service not available, closing control connection. */
  -    /* 500 Syntax error, command unrecognized. */
  -    /* 501 Syntax error in parameters or arguments. */
  -    /* 504 Command not implemented for that parameter. */
  -    /* 530 Not logged in. */
  -        i = ftp_getrc(f);
  -        ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", i);
  -        if (i == -1) {
  -            ap_kill_timeout(r);
  -            return ap_proxyerror(r, HTTP_BAD_GATEWAY,
  -                                 "Error reading from remote server");
  -        }
  -        if (i != 200 && i != 504) {
  -            ap_kill_timeout(r);
  -            return HTTP_BAD_GATEWAY;
  -        }
  -/* Allow not implemented */
  -        if (i == 504)
  -            parms[0] = '\0';
  +    else {
  +        /* make binary transfers the default */
  +        xfer_type = 'I';
       }
   
   /* try to set up PASV data connection first */
       dsock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP);
       if (dsock == -1) {
  -        ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  -                     "proxy: error creating PASV socket");
  -        ap_bclose(f);
  -        ap_kill_timeout(r);
  -        return HTTP_INTERNAL_SERVER_ERROR;
  +        return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                      ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, 
  +                                    "proxy: error creating PASV socket"));
       }
   
   #if !defined (TPF) && !defined(BEOS)
  @@ -826,8 +948,8 @@
       }
   #endif
   
  -    ap_bputs("PASV" CRLF, f);
  -    ap_bflush(f);
  +    ap_bputs("PASV" CRLF, ctrl);
  +    ap_bflush(ctrl);
       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: PASV command issued");
   /* possible results: 227, 421, 500, 501, 502, 530 */
       /* 227 Entering Passive Mode (h1,h2,h3,h4,p1,p2). */
  @@ -836,14 +958,12 @@
       /* 501 Syntax error in parameters or arguments. */
       /* 502 Command not implemented. */
       /* 530 Not logged in. */
  -    i = ap_bgets(pasv, sizeof(pasv), f);
  -    if (i == -1) {
  -        ap_log_rerror(APLOG_MARK, APLOG_ERR|APLOG_NOERRNO, r,
  -                     "PASV: control connection is toast");
  -        ap_pclosesocket(p, dsock);
  -        ap_bclose(f);
  -        ap_kill_timeout(r);
  -        return HTTP_INTERNAL_SERVER_ERROR;
  +
  +    i = ap_bgets(pasv, sizeof(pasv), ctrl);
  +    if (i == -1 || i == 421) {
  +        return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                      ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, 
  +                                    "proxy: PASV: control connection is toast"));
       }
       else {
           pasv[i - 1] = '\0';
  @@ -877,87 +997,81 @@
               i = ap_proxy_doconnect(dsock, &data_addr, r);
   
               if (i == -1) {
  -                ap_kill_timeout(r);
  -                return ap_proxyerror(r, HTTP_BAD_GATEWAY,
  -                                     ap_pstrcat(r->pool,
  -                                                "Could not connect to remote machine: ",
  -                                                strerror(errno), NULL));
  -            }
  -            else {
  -                pasvmode = 1;
  +                return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                          ap_proxyerror(r, HTTP_BAD_GATEWAY,
  +                             ap_pstrcat(r->pool,
  +                                        "Could not connect to remote machine: ",
  +                                        strerror(errno), NULL)));
               }
  +            pasvmode = 1;
           }
  -        else
  +        else {
               ap_pclosesocket(p, dsock);  /* and try the regular way */
  +            dsock = -1;
  +        }
       }
   
       if (!pasvmode) {            /* set up data connection */
           clen = sizeof(struct sockaddr_in);
           if (getsockname(sock, (struct sockaddr *) &server, &clen) < 0) {
  -            ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  -                         "proxy: error getting socket address");
  -            ap_bclose(f);
  -            ap_kill_timeout(r);
  -            return HTTP_INTERNAL_SERVER_ERROR;
  +            return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                      ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, 
  +                                    "proxy: error getting socket address"));
           }
   
           dsock = ap_psocket(p, PF_INET, SOCK_STREAM, IPPROTO_TCP);
           if (dsock == -1) {
  -            ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  -                         "proxy: error creating socket");
  -            ap_bclose(f);
  -            ap_kill_timeout(r);
  -            return HTTP_INTERNAL_SERVER_ERROR;
  +            return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                      ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, 
  +                                    "proxy: error creating socket"));
           }
   
           if (setsockopt(dsock, SOL_SOCKET, SO_REUSEADDR, (void *) &one,
                          sizeof(one)) == -1) {
   #ifndef _OSD_POSIX /* BS2000 has this option "always on" */
  -            ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  -                         "proxy: error setting reuseaddr option");
  -            ap_pclosesocket(p, dsock);
  -            ap_bclose(f);
  -            ap_kill_timeout(r);
  -            return HTTP_INTERNAL_SERVER_ERROR;
  +            return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                      ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, 
  +                                    "proxy: error setting reuseaddr option"));
   #endif /*_OSD_POSIX*/
           }
   
           if (bind(dsock, (struct sockaddr *) &server,
                    sizeof(struct sockaddr_in)) == -1) {
  -            char buff[22];
   
  -            ap_snprintf(buff, sizeof(buff), "%s:%d", inet_ntoa(server.sin_addr), server.sin_port);
  -            ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  -                         "proxy: error binding to ftp data socket %s", buff);
  -            ap_bclose(f);
  -            ap_pclosesocket(p, dsock);
  -            return HTTP_INTERNAL_SERVER_ERROR;
  +            return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                      ap_proxyerror(r, HTTP_INTERNAL_SERVER_ERROR, 
  +                        ap_psprintf(p, "proxy: error binding to ftp data socket %s:%d",
  +                           inet_ntoa(server.sin_addr), server.sin_port)));
           }
           listen(dsock, 2);        /* only need a short queue */
       }
   
   /* set request; "path" holds last path component */
       len = decodeenc(path);
  +    if (strchr(path, '/')) /* were there any '/' characters? */
  +        return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                    ap_proxyerror(r, HTTP_BAD_REQUEST,
  +                                  "Use of %2F is only allowed at the base directory"));
   
       /* TM - if len == 0 then it must be a directory (you can't RETR nothing) */
   
       if (len == 0) {
  -        parms = "d";
  +        get_dirlisting = 1;
       }
       else {
  -        ap_bvputs(f, "SIZE ", path, CRLF, NULL);
  -        ap_bflush(f);
  +        ap_bvputs(ctrl, "SIZE ", path, CRLF, NULL);
  +        ap_bflush(ctrl);
           ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: SIZE %s", path);
  -        i = ftp_getrc_msg(f, resp, sizeof resp);
  +        i = ftp_getrc_msg(ctrl, resp, sizeof resp);
           ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d with response %s", i, resp);
           if (i != 500) {         /* Size command not recognized */
               if (i == 550) {     /* Not a regular file */
                   ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: SIZE shows this is a directory");
  -                parms = "d";
  -                ap_bvputs(f, "CWD ", path, CRLF, NULL);
  -                ap_bflush(f);
  +                get_dirlisting = 1;
  +                ap_bvputs(ctrl, "CWD ", path, CRLF, NULL);
  +                ap_bflush(ctrl);
                   ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: CWD %s", path);
  -                i = ftp_getrc(f);
  +
                   /* possible results: 250, 421, 500, 501, 502, 530, 550 */
                   /* 250 Requested file action okay, completed. */
                   /* 421 Service not available, closing control connection. */
  @@ -966,20 +1080,18 @@
                   /* 502 Command not implemented. */
                   /* 530 Not logged in. */
                   /* 550 Requested action not taken. */
  +                i = ftp_getrc(ctrl);
                   ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", i);
  -                if (i == -1) {
  -                    ap_kill_timeout(r);
  -                    return ap_proxyerror(r, HTTP_BAD_GATEWAY,
  -                                         "Error reading from remote server");
  -                }
  -                if (i == 550) {
  -                    ap_kill_timeout(r);
  -                    return HTTP_NOT_FOUND;
  -                }
  -                if (i != 250) {
  -                    ap_kill_timeout(r);
  -                    return HTTP_BAD_GATEWAY;
  -                }
  +                if (i == -1 || i == 421)
  +                    return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                                ap_proxyerror(r, HTTP_BAD_GATEWAY,
  +                                              "Error reading from remote server"));
  +                if (i == 550)
  +                    return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                                                  HTTP_NOT_FOUND);
  +                if (i != 250)
  +                    return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                                                  HTTP_BAD_GATEWAY);
                   path = "";
                   len = 0;
               }
  @@ -994,8 +1106,8 @@
       }
   
   #ifdef AUTODETECT_PWD
  -    ap_bvputs(f, "PWD", CRLF, NULL);
  -    ap_bflush(f);
  +    ap_bvputs(ctrl, "PWD", CRLF, NULL);
  +    ap_bflush(ctrl);
       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: PWD");
   /* responses: 257, 500, 501, 502, 421, 550 */
       /* 257 "<directory-name>" <commentary> */
  @@ -1004,35 +1116,34 @@
       /* 501 Syntax error in parameters or arguments. */
       /* 502 Command not implemented. */
       /* 550 Requested action not taken. */
  -    i = ftp_getrc_msg(f, resp, sizeof resp);
  +    i = ftp_getrc_msg(ctrl, resp, sizeof resp);
       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: PWD returned status %d", i);
  -    if (i == -1 || i == 421) {
  -        ap_kill_timeout(r);
  -        return ap_proxyerror(r, HTTP_BAD_GATEWAY,
  -                             "Error reading from remote server");
  -    }
  -    if (i == 550) {
  -        ap_kill_timeout(r);
  -        return HTTP_NOT_FOUND;
  -    }
  +    if (i == -1 || i == 421)
  +        return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                                ap_proxyerror(r, HTTP_BAD_GATEWAY,
  +                                      "Error reading from remote server"));
  +    if (i == 550)
  +        return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                                      HTTP_NOT_FOUND);
       if (i == 257) {
           const char *dirp = resp;
           cwd = ap_getword_conf(r->pool, &dirp);
       }
   #endif /*AUTODETECT_PWD*/
   
  -    if (parms[0] == 'd') {
  +    if (get_dirlisting) {
           if (len != 0)
  -            ap_bvputs(f, "LIST ", path, CRLF, NULL);
  +            ap_bvputs(ctrl, "LIST ", path, CRLF, NULL);
           else
  -            ap_bputs("LIST -lag" CRLF, f);
  +            ap_bputs("LIST -lag" CRLF, ctrl);
           ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: LIST %s", (len == 0 ? "" : path));
       }
       else {
  -        ap_bvputs(f, "RETR ", path, CRLF, NULL);
  +        ftp_set_TYPE(r, ctrl, xfer_type);
  +        ap_bvputs(ctrl, "RETR ", path, CRLF, NULL);
           ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: RETR %s", path);
       }
  -    ap_bflush(f);
  +    ap_bflush(ctrl);
   /* RETR: 110, 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 530, 550
      NLST: 125, 150, 226, 250, 421, 425, 426, 450, 451, 500, 501, 502, 530 */
       /* 110 Restart marker reply. */
  @@ -1049,18 +1160,19 @@
       /* 501 Syntax error in parameters or arguments. */
       /* 530 Not logged in. */
       /* 550 Requested action not taken. */
  -    rc = ftp_getrc(f);
  +    rc = ftp_getrc(ctrl);
       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", rc);
  -    if (rc == -1) {
  -        ap_kill_timeout(r);
  -        return ap_proxyerror(r, HTTP_BAD_GATEWAY,
  -                             "Error reading from remote server");
  -    }
  +    if (rc == -1 || rc == 421)
  +        return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                                ap_proxyerror(r, HTTP_BAD_GATEWAY,
  +                                      "Error reading from remote server"));
       if (rc == 550) {
           ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: RETR failed, trying LIST instead");
  -        parms = "d";
  -        ap_bvputs(f, "CWD ", path, CRLF, NULL);
  -        ap_bflush(f);
  +        get_dirlisting = 1;
  +        ftp_set_TYPE(r, ctrl, 'A'); /* directories must be transferred in ASCII */
  +
  +        ap_bvputs(ctrl, "CWD ", path, CRLF, NULL);
  +        ap_bflush(ctrl);
           ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: CWD %s", path);
           /* possible results: 250, 421, 500, 501, 502, 530, 550 */
           /* 250 Requested file action okay, completed. */
  @@ -1070,25 +1182,22 @@
           /* 502 Command not implemented. */
           /* 530 Not logged in. */
           /* 550 Requested action not taken. */
  -        rc = ftp_getrc(f);
  +        rc = ftp_getrc(ctrl);
           ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", rc);
  -        if (rc == -1) {
  -            ap_kill_timeout(r);
  -            return ap_proxyerror(r, HTTP_BAD_GATEWAY,
  -                                 "Error reading from remote server");
  -        }
  -        if (rc == 550) {
  -            ap_kill_timeout(r);
  -            return HTTP_NOT_FOUND;
  -        }
  -        if (rc != 250) {
  -            ap_kill_timeout(r);
  -            return HTTP_BAD_GATEWAY;
  -        }
  +        if (rc == -1 || rc == 421)
  +            return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                                ap_proxyerror(r, HTTP_BAD_GATEWAY,
  +                                      "Error reading from remote server"));
  +        if (rc == 550)
  +            return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                                          HTTP_NOT_FOUND);
  +        if (rc != 250)
  +            return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                                          HTTP_BAD_GATEWAY);
   
   #ifdef AUTODETECT_PWD
  -        ap_bvputs(f, "PWD", CRLF, NULL);
  -        ap_bflush(f);
  +        ap_bvputs(ctrl, "PWD", CRLF, NULL);
  +        ap_bflush(ctrl);
           ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: PWD");
   /* responses: 257, 500, 501, 502, 421, 550 */
           /* 257 "<directory-name>" <commentary> */
  @@ -1097,35 +1206,35 @@
           /* 501 Syntax error in parameters or arguments. */
           /* 502 Command not implemented. */
           /* 550 Requested action not taken. */
  -        i = ftp_getrc_msg(f, resp, sizeof resp);
  +        i = ftp_getrc_msg(ctrl, resp, sizeof resp);
           ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: PWD returned status %d", i);
  -        if (i == -1 || i == 421) {
  -            ap_kill_timeout(r);
  -            return ap_proxyerror(r, HTTP_BAD_GATEWAY,
  -                                 "Error reading from remote server");
  -        }
  -        if (i == 550) {
  -            ap_kill_timeout(r);
  -            return HTTP_NOT_FOUND;
  -        }
  +        if (i == -1 || i == 421)
  +            return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                                ap_proxyerror(r, HTTP_BAD_GATEWAY,
  +                                      "Error reading from remote server"));
  +        if (i == 550)
  +            return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                                          HTTP_NOT_FOUND);
           if (i == 257) {
               const char *dirp = resp;
               cwd = ap_getword_conf(r->pool, &dirp);
           }
   #endif /*AUTODETECT_PWD*/
   
  -        ap_bputs("LIST -lag" CRLF, f);
  -        ap_bflush(f);
  +        ap_bputs("LIST -lag" CRLF, ctrl);
  +        ap_bflush(ctrl);
           ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: LIST -lag");
  -        rc = ftp_getrc(f);
  +        rc = ftp_getrc(ctrl);
           ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", rc);
  -        if (rc == -1)
  -            return ap_proxyerror(r, HTTP_BAD_GATEWAY,
  -                                 "Error reading from remote server");
  +        if (rc == -1 || rc == 421)
  +            return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                                ap_proxyerror(r, HTTP_BAD_GATEWAY,
  +                                      "Error reading from remote server"));
       }
       ap_kill_timeout(r);
       if (rc != 125 && rc != 150 && rc != 226 && rc != 250)
  -        return HTTP_BAD_GATEWAY;
  +        return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                                          HTTP_BAD_GATEWAY);
   
       r->status = HTTP_OK;
       r->status_line = "200 OK";
  @@ -1136,7 +1245,7 @@
       ap_table_setn(resp_hdrs, "Date", ap_gm_timestr_822(r->pool, r->request_time));
       ap_table_setn(resp_hdrs, "Server", ap_get_server_version());
   
  -    if (parms[0] == 'd') {
  +    if (get_dirlisting) {
           ap_table_setn(resp_hdrs, "Content-Type", "text/html");
   #ifdef CHARSET_EBCDIC
           r->ebcdic.conv_out = 1; /* server-generated */
  @@ -1153,7 +1262,7 @@
           else {
               ap_table_setn(resp_hdrs, "Content-Type", ap_default_type(r));
           }
  -        if (parms[0] != 'a' && size != NULL) {
  +        if (xfer_type != 'A' && size != NULL) {
               /* We "trust" the ftp server to really serve (size) bytes... */
               ap_table_set(resp_hdrs, "Content-Length", size);
               ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: Content-Length set to %s", size);
  @@ -1180,9 +1289,7 @@
       i = ap_proxy_cache_update(c, resp_hdrs, 0, nocache);
   
       if (i != DECLINED) {
  -        ap_pclosesocket(p, dsock);
  -        ap_bclose(f);
  -        return i;
  +        return ftp_cleanup_and_return(r, ctrl, data, sock, dsock, i);
       }
   
       if (!pasvmode) {            /* wait for connection */
  @@ -1194,14 +1301,11 @@
           if (csd == -1) {
               ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
                            "proxy: failed to accept data connection");
  -            ap_pclosesocket(p, dsock);
  -            ap_bclose(f);
  -            ap_kill_timeout(r);
               if (c != NULL)
                   c = ap_proxy_cache_error(c);
  -            return HTTP_BAD_GATEWAY;
  +            return ftp_cleanup_and_return(r, ctrl, data, sock, dsock,
  +                                          HTTP_BAD_GATEWAY);
           }
  -        ap_note_cleanups_for_socket(p, csd);
           data = ap_bcreate(p, B_RDWR | B_SOCKET);
           ap_bpushfd(data, csd, -1);
           ap_kill_timeout(r);
  @@ -1233,28 +1337,32 @@
   #endif
   /* send body */
       if (!r->header_only) {
  -        if (parms[0] != 'd') {
  +        if (!get_dirlisting) {
   /* we need to set this for ap_proxy_send_fb()... */
               if (c != NULL)
                   c->cache_completion = 0;
               ap_proxy_send_fb(data, r, c, -1, 0);
           } else
               send_dir(data, r, c, cwd);
  +        ap_bclose(data);
  +        data = NULL;
  +        dsock = -1;
   
  +        /* We checked for 125||150||226||250 above.
  +         * See if another rc is pending, and fetch it:
  +         */
           if (rc == 125 || rc == 150)
  -            rc = ftp_getrc(f);
  -
  -        /* XXX: we checked for 125||150||226||250 above. This is redundant. */
  -        if (rc != 226 && rc != 250)
  -            /* XXX: we no longer log an "error writing to c->tempfile" - should we? */
  -            c = ap_proxy_cache_error(c);
  +            rc = ftp_getrc(ctrl);
       }
       else {
  -/* abort the transfer */
  -        ap_bputs("ABOR" CRLF, f);
  -        ap_bflush(f);
  -        if (!pasvmode)
  +/* abort the transfer: we send the header only */
  +        ap_bputs("ABOR" CRLF, ctrl);
  +        ap_bflush(ctrl);
  +        if (data != NULL) {
               ap_bclose(data);
  +            data = NULL;
  +            dsock = -1;
  +        }
           ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: ABOR");
   /* responses: 225, 226, 421, 500, 501, 502 */
       /* 225 Data connection open; no transfer in progress. */
  @@ -1263,7 +1371,7 @@
       /* 500 Syntax error, command unrecognized. */
       /* 501 Syntax error in parameters or arguments. */
       /* 502 Command not implemented. */
  -        i = ftp_getrc(f);
  +        i = ftp_getrc(ctrl);
           ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: returned status %d", i);
       }
   
  @@ -1271,18 +1379,16 @@
       ap_proxy_cache_tidy(c);
   
   /* finish */
  -    ap_bputs("QUIT" CRLF, f);
  -    ap_bflush(f);
  +    ap_bputs("QUIT" CRLF, ctrl);
  +    ap_bflush(ctrl);
       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: QUIT");
   /* responses: 221, 500 */
       /* 221 Service closing control connection. */
       /* 500 Syntax error, command unrecognized. */
  -    i = ftp_getrc(f);
  +    i = ftp_getrc(ctrl);
       ap_log_error(APLOG_MARK, APLOG_DEBUG|APLOG_NOERRNO, r->server, "FTP: QUIT: status %d", i);
   
  -    if (pasvmode)
  -        ap_bclose(data);
  -    ap_bclose(f);
  +    ap_bclose(ctrl);
   
       ap_rflush(r);        /* flush before garbage collection */
   
  
  
  

Mime
View raw message