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