src/mail/ngx_mail_parse.c - nginx-1.7.10

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_spaces_before_argument,
  20.         sw_argument,
  21.         sw_almost_done
  22.     } state;

  23.     state = s->state;

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

  26.         switch (state) {

  27.         /* POP3 command */
  28.         case sw_start:
  29.             if (ch == ' ' || ch == CR || ch == LF) {
  30.                 c = s->buffer->start;

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

  32.                     c0 = ngx_toupper(c[0]);
  33.                     c1 = ngx_toupper(c[1]);
  34.                     c2 = ngx_toupper(c[2]);
  35.                     c3 = ngx_toupper(c[3]);

  36.                     if (c0 == 'U' && c1 == 'S' && c2 == 'E' && c3 == 'R')
  37.                     {
  38.                         s->command = NGX_POP3_USER;

  39.                     } else if (c0 == 'P' && c1 == 'A' && c2 == 'S' && c3 == 'S')
  40.                     {
  41.                         s->command = NGX_POP3_PASS;

  42.                     } else if (c0 == 'A' && c1 == 'P' && c2 == 'O' && c3 == 'P')
  43.                     {
  44.                         s->command = NGX_POP3_APOP;

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

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

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

  54.                     } else if (c0 == 'N' && c1 == 'O' && c2 == 'O' && c3 == 'P')
  55.                     {
  56.                         s->command = NGX_POP3_NOOP;
  57. #if (NGX_MAIL_SSL)
  58.                     } else if (c0 == 'S' && c1 == 'T' && c2 == 'L' && c3 == 'S')
  59.                     {
  60.                         s->command = NGX_POP3_STLS;
  61. #endif
  62.                     } else {
  63.                         goto invalid;
  64.                     }

  65.                 } else {
  66.                     goto invalid;
  67.                 }

  68.                 switch (ch) {
  69.                 case ' ':
  70.                     state = sw_spaces_before_argument;
  71.                     break;
  72.                 case CR:
  73.                     state = sw_almost_done;
  74.                     break;
  75.                 case LF:
  76.                     goto done;
  77.                 }
  78.                 break;
  79.             }

  80.             if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {
  81.                 goto invalid;
  82.             }

  83.             break;

  84.         case sw_spaces_before_argument:
  85.             switch (ch) {
  86.             case ' ':
  87.                 break;
  88.             case CR:
  89.                 state = sw_almost_done;
  90.                 s->arg_end = p;
  91.                 break;
  92.             case LF:
  93.                 s->arg_end = p;
  94.                 goto done;
  95.             default:
  96.                 if (s->args.nelts <= 2) {
  97.                     state = sw_argument;
  98.                     s->arg_start = p;
  99.                     break;
  100.                 }
  101.                 goto invalid;
  102.             }
  103.             break;

  104.         case sw_argument:
  105.             switch (ch) {

  106.             case ' ':

  107.                 /*
  108.                  * the space should be considered as part of the at username
  109.                  * or password, but not of argument in other commands
  110.                  */

  111.                 if (s->command == NGX_POP3_USER
  112.                     || s->command == NGX_POP3_PASS)
  113.                 {
  114.                     break;
  115.                 }

  116.                 /* fall through */

  117.             case CR:
  118.             case LF:
  119.                 arg = ngx_array_push(&s->args);
  120.                 if (arg == NULL) {
  121.                     return NGX_ERROR;
  122.                 }
  123.                 arg->len = p - s->arg_start;
  124.                 arg->data = s->arg_start;
  125.                 s->arg_start = NULL;

  126.                 switch (ch) {
  127.                 case ' ':
  128.                     state = sw_spaces_before_argument;
  129.                     break;
  130.                 case CR:
  131.                     state = sw_almost_done;
  132.                     break;
  133.                 case LF:
  134.                     goto done;
  135.                 }
  136.                 break;

  137.             default:
  138.                 break;
  139.             }
  140.             break;

  141.         case sw_almost_done:
  142.             switch (ch) {
  143.             case LF:
  144.                 goto done;
  145.             default:
  146.                 goto invalid;
  147.             }
  148.         }
  149.     }

  150.     s->buffer->pos = p;
  151.     s->state = state;

  152.     return NGX_AGAIN;

  153. done:

  154.     s->buffer->pos = p + 1;

  155.     if (s->arg_start) {
  156.         arg = ngx_array_push(&s->args);
  157.         if (arg == NULL) {
  158.             return NGX_ERROR;
  159.         }
  160.         arg->len = s->arg_end - s->arg_start;
  161.         arg->data = s->arg_start;
  162.         s->arg_start = NULL;
  163.     }

  164.     s->state = (s->command != NGX_POP3_AUTH) ? sw_start : sw_argument;

  165.     return NGX_OK;

  166. invalid:

  167.     s->state = sw_start;
  168.     s->arg_start = NULL;

  169.     return NGX_MAIL_PARSE_INVALID_COMMAND;
  170. }


  171. ngx_int_t
  172. ngx_mail_imap_parse_command(ngx_mail_session_t *s)
  173. {
  174.     u_char      ch, *p, *c;
  175.     ngx_str_t  *arg;
  176.     enum {
  177.         sw_start = 0,
  178.         sw_spaces_before_command,
  179.         sw_command,
  180.         sw_spaces_before_argument,
  181.         sw_argument,
  182.         sw_backslash,
  183.         sw_literal,
  184.         sw_no_sync_literal_argument,
  185.         sw_start_literal_argument,
  186.         sw_literal_argument,
  187.         sw_end_literal_argument,
  188.         sw_almost_done
  189.     } state;

  190.     state = s->state;

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

  193.         switch (state) {

  194.         /* IMAP tag */
  195.         case sw_start:
  196.             switch (ch) {
  197.             case ' ':
  198.                 s->tag.len = p - s->buffer->start + 1;
  199.                 s->tag.data = s->buffer->start;
  200.                 state = sw_spaces_before_command;
  201.                 break;
  202.             case CR:
  203.                 s->state = sw_start;
  204.                 return NGX_MAIL_PARSE_INVALID_COMMAND;
  205.             case LF:
  206.                 s->state = sw_start;
  207.                 return NGX_MAIL_PARSE_INVALID_COMMAND;
  208.             }
  209.             break;

  210.         case sw_spaces_before_command:
  211.             switch (ch) {
  212.             case ' ':
  213.                 break;
  214.             case CR:
  215.                 s->state = sw_start;
  216.                 return NGX_MAIL_PARSE_INVALID_COMMAND;
  217.             case LF:
  218.                 s->state = sw_start;
  219.                 return NGX_MAIL_PARSE_INVALID_COMMAND;
  220.             default:
  221.                 s->cmd_start = p;
  222.                 state = sw_command;
  223.                 break;
  224.             }
  225.             break;

  226.         case sw_command:
  227.             if (ch == ' ' || ch == CR || ch == LF) {

  228.                 c = s->cmd_start;

  229.                 switch (p - c) {

  230.                 case 4:
  231.                     if ((c[0] == 'N' || c[0] == 'n')
  232.                         && (c[1] == 'O'|| c[1] == 'o')
  233.                         && (c[2] == 'O'|| c[2] == 'o')
  234.                         && (c[3] == 'P'|| c[3] == 'p'))
  235.                     {
  236.                         s->command = NGX_IMAP_NOOP;

  237.                     } else {
  238.                         goto invalid;
  239.                     }
  240.                     break;

  241.                 case 5:
  242.                     if ((c[0] == 'L'|| c[0] == 'l')
  243.                         && (c[1] == 'O'|| c[1] == 'o')
  244.                         && (c[2] == 'G'|| c[2] == 'g')
  245.                         && (c[3] == 'I'|| c[3] == 'i')
  246.                         && (c[4] == 'N'|| c[4] == 'n'))
  247.                     {
  248.                         s->command = NGX_IMAP_LOGIN;

  249.                     } else {
  250.                         goto invalid;
  251.                     }
  252.                     break;

  253.                 case 6:
  254.                     if ((c[0] == 'L'|| c[0] == 'l')
  255.                         && (c[1] == 'O'|| c[1] == 'o')
  256.                         && (c[2] == 'G'|| c[2] == 'g')
  257.                         && (c[3] == 'O'|| c[3] == 'o')
  258.                         && (c[4] == 'U'|| c[4] == 'u')
  259.                         && (c[5] == 'T'|| c[5] == 't'))
  260.                     {
  261.                         s->command = NGX_IMAP_LOGOUT;

  262.                     } else {
  263.                         goto invalid;
  264.                     }
  265.                     break;

  266. #if (NGX_MAIL_SSL)
  267.                 case 8:
  268.                     if ((c[0] == 'S'|| c[0] == 's')
  269.                         && (c[1] == 'T'|| c[1] == 't')
  270.                         && (c[2] == 'A'|| c[2] == 'a')
  271.                         && (c[3] == 'R'|| c[3] == 'r')
  272.                         && (c[4] == 'T'|| c[4] == 't')
  273.                         && (c[5] == 'T'|| c[5] == 't')
  274.                         && (c[6] == 'L'|| c[6] == 'l')
  275.                         && (c[7] == 'S'|| c[7] == 's'))
  276.                     {
  277.                         s->command = NGX_IMAP_STARTTLS;

  278.                     } else {
  279.                         goto invalid;
  280.                     }
  281.                     break;
  282. #endif

  283.                 case 10:
  284.                     if ((c[0] == 'C'|| c[0] == 'c')
  285.                         && (c[1] == 'A'|| c[1] == 'a')
  286.                         && (c[2] == 'P'|| c[2] == 'p')
  287.                         && (c[3] == 'A'|| c[3] == 'a')
  288.                         && (c[4] == 'B'|| c[4] == 'b')
  289.                         && (c[5] == 'I'|| c[5] == 'i')
  290.                         && (c[6] == 'L'|| c[6] == 'l')
  291.                         && (c[7] == 'I'|| c[7] == 'i')
  292.                         && (c[8] == 'T'|| c[8] == 't')
  293.                         && (c[9] == 'Y'|| c[9] == 'y'))
  294.                     {
  295.                         s->command = NGX_IMAP_CAPABILITY;

  296.                     } else {
  297.                         goto invalid;
  298.                     }
  299.                     break;

  300.                 case 12:
  301.                     if ((c[0] == 'A'|| c[0] == 'a')
  302.                         && (c[1] == 'U'|| c[1] == 'u')
  303.                         && (c[2] == 'T'|| c[2] == 't')
  304.                         && (c[3] == 'H'|| c[3] == 'h')
  305.                         && (c[4] == 'E'|| c[4] == 'e')
  306.                         && (c[5] == 'N'|| c[5] == 'n')
  307.                         && (c[6] == 'T'|| c[6] == 't')
  308.                         && (c[7] == 'I'|| c[7] == 'i')
  309.                         && (c[8] == 'C'|| c[8] == 'c')
  310.                         && (c[9] == 'A'|| c[9] == 'a')
  311.                         && (c[10] == 'T'|| c[10] == 't')
  312.                         && (c[11] == 'E'|| c[11] == 'e'))
  313.                     {
  314.                         s->command = NGX_IMAP_AUTHENTICATE;

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

  319.                 default:
  320.                     goto invalid;
  321.                 }

  322.                 switch (ch) {
  323.                 case ' ':
  324.                     state = sw_spaces_before_argument;
  325.                     break;
  326.                 case CR:
  327.                     state = sw_almost_done;
  328.                     break;
  329.                 case LF:
  330.                     goto done;
  331.                 }
  332.                 break;
  333.             }

  334.             if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {
  335.                 goto invalid;
  336.             }

  337.             break;

  338.         case sw_spaces_before_argument:
  339.             switch (ch) {
  340.             case ' ':
  341.                 break;
  342.             case CR:
  343.                 state = sw_almost_done;
  344.                 s->arg_end = p;
  345.                 break;
  346.             case LF:
  347.                 s->arg_end = p;
  348.                 goto done;
  349.             case '"':
  350.                 if (s->args.nelts <= 2) {
  351.                     s->quoted = 1;
  352.                     s->arg_start = p + 1;
  353.                     state = sw_argument;
  354.                     break;
  355.                 }
  356.                 goto invalid;
  357.             case '{':
  358.                 if (s->args.nelts <= 2) {
  359.                     state = sw_literal;
  360.                     break;
  361.                 }
  362.                 goto invalid;
  363.             default:
  364.                 if (s->args.nelts <= 2) {
  365.                     s->arg_start = p;
  366.                     state = sw_argument;
  367.                     break;
  368.                 }
  369.                 goto invalid;
  370.             }
  371.             break;

  372.         case sw_argument:
  373.             if (ch == ' ' && s->quoted) {
  374.                 break;
  375.             }

  376.             switch (ch) {
  377.             case '"':
  378.                 if (!s->quoted) {
  379.                     break;
  380.                 }
  381.                 s->quoted = 0;
  382.                 /* fall through */
  383.             case ' ':
  384.             case CR:
  385.             case LF:
  386.                 arg = ngx_array_push(&s->args);
  387.                 if (arg == NULL) {
  388.                     return NGX_ERROR;
  389.                 }
  390.                 arg->len = p - s->arg_start;
  391.                 arg->data = s->arg_start;
  392.                 s->arg_start = NULL;

  393.                 switch (ch) {
  394.                 case '"':
  395.                 case ' ':
  396.                     state = sw_spaces_before_argument;
  397.                     break;
  398.                 case CR:
  399.                     state = sw_almost_done;
  400.                     break;
  401.                 case LF:
  402.                     goto done;
  403.                 }
  404.                 break;
  405.             case '\\':
  406.                 if (s->quoted) {
  407.                     s->backslash = 1;
  408.                     state = sw_backslash;
  409.                 }
  410.                 break;
  411.             }
  412.             break;

  413.         case sw_backslash:
  414.             switch (ch) {
  415.             case CR:
  416.             case LF:
  417.                 goto invalid;
  418.             default:
  419.                 state = sw_argument;
  420.             }
  421.             break;

  422.         case sw_literal:
  423.             if (ch >= '0' && ch <= '9') {
  424.                 s->literal_len = s->literal_len * 10 + (ch - '0');
  425.                 break;
  426.             }
  427.             if (ch == '}') {
  428.                 state = sw_start_literal_argument;
  429.                 break;
  430.             }
  431.             if (ch == '+') {
  432.                 state = sw_no_sync_literal_argument;
  433.                 break;
  434.             }
  435.             goto invalid;

  436.         case sw_no_sync_literal_argument:
  437.             if (ch == '}') {
  438.                 s->no_sync_literal = 1;
  439.                 state = sw_start_literal_argument;
  440.                 break;
  441.             }
  442.             goto invalid;

  443.         case sw_start_literal_argument:
  444.             switch (ch) {
  445.             case CR:
  446.                 break;
  447.             case LF:
  448.                 s->buffer->pos = p + 1;
  449.                 s->arg_start = p + 1;
  450.                 if (s->no_sync_literal == 0) {
  451.                     s->state = sw_literal_argument;
  452.                     return NGX_IMAP_NEXT;
  453.                 }
  454.                 state = sw_literal_argument;
  455.                 s->no_sync_literal = 0;
  456.                 break;
  457.             default:
  458.                 goto invalid;
  459.             }
  460.             break;

  461.         case sw_literal_argument:
  462.             if (s->literal_len && --s->literal_len) {
  463.                 break;
  464.             }

  465.             arg = ngx_array_push(&s->args);
  466.             if (arg == NULL) {
  467.                 return NGX_ERROR;
  468.             }
  469.             arg->len = p + 1 - s->arg_start;
  470.             arg->data = s->arg_start;
  471.             s->arg_start = NULL;
  472.             state = sw_end_literal_argument;

  473.             break;

  474.         case sw_end_literal_argument:
  475.             switch (ch) {
  476.             case '{':
  477.                 if (s->args.nelts <= 2) {
  478.                     state = sw_literal;
  479.                     break;
  480.                 }
  481.                 goto invalid;
  482.             case CR:
  483.                 state = sw_almost_done;
  484.                 break;
  485.             case LF:
  486.                 goto done;
  487.             default:
  488.                 state = sw_spaces_before_argument;
  489.                 break;
  490.             }
  491.             break;

  492.         case sw_almost_done:
  493.             switch (ch) {
  494.             case LF:
  495.                 goto done;
  496.             default:
  497.                 goto invalid;
  498.             }
  499.         }
  500.     }

  501.     s->buffer->pos = p;
  502.     s->state = state;

  503.     return NGX_AGAIN;

  504. done:

  505.     s->buffer->pos = p + 1;

  506.     if (s->arg_start) {
  507.         arg = ngx_array_push(&s->args);
  508.         if (arg == NULL) {
  509.             return NGX_ERROR;
  510.         }
  511.         arg->len = s->arg_end - s->arg_start;
  512.         arg->data = s->arg_start;

  513.         s->arg_start = NULL;
  514.         s->cmd_start = NULL;
  515.         s->quoted = 0;
  516.         s->no_sync_literal = 0;
  517.         s->literal_len = 0;
  518.     }

  519.     s->state = (s->command != NGX_IMAP_AUTHENTICATE) ? sw_start : sw_argument;

  520.     return NGX_OK;

  521. invalid:

  522.     s->state = sw_start;
  523.     s->quoted = 0;
  524.     s->no_sync_literal = 0;
  525.     s->literal_len = 0;

  526.     return NGX_MAIL_PARSE_INVALID_COMMAND;
  527. }


  528. ngx_int_t
  529. ngx_mail_smtp_parse_command(ngx_mail_session_t *s)
  530. {
  531.     u_char      ch, *p, *c, c0, c1, c2, c3;
  532.     ngx_str_t  *arg;
  533.     enum {
  534.         sw_start = 0,
  535.         sw_command,
  536.         sw_invalid,
  537.         sw_spaces_before_argument,
  538.         sw_argument,
  539.         sw_almost_done
  540.     } state;

  541.     state = s->state;

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

  544.         switch (state) {

  545.         /* SMTP command */
  546.         case sw_start:
  547.             s->cmd_start = p;
  548.             state = sw_command;

  549.             /* fall through */

  550.         case sw_command:
  551.             if (ch == ' ' || ch == CR || ch == LF) {
  552.                 c = s->cmd_start;

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

  554.                     c0 = ngx_toupper(c[0]);
  555.                     c1 = ngx_toupper(c[1]);
  556.                     c2 = ngx_toupper(c[2]);
  557.                     c3 = ngx_toupper(c[3]);

  558.                     if (c0 == 'H' && c1 == 'E' && c2 == 'L' && c3 == 'O')
  559.                     {
  560.                         s->command = NGX_SMTP_HELO;

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

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

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

  570.                     } else if (c0 == 'N' && c1 == 'O' && c2 == 'O' && c3 == 'P')
  571.                     {
  572.                         s->command = NGX_SMTP_NOOP;

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

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

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

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

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

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

  591.                     } else {
  592.                         goto invalid;
  593.                     }
  594. #if (NGX_MAIL_SSL)
  595.                 } else if (p - c == 8) {

  596.                     if ((c[0] == 'S'|| c[0] == 's')
  597.                         && (c[1] == 'T'|| c[1] == 't')
  598.                         && (c[2] == 'A'|| c[2] == 'a')
  599.                         && (c[3] == 'R'|| c[3] == 'r')
  600.                         && (c[4] == 'T'|| c[4] == 't')
  601.                         && (c[5] == 'T'|| c[5] == 't')
  602.                         && (c[6] == 'L'|| c[6] == 'l')
  603.                         && (c[7] == 'S'|| c[7] == 's'))
  604.                     {
  605.                         s->command = NGX_SMTP_STARTTLS;

  606.                     } else {
  607.                         goto invalid;
  608.                     }
  609. #endif
  610.                 } else {
  611.                     goto invalid;
  612.                 }

  613.                 s->cmd.data = s->cmd_start;
  614.                 s->cmd.len = p - s->cmd_start;

  615.                 switch (ch) {
  616.                 case ' ':
  617.                     state = sw_spaces_before_argument;
  618.                     break;
  619.                 case CR:
  620.                     state = sw_almost_done;
  621.                     break;
  622.                 case LF:
  623.                     goto done;
  624.                 }
  625.                 break;
  626.             }

  627.             if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {
  628.                 goto invalid;
  629.             }

  630.             break;

  631.         case sw_invalid:
  632.             goto invalid;

  633.         case sw_spaces_before_argument:
  634.             switch (ch) {
  635.             case ' ':
  636.                 break;
  637.             case CR:
  638.                 state = sw_almost_done;
  639.                 s->arg_end = p;
  640.                 break;
  641.             case LF:
  642.                 s->arg_end = p;
  643.                 goto done;
  644.             default:
  645.                 if (s->args.nelts <= 10) {
  646.                     state = sw_argument;
  647.                     s->arg_start = p;
  648.                     break;
  649.                 }
  650.                 goto invalid;
  651.             }
  652.             break;

  653.         case sw_argument:
  654.             switch (ch) {
  655.             case ' ':
  656.             case CR:
  657.             case LF:
  658.                 arg = ngx_array_push(&s->args);
  659.                 if (arg == NULL) {
  660.                     return NGX_ERROR;
  661.                 }
  662.                 arg->len = p - s->arg_start;
  663.                 arg->data = s->arg_start;
  664.                 s->arg_start = NULL;

  665.                 switch (ch) {
  666.                 case ' ':
  667.                     state = sw_spaces_before_argument;
  668.                     break;
  669.                 case CR:
  670.                     state = sw_almost_done;
  671.                     break;
  672.                 case LF:
  673.                     goto done;
  674.                 }
  675.                 break;

  676.             default:
  677.                 break;
  678.             }
  679.             break;

  680.         case sw_almost_done:
  681.             switch (ch) {
  682.             case LF:
  683.                 goto done;
  684.             default:
  685.                 goto invalid;
  686.             }
  687.         }
  688.     }

  689.     s->buffer->pos = p;
  690.     s->state = state;

  691.     return NGX_AGAIN;

  692. done:

  693.     s->buffer->pos = p + 1;

  694.     if (s->arg_start) {
  695.         arg = ngx_array_push(&s->args);
  696.         if (arg == NULL) {
  697.             return NGX_ERROR;
  698.         }
  699.         arg->len = s->arg_end - s->arg_start;
  700.         arg->data = s->arg_start;
  701.         s->arg_start = NULL;
  702.     }

  703.     s->state = (s->command != NGX_SMTP_AUTH) ? sw_start : sw_argument;

  704.     return NGX_OK;

  705. invalid:

  706.     s->state = sw_invalid;
  707.     s->arg_start = NULL;

  708.     /* skip invalid command till LF */

  709.     for (p = s->buffer->pos; p < s->buffer->last; p++) {
  710.         if (*p == LF) {
  711.             s->state = sw_start;
  712.             p++;
  713.             break;
  714.         }
  715.     }

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

  717.     return NGX_MAIL_PARSE_INVALID_COMMAND;
  718. }


  719. ngx_int_t
  720. ngx_mail_auth_parse(ngx_mail_session_t *s, ngx_connection_t *c)
  721. {
  722.     ngx_str_t                 *arg;

  723. #if (NGX_MAIL_SSL)
  724.     if (ngx_mail_starttls_only(s, c)) {
  725.         return NGX_MAIL_PARSE_INVALID_COMMAND;
  726.     }
  727. #endif

  728.     if (s->args.nelts == 0) {
  729.         return NGX_MAIL_PARSE_INVALID_COMMAND;
  730.     }

  731.     arg = s->args.elts;

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

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

  734.             if (s->args.nelts == 1) {
  735.                 return NGX_MAIL_AUTH_LOGIN;
  736.             }

  737.             if (s->args.nelts == 2) {
  738.                 return NGX_MAIL_AUTH_LOGIN_USERNAME;
  739.             }

  740.             return NGX_MAIL_PARSE_INVALID_COMMAND;
  741.         }

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

  743.             if (s->args.nelts == 1) {
  744.                 return NGX_MAIL_AUTH_PLAIN;
  745.             }

  746.             if (s->args.nelts == 2) {
  747.                 return ngx_mail_auth_plain(s, c, 1);
  748.             }
  749.         }

  750.         return NGX_MAIL_PARSE_INVALID_COMMAND;
  751.     }

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

  753.         if (s->args.nelts != 1) {
  754.             return NGX_MAIL_PARSE_INVALID_COMMAND;
  755.         }

  756.         if (ngx_strncasecmp(arg[0].data, (u_char *) "CRAM-MD5", 8) == 0) {
  757.             return NGX_MAIL_AUTH_CRAM_MD5;
  758.         }
  759.     }

  760.     return NGX_MAIL_PARSE_INVALID_COMMAND;
  761. }