[evolution-ews/wip/mcrha/office365] Mail message creation, but with disabled generic append to a folder



commit 0d1b403d40e5f9a378943c872e15b4424e97b171
Author: Milan Crha <mcrha redhat com>
Date:   Wed Jul 1 18:55:07 2020 +0200

    Mail message creation, but with disabled generic append to a folder

 config.h.in                                       |   3 +
 src/Office365/camel/camel-o365-folder.c           | 312 ++++++---
 src/Office365/camel/camel-o365-folder.h           |   1 +
 src/Office365/camel/camel-o365-store-summary.c    |  32 +
 src/Office365/camel/camel-o365-store-summary.h    |   1 +
 src/Office365/camel/camel-o365-store.c            |  60 +-
 src/Office365/camel/camel-o365-utils.c            | 741 ++++++++++++++++++++++
 src/Office365/camel/camel-o365-utils.h            |  12 +-
 src/Office365/common/e-o365-connection.c          | 153 +++--
 src/Office365/common/e-o365-connection.h          |  16 +
 src/Office365/common/e-o365-json-utils.c          | 610 +++++++++++++++++-
 src/Office365/common/e-o365-json-utils.h          | 164 ++++-
 src/Office365/common/e-oauth2-service-office365.c |   1 -
 13 files changed, 1930 insertions(+), 176 deletions(-)
---
diff --git a/config.h.in b/config.h.in
index ad3f60c7..d755ed53 100644
--- a/config.h.in
+++ b/config.h.in
@@ -12,6 +12,9 @@
 /* Package name for gettext */
 #define GETTEXT_PACKAGE "@GETTEXT_PACKAGE@"
 
+/* Configured with enabled maintainer mode */
+#cmakedefine ENABLE_MAINTAINER_MODE 1
+
 /* Define to add support for low level tests */
 #cmakedefine ENABLE_TESTS 1
 
diff --git a/src/Office365/camel/camel-o365-folder.c b/src/Office365/camel/camel-o365-folder.c
index 231fac41..c5ad7a25 100644
--- a/src/Office365/camel/camel-o365-folder.c
+++ b/src/Office365/camel/camel-o365-folder.c
@@ -58,6 +58,8 @@
 #define UNLOCK_SEARCH(_folder) g_mutex_unlock (&_folder->priv->search_lock)
 
 struct _CamelO365FolderPrivate {
+       gchar *id; /* folder ID; stays the same for the full life of the folder */
+
        GRecMutex cache_lock;
        CamelDataCache *cache;
 
@@ -217,6 +219,49 @@ o365_folder_save_summary (CamelO365Folder *o365_folder)
        }
 }
 
