From 6fb365ebd45e283f770cde341c6332af66051ab6 Mon Sep 17 00:00:00 2001
From: Michael Gratton <mike@vee.net>
Date: Sun, 27 Sep 2020 23:37:50 +1000
Subject: [PATCH 030/124] Geary.RFC822.Message: Fix plain text file attachment
 line ending conversion

Since RFC822 requires CRLF for line endings, it's not possible for a MUA
to determine the correct line ending for text attachments that use the
default or quoted-printable transfer encoding.

As such, always use a binary encoding for all non-body message parts
(which for now means always use Base64) so that line endings are
explicitly encoded and hence always decoded correctly.

Fixes #1001
---
 src/engine/rfc822/rfc822-message.vala | 65 +++++++++++++--------------
 1 file changed, 30 insertions(+), 35 deletions(-)

diff --git a/src/engine/rfc822/rfc822-message.vala b/src/engine/rfc822/rfc822-message.vala
index 51563caf..e6cc17a4 100644
--- a/src/engine/rfc822/rfc822-message.vala
+++ b/src/engine/rfc822/rfc822-message.vala
@@ -494,7 +494,7 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
             FileQueryInfoFlags.NONE
         );
 
-        GMime.Part part = new GMime.Part.with_type("text", "plain");
+        GMime.Part part = new GMime.Part();
         part.set_disposition(disposition.serialize());
         part.set_filename(file.get_basename());
 
@@ -504,10 +504,26 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
         );
         part.set_content_type(content_type);
 
+        // Always use a binary encoding since even when attaching
+        // text/plain parts, the line ending must always be preserved
+        // and this is not possible without a binary encoding. See
+        // https://gitlab.gnome.org/GNOME/geary/-/issues/1001
+        //
+        // TODO: The actual content encoding should be set based on
+        // the IMAP/SMTP server's supported encoding. For example, if
+        // 8-bit or binary is supported, then those should be used
+        // instead of Base64.
+        part.set_content_encoding(BASE64);
+
         GMime.StreamGIO stream = new GMime.StreamGIO(file);
         stream.set_owner(false);
+        part.set_content(
+            new GMime.DataWrapper.with_stream(
+                stream, GMime.ContentEncoding.BINARY
+            )
+        );
 
-        return yield finalise_attachment_part(stream, part, content_type, cancellable);
+        return part;
     }
 
     /**
@@ -540,50 +556,29 @@ public class Geary.RFC822.Message : BaseObject, EmailHeaderSet {
                 );
         }
 
-        GMime.Part part = new GMime.Part.with_type("text", "plain");
+        GMime.Part part = new GMime.Part();
         part.set_disposition(disposition.serialize());
         part.set_filename(basename);
         part.set_content_type(content_type);
 
-        GMime.StreamMem stream = Utils.create_stream_mem(buffer);
-
-        return yield finalise_attachment_part(stream, part, content_type, cancellable);
-    }
-
-    /**
-     * Set encoding and content object on GMime part
-     */
-    private async GMime.Part finalise_attachment_part(GMime.Stream stream,
-                                                      GMime.Part part,
-                                                      GMime.ContentType content_type,
-                                                      GLib.Cancellable? cancellable)
-        throws GLib.Error {
-
-        // Text parts should be scanned fully to determine best
-        // (i.e. most compact) transport encoding to use, but
-        // that's usually fine since they tend to be
-        // small. Non-text parts are nearly always going to be
-        // binary, so we just assume they require Base64.
+        // Always use a binary encoding since even when attaching
+        // text/plain parts, the line ending must always be preserved
+        // and this is not possible without a binary encoding. See
+        // https://gitlab.gnome.org/GNOME/geary/-/issues/1001
         //
-        // XXX We should be setting the content encoding lazily
-        // though because if sending via a MTA that supports 8-bit
-        // or binary transfer modes, we can avoid using a content
-        // encoding altogether.
-        GMime.ContentEncoding encoding = BASE64;
-        if (content_type.is_type("text", Mime.ContentType.WILDCARD)) {
-            encoding = yield Utils.get_best_encoding(
-                stream,
-                GMime.EncodingConstraint.7BIT,
-                cancellable
-            );
-        }
+        // TODO: The actual content encoding should be set based on
+        // the IMAP/SMTP server's supported encoding. For example, if
+        // 8-bit or binary is supported, then those should be used
+        // instead of Base64.
+        part.set_content_encoding(BASE64);
 
-        part.set_content_encoding(encoding);
+        GMime.StreamMem stream = Utils.create_stream_mem(buffer);
         part.set_content(
             new GMime.DataWrapper.with_stream(
                 stream, GMime.ContentEncoding.BINARY
             )
         );
+
         return part;
     }
 
-- 
2.29.2

