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