]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-mail: message-address: Add support for parsing RFC5322 "path" syntax.
authorStephan Bosch <stephan.bosch@dovecot.fi>
Wed, 31 Jan 2018 23:08:40 +0000 (00:08 +0100)
committerVille Savolainen <ville.savolainen@dovecot.fi>
Tue, 13 Mar 2018 04:06:51 +0000 (06:06 +0200)
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.

src/lib-mail/message-address.c
src/lib-mail/message-address.h
src/lib-mail/test-message-address.c

index 5cb7815dea7f7953f09cd6269f8569b121ebe368..172a707d49436a90363e3ad887e65e03d04cd74a 100644 (file)
@@ -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: .. ; */
index 2a91b3cc71a1abe9f23522439b0d08a6d7281eba..6dd32fed9e2b97ceec9faeacb0b5035897f52a75 100644 (file)
@@ -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);
index a6f5e5e31513ba5c3c13fce216a8e1c9a1ba385c..c1b5566d91f2a00fdb9275e7b2e1f6d497db7dcb 100644 (file)
@@ -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 } },
+               { "<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 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);