test-message-parser.c revision 498eecf19b1b408d5321cb048697744d7cee90b2
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber/* Copyright (c) 2007-2016 Dovecot authors, see the included COPYING file */
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber#include "lib.h"
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber#include "str.h"
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber#include "istream.h"
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber#include "message-parser.h"
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber#include "test-common.h"
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graberstatic const char test_msg[] =
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber"Return-Path: <test@example.org>\n"
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber"Subject: Hello world\n"
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber"From: Test User <test@example.org>\n"
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber"To: Another User <test2@example.org>\n"
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber"Message-Id: <1.2.3.4@example>\n"
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber"Mime-Version: 1.0\n"
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber"Date: Sun, 23 May 2007 04:58:08 +0300\n"
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber"Content-Type: multipart/signed; micalg=pgp-sha1;\n"
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber" protocol=\"application/pgp-signature\";\n"
948955a2d6f8e4e28bceada9666c5831de4a6bb8Stéphane Graber" boundary=\"=-GNQXLhuj24Pl1aCkk4/d\"\n"
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber"\n"
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber"--=-GNQXLhuj24Pl1aCkk4/d\n"
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber"Content-Type: text/plain\n"
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber"Content-Transfer-Encoding: quoted-printable\n"
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber"\n"
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber"There was a day=20\n"
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber"a happy=20day\n"
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber"\n"
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber"--=-GNQXLhuj24Pl1aCkk4/d\n"
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber"Content-Type: application/pgp-signature; name=signature.asc\n"
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber"\n"
787c3ebec62eae66ad0441710ee46c8d355bfcfdSerge Hallyn"-----BEGIN PGP SIGNATURE-----\n"
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber"Version: GnuPG v1.2.4 (GNU/Linux)\n"
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber"\n"
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber"invalid\n"
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber"-----END PGP SIGNATURE-----\n"
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber"\n"
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber"--=-GNQXLhuj24Pl1aCkk4/d--\n"
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber"\n"
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber"\n";
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber#define TEST_MSG_LEN (sizeof(test_msg)-1)
787c3ebec62eae66ad0441710ee46c8d355bfcfdSerge Hallyn
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graberstatic bool msg_parts_cmp(struct message_part *p1, struct message_part *p2)
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber{
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber while (p1 != NULL || p2 != NULL) {
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber if ((p1 != NULL) != (p2 != NULL))
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber return FALSE;
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber if ((p1->children != NULL) != (p2->children != NULL))
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber return FALSE;
21e624d9cf79d05179030f1534552048e0c506f5Arjun Sreedharan
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber if (p1->children) {
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber if (!msg_parts_cmp(p1->children, p2->children))
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber return FALSE;
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber }
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber if (p1->physical_pos != p2->physical_pos ||
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber p1->header_size.physical_size != p2->header_size.physical_size ||
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber p1->header_size.virtual_size != p2->header_size.virtual_size ||
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber p1->header_size.lines != p2->header_size.lines ||
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber p1->body_size.physical_size != p2->body_size.physical_size ||
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber p1->body_size.virtual_size != p2->body_size.virtual_size ||
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber p1->body_size.lines != p2->body_size.lines ||
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber p1->flags != p2->flags)
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber return FALSE;
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber p1 = p1->next;
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber p2 = p2->next;
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber }
afeecbba0359d2b4404cdf896e6b6d0b5a8443b0Serge Hallyn return TRUE;
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber}
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graberstatic void test_message_parser_small_blocks(void)
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber{
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber struct message_parser_ctx *parser;
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber struct istream *input;
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber struct message_part *parts, *parts2;
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber struct message_block block;
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber unsigned int i, end_of_headers_idx;
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber string_t *output;
787c3ebec62eae66ad0441710ee46c8d355bfcfdSerge Hallyn pool_t pool;
787c3ebec62eae66ad0441710ee46c8d355bfcfdSerge Hallyn int ret;
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber test_begin("message parser in small blocks");
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber pool = pool_alloconly_create("message parser", 10240);
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber input = test_istream_create(test_msg);
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber output = t_str_new(128);
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber /* full parsing */
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber parser = message_parser_init(pool, input, 0,
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber MESSAGE_PARSER_FLAG_INCLUDE_MULTIPART_BLOCKS |
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber MESSAGE_PARSER_FLAG_INCLUDE_BOUNDARIES);
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber while ((ret = message_parser_parse_next_block(parser, &block)) > 0) {
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber if (block.hdr != NULL)
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber message_header_line_write(output, block.hdr);
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber else
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber str_append_n(output, block.data, block.size);
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber }
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber test_assert(ret < 0);
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber test_assert(message_parser_deinit(&parser, &parts) == 0);
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber test_assert(strcmp(test_msg, str_c(output)) == 0);
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber /* parsing in small blocks */
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber i_stream_seek(input, 0);
72d0e1cb2facaa4b8ba2f15e311d6bb9491badb7Stéphane Graber test_istream_set_allow_eof(input, FALSE);
parser = message_parser_init(pool, input, 0, 0);
for (i = 1; i <= TEST_MSG_LEN*2+1; i++) {
test_istream_set_size(input, i/2);
if (i > TEST_MSG_LEN*2)
test_istream_set_allow_eof(input, TRUE);
while ((ret = message_parser_parse_next_block(parser,
&block)) > 0) ;
test_assert((ret == 0 && i <= TEST_MSG_LEN*2) ||
(ret < 0 && i > TEST_MSG_LEN*2));
}
test_assert(message_parser_deinit(&parser, &parts2) == 0);
test_assert(msg_parts_cmp(parts, parts2));
/* parsing in small blocks from preparsed parts */
i_stream_seek(input, 0);
test_istream_set_allow_eof(input, FALSE);
end_of_headers_idx = (strstr(test_msg, "\n-----") - test_msg);
parser = message_parser_init_from_parts(parts, input, 0,
MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK);
for (i = 1; i <= TEST_MSG_LEN*2+1; i++) {
test_istream_set_size(input, i/2);
if (i > TEST_MSG_LEN*2)
test_istream_set_allow_eof(input, TRUE);
while ((ret = message_parser_parse_next_block(parser,
&block)) > 0) ;
test_assert((ret == 0 && i/2 <= end_of_headers_idx) ||
(ret < 0 && i/2 > end_of_headers_idx));
}
test_assert(message_parser_deinit(&parser, &parts2) == 0);
test_assert(msg_parts_cmp(parts, parts2));
i_stream_unref(&input);
pool_unref(&pool);
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->body_size.lines == 8);
test_assert(parts->body_size.physical_size == 112);
test_assert(parts->body_size.virtual_size == 112+7);
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();
}
static void test_message_parser_no_eoh(void)
{
static const char input_msg[] = "a:b\n";
struct message_parser_ctx *parser;
struct istream *input;
struct message_part *parts;
struct message_block block;
pool_t pool;
test_begin("message parser no EOH");
pool = pool_alloconly_create("message parser", 10240);
input = test_istream_create(input_msg);
parser = message_parser_init(pool, input, 0, 0);
test_assert(message_parser_parse_next_block(parser, &block) > 0 &&
block.hdr != NULL && strcmp(block.hdr->name, "a") == 0 &&
block.hdr->value_len == 1 && block.hdr->value[0] == 'b');
test_assert(message_parser_parse_next_block(parser, &block) > 0 &&
block.hdr == NULL && block.size == 0);
test_assert(message_parser_parse_next_block(parser, &block) < 0);
test_assert(message_parser_deinit(&parser, &parts) == 0);
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,
test_message_parser_no_eoh,
NULL
};
return test_run(test_functions);
}