[evolution-ews] Microsoft365: Implement raw MIME message upload



commit 394a1097e026f2d27fb3ad478a448281a6dcba12
Author: Milan Crha <mcrha redhat com>
Date:   Wed Oct 5 09:51:50 2022 +0200

    Microsoft365: Implement raw MIME message upload
    
    The message is still marked as draft (isDraft flag) on the server.
    There does not seem to be a way to unset it. There also does not work
    upload to the specified folder, the server errors out, thus the code
    uploads to the Drafts folder and then moves the message to the specified
    folder. Maybe that plays its role with the isDraft flag as well.

 src/Microsoft365/camel/camel-m365-folder.c  | 28 ------------
 src/Microsoft365/camel/camel-m365-utils.c   | 54 +++++++++--------------
 src/Microsoft365/common/e-m365-connection.c | 67 +++++++++++++++++++++++++++++
 src/Microsoft365/common/e-m365-connection.h |  8 ++++
 4 files changed, 95 insertions(+), 62 deletions(-)
---
diff --git a/src/Microsoft365/camel/camel-m365-folder.c b/src/Microsoft365/camel/camel-m365-folder.c
index 14a97604..8ff8ca52 100644
--- a/src/Microsoft365/camel/camel-m365-folder.c
+++ b/src/Microsoft365/camel/camel-m365-folder.c
@@ -574,29 +574,6 @@ m365_folder_append_message_sync (CamelFolder *folder,
                                 GCancellable *cancellable,
                                 GError **error)
 {
-       /* 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;
        CamelM365Store *m365_store;
        EM365Connection *cnc = NULL;
@@ -628,11 +605,6 @@ m365_folder_append_message_sync (CamelFolder *folder,
                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 a Microsoft 365 account from another account. Only messages from 
the same account can be moved/copied between the Microsoft 365 folders."));
-       return FALSE;
-#endif
 }
 
 static gboolean
diff --git a/src/Microsoft365/camel/camel-m365-utils.c b/src/Microsoft365/camel/camel-m365-utils.c
index ebe17e82..46e72cad 100644
--- a/src/Microsoft365/camel/camel-m365-utils.c
+++ b/src/Microsoft365/camel/camel-m365-utils.c
@@ -934,56 +934,42 @@ camel_m365_utils_create_message_sync (EM365Connection *cnc,
                                      GError **error)
 {
        EM365MailMessage *appended_message = NULL;
-       GSList *attachments = NULL;
-       JsonBuilder *builder;
        gboolean success;
 
        g_return_val_if_fail (E_IS_M365_CONNECTION (cnc), FALSE);
        g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (message), FALSE);
 
-       builder = json_builder_new_immutable ();
-
-       e_m365_json_begin_object_member (builder, NULL);
-
-       if (!camel_m365_utils_fill_message_object_sync (builder, message, info, NULL, NULL, FALSE, 
&attachments, cancellable, error)) {
-               g_slist_free_full (attachments, g_object_unref);
-               g_object_unref (builder);
-
-               return FALSE;
-       }
-
-       e_m365_json_end_object_member (builder);
-
-       success = e_m365_connection_create_mail_message_sync (cnc, NULL, folder_id, builder, 
&appended_message, cancellable, error);
+       /* Cannot upload message directly to the folder_id, because the server returns:
+          {"error":{"code":"UnableToDeserializePostBody","message":"were unable to deserialize "}}
+          thus upload to the Drafts folder and then move the message to the right place. */
+       success = e_m365_connection_upload_mail_message_sync (cnc, NULL, NULL, message, &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_m365_mail_message_get_id (appended_message);
-
-               if (out_appended_id)
-                       *out_appended_id = g_strdup (message_id);
-
-               for (link = attachments; link && success; link = g_slist_next (link)) {
-                       CamelDataWrapper *dw = link->data;
+               GSList src_ids = { 0, }, *des_ids = NULL;
+               const gchar *id;
 
-                       builder = json_builder_new_immutable ();
+               id = e_m365_mail_message_get_id (appended_message);
+               g_warn_if_fail (id != NULL);
 
-                       m365_utils_add_attachment_object (builder, dw, cancellable);
+               src_ids.next = NULL;
+               src_ids.data = (gpointer) id;
 
-                       success = e_m365_connection_add_mail_message_attachment_sync (cnc, NULL, message_id, 
builder, NULL, cancellable, error);
+               /* Sadly, the isDraft flag cannot be unset, thus every uploaded message
+                  is a draft for the server, which is quite bad */
+               if (e_m365_connection_copy_move_mail_messages_sync (cnc, NULL, &src_ids, folder_id, FALSE, 
&des_ids, cancellable, error)) {
+                       if (des_ids) {
+                               if (out_appended_id)
+                                       *out_appended_id = g_strdup ((const gchar *) des_ids->data);
 
-                       g_object_unref (builder);
+                               g_slist_free_full (des_ids, (GDestroyNotify) camel_pstring_free);
+                       } else {
+                               g_warning ("Moved message to '%s', but did not return new message id", 
folder_id);
+                       }
                }
        }
 
-       g_slist_free_full (attachments, g_object_unref);
-
        if (appended_message)
                json_object_unref (appended_message);
 
diff --git a/src/Microsoft365/common/e-m365-connection.c b/src/Microsoft365/common/e-m365-connection.c
index cb7c6093..fc5e7083 100644
--- a/src/Microsoft365/common/e-m365-connection.c
+++ b/src/Microsoft365/common/e-m365-connection.c
@@ -2511,6 +2511,73 @@ e_m365_connection_create_mail_message_sync (EM365Connection *cnc,
        return success;
 }
 
+gboolean
+e_m365_connection_upload_mail_message_sync (EM365Connection *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 */
+                                           CamelMimeMessage *mime_message,
+                                           EM365MailMessage **out_created_message, /* free with 
json_object_unref() */
+                                           GCancellable *cancellable,
+                                           GError **error)
+{
+       SoupMessage *message;
+       GInputStream *input_stream;
+       CamelStream *mem_stream, *filter_stream;
+       CamelMimeFilter *base64_filter;
+       GByteArray *byte_array;
+       gboolean success;
+       gchar *uri;
+
+       g_return_val_if_fail (E_IS_M365_CONNECTION (cnc), FALSE);
+       g_return_val_if_fail (CAMEL_IS_MIME_MESSAGE (mime_message), FALSE);
+       g_return_val_if_fail (out_created_message != NULL, FALSE);
+
+       uri = e_m365_connection_construct_uri (cnc, TRUE, user_override, E_M365_API_V1_0, NULL,
+               folder_id ? "mailFolders" : "messages",
+               folder_id,
+               folder_id ? "messages" : NULL,
+               NULL);
+
+       message = m365_connection_new_soup_message (SOUP_METHOD_POST, uri, CSM_DEFAULT, error);
+
+       if (!message) {
+               g_free (uri);
+
+               return FALSE;
+       }
+
+       g_free (uri);
+
+       mem_stream = camel_stream_mem_new ();
+       filter_stream = camel_stream_filter_new (mem_stream);
+       base64_filter = camel_mime_filter_basic_new (CAMEL_MIME_FILTER_BASIC_BASE64_ENC);
+       camel_stream_filter_add (CAMEL_STREAM_FILTER (filter_stream), base64_filter);
+       g_clear_object (&base64_filter);
+
+       if (camel_data_wrapper_write_to_stream_sync (CAMEL_DATA_WRAPPER (mime_message), filter_stream, 
cancellable, error) == -1) {
+               g_clear_object (&filter_stream);
+               g_clear_object (&mem_stream);
+               g_clear_object (&message);
+
+               return FALSE;
+       }
+
+       byte_array = camel_stream_mem_get_byte_array (CAMEL_STREAM_MEM (mem_stream));
+
+       input_stream = g_memory_input_stream_new_from_data (byte_array->data, byte_array->len, NULL);
+
+       e_soup_session_util_set_message_request_body (message, "text/plain", input_stream, byte_array->len);
+
+       success = m365_connection_send_request_sync (cnc, message, e_m365_read_json_object_response_cb, NULL, 
out_created_message, cancellable, error);
+
+       g_clear_object (&input_stream);
+       g_clear_object (&filter_stream);
+       g_clear_object (&mem_stream);
+       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
diff --git a/src/Microsoft365/common/e-m365-connection.h b/src/Microsoft365/common/e-m365-connection.h
index 33fb831c..8ee1c2b0 100644
--- a/src/Microsoft365/common/e-m365-connection.h
+++ b/src/Microsoft365/common/e-m365-connection.h
@@ -247,6 +247,14 @@ gboolean   e_m365_connection_create_mail_message_sync
                                                 EM365MailMessage **out_created_message, /* free with 
json_object_unref() */
                                                 GCancellable *cancellable,
                                                 GError **error);
+gboolean       e_m365_connection_upload_mail_message_sync
+                                               (EM365Connection *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 */
+                                                CamelMimeMessage *mime_message,
+                                                EM365MailMessage **out_created_message, /* free with 
json_object_unref() */
+                                                GCancellable *cancellable,
+                                                GError **error);
 gboolean       e_m365_connection_add_mail_message_attachment_sync
                                                (EM365Connection *cnc,
                                                 const gchar *user_override, /* for which user, NULL to use 
the account user */


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