Mercurial > hg > nginx
annotate src/http/modules/ngx_http_autoindex_module.c @ 4253:6efec8b1ff52 stable-1.0
Merging r4193, r4194:
Autoindex fixes:
*) Autoindex: escape '?' in file names.
For files with '?' in their names autoindex generated links with '?' not
escaped. This resulted in effectively truncated links as '?' indicates
query string start.
This is an updated version of the patch originally posted at [1]. It
introduces generic NGX_ESCAPE_URI_COMPONENT which escapes everything but
unreserved characters as per RFC 3986. This approach also renders unneeded
special colon processing (as colon is percent-encoded now), it's dropped
accordingly.
[1] http://nginx.org/pipermail/nginx-devel/2010-February/000112.html
*) Autoindex: escape html in file names.
author | Igor Sysoev <igor@sysoev.ru> |
---|---|
date | Tue, 01 Nov 2011 14:09:15 +0000 |
parents | 84905c7b2aa7 |
children | 4919fb357a5d |
rev | line source |
---|---|
457 | 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; | |
525 | 27 size_t utf_len; |
2849
6a62bed048cd
fix colon in file name for ngx_http_autoindex_module
Igor Sysoev <igor@sysoev.ru>
parents:
2721
diff
changeset
|
28 size_t escape; |
4253 | 29 size_t escape_html; |
2849
6a62bed048cd
fix colon in file name for ngx_http_autoindex_module
Igor Sysoev <igor@sysoev.ru>
parents:
2721
diff
changeset
|
30 |
6a62bed048cd
fix colon in file name for ngx_http_autoindex_module
Igor Sysoev <igor@sysoev.ru>
parents:
2721
diff
changeset
|
31 unsigned dir:1; |
6a62bed048cd
fix colon in file name for ngx_http_autoindex_module
Igor Sysoev <igor@sysoev.ru>
parents:
2721
diff
changeset
|
32 |
457 | 33 time_t mtime; |
34 off_t size; | |
35 } ngx_http_autoindex_entry_t; | |
36 | |
37 | |
38 typedef struct { | |
39 ngx_flag_t enable; | |
519 | 40 ngx_flag_t localtime; |
527 | 41 ngx_flag_t exact_size; |
457 | 42 } ngx_http_autoindex_loc_conf_t; |
43 | |
44 | |
557 | 45 #define NGX_HTTP_AUTOINDEX_PREALLOCATE 50 |
46 | |
47 #define NGX_HTTP_AUTOINDEX_NAME_LEN 50 | |
457 | 48 |
49 | |
503 | 50 static int ngx_libc_cdecl ngx_http_autoindex_cmp_entries(const void *one, |
51 const void *two); | |
499 | 52 static ngx_int_t ngx_http_autoindex_error(ngx_http_request_t *r, |
557 | 53 ngx_dir_t *dir, ngx_str_t *name); |
681 | 54 static ngx_int_t ngx_http_autoindex_init(ngx_conf_t *cf); |
457 | 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, | |
499 | 57 void *parent, void *child); |
457 | 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 | |
519 | 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 | |
527 | 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 | |
457 | 83 ngx_null_command |
84 }; | |
85 | |
86 | |
667 | 87 static ngx_http_module_t ngx_http_autoindex_module_ctx = { |
509 | 88 NULL, /* preconfiguration */ |
681 | 89 ngx_http_autoindex_init, /* postconfiguration */ |
457 | 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 = { | |
509 | 103 NGX_MODULE_V1, |
577 | 104 &ngx_http_autoindex_module_ctx, /* module context */ |
457 | 105 ngx_http_autoindex_commands, /* module directives */ |
106 NGX_HTTP_MODULE, /* module type */ | |
541 | 107 NULL, /* init master */ |
681 | 108 NULL, /* init module */ |
541 | 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 | |
457 | 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 | |
499 | 136 static ngx_int_t |
137 ngx_http_autoindex_handler(ngx_http_request_t *r) | |
457 | 138 { |
557 | 139 u_char *last, *filename, scale; |
527 | 140 off_t length; |
4253 | 141 size_t len, char_len, escape_html, allocated, root; |
457 | 142 ngx_tm_t tm; |
143 ngx_err_t err; | |
144 ngx_buf_t *b; | |
557 | 145 ngx_int_t rc, size; |
146 ngx_str_t path; | |
457 | 147 ngx_dir_t dir; |
2889
0bb8c54f4c45
refactor ngx_http_charset_header_filter()
Igor Sysoev <igor@sysoev.ru>
parents:
2849
diff
changeset
|
148 ngx_uint_t i, level, utf8; |
457 | 149 ngx_pool_t *pool; |
563 | 150 ngx_time_t *tp; |
557 | 151 ngx_chain_t out; |
457 | 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 | |
645 | 163 if (!(r->method & (NGX_HTTP_GET|NGX_HTTP_HEAD))) { |
637 | 164 return NGX_DECLINED; |
165 } | |
166 | |
457 | 167 alcf = ngx_http_get_module_loc_conf(r, ngx_http_autoindex_module); |
168 | |
169 if (!alcf->enable) { | |
170 return NGX_DECLINED; | |
171 } | |
172 | |
557 | 173 /* NGX_DIR_MASK_LEN is lesser than NGX_HTTP_AUTOINDEX_PREALLOCATE */ |
457 | 174 |
773 | 175 last = ngx_http_map_uri_to_path(r, &path, &root, |
176 NGX_HTTP_AUTOINDEX_PREALLOCATE); | |
557 | 177 if (last == NULL) { |
178 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
457 | 179 } |
180 | |
557 | 181 allocated = path.len; |
1627 | 182 path.len = last - path.data; |
183 if (path.len > 1) { | |
184 path.len--; | |
185 } | |
557 | 186 path.data[path.len] = '\0'; |
457 | 187 |
188 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
557 | 189 "http autoindex: \"%s\"", path.data); |
457 | 190 |
557 | 191 if (ngx_open_dir(&path, &dir) == NGX_ERROR) { |
457 | 192 err = ngx_errno; |
193 | |
543 | 194 if (err == NGX_ENOENT |
195 || err == NGX_ENOTDIR | |
196 || err == NGX_ENAMETOOLONG) | |
197 { | |
457 | 198 level = NGX_LOG_ERR; |
199 rc = NGX_HTTP_NOT_FOUND; | |
200 | |
201 } else if (err == NGX_EACCES) { | |
202 level = NGX_LOG_ERR; | |
203 rc = NGX_HTTP_FORBIDDEN; | |
204 | |
205 } else { | |
206 level = NGX_LOG_CRIT; | |
207 rc = NGX_HTTP_INTERNAL_SERVER_ERROR; | |
208 } | |
209 | |
210 ngx_log_error(level, r->connection->log, err, | |
557 | 211 ngx_open_dir_n " \"%s\" failed", path.data); |
457 | 212 |
213 return rc; | |
214 } | |
215 | |
216 #if (NGX_SUPPRESS_WARN) | |
557 | 217 |
457 | 218 /* MSVC thinks 'entries' may be used without having been initialized */ |
219 ngx_memzero(&entries, sizeof(ngx_array_t)); | |
557 | 220 |
457 | 221 #endif |
222 | |
557 | 223 /* TODO: pool should be temporary pool */ |
224 pool = r->pool; | |
225 | |
226 if (ngx_array_init(&entries, pool, 40, sizeof(ngx_http_autoindex_entry_t)) | |
227 != NGX_OK) | |
457 | 228 { |
557 | 229 return ngx_http_autoindex_error(r, &dir, &path); |
457 | 230 } |
231 | |
523 | 232 r->headers_out.status = NGX_HTTP_OK; |
842
d8e2613a2b55
charset could not be set for ngx_http_autoindex_module responses
Igor Sysoev <igor@sysoev.ru>
parents:
773
diff
changeset
|
233 r->headers_out.content_type_len = sizeof("text/html") - 1; |
3516
dd1570b6f237
ngx_str_set() and ngx_str_null()
Igor Sysoev <igor@sysoev.ru>
parents:
2912
diff
changeset
|
234 ngx_str_set(&r->headers_out.content_type, "text/html"); |
523 | 235 |
236 rc = ngx_http_send_header(r); | |
237 | |
238 if (rc == NGX_ERROR || rc > NGX_OK || r->header_only) { | |
1499
090e391f53db
fix file leak for HEAD requests
Igor Sysoev <igor@sysoev.ru>
parents:
842
diff
changeset
|
239 if (ngx_close_dir(&dir) == NGX_ERROR) { |
090e391f53db
fix file leak for HEAD requests
Igor Sysoev <igor@sysoev.ru>
parents:
842
diff
changeset
|
240 ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno, |
090e391f53db
fix file leak for HEAD requests
Igor Sysoev <igor@sysoev.ru>
parents:
842
diff
changeset
|
241 ngx_close_dir_n " \"%V\" failed", &path); |
090e391f53db
fix file leak for HEAD requests
Igor Sysoev <igor@sysoev.ru>
parents:
842
diff
changeset
|
242 } |
090e391f53db
fix file leak for HEAD requests
Igor Sysoev <igor@sysoev.ru>
parents:
842
diff
changeset
|
243 |
523 | 244 return rc; |
245 } | |
246 | |
557 | 247 filename = path.data; |
248 filename[path.len] = '/'; | |
457 | 249 |
2889
0bb8c54f4c45
refactor ngx_http_charset_header_filter()
Igor Sysoev <igor@sysoev.ru>
parents:
2849
diff
changeset
|
250 if (r->headers_out.charset.len == 5 |
0bb8c54f4c45
refactor ngx_http_charset_header_filter()
Igor Sysoev <igor@sysoev.ru>
parents:
2849
diff
changeset
|
251 && ngx_strncasecmp(r->headers_out.charset.data, (u_char *) "utf-8", 5) |
0bb8c54f4c45
refactor ngx_http_charset_header_filter()
Igor Sysoev <igor@sysoev.ru>
parents:
2849
diff
changeset
|
252 == 0) |
0bb8c54f4c45
refactor ngx_http_charset_header_filter()
Igor Sysoev <igor@sysoev.ru>
parents:
2849
diff
changeset
|
253 { |
0bb8c54f4c45
refactor ngx_http_charset_header_filter()
Igor Sysoev <igor@sysoev.ru>
parents:
2849
diff
changeset
|
254 utf8 = 1; |
0bb8c54f4c45
refactor ngx_http_charset_header_filter()
Igor Sysoev <igor@sysoev.ru>
parents:
2849
diff
changeset
|
255 |
0bb8c54f4c45
refactor ngx_http_charset_header_filter()
Igor Sysoev <igor@sysoev.ru>
parents:
2849
diff
changeset
|
256 } else { |
0bb8c54f4c45
refactor ngx_http_charset_header_filter()
Igor Sysoev <igor@sysoev.ru>
parents:
2849
diff
changeset
|
257 utf8 = 0; |
0bb8c54f4c45
refactor ngx_http_charset_header_filter()
Igor Sysoev <igor@sysoev.ru>
parents:
2849
diff
changeset
|
258 } |
0bb8c54f4c45
refactor ngx_http_charset_header_filter()
Igor Sysoev <igor@sysoev.ru>
parents:
2849
diff
changeset
|
259 |
457 | 260 for ( ;; ) { |
261 ngx_set_errno(0); | |
262 | |
263 if (ngx_read_dir(&dir) == NGX_ERROR) { | |
264 err = ngx_errno; | |
265 | |
501 | 266 if (err != NGX_ENOMOREFILES) { |
457 | 267 ngx_log_error(NGX_LOG_CRIT, r->connection->log, err, |
557 | 268 ngx_read_dir_n " \"%V\" failed", &path); |
269 return ngx_http_autoindex_error(r, &dir, &path); | |
457 | 270 } |
271 | |
577 | 272 break; |
457 | 273 } |
274 | |
275 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, | |
276 "http autoindex file: \"%s\"", ngx_de_name(&dir)); | |
277 | |
278 len = ngx_de_namelen(&dir); | |
279 | |
547 | 280 if (ngx_de_name(&dir)[0] == '.') { |
459 | 281 continue; |
282 } | |
283 | |
457 | 284 if (!dir.valid_info) { |
285 | |
557 | 286 /* 1 byte for '/' and 1 byte for terminating '\0' */ |
457 | 287 |
557 | 288 if (path.len + 1 + len + 1 > allocated) { |
289 allocated = path.len + 1 + len + 1 | |
290 + NGX_HTTP_AUTOINDEX_PREALLOCATE; | |
291 | |
2049 | 292 filename = ngx_pnalloc(pool, allocated); |
557 | 293 if (filename == NULL) { |
294 return ngx_http_autoindex_error(r, &dir, &path); | |
457 | 295 } |
296 | |
557 | 297 last = ngx_cpystrn(filename, path.data, path.len + 1); |
457 | 298 *last++ = '/'; |
299 } | |
300 | |
301 ngx_cpystrn(last, ngx_de_name(&dir), len + 1); | |
302 | |
557 | 303 if (ngx_de_info(filename, &dir) == NGX_FILE_ERROR) { |
497 | 304 err = ngx_errno; |
305 | |
306 if (err != NGX_ENOENT) { | |
307 ngx_log_error(NGX_LOG_CRIT, r->connection->log, err, | |
557 | 308 ngx_de_info_n " \"%s\" failed", filename); |
2371
b438ffe54e34
skip protected symlinks in autoindex
Igor Sysoev <igor@sysoev.ru>
parents:
2125
diff
changeset
|
309 |
b438ffe54e34
skip protected symlinks in autoindex
Igor Sysoev <igor@sysoev.ru>
parents:
2125
diff
changeset
|
310 if (err == NGX_EACCES) { |
b438ffe54e34
skip protected symlinks in autoindex
Igor Sysoev <igor@sysoev.ru>
parents:
2125
diff
changeset
|
311 continue; |
b438ffe54e34
skip protected symlinks in autoindex
Igor Sysoev <igor@sysoev.ru>
parents:
2125
diff
changeset
|
312 } |
b438ffe54e34
skip protected symlinks in autoindex
Igor Sysoev <igor@sysoev.ru>
parents:
2125
diff
changeset
|
313 |
557 | 314 return ngx_http_autoindex_error(r, &dir, &path); |
497 | 315 } |
316 | |
557 | 317 if (ngx_de_link_info(filename, &dir) == NGX_FILE_ERROR) { |
497 | 318 ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, |
319 ngx_de_link_info_n " \"%s\" failed", | |
557 | 320 filename); |
321 return ngx_http_autoindex_error(r, &dir, &path); | |
497 | 322 } |
457 | 323 } |
324 } | |
325 | |
501 | 326 entry = ngx_array_push(&entries); |
327 if (entry == NULL) { | |
557 | 328 return ngx_http_autoindex_error(r, &dir, &path); |
457 | 329 } |
330 | |
577 | 331 entry->name.len = len; |
525 | 332 |
2049 | 333 entry->name.data = ngx_pnalloc(pool, len + 1); |
501 | 334 if (entry->name.data == NULL) { |
557 | 335 return ngx_http_autoindex_error(r, &dir, &path); |
457 | 336 } |
461 | 337 |
457 | 338 ngx_cpystrn(entry->name.data, ngx_de_name(&dir), len + 1); |
339 | |
529 | 340 entry->escape = 2 * ngx_escape_uri(NULL, ngx_de_name(&dir), len, |
4253 | 341 NGX_ESCAPE_URI_COMPONENT); |
342 | |
343 entry->escape_html = ngx_escape_html(NULL, entry->name.data, | |
344 entry->name.len); | |
529 | 345 |
2889
0bb8c54f4c45
refactor ngx_http_charset_header_filter()
Igor Sysoev <igor@sysoev.ru>
parents:
2849
diff
changeset
|
346 if (utf8) { |
2125
8e4b9d2acde8
rename ngx_utf_...() to ngx_utf8_...()
Igor Sysoev <igor@sysoev.ru>
parents:
2120
diff
changeset
|
347 entry->utf_len = ngx_utf8_length(entry->name.data, entry->name.len); |
525 | 348 } else { |
349 entry->utf_len = len; | |
350 } | |
351 | |
457 | 352 entry->dir = ngx_de_is_dir(&dir); |
353 entry->mtime = ngx_de_mtime(&dir); | |
354 entry->size = ngx_de_size(&dir); | |
355 } | |
356 | |
357 if (ngx_close_dir(&dir) == NGX_ERROR) { | |
358 ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno, | |
557 | 359 ngx_close_dir_n " \"%s\" failed", &path); |
457 | 360 } |
361 | |
4253 | 362 escape_html = ngx_escape_html(NULL, r->uri.data, r->uri.len); |
363 | |
457 | 364 len = sizeof(title) - 1 |
4253 | 365 + r->uri.len + escape_html |
457 | 366 + sizeof(header) - 1 |
4253 | 367 + r->uri.len + escape_html |
457 | 368 + sizeof("</h1>") - 1 |
369 + sizeof("<hr><pre><a href=\"../\">../</a>" CRLF) - 1 | |
370 + sizeof("</pre><hr>") - 1 | |
371 + sizeof(tail) - 1; | |
372 | |
373 entry = entries.elts; | |
374 for (i = 0; i < entries.nelts; i++) { | |
375 len += sizeof("<a href=\"") - 1 | |
529 | 376 + entry[i].name.len + entry[i].escape |
525 | 377 + 1 /* 1 is for "/" */ |
378 + sizeof("\">") - 1 | |
4253 | 379 + entry[i].name.len - entry[i].utf_len |
380 + entry[i].escape_html | |
525 | 381 + NGX_HTTP_AUTOINDEX_NAME_LEN + sizeof(">") - 2 |
382 + sizeof("</a>") - 1 | |
383 + sizeof(" 28-Sep-1970 12:00 ") - 1 | |
529 | 384 + 20 /* the file size */ |
525 | 385 + 2; |
457 | 386 } |
387 | |
501 | 388 b = ngx_create_temp_buf(r->pool, len); |
389 if (b == NULL) { | |
457 | 390 return NGX_HTTP_INTERNAL_SERVER_ERROR; |
391 } | |
392 | |
393 if (entries.nelts > 1) { | |
394 ngx_qsort(entry, (size_t) entries.nelts, | |
395 sizeof(ngx_http_autoindex_entry_t), | |
396 ngx_http_autoindex_cmp_entries); | |
397 } | |
398 | |
399 b->last = ngx_cpymem(b->last, title, sizeof(title) - 1); | |
4253 | 400 |
401 if (escape_html) { | |
402 b->last = (u_char *) ngx_escape_html(b->last, r->uri.data, r->uri.len); | |
403 b->last = ngx_cpymem(b->last, header, sizeof(header) - 1); | |
404 b->last = (u_char *) ngx_escape_html(b->last, r->uri.data, r->uri.len); | |
405 | |
406 } else { | |
407 b->last = ngx_cpymem(b->last, r->uri.data, r->uri.len); | |
408 b->last = ngx_cpymem(b->last, header, sizeof(header) - 1); | |
409 b->last = ngx_cpymem(b->last, r->uri.data, r->uri.len); | |
410 } | |
411 | |
457 | 412 b->last = ngx_cpymem(b->last, "</h1>", sizeof("</h1>") - 1); |
413 | |
414 b->last = ngx_cpymem(b->last, "<hr><pre><a href=\"../\">../</a>" CRLF, | |
415 sizeof("<hr><pre><a href=\"../\">../</a>" CRLF) - 1); | |
416 | |
563 | 417 tp = ngx_timeofday(); |
418 | |
457 | 419 for (i = 0; i < entries.nelts; i++) { |
420 b->last = ngx_cpymem(b->last, "<a href=\"", sizeof("<a href=\"") - 1); | |
461 | 421 |
422 if (entry[i].escape) { | |
423 ngx_escape_uri(b->last, entry[i].name.data, entry[i].name.len, | |
4253 | 424 NGX_ESCAPE_URI_COMPONENT); |
461 | 425 |
426 b->last += entry[i].name.len + entry[i].escape; | |
427 | |
428 } else { | |
429 b->last = ngx_cpymem(b->last, entry[i].name.data, | |
430 entry[i].name.len); | |
431 } | |
457 | 432 |
433 if (entry[i].dir) { | |
434 *b->last++ = '/'; | |
435 } | |
436 | |
437 *b->last++ = '"'; | |
438 *b->last++ = '>'; | |
439 | |
525 | 440 len = entry[i].utf_len; |
457 | 441 |
2120 | 442 if (entry[i].name.len != len) { |
527 | 443 if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) { |
4253 | 444 char_len = NGX_HTTP_AUTOINDEX_NAME_LEN - 3 + 1; |
527 | 445 |
446 } else { | |
4253 | 447 char_len = NGX_HTTP_AUTOINDEX_NAME_LEN + 1; |
527 | 448 } |
449 | |
4253 | 450 last = b->last; |
2125
8e4b9d2acde8
rename ngx_utf_...() to ngx_utf8_...()
Igor Sysoev <igor@sysoev.ru>
parents:
2120
diff
changeset
|
451 b->last = ngx_utf8_cpystrn(b->last, entry[i].name.data, |
4253 | 452 char_len, entry[i].name.len + 1); |
453 | |
454 if (entry[i].escape_html) { | |
455 b->last = (u_char *) ngx_escape_html(last, entry[i].name.data, | |
456 b->last - last); | |
457 } | |
458 | |
527 | 459 last = b->last; |
460 | |
461 } else { | |
4253 | 462 if (entry[i].escape_html) { |
463 if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) { | |
464 char_len = NGX_HTTP_AUTOINDEX_NAME_LEN - 3; | |
465 | |
466 } else { | |
467 char_len = len; | |
468 } | |
469 | |
470 b->last = (u_char *) ngx_escape_html(b->last, | |
471 entry[i].name.data, char_len); | |
472 last = b->last; | |
473 | |
474 } else { | |
475 b->last = ngx_cpystrn(b->last, entry[i].name.data, | |
476 NGX_HTTP_AUTOINDEX_NAME_LEN + 1); | |
477 last = b->last - 3; | |
478 } | |
527 | 479 } |
480 | |
457 | 481 if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) { |
527 | 482 b->last = ngx_cpymem(last, "..></a>", sizeof("..></a>") - 1); |
457 | 483 |
484 } else { | |
485 if (entry[i].dir && NGX_HTTP_AUTOINDEX_NAME_LEN - len > 0) { | |
486 *b->last++ = '/'; | |
487 len++; | |
488 } | |
489 | |
490 b->last = ngx_cpymem(b->last, "</a>", sizeof("</a>") - 1); | |
491 ngx_memset(b->last, ' ', NGX_HTTP_AUTOINDEX_NAME_LEN - len); | |
492 b->last += NGX_HTTP_AUTOINDEX_NAME_LEN - len; | |
493 } | |
494 | |
495 *b->last++ = ' '; | |
496 | |
563 | 497 ngx_gmtime(entry[i].mtime + tp->gmtoff * 60 * alcf->localtime, &tm); |
457 | 498 |
499 b->last = ngx_sprintf(b->last, "%02d-%s-%d %02d:%02d ", | |
500 tm.ngx_tm_mday, | |
501 months[tm.ngx_tm_mon - 1], | |
502 tm.ngx_tm_year, | |
503 tm.ngx_tm_hour, | |
504 tm.ngx_tm_min); | |
505 | |
527 | 506 if (alcf->exact_size) { |
507 if (entry[i].dir) { | |
508 b->last = ngx_cpymem(b->last, " -", | |
509 sizeof(" -") - 1); | |
510 } else { | |
511 b->last = ngx_sprintf(b->last, "%19O", entry[i].size); | |
577 | 512 } |
457 | 513 |
514 } else { | |
527 | 515 if (entry[i].dir) { |
547 | 516 b->last = ngx_cpymem(b->last, " -", |
517 sizeof(" -") - 1); | |
527 | 518 |
519 } else { | |
520 length = entry[i].size; | |
521 | |
522 if (length > 1024 * 1024 * 1024 - 1) { | |
523 size = (ngx_int_t) (length / (1024 * 1024 * 1024)); | |
524 if ((length % (1024 * 1024 * 1024)) | |
525 > (1024 * 1024 * 1024 / 2 - 1)) | |
577 | 526 { |
527 | 527 size++; |
528 } | |
529 scale = 'G'; | |
530 | |
531 } else if (length > 1024 * 1024 - 1) { | |
532 size = (ngx_int_t) (length / (1024 * 1024)); | |
533 if ((length % (1024 * 1024)) > (1024 * 1024 / 2 - 1)) { | |
534 size++; | |
535 } | |
536 scale = 'M'; | |
537 | |
538 } else if (length > 9999) { | |
539 size = (ngx_int_t) (length / 1024); | |
540 if (length % 1024 > 511) { | |
541 size++; | |
542 } | |
543 scale = 'K'; | |
544 | |
545 } else { | |
546 size = (ngx_int_t) length; | |
547 | 547 scale = '\0'; |
527 | 548 } |
549 | |
547 | 550 if (scale) { |
551 b->last = ngx_sprintf(b->last, "%6i%c", size, scale); | |
527 | 552 |
547 | 553 } else { |
554 b->last = ngx_sprintf(b->last, " %6i", size); | |
527 | 555 } |
556 } | |
457 | 557 } |
558 | |
559 *b->last++ = CR; | |
560 *b->last++ = LF; | |
561 } | |
562 | |
459 | 563 /* TODO: free temporary pool */ |
564 | |
457 | 565 b->last = ngx_cpymem(b->last, "</pre><hr>", sizeof("</pre><hr>") - 1); |
566 | |
567 b->last = ngx_cpymem(b->last, tail, sizeof(tail) - 1); | |
568 | |
597 | 569 if (r == r->main) { |
457 | 570 b->last_buf = 1; |
571 } | |
572 | |
509 | 573 b->last_in_chain = 1; |
574 | |
457 | 575 out.buf = b; |
576 out.next = NULL; | |
577 | |
578 return ngx_http_output_filter(r, &out); | |
579 } | |
580 | |
581 | |
503 | 582 static int ngx_libc_cdecl |
499 | 583 ngx_http_autoindex_cmp_entries(const void *one, const void *two) |
457 | 584 { |
585 ngx_http_autoindex_entry_t *first = (ngx_http_autoindex_entry_t *) one; | |
586 ngx_http_autoindex_entry_t *second = (ngx_http_autoindex_entry_t *) two; | |
587 | |
588 if (first->dir && !second->dir) { | |
589 /* move the directories to the start */ | |
590 return -1; | |
591 } | |
592 | |
593 if (!first->dir && second->dir) { | |
594 /* move the directories to the start */ | |
595 return 1; | |
596 } | |
597 | |
598 return (int) ngx_strcmp(first->name.data, second->name.data); | |
599 } | |
600 | |
601 | |
602 #if 0 | |
603 | |
499 | 604 static ngx_buf_t * |
605 ngx_http_autoindex_alloc(ngx_http_autoindex_ctx_t *ctx, size_t size) | |
457 | 606 { |
607 ngx_chain_t *cl; | |
608 | |
609 if (ctx->buf) { | |
610 | |
611 if ((size_t) (ctx->buf->end - ctx->buf->last) >= size) { | |
612 return ctx->buf; | |
613 } | |
614 | |
615 ctx->size += ctx->buf->last - ctx->buf->pos; | |
616 } | |
617 | |
501 | 618 ctx->buf = ngx_create_temp_buf(ctx->pool, ctx->alloc_size); |
619 if (ctx->buf == NULL) { | |
457 | 620 return NULL; |
621 } | |
622 | |
501 | 623 cl = ngx_alloc_chain_link(ctx->pool); |
624 if (cl == NULL) { | |
457 | 625 return NULL; |
626 } | |
627 | |
628 cl->buf = ctx->buf; | |
629 cl->next = NULL; | |
630 | |
631 *ctx->last_out = cl; | |
632 ctx->last_out = &cl->next; | |
633 | |
634 return ctx->buf; | |
635 } | |
636 | |
637 #endif | |
638 | |
639 | |
499 | 640 static ngx_int_t |
557 | 641 ngx_http_autoindex_error(ngx_http_request_t *r, ngx_dir_t *dir, ngx_str_t *name) |
457 | 642 { |
643 if (ngx_close_dir(dir) == NGX_ERROR) { | |
644 ngx_log_error(NGX_LOG_ALERT, r->connection->log, ngx_errno, | |
557 | 645 ngx_close_dir_n " \"%V\" failed", name); |
457 | 646 } |
647 | |
648 return NGX_HTTP_INTERNAL_SERVER_ERROR; | |
649 } | |
650 | |
651 | |
499 | 652 static void * |
653 ngx_http_autoindex_create_loc_conf(ngx_conf_t *cf) | |
457 | 654 { |
655 ngx_http_autoindex_loc_conf_t *conf; | |
656 | |
657 conf = ngx_palloc(cf->pool, sizeof(ngx_http_autoindex_loc_conf_t)); | |
658 if (conf == NULL) { | |
2912
c7d57b539248
return NULL instead of NGX_CONF_ERROR on a create conf failure
Igor Sysoev <igor@sysoev.ru>
parents:
2889
diff
changeset
|
659 return NULL; |
457 | 660 } |
661 | |
662 conf->enable = NGX_CONF_UNSET; | |
519 | 663 conf->localtime = NGX_CONF_UNSET; |
527 | 664 conf->exact_size = NGX_CONF_UNSET; |
457 | 665 |
666 return conf; | |
667 } | |
668 | |
669 | |
499 | 670 static char * |
671 ngx_http_autoindex_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) | |
457 | 672 { |
673 ngx_http_autoindex_loc_conf_t *prev = parent; | |
674 ngx_http_autoindex_loc_conf_t *conf = child; | |
675 | |
676 ngx_conf_merge_value(conf->enable, prev->enable, 0); | |
519 | 677 ngx_conf_merge_value(conf->localtime, prev->localtime, 0); |
527 | 678 ngx_conf_merge_value(conf->exact_size, prev->exact_size, 1); |
457 | 679 |
680 return NGX_CONF_OK; | |
681 } | |
681 | 682 |
683 | |
684 static ngx_int_t | |
685 ngx_http_autoindex_init(ngx_conf_t *cf) | |
686 { | |
687 ngx_http_handler_pt *h; | |
688 ngx_http_core_main_conf_t *cmcf; | |
689 | |
690 cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); | |
691 | |
692 h = ngx_array_push(&cmcf->phases[NGX_HTTP_CONTENT_PHASE].handlers); | |
693 if (h == NULL) { | |
694 return NGX_ERROR; | |
695 } | |
696 | |
697 *h = ngx_http_autoindex_handler; | |
698 | |
699 return NGX_OK; | |
700 } |