1 /*
2 * Copyright (C) 2006, 2008 Valery Kholodkov
3 * Client body reception code Copyright (c) 2002-2007 Igor Sysoev
4 * Temporary file name generation code Copyright (c) 2002-2007 Igor Sysoev
5 */
6 #include <ngx_config.h>
7 #include <ngx_core.h>
8 #include <ngx_http.h>
9 #include <nginx.h>
10
11 #if nginx_version >= 1011002
12
13 #include <ngx_md5.h>
14
15 typedef ngx_md5_t MD5_CTX;
16
17 #define MD5Init ngx_md5_init
18 #define MD5Update ngx_md5_update
19 #define MD5Final ngx_md5_final
20
21 #define MD5_DIGEST_LENGTH 16
22
23 #include <openssl/sha.h>
24
25 #else
26
27 #if (NGX_HAVE_OPENSSL_MD5_H)
28 #include <openssl/md5.h>
29 #else
30 #include <md5.h>
31 #endif
32
33 #if (NGX_OPENSSL_MD5)
34 #define MD5Init MD5_Init
35 #define MD5Update MD5_Update
36 #define MD5Final MD5_Final
37 #endif
38
39 #if (NGX_HAVE_OPENSSL_SHA1_H)
40 #include <openssl/sha.h>
41 #else
42 #include <sha.h>
43 #endif
44
45
46 #endif
47
48 #define MULTIPART_FORM_DATA_STRING "multipart/form-data"
49 #define BOUNDARY_STRING "boundary="
50 #define CONTENT_DISPOSITION_STRING "Content-Disposition:"
51 #define CONTENT_TYPE_STRING "Content-Type:"
52 #define CONTENT_RANGE_STRING "Content-Range:"
53 #define X_CONTENT_RANGE_STRING "X-Content-Range:"
54 #define SESSION_ID_STRING "Session-ID:"
55 #define X_SESSION_ID_STRING "X-Session-ID:"
56 #define FORM_DATA_STRING "form-data"
57 #define ATTACHMENT_STRING "attachment"
58 #define FILENAME_STRING "filename=\""
59 #define FIELDNAME_STRING "name=\""
60 #define BYTES_UNIT_STRING "bytes "
61
62 #define NGX_UPLOAD_MALFORMED -11
63 #define NGX_UPLOAD_NOMEM -12
64 #define NGX_UPLOAD_IOERROR -13
65 #define NGX_UPLOAD_SCRIPTERROR -14
66 #define NGX_UPLOAD_TOOLARGE -15
67
68 #ifndef NGX_HTTP_V2
69 #define NGX_HTTP_V2 0
70 #endif
71
72 /*
73 * State of multipart/form-data parser
74 */
75 typedef enum {
76 upload_state_boundary_seek,
77 upload_state_after_boundary,
78 upload_state_headers,
79 upload_state_data,
80 upload_state_finish
81 } upload_state_t;
82
83 /*
84 * Range
85 */
86 typedef struct {
87 off_t start, end, total;
88 } ngx_http_upload_range_t;
89
90 /*
91 * State of range merger
92 */
93 typedef struct {
94 ngx_buf_t *in_buf;
95 ngx_buf_t *out_buf;
96 ngx_http_upload_range_t current_range_n;
97 off_t *parser_state;
98 ngx_log_t *log;
99
100 u_char *range_header_buffer;
101 u_char *range_header_buffer_end;
102 u_char **range_header_buffer_pos;
103
104 unsigned int found_lower_bound:1;
105 unsigned int complete_ranges:1;
106 unsigned int first_range:1;
107 } ngx_http_upload_merger_state_t;
108
109 /*
110 * Template for a field to generate in output form
111 */
112 typedef struct {
113 ngx_table_elt_t value;
114 ngx_array_t *field_lengths;
115 ngx_array_t *field_values;
116 ngx_array_t *value_lengths;
117 ngx_array_t *value_values;
118 } ngx_http_upload_field_template_t;
119
120 /*
121 * Template for a header
122 */
123 typedef struct {
124 ngx_http_complex_value_t *name;
125 ngx_http_complex_value_t *value;
126 } ngx_http_upload_header_template_t;
127
128 /*
129 * Filter for fields in output form
130 */
131 typedef struct {
132 #if (NGX_PCRE)
133 ngx_regex_t *regex;
134 ngx_int_t ncaptures;
135 #else
136 ngx_str_t text;
137 #endif
138 } ngx_http_upload_field_filter_t;
139
140 typedef struct {
141 ngx_path_t *path;
142 ngx_http_complex_value_t dynamic;
143 unsigned is_dynamic:1;
144 } ngx_http_upload_path_t;
145
146 /*
147 * Upload cleanup record
148 */
149 typedef struct ngx_http_upload_cleanup_s {
150 ngx_fd_t fd;
151 u_char *filename;
152 ngx_http_headers_out_t *headers_out;
153 ngx_array_t *cleanup_statuses;
154 ngx_log_t *log;
155 unsigned int aborted:1;
156 } ngx_upload_cleanup_t;
157
158 /*
159 * Upload configuration for specific location
160 */
161 typedef struct {
162 ngx_str_t url;
163 ngx_http_complex_value_t *url_cv;
164 ngx_http_upload_path_t *state_store_path;
165 ngx_http_upload_path_t *store_path;
166 ngx_uint_t store_access;
167 size_t buffer_size;
168 size_t merge_buffer_size;
169 size_t range_header_buffer_size;
170 size_t max_header_len;
171 size_t max_output_body_len;
172 off_t max_file_size;
173 ngx_array_t *field_templates;
174 ngx_array_t *aggregate_field_templates;
175 ngx_array_t *field_filters;
176 ngx_array_t *cleanup_statuses;
177 ngx_array_t *header_templates;
178 ngx_flag_t forward_args;
179 ngx_flag_t tame_arrays;
180 ngx_flag_t resumable_uploads;
181 ngx_flag_t empty_field_names;
182 size_t limit_rate;
183
184 unsigned int md5:1;
185 unsigned int sha1:1;
186 unsigned int sha256:1;
187 unsigned int sha512:1;
188 unsigned int crc32:1;
189 } ngx_http_upload_loc_conf_t;
190
191 typedef struct ngx_http_upload_md5_ctx_s {
192 MD5_CTX md5;
193 u_char md5_digest[MD5_DIGEST_LENGTH * 2];
194 } ngx_http_upload_md5_ctx_t;
195
196 typedef struct ngx_http_upload_sha1_ctx_s {
197 SHA_CTX sha1;
198 u_char sha1_digest[SHA_DIGEST_LENGTH * 2];
199 } ngx_http_upload_sha1_ctx_t;
200
201 typedef struct ngx_http_upload_sha256_ctx_s {
202 SHA256_CTX sha256;
203 u_char sha256_digest[SHA256_DIGEST_LENGTH * 2];
204 } ngx_http_upload_sha256_ctx_t;
205
206 typedef struct ngx_http_upload_sha512_ctx_s {
207 SHA512_CTX sha512;
208 u_char sha512_digest[SHA512_DIGEST_LENGTH * 2];
209 } ngx_http_upload_sha512_ctx_t;
210
211 struct ngx_http_upload_ctx_s;
212
213 /*
214 * Request body data handler
215 */
216 typedef ngx_int_t (*ngx_http_request_body_data_handler_pt)
217 (struct ngx_http_upload_ctx_s*, u_char *, u_char*);
218
219 /*
220 * Upload module context
221 */
222 typedef struct ngx_http_upload_ctx_s {
223 ngx_str_t session_id;
224 ngx_str_t boundary;
225 u_char *boundary_start;
226 u_char *boundary_pos;
227
228 upload_state_t state;
229
230 u_char *header_accumulator;
231 u_char *header_accumulator_end;
232 u_char *header_accumulator_pos;
233
234 ngx_str_t field_name;
235 ngx_str_t file_name;
236 ngx_str_t content_type;
237 ngx_str_t content_range;
238 ngx_http_upload_range_t content_range_n;
239
240 ngx_uint_t ordinal;
241
242 u_char *output_buffer;
243 u_char *output_buffer_end;
244 u_char *output_buffer_pos;
245 u_char *merge_buffer;
246 u_char *range_header_buffer;
247 u_char *range_header_buffer_pos;
248 u_char *range_header_buffer_end;
249
250 ngx_http_request_body_data_handler_pt data_handler;
251
252 ngx_int_t (*start_part_f)(struct ngx_http_upload_ctx_s *upload_ctx);
253 void (*finish_part_f)(struct ngx_http_upload_ctx_s *upload_ctx);
254 void (*abort_part_f)(struct ngx_http_upload_ctx_s *upload_ctx);
255 ngx_int_t (*flush_output_buffer_f)(struct ngx_http_upload_ctx_s *upload_ctx, u_char *buf, size_t len);
256
257 ngx_http_request_t *request;
258 ngx_log_t *log;
259
260 ngx_file_t output_file;
261 ngx_file_t state_file;
262 ngx_chain_t *chain;
263 ngx_chain_t *last;
264 ngx_chain_t *checkpoint;
265 ngx_chain_t *to_write;
266 size_t output_body_len;
267 size_t limit_rate;
268 ssize_t received;
269
270 ngx_pool_cleanup_t *cln;
271
272 ngx_http_upload_md5_ctx_t *md5_ctx;
273 ngx_http_upload_sha1_ctx_t *sha1_ctx;
274 ngx_http_upload_sha256_ctx_t *sha256_ctx;
275 ngx_http_upload_sha512_ctx_t *sha512_ctx;
276 uint32_t crc32;
277 ngx_path_t *store_path;
278 ngx_path_t *state_store_path;
279
280 unsigned int first_part:1;
281 unsigned int discard_data:1;
282 unsigned int is_file:1;
283 unsigned int partial_content:1;
284 unsigned int prevent_output:1;
285 unsigned int calculate_crc32:1;
286 unsigned int started:1;
287 unsigned int unencoded:1;
288 unsigned int no_content:1;
289 unsigned int raw_input:1;
290 } ngx_http_upload_ctx_t;
291
292 static ngx_int_t ngx_http_upload_test_expect(ngx_http_request_t *r);
293
294 #if (NGX_HTTP_V2)
295 static void ngx_http_upload_read_event_handler(ngx_http_request_t *r);
296 #endif
297 static ngx_int_t ngx_http_upload_handler(ngx_http_request_t *r);
298 static ngx_int_t ngx_http_upload_options_handler(ngx_http_request_t *r);
299 static ngx_int_t ngx_http_upload_body_handler(ngx_http_request_t *r);
300
301 static void *ngx_http_upload_create_loc_conf(ngx_conf_t *cf);
302 static char *ngx_http_upload_merge_loc_conf(ngx_conf_t *cf,
303 void *parent, void *child);
304 static ngx_int_t ngx_http_upload_add_variables(ngx_conf_t *cf);
305 static ngx_int_t ngx_http_upload_variable(ngx_http_request_t *r,
306 ngx_http_variable_value_t *v, uintptr_t data);
307 static ngx_int_t ngx_http_upload_md5_variable(ngx_http_request_t *r,
308 ngx_http_variable_value_t *v, uintptr_t data);
309 static ngx_int_t ngx_http_upload_sha1_variable(ngx_http_request_t *r,
310 ngx_http_variable_value_t *v, uintptr_t data);
311 static ngx_int_t ngx_http_upload_sha256_variable(ngx_http_request_t *r,
312 ngx_http_variable_value_t *v, uintptr_t data);
313 static ngx_int_t ngx_http_upload_sha512_variable(ngx_http_request_t *r,
314 ngx_http_variable_value_t *v, uintptr_t data);
315 static ngx_int_t ngx_http_upload_file_size_variable(ngx_http_request_t *r,
316 ngx_http_variable_value_t *v, uintptr_t data);
317 static void ngx_http_upload_content_range_variable_set(ngx_http_request_t *r,
318 ngx_http_variable_value_t *v, uintptr_t data);
319 static ngx_int_t ngx_http_upload_content_range_variable(ngx_http_request_t *r,
320 ngx_http_variable_value_t *v, uintptr_t data);
321 static ngx_int_t ngx_http_upload_crc32_variable(ngx_http_request_t *r,
322 ngx_http_variable_value_t *v, uintptr_t data);
323 static ngx_int_t ngx_http_upload_uint_variable(ngx_http_request_t *r,
324 ngx_http_variable_value_t *v, uintptr_t data);
325 static char *ngx_http_upload_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
326
327 static ngx_int_t
328 ngx_http_upload_process_field_templates(ngx_http_request_t *r,
329 ngx_http_upload_field_template_t *t, ngx_str_t *field_name, ngx_str_t *field_value);
330
331 static ngx_int_t ngx_http_upload_start_handler(ngx_http_upload_ctx_t *u);
332 static void ngx_http_upload_finish_handler(ngx_http_upload_ctx_t *u);
333 static void ngx_http_upload_abort_handler(ngx_http_upload_ctx_t *u);
334
335 static ngx_int_t ngx_http_upload_flush_output_buffer(ngx_http_upload_ctx_t *u,
336 u_char *buf, size_t len);
337 static ngx_int_t ngx_http_upload_append_field(ngx_http_upload_ctx_t *u,
338 ngx_str_t *name, ngx_str_t *value);
339 static ngx_int_t ngx_http_upload_merge_ranges(ngx_http_upload_ctx_t *u, ngx_http_upload_range_t *range_n);
340 static ngx_int_t ngx_http_upload_parse_range(ngx_str_t *range, ngx_http_upload_range_t *range_n);
341
342 static void ngx_http_read_upload_client_request_body_handler(ngx_http_request_t *r);
343 static ngx_int_t ngx_http_do_read_upload_client_request_body(ngx_http_request_t *r);
344 static ngx_int_t ngx_http_process_request_body(ngx_http_request_t *r, ngx_chain_t *body);
345
346 static ngx_int_t ngx_http_read_upload_client_request_body(ngx_http_request_t *r);
347
348 static char *ngx_http_upload_set_form_field(ngx_conf_t *cf, ngx_command_t *cmd,
349 void *conf);
350 static ngx_int_t ngx_http_upload_eval_path(ngx_http_request_t *r);
351 static ngx_int_t ngx_http_upload_eval_state_path(ngx_http_request_t *r);
352 static char *ngx_http_upload_pass_form_field(ngx_conf_t *cf, ngx_command_t *cmd,
353 void *conf);
354 static char *ngx_http_upload_set_path_slot(ngx_conf_t *cf, ngx_command_t *cmd,
355 void *conf);
356 static char *ngx_http_upload_merge_path_value(ngx_conf_t *cf, ngx_http_upload_path_t **path, ngx_http_upload_path_t *prev,
357 ngx_path_init_t *init);
358 static char *ngx_http_upload_cleanup(ngx_conf_t *cf, ngx_command_t *cmd,
359 void *conf);
360 static void ngx_upload_cleanup_handler(void *data);
361
362 #if defined nginx_version && nginx_version >= 7052
363 static ngx_path_init_t ngx_http_upload_temp_path = {
364 ngx_string(NGX_HTTP_PROXY_TEMP_PATH), { 1, 2, 0 }
365 };
366 #endif
367
368 /*
369 * upload_init_ctx
370 *
371 * Initialize upload context. Memory for upload context which is being passed
372 * as upload_ctx parameter could be allocated anywhere and should not be freed
373 * prior to upload_shutdown_ctx call.
374 *
375 * IMPORTANT:
376 *
377 * After initialization the following routine SHOULD BE called:
378 *
379 * upload_parse_content_type -- to assign part boundary
380 *
381 * Parameter:
382 * upload_ctx -- upload context which is being initialized
383 *
384 */
385 static void upload_init_ctx(ngx_http_upload_ctx_t *upload_ctx);
386
387 /*
388 * upload_shutdown_ctx
389 *
390 * Shutdown upload context. Discard all remaining data and
391 * free all memory associated with upload context.
392 *
393 * Parameter:
394 * upload_ctx -- upload context which is being shut down
395 *
396 */
397 static void upload_shutdown_ctx(ngx_http_upload_ctx_t *upload_ctx);
398
399 /*
400 * upload_start
401 *
402 * Starts multipart stream processing. Initializes internal buffers
403 * and pointers
404 *
405 * Parameter:
406 * upload_ctx -- upload context which is being initialized
407 *
408 * Return value:
409 * NGX_OK on success
410 * NGX_ERROR if error has occured
411 *
412 */
413 static ngx_int_t upload_start(ngx_http_upload_ctx_t *upload_ctx, ngx_http_upload_loc_conf_t *ulcf);
414
415 /*
416 * upload_parse_request_headers
417 *
418 * Parse and verify HTTP headers, extract boundary or
419 * content disposition
420 *
421 * Parameters:
422 * upload_ctx -- upload context to populate
423 * headers_in -- request headers
424 *
425 * Return value:
426 * NGX_OK on success
427 * NGX_ERROR if error has occured
428 */
429 static ngx_int_t upload_parse_request_headers(ngx_http_upload_ctx_t *upload_ctx, ngx_http_headers_in_t *headers_in);
430
431 /*
432 * upload_process_buf
433 *
434 * Process buffer with multipart stream starting from start and terminating
435 * by end, operating on upload_ctx. The header information is accumulated in
436 * This call can invoke one or more calls to start_upload_file, finish_upload_file,
437 * abort_upload_file and flush_output_buffer routines.
438 *
439 * Returns value NGX_OK successful
440 * NGX_UPLOAD_MALFORMED stream is malformed
441 * NGX_UPLOAD_NOMEM insufficient memory
442 * NGX_UPLOAD_IOERROR input-output error
443 * NGX_UPLOAD_SCRIPTERROR nginx script engine failed
444 * NGX_UPLOAD_TOOLARGE field body is too large
445 */
446 static ngx_int_t upload_process_buf(ngx_http_upload_ctx_t *upload_ctx, u_char *start, u_char *end);
447 static ngx_int_t upload_process_raw_buf(ngx_http_upload_ctx_t *upload_ctx, u_char *start, u_char *end);
448
449 static ngx_command_t ngx_http_upload_commands[] = { /* {{{ */
450
451 /*
452 * Enables uploads for location and specifies location to pass modified request to
453 */
454 { ngx_string("upload_pass"),
455 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_HTTP_LIF_CONF
456 |NGX_CONF_TAKE1,
457 ngx_http_upload_pass,
458 NGX_HTTP_LOC_CONF_OFFSET,
459 0,
460 NULL },
461
462 /*
463 * Specifies base path of file store
464 */
465 { ngx_string("upload_store"),
466 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_HTTP_LIF_CONF
467 |NGX_CONF_TAKE1234,
468 ngx_http_upload_set_path_slot,
469 NGX_HTTP_LOC_CONF_OFFSET,
470 offsetof(ngx_http_upload_loc_conf_t, store_path),
471 NULL },
472
473 /*
474 * Specifies base path of state store
475 */
476 { ngx_string("upload_state_store"),
477 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,
478 ngx_http_upload_set_path_slot,
479 NGX_HTTP_LOC_CONF_OFFSET,
480 offsetof(ngx_http_upload_loc_conf_t, state_store_path),
481 NULL },
482
483 /*
484 * Specifies the access mode for files in store
485 */
486 { ngx_string("upload_store_access"),
487 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_HTTP_LIF_CONF
488 |NGX_CONF_TAKE123,
489 ngx_conf_set_access_slot,
490 NGX_HTTP_LOC_CONF_OFFSET,
491 offsetof(ngx_http_upload_loc_conf_t, store_access),
492 NULL },
493
494 /*
495 * Specifies the size of buffer, which will be used
496 * to write data to disk
497 */
498 { ngx_string("upload_buffer_size"),
499 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_HTTP_LIF_CONF
500 |NGX_CONF_TAKE1,
501 ngx_conf_set_size_slot,
502 NGX_HTTP_LOC_CONF_OFFSET,
503 offsetof(ngx_http_upload_loc_conf_t, buffer_size),
504 NULL },
505
506 /*
507 * Specifies the size of buffer, which will be used
508 * for merging ranges into state file
509 */
510 { ngx_string("upload_merge_buffer_size"),
511 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
512 ngx_conf_set_size_slot,
513 NGX_HTTP_LOC_CONF_OFFSET,
514 offsetof(ngx_http_upload_loc_conf_t, merge_buffer_size),
515 NULL },
516
517 /*
518 * Specifies the size of buffer, which will be used
519 * for returning range header
520 */
521 { ngx_string("upload_range_header_buffer_size"),
522 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
523 ngx_conf_set_size_slot,
524 NGX_HTTP_LOC_CONF_OFFSET,
525 offsetof(ngx_http_upload_loc_conf_t, range_header_buffer_size),
526 NULL },
527
528 /*
529 * Specifies the maximal length of the part header
530 */
531 { ngx_string("upload_max_part_header_len"),
532 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_HTTP_LIF_CONF
533 |NGX_CONF_TAKE1,
534 ngx_conf_set_size_slot,
535 NGX_HTTP_LOC_CONF_OFFSET,
536 offsetof(ngx_http_upload_loc_conf_t, max_header_len),
537 NULL },
538
539 /*
540 * Specifies the maximal size of the file to be uploaded
541 */
542 { ngx_string("upload_max_file_size"),
543 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_HTTP_LIF_CONF
544 |NGX_CONF_TAKE1,
545 ngx_conf_set_off_slot,
546 NGX_HTTP_LOC_CONF_OFFSET,
547 offsetof(ngx_http_upload_loc_conf_t, max_file_size),
548 NULL },
549
550 /*
551 * Specifies the maximal length of output body
552 */
553 { ngx_string("upload_max_output_body_len"),
554 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_HTTP_LIF_CONF
555 |NGX_CONF_TAKE1,
556 ngx_conf_set_size_slot,
557 NGX_HTTP_LOC_CONF_OFFSET,
558 offsetof(ngx_http_upload_loc_conf_t, max_output_body_len),
559 NULL },
560
561 /*
562 * Specifies the field to set in altered response body
563 */
564 { ngx_string("upload_set_form_field"),
565 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_HTTP_LIF_CONF
566 |NGX_CONF_TAKE2,
567 ngx_http_upload_set_form_field,
568 NGX_HTTP_LOC_CONF_OFFSET,
569 offsetof(ngx_http_upload_loc_conf_t, field_templates),
570 NULL},
571
572 /*
573 * Specifies the field with aggregate parameters
574 * to set in altered response body
575 */
576 { ngx_string("upload_aggregate_form_field"),
577 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_HTTP_LIF_CONF
578 |NGX_CONF_TAKE2,
579 ngx_http_upload_set_form_field,
580 NGX_HTTP_LOC_CONF_OFFSET,
581 offsetof(ngx_http_upload_loc_conf_t, aggregate_field_templates),
582 NULL},
583
584 /*
585 * Specifies the field to pass to backend
586 */
587 { ngx_string("upload_pass_form_field"),
588 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_HTTP_LIF_CONF
589 |NGX_CONF_TAKE1,
590 ngx_http_upload_pass_form_field,
591 NGX_HTTP_LOC_CONF_OFFSET,
592 0,
593 NULL},
594
595 /*
596 * Specifies http statuses upon reception of
597 * which cleanup of uploaded files will be initiated
598 */
599 { ngx_string("upload_cleanup"),
600 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_HTTP_LIF_CONF
601 |NGX_CONF_1MORE,
602 ngx_http_upload_cleanup,
603 NGX_HTTP_LOC_CONF_OFFSET,
604 0,
605 NULL},
606
607 /*
608 * Specifies the whether or not to forward query args
609 * to the upload_pass redirect location
610 */
611 { ngx_string("upload_pass_args"),
612 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_HTTP_LIF_CONF
613 |NGX_CONF_FLAG,
614 ngx_conf_set_flag_slot,
615 NGX_HTTP_LOC_CONF_OFFSET,
616 offsetof(ngx_http_upload_loc_conf_t, forward_args),
617 NULL },
618
619 /*
620 * Specifies request body reception rate limit
621 */
622 { ngx_string("upload_limit_rate"),
623 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
624 |NGX_CONF_TAKE1,
625 ngx_conf_set_size_slot,
626 NGX_HTTP_LOC_CONF_OFFSET,
627 offsetof(ngx_http_upload_loc_conf_t, limit_rate),
628 NULL },
629
630 /*
631 * Specifies whether array brackets in file field names must be dropped
632 */
633 { ngx_string("upload_tame_arrays"),
634 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_HTTP_LIF_CONF
635 |NGX_CONF_FLAG,
636 ngx_conf_set_flag_slot,
637 NGX_HTTP_LOC_CONF_OFFSET,
638 offsetof(ngx_http_upload_loc_conf_t, tame_arrays),
639 NULL },
640
641 /*
642 * Specifies whether resumable uploads are allowed
643 */
644 { ngx_string("upload_resumable"),
645 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_HTTP_LIF_CONF
646 |NGX_CONF_FLAG,
647 ngx_conf_set_flag_slot,
648 NGX_HTTP_LOC_CONF_OFFSET,
649 offsetof(ngx_http_upload_loc_conf_t, resumable_uploads),
650 NULL },
651
652 /*
653 * Specifies whether empty field names are allowed
654 */
655 { ngx_string("upload_empty_fiels_names"),
656 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_HTTP_LIF_CONF
657 |NGX_CONF_FLAG,
658 ngx_conf_set_flag_slot,
659 NGX_HTTP_LOC_CONF_OFFSET,
660 offsetof(ngx_http_upload_loc_conf_t, empty_field_names),
661 NULL },
662
663 /*
664 * Specifies the name and content of the header that will be added to the response
665 */
666 { ngx_string("upload_add_header"),
667 NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_HTTP_LIF_CONF
668 |NGX_CONF_TAKE2,
669 ngx_http_upload_set_form_field,
670 NGX_HTTP_LOC_CONF_OFFSET,
671 offsetof(ngx_http_upload_loc_conf_t, header_templates),
672 NULL},
673
674 ngx_null_command
675 }; /* }}} */
676
677 ngx_http_module_t ngx_http_upload_module_ctx = { /* {{{ */
678 ngx_http_upload_add_variables, /* preconfiguration */
679 NULL, /* postconfiguration */
680
681 NULL, /* create main configuration */
682 NULL, /* init main configuration */
683
684 NULL, /* create server configuration */
685 NULL, /* merge server configuration */
686
687 ngx_http_upload_create_loc_conf, /* create location configuration */
688 ngx_http_upload_merge_loc_conf /* merge location configuration */
689 }; /* }}} */
690
691 ngx_module_t ngx_http_upload_module = { /* {{{ */
692 NGX_MODULE_V1,
693 &ngx_http_upload_module_ctx, /* module context */
694 ngx_http_upload_commands, /* module directives */
695 NGX_HTTP_MODULE, /* module type */
696 NULL, /* init master */
697 NULL, /* init module */
698 NULL, /* init process */
699 NULL, /* init thread */
700 NULL, /* exit thread */
701 NULL, /* exit process */
702 NULL, /* exit master */
703 NGX_MODULE_V1_PADDING
704 }; /* }}} */
705
706 static ngx_http_variable_t ngx_http_upload_variables[] = { /* {{{ */
707
708 { ngx_string("upload_field_name"), NULL, ngx_http_upload_variable,
709 (uintptr_t) offsetof(ngx_http_upload_ctx_t, field_name),
710 NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
711
712 { ngx_string("upload_content_type"),
713 NULL,
714 ngx_http_upload_variable,
715 (uintptr_t) offsetof(ngx_http_upload_ctx_t, content_type),
716 NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
717
718 { ngx_string("upload_file_name"), NULL, ngx_http_upload_variable,
719 (uintptr_t) offsetof(ngx_http_upload_ctx_t, file_name),
720 NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
721
722 { ngx_string("upload_file_number"), NULL, ngx_http_upload_uint_variable,
723 (uintptr_t) offsetof(ngx_http_upload_ctx_t, ordinal),
724 NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
725
726 { ngx_string("upload_tmp_path"), NULL, ngx_http_upload_variable,
727 (uintptr_t) offsetof(ngx_http_upload_ctx_t, output_file.name),
728 NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
729
730 { ngx_string("upload_content_range"),
731 ngx_http_upload_content_range_variable_set,
732 ngx_http_upload_content_range_variable,
733 (uintptr_t) offsetof(ngx_http_upload_ctx_t, content_range_n),
734 NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
735
736 { ngx_null_string, NULL, NULL, 0, 0, 0 }
737 }; /* }}} */
738
739 static ngx_http_variable_t ngx_http_upload_aggregate_variables[] = { /* {{{ */
740
741 { ngx_string("upload_file_md5"), NULL, ngx_http_upload_md5_variable,
742 (uintptr_t) "0123456789abcdef",
743 NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
744
745 { ngx_string("upload_file_md5_uc"), NULL, ngx_http_upload_md5_variable,
746 (uintptr_t) "0123456789ABCDEF",
747 NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
748
749 { ngx_string("upload_file_sha1"), NULL, ngx_http_upload_sha1_variable,
750 (uintptr_t) "0123456789abcdef",
751 NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
752
753 { ngx_string("upload_file_sha1_uc"), NULL, ngx_http_upload_sha1_variable,
754 (uintptr_t) "0123456789ABCDEF",
755 NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
756
757 { ngx_string("upload_file_sha256"), NULL, ngx_http_upload_sha256_variable,
758 (uintptr_t) "0123456789abcdef",
759 NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
760
761 { ngx_string("upload_file_sha256_uc"), NULL, ngx_http_upload_sha256_variable,
762 (uintptr_t) "0123456789ABCDEF",
763 NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
764
765 { ngx_string("upload_file_sha512"), NULL, ngx_http_upload_sha512_variable,
766 (uintptr_t) "0123456789abcdef",
767 NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
768
769 { ngx_string("upload_file_sha512_uc"), NULL, ngx_http_upload_sha512_variable,
770 (uintptr_t) "0123456789ABCDEF",
771 NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
772
773 { ngx_string("upload_file_crc32"), NULL, ngx_http_upload_crc32_variable,
774 (uintptr_t) offsetof(ngx_http_upload_ctx_t, crc32),
775 NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
776
777 { ngx_string("upload_file_size"), NULL, ngx_http_upload_file_size_variable,
778 (uintptr_t) offsetof(ngx_http_upload_ctx_t, output_file.offset),
779 NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
780
781 { ngx_null_string, NULL, NULL, 0, 0, 0 }
782 }; /* }}} */
783
784 static ngx_str_t ngx_http_upload_empty_field_value = ngx_null_string;
785
786 static ngx_str_t ngx_upload_field_part1 = { /* {{{ */
787 sizeof(CRLF CONTENT_DISPOSITION_STRING " form-data; name=\"") - 1,
788 (u_char*)CRLF CONTENT_DISPOSITION_STRING " form-data; name=\""
789 }; /* }}} */
790
791 static ngx_str_t ngx_upload_field_part2 = { /* {{{ */
792 sizeof("\"" CRLF CRLF) - 1,
793 (u_char*)"\"" CRLF CRLF
794 }; /* }}} */
795
796 static ngx_int_t /* {{{ ngx_http_upload_handler */
ngx_http_upload_handler(ngx_http_request_t * r)797 ngx_http_upload_handler(ngx_http_request_t *r)
798 {
799 ngx_http_upload_loc_conf_t *ulcf;
800 ngx_http_upload_ctx_t *u;
801 ngx_int_t rc;
802
803 if(r->method & NGX_HTTP_OPTIONS)
804 return ngx_http_upload_options_handler(r);
805
806 if (!(r->method & NGX_HTTP_POST))
807 return NGX_HTTP_NOT_ALLOWED;
808
809 ulcf = ngx_http_get_module_loc_conf(r, ngx_http_upload_module);
810
811 u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
812
813 if (u == NULL) {
814 u = ngx_pcalloc(r->pool, sizeof(ngx_http_upload_ctx_t));
815 if (u == NULL) {
816 return NGX_HTTP_INTERNAL_SERVER_ERROR;
817 }
818
819 ngx_http_set_ctx(r, u, ngx_http_upload_module);
820 }
821
822 if(ulcf->md5) {
823 if(u->md5_ctx == NULL) {
824 u->md5_ctx = ngx_palloc(r->pool, sizeof(ngx_http_upload_md5_ctx_t));
825 if (u->md5_ctx == NULL) {
826 return NGX_HTTP_INTERNAL_SERVER_ERROR;
827 }
828 }
829 }else
830 u->md5_ctx = NULL;
831
832 if(ulcf->sha1) {
833 if(u->sha1_ctx == NULL) {
834 u->sha1_ctx = ngx_palloc(r->pool, sizeof(ngx_http_upload_sha1_ctx_t));
835 if (u->sha1_ctx == NULL) {
836 return NGX_HTTP_INTERNAL_SERVER_ERROR;
837 }
838 }
839 }else
840 u->sha1_ctx = NULL;
841
842 if(ulcf->sha256) {
843 if(u->sha256_ctx == NULL) {
844 u->sha256_ctx = ngx_palloc(r->pool, sizeof(ngx_http_upload_sha256_ctx_t));
845 if (u->sha256_ctx == NULL) {
846 return NGX_HTTP_INTERNAL_SERVER_ERROR;
847 }
848 }
849 }else
850 u->sha256_ctx = NULL;
851
852 if(ulcf->sha512) {
853 if(u->sha512_ctx == NULL) {
854 u->sha512_ctx = ngx_palloc(r->pool, sizeof(ngx_http_upload_sha512_ctx_t));
855 if (u->sha512_ctx == NULL) {
856 return NGX_HTTP_INTERNAL_SERVER_ERROR;
857 }
858 }
859 }else
860 u->sha512_ctx = NULL;
861
862 u->calculate_crc32 = ulcf->crc32;
863
864 u->request = r;
865 u->log = r->connection->log;
866 u->chain = u->last = u->checkpoint = NULL;
867 u->output_body_len = 0;
868
869 u->prevent_output = 0;
870 u->no_content = 1;
871 u->limit_rate = ulcf->limit_rate;
872 u->received = 0;
873 u->ordinal = 0;
874
875 upload_init_ctx(u);
876
877 rc = upload_parse_request_headers(u, &r->headers_in);
878
879 if(rc != NGX_OK) {
880 upload_shutdown_ctx(u);
881 return rc;
882 }
883
884 rc = ngx_http_upload_eval_path(r);
885
886 if(rc != NGX_OK) {
887 upload_shutdown_ctx(u);
888 return rc;
889 }
890
891 rc = ngx_http_upload_eval_state_path(r);
892
893 if(rc != NGX_OK) {
894 upload_shutdown_ctx(u);
895 return rc;
896 }
897
898 if (ngx_http_upload_test_expect(r) != NGX_OK) {
899 upload_shutdown_ctx(u);
900 return NGX_HTTP_INTERNAL_SERVER_ERROR;
901 }
902
903 if(upload_start(u, ulcf) != NGX_OK)
904 return NGX_HTTP_INTERNAL_SERVER_ERROR;
905
906 #if (NGX_HTTP_V2)
907 if (r->stream) {
908 r->request_body_no_buffering = 1;
909
910 rc = ngx_http_read_client_request_body(r, ngx_http_upload_read_event_handler);
911
912 if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
913 upload_shutdown_ctx(u);
914 return rc;
915 }
916
917 return NGX_DONE;
918 }
919 #endif
920
921 rc = ngx_http_read_upload_client_request_body(r);
922
923 if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
924 return rc;
925 }
926
927 return NGX_DONE;
928 } /* }}} */
929
930 #if (NGX_HTTP_V2)
931 static void
ngx_http_upload_read_event_handler(ngx_http_request_t * r)932 ngx_http_upload_read_event_handler(ngx_http_request_t *r)
933 {
934 ngx_http_upload_ctx_t *u;
935 ngx_http_request_body_t *rb;
936 ngx_int_t rc;
937 ngx_chain_t *in;
938 ssize_t n, limit, buf_read_size, next_buf_size, remaining;
939 ngx_msec_t delay;
940 ngx_event_t *rev;
941
942 if (ngx_exiting || ngx_terminate) {
943 ngx_http_finalize_request(r, NGX_HTTP_CLOSE);
944 return;
945 }
946
947 rev = r->connection->read;
948 rb = r->request_body;
949
950 if (rb == NULL) {
951 ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
952 return;
953 }
954
955 r->read_event_handler = ngx_http_upload_read_event_handler;
956
957 u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
958
959 for ( ;; ) {
960 buf_read_size = 0;
961
962 for (in = rb->bufs ; in; in = in->next) {
963 n = in->buf->last - in->buf->pos;
964
965 rc = u->data_handler(u, in->buf->pos, in->buf->pos + n);
966
967 in->buf->pos += n;
968 u->received += n;
969 buf_read_size += n;
970
971 if (rc != NGX_OK) {
972 goto err;
973 }
974 }
975 rb->bufs = NULL;
976
977 // We're done reading the request body, break out of loop
978 if (!r->reading_body) {
979 rc = u->data_handler(u, NULL, NULL);
980 if (rc == NGX_OK) {
981 break;
982 } else {
983 goto err;
984 }
985 }
986
987 // Check whether we have exceeded limit_rate and should delay the next
988 // buffer read
989 if (u->limit_rate) {
990 remaining = ((ssize_t) r->headers_in.content_length_n) - u->received;
991 next_buf_size = (buf_read_size > remaining) ? remaining : buf_read_size;
992 limit = u->limit_rate * (ngx_time() - r->start_sec + 1) - (u->received + next_buf_size);
993 if (limit < 0) {
994 rev->delayed = 1;
995 ngx_add_timer(rev, (ngx_msec_t) ((limit * -1000 / u->limit_rate) + 1));
996 return;
997 }
998 }
999
1000 rc = ngx_http_read_unbuffered_request_body(r);
1001
1002 if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
1003 goto err;
1004 }
1005
1006 if (rb->bufs == NULL) {
1007 return;
1008 }
1009
1010 // Check whether we should delay processing the latest request body
1011 // buffers to stay within limit_rate
1012 if (u->limit_rate) {
1013 buf_read_size = 0;
1014 for (in = rb->bufs ; in; in = in->next) {
1015 buf_read_size += (in->buf->last - in->buf->pos);
1016 }
1017 delay = (ngx_msec_t) (buf_read_size * 1000 / u->limit_rate + 1);
1018 if (delay > 0) {
1019 rev->delayed = 1;
1020 ngx_add_timer(rev, delay);
1021 return;
1022 }
1023 }
1024 }
1025
1026 // Finally, send the response
1027 rc = ngx_http_upload_body_handler(r);
1028
1029 err:
1030 switch(rc) {
1031 case NGX_UPLOAD_MALFORMED:
1032 rc = NGX_HTTP_BAD_REQUEST;
1033 break;
1034 case NGX_UPLOAD_TOOLARGE:
1035 rc = NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;
1036 break;
1037 case NGX_UPLOAD_IOERROR:
1038 rc = NGX_HTTP_SERVICE_UNAVAILABLE;
1039 break;
1040 case NGX_UPLOAD_NOMEM:
1041 case NGX_UPLOAD_SCRIPTERROR:
1042 rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
1043 break;
1044 }
1045 if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
1046 upload_shutdown_ctx(u);
1047 ngx_http_finalize_request(r, rc);
1048 }
1049 }
1050 #endif
1051
ngx_http_upload_add_headers(ngx_http_request_t * r,ngx_http_upload_loc_conf_t * ulcf)1052 static ngx_int_t ngx_http_upload_add_headers(ngx_http_request_t *r, ngx_http_upload_loc_conf_t *ulcf) { /* {{{ */
1053 ngx_str_t name;
1054 ngx_str_t value;
1055 ngx_http_upload_field_template_t *t;
1056 ngx_table_elt_t *h;
1057 ngx_uint_t i;
1058
1059 if (ulcf->header_templates != NULL) {
1060 t = ulcf->header_templates->elts;
1061 for (i = 0; i < ulcf->header_templates->nelts; i++) {
1062 if (ngx_http_upload_process_field_templates(r, &t[i], &name, &value) != NGX_OK) {
1063 return NGX_ERROR;
1064 }
1065
1066 if(name.len != 0 && value.len != 0) {
1067 h = ngx_list_push(&r->headers_out.headers);
1068 if(h == NULL) {
1069 return NGX_ERROR;
1070 }
1071
1072 h->hash = 1;
1073 h->key.len = name.len;
1074 h->key.data = name.data;
1075 h->value.len = value.len;
1076 h->value.data = value.data;
1077 }
1078 }
1079 }
1080
1081 return NGX_OK;
1082 } /* }}} */
1083
1084 static ngx_int_t /* {{{ */
ngx_http_upload_eval_path(ngx_http_request_t * r)1085 ngx_http_upload_eval_path(ngx_http_request_t *r) {
1086 ngx_http_upload_ctx_t *u;
1087 ngx_http_upload_loc_conf_t *ulcf;
1088 ngx_str_t value;
1089
1090 ulcf = ngx_http_get_module_loc_conf(r, ngx_http_upload_module);
1091 u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
1092
1093 if(ulcf->store_path->is_dynamic) {
1094 u->store_path = ngx_pcalloc(r->pool, sizeof(ngx_path_t));
1095 if(u->store_path == NULL) {
1096 return NGX_ERROR;
1097 }
1098
1099 ngx_memcpy(u->store_path, ulcf->store_path->path, sizeof(ngx_path_t));
1100
1101 if(ngx_http_complex_value(r, &ulcf->store_path->dynamic, &value) != NGX_OK) {
1102 return NGX_ERROR;
1103 }
1104
1105 u->store_path->name.data = value.data;
1106 u->store_path->name.len = value.len;
1107 }
1108 else{
1109 u->store_path = ulcf->store_path->path;
1110 }
1111
1112 return NGX_OK;
1113 } /* }}} */
1114
1115 static ngx_int_t /* {{{ */
ngx_http_upload_eval_state_path(ngx_http_request_t * r)1116 ngx_http_upload_eval_state_path(ngx_http_request_t *r) {
1117 ngx_http_upload_ctx_t *u;
1118 ngx_http_upload_loc_conf_t *ulcf;
1119 ngx_str_t value;
1120
1121 ulcf = ngx_http_get_module_loc_conf(r, ngx_http_upload_module);
1122 u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
1123
1124 if(ulcf->state_store_path->is_dynamic) {
1125 u->state_store_path = ngx_pcalloc(r->pool, sizeof(ngx_path_t));
1126 if(u->store_path == NULL) {
1127 return NGX_ERROR;
1128 }
1129
1130 ngx_memcpy(u->state_store_path, ulcf->state_store_path->path, sizeof(ngx_path_t));
1131
1132 if(ngx_http_complex_value(r, &ulcf->state_store_path->dynamic, &value) != NGX_OK) {
1133 return NGX_ERROR;
1134 }
1135
1136 u->state_store_path->name.data = value.data;
1137 u->state_store_path->name.len = value.len;
1138 }
1139 else{
1140 u->state_store_path = ulcf->state_store_path->path;
1141 }
1142
1143 return NGX_OK;
1144 } /* }}} */
1145
ngx_http_upload_options_handler(ngx_http_request_t * r)1146 static ngx_int_t ngx_http_upload_options_handler(ngx_http_request_t *r) { /* {{{ */
1147 ngx_http_upload_loc_conf_t *ulcf;
1148
1149 ulcf = ngx_http_get_module_loc_conf(r, ngx_http_upload_module);
1150
1151 r->headers_out.status = NGX_HTTP_OK;
1152
1153 if(ngx_http_upload_add_headers(r, ulcf) != NGX_OK) {
1154 return NGX_HTTP_INTERNAL_SERVER_ERROR;
1155 }
1156
1157 r->header_only = 1;
1158 r->headers_out.content_length_n = 0;
1159 r->allow_ranges = 0;
1160
1161 return ngx_http_send_header(r);
1162 } /* }}} */
1163
ngx_http_upload_body_handler(ngx_http_request_t * r)1164 static ngx_int_t ngx_http_upload_body_handler(ngx_http_request_t *r) { /* {{{ */
1165 ngx_http_upload_loc_conf_t *ulcf = ngx_http_get_module_loc_conf(r, ngx_http_upload_module);
1166 ngx_http_upload_ctx_t *ctx = ngx_http_get_module_ctx(r, ngx_http_upload_module);
1167
1168 ngx_str_t args;
1169 ngx_uint_t flags;
1170 ngx_int_t rc;
1171 ngx_str_t uri;
1172 ngx_buf_t *b;
1173 ngx_chain_t *cl, out;
1174 ngx_str_t dummy = ngx_string("<ngx_upload_module_dummy>");
1175 ngx_table_elt_t *h;
1176
1177 if(ctx->prevent_output) {
1178 r->headers_out.status = NGX_HTTP_CREATED;
1179
1180 /*
1181 * Add range header and body
1182 */
1183 if(ctx->range_header_buffer_pos != ctx->range_header_buffer) {
1184 h = ngx_list_push(&r->headers_out.headers);
1185 if (h == NULL) {
1186 return NGX_HTTP_INTERNAL_SERVER_ERROR;
1187 }
1188
1189 h->hash = 1;
1190 h->key.len = sizeof("Range") - 1;
1191 h->key.data = (u_char *) "Range";
1192 h->value.len = ctx->range_header_buffer_pos - ctx->range_header_buffer;
1193 h->value.data = ctx->range_header_buffer;
1194
1195 b = ngx_pcalloc(r->pool, sizeof(ngx_buf_t));
1196 if (b == NULL) {
1197 return NGX_HTTP_INTERNAL_SERVER_ERROR;
1198 }
1199
1200 r->headers_out.content_length_n = h->value.len;
1201
1202 r->allow_ranges = 0;
1203
1204 rc = ngx_http_send_header(r);
1205
1206 if(rc == NGX_ERROR) {
1207 return NGX_HTTP_INTERNAL_SERVER_ERROR;
1208 }
1209
1210 if(rc > NGX_OK) {
1211 return rc;
1212 }
1213
1214 b->in_file = 0;
1215 b->memory = 1;
1216 b->last_buf = b->last_in_chain = b->flush = 1;
1217
1218 b->start = b->pos = ctx->range_header_buffer;
1219 b->last = ctx->range_header_buffer_pos;
1220 b->end = ctx->range_header_buffer_end;
1221
1222 out.buf = b;
1223 out.next = NULL;
1224
1225 ngx_http_finalize_request(r, ngx_http_output_filter(r, &out));
1226 }
1227 else {
1228 r->header_only = 1;
1229 r->headers_out.content_length_n = 0;
1230
1231 ngx_http_finalize_request(r, ngx_http_send_header(r));
1232 }
1233
1234 return NGX_OK;
1235 }
1236
1237 if(ulcf->max_output_body_len != 0) {
1238 if(ctx->output_body_len + ctx->boundary.len + 4 > ulcf->max_output_body_len)
1239 return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;
1240 }
1241
1242 if(ctx->no_content) {
1243 rc = ngx_http_upload_append_field(ctx, &dummy, &ngx_http_upload_empty_field_value);
1244
1245 if(rc != NGX_OK) {
1246 return NGX_HTTP_INTERNAL_SERVER_ERROR;
1247 }
1248 }
1249
1250 /*
1251 * Append final boundary
1252 */
1253 b = ngx_create_temp_buf(r->pool, ctx->boundary.len + 4);
1254
1255 if (b == NULL) {
1256 return NGX_HTTP_INTERNAL_SERVER_ERROR;
1257 }
1258
1259 cl = ngx_alloc_chain_link(r->pool);
1260 if (cl == NULL) {
1261 return NGX_HTTP_INTERNAL_SERVER_ERROR;
1262 }
1263
1264 b->last_in_chain = 1;
1265 b->last_buf = 1;
1266
1267 cl->buf = b;
1268 cl->next = NULL;
1269
1270 if(ctx->chain == NULL) {
1271 ctx->chain = cl;
1272 ctx->last = cl;
1273 }else{
1274 ctx->last->next = cl;
1275 ctx->last = cl;
1276 }
1277
1278 b->last = ngx_cpymem(b->last, ctx->boundary.data, ctx->boundary.len);
1279
1280 *b->last++ = '-';
1281 *b->last++ = '-';
1282 *b->last++ = CR;
1283 *b->last++ = LF;
1284
1285 if (ulcf->url_cv) {
1286 /* complex value */
1287 if (ngx_http_complex_value(r, ulcf->url_cv, &uri) != NGX_OK) {
1288 return NGX_HTTP_INTERNAL_SERVER_ERROR;
1289 }
1290
1291 if (uri.len == 0) {
1292 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
1293 "empty \"upload_pass\" (was: \"%V\")",
1294 &ulcf->url_cv->value);
1295
1296 return NGX_HTTP_INTERNAL_SERVER_ERROR;
1297 }
1298 } else {
1299 /* simple value */
1300 uri = ulcf->url;
1301 }
1302
1303 if (ulcf->forward_args) {
1304 args = r->args; /* forward the query args */
1305 }
1306 else {
1307 args.len = 0;
1308 args.data = NULL;
1309 }
1310
1311 flags = 0;
1312
1313 if (ngx_http_parse_unsafe_uri(r, &uri, &args, &flags) != NGX_OK) {
1314 return NGX_HTTP_INTERNAL_SERVER_ERROR;
1315 }
1316
1317 r->request_body->bufs = ctx->chain;
1318
1319 // Recalculate content length
1320 r->headers_in.content_length_n = 0;
1321
1322 for(cl = ctx->chain ; cl ; cl = cl->next)
1323 r->headers_in.content_length_n += (cl->buf->last - cl->buf->pos);
1324
1325 r->headers_in.content_length->value.data = ngx_palloc(r->pool, NGX_OFF_T_LEN);
1326
1327 if (r->headers_in.content_length->value.data == NULL) {
1328 return NGX_HTTP_INTERNAL_SERVER_ERROR;
1329 }
1330
1331 r->headers_in.content_length->value.len =
1332 ngx_sprintf(r->headers_in.content_length->value.data, "%O", r->headers_in.content_length_n)
1333 - r->headers_in.content_length->value.data;
1334
1335 #if defined nginx_version && nginx_version >= 8011
1336 r->main->count--;
1337 #endif
1338
1339 if(uri.len != 0 && uri.data[0] == '/') {
1340 rc = ngx_http_internal_redirect(r, &uri, &args);
1341 }
1342 else{
1343 rc = ngx_http_named_location(r, &uri);
1344 }
1345
1346 if (rc == NGX_ERROR) {
1347 return NGX_HTTP_INTERNAL_SERVER_ERROR;
1348 }
1349
1350 return rc;
1351 } /* }}} */
1352
1353 static ngx_int_t
ngx_http_upload_process_field_templates(ngx_http_request_t * r,ngx_http_upload_field_template_t * t,ngx_str_t * name,ngx_str_t * value)1354 ngx_http_upload_process_field_templates(
1355 ngx_http_request_t *r, ngx_http_upload_field_template_t *t,
1356 ngx_str_t *name, ngx_str_t *value)
1357 {
1358 if (t->field_lengths == NULL) {
1359 *name = t->value.key;
1360 } else if (ngx_http_script_run(r, name, t->field_lengths->elts, 0,
1361 t->field_values->elts) == NULL) {
1362 return NGX_UPLOAD_SCRIPTERROR;
1363 }
1364
1365 if (t->value_lengths == NULL) {
1366 *value = t->value.value;
1367 } else if (ngx_http_script_run(r, value, t->value_lengths->elts, 0,
1368 t->value_values->elts) == NULL) {
1369 return NGX_UPLOAD_SCRIPTERROR;
1370 }
1371 return NGX_OK;
1372 }
1373
ngx_http_upload_start_handler(ngx_http_upload_ctx_t * u)1374 static ngx_int_t ngx_http_upload_start_handler(ngx_http_upload_ctx_t *u) { /* {{{ */
1375 ngx_http_request_t *r = u->request;
1376 ngx_http_upload_loc_conf_t *ulcf = ngx_http_get_module_loc_conf(r, ngx_http_upload_module);
1377
1378 ngx_file_t *file = &u->output_file;
1379 ngx_path_t *path = u->store_path;
1380 ngx_path_t *state_path = u->state_store_path;
1381 uint32_t n;
1382 ngx_uint_t i;
1383 ngx_int_t rc;
1384 ngx_err_t err;
1385 ngx_http_upload_field_template_t *t;
1386 ngx_http_upload_field_filter_t *f;
1387 ngx_str_t field_name, field_value;
1388 ngx_uint_t pass_field;
1389 ngx_upload_cleanup_t *ucln;
1390
1391 if(u->is_file) {
1392 u->ordinal++;
1393
1394 u->cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_upload_cleanup_t));
1395
1396 if(u->cln == NULL)
1397 return NGX_UPLOAD_NOMEM;
1398
1399 file->name.len = path->name.len + 1 + path->len + (u->session_id.len != 0 ? u->session_id.len : 10);
1400
1401 file->name.data = ngx_palloc(u->request->pool, file->name.len + 1);
1402
1403 if(file->name.data == NULL)
1404 return NGX_UPLOAD_NOMEM;
1405
1406 ngx_memcpy(file->name.data, path->name.data, path->name.len);
1407
1408 file->log = r->connection->log;
1409
1410 if(u->session_id.len != 0) {
1411 (void) ngx_sprintf(file->name.data + path->name.len + 1 + path->len,
1412 "%V%Z", &u->session_id);
1413
1414 ngx_create_hashed_filename(path, file->name.data, file->name.len);
1415
1416 ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0,
1417 "hashed path: %s", file->name.data);
1418
1419 if(u->partial_content) {
1420 ngx_file_t *state_file = &u->state_file;
1421 if(u->merge_buffer == NULL) {
1422 u->merge_buffer = ngx_palloc(r->pool, ulcf->merge_buffer_size);
1423
1424 if(u->merge_buffer == NULL)
1425 return NGX_UPLOAD_NOMEM;
1426 }
1427
1428 state_file->name.len = state_path->name.len + 1 + state_path->len + u->session_id.len + sizeof(".state")-1;
1429 state_file->name.data = ngx_palloc(u->request->pool, state_file->name.len + 1);
1430
1431 if(state_file->name.data == NULL)
1432 return NGX_UPLOAD_NOMEM;
1433
1434 ngx_memcpy(state_file->name.data, state_path->name.data, state_path->name.len);
1435 (void) ngx_sprintf(state_file->name.data + state_path->name.len + 1 + state_path->len,
1436 "%V.state%Z", &u->session_id);
1437
1438 ngx_create_hashed_filename(state_path, state_file->name.data, state_file->name.len);
1439
1440 ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0,
1441 "hashed path of state file: %s", state_file->name.data);
1442 }
1443
1444 file->fd = ngx_open_file(file->name.data, NGX_FILE_WRONLY, NGX_FILE_CREATE_OR_OPEN, ulcf->store_access);
1445
1446 if (file->fd == NGX_INVALID_FILE) {
1447 err = ngx_errno;
1448
1449 ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_errno,
1450 "failed to create output file \"%V\" for \"%V\"", &file->name, &u->file_name);
1451 return NGX_UPLOAD_IOERROR;
1452 }
1453
1454 file->offset = u->content_range_n.start;
1455 }
1456 else{
1457 for(;;) {
1458 n = (uint32_t) ngx_next_temp_number(0);
1459
1460 (void) ngx_sprintf(file->name.data + path->name.len + 1 + path->len,
1461 "%010uD%Z", n);
1462
1463 ngx_create_hashed_filename(path, file->name.data, file->name.len);
1464
1465 ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0,
1466 "hashed path: %s", file->name.data);
1467
1468 file->fd = ngx_open_tempfile(file->name.data, 1, ulcf->store_access);
1469
1470 if (file->fd != NGX_INVALID_FILE) {
1471 file->offset = 0;
1472 break;
1473 }
1474
1475 err = ngx_errno;
1476
1477 if (err == NGX_EEXIST) {
1478 n = (uint32_t) ngx_next_temp_number(1);
1479 continue;
1480 }
1481
1482 ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_errno,
1483 "failed to create output file \"%V\" for \"%V\"", &file->name, &u->file_name);
1484 return NGX_UPLOAD_IOERROR;
1485 }
1486 }
1487
1488 u->cln->handler = ngx_upload_cleanup_handler;
1489
1490 ucln = u->cln->data;
1491 ucln->fd = file->fd;
1492 ucln->filename = file->name.data;
1493 ucln->log = r->connection->log;
1494 ucln->headers_out = &r->headers_out;
1495 ucln->cleanup_statuses = ulcf->cleanup_statuses;
1496 ucln->aborted = 0;
1497
1498 if(ulcf->field_templates) {
1499
1500 if(ulcf->tame_arrays && u->field_name.len > 2 &&
1501 u->field_name.data[u->field_name.len - 1] == ']' &&
1502 u->field_name.data[u->field_name.len - 2] == '[')
1503 {
1504 u->field_name.len -= 2;
1505 }
1506
1507 t = ulcf->field_templates->elts;
1508 for (i = 0; i < ulcf->field_templates->nelts; i++) {
1509 rc = ngx_http_upload_process_field_templates(r, &t[i], &field_name, &field_value);
1510
1511 if(rc != NGX_OK)
1512 goto cleanup_file;
1513
1514 rc = ngx_http_upload_append_field(u, &field_name, &field_value);
1515
1516 if(rc != NGX_OK)
1517 goto cleanup_file;
1518 }
1519 }
1520
1521 if(u->md5_ctx != NULL)
1522 MD5Init(&u->md5_ctx->md5);
1523
1524 if(u->sha1_ctx != NULL)
1525 SHA1_Init(&u->sha1_ctx->sha1);
1526
1527 if(u->sha256_ctx != NULL)
1528 SHA256_Init(&u->sha256_ctx->sha256);
1529
1530 if(u->sha512_ctx != NULL)
1531 SHA512_Init(&u->sha512_ctx->sha512);
1532
1533 if(u->calculate_crc32)
1534 ngx_crc32_init(u->crc32);
1535
1536 if(u->partial_content) {
1537 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0
1538 , "started uploading part %O-%O/%O of file \"%V\" to \"%V\" (field \"%V\", content type \"%V\")"
1539 , u->content_range_n.start
1540 , u->content_range_n.end
1541 , u->content_range_n.total
1542 , &u->file_name
1543 , &u->output_file.name
1544 , &u->field_name
1545 , &u->content_type
1546 );
1547 }
1548 else {
1549 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0
1550 , "started uploading file \"%V\" to \"%V\" (field \"%V\", content type \"%V\")"
1551 , &u->file_name
1552 , &u->output_file.name
1553 , &u->field_name
1554 , &u->content_type
1555 );
1556 }
1557 }else{
1558 pass_field = 0;
1559
1560 if(ulcf->field_filters) {
1561 f = ulcf->field_filters->elts;
1562 for (i = 0; i < ulcf->field_filters->nelts; i++) {
1563 #if (NGX_PCRE)
1564 rc = ngx_regex_exec(f[i].regex, &u->field_name, NULL, 0);
1565
1566 /* Modified by Naren to work around iMovie and Quicktime which send empty values Added: && u->field_name.len > 0 */
1567 if ((ulcf->empty_field_names && rc != NGX_REGEX_NO_MATCHED && rc < 0 && u->field_name.len != 0)
1568 || (!ulcf->empty_field_names && rc != NGX_REGEX_NO_MATCHED && rc < 0))
1569 {
1570 return NGX_UPLOAD_SCRIPTERROR;
1571 }
1572
1573 /*
1574 * If at least one filter succeeds, we pass the field
1575 */
1576 if(rc == 0)
1577 pass_field = 1;
1578 #else
1579 if(ngx_strncmp(f[i].text.data, u->field_name.data, u->field_name.len) == 0)
1580 pass_field = 1;
1581 #endif
1582 }
1583 }
1584
1585 if(pass_field && u->field_name.len != 0) {
1586 /*
1587 * Here we do a small hack: the content of a non-file field
1588 * is not known until ngx_http_upload_flush_output_buffer
1589 * is called. We pass empty field value to simplify things.
1590 */
1591 rc = ngx_http_upload_append_field(u, &u->field_name, &ngx_http_upload_empty_field_value);
1592
1593 if(rc != NGX_OK)
1594 return rc;
1595 }else
1596 u->discard_data = 1;
1597 }
1598
1599
1600 if(ngx_http_upload_add_headers(r, ulcf) != NGX_OK) {
1601 return NGX_HTTP_INTERNAL_SERVER_ERROR;
1602 }
1603
1604 return NGX_OK;
1605
1606 cleanup_file:
1607 return rc;
1608 } /* }}} */
1609
ngx_http_upload_finish_handler(ngx_http_upload_ctx_t * u)1610 static void ngx_http_upload_finish_handler(ngx_http_upload_ctx_t *u) { /* {{{ */
1611 ngx_http_upload_field_template_t *af;
1612 ngx_str_t aggregate_field_name, aggregate_field_value;
1613 ngx_http_request_t *r = u->request;
1614 ngx_http_upload_loc_conf_t *ulcf = ngx_http_get_module_loc_conf(r, ngx_http_upload_module);
1615 ngx_uint_t i;
1616 ngx_int_t rc;
1617 ngx_upload_cleanup_t *ucln;
1618
1619 if(u->is_file) {
1620 ucln = u->cln->data;
1621 ucln->fd = -1;
1622
1623 ngx_close_file(u->output_file.fd);
1624
1625 if(u->md5_ctx)
1626 MD5Final(u->md5_ctx->md5_digest, &u->md5_ctx->md5);
1627
1628 if(u->sha1_ctx)
1629 SHA1_Final(u->sha1_ctx->sha1_digest, &u->sha1_ctx->sha1);
1630
1631 if(u->sha256_ctx)
1632 SHA256_Final(u->sha256_ctx->sha256_digest, &u->sha256_ctx->sha256);
1633
1634 if(u->sha512_ctx)
1635 SHA512_Final(u->sha512_ctx->sha512_digest, &u->sha512_ctx->sha512);
1636
1637 if(u->calculate_crc32)
1638 ngx_crc32_final(u->crc32);
1639
1640 if(u->partial_content) {
1641 if(u->output_file.offset != u->content_range_n.end + 1) {
1642 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0
1643 , "file offset at the end of a part %O does not match the end specified range %O-%O/%O"
1644 , u->output_file.offset
1645 , u->content_range_n.start
1646 , u->content_range_n.end
1647 , u->content_range_n.total
1648 , u->output_file.name
1649 );
1650
1651 goto rollback;
1652 }
1653
1654 rc = ngx_http_upload_merge_ranges(u, &u->content_range_n);
1655
1656 if(rc == NGX_ERROR) {
1657 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0
1658 , "error merging ranges"
1659 );
1660
1661 goto rollback;
1662 }
1663
1664 if(rc == NGX_AGAIN) {
1665 /*
1666 * If there are more parts to go, we do not produce any output
1667 */
1668 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0
1669 , "finished uploading part %O-%O/%O of a file \"%V\" to \"%V\""
1670 , u->content_range_n.start
1671 , u->content_range_n.end
1672 , u->content_range_n.total
1673 , &u->file_name
1674 , &u->output_file.name
1675 );
1676
1677 u->prevent_output = 1;
1678
1679 return;
1680 }
1681
1682 if(ngx_delete_file(u->state_file.name.data) == NGX_FILE_ERROR) {
1683 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0, "failed to remove state file \"%V\"", &u->state_file.name);
1684 } else {
1685 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "removed state file \"%V\"", &u->state_file.name);
1686 }
1687 }
1688
1689 ngx_log_error(NGX_LOG_INFO, r->connection->log, 0
1690 , "finished uploading file \"%V\" to \"%V\""
1691 , &u->file_name
1692 , &u->output_file.name
1693 );
1694
1695 if(ulcf->aggregate_field_templates) {
1696 af = ulcf->aggregate_field_templates->elts;
1697 for (i = 0; i < ulcf->aggregate_field_templates->nelts; i++) {
1698 rc = ngx_http_upload_process_field_templates(r, &af[i], &aggregate_field_name,
1699 &aggregate_field_value);
1700 if (rc != NGX_OK) {
1701 goto rollback;
1702 }
1703
1704 rc = ngx_http_upload_append_field(u, &aggregate_field_name, &aggregate_field_value);
1705
1706 if(rc != NGX_OK)
1707 goto rollback;
1708 }
1709 }
1710 }
1711
1712 // Checkpoint current output chain state
1713 u->checkpoint = u->last;
1714 return;
1715
1716 rollback:
1717 ngx_http_upload_abort_handler(u);
1718 } /* }}} */
1719
ngx_http_upload_abort_handler(ngx_http_upload_ctx_t * u)1720 static void ngx_http_upload_abort_handler(ngx_http_upload_ctx_t *u) { /* {{{ */
1721 ngx_upload_cleanup_t *ucln;
1722
1723 if(u->is_file) {
1724 /*
1725 * Upload of a part could be aborted due to temporary reasons, thus
1726 * next body part will be potentially processed successfuly.
1727 *
1728 * Therefore we don't postpone cleanup to the request finallization
1729 * in order to save additional resources, instead we mark existing
1730 * cleanup record as aborted.
1731 */
1732 ucln = u->cln->data;
1733 ucln->fd = -1;
1734 ucln->aborted = 1;
1735
1736 ngx_close_file(u->output_file.fd);
1737
1738 if(!u->partial_content) {
1739 if(ngx_delete_file(u->output_file.name.data) == NGX_FILE_ERROR) {
1740 ngx_log_error(NGX_LOG_ERR, u->log, ngx_errno
1741 , "aborted uploading file \"%V\" to \"%V\", failed to remove destination file"
1742 , &u->file_name
1743 , &u->output_file.name);
1744 } else {
1745 ngx_log_error(NGX_LOG_ALERT, u->log, 0
1746 , "aborted uploading file \"%V\" to \"%V\", dest file removed"
1747 , &u->file_name
1748 , &u->output_file.name);
1749 }
1750 }
1751 }
1752
1753 // Rollback output chain to the previous consistant state
1754 if(u->checkpoint != NULL) {
1755 u->last = u->checkpoint;
1756 u->last->next = NULL;
1757 }else{
1758 u->chain = u->last = NULL;
1759 u->first_part = 1;
1760 }
1761 } /* }}} */
1762
ngx_http_upload_flush_output_buffer(ngx_http_upload_ctx_t * u,u_char * buf,size_t len)1763 static ngx_int_t ngx_http_upload_flush_output_buffer(ngx_http_upload_ctx_t *u, u_char *buf, size_t len) { /* {{{ */
1764 ngx_http_request_t *r = u->request;
1765 ngx_buf_t *b;
1766 ngx_chain_t *cl;
1767 ngx_http_upload_loc_conf_t *ulcf = ngx_http_get_module_loc_conf(r, ngx_http_upload_module);
1768
1769 if(u->is_file) {
1770 if(u->partial_content) {
1771 if(u->output_file.offset > u->content_range_n.end)
1772 return NGX_OK;
1773
1774 if(u->output_file.offset + (off_t)len > u->content_range_n.end + 1)
1775 len = u->content_range_n.end - u->output_file.offset + 1;
1776 }
1777
1778 if(u->md5_ctx)
1779 MD5Update(&u->md5_ctx->md5, buf, len);
1780
1781 if(u->sha1_ctx)
1782 SHA1_Update(&u->sha1_ctx->sha1, buf, len);
1783
1784 if(u->sha256_ctx)
1785 SHA256_Update(&u->sha256_ctx->sha256, buf, len);
1786
1787 if(u->sha512_ctx)
1788 SHA512_Update(&u->sha512_ctx->sha512, buf, len);
1789
1790 if(u->calculate_crc32)
1791 ngx_crc32_update(&u->crc32, buf, len);
1792
1793 if(ulcf->max_file_size != 0 && !u->partial_content) {
1794 if(u->output_file.offset + (off_t)len > ulcf->max_file_size)
1795 return NGX_UPLOAD_TOOLARGE;
1796 }
1797
1798 if(ngx_write_file(&u->output_file, buf, len, u->output_file.offset) == NGX_ERROR) {
1799 ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_errno,
1800 "write to file \"%V\" failed", &u->output_file.name);
1801 return NGX_UPLOAD_IOERROR;
1802 }else
1803 return NGX_OK;
1804 }else{
1805 if(ulcf->max_output_body_len != 0) {
1806 if (u->output_body_len + len > ulcf->max_output_body_len)
1807 return NGX_UPLOAD_TOOLARGE;
1808 }
1809
1810 u->output_body_len += len;
1811
1812 b = ngx_create_temp_buf(u->request->pool, len);
1813
1814 if (b == NULL) {
1815 return NGX_ERROR;
1816 }
1817
1818 cl = ngx_alloc_chain_link(u->request->pool);
1819 if (cl == NULL) {
1820 return NGX_ERROR;
1821 }
1822
1823 b->last_in_chain = 0;
1824
1825 cl->buf = b;
1826 cl->next = NULL;
1827
1828 b->last = ngx_cpymem(b->last, buf, len);
1829
1830 if(u->chain == NULL) {
1831 u->chain = cl;
1832 u->last = cl;
1833 }else{
1834 u->last->next = cl;
1835 u->last = cl;
1836 }
1837
1838 return NGX_OK;
1839 }
1840 } /* }}} */
1841
1842 static void /* {{{ ngx_http_upload_append_str */
ngx_http_upload_append_str(ngx_http_upload_ctx_t * u,ngx_buf_t * b,ngx_chain_t * cl,ngx_str_t * s)1843 ngx_http_upload_append_str(ngx_http_upload_ctx_t *u, ngx_buf_t *b, ngx_chain_t *cl, ngx_str_t *s)
1844 {
1845 b->start = b->pos = s->data;
1846 b->end = b->last = s->data + s->len;
1847 b->memory = 1;
1848 b->temporary = 1;
1849 b->in_file = 0;
1850 b->last_buf = 0;
1851
1852 b->last_in_chain = 0;
1853 b->last_buf = 0;
1854
1855 cl->buf = b;
1856 cl->next = NULL;
1857
1858 if(u->chain == NULL) {
1859 u->chain = cl;
1860 u->last = cl;
1861 }else{
1862 u->last->next = cl;
1863 u->last = cl;
1864 }
1865
1866 u->output_body_len += s->len;
1867 } /* }}} */
1868
1869 static ngx_int_t /* {{{ ngx_http_upload_append_field */
ngx_http_upload_append_field(ngx_http_upload_ctx_t * u,ngx_str_t * name,ngx_str_t * value)1870 ngx_http_upload_append_field(ngx_http_upload_ctx_t *u, ngx_str_t *name, ngx_str_t *value)
1871 {
1872 ngx_http_upload_loc_conf_t *ulcf = ngx_http_get_module_loc_conf(u->request, ngx_http_upload_module);
1873 ngx_str_t boundary = { u->first_part ? u->boundary.len - 2 : u->boundary.len,
1874 u->first_part ? u->boundary.data + 2 : u->boundary.data };
1875
1876 ngx_buf_t *b;
1877 ngx_chain_t *cl;
1878
1879 if(name->len > 0) {
1880 if(ulcf->max_output_body_len != 0) {
1881 if(u->output_body_len + boundary.len + ngx_upload_field_part1.len + name->len
1882 + ngx_upload_field_part2.len + value->len > ulcf->max_output_body_len)
1883 return NGX_UPLOAD_TOOLARGE;
1884 }
1885
1886 b = ngx_palloc(u->request->pool, value->len > 0 ?
1887 5 * sizeof(ngx_buf_t) : 4 * sizeof(ngx_buf_t));
1888
1889 if (b == NULL) {
1890 return NGX_UPLOAD_NOMEM;
1891 }
1892
1893 cl = ngx_palloc(u->request->pool, value->len > 0 ?
1894 5 * sizeof(ngx_chain_t) : 4 * sizeof(ngx_chain_t));
1895
1896 if (cl == NULL) {
1897 return NGX_UPLOAD_NOMEM;
1898 }
1899
1900 ngx_http_upload_append_str(u, b, cl, &boundary);
1901
1902 ngx_http_upload_append_str(u, b + 1, cl + 1, &ngx_upload_field_part1);
1903
1904 ngx_http_upload_append_str(u, b + 2, cl + 2, name);
1905
1906 ngx_http_upload_append_str(u, b + 3, cl + 3, &ngx_upload_field_part2);
1907
1908 if(value->len > 0)
1909 ngx_http_upload_append_str(u, b + 4, cl + 4, value);
1910
1911 u->output_body_len += boundary.len + ngx_upload_field_part1.len + name->len
1912 + ngx_upload_field_part2.len + value->len;
1913
1914 u->first_part = 0;
1915
1916 u->no_content = 0;
1917 }
1918
1919 return NGX_OK;
1920 } /* }}} */
1921
ngx_http_upload_add_range(ngx_http_upload_merger_state_t * ms,ngx_http_upload_range_t * range_n)1922 static ngx_int_t ngx_http_upload_add_range(ngx_http_upload_merger_state_t *ms, ngx_http_upload_range_t *range_n) {
1923 ms->out_buf->last = ngx_sprintf(ms->out_buf->last, "%O-%O/%O\x0a",
1924 range_n->start,
1925 range_n->end,
1926 range_n->total);
1927
1928 if(*ms->range_header_buffer_pos < ms->range_header_buffer_end) {
1929 *ms->range_header_buffer_pos = ngx_sprintf(*ms->range_header_buffer_pos,
1930 ms->first_range ? "%O-%O/%O" : ",%O-%O/%O",
1931 range_n->start,
1932 range_n->end,
1933 range_n->total);
1934
1935 ms->first_range = 0;
1936 }
1937
1938 return NGX_OK;
1939 }
1940
1941 static ngx_int_t /* {{{ ngx_http_upload_buf_merge_range */
ngx_http_upload_buf_merge_range(ngx_http_upload_merger_state_t * ms,ngx_http_upload_range_t * range_n)1942 ngx_http_upload_buf_merge_range(ngx_http_upload_merger_state_t *ms, ngx_http_upload_range_t *range_n) {
1943 u_char *p, c;
1944 off_t *field;
1945
1946 p = ms->in_buf->pos;
1947
1948 field = ms->parser_state;
1949
1950 do{
1951 *field = 0;
1952
1953 while(p != ms->in_buf->last) {
1954
1955 c = *p++;
1956
1957 if(c >= '0' && c <= '9') {
1958 (*field) = (*field) * 10 + (c - '0');
1959 }
1960 else if(c == '-') {
1961 if(field != &ms->current_range_n.start) {
1962 ngx_log_debug0(NGX_LOG_DEBUG_CORE, ms->log, 0,
1963 "unexpected - while parsing range");
1964 return NGX_ERROR;
1965 }
1966
1967 field = &ms->current_range_n.end;
1968 break;
1969 }
1970 else if(c == '/') {
1971 if(field != &ms->current_range_n.end) {
1972 ngx_log_debug0(NGX_LOG_DEBUG_CORE, ms->log, 0,
1973 "unexpected / while parsing range");
1974 return NGX_ERROR;
1975 }
1976
1977 field = &ms->current_range_n.total;
1978 break;
1979 }
1980 else if(c == LF) {
1981 if(field != &ms->current_range_n.total) {
1982 ngx_log_debug0(NGX_LOG_DEBUG_CORE, ms->log, 0,
1983 "unexpected end of line while parsing range");
1984 return NGX_ERROR;
1985 }
1986
1987 if(ms->current_range_n.start >= ms->current_range_n.end || ms->current_range_n.start >= ms->current_range_n.total
1988 || ms->current_range_n.end > ms->current_range_n.total)
1989 {
1990 ngx_log_debug3(NGX_LOG_DEBUG_CORE, ms->log, 0,
1991 "inconsistent bounds while parsing range: %O-%O/%O",
1992 ms->current_range_n.start,
1993 ms->current_range_n.end,
1994 ms->current_range_n.total);
1995 return NGX_ERROR;
1996 }
1997
1998 if(ms->current_range_n.total != range_n->total) {
1999 ngx_log_debug0(NGX_LOG_DEBUG_CORE, ms->log, 0,
2000 "total number of bytes mismatch while parsing range");
2001 return NGX_ERROR;
2002 }
2003
2004 field = &ms->current_range_n.start;
2005
2006 if(ms->current_range_n.end + 1 < range_n->start) {
2007 /*
2008 * Current range is entirely below the new one,
2009 * output current one and seek next
2010 */
2011 if(ngx_http_upload_add_range(ms, &ms->current_range_n) != NGX_OK) {
2012 return NGX_ERROR;
2013 }
2014
2015 ngx_log_debug3(NGX_LOG_DEBUG_CORE, ms->log, 0,
2016 "< %O-%O/%O", ms->current_range_n.start,
2017 ms->current_range_n.end, ms->current_range_n.total);
2018 break;
2019 }
2020
2021 if(ms->current_range_n.start > range_n->end + 1) {
2022 /*
2023 * Current range is entirely above the new one,
2024 * insert new range
2025 */
2026 if(!ms->found_lower_bound) {
2027 if(ngx_http_upload_add_range(ms, range_n) != NGX_OK) {
2028 return NGX_ERROR;
2029 }
2030 }
2031
2032 if(ngx_http_upload_add_range(ms, &ms->current_range_n) != NGX_OK) {
2033 return NGX_ERROR;
2034 }
2035
2036 ngx_log_debug6(NGX_LOG_DEBUG_CORE, ms->log, 0,
2037 "> %O-%O/%O %O-%O/%O",
2038 range_n->start,
2039 range_n->end,
2040 range_n->total,
2041 ms->current_range_n.start,
2042 ms->current_range_n.end,
2043 ms->current_range_n.total);
2044
2045 ms->found_lower_bound = 1;
2046 break;
2047 }
2048
2049 /*
2050 * Extend range to be merged with the current range
2051 */
2052 range_n->start = range_n->start < ms->current_range_n.start ? range_n->start : ms->current_range_n.start;
2053 range_n->end = range_n->end > ms->current_range_n.end ? range_n->end : ms->current_range_n.end;
2054 break;
2055 }
2056 else {
2057 ngx_log_debug1(NGX_LOG_DEBUG_CORE, ms->log, 0,
2058 "unexpected character %c", *p);
2059 return NGX_ERROR;
2060 }
2061 }
2062 }while(p != ms->in_buf->last);
2063
2064 if(ms->in_buf->last_buf) {
2065 if(field != &ms->current_range_n.start) {
2066 ngx_log_debug0(NGX_LOG_DEBUG_CORE, ms->log, 0,
2067 "unexpected end of file while merging ranges");
2068 return NGX_ERROR;
2069 }
2070
2071 if(!ms->found_lower_bound) {
2072 if(ngx_http_upload_add_range(ms, range_n) != NGX_OK) {
2073 return NGX_ERROR;
2074 }
2075
2076 ngx_log_debug3(NGX_LOG_DEBUG_CORE, ms->log, 0,
2077 "a %O-%O/%O",
2078 range_n->start,
2079 range_n->end,
2080 range_n->total);
2081
2082 ms->complete_ranges = (range_n->start == 0) && (range_n->end == range_n->total - 1) ? 1 : 0;
2083
2084 ms->found_lower_bound = 1;
2085 }
2086 }
2087
2088 ms->parser_state = field;
2089
2090 return NGX_OK;
2091 } /* }}} */
2092
2093 static ngx_int_t /* {{{ ngx_http_upload_merge_ranges */
ngx_http_upload_merge_ranges(ngx_http_upload_ctx_t * u,ngx_http_upload_range_t * range_n)2094 ngx_http_upload_merge_ranges(ngx_http_upload_ctx_t *u, ngx_http_upload_range_t *range_n) {
2095 ngx_file_t *state_file = &u->state_file;
2096 ngx_http_upload_merger_state_t ms;
2097 off_t remaining;
2098 ssize_t rc;
2099 __attribute__((__unused__)) int result;
2100 ngx_buf_t in_buf;
2101 ngx_buf_t out_buf;
2102 ngx_http_upload_loc_conf_t *ulcf = ngx_http_get_module_loc_conf(u->request, ngx_http_upload_module);
2103 ngx_http_upload_range_t range_to_merge_n;
2104
2105
2106 state_file->fd = ngx_open_file(state_file->name.data, NGX_FILE_RDWR, NGX_FILE_CREATE_OR_OPEN, ulcf->store_access);
2107
2108 if (state_file->fd == NGX_INVALID_FILE) {
2109 ngx_log_error(NGX_LOG_ERR, u->log, ngx_errno,
2110 "failed to create or open state file \"%V\"", &state_file->name);
2111 return NGX_ERROR;
2112 }
2113
2114 ngx_lock_fd(state_file->fd);
2115
2116 ngx_fd_info(state_file->fd, &state_file->info);
2117
2118 state_file->offset = 0;
2119 state_file->log = u->log;
2120
2121 ms.in_buf = &in_buf;
2122 ms.out_buf = &out_buf;
2123 ms.parser_state = &ms.current_range_n.start;
2124 ms.log = u->log;
2125
2126 ms.found_lower_bound = 0;
2127 ms.complete_ranges = 0;
2128 ms.first_range = 1;
2129
2130 ms.range_header_buffer = u->range_header_buffer;
2131 ms.range_header_buffer_pos = &u->range_header_buffer_pos;
2132 ms.range_header_buffer_end = u->range_header_buffer_end;
2133
2134 range_to_merge_n = *range_n;
2135
2136 out_buf.start = out_buf.pos = out_buf.last = u->merge_buffer;
2137 out_buf.end = u->merge_buffer + (ulcf->merge_buffer_size >> 1) + NGX_OFF_T_LEN*3 + 2 + 1;
2138 out_buf.file_pos = 0;
2139
2140 in_buf.start = in_buf.pos = in_buf.last = out_buf.end;
2141 in_buf.end = u->merge_buffer + ulcf->merge_buffer_size;
2142
2143 do {
2144 in_buf.file_pos = state_file->offset;
2145 in_buf.pos = in_buf.last = in_buf.start;
2146
2147 if(state_file->offset < state_file->info.st_size) {
2148 remaining = state_file->info.st_size - state_file->offset > in_buf.end - in_buf.start
2149 ? in_buf.end - in_buf.start : state_file->info.st_size - state_file->offset;
2150
2151 rc = ngx_read_file(state_file, in_buf.pos, remaining, state_file->offset);
2152
2153 if(rc < 0 || rc != remaining) {
2154 goto failed;
2155 }
2156
2157 in_buf.last = in_buf.pos + rc;
2158 }
2159
2160 in_buf.last_buf = state_file->offset == state_file->info.st_size ? 1 : 0;
2161
2162 if(out_buf.pos != out_buf.last) {
2163 rc = ngx_write_file(state_file, out_buf.pos, out_buf.last - out_buf.pos, out_buf.file_pos);
2164
2165 if(rc < 0 || rc != out_buf.last - out_buf.pos) {
2166 goto failed;
2167 }
2168
2169 out_buf.file_pos += out_buf.last - out_buf.pos;
2170 }
2171
2172 out_buf.pos = out_buf.last = out_buf.start;
2173
2174 if(ngx_http_upload_buf_merge_range(&ms, &range_to_merge_n) != NGX_OK) {
2175 ngx_log_error(NGX_LOG_ERR, u->log, 0,
2176 "state file \"%V\" is corrupt", &state_file->name);
2177 rc = NGX_ERROR;
2178 goto failed;
2179 }
2180 } while(state_file->offset < state_file->info.st_size);
2181
2182 if(out_buf.pos != out_buf.last) {
2183 rc = ngx_write_file(state_file, out_buf.pos, out_buf.last - out_buf.pos, out_buf.file_pos);
2184
2185 if(rc < 0 || rc != out_buf.last - out_buf.pos) {
2186 goto failed;
2187 }
2188
2189 out_buf.file_pos += out_buf.last - out_buf.pos;
2190 }
2191
2192 if(out_buf.file_pos < state_file->info.st_size) {
2193 result = ftruncate(state_file->fd, out_buf.file_pos);
2194 }
2195
2196 rc = ms.complete_ranges ? NGX_OK : NGX_AGAIN;
2197
2198 failed:
2199 ngx_unlock_fd(state_file->fd);
2200
2201 ngx_close_file(state_file->fd);
2202
2203 return rc;
2204 } /* }}} */
2205
2206 static void * /* {{{ ngx_http_upload_create_loc_conf */
ngx_http_upload_create_loc_conf(ngx_conf_t * cf)2207 ngx_http_upload_create_loc_conf(ngx_conf_t *cf)
2208 {
2209 ngx_http_upload_loc_conf_t *conf;
2210
2211 conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upload_loc_conf_t));
2212 if (conf == NULL) {
2213 return NGX_CONF_ERROR;
2214 }
2215
2216 conf->store_access = NGX_CONF_UNSET_UINT;
2217 conf->forward_args = NGX_CONF_UNSET;
2218 conf->tame_arrays = NGX_CONF_UNSET;
2219 conf->resumable_uploads = NGX_CONF_UNSET;
2220 conf->empty_field_names = NGX_CONF_UNSET;
2221
2222 conf->buffer_size = NGX_CONF_UNSET_SIZE;
2223 conf->merge_buffer_size = NGX_CONF_UNSET_SIZE;
2224 conf->range_header_buffer_size = NGX_CONF_UNSET_SIZE;
2225 conf->max_header_len = NGX_CONF_UNSET_SIZE;
2226 conf->max_output_body_len = NGX_CONF_UNSET_SIZE;
2227 conf->max_file_size = NGX_CONF_UNSET;
2228 conf->limit_rate = NGX_CONF_UNSET_SIZE;
2229
2230 /*
2231 * conf->header_templates,
2232 * conf->field_templates,
2233 * conf->aggregate_field_templates,
2234 * and conf->field_filters are
2235 * zeroed by ngx_pcalloc
2236 */
2237
2238 return conf;
2239 } /* }}} */
2240
2241 static char * /* {{{ ngx_http_upload_merge_loc_conf */
ngx_http_upload_merge_loc_conf(ngx_conf_t * cf,void * parent,void * child)2242 ngx_http_upload_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
2243 {
2244 ngx_http_upload_loc_conf_t *prev = parent;
2245 ngx_http_upload_loc_conf_t *conf = child;
2246
2247 if ((conf->url.len == 0) && (conf->url_cv == NULL)) {
2248 conf->url = prev->url;
2249 conf->url_cv = prev->url_cv;
2250 }
2251
2252 if(conf->url.len != 0) {
2253 ngx_http_upload_merge_path_value(cf,
2254 &conf->store_path,
2255 prev->store_path,
2256 &ngx_http_upload_temp_path);
2257
2258 ngx_http_upload_merge_path_value(cf,
2259 &conf->state_store_path,
2260 prev->state_store_path,
2261 &ngx_http_upload_temp_path);
2262 }
2263
2264 ngx_conf_merge_uint_value(conf->store_access,
2265 prev->store_access, 0600);
2266
2267 ngx_conf_merge_size_value(conf->buffer_size,
2268 prev->buffer_size,
2269 (size_t) ngx_pagesize);
2270
2271 ngx_conf_merge_size_value(conf->merge_buffer_size,
2272 prev->merge_buffer_size,
2273 (size_t) ngx_pagesize >> 1);
2274
2275 ngx_conf_merge_size_value(conf->range_header_buffer_size,
2276 prev->range_header_buffer_size,
2277 (size_t) 256);
2278
2279 ngx_conf_merge_size_value(conf->max_header_len,
2280 prev->max_header_len,
2281 (size_t) 512);
2282
2283 ngx_conf_merge_size_value(conf->max_output_body_len,
2284 prev->max_output_body_len,
2285 (size_t) 100 * 1024);
2286
2287 ngx_conf_merge_off_value(conf->max_file_size,
2288 prev->max_file_size,
2289 0);
2290
2291 ngx_conf_merge_size_value(conf->limit_rate, prev->limit_rate, 0);
2292
2293 if(conf->forward_args == NGX_CONF_UNSET) {
2294 conf->forward_args = (prev->forward_args != NGX_CONF_UNSET) ?
2295 prev->forward_args : 0;
2296 }
2297
2298 if(conf->tame_arrays == NGX_CONF_UNSET) {
2299 conf->tame_arrays = (prev->tame_arrays != NGX_CONF_UNSET) ?
2300 prev->tame_arrays : 0;
2301 }
2302
2303 if(conf->resumable_uploads == NGX_CONF_UNSET) {
2304 conf->resumable_uploads = (prev->resumable_uploads != NGX_CONF_UNSET) ?
2305 prev->resumable_uploads : 0;
2306 }
2307
2308 if(conf->empty_field_names == NGX_CONF_UNSET) {
2309 conf->empty_field_names = (prev->empty_field_names != NGX_CONF_UNSET) ?
2310 prev->empty_field_names : 0;
2311 }
2312
2313 if(conf->field_templates == NULL) {
2314 conf->field_templates = prev->field_templates;
2315 }
2316
2317 if(conf->aggregate_field_templates == NULL) {
2318 conf->aggregate_field_templates = prev->aggregate_field_templates;
2319
2320 if(prev->md5) {
2321 conf->md5 = prev->md5;
2322 }
2323
2324 if(prev->sha1) {
2325 conf->sha1 = prev->sha1;
2326 }
2327
2328 if(prev->sha256) {
2329 conf->sha256 = prev->sha256;
2330 }
2331
2332 if(prev->sha512) {
2333 conf->sha512 = prev->sha512;
2334 }
2335
2336 if(prev->crc32) {
2337 conf->crc32 = prev->crc32;
2338 }
2339 }
2340
2341 if(conf->field_filters == NULL) {
2342 conf->field_filters = prev->field_filters;
2343 }
2344
2345 if(conf->cleanup_statuses == NULL) {
2346 conf->cleanup_statuses = prev->cleanup_statuses;
2347 }
2348
2349 if(conf->header_templates == NULL) {
2350 conf->header_templates = prev->header_templates;
2351 }
2352
2353 return NGX_CONF_OK;
2354 } /* }}} */
2355
2356 static ngx_int_t /* {{{ ngx_http_upload_add_variables */
ngx_http_upload_add_variables(ngx_conf_t * cf)2357 ngx_http_upload_add_variables(ngx_conf_t *cf)
2358 {
2359 ngx_http_variable_t *var, *v;
2360
2361 for (v = ngx_http_upload_variables; v->name.len; v++) {
2362 var = ngx_http_add_variable(cf, &v->name, v->flags);
2363 if (var == NULL) {
2364 return NGX_ERROR;
2365 }
2366
2367 var->get_handler = v->get_handler;
2368 var->data = v->data;
2369 }
2370
2371 for (v = ngx_http_upload_aggregate_variables; v->name.len; v++) {
2372 var = ngx_http_add_variable(cf, &v->name, v->flags);
2373 if (var == NULL) {
2374 return NGX_ERROR;
2375 }
2376
2377 var->get_handler = v->get_handler;
2378 var->data = v->data;
2379 }
2380
2381 return NGX_OK;
2382 } /* }}} */
2383
2384 static ngx_int_t /* {{{ ngx_http_upload_variable */
ngx_http_upload_variable(ngx_http_request_t * r,ngx_http_variable_value_t * v,uintptr_t data)2385 ngx_http_upload_variable(ngx_http_request_t *r,
2386 ngx_http_variable_value_t *v, uintptr_t data)
2387 {
2388 ngx_http_upload_ctx_t *u;
2389 ngx_str_t *value;
2390
2391 v->valid = 1;
2392 v->no_cacheable = 0;
2393 v->not_found = 0;
2394
2395 u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
2396
2397 value = (ngx_str_t *) ((char *) u + data);
2398
2399 v->data = value->data;
2400 v->len = value->len;
2401
2402 return NGX_OK;
2403 } /* }}} */
2404
2405 static ngx_int_t
ngx_http_upload_hash_variable(ngx_http_request_t * r,ngx_http_variable_value_t * v,uintptr_t data,u_char * digest,ngx_uint_t digest_len)2406 ngx_http_upload_hash_variable(ngx_http_request_t *r,
2407 ngx_http_variable_value_t *v, uintptr_t data, u_char *digest,
2408 ngx_uint_t digest_len)
2409 {
2410 ngx_uint_t i;
2411 u_char *c;
2412 u_char *p;
2413 u_char *hex_table;
2414
2415 v->valid = 1;
2416 v->no_cacheable = 0;
2417 v->not_found = 0;
2418
2419 hex_table = (u_char*)data;
2420
2421 p = ngx_palloc(r->pool, digest_len * 2);
2422 if (p == NULL) {
2423 return NGX_ERROR;
2424 }
2425
2426 c = p + digest_len * 2;
2427 i = digest_len;
2428
2429 do{
2430 i--;
2431 *--c = hex_table[digest[i] & 0xf];
2432 *--c = hex_table[digest[i] >> 4];
2433 }while(i != 0);
2434
2435 v->data = c;
2436 v->len = digest_len * 2;
2437
2438 return NGX_OK;
2439 } /* }}} */
2440
2441 static ngx_int_t /* {{{ ngx_http_upload_md5_variable */
ngx_http_upload_md5_variable(ngx_http_request_t * r,ngx_http_variable_value_t * v,uintptr_t data)2442 ngx_http_upload_md5_variable(ngx_http_request_t *r,
2443 ngx_http_variable_value_t *v, uintptr_t data)
2444 {
2445 ngx_http_upload_ctx_t *u;
2446
2447 u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
2448
2449 if(u->md5_ctx == NULL || u->partial_content) {
2450 v->not_found = 1;
2451 return NGX_OK;
2452 }
2453 return ngx_http_upload_hash_variable(r, v, data, u->md5_ctx->md5_digest, MD5_DIGEST_LENGTH);
2454 } /* }}} */
2455
2456 static ngx_int_t /* {{{ ngx_http_upload_sha1_variable */
ngx_http_upload_sha1_variable(ngx_http_request_t * r,ngx_http_variable_value_t * v,uintptr_t data)2457 ngx_http_upload_sha1_variable(ngx_http_request_t *r,
2458 ngx_http_variable_value_t *v, uintptr_t data)
2459 {
2460 ngx_http_upload_ctx_t *u;
2461
2462 u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
2463
2464 if(u->sha1_ctx == NULL || u->partial_content) {
2465 v->not_found = 1;
2466 return NGX_OK;
2467 }
2468
2469 return ngx_http_upload_hash_variable(r, v, data, u->sha1_ctx->sha1_digest, SHA_DIGEST_LENGTH);
2470 } /* }}} */
2471
2472 static ngx_int_t /* {{{ ngx_http_upload_sha256_variable */
ngx_http_upload_sha256_variable(ngx_http_request_t * r,ngx_http_variable_value_t * v,uintptr_t data)2473 ngx_http_upload_sha256_variable(ngx_http_request_t *r,
2474 ngx_http_variable_value_t *v, uintptr_t data)
2475 {
2476 ngx_http_upload_ctx_t *u;
2477
2478 u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
2479
2480 if(u->sha256_ctx == NULL || u->partial_content) {
2481 v->not_found = 1;
2482 return NGX_OK;
2483 }
2484
2485 return ngx_http_upload_hash_variable(r, v, data, u->sha256_ctx->sha256_digest, SHA256_DIGEST_LENGTH);
2486 } /* }}} */
2487
2488 static ngx_int_t /* {{{ ngx_http_upload_sha512_variable */
ngx_http_upload_sha512_variable(ngx_http_request_t * r,ngx_http_variable_value_t * v,uintptr_t data)2489 ngx_http_upload_sha512_variable(ngx_http_request_t *r,
2490 ngx_http_variable_value_t *v, uintptr_t data)
2491 {
2492 ngx_http_upload_ctx_t *u;
2493
2494 u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
2495
2496 if(u->sha512_ctx == NULL || u->partial_content) {
2497 v->not_found = 1;
2498 return NGX_OK;
2499 }
2500
2501 return ngx_http_upload_hash_variable(r, v, data, u->sha512_ctx->sha512_digest, SHA512_DIGEST_LENGTH);
2502 } /* }}} */
2503
2504 static ngx_int_t /* {{{ ngx_http_upload_crc32_variable */
ngx_http_upload_crc32_variable(ngx_http_request_t * r,ngx_http_variable_value_t * v,uintptr_t data)2505 ngx_http_upload_crc32_variable(ngx_http_request_t *r,
2506 ngx_http_variable_value_t *v, uintptr_t data)
2507 {
2508 ngx_http_upload_ctx_t *u;
2509 u_char *p;
2510 uint32_t *value;
2511
2512 u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
2513
2514 if(u->partial_content) {
2515 v->not_found = 1;
2516 return NGX_OK;
2517 }
2518
2519 value = (uint32_t *) ((char *) u + data);
2520
2521 p = ngx_palloc(r->pool, NGX_INT_T_LEN);
2522 if (p == NULL) {
2523 return NGX_ERROR;
2524 }
2525
2526 v->len = ngx_sprintf(p, "%08uxd", *value) - p;
2527 v->valid = 1;
2528 v->no_cacheable = 0;
2529 v->not_found = 0;
2530 v->data = p;
2531
2532 return NGX_OK;
2533 } /* }}} */
2534
2535 static ngx_int_t /* {{{ ngx_http_upload_file_size_variable */
ngx_http_upload_file_size_variable(ngx_http_request_t * r,ngx_http_variable_value_t * v,uintptr_t data)2536 ngx_http_upload_file_size_variable(ngx_http_request_t *r,
2537 ngx_http_variable_value_t *v, uintptr_t data)
2538 {
2539 ngx_http_upload_ctx_t *u;
2540 u_char *p;
2541 off_t *value;
2542
2543 u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
2544
2545 value = (off_t *) ((char *) u + data);
2546
2547 p = ngx_palloc(r->pool, NGX_OFF_T_LEN);
2548 if (p == NULL) {
2549 return NGX_ERROR;
2550 }
2551
2552 v->len = ngx_sprintf(p, "%O", *value) - p;
2553 v->valid = 1;
2554 v->no_cacheable = 0;
2555 v->not_found = 0;
2556 v->data = p;
2557
2558 return NGX_OK;
2559 } /* }}} */
2560
2561 static void /* {{{ ngx_http_upload_content_range_variable_set */
ngx_http_upload_content_range_variable_set(ngx_http_request_t * r,ngx_http_variable_value_t * v,uintptr_t data)2562 ngx_http_upload_content_range_variable_set(ngx_http_request_t *r,
2563 ngx_http_variable_value_t *v, uintptr_t data)
2564 {
2565 ngx_http_upload_ctx_t *u;
2566 ngx_str_t val;
2567 ngx_http_upload_range_t *value;
2568
2569 u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
2570
2571 value = (ngx_http_upload_range_t *) ((char *) u + data);
2572
2573 val.len = v->len;
2574 val.data = v->data;
2575
2576 if(ngx_http_upload_parse_range(&val, value) != NGX_OK) {
2577 ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
2578 "invalid range \"%V\"", &val);
2579 }
2580 } /* }}} */
2581
2582 static ngx_int_t /* {{{ ngx_http_upload_content_range_variable */
ngx_http_upload_content_range_variable(ngx_http_request_t * r,ngx_http_variable_value_t * v,uintptr_t data)2583 ngx_http_upload_content_range_variable(ngx_http_request_t *r,
2584 ngx_http_variable_value_t *v, uintptr_t data)
2585 {
2586 ngx_http_upload_ctx_t *u;
2587 u_char *p;
2588 ngx_http_upload_range_t *value;
2589
2590 u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
2591
2592 value = (ngx_http_upload_range_t *) ((char *) u + data);
2593
2594 p = ngx_palloc(r->pool, sizeof("bytes ") - 1 + 3*NGX_OFF_T_LEN + 2);
2595 if (p == NULL) {
2596 return NGX_ERROR;
2597 }
2598
2599 v->len = u->partial_content ?
2600 ngx_sprintf(p, "bytes %O-%O/%O", value->start, value->end, value->total) - p :
2601 ngx_sprintf(p, "bytes %O-%O/%O", (off_t)0, u->output_file.offset, u->output_file.offset) - p
2602 ;
2603 v->valid = 1;
2604 v->no_cacheable = 0;
2605 v->not_found = 0;
2606 v->data = p;
2607
2608 return NGX_OK;
2609 } /* }}} */
2610
2611 static ngx_int_t /* {{{ ngx_http_upload_uint_variable */
ngx_http_upload_uint_variable(ngx_http_request_t * r,ngx_http_variable_value_t * v,uintptr_t data)2612 ngx_http_upload_uint_variable(ngx_http_request_t *r,
2613 ngx_http_variable_value_t *v, uintptr_t data)
2614 {
2615 ngx_http_upload_ctx_t *u;
2616 u_char *p;
2617 ngx_uint_t *value;
2618
2619 u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
2620
2621 value = (ngx_uint_t *) ((char *) u + data);
2622
2623 p = ngx_palloc(r->pool, sizeof("18446744073709551616") - 1);
2624 if (p == NULL) {
2625 return NGX_ERROR;
2626 }
2627
2628 v->len = ngx_sprintf(p, "%ui", *value) - p;
2629 v->valid = 1;
2630 v->no_cacheable = 0;
2631 v->not_found = 0;
2632 v->data = p;
2633
2634 return NGX_OK;
2635 } /* }}} */
2636
2637 static char * /* {{{ ngx_http_upload_set_form_field */
ngx_http_upload_set_form_field(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)2638 ngx_http_upload_set_form_field(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
2639 {
2640 ngx_int_t n, i;
2641 ngx_str_t *value;
2642 ngx_http_script_compile_t sc;
2643 ngx_http_upload_field_template_t *h;
2644 ngx_array_t **field;
2645 ngx_http_variable_t *v;
2646 u_char *match;
2647 ngx_http_upload_loc_conf_t *ulcf = conf;
2648
2649 field = (ngx_array_t**) (((u_char*)conf) + cmd->offset);
2650
2651 value = cf->args->elts;
2652
2653 if (*field == NULL) {
2654 *field = ngx_array_create(cf->pool, 1,
2655 sizeof(ngx_http_upload_field_template_t));
2656 if (*field == NULL) {
2657 return NGX_CONF_ERROR;
2658 }
2659 }
2660
2661 h = ngx_array_push(*field);
2662 if (h == NULL) {
2663 return NGX_CONF_ERROR;
2664 }
2665
2666 h->value.hash = 1;
2667 h->value.key = value[1];
2668 h->value.value = value[2];
2669 h->field_lengths = NULL;
2670 h->field_values = NULL;
2671 h->value_lengths = NULL;
2672 h->value_values = NULL;
2673
2674 /*
2675 * Compile field name
2676 */
2677 n = ngx_http_script_variables_count(&value[1]);
2678
2679 if (n > 0) {
2680 ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
2681
2682 sc.cf = cf;
2683 sc.source = &value[1];
2684 sc.lengths = &h->field_lengths;
2685 sc.values = &h->field_values;
2686 sc.variables = n;
2687 sc.complete_lengths = 1;
2688 sc.complete_values = 1;
2689
2690 if (ngx_http_script_compile(&sc) != NGX_OK) {
2691 return NGX_CONF_ERROR;
2692 }
2693 }
2694
2695 /*
2696 * Compile field value
2697 */
2698 n = ngx_http_script_variables_count(&value[2]);
2699
2700 if (n > 0) {
2701 ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
2702
2703 sc.cf = cf;
2704 sc.source = &value[2];
2705 sc.lengths = &h->value_lengths;
2706 sc.values = &h->value_values;
2707 sc.variables = n;
2708 sc.complete_lengths = 1;
2709 sc.complete_values = 1;
2710
2711 if (ngx_http_script_compile(&sc) != NGX_OK) {
2712 return NGX_CONF_ERROR;
2713 }
2714 }
2715
2716 /*
2717 * Check for aggregate variables in script
2718 */
2719 for(i = 1;i <= 2;i++) {
2720 for (v = ngx_http_upload_aggregate_variables; v->name.len; v++) {
2721 match = ngx_strcasestrn(value[i].data, (char*)v->name.data, v->name.len - 1);
2722
2723 /*
2724 * ngx_http_script_compile does check for final bracket earlier,
2725 * therefore we don't need to care about it, which simplifies things
2726 */
2727 if(match != NULL
2728 && ((match - value[i].data >= 1 && match[-1] == '$')
2729 || (match - value[i].data >= 2 && match[-2] == '$' && match[-1] == '{')))
2730 {
2731 if(cmd->offset != offsetof(ngx_http_upload_loc_conf_t, aggregate_field_templates)) {
2732 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2733 "variables upload_file_md5"
2734 ", upload_file_md5_uc"
2735 ", upload_file_sha1"
2736 ", upload_file_sha1_uc"
2737 ", upload_file_sha256"
2738 ", upload_file_sha256_uc"
2739 ", upload_file_sha512"
2740 ", upload_file_sha512_uc"
2741 ", upload_file_crc32"
2742 ", upload_content_range"
2743 " and upload_file_size"
2744 " could be specified only in upload_aggregate_form_field directive");
2745 return NGX_CONF_ERROR;
2746 }
2747
2748 if(v->get_handler == ngx_http_upload_md5_variable)
2749 ulcf->md5 = 1;
2750
2751 if(v->get_handler == ngx_http_upload_sha1_variable)
2752 ulcf->sha1 = 1;
2753
2754 if(v->get_handler == ngx_http_upload_sha256_variable)
2755 ulcf->sha256 = 1;
2756
2757 if(v->get_handler == ngx_http_upload_sha512_variable)
2758 ulcf->sha512 = 1;
2759
2760 if(v->get_handler == ngx_http_upload_crc32_variable)
2761 ulcf->crc32 = 1;
2762 }
2763 }
2764 }
2765
2766 return NGX_CONF_OK;
2767 } /* }}} */
2768
2769 static char * /* {{{ ngx_http_upload_pass_form_field */
ngx_http_upload_pass_form_field(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)2770 ngx_http_upload_pass_form_field(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
2771 {
2772 ngx_http_upload_loc_conf_t *ulcf = conf;
2773
2774 ngx_str_t *value;
2775 #if (NGX_PCRE)
2776 #if defined nginx_version && nginx_version >= 8025
2777 ngx_regex_compile_t rc;
2778 u_char errstr[NGX_MAX_CONF_ERRSTR];
2779 #else
2780 ngx_int_t n;
2781 ngx_str_t err;
2782 #endif
2783 #endif
2784 ngx_http_upload_field_filter_t *f;
2785
2786 value = cf->args->elts;
2787
2788 if (ulcf->field_filters == NULL) {
2789 ulcf->field_filters = ngx_array_create(cf->pool, 1,
2790 sizeof(ngx_http_upload_field_filter_t));
2791 if (ulcf->field_filters == NULL) {
2792 return NGX_CONF_ERROR;
2793 }
2794 }
2795
2796 f = ngx_array_push(ulcf->field_filters);
2797 if (f == NULL) {
2798 return NGX_CONF_ERROR;
2799 }
2800
2801 #if (NGX_PCRE)
2802 #if defined nginx_version && nginx_version >= 8025
2803 ngx_memzero(&rc, sizeof(ngx_regex_compile_t));
2804
2805 rc.pattern = value[1];
2806 rc.pool = cf->pool;
2807 rc.err.len = NGX_MAX_CONF_ERRSTR;
2808 rc.err.data = errstr;
2809
2810 if(ngx_regex_compile(&rc) != NGX_OK) {
2811 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%V", &rc.err);
2812 return NGX_CONF_ERROR;
2813 }
2814
2815 f->regex = rc.regex;
2816 f->ncaptures = rc.captures;
2817 #else
2818 f->regex = ngx_regex_compile(&value[1], 0, cf->pool, &err);
2819
2820 if (f->regex == NULL) {
2821 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data);
2822 return NGX_CONF_ERROR;
2823 }
2824
2825 n = ngx_regex_capture_count(f->regex);
2826
2827 if (n < 0) {
2828 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2829 ngx_regex_capture_count_n " failed for "
2830 "pattern \"%V\"", &value[1]);
2831 return NGX_CONF_ERROR;
2832 }
2833
2834 f->ncaptures = n;
2835 #endif
2836 #else
2837 f->text.len = value[1].len;
2838 f->text.data = value[1].data;
2839 #endif
2840
2841 return NGX_CONF_OK;
2842 } /* }}} */
2843
2844 static char * /* {{{ ngx_http_upload_cleanup */
ngx_http_upload_cleanup(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)2845 ngx_http_upload_cleanup(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
2846 {
2847 ngx_http_upload_loc_conf_t *ulcf = conf;
2848
2849 ngx_str_t *value;
2850 ngx_uint_t i;
2851 ngx_int_t status, lo, hi;
2852 uint16_t *s;
2853
2854 value = cf->args->elts;
2855
2856 if (ulcf->cleanup_statuses == NULL) {
2857 ulcf->cleanup_statuses = ngx_array_create(cf->pool, 1,
2858 sizeof(uint16_t));
2859 if (ulcf->cleanup_statuses == NULL) {
2860 return NGX_CONF_ERROR;
2861 }
2862 }
2863
2864 for (i = 1; i < cf->args->nelts; i++) {
2865 if(value[i].len > 4 && value[i].data[3] == '-') {
2866 lo = ngx_atoi(value[i].data, 3);
2867
2868 if (lo == NGX_ERROR) {
2869 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2870 "invalid lower bound \"%V\"", &value[i]);
2871 return NGX_CONF_ERROR;
2872 }
2873
2874 hi = ngx_atoi(value[i].data + 4, value[i].len - 4);
2875
2876 if (hi == NGX_ERROR) {
2877 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2878 "invalid upper bound \"%V\"", &value[i]);
2879 return NGX_CONF_ERROR;
2880 }
2881
2882 if (hi < lo) {
2883 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2884 "upper bound must be greater then lower bound in \"%V\"",
2885 &value[i]);
2886 return NGX_CONF_ERROR;
2887 }
2888
2889 }else{
2890 status = ngx_atoi(value[i].data, value[i].len);
2891
2892 if (status == NGX_ERROR) {
2893 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2894 "invalid value \"%V\"", &value[i]);
2895 return NGX_CONF_ERROR;
2896 }
2897
2898 hi = lo = status;
2899 }
2900
2901 if (lo < 200 || hi > 599) {
2902 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2903 "value(s) \"%V\" must be between 200 and 599",
2904 &value[i]);
2905 return NGX_CONF_ERROR;
2906 }
2907
2908 for(status = lo ; status <= hi; status++) {
2909 s = ngx_array_push(ulcf->cleanup_statuses);
2910 if (s == NULL) {
2911 return NGX_CONF_ERROR;
2912 }
2913
2914 *s = status;
2915 }
2916 }
2917
2918
2919 return NGX_CONF_OK;
2920 } /* }}} */
2921
2922 static char * /* {{{ ngx_http_upload_pass */
ngx_http_upload_pass(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)2923 ngx_http_upload_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
2924 {
2925 ngx_http_core_loc_conf_t *clcf;
2926 ngx_http_upload_loc_conf_t *ulcf = conf;
2927 ngx_str_t *value;
2928 ngx_http_compile_complex_value_t ccv;
2929
2930 if ((ulcf->url.len != 0) || (ulcf->url_cv != NULL)) {
2931 return "is duplicate";
2932 }
2933
2934 value = cf->args->elts;
2935
2936 if (value[1].len == 0) {
2937 ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
2938 "empty value in \"%V\" directive",
2939 &cmd->name);
2940
2941 return NGX_CONF_ERROR;
2942 }
2943
2944 clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module);
2945 clcf->handler = ngx_http_upload_handler;
2946
2947 if (ngx_http_script_variables_count(&value[1])) {
2948 /* complex value */
2949 ulcf->url_cv = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
2950 if (ulcf->url_cv == NULL) {
2951 return NGX_CONF_ERROR;
2952 }
2953
2954 ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
2955
2956 ccv.cf = cf;
2957 ccv.value = &value[1];
2958 ccv.complex_value = ulcf->url_cv;
2959
2960 if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
2961 return NGX_CONF_ERROR;
2962 }
2963 } else {
2964 /* simple value */
2965 ulcf->url = value[1];
2966 }
2967
2968 return NGX_CONF_OK;
2969 } /* }}} */
2970
2971 static char * /* {{{ ngx_http_upload_set_path_slot */
ngx_http_upload_set_path_slot(ngx_conf_t * cf,ngx_command_t * cmd,void * conf)2972 ngx_http_upload_set_path_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
2973 {
2974 char *p = conf;
2975
2976 ssize_t level;
2977 ngx_str_t *value;
2978 ngx_uint_t i, n;
2979 ngx_http_upload_path_t *path, **slot;
2980 ngx_http_compile_complex_value_t ccv;
2981
2982 slot = (ngx_http_upload_path_t **) (p + cmd->offset);
2983
2984 if (*slot) {
2985 return "is duplicate";
2986 }
2987
2988 path = ngx_pcalloc(cf->pool, sizeof(ngx_http_upload_path_t));
2989 if (path == NULL) {
2990 return NGX_CONF_ERROR;
2991 }
2992
2993 path->path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t));
2994 if (path->path == NULL) {
2995 return NGX_CONF_ERROR;
2996 }
2997
2998 value = cf->args->elts;
2999
3000 path->path->name = value[1];
3001
3002 if (path->path->name.data[path->path->name.len - 1] == '/') {
3003 path->path->name.len--;
3004 }
3005
3006 if (ngx_conf_full_name(cf->cycle, &path->path->name, 0) != NGX_OK) {
3007 return NULL;
3008 }
3009
3010 path->path->len = 0;
3011 path->path->manager = NULL;
3012 path->path->loader = NULL;
3013 path->path->conf_file = cf->conf_file->file.name.data;
3014 path->path->line = cf->conf_file->line;
3015
3016 for (i = 0, n = 2; n < cf->args->nelts; i++, n++) {
3017 level = ngx_atoi(value[n].data, value[n].len);
3018 if (level == NGX_ERROR || level == 0) {
3019 return "invalid value";
3020 }
3021
3022 path->path->level[i] = level;
3023 path->path->len += level + 1;
3024 }
3025
3026 while (i < 3) {
3027 path->path->level[i++] = 0;
3028 }
3029
3030 *slot = path;
3031
3032 if(ngx_http_script_variables_count(&value[1])) {
3033 ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
3034
3035 ccv.cf = cf;
3036 ccv.value = &value[1];
3037 ccv.complex_value = &path->dynamic;
3038
3039 if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
3040 return NGX_CONF_ERROR;
3041 }
3042
3043 path->is_dynamic = 1;
3044 }
3045 else {
3046 if (ngx_add_path(cf, &path->path) == NGX_ERROR) {
3047 return NGX_CONF_ERROR;
3048 }
3049 }
3050
3051 return NGX_CONF_OK;
3052 } /* }}} */
3053
3054
3055 static char * /* {{{ ngx_http_upload_merge_path_value */
ngx_http_upload_merge_path_value(ngx_conf_t * cf,ngx_http_upload_path_t ** path,ngx_http_upload_path_t * prev,ngx_path_init_t * init)3056 ngx_http_upload_merge_path_value(ngx_conf_t *cf, ngx_http_upload_path_t **path, ngx_http_upload_path_t *prev,
3057 ngx_path_init_t *init)
3058 {
3059 if (*path) {
3060 return NGX_CONF_OK;
3061 }
3062
3063 if (prev) {
3064 *path = prev;
3065 return NGX_CONF_OK;
3066 }
3067
3068 *path = ngx_pcalloc(cf->pool, sizeof(ngx_http_upload_path_t));
3069 if(*path == NULL) {
3070 return NGX_CONF_ERROR;
3071 }
3072
3073 (*path)->path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t));
3074 if((*path)->path == NULL) {
3075 return NGX_CONF_ERROR;
3076 }
3077
3078 (*path)->path->name = init->name;
3079
3080 if(ngx_conf_full_name(cf->cycle, &(*path)->path->name, 0) != NGX_OK) {
3081 return NGX_CONF_ERROR;
3082 }
3083
3084 (*path)->path->level[0] = init->level[0];
3085 (*path)->path->level[1] = init->level[1];
3086 (*path)->path->level[2] = init->level[2];
3087
3088 (*path)->path->len = init->level[0] + (init->level[0] ? 1 : 0)
3089 + init->level[1] + (init->level[1] ? 1 : 0)
3090 + init->level[2] + (init->level[2] ? 1 : 0);
3091
3092 (*path)->path->manager = NULL;
3093 (*path)->path->loader = NULL;
3094 (*path)->path->conf_file = NULL;
3095
3096 if(ngx_add_path(cf, &(*path)->path) != NGX_OK) {
3097 return NGX_CONF_ERROR;
3098 }
3099
3100 return NGX_CONF_OK;
3101 } /* }}} */
3102
3103 ngx_int_t /* {{{ ngx_http_read_upload_client_request_body */
ngx_http_read_upload_client_request_body(ngx_http_request_t * r)3104 ngx_http_read_upload_client_request_body(ngx_http_request_t *r) {
3105 ssize_t size, preread;
3106 ngx_buf_t *b;
3107 ngx_chain_t *cl, **next;
3108 ngx_http_request_body_t *rb;
3109 ngx_http_core_loc_conf_t *clcf;
3110 ngx_http_upload_ctx_t *u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
3111
3112 #if defined nginx_version && nginx_version >= 8011
3113 r->main->count++;
3114 #endif
3115
3116 if (r->request_body || r->discard_body) {
3117 return NGX_OK;
3118 }
3119
3120 rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t));
3121 if (rb == NULL) {
3122 upload_shutdown_ctx(u);
3123 return NGX_HTTP_INTERNAL_SERVER_ERROR;
3124 }
3125
3126 r->request_body = rb;
3127
3128 if (r->headers_in.content_length_n <= 0) {
3129 upload_shutdown_ctx(u);
3130 return NGX_HTTP_BAD_REQUEST;
3131 }
3132
3133 /*
3134 * set by ngx_pcalloc():
3135 *
3136 * rb->bufs = NULL;
3137 * rb->buf = NULL;
3138 * rb->rest = 0;
3139 */
3140
3141 preread = r->header_in->last - r->header_in->pos;
3142
3143 if (preread) {
3144
3145 /* there is the pre-read part of the request body */
3146
3147 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
3148 "http client request body preread %uz", preread);
3149
3150 u->received = preread;
3151
3152 b = ngx_calloc_buf(r->pool);
3153 if (b == NULL) {
3154 upload_shutdown_ctx(u);
3155 return NGX_HTTP_INTERNAL_SERVER_ERROR;
3156 }
3157
3158 b->temporary = 1;
3159 b->start = r->header_in->pos;
3160 b->pos = r->header_in->pos;
3161 b->last = r->header_in->last;
3162 b->end = r->header_in->end;
3163
3164 rb->bufs = ngx_alloc_chain_link(r->pool);
3165 if (rb->bufs == NULL) {
3166 upload_shutdown_ctx(u);
3167 return NGX_HTTP_INTERNAL_SERVER_ERROR;
3168 }
3169
3170 rb->bufs->buf = b;
3171 rb->bufs->next = NULL;
3172 rb->buf = b;
3173
3174 if (preread >= r->headers_in.content_length_n) {
3175
3176 /* the whole request body was pre-read */
3177
3178 r->header_in->pos += r->headers_in.content_length_n;
3179 r->request_length += r->headers_in.content_length_n;
3180
3181 if (ngx_http_process_request_body(r, rb->bufs) != NGX_OK) {
3182 upload_shutdown_ctx(u);
3183 return NGX_HTTP_INTERNAL_SERVER_ERROR;
3184 }
3185
3186 upload_shutdown_ctx(u);
3187
3188 return ngx_http_upload_body_handler(r);
3189 }
3190
3191 /*
3192 * to not consider the body as pipelined request in
3193 * ngx_http_set_keepalive()
3194 */
3195 r->header_in->pos = r->header_in->last;
3196
3197 r->request_length += preread;
3198
3199 rb->rest = r->headers_in.content_length_n - preread;
3200
3201 if (rb->rest <= (off_t) (b->end - b->last)) {
3202
3203 /* the whole request body may be placed in r->header_in */
3204
3205 u->to_write = rb->bufs;
3206
3207 r->read_event_handler = ngx_http_read_upload_client_request_body_handler;
3208
3209 return ngx_http_do_read_upload_client_request_body(r);
3210 }
3211
3212 next = &rb->bufs->next;
3213
3214 } else {
3215 b = NULL;
3216 rb->rest = r->headers_in.content_length_n;
3217 next = &rb->bufs;
3218 }
3219
3220 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
3221
3222 size = clcf->client_body_buffer_size;
3223 size += size >> 2;
3224
3225 if (rb->rest < (ssize_t) size) {
3226 size = rb->rest;
3227
3228 if (r->request_body_in_single_buf) {
3229 size += preread;
3230 }
3231
3232 } else {
3233 size = clcf->client_body_buffer_size;
3234
3235 /* disable copying buffer for r->request_body_in_single_buf */
3236 b = NULL;
3237 }
3238
3239 rb->buf = ngx_create_temp_buf(r->pool, size);
3240 if (rb->buf == NULL) {
3241 upload_shutdown_ctx(u);
3242 return NGX_HTTP_INTERNAL_SERVER_ERROR;
3243 }
3244
3245 cl = ngx_alloc_chain_link(r->pool);
3246 if (cl == NULL) {
3247 upload_shutdown_ctx(u);
3248 return NGX_HTTP_INTERNAL_SERVER_ERROR;
3249 }
3250
3251 cl->buf = rb->buf;
3252 cl->next = NULL;
3253
3254 if (b && r->request_body_in_single_buf) {
3255 size = b->last - b->pos;
3256 ngx_memcpy(rb->buf->pos, b->pos, size);
3257 rb->buf->last += size;
3258
3259 next = &rb->bufs;
3260 }
3261
3262 *next = cl;
3263
3264 u->to_write = rb->bufs;
3265
3266 r->read_event_handler = ngx_http_read_upload_client_request_body_handler;
3267
3268 return ngx_http_do_read_upload_client_request_body(r);
3269 } /* }}} */
3270
3271 static void /* {{{ ngx_http_read_upload_client_request_body_handler */
ngx_http_read_upload_client_request_body_handler(ngx_http_request_t * r)3272 ngx_http_read_upload_client_request_body_handler(ngx_http_request_t *r)
3273 {
3274 ngx_int_t rc;
3275 ngx_http_upload_ctx_t *u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
3276 ngx_event_t *rev = r->connection->read;
3277 ngx_http_core_loc_conf_t *clcf;
3278
3279 if (rev->timedout) {
3280 if(!rev->delayed) {
3281 r->connection->timedout = 1;
3282 upload_shutdown_ctx(u);
3283 ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT);
3284 return;
3285 }
3286
3287 rev->timedout = 0;
3288 rev->delayed = 0;
3289
3290 if (!rev->ready) {
3291 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
3292 ngx_add_timer(rev, clcf->client_body_timeout);
3293
3294 if (ngx_handle_read_event(rev, clcf->send_lowat) != NGX_OK) {
3295 upload_shutdown_ctx(u);
3296 ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
3297 }
3298
3299 return;
3300 }
3301 }
3302 else{
3303 if (r->connection->read->delayed) {
3304 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
3305 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0,
3306 "http read delayed");
3307
3308 if (ngx_handle_read_event(rev, clcf->send_lowat) != NGX_OK) {
3309 upload_shutdown_ctx(u);
3310 ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR);
3311 }
3312
3313 return;
3314 }
3315 }
3316
3317 rc = ngx_http_do_read_upload_client_request_body(r);
3318
3319 if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
3320 upload_shutdown_ctx(u);
3321 ngx_http_finalize_request(r, rc);
3322 }
3323 } /* }}} */
3324
3325 static ngx_int_t /* {{{ ngx_http_do_read_upload_client_request_body */
ngx_http_do_read_upload_client_request_body(ngx_http_request_t * r)3326 ngx_http_do_read_upload_client_request_body(ngx_http_request_t *r)
3327 {
3328 ssize_t size, n, limit;
3329 ngx_connection_t *c;
3330 ngx_http_request_body_t *rb;
3331 ngx_http_upload_ctx_t *u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
3332 ngx_int_t rc;
3333 ngx_http_core_loc_conf_t *clcf;
3334 ngx_msec_t delay;
3335
3336 c = r->connection;
3337 rb = r->request_body;
3338
3339 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
3340 "http read client request body");
3341
3342 for ( ;; ) {
3343 for ( ;; ) {
3344 if (rb->buf->last == rb->buf->end) {
3345
3346 rc = ngx_http_process_request_body(r, u->to_write);
3347
3348 switch(rc) {
3349 case NGX_OK:
3350 break;
3351 case NGX_UPLOAD_MALFORMED:
3352 return NGX_HTTP_BAD_REQUEST;
3353 case NGX_UPLOAD_TOOLARGE:
3354 return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;
3355 case NGX_UPLOAD_IOERROR:
3356 return NGX_HTTP_SERVICE_UNAVAILABLE;
3357 case NGX_UPLOAD_NOMEM: case NGX_UPLOAD_SCRIPTERROR:
3358 default:
3359 return NGX_HTTP_INTERNAL_SERVER_ERROR;
3360 }
3361
3362 u->to_write = rb->bufs->next ? rb->bufs->next : rb->bufs;
3363 rb->buf->last = rb->buf->start;
3364 }
3365
3366 size = rb->buf->end - rb->buf->last;
3367
3368 if ((off_t)size > rb->rest) {
3369 size = (size_t)rb->rest;
3370 }
3371
3372 if (u->limit_rate) {
3373 limit = u->limit_rate * (ngx_time() - r->start_sec + 1) - u->received;
3374
3375 if (limit < 0) {
3376 c->read->delayed = 1;
3377 ngx_add_timer(c->read,
3378 (ngx_msec_t) (- limit * 1000 / u->limit_rate + 1));
3379
3380 return NGX_AGAIN;
3381 }
3382
3383 if(limit > 0 && size > limit) {
3384 size = limit;
3385 }
3386 }
3387
3388 n = c->recv(c, rb->buf->last, size);
3389
3390 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
3391 "http client request body recv %z", n);
3392
3393 if (n == NGX_AGAIN) {
3394 break;
3395 }
3396
3397 if (n == 0) {
3398 ngx_log_error(NGX_LOG_INFO, c->log, 0,
3399 "client closed prematurely connection");
3400 }
3401
3402 if (n == 0 || n == NGX_ERROR) {
3403 c->error = 1;
3404 return NGX_HTTP_BAD_REQUEST;
3405 }
3406
3407 rb->buf->last += n;
3408 rb->rest -= n;
3409 r->request_length += n;
3410 u->received += n;
3411
3412 if (rb->rest == 0) {
3413 break;
3414 }
3415
3416 if (rb->buf->last < rb->buf->end) {
3417 break;
3418 }
3419
3420 if (u->limit_rate) {
3421 delay = (ngx_msec_t) (n * 1000 / u->limit_rate + 1);
3422
3423 if (delay > 0) {
3424 c->read->delayed = 1;
3425 ngx_add_timer(c->read, delay);
3426 return NGX_AGAIN;
3427 }
3428 }
3429 }
3430
3431 ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
3432 "http client request body rest %uz", rb->rest);
3433
3434 if (rb->rest == 0) {
3435 break;
3436 }
3437
3438 if (!c->read->ready) {
3439 clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);
3440 ngx_add_timer(c->read, clcf->client_body_timeout);
3441
3442 if (ngx_handle_read_event(c->read, 0) != NGX_OK) {
3443 return NGX_HTTP_INTERNAL_SERVER_ERROR;
3444 }
3445
3446 return NGX_AGAIN;
3447 }
3448 }
3449
3450 if (c->read->timer_set) {
3451 ngx_del_timer(c->read);
3452 }
3453
3454 r->read_event_handler = ngx_http_block_reading;
3455
3456 rc = ngx_http_process_request_body(r, u->to_write);
3457
3458 switch(rc) {
3459 case NGX_OK:
3460 break;
3461 case NGX_UPLOAD_MALFORMED:
3462 return NGX_HTTP_BAD_REQUEST;
3463 case NGX_UPLOAD_TOOLARGE:
3464 return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;
3465 case NGX_UPLOAD_IOERROR:
3466 return NGX_HTTP_SERVICE_UNAVAILABLE;
3467 case NGX_UPLOAD_NOMEM: case NGX_UPLOAD_SCRIPTERROR:
3468 default:
3469 return NGX_HTTP_INTERNAL_SERVER_ERROR;
3470 }
3471
3472 upload_shutdown_ctx(u);
3473
3474 return ngx_http_upload_body_handler(r);
3475 } /* }}} */
3476
3477 static ngx_int_t /* {{{ ngx_http_process_request_body */
ngx_http_process_request_body(ngx_http_request_t * r,ngx_chain_t * body)3478 ngx_http_process_request_body(ngx_http_request_t *r, ngx_chain_t *body)
3479 {
3480 ngx_int_t rc;
3481 ngx_http_upload_ctx_t *u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
3482
3483 // Feed all the buffers into data handler
3484 while(body) {
3485 rc = u->data_handler(u, body->buf->pos, body->buf->last);
3486
3487 if(rc != NGX_OK)
3488 return rc;
3489
3490 body = body->next;
3491 }
3492
3493 if(u->raw_input) {
3494 // Signal end of body
3495 if(r->request_body->rest == 0) {
3496 rc = u->data_handler(u, NULL, NULL);
3497
3498 if(rc != NGX_OK)
3499 return rc;
3500 }
3501 }
3502
3503 return NGX_OK;
3504 } /* }}} */
3505
upload_parse_content_disposition(ngx_http_upload_ctx_t * upload_ctx,ngx_str_t * content_disposition)3506 static ngx_int_t upload_parse_content_disposition(ngx_http_upload_ctx_t *upload_ctx, ngx_str_t *content_disposition) { /* {{{ */
3507 char *filename_start, *filename_end;
3508 char *fieldname_start, *fieldname_end;
3509 char *p, *q;
3510
3511 p = (char*)content_disposition->data;
3512
3513 if(strncasecmp(FORM_DATA_STRING, p, sizeof(FORM_DATA_STRING)-1) &&
3514 strncasecmp(ATTACHMENT_STRING, p, sizeof(ATTACHMENT_STRING)-1)) {
3515 ngx_log_debug0(NGX_LOG_DEBUG_CORE, upload_ctx->log, 0,
3516 "Content-Disposition is not form-data or attachment");
3517 return NGX_UPLOAD_MALFORMED;
3518 }
3519
3520 filename_start = strstr(p, FILENAME_STRING);
3521
3522 if(filename_start != 0) {
3523
3524 filename_start += sizeof(FILENAME_STRING)-1;
3525
3526 filename_end = filename_start + strcspn(filename_start, "\"");
3527
3528 if(*filename_end != '\"') {
3529 ngx_log_debug0(NGX_LOG_DEBUG_CORE, upload_ctx->log, 0,
3530 "malformed filename in part header");
3531 return NGX_UPLOAD_MALFORMED;
3532 }
3533
3534 /*
3535 * IE sends full path, strip path from filename
3536 * Also strip all UNIX path references
3537 */
3538 for(q = filename_end-1; q > filename_start; q--)
3539 if(*q == '\\' || *q == '/') {
3540 filename_start = q+1;
3541 break;
3542 }
3543
3544 upload_ctx->file_name.len = filename_end - filename_start;
3545 upload_ctx->file_name.data = ngx_palloc(upload_ctx->request->pool, upload_ctx->file_name.len + 1);
3546
3547 if(upload_ctx->file_name.data == NULL)
3548 return NGX_UPLOAD_NOMEM;
3549
3550 strncpy((char*)upload_ctx->file_name.data, filename_start, filename_end - filename_start);
3551 }
3552
3553 fieldname_start = p;
3554
3555 // do{
3556 fieldname_start = strstr(fieldname_start, FIELDNAME_STRING);
3557 // }while((fieldname_start != 0) && (fieldname_start + sizeof(FIELDNAME_STRING) - 1 == filename_start));
3558
3559 if(fieldname_start != 0) {
3560 fieldname_start += sizeof(FIELDNAME_STRING)-1;
3561
3562 if(fieldname_start != filename_start) {
3563 fieldname_end = fieldname_start + strcspn(fieldname_start, "\"");
3564
3565 if(*fieldname_end != '\"') {
3566 ngx_log_error(NGX_LOG_ERR, upload_ctx->log, 0,
3567 "malformed fieldname in part header");
3568 return NGX_UPLOAD_MALFORMED;
3569 }
3570
3571 upload_ctx->field_name.len = fieldname_end - fieldname_start;
3572 upload_ctx->field_name.data = ngx_pcalloc(upload_ctx->request->pool, upload_ctx->field_name.len + 1);
3573
3574 if(upload_ctx->field_name.data == NULL)
3575 return NGX_UPLOAD_NOMEM;
3576
3577 strncpy((char*)upload_ctx->field_name.data, fieldname_start, fieldname_end - fieldname_start);
3578 }
3579 }
3580
3581 return NGX_OK;
3582 } /* }}} */
3583
upload_parse_part_header(ngx_http_upload_ctx_t * upload_ctx,char * header,char * header_end)3584 static ngx_int_t upload_parse_part_header(ngx_http_upload_ctx_t *upload_ctx, char *header, char *header_end) { /* {{{ */
3585 ngx_str_t s;
3586
3587 if(!strncasecmp(CONTENT_DISPOSITION_STRING, header, sizeof(CONTENT_DISPOSITION_STRING) - 1)) {
3588 char *p = header + sizeof(CONTENT_DISPOSITION_STRING) - 1;
3589
3590 p += strspn(p, " ");
3591
3592 s.data = (u_char*)p;
3593 s.len = header_end - p;
3594
3595 if(upload_parse_content_disposition(upload_ctx, &s) != NGX_OK) {
3596 ngx_log_debug0(NGX_LOG_DEBUG_CORE, upload_ctx->log, 0,
3597 "invalid Content-Disposition header");
3598 return NGX_UPLOAD_MALFORMED;
3599 }
3600 }
3601 else if(!strncasecmp(CONTENT_TYPE_STRING, header, sizeof(CONTENT_TYPE_STRING)-1)) {
3602 char *content_type_str = header + sizeof(CONTENT_TYPE_STRING)-1;
3603
3604 content_type_str += strspn(content_type_str, " ");
3605 upload_ctx->content_type.len = header_end - content_type_str;
3606
3607 if(upload_ctx->content_type.len == 0) {
3608 ngx_log_error(NGX_LOG_ERR, upload_ctx->log, 0,
3609 "empty Content-Type in part header");
3610 return NGX_UPLOAD_MALFORMED; // Empty Content-Type field
3611 }
3612
3613 upload_ctx->content_type.data = ngx_pcalloc(upload_ctx->request->pool, upload_ctx->content_type.len + 1);
3614
3615 if(upload_ctx->content_type.data == NULL)
3616 return NGX_UPLOAD_NOMEM; // Unable to allocate memory for string
3617
3618 strncpy((char*)upload_ctx->content_type.data, content_type_str, upload_ctx->content_type.len);
3619 }
3620
3621 return NGX_OK;
3622 } /* }}} */
3623
upload_discard_part_attributes(ngx_http_upload_ctx_t * upload_ctx)3624 static void upload_discard_part_attributes(ngx_http_upload_ctx_t *upload_ctx) { /* {{{ */
3625 upload_ctx->file_name.len = 0;
3626 upload_ctx->file_name.data = NULL;
3627
3628 upload_ctx->field_name.len = 0;
3629 upload_ctx->field_name.data = NULL;
3630
3631 upload_ctx->content_type.len = 0;
3632 upload_ctx->content_type.data = NULL;
3633
3634 upload_ctx->content_range.len = 0;
3635 upload_ctx->content_range.data = NULL;
3636
3637 upload_ctx->session_id.len = 0;
3638 upload_ctx->session_id.data = NULL;
3639
3640 upload_ctx->partial_content = 0;
3641 } /* }}} */
3642
upload_start_file(ngx_http_upload_ctx_t * upload_ctx)3643 static ngx_int_t upload_start_file(ngx_http_upload_ctx_t *upload_ctx) { /* {{{ */
3644 if(upload_ctx->start_part_f)
3645 return upload_ctx->start_part_f(upload_ctx);
3646 else
3647 return NGX_OK;
3648 } /* }}} */
3649
upload_finish_file(ngx_http_upload_ctx_t * upload_ctx)3650 static void upload_finish_file(ngx_http_upload_ctx_t *upload_ctx) { /* {{{ */
3651 // Call user-defined event handler
3652 if(upload_ctx->finish_part_f)
3653 upload_ctx->finish_part_f(upload_ctx);
3654
3655 upload_discard_part_attributes(upload_ctx);
3656
3657 upload_ctx->discard_data = 0;
3658 } /* }}} */
3659
upload_abort_file(ngx_http_upload_ctx_t * upload_ctx)3660 static void upload_abort_file(ngx_http_upload_ctx_t *upload_ctx) { /* {{{ */
3661 if(upload_ctx->abort_part_f)
3662 upload_ctx->abort_part_f(upload_ctx);
3663
3664 upload_discard_part_attributes(upload_ctx);
3665
3666 upload_ctx->discard_data = 0;
3667 } /* }}} */
3668
upload_flush_output_buffer(ngx_http_upload_ctx_t * upload_ctx)3669 static void upload_flush_output_buffer(ngx_http_upload_ctx_t *upload_ctx) { /* {{{ */
3670 if(upload_ctx->output_buffer_pos > upload_ctx->output_buffer) {
3671 if(upload_ctx->flush_output_buffer_f)
3672 if(upload_ctx->flush_output_buffer_f(upload_ctx, (void*)upload_ctx->output_buffer,
3673 (size_t)(upload_ctx->output_buffer_pos - upload_ctx->output_buffer)) != NGX_OK)
3674 upload_ctx->discard_data = 1;
3675
3676 upload_ctx->output_buffer_pos = upload_ctx->output_buffer;
3677 }
3678 } /* }}} */
3679
upload_init_ctx(ngx_http_upload_ctx_t * upload_ctx)3680 static void upload_init_ctx(ngx_http_upload_ctx_t *upload_ctx) { /* {{{ */
3681 upload_ctx->boundary.data = upload_ctx->boundary_start = upload_ctx->boundary_pos = 0;
3682
3683 upload_ctx->state = upload_state_boundary_seek;
3684
3685 upload_discard_part_attributes(upload_ctx);
3686
3687 upload_ctx->discard_data = 0;
3688
3689 upload_ctx->start_part_f = ngx_http_upload_start_handler;
3690 upload_ctx->finish_part_f = ngx_http_upload_finish_handler;
3691 upload_ctx->abort_part_f = ngx_http_upload_abort_handler;
3692 upload_ctx->flush_output_buffer_f = ngx_http_upload_flush_output_buffer;
3693
3694 upload_ctx->started = 0;
3695 upload_ctx->unencoded = 0;
3696 /*
3697 * Set default data handler
3698 */
3699 upload_ctx->data_handler = upload_process_buf;
3700 } /* }}} */
3701
upload_shutdown_ctx(ngx_http_upload_ctx_t * upload_ctx)3702 static void upload_shutdown_ctx(ngx_http_upload_ctx_t *upload_ctx) { /* {{{ */
3703 if(upload_ctx != 0) {
3704 // Abort file if we still processing it
3705 if(upload_ctx->state == upload_state_data) {
3706 upload_flush_output_buffer(upload_ctx);
3707 upload_abort_file(upload_ctx);
3708 }
3709
3710 upload_discard_part_attributes(upload_ctx);
3711 }
3712 } /* }}} */
3713
upload_start(ngx_http_upload_ctx_t * upload_ctx,ngx_http_upload_loc_conf_t * ulcf)3714 static ngx_int_t upload_start(ngx_http_upload_ctx_t *upload_ctx, ngx_http_upload_loc_conf_t *ulcf) { /* {{{ */
3715 if(upload_ctx == NULL)
3716 return NGX_ERROR;
3717
3718 upload_ctx->header_accumulator = ngx_pcalloc(upload_ctx->request->pool, ulcf->max_header_len + 1);
3719
3720 if(upload_ctx->header_accumulator == NULL)
3721 return NGX_ERROR;
3722
3723 upload_ctx->header_accumulator_pos = upload_ctx->header_accumulator;
3724 upload_ctx->header_accumulator_end = upload_ctx->header_accumulator + ulcf->max_header_len;
3725
3726 upload_ctx->output_buffer = ngx_pcalloc(upload_ctx->request->pool, ulcf->buffer_size);
3727
3728 if(upload_ctx->output_buffer == NULL)
3729 return NGX_ERROR;
3730
3731 upload_ctx->output_buffer_pos = upload_ctx->output_buffer;
3732 upload_ctx->output_buffer_end = upload_ctx->output_buffer + ulcf->buffer_size;
3733
3734 upload_ctx->header_accumulator_pos = upload_ctx->header_accumulator;
3735
3736 upload_ctx->range_header_buffer = ngx_pcalloc(upload_ctx->request->pool, ulcf->range_header_buffer_size);
3737
3738 if(upload_ctx->range_header_buffer == NULL)
3739 return NGX_ERROR;
3740
3741 upload_ctx->range_header_buffer_pos = upload_ctx->range_header_buffer;
3742 upload_ctx->range_header_buffer_end = upload_ctx->range_header_buffer + ulcf->range_header_buffer_size;
3743
3744 upload_ctx->first_part = 1;
3745
3746 return NGX_OK;
3747 } /* }}} */
3748
3749 static ngx_int_t /* {{{ ngx_http_upload_validate_session_id */
ngx_http_upload_validate_session_id(ngx_str_t * session_id)3750 ngx_http_upload_validate_session_id(ngx_str_t *session_id) {
3751 u_char *p, *q;
3752
3753 p = session_id->data;
3754 q = session_id->data + session_id->len;
3755
3756 while(p != q) {
3757 if(!((*p >= '0' && *p <= '9') || (*p >= 'a' && *p <= 'z') || (*p >= 'A' && *p <= 'Z')
3758 || *p == '_' || *p == '-'))
3759 {
3760 return NGX_ERROR;
3761 }
3762
3763 p++;
3764 }
3765
3766 return NGX_OK;
3767 }
3768
upload_parse_request_headers(ngx_http_upload_ctx_t * upload_ctx,ngx_http_headers_in_t * headers_in)3769 static ngx_int_t upload_parse_request_headers(ngx_http_upload_ctx_t *upload_ctx, ngx_http_headers_in_t *headers_in) { /* {{{ */
3770 ngx_str_t *content_type, s;
3771 ngx_list_part_t *part;
3772 ngx_table_elt_t *header;
3773 ngx_uint_t i;
3774 u_char *mime_type_end_ptr;
3775 u_char *boundary_start_ptr, *boundary_end_ptr;
3776 ngx_atomic_uint_t boundary;
3777 ngx_http_upload_loc_conf_t *ulcf;
3778
3779 ulcf = ngx_http_get_module_loc_conf(upload_ctx->request, ngx_http_upload_module);
3780
3781 // Check whether Content-Type header is missing
3782 if(headers_in->content_type == NULL) {
3783 ngx_log_error(NGX_LOG_ERR, upload_ctx->log, ngx_errno,
3784 "missing Content-Type header");
3785 return NGX_HTTP_BAD_REQUEST;
3786 }
3787
3788 content_type = &headers_in->content_type->value;
3789
3790 if(ngx_strncasecmp(content_type->data, (u_char*) MULTIPART_FORM_DATA_STRING,
3791 sizeof(MULTIPART_FORM_DATA_STRING) - 1)) {
3792
3793 if(!ulcf->resumable_uploads) {
3794 ngx_log_error(NGX_LOG_ERR, upload_ctx->log, 0,
3795 "Content-Type is not multipart/form-data and resumable uploads are off: %V", content_type);
3796 return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
3797 }
3798 /*
3799 * Content-Type is not multipart/form-data,
3800 * look for Content-Disposition header now
3801 */
3802 part = &headers_in->headers.part;
3803 header = part->elts;
3804
3805 for (i = 0;;i++) {
3806 if (i >= part->nelts) {
3807 if (part->next == NULL) {
3808 break;
3809 }
3810
3811 part = part->next;
3812 header = part->elts;
3813 i = 0;
3814 }
3815
3816 if(!strncasecmp(CONTENT_DISPOSITION_STRING, (char*)header[i].key.data, sizeof(CONTENT_DISPOSITION_STRING) - 1 - 1)) {
3817 if(upload_parse_content_disposition(upload_ctx, &header[i].value)) {
3818 ngx_log_error(NGX_LOG_INFO, upload_ctx->log, 0,
3819 "invalid Content-Disposition header");
3820 return NGX_ERROR;
3821 }
3822
3823 upload_ctx->is_file = 1;
3824 upload_ctx->unencoded = 1;
3825 upload_ctx->raw_input = 1;
3826
3827 upload_ctx->data_handler = upload_process_raw_buf;
3828 }else if(!strncasecmp(SESSION_ID_STRING, (char*)header[i].key.data, sizeof(SESSION_ID_STRING) - 1 - 1)
3829 || !strncasecmp(X_SESSION_ID_STRING, (char*)header[i].key.data, sizeof(X_SESSION_ID_STRING) - 1 - 1))
3830 {
3831 if(header[i].value.len == 0) {
3832 ngx_log_debug0(NGX_LOG_DEBUG_CORE, upload_ctx->log, 0,
3833 "empty Session-ID in header");
3834 return NGX_ERROR;
3835 }
3836
3837 if(ngx_http_upload_validate_session_id(&header[i].value) != NGX_OK) {
3838 ngx_log_debug0(NGX_LOG_DEBUG_CORE, upload_ctx->log, 0,
3839 "invalid Session-ID in header");
3840 return NGX_ERROR;
3841 }
3842
3843 upload_ctx->session_id = header[i].value;
3844
3845 ngx_log_debug1(NGX_LOG_DEBUG_CORE, upload_ctx->log, 0,
3846 "session id %V", &upload_ctx->session_id);
3847 }else if(!strncasecmp(CONTENT_RANGE_STRING, (char*)header[i].key.data, sizeof(CONTENT_RANGE_STRING) - 1 - 1)
3848 || !strncasecmp(X_CONTENT_RANGE_STRING, (char*)header[i].key.data, sizeof(X_CONTENT_RANGE_STRING) - 1 - 1))
3849 {
3850 if(header[i].value.len == 0) {
3851 ngx_log_debug0(NGX_LOG_DEBUG_CORE, upload_ctx->log, 0,
3852 "empty Content-Range in part header");
3853 return NGX_ERROR;
3854 }
3855
3856 if(strncasecmp((char*)header[i].value.data, BYTES_UNIT_STRING, sizeof(BYTES_UNIT_STRING) - 1)) {
3857 ngx_log_debug0(NGX_LOG_DEBUG_CORE, upload_ctx->log, 0,
3858 "unsupported range unit");
3859 return NGX_ERROR;
3860 }
3861
3862 s.data = (u_char*)(char*)header[i].value.data + sizeof(BYTES_UNIT_STRING) - 1;
3863 s.len = header[i].value.len - sizeof(BYTES_UNIT_STRING) + 1;
3864
3865 if(ngx_http_upload_parse_range(&s, &upload_ctx->content_range_n) != NGX_OK) {
3866 ngx_log_debug2(NGX_LOG_DEBUG_CORE, upload_ctx->log, 0,
3867 "invalid range %V (%V)", &s, &header[i].value);
3868 return NGX_ERROR;
3869 }
3870
3871 ngx_log_debug3(NGX_LOG_DEBUG_CORE, upload_ctx->log, 0,
3872 "partial content, range %O-%O/%O", upload_ctx->content_range_n.start,
3873 upload_ctx->content_range_n.end, upload_ctx->content_range_n.total);
3874
3875 if(ulcf->max_file_size != 0 && upload_ctx->content_range_n.total > ulcf->max_file_size) {
3876 ngx_log_error(NGX_LOG_ERR, upload_ctx->log, 0,
3877 "entity length is too big");
3878 return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;
3879 }
3880
3881 if( (upload_ctx->content_range_n.end - upload_ctx->content_range_n.start + 1)
3882 != headers_in->content_length_n)
3883 {
3884 ngx_log_error(NGX_LOG_ERR, upload_ctx->log, 0,
3885 "range length is not equal to content length");
3886 return NGX_HTTP_RANGE_NOT_SATISFIABLE;
3887 }
3888
3889 upload_ctx->partial_content = 1;
3890 }
3891 }
3892
3893 if(!upload_ctx->unencoded) {
3894 ngx_log_error(NGX_LOG_ERR, upload_ctx->log, 0,
3895 "Content-Type is not multipart/form-data and no Content-Disposition header found");
3896 return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE;
3897 }
3898
3899 upload_ctx->content_type = *content_type;
3900
3901 boundary = ngx_next_temp_number(0);
3902
3903 content_type->data =
3904 ngx_pnalloc(upload_ctx->request->pool,
3905 sizeof(MULTIPART_FORM_DATA_STRING "; boundary=") - 1
3906 + NGX_ATOMIC_T_LEN);
3907
3908 if (content_type->data == NULL) {
3909 return NGX_ERROR;
3910 }
3911
3912 content_type->len =
3913 ngx_sprintf(content_type->data,
3914 MULTIPART_FORM_DATA_STRING "; boundary=%0muA",
3915 boundary)
3916 - content_type->data;
3917
3918 boundary_start_ptr = content_type->data + sizeof(MULTIPART_FORM_DATA_STRING "; boundary=") - 1;
3919 boundary_end_ptr = content_type->data + content_type->len;
3920 }
3921 else{
3922 // Find colon in content type string, which terminates mime type
3923 mime_type_end_ptr = (u_char*) ngx_strchr(content_type->data, ';');
3924
3925 upload_ctx->boundary.data = 0;
3926
3927 if(mime_type_end_ptr == NULL) {
3928 ngx_log_debug0(NGX_LOG_DEBUG_CORE, upload_ctx->log, 0,
3929 "no boundary found in Content-Type");
3930 return NGX_UPLOAD_MALFORMED;
3931 }
3932
3933 boundary_start_ptr = ngx_strstrn(mime_type_end_ptr, BOUNDARY_STRING, sizeof(BOUNDARY_STRING) - 2);
3934
3935 if(boundary_start_ptr == NULL) {
3936 ngx_log_debug0(NGX_LOG_DEBUG_CORE, upload_ctx->log, 0,
3937 "no boundary found in Content-Type");
3938 return NGX_UPLOAD_MALFORMED; // No boundary found
3939 }
3940
3941 boundary_start_ptr += sizeof(BOUNDARY_STRING) - 1;
3942 boundary_end_ptr = boundary_start_ptr + strcspn((char*)boundary_start_ptr, " ;\n\r");
3943
3944 if(boundary_end_ptr == boundary_start_ptr) {
3945 ngx_log_debug0(NGX_LOG_DEBUG_CORE, upload_ctx->log, 0,
3946 "boundary is empty");
3947 return NGX_UPLOAD_MALFORMED;
3948 }
3949 }
3950
3951 // Allocate memory for entire boundary plus \r\n-- plus terminating character
3952 upload_ctx->boundary.len = boundary_end_ptr - boundary_start_ptr + 4;
3953 upload_ctx->boundary.data = ngx_palloc(upload_ctx->request->pool, upload_ctx->boundary.len + 1);
3954
3955 if(upload_ctx->boundary.data == NULL)
3956 return NGX_HTTP_INTERNAL_SERVER_ERROR;
3957
3958 ngx_cpystrn(upload_ctx->boundary.data + 4, boundary_start_ptr,
3959 boundary_end_ptr - boundary_start_ptr + 1);
3960
3961 // Prepend boundary data by \r\n--
3962 upload_ctx->boundary.data[0] = '\r';
3963 upload_ctx->boundary.data[1] = '\n';
3964 upload_ctx->boundary.data[2] = '-';
3965 upload_ctx->boundary.data[3] = '-';
3966
3967 /*
3968 * NOTE: first boundary doesn't start with \r\n. Here we
3969 * advance 2 positions forward. We will return 2 positions back
3970 * later
3971 */
3972 upload_ctx->boundary_start = upload_ctx->boundary.data + 2;
3973 upload_ctx->boundary_pos = upload_ctx->boundary_start;
3974
3975 return NGX_OK;
3976 } /* }}} */
3977
3978 static ngx_int_t /* {{{ ngx_http_upload_parse_range */
ngx_http_upload_parse_range(ngx_str_t * range,ngx_http_upload_range_t * range_n)3979 ngx_http_upload_parse_range(ngx_str_t *range, ngx_http_upload_range_t *range_n)
3980 {
3981 u_char *p = range->data;
3982 u_char *last = range->data + range->len;
3983 off_t *field = &range_n->start;
3984
3985 if(range_n == NULL)
3986 return NGX_ERROR;
3987
3988 do{
3989 *field = 0;
3990
3991 while(p < last) {
3992
3993 if(*p >= '0' && *p <= '9') {
3994 (*field) = (*field) * 10 + (*p - '0');
3995 }
3996 else if(*p == '-') {
3997 if(field != &range_n->start) {
3998 return NGX_ERROR;
3999 }
4000
4001 field = &range_n->end;
4002 p++;
4003 break;
4004 }
4005 else if(*p == '/') {
4006 if(field != &range_n->end) {
4007 return NGX_ERROR;
4008 }
4009
4010 field = &range_n->total;
4011 p++;
4012 break;
4013 }
4014 else {
4015 return NGX_ERROR;
4016 }
4017
4018 p++;
4019 }
4020 }while(p < last);
4021
4022 if(field != &range_n->total) {
4023 return NGX_ERROR;
4024 }
4025
4026 if(range_n->start > range_n->end || range_n->start >= range_n->total
4027 || range_n->end >= range_n->total)
4028 {
4029 return NGX_ERROR;
4030 }
4031
4032 return NGX_OK;
4033 } /* }}} */
4034
upload_putc(ngx_http_upload_ctx_t * upload_ctx,u_char c)4035 static void upload_putc(ngx_http_upload_ctx_t *upload_ctx, u_char c) { /* {{{ */
4036 if(!upload_ctx->discard_data) {
4037 *upload_ctx->output_buffer_pos = c;
4038
4039 upload_ctx->output_buffer_pos++;
4040
4041 if(upload_ctx->output_buffer_pos == upload_ctx->output_buffer_end)
4042 upload_flush_output_buffer(upload_ctx);
4043 }
4044 } /* }}} */
4045
upload_process_buf(ngx_http_upload_ctx_t * upload_ctx,u_char * start,u_char * end)4046 static ngx_int_t upload_process_buf(ngx_http_upload_ctx_t *upload_ctx, u_char *start, u_char *end) { /* {{{ */
4047
4048 u_char *p;
4049 ngx_int_t rc;
4050
4051 // No more data?
4052 if(start == end) {
4053 if(upload_ctx->state != upload_state_finish) {
4054 ngx_log_error(NGX_LOG_ERR, upload_ctx->log, 0, "premature end of body");
4055 return NGX_UPLOAD_MALFORMED; // Signal error if still haven't finished
4056 }
4057 else
4058 return NGX_OK; // Otherwise confirm end of stream
4059 }
4060
4061 for(p = start; p != end; p++) {
4062 switch(upload_ctx->state) {
4063 /*
4064 * Seek the boundary
4065 */
4066 case upload_state_boundary_seek:
4067 if(*p == *upload_ctx->boundary_pos)
4068 upload_ctx->boundary_pos++;
4069 else
4070 upload_ctx->boundary_pos = upload_ctx->boundary_start;
4071
4072 if(upload_ctx->boundary_pos == upload_ctx->boundary.data + upload_ctx->boundary.len) {
4073 upload_ctx->state = upload_state_after_boundary;
4074 upload_ctx->boundary_start = upload_ctx->boundary.data;
4075 upload_ctx->boundary_pos = upload_ctx->boundary_start;
4076 }
4077 break;
4078 case upload_state_after_boundary:
4079 switch(*p) {
4080 case '\n':
4081 upload_ctx->state = upload_state_headers;
4082 upload_ctx->header_accumulator_pos = upload_ctx->header_accumulator;
4083 case '\r':
4084 break;
4085 case '-':
4086 upload_ctx->state = upload_state_finish;
4087 break;
4088 }
4089 break;
4090 /*
4091 * Collect and store headers
4092 */
4093 case upload_state_headers:
4094 switch(*p) {
4095 case '\n':
4096 if(upload_ctx->header_accumulator_pos == upload_ctx->header_accumulator) {
4097 upload_ctx->is_file = (upload_ctx->file_name.data == 0) || (upload_ctx->file_name.len == 0) ? 0 : 1;
4098
4099 rc = upload_start_file(upload_ctx);
4100
4101 if(rc != NGX_OK) {
4102 upload_ctx->state = upload_state_finish;
4103 return rc; // User requested to cancel processing
4104 } else {
4105 upload_ctx->state = upload_state_data;
4106 upload_ctx->output_buffer_pos = upload_ctx->output_buffer;
4107 }
4108 } else {
4109 *upload_ctx->header_accumulator_pos = '\0';
4110
4111 rc = upload_parse_part_header(upload_ctx, (char*)upload_ctx->header_accumulator,
4112 (char*)upload_ctx->header_accumulator_pos);
4113
4114 if(rc != NGX_OK) {
4115 upload_ctx->state = upload_state_finish;
4116 return rc; // Malformed header
4117 } else
4118 upload_ctx->header_accumulator_pos = upload_ctx->header_accumulator;
4119 }
4120 case '\r':
4121 break;
4122 default:
4123 if(upload_ctx->header_accumulator_pos < upload_ctx->header_accumulator_end - 1)
4124 *upload_ctx->header_accumulator_pos++ = *p;
4125 else {
4126 ngx_log_error(NGX_LOG_ERR, upload_ctx->log, 0, "part header is too long");
4127
4128 upload_ctx->state = upload_state_finish;
4129 return NGX_UPLOAD_MALFORMED;
4130 }
4131 break;
4132 }
4133 break;
4134 /*
4135 * Search for separating or terminating boundary
4136 * and output data simultaneously
4137 */
4138 case upload_state_data:
4139 if(*p == *upload_ctx->boundary_pos)
4140 upload_ctx->boundary_pos++;
4141 else {
4142 if(upload_ctx->boundary_pos == upload_ctx->boundary_start) {
4143 // IE 5.0 bug workaround
4144 if(*p == '\n') {
4145 /*
4146 * Set current matched position beyond LF and prevent outputting
4147 * CR in case of unsuccessful match by altering boundary_start
4148 */
4149 upload_ctx->boundary_pos = upload_ctx->boundary.data + 2;
4150 upload_ctx->boundary_start = upload_ctx->boundary.data + 1;
4151 } else
4152 upload_putc(upload_ctx, *p);
4153 } else {
4154 // Output partially matched lump of boundary
4155 u_char *q;
4156 for(q = upload_ctx->boundary_start; q != upload_ctx->boundary_pos; q++)
4157 upload_putc(upload_ctx, *q);
4158
4159 p--; // Repeat reading last character
4160
4161 // And reset matched position
4162 upload_ctx->boundary_start = upload_ctx->boundary.data;
4163 upload_ctx->boundary_pos = upload_ctx->boundary_start;
4164 }
4165 }
4166
4167 if(upload_ctx->boundary_pos == upload_ctx->boundary.data + upload_ctx->boundary.len) {
4168 upload_ctx->state = upload_state_after_boundary;
4169 upload_ctx->boundary_pos = upload_ctx->boundary_start;
4170
4171 upload_flush_output_buffer(upload_ctx);
4172 if(!upload_ctx->discard_data)
4173 upload_finish_file(upload_ctx);
4174 else
4175 upload_abort_file(upload_ctx);
4176 }
4177 break;
4178 /*
4179 * Skip trailing garbage
4180 */
4181 case upload_state_finish:
4182 break;
4183 }
4184 }
4185
4186 return NGX_OK;
4187 } /* }}} */
4188
4189 static ngx_int_t
upload_process_raw_buf(ngx_http_upload_ctx_t * upload_ctx,u_char * start,u_char * end)4190 upload_process_raw_buf(ngx_http_upload_ctx_t *upload_ctx, u_char *start, u_char *end) { /* {{{ */
4191 ngx_int_t rc;
4192
4193 if(start == end) {
4194 if(!upload_ctx->discard_data)
4195 upload_finish_file(upload_ctx);
4196 else
4197 upload_abort_file(upload_ctx);
4198 return NGX_OK;
4199 }
4200
4201 if(!upload_ctx->started) {
4202 rc = upload_start_file(upload_ctx);
4203
4204 if(rc != NGX_OK) {
4205 return rc;
4206 }
4207
4208 upload_ctx->started = 1;
4209 }
4210
4211 if(upload_ctx->flush_output_buffer_f)
4212 if(upload_ctx->flush_output_buffer_f(upload_ctx, (void*)start,
4213 (size_t)(end - start)) != NGX_OK)
4214 upload_ctx->discard_data = 1;
4215
4216 return NGX_OK;
4217
4218 } /* }}} */
4219
4220 static void /* {{{ ngx_upload_cleanup_handler */
ngx_upload_cleanup_handler(void * data)4221 ngx_upload_cleanup_handler(void *data)
4222 {
4223 ngx_upload_cleanup_t *cln = data;
4224 ngx_uint_t i;
4225 uint16_t *s;
4226 u_char do_cleanup = 0;
4227
4228 if(!cln->aborted) {
4229 if(cln->fd >= 0) {
4230 if (ngx_close_file(cln->fd) == NGX_FILE_ERROR) {
4231 ngx_log_error(NGX_LOG_ALERT, cln->log, ngx_errno,
4232 ngx_close_file_n " \"%s\" failed", cln->filename);
4233 }
4234 }
4235
4236 if(cln->cleanup_statuses != NULL) {
4237 s = cln->cleanup_statuses->elts;
4238
4239 for(i = 0; i < cln->cleanup_statuses->nelts; i++) {
4240 if(cln->headers_out->status == s[i]) {
4241 do_cleanup = 1;
4242 }
4243 }
4244 }
4245
4246 if(do_cleanup) {
4247 if(ngx_delete_file(cln->filename) == NGX_FILE_ERROR) {
4248 ngx_log_error(NGX_LOG_ERR, cln->log, ngx_errno
4249 , "failed to remove destination file \"%s\" after http status %l"
4250 , cln->filename
4251 , cln->headers_out->status
4252 );
4253 }else
4254 ngx_log_error(NGX_LOG_INFO, cln->log, 0
4255 , "finished cleanup of file \"%s\" after http status %l"
4256 , cln->filename
4257 , cln->headers_out->status
4258 );
4259 }
4260 }
4261 } /* }}} */
4262
4263 static ngx_int_t /* {{{ */
ngx_http_upload_test_expect(ngx_http_request_t * r)4264 ngx_http_upload_test_expect(ngx_http_request_t *r)
4265 {
4266 ngx_int_t n;
4267 ngx_str_t *expect;
4268
4269 if (r->expect_tested
4270 || r->headers_in.expect == NULL
4271 || r->http_version < NGX_HTTP_VERSION_11)
4272 {
4273 return NGX_OK;
4274 }
4275
4276 r->expect_tested = 1;
4277
4278 expect = &r->headers_in.expect->value;
4279
4280 if (expect->len != sizeof("100-continue") - 1
4281 || ngx_strncasecmp(expect->data, (u_char *) "100-continue",
4282 sizeof("100-continue") - 1)
4283 != 0)
4284 {
4285 return NGX_OK;
4286 }
4287
4288 ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
4289 "send 100 Continue");
4290
4291 n = r->connection->send(r->connection,
4292 (u_char *) "HTTP/1.1 100 Continue" CRLF CRLF,
4293 sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1);
4294
4295 if (n == sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1) {
4296 return NGX_OK;
4297 }
4298
4299 /* we assume that such small packet should be send successfully */
4300
4301 return NGX_ERROR;
4302 } /* }}} */
4303