src/mail/ngx_mail_parse.c - nginx

Data types defined

Functions defined

Source code


  1. /*
  2. * Copyright (C) Igor Sysoev
  3. * Copyright (C) Nginx, Inc.
  4. */


  5. #include <ngx_config.h>
  6. #include <ngx_core.h>
  7. #include <ngx_event.h>
  8. #include <ngx_mail.h>
  9. #include <ngx_mail_pop3_module.h>
  10. #include <ngx_mail_imap_module.h>
  11. #include <ngx_mail_smtp_module.h>


  12. ngx_int_t
  13. ngx_mail_pop3_parse_command(ngx_mail_session_t *s)
  14. {
  15.     u_char      ch, *p, *c, c0, c1, c2, c3;
  16.     ngx_str_t  *arg;
  17.     enum {
  18.         sw_start = 0,
  19.         sw_command,
  20.         sw_invalid,
  21.         sw_spaces_before_argument,
  22.         sw_argument,
  23.         sw_almost_done
  24.     } state;

  25.     state = s->state;

  26.     for (p = s->buffer->pos; p < s->buffer->last; p++) {
  27.         ch = *p;

  28.         switch (state) {

  29.         /* POP3 command */
  30.         case sw_start:
  31.             s->cmd_start = p;
  32.             state = sw_command;

  33.             /* fall through */

  34.         case sw_command:
  35.             if (ch == ' ' || ch == CR || ch == LF) {
  36.                 c = s->cmd_start;

  37.                 if (p - c == 4) {

  38.                     c0 = ngx_toupper(c[0]);
  39.                     c1 = ngx_toupper(c[1]);
  40.                     c2 = ngx_toupper(c[2]);
  41.                     c3 = ngx_toupper(c[3]);

  42.                     if (c0 == 'U' && c1 == 'S' && c2 == 'E' && c3 == 'R')
  43.                     {
  44.                         s->command = NGX_POP3_USER;

  45.                     } else if (c0 == 'P' && c1 == 'A' && c2 == 'S' && c3 == 'S')
  46.                     {
  47.                         s->command = NGX_POP3_PASS;

  48.                     } else if (c0 == 'A' && c1 == 'P' && c2 == 'O' && c3 == 'P')
  49.                     {
  50.                         s->command = NGX_POP3_APOP;

  51.                     } else if (c0 == 'Q' && c1 == 'U' && c2 == 'I' && c3 == 'T')
  52.                     {
  53.                         s->command = NGX_POP3_QUIT;

  54.                     } else if (c0 == 'C' && c1 == 'A' && c2 == 'P' && c3 == 'A')
  55.                     {
  56.                         s->command = NGX_POP3_CAPA;

  57.                     } else if (c0 == 'A' && c1 == 'U' && c2 == 'T' && c3 == 'H')
  58.                     {
  59.                         s->command = NGX_POP3_AUTH;

  60.                     } else if (c0 == 'N' && c1 == 'O' && c2 == 'O' && c3 == 'P')
  61.                     {
  62.                         s->command = NGX_POP3_NOOP;
  63. #if (NGX_MAIL_SSL)
  64.                     } else if (c0 == 'S' && c1 == 'T' && c2 == 'L' && c3 == 'S')
  65.                     {
  66.                         s->command = NGX_POP3_STLS;
  67. #endif
  68.                     } else {
  69.                         goto invalid;
  70.                     }

  71.                 } else {
  72.                     goto invalid;
  73.                 }

  74.                 s->cmd.data = s->cmd_start;
  75.                 s->cmd.len = p - s->cmd_start;

  76.                 switch (ch) {
  77.                 case ' ':
  78.                     state = sw_spaces_before_argument;
  79.                     break;
  80.                 case CR:
  81.                     state = sw_almost_done;
  82.                     break;
  83.                 case LF:
  84.                     goto done;
  85.                 }
  86.                 break;
  87.             }

  88.             if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {
  89.                 goto invalid;
  90.             }

  91.             break;

  92.         case sw_invalid:
  93.             goto invalid;

  94.         case sw_spaces_before_argument:
  95.             switch (ch) {
  96.             case ' ':
  97.                 break;
  98.             case CR:
  99.                 state = sw_almost_done;
  100.                 break;
  101.             case LF:
  102.                 goto done;
  103.             default:
  104.                 if (s->args.nelts <= 2) {
  105.                     state = sw_argument;
  106.                     s->arg_start = p;
  107.                     break;
  108.                 }
  109.                 goto invalid;
  110.             }
  111.             break;

  112.         case sw_argument:
  113.             switch (ch) {

  114.             case ' ':

  115.                 /*
  116.                  * the space should be considered as part of the at username
  117.                  * or password, but not of argument in other commands
  118.                  */

  119.                 if (s->command == NGX_POP3_USER
  120.                     || s->command == NGX_POP3_PASS)
  121.                 {
  122.                     break;
  123.                 }

  124.                 /* fall through */

  125.             case CR:
  126.             case LF:
  127.                 arg = ngx_array_push(&s->args);
  128.                 if (arg == NULL) {
  129.                     return NGX_ERROR;
  130.                 }
  131.                 arg->len = p - s->arg_start;
  132.                 arg->data = s->arg_start;
  133.                 s->arg_start = NULL;

  134.                 switch (ch) {
  135.                 case ' ':
  136.                     state = sw_spaces_before_argument;
  137.                     break;
  138.                 case CR:
  139.                     state = sw_almost_done;
  140.                     break;
  141.                 case LF:
  142.                     goto done;
  143.                 }
  144.                 break;

  145.             default:
  146.                 break;
  147.             }
  148.             break;

  149.         case sw_almost_done:
  150.             switch (ch) {
  151.             case LF:
  152.                 goto done;
  153.             default:
  154.                 goto invalid;
  155.             }
  156.         }
  157.     }

  158.     s->buffer->pos = p;
  159.     s->state = state;

  160.     return NGX_AGAIN;

  161. done:

  162.     s->buffer->pos = p + 1;
  163.     s->state = (s->command != NGX_POP3_AUTH) ? sw_start : sw_argument;

  164.     return NGX_OK;

  165. invalid:

  166.     s->state = sw_invalid;

  167.     /* skip invalid command till LF */

  168.     for ( /* void */ ; p < s->buffer->last; p++) {
  169.         if (*p == LF) {
  170.             s->state = sw_start;
  171.             s->buffer->pos = p + 1;
  172.             return NGX_MAIL_PARSE_INVALID_COMMAND;
  173.         }
  174.     }

  175.     s->buffer->pos = p;

  176.     return NGX_AGAIN;
  177. }


  178. ngx_int_t
  179. ngx_mail_imap_parse_command(ngx_mail_session_t *s)
  180. {
  181.     u_char      ch, *p, *c, *dst, *src, *end;
  182.     ngx_str_t  *arg;
  183.     enum {
  184.         sw_start = 0,
  185.         sw_tag,
  186.         sw_invalid,
  187.         sw_spaces_before_command,
  188.         sw_command,
  189.         sw_spaces_before_argument,
  190.         sw_argument,
  191.         sw_backslash,
  192.         sw_literal,
  193.         sw_no_sync_literal_argument,
  194.         sw_start_literal_argument,
  195.         sw_literal_argument,
  196.         sw_end_literal_argument,
  197.         sw_almost_done
  198.     } state;

  199.     state = s->state;

  200.     for (p = s->buffer->pos; p < s->buffer->last; p++) {
  201.         ch = *p;

  202.         switch (state) {

  203.         /* IMAP tag */
  204.         case sw_start:
  205.             s->tag_start = p;
  206.             state = sw_tag;

  207.             /* fall through */

  208.         case sw_tag:
  209.             switch (ch) {
  210.             case ' ':
  211.                 s->tag.len = p - s->tag_start + 1;
  212.                 s->tag.data = s->tag_start;
  213.                 state = sw_spaces_before_command;
  214.                 break;
  215.             case CR:
  216.             case LF:
  217.                 goto invalid;
  218.             default:
  219.                 if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')
  220.                     && (ch < '0' || ch > '9') && ch != '-' && ch != '.'
  221.                     && ch != '_')
  222.                 {
  223.                     goto invalid;
  224.                 }
  225.                 if (p - s->tag_start > 31) {
  226.                     goto invalid;
  227.                 }
  228.                 break;
  229.             }
  230.             break;

  231.         case sw_invalid:
  232.             goto invalid;

  233.         case sw_spaces_before_command:
  234.             switch (ch) {
  235.             case ' ':
  236.                 break;
  237.             case CR:
  238.             case LF:
  239.                 goto invalid;
  240.             default:
  241.                 s->cmd_start = p;
  242.                 state = sw_command;
  243.                 break;
  244.             }
  245.             break;

  246.         case sw_command:
  247.             if (ch == ' ' || ch == CR || ch == LF) {

  248.                 c = s->cmd_start;

  249.                 switch (p - c) {

  250.                 case 4:
  251.                     if ((c[0] == 'N' || c[0] == 'n')
  252.                         && (c[1] == 'O'|| c[1] == 'o')
  253.                         && (c[2] == 'O'|| c[2] == 'o')
  254.                         && (c[3] == 'P'|| c[3] == 'p'))
  255.                     {
  256.                         s->command = NGX_IMAP_NOOP;

  257.                     } else {
  258.                         goto invalid;
  259.                     }
  260.                     break;

  261.                 case 5:
  262.                     if ((c[0] == 'L'|| c[0] == 'l')
  263.                         && (c[1] == 'O'|| c[1] == 'o')
  264.                         && (c[2] == 'G'|| c[2] == 'g')
  265.                         && (c[3] == 'I'|| c[3] == 'i')
  266.                         && (c[4] == 'N'|| c[4] == 'n'))
  267.                     {
  268.                         s->command = NGX_IMAP_LOGIN;

  269.                     } else {
  270.                         goto invalid;
  271.                     }
  272.                     break;

  273.                 case 6:
  274.                     if ((c[0] == 'L'|| c[0] == 'l')
  275.                         && (c[1] == 'O'|| c[1] == 'o')
  276.                         && (c[2] == 'G'|| c[2] == 'g')
  277.                         && (c[3] == 'O'|| c[3] == 'o')
  278.                         && (c[4] == 'U'|| c[4] == 'u')
  279.                         && (c[5] == 'T'|| c[5] == 't'))
  280.                     {
  281.                         s->command = NGX_IMAP_LOGOUT;

  282.                     } else {
  283.                         goto invalid;
  284.                     }
  285.                     break;

  286. #if (NGX_MAIL_SSL)
  287.                 case 8:
  288.                     if ((c[0] == 'S'|| c[0] == 's')
  289.                         && (c[1] == 'T'|| c[1] == 't')
  290.                         && (c[2] == 'A'|| c[2] == 'a')
  291.                         && (c[3] == 'R'|| c[3] == 'r')
  292.                         && (c[4] == 'T'|| c[4] == 't')
  293.                         && (c[5] == 'T'|| c[5] == 't')
  294.                         && (c[6] == 'L'|| c[6] == 'l')
  295.                         && (c[7] == 'S'|| c[7] == 's'))
  296.                     {
  297.                         s->command = NGX_IMAP_STARTTLS;

  298.                     } else {
  299.                         goto invalid;
  300.                     }
  301.                     break;
  302. #endif

  303.                 case 10:
  304.                     if ((c[0] == 'C'|| c[0] == 'c')
  305.                         && (c[1] == 'A'|| c[1] == 'a')
  306.                         && (c[2] == 'P'|| c[2] == 'p')
  307.                         && (c[3] == 'A'|| c[3] == 'a')
  308.                         && (c[4] == 'B'|| c[4] == 'b')
  309.                         && (c[5] == 'I'|| c[5] == 'i')
  310.                         && (c[6] == 'L'|| c[6] == 'l')
  311.                         && (c[7] == 'I'|| c[7] == 'i')
  312.                         && (c[8] == 'T'|| c[8] == 't')
  313.                         && (c[9] == 'Y'|| c[9] == 'y'))
  314.                     {
  315.                         s->command = NGX_IMAP_CAPABILITY;

  316.                     } else {
  317.                         goto invalid;
  318.                     }
  319.                     break;

  320.                 case 12:
  321.                     if ((c[0] == 'A'|| c[0] == 'a')
  322.                         && (c[1] == 'U'|| c[1] == 'u')
  323.                         && (c[2] == 'T'|| c[2] == 't')
  324.                         && (c[3] == 'H'|| c[3] == 'h')
  325.                         && (c[4] == 'E'|| c[4] == 'e')
  326.                         && (c[5] == 'N'|| c[5] == 'n')
  327.                         && (c[6] == 'T'|| c[6] == 't')
  328.                         && (c[7] == 'I'|| c[7] == 'i')
  329.                         && (c[8] == 'C'|| c[8] == 'c')
  330.                         && (c[9] == 'A'|| c[9] == 'a')
  331.                         && (c[10] == 'T'|| c[10] == 't')
  332.                         && (c[11] == 'E'|| c[11] == 'e'))
  333.                     {
  334.                         s->command = NGX_IMAP_AUTHENTICATE;

  335.                     } else {
  336.                         goto invalid;
  337.                     }
  338.                     break;

  339.                 default:
  340.                     goto invalid;
  341.                 }

  342.                 s->cmd.data = s->cmd_start;
  343.                 s->cmd.len = p - s->cmd_start;

  344.                 switch (ch) {
  345.                 case ' ':
  346.                     state = sw_spaces_before_argument;
  347.                     break;
  348.                 case CR:
  349.                     state = sw_almost_done;
  350.                     break;
  351.                 case LF:
  352.                     goto done;
  353.                 }
  354.                 break;
  355.             }

  356.             if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {
  357.                 goto invalid;
  358.             }

  359.             break;

  360.         case sw_spaces_before_argument:
  361.             switch (ch) {
  362.             case ' ':
  363.                 break;
  364.             case CR:
  365.                 state = sw_almost_done;
  366.                 break;
  367.             case LF:
  368.                 goto done;
  369.             case '"':
  370.                 if (s->args.nelts <= 2) {
  371.                     s->quoted = 1;
  372.                     s->arg_start = p + 1;
  373.                     state = sw_argument;
  374.                     break;
  375.                 }
  376.                 goto invalid;
  377.             case '{':
  378.                 if (s->args.nelts <= 2) {
  379.                     state = sw_literal;
  380.                     break;
  381.                 }
  382.                 goto invalid;
  383.             default:
  384.                 if (s->args.nelts <= 2) {
  385.                     s->arg_start = p;
  386.                     state = sw_argument;
  387.                     break;
  388.                 }
  389.                 goto invalid;
  390.             }
  391.             break;

  392.         case sw_argument:
  393.             if (ch == ' ' && s->quoted) {
  394.                 break;
  395.             }

  396.             switch (ch) {
  397.             case '"':
  398.                 if (!s->quoted) {
  399.                     break;
  400.                 }
  401.                 s->quoted = 0;
  402.                 /* fall through */
  403.             case ' ':
  404.             case CR:
  405.             case LF:
  406.                 arg = ngx_array_push(&s->args);
  407.                 if (arg == NULL) {
  408.                     return NGX_ERROR;
  409.                 }
  410.                 arg->len = p - s->arg_start;
  411.                 arg->data = s->arg_start;

  412.                 if (s->backslash) {
  413.                     dst = s->arg_start;
  414.                     end = p;

  415.                     for (src = dst; src < end; dst++) {
  416.                         *dst = *src;
  417.                         if (*src++ == '\\') {
  418.                             *dst = *src++;
  419.                         }
  420.                     }

  421.                     arg->len = dst - s->arg_start;
  422.                     s->backslash = 0;
  423.                 }

  424.                 s->arg_start = NULL;

  425.                 switch (ch) {
  426.                 case '"':
  427.                 case ' ':
  428.                     state = sw_spaces_before_argument;
  429.                     break;
  430.                 case CR:
  431.                     state = sw_almost_done;
  432.                     break;
  433.                 case LF:
  434.                     goto done;
  435.                 }
  436.                 break;
  437.             case '\\':
  438.                 if (s->quoted) {
  439.                     s->backslash = 1;
  440.                     state = sw_backslash;
  441.                 }
  442.                 break;
  443.             }
  444.             break;

  445.         case sw_backslash:
  446.             switch (ch) {
  447.             case CR:
  448.             case LF:
  449.                 goto invalid;
  450.             default:
  451.                 state = sw_argument;
  452.             }
  453.             break;

  454.         case sw_literal:
  455.             if (s->literal_len > NGX_MAX_SIZE_T_VALUE / 10) {
  456.                 goto invalid;
  457.             }
  458.             if (ch >= '0' && ch <= '9') {
  459.                 s->literal_len = s->literal_len * 10 + (ch - '0');
  460.                 break;
  461.             }
  462.             if (ch == '}') {
  463.                 state = sw_start_literal_argument;
  464.                 break;
  465.             }
  466.             if (ch == '+') {
  467.                 state = sw_no_sync_literal_argument;
  468.                 break;
  469.             }
  470.             goto invalid;

  471.         case sw_no_sync_literal_argument:
  472.             if (ch == '}') {
  473.                 s->no_sync_literal = 1;
  474.                 state = sw_start_literal_argument;
  475.                 break;
  476.             }
  477.             goto invalid;

  478.         case sw_start_literal_argument:
  479.             switch (ch) {
  480.             case CR:
  481.                 break;
  482.             case LF:
  483.                 s->buffer->pos = p + 1;
  484.                 s->arg_start = p + 1;
  485.                 if (s->no_sync_literal == 0) {
  486.                     s->state = sw_literal_argument;
  487.                     return NGX_IMAP_NEXT;
  488.                 }
  489.                 state = sw_literal_argument;
  490.                 s->no_sync_literal = 0;
  491.                 break;
  492.             default:
  493.                 goto invalid;
  494.             }
  495.             break;

  496.         case sw_literal_argument:
  497.             if (s->literal_len && --s->literal_len) {
  498.                 break;
  499.             }

  500.             arg = ngx_array_push(&s->args);
  501.             if (arg == NULL) {
  502.                 return NGX_ERROR;
  503.             }
  504.             arg->len = p + 1 - s->arg_start;
  505.             arg->data = s->arg_start;
  506.             s->arg_start = NULL;
  507.             state = sw_end_literal_argument;

  508.             break;

  509.         case sw_end_literal_argument:
  510.             switch (ch) {
  511.             case ' ':
  512.                 state = sw_spaces_before_argument;
  513.                 break;
  514.             case CR:
  515.                 state = sw_almost_done;
  516.                 break;
  517.             case LF:
  518.                 goto done;
  519.             default:
  520.                 goto invalid;
  521.             }
  522.             break;

  523.         case sw_almost_done:
  524.             switch (ch) {
  525.             case LF:
  526.                 goto done;
  527.             default:
  528.                 goto invalid;
  529.             }
  530.         }
  531.     }

  532.     s->buffer->pos = p;
  533.     s->state = state;

  534.     return NGX_AGAIN;

  535. done:

  536.     s->buffer->pos = p + 1;
  537.     s->state = (s->command != NGX_IMAP_AUTHENTICATE) ? sw_start : sw_argument;

  538.     return NGX_OK;

  539. invalid:

  540.     s->state = sw_invalid;
  541.     s->quoted = 0;
  542.     s->backslash = 0;
  543.     s->no_sync_literal = 0;
  544.     s->literal_len = 0;

  545.     /* skip invalid command till LF */

  546.     for ( /* void */ ; p < s->buffer->last; p++) {
  547.         if (*p == LF) {
  548.             s->state = sw_start;
  549.             s->buffer->pos = p + 1;

  550.             /* detect non-synchronizing literals */

  551.             if ((size_t) (p - s->buffer->start) > sizeof("{1+}") - 1) {
  552.                 p--;

  553.                 if (*p == CR) {
  554.                     p--;
  555.                 }

  556.                 if (*p == '}' && *(p - 1) == '+') {
  557.                     s->quit = 1;
  558.                 }
  559.             }

  560.             return NGX_MAIL_PARSE_INVALID_COMMAND;
  561.         }
  562.     }

  563.     s->buffer->pos = p;

  564.     return NGX_AGAIN;
  565. }


  566. ngx_int_t
  567. ngx_mail_smtp_parse_command(ngx_mail_session_t *s)
  568. {
  569.     u_char      ch, *p, *c, c0, c1, c2, c3;
  570.     ngx_str_t  *arg;
  571.     enum {
  572.         sw_start = 0,
  573.         sw_command,
  574.         sw_invalid,
  575.         sw_spaces_before_argument,
  576.         sw_argument,
  577.         sw_almost_done
  578.     } state;

  579.     state = s->state;

  580.     for (p = s->buffer->pos; p < s->buffer->last; p++) {
  581.         ch = *p;

  582.         switch (state) {

  583.         /* SMTP command */
  584.         case sw_start:
  585.             s->cmd_start = p;
  586.             state = sw_command;

  587.             /* fall through */

  588.         case sw_command:
  589.             if (ch == ' ' || ch == CR || ch == LF) {
  590.                 c = s->cmd_start;

  591.                 if (p - c == 4) {

  592.                     c0 = ngx_toupper(c[0]);
  593.                     c1 = ngx_toupper(c[1]);
  594.                     c2 = ngx_toupper(c[2]);
  595.                     c3 = ngx_toupper(c[3]);

  596.                     if (c0 == 'H' && c1 == 'E' && c2 == 'L' && c3 == 'O')
  597.                     {
  598.                         s->command = NGX_SMTP_HELO;

  599.                     } else if (c0 == 'E' && c1 == 'H' && c2 == 'L' && c3 == 'O')
  600.                     {
  601.                         s->command = NGX_SMTP_EHLO;

  602.                     } else if (c0 == 'Q' && c1 == 'U' && c2 == 'I' && c3 == 'T')
  603.                     {
  604.                         s->command = NGX_SMTP_QUIT;

  605.                     } else if (c0 == 'A' && c1 == 'U' && c2 == 'T' && c3 == 'H')
  606.                     {
  607.                         s->command = NGX_SMTP_AUTH;

  608.                     } else if (c0 == 'N' && c1 == 'O' && c2 == 'O' && c3 == 'P')
  609.                     {
  610.                         s->command = NGX_SMTP_NOOP;

  611.                     } else if (c0 == 'M' && c1 == 'A' && c2 == 'I' && c3 == 'L')
  612.                     {
  613.                         s->command = NGX_SMTP_MAIL;

  614.                     } else if (c0 == 'R' && c1 == 'S' && c2 == 'E' && c3 == 'T')
  615.                     {
  616.                         s->command = NGX_SMTP_RSET;

  617.                     } else if (c0 == 'R' && c1 == 'C' && c2 == 'P' && c3 == 'T')
  618.                     {
  619.                         s->command = NGX_SMTP_RCPT;

  620.                     } else if (c0 == 'V' && c1 == 'R' && c2 == 'F' && c3 == 'Y')
  621.                     {
  622.                         s->command = NGX_SMTP_VRFY;

  623.                     } else if (c0 == 'E' && c1 == 'X' && c2 == 'P' && c3 == 'N')
  624.                     {
  625.                         s->command = NGX_SMTP_EXPN;

  626.                     } else if (c0 == 'H' && c1 == 'E' && c2 == 'L' && c3 == 'P')
  627.                     {
  628.                         s->command = NGX_SMTP_HELP;

  629.                     } else {
  630.                         goto invalid;
  631.                     }
  632. #if (NGX_MAIL_SSL)
  633.                 } else if (p - c == 8) {

  634.                     if ((c[0] == 'S'|| c[0] == 's')
  635.                         && (c[1] == 'T'|| c[1] == 't')
  636.                         && (c[2] == 'A'|| c[2] == 'a')
  637.                         && (c[3] == 'R'|| c[3] == 'r')
  638.                         && (c[4] == 'T'|| c[4] == 't')
  639.                         && (c[5] == 'T'|| c[5] == 't')
  640.                         && (c[6] == 'L'|| c[6] == 'l')
  641.                         && (c[7] == 'S'|| c[7] == 's'))
  642.                     {
  643.                         s->command = NGX_SMTP_STARTTLS;

  644.                     } else {
  645.                         goto invalid;
  646.                     }
  647. #endif
  648.                 } else {
  649.                     goto invalid;
  650.                 }

  651.                 s->cmd.data = s->cmd_start;
  652.                 s->cmd.len = p - s->cmd_start;

  653.                 switch (ch) {
  654.                 case ' ':
  655.                     state = sw_spaces_before_argument;
  656.                     break;
  657.                 case CR:
  658.                     state = sw_almost_done;
  659.                     break;
  660.                 case LF:
  661.                     goto done;
  662.                 }
  663.                 break;
  664.             }

  665.             if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {
  666.                 goto invalid;
  667.             }

  668.             break;

  669.         case sw_invalid:
  670.             goto invalid;

  671.         case sw_spaces_before_argument:
  672.             switch (ch) {
  673.             case ' ':
  674.                 break;
  675.             case CR:
  676.                 state = sw_almost_done;
  677.                 break;
  678.             case LF:
  679.                 goto done;
  680.             default:
  681.                 if (s->args.nelts <= 10) {
  682.                     state = sw_argument;
  683.                     s->arg_start = p;
  684.                     break;
  685.                 }
  686.                 goto invalid;
  687.             }
  688.             break;

  689.         case sw_argument:
  690.             switch (ch) {
  691.             case ' ':
  692.             case CR:
  693.             case LF:
  694.                 arg = ngx_array_push(&s->args);
  695.                 if (arg == NULL) {
  696.                     return NGX_ERROR;
  697.                 }
  698.                 arg->len = p - s->arg_start;
  699.                 arg->data = s->arg_start;
  700.                 s->arg_start = NULL;

  701.                 switch (ch) {
  702.                 case ' ':
  703.                     state = sw_spaces_before_argument;
  704.                     break;
  705.                 case CR:
  706.                     state = sw_almost_done;
  707.                     break;
  708.                 case LF:
  709.                     goto done;
  710.                 }
  711.                 break;

  712.             default:
  713.                 break;
  714.             }
  715.             break;

  716.         case sw_almost_done:
  717.             switch (ch) {
  718.             case LF:
  719.                 goto done;
  720.             default:
  721.                 goto invalid;
  722.             }
  723.         }
  724.     }

  725.     s->buffer->pos = p;
  726.     s->state = state;

  727.     return NGX_AGAIN;

  728. done:

  729.     s->buffer->pos = p + 1;
  730.     s->state = (s->command != NGX_SMTP_AUTH) ? sw_start : sw_argument;

  731.     return NGX_OK;

  732. invalid:

  733.     s->state = sw_invalid;

  734.     /* skip invalid command till LF */

  735.     for ( /* void */ ; p < s->buffer->last; p++) {
  736.         if (*p == LF) {
  737.             s->state = sw_start;
  738.             s->buffer->pos = p + 1;
  739.             return NGX_MAIL_PARSE_INVALID_COMMAND;
  740.         }
  741.     }

  742.     s->buffer->pos = p;

  743.     return NGX_AGAIN;
  744. }


  745. ngx_int_t
  746. ngx_mail_auth_parse(ngx_mail_session_t *s, ngx_connection_t *c)
  747. {
  748.     ngx_str_t                 *arg;

  749. #if (NGX_MAIL_SSL)
  750.     if (ngx_mail_starttls_only(s, c)) {
  751.         return NGX_MAIL_PARSE_INVALID_COMMAND;
  752.     }
  753. #endif

  754.     if (s->args.nelts == 0) {
  755.         return NGX_MAIL_PARSE_INVALID_COMMAND;
  756.     }

  757.     arg = s->args.elts;

  758.     if (arg[0].len == 5) {

  759.         if (ngx_strncasecmp(arg[0].data, (u_char *) "LOGIN", 5) == 0) {

  760.             if (s->args.nelts == 1) {
  761.                 return NGX_MAIL_AUTH_LOGIN;
  762.             }

  763.             if (s->args.nelts == 2) {
  764.                 return NGX_MAIL_AUTH_LOGIN_USERNAME;
  765.             }

  766.             return NGX_MAIL_PARSE_INVALID_COMMAND;
  767.         }

  768.         if (ngx_strncasecmp(arg[0].data, (u_char *) "PLAIN", 5) == 0) {

  769.             if (s->args.nelts == 1) {
  770.                 return NGX_MAIL_AUTH_PLAIN;
  771.             }

  772.             if (s->args.nelts == 2) {
  773.                 return ngx_mail_auth_plain(s, c, 1);
  774.             }
  775.         }

  776.         return NGX_MAIL_PARSE_INVALID_COMMAND;
  777.     }

  778.     if (arg[0].len == 8) {

  779.         if (ngx_strncasecmp(arg[0].data, (u_char *) "CRAM-MD5", 8) == 0) {

  780.             if (s->args.nelts != 1) {
  781.                 return NGX_MAIL_PARSE_INVALID_COMMAND;
  782.             }

  783.             return NGX_MAIL_AUTH_CRAM_MD5;
  784.         }

  785.         if (ngx_strncasecmp(arg[0].data, (u_char *) "EXTERNAL", 8) == 0) {

  786.             if (s->args.nelts == 1) {
  787.                 return NGX_MAIL_AUTH_EXTERNAL;
  788.             }

  789.             if (s->args.nelts == 2) {
  790.                 return ngx_mail_auth_external(s, c, 1);
  791.             }
  792.         }

  793.         return NGX_MAIL_PARSE_INVALID_COMMAND;
  794.     }

  795.     return NGX_MAIL_PARSE_INVALID_COMMAND;
  796. }