This code is somewhat copy&pasted from parse_local_part() in Pigeonhole.
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)
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;
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;
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 */
/* 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:
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)
{
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