[evolution-mapi] Bug #619741 - Support multipart/appledouble attachments



commit 41aab21a5605dc8d33833c436906f3cdbbb9940f
Author: Milan Crha <mcrha redhat com>
Date:   Wed Jun 2 13:17:41 2010 +0200

    Bug #619741 - Support multipart/appledouble attachments

 src/libexchangemapi/exchange-mapi-mail-utils.c |  162 +++++++++++++++++++++---
 src/libexchangemapi/exchange-mapi-utils.c      |   19 +++
 src/libexchangemapi/exchange-mapi-utils.h      |    1 +
 3 files changed, 165 insertions(+), 17 deletions(-)
---
diff --git a/src/libexchangemapi/exchange-mapi-mail-utils.c b/src/libexchangemapi/exchange-mapi-mail-utils.c
index 3580c62..a615e4f 100644
--- a/src/libexchangemapi/exchange-mapi-mail-utils.c
+++ b/src/libexchangemapi/exchange-mapi-mail-utils.c
@@ -605,10 +605,57 @@ mapi_mime_build_multipart_mixed (CamelMultipart *content, GSList *attachs)
 	return m_mixed;
 }
 
+static gboolean
+is_apple_attachment (ExchangeMAPIAttachment *attach, guint32 *data_len, guint32 *resource_len)
+{
+	gboolean is_apple = FALSE;
+	ExchangeMAPIStream *enc_stream = exchange_mapi_util_find_stream (attach->streams, PR_ATTACH_ENCODING);
+	guint8 apple_enc_magic[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x14, 0x03, 0x0B, 0x01 };
+
+	if (enc_stream && enc_stream->value && enc_stream->value->len == G_N_ELEMENTS (apple_enc_magic)) {
+		gint idx;
+
+		is_apple = TRUE;
+		for (idx = 0; idx < enc_stream->value->len && is_apple; idx++) {
+			is_apple = apple_enc_magic[idx] == enc_stream->value->data[idx];
+		}
+	} else {
+		const struct Binary_r *bin = exchange_mapi_util_find_SPropVal_array_propval (attach->lpProps, PR_ATTACH_ENCODING);
+		if (bin && bin->cb == G_N_ELEMENTS (apple_enc_magic)) {
+			gint idx;
+
+			is_apple = TRUE;
+			for (idx = 0; idx < bin->cb && is_apple; idx++) {
+				is_apple = apple_enc_magic[idx] == bin->lpb[idx];
+			}
+		}
+	}
+
+	if (is_apple) {
+		/* check boundaries too */
+		ExchangeMAPIStream *data_stream = exchange_mapi_util_find_stream (attach->streams, PR_ATTACH_DATA_BIN);
+
+		is_apple = data_stream && data_stream->value && data_stream->value->len > 128;
+
+		if (is_apple) {
+			const guint8 *bin = data_stream->value->data;
+
+			/* in big-endian format */
+			*data_len = (bin[83] << 24) | (bin[84] << 16) | (bin[85] << 8) | (bin[86]);
+			*resource_len = (bin[87] << 24) | (bin[88] << 16) | (bin[89] << 8) | (bin[90]);
+
+			/* +/- mod 128 (but the first 128 is a header length) */
+			is_apple = 128 + *data_len + *resource_len <= data_stream->value->len && bin[1] < 64;
+		}
+	}
+
+	return is_apple;
+}
+
 /*Takes raw attachment streams and converts to MIME Parts. Parts are added to
   either inline / non-inline lists.*/
 static void
