summaryrefslogtreecommitdiffstats
path: root/src/lib-smtp/test-smtp-address.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 17:36:47 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 17:36:47 +0000
commit0441d265f2bb9da249c7abf333f0f771fadb4ab5 (patch)
tree3f3789daa2f6db22da6e55e92bee0062a7d613fe /src/lib-smtp/test-smtp-address.c
parentInitial commit. (diff)
downloaddovecot-0441d265f2bb9da249c7abf333f0f771fadb4ab5.tar.xz
dovecot-0441d265f2bb9da249c7abf333f0f771fadb4ab5.zip
Adding upstream version 1:2.3.21+dfsg1.upstream/1%2.3.21+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/lib-smtp/test-smtp-address.c')
-rw-r--r--src/lib-smtp/test-smtp-address.c1382
1 files changed, 1382 insertions, 0 deletions
diff --git a/src/lib-smtp/test-smtp-address.c b/src/lib-smtp/test-smtp-address.c
new file mode 100644
index 0000000..f16ac4c
--- /dev/null
+++ b/src/lib-smtp/test-smtp-address.c
@@ -0,0 +1,1382 @@
+/* Copyright (c) 2013-2018 Dovecot authors, see the included COPYING file */
+
+#include "test-lib.h"
+#include "str.h"
+#include "str-sanitize.h"
+#include "test-common.h"
+#include "smtp-address.h"
+
+/*
+ * Valid mailbox parse tests
+ */
+
+struct valid_mailbox_parse_test {
+ const char *input, *output;
+ enum smtp_address_parse_flags flags;
+
+ struct smtp_address address;
+};
+
+static const struct valid_mailbox_parse_test
+valid_mailbox_parse_tests[] = {
+ {
+ .input = "",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_ALLOW_EMPTY,
+ .address = { .localpart = NULL, .domain = NULL },
+ },
+ {
+ .input = "user",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_ALLOW_LOCALPART,
+ .address = { .localpart = "user", .domain = NULL },
+ },
+ {
+ .input = "user@domain.tld",
+ .address = { .localpart = "user", .domain = "domain.tld" },
+ },
+ {
+ .input = "1234567890@domain.tld",
+ .address = {
+ .localpart = "1234567890",
+ .domain = "domain.tld" },
+ },
+ {
+ .input = "_______@domain.tld",
+ .address = {
+ .localpart = "_______",
+ .domain = "domain.tld" },
+ },
+ {
+ .input = "firstname.lastname@domain.tld",
+ .address = {
+ .localpart = "firstname.lastname",
+ .domain = "domain.tld" },
+ },
+ {
+ .input = "firstname+lastname@domain.tld",
+ .address = {
+ .localpart = "firstname+lastname",
+ .domain = "domain.tld" },
+ },
+ {
+ .input = "firstname-lastname@domain.tld",
+ .address = {
+ .localpart = "firstname-lastname",
+ .domain = "domain.tld" },
+ },
+ {
+ .input = "\"user\"@domain.tld",
+ .address = { .localpart = "user", .domain = "domain.tld" },
+ .output = "user@domain.tld"
+ },
+ {
+ .input = "\"user@frop\"@domain.tld",
+ .address = { .localpart = "user@frop", .domain = "domain.tld" },
+ .output = "\"user@frop\"@domain.tld"
+ },
+ {
+ .input = "user@127.0.0.1",
+ .address = { .localpart = "user", .domain = "127.0.0.1" },
+ },
+ {
+ .input = "user@[127.0.0.1]",
+ .address = { .localpart = "user", .domain = "[127.0.0.1]" },
+ },
+ {
+ .input = "user@[IPv6:::1]",
+ .address = { .localpart = "user", .domain = "[IPv6:::1]" },
+ },
+ {
+ .input = "user@[IPv6:::127.0.0.1]",
+ .address = { .localpart = "user", .domain = "[IPv6:::127.0.0.1]" },
+ /* Japanese deviations */
+ },
+ {
+ .input = "email@-example.com",
+ .address = { .localpart = "email", .domain = "-example.com" },
+ },
+ {
+ .input = ".email@example.com",
+ .output = "\".email\"@example.com",
+ .address = { .localpart = ".email", .domain = "example.com" },
+ },
+ {
+ .input = "email.@example.com",
+ .output = "\"email.\"@example.com",
+ .address = { .localpart = "email.", .domain = "example.com" },
+ },
+ {
+ .input = "email..email@example.com",
+ .output = "\"email..email\"@example.com",
+ .address = { .localpart = "email..email", .domain = "example.com" },
+ },
+ {
+ .input = "Abc..123@example.com",
+ .output = "\"Abc..123\"@example.com",
+ .address = { .localpart = "Abc..123", .domain = "example.com" },
+ },
+ {
+ .input = "Abc..@example.com",
+ .output = "\"Abc..\"@example.com",
+ .address = { .localpart = "Abc..", .domain = "example.com" },
+ },
+};
+
+unsigned int valid_mailbox_parse_test_count =
+ N_ELEMENTS(valid_mailbox_parse_tests);
+
+static void
+test_smtp_mailbox_equal(const struct smtp_address *test,
+ const struct smtp_address *parsed)
+{
+ if (parsed->localpart == NULL) {
+ test_out("address->localpart = (null)",
+ (parsed->localpart == test->localpart));
+ } else {
+ test_out(t_strdup_printf("address->localpart = \"%s\"",
+ parsed->localpart),
+ null_strcmp(parsed->localpart, test->localpart) == 0);
+ }
+ if (parsed->domain == NULL) {
+ test_out(t_strdup_printf("address->domain = (null)"),
+ (parsed->domain == test->domain));
+ } else {
+ test_out(t_strdup_printf("address->domain = \"%s\"",
+ parsed->domain),
+ null_strcmp(parsed->domain, test->domain) == 0);
+ }
+}
+
+static void test_smtp_mailbox_parse_valid(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < valid_mailbox_parse_test_count; i++) T_BEGIN {
+ const struct valid_mailbox_parse_test *test;
+ struct smtp_address *address;
+ const char *error = NULL, *output, *encoded;
+ int ret;
+
+ test = &valid_mailbox_parse_tests[i];
+ ret = smtp_address_parse_mailbox(pool_datastack_create(),
+ test->input, test->flags,
+ &address, &error);
+
+ test_begin(t_strdup_printf("smtp mailbox valid [%d]", i));
+ test_out_reason(t_strdup_printf("parse(\"%s\")", test->input),
+ ret == 0, error);
+
+ if (!test_has_failed()) {
+ test_smtp_mailbox_equal(&test->address, address);
+
+ encoded = smtp_address_encode(address);
+ output = (test->output == NULL ?
+ test->input : test->output);
+ test_out(t_strdup_printf("encode() = \"%s\"", encoded),
+ strcmp(encoded, output) == 0);
+ }
+ test_end();
+ } T_END;
+}
+
+/*
+ * Valid path parse tests
+ */
+
+struct valid_path_parse_test {
+ const char *input, *output;
+ enum smtp_address_parse_flags flags;
+
+ struct smtp_address address;
+};
+
+static const struct valid_path_parse_test
+valid_path_parse_tests[] = {
+ {
+ .input = "<>",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_ALLOW_EMPTY,
+ .address = { .localpart = NULL, .domain = NULL }
+ },
+ {
+ .input = "<user>",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_ALLOW_LOCALPART,
+ .address = { .localpart = "user", .domain = NULL }
+ },
+ {
+ .input = "<user@domain.tld>",
+ .address = { .localpart = "user", .domain = "domain.tld" }
+ },
+ {
+ .input = "<@otherdomain.tld,@yetanotherdomain.tld:user@domain.tld>",
+ .address = { .localpart = "user", .domain = "domain.tld" },
+ .output = "<user@domain.tld>"
+ },
+ {
+ .input = "user@domain.tld",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL,
+ .address = { .localpart = "user", .domain = "domain.tld" },
+ .output = "<user@domain.tld>"
+ },
+ /* Raw */
+ {
+ .input = "<>",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_ALLOW_EMPTY |
+ SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW,
+ .address = { .localpart = NULL, .domain = NULL, .raw = NULL }
+ },
+ {
+ .input = "<user>",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_ALLOW_LOCALPART |
+ SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW,
+ .address = { .localpart = "user", .domain = NULL,
+ .raw = "user" }
+ },
+ {
+ .input = "<user@domain.tld>",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW,
+ .address = { .localpart = "user", .domain = "domain.tld",
+ .raw = "user@domain.tld" }
+ },
+ {
+ .input = "<@otherdomain.tld,@yetanotherdomain.tld:user@domain.tld>",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW,
+ .address = { .localpart = "user", .domain = "domain.tld",
+ .raw = "@otherdomain.tld,@yetanotherdomain.tld:"
+ "user@domain.tld" },
+ .output = "<user@domain.tld>"
+ },
+ {
+ .input = "user@domain.tld",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL |
+ SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW,
+ .address = { .localpart = "user", .domain = "domain.tld",
+ .raw = "user@domain.tld"},
+ .output = "<user@domain.tld>"
+ },
+ /* Broken */
+ {
+ .input = "<>",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_ALLOW_EMPTY |
+ SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW |
+ SMTP_ADDRESS_PARSE_FLAG_IGNORE_BROKEN,
+ .address = { .localpart = NULL, .domain = NULL, .raw = NULL }
+ },
+ {
+ .input = "<user>",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_ALLOW_LOCALPART |
+ SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW |
+ SMTP_ADDRESS_PARSE_FLAG_IGNORE_BROKEN,
+ .address = { .localpart = "user", .domain = NULL,
+ .raw = "user" }
+ },
+ {
+ .input = "<user@domain.tld>",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW |
+ SMTP_ADDRESS_PARSE_FLAG_IGNORE_BROKEN,
+ .address = { .localpart = "user", .domain = "domain.tld",
+ .raw = "user@domain.tld" }
+ },
+ {
+ .input = "<@otherdomain.tld,@yetanotherdomain.tld:user@domain.tld>",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW |
+ SMTP_ADDRESS_PARSE_FLAG_IGNORE_BROKEN,
+ .address = { .localpart = "user", .domain = "domain.tld",
+ .raw = "@otherdomain.tld,@yetanotherdomain.tld:"
+ "user@domain.tld" },
+ .output = "<user@domain.tld>"
+ },
+ {
+ .input = "user@domain.tld",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL |
+ SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW |
+ SMTP_ADDRESS_PARSE_FLAG_IGNORE_BROKEN,
+ .address = { .localpart = "user", .domain = "domain.tld",
+ .raw = "user@domain.tld"},
+ .output = "<user@domain.tld>"
+ },
+ {
+ .input = "u\"ser",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL |
+ SMTP_ADDRESS_PARSE_FLAG_ALLOW_LOCALPART |
+ SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW |
+ SMTP_ADDRESS_PARSE_FLAG_IGNORE_BROKEN,
+ .address = { .localpart = NULL, .domain = NULL,
+ .raw = "u\"ser" },
+ .output = "<>",
+ },
+ {
+ .input = "user\"@domain.tld",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL |
+ SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW |
+ SMTP_ADDRESS_PARSE_FLAG_IGNORE_BROKEN,
+ .address = { .localpart = NULL, .domain = NULL,
+ .raw = "user\"@domain.tld" },
+ .output = "<>",
+ },
+ {
+ .input = "<u\"ser>",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_ALLOW_LOCALPART |
+ SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW |
+ SMTP_ADDRESS_PARSE_FLAG_IGNORE_BROKEN,
+ .address = { .localpart = NULL, .domain = NULL,
+ .raw = "u\"ser" },
+ .output = "<>",
+ },
+ {
+ .input = "<user\"@domain.tld>",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW |
+ SMTP_ADDRESS_PARSE_FLAG_IGNORE_BROKEN,
+ .address = { .localpart = NULL, .domain = NULL,
+ .raw = "user\"@domain.tld" },
+ .output = "<>",
+ },
+ {
+ .input = "bla$die%bla@die&bla",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL |
+ SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW |
+ SMTP_ADDRESS_PARSE_FLAG_IGNORE_BROKEN,
+ .address = { .localpart = NULL, .domain = NULL,
+ .raw = "bla$die%bla@die&bla" },
+ .output = "<>",
+ },
+ {
+ .input = "/@)$@)BLAARGH!@#$$",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL |
+ SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW |
+ SMTP_ADDRESS_PARSE_FLAG_IGNORE_BROKEN,
+ .address = { .localpart = NULL, .domain = NULL,
+ .raw = "/@)$@)BLAARGH!@#$$" },
+ .output = "<>",
+ },
+ {
+ .input = "</@)$@)BLAARGH!@#$$>",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW |
+ SMTP_ADDRESS_PARSE_FLAG_IGNORE_BROKEN,
+ .address = { .localpart = NULL, .domain = NULL,
+ .raw = "/@)$@)BLAARGH!@#$$" },
+ .output = "<>",
+ },
+ {
+ .input = "/@)$@)BLAARGH!@#$$",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW |
+ SMTP_ADDRESS_PARSE_FLAG_IGNORE_BROKEN |
+ SMTP_ADDRESS_PARSE_FLAG_ALLOW_BAD_LOCALPART |
+ SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL,
+ .address = { .localpart = NULL, .domain = NULL,
+ .raw = "/@)$@)BLAARGH!@#$$" },
+ .output = "<>",
+ },
+ {
+ .input = "</@)$@)BLAARGH!@#$$>",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW |
+ SMTP_ADDRESS_PARSE_FLAG_IGNORE_BROKEN |
+ SMTP_ADDRESS_PARSE_FLAG_ALLOW_BAD_LOCALPART |
+ SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL,
+ .address = { .localpart = NULL, .domain = NULL,
+ .raw = "/@)$@)BLAARGH!@#$$" },
+ .output = "<>",
+ },
+ {
+ .input = "f\xc3\xb6\xc3\xa4@\xc3\xb6\xc3\xa4",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL |
+ SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW |
+ SMTP_ADDRESS_PARSE_FLAG_IGNORE_BROKEN |
+ SMTP_ADDRESS_PARSE_FLAG_ALLOW_BAD_LOCALPART |
+ SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL,
+ .address = { .localpart = NULL, .domain = NULL,
+ .raw = "f\xc3\xb6\xc3\xa4@\xc3\xb6\xc3\xa4" },
+ .output = "<>",
+ },
+ {
+ .input = "<f\xc3\xb6\xc3\xa4@\xc3\xb6\xc3\xa4>",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_PRESERVE_RAW |
+ SMTP_ADDRESS_PARSE_FLAG_IGNORE_BROKEN |
+ SMTP_ADDRESS_PARSE_FLAG_ALLOW_BAD_LOCALPART |
+ SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL,
+ .address = { .localpart = NULL, .domain = NULL,
+ .raw = "f\xc3\xb6\xc3\xa4@\xc3\xb6\xc3\xa4" },
+ .output = "<>",
+ },
+};
+
+unsigned int valid_path_parse_test_count =
+ N_ELEMENTS(valid_path_parse_tests);
+
+static void
+test_smtp_path_equal(const struct smtp_address *test,
+ const struct smtp_address *parsed)
+{
+ if (smtp_address_isnull(parsed) || smtp_address_isnull(test)) {
+ test_out("address = <>",
+ (smtp_address_isnull(parsed) &&
+ smtp_address_isnull(test)));
+ } else {
+ test_out(t_strdup_printf("address->localpart = \"%s\"",
+ parsed->localpart),
+ null_strcmp(parsed->localpart, test->localpart) == 0);
+ }
+ if (smtp_address_isnull(parsed)) {
+ /* nothing */
+ } else if (parsed->domain == NULL) {
+ test_out("address->domain = (null)",
+ (parsed->domain == test->domain));
+ } else {
+ test_out(t_strdup_printf("address->domain = \"%s\"",
+ parsed->domain),
+ null_strcmp(parsed->domain, test->domain) == 0);
+ }
+ if (parsed == NULL) {
+ test_out_quiet(t_strdup_printf("address = (null)"),
+ (test->raw == NULL));
+ } else if (parsed->raw == NULL) {
+ test_out_quiet(t_strdup_printf("address->raw = (null)"),
+ (parsed->raw == test->raw));
+ } else {
+ test_out_quiet(t_strdup_printf("address->raw = \"%s\"",
+ parsed->raw),
+ null_strcmp(parsed->raw, test->raw) == 0);
+ }
+}
+
+static void test_smtp_path_parse_valid(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < valid_path_parse_test_count; i++) T_BEGIN {
+ const struct valid_path_parse_test *test;
+ bool ignore_broken;
+ struct smtp_address *address;
+ const char *error = NULL, *output, *encoded;
+ int ret;
+
+ test = &valid_path_parse_tests[i];
+ ignore_broken = HAS_ALL_BITS(
+ test->flags, SMTP_ADDRESS_PARSE_FLAG_IGNORE_BROKEN);
+ ret = smtp_address_parse_path(pool_datastack_create(),
+ test->input, test->flags,
+ &address, &error);
+
+ test_begin(t_strdup_printf("smtp path valid [%d]", i));
+ test_out_reason(t_strdup_printf("parse(\"%s\")", test->input),
+ (ret == 0 || ignore_broken), error);
+
+ if (!test_has_failed()) {
+ test_smtp_path_equal(&test->address, address);
+
+ encoded = smtp_address_encode_path(address);
+ output = (test->output == NULL ?
+ test->input : test->output);
+ test_out(t_strdup_printf("encode() = \"%s\"", encoded),
+ strcmp(encoded, output) == 0);
+ }
+ test_end();
+ } T_END;
+}
+
+/*
+ * Valid username parse tests
+ */
+
+struct valid_username_parse_test {
+ const char *input, *output;
+
+ struct smtp_address address;
+};
+
+static const struct valid_username_parse_test
+valid_username_parse_tests[] = {
+ {
+ .input = "user",
+ .address = {
+ .localpart = "user",
+ .domain = NULL },
+ },
+ {
+ .input = "user@domain.tld",
+ .address = {
+ .localpart = "user",
+ .domain = "domain.tld" },
+ },
+ {
+ .input = "user@domain.tld",
+ .address = {
+ .localpart = "user",
+ .domain = "domain.tld" },
+ },
+ {
+ .input = "1234567890@domain.tld",
+ .address = {
+ .localpart = "1234567890",
+ .domain = "domain.tld" },
+ },
+ {
+ .input = "_______@domain.tld",
+ .address = {
+ .localpart = "_______",
+ .domain = "domain.tld" },
+ },
+ {
+ .input = "firstname.lastname@domain.tld",
+ .address = {
+ .localpart = "firstname.lastname",
+ .domain = "domain.tld" },
+ },
+ {
+ .input = "firstname+lastname@domain.tld",
+ .address = {
+ .localpart = "firstname+lastname",
+ .domain = "domain.tld" },
+ },
+ {
+ .input = "firstname-lastname@domain.tld",
+ .address = {
+ .localpart = "firstname-lastname",
+ .domain = "domain.tld" },
+ },
+ {
+ .input = "\"user\"@domain.tld",
+ .address = { .localpart = "user", .domain = "domain.tld" },
+ .output = "user@domain.tld"
+ },
+ {
+ .input = "\"user@frop\"@domain.tld",
+ .address = { .localpart = "user@frop", .domain = "domain.tld" },
+ .output = "\"user@frop\"@domain.tld"
+ },
+ {
+ .input = "user@frop@domain.tld",
+ .address = { .localpart = "user@frop", .domain = "domain.tld" },
+ .output = "\"user@frop\"@domain.tld"
+ },
+ {
+ .input = "user frop@domain.tld",
+ .address = { .localpart = "user frop", .domain = "domain.tld" },
+ .output = "\"user frop\"@domain.tld"
+ },
+ {
+ .input = "user\"frop@domain.tld",
+ .address = { .localpart = "user\"frop", .domain = "domain.tld" },
+ .output = "\"user\\\"frop\"@domain.tld"
+ },
+ {
+ .input = "user\\frop@domain.tld",
+ .address = { .localpart = "user\\frop", .domain = "domain.tld" },
+ .output = "\"user\\\\frop\"@domain.tld"
+ },
+ {
+ .input = "user@127.0.0.1",
+ .address = { .localpart = "user", .domain = "127.0.0.1" },
+ },
+ {
+ .input = "user@[127.0.0.1]",
+ .address = { .localpart = "user", .domain = "[127.0.0.1]" },
+ },
+ {
+ .input = "user@[IPv6:::1]",
+ .address = { .localpart = "user", .domain = "[IPv6:::1]" },
+ },
+ {
+ .input = "user@[IPv6:::127.0.0.1]",
+ .address = { .localpart = "user", .domain = "[IPv6:::127.0.0.1]" },
+ },
+};
+
+unsigned int valid_username_parse_test_count =
+ N_ELEMENTS(valid_username_parse_tests);
+
+static void test_smtp_username_parse_valid(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < valid_username_parse_test_count; i++) T_BEGIN {
+ const struct valid_username_parse_test *test;
+ struct smtp_address *address;
+ const char *error = NULL, *output, *encoded;
+ int ret;
+
+ test = &valid_username_parse_tests[i];
+ ret = smtp_address_parse_username(pool_datastack_create(),
+ test->input,
+ &address, &error);
+
+ test_begin(t_strdup_printf("smtp username valid [%d]", i));
+ test_out_reason(t_strdup_printf("parse(\"%s\")", test->input),
+ ret == 0, error);
+
+ if (!test_has_failed()) {
+ test_smtp_path_equal(&test->address, address);
+
+ encoded = smtp_address_encode(address);
+ output = (test->output == NULL ?
+ test->input : test->output);
+ test_out(t_strdup_printf("encode() = \"%s\"", encoded),
+ strcmp(encoded, output) == 0);
+ }
+ test_end();
+ } T_END;
+}
+
+/*
+ * Invalid mailbox parse tests
+ */
+
+struct invalid_mailbox_parse_test {
+ const char *input;
+ enum smtp_address_parse_flags flags;
+};
+
+static const struct invalid_mailbox_parse_test
+invalid_mailbox_parse_tests[] = {
+ {
+ .input = "",
+ },
+ {
+ .input = "user",
+ },
+ {
+ .input = "\"user@domain.tld",
+ },
+ {
+ .input = "us\"er@domain.tld",
+ },
+ {
+ .input = "user@frop@domain.tld",
+ },
+ {
+ .input = "user@.tld",
+ },
+ {
+ .input = "user@a$.tld",
+ },
+ {
+ .input = "user@a..tld",
+ },
+ {
+ .input = "user@[]",
+ },
+ {
+ .input = "user@[",
+ },
+ {
+ .input = "user@[AA]",
+ },
+ {
+ .input = "user@[AA",
+ },
+ {
+ .input = "user@[127.0.0]",
+ },
+ {
+ .input = "user@[256.256.256.256]",
+ },
+ {
+ .input = "user@[127.0.0.1",
+ },
+ {
+ .input = "user@[::1]",
+ },
+ {
+ .input = "user@[IPv6:flierp]",
+ },
+ {
+ .input = "user@[IPv6:aa:bb::cc::dd]",
+ },
+ {
+ .input = "user@[IPv6::1]",
+ },
+ {
+ .input = "user@[IPv6:::1",
+ },
+ {
+ .input = "user@[Gen:]",
+ },
+ {
+ .input = "user@[Gen:Hopsa",
+ },
+ {
+ .input = "user@[Gen-:Hopsa]",
+ },
+ {
+ .input = "#@%^%#$@#$@#.com",
+ },
+ {
+ .input = "@example.com",
+ },
+ {
+ .input = "Eric Mail <email@example.com>",
+ },
+ {
+ .input = "email.example.com",
+ },
+ {
+ .input = "email@example@example.com",
+ },
+ {
+ .input = "あいうえお@example.com",
+ },
+ {
+ .input = "email@example.com (Eric Mail)",
+ },
+ {
+ .input = "email@example..com",
+#if 0 /* These deviations are allowed (maybe implement strict mode) */
+ },
+ {
+ .input = "email@-example.com",
+ },
+ {
+ .input = ".email@example.com",
+ },
+ {
+ .input = "email.@example.com",
+ },
+ {
+ .input = "email..email@example.com",
+ },
+ {
+ .input = "Abc..123@example.com"
+#endif
+ },
+};
+
+unsigned int invalid_mailbox_parse_test_count =
+ N_ELEMENTS(invalid_mailbox_parse_tests);
+
+static void test_smtp_mailbox_parse_invalid(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < invalid_mailbox_parse_test_count; i++) T_BEGIN {
+ const struct invalid_mailbox_parse_test *test;
+ struct smtp_address *address;
+ const char *error = NULL;
+ int ret;
+
+ test = &invalid_mailbox_parse_tests[i];
+ ret = smtp_address_parse_mailbox(pool_datastack_create(),
+ test->input, test->flags,
+ &address, &error);
+
+ test_begin(t_strdup_printf("smtp mailbox invalid [%d]", i));
+ test_out_reason(t_strdup_printf("parse(\"%s\")", test->input),
+ ret < 0, error);
+ test_end();
+ } T_END;
+}
+
+/*
+ * Invalid path parse tests
+ */
+
+struct invalid_path_parse_test {
+ const char *input;
+ enum smtp_address_parse_flags flags;
+};
+
+static const struct invalid_path_parse_test
+invalid_path_parse_tests[] = {
+ {
+ .input = "",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "user",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "\"user@domain.tld",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "us\"er@domain.tld",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "user@frop@domain.tld",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "user@.tld",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "user@a$.tld",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "user@a..tld",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "user@[]",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "user@[",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "user@[AA]",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "user@[AA",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "user@[127.0.0]",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "user@[256.256.256.256]",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "user@[127.0.0.1",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "user@[::1]",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "user@[IPv6:flierp]",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "user@[IPv6:aa:bb::cc::dd]",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "user@[IPv6::1]",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "user@[IPv6:::1",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "user@[Gen:]",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "user@[Gen:Hopsa",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "user@[Gen-:Hopsa]",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "#@%^%#$@#$@#.com",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "@example.com",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "Eric Mail <email@example.com>",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "email.example.com",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "email@example@example.com",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "あいうえお@example.com",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "email@example.com (Eric Mail)",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "email@example..com",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "@otherdomain.tld,@yetanotherdomain.tld:user@domain.tld",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "user@domain.tld",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_IGNORE_BROKEN,
+ },
+ {
+ .input = "<>",
+ },
+ {
+ .input = "<user>",
+ },
+ {
+ .input = "<\"user@domain.tld>",
+ },
+ {
+ .input = "<us\"er@domain.tld>",
+ },
+ {
+ .input = "<user@frop@domain.tld>",
+ },
+ {
+ .input = "<user@.tld>",
+ },
+ {
+ .input = "<user@a$.tld>",
+ },
+ {
+ .input = "<user@a..tld>",
+ },
+ {
+ .input = "<user@[]>",
+ },
+ {
+ .input = "<user@[>",
+ },
+ {
+ .input = "<user@[AA]>",
+ },
+ {
+ .input = "<user@[AA>",
+ },
+ {
+ .input = "<user@[127.0.0]>",
+ },
+ {
+ .input = "<user@[256.256.256.256]>",
+ },
+ {
+ .input = "<user@[127.0.0.1>",
+ },
+ {
+ .input = "<user@[::1]>",
+ },
+ {
+ .input = "<user@[IPv6:flierp]>",
+ },
+ {
+ .input = "<user@[IPv6:aa:bb::cc::dd]>",
+ },
+ {
+ .input = "<user@[IPv6::1]>",
+ },
+ {
+ .input = "<user@[IPv6:::1>",
+ },
+ {
+ .input = "<user@[Gen:]>",
+ },
+ {
+ .input = "<user@[Gen:Hopsa>",
+ },
+ {
+ .input = "<user@[Gen-:Hopsa]>",
+ },
+ {
+ .input = "<#@%^%#$@#$@#.com>",
+ },
+ {
+ .input = "<@example.com>",
+ },
+ {
+ .input = "Eric Mail <email@example.com>",
+ },
+ {
+ .input = "<email.example.com>",
+ },
+ {
+ .input = "<email@example@example.com>",
+ },
+ {
+ .input = "<あいうえお@example.com>",
+ },
+ {
+ .input = "<email@example.com> (Eric Mail)",
+ },
+ {
+ .input = "<email@example..com>",
+ },
+ {
+ .input = "<email@example.com",
+ },
+ {
+ .input = "email@example.com>",
+ },
+ {
+ .input = "email@example.com>",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL
+ },
+ {
+ .input = "<",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_ALLOW_EMPTY,
+ },
+ {
+ .input = "<user",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_ALLOW_LOCALPART,
+ },
+ {
+ .input = "<@otherdomain.tld,@yetanotherdomain.tld.user@domain.tld>",
+ },
+ {
+ .input = "<@###domain.tld,@yetanotherdomain.tld.user@domain.tld>",
+ },
+ {
+ .input = "f\xc3\xb6\xc3\xa4@\xc3\xb6\xc3\xa4",
+ .flags = SMTP_ADDRESS_PARSE_FLAG_IGNORE_BROKEN,
+ }
+};
+
+unsigned int invalid_path_parse_test_count =
+ N_ELEMENTS(invalid_path_parse_tests);
+
+static void test_smtp_path_parse_invalid(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < invalid_path_parse_test_count; i++) T_BEGIN {
+ const struct invalid_path_parse_test *test;
+ struct smtp_address *address;
+ const char *error = NULL;
+ int ret;
+
+ test = &invalid_path_parse_tests[i];
+ ret = smtp_address_parse_path(pool_datastack_create(),
+ test->input, test->flags,
+ &address, &error);
+
+ test_begin(t_strdup_printf("smtp path invalid [%d]", i));
+ test_out_reason(t_strdup_printf("parse(\"%s\")", test->input),
+ (ret < 0 && !smtp_address_is_broken(address)),
+ error);
+ test_end();
+ } T_END;
+}
+
+/*
+ * Invalid username parse tests
+ */
+
+struct invalid_username_parse_test {
+ const char *input;
+ enum smtp_address_parse_flags flags;
+};
+
+static const struct invalid_username_parse_test
+invalid_username_parse_tests[] = {
+ {
+ .input = "frop@$%^$%^.tld",
+ },
+ {
+ .input = "fr\top@domain.tld",
+ }
+};
+
+unsigned int invalid_username_parse_test_count =
+ N_ELEMENTS(invalid_username_parse_tests);
+
+static void test_smtp_username_parse_invalid(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < invalid_username_parse_test_count; i++) T_BEGIN {
+ const struct invalid_username_parse_test *test;
+ struct smtp_address *address;
+ const char *error = NULL;
+ int ret;
+
+ test = &invalid_username_parse_tests[i];
+ ret = smtp_address_parse_username(pool_datastack_create(),
+ test->input,
+ &address, &error);
+
+ test_begin(t_strdup_printf("smtp username invalid [%d]", i));
+ test_out_reason(t_strdup_printf("parse(\"%s\")", test->input),
+ ret < 0, error);
+ test_end();
+ } T_END;
+}
+
+/*
+ * Address detail parsing
+ */
+
+struct address_detail_parse_test {
+ const char *delimiters;
+ const char *address;
+ const char *username;
+ const char *detail;
+ char delim;
+};
+
+static const struct address_detail_parse_test
+address_detail_parse_tests[] = {
+ { "", "test", "test", "", '\0' },
+ { "", "test+address", "test+address", "", '\0' },
+ { "", "\"test:address\"", "test:address", "", '\0' },
+ { "", "\"test-address:another+delim\"", "test-address:another+delim",
+ "", '\0' },
+ { "", "test@domain", "test@domain", "", '\0' },
+ { "", "test+address@domain", "test+address@domain", "", '\0' },
+ { "", "\"test:address\"@domain", "test:address@domain", "", '\0' },
+ { "", "\"test-address:another+delim\"@domain",
+ "test-address:another+delim@domain", "", '\0' },
+
+ { "+-:", "test", "test", "", '\0' },
+ { "+-:", "test+address", "test", "address", '+' },
+ { "+-:", "\"test:address\"", "test", "address", ':' },
+ { "+-:", "\"test-address:another+delim\"",
+ "test", "address:another+delim", '-' },
+ { "+-:", "test@domain", "test@domain", "", '\0' },
+ { "+-:", "test+address@domain", "test@domain", "address", '+' },
+ { "+-:", "\"test:address\"@domain", "test@domain", "address", ':' },
+ { "+-:", "\"test-address:another+delim\"@domain", "test@domain",
+ "address:another+delim", '-' },
+};
+
+unsigned int addresss_detail_parse_test_count =
+ N_ELEMENTS(address_detail_parse_tests);
+
+static void test_smtp_address_detail_parse(void)
+{
+ unsigned int i;
+
+
+ for (i = 0; i < N_ELEMENTS(address_detail_parse_tests); i++) T_BEGIN {
+ const struct address_detail_parse_test *test =
+ &address_detail_parse_tests[i];
+ struct smtp_address *address;
+ const char *username, *detail, *error;
+ char delim;
+ int ret;
+
+ test_begin(t_strdup_printf(
+ "smtp address detail parsing [%d]", i));
+
+ ret = smtp_address_parse_path(
+ pool_datastack_create(), test->address,
+ SMTP_ADDRESS_PARSE_FLAG_ALLOW_LOCALPART |
+ SMTP_ADDRESS_PARSE_FLAG_BRACKETS_OPTIONAL,
+ &address, &error);
+ test_out_reason("address parse", ret == 0, error);
+
+ if (!test_has_failed()) {
+ smtp_address_detail_parse_temp(test->delimiters,
+ address, &username,
+ &delim, &detail);
+ test_assert(strcmp(username, test->username) == 0);
+ test_assert(strcmp(detail, test->detail) == 0);
+ test_assert(delim == test->delim);
+ }
+
+ test_end();
+ } T_END;
+}
+
+/*
+ * Skip address tests
+ */
+
+struct any_address_parse_test {
+ const char *input;
+ const char *address;
+ size_t pos;
+ int ret;
+};
+
+static const struct any_address_parse_test
+any_address_parse_tests[] = {
+ {
+ .input = "",
+ .address = "",
+ .pos = 0,
+ .ret = 0,
+ },
+ {
+ .input = " ",
+ .address = "",
+ .pos = 0,
+ .ret = 0,
+ },
+ {
+ .input = "frop@example.com",
+ .address = "frop@example.com",
+ .pos = 16,
+ .ret = 0,
+ },
+ {
+ .input = "frop@example.com ",
+ .address = "frop@example.com",
+ .pos = 16,
+ .ret = 0,
+ },
+ {
+ .input = "<frop@example.com>",
+ .address = "frop@example.com",
+ .pos = 18,
+ .ret = 0,
+ },
+ {
+ .input = "<frop@example.com> ",
+ .address = "frop@example.com",
+ .pos = 18,
+ .ret = 0,
+ },
+ {
+ .input = "<frop@example.com",
+ .pos = 0,
+ .ret = -1,
+ },
+ {
+ .input = "<frop@example.com ",
+ .pos = 0,
+ .ret = -1,
+ },
+ {
+ .input = "fr\"op@example.com",
+ .address = "fr\"op@example.com",
+ .pos = 17,
+ .ret = 0,
+ },
+ {
+ .input = "fr\"op@example.com ",
+ .address = "fr\"op@example.com",
+ .pos = 17,
+ .ret = 0,
+ },
+ {
+ .input = "fr<op@example.com",
+ .address = "fr<op@example.com",
+ .pos = 17,
+ .ret = 0,
+ },
+ {
+ .input = "fr<op@example.com ",
+ .address = "fr<op@example.com",
+ .pos = 17,
+ .ret = 0,
+ },
+ {
+ .input = "\"frop\"@example.com",
+ .address = "\"frop\"@example.com",
+ .pos = 18,
+ .ret = 0,
+ },
+ {
+ .input = "\"frop\"@example.com ",
+ .address = "\"frop\"@example.com",
+ .pos = 18,
+ .ret = 0,
+ },
+ {
+ .input = "\"frop\\\"@example.com",
+ .pos = 0,
+ .ret = -1,
+ },
+ {
+ .input = "\"frop\\\"@example.com ",
+ .pos = 0,
+ .ret = -1,
+ },
+ {
+ .input = "<\"fr>op\"@example.com>",
+ .address = "\"fr>op\"@example.com",
+ .pos = 21,
+ .ret = 0,
+ },
+ {
+ .input = "<\"fr>op\"@example.com> ",
+ .address = "\"fr>op\"@example.com",
+ .pos = 21,
+ .ret = 0,
+ },
+ {
+ .input = "<\"fr>op\"@example.com",
+ .pos = 0,
+ .ret = -1,
+ },
+ {
+ .input = "<\"fr>op\"@example.com ",
+ .pos = 0,
+ .ret = -1,
+ },
+ {
+ .input = "<\"frop\">",
+ .address = "\"frop\"",
+ .pos = 8,
+ .ret = 0,
+ },
+ {
+ .input = "<\"frop\"> ",
+ .address = "\"frop\"",
+ .pos = 8,
+ .ret = 0,
+ },
+ {
+ .input = "<\"frop\"",
+ .pos = 0,
+ .ret = -1,
+ },
+ {
+ .input = "<\"frop\" ",
+ .pos = 0,
+ .ret = -1,
+ },
+ {
+ .input = "\"frop\\\" ",
+ .pos = 0,
+ .ret = -1,
+ },
+ {
+ .input = "\"frop\\\"",
+ .pos = 0,
+ .ret = -1,
+ },
+};
+
+unsigned int any_address_parse_tests_count =
+ N_ELEMENTS(any_address_parse_tests);
+
+static void test_smtp_parse_any_address(void)
+{
+ unsigned int i;
+
+ for (i = 0; i < any_address_parse_tests_count; i++) T_BEGIN {
+ const struct any_address_parse_test *test;
+ const char *address = NULL, *pos = NULL;
+ int ret;
+
+ test = &any_address_parse_tests[i];
+ ret = smtp_address_parse_any(test->input, &address, &pos);
+
+ test_begin(t_strdup_printf("smtp parse any [%d]", i));
+ test_out_quiet(t_strdup_printf("parse(\"%s\")",
+ str_sanitize(test->input, 256)),
+ (ret == test->ret) &&
+ ((size_t)(pos - test->input) == test->pos) &&
+ (null_strcmp(test->address, address ) == 0));
+ test_end();
+ } T_END;
+}
+
+/*
+ * Tests
+ */
+
+int main(void)
+{
+ static void (*test_functions[])(void) = {
+ test_smtp_mailbox_parse_valid,
+ test_smtp_path_parse_valid,
+ test_smtp_username_parse_valid,
+ test_smtp_mailbox_parse_invalid,
+ test_smtp_path_parse_invalid,
+ test_smtp_username_parse_invalid,
+ test_smtp_address_detail_parse,
+ test_smtp_parse_any_address,
+ NULL
+ };
+ return test_run(test_functions);
+}