changeset 88:e916a291e9aa NGINX_0_1_44

nginx 0.1.44 *) Feature: the IMAP/POP3 proxy supports SSL. *) Feature: the "proxy_timeout" directive of the ngx_imap_proxy_module. *) Feature: the "userid_mark" directive. *) Feature: the $remote_user variable value is determined independently of authorization use.
author Igor Sysoev <http://sysoev.ru>
date Tue, 06 Sep 2005 00:00:00 +0400
parents 5b7ec80c3c40
children 7ed9767f1c4e
files CHANGES CHANGES.ru auto/modules auto/options auto/sources src/core/nginx.c src/core/nginx.h src/core/ngx_connection.h src/event/ngx_event_accept.c src/event/ngx_event_connect.c src/event/ngx_event_openssl.c src/event/ngx_event_openssl.h src/http/modules/ngx_http_auth_basic_module.c src/http/modules/ngx_http_proxy_module.c src/http/modules/ngx_http_userid_filter_module.c src/http/ngx_http_core_module.c src/http/ngx_http_core_module.h src/http/ngx_http_request.c src/http/ngx_http_special_response.c src/http/ngx_http_variables.c src/imap/ngx_imap.h src/imap/ngx_imap_auth_http_module.c src/imap/ngx_imap_core_module.c src/imap/ngx_imap_handler.c src/imap/ngx_imap_proxy_module.c src/imap/ngx_imap_ssl_module.c src/imap/ngx_imap_ssl_module.h src/os/unix/ngx_freebsd_config.h src/os/unix/ngx_freebsd_init.c src/os/unix/ngx_freebsd_sendfile_chain.c src/os/unix/ngx_linux_config.h src/os/unix/ngx_linux_init.c src/os/unix/ngx_os.h src/os/unix/ngx_posix_config.h src/os/unix/ngx_posix_init.c src/os/unix/ngx_process.c src/os/unix/ngx_process.h src/os/unix/ngx_solaris_config.h src/os/unix/ngx_solaris_init.c
diffstat 39 files changed, 1151 insertions(+), 554 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,3 +1,15 @@
+
+Changes with nginx 0.1.44                                        06 Sep 2005
+
+    *) Feature: the IMAP/POP3 proxy supports SSL.
+
+    *) Feature: the "proxy_timeout" directive.
+
+    *) Feature: the "userid_mark" directive.
+
+    *) Feature: the $remote_user variable value is determined independently 
+       of authorization use.
+
 
 Changes with nginx 0.1.43                                        30 Aug 2005
 
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,3 +1,15 @@
+
+Изменения в nginx 0.1.44                                          06.09.2005
+
+    *) Добавление: IMAP/POP3 прокси поддерживает SSL.
+
+    *) Добавление: директива proxy_timeout.
+
+    *) Добавление: директива userid_mark.
+
+    *) Добавление: значение переменной $remote_user определяется независимо 
+       от того, используется ли авторизация или нет.
+
 
 Изменения в nginx 0.1.43                                          30.08.2005
 
--- a/auto/modules
+++ b/auto/modules
@@ -228,6 +228,14 @@ fi
 if [ $IMAP = YES ]; then
     modules="$modules $IMAP_MODULES"
 
+    if [ $IMAP_SSL = YES ]; then
+        modules="$modules $IMAP_SSL_MODULE"
+        IMAP_DEPS="$IMAP_DEPS $IMAP_SSL_DEPS"
+        IMAP_SRCS="$IMAP_SRCS $IMAP_SSL_SRCS"
+        have=NGX_IMAP_SSL . auto/have
+        USE_OPENSSL=YES
+    fi
+
     modules="$modules $IMAP_AUTH_HTTP_MODULE"
     IMAP_SRCS="$IMAP_SRCS $IMAP_AUTH_HTTP_SRCS"
 
--- a/auto/options
+++ b/auto/options
@@ -64,6 +64,7 @@ HTTP_FASTCGI=YES
 HTTP_STUB_STATUS=NO
 
 IMAP=NO
+IMAP_SSL=NO
 
 NGX_ADDONS=
 
@@ -141,6 +142,7 @@ do
         --with-http_stub_status_module)  HTTP_STUB_STATUS=YES       ;;
 
         --with-imap)                     IMAP=YES                   ;;
+        --with-imap_ssl_module)          IMAP_SSL=YES               ;;
 
         --add-module=*)                  NGX_ADDONS="$NGX_ADDONS $value" ;;
 
--- a/auto/sources
+++ b/auto/sources
@@ -346,6 +346,10 @@ IMAP_SRCS="src/imap/ngx_imap.c \
            src/imap/ngx_imap_handler.c \
            src/imap/ngx_imap_parse.c"
 
+IMAP_SSL_MODULE="ngx_imap_ssl_module"
+IMAP_SSL_DEPS="src/imap/ngx_imap_ssl_module.h"
+IMAP_SSL_SRCS="src/imap/ngx_imap_ssl_module.c"
+
 IMAP_AUTH_HTTP_MODULE="ngx_imap_auth_http_module"
 IMAP_AUTH_HTTP_SRCS="src/imap/ngx_imap_auth_http_module.c"
 
--- a/src/core/nginx.c
+++ b/src/core/nginx.c
@@ -236,6 +236,10 @@ main(int argc, char *const *argv)
 
 #else
 
