Annotation of embedaddon/lighttpd/src/mod_dirlisting.c, revision 1.1.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\">&nbsp;</td>"
                    576:                        "<td class=\"s\">- &nbsp;</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\">- &nbsp;</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>