~ubuntu-branches/ubuntu/feisty/apache2/feisty

« back to all changes in this revision

Viewing changes to modules/generators/mod_autoindex.c

  • Committer: Bazaar Package Importer
  • Author(s): Andreas Barth
  • Date: 2006-12-09 21:05:45 UTC
  • mfrom: (0.6.1 upstream)
  • Revision ID: james.westby@ubuntu.com-20061209210545-h70s0xaqc2v8vqr2
Tags: 2.2.3-3.2
* Non-maintainer upload.
* 043_ajp_connection_reuse: Patch from upstream Bugzilla, fixing a critical
  issue with regard to connection reuse in mod_proxy_ajp.
  Closes: #396265

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Licensed to the Apache Software Foundation (ASF) under one or more
 
2
 * contributor license agreements.  See the NOTICE file distributed with
 
3
 * this work for additional information regarding copyright ownership.
 
4
 * The ASF licenses this file to You under the Apache License, Version 2.0
 
5
 * (the "License"); you may not use this file except in compliance with
 
6
 * the License.  You may obtain a copy of the License at
 
7
 *
 
8
 *     http://www.apache.org/licenses/LICENSE-2.0
 
9
 *
 
10
 * Unless required by applicable law or agreed to in writing, software
 
11
 * distributed under the License is distributed on an "AS IS" BASIS,
 
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 
13
 * See the License for the specific language governing permissions and
 
14
 * limitations under the License.
 
15
 */
 
16
 
 
17
/*
 
18
 * mod_autoindex.c: Handles the on-the-fly html index generation
 
19
 *
 
20
 * Rob McCool
 
21
 * 3/23/93
 
22
 *
 
23
 * Adapted to Apache by rst.
 
24
 *
 
25
 * Version sort added by Martin Pool <mbp@humbug.org.au>.
 
26
 */
 
27
 
 
28
#include "apr_strings.h"
 
29
#include "apr_fnmatch.h"
 
30
#include "apr_strings.h"
 
31
#include "apr_lib.h"
 
32
 
 
33
#define APR_WANT_STRFUNC
 
34
#include "apr_want.h"
 
35
 
 
36
#include "ap_config.h"
 
37
#include "httpd.h"
 
38
#include "http_config.h"
 
39
#include "http_core.h"
 
40
#include "http_request.h"
 
41
#include "http_protocol.h"
 
42
#include "http_log.h"
 
43
#include "http_main.h"
 
44
#include "util_script.h"
 
45
 
 
46
#include "mod_core.h"
 
47
 
 
48
module AP_MODULE_DECLARE_DATA autoindex_module;
 
49
 
 
50
/****************************************************************
 
51
 *
 
52
 * Handling configuration directives...
 
53
 */
 
54
 
 
55
#define NO_OPTIONS          (1 <<  0)  /* Indexing options */
 
56
#define ICONS_ARE_LINKS     (1 <<  1)
 
57
#define SCAN_HTML_TITLES    (1 <<  2)
 
58
#define SUPPRESS_ICON       (1 <<  3)
 
59
#define SUPPRESS_LAST_MOD   (1 <<  4)
 
60
#define SUPPRESS_SIZE       (1 <<  5)
 
61
#define SUPPRESS_DESC       (1 <<  6)
 
62
#define SUPPRESS_PREAMBLE   (1 <<  7)
 
63
#define SUPPRESS_COLSORT    (1 <<  8)
 
64
#define SUPPRESS_RULES      (1 <<  9)
 
65
#define FOLDERS_FIRST       (1 << 10)
 
66
#define VERSION_SORT        (1 << 11)
 
67
#define TRACK_MODIFIED      (1 << 12)
 
68
#define FANCY_INDEXING      (1 << 13)
 
69
#define TABLE_INDEXING      (1 << 14)
 
70
#define IGNORE_CLIENT       (1 << 15)
 
71
#define IGNORE_CASE         (1 << 16)
 
72
#define EMIT_XHTML          (1 << 17)
 
73
#define SHOW_FORBIDDEN      (1 << 18)
 
74
 
 
75
#define K_NOADJUST 0
 
76
#define K_ADJUST 1
 
77
#define K_UNSET 2
 
78
 
 
79
/*
 
80
 * Define keys for sorting.
 
81
 */
 
82
#define K_NAME 'N'              /* Sort by file name (default) */
 
83
#define K_LAST_MOD 'M'          /* Last modification date */
 
84
#define K_SIZE 'S'              /* Size (absolute, not as displayed) */
 
85
#define K_DESC 'D'              /* Description */
 
86
#define K_VALID "NMSD"          /* String containing _all_ valid K_ opts */
 
87
 
 
88
#define D_ASCENDING 'A'
 
89
#define D_DESCENDING 'D'
 
90
#define D_VALID "AD"            /* String containing _all_ valid D_ opts */
 
91
 
 
92
/*
 
93
 * These are the dimensions of the default icons supplied with Apache.
 
94
 */
 
95
#define DEFAULT_ICON_WIDTH 20
 
96
#define DEFAULT_ICON_HEIGHT 22
 
97
 
 
98
/*
 
99
 * Other default dimensions.
 
100
 */
 
101
#define DEFAULT_NAME_WIDTH 23
 
102
#define DEFAULT_DESC_WIDTH 23
 
103
 
 
104
struct item {
 
105
    char *type;
 
106
    char *apply_to;
 
107
    char *apply_path;
 
108
    char *data;
 
109
};
 
110
 
 
111
typedef struct ai_desc_t {
 
112
    char *pattern;
 
113
    char *description;
 
114
    int full_path;
 
115
    int wildcards;
 
116
} ai_desc_t;
 
117
 
 
118
typedef struct autoindex_config_struct {
 
119
 
 
120
    char *default_icon;
 
121
    char *style_sheet;
 
122
    apr_int32_t opts;
 
123
    apr_int32_t incremented_opts;
 
124
    apr_int32_t decremented_opts;
 
125
    int name_width;
 
126
    int name_adjust;
 
127
    int desc_width;
 
128
    int desc_adjust;
 
129
    int icon_width;
 
130
    int icon_height;
 
131
    char default_keyid;
 
132
    char default_direction;
 
133
 
 
134
    apr_array_header_t *icon_list;
 
135
    apr_array_header_t *alt_list;
 
136
    apr_array_header_t *desc_list;
 
137
    apr_array_header_t *ign_list;
 
138
    apr_array_header_t *hdr_list;
 
139
    apr_array_header_t *rdme_list;
 
140
 
 
141
} autoindex_config_rec;
 
142
 
 
143
static char c_by_encoding, c_by_type, c_by_path;
 
144
 
 
145
#define BY_ENCODING &c_by_encoding
 
146
#define BY_TYPE &c_by_type
 
147
#define BY_PATH &c_by_path
 
148
 
 
149
/*
 
150
 * This routine puts the standard HTML header at the top of the index page.
 
151
 * We include the DOCTYPE because we may be using features therefrom (i.e.,
 
152
 * HEIGHT and WIDTH attributes on the icons if we're FancyIndexing).
 
153
 */
 
154
static void emit_preamble(request_rec *r, int xhtml, const char *title)
 
155
{
 
156
    autoindex_config_rec *d;
 
157
 
 
158
    d = (autoindex_config_rec *) ap_get_module_config(r->per_dir_config,
 
159
                                                      &autoindex_module);
 
160
 
 
161
    ap_rvputs(r, xhtml ? DOCTYPE_XHTML_1_0T : DOCTYPE_HTML_3_2,
 
162
              "<html>\n <head>\n  <title>Index of ", title,
 
163
              "</title>\n", NULL);
 
164
    if (d->style_sheet != NULL) {
 
165
        ap_rvputs(r, "  <link rel=\"stylesheet\" href=\"", d->style_sheet,
 
166
                "\" type=\"text/css\"", xhtml ? " />\n" : ">\n", NULL);
 
167
    }
 
168
    ap_rvputs(r, " </head>\n <body>\n", NULL);
 
169
}
 
170
 
 
171
static void push_item(apr_array_header_t *arr, char *type, const char *to,
 
172
                      const char *path, const char *data)
 
173
{
 
174
    struct item *p = (struct item *) apr_array_push(arr);
 
175
 
 
176
    if (!to) {
 
177
        to = "";
 
178
    }
 
179
    if (!path) {
 
180
        path = "";
 
181
    }
 
182
 
 
183
    p->type = type;
 
184
    p->data = data ? apr_pstrdup(arr->pool, data) : NULL;
 
185
    p->apply_path = apr_pstrcat(arr->pool, path, "*", NULL);
 
186
 
 
187
    if ((type == BY_PATH) && (!ap_is_matchexp(to))) {
 
188
        p->apply_to = apr_pstrcat(arr->pool, "*", to, NULL);
 
189
    }
 
190
    else if (to) {
 
191
        p->apply_to = apr_pstrdup(arr->pool, to);
 
192
    }
 
193
    else {
 
194
        p->apply_to = NULL;
 
195
    }
 
196
}
 
197
 
 
198
static const char *add_alt(cmd_parms *cmd, void *d, const char *alt,
 
199
                           const char *to)
 
200
{
 
201
    if (cmd->info == BY_PATH) {
 
202
        if (!strcmp(to, "**DIRECTORY**")) {
 
203
            to = "^^DIRECTORY^^";
 
204
        }
 
205
    }
 
206
    if (cmd->info == BY_ENCODING) {
 
207
        char *tmp = apr_pstrdup(cmd->pool, to);
 
208
        ap_str_tolower(tmp);
 
209
        to = tmp;
 
210
    }
 
211
 
 
212
    push_item(((autoindex_config_rec *) d)->alt_list, cmd->info, to,
 
213
              cmd->path, alt);
 
214
    return NULL;
 
215
}
 
216
 
 
217
static const char *add_icon(cmd_parms *cmd, void *d, const char *icon,
 
218
                            const char *to)
 
219
{
 
220
    char *iconbak = apr_pstrdup(cmd->pool, icon);
 
221
 
 
222
    if (icon[0] == '(') {
 
223
        char *alt;
 
224
        char *cl = strchr(iconbak, ')');
 
225
 
 
226
        if (cl == NULL) {
 
227
            return "missing closing paren";
 
228
        }
 
229
        alt = ap_getword_nc(cmd->pool, &iconbak, ',');
 
230
        *cl = '\0';                             /* Lose closing paren */
 
231
        add_alt(cmd, d, &alt[1], to);
 
232
    }
 
233
    if (cmd->info == BY_PATH) {
 
234
        if (!strcmp(to, "**DIRECTORY**")) {
 
235
            to = "^^DIRECTORY^^";
 
236
        }
 
237
    }
 
238
    if (cmd->info == BY_ENCODING) {
 
239
        char *tmp = apr_pstrdup(cmd->pool, to);
 
240
        ap_str_tolower(tmp);
 
241
        to = tmp;
 
242
    }
 
243
 
 
244
    push_item(((autoindex_config_rec *) d)->icon_list, cmd->info, to,
 
245
              cmd->path, iconbak);
 
246
    return NULL;
 
247
}
 
248
 
 
249
/*
 
250
 * Add description text for a filename pattern.  If the pattern has
 
251
 * wildcards already (or we need to add them), add leading and
 
252
 * trailing wildcards to it to ensure substring processing.  If the
 
253
 * pattern contains a '/' anywhere, force wildcard matching mode,
 
254
 * add a slash to the prefix so that "bar/bletch" won't be matched
 
255
 * by "foobar/bletch", and make a note that there's a delimiter;
 
256
 * the matching routine simplifies to just the actual filename
 
257
 * whenever it can.  This allows definitions in parent directories
 
258
 * to be made for files in subordinate ones using relative paths.
 
259
 */
 
260
 
 
261
/*
 
262
 * Absent a strcasestr() function, we have to force wildcards on
 
263
 * systems for which "AAA" and "aaa" mean the same file.
 
264
 */
 
265
#ifdef CASE_BLIND_FILESYSTEM
 
266
#define WILDCARDS_REQUIRED 1
 
267
#else
 
268
#define WILDCARDS_REQUIRED 0
 
269
#endif
 
270
 
 
271
static const char *add_desc(cmd_parms *cmd, void *d, const char *desc,
 
272
                            const char *to)
 
