aboutsummaryrefslogtreecommitdiffstats
path: root/www
diff options
context:
space:
mode:
authorosa <osa@FreeBSD.org>2013-09-11 23:00:04 +0800
committerosa <osa@FreeBSD.org>2013-09-11 23:00:04 +0800
commit4367cea8aac6a761af344c1c99196d6e785501e8 (patch)
tree61f219fcbf0b69d6501d91f129fc60c1be2cb0c8 /www
parent884c6b080b71fc6d76b5adca5e90382336f1b05d (diff)
downloadfreebsd-ports-gnome-4367cea8aac6a761af344c1c99196d6e785501e8.tar.gz
freebsd-ports-gnome-4367cea8aac6a761af344c1c99196d6e785501e8.tar.zst
freebsd-ports-gnome-4367cea8aac6a761af344c1c99196d6e785501e8.zip
Update patch for third-party http_upload module.
PR: 182010
Diffstat (limited to 'www')
-rw-r--r--www/nginx-devel/files/extra-patch-ngx_http_upload_module.c1082
-rw-r--r--www/nginx/files/extra-patch-ngx_http_upload_module.c1082
2 files changed, 2136 insertions, 28 deletions
diff --git a/www/nginx-devel/files/extra-patch-ngx_http_upload_module.c b/www/nginx-devel/files/extra-patch-ngx_http_upload_module.c
index e72932bdedee..d626021fa649 100644
--- a/www/nginx-devel/files/extra-patch-ngx_http_upload_module.c
+++ b/www/nginx-devel/files/extra-patch-ngx_http_upload_module.c
@@ -1,9 +1,106 @@
---- ../nginx_upload_module-2.2.0/ngx_http_upload_module.c.orig 2010-09-27 20:54:15.000000000 +0200
-+++ ../nginx_upload_module-2.2.0/ngx_http_upload_module.c 2013-02-21 22:32:08.000000000 +0100
-@@ -233,6 +233,17 @@
+--- ../nginx_upload_module-2.2.0/ngx_http_upload_module.c.orig 2010-09-27 21:54:15.000000000 +0300
++++ ../nginx_upload_module-2.2.0/ngx_http_upload_module.c 2013-09-10 17:40:59.570815847 +0300
+@@ -50,7 +50,7 @@
+ * State of multipart/form-data parser
+ */
+ typedef enum {
+- upload_state_boundary_seek,
++ upload_state_boundary_seek,
+ upload_state_after_boundary,
+ upload_state_headers,
+ upload_state_data,
+@@ -95,6 +95,14 @@
+ } ngx_http_upload_field_template_t;
+
+ /*
++ * Template for a header
++ */
++typedef struct {
++ ngx_http_complex_value_t *name;
++ ngx_http_complex_value_t *value;
++} ngx_http_upload_header_template_t;
++
++/*
+ * Filter for fields in output form
+ */
+ typedef struct {
+@@ -106,6 +114,12 @@
+ #endif
+ } ngx_http_upload_field_filter_t;
+
++typedef struct {
++ ngx_path_t *path;
++ ngx_http_complex_value_t dynamic;
++ unsigned is_dynamic:1;
++} ngx_http_upload_path_t;
++
+ /*
+ * Upload cleanup record
+ */
+@@ -124,8 +138,8 @@
+ typedef struct {
+ ngx_str_t url;
+ ngx_http_complex_value_t *url_cv;
+- ngx_path_t *state_store_path;
+- ngx_path_t *store_path;
++ ngx_http_upload_path_t *state_store_path;
++ ngx_http_upload_path_t *store_path;
+ ngx_uint_t store_access;
+ size_t buffer_size;
+ size_t merge_buffer_size;
+@@ -137,13 +151,17 @@
+ ngx_array_t *aggregate_field_templates;
+ ngx_array_t *field_filters;
+ ngx_array_t *cleanup_statuses;
++ ngx_array_t *header_templates;
+ ngx_flag_t forward_args;
+ ngx_flag_t tame_arrays;
+ ngx_flag_t resumable_uploads;
++ ngx_flag_t empty_field_names;
+ size_t limit_rate;
+
+ unsigned int md5:1;
+ unsigned int sha1:1;
++ unsigned int sha256:1;
++ unsigned int sha512:1;
+ unsigned int crc32:1;
+ } ngx_http_upload_loc_conf_t;
+
+@@ -157,6 +175,16 @@
+ u_char sha1_digest[SHA_DIGEST_LENGTH * 2];
+ } ngx_http_upload_sha1_ctx_t;
+
++typedef struct ngx_http_upload_sha256_ctx_s {
++ SHA256_CTX sha256;
++ u_char sha256_digest[SHA256_DIGEST_LENGTH * 2];
++} ngx_http_upload_sha256_ctx_t;
++
++typedef struct ngx_http_upload_sha512_ctx_s {
++ SHA512_CTX sha512;
++ u_char sha512_digest[SHA512_DIGEST_LENGTH * 2];
++} ngx_http_upload_sha512_ctx_t;
++
+ struct ngx_http_upload_ctx_s;
+
+ /*
+@@ -219,7 +247,11 @@
+
+ ngx_http_upload_md5_ctx_t *md5_ctx;
+ ngx_http_upload_sha1_ctx_t *sha1_ctx;
++ ngx_http_upload_sha256_ctx_t *sha256_ctx;
++ ngx_http_upload_sha512_ctx_t *sha512_ctx;
+ uint32_t crc32;
++ ngx_path_t *store_path;
++ ngx_path_t *state_store_path;
+
+ unsigned int first_part:1;
+ unsigned int discard_data:1;
+@@ -233,7 +265,21 @@
unsigned int raw_input:1;
} ngx_http_upload_ctx_t;
++static ngx_int_t ngx_http_upload_test_expect(ngx_http_request_t *r);
++
+static void ngx_http_read_client_request_body_handler(ngx_http_request_t *r);
+static ngx_int_t ngx_http_do_read_client_request_body(ngx_http_request_t *r);
+
@@ -16,12 +113,896 @@
+static ngx_int_t ngx_http_request_body_save_filter(ngx_http_request_t *r, ngx_chain_t *in);
+
static ngx_int_t ngx_http_upload_handler(ngx_http_request_t *r);
++static ngx_int_t ngx_http_upload_options_handler(ngx_http_request_t *r);
static ngx_int_t ngx_http_upload_body_handler(ngx_http_request_t *r);
-@@ -2523,6 +2534,534 @@
+ static void *ngx_http_upload_create_loc_conf(ngx_conf_t *cf);
+@@ -248,6 +294,10 @@
+ ngx_http_variable_value_t *v, uintptr_t data);
+ static ngx_int_t ngx_http_upload_sha1_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
++static ngx_int_t ngx_http_upload_sha256_variable(ngx_http_request_t *r,
++ ngx_http_variable_value_t *v, uintptr_t data);
++static ngx_int_t ngx_http_upload_sha512_variable(ngx_http_request_t *r,
++ ngx_http_variable_value_t *v, uintptr_t data);
+ static ngx_int_t ngx_http_upload_file_size_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+ static void ngx_http_upload_content_range_variable_set(ngx_http_request_t *r,
+@@ -271,6 +321,7 @@
+ static ngx_int_t ngx_http_upload_merge_ranges(ngx_http_upload_ctx_t *u, ngx_http_upload_range_t *range_n);
+ static ngx_int_t ngx_http_upload_parse_range(ngx_str_t *range, ngx_http_upload_range_t *range_n);
+
++
+ static void ngx_http_read_upload_client_request_body_handler(ngx_http_request_t *r);
+ static ngx_int_t ngx_http_do_read_upload_client_request_body(ngx_http_request_t *r);
+ static ngx_int_t ngx_http_process_request_body(ngx_http_request_t *r, ngx_chain_t *body);
+@@ -279,8 +330,16 @@
+
+ static char *ngx_http_upload_set_form_field(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
++static char *ngx_http_upload_add_header(ngx_conf_t *cf, ngx_command_t *cmd,
++ void *conf);
++static ngx_int_t ngx_http_upload_eval_path(ngx_http_request_t *r);
++static ngx_int_t ngx_http_upload_eval_state_path(ngx_http_request_t *r);
+ static char *ngx_http_upload_pass_form_field(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
++static char *ngx_http_upload_set_path_slot(ngx_conf_t *cf, ngx_command_t *cmd,
++ void *conf);
++static char *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);
+ static char *ngx_http_upload_cleanup(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+ static void ngx_upload_cleanup_handler(void *data);
+@@ -391,7 +450,7 @@
+ { ngx_string("upload_store"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_HTTP_LIF_CONF
+ |NGX_CONF_TAKE1234,
+- ngx_conf_set_path_slot,
++ ngx_http_upload_set_path_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_upload_loc_conf_t, store_path),
+ NULL },
+@@ -401,7 +460,7 @@
+ */
+ { ngx_string("upload_state_store"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,
+- ngx_conf_set_path_slot,
++ ngx_http_upload_set_path_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_upload_loc_conf_t, state_store_path),
+ NULL },
+@@ -575,6 +634,28 @@
+ offsetof(ngx_http_upload_loc_conf_t, resumable_uploads),
+ NULL },
+
++ /*
++ * Specifies whether empty field names are allowed
++ */
++ { ngx_string("upload_empty_fiels_names"),
++ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_HTTP_LIF_CONF
++ |NGX_CONF_FLAG,
++ ngx_conf_set_flag_slot,
++ NGX_HTTP_LOC_CONF_OFFSET,
++ offsetof(ngx_http_upload_loc_conf_t, empty_field_names),
++ NULL },
++
++ /*
++ * Specifies the name and content of the header that will be added to the response
++ */
++ { ngx_string("upload_add_header"),
++ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_HTTP_LIF_CONF
++ |NGX_CONF_TAKE2,
++ ngx_http_upload_add_header,
++ NGX_HTTP_LOC_CONF_OFFSET,
++ offsetof(ngx_http_upload_loc_conf_t, header_templates),
++ NULL},
++
+ ngx_null_command
+ }; /* }}} */
+
+@@ -658,6 +739,22 @@
+ (uintptr_t) "0123456789ABCDEF",
+ NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
+
++ { ngx_string("upload_file_sha256"), NULL, ngx_http_upload_sha256_variable,
++ (uintptr_t) "0123456789abcdef",
++ NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
++
++ { ngx_string("upload_file_sha256_uc"), NULL, ngx_http_upload_sha256_variable,
++ (uintptr_t) "0123456789ABCDEF",
++ NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
++
++ { ngx_string("upload_file_sha512"), NULL, ngx_http_upload_sha512_variable,
++ (uintptr_t) "0123456789abcdef",
++ NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
++
++ { ngx_string("upload_file_sha512_uc"), NULL, ngx_http_upload_sha512_variable,
++ (uintptr_t) "0123456789ABCDEF",
++ NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
++
+ { ngx_string("upload_file_crc32"), NULL, ngx_http_upload_crc32_variable,
+ (uintptr_t) offsetof(ngx_http_upload_ctx_t, crc32),
+ NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
+@@ -688,6 +785,9 @@
+ ngx_http_upload_ctx_t *u;
+ ngx_int_t rc;
+
++ if(r->method & NGX_HTTP_OPTIONS)
++ return ngx_http_upload_options_handler(r);
++
+ if (!(r->method & NGX_HTTP_POST))
+ return NGX_HTTP_NOT_ALLOWED;
+
+@@ -724,6 +824,26 @@
+ }else
+ u->sha1_ctx = NULL;
+
++ if(ulcf->sha256) {
++ if(u->sha256_ctx == NULL) {
++ u->sha256_ctx = ngx_palloc(r->pool, sizeof(ngx_http_upload_sha256_ctx_t));
++ if (u->sha256_ctx == NULL) {
++ return NGX_HTTP_INTERNAL_SERVER_ERROR;
++ }
++ }
++ }else
++ u->sha256_ctx = NULL;
++
++ if(ulcf->sha512) {
++ if(u->sha512_ctx == NULL) {
++ u->sha512_ctx = ngx_palloc(r->pool, sizeof(ngx_http_upload_sha512_ctx_t));
++ if (u->sha512_ctx == NULL) {
++ return NGX_HTTP_INTERNAL_SERVER_ERROR;
++ }
++ }
++ }else
++ u->sha512_ctx = NULL;
++
+ u->calculate_crc32 = ulcf->crc32;
+
+ u->request = r;
+@@ -746,6 +866,25 @@
+ return rc;
+ }
+
++ rc = ngx_http_upload_eval_path(r);
++
++ if(rc != NGX_OK) {
++ upload_shutdown_ctx(u);
++ return rc;
++ }
++
++ rc = ngx_http_upload_eval_state_path(r);
++
++ if(rc != NGX_OK) {
++ upload_shutdown_ctx(u);
++ return rc;
++ }
++
++ if (ngx_http_upload_test_expect(r) != NGX_OK) {
++ upload_shutdown_ctx(u);
++ return NGX_HTTP_INTERNAL_SERVER_ERROR;
++ }
++
+ if(upload_start(u, ulcf) != NGX_OK)
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+
+@@ -758,6 +897,124 @@
+ return NGX_DONE;
+ } /* }}} */
+
++static ngx_int_t ngx_http_upload_add_headers(ngx_http_request_t *r, ngx_http_upload_loc_conf_t *ulcf) { /* {{{ */
++ ngx_str_t name;
++ ngx_str_t value;
++ ngx_http_upload_header_template_t *t;
++ ngx_table_elt_t *h;
++ ngx_uint_t i;
++
++ if(ulcf->header_templates != NULL) {
++ t = ulcf->header_templates->elts;
++ for(i = 0; i < ulcf->header_templates->nelts; i++) {
++ if(ngx_http_complex_value(r, t->name, &name) != NGX_OK) {
++ return NGX_ERROR;
++ }
++
++ if(ngx_http_complex_value(r, t->value, &value) != NGX_OK) {
++ return NGX_ERROR;
++ }
++
++ if(name.len != 0 && value.len != 0) {
++ h = ngx_list_push(&r->headers_out.headers);
++ if(h == NULL) {
++ return NGX_ERROR;
++ }
++
++ h->hash = 1;
++ h->key.len = name.len;
++ h->key.data = name.data;
++ h->value.len = value.len;
++ h->value.data = value.data;
++ }
++
++ t++;
++ }
++ }
++
++ return NGX_OK;
++} /* }}} */
++
++static ngx_int_t /* {{{ */
++ngx_http_upload_eval_path(ngx_http_request_t *r) {
++ ngx_http_upload_ctx_t *u;
++ ngx_http_upload_loc_conf_t *ulcf;
++ ngx_str_t value;
++
++ ulcf = ngx_http_get_module_loc_conf(r, ngx_http_upload_module);
++ u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
++
++ if(ulcf->store_path->is_dynamic) {
++ u->store_path = ngx_pcalloc(r->pool, sizeof(ngx_path_t));
++ if(u->store_path == NULL) {
++ return NGX_ERROR;
++ }
++
++ ngx_memcpy(u->store_path, ulcf->store_path->path, sizeof(ngx_path_t));
++
++ if(ngx_http_complex_value(r, &ulcf->store_path->dynamic, &value) != NGX_OK) {
++ return NGX_ERROR;
++ }
++
++ u->store_path->name.data = value.data;
++ u->store_path->name.len = value.len;
++ }
++ else{
++ u->store_path = ulcf->store_path->path;
++ }
++
++ return NGX_OK;
++} /* }}} */
++
++static ngx_int_t /* {{{ */
++ngx_http_upload_eval_state_path(ngx_http_request_t *r) {
++ ngx_http_upload_ctx_t *u;
++ ngx_http_upload_loc_conf_t *ulcf;
++ ngx_str_t value;
++
++ ulcf = ngx_http_get_module_loc_conf(r, ngx_http_upload_module);
++ u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
++
++ if(ulcf->state_store_path->is_dynamic) {
++ u->state_store_path = ngx_pcalloc(r->pool, sizeof(ngx_path_t));
++ if(u->store_path == NULL) {
++ return NGX_ERROR;
++ }
++
++ ngx_memcpy(u->state_store_path, ulcf->state_store_path->path, sizeof(ngx_path_t));
++
++ if(ngx_http_complex_value(r, &ulcf->state_store_path->dynamic, &value) != NGX_OK) {
++ return NGX_ERROR;
++ }
++
++ u->state_store_path->name.data = value.data;
++ u->state_store_path->name.len = value.len;
++ }
++ else{
++ u->state_store_path = ulcf->state_store_path->path;
++ }
++
++ return NGX_OK;
++} /* }}} */
++
++static ngx_int_t ngx_http_upload_options_handler(ngx_http_request_t *r) { /* {{{ */
++ ngx_http_upload_loc_conf_t *ulcf;
++
++ ulcf = ngx_http_get_module_loc_conf(r, ngx_http_upload_module);
++
++ r->headers_out.status = NGX_HTTP_OK;
++
++ if(ngx_http_upload_add_headers(r, ulcf) != NGX_OK) {
++ return NGX_HTTP_INTERNAL_SERVER_ERROR;
++ }
++
++ r->header_only = 1;
++ r->headers_out.content_length_n = 0;
++ r->allow_ranges = 0;
++
++ return ngx_http_send_header(r);
++} /* }}} */
++
+ static ngx_int_t ngx_http_upload_body_handler(ngx_http_request_t *r) { /* {{{ */
+ ngx_http_upload_loc_conf_t *ulcf = ngx_http_get_module_loc_conf(r, ngx_http_upload_module);
+ ngx_http_upload_ctx_t *ctx = ngx_http_get_module_ctx(r, ngx_http_upload_module);
+@@ -771,6 +1028,10 @@
+ ngx_str_t dummy = ngx_string("<ngx_upload_module_dummy>");
+ ngx_table_elt_t *h;
+
++ if(ngx_http_upload_add_headers(r, ulcf) != NGX_OK) {
++ return NGX_HTTP_INTERNAL_SERVER_ERROR;
++ }
++
+ if(ctx->prevent_output) {
+ r->headers_out.status = NGX_HTTP_CREATED;
+
+@@ -952,7 +1213,8 @@
+ ngx_http_upload_loc_conf_t *ulcf = ngx_http_get_module_loc_conf(r, ngx_http_upload_module);
+
+ ngx_file_t *file = &u->output_file;
+- ngx_path_t *path = ulcf->store_path;
++ ngx_path_t *path = u->store_path;
++ ngx_path_t *state_path = u->state_store_path;
+ uint32_t n;
+ ngx_uint_t i;
+ ngx_int_t rc;
+@@ -992,6 +1254,7 @@
+ "hashed path: %s", file->name.data);
+
+ if(u->partial_content) {
++ ngx_file_t *state_file = &u->state_file;
+ if(u->merge_buffer == NULL) {
+ u->merge_buffer = ngx_palloc(r->pool, ulcf->merge_buffer_size);
+
+@@ -999,21 +1262,20 @@
+ return NGX_UPLOAD_NOMEM;
+ }
+
+- u->state_file.name.len = file->name.len + sizeof(".state") - 1;
+- u->state_file.name.data = ngx_palloc(u->request->pool, u->state_file.name.len + 1);
++ state_file->name.len = state_path->name.len + 1 + state_path->len + u->session_id.len + sizeof(".state");
++ state_file->name.data = ngx_palloc(u->request->pool, state_file->name.len + 1);
+
+- if(u->state_file.name.data == NULL)
++ if(state_file->name.data == NULL)
+ return NGX_UPLOAD_NOMEM;
+
+- ngx_memcpy(u->state_file.name.data, file->name.data, file->name.len);
++ ngx_memcpy(state_file->name.data, state_path->name.data, state_path->name.len);
++ (void) ngx_sprintf(state_file->name.data + state_path->name.len + 1 + state_path->len,
++ "%V.state%Z", &u->session_id);
+
+- /*
+- * NOTE: we add terminating zero for system calls
+- */
+- ngx_memcpy(u->state_file.name.data + file->name.len, ".state", sizeof(".state") - 1 + 1);
++ ngx_create_hashed_filename(state_path, state_file->name.data, state_file->name.len);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0,
+- "hashed path of state file: %s", u->state_file.name.data);
++ "hashed path of state file: %s", state_file->name.data);
+ }
+
+ file->fd = ngx_open_file(file->name.data, NGX_FILE_WRONLY, NGX_FILE_CREATE_OR_OPEN, ulcf->store_access);
+@@ -1117,6 +1379,12 @@
+ if(u->sha1_ctx != NULL)
+ SHA1_Init(&u->sha1_ctx->sha1);
+
++ if(u->sha256_ctx != NULL)
++ SHA256_Init(&u->sha256_ctx->sha256);
++
++ if(u->sha512_ctx != NULL)
++ SHA512_Init(&u->sha512_ctx->sha512);
++
+ if(u->calculate_crc32)
+ ngx_crc32_init(u->crc32);
+
+@@ -1150,7 +1418,10 @@
+ #if (NGX_PCRE)
+ rc = ngx_regex_exec(f[i].regex, &u->field_name, NULL, 0);
+
+- if (rc != NGX_REGEX_NO_MATCHED && rc < 0) {
++ /* Modified by Naren to work around iMovie and Quicktime which send empty values Added: && u->field_name.len > 0 */
++ if ((ulcf->empty_field_names && rc != NGX_REGEX_NO_MATCHED && rc < 0 && u->field_name.len != 0)
++ || (!ulcf->empty_field_names && rc != NGX_REGEX_NO_MATCHED && rc < 0))
++ {
+ return NGX_UPLOAD_SCRIPTERROR;
+ }
+
+@@ -1166,7 +1437,7 @@
+ }
+ }
+
+- if(pass_field && u->field_name.len > 0) {
++ if(pass_field && u->field_name.len != 0) {
+ /*
+ * Here we do a small hack: the content of a non-file field
+ * is not known until ngx_http_upload_flush_output_buffer
+@@ -1207,6 +1478,12 @@
+ if(u->sha1_ctx)
+ SHA1_Final(u->sha1_ctx->sha1_digest, &u->sha1_ctx->sha1);
+
++ if(u->sha256_ctx)
++ SHA256_Final(u->sha256_ctx->sha256_digest, &u->sha256_ctx->sha256);
++
++ if(u->sha512_ctx)
++ SHA512_Final(u->sha512_ctx->sha512_digest, &u->sha512_ctx->sha512);
++
+ if(u->calculate_crc32)
+ ngx_crc32_final(u->crc32);
+
+@@ -1369,6 +1646,12 @@
+ if(u->sha1_ctx)
+ SHA1_Update(&u->sha1_ctx->sha1, buf, len);
+
++ if(u->sha256_ctx)
++ SHA256_Update(&u->sha256_ctx->sha256, buf, len);
++
++ if(u->sha512_ctx)
++ SHA512_Update(&u->sha512_ctx->sha512, buf, len);
++
+ if(u->calculate_crc32)
+ ngx_crc32_update(&u->crc32, buf, len);
+
+@@ -1678,7 +1961,7 @@
+ ngx_http_upload_merger_state_t ms;
+ off_t remaining;
+ ssize_t rc;
+- int result;
++ __attribute__((__unused__)) int result;
+ ngx_buf_t in_buf;
+ ngx_buf_t out_buf;
+ ngx_http_upload_loc_conf_t *ulcf = ngx_http_get_module_loc_conf(u->request, ngx_http_upload_module);
+@@ -1799,6 +2082,7 @@
+ conf->forward_args = NGX_CONF_UNSET;
+ conf->tame_arrays = NGX_CONF_UNSET;
+ conf->resumable_uploads = NGX_CONF_UNSET;
++ conf->empty_field_names = NGX_CONF_UNSET;
+
+ conf->buffer_size = NGX_CONF_UNSET_SIZE;
+ conf->merge_buffer_size = NGX_CONF_UNSET_SIZE;
+@@ -1809,6 +2093,7 @@
+ conf->limit_rate = NGX_CONF_UNSET_SIZE;
+
+ /*
++ * conf->header_templates,
+ * conf->field_templates,
+ * conf->aggregate_field_templates,
+ * and conf->field_filters are
+@@ -1830,27 +2115,15 @@
+ }
+
+ if(conf->url.len != 0) {
+-#if defined nginx_version && nginx_version >= 7052
+- ngx_conf_merge_path_value(cf,
++ ngx_http_upload_merge_path_value(cf,
+ &conf->store_path,
+ prev->store_path,
+ &ngx_http_upload_temp_path);
+
+- ngx_conf_merge_path_value(cf,
++ ngx_http_upload_merge_path_value(cf,
+ &conf->state_store_path,
+ prev->state_store_path,
+ &ngx_http_upload_temp_path);
+-#else
+- ngx_conf_merge_path_value(conf->store_path,
+- prev->store_path,
+- NGX_HTTP_PROXY_TEMP_PATH, 1, 2, 0,
+- ngx_garbage_collector_temp_handler, cf);
+-
+- ngx_conf_merge_path_value(conf->state_store_path,
+- prev->state_store_path,
+- NGX_HTTP_PROXY_TEMP_PATH, 1, 2, 0,
+- ngx_garbage_collector_temp_handler, cf);
+-#endif
+ }
+
+ ngx_conf_merge_uint_value(conf->store_access,
+@@ -1897,6 +2170,11 @@
+ prev->resumable_uploads : 0;
+ }
+
++ if(conf->empty_field_names == NGX_CONF_UNSET) {
++ conf->empty_field_names = (prev->empty_field_names != NGX_CONF_UNSET) ?
++ prev->empty_field_names : 0;
++ }
++
+ if(conf->field_templates == NULL) {
+ conf->field_templates = prev->field_templates;
+ }
+@@ -1912,6 +2190,14 @@
+ conf->sha1 = prev->sha1;
+ }
+
++ if(prev->sha256) {
++ conf->sha256 = prev->sha256;
++ }
++
++ if(prev->sha512) {
++ conf->sha512 = prev->sha512;
++ }
++
+ if(prev->crc32) {
+ conf->crc32 = prev->crc32;
+ }
+@@ -1925,6 +2211,10 @@
+ conf->cleanup_statuses = prev->cleanup_statuses;
+ }
+
++ if(conf->header_templates == NULL) {
++ conf->header_templates = prev->header_templates;
++ }
++
+ return NGX_CONF_OK;
+ } /* }}} */
+
+@@ -2066,6 +2356,80 @@
+ return NGX_OK;
+ } /* }}} */
+
++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)
++{
++ ngx_uint_t i;
++ ngx_http_upload_ctx_t *u;
++ u_char *c;
++ u_char *hex_table;
++
++ u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
++
++ if(u->sha256_ctx == NULL || u->partial_content) {
++ v->not_found = 1;
++ return NGX_OK;
++ }
++
++ v->valid = 1;
++ v->no_cacheable = 0;
++ v->not_found = 0;
++
++ hex_table = (u_char*)data;
++ c = u->sha256_ctx->sha256_digest + SHA256_DIGEST_LENGTH * 2;
++
++ i = SHA256_DIGEST_LENGTH;
++
++ do{
++ i--;
++ *--c = hex_table[u->sha256_ctx->sha256_digest[i] & 0xf];
++ *--c = hex_table[u->sha256_ctx->sha256_digest[i] >> 4];
++ }while(i != 0);
++
++ v->data = u->sha256_ctx->sha256_digest;
++ v->len = SHA256_DIGEST_LENGTH * 2;
++
++ return NGX_OK;
++} /* }}} */
++
++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)
++{
++ ngx_uint_t i;
++ ngx_http_upload_ctx_t *u;
++ u_char *c;
++ u_char *hex_table;
++
++ u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
++
++ if(u->sha512_ctx == NULL || u->partial_content) {
++ v->not_found = 1;
++ return NGX_OK;
++ }
++
++ v->valid = 1;
++ v->no_cacheable = 0;
++ v->not_found = 0;
++
++ hex_table = (u_char*)data;
++ c = u->sha512_ctx->sha512_digest + SHA512_DIGEST_LENGTH * 2;
++
++ i = SHA512_DIGEST_LENGTH;
++
++ do{
++ i--;
++ *--c = hex_table[u->sha512_ctx->sha512_digest[i] & 0xf];
++ *--c = hex_table[u->sha512_ctx->sha512_digest[i] >> 4];
++ }while(i != 0);
++
++ v->data = u->sha512_ctx->sha512_digest;
++ v->len = SHA512_DIGEST_LENGTH * 2;
++
++ return NGX_OK;
++} /* }}} */
++
+ 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)
+@@ -2299,6 +2663,10 @@
+ ", upload_file_md5_uc"
+ ", upload_file_sha1"
+ ", upload_file_sha1_uc"
++ ", upload_file_sha256"
++ ", upload_file_sha256_uc"
++ ", upload_file_sha512"
++ ", upload_file_sha512_uc"
+ ", upload_file_crc32"
+ ", upload_content_range"
+ " and upload_file_size"
+@@ -2312,6 +2680,12 @@
+ if(v->get_handler == ngx_http_upload_sha1_variable)
+ ulcf->sha1 = 1;
+
++ if(v->get_handler == ngx_http_upload_sha256_variable)
++ ulcf->sha256 = 1;
++
++ if(v->get_handler == ngx_http_upload_sha512_variable)
++ ulcf->sha512 = 1;
++
+ if(v->get_handler == ngx_http_upload_crc32_variable)
+ ulcf->crc32 = 1;
+ }
+@@ -2396,37 +2770,104 @@
+ return NGX_CONF_OK;
+ } /* }}} */
+
+-static char * /* {{{ ngx_http_upload_cleanup */
+-ngx_http_upload_cleanup(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
++static char * /* {{{ ngx_http_upload_add_header */
++ngx_http_upload_add_header(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+ {
+- ngx_http_upload_loc_conf_t *ulcf = conf;
+-
+ ngx_str_t *value;
+- ngx_uint_t i;
+- ngx_int_t status, lo, hi;
+- uint16_t *s;
++ ngx_http_upload_header_template_t *h;
++ ngx_array_t **field;
++ ngx_http_compile_complex_value_t ccv;
++
++ field = (ngx_array_t**) (((u_char*)conf) + cmd->offset);
+
+ value = cf->args->elts;
+
+- if (ulcf->cleanup_statuses == NULL) {
+- ulcf->cleanup_statuses = ngx_array_create(cf->pool, 1,
+- sizeof(uint16_t));
+- if (ulcf->cleanup_statuses == NULL) {
++ /*
++ * Add new entry to header template list
++ */
++ if (*field == NULL) {
++ *field = ngx_array_create(cf->pool, 1,
++ sizeof(ngx_http_upload_header_template_t));
++ if (*field == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+- for (i = 1; i < cf->args->nelts; i++) {
+- if(value[i].len > 4 && value[i].data[3] == '-') {
+- lo = ngx_atoi(value[i].data, 3);
+-
+- if (lo == NGX_ERROR) {
+- ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+- "invalid lower bound \"%V\"", &value[i]);
+- return NGX_CONF_ERROR;
+- }
+-
+- hi = ngx_atoi(value[i].data + 4, value[i].len - 4);
++ h = ngx_array_push(*field);
++ if (h == NULL) {
++ return NGX_CONF_ERROR;
++ }
++
++ /*
++ * Compile header name
++ */
++ h->name = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
++ if(h->name == NULL) {
++ return NGX_CONF_ERROR;
++ }
++
++ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
++
++ ccv.cf = cf;
++ ccv.value = &value[1];
++ ccv.complex_value = h->name;
++
++ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
++ return NGX_CONF_ERROR;
++ }
++
++ /*
++ * Compile header value
++ */
++ h->value = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
++ if(h->value == NULL) {
++ return NGX_CONF_ERROR;
++ }
++
++ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
++
++ ccv.cf = cf;
++ ccv.value = &value[2];
++ ccv.complex_value = h->value;
++
++ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
++ return NGX_CONF_ERROR;
++ }
++
++ return NGX_CONF_OK;
++} /* }}} */
++
++static char * /* {{{ ngx_http_upload_cleanup */
++ngx_http_upload_cleanup(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
++{
++ ngx_http_upload_loc_conf_t *ulcf = conf;
++
++ ngx_str_t *value;
++ ngx_uint_t i;
++ ngx_int_t status, lo, hi;
++ uint16_t *s;
++
++ value = cf->args->elts;
++
++ if (ulcf->cleanup_statuses == NULL) {
++ ulcf->cleanup_statuses = ngx_array_create(cf->pool, 1,
++ sizeof(uint16_t));
++ if (ulcf->cleanup_statuses == NULL) {
++ return NGX_CONF_ERROR;
++ }
++ }
++
++ for (i = 1; i < cf->args->nelts; i++) {
++ if(value[i].len > 4 && value[i].data[3] == '-') {
++ lo = ngx_atoi(value[i].data, 3);
++
++ if (lo == NGX_ERROR) {
++ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
++ "invalid lower bound \"%V\"", &value[i]);
++ return NGX_CONF_ERROR;
++ }
++
++ hi = ngx_atoi(value[i].data + 4, value[i].len - 4);
+
+ if (hi == NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+@@ -2453,9 +2894,9 @@
+ hi = lo = status;
+ }
+
+- if (lo < 400 || hi > 599) {
++ if (lo < 200 || hi > 599) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+- "value(s) \"%V\" must be between 400 and 599",
++ "value(s) \"%V\" must be between 200 and 599",
+ &value[i]);
+ return NGX_CONF_ERROR;
+ }
+@@ -2523,6 +2964,665 @@
return NGX_CONF_OK;
} /* }}} */
++static char * /* {{{ ngx_http_upload_set_path_slot */
++ngx_http_upload_set_path_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
++{
++ char *p = conf;
++
++ ssize_t level;
++ ngx_str_t *value;
++ ngx_uint_t i, n;
++ ngx_http_upload_path_t *path, **slot;
++ ngx_http_compile_complex_value_t ccv;
++
++ slot = (ngx_http_upload_path_t **) (p + cmd->offset);
++
++ if (*slot) {
++ return "is duplicate";
++ }
++
++ path = ngx_pcalloc(cf->pool, sizeof(ngx_http_upload_path_t));
++ if (path == NULL) {
++ return NGX_CONF_ERROR;
++ }
++
++ path->path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t));
++ if (path->path == NULL) {
++ return NGX_CONF_ERROR;
++ }
++
++ value = cf->args->elts;
++
++ path->path->name = value[1];
++
++ if (path->path->name.data[path->path->name.len - 1] == '/') {
++ path->path->name.len--;
++ }
++
++ if (ngx_conf_full_name(cf->cycle, &path->path->name, 0) != NGX_OK) {
++ return NULL;
++ }
++
++ path->path->len = 0;
++ path->path->manager = NULL;
++ path->path->loader = NULL;
++ path->path->conf_file = cf->conf_file->file.name.data;
++ path->path->line = cf->conf_file->line;
++
++ for (i = 0, n = 2; n < cf->args->nelts; i++, n++) {
++ level = ngx_atoi(value[n].data, value[n].len);
++ if (level == NGX_ERROR || level == 0) {
++ return "invalid value";
++ }
++
++ path->path->level[i] = level;
++ path->path->len += level + 1;
++ }
++
++ while (i < 3) {
++ path->path->level[i++] = 0;
++ }
++
++ *slot = path;
++
++ if(ngx_http_script_variables_count(&value[1])) {
++ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
++
++ ccv.cf = cf;
++ ccv.value = &value[1];
++ ccv.complex_value = &path->dynamic;
++
++ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
++ return NGX_CONF_ERROR;
++ }
++
++ path->is_dynamic = 1;
++ }
++ else {
++ if (ngx_add_path(cf, &path->path) == NGX_ERROR) {
++ return NGX_CONF_ERROR;
++ }
++ }
++
++ return NGX_CONF_OK;
++} /* }}} */
++
++
++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)
++{
++ if (*path) {
++ return NGX_CONF_OK;
++ }
++
++ if (prev) {
++ *path = prev;
++ return NGX_CONF_OK;
++ }
++
++ *path = ngx_palloc(cf->pool, sizeof(ngx_http_upload_path_t));
++ if(*path == NULL) {
++ return NGX_CONF_ERROR;
++ }
++
++ (*path)->path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t));
++ if((*path)->path == NULL) {
++ return NGX_CONF_ERROR;
++ }
++
++ (*path)->path->name = init->name;
++
++ if(ngx_conf_full_name(cf->cycle, &(*path)->path->name, 0) != NGX_OK) {
++ return NGX_CONF_ERROR;
++ }
++
++ (*path)->path->level[0] = init->level[0];
++ (*path)->path->level[1] = init->level[1];
++ (*path)->path->level[2] = init->level[2];
++
++ (*path)->path->len = init->level[0] + (init->level[0] ? 1 : 0)
++ + init->level[1] + (init->level[1] ? 1 : 0)
++ + init->level[2] + (init->level[2] ? 1 : 0);
++
++ (*path)->path->manager = NULL;
++ (*path)->path->loader = NULL;
++ (*path)->path->conf_file = NULL;
++
++ if(ngx_add_path(cf, &(*path)->path) != NGX_OK) {
++ return NGX_CONF_ERROR;
++ }
++
++ return NGX_CONF_OK;
++} /* }}} */
++
+static ngx_int_t
+ngx_http_write_request_body(ngx_http_request_t *r)
+{
@@ -549,11 +1530,10 @@
+}
+
+
-+
ngx_int_t /* {{{ ngx_http_read_upload_client_request_body */
ngx_http_read_upload_client_request_body(ngx_http_request_t *r) {
ssize_t size, preread;
-@@ -2625,9 +3164,9 @@
+@@ -2625,9 +3725,9 @@
/* the whole request body may be placed in r->header_in */
@@ -566,34 +1546,37 @@
return ngx_http_do_read_upload_client_request_body(r);
}
-@@ -2684,7 +3223,7 @@
+@@ -2684,7 +3784,9 @@
*next = cl;
- rb->to_write = rb->bufs;
-+ /* rb->to_write = rb->bufs; */
++ /*
++ * rb->to_write = rb->bufs;
++ */
r->read_event_handler = ngx_http_read_upload_client_request_body_handler;
-@@ -2766,7 +3305,7 @@
+@@ -2766,7 +3868,7 @@
for ( ;; ) {
if (rb->buf->last == rb->buf->end) {
- rc = ngx_http_process_request_body(r, rb->to_write);
-+ rc = ngx_http_process_request_body(r, rb->bufs);
++ rc = ngx_http_process_request_body(r, rb->bufs);
switch(rc) {
case NGX_OK:
-@@ -2782,7 +3321,7 @@
+@@ -2781,8 +3883,7 @@
+ default:
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
-
+-
- rb->to_write = rb->bufs->next ? rb->bufs->next : rb->bufs;
-+ /* rb->to_write = rb->bufs->next ? rb->bufs->next : rb->bufs; */
++ rb->bufs = rb->bufs->next ? rb->bufs->next : rb->bufs;
rb->buf->last = rb->buf->start;
}
-@@ -2874,7 +3413,7 @@
+@@ -2874,7 +3975,7 @@
ngx_del_timer(c->read);
}
@@ -602,3 +1585,74 @@
switch(rc) {
case NGX_OK:
+@@ -3299,6 +4400,14 @@
+ return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;
+ }
+
++ if( (upload_ctx->content_range_n.end - upload_ctx->content_range_n.start + 1)
++ != headers_in->content_length_n)
++ {
++ ngx_log_error(NGX_LOG_ERR, upload_ctx->log, 0,
++ "range length is not equal to content length");
++ return NGX_HTTP_RANGE_NOT_SATISFIABLE;
++ }
++
+ upload_ctx->partial_content = 1;
+ }
+ }
+@@ -3436,8 +4545,8 @@
+ return NGX_ERROR;
+ }
+
+- if(range_n->start >= range_n->end || range_n->start >= range_n->total
+- || range_n->end > range_n->total)
++ if(range_n->start > range_n->end || range_n->start >= range_n->total
++ || range_n->end >= range_n->total)
+ {
+ return NGX_ERROR;
+ }
+@@ -3673,3 +4782,43 @@
+ }
+ } /* }}} */
+
++static ngx_int_t /* {{{ */
++ngx_http_upload_test_expect(ngx_http_request_t *r)
++{
++ ngx_int_t n;
++ ngx_str_t *expect;
++
++ if (r->expect_tested
++ || r->headers_in.expect == NULL
++ || r->http_version < NGX_HTTP_VERSION_11)
++ {
++ return NGX_OK;
++ }
++
++ r->expect_tested = 1;
++
++ expect = &r->headers_in.expect->value;
++
++ if (expect->len != sizeof("100-continue") - 1
++ || ngx_strncasecmp(expect->data, (u_char *) "100-continue",
++ sizeof("100-continue") - 1)
++ != 0)
++ {
++ return NGX_OK;
++ }
++
++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
++ "send 100 Continue");
++
++ n = r->connection->send(r->connection,
++ (u_char *) "HTTP/1.1 100 Continue" CRLF CRLF,
++ sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1);
++
++ if (n == sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1) {
++ return NGX_OK;
++ }
++
++ /* we assume that such small packet should be send successfully */
++
++ return NGX_ERROR;
++} /* }}} */
+
diff --git a/www/nginx/files/extra-patch-ngx_http_upload_module.c b/www/nginx/files/extra-patch-ngx_http_upload_module.c
index e72932bdedee..d626021fa649 100644
--- a/www/nginx/files/extra-patch-ngx_http_upload_module.c
+++ b/www/nginx/files/extra-patch-ngx_http_upload_module.c
@@ -1,9 +1,106 @@
---- ../nginx_upload_module-2.2.0/ngx_http_upload_module.c.orig 2010-09-27 20:54:15.000000000 +0200
-+++ ../nginx_upload_module-2.2.0/ngx_http_upload_module.c 2013-02-21 22:32:08.000000000 +0100
-@@ -233,6 +233,17 @@
+--- ../nginx_upload_module-2.2.0/ngx_http_upload_module.c.orig 2010-09-27 21:54:15.000000000 +0300
++++ ../nginx_upload_module-2.2.0/ngx_http_upload_module.c 2013-09-10 17:40:59.570815847 +0300
+@@ -50,7 +50,7 @@
+ * State of multipart/form-data parser
+ */
+ typedef enum {
+- upload_state_boundary_seek,
++ upload_state_boundary_seek,
+ upload_state_after_boundary,
+ upload_state_headers,
+ upload_state_data,
+@@ -95,6 +95,14 @@
+ } ngx_http_upload_field_template_t;
+
+ /*
++ * Template for a header
++ */
++typedef struct {
++ ngx_http_complex_value_t *name;
++ ngx_http_complex_value_t *value;
++} ngx_http_upload_header_template_t;
++
++/*
+ * Filter for fields in output form
+ */
+ typedef struct {
+@@ -106,6 +114,12 @@
+ #endif
+ } ngx_http_upload_field_filter_t;
+
++typedef struct {
++ ngx_path_t *path;
++ ngx_http_complex_value_t dynamic;
++ unsigned is_dynamic:1;
++} ngx_http_upload_path_t;
++
+ /*
+ * Upload cleanup record
+ */
+@@ -124,8 +138,8 @@
+ typedef struct {
+ ngx_str_t url;
+ ngx_http_complex_value_t *url_cv;
+- ngx_path_t *state_store_path;
+- ngx_path_t *store_path;
++ ngx_http_upload_path_t *state_store_path;
++ ngx_http_upload_path_t *store_path;
+ ngx_uint_t store_access;
+ size_t buffer_size;
+ size_t merge_buffer_size;
+@@ -137,13 +151,17 @@
+ ngx_array_t *aggregate_field_templates;
+ ngx_array_t *field_filters;
+ ngx_array_t *cleanup_statuses;
++ ngx_array_t *header_templates;
+ ngx_flag_t forward_args;
+ ngx_flag_t tame_arrays;
+ ngx_flag_t resumable_uploads;
++ ngx_flag_t empty_field_names;
+ size_t limit_rate;
+
+ unsigned int md5:1;
+ unsigned int sha1:1;
++ unsigned int sha256:1;
++ unsigned int sha512:1;
+ unsigned int crc32:1;
+ } ngx_http_upload_loc_conf_t;
+
+@@ -157,6 +175,16 @@
+ u_char sha1_digest[SHA_DIGEST_LENGTH * 2];
+ } ngx_http_upload_sha1_ctx_t;
+
++typedef struct ngx_http_upload_sha256_ctx_s {
++ SHA256_CTX sha256;
++ u_char sha256_digest[SHA256_DIGEST_LENGTH * 2];
++} ngx_http_upload_sha256_ctx_t;
++
++typedef struct ngx_http_upload_sha512_ctx_s {
++ SHA512_CTX sha512;
++ u_char sha512_digest[SHA512_DIGEST_LENGTH * 2];
++} ngx_http_upload_sha512_ctx_t;
++
+ struct ngx_http_upload_ctx_s;
+
+ /*
+@@ -219,7 +247,11 @@
+
+ ngx_http_upload_md5_ctx_t *md5_ctx;
+ ngx_http_upload_sha1_ctx_t *sha1_ctx;
++ ngx_http_upload_sha256_ctx_t *sha256_ctx;
++ ngx_http_upload_sha512_ctx_t *sha512_ctx;
+ uint32_t crc32;
++ ngx_path_t *store_path;
++ ngx_path_t *state_store_path;
+
+ unsigned int first_part:1;
+ unsigned int discard_data:1;
+@@ -233,7 +265,21 @@
unsigned int raw_input:1;
} ngx_http_upload_ctx_t;
++static ngx_int_t ngx_http_upload_test_expect(ngx_http_request_t *r);
++
+static void ngx_http_read_client_request_body_handler(ngx_http_request_t *r);
+static ngx_int_t ngx_http_do_read_client_request_body(ngx_http_request_t *r);
+
@@ -16,12 +113,896 @@
+static ngx_int_t ngx_http_request_body_save_filter(ngx_http_request_t *r, ngx_chain_t *in);
+
static ngx_int_t ngx_http_upload_handler(ngx_http_request_t *r);
++static ngx_int_t ngx_http_upload_options_handler(ngx_http_request_t *r);
static ngx_int_t ngx_http_upload_body_handler(ngx_http_request_t *r);
-@@ -2523,6 +2534,534 @@
+ static void *ngx_http_upload_create_loc_conf(ngx_conf_t *cf);
+@@ -248,6 +294,10 @@
+ ngx_http_variable_value_t *v, uintptr_t data);
+ static ngx_int_t ngx_http_upload_sha1_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
++static ngx_int_t ngx_http_upload_sha256_variable(ngx_http_request_t *r,
++ ngx_http_variable_value_t *v, uintptr_t data);
++static ngx_int_t ngx_http_upload_sha512_variable(ngx_http_request_t *r,
++ ngx_http_variable_value_t *v, uintptr_t data);
+ static ngx_int_t ngx_http_upload_file_size_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
+ static void ngx_http_upload_content_range_variable_set(ngx_http_request_t *r,
+@@ -271,6 +321,7 @@
+ static ngx_int_t ngx_http_upload_merge_ranges(ngx_http_upload_ctx_t *u, ngx_http_upload_range_t *range_n);
+ static ngx_int_t ngx_http_upload_parse_range(ngx_str_t *range, ngx_http_upload_range_t *range_n);
+
++
+ static void ngx_http_read_upload_client_request_body_handler(ngx_http_request_t *r);
+ static ngx_int_t ngx_http_do_read_upload_client_request_body(ngx_http_request_t *r);
+ static ngx_int_t ngx_http_process_request_body(ngx_http_request_t *r, ngx_chain_t *body);
+@@ -279,8 +330,16 @@
+
+ static char *ngx_http_upload_set_form_field(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
++static char *ngx_http_upload_add_header(ngx_conf_t *cf, ngx_command_t *cmd,
++ void *conf);
++static ngx_int_t ngx_http_upload_eval_path(ngx_http_request_t *r);
++static ngx_int_t ngx_http_upload_eval_state_path(ngx_http_request_t *r);
+ static char *ngx_http_upload_pass_form_field(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
++static char *ngx_http_upload_set_path_slot(ngx_conf_t *cf, ngx_command_t *cmd,
++ void *conf);
++static char *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);
+ static char *ngx_http_upload_cleanup(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+ static void ngx_upload_cleanup_handler(void *data);
+@@ -391,7 +450,7 @@
+ { ngx_string("upload_store"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_HTTP_LIF_CONF
+ |NGX_CONF_TAKE1234,
+- ngx_conf_set_path_slot,
++ ngx_http_upload_set_path_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_upload_loc_conf_t, store_path),
+ NULL },
+@@ -401,7 +460,7 @@
+ */
+ { ngx_string("upload_state_store"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234,
+- ngx_conf_set_path_slot,
++ ngx_http_upload_set_path_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_upload_loc_conf_t, state_store_path),
+ NULL },
+@@ -575,6 +634,28 @@
+ offsetof(ngx_http_upload_loc_conf_t, resumable_uploads),
+ NULL },
+
++ /*
++ * Specifies whether empty field names are allowed
++ */
++ { ngx_string("upload_empty_fiels_names"),
++ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_HTTP_LIF_CONF
++ |NGX_CONF_FLAG,
++ ngx_conf_set_flag_slot,
++ NGX_HTTP_LOC_CONF_OFFSET,
++ offsetof(ngx_http_upload_loc_conf_t, empty_field_names),
++ NULL },
++
++ /*
++ * Specifies the name and content of the header that will be added to the response
++ */
++ { ngx_string("upload_add_header"),
++ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_HTTP_LIF_CONF
++ |NGX_CONF_TAKE2,
++ ngx_http_upload_add_header,
++ NGX_HTTP_LOC_CONF_OFFSET,
++ offsetof(ngx_http_upload_loc_conf_t, header_templates),
++ NULL},
++
+ ngx_null_command
+ }; /* }}} */
+
+@@ -658,6 +739,22 @@
+ (uintptr_t) "0123456789ABCDEF",
+ NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
+
++ { ngx_string("upload_file_sha256"), NULL, ngx_http_upload_sha256_variable,
++ (uintptr_t) "0123456789abcdef",
++ NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
++
++ { ngx_string("upload_file_sha256_uc"), NULL, ngx_http_upload_sha256_variable,
++ (uintptr_t) "0123456789ABCDEF",
++ NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
++
++ { ngx_string("upload_file_sha512"), NULL, ngx_http_upload_sha512_variable,
++ (uintptr_t) "0123456789abcdef",
++ NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
++
++ { ngx_string("upload_file_sha512_uc"), NULL, ngx_http_upload_sha512_variable,
++ (uintptr_t) "0123456789ABCDEF",
++ NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
++
+ { ngx_string("upload_file_crc32"), NULL, ngx_http_upload_crc32_variable,
+ (uintptr_t) offsetof(ngx_http_upload_ctx_t, crc32),
+ NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
+@@ -688,6 +785,9 @@
+ ngx_http_upload_ctx_t *u;
+ ngx_int_t rc;
+
++ if(r->method & NGX_HTTP_OPTIONS)
++ return ngx_http_upload_options_handler(r);
++
+ if (!(r->method & NGX_HTTP_POST))
+ return NGX_HTTP_NOT_ALLOWED;
+
+@@ -724,6 +824,26 @@
+ }else
+ u->sha1_ctx = NULL;
+
++ if(ulcf->sha256) {
++ if(u->sha256_ctx == NULL) {
++ u->sha256_ctx = ngx_palloc(r->pool, sizeof(ngx_http_upload_sha256_ctx_t));
++ if (u->sha256_ctx == NULL) {
++ return NGX_HTTP_INTERNAL_SERVER_ERROR;
++ }
++ }
++ }else
++ u->sha256_ctx = NULL;
++
++ if(ulcf->sha512) {
++ if(u->sha512_ctx == NULL) {
++ u->sha512_ctx = ngx_palloc(r->pool, sizeof(ngx_http_upload_sha512_ctx_t));
++ if (u->sha512_ctx == NULL) {
++ return NGX_HTTP_INTERNAL_SERVER_ERROR;
++ }
++ }
++ }else
++ u->sha512_ctx = NULL;
++
+ u->calculate_crc32 = ulcf->crc32;
+
+ u->request = r;
+@@ -746,6 +866,25 @@
+ return rc;
+ }
+
++ rc = ngx_http_upload_eval_path(r);
++
++ if(rc != NGX_OK) {
++ upload_shutdown_ctx(u);
++ return rc;
++ }
++
++ rc = ngx_http_upload_eval_state_path(r);
++
++ if(rc != NGX_OK) {
++ upload_shutdown_ctx(u);
++ return rc;
++ }
++
++ if (ngx_http_upload_test_expect(r) != NGX_OK) {
++ upload_shutdown_ctx(u);
++ return NGX_HTTP_INTERNAL_SERVER_ERROR;
++ }
++
+ if(upload_start(u, ulcf) != NGX_OK)
+ return NGX_HTTP_INTERNAL_SERVER_ERROR;
+
+@@ -758,6 +897,124 @@
+ return NGX_DONE;
+ } /* }}} */
+
++static ngx_int_t ngx_http_upload_add_headers(ngx_http_request_t *r, ngx_http_upload_loc_conf_t *ulcf) { /* {{{ */
++ ngx_str_t name;
++ ngx_str_t value;
++ ngx_http_upload_header_template_t *t;
++ ngx_table_elt_t *h;
++ ngx_uint_t i;
++
++ if(ulcf->header_templates != NULL) {
++ t = ulcf->header_templates->elts;
++ for(i = 0; i < ulcf->header_templates->nelts; i++) {
++ if(ngx_http_complex_value(r, t->name, &name) != NGX_OK) {
++ return NGX_ERROR;
++ }
++
++ if(ngx_http_complex_value(r, t->value, &value) != NGX_OK) {
++ return NGX_ERROR;
++ }
++
++ if(name.len != 0 && value.len != 0) {
++ h = ngx_list_push(&r->headers_out.headers);
++ if(h == NULL) {
++ return NGX_ERROR;
++ }
++
++ h->hash = 1;
++ h->key.len = name.len;
++ h->key.data = name.data;
++ h->value.len = value.len;
++ h->value.data = value.data;
++ }
++
++ t++;
++ }
++ }
++
++ return NGX_OK;
++} /* }}} */
++
++static ngx_int_t /* {{{ */
++ngx_http_upload_eval_path(ngx_http_request_t *r) {
++ ngx_http_upload_ctx_t *u;
++ ngx_http_upload_loc_conf_t *ulcf;
++ ngx_str_t value;
++
++ ulcf = ngx_http_get_module_loc_conf(r, ngx_http_upload_module);
++ u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
++
++ if(ulcf->store_path->is_dynamic) {
++ u->store_path = ngx_pcalloc(r->pool, sizeof(ngx_path_t));
++ if(u->store_path == NULL) {
++ return NGX_ERROR;
++ }
++
++ ngx_memcpy(u->store_path, ulcf->store_path->path, sizeof(ngx_path_t));
++
++ if(ngx_http_complex_value(r, &ulcf->store_path->dynamic, &value) != NGX_OK) {
++ return NGX_ERROR;
++ }
++
++ u->store_path->name.data = value.data;
++ u->store_path->name.len = value.len;
++ }
++ else{
++ u->store_path = ulcf->store_path->path;
++ }
++
++ return NGX_OK;
++} /* }}} */
++
++static ngx_int_t /* {{{ */
++ngx_http_upload_eval_state_path(ngx_http_request_t *r) {
++ ngx_http_upload_ctx_t *u;
++ ngx_http_upload_loc_conf_t *ulcf;
++ ngx_str_t value;
++
++ ulcf = ngx_http_get_module_loc_conf(r, ngx_http_upload_module);
++ u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
++
++ if(ulcf->state_store_path->is_dynamic) {
++ u->state_store_path = ngx_pcalloc(r->pool, sizeof(ngx_path_t));
++ if(u->store_path == NULL) {
++ return NGX_ERROR;
++ }
++
++ ngx_memcpy(u->state_store_path, ulcf->state_store_path->path, sizeof(ngx_path_t));
++
++ if(ngx_http_complex_value(r, &ulcf->state_store_path->dynamic, &value) != NGX_OK) {
++ return NGX_ERROR;
++ }
++
++ u->state_store_path->name.data = value.data;
++ u->state_store_path->name.len = value.len;
++ }
++ else{
++ u->state_store_path = ulcf->state_store_path->path;
++ }
++
++ return NGX_OK;
++} /* }}} */
++
++static ngx_int_t ngx_http_upload_options_handler(ngx_http_request_t *r) { /* {{{ */
++ ngx_http_upload_loc_conf_t *ulcf;
++
++ ulcf = ngx_http_get_module_loc_conf(r, ngx_http_upload_module);
++
++ r->headers_out.status = NGX_HTTP_OK;
++
++ if(ngx_http_upload_add_headers(r, ulcf) != NGX_OK) {
++ return NGX_HTTP_INTERNAL_SERVER_ERROR;
++ }
++
++ r->header_only = 1;
++ r->headers_out.content_length_n = 0;
++ r->allow_ranges = 0;
++
++ return ngx_http_send_header(r);
++} /* }}} */
++
+ static ngx_int_t ngx_http_upload_body_handler(ngx_http_request_t *r) { /* {{{ */
+ ngx_http_upload_loc_conf_t *ulcf = ngx_http_get_module_loc_conf(r, ngx_http_upload_module);
+ ngx_http_upload_ctx_t *ctx = ngx_http_get_module_ctx(r, ngx_http_upload_module);
+@@ -771,6 +1028,10 @@
+ ngx_str_t dummy = ngx_string("<ngx_upload_module_dummy>");
+ ngx_table_elt_t *h;
+
++ if(ngx_http_upload_add_headers(r, ulcf) != NGX_OK) {
++ return NGX_HTTP_INTERNAL_SERVER_ERROR;
++ }
++
+ if(ctx->prevent_output) {
+ r->headers_out.status = NGX_HTTP_CREATED;
+
+@@ -952,7 +1213,8 @@
+ ngx_http_upload_loc_conf_t *ulcf = ngx_http_get_module_loc_conf(r, ngx_http_upload_module);
+
+ ngx_file_t *file = &u->output_file;
+- ngx_path_t *path = ulcf->store_path;
++ ngx_path_t *path = u->store_path;
++ ngx_path_t *state_path = u->state_store_path;
+ uint32_t n;
+ ngx_uint_t i;
+ ngx_int_t rc;
+@@ -992,6 +1254,7 @@
+ "hashed path: %s", file->name.data);
+
+ if(u->partial_content) {
++ ngx_file_t *state_file = &u->state_file;
+ if(u->merge_buffer == NULL) {
+ u->merge_buffer = ngx_palloc(r->pool, ulcf->merge_buffer_size);
+
+@@ -999,21 +1262,20 @@
+ return NGX_UPLOAD_NOMEM;
+ }
+
+- u->state_file.name.len = file->name.len + sizeof(".state") - 1;
+- u->state_file.name.data = ngx_palloc(u->request->pool, u->state_file.name.len + 1);
++ state_file->name.len = state_path->name.len + 1 + state_path->len + u->session_id.len + sizeof(".state");
++ state_file->name.data = ngx_palloc(u->request->pool, state_file->name.len + 1);
+
+- if(u->state_file.name.data == NULL)
++ if(state_file->name.data == NULL)
+ return NGX_UPLOAD_NOMEM;
+
+- ngx_memcpy(u->state_file.name.data, file->name.data, file->name.len);
++ ngx_memcpy(state_file->name.data, state_path->name.data, state_path->name.len);
++ (void) ngx_sprintf(state_file->name.data + state_path->name.len + 1 + state_path->len,
++ "%V.state%Z", &u->session_id);
+
+- /*
+- * NOTE: we add terminating zero for system calls
+- */
+- ngx_memcpy(u->state_file.name.data + file->name.len, ".state", sizeof(".state") - 1 + 1);
++ ngx_create_hashed_filename(state_path, state_file->name.data, state_file->name.len);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0,
+- "hashed path of state file: %s", u->state_file.name.data);
++ "hashed path of state file: %s", state_file->name.data);
+ }
+
+ file->fd = ngx_open_file(file->name.data, NGX_FILE_WRONLY, NGX_FILE_CREATE_OR_OPEN, ulcf->store_access);
+@@ -1117,6 +1379,12 @@
+ if(u->sha1_ctx != NULL)
+ SHA1_Init(&u->sha1_ctx->sha1);
+
++ if(u->sha256_ctx != NULL)
++ SHA256_Init(&u->sha256_ctx->sha256);
++
++ if(u->sha512_ctx != NULL)
++ SHA512_Init(&u->sha512_ctx->sha512);
++
+ if(u->calculate_crc32)
+ ngx_crc32_init(u->crc32);
+
+@@ -1150,7 +1418,10 @@
+ #if (NGX_PCRE)
+ rc = ngx_regex_exec(f[i].regex, &u->field_name, NULL, 0);
+
+- if (rc != NGX_REGEX_NO_MATCHED && rc < 0) {
++ /* Modified by Naren to work around iMovie and Quicktime which send empty values Added: && u->field_name.len > 0 */
++ if ((ulcf->empty_field_names && rc != NGX_REGEX_NO_MATCHED && rc < 0 && u->field_name.len != 0)
++ || (!ulcf->empty_field_names && rc != NGX_REGEX_NO_MATCHED && rc < 0))
++ {
+ return NGX_UPLOAD_SCRIPTERROR;
+ }
+
+@@ -1166,7 +1437,7 @@
+ }
+ }
+
+- if(pass_field && u->field_name.len > 0) {
++ if(pass_field && u->field_name.len != 0) {
+ /*
+ * Here we do a small hack: the content of a non-file field
+ * is not known until ngx_http_upload_flush_output_buffer
+@@ -1207,6 +1478,12 @@
+ if(u->sha1_ctx)
+ SHA1_Final(u->sha1_ctx->sha1_digest, &u->sha1_ctx->sha1);
+
++ if(u->sha256_ctx)
++ SHA256_Final(u->sha256_ctx->sha256_digest, &u->sha256_ctx->sha256);
++
++ if(u->sha512_ctx)
++ SHA512_Final(u->sha512_ctx->sha512_digest, &u->sha512_ctx->sha512);
++
+ if(u->calculate_crc32)
+ ngx_crc32_final(u->crc32);
+
+@@ -1369,6 +1646,12 @@
+ if(u->sha1_ctx)
+ SHA1_Update(&u->sha1_ctx->sha1, buf, len);
+
++ if(u->sha256_ctx)
++ SHA256_Update(&u->sha256_ctx->sha256, buf, len);
++
++ if(u->sha512_ctx)
++ SHA512_Update(&u->sha512_ctx->sha512, buf, len);
++
+ if(u->calculate_crc32)
+ ngx_crc32_update(&u->crc32, buf, len);
+
+@@ -1678,7 +1961,7 @@
+ ngx_http_upload_merger_state_t ms;
+ off_t remaining;
+ ssize_t rc;
+- int result;
++ __attribute__((__unused__)) int result;
+ ngx_buf_t in_buf;
+ ngx_buf_t out_buf;
+ ngx_http_upload_loc_conf_t *ulcf = ngx_http_get_module_loc_conf(u->request, ngx_http_upload_module);
+@@ -1799,6 +2082,7 @@
+ conf->forward_args = NGX_CONF_UNSET;
+ conf->tame_arrays = NGX_CONF_UNSET;
+ conf->resumable_uploads = NGX_CONF_UNSET;
++ conf->empty_field_names = NGX_CONF_UNSET;
+
+ conf->buffer_size = NGX_CONF_UNSET_SIZE;
+ conf->merge_buffer_size = NGX_CONF_UNSET_SIZE;
+@@ -1809,6 +2093,7 @@
+ conf->limit_rate = NGX_CONF_UNSET_SIZE;
+
+ /*
++ * conf->header_templates,
+ * conf->field_templates,
+ * conf->aggregate_field_templates,
+ * and conf->field_filters are
+@@ -1830,27 +2115,15 @@
+ }
+
+ if(conf->url.len != 0) {
+-#if defined nginx_version && nginx_version >= 7052
+- ngx_conf_merge_path_value(cf,
++ ngx_http_upload_merge_path_value(cf,
+ &conf->store_path,
+ prev->store_path,
+ &ngx_http_upload_temp_path);
+
+- ngx_conf_merge_path_value(cf,
++ ngx_http_upload_merge_path_value(cf,
+ &conf->state_store_path,
+ prev->state_store_path,
+ &ngx_http_upload_temp_path);
+-#else
+- ngx_conf_merge_path_value(conf->store_path,
+- prev->store_path,
+- NGX_HTTP_PROXY_TEMP_PATH, 1, 2, 0,
+- ngx_garbage_collector_temp_handler, cf);
+-
+- ngx_conf_merge_path_value(conf->state_store_path,
+- prev->state_store_path,
+- NGX_HTTP_PROXY_TEMP_PATH, 1, 2, 0,
+- ngx_garbage_collector_temp_handler, cf);
+-#endif
+ }
+
+ ngx_conf_merge_uint_value(conf->store_access,
+@@ -1897,6 +2170,11 @@
+ prev->resumable_uploads : 0;
+ }
+
++ if(conf->empty_field_names == NGX_CONF_UNSET) {
++ conf->empty_field_names = (prev->empty_field_names != NGX_CONF_UNSET) ?
++ prev->empty_field_names : 0;
++ }
++
+ if(conf->field_templates == NULL) {
+ conf->field_templates = prev->field_templates;
+ }
+@@ -1912,6 +2190,14 @@
+ conf->sha1 = prev->sha1;
+ }
+
++ if(prev->sha256) {
++ conf->sha256 = prev->sha256;
++ }
++
++ if(prev->sha512) {
++ conf->sha512 = prev->sha512;
++ }
++
+ if(prev->crc32) {
+ conf->crc32 = prev->crc32;
+ }
+@@ -1925,6 +2211,10 @@
+ conf->cleanup_statuses = prev->cleanup_statuses;
+ }
+
++ if(conf->header_templates == NULL) {
++ conf->header_templates = prev->header_templates;
++ }
++
+ return NGX_CONF_OK;
+ } /* }}} */
+
+@@ -2066,6 +2356,80 @@
+ return NGX_OK;
+ } /* }}} */
+
++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)
++{
++ ngx_uint_t i;
++ ngx_http_upload_ctx_t *u;
++ u_char *c;
++ u_char *hex_table;
++
++ u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
++
++ if(u->sha256_ctx == NULL || u->partial_content) {
++ v->not_found = 1;
++ return NGX_OK;
++ }
++
++ v->valid = 1;
++ v->no_cacheable = 0;
++ v->not_found = 0;
++
++ hex_table = (u_char*)data;
++ c = u->sha256_ctx->sha256_digest + SHA256_DIGEST_LENGTH * 2;
++
++ i = SHA256_DIGEST_LENGTH;
++
++ do{
++ i--;
++ *--c = hex_table[u->sha256_ctx->sha256_digest[i] & 0xf];
++ *--c = hex_table[u->sha256_ctx->sha256_digest[i] >> 4];
++ }while(i != 0);
++
++ v->data = u->sha256_ctx->sha256_digest;
++ v->len = SHA256_DIGEST_LENGTH * 2;
++
++ return NGX_OK;
++} /* }}} */
++
++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)
++{
++ ngx_uint_t i;
++ ngx_http_upload_ctx_t *u;
++ u_char *c;
++ u_char *hex_table;
++
++ u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
++
++ if(u->sha512_ctx == NULL || u->partial_content) {
++ v->not_found = 1;
++ return NGX_OK;
++ }
++
++ v->valid = 1;
++ v->no_cacheable = 0;
++ v->not_found = 0;
++
++ hex_table = (u_char*)data;
++ c = u->sha512_ctx->sha512_digest + SHA512_DIGEST_LENGTH * 2;
++
++ i = SHA512_DIGEST_LENGTH;
++
++ do{
++ i--;
++ *--c = hex_table[u->sha512_ctx->sha512_digest[i] & 0xf];
++ *--c = hex_table[u->sha512_ctx->sha512_digest[i] >> 4];
++ }while(i != 0);
++
++ v->data = u->sha512_ctx->sha512_digest;
++ v->len = SHA512_DIGEST_LENGTH * 2;
++
++ return NGX_OK;
++} /* }}} */
++
+ 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)
+@@ -2299,6 +2663,10 @@
+ ", upload_file_md5_uc"
+ ", upload_file_sha1"
+ ", upload_file_sha1_uc"
++ ", upload_file_sha256"
++ ", upload_file_sha256_uc"
++ ", upload_file_sha512"
++ ", upload_file_sha512_uc"
+ ", upload_file_crc32"
+ ", upload_content_range"
+ " and upload_file_size"
+@@ -2312,6 +2680,12 @@
+ if(v->get_handler == ngx_http_upload_sha1_variable)
+ ulcf->sha1 = 1;
+
++ if(v->get_handler == ngx_http_upload_sha256_variable)
++ ulcf->sha256 = 1;
++
++ if(v->get_handler == ngx_http_upload_sha512_variable)
++ ulcf->sha512 = 1;
++
+ if(v->get_handler == ngx_http_upload_crc32_variable)
+ ulcf->crc32 = 1;
+ }
+@@ -2396,37 +2770,104 @@
+ return NGX_CONF_OK;
+ } /* }}} */
+
+-static char * /* {{{ ngx_http_upload_cleanup */
+-ngx_http_upload_cleanup(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
++static char * /* {{{ ngx_http_upload_add_header */
++ngx_http_upload_add_header(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+ {
+- ngx_http_upload_loc_conf_t *ulcf = conf;
+-
+ ngx_str_t *value;
+- ngx_uint_t i;
+- ngx_int_t status, lo, hi;
+- uint16_t *s;
++ ngx_http_upload_header_template_t *h;
++ ngx_array_t **field;
++ ngx_http_compile_complex_value_t ccv;
++
++ field = (ngx_array_t**) (((u_char*)conf) + cmd->offset);
+
+ value = cf->args->elts;
+
+- if (ulcf->cleanup_statuses == NULL) {
+- ulcf->cleanup_statuses = ngx_array_create(cf->pool, 1,
+- sizeof(uint16_t));
+- if (ulcf->cleanup_statuses == NULL) {
++ /*
++ * Add new entry to header template list
++ */
++ if (*field == NULL) {
++ *field = ngx_array_create(cf->pool, 1,
++ sizeof(ngx_http_upload_header_template_t));
++ if (*field == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+- for (i = 1; i < cf->args->nelts; i++) {
+- if(value[i].len > 4 && value[i].data[3] == '-') {
+- lo = ngx_atoi(value[i].data, 3);
+-
+- if (lo == NGX_ERROR) {
+- ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+- "invalid lower bound \"%V\"", &value[i]);
+- return NGX_CONF_ERROR;
+- }
+-
+- hi = ngx_atoi(value[i].data + 4, value[i].len - 4);
++ h = ngx_array_push(*field);
++ if (h == NULL) {
++ return NGX_CONF_ERROR;
++ }
++
++ /*
++ * Compile header name
++ */
++ h->name = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
++ if(h->name == NULL) {
++ return NGX_CONF_ERROR;
++ }
++
++ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
++
++ ccv.cf = cf;
++ ccv.value = &value[1];
++ ccv.complex_value = h->name;
++
++ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
++ return NGX_CONF_ERROR;
++ }
++
++ /*
++ * Compile header value
++ */
++ h->value = ngx_palloc(cf->pool, sizeof(ngx_http_complex_value_t));
++ if(h->value == NULL) {
++ return NGX_CONF_ERROR;
++ }
++
++ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
++
++ ccv.cf = cf;
++ ccv.value = &value[2];
++ ccv.complex_value = h->value;
++
++ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
++ return NGX_CONF_ERROR;
++ }
++
++ return NGX_CONF_OK;
++} /* }}} */
++
++static char * /* {{{ ngx_http_upload_cleanup */
++ngx_http_upload_cleanup(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
++{
++ ngx_http_upload_loc_conf_t *ulcf = conf;
++
++ ngx_str_t *value;
++ ngx_uint_t i;
++ ngx_int_t status, lo, hi;
++ uint16_t *s;
++
++ value = cf->args->elts;
++
++ if (ulcf->cleanup_statuses == NULL) {
++ ulcf->cleanup_statuses = ngx_array_create(cf->pool, 1,
++ sizeof(uint16_t));
++ if (ulcf->cleanup_statuses == NULL) {
++ return NGX_CONF_ERROR;
++ }
++ }
++
++ for (i = 1; i < cf->args->nelts; i++) {
++ if(value[i].len > 4 && value[i].data[3] == '-') {
++ lo = ngx_atoi(value[i].data, 3);
++
++ if (lo == NGX_ERROR) {
++ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
++ "invalid lower bound \"%V\"", &value[i]);
++ return NGX_CONF_ERROR;
++ }
++
++ hi = ngx_atoi(value[i].data + 4, value[i].len - 4);
+
+ if (hi == NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+@@ -2453,9 +2894,9 @@
+ hi = lo = status;
+ }
+
+- if (lo < 400 || hi > 599) {
++ if (lo < 200 || hi > 599) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+- "value(s) \"%V\" must be between 400 and 599",
++ "value(s) \"%V\" must be between 200 and 599",
+ &value[i]);
+ return NGX_CONF_ERROR;
+ }
+@@ -2523,6 +2964,665 @@
return NGX_CONF_OK;
} /* }}} */
++static char * /* {{{ ngx_http_upload_set_path_slot */
++ngx_http_upload_set_path_slot(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
++{
++ char *p = conf;
++
++ ssize_t level;
++ ngx_str_t *value;
++ ngx_uint_t i, n;
++ ngx_http_upload_path_t *path, **slot;
++ ngx_http_compile_complex_value_t ccv;
++
++ slot = (ngx_http_upload_path_t **) (p + cmd->offset);
++
++ if (*slot) {
++ return "is duplicate";
++ }
++
++ path = ngx_pcalloc(cf->pool, sizeof(ngx_http_upload_path_t));
++ if (path == NULL) {
++ return NGX_CONF_ERROR;
++ }
++
++ path->path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t));
++ if (path->path == NULL) {
++ return NGX_CONF_ERROR;
++ }
++
++ value = cf->args->elts;
++
++ path->path->name = value[1];
++
++ if (path->path->name.data[path->path->name.len - 1] == '/') {
++ path->path->name.len--;
++ }
++
++ if (ngx_conf_full_name(cf->cycle, &path->path->name, 0) != NGX_OK) {
++ return NULL;
++ }
++
++ path->path->len = 0;
++ path->path->manager = NULL;
++ path->path->loader = NULL;
++ path->path->conf_file = cf->conf_file->file.name.data;
++ path->path->line = cf->conf_file->line;
++
++ for (i = 0, n = 2; n < cf->args->nelts; i++, n++) {
++ level = ngx_atoi(value[n].data, value[n].len);
++ if (level == NGX_ERROR || level == 0) {
++ return "invalid value";
++ }
++
++ path->path->level[i] = level;
++ path->path->len += level + 1;
++ }
++
++ while (i < 3) {
++ path->path->level[i++] = 0;
++ }
++
++ *slot = path;
++
++ if(ngx_http_script_variables_count(&value[1])) {
++ ngx_memzero(&ccv, sizeof(ngx_http_compile_complex_value_t));
++
++ ccv.cf = cf;
++ ccv.value = &value[1];
++ ccv.complex_value = &path->dynamic;
++
++ if (ngx_http_compile_complex_value(&ccv) != NGX_OK) {
++ return NGX_CONF_ERROR;
++ }
++
++ path->is_dynamic = 1;
++ }
++ else {
++ if (ngx_add_path(cf, &path->path) == NGX_ERROR) {
++ return NGX_CONF_ERROR;
++ }
++ }
++
++ return NGX_CONF_OK;
++} /* }}} */
++
++
++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)
++{
++ if (*path) {
++ return NGX_CONF_OK;
++ }
++
++ if (prev) {
++ *path = prev;
++ return NGX_CONF_OK;
++ }
++
++ *path = ngx_palloc(cf->pool, sizeof(ngx_http_upload_path_t));
++ if(*path == NULL) {
++ return NGX_CONF_ERROR;
++ }
++
++ (*path)->path = ngx_pcalloc(cf->pool, sizeof(ngx_path_t));
++ if((*path)->path == NULL) {
++ return NGX_CONF_ERROR;
++ }
++
++ (*path)->path->name = init->name;
++
++ if(ngx_conf_full_name(cf->cycle, &(*path)->path->name, 0) != NGX_OK) {
++ return NGX_CONF_ERROR;
++ }
++
++ (*path)->path->level[0] = init->level[0];
++ (*path)->path->level[1] = init->level[1];
++ (*path)->path->level[2] = init->level[2];
++
++ (*path)->path->len = init->level[0] + (init->level[0] ? 1 : 0)
++ + init->level[1] + (init->level[1] ? 1 : 0)
++ + init->level[2] + (init->level[2] ? 1 : 0);
++
++ (*path)->path->manager = NULL;
++ (*path)->path->loader = NULL;
++ (*path)->path->conf_file = NULL;
++
++ if(ngx_add_path(cf, &(*path)->path) != NGX_OK) {
++ return NGX_CONF_ERROR;
++ }
++
++ return NGX_CONF_OK;
++} /* }}} */
++
+static ngx_int_t
+ngx_http_write_request_body(ngx_http_request_t *r)
+{
@@ -549,11 +1530,10 @@
+}
+
+
-+
ngx_int_t /* {{{ ngx_http_read_upload_client_request_body */
ngx_http_read_upload_client_request_body(ngx_http_request_t *r) {
ssize_t size, preread;
-@@ -2625,9 +3164,9 @@
+@@ -2625,9 +3725,9 @@
/* the whole request body may be placed in r->header_in */
@@ -566,34 +1546,37 @@
return ngx_http_do_read_upload_client_request_body(r);
}
-@@ -2684,7 +3223,7 @@
+@@ -2684,7 +3784,9 @@
*next = cl;
- rb->to_write = rb->bufs;
-+ /* rb->to_write = rb->bufs; */
++ /*
++ * rb->to_write = rb->bufs;
++ */
r->read_event_handler = ngx_http_read_upload_client_request_body_handler;
-@@ -2766,7 +3305,7 @@
+@@ -2766,7 +3868,7 @@
for ( ;; ) {
if (rb->buf->last == rb->buf->end) {
- rc = ngx_http_process_request_body(r, rb->to_write);
-+ rc = ngx_http_process_request_body(r, rb->bufs);
++ rc = ngx_http_process_request_body(r, rb->bufs);
switch(rc) {
case NGX_OK:
-@@ -2782,7 +3321,7 @@
+@@ -2781,8 +3883,7 @@
+ default:
return NGX_HTTP_INTERNAL_SERVER_ERROR;
}
-
+-
- rb->to_write = rb->bufs->next ? rb->bufs->next : rb->bufs;
-+ /* rb->to_write = rb->bufs->next ? rb->bufs->next : rb->bufs; */
++ rb->bufs = rb->bufs->next ? rb->bufs->next : rb->bufs;
rb->buf->last = rb->buf->start;
}
-@@ -2874,7 +3413,7 @@
+@@ -2874,7 +3975,7 @@
ngx_del_timer(c->read);
}
@@ -602,3 +1585,74 @@
switch(rc) {
case NGX_OK:
+@@ -3299,6 +4400,14 @@
+ return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE;
+ }
+
++ if( (upload_ctx->content_range_n.end - upload_ctx->content_range_n.start + 1)
++ != headers_in->content_length_n)
++ {
++ ngx_log_error(NGX_LOG_ERR, upload_ctx->log, 0,
++ "range length is not equal to content length");
++ return NGX_HTTP_RANGE_NOT_SATISFIABLE;
++ }
++
+ upload_ctx->partial_content = 1;
+ }
+ }
+@@ -3436,8 +4545,8 @@
+ return NGX_ERROR;
+ }
+
+- if(range_n->start >= range_n->end || range_n->start >= range_n->total
+- || range_n->end > range_n->total)
++ if(range_n->start > range_n->end || range_n->start >= range_n->total
++ || range_n->end >= range_n->total)
+ {
+ return NGX_ERROR;
+ }
+@@ -3673,3 +4782,43 @@
+ }
+ } /* }}} */
+
++static ngx_int_t /* {{{ */
++ngx_http_upload_test_expect(ngx_http_request_t *r)
++{
++ ngx_int_t n;
++ ngx_str_t *expect;
++
++ if (r->expect_tested
++ || r->headers_in.expect == NULL
++ || r->http_version < NGX_HTTP_VERSION_11)
++ {
++ return NGX_OK;
++ }
++
++ r->expect_tested = 1;
++
++ expect = &r->headers_in.expect->value;
++
++ if (expect->len != sizeof("100-continue") - 1
++ || ngx_strncasecmp(expect->data, (u_char *) "100-continue",
++ sizeof("100-continue") - 1)
++ != 0)
++ {
++ return NGX_OK;
++ }
++
++ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
++ "send 100 Continue");
++
++ n = r->connection->send(r->connection,
++ (u_char *) "HTTP/1.1 100 Continue" CRLF CRLF,
++ sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1);
++
++ if (n == sizeof("HTTP/1.1 100 Continue" CRLF CRLF) - 1) {
++ return NGX_OK;
++ }
++
++ /* we assume that such small packet should be send successfully */
++
++ return NGX_ERROR;
++} /* }}} */
+