|  |  | 
|  | /* | 
|  | * Copyright (C) Igor Sysoev | 
|  | * Copyright (C) Nginx, Inc. | 
|  | */ | 
|  |  | 
|  |  | 
|  | #include <ngx_config.h> | 
|  | #include <ngx_core.h> | 
|  | #include <ngx_event.h> | 
|  | #include <ngx_mail.h> | 
|  | #include <ngx_mail_pop3_module.h> | 
|  | #include <ngx_mail_imap_module.h> | 
|  | #include <ngx_mail_smtp_module.h> | 
|  |  | 
|  |  | 
|  | ngx_int_t | 
|  | ngx_mail_pop3_parse_command(ngx_mail_session_t *s) | 
|  | { | 
|  | u_char      ch, *p, *c, c0, c1, c2, c3; | 
|  | ngx_str_t  *arg; | 
|  | enum { | 
|  | sw_start = 0, | 
|  | sw_spaces_before_argument, | 
|  | sw_argument, | 
|  | sw_almost_done | 
|  | } state; | 
|  |  | 
|  | state = s->state; | 
|  |  | 
|  | for (p = s->buffer->pos; p < s->buffer->last; p++) { | 
|  | ch = *p; | 
|  |  | 
|  | switch (state) { | 
|  |  | 
|  | /* POP3 command */ | 
|  | case sw_start: | 
|  | if (ch == ' ' || ch == CR || ch == LF) { | 
|  | c = s->buffer->start; | 
|  |  | 
|  | if (p - c == 4) { | 
|  |  | 
|  | c0 = ngx_toupper(c[0]); | 
|  | c1 = ngx_toupper(c[1]); | 
|  | c2 = ngx_toupper(c[2]); | 
|  | c3 = ngx_toupper(c[3]); | 
|  |  | 
|  | if (c0 == 'U' && c1 == 'S' && c2 == 'E' && c3 == 'R') | 
|  | { | 
|  | s->command = NGX_POP3_USER; | 
|  |  | 
|  | } else if (c0 == 'P' && c1 == 'A' && c2 == 'S' && c3 == 'S') | 
|  | { | 
|  | s->command = NGX_POP3_PASS; | 
|  |  | 
|  | } else if (c0 == 'A' && c1 == 'P' && c2 == 'O' && c3 == 'P') | 
|  | { | 
|  | s->command = NGX_POP3_APOP; | 
|  |  | 
|  | } else if (c0 == 'Q' && c1 == 'U' && c2 == 'I' && c3 == 'T') | 
|  | { | 
|  | s->command = NGX_POP3_QUIT; | 
|  |  | 
|  | } else if (c0 == 'C' && c1 == 'A' && c2 == 'P' && c3 == 'A') | 
|  | { | 
|  | s->command = NGX_POP3_CAPA; | 
|  |  | 
|  | } else if (c0 == 'A' && c1 == 'U' && c2 == 'T' && c3 == 'H') | 
|  | { | 
|  | s->command = NGX_POP3_AUTH; | 
|  |  | 
|  | } else if (c0 == 'N' && c1 == 'O' && c2 == 'O' && c3 == 'P') | 
|  | { | 
|  | s->command = NGX_POP3_NOOP; | 
|  | #if (NGX_MAIL_SSL) | 
|  | } else if (c0 == 'S' && c1 == 'T' && c2 == 'L' && c3 == 'S') | 
|  | { | 
|  | s->command = NGX_POP3_STLS; | 
|  | #endif | 
|  | } else { | 
|  | goto invalid; | 
|  | } | 
|  |  | 
|  | } else { | 
|  | goto invalid; | 
|  | } | 
|  |  | 
|  | switch (ch) { | 
|  | case ' ': | 
|  | state = sw_spaces_before_argument; | 
|  | break; | 
|  | case CR: | 
|  | state = sw_almost_done; | 
|  | break; | 
|  | case LF: | 
|  | goto done; | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) { | 
|  | goto invalid; | 
|  | } | 
|  |  | 
|  | break; | 
|  |  | 
|  | case sw_spaces_before_argument: | 
|  | switch (ch) { | 
|  | case ' ': | 
|  | break; | 
|  | case CR: | 
|  | state = sw_almost_done; | 
|  | s->arg_end = p; | 
|  | break; | 
|  | case LF: | 
|  | s->arg_end = p; | 
|  | goto done; | 
|  | default: | 
|  | if (s->args.nelts <= 2) { | 
|  | state = sw_argument; | 
|  | s->arg_start = p; | 
|  | break; | 
|  | } | 
|  | goto invalid; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case sw_argument: | 
|  | switch (ch) { | 
|  |  | 
|  | case ' ': | 
|  |  | 
|  | /* | 
|  | * the space should be considered as part of the at username | 
|  | * or password, but not of argument in other commands | 
|  | */ | 
|  |  | 
|  | if (s->command == NGX_POP3_USER | 
|  | || s->command == NGX_POP3_PASS) | 
|  | { | 
|  | break; | 
|  | } | 
|  |  | 
|  | /* fall through */ | 
|  |  | 
|  | case CR: | 
|  | case LF: | 
|  | arg = ngx_array_push(&s->args); | 
|  | if (arg == NULL) { | 
|  | return NGX_ERROR; | 
|  | } | 
|  | arg->len = p - s->arg_start; | 
|  | arg->data = s->arg_start; | 
|  | s->arg_start = NULL; | 
|  |  | 
|  | switch (ch) { | 
|  | case ' ': | 
|  | state = sw_spaces_before_argument; | 
|  | break; | 
|  | case CR: | 
|  | state = sw_almost_done; | 
|  | break; | 
|  | case LF: | 
|  | goto done; | 
|  | } | 
|  | break; | 
|  |  | 
|  | default: | 
|  | break; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case sw_almost_done: | 
|  | switch (ch) { | 
|  | case LF: | 
|  | goto done; | 
|  | default: | 
|  | goto invalid; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | s->buffer->pos = p; | 
|  | s->state = state; | 
|  |  | 
|  | return NGX_AGAIN; | 
|  |  | 
|  | done: | 
|  |  | 
|  | s->buffer->pos = p + 1; | 
|  |  | 
|  | if (s->arg_start) { | 
|  | arg = ngx_array_push(&s->args); | 
|  | if (arg == NULL) { | 
|  | return NGX_ERROR; | 
|  | } | 
|  | arg->len = s->arg_end - s->arg_start; | 
|  | arg->data = s->arg_start; | 
|  | s->arg_start = NULL; | 
|  | } | 
|  |  | 
|  | s->state = (s->command != NGX_POP3_AUTH) ? sw_start : sw_argument; | 
|  |  | 
|  | return NGX_OK; | 
|  |  | 
|  | invalid: | 
|  |  | 
|  | s->state = sw_start; | 
|  | s->arg_start = NULL; | 
|  |  | 
|  | return NGX_MAIL_PARSE_INVALID_COMMAND; | 
|  | } | 
|  |  | 
|  |  | 
|  | ngx_int_t | 
|  | ngx_mail_imap_parse_command(ngx_mail_session_t *s) | 
|  | { | 
|  | u_char      ch, *p, *c; | 
|  | ngx_str_t  *arg; | 
|  | enum { | 
|  | sw_start = 0, | 
|  | sw_spaces_before_command, | 
|  | sw_command, | 
|  | sw_spaces_before_argument, | 
|  | sw_argument, | 
|  | sw_backslash, | 
|  | sw_literal, | 
|  | sw_no_sync_literal_argument, | 
|  | sw_start_literal_argument, | 
|  | sw_literal_argument, | 
|  | sw_end_literal_argument, | 
|  | sw_almost_done | 
|  | } state; | 
|  |  | 
|  | state = s->state; | 
|  |  | 
|  | for (p = s->buffer->pos; p < s->buffer->last; p++) { | 
|  | ch = *p; | 
|  |  | 
|  | switch (state) { | 
|  |  | 
|  | /* IMAP tag */ | 
|  | case sw_start: | 
|  | switch (ch) { | 
|  | case ' ': | 
|  | s->tag.len = p - s->buffer->start + 1; | 
|  | s->tag.data = s->buffer->start; | 
|  | state = sw_spaces_before_command; | 
|  | break; | 
|  | case CR: | 
|  | s->state = sw_start; | 
|  | return NGX_MAIL_PARSE_INVALID_COMMAND; | 
|  | case LF: | 
|  | s->state = sw_start; | 
|  | return NGX_MAIL_PARSE_INVALID_COMMAND; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case sw_spaces_before_command: | 
|  | switch (ch) { | 
|  | case ' ': | 
|  | break; | 
|  | case CR: | 
|  | s->state = sw_start; | 
|  | return NGX_MAIL_PARSE_INVALID_COMMAND; | 
|  | case LF: | 
|  | s->state = sw_start; | 
|  | return NGX_MAIL_PARSE_INVALID_COMMAND; | 
|  | default: | 
|  | s->cmd_start = p; | 
|  | state = sw_command; | 
|  | break; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case sw_command: | 
|  | if (ch == ' ' || ch == CR || ch == LF) { | 
|  |  | 
|  | c = s->cmd_start; | 
|  |  | 
|  | switch (p - c) { | 
|  |  | 
|  | case 4: | 
|  | if ((c[0] == 'N' || c[0] == 'n') | 
|  | && (c[1] == 'O'|| c[1] == 'o') | 
|  | && (c[2] == 'O'|| c[2] == 'o') | 
|  | && (c[3] == 'P'|| c[3] == 'p')) | 
|  | { | 
|  | s->command = NGX_IMAP_NOOP; | 
|  |  | 
|  | } else { | 
|  | goto invalid; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case 5: | 
|  | if ((c[0] == 'L'|| c[0] == 'l') | 
|  | && (c[1] == 'O'|| c[1] == 'o') | 
|  | && (c[2] == 'G'|| c[2] == 'g') | 
|  | && (c[3] == 'I'|| c[3] == 'i') | 
|  | && (c[4] == 'N'|| c[4] == 'n')) | 
|  | { | 
|  | s->command = NGX_IMAP_LOGIN; | 
|  |  | 
|  | } else { | 
|  | goto invalid; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case 6: | 
|  | if ((c[0] == 'L'|| c[0] == 'l') | 
|  | && (c[1] == 'O'|| c[1] == 'o') | 
|  | && (c[2] == 'G'|| c[2] == 'g') | 
|  | && (c[3] == 'O'|| c[3] == 'o') | 
|  | && (c[4] == 'U'|| c[4] == 'u') | 
|  | && (c[5] == 'T'|| c[5] == 't')) | 
|  | { | 
|  | s->command = NGX_IMAP_LOGOUT; | 
|  |  | 
|  | } else { | 
|  | goto invalid; | 
|  | } | 
|  | break; | 
|  |  | 
|  | #if (NGX_MAIL_SSL) | 
|  | case 8: | 
|  | if ((c[0] == 'S'|| c[0] == 's') | 
|  | && (c[1] == 'T'|| c[1] == 't') | 
|  | && (c[2] == 'A'|| c[2] == 'a') | 
|  | && (c[3] == 'R'|| c[3] == 'r') | 
|  | && (c[4] == 'T'|| c[4] == 't') | 
|  | && (c[5] == 'T'|| c[5] == 't') | 
|  | && (c[6] == 'L'|| c[6] == 'l') | 
|  | && (c[7] == 'S'|| c[7] == 's')) | 
|  | { | 
|  | s->command = NGX_IMAP_STARTTLS; | 
|  |  | 
|  | } else { | 
|  | goto invalid; | 
|  | } | 
|  | break; | 
|  | #endif | 
|  |  | 
|  | case 10: | 
|  | if ((c[0] == 'C'|| c[0] == 'c') | 
|  | && (c[1] == 'A'|| c[1] == 'a') | 
|  | && (c[2] == 'P'|| c[2] == 'p') | 
|  | && (c[3] == 'A'|| c[3] == 'a') | 
|  | && (c[4] == 'B'|| c[4] == 'b') | 
|  | && (c[5] == 'I'|| c[5] == 'i') | 
|  | && (c[6] == 'L'|| c[6] == 'l') | 
|  | && (c[7] == 'I'|| c[7] == 'i') | 
|  | && (c[8] == 'T'|| c[8] == 't') | 
|  | && (c[9] == 'Y'|| c[9] == 'y')) | 
|  | { | 
|  | s->command = NGX_IMAP_CAPABILITY; | 
|  |  | 
|  | } else { | 
|  | goto invalid; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case 12: | 
|  | if ((c[0] == 'A'|| c[0] == 'a') | 
|  | && (c[1] == 'U'|| c[1] == 'u') | 
|  | && (c[2] == 'T'|| c[2] == 't') | 
|  | && (c[3] == 'H'|| c[3] == 'h') | 
|  | && (c[4] == 'E'|| c[4] == 'e') | 
|  | && (c[5] == 'N'|| c[5] == 'n') | 
|  | && (c[6] == 'T'|| c[6] == 't') | 
|  | && (c[7] == 'I'|| c[7] == 'i') | 
|  | && (c[8] == 'C'|| c[8] == 'c') | 
|  | && (c[9] == 'A'|| c[9] == 'a') | 
|  | && (c[10] == 'T'|| c[10] == 't') | 
|  | && (c[11] == 'E'|| c[11] == 'e')) | 
|  | { | 
|  | s->command = NGX_IMAP_AUTHENTICATE; | 
|  |  | 
|  | } else { | 
|  | goto invalid; | 
|  | } | 
|  | break; | 
|  |  | 
|  | default: | 
|  | goto invalid; | 
|  | } | 
|  |  | 
|  | switch (ch) { | 
|  | case ' ': | 
|  | state = sw_spaces_before_argument; | 
|  | break; | 
|  | case CR: | 
|  | state = sw_almost_done; | 
|  | break; | 
|  | case LF: | 
|  | goto done; | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) { | 
|  | goto invalid; | 
|  | } | 
|  |  | 
|  | break; | 
|  |  | 
|  | case sw_spaces_before_argument: | 
|  | switch (ch) { | 
|  | case ' ': | 
|  | break; | 
|  | case CR: | 
|  | state = sw_almost_done; | 
|  | s->arg_end = p; | 
|  | break; | 
|  | case LF: | 
|  | s->arg_end = p; | 
|  | goto done; | 
|  | case '"': | 
|  | if (s->args.nelts <= 2) { | 
|  | s->quoted = 1; | 
|  | s->arg_start = p + 1; | 
|  | state = sw_argument; | 
|  | break; | 
|  | } | 
|  | goto invalid; | 
|  | case '{': | 
|  | if (s->args.nelts <= 2) { | 
|  | state = sw_literal; | 
|  | break; | 
|  | } | 
|  | goto invalid; | 
|  | default: | 
|  | if (s->args.nelts <= 2) { | 
|  | s->arg_start = p; | 
|  | state = sw_argument; | 
|  | break; | 
|  | } | 
|  | goto invalid; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case sw_argument: | 
|  | if (ch == ' ' && s->quoted) { | 
|  | break; | 
|  | } | 
|  |  | 
|  | switch (ch) { | 
|  | case '"': | 
|  | if (!s->quoted) { | 
|  | break; | 
|  | } | 
|  | s->quoted = 0; | 
|  | /* fall through */ | 
|  | case ' ': | 
|  | case CR: | 
|  | case LF: | 
|  | arg = ngx_array_push(&s->args); | 
|  | if (arg == NULL) { | 
|  | return NGX_ERROR; | 
|  | } | 
|  | arg->len = p - s->arg_start; | 
|  | arg->data = s->arg_start; | 
|  | s->arg_start = NULL; | 
|  |  | 
|  | switch (ch) { | 
|  | case '"': | 
|  | case ' ': | 
|  | state = sw_spaces_before_argument; | 
|  | break; | 
|  | case CR: | 
|  | state = sw_almost_done; | 
|  | break; | 
|  | case LF: | 
|  | goto done; | 
|  | } | 
|  | break; | 
|  | case '\\': | 
|  | if (s->quoted) { | 
|  | s->backslash = 1; | 
|  | state = sw_backslash; | 
|  | } | 
|  | break; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case sw_backslash: | 
|  | switch (ch) { | 
|  | case CR: | 
|  | case LF: | 
|  | goto invalid; | 
|  | default: | 
|  | state = sw_argument; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case sw_literal: | 
|  | if (ch >= '0' && ch <= '9') { | 
|  | s->literal_len = s->literal_len * 10 + (ch - '0'); | 
|  | break; | 
|  | } | 
|  | if (ch == '}') { | 
|  | state = sw_start_literal_argument; | 
|  | break; | 
|  | } | 
|  | if (ch == '+') { | 
|  | state = sw_no_sync_literal_argument; | 
|  | break; | 
|  | } | 
|  | goto invalid; | 
|  |  | 
|  | case sw_no_sync_literal_argument: | 
|  | if (ch == '}') { | 
|  | s->no_sync_literal = 1; | 
|  | state = sw_start_literal_argument; | 
|  | break; | 
|  | } | 
|  | goto invalid; | 
|  |  | 
|  | case sw_start_literal_argument: | 
|  | switch (ch) { | 
|  | case CR: | 
|  | break; | 
|  | case LF: | 
|  | s->buffer->pos = p + 1; | 
|  | s->arg_start = p + 1; | 
|  | if (s->no_sync_literal == 0) { | 
|  | s->state = sw_literal_argument; | 
|  | return NGX_IMAP_NEXT; | 
|  | } | 
|  | state = sw_literal_argument; | 
|  | s->no_sync_literal = 0; | 
|  | break; | 
|  | default: | 
|  | goto invalid; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case sw_literal_argument: | 
|  | if (s->literal_len && --s->literal_len) { | 
|  | break; | 
|  | } | 
|  |  | 
|  | arg = ngx_array_push(&s->args); | 
|  | if (arg == NULL) { | 
|  | return NGX_ERROR; | 
|  | } | 
|  | arg->len = p + 1 - s->arg_start; | 
|  | arg->data = s->arg_start; | 
|  | s->arg_start = NULL; | 
|  | state = sw_end_literal_argument; | 
|  |  | 
|  | break; | 
|  |  | 
|  | case sw_end_literal_argument: | 
|  | switch (ch) { | 
|  | case '{': | 
|  | if (s->args.nelts <= 2) { | 
|  | state = sw_literal; | 
|  | break; | 
|  | } | 
|  | goto invalid; | 
|  | case CR: | 
|  | state = sw_almost_done; | 
|  | break; | 
|  | case LF: | 
|  | goto done; | 
|  | default: | 
|  | state = sw_spaces_before_argument; | 
|  | break; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case sw_almost_done: | 
|  | switch (ch) { | 
|  | case LF: | 
|  | goto done; | 
|  | default: | 
|  | goto invalid; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | s->buffer->pos = p; | 
|  | s->state = state; | 
|  |  | 
|  | return NGX_AGAIN; | 
|  |  | 
|  | done: | 
|  |  | 
|  | s->buffer->pos = p + 1; | 
|  |  | 
|  | if (s->arg_start) { | 
|  | arg = ngx_array_push(&s->args); | 
|  | if (arg == NULL) { | 
|  | return NGX_ERROR; | 
|  | } | 
|  | arg->len = s->arg_end - s->arg_start; | 
|  | arg->data = s->arg_start; | 
|  |  | 
|  | s->arg_start = NULL; | 
|  | s->cmd_start = NULL; | 
|  | s->quoted = 0; | 
|  | s->no_sync_literal = 0; | 
|  | s->literal_len = 0; | 
|  | } | 
|  |  | 
|  | s->state = (s->command != NGX_IMAP_AUTHENTICATE) ? sw_start : sw_argument; | 
|  |  | 
|  | return NGX_OK; | 
|  |  | 
|  | invalid: | 
|  |  | 
|  | s->state = sw_start; | 
|  | s->quoted = 0; | 
|  | s->no_sync_literal = 0; | 
|  | s->literal_len = 0; | 
|  |  | 
|  | return NGX_MAIL_PARSE_INVALID_COMMAND; | 
|  | } | 
|  |  | 
|  |  | 
|  | ngx_int_t | 
|  | ngx_mail_smtp_parse_command(ngx_mail_session_t *s) | 
|  | { | 
|  | u_char      ch, *p, *c, c0, c1, c2, c3; | 
|  | ngx_str_t  *arg; | 
|  | enum { | 
|  | sw_start = 0, | 
|  | sw_command, | 
|  | sw_invalid, | 
|  | sw_spaces_before_argument, | 
|  | sw_argument, | 
|  | sw_almost_done | 
|  | } state; | 
|  |  | 
|  | state = s->state; | 
|  |  | 
|  | for (p = s->buffer->pos; p < s->buffer->last; p++) { | 
|  | ch = *p; | 
|  |  | 
|  | switch (state) { | 
|  |  | 
|  | /* SMTP command */ | 
|  | case sw_start: | 
|  | s->cmd_start = p; | 
|  | state = sw_command; | 
|  |  | 
|  | /* fall through */ | 
|  |  | 
|  | case sw_command: | 
|  | if (ch == ' ' || ch == CR || ch == LF) { | 
|  | c = s->cmd_start; | 
|  |  | 
|  | if (p - c == 4) { | 
|  |  | 
|  | c0 = ngx_toupper(c[0]); | 
|  | c1 = ngx_toupper(c[1]); | 
|  | c2 = ngx_toupper(c[2]); | 
|  | c3 = ngx_toupper(c[3]); | 
|  |  | 
|  | if (c0 == 'H' && c1 == 'E' && c2 == 'L' && c3 == 'O') | 
|  | { | 
|  | s->command = NGX_SMTP_HELO; | 
|  |  | 
|  | } else if (c0 == 'E' && c1 == 'H' && c2 == 'L' && c3 == 'O') | 
|  | { | 
|  | s->command = NGX_SMTP_EHLO; | 
|  |  | 
|  | } else if (c0 == 'Q' && c1 == 'U' && c2 == 'I' && c3 == 'T') | 
|  | { | 
|  | s->command = NGX_SMTP_QUIT; | 
|  |  | 
|  | } else if (c0 == 'A' && c1 == 'U' && c2 == 'T' && c3 == 'H') | 
|  | { | 
|  | s->command = NGX_SMTP_AUTH; | 
|  |  | 
|  | } else if (c0 == 'N' && c1 == 'O' && c2 == 'O' && c3 == 'P') | 
|  | { | 
|  | s->command = NGX_SMTP_NOOP; | 
|  |  | 
|  | } else if (c0 == 'M' && c1 == 'A' && c2 == 'I' && c3 == 'L') | 
|  | { | 
|  | s->command = NGX_SMTP_MAIL; | 
|  |  | 
|  | } else if (c0 == 'R' && c1 == 'S' && c2 == 'E' && c3 == 'T') | 
|  | { | 
|  | s->command = NGX_SMTP_RSET; | 
|  |  | 
|  | } else if (c0 == 'R' && c1 == 'C' && c2 == 'P' && c3 == 'T') | 
|  | { | 
|  | s->command = NGX_SMTP_RCPT; | 
|  |  | 
|  | } else if (c0 == 'V' && c1 == 'R' && c2 == 'F' && c3 == 'Y') | 
|  | { | 
|  | s->command = NGX_SMTP_VRFY; | 
|  |  | 
|  | } else if (c0 == 'E' && c1 == 'X' && c2 == 'P' && c3 == 'N') | 
|  | { | 
|  | s->command = NGX_SMTP_EXPN; | 
|  |  | 
|  | } else if (c0 == 'H' && c1 == 'E' && c2 == 'L' && c3 == 'P') | 
|  | { | 
|  | s->command = NGX_SMTP_HELP; | 
|  |  | 
|  | } else { | 
|  | goto invalid; | 
|  | } | 
|  | #if (NGX_MAIL_SSL) | 
|  | } else if (p - c == 8) { | 
|  |  | 
|  | if ((c[0] == 'S'|| c[0] == 's') | 
|  | && (c[1] == 'T'|| c[1] == 't') | 
|  | && (c[2] == 'A'|| c[2] == 'a') | 
|  | && (c[3] == 'R'|| c[3] == 'r') | 
|  | && (c[4] == 'T'|| c[4] == 't') | 
|  | && (c[5] == 'T'|| c[5] == 't') | 
|  | && (c[6] == 'L'|| c[6] == 'l') | 
|  | && (c[7] == 'S'|| c[7] == 's')) | 
|  | { | 
|  | s->command = NGX_SMTP_STARTTLS; | 
|  |  | 
|  | } else { | 
|  | goto invalid; | 
|  | } | 
|  | #endif | 
|  | } else { | 
|  | goto invalid; | 
|  | } | 
|  |  | 
|  | s->cmd.data = s->cmd_start; | 
|  | s->cmd.len = p - s->cmd_start; | 
|  |  | 
|  | switch (ch) { | 
|  | case ' ': | 
|  | state = sw_spaces_before_argument; | 
|  | break; | 
|  | case CR: | 
|  | state = sw_almost_done; | 
|  | break; | 
|  | case LF: | 
|  | goto done; | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')) { | 
|  | goto invalid; | 
|  | } | 
|  |  | 
|  | break; | 
|  |  | 
|  | case sw_invalid: | 
|  | goto invalid; | 
|  |  | 
|  | case sw_spaces_before_argument: | 
|  | switch (ch) { | 
|  | case ' ': | 
|  | break; | 
|  | case CR: | 
|  | state = sw_almost_done; | 
|  | s->arg_end = p; | 
|  | break; | 
|  | case LF: | 
|  | s->arg_end = p; | 
|  | goto done; | 
|  | default: | 
|  | if (s->args.nelts <= 10) { | 
|  | state = sw_argument; | 
|  | s->arg_start = p; | 
|  | break; | 
|  | } | 
|  | goto invalid; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case sw_argument: | 
|  | switch (ch) { | 
|  | case ' ': | 
|  | case CR: | 
|  | case LF: | 
|  | arg = ngx_array_push(&s->args); | 
|  | if (arg == NULL) { | 
|  | return NGX_ERROR; | 
|  | } | 
|  | arg->len = p - s->arg_start; | 
|  | arg->data = s->arg_start; | 
|  | s->arg_start = NULL; | 
|  |  | 
|  | switch (ch) { | 
|  | case ' ': | 
|  | state = sw_spaces_before_argument; | 
|  | break; | 
|  | case CR: | 
|  | state = sw_almost_done; | 
|  | break; | 
|  | case LF: | 
|  | goto done; | 
|  | } | 
|  | break; | 
|  |  | 
|  | default: | 
|  | break; | 
|  | } | 
|  | break; | 
|  |  | 
|  | case sw_almost_done: | 
|  | switch (ch) { | 
|  | case LF: | 
|  | goto done; | 
|  | default: | 
|  | goto invalid; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | s->buffer->pos = p; | 
|  | s->state = state; | 
|  |  | 
|  | return NGX_AGAIN; | 
|  |  | 
|  | done: | 
|  |  | 
|  | s->buffer->pos = p + 1; | 
|  |  | 
|  | if (s->arg_start) { | 
|  | arg = ngx_array_push(&s->args); | 
|  | if (arg == NULL) { | 
|  | return NGX_ERROR; | 
|  | } | 
|  | arg->len = s->arg_end - s->arg_start; | 
|  | arg->data = s->arg_start; | 
|  | s->arg_start = NULL; | 
|  | } | 
|  |  | 
|  | s->state = (s->command != NGX_SMTP_AUTH) ? sw_start : sw_argument; | 
|  |  | 
|  | return NGX_OK; | 
|  |  | 
|  | invalid: | 
|  |  | 
|  | s->state = sw_invalid; | 
|  | s->arg_start = NULL; | 
|  |  | 
|  | /* skip invalid command till LF */ | 
|  |  | 
|  | for (p = s->buffer->pos; p < s->buffer->last; p++) { | 
|  | if (*p == LF) { | 
|  | s->state = sw_start; | 
|  | p++; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | s->buffer->pos = p; | 
|  |  | 
|  | return NGX_MAIL_PARSE_INVALID_COMMAND; | 
|  | } | 
|  |  | 
|  |  | 
|  | ngx_int_t | 
|  | ngx_mail_auth_parse(ngx_mail_session_t *s, ngx_connection_t *c) | 
|  | { | 
|  | ngx_str_t                 *arg; | 
|  |  | 
|  | #if (NGX_MAIL_SSL) | 
|  | if (ngx_mail_starttls_only(s, c)) { | 
|  | return NGX_MAIL_PARSE_INVALID_COMMAND; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | if (s->args.nelts == 0) { | 
|  | return NGX_MAIL_PARSE_INVALID_COMMAND; | 
|  | } | 
|  |  | 
|  | arg = s->args.elts; | 
|  |  | 
|  | if (arg[0].len == 5) { | 
|  |  | 
|  | if (ngx_strncasecmp(arg[0].data, (u_char *) "LOGIN", 5) == 0) { | 
|  |  | 
|  | if (s->args.nelts == 1) { | 
|  | return NGX_MAIL_AUTH_LOGIN; | 
|  | } | 
|  |  | 
|  | if (s->args.nelts == 2) { | 
|  | return NGX_MAIL_AUTH_LOGIN_USERNAME; | 
|  | } | 
|  |  | 
|  | return NGX_MAIL_PARSE_INVALID_COMMAND; | 
|  | } | 
|  |  | 
|  | if (ngx_strncasecmp(arg[0].data, (u_char *) "PLAIN", 5) == 0) { | 
|  |  | 
|  | if (s->args.nelts == 1) { | 
|  | return NGX_MAIL_AUTH_PLAIN; | 
|  | } | 
|  |  | 
|  | if (s->args.nelts == 2) { | 
|  | return ngx_mail_auth_plain(s, c, 1); | 
|  | } | 
|  | } | 
|  |  | 
|  | return NGX_MAIL_PARSE_INVALID_COMMAND; | 
|  | } | 
|  |  | 
|  | if (arg[0].len == 8) { | 
|  |  | 
|  | if (ngx_strncasecmp(arg[0].data, (u_char *) "CRAM-MD5", 8) == 0) { | 
|  |  | 
|  | if (s->args.nelts != 1) { | 
|  | return NGX_MAIL_PARSE_INVALID_COMMAND; | 
|  | } | 
|  |  | 
|  | return NGX_MAIL_AUTH_CRAM_MD5; | 
|  | } | 
|  |  | 
|  | if (ngx_strncasecmp(arg[0].data, (u_char *) "EXTERNAL", 8) == 0) { | 
|  |  | 
|  | if (s->args.nelts == 1) { | 
|  | return NGX_MAIL_AUTH_EXTERNAL; | 
|  | } | 
|  |  | 
|  | if (s->args.nelts == 2) { | 
|  | return ngx_mail_auth_external(s, c, 1); | 
|  | } | 
|  | } | 
|  |  | 
|  | return NGX_MAIL_PARSE_INVALID_COMMAND; | 
|  | } | 
|  |  | 
|  | return NGX_MAIL_PARSE_INVALID_COMMAND; | 
|  | } |