[evolution-ews/wip/mcrha/office365] Copy/move messages and flag changes save on the server



commit 257868783e6933b2ff5477ef42c37de006bd4233
Author: Milan Crha <mcrha redhat com>
Date:   Fri Jul 3 16:02:33 2020 +0200

    Copy/move messages and flag changes save on the server

 src/Office365/camel/camel-o365-folder.c  | 473 ++++++++++++++++++++++++++++++-
 src/Office365/camel/camel-o365-store.c   |  28 +-
 src/Office365/camel/camel-o365-store.h   |  10 +-
 src/Office365/camel/camel-o365-utils.c   |  20 +-
 src/Office365/camel/camel-o365-utils.h   |   4 +
 src/Office365/common/e-o365-connection.c | 409 ++++++++++++++++++++++++--
 src/Office365/common/e-o365-connection.h |  29 ++
 7 files changed, 920 insertions(+), 53 deletions(-)
---
diff --git a/src/Office365/camel/camel-o365-folder.c b/src/Office365/camel/camel-o365-folder.c
index c5ad7a25..e085ebb6 100644
--- a/src/Office365/camel/camel-o365-folder.c
+++ b/src/Office365/camel/camel-o365-folder.c
@@ -1094,6 +1094,477 @@ o365_folder_refresh_info_sync (CamelFolder *folder,
        return success;
 }
 
