Parent Directory
|
Revision Log
Tag 2.3.1 from trunk.
| 1 | fielding | 420983 | /* 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 | fielding | 83751 | * |
| 8 | nd | 102524 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | fielding | 83751 | * |
| 10 | nd | 102524 | * 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 | fielding | 83751 | */ |
| 16 | |||
| 17 | /* | ||
| 18 | * mod_autoindex.c: Handles the on-the-fly html index generation | ||
| 19 | nd | 98880 | * |
| 20 | fielding | 83751 | * Rob McCool |
| 21 | * 3/23/93 | ||
| 22 | nd | 98880 | * |
| 23 | fielding | 83751 | * Adapted to Apache by rst. |
| 24 | dgaudet | 85021 | * |
| 25 | gstein | 88060 | * Version sort added by Martin Pool <mbp@humbug.org.au>. |
| 26 | */ | ||
| 27 | fielding | 83751 | |
| 28 | rbb | 85867 | #include "apr_strings.h" |
| 29 | gstein | 88060 | #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 | rbb | 84497 | #include "ap_config.h" |
| 37 | fielding | 83751 | #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 | rbb | 88341 | #include "mod_core.h" |
| 47 | gstein | 88060 | |
| 48 | wrowe | 86609 | module AP_MODULE_DECLARE_DATA autoindex_module; |
| 49 | fielding | 83751 | |
| 50 | /**************************************************************** | ||
| 51 | * | ||
| 52 | * Handling configuration directives... | ||
| 53 | */ | ||
| 54 | |||
| 55 | wrowe | 98181 | #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 | nd | 101683 | #define EMIT_XHTML (1 << 17) |
| 73 | pquerna | 104212 | #define SHOW_FORBIDDEN (1 << 18) |
| 74 | fielding | 83751 | |
| 75 | #define K_NOADJUST 0 | ||
| 76 | #define K_ADJUST 1 | ||
| 77 | #define K_UNSET 2 | ||
| 78 | |||
| 79 | /* | ||
| 80 | * Define keys for sorting. | ||
| 81 | */ | ||
| 82 | aaron | 92624 | #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 | wrowe | 89981 | #define K_VALID "NMSD" /* String containing _all_ valid K_ opts */ |
| 87 | fielding | 83751 | |
| 88 | #define D_ASCENDING 'A' | ||
| 89 | #define D_DESCENDING 'D' | ||
| 90 | wrowe | 89981 | #define D_VALID "AD" /* String containing _all_ valid D_ opts */ |
| 91 | fielding | 83751 | |
| 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 | wrowe | 89979 | #define DEFAULT_DESC_WIDTH 23 |
| 103 | fielding | 83751 | |
| 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 | ianh | 101809 | char *style_sheet; |
| 122 | niq | 689261 | char *head_insert; |
| 123 | takashi | 729314 | char *header; |
| 124 | char *readme; | ||
| 125 | wrowe | 89981 | apr_int32_t opts; |
| 126 | apr_int32_t incremented_opts; | ||
| 127 | apr_int32_t decremented_opts; | ||
| 128 | fielding | 83751 | int name_width; |
| 129 | int name_adjust; | ||
| 130 | wrowe | 89979 | int desc_width; |
| 131 | int desc_adjust; | ||
| 132 | fielding | 83751 | int icon_width; |
| 133 | int icon_height; | ||
| 134 | wrowe | 89979 | char default_keyid; |
| 135 | char default_direction; | ||
| 136 | fielding | 83751 | |
| 137 | dougm | 85976 | apr_array_header_t *icon_list; |
| 138 | apr_array_header_t *alt_list; | ||
| 139 | apr_array_header_t *desc_list; | ||
| 140 | apr_array_header_t *ign_list; | ||
| 141 | fielding | 83751 | |
| 142 | jim | 570532 | char *ctype; |
| 143 | char *charset; | ||
| 144 | fielding | 83751 | } autoindex_config_rec; |
| 145 | |||
| 146 | static char c_by_encoding, c_by_type, c_by_path; | ||
| 147 | |||
| 148 | #define BY_ENCODING &c_by_encoding | ||
| 149 | #define BY_TYPE &c_by_type | ||
| 150 | #define BY_PATH &c_by_path | ||
| 151 | |||
| 152 | /* | ||
| 153 | * This routine puts the standard HTML header at the top of the index page. | ||
| 154 | * We include the DOCTYPE because we may be using features therefrom (i.e., | ||
| 155 | * HEIGHT and WIDTH attributes on the icons if we're FancyIndexing). | ||
| 156 | */ | ||
| 157 | nd | 101683 | static void emit_preamble(request_rec *r, int xhtml, const char *title) |
| 158 | fielding | 83751 | { |
| 159 | ianh | 101809 | autoindex_config_rec *d; |
| 160 | |||
| 161 | d = (autoindex_config_rec *) ap_get_module_config(r->per_dir_config, | ||
| 162 | &autoindex_module); | ||
| 163 | |||
| 164 | rpluem | 593816 | if (xhtml) { |
| 165 | ap_rvputs(r, DOCTYPE_XHTML_1_0T, | ||
| 166 | rpluem | 599589 | "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" |
| 167 | " <head>\n <title>Index of ", title, | ||
| 168 | "</title>\n", NULL); | ||
| 169 | rpluem | 593816 | } else { |
| 170 | ap_rvputs(r, DOCTYPE_HTML_3_2, | ||
| 171 | rpluem | 599589 | "<html>\n <head>\n" |
| 172 | " <title>Index of ", title, | ||
| 173 | "</title>\n", NULL); | ||
| 174 | rpluem | 593816 | } |
| 175 | |||
| 176 | ianh | 101809 | if (d->style_sheet != NULL) { |
| 177 | ap_rvputs(r, " <link rel=\"stylesheet\" href=\"", d->style_sheet, | ||
| 178 | nd | 101854 | "\" type=\"text/css\"", xhtml ? " />\n" : ">\n", NULL); |
| 179 | ianh | 101809 | } |
| 180 | niq | 689261 | if (d->head_insert != NULL) { |
| 181 | ap_rputs(d->head_insert, r); | ||
| 182 | } | ||
| 183 | nd | 101853 | ap_rvputs(r, " </head>\n <body>\n", NULL); |
| 184 | fielding | 83751 | } |
| 185 | |||
| 186 | dougm | 85976 | static void push_item(apr_array_header_t *arr, char *type, const char *to, |
| 187 | aaron | 92624 | const char *path, const char *data) |
| 188 | fielding | 83751 | { |
| 189 | dougm | 88019 | struct item *p = (struct item *) apr_array_push(arr); |
| 190 | fielding | 83751 | |
| 191 | if (!to) { | ||
| 192 | aaron | 92624 | to = ""; |
| 193 | fielding | 83751 | } |
| 194 | if (!path) { | ||
| 195 | aaron | 92624 | path = ""; |
| 196 | fielding | 83751 | } |
| 197 | |||
| 198 | p->type = type; | ||
| 199 | dreid | 89511 | p->data = data ? apr_pstrdup(arr->pool, data) : NULL; |
| 200 | p->apply_path = apr_pstrcat(arr->pool, path, "*", NULL); | ||
| 201 | fielding | 83751 | |
| 202 | if ((type == BY_PATH) && (!ap_is_matchexp(to))) { | ||
| 203 | aaron | 92624 | p->apply_to = apr_pstrcat(arr->pool, "*", to, NULL); |
| 204 | fielding | 83751 | } |
| 205 | else if (to) { | ||
| 206 | aaron | 92624 | p->apply_to = apr_pstrdup(arr->pool, to); |
| 207 | fielding | 83751 | } |
| 208 | else { | ||
| 209 | aaron | 92624 | p->apply_to = NULL; |
| 210 | fielding | 83751 | } |
| 211 | } | ||
| 212 | |||
| 213 | ben | 85640 | static const char *add_alt(cmd_parms *cmd, void *d, const char *alt, |
| 214 | aaron | 92624 | const char *to) |
| 215 | fielding | 83751 | { |
| 216 | if (cmd->info == BY_PATH) { | ||
| 217 | if (!strcmp(to, "**DIRECTORY**")) { | ||
| 218 | aaron | 92624 | to = "^^DIRECTORY^^"; |
| 219 | } | ||
| 220 | fielding | 83751 | } |
| 221 | if (cmd->info == BY_ENCODING) { | ||
| 222 | dougm | 85976 | char *tmp = apr_pstrdup(cmd->pool, to); |
| 223 | aaron | 92624 | ap_str_tolower(tmp); |
| 224 | to = tmp; | ||
| 225 | fielding | 83751 | } |
| 226 | |||
| 227 | push_item(((autoindex_config_rec *) d)->alt_list, cmd->info, to, | ||
| 228 | aaron | 92624 | cmd->path, alt); |
| 229 | fielding | 83751 | return NULL; |
| 230 | } | ||
| 231 | |||
| 232 | ben | 85640 | static const char *add_icon(cmd_parms *cmd, void *d, const char *icon, |
| 233 | aaron | 92624 | const char *to) |
| 234 | fielding | 83751 | { |
| 235 | dougm | 85976 | char *iconbak = apr_pstrdup(cmd->pool, icon); |
| 236 | fielding | 83751 | |
| 237 | if (icon[0] == '(') { | ||
| 238 | aaron | 92624 | char *alt; |
| 239 | char *cl = strchr(iconbak, ')'); | ||
| 240 | fielding | 83751 | |
| 241 | aaron | 92624 | if (cl == NULL) { |
| 242 | return "missing closing paren"; | ||
| 243 | } | ||
| 244 | alt = ap_getword_nc(cmd->pool, &iconbak, ','); | ||
| 245 | *cl = '\0'; /* Lose closing paren */ | ||
| 246 | add_alt(cmd, d, &alt[1], to); | ||
| 247 | fielding | 83751 | } |
| 248 | if (cmd->info == BY_PATH) { | ||
| 249 | if (!strcmp(to, "**DIRECTORY**")) { | ||
| 250 | aaron | 92624 | to = "^^DIRECTORY^^"; |
| 251 | } | ||
| 252 | fielding | 83751 | } |
| 253 | if (cmd->info == BY_ENCODING) { | ||
| 254 | dougm | 85976 | char *tmp = apr_pstrdup(cmd->pool, to); |
| 255 | aaron | 92624 | ap_str_tolower(tmp); |
| 256 | to = tmp; | ||
| 257 | fielding | 83751 | } |
| 258 | |||
| 259 | push_item(((autoindex_config_rec *) d)->icon_list, cmd->info, to, | ||
| 260 | aaron | 92624 | cmd->path, iconbak); |
| 261 | fielding | 83751 | return NULL; |
| 262 | } | ||
| 263 | |||
| 264 | /* | ||
| 265 | * Add description text for a filename pattern. If the pattern has | ||
| 266 | * wildcards already (or we need to add them), add leading and | ||
| 267 | * trailing wildcards to it to ensure substring processing. If the | ||
| 268 | * pattern contains a '/' anywhere, force wildcard matching mode, | ||
| 269 | * add a slash to the prefix so that "bar/bletch" won't be matched | ||
| 270 | * by "foobar/bletch", and make a note that there's a delimiter; | ||
| 271 | * the matching routine simplifies to just the actual filename | ||
| 272 | * whenever it can. This allows definitions in parent directories | ||
| 273 | * to be made for files in subordinate ones using relative paths. | ||
| 274 | */ | ||
| 275 | |||
| 276 | /* | ||
| 277 | * Absent a strcasestr() function, we have to force wildcards on | ||
| 278 | * systems for which "AAA" and "aaa" mean the same file. | ||
| 279 | */ | ||
| 280 | #ifdef CASE_BLIND_FILESYSTEM | ||
| 281 | #define WILDCARDS_REQUIRED 1 | ||
| 282 | #else | ||
| 283 | #define WILDCARDS_REQUIRED 0 | ||
| 284 | #endif | ||
| 285 | |||
| 286 | ben | 85640 | static const char *add_desc(cmd_parms *cmd, void *d, const char *desc, |
| 287 | aaron | 92624 | const char *to) |
| 288 | fielding | 83751 | { |
| 289 | autoindex_config_rec *dcfg = (autoindex_config_rec *) d; | ||
| 290 | ai_desc_t *desc_entry; | ||
| 291 | char *prefix = ""; | ||
| 292 | |||
| 293 | dougm | 88019 | desc_entry = (ai_desc_t *) apr_array_push(dcfg->desc_list); |
| 294 | ben | 85640 | desc_entry->full_path = (ap_strchr_c(to, '/') == NULL) ? 0 : 1; |
| 295 | fielding | 83751 | desc_entry->wildcards = (WILDCARDS_REQUIRED |
| 296 | aaron | 92624 | || desc_entry->full_path |
| 297 | wrowe | 95976 | || apr_fnmatch_test(to)); |
| 298 | fielding | 83751 | if (desc_entry->wildcards) { |
| 299 | aaron | 92624 | prefix = desc_entry->full_path ? "*/" : "*"; |
| 300 | desc_entry->pattern = apr_pstrcat(dcfg->desc_list->pool, | ||
| 301 | prefix, to, "*", NULL); | ||
| 302 | fielding | 83751 | } |
| 303 | else { | ||
| 304 | aaron | 92624 | desc_entry->pattern = apr_pstrdup(dcfg->desc_list->pool, to); |
| 305 | fielding | 83751 | } |
| 306 | dreid | 89511 | desc_entry->description = apr_pstrdup(dcfg->desc_list->pool, desc); |
| 307 | fielding | 83751 | return NULL; |
| 308 | } | ||
| 309 | |||
| 310 | trawick | 85627 | static const char *add_ignore(cmd_parms *cmd, void *d, const char *ext) |
| 311 | fielding | 83751 | { |
| 312 | push_item(((autoindex_config_rec *) d)->ign_list, 0, ext, cmd->path, NULL); | ||
| 313 | return NULL; | ||
| 314 | } | ||
| 315 | |||
| 316 | pquerna | 168202 | static const char *add_opts(cmd_parms *cmd, void *d, int argc, char *const argv[]) |
| 317 | fielding | 83751 | { |
| 318 | pquerna | 168202 | int i; |
| 319 | fielding | 83751 | char *w; |
| 320 | wrowe | 89981 | apr_int32_t opts; |
| 321 | apr_int32_t opts_add; | ||
| 322 | apr_int32_t opts_remove; | ||
| 323 | fielding | 83751 | char action; |
| 324 | autoindex_config_rec *d_cfg = (autoindex_config_rec *) d; | ||
| 325 | |||
| 326 | opts = d_cfg->opts; | ||
| 327 | opts_add = d_cfg->incremented_opts; | ||
| 328 | opts_remove = d_cfg->decremented_opts; | ||
| 329 | pquerna | 168202 | |
| 330 | for (i = 0; i < argc; i++) { | ||
| 331 | aaron | 92624 | int option = 0; |
| 332 | pquerna | 168202 | w = argv[i]; |
| 333 | fielding | 83751 | |
| 334 | aaron | 92624 | if ((*w == '+') || (*w == '-')) { |
| 335 | action = *(w++); | ||
| 336 | } | ||
| 337 | else { | ||
| 338 | action = '\0'; | ||
| 339 | } | ||
| 340 | if (!strcasecmp(w, "FancyIndexing")) { | ||
| 341 | option = FANCY_INDEXING; | ||
| 342 | } | ||
| 343 | coar | 91660 | else if (!strcasecmp(w, "FoldersFirst")) { |
| 344 | nd | 98880 | option = FOLDERS_FIRST; |
| 345 | } | ||
| 346 | aaron | 92624 | else if (!strcasecmp(w, "HTMLTable")) { |
| 347 | option = TABLE_INDEXING; | ||
| 348 | } | ||
| 349 | else if (!strcasecmp(w, "IconsAreLinks")) { | ||
| 350 | option = ICONS_ARE_LINKS; | ||
| 351 | } | ||
| 352 | wrowe | 98181 | else if (!strcasecmp(w, "IgnoreCase")) { |
| 353 | option = IGNORE_CASE; | ||
| 354 | } | ||
| 355 | wrowe | 89981 | else if (!strcasecmp(w, "IgnoreClient")) { |
| 356 | option = IGNORE_CLIENT; | ||
| 357 | aaron | 92624 | } |
| 358 | else if (!strcasecmp(w, "ScanHTMLTitles")) { | ||
| 359 | option = SCAN_HTML_TITLES; | ||
| 360 | } | ||
| 361 | wrowe | 89981 | else if (!strcasecmp(w, "SuppressColumnSorting")) { |
| 362 | option = SUPPRESS_COLSORT; | ||
| 363 | aaron | 92624 | } |
| 364 | else if (!strcasecmp(w, "SuppressDescription")) { | ||
| 365 | option = SUPPRESS_DESC; | ||
| 366 | } | ||
| 367 | else if (!strcasecmp(w, "SuppressHTMLPreamble")) { | ||
| 368 | option = SUPPRESS_PREAMBLE; | ||
| 369 | } | ||
| 370 | else if (!strcasecmp(w, "SuppressIcon")) { | ||
| 371 | option = SUPPRESS_ICON; | ||
| 372 | } | ||
| 373 | else if (!strcasecmp(w, "SuppressLastModified")) { | ||
| 374 | option = SUPPRESS_LAST_MOD; | ||
| 375 | } | ||
| 376 | else if (!strcasecmp(w, "SuppressSize")) { | ||
| 377 | option = SUPPRESS_SIZE; | ||
| 378 | } | ||
| 379 | wrowe | 89979 | else if (!strcasecmp(w, "SuppressRules")) { |
| 380 | option = SUPPRESS_RULES; | ||
| 381 | aaron | 92624 | } |
| 382 | coar | 91660 | else if (!strcasecmp(w, "TrackModified")) { |
| 383 | nd | 98880 | option = TRACK_MODIFIED; |
| 384 | } | ||
| 385 | dgaudet | 85021 | else if (!strcasecmp(w, "VersionSort")) { |
| 386 | option = VERSION_SORT; | ||
| 387 | aaron | 92624 | } |
| 388 | nd | 101683 | else if (!strcasecmp(w, "XHTML")) { |
| 389 | option = EMIT_XHTML; | ||
| 390 | } | ||
| 391 | pquerna | 104212 | else if (!strcasecmp(w, "ShowForbidden")) { |
| 392 | option = SHOW_FORBIDDEN; | ||
| 393 | } | ||
| 394 | aaron | 92624 | else if (!strcasecmp(w, "None")) { |
| 395 | if (action != '\0') { | ||
| 396 | return "Cannot combine '+' or '-' with 'None' keyword"; | ||
| 397 | } | ||
| 398 | opts = NO_OPTIONS; | ||
| 399 | opts_add = 0; | ||
| 400 | opts_remove = 0; | ||
| 401 | } | ||
| 402 | else if (!strcasecmp(w, "IconWidth")) { | ||
| 403 | if (action != '-') { | ||
| 404 | d_cfg->icon_width = DEFAULT_ICON_WIDTH; | ||
| 405 | } | ||
| 406 | else { | ||
| 407 | d_cfg->icon_width = 0; | ||
| 408 | } | ||
| 409 | } | ||
| 410 | else if (!strncasecmp(w, "IconWidth=", 10)) { | ||
| 411 | if (action == '-') { | ||
| 412 | return "Cannot combine '-' with IconWidth=n"; | ||
| 413 | } | ||
| 414 | d_cfg->icon_width = atoi(&w[10]); | ||
| 415 | } | ||
| 416 | else if (!strcasecmp(w, "IconHeight")) { | ||
| 417 | if (action != '-') { | ||
| 418 | d_cfg->icon_height = DEFAULT_ICON_HEIGHT; | ||
| 419 | } | ||
| 420 | else { | ||
| 421 | d_cfg->icon_height = 0; | ||
| 422 | } | ||
| 423 | } | ||
| 424 | else if (!strncasecmp(w, "IconHeight=", 11)) { | ||
| 425 | if (action == '-') { | ||
| 426 | return "Cannot combine '-' with IconHeight=n"; | ||
| 427 | } | ||
| 428 | d_cfg->icon_height = atoi(&w[11]); | ||
| 429 | } | ||
| 430 | else if (!strcasecmp(w, "NameWidth")) { | ||
| 431 | if (action != '-') { | ||
| 432 | return "NameWidth with no value may only appear as " | ||
| 433 | "'-NameWidth'"; | ||
| 434 | } | ||
| 435 | d_cfg->name_width = DEFAULT_NAME_WIDTH; | ||
| 436 | d_cfg->name_adjust = K_NOADJUST; | ||
| 437 | } | ||
| 438 | else if (!strncasecmp(w, "NameWidth=", 10)) { | ||
| 439 | if (action == '-') { | ||
| 440 | return "Cannot combine '-' with NameWidth=n"; | ||
| 441 | } | ||
| 442 | if (w[10] == '*') { | ||
| 443 | d_cfg->name_adjust = K_ADJUST; | ||
| 444 | } | ||
| 445 | else { | ||
| 446 | int width = atoi(&w[10]); | ||
| 447 | fielding | 83751 | |
| 448 | aaron | 92624 | if (width && (width < 5)) { |
| 449 | return "NameWidth value must be greater than 5"; | ||
| 450 | } | ||
| 451 | d_cfg->name_width = width; | ||
| 452 | d_cfg->name_adjust = K_NOADJUST; | ||
| 453 | } | ||
| 454 | } | ||
| 455 | coar | 91660 | else if (!strcasecmp(w, "DescriptionWidth")) { |
| 456 | nd | 98880 | if (action != '-') { |
| 457 | return "DescriptionWidth with no value may only appear as " | ||
| 458 | "'-DescriptionWidth'"; | ||
| 459 | } | ||
| 460 | d_cfg->desc_width = DEFAULT_DESC_WIDTH; | ||
| 461 | d_cfg->desc_adjust = K_NOADJUST; | ||
| 462 | } | ||
| 463 | coar | 91660 | else if (!strncasecmp(w, "DescriptionWidth=", 17)) { |
| 464 | nd | 98880 | if (action == '-') { |
| 465 | return "Cannot combine '-' with DescriptionWidth=n"; | ||
| 466 | } | ||
| 467 | if (w[17] == '*') { | ||
| 468 | d_cfg->desc_adjust = K_ADJUST; | ||
| 469 | } | ||
| 470 | coar | 91660 | else { |
| 471 | nd | 98880 | int width = atoi(&w[17]); |
| 472 | wrowe | 89979 | |
| 473 | nd | 98880 | if (width && (width < 12)) { |
| 474 | return "DescriptionWidth value must be greater than 12"; | ||
| 475 | } | ||
| 476 | d_cfg->desc_width = width; | ||
| 477 | d_cfg->desc_adjust = K_NOADJUST; | ||
| 478 | } | ||
| 479 | } | ||
| 480 | jim | 570558 | else if (!strncasecmp(w, "Type=", 5)) { |
| 481 | d_cfg->ctype = apr_pstrdup(cmd->pool, &w[5]); | ||
| 482 | jim | 570532 | } |
| 483 | else if (!strncasecmp(w, "Charset=", 8)) { | ||
| 484 | d_cfg->charset = apr_pstrdup(cmd->pool, &w[8]); | ||
| 485 | } | ||
| 486 | aaron | 92624 | else { |
| 487 | return "Invalid directory indexing option"; | ||
| 488 | } | ||
| 489 | if (action == '\0') { | ||
| 490 | opts |= option; | ||
| 491 | opts_add = 0; | ||
| 492 | opts_remove = 0; | ||
| 493 | } | ||
| 494 | else if (action == '+') { | ||
| 495 | opts_add |= option; | ||
| 496 | opts_remove &= ~option; | ||
| 497 | } | ||
| 498 | else { | ||
| 499 | opts_remove |= option; | ||
| 500 | opts_add &= ~option; | ||
| 501 | } | ||
| 502 | fielding | 83751 | } |
| 503 | if ((opts & NO_OPTIONS) && (opts & ~NO_OPTIONS)) { | ||
| 504 | aaron | 92624 | return "Cannot combine other IndexOptions keywords with 'None'"; |
| 505 | fielding | 83751 | } |
| 506 | d_cfg->incremented_opts = opts_add; | ||
| 507 | d_cfg->decremented_opts = opts_remove; | ||
| 508 | d_cfg->opts = opts; | ||
| 509 | return NULL; | ||
| 510 | } | ||
| 511 | |||
| 512 | aaron | 92624 | static const char *set_default_order(cmd_parms *cmd, void *m, |
| 513 | const char *direction, const char *key) | ||
| 514 | fielding | 83751 | { |
| 515 | autoindex_config_rec *d_cfg = (autoindex_config_rec *) m; | ||
| 516 | |||
| 517 | if (!strcasecmp(direction, "Ascending")) { | ||
| 518 | aaron | 92624 | d_cfg->default_direction = D_ASCENDING; |
| 519 | fielding | 83751 | } |
| 520 | else if (!strcasecmp(direction, "Descending")) { | ||
| 521 | aaron | 92624 | d_cfg->default_direction = D_DESCENDING; |
| 522 | fielding | 83751 | } |
| 523 | else { | ||
| 524 | aaron | 92624 | return "First keyword must be 'Ascending' or 'Descending'"; |
| 525 | fielding | 83751 | } |
| 526 | |||
| 527 | if (!strcasecmp(key, "Name")) { | ||
| 528 | aaron | 92624 | d_cfg->default_keyid = K_NAME; |
| 529 | fielding | 83751 | } |
| 530 | else if (!strcasecmp(key, "Date")) { | ||
| 531 | aaron | 92624 | d_cfg->default_keyid = K_LAST_MOD; |
| 532 | fielding | 83751 | } |
| 533 | else if (!strcasecmp(key, "Size")) { | ||
| 534 | aaron | 92624 | d_cfg->default_keyid = K_SIZE; |
| 535 | fielding | 83751 | } |
| 536 | else if (!strcasecmp(key, "Description")) { | ||
| 537 | aaron | 92624 | d_cfg->default_keyid = K_DESC; |
| 538 | fielding | 83751 | } |
| 539 | else { | ||
| 540 | aaron | 92624 | return "Second keyword must be 'Name', 'Date', 'Size', or " |
| 541 | "'Description'"; | ||
| 542 | fielding | 83751 | } |
| 543 | |||
| 544 | return NULL; | ||
| 545 | } | ||
| 546 | |||
| 547 | #define DIR_CMD_PERMS OR_INDEXES | ||
| 548 | |||
| 549 | static const command_rec autoindex_cmds[] = | ||
| 550 | { | ||
| 551 | nd | 98880 | AP_INIT_ITERATE2("AddIcon", add_icon, BY_PATH, DIR_CMD_PERMS, |
| 552 | trawick | 85627 | "an icon URL followed by one or more filenames"), |
| 553 | AP_INIT_ITERATE2("AddIconByType", add_icon, BY_TYPE, DIR_CMD_PERMS, | ||
| 554 | "an icon URL followed by one or more MIME types"), | ||
| 555 | AP_INIT_ITERATE2("AddIconByEncoding", add_icon, BY_ENCODING, DIR_CMD_PERMS, | ||
| 556 | "an icon URL followed by one or more content encodings"), | ||
| 557 | AP_INIT_ITERATE2("AddAlt", add_alt, BY_PATH, DIR_CMD_PERMS, | ||
| 558 | nd | 98880 | "alternate descriptive text followed by one or more " |
| 559 | "filenames"), | ||
| 560 | trawick | 85627 | AP_INIT_ITERATE2("AddAltByType", add_alt, BY_TYPE, DIR_CMD_PERMS, |
| 561 | nd | 98880 | "alternate descriptive text followed by one or more MIME " |
| 562 | "types"), | ||
| 563 | trawick | 85627 | AP_INIT_ITERATE2("AddAltByEncoding", add_alt, BY_ENCODING, DIR_CMD_PERMS, |
| 564 | nd | 98880 | "alternate descriptive text followed by one or more " |
| 565 | "content encodings"), | ||
| 566 | pquerna | 168202 | AP_INIT_TAKE_ARGV("IndexOptions", add_opts, NULL, DIR_CMD_PERMS, |
| 567 | "one or more index options [+|-][]"), | ||
| 568 | trawick | 85627 | AP_INIT_TAKE2("IndexOrderDefault", set_default_order, NULL, DIR_CMD_PERMS, |
| 569 | "{Ascending,Descending} {Name,Size,Description,Date}"), | ||
| 570 | AP_INIT_ITERATE("IndexIgnore", add_ignore, NULL, DIR_CMD_PERMS, | ||
| 571 | "one or more file extensions"), | ||
| 572 | AP_INIT_ITERATE2("AddDescription", add_desc, BY_PATH, DIR_CMD_PERMS, | ||
| 573 | "Descriptive text followed by one or more filenames"), | ||
| 574 | takashi | 729314 | AP_INIT_TAKE1("HeaderName", ap_set_string_slot, |
| 575 | (void *)APR_OFFSETOF(autoindex_config_rec, header), | ||
| 576 | DIR_CMD_PERMS, "a filename"), | ||
| 577 | AP_INIT_TAKE1("ReadmeName", ap_set_string_slot, | ||
| 578 | (void *)APR_OFFSETOF(autoindex_config_rec, header), | ||
| 579 | DIR_CMD_PERMS, "a filename"), | ||
| 580 | wrowe | 89897 | AP_INIT_RAW_ARGS("FancyIndexing", ap_set_deprecated, NULL, OR_ALL, |
| 581 | nd | 98880 | "The FancyIndexing directive is no longer supported. " |
| 582 | "Use IndexOptions FancyIndexing."), | ||
| 583 | trawick | 85627 | AP_INIT_TAKE1("DefaultIcon", ap_set_string_slot, |
| 584 | jwoolley | 95164 | (void *)APR_OFFSETOF(autoindex_config_rec, default_icon), |
| 585 | trawick | 85627 | DIR_CMD_PERMS, "an icon URL"), |
| 586 | ianh | 101809 | AP_INIT_TAKE1("IndexStyleSheet", ap_set_string_slot, |
| 587 | (void *)APR_OFFSETOF(autoindex_config_rec, style_sheet), | ||
| 588 | DIR_CMD_PERMS, "URL to style sheet"), | ||
| 589 | niq | 689261 | AP_INIT_TAKE1("IndexHeadInsert", ap_set_string_slot, |
| 590 | (void *)APR_OFFSETOF(autoindex_config_rec, head_insert), | ||
| 591 | DIR_CMD_PERMS, "String to insert in HTML HEAD section"), | ||
| 592 | fielding | 83751 | {NULL} |
| 593 | }; | ||
| 594 | |||
| 595 | dougm | 85976 | static void *create_autoindex_config(apr_pool_t *p, char *dummy) |
| 596 | fielding | 83751 | { |
| 597 | autoindex_config_rec *new = | ||
| 598 | dougm | 85976 | (autoindex_config_rec *) apr_pcalloc(p, sizeof(autoindex_config_rec)); |
| 599 | fielding | 83751 | |
| 600 | new->icon_width = 0; | ||
| 601 | new->icon_height = 0; | ||
| 602 | new->name_width = DEFAULT_NAME_WIDTH; | ||
| 603 | new->name_adjust = K_UNSET; | ||
| 604 | nd | 98880 | new->desc_width = DEFAULT_DESC_WIDTH; |
| 605 | new->desc_adjust = K_UNSET; | ||
| 606 | dougm | 88019 | new->icon_list = apr_array_make(p, 4, sizeof(struct item)); |
| 607 | new->alt_list = apr_array_make(p, 4, sizeof(struct item)); | ||
| 608 | new->desc_list = apr_array_make(p, 4, sizeof(ai_desc_t)); | ||
| 609 | new->ign_list = apr_array_make(p, 4, sizeof(struct item)); | ||
| 610 | fielding | 83751 | new->opts = 0; |
| 611 | new->incremented_opts = 0; | ||
| 612 | new->decremented_opts = 0; | ||
| 613 | wrowe | 89979 | new->default_keyid = '\0'; |
| 614 | new->default_direction = '\0'; | ||
| 615 | fielding | 83751 | |
| 616 | return (void *) new; | ||
| 617 | } | ||
| 618 | |||
| 619 | dougm | 85976 | static void *merge_autoindex_configs(apr_pool_t *p, void *basev, void *addv) |
| 620 | fielding | 83751 | { |
| 621 | autoindex_config_rec *new; | ||
| 622 | autoindex_config_rec *base = (autoindex_config_rec *) basev; | ||
| 623 | autoindex_config_rec *add = (autoindex_config_rec *) addv; | ||
| 624 | |||
| 625 | dougm | 85976 | new = (autoindex_config_rec *) apr_pcalloc(p, sizeof(autoindex_config_rec)); |
| 626 | fielding | 83751 | new->default_icon = add->default_icon ? add->default_icon |
| 627 | : base->default_icon; | ||
| 628 | ianh | 101809 | new->style_sheet = add->style_sheet ? add->style_sheet |
| 629 | : base->style_sheet; | ||
| 630 | niq | 689261 | new->head_insert = add->head_insert ? add->head_insert |
| 631 | : base->head_insert; | ||
| 632 | takashi | 729314 | new->header = add->header ? add->header |
| 633 | : base->header; | ||
| 634 | new->readme = add->readme ? add->readme | ||
| 635 | : base->readme; | ||
| 636 | fielding | 83751 | new->icon_height = add->icon_height ? add->icon_height : base->icon_height; |
| 637 | new->icon_width = add->icon_width ? add->icon_width : base->icon_width; | ||
| 638 | |||
| 639 | jim | 570532 | new->ctype = add->ctype ? add->ctype : base->ctype; |
| 640 | new->charset = add->charset ? add->charset : base->charset; | ||
| 641 | |||
| 642 | dougm | 88019 | new->alt_list = apr_array_append(p, add->alt_list, base->alt_list); |
| 643 | new->ign_list = apr_array_append(p, add->ign_list, base->ign_list); | ||
| 644 | new->desc_list = apr_array_append(p, add->desc_list, base->desc_list); | ||
| 645 | new->icon_list = apr_array_append(p, add->icon_list, base->icon_list); | ||
| 646 | fielding | 83751 | if (add->opts & NO_OPTIONS) { |
| 647 | aaron | 92624 | /* |
| 648 | * If the current directory says 'no options' then we also | ||
| 649 | * clear any incremental mods from being inheritable further down. | ||
| 650 | */ | ||
| 651 | new->opts = NO_OPTIONS; | ||
| 652 | new->incremented_opts = 0; | ||
| 653 | new->decremented_opts = 0; | ||
| 654 | fielding | 83751 | } |
| 655 | else { | ||
| 656 | aaron | 92624 | /* |
| 657 | * If there were any nonincremental options selected for | ||
| 658 | * this directory, they dominate and we don't inherit *anything.* | ||
| 659 | * Contrariwise, we *do* inherit if the only settings here are | ||
| 660 | * incremental ones. | ||
| 661 | */ | ||
| 662 | if (add->opts == 0) { | ||
| 663 | nd | 98880 | new->incremented_opts = (base->incremented_opts |
| 664 | aaron | 92624 | | add->incremented_opts) |
| 665 | & ~add->decremented_opts; | ||
| 666 | new->decremented_opts = (base->decremented_opts | ||
| 667 | | add->decremented_opts); | ||
| 668 | /* | ||
| 669 | * We may have incremental settings, so make sure we don't | ||
| 670 | * inadvertently inherit an IndexOptions None from above. | ||
| 671 | */ | ||
| 672 | new->opts = (base->opts & ~NO_OPTIONS); | ||
| 673 | } | ||
| 674 | else { | ||
| 675 | /* | ||
| 676 | * There are local nonincremental settings, which clear | ||
| 677 | * all inheritance from above. They *are* the new base settings. | ||
| 678 | */ | ||
| 679 | new->opts = add->opts;; | ||
| 680 | } | ||
| 681 | /* | ||
| 682 | * We're guaranteed that there'll be no overlap between | ||
| 683 | * the add-options and the remove-options. | ||
| 684 | */ | ||
| 685 | new->opts |= new->incremented_opts; | ||
| 686 | new->opts &= ~new->decremented_opts; | ||
| 687 | fielding | 83751 | } |
| 688 | /* | ||
| 689 | * Inherit the NameWidth settings if there aren't any specific to | ||
| 690 | * the new location; otherwise we'll end up using the defaults set in the | ||
| 691 | * config-rec creation routine. | ||
| 692 | */ | ||
| 693 | if (add->name_adjust == K_UNSET) { | ||
| 694 | aaron | 92624 | new->name_width = base->name_width; |
| 695 | new->name_adjust = base->name_adjust; | ||
| 696 | fielding | 83751 | } |
| 697 | else { | ||
| 698 | aaron | 92624 | new->name_width = add->name_width; |
| 699 | new->name_adjust = add->name_adjust; | ||
| 700 | fielding | 83751 | } |
| 701 | wrowe | 89981 | |
| 702 | nd | 98880 | /* |
| 703 | * Likewise for DescriptionWidth. | ||
| 704 | */ | ||
| 705 | if (add->desc_adjust == K_UNSET) { | ||
| 706 | new->desc_width = base->desc_width; | ||
| 707 | new->desc_adjust = base->desc_adjust; | ||
| 708 | } | ||
| 709 | coar | 91660 | else { |
| 710 | nd | 98880 | new->desc_width = add->desc_width; |
| 711 | new->desc_adjust = add->desc_adjust; | ||
| 712 | } | ||
| 713 | fielding | 83751 | |
| 714 | nd | 98880 | new->default_keyid = add->default_keyid ? add->default_keyid |
| 715 | wrowe | 89979 | : base->default_keyid; |
| 716 | nd | 98880 | new->default_direction = add->default_direction ? add->default_direction |
| 717 | wrowe | 89979 | : base->default_direction; |
| 718 | fielding | 83751 | return new; |
| 719 | } | ||
| 720 | |||
| 721 | /**************************************************************** | ||
| 722 | * | ||
| 723 | * Looking things up in config entries... | ||
| 724 | */ | ||
| 725 | |||
| 726 | /* Structure used to hold entries when we're actually building an index */ | ||
| 727 | |||
| 728 | struct ent { | ||
| 729 | char *name; | ||
| 730 | char *icon; | ||
| 731 | char *alt; | ||
| 732 | char *desc; | ||
| 733 | wrowe | 89683 | apr_off_t size; |
| 734 | dougm | 85976 | apr_time_t lm; |
| 735 | fielding | 83751 | struct ent *next; |
| 736 | wrowe | 98181 | int ascending, ignore_case, version_sort; |
| 737 | fielding | 83751 | char key; |
| 738 | wrowe | 89979 | int isdir; |
| 739 | fielding | 83751 | }; |
| 740 | |||
| 741 | takashi | 729388 | static char *find_item(const char *content_type, const char *content_encoding, |
| 742 | char *path, apr_array_header_t *list, int path_only) | ||
| 743 | fielding | 83751 | { |
| 744 | struct item *items = (struct item *) list->elts; | ||
| 745 | int i; | ||
| 746 | |||
| 747 | for (i = 0; i < list->nelts; ++i) { | ||
| 748 | aaron | 92624 | struct item *p = &items[i]; |
| 749 | fielding | 83751 | |
| 750 | aaron | 92624 | /* Special cased for ^^DIRECTORY^^ and ^^BLANKICON^^ */ |
| 751 | if ((path[0] == '^') || (!ap_strcmp_match(path, p->apply_path))) { | ||
| 752 | if (!*(p->apply_to)) { | ||
| 753 | return p->data; | ||
| 754 | } | ||
| 755 | else if (p->type == BY_PATH || path[0] == '^') { | ||
| 756 | if (!ap_strcmp_match(path, p->apply_to)) { | ||
| 757 | return p->data; | ||
| 758 | } | ||
| 759 | } | ||
| 760 | else if (!path_only) { | ||
| 761 | if (!content_encoding) { | ||
| 762 | if (p->type == BY_TYPE) { | ||
| 763 | if (content_type | ||
| 764 | && !ap_strcasecmp_match(content_type, | ||
| 765 | p->apply_to)) { | ||
| 766 | return p->data; | ||
| 767 | } | ||
| 768 | } | ||
| 769 | } | ||
| 770 | else { | ||
| 771 | if (p->type == BY_ENCODING) { | ||
| 772 | if (!ap_strcasecmp_match(content_encoding, | ||
| 773 | p->apply_to)) { | ||
| 774 | return p->data; | ||
| 775 | } | ||
| 776 | } | ||
| 777 | } | ||
| 778 | } | ||
| 779 | } | ||
| 780 | fielding | 83751 | } |
| 781 | return NULL; | ||
| 782 | } | ||
| 783 | |||
| 784 | takashi | 729388 | static char *find_item_by_request(request_rec *r, apr_array_header_t *list, int path_only) |
| 785 | fielding | 83751 | { |
| 786 | takashi | 729388 | return find_item(ap_field_noparam(r->pool, r->content_type), |
| 787 | r->content_encoding, r->filename, list, path_only); | ||
| 788 | fielding | 83751 | } |
| 789 | |||
| 790 | takashi | 729388 | #define find_icon(d,p,t) find_item_by_request(p,d->icon_list,t) |
| 791 | #define find_alt(d,p,t) find_item_by_request(p,d->alt_list,t) | ||
| 792 | #define find_default_icon(d,n) find_item(NULL, NULL, n, d->icon_list, 1) | ||
| 793 | #define find_default_alt(d,n) find_item(NULL, NULL, n, d->alt_list, 1) | ||
| 794 | wrowe | 89909 | |
| 795 | fielding | 83751 | /* |
| 796 | * Look through the list of pattern/description pairs and return the first one | ||
| 797 | * if any) that matches the filename in the request. If multiple patterns | ||
| 798 | * match, only the first one is used; since the order in the array is the | ||
| 799 | * same as the order in which directives were processed, earlier matching | ||
| 800 | * directives will dominate. | ||
| 801 | */ | ||
| 802 | |||
| 803 | #ifdef CASE_BLIND_FILESYSTEM | ||
| 804 | trawick | 101154 | #define MATCH_FLAGS APR_FNM_CASE_BLIND |
| 805 | fielding | 83751 | #else |
| 806 | #define MATCH_FLAGS 0 | ||
| 807 | #endif | ||
| 808 | |||
| 809 | wrowe | 89909 | static char *find_desc(autoindex_config_rec *dcfg, const char *filename_full) |
| 810 | fielding | 83751 | { |
| 811 | int i; | ||
| 812 | ai_desc_t *list = (ai_desc_t *) dcfg->desc_list->elts; | ||
| 813 | const char *filename_only; | ||
| 814 | const char *filename; | ||
| 815 | |||
| 816 | /* | ||
| 817 | * If the filename includes a path, extract just the name itself | ||
| 818 | * for the simple matches. | ||
| 819 | */ | ||
| 820 | ben | 85599 | if ((filename_only = ap_strrchr_c(filename_full, '/')) == NULL) { |
| 821 | aaron | 92624 | filename_only = filename_full; |
| 822 | fielding | 83751 | } |
| 823 | else { | ||
| 824 | aaron | 92624 | filename_only++; |
| 825 | fielding | 83751 | } |
| 826 | for (i = 0; i < dcfg->desc_list->nelts; ++i) { | ||
| 827 | aaron | 92624 | ai_desc_t *tuple = &list[i]; |
| 828 | int found; | ||
| 829 | fielding | 83751 | |
| 830 | aaron | 92624 | /* |
| 831 | * Only use the full-path filename if the pattern contains '/'s. | ||
| 832 | */ | ||
| 833 | filename = (tuple->full_path) ? filename_full : filename_only; | ||
| 834 | /* | ||
| 835 | * Make the comparison using the cheapest method; only do | ||
| 836 | * wildcard checking if we must. | ||
| 837 | */ | ||
| 838 | if (tuple->wildcards) { | ||
| 839 | found = (apr_fnmatch(tuple->pattern, filename, MATCH_FLAGS) == 0); | ||
| 840 | } | ||
| 841 | else { | ||
| 842 | found = (ap_strstr_c(filename, tuple->pattern) != NULL); | ||
| 843 | } | ||
| 844 | if (found) { | ||
| 845 | return tuple->description; | ||
| 846 | } | ||
| 847 | fielding | 83751 | } |
| 848 | return NULL; | ||
| 849 | } | ||
| 850 | |||
| 851 | static int ignore_entry(autoindex_config_rec *d, char *path) | ||
| 852 | { | ||
| 853 | dougm | 85976 | apr_array_header_t *list = d->ign_list; |
| 854 | fielding | 83751 | struct item *items = (struct item *) list->elts; |
| 855 | char *tt; | ||
| 856 | int i; | ||
| 857 | |||
| 858 | if ((tt = strrchr(path, '/')) == NULL) { | ||
| 859 | aaron | 92624 | tt = path; |
| 860 | fielding | 83751 | } |
| 861 | else { | ||
| 862 | aaron | 92624 | tt++; |
| 863 | fielding | 83751 | } |
| 864 | |||
| 865 | for (i = 0; i < list->nelts; ++i) { | ||
| 866 | aaron | 92624 | struct item *p = &items[i]; |
| 867 | char *ap; | ||
| 868 | fielding | 83751 | |
| 869 | aaron | 92624 | if ((ap = strrchr(p->apply_to, '/')) == NULL) { |
| 870 | ap = p->apply_to; | ||
| 871 | } | ||
| 872 | else { | ||
| 873 | ap++; | ||
| 874 | } | ||
| 875 | fielding | 83751 | |
| 876 | #ifndef CASE_BLIND_FILESYSTEM | ||
| 877 | aaron | 92624 | if (!ap_strcmp_match(path, p->apply_path) |
| 878 | && !ap_strcmp_match(tt, ap)) { | ||
| 879 | return 1; | ||
| 880 | } | ||
| 881 | fielding | 83751 | #else /* !CASE_BLIND_FILESYSTEM */ |
| 882 | aaron | 92624 | /* |
| 883 | * On some platforms, the match must be case-blind. This is really | ||
| 884 | * a factor of the filesystem involved, but we can't detect that | ||
| 885 | * reliably - so we have to granularise at the OS level. | ||
| 886 | */ | ||
| 887 | if (!ap_strcasecmp_match(path, p->apply_path) | ||
| 888 | && !ap_strcasecmp_match(tt, ap)) { | ||
| 889 | return 1; | ||
| 890 | } | ||
| 891 | fielding | 83751 | #endif /* !CASE_BLIND_FILESYSTEM */ |
| 892 | } | ||
| 893 | return 0; | ||
| 894 | } | ||
| 895 | |||
| 896 | /***************************************************************** | ||
| 897 | * | ||
| 898 | * Actually generating output | ||
| 899 | */ | ||
| 900 | |||
| 901 | /* | ||
| 902 | * Elements of the emitted document: | ||
| 903 | aaron | 92624 | * Preamble |
| 904 | * Emitted unless SUPPRESS_PREAMBLE is set AND ap_run_sub_req | ||
| 905 | * succeeds for the (content_type == text/html) header file. | ||
| 906 | * Header file | ||
| 907 | * Emitted if found (and able). | ||
| 908 | * H1 tag line | ||
| 909 | * Emitted if a header file is NOT emitted. | ||
| 910 | * Directory stuff | ||
| 911 | * Always emitted. | ||
| 912 | * HR | ||
| 913 | * Emitted if FANCY_INDEXING is set. | ||
| 914 | * Readme file | ||
| 915 | * Emitted if found (and able). | ||
| 916 | * ServerSig | ||
| 917 | * Emitted if ServerSignature is not Off AND a readme file | ||
| 918 | * is NOT emitted. | ||
| 919 | * Postamble | ||
| 920 | * Emitted unless SUPPRESS_PREAMBLE is set AND ap_run_sub_req | ||
| 921 | * succeeds for the (content_type == text/html) readme file. | ||
| 922 | fielding | 83751 | */ |
| 923 | |||
| 924 | |||
| 925 | /* | ||
| 926 | * emit a plain text file | ||
| 927 | */ | ||
| 928 | dougm | 85976 | static void do_emit_plain(request_rec *r, apr_file_t *f) |
| 929 | fielding | 83751 | { |
| 930 | rbb | 88282 | char buf[AP_IOBUFSIZE + 1]; |
| 931 | wrowe | 89715 | int ch; |
| 932 | apr_size_t i, c, n; | ||
| 933 | aaron | 92625 | apr_status_t rv; |
| 934 | fielding | 83751 | |
| 935 | wrowe | 89751 | ap_rputs("<pre>\n", r); |
| 936 | dougm | 88019 | while (!apr_file_eof(f)) { |
| 937 | aaron | 92624 | do { |
| 938 | rbb | 88282 | n = sizeof(char) * AP_IOBUFSIZE; |
| 939 | aaron | 92625 | rv = apr_file_read(f, buf, &n); |
| 940 | } while (APR_STATUS_IS_EINTR(rv)); | ||
| 941 | if (n == 0 || rv != APR_SUCCESS) { | ||
| 942 | wrowe | 89715 | /* ###: better error here? */ |
| 943 | aaron | 92624 | break; |
| 944 | } | ||
| 945 | buf[n] = '\0'; | ||
| 946 | c = 0; | ||
| 947 | while (c < n) { | ||
| 948 | for (i = c; i < n; i++) { | ||
| 949 | if (buf[i] == '<' || buf[i] == '>' || buf[i] == '&') { | ||
| 950 | break; | ||
| 951 | } | ||
| 952 | } | ||
| 953 | ch = buf[i]; | ||
| 954 | buf[i] = '\0'; | ||
| 955 | ap_rputs(&buf[c], r); | ||
| 956 | if (ch == '<') { | ||
| 957 | ap_rputs("<", r); | ||
| 958 | } | ||
| 959 | else if (ch == '>') { | ||
| 960 | ap_rputs(">", r); | ||
| 961 | } | ||
| 962 | else if (ch == '&') { | ||
| 963 | ap_rputs("&", r); | ||
| 964 | } | ||
| 965 | c = i + 1; | ||
| 966 | } | ||
| 967 | fielding | 83751 | } |
| 968 | wrowe | 89751 | ap_rputs("</pre>\n", r); |
| 969 | fielding | 83751 | } |
| 970 | |||
| 971 | /* | ||
| 972 | * Handle the preamble through the H1 tag line, inclusive. Locate | ||
| 973 | * the file with a subrequests. Process text/html documents by actually | ||
| 974 | * running the subrequest; text/xxx documents get copied verbatim, | ||
| 975 | * and any other content type is ignored. This means that a non-text | ||
| 976 | * document (such as HEADER.gif) might get multiviewed as the result | ||
| 977 | * instead of a text document, meaning nothing will be displayed, but | ||
| 978 | * oh well. | ||
| 979 | */ | ||
| 980 | static void emit_head(request_rec *r, char *header_fname, int suppress_amble, | ||
| 981 | nd | 101683 | int emit_xhtml, char *title) |
| 982 | fielding | 83751 | { |
| 983 | wrowe | 91609 | apr_table_t *hdrs = r->headers_in; |
| 984 | dougm | 85976 | apr_file_t *f = NULL; |
| 985 | fielding | 83751 | request_rec *rr = NULL; |
| 986 | int emit_amble = 1; | ||
| 987 | int emit_H1 = 1; | ||
| 988 | wrowe | 91609 | const char *r_accept; |
| 989 | const char *r_accept_enc; | ||
| 990 | fielding | 83751 | |
| 991 | /* | ||
| 992 | * If there's a header file, send a subrequest to look for it. If it's | ||
| 993 | wrowe | 91609 | * found and html do the subrequest, otherwise handle it |
| 994 | fielding | 83751 | */ |
| 995 | wrowe | 91609 | r_accept = apr_table_get(hdrs, "Accept"); |
| 996 | r_accept_enc = apr_table_get(hdrs, "Accept-Encoding"); | ||
| 997 | apr_table_setn(hdrs, "Accept", "text/html, text/plain"); | ||
| 998 | apr_table_unset(hdrs, "Accept-Encoding"); | ||
| 999 | |||
| 1000 | rbb | 94457 | |
| 1001 | wrowe | 91609 | if ((header_fname != NULL) && r->args) { |
| 1002 | header_fname = apr_pstrcat(r->pool, header_fname, "?", r->args, NULL); | ||
| 1003 | } | ||
| 1004 | |||
| 1005 | fielding | 83751 | if ((header_fname != NULL) |
| 1006 | wrowe | 93663 | && (rr = ap_sub_req_lookup_uri(header_fname, r, r->output_filters)) |
| 1007 | aaron | 92624 | && (rr->status == HTTP_OK) |
| 1008 | && (rr->filename != NULL) | ||
| 1009 | && (rr->finfo.filetype == APR_REG)) { | ||
| 1010 | /* | ||
| 1011 | * Check for the two specific cases we allow: text/html and | ||
| 1012 | * text/anything-else. The former is allowed to be processed for | ||
| 1013 | * SSIs. | ||
| 1014 | */ | ||
| 1015 | if (rr->content_type != NULL) { | ||
| 1016 | if (!strcasecmp(ap_field_noparam(r->pool, rr->content_type), | ||
| 1017 | "text/html")) { | ||
| 1018 | rbb | 94457 | ap_filter_t *f; |
| 1019 | /* Hope everything will work... */ | ||
| 1020 | aaron | 92624 | emit_amble = 0; |
| 1021 | emit_H1 = 0; | ||
| 1022 | fielding | 83751 | |
| 1023 | aaron | 92624 | if (! suppress_amble) { |
| 1024 | nd | 101683 | emit_preamble(r, emit_xhtml, title); |
| 1025 | aaron | 92624 | } |
| 1026 | rbb | 94457 | /* This is a hack, but I can't find any better way to do this. |
| 1027 | * The problem is that we have already created the sub-request, | ||
| 1028 | nd | 98880 | * but we just inserted the OLD_WRITE filter, and the |
| 1029 | rbb | 94457 | * sub-request needs to pass its data through the OLD_WRITE |
| 1030 | * filter, or things go horribly wrong (missing data, data in | ||
| 1031 | nd | 98880 | * the wrong order, etc). To fix it, if you create a |
| 1032 | rbb | 94457 | * sub-request and then insert the OLD_WRITE filter before you |
| 1033 | * run the request, you need to make sure that the sub-request | ||
| 1034 | nd | 98880 | * data goes through the OLD_WRITE filter. Just steal this |
| 1035 | rbb | 94457 | * code. The long-term solution is to remove the ap_r* |
| 1036 | * functions. | ||
| 1037 | */ | ||
| 1038 | nd | 98880 | for (f=rr->output_filters; |
| 1039 | rbb | 94457 | f->frec != ap_subreq_core_filter_handle; f = f->next); |
| 1040 | nd | 98880 | f->next = r->output_filters; |
| 1041 | rbb | 94457 | |
| 1042 | aaron | 92624 | /* |
| 1043 | * If there's a problem running the subrequest, display the | ||
| 1044 | * preamble if we didn't do it before -- the header file | ||
| 1045 | * didn't get displayed. | ||
| 1046 | */ | ||
| 1047 | if (ap_run_sub_req(rr) != OK) { | ||
| 1048 | /* It didn't work */ | ||
| 1049 | emit_amble = suppress_amble; | ||
| 1050 | emit_H1 = 1; | ||
| 1051 | } | ||
| 1052 | } | ||
| 1053 | else if (!strncasecmp("text/", rr->content_type, 5)) { | ||
| 1054 | /* | ||
| 1055 | * If we can open the file, prefix it with the preamble | ||
| 1056 | * regardless; since we'll be sending a <pre> block around | ||
| 1057 | * the file's contents, any HTML header it had won't end up | ||
| 1058 | * where it belongs. | ||
| 1059 | */ | ||
| 1060 | if (apr_file_open(&f, rr->filename, APR_READ, | ||
| 1061 | APR_OS_DEFAULT, r->pool) == APR_SUCCESS) { | ||
| 1062 | nd | 101683 | emit_preamble(r, emit_xhtml, title); |
| 1063 | aaron | 92624 | emit_amble = 0; |
| 1064 | do_emit_plain(r, f); | ||
| 1065 | apr_file_close(f); | ||
| 1066 | emit_H1 = 0; | ||
| 1067 | } | ||
| 1068 | } | ||
| 1069 | } | ||
| 1070 | fielding | 83751 | } |
| 1071 | |||
| 1072 | coar | 91660 | if (r_accept) { |
| 1073 | wrowe | 91609 | apr_table_setn(hdrs, "Accept", r_accept); |
| 1074 | coar | 91660 | } |
| 1075 | else { | ||
| 1076 | wrowe | 91609 | apr_table_unset(hdrs, "Accept"); |
| 1077 | coar | 91660 | } |
| 1078 | wrowe | 91609 | |
| 1079 | coar | 91660 | if (r_accept_enc) { |
| 1080 | wrowe | 91609 | apr_table_setn(hdrs, "Accept-Encoding", r_accept_enc); |
| 1081 | coar | 91660 | } |
| 1082 | wrowe | 91609 | |
| 1083 | fielding | 83751 | if (emit_amble) { |
| 1084 | nd | 101683 | emit_preamble(r, emit_xhtml, title); |
| 1085 | fielding | 83751 | } |
| 1086 | if (emit_H1) { | ||
| 1087 | aaron | 92624 | ap_rvputs(r, "<h1>Index of ", title, "</h1>\n", NULL); |
| 1088 | fielding | 83751 | } |
| 1089 | if (rr != NULL) { | ||
| 1090 | aaron | 92624 | ap_destroy_sub_req(rr); |
| 1091 | fielding | 83751 | } |
| 1092 | } | ||
| 1093 | |||
| 1094 | |||
| 1095 | /* | ||
| 1096 | * Handle the Readme file through the postamble, inclusive. Locate | ||
| 1097 | * the file with a subrequests. Process text/html documents by actually | ||
| 1098 | * running the subrequest; text/xxx documents get copied verbatim, | ||
| 1099 | * and any other content type is ignored. This means that a non-text | ||
| 1100 | * document (such as FOOTER.gif) might get multiviewed as the result | ||
| 1101 | * instead of a text document, meaning nothing will be displayed, but | ||
| 1102 | * oh well. | ||
| 1103 | */ | ||
| 1104 | static void emit_tail(request_rec *r, char *readme_fname, int suppress_amble) | ||
| 1105 | { | ||
| 1106 | dougm | 85976 | apr_file_t *f = NULL; |
| 1107 | fielding | 83751 | request_rec *rr = NULL; |
| 1108 | int suppress_post = 0; | ||
| 1109 | int suppress_sig = 0; | ||
| 1110 | |||
| 1111 | /* | ||
| 1112 | * If there's a readme file, send a subrequest to look for it. If it's | ||
| 1113 | * found and a text file, handle it -- otherwise fall through and | ||
| 1114 | * pretend there's nothing there. | ||
| 1115 | */ | ||
| 1116 | if ((readme_fname != NULL) | ||
| 1117 | wrowe | 93663 | && (rr = ap_sub_req_lookup_uri(readme_fname, r, r->output_filters)) |
| 1118 | aaron | 92624 | && (rr->status == HTTP_OK) |
| 1119 | && (rr->filename != NULL) | ||
| 1120 | && rr->finfo.filetype == APR_REG) { | ||
| 1121 | /* | ||
| 1122 | * Check for the two specific cases we allow: text/html and | ||
| 1123 | * text/anything-else. The former is allowed to be processed for | ||
| 1124 | * SSIs. | ||
| 1125 | */ | ||
| 1126 | if (rr->content_type != NULL) { | ||
| 1127 | if (!strcasecmp(ap_field_noparam(r->pool, rr->content_type), | ||
| 1128 | "text/html")) { | ||
| 1129 | rbb | 94457 | ap_filter_t *f; |
| 1130 | nd | 98880 | for (f=rr->output_filters; |
| 1131 | rbb | 94457 | f->frec != ap_subreq_core_filter_handle; f = f->next); |
| 1132 | nd | 98880 | f->next = r->output_filters; |
| 1133 | rbb | 94457 | |
| 1134 | |||
| 1135 | aaron | 92624 | if (ap_run_sub_req(rr) == OK) { |
| 1136 | /* worked... */ | ||
| 1137 | suppress_sig = 1; | ||
| 1138 | suppress_post = suppress_amble; | ||
| 1139 | } | ||
| 1140 | } | ||
| 1141 | else if (!strncasecmp("text/", rr->content_type, 5)) { | ||
| 1142 | /* | ||
| 1143 | * If we can open the file, suppress the signature. | ||
| 1144 | */ | ||
| 1145 | if (apr_file_open(&f, rr->filename, APR_READ, | ||
| 1146 | APR_OS_DEFAULT, r->pool) == APR_SUCCESS) { | ||
| 1147 | do_emit_plain(r, f); | ||
| 1148 | apr_file_close(f); | ||
| 1149 | suppress_sig = 1; | ||
| 1150 | } | ||
| 1151 | } | ||
| 1152 | } | ||
| 1153 | fielding | 83751 | } |
| 1154 | nd | 98880 | |
| 1155 | fielding | 83751 | if (!suppress_sig) { |
| 1156 | aaron | 92624 | ap_rputs(ap_psignature("", r), r); |
| 1157 | fielding | 83751 | } |
| 1158 | if (!suppress_post) { | ||
| 1159 | aaron | 92624 | ap_rputs("</body></html>\n", r); |
| 1160 | fielding | 83751 | } |
| 1161 | if (rr != NULL) { | ||
| 1162 | aaron | 92624 | ap_destroy_sub_req(rr); |
| 1163 | fielding | 83751 | } |
| 1164 | } | ||
| 1165 | |||
| 1166 | |||
| 1167 | static char *find_title(request_rec *r) | ||
| 1168 | { | ||
| 1169 | wrowe | 89751 | char titlebuf[MAX_STRING_LEN], *find = "<title>"; |
| 1170 | dougm | 85976 | apr_file_t *thefile = NULL; |
| 1171 | ben | 84032 | int x, y, p; |
| 1172 | rbb | 86915 | apr_size_t n; |
| 1173 | fielding | 83751 | |
| 1174 | if (r->status != HTTP_OK) { | ||
| 1175 | aaron | 92624 | return NULL; |
| 1176 | fielding | 83751 | } |
| 1177 | if ((r->content_type != NULL) | ||
| 1178 | aaron | 92624 | && (!strcasecmp(ap_field_noparam(r->pool, r->content_type), |
| 1179 | "text/html") | ||
| 1180 | || !strcmp(r->content_type, INCLUDES_MAGIC_TYPE)) | ||
| 1181 | && !r->content_encoding) { | ||
| 1182 | dougm | 88019 | if (apr_file_open(&thefile, r->filename, APR_READ, |
| 1183 | aaron | 92624 | APR_OS_DEFAULT, r->pool) != APR_SUCCESS) { |
| 1184 | return NULL; | ||
| 1185 | } | ||
| 1186 | rbb | 83879 | n = sizeof(char) * (MAX_STRING_LEN - 1); |
| 1187 | aaron | 92624 | apr_file_read(thefile, titlebuf, &n); |
| 1188 | if (n <= 0) { | ||
| 1189 | apr_file_close(thefile); | ||
| 1190 | return NULL; | ||
| 1191 | } | ||
| 1192 | titlebuf[n] = '\0'; | ||
| 1193 | for (x = 0, p = 0; titlebuf[x]; x++) { | ||
| 1194 | if (apr_tolower(titlebuf[x]) == find[p]) { | ||
| 1195 | if (!find[++p]) { | ||
| 1196 | if ((p = ap_ind(&titlebuf[++x], '<')) != -1) { | ||
| 1197 | titlebuf[x + p] = '\0'; | ||
| 1198 | } | ||
| 1199 | /* Scan for line breaks for Tanmoy's secretary */ | ||
| 1200 | for (y = x; titlebuf[y]; y++) { | ||
| 1201 | if ((titlebuf[y] == CR) || (titlebuf[y] == LF)) { | ||
| 1202 | if (y == x) { | ||
| 1203 | x++; | ||
| 1204 | } | ||
| 1205 | else { | ||
| 1206 | titlebuf[y] = ' '; | ||
| 1207 | } | ||
| 1208 | } | ||
| 1209 | } | ||
| 1210 | apr_file_close(thefile); | ||
| 1211 | return apr_pstrdup(r->pool, &titlebuf[x]); | ||
| 1212 | } | ||
| 1213 | } | ||
| 1214 | else { | ||
| 1215 | p = 0; | ||
| 1216 | } | ||
| 1217 | } | ||
| 1218 | apr_file_close(thefile); | ||
| 1219 | fielding | 83751 | } |
| 1220 | return NULL; | ||
| 1221 | } | ||
| 1222 | |||
| 1223 | wrowe | 89981 | static struct ent *make_parent_entry(apr_int32_t autoindex_opts, |
| 1224 | aaron | 92624 | autoindex_config_rec *d, |
| 1225 | nd | 98880 | request_rec *r, char keyid, |
| 1226 | wrowe | 89909 | char direction) |
| 1227 | { | ||
| 1228 | struct ent *p = (struct ent *) apr_pcalloc(r->pool, sizeof(struct ent)); | ||
| 1229 | char *testpath; | ||
| 1230 | /* | ||
| 1231 | * p->name is now the true parent URI. | ||
| 1232 | * testpath is a crafted lie, so that the syntax '/some/..' | ||
| 1233 | * (or simply '..')be used to describe 'up' from '/some/' | ||
| 1234 | * when processeing IndexIgnore, and Icon|Alt|Desc configs. | ||
| 1235 | */ | ||
| 1236 | |||
| 1237 | /* The output has always been to the parent. Don't make ourself | ||
| 1238 | * our own parent (worthless cyclical reference). | ||
| 1239 | */ | ||
| 1240 | coar | 91660 | if (!(p->name = ap_make_full_path(r->pool, r->uri, "../"))) { |
| 1241 | wrowe | 89909 | return (NULL); |
| 1242 | coar | 91660 | } |
| 1243 | wrowe | 89909 | ap_getparents(p->name); |
| 1244 | coar | 91660 | if (!*p->name) { |
| 1245 | wrowe | 89909 | return (NULL); |
| 1246 | coar | 91660 | } |
| 1247 | wrowe | 89909 | |
| 1248 | /* IndexIgnore has always compared "/thispath/.." */ | ||
| 1249 | testpath = ap_make_full_path(r->pool, r->filename, ".."); | ||
| 1250 | coar | 91660 | if (ignore_entry(d, testpath)) { |
| 1251 | wrowe | 89909 | return (NULL); |
| 1252 | coar | 91660 | } |
| 1253 | wrowe | 89909 | |
| 1254 | p->size = -1; | ||
| 1255 | p->lm = -1; | ||
| 1256 | p->key = apr_toupper(keyid); | ||
| 1257 | p->ascending = (apr_toupper(direction) == D_ASCENDING); | ||
| 1258 | p->version_sort = autoindex_opts & VERSION_SORT; | ||
| 1259 | if (autoindex_opts & FANCY_INDEXING) { | ||
| 1260 | coar | 91660 | if (!(p->icon = find_default_icon(d, testpath))) { |
| 1261 | aaron | 92624 | p->icon = find_default_icon(d, "^^DIRECTORY^^"); |
| 1262 | coar | 91660 | } |
| 1263 | if (!(p->alt = find_default_alt(d, testpath))) { | ||
| 1264 | if (!(p->alt = find_default_alt(d, "^^DIRECTORY^^"))) { | ||
| 1265 | aaron | 92624 | p->alt = "DIR"; |
| 1266 | coar | 91660 | } |
| 1267 | } | ||
| 1268 | wrowe | 89909 | p->desc = find_desc(d, testpath); |
| 1269 | } | ||
| 1270 | return p; | ||
| 1271 | } | ||
| 1272 | |||
| 1273 | nd | 98880 | static struct ent *make_autoindex_entry(const apr_finfo_t *dirent, |
| 1274 | wrowe | 89448 | int autoindex_opts, |
| 1275 | aaron | 92624 | autoindex_config_rec *d, |
| 1276 | request_rec *r, char keyid, | ||
| 1277 | char direction, | ||
| 1278 | wrowe | 89981 | const char *pattern) |
| 1279 | fielding | 83751 | { |
| 1280 | wrowe | 89899 | request_rec *rr; |
| 1281 | fielding | 83751 | struct ent *p; |
| 1282 | pquerna | 104212 | int show_forbidden = 0; |
| 1283 | fielding | 83751 | |
| 1284 | wrowe | 89909 | /* Dot is ignored, Parent is handled by make_parent_entry() */ |
| 1285 | if ((dirent->name[0] == '.') && (!dirent->name[1] | ||
| 1286 | trawick | 89911 | || ((dirent->name[1] == '.') && !dirent->name[2]))) |
| 1287 | aaron | 92624 | return (NULL); |
| 1288 | fielding | 83751 | |
| 1289 | nd | 101657 | /* |
| 1290 | * On some platforms, the match must be case-blind. This is really | ||
| 1291 | * a factor of the filesystem involved, but we can't detect that | ||
| 1292 | * reliably - so we have to granularise at the OS level. | ||
| 1293 | */ | ||
| 1294 | nd | 98880 | if (pattern && (apr_fnmatch(pattern, dirent->name, |
| 1295 | nd | 101657 | APR_FNM_NOESCAPE | APR_FNM_PERIOD |
| 1296 | #ifdef CASE_BLIND_FILESYSTEM | ||
| 1297 | | APR_FNM_CASE_BLIND | ||
| 1298 | #endif | ||
| 1299 | ) | ||
| 1300 | != APR_SUCCESS)) { | ||
| 1301 | wrowe | 89981 | return (NULL); |
| 1302 | nd | 101657 | } |
| 1303 | wrowe | 89981 | |
| 1304 | coar | 91660 | if (ignore_entry(d, ap_make_full_path(r->pool, |
| 1305 | r->filename, dirent->name))) { | ||
| 1306 | fielding | 83751 | return (NULL); |
| 1307 | coar | 91660 | } |
| 1308 | fielding | 83751 | |
| 1309 | wrowe | 93045 | if (!(rr = ap_sub_req_lookup_dirent(dirent, r, AP_SUBREQ_NO_ARGS, NULL))) { |
| 1310 | wrowe | 89899 | return (NULL); |
| 1311 | wrowe | 89981 | } |
| 1312 | wrowe | 89899 | |
| 1313 | jim | 332306 | if((autoindex_opts & SHOW_FORBIDDEN) |
| 1314 | pquerna | 104212 | && (rr->status == HTTP_UNAUTHORIZED || rr->status == HTTP_FORBIDDEN)) { |
| 1315 | show_forbidden = 1; | ||
| 1316 | } | ||
| 1317 | |||
| 1318 | wrowe | 89899 | if ((rr->finfo.filetype != APR_DIR && rr->finfo.filetype != APR_REG) |
| 1319 | wrowe | 89909 | || !(rr->status == OK || ap_is_HTTP_SUCCESS(rr->status) |
| 1320 | pquerna | 104212 | || ap_is_HTTP_REDIRECT(rr->status) |
| 1321 | || show_forbidden == 1)) { | ||
| 1322 | wrowe | 89899 | ap_destroy_sub_req(rr); |
| 1323 | return (NULL); | ||
| 1324 | } | ||
| 1325 | |||
| 1326 | dougm | 85976 | p = (struct ent *) apr_pcalloc(r->pool, sizeof(struct ent)); |
| 1327 | jerenkrantz | 93329 | if (dirent->filetype == APR_DIR) { |
| 1328 | wrowe | 90364 | p->name = apr_pstrcat(r->pool, dirent->name, "/", NULL); |
| 1329 | coar | 91660 | } |
| 1330 | else { | ||
| 1331 | wrowe | 90364 | p->name = apr_pstrdup(r->pool, dirent->name); |
| 1332 | coar | 91660 | } |
| 1333 | fielding | 83751 | p->size = -1; |
| 1334 | p->icon = NULL; | ||
| 1335 | p->alt = NULL; | ||
| 1336 | p->desc = NULL; | ||
| 1337 | p->lm = -1; | ||
| 1338 | wrowe | 89979 | p->isdir = 0; |
| 1339 | wrowe | 86008 | p->key = apr_toupper(keyid); |
| 1340 | p->ascending = (apr_toupper(direction) == D_ASCENDING); | ||
| 1341 | wrowe | 89981 | p->version_sort = !!(autoindex_opts & VERSION_SORT); |
| 1342 | wrowe | 98181 | p->ignore_case = !!(autoindex_opts & IGNORE_CASE); |
| 1343 | wrowe | 89979 | |
| 1344 | coar | 91660 | if (autoindex_opts & (FANCY_INDEXING | TABLE_INDEXING)) { |
| 1345 | aaron | 92624 | p->lm = rr->finfo.mtime; |
| 1346 | jerenkrantz | 93329 | if (dirent->filetype == APR_DIR) { |
| 1347 | coar | 91660 | if (autoindex_opts & FOLDERS_FIRST) { |
| 1348 | wrowe | 89979 | p->isdir = 1; |
| 1349 | coar | 91660 | } |
| 1350 | nd | 100628 | rr->filename = ap_make_dirstr_parent (rr->pool, rr->filename); |
| 1351 | nd | 101693 | |
| 1352 | /* omit the trailing slash (1.3 compat) */ | ||
| 1353 | rr->filename[strlen(rr->filename) - 1] = '\0'; | ||
| 1354 | |||
| 1355 | aaron | 92624 | if (!(p->icon = find_icon(d, rr, 1))) { |
| 1356 | p->icon = find_default_icon(d, "^^DIRECTORY^^"); | ||
| 1357 | } | ||
| 1358 | if (!(p->alt = find_alt(d, rr, 1))) { | ||
| 1359 | if (!(p->alt = find_default_alt(d, "^^DIRECTORY^^"))) { | ||
| 1360 | p->alt = "DIR"; | ||
| 1361 | coar | 91660 | } |
| 1362 | aaron | 92624 | } |
| 1363 | } | ||
| 1364 | else { | ||
| 1365 | p->icon = find_icon(d, rr, 0); | ||
| 1366 | p->alt = find_alt(d, rr, 0); | ||
| 1367 | p->size = rr->finfo.size; | ||
| 1368 | } | ||
| 1369 | fielding | 83751 | |
| 1370 | aaron | 92624 | p->desc = find_desc(d, rr->filename); |
| 1371 | fielding | 83751 | |
| 1372 | aaron | 92624 | if ((!p->desc) && (autoindex_opts & SCAN_HTML_TITLES)) { |
| 1373 | p->desc = apr_pstrdup(r->pool, find_title(rr)); | ||
| 1374 | } | ||
| 1375 | fielding | 83751 | } |
| 1376 | wrowe | 89899 | ap_destroy_sub_req(rr); |
| 1377 | fielding | 83751 | /* |
| 1378 | wrowe | 89899 | * We don't need to take any special action for the file size key. |
| 1379 | * If we did, it would go here. | ||
| 1380 | fielding | 83751 | */ |
| 1381 | if (keyid == K_LAST_MOD) { | ||
| 1382 | if (p->lm < 0) { | ||
| 1383 | aaron | 92624 | p->lm = 0; |
| 1384 | } | ||
| 1385 | fielding | 83751 | } |
| 1386 | return (p); | ||
| 1387 | } | ||
| 1388 | |||
| 1389 | static char *terminate_description(autoindex_config_rec *d, char *desc, | ||
| 1390 | aaron | 92624 | apr_int32_t autoindex_opts, int desc_width) |
| 1391 | fielding | 83751 | { |
| 1392 | wrowe | 89979 | int maxsize = desc_width; |
| 1393 | fielding | 83751 | register int x; |
| 1394 | |||
| 1395 | nd | 98880 | /* |
| 1396 | wrowe | 89979 | * If there's no DescriptionWidth in effect, default to the old |
| 1397 | nd | 98880 | * behaviour of adjusting the description size depending upon |
| 1398 | * what else is being displayed. Otherwise, stick with the | ||
| 1399 | * setting. | ||
| 1400 | */ | ||
| 1401 | if (d->desc_adjust == K_UNSET) { | ||
| 1402 | wrowe | 89979 | if (autoindex_opts & SUPPRESS_ICON) { |
| 1403 | aaron | 92624 | maxsize += 6; |
| 1404 | wrowe | 89979 | } |
| 1405 | if (autoindex_opts & SUPPRESS_LAST_MOD) { | ||
| 1406 | aaron | 92624 | maxsize += 19; |
| 1407 | wrowe | 89979 | } |
| 1408 | if (autoindex_opts & SUPPRESS_SIZE) { | ||
| 1409 | aaron | 92624 | maxsize += 7; |
| 1410 | wrowe | 89979 | } |
| 1411 | fielding | 83751 | } |
| 1412 | wrowe | 89979 | for (x = 0; desc[x] && ((maxsize > 0) || (desc[x] == '<')); x++) { |
| 1413 | aaron | 92624 | if (desc[x] == '<') { |
| 1414 | while (desc[x] != '>') { | ||
| 1415 | if (!desc[x]) { | ||
| 1416 | maxsize = 0; | ||
| 1417 | break; | ||
| 1418 | } | ||
| 1419 | ++x; | ||
| 1420 | } | ||
| 1421 | } | ||
| 1422 | else if (desc[x] == '&') { | ||
| 1423 | /* entities like ä count as one character */ | ||
| 1424 | --maxsize; | ||
| 1425 | for ( ; desc[x] != ';'; ++x) { | ||
| 1426 | if (desc[x] == '\0') { | ||
| 1427 | fielding | 83751 | maxsize = 0; |
| 1428 | break; | ||
| 1429 | aaron | 92624 | } |
| 1430 | } | ||
| 1431 | fielding | 83751 | } |
| 1432 | aaron | 92624 | else { |
| 1433 | --maxsize; | ||
| 1434 | } | ||
| 1435 | fielding | 83751 | } |
| 1436 | if (!maxsize && desc[x] != '\0') { | ||
| 1437 | aaron | 92624 | desc[x - 1] = '>'; /* Grump. */ |
| 1438 | desc[x] = '\0'; /* Double Grump! */ | ||
| 1439 | fielding | 83751 | } |
| 1440 | return desc; | ||
| 1441 | } | ||
| 1442 | |||
| 1443 | /* | ||
| 1444 | * Emit the anchor for the specified field. If a field is the key for the | ||
| 1445 | * current request, the link changes its meaning to reverse the order when | ||
| 1446 | * selected again. Non-active fields always start in ascending order. | ||
| 1447 | */ | ||
| 1448 | nd | 98880 | static void emit_link(request_rec *r, const char *anchor, char column, |
| 1449 | char curkey, char curdirection, | ||
| 1450 | wrowe | 89981 | const char *colargs, int nosort) |
| 1451 | fielding | 83751 | { |
| 1452 | nd | 98883 | if (!nosort) { |
| 1453 | char qvalue[9]; | ||
| 1454 | fielding | 83751 | |
| 1455 | aaron | 92624 | qvalue[0] = '?'; |
| 1456 | qvalue[1] = 'C'; | ||
| 1457 | qvalue[2] = '='; | ||
| 1458 | qvalue[3] = column; | ||
| 1459 | nd | 98883 | qvalue[4] = ';'; |
| 1460 | qvalue[5] = 'O'; | ||
| 1461 | qvalue[6] = '='; | ||
| 1462 | /* reverse? */ | ||
| 1463 | qvalue[7] = ((curkey == column) && (curdirection == D_ASCENDING)) | ||
| 1464 | ? D_DESCENDING : D_ASCENDING; | ||
| 1465 | qvalue[8] = '\0'; | ||
| 1466 | nd | 98880 | ap_rvputs(r, "<a href=\"", qvalue, colargs ? colargs : "", |
| 1467 | wrowe | 89981 | "\">", anchor, "</a>", NULL); |
| 1468 | fielding | 83751 | } |
| 1469 | else { | ||
| 1470 | ap_rputs(anchor, r); | ||
| 1471 | } | ||
| 1472 | } | ||
| 1473 | |||
| 1474 | static void output_directories(struct ent **ar, int n, | ||
| 1475 | aaron | 92624 | autoindex_config_rec *d, request_rec *r, |
| 1476 | nd | 98880 | apr_int32_t autoindex_opts, char keyid, |
| 1477 | wrowe | 89981 | char direction, const char *colargs) |
| 1478 | fielding | 83751 | { |
| 1479 | dreid | 84598 | int x; |
| 1480 | dougm | 85976 | apr_size_t rv; |
| 1481 | fielding | 83751 | char *name = r->uri; |
| 1482 | char *tp; | ||
| 1483 | wrowe | 89981 | int static_columns = !!(autoindex_opts & SUPPRESS_COLSORT); |
| 1484 | dougm | 85976 | apr_pool_t *scratch; |
| 1485 | fielding | 83751 | int name_width; |
| 1486 | wrowe | 89979 | int desc_width; |
| 1487 | fielding | 83751 | char *name_scratch; |
| 1488 | char *pad_scratch; | ||
| 1489 | wrowe | 89983 | char *breakrow = ""; |
| 1490 | fielding | 83751 | |
| 1491 | dougm | 88019 | apr_pool_create(&scratch, r->pool); |
| 1492 | fielding | 83751 | if (name[0] == '\0') { |
| 1493 | aaron | 92624 | name = "/"; |
| 1494 | fielding | 83751 | } |
| 1495 | |||
| 1496 | name_width = d->name_width; | ||
| 1497 | nd | 98880 | desc_width = d->desc_width; |
| 1498 | wrowe | 89979 | |
| 1499 | nd | 98880 | if ((autoindex_opts & (FANCY_INDEXING | TABLE_INDEXING)) |
| 1500 | wrowe | 89979 | == FANCY_INDEXING) { |
| 1501 | if (d->name_adjust == K_ADJUST) { | ||
| 1502 | aaron | 92624 | for (x = 0; x < n; x++) { |
| 1503 | int t = strlen(ar[x]->name); | ||
| 1504 | if (t > name_width) { | ||
| 1505 | name_width = t; | ||
| 1506 | } | ||
| 1507 | } | ||
| 1508 | wrowe | 89979 | } |
| 1509 | |||
| 1510 | nd | 98880 | if (d->desc_adjust == K_ADJUST) { |
| 1511 | coar | 91660 | for (x = 0; x < n; x++) { |
| 1512 | nd | 98880 | if (ar[x]->desc != NULL) { |
| 1513 | int t = strlen(ar[x]->desc); | ||
| 1514 | if (t > desc_width) { | ||
| 1515 | desc_width = t; | ||
| 1516 | } | ||
| 1517 | } | ||
| 1518 | } | ||
| 1519 | } | ||
| 1520 | fielding | 83751 | } |
| 1521 | dougm | 85976 | name_scratch = apr_palloc(r->pool, name_width + 1); |
| 1522 | pad_scratch = apr_palloc(r->pool, name_width + 1); | ||
| 1523 | fielding | 83751 | memset(pad_scratch, ' ', name_width); |
| 1524 | pad_scratch[name_width] = '\0'; | ||
| 1525 | |||
| 1526 | wrowe | 89979 | if (autoindex_opts & TABLE_INDEXING) { |
| 1527 | int cols = 1; | ||
| 1528 | aaron | 92624 | ap_rputs("<table><tr>", r); |
| 1529 | if (!(autoindex_opts & SUPPRESS_ICON)) { | ||
| 1530 | striker | 97158 | ap_rputs("<th>", r); |
| 1531 | aaron | 92624 | if ((tp = find_default_icon(d, "^^BLANKICON^^"))) { |
| 1532 | ap_rvputs(r, "<img src=\"", ap_escape_html(scratch, tp), | ||
| 1533 | "\" alt=\"[ICO]\"", NULL); | ||
| 1534 | if (d->icon_width) { | ||
| 1535 | ap_rprintf(r, " width=\"%d\"", d->icon_width); | ||
| 1536 | coar | 91660 | } |
| 1537 | if (d->icon_height) { | ||
| 1538 | aaron | 92624 | ap_rprintf(r, " height=\"%d\"", d->icon_height); |
| 1539 | coar | 91660 | } |
| 1540 | nd | 101683 | |
| 1541 | if (autoindex_opts & EMIT_XHTML) { | ||
| 1542 | ap_rputs(" /", r); | ||
| 1543 | } | ||
| 1544 | ap_rputs("></th>", r); | ||
| 1545 | wrowe | 89979 | } |
| 1546 | coar | 91660 | else { |
| 1547 | wrowe | 89979 | ap_rputs(" </th>", r); |
| 1548 | coar | 91660 | } |
| 1549 | nd | 98880 | |
| 1550 | wrowe | 89979 | ++cols; |
| 1551 | } | ||
| 1552 | ap_rputs("<th>", r); | ||
| 1553 | nd | 98880 | emit_link(r, "Name", K_NAME, keyid, direction, |
| 1554 | wrowe | 89981 | colargs, static_columns); |
| 1555 | aaron | 92624 | if (!(autoindex_opts & SUPPRESS_LAST_MOD)) { |
| 1556 | wrowe | 89979 | ap_rputs("</th><th>", r); |
| 1557 | nd | 98880 | emit_link(r, "Last modified", K_LAST_MOD, keyid, direction, |
| 1558 | wrowe | 89981 | colargs, static_columns); |
| 1559 | aaron | 92624 | ++cols; |
| 1560 | } | ||
| 1561 | if (!(autoindex_opts & SUPPRESS_SIZE)) { | ||
| 1562 | wrowe | 89979 | ap_rputs("</th><th>", r); |
| 1563 | nd | 98880 | emit_link(r, "Size", K_SIZE, keyid, direction, |
| 1564 | wrowe | 89981 | colargs, static_columns); |
| 1565 | aaron | 92624 | ++cols; |
| 1566 | } | ||
| 1567 | if (!(autoindex_opts & SUPPRESS_DESC)) { | ||
| 1568 | wrowe | 89979 | ap_rputs("</th><th>", r); |
| 1569 | nd | 98880 | emit_link(r, "Description", K_DESC, keyid, direction, |
| 1570 | wrowe | 89981 | colargs, static_columns); |
| 1571 | aaron | 92624 | ++cols; |
| 1572 | } | ||
| 1573 | coar | 91660 | if (!(autoindex_opts & SUPPRESS_RULES)) { |
| 1574 | breakrow = apr_psprintf(r->pool, | ||
| 1575 | "<tr><th colspan=\"%d\">" | ||
| 1576 | nd | 101683 | "<hr%s></th></tr>\n", cols, |
| 1577 | (autoindex_opts & EMIT_XHTML) ? " /" : ""); | ||
| 1578 | coar | 91660 | } |
| 1579 | aaron | 92624 | ap_rvputs(r, "</th></tr>", breakrow, NULL); |
| 1580 | wrowe | 89979 | } |
| 1581 | else if (autoindex_opts & FANCY_INDEXING) { | ||
| 1582 | aaron | 92624 | ap_rputs("<pre>", r); |
| 1583 | if (!(autoindex_opts & SUPPRESS_ICON)) { | ||
| 1584 | if ((tp = find_default_icon(d, "^^BLANKICON^^"))) { | ||
| 1585 | ap_rvputs(r, "<img src=\"", ap_escape_html(scratch, tp), | ||
| 1586 | "\" alt=\"Icon \"", NULL); | ||
| 1587 | if (d->icon_width) { | ||
| 1588 | ap_rprintf(r, " width=\"%d\"", d->icon_width); | ||
| 1589 | coar | 91660 | } |
| 1590 | if (d->icon_height) { | ||
| 1591 | aaron | 92624 | ap_rprintf(r, " height=\"%d\"", d->icon_height); |
| 1592 | coar | 91660 | } |
| 1593 | nd | 101683 | |
| 1594 | if (autoindex_opts & EMIT_XHTML) { | ||
| 1595 | ap_rputs(" /", r); | ||
| 1596 | } | ||
| 1597 | ap_rputs("> ", r); | ||
| 1598 | aaron | 92624 | } |
| 1599 | coar | 91660 | else { |
| 1600 | aaron | 92624 | ap_rputs(" ", r); |
| 1601 | coar | 91660 | } |
| 1602 | wrowe | 89979 | } |
| 1603 | nd | 98880 | emit_link(r, "Name", K_NAME, keyid, direction, |
| 1604 | wrowe | 89981 | colargs, static_columns); |
| 1605 | aaron | 92624 | ap_rputs(pad_scratch + 4, r); |
| 1606 | /* | ||
| 1607 | * Emit the guaranteed-at-least-one-space-between-columns byte. | ||
| 1608 | */ | ||
| 1609 | ap_rputs(" ", r); | ||
| 1610 | if (!(autoindex_opts & SUPPRESS_LAST_MOD)) { | ||
| 1611 | fielding | 83751 | emit_link(r, "Last modified", K_LAST_MOD, keyid, direction, |
| 1612 | wrowe | 89981 | colargs, static_columns); |
| 1613 | aaron | 92624 | ap_rputs(" ", r); |
| 1614 | } | ||
| 1615 | if (!(autoindex_opts & SUPPRESS_SIZE)) { | ||
| 1616 | nd | 98880 | emit_link(r, "Size", K_SIZE, keyid, direction, |
| 1617 | wrowe | 89981 | colargs, static_columns); |
| 1618 | aaron | 92624 | ap_rputs(" ", r); |
| 1619 | } | ||
| 1620 | if (!(autoindex_opts & SUPPRESS_DESC)) { | ||
| 1621 | fielding | 83751 | emit_link(r, "Description", K_DESC, keyid, direction, |
| 1622 | wrowe | 89981 | colargs, static_columns); |
| 1623 | aaron | 92624 | } |
| 1624 | if (!(autoindex_opts & SUPPRESS_RULES)) { | ||
| 1625 | nd | 101683 | ap_rputs("<hr", r); |
| 1626 | if (autoindex_opts & EMIT_XHTML) { | ||
| 1627 | ap_rputs(" /", r); | ||
| 1628 | } | ||
| 1629 | ap_rputs(">", r); | ||
| 1630 | coar | 91660 | } |
| 1631 | jwoolley | 94739 | else { |
| 1632 | ap_rputc('\n', r); | ||
| 1633 | } | ||
| 1634 | fielding | 83751 | } |
| 1635 | else { | ||
| 1636 | aaron | 92624 | ap_rputs("<ul>", r); |
| 1637 | fielding | 83751 | } |
| 1638 | |||
| 1639 | for (x = 0; x < n; x++) { | ||
| 1640 | aaron | 92624 | char *anchor, *t, *t2; |
| 1641 | int nwidth; | ||
| 1642 | fielding | 83751 | |
| 1643 | aaron | 92624 | apr_pool_clear(scratch); |
| 1644 | fielding | 83751 | |
| 1645 | wrowe | 89909 | t = ar[x]->name; |
| 1646 | aaron | 92624 | anchor = ap_escape_html(scratch, ap_os_escape_path(scratch, t, 0)); |
| 1647 | wrowe | 89909 | |
| 1648 | coar | 91660 | if (!x && t[0] == '/') { |
| 1649 | aaron | 92624 | t2 = "Parent Directory"; |
| 1650 | coar | 91660 | } |
| 1651 | else { | ||
| 1652 | aaron | 92624 | t2 = t; |
| 1653 | coar | 91660 | } |
| 1654 | fielding | 83751 | |
| 1655 | wrowe | 89979 | if (autoindex_opts & TABLE_INDEXING) { |
| 1656 | nd | 101661 | ap_rputs("<tr>", r); |
| 1657 | aaron | 92624 | if (!(autoindex_opts & SUPPRESS_ICON)) { |
| 1658 | nd | 101661 | ap_rputs("<td valign=\"top\">", r); |
| 1659 | wrowe | 89979 | if (autoindex_opts & ICONS_ARE_LINKS) { |
| 1660 | aaron | 92624 | ap_rvputs(r, "<a href=\"", anchor, "\">", NULL); |
| 1661 | } | ||
| 1662 | wrowe | 89979 | if ((ar[x]->icon) || d->default_icon) { |
| 1663 | aaron | 92624 | ap_rvputs(r, "<img src=\"", |
| 1664 | ap_escape_html(scratch, | ||
| 1665 | ar[x]->icon ? ar[x]->icon | ||
| 1666 | : d->default_icon), | ||
| 1667 | "\" alt=\"[", (ar[x]->alt ? ar[x]->alt : " "), | ||
| 1668 | "]\"", NULL); | ||
| 1669 | if (d->icon_width) { | ||
| 1670 | ap_rprintf(r, " width=\"%d\"", d->icon_width); | ||
| 1671 | coar | 91660 | } |
| 1672 | if (d->icon_height) { | ||
| 1673 | wrowe | 89979 | ap_rprintf(r, " height=\"%d\"", d->icon_height); |
| 1674 | coar | 91660 | } |
| 1675 | nd | 101683 | |
| 1676 | if (autoindex_opts & EMIT_XHTML) { | ||
| 1677 | ap_rputs(" /", r); | ||
| 1678 | } | ||
| 1679 | ap_rputs(">", r); | ||
| 1680 | aaron | 92624 | } |
| 1681 | coar | 91660 | else { |
| 1682 | nd | 98880 | ap_rputs(" ", r); |
| 1683 | coar | 91660 | } |
| 1684 | aaron | 92624 | if (autoindex_opts & ICONS_ARE_LINKS) { |
| 1685 | ap_rputs("</a></td>", r); | ||
| 1686 | coar | 91660 | } |
| 1687 | aaron | 92624 | else { |
| 1688 | wrowe | 89979 | ap_rputs("</td>", r); |
| 1689 | coar | 91660 | } |
| 1690 | wrowe | 89979 | } |
| 1691 | if (d->name_adjust == K_ADJUST) { | ||
| 1692 | aaron | 92624 | ap_rvputs(r, "<td><a href=\"", anchor, "\">", |
| 1693 | coar | 91660 | ap_escape_html(scratch, t2), "</a>", NULL); |
| 1694 | wrowe | 89979 | } |
| 1695 | else { | ||
| 1696 | aaron | 92624 | nwidth = strlen(t2); |
| 1697 | if (nwidth > name_width) { | ||
| 1698 | memcpy(name_scratch, t2, name_width - 3); | ||
| 1699 | name_scratch[name_width - 3] = '.'; | ||
| 1700 | name_scratch[name_width - 2] = '.'; | ||
| 1701 | name_scratch[name_width - 1] = '>'; | ||
| 1702 | name_scratch[name_width] = 0; | ||
| 1703 | t2 = name_scratch; | ||
| 1704 | nwidth = name_width; | ||
| 1705 | } | ||
| 1706 | ap_rvputs(r, "<td><a href=\"", anchor, "\">", | ||
| 1707 | ap_escape_html(scratch, t2), | ||
| 1708 | "</a>", pad_scratch + nwidth, NULL); | ||
| 1709 | wrowe | 89979 | } |
| 1710 | aaron | 92624 | if (!(autoindex_opts & SUPPRESS_LAST_MOD)) { |
| 1711 | if (ar[x]->lm != -1) { | ||
| 1712 | char time_str[MAX_STRING_LEN]; | ||
| 1713 | ianh | 93733 | apr_time_exp_t ts; |
| 1714 | striker | 94631 | apr_time_exp_lt(&ts, ar[x]->lm); |
| 1715 | nd | 98880 | apr_strftime(time_str, &rv, MAX_STRING_LEN, |
| 1716 | "</td><td align=\"right\">%d-%b-%Y %H:%M ", | ||
| 1717 | &ts); | ||
| 1718 | aaron | 92624 | ap_rputs(time_str, r); |
| 1719 | } | ||
| 1720 | else { | ||
| 1721 | ap_rputs("</td><td> ", r); | ||
| 1722 | } | ||
| 1723 | } | ||
| 1724 | if (!(autoindex_opts & SUPPRESS_SIZE)) { | ||
| 1725 | wrowe | 89979 | char buf[5]; |
| 1726 | aaron | 92624 | ap_rvputs(r, "</td><td align=\"right\">", |
| 1727 | apr_strfsize(ar[x]->size, buf), NULL); | ||
| 1728 | } | ||
| 1729 | if (!(autoindex_opts & SUPPRESS_DESC)) { | ||
| 1730 | if (ar[x]->desc) { | ||
| 1731 | coar | 91660 | if (d->desc_adjust == K_ADJUST) { |
| 1732 | aaron | 92624 | ap_rvputs(r, "</td><td>", ar[x]->desc, NULL); |
| 1733 | coar | 91660 | } |
| 1734 | else { | ||
| 1735 | nd | 98880 | ap_rvputs(r, "</td><td>", |
| 1736 | wrowe | 89979 | terminate_description(d, ar[x]->desc, |
| 1737 | nd | 98880 | autoindex_opts, |
| 1738 | wrowe | 90162 | desc_width), NULL); |
| 1739 | coar | 91660 | } |
| 1740 | aaron | 92624 | } |
| 1741 | } | ||
| 1742 | else { | ||
| 1743 | wrowe | 89979 | ap_rputs("</td><td> ", r); |
| 1744 | aaron | 92624 | } |
| 1745 | wrowe | 89979 | ap_rputs("</td></tr>\n", r); |
| 1746 | } | ||
| 1747 | aaron | 92624 | else if (autoindex_opts & FANCY_INDEXING) { |
| 1748 | if (!(autoindex_opts & SUPPRESS_ICON)) { | ||
| 1749 | if (autoindex_opts & ICONS_ARE_LINKS) { | ||
| 1750 | ap_rvputs(r, "<a href=\"", anchor, "\">", NULL); | ||
| 1751 | } | ||
| 1752 | if ((ar[x]->icon) || d->default_icon) { | ||
| 1753 | ap_rvputs(r, "<img src=\"", | ||
| 1754 | ap_escape_html(scratch, | ||
| 1755 | ar[x]->icon ? ar[x]->icon | ||
| 1756 | : d->default_icon), | ||
| 1757 | "\" alt=\"[", (ar[x]->alt ? ar[x]->alt : " "), | ||
| 1758 | "]\"", NULL); | ||
| 1759 | if (d->icon_width) { | ||
| 1760 | ap_rprintf(r, " width=\"%d\"", d->icon_width); | ||
| 1761 | coar | 91660 | } |
| 1762 | if (d->icon_height) { | ||
| 1763 | wrowe | 89979 | ap_rprintf(r, " height=\"%d\"", d->icon_height); |
| 1764 | coar | 91660 | } |
| 1765 | nd | 101683 | |
| 1766 | if (autoindex_opts & EMIT_XHTML) { | ||
| 1767 | ap_rputs(" /", r); | ||
| 1768 | } | ||
| 1769 | ap_rputs(">", r); | ||
| 1770 | aaron | 92624 | } |
| 1771 | coar | 91660 | else { |
| 1772 | aaron | 92624 | ap_rputs(" ", r); |
| 1773 | coar | 91660 | } |
| 1774 | aaron | 92624 | if (autoindex_opts & ICONS_ARE_LINKS) { |
| 1775 | ap_rputs("</a> ", r); | ||
| 1776 | coar | 91660 | } |
| 1777 | aaron | 92624 | else { |
| 1778 | ap_rputc(' ', r); | ||
| 1779 | coar | 91660 | } |
| 1780 | wrowe | 89979 | } |
| 1781 | aaron | 92624 | nwidth = strlen(t2); |
| 1782 | if (nwidth > name_width) { | ||
| 1783 | memcpy(name_scratch, t2, name_width - 3); | ||
| 1784 | name_scratch[name_width - 3] = '.'; | ||
| 1785 | name_scratch[name_width - 2] = '.'; | ||
| 1786 | name_scratch[name_width - 1] = '>'; | ||
| 1787 | name_scratch[name_width] = 0; | ||
| 1788 | t2 = name_scratch; | ||
| 1789 | nwidth = name_width; | ||
| 1790 | } | ||
| 1791 | ap_rvputs(r, "<a href=\"", anchor, "\">", | ||
| 1792 | ap_escape_html(scratch, t2), | ||
| 1793 | "</a>", pad_scratch + nwidth, NULL); | ||
| 1794 | /* | ||
| 1795 | * The blank before the storm.. er, before the next field. | ||
| 1796 | */ | ||
| 1797 | ap_rputs(" ", r); | ||
| 1798 | if (!(autoindex_opts & SUPPRESS_LAST_MOD)) { | ||
| 1799 | if (ar[x]->lm != -1) { | ||
| 1800 | char time_str[MAX_STRING_LEN]; | ||
| 1801 | ianh | 93733 | apr_time_exp_t ts; |
| 1802 | striker | 94631 | apr_time_exp_lt(&ts, ar[x]->lm); |
| 1803 | nd | 98880 | apr_strftime(time_str, &rv, MAX_STRING_LEN, |
| 1804 | dgaudet | 84413 | "%d-%b-%Y %H:%M ", &ts); |
| 1805 | aaron | 92624 | ap_rputs(time_str, r); |
| 1806 | } | ||
| 1807 | else { | ||
| 1808 | /*Length="22-Feb-1998 23:42 " (see 4 lines above) */ | ||
| 1809 | ap_rputs(" ", r); | ||
| 1810 | } | ||
| 1811 | } | ||
| 1812 | if (!(autoindex_opts & SUPPRESS_SIZE)) { | ||
| 1813 | wrowe | 89713 | char buf[5]; |
| 1814 | aaron | 92624 | ap_rputs(apr_strfsize(ar[x]->size, buf), r); |
| 1815 | ap_rputs(" ", r); | ||
| 1816 | } | ||
| 1817 |