summaryrefslogtreecommitdiffstats
path: root/src/lib-mail/test-message-header-decode.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:51:24 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-28 09:51:24 +0000
commitf7548d6d28c313cf80e6f3ef89aed16a19815df1 (patch)
treea3f6f2a3f247293bee59ecd28e8cd8ceb6ca064a /src/lib-mail/test-message-header-decode.c
parentInitial commit. (diff)
downloaddovecot-upstream.tar.xz
dovecot-upstream.zip
Adding upstream version 1:2.3.19.1+dfsg1.upstream/1%2.3.19.1+dfsg1upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--src/lib-mail/test-message-header-decode.c216
1 files changed, 216 insertions, 0 deletions
diff --git a/src/lib-mail/test-message-header-decode.c b/src/lib-mail/test-message-header-decode.c
new file mode 100644
index 0000000..0221d1a
--- /dev/null
+++ b/src/lib-mail/test-message-header-decode.c
@@ -0,0 +1,216 @@
+/* Copyright (c) 2009-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "buffer.h"
+#include "str.h"
+#include "randgen.h"
+#include "charset-utf8.h"
+#include "message-header-encode.h"
+#include "message-header-decode.h"
+#include "test-common.h"
+
+static void test_message_header_decode(void)
+{
+ static const char *data[] = {
+ " \t=?utf-8?q?=c3=a4?= =?utf-8?q?=c3=a4?= b \t\r\n ", "\xC3\xA4\xC3\xA4 b \t\r\n ",
+ "a =?utf-8?q?=c3=a4?= b", "a \xC3\xA4 b",
+ "a =?utf-8?q?=c3=a4?= b", "a \xC3\xA4 b",
+ "a =?utf-8?q?=c3=a4?=\t\t\r\n =?utf-8?q?=c3=a4?= b", "a \xC3\xA4\xC3\xA4 b",
+ "a =?utf-8?q?=c3=a4?= x =?utf-8?q?=c3=a4?= b", "a \xC3\xA4 x \xC3\xA4 b",
+ "a =?utf-8?b?w6TDpCDDpA==?= b", "a \xC3\xA4\xC3\xA4 \xC3\xA4 b",
+ "=?utf-8?b?w6Qgw6Q=?=", "\xC3\xA4 \xC3\xA4",
+ "a =?utf-8?b?////?= b", "a "UNICODE_REPLACEMENT_CHAR_UTF8" b",
+ "a =?utf-16le?b?UADkAGkAdgDkAOQA?= b", "a P\xC3\xA4iv\xC3\xA4\xC3\xA4 b",
+ "a =?utf-9?b?UMOkaXbDpMOk?= b", "a P\xC3\xA4iv\xC3\xA4\xC3\xA4 b",
+
+ };
+ string_t *dest;
+ unsigned int i;
+
+ test_begin("message header decode");
+
+ dest = t_str_new(256);
+ for (i = 0; i < N_ELEMENTS(data); i += 2) {
+ str_truncate(dest, 0);
+ message_header_decode_utf8((const unsigned char *)data[i],
+ strlen(data[i]), dest, NULL);
+ test_assert_strcmp_idx(str_c(dest), data[i+1], i / 2);
+ }
+ test_end();
+}
+
+static void test_message_header_decode_read_overflow(void)
+{
+ const unsigned char input[] = "=?utf-8?Q?=EF?=";
+ string_t *dest = t_str_new(32);
+
+ test_begin("message header decode read overflow");
+ message_header_decode_utf8(input, sizeof(input)-2, dest, NULL);
+ test_end();
+}
+
+static void check_encoded(string_t *encoded, unsigned int test_idx)
+{
+ const unsigned char *enc = str_data(encoded), *p, *pend;
+ size_t enc_len = str_len(encoded), cur_line_len = 0;
+
+ p = enc;
+ pend = enc + enc_len;
+ while (p < pend) {
+ if (*p == '\r') {
+ p++;
+ continue;
+ }
+ if (*p == '\n') {
+ test_assert_idx(cur_line_len <= 76, test_idx);
+ cur_line_len = 0;
+ p++;
+ continue;
+ }
+ cur_line_len++;
+ test_assert_idx((*p >= 0x20 && *p <= 0x7e) || *p == '\t',
+ test_idx);
+ p++;
+ }
+
+ test_assert_idx(cur_line_len <= 76, test_idx);
+}
+
+static void
+check_encode_decode_result(const unsigned char *inbuf, size_t inbuf_len,
+ string_t *out, unsigned int test_idx)
+{
+ static const unsigned char *rep_char =
+ (const unsigned char *)UNICODE_REPLACEMENT_CHAR_UTF8;
+ static const ptrdiff_t rep_char_len =
+ UNICODE_REPLACEMENT_CHAR_UTF8_LEN;
+ const unsigned char *outbuf = str_data(out);
+ size_t outbuf_len = str_len(out);
+ const unsigned char *pin, *pinend, *pout, *poutend;
+ bool invalid_char = FALSE;
+
+ if (test_has_failed())
+ return;
+
+ pin = inbuf;
+ pinend = inbuf + inbuf_len;
+ pout = outbuf;
+ poutend = outbuf + outbuf_len;
+
+ while (pin < pinend) {
+ unichar_t ch;
+ int nch;
+
+ nch = uni_utf8_get_char_n(pin, pinend - pin, &ch);
+ if (nch <= 0) {
+ /* Invalid character; check proper substitution of
+ replacement character in encoded/decoded output. */
+ pin++;
+ if (!invalid_char) {
+ /* Only one character is substituted for a run
+ of bad stuff. */
+ test_assert_idx(
+ (poutend - pout) >= rep_char_len &&
+ memcmp(pout, rep_char,
+ rep_char_len) == 0, test_idx);
+ pout += rep_char_len;
+ }
+ invalid_char = TRUE;
+ } else {
+ /* Valid character; check matching character bytes. */
+ invalid_char = FALSE;
+ test_assert_idx((pinend - pin) >= nch &&
+ (poutend - pout) >= nch &&
+ memcmp(pin, pout, nch) == 0, test_idx);
+ pin += nch;
+ pout += nch;
+ }
+
+ if (test_has_failed())
+ return;
+ }
+
+ /* Both buffers must have reached the end now. */
+ test_assert_idx(pin == pinend && pout == poutend, test_idx);
+}
+
+static void test_message_header_decode_encode_random(void)
+{
+ string_t *encoded, *decoded;
+ unsigned char buf[1024];
+ unsigned int i, j, buflen;
+
+ test_begin("message header encode & decode randomly (7 bit)");
+
+ encoded = t_str_new(256);
+ decoded = t_str_new(256);
+ for (i = 0; i < 1000; i++) {
+ /* fill only with 7bit data so we don't have to worry about
+ the data being valid UTF-8 */
+ buflen = i_rand_limit(sizeof(buf));
+ for (j = 0; j < buflen; j++)
+ buf[j] = i_rand_limit(128);
+
+ str_truncate(encoded, 0);
+ str_truncate(decoded, 0);
+
+ /* test Q */
+ message_header_encode_q(buf, buflen, encoded, 0);
+ check_encoded(encoded, i);
+ message_header_decode_utf8(encoded->data, encoded->used,
+ decoded, NULL);
+ test_assert_idx(decoded->used == buflen &&
+ memcmp(decoded->data, buf, buflen) == 0, i);
+
+ /* test B */
+ str_truncate(encoded, 0);
+ str_truncate(decoded, 0);
+
+ message_header_encode_b(buf, buflen, encoded, 0);
+ check_encoded(encoded, i);
+ message_header_decode_utf8(encoded->data, encoded->used,
+ decoded, NULL);
+ test_assert_idx(decoded->used == buflen &&
+ memcmp(decoded->data, buf, buflen) == 0, i);
+ }
+ test_end();
+
+ test_begin("message header encode & decode randomly (8 bit)");
+
+ for (i = 0; i < 1000; i++) {
+ buflen = i_rand_limit(sizeof(buf));
+ random_fill(buf, buflen);
+
+ str_truncate(encoded, 0);
+ str_truncate(decoded, 0);
+
+ /* test Q */
+ message_header_encode_q(buf, buflen, encoded, 0);
+ check_encoded(encoded, i);
+ message_header_decode_utf8(encoded->data, encoded->used,
+ decoded, NULL);
+ check_encode_decode_result(buf, buflen, decoded, i);
+
+ /* test B */
+ str_truncate(encoded, 0);
+ str_truncate(decoded, 0);
+
+ message_header_encode_b(buf, buflen, encoded, 0);
+ check_encoded(encoded, i);
+ message_header_decode_utf8(encoded->data, encoded->used,
+ decoded, NULL);
+ check_encode_decode_result(buf, buflen, decoded, i);
+ }
+ test_end();
+}
+
+int main(void)
+{
+ static void (*const test_functions[])(void) = {
+ test_message_header_decode,
+ test_message_header_decode_read_overflow,
+ test_message_header_decode_encode_random,
+ NULL
+ };
+ return test_run(test_functions);
+}