273
{
 
274
    autoindex_config_rec *dcfg = (autoindex_config_rec *) d;
 
275
    ai_desc_t *desc_entry;
 
276
    char *prefix = "";
 
277
 
 
278
    desc_entry = (ai_desc_t *) apr_array_push(dcfg->desc_list);
 
279
    desc_entry->full_path = (ap_strchr_c(to, '/') == NULL) ? 0 : 1;
 
280
    desc_entry->wildcards = (WILDCARDS_REQUIRED
 
281
                             || desc_entry->full_path
 
282
                             || apr_fnmatch_test(to));
 
283
    if (desc_entry->wildcards) {
 
284
        prefix = desc_entry->full_path ? "*/" : "*";
 
285
        desc_entry->pattern = apr_pstrcat(dcfg->desc_list->pool,
 
286
                                          prefix, to, "*", NULL);
 
287
    }
 
288
    else {
 
289
        desc_entry->pattern = apr_pstrdup(dcfg->desc_list->pool, to);
 
290
    }
 
291
    desc_entry->description = apr_pstrdup(dcfg->desc_list->pool, desc);
 
292
    return NULL;
 
293
}
 
294
 
 
295
static const char *add_ignore(cmd_parms *cmd, void *d, const char *ext)
 
296
{
 
297
    push_item(((autoindex_config_rec *) d)->ign_list, 0, ext, cmd->path, NULL);
 
298
    return NULL;
 
299
}
 
300
 
 
301
static const char *add_header(cmd_parms *cmd, void *d, const char *name)
 
302
{
 
303
    push_item(((autoindex_config_rec *) d)->hdr_list, 0, NULL, cmd->path,
 
304
              name);
 
305
    return NULL;
 
306
}
 
307
 
 
308
static const char *add_readme(cmd_parms *cmd, void *d, const char *name)
 
309
{
 
310
    push_item(((autoindex_config_rec *) d)->rdme_list, 0, NULL, cmd->path,
 
311
              name);
 
312
    return NULL;
 
313
}
 
314
 
 
315
static const char *add_opts(cmd_parms *cmd, void *d, int argc, char *const argv[])
 
316
{
 
317
    int i;
 
318
    char *w;
 
319
    apr_int32_t opts;
 
320
    apr_int32_t opts_add;
 
321
    apr_int32_t opts_remove;
 
322
    char action;
 
323
    autoindex_config_rec *d_cfg = (autoindex_config_rec *) d;
 
324
 
 
325
    opts = d_cfg->opts;
 
326
    opts_add = d_cfg->incremented_opts;
 
327
    opts_remove = d_cfg->decremented_opts;
 
328
 
 
329
    for (i = 0; i < argc; i++) {
 
330
        int option = 0;
 
331
        w = argv[i];
 
332
 
 
333
        if ((*w == '+') || (*w == '-')) {
 
334
            action = *(w++);
 
335
        }
 
336
        else {
 
337
            action = '\0';
 
338
        }
 
339
        if (!strcasecmp(w, "FancyIndexing")) {
 
340
            option = FANCY_INDEXING;
 
341
        }
 
342
        else if (!strcasecmp(w, "FoldersFirst")) {
 
343
            option = FOLDERS_FIRST;
 
344
        }
 
345
        else if (!strcasecmp(w, "HTMLTable")) {
 
346
            option = TABLE_INDEXING;
 
347
        }
 
348
        else if (!strcasecmp(w, "IconsAreLinks")) {
 
349
            option = ICONS_ARE_LINKS;
 
350
        }
 
351
        else if (!strcasecmp(w, "IgnoreCase")) {
 
352
            option = IGNORE_CASE;
 
353
        }
 
354
        else if (!strcasecmp(w, "IgnoreClient")) {
 
355
            option = IGNORE_CLIENT;
 
356
        }
 
357
        else if (!strcasecmp(w, "ScanHTMLTitles")) {
 
358
            option = SCAN_HTML_TITLES;
 
359
        }
 
360
        else if (!strcasecmp(w, "SuppressColumnSorting")) {
 
361
            option = SUPPRESS_COLSORT;
 
362
        }
 
363
        else if (!strcasecmp(w, "SuppressDescription")) {
 
364
            option = SUPPRESS_DESC;
 
365
        }
 
366
        else if (!strcasecmp(w, "SuppressHTMLPreamble")) {
 
367
            option = SUPPRESS_PREAMBLE;
 
368
        }
 
369
        else if (!strcasecmp(w, "SuppressIcon")) {
 
370
            option = SUPPRESS_ICON;
 
371
        }
 
372
        else if (!strcasecmp(w, "SuppressLastModified")) {
 
373
            option = SUPPRESS_LAST_MOD;
 
374
        }
 
375
        else if (!strcasecmp(w, "SuppressSize")) {
 
376
            option = SUPPRESS_SIZE;
 
377
        }
 
378
        else if (!strcasecmp(w, "SuppressRules")) {
 
379
            option = SUPPRESS_RULES;
 
380
        }
 
381
        else if (!strcasecmp(w, "TrackModified")) {
 
382
            option = TRACK_MODIFIED;
 
383
        }
 
384
        else if (!strcasecmp(w, "VersionSort")) {
 
385
            option = VERSION_SORT;
 
386
        }
 
387
        else if (!strcasecmp(w, "XHTML")) {
 
388
            option = EMIT_XHTML;
 
389
        }
 
390
        else if (!strcasecmp(w, "ShowForbidden")) {
 
391
            option = SHOW_FORBIDDEN;
 
392
        }
 
393
        else if (!strcasecmp(w, "None")) {
 
394
            if (action != '\0') {
 
395
                return "Cannot combine '+' or '-' with 'None' keyword";
 
396
            }
 
397
            opts = NO_OPTIONS;
 
398
            opts_add = 0;
 
399
            opts_remove = 0;
 
400
        }
 
401
        else if (!strcasecmp(w, "IconWidth")) {
 
402
            if (action != '-') {
 
403
                d_cfg->icon_width = DEFAULT_ICON_WIDTH;
 
404
            }
 
405
            else {
 
406
                d_cfg->icon_width = 0;
 
407
            }
 
408
        }
 
409
        else if (!strncasecmp(w, "IconWidth=", 10)) {
 
410
            if (action == '-') {
 
411
                return "Cannot combine '-' with IconWidth=n";
 
412
            }
 
413
            d_cfg->icon_width = atoi(&w[10]);
 
414
        }
 
415
        else if (!strcasecmp(w, "IconHeight")) {
 
416
            if (action != '-') {
 
417
                d_cfg->icon_height = DEFAULT_ICON_HEIGHT;
 
418
            }
 
419
            else {
 
420
                d_cfg->icon_height = 0;
 
421
            }
 
422
        }
 
423
        else if (!strncasecmp(w, "IconHeight=", 11)) {
 
424
            if (action == '-') {
 
425
                return "Cannot combine '-' with IconHeight=n";
 
426
            }
 
427
            d_cfg->icon_height = atoi(&w[11]);
 
428
        }
 
429
        else if (!strcasecmp(w, "NameWidth")) {
 
430
            if (action != '-') {
 
431
                return "NameWidth with no value may only appear as "
 
432
                       "'-NameWidth'";
 
433
            }
 
434
            d_cfg->name_width = DEFAULT_NAME_WIDTH;
 
435
            d_cfg->name_adjust = K_NOADJUST;
 
436
        }
 
437
        else if (!strncasecmp(w, "NameWidth=", 10)) {
 
438
            if (action == '-') {
 
439
                return "Cannot combine '-' with NameWidth=n";
 
440
            }
 
441
            if (w[10] == '*') {
 
442
                d_cfg->name_adjust = K_ADJUST;
 
443
            }
 
444
            else {
 
445
                int width = atoi(&w[10]);
 
446
 
 
447
                if (width && (width < 5)) {
 
448
                    return "NameWidth value must be greater than 5";
 
449
                }
 
450
                d_cfg->name_width = width;
 
451
                d_cfg->name_adjust = K_NOADJUST;
 
452
            }
 
453
        }
 
454
        else if (!strcasecmp(w, "DescriptionWidth")) {
 
455
            if (action != '-') {
 
456
                return "DescriptionWidth with no value may only appear as "
 
457
                       "'-DescriptionWidth'";
 
458
            }
 
459
            d_cfg->desc_width = DEFAULT_DESC_WIDTH;
 
460
            d_cfg->desc_adjust = K_NOADJUST;
 
461
        }
 
462
        else if (!strncasecmp(w, "DescriptionWidth=", 17)) {
 
463
            if (action == '-') {
 
464
                return "Cannot combine '-' with DescriptionWidth=n";
 
465
            }
 
466
            if (w[17] == '*') {
 
467
                d_cfg->desc_adjust = K_ADJUST;
 
468
            }
 
469
            else {
 
470
                int width = atoi(&w[17]);
 
471
 
 
472
                if (width && (width < 12)) {
 
473
                    return "DescriptionWidth value must be greater than 12";
 
474
                }
 
475
                d_cfg->desc_width = width;
 
476
                d_cfg->desc_adjust = K_NOADJUST;
 
477
            }
 
478
        }
 
479
        else {
 
480
            return "Invalid directory indexing option";
 
481
        }
 
482
        if (action == '\0') {
 
483
            opts |= option;
 
484
            opts_add = 0;
 
485
            opts_remove = 0;
 
486
        }
 
487
        else if (action == '+') {
 
488
            opts_add |= option;
 
489
            opts_remove &= ~option;
 
490
        }
 
491
        else {
 
492
            opts_remove |= option;
 
493
            opts_add &= ~option;
 
494
        }
 
495
    }
 
496
    if ((opts & NO_OPTIONS) && (opts & ~NO_OPTIONS)) {
 
497
        return "Cannot combine other IndexOptions keywords with 'None'";
 
498
    }
 
499
    d_cfg->incremented_opts = opts_add;
 
500
    d_cfg->decremented_opts = opts_remove;
 
501
    d_cfg->opts = opts;
 
502
    return NULL;
 
503
}
 
504
 
 
505
static const char *set_default_order(cmd_parms *cmd, void *m,
 
506
                                     const char *direction, const char *key)
 
507
{
 
508
    autoindex_config_rec *d_cfg = (autoindex_config_rec *) m;
 
509
 
 
510
    if (!strcasecmp(direction, "Ascending")) {
 
511
        d_cfg->default_direction = D_ASCENDING;
 
512
    }
 
513
    else if (!strcasecmp(direction, "Descending")) {
 
514
        d_cfg->default_direction = D_DESCENDING;
 
515
    }
 
516
    else {
 
517
        return "First keyword must be 'Ascending' or 'Descending'";
 
518
    }
 
519
 
 
520
    if (!strcasecmp(key, "Name")) {
 
521
        d_cfg->default_keyid = K_NAME;
 
522
    }
 
523
    else if (!strcasecmp(key, "Date")) {
 
524
        d_cfg->default_keyid = K_LAST_MOD;
 
525
    }
 
526
    else if (!strcasecmp(key, "Size")) {
 
527
        d_cfg->default_keyid = K_SIZE;
 
528
    }
 
529
    else if (!strcasecmp(key, "Description")) {
 
530
        d_cfg->default_keyid = K_DESC;
 
531
    }
 
532
    else {
 
533
        return "Second keyword must be 'Name', 'Date', 'Size', or "
 
534
               "'Description'";
 
535
    }
 
536
 
 
537
    return NULL;
 
538
}
 
539
 
 
540
#define DIR_CMD_PERMS OR_INDEXES
 
541
 
 
542
static const command_rec autoindex_cmds[] =
 
543
{
 
544
    AP_INIT_ITERATE2("AddIcon", add_icon, BY_PATH, DIR_CMD_PERMS,
 
545
                     "an icon URL followed by one or more filenames"),
 
546
    AP_INIT_ITERATE2("AddIconByType", add_icon, BY_TYPE, DIR_CMD_PERMS,
 
547
                     "an icon URL followed by one or more MIME types"),
 
548
    AP_INIT_ITERATE2("AddIconByEncoding", add_icon, BY_ENCODING, DIR_CMD_PERMS,
 
549
                     "an icon URL followed by one or more content encodings"),
 
550
    AP_INIT_ITERATE2("AddAlt", add_alt, BY_PATH, DIR_CMD_PERMS,
 
551
                     "alternate descriptive text followed by one or more "
 
552
                     "filenames"),
 
553
    AP_INIT_ITERATE2("AddAltByType", add_alt, BY_TYPE, DIR_CMD_PERMS,
 
554
                     "alternate descriptive text followed by one or more MIME "
 
555
                     "types"),
 
556
    AP_INIT_ITERATE2("AddAltByEncoding", add_alt, BY_ENCODING, DIR_CMD_PERMS,
 
557
                     "alternate descriptive text followed by one or more "
 
558
                     "content encodings"),
 
559
    AP_INIT_TAKE_ARGV("IndexOptions", add_opts, NULL, DIR_CMD_PERMS,
 
560
                      "one or more index options [+|-][]"),
 
561
    AP_INIT_TAKE2("IndexOrderDefault", set_default_order, NULL, DIR_CMD_PERMS,
 
562
                  "{Ascending,Descending} {Name,Size,Description,Date}"),
 
563
    AP_INIT_ITERATE("IndexIgnore", add_ignore, NULL, DIR_CMD_PERMS,
 
564
                    "one or more file extensions"),
 
565
    AP_INIT_ITERATE2("AddDescription", add_desc, BY_PATH, DIR_CMD_PERMS,
 
566
                     "Descriptive text followed by one or more filenames"),
 
567
    AP_INIT_TAKE1("HeaderName", add_header, NULL, DIR_CMD_PERMS,
 
568
                  "a filename"),
 
569
    AP_INIT_TAKE1("ReadmeName", add_readme, NULL, DIR_CMD_PERMS,
 
570
                  "a filename"),
 
571
    AP_INIT_RAW_ARGS("FancyIndexing", ap_set_deprecated, NULL, OR_ALL,
 
572
                     "The FancyIndexing directive is no longer supported. "
 
573
                     "Use IndexOptions FancyIndexing."),
 
574
    AP_INIT_TAKE1("DefaultIcon", ap_set_string_slot,
 
575
                  (void *)APR_OFFSETOF(autoindex_config_rec, default_icon),
 
576
                  DIR_CMD_PERMS, "an icon URL"),
 
577
    AP_INIT_TAKE1("IndexStyleSheet", ap_set_string_slot,
 
578
                  (void *)APR_OFFSETOF(autoindex_config_rec, style_sheet),
 
579
                  DIR_CMD_PERMS, "URL to style sheet"),
 
580
    {NULL}
 
581
};
 
