|
|
2c808a |
From 530c1e950a1bb46ff4e4a7c8e4b7cd945ff28916 Mon Sep 17 00:00:00 2001
|
|
|
2c808a |
From: Timo Sirainen <timo.sirainen@open-xchange.com>
|
|
|
2c808a |
Date: Wed, 18 Nov 2020 18:55:34 +0200
|
|
|
2c808a |
Subject: [PATCH] lib-imap: Fix writing BODYSTRUCTURE for truncated
|
|
|
2c808a |
message/rfc822 part
|
|
|
2c808a |
|
|
|
2c808a |
If the max nesting limit is reached, write the last part out as
|
|
|
2c808a |
application/octet-stream instead of dummy message/rfc822.
|
|
|
2c808a |
|
|
|
2c808a |
Fixes error while parsing BODYSTRUCTURE:
|
|
|
2c808a |
message_part message/rfc822 flag doesn't match BODYSTRUCTURE
|
|
|
2c808a |
---
|
|
|
2c808a |
src/lib-imap/imap-bodystructure.c | 54 +++++++++----------
|
|
|
2c808a |
src/lib-imap/test-imap-bodystructure.c | 73 ++++++++++++++++++++++++--
|
|
|
2c808a |
2 files changed, 96 insertions(+), 31 deletions(-)
|
|
|
2c808a |
|
|
|
2c808a |
diff --git a/src/lib-imap/imap-bodystructure.c b/src/lib-imap/imap-bodystructure.c
|
|
|
2c808a |
index e3da1090b4..ab422c00d2 100644
|
|
|
2c808a |
--- a/src/lib-imap/imap-bodystructure.c
|
|
|
2c808a |
+++ b/src/lib-imap/imap-bodystructure.c
|
|
|
2c808a |
@@ -142,31 +142,42 @@ static void part_write_body_multipart(const struct message_part *part,
|
|
|
2c808a |
part_write_bodystructure_common(data, str);
|
|
|
2c808a |
}
|
|
|
2c808a |
|
|
|
2c808a |
+static bool part_is_truncated(const struct message_part *part)
|
|
|
2c808a |
+{
|
|
|
2c808a |
+ const struct message_part_data *data = part->data;
|
|
|
2c808a |
+
|
|
|
2c808a |
+ i_assert((part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) == 0);
|
|
|
2c808a |
+
|
|
|
2c808a |
+ if (data->content_type != NULL) {
|
|
|
2c808a |
+ if (strcasecmp(data->content_type, "message") == 0 &&
|
|
|
2c808a |
+ strcasecmp(data->content_subtype, "rfc822") == 0) {
|
|
|
2c808a |
+ /* It's message/rfc822, but without
|
|
|
2c808a |
+ MESSAGE_PART_FLAG_MESSAGE_RFC822. */
|
|
|
2c808a |
+ return TRUE;
|
|
|
2c808a |
+ }
|
|
|
2c808a |
+ }
|
|
|
2c808a |
+ return FALSE;
|
|
|
2c808a |
+}
|
|
|
2c808a |
+
|
|
|
2c808a |
static void part_write_body(const struct message_part *part,
|
|
|
2c808a |
string_t *str, bool extended)
|
|
|
2c808a |
{
|
|
|
2c808a |
const struct message_part_data *data = part->data;
|
|
|
2c808a |
- bool text, message_rfc822;
|
|
|
2c808a |
+ bool text;
|
|
|
2c808a |
|
|
|
2c808a |
i_assert(part->data != NULL);
|
|
|
2c808a |
|
|
|
2c808a |
- if ((part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) != 0)
|
|
|
2c808a |
- message_rfc822 = TRUE;
|
|
|
2c808a |
- else if (data->content_type != NULL &&
|
|
|
2c808a |
- strcasecmp(data->content_type, "message") == 0 &&
|
|
|
2c808a |
- strcasecmp(data->content_subtype, "rfc822") == 0) {
|
|
|
2c808a |
- /* It's message/rfc822, but without
|
|
|
2c808a |
- MESSAGE_PART_FLAG_MESSAGE_RFC822. That likely means maximum
|
|
|
2c808a |
- MIME part count was reached while parsing the mail. Write
|
|
|
2c808a |
- the missing child mail's ENVELOPE and BODY as empty dummy
|
|
|
2c808a |
- values. */
|
|
|
2c808a |
- message_rfc822 = TRUE;
|
|
|
2c808a |
- } else
|
|
|
2c808a |
- message_rfc822 = FALSE;
|
|
|
2c808a |
-
|
|
|
2c808a |
- if (message_rfc822) {
|
|
|
2c808a |
+ if ((part->flags & MESSAGE_PART_FLAG_MESSAGE_RFC822) != 0) {
|
|
|
2c808a |
str_append(str, "\"message\" \"rfc822\"");
|
|
|
2c808a |
text = FALSE;
|
|
|
2c808a |
+ } else if (part_is_truncated(part)) {
|
|
|
2c808a |
+ /* Maximum MIME part count was reached while parsing the mail.
|
|
|
2c808a |
+ Write this part out as application/octet-stream instead.
|
|
|
2c808a |
+ We're not using text/plain, because it would require
|
|
|
2c808a |
+ message-parser to use MESSAGE_PART_FLAG_TEXT for this part
|
|
|
2c808a |
+ to avoid losing line count in message_part serialization. */
|
|
|
2c808a |
+ str_append(str, "\"application\" \"octet-stream\"");
|
|
|
2c808a |
+ text = FALSE;
|
|
|
2c808a |
} else {
|
|
|
2c808a |
/* "content type" "subtype" */
|
|
|
2c808a |
if (data->content_type == NULL) {
|
|
|
2c808a |
@@ -214,17 +225,6 @@ static void part_write_body(const struct message_part *part,
|
|
|
2c808a |
|
|
|
2c808a |
part_write_bodystructure_siblings(part->children, str, extended);
|
|
|
2c808a |
str_printfa(str, " %u", part->body_size.lines);
|
|
|
2c808a |
- } else if (message_rfc822) {
|
|
|
2c808a |
- /* truncated MIME part - write out dummy values */
|
|
|
2c808a |
- i_assert(part->children == NULL);
|
|
|
2c808a |
-
|
|
|
2c808a |
- str_append(str, " (NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL) ");
|
|
|
2c808a |
-
|
|
|
2c808a |
- if (!extended)
|
|
|
2c808a |
- str_append(str, EMPTY_BODY);
|
|
|
2c808a |
- else
|
|
|
2c808a |
- str_append(str, EMPTY_BODYSTRUCTURE);
|
|
|
2c808a |
- str_printfa(str, " %u", part->body_size.lines);
|
|
|
2c808a |
}
|
|
|
2c808a |
|
|
|
2c808a |
if (!extended)
|
|
|
2c808a |
diff --git a/src/lib-imap/test-imap-bodystructure.c b/src/lib-imap/test-imap-bodystructure.c
|
|
|
2c808a |
index dfc9957488..6cb699e126 100644
|
|
|
2c808a |
--- a/src/lib-imap/test-imap-bodystructure.c
|
|
|
2c808a |
+++ b/src/lib-imap/test-imap-bodystructure.c
|
|
|
2c808a |
@@ -4,6 +4,7 @@
|
|
|
2c808a |
#include "istream.h"
|
|
|
2c808a |
#include "str.h"
|
|
|
2c808a |
#include "message-part-data.h"
|
|
|
2c808a |
+#include "message-part-serialize.h"
|
|
|
2c808a |
#include "message-parser.h"
|
|
|
2c808a |
#include "imap-bodystructure.h"
|
|
|
2c808a |
#include "test-common.h"
|
|
|
2c808a |
@@ -379,12 +380,14 @@ struct normalize_test normalize_tests[] = {
|
|
|
2c808a |
static const unsigned int normalize_tests_count = N_ELEMENTS(normalize_tests);
|
|
|
2c808a |
|
|
|
2c808a |
static struct message_part *
|
|
|
2c808a |
-msg_parse(pool_t pool, const char *message, bool parse_bodystructure)
|
|
|
2c808a |
+msg_parse(pool_t pool, const char *message, unsigned int max_nested_mime_parts,
|
|
|
2c808a |
+ bool parse_bodystructure)
|
|
|
2c808a |
{
|
|
|
2c808a |
const struct message_parser_settings parser_set = {
|
|
|
2c808a |
.hdr_flags = MESSAGE_HEADER_PARSER_FLAG_SKIP_INITIAL_LWSP |
|
|
|
2c808a |
MESSAGE_HEADER_PARSER_FLAG_DROP_CR,
|
|
|
2c808a |
.flags = MESSAGE_PARSER_FLAG_SKIP_BODY_BLOCK,
|
|
|
2c808a |
+ .max_nested_mime_parts = max_nested_mime_parts,
|
|
|
2c808a |
};
|
|
|
2c808a |
struct message_parser_ctx *parser;
|
|
|
2c808a |
struct istream *input;
|
|
|
2c808a |
@@ -418,7 +421,7 @@ static void test_imap_bodystructure_write(void)
|
|
|
2c808a |
pool_t pool = pool_alloconly_create("imap bodystructure write", 1024);
|
|
|
2c808a |
|
|
|
2c808a |
test_begin(t_strdup_printf("imap bodystructure write [%u]", i));
|
|
|
2c808a |
- parts = msg_parse(pool, test->message, TRUE);
|
|
|
2c808a |
+ parts = msg_parse(pool, test->message, 0, TRUE);
|
|
|
2c808a |
|
|
|
2c808a |
imap_bodystructure_write(parts, str, TRUE);
|
|
|
2c808a |
test_assert(strcmp(str_c(str), test->bodystructure) == 0);
|
|
|
2c808a |
@@ -445,7 +448,7 @@ static void test_imap_bodystructure_parse(void)
|
|
|
2c808a |
pool_t pool = pool_alloconly_create("imap bodystructure parse", 1024);
|
|
|
2c808a |
|
|
|
2c808a |
test_begin(t_strdup_printf("imap bodystructure parser [%u]", i));
|
|
|
2c808a |
- parts = msg_parse(pool, test->message, FALSE);
|
|
|
2c808a |
+ parts = msg_parse(pool, test->message, 0, FALSE);
|
|
|
2c808a |
|
|
|
2c808a |
test_assert(imap_body_parse_from_bodystructure(test->bodystructure,
|
|
|
2c808a |
str, &error) == 0);
|
|
|
2c808a |
@@ -512,7 +515,7 @@ static void test_imap_bodystructure_normalize(void)
|
|
|
2c808a |
pool_t pool = pool_alloconly_create("imap bodystructure parse", 1024);
|
|
|
2c808a |
|
|
|
2c808a |
test_begin(t_strdup_printf("imap bodystructure normalize [%u]", i));
|
|
|
2c808a |
- parts = msg_parse(pool, test->message, FALSE);
|
|
|
2c808a |
+ parts = msg_parse(pool, test->message, 0, FALSE);
|
|
|
2c808a |
|
|
|
2c808a |
ret = imap_bodystructure_parse(test->input,
|
|
|
2c808a |
pool, parts, &error);
|
|
|
2c808a |
@@ -531,6 +534,67 @@ static void test_imap_bodystructure_normalize(void)
|
|
|
2c808a |
} T_END;
|
|
|
2c808a |
}
|
|
|
2c808a |
|
|
|
2c808a |
+static const struct {
|
|
|
2c808a |
+ const char *input;
|
|
|
2c808a |
+ const char *bodystructure;
|
|
|
2c808a |
+ unsigned int max_depth;
|
|
|
2c808a |
+} truncation_tests[] = {
|
|
|
2c808a |
+ {
|
|
|
2c808a |
+ .input = "Content-Type: message/rfc822\n"
|
|
|
2c808a |
+ "\n"
|
|
|
2c808a |
+ "Content-Type: message/rfc822\n"
|
|
|
2c808a |
+ "Header2: value2\n"
|
|
|
2c808a |
+ "\n"
|
|
|
2c808a |
+ "Subject: hello world\n"
|
|
|
2c808a |
+ "Header2: value2\n"
|
|
|
2c808a |
+ "Header3: value3\n"
|
|
|
2c808a |
+ "\n"
|
|
|
2c808a |
+ "body line 1\n"
|
|
|
2c808a |
+ "body line 2\n"
|
|
|
2c808a |
+ "body line 4\n"
|
|
|
2c808a |
+ "body line 3\n",
|
|
|
2c808a |
+ .bodystructure = "\"message\" \"rfc822\" NIL NIL NIL \"7bit\" 159 (NIL NIL NIL NIL NIL NIL NIL NIL NIL NIL) (\"application\" \"octet-stream\" NIL NIL NIL \"7bit\" 110 NIL NIL NIL NIL) 11 NIL NIL NIL NIL",
|
|
|
2c808a |
+ .max_depth = 2,
|
|
|
2c808a |
+ },
|
|
|
2c808a |
+};
|
|
|
2c808a |
+
|
|
|
2c808a |
+static void test_imap_bodystructure_truncation(void)
|
|
|
2c808a |
+{
|
|
|
2c808a |
+ struct message_part *parts;
|
|
|
2c808a |
+ const char *error;
|
|
|
2c808a |
+ string_t *str_body = t_str_new(128);
|
|
|
2c808a |
+ string_t *str_parts = t_str_new(128);
|
|
|
2c808a |
+ pool_t pool = pool_alloconly_create("imap bodystructure parse", 1024);
|
|
|
2c808a |
+
|
|
|
2c808a |
+ test_begin("imap bodystructure truncation");
|
|
|
2c808a |
+
|
|
|
2c808a |
+ for (unsigned int i = 0; i < N_ELEMENTS(truncation_tests); i++) {
|
|
|
2c808a |
+ p_clear(pool);
|
|
|
2c808a |
+ str_truncate(str_body, 0);
|
|
|
2c808a |
+ str_truncate(str_parts, 0);
|
|
|
2c808a |
+
|
|
|
2c808a |
+ parts = msg_parse(pool, truncation_tests[i].input,
|
|
|
2c808a |
+ truncation_tests[i].max_depth,
|
|
|
2c808a |
+ TRUE);
|
|
|
2c808a |
+
|
|
|
2c808a |
+ /* write out BODYSTRUCTURE and serialize message_parts */
|
|
|
2c808a |
+ imap_bodystructure_write(parts, str_body, TRUE);
|
|
|
2c808a |
+ message_part_serialize(parts, str_parts);
|
|
|
2c808a |
+
|
|
|
2c808a |
+ /* now deserialize message_parts and make sure they can be used
|
|
|
2c808a |
+ to parse BODYSTRUCTURE */
|
|
|
2c808a |
+ parts = message_part_deserialize(pool, str_data(str_parts),
|
|
|
2c808a |
+ str_len(str_parts), &error);
|
|
|
2c808a |
+ test_assert(parts != NULL);
|
|
|
2c808a |
+ test_assert(imap_bodystructure_parse(str_c(str_body), pool,
|
|
|
2c808a |
+ parts, &error) == 0);
|
|
|
2c808a |
+ test_assert_strcmp(str_c(str_body),
|
|
|
2c808a |
+ truncation_tests[i].bodystructure);
|
|
|
2c808a |
+ }
|
|
|
2c808a |
+ pool_unref(&pool);
|
|
|
2c808a |
+ test_end();
|
|
|
2c808a |
+}
|
|
|
2c808a |
+
|
|
|
2c808a |
int main(void)
|
|
|
2c808a |
{
|
|
|
2c808a |
static void (*const test_functions[])(void) = {
|
|
|
2c808a |
@@ -538,6 +602,7 @@ int main(void)
|
|
|
2c808a |
test_imap_bodystructure_parse,
|
|
|
2c808a |
test_imap_bodystructure_normalize,
|
|
|
2c808a |
test_imap_bodystructure_parse_full,
|
|
|
2c808a |
+ test_imap_bodystructure_truncation,
|
|
|
2c808a |
NULL
|
|
|
2c808a |
};
|
|
|
2c808a |
return test_run(test_functions);
|