[geary/wip/362-utf8-attachments: 4/8] Make adding MIME parts to a new RFC822 message async



commit 7cc3e6633e11a23701c443b18b238112aece79a6
Author: Michael Gratton <mike vee net>
Date:   Thu Jul 18 16:27:33 2019 +1000

    Make adding MIME parts to a new RFC822 message async
    
    This will allow us to schedule some long-running work on a background
    thread.

 src/client/composer/composer-widget.vala           |   4 +-
 src/engine/api/geary-composed-email.vala           |   7 +-
 .../imap-engine/imap-engine-generic-account.vala   |   7 +-
 src/engine/rfc822/rfc822-message.vala              | 154 +++++++++++++++------
 src/mailer/main.vala                               |   4 +-
 test/integration/smtp/client-session.vala          |  15 +-
 6 files changed, 136 insertions(+), 55 deletions(-)
---
diff --git a/src/client/composer/composer-widget.vala b/src/client/composer/composer-widget.vala
index c71afbff..c744668b 100644
--- a/src/client/composer/composer-widget.vala
+++ b/src/client/composer/composer-widget.vala
@@ -1473,7 +1473,9 @@ public class ComposerWidget : Gtk.EventBox, Geary.BaseInterface {
             try {
                 Geary.ComposedEmail draft = yield get_composed_email(null, true);
                 this.draft_manager.update(
-                    draft.to_rfc822_message(), this.draft_flags, null
+                    yield draft.to_rfc822_message(null, null),
+                    this.draft_flags,
+                    null
                 );
             } catch (Error err) {
                 GLib.message("Unable to save draft: %s", err.message);
diff --git a/src/engine/api/geary-composed-email.vala b/src/engine/api/geary-composed-email.vala
index 48467539..5b9be5f2 100644
--- a/src/engine/api/geary-composed-email.vala
+++ b/src/engine/api/geary-composed-email.vala
@@ -61,8 +61,11 @@ public class Geary.ComposedEmail : BaseObject {
         this.body_html = body_html;
     }
 
-    public Geary.RFC822.Message to_rfc822_message(string? message_id = null) {
-        return new RFC822.Message.from_composed_email(this, message_id);
+    public async Geary.RFC822.Message to_rfc822_message(string? message_id,
+                                                        GLib.Cancellable? cancellable) {
+        return yield new RFC822.Message.from_composed_email(
+            this, message_id, cancellable
+        );
     }
 
     /**
diff --git a/src/engine/imap-engine/imap-engine-generic-account.vala 
b/src/engine/imap-engine/imap-engine-generic-account.vala
index 2f0f5f45..19c97f67 100644
--- a/src/engine/imap-engine/imap-engine-generic-account.vala
+++ b/src/engine/imap-engine/imap-engine-generic-account.vala
@@ -504,9 +504,10 @@ private abstract class Geary.ImapEngine.GenericAccount : Geary.Account {
         string domain = composed.sender != null
             ? composed.sender.domain
             : this.information.primary_mailbox.domain;
-        Geary.RFC822.Message rfc822 = new Geary.RFC822.Message.from_composed_email(
-            composed, GMime.utils_generate_message_id(domain)
-        );
+        Geary.RFC822.Message rfc822 =
+            yield new Geary.RFC822.Message.from_composed_email(
+                composed, GMime.utils_generate_message_id(domain), cancellable
+            );
 
         yield this.smtp.queue_email(rfc822, cancellable);
     }
diff --git a/src/engine/rfc822/rfc822-message.vala b/src/engine/rfc822/rfc822-message.vala
index e140fad3..0550aa80 100644
--- a/src/engine/rfc822/rfc822-message.vala
+++ b/src/engine/rfc822/rfc822-message.vala
@@ -125,7 +125,9 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
         stock_from_gmime();
     }
 
-    public Message.from_composed_email(Geary.ComposedEmail email, string? message_id) {
+    public async Message.from_composed_email(Geary.ComposedEmail email,
+                                             string? message_id,
+                                             GLib.Cancellable? cancellable) {
         this.message = new GMime.Message(true);
 
         // Required headers
@@ -205,12 +207,26 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
 
         // Body: text format (optional)
         if (email.body_text != null) {
-            GMime.Part? body_text = body_data_to_part(email.body_text.data,
-                                                      ref body_charset,
-                                                      ref body_encoding,
-                                                      "text/plain",
-                                                      true);
-            body_parts.add(body_text);
+            GMime.Part? body_text = null;
+            try {
+                body_text = yield body_data_to_part(
+                    email.body_text.data,
+                    null,
+                    null,
+                    "text/plain",
+                    true,
+                    cancellable
+                );
+            } catch (GLib.Error err) {
+                warning("Error creating text body part: %s", err.message);
+            }
+            if (body_text != null) {
+                body_charset = body_text.get_content_type().get_parameter(
+                    "charset"
+                );
+                body_encoding = body_text.get_content_encoding();
+                body_parts.add(body_text);
+            }
         }
 
         // Body: HTML format (also optional)
@@ -233,9 +249,20 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
             foreach (string cid in email.cid_files.keys) {
                 if (email.contains_inline_img_src(CID_URL_PREFIX + cid)) {
                     File file = email.cid_files[cid];
-                    GMime.Object? inline_part = get_file_part(
-                        file, Geary.Mime.DispositionType.INLINE
-                    );
+                    GMime.Object? inline_part = null;
+                    try {
+                        inline_part = yield get_file_part(
+                            file,
+                            Geary.Mime.DispositionType.INLINE,
+                            cancellable
+                        );
+                    } catch (GLib.Error err) {
+                        warning(
+                            "Error creating CID part %s: %s",
+                            file.get_path(),
+                            err.message
+                        );
+                    }
                     if (inline_part != null) {
                         inline_part.set_content_id(cid);
                         related_parts.add(inline_part);
@@ -259,9 +286,20 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
 
                     if (email.replace_inline_img_src(name,
                                                      CID_URL_PREFIX + cid)) {
-                        GMime.Object? inline_part = get_file_part(
-                            inline_files[name], Geary.Mime.DispositionType.INLINE
-                        );
+                        GMime.Object? inline_part = null;
+                        try {
+                            inline_part = yield get_file_part(
+                                inline_files[name],
+                                Geary.Mime.DispositionType.INLINE,
+                                cancellable
+                            );
+                        } catch (GLib.Error err) {
+                            warning(
+                                "Error creating inline file part %s: %s",
+                                inline_files[name].get_path(),
+                                err.message
+                            );
+                        }
                         if (inline_part != null) {
                             inline_part.set_content_id(cid);
                             related_parts.add(inline_part);
@@ -270,11 +308,19 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
                 }
             }
 
-            GMime.Object? body_html = body_data_to_part(email.body_html.data,
-                                                        ref body_charset,
-                                                        ref body_encoding,
-                                                        "text/html",
-                                                        false);
+            GMime.Object? body_html = null;
+            try {
+                body_html = yield body_data_to_part(
+                    email.body_html.data,
+                    body_charset,
+                    body_encoding,
+                    "text/html",
+                    false,
+                    cancellable
+                );
+            } catch (GLib.Error err) {
+                warning("Error creating html body part: %s", err.message);
+            }
 
             // Assemble the HTML and inline images into a related
             // part, if needed
@@ -297,11 +343,23 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
 
         Gee.List<GMime.Object> attachment_parts = new Gee.LinkedList<GMime.Object>();
         foreach (File file in email.attached_files) {
-            GMime.Object? attachment_part = get_file_part(
-                file, Geary.Mime.DispositionType.ATTACHMENT
-            );
-            if (attachment_part != null)
+            GMime.Object? attachment_part = null;
+            try {
+                attachment_part = yield get_file_part(
+                    file,
+                    Geary.Mime.DispositionType.ATTACHMENT,
+                    cancellable
+                );
+            } catch (GLib.Error err) {
+                warning(
+                    "Error creating attachment file part %s: %s",
+                    file.get_path(),
+                    err.message
+                );
+            }
+            if (attachment_part != null) {
                 attachment_parts.add(attachment_part);
+            }
         }
         GMime.Object? attachment_part = coalesce_parts(attachment_parts, "mixed");
         if (attachment_part != null)
@@ -366,29 +424,39 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
         }
     }
 
-    private GMime.Part? get_file_part(File file,
-                                      Geary.Mime.DispositionType disposition) {
-        if (!file.query_exists())
-            return null;
-
-        FileInfo file_info;
-        try {
-            file_info = file.query_info(FileAttribute.STANDARD_CONTENT_TYPE, FileQueryInfoFlags.NONE);
-        } catch (Error err) {
-            debug("Error querying info from file: %s", err.message);
-            return null;
-        }
+    private async GMime.Part? get_file_part(File file,
+                                            Geary.Mime.DispositionType disposition,
+                                            GLib.Cancellable cancellable)
+        throws GLib.Error {
+        FileInfo file_info = yield file.query_info_async(
+            FileAttribute.STANDARD_CONTENT_TYPE,
+            FileQueryInfoFlags.NONE
+        );
 
         GMime.Part part = new GMime.Part();
         part.set_disposition(disposition.serialize());
         part.set_filename(file.get_basename());
-        part.set_content_type(new GMime.ContentType.from_string(file_info.get_content_type()));
+        part.set_content_type(
+            new GMime.ContentType.from_string(file_info.get_content_type())
+        );
 
         // This encoding is the initial encoding of the stream.
         GMime.StreamGIO stream = new GMime.StreamGIO(file);
         stream.set_owner(false);
-        part.set_content_object(new GMime.DataWrapper.with_stream(stream, GMime.ContentEncoding.BINARY));
-        part.set_content_encoding(Geary.RFC822.Utils.get_best_encoding(stream));
+        part.set_content_object(
+            new GMime.DataWrapper.with_stream(
+                stream, GMime.ContentEncoding.BINARY
+            )
+        );
+        part.set_content_encoding(
+            yield Utils.get_best_encoding(
+                stream,
+                // Determine this from the MTA's capabilities at send
+                // time
+                GMime.EncodingConstraint.7BIT,
+                cancellable
+            )
+        );
         return part;
     }
 
@@ -993,11 +1061,13 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
      * clean ASCII. So if we have guessed both already for a plain
      * text body, it will still apply for any HTML part.
      */
-    private GMime.Part body_data_to_part(uint8[] content,
-                                         ref string? charset,
-                                         ref GMime.ContentEncoding? encoding,
-                                         string content_type,
-                                         bool is_flowed) {
+    private async GMime.Part body_data_to_part(uint8[] content,
+                                               string? charset,
+                                               GMime.ContentEncoding? encoding,
+                                               string content_type,
+                                               bool is_flowed,
+                                               GLib.Cancellable? cancellable)
+        throws GLib.Error {
         GMime.Stream content_stream = new GMime.StreamMem.with_buffer(content);
         if (charset == null) {
             charset = Geary.RFC822.Utils.get_best_charset(content_stream);
diff --git a/src/mailer/main.vala b/src/mailer/main.vala
index 72cddd82..a639c1f7 100644
--- a/src/mailer/main.vala
+++ b/src/mailer/main.vala
@@ -43,7 +43,9 @@ async void main_async() throws Error {
                 composed_email.body_html = contents;
             }
 
-            msg = new Geary.RFC822.Message.from_composed_email(composed_email, null);
+            msg = yield new Geary.RFC822.Message.from_composed_email(
+                composed_email, null, null
+            );
         }
 
         stdout.printf("\n\n%s\n\n", msg.to_string());
diff --git a/test/integration/smtp/client-session.vala b/test/integration/smtp/client-session.vala
index 0c869a27..575a1240 100644
--- a/test/integration/smtp/client-session.vala
+++ b/test/integration/smtp/client-session.vala
@@ -91,13 +91,15 @@ class Integration.Smtp.ClientSession : TestCase {
                 null, this.config.credentials.user
             );
 
-        Geary.RFC822.Message message = new_message(
+        this.new_message.begin(
             return_path,
             new Geary.RFC822.MailboxAddress(
                 "Geary integration test",
                 this.config.credentials.user
-            )
+            ),
+            async_complete_full
         );
+        Geary.RFC822.Message message = new_message.end(async_result());
 
         this.session.send_email_async.begin(
             return_path,
@@ -115,8 +117,8 @@ class Integration.Smtp.ClientSession : TestCase {
         this.session.login_async.end(async_result());
     }
 
-    private Geary.RFC822.Message new_message(Geary.RFC822.MailboxAddress from,
-                                             Geary.RFC822.MailboxAddress to) {
+    private async Geary.RFC822.Message new_message(Geary.RFC822.MailboxAddress from,
+                                                   Geary.RFC822.MailboxAddress to) {
         Geary.ComposedEmail composed = new Geary.ComposedEmail(
             new GLib.DateTime.now_local(),
             new Geary.RFC822.MailboxAddresses.single(from),
@@ -128,9 +130,10 @@ class Integration.Smtp.ClientSession : TestCase {
             null
         );
 
-        return new Geary.RFC822.Message.from_composed_email(
+        return yield new Geary.RFC822.Message.from_composed_email(
             composed,
-            GMime.utils_generate_message_id(from.domain)
+            GMime.utils_generate_message_id(from.domain),
+            null
         );
     }
 


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