summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES35
-rw-r--r--CHANGES.ru37
-rw-r--r--conf/nginx.conf2
-rw-r--r--src/core/nginx.h2
-rw-r--r--src/core/ngx_connection.h1
-rw-r--r--src/core/ngx_palloc.c2
-rw-r--r--src/core/ngx_string.c54
-rw-r--r--src/core/ngx_string.h2
-rw-r--r--src/event/modules/ngx_rtsig_module.c2
-rw-r--r--src/event/ngx_event_pipe.c6
-rw-r--r--src/http/modules/ngx_http_autoindex_module.c93
-rw-r--r--src/http/modules/ngx_http_gzip_filter_module.c82
-rw-r--r--src/http/modules/ngx_http_proxy_module.c6
-rw-r--r--src/http/modules/ngx_http_rewrite_module.c28
-rw-r--r--src/http/modules/ngx_http_ssi_filter_module.c115
-rw-r--r--src/http/modules/ngx_http_static_module.c6
-rw-r--r--src/http/ngx_http_copy_filter_module.c12
-rw-r--r--src/http/ngx_http_core_module.c17
-rw-r--r--src/http/ngx_http_core_module.h1
-rw-r--r--src/http/ngx_http_postpone_filter_module.c15
-rw-r--r--src/http/ngx_http_request.c52
-rw-r--r--src/http/ngx_http_request.h3
-rw-r--r--src/http/ngx_http_request_body.c2
-rw-r--r--src/http/ngx_http_script.c10
-rw-r--r--src/http/ngx_http_script.h1
-rw-r--r--src/http/ngx_http_special_response.c3
-rw-r--r--src/http/ngx_http_upstream.c86
-rw-r--r--src/http/ngx_http_upstream.h6
-rw-r--r--src/http/ngx_http_variables.c4
-rw-r--r--src/http/ngx_http_write_filter_module.c15
-rw-r--r--src/imap/ngx_imap.c189
-rw-r--r--src/imap/ngx_imap.h189
-rw-r--r--src/imap/ngx_imap_auth_http_module.c1067
-rw-r--r--src/imap/ngx_imap_core_module.c455
-rw-r--r--src/imap/ngx_imap_handler.c561
-rw-r--r--src/imap/ngx_imap_parse.c500
-rw-r--r--src/imap/ngx_imap_proxy_module.c623
-rw-r--r--src/os/unix/ngx_linux_config.h5
-rw-r--r--src/os/unix/ngx_user.c4
39 files changed, 4152 insertions, 141 deletions
diff --git a/CHANGES b/CHANGES
index 94100007a..98a7a97f0 100644
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,39 @@
+Changes with nginx 0.1.38 08 Jul 2005
+
+ *) Feature: the "limit_rate" directive is supported in in proxy and
+ FastCGI mode.
+
+ *) Feature: the "X-Accel-Limit-Rate" response header line is supported
+ in proxy and FastCGI mode.
+
+ *) Feature: the "break" directive.
+
+ *) Feature: the "log_not_found" directive.
+
+ *) Bugfix: the response status code was not changed when request was
+ redirected by the ""X-Accel-Redirect" header line.
+
+ *) Bugfix: the variables set by the "set" directive could not be used
+ in SSI.
+
+ *) Bugfix: the segmentation fault may occurred if the SSI page has more
+ than one remote subrequest.
+
+ *) Bugfix: nginx treated the backend response as invalid if the status
+ line in the header was transferred in two packets; bug appeared in
+ 0.1.29.
+
+ *) Feature: the "ssi_types" directive.
+
+ *) Feature: the "autoindex_exact_size" directive.
+
+ *) Bugfix: the ngx_http_autoindex_module did not support the long file
+ names in UTF-8.
+
+ *) Feature: the IMAP/POP3 proxy.
+
+
Changes with nginx 0.1.37 23 Jun 2005
*) Change: now the "\n" is added to the end of the "nginx.pid" file.
diff --git a/CHANGES.ru b/CHANGES.ru
index e74b84324..d60d4220e 100644
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,39 @@
+Изменения в nginx 0.1.38 08.07.2005
+
+ *) Добавление: директива limit_rate поддерживается в режиме прокси и
+ FastCGI.
+
+ *) Добавление: в режиме прокси и FastCGI поддерживается строка
+ заголовка "X-Accel-Limit-Rate" в ответе бэкенда.
+
+ *) Добавление: директива break.
+
+ *) Добавление: директива log_not_found.
+
+ *) Исправление: при перенаправлении запроса с помощью строки заголовка
+ "X-Accel-Redirect" не изменялся код ответа.
+
+ *) Исправление: переменные, установленные директивой set не могли
+ использоваться в SSI.
+
+ *) Исправление: при включении в SSI более одного удалённого подзапроса
+ мог произойти segmentation fault.
+
+ *) Исправление: если статусная строка в ответе бэкенда передавалась в
+ двух пакетах, то nginx считал ответ неверным; ошибка появилась в
+ 0.1.29.
+
+ *) Добавление: директива ssi_types.
+
+ *) Добавление: директива autoindex_exact_size.
+
+ *) Исправление: модуль ngx_http_autoindex_module не поддерживал длинные
+ имена файлов в UTF-8.
+
+ *) Добавление: IMAP/POP3 прокси.
+
+
Изменения в nginx 0.1.37 23.06.2005
*) Изменение: в конце файла nginx.pid теперь добавляется "\n".
@@ -97,7 +132,7 @@
регулярных выражений.
*) Добавление: в режиме прокси и FastCGI поддерживается строка
- заголовка X-Accel-Redirect в ответе бэкенда.
+ заголовка "X-Accel-Redirect" в ответе бэкенда.
Изменения в nginx 0.1.31 16.05.2005
diff --git a/conf/nginx.conf b/conf/nginx.conf
index ffe1bf626..a4c1fc5e7 100644
--- a/conf/nginx.conf
+++ b/conf/nginx.conf
@@ -37,7 +37,7 @@ http {
# deny access to .htaccess files
#
- #location ~ \.ht {
+ #location ~ /\.ht {
# deny all;
#}
}
diff --git a/src/core/nginx.h b/src/core/nginx.h
index f9f1a061b..221ba38f0 100644
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,7 +8,7 @@
#define _NGINX_H_INCLUDED_
-#define NGINX_VER "nginx/0.1.37"
+#define NGINX_VER "nginx/0.1.38"
#define NGINX_VAR "NGINX"
#define NGX_NEWPID_EXT ".newbin"
diff --git a/src/core/ngx_connection.h b/src/core/ngx_connection.h
index 0224d98dd..10a5cd587 100644
--- a/src/core/ngx_connection.h
+++ b/src/core/ngx_connection.h
@@ -128,6 +128,7 @@ struct ngx_connection_s {
unsigned single_connection:1;
unsigned unexpected_eof:1;
unsigned timedout:1;
+ unsigned closed:1;
unsigned sendfile:1;
unsigned sndlowat:1;
diff --git a/src/core/ngx_palloc.c b/src/core/ngx_palloc.c
index e7c888285..9bb50f79c 100644
--- a/src/core/ngx_palloc.c
+++ b/src/core/ngx_palloc.c
@@ -15,7 +15,7 @@ ngx_create_pool(size_t size, ngx_log_t *log)
p = ngx_alloc(size, log);
if (p == NULL) {
- return NULL;
+ return NULL;
}
p->last = (u_char *) p + sizeof(ngx_pool_t);
diff --git a/src/core/ngx_string.c b/src/core/ngx_string.c
index e21a9fcdd..575e60e84 100644
--- a/src/core/ngx_string.c
+++ b/src/core/ngx_string.c
@@ -753,20 +753,62 @@ ngx_utf_length(ngx_str_t *utf)
continue;
}
- if (c < 0xC0) {
- /* invalid utf */
- return utf->len;
- }
+ if (c >= 0xc0) {
+ for (c <<= 1; c & 0x80; c <<= 1) {
+ i++;
+ }
- for (c <<= 1; c & 0x80; c <<= 1) {
- i++;
+ continue;
}
+
+ /* invalid utf */
+
+ return utf->len;
}
return len;
}
+u_char *
+ngx_utf_cpystrn(u_char *dst, u_char *src, size_t n)
+{
+ u_char c;
+
+ if (n == 0) {
+ return dst;
+ }
+
+ for ( /* void */ ; --n; dst++, src++) {
+
+ c = *src;
+ *dst = c;
+
+ if (c < 0x80) {
+ if (*dst != '\0') {
+ continue;
+ }
+
+ return dst;
+ }
+
+ if (c >= 0xc0) {
+ for (c <<= 1; c & 0x80; c <<= 1) {
+ *++dst = *++src;
+ }
+
+ continue;
+ }
+
+ /* invalid utf */
+ }
+
+ *dst = '\0';
+
+ return dst;
+}
+
+
uintptr_t
ngx_escape_uri(u_char *dst, u_char *src, size_t size, ngx_uint_t type)
{
diff --git a/src/core/ngx_string.h b/src/core/ngx_string.h
index ff21619ae..545c6cc85 100644
--- a/src/core/ngx_string.h
+++ b/src/core/ngx_string.h
@@ -97,6 +97,8 @@ 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);
size_t ngx_utf_length(ngx_str_t *utf);
+u_char * ngx_utf_cpystrn(u_char *dst, u_char *src, size_t n);
+
#define NGX_ESCAPE_URI 0
#define NGX_ESCAPE_ARGS 1
diff --git a/src/event/modules/ngx_rtsig_module.c b/src/event/modules/ngx_rtsig_module.c
index 122b0918d..838b27d70 100644
--- a/src/event/modules/ngx_rtsig_module.c
+++ b/src/event/modules/ngx_rtsig_module.c
@@ -761,7 +761,7 @@ ngx_rtsig_process_overflow(ngx_cycle_t *cycle)
}
}
- ngx_log_error(NGX_LOG_INFO, cycle->log, 0,
+ ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
"rt signal queue overflow recovered");
overflow = 0;
diff --git a/src/event/ngx_event_pipe.c b/src/event/ngx_event_pipe.c
index e18621167..74cfac2d1 100644
--- a/src/event/ngx_event_pipe.c
+++ b/src/event/ngx_event_pipe.c
@@ -182,7 +182,8 @@ static ngx_int_t ngx_event_pipe_read_upstream(ngx_event_pipe_t *p)
} else if (!p->cachable
&& p->downstream->data == p->output_ctx
- && p->downstream->write->ready)
+ && p->downstream->write->ready
+ && !p->downstream->write->delayed)
{
/*
* if the bufs are not needed to be saved in a cache and
@@ -461,7 +462,8 @@ static ngx_int_t ngx_event_pipe_write_to_downstream(ngx_event_pipe_t *p)
}
if (p->downstream->data != p->output_ctx
- || !p->downstream->write->ready)
+ || !p->downstream->write->ready
+ || p->downstream->write->delayed)
{
break;
}
diff --git a/src/http/modules/ngx_http_autoindex_module.c b/src/http/modules/ngx_http_autoindex_module.c
index e26d2947e..d12834083 100644
--- a/src/http/modules/ngx_http_autoindex_module.c
+++ b/src/http/modules/ngx_http_autoindex_module.c
@@ -35,6 +35,7 @@ typedef struct {
typedef struct {
ngx_flag_t enable;
ngx_flag_t localtime;
+ ngx_flag_t exact_size;
} ngx_http_autoindex_loc_conf_t;
@@ -67,6 +68,13 @@ static ngx_command_t ngx_http_autoindex_commands[] = {
offsetof(ngx_http_autoindex_loc_conf_t, localtime),
NULL },
+ { ngx_string("autoindex_exact_size"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_autoindex_loc_conf_t, exact_size),
+ NULL },
+
ngx_null_command
};
@@ -117,10 +125,11 @@ static u_char tail[] =
static ngx_int_t
ngx_http_autoindex_handler(ngx_http_request_t *r)
{
- u_char *last;
- size_t len;
+ u_char *last, scale;
+ off_t length;
+ size_t len, copy;
ngx_tm_t tm;
- ngx_int_t rc;
+ ngx_int_t rc, size;
ngx_uint_t i, level;
ngx_err_t err;
ngx_buf_t *b;
@@ -351,7 +360,7 @@ ngx_http_autoindex_handler(ngx_http_request_t *r)
+ NGX_HTTP_AUTOINDEX_NAME_LEN + sizeof("&gt;") - 2
+ sizeof("</a>") - 1
+ sizeof(" 28-Sep-1970 12:00 ") - 1
- + 19
+ + 20
+ 2;
}
@@ -396,14 +405,27 @@ ngx_http_autoindex_handler(ngx_http_request_t *r)
*b->last++ = '"';
*b->last++ = '>';
- b->last = ngx_cpystrn(b->last, entry[i].name.data,
- NGX_HTTP_AUTOINDEX_NAME_LEN + 1);
-
len = entry[i].utf_len;
+ if (len) {
+ if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) {
+ copy = NGX_HTTP_AUTOINDEX_NAME_LEN - 3 + 1;
+
+ } else {
+ copy = NGX_HTTP_AUTOINDEX_NAME_LEN + 1;
+ }
+
+ b->last = ngx_utf_cpystrn(b->last, entry[i].name.data, copy);
+ last = b->last;
+
+ } else {
+ b->last = ngx_cpystrn(b->last, entry[i].name.data,
+ NGX_HTTP_AUTOINDEX_NAME_LEN + 1);
+ last = b->last - 3;
+ }
+
if (len > NGX_HTTP_AUTOINDEX_NAME_LEN) {
- b->last = ngx_cpymem(b->last - 3, "..&gt;</a>",
- sizeof("..&gt;</a>") - 1);
+ b->last = ngx_cpymem(last, "..&gt;</a>", sizeof("..&gt;</a>") - 1);
} else {
if (entry[i].dir && NGX_HTTP_AUTOINDEX_NAME_LEN - len > 0) {
@@ -427,12 +449,55 @@ ngx_http_autoindex_handler(ngx_http_request_t *r)
tm.ngx_tm_hour,
tm.ngx_tm_min);
- if (entry[i].dir) {
- b->last = ngx_cpymem(b->last, " -",
- sizeof(" -") - 1);
+ if (alcf->exact_size) {
+ if (entry[i].dir) {
+ b->last = ngx_cpymem(b->last, " -",
+ sizeof(" -") - 1);
+ } else {
+ b->last = ngx_sprintf(b->last, "%19O", entry[i].size);
+ }
} else {
- b->last = ngx_sprintf(b->last, "%19O", entry[i].size);
+ if (entry[i].dir) {
+ b->last = ngx_cpymem(b->last, " -", sizeof(" -") - 1);
+
+ } else {
+ length = entry[i].size;
+
+ if (length > 1024 * 1024 * 1024 - 1) {
+ size = (ngx_int_t) (length / (1024 * 1024 * 1024));
+ if ((length % (1024 * 1024 * 1024))
+ > (1024 * 1024 * 1024 / 2 - 1))
+ {
+ size++;
+ }
+ scale = 'G';
+
+ } else if (length > 1024 * 1024 - 1) {
+ size = (ngx_int_t) (length / (1024 * 1024));
+ if ((length % (1024 * 1024)) > (1024 * 1024 / 2 - 1)) {
+ size++;
+ }
+ scale = 'M';
+
+ } else if (length > 9999) {
+ size = (ngx_int_t) (length / 1024);
+ if (length % 1024 > 511) {
+ size++;
+ }
+ scale = 'K';
+
+ } else {
+ size = (ngx_int_t) length;
+ scale = ' ';
+ }
+
+ b->last = ngx_sprintf(b->last, "%6i", size);
+
+ if (scale != ' ') {
+ *b->last++ = scale;
+ }
+ }
}
*b->last++ = CR;
@@ -559,6 +624,7 @@ ngx_http_autoindex_create_loc_conf(ngx_conf_t *cf)
conf->enable = NGX_CONF_UNSET;
conf->localtime = NGX_CONF_UNSET;
+ conf->exact_size = NGX_CONF_UNSET;
return conf;
}
@@ -572,6 +638,7 @@ ngx_http_autoindex_merge_loc_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_value(conf->enable, prev->enable, 0);
ngx_conf_merge_value(conf->localtime, prev->localtime, 0);
+ ngx_conf_merge_value(conf->exact_size, prev->exact_size, 1);
return NGX_CONF_OK;
}
diff --git a/src/http/modules/ngx_http_gzip_filter_module.c b/src/http/modules/ngx_http_gzip_filter_module.c
index f7cb92c18..596e52403 100644
--- a/src/http/modules/ngx_http_gzip_filter_module.c
+++ b/src/http/modules/ngx_http_gzip_filter_module.c
@@ -15,7 +15,7 @@ typedef struct {
ngx_flag_t enable;
ngx_flag_t no_buffer;
- ngx_array_t *types; /* array of ngx_http_gzip_type_t */
+ ngx_array_t *types; /* array of ngx_str_t */
ngx_bufs_t bufs;
@@ -29,12 +29,6 @@ typedef struct {
} ngx_http_gzip_conf_t;
-typedef struct {
- ngx_str_t name;
- ngx_uint_t enable;
-} ngx_http_gzip_type_t;
-
-
#define NGX_HTTP_GZIP_PROXIED_OFF 0x0002
#define NGX_HTTP_GZIP_PROXIED_EXPIRED 0x0004
#define NGX_HTTP_GZIP_PROXIED_NO_CACHE 0x0008
@@ -91,20 +85,18 @@ static ngx_int_t ngx_http_gzip_filter_init(ngx_cycle_t *cycle);
static void *ngx_http_gzip_create_conf(ngx_conf_t *cf);
static char *ngx_http_gzip_merge_conf(ngx_conf_t *cf,
void *parent, void *child);
-static char *ngx_http_gzip_set_types(ngx_conf_t *cf, ngx_command_t *cmd,
+static char *ngx_http_gzip_types(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
-static char *ngx_http_gzip_set_window(ngx_conf_t *cf, void *post, void *data);
-static char *ngx_http_gzip_set_hash(ngx_conf_t *cf, void *post, void *data);
+static char *ngx_http_gzip_window(ngx_conf_t *cf, void *post, void *data);
+static char *ngx_http_gzip_hash(ngx_conf_t *cf, void *post, void *data);
static ngx_conf_num_bounds_t ngx_http_gzip_comp_level_bounds = {
ngx_conf_check_num_bounds, 1, 9
};
-static ngx_conf_post_handler_pt ngx_http_gzip_set_window_p =
- ngx_http_gzip_set_window;
-static ngx_conf_post_handler_pt ngx_http_gzip_set_hash_p =
- ngx_http_gzip_set_hash;
+static ngx_conf_post_handler_pt ngx_http_gzip_window_p = ngx_http_gzip_window;
+static ngx_conf_post_handler_pt ngx_http_gzip_hash_p = ngx_http_gzip_hash;
@@ -148,7 +140,7 @@ static ngx_command_t ngx_http_gzip_filter_commands[] = {
{ ngx_string("gzip_types"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
- ngx_http_gzip_set_types,
+ ngx_http_gzip_types,
NGX_HTTP_LOC_CONF_OFFSET,
0,
NULL },
@@ -165,14 +157,14 @@ static ngx_command_t ngx_http_gzip_filter_commands[] = {
ngx_conf_set_size_slot,
NGX_HTTP_LOC_CONF_OFFSET,
offsetof(ngx_http_gzip_conf_t, wbits),
- &ngx_http_gzip_set_window_p },
+ &ngx_http_gzip_window_p },
{ ngx_string("gzip_hash"),
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_gzip_conf_t, memlevel),
- &ngx_http_gzip_set_hash_p },
+ &ngx_http_gzip_hash_p },
{ ngx_string("gzip_no_buffer"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
@@ -270,10 +262,10 @@ static ngx_http_output_body_filter_pt ngx_http_next_body_filter;
static ngx_int_t
ngx_http_gzip_header_filter(ngx_http_request_t *r)
{
- ngx_uint_t i, found;
+ ngx_str_t *type;
+ ngx_uint_t i;
ngx_http_gzip_ctx_t *ctx;
ngx_http_gzip_conf_t *conf;
- ngx_http_gzip_type_t *type;
conf = ngx_http_get_module_loc_conf(r, ngx_http_gzip_filter_module);
@@ -297,24 +289,21 @@ ngx_http_gzip_header_filter(ngx_http_request_t *r)
}
- found = 0;
type = conf->types->elts;
-
for (i = 0; i < conf->types->nelts; i++) {
- if (r->headers_out.content_type.len >= type[i].name.len
+ if (r->headers_out.content_type.len >= type[i].len
&& ngx_strncasecmp(r->headers_out.content_type.data,
- type[i].name.data, type[i].name.len) == 0)
+ type[i].data, type[i].len) == 0)
{
- found = 1;
- break;
+ goto found;
}
}
- if (!found) {
- return ngx_http_next_header_filter(r);
- }
+ return ngx_http_next_header_filter(r);
+found:
+
if (r->headers_in.via) {
if (conf->proxied & NGX_HTTP_GZIP_PROXIED_OFF) {
return ngx_http_next_header_filter(r);
@@ -1031,7 +1020,7 @@ ngx_http_gzip_merge_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_http_gzip_conf_t *prev = parent;
ngx_http_gzip_conf_t *conf = child;
- ngx_http_gzip_type_t *type;
+ ngx_str_t *type;
ngx_conf_merge_value(conf->enable, prev->enable, 0);
@@ -1051,8 +1040,7 @@ ngx_http_gzip_merge_conf(ngx_conf_t *cf, void *parent, void *child)
if (conf->types == NULL) {
if (prev->types == NULL) {
- conf->types = ngx_array_create(cf->pool, 1,
- sizeof(ngx_http_gzip_type_t));
+ conf->types = ngx_array_create(cf->pool, 1, sizeof(ngx_str_t));
if (conf->types == NULL) {
return NGX_CONF_ERROR;
}
@@ -1062,9 +1050,8 @@ ngx_http_gzip_merge_conf(ngx_conf_t *cf, void *parent, void *child)
return NGX_CONF_ERROR;
}
- type->name.len = sizeof("text/html") - 1;
- type->name.data = (u_char *) "text/html";
- type->enable = 1;
+ type->len = sizeof("text/html") - 1;
+ type->data = (u_char *) "text/html";
} else {
conf->types = prev->types;
@@ -1076,17 +1063,15 @@ ngx_http_gzip_merge_conf(ngx_conf_t *cf, void *parent, void *child)
static char *
-ngx_http_gzip_set_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+ngx_http_gzip_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_gzip_conf_t *gcf = conf;
- ngx_str_t *value;
- ngx_uint_t i;
- ngx_http_gzip_type_t *type;
+ ngx_str_t *value, *type;
+ ngx_uint_t i;
if (gcf->types == NULL) {
- gcf->types = ngx_array_create(cf->pool, 4,
- sizeof(ngx_http_gzip_type_t));
+ gcf->types = ngx_array_create(cf->pool, 4, sizeof(ngx_str_t));
if (gcf->types == NULL) {
return NGX_CONF_ERROR;
}
@@ -1096,9 +1081,8 @@ ngx_http_gzip_set_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
return NGX_CONF_ERROR;
}
- type->name.len = sizeof("text/html") - 1;
- type->name.data = (u_char *) "text/html";
- type->enable = 1;
+ type->len = sizeof("text/html") - 1;
+ type->data = (u_char *) "text/html";
}
value = cf->args->elts;
@@ -1114,14 +1098,14 @@ ngx_http_gzip_set_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
return NGX_CONF_ERROR;
}
- type->name.len = value[i].len;
+ type->len = value[i].len;
- type->name.data = ngx_palloc(cf->pool, type->name.len + 1);
- if (type->name.data == NULL) {
+ type->data = ngx_palloc(cf->pool, type->len + 1);
+ if (type->data == NULL) {
return NGX_CONF_ERROR;
}
- ngx_cpystrn(type->name.data, value[i].data, type->name.len + 1);
+ ngx_cpystrn(type->data, value[i].data, type->len + 1);
}
return NGX_CONF_OK;
@@ -1129,7 +1113,7 @@ ngx_http_gzip_set_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
static char *
-ngx_http_gzip_set_window(ngx_conf_t *cf, void *post, void *data)
+ngx_http_gzip_window(ngx_conf_t *cf, void *post, void *data)
{
int *np = data;
@@ -1153,7 +1137,7 @@ ngx_http_gzip_set_window(ngx_conf_t *cf, void *post, void *data)
static char *
-ngx_http_gzip_set_hash(ngx_conf_t *cf, void *post, void *data)
+ngx_http_gzip_hash(ngx_conf_t *cf, void *post, void *data)
{
int *np = data;
diff --git a/src/http/modules/ngx_http_proxy_module.c b/src/http/modules/ngx_http_proxy_module.c
index 83625be7b..85503d0ac 100644
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -914,7 +914,7 @@ ngx_http_proxy_parse_status_line(ngx_http_request_t *r, ngx_http_proxy_ctx_t *p)
}
break;
- /* end of request line */
+ /* end of status line */
case sw_almost_done:
p->status_end = pos - 1;
switch (ch) {
@@ -926,7 +926,7 @@ ngx_http_proxy_parse_status_line(ngx_http_request_t *r, ngx_http_proxy_ctx_t *p)
}
}
- u->header_in.pos = pos + 1;
+ u->header_in.pos = pos;
r->state = state;
return NGX_AGAIN;
@@ -1803,7 +1803,7 @@ ngx_http_proxy_pass(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
}
for (i = 0; i < plcf->peers->number; i++) {
- plcf->peers->peer[i].uri_separator = ":";
+ plcf->peers->peer[i].uri_separator = "";
}
plcf->host_header = inet_upstream.host_header;
diff --git a/src/http/modules/ngx_http_rewrite_module.c b/src/http/modules/ngx_http_rewrite_module.c
index ed7abd66a..5f73c026c 100644
--- a/src/http/modules/ngx_http_rewrite_module.c
+++ b/src/http/modules/ngx_http_rewrite_module.c
@@ -36,6 +36,8 @@ static ngx_int_t ngx_http_rewrite_init(ngx_cycle_t *cycle);
static char *ngx_http_rewrite(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
static char *ngx_http_rewrite_return(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
+static char *ngx_http_rewrite_break(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
static char *ngx_http_rewrite_if(ngx_conf_t *cf, ngx_command_t *cmd,
void *conf);
static char * ngx_http_rewrite_if_condition(ngx_conf_t *cf,
@@ -66,6 +68,14 @@ static ngx_command_t ngx_http_rewrite_commands[] = {
0,
NULL },
+ { ngx_string("break"),
+ NGX_HTTP_SRV_CONF|NGX_HTTP_SIF_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
+ |NGX_CONF_NOARGS,
+ ngx_http_rewrite_break,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
{ ngx_string("if"),
NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_BLOCK|NGX_CONF_1MORE,
ngx_http_rewrite_if,
@@ -601,6 +611,24 @@ ngx_http_rewrite_return(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
static char *
+ngx_http_rewrite_break(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_rewrite_loc_conf_t *lcf = conf;
+
+ ngx_http_script_code_pt *code;
+
+ code = ngx_http_script_start_code(cf->pool, &lcf->codes, sizeof(uintptr_t));
+ if (code == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *code = ngx_http_script_break_code;
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
ngx_http_rewrite_if(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
ngx_http_rewrite_loc_conf_t *lcf = conf;
diff --git a/src/http/modules/ngx_http_ssi_filter_module.c b/src/http/modules/ngx_http_ssi_filter_module.c
index 2992ff59d..856726a35 100644
--- a/src/http/modules/ngx_http_ssi_filter_module.c
+++ b/src/http/modules/ngx_http_ssi_filter_module.c
@@ -24,6 +24,8 @@ typedef struct {
ngx_flag_t silent_errors;
ngx_flag_t ignore_recycled_buffers;
+ ngx_array_t *types; /* array of ngx_str_t */
+
size_t min_file_chunk;
size_t value_len;
} ngx_http_ssi_conf_t;
@@ -127,6 +129,8 @@ static ngx_int_t ngx_http_ssi_endif(ngx_http_request_t *r,
static ngx_http_variable_value_t *
ngx_http_ssi_date_gmt_local_variable(ngx_http_request_t *r, uintptr_t gmt);
+static char *ngx_http_ssi_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+
static ngx_int_t ngx_http_ssi_add_variables(ngx_conf_t *cf);
static void *ngx_http_ssi_create_conf(ngx_conf_t *cf);
static char *ngx_http_ssi_merge_conf(ngx_conf_t *cf,
@@ -164,6 +168,13 @@ static ngx_command_t ngx_http_ssi_filter_commands[] = {
offsetof(ngx_http_ssi_conf_t, min_file_chunk),
NULL },
+ { ngx_string("ssi_types"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_1MORE,
+ ngx_http_ssi_types,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ 0,
+ NULL },
+
ngx_null_command
};
@@ -201,7 +212,7 @@ static ngx_int_t (*ngx_http_next_body_filter) (ngx_http_request_t *r,
static u_char ngx_http_ssi_string[] = "<!--";
static u_char ngx_http_ssi_error_string[] =
- "[an error occurred while processing the directive]";
+ "[an error occurred while processing the directive]";
static ngx_str_t ngx_http_ssi_none = ngx_string("(none)");
@@ -280,24 +291,34 @@ static ngx_http_variable_t ngx_http_ssi_vars[] = {
static ngx_int_t
ngx_http_ssi_header_filter(ngx_http_request_t *r)
{
+ ngx_uint_t i;
+ ngx_str_t *type;
ngx_http_ssi_ctx_t *ctx;
ngx_http_ssi_conf_t *conf;
conf = ngx_http_get_module_loc_conf(r, ngx_http_ssi_filter_module);
- if (!conf->enable) {
+ if (!conf->enable
+ || r->headers_out.content_type.len == 0)
+ {
return ngx_http_next_header_filter(r);
}
- /* TODO: "text/html" -> custom types */
- if (r->headers_out.content_type.len == 0
- || ngx_strncasecmp(r->headers_out.content_type.data, "text/html", 5)
- != 0)
- {
- return ngx_http_next_header_filter(r);
+ type = conf->types->elts;
+ for (i = 0; i < conf->types->nelts; i++) {
+ if (r->headers_out.content_type.len >= type[i].len
+ && ngx_strncasecmp(r->headers_out.content_type.data,
+ type[i].data, type[i].len) == 0)
+ {
+ goto found;
+ }
}
+ return ngx_http_next_header_filter(r);
+
+
+found:
ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_ssi_ctx_t));
if (ctx == NULL) {
@@ -1632,6 +1653,56 @@ ngx_http_ssi_date_gmt_local_variable(ngx_http_request_t *r, uintptr_t gmt)
}
+static char *
+ngx_http_ssi_types(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_http_ssi_conf_t *scf = conf;
+
+ ngx_str_t *value, *type;
+ ngx_uint_t i;
+
+ if (scf->types == NULL) {
+ scf->types = ngx_array_create(cf->pool, 4, sizeof(ngx_str_t));
+ if (scf->types == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ type = ngx_array_push(scf->types);
+ if (type == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ type->len = sizeof("text/html") - 1;
+ type->data = (u_char *) "text/html";
+ }
+
+ value = cf->args->elts;
+
+ for (i = 1; i < cf->args->nelts; i++) {
+
+ if (ngx_strcmp(value[i].data, "text/html") == 0) {
+ continue;
+ }
+
+ type = ngx_array_push(scf->types);
+ if (type == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ type->len = value[i].len;
+
+ type->data = ngx_palloc(cf->pool, type->len + 1);
+ if (type->data == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ngx_cpystrn(type->data, value[i].data, type->len + 1);
+ }
+
+ return NGX_CONF_OK;
+}
+
+
static ngx_int_t
ngx_http_ssi_add_variables(ngx_conf_t *cf)
{
@@ -1661,6 +1732,12 @@ ngx_http_ssi_create_conf(ngx_conf_t *cf)
return NGX_CONF_ERROR;
}
+ /*
+ * set by ngx_pcalloc():
+ *
+ * conf->types = NULL;
+ */
+
conf->enable = NGX_CONF_UNSET;
conf->silent_errors = NGX_CONF_UNSET;
conf->ignore_recycled_buffers = NGX_CONF_UNSET;
@@ -1678,6 +1755,8 @@ ngx_http_ssi_merge_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_http_ssi_conf_t *prev = parent;
ngx_http_ssi_conf_t *conf = child;
+ ngx_str_t *type;
+
ngx_conf_merge_value(conf->enable, prev->enable, 0);
ngx_conf_merge_value(conf->silent_errors, prev->silent_errors, 0);
ngx_conf_merge_value(conf->ignore_recycled_buffers,
@@ -1686,6 +1765,26 @@ ngx_http_ssi_merge_conf(ngx_conf_t *cf, void *parent, void *child)
ngx_conf_merge_size_value(conf->min_file_chunk, prev->min_file_chunk, 1024);
ngx_conf_merge_size_value(conf->value_len, prev->value_len, 256);
+ if (conf->types == NULL) {
+ if (prev->types == NULL) {
+ conf->types = ngx_array_create(cf->pool, 1, sizeof(ngx_str_t));
+ if (conf->types == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ type = ngx_array_push(conf->types);
+ if (type == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ type->len = sizeof("text/html") - 1;
+ type->data = (u_char *) "text/html";
+
+ } else {
+ conf->types = prev->types;
+ }
+ }
+
return NGX_CONF_OK;
}
diff --git a/src/http/modules/ngx_http_static_module.c b/src/http/modules/ngx_http_static_module.c
index a84a70eb6..54e624fa8 100644
--- a/src/http/modules/ngx_http_static_module.c
+++ b/src/http/modules/ngx_http_static_module.c
@@ -210,8 +210,10 @@ ngx_http_static_handler(ngx_http_request_t *r)
rc = NGX_HTTP_INTERNAL_SERVER_ERROR;
}
- ngx_log_error(level, log, err,
- ngx_open_file_n " \"%s\" failed", name.data);
+ if (rc != NGX_HTTP_NOT_FOUND || clcf->log_not_found) {
+ ngx_log_error(level, log, err,
+ ngx_open_file_n " \"%s\" failed", name.data);
+ }
return rc;
}
diff --git a/src/http/ngx_http_copy_filter_module.c b/src/http/ngx_http_copy_filter_module.c
index 5561134da..46715482a 100644
--- a/src/http/ngx_http_copy_filter_module.c
+++ b/src/http/ngx_http_copy_filter_module.c
@@ -68,13 +68,17 @@ ngx_http_copy_filter(ngx_http_request_t *r, ngx_chain_t *in)
ngx_output_chain_ctx_t *ctx;
ngx_http_copy_filter_conf_t *conf;
- if (r->connection->write->error) {
- return NGX_ERROR;
- }
-
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"copy filter: \"%V\"", &r->uri);
+ if (r->connection->closed) {
+ rc = ngx_http_next_filter(r, in);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "copy closed filter: %i \"%V\"", rc, &r->uri);
+ return rc;
+ }
+
ctx = ngx_http_get_module_ctx(r, ngx_http_copy_filter_module);
if (ctx == NULL) {
diff --git a/src/http/ngx_http_core_module.c b/src/http/ngx_http_core_module.c
index 1d399704f..ad5058512 100644
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -326,6 +326,13 @@ static ngx_command_t ngx_http_core_commands[] = {
offsetof(ngx_http_core_loc_conf_t, msie_padding),
NULL },
+ { ngx_string("log_not_found"),
+ NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_HTTP_LOC_CONF_OFFSET,
+ offsetof(ngx_http_core_loc_conf_t, log_not_found),
+ NULL },
+
{ ngx_string("error_page"),
NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_HTTP_LIF_CONF
|NGX_CONF_2MORE,
@@ -633,6 +640,8 @@ ngx_http_find_location_config(ngx_http_request_t *r)
return NGX_HTTP_MOVED_PERMANENTLY;
}
+ r->limit_rate = clcf->limit_rate;
+
if (clcf->handler) {
r->content_handler = clcf->handler;
}
@@ -862,10 +871,6 @@ ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
ngx_int_t rc;
- if (r->connection->write->error) {
- return NGX_ERROR;
- }
-
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http output filter \"%V\"", &r->uri);
@@ -873,7 +878,7 @@ ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in)
if (rc == NGX_ERROR) {
/* NGX_ERROR may be returned by any filter */
- r->connection->write->error = 1;
+ r->connection->closed = 1;
}
return rc;
@@ -1727,6 +1732,7 @@ ngx_http_core_create_loc_conf(ngx_conf_t *cf)
lcf->reset_timedout_connection = NGX_CONF_UNSET;
lcf->port_in_redirect = NGX_CONF_UNSET;
lcf->msie_padding = NGX_CONF_UNSET;
+ lcf->log_not_found = NGX_CONF_UNSET;
return lcf;
}
@@ -1841,6 +1847,7 @@ ngx_http_core_merge_loc_conf(ngx_conf_t *cf,
prev->reset_timedout_connection, 0);
ngx_conf_merge_value(conf->port_in_redirect, prev->port_in_redirect, 1);
ngx_conf_merge_value(conf->msie_padding, prev->msie_padding, 1);
+ ngx_conf_merge_value(conf->log_not_found, prev->log_not_found, 1);
if (conf->open_files == NULL) {
conf->open_files = prev->open_files;
diff --git a/src/http/ngx_http_core_module.h b/src/http/ngx_http_core_module.h
index 11640467e..1f6b185ca 100644
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -224,6 +224,7 @@ struct ngx_http_core_loc_conf_s {
ngx_flag_t reset_timedout_connection; /* reset_timedout_connection */
ngx_flag_t port_in_redirect; /* port_in_redirect */
ngx_flag_t msie_padding; /* msie_padding */
+ ngx_flag_t log_not_found; /* log_not_found */
ngx_array_t *error_pages; /* error_page */
diff --git a/src/http/ngx_http_postpone_filter_module.c b/src/http/ngx_http_postpone_filter_module.c
index 8e47816fb..d7d338f09 100644
--- a/src/http/ngx_http_postpone_filter_module.c
+++ b/src/http/ngx_http_postpone_filter_module.c
@@ -48,13 +48,18 @@ ngx_http_postpone_filter(ngx_http_request_t *r, ngx_chain_t *in)
ngx_http_request_t *mr;
ngx_http_postponed_request_t *pr, **ppr;
- if (r->connection->write->error) {
- return NGX_ERROR;
- }
-
ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
"http postpone filter \"%V\" %p", &r->uri, in);
+ if (r->connection->closed) {
+
+ if (r->postponed) {
+ r->postponed = r->postponed->next;
+ }
+
+ return NGX_ERROR;
+ }
+
if (r != r->connection->data || (r->postponed && in)) {
if (r->postponed) {
@@ -112,7 +117,7 @@ ngx_http_postpone_filter(ngx_http_request_t *r, ngx_chain_t *in)
if (rc == NGX_ERROR) {
/* NGX_ERROR may be returned by any filter */
- r->connection->write->error = 1;
+ r->connection->closed = 1;
}
return rc;
diff --git a/src/http/ngx_http_request.c b/src/http/ngx_http_request.c
index 9ced028a5..f41a891b7 100644
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -90,11 +90,6 @@ ngx_http_header_t ngx_http_headers_in[] = {
{ ngx_string("Range"), offsetof(ngx_http_headers_in_t, range),
ngx_http_process_header_line },
-#if 0
- { ngx_string("If-Range"), offsetof(ngx_http_headers_in_t, if_range),
- ngx_http_process_header_line },
-#endif
-
#if (NGX_HTTP_GZIP)
{ ngx_string("Accept-Encoding"),
offsetof(ngx_http_headers_in_t, accept_encoding),
@@ -1441,6 +1436,8 @@ ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
r->done = 1;
if (r != r->connection->data) {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http finalize non-active request: \"%V\"", &r->uri);
return;
}
@@ -1448,12 +1445,18 @@ ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
pr = r->parent;
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http parent request: \"%V\"", &pr->uri);
+
if (rc != NGX_AGAIN) {
pr->connection->data = pr;
}
if (pr->postponed) {
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http request: \"%V\" has postponed", &pr->uri);
+
if (rc != NGX_AGAIN && pr->postponed->request == r) {
pr->postponed = pr->postponed->next;
@@ -1462,9 +1465,13 @@ ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
}
}
+ ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http request: \"%V\" still has postponed",
+ &pr->uri);
+
if (pr->done) {
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
- "http wake request: \"%V\"", &pr->uri);
+ "http wake parent request: \"%V\"", &pr->uri);
pr->write_event_handler(pr);
}
@@ -1483,7 +1490,7 @@ ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
ngx_del_timer(r->connection->write);
}
- if (rc == NGX_HTTP_CLIENT_CLOSED_REQUEST || r->closed) {
+ if (r->connection->closed) {
ngx_http_close_request(r, 0);
ngx_http_close_connection(r->connection);
return;
@@ -1492,13 +1499,15 @@ ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
ngx_http_finalize_request(r, ngx_http_special_response_handler(r, rc));
return;
+ }
- } else if (rc == NGX_ERROR) {
+ if (rc == NGX_ERROR || r->connection->closed) {
ngx_http_close_request(r, 0);
ngx_http_close_connection(r->connection);
return;
+ }
- } else if (rc == NGX_AGAIN || r->out) {
+ if (rc == NGX_AGAIN || r->out) {
(void) ngx_http_set_write_handler(r);
return;
}
@@ -1553,6 +1562,10 @@ ngx_http_set_write_handler(ngx_http_request_t *r)
r->write_event_handler = ngx_http_writer;
+ if (r->connection->closed) {
+ return NGX_OK;
+ }
+
wev = r->connection->write;
if (wev->ready && wev->delayed) {
@@ -1673,6 +1686,9 @@ static ngx_int_t
ngx_http_postponed_handler(ngx_http_request_t *r)
{
ngx_int_t rc;
+#if 0
+ ngx_http_request_t *mr;
+#endif
ngx_http_postponed_request_t *pr;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
@@ -1687,20 +1703,16 @@ ngx_http_postponed_handler(ngx_http_request_t *r)
rc = ngx_http_output_filter(r, NULL);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
- "http postponed output filter: %d", rc);
+ "http postponed output filter: %d", rc);
if (rc == NGX_AGAIN) {
return rc;
}
- if (rc == NGX_ERROR) {
- /* NGX_ERROR may be returned by any filter */
- r->connection->write->error = 1;
-
- ngx_http_finalize_request(r, rc);
-
- return NGX_DONE;
- }
+ /*
+ * we treat NGX_ERROR as NGX_OK, because we need to complete
+ * all postponed requests
+ */
pr = r->postponed;
@@ -1713,7 +1725,7 @@ ngx_http_postponed_handler(ngx_http_request_t *r)
r->connection->data = r;
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
- "http postponed request \"%V\"", &r->uri);
+ "http wake child request \"%V\"", &r->uri);
r->write_event_handler(r);
@@ -1833,7 +1845,7 @@ ngx_http_read_discarded_body(ngx_http_request_t *r)
if (n == NGX_ERROR) {
- r->closed = 1;
+ r->connection->closed = 1;
/*
* if a client request body is discarded then we already set
diff --git a/src/http/ngx_http_request.h b/src/http/ngx_http_request.h
index 470f58459..d1ba774ef 100644
--- a/src/http/ngx_http_request.h
+++ b/src/http/ngx_http_request.h
@@ -310,6 +310,8 @@ struct ngx_http_request_s {
ngx_http_variable_value_t **variables;
+ size_t limit_rate;
+
/* used to learn the Apache compatible response length without a header */
size_t header_size;
@@ -361,7 +363,6 @@ struct ngx_http_request_s {
unsigned keepalive:1;
unsigned lingering_close:1;
unsigned internal:1;
- unsigned closed:1;
unsigned done:1;
unsigned utf8:1;
diff --git a/src/http/ngx_http_request_body.c b/src/http/ngx_http_request_body.c
index 42edfef52..eb2e6a570 100644
--- a/src/http/ngx_http_request_body.c
+++ b/src/http/ngx_http_request_body.c
@@ -236,7 +236,7 @@ ngx_http_do_read_client_request_body(ngx_http_request_t *r)
}
if (n == 0 || n == NGX_ERROR) {
- r->closed = 1;
+ c->closed = 1;
return NGX_HTTP_BAD_REQUEST;
}
diff --git a/src/http/ngx_http_script.c b/src/http/ngx_http_script.c
index 89256b355..716a6f054 100644
--- a/src/http/ngx_http_script.c
+++ b/src/http/ngx_http_script.c
@@ -558,6 +558,7 @@ ngx_http_script_regex_start_code(ngx_http_script_engine_t *e)
if (code->break_cycle) {
r->valid_location = 0;
+ r->uri_changed = 0;
} else {
r->uri_changed = 1;
@@ -713,6 +714,15 @@ ngx_http_script_return_code(ngx_http_script_engine_t *e)
void
+ngx_http_script_break_code(ngx_http_script_engine_t *e)
+{
+ e->request->uri_changed = 0;
+
+ e->ip = ngx_http_script_exit;
+}
+
+
+void
ngx_http_script_if_code(ngx_http_script_engine_t *e)
{
ngx_http_script_if_code_t *code;
diff --git a/src/http/ngx_http_script.h b/src/http/ngx_http_script.h
index eb6f17ea9..21625427f 100644
--- a/src/http/ngx_http_script.h
+++ b/src/http/ngx_http_script.h
@@ -166,6 +166,7 @@ void ngx_http_script_regex_start_code(ngx_http_script_engine_t *e);
void ngx_http_script_regex_end_code(ngx_http_script_engine_t *e);
#endif
void ngx_http_script_return_code(ngx_http_script_engine_t *e);
+void ngx_http_script_break_code(ngx_http_script_engine_t *e);
void ngx_http_script_if_code(ngx_http_script_engine_t *e);
void ngx_http_script_complex_value_code(ngx_http_script_engine_t *e);
void ngx_http_script_value_code(ngx_http_script_engine_t *e);
diff --git a/src/http/ngx_http_special_response.c b/src/http/ngx_http_special_response.c
index 7a341599a..14d4c8022 100644
--- a/src/http/ngx_http_special_response.c
+++ b/src/http/ngx_http_special_response.c
@@ -244,6 +244,9 @@ ngx_http_special_response_handler(ngx_http_request_t *r, ngx_int_t error)
ngx_http_err_page_t *err_page;
ngx_http_core_loc_conf_t *clcf;
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+ "http special response: %d, \"%V\"", error, &r->uri);
+
rc = ngx_http_discard_body(r);
if (rc == NGX_HTTP_INTERNAL_SERVER_ERROR) {
diff --git a/src/http/ngx_http_upstream.c b/src/http/ngx_http_upstream.c
index d7ee08f72..8571b869b 100644
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -39,6 +39,8 @@ static ngx_int_t
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_upstream_ignore_header_line(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
+static ngx_int_t ngx_http_upstream_process_limit_rate(ngx_http_request_t *r,
+ ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t ngx_http_upstream_copy_header_line(ngx_http_request_t *r,
ngx_table_elt_t *h, ngx_uint_t offset);
static ngx_int_t
@@ -143,6 +145,10 @@ ngx_http_upstream_header_t ngx_http_upstream_headers_in[] = {
offsetof(ngx_http_upstream_headers_in_t, x_accel_redirect),
ngx_http_upstream_ignore_header_line, 0, 0 },
+ { ngx_string("X-Accel-Limit-Rate"),
+ ngx_http_upstream_process_limit_rate, 0,
+ ngx_http_upstream_ignore_header_line, 0, 0 },
+
#if (NGX_HTTP_GZIP)
{ ngx_string("Content-Encoding"),
ngx_http_upstream_process_header_line,
@@ -299,12 +305,19 @@ ngx_http_upstream_check_broken_connection(ngx_http_request_t *r,
ngx_connection_t *c;
ngx_http_upstream_t *u;
- ngx_log_debug1(NGX_LOG_DEBUG_HTTP, ev->log, 0,
- "http upstream check client, write event:%d", ev->write);
+ ngx_log_debug2(NGX_LOG_DEBUG_HTTP, ev->log, 0,
+ "http upstream check client, write event:%d, \"%V\"",
+ ev->write, &r->uri);
c = r->connection;
u = r->upstream;
+ if (c->closed) {
+ ngx_http_upstream_finalize_request(r, u,
+ NGX_HTTP_CLIENT_CLOSED_REQUEST);
+ return;
+ }
+
if (u->peer.connection == NULL) {
return;
}
@@ -318,6 +331,7 @@ ngx_http_upstream_check_broken_connection(ngx_http_request_t *r,
}
ev->eof = 1;
+ c->closed = 1;
if (ev->kq_errno) {
ev->error = 1;
@@ -325,9 +339,8 @@ ngx_http_upstream_check_broken_connection(ngx_http_request_t *r,
if (!u->cachable && u->peer.connection) {
ngx_log_error(NGX_LOG_INFO, ev->log, ev->kq_errno,
- "kevent() reported that client closed "
- "prematurely connection, "
- "so upstream connection is closed too");
+ "kevent() reported that client closed prematurely "
+ "connection, so upstream connection is closed too");
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_CLIENT_CLOSED_REQUEST);
return;
@@ -374,6 +387,7 @@ ngx_http_upstream_check_broken_connection(ngx_http_request_t *r,
}
ev->eof = 1;
+ c->closed = 1;
if (n == -1) {
if (err == NGX_EAGAIN) {
@@ -924,6 +938,8 @@ ngx_http_upstream_process_header(ngx_event_t *rev)
}
}
+ r->headers_out.status_line.len = 0;
+
ngx_http_internal_redirect(r,
&r->upstream->headers_in.x_accel_redirect->value,
NULL);
@@ -1155,9 +1171,33 @@ ngx_http_upstream_process_body(ngx_event_t *ev)
if (ev->timedout) {
if (ev->write) {
- p->downstream_error = 1;
- ngx_log_error(NGX_LOG_ERR, c->log, NGX_ETIMEDOUT,
- "client timed out");
+ if (ev->delayed) {
+
+ ev->timedout = 0;
+ ev->delayed = 0;
+
+ if (!ev->ready) {
+ ngx_add_timer(ev, p->send_timeout);
+
+ if (ngx_handle_write_event(ev, p->send_lowat) == NGX_ERROR)
+ {
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ return;
+ }
+
+ if (ngx_event_pipe(p, ev->write) == NGX_ABORT) {
+ ngx_http_upstream_finalize_request(r, u, 0);
+ return;
+ }
+
+ } else {
+ p->downstream_error = 1;
+ ngx_log_error(NGX_LOG_ERR, c->log, NGX_ETIMEDOUT,
+ "client timed out");
+ }
} else {
p->upstream_error = 1;
@@ -1166,6 +1206,17 @@ ngx_http_upstream_process_body(ngx_event_t *ev)
}
} else {
+ if (ev->write && ev->delayed) {
+ ngx_log_debug0(NGX_LOG_DEBUG_HTTP, c->log, 0,
+ "http downstream delayed");
+
+ if (ngx_handle_write_event(ev, p->send_lowat) == NGX_ERROR) {
+ return;
+ }
+
+ return;
+ }
+
if (ngx_event_pipe(p, ev->write) == NGX_ABORT) {
ngx_http_upstream_finalize_request(r, u, 0);
return;
@@ -1281,6 +1332,7 @@ ngx_http_upstream_next(ngx_http_request_t *r, ngx_http_upstream_t *u,
}
if (r->connection->write->eof) {
+ r->connection->closed = 1;
ngx_http_upstream_finalize_request(r, u,
NGX_HTTP_CLIENT_CLOSED_REQUEST);
return;
@@ -1432,6 +1484,24 @@ ngx_http_upstream_ignore_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,
static ngx_int_t
+ngx_http_upstream_process_limit_rate(ngx_http_request_t *r, ngx_table_elt_t *h,
+ ngx_uint_t offset)
+{
+ ngx_int_t n;
+
+ r->upstream->headers_in.x_accel_limit_rate = h;
+
+ n = ngx_atoi(h->value.data, h->value.len);
+
+ if (n != NGX_ERROR) {
+ r->limit_rate = (size_t) n;
+ }
+
+ return NGX_OK;
+}
+
+
+static ngx_int_t
ngx_http_upstream_copy_header_line(ngx_http_request_t *r, ngx_table_elt_t *h,
ngx_uint_t offset)
{
diff --git a/src/http/ngx_http_upstream.h b/src/http/ngx_http_upstream.h
index 00a64aca6..428253c42 100644
--- a/src/http/ngx_http_upstream.h
+++ b/src/http/ngx_http_upstream.h
@@ -48,6 +48,7 @@ typedef struct {
ngx_msec_t connect_timeout;
ngx_msec_t send_timeout;
ngx_msec_t read_timeout;
+ ngx_msec_t timeout;
size_t send_lowat;
size_t header_buffer_size;
@@ -103,6 +104,7 @@ typedef struct {
ngx_table_elt_t *etag;
ngx_table_elt_t *x_accel_expires;
ngx_table_elt_t *x_accel_redirect;
+ ngx_table_elt_t *x_accel_limit_rate;
ngx_table_elt_t *content_type;
ngx_table_elt_t *content_length;
@@ -120,8 +122,6 @@ typedef struct {
struct ngx_http_upstream_s {
- ngx_http_request_t *request;
-
ngx_peer_connection_t peer;
ngx_event_pipe_t pipe;
@@ -146,6 +146,8 @@ struct ngx_http_upstream_s {
ngx_int_t (*rewrite_redirect)(ngx_http_request_t *r,
ngx_table_elt_t *h, size_t prefix);
+ ngx_msec_t timeout;
+
ngx_uint_t method;
ngx_http_log_handler_pt saved_log_handler;
diff --git a/src/http/ngx_http_variables.c b/src/http/ngx_http_variables.c
index d66f0dc6c..2a5cf7dcd 100644
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -706,7 +706,9 @@ ngx_http_variables_init_vars(ngx_conf_t *cf)
{
v[i].handler = av[n].handler;
v[i].data = av[n].data;
- v[i].flags = av[n].flags | NGX_HTTP_VAR_INDEXED;
+
+ av[n].flags |= NGX_HTTP_VAR_INDEXED;
+ v[i].flags = av[n].flags;
goto next;
}
diff --git a/src/http/ngx_http_write_filter_module.c b/src/http/ngx_http_write_filter_module.c
index b872f3dd1..af95c99cd 100644
--- a/src/http/ngx_http_write_filter_module.c
+++ b/src/http/ngx_http_write_filter_module.c
@@ -47,6 +47,12 @@ ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in)
ngx_connection_t *c;
ngx_http_core_loc_conf_t *clcf;
+ c = r->connection;
+
+ if (c->closed) {
+ return NGX_ERROR;
+ }
+
size = 0;
flush = 0;
last = 0;
@@ -151,8 +157,6 @@ ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in)
*ll = NULL;
- c = r->connection;
-
ngx_log_debug3(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http write filter: l:%d f:%d s:%O", last, flush, size);
@@ -197,19 +201,20 @@ ngx_http_write_filter(ngx_http_request_t *r, ngx_chain_t *in)
sent = c->sent;
- chain = c->send_chain(c, r->out, clcf->limit_rate);
+ chain = c->send_chain(c, r->out, r->limit_rate);
ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
"http write filter %p", chain);
- if (clcf->limit_rate) {
+ if (r->limit_rate) {
sent = c->sent - sent;
c->write->delayed = 1;
ngx_add_timer(r->connection->write,
- (ngx_msec_t) (sent * 1000 / clcf->limit_rate));
+ (ngx_msec_t) (sent * 1000 / r->limit_rate));
}
if (chain == NGX_CHAIN_ERROR) {
+ c->closed = 1;
return NGX_ERROR;
}
diff --git a/src/imap/ngx_imap.c b/src/imap/ngx_imap.c
new file mode 100644
index 000000000..aa28b2d6a
--- /dev/null
+++ b/src/imap/ngx_imap.c
@@ -0,0 +1,189 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_imap.h>
+
+
+static char *ngx_imap_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+
+
+ngx_uint_t ngx_imap_max_module;
+
+
+static ngx_command_t ngx_imap_commands[] = {
+
+ { ngx_string("imap"),
+ NGX_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
+ ngx_imap_block,
+ 0,
+ 0,
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_core_module_t ngx_imap_module_ctx = {
+ ngx_string("imap"),
+ NULL,
+ NULL
+};
+
+
+ngx_module_t ngx_imap_module = {
+ NGX_MODULE_V1,
+ &ngx_imap_module_ctx, /* module context */
+ ngx_imap_commands, /* module directives */
+ NGX_CORE_MODULE, /* module type */
+ NULL, /* init module */
+ NULL /* init process */
+};
+
+
+static char *
+ngx_imap_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *rv;
+ ngx_uint_t m, mi, s;
+ ngx_conf_t pcf;
+ ngx_imap_module_t *module;
+ ngx_imap_conf_ctx_t *ctx;
+ ngx_imap_core_srv_conf_t **cscfp;
+ ngx_imap_core_main_conf_t *cmcf;
+
+ /* the main imap context */
+
+ ctx = ngx_pcalloc(cf->pool, sizeof(ngx_imap_conf_ctx_t));
+ if (ctx == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *(ngx_imap_conf_ctx_t **) conf = ctx;
+
+ /* count the number of the http modules and set up their indices */
+
+ ngx_imap_max_module = 0;
+ for (m = 0; ngx_modules[m]; m++) {
+ if (ngx_modules[m]->type != NGX_IMAP_MODULE) {
+ continue;
+ }
+
+ ngx_modules[m]->ctx_index = ngx_imap_max_module++;
+ }
+
+
+ /* the imap main_conf context, it is the same in the all imap contexts */
+
+ ctx->main_conf = ngx_pcalloc(cf->pool,
+ sizeof(void *) * ngx_imap_max_module);
+ if (ctx->main_conf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+
+ /*
+ * the imap null srv_conf context, it is used to merge
+ * the server{}s' srv_conf's
+ */
+
+ ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_imap_max_module);
+ if (ctx->srv_conf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+
+ /*
+ * create the main_conf's, the null srv_conf's, and the null loc_conf's
+ * of the all imap modules
+ */
+
+ for (m = 0; ngx_modules[m]; m++) {
+ if (ngx_modules[m]->type != NGX_IMAP_MODULE) {
+ continue;
+ }
+
+ module = ngx_modules[m]->ctx;
+ mi = ngx_modules[m]->ctx_index;
+
+ if (module->create_main_conf) {
+ ctx->main_conf[mi] = module->create_main_conf(cf);
+ if (ctx->main_conf[mi] == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+
+ if (module->create_srv_conf) {
+ ctx->srv_conf[mi] = module->create_srv_conf(cf);
+ if (ctx->srv_conf[mi] == NULL) {
+ return NGX_CONF_ERROR;
+ }
+ }
+ }
+
+
+ /* parse inside the imap{} block */
+
+ pcf = *cf;
+ cf->ctx = ctx;
+
+ cf->module_type = NGX_IMAP_MODULE;
+ cf->cmd_type = NGX_IMAP_MAIN_CONF;
+ rv = ngx_conf_parse(cf, NULL);
+
+ if (rv != NGX_CONF_OK) {
+ *cf = pcf;
+ return rv;
+ }
+
+
+ /* init imap{} main_conf's, merge the server{}s' srv_conf's */
+
+ cmcf = ctx->main_conf[ngx_imap_core_module.ctx_index];
+ cscfp = cmcf->servers.elts;
+
+ for (m = 0; ngx_modules[m]; m++) {
+ if (ngx_modules[m]->type != NGX_IMAP_MODULE) {
+ continue;
+ }
+
+ module = ngx_modules[m]->ctx;
+ mi = ngx_modules[m]->ctx_index;
+
+ /* init imap{} main_conf's */
+
+ if (module->init_main_conf) {
+ rv = module->init_main_conf(cf, ctx->main_conf[mi]);
+ if (rv != NGX_CONF_OK) {
+ *cf = pcf;
+ return rv;
+ }
+ }
+
+ for (s = 0; s < cmcf->servers.nelts; s++) {
+
+ /* merge the server{}s' srv_conf's */
+
+ if (module->merge_srv_conf) {
+ rv = module->merge_srv_conf(cf,
+ ctx->srv_conf[mi],
+ cscfp[s]->ctx->srv_conf[mi]);
+ if (rv != NGX_CONF_OK) {
+ *cf = pcf;
+ return rv;
+ }
+ }
+ }
+ }
+
+ /* imap{}'s cf->ctx was needed while the configuration merging */
+
+ *cf = pcf;
+
+ return NGX_CONF_OK;
+}
diff --git a/src/imap/ngx_imap.h b/src/imap/ngx_imap.h
new file mode 100644
index 000000000..8a506db64
--- /dev/null
+++ b/src/imap/ngx_imap.h
@@ -0,0 +1,189 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_IMAP_H_INCLUDED_
+#define _NGX_IMAP_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_event_connect.h>
+
+
+typedef struct {
+ void **main_conf;
+ void **srv_conf;
+} ngx_imap_conf_ctx_t;
+
+
+typedef struct {
+ ngx_array_t servers; /* ngx_imap_core_srv_conf_t */
+} ngx_imap_core_main_conf_t;
+
+
+#define NGX_IMAP_POP3_PROTOCOL 0
+#define NGX_IMAP_IMAP_PROTOCOL 1
+
+typedef struct {
+ ngx_msec_t timeout;
+
+ size_t imap_client_buffer_size;
+ size_t proxy_buffer_size;
+
+ ngx_uint_t protocol;
+
+ ngx_buf_t *pop3_capability;
+ ngx_buf_t *imap_capability;
+
+ ngx_array_t pop3_capabilities;
+ ngx_array_t imap_capabilities;
+
+ /* server ctx */
+ ngx_imap_conf_ctx_t *ctx;
+} ngx_imap_core_srv_conf_t;
+
+
+typedef struct {
+ void *(*create_main_conf)(ngx_conf_t *cf);
+ char *(*init_main_conf)(ngx_conf_t *cf, void *conf);
+
+ void *(*create_srv_conf)(ngx_conf_t *cf);
+ char *(*merge_srv_conf)(ngx_conf_t *cf, void *prev, void *conf);
+} ngx_imap_module_t;
+
+
+typedef enum {
+ ngx_imap_start = 0,
+ ngx_imap_login,
+ ngx_imap_user,
+ ngx_imap_passwd,
+} ngx_imap_state_e;
+
+
+typedef enum {
+ ngx_pop3_start = 0,
+ ngx_pop3_user,
+ ngx_pop3_passwd
+} ngx_po3_state_e;
+
+
+typedef struct {
+ ngx_peer_connection_t upstream;
+ ngx_buf_t *buffer;
+} ngx_imap_proxy_ctx_t;
+
+
+typedef struct {
+ uint32_t signature; /* "IMAP" */
+
+ ngx_connection_t *connection;
+
+ ngx_buf_t *buffer;
+ ngx_str_t out;
+
+ void **ctx;
+ void **main_conf;
+ void **srv_conf;
+
+ ngx_imap_proxy_ctx_t *proxy;
+
+ ngx_uint_t imap_state;
+
+ unsigned protocol:1;
+ unsigned quoted:1;
+
+ ngx_str_t login;
+ ngx_str_t passwd;
+
+ ngx_str_t tag;
+
+ ngx_uint_t command;
+ ngx_array_t args;
+
+ ngx_uint_t login_attempt;
+
+ /* used to parse IMAP/POP3 command */
+
+ ngx_uint_t state;
+ u_char *cmd_start;
+ u_char *arg_start;
+ u_char *arg_end;
+ ngx_uint_t literal_len;
+} ngx_imap_session_t;
+
+
+#define NGX_POP3_USER 1
+#define NGX_POP3_PASS 2
+#define NGX_POP3_CAPA 3
+#define NGX_POP3_QUIT 4
+#define NGX_POP3_NOOP 5
+#define NGX_POP3_APOP 6
+#define NGX_POP3_STAT 7
+#define NGX_POP3_LIST 8
+#define NGX_POP3_RETR 9
+#define NGX_POP3_DELE 10
+#define NGX_POP3_RSET 11
+#define NGX_POP3_TOP 12
+#define NGX_POP3_UIDL 13
+
+
+#define NGX_IMAP_LOGIN 1
+#define NGX_IMAP_LOGOUT 2
+#define NGX_IMAP_CAPABILITY 3
+#define NGX_IMAP_NOOP 4
+
+#define NGX_IMAP_NEXT 5
+
+
+#define NGX_IMAP_PARSE_INVALID_COMMAND 20
+
+
+#define NGX_IMAP_PROXY_INVALID 10
+#define NGX_IMAP_PROXY_ERROR 11
+
+
+#define NGX_IMAP_MODULE 0x50414D49 /* "IMAP" */
+
+#define NGX_IMAP_MAIN_CONF 0x02000000
+#define NGX_IMAP_SRV_CONF 0x04000000
+
+
+#define NGX_IMAP_MAIN_CONF_OFFSET offsetof(ngx_imap_conf_ctx_t, main_conf)
+#define NGX_IMAP_SRV_CONF_OFFSET offsetof(ngx_imap_conf_ctx_t, srv_conf)
+
+
+#define ngx_imap_get_module_ctx(s, module) (s)->ctx[module.ctx_index]
+#define ngx_imap_set_ctx(s, c, module) s->ctx[module.ctx_index] = c;
+#define ngx_imap_delete_ctx(s, module) s->ctx[module.ctx_index] = NULL;
+
+
+#define ngx_imap_get_module_main_conf(s, module) \
+ (s)->main_conf[module.ctx_index]
+#define ngx_imap_get_module_srv_conf(s, module) (s)->srv_conf[module.ctx_index]
+
+
+void ngx_imap_init_connection(ngx_connection_t *c);
+void ngx_imap_auth_state(ngx_event_t *rev);
+void ngx_pop3_auth_state(ngx_event_t *rev);
+void ngx_imap_close_connection(ngx_connection_t *c);
+void ngx_imap_session_internal_server_error(ngx_imap_session_t *s);
+
+ngx_int_t ngx_imap_parse_command(ngx_imap_session_t *s);
+ngx_int_t ngx_pop3_parse_command(ngx_imap_session_t *s);
+
+
+/* STUB */
+void ngx_imap_proxy_init(ngx_imap_session_t *s, ngx_peers_t *peers);
+void ngx_imap_auth_http_init(ngx_imap_session_t *s);
+/**/
+
+
+extern ngx_uint_t ngx_imap_max_module;
+extern ngx_module_t ngx_imap_core_module;
+
+
+#endif /* _NGX_IMAP_H_INCLUDED_ */
diff --git a/src/imap/ngx_imap_auth_http_module.c b/src/imap/ngx_imap_auth_http_module.c
new file mode 100644
index 000000000..e66593a4a
--- /dev/null
+++ b/src/imap/ngx_imap_auth_http_module.c
@@ -0,0 +1,1067 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_event_connect.h>
+#include <ngx_imap.h>
+
+
+typedef struct {
+ ngx_peers_t *peers;
+
+ ngx_msec_t timeout;
+
+ ngx_str_t host_header;
+ ngx_str_t uri;
+} ngx_imap_auth_http_conf_t;
+
+
+typedef struct ngx_imap_auth_http_ctx_s ngx_imap_auth_http_ctx_t;
+
+typedef void (*ngx_imap_auth_http_handler_pt)(ngx_imap_session_t *s,
+ ngx_imap_auth_http_ctx_t *ctx);
+
+struct ngx_imap_auth_http_ctx_s {
+ ngx_buf_t *request;
+ ngx_buf_t *response;
+ ngx_peer_connection_t peer;
+
+ ngx_imap_auth_http_handler_pt handler;
+
+ ngx_uint_t state;
+ ngx_uint_t hash; /* no needed ? */
+
+ u_char *header_name_start;
+ u_char *header_name_end;
+ u_char *header_start;
+ u_char *header_end;
+
+ ngx_str_t addr;
+ ngx_str_t port;
+ ngx_str_t err;
+
+ ngx_msec_t sleep;
+
+ ngx_peers_t *peers;
+};
+
+
+static void ngx_imap_auth_http_write_handler(ngx_event_t *wev);
+static void ngx_imap_auth_http_read_handler(ngx_event_t *rev);
+static void ngx_imap_auth_http_ignore_status_line(ngx_imap_session_t *s,
+ ngx_imap_auth_http_ctx_t *ctx);
+static void ngx_imap_auth_http_process_headers(ngx_imap_session_t *s,
+ ngx_imap_auth_http_ctx_t *ctx);
+static void ngx_imap_auth_sleep_handler(ngx_event_t *rev);
+static ngx_int_t ngx_imap_auth_http_parse_header_line(ngx_imap_session_t *s,
+ ngx_imap_auth_http_ctx_t *ctx);
+static void ngx_imap_auth_http_block_read(ngx_event_t *rev);
+static void ngx_imap_auth_http_dummy_handler(ngx_event_t *ev);
+static ngx_buf_t *ngx_imap_auth_http_create_request(ngx_imap_session_t *s,
+ ngx_imap_auth_http_conf_t *ahcf);
+
+static void *ngx_imap_auth_http_create_conf(ngx_conf_t *cf);
+static char *ngx_imap_auth_http_merge_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+static char *ngx_imap_auth_http(ngx_conf_t *cf, ngx_command_t *cmd, void *conf);
+
+
+static ngx_command_t ngx_imap_auth_http_commands[] = {
+
+ { ngx_string("auth_http"),
+ NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_imap_auth_http,
+ NGX_IMAP_SRV_CONF_OFFSET,
+ 0,
+ NULL },
+
+ { ngx_string("auth_http_timeout"),
+ NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_IMAP_SRV_CONF_OFFSET,
+ offsetof(ngx_imap_auth_http_conf_t, timeout),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_imap_module_t ngx_imap_auth_http_module_ctx = {
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ ngx_imap_auth_http_create_conf, /* create server configuration */
+ ngx_imap_auth_http_merge_conf /* merge server configuration */
+};
+
+
+ngx_module_t ngx_imap_auth_http_module = {
+ NGX_MODULE_V1,
+ &ngx_imap_auth_http_module_ctx, /* module context */
+ ngx_imap_auth_http_commands, /* module directives */
+ NGX_IMAP_MODULE, /* module type */
+ NULL, /* init module */
+ NULL /* init process */
+};
+
+
+static char *ngx_imap_auth_http_protocol[] = { "pop3", "imap" };
+
+
+void
+ngx_imap_auth_http_init(ngx_imap_session_t *s)
+{
+ ngx_int_t rc;
+ ngx_imap_auth_http_ctx_t *ctx;
+ ngx_imap_auth_http_conf_t *ahcf;
+
+ ctx = ngx_pcalloc(s->connection->pool, sizeof(ngx_imap_auth_http_ctx_t));
+ if (ctx == NULL) {
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+
+ ahcf = ngx_imap_get_module_srv_conf(s, ngx_imap_auth_http_module);
+
+ ctx->request = ngx_imap_auth_http_create_request(s, ahcf);
+ if (ctx->request == NULL) {
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+
+ ngx_imap_set_ctx(s, ctx, ngx_imap_auth_http_module);
+
+ ctx->peer.peers = ahcf->peers;
+ ctx->peer.log = s->connection->log;
+ ctx->peer.log_error = NGX_ERROR_ERR;
+
+ rc = ngx_event_connect_peer(&ctx->peer);
+
+ if (rc == NGX_ERROR) {
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+
+ ctx->peer.connection->data = s;
+ ctx->peer.connection->pool = s->connection->pool;
+
+ s->connection->read->handler = ngx_imap_auth_http_block_read;
+ ctx->peer.connection->read->handler = ngx_imap_auth_http_read_handler;
+ ctx->peer.connection->write->handler = ngx_imap_auth_http_write_handler;
+
+ ctx->handler = ngx_imap_auth_http_ignore_status_line;
+
+ if (rc == NGX_OK) {
+ ngx_imap_auth_http_write_handler(ctx->peer.connection->write);
+ return;
+ }
+
+ ngx_add_timer(ctx->peer.connection->read, ahcf->timeout);
+ ngx_add_timer(ctx->peer.connection->write, ahcf->timeout);
+}
+
+
+static void
+ngx_imap_auth_http_write_handler(ngx_event_t *wev)
+{
+ ssize_t n, size;
+ ngx_connection_t *c;
+ ngx_imap_session_t *s;
+ ngx_imap_auth_http_ctx_t *ctx;
+ ngx_imap_auth_http_conf_t *ahcf;
+
+ c = wev->data;
+ s = c->data;
+
+ ctx = ngx_imap_get_module_ctx(s, ngx_imap_auth_http_module);
+
+ ngx_log_debug0(NGX_LOG_DEBUG_IMAP, wev->log, 0,
+ "imap auth http write handler");
+
+ if (wev->timedout) {
+ ngx_log_error(NGX_LOG_ERR, wev->log, NGX_ETIMEDOUT,
+ "auth http server timed out");
+ ngx_close_connection(ctx->peer.connection);
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+
+ size = ctx->request->last - ctx->request->pos;
+
+ n = ngx_send(c, ctx->request->pos, size);
+
+ if (n == NGX_ERROR) {
+ ngx_close_connection(ctx->peer.connection);
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+
+ if (n > 0) {
+ ctx->request->pos += n;
+
+ if (n == size) {
+ wev->handler = ngx_imap_auth_http_dummy_handler;
+
+ if (wev->timer_set) {
+ ngx_del_timer(wev);
+ }
+
+ return;
+ }
+ }
+
+ if (!wev->timer_set) {
+ ahcf = ngx_imap_get_module_srv_conf(s, ngx_imap_auth_http_module);
+ ngx_add_timer(wev, ahcf->timeout);
+ }
+}
+
+
+static void
+ngx_imap_auth_http_read_handler(ngx_event_t *rev)
+{
+ ssize_t n, size;
+ ngx_connection_t *c;
+ ngx_imap_session_t *s;
+ ngx_imap_auth_http_ctx_t *ctx;
+
+ c = rev->data;
+ s = c->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0,
+ "imap auth http read handler");
+
+ ctx = ngx_imap_get_module_ctx(s, ngx_imap_auth_http_module);
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_ERR, rev->log, NGX_ETIMEDOUT,
+ "auth http server timed out");
+ ngx_close_connection(ctx->peer.connection);
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+
+ if (ctx->response == NULL) {
+ ctx->response = ngx_create_temp_buf(s->connection->pool, 1024);
+ if (ctx->response == NULL) {
+ ngx_close_connection(ctx->peer.connection);
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+ }
+
+ size = ctx->response->end - ctx->response->last;
+
+ n = ngx_recv(c, ctx->response->pos, size);
+
+ if (n > 0) {
+ ctx->response->last += n;
+
+ ctx->handler(s, ctx);
+ return;
+ }
+
+ if (n == NGX_AGAIN) {
+ return;
+ }
+
+ ngx_close_connection(ctx->peer.connection);
+ ngx_imap_session_internal_server_error(s);
+}
+
+
+static void
+ngx_imap_auth_http_ignore_status_line(ngx_imap_session_t *s,
+ ngx_imap_auth_http_ctx_t *ctx)
+{
+ u_char *p, ch;
+ enum {
+ sw_start = 0,
+ sw_H,
+ sw_HT,
+ sw_HTT,
+ sw_HTTP,
+ sw_skip,
+ sw_almost_done
+ } state;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_IMAP, s->connection->log, 0,
+ "imap auth http process status line");
+
+ state = ctx->state;
+
+ for (p = ctx->response->pos; p < ctx->response->last; p++) {
+ ch = *p;
+
+ switch (state) {
+
+ /* "HTTP/" */
+ case sw_start:
+ if (ch == 'H') {
+ state = sw_H;
+ break;
+ }
+ goto next;
+
+ case sw_H:
+ if (ch == 'T') {
+ state = sw_HT;
+ break;
+ }
+ goto next;
+
+ case sw_HT:
+ if (ch == 'T') {
+ state = sw_HTT;
+ break;
+ }
+ goto next;
+
+ case sw_HTT:
+ if (ch == 'P') {
+ state = sw_HTTP;
+ break;
+ }
+ goto next;
+
+ case sw_HTTP:
+ if (ch == '/') {
+ state = sw_skip;
+ break;
+ }
+ goto next;
+
+ /* any text until end of line */
+ case sw_skip:
+ switch (ch) {
+ case CR:
+ state = sw_almost_done;
+
+ break;
+ case LF:
+ goto done;
+ }
+ break;
+
+ /* end of status line */
+ case sw_almost_done:
+ if (ch == LF) {
+ goto done;
+ }
+
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "auth http server sent invalid response");
+ ngx_close_connection(ctx->peer.connection);
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+ }
+
+ ctx->response->pos = p;
+ ctx->state = state;
+
+ return;
+
+next:
+
+ p = ctx->response->start - 1;
+
+done:
+
+ ctx->response->pos = p + 1;
+ ctx->state = 0;
+ ctx->handler = ngx_imap_auth_http_process_headers;
+ ctx->handler(s, ctx);
+}
+
+
+static void
+ngx_imap_auth_http_process_headers(ngx_imap_session_t *s,
+ ngx_imap_auth_http_ctx_t *ctx)
+{
+ u_char *p;
+ size_t len, size;
+ ngx_int_t rc, port, n;
+ struct sockaddr_in *sin;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_IMAP, s->connection->log, 0,
+ "imap auth http process headers");
+
+ for ( ;; ) {
+ rc = ngx_imap_auth_http_parse_header_line(s, ctx);
+
+ if (rc == NGX_OK) {
+
+#if (NGX_DEBUG)
+ {
+ ngx_str_t key, value;
+
+ key.len = ctx->header_name_end - ctx->header_name_start;
+ key.data = ctx->header_name_start;
+ value.len = ctx->header_end - ctx->header_start;
+ value.data = ctx->header_start;
+
+ ngx_log_debug2(NGX_LOG_DEBUG_IMAP, s->connection->log, 0,
+ "auth http header: \"%V: %V\"",
+ &key, &value);
+ }
+#endif
+
+ len = ctx->header_name_end - ctx->header_name_start;
+
+ if (len == sizeof("Auth-Status") - 1
+ && ngx_strncasecmp(ctx->header_name_start, "Auth-Status",
+ sizeof("Auth-Status") - 1) == 0)
+ {
+ len = ctx->header_end - ctx->header_start;
+
+ if (len == 2
+ && ctx->header_start[0] == 'O'
+ && ctx->header_start[1] == 'K')
+ {
+ continue;
+ }
+
+ if (s->protocol == NGX_IMAP_POP3_PROTOCOL) {
+ size = sizeof("-ERR") - 1 + len + sizeof(CRLF) - 1;
+
+ } else {
+ size = s->tag.len + sizeof("NO") - 1 + len
+ + sizeof(CRLF) - 1;
+ }
+
+ p = ngx_pcalloc(s->connection->pool, size);
+ if (p == NULL) {
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+
+ ctx->err.data = p;
+
+ if (s->protocol == NGX_IMAP_POP3_PROTOCOL) {
+ *p++ = '-'; *p++ = 'E'; *p++ = 'R'; *p++ = 'R';
+
+ } else {
+ p = ngx_cpymem(p, s->tag.data, s->tag.len);
+ *p++ = 'N'; *p++ = 'O';
+ }
+
+ *p++ = ' ';
+ p = ngx_cpymem(p, ctx->header_start, len);
+ *p++ = CR; *p++ = LF;
+
+ ctx->err.len = p - ctx->err.data;
+
+ continue;
+ }
+
+ if (len == sizeof("Auth-Server") - 1
+ && ngx_strncasecmp(ctx->header_name_start, "Auth-Server",
+ sizeof("Auth-Server") - 1) == 0)
+ {
+ ctx->addr.len = ctx->header_end - ctx->header_start;
+ ctx->addr.data = ctx->header_start;
+
+ continue;
+ }
+
+ if (len == sizeof("Auth-Port") - 1
+ && ngx_strncasecmp(ctx->header_name_start, "Auth-Port",
+ sizeof("Auth-Port") - 1) == 0)
+ {
+ ctx->port.len = ctx->header_end - ctx->header_start;
+ ctx->port.data = ctx->header_start;
+
+ continue;
+ }
+
+ if (len == sizeof("Auth-User") - 1
+ && ngx_strncasecmp(ctx->header_name_start, "Auth-User",
+ sizeof("Auth-User") - 1) == 0)
+ {
+ s->login.len = ctx->header_end - ctx->header_start;
+ s->login.data = ctx->header_start;
+
+ continue;
+ }
+
+ if (len == sizeof("Auth-Wait") - 1
+ && ngx_strncasecmp(ctx->header_name_start, "Auth-Wait",
+ sizeof("Auth-Wait") - 1) == 0)
+ {
+ n = ngx_atoi(ctx->header_start,
+ ctx->header_end - ctx->header_start);
+
+ if (n != NGX_ERROR) {
+ ctx->sleep = n;
+ }
+
+ continue;
+ }
+
+ /* ignore other headers */
+
+ continue;
+ }
+
+ if (rc == NGX_DONE) {
+ ngx_log_debug0(NGX_LOG_DEBUG_IMAP, s->connection->log, 0,
+ "auth http header done");
+
+ ngx_close_connection(ctx->peer.connection);
+
+ if (ctx->err.len) {
+ (void) ngx_send(s->connection, ctx->err.data, ctx->err.len);
+
+ if (ctx->sleep == 0) {
+ ngx_imap_close_connection(s->connection);
+ return;
+ }
+
+ ngx_add_timer(s->connection->read, ctx->sleep * 1000);
+
+ s->connection->read->handler = ngx_imap_auth_sleep_handler;
+
+ return;
+ }
+
+ if (ctx->addr.len == 0 || ctx->port.len == 0) {
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "auth http server did not send server or port");
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+
+ ctx->peers = ngx_pcalloc(s->connection->pool, sizeof(ngx_peers_t));
+ if (ctx->peers == NULL) {
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+
+ sin = ngx_pcalloc(s->connection->pool, sizeof(struct sockaddr_in));
+ if (sin == NULL) {
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+
+ sin->sin_family = AF_INET;
+
+ port = ngx_atoi(ctx->port.data, ctx->port.len);
+ if (port == NGX_ERROR || port < 1 || port > 65536) {
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "auth http server sent invalid server "
+ "port:\"%V\"", &ctx->port);
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+
+ sin->sin_port = htons((in_port_t) port);
+
+ ctx->addr.data[ctx->addr.len] = '\0';
+ sin->sin_addr.s_addr = inet_addr((char *) ctx->addr.data);
+ if (sin->sin_addr.s_addr == INADDR_NONE) {
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "auth http server sent invalid server "
+ "address:\"%V\"", &ctx->addr);
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+
+ ctx->peers->number = 1;
+
+ ctx->peers->peer[0].sockaddr = (struct sockaddr *) sin;
+ ctx->peers->peer[0].socklen = sizeof(struct sockaddr_in);
+
+ len = ctx->addr.len + 1 + ctx->port.len;
+
+ ctx->peers->peer[0].name.len = len;
+
+ ctx->peers->peer[0].name.data = ngx_palloc(s->connection->pool,
+ len);
+ if (ctx->peers->peer[0].name.data == NULL) {
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+
+ len = ctx->addr.len;
+
+ ngx_memcpy(ctx->peers->peer[0].name.data, ctx->addr.data, len);
+
+ ctx->peers->peer[0].name.data[len++] = ':';
+
+ ngx_memcpy(ctx->peers->peer[0].name.data + len,
+ ctx->port.data, ctx->port.len);
+
+ ctx->peers->peer[0].uri_separator = "";
+
+ ngx_imap_proxy_init(s, ctx->peers);
+
+ return;
+ }
+
+ if (rc == NGX_AGAIN ) {
+ return;
+ }
+
+ /* rc == NGX_ERROR */
+
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "auth http server sent invalid header in response");
+ ngx_close_connection(ctx->peer.connection);
+ ngx_imap_session_internal_server_error(s);
+
+ return;
+ }
+}
+
+
+static void
+ngx_imap_auth_sleep_handler(ngx_event_t *rev)
+{
+ ngx_connection_t *c;
+ ngx_imap_session_t *s;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, "imap auth sleep handler");
+
+ c = rev->data;
+ s = c->data;
+
+ if (rev->timedout) {
+
+ rev->timedout = 0;
+
+ if (s->protocol == NGX_IMAP_POP3_PROTOCOL) {
+ s->imap_state = ngx_pop3_start;
+ s->connection->read->handler = ngx_pop3_auth_state;
+
+ } else {
+ s->imap_state = ngx_imap_start;
+ s->connection->read->handler = ngx_imap_auth_state;
+ }
+
+ if (rev->ready) {
+ s->connection->read->handler(rev);
+ return;
+ }
+
+ if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+ ngx_imap_close_connection(s->connection);
+ }
+
+ return;
+ }
+
+ if (rev->active) {
+ if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+ ngx_imap_close_connection(s->connection);
+ }
+ }
+}
+
+
+static ngx_int_t
+ngx_imap_auth_http_parse_header_line(ngx_imap_session_t *s,
+ ngx_imap_auth_http_ctx_t *ctx)
+{
+ u_char c, ch, *p;
+ ngx_uint_t hash;
+ enum {
+ sw_start = 0,
+ sw_name,
+ sw_space_before_value,
+ sw_value,
+ sw_space_after_value,
+ sw_almost_done,
+ sw_header_almost_done
+ } state;
+
+ state = ctx->state;
+ hash = ctx->hash;
+
+ for (p = ctx->response->pos; p < ctx->response->last; p++) {
+ ch = *p;
+
+ switch (state) {
+
+ /* first char */
+ case sw_start:
+
+ switch (ch) {
+ case CR:
+ ctx->header_end = p;
+ state = sw_header_almost_done;
+ break;
+ case LF:
+ ctx->header_end = p;
+ goto header_done;
+ default:
+ state = sw_name;
+ ctx->header_name_start = p;
+
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'z') {
+ hash = c;
+ break;
+ }
+
+ if (ch >= '0' && ch <= '9') {
+ hash = ch;
+ break;
+ }
+
+ return NGX_ERROR;
+ }
+ break;
+
+ /* header name */
+ case sw_name:
+ c = (u_char) (ch | 0x20);
+ if (c >= 'a' && c <= 'z') {
+ hash += c;
+ break;
+ }
+
+ if (ch == ':') {
+ ctx->header_name_end = p;
+ state = sw_space_before_value;
+ break;
+ }
+
+ if (ch == '-') {
+ hash += ch;
+ break;
+ }
+
+ if (ch >= '0' && ch <= '9') {
+ hash += ch;
+ break;
+ }
+
+ if (ch == CR) {
+ ctx->header_name_end = p;
+ ctx->header_start = p;
+ ctx->header_end = p;
+ state = sw_almost_done;
+ break;
+ }
+
+ if (ch == LF) {
+ ctx->header_name_end = p;
+ ctx->header_start = p;
+ ctx->header_end = p;
+ goto done;
+ }
+
+ return NGX_ERROR;
+
+ /* space* before header value */
+ case sw_space_before_value:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ ctx->header_start = p;
+ ctx->header_end = p;
+ state = sw_almost_done;
+ break;
+ case LF:
+ ctx->header_start = p;
+ ctx->header_end = p;
+ goto done;
+ default:
+ ctx->header_start = p;
+ state = sw_value;
+ break;
+ }
+ break;
+
+ /* header value */
+ case sw_value:
+ switch (ch) {
+ case ' ':
+ ctx->header_end = p;
+ state = sw_space_after_value;
+ break;
+ case CR:
+ ctx->header_end = p;
+ state = sw_almost_done;
+ break;
+ case LF:
+ ctx->header_end = p;
+ goto done;
+ }
+ break;
+
+ /* space* before end of header line */
+ case sw_space_after_value:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ default:
+ state = sw_value;
+ break;
+ }
+ break;
+
+ /* end of header line */
+ case sw_almost_done:
+ switch (ch) {
+ case LF:
+ goto done;
+ default:
+ return NGX_ERROR;
+ }
+
+ /* end of header */
+ case sw_header_almost_done:
+ switch (ch) {
+ case LF:
+ goto header_done;
+ default:
+ return NGX_ERROR;
+ }
+ }
+ }
+
+ ctx->response->pos = p;
+ ctx->state = state;
+ ctx->hash = hash;
+
+ return NGX_AGAIN;
+
+done:
+
+ ctx->response->pos = p + 1;
+ ctx->state = sw_start;
+ ctx->hash = hash;
+
+ return NGX_OK;
+
+header_done:
+
+ ctx->response->pos = p + 1;
+ ctx->state = sw_start;
+
+ return NGX_DONE;
+}
+
+
+static void
+ngx_imap_auth_http_block_read(ngx_event_t *rev)
+{
+ ngx_connection_t *c;
+ ngx_imap_session_t *s;
+ ngx_imap_auth_http_ctx_t *ctx;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0,
+ "imap auth http block read");
+
+ if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+ c = rev->data;
+ s = c->data;
+
+ ctx = ngx_imap_get_module_ctx(s, ngx_imap_auth_http_module);
+
+ ngx_close_connection(ctx->peer.connection);
+ ngx_imap_session_internal_server_error(s);
+ }
+}
+
+
+static void
+ngx_imap_auth_http_dummy_handler(ngx_event_t *ev)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_IMAP, ev->log, 0,
+ "imap auth http dummy handler");
+}
+
+
+static ngx_buf_t *
+ngx_imap_auth_http_create_request(ngx_imap_session_t *s,
+ ngx_imap_auth_http_conf_t *ahcf)
+{
+ size_t len;
+ ngx_buf_t *b;
+
+ len = sizeof("GET ") - 1 + ahcf->uri.len + sizeof(" HTTP/1.0" CRLF) - 1
+ + sizeof("Host: ") - 1 + ahcf->host_header.len + sizeof(CRLF) - 1
+ + sizeof("Auth-Method: plain" CRLF) - 1
+ + sizeof("Auth-User: ") - 1 + s->login.len + sizeof(CRLF) - 1
+ + sizeof("Auth-Pass: ") - 1 + s->passwd.len + sizeof(CRLF) - 1
+ + sizeof("Auth-Protocol: imap" CRLF) - 1
+ + sizeof("Auth-Login-Attempt: ") - 1 + NGX_INT_T_LEN
+ + sizeof(CRLF) - 1
+ + sizeof("Client-IP: ") - 1 + s->connection->addr_text.len
+ + sizeof(CRLF) - 1
+ + sizeof(CRLF) - 1;
+
+ b = ngx_create_temp_buf(s->connection->pool, len);
+ if (b == NULL) {
+ return NULL;
+ }
+
+ b->last = ngx_cpymem(b->last, "GET ", sizeof("GET ") - 1);
+ b->last = ngx_cpymem(b->last, ahcf->uri.data, ahcf->uri.len);
+ b->last = ngx_cpymem(b->last, " HTTP/1.0" CRLF,
+ sizeof(" HTTP/1.0" CRLF) - 1);
+
+ b->last = ngx_cpymem(b->last, "Host: ", sizeof("Host: ") - 1);
+ b->last = ngx_cpymem(b->last, ahcf->host_header.data,
+ ahcf->host_header.len);
+ *b->last++ = CR; *b->last++ = LF;
+
+ b->last = ngx_cpymem(b->last, "Auth-Method: plain" CRLF,
+ sizeof("Auth-Method: plain" CRLF) - 1);
+
+ b->last = ngx_cpymem(b->last, "Auth-User: ", sizeof("Auth-User: ") - 1);
+ b->last = ngx_cpymem(b->last, s->login.data, s->login.len);
+ *b->last++ = CR; *b->last++ = LF;
+
+ b->last = ngx_cpymem(b->last, "Auth-Pass: ", sizeof("Auth-Pass: ") - 1);
+ b->last = ngx_cpymem(b->last, s->passwd.data, s->passwd.len);
+ *b->last++ = CR; *b->last++ = LF;
+
+ b->last = ngx_cpymem(b->last, "Auth-Protocol: ",
+ sizeof("Auth-Protocol: ") - 1);
+ b->last = ngx_cpymem(b->last, ngx_imap_auth_http_protocol[s->protocol],
+ sizeof("imap") - 1);
+ *b->last++ = CR; *b->last++ = LF;
+
+ b->last = ngx_sprintf(b->last, "Auth-Login-Attempt: %ui" CRLF,
+ s->login_attempt);
+
+ b->last = ngx_cpymem(b->last, "Client-IP: ", sizeof("Client-IP: ") - 1);
+ b->last = ngx_cpymem(b->last, s->connection->addr_text.data,
+ s->connection->addr_text.len);
+ *b->last++ = CR; *b->last++ = LF;
+
+ /* add "\r\n" at the header end */
+ *b->last++ = CR; *b->last++ = LF;
+
+#if (NGX_DEBUG)
+ {
+ ngx_str_t l;
+
+ l.len = b->last - b->pos;
+ l.data = b->pos;
+ ngx_log_debug1(NGX_LOG_DEBUG_IMAP, s->connection->log, 0,
+ "imap auth http header:\n\"%V\"", &l);
+ }
+#endif
+
+ return b;
+}
+
+
+static void *
+ngx_imap_auth_http_create_conf(ngx_conf_t *cf)
+{
+ ngx_imap_auth_http_conf_t *ahcf;
+
+ ahcf = ngx_pcalloc(cf->pool, sizeof(ngx_imap_auth_http_conf_t));
+ if (ahcf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ahcf->timeout = NGX_CONF_UNSET_MSEC;
+
+ return ahcf;
+}
+
+
+static char *
+ngx_imap_auth_http_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_imap_auth_http_conf_t *prev = parent;
+ ngx_imap_auth_http_conf_t *conf = child;
+
+ if (conf->peers == NULL) {
+ conf->peers = prev->peers;
+ conf->host_header = prev->host_header;
+ conf->uri = prev->uri;
+ }
+
+ ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000);
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_imap_auth_http(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ ngx_imap_auth_http_conf_t *ahcf = conf;
+
+ ngx_uint_t i;
+ ngx_str_t *value, *url;
+ ngx_inet_upstream_t inet_upstream;
+#if (NGX_HAVE_UNIX_DOMAIN)
+ ngx_unix_domain_upstream_t unix_upstream;
+#endif
+
+ value = cf->args->elts;
+
+ url = &value[1];
+
+ if (ngx_strncasecmp(url->data, "unix:", 5) == 0) {
+
+#if (NGX_HAVE_UNIX_DOMAIN)
+
+ ngx_memzero(&unix_upstream, sizeof(ngx_unix_domain_upstream_t));
+
+ unix_upstream.name = *url;
+ unix_upstream.url = *url;
+ unix_upstream.uri_part = 1;
+
+ ahcf->peers = ngx_unix_upstream_parse(cf, &unix_upstream);
+ if (ahcf->peers == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ahcf->peers->peer[0].uri_separator = ":";
+
+ ahcf->host_header.len = sizeof("localhost") - 1;
+ ahcf->host_header.data = (u_char *) "localhost";
+ ahcf->uri = unix_upstream.uri;
+
+#else
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "the unix domain sockets are not supported "
+ "on this platform");
+ return NGX_CONF_ERROR;
+
+#endif
+
+ } else {
+ ngx_memzero(&inet_upstream, sizeof(ngx_inet_upstream_t));
+
+ inet_upstream.name = *url;
+ inet_upstream.url = *url;
+ inet_upstream.default_port_value = 80;
+ inet_upstream.uri_part = 1;
+
+ ahcf->peers = ngx_inet_upstream_parse(cf, &inet_upstream);
+ if (ahcf->peers == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ for (i = 0; i < ahcf->peers->number; i++) {
+ ahcf->peers->peer[i].uri_separator = "";
+ }
+
+ ahcf->host_header = inet_upstream.host_header;
+ ahcf->uri = inet_upstream.uri;
+ }
+
+ return NGX_CONF_OK;
+}
diff --git a/src/imap/ngx_imap_core_module.c b/src/imap/ngx_imap_core_module.c
new file mode 100644
index 000000000..e5d7a2050
--- /dev/null
+++ b/src/imap/ngx_imap_core_module.c
@@ -0,0 +1,455 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_imap.h>
+
+
+static void *ngx_imap_core_create_main_conf(ngx_conf_t *cf);
+static void *ngx_imap_core_create_srv_conf(ngx_conf_t *cf);
+static char *ngx_imap_core_merge_srv_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+static char *ngx_imap_core_server(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_imap_core_listen(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+static char *ngx_imap_core_capability(ngx_conf_t *cf, ngx_command_t *cmd,
+ void *conf);
+
+
+static ngx_conf_enum_t ngx_imap_core_procotol[] = {
+ { ngx_string("pop3"), NGX_IMAP_POP3_PROTOCOL },
+ { ngx_string("imap"), NGX_IMAP_IMAP_PROTOCOL },
+ { ngx_null_string, 0 }
+};
+
+
+static ngx_str_t ngx_pop3_default_capabilities[] = {
+ ngx_string("TOP"),
+ ngx_string("USER"),
+ ngx_string("UIDL"),
+ ngx_null_string
+};
+
+
+static ngx_str_t ngx_imap_default_capabilities[] = {
+ ngx_string("IMAP4"),
+ ngx_string("IMAP4rev1"),
+ ngx_string("UIDPLUS"),
+ ngx_null_string
+};
+
+
+static ngx_command_t ngx_imap_core_commands[] = {
+
+ { ngx_string("server"),
+ NGX_IMAP_MAIN_CONF|NGX_CONF_BLOCK|NGX_CONF_NOARGS,
+ ngx_imap_core_server,
+ 0,
+ 0,
+ NULL },
+
+ { ngx_string("listen"),
+ NGX_IMAP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_imap_core_listen,
+ 0,
+ 0,
+ NULL },
+
+ { ngx_string("protocol"),
+ NGX_IMAP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_enum_slot,
+ NGX_IMAP_SRV_CONF_OFFSET,
+ offsetof(ngx_imap_core_srv_conf_t, protocol),
+ &ngx_imap_core_procotol },
+
+ { ngx_string("imap_client_buffer"),
+ NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_IMAP_SRV_CONF_OFFSET,
+ offsetof(ngx_imap_core_srv_conf_t, imap_client_buffer_size),
+ NULL },
+
+ { ngx_string("proxy_buffer"),
+ NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_size_slot,
+ NGX_IMAP_SRV_CONF_OFFSET,
+ offsetof(ngx_imap_core_srv_conf_t, proxy_buffer_size),
+ NULL },
+
+ { ngx_string("timeout"),
+ NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_TAKE1,
+ ngx_conf_set_msec_slot,
+ NGX_IMAP_SRV_CONF_OFFSET,
+ offsetof(ngx_imap_core_srv_conf_t, timeout),
+ NULL },
+
+ { ngx_string("pop3_capabilities"),
+ NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_1MORE,
+ ngx_imap_core_capability,
+ NGX_IMAP_SRV_CONF_OFFSET,
+ offsetof(ngx_imap_core_srv_conf_t, pop3_capabilities),
+ NULL },
+
+ { ngx_string("imap_capabilities"),
+ NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_1MORE,
+ ngx_imap_core_capability,
+ NGX_IMAP_SRV_CONF_OFFSET,
+ offsetof(ngx_imap_core_srv_conf_t, imap_capabilities),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_imap_module_t ngx_imap_core_module_ctx = {
+ ngx_imap_core_create_main_conf, /* create main configuration */
+ NULL, /* init main configuration */
+
+ ngx_imap_core_create_srv_conf, /* create server configuration */
+ ngx_imap_core_merge_srv_conf /* merge server configuration */
+};
+
+
+ngx_module_t ngx_imap_core_module = {
+ NGX_MODULE_V1,
+ &ngx_imap_core_module_ctx, /* module context */
+ ngx_imap_core_commands, /* module directives */
+ NGX_IMAP_MODULE, /* module type */
+ NULL, /* init module */
+ NULL /* init process */
+};
+
+
+static void *
+ngx_imap_core_create_main_conf(ngx_conf_t *cf)
+{
+ ngx_imap_core_main_conf_t *cmcf;
+
+ cmcf = ngx_pcalloc(cf->pool, sizeof(ngx_imap_core_main_conf_t));
+ if (cmcf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ if (ngx_array_init(&cmcf->servers, cf->pool, 4,
+ sizeof(ngx_imap_core_srv_conf_t *)) == NGX_ERROR)
+ {
+ return NGX_CONF_ERROR;
+ }
+
+ return cmcf;
+}
+
+
+static void *
+ngx_imap_core_create_srv_conf(ngx_conf_t *cf)
+{
+ ngx_imap_core_srv_conf_t *cscf;
+
+ cscf = ngx_pcalloc(cf->pool, sizeof(ngx_imap_core_srv_conf_t));
+ if (cscf == NULL) {
+ return NULL;
+ }
+
+ cscf->imap_client_buffer_size = NGX_CONF_UNSET_SIZE;
+ cscf->proxy_buffer_size = NGX_CONF_UNSET_SIZE;
+ cscf->timeout = NGX_CONF_UNSET_MSEC;
+ cscf->protocol = NGX_CONF_UNSET_UINT;
+
+ if (ngx_array_init(&cscf->pop3_capabilities, cf->pool, 4, sizeof(ngx_str_t))
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ if (ngx_array_init(&cscf->imap_capabilities, cf->pool, 4, sizeof(ngx_str_t))
+ != NGX_OK)
+ {
+ return NULL;
+ }
+
+ return cscf;
+}
+
+
+static char *
+ngx_imap_core_merge_srv_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_imap_core_srv_conf_t *prev = parent;
+ ngx_imap_core_srv_conf_t *conf = child;
+
+ size_t size;
+ ngx_buf_t *b;
+ ngx_str_t *c, *d;
+ ngx_uint_t i;
+
+ ngx_conf_merge_size_value(conf->imap_client_buffer_size,
+ prev->imap_client_buffer_size,
+ (size_t) ngx_pagesize);
+ ngx_conf_merge_size_value(conf->proxy_buffer_size, prev->proxy_buffer_size,
+ (size_t) ngx_pagesize);
+ ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 60000);
+ ngx_conf_merge_unsigned_value(conf->protocol, prev->protocol,
+ NGX_IMAP_IMAP_PROTOCOL);
+
+
+ if (conf->pop3_capabilities.nelts == 0) {
+ conf->pop3_capabilities = prev->pop3_capabilities;
+ }
+
+ if (conf->pop3_capabilities.nelts == 0) {
+
+ for (d = ngx_pop3_default_capabilities; d->len; d++) {
+ c = ngx_array_push(&conf->pop3_capabilities);
+ if (c == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *c = *d;
+ }
+ }
+
+ size = sizeof("+OK Capability list follows" CRLF) - 1
+ + sizeof("." CRLF) - 1;
+
+ c = conf->pop3_capabilities.elts;
+ for (i = 0; i < conf->pop3_capabilities.nelts; i++) {
+ size += c[i].len + sizeof(CRLF) - 1;
+ }
+
+ b = ngx_create_temp_buf(cf->pool, size);
+ if (b == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ b->last = ngx_cpymem(b->last, "+OK Capability list follows" CRLF,
+ sizeof("+OK Capability list follows" CRLF) - 1);
+
+ for (i = 0; i < conf->pop3_capabilities.nelts; i++) {
+ b->last = ngx_cpymem(b->last, c[i].data, c[i].len);
+ *b->last++ = CR; *b->last++ = LF;
+ }
+
+ *b->last++ = '.'; *b->last++ = CR; *b->last++ = LF;
+
+ conf->pop3_capability = b;
+
+
+ if (conf->imap_capabilities.nelts == 0) {
+ conf->imap_capabilities = prev->imap_capabilities;
+ }
+
+ if (conf->imap_capabilities.nelts == 0) {
+
+ for (d = ngx_imap_default_capabilities; d->len; d++) {
+ c = ngx_array_push(&conf->imap_capabilities);
+ if (c == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *c = *d;
+ }
+ }
+
+ size = sizeof("* CAPABILITY") - 1 + sizeof(CRLF) - 1;
+
+ c = conf->imap_capabilities.elts;
+ for (i = 0; i < conf->imap_capabilities.nelts; i++) {
+ size += 1 + c[i].len;
+ }
+
+ b = ngx_create_temp_buf(cf->pool, size);
+ if (b == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ b->last = ngx_cpymem(b->last, "* CAPABILITY", sizeof("* CAPABILITY") - 1);
+
+ for (i = 0; i < conf->imap_capabilities.nelts; i++) {
+ *b->last++ = ' ';
+ b->last = ngx_cpymem(b->last, c[i].data, c[i].len);
+ }
+
+ *b->last++ = CR; *b->last++ = LF;
+
+ conf->imap_capability = b;
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_imap_core_server(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *rv;
+ void *mconf;
+ ngx_uint_t m;
+ ngx_conf_t pcf;
+ ngx_imap_module_t *module;
+ ngx_imap_conf_ctx_t *ctx, *imap_ctx;
+ ngx_imap_core_srv_conf_t *cscf, **cscfp;
+ ngx_imap_core_main_conf_t *cmcf;
+
+
+ ctx = ngx_pcalloc(cf->pool, sizeof(ngx_imap_conf_ctx_t));
+ if (ctx == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ imap_ctx = cf->ctx;
+ ctx->main_conf = imap_ctx->main_conf;
+
+ /* the server{}'s srv_conf */
+
+ ctx->srv_conf = ngx_pcalloc(cf->pool, sizeof(void *) * ngx_imap_max_module);
+ if (ctx->srv_conf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ for (m = 0; ngx_modules[m]; m++) {
+ if (ngx_modules[m]->type != NGX_IMAP_MODULE) {
+ continue;
+ }
+
+ module = ngx_modules[m]->ctx;
+
+ if (module->create_srv_conf) {
+ mconf = module->create_srv_conf(cf);
+ if (mconf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ctx->srv_conf[ngx_modules[m]->ctx_index] = mconf;
+ }
+ }
+
+ /* the server configuration context */
+
+ cscf = ctx->srv_conf[ngx_imap_core_module.ctx_index];
+ cscf->ctx = ctx;
+
+ cmcf = ctx->main_conf[ngx_imap_core_module.ctx_index];
+
+ cscfp = ngx_array_push(&cmcf->servers);
+ if (cscfp == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *cscfp = cscf;
+
+
+ /* parse inside server{} */
+
+ pcf = *cf;
+ cf->ctx = ctx;
+ cf->cmd_type = NGX_IMAP_SRV_CONF;
+
+ rv = ngx_conf_parse(cf, NULL);
+
+ *cf = pcf;
+
+ return rv;
+}
+
+
+/* AF_INET only */
+
+static char *
+ngx_imap_core_listen(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *err;
+ ngx_str_t *value;
+ in_addr_t in_addr;
+ struct hostent *h;
+ ngx_listening_t *ls;
+ ngx_inet_upstream_t inet_upstream;
+
+ value = cf->args->elts;
+
+ ngx_memzero(&inet_upstream, sizeof(ngx_inet_upstream_t));
+
+ inet_upstream.url = value[1];
+ inet_upstream.port_only = 1;
+
+ err = ngx_inet_parse_host_port(&inet_upstream);
+
+ if (err) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "%s in \"%V\" of the \"listen\" directive",
+ err, &inet_upstream.url);
+ return NGX_CONF_ERROR;
+ }
+
+ if (inet_upstream.host.len) {
+ inet_upstream.host.data[inet_upstream.host.len] = '\0';
+
+ in_addr = inet_addr((const char *) inet_upstream.host.data);
+
+ if (in_addr == INADDR_NONE) {
+ h = gethostbyname((const char *) inet_upstream.host.data);
+
+ if (h == NULL || h->h_addr_list[0] == NULL) {
+ ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
+ "can not resolve host \"%s\" "
+ "in the \"listen\" directive",
+ inet_upstream.host.data);
+ return NGX_CONF_ERROR;
+ }
+
+ in_addr = *(in_addr_t *)(h->h_addr_list[0]);
+ }
+
+ } else {
+ in_addr = INADDR_ANY;
+ }
+
+
+ ls = ngx_listening_inet_stream_socket(cf, in_addr, inet_upstream.port);
+ if (ls == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ ls->backlog = -1;
+ ls->addr_ntop = 1;
+ ls->handler = ngx_imap_init_connection;
+ ls->pool_size = 256;
+
+ ls->ctx = cf->ctx;
+
+ /* STUB */
+ ls->log = cf->cycle->new_log;
+ /**/
+
+ return NGX_CONF_OK;
+}
+
+
+static char *
+ngx_imap_core_capability(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+ char *p = conf;
+
+ ngx_str_t *c, *value;
+ ngx_uint_t i;
+ ngx_array_t *a;
+
+ a = (ngx_array_t *) (p + cmd->offset);
+
+ value = cf->args->elts;
+
+ for (i = 1; i < cf->args->nelts; i++) {
+ c = ngx_array_push(a);
+ if (c == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ *c = value[i];
+ }
+
+ return NGX_CONF_OK;
+}
diff --git a/src/imap/ngx_imap_handler.c b/src/imap/ngx_imap_handler.c
new file mode 100644
index 000000000..6464d4a85
--- /dev/null
+++ b/src/imap/ngx_imap_handler.c
@@ -0,0 +1,561 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_imap.h>
+
+
+static void ngx_imap_init_session(ngx_event_t *rev);
+static ngx_int_t ngx_imap_read_command(ngx_imap_session_t *s);
+
+
+static ngx_str_t greetings[] = {
+ ngx_string("+OK POP3 ready" CRLF),
+ ngx_string("* OK IMAP ready" CRLF)
+};
+
+static ngx_str_t internal_server_errors[] = {
+ ngx_string("-ERR internal server error" CRLF),
+ ngx_string("* BAD internal server error" CRLF),
+};
+
+static u_char pop3_ok[] = "+OK" CRLF;
+static u_char pop3_invalid_command[] = "-ERR invalid command" CRLF;
+
+static u_char imap_ok[] = "OK" CRLF;
+static u_char imap_next[] = "+ OK" CRLF;
+static u_char imap_bye[] = "* BYE" CRLF;
+static u_char imap_invalid_command[] = "BAD invalid command" CRLF;
+
+
+void
+ngx_imap_init_connection(ngx_connection_t *c)
+{
+ ssize_t size;
+ ngx_imap_conf_ctx_t *ctx;
+ ngx_imap_core_srv_conf_t *cscf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_IMAP, c->log, 0, "imap init connection");
+
+ c->log_error = NGX_ERROR_INFO;
+
+ ctx = c->ctx;
+ cscf = ngx_imap_get_module_srv_conf(ctx, ngx_imap_core_module);
+
+ size = greetings[cscf->protocol].len;
+
+ if (ngx_send(c, greetings[cscf->protocol].data, size) < size) {
+ /*
+ * we treat the incomplete sending as NGX_ERROR
+ * because it is very strange here
+ */
+ ngx_imap_close_connection(c);
+ return;
+ }
+
+ c->read->handler = ngx_imap_init_session;
+
+ ngx_add_timer(c->read, cscf->timeout);
+
+ if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
+ ngx_imap_close_connection(c);
+ }
+}
+
+
+static void
+ngx_imap_init_session(ngx_event_t *rev)
+{
+ size_t size;
+ ngx_connection_t *c;
+ ngx_imap_session_t *s;
+ ngx_imap_conf_ctx_t *ctx;
+ ngx_imap_core_srv_conf_t *cscf;
+
+ c = rev->data;
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
+ ngx_imap_close_connection(c);
+ return;
+ }
+
+ s = ngx_pcalloc(c->pool, sizeof(ngx_imap_session_t));
+ if (s == NULL) {
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+
+ c->data = s;
+ s->connection = c;
+
+ s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_imap_max_module);
+ if (s->ctx == NULL) {
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+
+ ctx = c->ctx;
+ s->main_conf = ctx->main_conf;
+ s->srv_conf = ctx->srv_conf;
+
+ if (ngx_array_init(&s->args, c->pool, 2, sizeof(ngx_str_t)) == NGX_ERROR) {
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+
+ cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module);
+
+ s->protocol = cscf->protocol;
+
+ if (cscf->protocol == NGX_IMAP_POP3_PROTOCOL) {
+ size = 128;
+ s->imap_state = ngx_pop3_start;
+ c->read->handler = ngx_pop3_auth_state;
+
+ } else {
+ size = cscf->imap_client_buffer_size;
+ s->imap_state = ngx_imap_start;
+ c->read->handler = ngx_imap_auth_state;
+ }
+
+ s->buffer = ngx_create_temp_buf(c->pool, size);
+ if (s->buffer == NULL) {
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+
+ c->read->handler(rev);
+}
+
+
+void
+ngx_imap_auth_state(ngx_event_t *rev)
+{
+ u_char *text, *last, *out, *p;
+ ssize_t size, text_len, last_len;
+ ngx_str_t *arg;
+ ngx_int_t rc;
+ ngx_uint_t quit, tag;
+ ngx_connection_t *c;
+ ngx_imap_session_t *s;
+ ngx_imap_core_srv_conf_t *cscf;
+
+ c = rev->data;
+ s = c->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_IMAP, c->log, 0, "imap auth state");
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
+ ngx_imap_close_connection(c);
+ return;
+ }
+
+ rc = ngx_imap_read_command(s);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_IMAP, c->log, 0, "imap auth: %i", rc);
+
+ if (rc == NGX_AGAIN || rc == NGX_ERROR) {
+ return;
+ }
+
+ quit = 0;
+ tag = 1;
+
+ text = NULL;
+ text_len = 0;
+
+ last = imap_ok;
+ last_len = sizeof(imap_ok) - 1;
+
+ if (rc == NGX_OK) {
+
+ ngx_log_debug1(NGX_LOG_DEBUG_IMAP, c->log, 0, "imap auth command: %i",
+ s->command);
+
+ switch (s->command) {
+
+ case NGX_IMAP_LOGIN:
+ if (s->args.nelts == 2) {
+
+ arg = s->args.elts;
+
+ s->login.len = arg[0].len;
+ s->login.data = ngx_palloc(c->pool, s->login.len);
+ if (s->login.data == NULL) {
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+
+ ngx_memcpy(s->login.data, arg[0].data, s->login.len);
+
+ s->passwd.len = arg[1].len;
+ s->passwd.data = ngx_palloc(c->pool, s->passwd.len);
+ if (s->passwd.data == NULL) {
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+
+ ngx_memcpy(s->passwd.data, arg[1].data, s->passwd.len);
+
+ ngx_log_debug2(NGX_LOG_DEBUG_IMAP, c->log, 0,
+ "imap login:\"%V\" passwd:\"%V\"",
+ &s->login, &s->passwd);
+
+ s->args.nelts = 0;
+ s->buffer->pos = s->buffer->start;
+ s->buffer->last = s->buffer->start;
+
+ if (rev->timer_set) {
+ ngx_del_timer(rev);
+ }
+
+ s->login_attempt++;
+
+ ngx_imap_auth_http_init(s);
+
+ return;
+
+ } else {
+ rc = NGX_IMAP_PARSE_INVALID_COMMAND;
+ }
+
+ break;
+
+ case NGX_IMAP_CAPABILITY:
+ cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module);
+ text = cscf->imap_capability->pos;
+ text_len = cscf->imap_capability->last - cscf->imap_capability->pos;
+ break;
+
+ case NGX_IMAP_LOGOUT:
+ text = imap_bye;
+ text_len = sizeof(imap_bye) - 1;
+ quit = 1;
+ break;
+
+ case NGX_IMAP_NOOP:
+ break;
+
+ default:
+ rc = NGX_IMAP_PARSE_INVALID_COMMAND;
+ break;
+ }
+
+ } else if (rc == NGX_IMAP_NEXT) {
+ last = imap_next;
+ last_len = sizeof(imap_next) - 1;
+ tag = 0;
+ }
+
+ if (rc == NGX_IMAP_PARSE_INVALID_COMMAND) {
+ last = imap_invalid_command;
+ last_len = sizeof(imap_invalid_command) - 1;
+ }
+
+ if (tag) {
+ if (s->out.len < text_len + s->tag.len + last_len) {
+
+ s->out.len = text_len + s->tag.len + last_len;
+ s->out.data = ngx_palloc(c->pool, s->out.len);
+ if (s->out.data == NULL) {
+ ngx_imap_close_connection(c);
+ return;
+ }
+ }
+
+ out = s->out.data;
+ p = out;
+
+ if (text) {
+ p = ngx_cpymem(p, text, text_len);
+ }
+ p = ngx_cpymem(p, s->tag.data, s->tag.len);
+ ngx_memcpy(p, last, last_len);
+
+ size = text_len + s->tag.len + last_len;
+
+ } else {
+ out = last;
+ size = last_len;
+ }
+
+ if (ngx_send(c, out, size) < size) {
+ /*
+ * we treat the incomplete sending as NGX_ERROR
+ * because it is very strange here
+ */
+ ngx_imap_close_connection(c);
+ return;
+ }
+
+ if (rc == NGX_IMAP_NEXT) {
+ return;
+ }
+
+ if (quit) {
+ ngx_imap_close_connection(c);
+ return;
+ }
+
+ s->args.nelts = 0;
+ s->buffer->pos = s->buffer->start;
+ s->buffer->last = s->buffer->start;
+ s->tag.len = 0;
+}
+
+
+void
+ngx_pop3_auth_state(ngx_event_t *rev)
+{
+ u_char *text;
+ ssize_t size;
+ ngx_int_t rc;
+ ngx_uint_t quit;
+ ngx_str_t *arg;
+ ngx_connection_t *c;
+ ngx_imap_session_t *s;
+ ngx_imap_core_srv_conf_t *cscf;
+
+ c = rev->data;
+ s = c->data;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_IMAP, c->log, 0, "pop3 auth state");
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
+ ngx_imap_close_connection(c);
+ return;
+ }
+
+ rc = ngx_imap_read_command(s);
+
+ if (rc == NGX_AGAIN || rc == NGX_ERROR) {
+ return;
+ }
+
+ quit = 0;
+ text = pop3_ok;
+ size = sizeof(pop3_ok) - 1;
+
+ if (rc == NGX_OK) {
+ switch (s->imap_state) {
+
+ case ngx_pop3_start:
+
+ switch (s->command) {
+
+ case NGX_POP3_USER:
+ if (s->args.nelts == 1) {
+ s->imap_state = ngx_pop3_user;
+
+ arg = s->args.elts;
+ s->login.len = arg[0].len;
+ s->login.data = ngx_palloc(c->pool, s->login.len);
+ if (s->login.data == NULL) {
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+
+ ngx_memcpy(s->login.data, arg[0].data, s->login.len);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_IMAP, c->log, 0,
+ "pop3 login: \"%V\"", &s->login);
+
+ } else {
+ rc = NGX_IMAP_PARSE_INVALID_COMMAND;
+ }
+
+ break;
+
+ case NGX_POP3_CAPA:
+ cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module);
+ text = cscf->pop3_capability->pos;
+ size = cscf->pop3_capability->last - cscf->pop3_capability->pos;
+ break;
+
+ case NGX_POP3_QUIT:
+ quit = 1;
+ break;
+
+ case NGX_POP3_NOOP:
+ break;
+
+ default:
+ s->imap_state = ngx_pop3_start;
+ rc = NGX_IMAP_PARSE_INVALID_COMMAND;
+ break;
+ }
+
+ break;
+
+ case ngx_pop3_user:
+
+ switch (s->command) {
+
+ case NGX_POP3_PASS:
+ if (s->args.nelts == 1) {
+ /* STUB */ s->imap_state = ngx_pop3_start;
+
+ arg = s->args.elts;
+ s->passwd.len = arg[0].len;
+ s->passwd.data = ngx_palloc(c->pool, s->passwd.len);
+ if (s->passwd.data == NULL) {
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+
+ ngx_memcpy(s->passwd.data, arg[0].data, s->passwd.len);
+
+ ngx_log_debug1(NGX_LOG_DEBUG_IMAP, c->log, 0,
+ "pop3 passwd: \"%V\"", &s->passwd);
+
+ s->args.nelts = 0;
+ s->buffer->pos = s->buffer->start;
+ s->buffer->last = s->buffer->start;
+
+ if (rev->timer_set) {
+ ngx_del_timer(rev);
+ }
+
+ ngx_imap_auth_http_init(s);
+
+ return;
+
+ } else {
+ rc = NGX_IMAP_PARSE_INVALID_COMMAND;
+ }
+
+ break;
+
+ case NGX_POP3_CAPA:
+ cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module);
+ text = cscf->pop3_capability->pos;
+ size = cscf->pop3_capability->last - cscf->pop3_capability->pos;
+ break;
+
+ case NGX_POP3_QUIT:
+ quit = 1;
+ break;
+
+ case NGX_POP3_NOOP:
+ break;
+
+ default:
+ s->imap_state = ngx_pop3_start;
+ rc = NGX_IMAP_PARSE_INVALID_COMMAND;
+ break;
+ }
+
+ break;
+
+ /* suppress warinings */
+ case ngx_pop3_passwd:
+ break;
+ }
+ }
+
+ if (rc == NGX_IMAP_PARSE_INVALID_COMMAND) {
+ text = pop3_invalid_command;
+ size = sizeof(pop3_invalid_command) - 1;
+ }
+
+ if (ngx_send(c, text, size) < size) {
+ /*
+ * we treat the incomplete sending as NGX_ERROR
+ * because it is very strange here
+ */
+ ngx_imap_close_connection(c);
+ return;
+ }
+
+ if (quit) {
+ ngx_imap_close_connection(c);
+ return;
+ }
+
+ s->args.nelts = 0;
+ s->buffer->pos = s->buffer->start;
+ s->buffer->last = s->buffer->start;
+}
+
+
+static ngx_int_t
+ngx_imap_read_command(ngx_imap_session_t *s)
+{
+ ssize_t n;
+ ngx_int_t rc;
+
+ n = ngx_recv(s->connection, s->buffer->last,
+ s->buffer->end - s->buffer->last);
+
+ if (n == NGX_ERROR || n == 0) {
+ ngx_imap_close_connection(s->connection);
+ return NGX_ERROR;
+ }
+
+ if (n > 0) {
+ s->buffer->last += n;
+ }
+
+ if (n == NGX_AGAIN) {
+ if (ngx_handle_read_event(s->connection->read, 0) == NGX_ERROR) {
+ ngx_imap_session_internal_server_error(s);
+ return NGX_ERROR;
+ }
+
+ return NGX_AGAIN;
+ }
+
+ if (s->protocol == NGX_IMAP_POP3_PROTOCOL) {
+ rc = ngx_pop3_parse_command(s);
+ } else {
+ rc = ngx_imap_parse_command(s);
+ }
+
+ if (rc == NGX_AGAIN
+ || rc == NGX_IMAP_NEXT
+ || rc == NGX_IMAP_PARSE_INVALID_COMMAND)
+ {
+ return rc;
+ }
+
+ if (rc == NGX_ERROR) {
+ ngx_imap_close_connection(s->connection);
+ return NGX_ERROR;
+ }
+
+ return NGX_OK;
+}
+
+
+void
+ngx_imap_session_internal_server_error(ngx_imap_session_t *s)
+{
+ (void) ngx_send(s->connection, internal_server_errors[s->protocol].data,
+ internal_server_errors[s->protocol].len);
+
+ ngx_imap_close_connection(s->connection);
+}
+
+
+void
+ngx_imap_close_connection(ngx_connection_t *c)
+{
+ ngx_pool_t *pool;
+
+ ngx_log_debug1(NGX_LOG_DEBUG_IMAP, c->log, 0,
+ "close imap connection: %d", c->fd);
+
+ pool = c->pool;
+
+ ngx_close_connection(c);
+
+ ngx_destroy_pool(pool);
+}
diff --git a/src/imap/ngx_imap_parse.c b/src/imap/ngx_imap_parse.c
new file mode 100644
index 000000000..e3923e892
--- /dev/null
+++ b/src/imap/ngx_imap_parse.c
@@ -0,0 +1,500 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_imap.h>
+
+
+ngx_int_t ngx_imap_parse_command(ngx_imap_session_t *s)
+{
+ u_char ch, *p, *c;
+ ngx_str_t *arg;
+ enum {
+ sw_start = 0,
+ sw_spaces_before_command,
+ sw_command,
+ sw_spaces_before_argument,
+ sw_argument,
+ sw_literal,
+ sw_start_literal_argument,
+ sw_literal_argument,
+ sw_end_literal_argument,
+ sw_almost_done
+ } state;
+
+ state = s->state;
+
+ for (p = s->buffer->pos; p < s->buffer->last; p++) {
+ ch = *p;
+
+ switch (state) {
+
+ /* IMAP tag */
+ case sw_start:
+ switch (ch) {
+ case ' ':
+ s->tag.len = p - s->buffer->start + 1;
+ s->tag.data = s->buffer->start;
+ state = sw_spaces_before_command;
+ break;
+ case CR:
+ s->state = sw_start;
+ return NGX_IMAP_PARSE_INVALID_COMMAND;
+ case LF:
+ s->state = sw_start;
+ return NGX_IMAP_PARSE_INVALID_COMMAND;
+ }
+ break;
+
+ case sw_spaces_before_command:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ s->state = sw_start;
+ return NGX_IMAP_PARSE_INVALID_COMMAND;
+ case LF:
+ s->state = sw_start;
+ return NGX_IMAP_PARSE_INVALID_COMMAND;
+ default:
+ s->cmd_start = p;
+ state = sw_command;
+ break;
+ }
+ break;
+
+ case sw_command:
+ if (ch == ' ' || ch == CR || ch == LF) {
+
+ c = s->cmd_start;
+
+ switch (p - c) {
+
+ case 4:
+ if ((c[0] == 'N' || c[0] == 'n')
+ && (c[1] == 'O'|| c[1] == 'o')
+ && (c[2] == 'O'|| c[2] == 'o')
+ && (c[3] == 'P'|| c[3] == 'p'))
+ {
+ s->command = NGX_IMAP_NOOP;
+
+ } else {
+ goto invalid;
+ }
+ break;
+
+ case 5:
+ if ((c[0] == 'L'|| c[0] == 'l')
+ && (c[1] == 'O'|| c[1] == 'o')
+ && (c[2] == 'G'|| c[2] == 'g')
+ && (c[3] == 'I'|| c[3] == 'i')
+ && (c[4] == 'N'|| c[4] == 'n'))
+ {
+ s->command = NGX_IMAP_LOGIN;
+
+ } else {
+ goto invalid;
+ }
+ break;
+
+ case 6:
+ if ((c[0] == 'L'|| c[0] == 'l')
+ && (c[1] == 'O'|| c[1] == 'o')
+ && (c[2] == 'G'|| c[2] == 'g')
+ && (c[3] == 'O'|| c[3] == 'o')
+ && (c[4] == 'U'|| c[4] == 'u')
+ && (c[5] == 'T'|| c[5] == 't'))
+ {
+ s->command = NGX_IMAP_LOGOUT;
+
+ } else {
+ goto invalid;
+ }
+ break;
+
+ case 10:
+ if ((c[0] == 'C'|| c[0] == 'c')
+ && (c[1] == 'A'|| c[1] == 'a')
+ && (c[2] == 'P'|| c[2] == 'p')
+ && (c[3] == 'A'|| c[3] == 'a')
+ && (c[4] == 'B'|| c[4] == 'b')
+ && (c[5] == 'I'|| c[5] == 'i')
+ && (c[6] == 'L'|| c[6] == 'l')
+ && (c[7] == 'I'|| c[7] == 'i')
+ && (c[8] == 'T'|| c[8] == 't')
+ && (c[9] == 'Y'|| c[9] == 'y'))
+ {
+ s->command = NGX_IMAP_CAPABILITY;
+
+ } else {
+ goto invalid;
+ }
+ break;
+
+ default:
+ goto invalid;
+ }
+
+ switch (ch) {
+ case ' ':
+ state = sw_spaces_before_argument;
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ }
+ break;
+ }
+
+ if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {
+ goto invalid;
+ }
+
+ break;
+
+ case sw_spaces_before_argument:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ state = sw_almost_done;
+ s->arg_end = p;
+ break;
+ case LF:
+ s->arg_end = p;
+ goto done;
+ case '"':
+ if (s->args.nelts <= 2) {
+ s->quoted = 1;
+ s->arg_start = p + 1;
+ state = sw_argument;
+ break;
+ }
+ goto invalid;
+ case '{':
+ if (s->args.nelts <= 2) {
+ state = sw_literal;
+ break;
+ }
+ goto invalid;
+ default:
+ if (s->args.nelts <= 2) {
+ s->arg_start = p;
+ state = sw_argument;
+ break;
+ }
+ goto invalid;
+ }
+ break;
+
+ case sw_argument:
+ switch (ch) {
+ case '"':
+ if (!s->quoted) {
+ break;
+ }
+ s->quoted = 0;
+ /* fall through */
+ case ' ':
+ case CR:
+ case LF:
+ arg = ngx_array_push(&s->args);
+ if (arg == NULL) {
+ return NGX_ERROR;
+ }
+ arg->len = p - s->arg_start;
+ arg->data = s->arg_start;
+ s->arg_start = NULL;
+
+ switch (ch) {
+ case '"':
+ case ' ':
+ state = sw_spaces_before_argument;
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ }
+ break;
+ }
+ break;
+
+ case sw_literal:
+ if (ch >= '0' && ch <= '9') {
+ s->literal_len = s->literal_len * 10 + (ch - '0');
+ break;
+ }
+ if (ch == '}') {
+ state = sw_start_literal_argument;
+ break;
+ }
+ goto invalid;
+
+ case sw_start_literal_argument:
+ switch (ch) {
+ case CR:
+ break;
+ case LF:
+ s->buffer->pos = p + 1;
+ s->arg_start = p + 1;
+ s->state = sw_literal_argument;
+ return NGX_IMAP_NEXT;
+ }
+ goto invalid;
+
+ case sw_literal_argument:
+ if (--s->literal_len) {
+ break;
+ }
+
+ arg = ngx_array_push(&s->args);
+ if (arg == NULL) {
+ return NGX_ERROR;
+ }
+ arg->len = p + 1 - s->arg_start;
+ arg->data = s->arg_start;
+ s->arg_start = NULL;
+ state = sw_end_literal_argument;
+
+ break;
+
+ case sw_end_literal_argument:
+ switch (ch) {
+ case '{':
+ if (s->args.nelts <= 2) {
+ state = sw_literal;
+ break;
+ }
+ goto invalid;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ default:
+ goto invalid;
+ }
+ break;
+
+ case sw_almost_done:
+ switch (ch) {
+ case LF:
+ goto done;
+ default:
+ goto invalid;
+ }
+ }
+ }
+
+ s->buffer->pos = p;
+ s->state = state;
+
+ return NGX_AGAIN;
+
+done:
+
+ s->buffer->pos = p + 1;
+
+ if (s->arg_start) {
+ arg = ngx_array_push(&s->args);
+ if (arg == NULL) {
+ return NGX_ERROR;
+ }
+ arg->len = s->arg_end - s->arg_start;
+ arg->data = s->arg_start;
+ s->arg_start = NULL;
+ s->cmd_start = NULL;
+ s->quoted = 0;
+ s->literal_len = 0;
+ }
+
+ s->state = sw_start;
+
+ return NGX_OK;
+
+invalid:
+
+ s->state = sw_start;
+ s->quoted = 0;
+ s->literal_len = 0;
+
+ return NGX_IMAP_PARSE_INVALID_COMMAND;
+}
+
+
+ngx_int_t ngx_pop3_parse_command(ngx_imap_session_t *s)
+{
+ u_char ch, *p, *c, c0, c1, c2, c3;
+ ngx_str_t *arg;
+ enum {
+ sw_start = 0,
+ sw_spaces_before_argument,
+ sw_argument,
+ sw_almost_done
+ } state;
+
+ state = s->state;
+
+ for (p = s->buffer->pos; p < s->buffer->last; p++) {
+ ch = *p;
+
+ switch (state) {
+
+ /* POP3 command */
+ case sw_start:
+ if (ch == ' ' || ch == CR || ch == LF) {
+ c = s->buffer->start;
+
+ if (p - c == 4) {
+
+ c0 = ngx_toupper(c[0]);
+ c1 = ngx_toupper(c[1]);
+ c2 = ngx_toupper(c[2]);
+ c3 = ngx_toupper(c[3]);
+
+ if (c0 == 'U' && c1 == 'S' && c2 == 'E' && c3 == 'R')
+ {
+ s->command = NGX_POP3_USER;
+
+ } else if (c0 == 'P' && c1 == 'A' && c2 == 'S' && c3 == 'S')
+ {
+ s->command = NGX_POP3_PASS;
+
+ } else if (c0 == 'Q' && c1 == 'U' && c2 == 'I' && c3 == 'T')
+ {
+ s->command = NGX_POP3_QUIT;
+
+ } else if (c0 == 'C' && c1 == 'A' && c2 == 'P' && c3 == 'A')
+ {
+ s->command = NGX_POP3_CAPA;
+
+ } else if (c0 == 'N' && c1 == 'O' && c2 == 'O' && c3 == 'P')
+ {
+ s->command = NGX_POP3_NOOP;
+
+ } else {
+ goto invalid;
+ }
+
+ } else {
+ goto invalid;
+ }
+
+ switch (ch) {
+ case ' ':
+ state = sw_spaces_before_argument;
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ }
+ break;
+ }
+
+ if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {
+ goto invalid;
+ }
+
+ break;
+
+ case sw_spaces_before_argument:
+ switch (ch) {
+ case ' ':
+ break;
+ case CR:
+ state = sw_almost_done;
+ s->arg_end = p;
+ break;
+ case LF:
+ s->arg_end = p;
+ goto done;
+ default:
+ if (s->args.nelts <= 2) {
+ state = sw_argument;
+ s->arg_start = p;
+ break;
+ }
+ goto invalid;
+ }
+ break;
+
+ case sw_argument:
+ switch (ch) {
+ case ' ':
+ case CR:
+ case LF:
+ arg = ngx_array_push(&s->args);
+ if (arg == NULL) {
+ return NGX_ERROR;
+ }
+ arg->len = p - s->arg_start;
+ arg->data = s->arg_start;
+ s->arg_start = NULL;
+
+ switch (ch) {
+ case ' ':
+ state = sw_spaces_before_argument;
+ break;
+ case CR:
+ state = sw_almost_done;
+ break;
+ case LF:
+ goto done;
+ }
+ break;
+
+ default:
+ break;
+ }
+ break;
+
+ case sw_almost_done:
+ switch (ch) {
+ case LF:
+ goto done;
+ default:
+ goto invalid;
+ }
+ }
+ }
+
+ s->buffer->pos = p;
+ s->state = state;
+
+ return NGX_AGAIN;
+
+done:
+
+ s->buffer->pos = p + 1;
+
+ if (s->arg_start) {
+ arg = ngx_array_push(&s->args);
+ if (arg == NULL) {
+ return NGX_ERROR;
+ }
+ arg->len = s->arg_end - s->arg_start;
+ arg->data = s->arg_start;
+ s->arg_start = NULL;
+ }
+
+ s->state = sw_start;
+
+ return NGX_OK;
+
+invalid:
+
+ s->state = sw_start;
+
+ return NGX_IMAP_PARSE_INVALID_COMMAND;
+}
diff --git a/src/imap/ngx_imap_proxy_module.c b/src/imap/ngx_imap_proxy_module.c
new file mode 100644
index 000000000..5177e289f
--- /dev/null
+++ b/src/imap/ngx_imap_proxy_module.c
@@ -0,0 +1,623 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_event.h>
+#include <ngx_event_connect.h>
+#include <ngx_imap.h>
+
+
+typedef struct {
+ ngx_flag_t enable;
+} ngx_imap_proxy_conf_t;
+
+
+static void ngx_imap_proxy_block_read(ngx_event_t *rev);
+static void ngx_imap_proxy_imap_handler(ngx_event_t *rev);
+static void ngx_imap_proxy_pop3_handler(ngx_event_t *rev);
+static void ngx_imap_proxy_dummy_handler(ngx_event_t *ev);
+static ngx_int_t ngx_imap_proxy_read_response(ngx_imap_session_t *s,
+ ngx_uint_t what);
+static void ngx_imap_proxy_handler(ngx_event_t *ev);
+static void ngx_imap_proxy_internal_server_error(ngx_imap_session_t *s);
+static void ngx_imap_proxy_close_session(ngx_imap_session_t *s);
+static void *ngx_imap_proxy_create_conf(ngx_conf_t *cf);
+static char *ngx_imap_proxy_merge_conf(ngx_conf_t *cf, void *parent,
+ void *child);
+
+
+#define NGX_IMAP_WAIT_OK 0
+#define NGX_IMAP_WAIT_NEXT 1
+
+
+static ngx_command_t ngx_imap_proxy_commands[] = {
+ { ngx_string("proxy"),
+ NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_FLAG,
+ ngx_conf_set_flag_slot,
+ NGX_IMAP_SRV_CONF_OFFSET,
+ offsetof(ngx_imap_proxy_conf_t, enable),
+ NULL },
+
+ ngx_null_command
+};
+
+
+static ngx_imap_module_t ngx_imap_proxy_module_ctx = {
+ NULL, /* create main configuration */
+ NULL, /* init main configuration */
+
+ ngx_imap_proxy_create_conf, /* create server configuration */
+ ngx_imap_proxy_merge_conf /* merge server configuration */
+};
+
+
+ngx_module_t ngx_imap_proxy_module = {
+ NGX_MODULE_V1,
+ &ngx_imap_proxy_module_ctx, /* module context */
+ ngx_imap_proxy_commands, /* module directives */
+ NGX_IMAP_MODULE, /* module type */
+ NULL, /* init module */
+ NULL /* init process */
+};
+
+
+void
+ngx_imap_proxy_init(ngx_imap_session_t *s, ngx_peers_t *peers)
+{
+ ngx_int_t rc;
+ ngx_imap_proxy_ctx_t *p;
+ ngx_imap_core_srv_conf_t *cscf;
+
+ p = ngx_pcalloc(s->connection->pool, sizeof(ngx_imap_proxy_ctx_t));
+ if (p == NULL) {
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+
+ s->proxy = p;
+
+ p->upstream.peers = peers;
+ p->upstream.log = s->connection->log;
+ p->upstream.log_error = NGX_ERROR_ERR;
+
+ rc = ngx_event_connect_peer(&p->upstream);
+
+ if (rc == NGX_ERROR) {
+ ngx_imap_session_internal_server_error(s);
+ return;
+ }
+
+ cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module);
+ ngx_add_timer(p->upstream.connection->read, cscf->timeout);
+
+ p->upstream.connection->data = s;
+ p->upstream.connection->pool = s->connection->pool;
+
+ s->connection->read->handler = ngx_imap_proxy_block_read;
+ p->upstream.connection->write->handler = ngx_imap_proxy_dummy_handler;
+
+ if (s->protocol == NGX_IMAP_POP3_PROTOCOL) {
+ p->upstream.connection->read->handler = ngx_imap_proxy_pop3_handler;
+ s->imap_state = ngx_pop3_start;
+
+ } else {
+ p->upstream.connection->read->handler = ngx_imap_proxy_imap_handler;
+ s->imap_state = ngx_imap_start;
+ }
+}
+
+
+static void
+ngx_imap_proxy_block_read(ngx_event_t *rev)
+{
+ ngx_connection_t *c;
+ ngx_imap_session_t *s;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, "imap proxy block read");
+
+ if (ngx_handle_read_event(rev, 0) == NGX_ERROR) {
+ c = rev->data;
+ s = c->data;
+
+ ngx_imap_proxy_close_session(s);
+ }
+}
+
+
+static void
+ngx_imap_proxy_imap_handler(ngx_event_t *rev)
+{
+ u_char *p;
+ ngx_int_t rc;
+ ngx_str_t line;
+ ngx_connection_t *c;
+ ngx_imap_session_t *s;
+ ngx_imap_core_srv_conf_t *cscf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0,
+ "imap proxy imap auth handler");
+
+ c = rev->data;
+ s = c->data;
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
+ "upstream timed out");
+ ngx_imap_proxy_internal_server_error(s);
+ return;
+ }
+
+ if (s->proxy->buffer == NULL) {
+ cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module);
+
+ s->proxy->buffer = ngx_create_temp_buf(c->pool,
+ cscf->proxy_buffer_size);
+ if (s->proxy->buffer == NULL) {
+ ngx_imap_proxy_internal_server_error(s);
+ return;
+ }
+ }
+
+ rc = ngx_imap_proxy_read_response(s, s->imap_state == ngx_imap_start ?
+ NGX_IMAP_WAIT_OK : NGX_IMAP_WAIT_NEXT);
+
+ if (rc == NGX_AGAIN) {
+ return;
+ }
+
+ if (rc == NGX_ERROR) {
+ ngx_imap_proxy_internal_server_error(s);
+ return;
+ }
+
+ switch (s->imap_state) {
+
+ case ngx_imap_start:
+ ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0,
+ "imap proxy send login");
+
+ line.len = s->tag.len + sizeof("LOGIN ") - 1
+ + 1 + NGX_SIZE_T_LEN + 1 + 2;
+ line.data = ngx_palloc(c->pool, line.len);
+ if (line.data == NULL) {
+ ngx_imap_proxy_internal_server_error(s);
+ return;
+ }
+
+ line.len = ngx_sprintf(line.data, "%VLOGIN {%uz}" CRLF,
+ &s->tag, s->login.len)
+ - line.data;
+
+ s->imap_state = ngx_imap_login;
+ break;
+
+ case ngx_imap_login:
+ ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, "imap proxy send user");
+
+ line.len = s->login.len + 1 + NGX_SIZE_T_LEN + 1 + 2;
+ line.data = ngx_palloc(c->pool, line.len);
+ if (line.data == NULL) {
+ ngx_imap_proxy_internal_server_error(s);
+ return;
+ }
+
+ line.len = ngx_sprintf(line.data, "%V{%uz}" CRLF,
+ &s->login, s->passwd.len)
+ - line.data;
+
+ s->imap_state = ngx_imap_user;
+ break;
+
+ case ngx_imap_user:
+ ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0,
+ "imap proxy send passwd");
+
+ line.len = s->passwd.len + 2;
+ line.data = ngx_palloc(c->pool, line.len);
+ if (line.data == NULL) {
+ ngx_imap_proxy_internal_server_error(s);
+ return;
+ }
+
+ p = ngx_cpymem(line.data, s->passwd.data, s->passwd.len);
+ *p++ = CR; *p = LF;
+
+ s->imap_state = ngx_imap_passwd;
+ break;
+
+ default:
+#if (NGX_SUPPRESS_WARN)
+ line.len = 0;
+ line.data = NULL;
+#endif
+ break;
+ }
+
+ if (ngx_send(c, line.data, line.len) < (ssize_t) line.len) {
+ /*
+ * we treat the incomplete sending as NGX_ERROR
+ * because it is very strange here
+ */
+ ngx_imap_proxy_internal_server_error(s);
+ return;
+ }
+
+ s->proxy->buffer->pos = s->proxy->buffer->start;
+ s->proxy->buffer->last = s->proxy->buffer->start;
+
+ if (s->imap_state == ngx_imap_passwd) {
+ s->connection->read->handler = ngx_imap_proxy_handler;
+ s->connection->write->handler = ngx_imap_proxy_handler;
+ rev->handler = ngx_imap_proxy_handler;
+ c->write->handler = ngx_imap_proxy_handler;
+ }
+}
+
+
+static void
+ngx_imap_proxy_pop3_handler(ngx_event_t *rev)
+{
+ u_char *p;
+ ngx_int_t rc;
+ ngx_str_t line;
+ ngx_connection_t *c;
+ ngx_imap_session_t *s;
+ ngx_imap_core_srv_conf_t *cscf;
+
+ ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0,
+ "imap proxy pop3 auth handler");
+
+ c = rev->data;
+ s = c->data;
+
+ if (rev->timedout) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
+ "upstream timed out");
+ ngx_imap_proxy_internal_server_error(s);
+ return;
+ }
+
+ if (s->proxy->buffer == NULL) {
+ cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module);
+
+ s->proxy->buffer = ngx_create_temp_buf(c->pool,
+ cscf->proxy_buffer_size);
+ if (s->proxy->buffer == NULL) {
+ ngx_imap_proxy_internal_server_error(s);
+ return;
+ }
+ }
+
+ rc = ngx_imap_proxy_read_response(s, NGX_IMAP_WAIT_OK);
+
+ if (rc == NGX_AGAIN) {
+ return;
+ }
+
+ if (rc == NGX_ERROR) {
+ ngx_imap_proxy_internal_server_error(s);
+ return;
+ }
+
+ switch (s->imap_state) {
+
+ case ngx_pop3_start:
+ ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, "imap proxy send user");
+
+ line.len = sizeof("USER ") - 1 + s->login.len + 2;
+ line.data = ngx_palloc(c->pool, line.len);
+ if (line.data == NULL) {
+ ngx_imap_proxy_internal_server_error(s);
+ return;
+ }
+
+ p = ngx_cpymem(line.data, "USER ", sizeof("USER ") - 1);
+ p = ngx_cpymem(p, s->login.data, s->login.len);
+ *p++ = CR; *p = LF;
+
+ s->imap_state = ngx_pop3_user;
+ break;
+
+ case ngx_pop3_user:
+ ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0, "imap proxy send pass");
+
+ line.len = sizeof("PASS ") - 1 + s->passwd.len + 2;
+ line.data = ngx_palloc(c->pool, line.len);
+ if (line.data == NULL) {
+ ngx_imap_proxy_internal_server_error(s);
+ return;
+ }
+
+ p = ngx_cpymem(line.data, "PASS ", sizeof("PASS ") - 1);
+ p = ngx_cpymem(p, s->passwd.data, s->passwd.len);
+ *p++ = CR; *p = LF;
+
+ s->imap_state = ngx_pop3_passwd;
+ break;
+
+ default:
+#if (NGX_SUPPRESS_WARN)
+ line.len = 0;
+ line.data = NULL;
+#endif
+ break;
+ }
+
+ if (ngx_send(c, line.data, line.len) < (ssize_t) line.len) {
+ /*
+ * we treat the incomplete sending as NGX_ERROR
+ * because it is very strange here
+ */
+ ngx_imap_proxy_internal_server_error(s);
+ return;
+ }
+
+ s->proxy->buffer->pos = s->proxy->buffer->start;
+ s->proxy->buffer->last = s->proxy->buffer->start;
+
+ if (s->imap_state == ngx_pop3_passwd) {
+ s->connection->read->handler = ngx_imap_proxy_handler;
+ s->connection->write->handler = ngx_imap_proxy_handler;
+ rev->handler = ngx_imap_proxy_handler;
+ c->write->handler = ngx_imap_proxy_handler;
+ }
+}
+
+
+static void
+ngx_imap_proxy_dummy_handler(ngx_event_t *ev)
+{
+ ngx_log_debug0(NGX_LOG_DEBUG_IMAP, ev->log, 0, "imap proxy dummy handler");
+}
+
+
+static ngx_int_t
+ngx_imap_proxy_read_response(ngx_imap_session_t *s, ngx_uint_t what)
+{
+ u_char *p;
+ ssize_t n;
+ ngx_buf_t *b;
+
+ b = s->proxy->buffer;
+
+ n = ngx_recv(s->proxy->upstream.connection, b->last, b->end - b->last);
+
+ if (n == NGX_ERROR || n == 0) {
+ return NGX_ERROR;
+ }
+
+ if (n == NGX_AGAIN) {
+ return NGX_AGAIN;
+ }
+
+ b->last += n;
+
+ if (b->last - b->pos < 5) {
+ return NGX_AGAIN;
+ }
+
+ if (*(b->last - 2) != CR || *(b->last - 1) != LF) {
+ if (b->last == b->end) {
+ *(b->last - 1) = '\0';
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "upstream sent too long response line: \"%s\"",
+ b->pos);
+ return NGX_IMAP_PROXY_INVALID;
+ }
+
+ return NGX_AGAIN;
+ }
+
+ p = b->pos;
+
+ if (s->protocol == NGX_IMAP_POP3_PROTOCOL) {
+ if (p[0] == '+' && p[1] == 'O' && p[2] == 'K') {
+ return NGX_OK;
+ }
+
+ if (p[0] == '-' && p[1] == 'E' && p[2] == 'R' && p[3] == 'R') {
+ return NGX_IMAP_PROXY_ERROR;
+ }
+
+ } else {
+ if (what == NGX_IMAP_WAIT_OK) {
+ if (p[0] == '*' && p[1] == ' ' && p[2] == 'O' && p[3] == 'K') {
+ return NGX_OK;
+ }
+
+ } else {
+ if (p[0] == '+' && p[1] == ' ' && p[2] == 'O' && p[3] == 'K') {
+ return NGX_OK;
+ }
+ }
+ }
+
+ *(b->last - 2) = '\0';
+ ngx_log_error(NGX_LOG_ERR, s->connection->log, 0,
+ "upstream sent invalid response: \"%s\"", p);
+
+ return NGX_IMAP_PROXY_INVALID;
+}
+
+
+static void
+ngx_imap_proxy_handler(ngx_event_t *ev)
+{
+ size_t size;
+ ssize_t n;
+ ngx_buf_t *b;
+ ngx_uint_t again, do_write;
+ ngx_connection_t *c, *src, *dst;
+ ngx_imap_session_t *s;
+
+ c = ev->data;
+ s = c->data;
+
+ if (ev->timedout) {
+ if (c == s->connection) {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
+ "client timed out");
+ } else {
+ ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
+ "upstream timed out");
+ }
+
+ ngx_imap_proxy_close_session(s);
+ return;
+ }
+
+ if (c == s->connection) {
+ if (ev->write) {
+ src = s->proxy->upstream.connection;
+ dst = c;
+ b = s->proxy->buffer;
+
+ } else {
+ src = c;
+ dst = s->proxy->upstream.connection;
+ b = s->buffer;
+ }
+
+ } else {
+ if (ev->write) {
+ src = s->connection;
+ dst = c;
+ b = s->buffer;
+
+ } else {
+ src = c;
+ dst = s->connection;
+ b = s->proxy->buffer;
+ }
+ }
+
+ do_write = ev->write ? 1 : 0;
+
+ ngx_log_debug3(NGX_LOG_DEBUG_IMAP, ev->log, 0,
+ "imap proxy handler: %d, #%d > #%d",
+ do_write, src->fd, dst->fd);
+
+ do {
+ again = 0;
+
+ if (do_write == 1) {
+
+ size = b->last - b->pos;
+
+ if (size && dst->write->ready) {
+ n = ngx_send(dst, b->pos, size);
+
+ if (n == NGX_ERROR) {
+ ngx_imap_proxy_close_session(s);
+ return;
+ }
+
+ if (n > 0) {
+ again = 1;
+ b->pos += n;
+
+ if (b->pos == b->last) {
+ b->pos = b->start;
+ b->last = b->start;
+ }
+ }
+
+ if (n == NGX_AGAIN || n < (ssize_t) size) {
+ if (ngx_handle_write_event(dst->write, /* TODO: LOWAT */ 0)
+ == NGX_ERROR)
+ {
+ ngx_imap_proxy_close_session(s);
+ return;
+ }
+ }
+ }
+ }
+
+ size = b->end - b->last;
+
+ if (size && src->read->ready) {
+ n = ngx_recv(src, b->last, size);
+
+ if (n == NGX_ERROR || n == 0) {
+ ngx_imap_proxy_close_session(s);
+ return;
+ }
+
+ if (n > 0) {
+ again = 1;
+ do_write = 1;
+ b->last += n;
+ }
+
+ if (n == NGX_AGAIN || n < (ssize_t) size) {
+ if (ngx_handle_read_event(src->read, 0) == NGX_ERROR) {
+ ngx_imap_proxy_close_session(s);
+ return;
+ }
+ }
+ }
+
+ } while (again);
+}
+
+
+static void
+ngx_imap_proxy_internal_server_error(ngx_imap_session_t *s)
+{
+ if (s->proxy->upstream.connection) {
+ ngx_log_debug1(NGX_LOG_DEBUG_IMAP, s->connection->log, 0,
+ "close imap proxy connection: %d",
+ s->proxy->upstream.connection->fd);
+
+ ngx_close_connection(s->proxy->upstream.connection);
+ }
+
+ ngx_imap_session_internal_server_error(s);
+}
+
+
+static void
+ngx_imap_proxy_close_session(ngx_imap_session_t *s)
+{
+ if (s->proxy->upstream.connection) {
+ ngx_log_debug1(NGX_LOG_DEBUG_IMAP, s->connection->log, 0,
+ "close imap proxy connection: %d",
+ s->proxy->upstream.connection->fd);
+
+ ngx_close_connection(s->proxy->upstream.connection);
+ }
+
+ ngx_imap_close_connection(s->connection);
+}
+
+
+static void *
+ngx_imap_proxy_create_conf(ngx_conf_t *cf)
+{
+ ngx_imap_proxy_conf_t *pcf;
+
+ pcf = ngx_pcalloc(cf->pool, sizeof(ngx_imap_proxy_conf_t));
+ if (pcf == NULL) {
+ return NGX_CONF_ERROR;
+ }
+
+ pcf->enable = NGX_CONF_UNSET;
+
+ return pcf;
+}
+
+
+static char *
+ngx_imap_proxy_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+ ngx_imap_proxy_conf_t *prev = parent;
+ ngx_imap_proxy_conf_t *conf = child;
+
+ ngx_conf_merge_msec_value(conf->enable, prev->enable, 0);
+
+ return NGX_CONF_OK;
+}
diff --git a/src/os/unix/ngx_linux_config.h b/src/os/unix/ngx_linux_config.h
index 5bad6451c..7bd262a19 100644
--- a/src/os/unix/ngx_linux_config.h
+++ b/src/os/unix/ngx_linux_config.h
@@ -88,6 +88,11 @@ extern ssize_t sendfile(int s, int fd, int32_t *offset, size_t size);
#endif
+#ifndef NGX_HAVE_GNU_CRYPT_R
+#define NGX_HAVE_GNU_CRYPT_R 1
+#endif
+
+
#ifndef NGX_HAVE_INHERITED_NONBLOCK
#define NGX_HAVE_INHERITED_NONBLOCK 0
#endif
diff --git a/src/os/unix/ngx_user.c b/src/os/unix/ngx_user.c
index 22eafd20e..cbb08a852 100644
--- a/src/os/unix/ngx_user.c
+++ b/src/os/unix/ngx_user.c
@@ -20,7 +20,7 @@
#if (NGX_CRYPT)
-#if (NGX_LINUX)
+#if (NGX_HAVE_GNU_CRYPT_R)
ngx_int_t
ngx_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
@@ -33,6 +33,8 @@ ngx_crypt(ngx_pool_t *pool, u_char *key, u_char *salt, u_char **encrypted)
ngx_set_errno(0);
cd.initialized = 0;
+ /* work around the glibc-2.2.5 bug */
+ cd.current_saltbits = 0;
value = crypt_r((char *) key, (char *) salt, &cd);