Hi there, This is a late follow-on from some posts in May.
mod_autoindex in Apache/2 does not show filenames for which the requester is not authenticated, which is a change from the 1.3 behaviour. The first patch below introduces a per-directory directive which allows the administrator list which http statuses (or status groups) should be shown or hidden. When left unset, I found no clear performance difference in listing a directory where no authentication was required (as expected); and also found no clear performance difference in listing a directory where each file individually required authentication (which was a bit of a surprise to me). Built and tested against vanilla 2.0.43, it applies cleanly to the current CVS version, 1.111. The second patch below is a docs patch against mod_autoindex.xml 1.12. There are two further optional code patches to follow, which are only useful if something similar to these are accepted. Any comments (and further performance testing) welcome. f -- Francis Daly [EMAIL PROTECTED] --- modules/generators/mod_autoindex.c 17 Oct 2002 18:09:52 -0000 +++ modules/generators/mod_autoindex.c 21 Nov 2002 02:33:31 -0000 @@ -154,6 +154,12 @@ int wildcards; } ai_desc_t; +/* res_info is used when merging res_list */ +typedef struct res_info { + int *range; + apr_table_t *tab; +} res_info; + typedef struct autoindex_config_struct { char *default_icon; @@ -175,6 +181,7 @@ apr_array_header_t *ign_list; apr_array_header_t *hdr_list; apr_array_header_t *rdme_list; + apr_table_t *res_list; } autoindex_config_rec; @@ -320,6 +327,45 @@ return NULL; } +static const char *set_results(cmd_parms *cmd, void *d, const char *name) +{ + char entry[4]; + int showp = 1; + + if (name[0] == '-') { + showp = 0; + name++; + } + + /* verify that the form is valid */ + if (name[0] == '\0' || name[1] == '\0' || name[2] == '\0' || + name[3] != '\0') { + return "Value (after leading minus) must be three characters long"; + } + + /* verify that the value is valid */ + if ((name[0] < '1' || name[0] > '9') + || !((isdigit(name[1]) && isdigit(name[2])) + || (name[1] == 'x' && name[2] == 'x'))) { + return "Value must be [-]### or [-]#xx, where # is a digit"; + } + + strcpy(entry, name); + if (name[1] == 'x') { + entry[1] = '\0'; + } + + /* The "value" here is "a string beginning with 1" (for show) or + * "a string not beginning with 1" (for hide), as per show_result() */ + if (showp) { + apr_table_set(((autoindex_config_rec *) d)->res_list, entry, "1"); + } else { + apr_table_set(((autoindex_config_rec *) d)->res_list, entry, "0"); + } + + return NULL; +} + static const char *add_ignore(cmd_parms *cmd, void *d, const char *ext) { push_item(((autoindex_config_rec *) d)->ign_list, 0, ext, cmd->path, NULL); @@ -578,6 +624,8 @@ "one or more file extensions"), AP_INIT_ITERATE2("AddDescription", add_desc, BY_PATH, DIR_CMD_PERMS, "Descriptive text followed by one or more filenames"), + AP_INIT_ITERATE("IndexResults", set_results, NULL, DIR_CMD_PERMS, + "one or more http status codes"), AP_INIT_TAKE1("HeaderName", add_header, NULL, DIR_CMD_PERMS, "a filename"), AP_INIT_TAKE1("ReadmeName", add_readme, NULL, DIR_CMD_PERMS, @@ -590,7 +638,7 @@ {NULL} }; -static void *create_autoindex_config(apr_pool_t *p, char *dummy) +static void *create_autoindex_config(apr_pool_t *p, char *dir) { autoindex_config_rec *new = (autoindex_config_rec *) apr_pcalloc(p, sizeof(autoindex_config_rec)); @@ -607,15 +655,47 @@ new->ign_list = apr_array_make(p, 4, sizeof(struct item)); new->hdr_list = apr_array_make(p, 4, sizeof(struct item)); new->rdme_list = apr_array_make(p, 4, sizeof(struct item)); + new->res_list = apr_table_make(p, 4); new->opts = 0; new->incremented_opts = 0; new->decremented_opts = 0; new->default_keyid = '\0'; new->default_direction = '\0'; + /* include, effectively, a global "IndexResults 3xx" */ + if (dir == NULL) { + apr_table_set(new->res_list, "3", "1"); + } + return (void *) new; } +static int res_list_ranges(int *range, const char *key, const char *dummy) +{ + int ikey; + + ikey = atoi(key); + if (ikey < 10) { + range[ikey] = 1; + range[0] = 1; + } + return 1; +} + +static int res_list_del_entries(res_info *info, const char *key, + const char *dummy) +{ + int ikey; + + ikey = atoi(key); + if (ikey >= 100) { + if ((*info).range[ikey/100] == 1) { + apr_table_unset((*info).tab, key); + } + } + return 1; +} + static void *merge_autoindex_configs(apr_pool_t *p, void *basev, void *addv) { autoindex_config_rec *new; @@ -634,6 +714,37 @@ new->desc_list = apr_array_append(p, add->desc_list, base->desc_list); new->icon_list = apr_array_append(p, add->icon_list, base->icon_list); new->rdme_list = apr_array_append(p, add->rdme_list, base->rdme_list); + if (apr_is_empty_table(add->res_list)) { + /* Nothing new here */ + new->res_list = base->res_list; + } else { + /* + * For each new range, unset any old keys within the range. + * Then copy in all the new keys. + */ + int range[10] = {0}; + res_info info; + + new->res_list = apr_table_copy(p, base->res_list); + + /* This sets range[R] to 1 if the range Rxx is to be merged + * and range[0] to 1 if any others are set + */ + apr_table_do((int (*) (void *, const char *, const char *)) + res_list_ranges, &range, add->res_list, NULL); + + info.range = range; + info.tab = new->res_list; + /* This deletes each new{Rnn} if the range R is being merged */ + if (range[0] == 1) { + apr_table_do((int (*) (void *, const char *, const char *)) + res_list_del_entries, &info, new->res_list, NULL); + } + + /* This replaces-or-adds from add-> to new-> */ + apr_table_overlap(new->res_list, add->res_list, APR_OVERLAP_TABLES_SET); + } + if (add->opts & NO_OPTIONS) { /* * If the current directory says 'no options' then we also @@ -706,6 +817,7 @@ : base->default_keyid; new->default_direction = add->default_direction ? add->default_direction : base->default_direction; + return new; } @@ -795,6 +907,24 @@ #define find_default_icon(d,n) find_default_item(n, d->icon_list) #define find_default_alt(d,n) find_default_item(n, d->alt_list) +static int show_result(autoindex_config_rec *d, int status) +{ + char stat[4]; /* HTTP codes are 3 digits long */ + const char *res; + + /* Check for exact match */ + sprintf(stat, "%d", status); + if ((res = apr_table_get(d->res_list, stat)) != NULL) { + return res[0] == '1'; + } + /* Check for within-range match */ + stat[1] = '\0'; + if ((res = apr_table_get(d->res_list, stat)) != NULL) { + return res[0] == '1'; + } + return 0; +} + /* * Look through the list of pattern/description pairs and return the first one * if any) that matches the filename in the request. If multiple patterns @@ -1316,7 +1446,7 @@ if ((rr->finfo.filetype != APR_DIR && rr->finfo.filetype != APR_REG) || !(rr->status == OK || ap_is_HTTP_SUCCESS(rr->status) - || ap_is_HTTP_REDIRECT(rr->status))) { + || show_result(d, rr->status))) { ap_destroy_sub_req(rr); return (NULL); } --- mod_autoindex.xml-1.12 Thu Nov 21 01:39:04 2002 +++ mod_autoindex.xml Thu Nov 21 01:50:14 2002 @@ -846,6 +846,41 @@ </directivesynopsis> <directivesynopsis> +<name>IndexResults</name> +<description>Changes the list of files to show in a directory index, +based on the HTTP status</description> +<syntax>IndexResults <var>[-]code</var> [<var>[-]code</var>] ...</syntax> +<default>IndexResults 3xx<default> +<contextlist><context>server config</context><context>virtual host</context> +<context>directory</context><context>.htaccess</context> +</contextlist> +<override>Indexes</override> +<compatibility>Available in version 2.0.44 and later</compatibility> + +<usage> + <p>The <directive>IndexResults</directive> directive changes the + list of files to show in a directory index. <var>code</var> + is a HTTP status (from 100 to 999)(like <code>401</code>, for + "unauthorized"), or a range (from 1xx to 9xx)(like <code>4xx</code>, + meaning "all 400-series statuses") for additional files to show. + Prefix <var>code</var> with a minus (-) to explicitly hide that + status or range. Multiple IndexResults directives are processed + by first modifying the ranges (in order), and then the individual + statuses (in order). The <code>2xx</code> ("successful") range is + always present. By default, the list contains the <code>3xx</code> + range ("redirection"), but that can be explicitly overridden by a + <code>-3xx</code> if that is really wanted.</p> + + <example><title>Show unauthorized filenames</title> + IndexResults 401 + </example> + <example><title>Show unauthorized filenames, but no other 4xx ones</title> + IndexResults 401 -4xx + </example> +</usage> +</directivesynopsis> + +<directivesynopsis> <name>ReadmeName</name> <description>Name of the file that will be inserted at the end of the index listing</description>