diff -Nru nginx-0.6.34/auto/options nginx-0.6.39~ppa1~hardy/auto/options --- nginx-0.6.34/auto/options 2008-04-29 10:27:55.000000000 +0100 +++ nginx-0.6.39~ppa1~hardy/auto/options 2009-05-18 17:17:51.000000000 +0100 @@ -119,9 +119,12 @@ NGX_CPU_CACHE_LINE= +opt= for option do + opt="$opt `echo $option | sed -e \"s/\(--[^=]*=\)\(.* .*\)/\1'\2'/\"`" + case "$option" in -*=*) value=`echo "$option" | sed -e 's/[-_a-zA-Z0-9]*=//'` ;; *) value="" ;; @@ -215,6 +218,7 @@ --with-debug) NGX_DEBUG=YES ;; --without-pcre) USE_PCRE=DISABLED ;; + --with-pcre) USE_PCRE=YES ;; --with-pcre=*) PCRE="$value" ;; --with-pcre-opt=*) PCRE_OPT="$value" ;; @@ -247,6 +251,9 @@ done +NGX_CONFIGURE="$opt" + + if [ $help = yes ]; then cat << END @@ -333,7 +340,8 @@ pentium, pentiumpro, pentium3, pentium4, athlon, opteron, sparc32, sparc64, ppc64 - --without-pcre disable PCRE libarary usage + --without-pcre disable PCRE library usage + --with-pcre force PCRE library usage --with-pcre=DIR set path to PCRE library sources --with-pcre-opt=OPTIONS set additional options for PCRE building diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/CHANGES /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/CHANGES --- nginx-0.6.34/CHANGES 2008-11-27 15:35:25.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/CHANGES 2009-09-14 14:13:27.000000000 +0100 @@ -1,4 +1,86 @@ +Changes with nginx 0.6.39 14 Sep 2009 + + *) Security: a segmentation fault might occur in worker process while + specially crafted request handling. + Thanks to Chris Ries. + + *) Bugfix: a segmentation fault might occur in worker process, if + error_log was set to info or debug level. + Thanks to Sergey Bochenkov. + + +Changes with nginx 0.6.38 22 Jun 2009 + + *) Feature: the "keepalive_requests" directive. + + +Changes with nginx 0.6.37 18 May 2009 + + *) Feature: Microsoft specific "AUTH LOGIN with User Name" mode support + in mail proxy server. + Thanks to Maxim Dounin. + + *) Bugfix: nginx could not be built on platforms different from i386, + amd64, sparc, and ppc; the bug had appeared in 0.6.36. + + +Changes with nginx 0.6.36 02 Apr 2009 + + *) Change: now the "Invalid argument" error returned by + setsockopt(TCP_NODELAY) on Solaris, is ignored. + + *) Change: now POSTs without "Content-Length" header line are allowed. + + *) Feature: the "try_files" directive. + + *) Feature: the --with-pcre option in the configure. + + *) Feature: the "if_modified_since" directive. + + *) Feature: the "$cookie_..." variables. + + *) Feature: the "$arg_..." variables. + + *) Bugfix: compatibility with Tru64 UNIX. + Thanks to Dustin Marquess. + + *) Bugfix: a "ssl_engine" directive did not use a SSL-accelerator for + asymmetric ciphers. + Thanks to Marcin Gozdalik. + + *) Bugfix: in a redirect rewrite directive original arguments were + concatenated with new arguments by a "?" rather than an "&"; + the bug had appeared in 0.1.18. + Thanks to Maxim Dounin. + + *) Bugfix: nginx could not be built on AIX. + + *) Bugfix: a double response might be returned if the epoll or rtsig + methods are used and a redirect was returned to a request with + body. + Thanks to Eden Li. + + *) Bugfix: a segmentation fault might occur in worker process if + "resolver" directive was used in SMTP proxy. + + *) Bugfix: fastcgi_store stored files not always. + + *) Bugfix: nginx did not process a FastCGI server response, if the + server send too many messages to stderr before response. + + +Changes with nginx 0.6.35 26 Jan 2009 + + *) Bugfix: in shared memory allocations if nginx was built without + debugging. + Thanks to Andrey Kvasov. + + *) Bugfixes in an "Expect" request header line support. + + *) Bugfix: UTF-8 encoding usage in the ngx_http_autoindex_module. + + Changes with nginx 0.6.34 27 Nov 2008 *) Change: now the EAGAIN error returned by connect() is not considered @@ -950,8 +1032,8 @@ amd64, sparc, and ppc; the bug had appeared in 0.5.8. *) Bugfix: a segmentation fault might occur in worker process if the - temporarily files were used while working with FastCGI server; the - bug had appeared in 0.5.8. + temporary files were used while working with FastCGI server; the bug + had appeared in 0.5.8. *) Bugfix: a segmentation fault might occur in worker process if the $fastcgi_script_name variable was logged. @@ -1854,8 +1936,8 @@ in 0.3.18. *) Bugfix: if the HTTPS protocol was used in the "proxy_pass" directive - and the request body was in temporarily file then the request was - not transferred. + and the request body was in temporary file then the request was not + transferred. *) Bugfix: perl 5.8.8 compatibility. @@ -3007,8 +3089,8 @@ *) Bugfix: the proxy_set_x_var and fastcgi_set_var directives were not inherited. - *) Bugfix: in the redirect rewrite directive the arguments were - concatenated with URI by the "&" rather than the "?". + *) Bugfix: in a redirect rewrite directive arguments were concatenated + with URI by an "&" rather than a "?". *) Bugfix: the lines without trailing ";" in the file being included by the ngx_http_geo_module were silently ignored. diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/CHANGES.ru /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/CHANGES.ru --- nginx-0.6.34/CHANGES.ru 2008-11-27 15:35:24.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/CHANGES.ru 2009-09-14 14:13:26.000000000 +0100 @@ -1,4 +1,87 @@ +Изменения в nginx 0.6.39 14.09.2009 + + *) Безопасность: при обработке специально созданного запроса в рабочем + процессе мог произойти segmentation fault. + Спасибо Chris Ries. + + *) Исправление: при использовании error_log на уровне info или debug в + рабочем процессе мог произойти segmentation fault. + Спасибо Сергею Боченкову. + + +Изменения в nginx 0.6.38 22.06.2009 + + *) Добавление: директива keepalive_requests. + + +Изменения в nginx 0.6.37 18.05.2009 + + *) Добавление: поддержка Microsoft-специфичного режима + "AUTH LOGIN with User Name" в почтовом прокси-сервере. + Спасибо Максиму Дунину. + + *) Исправление: nginx не собирался на платформах, отличных от i386, + amd64, sparc и ppc; ошибка появилась в 0.6.36. + + +Изменения в nginx 0.6.36 02.04.2009 + + *) Изменение: ошибка "Invalid argument", возвращаемая + setsockopt(TCP_NODELAY) на Solaris, теперь игнорируется. + + *) Изменение: теперь разрешаются POST'ы без строки "Content-Length" в + заголовке запроса. + + *) Добавление: директива try_files. + + *) Добавление: параметр --with-pcre в configure. + + *) Добавление: директива if_modified_since. + + *) Добавление: переменные "$cookie_...". + + *) Добавление: переменные "$arg_...". + + *) Исправление: совместимость с Tru64 UNIX. + Спасибо Dustin Marquess. + + *) Исправление: директива ssl_engine не использовала SSL-акселератор + для асимметричных шифров. + Спасибо Marcin Gozdalik. + + *) Исправление: в директиве rewrite, возвращающей редирект, старые + аргументы присоединялись к новым через символ "?" вместо "&"; + ошибка появилась в 0.1.18. + Спасибо Максиму Дунину. + + *) Исправление: nginx не собирался на AIX. + + *) Исправление: если на запрос с телом возвращался редирект, то ответ + мог быть двойным при использовании методов epoll или rtsig. + Спасибо Eden Li. + + *) Исправление: при использовании директивы resolver в SMTP + прокси-сервере в рабочем процессе мог произойти segmentation fault. + + *) Исправление: fastcgi_store не всегда сохранял файлы. + + *) Исправление: nginx не обрабатывал ответ FastCGI-сервера, если перед + ответом сервер передавал много сообщений в stderr. + + +Изменения в nginx 0.6.35 26.01.2009 + + *) Исправление: ошибки выделения больших блоков в разделяемой памяти, + если nginx был собран без отладки. + Спасибо Андрею Квасову. + + *) Исправления в поддержке строки "Expect" в заголовке запроса. + + *) Исправление: ошибки при использовании кодировки UTF-8 в + ngx_http_autoindex_module. + + Изменения в nginx 0.6.34 27.11.2008 *) Изменение: теперь ошибка EAGAIN при вызове connect() не считается @@ -561,7 +644,7 @@ *) Добавление: директивы open_file_cache, open_file_cache_retest и open_file_cache_errors. - *) Исправление: утечка сокетов; ошибка появилась в 0.6.7. + *) Исправление: утечки сокетов; ошибка появилась в 0.6.7. *) Исправление: В строку заголовка ответа "Content-Type", указанную в методе $r->send_http_header(), не добавлялась кодировка, указанная в diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/configure /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/configure --- nginx-0.6.34/configure 2007-07-29 19:05:45.000000000 +0100 +++ nginx-0.6.39~ppa1~hardy/configure 2009-05-18 17:17:51.000000000 +0100 @@ -3,8 +3,6 @@ # Copyright (C) Igor Sysoev -NGX_CONFIGURE=`echo $@ | sed 's/"/\\\\"/g'` - . auto/options . auto/init . auto/sources diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/debian/changelog /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/debian/changelog --- nginx-0.6.34/debian/changelog 2009-09-17 17:12:08.000000000 +0100 +++ nginx-0.6.39~ppa1~hardy/debian/changelog 2009-09-16 19:33:32.000000000 +0100 @@ -1,3 +1,9 @@ +nginx (0.6.39~ppa1~hardy) hardy; urgency=low + + * Update to 0.6.39 to mitigate CVE-2009-2629 + + -- Mark Foster Wed, 16 Sep 2009 08:14:51 -0700 + nginx (0.6.34-2ubuntu1~hardy1~ppa2) hardy; urgency=low * Add upload module source (instead of using wget at build time) diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/debian/control /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/debian/control --- nginx-0.6.34/debian/control 2009-09-17 17:12:08.000000000 +0100 +++ nginx-0.6.39~ppa1~hardy/debian/control 2009-09-16 19:34:19.000000000 +0100 @@ -5,7 +5,7 @@ XSBC-Original-Maintainer: Jose Parrella Uploaders: Fabio Tranchitella Build-Depends: debhelper (>= 5), autotools-dev, libpcre3-dev, zlib1g-dev, libssl-dev, wget -Standards-Version: 3.8.0 +Standards-Version: 3.7.3 Homepage: http://nginx.net Vcs-Svn: svn://svn.debian.org/svn/collab-maint/deb-maint/nginx/trunk Vcs-Browser: http://svn.debian.org/wsvn/collab-maint/deb-maint/nginx/trunk diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/debian/rules /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/debian/rules --- nginx-0.6.34/debian/rules 2009-09-17 17:12:08.000000000 +0100 +++ nginx-0.6.39~ppa1~hardy/debian/rules 2009-09-16 16:20:47.000000000 +0100 @@ -28,7 +28,7 @@ --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --with-http_stub_status_module \ --with-http_flv_module --with-http_ssl_module --with-http_dav_module \ --with-http_realip_module \ - --add-module=nginx_upload_module-2.0.10 + --add-module=nginx_upload_module-2.0.8 build: config.status diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/LICENSE /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/LICENSE --- nginx-0.6.34/LICENSE 2008-01-03 12:33:54.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/LICENSE 2009-01-26 15:23:11.000000000 +0000 @@ -1,5 +1,5 @@ /* - * Copyright (C) 2002-2008 Igor Sysoev + * Copyright (C) 2002-2009 Igor Sysoev * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -21,5 +21,4 @@ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. - * */ diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/nginx_upload_module-2.0.10/Changelog /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/nginx_upload_module-2.0.10/Changelog --- nginx-0.6.34/nginx_upload_module-2.0.10/Changelog 2009-09-17 17:12:08.000000000 +0100 +++ nginx-0.6.39~ppa1~hardy/nginx_upload_module-2.0.10/Changelog 1970-01-01 01:00:00.000000000 +0100 @@ -1,74 +0,0 @@ - -Version 2.0.10 - * Change: compatibility with nginx API 0.8.11 - * Fixed bug: Prevent module from registering store path if no upload location - was configured - * Fixed bug: upload corrupted in case of short body + keepalive. Thanks to Dmitry - Dedukhin. - * Change: Return error 415 instead of 400 if request content type is not - multipart/form-data - -Version 2.0.9 - * Change: compatibility with nginx's API 0.7.52 and greater - * Fixed bug: module directives couldn't have appeared in limit_except block - * Added feature: directive upload_limit_rate and ability to limit upload rate - * Change: Malformed body issues are now logged to error log instead of debug log - -Version 2.0.8 - * Change: support for named locations - * Fixed bug: crash on missing Content-Type request header - * Fixed bug: compilation problem on amd 64 - -Version 2.0.7 - * Change: file size and output body size restrictions - * Added feature: directive upload_pass_args enables forwarding - of request arguments to a backend. Thanks to Todd Fisher. - -Version 2.0.6 - * Fixed bug: zero variables in aggregate field name caused allocation - of random amount of memory. Thanks to Dmitry Dedukhin. - * Fixed bug: Prevent generation of a field in case of empty field name - -Version 2.0.5 - * Fixed bug: prevent leaking of file descriptors on a timeout (unconfirmed problem). - * Fixed bug: variables in field values in upload_set_form_field and - upload_aggregate_form_field directives were not working if field name - contained 0 variables. - * Added feature: directive upload_cleanup now specifies statuses, - which initiate removal of uploaded files. Used for cleanup after - failure of a backend. - * Added feature: aggregate variable upload_file_crc32 allows to calculate - CRC32 if file on the fly. - * Fixed bug: Indicator of necessity to calculate SHA1 sum was not inheritable - from server configuration. - -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. - * Fixed bug: missing CRLF at the end of resulting body. - * Change: optimized out some unnecessary memory allocations and zeroing. - -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. - * upload_store_access directive did not work at all. Permissions were - defaulted to user:rw. Thanks to Brian Moran. - * In case of any errors at the last chunk of request body only 500 Internal Server Error - was generated intead of 400 Bad Request and 503 Service Unavailable. - * Fixed copyrights for temporary file name generation code - * Fixed compilation issue on 0.6.32. Thanks to Tomas Pollak. - * Added directive upload_pass_form_field to specify fields - to pass to backend. Fixes security hole found by Brian Moran. - -Version 2.0.2 - * Fixed crash in logging filename while aborting upload - * Added feasible debug logging - * Added support for variables to generate form fields - in resulting request body - * Added missing logging of errno after write failures - * Simplified upload abortion logic; simply discarding - already added fields - * Now returning explicit error code after script failures - to be able to generate Internal server error diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/nginx_upload_module-2.0.10/config /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/nginx_upload_module-2.0.10/config --- nginx-0.6.34/nginx_upload_module-2.0.10/config 2009-09-17 17:12:08.000000000 +0100 +++ nginx-0.6.39~ppa1~hardy/nginx_upload_module-2.0.10/config 1970-01-01 01:00:00.000000000 +0100 @@ -1,5 +0,0 @@ -USE_MD5=YES -USE_SHA1=YES -ngx_addon_name=ngx_http_upload_module -HTTP_MODULES="$HTTP_MODULES ngx_http_upload_module" -NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_upload_module.c" diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/nginx_upload_module-2.0.10/example.php /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/nginx_upload_module-2.0.10/example.php --- nginx-0.6.34/nginx_upload_module-2.0.10/example.php 2009-09-17 17:12:08.000000000 +0100 +++ nginx-0.6.39~ppa1~hardy/nginx_upload_module-2.0.10/example.php 1970-01-01 01:00:00.000000000 +0100 @@ -1,47 +0,0 @@ - - - -Test upload - - -Uploaded files:"; - echo ""; - - echo ""; - - for ($i=1;$i<=$slots;$i++){ - $key = $header_prefix.$i; - if (array_key_exists($key."_name", $_POST) && array_key_exists($key."_path",$_POST)) { - $tmp_name = $_POST[$key."_path"]; - $name = $_POST[$key."_name"]; - $content_type = $_POST[$key."_content_type"]; - $md5 = $_POST[$key."_md5"]; - $size = $_POST[$key."_size"]; - - echo ""; - } - } - - echo "
NameLocationContent typeMD5Size
$name$tmp_name$content_type$md5$size
"; - -}else{?> -

Select files to upload

