changeset 420:ad0a34a8efa6 NGINX_0_7_22

nginx 0.7.22 *) Feature: the "none" parameter in the "smtp_auth" directive. Thanks to Maxim Dounin. *) Feature: the "$cookie_..." variables. *) Bugfix: the "directio" directive did not work in XFS filesystem. *) Bugfix: the resolver did not understand big DNS responses. Thanks to Zyb.
author Igor Sysoev <http://sysoev.ru>
date Thu, 20 Nov 2008 00:00:00 +0300
parents b986babf3f57
children 10e4013f5f54
files CHANGES CHANGES.ru src/core/nginx.h src/core/ngx_output_chain.c src/core/ngx_resolver.c src/event/ngx_event_openssl.c src/http/modules/ngx_http_limit_req_module.c src/http/modules/perl/nginx.pm src/http/ngx_http_upstream.c src/http/ngx_http_variables.c src/mail/ngx_mail.h src/mail/ngx_mail_auth_http_module.c src/mail/ngx_mail_imap_module.c src/mail/ngx_mail_parse.c src/mail/ngx_mail_proxy_module.c src/mail/ngx_mail_smtp_handler.c src/mail/ngx_mail_smtp_module.c
diffstat 17 files changed, 484 insertions(+), 118 deletions(-) [+]
line wrap: on
line diff
--- a/CHANGES
+++ b/CHANGES
@@ -1,4 +1,17 @@
 
+Changes with nginx 0.7.22                                        20 Nov 2008
+
+    *) Feature: the "none" parameter in the "smtp_auth" directive.
+       Thanks to Maxim Dounin.
+
+    *) Feature: the "$cookie_..." variables.
+
+    *) Bugfix: the "directio" directive did not work in XFS filesystem.
+
+    *) Bugfix: the resolver did not understand big DNS responses.
+       Thanks to Zyb.
+
+
 Changes with nginx 0.7.21                                        11 Nov 2008
 
     *) Changes in the ngx_http_limit_req_module.
--- a/CHANGES.ru
+++ b/CHANGES.ru
@@ -1,4 +1,17 @@
 
+Изменения в nginx 0.7.22                                          20.11.2008
+
+    *) Добавление: параметр none в директиве smtp_auth.
+       Спасибо Максиму Дунину.
+
+    *) Добавление: переменные "$cookie_...".
+
+    *) Исправление: директива directio не работала с файловой системой XFS.
+
+    *) Исправление: resolver не понимал большие DNS-ответы.
+       Спасибо Zyb.
+
+
 Изменения в nginx 0.7.21                                          11.11.2008
 
     *) Изменения в модуле ngx_http_limit_req_module.
--- a/src/core/nginx.h
+++ b/src/core/nginx.h
@@ -8,7 +8,7 @@
 #define _NGINX_H_INCLUDED_
 
 
-#define NGINX_VERSION      "0.7.21"
+#define NGINX_VERSION      "0.7.22"
 #define NGINX_VER          "nginx/" NGINX_VERSION
 
 #define NGINX_VAR          "NGINX"
--- a/src/core/ngx_output_chain.c
+++ b/src/core/ngx_output_chain.c
@@ -13,6 +13,17 @@
 #define NGX_SENDFILE_LIMIT  4096
 #endif
 
+/*
+ * When DIRECTIO is enabled FreeBSD, Solaris, and MacOSX read directly
+ * to an application memory from a device if parameters are aligned
+ * to device sector boundary(512 bytes).  They fallback to usual read
+ * operation if the parameters are not aligned.
+ * Linux allows DIRECTIO only if the parameters are aligned to a filesystem
+ * sector boundary, otherwise it returns EINVAL.  The sector size is
+ * usually 512 bytes, however, on XFS it may be 4096 bytes.
+ */
+#define NGX_DIRECTIO_BLOCK  4096
+
 
 #define NGX_NONE            1
 