582
 
 
583
static void *create_autoindex_config(apr_pool_t *p, char *dummy)
 
584
{
 
585
    autoindex_config_rec *new =
 
586
    (autoindex_config_rec *) apr_pcalloc(p, sizeof(autoindex_config_rec));
 
587
 
 
588
    new->icon_width = 0;
 
589
    new->icon_height = 0;
 
590
    new->name_width = DEFAULT_NAME_WIDTH;
 
591
    new->name_adjust = K_UNSET;
 
592
    new->desc_width = DEFAULT_DESC_WIDTH;
 
593
    new->desc_adjust = K_UNSET;
 
594
    new->icon_list = apr_array_make(p, 4, sizeof(struct item));
 
595
    new->alt_list = apr_array_make(p, 4, sizeof(struct item));
 
596
    new->desc_list = apr_array_make(p, 4, sizeof(ai_desc_t));
 
597
    new->ign_list = apr_array_make(p, 4, sizeof(struct item));
 
598
    new->hdr_list = apr_array_make(p, 4, sizeof(struct item));
 
599
    new->rdme_list = apr_array_make(p, 4, sizeof(struct item));
 
600
    new->opts = 0;
 
601
    new->incremented_opts = 0;
 
602
    new->decremented_opts = 0;
 
603
    new->default_keyid = '\0';
 
604
    new->default_direction = '\0';
 
605
 
 
606
    return (void *) new;
 
607
}
 
608
 
 
609
static void *merge_autoindex_configs(apr_pool_t *p, void *basev, void *addv)
 
610
{
 
611
    autoindex_config_rec *new;
 
612
    autoindex_config_rec *base = (autoindex_config_rec *) basev;
 
613
    autoindex_config_rec *add = (autoindex_config_rec *) addv;
 
614
 
 
615
    new = (autoindex_config_rec *) apr_pcalloc(p, sizeof(autoindex_config_rec));
 
616
    new->default_icon = add->default_icon ? add->default_icon
 
617
                                          : base->default_icon;
 
618
    new->style_sheet = add->style_sheet ? add->style_sheet
 
619
                                          : base->style_sheet;
 
620
    new->icon_height = add->icon_height ? add->icon_height : base->icon_height;
 
621
    new->icon_width = add->icon_width ? add->icon_width : base->icon_width;
 
622
 
 
623
    new->alt_list = apr_array_append(p, add->alt_list, base->alt_list);
 
624
    new->ign_list = apr_array_append(p, add->ign_list, base->ign_list);
 
625
    new->hdr_list = apr_array_append(p, add->hdr_list, base->hdr_list);
 
626
    new->desc_list = apr_array_append(p, add->desc_list, base->desc_list);
 
627
    new->icon_list = apr_array_append(p, add->icon_list, base->icon_list);
 
628
    new->rdme_list = apr_array_append(p, add->rdme_list, base->rdme_list);
 
629
    if (add->opts & NO_OPTIONS) {
 
630
        /*
 
631
         * If the current directory says 'no options' then we also
 
632
         * clear any incremental mods from being inheritable further down.
 
633
         */
 
634
        new->opts = NO_OPTIONS;
 
635
        new->incremented_opts = 0;
 
636
        new->decremented_opts = 0;
 
637
    }
 
638
    else {
 
639
        /*
 
640
         * If there were any nonincremental options selected for
 
641
         * this directory, they dominate and we don't inherit *anything.*
 
642
         * Contrariwise, we *do* inherit if the only settings here are
 
643
         * incremental ones.
 
644
         */
 
645
        if (add->opts == 0) {
 
646
            new->incremented_opts = (base->incremented_opts
 
647
                                     | add->incremented_opts)
 
648
                                    & ~add->decremented_opts;
 
649
            new->decremented_opts = (base->decremented_opts
 
650
                                     | add->decremented_opts);
 
651
            /*
 
652
             * We may have incremental settings, so make sure we don't
 
653
             * inadvertently inherit an IndexOptions None from above.
 
654
             */
 
655
            new->opts = (base->opts & ~NO_OPTIONS);
 
656
        }
 
657
        else {
 
658
            /*
 
659
             * There are local nonincremental settings, which clear
 
660
             * all inheritance from above.  They *are* the new base settings.
 
661
             */
 
662
            new->opts = add->opts;;
 
663
        }
 
664
        /*
 
665
         * We're guaranteed that there'll be no overlap between
 
666
         * the add-options and the remove-options.
 
667
         */
 
668
        new->opts |= new->incremented_opts;
 
669
        new->opts &= ~new->decremented_opts;
 
670
    }
 
671
    /*
 
672
     * Inherit the NameWidth settings if there aren't any specific to
 
673
     * the new location; otherwise we'll end up using the defaults set in the
 
674
     * config-rec creation routine.
 
675
     */
 
676
    if (add->name_adjust == K_UNSET) {
 
677
        new->name_width = base->name_width;
 
678
        new->name_adjust = base->name_adjust;
 
679
    }
 
680
    else {
 
681
        new->name_width = add->name_width;
 
682
        new->name_adjust = add->name_adjust;
 
683
    }
 
684
 
 
685
    /*
 
686
     * Likewise for DescriptionWidth.
 
687
     */
 
688
    if (add->desc_adjust == K_UNSET) {
 
689
        new->desc_width = base->desc_width;
 
690
        new->desc_adjust = base->desc_adjust;
 
691
    }
 
692
    else {
 
693
        new->desc_width = add->desc_width;
 
694
        new->desc_adjust = add->desc_adjust;
 
695
    }
 
696
 
 
697
    new->default_keyid = add->default_keyid ? add->default_keyid
 
698
                                            : base->default_keyid;
 
699
    new->default_direction = add->default_direction ? add->default_direction
 
700
                                                    : base->default_direction;
 
701
    return new;
 
702
}
 
703
 
 
704
/****************************************************************
 
705
 *
 
706
 * Looking things up in config entries...
 
707
 */
 
708
 
 
709
/* Structure used to hold entries when we're actually building an index */
 
710
 
 
711
struct ent {
 
712
    char *name;
 
713
    char *icon;
 
714
    char *alt;
 
715
    char *desc;
 
716
    apr_off_t size;
 
717
    apr_time_t lm;
 
718
    struct ent *next;
 
719
    int ascending, ignore_case, version_sort;
 
720
    char key;
 
721
    int isdir;
 
722
};
 
723
 
 
724
static char *find_item(request_rec *r, apr_array_header_t *list, int path_only)
 
725
{
 
726
    const char *content_type = ap_field_noparam(r->pool, r->content_type);
 
727
    const char *content_encoding = r->content_encoding;
 
728
    char *path = r->filename;
 
729
 
 
730
    struct item *items = (struct item *) list->elts;
 
731
    int i;
 
732
 
 
733
    for (i = 0; i < list->nelts; ++i) {
 
734
        struct item *p = &items[i];
 
735
 
 
736
        /* Special cased for ^^DIRECTORY^^ and ^^BLANKICON^^ */
 
737
        if ((path[0] == '^') || (!ap_strcmp_match(path, p->apply_path))) {
 
738
            if (!*(p->apply_to)) {
 
739
                return p->data;
 
740
            }
 
741
            else if (p->type == BY_PATH || path[0] == '^') {
 
742
                if (!ap_strcmp_match(path, p->apply_to)) {
 
743
                    return p->data;
 
744
                }
 
745
            }
 
746
            else if (!path_only) {
 
747
                if (!content_encoding) {
 
748
                    if (p->type == BY_TYPE) {
 
749
                        if (content_type
 
750
                            && !ap_strcasecmp_match(content_type,
 
751
                                                    p->apply_to)) {
 
752
                            return p->data;
 
753
                        }
 
754
                    }
 
755
                }
 
756
                else {
 
757
                    if (p->type == BY_ENCODING) {
 
758
                        if (!ap_strcasecmp_match(content_encoding,
 
759
                                                 p->apply_to)) {
 
760
                            return p->data;
 
761
                        }
 
762
                    }
 
763
                }
 
764
            }
 
765
        }
 
766
    }
 
767
    return NULL;
 
768
}
 
769
 
 
770
#define find_icon(d,p,t) find_item(p,d->icon_list,t)
 
771
#define find_alt(d,p,t) find_item(p,d->alt_list,t)
 
772
#define find_header(d,p) find_item(p,d->hdr_list,0)
 
773
#define find_readme(d,p) find_item(p,d->rdme_list,0)
 
774
 
 
775
static char *find_default_item(char *bogus_name, apr_array_header_t *list)
 
776
{
 
777
    request_rec r;
 
778
    /* Bleah.  I tried to clean up find_item, and it lead to this bit
 
779
     * of ugliness.   Note that the fields initialized are precisely
 
780
     * those that find_item looks at...
 
781
     */
 
782
    r.filename = bogus_name;
 
783
    r.content_type = r.content_encoding = NULL;
 
784
    return find_item(&r, list, 1);
 
785
}
 
786
 
 
787
#define find_default_icon(d,n) find_default_item(n, d->icon_list)
 
788
#define find_default_alt(d,n) find_default_item(n, d->alt_list)
 
789
 
 
790
/*
 
791
 * Look through the list of pattern/description pairs and return the first one
 
792
 * if any) that matches the filename in the request.  If multiple patterns
 
793
 * match, only the first one is used; since the order in the array is the
 
794
 * same as the order in which directives were processed, earlier matching
 
795
 * directives will dominate.
 
796
 */
 
797
 
 
798
#ifdef CASE_BLIND_FILESYSTEM
 
799
#define MATCH_FLAGS APR_FNM_CASE_BLIND
 
800
#else
 
801
#define MATCH_FLAGS 0
 
802
#endif
 
803
 
 
804
static char *find_desc(autoindex_config_rec *dcfg, const char *filename_full)
 
805
{
 
806
    int i;
 
807
    ai_desc_t *list = (ai_desc_t *) dcfg->desc_list->elts;
 
808
    const char *filename_only;
 
809
    const char *filename;
 
810
 
 
811
    /*
 
812
     * If the filename includes a path, extract just the name itself
 
813
     * for the simple matches.
 
814
     */
 
815
    if ((filename_only = ap_strrchr_c(filename_full, '/')) == NULL) {
 
816
        filename_only = filename_full;
 
817
    }
 
818
    else {
 
819
        filename_only++;
 
820
    }
 
821
    for (i = 0; i < dcfg->desc_list->nelts; ++i) {
 
822
        ai_desc_t *tuple = &list[i];
 
823
        int found;
 
824
 
 
825
        /*
 
826
         * Only use the full-path filename if the pattern contains '/'s.
 
827
         */
 
828
        filename = (tuple->full_path) ? filename_full : filename_only;
 
829
        /*
 
830
         * Make the comparison using the cheapest method; only do
 
831
         * wildcard checking if we must.
 
832
         */
 
833
        if (tuple->wildcards) {
 
834
            found = (apr_fnmatch(tuple->pattern, filename, MATCH_FLAGS) == 0);
 
835
        }
 
836
        else {
 
837
            found = (ap_strstr_c(filename, tuple->pattern) != NULL);
 
838
        }
 
839
        if (found) {
 
840
            return tuple->description;
 
841
        }
 
842
    }
 
843
    return NULL;
 
844
}
 
845
 
 
846
static int ignore_entry(autoindex_config_rec *d, char *path)
 
