[PATCH] Added support for proxying managesieve protocol (fixed format)

Sander Hoentjen sander at hoentjen.eu
Wed Apr 8 18:38:20 UTC 2020


# HG changeset patch
# User Sander Hoentjen<shoentjen at antagonist.nl>
# Date 1586369831 -7200
#      Wed Apr 08 20:17:11 2020 +0200
# Node ID f1dffaf619688aaab90caf31781ebe27c3f79598
# Parent  0cb942c1c1aa98118076e72e0b89940e85e6291c
Added support for proxying managesieve protocol

Sieve is a widely used protocol to implement server-side filtering. Some
servers and clients that support this are listed at<http://sieve.info/>. This
commit is tested with Dovecot as the server and Roundcube as the client, but I
don't see a readon why it not would work with others. It is a nice addition to
nginx because it adds the possibility to add server side filtering to setups
that use IMAP behind an nginx proxy.

Fixeshttps://trac.nginx.org/nginx/ticket/1697

diff -r 0cb942c1c1aa -r f1dffaf61968 auto/modules
--- a/auto/modules	Fri Mar 13 02:12:10 2020 +0300
+++ b/auto/modules	Wed Apr 08 20:17:11 2020 +0200
@@ -965,6 +965,15 @@
          . auto/module
      fi
  
+    if [ $MAIL_SIEVE = YES ]; then
+        ngx_module_name=ngx_mail_sieve_module
+        ngx_module_deps=src/mail/ngx_mail_sieve_module.h
+        ngx_module_srcs="src/mail/ngx_mail_sieve_module.c \
+                         src/mail/ngx_mail_sieve_handler.c"
+
+        . auto/module
+    fi
+
      if [ $MAIL_SMTP = YES ]; then
          ngx_module_name=ngx_mail_smtp_module
          ngx_module_deps=src/mail/ngx_mail_smtp_module.h
diff -r 0cb942c1c1aa -r f1dffaf61968 auto/options
--- a/auto/options	Fri Mar 13 02:12:10 2020 +0300
+++ b/auto/options	Wed Apr 08 20:17:11 2020 +0200
@@ -112,6 +112,7 @@
  MAIL_SSL=NO
  MAIL_POP3=YES
  MAIL_IMAP=YES
+MAIL_SIEVE=YES
  MAIL_SMTP=YES
  
  STREAM=NO
@@ -305,6 +306,8 @@
          ;;
          --without-mail_pop3_module)      MAIL_POP3=NO               ;;
          --without-mail_imap_module)      MAIL_IMAP=NO               ;;
+        --without-mail_sieve_module)
+                                         MAIL_SIEVE=NO        ;;
          --without-mail_smtp_module)      MAIL_SMTP=NO               ;;
  
          --with-stream)                   STREAM=YES                 ;;
@@ -517,11 +520,12 @@
    --without-http                     disable HTTP server
    --without-http-cache               disable HTTP cache
  
-  --with-mail                        enable POP3/IMAP4/SMTP proxy module
-  --with-mail=dynamic                enable dynamic POP3/IMAP4/SMTP proxy module
+  --with-mail                        enable POP3/IMAP4/SIEVE/SMTP proxy module
+  --with-mail=dynamic                enable dynamic POP3/IMAP4/SIEVE/SMTP proxy module
    --with-mail_ssl_module             enable ngx_mail_ssl_module
    --without-mail_pop3_module         disable ngx_mail_pop3_module
    --without-mail_imap_module         disable ngx_mail_imap_module
+  --without-mail_sieve_module        disable ngx_mail_sieve_module
    --without-mail_smtp_module         disable ngx_mail_smtp_module
  
    --with-stream                      enable TCP/UDP proxy module
diff -r 0cb942c1c1aa -r f1dffaf61968 contrib/vim/syntax/nginx.vim
--- a/contrib/vim/syntax/nginx.vim	Fri Mar 13 02:12:10 2020 +0300
+++ b/contrib/vim/syntax/nginx.vim	Wed Apr 08 20:17:11 2020 +0200
@@ -571,6 +571,9 @@
  syn keyword ngxDirective contained session_log_format
  syn keyword ngxDirective contained session_log_zone
  syn keyword ngxDirective contained set_real_ip_from
