2525#define ZIP_METHOD_STORED 0
2626#define ZIP_METHOD_DEFLATED 8
2727
28- #define ZIP_FLAG_HAVE_DATA_DESC 0x0008
28+ #define ZIP_FLAG_ENCRYPTED 0x0001
29+ #define ZIP_FLAG_UNLIMITED 0x0008
30+ #define ZIP_FLAG_RESERVED1 0x0010
31+ #define ZIP_FLAG_PATCH 0x0020
32+ #define ZIP_FLAG_STRONG_ENCRYPTION 0x0040
33+ #define ZIP_FLAG_USE_UTF8 0x0800
34+ #define ZIP_FLAG_RESERVED2 0x1000
35+ #define ZIP_FLAG_LOCAL_HEADER_OBFUSCATED 0x2000
36+ #define ZIP_FLAG_RESERVED3 0x4000
37+ #define ZIP_FLAG_RESERVED4 0x8000
2938
3039#define ZIP_VERSION 20
3140
@@ -62,6 +71,7 @@ typedef enum {
6271 unzip_state_file_name ,
6372 unzip_state_extra_field ,
6473 unzip_state_file_data ,
74+ unzip_state_unlimited_file_data ,
6575 unzip_state_data_descriptor ,
6676 unzip_state_decryption_header ,
6777 unzip_state_extra_data_record ,
@@ -143,7 +153,7 @@ typedef struct ngx_unzip_ctx_s {
143153
144154 uint16_t version_needed ;
145155 uint16_t flags ;
146- uint16_t compression_method_number ;
156+ uint16_t compression_method ;
147157 uint16_t last_mod_time ;
148158 uint16_t last_mod_date ;
149159 uint32_t crc32 ;
@@ -163,6 +173,8 @@ typedef struct ngx_unzip_ctx_s {
163173 ngx_upload_content_filter_t * next_content_filter ;
164174 ngx_unzip_decompression_method_t * decompression_method ;
165175
176+ uint32_t calculated_crc32 ;
177+
166178 unsigned int discard_data :1 ;
167179} ngx_unzip_ctx_t ;
168180
@@ -241,27 +253,48 @@ static ngx_command_t ngx_http_unzip_filter_commands[] = { /* {{{ */
241253 offsetof(ngx_unzip_conf_t , bufs ),
242254 NULL },
243255
256+ /*
257+ * Specifies size window to use for decompressing
258+ */
244259 { ngx_string ("unzip_window" ),
245260 NGX_HTTP_LOC_CONF |NGX_CONF_TAKE1 ,
246261 ngx_conf_set_size_slot ,
247262 NGX_HTTP_LOC_CONF_OFFSET ,
248263 offsetof(ngx_unzip_conf_t , wbits ),
249264 & ngx_http_unzip_window_p },
250265
266+ /*
267+ * Specifies a form field with a special content to generate
268+ * in output form
269+ */
251270 { ngx_string ("unzip_set_form_field" ),
252271 NGX_HTTP_LOC_CONF |NGX_CONF_TAKE2 ,
253272 ngx_conf_set_size_slot ,
254273 NGX_HTTP_LOC_CONF_OFFSET ,
255274 offsetof(ngx_unzip_conf_t , wbits ),
256275 NULL },
257276
277+ /*
278+ * Specifies a form field with a special aggregate content to generate
279+ * in output form
280+ */
258281 { ngx_string ("unzip_aggregate_form_field" ),
259282 NGX_HTTP_LOC_CONF |NGX_CONF_TAKE2 ,
260283 ngx_conf_set_size_slot ,
261284 NGX_HTTP_LOC_CONF_OFFSET ,
262285 offsetof(ngx_unzip_conf_t , wbits ),
263286 NULL },
264287
288+ /*
289+ * Specifies the maximal length of a file name in archive
290+ */
291+ { ngx_string ("unzip_max_file_name_len" ),
292+ NGX_HTTP_LOC_CONF |NGX_CONF_TAKE1 ,
293+ ngx_conf_set_size_slot ,
294+ NGX_HTTP_LOC_CONF_OFFSET ,
295+ offsetof(ngx_unzip_conf_t , max_file_name_len ),
296+ NULL },
297+
265298 ngx_null_command
266299}; /* }}} */
267300
@@ -323,10 +356,11 @@ static ngx_int_t /* {{{ ngx_http_unzip_process_chain */
323356ngx_http_unzip_process_chain (ngx_unzip_ctx_t * ctx , ngx_chain_t * chain ) {
324357 ngx_int_t result ;
325358 ngx_buf_t * buf ;
359+ ngx_unzip_conf_t * uzcf ;
326360
327- while (chain != NULL && !chain -> buf -> last_in_chain ) {
328- buf = chain -> buf ;
361+ uzcf = ngx_http_get_module_loc_conf (ctx -> upload_ctx -> request , ngx_http_unzip_filter_module );
329362
363+ while (chain != NULL ) {
330364 for (buf = chain -> buf ; buf -> pos != buf -> last ; buf -> pos ++ ) {
331365 switch (ctx -> state ) {
332366 case unzip_state_signature : /* {{{ */
@@ -375,7 +409,7 @@ ngx_http_unzip_process_chain(ngx_unzip_ctx_t *ctx, ngx_chain_t *chain) {
375409
376410 ctx -> version_needed = EXTRACT_SHORT (ctx -> current_field );
377411 ctx -> flags = EXTRACT_SHORT (ctx -> current_field + 2 );
378- ctx -> compression_method_number = EXTRACT_SHORT (ctx -> current_field + 4 );
412+ ctx -> compression_method = EXTRACT_SHORT (ctx -> current_field + 4 );
379413 ctx -> last_mod_time = EXTRACT_SHORT (ctx -> current_field + 6 );
380414 ctx -> last_mod_date = EXTRACT_SHORT (ctx -> current_field + 8 );
381415 ctx -> crc32 = EXTRACT_LONG (ctx -> current_field + 10 );
@@ -385,24 +419,34 @@ ngx_http_unzip_process_chain(ngx_unzip_ctx_t *ctx, ngx_chain_t *chain) {
385419 ctx -> file_name_len = EXTRACT_SHORT (ctx -> current_field + 22 );
386420 ctx -> extra_field_len = EXTRACT_SHORT (ctx -> current_field + 24 );
387421
422+ if (uzcf -> max_file_name_len > 0 && ctx -> file_name_len > uzcf -> max_file_name_len ) {
423+ ngx_log_error (NGX_LOG_ALERT , ctx -> log , 0 ,
424+ "file name in is too long: %u" , ctx -> file_name_len );
425+ return NGX_UNZIP_MALFORMED ;
426+ }
427+
388428 if (ngx_http_unzip_set_decompression_method (ctx ,
389- ctx -> compression_method_number ) != NGX_OK )
429+ ctx -> compression_method ) != NGX_OK )
390430 {
391431 return NGX_UNZIP_MALFORMED ;
392432 }
393433
394434 if (ctx -> version_needed > ZIP_VERSION )
395435 {
436+ ngx_log_error (NGX_LOG_ALERT , ctx -> log , 0 ,
437+ "more recent version of unzip implementation required: %u, have %u" , ctx -> version_needed , ZIP_VERSION );
396438 return NGX_UNZIP_MALFORMED ;
397439 }
398440
399441 if (ctx -> file_name_len > 0 )
400442 ctx -> state = unzip_state_file_name ;
401443 else if (ctx -> extra_field_len > 0 )
402444 ctx -> state = unzip_state_extra_field ;
445+ else if (ctx -> flags & ZIP_FLAG_UNLIMITED )
446+ ctx -> state = unzip_state_unlimited_file_data ;
403447 else if (ctx -> compressed_size > 0 )
404448 ctx -> state = unzip_state_file_data ;
405- else if (ctx -> flags & ZIP_FLAG_HAVE_DATA_DESC )
449+ else if (ctx -> flags & ZIP_FLAG_UNLIMITED )
406450 ctx -> state = unzip_state_data_descriptor ;
407451 else
408452 ctx -> state = unzip_state_signature ;
@@ -412,7 +456,7 @@ ngx_http_unzip_process_chain(ngx_unzip_ctx_t *ctx, ngx_chain_t *chain) {
412456 if (ctx -> current_field_pos == 0 ) {
413457 ctx -> file_name .len = ctx -> file_name_len ;
414458
415- ctx -> file_name .data = ngx_palloc (ctx -> pool , ctx -> file_name_len + 1 );
459+ ctx -> file_name .data = ngx_palloc (ctx -> pool , ctx -> file_name_len );
416460
417461 if (ctx -> file_name .data == NULL ) {
418462 return NGX_UPLOAD_NOMEM ;
@@ -428,17 +472,17 @@ ngx_http_unzip_process_chain(ngx_unzip_ctx_t *ctx, ngx_chain_t *chain) {
428472 if (ctx -> current_field_pos == ctx -> current_field_len ) {
429473 ctx -> current_field_pos = 0 ;
430474
431- * ctx -> current_field_ptr = '\0' ;
432-
433475 if (ngx_http_unzip_parse_file_name (ctx , & ctx -> file_name ) != NGX_OK ) {
434476 return NGX_UNZIP_MALFORMED ;
435477 }
436478
437479 if (ctx -> extra_field_len > 0 )
438480 ctx -> state = unzip_state_extra_field ;
481+ else if (ctx -> flags & ZIP_FLAG_UNLIMITED )
482+ ctx -> state = unzip_state_unlimited_file_data ;
439483 else if (ctx -> compressed_size > 0 )
440484 ctx -> state = unzip_state_file_data ;
441- else if (ctx -> flags & ZIP_FLAG_HAVE_DATA_DESC )
485+ else if (ctx -> flags & ZIP_FLAG_UNLIMITED )
442486 ctx -> state = unzip_state_data_descriptor ;
443487 else
444488 ctx -> state = unzip_state_signature ;
@@ -454,15 +498,17 @@ ngx_http_unzip_process_chain(ngx_unzip_ctx_t *ctx, ngx_chain_t *chain) {
454498 if (ctx -> current_field_pos == ctx -> current_field_len ) {
455499 ctx -> current_field_pos = 0 ;
456500
457- if (ctx -> compressed_size > 0 )
501+ if (ctx -> flags & ZIP_FLAG_UNLIMITED )
502+ ctx -> state = unzip_state_unlimited_file_data ;
503+ else if (ctx -> compressed_size > 0 )
458504 ctx -> state = unzip_state_file_data ;
459- else if (ctx -> flags & ZIP_FLAG_HAVE_DATA_DESC )
505+ else if (ctx -> flags & ZIP_FLAG_UNLIMITED )
460506 ctx -> state = unzip_state_data_descriptor ;
461507 else
462508 ctx -> state = unzip_state_signature ;
463509 }
464510 break ;
465- case unzip_state_file_data :
511+ case unzip_state_file_data : /* {{{ */
466512 if (ctx -> current_field_pos == 0 ) {
467513 ctx -> current_field_len = ctx -> compressed_size ;
468514
@@ -496,12 +542,43 @@ ngx_http_unzip_process_chain(ngx_unzip_ctx_t *ctx, ngx_chain_t *chain) {
496542
497543 ctx -> current_field_pos = 0 ;
498544
499- if (ctx -> flags & ZIP_FLAG_HAVE_DATA_DESC )
545+ ctx -> state = unzip_state_signature ;
546+ }
547+ break ; /* }}} */
548+ case unzip_state_unlimited_file_data : /* {{{ */
549+ if (ctx -> current_field_pos == 0 ) {
550+ if (ctx -> decompression_method -> start (ctx ) != NGX_OK ) {
551+ ctx -> discard_data = 1 ;
552+ }
553+
554+ ctx -> current_field_pos = 1 ;
555+ }
556+
557+ if (!ctx -> discard_data ) {
558+ result = ctx -> decompression_method -> process_chain (ctx , chain );
559+
560+ buf -> pos -- ;
561+
562+ if (result == NGX_AGAIN ) {
563+ return NGX_AGAIN ;
564+ }
565+
566+ if (result != NGX_OK ) {
567+ ctx -> discard_data = 1 ;
568+
569+ ctx -> decompression_method -> abort (ctx );
570+
571+ return NGX_UNZIP_MALFORMED ;
572+ }else {
573+ if (!ctx -> discard_data )
574+ ctx -> decompression_method -> finish (ctx );
575+
576+ ctx -> current_field_pos = 0 ;
577+
500578 ctx -> state = unzip_state_data_descriptor ;
501- else
502- ctx -> state = unzip_state_signature ;
579+ }
503580 }
504- break ;
581+ break ; /* }}} */
505582 case unzip_state_data_descriptor :
506583 if (ctx -> current_field_pos == 0 ) {
507584 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) {
513590
514591 if (ctx -> current_field_pos == ctx -> current_field_len ) {
515592 ctx -> current_field_pos = 0 ;
593+
594+ ctx -> crc32 = EXTRACT_LONG (ctx -> current_field );
595+ ctx -> compressed_size = EXTRACT_LONG (ctx -> current_field + 4 );
596+ ctx -> uncompressed_size = EXTRACT_LONG (ctx -> current_field + 8 );
597+
516598 ctx -> state = unzip_state_signature ;
517599 }
518600 break ;
@@ -746,6 +828,8 @@ ngx_http_unzip_inflate_start(ngx_unzip_ctx_t *ctx) {
746828 return rc ;
747829 }
748830
831+ ngx_crc32_init (ctx -> calculated_crc32 );
832+
749833 return NGX_OK ;
750834cleanup :
751835 inflateEnd (& ctx -> stream );
@@ -754,6 +838,8 @@ ngx_http_unzip_inflate_start(ngx_unzip_ctx_t *ctx) {
754838
755839static void /* {{{ ngx_http_unzip_inflate_finish */
756840ngx_http_unzip_inflate_finish (ngx_unzip_ctx_t * ctx ) {
841+ ngx_crc32_final (ctx -> calculated_crc32 );
842+
757843 if (ctx -> next_content_filter -> finish )
758844 ctx -> next_content_filter -> finish (ctx -> upload_ctx );
759845
@@ -798,6 +884,9 @@ ngx_http_unzip_inflate_process_chain(ngx_unzip_ctx_t *ctx, ngx_chain_t *chain) {
798884 if (rc == Z_OK || rc == Z_STREAM_END ) {
799885 ctx -> output_buffer -> last = ctx -> stream .next_out ;
800886
887+ ngx_crc32_update (& ctx -> calculated_crc32 , ctx -> output_buffer -> pos ,
888+ ctx -> output_buffer -> last - ctx -> output_buffer -> pos );
889+
801890 if (ctx -> next_content_filter -> process_chain )
802891 ctx -> next_content_filter -> process_chain (ctx -> upload_ctx ,
803892 ctx -> output_chain );
@@ -887,6 +976,8 @@ ngx_http_unzip_parse_file_name(ngx_unzip_ctx_t *ctx, ngx_str_t *file_name) {
887976
888977 ngx_str_t archive_path = { file_name -> len , file_name -> data };
889978 ngx_str_t element_name = { file_name -> len , file_name -> data };
979+ ngx_str_t exten ;
980+ ngx_str_t content_type ;
890981
891982 for (p = file_name -> data + file_name -> len - 1 ; p >= file_name -> data ; p -- , archive_path .len -- ) {
892983 if (* p == '/' ) {
@@ -906,6 +997,24 @@ ngx_http_unzip_parse_file_name(ngx_unzip_ctx_t *ctx, ngx_str_t *file_name) {
906997 return rc ;
907998 }
908999
1000+ rc = ngx_upload_set_exten (ctx -> upload_ctx , & element_name , & exten );
1001+
1002+ if (rc != NGX_OK ) {
1003+ return rc ;
1004+ }
1005+
1006+ rc = ngx_upload_resolve_content_type (ctx -> upload_ctx , & exten , & content_type );
1007+
1008+ if (rc != NGX_OK ) {
1009+ return rc ;
1010+ }
1011+
1012+ rc = ngx_upload_set_content_type (ctx -> upload_ctx , & content_type );
1013+
1014+ if (rc != NGX_OK ) {
1015+ return rc ;
1016+ }
1017+
9091018 rc = ngx_upload_set_archive_path (ctx -> upload_ctx , & archive_path );
9101019
9111020 if (rc != NGX_OK ) {
0 commit comments