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