Skip to content

Commit

Permalink
lib-imap: imap-parser: Turned the fatal error flag into a proper erro…
Browse files Browse the repository at this point in the history
…r code.
  • Loading branch information
stephanbosch committed May 29, 2016
1 parent 7bb3714 commit 0adc24c
Show file tree
Hide file tree
Showing 7 changed files with 85 additions and 29 deletions.
11 changes: 8 additions & 3 deletions src/imap-login/imap-login-client.c
Expand Up @@ -55,15 +55,20 @@ static bool client_handle_parser_error(struct imap_client *client,
struct imap_parser *parser)
{
const char *msg;
bool fatal;
enum imap_parser_error parse_error;

msg = imap_parser_get_error(parser, &fatal);
if (fatal) {
msg = imap_parser_get_error(parser, &parse_error);
switch (parse_error) {
case IMAP_PARSE_ERROR_NONE:
i_unreached();
case IMAP_PARSE_ERROR_LITERAL_TOO_BIG:
client_send_reply(&client->common,
IMAP_CMD_REPLY_BYE, msg);
client_destroy(&client->common,
t_strconcat("Disconnected: ", msg, NULL));
return FALSE;
default:
break;
}

client_send_reply(&client->common, IMAP_CMD_REPLY_BAD, msg);
Expand Down
31 changes: 22 additions & 9 deletions src/imap/cmd-append.c
Expand Up @@ -392,7 +392,8 @@ static bool cmd_append_continue_catenate(struct client_command_context *cmd)
struct cmd_append_context *ctx = cmd->context;
const struct imap_arg *args;
const char *msg;
bool fatal, nonsync = FALSE;
enum imap_parser_error parse_error;
bool nonsync = FALSE;
int ret;

if (cmd->cancel) {
Expand All @@ -412,11 +413,17 @@ static bool cmd_append_continue_catenate(struct client_command_context *cmd)
IMAP_PARSE_FLAG_INSIDE_LIST, &args);
} while (ret > 0 && !catenate_args_can_stop(ctx, args));
if (ret == -1) {
msg = imap_parser_get_error(ctx->save_parser, &fatal);
if (fatal)
msg = imap_parser_get_error(ctx->save_parser, &parse_error);
switch (parse_error) {
case IMAP_PARSE_ERROR_NONE:
i_unreached();
case IMAP_PARSE_ERROR_LITERAL_TOO_BIG:
client_disconnect_with_error(client, msg);
else if (!ctx->failed)
client_send_command_error(cmd, msg);
break;
default:
if (!ctx->failed)
client_send_command_error(cmd, msg);
}
client->input_skip_line = TRUE;
cmd_append_finish(ctx);
return TRUE;
Expand Down Expand Up @@ -714,8 +721,9 @@ static bool cmd_append_parse_new_msg(struct client_command_context *cmd)
struct cmd_append_context *ctx = cmd->context;
const struct imap_arg *args;
const char *msg;
enum imap_parser_error parse_error;
unsigned int arg_min_count;
bool fatal, nonsync, last_literal;
bool nonsync, last_literal;
int ret;

/* this function gets called 1) after parsing APPEND <mailbox> and
Expand Down Expand Up @@ -748,11 +756,16 @@ static bool cmd_append_parse_new_msg(struct client_command_context *cmd)
!cmd_append_args_can_stop(ctx, args, &last_literal));
if (ret == -1) {
if (!ctx->failed) {
msg = imap_parser_get_error(ctx->save_parser, &fatal);
if (fatal)
msg = imap_parser_get_error(ctx->save_parser, &parse_error);
switch (parse_error) {
case IMAP_PARSE_ERROR_NONE:
i_unreached();
case IMAP_PARSE_ERROR_LITERAL_TOO_BIG:
client_disconnect_with_error(client, msg);
else
break;
default:
client_send_command_error(cmd, msg);
}
}
cmd_append_finish(ctx);
return TRUE;
Expand Down
15 changes: 10 additions & 5 deletions src/imap/cmd-setmetadata.c
Expand Up @@ -46,8 +46,8 @@ cmd_setmetadata_parse_entryvalue(struct imap_setmetadata_context *ctx,
{
const struct imap_arg *args;
const char *name, *error;
enum imap_parser_error parse_error;
int ret;
bool fatal;

/* parse the entry name */
ret = imap_parser_read_args(ctx->parser, 1,
Expand All @@ -72,12 +72,17 @@ cmd_setmetadata_parse_entryvalue(struct imap_setmetadata_context *ctx,
if (ret < 0) {
if (ret == -2)
return 0;
error = imap_parser_get_error(ctx->parser, &fatal);
if (fatal) {
error = imap_parser_get_error(ctx->parser, &parse_error);
switch (parse_error) {
case IMAP_PARSE_ERROR_NONE:
i_unreached();
case IMAP_PARSE_ERROR_LITERAL_TOO_BIG:
client_disconnect_with_error(ctx->cmd->client, error);
return -1;
break;
default:
client_send_command_error(ctx->cmd, error);
break;
}
client_send_command_error(ctx->cmd, error);
return -1;
}
if (args[1].type == IMAP_ARG_EOL) {
Expand Down
11 changes: 8 additions & 3 deletions src/imap/imap-client.c
Expand Up @@ -535,13 +535,18 @@ void client_send_command_error(struct client_command_context *cmd,
{
struct client *client = cmd->client;
const char *error, *cmd_name;
bool fatal;
enum imap_parser_error parse_error;

if (msg == NULL) {
msg = imap_parser_get_error(cmd->parser, &fatal);
if (fatal) {
msg = imap_parser_get_error(cmd->parser, &parse_error);
switch (parse_error) {
case IMAP_PARSE_ERROR_NONE:
i_unreached();
case IMAP_PARSE_ERROR_LITERAL_TOO_BIG:
client_disconnect_with_error(client, msg);
return;
default:
break;
}
}

Expand Down
27 changes: 22 additions & 5 deletions src/lib-imap/imap-parser.c
Expand Up @@ -54,6 +54,7 @@ struct imap_parser {
int str_first_escape; /* ARG_PARSE_STRING: index to first '\' */
uoff_t literal_size; /* ARG_PARSE_LITERAL: string size */

enum imap_parser_error error;
const char *error_msg;

unsigned int literal_skip_crlf:1;
Expand Down Expand Up @@ -120,6 +121,7 @@ void imap_parser_reset(struct imap_parser *parser)
parser->str_first_escape = 0;
parser->literal_size = 0;

parser->error = IMAP_PARSE_ERROR_NONE;
parser->error_msg = NULL;

parser->literal_skip_crlf = FALSE;
Expand All @@ -135,10 +137,11 @@ void imap_parser_set_streams(struct imap_parser *parser, struct istream *input,
parser->output = output;
}

const char *imap_parser_get_error(struct imap_parser *parser, bool *fatal)
const char *imap_parser_get_error(struct imap_parser *parser,
enum imap_parser_error *error_r)
{
if (fatal != NULL)
*fatal = parser->fatal_error;
if (error_r != NULL)
*error_r = parser->error;
return parser->error_msg;
}

Expand Down Expand Up @@ -195,6 +198,7 @@ static int imap_parser_close_list(struct imap_parser *parser)
return TRUE;
}
parser->error_msg = "Unexpected ')'";
parser->error = IMAP_PARSE_ERROR_BAD_SYNTAX;
return FALSE;
}

Expand Down Expand Up @@ -302,6 +306,7 @@ static int is_valid_atom_char(struct imap_parser *parser, char chr)

if ((parser->flags & IMAP_PARSE_FLAG_ATOM_ALLCHARS) != 0)
return TRUE;
parser->error = IMAP_PARSE_ERROR_BAD_SYNTAX;
parser->error_msg = error_msg;
return FALSE;
}
Expand All @@ -323,6 +328,7 @@ static int imap_parser_read_atom(struct imap_parser *parser,
break;
} else if ((parser->flags &
IMAP_PARSE_FLAG_ATOM_ALLCHARS) == 0) {
parser->error = IMAP_PARSE_ERROR_BAD_SYNTAX;
parser->error_msg = "Unexpected ')'";
return FALSE;
}
Expand Down Expand Up @@ -369,6 +375,7 @@ static int imap_parser_read_string(struct imap_parser *parser,
a linebreak.. */
if (is_linebreak(data[i]) &&
(parser->flags & IMAP_PARSE_FLAG_MULTILINE_STR) == 0) {
parser->error = IMAP_PARSE_ERROR_BAD_SYNTAX;
parser->error_msg = "Missing '\"'";
return FALSE;
}
Expand All @@ -385,8 +392,8 @@ static int imap_parser_literal_end(struct imap_parser *parser)
parser->literal_size >
parser->max_line_size - parser->line_size) {
/* too long string, abort. */
parser->error = IMAP_PARSE_ERROR_LITERAL_TOO_BIG;
parser->error_msg = "Literal size too large";
parser->fatal_error = TRUE;
return FALSE;
}

Expand Down Expand Up @@ -418,6 +425,7 @@ static int imap_parser_read_literal(struct imap_parser *parser,
}

if (parser->literal_nonsync) {
parser->error = IMAP_PARSE_ERROR_BAD_SYNTAX;
parser->error_msg = "Expecting '}' after '+'";
return FALSE;
}
Expand All @@ -428,6 +436,7 @@ static int imap_parser_read_literal(struct imap_parser *parser,
}

if (data[i] < '0' || data[i] > '9') {
parser->error = IMAP_PARSE_ERROR_BAD_SYNTAX;
parser->error_msg = "Invalid literal size";
return FALSE;
}
Expand All @@ -437,6 +446,7 @@ static int imap_parser_read_literal(struct imap_parser *parser,

if (parser->literal_size < prev_size) {
/* wrapped around, abort. */
parser->error = IMAP_PARSE_ERROR_LITERAL_TOO_BIG;
parser->error_msg = "Literal size too large";
return FALSE;
}
Expand Down Expand Up @@ -465,6 +475,7 @@ static int imap_parser_read_literal_data(struct imap_parser *parser,
}

if (*data != '\n') {
parser->error = IMAP_PARSE_ERROR_BAD_SYNTAX;
parser->error_msg = "Missing LF after literal size";
return FALSE;
}
Expand Down Expand Up @@ -578,13 +589,15 @@ static int imap_parser_read_arg(struct imap_parser *parser)
return FALSE;
}
if (data[1] != '\n') {
parser->error = IMAP_PARSE_ERROR_BAD_SYNTAX;
parser->error_msg = "CR sent without LF";
return FALSE;
}
/* fall through */
case '\n':
/* unexpected end of line */
if ((parser->flags & IMAP_PARSE_FLAG_INSIDE_LIST) != 0) {
parser->error = IMAP_PARSE_ERROR_BAD_SYNTAX;
parser->error_msg = "Missing ')'";
return FALSE;
}
Expand All @@ -605,6 +618,7 @@ static int imap_parser_read_arg(struct imap_parser *parser)
break;
}
if ((parser->flags & IMAP_PARSE_FLAG_LITERAL8) == 0) {
parser->error = IMAP_PARSE_ERROR_BAD_SYNTAX;
parser->error_msg = "literal8 not allowed here";
return FALSE;
}
Expand Down Expand Up @@ -671,6 +685,7 @@ static int imap_parser_read_arg(struct imap_parser *parser)
if (parser->cur_pos == data_size)
return FALSE;
if (data[parser->cur_pos] != '{') {
parser->error = IMAP_PARSE_ERROR_BAD_SYNTAX;
parser->error_msg = "Expected '{'";
return FALSE;
}
Expand Down Expand Up @@ -722,6 +737,7 @@ static int finish_line(struct imap_parser *parser, unsigned int count,

if (parser->list_arg != NULL && !parser->literal_size_return &&
(parser->flags & IMAP_PARSE_FLAG_STOP_AT_LIST) == 0) {
parser->error = IMAP_PARSE_ERROR_BAD_SYNTAX;
parser->error_msg = "Missing ')'";
*args_r = NULL;
return -1;
Expand Down Expand Up @@ -755,12 +771,13 @@ int imap_parser_read_args(struct imap_parser *parser, unsigned int count,
break;

if (parser->line_size > parser->max_line_size) {
parser->error = IMAP_PARSE_ERROR_LINE_TOO_LONG;
parser->error_msg = "IMAP command line too large";
break;
}
}

if (parser->error_msg != NULL) {
if (parser->error != IMAP_PARSE_ERROR_NONE) {
/* error, abort */
parser->line_size += parser->cur_pos;
i_stream_skip(parser->input, parser->cur_pos);
Expand Down
13 changes: 11 additions & 2 deletions src/lib-imap/imap-parser.h
Expand Up @@ -29,6 +29,15 @@ enum imap_parser_flags {
IMAP_PARSE_FLAG_STOP_AT_LIST = 0x100
};

enum imap_parser_error {
/* not fatal */
IMAP_PARSE_ERROR_NONE = 0,
IMAP_PARSE_ERROR_BAD_SYNTAX,
IMAP_PARSE_ERROR_LINE_TOO_LONG,
/* fatal */
IMAP_PARSE_ERROR_LITERAL_TOO_BIG
};

struct imap_parser;

/* Create new IMAP argument parser. output is used for sending command
Expand Down Expand Up @@ -58,8 +67,8 @@ void imap_parser_set_streams(struct imap_parser *parser, struct istream *input,
/* Return the last error in parser. fatal is set to TRUE if there's no way to
continue parsing, currently only if too large non-sync literal size was
given. */
const char *imap_parser_get_error(struct imap_parser *parser, bool *fatal)
ATTR_NULL(2);
const char *imap_parser_get_error(struct imap_parser *parser,
enum imap_parser_error *error_r) ATTR_NULL(2);

/* Read a number of arguments. This function doesn't call i_stream_read(), you
need to do that. Returns number of arguments read (may be less than count
Expand Down
6 changes: 4 additions & 2 deletions src/lib-imap/test-imap-parser.c
Expand Up @@ -12,7 +12,7 @@ static void test_imap_parser_crlf(void)
struct imap_parser *parser;
const struct imap_arg *args;
unsigned int i;
bool fatal;
enum imap_parser_error parse_error;

test_begin("imap parser crlf handling");
input = test_istream_create(test_input);
Expand Down Expand Up @@ -42,7 +42,9 @@ static void test_imap_parser_crlf(void)
test_istream_set_size(input, ++i);
(void)i_stream_read(input);
test_assert(imap_parser_read_args(parser, 0, 0, &args) == -1);
test_assert(strcmp(imap_parser_get_error(parser, &fatal), "CR sent without LF") == 0 && !fatal);
test_assert(strcmp(imap_parser_get_error
(parser, &parse_error), "CR sent without LF") == 0 &&
parse_error == IMAP_PARSE_ERROR_BAD_SYNTAX);

imap_parser_unref(&parser);
i_stream_destroy(&input);
Expand Down

0 comments on commit 0adc24c

Please sign in to comment.