847
{
 
848
    apr_array_header_t *list = d->ign_list;
 
849
    struct item *items = (struct item *) list->elts;
 
850
    char *tt;
 
851
    int i;
 
852
 
 
853
    if ((tt = strrchr(path, '/')) == NULL) {
 
854
        tt = path;
 
855
    }
 
856
    else {
 
857
        tt++;
 
858
    }
 
859
 
 
860
    for (i = 0; i < list->nelts; ++i) {
 
861
        struct item *p = &items[i];
 
862
        char *ap;
 
863
 
 
864
        if ((ap = strrchr(p->apply_to, '/')) == NULL) {
 
865
            ap = p->apply_to;
 
866
        }
 
867
        else {
 
868
            ap++;
 
869
        }
 
870
 
 
871
#ifndef CASE_BLIND_FILESYSTEM
 
872
        if (!ap_strcmp_match(path, p->apply_path)
 
873
            && !ap_strcmp_match(tt, ap)) {
 
874
            return 1;
 
875
        }
 
876
#else  /* !CASE_BLIND_FILESYSTEM */
 
877
        /*
 
878
         * On some platforms, the match must be case-blind.  This is really
 
879
         * a factor of the filesystem involved, but we can't detect that
 
880
         * reliably - so we have to granularise at the OS level.
 
881
         */
 
882
        if (!ap_strcasecmp_match(path, p->apply_path)
 
883
            && !ap_strcasecmp_match(tt, ap)) {
 
884
            return 1;
 
885
        }
 
886
#endif /* !CASE_BLIND_FILESYSTEM */
 
887
    }
 
888
    return 0;
 
889
}
 
890
 
 
891
/*****************************************************************
 
892
 *
 
893
 * Actually generating output
 
894
 */
 
895
 
 
896
/*
 
897
 * Elements of the emitted document:
 
898
 *      Preamble
 
899
 *              Emitted unless SUPPRESS_PREAMBLE is set AND ap_run_sub_req
 
900
 *              succeeds for the (content_type == text/html) header file.
 
901
 *      Header file
 
902
 *              Emitted if found (and able).
 
903
 *      H1 tag line
 
904
 *              Emitted if a header file is NOT emitted.
 
905
 *      Directory stuff
 
906
 *              Always emitted.
 
907
 *      HR
 
908
 *              Emitted if FANCY_INDEXING is set.
 
909
 *      Readme file
 
910
 *              Emitted if found (and able).
 
911
 *      ServerSig
 
912
 *              Emitted if ServerSignature is not Off AND a readme file
 
913
 *              is NOT emitted.
 
914
 *      Postamble
 
915
 *              Emitted unless SUPPRESS_PREAMBLE is set AND ap_run_sub_req
 
916
 *              succeeds for the (content_type == text/html) readme file.
 
917
 */
 
918
 
 
919
 
 
920
/*
 
921
 * emit a plain text file
 
922
 */
 
923
static void do_emit_plain(request_rec *r, apr_file_t *f)
 
924
{
 
925
    char buf[AP_IOBUFSIZE + 1];
 
926
    int ch;
 
927
    apr_size_t i, c, n;
 
928
    apr_status_t rv;
 
929
 
 
930
    ap_rputs("<pre>\n", r);
 
931
    while (!apr_file_eof(f)) {
 
932
        do {
 
933
            n = sizeof(char) * AP_IOBUFSIZE;
 
934
            rv = apr_file_read(f, buf, &n);
 
935
        } while (APR_STATUS_IS_EINTR(rv));
 
936
        if (n == 0 || rv != APR_SUCCESS) {
 
937
            /* ###: better error here? */
 
938
            break;
 
939
        }
 
940
        buf[n] = '\0';
 
941
        c = 0;
 
942
        while (c < n) {
 
943
            for (i = c; i < n; i++) {
 
944
                if (buf[i] == '<' || buf[i] == '>' || buf[i] == '&') {
 
945
                    break;
 
946
                }
 
947
            }
 
948
            ch = buf[i];
 
949
            buf[i] = '\0';
 
950
            ap_rputs(&buf[c], r);
 
951
            if (ch == '<') {
 
952
                ap_rputs("&lt;", r);
 
953
            }
 
954
            else if (ch == '>') {
 
955
                ap_rputs("&gt;", r);
 
956
            }
 
957
            else if (ch == '&') {
 
958
                ap_rputs("&amp;", r);
 
959
            }
 
960
            c = i + 1;
 
961
        }
 
962
    }
 
963
    ap_rputs("</pre>\n", r);
 
964
}
 
965
 
 
966
/*
 
967
 * Handle the preamble through the H1 tag line, inclusive.  Locate
 
968
 * the file with a subrequests.  Process text/html documents by actually
 
969
 * running the subrequest; text/xxx documents get copied verbatim,
 
970
 * and any other content type is ignored.  This means that a non-text
 
971
 * document (such as HEADER.gif) might get multiviewed as the result
 
972
 * instead of a text document, meaning nothing will be displayed, but
 
973
 * oh well.
 
974
 */
 
975
static void emit_head(request_rec *r, char *header_fname, int suppress_amble,
 
976
                      int emit_xhtml, char *title)
 
977
{
 
978
    apr_table_t *hdrs = r->headers_in;
 
979
    apr_file_t *f = NULL;
 
980
    request_rec *rr = NULL;
 
981
    int emit_amble = 1;
 
982
    int emit_H1 = 1;
 
983
    const char *r_accept;
 
984
    const char *r_accept_enc;
 
985
 
 
986
    /*
 
987
     * If there's a header file, send a subrequest to look for it.  If it's
 
988
     * found and html do the subrequest, otherwise handle it
 
989
     */
 
990
    r_accept = apr_table_get(hdrs, "Accept");
 
991
    r_accept_enc = apr_table_get(hdrs, "Accept-Encoding");
 
992
    apr_table_setn(hdrs, "Accept", "text/html, text/plain");
 
993
    apr_table_unset(hdrs, "Accept-Encoding");
 
994
 
 
995
 
 
996
    if ((header_fname != NULL) && r->args) {
 
997
        header_fname = apr_pstrcat(r->pool, header_fname, "?", r->args, NULL);
 
998
    }
 
999
 
 
1000
    if ((header_fname != NULL)
 
1001
        && (rr = ap_sub_req_lookup_uri(header_fname, r, r->output_filters))
 
1002
        && (rr->status == HTTP_OK)
 
1003
        && (rr->filename != NULL)
 
1004
        && (rr->finfo.filetype == APR_REG)) {
 
1005
        /*
 
1006
         * Check for the two specific cases we allow: text/html and
 
1007
         * text/anything-else.  The former is allowed to be processed for
 
1008
         * SSIs.
 
1009
         */
 
1010
        if (rr->content_type != NULL) {
 
1011
            if (!strcasecmp(ap_field_noparam(r->pool, rr->content_type),
 
1012
                            "text/html")) {
 
1013
                ap_filter_t *f;
 
1014
               /* Hope everything will work... */
 
1015
                emit_amble = 0;
 
1016
                emit_H1 = 0;
 
1017
 
 
1018
                if (! suppress_amble) {
 
1019
                    emit_preamble(r, emit_xhtml, title);
 
1020
                }
 
1021
                /* This is a hack, but I can't find any better way to do this.
 
1022
                 * The problem is that we have already created the sub-request,
 
1023
                 * but we just inserted the OLD_WRITE filter, and the
 
1024
                 * sub-request needs to pass its data through the OLD_WRITE
 
1025
                 * filter, or things go horribly wrong (missing data, data in
 
1026
                 * the wrong order, etc).  To fix it, if you create a
 
1027
                 * sub-request and then insert the OLD_WRITE filter before you
 
1028
                 * run the request, you need to make sure that the sub-request
 
1029
                 * data goes through the OLD_WRITE filter.  Just steal this
 
1030
                 * code.  The long-term solution is to remove the ap_r*
 
1031
                 * functions.
 
1032
                 */
 
1033
                for (f=rr->output_filters;
 
1034
                     f->frec != ap_subreq_core_filter_handle; f = f->next);
 
1035
                f->next = r->output_filters;
 
1036
 
 
1037
                /*
 
1038
                 * If there's a problem running the subrequest, display the
 
1039
                 * preamble if we didn't do it before -- the header file
 
1040
                 * didn't get displayed.
 
1041
                 */
 
1042
                if (ap_run_sub_req(rr) != OK) {
 
1043
                    /* It didn't work */
 
1044
                    emit_amble = suppress_amble;
 
1045
                    emit_H1 = 1;
 
1046
                }
 
1047
            }
 
1048
            else if (!strncasecmp("text/", rr->content_type, 5)) {
 
1049
                /*
 
1050
                 * If we can open the file, prefix it with the preamble
 
1051
                 * regardless; since we'll be sending a <pre> block around
 
1052
                 * the file's contents, any HTML header it had won't end up
 
1053
                 * where it belongs.
 
1054
                 */
 
1055
                if (apr_file_open(&f, rr->filename, APR_READ,
 
1056
                                  APR_OS_DEFAULT, r->pool) == APR_SUCCESS) {
 
1057
                    emit_preamble(r, emit_xhtml, title);
 
1058
                    emit_amble = 0;
 
1059
                    do_emit_plain(r, f);
 
1060
                    apr_file_close(f);
 
1061
                    emit_H1 = 0;
 
1062
                }
 
1063
            }
 
1064
        }
 
1065
    }
 
1066
 
 
1067
    if (r_accept) {
 
1068
        apr_table_setn(hdrs, "Accept", r_accept);
 
1069
    }
 
1070
    else {
 
1071
        apr_table_unset(hdrs, "Accept");
 
1072
    }
 
1073
 
 
1074
    if (r_accept_enc) {
 
1075
        apr_table_setn(hdrs, "Accept-Encoding", r_accept_enc);
 
1076
    }
 
1077
 
 
1078
    if (emit_amble) {
 
1079
        emit_preamble(r, emit_xhtml, title);
 
1080
    }
 
1081
    if (emit_H1) {
 
1082
        ap_rvputs(r, "<h1>Index of ", title, "</h1>\n", NULL);
 
1083
    }
 
1084
    if (rr != NULL) {
 
1085
        ap_destroy_sub_req(rr);
 
1086
    }
 
1087
}
 
1088
 
 
1089
 
 
1090
/*
 
1091
 * Handle the Readme file through the postamble, inclusive.  Locate
 
1092
 * the file with a subrequests.  Process text/html documents by actually
 
1093
 * running the subrequest; text/xxx documents get copied verbatim,
 
1094
 * and any other content type is ignored.  This means that a non-text
 
1095
 * document (such as FOOTER.gif) might get multiviewed as the result
 
1096
 * instead of a text document, meaning nothing will be displayed, but
 
1097
 * oh well.
 
1098
 */
 
1099
static void emit_tail(request_rec *r, char *readme_fname, int suppress_amble)
 
1100
{
 
1101
    apr_file_t *f = NULL;
 
1102
    request_rec *rr = NULL;
 
1103
    int suppress_post = 0;
 
1104
    int suppress_sig = 0;
 
1105
 
 
1106
    /*
 
1107
     * If there's a readme file, send a subrequest to look for it.  If it's
 
1108
     * found and a text file, handle it -- otherwise fall through and
 
1109
     * pretend there's nothing there.
 
1110
     */
 
1111
    if ((readme_fname != NULL)
 
1112
        && (rr = ap_sub_req_lookup_uri(readme_fname, r, r->output_filters))
 
1113
        && (rr->status == HTTP_OK)
 
1114
        && (rr->filename != NULL)
 
1115
        && rr->finfo.filetype == APR_REG) {
 
1116
        /*
 
1117
         * Check for the two specific cases we allow: text/html and
 
1118
         * text/anything-else.  The former is allowed to be processed for
 
1119
         * SSIs.
 
1120
         */
 
1121
        if (rr->content_type != NULL) {
 
1122
            if (!strcasecmp(ap_field_noparam(r->pool, rr->content_type),
 
1123
                            "text/html")) {
 
1124
                ap_filter_t *f;
 
1125
                for (f=rr->output_filters;
 
1126
                     f->frec != ap_subreq_core_filter_handle; f = f->next);
 
1127
                f->next = r->output_filters;
 
1128
 
 
1129
 
 
1130
                if (ap_run_sub_req(rr) == OK) {
 
1131
                    /* worked... */
 
1132
                    suppress_sig = 1;
 
1133
                    suppress_post = suppress_amble;
 
1134
                }
 
1135
            }
 
1136
            else if (!strncasecmp("text/", rr->content_type, 5)) {
 
1137
                /*
 
1138
                 * If we can open the file, suppress the signature.
 
1139
                 */
 
1140
                if (apr_file_open(&f, rr->filename, APR_READ,
 
1141
                                  APR_OS_DEFAULT, r->pool) == APR_SUCCESS) {
 
1142
                    do_emit_plain(r, f);
 
1143
                    apr_file_close(f);
 
1144
                    suppress_sig = 1;
 
1145
                }
 
1146
            }
 
1147
        }
 
1148
    }
 
1149
 
 
1150
    if (!suppress_sig) {
 
1151
        ap_rputs(ap_psignature("", r), r);
 
1152
    }
 
1153
    if (!suppress_post) {
 
1154
        ap_rputs("</body></html>\n", r);
 
1155
    }
 
1156
    if (rr != NULL) {
 
1157
        ap_destroy_sub_req(rr);
 
1158
    }
 
1159
}
 
