3 * Copyright (C) Igor Sysoev
7 #include <ngx_config.h>
19 ngx_chain_t
**last_out
;
20 } ngx_http_autoindex_ctx_t
;
31 } ngx_http_autoindex_entry_t
;
36 } ngx_http_autoindex_loc_conf_t
;
39 #define NGX_HTTP_AUTOINDEX_NAME_LEN 50
42 static int ngx_http_autoindex_cmp_entries(const void *one
, const void *two
);
43 static ngx_int_t
ngx_http_autoindex_error(ngx_http_request_t
*r
, ngx_dir_t
*dir
,
45 static ngx_int_t
ngx_http_autoindex_init(ngx_cycle_t
*cycle
);
46 static void *ngx_http_autoindex_create_loc_conf(ngx_conf_t
*cf
);
47 static char *ngx_http_autoindex_merge_loc_conf(ngx_conf_t
*cf
,
48 void *parent
, void *child
);
51 static ngx_command_t ngx_http_autoindex_commands
[] = {
53 { ngx_string("autoindex"),
54 NGX_HTTP_MAIN_CONF
|NGX_HTTP_SRV_CONF
|NGX_HTTP_LOC_CONF
|NGX_CONF_FLAG
,
55 ngx_conf_set_flag_slot
,
56 NGX_HTTP_LOC_CONF_OFFSET
,
57 offsetof(ngx_http_autoindex_loc_conf_t
, enable
),
64 ngx_http_module_t ngx_http_autoindex_module_ctx
= {
67 NULL
, /* create main configuration */
68 NULL
, /* init main configuration */
70 NULL
, /* create server configuration */
71 NULL
, /* merge server configuration */
73 ngx_http_autoindex_create_loc_conf
, /* create location configration */
74 ngx_http_autoindex_merge_loc_conf
/* merge location configration */
78 ngx_module_t ngx_http_autoindex_module
= {
80 &ngx_http_autoindex_module_ctx
, /* module context */
81 ngx_http_autoindex_commands
, /* module directives */
82 NGX_HTTP_MODULE
, /* module type */
83 ngx_http_autoindex_init
, /* init module */
88 static u_char title
[] =
90 "<head><title>Index of "
94 static u_char header
[] =
95 "</title></head>" CRLF
96 "<body bgcolor=\"white\">" CRLF
100 static u_char tail
[] =
106 static ngx_int_t
ngx_http_autoindex_handler(ngx_http_request_t
*r
)
116 ngx_str_t dname
, fname
;
120 ngx_http_core_loc_conf_t
*clcf
;
121 ngx_http_autoindex_entry_t
*entry
;
122 ngx_http_autoindex_loc_conf_t
*alcf
;
124 static char *months
[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
125 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
127 if (r
->uri
.data
[r
->uri
.len
- 1] != '/') {
132 if (r
->zero_in_uri
) {
136 alcf
= ngx_http_get_module_loc_conf(r
, ngx_http_autoindex_module
);
142 /* TODO: pool should be temporary pool */
145 clcf
= ngx_http_get_module_loc_conf(r
, ngx_http_core_module
);
148 dname
.data
= ngx_palloc(pool
, clcf
->root
.len
+ r
->uri
.len
151 if (dname
.data
== NULL
) {
152 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
155 last
= ngx_cpymem(dname
.data
, clcf
->root
.data
, clcf
->root
.len
);
156 last
= ngx_cpystrn(last
, r
->uri
.data
+ clcf
->name
.len
,
157 r
->uri
.len
- clcf
->name
.len
);
160 dname
.data
= ngx_palloc(pool
, clcf
->root
.len
+ r
->uri
.len
162 if (dname
.data
== NULL
) {
163 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
166 last
= ngx_cpymem(dname
.data
, clcf
->root
.data
, clcf
->root
.len
);
167 last
= ngx_cpystrn(last
, r
->uri
.data
, r
->uri
.len
);
171 dname
.len
= last
- dname
.data
;
173 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
174 "http autoindex: \"%s\"", dname
.data
);
177 if (ngx_open_dir(&dname
, &dir
) == NGX_ERROR
) {
180 if (err
== NGX_ENOENT
|| err
== NGX_ENOTDIR
) {
182 rc
= NGX_HTTP_NOT_FOUND
;
184 } else if (err
== NGX_EACCES
) {
186 rc
= NGX_HTTP_FORBIDDEN
;
189 level
= NGX_LOG_CRIT
;
190 rc
= NGX_HTTP_INTERNAL_SERVER_ERROR
;
193 ngx_log_error(level
, r
->connection
->log
, err
,
194 ngx_open_dir_n
" \"%s\" failed", dname
.data
);
199 #if (NGX_SUPPRESS_WARN)
200 /* MSVC thinks 'entries' may be used without having been initialized */
201 ngx_memzero(&entries
, sizeof(ngx_array_t
));
204 if (ngx_array_init(&entries
, pool
, 50, sizeof(ngx_http_autoindex_entry_t
))
207 return ngx_http_autoindex_error(r
, &dir
, dname
.data
);
211 #if (NGX_SUPPRESS_WARN)
218 if (ngx_read_dir(&dir
) == NGX_ERROR
) {
221 if (err
== NGX_ENOMOREFILES
) {
225 ngx_log_error(NGX_LOG_CRIT
, r
->connection
->log
, err
,
226 ngx_read_dir_n
" \"%s\" failed", dname
.data
);
227 return ngx_http_autoindex_error(r
, &dir
, dname
.data
);
233 ngx_log_debug1(NGX_LOG_DEBUG_HTTP
, r
->connection
->log
, 0,
234 "http autoindex file: \"%s\"", ngx_de_name(&dir
));
236 len
= ngx_de_namelen(&dir
);
238 if (len
== 1 && ngx_de_name(&dir
)[0] == '.') {
243 && ngx_de_name(&dir
)[0] == '.'
244 && ngx_de_name(&dir
)[0] == '.')
249 if (!dir
.valid_info
) {
251 if (dname
.len
+ 1 + len
> fname
.len
) {
252 fname
.len
= dname
.len
+ 1 + len
+ 32;
254 if (!(fname
.data
= ngx_palloc(pool
, fname
.len
))) {
255 return ngx_http_autoindex_error(r
, &dir
, dname
.data
);
258 last
= ngx_cpystrn(fname
.data
, dname
.data
,
263 ngx_cpystrn(last
, ngx_de_name(&dir
), len
+ 1);
265 if (ngx_de_info(fname
.data
, &dir
) == NGX_FILE_ERROR
) {
266 ngx_log_error(NGX_LOG_CRIT
, r
->connection
->log
, ngx_errno
,
267 ngx_de_info_n
" \"%s\" failed", fname
.data
);
268 return ngx_http_autoindex_error(r
, &dir
, dname
.data
);
272 if (!(entry
= ngx_array_push(&entries
))) {
273 return ngx_http_autoindex_error(r
, &dir
, dname
.data
);
276 entry
->name
.len
= len
;
277 entry
->escape
= 2 * ngx_escape_uri(NULL
, ngx_de_name(&dir
), len
,
280 if (!(entry
->name
.data
= ngx_palloc(pool
, len
+ entry
->escape
+ 1))) {
281 return ngx_http_autoindex_error(r
, &dir
, dname
.data
);
284 ngx_cpystrn(entry
->name
.data
, ngx_de_name(&dir
), len
+ 1);
286 entry
->dir
= ngx_de_is_dir(&dir
);
287 entry
->mtime
= ngx_de_mtime(&dir
);
288 entry
->size
= ngx_de_size(&dir
);
291 if (ngx_close_dir(&dir
) == NGX_ERROR
) {
292 ngx_log_error(NGX_LOG_ALERT
, r
->connection
->log
, ngx_errno
,
293 ngx_close_dir_n
" \"%s\" failed", dname
.data
);
296 len
= sizeof(title
) - 1
300 + sizeof("</h1>") - 1
301 + sizeof("<hr><pre><a href=\"../\">../</a>" CRLF
) - 1
302 + sizeof("</pre><hr>") - 1
305 entry
= entries
.elts
;
306 for (i
= 0; i
< entries
.nelts
; i
++) {
307 len
+= sizeof("<a href=\"") - 1
308 + 1 /* 1 is for "/" */
309 + entry
[i
].name
.len
+ entry
[i
].escape
311 + NGX_HTTP_AUTOINDEX_NAME_LEN
+ sizeof(">") - 2
313 + sizeof(" 28-Sep-1970 12:00 ") - 1
318 if (!(b
= ngx_create_temp_buf(r
->pool
, len
))) {
319 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
322 if (entries
.nelts
> 1) {
323 ngx_qsort(entry
, (size_t) entries
.nelts
,
324 sizeof(ngx_http_autoindex_entry_t
),
325 ngx_http_autoindex_cmp_entries
);
328 b
->last
= ngx_cpymem(b
->last
, title
, sizeof(title
) - 1);
329 b
->last
= ngx_cpymem(b
->last
, r
->uri
.data
, r
->uri
.len
);
330 b
->last
= ngx_cpymem(b
->last
, header
, sizeof(header
) - 1);
331 b
->last
= ngx_cpymem(b
->last
, r
->uri
.data
, r
->uri
.len
);
332 b
->last
= ngx_cpymem(b
->last
, "</h1>", sizeof("</h1>") - 1);
334 b
->last
= ngx_cpymem(b
->last
, "<hr><pre><a href=\"../\">../</a>" CRLF
,
335 sizeof("<hr><pre><a href=\"../\">../</a>" CRLF
) - 1);
337 for (i
= 0; i
< entries
.nelts
; i
++) {
338 b
->last
= ngx_cpymem(b
->last
, "<a href=\"", sizeof("<a href=\"") - 1);
340 if (entry
[i
].escape
) {
341 ngx_escape_uri(b
->last
, entry
[i
].name
.data
, entry
[i
].name
.len
,
344 b
->last
+= entry
[i
].name
.len
+ entry
[i
].escape
;
347 b
->last
= ngx_cpymem(b
->last
, entry
[i
].name
.data
,
358 b
->last
= ngx_cpystrn(b
->last
, entry
[i
].name
.data
,
359 NGX_HTTP_AUTOINDEX_NAME_LEN
+ 1);
361 len
= entry
[i
].name
.len
;
363 if (len
> NGX_HTTP_AUTOINDEX_NAME_LEN
) {
364 b
->last
= ngx_cpymem(b
->last
- 3, "..></a>",
365 sizeof("..></a>") - 1);
368 if (entry
[i
].dir
&& NGX_HTTP_AUTOINDEX_NAME_LEN
- len
> 0) {
373 b
->last
= ngx_cpymem(b
->last
, "</a>", sizeof("</a>") - 1);
374 ngx_memset(b
->last
, ' ', NGX_HTTP_AUTOINDEX_NAME_LEN
- len
);
375 b
->last
+= NGX_HTTP_AUTOINDEX_NAME_LEN
- len
;
380 ngx_gmtime(entry
[i
].mtime
, &tm
);
382 b
->last
= ngx_sprintf(b
->last
, "%02d-%s-%d %02d:%02d ",
384 months
[tm
.ngx_tm_mon
- 1],
390 b
->last
= ngx_cpymem(b
->last
, " -",
394 b
->last
= ngx_sprintf(b
->last
, "%19O", entry
[i
].size
);
401 /* TODO: free temporary pool */
403 b
->last
= ngx_cpymem(b
->last
, "</pre><hr>", sizeof("</pre><hr>") - 1);
405 b
->last
= ngx_cpymem(b
->last
, tail
, sizeof(tail
) - 1);
407 r
->headers_out
.status
= NGX_HTTP_OK
;
408 r
->headers_out
.content_length_n
= b
->last
- b
->pos
;
410 r
->headers_out
.content_type
= ngx_list_push(&r
->headers_out
.headers
);
411 if (r
->headers_out
.content_type
== NULL
) {
412 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
415 r
->headers_out
.content_type
->key
.len
= 0;
416 r
->headers_out
.content_type
->key
.data
= NULL
;
417 r
->headers_out
.content_type
->value
.len
= sizeof("text/html") - 1;
418 r
->headers_out
.content_type
->value
.data
= (u_char
*) "text/html";
420 rc
= ngx_http_send_header(r
);
422 if (rc
== NGX_ERROR
|| rc
> NGX_OK
|| r
->header_only
) {
433 return ngx_http_output_filter(r
, &out
);
437 static int ngx_http_autoindex_cmp_entries(const void *one
, const void *two
)
439 ngx_http_autoindex_entry_t
*first
= (ngx_http_autoindex_entry_t
*) one
;
440 ngx_http_autoindex_entry_t
*second
= (ngx_http_autoindex_entry_t
*) two
;
442 if (first
->dir
&& !second
->dir
) {
443 /* move the directories to the start */
447 if (!first
->dir
&& second
->dir
) {
448 /* move the directories to the start */
452 return (int) ngx_strcmp(first
->name
.data
, second
->name
.data
);
458 static ngx_buf_t
*ngx_http_autoindex_alloc(ngx_http_autoindex_ctx_t
*ctx
,
465 if ((size_t) (ctx
->buf
->end
- ctx
->buf
->last
) >= size
) {
469 ctx
->size
+= ctx
->buf
->last
- ctx
->buf
->pos
;
472 if (!(ctx
->buf
= ngx_create_temp_buf(ctx
->pool
, ctx
->alloc_size
))) {
476 if (!(cl
= ngx_alloc_chain_link(ctx
->pool
))) {
484 ctx
->last_out
= &cl
->next
;
492 static ngx_int_t
ngx_http_autoindex_error(ngx_http_request_t
*r
, ngx_dir_t
*dir
,
495 if (ngx_close_dir(dir
) == NGX_ERROR
) {
496 ngx_log_error(NGX_LOG_ALERT
, r
->connection
->log
, ngx_errno
,
497 ngx_close_dir_n
" \"%s\" failed", name
);
500 return NGX_HTTP_INTERNAL_SERVER_ERROR
;
504 static ngx_int_t
ngx_http_autoindex_init(ngx_cycle_t
*cycle
)
506 ngx_http_handler_pt
*h
;
507 ngx_http_core_main_conf_t
*cmcf
;
509 cmcf
= ngx_http_cycle_get_module_main_conf(cycle
, ngx_http_core_module
);
511 h
= ngx_array_push(&cmcf
->phases
[NGX_HTTP_CONTENT_PHASE
].handlers
);
516 *h
= ngx_http_autoindex_handler
;
522 static void *ngx_http_autoindex_create_loc_conf(ngx_conf_t
*cf
)
524 ngx_http_autoindex_loc_conf_t
*conf
;
526 conf
= ngx_palloc(cf
->pool
, sizeof(ngx_http_autoindex_loc_conf_t
));
528 return NGX_CONF_ERROR
;
531 conf
->enable
= NGX_CONF_UNSET
;
537 static char *ngx_http_autoindex_merge_loc_conf(ngx_conf_t
*cf
,
538 void *parent
, void *child
)
540 ngx_http_autoindex_loc_conf_t
*prev
= parent
;
541 ngx_http_autoindex_loc_conf_t
*conf
= child
;
543 ngx_conf_merge_value(conf
->enable
, prev
->enable
, 0);