From 29c564a64fe7a59be23111f18b799ec58931faae Mon Sep 17 00:00:00 2001 From: Milan Crha Date: Mon, 26 Apr 2021 11:50:10 +0200 Subject: [PATCH] I#1471 - Composer: Lost attachments in reply/forward of an encrypted message Closes https://gitlab.gnome.org/GNOME/evolution/-/issues/1471 --- src/composer/e-msg-composer.c | 64 ++++++++++++++++++++++++++++++ src/composer/e-msg-composer.h | 6 +++ src/mail/em-composer-utils.c | 32 +++++++-------- src/mail/em-utils.c | 73 ++++++++++++++++++++++++++++------- src/mail/em-utils.h | 11 +++++- 5 files changed, 154 insertions(+), 32 deletions(-) diff --git a/src/composer/e-msg-composer.c b/src/composer/e-msg-composer.c index 492170e7b1..8303470809 100644 --- a/src/composer/e-msg-composer.c +++ b/src/composer/e-msg-composer.c @@ -3255,6 +3255,70 @@ e_msg_composer_add_message_attachments (EMsgComposer *composer, composer, (CamelMultipart *) wrapper, just_inlines, 0); } +/** + * e_msg_composer_add_attachments_from_part_list: + * @composer: the composer to add the attachments to + * @part_list: an #EMailPartList with parts used to format the message + * @just_inlines: whether to attach all attachments or just add inline images + * + * Walk through all the parts in @part_list and add them to the @composer. + * + * Since: 3.42 + */ +void +e_msg_composer_add_attachments_from_part_list (EMsgComposer *composer, + EMailPartList *part_list, + gboolean just_inlines) +{ + EHTMLEditor *editor; + GQueue queue = G_QUEUE_INIT; + GList *link; + + g_return_if_fail (E_IS_MSG_COMPOSER (composer)); + + if (!part_list) + return; + + editor = e_msg_composer_get_editor (composer); + + e_mail_part_list_queue_parts (part_list, NULL, &queue); + + for (link = g_queue_peek_head_link (&queue); link; link = g_list_next (link)) { + EMailPart *part = link->data; + CamelMimePart *mime_part; + CamelContentType *content_type; + + if (!e_mail_part_get_is_attachment (part)) + continue; + + mime_part = e_mail_part_ref_mime_part (part); + if (!mime_part) + continue; + + content_type = camel_mime_part_get_content_type (mime_part); + if (!content_type) + continue; + + if (!just_inlines && + camel_content_type_is (content_type, "text", "*") && + camel_mime_part_get_filename (mime_part) == NULL) { + /* Do nothing if this is a text/anything without a + * filename, otherwise attach it too. */ + } else if (camel_content_type_is (content_type, "image", "*") && ( + camel_mime_part_get_content_id (mime_part) || + camel_mime_part_get_content_location (mime_part))) { + e_html_editor_add_cid_part (editor, mime_part); + } else if (!just_inlines) { + e_msg_composer_attach (composer, mime_part); + } + + g_object_unref (mime_part); + } + + while (!g_queue_is_empty (&queue)) + g_object_unref (g_queue_pop_head (&queue)); +} + static void handle_multipart_signed (EMsgComposer *composer, CamelMultipart *multipart, diff --git a/src/composer/e-msg-composer.h b/src/composer/e-msg-composer.h index f385889ba4..2e4657b35d 100644 --- a/src/composer/e-msg-composer.h +++ b/src/composer/e-msg-composer.h @@ -29,6 +29,7 @@ #include #include +#include /* Standard GObject macros */ #define E_TYPE_MSG_COMPOSER \ @@ -186,6 +187,11 @@ void e_msg_composer_add_message_attachments (EMsgComposer *composer, CamelMimeMessage *message, gboolean just_inlines); +void e_msg_composer_add_attachments_from_part_list + (EMsgComposer *composer, + EMailPartList *part_list, + gboolean just_inlines); + void e_msg_composer_request_close (EMsgComposer *composer); gboolean e_msg_composer_can_close (EMsgComposer *composer, diff --git a/src/mail/em-composer-utils.c b/src/mail/em-composer-utils.c index 944347064a..58c95017d1 100644 --- a/src/mail/em-composer-utils.c +++ b/src/mail/em-composer-utils.c @@ -2486,6 +2486,7 @@ forward_non_attached (EMsgComposer *composer, EMailForwardStyle style) { CamelSession *session; + EMailPartList *part_list = NULL; gchar *text, *forward, *subject; guint32 validity_found = 0; guint32 flags; @@ -2508,17 +2509,11 @@ forward_non_attached (EMsgComposer *composer, g_free (subject); forward = quoting_text (QUOTING_FORWARD, composer); - text = em_utils_message_to_html (session, message, forward, flags, NULL, NULL, NULL, &validity_found); + text = em_utils_message_to_html_ex (session, message, forward, flags, NULL, NULL, NULL, &validity_found, &part_list); + + e_msg_composer_add_attachments_from_part_list (composer, part_list, FALSE); if (text != NULL) { - CamelDataWrapper *content; - - content = camel_medium_get_content (CAMEL_MEDIUM (message)); - - if (CAMEL_IS_MULTIPART (content)) - e_msg_composer_add_message_attachments ( - composer, message, FALSE); - e_msg_composer_set_body_text (composer, text, TRUE); emu_add_composer_references_from_message (composer, message); @@ -2533,6 +2528,7 @@ forward_non_attached (EMsgComposer *composer, } g_clear_object (&session); + g_clear_object (&part_list); g_free (forward); } @@ -3597,7 +3593,8 @@ static void composer_set_body (EMsgComposer *composer, CamelMimeMessage *message, EMailReplyStyle style, - EMailPartList *parts_list) + EMailPartList *parts_list, + EMailPartList **out_used_part_list) { gchar *text, *credits, *original; ESource *identity_source; @@ -3625,9 +3622,9 @@ composer_set_body (EMsgComposer *composer, break; case E_MAIL_REPLY_STYLE_OUTLOOK: original = quoting_text (QUOTING_ORIGINAL, composer); - text = em_utils_message_to_html ( + text = em_utils_message_to_html_ex ( session, message, original, E_MAIL_FORMATTER_QUOTE_FLAG_HEADERS | keep_sig_flag, - parts_list, NULL, NULL, &validity_found); + parts_list, NULL, NULL, &validity_found, out_used_part_list); e_msg_composer_set_body_text (composer, text, TRUE); g_free (text); g_free (original); @@ -3643,9 +3640,9 @@ composer_set_body (EMsgComposer *composer, g_clear_object (&identity_source); - text = em_utils_message_to_html ( + text = em_utils_message_to_html_ex ( session, message, credits, E_MAIL_FORMATTER_QUOTE_FLAG_CITE | keep_sig_flag, - parts_list, NULL, NULL, &validity_found); + parts_list, NULL, NULL, &validity_found, out_used_part_list); g_free (credits); e_msg_composer_set_body_text (composer, text, TRUE); g_free (text); @@ -4484,6 +4481,7 @@ em_utils_reply_to_message (EMsgComposer *composer, EShell *shell; ESourceMailCompositionReplyStyle prefer_reply_style = E_SOURCE_MAIL_COMPOSITION_REPLY_STYLE_DEFAULT; ESource *source; + EMailPartList *used_part_list = NULL; EContentEditor *cnt_editor; gchar *identity_uid = NULL, *identity_name = NULL, *identity_address = NULL; guint32 flags; @@ -4566,7 +4564,6 @@ em_utils_reply_to_message (EMsgComposer *composer, } reply_setup_composer (composer, message, identity_uid, identity_name, identity_address, to, cc, folder, message_uid, postto); - e_msg_composer_add_message_attachments (composer, message, TRUE); if (postto) g_object_unref (postto); @@ -4617,7 +4614,10 @@ em_utils_reply_to_message (EMsgComposer *composer, break; } - composer_set_body (composer, message, style, parts_list); + composer_set_body (composer, message, style, parts_list, &used_part_list); + + e_msg_composer_add_attachments_from_part_list (composer, used_part_list, TRUE); + g_clear_object (&used_part_list); if (folder) emu_set_source_headers (composer, folder, message_uid, flags); diff --git a/src/mail/em-utils.c b/src/mail/em-utils.c index acc8c70854..1be0785b12 100644 --- a/src/mail/em-utils.c +++ b/src/mail/em-utils.c @@ -1223,12 +1223,13 @@ is_only_text_part_in_this_level (GList *parts, /** * em_utils_message_to_html: * @session: a #CamelSession - * @message: - * @credits: - * @flags: EMFormatQuote flags - * @source: - * @append: Text to append, can be NULL. - * @validity_found: if not NULL, then here will be set what validities + * @message: a #CamelMimeMessage + * @credits: (nullable): credits attribution string when quoting, or %NULL + * @flags: the %EMFormatQuote flags + * @part_list: (nullable): an #EMailPartList + * @prepend: (nulalble): text to prepend, or %NULL + * @append: (nullable): text to append, or %NULL + * @validity_found: (nullable): if not %NULL, then here will be set what validities * had been found during message conversion. Value is a bit OR * of EM_FORMAT_VALIDITY_FOUND_* constants. * @@ -1236,16 +1237,55 @@ is_only_text_part_in_this_level (GList *parts, * string is given. * * Return value: The html version as a NULL terminated string. + * + * See: em_utils_message_to_html_ex **/ gchar * em_utils_message_to_html (CamelSession *session, CamelMimeMessage *message, const gchar *credits, guint32 flags, - EMailPartList *parts_list, + EMailPartList *part_list, const gchar *prepend, const gchar *append, EMailPartValidityFlags *validity_found) +{ + return em_utils_message_to_html_ex (session, message, credits, flags, part_list, prepend, append, validity_found, NULL); +} + +/** + * em_utils_message_to_html_ex: + * @session: a #CamelSession + * @message: a #CamelMimeMessage + * @credits: (nullable): credits attribution string when quoting, or %NULL + * @flags: the %EMFormatQuote flags + * @part_list: (nullable): an #EMailPartList + * @prepend: (nulalble): text to prepend, or %NULL + * @append: (nullable): text to append, or %NULL + * @validity_found: (nullable): if not %NULL, then here will be set what validities + * had been found during message conversion. Value is a bit OR + * of EM_FORMAT_VALIDITY_FOUND_* constants. + * @out_part_list: (nullable): if not %NULL, sets it to the part list being + * used to generate the body. Unref it with g_object_unref(), + * when no longer needed. + * + * Convert a message to html, quoting if the @credits attribution + * string is given. + * + * Return value: The html version as a NULL terminated string. + * + * Since: 3.42 + **/ +gchar * +em_utils_message_to_html_ex (CamelSession *session, + CamelMimeMessage *message, + const gchar *credits, + guint32 flags, + EMailPartList *part_list, + const gchar *prepend, + const gchar *append, + EMailPartValidityFlags *validity_found, + EMailPartList **out_part_list) { EMailFormatter *formatter; EMailParser *parser = NULL; @@ -1271,7 +1311,7 @@ em_utils_message_to_html (CamelSession *session, e_mail_formatter_update_style (formatter, gtk_widget_get_state_flags (GTK_WIDGET (window))); - if (parts_list == NULL) { + if (part_list == NULL) { GSettings *settings; gchar *charset; @@ -1285,13 +1325,13 @@ em_utils_message_to_html (CamelSession *session, g_free (charset); parser = e_mail_parser_new (session); - parts_list = e_mail_parser_parse_sync (parser, NULL, NULL, message, NULL); + part_list = e_mail_parser_parse_sync (parser, NULL, NULL, message, NULL); } else { - g_object_ref (parts_list); + g_object_ref (part_list); } /* Return all found validities and possibly show hidden prefer-plain part */ - e_mail_part_list_queue_parts (parts_list, NULL, &queue); + e_mail_part_list_queue_parts (part_list, NULL, &queue); head = g_queue_peek_head_link (&queue); for (link = head; link != NULL; link = g_list_next (link)) { @@ -1332,16 +1372,19 @@ em_utils_message_to_html (CamelSession *session, stream, prepend, strlen (prepend), NULL, NULL, NULL); e_mail_formatter_format_sync ( - formatter, parts_list, stream, 0, + formatter, part_list, stream, 0, E_MAIL_FORMATTER_MODE_PRINTING, NULL); g_object_unref (formatter); if (hidden_text_html_part != NULL) hidden_text_html_part->is_hidden = TRUE; - g_object_unref (parts_list); - if (parser != NULL) - g_object_unref (parser); + if (out_part_list) + *out_part_list = part_list; + else + g_object_unref (part_list); + + g_clear_object (&parser); if (append != NULL && *append != '\0') g_output_stream_write_all ( diff --git a/src/mail/em-utils.h b/src/mail/em-utils.h index a258e3430c..ee1bebbf49 100644 --- a/src/mail/em-utils.h +++ b/src/mail/em-utils.h @@ -76,10 +76,19 @@ gchar * em_utils_message_to_html (CamelSession *session, CamelMimeMessage *msg, const gchar *credits, guint32 flags, - struct _EMailPartList *parts_list, + struct _EMailPartList *part_list, const gchar *prepend, const gchar *append, EMailPartValidityFlags *validity_found); +gchar * em_utils_message_to_html_ex (CamelSession *session, + CamelMimeMessage *message, + const gchar *credits, + guint32 flags, + EMailPartList *part_list, + const gchar *prepend, + const gchar *append, + EMailPartValidityFlags *validity_found, + EMailPartList **out_part_list); void em_utils_empty_trash (GtkWidget *parent, EMailSession *session);