-mapi_mime_classify_attachments (GSList *attachments, GSList **inline_attachs, GSList **noninline)
+mapi_mime_classify_attachments (ExchangeMapiConnection *conn, mapi_id_t fid, GSList *attachments, GSList **inline_attachs, GSList **noninline)
 {
 	for (;attachments != NULL; attachments = attachments->next) {
 		ExchangeMAPIAttachment *attach = (ExchangeMAPIAttachment *)attachments->data;
@@ -617,6 +664,8 @@ mapi_mime_classify_attachments (GSList *attachments, GSList **inline_attachs, GS
 		CamelContentType *content_type;
 		CamelMimePart *part;
 		const uint32_t *ui32;
+		gboolean is_apple;
+		guint32 apple_data_len = 0, apple_resource_len = 0;
 
 		stream = exchange_mapi_util_find_stream (attach->streams, PR_ATTACH_DATA_BIN);
 
@@ -624,6 +673,24 @@ mapi_mime_classify_attachments (GSList *attachments, GSList **inline_attachs, GS
 			continue;
 		}
 
+		is_apple = is_apple_attachment (attach, &apple_data_len, &apple_resource_len);
+
+		/*Content-Type*/
+		ui32 = (const uint32_t *) exchange_mapi_util_find_SPropVal_array_propval (attach->lpProps, PR_ATTACH_METHOD);
+		if (ui32 && *ui32 == ATTACH_EMBEDDED_MSG) {
+			mime_type = "message/rfc822";
+		} else {
+			mime_type = (const gchar *) exchange_mapi_util_find_SPropVal_array_propval (attach->lpProps, PR_ATTACH_MIME_TAG);
+			if (!mime_type)
+				mime_type = "application/octet-stream";
+		}
+
+		if (is_apple) {
+			mime_type = "application/applefile";
+		} else if (strstr (mime_type, "apple") != NULL) {
+			mime_type = "application/octet-stream";
+		}
+
 		part = camel_mime_part_new ();
 
 		filename = (const gchar *) exchange_mapi_util_find_SPropVal_array_propval(attach->lpProps,
@@ -632,32 +699,93 @@ mapi_mime_classify_attachments (GSList *attachments, GSList **inline_attachs, GS
 		if (!(filename && *filename))
 			filename = (const gchar *) exchange_mapi_util_find_SPropVal_array_propval(attach->lpProps,
 												 PR_ATTACH_FILENAME_UNICODE);
-		camel_mime_part_set_filename(part, g_strdup(filename));
+		camel_mime_part_set_filename (part, filename);
 		camel_content_type_set_param (((CamelDataWrapper *) part)->mime_type, "name", filename);
 
-		/*Content-Type*/
-		ui32 = (const uint32_t *) exchange_mapi_util_find_SPropVal_array_propval (attach->lpProps, PR_ATTACH_METHOD);
-		if (ui32 && *ui32 == ATTACH_EMBEDDED_MSG) {
-			mime_type = "message/rfc822";
-		} else {
-			mime_type = (const gchar *) exchange_mapi_util_find_SPropVal_array_propval (attach->lpProps, PR_ATTACH_MIME_TAG);
+		if (is_apple) {
+			ExchangeMAPIStream *strm;
+			CamelMultipart *mp;
+			uint32_t proptag;
+			gchar *apple_filename;
+
+			mp = camel_multipart_new ();
+			camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (mp), "multipart/appledouble");
+			camel_multipart_set_boundary (mp, NULL);
+
+			camel_mime_part_set_encoding (part, CAMEL_TRANSFER_ENCODING_BASE64);
+
+			strm = NULL;
+			proptag = exchange_mapi_connection_resolve_named_prop (conn, fid, PidNameAttachmentMacInfo);
+			if (proptag != MAPI_E_RESERVED)
+				strm = exchange_mapi_util_find_stream (attach->streams, proptag);
+			if (!strm)
+				strm = exchange_mapi_util_find_stream (attach->streams, PidNameAttachmentMacInfo);
+
+			if (strm && strm->value && strm->value->len > 0) {
+				camel_mime_part_set_content (part, (const gchar *) strm->value->data, strm->value->len, mime_type);
+			} else {
+				/* RFC 1740 */
+				guint8 header[] = {
+					0x00, 0x05, 0x16, 0x07, /* magic */
+					0x00, 0x02, 0x00, 0x00, /* version */
+					0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* filler */
+					0x00, 0x01, /* number of entries */
+					0x00, 0x00, 0x00, 0x02, /* entry ID - resource fork */
+					0x00, 0x00, 0x00, 0x26, /* entry offset - 38th byte*/
+					0x00, 0x00, 0x00, 0x00  /* entry length */
+				};
+
+				GByteArray *arr = g_byte_array_sized_new (apple_resource_len + G_N_ELEMENTS (header));
+
+				header[34] = (apple_resource_len >> 24) & 0xFF;
+				header[35] = (apple_resource_len >> 16) & 0xFF;
+				header[36] = (apple_resource_len >>  8) & 0xFF;
+				header[37] = (apple_resource_len      ) & 0xFF;
+
+				g_byte_array_append (arr, header, G_N_ELEMENTS (header));
+				g_byte_array_append (arr, stream->value->data + 128 + apple_data_len + (apple_data_len % 128), apple_resource_len);
+
+				camel_mime_part_set_content (part, (const gchar *) arr->data, arr->len, mime_type);
+
+				g_byte_array_free (arr, TRUE);
+			}
+
+			camel_multipart_add_part (mp, part);
+			g_object_unref (part);
+
+			part = camel_mime_part_new ();
+
+			apple_filename = g_strndup ((gchar *)stream->value->data + 2, stream->value->data[1]);
+			camel_mime_part_set_filename (part, (apple_filename && *apple_filename) ? apple_filename : filename);
+			g_free (apple_filename);
+
+			mime_type = exchange_mapi_util_find_SPropVal_array_namedid (attach->lpProps, conn, fid, PidNameAttachmentMacContentType);
 			if (!mime_type)
 				mime_type = "application/octet-stream";
-		}
-
-		camel_mime_part_set_content (part, (const gchar *) stream->value->data, stream->value->len, mime_type);
 
-		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 if (!ui32 || *ui32 != ATTACH_EMBEDDED_MSG)
+			camel_mime_part_set_content (part, (const gchar *) stream->value->data + 128, apple_data_len, mime_type);
 			camel_mime_part_set_encoding (part, CAMEL_TRANSFER_ENCODING_BASE64);
+			camel_multipart_add_part (mp, part);
+			g_object_unref (part);
+
+			part = camel_mime_part_new ();
+			camel_medium_set_content (CAMEL_MEDIUM (part), CAMEL_DATA_WRAPPER (mp));
+			g_object_unref (mp);
+		} else {
+			camel_mime_part_set_content (part, (const gchar *) stream->value->data, stream->value->len, mime_type);
+
+			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 if (!ui32 || *ui32 != ATTACH_EMBEDDED_MSG)
+				camel_mime_part_set_encoding (part, CAMEL_TRANSFER_ENCODING_BASE64);
+		}
 
 		/*Content-ID*/
 		content_id = (const gchar *) exchange_mapi_util_find_SPropVal_array_propval(attach->lpProps,
 											   PR_ATTACH_CONTENT_ID);
 		/* TODO : Add disposition */
-		if (content_id) {
+		if (content_id && !is_apple) {
 			camel_mime_part_set_content_id (part, content_id);
 			*inline_attachs = g_slist_append (*inline_attachs, part);
 		} else
@@ -686,7 +814,7 @@ mapi_mail_item_to_mime_message (ExchangeMapiConnection *conn, MailItem *item)
 
 	mapi_mime_set_recipient_list (msg, item);
 	mapi_mime_set_msg_headers (conn, msg, item);
-	mapi_mime_classify_attachments (attach_list, &inline_attachs, &noninline_attachs);
+	mapi_mime_classify_attachments (conn, item->fid, attach_list, &inline_attachs, &noninline_attachs);
 
 	build_alternative = (g_slist_length (item->msg.body_parts) > 1) && inline_attachs;
 	build_related = !build_alternative && inline_attachs;
diff --git a/src/libexchangemapi/exchange-mapi-utils.c b/src/libexchangemapi/exchange-mapi-utils.c
index 1b351c4..cb395bd 100644
--- a/src/libexchangemapi/exchange-mapi-utils.c
+++ b/src/libexchangemapi/exchange-mapi-utils.c
@@ -163,6 +163,25 @@ exchange_mapi_util_find_SPropVal_array_propval (struct SPropValue *values, uint3
 	return (get_SPropValue(values, proptag));
 }
 
+gconstpointer
+exchange_mapi_util_find_SPropVal_array_namedid (struct SPropValue *values, ExchangeMapiConnection *conn, mapi_id_t fid, uint32_t namedid)
+{
+	uint32_t proptag;
+	gconstpointer res = NULL;
+
+	g_return_val_if_fail (values != NULL, NULL);
+	g_return_val_if_fail (conn != NULL, NULL);
+
+	proptag = exchange_mapi_connection_resolve_named_prop (conn, fid, namedid);
+	if (proptag != MAPI_E_RESERVED)
+		res = exchange_mapi_util_find_SPropVal_array_propval (values, proptag);
+
+	if (!res)
+		res = exchange_mapi_util_find_SPropVal_array_propval (values, namedid);
+
+	return res;
+}
+
 /*
  * Retrieve the property value for a given SRow and property tag.
  *
diff --git a/src/libexchangemapi/exchange-mapi-utils.h b/src/libexchangemapi/exchange-mapi-utils.h
index 57a37b0..40be87f 100644
--- a/src/libexchangemapi/exchange-mapi-utils.h
+++ b/src/libexchangemapi/exchange-mapi-utils.h
@@ -40,6 +40,7 @@ gboolean
 exchange_mapi_util_mapi_ids_from_uid (const gchar *str, mapi_id_t *fid, mapi_id_t *mid);
 
 gconstpointer exchange_mapi_util_find_SPropVal_array_propval (struct SPropValue *values, uint32_t proptag);
+gconstpointer exchange_mapi_util_find_SPropVal_array_namedid (struct SPropValue *values, ExchangeMapiConnection *conn, mapi_id_t fid, uint32_t namedid);
 gconstpointer exchange_mapi_util_find_row_propval (struct SRow *aRow, uint32_t proptag);
 gconstpointer exchange_mapi_util_find_row_namedid (struct SRow *aRow, ExchangeMapiConnection *conn, mapi_id_t fid, uint32_t namedid);
 gconstpointer exchange_mapi_util_find_array_propval (struct mapi_SPropValue_array *properties, uint32_t proptag);



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]