changeset 21294:dfa20786a81d

lib-imap: imap-bodystructure: Prevent writing erroneous whitespace between items in an envelope address list. Both imap_bodystructure_write() and imap_body_parse_from_bodystructure() produced such invalid output. This caused an RFC 3501 violation in IMAP FETCH BODY and BODYSTRUCTURE responses. Test suite is amended to test this situation.
author Stephan Bosch <stephan.bosch@dovecot.fi>
date Wed, 14 Dec 2016 02:51:54 +0100
parents 0b5ede227460
children 75acc3287480
files src/lib-imap/imap-bodystructure.c src/lib-imap/test-imap-bodystructure.c
diffstat 2 files changed, 20 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/src/lib-imap/imap-bodystructure.c	Tue Dec 13 18:31:41 2016 +0200
+++ b/src/lib-imap/imap-bodystructure.c	Wed Dec 14 02:51:54 2016 +0100
@@ -547,12 +547,16 @@
 	return TRUE;
 }
 
-static void imap_write_list(const struct imap_arg *args, string_t *str)
+static void
+imap_write_envelope_list(const struct imap_arg *args, string_t *str,
+	bool toplevel)
 {
 	const struct imap_arg *children;
 
 	/* don't do any typechecking, just write it out */
 	while (!IMAP_ARG_IS_EOL(args)) {
+		bool list = FALSE;
+
 		if (!str_append_nstring(str, args)) {
 			if (!imap_arg_get_list(args, &children)) {
 				/* everything is either nstring or list */
@@ -560,16 +564,24 @@
 			}
 
 			str_append_c(str, '(');
-			imap_write_list(children, str);
+			imap_write_envelope_list(children, str, FALSE);
 			str_append_c(str, ')');
+
+			list = TRUE;
 		}
 		args++;
 
-		if (!IMAP_ARG_IS_EOL(args))
+		if ((toplevel || !list) && !IMAP_ARG_IS_EOL(args))
 			str_append_c(str, ' ');
 	}
 }
 
+static void
+imap_write_envelope(const struct imap_arg *args, string_t *str)
+{
+	imap_write_envelope_list(args, str, TRUE);
+}
+
 static int imap_write_nstring_list(const struct imap_arg *args, string_t *str)
 {
 	str_truncate(str, 0);
@@ -814,7 +826,7 @@
 			return -1;
 		}
 		str_truncate(tmpstr, 0);
-		imap_write_list(list_args, tmpstr);
+		imap_write_envelope(list_args, tmpstr);
 		child_data = part->children->context;
 		child_data->envelope_str = p_strdup(pool, str_c(tmpstr));
 
@@ -975,7 +987,7 @@
 			return -1;
 		}
 		str_append_c(str, '(');
