nginx-0.1.44-RELEASE import

    *) 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.
diff --git a/src/imap/ngx_imap.h b/src/imap/ngx_imap.h
index 8a506db..5e03774 100644
--- 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 @@
     ngx_msec_t            timeout;
 
     size_t                imap_client_buffer_size;
-    size_t                proxy_buffer_size;
 
     ngx_uint_t            protocol;
 
@@ -82,8 +86,8 @@
 
     ngx_connection_t       *connection;
 
-    ngx_buf_t              *buffer;
     ngx_str_t               out;
+    ngx_buf_t              *buffer;
 
     void                  **ctx;
     void                  **main_conf;
@@ -93,6 +97,8 @@
 
     ngx_uint_t              imap_state;
 
+    unsigned                blocked:1;
+    unsigned                quit:1;
     unsigned                protocol:1;
     unsigned                quoted:1;
 
@@ -100,6 +106,7 @@
     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 @@
 
 
 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);
diff --git a/src/imap/ngx_imap_auth_http_module.c b/src/imap/ngx_imap_auth_http_module.c
index e66593a..2bf0cc9 100644
--- a/src/imap/ngx_imap_auth_http_module.c
+++ b/src/imap/ngx_imap_auth_http_module.c
@@ -516,13 +516,14 @@
             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;
diff --git a/src/imap/ngx_imap_core_module.c b/src/imap/ngx_imap_core_module.c
index e5d7a20..8225574 100644
--- a/src/imap/ngx_imap_core_module.c
+++ b/src/imap/ngx_imap_core_module.c
@@ -75,13 +75,6 @@
       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 @@
     }
 
     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_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);
diff --git a/src/imap/ngx_imap_handler.c b/src/imap/ngx_imap_handler.c
index 25509b2..03dedaf 100644
--- 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 @@
 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 @@
     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_IMAP_SSL)
 
-    if (ngx_send(c, greetings[cscf->protocol].data, size) < size) {
-        /*
-         * we treat the incomplete sending as NGX_ERROR
-         * because it is very strange here
-         */
+    sslcf = ngx_imap_get_module_srv_conf(ctx, ngx_imap_ssl_module);
+
+    if (sslcf->enable) {
+
+        if (ngx_ssl_create_session(sslcf->ssl_ctx, c, NGX_SSL_BUFFER)
+            == NGX_ERROR)
+        {
+            ngx_imap_close_connection(c);
+            return;
+        }
+
+        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->read->handler = ngx_imap_init_session;
+    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 @@
     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 @@
         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 @@
 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 @@
         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 @@
         return;
     }
 
-    quit = 0;
     tag = 1;
 
     text = NULL;
@@ -235,9 +326,9 @@
             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 @@
     }
 
     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 @@
         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 @@
     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 @@
         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 @@
                 break;
 
             case NGX_POP3_QUIT:
-                quit = 1;
+                s->quit = 1;
                 break;
 
             case NGX_POP3_NOOP:
@@ -441,7 +524,7 @@
                 break;
 
             case NGX_POP3_QUIT:
-                quit = 1;
+                s->quit = 1;
                 break;
 
             case NGX_POP3_NOOP:
@@ -466,23 +549,14 @@
         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 @@
     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 @@
 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_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
diff --git a/src/imap/ngx_imap_proxy_module.c b/src/imap/ngx_imap_proxy_module.c
index be32e6f..f9a84ce 100644
--- 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 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 @@
       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 @@
 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 @@
     }
 
     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 @@
         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 @@
         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 @@
 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 @@
     }
 
     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 @@
         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 @@
         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 @@
 
     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 @@
 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 @@
             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 @@
         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 @@
                     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 @@
     }
 
     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_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;
 }
diff --git a/src/imap/ngx_imap_ssl_module.c b/src/imap/ngx_imap_ssl_module.c
new file mode 100644
index 0000000..e5834a6
--- /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;
+}
diff --git a/src/imap/ngx_imap_ssl_module.h b/src/imap/ngx_imap_ssl_module.h
new file mode 100644
index 0000000..150a617
--- /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_ */