+static void
+o365_folder_forget_all_mails (CamelO365Folder *o365_folder)
+{
+       CamelFolder *folder;
+       CamelFolderChangeInfo *changes;
+       CamelFolderSummary *folder_summary;
+       GPtrArray *known_uids;
+       gint ii;
+
+       g_return_if_fail (CAMEL_IS_O365_FOLDER (o365_folder));
+
+       folder = CAMEL_FOLDER (o365_folder);
+       g_return_if_fail (folder != NULL);
+
+       known_uids = camel_folder_summary_get_array (camel_folder_get_folder_summary (folder));
+
+       if (!known_uids)
+               return;
+
+       changes = camel_folder_change_info_new ();
+       folder_summary = camel_folder_get_folder_summary (folder);
+
+       camel_folder_summary_lock (folder_summary);
+
+       for (ii = 0; ii < known_uids->len; ii++) {
+               const gchar *uid = g_ptr_array_index (known_uids, ii);
+
+               camel_folder_change_info_remove_uid (changes, uid);
+               o365_folder_cache_remove (o365_folder, uid, NULL);
+       }
+
+       camel_folder_summary_clear (folder_summary, NULL);
+       camel_folder_summary_unlock (folder_summary);
+
+       o365_folder_save_summary (o365_folder);
+
+       if (camel_folder_change_info_changed (changes))
+               camel_folder_changed (folder, changes);
+
+       camel_folder_change_info_free (changes);
+       camel_folder_summary_free_array (known_uids);
+}
+
 static guint32
 o365_folder_get_permanent_flags (CamelFolder *folder)
 {
@@ -440,12 +485,11 @@ o365_folder_get_message_sync (CamelFolder *folder,
        CamelMimeMessage *message = NULL;
        CamelO365Folder *o365_folder;
        CamelO365Store *o365_store;
-       CamelO365StoreSummary *o365_store_summary;
        CamelStore *parent_store;
        CamelStream *cache_stream = NULL;
        EO365Connection *cnc = NULL;
        GError *local_error = NULL;
-       gchar *folder_id;
+       const gchar *folder_id;
        gboolean success = TRUE, remove_from_hash = FALSE;
 
        g_return_val_if_fail (CAMEL_IS_O365_FOLDER (folder), NULL);
@@ -453,8 +497,11 @@ o365_folder_get_message_sync (CamelFolder *folder,
 
        parent_store = camel_folder_get_parent_store (folder);
 
-       if (!parent_store)
+       if (!parent_store) {
+               g_set_error_literal (error, CAMEL_FOLDER_ERROR, CAMEL_FOLDER_ERROR_INVALID_STATE,
+                       _("Invalid folder state (missing parent store)"));
                return NULL;
+       }
 
        o365_folder = CAMEL_O365_FOLDER (folder);
        o365_store = CAMEL_O365_STORE (parent_store);
@@ -462,20 +509,7 @@ o365_folder_get_message_sync (CamelFolder *folder,
        if (!camel_o365_store_ensure_connected (o365_store, &cnc, cancellable, error))
                return NULL;
 
-       o365_store_summary = camel_o365_store_ref_store_summary (o365_store);
-
-       folder_id = camel_o365_store_summary_dup_folder_id_for_full_name (o365_store_summary,
-               camel_folder_get_full_name (folder));
-
-       if (!folder_id) {
-               g_set_error (error, CAMEL_STORE_ERROR, CAMEL_STORE_ERROR_NO_FOLDER, _("No such folder: %s"),
-                       camel_folder_get_full_name (folder));
-
-               g_clear_object (&o365_store_summary);
-               g_clear_object (&cnc);
-
-               return NULL;
-       }
+       folder_id = camel_o365_folder_get_id (o365_folder);
 
        g_mutex_lock (&o365_folder->priv->get_message_lock);
 
@@ -530,10 +564,8 @@ o365_folder_get_message_sync (CamelFolder *folder,
                }
        }
 
-       g_clear_object (&o365_store_summary);
        g_clear_object (&cache_stream);
        g_clear_object (&cnc);
-       g_free (folder_id);
 
        if (remove_from_hash) {
                g_mutex_lock (&o365_folder->priv->get_message_lock);
@@ -546,13 +578,72 @@ o365_folder_get_message_sync (CamelFolder *folder,
 }
 
 static gboolean
-o365_folder_is_system_user_flag (const gchar *name)
+o365_folder_append_message_sync (CamelFolder *folder,
+                                CamelMimeMessage *message,
+                                CamelMessageInfo *info,
+                                gchar **appended_uid,
+                                GCancellable *cancellable,
+                                GError **error)
 {
-       if (!name)
+       /* Cannot put existing messages from other providers, because:
+          1) those are always set as drafts
+          2) the set sentDateTime property is not respected
+          3) internetMessageHeaders is limited to 5! headers only:
+             {
+               "error": {
+                       "code": "InvalidInternetMessageHeaderCollection",
+                       "message": "Maximum number of headers in one message should be less than or equal to 
5.",
+                       "innerError": {
+                               "date": "2020-07-01T10:03:34",
+                               "request-id": "a46da0ea-8933-43c6-932d-7c751f226516"
+                       }
+               }
+             }
+          4) there are likely to be more limitations on the graph API, not spotted yet.
+
+          There is opened a feture request, which may eventually fix this, but it's currently not done yet 
(as of 2020-07-01):
+          
https://microsoftgraph.uservoice.com/forums/920506-microsoft-graph-feature-requests/suggestions/35049175-put-edit-mime-email-content-with-microsoft-graph
+
+          Thus just error out for now.
+       */
+
+#ifdef ENABLE_MAINTAINER_MODE /* Only for easier testing */
+       CamelStore *parent_store;
+       CamelO365Store *o365_store;
+       EO365Connection *cnc = NULL;
+       gboolean success;
+       GError *local_error = NULL;
+
+       parent_store = camel_folder_get_parent_store (folder);
+
+       if (!CAMEL_IS_O365_STORE (parent_store)) {
+               g_set_error_literal (error, CAMEL_FOLDER_ERROR, CAMEL_FOLDER_ERROR_INVALID_STATE,
+                       _("Invalid folder state (missing parent store)"));
                return FALSE;
+       }
 
-       return g_str_equal (name, "receipt-handled") ||
-               g_str_equal (name, "$has-cal");
+       o365_store = CAMEL_O365_STORE (parent_store);
+
+       if (!camel_o365_store_ensure_connected (o365_store, &cnc, cancellable, error))
+               return FALSE;
+
+       success = camel_o365_utils_create_message_sync (cnc, camel_o365_folder_get_id (CAMEL_O365_FOLDER 
(folder)),
+               message, info, FALSE, appended_uid, cancellable, &local_error);
+
+       g_clear_object (&cnc);
+
+       if (!success)
+               camel_o365_store_maybe_disconnect (o365_store, local_error);
+
+       if (local_error)
+               g_propagate_error (error, local_error);
+
+       return success;
+#else
+       g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED,
+               _("Cannot add messages into an Office 365 account from another account. Only messages from 
the same account can be moved/copied between the Office 365 folders."));
+       return FALSE;
+#endif
 }
 
 static gboolean
@@ -577,11 +668,10 @@ o365_folder_merge_server_user_flags (CamelMessageInfo *mi,
        user_flags = camel_message_info_get_user_flags (mi);
        len = camel_named_flags_get_length (user_flags);
 
-       /* transfer camel flags to a list */
        for (ii = 0; ii < len; ii++) {
                const gchar *name = camel_named_flags_get (user_flags, ii);
 
-               if (!o365_folder_is_system_user_flag (name))
+               if (!camel_o365_utils_is_system_user_flag (name))
                        g_hash_table_insert (current_labels, (gpointer) name, NULL);
        }
 
@@ -720,6 +810,10 @@ o365_folder_new_message_info_from_mail_message (CamelFolder *folder,
        CamelMessageInfo *mi = NULL;
        CamelNameValueArray *headers = NULL;
        JsonArray *json_headers;
+       EO365Recipient *from;
+       const gchar *ctmp;
+       time_t tt;
+       gchar *tmp;
 
        g_return_val_if_fail (CAMEL_IS_FOLDER (folder), NULL);
        g_return_val_if_fail (mail != NULL, NULL);
@@ -750,89 +844,80 @@ o365_folder_new_message_info_from_mail_message (CamelFolder *folder,
                }
        }
 
-       if (!mi) {
-               EO365Recipient *from;
-               const gchar *ctmp;
-               time_t tt;
-               gchar *tmp;
-
+       if (!mi)
                mi = camel_message_info_new (camel_folder_get_folder_summary (folder));
 
-               camel_message_info_set_abort_notifications (mi, TRUE);
+       camel_message_info_set_abort_notifications (mi, TRUE);
 
-               ctmp = e_o365_mail_message_get_subject (mail);
+       ctmp = e_o365_mail_message_get_subject (mail);
 
-               if (ctmp)
-                       camel_message_info_set_subject  (mi, ctmp);
+       if (ctmp)
+               camel_message_info_set_subject  (mi, ctmp);
 
-               from = e_o365_mail_message_get_from (mail);
+       from = e_o365_mail_message_get_from (mail);
 
-               if (from) {
-                       const gchar *name, *address;
+       if (from) {
+               const gchar *name, *address;
 
-                       name = e_o365_recipient_get_name (from);
-                       address = e_o365_recipient_get_address (from);
+               name = e_o365_recipient_get_name (from);
+               address = e_o365_recipient_get_address (from);
 
-                       if (address && *address) {
-                               tmp = camel_internet_address_format_address (name, address);
+               if (address && *address) {
+                       tmp = camel_internet_address_format_address (name, address);
 
-                               if (tmp) {
-                                       camel_message_info_set_from (mi, tmp);
+                       if (tmp) {
+                               camel_message_info_set_from (mi, tmp);
 
-                                       g_free (tmp);
-                               }
+                               g_free (tmp);
                        }
                }
+       }
 
-               tmp = o365_folder_recipients_as_string (e_o365_mail_message_get_to_recipients (mail));
+       tmp = o365_folder_recipients_as_string (e_o365_mail_message_get_to_recipients (mail));
 
-               if (tmp) {
-                       camel_message_info_set_to (mi, tmp);
-                       g_free (tmp);
-               }
+       if (tmp) {
+               camel_message_info_set_to (mi, tmp);
+               g_free (tmp);
+       }
 
-               tmp = o365_folder_recipients_as_string (e_o365_mail_message_get_cc_recipients (mail));
+       tmp = o365_folder_recipients_as_string (e_o365_mail_message_get_cc_recipients (mail));
 
-               if (tmp) {
-                       camel_message_info_set_cc (mi, tmp);
-                       g_free (tmp);
-               }
+       if (tmp) {
+               camel_message_info_set_cc (mi, tmp);
+               g_free (tmp);
+       }
 
-               tt = e_o365_mail_message_get_sent_date_time (mail);
+       tt = e_o365_mail_message_get_sent_date_time (mail);
 
-               if (tt)
-                       camel_message_info_set_date_sent (mi, (gint64) tt);
+       if (tt)
+               camel_message_info_set_date_sent (mi, (gint64) tt);
 
-               tt = e_o365_mail_message_get_received_date_time (mail);
+       tt = e_o365_mail_message_get_received_date_time (mail);
 
-               if (tt)
-                       camel_message_info_set_date_received (mi, (gint64) tt);
+       if (tt)
+               camel_message_info_set_date_received (mi, (gint64) tt);
 
-               ctmp = e_o365_mail_message_get_internet_message_id (mail);
+       ctmp = e_o365_mail_message_get_internet_message_id (mail);
 
-               if (ctmp && *ctmp) {
-                       GChecksum *checksum;
-                       CamelSummaryMessageID message_id;
-                       guint8 *digest;
-                       gsize length;
+       if (ctmp && *ctmp) {
+               GChecksum *checksum;
+               CamelSummaryMessageID message_id;
+               guint8 *digest;
+               gsize length;
 
-                       length = g_checksum_type_get_length (G_CHECKSUM_MD5);
-                       digest = g_alloca (length);
+               length = g_checksum_type_get_length (G_CHECKSUM_MD5);
+               digest = g_alloca (length);
 
-                       checksum = g_checksum_new (G_CHECKSUM_MD5);
-                       g_checksum_update (checksum, (const guchar *) ctmp, -1);
-                       g_checksum_get_digest (checksum, digest, &length);
-                       g_checksum_free (checksum);
+               checksum = g_checksum_new (G_CHECKSUM_MD5);
+               g_checksum_update (checksum, (const guchar *) ctmp, -1);
+               g_checksum_get_digest (checksum, digest, &length);
+               g_checksum_free (checksum);
 
-                       memcpy (message_id.id.hash, digest, sizeof (message_id.id.hash));
+               memcpy (message_id.id.hash, digest, sizeof (message_id.id.hash));
 
-                       camel_message_info_set_message_id (mi, message_id.id.id);
-               }
-
-               camel_message_info_set_abort_notifications (mi, FALSE);
+               camel_message_info_set_message_id (mi, message_id.id.id);
        }
 
-       camel_message_info_set_abort_notifications (mi, TRUE);
        camel_message_info_set_uid (mi, e_o365_mail_message_get_id (mail));
 
        if (headers)
@@ -925,21 +1010,24 @@ o365_folder_refresh_info_sync (CamelFolder *folder,
        CamelO365Folder *o365_folder;
        CamelO365FolderSummary *o365_folder_summary;
        CamelO365Store *o365_store;
-       CamelO365StoreSummary *o365_store_summary;
        CamelFolderSummary *folder_summary;
        CamelStore *parent_store;
        EO365Connection *cnc = NULL;
        SummaryDeltaData sdd;
        GError *local_error = NULL;
-       gchar *folder_id, *curr_delta_link, *new_delta_link = NULL;
+       const gchar *folder_id;
+       gchar *curr_delta_link, *new_delta_link = NULL;
        gboolean success;
 
        g_return_val_if_fail (CAMEL_IS_O365_FOLDER (folder), FALSE);
 
        parent_store = camel_folder_get_parent_store (folder);
 
-       if (!parent_store)
+       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_folder = CAMEL_O365_FOLDER (folder);
        o365_store = CAMEL_O365_STORE (parent_store);
@@ -947,21 +1035,7 @@ o365_folder_refresh_info_sync (CamelFolder *folder,
        if (!camel_o365_store_ensure_connected (o365_store, &cnc, cancellable, error))
                return FALSE;
 
-       o365_store_summary = camel_o365_store_ref_store_summary (o365_store);
-
-       folder_id = camel_o365_store_summary_dup_folder_id_for_full_name (o365_store_summary,
-               camel_folder_get_full_name (folder));
-
-       if (!folder_id) {
-               g_set_error (error, CAMEL_STORE_ERROR, CAMEL_STORE_ERROR_NO_FOLDER, _("No such folder: %s"),
-                       camel_folder_get_full_name (folder));
-
-               g_clear_object (&o365_store_summary);
-               g_clear_object (&cnc);
-
-               return FALSE;
-       }
-
+       folder_id = camel_o365_folder_get_id (o365_folder);
        folder_summary = camel_folder_get_folder_summary (folder);
        o365_folder_summary = CAMEL_O365_FOLDER_SUMMARY (folder_summary);
 
@@ -975,6 +1049,19 @@ o365_folder_refresh_info_sync (CamelFolder *folder,
                curr_delta_link, 0, o365_folder_got_summary_messages_cb, &sdd,
                &new_delta_link, cancellable, &local_error);
 
+       if (curr_delta_link && g_error_matches (local_error, SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED)) {
+               g_clear_error (&local_error);
+               g_clear_pointer (&curr_delta_link, g_free);
+
+               camel_o365_folder_summary_set_delta_link (o365_folder_summary, NULL);
+
+               o365_folder_forget_all_mails (o365_folder);
+
+               success = e_o365_connection_get_mail_messages_delta_sync (cnc, NULL, folder_id, 
O365_FETCH_SUMMARY_PROPERTIES,
+                       NULL, 0, o365_folder_got_summary_messages_cb, &sdd,
+                       &new_delta_link, cancellable, &local_error);
+       }
+
        if (success && new_delta_link)
                camel_o365_folder_summary_set_delta_link (o365_folder_summary, new_delta_link);
 
@@ -1000,11 +1087,9 @@ o365_folder_refresh_info_sync (CamelFolder *folder,
                success = FALSE;
        }
 
-       g_clear_object (&o365_store_summary);
        g_clear_object (&cnc);
        g_free (curr_delta_link);
        g_free (new_delta_link);
-       g_free (folder_id);
 
        return success;
 }
@@ -1094,6 +1179,8 @@ o365_folder_finalize (GObject *object)
 
        g_hash_table_destroy (o365_folder->priv->get_message_hash);
 
+       g_clear_pointer (&o365_folder->priv->id, g_free);
+
        /* Chain up to parent's method. */
        G_OBJECT_CLASS (camel_o365_folder_parent_class)->finalize (object);
 }
@@ -1117,9 +1204,7 @@ camel_o365_folder_class_init (CamelO365FolderClass *klass)
        folder_class->search_by_uids = o365_folder_search_by_uids;
        folder_class->search_free = o365_folder_search_free;
        folder_class->cmp_uids = o365_folder_cmp_uids;
-#if 0
        folder_class->append_message_sync = o365_folder_append_message_sync;
-#endif
        folder_class->get_message_sync = o365_folder_get_message_sync;
        folder_class->refresh_info_sync = o365_folder_refresh_info_sync;
 #if 0
@@ -1182,6 +1267,7 @@ camel_o365_folder_new (CamelStore *store,
        CamelFolder *folder;
        CamelFolderSummary *folder_summary;
        CamelO365Folder *o365_folder;
+       CamelO365StoreSummary *o365_store_summary;
        CamelSettings *settings;
        gboolean filter_inbox = FALSE;
        gboolean filter_junk = FALSE;
@@ -1191,6 +1277,17 @@ camel_o365_folder_new (CamelStore *store,
        gint offline_limit_value = 0;
        guint32 add_folder_flags = 0;
        gchar *state_file;
+       gchar *folder_id;
+
+       o365_store_summary = camel_o365_store_ref_store_summary (CAMEL_O365_STORE (store));
+       folder_id = camel_o365_store_summary_dup_folder_id_for_full_name (o365_store_summary, full_name);
+       g_clear_object (&o365_store_summary);
+
+       if (!folder_id) {
+               g_set_error (error, CAMEL_FOLDER_ERROR, CAMEL_FOLDER_ERROR_INVALID_PATH,
+                       _("Folder ā€œ%sā€ doesn't correspond to any known folder"), full_name);
+               return NULL;
+       }
 
        folder = g_object_new (CAMEL_TYPE_O365_FOLDER,
                "display_name", display_name,
@@ -1199,6 +1296,7 @@ camel_o365_folder_new (CamelStore *store,
                NULL);
 
        o365_folder = CAMEL_O365_FOLDER (folder);
+       o365_folder->priv->id = folder_id;
 
        folder_summary = camel_o365_folder_summary_new (folder);
 
@@ -1206,7 +1304,7 @@ camel_o365_folder_new (CamelStore *store,
                g_object_unref (folder);
                g_set_error (
                        error, CAMEL_ERROR, CAMEL_ERROR_GENERIC,
-                       _("Could not load summary for %s"), full_name);
+                       _("Could not load summary for ā€œ%sā€"), full_name);
                return NULL;
        }
 
@@ -1281,3 +1379,11 @@ camel_o365_folder_new (CamelStore *store,
 
        return folder;
 }
+
+const gchar *
+camel_o365_folder_get_id (CamelO365Folder *o365_folder)
+{
+       g_return_val_if_fail (CAMEL_IS_O365_FOLDER (o365_folder), NULL);
+
+       return o365_folder->priv->id;
+}
diff --git a/src/Office365/camel/camel-o365-folder.h b/src/Office365/camel/camel-o365-folder.h
index d189e72d..40ce51ee 100644
--- a/src/Office365/camel/camel-o365-folder.h
+++ b/src/Office365/camel/camel-o365-folder.h
@@ -64,6 +64,7 @@ CamelFolder * camel_o365_folder_new           (CamelStore *store,
                                                 const gchar *folder_dir,
                                                 GCancellable *cancellable,
                                                 GError **error);
+const gchar *  camel_o365_folder_get_id        (CamelO365Folder *o365_folder);
 
 G_END_DECLS
 
diff --git a/src/Office365/camel/camel-o365-store-summary.c b/src/Office365/camel/camel-o365-store-summary.c
index b02271bd..0428c707 100644
--- a/src/Office365/camel/camel-o365-store-summary.c
+++ b/src/Office365/camel/camel-o365-store-summary.c
@@ -1111,6 +1111,38 @@ camel_o365_store_summary_get_folder_is_public (CamelO365StoreSummary *store_summ
        return value;
 }
 
+GSList * /* gchar * */
+camel_o365_store_summary_list_folder_ids (CamelO365StoreSummary *store_summary)
+{
+       GSList *ids = NULL;
+       gchar **groups;
+       gint ii;
+
+       g_return_val_if_fail (CAMEL_IS_O365_STORE_SUMMARY (store_summary), NULL);
+
+       LOCK (store_summary);
+
+       groups = g_key_file_get_groups (store_summary->priv->key_file, NULL);
+
+       for (ii = 0; groups[ii]; ii++) {
+               gchar *group = groups[ii];
+
+               if (g_ascii_strcasecmp (group, STORE_GROUP_NAME) != 0 &&
+                   g_key_file_has_key (store_summary->priv->key_file, group, "DisplayName", NULL)) {
+                       ids = g_slist_prepend (ids, group);
+               } else {
+                       g_free (group);
+               }
+       }
+
+       UNLOCK (store_summary);
+
+       /* The array items are moved into the 'ids' GSList or freed */
+       g_free (groups);
+
+       return ids;
+}
+
 CamelFolderInfo *
 camel_o365_store_summary_build_folder_info_for_id (CamelO365StoreSummary *store_summary,
                                                   const gchar *id)
diff --git a/src/Office365/camel/camel-o365-store-summary.h b/src/Office365/camel/camel-o365-store-summary.h
index c4f3bfb3..a051b256 100644
--- a/src/Office365/camel/camel-o365-store-summary.h
+++ b/src/Office365/camel/camel-o365-store-summary.h
@@ -163,6 +163,7 @@ gboolean    camel_o365_store_summary_get_folder_is_foreign
 gboolean       camel_o365_store_summary_get_folder_is_public
                                                        (CamelO365StoreSummary *store_summary,
                                                         const gchar *id);
+GSList *       camel_o365_store_summary_list_folder_ids(CamelO365StoreSummary *store_summary); /* gchar 
*folder_id */
 CamelFolderInfo *
                camel_o365_store_summary_build_folder_info_for_id
                                                        (CamelO365StoreSummary *store_summary,
diff --git a/src/Office365/camel/camel-o365-store.c b/src/Office365/camel/camel-o365-store.c
index ee1474b4..c9b1ddf4 100644
--- a/src/Office365/camel/camel-o365-store.c
+++ b/src/Office365/camel/camel-o365-store.c
@@ -1176,12 +1176,44 @@ camel_o365_got_folders_delta_cb (EO365Connection *cnc,
        return TRUE;
 }
 
+static void
+o365_store_forget_all_folders (CamelO365Store *o365_store)
+{
+       CamelStore *store;
+       CamelSubscribable *subscribable;
+       GSList *ids, *link;
+
+       g_return_if_fail (CAMEL_IS_O365_STORE (o365_store));
+
+       store = CAMEL_STORE (o365_store);
+       subscribable = CAMEL_SUBSCRIBABLE (o365_store);
+       ids = camel_o365_store_summary_list_folder_ids (o365_store->priv->summary);
+
+       if (!ids)
+               return;
+
+       for (link = ids; link; link = g_slist_next (link)) {
+               const gchar *id = link->data;
+               CamelFolderInfo *fi;
+
+               fi = camel_o365_store_summary_build_folder_info_for_id (o365_store->priv->summary, id);
+               camel_subscribable_folder_unsubscribed (subscribable, fi);
+               camel_store_folder_deleted (store, fi);
+               camel_folder_info_free (fi);
+       }
+
+       g_slist_free_full (ids, g_free);
+
+       camel_o365_store_summary_set_delta_link (o365_store->priv->summary, "");
+       camel_o365_store_summary_clear (o365_store->priv->summary);
+}
+
 static CamelFolderInfo *
-o365_get_folder_info_sync (CamelStore *store,
-                          const gchar *top,
-                          guint32 flags,
-                          GCancellable *cancellable,
-                          GError **error)
+o365_store_get_folder_info_sync (CamelStore *store,
+                                const gchar *top,
+                                guint32 flags,
+                                GCancellable *cancellable,
+                                GError **error)
 {
        CamelO365Store *o365_store;
        CamelFolderInfo *fi;
@@ -1217,6 +1249,7 @@ o365_get_folder_info_sync (CamelStore *store,
                        if (cnc) {
                                FoldersDeltaData fdd;
                                gchar *old_delta_link, *new_delta_link = NULL;
+                               GError *local_error = NULL;
 
                                LOCK (o365_store);
 
@@ -1230,7 +1263,20 @@ o365_get_folder_info_sync (CamelStore *store,
                                fdd.removed_fis = NULL;
 
                                success = e_o365_connection_get_mail_folders_delta_sync (cnc, NULL, NULL, 
old_delta_link, 0,
-                                       camel_o365_got_folders_delta_cb, &fdd, &new_delta_link, cancellable, 
error);
+                                       camel_o365_got_folders_delta_cb, &fdd, &new_delta_link, cancellable, 
&local_error);
+
+                               if (old_delta_link && *old_delta_link && g_error_matches (local_error, 
SOUP_HTTP_ERROR, SOUP_STATUS_UNAUTHORIZED)) {
+                                       g_clear_pointer (&old_delta_link, g_free);
+                                       g_clear_error (&local_error);
+
+                                       o365_store_forget_all_folders (o365_store);
+
+                                       success = e_o365_connection_get_mail_folders_delta_sync (cnc, NULL, 
NULL, NULL, 0,
+                                               camel_o365_got_folders_delta_cb, &fdd, &new_delta_link, 
cancellable, error);
+                               }
+
+                               if (local_error)
+                                       g_propagate_error (error, local_error);
 
                                if (success) {
                                        CamelSubscribable *subscribable = CAMEL_SUBSCRIBABLE (o365_store);
@@ -1653,7 +1699,7 @@ camel_o365_store_class_init (CamelO365StoreClass *class)
        store_class->create_folder_sync = o365_store_create_folder_sync;
        store_class->delete_folder_sync = o365_store_delete_folder_sync;
        store_class->rename_folder_sync = o365_store_rename_folder_sync;
-       store_class->get_folder_info_sync = o365_get_folder_info_sync;
+       store_class->get_folder_info_sync = o365_store_get_folder_info_sync;
        store_class->initial_setup_sync = o365_store_initial_setup_sync;
        store_class->get_trash_folder_sync = o365_store_get_trash_folder_sync;
        store_class->get_junk_folder_sync = o365_store_get_junk_folder_sync;
diff --git a/src/Office365/camel/camel-o365-utils.c b/src/Office365/camel/camel-o365-utils.c
index fa6f11bb..76e86faa 100644
--- a/src/Office365/camel/camel-o365-utils.c
+++ b/src/Office365/camel/camel-o365-utils.c
@@ -144,6 +144,16 @@ camel_o365_utils_decode_category_name (const gchar *flag)
        return g_strdup (flag);
 }
 
+gboolean
+camel_o365_utils_is_system_user_flag (const gchar *name)
+{
+       if (!name)
+               return FALSE;
+
+       return g_str_equal (name, "receipt-handled") ||
+               g_str_equal (name, "$has-cal");
+}
+
 const gchar *
 camel_o365_utils_rename_label (const gchar *cat,
                               gboolean from_cat)
@@ -176,3 +186,734 @@ camel_o365_utils_rename_label (const gchar *cat,
 
        return cat;
 }
+
+static void
+o365_utils_add_address (JsonBuilder *builder,
+                       CamelInternetAddress *addr,
+                       void (* add_func) (JsonBuilder *builder,
+                                          const gchar *name,
+                                          const gchar *address))
+{
+       const gchar *name = NULL, *address = NULL;
+
+       g_return_if_fail (add_func != NULL);
+
+       if (!addr || camel_address_length (CAMEL_ADDRESS (addr)) < 1)
+               return;
+
+       if (camel_internet_address_get (addr, 0, &name, &address))
+               add_func (builder, name, address);
+}
+
+static void
+o365_utils_add_address_array (JsonBuilder *builder,
+                             CamelInternetAddress *addr,
+                             void (* begin_func) (JsonBuilder *builder),
+                             void (* end_func) (JsonBuilder *builder))
+{
+       gint ii, len;
+       gboolean did_add = FALSE;
+
+       g_return_if_fail (begin_func != NULL);
+       g_return_if_fail (end_func != NULL);
+
+       if (!addr)
+               return;
+
+       len = camel_address_length (CAMEL_ADDRESS (addr));
+
+       for (ii = 0; ii < len; ii++) {
+               const gchar *name = NULL, *address = NULL;
+
+               if (camel_internet_address_get (addr, 0, &name, &address)) {
+                       if (!did_add) {
+                               did_add = TRUE;
+                               begin_func (builder);
+                       }
+
+                       e_o365_add_recipient (builder, NULL, name, address);
+               }
+       }
+
+       if (did_add)
+               end_func (builder);
+}
+
+static void
+o365_utils_add_headers (JsonBuilder *builder,
+                       const CamelNameValueArray *headers,
+                       CamelInternetAddress **out_sender,
+                       gboolean *out_request_read_receipt)
+{
+       guint ii, len;
+       gint did_add = 0;
+
+       if (!headers)
+               return;
+
+       len = camel_name_value_array_get_length (headers);
+
+       for (ii = 0; ii < len; ii++) {
+               const gchar *name = NULL, *value = NULL;
+
+               if (camel_name_value_array_get (headers, ii, &name, &value) && name && *name && value &&
+                   g_ascii_strcasecmp (name, "X-Evolution-Source") != 0) {
+                       /* The Graph API allows only X- headers to be saved */
+                       if (g_ascii_strncasecmp (name, "X-", 2) == 0) {
+                               if (!did_add)
+                                       e_o365_mail_message_begin_internet_message_headers (builder);
+
+                               did_add++;
+
+                               /* Preserve only the first five... (see the comment at 
o365_folder_append_message_sync()) */
+                               if (did_add < 5)
+                                       e_o365_add_internet_message_header (builder, name, value);
+                       }
+
+                       if (out_sender && g_ascii_strcasecmp (name, "Sender") == 0) {
+                               CamelInternetAddress *addr;
+
+                               addr = camel_internet_address_new ();
+
+                               if (camel_address_decode (CAMEL_ADDRESS (addr), value) != -1) {
+                                       *out_sender = addr;
+                                       addr = NULL;
+                               }
+
+                               g_clear_object (&addr);
+
+                               /* To not compare the header name again */
+                               out_sender = NULL;
+                       } else if (out_request_read_receipt && g_ascii_strcasecmp (name, 
"Disposition-Notification-To") == 0) {
+                               *out_request_read_receipt = TRUE;
+
+                               /* To not compare the header name again */
+                               out_request_read_receipt = NULL;
+                       }
+               }
+       }
+
+       if (did_add)
+               e_o365_mail_message_end_internet_message_headers (builder);
+}
+
+static CamelStream *
+o365_utils_get_content_stream (CamelMimePart *part,
+                              gssize *out_wrote_bytes,
+                              GCancellable *cancellable)
+{
+       CamelStream *content_stream;
+       CamelStream *filter_stream = NULL;
+       CamelMimeFilterWindows *windows = NULL;
+       CamelDataWrapper *dw;
+       gssize wrote_bytes;
+
+       g_return_val_if_fail (part != NULL, NULL);
+
+       dw = camel_medium_get_content (CAMEL_MEDIUM (part));
+       g_return_val_if_fail (dw != NULL, NULL);
+
+       content_stream = camel_stream_mem_new ();
+
+       if (camel_mime_part_get_content_type (part)) {
+               const gchar *charset = camel_content_type_param (camel_mime_part_get_content_type (part), 
"charset");
+
+               if (charset && *charset && g_ascii_strcasecmp (charset, "utf8") != 0 && g_ascii_strcasecmp 
(charset, "utf-8") != 0) {
+                       if (g_ascii_strncasecmp (charset, "iso-8859-", 9) == 0) {
+                               CamelStream *null;
+
+                               /* Since a few Windows mailers like to claim they sent
+                                * out iso-8859-# encoded text when they really sent
+                                * out windows-cp125#, do some simple sanity checking
+                                * before we move on... */
+
+                               null = camel_stream_null_new ();
+                               filter_stream = camel_stream_filter_new (null);
+                               g_object_unref (null);
+
+                               windows = (CamelMimeFilterWindows *)camel_mime_filter_windows_new (charset);
+                               camel_stream_filter_add (CAMEL_STREAM_FILTER (filter_stream), 
CAMEL_MIME_FILTER (windows));
+
+                               camel_data_wrapper_decode_to_stream_sync (dw, CAMEL_STREAM (filter_stream), 
cancellable, NULL);
+                               camel_stream_flush (CAMEL_STREAM (filter_stream), cancellable, NULL);
+                               g_object_unref (filter_stream);
+
+                               charset = camel_mime_filter_windows_real_charset (windows);
+                       }
+
+                       if (charset && *charset) {
+                               CamelMimeFilter *filter;
+
+                               filter_stream = camel_stream_filter_new (content_stream);
+
+                               if ((filter = camel_mime_filter_charset_new (charset, "UTF-8"))) {
+                                       camel_stream_filter_add (CAMEL_STREAM_FILTER (filter_stream), 
CAMEL_MIME_FILTER (filter));
+                                       g_object_unref (filter);
+                               } else {
+                                       g_object_unref (filter_stream);
+                                       filter_stream = NULL;
+                               }
+                       }
+               }
+       }
+
+       if (filter_stream) {
+               wrote_bytes = camel_data_wrapper_decode_to_stream_sync (dw, CAMEL_STREAM (filter_stream), 
cancellable, NULL);
+               camel_stream_flush (filter_stream, cancellable, NULL);
+               g_object_unref (filter_stream);
+       } else {
+               wrote_bytes = camel_data_wrapper_decode_to_stream_sync (dw, CAMEL_STREAM (content_stream), 
cancellable, NULL);
+       }
+
+       if (windows)
+               g_object_unref (windows);
+
+       g_seekable_seek (G_SEEKABLE (content_stream), 0, G_SEEK_SET, NULL, NULL);
+
+       if (out_wrote_bytes)
+               *out_wrote_bytes = wrote_bytes;
+
+       return content_stream;
+}
+
+static gboolean
+o365_utils_part_is_attachment (CamelMimePart *part,
+                              gboolean *out_is_inline)
+{
+       const CamelContentDisposition *content_disposition;
+
+       g_return_val_if_fail (CAMEL_IS_MIME_PART (part), FALSE);
+
+       content_disposition = camel_mime_part_get_content_disposition (part);
+
+       if (!content_disposition)
+               return FALSE;
+
+       if (out_is_inline) {
+               *out_is_inline = content_disposition && content_disposition->disposition &&
+                               g_ascii_strcasecmp (content_disposition->disposition, "inline") == 0;
+       }
+
+       return content_disposition &&
+               content_disposition->disposition && (
+               g_ascii_strcasecmp (content_disposition->disposition, "attachment") == 0 ||
+               g_ascii_strcasecmp (content_disposition->disposition, "inline") == 0);
+}
+
+enum {
+       ADD_ATTACHMENT_WITH_CONTENT_TYPE = 1 << 0,
+       ADD_ATTACHMENT_PREFIX_CONTENT_TYPE_HEADER = 1 << 1,
+       ADD_ATTACHMENT_DECODE_CONTENT = 1 << 2
+};
+
+static void
+o365_utils_add_file_attachment_content (JsonBuilder *builder,
+                                       CamelDataWrapper *dw,
+                                       guint32 add_flags,
+                                       GCancellable *cancellable)
+{
+       CamelMimeFilter *filter;
+       CamelStream *content_stream, *filter_stream;
+       GByteArray *data;
+       CamelContentType *ct;
+       gchar *content_type_str;
+       const gchar *content_id;
+       gboolean is_inline = FALSE;
+
+       ct = camel_data_wrapper_get_mime_type_field (dw);
+       content_type_str = camel_content_type_format (ct);
+
+       if ((add_flags & ADD_ATTACHMENT_WITH_CONTENT_TYPE) != 0)
+               e_o365_attachment_add_content_type (builder, content_type_str);
+
+       content_stream = camel_stream_mem_new ();
+       filter_stream = camel_stream_filter_new (content_stream);
+
+       filter = camel_mime_filter_basic_new (CAMEL_MIME_FILTER_BASIC_BASE64_ENC);
+       camel_stream_filter_add (CAMEL_STREAM_FILTER (filter_stream), filter);
+       g_object_unref (filter);
+
+       if ((add_flags & ADD_ATTACHMENT_PREFIX_CONTENT_TYPE_HEADER) != 0) {
+               gchar *content_type_unfolded;
+
+               content_type_unfolded = camel_header_unfold (content_type_str);
+
+               #define wstr(str) camel_stream_write (filter_stream, str, strlen (str), cancellable, NULL)
+               wstr ("Content-Type: ");
+               wstr (content_type_unfolded);
+               wstr ("\r\n\r\n");
+               #undef wstr
+
+               g_free (content_type_unfolded);
+       }
+
+       g_free (content_type_str);
+
+       if (CAMEL_IS_MIME_PART (dw)) {
+               CamelMimePart *part = CAMEL_MIME_PART (dw);
+
+               content_id = camel_mime_part_get_content_id (part);
+               if (content_id)
+                       e_o365_file_attachment_add_content_id (builder, content_id);
+
+               if (o365_utils_part_is_attachment (part, &is_inline) && is_inline)
+                       e_o365_attachment_add_is_inline (builder, TRUE);
+
+               dw = camel_medium_get_content (CAMEL_MEDIUM (part));
+       }
+
+       if ((add_flags & ADD_ATTACHMENT_DECODE_CONTENT) != 0)
+               camel_data_wrapper_decode_to_stream_sync (dw, filter_stream, cancellable, NULL);
+       else
+               camel_data_wrapper_write_to_stream_sync (dw, filter_stream, cancellable, NULL);
+       camel_stream_flush (filter_stream, cancellable, NULL);
+       g_object_unref (filter_stream);
+
+       camel_stream_flush (content_stream, cancellable, NULL);
+
+       data = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (content_stream));
+
+       /* Ensure the string is NUL-terminated */
+       g_byte_array_append (data, (const guchar *) "\0", 1);
+
+       e_o365_file_attachment_add_content_bytes (builder, (const gchar *) data->data);
+
+       g_object_unref (content_stream);
+}
+
+static void
+o365_utils_add_file_attachment (JsonBuilder *builder,
+                               CamelDataWrapper *dw,
+                               GCancellable *cancellable)
+{
+       const gchar *filename = NULL;
+
+       g_return_if_fail (builder != NULL);
+       g_return_if_fail (dw != NULL);
+
+       o365_utils_add_file_attachment_content (builder, dw, ADD_ATTACHMENT_WITH_CONTENT_TYPE | 
ADD_ATTACHMENT_DECODE_CONTENT, cancellable);
+
+       if (CAMEL_IS_MIME_PART (dw))
+               filename = camel_mime_part_get_filename (CAMEL_MIME_PART (dw));
+
+       if (filename)
+               e_o365_attachment_add_name (builder, filename);
+       else
+               e_o365_attachment_add_name (builder, "attachment.dat");
+}
+
+static void
+o365_utils_add_smime_encrypted_attachment (JsonBuilder *builder,
+                                          CamelDataWrapper *dw,
+                                          GCancellable *cancellable)
+{
+       g_return_if_fail (builder != NULL);
+       g_return_if_fail (dw != NULL);
+
+       e_o365_attachment_add_name (builder, "smime.p7m");
+
+       o365_utils_add_file_attachment_content (builder, dw, ADD_ATTACHMENT_WITH_CONTENT_TYPE | 
ADD_ATTACHMENT_DECODE_CONTENT, cancellable);
+}
+
+static void
+o365_utils_add_smime_signed_attachment (JsonBuilder *builder,
+                                       CamelDataWrapper *dw,
+                                       GCancellable *cancellable)
+{
+       e_o365_attachment_add_content_type (builder, "multipart/signed");
+       e_o365_attachment_add_name (builder, "smime.txt");
+
+       o365_utils_add_file_attachment_content (builder, dw, ADD_ATTACHMENT_PREFIX_CONTENT_TYPE_HEADER, 
cancellable);
+}
+
+static gboolean
+o365_utils_do_smime_signed (CamelMultipart *multipart,
+                           CamelMimePart **out_body_part,
+                           GSList **out_attachments,
+                           GCancellable *cancellable)
+{
+       CamelMimePart *content, *signature;
+       /*CamelContentType *ct;*/
+
+       content = camel_multipart_get_part (multipart, CAMEL_MULTIPART_SIGNED_CONTENT);
+       signature = camel_multipart_get_part (multipart, CAMEL_MULTIPART_SIGNED_SIGNATURE);
+
+       g_return_val_if_fail (content != NULL, FALSE);
+       g_return_val_if_fail (signature != NULL, FALSE);
+
+       /*ct = camel_mime_part_get_content_type (content);
+
+       if (camel_content_type_is (ct, "text", "plain")) {
+               g_clear_object (out_body_part);
+               *out_body_part = g_object_ref (content);
+       } else if (camel_content_type_is (ct, "text", "html")) {
+               g_clear_object (out_body_part);
+               *out_body_part = g_object_ref (content);
+       } else {
+               *out_attachments = g_slist_prepend (*out_attachments, g_object_ref (content));
+       }*/
+
+       *out_attachments = g_slist_prepend (*out_attachments, g_object_ref (multipart));
+
+       return TRUE;
+}
+
+static gboolean
+o365_utils_do_multipart (CamelMultipart *mp,
+                        gboolean *is_first,
+                        CamelMimePart **out_body_part,
+                        GSList **out_attachments,
+                        GCancellable *cancellable)
+{
+       CamelDataWrapper *dw;
+       CamelContentType *type;
+       CamelMimePart *part;
+       gboolean parent_is_alternative;
+       gint nn, ii;
+
+       g_return_val_if_fail (is_first != NULL, FALSE);
+
+       type = camel_data_wrapper_get_mime_type_field (CAMEL_DATA_WRAPPER (mp));
+       parent_is_alternative = type && camel_content_type_is (type, "multipart", "alternative");
+
+       nn = camel_multipart_get_number (mp);
+
+       for (ii = 0; ii < nn; ii++) {
+               part = camel_multipart_get_part (mp, ii);
+
+               if (!part)
+                       continue;
+
+               dw = camel_medium_get_content (CAMEL_MEDIUM (part));
+
+               if (CAMEL_IS_MULTIPART (dw)) {
+                       if (!o365_utils_do_multipart (CAMEL_MULTIPART (dw), is_first, out_body_part, 
out_attachments, cancellable))
+                               return FALSE;
+                       continue;
+               }
+
+               type = camel_mime_part_get_content_type (part);
+
+               if (ii == 0 && (*is_first) && camel_content_type_is (type, "text", "plain")) {
+                       g_clear_object (out_body_part);
+                       *out_body_part = g_object_ref (part);
+
+                       *is_first = FALSE;
+               } else if ((ii == 0 || parent_is_alternative) &&
+                          camel_content_type_is (type, "text", "html") &&
+                          !o365_utils_part_is_attachment (part, NULL)) {
+                       g_clear_object (out_body_part);
+                       *out_body_part = g_object_ref (part);
+               } else {
+                       *out_attachments = g_slist_prepend (*out_attachments, g_object_ref (part));
+               }
+       }
+
+       return TRUE;
+}
+
+static CamelMimePart *
+o365_utils_get_body_part (CamelMimeMessage *message,
+                         GSList **out_attachments,
+                         GCancellable *cancellable)
+{
+       CamelContentType *ct;
+       CamelMimePart *body_part = NULL;
+
+       ct = camel_data_wrapper_get_mime_type_field (CAMEL_DATA_WRAPPER (message));
+       g_return_val_if_fail (ct != NULL, NULL);
+
+       if (camel_content_type_is (ct, "application", "x-pkcs7-mime") ||
+           camel_content_type_is (ct, "application", "pkcs7-mime")) {
+               *out_attachments = g_slist_prepend (*out_attachments, g_object_ref (message));
+       } else {
+               CamelDataWrapper *dw = NULL;
+               CamelMultipart *multipart;
+
+               /* contents body */
+               dw = camel_medium_get_content (CAMEL_MEDIUM (message));
+
+               if (CAMEL_IS_MULTIPART (dw)) {
+                       gboolean is_first = TRUE;
+
+                       multipart = CAMEL_MULTIPART (dw);
+                       if (CAMEL_IS_MULTIPART_SIGNED (multipart) && camel_multipart_get_number (multipart) 
== 2) {
+                               o365_utils_do_smime_signed (multipart, &body_part, out_attachments, 
cancellable);
+                       } else {
+                               o365_utils_do_multipart (multipart, &is_first, &body_part, out_attachments, 
cancellable);
+                       }
+               } else if (dw) {
+                       CamelContentType *type;
+                       CamelMimePart *part = CAMEL_MIME_PART (message);
+
+                       type = camel_data_wrapper_get_mime_type_field (dw);
+
+                       if (camel_content_type_is (type, "text", "plain")) {
+                               body_part = g_object_ref (part);
+                       } else if (camel_content_type_is (type, "text", "html")) {
+                               body_part = g_object_ref (part);
+                       } else {
+                               *out_attachments = g_slist_prepend (*out_attachments, g_object_ref (part));
+                       }
+               }
+       }
+
+       *out_attachments = g_slist_reverse (*out_attachments);
+
+       return body_part;
+}
+
+static void
+o365_utils_add_message_flags (JsonBuilder *builder,
+                             CamelMessageInfo *info,
+                             CamelMimeMessage *message)
+{
+       guint32 flags = 0;
+
+       if (info) {
+               const CamelNamedFlags *user_flags;
+               gboolean did_add = FALSE;
+               guint ii, len;
+
+               flags = camel_message_info_get_flags (info);
+
+               user_flags = camel_message_info_get_user_flags (info);
+               len = camel_named_flags_get_length (user_flags);
+
+               for (ii = 0; ii < len; ii++) {
+                       const gchar *name = camel_named_flags_get (user_flags, ii);
+
+                       if (!camel_o365_utils_is_system_user_flag (name)) {
+                               const gchar *renamed;
+
+                               renamed = camel_o365_utils_rename_label (name, FALSE);
+
+                               if (renamed && *renamed && renamed != name) {
+                                       if (!did_add) {
+                                               did_add = TRUE;
+                                               e_o365_mail_message_begin_categories (builder);
+                                       }
+
+                                       e_o365_mail_message_add_category (builder, renamed);
+                               } else if (renamed == name && name && *name) {
+                                       gchar *cat;
+
+                                       cat = camel_o365_utils_decode_category_name (name);
+
+                                       if (cat && *cat) {
+                                               if (!did_add) {
+                                                       did_add = TRUE;
+                                                       e_o365_mail_message_begin_categories (builder);
+                                               }
+
+                                               e_o365_mail_message_add_category (builder, cat);
+                                       }
+
+                                       g_free (cat);
+                               }
+                       }
+               }
+
+               if (did_add)
+                       e_o365_mail_message_end_categories (builder);
+       }
+
+       if (message && !(flags & CAMEL_MESSAGE_FLAGGED)) {
+               CamelMedium *medium = CAMEL_MEDIUM (message);
+               const gchar *value;
+
+               value = camel_medium_get_header (medium, "X-Priority");
+
+               if (g_strcmp0 (value, "1") == 0) {
+                       flags |= CAMEL_MESSAGE_FLAGGED;
+               } else {
+                       value = camel_medium_get_header (medium, "Importance");
+
+                       if (value && g_ascii_strcasecmp (value, "High") == 0)
+                               flags |= CAMEL_MESSAGE_FLAGGED;
+               }
+       }
+
+       e_o365_mail_message_add_importance (builder,
+               (flags & CAMEL_MESSAGE_FLAGGED) != 0 ? E_O365_IMPORTANCE_HIGH : E_O365_IMPORTANCE_NORMAL);
+
+       e_o365_mail_message_add_is_read (builder, (flags & CAMEL_MESSAGE_SEEN) != 0);
+}
+
+gboolean
+camel_o365_utils_create_message_sync (EO365Connection *cnc,
+                                     const gchar *folder_id,
+                                     CamelMimeMessage *message,
+                                     CamelMessageInfo *info,
+                                     gboolean is_send,
+                                     gchar **out_appended_uid,
+                                     GCancellable *cancellable,
+                                     GError **error)
+{
+       JsonBuilder *builder;
+       EO365MailMessage *appended_message = NULL;
+       CamelInternetAddress *addr, *sender = NULL;
+       CamelMimePart *body_part;
+       GSList *attachments = NULL;
+       time_t tt;
+       gint offset = 0;
+       const gchar *tmp;
+       gboolean success, request_read_receipt = FALSE;
+
+       g_return_val_if_fail (E_IS_O365_CONNECTION (cnc), FALSE);
+       g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
+
+       builder = json_builder_new_immutable ();
+
+       e_o365_json_begin_object_member (builder, NULL);
+
+       tmp = camel_mime_message_get_message_id (message);
+       if (tmp && *tmp)
+               e_o365_mail_message_add_internet_message_id (builder, tmp);
+
+       tmp = camel_mime_message_get_subject (message);
+       e_o365_mail_message_add_subject (builder, tmp ? tmp : "");
+
+       tt = camel_mime_message_get_date (message, &offset);
+
+       if (tt > (time_t) 0) {
+               /* Convert to UTC */
+               tt += (offset / 100) * 60 * 60;
+               tt += (offset % 100) * 60;
+
+               e_o365_mail_message_add_sent_date_time (builder, tt);
+       }
+
+       offset = 0;
+       tt = camel_mime_message_get_date_received (message, &offset);
+
+       if (tt > (time_t) 0) {
+               /* Convert to UTC */
+               tt += (offset / 100) * 60 * 60;
+               tt += (offset % 100) * 60;
+
+               e_o365_mail_message_add_received_date_time (builder, tt);
+       }
+
+       addr = camel_mime_message_get_from (message);
+       o365_utils_add_address (builder, addr, e_o365_mail_message_add_from);
+
+       addr = camel_mime_message_get_reply_to (message);
+       o365_utils_add_address_array (builder, addr, e_o365_mail_message_begin_reply_to, 
e_o365_mail_message_end_reply_to);
+
+       addr = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_TO);
+       o365_utils_add_address_array (builder, addr, e_o365_mail_message_begin_to_recipients, 
e_o365_mail_message_end_to_recipients);
+
+       addr = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_CC);
+       o365_utils_add_address_array (builder, addr, e_o365_mail_message_begin_cc_recipients, 
e_o365_mail_message_end_cc_recipients);
+
+       addr = camel_mime_message_get_recipients (message, CAMEL_RECIPIENT_TYPE_BCC);
+       o365_utils_add_address_array (builder, addr, e_o365_mail_message_begin_bcc_recipients, 
e_o365_mail_message_end_bcc_recipients);
+
+       o365_utils_add_headers (builder, camel_medium_get_headers (CAMEL_MEDIUM (message)), &sender, 
&request_read_receipt);
+
+       if (sender) {
+               const gchar *name = NULL, *address = NULL;
+
+               if (camel_internet_address_get (sender, 0, &name, &address) && ((name && *name) || (address 
&& *address)))
+                       e_o365_mail_message_add_sender (builder, name, address);
+
+               g_clear_object (&sender);
+       }
+
+       if (request_read_receipt)
+               e_o365_mail_message_add_is_read_receipt_requested (builder, TRUE);
+
+       body_part = o365_utils_get_body_part (message, &attachments, cancellable);
+
+       if (body_part) {
+               CamelContentType *ct;
+               EO365ItemBodyContentTypeType o365_content_type = E_O365_ITEM_BODY_CONTENT_TYPE_UNKNOWN;
+
+               ct = camel_mime_part_get_content_type (body_part);
+
+               if (ct && camel_content_type_is (ct, "text", "html"))
+                       o365_content_type = E_O365_ITEM_BODY_CONTENT_TYPE_HTML;
+               else if (ct && camel_content_type_is (ct, "text", "plain"))
+                       o365_content_type = E_O365_ITEM_BODY_CONTENT_TYPE_TEXT;
+
+               if (o365_content_type != E_O365_ITEM_BODY_CONTENT_TYPE_UNKNOWN) {
+                       CamelStream *mem;
+                       gssize wrote = -1;
+
+                       mem = o365_utils_get_content_stream (body_part, &wrote, cancellable);
+
+                       if (mem && wrote >= 0) {
+                               GByteArray *byte_array;
+
+                               camel_stream_flush (mem, cancellable, NULL);
+
+                               byte_array = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (mem));
+
+                               /* Ensure the string is NUL-terminated */
+                               g_byte_array_append (byte_array, (const guchar *) "\0", 1);
+
+                               e_o365_mail_message_add_body (builder, o365_content_type, (const gchar *) 
byte_array->data);
+                       }
+
+                       g_clear_object (&mem);
+               }
+
+               g_object_unref (body_part);
+       } else {
+               e_o365_json_add_null_member (builder, "body");
+       }
+
+       if (info || is_send)
+               o365_utils_add_message_flags (builder, info, is_send ? message : NULL);
+
+       e_o365_json_end_object_member (builder);
+
+       success = e_o365_connection_create_mail_message_sync (cnc, NULL, folder_id, builder, 
&appended_message, cancellable, error);
+
+       g_warn_if_fail ((success && appended_message) || (!success && !appended_message));
+
+       g_object_unref (builder);
+
+       if (success && appended_message) {
+               GSList *link;
+               const gchar *message_id;
+
+               message_id = e_o365_mail_message_get_id (appended_message);
+
+               if (out_appended_uid)
+                       *out_appended_uid = g_strdup (message_id);
+
+               for (link = attachments; link && success; link = g_slist_next (link)) {
+                       CamelDataWrapper *dw = link->data;
+                       CamelContentType *ct;
+
+                       ct = camel_data_wrapper_get_mime_type_field (dw);
+
+                       builder = json_builder_new_immutable ();
+                       e_o365_attachment_begin_attachment (builder, E_O365_ATTACHMENT_DATA_TYPE_FILE);
+
+                       if (camel_content_type_is (ct, "application", "x-pkcs7-mime") ||
+                           camel_content_type_is (ct, "application", "pkcs7-mime")) {
+                               o365_utils_add_smime_encrypted_attachment (builder, dw, cancellable);
+                       } else if (CAMEL_IS_MULTIPART_SIGNED (dw)) {
+                               o365_utils_add_smime_signed_attachment (builder, dw, cancellable);
+                       } else {
+                               o365_utils_add_file_attachment (builder, dw, cancellable);
+                       }
+
+                       e_o365_json_end_object_member (builder);
+
+                       success = e_o365_connection_add_mail_message_attachment (cnc, NULL, message_id, 
builder, NULL, cancellable, error);
+
+                       g_object_unref (builder);
+               }
+       }
+
+       g_slist_free_full (attachments, g_object_unref);
+
+       if (appended_message)
+               json_object_unref (appended_message);
+
+       return success;
+}
diff --git a/src/Office365/camel/camel-o365-utils.h b/src/Office365/camel/camel-o365-utils.h
index 6c4c507e..f719c2f0 100644
--- a/src/Office365/camel/camel-o365-utils.h
+++ b/src/Office365/camel/camel-o365-utils.h
@@ -29,8 +29,18 @@ gchar *              camel_o365_utils_encode_category_name
                                                (const gchar *name);
 gchar *                camel_o365_utils_decode_category_name
                                                (const gchar *flag);
+gboolean       camel_o365_utils_is_system_user_flag
+                                               (const gchar *name);
 const gchar *  camel_o365_utils_rename_label   (const gchar *cat,
                                                 gboolean from_cat);
-
+gboolean       camel_o365_utils_create_message_sync
+                                               (EO365Connection *cnc,
+                                                const gchar *folder_id,
+                                                CamelMimeMessage *message,
+                                                CamelMessageInfo *info,
+                                                gboolean is_send,
+                                                gchar **out_appended_uid,
+                                                GCancellable *cancellable,
+                                                GError **error);
 
 #endif /* CAMEL_O365_UTILS_H */
diff --git a/src/Office365/common/e-o365-connection.c b/src/Office365/common/e-o365-connection.c
index d1e95bc3..8f73fdbe 100644
--- a/src/Office365/common/e-o365-connection.c
+++ b/src/Office365/common/e-o365-connection.c
@@ -1468,7 +1468,7 @@ o365_connection_new_soup_message (const gchar *method,
        message = soup_message_new (method, uri);
 
        if (message) {
-               soup_message_headers_append (message->request_headers, "Connection", "Keep-Alive");
+               soup_message_headers_append (message->request_headers, "Connection", "Close");
                soup_message_headers_append (message->request_headers, "User-Agent", "Evolution-O365/" 
VERSION);
        } else {
                g_set_error (error, SOUP_HTTP_ERROR, SOUP_STATUS_MALFORMED, _("Malformed URI: ā€œ%sā€"), uri);
@@ -1893,10 +1893,8 @@ e_o365_connection_batch_request_internal_sync (EO365Connection *cnc,
 
        builder = json_builder_new_immutable ();
 
-       json_builder_begin_object (builder);
-
-       json_builder_set_member_name (builder, "requests");
-       json_builder_begin_array (builder);
+       e_o365_json_begin_object_member (builder, NULL);
+       e_o365_json_begin_array_member (builder, "requests");
 
        for (ii = 0; ii < requests->len; ii++) {
                SoupMessageHeadersIter iter;
@@ -1929,16 +1927,11 @@ e_o365_connection_batch_request_internal_sync (EO365Connection *cnc,
 
                g_snprintf (buff, sizeof (buff), "%d", ii);
 
-               json_builder_begin_object (builder);
-
-               json_builder_set_member_name (builder, "id");
-               json_builder_add_string_value (builder, buff);
-
-               json_builder_set_member_name (builder, "method");
-               json_builder_add_string_value (builder, submessage->method);
+               e_o365_json_begin_object_member (builder, NULL);
 
-               json_builder_set_member_name (builder, "url");
-               json_builder_add_string_value (builder, use_uri);
+               e_o365_json_add_string_member (builder, "id", buff);
+               e_o365_json_add_string_member (builder, "method", submessage->method);
+               e_o365_json_add_string_member (builder, "url", use_uri);
 
                g_free (uri);
 
@@ -1949,17 +1942,15 @@ e_o365_connection_batch_request_internal_sync (EO365Connection *cnc,
                                if (!has_headers) {
                                        has_headers = TRUE;
 
-                                       json_builder_set_member_name (builder, "headers");
-                                       json_builder_begin_object (builder);
+                                       e_o365_json_begin_object_member (builder, "headers");
                                }
 
-                               json_builder_set_member_name (builder, hdr_name);
-                               json_builder_add_string_value (builder, hdr_value);
+                               e_o365_json_add_string_member (builder, hdr_name, hdr_value);
                        }
                }
 
                if (has_headers)
-                       json_builder_end_object (builder);
+                       e_o365_json_end_object_member (builder); /* headers */
 
                if (submessage->request_body) {
                        SoupBuffer *sbuffer;
@@ -1967,19 +1958,18 @@ e_o365_connection_batch_request_internal_sync (EO365Connection *cnc,
                        sbuffer = soup_message_body_flatten (submessage->request_body);
 
                        if (sbuffer && sbuffer->length > 0) {
-                               json_builder_set_member_name (builder, "body");
-                               json_builder_add_string_value (builder, sbuffer->data);
+                               e_o365_json_add_string_member (builder, "body", sbuffer->data);
                        }
 
                        if (sbuffer)
                                soup_buffer_free (sbuffer);
                }
 
-               json_builder_end_object (builder);
+               e_o365_json_end_object_member (builder); /* unnamed object */
        }
 
-       json_builder_end_array (builder);
-       json_builder_end_object (builder);
+       e_o365_json_end_array_member (builder);
+       e_o365_json_end_object_member (builder);
 
        e_o365_connection_set_json_body (message, builder);
 
@@ -2308,10 +2298,9 @@ e_o365_connection_create_mail_folder_sync (EO365Connection *cnc,
 
        builder = json_builder_new_immutable ();
 
-       json_builder_begin_object (builder);
-       json_builder_set_member_name (builder, "displayName");
-       json_builder_add_string_value (builder, display_name);
-       json_builder_end_object (builder);
+       e_o365_json_begin_object_member (builder, NULL);
+       e_o365_json_add_string_member (builder, "displayName", display_name);
+       e_o365_json_end_object_member (builder);
 
        e_o365_connection_set_json_body (message, builder);
 
@@ -2400,10 +2389,9 @@ e_o365_connection_copy_move_mail_folder_sync (EO365Connection *cnc,
 
        builder = json_builder_new_immutable ();
 
-       json_builder_begin_object (builder);
-       json_builder_set_member_name (builder, "destinationId");
-       json_builder_add_string_value (builder, des_folder_id);
-       json_builder_end_object (builder);
+       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);
 
@@ -2454,10 +2442,9 @@ e_o365_connection_rename_mail_folder_sync (EO365Connection *cnc,
 
        builder = json_builder_new_immutable ();
 
-       json_builder_begin_object (builder);
-       json_builder_set_member_name (builder, "displayName");
-       json_builder_add_string_value (builder, display_name);
-       json_builder_end_object (builder);
+       e_o365_json_begin_object_member (builder, NULL);
+       e_o365_json_add_string_member (builder, "displayName", display_name);
+       e_o365_json_end_object_member (builder);
 
        e_o365_connection_set_json_body (message, builder);
 
@@ -2587,3 +2574,97 @@ e_o365_connection_get_mail_message_sync (EO365Connection *cnc,
 
        return success;
 }
+
+/* https://docs.microsoft.com/en-us/graph/api/user-post-messages?view=graph-rest-1.0&tabs=http */
+
+gboolean
+e_o365_connection_create_mail_message_sync (EO365Connection *cnc,
+                                           const gchar *user_override, /* for which user, NULL to use the 
account user */
+                                           const gchar *folder_id, /* if NULL, then goes to the Drafts 
folder */
+                                           JsonBuilder *mail_message, /* filled mailMessage object */
+                                           EO365MailMessage **out_appended_message, /* free with 
json_object_unref() */
+                                           GCancellable *cancellable,
+                                           GError **error)
+{
+       SoupMessage *message = NULL;
+       gboolean success;
+       gchar *uri;
+
+       g_return_val_if_fail (E_IS_O365_CONNECTION (cnc), FALSE);
+       g_return_val_if_fail (mail_message != NULL, FALSE);
+       g_return_val_if_fail (out_appended_message != NULL, FALSE);
+
+       uri = e_o365_connection_construct_uri (cnc, TRUE, user_override, E_O365_API_V1_0, NULL,
+               folder_id ? "mailFolders" : "messages",
+               folder_id,
+               folder_id ? "messages" : NULL,
+               NULL);
+
+       message = o365_connection_new_soup_message (SOUP_METHOD_POST, uri, error);
+
+       if (!message) {
+               g_free (uri);
+
+               return FALSE;
+       }
+
+       g_free (uri);
+
+       e_o365_connection_set_json_body (message, mail_message);
+
+       success = o365_connection_send_request_sync (cnc, message, e_o365_read_json_object_response_cb, NULL, 
out_appended_message, cancellable, error);
+
+       g_clear_object (&message);
+
+       return success;
+}
+
+/* https://docs.microsoft.com/en-us/graph/api/message-post-attachments?view=graph-rest-1.0&tabs=http */
+
+gboolean
+e_o365_connection_add_mail_message_attachment (EO365Connection *cnc,
+                                              const gchar *user_override, /* for which user, NULL to use the 
account user */
+                                              const gchar *message_id, /* the message to add it to */
+                                              JsonBuilder *attachment, /* filled attachment object */
+                                              gchar **out_attachment_id,
+                                              GCancellable *cancellable,
+                                              GError **error)
+{
+       SoupMessage *message = NULL;
+       JsonObject *added_attachment = NULL;
+       gboolean success;
+       gchar *uri;
+
+       g_return_val_if_fail (E_IS_O365_CONNECTION (cnc), FALSE);
+       g_return_val_if_fail (attachment != NULL, FALSE);
+
+       uri = e_o365_connection_construct_uri (cnc, TRUE, user_override, E_O365_API_V1_0, NULL,
+               "messages",
+               message_id,
+               "attachments",
+               NULL);
+
+       message = o365_connection_new_soup_message (SOUP_METHOD_POST, uri, error);
+
+       if (!message) {
+               g_free (uri);
+
+               return FALSE;
+       }
+
+       g_free (uri);
+
+       e_o365_connection_set_json_body (message, attachment);
+
+       success = o365_connection_send_request_sync (cnc, message, e_o365_read_json_object_response_cb, NULL, 
&added_attachment, cancellable, error);
+
+       if (success && added_attachment && out_attachment_id)
+               *out_attachment_id = g_strdup (e_o365_attachment_get_id (added_attachment));
+
+       if (added_attachment)
+               json_object_unref (added_attachment);
+
+       g_clear_object (&message);
+
+       return success;
+}
diff --git a/src/Office365/common/e-o365-connection.h b/src/Office365/common/e-o365-connection.h
index ee7ea81a..961cc32d 100644
--- a/src/Office365/common/e-o365-connection.h
+++ b/src/Office365/common/e-o365-connection.h
@@ -236,6 +236,22 @@ gboolean   e_o365_connection_get_mail_message_sync
                                                 gpointer func_user_data,
                                                 GCancellable *cancellable,
                                                 GError **error);
+gboolean       e_o365_connection_create_mail_message_sync
+                                               (EO365Connection *cnc,
+                                                const gchar *user_override, /* for which user, NULL to use 
the account user */
+                                                const gchar *folder_id, /* if NULL, then goes to the Drafts 
folder */
+                                                JsonBuilder *mail_message, /* filled mailMessage object */
+                                                EO365MailMessage **out_appended_message, /* free with 
json_object_unref() */
+                                                GCancellable *cancellable,
+                                                GError **error);
+gboolean       e_o365_connection_add_mail_message_attachment
+                                               (EO365Connection *cnc,
+                                                const gchar *user_override, /* for which user, NULL to use 
the account user */
+                                                const gchar *message_id, /* the message to add it to */
+                                                JsonBuilder *attachment, /* filled attachment object */
+                                                gchar **out_attachment_id,
+                                                GCancellable *cancellable,
+                                                GError **error);
 
 G_END_DECLS
 
diff --git a/src/Office365/common/e-o365-json-utils.c b/src/Office365/common/e-o365-json-utils.c
index 9b50c3f7..35e21046 100644
--- a/src/Office365/common/e-o365-json-utils.c
+++ b/src/Office365/common/e-o365-json-utils.c
@@ -40,6 +40,22 @@ e_o365_json_get_array_member (JsonObject *object,
        return json_node_get_array (node);
 }
 
+void
+e_o365_json_begin_array_member (JsonBuilder *builder,
+                               const gchar *member_name)
+{
+       if (member_name && *member_name)
+               json_builder_set_member_name (builder, member_name);
+
+       json_builder_begin_array (builder);
+}
+
+void
+e_o365_json_end_array_member (JsonBuilder *builder)
+{
+       json_builder_end_array (builder);
+}
+
 gboolean
 e_o365_json_get_boolean_member (JsonObject *object,
                                const gchar *member_name,
@@ -60,6 +76,17 @@ e_o365_json_get_boolean_member (JsonObject *object,
        return json_node_get_boolean (node);
 }
 
+void
+e_o365_json_add_boolean_member (JsonBuilder *builder,
+                               const gchar *member_name,
+                               gboolean value)
+{
+       g_return_if_fail (member_name && *member_name);
+
+       json_builder_set_member_name (builder, member_name);
+       json_builder_add_boolean_value (builder, value);
+}
+
 gdouble
 e_o365_json_get_double_member (JsonObject *object,
                               const gchar *member_name,
@@ -80,6 +107,17 @@ e_o365_json_get_double_member (JsonObject *object,
        return json_node_get_double (node);
 }
 
+void
+e_o365_json_add_double_member (JsonBuilder *builder,
+                              const gchar *member_name,
+                              gdouble value)
+{
+       g_return_if_fail (member_name && *member_name);
+
+       json_builder_set_member_name (builder, member_name);
+       json_builder_add_double_value (builder, value);
+}
+
 gint64
 e_o365_json_get_int_member (JsonObject *object,
                            const gchar *member_name,
@@ -100,6 +138,17 @@ e_o365_json_get_int_member (JsonObject *object,
        return json_node_get_int (node);
 }
 
+void
+e_o365_json_add_int_member (JsonBuilder *builder,
+                           const gchar *member_name,
+                           gint64 value)
+{
+       g_return_if_fail (member_name && *member_name);
+
+       json_builder_set_member_name (builder, member_name);
+       json_builder_add_int_value (builder, value);
+}
+
 gboolean
 e_o365_json_get_null_member (JsonObject *object,
                             const gchar *member_name,
@@ -120,6 +169,16 @@ e_o365_json_get_null_member (JsonObject *object,
        return json_node_is_null (node);
 }
 
+void
+e_o365_json_add_null_member (JsonBuilder *builder,
+                            const gchar *member_name)
+{
+       g_return_if_fail (member_name && *member_name);
+
+       json_builder_set_member_name (builder, member_name);
+       json_builder_add_null_value (builder);
+}
+
 JsonObject *
 e_o365_json_get_object_member (JsonObject *object,
                               const gchar *member_name)
@@ -139,6 +198,22 @@ e_o365_json_get_object_member (JsonObject *object,
        return json_node_get_object (node);
 }
 
+void
+e_o365_json_begin_object_member (JsonBuilder *builder,
+                                const gchar *member_name)
+{
+       if (member_name && *member_name)
+               json_builder_set_member_name (builder, member_name);
+
+       json_builder_begin_object (builder);
+}
+
+void
+e_o365_json_end_object_member (JsonBuilder *builder)
+{
+       json_builder_end_object (builder);
+}
+
 const gchar *
 e_o365_json_get_string_member (JsonObject *object,
                               const gchar *member_name,
@@ -159,6 +234,17 @@ e_o365_json_get_string_member (JsonObject *object,
        return json_node_get_string (node);
 }
 
+void
+e_o365_json_add_string_member (JsonBuilder *builder,
+                              const gchar *member_name,
+                              const gchar *value)
+{
+       g_return_if_fail (member_name && *member_name);
+
+       json_builder_set_member_name (builder, member_name);
+       json_builder_add_string_value (builder, value ? value : "");
+}
+
 time_t
 e_o365_get_date_time_offset_member (JsonObject *object,
                                    const gchar *member_name)
@@ -182,6 +268,30 @@ e_o365_get_date_time_offset_member (JsonObject *object,
        return res;
 }
 
+void
+e_o365_add_date_time_offset_member (JsonBuilder *builder,
+                                   const gchar *member_name,
+                                   time_t value)
+{
+       GDateTime *dt;
+       gchar *value_str;
+
+       if ((time_t) value <= 0) {
+               e_o365_json_add_null_member (builder, member_name);
+               return;
+       }
+
+       dt = g_date_time_new_from_unix_utc (value);
+       g_return_if_fail (dt != NULL);
+
+       value_str = g_date_time_format_iso8601 (dt);
+
+       e_o365_json_add_string_member (builder, member_name, value_str);
+
+       g_date_time_unref (dt);
+       g_free (value_str);
+}
+
 /* https://docs.microsoft.com/en-us/graph/delta-query-overview */
 
 gboolean
@@ -295,7 +405,7 @@ e_o365_mail_folder_get_unread_item_count (EO365MailFolder *folder)
    https://docs.microsoft.com/en-us/graph/api/resources/emailaddress?view=graph-rest-1.0
  */
 const gchar *
-e_o365_recipient_get_address (EO365Recipient *recipient)
+e_o365_recipient_get_name (EO365Recipient *recipient)
 {
        JsonObject *email_address;
 
@@ -304,11 +414,11 @@ e_o365_recipient_get_address (EO365Recipient *recipient)
        if (!email_address)
                return NULL;
 
-       return e_o365_json_get_string_member (email_address, "address", NULL);
+       return e_o365_json_get_string_member (email_address, "name", NULL);
 }
 
 const gchar *
-e_o365_recipient_get_name (EO365Recipient *recipient)
+e_o365_recipient_get_address (EO365Recipient *recipient)
 {
        JsonObject *email_address;
 
@@ -317,7 +427,28 @@ e_o365_recipient_get_name (EO365Recipient *recipient)
        if (!email_address)
                return NULL;
 
-       return e_o365_json_get_string_member (email_address, "name", NULL);
+       return e_o365_json_get_string_member (email_address, "address", NULL);
+}
+
+void
+e_o365_add_recipient (JsonBuilder *builder,
+                     const gchar *member_name,
+                     const gchar *name,
+                     const gchar *address)
+{
+       g_return_if_fail ((name && *name) || (address && *address));
+
+       e_o365_json_begin_object_member (builder, member_name);
+       e_o365_json_begin_object_member (builder, "emailAddress");
+
+       if (name && *name)
+               e_o365_json_add_string_member (builder, "name", name);
+
+       if (address && *address)
+               e_o365_json_add_string_member (builder, "address", address);
+
+       e_o365_json_end_object_member (builder); /* emailAddress */
+       e_o365_json_end_object_member (builder); /* member_name */
 }
 
 /* https://docs.microsoft.com/en-us/graph/api/resources/datetimetimezone?view=graph-rest-1.0 */
@@ -334,6 +465,29 @@ e_o365_date_time_get_time_zone (EO365DateTimeWithZone *datetime)
        return e_o365_json_get_string_member (datetime, "timeZone", NULL);
 }
 
+void
+e_o365_add_date_time (JsonBuilder *builder,
+                     const gchar *member_name,
+                     time_t date_time,
+                     const gchar *zone)
+{
+       g_return_if_fail (member_name != NULL);
+
+       if (date_time <= (time_t) 0) {
+               e_o365_json_add_null_member (builder, member_name);
+               return;
+       }
+
+       e_o365_json_begin_object_member (builder, member_name);
+
+       e_o365_add_date_time_offset_member (builder, "dateTime", date_time);
+
+       if (zone && *zone)
+               e_o365_json_add_string_member (builder, "timeZone", zone);
+
+       e_o365_json_end_object_member (builder);
+}
+
 /* https://docs.microsoft.com/en-us/graph/api/resources/internetmessageheader?view=graph-rest-1.0 */
 
 const gchar *
@@ -348,6 +502,25 @@ e_o365_internet_message_header_get_value (EO365InternetMessageHeader *header)
        return e_o365_json_get_string_member (header, "value", NULL);
 }
 
+void
+e_o365_add_internet_message_header (JsonBuilder *builder,
+                                   const gchar *name,
+                                   const gchar *value)
+{
+       g_return_if_fail (name && *name);
+       g_return_if_fail (value);
+
+       json_builder_begin_object (builder);
+
+       if (value && (*value == ' ' || *value == '\t'))
+               value++;
+
+       e_o365_json_add_string_member (builder, "name", name);
+       e_o365_json_add_string_member (builder, "value", value);
+
+       json_builder_end_object (builder);
+}
+
 /* https://docs.microsoft.com/en-us/graph/api/resources/followupflag?view=graph-rest-1.0 */
 
 EO365DateTimeWithZone *
@@ -356,12 +529,28 @@ e_o365_followup_flag_get_completed_date_time (EO365FollowupFlag *flag)
        return e_o365_json_get_object_member (flag, "completedDateTime");
 }
 
+void
+e_o365_followup_flag_add_completed_date_time (JsonBuilder *builder,
+                                             time_t date_time,
+                                             const gchar *zone)
+{
+       e_o365_add_date_time (builder, "completedDateTime", date_time, zone);
+}
+
 EO365DateTimeWithZone *
 e_o365_followup_flag_get_due_date_time (EO365FollowupFlag *flag)
 {
        return e_o365_json_get_object_member (flag, "dueDateTime");
 }
 
+void
+e_o365_followup_flag_add_due_date_time (JsonBuilder *builder,
+                                       time_t date_time,
+                                       const gchar *zone)
+{
+       e_o365_add_date_time (builder, "dueDateTime", date_time, zone);
+}
+
 EO365FollowupFlagStatusType
 e_o365_followup_flag_get_flag_status (EO365FollowupFlag *flag)
 {
@@ -372,24 +561,48 @@ e_o365_followup_flag_get_flag_status (EO365FollowupFlag *flag)
        if (!status)
                return E_O365_FOLLOWUP_FLAG_STATUS_NOT_SET;
 
-       if (g_strcmp0 (status, "notFlagged") == 0)
+       if (g_ascii_strcasecmp (status, "notFlagged") == 0)
                return E_O365_FOLLOWUP_FLAG_STATUS_NOT_FLAGGED;
 
-       if (g_strcmp0 (status, "complete") == 0)
+       if (g_ascii_strcasecmp (status, "complete") == 0)
                return E_O365_FOLLOWUP_FLAG_STATUS_COMPLETE;
 
-       if (g_strcmp0 (status, "flagged") == 0)
+       if (g_ascii_strcasecmp (status, "flagged") == 0)
                return E_O365_FOLLOWUP_FLAG_STATUS_FLAGGED;
 
        return E_O365_FOLLOWUP_FLAG_STATUS_UNKNOWN;
 }
 
+void
+e_o365_followup_flag_add_flag_status (JsonBuilder *builder,
+                                     EO365FollowupFlagStatusType status)
+{
+       const gchar *value;
+
+       if (status == E_O365_FOLLOWUP_FLAG_STATUS_COMPLETE)
+               value = "complete";
+       else if (status == E_O365_FOLLOWUP_FLAG_STATUS_FLAGGED)
+               value = "flagged";
+       else
+               value = "notFlagged";
+
+       e_o365_json_add_string_member (builder, "flagStatus", value);
+}
+
 EO365DateTimeWithZone *
 e_o365_followup_flag_get_start_date_time (EO365FollowupFlag *flag)
 {
        return e_o365_json_get_object_member (flag, "startDateTime");
 }
 
+void
+e_o365_followup_flag_add_start_date_time (JsonBuilder *builder,
+                                         time_t date_time,
+                                         const gchar *zone)
+{
+       e_o365_add_date_time (builder, "startDateTime", date_time, zone);
+}
+
 /* https://docs.microsoft.com/en-us/graph/api/resources/itembody?view=graph-rest-1.0 */
 
 const gchar *
@@ -408,15 +621,48 @@ e_o365_item_body_get_content_type (EO365ItemBody *item_body)
        if (!content_type)
                return E_O365_ITEM_BODY_CONTENT_TYPE_NOT_SET;
 
-       if (g_strcmp0 (content_type, "text") == 0)
+       if (g_ascii_strcasecmp (content_type, "text") == 0)
                return E_O365_ITEM_BODY_CONTENT_TYPE_TEXT;
 
-       if (g_strcmp0 (content_type, "html") == 0)
+       if (g_ascii_strcasecmp (content_type, "html") == 0)
                return E_O365_ITEM_BODY_CONTENT_TYPE_HTML;
 
        return E_O365_ITEM_BODY_CONTENT_TYPE_UNKNOWN;
 }
 
+void
+e_o365_add_item_body (JsonBuilder *builder,
+                     const gchar *member_name,
+                     EO365ItemBodyContentTypeType content_type,
+                     const gchar *content)
+{
+       const gchar *content_type_str;
+
+       g_return_if_fail (member_name != NULL);
+       g_return_if_fail (content != NULL);
+
+       switch (content_type) {
+       case E_O365_ITEM_BODY_CONTENT_TYPE_TEXT:
+               content_type_str = "text";
+               break;
+       case E_O365_ITEM_BODY_CONTENT_TYPE_HTML:
+               content_type_str = "html";
+               break;
+       default:
+               g_warn_if_reached ();
+
+               content_type_str = "text";
+               break;
+       }
+
+       e_o365_json_begin_object_member (builder, member_name);
+
+       e_o365_json_add_string_member (builder, "contentType", content_type_str);
+       e_o365_json_add_string_member (builder, "content", content);
+
+       e_o365_json_end_object_member (builder);
+}
+
 /* https://docs.microsoft.com/en-us/graph/api/resources/message?view=graph-rest-1.0 */
 
 JsonArray * /* EO365Recipient * */
@@ -425,12 +671,32 @@ e_o365_mail_message_get_bcc_recipients (EO365MailMessage *mail)
        return e_o365_json_get_array_member (mail, "bccRecipients");
 }
 
+void
+e_o365_mail_message_begin_bcc_recipients (JsonBuilder *builder)
+{
+       e_o365_json_begin_array_member (builder, "bccRecipients");
+}
+
+void
+e_o365_mail_message_end_bcc_recipients (JsonBuilder *builder)
+{
+       e_o365_json_end_array_member (builder);
+}
+
 EO365ItemBody *
 e_o365_mail_message_get_body (EO365MailMessage *mail)
 {
        return e_o365_json_get_object_member (mail, "body");
 }
 
+void
+e_o365_mail_message_add_body (JsonBuilder *builder,
+                             EO365ItemBodyContentTypeType content_type,
+                             const gchar *content)
+{
+       e_o365_add_item_body (builder, "body", content_type, content);
+}
+
 const gchar *
 e_o365_mail_message_get_body_preview (EO365MailMessage *mail)
 {
@@ -443,12 +709,45 @@ e_o365_mail_message_get_categories (EO365MailMessage *mail)
        return e_o365_json_get_array_member (mail, "categories");
 }
 
+void
+e_o365_mail_message_begin_categories (JsonBuilder *builder)
+{
+       e_o365_json_begin_array_member (builder, "categories");
+}
+
+void
+e_o365_mail_message_end_categories (JsonBuilder *builder)
+{
+       e_o365_json_end_array_member (builder);
+}
+
+void
+e_o365_mail_message_add_category (JsonBuilder *builder,
+                                 const gchar *category)
+{
+       g_return_if_fail (category && *category);
+
+       json_builder_add_string_value (builder, category);
+}
+
 JsonArray * /* EO365Recipient * */
 e_o365_mail_message_get_cc_recipients (EO365MailMessage *mail)
 {
        return e_o365_json_get_array_member (mail, "ccRecipients");
 }
 
+void
+e_o365_mail_message_begin_cc_recipients (JsonBuilder *builder)
+{
+       e_o365_json_begin_array_member (builder, "ccRecipients");
+}
+
+void
+e_o365_mail_message_end_cc_recipients (JsonBuilder *builder)
+{
+       e_o365_json_end_array_member (builder);
+}
+
 const gchar *
 e_o365_mail_message_get_change_key (EO365MailMessage *mail)
 {
@@ -479,12 +778,32 @@ e_o365_mail_message_get_flag (EO365MailMessage *mail)
        return e_o365_json_get_object_member (mail, "flag");
 }
 
+void
+e_o365_mail_message_begin_flag (JsonBuilder *builder)
+{
+       e_o365_json_begin_object_member (builder, "flag");
+}
+
+void
+e_o365_mail_message_end_flag (JsonBuilder *builder)
+{
+       e_o365_json_end_object_member (builder);
+}
+
 EO365Recipient *
 e_o365_mail_message_get_from (EO365MailMessage *mail)
 {
        return e_o365_json_get_object_member (mail, "from");
 }
 
+void
+e_o365_mail_message_add_from (JsonBuilder *builder,
+                             const gchar *name,
+                             const gchar *address)
+{
+       e_o365_add_recipient (builder, "from", name, address);
+}
+
 gboolean
 e_o365_mail_message_get_has_attachments        (EO365MailMessage *mail)
 {
@@ -505,18 +824,41 @@ e_o365_mail_message_get_importance (EO365MailMessage *mail)
        if (!value)
                return E_O365_IMPORTANCE_NOT_SET;
 
-       if (g_strcmp0 (value, "Low") == 0)
+       if (g_ascii_strcasecmp (value, "low") == 0)
                return E_O365_IMPORTANCE_LOW;
 
-       if (g_strcmp0 (value, "Normal") == 0)
+       if (g_ascii_strcasecmp (value, "normal") == 0)
                return E_O365_IMPORTANCE_NORMAL;
 
-       if (g_strcmp0 (value, "High") == 0)
+       if (g_ascii_strcasecmp (value, "high") == 0)
                return E_O365_IMPORTANCE_HIGH;
 
        return E_O365_IMPORTANCE_UNKNOWN;
 }
 
+void
+e_o365_mail_message_add_importance (JsonBuilder *builder,
+                                   EO365ImportanceType importance)
+{
+       const gchar *value = NULL;
+
+       switch (importance) {
+       case E_O365_IMPORTANCE_LOW:
+               value = "low";
+               break;
+       case E_O365_IMPORTANCE_NORMAL:
+               value = "normal";
+               break;
+       case E_O365_IMPORTANCE_HIGH:
+               value = "high";
+               break;
+       default:
+               return;
+       }
+
+       e_o365_json_add_string_member (builder, "importance", value);
+}
+
 EO365InferenceClassificationType
 e_o365_mail_message_get_inference_classification (EO365MailMessage *mail)
 {
@@ -525,10 +867,10 @@ e_o365_mail_message_get_inference_classification (EO365MailMessage *mail)
        if (!value)
                return E_O365_INFERENCE_CLASSIFICATION_NOT_SET;
 
-       if (g_strcmp0 (value, "focused") == 0)
+       if (g_ascii_strcasecmp (value, "focused") == 0)
                return E_O365_INFERENCE_CLASSIFICATION_FOCUSED;
 
-       if (g_strcmp0 (value, "other") == 0)
+       if (g_ascii_strcasecmp (value, "other") == 0)
                return E_O365_INFERENCE_CLASSIFICATION_OTHER;
 
        return E_O365_INFERENCE_CLASSIFICATION_UNKNOWN;
@@ -540,18 +882,45 @@ e_o365_mail_message_get_internet_message_headers (EO365MailMessage *mail)
        return e_o365_json_get_array_member (mail, "internetMessageHeaders");
 }
 
+void
+e_o365_mail_message_begin_internet_message_headers (JsonBuilder *builder)
+{
+       e_o365_json_begin_array_member (builder, "internetMessageHeaders");
+}
+
+void
+e_o365_mail_message_end_internet_message_headers (JsonBuilder *builder)
+{
+       e_o365_json_end_array_member (builder);
+}
+
 const gchar *
 e_o365_mail_message_get_internet_message_id (EO365MailMessage *mail)
 {
        return e_o365_json_get_string_member (mail, "internetMessageId", NULL);
 }
 
+void
+e_o365_mail_message_add_internet_message_id (JsonBuilder *builder,
+                                            const gchar *message_id)
+{
+       if (message_id && *message_id)
+               e_o365_json_add_string_member (builder, "internetMessageId", message_id);
+}
+
 gboolean
 e_o365_mail_message_get_is_delivery_receipt_requested (EO365MailMessage *mail)
 {
        return e_o365_json_get_boolean_member (mail, "isDeliveryReceiptRequested", FALSE);
 }
 
+void
+e_o365_mail_message_add_is_delivery_receipt_requested (JsonBuilder *builder,
+                                                      gboolean value)
+{
+       e_o365_json_add_boolean_member (builder, "isDeliveryReceiptRequested", value);
+}
+
 gboolean
 e_o365_mail_message_get_is_draft (EO365MailMessage *mail)
 {
@@ -564,12 +933,26 @@ e_o365_mail_message_get_is_read (EO365MailMessage *mail)
        return e_o365_json_get_boolean_member (mail, "isRead", FALSE);
 }
 
+void
+e_o365_mail_message_add_is_read (JsonBuilder *builder,
+                                gboolean value)
+{
+       e_o365_json_add_boolean_member (builder, "isRead", value);
+}
+
 gboolean
 e_o365_mail_message_get_is_read_receipt_requested (EO365MailMessage *mail)
 {
        return e_o365_json_get_boolean_member (mail, "isReadReceiptRequested", FALSE);
 }
 
+void
+e_o365_mail_message_add_is_read_receipt_requested (JsonBuilder *builder,
+                                                  gboolean value)
+{
+       e_o365_json_add_boolean_member (builder, "isReadReceiptRequested", value);
+}
+
 time_t
 e_o365_mail_message_get_last_modified_date_time (EO365MailMessage *mail)
 {
@@ -588,36 +971,92 @@ e_o365_mail_message_get_received_date_time (EO365MailMessage *mail)
        return e_o365_get_date_time_offset_member (mail, "receivedDateTime");
 }
 
+void
+e_o365_mail_message_add_received_date_time (JsonBuilder *builder,
+                                           time_t value)
+{
+       e_o365_add_date_time_offset_member (builder, "receivedDateTime", value);
+}
+
 JsonArray * /* EO365Recipient * */
 e_o365_mail_message_get_reply_to (EO365MailMessage *mail)
 {
        return e_o365_json_get_array_member (mail, "replyTo");
 }
 
+void
+e_o365_mail_message_begin_reply_to (JsonBuilder *builder)
+{
+       e_o365_json_begin_array_member (builder, "replyTo");
+}
+
+void
+e_o365_mail_message_end_reply_to (JsonBuilder *builder)
+{
+       e_o365_json_end_array_member (builder);
+}
+
 EO365Recipient *
 e_o365_mail_message_get_sender (EO365MailMessage *mail)
 {
        return e_o365_json_get_object_member (mail, "sender");
 }
 
+void
+e_o365_mail_message_add_sender (JsonBuilder *builder,
+                               const gchar *name,
+                               const gchar *address)
+{
+       g_return_if_fail ((name && *name) || (address && *address));
+
+       e_o365_add_recipient (builder, "sender", name, address);
+}
+
 time_t
 e_o365_mail_message_get_sent_date_time (EO365MailMessage *mail)
 {
        return e_o365_get_date_time_offset_member (mail, "sentDateTime");
 }
 
+void
+e_o365_mail_message_add_sent_date_time (JsonBuilder *builder,
+                                       time_t value)
+{
+       e_o365_add_date_time_offset_member (builder, "sentDateTime", value);
+}
+
 const gchar *
 e_o365_mail_message_get_subject (EO365MailMessage *mail)
 {
        return e_o365_json_get_string_member (mail, "subject", NULL);
 }
 
+void
+e_o365_mail_message_add_subject (JsonBuilder *builder,
+                                const gchar *subject)
+{
+       if (subject)
+               e_o365_json_add_string_member (builder, "subject", subject);
+}
+
 JsonArray * /* EO365Recipient * */
 e_o365_mail_message_get_to_recipients (EO365MailMessage *mail)
 {
        return e_o365_json_get_array_member (mail, "toRecipients");
 }
 
+void
+e_o365_mail_message_begin_to_recipients (JsonBuilder *builder)
+{
+       e_o365_json_begin_array_member (builder, "toRecipients");
+}
+
+void
+e_o365_mail_message_end_to_recipients (JsonBuilder *builder)
+{
+       e_o365_json_end_array_member (builder);
+}
+
 EO365ItemBody *
 e_o365_mail_message_get_unique_body (EO365MailMessage *mail)
 {
@@ -629,3 +1068,146 @@ e_o365_mail_message_get_web_link (EO365MailMessage *mail)
 {
        return e_o365_json_get_string_member (mail, "webLink", NULL);
 }
+
+/* https://docs.microsoft.com/en-us/graph/api/resources/attachment?view=graph-rest-1.0 */
+
+EO365AttachmentDataType
+e_o365_attachment_get_data_type (EO365Attachment *attachment)
+{
+       const gchar *data_type;
+
+       data_type = e_o365_json_get_string_member (attachment, "@odata.type", NULL);
+
+       if (!data_type)
+               return E_O365_ATTACHMENT_DATA_TYPE_NOT_SET;
+
+       if (g_ascii_strcasecmp (data_type, "#microsoft.graph.fileAttachment") == 0)
+               return E_O365_ATTACHMENT_DATA_TYPE_FILE;
+
+       if (g_ascii_strcasecmp (data_type, "#microsoft.graph.itemAttachment") == 0)
+               return E_O365_ATTACHMENT_DATA_TYPE_ITEM;
+
+       if (g_ascii_strcasecmp (data_type, "#microsoft.graph.referenceAttachment") == 0)
+               return E_O365_ATTACHMENT_DATA_TYPE_REFERENCE;
+
+       return E_O365_ATTACHMENT_DATA_TYPE_UNKNOWN;
+}
+
+void
+e_o365_attachment_begin_attachment (JsonBuilder *builder,
+                                   EO365AttachmentDataType data_type)
+{
+       e_o365_json_begin_object_member (builder, NULL);
+
+       if (data_type == E_O365_ATTACHMENT_DATA_TYPE_FILE)
+               e_o365_json_add_string_member (builder, "@odata.type", "#microsoft.graph.fileAttachment");
+       else if (data_type == E_O365_ATTACHMENT_DATA_TYPE_ITEM)
+               e_o365_json_add_string_member (builder, "@odata.type", "#microsoft.graph.itemAttachment");
+       else if (data_type == E_O365_ATTACHMENT_DATA_TYPE_REFERENCE)
+               e_o365_json_add_string_member (builder, "@odata.type", 
"#microsoft.graph.referenceAttachment");
+}
+
+void
+e_o365_attachment_end_attachment (JsonBuilder *builder)
+{
+       e_o365_json_end_object_member (builder);
+}
+
+const gchar *
+e_o365_attachment_get_content_type (EO365Attachment *attachment)
+{
+       return e_o365_json_get_string_member (attachment, "contentType", NULL);
+}
+
+void
+e_o365_attachment_add_content_type (JsonBuilder *builder,
+                                   const gchar *value)
+{
+       e_o365_json_add_string_member (builder, "contentType", value);
+}
+
+const gchar *
+e_o365_attachment_get_id (EO365Attachment *attachment)
+{
+       return e_o365_json_get_string_member (attachment, "id", NULL);
+}
+
+gboolean
+e_o365_attachment_get_is_inline (EO365Attachment *attachment)
+{
+       return e_o365_json_get_boolean_member (attachment, "isInline", FALSE);
+}
+
+void
+e_o365_attachment_add_is_inline (JsonBuilder *builder,
+                                gboolean value)
+{
+       e_o365_json_add_boolean_member (builder, "isInline", value);
+}
+
+time_t
+e_o365_attachment_get_last_modified_date_time (EO365Attachment *attachment)
+{
+       return e_o365_get_date_time_offset_member (attachment, "lastModifiedDateTime");
+}
+
+void
+e_o365_attachment_add_last_modified_date_time (JsonBuilder *builder,
+                                              time_t value)
+{
+       e_o365_add_date_time_offset_member (builder, "lastModifiedDateTime", value);
+}
+
+const gchar *
+e_o365_attachment_get_name (EO365Attachment *attachment)
+{
+       return e_o365_json_get_string_member (attachment, "name", NULL);
+}
+
+void
+e_o365_attachment_add_name (JsonBuilder *builder,
+                           const gchar *value)
+{
+       e_o365_json_add_string_member (builder, "name", value);
+}
+
+gint32
+e_o365_attachment_get_size (EO365Attachment *attachment)
+{
+       return (gint32) e_o365_json_get_int_member (attachment, "size", -1);
+}
+
+void
+e_o365_attachment_add_size (JsonBuilder *builder,
+                           gint32 value)
+{
+       e_o365_json_add_int_member (builder, "size", value);
+}
+
+/* https://docs.microsoft.com/en-us/graph/api/resources/fileattachment?view=graph-rest-1.0 */
+
+const gchar * /* base64-encoded */
+e_o365_file_attachment_get_content_bytes (EO365Attachment *attachment)
+{
+       return e_o365_json_get_string_member (attachment, "contentBytes", NULL);
+}
+
+void
+e_o365_file_attachment_add_content_bytes (JsonBuilder *builder,
+                                         const gchar *base64_value)
+{
+       e_o365_json_add_string_member (builder, "contentBytes", base64_value);
+}
+
+const gchar *
+e_o365_file_attachment_get_content_id (EO365Attachment *attachment)
+{
+       return e_o365_json_get_string_member (attachment, "contentId", NULL);
+}
+
+void
+e_o365_file_attachment_add_content_id (JsonBuilder *builder,
+                                      const gchar *value)
+{
+       e_o365_json_add_string_member (builder, "contentId", value);
+}
diff --git a/src/Office365/common/e-o365-json-utils.h b/src/Office365/common/e-o365-json-utils.h
index fb562900..be33cf8b 100644
--- a/src/Office365/common/e-o365-json-utils.h
+++ b/src/Office365/common/e-o365-json-utils.h
@@ -23,12 +23,21 @@
 
 G_BEGIN_DECLS
 
-typedef enum _EO365InferenceClassificationType {
-       E_O365_INFERENCE_CLASSIFICATION_NOT_SET,
-       E_O365_INFERENCE_CLASSIFICATION_UNKNOWN,
-       E_O365_INFERENCE_CLASSIFICATION_FOCUSED,
-       E_O365_INFERENCE_CLASSIFICATION_OTHER
-} EO365InferenceClassificationType;
+typedef enum _EO365AttachmentDataType {
+       E_O365_ATTACHMENT_DATA_TYPE_NOT_SET,
+       E_O365_ATTACHMENT_DATA_TYPE_UNKNOWN,
+       E_O365_ATTACHMENT_DATA_TYPE_FILE,
+       E_O365_ATTACHMENT_DATA_TYPE_ITEM,
+       E_O365_ATTACHMENT_DATA_TYPE_REFERENCE
+} EO365AttachmentDataType;
+
+typedef enum _EO365FollowupFlagStatusType {
+       E_O365_FOLLOWUP_FLAG_STATUS_NOT_SET,
+       E_O365_FOLLOWUP_FLAG_STATUS_UNKNOWN,
+       E_O365_FOLLOWUP_FLAG_STATUS_NOT_FLAGGED,
+       E_O365_FOLLOWUP_FLAG_STATUS_COMPLETE,
+       E_O365_FOLLOWUP_FLAG_STATUS_FLAGGED
+} EO365FollowupFlagStatusType;
 
 typedef enum _EO365ImportanceType {
        E_O365_IMPORTANCE_NOT_SET,
@@ -38,13 +47,12 @@ typedef enum _EO365ImportanceType {
        E_O365_IMPORTANCE_HIGH
 } EO365ImportanceType;
 
-typedef enum _EO365FollowupFlagStatusType {
-       E_O365_FOLLOWUP_FLAG_STATUS_NOT_SET,
-       E_O365_FOLLOWUP_FLAG_STATUS_UNKNOWN,
-       E_O365_FOLLOWUP_FLAG_STATUS_NOT_FLAGGED,
-       E_O365_FOLLOWUP_FLAG_STATUS_COMPLETE,
-       E_O365_FOLLOWUP_FLAG_STATUS_FLAGGED
-} EO365FollowupFlagStatusType;
+typedef enum _EO365InferenceClassificationType {
+       E_O365_INFERENCE_CLASSIFICATION_NOT_SET,
+       E_O365_INFERENCE_CLASSIFICATION_UNKNOWN,
+       E_O365_INFERENCE_CLASSIFICATION_FOCUSED,
+       E_O365_INFERENCE_CLASSIFICATION_OTHER
+} EO365InferenceClassificationType;
 
 typedef enum _EO365ItemBodyContentTypeType {
        E_O365_ITEM_BODY_CONTENT_TYPE_NOT_SET,
@@ -54,6 +62,7 @@ typedef enum _EO365ItemBodyContentTypeType {
 } EO365ItemBodyContentTypeType;
 
 /* Just for better readability */
+#define EO365Attachment                        JsonObject
 #define EO365Category                  JsonObject
 #define EO365DateTimeWithZone          JsonObject
 #define EO365FollowupFlag              JsonObject
@@ -65,26 +74,49 @@ typedef enum _EO365ItemBodyContentTypeType {
 
 JsonArray *    e_o365_json_get_array_member            (JsonObject *object,
                                                         const gchar *member_name);
+void           e_o365_json_begin_array_member          (JsonBuilder *builder,
+                                                        const gchar *member_name);
+void           e_o365_json_end_array_member            (JsonBuilder *builder);
 gboolean       e_o365_json_get_boolean_member          (JsonObject *object,
                                                         const gchar *member_name,
                                                         gboolean default_value);
+void           e_o365_json_add_boolean_member          (JsonBuilder *builder,
+                                                        const gchar *member_name,
+                                                        gboolean value);
 gdouble                e_o365_json_get_double_member           (JsonObject *object,
                                                         const gchar *member_name,
                                                         gdouble default_value);
+void           e_o365_json_add_double_member           (JsonBuilder *builder,
+                                                        const gchar *member_name,
+                                                        gdouble value);
 gint64         e_o365_json_get_int_member              (JsonObject *object,
                                                         const gchar *member_name,
                                                         gint64 default_value);
+void           e_o365_json_add_int_member              (JsonBuilder *builder,
+                                                        const gchar *member_name,
+                                                        gint64 value);
 gboolean       e_o365_json_get_null_member             (JsonObject *object,
                                                         const gchar *member_name,
                                                         gboolean default_value);
+void           e_o365_json_add_null_member             (JsonBuilder *builder,
+                                                        const gchar *member_name);
 JsonObject *   e_o365_json_get_object_member           (JsonObject *object,
                                                         const gchar *member_name);
+void           e_o365_json_begin_object_member         (JsonBuilder *builder,
+                                                        const gchar *member_name);
+void           e_o365_json_end_object_member           (JsonBuilder *builder);
 const gchar *  e_o365_json_get_string_member           (JsonObject *object,
                                                         const gchar *member_name,
                                                         const gchar *default_value);
+void           e_o365_json_add_string_member           (JsonBuilder *builder,
+                                                        const gchar *member_name,
+                                                        const gchar *value);
 
 time_t         e_o365_get_date_time_offset_member      (JsonObject *object,
                                                         const gchar *member_name);
+void           e_o365_add_date_time_offset_member      (JsonBuilder *builder,
+                                                        const gchar *member_name,
+                                                        time_t value);
 
 gboolean       e_o365_delta_is_removed_object          (JsonObject *object);
 
@@ -100,34 +132,72 @@ gint32            e_o365_mail_folder_get_child_folder_count
 gint32         e_o365_mail_folder_get_total_item_count (EO365MailFolder *folder);
 gint32         e_o365_mail_folder_get_unread_item_count(EO365MailFolder *folder);
 
-const gchar *  e_o365_recipient_get_address            (EO365Recipient *recipient);
 const gchar *  e_o365_recipient_get_name               (EO365Recipient *recipient);
+const gchar *  e_o365_recipient_get_address            (EO365Recipient *recipient);
+void           e_o365_add_recipient                    (JsonBuilder *builder,
+                                                        const gchar *member_name,
+                                                        const gchar *name,
+                                                        const gchar *address);
 
 time_t         e_o365_date_time_get_date_time          (EO365DateTimeWithZone *datetime);
 const gchar *  e_o365_date_time_get_time_zone          (EO365DateTimeWithZone *datetime);
+void           e_o365_add_date_time                    (JsonBuilder *builder,
+                                                        const gchar *member_name,
+                                                        time_t date_time,
+                                                        const gchar *zone);
 
 const gchar *  e_o365_internet_message_header_get_name (EO365InternetMessageHeader *header);
 const gchar *  e_o365_internet_message_header_get_value(EO365InternetMessageHeader *header);
+void           e_o365_add_internet_message_header      (JsonBuilder *builder,
+                                                        const gchar *name,
+                                                        const gchar *value);
 
 EO365DateTimeWithZone *
                e_o365_followup_flag_get_completed_date_time
                                                        (EO365FollowupFlag *flag);
+void           e_o365_followup_flag_add_completed_date_time
+                                                       (JsonBuilder *builder,
+                                                        time_t date_time,
+                                                        const gchar *zone);
 EO365DateTimeWithZone *
                e_o365_followup_flag_get_due_date_time  (EO365FollowupFlag *flag);
+void           e_o365_followup_flag_add_due_date_time  (JsonBuilder *builder,
+                                                        time_t date_time,
+                                                        const gchar *zone);
 EO365FollowupFlagStatusType
                e_o365_followup_flag_get_flag_status    (EO365FollowupFlag *flag);
+void           e_o365_followup_flag_add_flag_status    (JsonBuilder *builder,
+                                                        EO365FollowupFlagStatusType status);
 EO365DateTimeWithZone *
                e_o365_followup_flag_get_start_date_time(EO365FollowupFlag *flag);
+void           e_o365_followup_flag_add_start_date_time(JsonBuilder *builder,
+                                                        time_t date_time,
+                                                        const gchar *zone);
 
 const gchar *  e_o365_item_body_get_content            (EO365ItemBody *item_body);
 EO365ItemBodyContentTypeType
                e_o365_item_body_get_content_type       (EO365ItemBody *item_body);
+void           e_o365_add_item_body                    (JsonBuilder *builder,
+                                                        const gchar *member_name,
+                                                        EO365ItemBodyContentTypeType content_type,
+                                                        const gchar *content);
 
 JsonArray *    e_o365_mail_message_get_bcc_recipients  (EO365MailMessage *mail); /* EO365Recipient * */
+void           e_o365_mail_message_begin_bcc_recipients(JsonBuilder *builder);
+void           e_o365_mail_message_end_bcc_recipients  (JsonBuilder *builder);
 EO365ItemBody *        e_o365_mail_message_get_body            (EO365MailMessage *mail);
+void           e_o365_mail_message_add_body            (JsonBuilder *builder,
+                                                        EO365ItemBodyContentTypeType content_type,
+                                                        const gchar *content);
 const gchar *  e_o365_mail_message_get_body_preview    (EO365MailMessage *mail);
 JsonArray *    e_o365_mail_message_get_categories      (EO365MailMessage *mail); /* const gchar * */
+void           e_o365_mail_message_begin_categories    (JsonBuilder *builder);
+void           e_o365_mail_message_end_categories      (JsonBuilder *builder);
+void           e_o365_mail_message_add_category        (JsonBuilder *builder,
+                                                        const gchar *category);
 JsonArray *    e_o365_mail_message_get_cc_recipients   (EO365MailMessage *mail); /* EO365Recipient * */
+void           e_o365_mail_message_begin_cc_recipients (JsonBuilder *builder);
+void           e_o365_mail_message_end_cc_recipients   (JsonBuilder *builder);
 const gchar *  e_o365_mail_message_get_change_key      (EO365MailMessage *mail);
 const gchar *  e_o365_mail_message_get_conversation_id (EO365MailMessage *mail);
 JsonObject *   e_o365_mail_message_get_conversation_index
@@ -136,38 +206,104 @@ time_t           e_o365_mail_message_get_created_date_time
                                                        (EO365MailMessage *mail);
 EO365FollowupFlag *
                e_o365_mail_message_get_flag            (EO365MailMessage *mail);
+void           e_o365_mail_message_begin_flag          (JsonBuilder *builder);
+void           e_o365_mail_message_end_flag            (JsonBuilder *builder);
 EO365Recipient *
                e_o365_mail_message_get_from            (EO365MailMessage *mail);
+void           e_o365_mail_message_add_from            (JsonBuilder *builder,
+                                                        const gchar *name,
+                                                        const gchar *address);
 gboolean       e_o365_mail_message_get_has_attachments (EO365MailMessage *mail);
 const gchar *  e_o365_mail_message_get_id              (EO365MailMessage *mail);
 EO365ImportanceType
                e_o365_mail_message_get_importance      (EO365MailMessage *mail);
+void           e_o365_mail_message_add_importance      (JsonBuilder *builder,
+                                                        EO365ImportanceType importance);
 EO365InferenceClassificationType
                e_o365_mail_message_get_inference_classification
                                                        (EO365MailMessage *mail);
 JsonArray *    e_o365_mail_message_get_internet_message_headers
                                                        (EO365MailMessage *mail); /* 
EO365InternetMessageHeader * */
+void           e_o365_mail_message_begin_internet_message_headers
+                                                       (JsonBuilder *builder);
+void           e_o365_mail_message_end_internet_message_headers
+                                                       (JsonBuilder *builder);
 const gchar *  e_o365_mail_message_get_internet_message_id
                                                        (EO365MailMessage *mail);
+void           e_o365_mail_message_add_internet_message_id
+                                                       (JsonBuilder *builder,
+                                                        const gchar *message_id);
 gboolean       e_o365_mail_message_get_is_delivery_receipt_requested
                                                        (EO365MailMessage *mail);
+void           e_o365_mail_message_add_is_delivery_receipt_requested
+                                                       (JsonBuilder *builder,
+                                                        gboolean value);
 gboolean       e_o365_mail_message_get_is_draft        (EO365MailMessage *mail);
 gboolean       e_o365_mail_message_get_is_read         (EO365MailMessage *mail);
+void           e_o365_mail_message_add_is_read         (JsonBuilder *builder,
+                                                        gboolean value);
 gboolean       e_o365_mail_message_get_is_read_receipt_requested
                                                        (EO365MailMessage *mail);
+void           e_o365_mail_message_add_is_read_receipt_requested
+                                                       (JsonBuilder *builder,
+                                                        gboolean value);
 time_t         e_o365_mail_message_get_last_modified_date_time
                                                        (EO365MailMessage *mail);
 const gchar *  e_o365_mail_message_get_parent_folder_id(EO365MailMessage *mail);
 time_t         e_o365_mail_message_get_received_date_time
                                                        (EO365MailMessage *mail);
+void           e_o365_mail_message_add_received_date_time
+                                                       (JsonBuilder *builder,
+                                                        time_t value);
 JsonArray *    e_o365_mail_message_get_reply_to        (EO365MailMessage *mail); /* EO365Recipient * */
+void           e_o365_mail_message_begin_reply_to      (JsonBuilder *builder);
+void           e_o365_mail_message_end_reply_to        (JsonBuilder *builder);
 EO365Recipient *e_o365_mail_message_get_sender         (EO365MailMessage *mail);
+void           e_o365_mail_message_add_sender          (JsonBuilder *builder,
+                                                        const gchar *name,
+                                                        const gchar *address);
 time_t         e_o365_mail_message_get_sent_date_time  (EO365MailMessage *mail);
+void           e_o365_mail_message_add_sent_date_time  (JsonBuilder *builder,
+                                                        time_t value);
 const gchar *  e_o365_mail_message_get_subject         (EO365MailMessage *mail);
+void           e_o365_mail_message_add_subject         (JsonBuilder *builder,
+                                                        const gchar *subject);
 JsonArray *    e_o365_mail_message_get_to_recipients   (EO365MailMessage *mail); /* EO365Recipient * */
+void           e_o365_mail_message_begin_to_recipients (JsonBuilder *builder);
+void           e_o365_mail_message_end_to_recipients   (JsonBuilder *builder);
 EO365ItemBody *        e_o365_mail_message_get_unique_body     (EO365MailMessage *mail);
 const gchar *  e_o365_mail_message_get_web_link        (EO365MailMessage *mail);
 
+EO365AttachmentDataType
+               e_o365_attachment_get_data_type         (EO365Attachment *attachment);
+void           e_o365_attachment_begin_attachment      (JsonBuilder *builder,
+                                                        EO365AttachmentDataType data_type);
+void           e_o365_attachment_end_attachment        (JsonBuilder *builder);
+const gchar *  e_o365_attachment_get_content_type      (EO365Attachment *attachment);
+void           e_o365_attachment_add_content_type      (JsonBuilder *builder,
+                                                        const gchar *value);
+const gchar *  e_o365_attachment_get_id                (EO365Attachment *attachment);
+gboolean       e_o365_attachment_get_is_inline         (EO365Attachment *attachment);
+void           e_o365_attachment_add_is_inline         (JsonBuilder *builder,
+                                                        gboolean value);
+time_t         e_o365_attachment_get_last_modified_date_time
+                                                       (EO365Attachment *attachment);
+void           e_o365_attachment_add_last_modified_date_time
+                                                       (JsonBuilder *builder,
+                                                        time_t value);
+const gchar *  e_o365_attachment_get_name              (EO365Attachment *attachment);
+void           e_o365_attachment_add_name              (JsonBuilder *builder,
+                                                        const gchar *value);
+gint32         e_o365_attachment_get_size              (EO365Attachment *attachment);
+void           e_o365_attachment_add_size              (JsonBuilder *builder,
+                                                        gint32 value);
+const gchar *  e_o365_file_attachment_get_content_bytes(EO365Attachment *attachment); /* base64-encoded */
+void           e_o365_file_attachment_add_content_bytes(JsonBuilder *builder,
+                                                        const gchar *base64_value);
+const gchar *  e_o365_file_attachment_get_content_id   (EO365Attachment *attachment);
+void           e_o365_file_attachment_add_content_id   (JsonBuilder *builder,
+                                                        const gchar *value);
+
 G_END_DECLS
 
 #endif /* E_O365_JSON_UTILS_H */
diff --git a/src/Office365/common/e-oauth2-service-office365.c 
b/src/Office365/common/e-oauth2-service-office365.c
index 57a05dc9..e83534dd 100644
--- a/src/Office365/common/e-oauth2-service-office365.c
+++ b/src/Office365/common/e-oauth2-service-office365.c
@@ -254,7 +254,6 @@ eos_office365_prepare_authentication_uri_query (EOAuth2Service *service,
        e_oauth2_service_util_set_to_form (uri_query, "response_type", "code");
        e_oauth2_service_util_set_to_form (uri_query, "scope", OFFICE365_SCOPE);
        e_oauth2_service_util_set_to_form (uri_query, "response_mode", "query");
-       e_oauth2_service_util_set_to_form (uri_query, "login_hint", NULL);
 }
 
 static gboolean



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