1160
 
 
1161
 
 
1162
static char *find_title(request_rec *r)
 
1163
{
 
1164
    char titlebuf[MAX_STRING_LEN], *find = "<title>";
 
1165
    apr_file_t *thefile = NULL;
 
1166
    int x, y, p;
 
1167
    apr_size_t n;
 
1168
 
 
1169
    if (r->status != HTTP_OK) {
 
1170
        return NULL;
 
1171
    }
 
1172
    if ((r->content_type != NULL)
 
1173
        && (!strcasecmp(ap_field_noparam(r->pool, r->content_type),
 
1174
                        "text/html")
 
1175
            || !strcmp(r->content_type, INCLUDES_MAGIC_TYPE))
 
1176
        && !r->content_encoding) {
 
1177
        if (apr_file_open(&thefile, r->filename, APR_READ,
 
1178
                          APR_OS_DEFAULT, r->pool) != APR_SUCCESS) {
 
1179
            return NULL;
 
1180
        }
 
1181
        n = sizeof(char) * (MAX_STRING_LEN - 1);
 
1182
        apr_file_read(thefile, titlebuf, &n);
 
1183
        if (n <= 0) {
 
1184
            apr_file_close(thefile);
 
1185
            return NULL;
 
1186
        }
 
1187
        titlebuf[n] = '\0';
 
1188
        for (x = 0, p = 0; titlebuf[x]; x++) {
 
1189
            if (apr_tolower(titlebuf[x]) == find[p]) {
 
1190
                if (!find[++p]) {
 
1191
                    if ((p = ap_ind(&titlebuf[++x], '<')) != -1) {
 
1192
                        titlebuf[x + p] = '\0';
 
1193
                    }
 
1194
                    /* Scan for line breaks for Tanmoy's secretary */
 
1195
                    for (y = x; titlebuf[y]; y++) {
 
1196
                        if ((titlebuf[y] == CR) || (titlebuf[y] == LF)) {
 
1197
                            if (y == x) {
 
1198
                                x++;
 
1199
                            }
 
1200
                            else {
 
1201
                                titlebuf[y] = ' ';
 
1202
                            }
 
1203
                        }
 
1204
                    }
 
1205
                    apr_file_close(thefile);
 
1206
                    return apr_pstrdup(r->pool, &titlebuf[x]);
 
1207
                }
 
1208
            }
 
1209
            else {
 
1210
                p = 0;
 
1211
            }
 
1212
        }
 
1213
        apr_file_close(thefile);
 
1214
    }
 
1215
    return NULL;
 
1216
}
 
1217
 
 
1218
static struct ent *make_parent_entry(apr_int32_t autoindex_opts,
 
1219
                                     autoindex_config_rec *d,
 
1220
                                     request_rec *r, char keyid,
 
1221
                                     char direction)
 
1222
{
 
1223
    struct ent *p = (struct ent *) apr_pcalloc(r->pool, sizeof(struct ent));
 
1224
    char *testpath;
 
1225
    /*
 
1226
     * p->name is now the true parent URI.
 
1227
     * testpath is a crafted lie, so that the syntax '/some/..'
 
1228
     * (or simply '..')be used to describe 'up' from '/some/'
 
1229
     * when processeing IndexIgnore, and Icon|Alt|Desc configs.
 
1230
     */
 
1231
 
 
1232
    /* The output has always been to the parent.  Don't make ourself
 
1233
     * our own parent (worthless cyclical reference).
 
1234
     */
 
1235
    if (!(p->name = ap_make_full_path(r->pool, r->uri, "../"))) {
 
1236
        return (NULL);
 
1237
    }
 
1238
    ap_getparents(p->name);
 
1239
    if (!*p->name) {
 
1240
        return (NULL);
 
1241
    }
 
1242
 
 
1243
    /* IndexIgnore has always compared "/thispath/.." */
 
1244
    testpath = ap_make_full_path(r->pool, r->filename, "..");
 
1245
    if (ignore_entry(d, testpath)) {
 
1246
        return (NULL);
 
1247
    }
 
1248
 
 
1249
    p->size = -1;
 
1250
    p->lm = -1;
 
1251
    p->key = apr_toupper(keyid);
 
1252
    p->ascending = (apr_toupper(direction) == D_ASCENDING);
 
1253
    p->version_sort = autoindex_opts & VERSION_SORT;
 
1254
    if (autoindex_opts & FANCY_INDEXING) {
 
1255
        if (!(p->icon = find_default_icon(d, testpath))) {
 
1256
            p->icon = find_default_icon(d, "^^DIRECTORY^^");
 
1257
        }
 
1258
        if (!(p->alt = find_default_alt(d, testpath))) {
 
1259
            if (!(p->alt = find_default_alt(d, "^^DIRECTORY^^"))) {
 
1260
                p->alt = "DIR";
 
1261
            }
 
1262
        }
 
1263
        p->desc = find_desc(d, testpath);
 
1264
    }
 
1265
    return p;
 
1266
}
 
1267
 
 
1268
static struct ent *make_autoindex_entry(const apr_finfo_t *dirent,
 
1269
                                        int autoindex_opts,
 
1270
                                        autoindex_config_rec *d,
 
1271
                                        request_rec *r, char keyid,
 
1272
                                        char direction,
 
1273
                                        const char *pattern)
 
1274
{
 
1275
    request_rec *rr;
 
1276
    struct ent *p;
 
1277
    int show_forbidden = 0;
 
1278
 
 
1279
    /* Dot is ignored, Parent is handled by make_parent_entry() */
 
1280
    if ((dirent->name[0] == '.') && (!dirent->name[1]
 
1281
        || ((dirent->name[1] == '.') && !dirent->name[2])))
 
1282
        return (NULL);
 
1283
 
 
1284
    /*
 
1285
     * On some platforms, the match must be case-blind.  This is really
 
1286
     * a factor of the filesystem involved, but we can't detect that
 
1287
     * reliably - so we have to granularise at the OS level.
 
1288
     */
 
1289
    if (pattern && (apr_fnmatch(pattern, dirent->name,
 
1290
                                APR_FNM_NOESCAPE | APR_FNM_PERIOD
 
1291
#ifdef CASE_BLIND_FILESYSTEM
 
1292
                                | APR_FNM_CASE_BLIND
 
1293
#endif
 
1294
                                )
 
1295
                    != APR_SUCCESS)) {
 
1296
        return (NULL);
 
1297
    }
 
1298
 
 
1299
    if (ignore_entry(d, ap_make_full_path(r->pool,
 
1300
                                          r->filename, dirent->name))) {
 
1301
        return (NULL);
 
1302
    }
 
1303
 
 
1304
    if (!(rr = ap_sub_req_lookup_dirent(dirent, r, AP_SUBREQ_NO_ARGS, NULL))) {
 
1305
        return (NULL);
 
1306
    }
 
1307
 
 
1308
    if((autoindex_opts & SHOW_FORBIDDEN)
 
1309
        && (rr->status == HTTP_UNAUTHORIZED || rr->status == HTTP_FORBIDDEN)) {
 
1310
        show_forbidden = 1;
 
1311
    }
 
1312
 
 
1313
    if ((rr->finfo.filetype != APR_DIR && rr->finfo.filetype != APR_REG)
 
1314
        || !(rr->status == OK || ap_is_HTTP_SUCCESS(rr->status)
 
1315
                              || ap_is_HTTP_REDIRECT(rr->status)
 
1316
                              || show_forbidden == 1)) {
 
1317
        ap_destroy_sub_req(rr);
 
1318
        return (NULL);
 
1319
    }
 
1320
 
 
1321
    p = (struct ent *) apr_pcalloc(r->pool, sizeof(struct ent));
 
1322
    if (dirent->filetype == APR_DIR) {
 
1323
        p->name = apr_pstrcat(r->pool, dirent->name, "/", NULL);
 
1324
    }
 
1325
    else {
 
1326
        p->name = apr_pstrdup(r->pool, dirent->name);
 
1327
    }
 
1328
    p->size = -1;
 
1329
    p->icon = NULL;
 
1330
    p->alt = NULL;
 
1331
    p->desc = NULL;
 
1332
    p->lm = -1;
 
1333
    p->isdir = 0;
 
1334
    p->key = apr_toupper(keyid);
 
1335
    p->ascending = (apr_toupper(direction) == D_ASCENDING);
 
1336
    p->version_sort = !!(autoindex_opts & VERSION_SORT);
 
1337
    p->ignore_case = !!(autoindex_opts & IGNORE_CASE);
 
1338
 
 
1339
    if (autoindex_opts & (FANCY_INDEXING | TABLE_INDEXING)) {
 
1340
        p->lm = rr->finfo.mtime;
 
1341
        if (dirent->filetype == APR_DIR) {
 
1342
            if (autoindex_opts & FOLDERS_FIRST) {
 
1343
                p->isdir = 1;
 
1344
            }
 
1345
            rr->filename = ap_make_dirstr_parent (rr->pool, rr->filename);
 
1346
 
 
1347
            /* omit the trailing slash (1.3 compat) */
 
1348
            rr->filename[strlen(rr->filename) - 1] = '\0';
 
1349
 
 
1350
            if (!(p->icon = find_icon(d, rr, 1))) {
 
1351
                p->icon = find_default_icon(d, "^^DIRECTORY^^");
 
1352
            }
 
1353
            if (!(p->alt = find_alt(d, rr, 1))) {
 
1354
                if (!(p->alt = find_default_alt(d, "^^DIRECTORY^^"))) {
 
1355
                    p->alt = "DIR";
 
1356
                }
 
1357
            }
 
1358
        }
 
1359
        else {
 
1360
            p->icon = find_icon(d, rr, 0);
 
1361
            p->alt = find_alt(d, rr, 0);
 
1362
            p->size = rr->finfo.size;
 
1363
        }
 
1364
 
 
1365
        p->desc = find_desc(d, rr->filename);
 
1366
 
 
1367
        if ((!p->desc) && (autoindex_opts & SCAN_HTML_TITLES)) {
 
1368
            p->desc = apr_pstrdup(r->pool, find_title(rr));
 
1369
        }
 
1370
    }
 
1371
    ap_destroy_sub_req(rr);
 
1372
    /*
 
1373
     * We don't need to take any special action for the file size key.
 
1374
     * If we did, it would go here.
 
1375
     */
 
1376
    if (keyid == K_LAST_MOD) {
 
1377
        if (p->lm < 0) {
 
1378
            p->lm = 0;
 
1379
        }
 
1380
    }
 
1381
    return (p);
 
1382
}
 
1383
 
 
1384
static char *terminate_description(autoindex_config_rec *d, char *desc,
 
1385
                                   apr_int32_t autoindex_opts, int desc_width)
 
1386
{
 
1387
    int maxsize = desc_width;
 
1388
    register int x;
 
1389
 
 
1390
    /*
 
1391
     * If there's no DescriptionWidth in effect, default to the old
 
1392
     * behaviour of adjusting the description size depending upon
 
1393
     * what else is being displayed.  Otherwise, stick with the
 
1394
     * setting.
 
1395
     */
 
1396
    if (d->desc_adjust == K_UNSET) {
 
1397
        if (autoindex_opts & SUPPRESS_ICON) {
 
1398
            maxsize += 6;
 
1399
        }
 
1400
        if (autoindex_opts & SUPPRESS_LAST_MOD) {
 
1401
            maxsize += 19;
 
1402
        }
 
1403
        if (autoindex_opts & SUPPRESS_SIZE) {
 
1404
            maxsize += 7;
 
1405
        }
 
1406
    }
 
1407
    for (x = 0; desc[x] && ((maxsize > 0) || (desc[x] == '<')); x++) {
 
1408
        if (desc[x] == '<') {
 
1409
            while (desc[x] != '>') {
 
1410
                if (!desc[x]) {
 
1411
                    maxsize = 0;
 
1412
                    break;
 
1413
                }
 
1414
                ++x;
 
1415
            }
 
1416
        }
 
1417
        else if (desc[x] == '&') {
 
1418
            /* entities like &auml; count as one character */
 
1419
            --maxsize;
 
1420
            for ( ; desc[x] != ';'; ++x) {
 
1421
                if (desc[x] == '\0') {
 
1422
                     maxsize = 0;
 
1423
                     break;
 
1424
                }
 
1425
            }
 
1426
        }
 
1427
        else {
 
1428
            --maxsize;
 
1429
        }
 
1430
    }
 
1431
    if (!maxsize && desc[x] != '\0') {
 
1432
        desc[x - 1] = '>';      /* Grump. */
 
1433
        desc[x] = '\0';         /* Double Grump! */
 
1434
    }
 
1435
    return desc;
 
1436
}
 
