1 #include "first.h" 2 3 #include "base.h" 4 #include "log.h" 5 #include "buffer.h" 6 #include "fdevent.h" 7 #include "http_header.h" 8 9 #include "plugin.h" 10 11 #include "stat_cache.h" 12 13 #include <stdlib.h> 14 #include <string.h> 15 #include <dirent.h> 16 #include <errno.h> 17 #include <fcntl.h> 18 #include <unistd.h> 19 #include <time.h> 20 21 #ifdef HAVE_PCRE_H 22 #include <pcre.h> 23 #endif 24 25 /** 26 * this is a dirlisting for a lighttpd plugin 27 */ 28 29 #ifdef HAVE_ATTR_ATTRIBUTES_H 30 #include <attr/attributes.h> 31 #endif 32 33 #ifdef HAVE_SYS_EXTATTR_H 34 #include <sys/extattr.h> 35 #endif 36 37 /* plugin config for all request/connections */ 38 39 typedef struct { 40 #ifdef HAVE_PCRE_H 41 pcre *regex; 42 #endif 43 buffer *string; 44 } excludes; 45 46 typedef struct { 47 excludes **ptr; 48 49 size_t used; 50 size_t size; 51 } excludes_buffer; 52 53 typedef struct { 54 unsigned short dir_listing; 55 unsigned short hide_dot_files; 56 unsigned short hide_readme_file; 57 unsigned short encode_readme; 58 unsigned short hide_header_file; 59 unsigned short encode_header; 60 unsigned short auto_layout; 61 62 excludes_buffer *excludes; 63 64 buffer *show_readme; 65 buffer *show_header; 66 buffer *external_css; 67 buffer *external_js; 68 buffer *encoding; 69 buffer *set_footer; 70 } plugin_config; 71 72 typedef struct { 73 PLUGIN_DATA; 74 75 buffer *tmp_buf; 76 buffer *content_charset; 77 78 plugin_config **config_storage; 79 80 plugin_config conf; 81 } plugin_data; 82 83 static excludes_buffer *excludes_buffer_init(void) { 84 excludes_buffer *exb; 85 86 exb = calloc(1, sizeof(*exb)); 87 88 return exb; 89 } 90 91 #ifdef HAVE_PCRE_H 92 static int excludes_buffer_append(excludes_buffer *exb, buffer *string) { 93 size_t i; 94 const char *errptr; 95 int erroff; 96 97 if (!string) return -1; 98 99 if (exb->used == exb->size) { 100 exb->size += 4; 101 102 exb->ptr = realloc(exb->ptr, exb->size * sizeof(*exb->ptr)); 103 104 for(i = exb->used; i < exb->size; i++) { 105 exb->ptr[i] = calloc(1, sizeof(**exb->ptr)); 106 } 107 } 108 109 110 if (NULL == (exb->ptr[exb->used]->regex = pcre_compile(string->ptr, 0, 111 &errptr, &erroff, NULL))) { 112 return -1; 113 } 114 115 exb->ptr[exb->used]->string = buffer_init(); 116 buffer_copy_buffer(exb->ptr[exb->used]->string, string); 117 118 exb->used++; 119 120 return 0; 121 } 122 #endif 123 124 static void excludes_buffer_free(excludes_buffer *exb) { 125 #ifdef HAVE_PCRE_H 126 size_t i; 127 128 for (i = 0; i < exb->size; i++) { 129 if (exb->ptr[i]->regex) pcre_free(exb->ptr[i]->regex); 130 if (exb->ptr[i]->string) buffer_free(exb->ptr[i]->string); 131 free(exb->ptr[i]); 132 } 133 134 if (exb->ptr) free(exb->ptr); 135 #endif 136 137 free(exb); 138 } 139 140 /* init the plugin data */ 141 INIT_FUNC(mod_dirlisting_init) { 142 plugin_data *p; 143 144 p = calloc(1, sizeof(*p)); 145 146 p->tmp_buf = buffer_init(); 147 p->content_charset = buffer_init(); 148 149 return p; 150 } 151 152 /* detroy the plugin data */ 153 FREE_FUNC(mod_dirlisting_free) { 154 plugin_data *p = p_d; 155 156 UNUSED(srv); 157 158 if (!p) return HANDLER_GO_ON; 159 160 if (p->config_storage) { 161 size_t i; 162 for (i = 0; i < srv->config_context->used; i++) { 163 plugin_config *s = p->config_storage[i]; 164 165 if (!s) continue; 166 167 excludes_buffer_free(s->excludes); 168 buffer_free(s->show_readme); 169 buffer_free(s->show_header); 170 buffer_free(s->external_css); 171 buffer_free(s->external_js); 172 buffer_free(s->encoding); 173 buffer_free(s->set_footer); 174 175 free(s); 176 } 177 free(p->config_storage); 178 } 179 180 buffer_free(p->tmp_buf); 181 buffer_free(p->content_charset); 182 183 free(p); 184 185 return HANDLER_GO_ON; 186 } 187 188 /* handle plugin config and check values */ 189 190 #define CONFIG_EXCLUDE "dir-listing.exclude" 191 #define CONFIG_ACTIVATE "dir-listing.activate" 192 #define CONFIG_HIDE_DOTFILES "dir-listing.hide-dotfiles" 193 #define CONFIG_EXTERNAL_CSS "dir-listing.external-css" 194 #define CONFIG_EXTERNAL_JS "dir-listing.external-js" 195 #define CONFIG_ENCODING "dir-listing.encoding" 196 #define CONFIG_SHOW_README "dir-listing.show-readme" 197 #define CONFIG_HIDE_README_FILE "dir-listing.hide-readme-file" 198 #define CONFIG_SHOW_HEADER "dir-listing.show-header" 199 #define CONFIG_HIDE_HEADER_FILE "dir-listing.hide-header-file" 200 #define CONFIG_DIR_LISTING "server.dir-listing" 201 #define CONFIG_SET_FOOTER "dir-listing.set-footer" 202 #define CONFIG_ENCODE_README "dir-listing.encode-readme" 203 #define CONFIG_ENCODE_HEADER "dir-listing.encode-header" 204 #define CONFIG_AUTO_LAYOUT "dir-listing.auto-layout" 205 206 207 SETDEFAULTS_FUNC(mod_dirlisting_set_defaults) { 208 plugin_data *p = p_d; 209 size_t i = 0; 210 211 config_values_t cv[] = { 212 { CONFIG_EXCLUDE, NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ 213 { CONFIG_ACTIVATE, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ 214 { CONFIG_HIDE_DOTFILES, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ 215 { CONFIG_EXTERNAL_CSS, NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ 216 { CONFIG_ENCODING, NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 4 */ 217 { CONFIG_SHOW_README, NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 5 */ 218 { CONFIG_HIDE_README_FILE, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 6 */ 219 { CONFIG_SHOW_HEADER, NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 7 */ 220 { CONFIG_HIDE_HEADER_FILE, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 8 */ 221 { CONFIG_DIR_LISTING, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 9 */ 222 { CONFIG_SET_FOOTER, NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 10 */ 223 { CONFIG_ENCODE_README, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 11 */ 224 { CONFIG_ENCODE_HEADER, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 12 */ 225 { CONFIG_AUTO_LAYOUT, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 13 */ 226 { CONFIG_EXTERNAL_JS, NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 14 */ 227 228 { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } 229 }; 230 231 if (!p) return HANDLER_ERROR; 232 233 p->config_storage = calloc(srv->config_context->used, sizeof(plugin_config *)); 234 235 for (i = 0; i < srv->config_context->used; i++) { 236 data_config const* config = (data_config const*)srv->config_context->data[i]; 237 plugin_config *s; 238 data_unset *du_excludes; 239 240 s = calloc(1, sizeof(plugin_config)); 241 s->excludes = excludes_buffer_init(); 242 s->dir_listing = 0; 243 s->show_readme = buffer_init(); 244 s->show_header = buffer_init(); 245 s->external_css = buffer_init(); 246 s->external_js = buffer_init(); 247 s->hide_dot_files = 1; 248 s->hide_readme_file = 0; 249 s->hide_header_file = 0; 250 s->encode_readme = 1; 251 s->encode_header = 1; 252 s->auto_layout = 1; 253 254 s->encoding = buffer_init(); 255 s->set_footer = buffer_init(); 256 257 cv[0].destination = s->excludes; 258 cv[1].destination = &(s->dir_listing); 259 cv[2].destination = &(s->hide_dot_files); 260 cv[3].destination = s->external_css; 261 cv[4].destination = s->encoding; 262 cv[5].destination = s->show_readme; 263 cv[6].destination = &(s->hide_readme_file); 264 cv[7].destination = s->show_header; 265 cv[8].destination = &(s->hide_header_file); 266 cv[9].destination = &(s->dir_listing); /* old name */ 267 cv[10].destination = s->set_footer; 268 cv[11].destination = &(s->encode_readme); 269 cv[12].destination = &(s->encode_header); 270 cv[13].destination = &(s->auto_layout); 271 cv[14].destination = s->external_js; 272 273 p->config_storage[i] = s; 274 275 if (0 != config_insert_values_global(srv, config->value, cv, i == 0 ? T_CONFIG_SCOPE_SERVER : T_CONFIG_SCOPE_CONNECTION)) { 276 return HANDLER_ERROR; 277 } 278 279 if (NULL != (du_excludes = array_get_element(config->value, CONFIG_EXCLUDE))) { 280 array *excludes_list; 281 282 excludes_list = ((data_array*)du_excludes)->value; 283 284 if (du_excludes->type != TYPE_ARRAY || !array_is_vlist(excludes_list)) { 285 log_error_write(srv, __FILE__, __LINE__, "s", 286 "unexpected type for " CONFIG_EXCLUDE "; expected list of \"regex\""); 287 return HANDLER_ERROR; 288 } 289 290 #ifndef HAVE_PCRE_H 291 if (excludes_list->used > 0) { 292 log_error_write(srv, __FILE__, __LINE__, "sss", 293 "pcre support is missing for: ", CONFIG_EXCLUDE, ", please install libpcre and the headers"); 294 return HANDLER_ERROR; 295 } 296 #else 297 for (size_t j = 0; j < excludes_list->used; ++j) { 298 data_unset *du_exclude = excludes_list->data[j]; 299 300 if (du_exclude->type != TYPE_STRING) { 301 log_error_write(srv, __FILE__, __LINE__, "sssbs", 302 "unexpected type for key: ", CONFIG_EXCLUDE, "[", 303 du_exclude->key, "](string)"); 304 return HANDLER_ERROR; 305 } 306 307 if (0 != excludes_buffer_append(s->excludes, ((data_string*)(du_exclude))->value)) { 308 log_error_write(srv, __FILE__, __LINE__, "sb", 309 "pcre-compile failed for", ((data_string*)(du_exclude))->value); 310 return HANDLER_ERROR; 311 } 312 } 313 #endif 314 } 315 316 if (!buffer_string_is_empty(s->show_readme)) { 317 if (buffer_is_equal_string(s->show_readme, CONST_STR_LEN("enable"))) { 318 buffer_copy_string_len(s->show_readme, CONST_STR_LEN("README.txt")); 319 } 320 else if (buffer_is_equal_string(s->show_readme, CONST_STR_LEN("disable"))) { 321 buffer_clear(s->show_readme); 322 } 323 } 324 325 if (!buffer_string_is_empty(s->show_header)) { 326 if (buffer_is_equal_string(s->show_header, CONST_STR_LEN("enable"))) { 327 buffer_copy_string_len(s->show_header, CONST_STR_LEN("HEADER.txt")); 328 } 329 else if (buffer_is_equal_string(s->show_header, CONST_STR_LEN("disable"))) { 330 buffer_clear(s->show_header); 331 } 332 } 333 } 334 335 return HANDLER_GO_ON; 336 } 337 338 #define PATCH(x) \ 339 p->conf.x = s->x; 340 static int mod_dirlisting_patch_connection(server *srv, connection *con, plugin_data *p) { 341 size_t i, j; 342 plugin_config *s = p->config_storage[0]; 343 344 PATCH(dir_listing); 345 PATCH(external_css); 346 PATCH(external_js); 347 PATCH(hide_dot_files); 348 PATCH(encoding); 349 PATCH(show_readme); 350 PATCH(hide_readme_file); 351 PATCH(show_header); 352 PATCH(hide_header_file); 353 PATCH(excludes); 354 PATCH(set_footer); 355 PATCH(encode_readme); 356 PATCH(encode_header); 357 PATCH(auto_layout); 358 359 /* skip the first, the global context */ 360 for (i = 1; i < srv->config_context->used; i++) { 361 data_config *dc = (data_config *)srv->config_context->data[i]; 362 s = p->config_storage[i]; 363 364 /* condition didn't match */ 365 if (!config_check_cond(srv, con, dc)) continue; 366 367 /* merge config */ 368 for (j = 0; j < dc->value->used; j++) { 369 data_unset *du = dc->value->data[j]; 370 371 if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_ACTIVATE)) || 372 buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_DIR_LISTING))) { 373 PATCH(dir_listing); 374 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_HIDE_DOTFILES))) { 375 PATCH(hide_dot_files); 376 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_EXTERNAL_CSS))) { 377 PATCH(external_css); 378 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_EXTERNAL_JS))) { 379 PATCH(external_js); 380 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_ENCODING))) { 381 PATCH(encoding); 382 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_SHOW_README))) { 383 PATCH(show_readme); 384 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_HIDE_README_FILE))) { 385 PATCH(hide_readme_file); 386 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_SHOW_HEADER))) { 387 PATCH(show_header); 388 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_HIDE_HEADER_FILE))) { 389 PATCH(hide_header_file); 390 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_SET_FOOTER))) { 391 PATCH(set_footer); 392 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_EXCLUDE))) { 393 PATCH(excludes); 394 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_ENCODE_README))) { 395 PATCH(encode_readme); 396 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_ENCODE_HEADER))) { 397 PATCH(encode_header); 398 } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_AUTO_LAYOUT))) { 399 PATCH(auto_layout); 400 } 401 } 402 } 403 404 return 0; 405 } 406 #undef PATCH 407 408 typedef struct { 409 size_t namelen; 410 time_t mtime; 411 off_t size; 412 } dirls_entry_t; 413 414 typedef struct { 415 dirls_entry_t **ent; 416 size_t used; 417 size_t size; 418 } dirls_list_t; 419 420 #define DIRLIST_ENT_NAME(ent) ((char*)(ent) + sizeof(dirls_entry_t)) 421 #define DIRLIST_BLOB_SIZE 16 422 423 /* simple combsort algorithm */ 424 static void http_dirls_sort(dirls_entry_t **ent, int num) { 425 int gap = num; 426 int i, j; 427 int swapped; 428 dirls_entry_t *tmp; 429 430 do { 431 gap = (gap * 10) / 13; 432 if (gap == 9 || gap == 10) 433 gap = 11; 434 if (gap < 1) 435 gap = 1; 436 swapped = 0; 437 438 for (i = 0; i < num - gap; i++) { 439 j = i + gap; 440 if (strcmp(DIRLIST_ENT_NAME(ent[i]), DIRLIST_ENT_NAME(ent[j])) > 0) { 441 tmp = ent[i]; 442 ent[i] = ent[j]; 443 ent[j] = tmp; 444 swapped = 1; 445 } 446 } 447 448 } while (gap > 1 || swapped); 449 } 450 451 /* buffer must be able to hold "999.9K" 452 * conversion is simple but not perfect 453 */ 454 static int http_list_directory_sizefmt(char *buf, size_t bufsz, off_t size) { 455 const char unit[] = " KMGTPE"; /* Kilo, Mega, Giga, Tera, Peta, Exa */ 456 const char *u = unit; /* u will always increment at least once */ 457 int remain; 458 size_t buflen; 459 460 if (size < 100) 461 size += 99; 462 if (size < 100) 463 size = 0; 464 465 while (1) { 466 remain = (int) size & 1023; 467 size >>= 10; 468 u++; 469 if ((size & (~0 ^ 1023)) == 0) 470 break; 471 } 472 473 remain /= 100; 474 if (remain > 9) 475 remain = 9; 476 if (size > 999) { 477 size = 0; 478 remain = 9; 479 u++; 480 } 481 482 li_itostrn(buf, bufsz, size); 483 buflen = strlen(buf); 484 if (buflen + 3 >= bufsz) return buflen; 485 buf[buflen+0] = '.'; 486 buf[buflen+1] = remain + '0'; 487 buf[buflen+2] = *u; 488 buf[buflen+3] = '\0'; 489 490 return buflen + 3; 491 } 492 493 static void http_list_directory_include_file(buffer *out, int symlinks, buffer *path, const char *classname, int encode) { 494 int fd = fdevent_open_cloexec(path->ptr, symlinks, O_RDONLY, 0); 495 ssize_t rd; 496 char buf[8192]; 497 498 if (-1 == fd) return; 499 500 if (encode) { 501 buffer_append_string_len(out, CONST_STR_LEN("<pre class=\"")); 502 buffer_append_string(out, classname); 503 buffer_append_string_len(out, CONST_STR_LEN("\">")); 504 } 505 506 while ((rd = read(fd, buf, sizeof(buf))) > 0) { 507 if (encode) { 508 buffer_append_string_encoded(out, buf, (size_t)rd, ENCODING_MINIMAL_XML); 509 } else { 510 buffer_append_string_len(out, buf, (size_t)rd); 511 } 512 } 513 close(fd); 514 515 if (encode) { 516 buffer_append_string_len(out, CONST_STR_LEN("</pre>")); 517 } 518 } 519 520 /* portions copied from mod_status 521 * modified and specialized for stable dirlist sorting by name */ 522 static const char js_simple_table_resort[] = \ 523 "var click_column;\n" \ 524 "var name_column = 0;\n" \ 525 "var date_column = 1;\n" \ 526 "var size_column = 2;\n" \ 527 "var type_column = 3;\n" \ 528 "var prev_span = null;\n" \ 529 "\n" \ 530 "if (typeof(String.prototype.localeCompare) === 'undefined') {\n" \ 531 " String.prototype.localeCompare = function(str, locale, options) {\n" \ 532 " return ((this == str) ? 0 : ((this > str) ? 1 : -1));\n" \ 533 " };\n" \ 534 "}\n" \ 535 "\n" \ 536 "if (typeof(String.prototype.toLocaleUpperCase) === 'undefined') {\n" \ 537 " String.prototype.toLocaleUpperCase = function() {\n" \ 538 " return this.toUpperCase();\n" \ 539 " };\n" \ 540 "}\n" \ 541 "\n" \ 542 "function get_inner_text(el) {\n" \ 543 " if((typeof el == 'string')||(typeof el == 'undefined'))\n" \ 544 " return el;\n" \ 545 " if(el.innerText)\n" \ 546 " return el.innerText;\n" \ 547 " else {\n" \ 548 " var str = \"\";\n" \ 549 " var cs = el.childNodes;\n" \ 550 " var l = cs.length;\n" \ 551 " for (i=0;i<l;i++) {\n" \ 552 " if (cs[i].nodeType==1) str += get_inner_text(cs[i]);\n" \ 553 " else if (cs[i].nodeType==3) str += cs[i].nodeValue;\n" \ 554 " }\n" \ 555 " }\n" \ 556 " return str;\n" \ 557 "}\n" \ 558 "\n" \ 559 "function isdigit(c) {\n" \ 560 " return (c >= '0' && c <= '9');\n" \ 561 "}\n" \ 562 "\n" \ 563 "function unit_multiplier(unit) {\n" \ 564 " return (unit=='K') ? 1000\n" \ 565 " : (unit=='M') ? 1000000\n" \ 566 " : (unit=='G') ? 1000000000\n" \ 567 " : (unit=='T') ? 1000000000000\n" \ 568 " : (unit=='P') ? 1000000000000000\n" \ 569 " : (unit=='E') ? 1000000000000000000 : 1;\n" \ 570 "}\n" \ 571 "\n" \ 572 "var li_date_regex=/(\\d{4})-(\\w{3})-(\\d{2}) (\\d{2}):(\\d{2}):(\\d{2})/;\n" \ 573 "\n" \ 574 "var li_mon = ['Jan','Feb','Mar','Apr','May','Jun',\n" \ 575 " 'Jul','Aug','Sep','Oct','Nov','Dec'];\n" \ 576 "\n" \ 577 "function li_mon_num(mon) {\n" \ 578 " var i; for (i = 0; i < 12 && mon != li_mon[i]; ++i); return i;\n" \ 579 "}\n" \ 580 "\n" \ 581 "function li_date_cmp(s1, s2) {\n" \ 582 " var dp1 = li_date_regex.exec(s1)\n" \ 583 " var dp2 = li_date_regex.exec(s2)\n" \ 584 " for (var i = 1; i < 7; ++i) {\n" \ 585 " var cmp = (2 != i)\n" \ 586 " ? parseInt(dp1[i]) - parseInt(dp2[i])\n" \ 587 " : li_mon_num(dp1[2]) - li_mon_num(dp2[2]);\n" \ 588 " if (0 != cmp) return cmp;\n" \ 589 " }\n" \ 590 " return 0;\n" \ 591 "}\n" \ 592 "\n" \ 593 "function sortfn_then_by_name(a,b,sort_column) {\n" \ 594 " if (sort_column == name_column || sort_column == type_column) {\n" \ 595 " var ad = (a.cells[type_column].innerHTML === 'Directory');\n" \ 596 " var bd = (b.cells[type_column].innerHTML === 'Directory');\n" \ 597 " if (ad != bd) return (ad ? -1 : 1);\n" \ 598 " }\n" \ 599 " var at = get_inner_text(a.cells[sort_column]);\n" \ 600 " var bt = get_inner_text(b.cells[sort_column]);\n" \ 601 " var cmp;\n" \ 602 " if (sort_column == name_column) {\n" \ 603 " if (at == '..') return -1;\n" \ 604 " if (bt == '..') return 1;\n" \ 605 " }\n" \ 606 " if (a.cells[sort_column].className == 'int') {\n" \ 607 " cmp = parseInt(at)-parseInt(bt);\n" \ 608 " } else if (sort_column == date_column) {\n" \ 609 " var ad = isdigit(at.substr(0,1));\n" \ 610 " var bd = isdigit(bt.substr(0,1));\n" \ 611 " if (ad != bd) return (!ad ? -1 : 1);\n" \ 612 " cmp = li_date_cmp(at,bt);\n" \ 613 " } else if (sort_column == size_column) {\n" \ 614 " var ai = parseInt(at, 10) * unit_multiplier(at.substr(-1,1));\n" \ 615 " var bi = parseInt(bt, 10) * unit_multiplier(bt.substr(-1,1));\n" \ 616 " if (at.substr(0,1) == '-') ai = -1;\n" \ 617 " if (bt.substr(0,1) == '-') bi = -1;\n" \ 618 " cmp = ai - bi;\n" \ 619 " } else {\n" \ 620 " cmp = at.toLocaleUpperCase().localeCompare(bt.toLocaleUpperCase());\n" \ 621 " if (0 != cmp) return cmp;\n" \ 622 " cmp = at.localeCompare(bt);\n" \ 623 " }\n" \ 624 " if (0 != cmp || sort_column == name_column) return cmp;\n" \ 625 " return sortfn_then_by_name(a,b,name_column);\n" \ 626 "}\n" \ 627 "\n" \ 628 "function sortfn(a,b) {\n" \ 629 " return sortfn_then_by_name(a,b,click_column);\n" \ 630 "}\n" \ 631 "\n" \ 632 "function resort(lnk) {\n" \ 633 " var span = lnk.childNodes[1];\n" \ 634 " var table = lnk.parentNode.parentNode.parentNode.parentNode;\n" \ 635 " var rows = new Array();\n" \ 636 " for (j=1;j<table.rows.length;j++)\n" \ 637 " rows[j-1] = table.rows[j];\n" \ 638 " click_column = lnk.parentNode.cellIndex;\n" \ 639 " rows.sort(sortfn);\n" \ 640 "\n" \ 641 " if (prev_span != null) prev_span.innerHTML = '';\n" \ 642 " if (span.getAttribute('sortdir')=='down') {\n" \ 643 " span.innerHTML = '↑';\n" \ 644 " span.setAttribute('sortdir','up');\n" \ 645 " rows.reverse();\n" \ 646 " } else {\n" \ 647 " span.innerHTML = '↓';\n" \ 648 " span.setAttribute('sortdir','down');\n" \ 649 " }\n" \ 650 " for (i=0;i<rows.length;i++)\n" \ 651 " table.tBodies[0].appendChild(rows[i]);\n" \ 652 " prev_span = span;\n" \ 653 "}\n"; 654 655 /* portions copied from mod_dirlist (lighttpd2) */ 656 static const char js_simple_table_init_sort[] = \ 657 "\n" \ 658 "function init_sort(init_sort_column, ascending) {\n" \ 659 " var tables = document.getElementsByTagName(\"table\");\n" \ 660 " for (var i = 0; i < tables.length; i++) {\n" \ 661 " var table = tables[i];\n" \ 662 " //var c = table.getAttribute(\"class\")\n" \ 663 " //if (-1 != c.split(\" \").indexOf(\"sort\")) {\n" \ 664 " var row = table.rows[0].cells;\n" \ 665 " for (var j = 0; j < row.length; j++) {\n" \ 666 " var n = row[j];\n" \ 667 " if (n.childNodes.length == 1 && n.childNodes[0].nodeType == 3) {\n" \ 668 " var link = document.createElement(\"a\");\n" \ 669 " var title = n.childNodes[0].nodeValue.replace(/:$/, \"\");\n" \ 670 " link.appendChild(document.createTextNode(title));\n" \ 671 " link.setAttribute(\"href\", \"#\");\n" \ 672 " link.setAttribute(\"class\", \"sortheader\");\n" \ 673 " link.setAttribute(\"onclick\", \"resort(this);return false;\");\n" \ 674 " var arrow = document.createElement(\"span\");\n" \ 675 " arrow.setAttribute(\"class\", \"sortarrow\");\n" \ 676 " arrow.appendChild(document.createTextNode(\":\"));\n" \ 677 " link.appendChild(arrow)\n" \ 678 " n.replaceChild(link, n.firstChild);\n" \ 679 " }\n" \ 680 " }\n" \ 681 " var lnk = row[init_sort_column].firstChild;\n" \ 682 " if (ascending) {\n" \ 683 " var span = lnk.childNodes[1];\n" \ 684 " span.setAttribute('sortdir','down');\n" \ 685 " }\n" \ 686 " resort(lnk);\n" \ 687 " //}\n" \ 688 " }\n" \ 689 "}\n"; 690 691 static void http_dirlist_append_js_table_resort (buffer *b, connection *con) { 692 char col = '0'; 693 char ascending = '0'; 694 if (!buffer_string_is_empty(con->uri.query)) { 695 const char *qs = con->uri.query->ptr; 696 do { 697 if (qs[0] == 'C' && qs[1] == '=') { 698 switch (qs[2]) { 699 case 'N': col = '0'; break; 700 case 'M': col = '1'; break; 701 case 'S': col = '2'; break; 702 case 'T': 703 case 'D': col = '3'; break; 704 default: break; 705 } 706 } 707 else if (qs[0] == 'O' && qs[1] == '=') { 708 switch (qs[2]) { 709 case 'A': ascending = '1'; break; 710 case 'D': ascending = '0'; break; 711 default: break; 712 } 713 } 714 } while ((qs = strchr(qs, '&')) && *++qs); 715 } 716 717 buffer_append_string_len(b, CONST_STR_LEN("\n<script type=\"text/javascript\">\n// <!--\n\n")); 718 buffer_append_string_len(b, js_simple_table_resort, sizeof(js_simple_table_resort)-1); 719 buffer_append_string_len(b, js_simple_table_init_sort, sizeof(js_simple_table_init_sort)-1); 720 buffer_append_string_len(b, CONST_STR_LEN("\ninit_sort(")); 721 buffer_append_string_len(b, &col, 1); 722 buffer_append_string_len(b, CONST_STR_LEN(", ")); 723 buffer_append_string_len(b, &ascending, 1); 724 buffer_append_string_len(b, CONST_STR_LEN(");\n\n// -->\n</script>\n\n")); 725 } 726 727 static void http_list_directory_header(server *srv, connection *con, plugin_data *p, buffer *out) { 728 UNUSED(srv); 729 730 if (p->conf.auto_layout) { 731 buffer_append_string_len(out, CONST_STR_LEN( 732 "<!DOCTYPE html>\n" 733 "<html>\n" 734 "<head>\n" 735 )); 736 if (!buffer_string_is_empty(p->conf.encoding)) { 737 buffer_append_string_len(out, CONST_STR_LEN("<meta charset=\"")); 738 buffer_append_string_buffer(out, p->conf.encoding); 739 buffer_append_string_len(out, CONST_STR_LEN("\">\n")); 740 } 741 buffer_append_string_len(out, CONST_STR_LEN("<title>Index of ")); 742 buffer_append_string_encoded(out, CONST_BUF_LEN(con->uri.path), ENCODING_MINIMAL_XML); 743 buffer_append_string_len(out, CONST_STR_LEN("</title>\n")); 744 745 if (!buffer_string_is_empty(p->conf.external_css)) { 746 buffer_append_string_len(out, CONST_STR_LEN("<meta name=\"viewport\" content=\"initial-scale=1\">")); 747 buffer_append_string_len(out, CONST_STR_LEN("<link rel=\"stylesheet\" type=\"text/css\" href=\"")); 748 buffer_append_string_buffer(out, p->conf.external_css); 749 buffer_append_string_len(out, CONST_STR_LEN("\">\n")); 750 } else { 751 buffer_append_string_len(out, CONST_STR_LEN( 752 "<style type=\"text/css\">\n" 753 "a, a:active {text-decoration: none; color: blue;}\n" 754 "a:visited {color: #48468F;}\n" 755 "a:hover, a:focus {text-decoration: underline; color: red;}\n" 756 "body {background-color: #F5F5F5;}\n" 757 "h2 {margin-bottom: 12px;}\n" 758 "table {margin-left: 12px;}\n" 759 "th, td {" 760 " font: 90% monospace;" 761 " text-align: left;" 762 "}\n" 763 "th {" 764 " font-weight: bold;" 765 " padding-right: 14px;" 766 " padding-bottom: 3px;" 767 "}\n" 768 "td {padding-right: 14px;}\n" 769 "td.s, th.s {text-align: right;}\n" 770 "div.list {" 771 " background-color: white;" 772 " border-top: 1px solid #646464;" 773 " border-bottom: 1px solid #646464;" 774 " padding-top: 10px;" 775 " padding-bottom: 14px;" 776 "}\n" 777 "div.foot {" 778 " font: 90% monospace;" 779 " color: #787878;" 780 " padding-top: 4px;" 781 "}\n" 782 "</style>\n" 783 )); 784 } 785 786 buffer_append_string_len(out, CONST_STR_LEN("</head>\n<body>\n")); 787 } 788 789 if (!buffer_string_is_empty(p->conf.show_header)) { 790 /* if we have a HEADER file, display it in <pre class="header"></pre> */ 791 792 buffer *hb = p->conf.show_header; 793 if (hb->ptr[0] != '/') { 794 buffer_copy_buffer(p->tmp_buf, con->physical.path); 795 buffer_append_path_len(p->tmp_buf, CONST_BUF_LEN(p->conf.show_header)); 796 hb = p->tmp_buf; 797 } 798 799 http_list_directory_include_file(out, con->conf.follow_symlink, hb, "header", p->conf.encode_header); 800 } 801 802 buffer_append_string_len(out, CONST_STR_LEN("<h2>Index of ")); 803 buffer_append_string_encoded(out, CONST_BUF_LEN(con->uri.path), ENCODING_MINIMAL_XML); 804 buffer_append_string_len(out, CONST_STR_LEN( 805 "</h2>\n" 806 "<div class=\"list\">\n" 807 "<table summary=\"Directory Listing\" cellpadding=\"0\" cellspacing=\"0\">\n" 808 "<thead>" 809 "<tr>" 810 "<th class=\"n\">Name</th>" 811 "<th class=\"m\">Last Modified</th>" 812 "<th class=\"s\">Size</th>" 813 "<th class=\"t\">Type</th>" 814 "</tr>" 815 "</thead>\n" 816 "<tbody>\n" 817 )); 818 if (!buffer_is_equal_string(con->uri.path, CONST_STR_LEN("/"))) { 819 buffer_append_string_len(out, CONST_STR_LEN( 820 "<tr class=\"d\">" 821 "<td class=\"n\"><a href=\"../\">..</a>/</td>" 822 "<td class=\"m\"> </td>" 823 "<td class=\"s\">- </td>" 824 "<td class=\"t\">Directory</td>" 825 "</tr>\n" 826 )); 827 } 828 } 829 830 static void http_list_directory_footer(server *srv, connection *con, plugin_data *p, buffer *out) { 831 UNUSED(srv); 832 833 buffer_append_string_len(out, CONST_STR_LEN( 834 "</tbody>\n" 835 "</table>\n" 836 "</div>\n" 837 )); 838 839 if (!buffer_string_is_empty(p->conf.show_readme)) { 840 /* if we have a README file, display it in <pre class="readme"></pre> */ 841 842 buffer *rb = p->conf.show_readme; 843 if (rb->ptr[0] != '/') { 844 buffer_copy_buffer(p->tmp_buf, con->physical.path); 845 buffer_append_path_len(p->tmp_buf, CONST_BUF_LEN(p->conf.show_readme)); 846 rb = p->tmp_buf; 847 } 848 849 http_list_directory_include_file(out, con->conf.follow_symlink, rb, "readme", p->conf.encode_readme); 850 } 851 852 if(p->conf.auto_layout) { 853 854 buffer_append_string_len(out, CONST_STR_LEN( 855 "<div class=\"foot\">" 856 )); 857 858 if (!buffer_string_is_empty(p->conf.set_footer)) { 859 buffer_append_string_buffer(out, p->conf.set_footer); 860 } else { 861 buffer_append_string_buffer(out, con->conf.server_tag); 862 } 863 864 buffer_append_string_len(out, CONST_STR_LEN( 865 "</div>\n" 866 )); 867 868 if (!buffer_string_is_empty(p->conf.external_js)) { 869 buffer_append_string_len(out, CONST_STR_LEN("<script type=\"text/javascript\" src=\"")); 870 buffer_append_string_buffer(out, p->conf.external_js); 871 buffer_append_string_len(out, CONST_STR_LEN("\"></script>\n")); 872 } else if (buffer_is_empty(p->conf.external_js)) { 873 http_dirlist_append_js_table_resort(out, con); 874 } 875 876 buffer_append_string_len(out, CONST_STR_LEN( 877 "</body>\n" 878 "</html>\n" 879 )); 880 } 881 } 882 883 static int http_list_directory(server *srv, connection *con, plugin_data *p, buffer *dir) { 884 DIR *dp; 885 buffer *out; 886 struct dirent *dent; 887 struct stat st; 888 char *path, *path_file; 889 size_t i; 890 int hide_dotfiles = p->conf.hide_dot_files; 891 dirls_list_t dirs, files, *list; 892 dirls_entry_t *tmp; 893 char sizebuf[sizeof("999.9K")]; 894 char datebuf[sizeof("2005-Jan-01 22:23:24")]; 895 const char *content_type; 896 long name_max; 897 #if defined(HAVE_XATTR) || defined(HAVE_EXTATTR) 898 char attrval[128]; 899 int attrlen; 900 #endif 901 #ifdef HAVE_LOCALTIME_R 902 struct tm tm; 903 #endif 904 905 if (buffer_string_is_empty(dir)) return -1; 906 907 i = buffer_string_length(dir); 908 909 #ifdef HAVE_PATHCONF 910 if (0 >= (name_max = pathconf(dir->ptr, _PC_NAME_MAX))) { 911 /* some broken fs (fuse) return 0 instead of -1 */ 912 #ifdef NAME_MAX 913 name_max = NAME_MAX; 914 #else 915 name_max = 255; /* stupid default */ 916 #endif 917 } 918 #elif defined __WIN32 919 name_max = FILENAME_MAX; 920 #else 921 name_max = NAME_MAX; 922 #endif 923 924 path = malloc(i + name_max + 1); 925 force_assert(NULL != path); 926 memcpy(path, dir->ptr, i+1); 927 path_file = path + i; 928 929 if (NULL == (dp = opendir(path))) { 930 log_error_write(srv, __FILE__, __LINE__, "sbs", 931 "opendir failed:", dir, strerror(errno)); 932 933 free(path); 934 return -1; 935 } 936 937 dirs.ent = (dirls_entry_t**) malloc(sizeof(dirls_entry_t*) * DIRLIST_BLOB_SIZE); 938 force_assert(dirs.ent); 939 dirs.size = DIRLIST_BLOB_SIZE; 940 dirs.used = 0; 941 files.ent = (dirls_entry_t**) malloc(sizeof(dirls_entry_t*) * DIRLIST_BLOB_SIZE); 942 force_assert(files.ent); 943 files.size = DIRLIST_BLOB_SIZE; 944 files.used = 0; 945 946 while ((dent = readdir(dp)) != NULL) { 947 #ifdef HAVE_PCRE_H 948 unsigned short exclude_match = 0; 949 #endif 950 951 if (dent->d_name[0] == '.') { 952 if (hide_dotfiles) 953 continue; 954 if (dent->d_name[1] == '\0') 955 continue; 956 if (dent->d_name[1] == '.' && dent->d_name[2] == '\0') 957 continue; 958 } 959 960 if (p->conf.hide_readme_file && !buffer_string_is_empty(p->conf.show_readme)) { 961 if (strcmp(dent->d_name, p->conf.show_readme->ptr) == 0) 962 continue; 963 } 964 if (p->conf.hide_header_file && !buffer_string_is_empty(p->conf.show_header)) { 965 if (strcmp(dent->d_name, p->conf.show_header->ptr) == 0) 966 continue; 967 } 968 969 /* compare d_name against excludes array 970 * elements, skipping any that match. 971 */ 972 #ifdef HAVE_PCRE_H 973 for(i = 0; i < p->conf.excludes->used; i++) { 974 int n; 975 #define N 10 976 int ovec[N * 3]; 977 pcre *regex = p->conf.excludes->ptr[i]->regex; 978 979 if ((n = pcre_exec(regex, NULL, dent->d_name, 980 strlen(dent->d_name), 0, 0, ovec, 3 * N)) < 0) { 981 if (n != PCRE_ERROR_NOMATCH) { 982 log_error_write(srv, __FILE__, __LINE__, "sd", 983 "execution error while matching:", n); 984 985 /* aborting would require a lot of manual cleanup here. 986 * skip instead (to not leak names that break pcre matching) 987 */ 988 exclude_match = 1; 989 break; 990 } 991 } 992 else { 993 exclude_match = 1; 994 break; 995 } 996 } 997 998 if (exclude_match) { 999 continue; 1000 } 1001 #endif 1002 1003 i = strlen(dent->d_name); 1004 1005 /* NOTE: the manual says, d_name is never more than NAME_MAX 1006 * so this should actually not be a buffer-overflow-risk 1007 */ 1008 if (i > (size_t)name_max) continue; 1009 1010 memcpy(path_file, dent->d_name, i + 1); 1011 if (stat(path, &st) != 0) 1012 continue; 1013 1014 list = &files; 1015 if (S_ISDIR(st.st_mode)) 1016 list = &dirs; 1017 1018 if (list->used == list->size) { 1019 list->size += DIRLIST_BLOB_SIZE; 1020 list->ent = (dirls_entry_t**) realloc(list->ent, sizeof(dirls_entry_t*) * list->size); 1021 force_assert(list->ent); 1022 } 1023 1024 tmp = (dirls_entry_t*) malloc(sizeof(dirls_entry_t) + 1 + i); 1025 tmp->mtime = st.st_mtime; 1026 tmp->size = st.st_size; 1027 tmp->namelen = i; 1028 memcpy(DIRLIST_ENT_NAME(tmp), dent->d_name, i + 1); 1029 1030 list->ent[list->used++] = tmp; 1031 } 1032 closedir(dp); 1033 1034 if (dirs.used) http_dirls_sort(dirs.ent, dirs.used); 1035 1036 if (files.used) http_dirls_sort(files.ent, files.used); 1037 1038 out = chunkqueue_append_buffer_open(con->write_queue); 1039 http_list_directory_header(srv, con, p, out); 1040 1041 /* directories */ 1042 for (i = 0; i < dirs.used; i++) { 1043 tmp = dirs.ent[i]; 1044 1045 #ifdef HAVE_LOCALTIME_R 1046 localtime_r(&(tmp->mtime), &tm); 1047 strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", &tm); 1048 #else 1049 strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", localtime(&(tmp->mtime))); 1050 #endif 1051 1052 buffer_append_string_len(out, CONST_STR_LEN("<tr class=\"d\"><td class=\"n\"><a href=\"")); 1053 buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_REL_URI_PART); 1054 buffer_append_string_len(out, CONST_STR_LEN("/\">")); 1055 buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_MINIMAL_XML); 1056 buffer_append_string_len(out, CONST_STR_LEN("</a>/</td><td class=\"m\">")); 1057 buffer_append_string_len(out, datebuf, sizeof(datebuf) - 1); 1058 buffer_append_string_len(out, CONST_STR_LEN("</td><td class=\"s\">- </td><td class=\"t\">Directory</td></tr>\n")); 1059 1060 free(tmp); 1061 } 1062 1063 /* files */ 1064 for (i = 0; i < files.used; i++) { 1065 tmp = files.ent[i]; 1066 1067 content_type = NULL; 1068 #if defined(HAVE_XATTR) 1069 if (con->conf.use_xattr) { 1070 memcpy(path_file, DIRLIST_ENT_NAME(tmp), tmp->namelen + 1); 1071 attrlen = sizeof(attrval) - 1; 1072 if (attr_get(path, srv->srvconf.xattr_name->ptr, attrval, &attrlen, 0) == 0) { 1073 attrval[attrlen] = '\0'; 1074 content_type = attrval; 1075 } 1076 } 1077 #elif defined(HAVE_EXTATTR) 1078 if (con->conf.use_xattr) { 1079 memcpy(path_file, DIRLIST_ENT_NAME(tmp), tmp->namelen + 1); 1080 if(-1 != (attrlen = extattr_get_file(path, EXTATTR_NAMESPACE_USER, srv->srvconf.xattr_name->ptr, attrval, sizeof(attrval)-1))) { 1081 attrval[attrlen] = '\0'; 1082 content_type = attrval; 1083 } 1084 } 1085 #endif 1086 1087 if (content_type == NULL) { 1088 const buffer *type = stat_cache_mimetype_by_ext(con, DIRLIST_ENT_NAME(tmp), tmp->namelen); 1089 content_type = NULL != type ? type->ptr : "application/octet-stream"; 1090 } 1091 1092 #ifdef HAVE_LOCALTIME_R 1093 localtime_r(&(tmp->mtime), &tm); 1094 strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", &tm); 1095 #else 1096 strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", localtime(&(tmp->mtime))); 1097 #endif 1098 http_list_directory_sizefmt(sizebuf, sizeof(sizebuf), tmp->size); 1099 1100 buffer_append_string_len(out, CONST_STR_LEN("<tr><td class=\"n\"><a href=\"")); 1101 buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_REL_URI_PART); 1102 buffer_append_string_len(out, CONST_STR_LEN("\">")); 1103 buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_MINIMAL_XML); 1104 buffer_append_string_len(out, CONST_STR_LEN("</a></td><td class=\"m\">")); 1105 buffer_append_string_len(out, datebuf, sizeof(datebuf) - 1); 1106 buffer_append_string_len(out, CONST_STR_LEN("</td><td class=\"s\">")); 1107 buffer_append_string(out, sizebuf); 1108 buffer_append_string_len(out, CONST_STR_LEN("</td><td class=\"t\">")); 1109 buffer_append_string(out, content_type); 1110 buffer_append_string_len(out, CONST_STR_LEN("</td></tr>\n")); 1111 1112 free(tmp); 1113 } 1114 1115 free(files.ent); 1116 free(dirs.ent); 1117 free(path); 1118 1119 http_list_directory_footer(srv, con, p, out); 1120 1121 /* Insert possible charset to Content-Type */ 1122 if (buffer_string_is_empty(p->conf.encoding)) { 1123 http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); 1124 } else { 1125 buffer_copy_string_len(p->content_charset, CONST_STR_LEN("text/html; charset=")); 1126 buffer_append_string_buffer(p->content_charset, p->conf.encoding); 1127 http_header_response_set(con, HTTP_HEADER_CONTENT_TYPE, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->content_charset)); 1128 } 1129 1130 chunkqueue_append_buffer_commit(con->write_queue); 1131 con->file_finished = 1; 1132 1133 return 0; 1134 } 1135 1136 1137 1138 URIHANDLER_FUNC(mod_dirlisting_subrequest) { 1139 plugin_data *p = p_d; 1140 stat_cache_entry *sce = NULL; 1141 1142 UNUSED(srv); 1143 1144 /* we only handle GET and HEAD */ 1145 switch(con->request.http_method) { 1146 case HTTP_METHOD_GET: 1147 case HTTP_METHOD_HEAD: 1148 break; 1149 default: 1150 return HANDLER_GO_ON; 1151 } 1152 1153 if (con->mode != DIRECT) return HANDLER_GO_ON; 1154 1155 if (buffer_is_empty(con->physical.path)) return HANDLER_GO_ON; 1156 if (buffer_is_empty(con->uri.path)) return HANDLER_GO_ON; 1157 if (con->uri.path->ptr[buffer_string_length(con->uri.path) - 1] != '/') return HANDLER_GO_ON; 1158 1159 mod_dirlisting_patch_connection(srv, con, p); 1160 1161 if (!p->conf.dir_listing) return HANDLER_GO_ON; 1162 1163 if (con->conf.log_request_handling) { 1164 log_error_write(srv, __FILE__, __LINE__, "s", "-- handling the request as Dir-Listing"); 1165 log_error_write(srv, __FILE__, __LINE__, "sb", "URI :", con->uri.path); 1166 } 1167 1168 if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) { 1169 log_error_write(srv, __FILE__, __LINE__, "SB", "stat_cache_get_entry failed: ", con->physical.path); 1170 SEGFAULT(); 1171 } 1172 1173 if (!S_ISDIR(sce->st.st_mode)) return HANDLER_GO_ON; 1174 1175 if (http_list_directory(srv, con, p, con->physical.path)) { 1176 /* dirlisting failed */ 1177 con->http_status = 403; 1178 } 1179 1180 buffer_reset(con->physical.path); 1181 1182 /* not found */ 1183 return HANDLER_FINISHED; 1184 } 1185 1186 /* this function is called at dlopen() time and inits the callbacks */ 1187 1188 int mod_dirlisting_plugin_init(plugin *p); 1189 int mod_dirlisting_plugin_init(plugin *p) { 1190 p->version = LIGHTTPD_VERSION_ID; 1191 p->name = buffer_init_string("dirlisting"); 1192 1193 p->init = mod_dirlisting_init; 1194 p->handle_subrequest_start = mod_dirlisting_subrequest; 1195 p->set_defaults = mod_dirlisting_set_defaults; 1196 p->cleanup = mod_dirlisting_free; 1197 1198 p->data = NULL; 1199 1200 return 0; 1201 } 1202