ATEXT characters must be properly quoted when are in phrase. Test case: { name = "test\"test", mailbox = "user", domain = "host" } converts to: "test\"test" <user@host> --- src/lib-mail/message-address.c | 66 +++++++++++++++++++++++++++++++++++++--- 1 file changed, 62 insertions(+), 4 deletions(-) diff --git a/src/lib-mail/message-address.c b/src/lib-mail/message-address.c index 54d4ee1..7d6356c 100644 --- a/src/lib-mail/message-address.c +++ b/src/lib-mail/message-address.c @@ -2,6 +2,7 @@ #include "lib.h" #include "str.h" +#include "strescape.h" #include "message-parser.h" #include "message-address.h" #include "rfc822-parser.h" @@ -32,6 +33,49 @@ static void add_address(struct message_address_parser_context *ctx) ctx->last_addr = addr; } +/* quote with "" and escape all '\', '"' and "'" characters if need */ +static void str_append_maybe_escape(string_t *dest, const char *cstr, bool escape_dot) +{ + const char *p; + + /* see if we need to quote it */ + for (p = cstr; *p != '\0'; p++) { + if (!IS_ATEXT(*p) && (escape_dot || *p != '.')) + break; + } + + if (*p == '\0') { + str_append_data(dest, cstr, (size_t) (p - cstr)); + return; + } + + /* see if we need to escape it */ + for (p = cstr; *p != '\0'; p++) { + if (IS_ESCAPED_CHAR(*p)) + break; + } + + if (*p == '\0') { + /* only quote */ + str_append_c(dest, '"'); + str_append_data(dest, cstr, (size_t) (p - cstr)); + str_append_c(dest, '"'); + return; + } + + /* quote and escape */ + str_append_c(dest, '"'); + str_append_data(dest, cstr, (size_t) (p - cstr)); + + for (; *p != '\0'; p++) { + if (IS_ESCAPED_CHAR(*p)) + str_append_c(dest, '\\'); + str_append_c(dest, *p); + } + + str_append_c(dest, '"'); +} + static int parse_local_part(struct message_address_parser_context *ctx) { int ret; @@ -369,7 +413,14 @@ void message_address_write(string_t *str, const struct message_address *addr) /* beginning of group. mailbox is the group name, others are NULL. */ if (addr->mailbox != NULL && *addr->mailbox != '\0') { - str_append(str, addr->mailbox); + /* check for MIME encoded-word */ + if (strstr(addr->mailbox, "=?")) + /* MIME encoded-word MUST NOT appear within a 'quoted-string' + so escaping and quoting of phrase is not possible, instead + use obsolete RFC822 phrase syntax which allow spaces */ + str_append(str, addr->mailbox); + else + str_append_maybe_escape(str, addr->mailbox, TRUE); } else { /* empty group name needs to be quoted */ str_append(str, "\"\""); @@ -396,7 +447,7 @@ void message_address_write(string_t *str, const struct message_address *addr) /* no name and no route. use only mailbox@domain */ i_assert(addr->mailbox != NULL); - str_append(str, addr->mailbox); + str_append_maybe_escape(str, addr->mailbox, FALSE); str_append_c(str, '@'); str_append(str, addr->domain); } else { @@ -404,7 +455,14 @@ void message_address_write(string_t *str, const struct message_address *addr) i_assert(addr->mailbox != NULL); if (addr->name != NULL) { - str_append(str, addr->name); + /* check for MIME encoded-word */ + if (strstr(addr->name, "=?")) + /* MIME encoded-word MUST NOT appear within a 'quoted-string' + so escaping and quoting of phrase is not possible, instead + use obsolete RFC822 phrase syntax which allow spaces */ + str_append(str, addr->name); + else + str_append_maybe_escape(str, addr->name, TRUE); str_append_c(str, ' '); } str_append_c(str, '<'); @@ -412,7 +470,7 @@ void message_address_write(string_t *str, const struct message_address *addr) str_append(str, addr->route); str_append_c(str, ':'); } - str_append(str, addr->mailbox); + str_append_maybe_escape(str, addr->mailbox, FALSE); str_append_c(str, '@'); str_append(str, addr->domain); str_append_c(str, '>'); -- 1.7.9.5