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\">&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>