Annotation of embedaddon/lighttpd/src/mod_dirlisting.c, revision 1.1
1.1 ! misho 1: #include "base.h"
! 2: #include "log.h"
! 3: #include "buffer.h"
! 4:
! 5: #include "plugin.h"
! 6:
! 7: #include "response.h"
! 8: #include "stat_cache.h"
! 9: #include "stream.h"
! 10:
! 11: #include <ctype.h>
! 12: #include <stdlib.h>
! 13: #include <string.h>
! 14: #include <dirent.h>
! 15: #include <assert.h>
! 16: #include <errno.h>
! 17: #include <stdio.h>
! 18: #include <unistd.h>
! 19: #include <time.h>
! 20:
! 21: /**
! 22: * this is a dirlisting for a lighttpd plugin
! 23: */
! 24:
! 25:
! 26: #ifdef HAVE_SYS_SYSLIMITS_H
! 27: #include <sys/syslimits.h>
! 28: #endif
! 29:
! 30: #ifdef HAVE_ATTR_ATTRIBUTES_H
! 31: #include <attr/attributes.h>
! 32: #endif
! 33:
! 34: #include "version.h"
! 35:
! 36: /* plugin config for all request/connections */
! 37:
! 38: typedef struct {
! 39: #ifdef HAVE_PCRE_H
! 40: pcre *regex;
! 41: #endif
! 42: buffer *string;
! 43: } excludes;
! 44:
! 45: typedef struct {
! 46: excludes **ptr;
! 47:
! 48: size_t used;
! 49: size_t size;
! 50: } excludes_buffer;
! 51:
! 52: typedef struct {
! 53: unsigned short dir_listing;
! 54: unsigned short hide_dot_files;
! 55: unsigned short show_readme;
! 56: unsigned short hide_readme_file;
! 57: unsigned short encode_readme;
! 58: unsigned short show_header;
! 59: unsigned short hide_header_file;
! 60: unsigned short encode_header;
! 61: unsigned short auto_layout;
! 62:
! 63: excludes_buffer *excludes;
! 64:
! 65: buffer *external_css;
! 66: buffer *encoding;
! 67: buffer *set_footer;
! 68: } plugin_config;
! 69:
! 70: typedef struct {
! 71: PLUGIN_DATA;
! 72:
! 73: buffer *tmp_buf;
! 74: buffer *content_charset;
! 75:
! 76: plugin_config **config_storage;
! 77:
! 78: plugin_config conf;
! 79: } plugin_data;
! 80:
! 81: static excludes_buffer *excludes_buffer_init(void) {
! 82: excludes_buffer *exb;
! 83:
! 84: exb = calloc(1, sizeof(*exb));
! 85:
! 86: return exb;
! 87: }
! 88:
! 89: static int excludes_buffer_append(excludes_buffer *exb, buffer *string) {
! 90: #ifdef HAVE_PCRE_H
! 91: size_t i;
! 92: const char *errptr;
! 93: int erroff;
! 94:
! 95: if (!string) return -1;
! 96:
! 97: if (exb->size == 0) {
! 98: exb->size = 4;
! 99: exb->used = 0;
! 100:
! 101: exb->ptr = malloc(exb->size * sizeof(*exb->ptr));
! 102:
! 103: for(i = 0; i < exb->size ; i++) {
! 104: exb->ptr[i] = calloc(1, sizeof(**exb->ptr));
! 105: }
! 106: } else if (exb->used == exb->size) {
! 107: exb->size += 4;
! 108:
! 109: exb->ptr = realloc(exb->ptr, exb->size * sizeof(*exb->ptr));
! 110:
! 111: for(i = exb->used; i < exb->size; i++) {
! 112: exb->ptr[i] = calloc(1, sizeof(**exb->ptr));
! 113: }
! 114: }
! 115:
! 116:
! 117: if (NULL == (exb->ptr[exb->used]->regex = pcre_compile(string->ptr, 0,
! 118: &errptr, &erroff, NULL))) {
! 119: return -1;
! 120: }
! 121:
! 122: exb->ptr[exb->used]->string = buffer_init();
! 123: buffer_copy_string_buffer(exb->ptr[exb->used]->string, string);
! 124:
! 125: exb->used++;
! 126:
! 127: return 0;
! 128: #else
! 129: UNUSED(exb);
! 130: UNUSED(string);
! 131:
! 132: return -1;
! 133: #endif
! 134: }
! 135:
! 136: static void excludes_buffer_free(excludes_buffer *exb) {
! 137: #ifdef HAVE_PCRE_H
! 138: size_t i;
! 139:
! 140: for (i = 0; i < exb->size; i++) {
! 141: if (exb->ptr[i]->regex) pcre_free(exb->ptr[i]->regex);
! 142: if (exb->ptr[i]->string) buffer_free(exb->ptr[i]->string);
! 143: free(exb->ptr[i]);
! 144: }
! 145:
! 146: if (exb->ptr) free(exb->ptr);
! 147: #endif
! 148:
! 149: free(exb);
! 150: }
! 151:
! 152: /* init the plugin data */
! 153: INIT_FUNC(mod_dirlisting_init) {
! 154: plugin_data *p;
! 155:
! 156: p = calloc(1, sizeof(*p));
! 157:
! 158: p->tmp_buf = buffer_init();
! 159: p->content_charset = buffer_init();
! 160:
! 161: return p;
! 162: }
! 163:
! 164: /* detroy the plugin data */
! 165: FREE_FUNC(mod_dirlisting_free) {
! 166: plugin_data *p = p_d;
! 167:
! 168: UNUSED(srv);
! 169:
! 170: if (!p) return HANDLER_GO_ON;
! 171:
! 172: if (p->config_storage) {
! 173: size_t i;
! 174: for (i = 0; i < srv->config_context->used; i++) {
! 175: plugin_config *s = p->config_storage[i];
! 176:
! 177: if (!s) continue;
! 178:
! 179: excludes_buffer_free(s->excludes);
! 180: buffer_free(s->external_css);
! 181: buffer_free(s->encoding);
! 182: buffer_free(s->set_footer);
! 183:
! 184: free(s);
! 185: }
! 186: free(p->config_storage);
! 187: }
! 188:
! 189: buffer_free(p->tmp_buf);
! 190: buffer_free(p->content_charset);
! 191:
! 192: free(p);
! 193:
! 194: return HANDLER_GO_ON;
! 195: }
! 196:
! 197: static int parse_config_entry(server *srv, plugin_config *s, array *ca, const char *option) {
! 198: data_unset *du;
! 199:
! 200: if (NULL != (du = array_get_element(ca, option))) {
! 201: data_array *da;
! 202: size_t j;
! 203:
! 204: if (du->type != TYPE_ARRAY) {
! 205: log_error_write(srv, __FILE__, __LINE__, "sss",
! 206: "unexpected type for key: ", option, "array of strings");
! 207:
! 208: return HANDLER_ERROR;
! 209: }
! 210:
! 211: da = (data_array *)du;
! 212:
! 213: for (j = 0; j < da->value->used; j++) {
! 214: if (da->value->data[j]->type != TYPE_STRING) {
! 215: log_error_write(srv, __FILE__, __LINE__, "sssbs",
! 216: "unexpected type for key: ", option, "[",
! 217: da->value->data[j]->key, "](string)");
! 218:
! 219: return HANDLER_ERROR;
! 220: }
! 221:
! 222: if (0 != excludes_buffer_append(s->excludes,
! 223: ((data_string *)(da->value->data[j]))->value)) {
! 224: #ifdef HAVE_PCRE_H
! 225: log_error_write(srv, __FILE__, __LINE__, "sb",
! 226: "pcre-compile failed for", ((data_string *)(da->value->data[j]))->value);
! 227: #else
! 228: log_error_write(srv, __FILE__, __LINE__, "s",
! 229: "pcre support is missing, please install libpcre and the headers");
! 230: #endif
! 231: }
! 232: }
! 233: }
! 234:
! 235: return 0;
! 236: }
! 237:
! 238: /* handle plugin config and check values */
! 239:
! 240: #define CONFIG_EXCLUDE "dir-listing.exclude"
! 241: #define CONFIG_ACTIVATE "dir-listing.activate"
! 242: #define CONFIG_HIDE_DOTFILES "dir-listing.hide-dotfiles"
! 243: #define CONFIG_EXTERNAL_CSS "dir-listing.external-css"
! 244: #define CONFIG_ENCODING "dir-listing.encoding"
! 245: #define CONFIG_SHOW_README "dir-listing.show-readme"
! 246: #define CONFIG_HIDE_README_FILE "dir-listing.hide-readme-file"
! 247: #define CONFIG_SHOW_HEADER "dir-listing.show-header"
! 248: #define CONFIG_HIDE_HEADER_FILE "dir-listing.hide-header-file"
! 249: #define CONFIG_DIR_LISTING "server.dir-listing"
! 250: #define CONFIG_SET_FOOTER "dir-listing.set-footer"
! 251: #define CONFIG_ENCODE_README "dir-listing.encode-readme"
! 252: #define CONFIG_ENCODE_HEADER "dir-listing.encode-header"
! 253: #define CONFIG_AUTO_LAYOUT "dir-listing.auto-layout"
! 254:
! 255:
! 256: SETDEFAULTS_FUNC(mod_dirlisting_set_defaults) {
! 257: plugin_data *p = p_d;
! 258: size_t i = 0;
! 259:
! 260: config_values_t cv[] = {
! 261: { CONFIG_EXCLUDE, NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */
! 262: { CONFIG_ACTIVATE, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */
! 263: { CONFIG_HIDE_DOTFILES, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 2 */
! 264: { CONFIG_EXTERNAL_CSS, NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */
! 265: { CONFIG_ENCODING, NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 4 */
! 266: { CONFIG_SHOW_README, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 5 */
! 267: { CONFIG_HIDE_README_FILE, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 6 */
! 268: { CONFIG_SHOW_HEADER, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 7 */
! 269: { CONFIG_HIDE_HEADER_FILE, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 8 */
! 270: { CONFIG_DIR_LISTING, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 9 */
! 271: { CONFIG_SET_FOOTER, NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 10 */
! 272: { CONFIG_ENCODE_README, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 11 */
! 273: { CONFIG_ENCODE_HEADER, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 12 */
! 274: { CONFIG_AUTO_LAYOUT, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 13 */
! 275:
! 276: { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
! 277: };
! 278:
! 279: if (!p) return HANDLER_ERROR;
! 280:
! 281: p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
! 282:
! 283: for (i = 0; i < srv->config_context->used; i++) {
! 284: plugin_config *s;
! 285: array *ca;
! 286:
! 287: s = calloc(1, sizeof(plugin_config));
! 288: s->excludes = excludes_buffer_init();
! 289: s->dir_listing = 0;
! 290: s->external_css = buffer_init();
! 291: s->hide_dot_files = 0;
! 292: s->show_readme = 0;
! 293: s->hide_readme_file = 0;
! 294: s->show_header = 0;
! 295: s->hide_header_file = 0;
! 296: s->encode_readme = 1;
! 297: s->encode_header = 1;
! 298: s->auto_layout = 1;
! 299:
! 300: s->encoding = buffer_init();
! 301: s->set_footer = buffer_init();
! 302:
! 303: cv[0].destination = s->excludes;
! 304: cv[1].destination = &(s->dir_listing);
! 305: cv[2].destination = &(s->hide_dot_files);
! 306: cv[3].destination = s->external_css;
! 307: cv[4].destination = s->encoding;
! 308: cv[5].destination = &(s->show_readme);
! 309: cv[6].destination = &(s->hide_readme_file);
! 310: cv[7].destination = &(s->show_header);
! 311: cv[8].destination = &(s->hide_header_file);
! 312: cv[9].destination = &(s->dir_listing); /* old name */
! 313: cv[10].destination = s->set_footer;
! 314: cv[11].destination = &(s->encode_readme);
! 315: cv[12].destination = &(s->encode_header);
! 316: cv[13].destination = &(s->auto_layout);
! 317:
! 318: p->config_storage[i] = s;
! 319: ca = ((data_config *)srv->config_context->data[i])->value;
! 320:
! 321: if (0 != config_insert_values_global(srv, ca, cv)) {
! 322: return HANDLER_ERROR;
! 323: }
! 324:
! 325: parse_config_entry(srv, s, ca, CONFIG_EXCLUDE);
! 326: }
! 327:
! 328: return HANDLER_GO_ON;
! 329: }
! 330:
! 331: #define PATCH(x) \
! 332: p->conf.x = s->x;
! 333: static int mod_dirlisting_patch_connection(server *srv, connection *con, plugin_data *p) {
! 334: size_t i, j;
! 335: plugin_config *s = p->config_storage[0];
! 336:
! 337: PATCH(dir_listing);
! 338: PATCH(external_css);
! 339: PATCH(hide_dot_files);
! 340: PATCH(encoding);
! 341: PATCH(show_readme);
! 342: PATCH(hide_readme_file);
! 343: PATCH(show_header);
! 344: PATCH(hide_header_file);
! 345: PATCH(excludes);
! 346: PATCH(set_footer);
! 347: PATCH(encode_readme);
! 348: PATCH(encode_header);
! 349: PATCH(auto_layout);
! 350:
! 351: /* skip the first, the global context */
! 352: for (i = 1; i < srv->config_context->used; i++) {
! 353: data_config *dc = (data_config *)srv->config_context->data[i];
! 354: s = p->config_storage[i];
! 355:
! 356: /* condition didn't match */
! 357: if (!config_check_cond(srv, con, dc)) continue;
! 358:
! 359: /* merge config */
! 360: for (j = 0; j < dc->value->used; j++) {
! 361: data_unset *du = dc->value->data[j];
! 362:
! 363: if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_ACTIVATE)) ||
! 364: buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_DIR_LISTING))) {
! 365: PATCH(dir_listing);
! 366: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_HIDE_DOTFILES))) {
! 367: PATCH(hide_dot_files);
! 368: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_EXTERNAL_CSS))) {
! 369: PATCH(external_css);
! 370: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_ENCODING))) {
! 371: PATCH(encoding);
! 372: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_SHOW_README))) {
! 373: PATCH(show_readme);
! 374: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_HIDE_README_FILE))) {
! 375: PATCH(hide_readme_file);
! 376: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_SHOW_HEADER))) {
! 377: PATCH(show_header);
! 378: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_HIDE_HEADER_FILE))) {
! 379: PATCH(hide_header_file);
! 380: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_SET_FOOTER))) {
! 381: PATCH(set_footer);
! 382: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_EXCLUDE))) {
! 383: PATCH(excludes);
! 384: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_ENCODE_README))) {
! 385: PATCH(encode_readme);
! 386: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_ENCODE_HEADER))) {
! 387: PATCH(encode_header);
! 388: } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_AUTO_LAYOUT))) {
! 389: PATCH(auto_layout);
! 390: }
! 391: }
! 392: }
! 393:
! 394: return 0;
! 395: }
! 396: #undef PATCH
! 397:
! 398: typedef struct {
! 399: size_t namelen;
! 400: time_t mtime;
! 401: off_t size;
! 402: } dirls_entry_t;
! 403:
! 404: typedef struct {
! 405: dirls_entry_t **ent;
! 406: size_t used;
! 407: size_t size;
! 408: } dirls_list_t;
! 409:
! 410: #define DIRLIST_ENT_NAME(ent) ((char*)(ent) + sizeof(dirls_entry_t))
! 411: #define DIRLIST_BLOB_SIZE 16
! 412:
! 413: /* simple combsort algorithm */
! 414: static void http_dirls_sort(dirls_entry_t **ent, int num) {
! 415: int gap = num;
! 416: int i, j;
! 417: int swapped;
! 418: dirls_entry_t *tmp;
! 419:
! 420: do {
! 421: gap = (gap * 10) / 13;
! 422: if (gap == 9 || gap == 10)
! 423: gap = 11;
! 424: if (gap < 1)
! 425: gap = 1;
! 426: swapped = 0;
! 427:
! 428: for (i = 0; i < num - gap; i++) {
! 429: j = i + gap;
! 430: if (strcmp(DIRLIST_ENT_NAME(ent[i]), DIRLIST_ENT_NAME(ent[j])) > 0) {
! 431: tmp = ent[i];
! 432: ent[i] = ent[j];
! 433: ent[j] = tmp;
! 434: swapped = 1;
! 435: }
! 436: }
! 437:
! 438: } while (gap > 1 || swapped);
! 439: }
! 440:
! 441: /* buffer must be able to hold "999.9K"
! 442: * conversion is simple but not perfect
! 443: */
! 444: static int http_list_directory_sizefmt(char *buf, off_t size) {
! 445: const char unit[] = "KMGTPE"; /* Kilo, Mega, Tera, Peta, Exa */
! 446: const char *u = unit - 1; /* u will always increment at least once */
! 447: int remain;
! 448: char *out = buf;
! 449:
! 450: if (size < 100)
! 451: size += 99;
! 452: if (size < 100)
! 453: size = 0;
! 454:
! 455: while (1) {
! 456: remain = (int) size & 1023;
! 457: size >>= 10;
! 458: u++;
! 459: if ((size & (~0 ^ 1023)) == 0)
! 460: break;
! 461: }
! 462:
! 463: remain /= 100;
! 464: if (remain > 9)
! 465: remain = 9;
! 466: if (size > 999) {
! 467: size = 0;
! 468: remain = 9;
! 469: u++;
! 470: }
! 471:
! 472: out += LI_ltostr(out, size);
! 473: out[0] = '.';
! 474: out[1] = remain + '0';
! 475: out[2] = *u;
! 476: out[3] = '\0';
! 477:
! 478: return (out + 3 - buf);
! 479: }
! 480:
! 481: static void http_list_directory_header(server *srv, connection *con, plugin_data *p, buffer *out) {
! 482: UNUSED(srv);
! 483:
! 484: if (p->conf.auto_layout) {
! 485: buffer_append_string_len(out, CONST_STR_LEN(
! 486: "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n"
! 487: "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">\n"
! 488: "<head>\n"
! 489: "<title>Index of "
! 490: ));
! 491: buffer_append_string_encoded(out, CONST_BUF_LEN(con->uri.path), ENCODING_MINIMAL_XML);
! 492: buffer_append_string_len(out, CONST_STR_LEN("</title>\n"));
! 493:
! 494: if (p->conf.external_css->used > 1) {
! 495: buffer_append_string_len(out, CONST_STR_LEN("<link rel=\"stylesheet\" type=\"text/css\" href=\""));
! 496: buffer_append_string_buffer(out, p->conf.external_css);
! 497: buffer_append_string_len(out, CONST_STR_LEN("\" />\n"));
! 498: } else {
! 499: buffer_append_string_len(out, CONST_STR_LEN(
! 500: "<style type=\"text/css\">\n"
! 501: "a, a:active {text-decoration: none; color: blue;}\n"
! 502: "a:visited {color: #48468F;}\n"
! 503: "a:hover, a:focus {text-decoration: underline; color: red;}\n"
! 504: "body {background-color: #F5F5F5;}\n"
! 505: "h2 {margin-bottom: 12px;}\n"
! 506: "table {margin-left: 12px;}\n"
! 507: "th, td {"
! 508: " font: 90% monospace;"
! 509: " text-align: left;"
! 510: "}\n"
! 511: "th {"
! 512: " font-weight: bold;"
! 513: " padding-right: 14px;"
! 514: " padding-bottom: 3px;"
! 515: "}\n"
! 516: "td {padding-right: 14px;}\n"
! 517: "td.s, th.s {text-align: right;}\n"
! 518: "div.list {"
! 519: " background-color: white;"
! 520: " border-top: 1px solid #646464;"
! 521: " border-bottom: 1px solid #646464;"
! 522: " padding-top: 10px;"
! 523: " padding-bottom: 14px;"
! 524: "}\n"
! 525: "div.foot {"
! 526: " font: 90% monospace;"
! 527: " color: #787878;"
! 528: " padding-top: 4px;"
! 529: "}\n"
! 530: "</style>\n"
! 531: ));
! 532: }
! 533:
! 534: buffer_append_string_len(out, CONST_STR_LEN("</head>\n<body>\n"));
! 535: }
! 536:
! 537: /* HEADER.txt */
! 538: if (p->conf.show_header) {
! 539: stream s;
! 540: /* if we have a HEADER file, display it in <pre class="header"></pre> */
! 541:
! 542: buffer_copy_string_buffer(p->tmp_buf, con->physical.path);
! 543: BUFFER_APPEND_SLASH(p->tmp_buf);
! 544: buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("HEADER.txt"));
! 545:
! 546: if (-1 != stream_open(&s, p->tmp_buf)) {
! 547: if (p->conf.encode_header) {
! 548: buffer_append_string_len(out, CONST_STR_LEN("<pre class=\"header\">"));
! 549: buffer_append_string_encoded(out, s.start, s.size, ENCODING_MINIMAL_XML);
! 550: buffer_append_string_len(out, CONST_STR_LEN("</pre>"));
! 551: } else {
! 552: buffer_append_string_len(out, s.start, s.size);
! 553: }
! 554: }
! 555: stream_close(&s);
! 556: }
! 557:
! 558: buffer_append_string_len(out, CONST_STR_LEN("<h2>Index of "));
! 559: buffer_append_string_encoded(out, CONST_BUF_LEN(con->uri.path), ENCODING_MINIMAL_XML);
! 560: buffer_append_string_len(out, CONST_STR_LEN(
! 561: "</h2>\n"
! 562: "<div class=\"list\">\n"
! 563: "<table summary=\"Directory Listing\" cellpadding=\"0\" cellspacing=\"0\">\n"
! 564: "<thead>"
! 565: "<tr>"
! 566: "<th class=\"n\">Name</th>"
! 567: "<th class=\"m\">Last Modified</th>"
! 568: "<th class=\"s\">Size</th>"
! 569: "<th class=\"t\">Type</th>"
! 570: "</tr>"
! 571: "</thead>\n"
! 572: "<tbody>\n"
! 573: "<tr>"
! 574: "<td class=\"n\"><a href=\"../\">Parent Directory</a>/</td>"
! 575: "<td class=\"m\"> </td>"
! 576: "<td class=\"s\">- </td>"
! 577: "<td class=\"t\">Directory</td>"
! 578: "</tr>\n"
! 579: ));
! 580: }
! 581:
! 582: static void http_list_directory_footer(server *srv, connection *con, plugin_data *p, buffer *out) {
! 583: UNUSED(srv);
! 584:
! 585: buffer_append_string_len(out, CONST_STR_LEN(
! 586: "</tbody>\n"
! 587: "</table>\n"
! 588: "</div>\n"
! 589: ));
! 590:
! 591: if (p->conf.show_readme) {
! 592: stream s;
! 593: /* if we have a README file, display it in <pre class="readme"></pre> */
! 594:
! 595: buffer_copy_string_buffer(p->tmp_buf, con->physical.path);
! 596: BUFFER_APPEND_SLASH(p->tmp_buf);
! 597: buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("README.txt"));
! 598:
! 599: if (-1 != stream_open(&s, p->tmp_buf)) {
! 600: if (p->conf.encode_readme) {
! 601: buffer_append_string_len(out, CONST_STR_LEN("<pre class=\"readme\">"));
! 602: buffer_append_string_encoded(out, s.start, s.size, ENCODING_MINIMAL_XML);
! 603: buffer_append_string_len(out, CONST_STR_LEN("</pre>"));
! 604: } else {
! 605: buffer_append_string_len(out, s.start, s.size);
! 606: }
! 607: }
! 608: stream_close(&s);
! 609: }
! 610:
! 611: if(p->conf.auto_layout) {
! 612: buffer_append_string_len(out, CONST_STR_LEN(
! 613: "<div class=\"foot\">"
! 614: ));
! 615:
! 616: if (p->conf.set_footer->used > 1) {
! 617: buffer_append_string_buffer(out, p->conf.set_footer);
! 618: } else if (buffer_is_empty(con->conf.server_tag)) {
! 619: buffer_append_string_len(out, CONST_STR_LEN(PACKAGE_DESC));
! 620: } else {
! 621: buffer_append_string_buffer(out, con->conf.server_tag);
! 622: }
! 623:
! 624: buffer_append_string_len(out, CONST_STR_LEN(
! 625: "</div>\n"
! 626: "</body>\n"
! 627: "</html>\n"
! 628: ));
! 629: }
! 630: }
! 631:
! 632: static int http_list_directory(server *srv, connection *con, plugin_data *p, buffer *dir) {
! 633: DIR *dp;
! 634: buffer *out;
! 635: struct dirent *dent;
! 636: struct stat st;
! 637: char *path, *path_file;
! 638: size_t i;
! 639: int hide_dotfiles = p->conf.hide_dot_files;
! 640: dirls_list_t dirs, files, *list;
! 641: dirls_entry_t *tmp;
! 642: char sizebuf[sizeof("999.9K")];
! 643: char datebuf[sizeof("2005-Jan-01 22:23:24")];
! 644: size_t k;
! 645: const char *content_type;
! 646: long name_max;
! 647: #ifdef HAVE_XATTR
! 648: char attrval[128];
! 649: int attrlen;
! 650: #endif
! 651: #ifdef HAVE_LOCALTIME_R
! 652: struct tm tm;
! 653: #endif
! 654:
! 655: if (dir->used == 0) return -1;
! 656:
! 657: i = dir->used - 1;
! 658:
! 659: #ifdef HAVE_PATHCONF
! 660: if (0 >= (name_max = pathconf(dir->ptr, _PC_NAME_MAX))) {
! 661: /* some broken fs (fuse) return 0 instead of -1 */
! 662: #ifdef NAME_MAX
! 663: name_max = NAME_MAX;
! 664: #else
! 665: name_max = 255; /* stupid default */
! 666: #endif
! 667: }
! 668: #elif defined __WIN32
! 669: name_max = FILENAME_MAX;
! 670: #else
! 671: name_max = NAME_MAX;
! 672: #endif
! 673:
! 674: path = malloc(dir->used + name_max);
! 675: assert(path);
! 676: strcpy(path, dir->ptr);
! 677: path_file = path + i;
! 678:
! 679: if (NULL == (dp = opendir(path))) {
! 680: log_error_write(srv, __FILE__, __LINE__, "sbs",
! 681: "opendir failed:", dir, strerror(errno));
! 682:
! 683: free(path);
! 684: return -1;
! 685: }
! 686:
! 687: dirs.ent = (dirls_entry_t**) malloc(sizeof(dirls_entry_t*) * DIRLIST_BLOB_SIZE);
! 688: assert(dirs.ent);
! 689: dirs.size = DIRLIST_BLOB_SIZE;
! 690: dirs.used = 0;
! 691: files.ent = (dirls_entry_t**) malloc(sizeof(dirls_entry_t*) * DIRLIST_BLOB_SIZE);
! 692: assert(files.ent);
! 693: files.size = DIRLIST_BLOB_SIZE;
! 694: files.used = 0;
! 695:
! 696: while ((dent = readdir(dp)) != NULL) {
! 697: unsigned short exclude_match = 0;
! 698:
! 699: if (dent->d_name[0] == '.') {
! 700: if (hide_dotfiles)
! 701: continue;
! 702: if (dent->d_name[1] == '\0')
! 703: continue;
! 704: if (dent->d_name[1] == '.' && dent->d_name[2] == '\0')
! 705: continue;
! 706: }
! 707:
! 708: if (p->conf.hide_readme_file) {
! 709: if (strcmp(dent->d_name, "README.txt") == 0)
! 710: continue;
! 711: }
! 712: if (p->conf.hide_header_file) {
! 713: if (strcmp(dent->d_name, "HEADER.txt") == 0)
! 714: continue;
! 715: }
! 716:
! 717: /* compare d_name against excludes array
! 718: * elements, skipping any that match.
! 719: */
! 720: #ifdef HAVE_PCRE_H
! 721: for(i = 0; i < p->conf.excludes->used; i++) {
! 722: int n;
! 723: #define N 10
! 724: int ovec[N * 3];
! 725: pcre *regex = p->conf.excludes->ptr[i]->regex;
! 726:
! 727: if ((n = pcre_exec(regex, NULL, dent->d_name,
! 728: strlen(dent->d_name), 0, 0, ovec, 3 * N)) < 0) {
! 729: if (n != PCRE_ERROR_NOMATCH) {
! 730: log_error_write(srv, __FILE__, __LINE__, "sd",
! 731: "execution error while matching:", n);
! 732:
! 733: return -1;
! 734: }
! 735: }
! 736: else {
! 737: exclude_match = 1;
! 738: break;
! 739: }
! 740: }
! 741:
! 742: if (exclude_match) {
! 743: continue;
! 744: }
! 745: #endif
! 746:
! 747: i = strlen(dent->d_name);
! 748:
! 749: /* NOTE: the manual says, d_name is never more than NAME_MAX
! 750: * so this should actually not be a buffer-overflow-risk
! 751: */
! 752: if (i > (size_t)name_max) continue;
! 753:
! 754: memcpy(path_file, dent->d_name, i + 1);
! 755: if (stat(path, &st) != 0)
! 756: continue;
! 757:
! 758: list = &files;
! 759: if (S_ISDIR(st.st_mode))
! 760: list = &dirs;
! 761:
! 762: if (list->used == list->size) {
! 763: list->size += DIRLIST_BLOB_SIZE;
! 764: list->ent = (dirls_entry_t**) realloc(list->ent, sizeof(dirls_entry_t*) * list->size);
! 765: assert(list->ent);
! 766: }
! 767:
! 768: tmp = (dirls_entry_t*) malloc(sizeof(dirls_entry_t) + 1 + i);
! 769: tmp->mtime = st.st_mtime;
! 770: tmp->size = st.st_size;
! 771: tmp->namelen = i;
! 772: memcpy(DIRLIST_ENT_NAME(tmp), dent->d_name, i + 1);
! 773:
! 774: list->ent[list->used++] = tmp;
! 775: }
! 776: closedir(dp);
! 777:
! 778: if (dirs.used) http_dirls_sort(dirs.ent, dirs.used);
! 779:
! 780: if (files.used) http_dirls_sort(files.ent, files.used);
! 781:
! 782: out = chunkqueue_get_append_buffer(con->write_queue);
! 783: buffer_copy_string_len(out, CONST_STR_LEN("<?xml version=\"1.0\" encoding=\""));
! 784: if (buffer_is_empty(p->conf.encoding)) {
! 785: buffer_append_string_len(out, CONST_STR_LEN("iso-8859-1"));
! 786: } else {
! 787: buffer_append_string_buffer(out, p->conf.encoding);
! 788: }
! 789: buffer_append_string_len(out, CONST_STR_LEN("\"?>\n"));
! 790: http_list_directory_header(srv, con, p, out);
! 791:
! 792: /* directories */
! 793: for (i = 0; i < dirs.used; i++) {
! 794: tmp = dirs.ent[i];
! 795:
! 796: #ifdef HAVE_LOCALTIME_R
! 797: localtime_r(&(tmp->mtime), &tm);
! 798: strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", &tm);
! 799: #else
! 800: strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", localtime(&(tmp->mtime)));
! 801: #endif
! 802:
! 803: buffer_append_string_len(out, CONST_STR_LEN("<tr><td class=\"n\"><a href=\""));
! 804: buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_REL_URI_PART);
! 805: buffer_append_string_len(out, CONST_STR_LEN("/\">"));
! 806: buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_MINIMAL_XML);
! 807: buffer_append_string_len(out, CONST_STR_LEN("</a>/</td><td class=\"m\">"));
! 808: buffer_append_string_len(out, datebuf, sizeof(datebuf) - 1);
! 809: buffer_append_string_len(out, CONST_STR_LEN("</td><td class=\"s\">- </td><td class=\"t\">Directory</td></tr>\n"));
! 810:
! 811: free(tmp);
! 812: }
! 813:
! 814: /* files */
! 815: for (i = 0; i < files.used; i++) {
! 816: tmp = files.ent[i];
! 817:
! 818: content_type = NULL;
! 819: #ifdef HAVE_XATTR
! 820:
! 821: if (con->conf.use_xattr) {
! 822: memcpy(path_file, DIRLIST_ENT_NAME(tmp), tmp->namelen + 1);
! 823: attrlen = sizeof(attrval) - 1;
! 824: if (attr_get(path, "Content-Type", attrval, &attrlen, 0) == 0) {
! 825: attrval[attrlen] = '\0';
! 826: content_type = attrval;
! 827: }
! 828: }
! 829: #endif
! 830:
! 831: if (content_type == NULL) {
! 832: content_type = "application/octet-stream";
! 833: for (k = 0; k < con->conf.mimetypes->used; k++) {
! 834: data_string *ds = (data_string *)con->conf.mimetypes->data[k];
! 835: size_t ct_len;
! 836:
! 837: if (ds->key->used == 0)
! 838: continue;
! 839:
! 840: ct_len = ds->key->used - 1;
! 841: if (tmp->namelen < ct_len)
! 842: continue;
! 843:
! 844: if (0 == strncasecmp(DIRLIST_ENT_NAME(tmp) + tmp->namelen - ct_len, ds->key->ptr, ct_len)) {
! 845: content_type = ds->value->ptr;
! 846: break;
! 847: }
! 848: }
! 849: }
! 850:
! 851: #ifdef HAVE_LOCALTIME_R
! 852: localtime_r(&(tmp->mtime), &tm);
! 853: strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", &tm);
! 854: #else
! 855: strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", localtime(&(tmp->mtime)));
! 856: #endif
! 857: http_list_directory_sizefmt(sizebuf, tmp->size);
! 858:
! 859: buffer_append_string_len(out, CONST_STR_LEN("<tr><td class=\"n\"><a href=\""));
! 860: buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_REL_URI_PART);
! 861: buffer_append_string_len(out, CONST_STR_LEN("\">"));
! 862: buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_MINIMAL_XML);
! 863: buffer_append_string_len(out, CONST_STR_LEN("</a></td><td class=\"m\">"));
! 864: buffer_append_string_len(out, datebuf, sizeof(datebuf) - 1);
! 865: buffer_append_string_len(out, CONST_STR_LEN("</td><td class=\"s\">"));
! 866: buffer_append_string(out, sizebuf);
! 867: buffer_append_string_len(out, CONST_STR_LEN("</td><td class=\"t\">"));
! 868: buffer_append_string(out, content_type);
! 869: buffer_append_string_len(out, CONST_STR_LEN("</td></tr>\n"));
! 870:
! 871: free(tmp);
! 872: }
! 873:
! 874: free(files.ent);
! 875: free(dirs.ent);
! 876: free(path);
! 877:
! 878: http_list_directory_footer(srv, con, p, out);
! 879:
! 880: /* Insert possible charset to Content-Type */
! 881: if (buffer_is_empty(p->conf.encoding)) {
! 882: response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html"));
! 883: } else {
! 884: buffer_copy_string_len(p->content_charset, CONST_STR_LEN("text/html; charset="));
! 885: buffer_append_string_buffer(p->content_charset, p->conf.encoding);
! 886: response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->content_charset));
! 887: }
! 888:
! 889: con->file_finished = 1;
! 890:
! 891: return 0;
! 892: }
! 893:
! 894:
! 895:
! 896: URIHANDLER_FUNC(mod_dirlisting_subrequest) {
! 897: plugin_data *p = p_d;
! 898: stat_cache_entry *sce = NULL;
! 899:
! 900: UNUSED(srv);
! 901:
! 902: /* we only handle GET, POST and HEAD */
! 903: switch(con->request.http_method) {
! 904: case HTTP_METHOD_GET:
! 905: case HTTP_METHOD_POST:
! 906: case HTTP_METHOD_HEAD:
! 907: break;
! 908: default:
! 909: return HANDLER_GO_ON;
! 910: }
! 911:
! 912: if (con->mode != DIRECT) return HANDLER_GO_ON;
! 913:
! 914: if (con->physical.path->used == 0) return HANDLER_GO_ON;
! 915: if (con->uri.path->used == 0) return HANDLER_GO_ON;
! 916: if (con->uri.path->ptr[con->uri.path->used - 2] != '/') return HANDLER_GO_ON;
! 917:
! 918: mod_dirlisting_patch_connection(srv, con, p);
! 919:
! 920: if (!p->conf.dir_listing) return HANDLER_GO_ON;
! 921:
! 922: if (con->conf.log_request_handling) {
! 923: log_error_write(srv, __FILE__, __LINE__, "s", "-- handling the request as Dir-Listing");
! 924: log_error_write(srv, __FILE__, __LINE__, "sb", "URI :", con->uri.path);
! 925: }
! 926:
! 927: if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
! 928: log_error_write(srv, __FILE__, __LINE__, "SB", "stat_cache_get_entry failed: ", con->physical.path);
! 929: SEGFAULT();
! 930: }
! 931:
! 932: if (!S_ISDIR(sce->st.st_mode)) return HANDLER_GO_ON;
! 933:
! 934: if (http_list_directory(srv, con, p, con->physical.path)) {
! 935: /* dirlisting failed */
! 936: con->http_status = 403;
! 937: }
! 938:
! 939: buffer_reset(con->physical.path);
! 940:
! 941: /* not found */
! 942: return HANDLER_FINISHED;
! 943: }
! 944:
! 945: /* this function is called at dlopen() time and inits the callbacks */
! 946:
! 947: int mod_dirlisting_plugin_init(plugin *p);
! 948: int mod_dirlisting_plugin_init(plugin *p) {
! 949: p->version = LIGHTTPD_VERSION_ID;
! 950: p->name = buffer_init_string("dirlisting");
! 951:
! 952: p->init = mod_dirlisting_init;
! 953: p->handle_subrequest_start = mod_dirlisting_subrequest;
! 954: p->set_defaults = mod_dirlisting_set_defaults;
! 955: p->cleanup = mod_dirlisting_free;
! 956:
! 957: p->data = NULL;
! 958:
! 959: return 0;
! 960: }
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>