ATEXT characters must be properly quoted when are in phrase.
Test case:
{ name = "test\"test", mailbox = "user", domain = "host" }
converts to:
"test\"test"
---
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