1437
 
 
1438
/*
 
1439
 * Emit the anchor for the specified field.  If a field is the key for the
 
1440
 * current request, the link changes its meaning to reverse the order when
 
1441
 * selected again.  Non-active fields always start in ascending order.
 
1442
 */
 
1443
static void emit_link(request_rec *r, const char *anchor, char column,
 
1444
                      char curkey, char curdirection,
 
1445
                      const char *colargs, int nosort)
 
1446
{
 
1447
    if (!nosort) {
 
1448
        char qvalue[9];
 
1449
 
 
1450
        qvalue[0] = '?';
 
1451
        qvalue[1] = 'C';
 
1452
        qvalue[2] = '=';
 
1453
        qvalue[3] = column;
 
1454
        qvalue[4] = ';';
 
1455
        qvalue[5] = 'O';
 
1456
        qvalue[6] = '=';
 
1457
                    /* reverse? */
 
1458
        qvalue[7] = ((curkey == column) && (curdirection == D_ASCENDING))
 
1459
                      ? D_DESCENDING : D_ASCENDING;
 
1460
        qvalue[8] = '\0';
 
1461
        ap_rvputs(r, "<a href=\"", qvalue, colargs ? colargs : "",
 
1462
                     "\">", anchor, "</a>", NULL);
 
1463
    }
 
1464
    else {
 
1465
        ap_rputs(anchor, r);
 
1466
    }
 
1467
}
 
1468
 
 
1469
static void output_directories(struct ent **ar, int n,
 
1470
                               autoindex_config_rec *d, request_rec *r,
 
1471
                               apr_int32_t autoindex_opts, char keyid,
 
1472
                               char direction, const char *colargs)
 
1473
{
 
1474
    int x;
 
1475
    apr_size_t rv;
 
1476
    char *name = r->uri;
 
1477
    char *tp;
 
1478
    int static_columns = !!(autoindex_opts & SUPPRESS_COLSORT);
 
1479
    apr_pool_t *scratch;
 
1480
    int name_width;
 
1481
    int desc_width;
 
1482
    char *name_scratch;
 
1483
    char *pad_scratch;
 
1484
    char *breakrow = "";
 
1485
 
 
1486
    apr_pool_create(&scratch, r->pool);
 
1487
    if (name[0] == '\0') {
 
1488
        name = "/";
 
1489
    }
 
1490
 
 
1491
    name_width = d->name_width;
 
1492
    desc_width = d->desc_width;
 
1493
 
 
1494
    if ((autoindex_opts & (FANCY_INDEXING | TABLE_INDEXING))
 
1495
                        == FANCY_INDEXING) {
 
1496
        if (d->name_adjust == K_ADJUST) {
 
1497
            for (x = 0; x < n; x++) {
 
1498
                int t = strlen(ar[x]->name);
 
1499
                if (t > name_width) {
 
1500
                    name_width = t;
 
1501
                }
 
1502
            }
 
1503
        }
 
1504
 
 
1505
        if (d->desc_adjust == K_ADJUST) {
 
1506
            for (x = 0; x < n; x++) {
 
1507
                if (ar[x]->desc != NULL) {
 
1508
                    int t = strlen(ar[x]->desc);
 
1509
                    if (t > desc_width) {
 
1510
                        desc_width = t;
 
1511
                    }
 
1512
                }
 
1513
            }
 
1514
        }
 
1515
    }
 
1516
    name_scratch = apr_palloc(r->pool, name_width + 1);
 
1517
    pad_scratch = apr_palloc(r->pool, name_width + 1);
 
1518
    memset(pad_scratch, ' ', name_width);
 
1519
    pad_scratch[name_width] = '\0';
 
1520
 
 
1521
    if (autoindex_opts & TABLE_INDEXING) {
 
1522
        int cols = 1;
 
1523
        ap_rputs("<table><tr>", r);
 
1524
        if (!(autoindex_opts & SUPPRESS_ICON)) {
 
1525
            ap_rputs("<th>", r);
 
1526
            if ((tp = find_default_icon(d, "^^BLANKICON^^"))) {
 
1527
                ap_rvputs(r, "<img src=\"", ap_escape_html(scratch, tp),
 
1528
                             "\" alt=\"[ICO]\"", NULL);
 
1529
                if (d->icon_width) {
 
1530
                    ap_rprintf(r, " width=\"%d\"", d->icon_width);
 
1531
                }
 
1532
                if (d->icon_height) {
 
1533
                    ap_rprintf(r, " height=\"%d\"", d->icon_height);
 
1534
                }
 
1535
 
 
1536
                if (autoindex_opts & EMIT_XHTML) {
 
1537
                    ap_rputs(" /", r);
 
1538
                }
 
1539
                ap_rputs("></th>", r);
 
1540
            }
 
1541
            else {
 
1542
                ap_rputs("&nbsp;</th>", r);
 
1543
            }
 
1544
 
 
1545
            ++cols;
 
1546
        }
 
1547
        ap_rputs("<th>", r);
 
1548
        emit_link(r, "Name", K_NAME, keyid, direction,
 
1549
                  colargs, static_columns);
 
1550
        if (!(autoindex_opts & SUPPRESS_LAST_MOD)) {
 
1551
            ap_rputs("</th><th>", r);
 
1552
            emit_link(r, "Last modified", K_LAST_MOD, keyid, direction,
 
1553
                      colargs, static_columns);
 
1554
            ++cols;
 
1555
        }
 
1556
        if (!(autoindex_opts & SUPPRESS_SIZE)) {
 
1557
            ap_rputs("</th><th>", r);
 
1558
            emit_link(r, "Size", K_SIZE, keyid, direction,
 
1559
                      colargs, static_columns);
 
1560
            ++cols;
 
1561
        }
 
1562
        if (!(autoindex_opts & SUPPRESS_DESC)) {
 
1563
            ap_rputs("</th><th>", r);
 
1564
            emit_link(r, "Description", K_DESC, keyid, direction,
 
1565
                      colargs, static_columns);
 
1566
            ++cols;
 
1567
        }
 
1568
        if (!(autoindex_opts & SUPPRESS_RULES)) {
 
1569
            breakrow = apr_psprintf(r->pool,
 
1570
                                    "<tr><th colspan=\"%d\">"
 
1571
                                    "<hr%s></th></tr>\n", cols,
 
1572
                                    (autoindex_opts & EMIT_XHTML) ? " /" : "");
 
1573
        }
 
1574
        ap_rvputs(r, "</th></tr>", breakrow, NULL);
 
1575
    }
 
1576
    else if (autoindex_opts & FANCY_INDEXING) {
 
1577
        ap_rputs("<pre>", r);
 
1578
        if (!(autoindex_opts & SUPPRESS_ICON)) {
 
1579
            if ((tp = find_default_icon(d, "^^BLANKICON^^"))) {
 
1580
                ap_rvputs(r, "<img src=\"", ap_escape_html(scratch, tp),
 
1581
                             "\" alt=\"Icon \"", NULL);
 
1582
                if (d->icon_width) {
 
1583
                    ap_rprintf(r, " width=\"%d\"", d->icon_width);
 
1584
                }
 
1585
                if (d->icon_height) {
 
1586
                    ap_rprintf(r, " height=\"%d\"", d->icon_height);
 
1587
                }
 
1588
 
 
1589
                if (autoindex_opts & EMIT_XHTML) {
 
1590
                    ap_rputs(" /", r);
 
1591
                }
 
1592
                ap_rputs("> ", r);
 
1593
            }
 
1594
            else {
 
1595
                ap_rputs("      ", r);
 
1596
            }
 
1597
        }
 
1598
        emit_link(r, "Name", K_NAME, keyid, direction,
 
1599
                  colargs, static_columns);
 
1600
        ap_rputs(pad_scratch + 4, r);
 
1601
        /*
 
1602
         * Emit the guaranteed-at-least-one-space-between-columns byte.
 
1603
         */
 
1604
        ap_rputs(" ", r);
 
1605
        if (!(autoindex_opts & SUPPRESS_LAST_MOD)) {
 
1606
            emit_link(r, "Last modified", K_LAST_MOD, keyid, direction,
 
1607
                      colargs, static_columns);
 
1608
            ap_rputs("      ", r);
 
1609
        }
 
1610
        if (!(autoindex_opts & SUPPRESS_SIZE)) {
 
1611
            emit_link(r, "Size", K_SIZE, keyid, direction,
 
1612
                      colargs, static_columns);
 
1613
            ap_rputs("  ", r);
 
1614
        }
 
1615
        if (!(autoindex_opts & SUPPRESS_DESC)) {
 
1616
            emit_link(r, "Description", K_DESC, keyid, direction,
 
1617
                      colargs, static_columns);
 
1618
        }
 
1619
        if (!(autoindex_opts & SUPPRESS_RULES)) {
 
1620
            ap_rputs("<hr", r);
 
1621
            if (autoindex_opts & EMIT_XHTML) {
 
1622
                ap_rputs(" /", r);
 
1623
            }
 
1624
            ap_rputs(">", r);
 
1625
        }
 
1626
        else {
 
1627
            ap_rputc('\n', r);
 
1628
        }
 
1629
    }
 
1630
    else {
 
1631
        ap_rputs("<ul>", r);
 
1632
    }
 
1633
 
 
1634
    for (x = 0; x < n; x++) {
 
1635
        char *anchor, *t, *t2;
 
1636
        int nwidth;
 
1637
 
 
1638
        apr_pool_clear(scratch);
 
1639
 
 
1640
        t = ar[x]->name;
 
1641
        anchor = ap_escape_html(scratch, ap_os_escape_path(scratch, t, 0));
 
1642
 
 
1643
        if (!x && t[0] == '/') {
 
1644
            t2 = "Parent Directory";
 
1645
        }
 
1646
        else {
 
1647
            t2 = t;
 
1648
        }
 
1649
 
 
1650
        if (autoindex_opts & TABLE_INDEXING) {
 
1651
            ap_rputs("<tr>", r);
 
1652
            if (!(autoindex_opts & SUPPRESS_ICON)) {
 
1653
                ap_rputs("<td valign=\"top\">", r);
 
1654
                if (autoindex_opts & ICONS_ARE_LINKS) {
 
1655
                    ap_rvputs(r, "<a href=\"", anchor, "\">", NULL);
 
1656
                }
 
1657
                if ((ar[x]->icon) || d->default_icon) {
 
1658
                    ap_rvputs(r, "<img src=\"",
 
1659
                              ap_escape_html(scratch,
 
1660
                                             ar[x]->icon ? ar[x]->icon
 
1661
                                                         : d->default_icon),
 
1662
                              "\" alt=\"[", (ar[x]->alt ? ar[x]->alt : "   "),
 
1663
                              "]\"", NULL);
 
1664
                    if (d->icon_width) {
 
1665
                        ap_rprintf(r, " width=\"%d\"", d->icon_width);
 
1666
                    }
 
1667
                    if (d->icon_height) {
 
1668
                        ap_rprintf(r, " height=\"%d\"", d->icon_height);
 
1669
                    }
 
1670
 
 
1671
                    if (autoindex_opts & EMIT_XHTML) {
 
1672
                        ap_rputs(" /", r);
 
1673
                    }
 
1674
                    ap_rputs(">", r);
 
1675
                }
 
1676
                else {
 
1677
                    ap_rputs("&nbsp;", r);
 
1678
                }
 
1679
                if (autoindex_opts & ICONS_ARE_LINKS) {
 
1680
                    ap_rputs("</a></td>", r);
 
1681
                }
 
1682
                else {
 
1683
                    ap_rputs("</td>", r);
 
1684
                }
 
1685
            }
 
1686
            if (d->name_adjust == K_ADJUST) {
 
1687
                ap_rvputs(r, "<td><a href=\"", anchor, "\">",
 
1688
                          ap_escape_html(scratch, t2), "</a>", NULL);
 
1689
            }
 
1690
            else {
 
1691
                nwidth = strlen(t2);
 
1692
                if (nwidth > name_width) {
 
1693
                  memcpy(name_scratch, t2, name_width - 3);
 
1694
                  name_scratch[name_width - 3] = '.';
 
1695
                  name_scratch[name_width - 2] = '.';
 
1696
                  name_scratch[name_width - 1] = '>';
 
1697
                  name_scratch[name_width] = 0;
 
1698
                  t2 = name_scratch;
 
1699
                  nwidth = name_width;
 
1700
                }
 
1701
                ap_rvputs(r, "<td><a href=\"", anchor, "\">",
 
1702
                          ap_escape_html(scratch, t2),
 
1703
                          "</a>", pad_scratch + nwidth, NULL);
 
1704
            }
 
1705
            if (!(autoindex_opts & SUPPRESS_LAST_MOD)) {
 
1706
                if (ar[x]->lm != -1) {
 
1707
                    char time_str[MAX_STRING_LEN];
 
1708
                    apr_time_exp_t ts;
 
1709
                    apr_time_exp_lt(&ts, ar[x]->lm);
 
1710
                    apr_strftime(time_str, &rv, MAX_STRING_LEN,
 
1711
                                 "</td><td align=\"right\">%d-%b-%Y %H:%M  ",
 
1712
                                 &ts);
 
1713
                    ap_rputs(time_str, r);
 
1714
                }
 
1715
                else {
 
1716
                    ap_rputs("</td><td>&nbsp;", r);
 
1717
                }
 
1718
            }
 
1719
            if (!(autoindex_opts & SUPPRESS_SIZE)) {
 
1720
                char buf[5];
 
1721
                ap_rvputs(r, "</td><td align=\"right\">",
 
1722
                          apr_strfsize(ar[x]->size, buf), NULL);
 
1723
            }
 
1724
            if (!(autoindex_opts & SUPPRESS_DESC)) {
 
1725
                if (ar[x]->desc) {
 
1726
                    if (d->desc_adjust == K_ADJUST) {
 
1727
                        ap_rvputs(r, "</td><td>", ar[x]->desc, NULL);
 
1728
                    }
 
1729
                    else {
 
1730
                        ap_rvputs(r, "</td><td>",
 
1731
                                  terminate_description(d, ar[x]->desc,
 
1732
                                                        autoindex_opts,
 
1733
                                                        desc_width), NULL);
 
1734
                    }
 
1735
                }
 
1736
            }
 
1737
            else {
 
1738
                ap_rputs("</td><td>&nbsp;", r);
 
1739
            }
 
1740
            ap_rputs("</td></tr>\n", r);
 
1741
        }
 
1742
        else if (autoindex_opts & FANCY_INDEXING) {
 
1743
            if (!(autoindex_opts & SUPPRESS_ICON)) {
 
1744
                if (autoindex_opts & ICONS_ARE_LINKS) {
 
1745
                    ap_rvputs(r, "<a href=\"", anchor, "\">", NULL);
 
1746
                }
 
1747
                if ((ar[x]->icon) || d->default_icon) {
 
1748
                    ap_rvputs(r, "<img src=\"",
 
1749
                              ap_escape_html(scratch,
 
1750
                                             ar[x]->icon ? ar[x]->icon
 
1751
                                                         : d->default_icon),
 
1752
                              "\" alt=\"[", (ar[x]->alt ? ar[x]->alt : "   "),
 
1753
                              "]\"", NULL);
 
1754
                    if (d->icon_width) {
 
1755
                        ap_rprintf(r, " width=\"%d\"", d->icon_width);
 
1756
                    }
 
1757
                    if (d->icon_height) {
 
1758
                        ap_rprintf(r, " height=\"%d\"", d->icon_height);
 
1759
                    }
 
1760
 
 
1761
                    if (autoindex_opts & EMIT_XHTML) {
 
1762
                        ap_rputs(" /", r);
 
1763
                    }
 
1764
                    ap_rputs(">", r);
 
1765
                }
 
1766
                else {
 
1767
                    ap_rputs("     ", r);
 
1768
                }
 
1769
                if (autoindex_opts & ICONS_ARE_LINKS) {
 
1770
                    ap_rputs("</a> ", r);
 
1771
                }
 
1772
                else {
 
1773
                    ap_rputc(' ', r);
 
1774
                }
 
1775
            }
 
1776
            nwidth = strlen(t2);
 
1777
            if (nwidth > name_width) {
 
1778
                memcpy(name_scratch, t2, name_width - 3);
 
1779
                name_scratch[name_width - 3] = '.';
 
1780
                name_scratch[name_width - 2] = '.';
 
1781
                name_scratch[name_width - 1] = '>';
 
1782
                name_scratch[name_width] = 0;
 
1783
                t2 = name_scratch;
 
1784
                nwidth = name_width;
 
1785
            }
 
1786
            ap_rvputs(r, "<a href=\"", anchor, "\">",
 
1787
                      ap_escape_html(scratch, t2),
 
1788
                      "</a>", pad_scratch + nwidth, NULL);
 
1789
            /*
 
1790
             * The blank before the storm.. er, before the next field.
 
1791
             */
 
1792
            ap_rputs(" ", r);
 
1793
            if (!(autoindex_opts & SUPPRESS_LAST_MOD)) {
 
1794
                if (ar[x]->lm != -1) {
 
1795
                    char time_str[MAX_STRING_LEN];
 
1796
                    apr_time_exp_t ts;
 
1797
                    apr_time_exp_lt(&ts, ar[x]->lm);
 
1798
                    apr_strftime(time_str, &rv, MAX_STRING_LEN,
 
1799
                                "%d-%b-%Y %H:%M  ", &ts);
 
1800
                    ap_rputs(time_str, r);
 
1801
                }
 
1802
                else {
 
1803
                    /*Length="22-Feb-1998 23:42  " (see 4 lines above) */
 
1804
                    ap_rputs("                   ", r);
 
1805
                }
 
1806
            }
 
1807
            if (!(autoindex_opts & SUPPRESS_SIZE)) {
 
1808
                char buf[5];
 
1809
                ap_rputs(apr_strfsize(ar[x]->size, buf), r);
 
1810
                ap_rputs("  ", r);
 
1811
            }
 
1812
            if (!(autoindex_opts & SUPPRESS_DESC)) {
 
1813
                if (ar[x]->desc) {
 
1814
                    ap_rputs(terminate_description(d, ar[x]->desc,
 
1815
                                                   autoindex_opts,
 
1816
                                                   desc_width), r);
 
1817
                }
 
1818
            }
 
1819
            ap_rputc('\n', r);
 
1820
        }
 
1821
        else {
 
1822
            ap_rvputs(r, "<li><a href=\"", anchor, "\"> ",
 
1823
                      ap_escape_html(scratch, t2),
 
1824
                      "</a></li>\n", NULL);
 
1825
        }
 
1826
    }
 
1827
    if (autoindex_opts & TABLE_INDEXING) {
 
1828
        ap_rvputs(r, breakrow, "</table>\n", NULL);
 
1829
    }
 
1830
    else if (autoindex_opts & FANCY_INDEXING) {
 
1831
        if (!(autoindex_opts & SUPPRESS_RULES)) {
 
1832
            ap_rputs("<hr", r);
 
1833
            if (autoindex_opts & EMIT_XHTML) {
 
1834
                ap_rputs(" /", r);
 
1835
            }
 
1836
            ap_rputs("></pre>\n", r);
 
1837
        }
 
1838
        else {
 
1839
            ap_rputs("</pre>\n", r);
 
1840
        }
 
1841
    }
 
1842
    else {
 
1843
        ap_rputs("</ul>\n", r);
 
1844
    }
 
1845
}
 
