changeset 22158:862126b86ef1

lib-mail: message_address_write: Quote and escape strings if needed 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>
author Pali Rohár <pali.rohar@gmail.com>
date Sun, 05 Jun 2016 15:48:19 +0200
parents 19734488e58a
children 4d0360081722
files src/lib-mail/message-address.c
diffstat 1 files changed, 62 insertions(+), 4 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-mail/message-address.c	Sun Jun 05 15:48:18 2016 +0200
+++ b/src/lib-mail/message-address.c	Sun Jun 05 15:48:19 2016 +0200
@@ -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 @@
 	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;
@@ -371,7 +415,14 @@
 				/* 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, "=?") != NULL)
+						/* 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, "\"\"");
@@ -398,7 +449,7 @@
 			/* 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 {
@@ -406,7 +457,14 @@
 			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, '<');
@@ -414,7 +472,7 @@
 				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, '>');