976 lines
25 KiB
C
976 lines
25 KiB
C
|
|
/*
|
|
* 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_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) {
|
|
|
|
/* POP3 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 == '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;
|
|
}
|
|
|
|
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;
|
|
break;
|
|
case LF:
|
|
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;
|
|
s->state = (s->command != NGX_POP3_AUTH) ? sw_start : sw_argument;
|
|
|
|
return NGX_OK;
|
|
|
|
invalid:
|
|
|
|
s->state = sw_invalid;
|
|
|
|
/* skip invalid command till LF */
|
|
|
|
for ( /* void */ ; p < s->buffer->last; p++) {
|
|
if (*p == LF) {
|
|
s->state = sw_start;
|
|
s->buffer->pos = p + 1;
|
|
return NGX_MAIL_PARSE_INVALID_COMMAND;
|
|
}
|
|
}
|
|
|
|
s->buffer->pos = p;
|
|
|
|
return NGX_AGAIN;
|
|
}
|
|
|
|
|
|
ngx_int_t
|
|
ngx_mail_imap_parse_command(ngx_mail_session_t *s)
|
|
{
|
|
u_char ch, *p, *c, *dst, *src, *end;
|
|
ngx_str_t *arg;
|
|
enum {
|
|
sw_start = 0,
|
|
sw_tag,
|
|
sw_invalid,
|
|
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:
|
|
s->tag_start = p;
|
|
state = sw_tag;
|
|
|
|
/* fall through */
|
|
|
|
case sw_tag:
|
|
switch (ch) {
|
|
case ' ':
|
|
s->tag.len = p - s->tag_start + 1;
|
|
s->tag.data = s->tag_start;
|
|
state = sw_spaces_before_command;
|
|
break;
|
|
case CR:
|
|
case LF:
|
|
goto invalid;
|
|
default:
|
|
if ((ch < 'A' || ch > 'Z') && (ch < 'a' || ch > 'z')
|
|
&& (ch < '0' || ch > '9') && ch != '-' && ch != '.'
|
|
&& ch != '_')
|
|
{
|
|
goto invalid;
|
|
}
|
|
if (p - s->tag_start > 31) {
|
|
goto invalid;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
|
|
case sw_invalid:
|
|
goto invalid;
|
|
|
|
case sw_spaces_before_command:
|
|
switch (ch) {
|
|
case ' ':
|
|
break;
|
|
case CR:
|
|
case LF:
|
|
goto invalid;
|
|
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;
|
|
}
|
|
|
|
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_spaces_before_argument:
|
|
switch (ch) {
|
|
case ' ':
|
|
break;
|
|
case CR:
|
|
state = sw_almost_done;
|
|
break;
|
|
case LF:
|
|
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;
|
|
|
|
if (s->backslash) {
|
|
dst = s->arg_start;
|
|
end = p;
|
|
|
|
for (src = dst; src < end; dst++) {
|
|
*dst = *src;
|
|
if (*src++ == '\\') {
|
|
*dst = *src++;
|
|
}
|
|
}
|
|
|
|
arg->len = dst - s->arg_start;
|
|
s->backslash = 0;
|
|
}
|
|
|
|
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;
|
|
s->state = (s->command != NGX_IMAP_AUTHENTICATE) ? sw_start : sw_argument;
|
|
|
|
return NGX_OK;
|
|
|
|
invalid:
|
|
|
|
s->state = sw_invalid;
|
|
s->quoted = 0;
|
|
s->backslash = 0;
|
|
s->no_sync_literal = 0;
|
|
s->literal_len = 0;
|
|
|
|
/* skip invalid command till LF */
|
|
|
|
for ( /* void */ ; p < s->buffer->last; p++) {
|
|
if (*p == LF) {
|
|
s->state = sw_start;
|
|
s->buffer->pos = p + 1;
|
|
|
|
/* detect non-synchronizing literals */
|
|
|
|
if ((size_t) (p - s->buffer->start) > sizeof("{1+}") - 1) {
|
|
p--;
|
|
|
|
if (*p == CR) {
|
|
p--;
|
|
}
|
|
|
|
if (*p == '}' && *(p - 1) == '+') {
|
|
s->quit = 1;
|
|
}
|
|
}
|
|
|
|
return NGX_MAIL_PARSE_INVALID_COMMAND;
|
|
}
|
|
}
|
|
|
|
s->buffer->pos = p;
|
|
|
|
return NGX_AGAIN;
|
|
}
|
|
|
|
|
|
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;
|
|
break;
|
|
case LF:
|
|
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;
|
|
s->state = (s->command != NGX_SMTP_AUTH) ? sw_start : sw_argument;
|
|
|
|
return NGX_OK;
|
|
|
|
invalid:
|
|
|
|
s->state = sw_invalid;
|
|
|
|
/* skip invalid command till LF */
|
|
|
|
for ( /* void */ ; p < s->buffer->last; p++) {
|
|
if (*p == LF) {
|
|
s->state = sw_start;
|
|
s->buffer->pos = p + 1;
|
|
return NGX_MAIL_PARSE_INVALID_COMMAND;
|
|
}
|
|
}
|
|
|
|
s->buffer->pos = p;
|
|
|
|
return NGX_AGAIN;
|
|
}
|
|
|
|
|
|
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;
|
|
}
|