From ff76193025daa5c54e963195713aefdd4c34df07 Mon Sep 17 00:00:00 2001
From: Milan Crha <mcrha@redhat.com>
Date: Fri, 29 Jan 2010 18:29:55 +0000
Subject: Bug #586093 - Text Body larger than 4KB received broken

---
Index: evolution-mapi-0.28.2/src/camel/camel-mapi-folder.c
===================================================================
--- evolution-mapi-0.28.2.orig/src/camel/camel-mapi-folder.c
+++ evolution-mapi-0.28.2/src/camel/camel-mapi-folder.c
@@ -1293,10 +1293,13 @@ fetch_item_cb (FetchItemsCallbackData *i
 		item->msg.body_parts = g_slist_append (item->msg.body_parts, body);
 
 		item->is_cal = TRUE;
+
+		g_free (appointment_body_str);
 	} else { 
-		if (!((body = exchange_mapi_util_find_stream (item_data->streams, PR_HTML)) || 
+		/* always prefer unicode version, as that can be properly read */
+		if (!((body = exchange_mapi_util_find_stream (item_data->streams, PR_BODY_UNICODE)) || 
 		      (body = exchange_mapi_util_find_stream (item_data->streams, PR_BODY))))
-			body = exchange_mapi_util_find_stream (item_data->streams, PR_BODY_UNICODE);
+			body = exchange_mapi_util_find_stream (item_data->streams, PR_HTML);
 
 		item->msg.body_parts = g_slist_append (item->msg.body_parts, body);
 
@@ -1416,6 +1419,11 @@ mapi_populate_details_from_item (CamelFo
 			for (h = part->headers; h; h = h->next) {
 				const gchar *value = h->value;
 
+				/* skip all headers describing content of a message,
+				   because it's overwritten on message decomposition */
+				if (g_ascii_strncasecmp (h->name, "Content", 7) == 0)
+					continue;
+
 				while (value && camel_mime_is_lwsp (*value))
 					value++;
 
@@ -1463,15 +1471,12 @@ mapi_populate_details_from_item (CamelFo
 
 
 static void
-mapi_populate_msg_body_from_item (CamelMultipart *multipart, MapiItem *item, ExchangeMAPIStream *body)
+mapi_populate_msg_body_from_item (CamelMimePart *part, MapiItem *item, ExchangeMAPIStream *body)
 {
-	CamelMimePart *part;
-	const char* type = NULL;
-
-	part = camel_mime_part_new ();
-	camel_mime_part_set_encoding(part, CAMEL_TRANSFER_ENCODING_8BIT);
+	camel_mime_part_set_encoding (part, CAMEL_TRANSFER_ENCODING_8BIT);
 	
 	if (body) {
+		const gchar* type = NULL;
 		gchar *buff = NULL;
 
 		if (item->is_cal)
@@ -1491,14 +1496,36 @@ mapi_populate_msg_body_from_item (CamelM
 		camel_mime_part_set_content (part, (const char *) body->value->data, body->value->len, type);
 
 		g_free (buff);
-		type = NULL;
 	} else
-		camel_mime_part_set_content(part, " ", strlen(" "), "text/html");
-
-	camel_multipart_add_part (multipart, part);
-	camel_object_unref (part);
+		camel_mime_part_set_content (part, "", strlen (""), "text/plain");
 }
 
+static gint
+sort_bodies_cb (gconstpointer a, gconstpointer b)
+{
+	static const gint desired_order[] = { PR_BODY, PR_BODY_UNICODE, PR_HTML };
+	const ExchangeMAPIStream *stream_a = a, *stream_b = b;
+	gint aidx, bidx;
+
+	if (a == b)
+		return 0;
+	if (!a)
+		return -1;
+	if (!b)
+		return 1;
+
+	for (aidx = 0; aidx < G_N_ELEMENTS (desired_order); aidx++) {
+		if (desired_order[aidx] == stream_a->proptag)
+			break;
+	}
+
+	for (bidx = 0; bidx < G_N_ELEMENTS (desired_order); bidx++) {
+		if (desired_order[bidx] == stream_b->proptag)
+			break;
+	}
+
+	return aidx - bidx;
+}
 
 static CamelMimeMessage *
 mapi_folder_item_to_msg( CamelFolder *folder,
@@ -1507,32 +1534,14 @@ mapi_folder_item_to_msg( CamelFolder *fo
 {
 	CamelMimeMessage *msg = NULL;
 	CamelMultipart *multipart = NULL;
+	CamelMimePart *part = NULL;
+	CamelDataWrapper *msg_content = NULL;
 
 	GSList *attach_list = NULL;
-	/* int errno; */
-	/* char *body = NULL; */
-	ExchangeMAPIStream *body = NULL;
 	GSList *body_part_list = NULL;
 
 	attach_list = item->attachments;
 	msg = camel_mime_message_new ();
-	multipart = camel_multipart_new ();
-
-	/*FIXME : Using set of default. Fix it during mimewriter*/
-	camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (multipart),
-					  "multipart/related");
-	camel_content_type_set_param(CAMEL_DATA_WRAPPER (multipart)->mime_type, 
-				     "type", "multipart/alternative");
-
-	camel_multipart_set_boundary (multipart, NULL);
-
-	/* Handle Multipart */
-	body_part_list = item->msg.body_parts;
-	while (body_part_list){
-	       body = body_part_list->data;
-	       mapi_populate_msg_body_from_item (multipart, item, body);	       
-	       body_part_list = g_slist_next (body_part_list);
-	}
 
 	/* Threading */
 	if (item->header.message_id)
@@ -1544,18 +1553,69 @@ mapi_folder_item_to_msg( CamelFolder *fo
 	if (item->header.in_reply_to)
 		camel_medium_add_header (CAMEL_MEDIUM (msg), "In-Reply-To", item->header.in_reply_to);
 
-	/*Set recipient details*/
+	/* Set recipient details */
 	mapi_msg_set_recipient_list (msg, item);
 	mapi_populate_details_from_item (folder, msg, item);
 
-	/** Attachment Handling*/
+	if (g_slist_length (attach_list) > 0) {
+		multipart = camel_multipart_new ();
+		camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (multipart), "multipart/mixed");
+		camel_multipart_set_boundary (multipart, NULL);
+
+		msg_content = CAMEL_DATA_WRAPPER (multipart);
+	}
+
+	if (g_slist_length (item->msg.body_parts) > 1) {
+		multipart = camel_multipart_new ();
+		camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (multipart), "multipart/alternative");
+		camel_multipart_set_boundary (multipart, NULL);
+
+		item->msg.body_parts = g_slist_sort (item->msg.body_parts, sort_bodies_cb);
+
+		/* Handle Multipart */
+		for (body_part_list = item->msg.body_parts; body_part_list; body_part_list = g_slist_next (body_part_list)) {
+			part = camel_mime_part_new ();
+
+			mapi_populate_msg_body_from_item (part, item, body_part_list->data);
+
+			camel_multipart_add_part (CAMEL_MULTIPART (msg_content), part);
+			camel_object_unref (part);
+		}
+
+		if (msg_content) {
+			part = camel_mime_part_new ();
+			camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (part), "multipart/alternative");
+			camel_medium_set_content_object (CAMEL_MEDIUM (part), CAMEL_DATA_WRAPPER (multipart));
+			camel_object_unref (multipart);
+
+			camel_multipart_add_part (CAMEL_MULTIPART (msg_content), part);
+			camel_object_unref (part);
+		} else {
+			msg_content = CAMEL_DATA_WRAPPER (multipart);
+		}
+	} else {
+		if (msg_content) {
+			part = camel_mime_part_new ();
+			mapi_populate_msg_body_from_item (part, item, item->msg.body_parts ? item->msg.body_parts->data : NULL);
+			camel_multipart_add_part (CAMEL_MULTIPART (msg_content), part);
+			camel_object_unref (part);
+		} else {
+			msg_content = CAMEL_DATA_WRAPPER (msg);
+			mapi_populate_msg_body_from_item (CAMEL_MIME_PART (msg), item, item->msg.body_parts ? item->msg.body_parts->data : NULL);
+		}
+	}
+
+	/* Attachment Handling */
 	if (attach_list) {
-		GSList *al = attach_list;
+		GSList *al;
+
+		multipart = CAMEL_MULTIPART (msg_content);
+
 		for (al = attach_list; al != NULL; al = al->next) {
 			ExchangeMAPIAttachment *attach = (ExchangeMAPIAttachment *)al->data;
 			ExchangeMAPIStream *stream = NULL;
 			const char *filename, *mime_type, *content_id = NULL; 
-			CamelMimePart *part;
+			CamelContentType *content_type;
 
 			stream = exchange_mapi_util_find_stream (attach->streams, PR_ATTACH_DATA_BIN);
 
@@ -1575,8 +1635,9 @@ mapi_folder_item_to_msg( CamelFolder *fo
 			camel_content_type_set_param (((CamelDataWrapper *) part)->mime_type, "name", filename);
 
 			/*Content-Type*/
-			mime_type = (const char *) exchange_mapi_util_find_SPropVal_array_propval(attach->lpProps, 
-												  PR_ATTACH_MIME_TAG);
+			mime_type = (const char *) exchange_mapi_util_find_SPropVal_array_propval (attach->lpProps, PR_ATTACH_MIME_TAG);
+			if (!mime_type)
+				mime_type = "application/octet-stream";
 
 			camel_mime_part_set_content (part, (const char *) stream->value->data, stream->value->len, mime_type);
 
@@ -1586,8 +1647,11 @@ mapi_folder_item_to_msg( CamelFolder *fo
 
 			camel_mime_part_set_content_id (part, content_id);
 
-			/*FIXME : Mime Reader / Writer work*/
-			//camel_mime_part_set_encoding (part, CAMEL_TRANSFER_ENCODING_BASE64);
+			content_type = camel_mime_part_get_content_type (part);
+			if (content_type && camel_content_type_is (content_type, "text", "*"))
+				camel_mime_part_set_encoding (part, CAMEL_TRANSFER_ENCODING_QUOTEDPRINTABLE);
+			else
+				camel_mime_part_set_encoding (part, CAMEL_TRANSFER_ENCODING_BASE64);
 
 			camel_multipart_add_part (multipart, part);
 			camel_object_unref (part);
@@ -1596,16 +1660,14 @@ mapi_folder_item_to_msg( CamelFolder *fo
 		exchange_mapi_util_free_attachment_list (&attach_list);
 	}
 
-	camel_medium_set_content_object(CAMEL_MEDIUM (msg), CAMEL_DATA_WRAPPER(multipart));
-	camel_object_unref (multipart);
-
-	if (body)
-		g_free (body);
+	if (msg_content != CAMEL_DATA_WRAPPER (msg)) {
+		camel_medium_set_content_object (CAMEL_MEDIUM (msg), msg_content);
+		camel_object_unref (msg_content);
+	}
 
 	return msg;
 }
 
-
 static CamelMimeMessage *
 mapi_folder_get_message( CamelFolder *folder, const char *uid, CamelException *ex )
 {
Index: evolution-mapi-0.28.2/src/camel/camel-mapi-utils.c
===================================================================
--- evolution-mapi-0.28.2.orig/src/camel/camel-mapi-utils.c
+++ evolution-mapi-0.28.2/src/camel/camel-mapi-utils.c
@@ -133,18 +133,17 @@ mapi_item_set_body_stream (MapiItem *ite
 	guint8 *buf = g_new0 (guint8 , STREAM_SIZE);
 	guint32	read_size = 0, i;
 	ExchangeMAPIStream *stream = g_new0 (ExchangeMAPIStream, 1);
-	gboolean contains_only_7bit = TRUE;
+	gboolean contains_only_7bit = TRUE, is_null_terminated = FALSE;
 
 	camel_seekable_stream_seek((CamelSeekableStream *)body, 0, CAMEL_STREAM_SET);
 
 	stream->value = g_byte_array_new ();
 
-	while((read_size = camel_stream_read(body, (char *)buf, STREAM_SIZE))){
-		if (read_size == -1) 
-			return;
-
+	while (read_size = camel_stream_read (body, (char *)buf, STREAM_SIZE), read_size > 0){
 		stream->value = g_byte_array_append (stream->value, buf, read_size);
 
+		is_null_terminated = buf [read_size - 1] == 0;
+
 		for (i = 0; i < read_size && contains_only_7bit; i++) {
 			contains_only_7bit = buf[i] < 128;
 		}
@@ -162,22 +161,26 @@ mapi_item_set_body_stream (MapiItem *ite
 	}
 
 	if (stream->value->len < MAX_READ_SIZE && contains_only_7bit) {
+		if (!is_null_terminated)
+			g_byte_array_append (stream->value, (const guint8 *)"", 1);
+
 		item->msg.body_parts = g_slist_append (item->msg.body_parts, stream);
 	} else {
 		gsize written = 0;
 		gchar *in_unicode;
-		guint8 byte = 0;
 
-		while (stream->value->len > 0 && !stream->value->data [stream->value->len - 1]) {
+		if (is_null_terminated)
 			stream->value->len--;
-		}
+
 		/* convert to unicode, because stream is supposed to be in it */
 		in_unicode = g_convert ((const gchar *)stream->value->data, stream->value->len, "UTF-16", "UTF-8", NULL, &written, NULL);
 		if (in_unicode && written > 0) {
 			g_byte_array_set_size (stream->value, 0);
 			g_byte_array_append (stream->value, (const guint8 *) in_unicode, written);
-			g_byte_array_append (stream->value, &byte, 1);
-			g_byte_array_append (stream->value, &byte, 1);
+
+			/* null-terminated unicode string */
+			g_byte_array_append (stream->value, (const guint8 *)"", 1);
+			g_byte_array_append (stream->value, (const guint8 *)"", 1);
 		}
 		g_free (in_unicode);
 
@@ -192,6 +195,7 @@ mapi_item_add_attach (MapiItem *item, Ca
 	guint8 *buf = g_new0 (guint8 , STREAM_SIZE);
 	const gchar *content_id = NULL;
 	guint32	read_size, flag, i = 0;
+	CamelContentType *content_type;
 
 	ExchangeMAPIAttachment *item_attach;
 	ExchangeMAPIStream *stream; 
@@ -200,7 +204,7 @@ mapi_item_add_attach (MapiItem *item, Ca
 	
 	item_attach = g_new0 (ExchangeMAPIAttachment, 1);
 
-	item_attach->lpProps = g_new0 (struct SPropValue, 5);
+	item_attach->lpProps = g_new0 (struct SPropValue, 6);
 
 	flag = ATTACH_BY_VALUE; 
 	set_SPropValue_proptag(&(item_attach->lpProps[i++]), PR_ATTACH_METHOD, (const void *) (&flag));
@@ -231,6 +235,16 @@ mapi_item_add_attach (MapiItem *item, Ca
 				       (const void *) g_strdup(content_id));
 	}
 
+	content_type  = camel_mime_part_get_content_type (part);
+	if (content_type) {
+		gchar *ct = camel_content_type_simple (content_type);
+		if (ct) {
+			set_SPropValue_proptag (&(item_attach->lpProps[i++]), 
+					PR_ATTACH_MIME_TAG,
+					(const void *) ct);
+		}
+	}
+
 	item_attach->cValues = i;
 
 	stream = g_new0 (ExchangeMAPIStream, 1);
@@ -238,7 +252,7 @@ mapi_item_add_attach (MapiItem *item, Ca
 	stream->value = g_byte_array_new ();
 
 	camel_seekable_stream_seek((CamelSeekableStream *)content_stream, 0, CAMEL_STREAM_SET);
-	while((read_size = camel_stream_read(content_stream, (char *)buf, STREAM_SIZE))){
+	while (read_size = camel_stream_read(content_stream, (char *)buf, STREAM_SIZE), read_size > 0) {
 		stream->value = g_byte_array_append (stream->value, buf, read_size);
 	}
 
@@ -249,7 +263,7 @@ mapi_item_add_attach (MapiItem *item, Ca
 }
 
 static gboolean 
-mapi_do_multipart(CamelMultipart *mp, MapiItem *item)
+mapi_do_multipart (CamelMultipart *mp, MapiItem *item, gboolean *is_first)
 {
 	CamelDataWrapper *dw;
 	CamelStream *content_stream;
@@ -261,6 +275,8 @@ mapi_do_multipart(CamelMultipart *mp, Ma
 	const gchar *content_id;
 	gint content_size;
 
+	g_return_val_if_fail (is_first != NULL, FALSE);
+
 	n_part = camel_multipart_get_number(mp);
 	for (i_part = 0; i_part < n_part; i_part++) {
 		/* getting part */
@@ -268,7 +284,7 @@ mapi_do_multipart(CamelMultipart *mp, Ma
 		dw = camel_medium_get_content_object (CAMEL_MEDIUM (part));
 		if (CAMEL_IS_MULTIPART(dw)) {
 			/* recursive */
-			if (!mapi_do_multipart(CAMEL_MULTIPART(dw), item))
+			if (!mapi_do_multipart (CAMEL_MULTIPART (dw), item, is_first))
 				return FALSE;
 			continue ;
 		}
@@ -277,7 +293,6 @@ mapi_do_multipart(CamelMultipart *mp, Ma
 
 		content_stream = camel_stream_mem_new();
 		content_size = camel_data_wrapper_decode_to_stream (dw, (CamelStream *) content_stream);
-		camel_stream_write ((CamelStream *) content_stream, "", 1);
 
 		camel_seekable_stream_seek((CamelSeekableStream *)content_stream, 0, CAMEL_STREAM_SET);
 
@@ -286,8 +301,9 @@ mapi_do_multipart(CamelMultipart *mp, Ma
 		
 		type = camel_mime_part_get_content_type(part);
 
-		if (i_part == 0 && camel_content_type_is (type, "text", "plain")) {
+		if (i_part == 0 && (*is_first) && camel_content_type_is (type, "text", "plain")) {
 			mapi_item_set_body_stream (item, content_stream, PART_TYPE_PLAIN_TEXT);
+			*is_first = FALSE;
 		} else if (camel_content_type_is (type, "text", "html")) {
 			mapi_item_set_body_stream (item, content_stream, PART_TYPE_TEXT_HTML);
 		} else {
@@ -355,7 +371,8 @@ camel_mapi_utils_mime_to_item (CamelMime
 	multipart = (CamelMultipart *)camel_medium_get_content_object (CAMEL_MEDIUM (message));
 
 	if (CAMEL_IS_MULTIPART(multipart)) {
-		if (mapi_do_multipart(CAMEL_MULTIPART(multipart), item))
+		gboolean is_first = TRUE;
+		if (!mapi_do_multipart (CAMEL_MULTIPART(multipart), item, &is_first))
 			printf("camel message multi part error\n"); 
 	} else {
 		dw = camel_medium_get_content_object (CAMEL_MEDIUM (message));
@@ -365,7 +382,6 @@ camel_mapi_utils_mime_to_item (CamelMime
 
 			content_stream = (CamelStream *)camel_stream_mem_new();
 			content_size = camel_data_wrapper_decode_to_stream(dw, (CamelStream *)content_stream);
-			camel_stream_write ((CamelStream *) content_stream, "", 1);
 
 			mapi_item_set_body_stream (item, content_stream, PART_TYPE_PLAIN_TEXT);
 		}
