]> git.ipfire.org Git - thirdparty/apache/httpd.git/commitdiff
This commit was manufactured by cvs2svn to create branch
author(no author) <(no author)@unknown>
Tue, 13 May 1997 04:01:50 +0000 (04:01 +0000)
committer(no author) <(no author)@unknown>
Tue, 13 May 1997 04:01:50 +0000 (04:01 +0000)
'unlabeled-1.26.2'.

git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/1.3@78146 13f79535-47bb-0310-9956-ffa450edef68

unlabeled-1.26.2/src/modules/standard/mod_autoindex.c [new file with mode: 0644]

diff --git a/unlabeled-1.26.2/src/modules/standard/mod_autoindex.c b/unlabeled-1.26.2/src/modules/standard/mod_autoindex.c
new file mode 100644 (file)
index 0000000..bb803ec
--- /dev/null
@@ -0,0 +1,891 @@
+/* ====================================================================
+ * Copyright (c) 1995-1997 The Apache Group.  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
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * 4. The names "Apache Server" and "Apache Group" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission.
+ *
+ * 5. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the Apache Group
+ *    for use in the Apache HTTP server project (http://www.apache.org/)."
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``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 GROUP 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 Group and was originally based
+ * on public domain software written at the National Center for
+ * Supercomputing Applications, University of Illinois, Urbana-Champaign.
+ * For more information on the Apache Group and the Apache HTTP server
+ * project, please see <http://www.apache.org/>.
+ *
+ */
+
+/*
+ * http_dir.c: Handles the on-the-fly html index generation
+ * 
+ * Rob McCool
+ * 3/23/93
+ * 
+ * Adapted to Apache by rst.
+ */
+
+#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"
+
+module dir_module;
+
+/****************************************************************
+ *
+ * Handling configuration directives...
+ */
+
+#define FANCY_INDEXING 1       /* Indexing options */
+#define ICONS_ARE_LINKS 2
+#define SCAN_HTML_TITLES 4
+#define SUPPRESS_LAST_MOD 8
+#define SUPPRESS_SIZE 16
+#define SUPPRESS_DESC 32
+
+struct item {
+    char *type;
+    char *apply_to;
+    char *apply_path;
+    char *data;
+};
+
+typedef struct dir_config_struct {
+
+    char *default_icon;
+    char *index_names;
+  
+    array_header *icon_list, *alt_list, *desc_list, *ign_list;
+    array_header *hdr_list, *rdme_list, *opts_list;
+  
+} dir_config_rec;
+
+char c_by_encoding, c_by_type, c_by_path;
+
+#define BY_ENCODING &c_by_encoding
+#define BY_TYPE &c_by_type
+#define BY_PATH &c_by_path
+
+void push_item(array_header *arr, char *type, char *to, char *path, char *data)
+{
+    struct item *p = (struct item *)push_array(arr);
+
+    if (!to) to = "";
+    if (!path) path = "";
+    
+    p->type = type;
+    p->data = data ? pstrdup(arr->pool, data): NULL;
+    p->apply_path = pstrcat(arr->pool, path, "*", NULL);
+    
+    if((type == BY_PATH) && (!is_matchexp(to)))
+        p->apply_to = pstrcat (arr->pool, "*", to, NULL);
+    else if (to)
+        p->apply_to = pstrdup (arr->pool, to);
+    else
+        p->apply_to = NULL;
+}
+
+const char *add_alt(cmd_parms *cmd, void *d, char *alt, char *to)
+{
+    if (cmd->info == BY_PATH)
+        if(!strcmp(to,"**DIRECTORY**"))
+            to = "^^DIRECTORY^^";
+
+    push_item(((dir_config_rec *)d)->alt_list, cmd->info, to, cmd->path, alt);
+    return NULL;
+}
+
+const char *add_icon(cmd_parms *cmd, void *d, char *icon, char *to)
+{
+    char *iconbak = pstrdup (cmd->pool, icon);
+
+    if(icon[0] == '(') {
+        char *alt = getword_nc (cmd->pool, &iconbak, ',');
+        iconbak[strlen(iconbak) - 1] = '\0'; /* Lose closing paren */
+        add_alt(cmd, d, &alt[1], to);
+    }
+    if(cmd->info == BY_PATH) 
+        if(!strcmp(to,"**DIRECTORY**"))
+            to = "^^DIRECTORY^^";
+
+    push_item(((dir_config_rec *)d)->icon_list, cmd->info, to, cmd->path,
+             iconbak);
+    return NULL;
+}
+
+const char *add_desc(cmd_parms *cmd, void *d, char *desc, char *to)
+{
+    push_item(((dir_config_rec *)d)->desc_list, cmd->info, to, cmd->path,desc);
+    return NULL;
+}
+
+const char *add_ignore(cmd_parms *cmd, void *d, char *ext) {
+    push_item(((dir_config_rec *)d)->ign_list, 0, ext, cmd->path, NULL);
+    return NULL;
+}
+
+const char *add_header(cmd_parms *cmd, void *d, char *name) {
+    push_item(((dir_config_rec *)d)->hdr_list, 0, NULL, cmd->path, name);
+    return NULL;
+}
+
+const char *add_readme(cmd_parms *cmd, void *d, char *name) {
+    push_item(((dir_config_rec *)d)->rdme_list, 0, NULL, cmd->path, name);
+    return NULL;
+}
+
+
+const char *add_opts_int(cmd_parms *cmd, void *d, int opts) {
+    push_item(((dir_config_rec *)d)->opts_list, (char*)opts, NULL,
+             cmd->path, NULL);
+    return NULL;
+}
+
+const char *fancy_indexing (cmd_parms *cmd, void *d, int arg)
+{
+    return add_opts_int (cmd, d, arg? FANCY_INDEXING : 0);
+}
+
+const char *add_opts(cmd_parms *cmd, void *d, const char *optstr) {
+    char *w;
+    int opts = 0;
+
+    while(optstr[0]) {
+        w = getword_conf(cmd->pool, &optstr);
+        if(!strcasecmp(w,"FancyIndexing"))
+            opts |= FANCY_INDEXING;
+        else if(!strcasecmp(w,"IconsAreLinks"))
+            opts |= ICONS_ARE_LINKS;
+        else if(!strcasecmp(w,"ScanHTMLTitles"))
+            opts |= SCAN_HTML_TITLES;
+        else if(!strcasecmp(w,"SuppressLastModified"))
+            opts |= SUPPRESS_LAST_MOD;
+        else if(!strcasecmp(w,"SuppressSize"))
+            opts |= SUPPRESS_SIZE;
+        else if(!strcasecmp(w,"SuppressDescription"))
+            opts |= SUPPRESS_DESC;
+        else if(!strcasecmp(w,"None"))
+            opts = 0;
+       else
+           return "Invalid directory indexing option";
+    }
+    return add_opts_int(cmd, d, opts);
+}
+
+#define DIR_CMD_PERMS OR_INDEXES
+
+command_rec dir_cmds[] = {
+{ "AddIcon", add_icon, BY_PATH, DIR_CMD_PERMS, ITERATE2,
+    "an icon URL followed by one or more filenames" },
+{ "AddIconByType", add_icon, BY_TYPE, DIR_CMD_PERMS, ITERATE2,
+    "an icon URL followed by one or more MIME types" },
+{ "AddIconByEncoding", add_icon, BY_ENCODING, DIR_CMD_PERMS, ITERATE2,
+    "an icon URL followed by one or more content encodings" },
+{ "AddAlt", add_alt, BY_PATH, DIR_CMD_PERMS, ITERATE2,
+    "alternate descriptive text followed by one or more filenames" },
+{ "AddAltByType", add_alt, BY_TYPE, DIR_CMD_PERMS, ITERATE2,
+    "alternate descriptive text followed by one or more MIME types" },
+{ "AddAltByEncoding", add_alt, BY_ENCODING, DIR_CMD_PERMS, ITERATE2,
+    "alternate descriptive text followed by one or more content encodings" },
+{ "IndexOptions", add_opts, NULL, DIR_CMD_PERMS, RAW_ARGS,
+    "one or more index options" },
+{ "IndexIgnore", add_ignore, NULL, DIR_CMD_PERMS, ITERATE,
+    "one or more file extensions" },
+{ "AddDescription", add_desc, BY_PATH, DIR_CMD_PERMS, ITERATE2,
+    "Descriptive text followed by one or more filenames" },
+{ "HeaderName", add_header, NULL, DIR_CMD_PERMS, TAKE1, "a filename" },
+{ "ReadmeName", add_readme, NULL, DIR_CMD_PERMS, TAKE1, "a filename" },
+{ "FancyIndexing", fancy_indexing, NULL, DIR_CMD_PERMS, FLAG, NULL },
+{ "DefaultIcon", set_string_slot,
+    (void*)XtOffsetOf(dir_config_rec, default_icon),
+    DIR_CMD_PERMS, TAKE1, "an icon URL"},
+{ "DirectoryIndex", set_string_slot,
+    (void*)XtOffsetOf(dir_config_rec, index_names),
+    DIR_CMD_PERMS, RAW_ARGS, NULL },
+{ NULL }
+};
+
+void *create_dir_config (pool *p, char *dummy)
+{
+    dir_config_rec *new =
+        (dir_config_rec *) pcalloc (p, sizeof(dir_config_rec));
+
+    new->index_names = NULL;
+    new->icon_list = make_array (p, 4, sizeof (struct item));
+    new->alt_list = make_array (p, 4, sizeof (struct item));
+    new->desc_list = make_array (p, 4, sizeof (struct item));
+    new->ign_list = make_array (p, 4, sizeof (struct item));
+    new->hdr_list = make_array (p, 4, sizeof (struct item));
+    new->rdme_list = make_array (p, 4, sizeof (struct item));
+    new->opts_list = make_array (p, 4, sizeof (struct item));
+    
+    return (void *)new;
+}
+
+void *merge_dir_configs (pool *p, void *basev, void *addv)
+{
+    dir_config_rec *new=(dir_config_rec*)pcalloc (p, sizeof(dir_config_rec));
+    dir_config_rec *base = (dir_config_rec *)basev;
+    dir_config_rec *add = (dir_config_rec *)addv;
+
+    new->default_icon = add->default_icon?add->default_icon:base->default_icon;
+    new->index_names = add->index_names? add->index_names: base->index_names;
+
+    new->alt_list = append_arrays (p, add->alt_list, base->alt_list);
+    new->ign_list = append_arrays (p, add->ign_list, base->ign_list);
+    new->hdr_list = append_arrays (p, add->hdr_list, base->hdr_list);
+    new->desc_list = append_arrays (p, add->desc_list, base->desc_list);
+    new->icon_list = append_arrays (p, add->icon_list, base->icon_list);
+    new->rdme_list = append_arrays (p, add->rdme_list, base->rdme_list);
+    new->opts_list = append_arrays (p, add->opts_list, base->opts_list);
+    
+    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;
+    size_t size;
+    time_t lm;
+    struct ent *next;
+};
+
+char *find_item(request_rec *r, array_header *list, int path_only) {
+    char *content_type = r->content_type;
+    char *content_encoding = r->content_encoding;
+    char *path = r->filename;
+
+    struct item *items = (struct item *)list->elts;
+    int i;
+
+    for (i = 0; i < list->nelts; ++i) {
+        struct item *p = &items[i];
+      
+        /* Special cased for ^^DIRECTORY^^ and ^^BLANKICON^^ */
+        if((path[0] == '^') || (!strcmp_match(path,p->apply_path))) {
+            if(!*(p->apply_to))
+                return p->data;
+            else if(p->type == BY_PATH || path[0] == '^') {
+                if(!strcmp_match(path,p->apply_to))
+                    return p->data;
+            } else if(!path_only) {
+                if(!content_encoding) {
+                    if(p->type == BY_TYPE) {
+                        if(content_type && !strcmp_match(content_type,p->apply_to))
+                            return p->data;
+                    }
+                } else {
+                    if(p->type == BY_ENCODING) {
+                        if(!strcmp_match(content_encoding,p->apply_to))
+                            return p->data;
+                    }
+                }
+            }
+        }
+    }
+    return NULL;
+}
+
+#define find_icon(d,p,t) find_item(p,d->icon_list,t)
+#define find_alt(d,p,t) find_item(p,d->alt_list,t)
+#define find_desc(d,p) find_item(p,d->desc_list,0)
+#define find_header(d,p) find_item(p,d->hdr_list,0)
+#define find_readme(d,p) find_item(p,d->rdme_list,0)
+
+char *find_default_icon (dir_config_rec *d, char *bogus_name)
+{
+    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;
+    r.content_type = r.content_encoding = NULL;
+
+    return find_item (&r, d->icon_list, 1);
+}
+
+int ignore_entry(dir_config_rec *d, char *path) {
+    array_header *list = d->ign_list;
+    struct item *items = (struct item *)list->elts;
+    char *tt;
+    int i;
+
+    if((tt=strrchr(path,'/')) == NULL)
+      tt=path;
+    else {
+      tt++;
+    }
+
+    for (i = 0; i < list->nelts; ++i) {
+        struct item *p = &items[i];
+       char *ap;
+
+       if((ap=strrchr(p->apply_to,'/')) == NULL)
+         ap=p->apply_to;
+       else
+         ap++;
+
+        if(!strcmp_match(path,p->apply_path) && !strcmp_match(tt,ap))
+          return 1;
+    }
+    return 0;
+}
+
+int find_opts(dir_config_rec *d, request_rec *r) {
+    char *path = r->filename;
+    array_header *list = d->opts_list;
+    struct item *items = (struct item *)list->elts;
+    int i;
+
+    for (i = 0; i < list->nelts; ++i) {
+        struct item *p = &items[i];
+       
+        if(!strcmp_match(path,p->apply_path))
+            return (int)p->type;
+    }
+    return 0;
+}
+
+/*****************************************************************
+ *
+ * Actually generating output
+ */
+
+
+int insert_readme(char *name, char *readme_fname, int rule, request_rec *r) {
+    char *fn;
+    FILE *f;
+    struct stat finfo;
+    int plaintext=0;
+
+    fn = make_full_path(r->pool, name, readme_fname);
+    fn = pstrcat(r->pool, fn, ".html", NULL);
+    if(stat(fn,&finfo) == -1) {
+        /* A brief fake multiviews search for README.html */
+        fn[strlen(fn)-5] = '\0';
+        if(stat(fn,&finfo) == -1)
+            return 0;
+        plaintext=1;
+        if(rule) rputs("<HR>\n", r);
+        rputs("<PRE>\n", r);
+    }
+    else if (rule) rputs("<HR>\n", r);
+    if(!(f = pfopen(r->pool,fn,"r")))
+        return 0;
+    if (!plaintext)
+       send_fd(f, r);
+    else
+    {
+       char buf[IOBUFSIZE+1];
+       int i, n, c, ch;
+       while (!feof(f))
+       {
+           do n = fread(buf, sizeof(char), IOBUFSIZE, f);
+           while (n == -1 && ferror(f) && errno == EINTR);
+           if (n == -1 || n == 0) break;
+           buf[n] = '\0';
+           c = 0;
+           while (c < n)
+           {
+               for (i=c; i < n; i++)
+                   if (buf[i] == '<' || buf[i] == '>' || buf[i] == '&') break;
+               ch = buf[i];
+               buf[i] = '\0';
+               rputs(&buf[c], r);
+               if (ch == '<') rputs("&lt;", r);
+               else if (ch == '>') rputs("&gt;", r);
+               else if (ch == '&') rputs("&amp;", r);
+               c = i + 1;
+           }
+       }
+    }
+    pfclose(r->pool, f);
+    if(plaintext)
+        rputs("</PRE>\n", r);
+    return 1;
+}
+
+
+char *find_title(request_rec *r) {
+    char titlebuf[MAX_STRING_LEN], *find = "<TITLE>";
+    FILE *thefile = NULL;
+    int x,y,n,p;
+
+    if (r->content_type && !strcmp(r->content_type,"text/html") && !r->content_encoding) {
+        if(!(thefile = pfopen(r->pool, r->filename,"r")))
+            return NULL;
+        n = fread(titlebuf,sizeof(char),MAX_STRING_LEN - 1,thefile);
+        titlebuf[n] = '\0';
+        for(x=0,p=0;titlebuf[x];x++) {
+            if(toupper(titlebuf[x]) == find[p]) {
+                if(!find[++p]) {
+                    if((p = ind(&titlebuf[++x],'<')) != -1)
+                        titlebuf[x+p] = '\0';
+                    /* Scan for line breaks for Tanmoy's secretary */
+                    for(y=x;titlebuf[y];y++)
+                        if((titlebuf[y] == CR) || (titlebuf[y] == LF))
+                            titlebuf[y] = ' ';
+                   pfclose (r->pool, thefile);
+                    return pstrdup(r->pool, &titlebuf[x]);
+                }
+            } else p=0;
+        }
+       pfclose(r->pool, thefile);
+    }
+    return NULL;
+}
+
+struct ent *make_dir_entry(char *name, int dir_opts,
+                          dir_config_rec *d, request_rec *r)
+{
+    struct ent *p;
+
+    if((name[0] == '.') && (!name[1]))
+        return(NULL);
+
+    if (ignore_entry(d, make_full_path (r->pool, r->filename, name)))
+        return(NULL);
+
+    p=(struct ent *)pcalloc(r->pool, sizeof(struct ent));
+    p->name = pstrdup (r->pool, name);
+    p->size = -1;
+    p->icon = NULL;
+    p->alt = NULL;
+    p->desc = NULL;
+    p->lm = -1;
+
+    if(dir_opts & FANCY_INDEXING) {
+        request_rec *rr = sub_req_lookup_file (name, r);
+       
+       if (rr->finfo.st_mode != 0) {
+            p->lm = rr->finfo.st_mtime;
+            if(S_ISDIR(rr->finfo.st_mode)) {
+                if(!(p->icon = find_icon(d,rr,1)))
+                    p->icon = find_default_icon(d,"^^DIRECTORY^^");
+                if(!(p->alt = find_alt(d,rr,1)))
+                    p->alt = "DIR";
+                p->size = -1;
+               p->name = pstrcat (r->pool, name, "/", NULL);
+            }
+            else {
+                p->icon = find_icon(d, rr, 0);
+                p->alt = find_alt(d, rr, 0);
+                p->size = rr->finfo.st_size;
+            }
+        }
+       
+        p->desc = find_desc(d, rr);
+       
+        if((!p->desc) && (dir_opts & SCAN_HTML_TITLES))
+            p->desc = pstrdup (r->pool, find_title(rr));
+
+       destroy_sub_req (rr);
+    }
+    return(p);
+}
+
+char *terminate_description(dir_config_rec *d, char *desc, int dir_opts) {
+    int maxsize = 23;
+    register int x;
+    
+    if(dir_opts & SUPPRESS_LAST_MOD) maxsize += 17;
+    if(dir_opts & SUPPRESS_SIZE) maxsize += 7;
+
+    for(x=0;desc[x] && maxsize;x++) {
+        if(desc[x] == '<') {
+            while(desc[x] != '>') {
+                if(!desc[x]) {
+                    maxsize = 0;
+                    break;
+                }
+                ++x;
+            }
+        }
+        else --maxsize;
+    }
+    if(!maxsize) {
+        desc[x-1] = '>';       /* Grump. */
+       desc[x] = '\0';         /* Double Grump! */
+    }
+    return desc;
+}
+
+void output_directories(struct ent **ar, int n,
+                       dir_config_rec *d, request_rec *r, int dir_opts)
+{
+    int x, len;
+    char *name = r->uri;
+    char *tp;
+    pool *scratch = make_sub_pool (r->pool);
+    
+    if(name[0] == '\0') name = "/";
+
+    if(dir_opts & FANCY_INDEXING) {
+        rputs("<PRE>", r);
+        if((tp = find_default_icon(d,"^^BLANKICON^^")))
+            rvputs(r, "<IMG SRC=\"", escape_html(scratch, tp),
+                  "\" ALT=\"     \"> ", NULL);
+        rputs("Name                   ", r);
+        if(!(dir_opts & SUPPRESS_LAST_MOD))
+            rputs("Last modified     ", r);
+        if(!(dir_opts & SUPPRESS_SIZE))
+            rputs("Size  ", r);
+        if(!(dir_opts & SUPPRESS_DESC))
+            rputs("Description", r);
+        rputs("\n<HR>\n", r);
+    }
+    else {
+        rputs("<UL>", r);
+    }
+        
+    for(x=0;x<n;x++) {
+       char *anchor = NULL, *t = NULL, *t2 = NULL;
+       
+       clear_pool (scratch);
+       
+        if((!strcmp(ar[x]->name,"../")) || (!strcmp(ar[x]->name,".."))) {
+            char *t = make_full_path (scratch, name, "../");
+            getparents(t);
+            if(t[0] == '\0') t = "/";
+           anchor = pstrcat (scratch, "<A HREF=\"",
+                             escape_html(scratch, os_escape_path(scratch, t, 0)),
+                             "\">", NULL);
+           t2 = "Parent Directory</A>       ";
+        }
+        else {
+           t = ar[x]->name;
+           len = strlen(t);
+            if(len > 23) {
+               t2 = pstrdup(scratch, t);
+               t2[21] = '.';
+               t2[22] = '.';
+                t2[23] = '\0';
+               t2 = escape_html(scratch, t2);
+               t2 = pstrcat(scratch, t2, "</A>", NULL);
+            } else 
+           {
+               char buff[24]="                       ";
+               t2 = escape_html(scratch, t);
+               buff[23-len] = '\0';
+               t2 = pstrcat(scratch, t2, "</A>", buff, NULL);
+           }
+           anchor = pstrcat (scratch, "<A HREF=\"",
+                             escape_html(scratch, os_escape_path(scratch, t, 0)),
+                             "\">", NULL);
+        }
+
+        if(dir_opts & FANCY_INDEXING) {
+            if(dir_opts & ICONS_ARE_LINKS)
+                rputs(anchor, r);
+            if((ar[x]->icon) || d->default_icon) {
+                rvputs(r, "<IMG SRC=\"", 
+                      escape_html(scratch, ar[x]->icon ?
+                                  ar[x]->icon : d->default_icon),
+                      "\" ALT=\"[", (ar[x]->alt ? ar[x]->alt : "   "),
+                      "]\">", NULL);
+            }
+            if(dir_opts & ICONS_ARE_LINKS) 
+                rputs("</A>", r);
+
+            rvputs(r," ", anchor, t2, NULL);
+            if(!(dir_opts & SUPPRESS_LAST_MOD)) {
+                if(ar[x]->lm != -1) {
+                   char time[MAX_STRING_LEN];
+                    struct tm *ts = localtime(&ar[x]->lm);
+                    strftime(time,MAX_STRING_LEN,"%d-%b-%y %H:%M  ",ts);
+                   rputs(time, r);
+                }
+                else {
+                    rputs("                 ", r);
+                }
+            }
+            if(!(dir_opts & SUPPRESS_SIZE)) {
+                send_size(ar[x]->size,r);
+                rputs("  ", r);
+            }
+            if(!(dir_opts & SUPPRESS_DESC)) {
+                if(ar[x]->desc) {
+                    rputs(terminate_description(d, ar[x]->desc, dir_opts), r);
+                }
+            }
+        }
+        else
+            rvputs(r, "<LI> ", anchor," ", t2, NULL);
+        rputc('\n', r);
+    }
+    if(dir_opts & FANCY_INDEXING) {
+        rputs("</PRE>", r);
+    }
+    else {
+        rputs("</UL>", r);
+    }
+}
+
+
+int dsortf(struct ent **s1,struct ent **s2)
+{
+    return(strcmp((*s1)->name,(*s2)->name));
+}
+
+    
+int index_directory(request_rec *r, dir_config_rec *dir_conf)
+{
+    char *title_name = escape_html(r->pool, r->uri);
+    char *title_endp;
+    char *name = r->filename;
+    
+    DIR *d;
+    struct DIR_TYPE *dstruct;
+    int num_ent=0,x;
+    struct ent *head,*p;
+    struct ent **ar = NULL;
+    char *tmp;
+    int dir_opts = find_opts(dir_conf, r);
+
+    if(!(d=opendir(name))) {
+        log_reason ("Can't open directory for index", r->filename, r);
+        return HTTP_FORBIDDEN;
+    }
+
+    r->content_type = "text/html";
+    
+    send_http_header(r);
+
+    if (r->header_only) {
+       closedir (d);
+       return 0;
+    }
+    hard_timeout("send directory", r);
+
+    /* Spew HTML preamble */
+    
+    title_endp = title_name + strlen(title_name) - 1;
+
+    while (title_endp > title_name && *title_endp == '/')
+       *title_endp-- = '\0';
+    
+    rvputs
+       (
+           r,
+           "<HTML><HEAD>\n<TITLE>Index of ",
+           title_name,
+           "</TITLE>\n</HEAD><BODY>\n",
+           NULL
+       );
+
+    if((!(tmp = find_header(dir_conf,r))) || (!(insert_readme(name,tmp,0,r))))
+        rvputs(r, "<H1>Index of ", title_name, "</H1>\n", NULL);
+
+    /* 
+     * 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;
+    while((dstruct=readdir(d))) {
+        if((p = make_dir_entry(dstruct->d_name, dir_opts, dir_conf, r))) {
+            p->next=head;
+            head=p;
+            num_ent++;
+        }
+    }
+    if (num_ent > 0) {
+        ar=(struct ent **) 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 *),
+#ifdef ULTRIX_BRAIN_DEATH
+             (int (*))dsortf);
+#else
+             (int (*)(const void *,const void *))dsortf);
+#endif
+    }
+    output_directories(ar, num_ent, dir_conf, r, dir_opts);
+    closedir(d);
+
+    if (dir_opts & FANCY_INDEXING)
+        if((tmp = find_readme(dir_conf, r)))
+            insert_readme(name,tmp,1,r);
+    else {
+        rputs("</UL>", r);
+    }
+
+    rputs ("</BODY></HTML>\n", r);
+
+    kill_timeout(r);
+    return 0;
+}
+
+/* The formal handler... */
+
+int handle_dir (request_rec *r)
+{
+    dir_config_rec *d =
+      (dir_config_rec *)get_module_config (r->per_dir_config, &dir_module);
+    const char *names_ptr = d->index_names ? d->index_names : DEFAULT_INDEX;
+    int allow_opts = allow_options (r);
+    int error_notfound = 0;
+
+    if (r->uri[0] == '\0' || r->uri[strlen(r->uri)-1] != '/') {
+       char* ifile;
+       if (r->args != NULL)
+               ifile = pstrcat (r->pool, escape_uri(r->pool, r->uri),
+                       "/", "?", r->args, NULL);
+       else
+               ifile = pstrcat (r->pool, escape_uri(r->pool, r->uri),
+                        "/", NULL);
+
+       table_set (r->headers_out, "Location",
+                  construct_url(r->pool, ifile, r->server)); 
+       return HTTP_MOVED_PERMANENTLY;
+    }
+
+    /* 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...
+     */
+
+    r->filename = pstrcat (r->pool, r->filename, "/", NULL);
+    
+    while (*names_ptr) {
+          
+       char *name_ptr = getword_conf (r->pool, &names_ptr);
+       request_rec *rr = sub_req_lookup_uri (name_ptr, r);
+           
+       if (rr->status == HTTP_OK && rr->finfo.st_mode != 0) {
+           char* new_uri = escape_uri(r->pool, rr->uri);
+
+           if (rr->args != NULL)
+               new_uri = pstrcat(r->pool, new_uri, "?", rr->args, NULL);
+           else if (r->args != NULL)
+               new_uri = pstrcat(r->pool, new_uri, "?", r->args, NULL);
+       
+           destroy_sub_req (rr);
+           internal_redirect (new_uri, r);
+           return OK;
+       }
+
+       /* If the request returned a redirect, propagate it to the client */
+
+       if (is_HTTP_REDIRECT(rr->status) ||
+           (rr->status == HTTP_NOT_ACCEPTABLE && *names_ptr == '\0')) {
+
+           error_notfound = rr->status;
+           r->notes = overlay_tables(r->pool, r->notes, rr->notes);
+           r->headers_out = overlay_tables(r->pool, r->headers_out,
+                                                   rr->headers_out);
+           r->err_headers_out = overlay_tables(r->pool, r->err_headers_out,
+                                                       rr->err_headers_out);
+           destroy_sub_req(rr);
+           return error_notfound;
+       }
+            
+       /* If the request returned something other than 404 (or 200),
+        * it means the module encountered some sort of problem. To be
+        * secure, we should return the error, rather than create
+        * along a (possibly unsafe) directory index.
+        *
+        * So we store the error, and if none of the listed files
+        * exist, we return the last error response we got, instead
+        * of a directory listing.
+        */
+       if (rr->status && rr->status != HTTP_NOT_FOUND && rr->status != HTTP_OK)
+           error_notfound = rr->status;
+
+        destroy_sub_req (rr);
+    }
+
+    if (error_notfound)
+       return error_notfound;
+
+    if (r->method_number != M_GET) return NOT_IMPLEMENTED;
+    
+    /* OK, nothing easy.  Trot out the heavy artillery... */
+
+    if (allow_opts & OPT_INDEXES) 
+        return index_directory (r, d);
+    else {
+        log_reason ("Directory index forbidden by rule", r->filename, r);
+        return HTTP_FORBIDDEN;
+    }
+}
+
+
+handler_rec dir_handlers[] = {
+{ DIR_MAGIC_TYPE, handle_dir },
+{ NULL }
+};
+
+module dir_module = {
+   STANDARD_MODULE_STUFF,
+   NULL,                       /* initializer */
+   create_dir_config,          /* dir config creater */
+   merge_dir_configs,          /* dir merger --- default is to override */
+   NULL,                       /* server config */
+   NULL,                       /* merge server config */
+   dir_cmds,                   /* command table */
+   dir_handlers,               /* handlers */
+   NULL,                       /* filename translation */
+   NULL,                       /* check_user_id */
+   NULL,                       /* check auth */
+   NULL,                       /* check access */
+   NULL,                       /* type_checker */
+   NULL,                       /* fixups */
+   NULL,                       /* logger */
+   NULL                                /* header parser */
+};