+syn keyword ngxDirective contained sieve_auth
+syn keyword ngxDirective contained sieve_capabilities
+syn keyword ngxDirective contained sieve_client_buffer
  syn keyword ngxDirective contained slice
  syn keyword ngxDirective contained smtp_auth
  syn keyword ngxDirective contained smtp_capabilities
diff -r 0cb942c1c1aa -r f1dffaf61968 src/mail/ngx_mail.h
--- a/src/mail/ngx_mail.h	Fri Mar 13 02:12:10 2020 +0300
+++ b/src/mail/ngx_mail.h	Wed Apr 08 20:17:11 2020 +0200
@@ -102,6 +102,7 @@
  #define NGX_MAIL_POP3_PROTOCOL  0
  #define NGX_MAIL_IMAP_PROTOCOL  1
  #define NGX_MAIL_SMTP_PROTOCOL  2
+#define NGX_MAIL_SIEVE_PROTOCOL  3
  
  
  typedef struct ngx_mail_protocol_s  ngx_mail_protocol_t;
@@ -154,6 +155,21 @@
  
  
  typedef enum {
+    ngx_sieve_start = 0,
+    ngx_sieve_starttls,
+    ngx_sieve_auth_login_username,
+    ngx_sieve_auth_login_password,
+    ngx_sieve_auth_plain,
+    ngx_sieve_auth_cram_md5,
+    ngx_sieve_auth_external,
+    ngx_sieve_login,
+    ngx_sieve_login_capabilities,
+    ngx_sieve_user,
+    ngx_sieve_passwd
+} ngx_sieve_state_e;
+
+
+typedef enum {
      ngx_smtp_start = 0,
      ngx_smtp_auth_login_username,
      ngx_smtp_auth_login_password,
@@ -227,7 +243,7 @@
  
      ngx_uint_t              login_attempt;
  
-    /* used to parse POP3/IMAP/SMTP command */
+    /* used to parse POP3/IMAP/SIEVE/SMTP command */
  
      ngx_uint_t              state;
      u_char                 *cmd_start;
@@ -271,6 +287,14 @@
  #define NGX_IMAP_AUTHENTICATE  7
  
  
+#define NGX_SIEVE_LOGOUT        1
+#define NGX_SIEVE_CAPABILITY    2
+#define NGX_SIEVE_NOOP          3
+#define NGX_SIEVE_STARTTLS      4
+#define NGX_SIEVE_NEXT          5
+#define NGX_SIEVE_AUTHENTICATE  6
+
+
  #define NGX_SMTP_HELO          1
  #define NGX_SMTP_EHLO          2
  #define NGX_SMTP_AUTH          3
@@ -383,7 +407,7 @@
  ngx_int_t ngx_mail_auth_login_password(ngx_mail_session_t *s,
      ngx_connection_t *c);
  ngx_int_t ngx_mail_auth_cram_md5_salt(ngx_mail_session_t *s,
-    ngx_connection_t *c, char *prefix, size_t len);
+    ngx_connection_t *c, char *prefix, size_t len, ngx_uint_t quote);
  ngx_int_t ngx_mail_auth_cram_md5(ngx_mail_session_t *s, ngx_connection_t *c);
  ngx_int_t ngx_mail_auth_external(ngx_mail_session_t *s, ngx_connection_t *c,
      ngx_uint_t n);
diff -r 0cb942c1c1aa -r f1dffaf61968 src/mail/ngx_mail_auth_http_module.c
--- a/src/mail/ngx_mail_auth_http_module.c	Fri Mar 13 02:12:10 2020 +0300
+++ b/src/mail/ngx_mail_auth_http_module.c	Wed Apr 08 20:17:11 2020 +0200
@@ -533,6 +533,11 @@
                             + sizeof(CRLF) - 1;
                      break;
  
