]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-mail: Add MESSAGE_ADDRESS_PARSE_FLAG_NON_STRICT_DOTS
authorTimo Sirainen <timo.sirainen@dovecot.fi>
Fri, 4 May 2018 16:49:57 +0000 (19:49 +0300)
committerTimo Sirainen <timo.sirainen@dovecot.fi>
Wed, 4 Jul 2018 09:08:55 +0000 (09:08 +0000)
This code is somewhat copy&pasted from parse_local_part() in Pigeonhole.

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

index 4880a545aeeb0daf5f518c5387eb32313bc721b7..fcd525e30993254aa7ee70ca7cf10c73a061a563 100644 (file)
@@ -15,7 +15,7 @@ struct message_address_parser_context {
        struct message_address *first_addr, *last_addr, addr;
        string_t *str;
 
-       bool fill_missing;
+       bool fill_missing, non_strict_dots;
 };
 
 static void add_address(struct message_address_parser_context *ctx)
@@ -77,6 +77,29 @@ static void str_append_maybe_escape(string_t *dest, const char *cstr, bool escap
        str_append_c(dest, '"');
 }
 
+static int
+parse_nonstrict_dot_atom(struct rfc822_parser_context *ctx, string_t *str)
+{
+       int ret = -1;
+
+       do {
+               while (*ctx->data == '.') {
+                       str_append_c(str, '.');
+                       ctx->data++;
+                       if (ctx->data == ctx->end) {
+                               /* @domain is missing, but local-part
+                                  parsing was successful */
+                               return 0;
+                       }
+                       ret = 1;
+               }
+               if (*ctx->data == '@')
+                       break;
+               ret = rfc822_parse_atom(ctx, str);
+       } while (ret > 0 && *ctx->data == '.');
+       return ret;
+}
+
 static int parse_local_part(struct message_address_parser_context *ctx)
 {
        int ret;
@@ -90,8 +113,10 @@ static int parse_local_part(struct message_address_parser_context *ctx)
        str_truncate(ctx->str, 0);
        if (*ctx->parser.data == '"')
                ret = rfc822_parse_quoted_string(&ctx->parser, ctx->str);
-       else
+       else if (!ctx->non_strict_dots)
                ret = rfc822_parse_dot_atom(&ctx->parser, ctx->str);
+       else
+               ret = parse_nonstrict_dot_atom(&ctx->parser, ctx->str);
        if (ret < 0)
                return -1;
 
@@ -431,6 +456,7 @@ message_address_parse_real(pool_t pool, const unsigned char *data, size_t size,
        ctx.pool = pool;
        ctx.str = t_str_new(128);
        ctx.fill_missing = (flags & MESSAGE_ADDRESS_PARSE_FLAG_FILL_MISSING) != 0;
+       ctx.non_strict_dots = (flags & MESSAGE_ADDRESS_PARSE_FLAG_NON_STRICT_DOTS) != 0;
 
        if (rfc822_skip_lwsp(&ctx.parser) <= 0) {
                /* no addresses */
index 3498df0ffecd413e9a6a095f6c61785b45ba71f4..ec9b10f2f42fc5fe2364dd3524d5fd1a4cea0a7c 100644 (file)
@@ -7,6 +7,11 @@ enum message_address_parse_flags {
        /* If enabled, missing mailbox and domain are set to MISSING_MAILBOX
           and MISSING_DOMAIN strings. Otherwise they're set to "". */
        MESSAGE_ADDRESS_PARSE_FLAG_FILL_MISSING = BIT(0),
+       /* Allow local-part to contain any number of dots anywhere in it.
+          For example ".user", "us..ser" and "user." will be valid. This
+          isn't strictly allowed by RFC5322, but these addresses are commonly
+          used in Japan. */
+       MESSAGE_ADDRESS_PARSE_FLAG_NON_STRICT_DOTS = BIT(1),
 };
 
 /* group: ... ; will be stored like:
index 41cad7945d0ba21e8958bfd69914257f2ac97483..d46d2fc14b2e3f87d5da7b28a376fcd7f3cc19c0 100644 (file)
@@ -339,6 +339,40 @@ static void test_message_address_nuls(void)
        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 without non-strict-dots flag */
+               addr = message_address_parse(pool_datastack_create(),
+                       addr_input, strlen(inputs[i]), UINT_MAX, 0);
+               test_assert_idx(addr != NULL && addr->invalid_syntax, i);
+
+               /* valid with the non-strict-dots flag */
+               addr = message_address_parse(pool_datastack_create(),
+                       addr_input, strlen(inputs[i]), UINT_MAX,
+                       MESSAGE_ADDRESS_PARSE_FLAG_NON_STRICT_DOTS);
+               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)
 {
@@ -464,6 +498,7 @@ int main(void)
        static void (*const test_functions[])(void) = {
                test_message_address,
                test_message_address_nuls,
+               test_message_address_non_strict_dots,
                test_message_address_path,
                test_message_address_path_invalid,
                NULL