+static gboolean
+o365_folder_copy_move_to_folder_sync (CamelFolder *folder,
+                                     CamelO365Store *o365_store,
+                                     const GSList *uids,
+                                     const gchar *des_folder_id,
+                                     gboolean do_copy,
+                                     GCancellable *cancellable,
+                                     GError **error)
+{
+       CamelO365StoreSummary *o365_store_summary;
+       EO365Connection *cnc = NULL;
+       GSList *des_ids = NULL;
+       gboolean success;
+
+       g_return_val_if_fail (des_folder_id != NULL, FALSE);
+
+       o365_store_summary = camel_o365_store_ref_store_summary (o365_store);
+
+       if (g_strcmp0 (des_folder_id, "junkemail") == 0) {
+               des_folder_id = camel_o365_store_summary_dup_folder_id_for_type (o365_store_summary, 
CAMEL_FOLDER_TYPE_JUNK);
+       } else if (g_strcmp0 (des_folder_id, "deleteditems") == 0) {
+               des_folder_id = camel_o365_store_summary_dup_folder_id_for_type (o365_store_summary, 
CAMEL_FOLDER_TYPE_TRASH);
+       } else if (g_strcmp0 (des_folder_id, "inbox") == 0) {
+               des_folder_id = camel_o365_store_summary_dup_folder_id_for_type (o365_store_summary, 
CAMEL_FOLDER_TYPE_INBOX);
+       }
+
+       g_clear_object (&o365_store_summary);
+
+       if (!camel_o365_store_ensure_connected (o365_store, &cnc, cancellable, error))
+               return FALSE;
+
+       success = e_o365_connection_copy_move_mail_messages_sync (cnc, NULL, uids, des_folder_id, do_copy,
+               &des_ids, cancellable, error);
+
+       g_clear_object (&cnc);
+
+       if (!do_copy) {
+               CamelFolderChangeInfo *src_changes;
+               CamelO365Folder *o365_folder;
+               GSList *des_link, *src_link;
+               GList *removed_uids = NULL;
+
+               src_changes = camel_folder_change_info_new ();
+               o365_folder = CAMEL_O365_FOLDER (folder);
+
+               camel_folder_lock (folder);
+
+               /* Can succeed partially, thus always check the moved ids */
+               for (src_link = (GSList *) uids, des_link = des_ids;
+                    src_link && des_link;
+                    src_link = g_slist_next (src_link), des_link = g_slist_next (des_link)) {
+                       const gchar *src_uid = src_link->data;
+
+                       o365_folder_cache_remove (o365_folder, src_uid, NULL);
+
+                       removed_uids = g_list_prepend (removed_uids, (gpointer) src_uid);
+                       camel_folder_change_info_remove_uid (src_changes, src_uid);
+               }
+
+               if (removed_uids) {
+                       CamelFolderSummary *summary;
+
+                       summary = camel_folder_get_folder_summary (folder);
+                       camel_folder_summary_remove_uids (summary, removed_uids);
+
+                       g_list_free (removed_uids);
+               }
+
+               if (camel_folder_change_info_changed (src_changes))
+                       camel_folder_changed (folder, src_changes);
+
+               camel_folder_change_info_free (src_changes);
+
+               camel_folder_unlock (folder);
+       }
+
+       g_slist_free_full (des_ids, (GDestroyNotify) camel_pstring_free);
+
+       return success;
+}
+
+static gboolean
+o365_folder_delete_messages_sync (CamelFolder *folder,
+                                 CamelO365Store *o365_store,
+                                 const GSList *uids,
+                                 gboolean is_trash_folder,
+                                 GCancellable *cancellable,
+                                 GError **error)
+{
+       EO365Connection *cnc = NULL;
+       gboolean success;
+
+       if (!camel_o365_store_ensure_connected (o365_store, &cnc, cancellable, error))
+               return FALSE;
+
+       if (is_trash_folder) {
+               GSList *deleted_uids = NULL, *link;
+
+               success = e_o365_connection_delete_mail_messages_sync (cnc, NULL, uids, &deleted_uids, 
cancellable, error);
+
+               if (deleted_uids) {
+                       CamelFolderChangeInfo *changes;
+                       CamelO365Folder *o365_folder;
+                       GList *removed_uids = NULL;
+
+                       o365_folder = CAMEL_O365_FOLDER (folder);
+                       changes = camel_folder_change_info_new ();
+
+                       camel_folder_lock (folder);
+
+                       /* Can succeed partially, thus always check the moved ids */
+                       for (link = deleted_uids; link; link = g_slist_next (link)) {
+                               const gchar *uid = link->data;
+
+                               o365_folder_cache_remove (o365_folder, uid, NULL);
+
+                               removed_uids = g_list_prepend (removed_uids, (gpointer) uid);
+                               camel_folder_change_info_remove_uid (changes, uid);
+                       }
+
+                       if (removed_uids) {
+                               CamelFolderSummary *summary;
+
+                               summary = camel_folder_get_folder_summary (folder);
+                               camel_folder_summary_remove_uids (summary, removed_uids);
+
+                               g_list_free (removed_uids);
+                       }
+
+                       if (camel_folder_change_info_changed (changes))
+                               camel_folder_changed (folder, changes);
+
+                       camel_folder_change_info_free (changes);
+
+                       camel_folder_unlock (folder);
+
+                       g_slist_free (deleted_uids);
+               }
+       } else {
+               success = o365_folder_copy_move_to_folder_sync (folder, o365_store,
+                       uids, "deleteditems", FALSE, cancellable, error);
+       }
+
+       g_clear_object (&cnc);
+
+       return success;
+}
+
+static JsonBuilder *
+o365_folder_message_info_changes_to_json (CamelMessageInfo *mi)
+{
+       JsonBuilder *builder;
+
+       builder = json_builder_new_immutable ();
+       e_o365_json_begin_object_member (builder, NULL);
+
+       camel_o365_utils_add_message_flags (builder, mi, NULL);
+
+       e_o365_json_end_object_member (builder);
+
+       return builder;
+}
+
+static gboolean
+o365_folder_save_flags_sync (CamelFolder *folder,
+                            CamelO365Store *o365_store,
+                            GSList *mi_list, /* CamelMessageInfo * */
+                            GCancellable *cancellable,
+                            GError **error)
+{
+       EO365Connection *cnc = NULL;
+       gboolean success = TRUE;
+
+       /* Trap an error, but do not stop other processing */
+       g_return_val_if_fail (mi_list != NULL, TRUE);
+
+       if (!camel_o365_store_ensure_connected (o365_store, &cnc, cancellable, error))
+               return FALSE;
+
+       if (mi_list->next) {
+               GSList *link;
+               GPtrArray *requests;
+
+               requests = g_ptr_array_new_full (g_slist_length (mi_list), g_object_unref);
+
+               for (link = mi_list; link && success; link = g_slist_next (link)) {
+                       CamelMessageInfo *mi = link->data;
+                       SoupMessage *message;
+                       JsonBuilder *builder;
+
+                       builder = o365_folder_message_info_changes_to_json (mi);
+
+                       message = e_o365_connection_prepare_update_mail_message (cnc, NULL,
+                               camel_message_info_get_uid (mi), builder, error);
+
+                       g_clear_object (&builder);
+
+                       if (!message)
+                               success = FALSE;
+                       else
+                               g_ptr_array_add (requests, message);
+               }
+
+               if (success)
+                       success = e_o365_connection_batch_request_sync (cnc, E_O365_API_V1_0, requests, 
cancellable, error);
+
+               g_ptr_array_free (requests, TRUE);
+       } else {
+               CamelMessageInfo *mi = mi_list->data;
+               JsonBuilder *builder;
+
+               builder = o365_folder_message_info_changes_to_json (mi);
+
+               success = e_o365_connection_update_mail_message_sync (cnc, NULL,
+                       camel_message_info_get_uid (mi), builder, cancellable, error);
+
+               g_clear_object (&builder);
+       }
+
+       g_object_unref (cnc);
+
+       if (success) {
+               GSList *link;
+
+               camel_folder_lock (folder);
+
+               for (link = mi_list; link; link = g_slist_next (link)) {
+                       CamelMessageInfo *mi = link->data;
+
+                       camel_message_info_set_folder_flagged (mi, FALSE);
+               }
+
+               camel_folder_unlock (folder);
+       }
+
+       return success;
+}
+
+static gboolean
+o365_folder_is_of_type (CamelFolder *folder,
+                       guint32 folder_type)
+{
+       CamelStore *parent_store;
+       CamelO365Store *o365_store;
+       CamelO365StoreSummary *o365_store_summary;
+       gboolean is_of_type;
+       const gchar *folder_id;
+
+       g_return_val_if_fail (folder != NULL, FALSE);
+
+       parent_store = camel_folder_get_parent_store (folder);
+
+       if (!parent_store)
+               return FALSE;
+
+       o365_store = CAMEL_O365_STORE (parent_store);
+
+       g_return_val_if_fail (o365_store != NULL, FALSE);
+
+       o365_store_summary = camel_o365_store_ref_store_summary (o365_store);
+
+       folder_type = folder_type & CAMEL_FOLDER_TYPE_MASK;
+       folder_id = camel_o365_folder_get_id (CAMEL_O365_FOLDER (folder));
+       is_of_type = folder_id &&
+               (camel_o365_store_summary_get_folder_flags (o365_store_summary, folder_id) & 
CAMEL_FOLDER_TYPE_MASK) == folder_type;
+
+       g_clear_object (&o365_store_summary);
+
+       return is_of_type;
+}
+
+static gboolean
+o365_folder_synchronize_sync (CamelFolder *folder,
+                             gboolean expunge,
+                             GCancellable *cancellable,
+                             GError **error)
+{
+       CamelO365Store *o365_store;
+       CamelStore *parent_store;
+       CamelFolderSummary *folder_summary;
+       GPtrArray *uids;
+       GSList *mi_list = NULL, *deleted_uids = NULL, *junk_uids = NULL, *inbox_uids = NULL;
+       gint mi_list_len = 0;
+       gboolean is_junk_folder;
+       gboolean success = TRUE;
+       guint ii;
+       GError *local_error = NULL;
+
+       parent_store = camel_folder_get_parent_store (folder);
+
+       if (!parent_store) {
+               g_set_error_literal (error, CAMEL_FOLDER_ERROR, CAMEL_FOLDER_ERROR_INVALID_STATE,
+                       _("Invalid folder state (missing parent store)"));
+               return FALSE;
+       }
+
+       o365_store = CAMEL_O365_STORE (parent_store);
+
+       if (!camel_o365_store_ensure_connected (o365_store, NULL, cancellable, error))
+               return FALSE;
+
+       folder_summary = camel_folder_get_folder_summary (folder);
+
+       if (camel_folder_summary_get_deleted_count (folder_summary) > 0 ||
+           camel_folder_summary_get_junk_count (folder_summary) > 0) {
+               camel_folder_summary_prepare_fetch_all (folder_summary, NULL);
+               uids = camel_folder_summary_get_array (folder_summary);
+       } else {
+               uids = camel_folder_summary_get_changed (folder_summary);
+       }
+
+       if (!uids || !uids->len) {
+               camel_folder_summary_free_array (uids);
+               return TRUE;
+       }
+
+       is_junk_folder = o365_folder_is_of_type (folder, CAMEL_FOLDER_TYPE_JUNK);
+
+       for (ii = 0; success && ii < uids->len; ii++) {
+               guint32 flags_changed, flags_set;
+               CamelMessageInfo *mi;
+               const gchar *uid;
+
+               uid = uids->pdata[ii];
+               mi = camel_folder_summary_get (folder_summary, uid);
+
+               if (!mi)
+                       continue;
+
+               flags_set = camel_message_info_get_flags (mi);
+               flags_changed = camel_o365_message_info_get_server_flags (CAMEL_O365_MESSAGE_INFO (mi)) ^ 
flags_set;
+
+               if ((flags_set & CAMEL_MESSAGE_FOLDER_FLAGGED) != 0 &&
+                   (flags_changed & (CAMEL_MESSAGE_SEEN | CAMEL_MESSAGE_ANSWERED | CAMEL_MESSAGE_FORWARDED | 
CAMEL_MESSAGE_FLAGGED)) != 0) {
+                       mi_list = g_slist_prepend (mi_list, mi);
+                       mi_list_len++;
+
+                       if (flags_set & CAMEL_MESSAGE_DELETED)
+                               deleted_uids = g_slist_prepend (deleted_uids, (gpointer) camel_pstring_strdup 
(uid));
+                       else if (flags_set & CAMEL_MESSAGE_JUNK)
+                               junk_uids = g_slist_prepend (junk_uids, (gpointer) camel_pstring_strdup 
(uid));
+                       else if (is_junk_folder && (flags_set & CAMEL_MESSAGE_NOTJUNK) != 0)
+                               inbox_uids = g_slist_prepend (inbox_uids, (gpointer) camel_pstring_strdup 
(uid));
+               } else if (flags_set & CAMEL_MESSAGE_DELETED) {
+                       deleted_uids = g_slist_prepend (deleted_uids, (gpointer) camel_pstring_strdup (uid));
+                       g_clear_object (&mi);
+               } else if (flags_set & CAMEL_MESSAGE_JUNK) {
+                       junk_uids = g_slist_prepend (junk_uids, (gpointer) camel_pstring_strdup (uid));
+                       g_clear_object (&mi);
+               } else if (is_junk_folder && (flags_set & CAMEL_MESSAGE_NOTJUNK) != 0) {
+                       inbox_uids = g_slist_prepend (inbox_uids, (gpointer) camel_pstring_strdup (uid));
+                       g_clear_object (&mi);
+               } else if ((flags_set & CAMEL_MESSAGE_FOLDER_FLAGGED) != 0) {
+                       /* OK, the change must have been the labels */
+                       mi_list = g_slist_prepend (mi_list, mi);
+                       mi_list_len++;
+               } else {
+                       g_clear_object (&mi);
+               }
+
+               if (mi_list_len == E_O365_BATCH_MAX_REQUESTS) {
+                       success = o365_folder_save_flags_sync (folder, o365_store, mi_list, cancellable, 
&local_error);
+                       g_slist_free_full (mi_list, g_object_unref);
+                       mi_list = NULL;
+                       mi_list_len = 0;
+               }
+       }
+
+       if (mi_list != NULL && success)
+               success = o365_folder_save_flags_sync (folder, o365_store, mi_list, cancellable, 
&local_error);
+       g_slist_free_full (mi_list, g_object_unref);
+
+       if (deleted_uids && success)
+               success = o365_folder_delete_messages_sync (folder, o365_store, deleted_uids, 
o365_folder_is_of_type (folder, CAMEL_FOLDER_TYPE_TRASH), cancellable, &local_error);
+       g_slist_free_full (deleted_uids, (GDestroyNotify) camel_pstring_free);
+
+       if (junk_uids && success)
+               success = o365_folder_copy_move_to_folder_sync (folder, o365_store, junk_uids, "junkemail", 
FALSE, cancellable, &local_error);
+       g_slist_free_full (junk_uids, (GDestroyNotify) camel_pstring_free);
+
+       if (inbox_uids && success)
+               success = o365_folder_copy_move_to_folder_sync (folder, o365_store, inbox_uids, "inbox", 
FALSE, cancellable, &local_error);
+       g_slist_free_full (inbox_uids, (GDestroyNotify) camel_pstring_free);
+
+       camel_folder_summary_save (folder_summary, NULL);
+       camel_folder_summary_free_array (uids);
+
+       if (local_error) {
+               camel_o365_store_maybe_disconnect (o365_store, local_error);
+               g_propagate_error (error, local_error);
+       }
+
+       return success;
+}
+
+static gboolean
+o365_folder_expunge_sync (CamelFolder *folder,
+                         GCancellable *cancellable,
+                         GError **error)
+{
+       /* it does nothing special here, everything is done as part of the o365_folder_synchronize_sync() */
+
+       return TRUE;
+}
+
+static gboolean
+o365_folder_transfer_messages_to_sync (CamelFolder *source,
+                                      GPtrArray *uids,
+                                      CamelFolder *destination,
+                                      gboolean delete_originals,
+                                      GPtrArray **transferred_uids,
+                                      GCancellable *cancellable,
+                                      GError **error)
+{
+       CamelStore *parent_store;
+       CamelO365Store *o365_store;
+       GSList *uids_list = NULL;
+       gboolean success;
+       guint ii;
+       GError *local_error = NULL;
+
+       /* The parent class ensures this, but recheck anyway, for completeness */
+       g_return_val_if_fail (CAMEL_IS_O365_FOLDER (source), FALSE);
+       g_return_val_if_fail (CAMEL_IS_O365_FOLDER (destination), FALSE);
+       g_return_val_if_fail (uids != NULL, FALSE);
+
+       parent_store = camel_folder_get_parent_store (source);
+
+       if (!parent_store) {
+               g_set_error_literal (error, CAMEL_FOLDER_ERROR, CAMEL_FOLDER_ERROR_INVALID_STATE,
+                       _("Invalid folder state (missing parent store)"));
+               return FALSE;
+       }
+
+       /* The parent class ensures this, but recheck anyway, for completeness */
+       g_return_val_if_fail (camel_folder_get_parent_store (destination) == parent_store, FALSE);
+
+       o365_store = CAMEL_O365_STORE (parent_store);
+
+       if (!camel_o365_store_ensure_connected (o365_store, NULL, cancellable, error))
+               return FALSE;
+
+       for (ii = 0; ii < uids->len; ii++) {
+               uids_list = g_slist_prepend (uids_list, g_ptr_array_index (uids, ii));
+       }
+
+       uids_list = g_slist_reverse (uids_list);
+
+       success = o365_folder_copy_move_to_folder_sync (source, o365_store,
+               uids_list, camel_o365_folder_get_id (CAMEL_O365_FOLDER (destination)),
+               !delete_originals, cancellable, &local_error);
+
+       g_slist_free (uids_list);
+
+       /* Update destination folder only if not frozen, to not update
+          for each single message transfer during filtering.
+        */
+       if (success && !camel_folder_is_frozen (destination)) {
+               camel_operation_progress (cancellable, -1);
+
+               o365_folder_refresh_info_sync (destination, cancellable, NULL);
+       }
+
+       if (local_error) {
+               camel_o365_store_maybe_disconnect (o365_store, local_error);
+               g_propagate_error (error, local_error);
+       }
+
+       return success;
+}
+
 static void
 o365_folder_prepare_content_refresh (CamelFolder *folder)
 {
@@ -1207,11 +1678,9 @@ camel_o365_folder_class_init (CamelO365FolderClass *klass)
        folder_class->append_message_sync = o365_folder_append_message_sync;
        folder_class->get_message_sync = o365_folder_get_message_sync;
        folder_class->refresh_info_sync = o365_folder_refresh_info_sync;
-#if 0
        folder_class->synchronize_sync = o365_folder_synchronize_sync;
        folder_class->expunge_sync = o365_folder_expunge_sync;
        folder_class->transfer_messages_to_sync = o365_folder_transfer_messages_to_sync;
-#endif
        folder_class->prepare_content_refresh = o365_folder_prepare_content_refresh;
        folder_class->get_filename = o365_folder_get_filename;
 }
