/* 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"
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);
{
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;
- }
-
- 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;
+ if ((ret = message_parser_read_more(ctx, block_r, &full)) <= 0)
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) {
block_r->hdr = hdr;
block_r->size = 0;
+ ctx->prev_hdr_newline_size = hdr->no_newline ? 0 :
+ (hdr->crlf_newline ? 2 : 1);
return 1;
}
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);
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);