home *** CD-ROM | disk | FTP | other *** search
/ PC Online 1999 April / PCO0499.ISO / filesbbs / os2 / apach134.arj / APACH134.ZIP / src / modules / standard / mod_autoindex.c < prev    next >
Encoding:
C/C++ Source or Header  |  1999-01-04  |  39.5 KB  |  1,507 lines

  1. /* ====================================================================
  2.  * Copyright (c) 1995-1999 The Apache Group.  All rights reserved.
  3.  *
  4.  * Redistribution and use in source and binary forms, with or without
  5.  * modification, are permitted provided that the following conditions
  6.  * are met:
  7.  *
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer. 
  10.  *
  11.  * 2. Redistributions in binary form must reproduce the above copyright
  12.  *    notice, this list of conditions and the following disclaimer in
  13.  *    the documentation and/or other materials provided with the
  14.  *    distribution.
  15.  *
  16.  * 3. All advertising materials mentioning features or use of this
  17.  *    software must display the following acknowledgment:
  18.  *    "This product includes software developed by the Apache Group
  19.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  20.  *
  21.  * 4. The names "Apache Server" and "Apache Group" must not be used to
  22.  *    endorse or promote products derived from this software without
  23.  *    prior written permission. For written permission, please contact
  24.  *    apache@apache.org.
  25.  *
  26.  * 5. Products derived from this software may not be called "Apache"
  27.  *    nor may "Apache" appear in their names without prior written
  28.  *    permission of the Apache Group.
  29.  *
  30.  * 6. Redistributions of any form whatsoever must retain the following
  31.  *    acknowledgment:
  32.  *    "This product includes software developed by the Apache Group
  33.  *    for use in the Apache HTTP server project (http://www.apache.org/)."
  34.  *
  35.  * THIS SOFTWARE IS PROVIDED BY THE APACHE GROUP ``AS IS'' AND ANY
  36.  * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  37.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  38.  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE APACHE GROUP OR
  39.  * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  40.  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  41.  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  42.  * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  43.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
  44.  * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  45.  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
  46.  * OF THE POSSIBILITY OF SUCH DAMAGE.
  47.  * ====================================================================
  48.  *
  49.  * This software consists of voluntary contributions made by many
  50.  * individuals on behalf of the Apache Group and was originally based
  51.  * on public domain software written at the National Center for
  52.  * Supercomputing Applications, University of Illinois, Urbana-Champaign.
  53.  * For more information on the Apache Group and the Apache HTTP server
  54.  * project, please see <http://www.apache.org/>.
  55.  *
  56.  */
  57.  
  58. /*
  59.  * mod_autoindex.c: Handles the on-the-fly html index generation
  60.  * 
  61.  * Rob McCool
  62.  * 3/23/93
  63.  * 
  64.  * Adapted to Apache by rst.
  65.  */
  66.  
  67. #include "httpd.h"
  68. #include "http_config.h"
  69. #include "http_core.h"
  70. #include "http_request.h"
  71. #include "http_protocol.h"
  72. #include "http_log.h"
  73. #include "http_main.h"
  74. #include "util_script.h"
  75.  
  76. module MODULE_VAR_EXPORT autoindex_module;
  77.  
  78. /****************************************************************
  79.  *
  80.  * Handling configuration directives...
  81.  */
  82.  
  83. #define HRULE 1
  84. #define NO_HRULE 0
  85. #define FRONT_MATTER 1
  86. #define END_MATTER 0
  87.  
  88. #define FANCY_INDEXING 1    /* Indexing options */
  89. #define ICONS_ARE_LINKS 2
  90. #define SCAN_HTML_TITLES 4
  91. #define SUPPRESS_LAST_MOD 8
  92. #define SUPPRESS_SIZE 16
  93. #define SUPPRESS_DESC 32
  94. #define SUPPRESS_PREAMBLE 64
  95. #define SUPPRESS_COLSORT 128
  96. #define NO_OPTIONS 256
  97.  
  98. #define K_PAD 1
  99. #define K_NOPAD 0
  100.  
  101. #define K_NOADJUST 0
  102. #define K_ADJUST 1
  103. #define K_UNSET 2
  104.  
  105. /*
  106.  * Define keys for sorting.
  107.  */
  108. #define K_NAME 'N'        /* Sort by file name (default) */
  109. #define K_LAST_MOD 'M'        /* Last modification date */
  110. #define K_SIZE 'S'        /* Size (absolute, not as displayed) */
  111. #define K_DESC 'D'        /* Description */
  112.  
  113. #define D_ASCENDING 'A'
  114. #define D_DESCENDING 'D'
  115.  
  116. /*
  117.  * These are the dimensions of the default icons supplied with Apache.
  118.  */
  119. #define DEFAULT_ICON_WIDTH 20
  120. #define DEFAULT_ICON_HEIGHT 22
  121.  
  122. /*
  123.  * Other default dimensions.
  124.  */
  125. #define DEFAULT_NAME_WIDTH 23
  126.  
  127. struct item {
  128.     char *type;
  129.     char *apply_to;
  130.     char *apply_path;
  131.     char *data;
  132. };
  133.  
  134. typedef struct autoindex_config_struct {
  135.  
  136.     char *default_icon;
  137.     int opts;
  138.     int incremented_opts;
  139.     int decremented_opts;
  140.     int name_width;
  141.     int name_adjust;
  142.     int icon_width;
  143.     int icon_height;
  144.     char *default_order;
  145.  
  146.     array_header *icon_list, *alt_list, *desc_list, *ign_list;
  147.     array_header *hdr_list, *rdme_list;
  148.  
  149. } autoindex_config_rec;
  150.  
  151. static char c_by_encoding, c_by_type, c_by_path;
  152.  
  153. #define BY_ENCODING &c_by_encoding
  154. #define BY_TYPE &c_by_type
  155. #define BY_PATH &c_by_path
  156.  
  157. /*
  158.  * Return true if the specified string refers to the parent directory (i.e.,
  159.  * matches ".." or "../").  Hopefully this one call is significantly less
  160.  * expensive than multiple strcmp() calls.
  161.  */
  162. static ap_inline int is_parent(const char *name)
  163. {
  164.     /*
  165.      * Now, IFF the first two bytes are dots, and the third byte is either
  166.      * EOS (\0) or a slash followed by EOS, we have a match.
  167.      */
  168.     if (((name[0] == '.') && (name[1] == '.'))
  169.     && ((name[2] == '\0')
  170.         || ((name[2] == '/') && (name[3] == '\0')))) {
  171.         return 1;
  172.     }
  173.     return 0;
  174. }
  175.  
  176. /*
  177.  * This routine puts the standard HTML header at the top of the index page.
  178.  * We include the DOCTYPE because we may be using features therefrom (i.e.,
  179.  * HEIGHT and WIDTH attributes on the icons if we're FancyIndexing).
  180.  */
  181. static void emit_preamble(request_rec *r, char *title)
  182. {
  183.     ap_rvputs(r, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 3.2 Final//EN\">\n",
  184.           "<HTML>\n <HEAD>\n  <TITLE>Index of ", title,
  185.           "</TITLE>\n </HEAD>\n <BODY>\n", NULL);
  186. }
  187.  
  188. static void push_item(array_header *arr, char *type, char *to, char *path,
  189.               char *data)
  190. {
  191.     struct item *p = (struct item *) ap_push_array(arr);
  192.  
  193.     if (!to) {
  194.     to = "";
  195.     }
  196.     if (!path) {
  197.     path = "";
  198.     }
  199.  
  200.     p->type = type;
  201.     p->data = data ? ap_pstrdup(arr->pool, data) : NULL;
  202.     p->apply_path = ap_pstrcat(arr->pool, path, "*", NULL);
  203.  
  204.     if ((type == BY_PATH) && (!ap_is_matchexp(to))) {
  205.     p->apply_to = ap_pstrcat(arr->pool, "*", to, NULL);
  206.     }
  207.     else if (to) {
  208.     p->apply_to = ap_pstrdup(arr->pool, to);
  209.     }
  210.     else {
  211.     p->apply_to = NULL;
  212.     }
  213. }
  214.  
  215. static const char *add_alt(cmd_parms *cmd, void *d, char *alt, char *to)
  216. {
  217.     if (cmd->info == BY_PATH) {
  218.         if (!strcmp(to, "**DIRECTORY**")) {
  219.         to = "^^DIRECTORY^^";
  220.     }
  221.     }
  222.     if (cmd->info == BY_ENCODING) {
  223.     ap_str_tolower(to);
  224.     }
  225.  
  226.     push_item(((autoindex_config_rec *) d)->alt_list, cmd->info, to,
  227.           cmd->path, alt);
  228.     return NULL;
  229. }
  230.  
  231. static const char *add_icon(cmd_parms *cmd, void *d, char *icon, char *to)
  232. {
  233.     char *iconbak = ap_pstrdup(cmd->pool, icon);
  234.  
  235.     if (icon[0] == '(') {
  236.     char *alt;
  237.     char *cl = strchr(iconbak, ')');
  238.  
  239.     if (cl == NULL) {
  240.         return "missing closing paren";
  241.     }
  242.     alt = ap_getword_nc(cmd->pool, &iconbak, ',');
  243.     *cl = '\0';                /* Lose closing paren */
  244.     add_alt(cmd, d, &alt[1], to);
  245.     }
  246.     if (cmd->info == BY_PATH) {
  247.         if (!strcmp(to, "**DIRECTORY**")) {
  248.         to = "^^DIRECTORY^^";
  249.     }
  250.     }
  251.     if (cmd->info == BY_ENCODING) {
  252.     ap_str_tolower(to);
  253.     }
  254.  
  255.     push_item(((autoindex_config_rec *) d)->icon_list, cmd->info, to,
  256.           cmd->path, iconbak);
  257.     return NULL;
  258. }
  259.  
  260. static const char *add_desc(cmd_parms *cmd, void *d, char *desc, char *to)
  261. {
  262.     push_item(((autoindex_config_rec *) d)->desc_list, cmd->info, to,
  263.           cmd->path, desc);
  264.     return NULL;
  265. }
  266.  
  267. static const char *add_ignore(cmd_parms *cmd, void *d, char *ext)
  268. {
  269.     push_item(((autoindex_config_rec *) d)->ign_list, 0, ext, cmd->path, NULL);
  270.     return NULL;
  271. }
  272.  
  273. static const char *add_header(cmd_parms *cmd, void *d, char *name)
  274. {
  275.     if (strchr(name, '/')) {
  276.     return "HeaderName cannot contain a /";
  277.     }
  278.     push_item(((autoindex_config_rec *) d)->hdr_list, 0, NULL, cmd->path,
  279.           name);
  280.     return NULL;
  281. }
  282.  
  283. static const char *add_readme(cmd_parms *cmd, void *d, char *name)
  284. {
  285.     if (strchr(name, '/')) {
  286.     return "ReadmeName cannot contain a /";
  287.     }
  288.     push_item(((autoindex_config_rec *) d)->rdme_list, 0, NULL, cmd->path,
  289.           name);
  290.     return NULL;
  291. }
  292.  
  293. /* A legacy directive, FancyIndexing is superseded by the IndexOptions
  294.  * keyword.  But for compatibility..
  295.  */
  296. static const char *fancy_indexing(cmd_parms *cmd, void *d, int arg)
  297. {
  298.     int curopts;
  299.     int newopts;
  300.     autoindex_config_rec *cfg;
  301.  
  302.     cfg = (autoindex_config_rec *) d;
  303.     curopts = cfg->opts;
  304.     if (curopts & NO_OPTIONS) {
  305.     return "FancyIndexing directive conflicts with existing "
  306.            "IndexOptions None";
  307.     }
  308.     newopts = (arg ? (curopts | FANCY_INDEXING) : (curopts & ~FANCY_INDEXING));
  309.     cfg->opts = newopts;
  310.     return NULL;
  311. }
  312.  
  313. static const char *add_opts(cmd_parms *cmd, void *d, const char *optstr)
  314. {
  315.     char *w;
  316.     int opts;
  317.     int opts_add;
  318.     int opts_remove;
  319.     char action;
  320.     autoindex_config_rec *d_cfg = (autoindex_config_rec *) d;
  321.  
  322.     opts = d_cfg->opts;
  323.     opts_add = d_cfg->incremented_opts;
  324.     opts_remove = d_cfg->decremented_opts;
  325.     while (optstr[0]) {
  326.     int option = 0;
  327.  
  328.     w = ap_getword_conf(cmd->pool, &optstr);
  329.     if ((*w == '+') || (*w == '-')) {
  330.         action = *(w++);
  331.     }
  332.     else {
  333.         action = '\0';
  334.     }
  335.     if (!strcasecmp(w, "FancyIndexing")) {
  336.         option = FANCY_INDEXING;
  337.     }
  338.     else if (!strcasecmp(w, "IconsAreLinks")) {
  339.         option = ICONS_ARE_LINKS;
  340.     }
  341.     else if (!strcasecmp(w, "ScanHTMLTitles")) {
  342.         option = SCAN_HTML_TITLES;
  343.     }
  344.     else if (!strcasecmp(w, "SuppressLastModified")) {
  345.         option = SUPPRESS_LAST_MOD;
  346.     }
  347.     else if (!strcasecmp(w, "SuppressSize")) {
  348.         option = SUPPRESS_SIZE;
  349.     }
  350.     else if (!strcasecmp(w, "SuppressDescription")) {
  351.         option = SUPPRESS_DESC;
  352.     }
  353.     else if (!strcasecmp(w, "SuppressHTMLPreamble")) {
  354.         option = SUPPRESS_PREAMBLE;
  355.     }
  356.         else if (!strcasecmp(w, "SuppressColumnSorting")) {
  357.             option = SUPPRESS_COLSORT;
  358.     }
  359.     else if (!strcasecmp(w, "None")) {
  360.         if (action != '\0') {
  361.         return "Cannot combine '+' or '-' with 'None' keyword";
  362.         }
  363.         opts = NO_OPTIONS;
  364.         opts_add = 0;
  365.         opts_remove = 0;
  366.     }
  367.     else if (!strcasecmp(w, "IconWidth")) {
  368.         if (action != '-') {
  369.         d_cfg->icon_width = DEFAULT_ICON_WIDTH;
  370.         }
  371.         else {
  372.         d_cfg->icon_width = 0;
  373.         }
  374.     }
  375.     else if (!strncasecmp(w, "IconWidth=", 10)) {
  376.         if (action == '-') {
  377.         return "Cannot combine '-' with IconWidth=n";
  378.         }
  379.         d_cfg->icon_width = atoi(&w[10]);
  380.     }
  381.     else if (!strcasecmp(w, "IconHeight")) {
  382.         if (action != '-') {
  383.         d_cfg->icon_height = DEFAULT_ICON_HEIGHT;
  384.         }
  385.         else {
  386.         d_cfg->icon_height = 0;
  387.         }
  388.     }
  389.     else if (!strncasecmp(w, "IconHeight=", 11)) {
  390.         if (action == '-') {
  391.         return "Cannot combine '-' with IconHeight=n";
  392.         }
  393.         d_cfg->icon_height = atoi(&w[11]);
  394.     }
  395.     else if (!strcasecmp(w, "NameWidth")) {
  396.         if (action != '-') {
  397.         return "NameWidth with no value may only appear as "
  398.                "'-NameWidth'";
  399.         }
  400.         d_cfg->name_width = DEFAULT_NAME_WIDTH;
  401.         d_cfg->name_adjust = K_NOADJUST;
  402.     }
  403.     else if (!strncasecmp(w, "NameWidth=", 10)) {
  404.         if (action == '-') {
  405.         return "Cannot combine '-' with NameWidth=n";
  406.         }
  407.         if (w[10] == '*') {
  408.         d_cfg->name_adjust = K_ADJUST;
  409.         }
  410.         else {
  411.         int width = atoi(&w[10]);
  412.  
  413.         if (width < 1) {
  414.             return "NameWidth value must be greater than 1";
  415.         }
  416.         d_cfg->name_width = width;
  417.         d_cfg->name_adjust = K_NOADJUST;
  418.         }
  419.     }
  420.     else {
  421.         return "Invalid directory indexing option";
  422.     }
  423.     if (action == '\0') {
  424.         opts |= option;
  425.         opts_add = 0;
  426.         opts_remove = 0;
  427.     }
  428.     else if (action == '+') {
  429.         opts_add |= option;
  430.         opts_remove &= ~option;
  431.     }
  432.     else {
  433.         opts_remove |= option;
  434.         opts_add &= ~option;
  435.     }
  436.     }
  437.     if ((opts & NO_OPTIONS) && (opts & ~NO_OPTIONS)) {
  438.     return "Cannot combine other IndexOptions keywords with 'None'";
  439.     }
  440.     d_cfg->incremented_opts = opts_add;
  441.     d_cfg->decremented_opts = opts_remove;
  442.     d_cfg->opts = opts;
  443.     return NULL;
  444. }
  445.  
  446. static const char *set_default_order(cmd_parms *cmd, void *m, char *direction,
  447.                      char *key)
  448. {
  449.     char temp[4];
  450.     autoindex_config_rec *d_cfg = (autoindex_config_rec *) m;
  451.  
  452.     ap_cpystrn(temp, "k=d", sizeof(temp));
  453.     if (!strcasecmp(direction, "Ascending")) {
  454.     temp[2] = D_ASCENDING;
  455.     }
  456.     else if (!strcasecmp(direction, "Descending")) {
  457.     temp[2] = D_DESCENDING;
  458.     }
  459.     else {
  460.     return "First keyword must be 'Ascending' or 'Descending'";
  461.     }
  462.  
  463.     if (!strcasecmp(key, "Name")) {
  464.     temp[0] = K_NAME;
  465.     }
  466.     else if (!strcasecmp(key, "Date")) {
  467.     temp[0] = K_LAST_MOD;
  468.     }
  469.     else if (!strcasecmp(key, "Size")) {
  470.     temp[0] = K_SIZE;
  471.     }
  472.     else if (!strcasecmp(key, "Description")) {
  473.     temp[0] = K_DESC;
  474.     }
  475.     else {
  476.     return "Second keyword must be 'Name', 'Date', 'Size', or "
  477.         "'Description'";
  478.     }
  479.  
  480.     if (d_cfg->default_order == NULL) {
  481.     d_cfg->default_order = ap_palloc(cmd->pool, 4);
  482.     d_cfg->default_order[3] = '\0';
  483.     }
  484.     ap_cpystrn(d_cfg->default_order, temp, sizeof(temp));
  485.     return NULL;
  486. }
  487.  
  488. #define DIR_CMD_PERMS OR_INDEXES
  489.  
  490. static const command_rec autoindex_cmds[] =
  491. {
  492.     {"AddIcon", add_icon, BY_PATH, DIR_CMD_PERMS, ITERATE2,
  493.      "an icon URL followed by one or more filenames"},
  494.     {"AddIconByType", add_icon, BY_TYPE, DIR_CMD_PERMS, ITERATE2,
  495.      "an icon URL followed by one or more MIME types"},
  496.     {"AddIconByEncoding", add_icon, BY_ENCODING, DIR_CMD_PERMS, ITERATE2,
  497.      "an icon URL followed by one or more content encodings"},
  498.     {"AddAlt", add_alt, BY_PATH, DIR_CMD_PERMS, ITERATE2,
  499.      "alternate descriptive text followed by one or more filenames"},
  500.     {"AddAltByType", add_alt, BY_TYPE, DIR_CMD_PERMS, ITERATE2,
  501.      "alternate descriptive text followed by one or more MIME types"},
  502.     {"AddAltByEncoding", add_alt, BY_ENCODING, DIR_CMD_PERMS, ITERATE2,
  503.      "alternate descriptive text followed by one or more content encodings"},
  504.     {"IndexOptions", add_opts, NULL, DIR_CMD_PERMS, RAW_ARGS,
  505.      "one or more index options"},
  506.     {"IndexOrderDefault", set_default_order, NULL, DIR_CMD_PERMS, TAKE2,
  507.      "{Ascending,Descending} {Name,Size,Description,Date}"},
  508.     {"IndexIgnore", add_ignore, NULL, DIR_CMD_PERMS, ITERATE,
  509.      "one or more file extensions"},
  510.     {"AddDescription", add_desc, BY_PATH, DIR_CMD_PERMS, ITERATE2,
  511.      "Descriptive text followed by one or more filenames"},
  512.     {"HeaderName", add_header, NULL, DIR_CMD_PERMS, TAKE1, "a filename"},
  513.     {"ReadmeName", add_readme, NULL, DIR_CMD_PERMS, TAKE1, "a filename"},
  514.     {"FancyIndexing", fancy_indexing, NULL, DIR_CMD_PERMS, FLAG,
  515.      "Limited to 'on' or 'off' (superseded by IndexOptions FancyIndexing)"},
  516.     {"DefaultIcon", ap_set_string_slot,
  517.      (void *) XtOffsetOf(autoindex_config_rec, default_icon),
  518.      DIR_CMD_PERMS, TAKE1, "an icon URL"},
  519.     {NULL}
  520. };
  521.  
  522. static void *create_autoindex_config(pool *p, char *dummy)
  523. {
  524.     autoindex_config_rec *new =
  525.     (autoindex_config_rec *) ap_pcalloc(p, sizeof(autoindex_config_rec));
  526.  
  527.     new->icon_width = 0;
  528.     new->icon_height = 0;
  529.     new->name_width = DEFAULT_NAME_WIDTH;
  530.     new->name_adjust = K_UNSET;
  531.     new->icon_list = ap_make_array(p, 4, sizeof(struct item));
  532.     new->alt_list = ap_make_array(p, 4, sizeof(struct item));
  533.     new->desc_list = ap_make_array(p, 4, sizeof(struct item));
  534.     new->ign_list = ap_make_array(p, 4, sizeof(struct item));
  535.     new->hdr_list = ap_make_array(p, 4, sizeof(struct item));
  536.     new->rdme_list = ap_make_array(p, 4, sizeof(struct item));
  537.     new->opts = 0;
  538.     new->incremented_opts = 0;
  539.     new->decremented_opts = 0;
  540.     new->default_order = NULL;
  541.  
  542.     return (void *) new;
  543. }
  544.  
  545. static void *merge_autoindex_configs(pool *p, void *basev, void *addv)
  546. {
  547.     autoindex_config_rec *new;
  548.     autoindex_config_rec *base = (autoindex_config_rec *) basev;
  549.     autoindex_config_rec *add = (autoindex_config_rec *) addv;
  550.  
  551.     new = (autoindex_config_rec *) ap_pcalloc(p, sizeof(autoindex_config_rec));
  552.     new->default_icon = add->default_icon ? add->default_icon
  553.                                           : base->default_icon;
  554.     new->icon_height = add->icon_height ? add->icon_height : base->icon_height;
  555.     new->icon_width = add->icon_width ? add->icon_width : base->icon_width;
  556.  
  557.     new->alt_list = ap_append_arrays(p, add->alt_list, base->alt_list);
  558.     new->ign_list = ap_append_arrays(p, add->ign_list, base->ign_list);
  559.     new->hdr_list = ap_append_arrays(p, add->hdr_list, base->hdr_list);
  560.     new->desc_list = ap_append_arrays(p, add->desc_list, base->desc_list);
  561.     new->icon_list = ap_append_arrays(p, add->icon_list, base->icon_list);
  562.     new->rdme_list = ap_append_arrays(p, add->rdme_list, base->rdme_list);
  563.     if (add->opts & NO_OPTIONS) {
  564.     /*
  565.      * If the current directory says 'no options' then we also
  566.      * clear any incremental mods from being inheritable further down.
  567.      */
  568.     new->opts = NO_OPTIONS;
  569.     new->incremented_opts = 0;
  570.     new->decremented_opts = 0;
  571.     }
  572.     else {
  573.     /*
  574.      * If there were any non-incremental options selected for
  575.      * this directory, they dominate and we don't inherit *anything.*
  576.      * Contrariwise, we *do* inherit if the only settings here are
  577.      * incremental ones.
  578.      */
  579.     if (add->opts == 0) {
  580.         new->incremented_opts = (base->incremented_opts 
  581.                      | add->incremented_opts)
  582.                             & ~add->decremented_opts;
  583.         new->decremented_opts = (base->decremented_opts
  584.                      | add->decremented_opts);
  585.         /*
  586.          * We may have incremental settings, so make sure we don't
  587.          * inadvertently inherit an IndexOptions None from above.
  588.          */
  589.         new->opts = (base->opts & ~NO_OPTIONS);
  590.     }
  591.     else {
  592.         /*
  593.          * There are local non-incremental settings, which clear
  594.          * all inheritance from above.  They *are* the new base settings.
  595.          */
  596.         new->opts = add->opts;;
  597.     }
  598.     /*
  599.      * We're guaranteed that there'll be no overlap between
  600.      * the add-options and the remove-options.
  601.      */
  602.     new->opts |= new->incremented_opts;
  603.     new->opts &= ~new->decremented_opts;
  604.     }
  605.     /*
  606.      * Inherit the NameWidth settings if there aren't any specific to
  607.      * the new location; otherwise we'll end up using the defaults set in the
  608.      * config-rec creation routine.
  609.      */
  610.     if (add->name_adjust == K_UNSET) {
  611.     new->name_width = base->name_width;
  612.     new->name_adjust = base->name_adjust;
  613.     }
  614.     else {
  615.     new->name_width = add->name_width;
  616.     new->name_adjust = add->name_adjust;
  617.     }
  618.  
  619.     new->default_order = (add->default_order != NULL)
  620.     ? add->default_order : base->default_order;
  621.     return new;
  622. }
  623.  
  624. /****************************************************************
  625.  *
  626.  * Looking things up in config entries...
  627.  */
  628.  
  629. /* Structure used to hold entries when we're actually building an index */
  630.  
  631. struct ent {
  632.     char *name;
  633.     char *icon;
  634.     char *alt;
  635.     char *desc;
  636.     off_t size;
  637.     time_t lm;
  638.     struct ent *next;
  639.     int ascending;
  640.     char key;
  641. };
  642.  
  643. static char *find_item(request_rec *r, array_header *list, int path_only)
  644. {
  645.     const char *content_type = r->content_type;
  646.     const char *content_encoding = r->content_encoding;
  647.     char *path = r->filename;
  648.  
  649.     struct item *items = (struct item *) list->elts;
  650.     int i;
  651.  
  652.     for (i = 0; i < list->nelts; ++i) {
  653.     struct item *p = &items[i];
  654.  
  655.     /* Special cased for ^^DIRECTORY^^ and ^^BLANKICON^^ */
  656.     if ((path[0] == '^') || (!ap_strcmp_match(path, p->apply_path))) {
  657.         if (!*(p->apply_to)) {
  658.         return p->data;
  659.         }
  660.         else if (p->type == BY_PATH || path[0] == '^') {
  661.             if (!ap_strcmp_match(path, p->apply_to)) {
  662.             return p->data;
  663.         }
  664.         }
  665.         else if (!path_only) {
  666.         if (!content_encoding) {
  667.             if (p->type == BY_TYPE) {
  668.             if (content_type
  669.                 && !ap_strcasecmp_match(content_type,
  670.                             p->apply_to)) {
  671.                 return p->data;
  672.             }
  673.             }
  674.         }
  675.         else {
  676.             if (p->type == BY_ENCODING) {
  677.             if (!ap_strcasecmp_match(content_encoding,
  678.                          p->apply_to)) {
  679.                 return p->data;
  680.             }
  681.             }
  682.         }
  683.         }
  684.     }
  685.     }
  686.     return NULL;
  687. }
  688.  
  689. #define find_icon(d,p,t) find_item(p,d->icon_list,t)
  690. #define find_alt(d,p,t) find_item(p,d->alt_list,t)
  691. #define find_desc(d,p) find_item(p,d->desc_list,0)
  692. #define find_header(d,p) find_item(p,d->hdr_list,0)
  693. #define find_readme(d,p) find_item(p,d->rdme_list,0)
  694.  
  695. static char *find_default_icon(autoindex_config_rec *d, char *bogus_name)
  696. {
  697.     request_rec r;
  698.  
  699.     /* Bleah.  I tried to clean up find_item, and it lead to this bit
  700.      * of ugliness.   Note that the fields initialized are precisely
  701.      * those that find_item looks at...
  702.      */
  703.  
  704.     r.filename = bogus_name;
  705.     r.content_type = r.content_encoding = NULL;
  706.  
  707.     return find_item(&r, d->icon_list, 1);
  708. }
  709.  
  710. static int ignore_entry(autoindex_config_rec *d, char *path)
  711. {
  712.     array_header *list = d->ign_list;
  713.     struct item *items = (struct item *) list->elts;
  714.     char *tt;
  715.     int i;
  716.  
  717.     if ((tt = strrchr(path, '/')) == NULL) {
  718.     tt = path;
  719.     }
  720.     else {
  721.     tt++;
  722.     }
  723.  
  724.     for (i = 0; i < list->nelts; ++i) {
  725.     struct item *p = &items[i];
  726.     char *ap;
  727.  
  728.     if ((ap = strrchr(p->apply_to, '/')) == NULL) {
  729.         ap = p->apply_to;
  730.     }
  731.     else {
  732.         ap++;
  733.     }
  734.  
  735. #ifndef CASE_BLIND_FILESYSTEM
  736.     if (!ap_strcmp_match(path, p->apply_path)
  737.         && !ap_strcmp_match(tt, ap)) {
  738.         return 1;
  739.     }
  740. #else  /* !CASE_BLIND_FILESYSTEM */
  741.     /*
  742.      * On some platforms, the match must be case-blind.  This is really
  743.      * a factor of the filesystem involved, but we can't detect that
  744.      * reliably - so we have to granularise at the OS level.
  745.      */
  746.     if (!ap_strcasecmp_match(path, p->apply_path)
  747.         && !ap_strcasecmp_match(tt, ap)) {
  748.         return 1;
  749.     }
  750. #endif /* !CASE_BLIND_FILESYSTEM */
  751.     }
  752.     return 0;
  753. }
  754.  
  755. /*****************************************************************
  756.  *
  757.  * Actually generating output
  758.  */
  759.  
  760. /*
  761.  * Look for the specified file, and pump it into the response stream if we
  762.  * find it.
  763.  */
  764. static int insert_readme(char *name, char *readme_fname, char *title,
  765.              int hrule, int whichend, request_rec *r)
  766. {
  767.     char *fn;
  768.     FILE *f;
  769.     struct stat finfo;
  770.     int plaintext = 0;
  771.     request_rec *rr;
  772.     autoindex_config_rec *cfg;
  773.     int autoindex_opts;
  774.  
  775.     cfg = (autoindex_config_rec *) ap_get_module_config(r->per_dir_config,
  776.                             &autoindex_module);
  777.     autoindex_opts = cfg->opts;
  778.     /* XXX: this is a load of crap, it needs to do a full sub_req_lookup_uri */
  779.     fn = ap_make_full_path(r->pool, name, readme_fname);
  780.     fn = ap_pstrcat(r->pool, fn, ".html", NULL);
  781.     if (stat(fn, &finfo) == -1) {
  782.     /* A brief fake multiviews search for README.html */
  783.     fn[strlen(fn) - 5] = '\0';
  784.     if (stat(fn, &finfo) == -1) {
  785.         return 0;
  786.     }
  787.     plaintext = 1;
  788.     if (hrule) {
  789.         ap_rputs("<HR>\n", r);
  790.     }
  791.     }
  792.     else if (hrule) {
  793.     ap_rputs("<HR>\n", r);
  794.     }
  795.     /* XXX: when the above is rewritten properly, this necessary security
  796.      * check will be redundant. -djg */
  797.     rr = ap_sub_req_lookup_file(fn, r);
  798.     if (rr->status != HTTP_OK) {
  799.     ap_destroy_sub_req(rr);
  800.     return 0;
  801.     }
  802.     ap_destroy_sub_req(rr);
  803.     if (!(f = ap_pfopen(r->pool, fn, "r"))) {
  804.         return 0;
  805.     }
  806.     if ((whichend == FRONT_MATTER)
  807.     && (!(autoindex_opts & SUPPRESS_PREAMBLE))) {
  808.     emit_preamble(r, title);
  809.     }
  810.     if (!plaintext) {
  811.     ap_send_fd(f, r);
  812.     }
  813.     else {
  814.     char buf[IOBUFSIZE + 1];
  815.     int i, n, c, ch;
  816.     ap_rputs("<PRE>\n", r);
  817.     while (!feof(f)) {
  818.         do {
  819.         n = fread(buf, sizeof(char), IOBUFSIZE, f);
  820.         }
  821.         while (n == -1 && ferror(f) && errno == EINTR);
  822.         if (n == -1 || n == 0) {
  823.         break;
  824.         }
  825.         buf[n] = '\0';
  826.         c = 0;
  827.         while (c < n) {
  828.             for (i = c; i < n; i++) {
  829.             if (buf[i] == '<' || buf[i] == '>' || buf[i] == '&') {
  830.             break;
  831.             }
  832.         }
  833.         ch = buf[i];
  834.         buf[i] = '\0';
  835.         ap_rputs(&buf[c], r);
  836.         if (ch == '<') {
  837.             ap_rputs("<", r);
  838.         }
  839.         else if (ch == '>') {
  840.             ap_rputs(">", r);
  841.         }
  842.         else if (ch == '&') {
  843.             ap_rputs("&", r);
  844.         }
  845.         c = i + 1;
  846.         }
  847.     }
  848.     }
  849.     ap_pfclose(r->pool, f);
  850.     if (plaintext) {
  851.     ap_rputs("</PRE>\n", r);
  852.     }
  853.     return 1;
  854. }
  855.  
  856.  
  857. static char *find_title(request_rec *r)
  858. {
  859.     char titlebuf[MAX_STRING_LEN], *find = "<TITLE>";
  860.     FILE *thefile = NULL;
  861.     int x, y, n, p;
  862.  
  863.     if (r->status != HTTP_OK) {
  864.     return NULL;
  865.     }
  866.     if (r->content_type
  867.     && (!strcmp(r->content_type, "text/html")
  868.         || !strcmp(r->content_type, INCLUDES_MAGIC_TYPE))
  869.     && !r->content_encoding) {
  870.         if (!(thefile = ap_pfopen(r->pool, r->filename, "r"))) {
  871.         return NULL;
  872.     }
  873.     n = fread(titlebuf, sizeof(char), MAX_STRING_LEN - 1, thefile);
  874.     if (n <= 0) {
  875.         ap_pfclose(r->pool, thefile);
  876.         return NULL;
  877.     }
  878.     titlebuf[n] = '\0';
  879.     for (x = 0, p = 0; titlebuf[x]; x++) {
  880.         if (ap_toupper(titlebuf[x]) == find[p]) {
  881.         if (!find[++p]) {
  882.             if ((p = ap_ind(&titlebuf[++x], '<')) != -1) {
  883.             titlebuf[x + p] = '\0';
  884.             }
  885.             /* Scan for line breaks for Tanmoy's secretary */
  886.             for (y = x; titlebuf[y]; y++) {
  887.             if ((titlebuf[y] == CR) || (titlebuf[y] == LF)) {
  888.                 if (y == x) {
  889.                 x++;
  890.                 }
  891.                 else {
  892.                 titlebuf[y] = ' ';
  893.                 }
  894.             }
  895.             }
  896.             ap_pfclose(r->pool, thefile);
  897.             return ap_pstrdup(r->pool, &titlebuf[x]);
  898.         }
  899.         }
  900.         else {
  901.         p = 0;
  902.         }
  903.     }
  904.     ap_pfclose(r->pool, thefile);
  905.     }
  906.     return NULL;
  907. }
  908.  
  909. static struct ent *make_autoindex_entry(char *name, int autoindex_opts,
  910.                     autoindex_config_rec *d,
  911.                     request_rec *r, char keyid,
  912.                     char direction)
  913. {
  914.     struct ent *p;
  915.  
  916.     if ((name[0] == '.') && (!name[1])) {
  917.     return (NULL);
  918.     }
  919.  
  920.     if (ignore_entry(d, ap_make_full_path(r->pool, r->filename, name))) {
  921.         return (NULL);
  922.     }
  923.  
  924.     p = (struct ent *) ap_pcalloc(r->pool, sizeof(struct ent));
  925.     p->name = ap_pstrdup(r->pool, name);
  926.     p->size = -1;
  927.     p->icon = NULL;
  928.     p->alt = NULL;
  929.     p->desc = NULL;
  930.     p->lm = -1;
  931.     p->key = ap_toupper(keyid);
  932.     p->ascending = (ap_toupper(direction) == D_ASCENDING);
  933.  
  934.     if (autoindex_opts & FANCY_INDEXING) {
  935.     request_rec *rr = ap_sub_req_lookup_file(name, r);
  936.  
  937.     if (rr->finfo.st_mode != 0) {
  938.         p->lm = rr->finfo.st_mtime;
  939.         if (S_ISDIR(rr->finfo.st_mode)) {
  940.             if (!(p->icon = find_icon(d, rr, 1))) {
  941.             p->icon = find_default_icon(d, "^^DIRECTORY^^");
  942.         }
  943.         if (!(p->alt = find_alt(d, rr, 1))) {
  944.             p->alt = "DIR";
  945.         }
  946.         p->size = -1;
  947.         p->name = ap_pstrcat(r->pool, name, "/", NULL);
  948.         }
  949.         else {
  950.         p->icon = find_icon(d, rr, 0);
  951.         p->alt = find_alt(d, rr, 0);
  952.         p->size = rr->finfo.st_size;
  953.         }
  954.     }
  955.  
  956.     p->desc = find_desc(d, rr);
  957.  
  958.     if ((!p->desc) && (autoindex_opts & SCAN_HTML_TITLES)) {
  959.         p->desc = ap_pstrdup(r->pool, find_title(rr));
  960.     }
  961.  
  962.     ap_destroy_sub_req(rr);
  963.     }
  964.     /*
  965.      * We don't need to take any special action for the file size key.  If
  966.      * we did, it would go here.
  967.      */
  968.     if (keyid == K_LAST_MOD) {
  969.         if (p->lm < 0) {
  970.         p->lm = 0;
  971.     }
  972.     }
  973.     return (p);
  974. }
  975.  
  976. static char *terminate_description(autoindex_config_rec *d, char *desc,
  977.                    int autoindex_opts)
  978. {
  979.     int maxsize = 23;
  980.     register int x;
  981.  
  982.     if (autoindex_opts & SUPPRESS_LAST_MOD) {
  983.     maxsize += 19;
  984.     }
  985.     if (autoindex_opts & SUPPRESS_SIZE) {
  986.     maxsize += 7;
  987.     }
  988.  
  989.     for (x = 0; desc[x] && (maxsize > 0 || desc[x]=='<'); x++) {
  990.     if (desc[x] == '<') {
  991.         while (desc[x] != '>') {
  992.         if (!desc[x]) {
  993.             maxsize = 0;
  994.             break;
  995.         }
  996.         ++x;
  997.         }
  998.     }
  999.      else if (desc[x] == '&') {
  1000.          /* entities like ä count as one character */
  1001.          --maxsize;
  1002.          for ( ; desc[x] != ';'; ++x) {
  1003.          if (desc[x] == '\0') {
  1004.                      maxsize = 0;
  1005.                      break;
  1006.         }
  1007.         }
  1008.         }
  1009.     else {
  1010.         --maxsize;
  1011.     }
  1012.     }
  1013.     if (!maxsize && desc[x] != '\0') {
  1014.     desc[x - 1] = '>';    /* Grump. */
  1015.     desc[x] = '\0';        /* Double Grump! */
  1016.     }
  1017.     return desc;
  1018. }
  1019.  
  1020. /*
  1021.  * Emit the anchor for the specified field.  If a field is the key for the
  1022.  * current request, the link changes its meaning to reverse the order when
  1023.  * selected again.  Non-active fields always start in ascending order.
  1024.  */
  1025. static void emit_link(request_rec *r, char *anchor, char fname, char curkey,
  1026.                       char curdirection, int nosort)
  1027. {
  1028.     char qvalue[5];
  1029.     int reverse;
  1030.  
  1031.     if (!nosort) {
  1032.     qvalue[0] = '?';
  1033.     qvalue[1] = fname;
  1034.     qvalue[2] = '=';
  1035.     qvalue[4] = '\0';
  1036.     reverse = ((curkey == fname) && (curdirection == D_ASCENDING));
  1037.     qvalue[3] = reverse ? D_DESCENDING : D_ASCENDING;
  1038.     ap_rvputs(r, "<A HREF=\"", qvalue, "\">", anchor, "</A>", NULL);
  1039.     }
  1040.     else {
  1041.         ap_rputs(anchor, r);
  1042.     }
  1043. }
  1044.  
  1045. /*
  1046.  * Fit a string into a specified buffer width, marking any
  1047.  * truncation.  The size argument is the actual buffer size, including
  1048.  * the \0 termination byte.  The buffer will be prefilled with blanks.
  1049.  * If the pad argument is false, any extra spaces at the end of the
  1050.  * buffer are omitted.  (Used when constructing anchors.)
  1051.  */
  1052. static ap_inline char *widthify(const char *s, char *buff, int size, int pad)
  1053. {
  1054.     int s_len;
  1055.  
  1056.     memset(buff, ' ', size);
  1057.     buff[size - 1] = '\0';
  1058.     s_len = strlen(s);
  1059.     if (s_len > (size - 1)) {
  1060.     ap_cpystrn(buff, s, size);
  1061.     if (size > 1) {
  1062.         buff[size - 2] = '>';
  1063.     }
  1064.     if (size > 2) {
  1065.         buff[size - 3] = '.';
  1066.     }
  1067.     if (size > 3) {
  1068.         buff[size - 4] = '.';
  1069.     }
  1070.     }
  1071.     else {
  1072.     ap_cpystrn(buff, s, s_len + 1);
  1073.     if (pad) {
  1074.         buff[s_len] = ' ';
  1075.     }
  1076.     }
  1077.     return buff;
  1078. }
  1079.  
  1080. static void output_directories(struct ent **ar, int n,
  1081.                    autoindex_config_rec *d, request_rec *r,
  1082.                    int autoindex_opts, char keyid, char direction)
  1083. {
  1084.     int x;
  1085.     char *name = r->uri;
  1086.     char *tp;
  1087.     int static_columns = (autoindex_opts & SUPPRESS_COLSORT);
  1088.     pool *scratch = ap_make_sub_pool(r->pool);
  1089.     int name_width;
  1090.     char *name_scratch;
  1091.  
  1092.     if (name[0] == '\0') {
  1093.     name = "/";
  1094.     }
  1095.  
  1096.     name_width = d->name_width;
  1097.     if (d->name_adjust == K_ADJUST) {
  1098.     for (x = 0; x < n; x++) {
  1099.         int t = strlen(ar[x]->name);
  1100.         if (t > name_width) {
  1101.         name_width = t;
  1102.         }
  1103.     }
  1104.     }
  1105.     ++name_width;
  1106.     name_scratch = ap_palloc(r->pool, name_width + 1);
  1107.     memset(name_scratch, ' ', name_width);
  1108.     name_scratch[name_width] = '\0';
  1109.  
  1110.     if (autoindex_opts & FANCY_INDEXING) {
  1111.     ap_rputs("<PRE>", r);
  1112.     if ((tp = find_default_icon(d, "^^BLANKICON^^"))) {
  1113.         ap_rvputs(r, "<IMG SRC=\"", ap_escape_html(scratch, tp),
  1114.            "\" ALT=\"     \"", NULL);
  1115.         if (d->icon_width && d->icon_height) {
  1116.         ap_rprintf
  1117.             (
  1118.             r,
  1119.             " HEIGHT=\"%d\" WIDTH=\"%d\"",
  1120.             d->icon_height,
  1121.             d->icon_width
  1122.             );
  1123.         }
  1124.         ap_rputs("> ", r);
  1125.     }
  1126.         emit_link(r, widthify("Name", name_scratch,
  1127.                   (name_width > 5) ? 5 : name_width, K_NOPAD),
  1128.           K_NAME, keyid, direction, static_columns);
  1129.     if (name_width > 5) {
  1130.         memset(name_scratch, ' ', name_width);
  1131.         name_scratch[name_width] = '\0';
  1132.         ap_rputs(&name_scratch[5], r);
  1133.     }
  1134.     /*
  1135.      * Emit the guaranteed-at-least-one-space-between-columns byte.
  1136.      */
  1137.     ap_rputs(" ", r);
  1138.     if (!(autoindex_opts & SUPPRESS_LAST_MOD)) {
  1139.             emit_link(r, "Last modified", K_LAST_MOD, keyid, direction,
  1140.                       static_columns);
  1141.         ap_rputs("       ", r);
  1142.     }
  1143.     if (!(autoindex_opts & SUPPRESS_SIZE)) {
  1144.             emit_link(r, "Size", K_SIZE, keyid, direction, static_columns);
  1145.         ap_rputs("  ", r);
  1146.     }
  1147.     if (!(autoindex_opts & SUPPRESS_DESC)) {
  1148.             emit_link(r, "Description", K_DESC, keyid, direction,
  1149.                       static_columns);
  1150.     }
  1151.     ap_rputs("\n<HR>\n", r);
  1152.     }
  1153.     else {
  1154.     ap_rputs("<UL>", r);
  1155.     }
  1156.  
  1157.     for (x = 0; x < n; x++) {
  1158.     char *anchor, *t, *t2;
  1159.     char *pad;
  1160.     int nwidth;
  1161.  
  1162.     ap_clear_pool(scratch);
  1163.  
  1164.     if (is_parent(ar[x]->name)) {
  1165.         t = ap_make_full_path(scratch, name, "../");
  1166.         ap_getparents(t);
  1167.         if (t[0] == '\0') {
  1168.         t = "/";
  1169.         }
  1170.            /* 1234567890123456 */
  1171.         t2 = "Parent Directory";
  1172.         pad = name_scratch + 16;
  1173.         anchor = ap_escape_html(scratch, ap_os_escape_path(scratch, t, 0));
  1174.     }
  1175.     else {
  1176.         t = ar[x]->name;
  1177.         pad = name_scratch + strlen(t);
  1178.         t2 = ap_escape_html(scratch, t);
  1179.         anchor = ap_escape_html(scratch, ap_os_escape_path(scratch, t, 0));
  1180.     }
  1181.  
  1182.     if (autoindex_opts & FANCY_INDEXING) {
  1183.         if (autoindex_opts & ICONS_ARE_LINKS) {
  1184.         ap_rvputs(r, "<A HREF=\"", anchor, "\">", NULL);
  1185.         }
  1186.         if ((ar[x]->icon) || d->default_icon) {
  1187.         ap_rvputs(r, "<IMG SRC=\"",
  1188.               ap_escape_html(scratch,
  1189.                      ar[x]->icon ? ar[x]->icon
  1190.                                  : d->default_icon),
  1191.               "\" ALT=\"[", (ar[x]->alt ? ar[x]->alt : "   "),
  1192.               "]\"", NULL);
  1193.         if (d->icon_width && d->icon_height) {
  1194.             ap_rprintf(r, " HEIGHT=\"%d\" WIDTH=\"%d\"",
  1195.                    d->icon_height, d->icon_width);
  1196.         }
  1197.         ap_rputs(">", r);
  1198.         }
  1199.         if (autoindex_opts & ICONS_ARE_LINKS) {
  1200.         ap_rputs("</A>", r);
  1201.         }
  1202.  
  1203.         ap_rvputs(r, " <A HREF=\"", anchor, "\">",
  1204.               widthify(t2, name_scratch, name_width, K_NOPAD),
  1205.               "</A>", NULL);
  1206.         /*
  1207.          * We know that widthify() prefilled the buffer with spaces
  1208.          * before doing its thing, so use them.
  1209.          */
  1210.         nwidth = strlen(t2);
  1211.         if (nwidth < (name_width - 1)) {
  1212.         name_scratch[nwidth] = ' ';
  1213.         ap_rputs(&name_scratch[nwidth], r);
  1214.         }
  1215.         /*
  1216.          * The blank before the storm.. er, before the next field.
  1217.          */
  1218.         ap_rputs(" ", r);
  1219.         if (!(autoindex_opts & SUPPRESS_LAST_MOD)) {
  1220.         if (ar[x]->lm != -1) {
  1221.             char time_str[MAX_STRING_LEN];
  1222.             struct tm *ts = localtime(&ar[x]->lm);
  1223.             strftime(time_str, MAX_STRING_LEN, "%d-%b-%Y %H:%M  ", ts);
  1224.             ap_rputs(time_str, r);
  1225.         }
  1226.         else {
  1227.             /*Length="22-Feb-1998 23:42  " (see 4 lines above) */
  1228.             ap_rputs("                   ", r);
  1229.         }
  1230.         }
  1231.         if (!(autoindex_opts & SUPPRESS_SIZE)) {
  1232.         ap_send_size(ar[x]->size, r);
  1233.         ap_rputs("  ", r);
  1234.         }
  1235.         if (!(autoindex_opts & SUPPRESS_DESC)) {
  1236.         if (ar[x]->desc) {
  1237.             ap_rputs(terminate_description(d, ar[x]->desc,
  1238.                            autoindex_opts), r);
  1239.         }
  1240.         }
  1241.     }
  1242.     else {
  1243.         ap_rvputs(r, "<LI><A HREF=\"", anchor, "\"> ", t2,
  1244.               "</A>", pad, NULL);
  1245.     }
  1246.     ap_rputc('\n', r);
  1247.     }
  1248.     if (autoindex_opts & FANCY_INDEXING) {
  1249.     ap_rputs("</PRE>", r);
  1250.     }
  1251.     else {
  1252.     ap_rputs("</UL>", r);
  1253.     }
  1254. }
  1255.  
  1256. /*
  1257.  * Compare two file entries according to the sort criteria.  The return
  1258.  * is essentially a signum function value.
  1259.  */
  1260.  
  1261. static int dsortf(struct ent **e1, struct ent **e2)
  1262. {
  1263.     struct ent *c1;
  1264.     struct ent *c2;
  1265.     int result = 0;
  1266.  
  1267.     /*
  1268.      * First, see if either of the entries is for the parent directory.
  1269.      * If so, that *always* sorts lower than anything else.
  1270.      */
  1271.     if (is_parent((*e1)->name)) {
  1272.         return -1;
  1273.     }
  1274.     if (is_parent((*e2)->name)) {
  1275.         return 1;
  1276.     }
  1277.     /*
  1278.      * All of our comparisons will be of the c1 entry against the c2 one,
  1279.      * so assign them appropriately to take care of the ordering.
  1280.      */
  1281.     if ((*e1)->ascending) {
  1282.         c1 = *e1;
  1283.         c2 = *e2;
  1284.     }
  1285.     else {
  1286.         c1 = *e2;
  1287.         c2 = *e1;
  1288.     }
  1289.     switch (c1->key) {
  1290.     case K_LAST_MOD:
  1291.     if (c1->lm > c2->lm) {
  1292.             return 1;
  1293.         }
  1294.         else if (c1->lm < c2->lm) {
  1295.             return -1;
  1296.         }
  1297.         break;
  1298.     case K_SIZE:
  1299.         if (c1->size > c2->size) {
  1300.             return 1;
  1301.         }
  1302.         else if (c1->size < c2->size) {
  1303.             return -1;
  1304.         }
  1305.         break;
  1306.     case K_DESC:
  1307.         result = strcmp(c1->desc ? c1->desc : "", c2->desc ? c2->desc : "");
  1308.         if (result) {
  1309.             return result;
  1310.         }
  1311.         break;
  1312.     }
  1313.     return strcmp(c1->name, c2->name);
  1314. }
  1315.  
  1316.  
  1317. static int index_directory(request_rec *r,
  1318.                autoindex_config_rec *autoindex_conf)
  1319. {
  1320.     char *title_name = ap_escape_html(r->pool, r->uri);
  1321.     char *title_endp;
  1322.     char *name = r->filename;
  1323.  
  1324.     DIR *d;
  1325.     struct DIR_TYPE *dstruct;
  1326.     int num_ent = 0, x;
  1327.     struct ent *head, *p;
  1328.     struct ent **ar = NULL;
  1329.     char *tmp;
  1330.     const char *qstring;
  1331.     int autoindex_opts = autoindex_conf->opts;
  1332.     char keyid;
  1333.     char direction;
  1334.  
  1335.     if (!(d = ap_popendir(r->pool, name))) {
  1336.     ap_log_rerror(APLOG_MARK, APLOG_ERR, r,
  1337.             "Can't open directory for index: %s", r->filename);
  1338.     return HTTP_FORBIDDEN;
  1339.     }
  1340.  
  1341.     r->content_type = "text/html";
  1342.  
  1343.     ap_send_http_header(r);
  1344.  
  1345.     if (r->header_only) {
  1346.     ap_pclosedir(r->pool, d);
  1347.     return 0;
  1348.     }
  1349.     ap_hard_timeout("send directory", r);
  1350.  
  1351.     /* Spew HTML preamble */
  1352.  
  1353.     title_endp = title_name + strlen(title_name) - 1;
  1354.  
  1355.     while (title_endp > title_name && *title_endp == '/') {
  1356.     *title_endp-- = '\0';
  1357.     }
  1358.  
  1359.     if ((!(tmp = find_header(autoindex_conf, r)))
  1360.     || (!(insert_readme(name, tmp, title_name, NO_HRULE, FRONT_MATTER, r)))
  1361.     ) {
  1362.     emit_preamble(r, title_name);
  1363.     ap_rvputs(r, "<H1>Index of ", title_name, "</H1>\n", NULL);
  1364.     }
  1365.  
  1366.     /*
  1367.      * Figure out what sort of indexing (if any) we're supposed to use.
  1368.      *
  1369.      * If no QUERY_STRING was specified or column sorting has been
  1370.      * explicitly disabled, we use the default specified by the
  1371.      * IndexOrderDefault directive (if there is one); otherwise,
  1372.      * we fall back to ascending by name.
  1373.      */
  1374.     qstring = r->args;
  1375.     if ((autoindex_opts & SUPPRESS_COLSORT)
  1376.     || ((qstring == NULL) || (*qstring == '\0'))) {
  1377.     qstring = autoindex_conf->default_order;
  1378.     }
  1379.     /*
  1380.      * If there is no specific ordering defined for this directory,
  1381.      * default to ascending by filename.
  1382.      */
  1383.     if ((qstring == NULL) || (*qstring == '\0')) {
  1384.     keyid = K_NAME;
  1385.     direction = D_ASCENDING;
  1386.     }
  1387.     else {
  1388.     keyid = *qstring;
  1389.     ap_getword(r->pool, &qstring, '=');
  1390.     if (qstring != '\0') {
  1391.         direction = *qstring;
  1392.     }
  1393.     else {
  1394.         direction = D_ASCENDING;
  1395.     }
  1396.     }
  1397.  
  1398.     /* 
  1399.      * Since we don't know how many dir. entries there are, put them into a 
  1400.      * linked list and then arrayificate them so qsort can use them. 
  1401.      */
  1402.     head = NULL;
  1403.     while ((dstruct = readdir(d))) {
  1404.     p = make_autoindex_entry(dstruct->d_name, autoindex_opts,
  1405.                  autoindex_conf, r, keyid, direction);
  1406.     if (p != NULL) {
  1407.         p->next = head;
  1408.         head = p;
  1409.         num_ent++;
  1410.     }
  1411.     }
  1412.     if (num_ent > 0) {
  1413.     ar = (struct ent **) ap_palloc(r->pool,
  1414.                        num_ent * sizeof(struct ent *));
  1415.     p = head;
  1416.     x = 0;
  1417.     while (p) {
  1418.         ar[x++] = p;
  1419.         p = p->next;
  1420.     }
  1421.  
  1422.     qsort((void *) ar, num_ent, sizeof(struct ent *),
  1423.           (int (*)(const void *, const void *)) dsortf);
  1424.     }
  1425.     output_directories(ar, num_ent, autoindex_conf, r, autoindex_opts, keyid,
  1426.                direction);
  1427.     ap_pclosedir(r->pool, d);
  1428.  
  1429.     if ((tmp = find_readme(autoindex_conf, r))) {
  1430.     if (!insert_readme(name, tmp, "",
  1431.                ((autoindex_opts & FANCY_INDEXING) ? HRULE
  1432.                                                   : NO_HRULE),
  1433.                END_MATTER, r)) {
  1434.         ap_rputs(ap_psignature("<HR>\n", r), r);
  1435.     }
  1436.     }
  1437.     ap_rputs("</BODY></HTML>\n", r);
  1438.  
  1439.     ap_kill_timeout(r);
  1440.     return 0;
  1441. }
  1442.  
  1443. /* The formal handler... */
  1444.  
  1445. static int handle_autoindex(request_rec *r)
  1446. {
  1447.     autoindex_config_rec *d;
  1448.     int allow_opts = ap_allow_options(r);
  1449.  
  1450.     d = (autoindex_config_rec *) ap_get_module_config(r->per_dir_config,
  1451.                               &autoindex_module);
  1452.  
  1453.     r->allowed |= (1 << M_GET);
  1454.     if (r->method_number != M_GET) {
  1455.     return DECLINED;
  1456.     }
  1457.  
  1458.     /* OK, nothing easy.  Trot out the heavy artillery... */
  1459.  
  1460.     if (allow_opts & OPT_INDEXES) {
  1461.     /* KLUDGE --- make the sub_req lookups happen in the right directory.
  1462.      * Fixing this in the sub_req_lookup functions themselves is difficult,
  1463.      * and would probably break virtual includes...
  1464.      */
  1465.  
  1466.     if (r->filename[strlen(r->filename) - 1] != '/') {
  1467.         r->filename = ap_pstrcat(r->pool, r->filename, "/", NULL);
  1468.     }
  1469.     return index_directory(r, d);
  1470.     }
  1471.     else {
  1472.     ap_log_rerror(APLOG_MARK, APLOG_NOERRNO|APLOG_ERR, r,
  1473.              "Directory index forbidden by rule: %s", r->filename);
  1474.     return HTTP_FORBIDDEN;
  1475.     }
  1476. }
  1477.  
  1478.  
  1479. static const handler_rec autoindex_handlers[] =
  1480. {
  1481.     {DIR_MAGIC_TYPE, handle_autoindex},
  1482.     {NULL}
  1483. };
  1484.  
  1485. module MODULE_VAR_EXPORT autoindex_module =
  1486. {
  1487.     STANDARD_MODULE_STUFF,
  1488.     NULL,            /* initializer */
  1489.     create_autoindex_config,    /* dir config creater */
  1490.     merge_autoindex_configs,    /* dir merger --- default is to override */
  1491.     NULL,            /* server config */
  1492.     NULL,            /* merge server config */
  1493.     autoindex_cmds,        /* command table */
  1494.     autoindex_handlers,        /* handlers */
  1495.     NULL,            /* filename translation */
  1496.     NULL,            /* check_user_id */
  1497.     NULL,            /* check auth */
  1498.     NULL,            /* check access */
  1499.     NULL,            /* type_checker */
  1500.     NULL,            /* fixups */
  1501.     NULL,            /* logger */
  1502.     NULL,            /* header parser */
  1503.     NULL,            /* child_init */
  1504.     NULL,            /* child_exit */
  1505.     NULL            /* post read-request */
  1506. };
  1507.