Browse Source

Load local header and footer files when configuring

Make the configuration process load the local files specified with
'fancyindex_{header,footer} "foo" local', instead of loading them
on every request.

Additionally, change the syntax of the configuration directive to accept
an optional parameter indicating whether the header (or footer) is to be
loaded as a subrequest (default, for backwards compatibility) or a local
file (must be specified explicitly). This avoids the footgun of having
a local file which accidentally happens to match the subrequest that one
had the intention to do when configuring.
pull/118/head
Adrian Perez de Castro 4 years ago committed by Adrian Perez
parent
commit
527511fbf8
  1. 12
      README.rst
  2. 320
      ngx_http_fancyindex_module.c

12
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.

320
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,15 +371,10 @@ 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)
ngx_force_inline;
static ngx_inline ngx_buf_t*
make_footer_buf(ngx_http_request_t *r, ngx_str_t local)
make_header_buf(ngx_http_request_t *r, const ngx_str_t css_href)
ngx_force_inline;
static ngx_command_t ngx_http_fancyindex_commands[] = {
{ ngx_string("fancyindex"),
@ -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;

Loading…
Cancel
Save