summaryrefslogtreecommitdiffstats
path: root/src/lib-mail/test-message-part-serialize.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/lib-mail/test-message-part-serialize.c266
1 files changed, 266 insertions, 0 deletions
diff --git a/src/lib-mail/test-message-part-serialize.c b/src/lib-mail/test-message-part-serialize.c
new file mode 100644
index 0000000..2e03140
--- /dev/null
+++ b/src/lib-mail/test-message-part-serialize.c
@@ -0,0 +1,266 @@
+/* Copyright (c) 2020 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "str.h"
+#include "istream.h"
+#include "message-parser.h"
+#include "message-part.h"
+#include "message-part-serialize.h"
+#include "test-common.h"
+
+static const struct message_parser_settings set_empty = { .flags = 0 };
+
+static int message_parse_stream(pool_t pool, struct istream *input,
+ const struct message_parser_settings *set,
+ struct message_part **parts_r)
+{
+ int ret;
+ struct message_parser_ctx *parser;
+ struct message_block block;
+
+ i_zero(&block);
+ parser = message_parser_init(pool, input, set);
+ while ((ret = message_parser_parse_next_block(parser, &block)) > 0) ;
+ message_parser_deinit(&parser, parts_r);
+ test_assert(input->stream_errno == 0);
+ return ret;
+}
+
+static void test_parsed_parts(struct istream *input, struct message_part *parts)
+{
+ const struct message_parser_settings parser_set = {
+ .flags = MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK,
+ };
+ struct message_parser_ctx *parser;
+ struct message_block block;
+ struct message_part *parts2;
+ const char *error;
+
+ i_stream_seek(input, 0);
+
+ parser = message_parser_init_from_parts(parts, input, &parser_set);
+ while (message_parser_parse_next_block(parser, &block) > 0) ;
+ test_assert(message_parser_deinit_from_parts(&parser, &parts2, &error) == 0);
+ test_assert(message_part_is_equal(parts, parts2));
+}
+
+#define TEST_CASE_DATA(x) \
+ { .value = (const unsigned char*)((x)), .value_len = sizeof((x))-1 }
+
+static void test_message_serialize_deserialize(void)
+{
+static struct test_case {
+ struct test_case_data {
+ const unsigned char *value;
+ size_t value_len;
+ } input;
+ int expect_ret;
+} test_cases[] = {
+ {
+ .input = TEST_CASE_DATA("hello, world"),
+ .expect_ret = -1,
+ },
+ {
+ .input = TEST_CASE_DATA(
+"Subject: Hide and seek\n"
+"MIME-Version: 1.0\n"
+"Content-Type: multipart/mixed; boundary=1\n"
+"\n--1\n"
+"Content-Type: multipart/signed; protocol=\"signature/plain\"; migalc=\"pen+paper\"; boundary=2\n"
+"X-Signature-Type: penmanship\n"
+"\n--2\n"
+"Content-Type: multipart/alternative; boundary=3\n"
+"\n--3\n"
+"Content-Type: text/html; charset=us-ascii\n\n"
+"<html><head><title>Search me</title></head><body><p>Don't find me here</p></body></html>\n"
+"\n--3\n"
+"Content-Type: text/plain\n"
+"Content-Transfer-Encoding: binary\n"
+"\n"
+"Search me, and Find me here"
+"\n--3--\n"
+"\n--2\n"
+"Content-Type: signature/plain; charset=us-ascii\n"
+"\n"
+"Signed by undersigned"
+"\n--2--\n"
+"\n--1--"),
+ .expect_ret = -1,
+ },
+ {
+ .input = TEST_CASE_DATA(
+"From: Moderator-Address <moderator>\n" \
+"Content-Type: multipart/digest; boundary=1;\n" \
+"\n\n--1\n" \
+"From: someone-else <someone@else>\n" \
+"Subject: my opinion\n" \
+"\n" \
+"This is my opinion" \
+"\n--1\n\n" \
+"From: another one <another@one>\n" \
+"Subject: i disagree\n" \
+"\n" \
+"Not agreeing one bit!" \
+"\n--1\n\n" \
+"From: attachment <attachment@user>\n" \
+"Subject: funny hat\n" \
+"Content-Type: multipart/mixed; boundary=2\n" \
+"\n--2\n" \
+"Content-Type: text/plain\n"
+"Content-Transfer-Encoding: binary\n"
+"\n" \
+"Lovely attachment for you" \
+"\n--2\n" \
+"Content-Type: application/octet-stream; disposition=attachment; name=\"test.txt\"\n" \
+"Content-Transfer-Encoding: binary\n" \
+"\n" \
+"Foobar" \
+"\n--2--" \
+"\n--1--"),
+ .expect_ret = -1,
+ },
+};
+ test_begin("message part serialize deserialize");
+ for (size_t i = 0; i < N_ELEMENTS(test_cases); i++) {
+ const struct test_case *tc = &test_cases[i];
+ struct message_part *parts;
+ const char *error;
+ pool_t pool = pool_alloconly_create("message parser", 10240);
+ struct istream *is =
+ test_istream_create_data(tc->input.value, tc->input.value_len);
+ test_assert(message_parse_stream(pool, is, &set_empty, &parts) ==
+ tc->expect_ret);
+ buffer_t *dest = buffer_create_dynamic(pool, 256);
+ message_part_serialize(parts, dest);
+ parts = message_part_deserialize(pool, dest->data, dest->used,
+ &error);
+ test_assert(parts != NULL);
+ if (parts != NULL)
+ test_parsed_parts(is, parts);
+ else
+ i_error("message_part_deserialize: %s", error);
+ i_stream_unref(&is);
+ pool_unref(&pool);
+ }
+ test_end();
+}
+
+#define TEST_CASE(data, size, expect_error) \
+ test_assert(message_part_deserialize(pool, (data), (size), &error) == NULL); \
+ test_assert_strcmp(error, (expect_error))
+
+static void test_message_deserialize_errors(void)
+{
+ test_begin("message part deserialize errors");
+ const char *error = NULL;
+ struct message_part part, child1, child2;
+ pool_t pool = pool_datastack_create();
+ buffer_t *dest = buffer_create_dynamic(pool, 256);
+
+ /* empty part */
+ TEST_CASE("", 0, "Not enough data");
+
+ /* truncated part */
+ TEST_CASE("\x08\x00\x00", 3, "Not enough data");
+
+ /* bad sizes */
+ i_zero(&part);
+ part.flags = MESSAGE_PART_FLAG_TEXT;
+ part.header_size.virtual_size = 0;
+ part.header_size.physical_size = 100;
+ message_part_serialize(&part, dest);
+ TEST_CASE(dest->data, dest->used, "header_size.virtual_size too small");
+ buffer_set_used_size(dest, 0);
+
+ i_zero(&part);
+ part.flags = MESSAGE_PART_FLAG_TEXT;
+ part.body_size.virtual_size = 0;
+ part.body_size.physical_size = 100;
+ message_part_serialize(&part, dest);
+ TEST_CASE(dest->data, dest->used, "body_size.virtual_size too small");
+ buffer_set_used_size(dest, 0);
+
+ i_zero(&part);
+ part.flags = MESSAGE_PART_FLAG_MESSAGE_RFC822;
+ message_part_serialize(&part, dest);
+ TEST_CASE(dest->data, dest->used, "message/rfc822 part has no children");
+ buffer_set_used_size(dest, 0);
+
+ i_zero(&part);
+ i_zero(&child1);
+ i_zero(&child2);
+ part.flags = MESSAGE_PART_FLAG_MESSAGE_RFC822;
+ part.children_count = 2;
+ child1.flags = MESSAGE_PART_FLAG_TEXT;
+ child1.parent = &part;
+ part.children = &child1;
+ child2.flags = MESSAGE_PART_FLAG_TEXT;
+ part.children->next = &child2;
+ child2.parent = &part;
+ message_part_serialize(&part, dest);
+ TEST_CASE(dest->data, dest->used, "message/rfc822 part has multiple children");
+ buffer_set_used_size(dest, 0);
+
+ i_zero(&part);
+ i_zero(&child1);
+ part.flags = MESSAGE_PART_FLAG_MULTIPART|MESSAGE_PART_FLAG_IS_MIME;
+ part.children_count = 1;
+ child1.flags = MESSAGE_PART_FLAG_TEXT;
+ child1.parent = &part;
+ part.children = &child1;
+ message_part_serialize(&part, dest);
+ for (size_t i = 0; i < dest->used - 1; i++)
+ TEST_CASE(dest->data, i, "Not enough data");
+ buffer_append_c(dest, '\x00');
+ TEST_CASE(dest->data, dest->used, "Too much data");
+
+ test_end();
+}
+
+static enum fatal_test_state test_message_deserialize_fatals(unsigned int stage)
+{
+ const char *error = NULL;
+ struct message_part part, child1, child2;
+
+ pool_t pool = pool_datastack_create();
+ buffer_t *dest = buffer_create_dynamic(pool, 256);
+
+ switch(stage) {
+ case 0:
+ test_expect_fatal_string("part->children == NULL");
+ test_begin("message deserialize fatals");
+ i_zero(&part);
+ i_zero(&child1);
+ i_zero(&child2);
+ part.flags = MESSAGE_PART_FLAG_MULTIPART|MESSAGE_PART_FLAG_IS_MIME;
+ part.children_count = 1;
+ child1.flags = MESSAGE_PART_FLAG_TEXT;
+ child1.parent = &part;
+ part.children = &child1;
+ child2.parent = &child1;
+ child1.children_count = 1;
+ child1.children = &child2;
+
+ message_part_serialize(&part, dest);
+ TEST_CASE(dest->data, dest->used, "message/rfc822 part has multiple children");
+ buffer_set_used_size(dest, 0);
+ return FATAL_TEST_FAILURE;
+ };
+
+ test_end();
+ return FATAL_TEST_FINISHED;
+}
+
+int main(void)
+{
+ static void (*const test_functions[])(void) = {
+ test_message_serialize_deserialize,
+ test_message_deserialize_errors,
+ NULL
+ };
+ static enum fatal_test_state (*const fatal_functions[])(unsigned int) = {
+ test_message_deserialize_fatals,
+ NULL
+ };
+ return test_run_with_fatals(test_functions, fatal_functions);
+}