[geary/mjog/crash-sending-multipart: 3/3] Geary.RFC822.Message: Fix `get_body()` critical with multipart messages




commit ec4c6d39899bf65e14b3abafe093e08777782658
Author: Michael Gratton <mike vee net>
Date:   Mon Aug 3 14:13:34 2020 +1000

    Geary.RFC822.Message: Fix `get_body()` critical with multipart messages
    
    Fixes a bad assumption MR !534 made about GMime structure and causing
    a crash on sending multipart messages. Add unit tests to cover this
    case.

 src/engine/rfc822/rfc822-message.vala       | 24 +++++++++++--
 test/engine/api/geary-email-test.vala       | 52 +++++++++++++++++++++++++++++
 test/engine/rfc822/rfc822-message-test.vala | 30 +++++++++++++++--
 test/meson.build                            |  3 +-
 test/test-engine.vala                       |  1 +
 5 files changed, 103 insertions(+), 7 deletions(-)
---
diff --git a/src/engine/rfc822/rfc822-message.vala b/src/engine/rfc822/rfc822-message.vala
index 61a0f3d66..e995f430b 100644
--- a/src/engine/rfc822/rfc822-message.vala
+++ b/src/engine/rfc822/rfc822-message.vala
@@ -624,11 +624,29 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
     }
 
     /**
-     * Returns the possibly body of the message.
+     * Returns the body of the message.
      */
     public Text get_body() {
-        GMime.Part part = (GMime.Part) this.message.get_mime_part();
-        return new Text.from_gmime(part.get_content().get_stream());
+        Text? body = null;
+        GMime.Object? gmime = this.message.get_mime_part();
+        if (gmime != null) {
+            var stream = new GMime.StreamMem();
+
+            // GMime doens't support writing content-only via the
+            // public API, so suppress all headers in the message
+            // instead.
+            GMime.FormatOptions options = Geary.RFC822.get_format_options().clone();
+            GMime.HeaderList headers = message.get_header_list();
+            int count = headers.get_count();
+            for (int i = 0; i < count; i++) {
+                options.add_hidden_header(headers.get_header_at(i).get_name());
+            }
+            gmime.write_to_stream(options, stream);
+            body = new Text.from_gmime(stream);
+        } else {
+            body = new Text(Memory.EmptyBuffer.instance);
+        }
+        return body;
     }
 
     /**
diff --git a/test/engine/api/geary-email-test.vala b/test/engine/api/geary-email-test.vala
new file mode 100644
index 000000000..d52f929d3
--- /dev/null
+++ b/test/engine/api/geary-email-test.vala
@@ -0,0 +1,52 @@
+/*
+ * Copyright © 2020 Michael Gratton <mike vee net>
+ *
+ * This software is licensed under the GNU Lesser General Public License
+ * (version 2.1 or later). See the COPYING file in this distribution.
+ */
+
+class Geary.EmailTest: TestCase {
+
+
+    private const string BASIC_TEXT_PLAIN = "basic-text-plain.eml";
+    private const string BASIC_MULTIPART_ALTERNATIVE =
+        "basic-multipart-alternative.eml";
+
+
+    public EmailTest() {
+        base("Geary.EmailTest");
+        add_test("email_from_basic_message", email_from_basic_message);
+        add_test("email_from_multipart", email_from_multipart);
+    }
+
+    public void email_from_basic_message() throws GLib.Error {
+        var message = resource_to_message(BASIC_TEXT_PLAIN);
+        var email = new Email.from_message(new MockEmailIdentifer(0), message);
+
+        assert_non_null(email);
+        assert_non_null(email.subject);
+        assert_equal(email.subject.to_string(), "Re: Basic text/plain message");
+    }
+
+    public void email_from_multipart() throws GLib.Error {
+        var message = resource_to_message(BASIC_MULTIPART_ALTERNATIVE);
+        var email = new Email.from_message(new MockEmailIdentifer(0), message);
+
+        assert_non_null(email);
+        assert_non_null(email.subject);
+        assert_equal(email.subject.to_string(), "Re: Basic text/html message");
+    }
+
+    private RFC822.Message resource_to_message(string path) throws GLib.Error {
+        GLib.File resource =
+            GLib.File.new_for_uri(RESOURCE_URI).resolve_relative_path(path);
+
+        uint8[] contents;
+        resource.load_contents(null, out contents, null);
+
+        return new RFC822.Message.from_buffer(
+            new Geary.Memory.ByteBuffer(contents, contents.length)
+        );
+    }
+
+}
diff --git a/test/engine/rfc822/rfc822-message-test.vala b/test/engine/rfc822/rfc822-message-test.vala
index 2d151ba39..083ea3d43 100644
--- a/test/engine/rfc822/rfc822-message-test.vala
+++ b/test/engine/rfc822/rfc822-message-test.vala
@@ -54,7 +54,8 @@ This is the second line.
         add_test("multipart_alternative_as_html",
                  multipart_alternative_as_html);
         add_test("get_header", get_header);
