1 
2 /*
3  * Copyright (C) Igor Sysoev
4  * Copyright (C) Nginx, Inc.
5  */
6 
7 
8 #include <ngx_config.h>
9 #include <ngx_core.h>
10 #include <ngx_event.h>
11 #include <ngx_mail.h>
12 #include <ngx_mail_pop3_module.h>
13 #include <ngx_mail_imap_module.h>
14 #include <ngx_mail_smtp_module.h>
15 
16 
17 ngx_int_t
ngx_mail_pop3_parse_command(ngx_mail_session_t * s)18 ngx_mail_pop3_parse_command(ngx_mail_session_t *s)
19 {
20     u_char      ch, *p, *c, c0, c1, c2, c3;
21     ngx_str_t  *arg;
22     enum {
23         sw_start = 0,
24         sw_spaces_before_argument,
25         sw_argument,
26         sw_almost_done
27     } state;
28 
29     state = s->state;
30 
31     for (p = s->buffer->pos; p < s->buffer->last; p++) {
32         ch = *p;
33 
34         switch (state) {
35 
36         /* POP3 command */
37         case sw_start:
38             if (ch == ' ' || ch == CR || ch == LF) {
39                 c = s->buffer->start;
40 
41                 if (p - c == 4) {
42 
43                     c0 = ngx_toupper(c[0]);
44                     c1 = ngx_toupper(c[1]);
45                     c2 = ngx_toupper(c[2]);
46                     c3 = ngx_toupper(c[3]);
47 
48                     if (c0 == 'U' && c1 == 'S' && c2 == 'E' && c3 == 'R')
49                     {
50                         s->command = NGX_POP3_USER;
51 
52                     } else if (c0 == 'P' && c1 == 'A' && c2 == 'S' && c3 == 'S')
53                     {
54                         s->command = NGX_POP3_PASS;
55 
56                     } else if (c0 == 'A' && c1 == 'P' && c2 == 'O' && c3 == 'P')
57                     {
58                         s->command = NGX_POP3_APOP;
59 
60                     } else if (c0 == 'Q' && c1 == 'U' && c2 == 'I' && c3 == 'T')
61                     {
62                         s->command = NGX_POP3_QUIT;
63 
64                     } else if (c0 == 'C' && c1 == 'A' && c2 == 'P' && c3 == 'A')
65                     {
66                         s->command = NGX_POP3_CAPA;
67 
68                     } else if (c0 == 'A' && c1 == 'U' && c2 == 'T' && c3 == 'H')
69                     {
70                         s->command = NGX_POP3_AUTH;
71 
72                     } else if (c0 == 'N' && c1 == 'O' && c2 == 'O' && c3 == 'P')
73                     {
74                         s->command = NGX_POP3_NOOP;
75 #if (NGX_MAIL_SSL)
76                     } else if (c0 == 'S' && c1 == 'T' && c2 == 'L' && c3 == 'S')
77                     {
78                         s->command = NGX_POP3_STLS;
79 #endif
80                     } else {
81                         goto invalid;
82                     }
83 
84                 } else {
85                     goto invalid;
86                 }
87 
88                 switch (ch) {
89                 case ' ':
90                     state = sw_spaces_before_argument;
91                     break;
92                 case CR:
93                     state = sw_almost_done;
94                     break;
95                 case LF:
96                     goto done;
97                 }
98                 break;
99             }
100 
101             if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {
102                 goto invalid;
103             }
104 
105             break;
106 
107         case sw_spaces_before_argument:
108             switch (ch) {
109             case ' ':
110                 break;
111             case CR:
112                 state = sw_almost_done;
113                 s->arg_end = p;
114                 break;
115             case LF:
116                 s->arg_end = p;
117                 goto done;
118             default:
119                 if (s->args.nelts <= 2) {
120                     state = sw_argument;
121                     s->arg_start = p;
122                     break;
123                 }
124                 goto invalid;
125             }
126             break;
127 
128         case sw_argument:
129             switch (ch) {
130 
131             case ' ':
132 
133                 /*
134                  * the space should be considered as part of the at username
135                  * or password, but not of argument in other commands
136                  */
137 
138                 if (s->command == NGX_POP3_USER
139                     || s->command == NGX_POP3_PASS)
140                 {
141                     break;
142                 }
143 
144                 /* fall through */
145 
146             case CR:
147             case LF:
148                 arg = ngx_array_push(&s->args);
149                 if (arg == NULL) {
150                     return NGX_ERROR;
151                 }
152                 arg->len = p - s->arg_start;
153                 arg->data = s->arg_start;
154                 s->arg_start = NULL;
155 
156                 switch (ch) {
157                 case ' ':
158                     state = sw_spaces_before_argument;
159                     break;
160                 case CR:
161                     state = sw_almost_done;
162                     break;
163                 case LF:
164                     goto done;
165                 }
166                 break;
167 
168             default:
169                 break;
170             }
171             break;
172 
173         case sw_almost_done:
174             switch (ch) {
175             case LF:
176                 goto done;
177             default:
178                 goto invalid;
179             }
180         }
181     }
182 
183     s->buffer->pos = p;
184     s->state = state;
185 
186     return NGX_AGAIN;
187 
188 done:
189 
190     s->buffer->pos = p + 1;
191 
192     if (s->arg_start) {
193         arg = ngx_array_push(&s->args);
194         if (arg == NULL) {
195             return NGX_ERROR;
196         }
197         arg->len = s->arg_end - s->arg_start;
198         arg->data = s->arg_start;
199         s->arg_start = NULL;
200     }
201 
202     s->state = (s->command != NGX_POP3_AUTH) ? sw_start : sw_argument;
203 
204     return NGX_OK;
205 
206 invalid:
207 
208     s->state = sw_start;
209     s->arg_start = NULL;
210 
211     return NGX_MAIL_PARSE_INVALID_COMMAND;
212 }
213 
214 
215 ngx_int_t
ngx_mail_imap_parse_command(ngx_mail_session_t * s)216 ngx_mail_imap_parse_command(ngx_mail_session_t *s)
217 {
218     u_char      ch, *p, *c;
219     ngx_str_t  *arg;
220     enum {
221         sw_start = 0,
222         sw_spaces_before_command,
223         sw_command,
224         sw_spaces_before_argument,
225         sw_argument,
226         sw_backslash,
227         sw_literal,
228         sw_no_sync_literal_argument,
229         sw_start_literal_argument,
230         sw_literal_argument,
231         sw_end_literal_argument,
232         sw_almost_done
233     } state;
234 
235     state = s->state;
236 
237     for (p = s->buffer->pos; p < s->buffer->last; p++) {
238         ch = *p;
239 
240         switch (state) {
241 
242         /* IMAP tag */
243         case sw_start:
244             switch (ch) {
245             case ' ':
246                 s->tag.len = p - s->buffer->start + 1;
247                 s->tag.data = s->buffer->start;
248                 state = sw_spaces_before_command;
249                 break;
250             case CR:
251                 s->state = sw_start;
252                 return NGX_MAIL_PARSE_INVALID_COMMAND;
253             case LF:
254                 s->state = sw_start;
255                 return NGX_MAIL_PARSE_INVALID_COMMAND;
256             }
257             break;
258 
259         case sw_spaces_before_command:
260             switch (ch) {
261             case ' ':
262                 break;
263             case CR:
264                 s->state = sw_start;
265                 return NGX_MAIL_PARSE_INVALID_COMMAND;
266             case LF:
267                 s->state = sw_start;
268                 return NGX_MAIL_PARSE_INVALID_COMMAND;
269             default:
270                 s->cmd_start = p;
271                 state = sw_command;
272                 break;
273             }
274             break;
275 
276         case sw_command:
277             if (ch == ' ' || ch == CR || ch == LF) {
278 
279                 c = s->cmd_start;
280 
281                 switch (p - c) {
282 
283                 case 4:
284                     if ((c[0] == 'N' || c[0] == 'n')
285                         && (c[1] == 'O'|| c[1] == 'o')
286                         && (c[2] == 'O'|| c[2] == 'o')
287                         && (c[3] == 'P'|| c[3] == 'p'))
288                     {
289                         s->command = NGX_IMAP_NOOP;
290 
291                     } else {
292                         goto invalid;
293                     }
294                     break;
295 
296                 case 5:
297                     if ((c[0] == 'L'|| c[0] == 'l')
298                         && (c[1] == 'O'|| c[1] == 'o')
299                         && (c[2] == 'G'|| c[2] == 'g')
300                         && (c[3] == 'I'|| c[3] == 'i')
301                         && (c[4] == 'N'|| c[4] == 'n'))
302                     {
303                         s->command = NGX_IMAP_LOGIN;
304 
305                     } else {
306                         goto invalid;
307                     }
308                     break;
309 
310                 case 6:
311                     if ((c[0] == 'L'|| c[0] == 'l')
312                         && (c[1] == 'O'|| c[1] == 'o')
313                         && (c[2] == 'G'|| c[2] == 'g')
314                         && (c[3] == 'O'|| c[3] == 'o')
315                         && (c[4] == 'U'|| c[4] == 'u')
316                         && (c[5] == 'T'|| c[5] == 't'))
317                     {
318                         s->command = NGX_IMAP_LOGOUT;
319 
320                     } else {
321                         goto invalid;
322                     }
323                     break;
324 
325 #if (NGX_MAIL_SSL)
326                 case 8:
327                     if ((c[0] == 'S'|| c[0] == 's')
328                         && (c[1] == 'T'|| c[1] == 't')
329                         && (c[2] == 'A'|| c[2] == 'a')
330                         && (c[3] == 'R'|| c[3] == 'r')
331                         && (c[4] == 'T'|| c[4] == 't')
332                         && (c[5] == 'T'|| c[5] == 't')
333                         && (c[6] == 'L'|| c[6] == 'l')
334                         && (c[7] == 'S'|| c[7] == 's'))
335                     {
336                         s->command = NGX_IMAP_STARTTLS;
337 
338                     } else {
339                         goto invalid;
340                     }
341                     break;
342 #endif
343 
344                 case 10:
345                     if ((c[0] == 'C'|| c[0] == 'c')
346                         && (c[1] == 'A'|| c[1] == 'a')
347                         && (c[2] == 'P'|| c[2] == 'p')
348                         && (c[3] == 'A'|| c[3] == 'a')
349                         && (c[4] == 'B'|| c[4] == 'b')
350                         && (c[5] == 'I'|| c[5] == 'i')
351                         && (c[6] == 'L'|| c[6] == 'l')
352                         && (c[7] == 'I'|| c[7] == 'i')
353                         && (c[8] == 'T'|| c[8] == 't')
354                         && (c[9] == 'Y'|| c[9] == 'y'))
355                     {
356                         s->command = NGX_IMAP_CAPABILITY;
357 
358                     } else {
359                         goto invalid;
360                     }
361                     break;
362 
363                 case 12:
364                     if ((c[0] == 'A'|| c[0] == 'a')
365                         && (c[1] == 'U'|| c[1] == 'u')
366                         && (c[2] == 'T'|| c[2] == 't')
367                         && (c[3] == 'H'|| c[3] == 'h')
368                         && (c[4] == 'E'|| c[4] == 'e')
369                         && (c[5] == 'N'|| c[5] == 'n')
370                         && (c[6] == 'T'|| c[6] == 't')
371                         && (c[7] == 'I'|| c[7] == 'i')
372                         && (c[8] == 'C'|| c[8] == 'c')
373                         && (c[9] == 'A'|| c[9] == 'a')
374                         && (c[10] == 'T'|| c[10] == 't')
375                         && (c[11] == 'E'|| c[11] == 'e'))
376                     {
377                         s->command = NGX_IMAP_AUTHENTICATE;
378 
379                     } else {
380                         goto invalid;
381                     }
382                     break;
383 
384                 default:
385                     goto invalid;
386                 }
387 
388                 switch (ch) {
389                 case ' ':
390                     state = sw_spaces_before_argument;
391                     break;
392                 case CR:
393                     state = sw_almost_done;
394                     break;
395                 case LF:
396                     goto done;
397                 }
398                 break;
399             }
400 
401             if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {
402                 goto invalid;
403             }
404 
405             break;
406 
407         case sw_spaces_before_argument:
408             switch (ch) {
409             case ' ':
410                 break;
411             case CR:
412                 state = sw_almost_done;
413                 s->arg_end = p;
414                 break;
415             case LF:
416                 s->arg_end = p;
417                 goto done;
418             case '"':
419                 if (s->args.nelts <= 2) {
420                     s->quoted = 1;
421                     s->arg_start = p + 1;
422                     state = sw_argument;
423                     break;
424                 }
425                 goto invalid;
426             case '{':
427                 if (s->args.nelts <= 2) {
428                     state = sw_literal;
429                     break;
430                 }
431                 goto invalid;
432             default:
433                 if (s->args.nelts <= 2) {
434                     s->arg_start = p;
435                     state = sw_argument;
436                     break;
437                 }
438                 goto invalid;
439             }
440             break;
441 
442         case sw_argument:
443             if (ch == ' ' && s->quoted) {
444                 break;
445             }
446 
447             switch (ch) {
448             case '"':
449                 if (!s->quoted) {
450                     break;
451                 }
452                 s->quoted = 0;
453                 /* fall through */
454             case ' ':
455             case CR:
456             case LF:
457                 arg = ngx_array_push(&s->args);
458                 if (arg == NULL) {
459                     return NGX_ERROR;
460                 }
461                 arg->len = p - s->arg_start;
462                 arg->data = s->arg_start;
463                 s->arg_start = NULL;
464 
465                 switch (ch) {
466                 case '"':
467                 case ' ':
468                     state = sw_spaces_before_argument;
469                     break;
470                 case CR:
471                     state = sw_almost_done;
472                     break;
473                 case LF:
474                     goto done;
475                 }
476                 break;
477             case '\\':
478                 if (s->quoted) {
479                     s->backslash = 1;
480                     state = sw_backslash;
481                 }
482                 break;
483             }
484             break;
485 
486         case sw_backslash:
487             switch (ch) {
488             case CR:
489             case LF:
490                 goto invalid;
491             default:
492                 state = sw_argument;
493             }
494             break;
495 
496         case sw_literal:
497             if (ch >= '0' && ch <= '9') {
498                 s->literal_len = s->literal_len * 10 + (ch - '0');
499                 break;
500             }
501             if (ch == '}') {
502                 state = sw_start_literal_argument;
503                 break;
504             }
505             if (ch == '+') {
506                 state = sw_no_sync_literal_argument;
507                 break;
508             }
509             goto invalid;
510 
511         case sw_no_sync_literal_argument:
512             if (ch == '}') {
513                 s->no_sync_literal = 1;
514                 state = sw_start_literal_argument;
515                 break;
516             }
517             goto invalid;
518 
519         case sw_start_literal_argument:
520             switch (ch) {
521             case CR:
522                 break;
523             case LF:
524                 s->buffer->pos = p + 1;
525                 s->arg_start = p + 1;
526                 if (s->no_sync_literal == 0) {
527                     s->state = sw_literal_argument;
528                     return NGX_IMAP_NEXT;
529                 }
530                 state = sw_literal_argument;
531                 s->no_sync_literal = 0;
532                 break;
533             default:
534                 goto invalid;
535             }
536             break;
537 
538         case sw_literal_argument:
539             if (s->literal_len && --s->literal_len) {
540                 break;
541             }
542 
543             arg = ngx_array_push(&s->args);
544             if (arg == NULL) {
545                 return NGX_ERROR;
546             }
547             arg->len = p + 1 - s->arg_start;
548             arg->data = s->arg_start;
549             s->arg_start = NULL;
550             state = sw_end_literal_argument;
551 
552             break;
553 
554         case sw_end_literal_argument:
555             switch (ch) {
556             case '{':
557                 if (s->args.nelts <= 2) {
558                     state = sw_literal;
559                     break;
560                 }
561                 goto invalid;
562             case CR:
563                 state = sw_almost_done;
564                 break;
565             case LF:
566                 goto done;
567             default:
568                 state = sw_spaces_before_argument;
569                 break;
570             }
571             break;
572 
573         case sw_almost_done:
574             switch (ch) {
575             case LF:
576                 goto done;
577             default:
578                 goto invalid;
579             }
580         }
581     }
582 
583     s->buffer->pos = p;
584     s->state = state;
585 
586     return NGX_AGAIN;
587 
588 done:
589 
590     s->buffer->pos = p + 1;
591 
592     if (s->arg_start) {
593         arg = ngx_array_push(&s->args);
594         if (arg == NULL) {
595             return NGX_ERROR;
596         }
597         arg->len = s->arg_end - s->arg_start;
598         arg->data = s->arg_start;
599 
600         s->arg_start = NULL;
601         s->cmd_start = NULL;
602         s->quoted = 0;
603         s->no_sync_literal = 0;
604         s->literal_len = 0;
605     }
606 
607     s->state = (s->command != NGX_IMAP_AUTHENTICATE) ? sw_start : sw_argument;
608 
609     return NGX_OK;
610 
611 invalid:
612 
613     s->state = sw_start;
614     s->quoted = 0;
615     s->no_sync_literal = 0;
616     s->literal_len = 0;
617 
618     return NGX_MAIL_PARSE_INVALID_COMMAND;
619 }
620 
621 
622 ngx_int_t
ngx_mail_smtp_parse_command(ngx_mail_session_t * s)623 ngx_mail_smtp_parse_command(ngx_mail_session_t *s)
624 {
625     u_char      ch, *p, *c, c0, c1, c2, c3;
626     ngx_str_t  *arg;
627     enum {
628         sw_start = 0,
629         sw_command,
630         sw_invalid,
631         sw_spaces_before_argument,
632         sw_argument,
633         sw_almost_done
634     } state;
635 
636     state = s->state;
637 
638     for (p = s->buffer->pos; p < s->buffer->last; p++) {
639         ch = *p;
640 
641         switch (state) {
642 
643         /* SMTP command */
644         case sw_start:
645             s->cmd_start = p;
646             state = sw_command;
647 
648             /* fall through */
649 
650         case sw_command:
651             if (ch == ' ' || ch == CR || ch == LF) {
652                 c = s->cmd_start;
653 
654                 if (p - c == 4) {
655 
656                     c0 = ngx_toupper(c[0]);
657                     c1 = ngx_toupper(c[1]);
658                     c2 = ngx_toupper(c[2]);
659                     c3 = ngx_toupper(c[3]);
660 
661                     if (c0 == 'H' && c1 == 'E' && c2 == 'L' && c3 == 'O')
662                     {
663                         s->command = NGX_SMTP_HELO;
664 
665                     } else if (c0 == 'E' && c1 == 'H' && c2 == 'L' && c3 == 'O')
666                     {
667                         s->command = NGX_SMTP_EHLO;
668 
669                     } else if (c0 == 'Q' && c1 == 'U' && c2 == 'I' && c3 == 'T')
670                     {
671                         s->command = NGX_SMTP_QUIT;
672 
673                     } else if (c0 == 'A' && c1 == 'U' && c2 == 'T' && c3 == 'H')
674                     {
675                         s->command = NGX_SMTP_AUTH;
676 
677                     } else if (c0 == 'N' && c1 == 'O' && c2 == 'O' && c3 == 'P')
678                     {
679                         s->command = NGX_SMTP_NOOP;
680 
681                     } else if (c0 == 'M' && c1 == 'A' && c2 == 'I' && c3 == 'L')
682                     {
683                         s->command = NGX_SMTP_MAIL;
684 
685                     } else if (c0 == 'R' && c1 == 'S' && c2 == 'E' && c3 == 'T')
686                     {
687                         s->command = NGX_SMTP_RSET;
688 
689                     } else if (c0 == 'R' && c1 == 'C' && c2 == 'P' && c3 == 'T')
690                     {
691                         s->command = NGX_SMTP_RCPT;
692 
693                     } else if (c0 == 'V' && c1 == 'R' && c2 == 'F' && c3 == 'Y')
694                     {
695                         s->command = NGX_SMTP_VRFY;
696 
697                     } else if (c0 == 'E' && c1 == 'X' && c2 == 'P' && c3 == 'N')
698                     {
699                         s->command = NGX_SMTP_EXPN;
700 
701                     } else if (c0 == 'H' && c1 == 'E' && c2 == 'L' && c3 == 'P')
702                     {
703                         s->command = NGX_SMTP_HELP;
704 
705                     } else {
706                         goto invalid;
707                     }
708 #if (NGX_MAIL_SSL)
709                 } else if (p - c == 8) {
710 
711                     if ((c[0] == 'S'|| c[0] == 's')
712                         && (c[1] == 'T'|| c[1] == 't')
713                         && (c[2] == 'A'|| c[2] == 'a')
714                         && (c[3] == 'R'|| c[3] == 'r')
715                         && (c[4] == 'T'|| c[4] == 't')
716                         && (c[5] == 'T'|| c[5] == 't')
717                         && (c[6] == 'L'|| c[6] == 'l')
718                         && (c[7] == 'S'|| c[7] == 's'))
719                     {
720                         s->command = NGX_SMTP_STARTTLS;
721 
722                     } else {
723                         goto invalid;
724                     }
725 #endif
726                 } else {
727                     goto invalid;
728                 }
729 
730                 s->cmd.data = s->cmd_start;
731                 s->cmd.len = p - s->cmd_start;
732 
733                 switch (ch) {
734                 case ' ':
735                     state = sw_spaces_before_argument;
736                     break;
737                 case CR:
738                     state = sw_almost_done;
739                     break;
740                 case LF:
741                     goto done;
742                 }
743                 break;
744             }
745 
746             if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {
747                 goto invalid;
748             }
749 
750             break;
751 
752         case sw_invalid:
753             goto invalid;
754 
755         case sw_spaces_before_argument:
756             switch (ch) {
757             case ' ':
758                 break;
759             case CR:
760                 state = sw_almost_done;
761                 s->arg_end = p;
762                 break;
763             case LF:
764                 s->arg_end = p;
765                 goto done;
766             default:
767                 if (s->args.nelts <= 10) {
768                     state = sw_argument;
769                     s->arg_start = p;
770                     break;
771                 }
772                 goto invalid;
773             }
774             break;
775 
776         case sw_argument:
777             switch (ch) {
778             case ' ':
779             case CR:
780             case LF:
781                 arg = ngx_array_push(&s->args);
782                 if (arg == NULL) {
783                     return NGX_ERROR;
784                 }
785                 arg->len = p - s->arg_start;
786                 arg->data = s->arg_start;
787                 s->arg_start = NULL;
788 
789                 switch (ch) {
790                 case ' ':
791                     state = sw_spaces_before_argument;
792                     break;
793                 case CR:
794                     state = sw_almost_done;
795                     break;
796                 case LF:
797                     goto done;
798                 }
799                 break;
800 
801             default:
802                 break;
803             }
804             break;
805 
806         case sw_almost_done:
807             switch (ch) {
808             case LF:
809                 goto done;
810             default:
811                 goto invalid;
812             }
813         }
814     }
815 
816     s->buffer->pos = p;
817     s->state = state;
818 
819     return NGX_AGAIN;
820 
821 done:
822 
823     s->buffer->pos = p + 1;
824 
825     if (s->arg_start) {
826         arg = ngx_array_push(&s->args);
827         if (arg == NULL) {
828             return NGX_ERROR;
829         }
830         arg->len = s->arg_end - s->arg_start;
831         arg->data = s->arg_start;
832         s->arg_start = NULL;
833     }
834 
835     s->state = (s->command != NGX_SMTP_AUTH) ? sw_start : sw_argument;
836 
837     return NGX_OK;
838 
839 invalid:
840 
841     s->state = sw_invalid;
842     s->arg_start = NULL;
843 
844     /* skip invalid command till LF */
845 
846     for (p = s->buffer->pos; p < s->buffer->last; p++) {
847         if (*p == LF) {
848             s->state = sw_start;
849             p++;
850             break;
851         }
852     }
853 
854     s->buffer->pos = p;
855 
856     return NGX_MAIL_PARSE_INVALID_COMMAND;
857 }
858 
859 
860 ngx_int_t
ngx_mail_auth_parse(ngx_mail_session_t * s,ngx_connection_t * c)861 ngx_mail_auth_parse(ngx_mail_session_t *s, ngx_connection_t *c)
862 {
863     ngx_str_t                 *arg;
864 
865 #if (NGX_MAIL_SSL)
866     if (ngx_mail_starttls_only(s, c)) {
867         return NGX_MAIL_PARSE_INVALID_COMMAND;
868     }
869 #endif
870 
871     if (s->args.nelts == 0) {
872         return NGX_MAIL_PARSE_INVALID_COMMAND;
873     }
874 
875     arg = s->args.elts;
876 
877     if (arg[0].len == 5) {
878 
879         if (ngx_strncasecmp(arg[0].data, (u_char *) "LOGIN", 5) == 0) {
880 
881             if (s->args.nelts == 1) {
882                 return NGX_MAIL_AUTH_LOGIN;
883             }
884 
885             if (s->args.nelts == 2) {
886                 return NGX_MAIL_AUTH_LOGIN_USERNAME;
887             }
888 
889             return NGX_MAIL_PARSE_INVALID_COMMAND;
890         }
891 
892         if (ngx_strncasecmp(arg[0].data, (u_char *) "PLAIN", 5) == 0) {
893 
894             if (s->args.nelts == 1) {
895                 return NGX_MAIL_AUTH_PLAIN;
896             }
897 
898             if (s->args.nelts == 2) {
899                 return ngx_mail_auth_plain(s, c, 1);
900             }
901         }
902 
903         return NGX_MAIL_PARSE_INVALID_COMMAND;
904     }
905 
906     if (arg[0].len == 8) {
907 
908         if (ngx_strncasecmp(arg[0].data, (u_char *) "CRAM-MD5", 8) == 0) {
909 
910             if (s->args.nelts != 1) {
911                 return NGX_MAIL_PARSE_INVALID_COMMAND;
912             }
913 
914             return NGX_MAIL_AUTH_CRAM_MD5;
915         }
916 
917         if (ngx_strncasecmp(arg[0].data, (u_char *) "EXTERNAL", 8) == 0) {
918 
919             if (s->args.nelts == 1) {
920                 return NGX_MAIL_AUTH_EXTERNAL;
921             }
922 
923             if (s->args.nelts == 2) {
924                 return ngx_mail_auth_external(s, c, 1);
925             }
926         }
927 
928         return NGX_MAIL_PARSE_INVALID_COMMAND;
929     }
930 
931     return NGX_MAIL_PARSE_INVALID_COMMAND;
932 }
933