From: Timo Sirainen Date: Fri, 4 May 2018 16:49:57 +0000 (+0300) Subject: lib-mail: Add MESSAGE_ADDRESS_PARSE_FLAG_NON_STRICT_DOTS X-Git-Tag: 2.3.9~1612 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=0d9218abe8f20ea8e2b72c2217e1ff51265b7541;p=thirdparty%2Fdovecot%2Fcore.git lib-mail: Add MESSAGE_ADDRESS_PARSE_FLAG_NON_STRICT_DOTS This code is somewhat copy&pasted from parse_local_part() in Pigeonhole. --- diff --git a/src/lib-mail/message-address.c b/src/lib-mail/message-address.c index 4880a545ae..fcd525e309 100644 --- a/src/lib-mail/message-address.c +++ b/src/lib-mail/message-address.c @@ -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 */ diff --git a/src/lib-mail/message-address.h b/src/lib-mail/message-address.h index 3498df0ffe..ec9b10f2f4 100644 --- a/src/lib-mail/message-address.h +++ b/src/lib-mail/message-address.h @@ -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: diff --git a/src/lib-mail/test-message-address.c b/src/lib-mail/test-message-address.c index 41cad7945d..d46d2fc14b 100644 --- a/src/lib-mail/test-message-address.c +++ b/src/lib-mail/test-message-address.c @@ -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