1846
 
 
1847
/*
 
1848
 * Compare two file entries according to the sort criteria.  The return
 
1849
 * is essentially a signum function value.
 
1850
 */
 
1851
 
 
1852
static int dsortf(struct ent **e1, struct ent **e2)
 
1853
{
 
1854
    struct ent *c1;
 
1855
    struct ent *c2;
 
1856
    int result = 0;
 
1857
 
 
1858
    /*
 
1859
     * First, see if either of the entries is for the parent directory.
 
1860
     * If so, that *always* sorts lower than anything else.
 
1861
     */
 
1862
    if ((*e1)->name[0] == '/') {
 
1863
        return -1;
 
1864
    }
 
1865
    if ((*e2)->name[0] == '/') {
 
1866
        return 1;
 
1867
    }
 
1868
    /*
 
1869
     * Now see if one's a directory and one isn't, if we're set
 
1870
     * isdir for FOLDERS_FIRST.
 
1871
     */
 
1872
    if ((*e1)->isdir != (*e2)->isdir) {
 
1873
        return (*e1)->isdir ? -1 : 1;
 
1874
    }
 
1875
    /*
 
1876
     * All of our comparisons will be of the c1 entry against the c2 one,
 
1877
     * so assign them appropriately to take care of the ordering.
 
1878
     */
 
1879
    if ((*e1)->ascending) {
 
1880
        c1 = *e1;
 
1881
        c2 = *e2;
 
1882
    }
 
1883
    else {
 
1884
        c1 = *e2;
 
1885
        c2 = *e1;
 
1886
    }
 
1887
 
 
1888
    switch (c1->key) {
 
1889
    case K_LAST_MOD:
 
1890
        if (c1->lm > c2->lm) {
 
1891
            return 1;
 
1892
        }
 
1893
        else if (c1->lm < c2->lm) {
 
1894
            return -1;
 
1895
        }
 
1896
        break;
 
1897
    case K_SIZE:
 
1898
        if (c1->size > c2->size) {
 
1899
            return 1;
 
1900
        }
 
1901
        else if (c1->size < c2->size) {
 
1902
            return -1;
 
1903
        }
 
1904
        break;
 
1905
    case K_DESC:
 
1906
        if (c1->version_sort) {
 
1907
            result = apr_strnatcmp(c1->desc ? c1->desc : "",
 
1908
                                   c2->desc ? c2->desc : "");
 
1909
        }
 
1910
        else {
 
1911
            result = strcmp(c1->desc ? c1->desc : "",
 
1912
                            c2->desc ? c2->desc : "");
 
1913
        }
 
1914
        if (result) {
 
1915
            return result;
 
1916
        }
 
1917
        break;
 
1918
    }
 
1919
 
 
1920
    /* names may identical when treated case-insensitively,
 
1921
     * so always fall back on strcmp() flavors to put entries
 
1922
     * in deterministic order.  This means that 'ABC' and 'abc'
 
1923
     * will always appear in the same order, rather than
 
1924
     * variably between 'ABC abc' and 'abc ABC' order.
 
1925
     */
 
1926
 
 
1927
    if (c1->version_sort) {
 
1928
        if (c1->ignore_case) {
 
1929
            result = apr_strnatcasecmp (c1->name, c2->name);
 
1930
        }
 
1931
        if (!result) {
 
1932
            result = apr_strnatcmp(c1->name, c2->name);
 
1933
        }
 
1934
    }
 
1935
 
 
1936
    /* The names may be identical in respects other other than
 
1937
     * filename case when strnatcmp is used above, so fall back
 
1938
     * to strcmp on conflicts so that fn1.01.zzz and fn1.1.zzz
 
1939
     * are also sorted in a deterministic order.
 
1940
     */
 
1941
 
 
1942
    if (!result && c1->ignore_case) {
 
1943
        result = strcasecmp (c1->name, c2->name);
 
1944
    }
 
1945
 
 
1946
    if (!result) {
 
1947
        result = strcmp (c1->name, c2->name);
 
1948
    }
 
1949
 
 
1950
    return result;
 
1951
}
 
1952
 
 
1953
 
 
1954
static int index_directory(request_rec *r,
 
1955
                           autoindex_config_rec *autoindex_conf)
 
