diff --git a/src/lib-mail/rfc822-parser.c b/src/lib-mail/rfc822-parser.c index 886707549a..789d64be74 100644 --- a/src/lib-mail/rfc822-parser.c +++ b/src/lib-mail/rfc822-parser.c @@ -64,6 +64,7 @@ void rfc822_parser_init(struct rfc822_parser_context *ctx, int rfc822_skip_comment(struct rfc822_parser_context *ctx) { const unsigned char *start; + size_t len; int level = 1; i_assert(*ctx->data == '('); @@ -87,16 +88,33 @@ int rfc822_skip_comment(struct rfc822_parser_context *ctx) return ctx->data < ctx->end ? 1 : 0; } break; - case '\\': - if (ctx->last_comment != NULL) { - str_append_data(ctx->last_comment, start, - ctx->data - start); - } + case '\n': + /* folding whitespace, remove the (CR)LF */ + if (ctx->last_comment == NULL) + break; + len = ctx->data - start; + if (len > 0 && start[len-1] == '\r') + len--; + str_append_data(ctx->last_comment, start, len); start = ctx->data + 1; - + break; + case '\\': ctx->data++; if (ctx->data >= ctx->end) return -1; + + if (*ctx->data == '\r' || *ctx->data == '\n') { + /* quoted-pair doesn't allow CR/LF. + They are part of the obs-qp though, so don't + return them as error. */ + ctx->data--; + break; + } + if (ctx->last_comment != NULL) { + str_append_data(ctx->last_comment, start, + ctx->data - start - 1); + } + start = ctx->data; break; } } diff --git a/src/lib-mail/test-rfc822-parser.c b/src/lib-mail/test-rfc822-parser.c index 3b0aaae640..569232afcf 100644 --- a/src/lib-mail/test-rfc822-parser.c +++ b/src/lib-mail/test-rfc822-parser.c @@ -5,6 +5,51 @@ #include "rfc822-parser.h" #include "test-common.h" +static void test_rfc822_parse_comment(void) +{ + static const struct { + const char *input, *output; + int ret; + } tests[] = { + { "(", "", -1 }, + { "(()", "", -1 }, + + { "()", "", 0 }, + { "(())", "()", 0 }, + { "(foo ( bar ) baz)", "foo ( bar ) baz", 0 }, + { "(foo\t\tbar)", "foo\t\tbar", 0 }, + { "(foo\\(bar)", "foo(bar", 0 }, + { "(foo\\\\bar)", "foo\\bar", 0 }, + { "(foo\\\\\\\\)", "foo\\\\", 0 }, + { "(foo\\)bar)", "foo)bar", 0 }, + { "(foo\"flop\"\"bar)", "foo\"flop\"\"bar", 0 }, + + { "(foo\n bar)", "foo bar", 0 }, + { "(foo\n\t\t bar)", "foo\t\t bar", 0 }, + { "(foo\\\n bar)", "foo\\ bar", 0 }, + { "(foo\\\r\n bar)", "foo\\ bar", 0 }, + }; + struct rfc822_parser_context parser, parser2; + string_t *str = t_str_new(64); + unsigned int i = 0; + + test_begin("rfc822 parse comment"); + for (i = 0; i < N_ELEMENTS(tests); i++) { + rfc822_parser_init(&parser, (const void *)tests[i].input, + strlen(tests[i].input), str); + rfc822_parser_init(&parser2, (const void *)tests[i].input, + strlen(tests[i].input), NULL); + test_assert_idx(rfc822_skip_comment(&parser) == tests[i].ret, i); + test_assert_idx(rfc822_skip_comment(&parser2) == tests[i].ret, i); + test_assert_idx(tests[i].ret < 0 || + strcmp(tests[i].output, str_c(str)) == 0, i); + rfc822_parser_deinit(&parser); + rfc822_parser_deinit(&parser2); + str_truncate(str, 0); + } + test_end(); +} + static void test_rfc822_parse_quoted_string(void) { static const struct { @@ -108,6 +153,7 @@ static void test_rfc822_parse_content_param(void) int main(void) { static void (*const test_functions[])(void) = { + test_rfc822_parse_comment, test_rfc822_parse_quoted_string, test_rfc822_parse_domain_literal, test_rfc822_parse_content_param,