ngx_mail_parse.c revision e18a033b
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
17ngx_int_t
18ngx_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
188done:
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
206invalid:
207
208    s->state = sw_start;
209    s->arg_start = NULL;
210
211    return NGX_MAIL_PARSE_INVALID_COMMAND;
212}
213
214
215ngx_int_t
216ngx_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
588done:
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
611invalid:
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
622ngx_int_t
623ngx_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
821done:
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
839invalid:
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
860ngx_int_t
861ngx_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