+                case NGX_MAIL_SIEVE_PROTOCOL:
+                    size = sizeof("BYE \"\"") - 1 + len
+                           + sizeof(CRLF) - 1;
+                    break;
+
                  default: /* NGX_MAIL_SMTP_PROTOCOL */
                      ctx->err = ctx->errmsg;
                      continue;
@@ -559,11 +564,27 @@
                      *p++ = 'N'; *p++ = 'O'; *p++ = ' ';
                      break;
  
+                case NGX_MAIL_SIEVE_PROTOCOL:
+                    p = ngx_cpymem(p, s->tag.data, s->tag.len);
+                    *p++ = 'B'; *p++ = 'Y'; *p++ = 'E'; *p++ = ' '; *p++ = '"';
+                    break;
+
                  default: /* NGX_MAIL_SMTP_PROTOCOL */
                      break;
                  }
  
                  p = ngx_cpymem(p, ctx->header_start, len);
+
+                switch (s->protocol) {
+
+                case NGX_MAIL_SIEVE_PROTOCOL:
+                    *p++ = '"';
+                    break;
+
+                default:
+                    break;
+                }
+
                  *p++ = CR; *p++ = LF;
  
                  ctx->err.len = p - ctx->err.data;
diff -r 0cb942c1c1aa -r f1dffaf61968 src/mail/ngx_mail_handler.c
--- a/src/mail/ngx_mail_handler.c	Fri Mar 13 02:12:10 2020 +0300
+++ b/src/mail/ngx_mail_handler.c	Wed Apr 08 20:17:11 2020 +0200
@@ -520,28 +520,39 @@
  
  ngx_int_t
  ngx_mail_auth_cram_md5_salt(ngx_mail_session_t *s, ngx_connection_t *c,
-    char *prefix, size_t len)
+    char *prefix, size_t len, ngx_uint_t quote)
  {
      u_char      *p;
      ngx_str_t    salt;
-    ngx_uint_t   n;
+    ngx_uint_t   n = 0;
  
+    if (quote) {
+        len += 2;
+    }
      p = ngx_pnalloc(c->pool, len + ngx_base64_encoded_length(s->salt.len) + 2);
      if (p == NULL) {
          return NGX_ERROR;
      }
  
-    salt.data = ngx_cpymem(p, prefix, len);
+    if (quote) {
+        len -= 2;
+        salt.data = ngx_cpymem(p, "\"", 1);
+	n++;
+    }
+    salt.data = ngx_cpymem(p+ n, prefix, len);
      s->salt.len -= 2;
  
      ngx_encode_base64(&salt, &s->salt);
  
      s->salt.len += 2;
-    n = len + salt.len;
+    n += len + salt.len;
+    if (quote) {
+        p[n++] = '"';
+    }
      p[n++] = CR; p[n++] = LF;
  
      s->out.len = n;
-    s->out.data = p;
+    s->out.data = p--;
  
      return NGX_OK;
  }
diff -r 0cb942c1c1aa -r f1dffaf61968 src/mail/ngx_mail_imap_handler.c
--- a/src/mail/ngx_mail_imap_handler.c	Fri Mar 13 02:12:10 2020 +0300
+++ b/src/mail/ngx_mail_imap_handler.c	Wed Apr 08 20:17:11 2020 +0200
@@ -397,7 +397,7 @@
              }
          }
  
