summaryrefslogtreecommitdiffstats
path: root/src/lib-mail/test-message-address.c
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--src/lib-mail/test-message-address.c532
1 files changed, 532 insertions, 0 deletions
diff --git a/src/lib-mail/test-message-address.c b/src/lib-mail/test-message-address.c
new file mode 100644
index 0000000..e6204bb
--- /dev/null
+++ b/src/lib-mail/test-message-address.c
@@ -0,0 +1,532 @@
+/* Copyright (c) 2007-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "str.h"
+#include "message-address.h"
+#include "test-common.h"
+
+enum test_message_address {
+ TEST_MESSAGE_ADDRESS_FLAG_SKIP_LIST = BIT(0),
+};
+
+static bool cmp_addr(const struct message_address *a1,
+ const struct message_address *a2)
+{
+ return null_strcmp(a1->name, a2->name) == 0 &&
+ null_strcmp(a1->route, a2->route) == 0 &&
+ null_strcmp(a1->mailbox, a2->mailbox) == 0 &&
+ null_strcmp(a1->domain, a2->domain) == 0 &&
+ a1->invalid_syntax == a2->invalid_syntax;
+}
+
+static const struct message_address *
+test_parse_address(const char *input, bool fill_missing)
+{
+ const enum message_address_parse_flags flags =
+ fill_missing ? MESSAGE_ADDRESS_PARSE_FLAG_FILL_MISSING : 0;
+ /* duplicate the input (without trailing NUL) so valgrind notices
+ if there's any out-of-bounds access */
+ size_t input_len = strlen(input);
+ unsigned char *input_dup = i_memdup(input, input_len);
+ const struct message_address *addr =
+ message_address_parse(pool_datastack_create(),
+ input_dup, input_len, UINT_MAX, flags);
+ i_free(input_dup);
+ return addr;
+}
+
+static void test_message_address(void)
+{
+ static const struct test {
+ const char *input;
+ const char *wanted_output;
+ const char *wanted_filled_output;
+ struct message_address addr;
+ struct message_address filled_addr;
+ enum test_message_address flags;
+ } tests[] = {
+ /* user@domain -> <user@domain> */
+ { "user@domain", "<user@domain>", NULL,
+ { NULL, NULL, NULL, "user", "domain", FALSE },
+ { NULL, NULL, NULL, "user", "domain", FALSE }, 0 },
+ { "\"user\"@domain", "<user@domain>", NULL,
+ { NULL, NULL, NULL, "user", "domain", FALSE },
+ { NULL, NULL, NULL, "user", "domain", FALSE }, 0 },
+ { "\"user name\"@domain", "<\"user name\"@domain>", NULL,
+ { NULL, NULL, NULL, "user name", "domain", FALSE },
+ { NULL, NULL, NULL, "user name", "domain", FALSE }, 0 },
+ { "\"user@na\\\\me\"@domain", "<\"user@na\\\\me\"@domain>", NULL,
+ { NULL, NULL, NULL, "user@na\\me", "domain", FALSE },
+ { NULL, NULL, NULL, "user@na\\me", "domain", FALSE }, 0 },
+ { "\"user\\\"name\"@domain", "<\"user\\\"name\"@domain>", NULL,
+ { NULL, NULL, NULL, "user\"name", "domain", FALSE },
+ { NULL, NULL, NULL, "user\"name", "domain", FALSE }, 0 },
+ { "\"\"@domain", "<\"\"@domain>", NULL,
+ { NULL, NULL, NULL, "", "domain", FALSE },
+ { NULL, NULL, NULL, "", "domain", FALSE }, 0 },
+ { "user", "<user>", "<user@MISSING_DOMAIN>",
+ { NULL, NULL, NULL, "user", "", TRUE },
+ { NULL, NULL, NULL, "user", "MISSING_DOMAIN", TRUE }, 0 },
+ { "@domain", "<\"\"@domain>", "<MISSING_MAILBOX@domain>",
+ { NULL, NULL, NULL, "", "domain", TRUE },
+ { NULL, NULL, NULL, "MISSING_MAILBOX", "domain", TRUE }, 0 },
+
+ /* Display Name -> Display Name */
+ { "Display Name", "\"Display Name\"", "\"Display Name\" <MISSING_MAILBOX@MISSING_DOMAIN>",
+ { NULL, "Display Name", NULL, "", "", TRUE },
+ { NULL, "Display Name", NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE }, 0 },
+ { "\"Display Name\"", "\"Display Name\"", "\"Display Name\" <MISSING_MAILBOX@MISSING_DOMAIN>",
+ { NULL, "Display Name", NULL, "", "", TRUE },
+ { NULL, "Display Name", NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE }, 0 },
+ { "Display \"Name\"", "\"Display Name\"", "\"Display Name\" <MISSING_MAILBOX@MISSING_DOMAIN>",
+ { NULL, "Display Name", NULL, "", "", TRUE },
+ { NULL, "Display Name", NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE }, 0 },
+ { "\"Display\" \"Name\"", "\"Display Name\"", "\"Display Name\" <MISSING_MAILBOX@MISSING_DOMAIN>",
+ { NULL, "Display Name", NULL, "", "", TRUE },
+ { NULL, "Display Name", NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE }, 0 },
+ { "\"\"", "", "<MISSING_MAILBOX@MISSING_DOMAIN>",
+ { NULL, "", NULL, "", "", TRUE },
+ { NULL, "", NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE }, 0 },
+
+ /* <user@domain> -> <user@domain> */
+ { "<user@domain>", NULL, NULL,
+ { NULL, NULL, NULL, "user", "domain", FALSE },
+ { NULL, NULL, NULL, "user", "domain", FALSE }, 0 },
+ { "<\"user\"@domain>", "<user@domain>", NULL,
+ { NULL, NULL, NULL, "user", "domain", FALSE },
+ { NULL, NULL, NULL, "user", "domain", FALSE }, 0 },
+ { "<\"user name\"@domain>", NULL, NULL,
+ { NULL, NULL, NULL, "user name", "domain", FALSE },
+ { NULL, NULL, NULL, "user name", "domain", FALSE }, 0 },
+ { "<\"user@na\\\\me\"@domain>", NULL, NULL,
+ { NULL, NULL, NULL, "user@na\\me", "domain", FALSE },
+ { NULL, NULL, NULL, "user@na\\me", "domain", FALSE }, 0 },
+ { "<\"user\\\"name\"@domain>", NULL, NULL,
+ { NULL, NULL, NULL, "user\"name", "domain", FALSE },
+ { NULL, NULL, NULL, "user\"name", "domain", FALSE }, 0 },
+ { "<\"\"@domain>", NULL, NULL,
+ { NULL, NULL, NULL, "", "domain", FALSE },
+ { NULL, NULL, NULL, "", "domain", FALSE }, 0 },
+ { "<user>", NULL, "<user@MISSING_DOMAIN>",
+ { NULL, NULL, NULL, "user", "", TRUE },
+ { NULL, NULL, NULL, "user", "MISSING_DOMAIN", TRUE }, 0 },
+ { "<@route>", "<@route:\"\">", "<INVALID_ROUTE:MISSING_MAILBOX@MISSING_DOMAIN>",
+ { NULL, NULL, "@route", "", "", TRUE },
+ { NULL, NULL, "INVALID_ROUTE", "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE }, 0 },
+
+ /* user@domain (Display Name) -> "Display Name" <user@domain> */
+ { "user@domain (DisplayName)", "DisplayName <user@domain>", NULL,
+ { NULL, "DisplayName", NULL, "user", "domain", FALSE },
+ { NULL, "DisplayName", NULL, "user", "domain", FALSE }, 0 },
+ { "user@domain (Display Name)", "\"Display Name\" <user@domain>", NULL,
+ { NULL, "Display Name", NULL, "user", "domain", FALSE },
+ { NULL, "Display Name", NULL, "user", "domain", FALSE }, 0 },
+ { "user@domain (Display\"Name)", "\"Display\\\"Name\" <user@domain>", NULL,
+ { NULL, "Display\"Name", NULL, "user", "domain", FALSE },
+ { NULL, "Display\"Name", NULL, "user", "domain", FALSE }, 0 },
+ { "user (Display Name)", "\"Display Name\" <user>", "\"Display Name\" <user@MISSING_DOMAIN>",
+ { NULL, "Display Name", NULL, "user", "", TRUE },
+ { NULL, "Display Name", NULL, "user", "MISSING_DOMAIN", TRUE }, 0 },
+ { "@domain (Display Name)", "\"Display Name\" <\"\"@domain>", "\"Display Name\" <MISSING_MAILBOX@domain>",
+ { NULL, "Display Name", NULL, "", "domain", TRUE },
+ { NULL, "Display Name", NULL, "MISSING_MAILBOX", "domain", TRUE }, 0 },
+ { "user@domain ()", "<user@domain>", NULL,
+ { NULL, NULL, NULL, "user", "domain", FALSE },
+ { NULL, NULL, NULL, "user", "domain", FALSE }, 0 },
+
+ /* Display Name <user@domain> -> "Display Name" <user@domain> */
+ { "DisplayName <user@domain>", NULL, NULL,
+ { NULL, "DisplayName", NULL, "user", "domain", FALSE },
+ { NULL, "DisplayName", NULL, "user", "domain", FALSE }, 0 },
+ { "Display Name <user@domain>", "\"Display Name\" <user@domain>", NULL,
+ { NULL, "Display Name", NULL, "user", "domain", FALSE },
+ { NULL, "Display Name", NULL, "user", "domain", FALSE }, 0 },
+ { "\"Display Name\" <user@domain>", NULL, NULL,
+ { NULL, "Display Name", NULL, "user", "domain", FALSE },
+ { NULL, "Display Name", NULL, "user", "domain", FALSE }, 0 },
+ { "\"Display\\\"Name\" <user@domain>", NULL, NULL,
+ { NULL, "Display\"Name", NULL, "user", "domain", FALSE },
+ { NULL, "Display\"Name", NULL, "user", "domain", FALSE }, 0 },
+ { "Display Name <user>", "\"Display Name\" <user>", "\"Display Name\" <user@MISSING_DOMAIN>",
+ { NULL, "Display Name", NULL, "user", "", TRUE },
+ { NULL, "Display Name", NULL, "user", "MISSING_DOMAIN", TRUE }, 0 },
+ { "\"\" <user@domain>", "<user@domain>", NULL,
+ { NULL, NULL, NULL, "user", "domain", FALSE },
+ { NULL, NULL, NULL, "user", "domain", FALSE }, 0 },
+
+ /* <@route:user@domain> -> <@route:user@domain> */
+ { "<@route:user@domain>", NULL, NULL,
+ { NULL, NULL, "@route", "user", "domain", FALSE },
+ { NULL, NULL, "@route", "user", "domain", FALSE }, 0 },
+ { "<@route,@route2:user@domain>", NULL, NULL,
+ { NULL, NULL, "@route,@route2", "user", "domain", FALSE },
+ { NULL, NULL, "@route,@route2", "user", "domain", FALSE }, 0 },
+ { "<@route@route2:user@domain>", "<@route,@route2:user@domain>", NULL,
+ { NULL, NULL, "@route,@route2", "user", "domain", FALSE },
+ { NULL, NULL, "@route,@route2", "user", "domain", FALSE }, 0 },
+ { "<@route@route2:user>", "<@route,@route2:user>", "<@route,@route2:user@MISSING_DOMAIN>",
+ { NULL, NULL, "@route,@route2", "user", "", TRUE },
+ { NULL, NULL, "@route,@route2", "user", "MISSING_DOMAIN", TRUE }, 0 },
+ { "<@route@route2:\"\"@domain>", "<@route,@route2:\"\"@domain>", NULL,
+ { NULL, NULL, "@route,@route2", "", "domain", FALSE },
+ { NULL, NULL, "@route,@route2", "", "domain", FALSE }, 0 },
+
+ /* Display Name <@route:user@domain> ->
+ "Display Name" <@route:user@domain> */
+ { "Display Name <@route:user@domain>", "\"Display Name\" <@route:user@domain>", NULL,
+ { NULL, "Display Name", "@route", "user", "domain", FALSE },
+ { NULL, "Display Name", "@route", "user", "domain", FALSE }, 0 },
+ { "Display Name <@route,@route2:user@domain>", "\"Display Name\" <@route,@route2:user@domain>", NULL,
+ { NULL, "Display Name", "@route,@route2", "user", "domain", FALSE },
+ { NULL, "Display Name", "@route,@route2", "user", "domain", FALSE }, 0 },
+ { "Display Name <@route@route2:user@domain>", "\"Display Name\" <@route,@route2:user@domain>", NULL,
+ { NULL, "Display Name", "@route,@route2", "user", "domain", FALSE },
+ { NULL, "Display Name", "@route,@route2", "user", "domain", FALSE }, 0 },
+ { "Display Name <@route@route2:user>", "\"Display Name\" <@route,@route2:user>", "\"Display Name\" <@route,@route2:user@MISSING_DOMAIN>",
+ { NULL, "Display Name", "@route,@route2", "user", "", TRUE },
+ { NULL, "Display Name", "@route,@route2", "user", "MISSING_DOMAIN", TRUE }, 0 },
+ { "Display Name <@route@route2:\"\"@domain>", "\"Display Name\" <@route,@route2:\"\"@domain>", NULL,
+ { NULL, "Display Name", "@route,@route2", "", "domain", FALSE },
+ { NULL, "Display Name", "@route,@route2", "", "domain", FALSE }, 0 },
+
+ /* other tests: */
+ { "\"foo: <a@b>;,\" <user@domain>", NULL, NULL,
+ { NULL, "foo: <a@b>;,", NULL, "user", "domain", FALSE },
+ { NULL, "foo: <a@b>;,", NULL, "user", "domain", FALSE }, 0 },
+ { "<>", "", "<MISSING_MAILBOX@MISSING_DOMAIN>",
+ { NULL, NULL, NULL, "", "", TRUE },
+ { NULL, NULL, NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE }, 0 },
+ { "<@>", "", "<INVALID_ROUTE:MISSING_MAILBOX@MISSING_DOMAIN>",
+ { NULL, NULL, NULL, "", "", TRUE },
+ { NULL, NULL, "INVALID_ROUTE", "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE }, 0 },
+
+ /* Test against a out-of-bounds read bug - keep these two tests
+ together in this same order: */
+ { "aaaa@", "<aaaa>", "<aaaa@MISSING_DOMAIN>",
+ { NULL, NULL, NULL, "aaaa", "", TRUE },
+ { NULL, NULL, NULL, "aaaa", "MISSING_DOMAIN", TRUE }, 0 },
+ { "a(aa", "", "<MISSING_MAILBOX@MISSING_DOMAIN>",
+ { NULL, NULL, NULL, "", "", TRUE },
+ { NULL, NULL, NULL, "MISSING_MAILBOX", "MISSING_DOMAIN", TRUE },
+ TEST_MESSAGE_ADDRESS_FLAG_SKIP_LIST },
+ };
+ static struct message_address group_prefix = {
+ NULL, NULL, NULL, "group", NULL, FALSE
+ };
+ static struct message_address group_suffix = {
+ NULL, NULL, NULL, NULL, NULL, FALSE
+ };
+ const struct message_address *addr;
+ string_t *str, *group;
+ const char *wanted_string;
+ unsigned int i;
+
+ test_begin("message address parsing");
+ str = t_str_new(128);
+ group = t_str_new(256);
+
+ for (i = 0; i < N_ELEMENTS(tests)*2; i++) {
+ const struct test *test = &tests[i/2];
+ const struct message_address *test_wanted_addr;
+ bool fill_missing = i%2 != 0;
+
+ test_wanted_addr = !fill_missing ?
+ &test->addr : &test->filled_addr;
+ addr = test_parse_address(test->input, fill_missing);
+ test_assert_idx(addr != NULL && addr->next == NULL &&
+ cmp_addr(addr, test_wanted_addr), i);
+
+ /* test the address alone */
+ str_truncate(str, 0);
+ message_address_write(str, addr);
+ if (fill_missing && test->wanted_filled_output != NULL)
+ wanted_string = test->wanted_filled_output;
+ else if (test->wanted_output != NULL)
+ wanted_string = test->wanted_output;
+ else
+ wanted_string = test->input;
+ test_assert_idx(strcmp(str_c(str), wanted_string) == 0, i);
+
+ if ((test->flags & TEST_MESSAGE_ADDRESS_FLAG_SKIP_LIST) != 0)
+ continue;
+
+ /* test the address as a list of itself */
+ for (unsigned int list_length = 2; list_length <= 5; list_length++) {
+ str_truncate(group, 0);
+ str_append(group, test->input);
+ for (unsigned int j = 1; j < list_length; j++) {
+ if ((j % 2) == 0)
+ str_append(group, ",");
+ else
+ str_append(group, " , \n ");
+ str_append(group, test->input);
+ }
+
+ addr = test_parse_address(str_c(group), fill_missing);
+ for (unsigned int j = 0; j < list_length; j++) {
+ test_assert_idx(addr != NULL &&
+ cmp_addr(addr, test_wanted_addr), i);
+ if (addr != NULL)
+ addr = addr->next;
+ }
+ test_assert_idx(addr == NULL, i);
+ }
+
+ /* test the address as a group of itself */
+ for (unsigned int list_length = 1; list_length <= 5; list_length++) {
+ str_truncate(group, 0);
+ str_printfa(group, "group: %s", test->input);
+ for (unsigned int j = 1; j < list_length; j++) {
+ if ((j % 2) == 0)
+ str_append(group, ",");
+ else
+ str_append(group, " , \n ");
+ str_append(group, test->input);
+ }
+ str_append_c(group, ';');
+
+ addr = test_parse_address(str_c(group), fill_missing);
+ test_assert(addr != NULL && cmp_addr(addr, &group_prefix));
+ addr = addr->next;
+ for (unsigned int j = 0; j < list_length; j++) {
+ test_assert_idx(addr != NULL &&
+ cmp_addr(addr, test_wanted_addr), i);
+ if (addr != NULL)
+ addr = addr->next;
+ }
+ test_assert_idx(addr != NULL && addr->next == NULL &&
+ cmp_addr(addr, &group_suffix), i);
+ }
+ }
+ test_end();
+
+ test_begin("message address parsing with empty group");
+ str_truncate(group, 0);
+ str_append(group, "group:;");
+ addr = test_parse_address(str_c(group), FALSE);
+ str_truncate(str, 0);
+ message_address_write(str, addr);
+ test_assert(addr != NULL && cmp_addr(addr, &group_prefix));
+ addr = addr->next;
+ test_assert(addr != NULL && addr->next == NULL &&
+ cmp_addr(addr, &group_suffix));
+ test_assert(strcmp(str_c(str), "group:;") == 0);
+ test_end();
+
+ test_begin("message address parsing empty string");
+ test_assert(message_address_parse(unsafe_data_stack_pool, &uchar_nul, 0, 10,
+ MESSAGE_ADDRESS_PARSE_FLAG_FILL_MISSING) == NULL);
+ str_truncate(str, 0);
+ message_address_write(str, NULL);
+ test_assert(str_len(str) == 0);
+ test_end();
+}
+
+static void test_message_address_nuls(void)
+{
+ const unsigned char input[] =
+ "\"user\0nuls\\\0-esc\"@[domain\0nuls\\\0-esc] (comment\0nuls\\\0-esc)";
+ const struct message_address output = {
+ NULL, "comment\xEF\xBF\xBDnuls\\\xEF\xBF\xBD-esc", NULL,
+ "user\xEF\xBF\xBDnuls\\\xEF\xBF\xBD-esc",
+ "[domain\xEF\xBF\xBDnuls\\\xEF\xBF\xBD-esc]", FALSE
+ };
+ const struct message_address *addr;
+
+ test_begin("message address parsing with NULs");
+ addr = message_address_parse(pool_datastack_create(),
+ input, sizeof(input)-1, UINT_MAX, 0);
+ test_assert(addr != NULL && cmp_addr(addr, &output));
+ test_end();
+}
+
+static void test_message_address_nuls_display_name(void)
+{
+ const unsigned char input[] =
+ "\"displayname\0nuls\\\0-esc\" <\"user\0nuls\\\0-esc\"@[domain\0nuls\\\0-esc]>";
+ const struct message_address output = {
+ NULL, "displayname\xEF\xBF\xBDnuls\\\xEF\xBF\xBD-esc", NULL,
+ "user\xEF\xBF\xBDnuls\\\xEF\xBF\xBD-esc",
+ "[domain\xEF\xBF\xBDnuls\\\xEF\xBF\xBD-esc]", FALSE
+ };
+ const struct message_address *addr;
+
+ test_begin("message address parsing with NULs in display-name");
+ addr = message_address_parse(pool_datastack_create(),
+ input, sizeof(input)-1, UINT_MAX, 0);
+ test_assert(addr != NULL && cmp_addr(addr, &output));
+ test_end();
+}
+
+static void test_message_address_non_strict_dots(void)
+{
+ const char *const inputs[] = {
+ ".@example.com",
+ "..@example.com",
+ "..foo@example.com",
+ "..foo..@example.com",
+ "..foo..bar..@example.com",
+ };
+ const struct message_address *addr;
+ struct message_address output = {
+ NULL, NULL, NULL, "local-part",
+ "example.com", FALSE
+ };
+
+ test_begin("message address parsing with non-strict dots");
+ for (unsigned int i = 0; i < N_ELEMENTS(inputs); i++) {
+ const unsigned char *addr_input =
+ (const unsigned char *)inputs[i];
+ /* invalid with strict-dots flag */
+ addr = message_address_parse(pool_datastack_create(),
+ addr_input, strlen(inputs[i]), UINT_MAX,
+ MESSAGE_ADDRESS_PARSE_FLAG_STRICT_DOTS);
+ test_assert_idx(addr != NULL && addr->invalid_syntax, i);
+
+ /* valid without the strict-dots flag */
+ addr = message_address_parse(pool_datastack_create(),
+ addr_input, strlen(inputs[i]), UINT_MAX, 0);
+ output.mailbox = t_strcut(inputs[i], '@');
+ test_assert_idx(addr != NULL && cmp_addr(addr, &output), i);
+ }
+ test_end();
+}
+
+static int
+test_parse_path(const char *input, const struct message_address **addr_r)
+{
+ struct message_address *addr;
+ char *input_dup;
+ int ret;
+
+ /* duplicate the input (without trailing NUL) so valgrind notices
+ if there's any out-of-bounds access */
+ size_t input_len = strlen(input);
+ if (input_len > 0)
+ input = input_dup = i_memdup(input, input_len);
+ ret = message_address_parse_path(pool_datastack_create(),
+ (const unsigned char *)input, input_len,
+ &addr);
+ if (input_len > 0)
+ i_free(input_dup);
+ *addr_r = addr;
+ return ret;
+}
+
+static void test_message_address_path(void)
+{
+ static const struct test {
+ const char *input;
+ const char *wanted_output;
+ struct message_address addr;
+ } tests[] = {
+ { "<>", NULL,
+ { NULL, NULL, NULL, NULL, NULL, FALSE } },
+ { " < > ", "<>",
+ { NULL, NULL, NULL, NULL, NULL, FALSE } },
+ { "<user@domain>", NULL,
+ { NULL, NULL, NULL, "user", "domain", FALSE } },
+ { " <user@domain> ", "<user@domain>",
+ { NULL, NULL, NULL, "user", "domain", FALSE } },
+ { "user@domain", "<user@domain>",
+ { NULL, NULL, NULL, "user", "domain", FALSE } },
+ { " user@domain ", "<user@domain>",
+ { NULL, NULL, NULL, "user", "domain", FALSE } },
+ { "<\"user\"@domain>", "<user@domain>",
+ { NULL, NULL, NULL, "user", "domain", FALSE } },
+ { "<\"user name\"@domain>", NULL,
+ { NULL, NULL, NULL, "user name", "domain", FALSE } },
+ { "<\"user@na\\\\me\"@domain>", NULL,
+ { NULL, NULL, NULL, "user@na\\me", "domain", FALSE } },
+ { "<\"user\\\"name\"@domain>", NULL,
+ { NULL, NULL, NULL, "user\"name", "domain", FALSE } },
+ { "<\"\"@domain>", NULL,
+ { NULL, NULL, NULL, "", "domain", FALSE } },
+ { "<@source", "<>",
+ { NULL, NULL, NULL, NULL, NULL, TRUE } },
+ };
+ const struct message_address *addr;
+ string_t *str;
+ const char *wanted_string;
+ unsigned int i;
+
+ test_begin("message address path parsing");
+ str = t_str_new(128);
+
+ for (i = 0; i < N_ELEMENTS(tests); i++) {
+ const struct test *test = &tests[i];
+ const struct message_address *test_wanted_addr;
+ int ret;
+
+ test_wanted_addr = &test->addr;
+ ret = test_parse_path(test->input, &addr);
+ if (addr->invalid_syntax)
+ test_assert_idx(ret == -1, i);
+ else
+ test_assert_idx(ret == 0, i);
+ test_assert_idx(addr != NULL && addr->next == NULL &&
+ cmp_addr(addr, test_wanted_addr), i);
+
+ /* test the address alone */
+ str_truncate(str, 0);
+ message_address_write(str, addr);
+ if (test->wanted_output != NULL)
+ wanted_string = test->wanted_output;
+ else
+ wanted_string = test->input;
+ test_assert_idx(strcmp(str_c(str), wanted_string) == 0, i);
+ }
+ test_end();
+}
+
+static void test_message_address_path_invalid(void)
+{
+ static const char *tests[] = {
+ "",
+ "<",
+ " < ",
+ ">",
+ " > ",
+ "<user@domain",
+ " <user@domain ",
+ "user@domain>",
+ " user@domain> ",
+ "<user>",
+ "<@route@route2:user>",
+ "<@domain>",
+ "@domain",
+ " @domain ",
+ "<user@>",
+ "user@",
+ " user@ ",
+ "<user@domain>bladiebla",
+ "user@domain@"
+ };
+ const struct message_address *addr;
+ unsigned int i;
+
+ test_begin("message address path invalid");
+
+ for (i = 0; i < N_ELEMENTS(tests); i++) {
+ const char *test = tests[i];
+ int ret;
+
+ ret = test_parse_path(test, &addr);
+ test_assert_idx(ret < 0, i);
+ }
+ test_end();
+}
+
+int main(void)
+{
+ static void (*const test_functions[])(void) = {
+ test_message_address,
+ test_message_address_nuls,
+ test_message_address_nuls_display_name,
+ test_message_address_non_strict_dots,
+ test_message_address_path,
+ test_message_address_path_invalid,
+ NULL
+ };
+ return test_run(test_functions);
+}