Skip to content

Commit

Permalink
lib-mail: rfc822-parser: Add nul_replacement_char
Browse files Browse the repository at this point in the history
  • Loading branch information
sirainen committed Apr 23, 2018
1 parent 8bdf274 commit 8d2ad31
Show file tree
Hide file tree
Showing 3 changed files with 60 additions and 2 deletions.
29 changes: 27 additions & 2 deletions src/lib-mail/rfc822-parser.c
Expand Up @@ -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;
Expand Down Expand Up @@ -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--;
Expand Down Expand Up @@ -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++;
Expand Down Expand Up @@ -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;
Expand Down
6 changes: 6 additions & 0 deletions src/lib-mail/rfc822-parser.h
@@ -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) \
Expand Down
27 changes: 27 additions & 0 deletions src/lib-mail/test-rfc822-parser.c
Expand Up @@ -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 {
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 8d2ad31

Please sign in to comment.