-        if (ngx_mail_auth_cram_md5_salt(s, c, "+ ", 2) == NGX_OK) {
+        if (ngx_mail_auth_cram_md5_salt(s, c, "+ ", 2, 0) == NGX_OK) {
              s->mail_state = ngx_imap_auth_cram_md5;
              return NGX_OK;
          }
diff -r 0cb942c1c1aa -r f1dffaf61968 src/mail/ngx_mail_parse.c
--- a/src/mail/ngx_mail_parse.c	Fri Mar 13 02:12:10 2020 +0300
+++ b/src/mail/ngx_mail_parse.c	Wed Apr 08 20:17:11 2020 +0200
@@ -11,6 +11,7 @@
  #include <ngx_mail.h>
  #include <ngx_mail_pop3_module.h>
  #include <ngx_mail_imap_module.h>
+#include <ngx_mail_sieve_module.h>
  #include <ngx_mail_smtp_module.h>
  
  
@@ -620,6 +621,364 @@
  
  
  ngx_int_t
+ngx_mail_sieve_parse_command(ngx_mail_session_t *s)
+{
+    u_char      ch, *p, *c;
+    ngx_str_t  *arg;
+    enum {
+        sw_start = 0,
+        sw_spaces_before_argument,
+        sw_argument,
+        sw_backslash,
+        sw_literal,
+        sw_no_sync_literal_argument,
+        sw_start_literal_argument,
+        sw_literal_argument,
+        sw_end_literal_argument,
+        sw_almost_done
+    } state;
+
+    state = s->state;
+
+    for (p = s->buffer->pos; p < s->buffer->last; p++) {
+        ch = *p;
+
+        switch (state) {
+
+        /* SIEVE command */
+        case sw_start:
+            if (ch == ' ' || ch == CR || ch == LF) {
+
+                c = s->buffer->start;
+
+                switch (p - c) {
+
+                case 4:
+                    if ((c[0] == 'N' || c[0] == 'n')
+                        && (c[1] == 'O'|| c[1] == 'o')
+                        && (c[2] == 'O'|| c[2] == 'o')
+                        && (c[3] == 'P'|| c[3] == 'p'))
+                    {
+                        s->command = NGX_SIEVE_NOOP;
+
+                    } else {
+                        goto invalid;
+                    }
+                    break;
+
+                case 6:
+                    if ((c[0] == 'L'|| c[0] == 'l')
+                        && (c[1] == 'O'|| c[1] == 'o')
+                        && (c[2] == 'G'|| c[2] == 'g')
+                        && (c[3] == 'O'|| c[3] == 'o')
+                        && (c[4] == 'U'|| c[4] == 'u')
+                        && (c[5] == 'T'|| c[5] == 't'))
+                    {
+                        s->command = NGX_SIEVE_LOGOUT;
+
+                    } else {
+                        goto invalid;
+                    }
+                    break;
+
+#if (NGX_MAIL_SSL)
+                case 8:
+                    if ((c[0] == 'S'|| c[0] == 's')
+                        && (c[1] == 'T'|| c[1] == 't')
+                        && (c[2] == 'A'|| c[2] == 'a')
+                        && (c[3] == 'R'|| c[3] == 'r')
+                        && (c[4] == 'T'|| c[4] == 't')
+                        && (c[5] == 'T'|| c[5] == 't')
+                        && (c[6] == 'L'|| c[6] == 'l')
+                        && (c[7] == 'S'|| c[7] == 's'))
+                    {
+                        s->command = NGX_SIEVE_STARTTLS;
+
+                    } else {
+                        goto invalid;
+                    }
+                    break;
+#endif
+
+                case 10:
+                    if ((c[0] == 'C'|| c[0] == 'c')
+                        && (c[1] == 'A'|| c[1] == 'a')
+                        && (c[2] == 'P'|| c[2] == 'p')
+                        && (c[3] == 'A'|| c[3] == 'a')
+                        && (c[4] == 'B'|| c[4] == 'b')
+                        && (c[5] == 'I'|| c[5] == 'i')
+                        && (c[6] == 'L'|| c[6] == 'l')
+                        && (c[7] == 'I'|| c[7] == 'i')
+                        && (c[8] == 'T'|| c[8] == 't')
+                        && (c[9] == 'Y'|| c[9] == 'y'))
+                    {
+                        s->command = NGX_SIEVE_CAPABILITY;
+
+                    } else {
+                        goto invalid;
+                    }
+                    break;
+
+                case 12:
+                    if ((c[0] == 'A'|| c[0] == 'a')
+                        && (c[1] == 'U'|| c[1] == 'u')
+                        && (c[2] == 'T'|| c[2] == 't')
+                        && (c[3] == 'H'|| c[3] == 'h')
+                        && (c[4] == 'E'|| c[4] == 'e')
+                        && (c[5] == 'N'|| c[5] == 'n')
+                        && (c[6] == 'T'|| c[6] == 't')
+                        && (c[7] == 'I'|| c[7] == 'i')
+                        && (c[8] == 'C'|| c[8] == 'c')
+                        && (c[9] == 'A'|| c[9] == 'a')
+                        && (c[10] == 'T'|| c[10] == 't')
+                        && (c[11] == 'E'|| c[11] == 'e'))
+                    {
+                        s->command = NGX_SIEVE_AUTHENTICATE;
+
+                    } else {
+                        goto invalid;
+                    }
+                    break;
+
+                default:
+                    goto invalid;
+                }
+
+                switch (ch) {
+                case ' ':
+                    state = sw_spaces_before_argument;
+                    break;
+                case CR:
+                    state = sw_almost_done;
+                    break;
+                case LF:
+                    goto done;
+                }
+                break;
+            }
+
+            if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {
+                goto invalid;
+            }
+
+            break;
+
+        case sw_spaces_before_argument:
+            switch (ch) {
+            case ' ':
+                break;
+            case CR:
+                state = sw_almost_done;
+                s->arg_end = p;
+                break;
+            case LF:
+                s->arg_end = p;
+                goto done;
+            case '"':
+                if (s->args.nelts <= 2) {
+                    s->quoted = 1;
+                    s->arg_start = p + 1;
+                    state = sw_argument;
+                    break;
+                }
+                goto invalid;
+            case '{':
+                if (s->args.nelts <= 2) {
+                    state = sw_literal;
+                    break;
+                }
+                goto invalid;
+            default:
+                if (s->args.nelts <= 2) {
+                    s->arg_start = p;
+                    state = sw_argument;
+                    break;
+                }
+                goto invalid;
+            }
+            break;
+
+        case sw_argument:
+            if (ch == ' ' && s->quoted) {
+                break;
+            }
+
+            switch (ch) {
+            case '"':
+                if (!s->quoted) {
+                    break;
+                }
+                s->quoted = 0;
+                /* fall through */
+            case ' ':
+            case CR:
+            case LF:
+                arg = ngx_array_push(&s->args);
+                if (arg == NULL) {
+                    return NGX_ERROR;
+                }
+                arg->len = p - s->arg_start;
+                arg->data = s->arg_start;
+                s->arg_start = NULL;
+
+                switch (ch) {
+                case '"':
+                case ' ':
+                    state = sw_spaces_before_argument;
+                    break;
+                case CR:
+                    state = sw_almost_done;
+                    break;
+                case LF:
+                    goto done;
+                }
+                break;
+            case '\\':
+                if (s->quoted) {
+                    s->backslash = 1;
+                    state = sw_backslash;
+                }
+                break;
+            }
+            break;
+
+        case sw_backslash:
+            switch (ch) {
+            case CR:
+            case LF:
+                goto invalid;
+            default:
+                state = sw_argument;
+            }
+            break;
+
+        case sw_literal:
+            if (ch >= '0' && ch <= '9') {
+                s->literal_len = s->literal_len * 10 + (ch - '0');
+                break;
+            }
+            if (ch == '}') {
+                state = sw_start_literal_argument;
+                break;
+            }
+            if (ch == '+') {
+                state = sw_no_sync_literal_argument;
+                break;
+            }
+            goto invalid;
+
+        case sw_no_sync_literal_argument:
+            if (ch == '}') {
+                s->no_sync_literal = 1;
+                state = sw_start_literal_argument;
+                break;
+            }
+            goto invalid;
+
+        case sw_start_literal_argument:
+            switch (ch) {
+            case CR:
+                break;
+            case LF:
+                s->buffer->pos = p + 1;
+                s->arg_start = p + 1;
+                if (s->no_sync_literal == 0) {
+                    s->state = sw_literal_argument;
+                    return NGX_SIEVE_NEXT;
+                }
+                state = sw_literal_argument;
+                s->no_sync_literal = 0;
+                break;
+            default:
+                goto invalid;
+            }
+            break;
+
+        case sw_literal_argument:
+            if (s->literal_len && --s->literal_len) {
+                break;
+            }
+
+            arg = ngx_array_push(&s->args);
+            if (arg == NULL) {
+                return NGX_ERROR;
+            }
+            arg->len = p + 1 - s->arg_start;
+            arg->data = s->arg_start;
+            s->arg_start = NULL;
+            state = sw_end_literal_argument;
+
+            break;
+
+        case sw_end_literal_argument:
+            switch (ch) {
+            case '{':
+                if (s->args.nelts <= 2) {
+                    state = sw_literal;
+                    break;
+                }
+                goto invalid;
+            case CR:
+                state = sw_almost_done;
+                break;
+            case LF:
+                goto done;
+            default:
+                state = sw_spaces_before_argument;
+                break;
+            }
+            break;
+
+        case sw_almost_done:
+            switch (ch) {
+            case LF:
+                goto done;
+            default:
+                goto invalid;
+            }
+        }
+    }
+
+    s->buffer->pos = p;
+    s->state = state;
+
+    return NGX_AGAIN;
+
+done:
+
+    s->buffer->pos = p + 1;
+
+    if (s->arg_start) {
+        arg = ngx_array_push(&s->args);
+        if (arg == NULL) {
+            return NGX_ERROR;
+        }
+        arg->len = s->arg_end - s->arg_start;
+        arg->data = s->arg_start;
+
+        s->arg_start = NULL;
+        s->cmd_start = NULL;
+        s->quoted = 0;
+        s->no_sync_literal = 0;
+        s->literal_len = 0;
+    }
+
+    s->state = (s->command != NGX_SIEVE_AUTHENTICATE) ? sw_start : sw_spaces_before_argument;
+
+    return NGX_OK;
+
+invalid:
+
+    s->state = sw_start;
+    s->quoted = 0;
+    s->no_sync_literal = 0;
+    s->literal_len = 0;
+
+    return NGX_MAIL_PARSE_INVALID_COMMAND;
+}
+
+
+ngx_int_t
  ngx_mail_smtp_parse_command(ngx_mail_session_t *s)
  {
      u_char      ch, *p, *c, c0, c1, c2, c3;
diff -r 0cb942c1c1aa -r f1dffaf61968 src/mail/ngx_mail_pop3_handler.c
--- a/src/mail/ngx_mail_pop3_handler.c	Fri Mar 13 02:12:10 2020 +0300
+++ b/src/mail/ngx_mail_pop3_handler.c	Wed Apr 08 20:17:11 2020 +0200
@@ -492,7 +492,7 @@
              return NGX_MAIL_PARSE_INVALID_COMMAND;
          }
  
-        if (ngx_mail_auth_cram_md5_salt(s, c, "+ ", 2) == NGX_OK) {
+        if (ngx_mail_auth_cram_md5_salt(s, c, "+ ", 2, 0) == NGX_OK) {
              s->mail_state = ngx_pop3_auth_cram_md5;
              return NGX_OK;
          }
diff -r 0cb942c1c1aa -r f1dffaf61968 src/mail/ngx_mail_proxy_module.c
--- a/src/mail/ngx_mail_proxy_module.c	Fri Mar 13 02:12:10 2020 +0300
+++ b/src/mail/ngx_mail_proxy_module.c	Wed Apr 08 20:17:11 2020 +0200
@@ -24,6 +24,7 @@
  static void ngx_mail_proxy_block_read(ngx_event_t *rev);
  static void ngx_mail_proxy_pop3_handler(ngx_event_t *rev);
  static void ngx_mail_proxy_imap_handler(ngx_event_t *rev);
+static void ngx_mail_proxy_sieve_handler(ngx_event_t *rev);
  static void ngx_mail_proxy_smtp_handler(ngx_event_t *rev);
  static void ngx_mail_proxy_dummy_handler(ngx_event_t *ev);
  static ngx_int_t ngx_mail_proxy_read_response(ngx_mail_session_t *s,
@@ -173,6 +174,11 @@
          s->mail_state = ngx_imap_start;
          break;
  
+    case NGX_MAIL_SIEVE_PROTOCOL:
+        p->upstream.connection->read->handler = ngx_mail_proxy_sieve_handler;
+        s->mail_state = ngx_sieve_start;
+        break;
+
      default: /* NGX_MAIL_SMTP_PROTOCOL */
          p->upstream.connection->read->handler = ngx_mail_proxy_smtp_handler;
          s->mail_state = ngx_smtp_start;
@@ -446,6 +452,144 @@
  
  
  static void
+ngx_mail_proxy_sieve_handler(ngx_event_t *rev)
+{
+    u_char                 *p;
+    ngx_int_t               rc;
+    ngx_str_t               line;
+    ngx_str_t               userpass;
+    ngx_str_t               b64_userpass;
+    ngx_connection_t       *c;
+    ngx_mail_session_t     *s;
+    ngx_mail_proxy_conf_t  *pcf;
+
+    ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
+                   "mail proxy sieve auth handler");
+
+    c = rev->data;
+    s = c->data;
+
+    if (rev->timedout) {
+        ngx_log_error(NGX_LOG_INFO, c->log, NGX_ETIMEDOUT,
+                      "upstream timed out");
+        c->timedout = 1;
+        ngx_mail_proxy_internal_server_error(s);
+        return;
+    }
+
+    rc = ngx_mail_proxy_read_response(s, s->mail_state);
+
+    if (rc == NGX_AGAIN) {
+        return;
+    }
+
+    if (rc == NGX_ERROR) {
+        ngx_mail_proxy_upstream_error(s);
+        return;
+    }
+
+    switch (s->mail_state) {
+
+    case ngx_sieve_start:
+        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
+                       "mail proxy send login");
+
+        s->connection->log->action = "sending LOGIN command to upstream";
+
+        line.len = s->tag.len + sizeof("AUTHENTICATE ") - 1
+                   + 1 + NGX_SIZE_T_LEN + 1 + 2;
+        line.data = ngx_pnalloc(c->pool, line.len);
+        if (line.data == NULL) {
+            ngx_mail_proxy_internal_server_error(s);
+            return;
+        }
+
+	userpass.len = s->login.len + s->passwd.len + 2;
+	userpass.data = ngx_pnalloc(c->pool, userpass.len);
+        if (userpass.data == NULL) {
+            ngx_mail_proxy_internal_server_error(s);
+            return;
+        }
+	b64_userpass.len = ngx_base64_encoded_length(userpass.len) + 2;
+	b64_userpass.data = ngx_pnalloc(c->pool, b64_userpass.len);
+        if (b64_userpass.data == NULL) {
+            ngx_mail_proxy_internal_server_error(s);
+            return;
+        }
+
+	p = userpass.data;
+	*p++ = '\0';
+        p = ngx_cpymem(p, s->login.data, s->login.len);
+        *p++ = '\0';
+        p = ngx_cpymem(p, s->passwd.data, s->passwd.len);
+
+	ngx_encode_base64(&b64_userpass, &userpass);
+	line.len = ngx_sprintf(line.data, "AUTHENTICATE \"PLAIN\" \"%V\"" CRLF,
+                               &b64_userpass)
+                   - line.data;
+
+        s->mail_state = ngx_sieve_login;
+        break;
+
+    case ngx_sieve_login:
+        ngx_log_debug0(NGX_LOG_DEBUG_MAIL, rev->log, 0,
+                       "mail proxy send capabilities");
+
+        s->connection->log->action = "sending CAPABILITY command to upstream";
+
+        line.len = s->tag.len + sizeof("AUTHENTICATE ") - 1
+                   + 1 + NGX_SIZE_T_LEN + 1 + 2;
+        line.data = ngx_pnalloc(c->pool, line.len);
+        if (line.data == NULL) {
+            ngx_mail_proxy_internal_server_error(s);
+            return;
+        }
+
+	line.len = ngx_sprintf(line.data, "CAPABILITY" CRLF)
+                   - line.data;
+
+        s->mail_state = ngx_sieve_login_capabilities;
+        break;
+
+    case ngx_sieve_login_capabilities:
+	s->connection->read->handler = ngx_mail_proxy_handler;
+        s->connection->write->handler = ngx_mail_proxy_handler;
+        rev->handler = ngx_mail_proxy_handler;
+        c->write->handler = ngx_mail_proxy_handler;
+
+        pcf = ngx_mail_get_module_srv_conf(s, ngx_mail_proxy_module);
+        ngx_add_timer(s->connection->read, pcf->timeout);
+        ngx_del_timer(c->read);
+
+        c->log->action = NULL;
+        ngx_log_error(NGX_LOG_INFO, c->log, 0, "client logged in");
+
+        ngx_mail_proxy_handler(s->connection->write);
+
+        return;
+
+    default:
+#if (NGX_SUPPRESS_WARN)
+        ngx_str_null(&line);
+#endif
+        break;
+    }
+
+    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
+         */
+        ngx_mail_proxy_internal_server_error(s);
+        return;
+    }
+
+    s->proxy->buffer->pos = s->proxy->buffer->start;
+    s->proxy->buffer->last = s->proxy->buffer->start;
+}
+
+
+static void
  ngx_mail_proxy_smtp_handler(ngx_event_t *rev)
  {
      u_char                    *p;
@@ -793,6 +937,42 @@
  
          break;
  
+    case NGX_MAIL_SIEVE_PROTOCOL:
+        if (p[0] == '"') {
+            m = b->last - (sizeof(CRLF "OK" CRLF) - 1);
+
+            while (m > p) {
+                if (m[0] == CR && m[1] == LF) {
+                    break;
+                }
+                m--;
+            }
+
+            if (m <= p || m[0] == '"') {
+                return NGX_AGAIN;
+            }
+            p = m + 2;
+        }
+
+        switch (state) {
+
+        case ngx_sieve_start:
+        case ngx_sieve_login:
+	     ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "1 upstream sent: \"%s\"", p);
+            if (p[0] == 'O' && p[1] == 'K') {
+                return NGX_OK;
+            }
+            break;
+        case ngx_sieve_login_capabilities:
+	     ngx_log_error(NGX_LOG_INFO, s->connection->log, 0, "2 upstream sent: \"%s\"", p);
+            if (p[0] == 'O' && p[1] == 'K') {
+                return NGX_OK;
+            }
+            break;
+        }
+
+        break;
+
      default: /* NGX_MAIL_SMTP_PROTOCOL */
  
          if (p[3] == '-') {
diff -r 0cb942c1c1aa -r f1dffaf61968 src/mail/ngx_mail_smtp_handler.c
--- a/src/mail/ngx_mail_smtp_handler.c	Fri Mar 13 02:12:10 2020 +0300
+++ b/src/mail/ngx_mail_smtp_handler.c	Wed Apr 08 20:17:11 2020 +0200
@@ -693,7 +693,7 @@
              }
          }
  
-        if (ngx_mail_auth_cram_md5_salt(s, c, "334 ", 4) == NGX_OK) {
+        if (ngx_mail_auth_cram_md5_salt(s, c, "334 ", 4, 0) == NGX_OK) {
              s->mail_state = ngx_smtp_auth_cram_md5;
              return NGX_OK;
          }
diff -r 0cb942c1c1aa -r f1dffaf61968 src/misc/ngx_cpp_test_module.cpp
--- a/src/misc/ngx_cpp_test_module.cpp	Fri Mar 13 02:12:10 2020 +0300
+++ b/src/misc/ngx_cpp_test_module.cpp	Wed Apr 08 20:17:11 2020 +0200
@@ -13,6 +13,7 @@
    #include <ngx_mail.h>
    #include <ngx_mail_pop3_module.h>
    #include <ngx_mail_imap_module.h>
+  #include <ngx_mail_sieve_module.h>
    #include <ngx_mail_smtp_module.h>
  }
  

-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mailman.nginx.org/pipermail/nginx-devel/attachments/20200408/ac717f01/attachment-0001.htm>


More information about the nginx-devel mailing list