sup

A curses threads-with-tags style email client

sup.git

git clone https://supmua.dev/git/sup/
commit d02f27488918e848112e527499f83c66130c0782
parent e2bdf6ac790fc2926ee0d8da2685ba2cadca253b
Author: Dan Callaghan <djc@djc.id.au>
Date:   Sun,  7 Jul 2024 16:48:05 +1000

don't add multiple contradictory Content-Transfer-Encoding headers

Fixes #502.

Diffstat:
M Manifest.txt | 1 +
M lib/sup/modes/edit_message_mode.rb | 4 ++--
A test/unit/test_edit_message_mode.rb | 69 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 72 insertions(+), 2 deletions(-)
diff --git a/Manifest.txt b/Manifest.txt
@@ -165,6 +165,7 @@ test/test_messages_dir.rb
 test/test_yaml_regressions.rb
 test/unit/service/test_label_service.rb
 test/unit/test_contact.rb
+test/unit/test_edit_message_mode.rb
 test/unit/test_horizontal_selector.rb
 test/unit/test_locale_fiddler.rb
 test/unit/test_person.rb
diff --git a/lib/sup/modes/edit_message_mode.rb b/lib/sup/modes/edit_message_mode.rb
@@ -716,10 +716,10 @@ private
     ## encode to quoted-printable for all text/* MIME types,
     ## use base64 otherwise
     if msg_part.header["Content-Type"] =~ /text\/.*/
-      msg_part.header["Content-Transfer-Encoding"] = 'quoted-printable'
+      msg_part.header.set "Content-Transfer-Encoding", "quoted-printable"
       msg_part.body = [msg_part.body].pack('M')
     else
-      msg_part.header["Content-Transfer-Encoding"] = 'base64'
+      msg_part.header.set "Content-Transfer-Encoding", "base64"
       msg_part.body = [msg_part.body].pack('m')
     end
     msg_part
diff --git a/test/unit/test_edit_message_mode.rb b/test/unit/test_edit_message_mode.rb
@@ -0,0 +1,69 @@
+require "test_helper"
+
+require "sup"
+
+class DummySelector
+  attr_accessor :val
+  def initialize val
+    @val = val
+  end
+end
+
+class DummyCryptoManager
+  def have_crypto?; true; end
+  def sign from, to, payload
+    envelope = RMail::Message.new
+    envelope.header["Content-Type"] = "multipart/signed; protocol=testdummy"
+    envelope.add_part payload
+    envelope
+  end
+end
+
+class TestEditMessageMode < Minitest::Test
+  def setup
+    $config = {}
+    @path = Dir.mktmpdir
+    Redwood::HookManager.init File.join(@path, "hooks")
+    Redwood::AccountManager.init :default => {name: "test", email: "sender@example.invalid"}
+    Redwood::CryptoManager.instance_variable_set :@instance, DummyCryptoManager.new
+  end
+
+  def teardown
+    Redwood::CryptoManager.deinstantiate!
+    Redwood::AccountManager.deinstantiate!
+    Redwood::HookManager.deinstantiate!
+    FileUtils.rm_r @path
+    $config = nil
+  end
+
+  def test_attachment_content_transfer_encoding_signed
+    ## RMail::Message#make_attachment will choose
+    ## Content-Transfer-Encoding: 8bit for a CSV file.
+    attachment_filename = File.join @path, "dummy.csv"
+    ## Include some high bytes in the attachment contents in order to
+    ## exercise quote-printable transfer encoding.
+    File.write attachment_filename, "löl,\ntest,\n"
+
+    opts = {
+      :header => {
+        "From" => "sender@example.invalid",
+        "To" => "recip@example.invalid",
+      },
+      :attachments => {
+        "dummy.csv" => RMail::Message.make_file_attachment(attachment_filename),
+      },
+    }
+    mode = Redwood::EditMessageMode.new opts
+    mode.instance_variable_set :@crypto_selector, DummySelector.new(:sign)
+
+    msg = mode.send :build_message, Time.now
+    ## The outermost message is a (fake) multipart/signed created by DummyCryptoManager#send.
+    ## Inside that we have our inline message at index 0 and CSV attachment at index 1.
+    attachment = msg.part(0).part(1)
+    ## The attachment should have been re-encoded as quoted-printable for GPG signing.
+    assert_equal "l=C3=B6l,\ntest,\n", attachment.body
+    ## There shouldn't be multiple Content-Transfer-Encoding headers.
+    ## This was: https://github.com/sup-heliotrope/sup/issues/502
+    assert_equal ["quoted-printable"], attachment.header.fetch_all("Content-Transfer-Encoding")
+  end
+end