-        add_test("get_body", get_body);
+        add_test("get_body_single_part", get_body_single_part);
+        add_test("get_body_multipart", get_body_multipart);
         add_test("get_preview", get_preview);
         add_test("get_recipients", get_recipients);
         add_test("get_searchable_body", get_searchable_body);
@@ -185,10 +186,33 @@ This is the second line.
         assert(header.get_header("From") == "Alice <alice example net>");
     }
 
-    public void get_body() throws GLib.Error {
+    public void get_body_single_part() throws GLib.Error {
         Message message = resource_to_message(BASIC_TEXT_PLAIN);
         Text body = message.get_body();
-        assert(body.buffer.to_string().replace("\r", "") == BASIC_PLAIN_BODY);
+        assert_string(
+            body.buffer.to_string().replace("\r", "")
+        ).contains(
+            // should contain the body
+            BASIC_PLAIN_BODY
+        ).not_contains(
+            // should not contain headers (like the subject)
+            "Re: Basic text/plain message"
+        );
+    }
+
+    public void get_body_multipart() throws GLib.Error {
+        Message message = resource_to_message(BASIC_MULTIPART_ALTERNATIVE);
+        Text body = message.get_body();
+
+        assert_string(
+            body.buffer.to_string().replace("\r", "")
+        ).contains(
+            // should contain  the body
+            BASIC_PLAIN_BODY
+        ).not_contains(
+            // should not contain headers (like the subject)
+            "Re: Basic text/html message"
+        );
     }
 
     public void get_preview() throws GLib.Error {
diff --git a/test/meson.build b/test/meson.build
index aec82e3f4..4ccfbd0f5 100644
--- a/test/meson.build
+++ b/test/meson.build
@@ -21,10 +21,11 @@ geary_test_engine_sources = [
 
   'engine/api/geary-account-information-test.vala',
   'engine/api/geary-attachment-test.vala',
+  'engine/api/geary-composed-email-test.vala',
+  'engine/api/geary-email-test.vala',
   'engine/api/geary-engine-test.vala',
   'engine/api/geary-folder-path-test.vala',
   'engine/api/geary-service-information-test.vala',
-  'engine/api/geary-composed-email-test.vala',
   'engine/app/app-conversation-test.vala',
   'engine/app/app-conversation-monitor-test.vala',
   'engine/app/app-conversation-set-test.vala',
diff --git a/test/test-engine.vala b/test/test-engine.vala
index 82d728b0f..483163deb 100644
--- a/test/test-engine.vala
+++ b/test/test-engine.vala
@@ -90,6 +90,7 @@ int main(string[] args) {
     // last
     engine.add_suite(new Geary.RFC822.MessageTest().suite);
     engine.add_suite(new Geary.String.Test().suite);
+    engine.add_suite(new Geary.EmailTest().suite);
     engine.add_suite(new Geary.ComposedEmailTest().suite);
 
     /*


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