[PATCH] Mail: add IMAP ID command support (RFC2971)

Filipe da Silva fdasilvayy at gmail.com
Mon Feb 10 08:50:22 UTC 2014


# HG changeset patch
# User Filipe da Silva <fdasilvayy at gmail.com>
# Date 1392021996 -3600
#      Mon Feb 10 09:46:36 2014 +0100
# Node ID dec4454e6b1327d7737ae95db9f713cc9117ab81
# Parent  887e5abf8446603d9163a8cd011f14fab57e2a3a
Mail: add IMAP ID command support (RFC2971).

Parse the ID command and its arguments( NIL or params_list)
Handle the server response to ID command.

It accepts :
tag ID NIL
tag ID ( "Key" "Value" )
tag ID ( "Key" NIL )
tag ID ()

diff -r 887e5abf8446 -r dec4454e6b13 src/mail/ngx_mail.h
--- a/src/mail/ngx_mail.h	Mon Feb 10 09:46:36 2014 +0100
+++ b/src/mail/ngx_mail.h	Mon Feb 10 09:46:36 2014 +0100
@@ -215,6 +215,7 @@ typedef struct {
     unsigned                quoted:1;
     unsigned                backslash:1;
     unsigned                no_sync_literal:1;
+    unsigned                params_list:1;
     unsigned                starttls:1;
     unsigned                esmtp:1;
     unsigned                auth_method:3;
@@ -233,6 +234,7 @@ typedef struct {
     ngx_str_t               smtp_helo;
     ngx_str_t               smtp_from;
     ngx_str_t               smtp_to;
+    ngx_str_t               imap_id;
 
     ngx_str_t               cmd;
 
@@ -279,10 +281,10 @@ typedef struct {
 #define NGX_IMAP_CAPABILITY    3
 #define NGX_IMAP_NOOP          4
 #define NGX_IMAP_STARTTLS      5
+#define NGX_IMAP_AUTHENTICATE  6
+#define NGX_IMAP_ID            7
 
-#define NGX_IMAP_NEXT          6
-
-#define NGX_IMAP_AUTHENTICATE  7
+#define NGX_IMAP_NEXT          8
 
 
 #define NGX_SMTP_HELO          1
diff -r 887e5abf8446 -r dec4454e6b13 src/mail/ngx_mail_imap_handler.c
--- a/src/mail/ngx_mail_imap_handler.c	Mon Feb 10 09:46:36 2014 +0100
+++ b/src/mail/ngx_mail_imap_handler.c	Mon Feb 10 09:46:36 2014 +0100
@@ -18,6 +18,8 @@ static ngx_int_t ngx_mail_imap_authentic
     ngx_connection_t *c);
 static ngx_int_t ngx_mail_imap_capability(ngx_mail_session_t *s,
     ngx_connection_t *c);
+static ngx_int_t ngx_mail_imap_id(ngx_mail_session_t *s,
+    ngx_connection_t *c);
 static ngx_int_t ngx_mail_imap_starttls(ngx_mail_session_t *s,
     ngx_connection_t *c);
 
@@ -31,6 +33,7 @@ static u_char  imap_username[] = "+ VXNl
 static u_char  imap_password[] = "+ UGFzc3dvcmQ6" CRLF;
 static u_char  imap_bye[] = "* BYE" CRLF;
 static u_char  imap_invalid_command[] = "BAD invalid command" CRLF;
+static u_char  imap_server_id_nil[] = "* ID NIL" CRLF;
 
 
 void
@@ -183,6 +186,10 @@ ngx_mail_imap_auth_state(ngx_event_t *re
                 rc = ngx_mail_imap_capability(s, c);
                 break;
 
+            case NGX_IMAP_ID:
+                rc = ngx_mail_imap_id(s, c);
+                break;
+
             case NGX_IMAP_LOGOUT:
                 s->quit = 1;
                 ngx_str_set(&s->text, imap_bye);
@@ -438,6 +445,70 @@ ngx_mail_imap_capability(ngx_mail_sessio
 
 
 static ngx_int_t
+ngx_mail_imap_id(ngx_mail_session_t *s, ngx_connection_t *c)
+{
+    ngx_uint_t   i;
+    ngx_str_t   *arg, cmd;
+
+    arg = s->args.elts;
+    cmd.data = s->tag.data + s->tag.len;
+    cmd.len = s->arg_end - cmd.data;
+
+    /* client may send 'tag ID NIL' */
+    if (s->args.nelts == 1) {
+        if (cmd.len != 6
+            || ngx_strncasecmp(cmd.data, (u_char *) "ID NIL", 6) != 0)
+        {
+            ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+                           "Invalid argument supplied:\"%V\"", &cmd);
+            return NGX_MAIL_PARSE_INVALID_COMMAND;
+        }
+
+        goto valid;
+    }
+
+    /*
+     * Client may send 'tag ID ( "Key" "value" )'.
+     * Only even list count is allowed.
+     */
+    if (s->args.nelts % 2 != 0) {
+        return NGX_MAIL_PARSE_INVALID_COMMAND;
+    }
+
+    for (i = 0; i < s->args.nelts; i += 2) {
+
+        if ( arg[i].len == 0) {
+            ngx_log_debug1(NGX_LOG_DEBUG_MAIL, c->log, 0,
+                           "ID empty key #%ui name : \"\"", i );
+            return NGX_MAIL_PARSE_INVALID_COMMAND;
+        }
+        else if (arg[i].len > 30) {
+            ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
+                           "ID Key #%ui name \"%V\" is too long", i, &arg[i]);
+            return NGX_MAIL_PARSE_INVALID_COMMAND;
+        }
+    }
+
+valid:
+    s->imap_id.len = cmd.len;
+    s->imap_id.data = ngx_pnalloc(c->pool, cmd.len);
+    if (s->imap_id.data == NULL) {
+        return NGX_ERROR;
+    }
+
+    ngx_memcpy(s->imap_id.data, cmd.data, cmd.len);
+
+    ngx_log_debug2(NGX_LOG_DEBUG_MAIL, c->log, 0,
+                   "imap client ID:\"%V%V\"", &s->tag, &s->imap_id);
+
+    /* prepare server response to ID command */
+    ngx_str_set(&s->text, imap_server_id_nil);
+
+    return NGX_OK;
+}
+
+
+static ngx_int_t
 ngx_mail_imap_starttls(ngx_mail_session_t *s, ngx_connection_t *c)
 {
 #if (NGX_MAIL_SSL)
diff -r 887e5abf8446 -r dec4454e6b13 src/mail/ngx_mail_parse.c
--- a/src/mail/ngx_mail_parse.c	Mon Feb 10 09:46:36 2014 +0100
+++ b/src/mail/ngx_mail_parse.c	Mon Feb 10 09:46:36 2014 +0100
@@ -280,6 +280,17 @@ ngx_mail_imap_parse_command(ngx_mail_ses
 
                 switch (p - c) {
 
+                case 2:
+                    if ((c[0] == 'I' || c[0] == 'i')
+                        && (c[1] == 'D'|| c[1] == 'd'))
+                    {
+                        s->command = NGX_IMAP_ID;
+
+                    } else {
+                        goto invalid;
+                    }
+                    break;
+
                 case 4:
                     if ((c[0] == 'N' || c[0] == 'n')
                         && (c[1] == 'O'|| c[1] == 'o')
@@ -385,6 +396,9 @@ ngx_mail_imap_parse_command(ngx_mail_ses
                     goto invalid;
                 }
 
+                s->cmd.data = s->cmd_start;
+                s->cmd.len = p - s->cmd_start;
+
                 switch (ch) {
                 case ' ':
                     state = sw_spaces_before_argument;
@@ -409,14 +423,33 @@ ngx_mail_imap_parse_command(ngx_mail_ses
             case ' ':
                 break;
             case CR:
+                if (s->params_list) {
+                    goto invalid;
+                }
                 state = sw_almost_done;
                 s->arg_end = p;
                 break;
             case LF:
+                if (s->params_list) {
+                    goto invalid;
+                }
                 s->arg_end = p;
                 goto done;
+            case '(':
+                if (!s->params_list && s->args.nelts == 0) {
+                    s->params_list = 1;
+                    break;
+                }
+                goto invalid;
+            case ')':
+                if (s->params_list && s->args.nelts % 2 == 0) {
+                    s->params_list = 0;
+                    break;
+                }
+                goto invalid;
             case '"':
-                if (s->args.nelts <= 2) {
+                if (s->args.nelts <= 2
+                    || (s->params_list && s->args.nelts < 60)) {
                     s->quoted = 1;
                     s->arg_start = p + 1;
                     state = sw_argument;
@@ -430,7 +463,8 @@ ngx_mail_imap_parse_command(ngx_mail_ses
                 }
                 goto invalid;
             default:
-                if (s->args.nelts <= 2) {
+                if (s->args.nelts <= 2
+                    || (s->params_list && s->args.nelts < 60)) {
                     s->arg_start = p;
                     state = sw_argument;
                     break;
@@ -443,6 +477,9 @@ ngx_mail_imap_parse_command(ngx_mail_ses
             if (ch == ' ' && s->quoted) {
                 break;
             }
+            if (ch == ')' && s->quoted) {
+                break;
+            }
 
             switch (ch) {
             case '"':
@@ -451,6 +488,7 @@ ngx_mail_imap_parse_command(ngx_mail_ses
                 }
                 s->quoted = 0;
                 /* fall through */
+            case ')':
             case ' ':
             case CR:
             case LF:
@@ -462,15 +500,42 @@ ngx_mail_imap_parse_command(ngx_mail_ses
                 arg->data = s->arg_start;
                 s->arg_start = NULL;
 
+                /* only accepts 'nil' as keyword in arguments */
+                if ( s->command == NGX_IMAP_ID && ch != '"') {
+                    c = arg->data;
+                    if (arg->len != 3
+                        || (c[0] != 'N' && c[0] != 'n')
+                        || (c[1] != 'I' && c[1] != 'i')
+                        || (c[2] != 'L' && c[2] != 'l')) {
+                        goto invalid;
+                    }
+                    /* only accepts 'nil' as value in id_params_list */
+                    if (s->params_list && s->args.nelts % 2 != 0) {
+                        goto invalid;
+                    }
+                }
+
                 switch (ch) {
+                case ')':
+                    if (!s->params_list) {
+                        goto invalid;
+                    }
+                    s->params_list = 0;
+                    /* fall through */
                 case '"':
                 case ' ':
                     state = sw_spaces_before_argument;
                     break;
                 case CR:
+                    if (s->params_list)
+                        goto invalid;
                     state = sw_almost_done;
+                    s->arg_end = p;
                     break;
                 case LF:
+                    if (s->params_list)
+                        goto invalid;
+                    s->arg_end = p;
                     goto done;
                 }
                 break;
@@ -614,6 +679,7 @@ invalid:
     s->quoted = 0;
     s->no_sync_literal = 0;
     s->literal_len = 0;
+    s->params_list = 0;
 
     return NGX_MAIL_PARSE_INVALID_COMMAND;
 }
-------------- next part --------------
A non-text attachment was scrubbed...
Name: 000-ImapID_CommandSupport.diff
Type: text/x-patch
Size: 9778 bytes
Desc: not available
URL: <http://mailman.nginx.org/pipermail/nginx-devel/attachments/20140210/2fe4d3fb/attachment.bin>


More information about the nginx-devel mailing list