<?xml version="1.0" encoding="UTF-8"?>
<commit>
  <added type="array"/>
  <modified type="array">
    <modified>
      <diff>@@ -1,4 +1,13 @@
 
+Version 2.0.4
+ * Fixed bug: location configuration of upload_set_form_field and upload_pass_form_field
+   was not inheritable from server configuration.
+ * Added feature: directive upload_aggregate_form_field to pass aggragate properties
+   of a file like file size, MD5 and SHA1 sums to backend. MD5 and SHA1 sums are calculated
+   on request for file content. When no calculation is requested, additional memory
+   and CPU are not used. Calculation could be requested via specifying variables
+   $upload_file_md5, $upload_file_md5_uc, $upload_file_sha1 and $upload_file_sha1_uc.
+
 Version 2.0.3
  * upload_store directive was not able to receive more than one argument.
    As a result no hashed dirs for file uploads were possible.</diff>
      <filename>Changelog</filename>
    </modified>
    <modified>
      <diff>@@ -1,4 +1,4 @@
-* Copyright (c) 2008, Valery Kholodkov
+* Copyright (c) 2006, 2008, Valery Kholodkov
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without</diff>
      <filename>LICENCE</filename>
    </modified>
    <modified>
      <diff>@@ -1,3 +1,5 @@
+USE_MD5=YES
+USE_SHA1=YES
 ngx_addon_name=ngx_http_upload_module
 HTTP_MODULES=&quot;$HTTP_MODULES ngx_http_upload_module&quot;
 NGX_ADDON_SRCS=&quot;$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_upload_module.c&quot;</diff>
      <filename>config</filename>
    </modified>
    <modified>
      <diff>@@ -7,6 +7,24 @@
 #include &lt;ngx_core.h&gt;
 #include &lt;ngx_http.h&gt;
 
+#if (NGX_HAVE_OPENSSL_MD5_H)
+#include &lt;openssl/md5.h&gt;
+#else
+#include &lt;md5.h&gt;
+#endif
+
+#if (NGX_OPENSSL_MD5)
+#define  MD5Init    MD5_Init
+#define  MD5Update  MD5_Update
+#define  MD5Final   MD5_Final
+#endif
+
+#if (NGX_HAVE_OPENSSL_SHA1_H)
+#include &lt;openssl/sha.h&gt;
+#else
+#include &lt;sha.h&gt;
+#endif
+
 #define MULTIPART_FORM_DATA_STRING              &quot;multipart/form-data&quot;
 #define BOUNDARY_STRING                         &quot;boundary=&quot;
 #define CONTENT_DISPOSITION_STRING              &quot;Content-Disposition:&quot;