-		imap_write_list(list_args, str);
+		imap_write_envelope(list_args, str);
 		str_append(str, ") (");
 
 		if (!imap_arg_get_list(&args[1], &list_args)) {
--- a/src/lib-imap/test-imap-bodystructure.c	Tue Dec 13 18:31:41 2016 +0200
+++ b/src/lib-imap/test-imap-bodystructure.c	Wed Dec 14 02:51:54 2016 +0100
@@ -31,6 +31,7 @@
 "Content-Type: message/rfc822\n"
 "\n"
 "From: sub@domain.org\n"
+"To: sub-to1@domain.org, sub-to2@domain.org\n"
 "Date: Sun, 12 Aug 2012 12:34:56 +0300\n"
 "Subject: submsg\n"
 "Content-Type: multipart/alternative; boundary=\"sub1\"\n"
@@ -55,10 +56,10 @@
 "Root MIME epilogue\n";
 
 static const char testmsg_bodystructure[] =
-"(\"text\" \"x-myown\" (\"charset\" \"us-ascii\" \"foo\" \"quoted\\\"string\") \"<foo@example.com>\" \"hellodescription\" \"7bit\" 7 1 \"Q2hlY2sgSW50ZWdyaXR5IQ==\" (\"inline\" (\"foo\" \"bar\")) (\"en\" \"fi\" \"se\") \"http://example.com/test.txt\")(\"message\" \"rfc822\" NIL NIL NIL \"7bit\" 368 (\"Sun, 12 Aug 2012 12:34:56 +0300\" \"submsg\" ((NIL NIL \"sub\" \"domain.org\")) ((NIL NIL \"sub\" \"domain.org\")) ((NIL NIL \"sub\" \"domain.org\")) NIL NIL NIL NIL NIL) ((\"text\" \"html\" (\"charset\" \"us-ascii\") NIL NIL \"8bit\" 20 1 NIL NIL NIL NIL)(\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 21 1 NIL NIL NIL NIL) \"alternative\" (\"boundary\" \"sub1\") NIL NIL NIL) 20 NIL NIL NIL NIL) \"mixed\" (\"boundary\" \"foo bar\") NIL NIL NIL";
+"(\"text\" \"x-myown\" (\"charset\" \"us-ascii\" \"foo\" \"quoted\\\"string\") \"<foo@example.com>\" \"hellodescription\" \"7bit\" 7 1 \"Q2hlY2sgSW50ZWdyaXR5IQ==\" (\"inline\" (\"foo\" \"bar\")) (\"en\" \"fi\" \"se\") \"http://example.com/test.txt\")(\"message\" \"rfc822\" NIL NIL NIL \"7bit\" 412 (\"Sun, 12 Aug 2012 12:34:56 +0300\" \"submsg\" ((NIL NIL \"sub\" \"domain.org\")) ((NIL NIL \"sub\" \"domain.org\")) ((NIL NIL \"sub\" \"domain.org\")) ((NIL NIL \"sub-to1\" \"domain.org\")(NIL NIL \"sub-to2\" \"domain.org\")) NIL NIL NIL NIL) ((\"text\" \"html\" (\"charset\" \"us-ascii\") NIL NIL \"8bit\" 20 1 NIL NIL NIL NIL)(\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 21 1 NIL NIL NIL NIL) \"alternative\" (\"boundary\" \"sub1\") NIL NIL NIL) 21 NIL NIL NIL NIL) \"mixed\" (\"boundary\" \"foo bar\") NIL NIL NIL";
 
 static const char testmsg_body[] =
-"(\"text\" \"x-myown\" (\"charset\" \"us-ascii\" \"foo\" \"quoted\\\"string\") \"<foo@example.com>\" \"hellodescription\" \"7bit\" 7 1)(\"message\" \"rfc822\" NIL NIL NIL \"7bit\" 368 (\"Sun, 12 Aug 2012 12:34:56 +0300\" \"submsg\" ((NIL NIL \"sub\" \"domain.org\")) ((NIL NIL \"sub\" \"domain.org\")) ((NIL NIL \"sub\" \"domain.org\")) NIL NIL NIL NIL NIL) ((\"text\" \"html\" (\"charset\" \"us-ascii\") NIL NIL \"8bit\" 20 1)(\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 21 1) \"alternative\") 20) \"mixed\"";
+"(\"text\" \"x-myown\" (\"charset\" \"us-ascii\" \"foo\" \"quoted\\\"string\") \"<foo@example.com>\" \"hellodescription\" \"7bit\" 7 1)(\"message\" \"rfc822\" NIL NIL NIL \"7bit\" 412 (\"Sun, 12 Aug 2012 12:34:56 +0300\" \"submsg\" ((NIL NIL \"sub\" \"domain.org\")) ((NIL NIL \"sub\" \"domain.org\")) ((NIL NIL \"sub\" \"domain.org\")) ((NIL NIL \"sub-to1\" \"domain.org\")(NIL NIL \"sub-to2\" \"domain.org\")) NIL NIL NIL NIL) ((\"text\" \"html\" (\"charset\" \"us-ascii\") NIL NIL \"8bit\" 20 1)(\"text\" \"plain\" (\"charset\" \"us-ascii\") NIL NIL \"7bit\" 21 1) \"alternative\") 21) \"mixed\"";
 
 static struct message_part *msg_parse(pool_t pool, bool parse_bodystructure)
 {