diff --git a/src/Office365/camel/camel-o365-store.c b/src/Office365/camel/camel-o365-store.c
index c9b1ddf4..c54af794 100644
--- a/src/Office365/camel/camel-o365-store.c
+++ b/src/Office365/camel/camel-o365-store.c
@@ -1732,20 +1732,20 @@ camel_o365_store_init (CamelO365Store *o365_store)
 }
 
 CamelO365StoreSummary *
-camel_o365_store_ref_store_summary (CamelO365Store *store)
+camel_o365_store_ref_store_summary (CamelO365Store *o365_store)
 {
        CamelO365StoreSummary *summary;
 
-       g_return_val_if_fail (CAMEL_IS_O365_STORE (store), NULL);
+       g_return_val_if_fail (CAMEL_IS_O365_STORE (o365_store), NULL);
 
-       LOCK (store);
+       LOCK (o365_store);
 
-       summary = store->priv->summary;
+       summary = o365_store->priv->summary;
 
        if (summary)
                g_object_ref (summary);
 
-       UNLOCK (store);
+       UNLOCK (o365_store);
 
        return summary;
 }
@@ -1800,17 +1800,17 @@ camel_o365_store_ensure_connected (CamelO365Store *o365_store,
 }
 
 void
-camel_o365_store_maybe_disconnect (CamelO365Store *store,
+camel_o365_store_maybe_disconnect (CamelO365Store *o365_store,
                                   const GError *error)
 {
        CamelService *service;
 
-       g_return_if_fail (CAMEL_IS_O365_STORE (store));
+       g_return_if_fail (CAMEL_IS_O365_STORE (o365_store));
 
        if (!error)
                return;
 
-       service = CAMEL_SERVICE (store);
+       service = CAMEL_SERVICE (o365_store);
 
        if (camel_service_get_connection_status (service) != CAMEL_SERVICE_CONNECTED)
                return;
@@ -1823,16 +1823,16 @@ camel_o365_store_maybe_disconnect (CamelO365Store *store,
 }
 
 void
-camel_o365_store_connect_folder_summary (CamelO365Store *store,
+camel_o365_store_connect_folder_summary (CamelO365Store *o365_store,
                                         CamelFolderSummary *folder_summary)
 {
-       g_return_if_fail (CAMEL_IS_O365_STORE (store));
+       g_return_if_fail (CAMEL_IS_O365_STORE (o365_store));
        g_return_if_fail (CAMEL_IS_FOLDER_SUMMARY (folder_summary));
 
-       LOCK (store);
+       LOCK (o365_store);
 
-       if (store->priv->summary)
-               camel_o365_store_summary_connect_folder_summary (store->priv->summary, folder_summary);
+       if (o365_store->priv->summary)
+               camel_o365_store_summary_connect_folder_summary (o365_store->priv->summary, folder_summary);
 
-       UNLOCK (store);
+       UNLOCK (o365_store);
 }
diff --git a/src/Office365/camel/camel-o365-store.h b/src/Office365/camel/camel-o365-store.h
index eb6ad5d2..68e08013 100644
--- a/src/Office365/camel/camel-o365-store.h
+++ b/src/Office365/camel/camel-o365-store.h
@@ -61,19 +61,19 @@ GType               camel_o365_store_get_type       (void);
 
 CamelO365StoreSummary *
                camel_o365_store_ref_store_summary
-                                               (CamelO365Store *store);
+                                               (CamelO365Store *o365_store);
 EO365Connection *
                camel_o365_store_ref_connection (CamelO365Store *o365_store);
 gboolean       camel_o365_store_ensure_connected
-                                               (CamelO365Store *store,
-                                                EO365Connection **out_cnc, /* out, nullable, trasnfer full */
+                                               (CamelO365Store *o365_store,
+                                                EO365Connection **out_cnc, /* out, nullable, transfer full */
                                                 GCancellable *cancellable,
                                                 GError **error);
 void           camel_o365_store_maybe_disconnect
-                                               (CamelO365Store *store,
+                                               (CamelO365Store *o365_store,
                                                 const GError *error);
 void           camel_o365_store_connect_folder_summary
-                                               (CamelO365Store *store,
+                                               (CamelO365Store *o365_store,
                                                 CamelFolderSummary *folder_summary);
 
 G_END_DECLS
diff --git a/src/Office365/camel/camel-o365-utils.c b/src/Office365/camel/camel-o365-utils.c
index f2e9c585..20f51181 100644
--- a/src/Office365/camel/camel-o365-utils.c
+++ b/src/Office365/camel/camel-o365-utils.c
@@ -163,11 +163,11 @@ camel_o365_utils_rename_label (const gchar *cat,
        /* This is a mapping from Outlook categories to
         * Evolution labels based on the standard colors */
        const gchar *labels[] = {
-               "Red Category", "$Labelimportant",
-               "Orange Category", "$Labelwork",
-               "Green Category", "$Labelpersonal",
-               "Blue Category", "$Labeltodo",
-               "Purple Category", "$Labellater",
+               "Red category", "$Labelimportant",
+               "Orange category", "$Labelwork",
+               "Green category", "$Labelpersonal",
+               "Blue category", "$Labeltodo",
+               "Purple category", "$Labellater",
                NULL, NULL
        };
 
@@ -663,10 +663,10 @@ o365_utils_get_body_part (CamelMimeMessage *message,
        return body_part;
 }
 
-static void
-o365_utils_add_message_flags (JsonBuilder *builder,
-                             CamelMessageInfo *info,
-                             CamelMimeMessage *message)
+void
+camel_o365_utils_add_message_flags (JsonBuilder *builder,
+                                   CamelMessageInfo *info,
+                                   CamelMimeMessage *message)
 {
        guint32 flags = 0;
 
@@ -865,7 +865,7 @@ camel_o365_utils_create_message_sync (EO365Connection *cnc,
        }
 
        if (info || is_send)
-               o365_utils_add_message_flags (builder, info, is_send ? message : NULL);
+               camel_o365_utils_add_message_flags (builder, info, is_send ? message : NULL);
 
        e_o365_json_end_object_member (builder);
 
diff --git a/src/Office365/camel/camel-o365-utils.h b/src/Office365/camel/camel-o365-utils.h
index f719c2f0..3815c322 100644
--- a/src/Office365/camel/camel-o365-utils.h
+++ b/src/Office365/camel/camel-o365-utils.h
@@ -33,6 +33,10 @@ gboolean     camel_o365_utils_is_system_user_flag
                                                (const gchar *name);
 const gchar *  camel_o365_utils_rename_label   (const gchar *cat,
                                                 gboolean from_cat);
+void           camel_o365_utils_add_message_flags
+                                               (JsonBuilder *builder,
+                                                CamelMessageInfo *info,
+                                                CamelMimeMessage *message);
 gboolean       camel_o365_utils_create_message_sync
                                                (EO365Connection *cnc,
                                                 const gchar *folder_id,
diff --git a/src/Office365/common/e-o365-connection.c b/src/Office365/common/e-o365-connection.c
index f2627358..9ceb2486 100644
--- a/src/Office365/common/e-o365-connection.c
+++ b/src/Office365/common/e-o365-connection.c
@@ -32,6 +32,11 @@
 
 #define X_EVO_O365_DATA "X-EVO-O365-DATA"
 
+typedef enum _CSMFlags {
+       CSM_DEFAULT             = 0,
+       CSM_DISABLE_RESPONSE    = 1 << 0
+} CSMFlags;
+
 struct _EO365ConnectionPrivate {
        GRecMutex property_lock;
 
@@ -1457,6 +1462,7 @@ e_o365_read_json_object_response_cb (EO365Connection *cnc,
 static SoupMessage *
 o365_connection_new_soup_message (const gchar *method,
                                  const gchar *uri,
+                                 CSMFlags csm_flags,
                                  GError **error)
 {
        SoupMessage *message;
@@ -1469,6 +1475,9 @@ o365_connection_new_soup_message (const gchar *method,
        if (message) {
                soup_message_headers_append (message->request_headers, "Connection", "Close");
                soup_message_headers_append (message->request_headers, "User-Agent", "Evolution-O365/" 
VERSION);
+
+               if ((csm_flags & CSM_DISABLE_RESPONSE) != 0)
+                       soup_message_headers_append (message->request_headers, "Prefer", "return=minimal");
        } else {
                g_set_error (error, SOUP_HTTP_ERROR, SOUP_STATUS_MALFORMED, _("Malformed URI: ā€œ%sā€"), uri);
        }
@@ -1524,7 +1533,7 @@ e_o365_connection_authenticate_sync (EO365Connection *cnc,
                "$top", "1",
                NULL);
 
-       message = o365_connection_new_soup_message (SOUP_METHOD_GET, uri, error);
+       message = o365_connection_new_soup_message (SOUP_METHOD_GET, uri, CSM_DEFAULT, error);
 
        if (!message) {
                g_free (uri);
@@ -1880,7 +1889,7 @@ e_o365_connection_batch_request_internal_sync (EO365Connection *cnc,
        uri = e_o365_connection_construct_uri (cnc, FALSE, NULL, api_version, "",
                "$batch", NULL, NULL, NULL);
 
-       message = o365_connection_new_soup_message (SOUP_METHOD_POST, uri, error);
+       message = o365_connection_new_soup_message (SOUP_METHOD_POST, uri, CSM_DEFAULT, error);
 
        if (!message) {
                g_free (uri);
@@ -2123,7 +2132,7 @@ e_o365_connection_get_categories_sync (EO365Connection *cnc,
                NULL,
                NULL);
 
-       message = o365_connection_new_soup_message (SOUP_METHOD_GET, uri, error);
+       message = o365_connection_new_soup_message (SOUP_METHOD_GET, uri, CSM_DEFAULT, error);
 
        if (!message) {
                g_free (uri);
@@ -2170,7 +2179,7 @@ e_o365_connection_list_mail_folders_sync (EO365Connection *cnc,
                "$select", select,
                NULL);
 
-       message = o365_connection_new_soup_message (SOUP_METHOD_GET, uri, error);
+       message = o365_connection_new_soup_message (SOUP_METHOD_GET, uri, CSM_DEFAULT, error);
 
        if (!message) {
                g_free (uri);
@@ -2212,7 +2221,7 @@ e_o365_connection_get_mail_folders_delta_sync (EO365Connection *cnc,
        g_return_val_if_fail (func != NULL, FALSE);
 
        if (delta_link)
-               message = o365_connection_new_soup_message (SOUP_METHOD_GET, delta_link, NULL);
+               message = o365_connection_new_soup_message (SOUP_METHOD_GET, delta_link, CSM_DEFAULT, NULL);
 
        if (!message) {
                gchar *uri;
@@ -2224,7 +2233,7 @@ e_o365_connection_get_mail_folders_delta_sync (EO365Connection *cnc,
                        "$select", select,
                        NULL);
 
-               message = o365_connection_new_soup_message (SOUP_METHOD_GET, uri, error);
+               message = o365_connection_new_soup_message (SOUP_METHOD_GET, uri, CSM_DEFAULT, error);
 
                if (!message) {
                        g_free (uri);
@@ -2270,7 +2279,7 @@ e_o365_connection_create_mail_folder_sync (EO365Connection *cnc,
                                           GCancellable *cancellable,
                                           GError **error)
 {
-       SoupMessage *message = NULL;
+       SoupMessage *message;
        JsonBuilder *builder;
        gboolean success;
        gchar *uri;
@@ -2285,7 +2294,7 @@ e_o365_connection_create_mail_folder_sync (EO365Connection *cnc,
                parent_folder_id ? "childFolders" : NULL,
                NULL);
 
-       message = o365_connection_new_soup_message (SOUP_METHOD_POST, uri, error);
+       message = o365_connection_new_soup_message (SOUP_METHOD_POST, uri, CSM_DEFAULT, error);
 
        if (!message) {
                g_free (uri);
@@ -2321,7 +2330,7 @@ e_o365_connection_delete_mail_folder_sync (EO365Connection *cnc,
                                           GCancellable *cancellable,
                                           GError **error)
 {
-       SoupMessage *message = NULL;
+       SoupMessage *message;
        gboolean success;
        gchar *uri;
 
@@ -2331,7 +2340,7 @@ e_o365_connection_delete_mail_folder_sync (EO365Connection *cnc,
        uri = e_o365_connection_construct_uri (cnc, TRUE, user_override, E_O365_API_V1_0, NULL,
                "mailFolders", folder_id, NULL, NULL);
 
-       message = o365_connection_new_soup_message (SOUP_METHOD_DELETE, uri, error);
+       message = o365_connection_new_soup_message (SOUP_METHOD_DELETE, uri, CSM_DEFAULT, error);
 
        if (!message) {
                g_free (uri);
@@ -2361,7 +2370,7 @@ e_o365_connection_copy_move_mail_folder_sync (EO365Connection *cnc,
                                              GCancellable *cancellable,
                                              GError **error)
 {
-       SoupMessage *message = NULL;
+       SoupMessage *message;
        JsonBuilder *builder;
        gboolean success;
        gchar *uri;
@@ -2376,7 +2385,7 @@ e_o365_connection_copy_move_mail_folder_sync (EO365Connection *cnc,
                do_copy ? "copy" : "move",
                NULL);
 
-       message = o365_connection_new_soup_message (SOUP_METHOD_POST, uri, error);
+       message = o365_connection_new_soup_message (SOUP_METHOD_POST, uri, CSM_DEFAULT, error);
 
        if (!message) {
                g_free (uri);
@@ -2414,7 +2423,7 @@ e_o365_connection_rename_mail_folder_sync (EO365Connection *cnc,
                                           GCancellable *cancellable,
                                           GError **error)
 {
-       SoupMessage *message = NULL;
+       SoupMessage *message;
        JsonBuilder *builder;
        gboolean success;
        gchar *uri;
@@ -2429,7 +2438,7 @@ e_o365_connection_rename_mail_folder_sync (EO365Connection *cnc,
                NULL,
                NULL);
 
-       message = o365_connection_new_soup_message ("PATCH", uri, error);
+       message = o365_connection_new_soup_message ("PATCH", uri, CSM_DEFAULT, error);
 
        if (!message) {
                g_free (uri);
@@ -2481,7 +2490,7 @@ e_o365_connection_get_mail_messages_delta_sync (EO365Connection *cnc,
        g_return_val_if_fail (func != NULL, FALSE);
 
        if (delta_link)
-               message = o365_connection_new_soup_message (SOUP_METHOD_GET, delta_link, NULL);
+               message = o365_connection_new_soup_message (SOUP_METHOD_GET, delta_link, CSM_DEFAULT, NULL);
 
        if (!message) {
                gchar *uri;
@@ -2494,7 +2503,7 @@ e_o365_connection_get_mail_messages_delta_sync (EO365Connection *cnc,
                        "$select", select,
                        NULL);
 
-               message = o365_connection_new_soup_message (SOUP_METHOD_GET, uri, error);
+               message = o365_connection_new_soup_message (SOUP_METHOD_GET, uri, CSM_DEFAULT, error);
 
                if (!message) {
                        g_free (uri);
@@ -2540,7 +2549,7 @@ e_o365_connection_get_mail_message_sync (EO365Connection *cnc,
                                         GCancellable *cancellable,
                                         GError **error)
 {
-       SoupMessage *message = NULL;
+       SoupMessage *message;
        gboolean success;
        gchar *uri;
 
@@ -2557,7 +2566,7 @@ e_o365_connection_get_mail_message_sync (EO365Connection *cnc,
                "", "$value",
                NULL);
 
-       message = o365_connection_new_soup_message (SOUP_METHOD_GET, uri, error);
+       message = o365_connection_new_soup_message (SOUP_METHOD_GET, uri, CSM_DEFAULT, error);
 
        if (!message) {
                g_free (uri);
@@ -2585,7 +2594,7 @@ e_o365_connection_create_mail_message_sync (EO365Connection *cnc,
                                            GCancellable *cancellable,
                                            GError **error)
 {
-       SoupMessage *message = NULL;
+       SoupMessage *message;
        gboolean success;
        gchar *uri;
 
@@ -2599,7 +2608,7 @@ e_o365_connection_create_mail_message_sync (EO365Connection *cnc,
                folder_id ? "messages" : NULL,
                NULL);
 
-       message = o365_connection_new_soup_message (SOUP_METHOD_POST, uri, error);
+       message = o365_connection_new_soup_message (SOUP_METHOD_POST, uri, CSM_DEFAULT, error);
 
        if (!message) {
                g_free (uri);
@@ -2629,7 +2638,7 @@ e_o365_connection_add_mail_message_attachment_sync (EO365Connection *cnc,
                                                    GCancellable *cancellable,
                                                    GError **error)
 {
-       SoupMessage *message = NULL;
+       SoupMessage *message;
        JsonObject *added_attachment = NULL;
        gboolean success;
        gchar *uri;
@@ -2643,7 +2652,7 @@ e_o365_connection_add_mail_message_attachment_sync (EO365Connection *cnc,
                "attachments",
                NULL);
 
-       message = o365_connection_new_soup_message (SOUP_METHOD_POST, uri, error);
+       message = o365_connection_new_soup_message (SOUP_METHOD_POST, uri, CSM_DEFAULT, error);
 
        if (!message) {
                g_free (uri);
@@ -2667,3 +2676,359 @@ e_o365_connection_add_mail_message_attachment_sync (EO365Connection *cnc,
 
        return success;
 }
+
+/* https://docs.microsoft.com/en-us/graph/api/message-update?view=graph-rest-1.0&tabs=http */
+
+SoupMessage *
+e_o365_connection_prepare_update_mail_message (EO365Connection *cnc,
+                                              const gchar *user_override, /* for which user, NULL to use the 
account user */
+                                              const gchar *message_id,
+                                              JsonBuilder *mail_message, /* values to update, as a 
mailMessage object */
+                                              GError **error)
+{
+       SoupMessage *message;
+       gchar *uri;
+
+       g_return_val_if_fail (E_IS_O365_CONNECTION (cnc), NULL);
+       g_return_val_if_fail (message_id != NULL, NULL);
+       g_return_val_if_fail (mail_message != NULL, NULL);
+
+       uri = e_o365_connection_construct_uri (cnc, TRUE, user_override, E_O365_API_V1_0, NULL,
+               "messages",
+               message_id,
+               NULL,
+               NULL);
+
+       /* The server returns the mailMessage object back, but it can be ignored here */
+       message = o365_connection_new_soup_message ("PATCH", uri, CSM_DISABLE_RESPONSE, error);
+
+       if (!message) {
+               g_free (uri);
+
+               return NULL;
+       }
+
+       g_free (uri);
+
+       e_o365_connection_set_json_body (message, mail_message);
+
+       return message;
+}
+
+gboolean
+e_o365_connection_update_mail_message_sync (EO365Connection *cnc,
+                                           const gchar *user_override, /* for which user, NULL to use the 
account user */
+                                           const gchar *message_id,
+                                           JsonBuilder *mail_message, /* values to update, as a mailMessage 
object */
+                                           GCancellable *cancellable,
+                                           GError **error)
+{
+       SoupMessage *message;
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_O365_CONNECTION (cnc), FALSE);
+       g_return_val_if_fail (message_id != NULL, FALSE);
+       g_return_val_if_fail (mail_message != NULL, FALSE);
+
+       message = e_o365_connection_prepare_update_mail_message (cnc, user_override, message_id, 
mail_message, error);
+
+       if (!message)
+               return FALSE;
+
+       success = o365_connection_send_request_sync (cnc, message, NULL, e_o365_read_no_response_cb, NULL, 
cancellable, error);
+
+       g_clear_object (&message);
+
+       return success;
+}
+
+/* https://docs.microsoft.com/en-us/graph/api/message-copy?view=graph-rest-1.0&tabs=http
+   https://docs.microsoft.com/en-us/graph/api/message-move?view=graph-rest-1.0&tabs=http
+ */
+static SoupMessage *
+e_o365_connection_prepare_copy_move_mail_message (EO365Connection *cnc,
+                                                 const gchar *user_override,
+                                                 const gchar *message_id,
+                                                 const gchar *des_folder_id,
+                                                 gboolean do_copy,
+                                                 GError **error)
+{
+       SoupMessage *message;
+       JsonBuilder *builder;
+       gchar *uri;
+
+       g_return_val_if_fail (E_IS_O365_CONNECTION (cnc), NULL);
+       g_return_val_if_fail (message_id != NULL, NULL);
+
+       uri = e_o365_connection_construct_uri (cnc, TRUE, user_override, E_O365_API_V1_0, NULL,
+               "messages",
+               message_id,
+               do_copy ? "copy" : "move",
+               NULL);
+
+       message = o365_connection_new_soup_message (SOUP_METHOD_POST, uri, CSM_DEFAULT, error);
+
+       if (!message) {
+               g_free (uri);
+
+               return FALSE;
+       }
+
+       g_free (uri);
+
+       builder = json_builder_new_immutable ();
+
+       e_o365_json_begin_object_member (builder, NULL);
+       e_o365_json_add_string_member (builder, "destinationId", des_folder_id);
+       e_o365_json_end_object_member (builder);
+
+       e_o365_connection_set_json_body (message, builder);
+
+       g_object_unref (builder);
+
+       return message;
+}
+
+/* out_des_message_ids: Camel-pooled gchar *, new ids, in the same order as in message_ids; can be partial */
+gboolean
+e_o365_connection_copy_move_mail_messages_sync (EO365Connection *cnc,
+                                               const gchar *user_override, /* for which user, NULL to use 
the account user */
+                                               const GSList *message_ids, /* const gchar * */
+                                               const gchar *des_folder_id,
+                                               gboolean do_copy,
+                                               GSList **out_des_message_ids, /* Camel-pooled gchar * */
+                                               GCancellable *cancellable,
+                                               GError **error)
+{
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_O365_CONNECTION (cnc), FALSE);
+       g_return_val_if_fail (message_ids != NULL, FALSE);
+       g_return_val_if_fail (des_folder_id != NULL, FALSE);
+       g_return_val_if_fail (out_des_message_ids != NULL, FALSE);
+
+       *out_des_message_ids = NULL;
+
+       if (g_slist_next (message_ids)) {
+               GPtrArray *requests;
+               GSList *link;
+               guint total, done = 0;
+
+               total = g_slist_length ((GSList *) message_ids);
+               requests = g_ptr_array_new_full (MIN (E_O365_BATCH_MAX_REQUESTS, 50), g_object_unref);
+
+               for (link = (GSList *) message_ids; link && success; link = g_slist_next (link)) {
+                       const gchar *id = link->data;
+                       SoupMessage *message;
+
+                       message = e_o365_connection_prepare_copy_move_mail_message (cnc, user_override, id, 
des_folder_id, do_copy, error);
+
+                       if (!message) {
+                               success = FALSE;
+                               break;
+                       }
+
+                       g_ptr_array_add (requests, message);
+
+                       if (requests->len == E_O365_BATCH_MAX_REQUESTS || !link->next) {
+                               if (requests->len == 1) {
+                                       JsonObject *response = NULL;
+
+                                       success = o365_connection_send_request_sync (cnc, message, 
e_o365_read_json_object_response_cb, NULL, &response, cancellable, error);
+
+                                       if (response) {
+                                               *out_des_message_ids = g_slist_prepend (*out_des_message_ids,
+                                                       (gpointer) camel_pstring_strdup 
(e_o365_mail_message_get_id (response)));
+                                               json_object_unref (response);
+                                       } else {
+                                               success = FALSE;
+                                       }
+                               } else {
+                                       success = e_o365_connection_batch_request_sync (cnc, E_O365_API_V1_0, 
requests, cancellable, error);
+
+                                       if (success) {
+                                               guint ii;
+
+                                               for (ii = 0; success && ii < requests->len; ii++) {
+                                                       JsonNode *node = NULL;
+
+                                                       message = g_ptr_array_index (requests, ii);
+
+                                                       success = e_o365_connection_json_node_from_message 
(message, NULL, &node, cancellable, error);
+
+                                                       if (success && node && JSON_NODE_HOLDS_OBJECT (node)) 
{
+                                                               JsonObject *response;
+
+                                                               response = json_node_get_object (node);
+
+                                                               if (response) {
+                                                                       *out_des_message_ids = 
g_slist_prepend (*out_des_message_ids,
+                                                                               (gpointer) 
camel_pstring_strdup (e_o365_mail_message_get_id (response)));
+                                                               } else {
+                                                                       success = FALSE;
+                                                               }
+                                                       } else {
+                                                               success = FALSE;
+                                                       }
+
+                                                       if (node)
+                                                               json_node_unref (node);
+                                               }
+                                       }
+                               }
+
+                               g_ptr_array_remove_range (requests, 0, requests->len);
+
+                               done += requests->len;
+
+                               camel_operation_progress (cancellable, done * 100.0 / total);
+                       }
+               }
+
+               g_ptr_array_free (requests, TRUE);
+       } else {
+               SoupMessage *message;
+
+               message = e_o365_connection_prepare_copy_move_mail_message (cnc, user_override, 
message_ids->data, des_folder_id, do_copy, error);
+
+               if (message) {
+                       JsonObject *response = NULL;
+
+                       success = o365_connection_send_request_sync (cnc, message, 
e_o365_read_json_object_response_cb, NULL, &response, cancellable, error);
+
+                       if (response) {
+                               *out_des_message_ids = g_slist_prepend (*out_des_message_ids,
+                                       (gpointer) camel_pstring_strdup (e_o365_mail_message_get_id 
(response)));
+                               json_object_unref (response);
+                       } else {
+                               success = FALSE;
+                       }
+
+                       g_clear_object (&message);
+               } else {
+                       success = FALSE;
+               }
+       }
+
+       *out_des_message_ids = g_slist_reverse (*out_des_message_ids);
+
+       return success;
+}
+
+/* https://docs.microsoft.com/en-us/graph/api/message-delete?view=graph-rest-1.0&tabs=http */
+
+static SoupMessage *
+e_o365_connection_prepare_delete_mail_message (EO365Connection *cnc,
+                                              const gchar *user_override, /* for which user, NULL to use the 
account user */
+                                              const gchar *message_id,
+                                              GError **error)
+{
+       SoupMessage *message;
+       gchar *uri;
+
+       g_return_val_if_fail (E_IS_O365_CONNECTION (cnc), NULL);
+       g_return_val_if_fail (message_id != NULL, NULL);
+
+       uri = e_o365_connection_construct_uri (cnc, TRUE, user_override, E_O365_API_V1_0, NULL,
+               "messages",
+               message_id,
+               NULL,
+               NULL);
+
+       message = o365_connection_new_soup_message (SOUP_METHOD_DELETE, uri, CSM_DEFAULT, error);
+
+       if (!message) {
+               g_free (uri);
+
+               return NULL;
+       }
+
+       g_free (uri);
+
+       return message;
+}
+
+gboolean
+e_o365_connection_delete_mail_messages_sync (EO365Connection *cnc,
+                                            const gchar *user_override, /* for which user, NULL to use the 
account user */
+                                            const GSList *message_ids, /* const gchar * */
+                                            GSList **out_deleted_ids, /* (transfer container): const gchar 
*, borrowed from message_ids */
+                                            GCancellable *cancellable,
+                                            GError **error)
+{
+       gboolean success;
+
+       g_return_val_if_fail (E_IS_O365_CONNECTION (cnc), FALSE);
+       g_return_val_if_fail (message_ids != NULL, FALSE);
+
+       if (g_slist_next (message_ids)) {
+               GPtrArray *requests;
+               GSList *link, *from_link = (GSList *) message_ids;
+               guint total, done = 0;
+
+               total = g_slist_length ((GSList *) message_ids);
+               requests = g_ptr_array_new_full (MIN (E_O365_BATCH_MAX_REQUESTS, 50), g_object_unref);
+
+               for (link = (GSList *) message_ids; link && success; link = g_slist_next (link)) {
+                       const gchar *id = link->data;
+                       SoupMessage *message;
+
+                       message = e_o365_connection_prepare_delete_mail_message (cnc, user_override, id, 
error);
+
+                       if (!message) {
+                               success = FALSE;
+                               break;
+                       }
+
+                       g_ptr_array_add (requests, message);
+
+                       if (requests->len == E_O365_BATCH_MAX_REQUESTS || !link->next) {
+                               if (requests->len == 1) {
+                                       success = o365_connection_send_request_sync (cnc, message, NULL, 
e_o365_read_no_response_cb, NULL, cancellable, error);
+                               } else {
+                                       success = e_o365_connection_batch_request_sync (cnc, E_O365_API_V1_0, 
requests, cancellable, error);
+                               }
+
+                               if (success && out_deleted_ids) {
+                                       while (from_link) {
+                                               *out_deleted_ids = g_slist_prepend (*out_deleted_ids, 
from_link->data);
+
+                                               if (from_link == link)
+                                                       break;
+
+                                               from_link = g_slist_next (from_link);
+                                       }
+                               }
+
+                               g_ptr_array_remove_range (requests, 0, requests->len);
+                               from_link = g_slist_next (link);
+
+                               done += requests->len;
+
+                               camel_operation_progress (cancellable, done * 100.0 / total);
+                       }
+               }
+
+               g_ptr_array_free (requests, TRUE);
+       } else {
+               SoupMessage *message;
+
+               message = e_o365_connection_prepare_delete_mail_message (cnc, user_override, 
message_ids->data, error);
+
+               if (message) {
+                       success = o365_connection_send_request_sync (cnc, message, NULL, 
e_o365_read_no_response_cb, NULL, cancellable, error);
+
+                       if (success && out_deleted_ids)
+                               *out_deleted_ids = g_slist_prepend (*out_deleted_ids, message_ids->data);
+
+                       g_clear_object (&message);
+               } else {
+                       success = FALSE;
+               }
+       }
+
+       if (out_deleted_ids && *out_deleted_ids && g_slist_next (*out_deleted_ids))
+               *out_deleted_ids = g_slist_reverse (*out_deleted_ids);
+
+       return success;
+}
diff --git a/src/Office365/common/e-o365-connection.h b/src/Office365/common/e-o365-connection.h
index 1ab45af2..f8af1cfd 100644
--- a/src/Office365/common/e-o365-connection.h
+++ b/src/Office365/common/e-o365-connection.h
@@ -252,6 +252,35 @@ gboolean   e_o365_connection_add_mail_message_attachment_sync
                                                 gchar **out_attachment_id,
                                                 GCancellable *cancellable,
                                                 GError **error);
+SoupMessage *  e_o365_connection_prepare_update_mail_message
+                                               (EO365Connection *cnc,
+                                                const gchar *user_override, /* for which user, NULL to use 
the account user */
+                                                const gchar *message_id,
+                                                JsonBuilder *mail_message, /* values to update, as a 
mailMessage object */
+                                                GError **error);
+gboolean       e_o365_connection_update_mail_message_sync
+                                               (EO365Connection *cnc,
+                                                const gchar *user_override, /* for which user, NULL to use 
the account user */
+                                                const gchar *message_id,
+                                                JsonBuilder *mail_message, /* values to update, as a 
mailMessage object */
+                                                GCancellable *cancellable,
+                                                GError **error);
+gboolean       e_o365_connection_copy_move_mail_messages_sync
+                                               (EO365Connection *cnc,
+                                                const gchar *user_override, /* for which user, NULL to use 
the account user */
+                                                const GSList *message_ids, /* const gchar * */
+                                                const gchar *des_folder_id,
+                                                gboolean do_copy,
+                                                GSList **out_des_message_ids, /* Camel-pooled gchar *, can 
be partial */
+                                                GCancellable *cancellable,
+                                                GError **error);
+gboolean       e_o365_connection_delete_mail_messages_sync
+                                               (EO365Connection *cnc,
+                                                const gchar *user_override, /* for which user, NULL to use 
the account user */
+                                                const GSList *message_ids, /* const gchar * */
+                                                GSList **out_deleted_ids, /* (transfer container): const 
gchar *, borrowed from message_ids */
+                                                GCancellable *cancellable,
+                                                GError **error);
 
 G_END_DECLS
 



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