@@ -36,7 +54,7 @@ typedef enum {
  * Template for a field to generate in output form
  */
 typedef struct {
-    ngx_table_elt_t          value;
+    ngx_table_elt_t         value;
     ngx_array_t             *field_lengths;
     ngx_array_t             *field_values;
     ngx_array_t             *value_lengths;
@@ -64,10 +82,27 @@ typedef struct {
     ngx_uint_t        store_access;
     size_t            buffer_size;
     size_t            max_header_len;
+    size_t            max_output_body_len;
     ngx_array_t       *field_templates;
+    ngx_array_t       *aggregate_field_templates;
     ngx_array_t       *field_filters;
+
+    unsigned int      md5:1;
+    unsigned int      sha1:1;
 } ngx_http_upload_loc_conf_t;
 
+typedef struct ngx_http_upload_md5_ctx_s {
+    MD5_CTX     md5;
+    u_char      md5_digest[MD5_DIGEST_LENGTH];
+    u_char      md5_digest_hex[MD5_DIGEST_LENGTH * 2];
+} ngx_http_upload_md5_ctx_t;
+
+typedef struct ngx_http_upload_sha1_ctx_s {
+    SHA1_CTX    sha1;
+    u_char      sha1_digest[SHA_DIGEST_LENGTH];
+    u_char      sha1_digest_hex[SHA_DIGEST_LENGTH * 2];
+} ngx_http_upload_sha1_ctx_t;
+
 /*
  * Upload module context
  */
@@ -90,8 +125,6 @@ typedef struct ngx_http_upload_ctx_s {
     u_char              *output_buffer_end;
     u_char              *output_buffer_pos;
 
-    ngx_pool_t          *pool;
-
     ngx_int_t (*start_part_f)(struct ngx_http_upload_ctx_s *upload_ctx);
     void (*finish_part_f)(struct ngx_http_upload_ctx_s *upload_ctx);
     void (*abort_part_f)(struct ngx_http_upload_ctx_s *upload_ctx);
@@ -105,6 +138,9 @@ typedef struct ngx_http_upload_ctx_s {
     ngx_chain_t         *last;
     ngx_chain_t         *checkpoint;
 
+    ngx_http_upload_md5_ctx_t   *md5_ctx;    
+    ngx_http_upload_sha1_ctx_t  *sha1_ctx;    
+
     unsigned int        first_part:1;
     unsigned int        discard_data:1;
     unsigned int        is_file:1;
@@ -118,7 +154,11 @@ static char *ngx_http_upload_merge_loc_conf(ngx_conf_t *cf,
     void *parent, void *child);
 static ngx_int_t ngx_http_upload_add_variables(ngx_conf_t *cf);
 static ngx_int_t ngx_http_upload_variable(ngx_http_request_t *r,
-    ngx_http_variable_value_t *v,  uintptr_t data);
+    ngx_http_variable_value_t *v, uintptr_t data);
+static ngx_int_t ngx_http_upload_md5_variable(ngx_http_request_t *r,
+    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 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);
@@ -209,15 +249,14 @@ ngx_int_t upload_parse_content_type(ngx_http_upload_ctx_t *upload_ctx, ngx_str_t
  *
  * Process buffer with multipart stream starting from start and terminating
  * by end, operating on upload_ctx. The header information is accumulated in
- * upload_ctx and could be retrieved using upload_get_file_content_type,
- * upload_get_file_name, upload_get_field_name functions. This call can issue
- * one or more calls to start_upload_file, finish_upload_file, abort_upload_file
- * and flush_output_buffer routines.
+ * This call can invoke one or more calls to start_upload_file, finish_upload_file,
+ * abort_upload_file and flush_output_buffer routines.
  *
- * Returns value &gt; 0 if context is ready to process next portion of data,
- *               = 0 if processing finished and remaining data could be discarded,
- *               -1 stream is malformed
- *               -2 insufficient memory 
+ * Returns value NGX_OK successful
+ *               NGX_UPLOAD_MALFORMED stream is malformed
+ *               NGX_UPLOAD_NOMEM insufficient memory 
+ *               NGX_UPLOAD_IOERROR input-output error
+ *               NGX_UPLOAD_SCRIPTERROR nginx script engine failed
  */
 int upload_process_buf(ngx_http_upload_ctx_t *upload_ctx, u_char *start, u_char *end);
 
@@ -275,6 +314,16 @@ static ngx_command_t  ngx_http_upload_commands[] = { /* {{{ */
       NULL },
 
     /*
+     * Specifies the maximal length of resulting body
+     */
+    { ngx_string(&quot;upload_max_output_body_len&quot;),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_size_slot,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_upload_loc_conf_t, max_output_body_len),
+      NULL },
+
+    /*
      * Specifies the field to set in altered response body
      */
     { ngx_string(&quot;upload_set_form_field&quot;),
@@ -282,7 +331,19 @@ static ngx_command_t  ngx_http_upload_commands[] = { /* {{{ */
                         |NGX_CONF_TAKE2,
       ngx_http_upload_set_form_field,
       NGX_HTTP_LOC_CONF_OFFSET,
-      0,
+      offsetof(ngx_http_upload_loc_conf_t, field_templates),
+      NULL},
+
+    /*
+     * Specifies the field with aggregate parameters
+     * to set in altered response body
+     */
+    { ngx_string(&quot;upload_aggregate_form_field&quot;),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
+                        |NGX_CONF_TAKE2,
+      ngx_http_upload_set_form_field,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      offsetof(ngx_http_upload_loc_conf_t, aggregate_field_templates),
       NULL},
 
     /*
@@ -309,14 +370,14 @@ ngx_http_module_t  ngx_http_upload_module_ctx = { /* {{{ */
     NULL,                                  /* create server configuration */
     NULL,                                  /* merge server configuration */
 
-    ngx_http_upload_create_loc_conf,          /* create location configuration */
-    ngx_http_upload_merge_loc_conf            /* merge location configuration */
+    ngx_http_upload_create_loc_conf,       /* create location configuration */
+    ngx_http_upload_merge_loc_conf         /* merge location configuration */
 }; /* }}} */
 
 ngx_module_t  ngx_http_upload_module = { /* {{{ */
     NGX_MODULE_V1,
-    &amp;ngx_http_upload_module_ctx,              /* module context */
-    ngx_http_upload_commands,                 /* module directives */
+    &amp;ngx_http_upload_module_ctx,           /* module context */
+    ngx_http_upload_commands,              /* module directives */
     NGX_HTTP_MODULE,                       /* module type */
     NULL,                                  /* init master */
     NULL,                                  /* init module */
@@ -328,10 +389,47 @@ ngx_module_t  ngx_http_upload_module = { /* {{{ */
     NGX_MODULE_V1_PADDING
 }; /* }}} */
 
-static ngx_str_t  ngx_http_upload_field_name = ngx_string(&quot;upload_field_name&quot;);
-static ngx_str_t  ngx_http_upload_content_type = ngx_string(&quot;upload_content_type&quot;);
-static ngx_str_t  ngx_http_upload_file_name = ngx_string(&quot;upload_file_name&quot;);
-static ngx_str_t  ngx_http_upload_tmp_path = ngx_string(&quot;upload_tmp_path&quot;);
+static ngx_http_variable_t  ngx_http_upload_variables[] = { /* {{{ */
+
+    { ngx_string(&quot;upload_field_name&quot;), NULL, ngx_http_upload_variable,
+      (uintptr_t) offsetof(ngx_http_upload_ctx_t, field_name),
+      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
+
+    { ngx_string(&quot;upload_content_type&quot;), NULL, ngx_http_upload_variable,
+      (uintptr_t) offsetof(ngx_http_upload_ctx_t, content_type),
+      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
+
+    { ngx_string(&quot;upload_file_name&quot;), NULL, ngx_http_upload_variable,
+      (uintptr_t) offsetof(ngx_http_upload_ctx_t, file_name),
+      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
+
+    { ngx_string(&quot;upload_tmp_path&quot;), NULL, ngx_http_upload_variable,
+      (uintptr_t) offsetof(ngx_http_upload_ctx_t, output_file.name),
+      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
+
+    { ngx_null_string, NULL, NULL, 0, 0, 0 }
+}; /* }}} */
+
+static ngx_http_variable_t  ngx_http_upload_aggregate_variables[] = { /* {{{ */
+
+    { ngx_string(&quot;upload_file_md5&quot;), NULL, ngx_http_upload_md5_variable,
+      (uintptr_t) &quot;0123456789abcdef&quot;,
+      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
+
+    { ngx_string(&quot;upload_file_md5_uc&quot;), NULL, ngx_http_upload_md5_variable,
+      (uintptr_t) &quot;0123456789ABCDEF&quot;,
+      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
+
+    { ngx_string(&quot;upload_file_sha1&quot;), NULL, ngx_http_upload_sha1_variable,
+      (uintptr_t) &quot;0123456789abcdef&quot;,
+      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
+
+    { ngx_string(&quot;upload_file_sha1_uc&quot;), NULL, ngx_http_upload_sha1_variable,
+      (uintptr_t) &quot;0123456789ABCDEF&quot;,
+      NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 },
+
+    { ngx_null_string, NULL, NULL, 0, 0, 0 }
+}; /* }}} */
 
 static ngx_str_t  ngx_http_upload_empty_field_value = ngx_null_string;
 
@@ -355,6 +453,20 @@ ngx_http_upload_handler(ngx_http_request_t *r)
             return NGX_HTTP_INTERNAL_SERVER_ERROR;
         }
 
+        if(ulcf-&gt;md5) {
+            u-&gt;md5_ctx = ngx_pcalloc(r-&gt;pool, sizeof(ngx_http_upload_md5_ctx_t));
+            if (u-&gt;md5_ctx == NULL) {
+                return NGX_HTTP_INTERNAL_SERVER_ERROR;
+            }
+        }
+
+        if(ulcf-&gt;sha1) {
+            u-&gt;sha1_ctx = ngx_pcalloc(r-&gt;pool, sizeof(ngx_http_upload_sha1_ctx_t));
+            if (u-&gt;sha1_ctx == NULL) {
+                return NGX_HTTP_INTERNAL_SERVER_ERROR;
+            }
+        }
+
         ngx_http_set_ctx(r, u, ngx_http_upload_module);
     }
 
@@ -367,7 +479,6 @@ ngx_http_upload_handler(ngx_http_request_t *r)
 
     u-&gt;request = r;
     u-&gt;log = r-&gt;connection-&gt;log;
-    u-&gt;pool = r-&gt;pool;
     u-&gt;chain = u-&gt;last = u-&gt;checkpoint = NULL;
 
     upload_init_ctx(u);
@@ -492,14 +603,14 @@ static ngx_int_t ngx_http_upload_start_handler(ngx_http_upload_ctx_t *u) { /* {{
     if(u-&gt;is_file) {
         file-&gt;name.len = path-&gt;name.len + 1 + path-&gt;len + 10;
 
-        file-&gt;name.data = ngx_palloc(u-&gt;pool, file-&gt;name.len + 1);
+        file-&gt;name.data = ngx_palloc(u-&gt;request-&gt;pool, file-&gt;name.len + 1);
 
         if(file-&gt;name.data == NULL)
             return NGX_UPLOAD_NOMEM;
 
         ngx_memcpy(file-&gt;name.data, path-&gt;name.data, path-&gt;name.len);
 
-        file-&gt;log = u-&gt;log;
+        file-&gt;log = r-&gt;connection-&gt;log;
 
         for(;;) {
             n = (uint32_t) ngx_next_temp_number(0);
@@ -526,7 +637,7 @@ static ngx_int_t ngx_http_upload_start_handler(ngx_http_upload_ctx_t *u) { /* {{
                 continue;
             }
 
-            ngx_log_error(NGX_LOG_ERR, u-&gt;log, ngx_errno,
+            ngx_log_error(NGX_LOG_ERR, r-&gt;connection-&gt;log, ngx_errno,
                           &quot;failed to create output file \&quot;%s\&quot; for \&quot;%s\&quot;&quot;, file-&gt;name.data, u-&gt;file_name.data);
             return NGX_UPLOAD_IOERROR;
         }
@@ -562,7 +673,13 @@ static ngx_int_t ngx_http_upload_start_handler(ngx_http_upload_ctx_t *u) { /* {{
             }
         }
 
-        ngx_log_error(NGX_LOG_INFO, u-&gt;log, 0
+        if(u-&gt;md5_ctx != NULL)
+            MD5Init(&amp;u-&gt;md5_ctx-&gt;md5);
+
+        if(u-&gt;sha1_ctx != NULL)
+            SHA1_Init(&amp;u-&gt;sha1_ctx-&gt;sha1);
+
+        ngx_log_error(NGX_LOG_INFO, r-&gt;connection-&gt;log, 0
             , &quot;started uploading file \&quot;%s\&quot; to \&quot;%s\&quot; (field \&quot;%s\&quot;, content type \&quot;%s\&quot;)&quot;
             , u-&gt;file_name.data
             , u-&gt;output_file.name.data
@@ -612,14 +729,58 @@ static ngx_int_t ngx_http_upload_start_handler(ngx_http_upload_ctx_t *u) { /* {{
 } /* }}} */
 
 static void ngx_http_upload_finish_handler(ngx_http_upload_ctx_t *u) { /* {{{ */
+    ngx_http_upload_field_template_t    *af;
+    ngx_str_t   aggregate_field_name, aggregate_field_value;
+    ngx_http_request_t        *r = u-&gt;request;
+    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;
+
     if(u-&gt;is_file) {
         ngx_close_file(u-&gt;output_file.fd);
 
-        ngx_log_error(NGX_LOG_INFO, u-&gt;log, 0
+        ngx_log_error(NGX_LOG_INFO, r-&gt;connection-&gt;log, 0
             , &quot;finished uploading file \&quot;%s\&quot; to \&quot;%s\&quot;&quot;
             , u-&gt;file_name.data
             , u-&gt;output_file.name.data
             );
+
+        if(u-&gt;md5_ctx)
+            MD5Final(u-&gt;md5_ctx-&gt;md5_digest, &amp;u-&gt;md5_ctx-&gt;md5);
+
+        if(u-&gt;sha1_ctx)
+            SHA1_Final(u-&gt;sha1_ctx-&gt;sha1_digest, &amp;u-&gt;sha1_ctx-&gt;sha1);
+
+        if(ulcf-&gt;aggregate_field_templates) {
+            af = ulcf-&gt;aggregate_field_templates-&gt;elts;
+            for (i = 0; i &lt; ulcf-&gt;aggregate_field_templates-&gt;nelts; i++) {
+
+                if (af[i].field_lengths == NULL) {
+                    aggregate_field_value = af[i].value.value;
+                }else{
+                    if (ngx_http_script_run(r, &amp;aggregate_field_name, af[i].field_lengths-&gt;elts, 0,
+                        af[i].field_values-&gt;elts) == NULL)
+                    {
+                        return;
+                    }
+                }
+
+                if (af[i].value_lengths == NULL) {
+                    aggregate_field_value = af[i].value.value;
+                }else{
+                    if (ngx_http_script_run(r, &amp;aggregate_field_value, af[i].value_lengths-&gt;elts, 0,
+                        af[i].value_values-&gt;elts) == NULL)
+                    {
+                        return;
+                    }
+                }
+
+                rc = ngx_http_upload_append_field(u, &amp;aggregate_field_name, &amp;aggregate_field_value);
+
+                if(rc != NGX_OK)
+                    return;
+            }
+        }
     }
 
     // Checkpoint current output chain state
@@ -659,6 +820,12 @@ static ngx_int_t ngx_http_upload_flush_output_buffer(ngx_http_upload_ctx_t *u, u
     ngx_chain_t                    *cl;
 
     if(u-&gt;is_file) {
+        if(u-&gt;md5_ctx)
+            MD5Update(&amp;u-&gt;md5_ctx-&gt;md5, buf, len);
+
+        if(u-&gt;sha1_ctx)
+            SHA1_Update(&amp;u-&gt;sha1_ctx-&gt;sha1, buf, len);
+
         if(ngx_write_file(&amp;u-&gt;output_file, buf, len, u-&gt;output_file.offset) == NGX_ERROR) {
             ngx_log_error(NGX_LOG_ERR, u-&gt;log, ngx_errno,
                            &quot;write to file \&quot;%s\&quot; failed&quot;, u-&gt;output_file.name.data);
@@ -666,13 +833,13 @@ static ngx_int_t ngx_http_upload_flush_output_buffer(ngx_http_upload_ctx_t *u, u
         }else
             return NGX_OK;
     }else{
-        b = ngx_create_temp_buf(u-&gt;pool, len);
+        b = ngx_create_temp_buf(u-&gt;request-&gt;pool, len);
 
         if (b == NULL) {
             return NGX_ERROR;
         }
 
-        cl = ngx_alloc_chain_link(u-&gt;pool);
+        cl = ngx_alloc_chain_link(u-&gt;request-&gt;pool);
         if (cl == NULL) {
             return NGX_ERROR;
         }
@@ -713,13 +880,13 @@ ngx_http_upload_append_field(ngx_http_upload_ctx_t *u, ngx_str_t *name, ngx_str_
 
     len += value-&gt;len;
 
-    b = ngx_create_temp_buf(u-&gt;pool, len);
+    b = ngx_create_temp_buf(u-&gt;request-&gt;pool, len);
 
     if (b == NULL) {
         return NGX_UPLOAD_NOMEM;
     }
 
-    cl = ngx_alloc_chain_link(u-&gt;pool);
+    cl = ngx_alloc_chain_link(u-&gt;request-&gt;pool);
     if (cl == NULL) {
         return NGX_UPLOAD_NOMEM;
     }
@@ -767,6 +934,14 @@ ngx_http_upload_create_loc_conf(ngx_conf_t *cf)
 
     conf-&gt;buffer_size = NGX_CONF_UNSET_SIZE;
     conf-&gt;max_header_len = NGX_CONF_UNSET_SIZE;
+    conf-&gt;max_output_body_len = NGX_CONF_UNSET_SIZE;
+
+    /*
+     * conf-&gt;field_templates,
+     * conf-&gt;aggregate_field_templates,
+     * and conf-&gt;field_filters are
+     * zeroed by ngx_pcalloc
+     */
 
     return conf;
 } /* }}} */
@@ -795,72 +970,138 @@ ngx_http_upload_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
                               prev-&gt;max_header_len,
                               (size_t) 512);
 
+    ngx_conf_merge_size_value(conf-&gt;max_output_body_len,
+                              prev-&gt;max_output_body_len,
+                              (size_t) 256 * 1024);
+
+    if(conf-&gt;field_templates == NULL) {
+        conf-&gt;field_templates = prev-&gt;field_templates;
+    }
+
+    if(conf-&gt;aggregate_field_templates == NULL) {
+        conf-&gt;aggregate_field_templates = prev-&gt;aggregate_field_templates;
+    }
+
+    if(conf-&gt;field_filters == NULL) {
+        conf-&gt;field_filters = prev-&gt;field_filters;
+    }
+
     return NGX_CONF_OK;
 } /* }}} */
 
 static ngx_int_t /* {{{ ngx_http_upload_add_variables */
 ngx_http_upload_add_variables(ngx_conf_t *cf)
 {
-    ngx_http_variable_t  *var;
+    ngx_http_variable_t  *var, *v;
 
-    var = ngx_http_add_variable(cf, &amp;ngx_http_upload_field_name,
-        NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH);
+    for (v = ngx_http_upload_variables; v-&gt;name.len; v++) {
+        var = ngx_http_add_variable(cf, &amp;v-&gt;name, v-&gt;flags);
+        if (var == NULL) {
+            return NGX_ERROR;
+        }
 
-    if (var == NULL) {
-        return NGX_ERROR;
+        var-&gt;get_handler = v-&gt;get_handler;
+        var-&gt;data = v-&gt;data;
     }
 
-    var-&gt;get_handler = ngx_http_upload_variable;
-    var-&gt;data = offsetof(ngx_http_upload_ctx_t, field_name);
-
-    var = ngx_http_add_variable(cf, &amp;ngx_http_upload_content_type,
-        NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH);
+    for (v = ngx_http_upload_aggregate_variables; v-&gt;name.len; v++) {
+        var = ngx_http_add_variable(cf, &amp;v-&gt;name, v-&gt;flags);
+        if (var == NULL) {
+            return NGX_ERROR;
+        }
 
-    if (var == NULL) {
-        return NGX_ERROR;
+        var-&gt;get_handler = v-&gt;get_handler;
+        var-&gt;data = v-&gt;data;
     }
 
-    var-&gt;get_handler = ngx_http_upload_variable;
-    var-&gt;data = offsetof(ngx_http_upload_ctx_t, content_type);
+    return NGX_OK;
+} /* }}} */
+
+static ngx_int_t /* {{{ ngx_http_upload_variable */
+ngx_http_upload_variable(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v, uintptr_t data)
+{
+    ngx_http_upload_ctx_t  *u;
+    ngx_str_t              *value;
+
+    v-&gt;valid = 1;
+    v-&gt;no_cacheable = 0;
+    v-&gt;not_found = 0;
+
+    u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
+
+    value = (ngx_str_t *) ((char *) u + data);
+
+    v-&gt;data = value-&gt;data;
+    v-&gt;len = value-&gt;len;
+
+    return NGX_OK;
+} /* }}} */
+
+static ngx_int_t /* {{{ ngx_http_upload_md5_variable */
+ngx_http_upload_md5_variable(ngx_http_request_t *r,
+    ngx_http_variable_value_t *v,  uintptr_t data)
+{
+    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);
 
-    var = ngx_http_add_variable(cf, &amp;ngx_http_upload_file_name,
-        NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH);
-    if (var == NULL) {
-        return NGX_ERROR;
+    if(u-&gt;md5_ctx == NULL) {
+        v-&gt;not_found = 1;
+        return NGX_OK;
     }
 
-    var-&gt;get_handler = ngx_http_upload_variable;
-    var-&gt;data = offsetof(ngx_http_upload_ctx_t, file_name);
+    v-&gt;valid = 1;
+    v-&gt;no_cacheable = 0;
+    v-&gt;not_found = 0;
+
+    hex_table = (u_char*)data;
+    c = u-&gt;md5_ctx-&gt;md5_digest_hex;
 
-    var = ngx_http_add_variable(cf, &amp;ngx_http_upload_tmp_path,
-        NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH);
-    if (var == NULL) {
-        return NGX_ERROR;
+    for (i = 0; i &lt; MD5_DIGEST_LENGTH; i++) {
+        *c++ = hex_table[u-&gt;md5_ctx-&gt;md5_digest[i] &gt;&gt; 4];
+        *c++ = hex_table[u-&gt;md5_ctx-&gt;md5_digest[i] &amp; 0xf];
     }
 
-    var-&gt;get_handler = ngx_http_upload_variable;
-    var-&gt;data = offsetof(ngx_http_upload_ctx_t, output_file.name);
+    v-&gt;data = u-&gt;md5_ctx-&gt;md5_digest_hex;
+    v-&gt;len = MD5_DIGEST_LENGTH * 2;
 
     return NGX_OK;
 } /* }}} */
 
-static ngx_int_t /* {{{ ngx_http_upload_variable */
-ngx_http_upload_variable(ngx_http_request_t *r,
+static ngx_int_t /* {{{ ngx_http_upload_sha1_variable */
+ngx_http_upload_sha1_variable(ngx_http_request_t *r,
     ngx_http_variable_value_t *v,  uintptr_t data)
 {
+    ngx_uint_t             i;
     ngx_http_upload_ctx_t  *u;
-    ngx_str_t              *value;
+    u_char                 *c;
+    u_char                 *hex_table;
+
+    u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
+
+    if(u-&gt;sha1_ctx == NULL) {
+        v-&gt;not_found = 1;
+        return NGX_OK;
+    }
 
     v-&gt;valid = 1;
     v-&gt;no_cacheable = 0;
     v-&gt;not_found = 0;
 
-    u = ngx_http_get_module_ctx(r, ngx_http_upload_module);
+    hex_table = (u_char*)data;
+    c = u-&gt;sha1_ctx-&gt;sha1_digest_hex;
 
-    value = (ngx_str_t *) ((char *) u + data);
+    for (i = 0; i &lt; SHA_DIGEST_LENGTH; i++) {
+        *c++ = hex_table[u-&gt;sha1_ctx-&gt;sha1_digest[i] &gt;&gt; 4];
+        *c++ = hex_table[u-&gt;sha1_ctx-&gt;sha1_digest[i] &amp; 0xf];
+    }
 
-    v-&gt;data = value-&gt;data;
-    v-&gt;len = value-&gt;len;
+    v-&gt;data = u-&gt;sha1_ctx-&gt;sha1_digest_hex;
+    v-&gt;len = SHA_DIGEST_LENGTH * 2;
 
     return NGX_OK;
 } /* }}} */
@@ -868,24 +1109,28 @@ ngx_http_upload_variable(ngx_http_request_t *r,
 static char * /* {{{ ngx_http_upload_set_form_field */
 ngx_http_upload_set_form_field(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
-    ngx_http_upload_loc_conf_t *ulcf = conf;
-
-    ngx_int_t                   n;
+    ngx_int_t                   n, i;
     ngx_str_t                  *value;
     ngx_http_script_compile_t   sc;
     ngx_http_upload_field_template_t *h;
+    ngx_array_t                 **field;
+    ngx_http_variable_t         *v;
+    u_char                      *match;
+    ngx_http_upload_loc_conf_t  *ulcf = conf;
+
+    field = (ngx_array_t**) (((u_char*)conf) + cmd-&gt;offset);
 
     value = cf-&gt;args-&gt;elts;
 
-    if (ulcf-&gt;field_templates == NULL) {
-        ulcf-&gt;field_templates = ngx_array_create(cf-&gt;pool, 1,
-                                        sizeof(ngx_http_upload_field_template_t));
-        if (ulcf-&gt;field_templates == NULL) {
+    if (*field == NULL) {
+        *field = ngx_array_create(cf-&gt;pool, 1,
+                                  sizeof(ngx_http_upload_field_template_t));
+        if (*field == NULL) {
             return NGX_CONF_ERROR;
         }
     }
 
-    h = ngx_array_push(ulcf-&gt;field_templates);
+    h = ngx_array_push(*field);
     if (h == NULL) {
         return NGX_CONF_ERROR;
     }
@@ -944,10 +1189,45 @@ ngx_http_upload_set_form_field(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
         return NGX_CONF_ERROR;
     }
 
+    /*
+     * Check for aggregate variables in script
+     */
+    for(i = 1;i &lt;= 2;i++) {
+        for (v = ngx_http_upload_aggregate_variables; v-&gt;name.len; v++) {
+            match = ngx_strcasestrn(value[i].data, (char*)v-&gt;name.data, v-&gt;name.len - 1);
+
+            /*
+             * ngx_http_script_compile does check for final bracket earlier,
+             * so we don't need to care about it, which simplifies things
+             */
+            if(match != NULL
+                &amp;&amp; ((match - value[i].data &gt;= 1 &amp;&amp; match[-1] == '$') 
+                    || (match - value[i].data &gt;= 2 &amp;&amp; match[-2] == '$' &amp;&amp; match[-1] == '{')))
+            {
+                if(cmd-&gt;offset != offsetof(ngx_http_upload_loc_conf_t, aggregate_field_templates)) {
+                    ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+                                       &quot;variables upload_file_md5&quot;
+                                       &quot;, upload_file_md5_uc&quot;
+                                       &quot;, upload_file_sha1&quot;
+                                       &quot;, upload_file_sha1_uc&quot;
+                                       &quot; and upload_file_size&quot;
+                                       &quot; could be specified only in upload_aggregate_form_field directive&quot;);
+                    return NGX_CONF_ERROR;
+                }
+
+                if(v-&gt;get_handler == ngx_http_upload_md5_variable)
+                    ulcf-&gt;md5 = 1;
+
+                if(v-&gt;get_handler == ngx_http_upload_sha1_variable)
+                    ulcf-&gt;sha1 = 1;
+            }
+        }
+    }
+
     return NGX_CONF_OK;
 } /* }}} */
 
-static char * /* {{{ ngx_http_upload_set_form_field */
+static char * /* {{{ ngx_http_upload_pass_form_field */
 ngx_http_upload_pass_form_field(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
     ngx_http_upload_loc_conf_t *ulcf = conf;
@@ -1000,7 +1280,7 @@ 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_pass  */
+static char * /* {{{ ngx_http_upload_pass */
 ngx_http_upload_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
 {
     ngx_http_core_loc_conf_t    *clcf;
@@ -1389,7 +1669,7 @@ static ngx_int_t upload_parse_part_header(ngx_http_upload_ctx_t *upload_ctx, cha
                 }
 
             upload_ctx-&gt;file_name.len = filename_end - filename_start;
-            upload_ctx-&gt;file_name.data = ngx_pcalloc(upload_ctx-&gt;pool, upload_ctx-&gt;file_name.len + 1);
+            upload_ctx-&gt;file_name.data = ngx_pcalloc(upload_ctx-&gt;request-&gt;pool, upload_ctx-&gt;file_name.len + 1);
             
             if(upload_ctx-&gt;file_name.data == NULL)
                 return NGX_UPLOAD_NOMEM;
@@ -1416,7 +1696,7 @@ static ngx_int_t upload_parse_part_header(ngx_http_upload_ctx_t *upload_ctx, cha
                 }
 
                 upload_ctx-&gt;field_name.len = fieldname_end - fieldname_start;
-                upload_ctx-&gt;field_name.data = ngx_pcalloc(upload_ctx-&gt;pool, upload_ctx-&gt;field_name.len + 1);
+                upload_ctx-&gt;field_name.data = ngx_pcalloc(upload_ctx-&gt;request-&gt;pool, upload_ctx-&gt;field_name.len + 1);
 
                 if(upload_ctx-&gt;field_name.data == NULL)
                     return NGX_UPLOAD_NOMEM;
@@ -1436,7 +1716,7 @@ static ngx_int_t upload_parse_part_header(ngx_http_upload_ctx_t *upload_ctx, cha
             return NGX_UPLOAD_MALFORMED; // Empty Content-Type field
         }
 
-        upload_ctx-&gt;content_type.data = ngx_pcalloc(upload_ctx-&gt;pool, upload_ctx-&gt;content_type.len + 1);
+        upload_ctx-&gt;content_type.data = ngx_pcalloc(upload_ctx-&gt;request-&gt;pool, upload_ctx-&gt;content_type.len + 1);
         
         if(upload_ctx-&gt;content_type.data == NULL)
             return NGX_UPLOAD_NOMEM; // Unable to allocate memory for string
@@ -1534,7 +1814,7 @@ ngx_int_t upload_start(ngx_http_upload_ctx_t *upload_ctx, ngx_http_upload_loc_co
 	if(upload_ctx == NULL)
 		return NGX_ERROR;
 
-	upload_ctx-&gt;header_accumulator = ngx_pcalloc(upload_ctx-&gt;pool, ulcf-&gt;max_header_len + 1);
+	upload_ctx-&gt;header_accumulator = ngx_pcalloc(upload_ctx-&gt;request-&gt;pool, ulcf-&gt;max_header_len + 1);
 
 	if(upload_ctx-&gt;header_accumulator == NULL)
 		return NGX_ERROR;
@@ -1542,7 +1822,7 @@ ngx_int_t upload_start(ngx_http_upload_ctx_t *upload_ctx, ngx_http_upload_loc_co
 	upload_ctx-&gt;header_accumulator_pos = upload_ctx-&gt;header_accumulator;
 	upload_ctx-&gt;header_accumulator_end = upload_ctx-&gt;header_accumulator + ulcf-&gt;max_header_len;
 
-	upload_ctx-&gt;output_buffer = ngx_pcalloc(upload_ctx-&gt;pool, ulcf-&gt;buffer_size);
+	upload_ctx-&gt;output_buffer = ngx_pcalloc(upload_ctx-&gt;request-&gt;pool, ulcf-&gt;buffer_size);
 
 	if(upload_ctx-&gt;output_buffer == NULL)
 		return NGX_ERROR;
@@ -1595,7 +1875,7 @@ ngx_int_t upload_parse_content_type(ngx_http_upload_ctx_t *upload_ctx, ngx_str_t
 
     // Allocate memory for entire boundary plus \r\n plus terminating character
     upload_ctx-&gt;boundary.len = boundary_end_ptr - boundary_start_ptr + 4;
-    upload_ctx-&gt;boundary.data = ngx_pcalloc(upload_ctx-&gt;pool, upload_ctx-&gt;boundary.len + 1);
+    upload_ctx-&gt;boundary.data = ngx_pcalloc(upload_ctx-&gt;request-&gt;pool, upload_ctx-&gt;boundary.len + 1);
 
     if(upload_ctx-&gt;boundary.data == NULL)
         return NGX_UPLOAD_NOMEM;</diff>
      <filename>ngx_http_upload_module.c</filename>
    </modified>
    <modified>
      <diff>@@ -3,9 +3,14 @@
 &lt;title&gt;Test upload&lt;/title&gt;
 &lt;/head&gt;
 &lt;body&gt;
-&lt;h2&gt;Select file to upload&lt;/h2&gt;
-&lt;form name=&quot;upload&quot; method=&quot;POST&quot; enctype=&quot;multipart/form-data&quot; action=&quot;http://localhost/upload&quot;&gt;
+&lt;h2&gt;Select files to upload&lt;/h2&gt;
+&lt;form enctype=&quot;multipart/form-data&quot; action=&quot;http://localhost/upload&quot; method=&quot;post&quot;&gt;
 &lt;input type=&quot;file&quot; name=&quot;file1&quot;&gt;&lt;br&gt;
+&lt;input type=&quot;file&quot; name=&quot;file2&quot;&gt;&lt;br&gt;
+&lt;input type=&quot;file&quot; name=&quot;file3&quot;&gt;&lt;br&gt;
+&lt;input type=&quot;file&quot; name=&quot;file4&quot;&gt;&lt;br&gt;
+&lt;input type=&quot;file&quot; name=&quot;file5&quot;&gt;&lt;br&gt;
+&lt;input type=&quot;file&quot; name=&quot;file6&quot;&gt;&lt;br&gt;
 &lt;input type=&quot;submit&quot; name=&quot;submit&quot; value=&quot;Upload&quot;&gt;
 &lt;input type=&quot;hidden&quot; name=&quot;test&quot; value=&quot;value&quot;&gt;
 &lt;/form&gt;</diff>
      <filename>upload.html</filename>
    </modified>
  </modified>
  <removed type="array"/>
  <parents type="array">
    <parent>
      <id>01bb9ecbc00385a2776cf7d1e2f00ebc59301236</id>
    </parent>
  </parents>
  <author>
    <name>Valery Kholodkov</name>
    <email>valery@grid.net.ru</email>
  </author>
  <url>http://github.com/vkholodkov/nginx-upload-module/commit/98f75c8219b35f71a67d0cabc0bde99b2f895f43</url>
  <id>98f75c8219b35f71a67d0cabc0bde99b2f895f43</id>
  <committed-date>2008-07-25T00:02:39-07:00</committed-date>
  <authored-date>2008-07-25T00:02:39-07:00</authored-date>
  <message>Added calculation of MD5 and SHA1 checksums of uploaded files</message>
  <tree>17b923627760c9fedc6032bee871d4f70afe4e25</tree>
  <committer>
    <name>Valery Kholodkov</name>
    <email>valery@grid.net.ru</email>
  </committer>
</commit>
