]> git.ipfire.org Git - thirdparty/dovecot/core.git/commitdiff
lib-mail: rfc822-parser: Add nul_replacement_char
authorTimo Sirainen <timo.sirainen@dovecot.fi>
Sat, 21 Apr 2018 13:27:21 +0000 (16:27 +0300)
committerTimo Sirainen <timo.sirainen@dovecot.fi>
Thu, 30 Aug 2018 08:13:12 +0000 (11:13 +0300)
src/lib-mail/rfc822-parser.c
src/lib-mail/rfc822-parser.h
src/lib-mail/test-rfc822-parser.c

index 789d64be747dc7e1e2fa96ba07e4d66290aaaac6..b8508ffbcbb793f017ca750ade7f83f05d7dfc72 100644 (file)
@@ -75,6 +75,16 @@ int rfc822_skip_comment(struct rfc822_parser_context *ctx)
        start = ++ctx->data;
        for (; ctx->data < ctx->end; ctx->data++) {
                switch (*ctx->data) {
+               case '\0':
+                       if (ctx->last_comment != NULL &&
+                           ctx->nul_replacement_char != '\0') {
+                               str_append_data(ctx->last_comment, start,
+                                               ctx->data - start);
+                               str_append_c(ctx->last_comment,
+                                            ctx->nul_replacement_char);
+                               start = ctx->data + 1;
+                       }
+                       break;
                case '(':
                        level++;
                        break;
@@ -103,8 +113,9 @@ int rfc822_skip_comment(struct rfc822_parser_context *ctx)
                        if (ctx->data >= ctx->end)
                                return -1;
 
-                       if (*ctx->data == '\r' || *ctx->data == '\n') {
-                               /* quoted-pair doesn't allow CR/LF.
+                       if (*ctx->data == '\r' || *ctx->data == '\n' ||
+                           *ctx->data == '\0') {
+                               /* quoted-pair doesn't allow CR/LF/NUL.
                                   They are part of the obs-qp though, so don't
                                   return them as error. */
                                ctx->data--;
@@ -235,6 +246,13 @@ int rfc822_parse_quoted_string(struct rfc822_parser_context *ctx, string_t *str)
 
        for (start = ctx->data; ctx->data < ctx->end; ctx->data++) {
                switch (*ctx->data) {
+               case '\0':
+                       if (ctx->nul_replacement_char != '\0') {
+                               str_append_data(str, start, ctx->data - start);
+                               str_append_c(str, ctx->nul_replacement_char);
+                               start = ctx->data + 1;
+                       }
+                       break;
                case '"':
                        str_append_data(str, start, ctx->data - start);
                        ctx->data++;
@@ -345,6 +363,13 @@ rfc822_parse_domain_literal(struct rfc822_parser_context *ctx, string_t *str)
 
        for (start = ctx->data++; ctx->data < ctx->end; ctx->data++) {
                switch (*ctx->data) {
+               case '\0':
+                       if (ctx->nul_replacement_char != '\0') {
+                               str_append_data(str, start, ctx->data - start);
+                               str_append_c(str, ctx->nul_replacement_char);
+                               start = ctx->data + 1;
+                       }
+                       break;
                case '[':
                        /* not allowed */
                        return -1;
index fe6afd26280671c15180b08ef9345e39ac5c5e6b..f36ec103f036869727f25ee001cf1cc429dc60cc 100644 (file)
@@ -1,9 +1,15 @@
 #ifndef RFC822_PARSER_H
 #define RFC822_PARSER_H
 
+/* This can be used as a common NUL replacement character */
+#define RFC822_NUL_REPLACEMENT_CHAR 0x80
+
 struct rfc822_parser_context {
        const unsigned char *data, *end;
        string_t *last_comment;
+
+       /* Replace NULs with this character */
+       char nul_replacement_char;
 };
 
 #define IS_ATEXT(c) \
index 569232afcffe682731b823f0d80cd272397ed7a1..7f0e1b1c5fcfceb47fe2cb3add1965ba55d77e88 100644 (file)
@@ -50,6 +50,32 @@ static void test_rfc822_parse_comment(void)
        test_end();
 }
 
+static void test_rfc822_parse_comment_nuls(void)
+{
+       const unsigned char input[] = "(\000a\000\000b\\\000c(\000d)\000)";
+       const char output[] = "!a!!b\\!c(!d)!";
+       struct rfc822_parser_context parser;
+       string_t *str = t_str_new(64);
+
+       test_begin("rfc822 parse comment with NULs");
+
+       rfc822_parser_init(&parser, input, sizeof(input)-1, str);
+       test_assert(rfc822_skip_comment(&parser) == 0);
+       /* should be same as input, except the outer () removed */
+       test_assert(str_len(str) == sizeof(input)-1-2 &&
+                   memcmp(input+1, str_data(str), str_len(str)) == 0);
+       rfc822_parser_deinit(&parser);
+
+       str_truncate(str, 0);
+       rfc822_parser_init(&parser, input, sizeof(input)-1, str);
+       parser.nul_replacement_char = '!';
+       test_assert(rfc822_skip_comment(&parser) == 0);
+       test_assert(strcmp(str_c(str), output) == 0);
+       rfc822_parser_deinit(&parser);
+
+       test_end();
+}
+
 static void test_rfc822_parse_quoted_string(void)
 {
        static const struct {
@@ -154,6 +180,7 @@ int main(void)
 {
        static void (*const test_functions[])(void) = {
                test_rfc822_parse_comment,
+               test_rfc822_parse_comment_nuls,
                test_rfc822_parse_quoted_string,
                test_rfc822_parse_domain_literal,
                test_rfc822_parse_content_param,