[PATCH v2 6/7] lib-mail: message_address_write: Quote and escape strings if needed

Pali Rohár pali.rohar at gmail.com
Sun Jun 5 13:48:19 UTC 2016


ATEXT characters must be properly quoted when are in phrase.

Test case:

  { name = "test\"test", mailbox = "user", domain = "host" }

converts to:

  "test\"test" <user at 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 at 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



More information about the dovecot mailing list