static apr_status_t proxy_send_dir_filter()

in modules/proxy/mod_proxy_ftp.c [449:777]


static apr_status_t proxy_send_dir_filter(ap_filter_t *f,
                                          apr_bucket_brigade *in)
{
    request_rec *r = f->r;
    conn_rec *c = r->connection;
    apr_pool_t *p = r->pool;
    apr_bucket_brigade *out = apr_brigade_create(p, c->bucket_alloc);
    apr_status_t rv;

    int n;
    char *dir, *path, *reldir, *site, *str, *type;

    const char *pwd = apr_table_get(r->notes, "Directory-PWD");
    const char *readme = apr_table_get(r->notes, "Directory-README");

    proxy_dir_ctx_t *ctx = f->ctx;

    if (!ctx) {
        f->ctx = ctx = apr_pcalloc(p, sizeof(*ctx));
        ctx->in = apr_brigade_create(p, c->bucket_alloc);
        ctx->buffer[0] = 0;
        ctx->state = HEADER;
    }

    /* combine the stored and the new */
    APR_BRIGADE_CONCAT(ctx->in, in);

    if (HEADER == ctx->state) {

        /* basedir is either "", or "/%2f" for the "squid %2f hack" */
        const char *basedir = "";  /* By default, path is relative to the $HOME dir */
        char *wildcard = NULL;
        const char *escpath;

        /*
         * In the reverse proxy case we need to construct our site string
         * via ap_construct_url. For non anonymous sites apr_uri_unparse would
         * only supply us with 'username@' which leads to the construction of
         * an invalid base href later on. Losing the username part of the URL
         * is no problem in the reverse proxy case as the browser sents the
         * credentials anyway once entered.
         */
        if (r->proxyreq == PROXYREQ_REVERSE) {
            site = ap_construct_url(p, "", r);
        }
        else {
            /* Save "scheme://site" prefix without password */
            site = apr_uri_unparse(p, &f->r->parsed_uri,
                                   APR_URI_UNP_OMITPASSWORD |
                                   APR_URI_UNP_OMITPATHINFO);
        }

        /* ... and path without query args */
        path = apr_uri_unparse(p, &f->r->parsed_uri, APR_URI_UNP_OMITSITEPART | APR_URI_UNP_OMITQUERY);

        /* If path began with /%2f, change the basedir */
        if (ap_cstr_casecmpn(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;

        reldir = strrchr(path, '/');
        if (reldir != NULL && ftp_check_globbingchars(reldir)) {
            wildcard = &reldir[1];
            reldir[0] = '\0'; /* strip off the wildcard suffix */
        }

        /* Copy path, strip (all except the last) trailing slashes */
        /* (the trailing slash is needed for the dir component loop below) */
        path = dir = apr_pstrcat(p, path, "/", NULL);
        for (n = strlen(path); n > 1 && path[n - 1] == '/' && path[n - 2] == '/'; --n)
            path[n - 1] = '\0';

        /* Add a link to the root directory (if %2f hack was used) */
        str = (basedir[0] != '\0') ? "<a href=\"/%2f/\">%2f</a>/" : "";

        /* print "ftp://host/" */
        escpath = ap_escape_html(p, path);
        str = apr_psprintf(p, DOCTYPE_HTML_4_01
                "<html>\n <head>\n  <title>%s%s%s</title>\n"
                "<base href=\"%s%s%s\">\n"
                " </head>\n"
                " <body>\n  <h2>Directory of "
                "<a href=\"/\">%s</a>/%s",
                ap_escape_html(p, site), basedir, escpath,
                ap_escape_uri(p, site), basedir, escpath,
                ap_escape_uri(p, site), str);

        APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(str, strlen(str),
                                                          p, c->bucket_alloc));

        for (dir = path+1; (dir = strchr(dir, '/')) != NULL; )
        {
            *dir = '\0';
            if ((reldir = strrchr(path+1, '/'))==NULL) {
                reldir = path+1;
            }
            else
                ++reldir;
            /* print "path/" component */
            str = apr_psprintf(p, "<a href=\"%s%s/\">%s</a>/", basedir,
                        ap_escape_uri(p, path),
                        ap_escape_html(p, reldir));
            *dir = '/';
            while (*dir == '/')
              ++dir;
            APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(str,
                                                           strlen(str), p,
                                                           c->bucket_alloc));
        }
        if (wildcard != NULL) {
            wildcard = ap_escape_html(p, wildcard);
            APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(wildcard,
                                                           strlen(wildcard), p,
                                                           c->bucket_alloc));
        }

        /* If the caller has determined the current directory, and it differs */
        /* from what the client requested, then show the real name */
        if (pwd == NULL || strncmp(pwd, path, strlen(pwd)) == 0) {
            str = apr_psprintf(p, "</h2>\n\n  <hr />\n\n<pre>");
        }
        else {
            str = apr_psprintf(p, "</h2>\n\n(%s)\n\n  <hr />\n\n<pre>",
                               ap_escape_html(p, pwd));
        }
        APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(str, strlen(str),
                                                           p, c->bucket_alloc));

        /* print README */
        if (readme) {
            str = apr_psprintf(p, "%s\n</pre>\n\n<hr />\n\n<pre>\n",
                               ap_escape_html(p, readme));

            APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(str,
                                                           strlen(str), p,
                                                           c->bucket_alloc));
        }

        /* make sure page intro gets sent out */
        APR_BRIGADE_INSERT_TAIL(out, apr_bucket_flush_create(c->bucket_alloc));
        if (APR_SUCCESS != (rv = ap_pass_brigade(f->next, out))) {
            return rv;
        }
        apr_brigade_cleanup(out);

        ctx->state = BODY;
    }

    /* loop through each line of directory */
    while (BODY == ctx->state) {
        char *filename;
        int found = 0;
        int eos = 0;
        ap_regmatch_t re_result[LS_REG_MATCH];

        /* get a complete line */
        /* if the buffer overruns - throw data away */
        while (!found && !APR_BRIGADE_EMPTY(ctx->in)) {
            char *pos, *response;
            apr_size_t len, max;
            apr_bucket *e;

            e = APR_BRIGADE_FIRST(ctx->in);
            if (APR_BUCKET_IS_EOS(e)) {
                eos = 1;
                break;
            }
            if (APR_SUCCESS != (rv = apr_bucket_read(e, (const char **)&response, &len, APR_BLOCK_READ))) {
                return rv;
            }
            pos = memchr(response, APR_ASCII_LF, len);
            if (pos != NULL) {
                if ((response + len) != (pos + 1)) {
                    len = pos - response + 1;
                    apr_bucket_split(e, pos - response + 1);
                }
                found = 1;
            }
            max = sizeof(ctx->buffer) - strlen(ctx->buffer) - 1;
            if (len > max) {
                len = max;
            }

            /* len+1 to leave space for the trailing nil char */
            apr_cpystrn(ctx->buffer+strlen(ctx->buffer), response, len+1);

            apr_bucket_delete(e);
        }

        /* EOS? jump to footer */
        if (eos) {
            ctx->state = FOOTER;
            break;
        }

        /* not complete? leave and try get some more */
        if (!found) {
            return APR_SUCCESS;
        }

        {
            apr_size_t n = strlen(ctx->buffer);
            if (ctx->buffer[n-1] == CRLF[1])  /* strip trailing '\n' */
                ctx->buffer[--n] = '\0';
            if (ctx->buffer[n-1] == CRLF[0])  /* strip trailing '\r' if present */
                ctx->buffer[--n] = '\0';
        }

        /* a symlink? */
        if (ctx->buffer[0] == 'l' && (filename = strstr(ctx->buffer, " -> ")) != NULL) {
            char *link_ptr = filename;

            do {
                filename--;
            } while (filename[0] != ' ' && filename > ctx->buffer);
            if (filename > ctx->buffer)
                *(filename++) = '\0';
            *(link_ptr++) = '\0';
            str = apr_psprintf(p, "%s <a href=\"%s\">%s %s</a>\n",
                               ap_escape_html(p, ctx->buffer),
                               ap_escape_uri(p, filename),
                               ap_escape_html(p, filename),
                               ap_escape_html(p, link_ptr));
        }

        /* a directory/file? */
        else if (ctx->buffer[0] == 'd' || ctx->buffer[0] == '-' || ctx->buffer[0] == 'l' || apr_isdigit(ctx->buffer[0])) {
            int searchidx = 0;
            char *searchptr = NULL;
            int firstfile = 1;
            if (apr_isdigit(ctx->buffer[0])) {  /* handle DOS dir */
                searchptr = strchr(ctx->buffer, '<');
                if (searchptr != NULL)
                    *searchptr = '[';
                searchptr = strchr(ctx->buffer, '>');
                if (searchptr != NULL)
                    *searchptr = ']';
            }

            filename = strrchr(ctx->buffer, ' ');
            if (filename == NULL) {
                /* Line is broken.  Ignore it. */
                ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01034)
                              "proxy_ftp: could not parse line %s",
                              ctx->buffer);
                /* erase buffer for next time around */
                ctx->buffer[0] = 0;
                continue;  /* while state is BODY */
            }
            *(filename++) = '\0';

            /* handle filenames with spaces in 'em */
            if (!strcmp(filename, ".") || !strcmp(filename, "..") || firstfile) {
                firstfile = 0;
                searchidx = filename - ctx->buffer;
            }
            else if (searchidx != 0 && ctx->buffer[searchidx] != 0) {
                *(--filename) = ' ';
                ctx->buffer[searchidx - 1] = '\0';
                filename = &ctx->buffer[searchidx];
            }

            /* Append a slash to the HREF link for directories */
            if (!strcmp(filename, ".") || !strcmp(filename, "..") || ctx->buffer[0] == 'd') {
                str = apr_psprintf(p, "%s <a href=\"%s/\">%s</a>\n",
                                   ap_escape_html(p, ctx->buffer),
                                   ap_escape_uri(p, filename),
                                   ap_escape_html(p, filename));
            }
            else {
                str = apr_psprintf(p, "%s <a href=\"%s\">%s</a>\n",
                                   ap_escape_html(p, ctx->buffer),
                                   ap_escape_uri(p, filename),
                                   ap_escape_html(p, filename));
            }
        }
        /* Try a fallback for listings in the format of "ls -s1" */
        else if (0 == ap_regexec(ls_regex, ctx->buffer, LS_REG_MATCH, re_result, 0)) {
            /*
             * We don't need to check for rm_eo == rm_so == -1 here since ls_regex
             * is such that $2 cannot be unset if we have a match.
             */
            filename = apr_pstrndup(p, &ctx->buffer[re_result[2].rm_so], re_result[2].rm_eo - re_result[2].rm_so);

            str = apr_pstrcat(p, ap_escape_html(p, apr_pstrndup(p, ctx->buffer, re_result[2].rm_so)),
                              "<a href=\"", ap_escape_uri(p, filename), "\">",
                              ap_escape_html(p, filename), "</a>\n", NULL);
        }
        else {
            strcat(ctx->buffer, "\n"); /* re-append the newline */
            str = ap_escape_html(p, ctx->buffer);
        }

        /* erase buffer for next time around */
        ctx->buffer[0] = 0;

        APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(str, strlen(str), p,
                                                            c->bucket_alloc));
        APR_BRIGADE_INSERT_TAIL(out, apr_bucket_flush_create(c->bucket_alloc));
        if (APR_SUCCESS != (rv = ap_pass_brigade(f->next, out))) {
            return rv;
        }
        apr_brigade_cleanup(out);

    }

    if (FOOTER == ctx->state) {
        str = apr_psprintf(p, "</pre>\n\n  <hr />\n\n  %s\n\n </body>\n</html>\n", ap_psignature("", r));
        APR_BRIGADE_INSERT_TAIL(out, apr_bucket_pool_create(str, strlen(str), p,
                                                            c->bucket_alloc));
        APR_BRIGADE_INSERT_TAIL(out, apr_bucket_flush_create(c->bucket_alloc));
        APR_BRIGADE_INSERT_TAIL(out, apr_bucket_eos_create(c->bucket_alloc));
        if (APR_SUCCESS != (rv = ap_pass_brigade(f->next, out))) {
            return rv;
        }
        apr_brigade_destroy(out);
    }

    return APR_SUCCESS;
}