1
2 /*
3 * Copyright (C) Igor Sysoev
4 */
5
6
7 #include <ngx_config.h>
8 #include <ngx_core.h>
9 #include <ngx_http.h>
10
11
12 #if 0
13
14 typedef struct {
15 ngx_buf_t *buf;
16 size_t size;
17 ngx_pool_t *pool;
18 size_t alloc_size;
19 ngx_chain_t **last_out;
20 } ngx_http_autoindex_ctx_t;
21
22 #endif
23
24
25 typedef struct {
26 ngx_str_t name;
27 size_t utf_len;
28 size_t escape;
29
30 unsigned dir:1;
31 unsigned colon:1;
32
33 time_t mtime;
34 off_t size;
35 } ngx_http_autoindex_entry_t;
36
37
38 typedef struct {
39 ngx_flag_t enable;
40 ngx_flag_t localtime;
41 ngx_flag_t exact_size;
42 } ngx_http_autoindex_loc_conf_t;
43
44
45 #define NGX_HTTP_AUTOINDEX_PREALLOCATE 50
46
47 #define NGX_HTTP_AUTOINDEX_NAME_LEN 50
48
49
50 static int ngx_libc_cdecl ngx_http_autoindex_cmp_entries(const void *one,
51 const void *two);
52 static ngx_int_t ngx_http_autoindex_error(ngx_http_request_t *r,
53 ngx_dir_t *dir, ngx_str_t *name);
54 static ngx_int_t ngx_http_autoindex_init(ngx_conf_t *cf);
55 static void *ngx_http_autoindex_create_loc_conf(ngx_conf_t *cf);
56 static char *ngx_http_autoindex_merge_loc_conf(ngx_conf_t *cf,
57 void *parent, void *child);
58
59
60 static ngx_command_t ngx_http_autoindex_commands[] = {
61
62 { ngx_string("autoindex"),
63 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
64 ngx_conf_set_flag_slot,
65 NGX_HTTP_LOC_CONF_OFFSET,
66 offsetof(ngx_http_autoindex_loc_conf_t, enable),
67 NULL },
68
69 { ngx_string("autoindex_localtime"),
70 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
71 ngx_conf_set_flag_slot,
72 NGX_HTTP_LOC_CONF_OFFSET,
73 offsetof(ngx_http_autoindex_loc_conf_t, localtime),
74 NULL },
75
76 { ngx_string("autoindex_exact_size"),
77 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
78 ngx_conf_set_flag_slot,
79 NGX_HTTP_LOC_CONF_OFFSET,
80 offsetof(ngx_http_autoindex_loc_conf_t, exact_size),
81 NULL },
82
83 ngx_null_command
84 };
85
86
87 static ngx_http_module_t ngx_http_autoindex_module_ctx = {
88 NULL, /* preconfiguration */
89 ngx_http_autoindex_init, /* postconfiguration */
90
91 NULL, /* create main configuration */
92 NULL, /* init main configuration */
93
94 NULL, /* create server configuration */
95 NULL, /* merge server configuration */
96
97 ngx_http_autoindex_create_loc_conf, /* create location configration */
98 ngx_http_autoindex_merge_loc_conf /* merge location configration */
99 };
100
101
102 ngx_module_t ngx_http_autoindex_module = {
103 NGX_MODULE_V1,
104 &ngx_http_autoindex_module_ctx, /* module context */
105 ngx_http_autoindex_commands, /* module directives */
106 NGX_HTTP_MODULE, /* module type */
107 NULL, /* init master */
108 NULL, /* init module */
109 NULL, /* init process */
110 NULL, /* init thread */
111 NULL, /* exit thread */
112 NULL, /* exit process */
113 NULL, /* exit master */
114 NGX_MODULE_V1_PADDING
115 };
116
117
118 static u_char title[] =
119 "<html>" CRLF
120 "<head><title>Index of "
121 ;
122
123
124 static u_char header[] =
125 "</title></head>" CRLF
126 "<body bgcolor=\"white\">" CRLF
127 "<h1>Index of "
128 ;
129
130 static u_char tail[] =
131 "</body>" CRLF
132 "</html>" CRLF
133 ;
134
135
136 static ngx_int_t
137 ngx_http_autoindex_handler(ngx_http_request_t *r)
138 {
139 u_char *last, *filename, scale;
140 off_t length;
141 size_t len, utf_len, allocated, root;
142 ngx_tm_t tm;
143 ngx_err_t err;
144 ngx_buf_t *b;
145 ngx_int_t rc, size;
146 ngx_str_t path;
147 ngx_dir_t dir;
148 ngx_uint_t i, level, utf8;
149 ngx_pool_t *pool;
150 ngx_time_t *tp;
151 ngx_chain_t out;
152 ngx_array_t entries;
153 ngx_http_autoindex_entry_t *entry;
154 ngx_http_autoindex_loc_conf_t *alcf;
155
156 static char *months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
157 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
158
159 if (r->uri.data[r->uri.len - 1] != '/') {
160 return NGX_DECLINED;
161 }
162
163 if (r->zero_in_uri) {
164 return NGX_DECLINED;
165 }
166
167 if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) {
168 return NGX_DECLINED;
169 }
170
171 alcf = ngx_http_get_module_loc_conf(r, ngx_http_autoindex_module);
172
173 if (!alcf->enable) {
174 return NGX_DECLINED;
175 }
176
177 /* NGX_DIR_MASK_LEN is lesser than NGX_HTTP_AUTOINDEX_PREALLOCATE */
178
179 last = ngx_http_map_uri_to_path(r, &path, &root,
180 NGX_HTTP_AUTOINDEX_PREALLOCATE);
181 if (last == NULL) {
182 return NGX_HTTP_INTERNAL_SERVER_ERROR;
183 }
184
185 allocated = path.len;
186 path.len = last - path.data;
187 if (path.len > 1) {
188 path.len--;
189 }
190 path.data[path.len] = '\0';
191
192 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
193 "http autoindex: \"%s\"", path.data);
194
195 if (ngx_open_dir(&path, &dir) == NGX_ERROR) {
196 err = ngx_errno;
197
198 if (err == NGX_ENOENT
199 || err == NGX_ENOTDIR
200 || err == NGX_ENAMETOOLONG)
201 {
202 level = NGX_LOG_ERR;
203 rc = NGX_HTTP_NOT_FOUND;
204
205 } else if (err == NGX_EACCES) {
206 level = NGX_LOG_ERR;
207 rc = NGX_HTTP_FORBIDDEN;
208
209 } else {
210 level = NGX_LOG_CRIT;
211 rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
212 }
213
214 ngx_log_error(level, r->connection->log, err,
215 ngx_open_dir_n " \"%s\" failed", path.data);
216
217 return rc;
218 }
219
220 #if (NGX_SUPPRESS_WARN)
221
222 /* MSVC thinks 'entries' may be used without having been initialized */
223 ngx_memzero(&entries, sizeof(ngx_array_t));
224
225 #endif
226
227 /* TODO: pool should be temporary pool */
228 pool = r->pool;
229
230 if (ngx_array_init(&entries, pool, 40, sizeof(ngx_http_autoindex_entry_t))
231 != NGX_OK)
232 {
233 return ngx_http_autoindex_error(r, &dir, &path);
234 }
235
236 r->headers_out.status = NGX_HTTP_OK;
237 r->headers_out.content_type_len = sizeof("text/html") - 1;
238 r->headers_out.content_type.len = sizeof("text/html") - 1;
239 r->headers_out.content_type.data = (u_char *) "text/html";
240
241 rc = ngx_http_send_header(r);
242
243 if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) {
244 if (ngx_close_dir(&dir) == NGX_ERROR) {
245 ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
246 ngx_close_dir_n " \"%V\" failed", &path);
247 }
248
249 return rc;
250 }
251
252 filename = path.data;
253 filename[path.len] = '/';
254
255 if (r->headers_out.charset.len == 5
256 && ngx_strncasecmp(r->headers_out.charset.data, (u_char *) "utf-8", 5)
257 == 0)
258 {
259 utf8 = 1;
260
261 } else {
262 utf8 = 0;
263 }
264
265 for ( ;; ) {
266 ngx_set_errno(0);
267
268 if (ngx_read_dir(&dir) == NGX_ERROR) {
269 err = ngx_errno;
270
271 if (err != NGX_ENOMOREFILES) {
272 ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
273 ngx_read_dir_n " \"%V\" failed", &path);
274 return ngx_http_autoindex_error(r, &dir, &path);
275 }
276
277 break;
278 }
279
280 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
281 "http autoindex file: \"%s\"", ngx_de_name(&dir));
282
283 len = ngx_de_namelen(&dir);
284
285 if (ngx_de_name(&dir)[0] == '.') {
286 continue;
287 }
288
289 if (!dir.valid_info) {
290
291 /* 1 byte for '/' and 1 byte for terminating '\0' */
292
293 if (path.len + 1 + len + 1 > allocated) {
294 allocated = path.len + 1 + len + 1
295 + NGX_HTTP_AUTOINDEX_PREALLOCATE;
296
297 filename = ngx_pnalloc(pool, allocated);
298 if (filename == NULL) {
299 return ngx_http_autoindex_error(r, &dir, &path);
300 }
301
302 last = ngx_cpystrn(filename, path.data, path.len + 1);
303 *last++ = '/';
304 }
305
306 ngx_cpystrn(last, ngx_de_name(&dir), len + 1);
307
308 if (ngx_de_info(filename, &dir) == NGX_FILE_ERROR) {
309 err = ngx_errno;
310
311 if (err != NGX_ENOENT) {
312 ngx_log_error(NGX_LOG_CRIT, r->connection->log, err,
313 ngx_de_info_n " \"%s\" failed", filename);
314
315 if (err == NGX_EACCES) {
316 continue;
317 }
318
319 return ngx_http_autoindex_error(r, &dir, &path);
320 }
321
322 if (ngx_de_link_info(filename, &dir) == NGX_FILE_ERROR) {
323 ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno,
324 ngx_de_link_info_n " \"%s\" failed",
325 filename);
326 return ngx_http_autoindex_error(r, &dir, &path);
327 }
328 }
329 }
330
331 entry = ngx_array_push(&entries);
332 if (entry == NULL) {
333 return ngx_http_autoindex_error(r, &dir, &path);
334 }
335
336 entry->name.len = len;
337
338 entry->name.data = ngx_pnalloc(pool, len + 1);
339 if (entry->name.data == NULL) {
340 return ngx_http_autoindex_error(r, &dir, &path);
341 }
342
343 ngx_cpystrn(entry->name.data, ngx_de_name(&dir), len + 1);
344
345 entry->escape = 2 * ngx_escape_uri(NULL, ngx_de_name(&dir), len,
346 NGX_ESCAPE_HTML);
347
348 if (utf8) {
349 entry->utf_len = ngx_utf8_length(entry->name.data, entry->name.len);
350 } else {
351 entry->utf_len = len;
352 }
353
354 entry->colon = (ngx_strchr(entry->name.data, ':') != NULL);
355
356 entry->dir = ngx_de_is_dir(&dir);
357 entry->mtime = ngx_de_mtime(&dir);
358 entry->size = ngx_de_size(&dir);
359 }
360
361 if (ngx_close_dir(&dir) == NGX_ERROR) {
362 ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
363 ngx_close_dir_n " \"%s\" failed", &path);
364 }
365
366 len = sizeof(title) - 1
367 + r->uri.len
368 + sizeof(header) - 1
369 + r->uri.len
370 + sizeof("</h1>") - 1
371 + sizeof("<hr><pre><a href=\"../\">../</a>" CRLF) - 1
372 + sizeof("</pre><hr>") - 1
373 + sizeof(tail) - 1;
374
375 entry = entries.elts;
376 for (i = 0; i < entries.nelts; i++) {
377 len += sizeof("<a href=\"") - 1
378 + entry[i].name.len + entry[i].escape
379 + 1 /* 1 is for "/" */
380 + sizeof("\">") - 1
381 + entry[i].name.len - entry[i].utf_len + entry[i].colon * 2
382 + NGX_HTTP_AUTOINDEX_NAME_LEN + sizeof(">") - 2
383 + sizeof("</a>") - 1
384 + sizeof(" 28-Sep-1970 12:00 ") - 1
385 + 20 /* the file size */
386 + 2;
387 }
388
389 b = ngx_create_temp_buf(r->pool, len);
390 if (b == NULL) {
391 return NGX_HTTP_INTERNAL_SERVER_ERROR;
392 }
393
394 if (entries.nelts > 1) {
395 ngx_qsort(entry, (size_t) entries.nelts,
396 sizeof(ngx_http_autoindex_entry_t),
397 ngx_http_autoindex_cmp_entries);
398 }
399
400 b->last = ngx_cpymem(b->last, title, sizeof(title) - 1);
401 b->last = ngx_cpymem(b->last, r->uri.data, r->uri.len);
402 b->last = ngx_cpymem(b->last, header, sizeof(header) - 1);
403 b->last = ngx_cpymem(b->last, r->uri.data, r->uri.len);
404 b->last = ngx_cpymem(b->last, "</h1>", sizeof("</h1>") - 1);
405
406 b->last = ngx_cpymem(b->last, "<hr><pre><a href=\"../\">../</a>" CRLF,
407 sizeof("<hr><pre><a href=\"../\">../</a>" CRLF) - 1);
408
409 tp = ngx_timeofday();
410
411 for (i = 0; i < entries.nelts; i++) {
412 b->last = ngx_cpymem(b->last, "<a href=\"", sizeof("<a href=\"") - 1);
413
414 if (entry[i].colon) {
415 *b->last++ = '.';
416 *b->last++ = '/';
417 }
418
419 if (entry[i].escape) {
420 ngx_escape_uri(b->last, entry[i].name.data, entry[i].name.len,
421 NGX_ESCAPE_HTML);
422
423 b->last += entry[i].name.len + entry[i].escape;
424
425 } else {
426 b->last = ngx_cpymem(b->last, entry[i].name.data,
427 entry[i].name.len);
428 }
429
430 if (entry[i].dir) {
431 *b->last++ = '/';
432 }
433
434 *b->last++ = '"';
435 *b->last++ = '>';
436
437 len = entry[i].utf_len;
438
439 if (entry[i].name.len != len) {
440 if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) {
441 utf_len = NGX_HTTP_AUTOINDEX_NAME_LEN - 3 + 1;
442
443 } else {
444 utf_len = NGX_HTTP_AUTOINDEX_NAME_LEN + 1;
445 }
446
447 b->last = ngx_utf8_cpystrn(b->last, entry[i].name.data,
448 utf_len, entry[i].name.len + 1);
449 last = b->last;
450
451 } else {
452 b->last = ngx_cpystrn(b->last, entry[i].name.data,
453 NGX_HTTP_AUTOINDEX_NAME_LEN + 1);
454 last = b->last - 3;
455 }
456
457 if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) {
458 b->last = ngx_cpymem(last, "..></a>", sizeof("..></a>") - 1);
459
460 } else {
461 if (entry[i].dir && NGX_HTTP_AUTOINDEX_NAME_LEN - len > 0) {
462 *b->last++ = '/';
463 len++;
464 }
465
466 b->last = ngx_cpymem(b->last, "</a>", sizeof("</a>") - 1);
467 ngx_memset(b->last, ' ', NGX_HTTP_AUTOINDEX_NAME_LEN - len);
468 b->last += NGX_HTTP_AUTOINDEX_NAME_LEN - len;
469 }
470
471 *b->last++ = ' ';
472
473 ngx_gmtime(entry[i].mtime + tp->gmtoff * 60 * alcf->localtime, &tm);
474
475 b->last = ngx_sprintf(b->last, "%02d-%s-%d %02d:%02d ",
476 tm.ngx_tm_mday,
477 months[tm.ngx_tm_mon - 1],
478 tm.ngx_tm_year,
479 tm.ngx_tm_hour,
480 tm.ngx_tm_min);
481
482 if (alcf->exact_size) {
483 if (entry[i].dir) {
484 b->last = ngx_cpymem(b->last, " -",
485 sizeof(" -") - 1);
486 } else {
487 b->last = ngx_sprintf(b->last, "%19O", entry[i].size);
488 }
489
490 } else {
491 if (entry[i].dir) {
492 b->last = ngx_cpymem(b->last, " -",
493 sizeof(" -") - 1);
494
495 } else {
496 length = entry[i].size;
497
498 if (length > 1024 * 1024 * 1024 - 1) {
499 size = (ngx_int_t) (length / (1024 * 1024 * 1024));
500 if ((length % (1024 * 1024 * 1024))
501 > (1024 * 1024 * 1024 / 2 - 1))
502 {
503 size++;
504 }
505 scale = 'G';
506
507 } else if (length > 1024 * 1024 - 1) {
508 size = (ngx_int_t) (length / (1024 * 1024));
509 if ((length % (1024 * 1024)) > (1024 * 1024 / 2 - 1)) {
510 size++;
511 }
512 scale = 'M';
513
514 } else if (length > 9999) {
515 size = (ngx_int_t) (length / 1024);
516 if (length % 1024 > 511) {
517 size++;
518 }
519 scale = 'K';
520
521 } else {
522 size = (ngx_int_t) length;
523 scale = '\0';
524 }
525
526 if (scale) {
527 b->last = ngx_sprintf(b->last, "%6i%c", size, scale);
528
529 } else {
530 b->last = ngx_sprintf(b->last, " %6i", size);
531 }
532 }
533 }
534
535 *b->last++ = CR;
536 *b->last++ = LF;
537 }
538
539 /* TODO: free temporary pool */
540
541 b->last = ngx_cpymem(b->last, "</pre><hr>", sizeof("</pre><hr>") - 1);
542
543 b->last = ngx_cpymem(b->last, tail, sizeof(tail) - 1);
544
545 if (r == r->main) {
546 b->last_buf = 1;
547 }
548
549 b->last_in_chain = 1;
550
551 out.buf = b;
552 out.next = NULL;
553
554 return ngx_http_output_filter(r, &out);
555 }
556
557
558 static int ngx_libc_cdecl
559 ngx_http_autoindex_cmp_entries(const void *one, const void *two)
560 {
561 ngx_http_autoindex_entry_t *first = (ngx_http_autoindex_entry_t *) one;
562 ngx_http_autoindex_entry_t *second = (ngx_http_autoindex_entry_t *) two;
563
564 if (first->dir && !second->dir) {
565 /* move the directories to the start */
566 return -1;
567 }
568
569 if (!first->dir && second->dir) {
570 /* move the directories to the start */
571 return 1;
572 }
573
574 return (int) ngx_strcmp(first->name.data, second->name.data);
575 }
576
577
578 #if 0
579
580 static ngx_buf_t *
581 ngx_http_autoindex_alloc(ngx_http_autoindex_ctx_t *ctx, size_t size)
582 {
583 ngx_chain_t *cl;
584
585 if (ctx->buf) {
586
587 if ((size_t) (ctx->buf->end - ctx->buf->last) >= size) {
588 return ctx->buf;
589 }
590
591 ctx->size += ctx->buf->last - ctx->buf->pos;
592 }
593
594 ctx->buf = ngx_create_temp_buf(ctx->pool, ctx->alloc_size);
595 if (ctx->buf == NULL) {
596 return NULL;
597 }
598
599 cl = ngx_alloc_chain_link(ctx->pool);
600 if (cl == NULL) {
601 return NULL;
602 }
603
604 cl->buf = ctx->buf;
605 cl->next = NULL;
606
607 *ctx->last_out = cl;
608 ctx->last_out = &cl->next;
609
610 return ctx->buf;
611 }
612
613 #endif
614
615
616 static ngx_int_t
617 ngx_http_autoindex_error(ngx_http_request_t *r, ngx_dir_t *dir, ngx_str_t *name)
618 {
619 if (ngx_close_dir(dir) == NGX_ERROR) {
620 ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno,
621 ngx_close_dir_n " \"%V\" failed", name);
622 }
623
624 return NGX_HTTP_INTERNAL_SERVER_ERROR;
625 }
626
627
628 static void *
629 ngx_http_autoindex_create_loc_conf(ngx_conf_t *cf)
630 {
631 ngx_http_autoindex_loc_conf_t *conf;
632
633 conf = ngx_palloc(cf->pool, sizeof(ngx_http_autoindex_loc_conf_t));
634 if (conf == NULL) {
635 return NULL;
636 }
637
638 conf->enable = NGX_CONF_UNSET;
639 conf->localtime = NGX_CONF_UNSET;
640 conf->exact_size = NGX_CONF_UNSET;
641
642 return conf;
643 }
644
645
646 static char *
647 ngx_http_autoindex_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
648 {
649 ngx_http_autoindex_loc_conf_t *prev = parent;
650 ngx_http_autoindex_loc_conf_t *conf = child;
651
652 ngx_conf_merge_value(conf->enable, prev->enable, 0);
653 ngx_conf_merge_value(conf->localtime, prev->localtime, 0);
654 ngx_conf_merge_value(conf->exact_size, prev->exact_size, 1);
655
656 return NGX_CONF_OK;
657 }
658
659
660 static ngx_int_t
661 ngx_http_autoindex_init(ngx_conf_t *cf)
662 {
663 ngx_http_handler_pt *h;
664 ngx_http_core_main_conf_t *cmcf;
665
666 cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module);
667
668 h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers);
669 if (h == NULL) {
670 return NGX_ERROR;
671 }
672
673 *h = ngx_http_autoindex_handler;
674
675 return NGX_OK;
676 }
677
This page was automatically generated by the
LXR engine.
Visit the LXR main site for more
information.