diff options
Diffstat (limited to 'src/mod_dirlisting.c')
-rw-r--r-- | src/mod_dirlisting.c | 957 |
1 files changed, 0 insertions, 957 deletions
diff --git a/src/mod_dirlisting.c b/src/mod_dirlisting.c deleted file mode 100644 index 63f412dc..00000000 --- a/src/mod_dirlisting.c +++ /dev/null @@ -1,957 +0,0 @@ -#include <ctype.h> -#include <stdlib.h> -#include <string.h> -#include <assert.h> -#include <errno.h> -#include <stdio.h> -#include <time.h> - -#include "base.h" -#include "log.h" -#include "buffer.h" - -#include "plugin.h" - -#include "response.h" -#include "stat_cache.h" -#include "stream.h" -#include "etag.h" - -#include "sys-strings.h" - -/** - * this is a dirlisting for a lighttpd plugin - */ - - -#ifdef HAVE_SYS_SYSLIMITS_H -#include <sys/syslimits.h> -#endif - -#ifdef HAVE_XATTR -#include <attr/attributes.h> -#endif - -#include "sys-files.h" -#include "sys-strings.h" - -/* plugin config for all request/connections */ - -typedef struct { -#ifdef HAVE_PCRE_H - pcre *regex; -#endif - buffer *string; -} excludes; - -typedef struct { - excludes **ptr; - - size_t used; - size_t size; -} excludes_buffer; - -typedef struct { - unsigned short dir_listing; - unsigned short hide_dot_files; - unsigned short show_readme; - unsigned short hide_readme_file; - unsigned short show_header; - unsigned short hide_header_file; - - excludes_buffer *excludes; - - buffer *external_css; - buffer *encoding; - buffer *set_footer; -} plugin_config; - -typedef struct { - PLUGIN_DATA; - - buffer *tmp_buf; - buffer *content_charset; - buffer *path; - - plugin_config **config_storage; - - plugin_config conf; -} plugin_data; - -static excludes_buffer *excludes_buffer_init(void) { - excludes_buffer *exb; - - exb = calloc(1, sizeof(*exb)); - - return exb; -} - -static int excludes_buffer_append(excludes_buffer *exb, buffer *string) { -#ifdef HAVE_PCRE_H - size_t i; - const char *errptr; - int erroff; - - if (!string) return -1; - - if (exb->size == 0) { - exb->size = 4; - exb->used = 0; - - exb->ptr = malloc(exb->size * sizeof(*exb->ptr)); - - for(i = 0; i < exb->size ; i++) { - exb->ptr[i] = calloc(1, sizeof(**exb->ptr)); - } - } else if (exb->used == exb->size) { - exb->size += 4; - - exb->ptr = realloc(exb->ptr, exb->size * sizeof(*exb->ptr)); - - for(i = exb->used; i < exb->size; i++) { - exb->ptr[i] = calloc(1, sizeof(**exb->ptr)); - } - } - - - if (NULL == (exb->ptr[exb->used]->regex = pcre_compile(string->ptr, 0, - &errptr, &erroff, NULL))) { - return -1; - } - - exb->ptr[exb->used]->string = buffer_init(); - buffer_copy_string_buffer(exb->ptr[exb->used]->string, string); - - exb->used++; - - return 0; -#else - UNUSED(exb); - UNUSED(string); - - return -1; -#endif -} - -static void excludes_buffer_free(excludes_buffer *exb) { -#ifdef HAVE_PCRE_H - size_t i; - - for (i = 0; i < exb->size; i++) { - if (exb->ptr[i]->regex) pcre_free(exb->ptr[i]->regex); - if (exb->ptr[i]->string) buffer_free(exb->ptr[i]->string); - free(exb->ptr[i]); - } - - if (exb->ptr) free(exb->ptr); -#endif - - free(exb); -} - -/* init the plugin data */ -INIT_FUNC(mod_dirlisting_init) { - plugin_data *p; - - UNUSED(srv); - - p = calloc(1, sizeof(*p)); - - p->tmp_buf = buffer_init(); - p->content_charset = buffer_init(); - p->path = buffer_init(); - - return p; -} - -/* destroy the plugin data */ -FREE_FUNC(mod_dirlisting_free) { - plugin_data *p = p_d; - - UNUSED(srv); - - if (!p) return HANDLER_GO_ON; - - if (p->config_storage) { - size_t i; - for (i = 0; i < srv->config_context->used; i++) { - plugin_config *s = p->config_storage[i]; - - if (!s) continue; - - excludes_buffer_free(s->excludes); - buffer_free(s->external_css); - buffer_free(s->encoding); - buffer_free(s->set_footer); - - free(s); - } - free(p->config_storage); - } - - buffer_free(p->tmp_buf); - buffer_free(p->path); - buffer_free(p->content_charset); - - free(p); - - return HANDLER_GO_ON; -} - -static int parse_config_entry(server *srv, plugin_config *s, array *ca, const char *option) { - data_unset *du; - - if (NULL != (du = array_get_element(ca, option, strlen(option)))) { - data_array *da = (data_array *)du; - size_t j; - - if (du->type != TYPE_ARRAY) { - log_error_write(srv, __FILE__, __LINE__, "sss", - "unexpected type for key: ", option, "array of strings"); - - return HANDLER_ERROR; - } - - da = (data_array *)du; - - for (j = 0; j < da->value->used; j++) { - if (da->value->data[j]->type != TYPE_STRING) { - log_error_write(srv, __FILE__, __LINE__, "sssbs", - "unexpected type for key: ", option, "[", - da->value->data[j]->key, "](string)"); - - return HANDLER_ERROR; - } - - if (0 != excludes_buffer_append(s->excludes, - ((data_string *)(da->value->data[j]))->value)) { -#ifdef HAVE_PCRE_H - log_error_write(srv, __FILE__, __LINE__, "sb", - "pcre-compile failed for", ((data_string *)(da->value->data[j]))->value); -#else - log_error_write(srv, __FILE__, __LINE__, "s", - "pcre support is missing, please install libpcre and the headers"); -#endif - } - } - } - - return 0; -} - -/* handle plugin config and check values */ - -#define CONFIG_EXCLUDE "dir-listing.exclude" -#define CONFIG_ACTIVATE "dir-listing.activate" -#define CONFIG_HIDE_DOTFILES "dir-listing.hide-dotfiles" -#define CONFIG_EXTERNAL_CSS "dir-listing.external-css" -#define CONFIG_ENCODING "dir-listing.encoding" -#define CONFIG_SHOW_README "dir-listing.show-readme" -#define CONFIG_HIDE_README_FILE "dir-listing.hide-readme-file" -#define CONFIG_SHOW_HEADER "dir-listing.show-header" -#define CONFIG_HIDE_HEADER_FILE "dir-listing.hide-header-file" -#define CONFIG_DIR_LISTING "server.dir-listing" -#define CONFIG_SET_FOOTER "dir-listing.set-footer" - - -SETDEFAULTS_FUNC(mod_dirlisting_set_defaults) { - plugin_data *p = p_d; - size_t i = 0; - - config_values_t cv[] = { - { CONFIG_EXCLUDE, NULL, T_CONFIG_LOCAL, T_CONFIG_SCOPE_CONNECTION }, /* 0 */ - { CONFIG_ACTIVATE, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 1 */ - { CONFIG_HIDE_DOTFILES, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 2 */ - { CONFIG_EXTERNAL_CSS, NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 3 */ - { CONFIG_ENCODING, NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 4 */ - { CONFIG_SHOW_README, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 5 */ - { CONFIG_HIDE_README_FILE, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 6 */ - { CONFIG_SHOW_HEADER, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 7 */ - { CONFIG_HIDE_HEADER_FILE, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 8 */ - { CONFIG_DIR_LISTING, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION }, /* 9 */ - { CONFIG_SET_FOOTER, NULL, T_CONFIG_STRING, T_CONFIG_SCOPE_CONNECTION }, /* 10 */ - - { NULL, NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET } - }; - - if (!p) return HANDLER_ERROR; - - p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *)); - - for (i = 0; i < srv->config_context->used; i++) { - plugin_config *s; - array *ca; - - s = calloc(1, sizeof(plugin_config)); - s->excludes = excludes_buffer_init(); - s->dir_listing = 0; - s->external_css = buffer_init(); - s->hide_dot_files = 0; - s->show_readme = 0; - s->hide_readme_file = 0; - s->show_header = 0; - s->hide_header_file = 0; - s->encoding = buffer_init(); - s->set_footer = buffer_init(); - - cv[0].destination = s->excludes; - cv[1].destination = &(s->dir_listing); - cv[2].destination = &(s->hide_dot_files); - cv[3].destination = s->external_css; - cv[4].destination = s->encoding; - cv[5].destination = &(s->show_readme); - cv[6].destination = &(s->hide_readme_file); - cv[7].destination = &(s->show_header); - cv[8].destination = &(s->hide_header_file); - cv[9].destination = &(s->dir_listing); /* old name */ - cv[10].destination = s->set_footer; - - p->config_storage[i] = s; - ca = ((data_config *)srv->config_context->data[i])->value; - - if (0 != config_insert_values_global(srv, ca, cv)) { - return HANDLER_ERROR; - } - - parse_config_entry(srv, s, ca, CONFIG_EXCLUDE); - } - - return HANDLER_GO_ON; -} - -static int mod_dirlisting_patch_connection(server *srv, connection *con, plugin_data *p) { - size_t i, j; - plugin_config *s = p->config_storage[0]; - - PATCH_OPTION(dir_listing); - PATCH_OPTION(external_css); - PATCH_OPTION(hide_dot_files); - PATCH_OPTION(encoding); - PATCH_OPTION(show_readme); - PATCH_OPTION(hide_readme_file); - PATCH_OPTION(show_header); - PATCH_OPTION(hide_header_file); - PATCH_OPTION(excludes); - PATCH_OPTION(set_footer); - - /* skip the first, the global context */ - for (i = 1; i < srv->config_context->used; i++) { - data_config *dc = (data_config *)srv->config_context->data[i]; - s = p->config_storage[i]; - - /* condition didn't match */ - if (!config_check_cond(srv, con, dc)) continue; - - /* merge config */ - for (j = 0; j < dc->value->used; j++) { - data_unset *du = dc->value->data[j]; - - if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_ACTIVATE)) || - buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_DIR_LISTING))) { - PATCH_OPTION(dir_listing); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_HIDE_DOTFILES))) { - PATCH_OPTION(hide_dot_files); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_EXTERNAL_CSS))) { - PATCH_OPTION(external_css); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_ENCODING))) { - PATCH_OPTION(encoding); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_SHOW_README))) { - PATCH_OPTION(show_readme); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_HIDE_README_FILE))) { - PATCH_OPTION(hide_readme_file); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_SHOW_HEADER))) { - PATCH_OPTION(show_header); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_HIDE_HEADER_FILE))) { - PATCH_OPTION(hide_header_file); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_SET_FOOTER))) { - PATCH_OPTION(set_footer); - } else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_EXCLUDE))) { - PATCH_OPTION(excludes); - } - } - } - - return 0; -} - -typedef struct { - size_t namelen; - time_t mtime; - off_t size; -} dirls_entry_t; - -typedef struct { - dirls_entry_t **ent; - size_t used; - size_t size; -} dirls_list_t; - -#define DIRLIST_ENT_NAME(ent) ((char*)(ent) + sizeof(dirls_entry_t)) -#define DIRLIST_BLOB_SIZE 16 - -/* simple combsort algorithm */ -static void http_dirls_sort(dirls_entry_t **ent, int num) { - int gap = num; - int i, j; - int swapped; - dirls_entry_t *tmp; - - do { - gap = (gap * 10) / 13; - if (gap == 9 || gap == 10) - gap = 11; - if (gap < 1) - gap = 1; - swapped = 0; - - for (i = 0; i < num - gap; i++) { - j = i + gap; - if (strcmp(DIRLIST_ENT_NAME(ent[i]), DIRLIST_ENT_NAME(ent[j])) > 0) { - tmp = ent[i]; - ent[i] = ent[j]; - ent[j] = tmp; - swapped = 1; - } - } - - } while (gap > 1 || swapped); -} - -/* buffer must be able to hold "999.9K" - * conversion is simple but not perfect - */ -static int http_list_directory_sizefmt(char *buf, off_t size) { - const char unit[] = "KMGTPE"; /* Kilo, Mega, Tera, Peta, Exa */ - const char *u = unit - 1; /* u will always increment at least once */ - int remain; - char *out = buf; - - if (size < 100) - size += 99; - if (size < 100) - size = 0; - - while (1) { - remain = (int) size & 1023; - size >>= 10; - u++; - if ((size & (~0 ^ 1023)) == 0) - break; - } - - remain /= 100; - if (remain > 9) - remain = 9; - if (size > 999) { - size = 0; - remain = 9; - u++; - } - - out += LI_ltostr(out, size); - out[0] = '.'; - out[1] = remain + '0'; - out[2] = *u; - out[3] = '\0'; - - return (out + 3 - buf); -} - -static void http_list_directory_header(server *srv, connection *con, plugin_data *p, buffer *out) { - UNUSED(srv); - - buffer_append_string_len(out, CONST_STR_LEN( - "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\" \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n" - "<html xmlns=\"http://www.w3.org/1999/xhtml\" xml:lang=\"en\">\n" - "<head>\n" - "<title>Index of " - )); - buffer_append_string_encoded(out, CONST_BUF_LEN(con->uri.path), ENCODING_MINIMAL_XML); - buffer_append_string_len(out, CONST_STR_LEN("</title>\n")); - - if (p->conf.external_css->used > 1) { - buffer_append_string_len(out, CONST_STR_LEN("<link rel=\"stylesheet\" type=\"text/css\" href=\"")); - buffer_append_string_buffer(out, p->conf.external_css); - buffer_append_string_len(out, CONST_STR_LEN("\" />\n")); - } else { - buffer_append_string_len(out, CONST_STR_LEN( - "<style type=\"text/css\">\n" - "a, a:active {text-decoration: none; color: blue;}\n" - "a:visited {color: #48468F;}\n" - "a:hover, a:focus {text-decoration: underline; color: red;}\n" - "body {background-color: #F5F5F5;}\n" - "h2 {margin-bottom: 12px;}\n" - "table {margin-left: 12px;}\n" - "th, td {" - " font: 90% monospace;" - " text-align: left;" - "}\n" - "th {" - " font-weight: bold;" - " padding-right: 14px;" - " padding-bottom: 3px;" - "}\n" - - "td {padding-right: 14px;}\n" - "td.s, th.s {text-align: right;}\n" - "div.list {" - " background-color: white;" - " border-top: 1px solid #646464;" - " border-bottom: 1px solid #646464;" - " padding-top: 10px;" - " padding-bottom: 14px;" - "}\n" - "div.foot {" - " font: 90% monospace;" - " color: #787878;" - " padding-top: 4px;" - "}\n" - "</style>\n" - )); - } - - buffer_append_string_len(out, CONST_STR_LEN("</head>\n<body>\n")); - - /* HEADER.txt */ - if (p->conf.show_header) { - stream s; - /* if we have a HEADER file, display it in <pre class="header"></pre> */ - - buffer_copy_string_buffer(p->tmp_buf, con->physical.path); - PATHNAME_APPEND_SLASH(p->tmp_buf); - buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("HEADER.txt")); - - if (-1 != stream_open(&s, p->tmp_buf)) { - buffer_append_string_len(out, CONST_STR_LEN("<pre class=\"header\">")); - buffer_append_string_encoded(out, s.start, s.size, ENCODING_MINIMAL_XML); - buffer_append_string_len(out, CONST_STR_LEN("</pre>")); - } - stream_close(&s); - } - - buffer_append_string_len(out, CONST_STR_LEN("<h2>Index of ")); - buffer_append_string_encoded(out, CONST_BUF_LEN(con->uri.path), ENCODING_MINIMAL_XML); - buffer_append_string_len(out, CONST_STR_LEN( - "</h2>\n" - "<div class=\"list\">\n" - "<table summary=\"Directory Listing\" cellpadding=\"0\" cellspacing=\"0\">\n" - "<thead>" - "<tr>" - "<th class=\"n\">Name</th>" - "<th class=\"m\">Last Modified</th>" - "<th class=\"s\">Size</th>" - "<th class=\"t\">Type</th>" - "</tr>" - "</thead>\n" - "<tbody>\n" - "<tr>" - "<td class=\"n\"><a href=\"../\">Parent Directory</a>/</td>" - "<td class=\"m\"> </td>" - "<td class=\"s\">- </td>" - "<td class=\"t\">Directory</td>" - "</tr>\n" - )); -} - -static void http_list_directory_footer(server *srv, connection *con, plugin_data *p, buffer *out) { - UNUSED(srv); - - buffer_append_string_len(out, CONST_STR_LEN( - "</tbody>\n" - "</table>\n" - "</div>\n" - )); - - if (p->conf.show_readme) { - stream s; - /* if we have a README file, display it in <pre class="readme"></pre> */ - - buffer_copy_string_buffer(p->tmp_buf, con->physical.path); - PATHNAME_APPEND_SLASH(p->tmp_buf); - buffer_append_string_len(p->tmp_buf, CONST_STR_LEN("README.txt")); - - if (-1 != stream_open(&s, p->tmp_buf)) { - buffer_append_string_len(out, CONST_STR_LEN("<pre class=\"readme\">")); - buffer_append_string_encoded(out, s.start, s.size, ENCODING_MINIMAL_XML); - buffer_append_string_len(out, CONST_STR_LEN("</pre>")); - } - stream_close(&s); - } - - buffer_append_string_len(out, CONST_STR_LEN( - "<div class=\"foot\">" - )); - - if (p->conf.set_footer->used > 1) { - buffer_append_string_buffer(out, p->conf.set_footer); - } else if (buffer_is_empty(con->conf.server_tag)) { - buffer_append_string_len(out, CONST_STR_LEN(PACKAGE_NAME "/" PACKAGE_VERSION)); - } else { - buffer_append_string_buffer(out, con->conf.server_tag); - } - - buffer_append_string_len(out, CONST_STR_LEN( - "</div>\n" - "</body>\n" - "</html>\n" - )); -} - -static int http_list_directory(server *srv, connection *con, plugin_data *p, buffer *dir) { - DIR *dp; - buffer *out; - struct dirent *dent; - struct stat st; - size_t i; - int hide_dotfiles = p->conf.hide_dot_files; - dirls_list_t dirs, files, *list; - dirls_entry_t *tmp; - char sizebuf[sizeof("999.9K")]; - char datebuf[sizeof("2005-Jan-01 22:23:24")]; - size_t k; - const char *content_type; - long name_max; - -#ifdef HAVE_XATTR - char attrval[128]; - int attrlen; -#endif -#ifdef HAVE_LOCALTIME_R - struct tm tm; -#endif - - /* empty pathname, never ... */ - if (buffer_is_empty(dir)) return -1; - - /* max-length for the opendir */ -#ifdef HAVE_PATHCONF - if (-1 == (name_max = pathconf(dir->ptr, _PC_NAME_MAX))) { -#ifdef NAME_MAX - name_max = NAME_MAX; -#else - name_max = 256; /* stupid default */ -#endif - } -#elif defined _WIN32 - name_max = FILENAME_MAX; -#else - name_max = NAME_MAX; -#endif - - buffer_copy_string_buffer(p->path, dir); - PATHNAME_APPEND_SLASH(p->path); - -#ifdef _WIN32 - /* append *.* to the path */ - buffer_append_string_len(p->path, CONST_STR_LEN("*.*")); -#endif - - if (NULL == (dp = opendir(p->path->ptr))) { - log_error_write(srv, __FILE__, __LINE__, "sbs", - "opendir failed:", dir, strerror(errno)); - - return -1; - } - - dirs.ent = (dirls_entry_t**) malloc(sizeof(dirls_entry_t*) * DIRLIST_BLOB_SIZE); - assert(dirs.ent); - dirs.size = DIRLIST_BLOB_SIZE; - dirs.used = 0; - files.ent = (dirls_entry_t**) malloc(sizeof(dirls_entry_t*) * DIRLIST_BLOB_SIZE); - assert(files.ent); - files.size = DIRLIST_BLOB_SIZE; - files.used = 0; - - while ((dent = readdir(dp)) != NULL) { - unsigned short exclude_match = 0; - - if (dent->d_name[0] == '.') { - if (hide_dotfiles) - continue; - if (dent->d_name[1] == '\0') - continue; - if (dent->d_name[1] == '.' && dent->d_name[2] == '\0') - continue; - } - - if (p->conf.hide_readme_file) { - if (strcmp(dent->d_name, "README.txt") == 0) - continue; - } - if (p->conf.hide_header_file) { - if (strcmp(dent->d_name, "HEADER.txt") == 0) - continue; - } - - /* compare d_name against excludes array - * elements, skipping any that match. - */ -#ifdef HAVE_PCRE_H - for(i = 0; i < p->conf.excludes->used; i++) { - int n; -#define N 10 - int ovec[N * 3]; - pcre *regex = p->conf.excludes->ptr[i]->regex; - - if ((n = pcre_exec(regex, NULL, dent->d_name, - strlen(dent->d_name), 0, 0, ovec, 3 * N)) < 0) { - if (n != PCRE_ERROR_NOMATCH) { - log_error_write(srv, __FILE__, __LINE__, "sd", - "execution error while matching:", n); - - return -1; - } - } - else { - exclude_match = 1; - break; - } - } - - if (exclude_match) { - continue; - } -#endif - - i = strlen(dent->d_name); - - /* NOTE: the manual says, d_name is never more than NAME_MAX - * so this should actually not be a buffer-overflow-risk - */ - if (i > (size_t)name_max) continue; - - /* build the dirname */ - buffer_copy_string_buffer(p->path, dir); - PATHNAME_APPEND_SLASH(p->path); - buffer_append_string(p->path, dent->d_name); - - if (stat(p->path->ptr, &st) != 0) { - fprintf(stderr, "%s.%d: %s, %s\r\n", __FILE__, __LINE__, p->path->ptr, strerror(errno)); - continue; - } - - list = &files; - if (S_ISDIR(st.st_mode)) - list = &dirs; - - if (list->used == list->size) { - list->size += DIRLIST_BLOB_SIZE; - list->ent = (dirls_entry_t**) realloc(list->ent, sizeof(dirls_entry_t*) * list->size); - assert(list->ent); - } - - tmp = (dirls_entry_t*) malloc(sizeof(dirls_entry_t) + 1 + i); - tmp->mtime = st.st_mtime; - tmp->size = st.st_size; - tmp->namelen = i; - memcpy(DIRLIST_ENT_NAME(tmp), dent->d_name, i + 1); - - list->ent[list->used++] = tmp; - } - closedir(dp); - - if (dirs.used) http_dirls_sort(dirs.ent, dirs.used); - - if (files.used) http_dirls_sort(files.ent, files.used); - - out = chunkqueue_get_append_buffer(con->send); - buffer_copy_string_len(out, CONST_STR_LEN("<?xml version=\"1.0\" encoding=\"")); - if (buffer_is_empty(p->conf.encoding)) { - buffer_append_string_len(out, CONST_STR_LEN("iso-8859-1")); - } else { - buffer_append_string_buffer(out, p->conf.encoding); - } - buffer_append_string_len(out, CONST_STR_LEN("\"?>\n")); - http_list_directory_header(srv, con, p, out); - - /* directories */ - for (i = 0; i < dirs.used; i++) { - tmp = dirs.ent[i]; - -#ifdef HAVE_LOCALTIME_R - localtime_r(&(tmp->mtime), &tm); - strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", &tm); -#else - strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", localtime(&(tmp->mtime))); -#endif - - buffer_append_string_len(out, CONST_STR_LEN("<tr><td class=\"n\"><a href=\"")); - buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_REL_URI_PART); - buffer_append_string_len(out, CONST_STR_LEN("/\">")); - buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_MINIMAL_XML); - buffer_append_string_len(out, CONST_STR_LEN("</a>/</td><td class=\"m\">")); - buffer_append_string_len(out, datebuf, sizeof(datebuf) - 1); - buffer_append_string_len(out, CONST_STR_LEN("</td><td class=\"s\">- </td><td class=\"t\">Directory</td></tr>\n")); - - free(tmp); - } - - /* files */ - for (i = 0; i < files.used; i++) { - tmp = files.ent[i]; - - content_type = NULL; - -#ifdef HAVE_XATTR - if (con->conf.use_xattr) { - /* build the dirname */ - buffer_copy_string_buffer(p->path, dir); - PATHNAME_APPEND_SLASH(p->path); - buffer_append_string_len(p->path, DIRLIST_ENT_NAME(tmp), tmp->namelen); - - attrlen = sizeof(attrval) - 1; - if (attr_get(p->path->ptr, "Content-Type", attrval, &attrlen, 0) == 0) { - attrval[attrlen] = '\0'; - content_type = attrval; - } - } -#endif - - if (content_type == NULL) { - content_type = "application/octet-stream"; - for (k = 0; k < con->conf.mimetypes->used; k++) { - data_string *ds = (data_string *)con->conf.mimetypes->data[k]; - size_t ct_len; - - if (ds->key->used == 0) - continue; - - ct_len = ds->key->used - 1; - if (tmp->namelen < ct_len) - continue; - - if (0 == strncasecmp(DIRLIST_ENT_NAME(tmp) + tmp->namelen - ct_len, ds->key->ptr, ct_len)) { - content_type = ds->value->ptr; - break; - } - } - } - -#ifdef HAVE_LOCALTIME_R - localtime_r(&(tmp->mtime), &tm); - strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", &tm); -#else - strftime(datebuf, sizeof(datebuf), "%Y-%b-%d %H:%M:%S", localtime(&(tmp->mtime))); -#endif - http_list_directory_sizefmt(sizebuf, tmp->size); - - buffer_append_string_len(out, CONST_STR_LEN("<tr><td class=\"n\"><a href=\"")); - buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_REL_URI_PART); - buffer_append_string_len(out, CONST_STR_LEN("\">")); - buffer_append_string_encoded(out, DIRLIST_ENT_NAME(tmp), tmp->namelen, ENCODING_MINIMAL_XML); - buffer_append_string_len(out, CONST_STR_LEN("</a></td><td class=\"m\">")); - buffer_append_string_len(out, datebuf, sizeof(datebuf) - 1); - buffer_append_string_len(out, CONST_STR_LEN("</td><td class=\"s\">")); - buffer_append_string(out, sizebuf); - buffer_append_string_len(out, CONST_STR_LEN("</td><td class=\"t\">")); - buffer_append_string(out, content_type); - buffer_append_string_len(out, CONST_STR_LEN("</td></tr>\n")); - - free(tmp); - } - - free(files.ent); - free(dirs.ent); - - http_list_directory_footer(srv, con, p, out); - - /* Insert possible charset to Content-Type */ - if (buffer_is_empty(p->conf.encoding)) { - response_header_insert(srv, con, CONST_STR_LEN("Content-Type"), CONST_STR_LEN("text/html")); - } else { - buffer_copy_string_len(p->content_charset, CONST_STR_LEN("text/html; charset=")); - buffer_append_string_buffer(p->content_charset, p->conf.encoding); - response_header_insert(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(p->content_charset)); - } - - con->send->bytes_in += out->used - 1; - - con->send->is_closed = 1; - - return 0; -} - - - -URIHANDLER_FUNC(mod_dirlisting_subrequest) { - plugin_data *p = p_d; - stat_cache_entry *sce = NULL; - buffer *mtime; - data_string *ds; - - /* we only handle GET, POST and HEAD */ - switch(con->request.http_method) { - case HTTP_METHOD_GET: - case HTTP_METHOD_POST: - case HTTP_METHOD_HEAD: - break; - default: - return HANDLER_GO_ON; - } - - if (con->uri.path->used < 2) return HANDLER_GO_ON; - if (con->uri.path->ptr[con->uri.path->used - 2] != '/') return HANDLER_GO_ON; - if (con->physical.path->used == 0) return HANDLER_GO_ON; - - mod_dirlisting_patch_connection(srv, con, p); - - if (!p->conf.dir_listing) return HANDLER_GO_ON; - - if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) { - /* just a second ago the file was still there */ - return HANDLER_GO_ON; - } - - if (!S_ISDIR(sce->st.st_mode)) return HANDLER_GO_ON; - - if (con->conf.log_request_handling) { - log_error_write(srv, __FILE__, __LINE__, "s", "-- handling the request as Dir-Listing"); - log_error_write(srv, __FILE__, __LINE__, "sb", "URI :", con->uri.path); - } - - /* perhaps this a cachable request - * - we use the etag of the directory - * */ - - etag_mutate(con->physical.etag, sce->etag); - response_header_overwrite(srv, con, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag)); - - /* prepare header */ - if (NULL == (ds = (data_string *)array_get_element(con->response.headers, CONST_STR_LEN("Last-Modified")))) { - mtime = strftime_cache_get(srv, sce->st.st_mtime); - response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime)); - } else { - mtime = ds->value; - } - - if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime)) { - return HANDLER_FINISHED; - } - - if (http_list_directory(srv, con, p, con->physical.path)) { - /* dirlisting failed */ - con->http_status = 403; - } - - buffer_reset(con->physical.path); - - /* not found */ - return HANDLER_FINISHED; -} - -/* this function is called at dlopen() time and inits the callbacks */ - -LI_EXPORT int mod_dirlisting_plugin_init(plugin *p); -LI_EXPORT int mod_dirlisting_plugin_init(plugin *p) { - p->version = LIGHTTPD_VERSION_ID; - p->name = buffer_init_string("dirlisting"); - - p->init = mod_dirlisting_init; - p->handle_start_backend = mod_dirlisting_subrequest; - p->set_defaults = mod_dirlisting_set_defaults; - p->cleanup = mod_dirlisting_free; - - p->data = NULL; - - return 0; -} |