# HG changeset patch # User Timo Sirainen # Date 1450193529 -7200 # Node ID 74b03ecb79fd17d42e2d6c120392e81e3d9474ba # Parent 9f3e9150b6a3a5307787b346053ea7ed3fba2eac lib-mail: message-parser didn't detect MIME part boundaries in the middle of MIME part headers. Instead the --boundary line was thought to be part of the header itself. diff -r 9f3e9150b6a3 -r 74b03ecb79fd src/lib-mail/message-parser.c --- a/src/lib-mail/message-parser.c Tue Dec 15 17:29:11 2015 +0200 +++ b/src/lib-mail/message-parser.c Tue Dec 15 17:32:09 2015 +0200 @@ -1,6 +1,7 @@ /* Copyright (c) 2002-2015 Dovecot authors, see the included COPYING file */ #include "lib.h" +#include "buffer.h" #include "str.h" #include "istream.h" #include "rfc822-parser.h" @@ -37,6 +38,7 @@ unsigned int want_count; struct message_header_parser_ctx *hdr_parser_ctx; + unsigned int prev_hdr_newline_size; int (*parse_next_block)(struct message_parser_ctx *ctx, struct message_block *block_r); @@ -510,17 +512,42 @@ { struct message_part *part = ctx->part; struct message_header_line *hdr; + struct message_boundary *boundary; + bool full; int ret; - if (ctx->skip > 0) { - i_stream_skip(ctx->input, ctx->skip); - ctx->skip = 0; - } + if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0) + return ret; - ret = message_parse_header_next(ctx->hdr_parser_ctx, &hdr); - if (ret == 0 || (ret < 0 && ctx->input->stream_errno != 0)) { - ctx->want_count = i_stream_get_data_size(ctx->input) + 1; - return ret; + /* before parsing the header see if we can find a --boundary from here. + we're guaranteed to be at the beginning of the line here. */ + ret = ctx->boundaries == NULL ? -1 : + boundary_line_find(ctx, block_r->data, + block_r->size, full, &boundary); + if (ret < 0) { + /* no boundary */ + ret = message_parse_header_next(ctx->hdr_parser_ctx, &hdr); + if (ret == 0 || (ret < 0 && ctx->input->stream_errno != 0)) { + ctx->want_count = i_stream_get_data_size(ctx->input) + 1; + return ret; + } + } else if (ret == 0) { + /* need more data */ + return 0; + } else { + /* boundary found. stop parsing headers here. The previous + [CR]LF belongs to the MIME boundary though. */ + if (ctx->prev_hdr_newline_size > 0) { + i_assert(ctx->part->header_size.lines > 0); + ctx->part->header_size.lines--; + ctx->part->header_size.physical_size -= + ctx->prev_hdr_newline_size; + ctx->part->header_size.virtual_size -= + ctx->prev_hdr_newline_size; + if (ctx->prev_hdr_newline_size == 1) + ctx->part->header_size.virtual_size--; + } + hdr = NULL; } if (hdr != NULL) { @@ -543,6 +570,8 @@ block_r->hdr = hdr; block_r->size = 0; + ctx->prev_hdr_newline_size = hdr->no_newline ? 0 : + (hdr->crlf_newline ? 2 : 1); return 1; } @@ -608,6 +637,7 @@ message_parse_header_init(ctx->input, &ctx->part->header_size, ctx->hdr_flags); ctx->part_seen_content_type = FALSE; + ctx->prev_hdr_newline_size = 0; ctx->parse_next_block = parse_next_header; return parse_next_header(ctx, block_r); diff -r 9f3e9150b6a3 -r 74b03ecb79fd src/lib-mail/test-message-parser.c --- a/src/lib-mail/test-message-parser.c Tue Dec 15 17:29:11 2015 +0200 +++ b/src/lib-mail/test-message-parser.c Tue Dec 15 17:32:09 2015 +0200 @@ -128,10 +128,64 @@ test_end(); } +static void test_message_parser_truncated_mime_headers(void) +{ +static const char input_msg[] = +"Content-Type: multipart/mixed; boundary=\":foo\"\n" +"\n" +"--:foo\n" +"--:foo\n" +"Content-Type: text/plain\n" +"--:foo\n" +"Content-Type: text/plain\r\n" +"--:foo\n" +"Content-Type: text/html\n" +"--:foo--\n"; + struct message_parser_ctx *parser; + struct istream *input; + struct message_part *parts, *part; + struct message_block block; + pool_t pool; + int ret; + + test_begin("message parser truncated mime headers"); + pool = pool_alloconly_create("message parser", 10240); + input = test_istream_create(input_msg); + + parser = message_parser_init(pool, input, 0, 0); + while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ; + test_assert(ret < 0); + test_assert(message_parser_deinit(&parser, &parts) == 0); + + test_assert((parts->flags & MESSAGE_PART_FLAG_MULTIPART) != 0); + test_assert(parts->children->header_size.physical_size == 0); + test_assert(parts->children->body_size.physical_size == 0); + test_assert(parts->children->body_size.lines == 0); + test_assert(parts->children->next->header_size.physical_size == 24); + test_assert(parts->children->next->header_size.virtual_size == 24); + test_assert(parts->children->next->header_size.lines == 0); + test_assert(parts->children->next->next->header_size.physical_size == 24); + test_assert(parts->children->next->next->header_size.virtual_size == 24); + test_assert(parts->children->next->next->header_size.lines == 0); + test_assert(parts->children->next->next->next->header_size.physical_size == 23); + test_assert(parts->children->next->next->next->header_size.virtual_size == 23); + test_assert(parts->children->next->next->next->header_size.lines == 0); + for (part = parts->children; part != NULL; part = part->next) { + test_assert(part->body_size.physical_size == 0); + test_assert(part->body_size.virtual_size == 0); + } + test_assert(parts->children->next->next->next->next == NULL); + + i_stream_unref(&input); + pool_unref(&pool); + test_end(); +} + int main(void) { static void (*test_functions[])(void) = { test_message_parser_small_blocks, + test_message_parser_truncated_mime_headers, NULL }; return test_run(test_functions);