[evolution-mapi] Use fast-transfer for message fetching in camel
- From: Milan Crha <mcrha src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [evolution-mapi] Use fast-transfer for message fetching in camel
- Date: Thu, 10 Nov 2011 12:02:30 +0000 (UTC)
commit 6f0c329838389f562a8038b4c686a14b82c7bc7b
Author: Milan Crha <mcrha redhat com>
Date: Thu Nov 10 13:01:45 2011 +0100
Use fast-transfer for message fetching in camel
src/camel/camel-mapi-folder.c | 364 ++++++++-----
src/camel/camel-mapi-store.c | 71 +--
src/libexchangemapi/e-mapi-cal-recur-utils.c | 8 +-
src/libexchangemapi/e-mapi-cal-recur-utils.h | 2 +-
src/libexchangemapi/e-mapi-cal-tz-utils.c | 5 +-
src/libexchangemapi/e-mapi-cal-tz-utils.h | 2 +-
src/libexchangemapi/e-mapi-cal-utils.c | 630 ++++++++++++++++++++-
src/libexchangemapi/e-mapi-cal-utils.h | 10 +-
src/libexchangemapi/e-mapi-connection.c | 289 ++++++++++-
src/libexchangemapi/e-mapi-connection.h | 10 +-
src/libexchangemapi/e-mapi-debug.c | 8 +-
src/libexchangemapi/e-mapi-fast-transfer.c | 40 ++-
src/libexchangemapi/e-mapi-fast-transfer.h | 9 +
src/libexchangemapi/e-mapi-mail-utils.c | 774 ++++++++++++++++++++++++++
src/libexchangemapi/e-mapi-mail-utils.h | 28 +-
src/libexchangemapi/e-mapi-utils.c | 63 +++
src/libexchangemapi/e-mapi-utils.h | 18 +-
17 files changed, 2106 insertions(+), 225 deletions(-)
---
diff --git a/src/camel/camel-mapi-folder.c b/src/camel/camel-mapi-folder.c
index 4c3221b..46fd125 100644
--- a/src/camel/camel-mapi-folder.c
+++ b/src/camel/camel-mapi-folder.c
@@ -377,6 +377,48 @@ mapi_utils_do_flags_diff (flags_diff_t *diff, guint32 old, guint32 _new)
diff->bits = _new & diff->changed;
}
+static void
+add_message_to_cache (CamelMapiFolder *mapi_folder, const gchar *uid, CamelMimeMessage **msg, GCancellable *cancellable)
+{
+ CamelFolder *folder;
+ CamelStream *cache_stream;
+
+ g_return_if_fail (mapi_folder != NULL);
+ g_return_if_fail (msg != NULL);
+ g_return_if_fail (*msg != NULL);
+
+ folder = CAMEL_FOLDER (mapi_folder);
+ g_return_if_fail (folder != NULL);
+
+ camel_folder_summary_lock (folder->summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
+
+ if ((cache_stream = camel_data_cache_add (mapi_folder->cache, "cache", uid, NULL))) {
+ if (camel_data_wrapper_write_to_stream_sync ((CamelDataWrapper *) (*msg), cache_stream, cancellable, NULL) == -1
+ || camel_stream_flush (cache_stream, cancellable, NULL) == -1) {
+ camel_data_cache_remove (mapi_folder->cache, "cache", uid, NULL);
+ } else {
+ CamelMimeMessage *msg2;
+
+ /* workaround to get message back from cache, as that one is properly
+ encoded with attachments and so on. Not sure what's going wrong when
+ composing message in memory, but when they are read from the cache
+ they appear properly in the UI. */
+ msg2 = camel_mime_message_new ();
+ g_seekable_seek (G_SEEKABLE (cache_stream), 0, G_SEEK_SET, NULL, NULL);
+ if (!camel_data_wrapper_construct_from_stream_sync (CAMEL_DATA_WRAPPER (msg2), cache_stream, cancellable, NULL)) {
+ g_object_unref (msg2);
+ } else {
+ g_object_unref (*msg);
+ *msg = msg2;
+ }
+ }
+
+ g_object_unref (cache_stream);
+ }
+
+ camel_folder_summary_unlock (folder->summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
+}
+
static gboolean
build_last_modify_restriction (EMapiConnection *conn,
mapi_id_t fid,
@@ -518,6 +560,114 @@ remove_removed_uids_cb (gpointer uid_str, gpointer value, gpointer user_data)
}
static gboolean
+gather_object_offline_cb (EMapiConnection *conn,
+ TALLOC_CTX *mem_ctx,
+ /* const */ EMapiObject *object,
+ guint32 obj_index,
+ guint32 obj_total,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **perror)
+{
+ struct GatherObjectSummaryData *gos = user_data;
+ CamelMimeMessage *msg;
+
+ g_return_val_if_fail (gos != NULL, FALSE);
+ g_return_val_if_fail (gos->folder != NULL, FALSE);
+ g_return_val_if_fail (object != NULL, FALSE);
+
+ msg = e_mapi_mail_utils_object_to_message (conn, object);
+ if (msg) {
+ gchar *uid_str;
+ const mapi_id_t *pmid;
+ const uint32_t *pmsg_flags;
+ const struct FILETIME *last_modified;
+ uint32_t msg_flags;
+ CamelMessageInfo *info;
+ gboolean is_new;
+
+ pmid = e_mapi_util_find_array_propval (&object->properties, PR_MID);
+ pmsg_flags = e_mapi_util_find_array_propval (&object->properties, PR_MESSAGE_FLAGS);
+ last_modified = e_mapi_util_find_array_propval (&object->properties, PR_LAST_MODIFICATION_TIME);
+
+ if (!pmid) {
+ g_debug ("%s: Received message [%d/%d] without PR_MID", G_STRFUNC, obj_index, obj_total);
+ e_mapi_debug_dump_object (object, TRUE, 3);
+ return TRUE;
+ }
+
+ if (!last_modified) {
+ g_debug ("%s: Received message [%d/%d] without PR_LAST_MODIFICATION_TIME", G_STRFUNC, obj_index, obj_total);
+ e_mapi_debug_dump_object (object, TRUE, 3);
+ }
+
+ uid_str = e_mapi_util_mapi_id_to_string (*pmid);
+ if (!uid_str)
+ return FALSE;
+
+ msg_flags = pmsg_flags ? *pmsg_flags : 0;
+
+ is_new = !camel_folder_summary_check_uid (gos->folder->summary, uid_str);
+ if (!is_new)
+ camel_folder_summary_remove_uid (gos->folder->summary, uid_str);
+
+ info = camel_folder_summary_info_new_from_message (gos->folder->summary, msg, NULL);
+ if (info) {
+ CamelMapiMessageInfo *minfo = (CamelMapiMessageInfo *) info;
+ guint32 flags = 0, mask = CAMEL_MESSAGE_SEEN | CAMEL_MESSAGE_ATTACHMENTS;
+
+ minfo->info.uid = camel_pstring_strdup (uid_str);
+
+ if (last_modified) {
+ minfo->last_modified = e_mapi_util_filetime_to_time_t (last_modified);
+ } else {
+ minfo->last_modified = 0;
+ }
+
+ if ((msg_flags & MSGFLAG_READ) != 0)
+ flags |= CAMEL_MESSAGE_SEEN;
+ if ((msg_flags & MSGFLAG_HASATTACH) != 0)
+ flags |= CAMEL_MESSAGE_ATTACHMENTS;
+
+ if ((camel_message_info_flags (info) & mask) != flags) {
+ if (is_new)
+ minfo->info.flags = flags;
+ else
+ camel_message_info_set_flags (info, mask, flags);
+ minfo->server_flags = camel_message_info_flags (info);
+ minfo->info.dirty = TRUE;
+ }
+
+ if (is_new) {
+ camel_folder_summary_add (gos->folder->summary, info);
+ camel_folder_change_info_add_uid (gos->changes, camel_message_info_uid (info));
+ camel_folder_change_info_recent_uid (gos->changes, camel_message_info_uid (info));
+
+ camel_message_info_ref (info);
+ } else {
+ camel_folder_change_info_change_uid (gos->changes, camel_message_info_uid (info));
+ }
+
+ add_message_to_cache (CAMEL_MAPI_FOLDER (gos->folder), uid_str, &msg, cancellable);
+
+ camel_message_info_free (info);
+ } else {
+ g_debug ("%s: Failed to create message info from message", G_STRFUNC);
+ }
+
+ g_free (uid_str);
+ g_object_unref (msg);
+ } else {
+ g_debug ("%s: Failed to create message from object", G_STRFUNC);
+ }
+
+ if (obj_total > 0)
+ camel_operation_progress (cancellable, obj_index * 100 / obj_total);
+
+ return TRUE;
+}
+
+static gboolean
gather_object_summary_cb (EMapiConnection *conn,
TALLOC_CTX *mem_ctx,
/* const */ EMapiObject *object,
@@ -594,11 +744,10 @@ gather_object_summary_cb (EMapiConnection *conn,
if (!info) {
CamelMapiMessageInfo *minfo;
const gchar *subject, *message_id, *references, *in_reply_to, *display_to, *display_cc;
- const gchar *from_addr_type, *from_name, *from_email;
const struct FILETIME *delivery_time;
const uint32_t *msg_size;
- EMapiRecipient *recipient;
- gchar *to = NULL, *cc = NULL, *formatted_addr;
+ gchar *formatted_addr, *from_name, *from_email;
+ CamelAddress *to_addr, *cc_addr, *bcc_addr;
subject = e_mapi_util_find_array_propval (&object->properties, PR_SUBJECT_UNICODE);
delivery_time = e_mapi_util_find_array_propval (&object->properties, PR_MESSAGE_DELIVERY_TIME);
@@ -608,9 +757,6 @@ gather_object_summary_cb (EMapiConnection *conn,
in_reply_to = e_mapi_util_find_array_propval (&object->properties, PR_IN_REPLY_TO_ID);
display_to = e_mapi_util_find_array_propval (&object->properties, PR_DISPLAY_TO_UNICODE);
display_cc = e_mapi_util_find_array_propval (&object->properties, PR_DISPLAY_CC_UNICODE);
- from_addr_type = e_mapi_util_find_array_propval (&object->properties, PR_SENT_REPRESENTING_ADDRTYPE);
- from_name = e_mapi_util_find_array_propval (&object->properties, PR_SENT_REPRESENTING_NAME_UNICODE);
- from_email = e_mapi_util_find_array_propval (&object->properties, PR_SENT_REPRESENTING_EMAIL_ADDRESS_UNICODE);
info = camel_message_info_new (gos->folder->summary);
minfo = (CamelMapiMessageInfo *) info;
@@ -626,78 +772,51 @@ gather_object_summary_cb (EMapiConnection *conn,
mapi_set_message_references (minfo, references, in_reply_to);
/* Recipients */
- for (recipient = object->recipients; recipient; recipient = recipient->next) {
- const uint32_t *recip_type = e_mapi_util_find_array_propval (&recipient->properties, PR_RECIPIENT_TYPE);
- const gchar *name, *email;
- gchar **dest = NULL;
-
- if (!recip_type)
- continue;
-
- switch (*recip_type) {
- case MAPI_TO:
- dest = &to;
- break;
- case MAPI_CC:
- dest = &cc;
- break;
- default:
- break;
- }
+ to_addr = (CamelAddress *) camel_internet_address_new ();
+ cc_addr = (CamelAddress *) camel_internet_address_new ();
+ bcc_addr = (CamelAddress *) camel_internet_address_new ();
- if (!dest)
- continue;
-
- /* PidTagNickname for Recipients table */
- name = e_mapi_util_find_array_propval (&recipient->properties, PROP_TAG (PT_UNICODE, 0x6001));
- name = name ? name : e_mapi_util_find_array_propval (&recipient->properties, PidTagNickname);
- name = name ? name : e_mapi_util_find_array_propval (&recipient->properties, PR_DISPLAY_NAME_UNICODE);
- name = name ? name : e_mapi_util_find_array_propval (&recipient->properties, PR_RECIPIENT_DISPLAY_NAME_UNICODE);
- name = name ? name : e_mapi_util_find_array_propval (&recipient->properties, PR_7BIT_DISPLAY_NAME_UNICODE);
-
- email = e_mapi_util_find_array_propval (&recipient->properties, PidTagPrimarySmtpAddress);
- email = email ? email : e_mapi_util_find_array_propval (&recipient->properties, PidTagSmtpAddress);
-
- formatted_addr = camel_internet_address_format_address (name, email ? email : "");
- if (*dest) {
- gchar *tmp = *dest;
- *dest = g_strconcat (*dest, ", ", formatted_addr, NULL);
- g_free (formatted_addr);
- g_free (tmp);
- } else {
- *dest = formatted_addr;
- }
- }
+ e_mapi_mail_utils_decode_recipients (conn, object->recipients, to_addr, cc_addr, bcc_addr);
- minfo->info.to = to ? camel_pstring_strdup (to) : camel_pstring_strdup (display_to);
- minfo->info.cc = cc ? camel_pstring_strdup (cc) : camel_pstring_strdup (display_cc);
+ if (camel_address_length (to_addr) > 0) {
+ formatted_addr = camel_address_format (to_addr);
+ minfo->info.to = camel_pstring_strdup (formatted_addr);
+ g_free (formatted_addr);
+ } else {
+ minfo->info.to = camel_pstring_strdup (display_to);
+ }
- if (from_addr_type && g_ascii_strcasecmp (from_addr_type, "EX") == 0) {
- gchar *email = NULL, *name = NULL;
+ if (camel_address_length (cc_addr) > 0) {
+ formatted_addr = camel_address_format (cc_addr);
+ minfo->info.cc = camel_pstring_strdup (formatted_addr);
+ g_free (formatted_addr);
+ } else {
+ minfo->info.cc = camel_pstring_strdup (display_cc);
+ }
- email = e_mapi_connection_ex_to_smtp (conn, from_email, &name, cancellable, perror);
- if (email && *email) {
- gchar *from = camel_internet_address_format_address (name, email);
+ g_object_unref (to_addr);
+ g_object_unref (cc_addr);
+ g_object_unref (bcc_addr);
- minfo->info.from = camel_pstring_strdup (from);
+ from_name = NULL;
+ from_email = NULL;
- g_free (from);
- } else if (from_name && *from_name) {
- minfo->info.from = camel_pstring_strdup (from_name);
- }
+ e_mapi_mail_utils_decode_email_address1 (conn, &object->properties,
+ PR_SENT_REPRESENTING_NAME_UNICODE,
+ PR_SENT_REPRESENTING_EMAIL_ADDRESS_UNICODE,
+ PR_SENT_REPRESENTING_ADDRTYPE,
+ &from_name, &from_email);
- g_free (name);
- g_free (email);
- } else if (from_email) {
- gchar *from = camel_internet_address_format_address (from_name, from_email);
+ if (from_email && *from_email) {
+ formatted_addr = camel_internet_address_format_address (from_name, from_email);
- minfo->info.from = camel_pstring_strdup (from);
+ minfo->info.from = camel_pstring_strdup (formatted_addr);
- g_free (from);
+ g_free (formatted_addr);
}
-
- g_free (to);
- g_free (cc);
+
+ g_free (from_name);
+ g_free (from_email);
}
}
@@ -770,10 +889,7 @@ camel_mapi_folder_fetch_summary (CamelFolder *folder, GCancellable *cancellable,
camel_offline_settings_get_stay_synchronized (CAMEL_OFFLINE_SETTINGS (settings)) ||
camel_offline_folder_get_offline_sync (CAMEL_OFFLINE_FOLDER (folder));
- if (full_download)
- camel_operation_push_message (cancellable, _("Downloading messages in folder '%s'"), camel_folder_get_display_name (folder));
- else
- camel_operation_push_message (cancellable, _("Refreshing folder '%s'"), camel_folder_get_display_name (folder));
+ camel_operation_push_message (cancellable, _("Refreshing folder '%s'"), camel_folder_get_display_name (folder));
camel_service_lock (CAMEL_SERVICE (mapi_store), CAMEL_SERVICE_REC_CONNECT_LOCK);
@@ -829,7 +945,15 @@ camel_mapi_folder_fetch_summary (CamelFolder *folder, GCancellable *cancellable,
if (gco.removed_uids)
g_hash_table_foreach (gco.removed_uids, remove_removed_uids_cb, &gos);
- status = e_mapi_connection_transfer_summary (conn, &obj_folder, uids, gather_object_summary_cb, &gos, cancellable, mapi_error);
+ if (full_download) {
+ camel_operation_push_message (cancellable, _("Downloading messages in folder '%s'"), camel_folder_get_display_name (folder));
+
+ status = e_mapi_connection_transfer_objects (conn, &obj_folder, uids, gather_object_offline_cb, &gos, cancellable, mapi_error);
+
+ camel_operation_pop_message (cancellable);
+ } else {
+ status = e_mapi_connection_transfer_summary (conn, &obj_folder, uids, gather_object_summary_cb, &gos, cancellable, mapi_error);
+ }
g_slist_free (uids);
@@ -1370,6 +1494,29 @@ mapi_folder_get_message_cached (CamelFolder *folder,
return msg;
}
+static gboolean
+transfer_mail_object_cb (EMapiConnection *conn,
+ TALLOC_CTX *mem_ctx,
+ /* const */ EMapiObject *object,
+ guint32 obj_index,
+ guint32 obj_total,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **perror)
+{
+ CamelMimeMessage **pmessage = user_data;
+
+ g_return_val_if_fail (object != NULL, FALSE);
+ g_return_val_if_fail (pmessage != NULL, FALSE);
+
+ *pmessage = e_mapi_mail_utils_object_to_message (conn, object);
+
+ if (obj_total > 0)
+ camel_operation_progress (cancellable, obj_index * 100 / obj_total);
+
+ return TRUE;
+}
+
static CamelMimeMessage *
mapi_folder_get_message_sync (CamelFolder *folder,
const gchar *uid,
@@ -1380,11 +1527,11 @@ mapi_folder_get_message_sync (CamelFolder *folder,
CamelMapiFolder *mapi_folder;
CamelMapiStore *mapi_store;
CamelMapiMessageInfo *mi = NULL;
- CamelStream *cache_stream;
CamelStore *parent_store;
mapi_id_t id_message;
- MailItem *item = NULL;
- guint32 options = 0;
+ EMapiConnection *conn;
+ mapi_object_t obj_folder;
+ gboolean success;
GError *mapi_error = NULL;
parent_store = camel_folder_get_parent_store (folder);
@@ -1431,23 +1578,22 @@ mapi_folder_get_message_sync (CamelFolder *folder,
return NULL;
}
- options = MAPI_OPTIONS_FETCH_ALL | MAPI_OPTIONS_FETCH_BODY_STREAM |
- MAPI_OPTIONS_GETBESTBODY | MAPI_OPTIONS_FETCH_RECIPIENTS;
-
e_mapi_util_mapi_id_from_string (uid, &id_message);
- if (((CamelMapiFolder *)folder)->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_PUBLIC) {
- options |= MAPI_OPTIONS_USE_PFSTORE;
- }
+ conn = camel_mapi_store_get_connection (mapi_store);
- camel_service_lock (CAMEL_SERVICE (mapi_store), CAMEL_SERVICE_REC_CONNECT_LOCK);
- e_mapi_connection_fetch_item (camel_mapi_store_get_connection (mapi_store), mapi_folder->folder_id, id_message,
- mapi_mail_get_item_prop_list, NULL,
- fetch_props_to_mail_item_cb, &item,
- options, cancellable, &mapi_error);
- camel_service_unlock (CAMEL_SERVICE (mapi_store), CAMEL_SERVICE_REC_CONNECT_LOCK);
+ if (mapi_folder->mapi_folder_flags & CAMEL_MAPI_STORE_FOLDER_FLAG_PUBLIC)
+ success = e_mapi_connection_open_public_folder (conn, mapi_folder->folder_id, &obj_folder, cancellable, &mapi_error);
+ else
+ success = e_mapi_connection_open_personal_folder (conn, mapi_folder->folder_id, &obj_folder, cancellable, &mapi_error);
- if (item == NULL) {
+ if (success) {
+ success = e_mapi_connection_transfer_object (conn, &obj_folder, id_message, transfer_mail_object_cb, &msg, cancellable, &mapi_error);
+
+ e_mapi_connection_close_folder (conn, &obj_folder, cancellable, NULL);
+ }
+
+ if (!msg) {
if (mapi_error) {
g_set_error (
error, CAMEL_SERVICE_ERROR,
@@ -1464,45 +1610,7 @@ mapi_folder_get_message_sync (CamelFolder *folder,
return NULL;
}
- msg = mapi_mail_item_to_mime_message (camel_mapi_store_get_connection (mapi_store), item);
- mail_item_free (item);
-
- if (!msg) {
- g_set_error (
- error, CAMEL_SERVICE_ERROR,
- CAMEL_SERVICE_ERROR_INVALID,
- _("Could not get message"));
- camel_message_info_free (&mi->info);
-
- return NULL;
- }
-
- /* add to cache */
- camel_folder_summary_lock (folder->summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
- if ((cache_stream = camel_data_cache_add (mapi_folder->cache, "cache", uid, NULL))) {
- if (camel_data_wrapper_write_to_stream_sync ((CamelDataWrapper *) msg, cache_stream, cancellable, NULL) == -1
- || camel_stream_flush (cache_stream, cancellable, NULL) == -1) {
- camel_data_cache_remove (mapi_folder->cache, "cache", uid, NULL);
- } else {
- CamelMimeMessage *msg2;
-
- /* workaround to get message back from cache, as that one is properly
- encoded with attachments and so on. Not sure what's going wrong when
- composing message in memory, but when they are read from the cache
- they appear properly in the UI. */
- msg2 = camel_mime_message_new ();
- g_seekable_seek (G_SEEKABLE (cache_stream), 0, G_SEEK_SET, NULL, NULL);
- if (!camel_data_wrapper_construct_from_stream_sync (CAMEL_DATA_WRAPPER (msg2), cache_stream, cancellable, NULL)) {
- g_object_unref (msg2);
- } else {
- g_object_unref (msg);
- msg = msg2;
- }
- }
- g_object_unref (cache_stream);
- }
-
- camel_folder_summary_unlock (folder->summary, CAMEL_FOLDER_SUMMARY_SUMMARY_LOCK);
+ add_message_to_cache (mapi_folder, uid, &msg, cancellable);
camel_message_info_free (&mi->info);
diff --git a/src/camel/camel-mapi-store.c b/src/camel/camel-mapi-store.c
index 8057860..30c3435 100644
--- a/src/camel/camel-mapi-store.c
+++ b/src/camel/camel-mapi-store.c
@@ -926,50 +926,6 @@ mapi_store_get_folder_sync (CamelStore *store,
return folder;
}
-static void
-mapi_update_folder_info_cb (CamelSession *session,
- GCancellable *cancellable,
- gpointer store,
- GError **error)
-{
- CamelMapiStore *mapi_store = CAMEL_MAPI_STORE (store);
- CamelServiceConnectionStatus status;
- CamelService *service;
- gchar *name;
-
- service = CAMEL_SERVICE (store);
-
- camel_service_lock (service, CAMEL_SERVICE_REC_CONNECT_LOCK);
-
- name = camel_service_get_name (service, TRUE);
- camel_operation_push_message (cancellable, _("Scanning folders in '%s'"), name);
-
- status = camel_service_get_connection_status (service);
- if (camel_offline_store_get_online (CAMEL_OFFLINE_STORE (store))) {
- if (status == CAMEL_SERVICE_DISCONNECTED) {
- camel_operation_push_message (cancellable, _("Connecting to '%s'"), name);
- camel_service_connect_sync (service, NULL);
- camel_operation_pop_message (cancellable);
- }
-
- /* update folders from the server only when asking for the top most or the 'top' is not known;
- otherwise believe the local cache, because folders sync is pretty slow operation to be done
- one every single question on the folder info */
- status = camel_service_get_connection_status (service);
- if (check_for_connection (service, NULL) || status == CAMEL_SERVICE_CONNECTING) {
- if (mapi_folders_sync (mapi_store, CAMEL_STORE_FOLDER_INFO_RECURSIVE, cancellable, error)) {
- camel_store_summary_touch (mapi_store->summary);
- camel_store_summary_save (mapi_store->summary);
- }
- }
- }
-
- g_free (name);
- camel_operation_pop_message (cancellable);
-
- camel_service_unlock (service, CAMEL_SERVICE_REC_CONNECT_LOCK);
-}
-
static CamelFolderInfo*
mapi_store_get_folder_info_sync (CamelStore *store,
const gchar *top,
@@ -995,24 +951,31 @@ mapi_store_get_folder_info_sync (CamelStore *store,
if ((flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIPTION_LIST) != 0 ||
(!(flags & CAMEL_STORE_FOLDER_INFO_SUBSCRIBED)) ||
(top && *top && !camel_mapi_store_folder_id_lookup (mapi_store, top)) ||
- camel_store_summary_count (mapi_store->summary) <= 1) {
- if (status == CAMEL_SERVICE_DISCONNECTED)
+ camel_store_summary_count (mapi_store->summary) <= 1 ||
+ !mapi_store->priv->folders_synced) {
+ if (status == CAMEL_SERVICE_DISCONNECTED) {
+ gchar *name = camel_service_get_name (service, TRUE);
+
+ camel_operation_push_message (cancellable, _("Connecting to '%s'"), name);
camel_service_connect_sync (service, NULL);
+ camel_operation_pop_message (cancellable);
+
+ g_free (name);
+ }
if (check_for_connection (service, NULL) || status == CAMEL_SERVICE_CONNECTING) {
+ gboolean first_check = !mapi_store->priv->folders_synced;
+
if (!mapi_folders_sync (mapi_store, flags, cancellable, error)) {
camel_service_unlock (service, CAMEL_SERVICE_REC_CONNECT_LOCK);
return NULL;
}
+
+ if (first_check) {
+ camel_store_summary_touch (mapi_store->summary);
+ camel_store_summary_save (mapi_store->summary);
+ }
}
- } else if (!mapi_store->priv->folders_synced) {
- mapi_store->priv->folders_synced = TRUE;
-
- camel_session_submit_job (
- camel_service_get_session (CAMEL_SERVICE (store)),
- mapi_update_folder_info_cb,
- g_object_ref (store),
- g_object_unref);
}
}
diff --git a/src/libexchangemapi/e-mapi-cal-recur-utils.c b/src/libexchangemapi/e-mapi-cal-recur-utils.c
index f424b5d..d9580fc 100644
--- a/src/libexchangemapi/e-mapi-cal-recur-utils.c
+++ b/src/libexchangemapi/e-mapi-cal-recur-utils.c
@@ -699,7 +699,7 @@ check_calendar_type (guint16 type)
}
gboolean
-e_mapi_cal_util_bin_to_rrule (GByteArray *ba, ECalComponent *comp, GSList **extra_detached, icaltimezone *recur_zone)
+e_mapi_cal_util_bin_to_rrule (const guint8 *lpb, guint32 cb, ECalComponent *comp, GSList **extra_detached, icaltimezone *recur_zone)
{
struct icalrecurrencetype rt;
struct ema_AppointmentRecurrencePattern arp;
@@ -708,11 +708,15 @@ e_mapi_cal_util_bin_to_rrule (GByteArray *ba, ECalComponent *comp, GSList **extr
gint i;
ptrdiff_t off = 0;
GSList *exdate_list = NULL;
+ GByteArray fake_ba;
+
+ fake_ba.data = (guint8 *) lpb;
+ fake_ba.len = cb;
icalrecurrencetype_clear (&rt);
memset(&arp, 0, sizeof (struct ema_AppointmentRecurrencePattern));
- if (! gba_to_arp (ba, &off, &arp))
+ if (! gba_to_arp (&fake_ba, &off, &arp))
goto cleanup;
rp = &arp.RecurrencePattern;
diff --git a/src/libexchangemapi/e-mapi-cal-recur-utils.h b/src/libexchangemapi/e-mapi-cal-recur-utils.h
index 29c149d..0c2dfff 100644
--- a/src/libexchangemapi/e-mapi-cal-recur-utils.h
+++ b/src/libexchangemapi/e-mapi-cal-recur-utils.h
@@ -30,7 +30,7 @@
G_BEGIN_DECLS
-gboolean e_mapi_cal_util_bin_to_rrule (GByteArray *ba, ECalComponent *comp, GSList **extra_detached, icaltimezone *recur_zone);
+gboolean e_mapi_cal_util_bin_to_rrule (const guint8 *lpb, guint32 cb, ECalComponent *comp, GSList **extra_detached, icaltimezone *recur_zone);
GByteArray * e_mapi_cal_util_rrule_to_bin (ECalComponent *comp, GSList *modified_comps);
diff --git a/src/libexchangemapi/e-mapi-cal-tz-utils.c b/src/libexchangemapi/e-mapi-cal-tz-utils.c
index f992c72..7cf3513 100644
--- a/src/libexchangemapi/e-mapi-cal-tz-utils.c
+++ b/src/libexchangemapi/e-mapi-cal-tz-utils.c
@@ -344,12 +344,11 @@ e_mapi_cal_util_mapi_tz_to_bin (const gchar *mapi_tzid, struct Binary_r *sb)
}
gchar *
-e_mapi_cal_util_bin_to_mapi_tz (GByteArray *ba)
+e_mapi_cal_util_bin_to_mapi_tz (const guint8 *lpb, guint32 cb)
{
guint8 flag8;
guint16 flag16, cbHeader = 0;
- guint8 *ptr = ba->data;
-// guint len = ba->len;
+ const guint8 *ptr = lpb;
gchar *buf = NULL;
d(g_message ("New timezone stream.. Length: %d bytes.. Info follows:", ba->len));
diff --git a/src/libexchangemapi/e-mapi-cal-tz-utils.h b/src/libexchangemapi/e-mapi-cal-tz-utils.h
index 5a789b4..0e097bf 100644
--- a/src/libexchangemapi/e-mapi-cal-tz-utils.h
+++ b/src/libexchangemapi/e-mapi-cal-tz-utils.h
@@ -37,7 +37,7 @@ void e_mapi_cal_tz_util_destroy (void);
void e_mapi_cal_tz_util_dump (void);
void e_mapi_cal_util_mapi_tz_to_bin (const gchar *mapi_tzid, struct Binary_r *sb);
int e_mapi_cal_util_mapi_tz_pidlidtimezone (icaltimezone *ictz);
-gchar * e_mapi_cal_util_bin_to_mapi_tz (GByteArray *ba);
+gchar * e_mapi_cal_util_bin_to_mapi_tz (const guint8 *lpb, guint32 cb);
G_END_DECLS
diff --git a/src/libexchangemapi/e-mapi-cal-utils.c b/src/libexchangemapi/e-mapi-cal-utils.c
index 9223bce..79f884b 100644
--- a/src/libexchangemapi/e-mapi-cal-utils.c
+++ b/src/libexchangemapi/e-mapi-cal-utils.c
@@ -30,6 +30,8 @@
#include <fcntl.h>
#include <libecal/e-cal-util.h>
#include <libedataserver/e-data-server-util.h>
+
+#include "e-mapi-mail-utils.h"
#include "e-mapi-cal-utils.h"
#ifndef O_BINARY
@@ -650,28 +652,27 @@ e_mapi_cal_util_generate_globalobjectid (gboolean is_clean, const gchar *uid, co
/* returns complete globalid as base64 encoded string */
static gchar *
-globalid_to_string (GByteArray *ba)
+globalid_to_string (const guint8 *lpb, guint32 cb)
{
- guint8 *ptr;
- guint len;
+ const guint8 *ptr;
guint32 i, j;
- g_return_val_if_fail (ba != NULL, NULL);
+ g_return_val_if_fail (lpb != NULL, NULL);
/* MSDN docs: the globalID must have an even number of bytes */
- if ((ba->len) % 2 != 0)
+ if ((cb) % 2 != 0)
return NULL;
- ptr = ba->data;
- len = ba->len;
+ ptr = lpb;
/* starting seq - len = 16 bytes */
- for (i = 0, j = 0;(i < len) && (j < sizeof (GID_START_SEQ)); ++i, ++ptr, ++j)
+ for (i = 0, j = 0; i < cb && j < sizeof (GID_START_SEQ); i++, ptr++, j++) {
if (*ptr != GID_START_SEQ[j])
return NULL;
+ }
/* take complete global id */
- return g_base64_encode (ba->data, ba->len);
+ return g_base64_encode (lpb, cb);
}
ECalComponent *
@@ -765,8 +766,8 @@ e_mapi_cal_util_mapi_props_to_comp (EMapiConnection *conn, mapi_id_t fid, icalco
/* GlobalObjectId */
stream = e_mapi_util_find_stream_namedid (streams, conn, fid, PidLidGlobalObjectId);
- if (stream) {
- gchar *value = globalid_to_string (stream->value);
+ if (stream && stream->value) {
+ gchar *value = globalid_to_string (stream->value->data, stream->value->len);
prop = icalproperty_new_x (value);
icalproperty_set_x_name (prop, "X-EVOLUTION-MAPI-GLOBALID");
icalcomponent_add_property (ical_comp, prop);
@@ -811,8 +812,8 @@ e_mapi_cal_util_mapi_props_to_comp (EMapiConnection *conn, mapi_id_t fid, icalco
all_day = b && *b;
stream = e_mapi_util_find_stream_namedid (streams, conn, fid, PidLidAppointmentTimeZoneDefinitionStartDisplay);
- if (stream) {
- gchar *buf = e_mapi_cal_util_bin_to_mapi_tz (stream->value);
+ if (stream && stream->value) {
+ gchar *buf = e_mapi_cal_util_bin_to_mapi_tz (stream->value->data, stream->value->len);
dtstart_tz_location = e_mapi_cal_tz_util_get_ical_equivalent (buf);
g_free (buf);
}
@@ -828,8 +829,8 @@ e_mapi_cal_util_mapi_props_to_comp (EMapiConnection *conn, mapi_id_t fid, icalco
}
stream = e_mapi_util_find_stream_namedid (streams, conn, fid, PidLidAppointmentTimeZoneDefinitionEndDisplay);
- if (stream) {
- gchar *buf = e_mapi_cal_util_bin_to_mapi_tz (stream->value);
+ if (stream && stream->value) {
+ gchar *buf = e_mapi_cal_util_bin_to_mapi_tz (stream->value->data, stream->value->len);
dtend_tz_location = e_mapi_cal_tz_util_get_ical_equivalent (buf);
g_free (buf);
}
@@ -966,7 +967,7 @@ e_mapi_cal_util_mapi_props_to_comp (EMapiConnection *conn, mapi_id_t fid, icalco
b = e_mapi_util_find_array_namedid (properties, conn, fid, PidLidRecurring);
if (b && *b) {
stream = e_mapi_util_find_stream_namedid (streams, conn, fid, PidLidAppointmentRecur);
- if (stream) {
+ if (stream && stream->value) {
icaltimezone *recur_zone;
const gchar *recur_tz_location;
@@ -975,7 +976,7 @@ e_mapi_cal_util_mapi_props_to_comp (EMapiConnection *conn, mapi_id_t fid, icalco
recur_tz_location = e_mapi_cal_tz_util_get_ical_equivalent (recur_tz_location);
recur_zone = recur_tz_location ? icaltimezone_get_builtin_timezone (recur_tz_location) : (icaltimezone *) default_zone;
- e_mapi_cal_util_bin_to_rrule (stream->value, comp, detached_components, recur_zone);
+ e_mapi_cal_util_bin_to_rrule (stream->value->data, stream->value->len, comp, detached_components, recur_zone);
}
}
@@ -2422,3 +2423,598 @@ e_mapi_cal_utils_get_icomp_x_prop (icalcomponent *comp, const gchar *key)
return NULL;
}
+static void
+populate_ical_attendees (EMapiConnection *conn,
+ EMapiRecipient *recipients,
+ icalcomponent *icalcomp,
+ gboolean rsvp)
+{
+ const uint32_t name_proptags[] = {
+ PROP_TAG (PT_UNICODE, 0x6001), /* PidTagNickname for Recipients table */
+ PidTagNickname,
+ PidTagRecipientDisplayName,
+ PidTagDisplayName,
+ PidTag7BitDisplayName
+ };
+
+ const uint32_t email_proptags[] = {
+ PidTagPrimarySmtpAddress,
+ PidTagSmtpAddress
+ };
+
+ EMapiRecipient *recipient;
+
+ g_return_if_fail (conn != NULL);
+ g_return_if_fail (icalcomp != NULL);
+
+ for (recipient = recipients; recipient; recipient = recipient->next) {
+ gchar *name = NULL, *email = NULL, *icalemail;
+ icalproperty *prop;
+ icalparameter *param;
+ const uint32_t *ui32;
+ const uint32_t *flags;
+
+ e_mapi_mail_utils_decode_email_address (conn, &recipient->properties,
+ name_proptags, G_N_ELEMENTS (name_proptags),
+ email_proptags, G_N_ELEMENTS (email_proptags),
+ PidTagAddressType, PidTagEmailAddress,
+ &name, &email);
+
+ if (!email) {
+ g_free (name);
+ g_debug ("%s: Skipping event recipient without email", G_STRFUNC);
+ continue;
+ }
+
+ icalemail = g_strdup_printf ("MAILTO:%s", email);
+
+ flags = e_mapi_util_find_array_propval (&recipient->properties, PidTagRecipientFlags);
+
+ if (flags && (*flags & RECIP_ORGANIZER)) {
+ prop = icalproperty_new_organizer (icalemail);
+
+ /* CN */
+ if (name && *name) {
+ param = icalparameter_new_cn (name);
+ icalproperty_add_parameter (prop, param);
+ }
+ } else {
+ prop = icalproperty_new_attendee (icalemail);
+
+ /* CN */
+ if (name && *name) {
+ param = icalparameter_new_cn (name);
+ icalproperty_add_parameter (prop, param);
+ }
+
+ /* RSVP */
+ param = icalparameter_new_rsvp (rsvp ? ICAL_RSVP_TRUE : ICAL_RSVP_FALSE);
+ icalproperty_add_parameter (prop, param);
+
+ /* PARTSTAT */
+ ui32 = e_mapi_util_find_array_propval (&recipient->properties, PidTagRecipientTrackStatus);
+ param = icalparameter_new_partstat (get_partstat_from_trackstatus (ui32 ? *ui32 : olResponseNone));
+ icalproperty_add_parameter (prop, param);
+
+ /* ROLE */
+ ui32 = e_mapi_util_find_array_propval (&recipient->properties, PidTagRecipientType);
+ param = icalparameter_new_role (get_role_from_type (ui32 ? *ui32 : olTo));
+ icalproperty_add_parameter (prop, param);
+
+ /* CALENDAR USER TYPE */
+ param = NULL;
+ if (ui32 && *ui32 == 0x03)
+ param = icalparameter_new_cutype (ICAL_CUTYPE_RESOURCE);
+ if (!param)
+ param = icalparameter_new_cutype (ICAL_CUTYPE_INDIVIDUAL);
+
+ icalproperty_add_parameter (prop, param);
+ }
+
+ icalcomponent_add_property (icalcomp, prop);
+
+ g_free (icalemail);
+ g_free (email);
+ g_free (name);
+ }
+}
+
+static void
+set_attachments_to_comp (EMapiConnection *conn, EMapiAttachment *attachments, ECalComponent *comp, const gchar *local_store_path)
+{
+ GSList *comp_attach_list = NULL;
+ EMapiAttachment *attach;
+ const gchar *uid;
+
+ g_return_if_fail (comp != NULL);
+ g_return_if_fail (local_store_path != NULL);
+
+ if (!attachments)
+ return;
+
+ e_cal_component_get_uid (comp, &uid);
+
+ for (attach = attachments; attach; attach = attach->next) {
+ const struct SBinary_short *data_bin;
+ const gchar *filename;
+ const uint32_t *ui32;
+ gchar *path, *attach_uri;
+ GError *error = NULL;
+
+ data_bin = e_mapi_util_find_array_propval (&attach->properties, PidTagAttachDataBinary);
+ if (!data_bin) {
+ g_debug ("%s: Skipping calendar attachment without data", G_STRFUNC);
+ continue;
+ }
+
+ filename = e_mapi_util_find_array_propval (&attach->properties, PidTagAttachLongFilename);
+ if (!filename || !*filename)
+ filename = e_mapi_util_find_array_propval (&attach->properties, PidTagAttachFilename);
+
+ ui32 = e_mapi_util_find_array_propval (&attach->properties, PidTagAttachNumber);
+ path = e_filename_mkdir_encoded (local_store_path, uid, filename, ui32 ? *ui32 : 0);
+
+ attach_uri = g_filename_to_uri (path, NULL, &error);
+ if (!attach_uri) {
+ g_debug ("%s: Could not get attach_uri from '%s': %s", G_STRFUNC, path, error ? error->message : "Unknown error");
+ g_clear_error (&error);
+ g_free (path);
+ continue;
+ }
+
+ if (!g_file_set_contents (path, (const gchar *) data_bin->lpb, data_bin->cb, &error)) {
+ g_debug ("%s: Failed to write attachment content to '%s': %s", G_STRFUNC, path, error ? error->message : "Unknown error");
+ g_free (attach_uri);
+ g_clear_error (&error);
+ } else {
+ comp_attach_list = g_slist_append (comp_attach_list, attach_uri);
+ }
+
+ g_free (path);
+ }
+
+ e_cal_component_set_attachment_list (comp, comp_attach_list);
+
+ g_slist_free_full (comp_attach_list, g_free);
+}
+
+ECalComponent *
+e_mapi_cal_util_object_to_comp (EMapiConnection *conn,
+ EMapiObject *object,
+ icalcomponent_kind kind,
+ gboolean is_reply,
+ const gchar *local_store_uri,
+ const gchar *use_uid,
+ GSList **detached_components)
+{
+ ECalComponent *comp = NULL;
+ struct timeval t;
+ const gchar *str;
+ const struct mapi_SPLSTRArrayW *categories_array;
+ const struct SBinary_short *bin;
+ const uint32_t *ui32;
+ const bool *b;
+ icalcomponent *ical_comp;
+ icalproperty *prop = NULL;
+ icalparameter *param = NULL;
+ icaltimezone *utc_zone;
+
+ g_return_val_if_fail (conn != NULL, NULL);
+ g_return_val_if_fail (object != NULL, NULL);
+ g_return_val_if_fail (use_uid != NULL, NULL);
+
+ switch (kind) {
+ case ICAL_VEVENT_COMPONENT:
+ case ICAL_VTODO_COMPONENT:
+ case ICAL_VJOURNAL_COMPONENT:
+ comp = e_cal_component_new ();
+ ical_comp = icalcomponent_new (kind);
+ if (!e_cal_component_set_icalcomponent (comp, ical_comp)) {
+ icalcomponent_free (ical_comp);
+ g_object_unref (comp);
+ return NULL;
+ }
+ e_cal_component_set_uid (comp, use_uid);
+ break;
+ default:
+ return NULL;
+ }
+
+ utc_zone = icaltimezone_get_utc_timezone ();
+ if (!local_store_uri)
+ local_store_uri = g_get_tmp_dir ();
+
+ str = e_mapi_util_find_array_propval (&object->properties, PidTagSubject);
+ str = str ? str : e_mapi_util_find_array_propval (&object->properties, PidTagNormalizedSubject);
+ str = str ? str : e_mapi_util_find_array_propval (&object->properties, PidTagConversationTopic);
+ str = str ? str : "";
+ icalcomponent_set_summary (ical_comp, str);
+
+ ui32 = e_mapi_util_find_array_propval (&object->properties, PidTagInternetCodepage);
+ str = e_mapi_util_find_array_propval (&object->properties, PidTagBody);
+ if (str) {
+ gchar *utf8_str = NULL;
+ uint32_t proptag = e_mapi_util_find_array_proptag (&object->properties, PidTagBody);
+
+ if (e_mapi_utils_ensure_utf8_string (proptag, ui32, (const guint8 *) str, strlen (str), &utf8_str))
+ str = utf8_str;
+
+ icalcomponent_set_description (ical_comp, str);
+
+ g_free (utf8_str);
+ } else {
+ const struct SBinary_short *html_bin = e_mapi_util_find_array_propval (&object->properties, PidTagHtml);
+
+ if (html_bin) {
+ gchar *utf8_str = NULL;
+
+ if (e_mapi_utils_ensure_utf8_string (PidTagHtml, ui32, html_bin->lpb, html_bin->cb, &utf8_str))
+ icalcomponent_set_description (ical_comp, utf8_str);
+
+ g_free (utf8_str);
+ }
+ }
+
+ /* set dtstamp - in UTC */
+ if (e_mapi_util_find_array_datetime_propval (&t, &object->properties, PidTagCreationTime) == MAPI_E_SUCCESS) {
+ icalcomponent_set_dtstamp (ical_comp, icaltime_from_timet_with_zone (t.tv_sec, 0, utc_zone));
+
+ prop = icalproperty_new_created (icaltime_from_timet_with_zone (t.tv_sec, 0, utc_zone));
+ icalcomponent_add_property (ical_comp, prop);
+ } else {
+ /* created - in UTC */
+ prop = icalproperty_new_created (icaltime_current_time_with_zone (utc_zone));
+ icalcomponent_add_property (ical_comp, prop);
+ }
+
+ /* last modified - in UTC */
+ if (get_mapi_SPropValue_array_date_timeval (&t, &object->properties, PidTagLastModificationTime) == MAPI_E_SUCCESS) {
+ prop = icalproperty_new_lastmodified (icaltime_from_timet_with_zone (t.tv_sec, 0, utc_zone));
+ icalcomponent_add_property (ical_comp, prop);
+ }
+
+ categories_array = e_mapi_util_find_array_propval (&object->properties, PidNameKeywords);
+ if (categories_array) {
+ GSList *categories = NULL;
+ gint ii;
+
+ for (ii = 0; ii < categories_array->cValues; ii++) {
+ const gchar *category = categories_array->strings[ii].lppszW;
+
+ if (!category || !*category)
+ continue;
+
+ categories = g_slist_append (categories, (gpointer) category);
+ }
+
+ e_cal_component_set_categories_list (comp, categories);
+
+ g_slist_free (categories);
+ }
+
+ if (icalcomponent_isa (ical_comp) == ICAL_VEVENT_COMPONENT) {
+ const gchar *location = NULL;
+ const gchar *dtstart_tz_location = NULL, *dtend_tz_location = NULL;
+ gboolean all_day;
+
+ /* GlobalObjectId */
+ bin = e_mapi_util_find_array_propval (&object->properties, PidLidGlobalObjectId);
+ if (bin) {
+ gchar *value = globalid_to_string (bin->lpb, bin->cb);
+ prop = icalproperty_new_x (value);
+ icalproperty_set_x_name (prop, "X-EVOLUTION-MAPI-GLOBALID");
+ icalcomponent_add_property (ical_comp, prop);
+ if (value && *value) {
+ e_cal_component_set_uid (comp, value);
+
+ if (!g_str_equal (value, use_uid)) {
+ prop = icalproperty_new_x (use_uid);
+ icalproperty_set_x_name (prop, "X-EVOLUTION-MAPI-MID");
+ icalcomponent_add_property (ical_comp, prop);
+ }
+ }
+
+ g_free (value);
+ }
+
+ ui32 = e_mapi_util_find_array_propval (&object->properties, PidTagOwnerAppointmentId);
+ if (ui32) {
+ gchar *value = e_mapi_util_mapi_id_to_string ((mapi_id_t) (*ui32));
+
+ prop = icalproperty_new_x (value);
+ icalproperty_set_x_name (prop, "X-EVOLUTION-MAPI-OWNER-APPT-ID");
+ icalcomponent_add_property (ical_comp, prop);
+ g_free (value);
+ }
+
+ /* AppointmentSequence */
+ ui32 = e_mapi_util_find_array_propval (&object->properties, PidLidAppointmentSequence);
+ if (ui32) {
+ gchar *value = g_strdup_printf ("%d", *ui32);
+ prop = icalproperty_new_x (value);
+ icalproperty_set_x_name (prop, "X-EVOLUTION-MAPI-APPTSEQ");
+ icalcomponent_add_property (ical_comp, prop);
+ g_free (value);
+ }
+
+ location = e_mapi_util_find_array_propval (&object->properties, PidLidLocation);
+ if (location && *location)
+ icalcomponent_set_location (ical_comp, location);
+
+ b = e_mapi_util_find_array_propval (&object->properties, PidLidAppointmentSubType);;
+ all_day = b && *b;
+
+ bin = e_mapi_util_find_array_propval (&object->properties, PidLidAppointmentTimeZoneDefinitionStartDisplay);
+ if (bin) {
+ gchar *buf = e_mapi_cal_util_bin_to_mapi_tz (bin->lpb, bin->cb);
+ dtstart_tz_location = e_mapi_cal_tz_util_get_ical_equivalent (buf);
+ g_free (buf);
+ }
+
+ if (e_mapi_util_find_array_datetime_propval (&t, &object->properties, PidLidAppointmentStartWhole) == MAPI_E_SUCCESS) {
+ icaltimezone *zone = dtstart_tz_location ? icaltimezone_get_builtin_timezone (dtstart_tz_location) : utc_zone;
+ prop = icalproperty_new_dtstart (icaltime_from_timet_with_zone (t.tv_sec, all_day, zone));
+ if (!all_day && zone && icaltimezone_get_tzid (zone)) {
+ icalproperty_add_parameter (prop, icalparameter_new_tzid (icaltimezone_get_tzid (zone)));
+ }
+
+ icalcomponent_add_property (ical_comp, prop);
+ }
+
+ bin = e_mapi_util_find_array_propval (&object->properties, PidLidAppointmentTimeZoneDefinitionEndDisplay);
+ if (bin) {
+ gchar *buf = e_mapi_cal_util_bin_to_mapi_tz (bin->lpb, bin->cb);
+ dtend_tz_location = e_mapi_cal_tz_util_get_ical_equivalent (buf);
+ g_free (buf);
+ }
+
+ if (e_mapi_util_find_array_datetime_propval (&t, &object->properties, PidLidAppointmentEndWhole) == MAPI_E_SUCCESS) {
+ icaltimezone *zone;
+
+ if (!dtend_tz_location)
+ dtend_tz_location = dtstart_tz_location;
+
+ zone = dtend_tz_location ? icaltimezone_get_builtin_timezone (dtend_tz_location) : utc_zone;
+ prop = icalproperty_new_dtend (icaltime_from_timet_with_zone (t.tv_sec, all_day, zone));
+ if (!all_day && zone && icaltimezone_get_tzid (zone)) {
+ icalproperty_add_parameter (prop, icalparameter_new_tzid (icaltimezone_get_tzid (zone)));
+ }
+
+ icalcomponent_add_property (ical_comp, prop);
+ }
+
+ ui32 = e_mapi_util_find_array_propval (&object->properties, PidLidBusyStatus);
+ if (ui32) {
+ prop = icalproperty_new_transp (get_transp_from_prop (*ui32));
+ icalcomponent_add_property (ical_comp, prop);
+ }
+
+ if (object->recipients) {
+ gchar *name = NULL, *email = NULL;
+ gchar *val;
+
+ b = e_mapi_util_find_array_propval (&object->properties, PidTagResponseRequested);
+ populate_ical_attendees (conn, object->recipients, ical_comp, (b && *b));
+ if (is_reply) {
+ if (icalcomponent_get_first_property (ical_comp, ICAL_ORGANIZER_PROPERTY) == NULL) {
+ name = NULL;
+ email = NULL;
+
+ e_mapi_mail_utils_decode_email_address1 (conn, &object->properties,
+ PidTagReceivedRepresentingName,
+ PidTagReceivedRepresentingEmailAddress,
+ PidTagReceivedRepresentingAddressType,
+ &name, &email);
+
+ if (email) {
+ val = g_strdup_printf ("MAILTO:%s", email);
+ prop = icalproperty_new_organizer (val);
+ g_free (val);
+
+ if (name && g_strcmp0 (name, email) != 0) {
+ /* CN */
+ param = icalparameter_new_cn (name);
+ icalproperty_add_parameter (prop, param);
+ }
+
+ icalcomponent_add_property (ical_comp, prop);
+ }
+
+ g_free (name);
+ g_free (email);
+ }
+
+ if (icalcomponent_get_first_property (ical_comp, ICAL_ATTENDEE_PROPERTY) == NULL) {
+ name = NULL;
+ email = NULL;
+
+ e_mapi_mail_utils_decode_email_address1 (conn, &object->properties,
+ PidTagSentRepresentingName,
+ PidTagSentRepresentingEmailAddress,
+ PidTagSentRepresentingAddressType,
+ &name, &email);
+
+ if (email) {
+ val = g_strdup_printf ("MAILTO:%s", email);
+ prop = icalproperty_new_attendee (val);
+ g_free (val);
+
+ if (name && g_strcmp0 (name, email) != 0) {
+ /* CN */
+ param = icalparameter_new_cn (name);
+ icalproperty_add_parameter (prop, param);
+ }
+
+ ui32 = e_mapi_util_find_array_propval (&object->properties, PidLidResponseStatus);
+ param = icalparameter_new_partstat (get_partstat_from_trackstatus (ui32 ? *ui32 : olResponseNone));
+ icalproperty_add_parameter (prop, param);
+
+ icalcomponent_add_property (ical_comp, prop);
+ }
+
+ g_free (name);
+ g_free (email);
+ }
+ } else if (icalcomponent_get_first_property (ical_comp, ICAL_ORGANIZER_PROPERTY) == NULL) {
+ gchar *sent_name = NULL, *sent_email = NULL;
+
+ name = NULL;
+ email = NULL;
+
+ e_mapi_mail_utils_decode_email_address1 (conn, &object->properties,
+ PidTagSenderName,
+ PidTagSenderEmailAddress,
+ PidTagSenderAddressType,
+ &name, &email);
+
+ e_mapi_mail_utils_decode_email_address1 (conn, &object->properties,
+ PidTagSentRepresentingName,
+ PidTagSentRepresentingEmailAddress,
+ PidTagSentRepresentingAddressType,
+ &sent_name, &sent_email);
+
+ if (sent_email) {
+ val = g_strdup_printf ("MAILTO:%s", sent_email);
+ prop = icalproperty_new_organizer (val);
+ g_free (val);
+
+ if (sent_name && g_strcmp0 (sent_name, sent_email) != 0) {
+ /* CN */
+ param = icalparameter_new_cn (sent_name);
+ icalproperty_add_parameter (prop, param);
+ }
+
+ /* SENTBY */
+ if (email && g_utf8_collate (sent_email, email)) {
+ val = g_strdup_printf ("MAILTO:%s", email);
+ param = icalparameter_new_sentby (val);
+ icalproperty_add_parameter (prop, param);
+ g_free (val);
+ }
+
+ icalcomponent_add_property (ical_comp, prop);
+ }
+
+
+ g_free (name);
+ g_free (email);
+ g_free (sent_name);
+ g_free (sent_email);
+ }
+ }
+
+ b = e_mapi_util_find_array_propval (&object->properties, PidLidRecurring);
+ if (b && *b) {
+ bin = e_mapi_util_find_array_propval (&object->properties, PidLidAppointmentRecur);
+ if (bin) {
+ icaltimezone *recur_zone;
+ const gchar *recur_tz_location;
+
+ recur_tz_location = e_mapi_util_find_array_propval (&object->properties, PidLidTimeZoneDescription);
+ if (recur_tz_location)
+ recur_tz_location = e_mapi_cal_tz_util_get_ical_equivalent (recur_tz_location);
+ recur_zone = recur_tz_location ? icaltimezone_get_builtin_timezone (recur_tz_location) : utc_zone;
+
+ e_mapi_cal_util_bin_to_rrule (bin->lpb, bin->cb, comp, detached_components, recur_zone);
+ }
+ }
+
+ b = e_mapi_util_find_array_propval (&object->properties, PidLidReminderSet);
+ if (b && *b) {
+ struct timeval start, displaytime;
+
+ if ((e_mapi_util_find_array_datetime_propval (&start, &object->properties, PidLidReminderTime) == MAPI_E_SUCCESS)
+ && (e_mapi_util_find_array_datetime_propval (&displaytime, &object->properties, PidLidReminderSignalTime) == MAPI_E_SUCCESS)) {
+ ECalComponentAlarm *e_alarm = e_cal_component_alarm_new ();
+ ECalComponentAlarmTrigger trigger;
+
+ trigger.type = E_CAL_COMPONENT_ALARM_TRIGGER_RELATIVE_START;
+ trigger.u.rel_duration = icaltime_subtract (icaltime_from_timet_with_zone (displaytime.tv_sec, 0, 0),
+ icaltime_from_timet_with_zone (start.tv_sec, 0, 0));
+
+ e_cal_component_alarm_set_action (e_alarm, E_CAL_COMPONENT_ALARM_DISPLAY);
+ e_cal_component_alarm_set_trigger (e_alarm, trigger);
+
+ e_cal_component_add_alarm (comp, e_alarm);
+ }
+ } else
+ e_cal_component_remove_all_alarms (comp);
+
+ } else if (icalcomponent_isa (ical_comp) == ICAL_VTODO_COMPONENT) {
+ const double *complete = NULL;
+ const uint64_t *status = NULL;
+
+ /* NOTE: Exchange tasks are DATE values, not DATE-TIME values, but maybe someday, we could expect Exchange to support it;) */
+ if (e_mapi_util_find_array_datetime_propval (&t, &object->properties, PidLidTaskStartDate) == MAPI_E_SUCCESS)
+ icalcomponent_set_dtstart (ical_comp, icaltime_from_timet_with_zone (t.tv_sec, 1, utc_zone));
+ if (e_mapi_util_find_array_datetime_propval (&t, &object->properties, PidLidTaskDueDate) == MAPI_E_SUCCESS)
+ icalcomponent_set_due (ical_comp, icaltime_from_timet_with_zone (t.tv_sec, 1, utc_zone));
+
+ status = e_mapi_util_find_array_propval (&object->properties, PidLidTaskStatus);
+ if (status) {
+ icalcomponent_set_status (ical_comp, get_taskstatus_from_prop (*status));
+ if (*status == olTaskComplete
+ && e_mapi_util_find_array_datetime_propval (&t, &object->properties, PidLidTaskDateCompleted) == MAPI_E_SUCCESS) {
+ prop = icalproperty_new_completed (icaltime_from_timet_with_zone (t.tv_sec, 1, utc_zone));
+ icalcomponent_add_property (ical_comp, prop);
+ }
+ }
+
+ complete = e_mapi_util_find_array_propval (&object->properties, PidLidPercentComplete);
+ if (complete) {
+ prop = icalproperty_new_percentcomplete ((gint) ((*complete) * 100 + 1e-9));
+ icalcomponent_add_property (ical_comp, prop);
+ }
+
+ b = e_mapi_util_find_array_propval (&object->properties, PidLidTaskFRecurring);
+ if (b && *b) {
+ g_debug ("%s: Evolution does not support recurring tasks.", G_STRFUNC);
+ }
+
+ b = e_mapi_util_find_array_propval (&object->properties, PidLidReminderSet);
+ if (b && *b) {
+ struct timeval abs;
+
+ if (e_mapi_util_find_array_datetime_propval (&abs, &object->properties, PidLidReminderTime) == MAPI_E_SUCCESS) {
+ ECalComponentAlarm *e_alarm = e_cal_component_alarm_new ();
+ ECalComponentAlarmTrigger trigger;
+
+ trigger.type = E_CAL_COMPONENT_ALARM_TRIGGER_ABSOLUTE;
+ trigger.u.abs_time = icaltime_from_timet_with_zone (abs.tv_sec, 0, utc_zone);
+
+ e_cal_component_alarm_set_action (e_alarm, E_CAL_COMPONENT_ALARM_DISPLAY);
+ e_cal_component_alarm_set_trigger (e_alarm, trigger);
+
+ e_cal_component_add_alarm (comp, e_alarm);
+ }
+ } else
+ e_cal_component_remove_all_alarms (comp);
+
+ } else if (icalcomponent_isa (ical_comp) == ICAL_VJOURNAL_COMPONENT) {
+ if (e_mapi_util_find_array_datetime_propval (&t, &object->properties, PidTagLastModificationTime) == MAPI_E_SUCCESS)
+ icalcomponent_set_dtstart (ical_comp, icaltime_from_timet_with_zone (t.tv_sec, 1, utc_zone));
+ }
+
+ if (icalcomponent_isa (ical_comp) == ICAL_VEVENT_COMPONENT ||
+ icalcomponent_isa (ical_comp) == ICAL_VTODO_COMPONENT) {
+ /* priority */
+ ui32 = e_mapi_util_find_array_propval (&object->properties, PidTagPriority);
+ if (ui32) {
+ prop = icalproperty_new_priority (get_priority_from_prop (*ui32));
+ icalcomponent_add_property (ical_comp, prop);
+ }
+ }
+
+ /* classification */
+ ui32 = e_mapi_util_find_array_propval (&object->properties, PidTagSensitivity);
+ if (ui32) {
+ prop = icalproperty_new_class (get_class_from_prop (*ui32));
+ icalcomponent_add_property (ical_comp, prop);
+ }
+
+ set_attachments_to_comp (conn, object->attachments, comp, local_store_uri);
+
+ e_cal_component_rescan (comp);
+
+ return comp;
+}
diff --git a/src/libexchangemapi/e-mapi-cal-utils.h b/src/libexchangemapi/e-mapi-cal-utils.h
index 2091c0a..b512bdb 100644
--- a/src/libexchangemapi/e-mapi-cal-utils.h
+++ b/src/libexchangemapi/e-mapi-cal-utils.h
@@ -128,7 +128,15 @@ gboolean e_mapi_cal_utils_get_free_busy_data (EMapiConnection *conn,
GCancellable *cancellable,
GError **mapi_error);
-gchar *e_mapi_cal_utils_get_icomp_x_prop (icalcomponent *comp, const gchar *key);
+ECalComponent * e_mapi_cal_util_object_to_comp (EMapiConnection *conn,
+ EMapiObject *object,
+ icalcomponent_kind kind,
+ gboolean is_reply,
+ const gchar *local_store_uri,
+ const gchar *use_uid,
+ GSList **detached_components);
+
+gchar * e_mapi_cal_utils_get_icomp_x_prop (icalcomponent *comp, const gchar *key);
G_END_DECLS
diff --git a/src/libexchangemapi/e-mapi-connection.c b/src/libexchangemapi/e-mapi-connection.c
index 5aefef7..e1f89a4 100644
--- a/src/libexchangemapi/e-mapi-connection.c
+++ b/src/libexchangemapi/e-mapi-connection.c
@@ -2195,6 +2195,269 @@ e_mapi_connection_list_objects (EMapiConnection *conn,
return ms == MAPI_E_SUCCESS;
}
+static gboolean
+has_embedded_message_with_html (EMapiObject *object)
+{
+ EMapiAttachment *attach;
+
+ if (!object)
+ return FALSE;
+
+ for (attach = object->attachments; attach; attach = attach->next) {
+ if (!attach->embedded_object)
+ continue;
+
+ if (e_mapi_util_find_array_propval (&attach->embedded_object->properties, PidTagHtml) &&
+ !e_mapi_util_find_array_propval (&attach->embedded_object->properties, PidTagBody))
+ return TRUE;
+
+ if (has_embedded_message_with_html (attach->embedded_object))
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static gboolean
+get_additional_properties_cb (EMapiConnection *conn,
+ TALLOC_CTX *mem_ctx,
+ /* const */ EMapiObject *object,
+ guint32 obj_index,
+ guint32 obj_total,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **perror)
+{
+ uint32_t ii;
+ EMapiObject *dest_object = user_data;
+
+ g_return_val_if_fail (object != NULL, FALSE);
+ g_return_val_if_fail (dest_object != NULL, FALSE);
+
+ for (ii = 0; ii < object->properties.cValues; ii++) {
+ uint32_t proptag = object->properties.lpProps[ii].ulPropTag;
+
+ if ((proptag & 0xFFFF) == PT_ERROR
+ || e_mapi_util_find_array_propval (&dest_object->properties, proptag))
+ continue;
+
+ dest_object->properties.cValues++;
+ dest_object->properties.lpProps = talloc_realloc (mem_ctx,
+ dest_object->properties.lpProps,
+ struct mapi_SPropValue,
+ dest_object->properties.cValues + 1);
+ dest_object->properties.lpProps[dest_object->properties.cValues - 1] = object->properties.lpProps[ii];
+
+ #define steal_ptr(x) (x) = talloc_steal (dest_object, (x))
+ switch (proptag & 0xFFFF) {
+ case PT_BOOLEAN:
+ case PT_I2:
+ case PT_LONG:
+ case PT_DOUBLE:
+ case PT_I8:
+ case PT_SYSTIME:
+ break;
+ case PT_STRING8:
+ steal_ptr (dest_object->properties.lpProps[dest_object->properties.cValues - 1].value.lpszA);
+ break;
+ case PT_UNICODE:
+ steal_ptr (dest_object->properties.lpProps[dest_object->properties.cValues - 1].value.lpszW);
+ break;
+ default:
+ g_debug ("%s: Do not know how to steal property type 0x%x, skipping it", G_STRFUNC, proptag & 0xFFFF);
+ dest_object->properties.cValues--;
+ break;
+ }
+ #undef steal_ptr
+
+ dest_object->properties.lpProps[dest_object->properties.cValues].ulPropTag = 0;
+ }
+
+ return TRUE;
+}
+
+static void
+traverse_attachments_for_body (EMapiConnection *conn,
+ TALLOC_CTX *mem_ctx,
+ EMapiObject *object,
+ mapi_object_t *obj_message,
+ GCancellable *cancellable,
+ GError **perror)
+{
+ EMapiAttachment *attach;
+
+ g_return_if_fail (conn != NULL);
+ g_return_if_fail (mem_ctx != NULL);
+ g_return_if_fail (obj_message != NULL);
+
+ if (!has_embedded_message_with_html (object))
+ return;
+
+ for (attach = object->attachments; attach && !g_cancellable_is_cancelled (cancellable); attach = attach->next) {
+ if (attach->embedded_object) {
+ const uint32_t *pattach_num;
+ mapi_object_t obj_attach;
+ mapi_object_t obj_embedded;
+ gboolean have_embedded = FALSE;
+
+ pattach_num = e_mapi_util_find_array_propval (&attach->properties, PidTagAttachNumber);
+ if (!pattach_num)
+ continue;
+
+ mapi_object_init (&obj_attach);
+ mapi_object_init (&obj_embedded);
+
+ if (e_mapi_util_find_array_propval (&attach->embedded_object->properties, PidTagHtml) &&
+ !e_mapi_util_find_array_propval (&attach->embedded_object->properties, PidTagBody)) {
+ struct SPropTagArray *tags;
+
+ if (OpenAttach (obj_message, *pattach_num, &obj_attach) != MAPI_E_SUCCESS)
+ continue;
+
+ if (OpenEmbeddedMessage (&obj_attach, &obj_embedded, MAPI_READONLY) != MAPI_E_SUCCESS) {
+ mapi_object_release (&obj_attach);
+ continue;
+ }
+
+ have_embedded = TRUE;
+
+ tags = set_SPropTagArray (mem_ctx, 1, PidTagBody);
+
+ e_mapi_fast_transfer_properties (conn, mem_ctx, &obj_embedded, tags, get_additional_properties_cb, attach->embedded_object, cancellable, perror);
+
+ talloc_free (tags);
+ }
+
+ if (has_embedded_message_with_html (attach->embedded_object)) {
+ if (!have_embedded) {
+ if (OpenAttach (obj_message, *pattach_num, &obj_attach) != MAPI_E_SUCCESS)
+ continue;
+
+ if (OpenEmbeddedMessage (&obj_attach, &obj_embedded, MAPI_READONLY) != MAPI_E_SUCCESS) {
+ mapi_object_release (&obj_attach);
+ continue;
+ }
+
+ have_embedded = TRUE;
+ }
+
+ traverse_attachments_for_body (conn, mem_ctx, attach->embedded_object, &obj_embedded, cancellable, perror);
+ }
+
+ mapi_object_release (&obj_embedded);
+ mapi_object_release (&obj_attach);
+ }
+ }
+}
+
+struct EnsureAdditionalPropertiesData
+{
+ TransferObjectCB cb;
+ gpointer cb_user_data;
+ mapi_object_t *obj_folder;
+};
+
+static gboolean
+ensure_additional_properties_cb (EMapiConnection *conn,
+ TALLOC_CTX *mem_ctx,
+ /* const */ EMapiObject *object,
+ guint32 obj_index,
+ guint32 obj_total,
+ gpointer user_data,
+ GCancellable *cancellable,
+ GError **perror)
+{
+ struct ap_data {
+ uint32_t orig_proptag, use_proptag;
+ } additional_properties[] = {
+ { PidTagBody, MAPI_E_RESERVED },
+ { PidNameContentClass, MAPI_E_RESERVED }
+ };
+ struct EnsureAdditionalPropertiesData *eap = user_data;
+ gboolean need_any = FALSE, need_attachments;
+ uint32_t ii;
+
+ g_return_val_if_fail (eap != NULL, FALSE);
+ g_return_val_if_fail (eap->cb != NULL, FALSE);
+ g_return_val_if_fail (object != NULL, FALSE);
+
+ for (ii = 0; ii < G_N_ELEMENTS (additional_properties); ii++) {
+ uint32_t prop = additional_properties[ii].orig_proptag;
+
+ if (!e_mapi_util_find_array_propval (&object->properties, prop)) {
+ if (((prop >> 16) & 0xFFFF) >= 0x8000) {
+ prop = e_mapi_connection_resolve_named_prop (conn, mapi_object_get_id (eap->obj_folder), prop, cancellable, NULL);
+ }
+ } else {
+ prop = MAPI_E_RESERVED;
+ }
+
+ additional_properties[ii].use_proptag = prop;
+ need_any = need_any || prop != MAPI_E_RESERVED;
+ }
+
+ need_attachments = has_embedded_message_with_html (object);
+
+ /* Fast-transfer transfers only Html or Body, never both */
+ if (need_any || need_attachments) {
+ const mapi_id_t *mid;
+
+ mid = e_mapi_util_find_array_propval (&object->properties, PidTagMid);
+ if (mid && *mid) {
+ mapi_object_t obj_message;
+
+ mapi_object_init (&obj_message);
+
+ if (OpenMessage (eap->obj_folder, mapi_object_get_id (eap->obj_folder), *mid, &obj_message, 0) == MAPI_E_SUCCESS) {
+ struct SPropTagArray *tags = NULL;
+
+ for (ii = 0; ii < G_N_ELEMENTS (additional_properties); ii++) {
+ uint32_t prop = additional_properties[ii].use_proptag;
+
+ if (prop == MAPI_E_RESERVED)
+ continue;
+
+ if (!tags)
+ tags = set_SPropTagArray (mem_ctx, 1, prop);
+ else
+ SPropTagArray_add (mem_ctx, tags, prop);
+ }
+
+ if (tags) {
+ uint32_t jj = object->properties.cValues;
+
+ e_mapi_fast_transfer_properties (conn, mem_ctx, &obj_message, tags, get_additional_properties_cb, object, cancellable, perror);
+
+ while (jj < object->properties.cValues) {
+ for (ii = 0; ii < G_N_ELEMENTS (additional_properties); ii++) {
+ uint32_t proptag = object->properties.lpProps[jj].ulPropTag;
+
+ if (additional_properties[ii].use_proptag == proptag ||
+ (((proptag & 0xFFFF) == PT_STRING8 || (proptag & 0xFFFF) == PT_UNICODE) &&
+ (proptag & ~0xFFFF) == (additional_properties[ii].use_proptag & ~0xFFFF))) {
+ /* string8 and unicode properties are interchangeable in the union, luckily */
+ object->properties.lpProps[jj].ulPropTag = additional_properties[ii].orig_proptag;
+ break;
+ }
+ }
+
+ jj++;
+ }
+
+ talloc_free (tags);
+ }
+
+ if (need_attachments)
+ traverse_attachments_for_body (conn, mem_ctx, object, &obj_message, cancellable, perror);
+ }
+
+ mapi_object_release (&obj_message);
+ }
+ }
+
+ return eap->cb (conn, mem_ctx, object, obj_index, obj_total, eap->cb_user_data, cancellable, perror);
+}
+
/* deals with named IDs transparently, thus it's OK to check with PidLid and PidName constants only */
gboolean
e_mapi_connection_transfer_objects (EMapiConnection *conn,
@@ -2209,6 +2472,7 @@ e_mapi_connection_transfer_objects (EMapiConnection *conn,
TALLOC_CTX *mem_ctx;
mapi_id_array_t ids;
const GSList *iter;
+ struct EnsureAdditionalPropertiesData eap;
CHECK_CORRECT_CONN_AND_GET_PRIV (conn, FALSE);
e_return_val_mapi_error_if_fail (priv->session != NULL, MAPI_E_INVALID_PARAMETER, FALSE);
@@ -2237,7 +2501,11 @@ e_mapi_connection_transfer_objects (EMapiConnection *conn,
goto cleanup;
}
- ms = e_mapi_fast_transfer_objects (conn, mem_ctx, obj_folder, &ids, cb, cb_user_data, cancellable, perror);
+ eap.cb = cb;
+ eap.cb_user_data = cb_user_data;
+ eap.obj_folder = obj_folder;
+
+ ms = e_mapi_fast_transfer_objects (conn, mem_ctx, obj_folder, &ids, ensure_additional_properties_cb, &eap, cancellable, perror);
mapi_id_array_release (&ids);
@@ -2248,6 +2516,25 @@ e_mapi_connection_transfer_objects (EMapiConnection *conn,
return ms == MAPI_E_SUCCESS;
}
+gboolean
+e_mapi_connection_transfer_object (EMapiConnection *conn,
+ mapi_object_t *obj_folder,
+ mapi_id_t message_id,
+ TransferObjectCB cb,
+ gpointer cb_user_data,
+ GCancellable *cancellable,
+ GError **perror)
+{
+ GSList *mids;
+ gboolean res;
+
+ mids = g_slist_append (NULL, &message_id);
+ res = e_mapi_connection_transfer_objects (conn, obj_folder, mids, cb, cb_user_data, cancellable, perror);
+ g_slist_free (mids);
+
+ return res;
+}
+
struct GetSummaryData {
guint32 obj_index;
guint32 obj_total;
diff --git a/src/libexchangemapi/e-mapi-connection.h b/src/libexchangemapi/e-mapi-connection.h
index 15afe6a..46ce521 100644
--- a/src/libexchangemapi/e-mapi-connection.h
+++ b/src/libexchangemapi/e-mapi-connection.h
@@ -166,7 +166,7 @@ struct _EMapiRecipient
struct _EMapiAttachment
{
struct mapi_SPropValue_array properties;
- EMapiObject *embeded_object;
+ EMapiObject *embedded_object;
EMapiAttachment *next;
};
@@ -328,6 +328,14 @@ gboolean e_mapi_connection_transfer_objects (EMapiConnection *conn,
GCancellable *cancellable,
GError **perror);
+gboolean e_mapi_connection_transfer_object (EMapiConnection *conn,
+ mapi_object_t *obj_folder,
+ mapi_id_t message_id,
+ TransferObjectCB cb,
+ gpointer cb_user_data,
+ GCancellable *cancellable,
+ GError **perror);
+
gboolean e_mapi_connection_transfer_summary (EMapiConnection *conn,
mapi_object_t *obj_folder,
const GSList *mids,
diff --git a/src/libexchangemapi/e-mapi-debug.c b/src/libexchangemapi/e-mapi-debug.c
index 2508747..a715895 100644
--- a/src/libexchangemapi/e-mapi-debug.c
+++ b/src/libexchangemapi/e-mapi-debug.c
@@ -867,16 +867,16 @@ e_mapi_debug_dump_object (EMapiObject *object, gboolean with_properties, gint in
for (index = 0, recipient = object->recipients; recipient; index++, recipient = recipient->next) {
g_print ("%*sRecipient[%d]:\n", indent + 2, "", index);
if (with_properties)
- e_mapi_debug_dump_properties (NULL, 0, &recipient->properties, indent + 3);
+ e_mapi_debug_dump_properties (NULL, 0, &recipient->properties, indent + 5);
}
for (index = 0, attachment = object->attachments; attachment; index++, attachment = attachment->next) {
g_print ("%*sAttachment[%d]:\n", indent + 2, "", index);
if (with_properties)
e_mapi_debug_dump_properties (NULL, 0, &attachment->properties, indent + 3);
- if (attachment->embeded_object) {
- g_print ("%*sEmbeded object:\n", indent + 3, "");
- e_mapi_debug_dump_object (attachment->embeded_object, indent + 5, with_properties);
+ if (attachment->embedded_object) {
+ g_print ("%*sEmbedded object:\n", indent + 3, "");
+ e_mapi_debug_dump_object (attachment->embedded_object, with_properties, indent + 5);
}
}
}
diff --git a/src/libexchangemapi/e-mapi-fast-transfer.c b/src/libexchangemapi/e-mapi-fast-transfer.c
index 57be53e..fb44646 100644
--- a/src/libexchangemapi/e-mapi-fast-transfer.c
+++ b/src/libexchangemapi/e-mapi-fast-transfer.c
@@ -95,7 +95,7 @@ e_mapi_attachment_new (TALLOC_CTX *mem_ctx)
attachment->properties.cValues = 0;
attachment->properties.lpProps = talloc_zero_array (mem_ctx, struct mapi_SPropValue, 1);
- attachment->embeded_object = NULL;
+ attachment->embedded_object = NULL;
attachment->next = NULL;
g_assert (attachment->properties.lpProps != NULL);
@@ -109,7 +109,7 @@ e_mapi_attachment_free (EMapiAttachment *attachment)
if (!attachment)
return;
- e_mapi_object_free (attachment->embeded_object);
+ e_mapi_object_free (attachment->embedded_object);
talloc_free (attachment->properties.lpProps);
talloc_free (attachment);
}
@@ -291,15 +291,15 @@ parse_marker_cb (uint32_t marker, void *closure)
g_debug ("%s: PidTagStartEmbed no object started", G_STRFUNC);
} else if (!data->current_object->attachments) {
g_debug ("%s: PidTagStartEmbed no attachment started", G_STRFUNC);
- } else if (data->current_object->attachments->embeded_object) {
- g_debug ("%s: PidTagStartEmbed attachment has embeded object already", G_STRFUNC);
+ } else if (data->current_object->attachments->embedded_object) {
+ g_debug ("%s: PidTagStartEmbed attachment has embedded object already", G_STRFUNC);
} else {
EMapiObject *object;
object = e_mapi_object_new (data->mem_ctx);
object->parent = data->current_object;
- data->current_object->attachments->embeded_object = object;
+ data->current_object->attachments->embedded_object = object;
data->current_object = object;
data->current_properties = &object->properties;
}
@@ -560,3 +560,33 @@ e_mapi_fast_transfer_object (EMapiConnection *conn,
return ms;
}
+
+enum MAPISTATUS
+e_mapi_fast_transfer_properties (EMapiConnection *conn,
+ TALLOC_CTX *mem_ctx,
+ mapi_object_t *object,
+ struct SPropTagArray *tags,
+ TransferObjectCB cb,
+ gpointer cb_user_data,
+ GCancellable *cancellable,
+ GError **perror)
+{
+ enum MAPISTATUS ms;
+ mapi_object_t fasttransfer_ctx;
+
+ g_return_val_if_fail (tags != NULL, MAPI_E_INVALID_PARAMETER);
+ g_return_val_if_fail (tags->cValues > 0, MAPI_E_INVALID_PARAMETER);
+
+ mapi_object_init (&fasttransfer_ctx);
+
+ ms = FXCopyProperties (object, 0, 0, FastTransfer_Unicode, tags, &fasttransfer_ctx);
+ if (ms == MAPI_E_SUCCESS)
+ ms = e_mapi_fast_transfer_internal (conn, mem_ctx, cb, cb_user_data, 1, FALSE, &fasttransfer_ctx, cancellable, perror);
+
+ mapi_object_release (&fasttransfer_ctx);
+
+ if (perror && !*perror && ms != MAPI_E_SUCCESS)
+ make_mapi_error (perror, G_STRFUNC, ms);
+
+ return ms;
+}
diff --git a/src/libexchangemapi/e-mapi-fast-transfer.h b/src/libexchangemapi/e-mapi-fast-transfer.h
index 7e1d4fb..b604076 100644
--- a/src/libexchangemapi/e-mapi-fast-transfer.h
+++ b/src/libexchangemapi/e-mapi-fast-transfer.h
@@ -58,6 +58,15 @@ enum MAPISTATUS e_mapi_fast_transfer_object (EMapiConnection *conn,
GCancellable *cancellable,
GError **perror);
+enum MAPISTATUS e_mapi_fast_transfer_properties (EMapiConnection *conn,
+ TALLOC_CTX *mem_ctx,
+ mapi_object_t *object,
+ struct SPropTagArray *tags,
+ TransferObjectCB cb,
+ gpointer cb_user_data,
+ GCancellable *cancellable,
+ GError **perror);
+
G_END_DECLS
#endif /* E_MAPI_FAST_TRANSFER_H */
diff --git a/src/libexchangemapi/e-mapi-mail-utils.c b/src/libexchangemapi/e-mapi-mail-utils.c
index 77e893d..2f12040 100644
--- a/src/libexchangemapi/e-mapi-mail-utils.c
+++ b/src/libexchangemapi/e-mapi-mail-utils.c
@@ -22,6 +22,7 @@
#endif
#include <camel/camel.h>
+#include <libecal/e-cal-util.h>
#include "e-mapi-defs.h"
#include "e-mapi-utils.h"
@@ -977,6 +978,779 @@ mapi_mail_item_to_mime_message (EMapiConnection *conn, MailItem *item)
return msg;
}
+void
+e_mapi_mail_utils_decode_email_address (EMapiConnection *conn,
+ struct mapi_SPropValue_array *properties,
+ const uint32_t *name_proptags,
+ guint name_proptags_len,
+ const uint32_t *smtp_proptags,
+ guint smtp_proptags_len,
+ uint32_t email_type_proptag,
+ uint32_t email_proptag,
+ gchar **name,
+ gchar **email)
+{
+ gint ii;
+ const gchar *cname = NULL, *cemail = NULL;
+
+ g_return_if_fail (conn != NULL);
+ g_return_if_fail (properties != NULL);
+ g_return_if_fail (name_proptags_len == 0 || name_proptags != NULL);
+ g_return_if_fail (smtp_proptags_len == 0 || smtp_proptags != NULL);
+ g_return_if_fail (name != NULL);
+ g_return_if_fail (email != NULL);
+
+ *name = NULL;
+ *email = NULL;
+
+ for (ii = 0; ii < name_proptags_len && !cname; ii++) {
+ cname = e_mapi_util_find_array_propval (properties, name_proptags[ii]);
+ }
+
+ for (ii = 0; ii < smtp_proptags_len && !cemail; ii++) {
+ cemail = e_mapi_util_find_array_propval (properties, smtp_proptags[ii]);
+ }
+
+ if (!cemail) {
+ const gchar *addr_type = e_mapi_util_find_array_propval (properties, email_type_proptag);
+ const gchar *email_addr = e_mapi_util_find_array_propval (properties, email_proptag);
+
+ if (addr_type && g_ascii_strcasecmp (addr_type, "EX") == 0 && email_addr)
+ *email = e_mapi_connection_ex_to_smtp (conn, email_addr, name, NULL, NULL);
+ else if (addr_type && g_ascii_strcasecmp (addr_type, "SMTP") == 0)
+ cemail = email_addr;
+ }
+
+ if (!*email) {
+ *name = g_strdup (cname);
+ *email = g_strdup (cemail);
+ }
+}
+
+void
+e_mapi_mail_utils_decode_email_address1 (EMapiConnection *conn,
+ struct mapi_SPropValue_array *properties,
+ uint32_t name_proptag,
+ uint32_t email_proptag,
+ uint32_t email_type_proptag,
+ gchar **name,
+ gchar **email)
+{
+ uint32_t names[1];
+
+ names[0] = name_proptag;
+
+ e_mapi_mail_utils_decode_email_address (conn, properties, names, 1, NULL, 0, email_type_proptag, email_proptag, name, email);
+}
+
+void
+e_mapi_mail_utils_decode_recipients (EMapiConnection *conn,
+ EMapiRecipient *recipients,
+ CamelAddress *to_addr,
+ CamelAddress *cc_addr,
+ CamelAddress *bcc_addr)
+{
+ const uint32_t name_proptags[] = {
+ PROP_TAG (PT_UNICODE, 0x6001), /* PidTagNickname for Recipients table */
+ PidTagNickname,
+ PidTagDisplayName,
+ PidTagRecipientDisplayName,
+ PidTag7BitDisplayName
+ };
+
+ const uint32_t email_proptags[] = {
+ PidTagPrimarySmtpAddress,
+ PidTagSmtpAddress
+ };
+
+ EMapiRecipient *recipient;
+
+ g_return_if_fail (conn != NULL);
+ g_return_if_fail (to_addr != NULL);
+ g_return_if_fail (cc_addr != NULL);
+ g_return_if_fail (bcc_addr != NULL);
+
+ for (recipient = recipients; recipient; recipient = recipient->next) {
+ const uint32_t *recip_type = e_mapi_util_find_array_propval (&recipient->properties, PidTagRecipientType);
+ gchar *name = NULL, *email = NULL;
+ CamelAddress *addr = NULL;
+
+ if (!recip_type)
+ continue;
+
+ switch (*recip_type) {
+ case MAPI_TO:
+ addr = to_addr;
+ break;
+ case MAPI_CC:
+ addr = cc_addr;
+ break;
+ case MAPI_BCC:
+ addr = bcc_addr;
+ break;
+ default:
+ break;
+ }
+
+ if (!addr)
+ continue;
+
+ e_mapi_mail_utils_decode_email_address (conn, &recipient->properties,
+ name_proptags, G_N_ELEMENTS (name_proptags),
+ email_proptags, G_N_ELEMENTS (email_proptags),
+ PidTagAddressType, PidTagEmailAddress,
+ &name, &email);
+
+ camel_internet_address_add (CAMEL_INTERNET_ADDRESS (addr), name, email ? email : "");
+
+ g_free (name);
+ g_free (email);
+ }
+}
+
+static void
+build_body_part_content (CamelMimePart *part, EMapiObject *object, uint32_t proptag)
+{
+ gconstpointer value;
+
+ g_return_if_fail (part != NULL);
+ g_return_if_fail (object != NULL);
+ g_return_if_fail (proptag == PidTagHtml || proptag == PidTagBody);
+
+ camel_mime_part_set_encoding (part, CAMEL_TRANSFER_ENCODING_8BIT);
+
+ value = e_mapi_util_find_array_propval (&object->properties, proptag);
+ if (value) {
+ const gchar *type = NULL;
+ gchar *buff = NULL, *in_utf8;
+ const uint32_t *pcpid = e_mapi_util_find_array_propval (&object->properties, PidTagInternetCodepage);
+
+ if (proptag == PidTagBody) {
+ type = "text/plain";
+ } else {
+ type = "text/html";
+ }
+
+ proptag = e_mapi_util_find_array_proptag (&object->properties, proptag);
+ if (pcpid && *pcpid && (proptag & 0xFFFF) != PT_UNICODE) {
+ uint32_t cpid = *pcpid;
+
+ if (cpid == 20127)
+ buff = g_strdup_printf ("%s; charset=\"us-ascii\"", type);
+ else if (cpid >= 28591 && cpid <= 28599)
+ buff = g_strdup_printf ("%s; charset=\"ISO-8859-%d\"", type, cpid % 10);
+ else if (cpid == 28603)
+ buff = g_strdup_printf ("%s; charset=\"ISO-8859-13\"", type);
+ else if (cpid == 28605)
+ buff = g_strdup_printf ("%s; charset=\"ISO-8859-15\"", type);
+ else if (cpid == 65000)
+ buff = g_strdup_printf ("%s; charset=\"UTF-7\"", type);
+ else if (cpid == 65001)
+ buff = g_strdup_printf ("%s; charset=\"UTF-8\"", type);
+ else
+ buff = g_strdup_printf ("%s; charset=\"CP%d\"", type, cpid);
+ type = buff;
+ }
+
+ in_utf8 = NULL;
+
+ if (proptag == PidTagHtml) {
+ const struct SBinary_short *html_bin = value;
+
+ if (e_mapi_utils_ensure_utf8_string (proptag, pcpid, html_bin->lpb, html_bin->cb, &in_utf8))
+ camel_mime_part_set_content (part, in_utf8, strlen (in_utf8), type);
+ else
+ camel_mime_part_set_content (part, (const gchar *) html_bin->lpb, html_bin->cb, type);
+
+ } else {
+ const gchar *str = value;
+
+ if (e_mapi_utils_ensure_utf8_string (proptag, pcpid, (const guint8 *) str, strlen (str), &in_utf8))
+ str = in_utf8;
+
+ camel_mime_part_set_content (part, str, strlen (str), type);
+ }
+
+ g_free (in_utf8);
+ g_free (buff);
+ } else
+ camel_mime_part_set_content (part, " ", strlen (" "), "text/plain");
+}
+
+static gboolean
+is_apple_attach (EMapiAttachment *attach, guint32 *data_len, guint32 *resource_len)
+{
+ gboolean is_apple = FALSE;
+ const struct SBinary_short *encoding_bin = e_mapi_util_find_array_propval (&attach->properties, PidTagAttachEncoding);
+ guint8 apple_enc_magic[] = { 0x2A, 0x86, 0x48, 0x86, 0xF7, 0x14, 0x03, 0x0B, 0x01 };
+
+ if (encoding_bin && encoding_bin->lpb && encoding_bin->cb == G_N_ELEMENTS (apple_enc_magic)) {
+ gint idx;
+
+ is_apple = TRUE;
+ for (idx = 0; idx < encoding_bin->cb && is_apple; idx++) {
+ is_apple = apple_enc_magic[idx] == encoding_bin->lpb[idx];
+ }
+ }
+
+ if (is_apple) {
+ /* check boundaries too */
+ const struct SBinary_short *data_bin = e_mapi_util_find_array_propval (&attach->properties, PidTagAttachDataBinary);
+
+ is_apple = data_bin && data_bin->lpb && data_bin->cb > 128;
+
+ if (is_apple) {
+ const guint8 *bin = data_bin->lpb;
+
+ /* 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_bin->cb && bin[1] < 64;
+ }
+ }
+
+ return is_apple;
+}
+
+static void
+classify_attachments (EMapiConnection *conn, EMapiAttachment *attachments, const gchar *msg_class, GSList **inline_attachments, GSList **noninline_attachments)
+{
+ EMapiAttachment *attach;
+ gboolean is_smime = msg_class && strstr (msg_class, ".SMIME.") > msg_class;
+
+ g_return_if_fail (inline_attachments != NULL);
+ g_return_if_fail (noninline_attachments != NULL);
+
+ for (attach = attachments; attach != NULL; attach = attach->next) {
+ const gchar *filename, *mime_type, *content_id = NULL;
+ CamelContentType *content_type;
+ CamelMimePart *part;
+ const uint32_t *ui32;
+ const struct SBinary_short *data_bin;
+ gboolean is_apple;
+ guint32 apple_data_len = 0, apple_resource_len = 0;
+
+ data_bin = e_mapi_util_find_array_propval (&attach->properties, PidTagAttachDataBinary);
+ if (!data_bin && !attach->embedded_object) {
+ g_debug ("%s: Skipping attachment without data and without embedded object", G_STRFUNC);
+ continue;
+ }
+
+ is_apple = is_apple_attach (attach, &apple_data_len, &apple_resource_len);
+
+ /* Content-Type */
+ ui32 = e_mapi_util_find_array_propval (&attach->properties, PidTagAttachMethod);
+ if (ui32 && *ui32 == ATTACH_EMBEDDED_MSG) {
+ mime_type = "message/rfc822";
+ } else {
+ mime_type = e_mapi_util_find_array_propval (&attach->properties, PidTagAttachMimeTag);
+ 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 = e_mapi_util_find_array_propval (&attach->properties, PidTagAttachLongFilename);
+ if (!filename || !*filename)
+ filename = e_mapi_util_find_array_propval (&attach->properties, PidTagAttachFilename);
+ camel_mime_part_set_filename (part, filename);
+ camel_content_type_set_param (((CamelDataWrapper *) part)->mime_type, "name", filename);
+
+ if (is_apple) {
+ CamelMultipart *mp;
+ gchar *apple_filename;
+ const struct SBinary_short *mac_info_bin;
+
+ 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);
+
+ mac_info_bin = e_mapi_util_find_array_propval (&attach->properties, PidNameAttachmentMacInfo);
+ if (mac_info_bin && mac_info_bin->lpb && mac_info_bin->cb > 0) {
+ camel_mime_part_set_content (part, (const gchar *) mac_info_bin->lpb, mac_info_bin->cb, 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, data_bin->lpb + 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 ((const gchar *) data_bin->lpb + 2, data_bin->lpb[1]);
+ camel_mime_part_set_filename (part, (apple_filename && *apple_filename) ? apple_filename : filename);
+ g_free (apple_filename);
+
+ mime_type = e_mapi_util_find_array_propval (&attach->properties, PidNameAttachmentMacContentType);
+ if (!mime_type)
+ mime_type = "application/octet-stream";
+
+ camel_mime_part_set_content (part, (const gchar *) data_bin->lpb + 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 if (is_smime) {
+ CamelMimeParser *parser;
+ CamelStream *mem;
+
+ mem = camel_stream_mem_new ();
+ camel_stream_write (mem, (const gchar *) data_bin->lpb, data_bin->cb, NULL, NULL);
+ g_seekable_seek (G_SEEKABLE (mem), 0, G_SEEK_SET, NULL, NULL);
+
+ parser = camel_mime_parser_new ();
+ camel_mime_parser_scan_from (parser, FALSE);
+ camel_mime_parser_scan_pre_from (parser, FALSE);
+ camel_mime_parser_init_with_stream (parser, mem, NULL);
+
+ if (camel_mime_parser_step (parser, NULL, NULL) == CAMEL_MIME_PARSER_STATE_HEADER
+ && camel_mime_parser_content_type (parser) != NULL) {
+ g_object_unref (part);
+ part = camel_mime_part_new ();
+
+ camel_data_wrapper_set_mime_type_field (CAMEL_DATA_WRAPPER (part), camel_mime_parser_content_type (parser));
+ camel_mime_part_construct_content_from_parser (part, parser, NULL, NULL);
+ } else {
+ is_smime = FALSE;
+ }
+
+ g_object_unref (parser);
+ g_object_unref (mem);
+ }
+
+ if (!is_smime && !is_apple) {
+ if (ui32 && *ui32 == ATTACH_EMBEDDED_MSG && attach->embedded_object) {
+ CamelMimeMessage *embedded_msg;
+
+ embedded_msg = e_mapi_mail_utils_object_to_message (conn, attach->embedded_object);
+ if (embedded_msg) {
+ CamelStream *mem = camel_stream_mem_new ();
+ GByteArray *data;
+
+ data = g_byte_array_new ();
+
+ mem = camel_stream_mem_new ();
+ camel_stream_mem_set_byte_array (CAMEL_STREAM_MEM (mem), data);
+ camel_data_wrapper_write_to_stream_sync (
+ CAMEL_DATA_WRAPPER (embedded_msg), mem, NULL, NULL);
+
+ g_object_unref (mem);
+ g_object_unref (embedded_msg);
+
+ camel_mime_part_set_content (part, (const gchar *) data->data, data->len, mime_type);
+
+ g_byte_array_free (data, TRUE);
+ } else {
+ camel_mime_part_set_content (part, (const gchar *) data_bin->lpb, data_bin->cb, mime_type);
+ }
+ } else {
+ camel_mime_part_set_content (part, (const gchar *) data_bin->lpb, data_bin->cb, 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 = e_mapi_util_find_array_propval (&attach->properties, PidTagAttachContentId);
+
+ /* TODO : Add disposition */
+ if (content_id && !is_apple && !is_smime) {
+ camel_mime_part_set_content_id (part, content_id);
+ *inline_attachments = g_slist_append (*inline_attachments, part);
+ } else
+ *noninline_attachments = g_slist_append (*noninline_attachments, part);
+ }
+}
+
+static void
+add_multipart_attachments (CamelMultipart *multipart, GSList *attachments)
+{
+ CamelMimePart *part;
+ while (attachments) {
+ part = attachments->data;
+ camel_multipart_add_part (multipart, part);
+ attachments = attachments->next;
+ }
+}
+
+static CamelMultipart *
+build_multipart_related (EMapiObject *object, GSList *inline_attachments)
+{
+ CamelMimePart *part;
+ CamelMultipart *m_related = camel_multipart_new ();
+ camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (m_related), "multipart/related");
+ camel_multipart_set_boundary (m_related, NULL);
+
+ if (e_mapi_util_find_array_propval (&object->properties, PidTagHtml)) {
+ part = camel_mime_part_new ();
+ build_body_part_content (part, object, PidTagHtml);
+ camel_multipart_add_part (m_related, part);
+ g_object_unref (part);
+ } else if (e_mapi_util_find_array_propval (&object->properties, PidTagBody)) {
+ part = camel_mime_part_new ();
+ build_body_part_content (part, object, PidTagBody);
+ camel_multipart_add_part (m_related, part);
+ g_object_unref (part);
+ }
+
+ add_multipart_attachments (m_related, inline_attachments);
+
+ return m_related;
+}
+
+static CamelMultipart *
+build_multipart_alternative (EMapiObject *object, GSList *inline_attachments)
+{
+ CamelMimePart *part;
+ CamelMultipart *m_alternative;
+
+ m_alternative = camel_multipart_new ();
+ camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (m_alternative), "multipart/alternative");
+ camel_multipart_set_boundary (m_alternative, NULL);
+
+ if (e_mapi_util_find_array_propval (&object->properties, PidTagBody)) {
+ part = camel_mime_part_new ();
+ build_body_part_content (part, object, PidTagBody);
+ camel_multipart_add_part (m_alternative, part);
+ g_object_unref (part);
+ }
+
+ if (e_mapi_util_find_array_propval (&object->properties, PidTagHtml)) {
+ part = camel_mime_part_new ();
+ if (inline_attachments) {
+ CamelMultipart *m_related;
+
+ m_related = build_multipart_related (object, inline_attachments);
+ camel_medium_set_content (CAMEL_MEDIUM (part), CAMEL_DATA_WRAPPER (m_related));
+ g_object_unref (m_related);
+ } else {
+ build_body_part_content (part, object, PidTagHtml);
+ }
+ camel_multipart_add_part (m_alternative, part);
+ g_object_unref (part);
+ }
+
+ return m_alternative;
+}
+
+static CamelMultipart *
+build_multipart_mixed (CamelMultipart *content, GSList *attachments)
+{
+ CamelMimePart *part = camel_mime_part_new ();
+ CamelMultipart *m_mixed = camel_multipart_new ();
+ camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (m_mixed), "multipart/mixed");
+ camel_multipart_set_boundary (m_mixed, NULL);
+
+ camel_medium_set_content (CAMEL_MEDIUM (part), CAMEL_DATA_WRAPPER (content));
+ camel_multipart_add_part (m_mixed, part);
+
+ add_multipart_attachments (m_mixed, attachments);
+
+ return m_mixed;
+}
+
+static gchar *
+build_ical_string (EMapiConnection *conn,
+ EMapiObject *object,
+ const gchar *msg_class)
+{
+ gchar *ical_string = NULL, *use_uid;
+ icalcomponent_kind ical_kind = ICAL_NO_COMPONENT;
+ icalproperty_method ical_method = ICAL_METHOD_NONE;
+ const uint64_t *pmid;
+ ECalComponent *comp;
+ icalcomponent *icalcomp;
+ GSList *detached_components = NULL, *iter;
+
+ g_return_val_if_fail (conn != NULL, NULL);
+ g_return_val_if_fail (object != NULL, NULL);
+ g_return_val_if_fail (msg_class != NULL, NULL);
+
+ if (!g_ascii_strcasecmp (msg_class, IPM_SCHEDULE_MEETING_REQUEST)) {
+ ical_method = ICAL_METHOD_REQUEST;
+ ical_kind = ICAL_VEVENT_COMPONENT;
+ } else if (!g_ascii_strcasecmp (msg_class, IPM_SCHEDULE_MEETING_CANCELED)) {
+ ical_method = ICAL_METHOD_CANCEL;
+ ical_kind = ICAL_VEVENT_COMPONENT;
+ } else if (g_str_has_prefix (msg_class, IPM_SCHEDULE_MEETING_RESP_PREFIX)) {
+ ical_method = ICAL_METHOD_REPLY;
+ ical_kind = ICAL_VEVENT_COMPONENT;
+ } else {
+ return NULL;
+ }
+
+ pmid = e_mapi_util_find_array_propval (&object->properties, PidTagMid);
+ if (pmid)
+ use_uid = e_mapi_util_mapi_id_to_string (*pmid);
+ else
+ use_uid = e_cal_component_gen_uid ();
+
+ comp = e_mapi_cal_util_object_to_comp (conn, object, ical_kind, ical_method == ICAL_METHOD_REPLY, NULL, use_uid, &detached_components);
+
+ g_free (use_uid);
+
+ if (!comp)
+ return NULL;
+
+ icalcomp = e_cal_util_new_top_level ();
+ icalcomponent_set_method (icalcomp, ical_method);
+ if (comp)
+ icalcomponent_add_component (icalcomp,
+ icalcomponent_new_clone (e_cal_component_get_icalcomponent (comp)));
+ for (iter = detached_components; iter; iter = g_slist_next (iter)) {
+ icalcomponent_add_component (icalcomp,
+ icalcomponent_new_clone (e_cal_component_get_icalcomponent (iter->data)));
+ }
+
+ ical_string = icalcomponent_as_ical_string_r (icalcomp);
+
+ icalcomponent_free (icalcomp);
+ g_slist_free_full (detached_components, g_object_unref);
+ g_object_unref (comp);
+
+ return ical_string;
+}
+
+CamelMimeMessage *
+e_mapi_mail_utils_object_to_message (EMapiConnection *conn, /* const */ EMapiObject *object)
+{
+ CamelMimeMessage *msg;
+ CamelMultipart *multipart_body = NULL;
+ GSList *inline_attachments, *noninline_attachments;
+ gboolean build_alternative, build_related, build_calendar;
+ const gchar *str, *msg_class;
+ gboolean skip_set_content = FALSE;
+ gchar *ical_string = NULL;
+
+ g_return_val_if_fail (conn != NULL, NULL);
+ g_return_val_if_fail (object != NULL, NULL);
+
+ msg = camel_mime_message_new ();
+
+ str = e_mapi_util_find_array_propval (&object->properties, PidTagTransportMessageHeaders);
+ if (str && *str) {
+ CamelMimePart *part = camel_mime_part_new ();
+ CamelStream *stream;
+ CamelMimeParser *parser;
+
+ stream = camel_stream_mem_new_with_buffer (str, strlen (str));
+ parser = camel_mime_parser_new ();
+ camel_mime_parser_init_with_stream (parser, stream, NULL);
+ camel_mime_parser_scan_from (parser, FALSE);
+ g_object_unref (stream);
+
+ if (camel_mime_part_construct_from_parser_sync (part, parser, NULL, NULL)) {
+ struct _camel_header_raw *h;
+
+ 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++;
+
+ camel_medium_add_header (CAMEL_MEDIUM (msg), h->name, value);
+ }
+ }
+
+ g_object_unref (parser);
+ g_object_unref (part);
+ } else {
+ CamelInternetAddress *to_addr, *cc_addr, *bcc_addr;
+ const struct FILETIME *delivery_time;
+ gchar *name, *email;
+
+ to_addr = camel_internet_address_new ();
+ cc_addr = camel_internet_address_new ();
+ bcc_addr = camel_internet_address_new ();
+
+ e_mapi_mail_utils_decode_recipients (conn, object->recipients, (CamelAddress *) to_addr, (CamelAddress *) cc_addr, (CamelAddress *) bcc_addr);
+
+ camel_mime_message_set_recipients (msg, CAMEL_RECIPIENT_TYPE_TO, to_addr);
+ camel_mime_message_set_recipients (msg, CAMEL_RECIPIENT_TYPE_CC, cc_addr);
+ camel_mime_message_set_recipients (msg, CAMEL_RECIPIENT_TYPE_BCC, bcc_addr);
+
+ g_object_unref (to_addr);
+ g_object_unref (cc_addr);
+ g_object_unref (bcc_addr);
+
+ delivery_time = e_mapi_util_find_array_propval (&object->properties, PidTagMessageDeliveryTime);
+ if (delivery_time) {
+ time_t received_time, actual_time;
+ gint offset = 0;
+
+ received_time = e_mapi_util_filetime_to_time_t (delivery_time);
+ actual_time = camel_header_decode_date (ctime (&received_time), &offset);
+ camel_mime_message_set_date (msg, actual_time, offset);
+ }
+
+ str = e_mapi_util_find_array_propval (&object->properties, PidTagSubject);
+ if (str)
+ camel_mime_message_set_subject (msg, str);
+
+ name = NULL;
+ email = NULL;
+
+ e_mapi_mail_utils_decode_email_address1 (conn, &object->properties,
+ PidTagSentRepresentingName,
+ PidTagSentRepresentingEmailAddress,
+ PidTagSentRepresentingAddressType,
+ &name, &email);
+
+ if (email && *email) {
+ CamelInternetAddress *addr;
+
+ addr = camel_internet_address_new();
+ camel_internet_address_add (addr, name, email);
+ camel_mime_message_set_from (msg, addr);
+ }
+
+ g_free (name);
+ g_free (email);
+
+ /* Threading */
+ str = e_mapi_util_find_array_propval (&object->properties, PidTagInternetMessageId);
+ if (str)
+ camel_medium_add_header (CAMEL_MEDIUM (msg), "Message-ID", str);
+
+ str = e_mapi_util_find_array_propval (&object->properties, PidTagInternetReferences);
+ if (str)
+ camel_medium_add_header (CAMEL_MEDIUM (msg), "References", str);
+
+ str = e_mapi_util_find_array_propval (&object->properties, PidTagInReplyToId);
+ if (str)
+ camel_medium_add_header (CAMEL_MEDIUM (msg), "In-Reply-To", str);
+ }
+
+ str = e_mapi_util_find_array_propval (&object->properties, PidNameContentClass);
+ if (str)
+ camel_medium_add_header (CAMEL_MEDIUM (msg), "Content-class", str);
+
+ inline_attachments = NULL;
+ noninline_attachments = NULL;
+ msg_class = e_mapi_util_find_array_propval (&object->properties, PidTagMessageClass);
+ classify_attachments (conn, object->attachments, msg_class, &inline_attachments, &noninline_attachments);
+
+ build_calendar = msg_class && g_str_has_prefix (msg_class, IPM_SCHEDULE_MEETING_PREFIX);
+ if (build_calendar) {
+ ical_string = build_ical_string (conn, object, msg_class);
+ if (!ical_string)
+ build_calendar = FALSE;
+ }
+
+ build_alternative = !build_calendar
+ && e_mapi_util_find_array_propval (&object->properties, PidTagHtml)
+ && e_mapi_util_find_array_propval (&object->properties, PidTagBody);
+ build_related = !build_calendar && !build_alternative && inline_attachments;
+
+ if (build_calendar) {
+ g_return_val_if_fail (ical_string != NULL, msg);
+
+ camel_mime_part_set_content (CAMEL_MIME_PART (msg), ical_string, strlen (ical_string), "text/calendar");
+ } else if (build_alternative) {
+ multipart_body = build_multipart_alternative (object, inline_attachments);
+ } else if (build_related) {
+ multipart_body = build_multipart_related (object, inline_attachments);
+ } else if (noninline_attachments) {
+ /* Simple multipart/mixed */
+ CamelMimePart *part = camel_mime_part_new ();
+
+ multipart_body = camel_multipart_new ();
+ camel_data_wrapper_set_mime_type (CAMEL_DATA_WRAPPER (multipart_body), "multipart/mixed");
+ camel_multipart_set_boundary (multipart_body, NULL);
+ if (e_mapi_util_find_array_propval (&object->properties, PidTagHtml))
+ build_body_part_content (part, object, PidTagHtml);
+ else
+ build_body_part_content (part, object, PidTagBody);
+ camel_multipart_add_part (multipart_body, part);
+ g_object_unref (part);
+ } else {
+ /* Flat message */
+ if (e_mapi_util_find_array_propval (&object->properties, PidTagHtml))
+ build_body_part_content (CAMEL_MIME_PART (msg), object, PidTagHtml);
+ else
+ build_body_part_content (CAMEL_MIME_PART (msg), object, PidTagBody);
+ }
+
+ if (noninline_attachments) { /* multipart/mixed */
+ if (build_alternative || build_related || build_calendar) {
+ multipart_body = build_multipart_mixed (multipart_body, noninline_attachments);
+ } else if (g_slist_length (noninline_attachments) == 1 && msg_class && strstr (msg_class, ".SMIME") > msg_class) {
+ CamelMimePart *part = noninline_attachments->data;
+
+ skip_set_content = TRUE;
+
+ camel_medium_set_content (CAMEL_MEDIUM (msg), CAMEL_DATA_WRAPPER (part));
+
+ if (!strstr (msg_class, ".SMIME.")) {
+ /* encrypted */
+ camel_medium_set_content (CAMEL_MEDIUM (msg), camel_medium_get_content (CAMEL_MEDIUM (part)));
+ camel_mime_part_set_encoding (CAMEL_MIME_PART (msg), camel_mime_part_get_encoding (part));
+ } else {
+ /* signed */
+ camel_medium_set_content (CAMEL_MEDIUM (msg), CAMEL_DATA_WRAPPER (part));
+ }
+ } else {
+ add_multipart_attachments (multipart_body, noninline_attachments);
+ }
+ }
+
+ if (!skip_set_content && multipart_body)
+ camel_medium_set_content (CAMEL_MEDIUM (msg), CAMEL_DATA_WRAPPER (multipart_body));
+
+ if (multipart_body)
+ g_object_unref (multipart_body);
+ g_slist_free_full (inline_attachments, g_object_unref);
+ g_slist_free_full (noninline_attachments, g_object_unref);
+ g_free (ical_string);
+
+ return msg;
+}
+
#define STREAM_SIZE 4000
static void
diff --git a/src/libexchangemapi/e-mapi-mail-utils.h b/src/libexchangemapi/e-mapi-mail-utils.h
index 01c9cf7..0723633 100644
--- a/src/libexchangemapi/e-mapi-mail-utils.h
+++ b/src/libexchangemapi/e-mapi-mail-utils.h
@@ -97,10 +97,36 @@ gboolean mapi_mail_get_item_prop_list (EMapiConnection *conn,
GCancellable *cancellable,
GError **perror);
+struct _CamelAddress;
struct _CamelMimeMessage;
+
struct _CamelMimeMessage *mapi_mail_item_to_mime_message (EMapiConnection *conn, MailItem *item);
-struct _CamelAddress;
+struct _CamelMimeMessage *e_mapi_mail_utils_object_to_message (EMapiConnection *conn,
+ /* const */ EMapiObject *object);
+void e_mapi_mail_utils_decode_email_address (EMapiConnection *conn,
+ struct mapi_SPropValue_array *properties,
+ const uint32_t *name_proptags,
+ guint name_proptags_len,
+ const uint32_t *email_proptags,
+ guint email_proptags_len,
+ uint32_t email_type_proptag,
+ uint32_t email_proptag,
+ gchar **name,
+ gchar **email);
+void e_mapi_mail_utils_decode_email_address1 (EMapiConnection *conn,
+ struct mapi_SPropValue_array *properties,
+ uint32_t name_proptag,
+ uint32_t email_proptag,
+ uint32_t email_type_proptag,
+ gchar **name,
+ gchar **email);
+void e_mapi_mail_utils_decode_recipients (EMapiConnection *conn,
+ EMapiRecipient *recipients,
+ struct _CamelAddress *to,
+ struct _CamelAddress *cc,
+ struct _CamelAddress *bcc);
+
MailItem *mapi_mime_message_to_mail_item (struct _CamelMimeMessage *message, gint32 message_camel_flags, struct _CamelAddress *from, GCancellable *cancellable, GError **error);
/* uses MailItem * as 'data' pointer */
diff --git a/src/libexchangemapi/e-mapi-utils.c b/src/libexchangemapi/e-mapi-utils.c
index 6f5b766..95318dc 100644
--- a/src/libexchangemapi/e-mapi-utils.c
+++ b/src/libexchangemapi/e-mapi-utils.c
@@ -263,6 +263,31 @@ e_mapi_util_find_array_namedid (struct mapi_SPropValue_array *properties, EMapiC
return res;
}
+uint32_t
+e_mapi_util_find_array_proptag (struct mapi_SPropValue_array *properties, uint32_t proptag)
+{
+ g_return_val_if_fail (properties != NULL, proptag);
+
+ if ((proptag & 0xFFFF) == PT_STRING8 ||
+ (proptag & 0xFFFF) == PT_UNICODE) {
+ gint ii;
+ uint32_t tag1, tag2;
+
+ tag1 = (proptag & 0xFFFF0000) | PT_STRING8;
+ tag2 = (proptag & 0xFFFF0000) | PT_UNICODE;
+
+ for (ii = 0; ii < properties->cValues; ii++) {
+ uint32_t tag = properties->lpProps[ii].ulPropTag;
+ if (tag == tag1 || tag == tag2) {
+ proptag = tag;
+ break;
+ }
+ }
+ }
+
+ return proptag;
+}
+
enum MAPISTATUS
e_mapi_util_find_array_datetime_propval (struct timeval *tv, struct mapi_SPropValue_array *properties, uint32_t proptag)
{
@@ -1255,3 +1280,41 @@ e_mapi_utils_destroy_mapi_context (struct mapi_context *mapi_ctx)
MAPIUninitialize (mapi_ctx);
e_mapi_utils_global_unlock ();
}
+
+gboolean
+e_mapi_utils_ensure_utf8_string (uint32_t proptag,
+ const uint32_t *cpid,
+ const guint8 *buf_data,
+ guint32 buf_len,
+ gchar **out_utf8)
+{
+ g_return_val_if_fail (buf_data != NULL, FALSE);
+ g_return_val_if_fail (out_utf8 != NULL, FALSE);
+
+ if (proptag != PidTagHtml && (proptag & 0xFFFF) != PT_UNICODE)
+ return FALSE;
+
+ *out_utf8 = NULL;
+
+ if ((cpid && (*cpid == 1200 || *cpid == 1201)) || (buf_len > 5 && buf_data[3] == '\0')) {
+ /* this is special, get the CPID and transform to utf8 when it's utf16 */
+ gsize written = 0;
+ gchar *in_utf8;
+
+ /* skip Unicode marker, if there */
+ if (buf_len >= 2 && buf_data[0] == 0xFF && buf_data[1] == 0xFE)
+ in_utf8 = g_convert ((const gchar *) buf_data + 2, buf_len - 2, "UTF-8", "UTF-16", NULL, &written, NULL);
+ else
+ in_utf8 = g_convert ((const gchar *) buf_data, buf_len, "UTF-8", "UTF-16", NULL, &written, NULL);
+
+ if (in_utf8 && written > 0) {
+ *out_utf8 = g_strndup (in_utf8, written);
+ g_free (in_utf8);
+ }
+ }
+
+ if (!*out_utf8)
+ *out_utf8 = g_strndup ((const gchar *) buf_data, buf_len);
+
+ return TRUE;
+}
diff --git a/src/libexchangemapi/e-mapi-utils.h b/src/libexchangemapi/e-mapi-utils.h
index 0d47066..83fd432 100644
--- a/src/libexchangemapi/e-mapi-utils.h
+++ b/src/libexchangemapi/e-mapi-utils.h
@@ -34,12 +34,13 @@ gboolean e_mapi_util_mapi_id_from_string (const gchar *str, mapi_id_t *id);
gchar * e_mapi_util_mapi_ids_to_uid (mapi_id_t fid, mapi_id_t mid);
gboolean e_mapi_util_mapi_ids_from_uid (const gchar *str, mapi_id_t *fid, mapi_id_t *mid);
-gconstpointer e_mapi_util_find_SPropVal_array_propval (struct SPropValue *values, uint32_t proptag);
-gconstpointer e_mapi_util_find_SPropVal_array_namedid (struct SPropValue *values, EMapiConnection *conn, mapi_id_t fid, uint32_t namedid);
-gconstpointer e_mapi_util_find_row_propval (struct SRow *aRow, uint32_t proptag);
-gconstpointer e_mapi_util_find_row_namedid (struct SRow *aRow, EMapiConnection *conn, mapi_id_t fid, uint32_t namedid);
-gconstpointer e_mapi_util_find_array_propval (struct mapi_SPropValue_array *properties, uint32_t proptag);
-gconstpointer e_mapi_util_find_array_namedid (struct mapi_SPropValue_array *properties, EMapiConnection *conn, mapi_id_t fid, uint32_t namedid);
+gconstpointer e_mapi_util_find_SPropVal_array_propval (struct SPropValue *values, uint32_t proptag);
+gconstpointer e_mapi_util_find_SPropVal_array_namedid (struct SPropValue *values, EMapiConnection *conn, mapi_id_t fid, uint32_t namedid);
+gconstpointer e_mapi_util_find_row_propval (struct SRow *aRow, uint32_t proptag);
+gconstpointer e_mapi_util_find_row_namedid (struct SRow *aRow, EMapiConnection *conn, mapi_id_t fid, uint32_t namedid);
+gconstpointer e_mapi_util_find_array_propval (struct mapi_SPropValue_array *properties, uint32_t proptag);
+gconstpointer e_mapi_util_find_array_namedid (struct mapi_SPropValue_array *properties, EMapiConnection *conn, mapi_id_t fid, uint32_t namedid);
+uint32_t e_mapi_util_find_array_proptag (struct mapi_SPropValue_array *properties, uint32_t proptag);
enum MAPISTATUS e_mapi_util_find_array_datetime_propval (struct timeval *tv, struct mapi_SPropValue_array *properties, uint32_t proptag);
enum MAPISTATUS e_mapi_util_find_array_datetime_namedid (struct timeval *tv, struct mapi_SPropValue_array *properties, EMapiConnection *conn, mapi_id_t fid, uint32_t namedid);
@@ -91,6 +92,11 @@ gboolean e_mapi_utils_add_spropvalue_namedid (EMapiConnection *conn,
gconstpointer prop_value,
GCancellable *cancellable,
GError **perror);
+gboolean e_mapi_utils_ensure_utf8_string (uint32_t proptag,
+ const uint32_t *cpid,
+ const guint8 *buf_data,
+ guint32 buf_len,
+ gchar **out_utf8);
uint32_t e_mapi_utils_push_crc32 (uint32_t crc32, uint8_t *bytes, uint32_t n_bytes);
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]