@@ -327,7 +338,7 @@ ngx_output_chain_align_file_buf(ngx_outp
 
     ctx->directio = 1;
 
-    size = (size_t) (in->file_pos - (in->file_pos & ~511));
+    size = (size_t) (in->file_pos - (in->file_pos & ~(NGX_DIRECTIO_BLOCK - 1)));
 
     if (size == 0) {
 
@@ -338,7 +349,7 @@ ngx_output_chain_align_file_buf(ngx_outp
         size = (size_t) bsize;
 
     } else {
-        size = 512 - size;
+        size = NGX_DIRECTIO_BLOCK - size;
 
         if ((off_t) size > bsize) {
             size = (size_t) bsize;
@@ -413,7 +424,7 @@ ngx_output_chain_get_buf(ngx_output_chai
          * userland buffer direct usage conjunctly with directio
          */
 
-        b->start = ngx_pmemalign(ctx->pool, size, 512);
+        b->start = ngx_pmemalign(ctx->pool, size, NGX_DIRECTIO_BLOCK);
         if (b->start == NULL) {
             return NGX_ERROR;
         }
--- a/src/core/ngx_resolver.c
+++ b/src/core/ngx_resolver.c
@@ -1866,7 +1866,7 @@ ngx_resolver_copy(ngx_resolver_t *r, ngx
         }
 
         if (n & 0xc0) {
-            n = (n & 0x3f << 8) + *p;
+            n = ((n & 0x3f) << 8) + *p;
             p = &buf[n];
 
         } else {
@@ -1916,7 +1916,7 @@ done:
             }
 
         } else {
-            n = (n & 0x3f << 8) + *src;
+            n = ((n & 0x3f) << 8) + *src;
             src = &buf[n];
 
             n = *src++;
--- a/src/event/ngx_event_openssl.c
+++ b/src/event/ngx_event_openssl.c
@@ -1240,23 +1240,37 @@ ngx_ssl_connection_error(ngx_connection_
         n = ERR_GET_REASON(ERR_peek_error());
 
             /* handshake failures */
-        if (n == SSL_R_DIGEST_CHECK_FAILED
-            || n == SSL_R_NO_SHARED_CIPHER
-            || n == SSL_R_UNEXPECTED_MESSAGE
-            || n == SSL_R_WRONG_VERSION_NUMBER
-            || n == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC
+        if (n == SSL_R_DIGEST_CHECK_FAILED                           /*  149 */
+            || n == SSL_R_NO_CIPHERS_PASSED                          /*  182 */
+            || n == SSL_R_NO_SHARED_CIPHER                           /*  193 */
+            || n == SSL_R_UNEXPECTED_MESSAGE                         /*  244 */
+            || n == SSL_R_UNEXPECTED_RECORD                          /*  245 */
+            || n == SSL_R_WRONG_VERSION_NUMBER                       /*  267 */
+            || n == SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC        /*  281 */
             || n == 1000 /* SSL_R_SSLV3_ALERT_CLOSE_NOTIFY */
-            || n == SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE
-            || n == SSL_R_SSLV3_ALERT_BAD_RECORD_MAC
-            || n == SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE
-            || n == SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE
-            || n == SSL_R_SSLV3_ALERT_BAD_CERTIFICATE
-            || n == SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE
-            || n == SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED
-            || n == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED
-            || n == SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN
-            || n == SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER
-            || n == SSL_R_TLSV1_ALERT_UNKNOWN_CA)
+            || n == SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE             /* 1010 */
+            || n == SSL_R_SSLV3_ALERT_BAD_RECORD_MAC                 /* 1020 */
+            || n == SSL_R_TLSV1_ALERT_DECRYPTION_FAILED              /* 1021 */
+            || n == SSL_R_TLSV1_ALERT_RECORD_OVERFLOW                /* 1022 */
+            || n == SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE          /* 1030 */
+            || n == SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE              /* 1040 */
+            || n == SSL_R_SSLV3_ALERT_NO_CERTIFICATE                 /* 1041 */
+            || n == SSL_R_SSLV3_ALERT_BAD_CERTIFICATE                /* 1042 */
+            || n == SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE        /* 1043 */
+            || n == SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED            /* 1044 */
+            || n == SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED            /* 1045 */
+            || n == SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN            /* 1046 */
+            || n == SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER              /* 1047 */
+            || n == SSL_R_TLSV1_ALERT_UNKNOWN_CA                     /* 1048 */
+            || n == SSL_R_TLSV1_ALERT_ACCESS_DENIED                  /* 1049 */
+            || n == SSL_R_TLSV1_ALERT_DECODE_ERROR                   /* 1050 */
+            || n == SSL_R_TLSV1_ALERT_DECRYPT_ERROR                  /* 1051 */
+            || n == SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION             /* 1060 */
+            || n == SSL_R_TLSV1_ALERT_PROTOCOL_VERSION               /* 1070 */
+            || n == SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY          /* 1071 */
+            || n == SSL_R_TLSV1_ALERT_INTERNAL_ERROR                 /* 1080 */
+            || n == SSL_R_TLSV1_ALERT_USER_CANCELLED                 /* 1090 */
+            || n == SSL_R_TLSV1_ALERT_NO_RENEGOTIATION)              /* 1100 */
         {
             switch (c->log_error) {
 
--- a/src/http/modules/ngx_http_limit_req_module.c
+++ b/src/http/modules/ngx_http_limit_req_module.c
@@ -15,7 +15,7 @@ typedef struct {
     u_short             len;
     ngx_queue_t         queue;
     ngx_msec_t          last;
-    float               rate;
+    ngx_uint_t          excess; /* integer value, 1 corresponds to 0.001 r/s */
     u_char              data[1];
 } ngx_http_limit_req_node_t;
 
@@ -24,7 +24,7 @@ typedef struct {
     ngx_rbtree_t       *rbtree;
     ngx_queue_t        *queue;
     ngx_slab_pool_t    *shpool;
-    float               rate;
+    ngx_uint_t          rate;   /* integer value, 1 corresponds to 0.001 r/s */
     ngx_int_t           index;
     ngx_str_t           var;
 } ngx_http_limit_req_ctx_t;
@@ -32,8 +32,8 @@ typedef struct {
 
 typedef struct {
     ngx_shm_zone_t     *shm_zone;
-    float               burst;
-    ngx_uint_t          nodelay;     /* unsigned  nodelay:1 */
+    ngx_uint_t          burst;  /* integer value, 1 corresponds to 0.001 r/s */
+    ngx_uint_t          nodelay;/* unsigned  nodelay:1 */
 } ngx_http_limit_req_conf_t;
 
 
@@ -107,10 +107,10 @@ ngx_module_t  ngx_http_limit_req_module 
 static ngx_int_t
 ngx_http_limit_req_handler(ngx_http_request_t *r)
 {
-    float                       rate;
     size_t                      len, n;
     uint32_t                    hash;
     ngx_int_t                   rc;
+    ngx_uint_t                  excess;
     ngx_time_t                 *tp;
     ngx_rbtree_node_t          *node;
     ngx_http_variable_value_t  *vv;
@@ -165,20 +165,21 @@ ngx_http_limit_req_handler(ngx_http_requ
 
         ngx_queue_insert_head(ctx->queue, &lz->queue);
 
-        rate = lz->rate;
+        excess = lz->excess;
 
     } else {
-        rate = 0.0;
+        excess = 0;
     }
 
-    ngx_log_debug2(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
-                  "limit_req: %i %.3f", rc, rate);
+    ngx_log_debug3(NGX_LOG_DEBUG_HTTP, r->connection->log, 0,
+                  "limit_req: %i %ui.%03ui", rc, excess / 1000, excess % 1000);
 
     if (rc == NGX_BUSY) {
         ngx_shmtx_unlock(&ctx->shpool->mutex);
 
         ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
-                      "limiting requests, excess: %.3f", rate);
+                      "limiting requests, excess: %ui.%03ui",
+                      excess / 1000, excess % 1000);
 
         return NGX_HTTP_SERVICE_UNAVAILABLE;
     }
@@ -191,7 +192,8 @@ ngx_http_limit_req_handler(ngx_http_requ
         }
 
         ngx_log_error(NGX_LOG_WARN, r->connection->log, 0,
-                      "delaying request, excess: %.3f", rate);
+                      "delaying request, excess: %ui.%03ui",
+                      excess / 1000, excess % 1000);
 
         if (ngx_handle_read_event(r->connection->read, 0) != NGX_OK) {
             return NGX_HTTP_INTERNAL_SERVER_ERROR;
@@ -199,7 +201,7 @@ ngx_http_limit_req_handler(ngx_http_requ
 
         r->read_event_handler = ngx_http_test_reading;
         r->write_event_handler = ngx_http_limit_req_delay;
-        ngx_add_timer(r->connection->write, (ngx_msec_t) (rate * 1000));
+        ngx_add_timer(r->connection->write, (ngx_msec_t) excess);
 
         return NGX_AGAIN;
     }
@@ -234,7 +236,7 @@ ngx_http_limit_req_handler(ngx_http_requ
     tp = ngx_timeofday();
     lz->last = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
 
-    lz->rate = 0.0;
+    lz->excess = 0;
     ngx_memcpy(lz->data, vv->data, len);
 
     ngx_rbtree_insert(ctx->rbtree, node);
@@ -312,7 +314,7 @@ static ngx_int_t
 ngx_http_limit_req_lookup(ngx_http_limit_req_conf_t *lzcf, ngx_uint_t hash,
     u_char *data, size_t len, ngx_http_limit_req_node_t **lzp)
 {
-    ngx_int_t                   rc;
+    ngx_int_t                   rc, excess;
     ngx_time_t                 *tp;
     ngx_msec_t                  now;
     ngx_msec_int_t              ms;
@@ -351,21 +353,22 @@ ngx_http_limit_req_lookup(ngx_http_limit
                 now = (ngx_msec_t) (tp->sec * 1000 + tp->msec);
                 ms = (ngx_msec_int_t) (now - lz->last);
 
-                lz->rate = lz->rate - ctx->rate * ngx_abs(ms) / 1000 + 1;
+                excess = lz->excess - ctx->rate * ngx_abs(ms) / 1000 + 1000;
 
-                if (lz->rate < 0.0) {
-                    lz->rate = 0.0;
+                if (excess < 0) {
+                    excess = 0;
                 }
 
+                lz->excess = excess;
                 lz->last = now;
 
                 *lzp = lz;
 
-                if (lz->rate > lzcf->burst) {
+                if ((ngx_uint_t) excess > lzcf->burst) {
                     return NGX_BUSY;
                 }
 
-                if (lz->rate > 0.0) {
+                if (excess) {
                     return NGX_AGAIN;
                 }
 
@@ -388,7 +391,7 @@ ngx_http_limit_req_lookup(ngx_http_limit
 static void
 ngx_http_limit_req_expire(ngx_http_limit_req_ctx_t *ctx, ngx_uint_t n)
 {
-    float                       rate;
+    ngx_int_t                   excess;
     ngx_time_t                 *tp;
     ngx_msec_t                  now;
     ngx_queue_t                *q;
@@ -425,9 +428,9 @@ ngx_http_limit_req_expire(ngx_http_limit
                 return;
             }
 
-            rate = lz->rate - ctx->rate * ms / 1000;
+            excess = lz->excess - ctx->rate * ms / 1000;
 
-            if (rate > 0.0) {
+            if (excess > 0) {
                 return;
             }
         }
@@ -641,7 +644,7 @@ ngx_http_limit_req_zone(ngx_conf_t *cf, 
         return NGX_CONF_ERROR;
     }
 
-    ctx->rate = (float) rate / scale;
+    ctx->rate = rate * 1000 / scale;
 
     shm_zone = ngx_shared_memory_add(cf, &name, size,
                                      &ngx_http_limit_req_module);
@@ -734,7 +737,7 @@ ngx_http_limit_req(ngx_conf_t *cf, ngx_c
         return NGX_CONF_ERROR;
     }
 
-    lzcf->burst = (float) burst;
+    lzcf->burst = burst * 1000;
 
     return NGX_CONF_OK;
 }
--- a/src/http/modules/perl/nginx.pm
+++ b/src/http/modules/perl/nginx.pm
@@ -47,7 +47,7 @@ our @EXPORT = qw(
     HTTP_INSUFFICIENT_STORAGE
 );
 
-our $VERSION = '0.7.21';
+our $VERSION = '0.7.22';
 
 require XSLoader;
 XSLoader::load('nginx', $VERSION);
--- a/src/http/ngx_http_upstream.c
+++ b/src/http/ngx_http_upstream.c
@@ -1337,14 +1337,20 @@ ngx_http_upstream_process_header(ngx_eve
         return;
     }
 
-    if (u->buffer.last - u->buffer.pos >= (ssize_t) u->length) {
-        if (u->input_filter(u->input_filter_ctx, 0) == NGX_ERROR) {
+    n = u->buffer.last - u->buffer.pos;
+
+    if (n) {
+        u->buffer.last -= n;
+
+        if (u->input_filter(u->input_filter_ctx, n) == NGX_ERROR) {
             ngx_http_upstream_finalize_request(r, u, NGX_ERROR);
             return;
         }
 
-        ngx_http_upstream_finalize_request(r, u, 0);
-        return;
+        if (u->length == 0) {
+            ngx_http_upstream_finalize_request(r, u, 0);
+            return;
+        }
     }
 
     rev->handler = ngx_http_upstream_process_body_in_memory;
--- a/src/http/ngx_http_variables.c
+++ b/src/http/ngx_http_variables.c
@@ -488,6 +488,15 @@ ngx_http_get_variable(ngx_http_request_t
         return NULL;
     }
 
+    if (ngx_strncmp(name->data, "cookie_", 7) == 0) {
+
+        if (ngx_http_variable_argument(r, vv, (uintptr_t) name) == NGX_OK) {
+            return vv;
+        }
+
+        return NULL;
+    }
+
     if (ngx_strncmp(name->data, "arg_", 4) == 0) {
 
         if (ngx_http_variable_argument(r, vv, (uintptr_t) name) == NGX_OK) {
@@ -728,6 +737,34 @@ ngx_http_variable_unknown_header(ngx_htt
 
 
 static ngx_int_t
+ngx_http_variable_cookie(ngx_http_request_t *r, ngx_http_variable_value_t *v,
+    uintptr_t data)
+{
+    ngx_str_t *name = (ngx_str_t *) data;
+
+    ngx_str_t  cookie, s;
+
+    s.len = name->len - (sizeof("cookie_") - 1);
+    s.data = name->data + sizeof("cookie_") - 1;
+
+    if (ngx_http_parse_multi_header_lines(&r->headers_in.cookies, &s, &cookie)
+        == NGX_DECLINED)
+    {
+        v->not_found = 1;
+        return NGX_OK;
+    }
+
+    v->len = cookie.len;
+    v->valid = 1;
+    v->no_cacheable = 0;
+    v->not_found = 0;
+    v->data = cookie.data;
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
 ngx_http_variable_argument(ngx_http_request_t *r, ngx_http_variable_value_t *v,
     uintptr_t data)
 {
@@ -1544,6 +1581,13 @@ ngx_http_variables_init_vars(ngx_conf_t 
             continue;
         }
 
+        if (ngx_strncmp(v[i].name.data, "cookie_", 7) == 0) {
+            v[i].get_handler = ngx_http_variable_cookie;
+            v[i].data = (uintptr_t) &v[i].name;
+
+            continue;
+        }
+
         if (ngx_strncmp(v[i].name.data, "arg_", 4) == 0) {
             v[i].get_handler = ngx_http_variable_argument;
             v[i].data = (uintptr_t) &v[i].name;
--- a/src/mail/ngx_mail.h
+++ b/src/mail/ngx_mail.h
@@ -136,8 +136,12 @@ typedef enum {
     ngx_smtp_auth_plain,
     ngx_smtp_auth_cram_md5,
     ngx_smtp_helo,
-    ngx_smtp_noxclient,
-    ngx_smtp_xclient
+    ngx_smtp_helo_xclient,
+    ngx_smtp_helo_from,
+    ngx_smtp_xclient,
+    ngx_smtp_xclient_from,
+    ngx_smtp_from,
+    ngx_smtp_to
 } ngx_smtp_state_e;
 
 
@@ -173,7 +177,7 @@ typedef struct {
     unsigned                no_sync_literal:1;
     unsigned                starttls:1;
     unsigned                esmtp:1;
-    unsigned                auth_method:2;
+    unsigned                auth_method:3;
     unsigned                auth_wait:1;
 
     ngx_str_t               login;
@@ -187,6 +191,8 @@ typedef struct {
     ngx_str_t              *addr_text;
     ngx_str_t               host;
     ngx_str_t               smtp_helo;
+    ngx_str_t               smtp_from;
+    ngx_str_t               smtp_to;
 
     ngx_uint_t              command;
     ngx_array_t             args;
@@ -256,12 +262,14 @@ typedef struct {
 #define NGX_MAIL_AUTH_LOGIN     1
 #define NGX_MAIL_AUTH_APOP      2
 #define NGX_MAIL_AUTH_CRAM_MD5  3
+#define NGX_MAIL_AUTH_NONE      4
 
 
 #define NGX_MAIL_AUTH_PLAIN_ENABLED     0x0002
 #define NGX_MAIL_AUTH_LOGIN_ENABLED     0x0004
 #define NGX_MAIL_AUTH_APOP_ENABLED      0x0008
 #define NGX_MAIL_AUTH_CRAM_MD5_ENABLED  0x0010
+#define NGX_MAIL_AUTH_NONE_ENABLED      0x0020
 
 
 #define NGX_MAIL_PARSE_INVALID_COMMAND  20
--- a/src/mail/ngx_mail_auth_http_module.c
+++ b/src/mail/ngx_mail_auth_http_module.c
@@ -40,7 +40,6 @@ struct ngx_mail_auth_http_ctx_s {
     ngx_mail_auth_http_handler_pt   handler;
 
     ngx_uint_t                      state;
-    ngx_uint_t                      hash;   /* no needed ? */
 
     u_char                         *header_name_start;
     u_char                         *header_name_end;
@@ -141,7 +140,8 @@ static ngx_str_t   ngx_mail_auth_http_me
     ngx_string("plain"),
     ngx_string("plain"),
     ngx_string("apop"),
-    ngx_string("cram-md5")
+    ngx_string("cram-md5"),
+    ngx_string("none")
 };
 
 static ngx_str_t   ngx_mail_smtp_errcode = ngx_string("535 5.7.0");
@@ -914,7 +914,6 @@ ngx_mail_auth_http_parse_header_line(ngx
     ngx_mail_auth_http_ctx_t *ctx)
 {
     u_char      c, ch, *p;
-    ngx_uint_t  hash;
     enum {
         sw_start = 0,
         sw_name,
@@ -926,7 +925,6 @@ ngx_mail_auth_http_parse_header_line(ngx
     } state;
 
     state = ctx->state;
-    hash = ctx->hash;
 
     for (p = ctx->response->pos; p < ctx->response->last; p++) {
         ch = *p;
@@ -950,12 +948,10 @@ ngx_mail_auth_http_parse_header_line(ngx
 
                 c = (u_char) (ch | 0x20);
                 if (c >= 'a' && c <= 'z') {
-                    hash = c;
                     break;
                 }
 
                 if (ch >= '0' && ch <= '9') {
-                    hash = ch;
                     break;
                 }
 
@@ -967,7 +963,6 @@ ngx_mail_auth_http_parse_header_line(ngx
         case sw_name:
             c = (u_char) (ch | 0x20);
             if (c >= 'a' && c <= 'z') {
-                hash += c;
                 break;
             }
 
@@ -978,12 +973,10 @@ ngx_mail_auth_http_parse_header_line(ngx
             }
 
             if (ch == '-') {
-                hash += ch;
                 break;
             }
 
             if (ch >= '0' && ch <= '9') {
-                hash += ch;
                 break;
             }
 
@@ -1080,7 +1073,6 @@ ngx_mail_auth_http_parse_header_line(ngx
 
     ctx->response->pos = p;
     ctx->state = state;
-    ctx->hash = hash;
 
     return NGX_AGAIN;
 
@@ -1088,7 +1080,6 @@ done:
 
     ctx->response->pos = p + 1;
     ctx->state = sw_start;
-    ctx->hash = hash;
 
     return NGX_OK;
 
@@ -1165,6 +1156,10 @@ ngx_mail_auth_http_create_request(ngx_ma
                 + sizeof(CRLF) - 1
           + sizeof("Client-IP: ") - 1 + s->connection->addr_text.len
                 + sizeof(CRLF) - 1
+          + sizeof("Client-Host: ") - 1 + s->host.len + sizeof(CRLF) - 1
+          + sizeof("Auth-SMTP-Helo: ") - 1 + s->smtp_helo.len
+          + sizeof("Auth-SMTP-From: ") - 1 + s->smtp_from.len
+          + sizeof("Auth-SMTP-To: ") - 1 + s->smtp_to.len
           + ahcf->header.len
           + sizeof(CRLF) - 1;
 
@@ -1216,9 +1211,37 @@ ngx_mail_auth_http_create_request(ngx_ma
 
     b->last = ngx_cpymem(b->last, "Client-IP: ", sizeof("Client-IP: ") - 1);
     b->last = ngx_copy(b->last, s->connection->addr_text.data,
-                         s->connection->addr_text.len);
+                       s->connection->addr_text.len);
     *b->last++ = CR; *b->last++ = LF;
 
+    if (s->host.len) {
+        b->last = ngx_cpymem(b->last, "Client-Host: ",
+                             sizeof("Client-Host: ") - 1);
+        b->last = ngx_copy(b->last, s->host.data, s->host.len);
+        *b->last++ = CR; *b->last++ = LF;
+    }
+
+    if (s->auth_method == NGX_MAIL_AUTH_NONE) {
+
+        /* HELO, MAIL FROM, and RCPT TO can't contain CRLF, no need to escape */
+
+        b->last = ngx_cpymem(b->last, "Auth-SMTP-Helo: ",
+                             sizeof("Auth-SMTP-Helo: ") - 1);
+        b->last = ngx_copy(b->last, s->smtp_helo.data, s->smtp_helo.len);
+        *b->last++ = CR; *b->last++ = LF;
+
+        b->last = ngx_cpymem(b->last, "Auth-SMTP-From: ",
+                             sizeof("Auth-SMTP-From: ") - 1);
+        b->last = ngx_copy(b->last, s->smtp_from.data, s->smtp_from.len);
+        *b->last++ = CR; *b->last++ = LF;
+
+        b->last = ngx_cpymem(b->last, "Auth-SMTP-To: ",
+                             sizeof("Auth-SMTP-To: ") - 1);
+        b->last = ngx_copy(b->last, s->smtp_to.data, s->smtp_to.len);
+        *b->last++ = CR; *b->last++ = LF;
+
+    }
+
     if (ahcf->header.len) {
         b->last = ngx_copy(b->last, ahcf->header.data, ahcf->header.len);
     }
--- a/src/mail/ngx_mail_imap_module.c
+++ b/src/mail/ngx_mail_imap_module.c
@@ -36,7 +36,8 @@ static ngx_str_t  ngx_mail_imap_auth_met
     ngx_string("AUTH=PLAIN"),
     ngx_string("AUTH=LOGIN"),
     ngx_null_string,  /* APOP */
-    ngx_string("AUTH=CRAM-MD5")
+    ngx_string("AUTH=CRAM-MD5"),
+    ngx_null_string   /* NONE */
 };
 
 
--- a/src/mail/ngx_mail_parse.c
+++ b/src/mail/ngx_mail_parse.c
@@ -746,7 +746,7 @@ ngx_mail_smtp_parse_command(ngx_mail_ses
                 s->arg_end = p;
                 goto done;
             default:
-                if (s->args.nelts <= 2) {
+                if (s->args.nelts <= 10) {
                     state = sw_argument;
                     s->arg_start = p;
                     break;
--- a/src/mail/ngx_mail_proxy_module.c
+++ b/src/mail/ngx_mail_proxy_module.c
@@ -104,7 +104,7 @@ ngx_module_t  ngx_mail_proxy_module = {
 };
 
 
-static u_char  smtp_ok[] = "235 2.0.0 OK" CRLF;
+static u_char  smtp_ok[] = "250 2.0.0 OK" CRLF;
 
 
 void
@@ -465,6 +465,7 @@ ngx_mail_proxy_smtp_handler(ngx_event_t 
     u_char                    *p;
     ngx_int_t                  rc;
     ngx_str_t                  line;
+    ngx_buf_t                 *b;
     ngx_connection_t          *c;
     ngx_mail_session_t        *s;
     ngx_mail_proxy_conf_t     *pcf;
@@ -520,11 +521,19 @@ ngx_mail_proxy_smtp_handler(ngx_event_t 
         p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len);
         *p++ = CR; *p = LF;
 
-        s->mail_state = pcf->xclient ? ngx_smtp_helo: ngx_smtp_noxclient;
+        if (pcf->xclient) {
+            s->mail_state = ngx_smtp_helo_xclient;
+
+        } else if (s->auth_method == NGX_MAIL_AUTH_NONE) {
+            s->mail_state = ngx_smtp_helo_from;
+
+        } else {
+            s->mail_state = ngx_smtp_helo;
+        }
 
         break;
 
-    case ngx_smtp_helo:
+    case ngx_smtp_helo_xclient:
         ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
                        "mail proxy send xclient");
 
@@ -541,30 +550,73 @@ ngx_mail_proxy_smtp_handler(ngx_event_t 
             return;
         }
 
-        if (s->smtp_helo.len) {
-            line.len = ngx_sprintf(line.data,
-                           "XCLIENT PROTO=%sSMTP HELO=%V ADDR=%V LOGIN=%V "
-                           "NAME=%V" CRLF,
-                           (s->esmtp ? "E" : ""), &s->smtp_helo,
-                           &s->connection->addr_text, &s->login, &s->host)
-                       - line.data;
-        } else {
-            line.len = ngx_sprintf(line.data,
-                           "XCLIENT PROTO=SMTP ADDR=%V LOGIN=%V NAME=%V" CRLF,
-                           &s->connection->addr_text, &s->login, &s->host)
-                       - line.data;
+        line.len = ngx_sprintf(line.data,
+                       "XCLIENT PROTO=%sSMTP%s%V ADDR=%V%s%V NAME=%V" CRLF,
+                       (s->esmtp ? "E" : ""),
+                       (s->smtp_helo.len ? " HELO=" : ""), &s->smtp_helo,
+                       &s->connection->addr_text,
+                       (s->login.len ? " LOGIN=" : ""), &s->login, &s->host)
+                   - line.data;
+
+        s->mail_state = (s->auth_method == NGX_MAIL_AUTH_NONE) ?
+                            ngx_smtp_xclient_from : ngx_smtp_xclient;
+
+        break;
+
+    case ngx_smtp_helo_from:
+    case ngx_smtp_xclient_from:
+        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
+                       "mail proxy send mail from");
+
+        s->connection->log->action = "sending MAIL FROM to upstream";
+
+        line.len = s->smtp_from.len + sizeof(CRLF) - 1;
+        line.data = ngx_pnalloc(c->pool, line.len);
+        if (line.data == NULL) {
+            ngx_mail_proxy_internal_server_error(s);
+            return;
         }
 
-        s->mail_state = ngx_smtp_xclient;
+        p = ngx_cpymem(line.data, s->smtp_from.data, s->smtp_from.len);
+        *p++ = CR; *p = LF;
+
+        s->mail_state = ngx_smtp_from;
+
         break;
 
-    case ngx_smtp_noxclient:
-    case ngx_smtp_xclient:
+    case ngx_smtp_from:
+        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
+                       "mail proxy send rcpt to");
+
+        s->connection->log->action = "sending RCPT TO to upstream";
+
+        line.len = s->smtp_to.len + sizeof(CRLF) - 1;
+        line.data = ngx_pnalloc(c->pool, line.len);
+        if (line.data == NULL) {
+            ngx_mail_proxy_internal_server_error(s);
+            return;
+        }
+
+        p = ngx_cpymem(line.data, s->smtp_to.data, s->smtp_to.len);
+        *p++ = CR; *p = LF;
 
-        ngx_memcpy(s->proxy->buffer->start, smtp_ok, sizeof(smtp_ok) - 1);
+        s->mail_state = ngx_smtp_to;
+
+        break;
+
+    case ngx_smtp_helo:
+    case ngx_smtp_xclient:
+    case ngx_smtp_to:
 
-        s->proxy->buffer->pos = s->proxy->buffer->start;
-        s->proxy->buffer->last = s->proxy->buffer->start + sizeof(smtp_ok) - 1;
+        b = s->proxy->buffer;
+
+        if (s->auth_method == NGX_MAIL_AUTH_NONE) {
+            b->pos = b->start;
+
+        } else {
+            ngx_memcpy(b->start, smtp_ok, sizeof(smtp_ok) - 1);
+            b->last = b->start + sizeof(smtp_ok) - 1;
+        }
 
         s->connection->read->handler = ngx_mail_proxy_handler;
         s->connection->write->handler = ngx_mail_proxy_handler;
@@ -703,19 +755,30 @@ ngx_mail_proxy_read_response(ngx_mail_se
     default: /* NGX_MAIL_SMTP_PROTOCOL */
         switch (state) {
 
+        case ngx_smtp_start:
+            if (p[0] == '2' && p[1] == '2' && p[2] == '0') {
+                return NGX_OK;
+            }
+            break;
+
         case ngx_smtp_helo:
-        case ngx_smtp_noxclient:
+        case ngx_smtp_helo_xclient:
+        case ngx_smtp_helo_from:
+        case ngx_smtp_from:
             if (p[0] == '2' && p[1] == '5' && p[2] == '0') {
                 return NGX_OK;
             }
             break;
 
-        case ngx_smtp_start:
         case ngx_smtp_xclient:
-            if (p[0] == '2' && p[1] == '2' && p[2] == '0') {
+        case ngx_smtp_xclient_from:
+            if (p[0] == '2' && (p[1] == '2' || p[1] == '5') && p[2] == '0') {
                 return NGX_OK;
             }
             break;
+
+        case ngx_smtp_to:
+            return NGX_OK;
         }
 
         break;
--- a/src/mail/ngx_mail_smtp_handler.c
+++ b/src/mail/ngx_mail_smtp_handler.c
@@ -23,6 +23,8 @@ static ngx_int_t ngx_mail_smtp_auth(ngx_
 static ngx_int_t ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c);
 static ngx_int_t ngx_mail_smtp_starttls(ngx_mail_session_t *s,
     ngx_connection_t *c);
+static ngx_int_t ngx_mail_smtp_rset(ngx_mail_session_t *s, ngx_connection_t *c);
+static ngx_int_t ngx_mail_smtp_rcpt(ngx_mail_session_t *s, ngx_connection_t *c);
 
 static ngx_int_t ngx_mail_smtp_discard_command(ngx_mail_session_t *s,
     ngx_connection_t *c, char *err);
@@ -41,6 +43,7 @@ static u_char  smtp_invalid_pipelining[]
    "503 5.5.0 Improper use of SMTP command pipelining" CRLF;
 static u_char  smtp_invalid_argument[] = "501 5.5.4 Invalid argument" CRLF;
 static u_char  smtp_auth_required[] = "530 5.7.1 Authentication required" CRLF;
+static u_char  smtp_bad_sequence[] = "503 5.5.1 Bad sequence of commands" CRLF;
 
 
 static ngx_str_t  smtp_unavailable = ngx_string("[UNAVAILABLE]");
@@ -417,8 +420,15 @@ ngx_mail_smtp_auth_state(ngx_event_t *re
                 rc = ngx_mail_smtp_mail(s, c);
                 break;
 
+            case NGX_SMTP_RCPT:
+                rc = ngx_mail_smtp_rcpt(s, c);
+                break;
+
+            case NGX_SMTP_RSET:
+                rc = ngx_mail_smtp_rset(s, c);
+                break;
+
             case NGX_SMTP_NOOP:
-            case NGX_SMTP_RSET:
                 break;
 
             case NGX_SMTP_STARTTLS:
@@ -513,6 +523,11 @@ ngx_mail_smtp_helo(ngx_mail_session_t *s
 
     ngx_memcpy(s->smtp_helo.data, arg[0].data, arg[0].len);
 
+    s->smtp_from.len = 0;
+    s->smtp_from.data = NULL;
+    s->smtp_to.len = 0;
+    s->smtp_to.data = NULL;
+
     sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);
 
     if (s->command == NGX_SMTP_HELO) {
@@ -618,10 +633,136 @@ ngx_mail_smtp_auth(ngx_mail_session_t *s
 static ngx_int_t
 ngx_mail_smtp_mail(ngx_mail_session_t *s, ngx_connection_t *c)
 {
-    ngx_mail_smtp_log_rejected_command(s, c, "client was rejected: \"%V\"");
+    u_char                     ch;
+    ngx_str_t                  l;
+    ngx_uint_t                 i;
+    ngx_mail_smtp_srv_conf_t  *sscf;
+
+    sscf = ngx_mail_get_module_srv_conf(s, ngx_mail_smtp_module);
+
+    if (!(sscf->auth_methods & NGX_MAIL_AUTH_NONE_ENABLED)) {
+        ngx_mail_smtp_log_rejected_command(s, c, "client was rejected: \"%V\"");
+
+        s->out.len = sizeof(smtp_auth_required) - 1;
+        s->out.data = smtp_auth_required;
+
+        return NGX_OK;
+    }
+
+    /* auth none */
+
+    if (s->smtp_from.len) {
+        s->out.len = sizeof(smtp_bad_sequence) - 1;
+        s->out.data = smtp_bad_sequence;
+        return NGX_OK;
+    }
+
+    l.len = s->buffer->last - s->buffer->start;
+    l.data = s->buffer->start;
+
+    for (i = 0; i < l.len; i++) {
+        ch = l.data[i];
+
+        if (ch != CR && ch != LF) {
+            continue;
+        }
+
+        l.data[i] = ' ';
+    }
+
+    while (i) {
+        if (l.data[i - 1] != ' ') {
+            break;
+        }
+
+        i--;
+    }
+
+    l.len = i;
+
+    s->smtp_from.len = l.len;
+
+    s->smtp_from.data = ngx_pnalloc(c->pool, l.len);
+    if (s->smtp_from.data == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_memcpy(s->smtp_from.data, l.data, l.len);
+
+    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+                   "smtp mail from:\"%V\"", &s->smtp_from);
+
+    s->out.len = sizeof(smtp_ok) - 1;
+    s->out.data = smtp_ok;
+
+    return NGX_OK;
+}
 
-    s->out.len = sizeof(smtp_auth_required) - 1;
-    s->out.data = smtp_auth_required;
+
+static ngx_int_t
+ngx_mail_smtp_rcpt(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+    u_char      ch;
+    ngx_str_t   l;
+    ngx_uint_t  i;
+
+    if (s->smtp_from.len == 0) {
+        s->out.len = sizeof(smtp_bad_sequence) - 1;
+        s->out.data = smtp_bad_sequence;
+        return NGX_OK;
+    }
+
+    l.len = s->buffer->last - s->buffer->start;
+    l.data = s->buffer->start;
+
+    for (i = 0; i < l.len; i++) {
+        ch = l.data[i];
+
+        if (ch != CR && ch != LF) {
+            continue;
+        }
+
+        l.data[i] = ' ';
+    }
+
+    while (i) {
+        if (l.data[i - 1] != ' ') {
+            break;
+        }
+
+        i--;
+    }
+
+    l.len = i;
+
+    s->smtp_to.len = l.len;
+
+    s->smtp_to.data = ngx_pnalloc(c->pool, l.len);
+    if (s->smtp_to.data == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_memcpy(s->smtp_to.data, l.data, l.len);
+
+    ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+                   "smtp rcpt to:\"%V\"", &s->smtp_to);
+
+    s->auth_method = NGX_MAIL_AUTH_NONE;
+
+    return NGX_DONE;
+}
+
+
+static ngx_int_t
+ngx_mail_smtp_rset(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+    s->smtp_from.len = 0;
+    s->smtp_from.data = NULL;
+    s->smtp_to.len = 0;
+    s->smtp_to.data = NULL;
+
+    s->out.len = sizeof(smtp_ok) - 1;
+    s->out.data = smtp_ok;
 
     return NGX_OK;
 }
@@ -644,6 +785,10 @@ ngx_mail_smtp_starttls(ngx_mail_session_
 
             s->smtp_helo.len = 0;
             s->smtp_helo.data = NULL;
+            s->smtp_from.len = 0;
+            s->smtp_from.data = NULL;
+            s->smtp_to.len = 0;
+            s->smtp_to.data = NULL;
 
             c->read->handler = ngx_mail_starttls_handler;
             return NGX_OK;
--- a/src/mail/ngx_mail_smtp_module.c
+++ b/src/mail/ngx_mail_smtp_module.c
@@ -20,6 +20,7 @@ static ngx_conf_bitmask_t  ngx_mail_smtp
     { ngx_string("plain"), NGX_MAIL_AUTH_PLAIN_ENABLED },
     { ngx_string("login"), NGX_MAIL_AUTH_LOGIN_ENABLED },
     { ngx_string("cram-md5"), NGX_MAIL_AUTH_CRAM_MD5_ENABLED },
+    { ngx_string("none"), NGX_MAIL_AUTH_NONE_ENABLED },
     { ngx_null_string, 0 }
 };
 
@@ -28,7 +29,8 @@ static ngx_str_t  ngx_mail_smtp_auth_met
     ngx_string("PLAIN"),
     ngx_string("LOGIN"),
     ngx_null_string,  /* APOP */
-    ngx_string("CRAM-MD5")
+    ngx_string("CRAM-MD5"),
+    ngx_null_string   /* NONE */
 };
 
 
@@ -136,10 +138,10 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t 
     ngx_mail_smtp_srv_conf_t *prev = parent;
     ngx_mail_smtp_srv_conf_t *conf = child;
 
-    u_char                    *p, *auth;
+    u_char                    *p, *auth, *last;
     size_t                     size;
     ngx_str_t                 *c;
-    ngx_uint_t                 i, m;
+    ngx_uint_t                 i, m, auth_enabled;
     ngx_mail_core_srv_conf_t  *cscf;
 
     ngx_conf_merge_size_value(conf->client_buffer_size,
@@ -192,23 +194,29 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t 
         conf->capabilities = prev->capabilities;
     }
 
-    size = sizeof("250-") - 1 + cscf->server_name.len + sizeof(CRLF) - 1
-           + sizeof("250 AUTH") - 1 + sizeof(CRLF) - 1;
+    size = sizeof("250-") - 1 + cscf->server_name.len + sizeof(CRLF) - 1;
 
     c = conf->capabilities.elts;
     for (i = 0; i < conf->capabilities.nelts; i++) {
         size += sizeof("250 ") - 1 + c[i].len + sizeof(CRLF) - 1;
     }
 
+    auth_enabled = 0;
+
     for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
          m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED;
          m <<= 1, i++)
     {
         if (m & conf->auth_methods) {
             size += 1 + ngx_mail_smtp_auth_methods_names[i].len;
+            auth_enabled = 1;
         }
     }
 
+    if (auth_enabled) {
+        size += sizeof("250 AUTH") - 1 + sizeof(CRLF) - 1;
+    }
+
     p = ngx_pnalloc(cf->pool, size);
     if (p == NULL) {
         return NGX_CONF_ERROR;
@@ -217,11 +225,14 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t 
     conf->capability.len = size;
     conf->capability.data = p;
 
+    last = p;
+
     *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = '-';
     p = ngx_cpymem(p, cscf->server_name.data, cscf->server_name.len);
     *p++ = CR; *p++ = LF;
 
     for (i = 0; i < conf->capabilities.nelts; i++) {
+        last = p;
         *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = '-';
         p = ngx_cpymem(p, c[i].data, c[i].len);
         *p++ = CR; *p++ = LF;
@@ -229,21 +240,28 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t 
 
     auth = p;
 
-    *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' ';
-    *p++ = 'A'; *p++ = 'U'; *p++ = 'T'; *p++ = 'H';
+    if (auth_enabled) {
+        last = p;
+
+        *p++ = '2'; *p++ = '5'; *p++ = '0'; *p++ = ' ';
+        *p++ = 'A'; *p++ = 'U'; *p++ = 'T'; *p++ = 'H';
 
-    for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
-         m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED;
-         m <<= 1, i++)
-    {
-        if (m & conf->auth_methods) {
-            *p++ = ' ';
-            p = ngx_cpymem(p, ngx_mail_smtp_auth_methods_names[i].data,
-                           ngx_mail_smtp_auth_methods_names[i].len);
+        for (m = NGX_MAIL_AUTH_PLAIN_ENABLED, i = 0;
+             m <= NGX_MAIL_AUTH_CRAM_MD5_ENABLED;
+             m <<= 1, i++)
+        {
+            if (m & conf->auth_methods) {
+                *p++ = ' ';
+                p = ngx_cpymem(p, ngx_mail_smtp_auth_methods_names[i].data,
+                               ngx_mail_smtp_auth_methods_names[i].len);
+            }
         }
-    }
+
+        *p++ = CR; *p = LF;
 
-    *p++ = CR; *p = LF;
+    } else {
+        last[3] = ' ';
+    }
 
     size += sizeof("250 STARTTLS" CRLF) - 1;
 
@@ -255,14 +273,13 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t 
     conf->starttls_capability.len = size;
     conf->starttls_capability.data = p;
 
-    p = ngx_cpymem(p, conf->capability.data,
-                   conf->capability.len);
+    p = ngx_cpymem(p, conf->capability.data, conf->capability.len);
 
     p = ngx_cpymem(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1);
     *p++ = CR; *p = LF;
 
     p = conf->starttls_capability.data
-        + (auth - conf->capability.data) + 3;
+        + (last - conf->capability.data) + 3;
     *p = '-';
 
     size = (auth - conf->capability.data)
@@ -276,10 +293,15 @@ ngx_mail_smtp_merge_srv_conf(ngx_conf_t 
     conf->starttls_only_capability.len = size;
     conf->starttls_only_capability.data = p;
 
-    p = ngx_cpymem(p, conf->capability.data,
-                   auth - conf->capability.data);
+    p = ngx_cpymem(p, conf->capability.data, auth - conf->capability.data);
 
     ngx_memcpy(p, "250 STARTTLS" CRLF, sizeof("250 STARTTLS" CRLF) - 1);
 
+    if (last < auth) {
+        p = conf->starttls_only_capability.data
+            + (last - conf->capability.data) + 3;
+        *p = '-';
+    }
+
     return NGX_CONF_OK;
 }