MESSAGE_HEADER_PARSER_FLAG_CLEAN_ONELINE;
enum message_header_parser_flags hdr_flags;
struct message_header_parser_ctx *parser;
- struct message_size hdr_size;
+ struct message_size hdr_size, hdr_size2;
struct istream *input;
+ bool has_nuls;
test_begin("message header parser");
input = test_istream_create(test1_msg);
parser = message_parse_header_init(input, &hdr_size, hdr_flags);
test_message_header_parser_one(parser, hdr_flags);
message_parse_header_deinit(&parser);
+ i_stream_seek(input, 0);
+ message_get_header_size(input, &hdr_size2, &has_nuls);
}
+
+ test_assert(!has_nuls);
+ test_assert(hdr_size.physical_size == hdr_size2.physical_size);
+ test_assert(hdr_size.virtual_size == hdr_size2.virtual_size);
+ test_assert(hdr_size.lines == hdr_size2.lines);
test_assert(hdr_size.physical_size == strlen(test1_msg)-TEST1_MSG_BODY_LEN);
test_assert(hdr_size.virtual_size == strlen(test1_msg) - TEST1_MSG_BODY_LEN + 4);
static void
test_message_header_parser_long_lines_str(const char *str,
unsigned int buffer_size,
- struct message_size *size_r)
+ struct message_size *size_r,
+ struct message_size *size_2_r)
{
struct message_header_parser_ctx *parser;
struct message_header_line *hdr;
struct istream *input;
unsigned int i;
size_t len = strlen(str);
+ bool has_nuls;
input = test_istream_create(str);
test_istream_set_max_buffer_size(input, buffer_size);
while (message_parse_header_next(parser, &hdr) > 0) ;
}
message_parse_header_deinit(&parser);
+ i_stream_seek(input, 0);
+ /* Buffer must be +1 for message_get_header_size as it's using
+ i_stream_read_bytes which does not work with lower buffersize
+ because it returns -2 (input buffer full) if 2 bytes are wanted. */
+ test_istream_set_max_buffer_size(input, buffer_size+1);
+ message_get_header_size(input, size_2_r, &has_nuls);
i_stream_unref(&input);
}
{
static const char *lf_str = "1234567890: 345\n\n";
static const char *crlf_str = "1234567890: 345\r\n\r\n";
- struct message_size hdr_size;
+ struct message_size hdr_size, hdr_size2;
size_t i, len;
test_begin("message header parser long lines");
len = strlen(lf_str);
for (i = 2; i < len; i++) {
- test_message_header_parser_long_lines_str(lf_str, i, &hdr_size);
+ test_message_header_parser_long_lines_str(lf_str, i, &hdr_size, &hdr_size2);
test_assert(hdr_size.physical_size == len);
test_assert(hdr_size.virtual_size == len + 2);
+ test_assert(hdr_size.virtual_size == hdr_size2.virtual_size);
+ test_assert(hdr_size.physical_size == hdr_size2.physical_size);
}
len = strlen(crlf_str);
for (i = 3; i < len; i++) {
- test_message_header_parser_long_lines_str(crlf_str, i, &hdr_size);
+ test_message_header_parser_long_lines_str(crlf_str, i, &hdr_size, &hdr_size2);
test_assert(hdr_size.physical_size == len);
test_assert(hdr_size.virtual_size == len);
+ test_assert(hdr_size.virtual_size == hdr_size2.virtual_size);
+ test_assert(hdr_size.physical_size == hdr_size2.physical_size);
}
test_end();
}
#include "istream.h"
#include "message-parser.h"
#include "message-part-data.h"
+#include "message-size.h"
#include "test-common.h"
static const char test_msg[] =
test_end();
}
+static void test_message_parser_get_sizes(struct istream *input,
+ struct message_size *body_size_r,
+ struct message_size *header_size_r,
+ bool expect_has_nuls)
+{
+ bool has_nuls;
+ i_zero(body_size_r);
+ i_zero(header_size_r);
+
+ message_get_header_size(input, header_size_r, &has_nuls);
+ test_assert(has_nuls == expect_has_nuls);
+ message_get_body_size(input, body_size_r, &has_nuls);
+ test_assert(has_nuls == expect_has_nuls);
+}
+
+static void test_message_parser_assert_sizes(const struct message_part *part,
+ const struct message_size *body_size,
+ const struct message_size *header_size)
+{
+ test_assert(part->header_size.lines == header_size->lines);
+ test_assert(part->header_size.physical_size == header_size->physical_size);
+ test_assert(part->header_size.virtual_size == header_size->virtual_size);
+ test_assert(part->body_size.lines == body_size->lines);
+ test_assert(part->body_size.physical_size == body_size->physical_size);
+ test_assert(part->body_size.virtual_size == body_size->virtual_size);
+}
+
static void test_message_parser_truncated_mime_headers(void)
{
static const char input_msg[] =
"--:foo--\n";
struct istream *input;
struct message_part *parts, *part;
+ struct message_size body_size, header_size;
pool_t pool;
test_begin("message parser truncated mime headers");
test_assert(message_parse_stream(pool, input, &set_empty, FALSE, &parts) < 0);
+ i_stream_seek(input, 0);
+ test_message_parser_get_sizes(input, &body_size, &header_size, FALSE);
+
test_assert((parts->flags & MESSAGE_PART_FLAG_MULTIPART) != 0);
test_assert(parts->children_count == 4);
test_assert(parts->header_size.lines == 2);
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_message_parser_assert_sizes(parts, &body_size, &header_size);
+
test_assert(parts->children->physical_pos == 55);
test_assert(parts->children->header_size.physical_size == 0);
test_assert(parts->children->body_size.physical_size == 0);
"--a\n\n";
struct istream *input;
struct message_part *parts;
+ struct message_size body_size, header_size;
pool_t pool;
test_begin("message parser truncated mime headers 2");
test_assert(message_parse_stream(pool, input, &set_empty, FALSE, &parts) < 0);
+ i_stream_seek(input, 0);
+ test_message_parser_get_sizes(input, &body_size, &header_size, FALSE);
+
test_assert(parts->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
test_assert(parts->children_count == 2);
test_assert(parts->header_size.lines == 2);
test_assert(parts->body_size.lines == 8);
test_assert(parts->body_size.physical_size == 86);
test_assert(parts->body_size.virtual_size == 86+8);
+ test_message_parser_assert_sizes(parts, &body_size, &header_size);
test_assert(parts->children->children_count == 0);
test_assert(parts->children->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
"body\n";
struct istream *input;
struct message_part *parts;
+ struct message_size body_size, header_size;
pool_t pool;
test_begin("message parser duplicate mime boundary");
test_assert(message_parse_stream(pool, input, &set_empty, FALSE, &parts) < 0);
+ i_stream_seek(input, 0);
+ test_message_parser_get_sizes(input, &body_size, &header_size, FALSE);
+
test_assert(parts->children_count == 2);
test_assert(parts->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
test_assert(parts->header_size.lines == 2);
test_assert(parts->body_size.lines == 7);
test_assert(parts->body_size.physical_size == 84);
test_assert(parts->body_size.virtual_size == 84+7);
+ test_message_parser_assert_sizes(parts, &body_size, &header_size);
+
test_assert(parts->children->children_count == 1);
test_assert(parts->children->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
test_assert(parts->children->physical_pos == 49);
"body\n";
struct istream *input;
struct message_part *parts;
+ struct message_size body_size, header_size;
pool_t pool;
test_begin("message parser garbage suffix mime boundary");
test_assert(message_parse_stream(pool, input, &set_empty, FALSE, &parts) < 0);
+ i_stream_seek(input, 0);
+ test_message_parser_get_sizes(input, &body_size, &header_size, FALSE);
+
test_assert(parts->children_count == 2);
test_assert(parts->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
test_assert(parts->header_size.lines == 2);
test_assert(parts->body_size.lines == 7);
test_assert(parts->body_size.physical_size == 86);
test_assert(parts->body_size.virtual_size == 86+7);
+ test_message_parser_assert_sizes(parts, &body_size, &header_size);
+
test_assert(parts->children->children_count == 1);
test_assert(parts->children->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
test_assert(parts->children->physical_pos == 50);
test_assert(message_parse_stream(pool, input, &set_empty, FALSE, &parts) < 0);
+ i_stream_seek(input, 0);
+
test_assert(parts->children_count == 2);
test_assert(parts->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
test_assert(parts->header_size.lines == 2);
"--a--\n\n";
struct istream *input;
struct message_part *parts, *part;
+ struct message_size body_size, header_size;
pool_t pool;
test_begin("message parser continuing truncated mime boundary");
test_assert(message_parse_stream(pool, input, &set_empty, FALSE, &parts) < 0);
+ i_stream_seek(input, 0);
+ test_message_parser_get_sizes(input, &body_size, &header_size, FALSE);
+
part = parts;
test_assert(part->children_count == 3);
test_assert(part->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
test_assert(part->body_size.lines == 9);
test_assert(part->body_size.physical_size == 112);
test_assert(part->body_size.virtual_size == 112+9);
+ test_message_parser_assert_sizes(part, &body_size, &header_size);
part = parts->children;
test_assert(part->children_count == 0);
"body2\n";
struct istream *input;
struct message_part *parts;
+ struct message_size body_size, header_size;
pool_t pool;
test_begin("message parser continuing mime boundary reverse");
test_assert(message_parse_stream(pool, input, &set_empty, FALSE, &parts) < 0);
+ i_stream_seek(input, 0);
+ test_message_parser_get_sizes(input, &body_size, &header_size, FALSE);
+
test_assert(parts->children_count == 3);
test_assert(parts->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
test_assert(parts->header_size.lines == 2);
test_assert(parts->body_size.lines == 11);
test_assert(parts->body_size.physical_size == 121);
test_assert(parts->body_size.virtual_size == 121+11);
+ test_message_parser_assert_sizes(parts, &body_size, &header_size);
+
test_assert(parts->children->children_count == 1);
test_assert(parts->children->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
test_assert(parts->children->physical_pos == 51);
"4444\n";
struct istream *input;
struct message_part *parts, *part;
+ struct message_size body_size, header_size;
pool_t pool;
test_begin("message parser long mime boundary");
test_assert(message_parse_stream(pool, input, &set_empty, FALSE, &parts) < 0);
+ i_stream_seek(input, 0);
+ test_message_parser_get_sizes(input, &body_size, &header_size, FALSE);
+
part = parts;
test_assert(part->children_count == 6);
test_assert(part->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
test_assert(part->body_size.lines == 22);
test_assert(part->body_size.physical_size == 871);
test_assert(part->body_size.virtual_size == 871+22);
+ test_message_parser_assert_sizes(part, &body_size, &header_size);
part = parts->children;
test_assert(part->children_count == 5);
};
struct istream *input;
struct message_part *parts, *part;
+ struct message_size body_size, header_size;
pool_t pool;
test_begin("message parser mime part nested limit");
test_assert(message_parse_stream(pool, input, &parser_set, FALSE, &parts) < 0);
+ i_stream_seek(input, 0);
+ test_message_parser_get_sizes(input, &body_size, &header_size, FALSE);
+
part = parts;
test_assert(part->children_count == 2);
test_assert(part->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
test_assert(part->body_size.lines == 15);
test_assert(part->body_size.physical_size == 148);
test_assert(part->body_size.virtual_size == 148+15);
+ test_message_parser_assert_sizes(part, &body_size, &header_size);
part = parts->children;
test_assert(part->children_count == 0);
};
struct istream *input;
struct message_part *parts, *part;
+ struct message_size body_size, header_size;
pool_t pool;
test_begin("message parser mime part nested limit rfc822");
test_assert(message_parse_stream(pool, input, &parser_set, FALSE, &parts) < 0);
+ i_stream_seek(input, 0);
+ test_message_parser_get_sizes(input, &body_size, &header_size, FALSE);
+
part = parts;
test_assert(part->children_count == 1);
test_assert(part->flags == (MESSAGE_PART_FLAG_MESSAGE_RFC822 | MESSAGE_PART_FLAG_IS_MIME));
test_assert(part->body_size.lines == 5);
test_assert(part->body_size.physical_size == 58);
test_assert(part->body_size.virtual_size == 58+5);
+ test_message_parser_assert_sizes(part, &body_size, &header_size);
part = parts->children;
test_assert(part->children_count == 0);
};
struct istream *input;
struct message_part *parts, *part;
+ struct message_size body_size, header_size;
pool_t pool;
test_begin("message parser mime part limit");
test_assert(message_parse_stream(pool, input, &parser_set, FALSE, &parts) < 0);
+ i_stream_seek(input, 0);
+ test_message_parser_get_sizes(input, &body_size, &header_size, FALSE);
+
part = parts;
test_assert(part->children_count == 3);
test_assert(part->flags == (MESSAGE_PART_FLAG_MULTIPART | MESSAGE_PART_FLAG_IS_MIME));
test_assert(part->body_size.lines == 15);
test_assert(part->body_size.physical_size == 148);
test_assert(part->body_size.virtual_size == 148+15);
+ test_message_parser_assert_sizes(part, &body_size, &header_size);
part = parts->children;
test_assert(part->children_count == 2);
--- /dev/null
+/* Copyright (c) 2020 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "istream.h"
+#include "message-size.h"
+#include "test-common.h"
+
+static const char test_msg[] =
+"Return-Path: <test@example.org>\n"
+"Subject: Hello world\n"
+"From: Test User <test@example.org>\n"
+"To: Another User <test2@example.org>\n"
+"Message-Id: <1.2.3.4@example>\n"
+"Mime-Version: 1.0\n"
+"Date: Sun, 23 May 2007 04:58:08 +0300\n"
+"Content-Type: multipart/signed; micalg=pgp-sha1;\n"
+" protocol=\"application/pgp-signature\";\n"
+" boundary=\"=-GNQXLhuj24Pl1aCkk4/d\"\n"
+"\n"
+"--=-GNQXLhuj24Pl1aCkk4/d\n"
+"Content-Type: text/plain\n"
+"Content-Transfer-Encoding: quoted-printable\n"
+"\n"
+"There was a day=20\n"
+"a happy=20day\n"
+"\n"
+"--=-GNQXLhuj24Pl1aCkk4/d\n"
+"Content-Type: application/pgp-signature; name=signature.asc\n"
+"\n"
+"-----BEGIN PGP SIGNATURE-----\n"
+"Version: GnuPG v1.2.4 (GNU/Linux)\n"
+"\n"
+"invalid\n"
+"-----END PGP SIGNATURE-----\n"
+"\n"
+"--=-GNQXLhuj24Pl1aCkk4/d--\n"
+"\n"
+"\n";
+
+static const char test_msg_with_nuls[] =
+"Return-Path: <test@example.org>\n"
+"Subject: Hello world\n"
+"From: Test User <test@example.org>\n"
+"To: Another User <test2@example.org>\n"
+"Message-Id: <1.2.3.4@example>\n"
+"Mime-Version: 1.0\0\n"
+"Date: Sun, 23 May 2007 04:58:08 +0300\n"
+"Content-Type: multipart/signed; micalg=pgp-sha1;\n"
+" protocol=\"application/pgp-signature\";\n"
+" boundary=\"=-GNQXLhuj24Pl1aCkk4/d\"\n"
+"\n"
+"--=-GNQXLhuj24Pl1aCkk4/d\n"
+"\n"
+"Content-Type: text/plain\n"
+"Content-Transfer-Encoding: quoted-printable\n"
+"\n"
+"There was\0 a day=20\n"
+"a happy=20day\n"
+"\n"
+"--=-GNQXLhuj24Pl1aCkk4/d\n"
+"Content-Type: application/pgp-signature; name=signature.asc\n"
+"\n"
+"-----BEGIN PGP SIGNATURE-----\n"
+"Version: GnuPG v1.2.4 (GNU/Linux)\n"
+"\n"
+"inva\0lid\n"
+"-----END PGP SIGNATURE-----\n"
+"\n"
+"--=-GNQXLhuj24Pl1aCkk4/d--\n"
+"\n"
+"\n";
+
+struct test_case {
+ const char *test_name;
+ const char *message;
+ bool has_nuls;
+ unsigned int body_newlines;
+ unsigned int header_newlines;
+ unsigned int message_len;
+ unsigned int header_len;
+};
+static const struct test_case test_cases[] = {
+ {
+ .test_name = "message size",
+ .message = test_msg,
+ .has_nuls = FALSE,
+ .body_newlines = 19,
+ .header_newlines = 11,
+ .message_len = sizeof(test_msg)-1,
+ .header_len = 335,
+ },
+ {
+ .test_name = "message size with nuls",
+ .message = test_msg_with_nuls,
+ .has_nuls = TRUE,
+ .body_newlines = 20,
+ .header_newlines = 11,
+ .message_len = sizeof(test_msg_with_nuls)-1,
+ .header_len = 336,
+ },
+};
+
+static void test_message_size(void)
+{
+ struct istream *input;
+ struct message_size body_size, header_size;
+ bool has_nuls;
+ bool last_cr;
+ unsigned int i;
+
+ for (i = 0; i < N_ELEMENTS(test_cases); i++) {
+ test_begin(test_cases[i].test_name);
+ input = i_stream_create_from_data(test_cases[i].message,
+ test_cases[i].message_len);
+
+ /* Read physical_size */
+ message_get_header_size(input, &header_size, &has_nuls);
+ test_assert_idx(has_nuls == test_cases[i].has_nuls, i);
+ test_assert_idx(input->v_offset == test_cases[i].header_len, i);
+ message_get_body_size(input, &body_size, &has_nuls);
+ test_assert_idx(has_nuls == test_cases[i].has_nuls, i);
+ test_assert_idx(input->v_offset - body_size.physical_size ==
+ test_cases[i].header_len, i);
+ test_assert_idx(body_size.physical_size + header_size.physical_size ==
+ test_cases[i].message_len, i);
+
+ /* Test last_cr handling */
+ i_stream_seek(input, 0);
+ message_skip_virtual(input, 0, &last_cr);
+ test_assert_idx(!last_cr, i);
+ message_skip_virtual(input, header_size.virtual_size-1, &last_cr);
+ test_assert_idx(last_cr, i);
+ message_skip_virtual(input, 2, &last_cr);
+ test_assert_idx(!last_cr, i);
+
+ /* Skipped header size so read body again */
+ message_get_body_size(input, &body_size, &has_nuls);
+ test_assert_idx(has_nuls == test_cases[i].has_nuls, i);
+ test_assert_idx(input->v_offset - body_size.physical_size ==
+ test_cases[i].header_len, i);
+ test_assert_idx(body_size.physical_size + test_cases[i].body_newlines ==
+ body_size.virtual_size, i);
+ test_assert_idx(body_size.virtual_size + header_size.virtual_size -
+ test_cases[i].body_newlines - test_cases[i].header_newlines ==
+ test_cases[i].message_len, i);
+
+ i_stream_unref(&input);
+ test_end();
+ }
+}
+
+int main(void)
+{
+ static void (*const test_functions[])(void) = {
+ test_message_size,
+ NULL
+ };
+ return test_run(test_functions);
+}