From cebbf6fdfcc7eb58aa8db7d3274b2e463058d592 Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Thu, 7 Dec 2017 14:35:45 +0100 Subject: [PATCH] Drag&drop of multiple messages from message list to composer fails One message works fine, but multiple messages failed due to changed format of x-uid-list content. --- src/mail/em-utils.c | 82 +++++++-- src/mail/em-utils.h | 14 ++ src/modules/mail/e-mail-attachment-handler.c | 184 ++++++++----------- 3 files changed, 158 insertions(+), 122 deletions(-) diff --git a/src/mail/em-utils.c b/src/mail/em-utils.c index 0d6edb40d3..bab5a9c7cb 100644 --- a/src/mail/em-utils.c +++ b/src/mail/em-utils.c @@ -742,22 +742,27 @@ em_utils_selection_set_uidlist (GtkSelectionData *selection_data, } /** - * em_utils_selection_get_uidlist: - * @data: selection data + * em_utils_selection_uidlist_foreach_sync: + * @selection_data: a #GtkSelectionData with x-uid-list content * @session: an #EMailSession - * @move: do we delete the messages. + * @func: a function to call for each UID and its folder + * @user_data: user data for @func + * @cancellable: optional #GCancellable object, or %NULL + * @error: return location for a #GError, or %NULL * - * Convert a uid list into a copy/move operation. + * Calls @func for each folder and UID provided in @selection_data. * * Warning: Could take some time to run. + * + * Since: 3.28 **/ void -em_utils_selection_get_uidlist (GtkSelectionData *selection_data, - EMailSession *session, - CamelFolder *dest, - gint move, - GCancellable *cancellable, - GError **error) +em_utils_selection_uidlist_foreach_sync (GtkSelectionData *selection_data, + EMailSession *session, + EMUtilsUIDListFunc func, + gpointer user_data, + GCancellable *cancellable, + GError **error) { /* format: "uri1\0uid1\0uri2\0uid2\0...\0urin\0uidn\0" */ gchar *inptr, *inend; @@ -768,10 +773,12 @@ em_utils_selection_get_uidlist (GtkSelectionData *selection_data, GHashTable *uids_by_uri; GHashTableIter iter; gpointer key, value; + gboolean can_continue = TRUE; GError *local_error = NULL; g_return_if_fail (selection_data != NULL); g_return_if_fail (E_IS_MAIL_SESSION (session)); + g_return_if_fail (func != NULL); data = gtk_selection_data_get_data (selection_data); length = gtk_selection_data_get_length (selection_data); @@ -823,14 +830,12 @@ em_utils_selection_get_uidlist (GtkSelectionData *selection_data, const gchar *uri = key; GPtrArray *uids = value; - if (!local_error) { + if (!local_error && can_continue) { /* FIXME e_mail_session_uri_to_folder_sync() may block. */ folder = e_mail_session_uri_to_folder_sync ( session, uri, 0, cancellable, &local_error); if (folder) { - /* FIXME camel_folder_transfer_messages_to_sync() may block. */ - camel_folder_transfer_messages_to_sync ( - folder, uids, dest, move, NULL, cancellable, &local_error); + can_continue = func (folder, uids, user_data, cancellable, &local_error); g_object_unref (folder); } } @@ -845,6 +850,55 @@ em_utils_selection_get_uidlist (GtkSelectionData *selection_data, g_propagate_error (error, local_error); } +struct UIDListData { + CamelFolder *dest; + gboolean move; +}; + +static gboolean +uidlist_move_uids_cb (CamelFolder *folder, + const GPtrArray *uids, + gpointer user_data, + GCancellable *cancellable, + GError **error) +{ + struct UIDListData *uld = user_data; + + g_return_val_if_fail (uld != NULL, FALSE); + + /* FIXME camel_folder_transfer_messages_to_sync() may block. */ + return camel_folder_transfer_messages_to_sync ( + folder, (GPtrArray *) uids, uld->dest, uld->move, NULL, cancellable, error); +} + +/** + * em_utils_selection_get_uidlist: + * @data: selection data + * @session: an #EMailSession + * @move: do we delete the messages. + * + * Convert a uid list into a copy/move operation. + * + * Warning: Could take some time to run. + **/ +void +em_utils_selection_get_uidlist (GtkSelectionData *selection_data, + EMailSession *session, + CamelFolder *dest, + gint move, + GCancellable *cancellable, + GError **error) +{ + struct UIDListData uld; + + g_return_if_fail (CAMEL_IS_FOLDER (dest)); + + uld.dest = dest; + uld.move = move; + + em_utils_selection_uidlist_foreach_sync (selection_data, session, uidlist_move_uids_cb, &uld, cancellable, error); +} + /** * em_utils_build_export_basename: * @folder: a #CamelFolder where the message belongs diff --git a/src/mail/em-utils.h b/src/mail/em-utils.h index 6a0787c559..d415659643 100644 --- a/src/mail/em-utils.h +++ b/src/mail/em-utils.h @@ -57,6 +57,20 @@ void em_utils_selection_get_uidlist (GtkSelectionData *data, EMailSession *sessi void em_utils_selection_set_urilist (GtkSelectionData *data, CamelFolder *folder, GPtrArray *uids); void em_utils_selection_get_urilist (GtkSelectionData *data, CamelFolder *folder); +/* Return TRUE to continue, FALSE to stop further processing */ +typedef gboolean (* EMUtilsUIDListFunc) (CamelFolder *folder, + const GPtrArray *uids, + gpointer user_data, + GCancellable *cancellable, + GError **error); + +void em_utils_selection_uidlist_foreach_sync (GtkSelectionData *selection_data, + EMailSession *session, + EMUtilsUIDListFunc func, + gpointer user_data, + GCancellable *cancellable, + GError **error); + /* FIXME: should this have an override charset? */ gchar * em_utils_message_to_html (CamelSession *session, CamelMimeMessage *msg, diff --git a/src/modules/mail/e-mail-attachment-handler.c b/src/modules/mail/e-mail-attachment-handler.c index e0825e3eb4..a035434d78 100644 --- a/src/modules/mail/e-mail-attachment-handler.c +++ b/src/modules/mail/e-mail-attachment-handler.c @@ -27,6 +27,7 @@ #include "mail/e-mail-backend.h" #include "mail/e-mail-reader.h" #include "mail/em-composer-utils.h" +#include "mail/em-utils.h" #define E_MAIL_ATTACHMENT_HANDLER_GET_PRIVATE(obj) \ (G_TYPE_INSTANCE_GET_PRIVATE \ @@ -533,6 +534,33 @@ exit: gtk_drag_finish (drag_context, success, FALSE, time); } +static gboolean +gather_x_uid_list_messages_cb (CamelFolder *folder, + const GPtrArray *uids, + gpointer user_data, + GCancellable *cancellable, + GError **error) +{ + GSList **pmessages = user_data; + guint ii; + + g_return_val_if_fail (CAMEL_IS_FOLDER (folder), FALSE); + g_return_val_if_fail (uids != NULL, FALSE); + g_return_val_if_fail (pmessages != NULL, FALSE); + + for (ii = 0; ii < uids->len; ii++) { + CamelMimeMessage *message; + + message = camel_folder_get_message_sync (folder, uids->pdata[ii], cancellable, error); + if (!message) + return FALSE; + + *pmessages = g_slist_prepend (*pmessages, message); + } + + return TRUE; +} + static void mail_attachment_handler_x_uid_list (EAttachmentView *view, GdkDragContext *drag_context, @@ -546,20 +574,14 @@ mail_attachment_handler_x_uid_list (EAttachmentView *view, static GdkAtom atom = GDK_NONE; EMailAttachmentHandlerPrivate *priv; CamelDataWrapper *wrapper; - CamelMimeMessage *message; CamelMultipart *multipart; CamelMimePart *mime_part; - CamelFolder *folder = NULL; EAttachment *attachment; EAttachmentStore *store; EMailSession *session; - GPtrArray *uids; - const gchar *data; - const gchar *cp, *end; + GSList *messages = NULL, *link; gchar *description; gpointer parent; - gint length; - guint ii; GError *local_error = NULL; if (G_UNLIKELY (atom == GDK_NONE)) @@ -574,124 +596,73 @@ mail_attachment_handler_x_uid_list (EAttachmentView *view, parent = gtk_widget_get_toplevel (GTK_WIDGET (view)); parent = gtk_widget_is_toplevel (parent) ? parent : NULL; - uids = g_ptr_array_new (); - - data = (const gchar *) gtk_selection_data_get_data (selection_data); - length = gtk_selection_data_get_length (selection_data); - - /* The UID list is delimited by NUL characters. - * Brilliant. So we can't use g_strsplit(). */ - - cp = data; - end = data + length; - - while (cp < end) { - const gchar *start = cp; - - while (cp < end && *cp != '\0') - cp++; - - /* Skip the first string. */ - if (start > data) - g_ptr_array_add (uids, g_strndup (start, cp - start)); - - cp++; - } - - if (uids->len == 0) - goto exit; - session = e_mail_backend_get_session (priv->backend); - /* The first string is the folder URI. */ - /* FIXME Not passing a GCancellable here. */ - folder = e_mail_session_uri_to_folder_sync ( - session, data, 0, NULL, &local_error); - if (folder == NULL) + em_utils_selection_uidlist_foreach_sync (selection_data, session, + gather_x_uid_list_messages_cb, &messages, NULL, &local_error); + + if (local_error || !messages) goto exit; /* Handle one message. */ - if (uids->len == 1) { - const gchar *message_uid; - - message_uid = g_ptr_array_index (uids, 0); - - /* FIXME Not passing a GCancellable here. */ - message = camel_folder_get_message_sync ( - folder, message_uid, NULL, &local_error); - if (message == NULL) - goto exit; - - attachment = e_attachment_new_for_message (message); + if (!messages->next) { + attachment = e_attachment_new_for_message (messages->data); e_attachment_store_add_attachment (store, attachment); e_attachment_load_async ( attachment, (GAsyncReadyCallback) call_attachment_load_handle_error, parent ? g_object_ref (parent) : NULL); g_object_unref (attachment); + } else { + gint n_messages = g_slist_length (messages); - g_object_unref (message); - goto exit; - } + messages = g_slist_reverse (messages); - /* Build a multipart/digest message out of the UIDs. */ + /* Build a multipart/digest message out of the UIDs. */ + multipart = camel_multipart_new (); + wrapper = CAMEL_DATA_WRAPPER (multipart); + camel_data_wrapper_set_mime_type (wrapper, "multipart/digest"); + camel_multipart_set_boundary (multipart, NULL); - multipart = camel_multipart_new (); - wrapper = CAMEL_DATA_WRAPPER (multipart); - camel_data_wrapper_set_mime_type (wrapper, "multipart/digest"); - camel_multipart_set_boundary (multipart, NULL); - - for (ii = 0; ii < uids->len; ii++) { - /* FIXME Not passing a GCancellable here. */ - message = camel_folder_get_message_sync ( - folder, uids->pdata[ii], NULL, &local_error); - if (message == NULL) { - g_object_unref (multipart); - goto exit; + for (link = messages; link; link = g_slist_next (link)) { + mime_part = camel_mime_part_new (); + wrapper = CAMEL_DATA_WRAPPER (link->data); + camel_mime_part_set_disposition (mime_part, "inline"); + camel_medium_set_content ( + CAMEL_MEDIUM (mime_part), wrapper); + camel_mime_part_set_content_type (mime_part, "message/rfc822"); + camel_multipart_add_part (multipart, mime_part); + g_object_unref (mime_part); } mime_part = camel_mime_part_new (); - wrapper = CAMEL_DATA_WRAPPER (message); - camel_mime_part_set_disposition (mime_part, "inline"); - camel_medium_set_content ( - CAMEL_MEDIUM (mime_part), wrapper); - camel_mime_part_set_content_type (mime_part, "message/rfc822"); - camel_multipart_add_part (multipart, mime_part); - g_object_unref (mime_part); + wrapper = CAMEL_DATA_WRAPPER (multipart); + camel_medium_set_content (CAMEL_MEDIUM (mime_part), wrapper); - g_object_unref (message); + description = g_strdup_printf ( + ngettext ( + "%d attached message", + "%d attached messages", + n_messages), + n_messages); + camel_mime_part_set_description (mime_part, description); + g_free (description); + + attachment = e_attachment_new (); + e_attachment_set_mime_part (attachment, mime_part); + e_attachment_store_add_attachment (store, attachment); + e_attachment_load_async ( + attachment, (GAsyncReadyCallback) + call_attachment_load_handle_error, parent ? g_object_ref (parent) : NULL); + g_object_unref (attachment); + + g_object_unref (mime_part); + g_object_unref (multipart); } - mime_part = camel_mime_part_new (); - wrapper = CAMEL_DATA_WRAPPER (multipart); - camel_medium_set_content (CAMEL_MEDIUM (mime_part), wrapper); - - description = g_strdup_printf ( - ngettext ( - "%d attached message", - "%d attached messages", - uids->len), - uids->len); - camel_mime_part_set_description (mime_part, description); - g_free (description); - - attachment = e_attachment_new (); - e_attachment_set_mime_part (attachment, mime_part); - e_attachment_store_add_attachment (store, attachment); - e_attachment_load_async ( - attachment, (GAsyncReadyCallback) - call_attachment_load_handle_error, parent ? g_object_ref (parent) : NULL); - g_object_unref (attachment); - - g_object_unref (mime_part); - g_object_unref (multipart); - -exit: + exit: if (local_error != NULL) { - const gchar *folder_name = data; - - if (folder != NULL) - folder_name = camel_folder_get_display_name (folder); + const gchar *folder_name = (const gchar *) gtk_selection_data_get_data (selection_data); e_alert_run_dialog_for_args ( parent, "mail-composer:attach-nomessages", @@ -700,10 +671,7 @@ exit: g_clear_error (&local_error); } - if (folder != NULL) - g_object_unref (folder); - - g_ptr_array_free (uids, TRUE); + g_slist_free_full (messages, g_object_unref); g_signal_stop_emission_by_name (view, "drag-data-received"); }