src/mail/ngx_mail_parse.c - nginx source code

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 (ch >= '0' && ch <= '9') {
  456.                 s->literal_len = s->literal_len * 10 + (ch - '0');
  457.                 break;
  458.             }
  459.             if (ch == '}') {
  460.                 state = sw_start_literal_argument;
  461.                 break;
  462.             }
  463.             if (ch == '+') {
  464.                 state = sw_no_sync_literal_argument;
  465.                 break;
  466.             }
  467.             goto invalid;

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

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

  493.         case sw_literal_argument:
  494.             if (s->literal_len && --s->literal_len) {
  495.                 break;
  496.             }

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

  505.             break;

  506.         case sw_end_literal_argument:
  507.             switch (ch) {
  508.             case '{':
  509.                 if (s->args.nelts <= 2) {
  510.                     state = sw_literal;
  511.                     break;
  512.                 }
  513.                 goto invalid;
  514.             case CR:
  515.                 state = sw_almost_done;
  516.                 break;
  517.             case LF:
  518.                 goto done;
  519.             default:
  520.                 state = sw_spaces_before_argument;
  521.                 break;
  522.             }
  523.             break;

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

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

  535.     return NGX_AGAIN;

  536. done:

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

  539.     return NGX_OK;

  540. invalid:

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

  546.     /* skip invalid command till LF */

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

  551.             /* detect non-synchronizing literals */

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

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

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

  561.             return NGX_MAIL_PARSE_INVALID_COMMAND;
  562.         }
  563.     }

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

  565.     return NGX_AGAIN;
  566. }


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

  580.     state = s->state;

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

  583.         switch (state) {

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

  588.             /* fall through */

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  669.             break;

  670.         case sw_invalid:
  671.             goto invalid;

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

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

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

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

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

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

  728.     return NGX_AGAIN;

  729. done:

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

  732.     return NGX_OK;

  733. invalid:

  734.     s->state = sw_invalid;

  735.     /* skip invalid command till LF */

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

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

  744.     return NGX_AGAIN;
  745. }


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

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

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

  758.     arg = s->args.elts;

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

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

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

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

  767.             return NGX_MAIL_PARSE_INVALID_COMMAND;
  768.         }

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

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

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

  777.         return NGX_MAIL_PARSE_INVALID_COMMAND;
  778.     }

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

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

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

  784.             return NGX_MAIL_AUTH_CRAM_MD5;
  785.         }

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

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

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

  794.         return NGX_MAIL_PARSE_INVALID_COMMAND;
  795.     }

  796.     return NGX_MAIL_PARSE_INVALID_COMMAND;
  797. }