Back to home page

Nginx displayed by LXR

Source navigation ]
Diff markup ]
Identifier search ]
general search ]
 
 
Version: nginx-1.15.12 ]​[ nginx-1.16.0 ]​

0001 
0002 /*
0003  * Copyright (C) Igor Sysoev
0004  * Copyright (C) Nginx, Inc.
0005  */
0006 
0007 
0008 #include <ngx_config.h>
0009 #include <ngx_core.h>
0010 #include <ngx_event.h>
0011 #include <ngx_mail.h>
0012 #include <ngx_mail_pop3_module.h>
0013 #include <ngx_mail_imap_module.h>
0014 #include <ngx_mail_smtp_module.h>
0015 
0016 
0017 ngx_int_t
0018 ngx_mail_pop3_parse_command(ngx_mail_session_t *s)
0019 {
0020     u_char      ch, *p, *c, c0, c1, c2, c3;
0021     ngx_str_t  *arg;
0022     enum {
0023         sw_start = 0,
0024         sw_spaces_before_argument,
0025         sw_argument,
0026         sw_almost_done
0027     } state;
0028 
0029     state = s->state;
0030 
0031     for (p = s->buffer->pos; p < s->buffer->last; p++) {
0032         ch = *p;
0033 
0034         switch (state) {
0035 
0036         /* POP3 command */
0037         case sw_start:
0038             if (ch == ' ' || ch == CR || ch == LF) {
0039                 c = s->buffer->start;
0040 
0041                 if (p - c == 4) {
0042 
0043                     c0 = ngx_toupper(c[0]);
0044                     c1 = ngx_toupper(c[1]);
0045                     c2 = ngx_toupper(c[2]);
0046                     c3 = ngx_toupper(c[3]);
0047 
0048                     if (c0 == 'U' && c1 == 'S' && c2 == 'E' && c3 == 'R')
0049                     {
0050                         s->command = NGX_POP3_USER;
0051 
0052                     } else if (c0 == 'P' && c1 == 'A' && c2 == 'S' && c3 == 'S')
0053                     {
0054                         s->command = NGX_POP3_PASS;
0055 
0056                     } else if (c0 == 'A' && c1 == 'P' && c2 == 'O' && c3 == 'P')
0057                     {
0058                         s->command = NGX_POP3_APOP;
0059 
0060                     } else if (c0 == 'Q' && c1 == 'U' && c2 == 'I' && c3 == 'T')
0061                     {
0062                         s->command = NGX_POP3_QUIT;
0063 
0064                     } else if (c0 == 'C' && c1 == 'A' && c2 == 'P' && c3 == 'A')
0065                     {
0066                         s->command = NGX_POP3_CAPA;
0067 
0068                     } else if (c0 == 'A' && c1 == 'U' && c2 == 'T' && c3 == 'H')
0069                     {
0070                         s->command = NGX_POP3_AUTH;
0071 
0072                     } else if (c0 == 'N' && c1 == 'O' && c2 == 'O' && c3 == 'P')
0073                     {
0074                         s->command = NGX_POP3_NOOP;
0075 #if (NGX_MAIL_SSL)
0076                     } else if (c0 == 'S' && c1 == 'T' && c2 == 'L' && c3 == 'S')
0077                     {
0078                         s->command = NGX_POP3_STLS;
0079 #endif
0080                     } else {
0081                         goto invalid;
0082                     }
0083 
0084                 } else {
0085                     goto invalid;
0086                 }
0087 
0088                 switch (ch) {
0089                 case ' ':
0090                     state = sw_spaces_before_argument;
0091                     break;
0092                 case CR:
0093                     state = sw_almost_done;
0094                     break;
0095                 case LF:
0096                     goto done;
0097                 }
0098                 break;
0099             }
0100 
0101             if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {
0102                 goto invalid;
0103             }
0104 
0105             break;
0106 
0107         case sw_spaces_before_argument:
0108             switch (ch) {
0109             case ' ':
0110                 break;
0111             case CR:
0112                 state = sw_almost_done;
0113                 s->arg_end = p;
0114                 break;
0115             case LF:
0116                 s->arg_end = p;
0117                 goto done;
0118             default:
0119                 if (s->args.nelts <= 2) {
0120                     state = sw_argument;
0121                     s->arg_start = p;
0122                     break;
0123                 }
0124                 goto invalid;
0125             }
0126             break;
0127 
0128         case sw_argument:
0129             switch (ch) {
0130 
0131             case ' ':
0132 
0133                 /*
0134                  * the space should be considered as part of the at username
0135                  * or password, but not of argument in other commands
0136                  */
0137 
0138                 if (s->command == NGX_POP3_USER
0139                     || s->command == NGX_POP3_PASS)
0140                 {
0141                     break;
0142                 }
0143 
0144                 /* fall through */
0145 
0146             case CR:
0147             case LF:
0148                 arg = ngx_array_push(&s->args);
0149                 if (arg == NULL) {
0150                     return NGX_ERROR;
0151                 }
0152                 arg->len = p - s->arg_start;
0153                 arg->data = s->arg_start;
0154                 s->arg_start = NULL;
0155 
0156                 switch (ch) {
0157                 case ' ':
0158                     state = sw_spaces_before_argument;
0159                     break;
0160                 case CR:
0161                     state = sw_almost_done;
0162                     break;
0163                 case LF:
0164                     goto done;
0165                 }
0166                 break;
0167 
0168             default:
0169                 break;
0170             }
0171             break;
0172 
0173         case sw_almost_done:
0174             switch (ch) {
0175             case LF:
0176                 goto done;
0177             default:
0178                 goto invalid;
0179             }
0180         }
0181     }
0182 
0183     s->buffer->pos = p;
0184     s->state = state;
0185 
0186     return NGX_AGAIN;
0187 
0188 done:
0189 
0190     s->buffer->pos = p + 1;
0191 
0192     if (s->arg_start) {
0193         arg = ngx_array_push(&s->args);
0194         if (arg == NULL) {
0195             return NGX_ERROR;
0196         }
0197         arg->len = s->arg_end - s->arg_start;
0198         arg->data = s->arg_start;
0199         s->arg_start = NULL;
0200     }
0201 
0202     s->state = (s->command != NGX_POP3_AUTH) ? sw_start : sw_argument;
0203 
0204     return NGX_OK;
0205 
0206 invalid:
0207 
0208     s->state = sw_start;
0209     s->arg_start = NULL;
0210 
0211     return NGX_MAIL_PARSE_INVALID_COMMAND;
0212 }
0213 
0214 
0215 ngx_int_t
0216 ngx_mail_imap_parse_command(ngx_mail_session_t *s)
0217 {
0218     u_char      ch, *p, *c;
0219     ngx_str_t  *arg;
0220     enum {
0221         sw_start = 0,
0222         sw_spaces_before_command,
0223         sw_command,
0224         sw_spaces_before_argument,
0225         sw_argument,
0226         sw_backslash,
0227         sw_literal,
0228         sw_no_sync_literal_argument,
0229         sw_start_literal_argument,
0230         sw_literal_argument,
0231         sw_end_literal_argument,
0232         sw_almost_done
0233     } state;
0234 
0235     state = s->state;
0236 
0237     for (p = s->buffer->pos; p < s->buffer->last; p++) {
0238         ch = *p;
0239 
0240         switch (state) {
0241 
0242         /* IMAP tag */
0243         case sw_start:
0244             switch (ch) {
0245             case ' ':
0246                 s->tag.len = p - s->buffer->start + 1;
0247                 s->tag.data = s->buffer->start;
0248                 state = sw_spaces_before_command;
0249                 break;
0250             case CR:
0251                 s->state = sw_start;
0252                 return NGX_MAIL_PARSE_INVALID_COMMAND;
0253             case LF:
0254                 s->state = sw_start;
0255                 return NGX_MAIL_PARSE_INVALID_COMMAND;
0256             }
0257             break;
0258 
0259         case sw_spaces_before_command:
0260             switch (ch) {
0261             case ' ':
0262                 break;
0263             case CR:
0264                 s->state = sw_start;
0265                 return NGX_MAIL_PARSE_INVALID_COMMAND;
0266             case LF:
0267                 s->state = sw_start;
0268                 return NGX_MAIL_PARSE_INVALID_COMMAND;
0269             default:
0270                 s->cmd_start = p;
0271                 state = sw_command;
0272                 break;
0273             }
0274             break;
0275 
0276         case sw_command:
0277             if (ch == ' ' || ch == CR || ch == LF) {
0278 
0279                 c = s->cmd_start;
0280 
0281                 switch (p - c) {
0282 
0283                 case 4:
0284                     if ((c[0] == 'N' || c[0] == 'n')
0285                         && (c[1] == 'O'|| c[1] == 'o')
0286                         && (c[2] == 'O'|| c[2] == 'o')
0287                         && (c[3] == 'P'|| c[3] == 'p'))
0288                     {
0289                         s->command = NGX_IMAP_NOOP;
0290 
0291                     } else {
0292                         goto invalid;
0293                     }
0294                     break;
0295 
0296                 case 5:
0297                     if ((c[0] == 'L'|| c[0] == 'l')
0298                         && (c[1] == 'O'|| c[1] == 'o')
0299                         && (c[2] == 'G'|| c[2] == 'g')
0300                         && (c[3] == 'I'|| c[3] == 'i')
0301                         && (c[4] == 'N'|| c[4] == 'n'))
0302                     {
0303                         s->command = NGX_IMAP_LOGIN;
0304 
0305                     } else {
0306                         goto invalid;
0307                     }
0308                     break;
0309 
0310                 case 6:
0311                     if ((c[0] == 'L'|| c[0] == 'l')
0312                         && (c[1] == 'O'|| c[1] == 'o')
0313                         && (c[2] == 'G'|| c[2] == 'g')
0314                         && (c[3] == 'O'|| c[3] == 'o')
0315                         && (c[4] == 'U'|| c[4] == 'u')
0316                         && (c[5] == 'T'|| c[5] == 't'))
0317                     {
0318                         s->command = NGX_IMAP_LOGOUT;
0319 
0320                     } else {
0321                         goto invalid;
0322                     }
0323                     break;
0324 
0325 #if (NGX_MAIL_SSL)
0326                 case 8:
0327                     if ((c[0] == 'S'|| c[0] == 's')
0328                         && (c[1] == 'T'|| c[1] == 't')
0329                         && (c[2] == 'A'|| c[2] == 'a')
0330                         && (c[3] == 'R'|| c[3] == 'r')
0331                         && (c[4] == 'T'|| c[4] == 't')
0332                         && (c[5] == 'T'|| c[5] == 't')
0333                         && (c[6] == 'L'|| c[6] == 'l')
0334                         && (c[7] == 'S'|| c[7] == 's'))
0335                     {
0336                         s->command = NGX_IMAP_STARTTLS;
0337 
0338                     } else {
0339                         goto invalid;
0340                     }
0341                     break;
0342 #endif
0343 
0344                 case 10:
0345                     if ((c[0] == 'C'|| c[0] == 'c')
0346                         && (c[1] == 'A'|| c[1] == 'a')
0347                         && (c[2] == 'P'|| c[2] == 'p')
0348                         && (c[3] == 'A'|| c[3] == 'a')
0349                         && (c[4] == 'B'|| c[4] == 'b')
0350                         && (c[5] == 'I'|| c[5] == 'i')
0351                         && (c[6] == 'L'|| c[6] == 'l')
0352                         && (c[7] == 'I'|| c[7] == 'i')
0353                         && (c[8] == 'T'|| c[8] == 't')
0354                         && (c[9] == 'Y'|| c[9] == 'y'))
0355                     {
0356                         s->command = NGX_IMAP_CAPABILITY;
0357 
0358                     } else {
0359                         goto invalid;
0360                     }
0361                     break;
0362 
0363                 case 12:
0364                     if ((c[0] == 'A'|| c[0] == 'a')
0365                         && (c[1] == 'U'|| c[1] == 'u')
0366                         && (c[2] == 'T'|| c[2] == 't')
0367                         && (c[3] == 'H'|| c[3] == 'h')
0368                         && (c[4] == 'E'|| c[4] == 'e')
0369                         && (c[5] == 'N'|| c[5] == 'n')
0370                         && (c[6] == 'T'|| c[6] == 't')
0371                         && (c[7] == 'I'|| c[7] == 'i')
0372                         && (c[8] == 'C'|| c[8] == 'c')
0373                         && (c[9] == 'A'|| c[9] == 'a')
0374                         && (c[10] == 'T'|| c[10] == 't')
0375                         && (c[11] == 'E'|| c[11] == 'e'))
0376                     {
0377                         s->command = NGX_IMAP_AUTHENTICATE;
0378 
0379                     } else {
0380                         goto invalid;
0381                     }
0382                     break;
0383 
0384                 default:
0385                     goto invalid;
0386                 }
0387 
0388                 switch (ch) {
0389                 case ' ':
0390                     state = sw_spaces_before_argument;
0391                     break;
0392                 case CR:
0393                     state = sw_almost_done;
0394                     break;
0395                 case LF:
0396                     goto done;
0397                 }
0398                 break;
0399             }
0400 
0401             if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {
0402                 goto invalid;
0403             }
0404 
0405             break;
0406 
0407         case sw_spaces_before_argument:
0408             switch (ch) {
0409             case ' ':
0410                 break;
0411             case CR:
0412                 state = sw_almost_done;
0413                 s->arg_end = p;
0414                 break;
0415             case LF:
0416                 s->arg_end = p;
0417                 goto done;
0418             case '"':
0419                 if (s->args.nelts <= 2) {
0420                     s->quoted = 1;
0421                     s->arg_start = p + 1;
0422                     state = sw_argument;
0423                     break;
0424                 }
0425                 goto invalid;
0426             case '{':
0427                 if (s->args.nelts <= 2) {
0428                     state = sw_literal;
0429                     break;
0430                 }
0431                 goto invalid;
0432             default:
0433                 if (s->args.nelts <= 2) {
0434                     s->arg_start = p;
0435                     state = sw_argument;
0436                     break;
0437                 }
0438                 goto invalid;
0439             }
0440             break;
0441 
0442         case sw_argument:
0443             if (ch == ' ' && s->quoted) {
0444                 break;
0445             }
0446 
0447             switch (ch) {
0448             case '"':
0449                 if (!s->quoted) {
0450                     break;
0451                 }
0452                 s->quoted = 0;
0453                 /* fall through */
0454             case ' ':
0455             case CR:
0456             case LF:
0457                 arg = ngx_array_push(&s->args);
0458                 if (arg == NULL) {
0459                     return NGX_ERROR;
0460                 }
0461                 arg->len = p - s->arg_start;
0462                 arg->data = s->arg_start;
0463                 s->arg_start = NULL;
0464 
0465                 switch (ch) {
0466                 case '"':
0467                 case ' ':
0468                     state = sw_spaces_before_argument;
0469                     break;
0470                 case CR:
0471                     state = sw_almost_done;
0472                     break;
0473                 case LF:
0474                     goto done;
0475                 }
0476                 break;
0477             case '\\':
0478                 if (s->quoted) {
0479                     s->backslash = 1;
0480                     state = sw_backslash;
0481                 }
0482                 break;
0483             }
0484             break;
0485 
0486         case sw_backslash:
0487             switch (ch) {
0488             case CR:
0489             case LF:
0490                 goto invalid;
0491             default:
0492                 state = sw_argument;
0493             }
0494             break;
0495 
0496         case sw_literal:
0497             if (ch >= '0' && ch <= '9') {
0498                 s->literal_len = s->literal_len * 10 + (ch - '0');
0499                 break;
0500             }
0501             if (ch == '}') {
0502                 state = sw_start_literal_argument;
0503                 break;
0504             }
0505             if (ch == '+') {
0506                 state = sw_no_sync_literal_argument;
0507                 break;
0508             }
0509             goto invalid;
0510 
0511         case sw_no_sync_literal_argument:
0512             if (ch == '}') {
0513                 s->no_sync_literal = 1;
0514                 state = sw_start_literal_argument;
0515                 break;
0516             }
0517             goto invalid;
0518 
0519         case sw_start_literal_argument:
0520             switch (ch) {
0521             case CR:
0522                 break;
0523             case LF:
0524                 s->buffer->pos = p + 1;
0525                 s->arg_start = p + 1;
0526                 if (s->no_sync_literal == 0) {
0527                     s->state = sw_literal_argument;
0528                     return NGX_IMAP_NEXT;
0529                 }
0530                 state = sw_literal_argument;
0531                 s->no_sync_literal = 0;
0532                 break;
0533             default:
0534                 goto invalid;
0535             }
0536             break;
0537 
0538         case sw_literal_argument:
0539             if (s->literal_len && --s->literal_len) {
0540                 break;
0541             }
0542 
0543             arg = ngx_array_push(&s->args);
0544             if (arg == NULL) {
0545                 return NGX_ERROR;
0546             }
0547             arg->len = p + 1 - s->arg_start;
0548             arg->data = s->arg_start;
0549             s->arg_start = NULL;
0550             state = sw_end_literal_argument;
0551 
0552             break;
0553 
0554         case sw_end_literal_argument:
0555             switch (ch) {
0556             case '{':
0557                 if (s->args.nelts <= 2) {
0558                     state = sw_literal;
0559                     break;
0560                 }
0561                 goto invalid;
0562             case CR:
0563                 state = sw_almost_done;
0564                 break;
0565             case LF:
0566                 goto done;
0567             default:
0568                 state = sw_spaces_before_argument;
0569                 break;
0570             }
0571             break;
0572 
0573         case sw_almost_done:
0574             switch (ch) {
0575             case LF:
0576                 goto done;
0577             default:
0578                 goto invalid;
0579             }
0580         }
0581     }
0582 
0583     s->buffer->pos = p;
0584     s->state = state;
0585 
0586     return NGX_AGAIN;
0587 
0588 done:
0589 
0590     s->buffer->pos = p + 1;
0591 
0592     if (s->arg_start) {
0593         arg = ngx_array_push(&s->args);
0594         if (arg == NULL) {
0595             return NGX_ERROR;
0596         }
0597         arg->len = s->arg_end - s->arg_start;
0598         arg->data = s->arg_start;
0599 
0600         s->arg_start = NULL;
0601         s->cmd_start = NULL;
0602         s->quoted = 0;
0603         s->no_sync_literal = 0;
0604         s->literal_len = 0;
0605     }
0606 
0607     s->state = (s->command != NGX_IMAP_AUTHENTICATE) ? sw_start : sw_argument;
0608 
0609     return NGX_OK;
0610 
0611 invalid:
0612 
0613     s->state = sw_start;
0614     s->quoted = 0;
0615     s->no_sync_literal = 0;
0616     s->literal_len = 0;
0617 
0618     return NGX_MAIL_PARSE_INVALID_COMMAND;
0619 }
0620 
0621 
0622 ngx_int_t
0623 ngx_mail_smtp_parse_command(ngx_mail_session_t *s)
0624 {
0625     u_char      ch, *p, *c, c0, c1, c2, c3;
0626     ngx_str_t  *arg;
0627     enum {
0628         sw_start = 0,
0629         sw_command,
0630         sw_invalid,
0631         sw_spaces_before_argument,
0632         sw_argument,
0633         sw_almost_done
0634     } state;
0635 
0636     state = s->state;
0637 
0638     for (p = s->buffer->pos; p < s->buffer->last; p++) {
0639         ch = *p;
0640 
0641         switch (state) {
0642 
0643         /* SMTP command */
0644         case sw_start:
0645             s->cmd_start = p;
0646             state = sw_command;
0647 
0648             /* fall through */
0649 
0650         case sw_command:
0651             if (ch == ' ' || ch == CR || ch == LF) {
0652                 c = s->cmd_start;
0653 
0654                 if (p - c == 4) {
0655 
0656                     c0 = ngx_toupper(c[0]);
0657                     c1 = ngx_toupper(c[1]);
0658                     c2 = ngx_toupper(c[2]);
0659                     c3 = ngx_toupper(c[3]);
0660 
0661                     if (c0 == 'H' && c1 == 'E' && c2 == 'L' && c3 == 'O')
0662                     {
0663                         s->command = NGX_SMTP_HELO;
0664 
0665                     } else if (c0 == 'E' && c1 == 'H' && c2 == 'L' && c3 == 'O')
0666                     {
0667                         s->command = NGX_SMTP_EHLO;
0668 
0669                     } else if (c0 == 'Q' && c1 == 'U' && c2 == 'I' && c3 == 'T')
0670                     {
0671                         s->command = NGX_SMTP_QUIT;
0672 
0673                     } else if (c0 == 'A' && c1 == 'U' && c2 == 'T' && c3 == 'H')
0674                     {
0675                         s->command = NGX_SMTP_AUTH;
0676 
0677                     } else if (c0 == 'N' && c1 == 'O' && c2 == 'O' && c3 == 'P')
0678                     {
0679                         s->command = NGX_SMTP_NOOP;
0680 
0681                     } else if (c0 == 'M' && c1 == 'A' && c2 == 'I' && c3 == 'L')
0682                     {
0683                         s->command = NGX_SMTP_MAIL;
0684 
0685                     } else if (c0 == 'R' && c1 == 'S' && c2 == 'E' && c3 == 'T')
0686                     {
0687                         s->command = NGX_SMTP_RSET;
0688 
0689                     } else if (c0 == 'R' && c1 == 'C' && c2 == 'P' && c3 == 'T')
0690                     {
0691                         s->command = NGX_SMTP_RCPT;
0692 
0693                     } else if (c0 == 'V' && c1 == 'R' && c2 == 'F' && c3 == 'Y')
0694                     {
0695                         s->command = NGX_SMTP_VRFY;
0696 
0697                     } else if (c0 == 'E' && c1 == 'X' && c2 == 'P' && c3 == 'N')
0698                     {
0699                         s->command = NGX_SMTP_EXPN;
0700 
0701                     } else if (c0 == 'H' && c1 == 'E' && c2 == 'L' && c3 == 'P')
0702                     {
0703                         s->command = NGX_SMTP_HELP;
0704 
0705                     } else {
0706                         goto invalid;
0707                     }
0708 #if (NGX_MAIL_SSL)
0709                 } else if (p - c == 8) {
0710 
0711                     if ((c[0] == 'S'|| c[0] == 's')
0712                         && (c[1] == 'T'|| c[1] == 't')
0713                         && (c[2] == 'A'|| c[2] == 'a')
0714                         && (c[3] == 'R'|| c[3] == 'r')
0715                         && (c[4] == 'T'|| c[4] == 't')
0716                         && (c[5] == 'T'|| c[5] == 't')
0717                         && (c[6] == 'L'|| c[6] == 'l')
0718                         && (c[7] == 'S'|| c[7] == 's'))
0719                     {
0720                         s->command = NGX_SMTP_STARTTLS;
0721 
0722                     } else {
0723                         goto invalid;
0724                     }
0725 #endif
0726                 } else {
0727                     goto invalid;
0728                 }
0729 
0730                 s->cmd.data = s->cmd_start;
0731                 s->cmd.len = p - s->cmd_start;
0732 
0733                 switch (ch) {
0734                 case ' ':
0735                     state = sw_spaces_before_argument;
0736                     break;
0737                 case CR:
0738                     state = sw_almost_done;
0739                     break;
0740                 case LF:
0741                     goto done;
0742                 }
0743                 break;
0744             }
0745 
0746             if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {
0747                 goto invalid;
0748             }
0749 
0750             break;
0751 
0752         case sw_invalid:
0753             goto invalid;
0754 
0755         case sw_spaces_before_argument:
0756             switch (ch) {
0757             case ' ':
0758                 break;
0759             case CR:
0760                 state = sw_almost_done;
0761                 s->arg_end = p;
0762                 break;
0763             case LF:
0764                 s->arg_end = p;
0765                 goto done;
0766             default:
0767                 if (s->args.nelts <= 10) {
0768                     state = sw_argument;
0769                     s->arg_start = p;
0770                     break;
0771                 }
0772                 goto invalid;
0773             }
0774             break;
0775 
0776         case sw_argument:
0777             switch (ch) {
0778             case ' ':
0779             case CR:
0780             case LF:
0781                 arg = ngx_array_push(&s->args);
0782                 if (arg == NULL) {
0783                     return NGX_ERROR;
0784                 }
0785                 arg->len = p - s->arg_start;
0786                 arg->data = s->arg_start;
0787                 s->arg_start = NULL;
0788 
0789                 switch (ch) {
0790                 case ' ':
0791                     state = sw_spaces_before_argument;
0792                     break;
0793                 case CR:
0794                     state = sw_almost_done;
0795                     break;
0796                 case LF:
0797                     goto done;
0798                 }
0799                 break;
0800 
0801             default:
0802                 break;
0803             }
0804             break;
0805 
0806         case sw_almost_done:
0807             switch (ch) {
0808             case LF:
0809                 goto done;
0810             default:
0811                 goto invalid;
0812             }
0813         }
0814     }
0815 
0816     s->buffer->pos = p;
0817     s->state = state;
0818 
0819     return NGX_AGAIN;
0820 
0821 done:
0822 
0823     s->buffer->pos = p + 1;
0824 
0825     if (s->arg_start) {
0826         arg = ngx_array_push(&s->args);
0827         if (arg == NULL) {
0828             return NGX_ERROR;
0829         }
0830         arg->len = s->arg_end - s->arg_start;
0831         arg->data = s->arg_start;
0832         s->arg_start = NULL;
0833     }
0834 
0835     s->state = (s->command != NGX_SMTP_AUTH) ? sw_start : sw_argument;
0836 
0837     return NGX_OK;
0838 
0839 invalid:
0840 
0841     s->state = sw_invalid;
0842     s->arg_start = NULL;
0843 
0844     /* skip invalid command till LF */
0845 
0846     for (p = s->buffer->pos; p < s->buffer->last; p++) {
0847         if (*p == LF) {
0848             s->state = sw_start;
0849             p++;
0850             break;
0851         }
0852     }
0853 
0854     s->buffer->pos = p;
0855 
0856     return NGX_MAIL_PARSE_INVALID_COMMAND;
0857 }
0858 
0859 
0860 ngx_int_t
0861 ngx_mail_auth_parse(ngx_mail_session_t *s, ngx_connection_t *c)
0862 {
0863     ngx_str_t                 *arg;
0864 
0865 #if (NGX_MAIL_SSL)
0866     if (ngx_mail_starttls_only(s, c)) {
0867         return NGX_MAIL_PARSE_INVALID_COMMAND;
0868     }
0869 #endif
0870 
0871     if (s->args.nelts == 0) {
0872         return NGX_MAIL_PARSE_INVALID_COMMAND;
0873     }
0874 
0875     arg = s->args.elts;
0876 
0877     if (arg[0].len == 5) {
0878 
0879         if (ngx_strncasecmp(arg[0].data, (u_char *) "LOGIN", 5) == 0) {
0880 
0881             if (s->args.nelts == 1) {
0882                 return NGX_MAIL_AUTH_LOGIN;
0883             }
0884 
0885             if (s->args.nelts == 2) {
0886                 return NGX_MAIL_AUTH_LOGIN_USERNAME;
0887             }
0888 
0889             return NGX_MAIL_PARSE_INVALID_COMMAND;
0890         }
0891 
0892         if (ngx_strncasecmp(arg[0].data, (u_char *) "PLAIN", 5) == 0) {
0893 
0894             if (s->args.nelts == 1) {
0895                 return NGX_MAIL_AUTH_PLAIN;
0896             }
0897 
0898             if (s->args.nelts == 2) {
0899                 return ngx_mail_auth_plain(s, c, 1);
0900             }
0901         }
0902 
0903         return NGX_MAIL_PARSE_INVALID_COMMAND;
0904     }
0905 
0906     if (arg[0].len == 8) {
0907 
0908         if (ngx_strncasecmp(arg[0].data, (u_char *) "CRAM-MD5", 8) == 0) {
0909 
0910             if (s->args.nelts != 1) {
0911                 return NGX_MAIL_PARSE_INVALID_COMMAND;
0912             }
0913 
0914             return NGX_MAIL_AUTH_CRAM_MD5;
0915         }
0916 
0917         if (ngx_strncasecmp(arg[0].data, (u_char *) "EXTERNAL", 8) == 0) {
0918 
0919             if (s->args.nelts == 1) {
0920                 return NGX_MAIL_AUTH_EXTERNAL;
0921             }
0922 
0923             if (s->args.nelts == 2) {
0924                 return ngx_mail_auth_external(s, c, 1);
0925             }
0926         }
0927 
0928         return NGX_MAIL_PARSE_INVALID_COMMAND;
0929     }
0930 
0931     return NGX_MAIL_PARSE_INVALID_COMMAND;
0932 }