Update and clean Tomato RAF files
[tomato.git] / release / src / router / nginx / src / mail / ngx_mail_parse.c
blobeb16d5b4fe91673aa183f8c5b7e72c3f0668916b
2 /*
3 * Copyright (C) Igor Sysoev
4 * Copyright (C) Nginx, Inc.
5 */
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>
17 ngx_int_t
18 ngx_mail_pop3_parse_command(ngx_mail_session_t *s)
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;
29 state = s->state;
31 for (p = s->buffer->pos; p < s->buffer->last; p++) {
32 ch = *p;
34 switch (state) {
36 /* POP3 command */
37 case sw_start:
38 if (ch == ' ' || ch == CR || ch == LF) {
39 c = s->buffer->start;
41 if (p - c == 4) {
43 c0 = ngx_toupper(c[0]);
44 c1 = ngx_toupper(c[1]);
45 c2 = ngx_toupper(c[2]);
46 c3 = ngx_toupper(c[3]);
48 if (c0 == 'U' && c1 == 'S' && c2 == 'E' && c3 == 'R')
50 s->command = NGX_POP3_USER;
52 } else if (c0 == 'P' && c1 == 'A' && c2 == 'S' && c3 == 'S')
54 s->command = NGX_POP3_PASS;
56 } else if (c0 == 'A' && c1 == 'P' && c2 == 'O' && c3 == 'P')
58 s->command = NGX_POP3_APOP;
60 } else if (c0 == 'Q' && c1 == 'U' && c2 == 'I' && c3 == 'T')
62 s->command = NGX_POP3_QUIT;
64 } else if (c0 == 'C' && c1 == 'A' && c2 == 'P' && c3 == 'A')
66 s->command = NGX_POP3_CAPA;
68 } else if (c0 == 'A' && c1 == 'U' && c2 == 'T' && c3 == 'H')
70 s->command = NGX_POP3_AUTH;
72 } else if (c0 == 'N' && c1 == 'O' && c2 == 'O' && c3 == 'P')
74 s->command = NGX_POP3_NOOP;
75 #if (NGX_MAIL_SSL)
76 } else if (c0 == 'S' && c1 == 'T' && c2 == 'L' && c3 == 'S')
78 s->command = NGX_POP3_STLS;
79 #endif
80 } else {
81 goto invalid;
84 } else {
85 goto invalid;
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;
98 break;
101 if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {
102 goto invalid;
105 break;
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;
124 goto invalid;
126 break;
128 case sw_argument:
129 switch (ch) {
131 case ' ':
134 * the space should be considered as part of the at username
135 * or password, but not of argument in other commands
138 if (s->command == NGX_POP3_USER
139 || s->command == NGX_POP3_PASS)
141 break;
144 /* fall through */
146 case CR:
147 case LF:
148 arg = ngx_array_push(&s->args);
149 if (arg == NULL) {
150 return NGX_ERROR;
152 arg->len = p - s->arg_start;
153 arg->data = s->arg_start;
154 s->arg_start = NULL;
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;
166 break;
168 default:
169 break;
171 break;
173 case sw_almost_done:
174 switch (ch) {
175 case LF:
176 goto done;
177 default:
178 goto invalid;
183 s->buffer->pos = p;
184 s->state = state;
186 return NGX_AGAIN;
188 done:
190 s->buffer->pos = p + 1;
192 if (s->arg_start) {
193 arg = ngx_array_push(&s->args);
194 if (arg == NULL) {
195 return NGX_ERROR;
197 arg->len = s->arg_end - s->arg_start;
198 arg->data = s->arg_start;
199 s->arg_start = NULL;
202 s->state = (s->command != NGX_POP3_AUTH) ? sw_start : sw_argument;
204 return NGX_OK;
206 invalid:
208 s->state = sw_start;
209 s->arg_start = NULL;
211 return NGX_MAIL_PARSE_INVALID_COMMAND;
215 ngx_int_t
216 ngx_mail_imap_parse_command(ngx_mail_session_t *s)
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;
235 state = s->state;
237 for (p = s->buffer->pos; p < s->buffer->last; p++) {
238 ch = *p;
240 switch (state) {
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;
257 break;
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;
274 break;
276 case sw_command:
277 if (ch == ' ' || ch == CR || ch == LF) {
279 c = s->cmd_start;
281 switch (p - c) {
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'))
289 s->command = NGX_IMAP_NOOP;
291 } else {
292 goto invalid;
294 break;
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'))
303 s->command = NGX_IMAP_LOGIN;
305 } else {
306 goto invalid;
308 break;
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'))
318 s->command = NGX_IMAP_LOGOUT;
320 } else {
321 goto invalid;
323 break;
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'))
336 s->command = NGX_IMAP_STARTTLS;
338 } else {
339 goto invalid;
341 break;
342 #endif
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'))
356 s->command = NGX_IMAP_CAPABILITY;
358 } else {
359 goto invalid;
361 break;
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'))
377 s->command = NGX_IMAP_AUTHENTICATE;
379 } else {
380 goto invalid;
382 break;
384 default:
385 goto invalid;
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;
398 break;
401 if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {
402 goto invalid;
405 break;
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;
425 goto invalid;
426 case '{':
427 if (s->args.nelts <= 2) {
428 state = sw_literal;
429 break;
431 goto invalid;
432 default:
433 if (s->args.nelts <= 2) {
434 s->arg_start = p;
435 state = sw_argument;
436 break;
438 goto invalid;
440 break;
442 case sw_argument:
443 if (ch == ' ' && s->quoted) {
444 break;
447 switch (ch) {
448 case '"':
449 if (!s->quoted) {
450 break;
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;
461 arg->len = p - s->arg_start;
462 arg->data = s->arg_start;
463 s->arg_start = NULL;
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;
476 break;
477 case '\\':
478 if (s->quoted) {
479 s->backslash = 1;
480 state = sw_backslash;
482 break;
484 break;
486 case sw_backslash:
487 switch (ch) {
488 case CR:
489 case LF:
490 goto invalid;
491 default:
492 state = sw_argument;
494 break;
496 case sw_literal:
497 if (ch >= '0' && ch <= '9') {
498 s->literal_len = s->literal_len * 10 + (ch - '0');
499 break;
501 if (ch == '}') {
502 state = sw_start_literal_argument;
503 break;
505 if (ch == '+') {
506 state = sw_no_sync_literal_argument;
507 break;
509 goto invalid;
511 case sw_no_sync_literal_argument:
512 if (ch == '}') {
513 s->no_sync_literal = 1;
514 state = sw_start_literal_argument;
515 break;
517 goto invalid;
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;
530 state = sw_literal_argument;
531 s->no_sync_literal = 0;
532 break;
533 default:
534 goto invalid;
536 break;
538 case sw_literal_argument:
539 if (s->literal_len && --s->literal_len) {
540 break;
543 arg = ngx_array_push(&s->args);
544 if (arg == NULL) {
545 return NGX_ERROR;
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;
552 break;
554 case sw_end_literal_argument:
555 switch (ch) {
556 case '{':
557 if (s->args.nelts <= 2) {
558 state = sw_literal;
559 break;
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;
571 break;
573 case sw_almost_done:
574 switch (ch) {
575 case LF:
576 goto done;
577 default:
578 goto invalid;
583 s->buffer->pos = p;
584 s->state = state;
586 return NGX_AGAIN;
588 done:
590 s->buffer->pos = p + 1;
592 if (s->arg_start) {
593 arg = ngx_array_push(&s->args);
594 if (arg == NULL) {
595 return NGX_ERROR;
597 arg->len = s->arg_end - s->arg_start;
598 arg->data = s->arg_start;
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;
607 s->state = (s->command != NGX_IMAP_AUTHENTICATE) ? sw_start : sw_argument;
609 return NGX_OK;
611 invalid:
613 s->state = sw_start;
614 s->quoted = 0;
615 s->no_sync_literal = 0;
616 s->literal_len = 0;
618 return NGX_MAIL_PARSE_INVALID_COMMAND;
622 ngx_int_t
623 ngx_mail_smtp_parse_command(ngx_mail_session_t *s)
625 u_char ch, *p, *c, c0, c1, c2, c3;
626 ngx_str_t *arg;
627 enum {
628 sw_start = 0,
629 sw_spaces_before_argument,
630 sw_argument,
631 sw_almost_done
632 } state;
634 state = s->state;
636 for (p = s->buffer->pos; p < s->buffer->last; p++) {
637 ch = *p;
639 switch (state) {
641 /* SMTP command */
642 case sw_start:
643 if (ch == ' ' || ch == CR || ch == LF) {
644 c = s->buffer->start;
646 if (p - c == 4) {
648 c0 = ngx_toupper(c[0]);
649 c1 = ngx_toupper(c[1]);
650 c2 = ngx_toupper(c[2]);
651 c3 = ngx_toupper(c[3]);
653 if (c0 == 'H' && c1 == 'E' && c2 == 'L' && c3 == 'O')
655 s->command = NGX_SMTP_HELO;
657 } else if (c0 == 'E' && c1 == 'H' && c2 == 'L' && c3 == 'O')
659 s->command = NGX_SMTP_EHLO;
661 } else if (c0 == 'Q' && c1 == 'U' && c2 == 'I' && c3 == 'T')
663 s->command = NGX_SMTP_QUIT;
665 } else if (c0 == 'A' && c1 == 'U' && c2 == 'T' && c3 == 'H')
667 s->command = NGX_SMTP_AUTH;
669 } else if (c0 == 'N' && c1 == 'O' && c2 == 'O' && c3 == 'P')
671 s->command = NGX_SMTP_NOOP;
673 } else if (c0 == 'M' && c1 == 'A' && c2 == 'I' && c3 == 'L')
675 s->command = NGX_SMTP_MAIL;
677 } else if (c0 == 'R' && c1 == 'S' && c2 == 'E' && c3 == 'T')
679 s->command = NGX_SMTP_RSET;
681 } else if (c0 == 'R' && c1 == 'C' && c2 == 'P' && c3 == 'T')
683 s->command = NGX_SMTP_RCPT;
685 } else if (c0 == 'V' && c1 == 'R' && c2 == 'F' && c3 == 'Y')
687 s->command = NGX_SMTP_VRFY;
689 } else if (c0 == 'E' && c1 == 'X' && c2 == 'P' && c3 == 'N')
691 s->command = NGX_SMTP_EXPN;
693 } else if (c0 == 'H' && c1 == 'E' && c2 == 'L' && c3 == 'P')
695 s->command = NGX_SMTP_HELP;
697 } else {
698 goto invalid;
700 #if (NGX_MAIL_SSL)
701 } else if (p - c == 8) {
703 if ((c[0] == 'S'|| c[0] == 's')
704 && (c[1] == 'T'|| c[1] == 't')
705 && (c[2] == 'A'|| c[2] == 'a')
706 && (c[3] == 'R'|| c[3] == 'r')
707 && (c[4] == 'T'|| c[4] == 't')
708 && (c[5] == 'T'|| c[5] == 't')
709 && (c[6] == 'L'|| c[6] == 'l')
710 && (c[7] == 'S'|| c[7] == 's'))
712 s->command = NGX_SMTP_STARTTLS;
714 } else {
715 goto invalid;
717 #endif
718 } else {
719 goto invalid;
722 switch (ch) {
723 case ' ':
724 state = sw_spaces_before_argument;
725 break;
726 case CR:
727 state = sw_almost_done;
728 break;
729 case LF:
730 goto done;
732 break;
735 if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) {
736 goto invalid;
739 break;
741 case sw_spaces_before_argument:
742 switch (ch) {
743 case ' ':
744 break;
745 case CR:
746 state = sw_almost_done;
747 s->arg_end = p;
748 break;
749 case LF:
750 s->arg_end = p;
751 goto done;
752 default:
753 if (s->args.nelts <= 10) {
754 state = sw_argument;
755 s->arg_start = p;
756 break;
758 goto invalid;
760 break;
762 case sw_argument:
763 switch (ch) {
764 case ' ':
765 case CR:
766 case LF:
767 arg = ngx_array_push(&s->args);
768 if (arg == NULL) {
769 return NGX_ERROR;
771 arg->len = p - s->arg_start;
772 arg->data = s->arg_start;
773 s->arg_start = NULL;
775 switch (ch) {
776 case ' ':
777 state = sw_spaces_before_argument;
778 break;
779 case CR:
780 state = sw_almost_done;
781 break;
782 case LF:
783 goto done;
785 break;
787 default:
788 break;
790 break;
792 case sw_almost_done:
793 switch (ch) {
794 case LF:
795 goto done;
796 default:
797 goto invalid;
802 s->buffer->pos = p;
803 s->state = state;
805 return NGX_AGAIN;
807 done:
809 s->buffer->pos = p + 1;
811 if (s->arg_start) {
812 arg = ngx_array_push(&s->args);
813 if (arg == NULL) {
814 return NGX_ERROR;
816 arg->len = s->arg_end - s->arg_start;
817 arg->data = s->arg_start;
818 s->arg_start = NULL;
821 s->state = (s->command != NGX_SMTP_AUTH) ? sw_start : sw_argument;
823 return NGX_OK;
825 invalid:
827 s->state = sw_start;
828 s->arg_start = NULL;
830 return NGX_MAIL_PARSE_INVALID_COMMAND;
834 ngx_int_t
835 ngx_mail_auth_parse(ngx_mail_session_t *s, ngx_connection_t *c)
837 ngx_str_t *arg;
839 #if (NGX_MAIL_SSL)
840 if (ngx_mail_starttls_only(s, c)) {
841 return NGX_MAIL_PARSE_INVALID_COMMAND;
843 #endif
845 arg = s->args.elts;
847 if (arg[0].len == 5) {
849 if (ngx_strncasecmp(arg[0].data, (u_char *) "LOGIN", 5) == 0) {
851 if (s->args.nelts == 1) {
852 return NGX_MAIL_AUTH_LOGIN;
855 if (s->args.nelts == 2) {
856 return NGX_MAIL_AUTH_LOGIN_USERNAME;
859 return NGX_MAIL_PARSE_INVALID_COMMAND;
862 if (ngx_strncasecmp(arg[0].data, (u_char *) "PLAIN", 5) == 0) {
864 if (s->args.nelts == 1) {
865 return NGX_MAIL_AUTH_PLAIN;
868 if (s->args.nelts == 2) {
869 return ngx_mail_auth_plain(s, c, 1);
873 return NGX_MAIL_PARSE_INVALID_COMMAND;
876 if (arg[0].len == 8) {
878 if (s->args.nelts != 1) {
879 return NGX_MAIL_PARSE_INVALID_COMMAND;
882 if (ngx_strncasecmp(arg[0].data, (u_char *) "CRAM-MD5", 8) == 0) {
883 return NGX_MAIL_AUTH_CRAM_MD5;
887 return NGX_MAIL_PARSE_INVALID_COMMAND;