From: Stephan Bosch Date: Wed, 31 Jan 2018 23:08:40 +0000 (+0100) Subject: lib-mail: message-address: Add support for parsing RFC5322 "path" syntax. X-Git-Tag: 2.3.1~75 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=435e4ffe5ac87cdf07a7c8be9289a25f8334ff6e;p=thirdparty%2Fdovecot%2Fcore.git lib-mail: message-address: Add support for parsing RFC5322 "path" syntax. This is either a single angle-addr or just <>. This path syntax differs from the RFC5321 "Path" syntax in that it allows whitespace, which is very important when it is parsed from a header. --- diff --git a/src/lib-mail/message-address.c b/src/lib-mail/message-address.c index 5cb7815dea..172a707d49 100644 --- a/src/lib-mail/message-address.c +++ b/src/lib-mail/message-address.c @@ -408,6 +408,32 @@ message_address_parse_real(pool_t pool, const unsigned char *data, size_t size, return ctx.first_addr; } +static int +message_address_parse_path_real(pool_t pool, const unsigned char *data, + size_t size, struct message_address **addr_r) +{ + struct message_address_parser_context ctx; + int ret; + + i_zero(&ctx); + *addr_r = NULL; + + rfc822_parser_init(&ctx.parser, data, size, NULL); + ctx.pool = pool; + ctx.str = t_str_new(128); + + if (rfc822_skip_lwsp(&ctx.parser) <= 0) + return -1; + if ((ret=parse_angle_addr(&ctx)) < 0 || + (ctx.addr.mailbox != NULL && ctx.addr.domain == NULL)) { + ctx.addr.invalid_syntax = TRUE; + ret = -1; + } + add_address(&ctx); + *addr_r = ctx.first_addr; + return (ret < 0 ? -1 : 0); +} + struct message_address * message_address_parse(pool_t pool, const unsigned char *data, size_t size, unsigned int max_addresses, bool fill_missing) @@ -425,6 +451,20 @@ message_address_parse(pool_t pool, const unsigned char *data, size_t size, return addr; } +int message_address_parse_path(pool_t pool, const unsigned char *data, + size_t size, struct message_address **addr_r) +{ + int ret; + + if (pool->datastack_pool) { + return message_address_parse_path_real(pool, data, size, addr_r); + } + T_BEGIN { + ret = message_address_parse_path_real(pool, data, size, addr_r); + } T_END; + return ret; +} + void message_address_write(string_t *str, const struct message_address *addr) { const char *tmp; @@ -433,6 +473,13 @@ void message_address_write(string_t *str, const struct message_address *addr) if (addr == NULL) return; + /* <> path */ + if (addr->mailbox == NULL && addr->domain == NULL) { + i_assert(addr->next == NULL); + str_append(str, "<>"); + return; + } + /* a) mailbox@domain b) name <@route:mailbox@domain> c) group: .. ; */ diff --git a/src/lib-mail/message-address.h b/src/lib-mail/message-address.h index 2a91b3cc71..6dd32fed9e 100644 --- a/src/lib-mail/message-address.h +++ b/src/lib-mail/message-address.h @@ -30,6 +30,12 @@ struct message_address * message_address_parse(pool_t pool, const unsigned char *data, size_t size, unsigned int max_addresses, bool fill_missing); +/* Parse RFC 5322 "path" (Return-Path header) from given data. Returns -1 if + the path is invalid and 0 otherwise. + */ +int message_address_parse_path(pool_t pool, const unsigned char *data, + size_t size, struct message_address **addr_r); + void message_address_init(struct message_address *addr, const char *name, const char *mailbox, const char *domain) ATTR_NULL(1); diff --git a/src/lib-mail/test-message-address.c b/src/lib-mail/test-message-address.c index a6f5e5e315..c1b5566d91 100644 --- a/src/lib-mail/test-message-address.c +++ b/src/lib-mail/test-message-address.c @@ -320,10 +320,86 @@ static void test_message_address(void) test_end(); } +static int +test_parse_path(const char *input, const struct message_address **addr_r) +{ + struct message_address *addr; + 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); + unsigned char *input_dup = i_malloc(input_len); + memcpy(input_dup, input, input_len); + ret = message_address_parse_path(pool_datastack_create(), + input_dup, input_len, &addr); + 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 } }, + { "", NULL, + { NULL, NULL, NULL, "user", "domain", FALSE } }, + { " ", "", + { NULL, NULL, NULL, "user", "domain", FALSE } }, + { "<\"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 } }, + }; + 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); + 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(); +} + int main(void) { static void (*const test_functions[])(void) = { test_message_address, + test_message_address_path, NULL }; return test_run(test_functions);