blob: d8c436b87d5b3e89a1bf7c168f30fff2148b2472 [file] [log] [blame]
/*
* Copyright (C) Igor Sysoev
*/
#include <ngx_config.h>
#include <ngx_core.h>
#include <ngx_event.h>
#include <ngx_imap.h>
ngx_int_t ngx_imap_parse_command(ngx_imap_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_IMAP_PARSE_INVALID_COMMAND;
case LF:
s->state = sw_start;
return NGX_IMAP_PARSE_INVALID_COMMAND;
}
break;
case sw_spaces_before_command:
switch (ch) {
case ' ':
break;
case CR:
s->state = sw_start;
return NGX_IMAP_PARSE_INVALID_COMMAND;
case LF:
s->state = sw_start;
return NGX_IMAP_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;
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;
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:
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 = sw_start;
return NGX_OK;
invalid:
s->state = sw_start;
s->quoted = 0;
s->no_sync_literal = 0;
s->literal_len = 0;
return NGX_IMAP_PARSE_INVALID_COMMAND;
}
ngx_int_t ngx_pop3_parse_command(ngx_imap_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 == '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 == 'N' && c1 == 'O' && c2 == 'O' && c3 == 'P')
{
s->command = NGX_POP3_NOOP;
} 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) {
/*
* the space should be considered part of the at username
* or password, but not of argument in other commands
*
* 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 = sw_start;
return NGX_OK;
invalid:
s->state = sw_start;
return NGX_IMAP_PARSE_INVALID_COMMAND;
}