static int index_directory()

in modules/generators/mod_autoindex.c [2025:2291]


static int index_directory(request_rec *r,
                           autoindex_config_rec *autoindex_conf)
{
    char *title_name = ap_escape_html(r->pool, r->uri);
    char *title_endp;
    char *name = r->filename;
    char *pstring = NULL;
    apr_finfo_t dirent;
    apr_dir_t *thedir;
    apr_status_t status;
    int num_ent = 0, x;
    struct ent *head, *p;
    struct ent **ar = NULL;
    const char *qstring;
    apr_int32_t autoindex_opts = autoindex_conf->opts;
    char keyid;
    char direction;
    char *colargs;
    char *fullpath;
    apr_size_t dirpathlen;
    char *ctype = "text/html";
    char *charset;

    if ((status = apr_dir_open(&thedir, name, r->pool)) != APR_SUCCESS) {
        ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r, APLOGNO(01275)
                      "Can't open directory for index: %s", r->filename);
        return HTTP_FORBIDDEN;
    }

    if (autoindex_conf->ctype) {
        ctype = autoindex_conf->ctype;
    }
    if (autoindex_conf->charset) {
        charset = autoindex_conf->charset;
    }
    else {
#if APR_HAS_UNICODE_FS
        charset = "UTF-8";
#else
        charset = "ISO-8859-1";
#endif
    }
    if (*charset) {
        ap_set_content_type_ex(r, apr_pstrcat(r->pool, ctype, ";charset=",
                            charset, NULL), 1);
    }
    else {
        ap_set_content_type_ex(r, ctype, 1);
    }

    if (autoindex_opts & TRACK_MODIFIED) {
        ap_update_mtime(r, r->finfo.mtime);
        ap_set_last_modified(r);
        ap_set_etag(r);
    }
    if (r->header_only) {
        apr_dir_close(thedir);
        return 0;
    }

    /*
     * If there is no specific ordering defined for this directory,
     * default to ascending by filename.
     */
    keyid = autoindex_conf->default_keyid
                ? autoindex_conf->default_keyid : K_NAME;
    direction = autoindex_conf->default_direction
                ? autoindex_conf->default_direction : D_ASCENDING;

    /*
     * Figure out what sort of indexing (if any) we're supposed to use.
     *
     * If no QUERY_STRING was specified or client query strings have been
     * explicitly disabled.
     * If we are ignoring the client, suppress column sorting as well.
     */
    if (autoindex_opts & IGNORE_CLIENT) {
        qstring = NULL;
        autoindex_opts |= SUPPRESS_COLSORT;
        colargs = "";
    }
    else {
        char fval[5], vval[5], *ppre = "", *epattern = "";
        fval[0] = '\0'; vval[0] = '\0';
        qstring = r->args;

        while (qstring && *qstring) {

            /* C= First Sort key Column (N, M, S, D) */
            if (   qstring[0] == 'C' && qstring[1] == '='
                && qstring[2] && strchr(K_VALID, qstring[2])
                && (   qstring[3] == '&' || qstring[3] == ';'
                    || !qstring[3])) {
                keyid = qstring[2];
                qstring += qstring[3] ? 4 : 3;
            }

            /* O= Sort order (A, D) */
            else if (   qstring[0] == 'O' && qstring[1] == '='
                     && (   (qstring[2] == D_ASCENDING)
                         || (qstring[2] == D_DESCENDING))
                     && (   qstring[3] == '&' || qstring[3] == ';'
                         || !qstring[3])) {
                direction = qstring[2];
                qstring += qstring[3] ? 4 : 3;
            }

            /* F= Output Format (0 plain, 1 fancy (pre), 2 table) */
            else if (   qstring[0] == 'F' && qstring[1] == '='
                     && qstring[2] && strchr("012", qstring[2])
                     && (   qstring[3] == '&' || qstring[3] == ';'
                         || !qstring[3])) {
                if (qstring[2] == '0') {
                    autoindex_opts &= ~(FANCY_INDEXING | TABLE_INDEXING);
                }
                else if (qstring[2] == '1') {
                    autoindex_opts = (autoindex_opts | FANCY_INDEXING)
                        & ~TABLE_INDEXING;
                }
                else if (qstring[2] == '2') {
                    autoindex_opts |= FANCY_INDEXING | TABLE_INDEXING;
                }
                strcpy(fval, ";F= ");
                fval[3] = qstring[2];
                qstring += qstring[3] ? 4 : 3;
            }

            /* V= Version sort (0, 1) */
            else if (   qstring[0] == 'V' && qstring[1] == '='
                     && (qstring[2] == '0' || qstring[2] == '1')
                     && (   qstring[3] == '&' || qstring[3] == ';'
                         || !qstring[3])) {
                if (qstring[2] == '0') {
                    autoindex_opts &= ~VERSION_SORT;
                }
                else if (qstring[2] == '1') {
                    autoindex_opts |= VERSION_SORT;
                }
                strcpy(vval, ";V= ");
                vval[3] = qstring[2];
                qstring += qstring[3] ? 4 : 3;
            }

            /* P= wildcard pattern (*.foo) */
            else if (qstring[0] == 'P' && qstring[1] == '=') {
                const char *eos = qstring += 2; /* for efficiency */

                while (*eos && *eos != '&' && *eos != ';') {
                    ++eos;
                }

                if (eos == qstring) {
                    pstring = NULL;
                }
                else {
                    pstring = apr_pstrndup(r->pool, qstring, eos - qstring);
                    if (ap_unescape_url(pstring) != OK) {
                        /* ignore the pattern, if it's bad. */
                        pstring = NULL;
                    }
                    else {
                        ppre = ";P=";
                        /* be correct */
                        epattern = ap_escape_uri(r->pool, pstring);
                    }
                }

                if (*eos && *++eos) {
                    qstring = eos;
                }
                else {
                    qstring = NULL;
                }
            }

            /* Syntax error?  Ignore the remainder! */
            else {
                qstring = NULL;
            }
        }
        colargs = apr_pstrcat(r->pool, fval, vval, ppre, epattern, NULL);
    }

    /* Spew HTML preamble */
    title_endp = title_name + strlen(title_name) - 1;

    while (title_endp > title_name && *title_endp == '/') {
        *title_endp-- = '\0';
    }

    emit_head(r, autoindex_conf->header,
              autoindex_opts & SUPPRESS_PREAMBLE,
              autoindex_opts & EMIT_XHTML, title_name);

    /*
     * Since we don't know how many dir. entries there are, put them into a
     * linked list and then arrayificate them so qsort can use them.
     */
    head = NULL;
    p = make_parent_entry(autoindex_opts, autoindex_conf, r, keyid, direction);
    if (p != NULL) {
        p->next = head;
        head = p;
        num_ent++;
    }
    fullpath = apr_palloc(r->pool, APR_PATH_MAX);
    dirpathlen = strlen(name);
    memcpy(fullpath, name, dirpathlen);

    do {
        status = apr_dir_read(&dirent, APR_FINFO_MIN | APR_FINFO_NAME, thedir);
        if (APR_STATUS_IS_INCOMPLETE(status)) {
            continue; /* ignore un-stat()able files */
        }
        else if (status != APR_SUCCESS) {
            break;
        }

        /* We want to explode symlinks here. */
        if (dirent.filetype == APR_LNK) {
            const char *savename;
            apr_finfo_t fi;
            /* We *must* have FNAME. */
            savename = dirent.name;
            apr_cpystrn(fullpath + dirpathlen, dirent.name,
                        APR_PATH_MAX - dirpathlen);
            status = apr_stat(&fi, fullpath,
                              dirent.valid & ~(APR_FINFO_NAME), r->pool);
            if (status != APR_SUCCESS) {
                /* Something bad happened, skip this file. */
                continue;
            }
            memcpy(&dirent, &fi, sizeof(fi));
            dirent.name = savename;
            dirent.valid |= APR_FINFO_NAME;
        }
        p = make_autoindex_entry(&dirent, autoindex_opts, autoindex_conf, r,
                                 keyid, direction, pstring);
        if (p != NULL) {
            p->next = head;
            head = p;
            num_ent++;
        }
    } while (1);

    if (num_ent > 0) {
        ar = (struct ent **) apr_palloc(r->pool,
                                        num_ent * sizeof(struct ent *));
        p = head;
        x = 0;
        while (p) {
            ar[x++] = p;
            p = p->next;
        }

        qsort((void *) ar, num_ent, sizeof(struct ent *),
              (int (*)(const void *, const void *)) dsortf);
    }
    output_directories(ar, num_ent, autoindex_conf, r, autoindex_opts,
                       keyid, direction, colargs);
    apr_dir_close(thedir);

    emit_tail(r, autoindex_conf->readme,
              autoindex_opts & SUPPRESS_PREAMBLE);

    return 0;
}