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