mod_autoindex.c revision 72360798a27566f28e2cbee0e39a0a6e3c3bd729
/* ====================================================================
* The Apache Software License, Version 1.1
*
* Copyright (c) 2000-2001 The Apache Software Foundation. All rights
* reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* distribution.
*
* 3. The end-user documentation included with the redistribution,
* if any, must include the following acknowledgment:
* "This product includes software developed by the
* Apache Software Foundation (http://www.apache.org/)."
* Alternately, this acknowledgment may appear in the software itself,
* if and wherever such third-party acknowledgments normally appear.
*
* 4. The names "Apache" and "Apache Software Foundation" must
* not be used to endorse or promote products derived from this
* software without prior written permission. For written
* permission, please contact apache@apache.org.
*
* 5. Products derived from this software may not be called "Apache",
* nor may "Apache" appear in their name, without prior written
* permission of the Apache Software Foundation.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
* ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
* OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
* ====================================================================
*
* This software consists of voluntary contributions made by many
* individuals on behalf of the Apache Software Foundation. For more
* information on the Apache Software Foundation, please see
*
* Portions of this software are based upon public domain software
* originally written at the National Center for Supercomputing Applications,
* University of Illinois, Urbana-Champaign.
*/
/*
* mod_autoindex.c: Handles the on-the-fly html index generation
*
* Rob McCool
* 3/23/93
*
* Adapted to Apache by rst.
*
* Version sort added by Martin Pool <mbp@humbug.org.au>.
*/
#include "apr_strings.h"
#include "apr_fnmatch.h"
#include "apr_strings.h"
#include "apr_lib.h"
#define APR_WANT_STRFUNC
#include "apr_want.h"
#include "ap_config.h"
#include "httpd.h"
#include "http_config.h"
#include "http_core.h"
#include "http_request.h"
#include "http_protocol.h"
#include "http_log.h"
#include "http_main.h"
#include "util_script.h"
#include "mod_core.h"
/****************************************************************
*
* Handling configuration directives...
*/
#define HRULE 1
#define NO_HRULE 0
#define FRONT_MATTER 1
#define END_MATTER 0
#define ICONS_ARE_LINKS 2
#define SCAN_HTML_TITLES 4
#define SUPPRESS_LAST_MOD 8
#define SUPPRESS_SIZE 16
#define SUPPRESS_DESC 32
#define SUPPRESS_PREAMBLE 64
#define SUPPRESS_COLSORT 128
#define NO_OPTIONS 256
#define VERSION_SORT 512
#define K_PAD 1
#define K_NOPAD 0
#define K_NOADJUST 0
#define K_ADJUST 1
#define K_UNSET 2
/*
* Define keys for sorting.
*/
#define D_ASCENDING 'A'
#define D_DESCENDING 'D'
/*
* These are the dimensions of the default icons supplied with Apache.
*/
#define DEFAULT_ICON_WIDTH 20
#define DEFAULT_ICON_HEIGHT 22
/*
* Other default dimensions.
*/
#define DEFAULT_NAME_WIDTH 23
struct item {
char *type;
char *apply_to;
char *apply_path;
char *data;
};
typedef struct ai_desc_t {
char *pattern;
char *description;
int full_path;
int wildcards;
} ai_desc_t;
typedef struct autoindex_config_struct {
char *default_icon;
int opts;
int incremented_opts;
int decremented_opts;
int name_width;
int name_adjust;
int icon_width;
int icon_height;
char *default_order;
#define BY_ENCODING &c_by_encoding
/*
* Return true if the specified string refers to the parent directory (i.e.,
* matches ".." or "../"). Hopefully this one call is significantly less
* expensive than multiple strcmp() calls.
*/
{
/*
* Now, IFF the first two bytes are dots, and the third byte is either
* EOS (\0) or a slash followed by EOS, we have a match.
*/
return 1;
}
return 0;
}
/*
* This routine puts the standard HTML header at the top of the index page.
* We include the DOCTYPE because we may be using features therefrom (i.e.,
* HEIGHT and WIDTH attributes on the icons if we're FancyIndexing).
*/
{
"<HTML>\n <HEAD>\n <TITLE>Index of ", title,
"</TITLE>\n </HEAD>\n <BODY>\n", NULL);
}
{
if (!to) {
to = "";
}
if (!path) {
path = "";
}
}
else if (to) {
}
else {
}
}
const char *to)
{
to = "^^DIRECTORY^^";
}
}
}
return NULL;
}
const char *to)
{
if (icon[0] == '(') {
char *alt;
return "missing closing paren";
}
}
to = "^^DIRECTORY^^";
}
}
}
return NULL;
}
/*
* Add description text for a filename pattern. If the pattern has
* wildcards already (or we need to add them), add leading and
* trailing wildcards to it to ensure substring processing. If the
* pattern contains a '/' anywhere, force wildcard matching mode,
* the matching routine simplifies to just the actual filename
* whenever it can. This allows definitions in parent directories
* to be made for files in subordinate ones using relative paths.
*/
/*
* Absent a strcasestr() function, we have to force wildcards on
* systems for which "AAA" and "aaa" mean the same file.
*/
#ifdef CASE_BLIND_FILESYSTEM
#define WILDCARDS_REQUIRED 1
#else
#define WILDCARDS_REQUIRED 0
#endif
const char *to)
{
char *prefix = "";
|| desc_entry->full_path
|| apr_is_fnmatch(to));
if (desc_entry->wildcards) {
}
else {
}
return NULL;
}
{
return NULL;
}
{
name);
return NULL;
}
{
name);
return NULL;
}
/* A legacy directive, FancyIndexing is superseded by the IndexOptions
* keyword. But for compatibility..
*/
{
int curopts;
int newopts;
cfg = (autoindex_config_rec *) d;
if (curopts & NO_OPTIONS) {
return "FancyIndexing directive conflicts with existing "
"IndexOptions None";
}
return NULL;
}
{
char *w;
int opts;
int opts_add;
int opts_remove;
char action;
while (optstr[0]) {
int option = 0;
if ((*w == '+') || (*w == '-')) {
action = *(w++);
}
else {
action = '\0';
}
if (!strcasecmp(w, "FancyIndexing")) {
}
else if (!strcasecmp(w, "IconsAreLinks")) {
}
else if (!strcasecmp(w, "ScanHTMLTitles")) {
}
else if (!strcasecmp(w, "SuppressLastModified")) {
}
else if (!strcasecmp(w, "SuppressSize")) {
}
else if (!strcasecmp(w, "SuppressDescription")) {
}
else if (!strcasecmp(w, "SuppressHTMLPreamble")) {
}
else if (!strcasecmp(w, "SuppressColumnSorting")) {
}
else if (!strcasecmp(w, "VersionSort")) {
}
else if (!strcasecmp(w, "None")) {
if (action != '\0') {
return "Cannot combine '+' or '-' with 'None' keyword";
}
opts = NO_OPTIONS;
opts_add = 0;
opts_remove = 0;
}
else if (!strcasecmp(w, "IconWidth")) {
if (action != '-') {
}
else {
d_cfg->icon_width = 0;
}
}
if (action == '-') {
return "Cannot combine '-' with IconWidth=n";
}
}
else if (!strcasecmp(w, "IconHeight")) {
if (action != '-') {
}
else {
d_cfg->icon_height = 0;
}
}
if (action == '-') {
return "Cannot combine '-' with IconHeight=n";
}
}
else if (!strcasecmp(w, "NameWidth")) {
if (action != '-') {
return "NameWidth with no value may only appear as "
"'-NameWidth'";
}
}
if (action == '-') {
return "Cannot combine '-' with NameWidth=n";
}
if (w[10] == '*') {
}
else {
if (width < 5) {
return "NameWidth value must be greater than 5";
}
}
}
else {
return "Invalid directory indexing option";
}
if (action == '\0') {
opts_add = 0;
opts_remove = 0;
}
else if (action == '+') {
opts_remove &= ~option;
}
else {
opts_remove |= option;
}
}
return "Cannot combine other IndexOptions keywords with 'None'";
}
return NULL;
}
const char *key)
{
char temp[4];
}
}
else {
return "First keyword must be 'Ascending' or 'Descending'";
}
}
temp[0] = K_LAST_MOD;
}
}
}
else {
return "Second keyword must be 'Name', 'Date', 'Size', or "
"'Description'";
}
}
return NULL;
}
#define DIR_CMD_PERMS OR_INDEXES
static const command_rec autoindex_cmds[] =
{
"an icon URL followed by one or more filenames"),
"an icon URL followed by one or more MIME types"),
"an icon URL followed by one or more content encodings"),
"alternate descriptive text followed by one or more filenames"),
"alternate descriptive text followed by one or more MIME types"),
"alternate descriptive text followed by one or more content encodings"),
"one or more index options"),
"{Ascending,Descending} {Name,Size,Description,Date}"),
"one or more file extensions"),
"Descriptive text followed by one or more filenames"),
"a filename"),
"a filename"),
"Limited to 'on' or 'off' (superseded by IndexOptions FancyIndexing)"),
DIR_CMD_PERMS, "an icon URL"),
{NULL}
};
{
new->icon_width = 0;
new->icon_height = 0;
new->incremented_opts = 0;
new->decremented_opts = 0;
return (void *) new;
}
{
: base->default_icon;
/*
* If the current directory says 'no options' then we also
* clear any incremental mods from being inheritable further down.
*/
new->incremented_opts = 0;
new->decremented_opts = 0;
}
else {
/*
* If there were any nonincremental options selected for
* this directory, they dominate and we don't inherit *anything.*
* Contrariwise, we *do* inherit if the only settings here are
* incremental ones.
*/
| add->incremented_opts)
& ~add->decremented_opts;
| add->decremented_opts);
/*
* We may have incremental settings, so make sure we don't
* inadvertently inherit an IndexOptions None from above.
*/
}
else {
/*
* There are local nonincremental settings, which clear
* all inheritance from above. They *are* the new base settings.
*/
}
/*
* We're guaranteed that there'll be no overlap between
* the add-options and the remove-options.
*/
}
/*
* Inherit the NameWidth settings if there aren't any specific to
* the new location; otherwise we'll end up using the defaults set in the
* config-rec creation routine.
*/
}
else {
}
return new;
}
/****************************************************************
*
* Looking things up in config entries...
*/
/* Structure used to hold entries when we're actually building an index */
struct ent {
char *name;
char *icon;
char *alt;
char *desc;
int ascending, version_sort;
char key;
};
{
const char *content_encoding = r->content_encoding;
int i;
/* Special cased for ^^DIRECTORY^^ and ^^BLANKICON^^ */
if (!*(p->apply_to)) {
return p->data;
}
return p->data;
}
}
else if (!path_only) {
if (!content_encoding) {
if (content_type
p->apply_to)) {
return p->data;
}
}
}
else {
if (p->type == BY_ENCODING) {
p->apply_to)) {
return p->data;
}
}
}
}
}
}
return NULL;
}
{
request_rec r;
/* Bleah. I tried to clean up find_item, and it lead to this bit
* of ugliness. Note that the fields initialized are precisely
* those that find_item looks at...
*/
r.filename = bogus_name;
}
/*
* 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
* match, only the first one is used; since the order in the array is the
* same as the order in which directives were processed, earlier matching
* directives will dominate.
*/
#ifdef CASE_BLIND_FILESYSTEM
#define MATCH_FLAGS FNM_CASE_BLIND
#else
#define MATCH_FLAGS 0
#endif
{
int i;
const char *filename_full = r->filename;
const char *filename_only;
const char *filename;
/*
* If the filename includes a path, extract just the name itself
* for the simple matches.
*/
}
else {
}
int found;
/*
* Only use the full-path filename if the pattern contains '/'s.
*/
/*
* Make the comparison using the cheapest method; only do
* wildcard checking if we must.
*/
}
else {
}
if (found) {
return tuple->description;
}
}
return NULL;
}
{
char *tt;
int i;
}
else {
tt++;
}
char *ap;
}
else {
ap++;
}
#ifndef CASE_BLIND_FILESYSTEM
return 1;
}
#else /* !CASE_BLIND_FILESYSTEM */
/*
* On some platforms, the match must be case-blind. This is really
* a factor of the filesystem involved, but we can't detect that
* reliably - so we have to granularise at the OS level.
*/
return 1;
}
#endif /* !CASE_BLIND_FILESYSTEM */
}
return 0;
}
/*****************************************************************
*
* Actually generating output
*/
/*
* Elements of the emitted document:
* Preamble
* Emitted unless SUPPRESS_PREAMBLE is set AND ap_run_sub_req
* Header file
* Emitted if found (and able).
* H1 tag line
* Emitted if a header file is NOT emitted.
* Directory stuff
* Always emitted.
* HR
* Emitted if FANCY_INDEXING is set.
* Readme file
* Emitted if found (and able).
* ServerSig
* Emitted if ServerSignature is not Off AND a readme file
* is NOT emitted.
* Postamble
* Emitted unless SUPPRESS_PREAMBLE is set AND ap_run_sub_req
*/
/*
* emit a plain text file
*/
{
int i, c, ch;
apr_size_t n;
ap_rputs("<PRE>\n", r);
while (!apr_file_eof(f)) {
do {
n = sizeof(char) * AP_IOBUFSIZE;
}
if (n == -1 || n == 0) {
break;
}
buf[n] = '\0';
c = 0;
while (c < n) {
for (i = c; i < n; i++) {
break;
}
}
buf[i] = '\0';
if (ch == '<') {
ap_rputs("<", r);
}
else if (ch == '>') {
ap_rputs(">", r);
}
else if (ch == '&') {
ap_rputs("&", r);
}
c = i + 1;
}
}
ap_rputs("</PRE>\n", r);
}
/*
* Handle the preamble through the H1 tag line, inclusive. Locate
* and any other content type is ignored. This means that a non-text
* document (such as HEADER.gif) might get multiviewed as the result
* instead of a text document, meaning nothing will be displayed, but
* oh well.
*/
char *title)
{
apr_file_t *f = NULL;
int emit_amble = 1;
int emit_H1 = 1;
/*
* If there's a header file, send a subrequest to look for it. If it's
* found and a text file, handle it -- otherwise fall through and
* pretend there's nothing there.
*/
if ((header_fname != NULL)
/*
* text/anything-else. The former is allowed to be processed for
* SSIs.
*/
/* Hope everything will work... */
emit_amble = 0;
emit_H1 = 0;
if (! suppress_amble) {
emit_preamble(r, title);
}
/*
* If there's a problem running the subrequest, display the
* preamble if we didn't do it before -- the header file
* didn't get displayed.
*/
/* It didn't work */
emit_H1 = 1;
}
}
/*
* If we can open the file, prefix it with the preamble
* regardless; since we'll be sending a <PRE> block around
* the file's contents, any HTML header it had won't end up
* where it belongs.
*/
emit_preamble(r, title);
emit_amble = 0;
do_emit_plain(r, f);
apr_file_close(f);
emit_H1 = 0;
}
}
}
}
if (emit_amble) {
emit_preamble(r, title);
}
if (emit_H1) {
}
}
}
/*
* Handle the Readme file through the postamble, inclusive. Locate
* and any other content type is ignored. This means that a non-text
* document (such as FOOTER.gif) might get multiviewed as the result
* instead of a text document, meaning nothing will be displayed, but
* oh well.
*/
{
apr_file_t *f = NULL;
int suppress_post = 0;
int suppress_sig = 0;
/*
* If there's a readme file, send a subrequest to look for it. If it's
* found and a text file, handle it -- otherwise fall through and
* pretend there's nothing there.
*/
if ((readme_fname != NULL)
/*
* text/anything-else. The former is allowed to be processed for
* SSIs.
*/
/* worked... */
suppress_sig = 1;
}
}
/*
* If we can open the file, suppress the signature.
*/
do_emit_plain(r, f);
apr_file_close(f);
suppress_sig = 1;
}
}
}
}
if (!suppress_sig) {
}
if (!suppress_post) {
ap_rputs("</BODY></HTML>\n", r);
}
}
}
static char *find_title(request_rec *r)
{
int x, y, p;
apr_size_t n;
return NULL;
}
if ((r->content_type != NULL)
&& !r->content_encoding) {
return NULL;
}
n = sizeof(char) * (MAX_STRING_LEN - 1);
if (n <= 0) {
return NULL;
}
titlebuf[n] = '\0';
for (x = 0, p = 0; titlebuf[x]; x++) {
if (!find[++p]) {
titlebuf[x + p] = '\0';
}
/* Scan for line breaks for Tanmoy's secretary */
for (y = x; titlebuf[y]; y++) {
if (y == x) {
x++;
}
else {
titlebuf[y] = ' ';
}
}
}
}
}
else {
p = 0;
}
}
}
return NULL;
}
request_rec *r, char keyid,
char direction)
{
struct ent *p;
return (NULL);
}
return (NULL);
}
p->size = -1;
p->lm = -1;
if (autoindex_opts & FANCY_INDEXING) {
}
p->alt = "DIR";
}
p->size = -1;
}
else {
}
}
}
}
/*
* We don't need to take any special action for the file size key. If
* we did, it would go here.
*/
if (keyid == K_LAST_MOD) {
if (p->lm < 0) {
p->lm = 0;
}
}
return (p);
}
int autoindex_opts)
{
int maxsize = 23;
register int x;
if (autoindex_opts & SUPPRESS_LAST_MOD) {
maxsize += 19;
}
if (autoindex_opts & SUPPRESS_SIZE) {
maxsize += 7;
}
if (desc[x] == '<') {
while (desc[x] != '>') {
if (!desc[x]) {
maxsize = 0;
break;
}
++x;
}
}
else if (desc[x] == '&') {
/* entities like ä count as one character */
--maxsize;
for ( ; desc[x] != ';'; ++x) {
if (desc[x] == '\0') {
maxsize = 0;
break;
}
}
}
else {
--maxsize;
}
}
}
return desc;
}
/*
* Emit the anchor for the specified field. If a field is the key for the
* current request, the link changes its meaning to reverse the order when
* selected again. Non-active fields always start in ascending order.
*/
char curdirection, int nosort)
{
char qvalue[5];
int reverse;
if (!nosort) {
qvalue[0] = '?';
}
else {
}
}
autoindex_config_rec *d, request_rec *r,
{
int x;
char *tp;
int name_width;
char *name_scratch;
char *pad_scratch;
if (name[0] == '\0') {
name = "/";
}
name_width = d->name_width;
if (d->name_adjust == K_ADJUST) {
for (x = 0; x < n; x++) {
if (t > name_width) {
name_width = t;
}
}
}
if (autoindex_opts & FANCY_INDEXING) {
ap_rputs("<PRE>", r);
"\" ALT=\" \"", NULL);
if (d->icon_width && d->icon_height) {
(
r,
" HEIGHT=\"%d\" WIDTH=\"%d\"",
d->icon_height,
d->icon_width
);
}
ap_rputs("> ", r);
}
/*
* Emit the guaranteed-at-least-one-space-between-columns byte.
*/
ap_rputs(" ", r);
if (!(autoindex_opts & SUPPRESS_LAST_MOD)) {
ap_rputs(" ", r);
}
if (!(autoindex_opts & SUPPRESS_SIZE)) {
ap_rputs(" ", r);
}
if (!(autoindex_opts & SUPPRESS_DESC)) {
}
ap_rputs("\n<HR>\n", r);
}
else {
ap_rputs("<UL>", r);
}
for (x = 0; x < n; x++) {
int nwidth;
ap_getparents(t);
if (t[0] == '\0') {
t = "/";
}
t2 = "Parent Directory";
}
else {
t2 = t;
}
if (autoindex_opts & FANCY_INDEXING) {
if (autoindex_opts & ICONS_ARE_LINKS) {
}
ap_rvputs(r, "<IMG SRC=\"",
: d->default_icon),
"]\"", NULL);
if (d->icon_width && d->icon_height) {
ap_rprintf(r, " HEIGHT=\"%d\" WIDTH=\"%d\"",
d->icon_height, d->icon_width);
}
ap_rputs(">", r);
}
if (autoindex_opts & ICONS_ARE_LINKS) {
ap_rputs("</A>", r);
}
if (nwidth > name_width) {
name_scratch[name_width] = 0;
t2 = name_scratch;
nwidth = name_width;
}
NULL);
/*
* The blank before the storm.. er, before the next field.
*/
ap_rputs(" ", r);
if (!(autoindex_opts & SUPPRESS_LAST_MOD)) {
char time_str[MAX_STRING_LEN];
"%d-%b-%Y %H:%M ", &ts);
}
else {
/*Length="22-Feb-1998 23:42 " (see 4 lines above) */
ap_rputs(" ", r);
}
}
if (!(autoindex_opts & SUPPRESS_SIZE)) {
ap_rputs(" ", r);
}
if (!(autoindex_opts & SUPPRESS_DESC)) {
autoindex_opts), r);
}
}
}
else {
"</A>", NULL);
}
ap_rputc('\n', r);
}
if (autoindex_opts & FANCY_INDEXING) {
ap_rputs("</PRE>", r);
}
else {
ap_rputs("</UL>", r);
}
}
/*
* Compare two file entries according to the sort criteria. The return
* is essentially a signum function value.
*/
{
int result = 0;
/*
* First, see if either of the entries is for the parent directory.
* If so, that *always* sorts lower than anything else.
*/
return -1;
}
return 1;
}
/*
* All of our comparisons will be of the c1 entry against the c2 one,
* so assign them appropriately to take care of the ordering.
*/
}
else {
}
case K_LAST_MOD:
return 1;
}
return -1;
}
break;
case K_SIZE:
return 1;
}
return -1;
}
break;
case K_DESC:
if (c1->version_sort)
else
if (result) {
return result;
}
break;
}
if (c1->version_sort)
else
}
static int index_directory(request_rec *r,
{
char *title_endp;
int num_ent = 0, x;
const char *qstring;
char keyid;
char direction;
"Can't open directory for index: %s", r->filename);
return HTTP_FORBIDDEN;
}
r->content_type = "text/html;charset=utf-8";
#else
r->content_type = "text/html";
#endif
ap_set_etag(r);
if (r->header_only) {
return 0;
}
/* Spew HTML preamble */
*title_endp-- = '\0';
}
/*
* Figure out what sort of indexing (if any) we're supposed to use.
*
* If no QUERY_STRING was specified or column sorting has been
* explicitly disabled, we use the default specified by the
* IndexOrderDefault directive (if there is one); otherwise,
* we fall back to ascending by name.
*/
if ((autoindex_opts & SUPPRESS_COLSORT)
}
/*
* If there is no specific ordering defined for this directory,
* default to ascending by filename.
*/
}
else {
if (qstring != '\0') {
}
else {
}
}
/*
* 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.
*/
if (p != NULL) {
head = p;
num_ent++;
}
}
if (num_ent > 0) {
p = head;
x = 0;
while (p) {
ar[x++] = p;
p = p->next;
}
(int (*)(const void *, const void *)) dsortf);
}
if (autoindex_opts & FANCY_INDEXING) {
ap_rputs("<HR>\n", r);
}
return 0;
}
/* The formal handler... */
static int handle_autoindex(request_rec *r)
{
int allow_opts;
return DECLINED;
allow_opts = ap_allow_options(r);
if (r->method_number != M_GET) {
return DECLINED;
}
/* OK, nothing easy. Trot out the heavy artillery... */
if (allow_opts & OPT_INDEXES) {
/* KLUDGE --- make the sub_req lookups happen in the right directory.
* Fixing this in the sub_req_lookup functions themselves is difficult,
* and would probably break virtual includes...
*/
}
return index_directory(r, d);
}
else {
"Directory index forbidden by rule: %s", r->filename);
return HTTP_FORBIDDEN;
}
}
static void register_hooks(apr_pool_t *p)
{
}
{
create_autoindex_config, /* dir config creater */
merge_autoindex_configs, /* dir merger --- default is to override */
NULL, /* server config */
NULL, /* merge server config */
autoindex_cmds, /* command apr_table_t */
register_hooks /* register hooks */
};