+    if (ngx_init_signals(cycle->log) == NGX_ERROR) {
+        return 1;
+    }
+
     if (!ngx_inherited && ccf->daemon) {
         if (ngx_daemon(cycle->log) == NGX_ERROR) {
             return 1;
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,7 +8,7 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define NGINX_VER          "nginx/0.1.43"
+#define NGINX_VER          "nginx/0.1.44"
 
 #define NGINX_VAR          "NGINX"
 #define NGX_NEWPID_EXT     ".newbin"
--- a/src/core/ngx_connection.h
+++ b/src/core/ngx_connection.h
@@ -92,6 +92,7 @@ struct ngx_connection_s {
     ngx_socket_t        fd;
 
     ngx_recv_pt         recv;
+    ngx_send_pt         send;
     ngx_send_chain_pt   send_chain;
 
     ngx_listening_t    *listening;
--- a/src/event/ngx_event_accept.c
+++ b/src/event/ngx_event_accept.c
@@ -256,6 +256,7 @@ ngx_event_accept(ngx_event_t *ev)
         c->servers = ls->servers;
 
         c->recv = ngx_recv;
+        c->send = ngx_send;
         c->send_chain = ngx_send_chain;
 
         c->log = log;
--- a/src/event/ngx_event_connect.c
+++ b/src/event/ngx_event_connect.c
@@ -229,6 +229,10 @@ ngx_event_connect_peer(ngx_peer_connecti
     c->write = wev;
     wev->write = 1;
 
+    c->recv = ngx_recv;
+    c->send = ngx_send;
+    c->send_chain = ngx_send_chain;
+
     c->log = pc->log;
     rev->log = pc->log;
     wev->log = pc->log;
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -13,7 +13,6 @@
 
 static ngx_int_t ngx_ssl_handle_recv(ngx_connection_t *c, int n);
 static void ngx_ssl_write_handler(ngx_event_t *wev);
-static ssize_t ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size);
 static void ngx_ssl_read_handler(ngx_event_t *rev);
 
 
@@ -209,8 +208,10 @@ ngx_ssl_handle_recv(ngx_connection_t *c,
     }
 
     if (sslerr == SSL_ERROR_WANT_WRITE) {
-        ngx_log_error(NGX_LOG_ALERT, c->log, err,
-                      "SSL wants to write%s", handshake);
+
+        ngx_log_error(NGX_LOG_INFO, c->log, err,
+                      "client does SSL %shandshake",
+                      SSL_is_init_finished(c->ssl->ssl) ? "re" : "");
 
         c->write->ready = 0;
 
@@ -391,12 +392,11 @@ ngx_ssl_send_chain(ngx_connection_t *c, 
 }
 
 
-static ssize_t
+ssize_t
 ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size)
 {
-    int         n, sslerr;
-    ngx_err_t   err;
-    char       *handshake;
+    int        n, sslerr;
+    ngx_err_t  err;
 
     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL to write: %d", size);
 
@@ -405,6 +405,47 @@ ngx_ssl_write(ngx_connection_t *c, u_cha
     ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0, "SSL_write: %d", n);
 
     if (n > 0) {
+
+#if (NGX_DEBUG)
+
+        if (!c->ssl->handshaked && SSL_is_init_finished(c->ssl->ssl)) {
+            char         buf[129], *s, *d;
+            SSL_CIPHER  *cipher;
+
+            c->ssl->handshaked = 1;
+
+            cipher = SSL_get_current_cipher(c->ssl->ssl);
+
+            if (cipher) {
+                SSL_CIPHER_description(cipher, &buf[1], 128);
+
+                for (s = &buf[1], d = buf; *s; s++) {
+                    if (*s == ' ' && *d == ' ') {
+                        continue;
+                    }
+
+                    if (*s == LF || *s == CR) {
+                        continue;
+                    }
+
+                    *++d = *s;
+                }
+
+                if (*d != ' ') {
+                    d++;
+                }
+
+                *d = '\0';
+
+                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                               "SSL cipher: \"%s\"", &buf[1]); 
+            } else {
+                ngx_log_debug0(NGX_LOG_DEBUG_EVENT, c->log, 0,
+                               "SSL no shared ciphers"); 
+            }
+        }
+#endif
+
         if (c->ssl->saved_read_handler) {
 
             c->read->handler = c->ssl->saved_read_handler;
@@ -440,15 +481,9 @@ ngx_ssl_write(ngx_connection_t *c, u_cha
 
     if (sslerr == SSL_ERROR_WANT_READ) {
 
-        if (!SSL_is_init_finished(c->ssl->ssl)) {
-            handshake = " in SSL handshake";
-
-        } else {
-            handshake = "";
-        }
-
-        ngx_log_error(NGX_LOG_ALERT, c->log, err,
-                      "SSL wants to read%s", handshake);
+        ngx_log_error(NGX_LOG_INFO, c->log, err,
+                      "client does SSL %shandshake",
+                      SSL_is_init_finished(c->ssl->ssl) ? "re" : "");
 
         c->read->ready = 0;
 
--- a/src/event/ngx_event_openssl.h
+++ b/src/event/ngx_event_openssl.h
@@ -49,6 +49,7 @@ ngx_int_t ngx_ssl_create_session(ngx_ssl
 #define ngx_ssl_handshake(c)     NGX_OK
 
 ssize_t ngx_ssl_recv(ngx_connection_t *c, u_char *buf, size_t size);
+ssize_t ngx_ssl_write(ngx_connection_t *c, u_char *data, size_t size);
 ngx_chain_t *ngx_ssl_send_chain(ngx_connection_t *c, ngx_chain_t *in,
     off_t limit);
 ngx_int_t ngx_ssl_shutdown(ngx_connection_t *c);
--- a/src/http/modules/ngx_http_auth_basic_module.c
+++ b/src/http/modules/ngx_http_auth_basic_module.c
@@ -90,8 +90,9 @@ ngx_http_auth_basic_handler(ngx_http_req
     off_t                            offset;
     ssize_t                          n;
     ngx_fd_t                         fd;
-    ngx_str_t                        auth, encoded, pwd;
-    ngx_uint_t                       i, login, len, left, passwd;
+    ngx_int_t                        rc;
+    ngx_str_t                        pwd;
+    ngx_uint_t                       i, login, left, passwd;
     ngx_file_t                       file;
     ngx_http_auth_basic_ctx_t       *ctx;
     ngx_http_auth_basic_loc_conf_t  *alcf;
@@ -115,57 +116,16 @@ ngx_http_auth_basic_handler(ngx_http_req
                                                  &alcf->realm);
     }
 
-    if (r->headers_in.authorization == NULL) {
-        return ngx_http_auth_basic_set_realm(r, &alcf->realm);
-    }
-
-    encoded = r->headers_in.authorization->value;
+    rc = ngx_http_auth_basic_user(r);
 
-    if (encoded.len < sizeof("Basic ") - 1
-        || ngx_strncasecmp(encoded.data, "Basic ", sizeof("Basic ") - 1) != 0)
-    {
-        return ngx_http_auth_basic_set_realm(r, &alcf->realm);
-    }
-
-    encoded.len -= sizeof("Basic ") - 1;
-    encoded.data += sizeof("Basic ") - 1;
-
-    while (encoded.len && encoded.data[0] == ' ') {
-        encoded.len--;
-        encoded.data++;
-    }
-
-    if (encoded.len == 0) {
+    if (rc == NGX_DECLINED) {
         return ngx_http_auth_basic_set_realm(r, &alcf->realm);
     }
 
-    auth.len = ngx_base64_decoded_length(encoded.len);
-    auth.data = ngx_palloc(r->pool, auth.len + 1);
-    if (auth.data == NULL) {
+    if (rc == NGX_ERROR) {
         return NGX_HTTP_INTERNAL_SERVER_ERROR;
     }
 
-    if (ngx_decode_base64(&auth, &encoded) != NGX_OK) {
-        return ngx_http_auth_basic_set_realm(r, &alcf->realm);
-    }
-
-    auth.data[auth.len] = '\0';
-
-    for (len = 0; len < auth.len; len++) {
-        if (auth.data[len] == ':') {
-            break;
-        }
-    }
-
-    if (len == auth.len) {
-        return ngx_http_auth_basic_set_realm(r, &alcf->realm);
-    }
-
-    r->headers_in.user.len = len;
-    r->headers_in.user.data = auth.data;
-    r->headers_in.passwd.len = auth.len - len - 1;
-    r->headers_in.passwd.data = &auth.data[len + 1];
-
     fd = ngx_open_file(alcf->user_file.data, NGX_FILE_RDONLY, NGX_FILE_OPEN);
 
     if (fd == NGX_INVALID_FILE) {
@@ -208,12 +168,12 @@ ngx_http_auth_basic_handler(ngx_http_req
                     break;
                 }
 
-                if (buf[i] != auth.data[login]) {
+                if (buf[i] != r->headers_in.user.data[login]) {
                     state = sw_skip;
                     break;
                 }
 
-                if (login == len) {
+                if (login == r->headers_in.user.len) {
                     state = sw_passwd;
                     passwd = i + 1;
                 }
--- a/src/http/modules/ngx_http_proxy_module.c
+++ b/src/http/modules/ngx_http_proxy_module.c
@@ -424,10 +424,10 @@ ngx_http_proxy_create_request(ngx_http_r
 
     escape = 0;
 
-    loc_len = r->valid_location ? u->conf->location->len : 1;
+    loc_len = r->valid_location ? u->conf->location->len - 1 : 0;
 
     if (plcf->upstream.pass_unparsed_uri && r->valid_unparsed_uri) {
-        len += r->unparsed_uri.len - 1;
+        len += r->unparsed_uri.len;
 
     } else {
         if (r->quoted_uri) {
@@ -508,11 +508,11 @@ ngx_http_proxy_create_request(ngx_http_r
                              r->method_name.len + 1);
     }
 
-    b->last = ngx_cpymem(b->last, u->conf->uri.data, u->conf->uri.len);
+    b->last = ngx_cpymem(b->last, u->conf->uri.data, u->conf->uri.len - 1);
 
     if (plcf->upstream.pass_unparsed_uri && r->valid_unparsed_uri) {
-        b->last = ngx_cpymem(b->last, r->unparsed_uri.data + 1,
-                             r->unparsed_uri.len - 1);
+        b->last = ngx_cpymem(b->last, r->unparsed_uri.data,
+                             r->unparsed_uri.len);
     } else {
         if (escape) {
             ngx_escape_uri(b->last, r->uri.data + loc_len,
--- a/src/http/modules/ngx_http_userid_filter_module.c
+++ b/src/http/modules/ngx_http_userid_filter_module.c
@@ -26,18 +26,22 @@ typedef struct {
     ngx_str_t   name;
     ngx_str_t   domain;
     ngx_str_t   path;
+    ngx_str_t   p3p;
+
     time_t      expires;
-    ngx_str_t   p3p;
+
+    u_char      mark;
 } ngx_http_userid_conf_t;
 
 
 typedef struct {
     uint32_t    uid_got[4];
     uint32_t    uid_set[4];
+    ngx_str_t   cookie;
 } ngx_http_userid_ctx_t;
 
 
-static ngx_int_t ngx_http_userid_get_uid(ngx_http_request_t *r,
+static void ngx_http_userid_get_uid(ngx_http_request_t *r,
     ngx_http_userid_ctx_t *ctx, ngx_http_userid_conf_t *conf);
 static ngx_int_t ngx_http_userid_set_uid(ngx_http_request_t *r,
     ngx_http_userid_ctx_t *ctx, ngx_http_userid_conf_t *conf);
@@ -61,6 +65,9 @@ static char *ngx_http_userid_path(ngx_co
 static char *ngx_http_userid_expires(ngx_conf_t *cf, ngx_command_t *cmd,
     void *conf);
 static char *ngx_http_userid_p3p(ngx_conf_t *cf, void *post, void *data);
+static char *ngx_http_userid_mark(ngx_conf_t *cf, ngx_command_t *cmd,
+    void *conf);
+
 
 
 static uint32_t  sequencer_v1 = 1;
@@ -84,7 +91,6 @@ static ngx_conf_enum_t  ngx_http_userid_
 
 static ngx_conf_post_handler_pt  ngx_http_userid_domain_p =
     ngx_http_userid_domain;
-
 static ngx_conf_post_handler_pt  ngx_http_userid_path_p = ngx_http_userid_path;
 static ngx_conf_post_handler_pt  ngx_http_userid_p3p_p = ngx_http_userid_p3p;
 
@@ -99,28 +105,28 @@ static ngx_command_t  ngx_http_userid_co
       ngx_http_userid_state },
 
     { ngx_string("userid_service"),
-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_num_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
       offsetof(ngx_http_userid_conf_t, service),
       NULL },
 
     { ngx_string("userid_name"),
-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_str_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
       offsetof(ngx_http_userid_conf_t, name),
       NULL },
 
     { ngx_string("userid_domain"),
-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_str_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
       offsetof(ngx_http_userid_conf_t, domain),
       &ngx_http_userid_domain_p },
 
     { ngx_string("userid_path"),
-      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_FLAG,
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
       ngx_conf_set_str_slot,
       NGX_HTTP_LOC_CONF_OFFSET,
       offsetof(ngx_http_userid_conf_t, path),
@@ -140,6 +146,13 @@ static ngx_command_t  ngx_http_userid_co
       offsetof(ngx_http_userid_conf_t, p3p),
       &ngx_http_userid_p3p_p },
 
+    { ngx_string("userid_mark"),
+      NGX_HTTP_MAIN_CONF|NGX_HTTP_SRV_CONF|NGX_HTTP_LOC_CONF|NGX_CONF_TAKE1,
+      ngx_http_userid_mark,
+      NGX_HTTP_LOC_CONF_OFFSET,
+      0,
+      NULL },
+
       ngx_null_command
 };
 
@@ -205,15 +218,24 @@ ngx_http_userid_filter(ngx_http_request_
 
     ngx_http_set_ctx(r, ctx, ngx_http_userid_filter_module);
 
-
-    rc = ngx_http_userid_get_uid(r, ctx, conf);
+    ngx_http_userid_get_uid(r, ctx, conf);
 
-    if (rc != NGX_OK) {
-        return rc;
+    if (conf->enable == NGX_HTTP_USERID_LOG) {
+        return ngx_http_next_header_filter(r);
     }
 
-    if (conf->enable == NGX_HTTP_USERID_LOG || ctx->uid_got[3] != 0) {
-        return ngx_http_next_header_filter(r);
+    if (ctx->uid_got[3] != 0) {
+        if (conf->mark == '\0') {
+            return ngx_http_next_header_filter(r);
+
+        } else {
+            if (ctx->cookie.len > 23
+                && ctx->cookie.data[22] == conf->mark
+                && ctx->cookie.data[23] == '=')
+            {
+                return ngx_http_next_header_filter(r);
+            }
+        }
     }
 
     rc = ngx_http_userid_set_uid(r, ctx, conf);
@@ -226,7 +248,7 @@ ngx_http_userid_filter(ngx_http_request_
 }
 
 
-static ngx_int_t
+static void
 ngx_http_userid_get_uid(ngx_http_request_t *r, ngx_http_userid_ctx_t *ctx,
     ngx_http_userid_conf_t *conf)
 {
@@ -235,23 +257,29 @@ ngx_http_userid_get_uid(ngx_http_request
     ngx_table_elt_t  **cookies;
 
     n = ngx_http_parse_multi_header_lines(&r->headers_in.cookies, &conf->name,
-                                          &src);
+                                          &ctx->cookie);
     if (n == NGX_DECLINED) {
-        return NGX_OK;
+        return;
     }
 
-    if (src.len < 22) {
+    ngx_log_debug1(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                   "uid cookie: \"%V\"", &ctx->cookie);
+
+    if (ctx->cookie.len < 22) {
         cookies = r->headers_in.cookies.elts;
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                       "client sent too short userid cookie \"%V\"",
                       &cookies[n]->value);
-        return NGX_OK;
+        return;
     }
 
+    src = ctx->cookie;
+
     /*
-     * we have to limit encoded string to 22 characters
-     * because there are already the millions cookies with a garbage
-     * instead of the correct base64 trail "=="
+     * we have to limit the encoded string to 22 characters because
+     *  1) cookie may be marked by "userid_mark",
+     *  2) and there are already the millions cookies with a garbage
+     *     instead of the correct base64 trail "=="
      */
 
     src.len = 22;
@@ -263,15 +291,13 @@ ngx_http_userid_get_uid(ngx_http_request
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                       "client sent invalid userid cookie \"%V\"",
                       &cookies[n]->value);
-        return NGX_OK;
+        return;
     }
 
     ngx_log_debug4(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
                    "uid: %08XD%08XD%08XD%08XD",
                    ctx->uid_got[0], ctx->uid_got[1],
                    ctx->uid_got[2], ctx->uid_got[3]);
-
-    return NGX_OK;
 }
 
 
@@ -286,47 +312,58 @@ ngx_http_userid_set_uid(ngx_http_request
     ngx_str_t            src, dst;
     ngx_table_elt_t     *set_cookie, *p3p;
 
-    /* TODO: mutex for sequencers */
+    /*
+     * TODO: in the threaded mode the sequencers should be in TLS and their
+     * ranges should be divided between threads
+     */
+
+    if (ctx->uid_got[3] == 0) {
 
-    if (conf->enable == NGX_HTTP_USERID_V1) {
-        if (conf->service == NGX_CONF_UNSET) {
-            ctx->uid_set[0] = 0;
+        if (conf->enable == NGX_HTTP_USERID_V1) {
+            if (conf->service == NGX_CONF_UNSET) {
+                ctx->uid_set[0] = 0;
+            } else {
+                ctx->uid_set[0] = conf->service;
+            }
+            ctx->uid_set[1] = ngx_time();
+            ctx->uid_set[2] = ngx_pid;
+            ctx->uid_set[3] = sequencer_v1;
+            sequencer_v1 += 0x100;
+
         } else {
-            ctx->uid_set[0] = htonl(conf->service);
-        }
+            if (conf->service == NGX_CONF_UNSET) {
+                if (r->in_addr == 0) {
+                    slen = sizeof(struct sockaddr_in);
+                    if (getsockname(r->connection->fd,
+                                    (struct sockaddr *) &sin, &slen) == -1)
+                    {
+                        ngx_log_error(NGX_LOG_CRIT, r->connection->log,
+                                      ngx_socket_errno, "getsockname() failed");
+                    }
+
+                    r->in_addr = sin.sin_addr.s_addr;
+                }
 
-        ctx->uid_set[1] = ngx_time();
-        ctx->uid_set[2] = ngx_pid;
-        ctx->uid_set[3] = sequencer_v1;
-        sequencer_v1 += 0x100;
+                ctx->uid_set[0] = htonl(r->in_addr);
+
+            } else {
+                ctx->uid_set[0] = htonl(conf->service);
+            }
+
+            ctx->uid_set[1] = htonl(ngx_time());
+            ctx->uid_set[2] = htonl(ngx_pid);
+            ctx->uid_set[3] = htonl(sequencer_v2);
+            sequencer_v2 += 0x100;
+            if (sequencer_v2 < 0x03030302) {
+                sequencer_v2 = 0x03030302;
+            }
+        }
 
     } else {
-        if (conf->service == NGX_CONF_UNSET) {
-            if (r->in_addr == 0) {
-                slen = sizeof(struct sockaddr_in);
-                if (getsockname(r->connection->fd,
-                                (struct sockaddr *) &sin, &slen) == -1)
-                {
-                    ngx_log_error(NGX_LOG_CRIT, r->connection->log,
-                                  ngx_socket_errno, "getsockname() failed");
-                }
-
-                r->in_addr = sin.sin_addr.s_addr;
-            }
-
-            ctx->uid_set[0] = htonl(r->in_addr);
-
-        } else {
-            ctx->uid_set[0] = htonl(conf->service);
-        }
-
-        ctx->uid_set[1] = htonl(ngx_time());
-        ctx->uid_set[2] = htonl(ngx_pid);
-        ctx->uid_set[3] = htonl(sequencer_v2);
-        sequencer_v2 += 0x100;
-        if (sequencer_v2 < 0x03030302) {
-            sequencer_v2 = 0x03030302;
-        }
+        ctx->uid_set[0] = ctx->uid_got[0];
+        ctx->uid_set[1] = ctx->uid_got[1];
+        ctx->uid_set[2] = ctx->uid_got[2];
+        ctx->uid_set[3] = ctx->uid_got[3];
     }
 
     len = conf->name.len + 1 + ngx_base64_encoded_length(16) + conf->path.len;
@@ -347,13 +384,24 @@ ngx_http_userid_set_uid(ngx_http_request
     p = ngx_cpymem(cookie, conf->name.data, conf->name.len);
     *p++ = '=';
 
-    src.len = 16;
-    src.data = (u_char *) ctx->uid_set;
-    dst.data = p;
+    if (ctx->uid_got[3] == 0) {
+        src.len = 16;
+        src.data = (u_char *) ctx->uid_set;
+        dst.data = p;
+
+        ngx_encode_base64(&dst, &src);
+
+        p += dst.len;
 
-    ngx_encode_base64(&dst, &src);
+        if (conf->mark) {
+            *(p - 2) = conf->mark;
+        }
 
-    p += dst.len;
+    } else {
+        p = ngx_cpymem(p, ctx->cookie.data, 22);
+        *p++ = conf->mark;
+        *p++ = '=';
+    }
 
     if (conf->expires == NGX_HTTP_USERID_MAX_EXPIRES) {
         p = ngx_cpymem(p, expires, sizeof(expires) - 1);
@@ -545,6 +593,7 @@ ngx_http_userid_create_conf(ngx_conf_t *
     conf->enable = NGX_CONF_UNSET_UINT;
     conf->service = NGX_CONF_UNSET;
     conf->expires = NGX_CONF_UNSET;
+    conf->mark = (u_char) '\xFF';
 
     return conf;
 }   
@@ -567,6 +616,14 @@ ngx_http_userid_merge_conf(ngx_conf_t *c
     ngx_conf_merge_value(conf->service, prev->service, NGX_CONF_UNSET);
     ngx_conf_merge_sec_value(conf->expires, prev->expires, 0);
 
+    if (conf->mark == (u_char) '\xFF') {
+        if (prev->mark == (u_char) '\xFF') {
+            conf->mark = '\0';
+        } else {
+            conf->mark = prev->mark;
+        }
+    }
+
     return NGX_CONF_OK;
 }
 
@@ -627,7 +684,7 @@ ngx_http_userid_expires(ngx_conf_t *cf, 
 {
     ngx_http_userid_conf_t *ucf = conf;
 
-    ngx_str_t   *value;
+    ngx_str_t  *value;
 
     if (ucf->expires != NGX_CONF_UNSET) {
         return "is duplicate";
@@ -670,3 +727,36 @@ ngx_http_userid_p3p(ngx_conf_t *cf, void
 
     return NGX_CONF_OK;
 }
+
+
+static char *
+ngx_http_userid_mark(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
+{
+    ngx_http_userid_conf_t *ucf = conf;
+
+    ngx_str_t  *value;
+
+    if (ucf->mark != (u_char) '\xFF') {
+        return "is duplicate";
+    }
+
+    value = cf->args->elts;
+
+    if (ngx_strcmp(value[1].data, "off") == 0) {
+        ucf->mark = '\0';
+        return NGX_CONF_OK;
+    }
+
+    if (value[1].len != 1
+        || !((value[1].data[0] >= '0' && value[1].data[0] <= '9')
+              || (value[1].data[0] >= 'A' && value[1].data[0] <= 'Z')
+              || (value[1].data[0] >= 'a' && value[1].data[0] <= 'z')
+              || value[1].data[0] == '='))
+    {
+        return "value must be \"off\" or a single letter, digit or \"=\"";
+    }
+
+    ucf->mark = value[1].data[0];
+
+    return NGX_CONF_OK;
+}
--- a/src/http/ngx_http_core_module.c
+++ b/src/http/ngx_http_core_module.c
@@ -931,6 +931,76 @@ ngx_http_set_exten(ngx_http_request_t *r
 
 
 ngx_int_t
+ngx_http_auth_basic_user(ngx_http_request_t *r)
+{
+    ngx_str_t   auth, encoded;
+    ngx_uint_t  len;
+
+    if (r->headers_in.user.len == 0 && r->headers_in.user.data != NULL) {
+        return NGX_DECLINED;
+    }
+
+    if (r->headers_in.authorization == NULL) {
+        r->headers_in.user.data = (u_char *) "";
+        return NGX_DECLINED;
+    }
+
+    encoded = r->headers_in.authorization->value;
+
+    if (encoded.len < sizeof("Basic ") - 1
+        || ngx_strncasecmp(encoded.data, "Basic ", sizeof("Basic ") - 1) != 0)
+    {
+        r->headers_in.user.data = (u_char *) "";
+        return NGX_DECLINED;
+    }
+
+    encoded.len -= sizeof("Basic ") - 1;
+    encoded.data += sizeof("Basic ") - 1;
+
+    while (encoded.len && encoded.data[0] == ' ') {
+        encoded.len--;
+        encoded.data++;
+    }
+
+    if (encoded.len == 0) {
+        r->headers_in.user.data = (u_char *) "";
+        return NGX_DECLINED;
+    }
+    
+    auth.len = ngx_base64_decoded_length(encoded.len);
+    auth.data = ngx_palloc(r->pool, auth.len + 1); 
+    if (auth.data == NULL) {
+        return NGX_ERROR;
+    }
+
+    if (ngx_decode_base64(&auth, &encoded) != NGX_OK) {
+        r->headers_in.user.data = (u_char *) "";
+        return NGX_DECLINED;
+    }
+    
+    auth.data[auth.len] = '\0';
+    
+    for (len = 0; len < auth.len; len++) { 
+        if (auth.data[len] == ':') {
+            break;
+        }
+    }
+    
+    if (len == auth.len) {
+        r->headers_in.user.data = (u_char *) "";
+        return NGX_DECLINED;
+    }
+
+    r->headers_in.user.len = len;
+    r->headers_in.user.data = auth.data;
+    r->headers_in.passwd.len = auth.len - len - 1;
+    r->headers_in.passwd.data = &auth.data[len + 1];
+
+    return NGX_OK;
+}
+
+
+ngx_int_t
 ngx_http_subrequest(ngx_http_request_t *r,
     ngx_str_t *uri, ngx_str_t *args)
 {
--- a/src/http/ngx_http_core_module.h
+++ b/src/http/ngx_http_core_module.h
@@ -252,6 +252,7 @@ ngx_int_t ngx_http_find_location_config(
 
 ngx_int_t ngx_http_set_content_type(ngx_http_request_t *r);
 ngx_int_t ngx_http_set_exten(ngx_http_request_t *r);
+ngx_int_t ngx_http_auth_basic_user(ngx_http_request_t *r);
 
 ngx_int_t ngx_http_subrequest(ngx_http_request_t *r,
     ngx_str_t *uri, ngx_str_t *args);
--- a/src/http/ngx_http_request.c
+++ b/src/http/ngx_http_request.c
@@ -11,9 +11,6 @@
 
 
 static void ngx_http_init_request(ngx_event_t *ev);
-#if (NGX_HTTP_SSL)
-static void ngx_http_ssl_handshake(ngx_event_t *rev);
-#endif
 static void ngx_http_process_request_line(ngx_event_t *rev);
 static void ngx_http_process_request_headers(ngx_event_t *rev);
 static ssize_t ngx_http_read_request_header(ngx_http_request_t *r);
@@ -50,6 +47,11 @@ static u_char *ngx_http_log_error(ngx_lo
 static u_char *ngx_http_log_error_handler(ngx_http_request_t *r, u_char *buf,
     size_t len);
 
+#if (NGX_HTTP_SSL)
+static void ngx_http_ssl_handshake(ngx_event_t *rev);
+static void ngx_http_ssl_close_handler(ngx_event_t *ev);
+#endif
+
 
 static char *ngx_http_client_errors[] = {
 
@@ -490,6 +492,7 @@ ngx_http_ssl_handshake(ngx_event_t *rev)
                            "https ssl handshake: 0x%02Xd", buf[0]);
 
             c->recv = ngx_ssl_recv;
+            c->send = ngx_ssl_write;
             c->send_chain = ngx_ssl_send_chain;
 
             rc = ngx_ssl_handshake(c);
@@ -2412,27 +2415,6 @@ ngx_http_close_request(ngx_http_request_
 }
 
 
-#if (NGX_HTTP_SSL)
-
-static void
-ngx_http_ssl_close_handler(ngx_event_t *ev)
-{
-    ngx_connection_t  *c;
-
-    c = ev->data;
-
-    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, "http ssl close handler");
-
-    if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
-        return;
-    }
-
-    ngx_http_close_connection(c);
-}
-
-#endif
-
-
 static void
 ngx_http_close_connection(ngx_connection_t *c)
 {
@@ -2441,7 +2423,7 @@ ngx_http_close_connection(ngx_connection
     ngx_log_debug1(NGX_LOG_DEBUG_HTTP, c->log, 0,
                    "close http connection: %d", c->fd);
 
-#if (NGX_OPENSSL)
+#if (NGX_HTTP_SSL)
 
     if (c->ssl) {
         if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
@@ -2465,6 +2447,27 @@ ngx_http_close_connection(ngx_connection
 }
 
 
+#if (NGX_HTTP_SSL)
+
+static void
+ngx_http_ssl_close_handler(ngx_event_t *ev)
+{
+    ngx_connection_t  *c;
+
+    c = ev->data;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_HTTP, ev->log, 0, "http ssl close handler");
+
+    if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
+        return;
+    }
+
+    ngx_http_close_connection(c);
+}
+
+#endif
+
+
 static u_char *
 ngx_http_log_error(ngx_log_t *log, u_char *buf, size_t len)
 {
--- a/src/http/ngx_http_special_response.c
+++ b/src/http/ngx_http_special_response.c
@@ -292,6 +292,8 @@ ngx_http_special_response_handler(ngx_ht
 
                 r->err_ctx = r->ctx;
 
+                r->method = NGX_HTTP_GET;
+
                 return ngx_http_internal_redirect(r, &err_page[i].uri, NULL);
             }
         }
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -32,6 +32,8 @@ static ngx_http_variable_value_t *
     ngx_http_variable_document_root(ngx_http_request_t *r, uintptr_t data);
 static ngx_http_variable_value_t *
     ngx_http_variable_request_filename(ngx_http_request_t *r, uintptr_t data);
+static ngx_http_variable_value_t *
+    ngx_http_variable_remote_user(ngx_http_request_t *r, uintptr_t data);
 
 
 /*
@@ -108,8 +110,7 @@ static ngx_http_variable_t  ngx_http_cor
     { ngx_string("request_method"), ngx_http_variable_request,
       offsetof(ngx_http_request_t, method_name), 0, 0 },
 
-    { ngx_string("remote_user"), ngx_http_variable_request,
-      offsetof(ngx_http_request_t, headers_in.user), 0, 0 },
+    { ngx_string("remote_user"), ngx_http_variable_remote_user, 0, 0, 0 },
 
     { ngx_null_string, NULL, 0, 0, 0 }
 };
@@ -657,6 +658,34 @@ ngx_http_variable_request_filename(ngx_h
 }
 
 
+static ngx_http_variable_value_t *
+ngx_http_variable_remote_user(ngx_http_request_t *r, uintptr_t data)
+{
+    ngx_int_t                   rc;
+    ngx_http_variable_value_t  *vv;
+
+    rc = ngx_http_auth_basic_user(r);
+
+    if (rc == NGX_DECLINED) {
+        return NGX_HTTP_VAR_NOT_FOUND;
+    }
+
+    if (rc == NGX_ERROR) {
+        return NULL;
+    }
+
+    vv = ngx_palloc(r->pool, sizeof(ngx_http_variable_value_t));
+    if (vv == NULL) {
+        return NULL;
+    }
+
+    vv->value = 0;
+    vv->text = r->headers_in.user;
+
+    return vv;
+}
+
+
 ngx_int_t
 ngx_http_variables_add_core_vars(ngx_conf_t *cf)
 {
--- a/src/imap/ngx_imap.h
+++ b/src/imap/ngx_imap.h
@@ -13,6 +13,11 @@
 #include <ngx_event.h>
 #include <ngx_event_connect.h>
 
+#if (NGX_IMAP_SSL)
+#include <ngx_imap_ssl_module.h>
+#endif
+
+
 
 typedef struct {
     void   **main_conf;
@@ -32,7 +37,6 @@ typedef struct {
     ngx_msec_t            timeout;
 
     size_t                imap_client_buffer_size;
-    size_t                proxy_buffer_size;
 
     ngx_uint_t            protocol;
 
@@ -82,8 +86,8 @@ typedef struct {
 
     ngx_connection_t       *connection;
 
+    ngx_str_t               out;
     ngx_buf_t              *buffer;
-    ngx_str_t               out;
 
     void                  **ctx;
     void                  **main_conf;
@@ -93,6 +97,8 @@ typedef struct {
 
     ngx_uint_t              imap_state;
 
+    unsigned                blocked:1;
+    unsigned                quit:1;
     unsigned                protocol:1;
     unsigned                quoted:1;
 
@@ -100,6 +106,7 @@ typedef struct {
     ngx_str_t               passwd;
 
     ngx_str_t               tag;
+    ngx_str_t               tagged_line;
 
     ngx_uint_t              command;
     ngx_array_t             args;
@@ -167,6 +174,7 @@ typedef struct {
 
 
 void ngx_imap_init_connection(ngx_connection_t *c);
+void ngx_imap_send(ngx_event_t *wev);
 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);
--- a/src/imap/ngx_imap_auth_http_module.c
+++ b/src/imap/ngx_imap_auth_http_module.c
@@ -516,13 +516,14 @@ ngx_imap_auth_http_process_headers(ngx_i
             ngx_close_connection(ctx->peer.connection);
 
             if (ctx->err.len) {
-                (void) ngx_send(s->connection, ctx->err.data, ctx->err.len);
+                s->out = ctx->err;
 
                 if (ctx->sleep == 0) {
-                    ngx_imap_close_connection(s->connection);
-                    return;
+                    s->quit = 1;
                 }
 
+                ngx_imap_send(s->connection->write);
+
                 ngx_add_timer(s->connection->read, ctx->sleep * 1000);
 
                 s->connection->read->handler = ngx_imap_auth_sleep_handler;
--- a/src/imap/ngx_imap_core_module.c
+++ b/src/imap/ngx_imap_core_module.c
@@ -75,13 +75,6 @@ static ngx_command_t  ngx_imap_core_comm
       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,
@@ -157,7 +150,6 @@ ngx_imap_core_create_srv_conf(ngx_conf_t
     }
 
     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;
 
@@ -191,8 +183,6 @@ ngx_imap_core_merge_srv_conf(ngx_conf_t 
     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);
--- a/src/imap/ngx_imap_handler.c
+++ b/src/imap/ngx_imap_handler.c
@@ -13,6 +13,10 @@
 static void ngx_imap_init_session(ngx_event_t *rev);
 static ngx_int_t ngx_imap_read_command(ngx_imap_session_t *s);
 
+#if (NGX_IMAP_SSL)
+static void ngx_imap_ssl_close_handler(ngx_event_t *ev);
+#endif
+
 
 static ngx_str_t  greetings[] = {
    ngx_string("+OK POP3 ready" CRLF),
@@ -36,8 +40,12 @@ static u_char  imap_invalid_command[] = 
 void
 ngx_imap_init_connection(ngx_connection_t *c)
 {
-    ssize_t                    size;
+    ngx_imap_session_t        *s;
     ngx_imap_conf_ctx_t       *ctx;
+#if (NGX_IMAP_SSL)
+    ngx_int_t                  rc;
+    ngx_imap_ssl_conf_t       *sslcf;
+#endif
     ngx_imap_core_srv_conf_t  *cscf;
 
     ngx_log_debug0(NGX_LOG_DEBUG_IMAP, c->log, 0, "imap init connection");
@@ -45,26 +53,123 @@ ngx_imap_init_connection(ngx_connection_
     c->log_error = NGX_ERROR_INFO;
 
     ctx = c->ctx;
-    cscf = ngx_imap_get_module_srv_conf(ctx, ngx_imap_core_module);
+
+#if (NGX_IMAP_SSL)
+
+    sslcf = ngx_imap_get_module_srv_conf(ctx, ngx_imap_ssl_module);
+
+    if (sslcf->enable) {
 
-    size = greetings[cscf->protocol].len;
+        if (ngx_ssl_create_session(sslcf->ssl_ctx, c, NGX_SSL_BUFFER)
+            == NGX_ERROR)
+        {
+            ngx_imap_close_connection(c);
+            return;
+        }
 
-    if (ngx_send(c, greetings[cscf->protocol].data, size) < size) {
-        /*
-         * we treat the incomplete sending as NGX_ERROR
-         * because it is very strange here
-         */
+        rc = ngx_ssl_handshake(c);
+
+        if (rc == NGX_ERROR) {
+            ngx_imap_close_connection(c);
+            return;
+        }
+
+        c->recv = ngx_ssl_recv;
+        c->send = ngx_ssl_write;
+        c->send_chain = ngx_ssl_send_chain;
+    }
+
+#endif
+
+    s = ngx_pcalloc(c->pool, sizeof(ngx_imap_session_t));
+    if (s == NULL) {
         ngx_imap_close_connection(c);
         return;
     }
 
+    c->data = s;
+    s->connection = c;
+
+    cscf = ngx_imap_get_module_srv_conf(ctx, ngx_imap_core_module);
+    s->protocol = cscf->protocol;
+
+    s->ctx = ngx_pcalloc(c->pool, sizeof(void *) * ngx_imap_max_module);
+    if (s->ctx == NULL) {
+        ngx_imap_session_internal_server_error(s);
+        return;
+    }
+
+    s->main_conf = ctx->main_conf;
+    s->srv_conf = ctx->srv_conf;
+
+    s->out = greetings[s->protocol];
+
     c->read->handler = ngx_imap_init_session;
+    c->write->handler = ngx_imap_send;
 
+    ngx_add_timer(c->write, cscf->timeout);
     ngx_add_timer(c->read, cscf->timeout);
 
     if (ngx_handle_read_event(c->read, 0) == NGX_ERROR) {
         ngx_imap_close_connection(c);
     }
+
+    ngx_imap_send(c->write);
+}
+
+
+void
+ngx_imap_send(ngx_event_t *wev)
+{
+    ngx_int_t            n;
+    ngx_connection_t    *c;
+    ngx_imap_session_t  *s;
+
+    c = wev->data;
+    s = c->data;
+
+    if (wev->timedout) {
+        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT, "client timed out");
+        ngx_imap_close_connection(c);
+        return;
+    }
+
+    if (s->out.len == 0) {
+        if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) {
+            ngx_imap_close_connection(c);
+        }
+
+        return;
+    }
+
+    n = c->send(c, s->out.data, s->out.len);
+
+    if (n > 0) {
+        s->out.len -= n;
+
+        if (s->quit) {
+            ngx_imap_close_connection(c);
+            return;
+        }
+
+        if (s->blocked) {
+            c->read->handler(c->read);
+        }
+
+        return;
+    }
+
+    if (n == NGX_ERROR) {
+        ngx_imap_close_connection(c);
+        return;
+    }
+
+    /* n == NGX_AGAIN */
+
+    if (ngx_handle_write_event(c->write, 0) == NGX_ERROR) {
+        ngx_imap_close_connection(c);
+        return;
+    }
 }
 
 
@@ -74,7 +179,6 @@ 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;
@@ -85,40 +189,20 @@ ngx_imap_init_session(ngx_event_t *rev)
         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;
+    s = c->data;
 
     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) {
+    if (s->protocol == NGX_IMAP_POP3_PROTOCOL) {
         size = 128;
         s->imap_state = ngx_pop3_start;
         c->read->handler = ngx_pop3_auth_state;
 
     } else {
+        cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module);
         size = cscf->imap_client_buffer_size;
         s->imap_state = ngx_imap_start;
         c->read->handler = ngx_imap_auth_state;
@@ -137,11 +221,11 @@ ngx_imap_init_session(ngx_event_t *rev)
 void
 ngx_imap_auth_state(ngx_event_t *rev)
 {
-    u_char                    *text, *last, *out, *p;
-    ssize_t                    size, text_len, last_len;
+    u_char                    *text, *last, *p;
+    ssize_t                    text_len, last_len;
     ngx_str_t                 *arg;
     ngx_int_t                  rc;
-    ngx_uint_t                 quit, tag;
+    ngx_uint_t                 tag;
     ngx_connection_t          *c;
     ngx_imap_session_t        *s;
     ngx_imap_core_srv_conf_t  *cscf;
@@ -157,6 +241,14 @@ ngx_imap_auth_state(ngx_event_t *rev)
         return;
     }
 
+    if (s->out.len) {
+        ngx_log_debug0(NGX_LOG_DEBUG_IMAP, c->log, 0, "imap send handler busy");
+        s->blocked = 1;
+        return;
+    }
+
+    s->blocked = 0;
+
     rc = ngx_imap_read_command(s);
 
     ngx_log_debug1(NGX_LOG_DEBUG_IMAP, c->log, 0, "imap auth: %i", rc);
@@ -165,7 +257,6 @@ ngx_imap_auth_state(ngx_event_t *rev)
         return;
     }
 
-    quit = 0;
     tag = 1;
 
     text = NULL;
@@ -235,9 +326,9 @@ ngx_imap_auth_state(ngx_event_t *rev)
             break;
 
         case NGX_IMAP_LOGOUT:
+            s->quit = 1;
             text = imap_bye;
             text_len = sizeof(imap_bye) - 1;
-            quit = 1;
             break;
 
         case NGX_IMAP_NOOP:
@@ -260,18 +351,19 @@ ngx_imap_auth_state(ngx_event_t *rev)
     }
 
     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) {
+        if (s->tagged_line.len < s->tag.len + text_len + last_len) {
+            s->tagged_line.len = s->tag.len + text_len + last_len;
+            s->tagged_line.data = ngx_palloc(c->pool, s->tagged_line.len);
+            if (s->tagged_line.data == NULL) {
                 ngx_imap_close_connection(c);
                 return;
             }
         }
 
-        out = s->out.data;
-        p = out;
+        s->out.data = s->tagged_line.data;
+        s->out.len = s->tag.len + text_len + last_len;
+
+        p = s->out.data;
 
         if (text) {
             p = ngx_cpymem(p, text, text_len);
@@ -279,35 +371,20 @@ ngx_imap_auth_state(ngx_event_t *rev)
         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;
+        s->out.data = last;
+        s->out.len = 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) {
+        s->args.nelts = 0;
+        s->buffer->pos = s->buffer->start;
+        s->buffer->last = s->buffer->start;
+        s->tag.len = 0;
     }
 
-    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;
+    ngx_imap_send(c->write);
 }
 
 
@@ -317,7 +394,6 @@ 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;
@@ -334,13 +410,20 @@ ngx_pop3_auth_state(ngx_event_t *rev)
         return;
     }
 
+    if (s->out.len) {
+        ngx_log_debug0(NGX_LOG_DEBUG_IMAP, c->log, 0, "imap send handler busy");
+        s->blocked = 1;
+        return;
+    }
+
+    s->blocked = 0;
+
     rc = ngx_imap_read_command(s);
 
     if (rc == NGX_AGAIN || rc == NGX_ERROR) {
         return;
     }
 
-    quit = 0;
     text = pop3_ok;
     size = sizeof(pop3_ok) - 1;
 
@@ -381,7 +464,7 @@ ngx_pop3_auth_state(ngx_event_t *rev)
                 break;
 
             case NGX_POP3_QUIT:
-                quit = 1;
+                s->quit = 1;
                 break;
 
             case NGX_POP3_NOOP:
@@ -441,7 +524,7 @@ ngx_pop3_auth_state(ngx_event_t *rev)
                 break;
 
             case NGX_POP3_QUIT:
-                quit = 1;
+                s->quit = 1;
                 break;
 
             case NGX_POP3_NOOP:
@@ -466,23 +549,14 @@ ngx_pop3_auth_state(ngx_event_t *rev)
         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;
+
+    s->out.data = text;
+    s->out.len = size;
+
+    ngx_imap_send(c->write);
 }
 
 
@@ -492,8 +566,8 @@ ngx_imap_read_command(ngx_imap_session_t
     ssize_t    n;
     ngx_int_t  rc;
 
-    n = ngx_recv(s->connection, s->buffer->last,
-                 s->buffer->end - s->buffer->last);
+    n = s->connection->recv(s->connection, s->buffer->last,
+                            s->buffer->end - s->buffer->last);
 
     if (n == NGX_ERROR || n == 0) {
         ngx_imap_close_connection(s->connection);
@@ -538,10 +612,10 @@ ngx_imap_read_command(ngx_imap_session_t
 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);
+    s->out = internal_server_errors[s->protocol];
+    s->quit = 1;
 
-    ngx_imap_close_connection(s->connection);
+    ngx_imap_send(s->connection->write);
 }
 
 
@@ -553,9 +627,42 @@ ngx_imap_close_connection(ngx_connection
     ngx_log_debug1(NGX_LOG_DEBUG_IMAP, c->log, 0,
                    "close imap connection: %d", c->fd);
 
+#if (NGX_IMAP_SSL)
+
+    if (c->ssl) {
+        if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
+            c->read->handler = ngx_imap_ssl_close_handler;
+            c->write->handler = ngx_imap_ssl_close_handler;
+            return;
+        }
+    }
+
+#endif
+
     pool = c->pool;
 
     ngx_close_connection(c);
 
     ngx_destroy_pool(pool);
 }
+
+
+#if (NGX_IMAP_SSL)
+ 
+static void
+ngx_imap_ssl_close_handler(ngx_event_t *ev)
+{
+    ngx_connection_t  *c;
+
+    c = ev->data;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_IMAP, ev->log, 0, "http ssl close handler");
+
+    if (ngx_ssl_shutdown(c) == NGX_AGAIN) {
+        return;
+    }
+
+    ngx_imap_close_connection(c);
+}
+
+#endif
--- a/src/imap/ngx_imap_proxy_module.c
+++ b/src/imap/ngx_imap_proxy_module.c
@@ -13,6 +13,8 @@
 
 typedef struct {
     ngx_flag_t  enable;
+    size_t      buffer_size;
+    ngx_msec_t  timeout;
 } ngx_imap_proxy_conf_t;
 
 
@@ -35,6 +37,7 @@ static char *ngx_imap_proxy_merge_conf(n
 
 
 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,
@@ -42,6 +45,20 @@ static ngx_command_t  ngx_imap_proxy_com
       offsetof(ngx_imap_proxy_conf_t, enable),
       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_proxy_conf_t, buffer_size),
+      NULL },
+
+    { ngx_string("proxy_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_proxy_conf_t, timeout),
+      NULL },
+
       ngx_null_command
 };
 
@@ -131,12 +148,12 @@ ngx_imap_proxy_block_read(ngx_event_t *r
 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;
+    u_char                 *p;
+    ngx_int_t               rc;
+    ngx_str_t               line;
+    ngx_connection_t       *c;
+    ngx_imap_session_t     *s;
+    ngx_imap_proxy_conf_t  *pcf;
 
     ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0,
                    "imap proxy imap auth handler");
@@ -152,10 +169,9 @@ ngx_imap_proxy_imap_handler(ngx_event_t 
     }
 
     if (s->proxy->buffer == NULL) {
-        cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module);
+        pcf = ngx_imap_get_module_srv_conf(s, ngx_imap_proxy_module);
 
-        s->proxy->buffer = ngx_create_temp_buf(c->pool,
-                                               cscf->proxy_buffer_size);
+        s->proxy->buffer = ngx_create_temp_buf(c->pool, pcf->buffer_size);
         if (s->proxy->buffer == NULL) {
             ngx_imap_proxy_internal_server_error(s);
             return;
@@ -247,7 +263,7 @@ ngx_imap_proxy_imap_handler(ngx_event_t 
         break;
     }
 
-    if (ngx_send(c, line.data, line.len) < (ssize_t) line.len) {
+    if (c->send(c, line.data, line.len) < (ssize_t) line.len) {
         /*
          * we treat the incomplete sending as NGX_ERROR
          * because it is very strange here
@@ -265,6 +281,8 @@ ngx_imap_proxy_imap_handler(ngx_event_t 
         rev->handler = ngx_imap_proxy_handler;
         c->write->handler = ngx_imap_proxy_handler;
 
+        pcf = ngx_imap_get_module_srv_conf(s, ngx_imap_proxy_module);
+        ngx_add_timer(s->connection->read, pcf->timeout);
         ngx_del_timer(c->read);
     }
 }
@@ -273,12 +291,12 @@ ngx_imap_proxy_imap_handler(ngx_event_t 
 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;
+    u_char                 *p;
+    ngx_int_t               rc;
+    ngx_str_t               line;
+    ngx_connection_t       *c;
+    ngx_imap_session_t     *s;
+    ngx_imap_proxy_conf_t  *pcf;
 
     ngx_log_debug0(NGX_LOG_DEBUG_IMAP, rev->log, 0,
                    "imap proxy pop3 auth handler");
@@ -294,10 +312,9 @@ ngx_imap_proxy_pop3_handler(ngx_event_t 
     }
 
     if (s->proxy->buffer == NULL) {
-        cscf = ngx_imap_get_module_srv_conf(s, ngx_imap_core_module);
+        pcf = ngx_imap_get_module_srv_conf(s, ngx_imap_proxy_module);
 
-        s->proxy->buffer = ngx_create_temp_buf(c->pool,
-                                               cscf->proxy_buffer_size);
+        s->proxy->buffer = ngx_create_temp_buf(c->pool, pcf->buffer_size);
         if (s->proxy->buffer == NULL) {
             ngx_imap_proxy_internal_server_error(s);
             return;
@@ -369,7 +386,7 @@ ngx_imap_proxy_pop3_handler(ngx_event_t 
         break;
     }
 
-    if (ngx_send(c, line.data, line.len) < (ssize_t) line.len) {
+    if (c->send(c, line.data, line.len) < (ssize_t) line.len) {
         /*
          * we treat the incomplete sending as NGX_ERROR
          * because it is very strange here
@@ -387,6 +404,8 @@ ngx_imap_proxy_pop3_handler(ngx_event_t 
         rev->handler = ngx_imap_proxy_handler;
         c->write->handler = ngx_imap_proxy_handler;
 
+        pcf = ngx_imap_get_module_srv_conf(s, ngx_imap_proxy_module);
+        ngx_add_timer(s->connection->read, pcf->timeout);
         ngx_del_timer(c->read);
     }
 }
@@ -408,7 +427,8 @@ ngx_imap_proxy_read_response(ngx_imap_se
 
     b = s->proxy->buffer;
 
-    n = ngx_recv(s->proxy->upstream.connection, b->last, b->end - b->last);
+    n = s->proxy->upstream.connection->recv(s->proxy->upstream.connection,
+                                            b->last, b->end - b->last);
 
     if (n == NGX_ERROR || n == 0) {
         return NGX_ERROR;
@@ -475,12 +495,13 @@ ngx_imap_proxy_read_response(ngx_imap_se
 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;
+    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;
+    ngx_imap_proxy_conf_t  *pcf;
 
     c = ev->data;
     s = c->data;
@@ -537,7 +558,7 @@ ngx_imap_proxy_handler(ngx_event_t *ev)
             size = b->last - b->pos;
 
             if (size && dst->write->ready) {
-                n = ngx_send(dst, b->pos, size);
+                n = dst->send(dst, b->pos, size);
 
                 if (n == NGX_ERROR) {
                     ngx_imap_proxy_close_session(s);
@@ -568,7 +589,7 @@ ngx_imap_proxy_handler(ngx_event_t *ev)
         size = b->end - b->last;
 
         if (size && src->read->ready) {
-            n = ngx_recv(src, b->last, size);
+            n = src->recv(src, b->last, size);
 
             if (n == NGX_ERROR || n == 0) {
                 ngx_imap_proxy_close_session(s);
@@ -587,6 +608,11 @@ ngx_imap_proxy_handler(ngx_event_t *ev)
                     return;
                 }
             }
+
+            if (c == s->connection) {
+                pcf = ngx_imap_get_module_srv_conf(s, ngx_imap_proxy_module);
+                ngx_add_timer(c->read, pcf->timeout);
+            }
         }
 
     } while (again);
@@ -634,6 +660,8 @@ ngx_imap_proxy_create_conf(ngx_conf_t *c
     }
 
     pcf->enable = NGX_CONF_UNSET;
+    pcf->buffer_size = NGX_CONF_UNSET_SIZE;
+    pcf->timeout = NGX_CONF_UNSET_MSEC;
 
     return pcf;
 }
@@ -645,7 +673,10 @@ ngx_imap_proxy_merge_conf(ngx_conf_t *cf
     ngx_imap_proxy_conf_t *prev = parent;
     ngx_imap_proxy_conf_t *conf = child;
 
-    ngx_conf_merge_msec_value(conf->enable, prev->enable, 0);
+    ngx_conf_merge_value(conf->enable, prev->enable, 0);
+    ngx_conf_merge_size_value(conf->buffer_size, prev->buffer_size,
+                              (size_t) ngx_pagesize);
+    ngx_conf_merge_msec_value(conf->timeout, prev->timeout, 24 * 60 * 60000);
 
     return NGX_CONF_OK;
 }
new file mode 100644
--- /dev/null
+++ b/src/imap/ngx_imap_ssl_module.c
@@ -0,0 +1,174 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_imap.h>
+
+
+#define NGX_DEFLAUT_CERTIFICATE      "cert.pem"
+#define NGX_DEFLAUT_CERTIFICATE_KEY  "cert.pem"
+
+
+static void *ngx_imap_ssl_create_conf(ngx_conf_t *cf);
+static char *ngx_imap_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child);
+
+
+static ngx_command_t  ngx_imap_ssl_commands[] = {
+
+    { ngx_string("ssl"),
+      NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_FLAG,
+      ngx_conf_set_flag_slot,
+      NGX_IMAP_SRV_CONF_OFFSET,
+      offsetof(ngx_imap_ssl_conf_t, enable),
+      NULL },
+
+    { ngx_string("ssl_certificate"),
+      NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_str_slot,
+      NGX_IMAP_SRV_CONF_OFFSET,
+      offsetof(ngx_imap_ssl_conf_t, certificate),
+      NULL },
+
+    { ngx_string("ssl_certificate_key"),
+      NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_str_slot,
+      NGX_IMAP_SRV_CONF_OFFSET,
+      offsetof(ngx_imap_ssl_conf_t, certificate_key),
+      NULL },
+
+    { ngx_string("ssl_ciphers"),
+      NGX_IMAP_MAIN_CONF|NGX_IMAP_SRV_CONF|NGX_CONF_TAKE1,
+      ngx_conf_set_str_slot,
+      NGX_IMAP_SRV_CONF_OFFSET,
+      offsetof(ngx_imap_ssl_conf_t, ciphers),
+      NULL },
+
+      ngx_null_command
+};
+
+
+static ngx_imap_module_t  ngx_imap_ssl_module_ctx = {
+    NULL,                                  /* create main configuration */
+    NULL,                                  /* init main configuration */
+
+    ngx_imap_ssl_create_conf,              /* create server configuration */
+    ngx_imap_ssl_merge_conf                /* merge server configuration */
+};
+
+
+ngx_module_t  ngx_imap_ssl_module = {
+    NGX_MODULE_V1,
+    &ngx_imap_ssl_module_ctx,              /* module context */
+    ngx_imap_ssl_commands,                 /* module directives */
+    NGX_IMAP_MODULE,                       /* module type */
+    NULL,                                  /* init module */
+    NULL                                   /* init process */
+};
+
+
+static void *
+ngx_imap_ssl_create_conf(ngx_conf_t *cf)
+{           
+    ngx_imap_ssl_conf_t  *scf;
+            
+    scf = ngx_pcalloc(cf->pool, sizeof(ngx_imap_ssl_conf_t));
+    if (scf == NULL) {
+        return NGX_CONF_ERROR;
+    }
+
+    /*
+     * set by ngx_pcalloc():  
+     *
+     *     scf->certificate.len = 0;
+     *     scf->certificate.data = NULL;
+     *     scf->certificate_key.len = 0;
+     *     scf->certificate_key.data = NULL;
+     *     scf->ciphers.len = 0;
+     *     scf->ciphers.data = NULL;
+     */
+
+    scf->enable = NGX_CONF_UNSET;
+
+    return scf;
+}
+
+
+static char *
+ngx_imap_ssl_merge_conf(ngx_conf_t *cf, void *parent, void *child)
+{
+    ngx_imap_ssl_conf_t *prev = parent;
+    ngx_imap_ssl_conf_t *conf = child;
+
+    ngx_conf_merge_value(conf->enable, prev->enable, 0);
+
+    if (conf->enable == 0) {
+        return NGX_CONF_OK;
+    }
+
+    ngx_conf_merge_str_value(conf->certificate, prev->certificate,
+                             NGX_DEFLAUT_CERTIFICATE);
+
+    ngx_conf_merge_str_value(conf->certificate_key, prev->certificate_key,
+                             NGX_DEFLAUT_CERTIFICATE_KEY);
+
+    ngx_conf_merge_str_value(conf->ciphers, prev->ciphers, "");
+
+
+    /* TODO: configure methods */
+
+    conf->ssl_ctx = SSL_CTX_new(SSLv23_server_method());
+
+    if (conf->ssl_ctx == NULL) {
+        ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0, "SSL_CTX_new() failed");
+        return NGX_CONF_ERROR;
+    }
+
+    if (ngx_pool_cleanup_add(cf->pool, ngx_ssl_cleanup_ctx, conf->ssl_ctx)
+        == NULL)
+    {
+        return NGX_CONF_ERROR;
+    }
+
+
+#if 0
+    SSL_CTX_set_options(conf->ssl_ctx, SSL_OP_ALL);
+    SSL_CTX_set_options(conf->ssl_ctx, SSL_OP_NO_SSLv3);
+    SSL_CTX_set_options(conf->ssl_ctx, SSL_OP_SINGLE_DH_USE);
+#endif
+
+    if (conf->ciphers.len) {
+        if (SSL_CTX_set_cipher_list(conf->ssl_ctx,
+                                   (const char *) conf->ciphers.data) == 0)
+        {
+            ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0,
+                          "SSL_CTX_set_cipher_list(\"%V\") failed",
+                          &conf->ciphers);
+        }
+    }
+
+    if (SSL_CTX_use_certificate_chain_file(conf->ssl_ctx,
+                                         (char *) conf->certificate.data) == 0)
+    {
+        ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0,
+                      "SSL_CTX_use_certificate_chain_file(\"%s\") failed",
+                      conf->certificate.data);
+        return NGX_CONF_ERROR;
+    }
+
+
+    if (SSL_CTX_use_PrivateKey_file(conf->ssl_ctx,
+                                    (char *) conf->certificate_key.data,
+                                    SSL_FILETYPE_PEM) == 0)
+    {
+        ngx_ssl_error(NGX_LOG_EMERG, cf->log, 0,
+                      "SSL_CTX_use_PrivateKey_file(\"%s\") failed",
+                      conf->certificate_key.data);
+        return NGX_CONF_ERROR;
+    }
+
+    return NGX_CONF_OK;
+}
new file mode 100644
--- /dev/null
+++ b/src/imap/ngx_imap_ssl_module.h
@@ -0,0 +1,30 @@
+
+/*
+ * Copyright (C) Igor Sysoev
+ */
+
+
+#ifndef _NGX_IMAP_SSL_H_INCLUDED_
+#define _NGX_IMAP_SSL_H_INCLUDED_
+
+
+#include <ngx_config.h>
+#include <ngx_core.h>
+#include <ngx_imap.h>
+
+
+typedef struct {
+    ngx_flag_t      enable;
+    ngx_str_t       certificate;
+    ngx_str_t       certificate_key;
+
+    ngx_str_t       ciphers;
+
+    ngx_ssl_ctx_t  *ssl_ctx;
+} ngx_imap_ssl_conf_t;
+
+
+extern ngx_module_t  ngx_imap_ssl_module;
+
+
+#endif /* _NGX_IMAP_SSL_H_INCLUDED_ */
--- a/src/os/unix/ngx_freebsd_config.h
+++ b/src/os/unix/ngx_freebsd_config.h
@@ -99,5 +99,7 @@ pid_t rfork_thread(int flags, void *stac
 
 extern char *malloc_options;
 
+#define NGX_HAVE_OS_SPECIFIC_INIT    1
+
 
 #endif /* _NGX_FREEBSD_CONFIG_H_INCLUDED_ */
--- a/src/os/unix/ngx_freebsd_init.c
+++ b/src/os/unix/ngx_freebsd_init.c
@@ -27,7 +27,7 @@ ngx_uint_t ngx_freebsd_sendfile_nbytes_b
 ngx_uint_t ngx_freebsd_use_tcp_nopush;
 
 
-ngx_os_io_t ngx_os_io = {
+static ngx_os_io_t ngx_freebsd_io = {
     ngx_unix_recv,
     ngx_readv_chain,
     ngx_unix_send,
@@ -74,7 +74,8 @@ sysctl_t sysctls[] = {
 };
 
 
-void ngx_debug_init()
+void
+ngx_debug_init()
 {
 #if (NGX_DEBUG && !NGX_NO_DEBUG_MALLOC)
 
@@ -88,7 +89,8 @@ void ngx_debug_init()
 }
 
 
-ngx_int_t ngx_os_init(ngx_log_t *log)
+ngx_int_t
+ngx_os_specific_init(ngx_log_t *log)
 {
     int         version, somaxconn;
     size_t      size;
@@ -223,12 +225,14 @@ ngx_int_t ngx_os_init(ngx_log_t *log)
 
     ngx_tcp_nodelay_and_tcp_nopush = 1;
 
+    ngx_os_io = ngx_freebsd_io;
 
-    return ngx_posix_init(log);
+    return NGX_OK;
 }
 
 
-void ngx_os_status(ngx_log_t *log)
+void
+ngx_os_specific_status(ngx_log_t *log)
 {
     ngx_uint_t  i;
 
@@ -251,7 +255,4 @@ void ngx_os_status(ngx_log_t *log)
                           sysctls[i].name, *sysctls[i].value);
         }
     }
-
-
-    ngx_posix_status(log);
 }
--- a/src/os/unix/ngx_freebsd_sendfile_chain.c
+++ b/src/os/unix/ngx_freebsd_sendfile_chain.c
@@ -56,7 +56,11 @@ ngx_freebsd_sendfile_chain(ngx_connectio
 
 #if (NGX_HAVE_KQUEUE)
 
-    if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT) && wev->pending_eof) {
+    if ((ngx_event_flags & NGX_USE_KQUEUE_EVENT)
+        && wev->pending_eof
+           /* FreeBSD 6.0 may erroneously report ETIMEDOUT */
+        && wev->kq_errno != NGX_ETIMEDOUT)
+    {
         (void) ngx_connection_error(c, wev->kq_errno,
                                "kevent() reported about an closed connection");
         wev->error = 1;
--- a/src/os/unix/ngx_linux_config.h
+++ b/src/os/unix/ngx_linux_config.h
@@ -103,4 +103,7 @@ extern ssize_t sendfile(int s, int fd, i
 #endif
 
 
+#define NGX_HAVE_OS_SPECIFIC_INIT    1
+
+
 #endif /* _NGX_LINUX_CONFIG_H_INCLUDED_ */
--- a/src/os/unix/ngx_linux_init.c
+++ b/src/os/unix/ngx_linux_init.c
@@ -14,7 +14,7 @@ char ngx_linux_kern_osrelease[20];
 int ngx_linux_rtsig_max;
 
 
-ngx_os_io_t ngx_os_io = {
+static ngx_os_io_t ngx_linux_io = {
     ngx_unix_recv,
     ngx_readv_chain,
     ngx_unix_send,
@@ -29,7 +29,7 @@ ngx_os_io_t ngx_os_io = {
 
 
 ngx_int_t
-ngx_os_init(ngx_log_t *log)
+ngx_os_specific_init(ngx_log_t *log)
 {
     int        name[2];
     size_t     len;
@@ -74,19 +74,18 @@ ngx_os_init(ngx_log_t *log)
     }
 
 
-    return ngx_posix_init(log);
+    ngx_os_io = ngx_linux_io;
+
+    return NGX_OK;
 }
 
 
 void
-ngx_os_status(ngx_log_t *log)
+ngx_os_specific_status(ngx_log_t *log)
 {
     ngx_log_error(NGX_LOG_NOTICE, log, 0, "OS: %s %s",
                   ngx_linux_kern_ostype, ngx_linux_kern_osrelease);
 
     ngx_log_error(NGX_LOG_NOTICE, log, 0, "sysctl(KERN_RTSIGMAX): %d",
                   ngx_linux_rtsig_max);
-
-
-    ngx_posix_status(log);
 }
--- a/src/os/unix/ngx_os.h
+++ b/src/os/unix/ngx_os.h
@@ -34,10 +34,9 @@ typedef struct {
 void ngx_debug_init(void);
 ngx_int_t ngx_os_init(ngx_log_t *log);
 void ngx_os_status(ngx_log_t *log);
+ngx_int_t ngx_os_specific_init(ngx_log_t *log);
+void ngx_os_specific_status(ngx_log_t *log);
 ngx_int_t ngx_daemon(ngx_log_t *log);
-ngx_int_t ngx_posix_init(ngx_log_t *log);
-void ngx_posix_status(ngx_log_t *log);
-ngx_int_t ngx_posix_post_conf_init(ngx_log_t *log);
 
 
 ssize_t ngx_unix_recv(ngx_connection_t *c, u_char *buf, size_t size);
--- a/src/os/unix/ngx_posix_config.h
+++ b/src/os/unix/ngx_posix_config.h
@@ -102,7 +102,4 @@
 #endif
 
 
-#define NGX_POSIX_IO  1
-
-
 #endif /* _NGX_POSIX_CONFIG_H_INCLUDED_ */
--- a/src/os/unix/ngx_posix_init.c
+++ b/src/os/unix/ngx_posix_init.c
@@ -6,6 +6,7 @@
 
 #include <ngx_config.h>
 #include <ngx_core.h>
+#include <nginx.h>
 
 
 ngx_int_t   ngx_ncpu;
@@ -17,8 +18,6 @@ ngx_uint_t  ngx_tcp_nodelay_and_tcp_nopu
 struct rlimit  rlmt;
 
 
-#if (NGX_POSIX_IO)
-
 ngx_os_io_t ngx_os_io = {
     ngx_unix_recv,
     ngx_readv_chain,
@@ -28,74 +27,16 @@ ngx_os_io_t ngx_os_io = {
 };
 
 
-ngx_int_t ngx_os_init(ngx_log_t *log)
-{
-    return ngx_posix_init(log);
-}
-
-
-void ngx_os_status(ngx_log_t *log)
+ngx_int_t
+ngx_os_init(ngx_log_t *log)
 {
-    ngx_posix_status(log);
-}
-
-
-#endif
-
-
-void ngx_signal_handler(int signo);
-
-
-typedef struct {
-     int     signo;
-     char   *signame;
-     void  (*handler)(int signo);
-} ngx_signal_t;
-
-
-ngx_signal_t  signals[] = {
-    { ngx_signal_value(NGX_RECONFIGURE_SIGNAL),
-      "SIG" ngx_value(NGX_RECONFIGURE_SIGNAL),
-      ngx_signal_handler },
-
-    { ngx_signal_value(NGX_REOPEN_SIGNAL),
-      "SIG" ngx_value(NGX_REOPEN_SIGNAL),
-      ngx_signal_handler },
+    ngx_log_error(NGX_LOG_NOTICE, log, 0, NGINX_VER);
 
-    { ngx_signal_value(NGX_NOACCEPT_SIGNAL),
-      "SIG" ngx_value(NGX_NOACCEPT_SIGNAL),
-      ngx_signal_handler },
-
-    { ngx_signal_value(NGX_TERMINATE_SIGNAL),
-      "SIG" ngx_value(NGX_TERMINATE_SIGNAL),
-      ngx_signal_handler },
-
-    { ngx_signal_value(NGX_SHUTDOWN_SIGNAL),
-      "SIG" ngx_value(NGX_SHUTDOWN_SIGNAL),
-      ngx_signal_handler },
-
-    { ngx_signal_value(NGX_CHANGEBIN_SIGNAL),
-      "SIG" ngx_value(NGX_CHANGEBIN_SIGNAL),
-      ngx_signal_handler },
-
-    { SIGALRM, "SIGALRM", ngx_signal_handler },
-
-    { SIGINT, "SIGINT", ngx_signal_handler },
-
-    { SIGIO, "SIGIO", ngx_signal_handler },
-
-    { SIGCHLD, "SIGCHLD", ngx_signal_handler },
-
-    { SIGPIPE, "SIGPIPE, SIG_IGN", SIG_IGN },
-
-    { 0, NULL, NULL }
-};
-
-
-ngx_int_t ngx_posix_init(ngx_log_t *log)
-{
-    ngx_signal_t      *sig;
-    struct sigaction   sa;
+#if (NGX_HAVE_OS_SPECIFIC_INIT)
+    if (ngx_os_specific_init(log) != NGX_OK) {
+        return NGX_ERROR;
+    }
+#endif
 
     ngx_init_setproctitle(log);
 
@@ -105,23 +46,12 @@ ngx_int_t ngx_posix_init(ngx_log_t *log)
         ngx_ncpu = 1;
     }
 
-    for (sig = signals; sig->signo != 0; sig++) {
-        ngx_memzero(&sa, sizeof(struct sigaction));
-        sa.sa_handler = sig->handler;
-        sigemptyset(&sa.sa_mask);
-        if (sigaction(sig->signo, &sa, NULL) == -1) {
-            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
-                          "sigaction(%s) failed", sig->signame);
-            return NGX_ERROR;
-        }
-    }
-
     if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {
         ngx_log_error(NGX_LOG_ALERT, log, errno,
                       "getrlimit(RLIMIT_NOFILE) failed)");
         return NGX_ERROR;
     }
-
+    
     ngx_max_sockets = rlmt.rlim_cur;
 
 #if (NGX_HAVE_INHERITED_NONBLOCK)
@@ -134,157 +64,21 @@ ngx_int_t ngx_posix_init(ngx_log_t *log)
 }
 
 
-void ngx_posix_status(ngx_log_t *log)
+void
+ngx_os_status(ngx_log_t *log)
 {
+#if (NGX_HAVE_OS_SPECIFIC_INIT)
+    ngx_os_specific_status(log);
+#endif
+
     ngx_log_error(NGX_LOG_NOTICE, log, 0,
                   "getrlimit(RLIMIT_NOFILE): %r:%r",
                   rlmt.rlim_cur, rlmt.rlim_max);
 }
 
 
-void ngx_signal_handler(int signo)
-{
-    char            *action;
-    struct timeval   tv;
-    ngx_int_t        ignore;
-    ngx_err_t        err;
-    ngx_signal_t    *sig;
-
-    ignore = 0;
-
-    err = ngx_errno;
-
-    for (sig = signals; sig->signo != 0; sig++) {
-        if (sig->signo == signo) {
-            break;
-        }
-    }
-
-    ngx_gettimeofday(&tv);
-    ngx_time_update(tv.tv_sec);
-
-    action = "";
-
-    switch (ngx_process) {
-
-    case NGX_PROCESS_MASTER:
-    case NGX_PROCESS_SINGLE:
-        switch (signo) {
-
-        case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
-            ngx_quit = 1;
-            action = ", shutting down";
-            break;
-
-        case ngx_signal_value(NGX_TERMINATE_SIGNAL):
-        case SIGINT:
-            ngx_terminate = 1;
-            action = ", exiting";
-            break;
-
-        case ngx_signal_value(NGX_NOACCEPT_SIGNAL):
-            ngx_noaccept = 1;
-            action = ", stop accepting connections";
-            break;
-
-        case ngx_signal_value(NGX_RECONFIGURE_SIGNAL):
-            ngx_reconfigure = 1;
-            action = ", reconfiguring";
-            break;
-
-        case ngx_signal_value(NGX_REOPEN_SIGNAL):
-            ngx_reopen = 1;
-            action = ", reopening logs";
-            break;
-
-        case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):
-            if (getppid() > 1 || ngx_new_binary > 0) {
-
-                /*
-                 * Ignore the signal in the new binary if its parent is
-                 * not the init process, i.e. the old binary's process
-                 * is still running.  Or ingore the signal in the old binary's
-                 * process if the new binary's process is already running.
-                 */
-
-                action = ", ignoring";
-                ignore = 1;
-                break;
-            }
-
-            ngx_change_binary = 1;
-            action = ", changing binary";
-            break;
-
-        case SIGALRM:
-            if (!ngx_terminate) {
-                ngx_timer = 1;
-                action = ", shutting down old worker processes";
-            }
-
-            break;
-
-        case SIGIO:
-            ngx_sigio = 1;
-            break;
-
-        case SIGCHLD:
-            ngx_reap = 1;
-            break;
-        }
-
-        break;
-
-    case NGX_PROCESS_WORKER:
-        switch (signo) {
-
-        case ngx_signal_value(NGX_NOACCEPT_SIGNAL):
-            ngx_debug_quit = 1;
-        case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
-            ngx_quit = 1;
-            action = ", shutting down";
-            break;
-
-        case ngx_signal_value(NGX_TERMINATE_SIGNAL):
-        case SIGINT:
-            ngx_terminate = 1;
-            action = ", exiting";
-            break;
-
-        case ngx_signal_value(NGX_REOPEN_SIGNAL):
-            ngx_reopen = 1;
-            action = ", reopening logs";
-            break;
-
-        case ngx_signal_value(NGX_RECONFIGURE_SIGNAL):
-        case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):
-        case SIGIO:
-            action = ", ignoring";
-            break;
-        }
-
-        break;
-    }
-
-    ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,
-                  "signal %d (%s) received%s", signo, sig->signame, action);
-
-    if (ignore) {
-        ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, 0,
-                      "the changing binary signal is ignored: "
-                      "you should shutdown or terminate "
-                      "before either old or new binary's process");
-    }
-
-    if (signo == SIGCHLD) {
-        ngx_process_get_status();
-    }
-
-    ngx_set_errno(err);
-}
-
-
-ngx_int_t ngx_posix_post_conf_init(ngx_log_t *log)
+ngx_int_t
+ngx_posix_post_conf_init(ngx_log_t *log)
 {
     ngx_fd_t  pp[2];
 
--- a/src/os/unix/ngx_process.c
+++ b/src/os/unix/ngx_process.c
@@ -10,7 +10,17 @@
 #include <ngx_channel.h>
 
 
+typedef struct {
+     int     signo;
+     char   *signame;
+     void  (*handler)(int signo);
+} ngx_signal_t;
+
+
+
 static void ngx_execute_proc(ngx_cycle_t *cycle, void *data);
+static void ngx_signal_handler(int signo);
+static void ngx_process_get_status(void);
 
 
 int              ngx_argc;
@@ -23,6 +33,45 @@ ngx_int_t        ngx_last_process;
 ngx_process_t    ngx_processes[NGX_MAX_PROCESSES];
 
 
+ngx_signal_t  signals[] = {
+    { ngx_signal_value(NGX_RECONFIGURE_SIGNAL),
+      "SIG" ngx_value(NGX_RECONFIGURE_SIGNAL),
+      ngx_signal_handler },
+
+    { ngx_signal_value(NGX_REOPEN_SIGNAL),
+      "SIG" ngx_value(NGX_REOPEN_SIGNAL),
+      ngx_signal_handler },
+
+    { ngx_signal_value(NGX_NOACCEPT_SIGNAL),
+      "SIG" ngx_value(NGX_NOACCEPT_SIGNAL),
+      ngx_signal_handler },
+
+    { ngx_signal_value(NGX_TERMINATE_SIGNAL),
+      "SIG" ngx_value(NGX_TERMINATE_SIGNAL),
+      ngx_signal_handler },
+
+    { ngx_signal_value(NGX_SHUTDOWN_SIGNAL),
+      "SIG" ngx_value(NGX_SHUTDOWN_SIGNAL),
+      ngx_signal_handler },
+
+    { ngx_signal_value(NGX_CHANGEBIN_SIGNAL),
+      "SIG" ngx_value(NGX_CHANGEBIN_SIGNAL),
+      ngx_signal_handler },
+
+    { SIGALRM, "SIGALRM", ngx_signal_handler },
+
+    { SIGINT, "SIGINT", ngx_signal_handler },
+
+    { SIGIO, "SIGIO", ngx_signal_handler },
+
+    { SIGCHLD, "SIGCHLD", ngx_signal_handler },
+
+    { SIGPIPE, "SIGPIPE, SIG_IGN", SIG_IGN },
+
+    { 0, NULL, NULL }
+};
+
+
 ngx_pid_t
 ngx_spawn_process(ngx_cycle_t *cycle, ngx_spawn_proc_pt proc, void *data,
     char *name, ngx_int_t respawn)
@@ -208,7 +257,171 @@ ngx_execute_proc(ngx_cycle_t *cycle, voi
 }
 
 
+ngx_int_t
+ngx_init_signals(ngx_log_t *log)
+{
+    ngx_signal_t      *sig;
+    struct sigaction   sa;
+
+    for (sig = signals; sig->signo != 0; sig++) {
+        ngx_memzero(&sa, sizeof(struct sigaction));
+        sa.sa_handler = sig->handler;
+        sigemptyset(&sa.sa_mask);
+        if (sigaction(sig->signo, &sa, NULL) == -1) {
+            ngx_log_error(NGX_LOG_EMERG, log, ngx_errno,
+                          "sigaction(%s) failed", sig->signame);
+            return NGX_ERROR;
+        }
+    }
+
+    return NGX_OK;
+}
+
+
 void
+ngx_signal_handler(int signo)
+{
+    char            *action;
+    struct timeval   tv;
+    ngx_int_t        ignore;
+    ngx_err_t        err;
+    ngx_signal_t    *sig;
+
+    ignore = 0;
+
+    err = ngx_errno;
+
+    for (sig = signals; sig->signo != 0; sig++) {
+        if (sig->signo == signo) {
+            break;
+        }
+    }
+
+    ngx_gettimeofday(&tv);
+    ngx_time_update(tv.tv_sec);
+
+    action = "";
+
+    switch (ngx_process) {
+
+    case NGX_PROCESS_MASTER:
+    case NGX_PROCESS_SINGLE:
+        switch (signo) {
+
+        case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
+            ngx_quit = 1;
+            action = ", shutting down";
+            break;
+
+        case ngx_signal_value(NGX_TERMINATE_SIGNAL):
+        case SIGINT:
+            ngx_terminate = 1;
+            action = ", exiting";
+            break;
+
+        case ngx_signal_value(NGX_NOACCEPT_SIGNAL):
+            ngx_noaccept = 1;
+            action = ", stop accepting connections";
+            break;
+
+        case ngx_signal_value(NGX_RECONFIGURE_SIGNAL):
+            ngx_reconfigure = 1;
+            action = ", reconfiguring";
+            break;
+
+        case ngx_signal_value(NGX_REOPEN_SIGNAL):
+            ngx_reopen = 1;
+            action = ", reopening logs";
+            break;
+
+        case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):
+            if (getppid() > 1 || ngx_new_binary > 0) {
+
+                /*
+                 * Ignore the signal in the new binary if its parent is
+                 * not the init process, i.e. the old binary's process
+                 * is still running.  Or ingore the signal in the old binary's
+                 * process if the new binary's process is already running.
+                 */
+
+                action = ", ignoring";
+                ignore = 1;
+                break;
+            }
+
+            ngx_change_binary = 1;
+            action = ", changing binary";
+            break;
+
+        case SIGALRM:
+            if (!ngx_terminate) {
+                ngx_timer = 1;
+                action = ", shutting down old worker processes";
+            }
+
+            break;
+
+        case SIGIO:
+            ngx_sigio = 1;
+            break;
+
+        case SIGCHLD:
+            ngx_reap = 1;
+            break;
+        }
+
+        break;
+
+    case NGX_PROCESS_WORKER:
+        switch (signo) {
+
+        case ngx_signal_value(NGX_NOACCEPT_SIGNAL):
+            ngx_debug_quit = 1;
+        case ngx_signal_value(NGX_SHUTDOWN_SIGNAL):
+            ngx_quit = 1;
+            action = ", shutting down";
+            break;
+
+        case ngx_signal_value(NGX_TERMINATE_SIGNAL):
+        case SIGINT:
+            ngx_terminate = 1;
+            action = ", exiting";
+            break;
+
+        case ngx_signal_value(NGX_REOPEN_SIGNAL):
+            ngx_reopen = 1;
+            action = ", reopening logs";
+            break;
+
+        case ngx_signal_value(NGX_RECONFIGURE_SIGNAL):
+        case ngx_signal_value(NGX_CHANGEBIN_SIGNAL):
+        case SIGIO:
+            action = ", ignoring";
+            break;
+        }
+
+        break;
+    }
+
+    ngx_log_error(NGX_LOG_NOTICE, ngx_cycle->log, 0,
+                  "signal %d (%s) received%s", signo, sig->signame, action);
+
+    if (ignore) {
+        ngx_log_error(NGX_LOG_CRIT, ngx_cycle->log, 0,
+                      "the changing binary signal is ignored: "
+                      "you should shutdown or terminate "
+                      "before either old or new binary's process");
+    }
+
+    if (signo == SIGCHLD) {
+        ngx_process_get_status();
+    }
+
+    ngx_set_errno(err);
+}
+
+
+static void
 ngx_process_get_status(void)
 {
     int              status;
--- a/src/os/unix/ngx_process.h
+++ b/src/os/unix/ngx_process.h
@@ -54,7 +54,7 @@ typedef struct {
 ngx_pid_t ngx_spawn_process(ngx_cycle_t *cycle,
     ngx_spawn_proc_pt proc, void *data, char *name, ngx_int_t respawn);
 ngx_pid_t ngx_execute(ngx_cycle_t *cycle, ngx_exec_ctx_t *ctx);
-void ngx_process_get_status(void);
+ngx_int_t ngx_init_signals(ngx_log_t *log);
 void ngx_debug_point(void);
 
 
--- a/src/os/unix/ngx_solaris_config.h
+++ b/src/os/unix/ngx_solaris_config.h
@@ -83,4 +83,7 @@
 #endif
 
 
+#define NGX_HAVE_OS_SPECIFIC_INIT    1
+
+
 #endif /* _NGX_SOLARIS_CONFIG_H_INCLUDED_ */
--- a/src/os/unix/ngx_solaris_init.c
+++ b/src/os/unix/ngx_solaris_init.c
@@ -13,7 +13,7 @@ char ngx_solaris_release[10];
 char ngx_solaris_version[50];
 
 
-ngx_os_io_t ngx_os_io = {
+static ngx_os_io_t ngx_solaris_io = {
     ngx_unix_recv,
     ngx_readv_chain,
     ngx_unix_send,
@@ -27,10 +27,11 @@ ngx_os_io_t ngx_os_io = {
 };
 
 
-ngx_int_t ngx_os_init(ngx_log_t *log)
+ngx_int_t
+ngx_os_specific_init(ngx_log_t *log)
 {
     if (sysinfo(SI_SYSNAME, ngx_solaris_sysname, sizeof(ngx_solaris_sysname))
-                                                                         == -1)
+        == -1)
     {
         ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                       "sysinfo(SI_SYSNAME) failed");
@@ -38,7 +39,7 @@ ngx_int_t ngx_os_init(ngx_log_t *log)
     }
 
     if (sysinfo(SI_RELEASE, ngx_solaris_release, sizeof(ngx_solaris_release))
-                                                                         == -1)
+        == -1)
     {
         ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                       "sysinfo(SI_RELEASE) failed");
@@ -46,7 +47,7 @@ ngx_int_t ngx_os_init(ngx_log_t *log)
     }
 
     if (sysinfo(SI_VERSION, ngx_solaris_version, sizeof(ngx_solaris_version))
-                                                                         == -1)
+        == -1)
     {
         ngx_log_error(NGX_LOG_ALERT, log, ngx_errno,
                       "sysinfo(SI_SYSNAME) failed");
@@ -54,11 +55,14 @@ ngx_int_t ngx_os_init(ngx_log_t *log)
     }
 
 
-    return ngx_posix_init(log);
+    ngx_os_io = ngx_solaris_io;
+
+    return NGX_OK;;
 }
 
 
-void ngx_os_status(ngx_log_t *log)
+void
+ngx_os_specific_status(ngx_log_t *log)
 {
 
     ngx_log_error(NGX_LOG_NOTICE, log, 0, "OS: %s %s",
@@ -66,6 +70,4 @@ void ngx_os_status(ngx_log_t *log)
 
     ngx_log_error(NGX_LOG_NOTICE, log, 0, "version: %s",
                   ngx_solaris_version);
-
-    ngx_posix_status(log);
 }