1956
{
 
1957
    char *title_name = ap_escape_html(r->pool, r->uri);
 
1958
    char *title_endp;
 
1959
    char *name = r->filename;
 
1960
    char *pstring = NULL;
 
1961
    apr_finfo_t dirent;
 
1962
    apr_dir_t *thedir;
 
1963
    apr_status_t status;
 
1964
    int num_ent = 0, x;
 
1965
    struct ent *head, *p;
 
1966
    struct ent **ar = NULL;
 
1967
    const char *qstring;
 
1968
    apr_int32_t autoindex_opts = autoindex_conf->opts;
 
1969
    char keyid;
 
1970
    char direction;
 
1971
    char *colargs;
 
1972
    char *fullpath;
 
1973
    apr_size_t dirpathlen;
 
1974
 
 
1975
    if ((status = apr_dir_open(&thedir, name, r->pool)) != APR_SUCCESS) {
 
1976
        ap_log_rerror(APLOG_MARK, APLOG_ERR, status, r,
 
1977
                      "Can't open directory for index: %s", r->filename);
 
1978
        return HTTP_FORBIDDEN;
 
1979
    }
 
1980
 
 
1981
#if APR_HAS_UNICODE_FS
 
1982
    ap_set_content_type(r, "text/html;charset=utf-8");
 
1983
#else
 
1984
    ap_set_content_type(r, "text/html");
 
1985
#endif
 
1986
    if (autoindex_opts & TRACK_MODIFIED) {
 
1987
        ap_update_mtime(r, r->finfo.mtime);
 
1988
        ap_set_last_modified(r);
 
1989
        ap_set_etag(r);
 
1990
    }
 
1991
    if (r->header_only) {
 
1992
        apr_dir_close(thedir);
 
1993
        return 0;
 
1994
    }
 
1995
 
 
1996
    /*
 
1997
     * If there is no specific ordering defined for this directory,
 
1998
     * default to ascending by filename.
 
1999
     */
 
2000
    keyid = autoindex_conf->default_keyid
 
2001
                ? autoindex_conf->default_keyid : K_NAME;
 
2002
    direction = autoindex_conf->default_direction
 
2003
                ? autoindex_conf->default_direction : D_ASCENDING;
 
2004
 
 
2005
    /*
 
2006
     * Figure out what sort of indexing (if any) we're supposed to use.
 
2007
     *
 
2008
     * If no QUERY_STRING was specified or client query strings have been
 
2009
     * explicitly disabled.
 
2010
     * If we are ignoring the client, suppress column sorting as well.
 
2011
     */
 
2012
    if (autoindex_opts & IGNORE_CLIENT) {
 
2013
        qstring = NULL;
 
2014
        autoindex_opts |= SUPPRESS_COLSORT;
 
2015
        colargs = "";
 
2016
    }
 
2017
    else {
 
2018
        char fval[5], vval[5], *ppre = "", *epattern = "";
 
2019
        fval[0] = '\0'; vval[0] = '\0';
 
2020
        qstring = r->args;
 
2021
 
 
2022
        while (qstring && *qstring) {
 
2023
 
 
2024
            /* C= First Sort key Column (N, M, S, D) */
 
2025
            if (   qstring[0] == 'C' && qstring[1] == '='
 
2026
                && qstring[2] && strchr(K_VALID, qstring[2])
 
2027
                && (   qstring[3] == '&' || qstring[3] == ';'
 
2028
                    || !qstring[3])) {
 
2029
                keyid = qstring[2];
 
2030
                qstring += qstring[3] ? 4 : 3;
 
2031
            }
 
2032
 
 
2033
            /* O= Sort order (A, D) */
 
2034
            else if (   qstring[0] == 'O' && qstring[1] == '='
 
2035
                     && (   (qstring[2] == D_ASCENDING)
 
2036
                         || (qstring[2] == D_DESCENDING))
 
2037
                     && (   qstring[3] == '&' || qstring[3] == ';'
 
2038
                         || !qstring[3])) {
 
2039
                direction = qstring[2];
 
2040
                qstring += qstring[3] ? 4 : 3;
 
2041
            }
 
2042
 
 
2043
            /* F= Output Format (0 plain, 1 fancy (pre), 2 table) */
 
2044
            else if (   qstring[0] == 'F' && qstring[1] == '='
 
2045
                     && qstring[2] && strchr("012", qstring[2])
 
2046
                     && (   qstring[3] == '&' || qstring[3] == ';'
 
2047
                         || !qstring[3])) {
 
2048
                if (qstring[2] == '0') {
 
2049
                    autoindex_opts &= ~(FANCY_INDEXING | TABLE_INDEXING);
 
2050
                }
 
2051
                else if (qstring[2] == '1') {
 
2052
                    autoindex_opts = (autoindex_opts | FANCY_INDEXING)
 
2053
                        & ~TABLE_INDEXING;
 
2054
                }
 
2055
                else if (qstring[2] == '2') {
 
2056
                    autoindex_opts |= FANCY_INDEXING | TABLE_INDEXING;
 
2057
                }
 
2058
                strcpy(fval, ";F= ");
 
2059
                fval[3] = qstring[2];
 
2060
                qstring += qstring[3] ? 4 : 3;
 
2061
            }
 
2062
 
 
2063
            /* V= Version sort (0, 1) */
 
2064
            else if (   qstring[0] == 'V' && qstring[1] == '='
 
2065
                     && (qstring[2] == '0' || qstring[2] == '1')
 
2066
                     && (   qstring[3] == '&' || qstring[3] == ';'
 
2067
                         || !qstring[3])) {
 
2068
                if (qstring[2] == '0') {
 
2069
                    autoindex_opts &= ~VERSION_SORT;
 
2070
                }
 
2071
                else if (qstring[2] == '1') {
 
2072
                    autoindex_opts |= VERSION_SORT;
 
2073
                }
 
2074
                strcpy(vval, ";V= ");
 
2075
                vval[3] = qstring[2];
 
2076
                qstring += qstring[3] ? 4 : 3;
 
2077
            }
 
2078
 
 
2079
            /* P= wildcard pattern (*.foo) */
 
2080
            else if (qstring[0] == 'P' && qstring[1] == '=') {
 
2081
                const char *eos = qstring += 2; /* for efficiency */
 
2082
 
 
2083
                while (*eos && *eos != '&' && *eos != ';') {
 
2084
                    ++eos;
 
2085
                }
 
2086
 
 
2087
                if (eos == qstring) {
 
2088
                    pstring = NULL;
 
2089
                }
 
2090
                else {
 
2091
                    pstring = apr_pstrndup(r->pool, qstring, eos - qstring);
 
2092
                    if (ap_unescape_url(pstring) != OK) {
 
2093
                        /* ignore the pattern, if it's bad. */
 
2094
                        pstring = NULL;
 
2095
                    }
 
2096
                    else {
 
2097
                        ppre = ";P=";
 
2098
                        /* be correct */
 
2099
                        epattern = ap_escape_uri(r->pool, pstring);
 
2100
                    }
 
2101
                }
 
2102
 
 
2103
                if (*eos && *++eos) {
 
2104
                    qstring = eos;
 
2105
                }
 
2106
                else {
 
2107
                    qstring = NULL;
 
2108
                }
 
2109
            }
 
2110
 
 
2111
            /* Syntax error?  Ignore the remainder! */
 
2112
            else {
 
2113
                qstring = NULL;
 
2114
            }
 
2115
        }
 
2116
        colargs = apr_pstrcat(r->pool, fval, vval, ppre, epattern, NULL);
 
2117
    }
 
2118
 
 
2119
    /* Spew HTML preamble */
 
2120
    title_endp = title_name + strlen(title_name) - 1;
 
2121
 
 
2122
    while (title_endp > title_name && *title_endp == '/') {
 
2123
        *title_endp-- = '\0';
 
2124
    }
 
2125
 
 
2126
    emit_head(r, find_header(autoindex_conf, r),
 
2127
              autoindex_opts & SUPPRESS_PREAMBLE,
 
2128
              autoindex_opts & EMIT_XHTML, title_name);
 
2129
 
 
2130
    /*
 
2131
     * Since we don't know how many dir. entries there are, put them into a
 
2132
     * linked list and then arrayificate them so qsort can use them.
 
2133
     */
 
2134
    head = NULL;
 
2135
    p = make_parent_entry(autoindex_opts, autoindex_conf, r, keyid, direction);
 
2136
    if (p != NULL) {
 
2137
        p->next = head;
 
2138
        head = p;
 
2139
        num_ent++;
 
2140
    }
 
2141
    fullpath = apr_palloc(r->pool, APR_PATH_MAX);
 
2142
    dirpathlen = strlen(name);
 
2143
    memcpy(fullpath, name, dirpathlen);
 
2144
 
 
2145
    do {
 
2146
        status = apr_dir_read(&dirent, APR_FINFO_MIN | APR_FINFO_NAME, thedir);
 
2147
        if (APR_STATUS_IS_INCOMPLETE(status)) {
 
2148
            continue; /* ignore un-stat()able files */
 
2149
        }
 
2150
        else if (status != APR_SUCCESS) {
 
2151
            break;
 
2152
        }
 
2153
 
 
2154
        /* We want to explode symlinks here. */
 
2155
        if (dirent.filetype == APR_LNK) {
 
2156
            const char *savename;
 
2157
            apr_finfo_t fi;
 
2158
            /* We *must* have FNAME. */
 
2159
            savename = dirent.name;
 
2160
            apr_cpystrn(fullpath + dirpathlen, dirent.name,
 
2161
                        APR_PATH_MAX - dirpathlen);
 
2162
            status = apr_stat(&fi, fullpath,
 
2163
                              dirent.valid & ~(APR_FINFO_NAME), r->pool);
 
2164
            if (status != APR_SUCCESS) {
 
2165
                /* Something bad happened, skip this file. */
 
2166
                continue;
 
2167
            }
 
2168
            memcpy(&dirent, &fi, sizeof(fi));
 
2169
            dirent.name = savename;
 
2170
            dirent.valid |= APR_FINFO_NAME;
 
2171
        }
 
2172
        p = make_autoindex_entry(&dirent, autoindex_opts, autoindex_conf, r,
 
2173
                                 keyid, direction, pstring);
 
2174
        if (p != NULL) {
 
2175
            p->next = head;
 
2176
            head = p;
 
2177
            num_ent++;
 
2178
        }
 
2179
    } while (1);
 
2180
 
 
2181
    if (num_ent > 0) {
 
2182
        ar = (struct ent **) apr_palloc(r->pool,
 
2183
                                        num_ent * sizeof(struct ent *));
 
2184
        p = head;
 
2185
        x = 0;
 
2186
        while (p) {
 
2187
            ar[x++] = p;
 
2188
            p = p->next;
 
2189
        }
 
2190
 
 
2191
        qsort((void *) ar, num_ent, sizeof(struct ent *),
 
2192
              (int (*)(const void *, const void *)) dsortf);
 
2193
    }
 
2194
    output_directories(ar, num_ent, autoindex_conf, r, autoindex_opts,
 
2195
                       keyid, direction, colargs);
 
2196
    apr_dir_close(thedir);
 
2197
 
 
2198
    emit_tail(r, find_readme(autoindex_conf, r),
 
2199
              autoindex_opts & SUPPRESS_PREAMBLE);
 
2200
 
 
2201
    return 0;
 
2202
}
 
2203
 
 
2204
/* The formal handler... */
 
2205
 
 
2206
static int handle_autoindex(request_rec *r)
 
2207
{
 
2208
    autoindex_config_rec *d;
 
2209
    int allow_opts;
 
2210
 
 
2211
    if(strcmp(r->handler,DIR_MAGIC_TYPE)) {
 
2212
        return DECLINED;
 
2213
    }
 
2214
 
 
2215
    allow_opts = ap_allow_options(r);
 
2216
 
 
2217
    d = (autoindex_config_rec *) ap_get_module_config(r->per_dir_config,
 
2218
                                                      &autoindex_module);
 
2219
 
 
2220
    r->allowed |= (AP_METHOD_BIT << M_GET);
 
2221
    if (r->method_number != M_GET) {
 
2222
        return DECLINED;
 
2223
    }
 
2224
 
 
2225
    /* OK, nothing easy.  Trot out the heavy artillery... */
 
2226
 
 
2227
    if (allow_opts & OPT_INDEXES) {
 
2228
        int errstatus;
 
2229
 
 
2230
        if ((errstatus = ap_discard_request_body(r)) != OK) {
 
2231
            return errstatus;
 
2232
        }
 
2233
 
 
2234
        /* KLUDGE --- make the sub_req lookups happen in the right directory.
 
2235
         * Fixing this in the sub_req_lookup functions themselves is difficult,
 
2236
         * and would probably break virtual includes...
 
2237
         */
 
2238
 
 
2239
        if (r->filename[strlen(r->filename) - 1] != '/') {
 
2240
            r->filename = apr_pstrcat(r->pool, r->filename, "/", NULL);
 
2241
        }
 
2242
        return index_directory(r, d);
 
2243
    }
 
2244
    else {
 
2245
        ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
 
2246
                      "Directory index forbidden by "
 
2247
                      "Options directive: %s", r->filename);
 
2248
        return HTTP_FORBIDDEN;
 
2249
    }
 
2250
}
 
2251
 
 
2252
static void register_hooks(apr_pool_t *p)
 
2253
{
 
2254
    ap_hook_handler(handle_autoindex,NULL,NULL,APR_HOOK_MIDDLE);
 
2255
}
 
2256
 
 
2257
module AP_MODULE_DECLARE_DATA autoindex_module =
 
2258
{
 
2259
    STANDARD20_MODULE_STUFF,
 
2260
    create_autoindex_config,    /* dir config creater */
 
2261
    merge_autoindex_configs,    /* dir merger --- default is to override */
 
2262
    NULL,                       /* server config */
 
2263
    NULL,                       /* merge server config */
 
2264
    autoindex_cmds,             /* command apr_table_t */
 
2265
    register_hooks              /* register hooks */
 
2266
};