Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Merged changes from branch 2.0; added resolution of content type for …

…files in ZIP archive
  • Loading branch information...
commit a378f00b0aca0a13e3dd8fb8619f0e294dbdaa1a 1 parent 07406fe
@vkholodkov authored
Showing with 444 additions and 108 deletions.
  1. +127 −18 ngx_http_unzip_filter_module.c
  2. +18 −0 ngx_http_upload.h
  3. +299 −90 ngx_http_upload_module.c
View
145 ngx_http_unzip_filter_module.c
@@ -25,7 +25,16 @@
#define ZIP_METHOD_STORED 0
#define ZIP_METHOD_DEFLATED 8
-#define ZIP_FLAG_HAVE_DATA_DESC 0x0008
+#define ZIP_FLAG_ENCRYPTED 0x0001
+#define ZIP_FLAG_UNLIMITED 0x0008
+#define ZIP_FLAG_RESERVED1 0x0010
+#define ZIP_FLAG_PATCH 0x0020
+#define ZIP_FLAG_STRONG_ENCRYPTION 0x0040
+#define ZIP_FLAG_USE_UTF8 0x0800
+#define ZIP_FLAG_RESERVED2 0x1000
+#define ZIP_FLAG_LOCAL_HEADER_OBFUSCATED 0x2000
+#define ZIP_FLAG_RESERVED3 0x4000
+#define ZIP_FLAG_RESERVED4 0x8000
#define ZIP_VERSION 20
@@ -62,6 +71,7 @@ typedef enum {
unzip_state_file_name,
unzip_state_extra_field,
unzip_state_file_data,
+ unzip_state_unlimited_file_data,
unzip_state_data_descriptor,
unzip_state_decryption_header,
unzip_state_extra_data_record,
@@ -143,7 +153,7 @@ typedef struct ngx_unzip_ctx_s {
uint16_t version_needed;
uint16_t flags;
- uint16_t compression_method_number;
+ uint16_t compression_method;
uint16_t last_mod_time;
uint16_t last_mod_date;
uint32_t crc32;
@@ -163,6 +173,8 @@ typedef struct ngx_unzip_ctx_s {
ngx_upload_content_filter_t *next_content_filter;
ngx_unzip_decompression_method_t *decompression_method;
+ uint32_t calculated_crc32;
+
unsigned int discard_data:1;
} ngx_unzip_ctx_t;
@@ -241,6 +253,9 @@ static ngx_command_t ngx_http_unzip_filter_commands[] = { /* {{{ */
offsetof(ngx_unzip_conf_t, bufs),
NULL },
+ /*
+ * Specifies size window to use for decompressing
+ */
{ ngx_string("unzip_window"),
NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
ngx_conf_set_size_slot,
@@ -248,6 +263,10 @@ static ngx_command_t ngx_http_unzip_filter_commands[] = { /* {{{ */
offsetof(ngx_unzip_conf_t, wbits),
&ngx_http_unzip_window_p },
+ /*
+ * Specifies a form field with a special content to generate
+ * in output form
+ */
{ ngx_string("unzip_set_form_field"),
NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
ngx_conf_set_size_slot,
@@ -255,6 +274,10 @@ static ngx_command_t ngx_http_unzip_filter_commands[] = { /* {{{ */
offsetof(ngx_unzip_conf_t, wbits),
NULL },
+ /*
+ * Specifies a form field with a special aggregate content to generate
+ * in output form
+ */
{ ngx_string("unzip_aggregate_form_field"),
NGX_HTTP_LOC_CONF|NGX_CONF_TAKE2,
ngx_conf_set_size_slot,
@@ -262,6 +285,16 @@ static ngx_command_t ngx_http_unzip_filter_commands[] = { /* {{{ */
offsetof(ngx_unzip_conf_t, wbits),
NULL },
+ /*
+ * Specifies the maximal length of a file name in archive
+ */
+ { ngx_string("unzip_max_file_name_len"),
+ NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_unzip_conf_t, max_file_name_len),
+ NULL },
+
ngx_null_command
}; /* }}} */
@@ -323,10 +356,11 @@ static ngx_int_t /* {{{ ngx_http_unzip_process_chain */
ngx_http_unzip_process_chain(ngx_unzip_ctx_t *ctx, ngx_chain_t *chain) {
ngx_int_t result;
ngx_buf_t *buf;
+ ngx_unzip_conf_t *uzcf;
- while(chain != NULL && !chain->buf->last_in_chain) {
- buf = chain->buf;
+ uzcf = ngx_http_get_module_loc_conf(ctx->upload_ctx->request, ngx_http_unzip_filter_module);
+ while(chain != NULL) {
for(buf = chain->buf ; buf->pos != buf->last ; buf->pos++) {
switch(ctx->state) {
case unzip_state_signature: /* {{{ */
@@ -375,7 +409,7 @@ ngx_http_unzip_process_chain(ngx_unzip_ctx_t *ctx, ngx_chain_t *chain) {
ctx->version_needed = EXTRACT_SHORT(ctx->current_field);
ctx->flags = EXTRACT_SHORT(ctx->current_field + 2);
- ctx->compression_method_number = EXTRACT_SHORT(ctx->current_field + 4);
+ ctx->compression_method = EXTRACT_SHORT(ctx->current_field + 4);
ctx->last_mod_time = EXTRACT_SHORT(ctx->current_field + 6);
ctx->last_mod_date = EXTRACT_SHORT(ctx->current_field + 8);
ctx->crc32 = EXTRACT_LONG(ctx->current_field + 10);
@@ -385,14 +419,22 @@ ngx_http_unzip_process_chain(ngx_unzip_ctx_t *ctx, ngx_chain_t *chain) {
ctx->file_name_len = EXTRACT_SHORT(ctx->current_field + 22);
ctx->extra_field_len = EXTRACT_SHORT(ctx->current_field + 24);
+ if(uzcf->max_file_name_len > 0 && ctx->file_name_len > uzcf->max_file_name_len) {
+ ngx_log_error(NGX_LOG_ALERT, ctx->log, 0,
+ "file name in is too long: %u", ctx->file_name_len);
+ return NGX_UNZIP_MALFORMED;
+ }
+
if(ngx_http_unzip_set_decompression_method(ctx,
- ctx->compression_method_number) != NGX_OK)
+ ctx->compression_method) != NGX_OK)
{
return NGX_UNZIP_MALFORMED;
}
if(ctx->version_needed > ZIP_VERSION)
{
+ ngx_log_error(NGX_LOG_ALERT, ctx->log, 0,
+ "more recent version of unzip implementation required: %u, have %u", ctx->version_needed, ZIP_VERSION);
return NGX_UNZIP_MALFORMED;
}
@@ -400,9 +442,11 @@ ngx_http_unzip_process_chain(ngx_unzip_ctx_t *ctx, ngx_chain_t *chain) {
ctx->state = unzip_state_file_name;
else if(ctx->extra_field_len > 0)
ctx->state = unzip_state_extra_field;
+ else if(ctx->flags & ZIP_FLAG_UNLIMITED)
+ ctx->state = unzip_state_unlimited_file_data;
else if(ctx->compressed_size > 0)
ctx->state = unzip_state_file_data;
- else if(ctx->flags & ZIP_FLAG_HAVE_DATA_DESC)
+ else if(ctx->flags & ZIP_FLAG_UNLIMITED)
ctx->state = unzip_state_data_descriptor;
else
ctx->state = unzip_state_signature;
@@ -412,7 +456,7 @@ ngx_http_unzip_process_chain(ngx_unzip_ctx_t *ctx, ngx_chain_t *chain) {
if(ctx->current_field_pos == 0) {
ctx->file_name.len = ctx->file_name_len;
- ctx->file_name.data = ngx_palloc(ctx->pool, ctx->file_name_len + 1);
+ ctx->file_name.data = ngx_palloc(ctx->pool, ctx->file_name_len);
if(ctx->file_name.data == NULL) {
return NGX_UPLOAD_NOMEM;
@@ -428,17 +472,17 @@ ngx_http_unzip_process_chain(ngx_unzip_ctx_t *ctx, ngx_chain_t *chain) {
if(ctx->current_field_pos == ctx->current_field_len) {
ctx->current_field_pos = 0;
- *ctx->current_field_ptr = '\0';
-
if(ngx_http_unzip_parse_file_name(ctx, &ctx->file_name) != NGX_OK) {
return NGX_UNZIP_MALFORMED;
}
if(ctx->extra_field_len > 0)
ctx->state = unzip_state_extra_field;
+ else if(ctx->flags & ZIP_FLAG_UNLIMITED)
+ ctx->state = unzip_state_unlimited_file_data;
else if(ctx->compressed_size > 0)
ctx->state = unzip_state_file_data;
- else if(ctx->flags & ZIP_FLAG_HAVE_DATA_DESC)
+ else if(ctx->flags & ZIP_FLAG_UNLIMITED)
ctx->state = unzip_state_data_descriptor;
else
ctx->state = unzip_state_signature;
@@ -454,15 +498,17 @@ ngx_http_unzip_process_chain(ngx_unzip_ctx_t *ctx, ngx_chain_t *chain) {
if(ctx->current_field_pos == ctx->current_field_len) {
ctx->current_field_pos = 0;
- if(ctx->compressed_size > 0)
+ if(ctx->flags & ZIP_FLAG_UNLIMITED)
+ ctx->state = unzip_state_unlimited_file_data;
+ else if(ctx->compressed_size > 0)
ctx->state = unzip_state_file_data;
- else if(ctx->flags & ZIP_FLAG_HAVE_DATA_DESC)
+ else if(ctx->flags & ZIP_FLAG_UNLIMITED)
ctx->state = unzip_state_data_descriptor;
else
ctx->state = unzip_state_signature;
}
break;
- case unzip_state_file_data:
+ case unzip_state_file_data: /* {{{ */
if(ctx->current_field_pos == 0) {
ctx->current_field_len = ctx->compressed_size;
@@ -496,12 +542,43 @@ ngx_http_unzip_process_chain(ngx_unzip_ctx_t *ctx, ngx_chain_t *chain) {
ctx->current_field_pos = 0;
- if(ctx->flags & ZIP_FLAG_HAVE_DATA_DESC)
+ ctx->state = unzip_state_signature;
+ }
+ break; /* }}} */
+ case unzip_state_unlimited_file_data: /* {{{ */
+ if(ctx->current_field_pos == 0) {
+ if(ctx->decompression_method->start(ctx) != NGX_OK) {
+ ctx->discard_data = 1;
+ }
+
+ ctx->current_field_pos = 1;
+ }
+
+ if(!ctx->discard_data) {
+ result = ctx->decompression_method->process_chain(ctx, chain);
+
+ buf->pos--;
+
+ if(result == NGX_AGAIN) {
+ return NGX_AGAIN;
+ }
+
+ if(result != NGX_OK) {
+ ctx->discard_data = 1;
+
+ ctx->decompression_method->abort(ctx);
+
+ return NGX_UNZIP_MALFORMED;
+ }else{
+ if(!ctx->discard_data)
+ ctx->decompression_method->finish(ctx);
+
+ ctx->current_field_pos = 0;
+
ctx->state = unzip_state_data_descriptor;
- else
- ctx->state = unzip_state_signature;
+ }
}
- break;
+ break; /* }}} */
case unzip_state_data_descriptor:
if(ctx->current_field_pos == 0) {
ctx->current_field_len = DATA_DESCRIPTOR_LEN;
@@ -513,6 +590,11 @@ ngx_http_unzip_process_chain(ngx_unzip_ctx_t *ctx, ngx_chain_t *chain) {
if(ctx->current_field_pos == ctx->current_field_len) {
ctx->current_field_pos = 0;
+
+ ctx->crc32 = EXTRACT_LONG(ctx->current_field);
+ ctx->compressed_size = EXTRACT_LONG(ctx->current_field + 4);
+ ctx->uncompressed_size = EXTRACT_LONG(ctx->current_field + 8);
+
ctx->state = unzip_state_signature;
}
break;
@@ -746,6 +828,8 @@ ngx_http_unzip_inflate_start(ngx_unzip_ctx_t *ctx) {
return rc;
}
+ ngx_crc32_init(ctx->calculated_crc32);
+
return NGX_OK;
cleanup:
inflateEnd(&ctx->stream);
@@ -754,6 +838,8 @@ ngx_http_unzip_inflate_start(ngx_unzip_ctx_t *ctx) {
static void /* {{{ ngx_http_unzip_inflate_finish */
ngx_http_unzip_inflate_finish(ngx_unzip_ctx_t *ctx) {
+ ngx_crc32_final(ctx->calculated_crc32);
+
if(ctx->next_content_filter->finish)
ctx->next_content_filter->finish(ctx->upload_ctx);
@@ -798,6 +884,9 @@ ngx_http_unzip_inflate_process_chain(ngx_unzip_ctx_t *ctx, ngx_chain_t *chain) {
if(rc == Z_OK || rc == Z_STREAM_END) {
ctx->output_buffer->last = ctx->stream.next_out;
+ ngx_crc32_update(&ctx->calculated_crc32, ctx->output_buffer->pos,
+ ctx->output_buffer->last - ctx->output_buffer->pos);
+
if(ctx->next_content_filter->process_chain)
ctx->next_content_filter->process_chain(ctx->upload_ctx,
ctx->output_chain);
@@ -887,6 +976,8 @@ ngx_http_unzip_parse_file_name(ngx_unzip_ctx_t *ctx, ngx_str_t *file_name) {
ngx_str_t archive_path = { file_name->len, file_name->data };
ngx_str_t element_name = { file_name->len, file_name->data };
+ ngx_str_t exten;
+ ngx_str_t content_type;
for(p = file_name->data + file_name->len - 1; p >= file_name->data ; p--, archive_path.len--) {
if(*p == '/') {
@@ -906,6 +997,24 @@ ngx_http_unzip_parse_file_name(ngx_unzip_ctx_t *ctx, ngx_str_t *file_name) {
return rc;
}
+ rc = ngx_upload_set_exten(ctx->upload_ctx, &element_name, &exten);
+
+ if(rc != NGX_OK) {
+ return rc;
+ }
+
+ rc = ngx_upload_resolve_content_type(ctx->upload_ctx, &exten, &content_type);
+
+ if(rc != NGX_OK) {
+ return rc;
+ }
+
+ rc = ngx_upload_set_content_type(ctx->upload_ctx, &content_type);
+
+ if(rc != NGX_OK) {
+ return rc;
+ }
+
rc = ngx_upload_set_archive_path(ctx->upload_ctx, &archive_path);
if(rc != NGX_OK) {
View
18 ngx_http_upload.h
@@ -96,6 +96,18 @@ typedef struct {
} ngx_upload_content_type_map_t;
/*
+ * Upload cleanup record
+ */
+typedef struct ngx_http_upload_cleanup_s {
+ ngx_fd_t fd;
+ u_char *filename;
+ ngx_http_headers_out_t *headers_out;
+ ngx_array_t *cleanup_statuses;
+ ngx_log_t *log;
+ unsigned int aborted:1;
+} ngx_upload_cleanup_t;
+
+/*
* Upload configuration for specific location
*/
typedef struct ngx_http_upload_loc_conf_s {
@@ -109,12 +121,14 @@ typedef struct ngx_http_upload_loc_conf_s {
ngx_array_t *field_templates;
ngx_array_t *aggregate_field_templates;
ngx_array_t *field_filters;
+ ngx_array_t *cleanup_statuses;
ngx_array_t *content_filters;
ngx_array_t *content_type_map;
unsigned int md5:1;
unsigned int sha1:1;
+ unsigned int crc32:1;
} ngx_http_upload_loc_conf_t;
typedef struct ngx_http_upload_md5_ctx_s {
@@ -166,6 +180,7 @@ typedef struct ngx_http_upload_ctx_s {
ngx_http_upload_md5_ctx_t *md5_ctx;
ngx_http_upload_sha1_ctx_t *sha1_ctx;
+ uint32_t crc32;
ngx_array_t *current_content_filter_chain;
ngx_uint_t current_content_filter_idx;
@@ -173,10 +188,13 @@ typedef struct ngx_http_upload_ctx_s {
unsigned int first_part:1;
unsigned int discard_data:1;
unsigned int is_file:1;
+ unsigned int calculate_crc32:1;
} ngx_http_upload_ctx_t;
ngx_module_t ngx_http_upload_module;
+ngx_int_t ngx_upload_set_exten(ngx_http_upload_ctx_t *u, ngx_str_t *file_name, ngx_str_t *exten);
+ngx_int_t ngx_upload_resolve_content_type(ngx_http_upload_ctx_t *u, ngx_str_t *exten, ngx_str_t *content_type);
ngx_int_t ngx_upload_set_file_name(ngx_http_upload_ctx_t *ctx, ngx_str_t *file_name);
ngx_int_t ngx_upload_set_content_type(ngx_http_upload_ctx_t *ctx, ngx_str_t *content_type);
ngx_int_t ngx_upload_set_archive_path(ngx_http_upload_ctx_t *ctx, ngx_str_t *archive_path);
View
389 ngx_http_upload_module.c
@@ -24,6 +24,8 @@ 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_file_size_variable(ngx_http_request_t *r,
ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_upload_crc32_variable(ngx_http_request_t *r,
+ ngx_http_variable_value_t *v, uintptr_t data);
static char *ngx_http_upload_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static ngx_int_t ngx_http_upload_start_handler(ngx_http_upload_ctx_t *u);
@@ -45,6 +47,10 @@ static char *ngx_http_upload_set_form_field(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char *ngx_http_upload_pass_form_field(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
+static char *ngx_http_upload_cleanup(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static void ngx_upload_cleanup_handler(void *data);
+
static char *ngx_http_upload_filter_block(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
@@ -70,7 +76,7 @@ ngx_int_t ngx_http_unzip_data_handler(ngx_http_upload_ctx_t *u,
* upload_ctx -- upload context which is being initialized
*
*/
-void upload_init_ctx(ngx_http_upload_ctx_t *upload_ctx);
+static void upload_init_ctx(ngx_http_upload_ctx_t *upload_ctx);
/*
* upload_shutdown_ctx
@@ -82,7 +88,7 @@ void upload_init_ctx(ngx_http_upload_ctx_t *upload_ctx);
* upload_ctx -- upload context which is being shut down
*
*/
-void upload_shutdown_ctx(ngx_http_upload_ctx_t *upload_ctx);
+static void upload_shutdown_ctx(ngx_http_upload_ctx_t *upload_ctx);
/*
* upload_start
@@ -98,7 +104,7 @@ void upload_shutdown_ctx(ngx_http_upload_ctx_t *upload_ctx);
* NGX_ERROR if error has occured
*
*/
-ngx_int_t upload_start(ngx_http_upload_ctx_t *upload_ctx, ngx_http_upload_loc_conf_t *ulcf);
+static ngx_int_t upload_start(ngx_http_upload_ctx_t *upload_ctx, ngx_http_upload_loc_conf_t *ulcf);
/*
* upload_parse_content_type
@@ -114,7 +120,7 @@ ngx_int_t upload_start(ngx_http_upload_ctx_t *upload_ctx, ngx_http_upload_loc_co
* NGX_OK on success
* NGX_ERROR if error has occured
*/
-ngx_int_t upload_parse_content_type(ngx_http_upload_ctx_t *upload_ctx, ngx_str_t *content_type);
+static ngx_int_t upload_parse_content_type(ngx_http_upload_ctx_t *upload_ctx, ngx_str_t *content_type);
/*
* upload_process_buf
@@ -131,7 +137,7 @@ ngx_int_t upload_parse_content_type(ngx_http_upload_ctx_t *upload_ctx, ngx_str_t
* NGX_UPLOAD_SCRIPTERROR nginx script engine failed
* NGX_UPLOAD_TOOLARGE field body is too large
*/
-ngx_int_t upload_process_buf(ngx_http_upload_ctx_t *upload_ctx, u_char *start, u_char *end);
+static ngx_int_t upload_process_buf(ngx_http_upload_ctx_t *upload_ctx, u_char *start, u_char *end);
static ngx_upload_content_filter_t ngx_write_content_filter = {
ngx_http_upload_start_handler,
@@ -228,6 +234,18 @@ static ngx_command_t ngx_http_upload_commands[] = { /* {{{ */
NULL},
/*
+ * Specifies http statuses upon reception of
+ * which cleanup of uploaded files will be initiated
+ */
+ { ngx_string("upload_cleanup"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
+ |NGX_CONF_1MORE,
+ ngx_http_upload_cleanup,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL},
+
+ /*
* Specifies a content filter for files of specified mime type
*/
{ ngx_string("upload_filter"),
@@ -312,6 +330,10 @@ static ngx_http_variable_t ngx_http_upload_aggregate_variables[] = { /* {{{ */
(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 },
+
{ ngx_string("upload_file_size"), NULL, ngx_http_upload_file_size_variable,
(uintptr_t) offsetof(ngx_http_upload_ctx_t, output_file.offset),
NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
@@ -374,13 +396,7 @@ ngx_http_upload_handler(ngx_http_request_t *r)
}else
u->sha1_ctx = NULL;
- u->cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_pool_cleanup_file_t));
-
- if (u->cln == NULL) {
- return NGX_HTTP_INTERNAL_SERVER_ERROR;
- }
-
- u->cln->handler = NULL;
+ u->calculate_crc32 = ulcf->crc32;
// Check whether Content-Type header is missing
if(r->headers_in.content_type == NULL) {
@@ -517,9 +533,14 @@ static ngx_int_t ngx_http_upload_start_handler(ngx_http_upload_ctx_t *u) { /* {{
ngx_http_upload_field_filter_t *f;
ngx_str_t field_name, field_value;
ngx_uint_t pass_field;
- ngx_pool_cleanup_file_t *clnf;
+ ngx_upload_cleanup_t *ucln;
if(u->is_file) {
+ u->cln = ngx_pool_cleanup_add(r->pool, sizeof(ngx_upload_cleanup_t));
+
+ if(u->cln == NULL)
+ return NGX_UPLOAD_NOMEM;
+
file->name.len = path->name.len + 1 + path->len + 10;
file->name.data = ngx_palloc(u->request->pool, file->name.len + 1);
@@ -561,12 +582,15 @@ static ngx_int_t ngx_http_upload_start_handler(ngx_http_upload_ctx_t *u) { /* {{
return NGX_UPLOAD_IOERROR;
}
- u->cln->handler = ngx_pool_delete_file;
+ u->cln->handler = ngx_upload_cleanup_handler;
- clnf = u->cln->data;
- clnf->fd = file->fd;
- clnf->name = file->name.data;
- clnf->log = r->connection->log;
+ ucln = u->cln->data;
+ ucln->fd = file->fd;
+ ucln->filename = file->name.data;
+ ucln->log = r->connection->log;
+ ucln->headers_out = &r->headers_out;
+ ucln->cleanup_statuses = ulcf->cleanup_statuses;
+ ucln->aborted = 0;
if(ulcf->field_templates) {
t = ulcf->field_templates->elts;
@@ -607,6 +631,9 @@ static ngx_int_t ngx_http_upload_start_handler(ngx_http_upload_ctx_t *u) { /* {{
if(u->sha1_ctx != NULL)
SHA1_Init(&u->sha1_ctx->sha1);
+ if(u->calculate_crc32)
+ ngx_crc32_init(u->crc32);
+
ngx_log_error(NGX_LOG_INFO, r->connection->log, 0
, "started writing file \"%V\" to \"%V\" (field \"%V\", content type \"%V\")"
, &u->file_name
@@ -639,7 +666,7 @@ static ngx_int_t ngx_http_upload_start_handler(ngx_http_upload_ctx_t *u) { /* {{
}
}
- if(pass_field) {
+ if(pass_field && u->field_name.len > 0) {
/*
* Here we do a small hack: the content of a normal field
* is not known until ngx_http_upload_flush_output_buffer
@@ -656,7 +683,6 @@ static ngx_int_t ngx_http_upload_start_handler(ngx_http_upload_ctx_t *u) { /* {{
return NGX_OK;
cleanup_file:
- ngx_pool_delete_file(clnf);
return rc;
} /* }}} */
@@ -667,9 +693,11 @@ static void ngx_http_upload_finish_handler(ngx_http_upload_ctx_t *u) { /* {{{ */
ngx_http_upload_loc_conf_t *ulcf = ngx_http_get_module_loc_conf(r, ngx_http_upload_module);
ngx_uint_t i;
ngx_int_t rc;
+ ngx_upload_cleanup_t *ucln;
if(u->is_file) {
- u->cln->handler = NULL;
+ ucln = u->cln->data;
+ ucln->fd = -1;
ngx_close_file(u->output_file.fd);
@@ -685,12 +713,15 @@ static void ngx_http_upload_finish_handler(ngx_http_upload_ctx_t *u) { /* {{{ */
if(u->sha1_ctx)
SHA1_Final(u->sha1_ctx->sha1_digest, &u->sha1_ctx->sha1);
+ if(u->calculate_crc32)
+ ngx_crc32_final(u->crc32);
+
if(ulcf->aggregate_field_templates) {
af = ulcf->aggregate_field_templates->elts;
for (i = 0; i < ulcf->aggregate_field_templates->nelts; i++) {
if (af[i].field_lengths == NULL) {
- aggregate_field_value = af[i].value.value;
+ aggregate_field_name = af[i].value.key;
}else{
if (ngx_http_script_run(r, &aggregate_field_name, af[i].field_lengths->elts, 0,
af[i].field_values->elts) == NULL)
@@ -726,8 +757,20 @@ static void ngx_http_upload_finish_handler(ngx_http_upload_ctx_t *u) { /* {{{ */
} /* }}} */
static void ngx_http_upload_abort_handler(ngx_http_upload_ctx_t *u) { /* {{{ */
+ ngx_upload_cleanup_t *ucln;
+
if(u->is_file) {
- u->cln->handler = NULL;
+ /*
+ * Upload of a part could be aborted due to temporary reasons, thus
+ * next body part will be potentially processed successfuly.
+ *
+ * Therefore we don't postpone cleanup to the request finallization
+ * in order to save additional resources, instead we mark existing
+ * cleanup record as aborted.
+ */
+ ucln = u->cln->data;
+ ucln->fd = -1;
+ ucln->aborted = 1;
ngx_close_file(u->output_file.fd);
@@ -766,6 +809,9 @@ static ngx_int_t ngx_http_upload_process_chain(ngx_http_upload_ctx_t *u, ngx_cha
if(u->sha1_ctx)
SHA1_Update(&u->sha1_ctx->sha1, cl->buf->pos, cl->buf->last - cl->buf->pos);
+
+ if(u->calculate_crc32)
+ ngx_crc32_update(&u->crc32, cl->buf->pos, cl->buf->last - cl->buf->pos);
}
}
@@ -847,30 +893,34 @@ ngx_http_upload_append_field(ngx_http_upload_ctx_t *u, ngx_str_t *name, ngx_str_
ngx_buf_t *b;
ngx_chain_t *cl;
- b = ngx_palloc(u->request->pool, 5 * sizeof(ngx_buf_t));
+ if(name->len > 0) {
+ b = ngx_palloc(u->request->pool, value->len > 0 ?
+ 5 * sizeof(ngx_buf_t) : 4 * sizeof(ngx_buf_t));
- if (b == NULL) {
- return NGX_UPLOAD_NOMEM;
- }
+ if (b == NULL) {
+ return NGX_UPLOAD_NOMEM;
+ }
- cl = ngx_palloc(u->request->pool, 5 * sizeof(ngx_chain_t));
+ cl = ngx_palloc(u->request->pool, value->len > 0 ?
+ 5 * sizeof(ngx_chain_t) : 4 * sizeof(ngx_chain_t));
- if (cl == NULL) {
- return NGX_UPLOAD_NOMEM;
- }
+ if (cl == NULL) {
+ return NGX_UPLOAD_NOMEM;
+ }
- ngx_http_upload_append_str(u, b, cl, &boundary);
+ ngx_http_upload_append_str(u, b, cl, &boundary);
- ngx_http_upload_append_str(u, b + 1, cl + 1, &ngx_upload_field_part1);
+ ngx_http_upload_append_str(u, b + 1, cl + 1, &ngx_upload_field_part1);
- ngx_http_upload_append_str(u, b + 2, cl + 2, name);
+ ngx_http_upload_append_str(u, b + 2, cl + 2, name);
- ngx_http_upload_append_str(u, b + 3, cl + 3, &ngx_upload_field_part2);
+ ngx_http_upload_append_str(u, b + 3, cl + 3, &ngx_upload_field_part2);
- if(value->len > 0)
- ngx_http_upload_append_str(u, b + 4, cl + 4, value);
+ if(value->len > 0)
+ ngx_http_upload_append_str(u, b + 4, cl + 4, value);
- u->first_part = 0;
+ u->first_part = 0;
+ }
return NGX_OK;
} /* }}} */
@@ -934,14 +984,22 @@ ngx_http_upload_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
if(prev->md5) {
conf->md5 = prev->md5;
}
+
+ if(prev->sha1) {
+ conf->sha1 = prev->sha1;
+ }
+
+ if(prev->crc32) {
+ conf->crc32 = prev->crc32;
+ }
}
if(conf->field_filters == NULL) {
conf->field_filters = prev->field_filters;
+ }
- if(prev->sha1) {
- conf->sha1 = prev->sha1;
- }
+ if(conf->cleanup_statuses == NULL) {
+ conf->cleanup_statuses = prev->cleanup_statuses;
}
return NGX_CONF_OK;
@@ -1070,6 +1128,32 @@ ngx_http_upload_sha1_variable(ngx_http_request_t *r,
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)
+{
+ ngx_http_upload_ctx_t *u;
+ u_char *p;
+ uint32_t *value;
+
+ u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
+
+ value = (uint32_t *) ((char *) u + data);
+
+ p = ngx_palloc(r->pool, NGX_INT_T_LEN);
+ if (p == NULL) {
+ return NGX_ERROR;
+ }
+
+ v->len = ngx_sprintf(p, "%08uxd", *value) - p;
+ v->valid = 1;
+ v->no_cacheable = 0;
+ v->not_found = 0;
+ v->data = p;
+
+ return NGX_OK;
+} /* }}} */
+
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)
@@ -1138,22 +1222,20 @@ ngx_http_upload_set_form_field(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
*/
n = ngx_http_script_variables_count(&value[1]);
- if (n == 0) {
- return NGX_CONF_OK;
- }
-
- ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+ if (n > 0) {
+ ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
- sc.cf = cf;
- sc.source = &value[1];
- sc.lengths = &h->field_lengths;
- sc.values = &h->field_values;
- sc.variables = n;
- sc.complete_lengths = 1;
- sc.complete_values = 1;
+ sc.cf = cf;
+ sc.source = &value[1];
+ sc.lengths = &h->field_lengths;
+ sc.values = &h->field_values;
+ sc.variables = n;
+ sc.complete_lengths = 1;
+ sc.complete_values = 1;
- if (ngx_http_script_compile(&sc) != NGX_OK) {
- return NGX_CONF_ERROR;
+ if (ngx_http_script_compile(&sc) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
}
/*
@@ -1161,22 +1243,20 @@ ngx_http_upload_set_form_field(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
*/
n = ngx_http_script_variables_count(&value[2]);
- if (n == 0) {
- return NGX_CONF_OK;
- }
+ if (n > 0) {
+ ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
- ngx_memzero(&sc, sizeof(ngx_http_script_compile_t));
+ sc.cf = cf;
+ sc.source = &value[2];
+ sc.lengths = &h->value_lengths;
+ sc.values = &h->value_values;
+ sc.variables = n;
+ sc.complete_lengths = 1;
+ sc.complete_values = 1;
- sc.cf = cf;
- sc.source = &value[2];
- sc.lengths = &h->value_lengths;
- sc.values = &h->value_values;
- sc.variables = n;
- sc.complete_lengths = 1;
- sc.complete_values = 1;
-
- if (ngx_http_script_compile(&sc) != NGX_OK) {
- return NGX_CONF_ERROR;
+ if (ngx_http_script_compile(&sc) != NGX_OK) {
+ return NGX_CONF_ERROR;
+ }
}
/*
@@ -1200,6 +1280,7 @@ ngx_http_upload_set_form_field(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
", upload_file_md5_uc"
", upload_file_sha1"
", upload_file_sha1_uc"
+ ", upload_file_crc32"
" and upload_file_size"
" could be specified only in upload_aggregate_form_field directive");
return NGX_CONF_ERROR;
@@ -1210,6 +1291,9 @@ ngx_http_upload_set_form_field(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
if(v->get_handler == ngx_http_upload_sha1_variable)
ulcf->sha1 = 1;
+
+ if(v->get_handler == ngx_http_upload_crc32_variable)
+ ulcf->crc32 = 1;
}
}
}
@@ -1270,6 +1354,84 @@ ngx_http_upload_pass_form_field(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
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,
+ "invalid upper bound \"%V\"", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ if (hi < lo) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "upper bound must be greater then lower bound in \"%V\"",
+ &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ }else{
+ status = ngx_atoi(value[i].data, value[i].len);
+
+ if (status == NGX_ERROR) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "invalid value \"%V\"", &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ hi = lo = status;
+ }
+
+ if (lo < 400 || hi > 599) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "value(s) \"%V\" must be between 400 and 599",
+ &value[i]);
+ return NGX_CONF_ERROR;
+ }
+
+ for(status = lo ; status <= hi; status++) {
+ s = ngx_array_push(ulcf->cleanup_statuses);
+ if (s == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *s = status;
+ }
+ }
+
+
+ return NGX_CONF_OK;
+} /* }}} */
+
static ngx_uint_t /* {{{ ngx_http_upload_add_slave_conf */
ngx_http_upload_add_slave_conf(ngx_str_t *content_type, ngx_array_t **content_type_map,
ngx_http_upload_loc_conf_t *ulcf, ngx_pool_t *pool)
@@ -1828,7 +1990,7 @@ static ngx_int_t upload_parse_part_header(ngx_http_upload_ctx_t *upload_ctx, cha
if(*fieldname_end != '\"') {
ngx_log_debug0(NGX_LOG_DEBUG_CORE, upload_ctx->log, 0,
- "malformed filename in part header");
+ "malformed fieldname in part header");
return NGX_UPLOAD_MALFORMED;
}
@@ -1875,23 +2037,23 @@ static void upload_discard_part_attributes(ngx_http_upload_ctx_t *upload_ctx) {
upload_ctx->content_type.data = NULL;
} /* }}} */
-static ngx_int_t /* {{{ ngx_upload_set_exten */
-ngx_upload_set_exten(ngx_http_upload_ctx_t *u, ngx_str_t *exten)
+ngx_int_t /* {{{ ngx_upload_set_exten */
+ngx_upload_set_exten(ngx_http_upload_ctx_t *u, ngx_str_t *file_name, ngx_str_t *exten)
{
ngx_int_t i;
exten->len = 0;
exten->data = NULL;
- for (i = u->file_name.len - 1; i > 1; i--) {
- if (u->file_name.data[i] == '.' && u->file_name.data[i - 1] != '/') {
+ for (i = file_name->len - 1; i > 1; i--) {
+ if (file_name->data[i] == '.' && file_name->data[i - 1] != '/') {
- exten->len = u->file_name.len - i - 1;
- exten->data = &u->file_name.data[i + 1];
+ exten->len = file_name->len - i - 1;
+ exten->data = &file_name->data[i + 1];
break;
- } else if (u->file_name.data[i] == '/') {
+ } else if (file_name->data[i] == '/') {
break;
}
}
@@ -1899,7 +2061,7 @@ ngx_upload_set_exten(ngx_http_upload_ctx_t *u, ngx_str_t *exten)
return NGX_OK;
} /* }}} */
-static ngx_int_t /* {{{ ngx_upload_resolve_content_type */
+ngx_int_t /* {{{ ngx_upload_resolve_content_type */
ngx_upload_resolve_content_type(ngx_http_upload_ctx_t *u, ngx_str_t *exten, ngx_str_t *content_type)
{
u_char c, *p, *_exten;
@@ -1966,6 +2128,16 @@ ngx_upload_set_file_name(ngx_http_upload_ctx_t *ctx,
return NGX_OK;
} /* }}} */
+ngx_int_t /* {{{ ngx_upload_set_content_type */
+ngx_upload_set_content_type(ngx_http_upload_ctx_t *ctx,
+ ngx_str_t *content_type)
+{
+ ctx->content_type.data = content_type->data;
+ ctx->content_type.len = content_type->len;
+
+ return NGX_OK;
+} /* }}} */
+
ngx_int_t /* {{{ ngx_upload_set_archive_path */
ngx_upload_set_archive_path(ngx_http_upload_ctx_t *ctx,
ngx_str_t *archive_path)
@@ -2018,16 +2190,10 @@ static ngx_int_t upload_start_file(ngx_http_upload_ctx_t *upload_ctx) { /* {{{ *
upload_ctx->archive_path.data = NULL;
upload_ctx->archive_path.len = 0;
- ngx_upload_set_exten(upload_ctx, &exten);
+ ngx_upload_set_exten(upload_ctx, &upload_ctx->file_name, &exten);
ngx_upload_resolve_content_type(upload_ctx, &exten, &content_type);
- ngx_log_debug2(NGX_LOG_INFO, upload_ctx->log, 0
- , "guessed content type by extension \"%V\" -> \"%V\""
- , &exten
- , &content_type
- );
-
ngx_upload_set_content_filter(upload_ctx, &content_type);
cflt = ngx_upload_get_next_content_filter(upload_ctx);
@@ -2098,7 +2264,7 @@ void upload_init_ctx(ngx_http_upload_ctx_t *upload_ctx) { /* {{{ */
upload_ctx->discard_data = 0;
} /* }}} */
-void upload_shutdown_ctx(ngx_http_upload_ctx_t *upload_ctx) { /* {{{ */
+static void upload_shutdown_ctx(ngx_http_upload_ctx_t *upload_ctx) { /* {{{ */
if(upload_ctx != 0) {
// Abort file if we still processing it
if(upload_ctx->state == upload_state_data) {
@@ -2110,7 +2276,7 @@ void upload_shutdown_ctx(ngx_http_upload_ctx_t *upload_ctx) { /* {{{ */
}
} /* }}} */
-ngx_int_t upload_start(ngx_http_upload_ctx_t *upload_ctx, ngx_http_upload_loc_conf_t *ulcf) { /* {{{ */
+static ngx_int_t upload_start(ngx_http_upload_ctx_t *upload_ctx, ngx_http_upload_loc_conf_t *ulcf) { /* {{{ */
if(upload_ctx == NULL)
return NGX_ERROR;
@@ -2134,7 +2300,7 @@ ngx_int_t upload_start(ngx_http_upload_ctx_t *upload_ctx, ngx_http_upload_loc_co
return NGX_OK;
} /* }}} */
-ngx_int_t upload_parse_content_type(ngx_http_upload_ctx_t *upload_ctx, ngx_str_t *content_type) { /* {{{ */
+static ngx_int_t upload_parse_content_type(ngx_http_upload_ctx_t *upload_ctx, ngx_str_t *content_type) { /* {{{ */
// Find colon in content type string, which terminates mime type
u_char *mime_type_end_ptr = (u_char*) ngx_strchr(content_type->data, ';');
u_char *boundary_start_ptr, *boundary_end_ptr;
@@ -2198,7 +2364,7 @@ ngx_int_t upload_parse_content_type(ngx_http_upload_ctx_t *upload_ctx, ngx_str_t
return NGX_OK;
} /* }}} */
-void upload_putc(ngx_http_upload_ctx_t *upload_ctx, u_char c) { /* {{{ */
+static void upload_putc(ngx_http_upload_ctx_t *upload_ctx, u_char c) { /* {{{ */
if(!upload_ctx->discard_data) {
*upload_ctx->output_buffer->pos++ = c;
@@ -2207,7 +2373,7 @@ void upload_putc(ngx_http_upload_ctx_t *upload_ctx, u_char c) { /* {{{ */
}
} /* }}} */
-ngx_int_t upload_process_buf(ngx_http_upload_ctx_t *upload_ctx, u_char *start, u_char *end) { /* {{{ */
+static ngx_int_t upload_process_buf(ngx_http_upload_ctx_t *upload_ctx, u_char *start, u_char *end) { /* {{{ */
u_char *p;
ngx_int_t rc;
@@ -2346,3 +2512,46 @@ ngx_int_t upload_process_buf(ngx_http_upload_ctx_t *upload_ctx, u_char *start, u
return NGX_OK;
} /* }}} */
+static void /* {{{ ngx_upload_cleanup_handler */
+ngx_upload_cleanup_handler(void *data)
+{
+ ngx_upload_cleanup_t *cln = data;
+ ngx_uint_t i;
+ uint16_t *s;
+ u_char do_cleanup = 0;
+
+ if(!cln->aborted) {
+ if(cln->fd >= 0) {
+ if (ngx_close_file(cln->fd) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ALERT, cln->log, ngx_errno,
+ ngx_close_file_n " \"%s\" failed", cln->filename);
+ }
+ }
+
+ if(cln->cleanup_statuses != NULL) {
+ s = cln->cleanup_statuses->elts;
+
+ for(i = 0; i < cln->cleanup_statuses->nelts; i++) {
+ if(cln->headers_out->status == s[i]) {
+ do_cleanup = 1;
+ }
+ }
+ }
+
+ if(do_cleanup) {
+ if(ngx_delete_file(cln->filename) == NGX_FILE_ERROR) {
+ ngx_log_error(NGX_LOG_ERR, cln->log, ngx_errno
+ , "failed to remove destination file \"%s\" after http status %l"
+ , cln->filename
+ , cln->headers_out->status
+ );
+ }else
+ ngx_log_error(NGX_LOG_INFO, cln->log, 0
+ , "finished cleanup of file \"%s\" after http status %l"
+ , cln->filename
+ , cln->headers_out->status
+ );
+ }
+ }
+} /* }}} */
+
Please sign in to comment.
Something went wrong with that request. Please try again.