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