-
-
-
-
-
-
-
- - -
- - - diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/nginx_upload_module-2.0.10/LICENCE /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/nginx_upload_module-2.0.10/LICENCE --- nginx-0.6.34/nginx_upload_module-2.0.10/LICENCE 2009-09-17 17:12:08.000000000 +0100 +++ nginx-0.6.39~ppa1~hardy/nginx_upload_module-2.0.10/LICENCE 1970-01-01 01:00:00.000000000 +0100 @@ -1,24 +0,0 @@ -* Copyright (c) 2006, 2008, Valery Kholodkov -* All rights reserved. -* -* Redistribution and use in source and binary forms, with or without -* modification, are permitted provided that the following conditions are met: -* * Redistributions of source code must retain the above copyright -* notice, this list of conditions and the following disclaimer. -* * Redistributions in binary form must reproduce the above copyright -* notice, this list of conditions and the following disclaimer in the -* documentation and/or other materials provided with the distribution. -* * Neither the name of the Valery Kholodkov nor the -* names of its contributors may be used to endorse or promote products -* derived from this software without specific prior written permission. -* -* THIS SOFTWARE IS PROVIDED BY VALERY KHOLODKOV ''AS IS'' AND ANY -* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -* DISCLAIMED. IN NO EVENT SHALL VALERY KHOLODKOV BE LIABLE FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/nginx_upload_module-2.0.10/nginx.conf /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/nginx_upload_module-2.0.10/nginx.conf --- nginx-0.6.34/nginx_upload_module-2.0.10/nginx.conf 2009-09-17 17:12:08.000000000 +0100 +++ nginx-0.6.39~ppa1~hardy/nginx_upload_module-2.0.10/nginx.conf 1970-01-01 01:00:00.000000000 +0100 @@ -1,49 +0,0 @@ - -worker_processes 20; - -error_log logs/error.log notice; - -working_directory /usr/local/nginx; - -events { - worker_connections 1024; -} - -http { - include mime.types; - default_type application/octet-stream; - - server { - listen 80; - client_max_body_size 100m; - - # Upload form should be submitted to this location - location /upload { - # Pass altered request body to this location - upload_pass @test; - - # Store files to this directory - # The directory is hashed, subdirectories 0 1 2 3 4 5 6 7 8 9 should exist - upload_store /tmp 1; - - # Allow uploaded files to be read only by user - upload_store_access user:r; - - # Set specified fields in request body - upload_set_form_field "${upload_field_name}_name" $upload_file_name; - upload_set_form_field "${upload_field_name}_content_type" $upload_content_type; - upload_set_form_field "${upload_field_name}_path" $upload_tmp_path; - - # Inform backend about hash and size of a file - upload_aggregate_form_field "${upload_field_name}_md5" $upload_file_md5; - upload_aggregate_form_field "${upload_field_name}_size" $upload_file_size; - - upload_pass_form_field "^submit$|^description$"; - } - - # Pass altered request body to a backend - location @test { - proxy_pass http://localhost:8080; - } - } -} diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/nginx_upload_module-2.0.10/ngx_http_upload_module.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/nginx_upload_module-2.0.10/ngx_http_upload_module.c --- nginx-0.6.34/nginx_upload_module-2.0.10/ngx_http_upload_module.c 2009-09-17 17:12:08.000000000 +0100 +++ nginx-0.6.39~ppa1~hardy/nginx_upload_module-2.0.10/ngx_http_upload_module.c 1970-01-01 01:00:00.000000000 +0100 @@ -1,2567 +0,0 @@ -/* - * Copyright (C) 2006, 2008 Valery Kholodkov - * Client body reception code Copyright (c) 2002-2007 Igor Sysoev - * Temporary file name generation code Copyright (c) 2002-2007 Igor Sysoev - */ -#include -#include -#include -#include - -#if (NGX_HAVE_OPENSSL_MD5_H) -#include -#else -#include -#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 -#else -#include -#endif - -#define MULTIPART_FORM_DATA_STRING "multipart/form-data" -#define BOUNDARY_STRING "boundary=" -#define CONTENT_DISPOSITION_STRING "Content-Disposition:" -#define CONTENT_TYPE_STRING "Content-Type:" -#define FORM_DATA_STRING "form-data" -#define ATTACHMENT_STRING "attachment" -#define FILENAME_STRING "filename=\"" -#define FIELDNAME_STRING "name=\"" - -#define NGX_UPLOAD_MALFORMED -1 -#define NGX_UPLOAD_NOMEM -2 -#define NGX_UPLOAD_IOERROR -3 -#define NGX_UPLOAD_SCRIPTERROR -4 -#define NGX_UPLOAD_TOOLARGE -5 - -/* - * State of multipart/form-data parser - */ -typedef enum { - upload_state_boundary_seek, - upload_state_after_boundary, - upload_state_headers, - upload_state_data, - upload_state_finish -} upload_state_t; - -/* - * Template for a field to generate in output form - */ -typedef struct { - ngx_table_elt_t value; - ngx_array_t *field_lengths; - ngx_array_t *field_values; - ngx_array_t *value_lengths; - ngx_array_t *value_values; -} ngx_http_upload_field_template_t; - -/* - * Filter for fields in output form - */ -typedef struct { -#if (NGX_PCRE) - ngx_regex_t *regex; - ngx_int_t ncaptures; -#else - ngx_str_t text; -#endif -} ngx_http_upload_field_filter_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_str_t url; - ngx_path_t *store_path; - ngx_uint_t store_access; - size_t buffer_size; - size_t max_header_len; - size_t max_output_body_len; - off_t max_file_size; - ngx_array_t *field_templates; - ngx_array_t *aggregate_field_templates; - ngx_array_t *field_filters; - ngx_array_t *cleanup_statuses; - ngx_flag_t forward_args; - size_t limit_rate; - - 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 { - MD5_CTX md5; - u_char md5_digest[MD5_DIGEST_LENGTH * 2]; -} ngx_http_upload_md5_ctx_t; - -typedef struct ngx_http_upload_sha1_ctx_s { - SHA_CTX sha1; - u_char sha1_digest[SHA_DIGEST_LENGTH * 2]; -} ngx_http_upload_sha1_ctx_t; - -/* - * Upload module context - */ -typedef struct ngx_http_upload_ctx_s { - ngx_str_t boundary; - u_char *boundary_start; - u_char *boundary_pos; - - upload_state_t state; - - u_char *header_accumulator; - u_char *header_accumulator_end; - u_char *header_accumulator_pos; - - ngx_str_t field_name; - ngx_str_t file_name; - ngx_str_t content_type; - - u_char *output_buffer; - u_char *output_buffer_end; - u_char *output_buffer_pos; - - 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); - ngx_int_t (*flush_output_buffer_f)(struct ngx_http_upload_ctx_s *upload_ctx, u_char *buf, size_t len); - - ngx_http_request_t *request; - ngx_log_t *log; - - ngx_file_t output_file; - ngx_chain_t *chain; - ngx_chain_t *last; - ngx_chain_t *checkpoint; - size_t output_body_len; - size_t limit_rate; - ssize_t received; - - ngx_pool_cleanup_t *cln; - - ngx_http_upload_md5_ctx_t *md5_ctx; - ngx_http_upload_sha1_ctx_t *sha1_ctx; - uint32_t crc32; - - unsigned int first_part:1; - unsigned int discard_data:1; - unsigned int is_file:1; - unsigned int calculate_crc32:1; - unsigned int no_content:1; -} ngx_http_upload_ctx_t; - -static ngx_int_t ngx_http_upload_handler(ngx_http_request_t *r); -static ngx_int_t ngx_http_upload_body_handler(ngx_http_request_t *r); - -static void *ngx_http_upload_create_loc_conf(ngx_conf_t *cf); -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); -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 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); -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); - -static ngx_int_t ngx_http_upload_flush_output_buffer(ngx_http_upload_ctx_t *u, - u_char *buf, size_t len); -static ngx_int_t ngx_http_upload_append_field(ngx_http_upload_ctx_t *u, - ngx_str_t *name, ngx_str_t *value); - -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); - -static ngx_int_t ngx_http_read_upload_client_request_body(ngx_http_request_t *r); - -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); - -#if defined nginx_version && nginx_version >= 7052 -static ngx_path_init_t ngx_http_upload_temp_path = { - ngx_string(NGX_HTTP_PROXY_TEMP_PATH), { 1, 2, 0 } -}; -#endif - -/* - * upload_init_ctx - * - * Initialize upload context. Memory for upload context which is being passed - * as upload_ctx parameter could be allocated anywhere and should not be freed - * prior to upload_shutdown_ctx call. - * - * IMPORTANT: - * - * After initialization the following routine SHOULD BE called: - * - * upload_parse_content_type -- to assign part boundary - * - * Parameter: - * upload_ctx -- upload context which is being initialized - * - */ -static void upload_init_ctx(ngx_http_upload_ctx_t *upload_ctx); - -/* - * upload_shutdown_ctx - * - * Shutdown upload context. Discard all remaining data and - * free all memory associated with upload context. - * - * Parameter: - * upload_ctx -- upload context which is being shut down - * - */ -static void upload_shutdown_ctx(ngx_http_upload_ctx_t *upload_ctx); - -/* - * upload_start - * - * Starts multipart stream processing. Initializes internal buffers - * and pointers - * - * Parameter: - * upload_ctx -- upload context which is being initialized - * - * Return value: - * NGX_OK on success - * NGX_ERROR if error has occured - * - */ -static ngx_int_t upload_start(ngx_http_upload_ctx_t *upload_ctx, ngx_http_upload_loc_conf_t *ulcf); - -/* - * upload_parse_content_type - * - * Parse and verify content type from HTTP header, extract boundary and - * assign it to upload context - * - * Parameters: - * upload_ctx -- upload context to populate - * content_type -- value of Content-Type header to parse - * - * Return value: - * NGX_OK on success - * NGX_ERROR if error has occured - */ -static ngx_int_t upload_parse_content_type(ngx_http_upload_ctx_t *upload_ctx, ngx_str_t *content_type); - -/* - * upload_process_buf - * - * Process buffer with multipart stream starting from start and terminating - * by end, operating on upload_ctx. The header information is accumulated in - * 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 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 - * NGX_UPLOAD_TOOLARGE field body is too large - */ -static ngx_int_t upload_process_buf(ngx_http_upload_ctx_t *upload_ctx, u_char *start, u_char *end); - -static ngx_command_t ngx_http_upload_commands[] = { /* {{{ */ - - /* - * Enables uploads for location and specifies location to pass modified request to - */ - { ngx_string("upload_pass"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1, - ngx_http_upload_pass, - NGX_HTTP_LOC_CONF_OFFSET, - 0, - NULL }, - - /* - * Specifies base path of file store - */ - { ngx_string("upload_store"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1234, - ngx_conf_set_path_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_upload_loc_conf_t, store_path), - NULL }, - - /* - * Specifies the access mode for files in store - */ - { ngx_string("upload_store_access"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE123, - ngx_conf_set_access_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_upload_loc_conf_t, store_access), - NULL }, - - /* - * Specifies the size of buffer, which will be used - * to write data to disk - */ - { ngx_string("upload_buffer_size"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1, - ngx_conf_set_size_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_upload_loc_conf_t, buffer_size), - NULL }, - - /* - * Specifies the maximal length of the part header - */ - { ngx_string("upload_max_part_header_len"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1, - ngx_conf_set_size_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_upload_loc_conf_t, max_header_len), - NULL }, - - /* - * Specifies the maximal size of the file to be uploaded - */ - { ngx_string("upload_max_file_size"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_CONF_TAKE1, - ngx_conf_set_off_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_upload_loc_conf_t, max_file_size), - NULL }, - - /* - * Specifies the maximal length of output body - */ - { ngx_string("upload_max_output_body_len"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_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("upload_set_form_field"), - 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_set_form_field, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_upload_loc_conf_t, field_templates), - NULL}, - - /* - * Specifies the field with aggregate parameters - * to set in altered response body - */ - { ngx_string("upload_aggregate_form_field"), - 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_set_form_field, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_upload_loc_conf_t, aggregate_field_templates), - NULL}, - - /* - * Specifies the field to pass to backend - */ - { ngx_string("upload_pass_form_field"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LMT_CONF|NGX_HTTP_LIF_CONF - |NGX_CONF_TAKE1, - ngx_http_upload_pass_form_field, - NGX_HTTP_LOC_CONF_OFFSET, - 0, - 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_LMT_CONF|NGX_HTTP_LIF_CONF - |NGX_CONF_1MORE, - ngx_http_upload_cleanup, - NGX_HTTP_LOC_CONF_OFFSET, - 0, - NULL}, - - /* - * Specifies the whether or not to forward query args - * to the upload_pass redirect location - */ - { ngx_string("upload_pass_args"), - 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, forward_args), - NULL }, - - /* - * Specifies request body reception rate limit - */ - { ngx_string("upload_limit_rate"), - NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF - |NGX_CONF_TAKE1, - ngx_conf_set_size_slot, - NGX_HTTP_LOC_CONF_OFFSET, - offsetof(ngx_http_upload_loc_conf_t, limit_rate), - NULL }, - - ngx_null_command -}; /* }}} */ - -ngx_http_module_t ngx_http_upload_module_ctx = { /* {{{ */ - ngx_http_upload_add_variables, /* preconfiguration */ - NULL, /* postconfiguration */ - - NULL, /* create main configuration */ - NULL, /* init main configuration */ - - 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_module_t ngx_http_upload_module = { /* {{{ */ - NGX_MODULE_V1, - &ngx_http_upload_module_ctx, /* module context */ - ngx_http_upload_commands, /* module directives */ - NGX_HTTP_MODULE, /* module type */ - NULL, /* init master */ - NULL, /* init module */ - NULL, /* init process */ - NULL, /* init thread */ - NULL, /* exit thread */ - NULL, /* exit process */ - NULL, /* exit master */ - NGX_MODULE_V1_PADDING -}; /* }}} */ - -static ngx_http_variable_t ngx_http_upload_variables[] = { /* {{{ */ - - { ngx_string("upload_field_name"), 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("upload_content_type"), 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("upload_file_name"), 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("upload_tmp_path"), 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("upload_file_md5"), NULL, ngx_http_upload_md5_variable, - (uintptr_t) "0123456789abcdef", - NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 }, - - { ngx_string("upload_file_md5_uc"), NULL, ngx_http_upload_md5_variable, - (uintptr_t) "0123456789ABCDEF", - NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 }, - - { ngx_string("upload_file_sha1"), NULL, ngx_http_upload_sha1_variable, - (uintptr_t) "0123456789abcdef", - NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 }, - - { ngx_string("upload_file_sha1_uc"), NULL, ngx_http_upload_sha1_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 }, - - { 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 }, - - { ngx_null_string, NULL, NULL, 0, 0, 0 } -}; /* }}} */ - -static ngx_str_t ngx_http_upload_empty_field_value = ngx_null_string; - -static ngx_str_t ngx_upload_field_part1 = { /* {{{ */ - sizeof(CRLF "Content-Disposition: form-data; name=\"") - 1, - (u_char*)CRLF "Content-Disposition: form-data; name=\"" -}; /* }}} */ - -static ngx_str_t ngx_upload_field_part2 = { /* {{{ */ - sizeof("\"" CRLF CRLF) - 1, - (u_char*)"\"" CRLF CRLF -}; /* }}} */ - -static ngx_int_t /* {{{ ngx_http_upload_handler */ -ngx_http_upload_handler(ngx_http_request_t *r) -{ - ngx_http_upload_loc_conf_t *ulcf; - ngx_http_upload_ctx_t *u; - ngx_int_t rc; - - if (!(r->method & NGX_HTTP_POST)) - return NGX_DECLINED; - - ulcf = ngx_http_get_module_loc_conf(r, ngx_http_upload_module); - - u = ngx_http_get_module_ctx(r, ngx_http_upload_module); - - if (u == NULL) { - u = ngx_pcalloc(r->pool, sizeof(ngx_http_upload_ctx_t)); - if (u == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - ngx_http_set_ctx(r, u, ngx_http_upload_module); - } - - if(ulcf->md5) { - if(u->md5_ctx == NULL) { - u->md5_ctx = ngx_palloc(r->pool, sizeof(ngx_http_upload_md5_ctx_t)); - if (u->md5_ctx == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - } - }else - u->md5_ctx = NULL; - - if(ulcf->sha1) { - if(u->sha1_ctx == NULL) { - u->sha1_ctx = ngx_palloc(r->pool, sizeof(ngx_http_upload_sha1_ctx_t)); - if (u->sha1_ctx == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - } - }else - u->sha1_ctx = NULL; - - u->calculate_crc32 = ulcf->crc32; - - // Check whether Content-Type header is missing - if(r->headers_in.content_type == NULL) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_errno, - "missing Content-Type header"); - return NGX_HTTP_BAD_REQUEST; - } - - u->request = r; - u->log = r->connection->log; - u->chain = u->last = u->checkpoint = NULL; - u->output_body_len = 0; - u->no_content = 1; - u->limit_rate = ulcf->limit_rate; - u->received = 0; - - upload_init_ctx(u); - - rc = upload_parse_content_type(u, &r->headers_in.content_type->value); - - if(rc != NGX_OK) { - upload_shutdown_ctx(u); - return rc; - } - - if(upload_start(u, ulcf) != NGX_OK) - return NGX_HTTP_INTERNAL_SERVER_ERROR; - - rc = ngx_http_read_upload_client_request_body(r); - - if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { - return rc; - } - - return NGX_DONE; -} /* }}} */ - -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); - - ngx_str_t args; - ngx_uint_t flags; - ngx_int_t rc; - ngx_str_t *uri; - ngx_buf_t *b; - ngx_chain_t *cl; - ngx_str_t dummy = ngx_string(""); - - if(ulcf->max_output_body_len != 0) { - if(ctx->output_body_len + ctx->boundary.len + 4 > ulcf->max_output_body_len) - return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; - } - - if(ctx->no_content) { - rc = ngx_http_upload_append_field(ctx, &dummy, &ngx_http_upload_empty_field_value); - - if(rc != NGX_OK) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - } - - /* - * Append final boundary - */ - b = ngx_create_temp_buf(r->pool, ctx->boundary.len + 4); - - if (b == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - b->last_in_chain = 1; - b->last_buf = 1; - - cl->buf = b; - cl->next = NULL; - - if(ctx->chain == NULL) { - ctx->chain = cl; - ctx->last = cl; - }else{ - ctx->last->next = cl; - ctx->last = cl; - } - - b->last = ngx_cpymem(b->last, ctx->boundary.data, ctx->boundary.len); - - *b->last++ = '-'; - *b->last++ = '-'; - *b->last++ = CR; - *b->last++ = LF; - - uri = &ulcf->url; - - if (ulcf->forward_args) { - args = r->args; /* forward the query args */ - } - else { - args.len = 0; - args.data = NULL; - } - - flags = 0; - - if (ngx_http_parse_unsafe_uri(r, uri, &args, &flags) != NGX_OK) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - r->request_body->bufs = ctx->chain; - - // Recalculate content length - r->headers_in.content_length_n = 0; - - for(cl = ctx->chain ; cl ; cl = cl->next) - r->headers_in.content_length_n += (cl->buf->last - cl->buf->pos); - - r->headers_in.content_length->value.data = ngx_palloc(r->pool, NGX_OFF_T_LEN); - - if (r->headers_in.content_length->value.data == NULL) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - r->headers_in.content_length->value.len = - ngx_sprintf(r->headers_in.content_length->value.data, "%O", r->headers_in.content_length_n) - - r->headers_in.content_length->value.data; - - if(uri->len != 0 && uri->data[0] == '/') { - rc = ngx_http_internal_redirect(r, uri, &args); - } - else{ - rc = ngx_http_named_location(r, uri); - } - - if (rc == NGX_ERROR) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - return rc; -} /* }}} */ - -static ngx_int_t ngx_http_upload_start_handler(ngx_http_upload_ctx_t *u) { /* {{{ */ - ngx_http_request_t *r = u->request; - 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; - uint32_t n; - ngx_uint_t i; - ngx_int_t rc; - ngx_err_t err; - ngx_http_upload_field_template_t *t; - ngx_http_upload_field_filter_t *f; - ngx_str_t field_name, field_value; - ngx_uint_t pass_field; - ngx_upload_cleanup_t *ucln; - - if(u->is_file) { - u->no_content = 0; - - 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); - - if(file->name.data == NULL) - return NGX_UPLOAD_NOMEM; - - ngx_memcpy(file->name.data, path->name.data, path->name.len); - - file->log = r->connection->log; - - for(;;) { - n = (uint32_t) ngx_next_temp_number(0); - - (void) ngx_sprintf(file->name.data + path->name.len + 1 + path->len, - "%010uD%Z", n); - - ngx_create_hashed_filename(path, file->name.data, file->name.len); - - ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0, - "hashed path: %s", file->name.data); - - file->fd = ngx_open_tempfile(file->name.data, 1, ulcf->store_access); - - if (file->fd != NGX_INVALID_FILE) { - file->offset = 0; - break; - } - - err = ngx_errno; - - if (err == NGX_EEXIST) { - n = (uint32_t) ngx_next_temp_number(1); - continue; - } - - ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_errno, - "failed to create output file \"%V\" for \"%V\"", &file->name, &u->file_name); - return NGX_UPLOAD_IOERROR; - } - - u->cln->handler = ngx_upload_cleanup_handler; - - 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; - for (i = 0; i < ulcf->field_templates->nelts; i++) { - - if (t[i].field_lengths == NULL) { - field_name = t[i].value.key; - }else{ - if (ngx_http_script_run(r, &field_name, t[i].field_lengths->elts, 0, - t[i].field_values->elts) == NULL) - { - rc = NGX_UPLOAD_SCRIPTERROR; - goto cleanup_file; - } - } - - if (t[i].value_lengths == NULL) { - field_value = t[i].value.value; - }else{ - if (ngx_http_script_run(r, &field_value, t[i].value_lengths->elts, 0, - t[i].value_values->elts) == NULL) - { - rc = NGX_UPLOAD_SCRIPTERROR; - goto cleanup_file; - } - } - - rc = ngx_http_upload_append_field(u, &field_name, &field_value); - - if(rc != NGX_OK) - goto cleanup_file; - } - } - - if(u->md5_ctx != NULL) - MD5Init(&u->md5_ctx->md5); - - 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 uploading file \"%V\" to \"%V\" (field \"%V\", content type \"%V\")" - , &u->file_name - , &u->output_file.name - , &u->field_name - , &u->content_type - ); - }else{ - pass_field = 0; - - if(ulcf->field_filters) { - f = ulcf->field_filters->elts; - for (i = 0; i < ulcf->field_filters->nelts; i++) { -#if (NGX_PCRE) - rc = ngx_regex_exec(f[i].regex, &u->field_name, NULL, 0); - - if (rc != NGX_REGEX_NO_MATCHED && rc < 0) { - return NGX_UPLOAD_SCRIPTERROR; - } - - /* - * If at least one filter succeeds, we pass the field - */ - if(rc == 0) - pass_field = 1; -#else - if(ngx_strncmp(f[i].text.data, u->field_name.data, u->field_name.len) == 0) - pass_field = 1; -#endif - } - } - - 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 - * is called. We pass empty field value to simplify things. - */ - rc = ngx_http_upload_append_field(u, &u->field_name, &ngx_http_upload_empty_field_value); - - if(rc != NGX_OK) - return rc; - }else - u->discard_data = 1; - } - - return NGX_OK; - -cleanup_file: - return rc; -} /* }}} */ - -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->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; - ngx_upload_cleanup_t *ucln; - - if(u->is_file) { - ucln = u->cln->data; - ucln->fd = -1; - - ngx_close_file(u->output_file.fd); - - ngx_log_error(NGX_LOG_INFO, r->connection->log, 0 - , "finished uploading file \"%V\" to \"%V\"" - , &u->file_name - , &u->output_file.name - ); - - if(u->md5_ctx) - MD5Final(u->md5_ctx->md5_digest, &u->md5_ctx->md5); - - 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_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) - { - goto rollback; - } - } - - if (af[i].value_lengths == NULL) { - aggregate_field_value = af[i].value.value; - }else{ - if (ngx_http_script_run(r, &aggregate_field_value, af[i].value_lengths->elts, 0, - af[i].value_values->elts) == NULL) - { - goto rollback; - } - } - - rc = ngx_http_upload_append_field(u, &aggregate_field_name, &aggregate_field_value); - - if(rc != NGX_OK) - goto rollback; - } - } - } - - // Checkpoint current output chain state - u->checkpoint = u->last; - return; - -rollback: - ngx_http_upload_abort_handler(u); -} /* }}} */ - -static void ngx_http_upload_abort_handler(ngx_http_upload_ctx_t *u) { /* {{{ */ - ngx_upload_cleanup_t *ucln; - - if(u->is_file) { - /* - * 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); - - if(ngx_delete_file(u->output_file.name.data) == NGX_FILE_ERROR) { - ngx_log_error(NGX_LOG_ERR, u->log, ngx_errno - , "aborted uploading file \"%V\" to \"%V\", failed to remove destination file" - , &u->file_name - , &u->output_file.name); - } else { - ngx_log_error(NGX_LOG_ALERT, u->log, 0 - , "aborted uploading file \"%V\" to \"%V\", dest file removed" - , &u->file_name - , &u->output_file.name); - } - } - - // Rollback output chain to the previous consistant state - if(u->checkpoint != NULL) { - u->last = u->checkpoint; - u->last->next = NULL; - }else{ - u->chain = u->last = NULL; - u->first_part = 1; - } -} /* }}} */ - -static ngx_int_t ngx_http_upload_flush_output_buffer(ngx_http_upload_ctx_t *u, u_char *buf, size_t len) { /* {{{ */ - ngx_http_request_t *r = u->request; - ngx_buf_t *b; - ngx_chain_t *cl; - ngx_http_upload_loc_conf_t *ulcf = ngx_http_get_module_loc_conf(r, ngx_http_upload_module); - - if(u->is_file) { - if(u->md5_ctx) - MD5Update(&u->md5_ctx->md5, buf, len); - - if(u->sha1_ctx) - SHA1_Update(&u->sha1_ctx->sha1, buf, len); - - if(u->calculate_crc32) - ngx_crc32_update(&u->crc32, buf, len); - - if(ulcf->max_file_size != 0) { - if(u->output_file.offset + (off_t)len > ulcf->max_file_size) - return NGX_UPLOAD_TOOLARGE; - } - - if(ngx_write_file(&u->output_file, buf, len, u->output_file.offset) == NGX_ERROR) { - ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_errno, - "write to file \"%V\" failed", &u->output_file.name); - return NGX_UPLOAD_IOERROR; - }else - return NGX_OK; - }else{ - if(ulcf->max_output_body_len != 0) { - if (u->output_body_len + len > ulcf->max_output_body_len) - return NGX_UPLOAD_TOOLARGE; - } - - u->output_body_len += len; - - b = ngx_create_temp_buf(u->request->pool, len); - - if (b == NULL) { - return NGX_ERROR; - } - - cl = ngx_alloc_chain_link(u->request->pool); - if (cl == NULL) { - return NGX_ERROR; - } - - b->last_in_chain = 0; - - cl->buf = b; - cl->next = NULL; - - b->last = ngx_cpymem(b->last, buf, len); - - if(u->chain == NULL) { - u->chain = cl; - u->last = cl; - }else{ - u->last->next = cl; - u->last = cl; - } - - return NGX_OK; - } -} /* }}} */ - -static void /* {{{ ngx_http_upload_append_str */ -ngx_http_upload_append_str(ngx_http_upload_ctx_t *u, ngx_buf_t *b, ngx_chain_t *cl, ngx_str_t *s) -{ - b->start = b->pos = s->data; - b->end = b->last = s->data + s->len; - b->memory = 1; - b->temporary = 1; - b->in_file = 0; - b->last_buf = 0; - - b->last_in_chain = 0; - b->last_buf = 0; - - cl->buf = b; - cl->next = NULL; - - if(u->chain == NULL) { - u->chain = cl; - u->last = cl; - }else{ - u->last->next = cl; - u->last = cl; - } - - u->output_body_len += s->len; -} /* }}} */ - -static ngx_int_t /* {{{ ngx_http_upload_append_field */ -ngx_http_upload_append_field(ngx_http_upload_ctx_t *u, ngx_str_t *name, ngx_str_t *value) -{ - ngx_http_upload_loc_conf_t *ulcf = ngx_http_get_module_loc_conf(u->request, ngx_http_upload_module); - ngx_str_t boundary = { u->first_part ? u->boundary.len - 2 : u->boundary.len, - u->first_part ? u->boundary.data + 2 : u->boundary.data }; - - ngx_buf_t *b; - ngx_chain_t *cl; - - if(name->len > 0) { - if(ulcf->max_output_body_len != 0) { - if(u->output_body_len + boundary.len + ngx_upload_field_part1.len + name->len - + ngx_upload_field_part2.len + value->len > ulcf->max_output_body_len) - return NGX_UPLOAD_TOOLARGE; - } - - 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; - } - - 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; - } - - 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 + 2, cl + 2, name); - - 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); - - u->output_body_len += boundary.len + ngx_upload_field_part1.len + name->len - + ngx_upload_field_part2.len + value->len; - - u->first_part = 0; - } - - return NGX_OK; -} /* }}} */ - -static void * /* {{{ ngx_http_upload_create_loc_conf */ -ngx_http_upload_create_loc_conf(ngx_conf_t *cf) -{ - ngx_http_upload_loc_conf_t *conf; - - conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upload_loc_conf_t)); - if (conf == NULL) { - return NGX_CONF_ERROR; - } - - conf->store_access = NGX_CONF_UNSET_UINT; - conf->forward_args = NGX_CONF_UNSET; - - conf->buffer_size = NGX_CONF_UNSET_SIZE; - conf->max_header_len = NGX_CONF_UNSET_SIZE; - conf->max_output_body_len = NGX_CONF_UNSET_SIZE; - conf->max_file_size = NGX_CONF_UNSET; - conf->limit_rate = NGX_CONF_UNSET_SIZE; - - /* - * conf->field_templates, - * conf->aggregate_field_templates, - * and conf->field_filters are - * zeroed by ngx_pcalloc - */ - - return conf; -} /* }}} */ - -static char * /* {{{ ngx_http_upload_merge_loc_conf */ -ngx_http_upload_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) -{ - ngx_http_upload_loc_conf_t *prev = parent; - ngx_http_upload_loc_conf_t *conf = child; - - ngx_conf_merge_str_value(conf->url, prev->url, ""); - - if(conf->url.len != 0) { -#if defined nginx_version && nginx_version >= 7052 - ngx_conf_merge_path_value(cf, - &conf->store_path, - prev->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); -#endif - } - - ngx_conf_merge_uint_value(conf->store_access, - prev->store_access, 0600); - - ngx_conf_merge_size_value(conf->buffer_size, - prev->buffer_size, - (size_t) ngx_pagesize); - - ngx_conf_merge_size_value(conf->max_header_len, - prev->max_header_len, - (size_t) 512); - - ngx_conf_merge_size_value(conf->max_output_body_len, - prev->max_output_body_len, - (size_t) 100 * 1024); - - ngx_conf_merge_off_value(conf->max_file_size, - prev->max_file_size, - 0); - - ngx_conf_merge_size_value(conf->limit_rate, prev->limit_rate, 0); - - if(conf->forward_args == NGX_CONF_UNSET) { - conf->forward_args = (prev->forward_args != NGX_CONF_UNSET) ? - prev->forward_args : 0; - } - - if(conf->field_templates == NULL) { - conf->field_templates = prev->field_templates; - } - - if(conf->aggregate_field_templates == NULL) { - conf->aggregate_field_templates = prev->aggregate_field_templates; - - 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(conf->cleanup_statuses == NULL) { - conf->cleanup_statuses = prev->cleanup_statuses; - } - - 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, *v; - - for (v = ngx_http_upload_variables; v->name.len; v++) { - var = ngx_http_add_variable(cf, &v->name, v->flags); - if (var == NULL) { - return NGX_ERROR; - } - - var->get_handler = v->get_handler; - var->data = v->data; - } - - for (v = ngx_http_upload_aggregate_variables; v->name.len; v++) { - var = ngx_http_add_variable(cf, &v->name, v->flags); - if (var == NULL) { - return NGX_ERROR; - } - - var->get_handler = v->get_handler; - var->data = v->data; - } - - 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->valid = 1; - v->no_cacheable = 0; - v->not_found = 0; - - u = ngx_http_get_module_ctx(r, ngx_http_upload_module); - - value = (ngx_str_t *) ((char *) u + data); - - v->data = value->data; - v->len = value->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); - - if(u->md5_ctx == NULL) { - 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->md5_ctx->md5_digest + MD5_DIGEST_LENGTH * 2; - - i = MD5_DIGEST_LENGTH; - - do{ - i--; - *--c = hex_table[u->md5_ctx->md5_digest[i] & 0xf]; - *--c = hex_table[u->md5_ctx->md5_digest[i] >> 4]; - }while(i != 0); - - v->data = u->md5_ctx->md5_digest; - v->len = MD5_DIGEST_LENGTH * 2; - - return NGX_OK; -} /* }}} */ - -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; - u_char *c; - u_char *hex_table; - - u = ngx_http_get_module_ctx(r, ngx_http_upload_module); - - if(u->sha1_ctx == NULL) { - 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->sha1_ctx->sha1_digest + SHA_DIGEST_LENGTH * 2; - - i = SHA_DIGEST_LENGTH; - - do{ - i--; - *--c = hex_table[u->sha1_ctx->sha1_digest[i] & 0xf]; - *--c = hex_table[u->sha1_ctx->sha1_digest[i] >> 4]; - }while(i != 0); - - v->data = u->sha1_ctx->sha1_digest; - v->len = SHA_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) -{ - 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) -{ - ngx_http_upload_ctx_t *u; - u_char *p; - off_t *value; - - u = ngx_http_get_module_ctx(r, ngx_http_upload_module); - - value = (off_t *) ((char *) u + data); - - p = ngx_palloc(r->pool, NGX_OFF_T_LEN); - if (p == NULL) { - return NGX_ERROR; - } - - v->len = ngx_sprintf(p, "%O", *value) - p; - v->valid = 1; - v->no_cacheable = 0; - v->not_found = 0; - v->data = p; - - return NGX_OK; -} /* }}} */ - -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_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->offset); - - value = cf->args->elts; - - if (*field == NULL) { - *field = ngx_array_create(cf->pool, 1, - sizeof(ngx_http_upload_field_template_t)); - if (*field == NULL) { - return NGX_CONF_ERROR; - } - } - - h = ngx_array_push(*field); - if (h == NULL) { - return NGX_CONF_ERROR; - } - - h->value.hash = 1; - h->value.key = value[1]; - h->value.value = value[2]; - h->field_lengths = NULL; - h->field_values = NULL; - h->value_lengths = NULL; - h->value_values = NULL; - - /* - * Compile field name - */ - n = ngx_http_script_variables_count(&value[1]); - - 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; - - if (ngx_http_script_compile(&sc) != NGX_OK) { - return NGX_CONF_ERROR; - } - } - - /* - * Compile field value - */ - n = ngx_http_script_variables_count(&value[2]); - - if (n > 0) { - 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; - - if (ngx_http_script_compile(&sc) != NGX_OK) { - return NGX_CONF_ERROR; - } - } - - /* - * Check for aggregate variables in script - */ - for(i = 1;i <= 2;i++) { - for (v = ngx_http_upload_aggregate_variables; v->name.len; v++) { - match = ngx_strcasestrn(value[i].data, (char*)v->name.data, v->name.len - 1); - - /* - * ngx_http_script_compile does check for final bracket earlier, - * therefore we don't need to care about it, which simplifies things - */ - if(match != NULL - && ((match - value[i].data >= 1 && match[-1] == '$') - || (match - value[i].data >= 2 && match[-2] == '$' && match[-1] == '{'))) - { - if(cmd->offset != offsetof(ngx_http_upload_loc_conf_t, aggregate_field_templates)) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - "variables upload_file_md5" - ", 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; - } - - if(v->get_handler == ngx_http_upload_md5_variable) - ulcf->md5 = 1; - - if(v->get_handler == ngx_http_upload_sha1_variable) - ulcf->sha1 = 1; - - if(v->get_handler == ngx_http_upload_crc32_variable) - ulcf->crc32 = 1; - } - } - } - - return NGX_CONF_OK; -} /* }}} */ - -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; - - ngx_str_t *value; -#if (NGX_PCRE) - ngx_int_t n; - ngx_str_t err; -#endif - ngx_http_upload_field_filter_t *f; - - value = cf->args->elts; - - if (ulcf->field_filters == NULL) { - ulcf->field_filters = ngx_array_create(cf->pool, 1, - sizeof(ngx_http_upload_field_filter_t)); - if (ulcf->field_filters == NULL) { - return NGX_CONF_ERROR; - } - } - - f = ngx_array_push(ulcf->field_filters); - if (f == NULL) { - return NGX_CONF_ERROR; - } - -#if (NGX_PCRE) - f->regex = ngx_regex_compile(&value[1], 0, cf->pool, &err); - - if (f->regex == NULL) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data); - return NGX_CONF_ERROR; - } - - n = ngx_regex_capture_count(f->regex); - - if (n < 0) { - ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, - ngx_regex_capture_count_n " failed for " - "pattern \"%V\"", &value[1]); - return NGX_CONF_ERROR; - } - - f->ncaptures = n; -#else - f->text.len = value[1].len; - f->text.data = value[1].data; -#endif - - 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 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; - ngx_http_upload_loc_conf_t *ulcf = conf; - - ngx_str_t *value, *url; - - value = cf->args->elts; - url = &value[1]; - - clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); - clcf->handler = ngx_http_upload_handler; - - ulcf->url = *url; - - return NGX_CONF_OK; -} /* }}} */ - -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; - ngx_buf_t *b; - ngx_chain_t *cl, **next; - ngx_http_request_body_t *rb; - ngx_http_core_loc_conf_t *clcf; - ngx_http_upload_ctx_t *u = ngx_http_get_module_ctx(r, ngx_http_upload_module); - -#if defined nginx_version && nginx_version >= 8011 - r->main->count++; -#endif - - if (r->request_body || r->discard_body) { - return NGX_OK; - } - - rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); - if (rb == NULL) { - upload_shutdown_ctx(u); - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - r->request_body = rb; - - if (r->headers_in.content_length_n <= 0) { - upload_shutdown_ctx(u); - return NGX_HTTP_BAD_REQUEST; - } - - /* - * set by ngx_pcalloc(): - * - * rb->bufs = NULL; - * rb->buf = NULL; - * rb->rest = 0; - */ - - preread = r->header_in->last - r->header_in->pos; - - if (preread) { - - /* there is the pre-read part of the request body */ - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, - "http client request body preread %uz", preread); - - u->received = preread; - - b = ngx_calloc_buf(r->pool); - if (b == NULL) { - upload_shutdown_ctx(u); - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - b->temporary = 1; - b->start = r->header_in->pos; - b->pos = r->header_in->pos; - b->last = r->header_in->last; - b->end = r->header_in->end; - - rb->bufs = ngx_alloc_chain_link(r->pool); - if (rb->bufs == NULL) { - upload_shutdown_ctx(u); - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - rb->bufs->buf = b; - rb->bufs->next = NULL; - - if (preread >= r->headers_in.content_length_n) { - - /* the whole request body was pre-read */ - - r->header_in->pos += r->headers_in.content_length_n; - r->request_length += r->headers_in.content_length_n; - - if (ngx_http_process_request_body(r, rb->bufs) != NGX_OK) { - upload_shutdown_ctx(u); - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - upload_shutdown_ctx(u); - - return ngx_http_upload_body_handler(r); - } - - /* - * to not consider the body as pipelined request in - * ngx_http_set_keepalive() - */ - r->header_in->pos = r->header_in->last; - - r->request_length += preread; - - rb->rest = r->headers_in.content_length_n - preread; - - if (rb->rest <= (off_t) (b->end - b->last)) { - - /* the whole request body may be placed in r->header_in */ - - rb->to_write = rb->bufs; - - r->read_event_handler = ngx_http_read_upload_client_request_body_handler; - - return ngx_http_do_read_upload_client_request_body(r); - } - - next = &rb->bufs->next; - - } else { - b = NULL; - rb->rest = r->headers_in.content_length_n; - next = &rb->bufs; - } - - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - - size = clcf->client_body_buffer_size; - size += size >> 2; - - if (rb->rest < (ssize_t) size) { - size = rb->rest; - - if (r->request_body_in_single_buf) { - size += preread; - } - - } else { - size = clcf->client_body_buffer_size; - - /* disable copying buffer for r->request_body_in_single_buf */ - b = NULL; - } - - rb->buf = ngx_create_temp_buf(r->pool, size); - if (rb->buf == NULL) { - upload_shutdown_ctx(u); - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - cl = ngx_alloc_chain_link(r->pool); - if (cl == NULL) { - upload_shutdown_ctx(u); - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - cl->buf = rb->buf; - cl->next = NULL; - - if (b && r->request_body_in_single_buf) { - size = b->last - b->pos; - ngx_memcpy(rb->buf->pos, b->pos, size); - rb->buf->last += size; - - next = &rb->bufs; - } - - *next = cl; - - rb->to_write = rb->bufs; - - r->read_event_handler = ngx_http_read_upload_client_request_body_handler; - - return ngx_http_do_read_upload_client_request_body(r); -} /* }}} */ - -static void /* {{{ ngx_http_read_upload_client_request_body_handler */ -ngx_http_read_upload_client_request_body_handler(ngx_http_request_t *r) -{ - ngx_int_t rc; - ngx_http_upload_ctx_t *u = ngx_http_get_module_ctx(r, ngx_http_upload_module); - ngx_event_t *rev = r->connection->read; - ngx_http_core_loc_conf_t *clcf; - - if (rev->timedout) { - if(!rev->delayed) { - r->connection->timedout = 1; - upload_shutdown_ctx(u); - ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT); - return; - } - - rev->timedout = 0; - rev->delayed = 0; - - if (!rev->ready) { - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - ngx_add_timer(rev, clcf->client_body_timeout); - - if (ngx_handle_read_event(rev, clcf->send_lowat) != NGX_OK) { - upload_shutdown_ctx(u); - ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - } - - return; - } - } - else{ - if (r->connection->read->delayed) { - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, - "http read delayed"); - - if (ngx_handle_read_event(rev, clcf->send_lowat) != NGX_OK) { - upload_shutdown_ctx(u); - ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); - } - - return; - } - } - - rc = ngx_http_do_read_upload_client_request_body(r); - - if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { - upload_shutdown_ctx(u); - ngx_http_finalize_request(r, rc); - } -} /* }}} */ - -static ngx_int_t /* {{{ ngx_http_do_read_upload_client_request_body */ -ngx_http_do_read_upload_client_request_body(ngx_http_request_t *r) -{ - ssize_t size, n, limit; - ngx_connection_t *c; - ngx_http_request_body_t *rb; - ngx_http_core_loc_conf_t *clcf; - ngx_http_upload_ctx_t *u = ngx_http_get_module_ctx(r, ngx_http_upload_module); - ngx_int_t rc; - ngx_msec_t delay; - - c = r->connection; - rb = r->request_body; - - ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http read client request body"); - - for ( ;; ) { - for ( ;; ) { - if (rb->buf->last == rb->buf->end) { - - rc = ngx_http_process_request_body(r, rb->to_write); - - switch(rc) { - case NGX_OK: - break; - case NGX_UPLOAD_MALFORMED: - return NGX_HTTP_BAD_REQUEST; - case NGX_UPLOAD_TOOLARGE: - return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; - case NGX_UPLOAD_IOERROR: - return NGX_HTTP_SERVICE_UNAVAILABLE; - case NGX_UPLOAD_NOMEM: case NGX_UPLOAD_SCRIPTERROR: - default: - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - rb->to_write = rb->bufs->next ? rb->bufs->next : rb->bufs; - rb->buf->last = rb->buf->start; - } - - size = rb->buf->end - rb->buf->last; - - if ((off_t)size > rb->rest) { - size = (size_t)rb->rest; - } - - if (u->limit_rate) { - limit = u->limit_rate * (ngx_time() - r->start_sec + 1) - u->received; - - if (limit < 0) { - c->read->delayed = 1; - ngx_add_timer(c->read, - (ngx_msec_t) (- limit * 1000 / u->limit_rate + 1)); - - return NGX_AGAIN; - } - - if(limit > 0 && size > limit) { - size = limit; - } - } - - n = c->recv(c, rb->buf->last, size); - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http client request body recv %z", n); - - if (n == NGX_AGAIN) { - break; - } - - if (n == 0) { - ngx_log_error(NGX_LOG_INFO, c->log, 0, - "client closed prematurely connection"); - } - - if (n == 0 || n == NGX_ERROR) { - c->error = 1; - return NGX_HTTP_BAD_REQUEST; - } - - rb->buf->last += n; - rb->rest -= n; - r->request_length += n; - u->received += n; - - if (rb->rest == 0) { - break; - } - - if (rb->buf->last < rb->buf->end) { - break; - } - - if (u->limit_rate) { - delay = (ngx_msec_t) (n * 1000 / u->limit_rate + 1); - - if (delay > 0) { - c->read->delayed = 1; - ngx_add_timer(c->read, delay); - return NGX_AGAIN; - } - } - } - - ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, - "http client request body rest %uz", rb->rest); - - if (rb->rest == 0) { - break; - } - - if (!c->read->ready) { - clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); - ngx_add_timer(c->read, clcf->client_body_timeout); - - if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - return NGX_AGAIN; - } - } - - if (c->read->timer_set) { - ngx_del_timer(c->read); - } - - rc = ngx_http_process_request_body(r, rb->to_write); - - switch(rc) { - case NGX_OK: - break; - case NGX_UPLOAD_MALFORMED: - return NGX_HTTP_BAD_REQUEST; - case NGX_UPLOAD_TOOLARGE: - return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; - case NGX_UPLOAD_IOERROR: - return NGX_HTTP_SERVICE_UNAVAILABLE; - case NGX_UPLOAD_NOMEM: case NGX_UPLOAD_SCRIPTERROR: - default: - return NGX_HTTP_INTERNAL_SERVER_ERROR; - } - - upload_shutdown_ctx(u); - - return ngx_http_upload_body_handler(r); -} /* }}} */ - -static ngx_int_t /* {{{ ngx_http_process_request_body */ -ngx_http_process_request_body(ngx_http_request_t *r, ngx_chain_t *body) -{ - ngx_int_t rc; - ngx_http_upload_ctx_t *u = ngx_http_get_module_ctx(r, ngx_http_upload_module); - - // Feed all the buffers into multipart/form-data processor - while(body) { - rc = upload_process_buf(u, body->buf->pos, body->buf->last); - - if(rc != NGX_OK) - return rc; - - // Signal end of body - if(body->buf->last_buf) { - rc = upload_process_buf(u, body->buf->pos, body->buf->pos); - - if(rc != NGX_OK) - return rc; - } - - body = body->next; - } - - return NGX_OK; -} /* }}} */ - -static ngx_int_t upload_parse_part_header(ngx_http_upload_ctx_t *upload_ctx, char *header, char *header_end) { /* {{{ */ - if(!strncasecmp(CONTENT_DISPOSITION_STRING, header, sizeof(CONTENT_DISPOSITION_STRING) - 1)) { - char *p = header + sizeof(CONTENT_DISPOSITION_STRING) - 1; - char *filename_start, *filename_end; - char *fieldname_start, *fieldname_end; - - p += strspn(p, " "); - - if(strncasecmp(FORM_DATA_STRING, p, sizeof(FORM_DATA_STRING)-1) && - strncasecmp(ATTACHMENT_STRING, p, sizeof(ATTACHMENT_STRING)-1)) { - ngx_log_error(NGX_LOG_ERR, upload_ctx->log, 0, - "Content-Disposition is not form-data or attachment"); - return NGX_UPLOAD_MALFORMED; - } - - filename_start = strstr(p, FILENAME_STRING); - - if(filename_start != 0) { - char *q; - - filename_start += sizeof(FILENAME_STRING)-1; - - filename_end = filename_start + strcspn(filename_start, "\""); - - if(*filename_end != '\"') { - ngx_log_error(NGX_LOG_ERR, upload_ctx->log, 0, - "malformed filename in part header"); - return NGX_UPLOAD_MALFORMED; - } - - /* - * IE sends full path, strip path from filename - * Also strip all UNIX path references - */ - for(q = filename_end-1; q > filename_start; q--) - if(*q == '\\' || *q == '/') { - filename_start = q+1; - break; - } - - upload_ctx->file_name.len = filename_end - filename_start; - upload_ctx->file_name.data = ngx_palloc(upload_ctx->request->pool, upload_ctx->file_name.len + 1); - - if(upload_ctx->file_name.data == NULL) - return NGX_UPLOAD_NOMEM; - - strncpy((char*)upload_ctx->file_name.data, filename_start, filename_end - filename_start); - } - - fieldname_start = p; - - do{ - fieldname_start = strstr(fieldname_start, FIELDNAME_STRING); - }while((fieldname_start != 0) && (fieldname_start + sizeof(FIELDNAME_STRING) - 1 == filename_start)); - - if(fieldname_start != 0) { - fieldname_start += sizeof(FIELDNAME_STRING)-1; - - if(fieldname_start != filename_start) { - fieldname_end = fieldname_start + strcspn(fieldname_start, "\""); - - if(*fieldname_end != '\"') { - ngx_log_error(NGX_LOG_ERR, upload_ctx->log, 0, - "malformed fieldname in part header"); - return NGX_UPLOAD_MALFORMED; - } - - upload_ctx->field_name.len = fieldname_end - fieldname_start; - upload_ctx->field_name.data = ngx_pcalloc(upload_ctx->request->pool, upload_ctx->field_name.len + 1); - - if(upload_ctx->field_name.data == NULL) - return NGX_UPLOAD_NOMEM; - - strncpy((char*)upload_ctx->field_name.data, fieldname_start, fieldname_end - fieldname_start); - } - } - }else if(!strncasecmp(CONTENT_TYPE_STRING, header, sizeof(CONTENT_TYPE_STRING)-1)) { - char *content_type_str = header + sizeof(CONTENT_TYPE_STRING)-1; - - content_type_str += strspn(content_type_str, " "); - upload_ctx->content_type.len = header_end - content_type_str; - - if(upload_ctx->content_type.len == 0) { - ngx_log_error(NGX_LOG_ERR, upload_ctx->log, 0, - "empty Content-Type in part header"); - return NGX_UPLOAD_MALFORMED; // Empty Content-Type field - } - - upload_ctx->content_type.data = ngx_pcalloc(upload_ctx->request->pool, upload_ctx->content_type.len + 1); - - if(upload_ctx->content_type.data == NULL) - return NGX_UPLOAD_NOMEM; // Unable to allocate memory for string - - strncpy((char*)upload_ctx->content_type.data, content_type_str, upload_ctx->content_type.len); - } - - return NGX_OK; -} /* }}} */ - -static void upload_discard_part_attributes(ngx_http_upload_ctx_t *upload_ctx) { /* {{{ */ - upload_ctx->file_name.len = 0; - upload_ctx->file_name.data = NULL; - - upload_ctx->field_name.len = 0; - upload_ctx->field_name.data = NULL; - - upload_ctx->content_type.len = 0; - upload_ctx->content_type.data = NULL; -} /* }}} */ - -static ngx_int_t upload_start_file(ngx_http_upload_ctx_t *upload_ctx) { /* {{{ */ - // Call user-defined event handler - if(upload_ctx->start_part_f) - return upload_ctx->start_part_f(upload_ctx); - else - return NGX_OK; -} /* }}} */ - -static void upload_finish_file(ngx_http_upload_ctx_t *upload_ctx) { /* {{{ */ - // Call user-defined event handler - if(upload_ctx->finish_part_f) - upload_ctx->finish_part_f(upload_ctx); - - upload_discard_part_attributes(upload_ctx); - - upload_ctx->discard_data = 0; -} /* }}} */ - -static void upload_abort_file(ngx_http_upload_ctx_t *upload_ctx) { /* {{{ */ - if(upload_ctx->abort_part_f) - upload_ctx->abort_part_f(upload_ctx); - - upload_discard_part_attributes(upload_ctx); - - upload_ctx->discard_data = 0; -} /* }}} */ - -static void upload_flush_output_buffer(ngx_http_upload_ctx_t *upload_ctx) { /* {{{ */ - if(upload_ctx->output_buffer_pos > upload_ctx->output_buffer) { - if(upload_ctx->flush_output_buffer_f) - if(upload_ctx->flush_output_buffer_f(upload_ctx, (void*)upload_ctx->output_buffer, - (size_t)(upload_ctx->output_buffer_pos - upload_ctx->output_buffer)) != NGX_OK) - upload_ctx->discard_data = 1; - - upload_ctx->output_buffer_pos = upload_ctx->output_buffer; - } -} /* }}} */ - -static void upload_init_ctx(ngx_http_upload_ctx_t *upload_ctx) { /* {{{ */ - upload_ctx->boundary.data = upload_ctx->boundary_start = upload_ctx->boundary_pos = 0; - - upload_ctx->state = upload_state_boundary_seek; - - upload_ctx->content_type.len = 0; - upload_ctx->content_type.data = NULL; - - upload_ctx->field_name.len = 0; - upload_ctx->field_name.data = NULL; - - upload_ctx->file_name.len = 0; - upload_ctx->file_name.data = NULL; - - upload_ctx->discard_data = 0; - - upload_ctx->start_part_f = ngx_http_upload_start_handler; - upload_ctx->finish_part_f = ngx_http_upload_finish_handler; - upload_ctx->abort_part_f = ngx_http_upload_abort_handler; - upload_ctx->flush_output_buffer_f = ngx_http_upload_flush_output_buffer; -} /* }}} */ - -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) { - upload_flush_output_buffer(upload_ctx); - upload_abort_file(upload_ctx); - } - - upload_discard_part_attributes(upload_ctx); - } -} /* }}} */ - -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; - - upload_ctx->header_accumulator = ngx_pcalloc(upload_ctx->request->pool, ulcf->max_header_len + 1); - - if(upload_ctx->header_accumulator == NULL) - return NGX_ERROR; - - upload_ctx->header_accumulator_pos = upload_ctx->header_accumulator; - upload_ctx->header_accumulator_end = upload_ctx->header_accumulator + ulcf->max_header_len; - - upload_ctx->output_buffer = ngx_pcalloc(upload_ctx->request->pool, ulcf->buffer_size); - - if(upload_ctx->output_buffer == NULL) - return NGX_ERROR; - - upload_ctx->output_buffer_pos = upload_ctx->output_buffer; - upload_ctx->output_buffer_end = upload_ctx->output_buffer + ulcf->buffer_size; - - upload_ctx->header_accumulator_pos = upload_ctx->header_accumulator; - - upload_ctx->first_part = 1; - - return NGX_OK; -} /* }}} */ - -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; - - upload_ctx->boundary.data = 0; - - if(mime_type_end_ptr == NULL) { - ngx_log_error(NGX_LOG_ERR, upload_ctx->log, 0, - "no boundary found in Content-Type"); - return NGX_HTTP_BAD_REQUEST; - } - - if(ngx_strncasecmp(content_type->data, (u_char*) MULTIPART_FORM_DATA_STRING, - sizeof(MULTIPART_FORM_DATA_STRING) - 1)) { - ngx_log_error(NGX_LOG_ERR, upload_ctx->log, 0, - "Content-Type is not multipart/form-data: %V", content_type); - return NGX_HTTP_UNSUPPORTED_MEDIA_TYPE; - } - - boundary_start_ptr = ngx_strstrn(mime_type_end_ptr, BOUNDARY_STRING, sizeof(BOUNDARY_STRING) - 2); - - if(boundary_start_ptr == NULL) { - ngx_log_error(NGX_LOG_ERR, upload_ctx->log, 0, - "no boundary found in Content-Type"); - return NGX_HTTP_BAD_REQUEST; // No boundary found - } - - boundary_start_ptr += sizeof(BOUNDARY_STRING) - 1; - boundary_end_ptr = boundary_start_ptr + strcspn((char*)boundary_start_ptr, " ;\n\r"); - - if(boundary_end_ptr == boundary_start_ptr) { - ngx_log_error(NGX_LOG_ERR, upload_ctx->log, 0, - "boundary is empty"); - return NGX_HTTP_BAD_REQUEST; - } - - // Allocate memory for entire boundary plus \r\n-- plus terminating character - upload_ctx->boundary.len = boundary_end_ptr - boundary_start_ptr + 4; - upload_ctx->boundary.data = ngx_palloc(upload_ctx->request->pool, upload_ctx->boundary.len + 1); - - if(upload_ctx->boundary.data == NULL) - return NGX_HTTP_INTERNAL_SERVER_ERROR; - - ngx_cpystrn(upload_ctx->boundary.data + 4, boundary_start_ptr, - boundary_end_ptr - boundary_start_ptr + 1); - - // Prepend boundary data by \r\n-- - upload_ctx->boundary.data[0] = '\r'; - upload_ctx->boundary.data[1] = '\n'; - upload_ctx->boundary.data[2] = '-'; - upload_ctx->boundary.data[3] = '-'; - - /* - * NOTE: first boundary doesn't start with \r\n. Here we - * advance 2 positions forward. We will return 2 positions back - * later - */ - upload_ctx->boundary_start = upload_ctx->boundary.data + 2; - upload_ctx->boundary_pos = upload_ctx->boundary_start; - - return NGX_OK; -} /* }}} */ - -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; - - upload_ctx->output_buffer_pos++; - - if(upload_ctx->output_buffer_pos == upload_ctx->output_buffer_end) - upload_flush_output_buffer(upload_ctx); - } -} /* }}} */ - -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; - - // No more data? - if(start == end) { - if(upload_ctx->state != upload_state_finish) - return NGX_UPLOAD_MALFORMED; // Signal error if still haven't finished - else - return NGX_OK; // Otherwise confirm end of stream - } - - for(p = start; p != end; p++) { - switch(upload_ctx->state) { - /* - * Seek the boundary - */ - case upload_state_boundary_seek: - if(*p == *upload_ctx->boundary_pos) - upload_ctx->boundary_pos++; - else - upload_ctx->boundary_pos = upload_ctx->boundary_start; - - if(upload_ctx->boundary_pos == upload_ctx->boundary.data + upload_ctx->boundary.len) { - upload_ctx->state = upload_state_after_boundary; - upload_ctx->boundary_start = upload_ctx->boundary.data; - upload_ctx->boundary_pos = upload_ctx->boundary_start; - } - break; - case upload_state_after_boundary: - switch(*p) { - case '\n': - upload_ctx->state = upload_state_headers; - upload_ctx->header_accumulator_pos = upload_ctx->header_accumulator; - case '\r': - break; - case '-': - upload_ctx->state = upload_state_finish; - break; - } - break; - /* - * Collect and store headers - */ - case upload_state_headers: - switch(*p) { - case '\n': - if(upload_ctx->header_accumulator_pos == upload_ctx->header_accumulator) { - upload_ctx->is_file = (upload_ctx->file_name.data == 0) || (upload_ctx->file_name.len == 0) ? 0 : 1; - - rc = upload_start_file(upload_ctx); - - if(rc != NGX_OK) { - upload_ctx->state = upload_state_finish; - return rc; // User requested to cancel processing - } else { - upload_ctx->state = upload_state_data; - upload_ctx->output_buffer_pos = upload_ctx->output_buffer; - } - } else { - *upload_ctx->header_accumulator_pos = '\0'; - - rc = upload_parse_part_header(upload_ctx, (char*)upload_ctx->header_accumulator, - (char*)upload_ctx->header_accumulator_pos); - - if(rc != NGX_OK) { - upload_ctx->state = upload_state_finish; - return rc; // Malformed header - } else - upload_ctx->header_accumulator_pos = upload_ctx->header_accumulator; - } - case '\r': - break; - default: - if(upload_ctx->header_accumulator_pos < upload_ctx->header_accumulator_end - 1) - *upload_ctx->header_accumulator_pos++ = *p; - else { - upload_ctx->state = upload_state_finish; - return NGX_UPLOAD_MALFORMED; // Header is too long - } - break; - } - break; - /* - * Search for separating or terminating boundary - * and output data simultaneously - */ - case upload_state_data: - if(*p == *upload_ctx->boundary_pos) - upload_ctx->boundary_pos++; - else { - if(upload_ctx->boundary_pos == upload_ctx->boundary_start) { - // IE 5.0 bug workaround - if(*p == '\n') { - /* - * Set current matched position beyond LF and prevent outputting - * CR in case of unsuccessful match by altering boundary_start - */ - upload_ctx->boundary_pos = upload_ctx->boundary.data + 2; - upload_ctx->boundary_start = upload_ctx->boundary.data + 1; - } else - upload_putc(upload_ctx, *p); - } else { - // Output partially matched lump of boundary - u_char *q; - for(q = upload_ctx->boundary_start; q != upload_ctx->boundary_pos; q++) - upload_putc(upload_ctx, *q); - - p--; // Repeat reading last character - - // And reset matched position - upload_ctx->boundary_start = upload_ctx->boundary.data; - upload_ctx->boundary_pos = upload_ctx->boundary_start; - } - } - - if(upload_ctx->boundary_pos == upload_ctx->boundary.data + upload_ctx->boundary.len) { - upload_ctx->state = upload_state_after_boundary; - upload_ctx->boundary_pos = upload_ctx->boundary_start; - - upload_flush_output_buffer(upload_ctx); - if(!upload_ctx->discard_data) - upload_finish_file(upload_ctx); - else - upload_abort_file(upload_ctx); - } - break; - /* - * Skip trailing garbage - */ - case upload_state_finish: - break; - } - } - - 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 - ); - } - } -} /* }}} */ - diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/nginx_upload_module-2.0.10/upload.html /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/nginx_upload_module-2.0.10/upload.html --- nginx-0.6.34/nginx_upload_module-2.0.10/upload.html 2009-09-17 17:12:08.000000000 +0100 +++ nginx-0.6.39~ppa1~hardy/nginx_upload_module-2.0.10/upload.html 1970-01-01 01:00:00.000000000 +0100 @@ -1,18 +0,0 @@ - - -Test upload - - -

Select files to upload

-
-
-
-
-
-
-
- - -
- - diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/nginx_upload_module-2.0.8/Changelog /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/nginx_upload_module-2.0.8/Changelog --- nginx-0.6.34/nginx_upload_module-2.0.8/Changelog 1970-01-01 01:00:00.000000000 +0100 +++ nginx-0.6.39~ppa1~hardy/nginx_upload_module-2.0.8/Changelog 2008-12-20 17:18:01.000000000 +0000 @@ -0,0 +1,59 @@ + +Version 2.0.8 + * Change: support for named locations + * Fixed bug: crash on missing Content-Type request header + * Fixed bug: compilation problem on amd 64 + +Version 2.0.7 + * Change: file size and output body size restrictions + * Added feature: directive upload_pass_args enables forwarding + of request arguments to a backend. Thanks to Todd Fisher. + +Version 2.0.6 + * Fixed bug: zero variables in aggregate field name caused allocation + of random amount of memory. Thanks to Dmitry Dedukhin. + * Fixed bug: Prevent generation of a field in case of empty field name + +Version 2.0.5 + * Fixed bug: prevent leaking of file descriptors on a timeout (unconfirmed problem). + * Fixed bug: variables in field values in upload_set_form_field and + upload_aggregate_form_field directives were not working if field name + contained 0 variables. + * Added feature: directive upload_cleanup now specifies statuses, + which initiate removal of uploaded files. Used for cleanup after + failure of a backend. + * Added feature: aggregate variable upload_file_crc32 allows to calculate + CRC32 if file on the fly. + * Fixed bug: Indicator of necessity to calculate SHA1 sum was not inheritable + from server configuration. + +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. + * Fixed bug: missing CRLF at the end of resulting body. + * Change: optimized out some unnecessary memory allocations and zeroing. + +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. + * upload_store_access directive did not work at all. Permissions were + defaulted to user:rw. Thanks to Brian Moran. + * In case of any errors at the last chunk of request body only 500 Internal Server Error + was generated intead of 400 Bad Request and 503 Service Unavailable. + * Fixed copyrights for temporary file name generation code + * Fixed compilation issue on 0.6.32. Thanks to Tomas Pollak. + * Added directive upload_pass_form_field to specify fields + to pass to backend. Fixes security hole found by Brian Moran. + +Version 2.0.2 + * Fixed crash in logging filename while aborting upload + * Added feasible debug logging + * Added support for variables to generate form fields + in resulting request body + * Added missing logging of errno after write failures + * Simplified upload abortion logic; simply discarding + already added fields + * Now returning explicit error code after script failures + to be able to generate Internal server error diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/nginx_upload_module-2.0.8/config /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/nginx_upload_module-2.0.8/config --- nginx-0.6.34/nginx_upload_module-2.0.8/config 1970-01-01 01:00:00.000000000 +0100 +++ nginx-0.6.39~ppa1~hardy/nginx_upload_module-2.0.8/config 2008-08-06 19:47:33.000000000 +0100 @@ -0,0 +1,5 @@ +USE_MD5=YES +USE_SHA1=YES +ngx_addon_name=ngx_http_upload_module +HTTP_MODULES="$HTTP_MODULES ngx_http_upload_module" +NGX_ADDON_SRCS="$NGX_ADDON_SRCS $ngx_addon_dir/ngx_http_upload_module.c" diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/nginx_upload_module-2.0.8/example.php /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/nginx_upload_module-2.0.8/example.php --- nginx-0.6.34/nginx_upload_module-2.0.8/example.php 1970-01-01 01:00:00.000000000 +0100 +++ nginx-0.6.39~ppa1~hardy/nginx_upload_module-2.0.8/example.php 2008-08-06 19:47:33.000000000 +0100 @@ -0,0 +1,47 @@ + + + +Test upload + + +Uploaded files:"; + echo ""; + + echo ""; + + for ($i=1;$i<=$slots;$i++){ + $key = $header_prefix.$i; + if (array_key_exists($key."_name", $_POST) && array_key_exists($key."_path",$_POST)) { + $tmp_name = $_POST[$key."_path"]; + $name = $_POST[$key."_name"]; + $content_type = $_POST[$key."_content_type"]; + $md5 = $_POST[$key."_md5"]; + $size = $_POST[$key."_size"]; + + echo ""; + } + } + + echo "
NameLocationContent typeMD5Size
$name$tmp_name$content_type$md5$size
"; + +}else{?> +

Select files to upload

+
+
+
+
+
+
+
+ + +
+ + + diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/nginx_upload_module-2.0.8/LICENCE /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/nginx_upload_module-2.0.8/LICENCE --- nginx-0.6.34/nginx_upload_module-2.0.8/LICENCE 1970-01-01 01:00:00.000000000 +0100 +++ nginx-0.6.39~ppa1~hardy/nginx_upload_module-2.0.8/LICENCE 2008-08-06 19:47:33.000000000 +0100 @@ -0,0 +1,24 @@ +* Copyright (c) 2006, 2008, Valery Kholodkov +* All rights reserved. +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are met: +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above copyright +* notice, this list of conditions and the following disclaimer in the +* documentation and/or other materials provided with the distribution. +* * Neither the name of the Valery Kholodkov nor the +* names of its contributors may be used to endorse or promote products +* derived from this software without specific prior written permission. +* +* THIS SOFTWARE IS PROVIDED BY VALERY KHOLODKOV ''AS IS'' AND ANY +* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +* DISCLAIMED. IN NO EVENT SHALL VALERY KHOLODKOV BE LIABLE FOR ANY +* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/nginx_upload_module-2.0.8/nginx.conf /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/nginx_upload_module-2.0.8/nginx.conf --- nginx-0.6.34/nginx_upload_module-2.0.8/nginx.conf 1970-01-01 01:00:00.000000000 +0100 +++ nginx-0.6.39~ppa1~hardy/nginx_upload_module-2.0.8/nginx.conf 2008-12-20 17:19:47.000000000 +0000 @@ -0,0 +1,49 @@ + +worker_processes 20; + +error_log logs/error.log notice; + +working_directory /usr/local/nginx; + +events { + worker_connections 1024; +} + +http { + include mime.types; + default_type application/octet-stream; + + server { + listen 80; + client_max_body_size 100m; + + # Upload form should be submitted to this location + location /upload { + # Pass altered request body to this location + upload_pass @test; + + # Store files to this directory + # The directory is hashed, subdirectories 0 1 2 3 4 5 6 7 8 9 should exist + upload_store /tmp 1; + + # Allow uploaded files to be read only by user + upload_store_access user:r; + + # Set specified fields in request body + upload_set_form_field "${upload_field_name}_name" $upload_file_name; + upload_set_form_field "${upload_field_name}_content_type" $upload_content_type; + upload_set_form_field "${upload_field_name}_path" $upload_tmp_path; + + # Inform backend about hash and size of a file + upload_aggregate_form_field "${upload_field_name}_md5" $upload_file_md5; + upload_aggregate_form_field "${upload_field_name}_size" $upload_file_size; + + upload_pass_form_field "^submit$|^description$"; + } + + # Pass altered request body to a backend + location @test { + proxy_pass http://localhost:8080; + } + } +} diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/nginx_upload_module-2.0.8/ngx_http_upload_module.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/nginx_upload_module-2.0.8/ngx_http_upload_module.c --- nginx-0.6.34/nginx_upload_module-2.0.8/ngx_http_upload_module.c 1970-01-01 01:00:00.000000000 +0100 +++ nginx-0.6.39~ppa1~hardy/nginx_upload_module-2.0.8/ngx_http_upload_module.c 2008-12-20 20:58:16.000000000 +0000 @@ -0,0 +1,2451 @@ +/* + * Copyright (C) 2006, 2008 Valery Kholodkov + * Client body reception code Copyright (c) 2002-2007 Igor Sysoev + * Temporary file name generation code Copyright (c) 2002-2007 Igor Sysoev + */ +#include +#include +#include + +#if (NGX_HAVE_OPENSSL_MD5_H) +#include +#else +#include +#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 +#else +#include +#endif + +#define MULTIPART_FORM_DATA_STRING "multipart/form-data" +#define BOUNDARY_STRING "boundary=" +#define CONTENT_DISPOSITION_STRING "Content-Disposition:" +#define CONTENT_TYPE_STRING "Content-Type:" +#define FORM_DATA_STRING "form-data" +#define ATTACHMENT_STRING "attachment" +#define FILENAME_STRING "filename=\"" +#define FIELDNAME_STRING "name=\"" + +#define NGX_UPLOAD_MALFORMED -1 +#define NGX_UPLOAD_NOMEM -2 +#define NGX_UPLOAD_IOERROR -3 +#define NGX_UPLOAD_SCRIPTERROR -4 +#define NGX_UPLOAD_TOOLARGE -5 + +/* + * State of multipart/form-data parser + */ +typedef enum { + upload_state_boundary_seek, + upload_state_after_boundary, + upload_state_headers, + upload_state_data, + upload_state_finish +} upload_state_t; + +/* + * Template for a field to generate in output form + */ +typedef struct { + ngx_table_elt_t value; + ngx_array_t *field_lengths; + ngx_array_t *field_values; + ngx_array_t *value_lengths; + ngx_array_t *value_values; +} ngx_http_upload_field_template_t; + +/* + * Filter for fields in output form + */ +typedef struct { +#if (NGX_PCRE) + ngx_regex_t *regex; + ngx_int_t ncaptures; +#else + ngx_str_t text; +#endif +} ngx_http_upload_field_filter_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_str_t url; + ngx_path_t *store_path; + ngx_uint_t store_access; + size_t buffer_size; + size_t max_header_len; + size_t max_output_body_len; + off_t max_file_size; + ngx_array_t *field_templates; + ngx_array_t *aggregate_field_templates; + ngx_array_t *field_filters; + ngx_array_t *cleanup_statuses; + ngx_flag_t forward_args; + + 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 { + MD5_CTX md5; + u_char md5_digest[MD5_DIGEST_LENGTH * 2]; +} ngx_http_upload_md5_ctx_t; + +typedef struct ngx_http_upload_sha1_ctx_s { + SHA_CTX sha1; + u_char sha1_digest[SHA_DIGEST_LENGTH * 2]; +} ngx_http_upload_sha1_ctx_t; + +/* + * Upload module context + */ +typedef struct ngx_http_upload_ctx_s { + ngx_str_t boundary; + u_char *boundary_start; + u_char *boundary_pos; + + upload_state_t state; + + u_char *header_accumulator; + u_char *header_accumulator_end; + u_char *header_accumulator_pos; + + ngx_str_t field_name; + ngx_str_t file_name; + ngx_str_t content_type; + + u_char *output_buffer; + u_char *output_buffer_end; + u_char *output_buffer_pos; + + 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); + ngx_int_t (*flush_output_buffer_f)(struct ngx_http_upload_ctx_s *upload_ctx, u_char *buf, size_t len); + + ngx_http_request_t *request; + ngx_log_t *log; + + ngx_file_t output_file; + ngx_chain_t *chain; + ngx_chain_t *last; + ngx_chain_t *checkpoint; + size_t output_body_len; + + ngx_pool_cleanup_t *cln; + + ngx_http_upload_md5_ctx_t *md5_ctx; + ngx_http_upload_sha1_ctx_t *sha1_ctx; + uint32_t crc32; + + 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; + +static ngx_int_t ngx_http_upload_handler(ngx_http_request_t *r); +static ngx_int_t ngx_http_upload_body_handler(ngx_http_request_t *r); + +static void *ngx_http_upload_create_loc_conf(ngx_conf_t *cf); +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); +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 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); +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); + +static ngx_int_t ngx_http_upload_flush_output_buffer(ngx_http_upload_ctx_t *u, + u_char *buf, size_t len); +static ngx_int_t ngx_http_upload_append_field(ngx_http_upload_ctx_t *u, + ngx_str_t *name, ngx_str_t *value); + +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); + +static ngx_int_t ngx_http_read_upload_client_request_body(ngx_http_request_t *r); + +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); + +/* + * upload_init_ctx + * + * Initialize upload context. Memory for upload context which is being passed + * as upload_ctx parameter could be allocated anywhere and should not be freed + * prior to upload_shutdown_ctx call. + * + * IMPORTANT: + * + * After initialization the following routine SHOULD BE called: + * + * upload_parse_content_type -- to assign part boundary + * + * Parameter: + * upload_ctx -- upload context which is being initialized + * + */ +static void upload_init_ctx(ngx_http_upload_ctx_t *upload_ctx); + +/* + * upload_shutdown_ctx + * + * Shutdown upload context. Discard all remaining data and + * free all memory associated with upload context. + * + * Parameter: + * upload_ctx -- upload context which is being shut down + * + */ +static void upload_shutdown_ctx(ngx_http_upload_ctx_t *upload_ctx); + +/* + * upload_start + * + * Starts multipart stream processing. Initializes internal buffers + * and pointers + * + * Parameter: + * upload_ctx -- upload context which is being initialized + * + * Return value: + * NGX_OK on success + * NGX_ERROR if error has occured + * + */ +static ngx_int_t upload_start(ngx_http_upload_ctx_t *upload_ctx, ngx_http_upload_loc_conf_t *ulcf); + +/* + * upload_parse_content_type + * + * Parse and verify content type from HTTP header, extract boundary and + * assign it to upload context + * + * Parameters: + * upload_ctx -- upload context to populate + * content_type -- value of Content-Type header to parse + * + * Return value: + * NGX_OK on success + * NGX_ERROR if error has occured + */ +static ngx_int_t upload_parse_content_type(ngx_http_upload_ctx_t *upload_ctx, ngx_str_t *content_type); + +/* + * upload_process_buf + * + * Process buffer with multipart stream starting from start and terminating + * by end, operating on upload_ctx. The header information is accumulated in + * 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 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 + * NGX_UPLOAD_TOOLARGE field body is too large + */ +static ngx_int_t upload_process_buf(ngx_http_upload_ctx_t *upload_ctx, u_char *start, u_char *end); + +static ngx_command_t ngx_http_upload_commands[] = { /* {{{ */ + + /* + * Enables uploads for location and specifies location to pass modified request to + */ + { ngx_string("upload_pass"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_http_upload_pass, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + + /* + * Specifies base path of file store + */ + { ngx_string("upload_store"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1234, + ngx_conf_set_path_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_upload_loc_conf_t, store_path), + NULL }, + + /* + * Specifies the access mode for files in store + */ + { ngx_string("upload_store_access"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE123, + ngx_conf_set_access_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_upload_loc_conf_t, store_access), + NULL }, + + /* + * Specifies the size of buffer, which will be used + * to write data to disk + */ + { ngx_string("upload_buffer_size"), + 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, buffer_size), + NULL }, + + /* + * Specifies the maximal length of the part header + */ + { ngx_string("upload_max_part_header_len"), + 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_header_len), + NULL }, + + /* + * Specifies the maximal size of the file to be uploaded + */ + { ngx_string("upload_max_file_size"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_off_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_upload_loc_conf_t, max_file_size), + NULL }, + + /* + * Specifies the maximal length of output body + */ + { ngx_string("upload_max_output_body_len"), + 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("upload_set_form_field"), + 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, field_templates), + NULL}, + + /* + * Specifies the field with aggregate parameters + * to set in altered response body + */ + { ngx_string("upload_aggregate_form_field"), + 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}, + + /* + * Specifies the field to pass to backend + */ + { ngx_string("upload_pass_form_field"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF + |NGX_CONF_TAKE1, + ngx_http_upload_pass_form_field, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + 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 the whether or not to forward query args + * to the upload_pass redirect location + */ + { ngx_string("upload_pass_args"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_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, forward_args), + NULL }, + + ngx_null_command +}; /* }}} */ + +ngx_http_module_t ngx_http_upload_module_ctx = { /* {{{ */ + ngx_http_upload_add_variables, /* preconfiguration */ + NULL, /* postconfiguration */ + + NULL, /* create main configuration */ + NULL, /* init main configuration */ + + 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_module_t ngx_http_upload_module = { /* {{{ */ + NGX_MODULE_V1, + &ngx_http_upload_module_ctx, /* module context */ + ngx_http_upload_commands, /* module directives */ + NGX_HTTP_MODULE, /* module type */ + NULL, /* init master */ + NULL, /* init module */ + NULL, /* init process */ + NULL, /* init thread */ + NULL, /* exit thread */ + NULL, /* exit process */ + NULL, /* exit master */ + NGX_MODULE_V1_PADDING +}; /* }}} */ + +static ngx_http_variable_t ngx_http_upload_variables[] = { /* {{{ */ + + { ngx_string("upload_field_name"), 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("upload_content_type"), 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("upload_file_name"), 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("upload_tmp_path"), 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("upload_file_md5"), NULL, ngx_http_upload_md5_variable, + (uintptr_t) "0123456789abcdef", + NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 }, + + { ngx_string("upload_file_md5_uc"), NULL, ngx_http_upload_md5_variable, + (uintptr_t) "0123456789ABCDEF", + NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 }, + + { ngx_string("upload_file_sha1"), NULL, ngx_http_upload_sha1_variable, + (uintptr_t) "0123456789abcdef", + NGX_HTTP_VAR_CHANGEABLE|NGX_HTTP_VAR_NOCACHEABLE|NGX_HTTP_VAR_NOHASH, 0 }, + + { ngx_string("upload_file_sha1_uc"), NULL, ngx_http_upload_sha1_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 }, + + { 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 }, + + { ngx_null_string, NULL, NULL, 0, 0, 0 } +}; /* }}} */ + +static ngx_str_t ngx_http_upload_empty_field_value = ngx_null_string; + +static ngx_str_t ngx_upload_field_part1 = { /* {{{ */ + sizeof(CRLF "Content-Disposition: form-data; name=\"") - 1, + (u_char*)CRLF "Content-Disposition: form-data; name=\"" +}; /* }}} */ + +static ngx_str_t ngx_upload_field_part2 = { /* {{{ */ + sizeof("\"" CRLF CRLF) - 1, + (u_char*)"\"" CRLF CRLF +}; /* }}} */ + +static ngx_int_t /* {{{ ngx_http_upload_handler */ +ngx_http_upload_handler(ngx_http_request_t *r) +{ + ngx_http_upload_loc_conf_t *ulcf; + ngx_http_upload_ctx_t *u; + ngx_int_t rc; + + if (!(r->method & NGX_HTTP_POST)) + return NGX_DECLINED; + + ulcf = ngx_http_get_module_loc_conf(r, ngx_http_upload_module); + + u = ngx_http_get_module_ctx(r, ngx_http_upload_module); + + if (u == NULL) { + u = ngx_pcalloc(r->pool, sizeof(ngx_http_upload_ctx_t)); + if (u == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + ngx_http_set_ctx(r, u, ngx_http_upload_module); + } + + if(ulcf->md5) { + if(u->md5_ctx == NULL) { + u->md5_ctx = ngx_palloc(r->pool, sizeof(ngx_http_upload_md5_ctx_t)); + if (u->md5_ctx == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + } + }else + u->md5_ctx = NULL; + + if(ulcf->sha1) { + if(u->sha1_ctx == NULL) { + u->sha1_ctx = ngx_palloc(r->pool, sizeof(ngx_http_upload_sha1_ctx_t)); + if (u->sha1_ctx == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + } + }else + u->sha1_ctx = NULL; + + u->calculate_crc32 = ulcf->crc32; + + // Check whether Content-Type header is missing + if(r->headers_in.content_type == NULL) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_errno, + "missing Content-Type header"); + return NGX_HTTP_BAD_REQUEST; + } + + u->request = r; + u->log = r->connection->log; + u->chain = u->last = u->checkpoint = NULL; + u->output_body_len = 0; + + upload_init_ctx(u); + + if(upload_parse_content_type(u, &r->headers_in.content_type->value) != NGX_OK) { + upload_shutdown_ctx(u); + return NGX_HTTP_BAD_REQUEST; + } + + if(upload_start(u, ulcf) != NGX_OK) + return NGX_HTTP_INTERNAL_SERVER_ERROR; + + rc = ngx_http_read_upload_client_request_body(r); + + if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { + return rc; + } + + return NGX_DONE; +} /* }}} */ + +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); + + ngx_str_t args; + ngx_uint_t flags; + ngx_int_t rc; + ngx_str_t *uri; + ngx_buf_t *b; + ngx_chain_t *cl; + + if(ulcf->max_output_body_len != 0) { + if(ctx->output_body_len + ctx->boundary.len + 4 > ulcf->max_output_body_len) + return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; + } + + /* + * Append final boundary + */ + b = ngx_create_temp_buf(r->pool, ctx->boundary.len + 4); + + if (b == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + b->last_in_chain = 1; + b->last_buf = 1; + + cl->buf = b; + cl->next = NULL; + + if(ctx->chain == NULL) { + ctx->chain = cl; + ctx->last = cl; + }else{ + ctx->last->next = cl; + ctx->last = cl; + } + + b->last = ngx_cpymem(b->last, ctx->boundary.data, ctx->boundary.len); + + *b->last++ = '-'; + *b->last++ = '-'; + *b->last++ = CR; + *b->last++ = LF; + + uri = &ulcf->url; + + if (ulcf->forward_args) { + args = r->args; /* forward the query args */ + } + else { + args.len = 0; + args.data = NULL; + } + + flags = 0; + + if (ngx_http_parse_unsafe_uri(r, uri, &args, &flags) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + r->request_body->bufs = ctx->chain; + + // Recalculate content length + r->headers_in.content_length_n = 0; + + for(cl = ctx->chain ; cl ; cl = cl->next) + r->headers_in.content_length_n += (cl->buf->last - cl->buf->pos); + + r->headers_in.content_length->value.data = ngx_palloc(r->pool, NGX_OFF_T_LEN); + + if (r->headers_in.content_length->value.data == NULL) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + r->headers_in.content_length->value.len = + ngx_sprintf(r->headers_in.content_length->value.data, "%O", r->headers_in.content_length_n) + - r->headers_in.content_length->value.data; + + if(uri->len != 0 && uri->data[0] == '/') { + rc = ngx_http_internal_redirect(r, uri, &args); + } + else{ + rc = ngx_http_named_location(r, uri); + } + + if (rc == NGX_ERROR) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + return rc; +} /* }}} */ + +static ngx_int_t ngx_http_upload_start_handler(ngx_http_upload_ctx_t *u) { /* {{{ */ + ngx_http_request_t *r = u->request; + 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; + uint32_t n; + ngx_uint_t i; + ngx_int_t rc; + ngx_err_t err; + ngx_http_upload_field_template_t *t; + ngx_http_upload_field_filter_t *f; + ngx_str_t field_name, field_value; + ngx_uint_t pass_field; + 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); + + if(file->name.data == NULL) + return NGX_UPLOAD_NOMEM; + + ngx_memcpy(file->name.data, path->name.data, path->name.len); + + file->log = r->connection->log; + + for(;;) { + n = (uint32_t) ngx_next_temp_number(0); + + (void) ngx_sprintf(file->name.data + path->name.len + 1 + path->len, + "%010uD%Z", n); + + ngx_create_hashed_filename(path, file->name.data, file->name.len); + + ngx_log_debug1(NGX_LOG_DEBUG_CORE, file->log, 0, + "hashed path: %s", file->name.data); + + file->fd = ngx_open_tempfile(file->name.data, 1, ulcf->store_access); + + if (file->fd != NGX_INVALID_FILE) { + file->offset = 0; + break; + } + + err = ngx_errno; + + if (err == NGX_EEXIST) { + n = (uint32_t) ngx_next_temp_number(1); + continue; + } + + ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_errno, + "failed to create output file \"%V\" for \"%V\"", &file->name, &u->file_name); + return NGX_UPLOAD_IOERROR; + } + + u->cln->handler = ngx_upload_cleanup_handler; + + 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; + for (i = 0; i < ulcf->field_templates->nelts; i++) { + + if (t[i].field_lengths == NULL) { + field_name = t[i].value.key; + }else{ + if (ngx_http_script_run(r, &field_name, t[i].field_lengths->elts, 0, + t[i].field_values->elts) == NULL) + { + rc = NGX_UPLOAD_SCRIPTERROR; + goto cleanup_file; + } + } + + if (t[i].value_lengths == NULL) { + field_value = t[i].value.value; + }else{ + if (ngx_http_script_run(r, &field_value, t[i].value_lengths->elts, 0, + t[i].value_values->elts) == NULL) + { + rc = NGX_UPLOAD_SCRIPTERROR; + goto cleanup_file; + } + } + + rc = ngx_http_upload_append_field(u, &field_name, &field_value); + + if(rc != NGX_OK) + goto cleanup_file; + } + } + + if(u->md5_ctx != NULL) + MD5Init(&u->md5_ctx->md5); + + 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 uploading file \"%V\" to \"%V\" (field \"%V\", content type \"%V\")" + , &u->file_name + , &u->output_file.name + , &u->field_name + , &u->content_type + ); + }else{ + pass_field = 0; + + if(ulcf->field_filters) { + f = ulcf->field_filters->elts; + for (i = 0; i < ulcf->field_filters->nelts; i++) { +#if (NGX_PCRE) + rc = ngx_regex_exec(f[i].regex, &u->field_name, NULL, 0); + + if (rc != NGX_REGEX_NO_MATCHED && rc < 0) { + return NGX_UPLOAD_SCRIPTERROR; + } + + /* + * If at least one filter succeeds, we pass the field + */ + if(rc == 0) + pass_field = 1; +#else + if(ngx_strncmp(f[i].text.data, u->field_name.data, u->field_name.len) == 0) + pass_field = 1; +#endif + } + } + + 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 + * is called. We pass empty field value to simplify things. + */ + rc = ngx_http_upload_append_field(u, &u->field_name, &ngx_http_upload_empty_field_value); + + if(rc != NGX_OK) + return rc; + }else + u->discard_data = 1; + } + + return NGX_OK; + +cleanup_file: + return rc; +} /* }}} */ + +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->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; + ngx_upload_cleanup_t *ucln; + + if(u->is_file) { + ucln = u->cln->data; + ucln->fd = -1; + + ngx_close_file(u->output_file.fd); + + ngx_log_error(NGX_LOG_INFO, r->connection->log, 0 + , "finished uploading file \"%V\" to \"%V\"" + , &u->file_name + , &u->output_file.name + ); + + if(u->md5_ctx) + MD5Final(u->md5_ctx->md5_digest, &u->md5_ctx->md5); + + 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_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) + { + goto rollback; + } + } + + if (af[i].value_lengths == NULL) { + aggregate_field_value = af[i].value.value; + }else{ + if (ngx_http_script_run(r, &aggregate_field_value, af[i].value_lengths->elts, 0, + af[i].value_values->elts) == NULL) + { + goto rollback; + } + } + + rc = ngx_http_upload_append_field(u, &aggregate_field_name, &aggregate_field_value); + + if(rc != NGX_OK) + goto rollback; + } + } + } + + // Checkpoint current output chain state + u->checkpoint = u->last; + return; + +rollback: + ngx_http_upload_abort_handler(u); +} /* }}} */ + +static void ngx_http_upload_abort_handler(ngx_http_upload_ctx_t *u) { /* {{{ */ + ngx_upload_cleanup_t *ucln; + + if(u->is_file) { + /* + * 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); + + if(ngx_delete_file(u->output_file.name.data) == NGX_FILE_ERROR) { + ngx_log_error(NGX_LOG_ERR, u->log, ngx_errno + , "aborted uploading file \"%V\" to \"%V\", failed to remove destination file" + , &u->file_name + , &u->output_file.name); + } else { + ngx_log_error(NGX_LOG_ALERT, u->log, 0 + , "aborted uploading file \"%V\" to \"%V\", dest file removed" + , &u->file_name + , &u->output_file.name); + } + } + + // Rollback output chain to the previous consistant state + if(u->checkpoint != NULL) { + u->last = u->checkpoint; + u->last->next = NULL; + }else{ + u->chain = u->last = NULL; + u->first_part = 1; + } +} /* }}} */ + +static ngx_int_t ngx_http_upload_flush_output_buffer(ngx_http_upload_ctx_t *u, u_char *buf, size_t len) { /* {{{ */ + ngx_http_request_t *r = u->request; + ngx_buf_t *b; + ngx_chain_t *cl; + ngx_http_upload_loc_conf_t *ulcf = ngx_http_get_module_loc_conf(r, ngx_http_upload_module); + + if(u->is_file) { + if(u->md5_ctx) + MD5Update(&u->md5_ctx->md5, buf, len); + + if(u->sha1_ctx) + SHA1_Update(&u->sha1_ctx->sha1, buf, len); + + if(u->calculate_crc32) + ngx_crc32_update(&u->crc32, buf, len); + + if(ulcf->max_file_size != 0) { + if(u->output_file.offset + (off_t)len > ulcf->max_file_size) + return NGX_UPLOAD_TOOLARGE; + } + + if(ngx_write_file(&u->output_file, buf, len, u->output_file.offset) == NGX_ERROR) { + ngx_log_error(NGX_LOG_ERR, r->connection->log, ngx_errno, + "write to file \"%V\" failed", &u->output_file.name); + return NGX_UPLOAD_IOERROR; + }else + return NGX_OK; + }else{ + if(ulcf->max_output_body_len != 0) { + if (u->output_body_len + len > ulcf->max_output_body_len) + return NGX_UPLOAD_TOOLARGE; + } + + u->output_body_len += len; + + b = ngx_create_temp_buf(u->request->pool, len); + + if (b == NULL) { + return NGX_ERROR; + } + + cl = ngx_alloc_chain_link(u->request->pool); + if (cl == NULL) { + return NGX_ERROR; + } + + b->last_in_chain = 0; + + cl->buf = b; + cl->next = NULL; + + b->last = ngx_cpymem(b->last, buf, len); + + if(u->chain == NULL) { + u->chain = cl; + u->last = cl; + }else{ + u->last->next = cl; + u->last = cl; + } + + return NGX_OK; + } +} /* }}} */ + +static void /* {{{ ngx_http_upload_append_str */ +ngx_http_upload_append_str(ngx_http_upload_ctx_t *u, ngx_buf_t *b, ngx_chain_t *cl, ngx_str_t *s) +{ + b->start = b->pos = s->data; + b->end = b->last = s->data + s->len; + b->memory = 1; + b->temporary = 1; + b->in_file = 0; + b->last_buf = 0; + + b->last_in_chain = 0; + b->last_buf = 0; + + cl->buf = b; + cl->next = NULL; + + if(u->chain == NULL) { + u->chain = cl; + u->last = cl; + }else{ + u->last->next = cl; + u->last = cl; + } + + u->output_body_len += s->len; +} /* }}} */ + +static ngx_int_t /* {{{ ngx_http_upload_append_field */ +ngx_http_upload_append_field(ngx_http_upload_ctx_t *u, ngx_str_t *name, ngx_str_t *value) +{ + ngx_http_upload_loc_conf_t *ulcf = ngx_http_get_module_loc_conf(u->request, ngx_http_upload_module); + ngx_str_t boundary = { u->first_part ? u->boundary.len - 2 : u->boundary.len, + u->first_part ? u->boundary.data + 2 : u->boundary.data }; + + ngx_buf_t *b; + ngx_chain_t *cl; + + if(name->len > 0) { + if(ulcf->max_output_body_len != 0) { + if(u->output_body_len + boundary.len + ngx_upload_field_part1.len + name->len + + ngx_upload_field_part2.len + value->len > ulcf->max_output_body_len) + return NGX_UPLOAD_TOOLARGE; + } + + 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; + } + + 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; + } + + 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 + 2, cl + 2, name); + + 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); + + u->output_body_len += boundary.len + ngx_upload_field_part1.len + name->len + + ngx_upload_field_part2.len + value->len; + + u->first_part = 0; + } + + return NGX_OK; +} /* }}} */ + +static void * /* {{{ ngx_http_upload_create_loc_conf */ +ngx_http_upload_create_loc_conf(ngx_conf_t *cf) +{ + ngx_http_upload_loc_conf_t *conf; + + conf = ngx_pcalloc(cf->pool, sizeof(ngx_http_upload_loc_conf_t)); + if (conf == NULL) { + return NGX_CONF_ERROR; + } + + conf->store_access = NGX_CONF_UNSET_UINT; + conf->forward_args = NGX_CONF_UNSET; + + conf->buffer_size = NGX_CONF_UNSET_SIZE; + conf->max_header_len = NGX_CONF_UNSET_SIZE; + conf->max_output_body_len = NGX_CONF_UNSET_SIZE; + conf->max_file_size = NGX_CONF_UNSET; + + /* + * conf->field_templates, + * conf->aggregate_field_templates, + * and conf->field_filters are + * zeroed by ngx_pcalloc + */ + + return conf; +} /* }}} */ + +static char * /* {{{ ngx_http_upload_merge_loc_conf */ +ngx_http_upload_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child) +{ + ngx_http_upload_loc_conf_t *prev = parent; + ngx_http_upload_loc_conf_t *conf = child; + + ngx_conf_merge_str_value(conf->url, prev->url, ""); + + 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_uint_value(conf->store_access, + prev->store_access, 0600); + + ngx_conf_merge_size_value(conf->buffer_size, + prev->buffer_size, + (size_t) ngx_pagesize); + + ngx_conf_merge_size_value(conf->max_header_len, + prev->max_header_len, + (size_t) 512); + + ngx_conf_merge_size_value(conf->max_output_body_len, + prev->max_output_body_len, + (size_t) 100 * 1024); + + ngx_conf_merge_off_value(conf->max_file_size, + prev->max_file_size, + 0); + + if(conf->forward_args == NGX_CONF_UNSET) { + conf->forward_args = (prev->forward_args != NGX_CONF_UNSET) ? + prev->forward_args : 0; + } + + if(conf->field_templates == NULL) { + conf->field_templates = prev->field_templates; + } + + if(conf->aggregate_field_templates == NULL) { + conf->aggregate_field_templates = prev->aggregate_field_templates; + + 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(conf->cleanup_statuses == NULL) { + conf->cleanup_statuses = prev->cleanup_statuses; + } + + 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, *v; + + for (v = ngx_http_upload_variables; v->name.len; v++) { + var = ngx_http_add_variable(cf, &v->name, v->flags); + if (var == NULL) { + return NGX_ERROR; + } + + var->get_handler = v->get_handler; + var->data = v->data; + } + + for (v = ngx_http_upload_aggregate_variables; v->name.len; v++) { + var = ngx_http_add_variable(cf, &v->name, v->flags); + if (var == NULL) { + return NGX_ERROR; + } + + var->get_handler = v->get_handler; + var->data = v->data; + } + + 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->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + u = ngx_http_get_module_ctx(r, ngx_http_upload_module); + + value = (ngx_str_t *) ((char *) u + data); + + v->data = value->data; + v->len = value->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); + + if(u->md5_ctx == NULL) { + 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->md5_ctx->md5_digest + MD5_DIGEST_LENGTH * 2; + + i = MD5_DIGEST_LENGTH; + + do{ + i--; + *--c = hex_table[u->md5_ctx->md5_digest[i] & 0xf]; + *--c = hex_table[u->md5_ctx->md5_digest[i] >> 4]; + }while(i != 0); + + v->data = u->md5_ctx->md5_digest; + v->len = MD5_DIGEST_LENGTH * 2; + + return NGX_OK; +} /* }}} */ + +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; + u_char *c; + u_char *hex_table; + + u = ngx_http_get_module_ctx(r, ngx_http_upload_module); + + if(u->sha1_ctx == NULL) { + 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->sha1_ctx->sha1_digest + SHA_DIGEST_LENGTH * 2; + + i = SHA_DIGEST_LENGTH; + + do{ + i--; + *--c = hex_table[u->sha1_ctx->sha1_digest[i] & 0xf]; + *--c = hex_table[u->sha1_ctx->sha1_digest[i] >> 4]; + }while(i != 0); + + v->data = u->sha1_ctx->sha1_digest; + v->len = SHA_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) +{ + 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) +{ + ngx_http_upload_ctx_t *u; + u_char *p; + off_t *value; + + u = ngx_http_get_module_ctx(r, ngx_http_upload_module); + + value = (off_t *) ((char *) u + data); + + p = ngx_palloc(r->pool, NGX_OFF_T_LEN); + if (p == NULL) { + return NGX_ERROR; + } + + v->len = ngx_sprintf(p, "%O", *value) - p; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = p; + + return NGX_OK; +} /* }}} */ + +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_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->offset); + + value = cf->args->elts; + + if (*field == NULL) { + *field = ngx_array_create(cf->pool, 1, + sizeof(ngx_http_upload_field_template_t)); + if (*field == NULL) { + return NGX_CONF_ERROR; + } + } + + h = ngx_array_push(*field); + if (h == NULL) { + return NGX_CONF_ERROR; + } + + h->value.hash = 1; + h->value.key = value[1]; + h->value.value = value[2]; + h->field_lengths = NULL; + h->field_values = NULL; + h->value_lengths = NULL; + h->value_values = NULL; + + /* + * Compile field name + */ + n = ngx_http_script_variables_count(&value[1]); + + 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; + + if (ngx_http_script_compile(&sc) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + + /* + * Compile field value + */ + n = ngx_http_script_variables_count(&value[2]); + + if (n > 0) { + 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; + + if (ngx_http_script_compile(&sc) != NGX_OK) { + return NGX_CONF_ERROR; + } + } + + /* + * Check for aggregate variables in script + */ + for(i = 1;i <= 2;i++) { + for (v = ngx_http_upload_aggregate_variables; v->name.len; v++) { + match = ngx_strcasestrn(value[i].data, (char*)v->name.data, v->name.len - 1); + + /* + * ngx_http_script_compile does check for final bracket earlier, + * therefore we don't need to care about it, which simplifies things + */ + if(match != NULL + && ((match - value[i].data >= 1 && match[-1] == '$') + || (match - value[i].data >= 2 && match[-2] == '$' && match[-1] == '{'))) + { + if(cmd->offset != offsetof(ngx_http_upload_loc_conf_t, aggregate_field_templates)) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + "variables upload_file_md5" + ", 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; + } + + if(v->get_handler == ngx_http_upload_md5_variable) + ulcf->md5 = 1; + + if(v->get_handler == ngx_http_upload_sha1_variable) + ulcf->sha1 = 1; + + if(v->get_handler == ngx_http_upload_crc32_variable) + ulcf->crc32 = 1; + } + } + } + + return NGX_CONF_OK; +} /* }}} */ + +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; + + ngx_str_t *value; +#if (NGX_PCRE) + ngx_int_t n; + ngx_str_t err; +#endif + ngx_http_upload_field_filter_t *f; + + value = cf->args->elts; + + if (ulcf->field_filters == NULL) { + ulcf->field_filters = ngx_array_create(cf->pool, 1, + sizeof(ngx_http_upload_field_filter_t)); + if (ulcf->field_filters == NULL) { + return NGX_CONF_ERROR; + } + } + + f = ngx_array_push(ulcf->field_filters); + if (f == NULL) { + return NGX_CONF_ERROR; + } + +#if (NGX_PCRE) + f->regex = ngx_regex_compile(&value[1], 0, cf->pool, &err); + + if (f->regex == NULL) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "%s", err.data); + return NGX_CONF_ERROR; + } + + n = ngx_regex_capture_count(f->regex); + + if (n < 0) { + ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, + ngx_regex_capture_count_n " failed for " + "pattern \"%V\"", &value[1]); + return NGX_CONF_ERROR; + } + + f->ncaptures = n; +#else + f->text.len = value[1].len; + f->text.data = value[1].data; +#endif + + 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 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; + ngx_http_upload_loc_conf_t *ulcf = conf; + + ngx_str_t *value, *url; + + value = cf->args->elts; + url = &value[1]; + + clcf = ngx_http_conf_get_module_loc_conf(cf, ngx_http_core_module); + clcf->handler = ngx_http_upload_handler; + + ulcf->url = *url; + + return NGX_CONF_OK; +} /* }}} */ + +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; + ngx_buf_t *b; + ngx_chain_t *cl, **next; + ngx_http_request_body_t *rb; + ngx_http_core_loc_conf_t *clcf; + ngx_http_upload_ctx_t *u = ngx_http_get_module_ctx(r, ngx_http_upload_module); + + if (r->request_body || r->discard_body) { + return NGX_OK; + } + + rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); + if (rb == NULL) { + upload_shutdown_ctx(u); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + r->request_body = rb; + + if (r->headers_in.content_length_n <= 0) { + upload_shutdown_ctx(u); + return NGX_HTTP_BAD_REQUEST; + } + + /* + * set by ngx_pcalloc(): + * + * rb->bufs = NULL; + * rb->buf = NULL; + * rb->rest = 0; + */ + + preread = r->header_in->last - r->header_in->pos; + + if (preread) { + + /* there is the pre-read part of the request body */ + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "http client request body preread %uz", preread); + + b = ngx_calloc_buf(r->pool); + if (b == NULL) { + upload_shutdown_ctx(u); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + b->temporary = 1; + b->start = r->header_in->pos; + b->pos = r->header_in->pos; + b->last = r->header_in->last; + b->end = r->header_in->end; + + rb->bufs = ngx_alloc_chain_link(r->pool); + if (rb->bufs == NULL) { + upload_shutdown_ctx(u); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + rb->bufs->buf = b; + rb->bufs->next = NULL; + + if (preread >= r->headers_in.content_length_n) { + + /* the whole request body was pre-read */ + + r->header_in->pos += r->headers_in.content_length_n; + r->request_length += r->headers_in.content_length_n; + + if (ngx_http_process_request_body(r, rb->bufs) != NGX_OK) { + upload_shutdown_ctx(u); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + upload_shutdown_ctx(u); + + return ngx_http_upload_body_handler(r); + } + + /* + * to not consider the body as pipelined request in + * ngx_http_set_keepalive() + */ + r->header_in->pos = r->header_in->last; + + r->request_length += preread; + + rb->rest = r->headers_in.content_length_n - preread; + + if (rb->rest <= (off_t) (b->end - b->last)) { + + /* the whole request body may be placed in r->header_in */ + + rb->buf = b; + + r->read_event_handler = ngx_http_read_upload_client_request_body_handler; + + return ngx_http_do_read_upload_client_request_body(r); + } + + next = &rb->bufs->next; + + } else { + b = NULL; + rb->rest = r->headers_in.content_length_n; + next = &rb->bufs; + } + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + size = clcf->client_body_buffer_size; + size += size >> 2; + + if (rb->rest < (ssize_t) size) { + size = rb->rest; + + if (r->request_body_in_single_buf) { + size += preread; + } + + } else { + size = clcf->client_body_buffer_size; + + /* disable copying buffer for r->request_body_in_single_buf */ + b = NULL; + } + + rb->buf = ngx_create_temp_buf(r->pool, size); + if (rb->buf == NULL) { + upload_shutdown_ctx(u); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + cl = ngx_alloc_chain_link(r->pool); + if (cl == NULL) { + upload_shutdown_ctx(u); + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + cl->buf = rb->buf; + cl->next = NULL; + + if (b && r->request_body_in_single_buf) { + size = b->last - b->pos; + ngx_memcpy(rb->buf->pos, b->pos, size); + rb->buf->last += size; + + next = &rb->bufs; + } + + *next = cl; + + rb->to_write = rb->bufs; + + r->read_event_handler = ngx_http_read_upload_client_request_body_handler; + + return ngx_http_do_read_upload_client_request_body(r); +} /* }}} */ + +static void /* {{{ ngx_http_read_upload_client_request_body_handler */ +ngx_http_read_upload_client_request_body_handler(ngx_http_request_t *r) +{ + ngx_int_t rc; + ngx_http_upload_ctx_t *u = ngx_http_get_module_ctx(r, ngx_http_upload_module); + + if (r->connection->read->timedout) { + r->connection->timedout = 1; + upload_shutdown_ctx(u); + ngx_http_finalize_request(r, NGX_HTTP_REQUEST_TIME_OUT); + return; + } + + rc = ngx_http_do_read_upload_client_request_body(r); + + if (rc >= NGX_HTTP_SPECIAL_RESPONSE) { + upload_shutdown_ctx(u); + ngx_http_finalize_request(r, rc); + } +} /* }}} */ + +static ngx_int_t /* {{{ ngx_http_do_read_upload_client_request_body */ +ngx_http_do_read_upload_client_request_body(ngx_http_request_t *r) +{ + size_t size; + ssize_t n; + ngx_connection_t *c; + ngx_http_request_body_t *rb; + ngx_http_core_loc_conf_t *clcf; + ngx_http_upload_ctx_t *u = ngx_http_get_module_ctx(r, ngx_http_upload_module); + ngx_int_t rc; + + c = r->connection; + rb = r->request_body; + + ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http read client request body"); + + for ( ;; ) { + for ( ;; ) { + if (rb->buf->last == rb->buf->end) { + + rc = ngx_http_process_request_body(r, rb->to_write); + + switch(rc) { + case NGX_OK: + break; + case NGX_UPLOAD_MALFORMED: + return NGX_HTTP_BAD_REQUEST; + case NGX_UPLOAD_TOOLARGE: + return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; + case NGX_UPLOAD_IOERROR: + return NGX_HTTP_SERVICE_UNAVAILABLE; + case NGX_UPLOAD_NOMEM: case NGX_UPLOAD_SCRIPTERROR: + default: + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + rb->to_write = rb->bufs->next ? rb->bufs->next : rb->bufs; + rb->buf->last = rb->buf->start; + } + + size = rb->buf->end - rb->buf->last; + + if ((off_t)size > rb->rest) { + size = (size_t)rb->rest; + } + + n = c->recv(c, rb->buf->last, size); + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http client request body recv %z", n); + + if (n == NGX_AGAIN) { + break; + } + + if (n == 0) { + ngx_log_error(NGX_LOG_INFO, c->log, 0, + "client closed prematurely connection"); + } + + if (n == 0 || n == NGX_ERROR) { + c->error = 1; + return NGX_HTTP_BAD_REQUEST; + } + + rb->buf->last += n; + rb->rest -= n; + r->request_length += n; + + if (rb->rest == 0) { + break; + } + + if (rb->buf->last < rb->buf->end) { + break; + } + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0, + "http client request body rest %uz", rb->rest); + + if (rb->rest == 0) { + break; + } + + if (!c->read->ready) { + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + ngx_add_timer(c->read, clcf->client_body_timeout); + + if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + return NGX_AGAIN; + } + } + + if (c->read->timer_set) { + ngx_del_timer(c->read); + } + + rc = ngx_http_process_request_body(r, rb->to_write); + + switch(rc) { + case NGX_OK: + break; + case NGX_UPLOAD_MALFORMED: + return NGX_HTTP_BAD_REQUEST; + case NGX_UPLOAD_TOOLARGE: + return NGX_HTTP_REQUEST_ENTITY_TOO_LARGE; + case NGX_UPLOAD_IOERROR: + return NGX_HTTP_SERVICE_UNAVAILABLE; + case NGX_UPLOAD_NOMEM: case NGX_UPLOAD_SCRIPTERROR: + default: + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + + upload_shutdown_ctx(u); + + return ngx_http_upload_body_handler(r); +} /* }}} */ + +static ngx_int_t /* {{{ ngx_http_process_request_body */ +ngx_http_process_request_body(ngx_http_request_t *r, ngx_chain_t *body) +{ + ngx_int_t rc; + ngx_http_upload_ctx_t *u = ngx_http_get_module_ctx(r, ngx_http_upload_module); + + // Feed all the buffers into multipart/form-data processor + while(body) { + rc = upload_process_buf(u, body->buf->pos, body->buf->last); + + if(rc != NGX_OK) + return rc; + + // Signal end of body + if(body->buf->last_buf) { + rc = upload_process_buf(u, body->buf->pos, body->buf->pos); + + if(rc != NGX_OK) + return rc; + } + + body = body->next; + } + + return NGX_OK; +} /* }}} */ + +static ngx_int_t upload_parse_part_header(ngx_http_upload_ctx_t *upload_ctx, char *header, char *header_end) { /* {{{ */ + if(!strncasecmp(CONTENT_DISPOSITION_STRING, header, sizeof(CONTENT_DISPOSITION_STRING) - 1)) { + char *p = header + sizeof(CONTENT_DISPOSITION_STRING) - 1; + char *filename_start, *filename_end; + char *fieldname_start, *fieldname_end; + + p += strspn(p, " "); + + if(strncasecmp(FORM_DATA_STRING, p, sizeof(FORM_DATA_STRING)-1) && + strncasecmp(ATTACHMENT_STRING, p, sizeof(ATTACHMENT_STRING)-1)) { + ngx_log_debug0(NGX_LOG_DEBUG_CORE, upload_ctx->log, 0, + "Content-Disposition is not form-data or attachment"); + return NGX_UPLOAD_MALFORMED; + } + + filename_start = strstr(p, FILENAME_STRING); + + if(filename_start != 0) { + char *q; + + filename_start += sizeof(FILENAME_STRING)-1; + + filename_end = filename_start + strcspn(filename_start, "\""); + + if(*filename_end != '\"') { + ngx_log_debug0(NGX_LOG_DEBUG_CORE, upload_ctx->log, 0, + "malformed filename in part header"); + return NGX_UPLOAD_MALFORMED; + } + + /* + * IE sends full path, strip path from filename + * Also strip all UNIX path references + */ + for(q = filename_end-1; q > filename_start; q--) + if(*q == '\\' || *q == '/') { + filename_start = q+1; + break; + } + + upload_ctx->file_name.len = filename_end - filename_start; + upload_ctx->file_name.data = ngx_palloc(upload_ctx->request->pool, upload_ctx->file_name.len + 1); + + if(upload_ctx->file_name.data == NULL) + return NGX_UPLOAD_NOMEM; + + strncpy((char*)upload_ctx->file_name.data, filename_start, filename_end - filename_start); + } + + fieldname_start = p; + + do{ + fieldname_start = strstr(fieldname_start, FIELDNAME_STRING); + }while((fieldname_start != 0) && (fieldname_start + sizeof(FIELDNAME_STRING) - 1 == filename_start)); + + if(fieldname_start != 0) { + fieldname_start += sizeof(FIELDNAME_STRING)-1; + + if(fieldname_start != filename_start) { + fieldname_end = fieldname_start + strcspn(fieldname_start, "\""); + + if(*fieldname_end != '\"') { + ngx_log_debug0(NGX_LOG_DEBUG_CORE, upload_ctx->log, 0, + "malformed fieldname in part header"); + return NGX_UPLOAD_MALFORMED; + } + + upload_ctx->field_name.len = fieldname_end - fieldname_start; + upload_ctx->field_name.data = ngx_pcalloc(upload_ctx->request->pool, upload_ctx->field_name.len + 1); + + if(upload_ctx->field_name.data == NULL) + return NGX_UPLOAD_NOMEM; + + strncpy((char*)upload_ctx->field_name.data, fieldname_start, fieldname_end - fieldname_start); + } + } + }else if(!strncasecmp(CONTENT_TYPE_STRING, header, sizeof(CONTENT_TYPE_STRING)-1)) { + char *content_type_str = header + sizeof(CONTENT_TYPE_STRING)-1; + + content_type_str += strspn(content_type_str, " "); + upload_ctx->content_type.len = header_end - content_type_str; + + if(upload_ctx->content_type.len == 0) { + ngx_log_debug0(NGX_LOG_DEBUG_CORE, upload_ctx->log, 0, + "empty Content-Type in part header"); + return NGX_UPLOAD_MALFORMED; // Empty Content-Type field + } + + upload_ctx->content_type.data = ngx_pcalloc(upload_ctx->request->pool, upload_ctx->content_type.len + 1); + + if(upload_ctx->content_type.data == NULL) + return NGX_UPLOAD_NOMEM; // Unable to allocate memory for string + + strncpy((char*)upload_ctx->content_type.data, content_type_str, upload_ctx->content_type.len); + } + + return NGX_OK; +} /* }}} */ + +static void upload_discard_part_attributes(ngx_http_upload_ctx_t *upload_ctx) { /* {{{ */ + upload_ctx->file_name.len = 0; + upload_ctx->file_name.data = NULL; + + upload_ctx->field_name.len = 0; + upload_ctx->field_name.data = NULL; + + upload_ctx->content_type.len = 0; + upload_ctx->content_type.data = NULL; +} /* }}} */ + +static ngx_int_t upload_start_file(ngx_http_upload_ctx_t *upload_ctx) { /* {{{ */ + // Call user-defined event handler + if(upload_ctx->start_part_f) + return upload_ctx->start_part_f(upload_ctx); + else + return NGX_OK; +} /* }}} */ + +static void upload_finish_file(ngx_http_upload_ctx_t *upload_ctx) { /* {{{ */ + // Call user-defined event handler + if(upload_ctx->finish_part_f) + upload_ctx->finish_part_f(upload_ctx); + + upload_discard_part_attributes(upload_ctx); + + upload_ctx->discard_data = 0; +} /* }}} */ + +static void upload_abort_file(ngx_http_upload_ctx_t *upload_ctx) { /* {{{ */ + if(upload_ctx->abort_part_f) + upload_ctx->abort_part_f(upload_ctx); + + upload_discard_part_attributes(upload_ctx); + + upload_ctx->discard_data = 0; +} /* }}} */ + +static void upload_flush_output_buffer(ngx_http_upload_ctx_t *upload_ctx) { /* {{{ */ + if(upload_ctx->output_buffer_pos > upload_ctx->output_buffer) { + if(upload_ctx->flush_output_buffer_f) + if(upload_ctx->flush_output_buffer_f(upload_ctx, (void*)upload_ctx->output_buffer, + (size_t)(upload_ctx->output_buffer_pos - upload_ctx->output_buffer)) != NGX_OK) + upload_ctx->discard_data = 1; + + upload_ctx->output_buffer_pos = upload_ctx->output_buffer; + } +} /* }}} */ + +static void upload_init_ctx(ngx_http_upload_ctx_t *upload_ctx) { /* {{{ */ + upload_ctx->boundary.data = upload_ctx->boundary_start = upload_ctx->boundary_pos = 0; + + upload_ctx->state = upload_state_boundary_seek; + + upload_ctx->content_type.len = 0; + upload_ctx->content_type.data = NULL; + + upload_ctx->field_name.len = 0; + upload_ctx->field_name.data = NULL; + + upload_ctx->file_name.len = 0; + upload_ctx->file_name.data = NULL; + + upload_ctx->discard_data = 0; + + upload_ctx->start_part_f = ngx_http_upload_start_handler; + upload_ctx->finish_part_f = ngx_http_upload_finish_handler; + upload_ctx->abort_part_f = ngx_http_upload_abort_handler; + upload_ctx->flush_output_buffer_f = ngx_http_upload_flush_output_buffer; +} /* }}} */ + +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) { + upload_flush_output_buffer(upload_ctx); + upload_abort_file(upload_ctx); + } + + upload_discard_part_attributes(upload_ctx); + } +} /* }}} */ + +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; + + upload_ctx->header_accumulator = ngx_pcalloc(upload_ctx->request->pool, ulcf->max_header_len + 1); + + if(upload_ctx->header_accumulator == NULL) + return NGX_ERROR; + + upload_ctx->header_accumulator_pos = upload_ctx->header_accumulator; + upload_ctx->header_accumulator_end = upload_ctx->header_accumulator + ulcf->max_header_len; + + upload_ctx->output_buffer = ngx_pcalloc(upload_ctx->request->pool, ulcf->buffer_size); + + if(upload_ctx->output_buffer == NULL) + return NGX_ERROR; + + upload_ctx->output_buffer_pos = upload_ctx->output_buffer; + upload_ctx->output_buffer_end = upload_ctx->output_buffer + ulcf->buffer_size; + + upload_ctx->header_accumulator_pos = upload_ctx->header_accumulator; + + upload_ctx->first_part = 1; + + return NGX_OK; +} /* }}} */ + +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; + + upload_ctx->boundary.data = 0; + + if(mime_type_end_ptr == NULL) { + ngx_log_debug0(NGX_LOG_DEBUG_CORE, upload_ctx->log, 0, + "no boundary found in Content-Type"); + return NGX_UPLOAD_MALFORMED; + } + + if(ngx_strncasecmp(content_type->data, (u_char*) MULTIPART_FORM_DATA_STRING, + sizeof(MULTIPART_FORM_DATA_STRING) - 1)) { + ngx_log_debug1(NGX_LOG_DEBUG_CORE, upload_ctx->log, 0, + "Content-Type is not multipart/form-data: %V", content_type); + return NGX_UPLOAD_MALFORMED; + } + + boundary_start_ptr = ngx_strstrn(mime_type_end_ptr, BOUNDARY_STRING, sizeof(BOUNDARY_STRING) - 2); + + if(boundary_start_ptr == NULL) { + ngx_log_debug0(NGX_LOG_DEBUG_CORE, upload_ctx->log, 0, + "no boundary found in Content-Type"); + return NGX_UPLOAD_MALFORMED; // No boundary found + } + + boundary_start_ptr += sizeof(BOUNDARY_STRING) - 1; + boundary_end_ptr = boundary_start_ptr + strcspn((char*)boundary_start_ptr, " ;\n\r"); + + if(boundary_end_ptr == boundary_start_ptr) { + ngx_log_debug0(NGX_LOG_DEBUG_CORE, upload_ctx->log, 0, + "boundary is empty"); + return NGX_UPLOAD_MALFORMED; + } + + // Allocate memory for entire boundary plus \r\n-- plus terminating character + upload_ctx->boundary.len = boundary_end_ptr - boundary_start_ptr + 4; + upload_ctx->boundary.data = ngx_palloc(upload_ctx->request->pool, upload_ctx->boundary.len + 1); + + if(upload_ctx->boundary.data == NULL) + return NGX_UPLOAD_NOMEM; + + ngx_cpystrn(upload_ctx->boundary.data + 4, boundary_start_ptr, + boundary_end_ptr - boundary_start_ptr + 1); + + // Prepend boundary data by \r\n-- + upload_ctx->boundary.data[0] = '\r'; + upload_ctx->boundary.data[1] = '\n'; + upload_ctx->boundary.data[2] = '-'; + upload_ctx->boundary.data[3] = '-'; + + /* + * NOTE: first boundary doesn't start with \r\n. Here we + * advance 2 positions forward. We will return 2 positions back + * later + */ + upload_ctx->boundary_start = upload_ctx->boundary.data + 2; + upload_ctx->boundary_pos = upload_ctx->boundary_start; + + return NGX_OK; +} /* }}} */ + +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; + + upload_ctx->output_buffer_pos++; + + if(upload_ctx->output_buffer_pos == upload_ctx->output_buffer_end) + upload_flush_output_buffer(upload_ctx); + } +} /* }}} */ + +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; + + // No more data? + if(start == end) { + if(upload_ctx->state != upload_state_finish) + return NGX_UPLOAD_MALFORMED; // Signal error if still haven't finished + else + return NGX_OK; // Otherwise confirm end of stream + } + + for(p = start; p != end; p++) { + switch(upload_ctx->state) { + /* + * Seek the boundary + */ + case upload_state_boundary_seek: + if(*p == *upload_ctx->boundary_pos) + upload_ctx->boundary_pos++; + else + upload_ctx->boundary_pos = upload_ctx->boundary_start; + + if(upload_ctx->boundary_pos == upload_ctx->boundary.data + upload_ctx->boundary.len) { + upload_ctx->state = upload_state_after_boundary; + upload_ctx->boundary_start = upload_ctx->boundary.data; + upload_ctx->boundary_pos = upload_ctx->boundary_start; + } + break; + case upload_state_after_boundary: + switch(*p) { + case '\n': + upload_ctx->state = upload_state_headers; + upload_ctx->header_accumulator_pos = upload_ctx->header_accumulator; + case '\r': + break; + case '-': + upload_ctx->state = upload_state_finish; + break; + } + break; + /* + * Collect and store headers + */ + case upload_state_headers: + switch(*p) { + case '\n': + if(upload_ctx->header_accumulator_pos == upload_ctx->header_accumulator) { + upload_ctx->is_file = (upload_ctx->file_name.data == 0) || (upload_ctx->file_name.len == 0) ? 0 : 1; + + rc = upload_start_file(upload_ctx); + + if(rc != NGX_OK) { + upload_ctx->state = upload_state_finish; + return rc; // User requested to cancel processing + } else { + upload_ctx->state = upload_state_data; + upload_ctx->output_buffer_pos = upload_ctx->output_buffer; + } + } else { + *upload_ctx->header_accumulator_pos = '\0'; + + rc = upload_parse_part_header(upload_ctx, (char*)upload_ctx->header_accumulator, + (char*)upload_ctx->header_accumulator_pos); + + if(rc != NGX_OK) { + upload_ctx->state = upload_state_finish; + return rc; // Malformed header + } else + upload_ctx->header_accumulator_pos = upload_ctx->header_accumulator; + } + case '\r': + break; + default: + if(upload_ctx->header_accumulator_pos < upload_ctx->header_accumulator_end - 1) + *upload_ctx->header_accumulator_pos++ = *p; + else { + upload_ctx->state = upload_state_finish; + return NGX_UPLOAD_MALFORMED; // Header is too long + } + break; + } + break; + /* + * Search for separating or terminating boundary + * and output data simultaneously + */ + case upload_state_data: + if(*p == *upload_ctx->boundary_pos) + upload_ctx->boundary_pos++; + else { + if(upload_ctx->boundary_pos == upload_ctx->boundary_start) { + // IE 5.0 bug workaround + if(*p == '\n') { + /* + * Set current matched position beyond LF and prevent outputting + * CR in case of unsuccessful match by altering boundary_start + */ + upload_ctx->boundary_pos = upload_ctx->boundary.data + 2; + upload_ctx->boundary_start = upload_ctx->boundary.data + 1; + } else + upload_putc(upload_ctx, *p); + } else { + // Output partially matched lump of boundary + u_char *q; + for(q = upload_ctx->boundary_start; q != upload_ctx->boundary_pos; q++) + upload_putc(upload_ctx, *q); + + p--; // Repeat reading last character + + // And reset matched position + upload_ctx->boundary_start = upload_ctx->boundary.data; + upload_ctx->boundary_pos = upload_ctx->boundary_start; + } + } + + if(upload_ctx->boundary_pos == upload_ctx->boundary.data + upload_ctx->boundary.len) { + upload_ctx->state = upload_state_after_boundary; + upload_ctx->boundary_pos = upload_ctx->boundary_start; + + upload_flush_output_buffer(upload_ctx); + if(!upload_ctx->discard_data) + upload_finish_file(upload_ctx); + else + upload_abort_file(upload_ctx); + } + break; + /* + * Skip trailing garbage + */ + case upload_state_finish: + break; + } + } + + 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 + ); + } + } +} /* }}} */ + diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/nginx_upload_module-2.0.8/upload.html /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/nginx_upload_module-2.0.8/upload.html --- nginx-0.6.34/nginx_upload_module-2.0.8/upload.html 1970-01-01 01:00:00.000000000 +0100 +++ nginx-0.6.39~ppa1~hardy/nginx_upload_module-2.0.8/upload.html 2008-08-06 19:47:33.000000000 +0100 @@ -0,0 +1,18 @@ + + +Test upload + + +

Select files to upload

+
+
+
+
+
+
+
+ + +
+ + diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/core/nginx.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/core/nginx.c --- nginx-0.6.34/src/core/nginx.c 2008-11-20 17:23:08.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/core/nginx.c 2009-05-18 17:17:51.000000000 +0100 @@ -261,8 +261,8 @@ } #endif - p = "configure arguments: " NGX_CONFIGURE CRLF; - n = sizeof("configure arguments :" NGX_CONFIGURE CRLF) - 1; + p = "configure arguments:" NGX_CONFIGURE CRLF; + n = sizeof("configure arguments:" NGX_CONFIGURE CRLF) - 1; if (ngx_write_fd(ngx_stderr_fileno, p, n) != n) { return 1; diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/core/nginx.h /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/core/nginx.h --- nginx-0.6.34/src/core/nginx.h 2008-11-27 14:01:11.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/core/nginx.h 2009-09-07 12:53:09.000000000 +0100 @@ -8,7 +8,8 @@ #define _NGINX_H_INCLUDED_ -#define NGINX_VERSION "0.6.34" +#define nginx_version 006039 +#define NGINX_VERSION "0.6.39" #define NGINX_VER "nginx/" NGINX_VERSION #define NGINX_VAR "NGINX" diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/core/ngx_conf_file.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/core/ngx_conf_file.c --- nginx-0.6.34/src/core/ngx_conf_file.c 2008-11-20 17:23:08.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/core/ngx_conf_file.c 2009-04-01 16:47:12.000000000 +0100 @@ -201,14 +201,14 @@ if (filename) { ngx_free(cf->conf_file->buffer->start); - cf->conf_file = prev; - if (ngx_close_file(fd) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_ALERT, cf->log, ngx_errno, ngx_close_file_n " %s failed", cf->conf_file->file.name.data); return NGX_CONF_ERROR; } + + cf->conf_file = prev; } if (rc == NGX_ERROR) { @@ -853,31 +853,47 @@ ngx_conf_log_error(ngx_uint_t level, ngx_conf_t *cf, ngx_err_t err, char *fmt, ...) { - u_char errstr[NGX_MAX_CONF_ERRSTR], *buf, *last; + u_char errstr[NGX_MAX_CONF_ERRSTR], *p, *last; va_list args; last = errstr + NGX_MAX_CONF_ERRSTR; va_start(args, fmt); - buf = ngx_vsnprintf(errstr, last - errstr, fmt, args); + p = ngx_vsnprintf(errstr, last - errstr, fmt, args); va_end(args); - *buf = '\0'; - if (err) { - buf = ngx_snprintf(buf, last - buf - 1, " (%d: ", err); - buf = ngx_strerror_r(err, buf, last - buf - 1); - *buf++ = ')'; - *buf = '\0'; + + if (p > last - 50) { + + /* leave a space for an error code */ + + p = last - 50; + *p++ = '.'; + *p++ = '.'; + *p++ = '.'; + } + +#if (NGX_WIN32) + p = ngx_snprintf(p, last - p, ((unsigned) err < 0x80000000) + ? " (%d: " : " (%Xd: ", err); +#else + p = ngx_snprintf(p, last - p, " (%d: ", err); +#endif + + p = ngx_strerror_r(err, p, last - p); + + *p++ = ')'; } if (cf->conf_file == NULL) { - ngx_log_error(level, cf->log, 0, "%s", errstr); + ngx_log_error(level, cf->log, 0, "%*s", p - errstr, errstr); return; } - ngx_log_error(level, cf->log, 0, "%s in %s:%ui", - errstr, cf->conf_file->file.name.data, cf->conf_file->line); + ngx_log_error(level, cf->log, 0, "%*s in %s:%ui", + p - errstr, errstr, + cf->conf_file->file.name.data, cf->conf_file->line); } diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/core/ngx_conf_file.h /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/core/ngx_conf_file.h --- nginx-0.6.34/src/core/ngx_conf_file.h 2007-09-01 13:05:55.000000000 +0100 +++ nginx-0.6.39~ppa1~hardy/src/core/ngx_conf_file.h 2009-04-01 16:47:12.000000000 +0100 @@ -71,7 +71,7 @@ #define NGX_CONF_MODULE 0x464E4F43 /* "CONF" */ -#define NGX_MAX_CONF_ERRSTR 256 +#define NGX_MAX_CONF_ERRSTR 1024 struct ngx_command_s { diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/core/ngx_connection.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/core/ngx_connection.c --- nginx-0.6.34/src/core/ngx_connection.c 2008-07-07 11:18:41.000000000 +0100 +++ nginx-0.6.39~ppa1~hardy/src/core/ngx_connection.c 2009-04-01 17:42:09.000000000 +0100 @@ -779,12 +779,16 @@ { ngx_uint_t level; - if (err == NGX_ECONNRESET - && c->log_error == NGX_ERROR_IGNORE_ECONNRESET) - { + if (err == NGX_ECONNRESET && c->log_error == NGX_ERROR_IGNORE_ECONNRESET) { return 0; } +#if (NGX_SOLARIS) + if (err == NGX_EINVAL && c->log_error == NGX_ERROR_IGNORE_EINVAL) { + return 0; + } +#endif + if (err == 0 || err == NGX_ECONNRESET #if !(NGX_WIN32) @@ -800,6 +804,7 @@ { switch (c->log_error) { + case NGX_ERROR_IGNORE_EINVAL: case NGX_ERROR_IGNORE_ECONNRESET: case NGX_ERROR_INFO: level = NGX_LOG_INFO; diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/core/ngx_connection.h /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/core/ngx_connection.h --- nginx-0.6.34/src/core/ngx_connection.h 2007-03-19 13:20:15.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/core/ngx_connection.h 2009-06-22 10:50:51.000000000 +0100 @@ -71,10 +71,11 @@ typedef enum { - NGX_ERROR_CRIT = 0, + NGX_ERROR_ALERT = 0, NGX_ERROR_ERR, NGX_ERROR_INFO, - NGX_ERROR_IGNORE_ECONNRESET + NGX_ERROR_IGNORE_ECONNRESET, + NGX_ERROR_IGNORE_EINVAL } ngx_connection_log_error_e; @@ -133,9 +134,11 @@ ngx_atomic_uint_t number; + ngx_uint_t requests; + unsigned buffered:8; - unsigned log_error:2; /* ngx_connection_log_error_e */ + unsigned log_error:3; /* ngx_connection_log_error_e */ unsigned single_connection:1; unsigned unexpected_eof:1; diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/core/ngx_cpuinfo.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/core/ngx_cpuinfo.c --- nginx-0.6.34/src/core/ngx_cpuinfo.c 2008-01-29 07:06:18.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/core/ngx_cpuinfo.c 2009-05-18 17:02:06.000000000 +0100 @@ -72,7 +72,7 @@ ngx_cpuinfo(void) { u_char *vendor; - uint32_t vbuf[5], cpu[4]; + uint32_t vbuf[5], cpu[4], model; vbuf[0] = 0; vbuf[1] = 0; @@ -103,8 +103,10 @@ case 6: ngx_cacheline_size = 32; - if ((cpu[0] & 0xf0) >= 0xd0) { - /* Intel Core */ + model = ((cpu[0] & 0xf0000) >> 8) | (cpu[0] & 0xf0); + + if (model >= 0xd0) { + /* Intel Core, Core 2, Atom */ ngx_cacheline_size = 64; } diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/core/ngx_log.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/core/ngx_log.c --- nginx-0.6.34/src/core/ngx_log.c 2008-11-20 17:23:08.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/core/ngx_log.c 2009-05-18 17:05:02.000000000 +0100 @@ -127,18 +127,10 @@ } #if (NGX_WIN32) - - if ((unsigned) err >= 0x80000000) { - p = ngx_snprintf(p, last - p, " (%Xd: ", err); - - } else { - p = ngx_snprintf(p, last - p, " (%d: ", err); - } - + p = ngx_snprintf(p, last - p, ((unsigned) err < 0x80000000) + ? " (%d: " : " (%Xd: ", err); #else - p = ngx_snprintf(p, last - p, " (%d: ", err); - #endif p = ngx_strerror_r(err, p, last - p); @@ -192,9 +184,9 @@ void -ngx_log_abort(ngx_err_t err, const char *text) +ngx_log_abort(ngx_err_t err, const char *text, void *param) { - ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, err, text); + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, err, text, param); } @@ -310,7 +302,10 @@ } } - if (log->log_level == NGX_LOG_DEBUG) { + if (log->log_level == 0) { + log->log_level = NGX_LOG_ERR; + + } else if (log->log_level == NGX_LOG_DEBUG) { log->log_level = NGX_LOG_DEBUG_ALL; } diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/core/ngx_log.h /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/core/ngx_log.h --- nginx-0.6.34/src/core/ngx_log.h 2008-03-20 07:31:51.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/core/ngx_log.h 2009-05-18 17:05:02.000000000 +0100 @@ -198,7 +198,7 @@ ngx_log_t *ngx_log_init(void); ngx_log_t *ngx_log_create_errlog(ngx_cycle_t *cycle, ngx_array_t *args); char *ngx_set_error_log_levels(ngx_conf_t *cf, ngx_log_t *log); -void ngx_log_abort(ngx_err_t err, const char *text); +void ngx_log_abort(ngx_err_t err, const char *text, void *param); extern ngx_module_t ngx_errlog_module; diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/core/ngx_resolver.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/core/ngx_resolver.c --- nginx-0.6.34/src/core/ngx_resolver.c 2008-11-20 17:24:16.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/core/ngx_resolver.c 2009-04-01 17:25:12.000000000 +0100 @@ -578,6 +578,7 @@ ngx_int_t ngx_resolve_addr(ngx_resolver_ctx_t *ctx) { + u_char *name; ngx_resolver_t *r; ngx_resolver_node_t *rn; @@ -601,19 +602,21 @@ ngx_queue_insert_head(&r->addr_expire_queue, &rn->queue); - ctx->name.len = rn->nlen; - ctx->name.data = ngx_resolver_dup(r, rn->name, rn->nlen); - if (ctx->name.data == NULL) { + name = ngx_resolver_dup(r, rn->name, rn->nlen); + if (name == NULL) { goto failed; } + ctx->name.len = rn->nlen; + ctx->name.data = name; + /* unlock addr mutex */ ctx->state = NGX_OK; ctx->handler(ctx); - ngx_resolver_free(r, ctx->name.data); + ngx_resolver_free(r, name); return NGX_OK; } @@ -623,7 +626,9 @@ ctx->next = rn->waiting; rn->waiting = ctx; - return NGX_AGAIN; + /* unlock addr mutex */ + + return NGX_OK; } ngx_queue_remove(&rn->queue); @@ -1306,7 +1311,7 @@ ctx->handler(ctx); } - if (naddrs) { + if (naddrs > 1) { ngx_resolver_free(r, addrs); } @@ -1483,20 +1488,23 @@ goto short_response; } - len -= 2; - if (ngx_resolver_copy(r, &name, buf, &buf[i], &buf[n]) != NGX_OK) { return; } ngx_log_debug1(NGX_LOG_DEBUG_CORE, r->log, 0, "resolver an:%V", &name); - if (len != (size_t) rn->nlen || ngx_strncmp(name.data, rn->name, len) != 0) + if (name.len != (size_t) rn->nlen + || ngx_strncmp(name.data, rn->name, name.len) != 0) { - ngx_resolver_free(r, rn->name); + if (rn->nlen) { + ngx_resolver_free(r, rn->name); + } + + rn->nlen = (u_short) name.len; rn->name = name.data; - name.data = ngx_resolver_dup(r, rn->name, len); + name.data = ngx_resolver_dup(r, rn->name, name.len); if (name.data == NULL) { goto failed; } diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/core/ngx_shmtx.h /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/core/ngx_shmtx.h --- nginx-0.6.34/src/core/ngx_shmtx.h 2007-02-14 13:52:47.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/core/ngx_shmtx.h 2009-04-01 17:39:29.000000000 +0100 @@ -57,7 +57,15 @@ return 0; } - ngx_log_abort(err, ngx_trylock_fd_n " failed"); +#if __osf__ /* Tru64 UNIX */ + + if (err == NGX_EACCESS) { + return 0; + } + +#endif + + ngx_log_abort(err, ngx_trylock_fd_n " %s failed", mtx->name); return 0; } @@ -74,7 +82,7 @@ return; } - ngx_log_abort(err, ngx_lock_fd_n " failed"); + ngx_log_abort(err, ngx_lock_fd_n " %s failed", mtx->name); } @@ -89,7 +97,7 @@ return; } - ngx_log_abort(err, ngx_unlock_fd_n " failed"); + ngx_log_abort(err, ngx_unlock_fd_n " %s failed", mtx->name); } diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/core/ngx_slab.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/core/ngx_slab.c --- nginx-0.6.34/src/core/ngx_slab.c 2007-12-22 11:06:53.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/core/ngx_slab.c 2009-01-26 15:24:14.000000000 +0000 @@ -661,11 +661,8 @@ } page->slab = pages | NGX_SLAB_PAGE_START; - -#if (NGX_DEBUG) page->next = NULL; page->prev = NGX_SLAB_PAGE; -#endif if (--pages == 0) { return page; @@ -673,10 +670,8 @@ for (p = page + 1; pages; pages--) { p->slab = NGX_SLAB_PAGE_BUSY; -#if (NGX_DEBUG) p->next = NULL; p->prev = NGX_SLAB_PAGE; -#endif p++; } diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/core/ngx_string.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/core/ngx_string.c --- nginx-0.6.34/src/core/ngx_string.c 2008-03-24 13:04:02.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/core/ngx_string.c 2009-01-26 15:09:55.000000000 +0000 @@ -952,16 +952,16 @@ /* - * ngx_utf_decode() decodes two and more bytes UTF sequences only + * ngx_utf8_decode() decodes two and more bytes UTF sequences only * the return values: * 0x80 - 0x10ffff valid character - * 0x10ffff - 0xfffffffd invalid sequence + * 0x110000 - 0xfffffffd invalid sequence * 0xfffffffe incomplete sequence * 0xffffffff error */ uint32_t -ngx_utf_decode(u_char **p, size_t n) +ngx_utf8_decode(u_char **p, size_t n) { size_t len; uint32_t u, i, valid; @@ -1018,31 +1018,26 @@ size_t -ngx_utf_length(u_char *p, size_t n) +ngx_utf8_length(u_char *p, size_t n) { - u_char c; - size_t len; - ngx_uint_t i; + u_char c, *last; + size_t len; - for (len = 0, i = 0; i < n; len++, i++) { + last = p + n; - c = p[i]; + for (len = 0; p < last; len++) { + + c = *p; if (c < 0x80) { + p++; continue; } - if (c >= 0xc0) { - for (c <<= 1; c & 0x80; c <<= 1) { - i++; - } - - continue; + if (ngx_utf8_decode(&p, n) > 0x10ffff) { + /* invalid UTF-8 */ + return n; } - - /* invalid utf */ - - return n; } return len; @@ -1050,36 +1045,45 @@ u_char * -ngx_utf_cpystrn(u_char *dst, u_char *src, size_t n) +ngx_utf8_cpystrn(u_char *dst, u_char *src, size_t n, size_t len) { - u_char c; + u_char c, *next; if (n == 0) { return dst; } - for ( /* void */ ; --n; dst++, src++) { + while (--n) { c = *src; *dst = c; if (c < 0x80) { - if (*dst != '\0') { + + if (c != '\0') { + dst++; + src++; + len--; + continue; } return dst; } - if (c >= 0xc0) { - for (c <<= 1; c & 0x80; c <<= 1) { - *++dst = *++src; - } + next = src; - continue; + if (ngx_utf8_decode(&next, len) > 0x10ffff) { + /* invalid UTF-8 */ + break; } - /* invalid utf */ + len--; + + while (src < next) { + *++dst = *++src; + len--; + } } *dst = '\0'; diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/core/ngx_string.h /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/core/ngx_string.h --- nginx-0.6.34/src/core/ngx_string.h 2008-07-07 11:24:25.000000000 +0100 +++ nginx-0.6.39~ppa1~hardy/src/core/ngx_string.h 2009-04-02 07:44:45.000000000 +0100 @@ -52,9 +52,25 @@ #define ngx_strstr(s1, s2) strstr((const char *) s1, (const char *) s2) -#define ngx_strchr(s1, c) strchr((const char *) s1, (int) c) #define ngx_strlen(s) strlen((const char *) s) +#define ngx_strchr(s1, c) strchr((const char *) s1, (int) c) + +static ngx_inline u_char * +ngx_strlchr(u_char *p, u_char *last, u_char c) +{ + while (p < last) { + + if (*p == c) { + return p; + } + + p++; + } + + return NULL; +} + /* * msvc and icc7 compile memset() to the inline "rep stos" @@ -151,9 +167,9 @@ void ngx_encode_base64(ngx_str_t *dst, ngx_str_t *src); ngx_int_t ngx_decode_base64(ngx_str_t *dst, ngx_str_t *src); -uint32_t ngx_utf_decode(u_char **p, size_t n); -size_t ngx_utf_length(u_char *p, size_t n); -u_char *ngx_utf_cpystrn(u_char *dst, u_char *src, size_t n); +uint32_t ngx_utf8_decode(u_char **p, size_t n); +size_t ngx_utf8_length(u_char *p, size_t n); +u_char *ngx_utf8_cpystrn(u_char *dst, u_char *src, size_t n, size_t len); #define NGX_ESCAPE_URI 0 diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/event/ngx_event_openssl.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/event/ngx_event_openssl.c --- nginx-0.6.34/src/event/ngx_event_openssl.c 2008-11-20 17:20:15.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/event/ngx_event_openssl.c 2009-04-01 17:32:47.000000000 +0100 @@ -10,7 +10,7 @@ typedef struct { - ngx_str_t engine; + ngx_uint_t engine; /* unsigned engine:1; */ } ngx_openssl_conf_t; @@ -37,26 +37,17 @@ ngx_rbtree_node_t *node, ngx_rbtree_node_t *sentinel); static void *ngx_openssl_create_conf(ngx_cycle_t *cycle); -static char *ngx_openssl_init_conf(ngx_cycle_t *cycle, void *conf); +static char *ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static void ngx_openssl_exit(ngx_cycle_t *cycle); -#if !(NGX_SSL_ENGINE) -static char *ngx_openssl_noengine(ngx_conf_t *cf, ngx_command_t *cmd, - void *conf); -#endif - static ngx_command_t ngx_openssl_commands[] = { { ngx_string("ssl_engine"), NGX_MAIN_CONF|NGX_DIRECT_CONF|NGX_CONF_TAKE1, -#if (NGX_SSL_ENGINE) - ngx_conf_set_str_slot, -#else - ngx_openssl_noengine, -#endif + ngx_openssl_engine, + 0, 0, - offsetof(ngx_openssl_conf_t, engine), NULL }, ngx_null_command @@ -66,7 +57,7 @@ static ngx_core_module_t ngx_openssl_module_ctx = { ngx_string("openssl"), ngx_openssl_create_conf, - ngx_openssl_init_conf + NULL }; @@ -1921,8 +1912,7 @@ /* * set by ngx_pcalloc(): * - * oscf->engine.len = 0; - * oscf->engine.data = NULL; + * oscf->engine = 0; */ return oscf; @@ -1930,53 +1920,54 @@ static char * -ngx_openssl_init_conf(ngx_cycle_t *cycle, void *conf) +ngx_openssl_engine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { #if (NGX_SSL_ENGINE) ngx_openssl_conf_t *oscf = conf; - ENGINE *engine; + ENGINE *engine; + ngx_str_t *value; - if (oscf->engine.len == 0) { - return NGX_CONF_OK; + if (oscf->engine) { + return "is duplicate"; } - engine = ENGINE_by_id((const char *) oscf->engine.data); + oscf->engine = 1; + + value = cf->args->elts; + + engine = ENGINE_by_id((const char *) value[1].data); if (engine == NULL) { - ngx_ssl_error(NGX_LOG_WARN, cycle->log, 0, - "ENGINE_by_id(\"%V\") failed", &oscf->engine); + ngx_ssl_error(NGX_LOG_WARN, cf->log, 0, + "ENGINE_by_id(\"%V\") failed", &value[1]); return NGX_CONF_ERROR; } if (ENGINE_set_default(engine, ENGINE_METHOD_ALL) == 0) { - ngx_ssl_error(NGX_LOG_WARN, cycle->log, 0, + ngx_ssl_error(NGX_LOG_WARN, cf->log, 0, "ENGINE_set_default(\"%V\", ENGINE_METHOD_ALL) failed", - &oscf->engine); + &value[1]); + + ENGINE_free(engine); + return NGX_CONF_ERROR; } ENGINE_free(engine); -#endif - return NGX_CONF_OK; -} +#else -#if !(NGX_SSL_ENGINE) - -static char * -ngx_openssl_noengine(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) -{ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, "\"ssl_engine\" directive is available only in " "OpenSSL 0.9.7 and higher,"); return NGX_CONF_ERROR; -} #endif +} static void diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/http/modules/ngx_http_autoindex_module.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/http/modules/ngx_http_autoindex_module.c --- nginx-0.6.34/src/http/modules/ngx_http_autoindex_module.c 2007-11-08 15:21:54.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/http/modules/ngx_http_autoindex_module.c 2009-04-01 17:02:24.000000000 +0100 @@ -135,7 +135,7 @@ { u_char *last, *filename, scale; off_t length; - size_t len, copy, allocated, root; + size_t len, utf_len, allocated, root; ngx_tm_t tm; ngx_err_t err; ngx_buf_t *b; @@ -299,6 +299,11 @@ if (err != NGX_ENOENT) { ngx_log_error(NGX_LOG_CRIT, r->connection->log, err, ngx_de_info_n " \"%s\" failed", filename); + + if (err == NGX_EACCES) { + continue; + } + return ngx_http_autoindex_error(r, &dir, &path); } @@ -329,7 +334,7 @@ NGX_ESCAPE_HTML); if (r->utf8) { - entry->utf_len = ngx_utf_length(entry->name.data, entry->name.len); + entry->utf_len = ngx_utf8_length(entry->name.data, entry->name.len); } else { entry->utf_len = len; } @@ -412,15 +417,16 @@ len = entry[i].utf_len; - if (entry[i].name.len - len) { + if (entry[i].name.len != len) { if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) { - copy = NGX_HTTP_AUTOINDEX_NAME_LEN - 3 + 1; + utf_len = NGX_HTTP_AUTOINDEX_NAME_LEN - 3 + 1; } else { - copy = NGX_HTTP_AUTOINDEX_NAME_LEN + 1; + utf_len = NGX_HTTP_AUTOINDEX_NAME_LEN + 1; } - b->last = ngx_utf_cpystrn(b->last, entry[i].name.data, copy); + b->last = ngx_utf8_cpystrn(b->last, entry[i].name.data, + utf_len, entry[i].name.len + 1); last = b->last; } else { diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/http/modules/ngx_http_charset_filter_module.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/http/modules/ngx_http_charset_filter_module.c --- nginx-0.6.34/src/http/modules/ngx_http_charset_filter_module.c 2008-11-27 14:06:46.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/http/modules/ngx_http_charset_filter_module.c 2009-01-26 15:09:55.000000000 +0000 @@ -642,7 +642,7 @@ size = buf->last - src; saved = src; - n = ngx_utf_decode(&saved, size); + n = ngx_utf8_decode(&saved, size); if (n == 0xfffffffe) { /* incomplete UTF-8 symbol */ @@ -710,7 +710,7 @@ } saved = ctx->saved; - n = ngx_utf_decode(&saved, i); + n = ngx_utf8_decode(&saved, i); c = '\0'; @@ -818,7 +818,7 @@ len = buf->last - src; - n = ngx_utf_decode(&src, len); + n = ngx_utf8_decode(&src, len); if (n < 0x10000) { @@ -1270,7 +1270,7 @@ p = &table->src2dst[src * NGX_UTF_LEN] + 1; - n = ngx_utf_decode(&p, i); + n = ngx_utf8_decode(&p, i); if (n > 0xffff) { ngx_conf_log_error(NGX_LOG_EMERG, cf, 0, diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/http/modules/ngx_http_not_modified_filter_module.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/http/modules/ngx_http_not_modified_filter_module.c --- nginx-0.6.34/src/http/modules/ngx_http_not_modified_filter_module.c 2007-11-09 15:43:43.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/http/modules/ngx_http_not_modified_filter_module.c 2009-04-01 16:58:21.000000000 +0100 @@ -50,7 +50,8 @@ static ngx_int_t ngx_http_not_modified_header_filter(ngx_http_request_t *r) { - time_t ims; + time_t ims; + ngx_http_core_loc_conf_t *clcf; if (r->headers_out.status != NGX_HTTP_OK || r != r->main @@ -60,23 +61,32 @@ return ngx_http_next_header_filter(r); } + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf->if_modified_since == NGX_HTTP_IMS_OFF) { + return ngx_http_next_header_filter(r); + } + ims = ngx_http_parse_time(r->headers_in.if_modified_since->value.data, r->headers_in.if_modified_since->value.len); ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http ims:%d lm:%d", ims, r->headers_out.last_modified_time); - /* - * I think that the equality of the dates is correcter - */ - - if (ims == r->headers_out.last_modified_time) { - r->headers_out.status = NGX_HTTP_NOT_MODIFIED; - r->headers_out.content_type.len = 0; - ngx_http_clear_content_length(r); - ngx_http_clear_accept_ranges(r); + if (ims != r->headers_out.last_modified_time) { + + if (clcf->if_modified_since == NGX_HTTP_IMS_EXACT + || ims < r->headers_out.last_modified_time) + { + return ngx_http_next_header_filter(r); + } } + r->headers_out.status = NGX_HTTP_NOT_MODIFIED; + r->headers_out.content_type.len = 0; + ngx_http_clear_content_length(r); + ngx_http_clear_accept_ranges(r); + return ngx_http_next_header_filter(r); } diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/http/modules/ngx_http_proxy_module.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/http/modules/ngx_http_proxy_module.c --- nginx-0.6.34/src/http/modules/ngx_http_proxy_module.c 2008-11-27 14:22:34.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/http/modules/ngx_http_proxy_module.c 2009-05-18 17:13:02.000000000 +0100 @@ -1899,6 +1899,12 @@ conf->proxy_values = prev->proxy_values; } +#if (NGX_HTTP_SSL) + if (conf->upstream.ssl == NULL) { + conf->upstream.ssl = prev->upstream.ssl; + } +#endif + ngx_conf_merge_uint_value(conf->headers_hash_max_size, prev->headers_hash_max_size, 512); diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/http/modules/perl/nginx.pm /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/http/modules/perl/nginx.pm --- nginx-0.6.34/src/http/modules/perl/nginx.pm 2008-11-27 14:01:11.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/http/modules/perl/nginx.pm 2009-09-07 12:53:09.000000000 +0100 @@ -47,7 +47,7 @@ HTTP_INSUFFICIENT_STORAGE ); -our $VERSION = '0.6.34'; +our $VERSION = '0.6.39'; require XSLoader; XSLoader::load('nginx', $VERSION); diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/http/ngx_http.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/http/ngx_http.c --- nginx-0.6.34/src/http/ngx_http.c 2008-11-27 14:46:01.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/http/ngx_http.c 2009-04-02 07:44:45.000000000 +0100 @@ -406,7 +406,7 @@ use_rewrite = cmcf->phases[NGX_HTTP_REWRITE_PHASE].handlers.nelts ? 1 : 0; use_access = cmcf->phases[NGX_HTTP_ACCESS_PHASE].handlers.nelts ? 1 : 0; - n = use_rewrite + use_access + 1; /* find config phase */ + n = use_rewrite + use_access + cmcf->try_files + 1 /* find config phase */; for (i = 0; i < NGX_HTTP_LOG_PHASE; i++) { n += cmcf->phases[i].handlers.nelts; @@ -475,6 +475,15 @@ continue; + case NGX_HTTP_TRY_FILES_PHASE: + if (cmcf->try_files) { + ph->checker = ngx_http_core_try_files_phase; + n++; + ph++; + } + + continue; + case NGX_HTTP_CONTENT_PHASE: checker = ngx_http_core_content_phase; break; @@ -896,8 +905,8 @@ if (in_addr[i].hash.buckets == NULL && (in_addr[i].wc_head == NULL || in_addr[i].wc_head->hash.buckets == NULL) - && (in_addr[i].wc_head == NULL - || in_addr[i].wc_head->hash.buckets == NULL)) + && (in_addr[i].wc_tail == NULL + || in_addr[i].wc_tail->hash.buckets == NULL)) { continue; } diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/http/ngx_http_core_module.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/http/ngx_http_core_module.c --- nginx-0.6.34/src/http/ngx_http_core_module.c 2008-11-27 14:22:34.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/http/ngx_http_core_module.c 2009-06-22 10:50:51.000000000 +0100 @@ -30,7 +30,6 @@ static ngx_int_t ngx_http_core_find_location(ngx_http_request_t *r, ngx_array_t *locations, ngx_uint_t regex_start, size_t len); -static ngx_int_t ngx_http_core_send_continue(ngx_http_request_t *r); static ngx_int_t ngx_http_core_preconfiguration(ngx_conf_t *cf); static void *ngx_http_core_create_main_conf(ngx_conf_t *cf); @@ -63,6 +62,8 @@ void *conf); static char *ngx_http_core_error_page(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); +static char *ngx_http_core_try_files(ngx_conf_t *cf, ngx_command_t *cmd, + void *conf); static char *ngx_http_core_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf); static char *ngx_http_core_error_log(ngx_conf_t *cf, ngx_command_t *cmd, @@ -115,6 +116,14 @@ }; +static ngx_conf_enum_t ngx_http_core_if_modified_since[] = { + { ngx_string("off"), NGX_HTTP_IMS_OFF }, + { ngx_string("exact"), NGX_HTTP_IMS_EXACT }, + { ngx_string("before"), NGX_HTTP_IMS_BEFORE }, + { ngx_null_string, 0 } +}; + + #if (NGX_HTTP_GZIP) static ngx_conf_enum_t ngx_http_gzip_http_version[] = { @@ -417,6 +426,13 @@ 0, NULL }, + { ngx_string("keepalive_requests"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_num_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_core_loc_conf_t, keepalive_requests), + NULL }, + { ngx_string("satisfy"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, ngx_conf_set_enum_slot, @@ -508,6 +524,13 @@ offsetof(ngx_http_core_loc_conf_t, server_tokens), NULL }, + { ngx_string("if_modified_since"), + NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1, + ngx_conf_set_enum_slot, + NGX_HTTP_LOC_CONF_OFFSET, + offsetof(ngx_http_core_loc_conf_t, if_modified_since), + &ngx_http_core_if_modified_since }, + { ngx_string("error_page"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_2MORE, @@ -516,6 +539,13 @@ 0, NULL }, + { ngx_string("try_files"), + NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_2MORE, + ngx_http_core_try_files, + NGX_HTTP_LOC_CONF_OFFSET, + 0, + NULL }, + { ngx_string("post_action"), NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF |NGX_CONF_TAKE1, @@ -786,7 +816,7 @@ { u_char *p; size_t len; - ngx_int_t rc, expect; + ngx_int_t rc; ngx_http_core_loc_conf_t *clcf; ngx_http_core_srv_conf_t *cscf; @@ -833,15 +863,6 @@ return NGX_OK; } - if (r->headers_in.expect) { - expect = ngx_http_core_send_continue(r); - - if (expect != NGX_OK) { - ngx_http_finalize_request(r, expect); - return NGX_OK; - } - } - if (rc == NGX_HTTP_LOCATION_AUTO_REDIRECT) { r->headers_out.location = ngx_list_push(&r->headers_out.headers); if (r->headers_out.location == NULL) { @@ -1007,6 +1028,185 @@ ngx_int_t +ngx_http_core_try_files_phase(ngx_http_request_t *r, + ngx_http_phase_handler_t *ph) +{ + size_t len, root, alias, reserve, allocated; + u_char *p, *name; + ngx_str_t path, args; + ngx_uint_t test_dir; + ngx_http_try_file_t *tf; + ngx_open_file_info_t of; + ngx_http_script_code_pt code; + ngx_http_script_engine_t e; + ngx_http_core_loc_conf_t *clcf; + ngx_http_script_len_code_pt lcode; + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "try files phase: %ui", r->phase_handler); + + clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module); + + if (clcf->try_files == NULL) { + r->phase_handler++; + return NGX_AGAIN; + } + + allocated = 0; + root = 0; + name = NULL; + /* suppress MSVC warning */ + path.data = NULL; + + tf = clcf->try_files; + + alias = clcf->alias ? clcf->name.len : 0; + + for ( ;; ) { + + if (tf->lengths) { + ngx_memzero(&e, sizeof(ngx_http_script_engine_t)); + + e.ip = tf->lengths->elts; + e.request = r; + + /* 1 is for terminating '\0' as in static names */ + len = 1; + + while (*(uintptr_t *) e.ip) { + lcode = *(ngx_http_script_len_code_pt *) e.ip; + len += lcode(&e); + } + + } else { + len = tf->name.len; + } + + /* 16 bytes are preallocation */ + reserve = ngx_abs((ssize_t) (len - r->uri.len)) + alias + 16; + + if (reserve > allocated) { + + /* we just need to allocate path and to copy a root */ + + if (ngx_http_map_uri_to_path(r, &path, &root, reserve) == NULL) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_OK; + } + + name = path.data + root; + allocated = path.len - root - (r->uri.len - alias); + } + + if (tf->values == NULL) { + + /* tf->name.len includes the terminating '\0' */ + + ngx_memcpy(name, tf->name.data, tf->name.len); + + path.len = (name + tf->name.len - 1) - path.data; + + } else { + e.ip = tf->values->elts; + e.pos = name; + e.flushed = 1; + + while (*(uintptr_t *) e.ip) { + code = *(ngx_http_script_code_pt *) e.ip; + code((ngx_http_script_engine_t *) &e); + } + + path.len = e.pos - path.data; + + *e.pos = '\0'; + + if (alias && ngx_strncmp(name, clcf->name.data, alias) == 0) { + ngx_memcpy(name, name + alias, len - alias); + path.len -= alias; + } + } + + test_dir = tf->test_dir; + + tf++; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "try to use file: \"%s\" \"%s\"", name, path.data); + + if (tf->lengths == NULL && tf->name.len == 0) { + + path.len -= root; + path.data += root; + + if (path.data[0] == '@') { + (void) ngx_http_named_location(r, &path); + + } else { + ngx_http_split_args(r, &path, &args); + + (void) ngx_http_internal_redirect(r, &path, &args); + } + + return NGX_OK; + } + + ngx_memzero(&of, sizeof(ngx_open_file_info_t)); + + of.valid = clcf->open_file_cache_valid; + of.min_uses = clcf->open_file_cache_min_uses; + of.errors = clcf->open_file_cache_errors; + of.events = clcf->open_file_cache_events; + + if (ngx_open_cached_file(clcf->open_file_cache, &path, &of, r->pool) + != NGX_OK) + { + if (of.err != NGX_ENOENT && of.err != NGX_ENOTDIR) { + ngx_log_error(NGX_LOG_CRIT, r->connection->log, of.err, + ngx_open_file_n " \"%s\" failed", path.data); + } + + continue; + } + + if (of.is_dir && !test_dir) { + continue; + } + + path.len -= root; + path.data += root; + + if (!alias) { + r->uri = path; + + } else { + r->uri.len = alias + path.len; + r->uri.data = ngx_palloc(r->pool, r->uri.len); + if (r->uri.data == NULL) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_OK; + } + + p = ngx_copy(r->uri.data, clcf->name.data, alias); + ngx_memcpy(p, name, path.len); + } + + if (ngx_http_set_exten(r) != NGX_OK) { + ngx_http_finalize_request(r, NGX_HTTP_INTERNAL_SERVER_ERROR); + return NGX_OK; + } + + ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + "try file uri: \"%V\"", &r->uri); + + r->phase_handler++; + return NGX_AGAIN; + } + + /* not reached */ +} + + +ngx_int_t ngx_http_core_content_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph) { @@ -1101,8 +1301,13 @@ r->request_body_file_log_level = NGX_LOG_WARN; } - if (r->keepalive && clcf->keepalive_timeout == 0) { - r->keepalive = 0; + if (r->keepalive) { + if (clcf->keepalive_timeout == 0) { + r->keepalive = 0; + + } else if (r->connection->requests >= clcf->keepalive_requests) { + r->keepalive = 0; + } } if (!clcf->tcp_nopush) { @@ -1261,45 +1466,6 @@ } -static ngx_int_t -ngx_http_core_send_continue(ngx_http_request_t *r) -{ - ngx_int_t n; - ngx_str_t *expect; - - if (r->expect_tested) { - 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_HTTP_INTERNAL_SERVER_ERROR; -} - - ngx_int_t ngx_http_set_content_type(ngx_http_request_t *r) { @@ -1403,16 +1569,24 @@ ngx_int_t ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in) { - ngx_int_t rc; + ngx_int_t rc; + ngx_connection_t *c; - ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, + c = r->connection; + + ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0, "http output filter \"%V?%V\"", &r->uri, &r->args); rc = ngx_http_top_body_filter(r, in); if (rc == NGX_ERROR) { + + if (c->destroyed) { + return NGX_DONE; + } + /* NGX_ERROR may be returned by any filter */ - r->connection->error = 1; + c->error = 1; } return rc; @@ -1861,6 +2035,7 @@ sr->fast_subrequest = 1; sr->discard_body = r->discard_body; + sr->expect_tested = 1; sr->main_filter_need_in_memory = r->main_filter_need_in_memory; sr->uri_changes = NGX_HTTP_MAX_URI_CHANGES + 1; @@ -2720,6 +2895,7 @@ * lcf->default_type = { 0, NULL }; * lcf->err_log = NULL; * lcf->error_pages = NULL; + * lcf->try_files = NULL; * lcf->client_body_path = NULL; * lcf->regex = NULL; * lcf->exact_match = 0; @@ -2732,6 +2908,7 @@ lcf->client_body_buffer_size = NGX_CONF_UNSET_SIZE; lcf->client_body_timeout = NGX_CONF_UNSET_MSEC; lcf->satisfy = NGX_CONF_UNSET_UINT; + lcf->if_modified_since = NGX_CONF_UNSET_UINT; lcf->internal = NGX_CONF_UNSET; lcf->client_body_in_file_only = NGX_CONF_UNSET; lcf->sendfile = NGX_CONF_UNSET; @@ -2744,6 +2921,7 @@ lcf->limit_rate = NGX_CONF_UNSET_SIZE; lcf->keepalive_timeout = NGX_CONF_UNSET_MSEC; lcf->keepalive_header = NGX_CONF_UNSET; + lcf->keepalive_requests = NGX_CONF_UNSET_UINT; lcf->lingering_time = NGX_CONF_UNSET_MSEC; lcf->lingering_timeout = NGX_CONF_UNSET_MSEC; lcf->resolver_timeout = NGX_CONF_UNSET_MSEC; @@ -2918,6 +3096,8 @@ ngx_conf_merge_uint_value(conf->satisfy, prev->satisfy, NGX_HTTP_SATISFY_ALL); + ngx_conf_merge_uint_value(conf->if_modified_since, prev->if_modified_since, + NGX_HTTP_IMS_EXACT); ngx_conf_merge_value(conf->internal, prev->internal, 0); ngx_conf_merge_value(conf->client_body_in_file_only, prev->client_body_in_file_only, 0); @@ -2936,6 +3116,8 @@ prev->keepalive_timeout, 75000); ngx_conf_merge_sec_value(conf->keepalive_header, prev->keepalive_header, 0); + ngx_conf_merge_uint_value(conf->keepalive_requests, + prev->keepalive_requests, 100); ngx_conf_merge_msec_value(conf->lingering_time, prev->lingering_time, 30000); ngx_conf_merge_msec_value(conf->lingering_timeout, @@ -3636,6 +3818,71 @@ static char * +ngx_http_core_try_files(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) +{ + ngx_http_core_loc_conf_t *clcf = conf; + + ngx_str_t *value; + ngx_uint_t i, n; + ngx_http_try_file_t *tf; + ngx_http_script_compile_t sc; + ngx_http_core_main_conf_t *cmcf; + + if (clcf->try_files) { + return "is duplicate"; + } + + cmcf = ngx_http_conf_get_module_main_conf(cf, ngx_http_core_module); + + cmcf->try_files = 1; + + tf = ngx_pcalloc(cf->pool, cf->args->nelts * sizeof(ngx_http_try_file_t)); + if (tf == NULL) { + return NGX_CONF_ERROR; + } + + clcf->try_files = tf; + + value = cf->args->elts; + + for (i = 0; i < cf->args->nelts - 1; i++) { + + tf[i].name = value[i + 1]; + + if (tf[i].name.data[tf[i].name.len - 1] == '/') { + tf[i].test_dir = 1; + tf[i].name.len--; + tf[i].name.data[tf[i].name.len] = '\0'; + } + + n = ngx_http_script_variables_count(&tf[i].name); + + if (n) { + ngx_memzero(&sc, sizeof(ngx_http_script_compile_t)); + + sc.cf = cf; + sc.source = &tf[i].name; + sc.lengths = &tf[i].lengths; + sc.values = &tf[i].values; + sc.variables = n; + sc.complete_lengths = 1; + sc.complete_values = 1; + + if (ngx_http_script_compile(&sc) != NGX_OK) { + return NGX_CONF_ERROR; + } + + } else { + /* add trailing '\0' to length */ + tf[i].name.len++; + } + } + + return NGX_CONF_OK; +} + + +static char * ngx_http_core_open_file_cache(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) { ngx_http_core_loc_conf_t *lcf = conf; diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/http/ngx_http_core_module.h /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/http/ngx_http_core_module.h --- nginx-0.6.34/src/http/ngx_http_core_module.h 2008-07-07 11:18:41.000000000 +0100 +++ nginx-0.6.39~ppa1~hardy/src/http/ngx_http_core_module.h 2009-06-22 10:50:51.000000000 +0100 @@ -28,6 +28,11 @@ #define NGX_HTTP_SATISFY_ANY 1 +#define NGX_HTTP_IMS_OFF 0 +#define NGX_HTTP_IMS_EXACT 1 +#define NGX_HTTP_IMS_BEFORE 2 + + typedef struct { unsigned default_server:1; unsigned bind:1; @@ -74,6 +79,7 @@ NGX_HTTP_ACCESS_PHASE, NGX_HTTP_POST_ACCESS_PHASE, + NGX_HTTP_TRY_FILES_PHASE, NGX_HTTP_CONTENT_PHASE, NGX_HTTP_LOG_PHASE @@ -122,6 +128,8 @@ ngx_hash_keys_arrays_t *variables_keys; + ngx_uint_t try_files; /* unsigned try_files:1 */ + ngx_http_phase_t phases[NGX_HTTP_LOG_PHASE + 1]; } ngx_http_core_main_conf_t; @@ -231,6 +239,14 @@ } ngx_http_err_page_t; +typedef struct { + ngx_array_t *lengths; + ngx_array_t *values; + ngx_str_t name; + ngx_uint_t test_dir; /* unsigned test_dir:1; */ +} ngx_http_try_file_t; + + typedef struct ngx_http_core_loc_conf_s ngx_http_core_loc_conf_t; struct ngx_http_core_loc_conf_s { @@ -291,7 +307,9 @@ time_t keepalive_header; /* keepalive_timeout */ + ngx_uint_t keepalive_requests; /* keepalive_requests */ ngx_uint_t satisfy; /* satisfy */ + ngx_uint_t if_modified_since; /* if_modified_since */ ngx_flag_t internal; /* internal */ ngx_flag_t client_body_in_file_only; /* client_body_in_file_only */ @@ -319,6 +337,7 @@ #endif ngx_array_t *error_pages; /* error_page */ + ngx_http_try_file_t *try_files; /* try_files */ ngx_path_t *client_body_temp_path; /* client_body_temp_path */ @@ -350,6 +369,8 @@ ngx_http_phase_handler_t *ph); ngx_int_t ngx_http_core_post_access_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph); +ngx_int_t ngx_http_core_try_files_phase(ngx_http_request_t *r, + ngx_http_phase_handler_t *ph); ngx_int_t ngx_http_core_content_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph); diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/http/ngx_http.h /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/http/ngx_http.h --- nginx-0.6.34/src/http/ngx_http.h 2007-12-30 08:01:50.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/http/ngx_http.h 2009-04-02 07:44:45.000000000 +0100 @@ -71,6 +71,8 @@ ngx_int_t ngx_http_parse_header_line(ngx_http_request_t *r, ngx_buf_t *b); ngx_int_t ngx_http_parse_multi_header_lines(ngx_array_t *headers, ngx_str_t *name, ngx_str_t *value); +void ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri, + ngx_str_t *args); ngx_int_t ngx_http_find_server_conf(ngx_http_request_t *r); void ngx_http_update_location_config(ngx_http_request_t *r); diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/http/ngx_http_parse.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/http/ngx_http_parse.c --- nginx-0.6.34/src/http/ngx_http_parse.c 2008-03-16 16:47:16.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/http/ngx_http_parse.c 2009-09-07 12:57:04.000000000 +0100 @@ -738,6 +738,7 @@ /* first char */ case sw_start: + r->header_name_start = p; r->invalid_header = 0; switch (ch) { @@ -750,7 +751,6 @@ goto header_done; default: state = sw_name; - r->header_name_start = p; c = lowcase[ch]; @@ -1123,11 +1123,15 @@ #endif case '/': state = sw_slash; - u -= 4; - if (u < r->uri.data) { - return NGX_HTTP_PARSE_INVALID_REQUEST; - } - while (*(u - 1) != '/') { + u -= 5; + for ( ;; ) { + if (u < r->uri.data) { + return NGX_HTTP_PARSE_INVALID_REQUEST; + } + if (*u == '/') { + u++; + break; + } u--; } break; @@ -1467,3 +1471,39 @@ return NGX_DECLINED; } + + +void +ngx_http_split_args(ngx_http_request_t *r, ngx_str_t *uri, ngx_str_t *args) +{ + u_char ch, *p, *last; + + p = uri->data; + + last = p + uri->len; + + args->len = 0; + + while (p < last) { + + ch = *p++; + + if (ch == '?') { + args->len = last - p; + args->data = p; + + uri->len = p - 1 - uri->data; + + if (ngx_strlchr(p, last, '\0') != NULL) { + r->zero_in_uri = 1; + } + + return; + } + + if (ch == '\0') { + r->zero_in_uri = 1; + continue; + } + } +} diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/http/ngx_http_postpone_filter_module.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/http/ngx_http_postpone_filter_module.c --- nginx-0.6.34/src/http/ngx_http_postpone_filter_module.c 2008-07-07 12:32:02.000000000 +0100 +++ nginx-0.6.39~ppa1~hardy/src/http/ngx_http_postpone_filter_module.c 2009-05-18 16:39:08.000000000 +0100 @@ -216,7 +216,7 @@ r->postponed = r->postponed->next; } - if (r->out) { + if (r != r->main && r->out) { ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "http postpone filter out again \"%V?%V\"", &r->uri, &r->args); diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/http/ngx_http_request_body.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/http/ngx_http_request_body.c --- nginx-0.6.34/src/http/ngx_http_request_body.c 2007-11-15 14:26:36.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/http/ngx_http_request_body.c 2009-01-26 15:22:24.000000000 +0000 @@ -16,6 +16,7 @@ ngx_chain_t *body); static void ngx_http_read_discarded_request_body_handler(ngx_http_request_t *r); static ngx_int_t ngx_http_read_discarded_request_body(ngx_http_request_t *r); +static ngx_int_t ngx_http_test_expect(ngx_http_request_t *r); /* @@ -42,6 +43,10 @@ return NGX_OK; } + if (ngx_http_test_expect(r) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + rb = ngx_pcalloc(r->pool, sizeof(ngx_http_request_body_t)); if (rb == NULL) { return NGX_HTTP_INTERNAL_SERVER_ERROR; @@ -434,6 +439,10 @@ return NGX_OK; } + if (ngx_http_test_expect(r) != NGX_OK) { + return NGX_HTTP_INTERNAL_SERVER_ERROR; + } + rev = r->connection->read; ngx_log_debug0(NGX_LOG_DEBUG_HTTP, rev->log, 0, "http set discard body"); @@ -582,3 +591,45 @@ return NGX_AGAIN; } + + +static ngx_int_t +ngx_http_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 -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/http/ngx_http_request.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/http/ngx_http_request.c --- nginx-0.6.34/src/http/ngx_http_request.c 2008-11-27 14:22:34.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/http/ngx_http_request.c 2009-06-22 10:50:51.000000000 +0100 @@ -253,6 +253,8 @@ return; } + c->requests++; + hc = c->data; if (hc == NULL) { @@ -650,6 +652,7 @@ r->request_line.len = r->request_end - r->request_start; r->request_line.data = r->request_start; + *r->request_end = '\0'; if (r->args_start) { @@ -1387,9 +1390,7 @@ } } - if (r->method & (NGX_HTTP_POST|NGX_HTTP_PUT) - && r->headers_in.content_length_n == -1) - { + if (r->method & NGX_HTTP_PUT && r->headers_in.content_length_n == -1) { ngx_log_error(NGX_LOG_INFO, r->connection->log, 0, "client sent %V method without \"Content-Length\" header", &r->method_name); @@ -2091,6 +2092,7 @@ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0, "set http keepalive handler"); if (r->discard_body) { + r->write_event_handler = ngx_http_request_empty_handler; r->lingering_time = ngx_time() + (time_t) (clcf->lingering_time / 1000); ngx_add_timer(rev, clcf->lingering_timeout); return; @@ -2264,8 +2266,15 @@ (const void *) &tcp_nodelay, sizeof(int)) == -1) { +#if (NGX_SOLARIS) + /* Solaris returns EINVAL if a socket has been shut down */ + c->log_error = NGX_ERROR_IGNORE_EINVAL; +#endif + ngx_connection_error(c, ngx_socket_errno, "setsockopt(TCP_NODELAY) failed"); + + c->log_error = NGX_ERROR_INFO; ngx_http_close_connection(c); return; } diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/http/ngx_http_script.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/http/ngx_http_script.c --- nginx-0.6.34/src/http/ngx_http_script.c 2008-02-12 18:05:32.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/http/ngx_http_script.c 2009-04-01 17:31:18.000000000 +0100 @@ -244,10 +244,21 @@ name.data = &sc->source->data[i]; - while (i < sc->source->len - && sc->source->data[i] != '$' - && !(sc->source->data[i] == '?' && sc->compile_args)) - { + while (i < sc->source->len) { + + if (sc->source->data[i] == '$') { + break; + } + + if (sc->source->data[i] == '?') { + + sc->args = 1; + + if (sc->compile_args) { + break; + } + } + i++; name.len++; } diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/http/ngx_http_special_response.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/http/ngx_http_special_response.c --- nginx-0.6.34/src/http/ngx_http_special_response.c 2008-11-20 17:09:15.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/http/ngx_http_special_response.c 2009-01-26 15:22:24.000000000 +0000 @@ -378,6 +378,8 @@ } } + r->expect_tested = 1; + if (ngx_http_discard_request_body(r) != NGX_OK) { error = NGX_HTTP_INTERNAL_SERVER_ERROR; } @@ -430,11 +432,18 @@ ngx_http_send_error_page(ngx_http_request_t *r, ngx_http_err_page_t *err_page) { u_char ch, *p, *last; + ngx_int_t overwrite; ngx_str_t *uri, *args, u, a; ngx_table_elt_t *location; ngx_http_core_loc_conf_t *clcf; - r->err_status = err_page->overwrite; + overwrite = err_page->overwrite; + + if (overwrite && overwrite != NGX_HTTP_OK) { + r->expect_tested = 1; + } + + r->err_status = overwrite; r->zero_in_uri = 0; diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/http/ngx_http_upstream.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/http/ngx_http_upstream.c --- nginx-0.6.34/src/http/ngx_http_upstream.c 2008-11-27 14:31:44.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/http/ngx_http_upstream.c 2009-04-01 17:21:05.000000000 +0100 @@ -1099,62 +1099,59 @@ #endif } - n = c->recv(c, u->buffer.last, u->buffer.end - u->buffer.last); + for ( ;; ) { + + n = c->recv(c, u->buffer.last, u->buffer.end - u->buffer.last); - if (n == NGX_AGAIN) { + if (n == NGX_AGAIN) { #if 0 - ngx_add_timer(rev, u->read_timeout); + ngx_add_timer(rev, u->read_timeout); #endif - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { - ngx_http_upstream_finalize_request(r, u, + if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { + ngx_http_upstream_finalize_request(r, u, NGX_HTTP_INTERNAL_SERVER_ERROR); + return; + } + return; } - return; - } - - if (n == 0) { - ngx_log_error(NGX_LOG_ERR, rev->log, 0, - "upstream prematurely closed connection"); - } + if (n == 0) { + ngx_log_error(NGX_LOG_ERR, rev->log, 0, + "upstream prematurely closed connection"); + } - if (n == NGX_ERROR || n == 0) { - ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); - return; - } + if (n == NGX_ERROR || n == 0) { + ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_ERROR); + return; + } - u->buffer.last += n; + u->buffer.last += n; #if 0 - u->valid_header_in = 0; + u->valid_header_in = 0; - u->peer.cached = 0; + u->peer.cached = 0; #endif - rc = u->process_header(r); + rc = u->process_header(r); - if (rc == NGX_AGAIN) { -#if 0 - ngx_add_timer(rev, u->read_timeout); -#endif + if (rc == NGX_AGAIN) { - if (u->buffer.pos == u->buffer.end) { - ngx_log_error(NGX_LOG_ERR, rev->log, 0, - "upstream sent too big header"); + if (u->buffer.pos == u->buffer.end) { + ngx_log_error(NGX_LOG_ERR, rev->log, 0, + "upstream sent too big header"); - ngx_http_upstream_next(r, u, NGX_HTTP_UPSTREAM_FT_INVALID_HEADER); - return; - } + ngx_http_upstream_next(r, u, + NGX_HTTP_UPSTREAM_FT_INVALID_HEADER); + return; + } - if (ngx_handle_read_event(rev, 0) == NGX_ERROR) { - ngx_http_upstream_finalize_request(r, u, - NGX_HTTP_INTERNAL_SERVER_ERROR); - return; + continue; } - return; + break; } if (rc == NGX_HTTP_UPSTREAM_INVALID_HEADER) { @@ -1962,6 +1959,7 @@ static void ngx_http_upstream_process_body(ngx_event_t *ev) { + ngx_uint_t del; ngx_temp_file_t *tf; ngx_event_pipe_t *p; ngx_connection_t *c, *downstream; @@ -2057,20 +2055,25 @@ if (u->store) { + del = p->upstream_error; + tf = u->pipe->temp_file; - if (p->upstream_eof - && u->headers_in.status_n == NGX_HTTP_OK - && (u->headers_in.content_length_n == -1 - || (u->headers_in.content_length_n == tf->offset))) - { - ngx_http_upstream_store(r, u); + if (p->upstream_eof || p->upstream_done) { + + if (u->headers_in.status_n == NGX_HTTP_OK + && (u->headers_in.content_length_n == -1 + || (u->headers_in.content_length_n == tf->offset))) + { + ngx_http_upstream_store(r, u); + + } else { + del = 1; + } + } + + if (del && tf->file.fd != NGX_INVALID_FILE) { - } else if ((p->upstream_error - || (p->upstream_eof - && u->headers_in.status_n != NGX_HTTP_OK)) - && tf->file.fd != NGX_INVALID_FILE) - { if (ngx_delete_file(tf->file.name.data) == NGX_FILE_ERROR) { ngx_log_error(NGX_LOG_CRIT, r->connection->log, ngx_errno, @@ -2345,7 +2348,9 @@ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0, "finalize http upstream request: %i", rc); - *u->cleanup = NULL; + if (u->cleanup) { + *u->cleanup = NULL; + } if (u->state && u->state->response_sec) { tp = ngx_timeofday(); diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/http/ngx_http_variables.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/http/ngx_http_variables.c --- nginx-0.6.34/src/http/ngx_http_variables.c 2008-07-07 12:20:46.000000000 +0100 +++ nginx-0.6.39~ppa1~hardy/src/http/ngx_http_variables.c 2009-04-01 16:36:15.000000000 +0100 @@ -26,6 +26,10 @@ ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_unknown_header_out(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_cookie(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); +static ngx_int_t ngx_http_variable_argument(ngx_http_request_t *r, + ngx_http_variable_value_t *v, uintptr_t data); static ngx_int_t ngx_http_variable_host(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data); @@ -481,6 +485,24 @@ return NULL; } + if (ngx_strncmp(name->data, "cookie_", 7) == 0) { + + if (ngx_http_variable_cookie(r, vv, (uintptr_t) name) == NGX_OK) { + return vv; + } + + return NULL; + } + + if (ngx_strncmp(name->data, "arg_", 4) == 0) { + + if (ngx_http_variable_argument(r, vv, (uintptr_t) name) == NGX_OK) { + return vv; + } + + return NULL; + } + vv->not_found = 1; if (nowarn == 0) { @@ -712,6 +734,90 @@ static ngx_int_t +ngx_http_variable_cookie(ngx_http_request_t *r, ngx_http_variable_value_t *v, + uintptr_t data) +{ + ngx_str_t *name = (ngx_str_t *) data; + + ngx_str_t cookie, s; + + s.len = name->len - (sizeof("cookie_") - 1); + s.data = name->data + sizeof("cookie_") - 1; + + if (ngx_http_parse_multi_header_lines(&r->headers_in.cookies, &s, &cookie) + == NGX_DECLINED) + { + v->not_found = 1; + return NGX_OK; + } + + v->len = cookie.len; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + v->data = cookie.data; + + return NGX_OK; +} + + +static ngx_int_t +ngx_http_variable_argument(ngx_http_request_t *r, ngx_http_variable_value_t *v, + uintptr_t data) +{ + ngx_str_t *name = (ngx_str_t *) data; + + u_char *p, *arg; + size_t len; + + if (r->args.len == 0) { + v->not_found = 1; + return NGX_OK; + } + + len = name->len - 1 - (sizeof("arg_") - 1); + arg = name->data + sizeof("arg_") - 1; + + for (p = r->args.data; *p && *p != ' '; p++) { + + /* + * although r->args.data is not null-terminated by itself, + * however, there is null in the end of request line + */ + + p = ngx_strcasestrn(p, (char *) arg, len); + + if (p == NULL) { + v->not_found = 1; + return NGX_OK; + } + + if ((p == r->args.data || *(p - 1) == '&') && *(p + len + 1) == '=') { + + v->data = p + len + 2; + + p = (u_char *) ngx_strchr(p, '&'); + + if (p == NULL) { + p = r->args.data + r->args.len; + } + + v->len = p - v->data; + v->valid = 1; + v->no_cacheable = 0; + v->not_found = 0; + + return NGX_OK; + } + } + + v->not_found = 1; + + return NGX_OK; +} + + +static ngx_int_t ngx_http_variable_host(ngx_http_request_t *r, ngx_http_variable_value_t *v, uintptr_t data) { @@ -1396,6 +1502,20 @@ continue; } + if (ngx_strncmp(v[i].name.data, "cookie_", 7) == 0) { + v[i].get_handler = ngx_http_variable_cookie; + v[i].data = (uintptr_t) &v[i].name; + + continue; + } + + if (ngx_strncmp(v[i].name.data, "arg_", 4) == 0) { + v[i].get_handler = ngx_http_variable_argument; + v[i].data = (uintptr_t) &v[i].name; + + continue; + } + ngx_log_error(NGX_LOG_EMERG, cf->log, 0, "unknown \"%V\" variable", &v[i].name); diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/mail/ngx_mail_auth_http_module.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/mail/ngx_mail_auth_http_module.c --- nginx-0.6.34/src/mail/ngx_mail_auth_http_module.c 2007-11-15 14:26:36.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/mail/ngx_mail_auth_http_module.c 2009-05-18 16:58:46.000000000 +0100 @@ -140,6 +140,7 @@ static ngx_str_t ngx_mail_auth_http_method[] = { ngx_string("plain"), ngx_string("plain"), + ngx_string("plain"), ngx_string("apop"), ngx_string("cram-md5") }; diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/mail/ngx_mail.h /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/mail/ngx_mail.h --- nginx-0.6.34/src/mail/ngx_mail.h 2008-02-13 13:50:04.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/mail/ngx_mail.h 2009-05-18 16:58:46.000000000 +0100 @@ -164,7 +164,7 @@ unsigned no_sync_literal:1; unsigned starttls:1; unsigned esmtp:1; - unsigned auth_method:2; + unsigned auth_method:3; unsigned auth_wait:1; ngx_str_t login; @@ -243,10 +243,11 @@ #define NGX_SMTP_STARTTLS 13 -#define NGX_MAIL_AUTH_PLAIN 0 -#define NGX_MAIL_AUTH_LOGIN 1 -#define NGX_MAIL_AUTH_APOP 2 -#define NGX_MAIL_AUTH_CRAM_MD5 3 +#define NGX_MAIL_AUTH_PLAIN 0 +#define NGX_MAIL_AUTH_LOGIN 1 +#define NGX_MAIL_AUTH_LOGIN_USERNAME 2 +#define NGX_MAIL_AUTH_APOP 3 +#define NGX_MAIL_AUTH_CRAM_MD5 4 #define NGX_MAIL_AUTH_PLAIN_ENABLED 0x0002 @@ -329,7 +330,7 @@ ngx_int_t ngx_mail_auth_plain(ngx_mail_session_t *s, ngx_connection_t *c, ngx_uint_t n); ngx_int_t ngx_mail_auth_login_username(ngx_mail_session_t *s, - ngx_connection_t *c); + ngx_connection_t *c, ngx_uint_t n); ngx_int_t ngx_mail_auth_login_password(ngx_mail_session_t *s, ngx_connection_t *c); ngx_int_t ngx_mail_auth_cram_md5_salt(ngx_mail_session_t *s, diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/mail/ngx_mail_handler.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/mail/ngx_mail_handler.c --- nginx-0.6.34/src/mail/ngx_mail_handler.c 2007-12-10 12:09:51.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/mail/ngx_mail_handler.c 2009-05-18 16:58:46.000000000 +0100 @@ -335,21 +335,22 @@ ngx_int_t -ngx_mail_auth_login_username(ngx_mail_session_t *s, ngx_connection_t *c) +ngx_mail_auth_login_username(ngx_mail_session_t *s, ngx_connection_t *c, + ngx_uint_t n) { ngx_str_t *arg; arg = s->args.elts; ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, - "mail auth login username: \"%V\"", &arg[0]); + "mail auth login username: \"%V\"", &arg[n]); - s->login.data = ngx_palloc(c->pool, ngx_base64_decoded_length(arg[0].len)); + s->login.data = ngx_palloc(c->pool, ngx_base64_decoded_length(arg[n].len)); if (s->login.data == NULL){ return NGX_ERROR; } - if (ngx_decode_base64(&s->login, &arg[0]) != NGX_OK) { + if (ngx_decode_base64(&s->login, &arg[n]) != NGX_OK) { ngx_log_error(NGX_LOG_INFO, c->log, 0, "client sent invalid base64 encoding in AUTH LOGIN command"); return NGX_MAIL_PARSE_INVALID_COMMAND; diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/mail/ngx_mail_imap_handler.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/mail/ngx_mail_imap_handler.c --- nginx-0.6.34/src/mail/ngx_mail_imap_handler.c 2007-12-10 12:09:51.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/mail/ngx_mail_imap_handler.c 2009-05-18 16:58:46.000000000 +0100 @@ -205,7 +205,7 @@ break; case ngx_imap_auth_login_username: - rc = ngx_mail_auth_login_username(s, c); + rc = ngx_mail_auth_login_username(s, c, 0); tag = 0; s->out.len = sizeof(imap_password) - 1; @@ -370,6 +370,14 @@ return NGX_OK; + case NGX_MAIL_AUTH_LOGIN_USERNAME: + + s->out.len = sizeof(imap_password) - 1; + s->out.data = imap_password; + s->mail_state = ngx_imap_auth_login_password; + + return ngx_mail_auth_login_username(s, c, 1); + case NGX_MAIL_AUTH_PLAIN: s->out.len = sizeof(imap_plain_next) - 1; diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/mail/ngx_mail_parse.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/mail/ngx_mail_parse.c --- nginx-0.6.34/src/mail/ngx_mail_parse.c 2007-09-13 22:24:27.000000000 +0100 +++ nginx-0.6.39~ppa1~hardy/src/mail/ngx_mail_parse.c 2009-05-18 16:58:46.000000000 +0100 @@ -848,6 +848,10 @@ return NGX_MAIL_AUTH_LOGIN; } + if (s->args.nelts == 2) { + return NGX_MAIL_AUTH_LOGIN_USERNAME; + } + return NGX_MAIL_PARSE_INVALID_COMMAND; } diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/mail/ngx_mail_pop3_handler.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/mail/ngx_mail_pop3_handler.c --- nginx-0.6.34/src/mail/ngx_mail_pop3_handler.c 2007-12-10 12:09:51.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/mail/ngx_mail_pop3_handler.c 2009-05-18 16:58:46.000000000 +0100 @@ -226,7 +226,7 @@ break; case ngx_pop3_auth_login_username: - rc = ngx_mail_auth_login_username(s, c); + rc = ngx_mail_auth_login_username(s, c, 0); s->out.len = sizeof(pop3_password) - 1; s->out.data = pop3_password; @@ -474,6 +474,14 @@ return NGX_OK; + case NGX_MAIL_AUTH_LOGIN_USERNAME: + + s->out.len = sizeof(pop3_password) - 1; + s->out.data = pop3_password; + s->mail_state = ngx_pop3_auth_login_password; + + return ngx_mail_auth_login_username(s, c, 1); + case NGX_MAIL_AUTH_PLAIN: s->out.len = sizeof(pop3_next) - 1; diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/mail/ngx_mail_smtp_handler.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/mail/ngx_mail_smtp_handler.c --- nginx-0.6.34/src/mail/ngx_mail_smtp_handler.c 2008-02-16 13:46:33.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/mail/ngx_mail_smtp_handler.c 2009-05-18 16:58:46.000000000 +0100 @@ -12,6 +12,7 @@ static void ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t *ctx); +static void ngx_mail_smtp_resolve_name(ngx_event_t *rev); static void ngx_mail_smtp_resolve_name_handler(ngx_resolver_ctx_t *ctx); static void ngx_mail_smtp_greeting(ngx_mail_session_t *s, ngx_connection_t *c); static void ngx_mail_smtp_invalid_pipelining(ngx_event_t *rev); @@ -88,9 +89,8 @@ static void ngx_mail_smtp_resolve_addr_handler(ngx_resolver_ctx_t *ctx) { - ngx_connection_t *c; - ngx_mail_session_t *s; - ngx_mail_core_srv_conf_t *cscf; + ngx_connection_t *c; + ngx_mail_session_t *s; s = ctx->data; c = s->connection; @@ -131,6 +131,23 @@ ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0, "address resolved: %V", &s->host); + c->read->handler = ngx_mail_smtp_resolve_name; + + ngx_post_event(c->read, &ngx_posted_events); +} + + +static void +ngx_mail_smtp_resolve_name(ngx_event_t *rev) +{ + ngx_connection_t *c; + ngx_mail_session_t *s; + ngx_resolver_ctx_t *ctx; + ngx_mail_core_srv_conf_t *cscf; + + c = rev->data; + s = c->data; + cscf = ngx_mail_get_module_srv_conf(s, ngx_mail_core_module); ctx = ngx_resolve_start(cscf->resolver, NULL); @@ -435,7 +452,7 @@ break; case ngx_smtp_auth_login_username: - rc = ngx_mail_auth_login_username(s, c); + rc = ngx_mail_auth_login_username(s, c, 0); s->out.len = sizeof(smtp_password) - 1; s->out.data = smtp_password; @@ -579,6 +596,14 @@ return NGX_OK; + case NGX_MAIL_AUTH_LOGIN_USERNAME: + + s->out.len = sizeof(smtp_password) - 1; + s->out.data = smtp_password; + s->mail_state = ngx_smtp_auth_login_password; + + return ngx_mail_auth_login_username(s, c, 1); + case NGX_MAIL_AUTH_PLAIN: s->out.len = sizeof(smtp_next) - 1; diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/os/unix/ngx_errno.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/os/unix/ngx_errno.c --- nginx-0.6.34/src/os/unix/ngx_errno.c 2006-08-30 11:39:17.000000000 +0100 +++ nginx-0.6.39~ppa1~hardy/src/os/unix/ngx_errno.c 2009-04-01 16:47:12.000000000 +0100 @@ -10,10 +10,11 @@ #if (NGX_HAVE_STRERROR_R) -u_char *ngx_strerror_r(int err, u_char *errstr, size_t size) +u_char * +ngx_strerror_r(int err, u_char *errstr, size_t size) { if (size == 0) { - return 0; + return errstr; } errstr[0] = '\0'; @@ -32,12 +33,13 @@ /* Linux strerror_r() */ -u_char *ngx_strerror_r(int err, u_char *errstr, size_t size) +u_char * +ngx_strerror_r(int err, u_char *errstr, size_t size) { char *str; if (size == 0) { - return 0; + return errstr; } errstr[0] = '\0'; diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/os/unix/ngx_posix_init.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/os/unix/ngx_posix_init.c --- nginx-0.6.34/src/os/unix/ngx_posix_init.c 2007-12-03 16:46:46.000000000 +0000 +++ nginx-0.6.39~ppa1~hardy/src/os/unix/ngx_posix_init.c 2009-01-26 15:22:24.000000000 +0000 @@ -22,7 +22,7 @@ ngx_unix_recv, ngx_readv_chain, ngx_udp_unix_recv, - NULL, + ngx_unix_send, ngx_writev_chain, 0 }; diff -Nru /tmp/XwShcMNjPH/nginx-0.6.34/src/os/unix/ngx_process.c /tmp/L6jF7LiS7s/nginx-0.6.39~ppa1~hardy/src/os/unix/ngx_process.c --- nginx-0.6.34/src/os/unix/ngx_process.c 2008-04-09 20:09:13.000000000 +0100 +++ nginx-0.6.39~ppa1~hardy/src/os/unix/ngx_process.c 2009-04-01 17:28:47.000000000 +0100 @@ -494,10 +494,16 @@ } if (WTERMSIG(status)) { +#ifdef WCOREDUMP ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, "%s %P exited on signal %d%s", process, pid, WTERMSIG(status), WCOREDUMP(status) ? " (core dumped)" : ""); +#else + ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0, + "%s %P exited on signal %d", + process, pid, WTERMSIG(status)); +#endif } else { ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,