diff --git a/README.rst b/README.rst index 5331d09..c61a6a4 100644 --- a/README.rst +++ b/README.rst @@ -182,13 +182,15 @@ fancyindex_name_length fancyindex_footer ~~~~~~~~~~~~~~~~~ -:Syntax: *fancyindex_footer path* +:Syntax: *fancyindex_footer path* [*subrequest* | *local*] :Default: fancyindex_footer "" :Context: http, server, location :Description: Specifies which file should be inserted at the foot of directory listings. If set to an empty string, the default footer supplied by the module will - be sent. + be sent. The optional parameter indicates whether the *path* is to be + treated as an URI to load using a *subrequest* (the default), or whether + it refers to a *local* file. .. note:: Using this directive needs the ngx_http_addition_module_ built into Nginx. @@ -203,13 +205,15 @@ fancyindex_footer fancyindex_header ~~~~~~~~~~~~~~~~~ -:Syntax: *fancyindex_header path* +:Syntax: *fancyindex_header path* [*subrequest* | *local*] :Default: fancyindex_header "" :Context: http, server, location :Description: Specifies which file should be inserted at the head of directory listings. If set to an empty string, the default header supplied by the module will - be sent. + be sent. The optional parameter indicates whether the *path* is to be + treated as an URI to load using a *subrequest* (the default), or whether + it refers to a *local* file. .. note:: Using this directive needs the ngx_http_addition_module_ built into Nginx. diff --git a/ngx_http_fancyindex_module.c b/ngx_http_fancyindex_module.c index eb00025..d9530d1 100644 --- a/ngx_http_fancyindex_module.c +++ b/ngx_http_fancyindex_module.c @@ -112,59 +112,6 @@ ngx_fancyindex_timefmt_calc_size (const ngx_str_t *fmt) #undef DATETIME_CASE } -static ngx_str_t -ngx_fancyindex_get_file(ngx_http_request_t *r, ngx_str_t *fname) -{ - ngx_file_t file; - ngx_file_info_t fi; - ssize_t size, n; - ngx_str_t ret = { 0, NULL }; - - ngx_memzero(&file, sizeof(ngx_file_t)); - file.log = r->connection->log; - - file.fd = ngx_open_file ((const char*)fname->data, NGX_FILE_RDONLY, 0, 0); - if (file.fd == NGX_INVALID_FILE) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http fancyindex: '%s' is not regular file.", fname->data); - return ret; - } - - if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http fancyindex: can't get information of local file '%s'.", fname->data); - ngx_close_file(file.fd); - return ret; - } - - size = (ssize_t) ngx_file_size(&fi); - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http fancyindex: '%s' -> size %d.", fname->data, size); - ret.data = (u_char*) ngx_calloc (size + 1, r->connection->log); - if (ret.data == NULL) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http fancyindex: memory allocation failed on %s", __FUNCTION__); - ngx_close_file(file.fd); - return ret; - } - n = ngx_read_file(&file, ret.data, size, 0); - if (n == NGX_ERROR) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http fancyindex: failed read of footer file '%s'.", fname->data); - ngx_close_file(file.fd); - ngx_free(ret.data); - - ret.len = 0; - ret.data = NULL; - - return ret; - } - - ret.len = n; - ngx_close_file(file.fd); - - return ret; -} static u_char* ngx_fancyindex_timefmt (u_char *buffer, const ngx_str_t *fmt, const ngx_tm_t *tm) @@ -193,6 +140,10 @@ ngx_fancyindex_timefmt (u_char *buffer, const ngx_str_t *fmt, const ngx_tm_t *tm #undef DATETIME_CASE } +typedef struct { + ngx_str_t path; + ngx_str_t local; +} ngx_fancyindex_headerfooter_conf_t; /** * Configuration structure for the fancyindex module. The configuration @@ -210,12 +161,13 @@ typedef struct { ngx_flag_t hide_parent; /**< Hide parent directory. */ ngx_flag_t show_dot_files; /**< Show files that start with a dot.*/ - ngx_str_t header; /**< File name for header, or empty if none. */ - ngx_str_t footer; /**< File name for footer, or empty if none. */ ngx_str_t css_href; /**< Link to a CSS stylesheet, or empty if none. */ ngx_str_t time_format; /**< Format used for file timestamps. */ ngx_array_t *ignore; /**< List of files to ignore in listings. */ + + ngx_fancyindex_headerfooter_conf_t header; + ngx_fancyindex_headerfooter_conf_t footer; } ngx_http_fancyindex_loc_conf_t; #define NGX_HTTP_FANCYINDEX_SORT_CRITERION_NAME 0 @@ -235,6 +187,106 @@ static ngx_conf_enum_t ngx_http_fancyindex_sort_criteria[] = { { ngx_null_string, 0 } }; +enum { + NGX_HTTP_FANCYINDEX_HEADERFOOTER_SUBREQUEST, + NGX_HTTP_FANCYINDEX_HEADERFOOTER_LOCAL, +}; + +static ngx_uint_t +headerfooter_kind(const ngx_str_t *value) +{ + static const struct { + ngx_str_t name; + ngx_uint_t value; + } values[] = { + { ngx_string("subrequest"), NGX_HTTP_FANCYINDEX_HEADERFOOTER_SUBREQUEST }, + { ngx_string("local"), NGX_HTTP_FANCYINDEX_HEADERFOOTER_LOCAL }, + }; + + unsigned i; + + for (i = 0; i < sizeof(values) / sizeof(values[0]); i++) { + if (value->len == values[i].name.len && + ngx_strcasecmp(value->data, values[i].name.data) == 0) + { + return values[i].value; + } + } + + return NGX_CONF_UNSET_UINT; +} + +static char* +ngx_fancyindex_conf_set_headerfooter(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_fancyindex_headerfooter_conf_t *item = + (void*) (((char*) conf) + cmd->offset); + ngx_str_t *values = cf->args->elts; + + if (item->path.data) + return "is duplicate"; + + item->path = values[1]; + + /* Kind of path. Default is "subrequest". */ + ngx_uint_t kind = NGX_HTTP_FANCYINDEX_HEADERFOOTER_SUBREQUEST; + if (cf->args->nelts == 2) { + kind = headerfooter_kind(&values[2]); + if (kind == NGX_CONF_UNSET_UINT) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "unknown header/footer kind \"%V\"", &values[2]); + return NGX_CONF_ERROR; + } + } + + if (kind == NGX_HTTP_FANCYINDEX_HEADERFOOTER_LOCAL) { + ngx_file_t file; + ngx_file_info_t fi; + ssize_t n; + + ngx_memzero(&file, sizeof(ngx_file_t)); + file.log = cf->log; + file.fd = ngx_open_file(item->path.data, NGX_FILE_RDONLY, 0, 0); + if (file.fd == NGX_INVALID_FILE) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, + "cannot open file \"%V\"", &values[1]); + return NGX_CONF_ERROR; + } + + if (ngx_fd_info(file.fd, &fi) == NGX_FILE_ERROR) { + ngx_close_file(file.fd); + ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, + "cannot get info for file \"%V\"", &values[1]); + return NGX_CONF_ERROR; + } + + item->local.len = ngx_file_size(&fi); + item->local.data = ngx_pcalloc(cf->pool, item->local.len + 1); + if (item->local.data == NULL) { + ngx_close_file(file.fd); + return NGX_CONF_ERROR; + } + + n = item->local.len; + while (n > 0) { + ssize_t r = ngx_read_file(&file, + item->local.data + file.offset, + n, + file.offset); + if (r == NGX_ERROR) { + ngx_close_file(file.fd); + ngx_conf_log_error(NGX_LOG_EMERG, cf, ngx_errno, + "cannot read file \"%V\"", &values[1]); + return NGX_CONF_ERROR; + } + + n -= r; + } + item->local.data[item->local.len] = '\0'; + } + + return NGX_CONF_OK; +} #define NGX_HTTP_FANCYINDEX_PREALLOCATE 50 @@ -319,14 +371,9 @@ static uintptr_t * above). */ static ngx_inline ngx_buf_t* - make_header_buf(ngx_http_request_t *r, const ngx_str_t css_href, ngx_str_t local) + make_header_buf(ngx_http_request_t *r, const ngx_str_t css_href) ngx_force_inline; -static ngx_inline ngx_buf_t* - make_footer_buf(ngx_http_request_t *r, ngx_str_t local) - ngx_force_inline; - - static ngx_command_t ngx_http_fancyindex_commands[] = { @@ -373,15 +420,15 @@ static ngx_command_t ngx_http_fancyindex_commands[] = { NULL }, { ngx_string("fancyindex_header"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, - ngx_conf_set_str_slot, + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12, + ngx_fancyindex_conf_set_headerfooter, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_fancyindex_loc_conf_t, header), NULL }, { ngx_string("fancyindex_footer"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG, - ngx_conf_set_str_slot, + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE12, + ngx_fancyindex_conf_set_headerfooter, NGX_HTTP_LOC_CONF_OFFSET, offsetof(ngx_http_fancyindex_loc_conf_t, footer), NULL }, @@ -573,20 +620,10 @@ ngx_fancyindex_escape_filename(u_char *dst, u_char *src, size_t size) static ngx_inline ngx_buf_t* -make_header_buf(ngx_http_request_t *r, const ngx_str_t css_href, ngx_str_t local) +make_header_buf(ngx_http_request_t *r, const ngx_str_t css_href) { - size_t blen; ngx_buf_t *b; - - if (local.len > 0) { - blen = local.len; - if ((b = ngx_create_temp_buf(r->pool, blen)) == NULL) - return NULL; - b->last = ngx_cpymem(b->last, local.data, local.len); - return b; - } - - blen = r->uri.len + size_t blen = r->uri.len + ngx_sizeof_ssz(t01_head1) + ngx_sizeof_ssz(t02_head2) + ngx_sizeof_ssz(t03_head3) @@ -620,34 +657,6 @@ make_header_buf(ngx_http_request_t *r, const ngx_str_t css_href, ngx_str_t local } - -static ngx_inline ngx_buf_t* -make_footer_buf(ngx_http_request_t *r, ngx_str_t local) -{ - /* - * TODO: Make this buffer static (i.e. readonly and reusable from - * one request to another. - */ - ngx_buf_t *b; - - // ngx_str_t local :: contents of local footer file - if (local.len > 0) { - if ((b = ngx_create_temp_buf(r->pool, local.len)) == NULL) - return NULL; - b->last = ngx_cpymem(b->last, local.data, local.len); - return b; - } - - if ((b = ngx_create_temp_buf(r->pool, ngx_sizeof_ssz(t08_foot1))) == NULL) - return NULL; - - b->last = ngx_cpymem_ssz(b->last, t08_foot1); - - return b; -} - - - static ngx_inline ngx_int_t make_content_buf( ngx_http_request_t *r, ngx_buf_t **pb, @@ -1155,8 +1164,6 @@ ngx_http_fancyindex_handler(ngx_http_request_t *r) ngx_http_request_t *sr; ngx_str_t *sr_uri; ngx_str_t rel_uri; - ngx_str_t lheader = { 0, NULL }; - ngx_str_t lfooter = { 0, NULL }; ngx_int_t rc; ngx_http_fancyindex_loc_conf_t *alcf; ngx_chain_t out[3] = { @@ -1200,24 +1207,19 @@ ngx_http_fancyindex_handler(ngx_http_request_t *r) if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) return rc; - if (alcf->header.len > 0) { - /* if header is local file */ - lheader = ngx_fancyindex_get_file(r, &(alcf->header)); - if (lheader.len > 0) - goto add_builtin_header; - + if (alcf->header.path.len > 0 && alcf->header.local.len == 0) { /* URI is configured, make Nginx take care of with a subrequest. */ - sr_uri = &alcf->header; + sr_uri = &alcf->header.path; if (*sr_uri->data != '/') { /* Relative path */ - rel_uri.len = r->uri.len + alcf->header.len; + rel_uri.len = r->uri.len + alcf->header.path.len; rel_uri.data = ngx_palloc(r->pool, rel_uri.len); if (rel_uri.data == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_memcpy(ngx_cpymem(rel_uri.data, r->uri.data, r->uri.len), - alcf->header.data, alcf->header.len); + alcf->header.path.data, alcf->header.path.len); sr_uri = &rel_uri; } @@ -1245,45 +1247,41 @@ ngx_http_fancyindex_handler(ngx_http_request_t *r) } else { add_builtin_header: - if (lheader.len > 0) { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http fancyindex: adding local filesystem header"); - } else { - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http fancyindex: adding built-in header"); - } /* Make space before */ out[1].next = out[0].next; out[1].buf = out[0].buf; /* Chain header buffer */ out[0].next = &out[1]; - out[0].buf = make_header_buf(r, alcf->css_href, lheader); - if ( lheader.data != NULL ) { - /* lheader.len is used by the local footer and should not be initialized. */ - ngx_free(lheader.data); - lheader.data = NULL; + if (alcf->header.local.len > 0) { + /* Header buffer is local, make a buffer pointing to the data. */ + out[0].buf = ngx_calloc_buf(r->pool); + if (out[0].buf == NULL) + return NGX_ERROR; + out[0].buf->memory = 1; + out[0].buf->pos = alcf->header.local.data; + out[0].buf->last = alcf->header.local.data + alcf->header.local.len; + } else { + /* Prepare a buffer with the contents of the builtin header. */ + out[0].buf = make_header_buf(r, alcf->css_href); } } -add_local_footer: /* If footer is disabled, chain up footer buffer. */ - if (alcf->footer.len == 0 || lfooter.len > 0) { - ngx_uint_t last = (alcf->header.len == 0 || lheader.len > 0) ? 2 : 1; - - if (lfooter.len > 0) { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http fancyindex: adding local filesystem footer at %i", last); - } else { - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http fancyindex: adding built-in footer at %i", last); - } + if (alcf->footer.path.len == 0 || alcf->footer.local.len > 0) { + ngx_uint_t last = (alcf->header.path.len == 0) ? 2 : 1; out[last-1].next = &out[last]; - out[last].buf = make_footer_buf(r, lfooter); - if (lfooter.data != NULL) { - ngx_free(lfooter.data); - lfooter.len = 0; - lfooter.data = NULL; + out[last].buf = ngx_calloc_buf(r->pool); + if (out[last].buf == NULL) + return NGX_ERROR; + + out[last].buf->memory = 1; + if (alcf->footer.local.len > 0) { + out[last].buf->pos = alcf->footer.local.data; + out[last].buf->last = alcf->footer.local.data + alcf->footer.local.len; + } else { + out[last].buf->pos = (u_char*) t08_foot1; + out[last].buf->last = (u_char*) t08_foot1 + sizeof(t08_foot1) - 1; } out[last-1].buf->last_in_chain = 0; @@ -1291,10 +1289,6 @@ add_local_footer: out[last].buf->last_buf = 1; /* Send everything with a single call :D */ return ngx_http_output_filter(r, &out[0]); - } else { - lfooter = ngx_fancyindex_get_file(r, &(alcf->footer)); - if (lfooter.len > 0) - goto add_local_footer; } /* @@ -1309,17 +1303,17 @@ add_local_footer: return NGX_HTTP_INTERNAL_SERVER_ERROR; /* URI is configured, make Nginx take care of with a subrequest. */ - sr_uri = &alcf->footer; + sr_uri = &alcf->footer.path; if (*sr_uri->data != '/') { /* Relative path */ - rel_uri.len = r->uri.len + alcf->footer.len; + rel_uri.len = r->uri.len + alcf->footer.path.len; rel_uri.data = ngx_palloc(r->pool, rel_uri.len); if (rel_uri.data == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; } ngx_memcpy(ngx_cpymem(rel_uri.data, r->uri.data, r->uri.len), - alcf->footer.data, alcf->footer.len); + alcf->footer.path.data, alcf->footer.path.len); sr_uri = &rel_uri; } @@ -1344,7 +1338,12 @@ add_local_footer: * we get something different from a 404? */ out[0].next = NULL; - out[0].buf = make_footer_buf(r, lfooter); + out[0].buf = ngx_calloc_buf(r->pool); + if (out[0].buf == NULL) + return NGX_ERROR; + out[0].buf->memory = 1; + out[0].buf->pos = (u_char*) t08_foot1; + out[0].buf->last = (u_char*) t08_foot1 + sizeof(t08_foot1) - 1; out[0].buf->last_in_chain = 1; out[0].buf->last_buf = 1; /* Directly send out the builtin footer */ @@ -1439,10 +1438,10 @@ ngx_http_fancyindex_create_loc_conf(ngx_conf_t *cf) /* * Set by ngx_pcalloc: - * conf->header.len = 0 - * conf->header.data = NULL - * conf->footer.len = 0 - * conf->footer.data = NULL + * conf->header.*.len = 0 + * conf->header.*.data = NULL + * conf->footer.*.len = 0 + * conf->footer.*.data = NULL * conf->css_href.len = 0 * conf->css_href.data = NULL * conf->time_format.len = 0 @@ -1481,8 +1480,11 @@ ngx_http_fancyindex_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->show_dot_files, prev->show_dot_files, 0); ngx_conf_merge_uint_value(conf->name_length, prev->name_length, 50); - ngx_conf_merge_str_value(conf->header, prev->header, ""); - ngx_conf_merge_str_value(conf->footer, prev->footer, ""); + ngx_conf_merge_str_value(conf->header.path, prev->header.path, ""); + ngx_conf_merge_str_value(conf->header.path, prev->header.local, ""); + ngx_conf_merge_str_value(conf->footer.path, prev->footer.path, ""); + ngx_conf_merge_str_value(conf->footer.path, prev->footer.local, ""); + ngx_conf_merge_str_value(conf->css_href, prev->css_href, ""); ngx_conf_merge_str_value(conf->time_format, prev->time_format, "%Y-%b-%d %H:%M"); @@ -1491,7 +1493,7 @@ ngx_http_fancyindex_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) ngx_conf_merge_value(conf->hide_parent, prev->hide_parent, 0); /* Just make sure we haven't disabled the show_path directive without providing a custom header */ - if (conf->show_path == 0 && conf->header.len == 0) + if (conf->show_path == 0 && conf->header.path.len == 0) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "FancyIndex : cannot set show_path to off without providing a custom header !"); return NGX_CONF_ERROR;