sup 1.2 → 1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (70) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/checks.yml +16 -2
  3. data/.gitmodules +1 -0
  4. data/History.txt +16 -0
  5. data/Manifest.txt +12 -1
  6. data/contrib/nix/Gemfile +2 -0
  7. data/contrib/nix/Gemfile.lock +37 -16
  8. data/contrib/nix/gem-install-shell.nix +2 -0
  9. data/contrib/nix/gemset.nix +83 -31
  10. data/contrib/nix/ruby2.4-Gemfile.lock +5 -1
  11. data/contrib/nix/ruby2.4-gemset.nix +22 -2
  12. data/contrib/nix/ruby2.4-shell.nix +2 -6
  13. data/contrib/nix/ruby2.5-Gemfile.lock +5 -1
  14. data/contrib/nix/ruby2.5-gemset.nix +22 -2
  15. data/contrib/nix/ruby2.5-shell.nix +2 -6
  16. data/contrib/nix/ruby2.6-Gemfile.lock +5 -1
  17. data/contrib/nix/ruby2.6-gemset.nix +22 -2
  18. data/contrib/nix/ruby2.6-shell.nix +2 -6
  19. data/contrib/nix/ruby2.7-Gemfile.lock +91 -0
  20. data/contrib/nix/ruby2.7-gemset.nix +359 -0
  21. data/contrib/nix/ruby2.7-shell.nix +2 -11
  22. data/contrib/nix/ruby3.0-Gemfile.lock +91 -0
  23. data/contrib/nix/ruby3.0-gemset.nix +359 -0
  24. data/contrib/nix/ruby3.0-shell.nix +2 -11
  25. data/contrib/nix/ruby3.1-shell.nix +9 -7
  26. data/contrib/nix/ruby3.2-shell.nix +9 -7
  27. data/contrib/nix/ruby3.3-shell.nix +9 -7
  28. data/contrib/nix/ruby3.4-shell.nix +36 -0
  29. data/contrib/nix/test-all-rubies.sh +1 -1
  30. data/lib/sup/crypto.rb +7 -5
  31. data/lib/sup/maildir.rb +4 -0
  32. data/lib/sup/mbox.rb +25 -7
  33. data/lib/sup/message.rb +13 -10
  34. data/lib/sup/modes/edit_message_mode.rb +5 -5
  35. data/lib/sup/util.rb +9 -2
  36. data/lib/sup/version.rb +1 -1
  37. data/man/sup-add.1 +15 -15
  38. data/man/sup-config.1 +9 -9
  39. data/man/sup-dump.1 +13 -12
  40. data/man/sup-import-dump.1 +20 -20
  41. data/man/sup-psych-ify-config-files.1 +11 -11
  42. data/man/sup-recover-sources.1 +18 -18
  43. data/man/sup-sync-back-maildir.1 +18 -17
  44. data/man/sup-sync.1 +29 -29
  45. data/man/sup-tweak-labels.1 +22 -21
  46. data/man/sup.1 +21 -21
  47. data/sup.gemspec +2 -0
  48. data/test/dummy_source.rb +6 -0
  49. data/test/fixtures/embedded-message-rfc6532.eml +33 -0
  50. data/test/fixtures/invalid-date.eml +8 -0
  51. data/test/gnupg_test_home/private-keys-v1.d/26C05E44706A8E230B3255BB9532B34DC9420232.key +42 -0
  52. data/test/gnupg_test_home/private-keys-v1.d/D187ADC90EC4DEB7047678EAA37E33A53A465D47.key +5 -0
  53. data/test/gnupg_test_home/private-keys-v1.d/FB2D9BD3B1BE90B5BCF697781F8404224B0FCF5B.key +5 -0
  54. data/test/gnupg_test_home/pubring.gpg +0 -0
  55. data/test/gnupg_test_home/receiver_pubring.gpg +0 -0
  56. data/test/gnupg_test_home/receiver_secring.gpg +0 -0
  57. data/test/gnupg_test_home/regen_keys.sh +11 -2
  58. data/test/gnupg_test_home/secring.gpg +0 -0
  59. data/test/gnupg_test_home/sup-test-2@foo.bar.asc +20 -20
  60. data/test/integration/test_maildir.rb +15 -1
  61. data/test/integration/test_mbox.rb +10 -0
  62. data/test/test_crypto.rb +108 -71
  63. data/test/test_message.rb +42 -0
  64. data/test/unit/test_contact.rb +1 -1
  65. data/test/unit/test_edit_message_mode.rb +94 -0
  66. data/test/unit/test_person.rb +3 -3
  67. data/test/unit/test_rmail_message.rb +36 -0
  68. data/test/unit/util/test_string.rb +3 -3
  69. metadata +50 -5
  70. data/test/gnupg_test_home/private-keys-v1.d/306D2EE90FF0014B5B9FD07E265C751791674140.key +0 -0
data/test/test_crypto.rb CHANGED
@@ -38,14 +38,10 @@ class TestCryptoManager < Minitest::Test
38
38
  @path = Dir.mktmpdir
39
39
  Redwood::HookManager.init File.join(@path, 'hooks')
40
40
 
41
- am = {:default=> {name: "test", email: @from_email, alternates: [@from_email_ecc]}}
41
+ am = {:default=> {name: +"test", email: @from_email.dup, alternates: [@from_email_ecc.dup]}}
42
42
  Redwood::AccountManager.init am
43
43
 
44
44
  Redwood::CryptoManager.init
45
-
46
- if not CryptoManager.have_crypto?
47
- warn "No crypto set up, crypto will not be tested. Reason: #{CryptoManager.not_working_reason}"
48
- end
49
45
  end
50
46
 
51
47
  def teardown
@@ -58,90 +54,132 @@ class TestCryptoManager < Minitest::Test
58
54
  end
59
55
 
60
56
  def test_sign
61
- if CryptoManager.have_crypto? then
62
- signed = CryptoManager.sign @from_email,@to_email,"ABCDEFG"
63
- assert_instance_of RMail::Message, signed
64
- assert_equal("multipart/signed; protocol=application/pgp-signature; micalg=pgp-sha256",
65
- signed.header["Content-Type"])
66
- assert_equal "ABCDEFG", signed.body[0]
67
- assert signed.body[1].body.length > 0 , "signature length must be > 0"
68
- assert (signed.body[1].body.include? "-----BEGIN PGP SIGNATURE-----") , "Expecting PGP armored data"
69
- end
57
+ skip CryptoManager.not_working_reason if not CryptoManager.have_crypto?
58
+
59
+ signed = CryptoManager.sign @from_email,@to_email,"ABCDEFG"
60
+ assert_instance_of RMail::Message, signed
61
+ assert_equal("multipart/signed; protocol=application/pgp-signature; micalg=pgp-sha256",
62
+ signed.header["Content-Type"])
63
+ assert_equal "ABCDEFG", signed.body[0]
64
+ assert signed.body[1].body.length > 0 , "signature length must be > 0"
65
+ assert (signed.body[1].body.include? "-----BEGIN PGP SIGNATURE-----") , "Expecting PGP armored data"
70
66
  end
71
67
 
72
68
  def test_sign_nested_parts
73
- if CryptoManager.have_crypto? then
74
- body = RMail::Message.new
75
- body.header["Content-Disposition"] = "inline"
76
- body.body = "ABCDEFG"
77
- payload = RMail::Message.new
78
- payload.header["MIME-Version"] = "1.0"
79
- payload.add_part body
80
- payload.add_part RMail::Message.make_attachment "attachment", "text/plain", nil, "attachment.txt"
81
- signed = CryptoManager.sign @from_email, @to_email, payload
82
- ## The result is a multipart/signed containing a multipart/mixed.
83
- ## There should be a MIME-Version header on the top-level
84
- ## multipart/signed message, but *not* on the enclosed
85
- ## multipart/mixed part.
86
- assert_equal 1, signed.to_s.scan(/MIME-Version:/).size
87
- end
69
+ skip CryptoManager.not_working_reason if not CryptoManager.have_crypto?
70
+
71
+ body = RMail::Message.new
72
+ body.header["Content-Disposition"] = +"inline"
73
+ body.body = "ABCDEFG"
74
+ payload = RMail::Message.new
75
+ payload.header["MIME-Version"] = +"1.0"
76
+ payload.add_part body
77
+ payload.add_part RMail::Message.make_attachment "attachment", "text/plain", nil, "attachment.txt"
78
+ signed = CryptoManager.sign @from_email, @to_email, payload
79
+ ## The result is a multipart/signed containing a multipart/mixed.
80
+ ## There should be a MIME-Version header on the top-level
81
+ ## multipart/signed message, but *not* on the enclosed
82
+ ## multipart/mixed part.
83
+ assert_equal 1, signed.to_s.scan(/MIME-Version:/).size
88
84
  end
89
85
 
90
86
  def test_encrypt
91
- if CryptoManager.have_crypto? then
92
- encrypted = CryptoManager.encrypt @from_email, [@to_email], "ABCDEFG"
93
- assert_instance_of RMail::Message, encrypted
94
- assert (encrypted.body[1].body.include? "-----BEGIN PGP MESSAGE-----") , "Expecting PGP armored data"
95
- end
87
+ skip CryptoManager.not_working_reason if not CryptoManager.have_crypto?
88
+
89
+ encrypted = CryptoManager.encrypt @from_email, [@to_email], "ABCDEFG"
90
+ assert_instance_of RMail::Message, encrypted
91
+ assert (encrypted.body[1].body.include? "-----BEGIN PGP MESSAGE-----") , "Expecting PGP armored data"
96
92
  end
97
93
 
98
94
  def test_sign_and_encrypt
99
- if CryptoManager.have_crypto? then
100
- encrypted = CryptoManager.sign_and_encrypt @from_email, [@to_email], "ABCDEFG"
101
- assert_instance_of RMail::Message, encrypted
102
- assert (encrypted.body[1].body.include? "-----BEGIN PGP MESSAGE-----") , "Expecting PGP armored data"
103
- end
95
+ skip CryptoManager.not_working_reason if not CryptoManager.have_crypto?
96
+
97
+ encrypted = CryptoManager.sign_and_encrypt @from_email, [@to_email], "ABCDEFG"
98
+ assert_instance_of RMail::Message, encrypted
99
+ assert (encrypted.body[1].body.include? "-----BEGIN PGP MESSAGE-----") , "Expecting PGP armored data"
104
100
  end
105
101
 
106
102
  def test_decrypt
107
- if CryptoManager.have_crypto? then
108
- encrypted = CryptoManager.encrypt @from_email, [@to_email], "ABCDEFG"
109
- assert_instance_of RMail::Message, encrypted
110
- assert_instance_of String, (encrypted.body[1].body)
111
- decrypted = CryptoManager.decrypt encrypted.body[1], true
112
- assert_instance_of Array, decrypted
113
- assert_instance_of Chunk::CryptoNotice, decrypted[0]
114
- assert_instance_of Chunk::CryptoNotice, decrypted[1]
115
- assert_instance_of RMail::Message, decrypted[2]
116
- assert_equal "ABCDEFG" , decrypted[2].body
117
- end
103
+ skip CryptoManager.not_working_reason if not CryptoManager.have_crypto?
104
+
105
+ encrypted = CryptoManager.encrypt @from_email, [@to_email], "ABCDEFG"
106
+ assert_instance_of RMail::Message, encrypted
107
+ assert_instance_of String, (encrypted.body[1].body)
108
+ decrypted = CryptoManager.decrypt encrypted.body[1], true
109
+ assert_instance_of Array, decrypted
110
+ assert_instance_of Chunk::CryptoNotice, decrypted[0]
111
+ assert_instance_of Chunk::CryptoNotice, decrypted[1]
112
+ assert_instance_of RMail::Message, decrypted[2]
113
+ assert_equal "ABCDEFG" , decrypted[2].body
114
+ end
115
+
116
+ def test_decrypt_and_verify
117
+ skip CryptoManager.not_working_reason if not CryptoManager.have_crypto?
118
+
119
+ encrypted = CryptoManager.sign_and_encrypt @from_email, [@to_email], "ABCDEFG"
120
+ assert_instance_of RMail::Message, encrypted
121
+ assert_instance_of String, (encrypted.body[1].body)
122
+ decrypted = CryptoManager.decrypt encrypted.body[1], true
123
+ assert_instance_of Array, decrypted
124
+ assert_instance_of Chunk::CryptoNotice, decrypted[0]
125
+ assert_instance_of Chunk::CryptoNotice, decrypted[1]
126
+ assert_instance_of RMail::Message, decrypted[2]
127
+ assert_match(/^Signature made .* using RSA key ID 072B50BE/,
128
+ decrypted[1].lines[0])
129
+ assert_equal "Good signature from \"#{@from_email}\"", decrypted[1].lines[1]
130
+ assert_equal "ABCDEFG" , decrypted[2].body
131
+ end
132
+
133
+ def test_decrypt_and_verify_nondefault_key
134
+ skip CryptoManager.not_working_reason if not CryptoManager.have_crypto?
135
+
136
+ encrypted = CryptoManager.sign_and_encrypt @from_email_ecc, [@to_email], "ABCDEFG"
137
+ assert_instance_of RMail::Message, encrypted
138
+ assert_instance_of String, (encrypted.body[1].body)
139
+ decrypted = CryptoManager.decrypt encrypted.body[1], true
140
+ assert_instance_of Array, decrypted
141
+ assert_instance_of Chunk::CryptoNotice, decrypted[0]
142
+ assert_instance_of Chunk::CryptoNotice, decrypted[1]
143
+ assert_instance_of RMail::Message, decrypted[2]
144
+ assert_match(/^Signature made .* key ID AC34B83C/, decrypted[1].lines[0])
145
+ assert_equal "Good signature from \"#{@from_email_ecc}\"", decrypted[1].lines[1]
146
+ assert_equal "ABCDEFG" , decrypted[2].body
118
147
  end
119
148
 
120
149
  def test_verify
121
- if CryptoManager.have_crypto?
122
- signed = CryptoManager.sign @from_email, @to_email, "ABCDEFG"
123
- assert_instance_of RMail::Message, signed
124
- assert_instance_of String, (signed.body[1].body)
125
- CryptoManager.verify signed.body[0], signed.body[1], true
126
- end
150
+ skip CryptoManager.not_working_reason if not CryptoManager.have_crypto?
151
+
152
+ signed = CryptoManager.sign @from_email, @to_email, "ABCDEFG"
153
+ assert_instance_of RMail::Message, signed
154
+ assert_instance_of String, (signed.body[1].body)
155
+ chunk = CryptoManager.verify signed.body[0], signed.body[1], true
156
+ assert_instance_of Redwood::Chunk::CryptoNotice, chunk
157
+ assert_match(/^Signature made .* using RSA key ID 072B50BE/,
158
+ chunk.lines[0])
159
+ assert_equal "Good signature from \"#{@from_email}\"", chunk.lines[1]
127
160
  end
128
161
 
129
162
  def test_verify_unknown_keytype
130
- if CryptoManager.have_crypto?
131
- signed = CryptoManager.sign @from_email_ecc, @to_email, "ABCDEFG"
132
- assert_instance_of RMail::Message, signed
133
- assert_instance_of String, (signed.body[1].body)
134
- CryptoManager.verify signed.body[0], signed.body[1], true
135
- end
163
+ skip CryptoManager.not_working_reason if not CryptoManager.have_crypto?
164
+
165
+ signed = CryptoManager.sign @from_email_ecc, @to_email, "ABCDEFG"
166
+ assert_instance_of RMail::Message, signed
167
+ assert_instance_of String, (signed.body[1].body)
168
+ chunk = CryptoManager.verify signed.body[0], signed.body[1], true
169
+ assert_instance_of Redwood::Chunk::CryptoNotice, chunk
170
+ assert_match(/^Signature made .* using unknown key type \(303\) key ID AC34B83C/,
171
+ chunk.lines[0])
172
+ assert_equal "Good signature from \"#{@from_email_ecc}\"", chunk.lines[1]
136
173
  end
137
174
 
138
175
  def test_verify_nested_parts
139
- if CryptoManager.have_crypto?
140
- ## Generate a multipart/signed containing a multipart/mixed.
141
- ## We will test verifying the generated signature below.
142
- ## Importantly, the inner multipart/mixed does *not* have a
143
- ## MIME-Version header because it is not a top-level message.
144
- payload = RMail::Parser.read <<EOS
176
+ skip CryptoManager.not_working_reason if not CryptoManager.have_crypto?
177
+
178
+ ## Generate a multipart/signed containing a multipart/mixed.
179
+ ## We will test verifying the generated signature below.
180
+ ## Importantly, the inner multipart/mixed does *not* have a
181
+ ## MIME-Version header because it is not a top-level message.
182
+ payload = RMail::Parser.read <<EOS
145
183
  Content-Type: multipart/mixed; boundary="=-1652088224-7794-561531-1825-1-="
146
184
 
147
185
 
@@ -156,9 +194,8 @@ Content-Type: text/plain; name="attachment.txt"
156
194
  attachment
157
195
  --=-1652088224-7794-561531-1825-1-=--
158
196
  EOS
159
- signed = CryptoManager.sign @from_email_ecc, @to_email, payload
160
- CryptoManager.verify payload, signed.body[1], true
161
- end
197
+ signed = CryptoManager.sign @from_email_ecc, @to_email, payload
198
+ CryptoManager.verify payload, signed.body[1], true
162
199
  end
163
200
  end
164
201
 
data/test/test_message.rb CHANGED
@@ -344,6 +344,35 @@ class TestMessage < Minitest::Test
344
344
  assert_equal("Second line.", chunks[2].lines[1])
345
345
  end
346
346
 
347
+ def test_embedded_message_rfc6532
348
+ source = DummySource.new("sup-test://test_embedded_message_rfc6532")
349
+ source.messages = [ fixture_path("embedded-message-rfc6532.eml") ]
350
+
351
+ sup_message = Message.build_from_source(source, 0)
352
+
353
+ chunks = sup_message.load_from_source!
354
+ assert_equal(3, chunks.length)
355
+
356
+ assert_equal("Email with embedded message", sup_message.subj)
357
+
358
+ assert(chunks[0].is_a? Redwood::Chunk::Text)
359
+ assert_equal("Example outer message.", chunks[0].lines[0])
360
+
361
+ assert(chunks[1].is_a? Redwood::Chunk::EnclosedMessage)
362
+ assert_equal(4, chunks[1].lines.length)
363
+ assert_equal("From: Embed sender <embed@example.com>", chunks[1].lines[0])
364
+ assert_equal("To: rcpt2 <rcpt2@example.invalid>", chunks[1].lines[1])
365
+ assert_equal("Date: ", chunks[1].lines[2][0..5])
366
+ assert_equal(
367
+ Time.rfc2822("Sun, 12 May 2024 17:34:29 +1000"),
368
+ Time.rfc2822(chunks[1].lines[2][6..-1])
369
+ )
370
+ assert_equal("Subject: Embedded subject line with emoji ✨", chunks[1].lines[3])
371
+
372
+ assert(chunks[2].is_a? Redwood::Chunk::Text)
373
+ assert_equal("Example embedded message, with UTF-8 headers.", chunks[2].lines[0])
374
+ end
375
+
347
376
  def test_malicious_attachment_names
348
377
  source = DummySource.new("sup-test://test_blank_header_lines")
349
378
  source.messages = [ fixture_path('malicious-attachment-names.eml') ]
@@ -359,6 +388,19 @@ class TestMessage < Minitest::Test
359
388
  fn = chunks[3].safe_filename
360
389
  assert_equal(fn, File.basename(fn))
361
390
  end
391
+
392
+ def test_invalid_date_header
393
+ fallback_date = Time.utc 2024, 5, 12, 15, 5, 56
394
+ source = DummySource.new("sup-test://test_invalid_date_header")
395
+ source.messages = [ fixture_path("invalid-date.eml") ]
396
+ source.fallback_date = fallback_date
397
+
398
+ sup_message = Message.build_from_source(source, 0)
399
+ sup_message.load_from_source!
400
+
401
+ assert_equal(fallback_date, sup_message.date)
402
+ end
403
+
362
404
  # TODO: test different error cases, malformed messages etc.
363
405
 
364
406
  # TODO: test different quoting styles, see that they are all divided
@@ -6,7 +6,7 @@ module Redwood
6
6
  class TestContact < Minitest::Test
7
7
  def setup
8
8
  @contact = ContactManager.init(File.expand_path("../../fixtures/contacts.txt", __FILE__))
9
- @person = Person.new "Terrible Name", "terrible@name.com"
9
+ @person = Person.new (+"Terrible Name"), (+"terrible@name.com")
10
10
  end
11
11
 
12
12
  def teardown
@@ -0,0 +1,94 @@
1
+ require "test_helper"
2
+
3
+ require "sup"
4
+
5
+ class DummySelector
6
+ attr_accessor :val
7
+ def initialize val
8
+ @val = val
9
+ end
10
+ end
11
+
12
+ class DummyCryptoManager
13
+ def have_crypto?; true; end
14
+ def sign from, to, payload
15
+ envelope = RMail::Message.new
16
+ envelope.header["Content-Type"] = +"multipart/signed; protocol=testdummy"
17
+ envelope.add_part payload
18
+ envelope
19
+ end
20
+ end
21
+
22
+ class TestEditMessageMode < Minitest::Test
23
+ def setup
24
+ $config = {}
25
+ @path = Dir.mktmpdir
26
+ Redwood::HookManager.init File.join(@path, "hooks")
27
+ Redwood::AccountManager.init :default => {name: +"test", email: +"sender@example.invalid"}
28
+ Redwood::CryptoManager.instance_variable_set :@instance, DummyCryptoManager.new
29
+ end
30
+
31
+ def teardown
32
+ Redwood::CryptoManager.deinstantiate!
33
+ Redwood::AccountManager.deinstantiate!
34
+ Redwood::HookManager.deinstantiate!
35
+ FileUtils.rm_r @path
36
+ $config = nil
37
+ end
38
+
39
+ def test_attachment_content_transfer_encoding
40
+ ## RMail::Message#make_attachment will choose
41
+ ## Content-Transfer-Encoding: 8bit for a CSV file.
42
+ ## If we're not GPG signing or encrypting then the attachment will be sent
43
+ ## as is. Note this assumes the SMTP servers in the delivery path all
44
+ ## support the 8BITMIME extension.
45
+ attachment_content = "löl,\ntest,\n"
46
+ attachment_filename = File.join @path, "dummy.csv"
47
+ File.write attachment_filename, attachment_content
48
+
49
+ opts = {
50
+ :header => {
51
+ "From" => +"sender@example.invalid",
52
+ "To" => +"recip@example.invalid",
53
+ },
54
+ :attachments => {
55
+ "dummy.csv" => RMail::Message.make_file_attachment(attachment_filename),
56
+ },
57
+ }
58
+ mode = Redwood::EditMessageMode.new opts
59
+
60
+ msg = mode.send :build_message, Time.now
61
+ attachment = msg.part(1)
62
+ assert_equal attachment_content, attachment.body
63
+ assert_equal "8bit", attachment.header["Content-Transfer-Encoding"]
64
+ end
65
+
66
+ def test_attachment_content_transfer_encoding_signed
67
+ attachment_filename = File.join @path, "dummy.csv"
68
+ ## Include some high bytes in the attachment contents in order to
69
+ ## exercise quote-printable transfer encoding.
70
+ File.write attachment_filename, "löl,\ntest,\n"
71
+
72
+ opts = {
73
+ :header => {
74
+ "From" => +"sender@example.invalid",
75
+ "To" => +"recip@example.invalid",
76
+ },
77
+ :attachments => {
78
+ "dummy.csv" => RMail::Message.make_file_attachment(attachment_filename),
79
+ },
80
+ }
81
+ mode = Redwood::EditMessageMode.new opts
82
+ mode.instance_variable_set :@crypto_selector, DummySelector.new(:sign)
83
+
84
+ msg = mode.send :build_message, Time.now
85
+ ## The outermost message is a (fake) multipart/signed created by DummyCryptoManager#send.
86
+ ## Inside that we have our inline message at index 0 and CSV attachment at index 1.
87
+ attachment = msg.part(0).part(1)
88
+ ## The attachment should have been re-encoded as quoted-printable for GPG signing.
89
+ assert_equal "l=C3=B6l,\ntest,\n", attachment.body
90
+ ## There shouldn't be multiple Content-Transfer-Encoding headers.
91
+ ## This was: https://github.com/sup-heliotrope/sup/issues/502
92
+ assert_equal ["quoted-printable"], attachment.header.fetch_all("Content-Transfer-Encoding")
93
+ end
94
+ end
@@ -5,12 +5,12 @@ module Redwood
5
5
 
6
6
  class TestPerson < Minitest::Test
7
7
  def setup
8
- @person = Person.new("Thomassen, Bob", "bob@thomassen.com")
9
- @no_name = Person.new(nil, "alice@alice.com")
8
+ @person = Person.new(+"Thomassen, Bob", +"bob@thomassen.com")
9
+ @no_name = Person.new(nil, +"alice@alice.com")
10
10
  end
11
11
 
12
12
  def test_email_must_be_supplied
13
- assert_raises (ArgumentError) { Person.new("Alice", nil) }
13
+ assert_raises (ArgumentError) { Person.new(+"Alice", nil) }
14
14
  end
15
15
 
16
16
  def test_to_string
@@ -0,0 +1,36 @@
1
+ require "test_helper"
2
+
3
+ require "sup"
4
+
5
+ class TestRMailMessage < Minitest::Test
6
+ def setup
7
+ @path = Dir.mktmpdir
8
+ end
9
+
10
+ def teardown
11
+ FileUtils.rm_r @path
12
+ end
13
+
14
+ def test_make_file_attachment
15
+ filename = File.join @path, "test.html"
16
+ File.write filename, "<html></html>"
17
+
18
+ a = RMail::Message.make_file_attachment(filename)
19
+ assert_equal "text/html; name=\"test.html\"", a.header["Content-Type"]
20
+ assert_equal "attachment; filename=\"test.html\"", a.header["Content-Disposition"]
21
+ assert_equal "8bit", a.header["Content-Transfer-Encoding"]
22
+ end
23
+
24
+ def test_make_file_attachment_text_with_long_lines
25
+ filename = File.join @path, "test.html"
26
+ File.write filename, "a" * 1023
27
+
28
+ a = RMail::Message.make_file_attachment(filename)
29
+ assert_equal "text/html; name=\"test.html\"", a.header["Content-Type"]
30
+ assert_equal "attachment; filename=\"test.html\"", a.header["Content-Disposition"]
31
+ assert_equal "quoted-printable", a.header["Content-Transfer-Encoding"]
32
+
33
+ qp_encoded = ("a" * 73 + "=\n") * 14 + "a=\n"
34
+ assert_equal qp_encoded, a.body
35
+ end
36
+ end
@@ -18,7 +18,7 @@ describe "Sup's String extension" do
18
18
 
19
19
  it "calculates display length of a string" do
20
20
  data.each do |(str, length)|
21
- assert_equal length, str.display_length
21
+ assert_equal length, str.dup.display_length
22
22
  end
23
23
  end
24
24
  end
@@ -36,7 +36,7 @@ describe "Sup's String extension" do
36
36
 
37
37
  it "slices string by display length" do
38
38
  data.each do |(str, length, sliced)|
39
- assert_equal sliced, str.slice_by_display_length(length)
39
+ assert_equal sliced, str.dup.slice_by_display_length(length)
40
40
  end
41
41
  end
42
42
  end
@@ -56,7 +56,7 @@ describe "Sup's String extension" do
56
56
 
57
57
  it "wraps string by display length" do
58
58
  data.each do |(str, length, wrapped)|
59
- assert_equal wrapped, str.wrap(length)
59
+ assert_equal wrapped, str.dup.wrap(length)
60
60
  end
61
61
  end
62
62
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sup
3
3
  version: !ruby/object:Gem::Version
4
- version: '1.2'
4
+ version: '1.3'
5
5
  platform: ruby
6
6
  authors:
7
7
  - William Morgan
@@ -11,7 +11,7 @@ authors:
11
11
  autorequire:
12
12
  bindir: bin
13
13
  cert_chain: []
14
- date: 2024-04-21 00:00:00.000000000 Z
14
+ date: 2025-04-21 00:00:00.000000000 Z
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
17
17
  name: ncursesw
@@ -159,6 +159,34 @@ dependencies:
159
159
  - - ">="
160
160
  - !ruby/object:Gem::Version
161
161
  version: '0'
162
+ - !ruby/object:Gem::Dependency
163
+ name: benchmark
164
+ requirement: !ruby/object:Gem::Requirement
165
+ requirements:
166
+ - - ">="
167
+ - !ruby/object:Gem::Version
168
+ version: '0'
169
+ type: :runtime
170
+ prerelease: false
171
+ version_requirements: !ruby/object:Gem::Requirement
172
+ requirements:
173
+ - - ">="
174
+ - !ruby/object:Gem::Version
175
+ version: '0'
176
+ - !ruby/object:Gem::Dependency
177
+ name: fiddle
178
+ requirement: !ruby/object:Gem::Requirement
179
+ requirements:
180
+ - - ">="
181
+ - !ruby/object:Gem::Version
182
+ version: '0'
183
+ type: :runtime
184
+ prerelease: false
185
+ version_requirements: !ruby/object:Gem::Requirement
186
+ requirements:
187
+ - - ">="
188
+ - !ruby/object:Gem::Version
189
+ version: '0'
162
190
  - !ruby/object:Gem::Dependency
163
191
  name: bundler
164
192
  requirement: !ruby/object:Gem::Requirement
@@ -336,11 +364,16 @@ files:
336
364
  - contrib/nix/ruby2.6-Gemfile.lock
337
365
  - contrib/nix/ruby2.6-gemset.nix
338
366
  - contrib/nix/ruby2.6-shell.nix
367
+ - contrib/nix/ruby2.7-Gemfile.lock
368
+ - contrib/nix/ruby2.7-gemset.nix
339
369
  - contrib/nix/ruby2.7-shell.nix
370
+ - contrib/nix/ruby3.0-Gemfile.lock
371
+ - contrib/nix/ruby3.0-gemset.nix
340
372
  - contrib/nix/ruby3.0-shell.nix
341
373
  - contrib/nix/ruby3.1-shell.nix
342
374
  - contrib/nix/ruby3.2-shell.nix
343
375
  - contrib/nix/ruby3.3-shell.nix
376
+ - contrib/nix/ruby3.4-shell.nix
344
377
  - contrib/nix/test-all-rubies.sh
345
378
  - devel/console.sh
346
379
  - devel/count-loc.sh
@@ -435,7 +468,9 @@ files:
435
468
  - test/fixtures/binary-content-transfer-encoding-2.eml
436
469
  - test/fixtures/blank-header-fields.eml
437
470
  - test/fixtures/contacts.txt
471
+ - test/fixtures/embedded-message-rfc6532.eml
438
472
  - test/fixtures/embedded-message.eml
473
+ - test/fixtures/invalid-date.eml
439
474
  - test/fixtures/mailing-list-header.eml
440
475
  - test/fixtures/malicious-attachment-names.eml
441
476
  - test/fixtures/missing-from-to.eml
@@ -452,7 +487,9 @@ files:
452
487
  - test/fixtures/zimbra-quote-with-bottom-post.eml
453
488
  - test/gnupg_test_home/.gpg-v21-migrated
454
489
  - test/gnupg_test_home/gpg.conf
455
- - test/gnupg_test_home/private-keys-v1.d/306D2EE90FF0014B5B9FD07E265C751791674140.key
490
+ - test/gnupg_test_home/private-keys-v1.d/26C05E44706A8E230B3255BB9532B34DC9420232.key
491
+ - test/gnupg_test_home/private-keys-v1.d/D187ADC90EC4DEB7047678EAA37E33A53A465D47.key
492
+ - test/gnupg_test_home/private-keys-v1.d/FB2D9BD3B1BE90B5BCF697781F8404224B0FCF5B.key
456
493
  - test/gnupg_test_home/pubring.gpg
457
494
  - test/gnupg_test_home/receiver_pubring.gpg
458
495
  - test/gnupg_test_home/receiver_secring.gpg
@@ -471,9 +508,11 @@ files:
471
508
  - test/test_yaml_regressions.rb
472
509
  - test/unit/service/test_label_service.rb
473
510
  - test/unit/test_contact.rb
511
+ - test/unit/test_edit_message_mode.rb
474
512
  - test/unit/test_horizontal_selector.rb
475
513
  - test/unit/test_locale_fiddler.rb
476
514
  - test/unit/test_person.rb
515
+ - test/unit/test_rmail_message.rb
477
516
  - test/unit/util/test_query.rb
478
517
  - test/unit/util/test_string.rb
479
518
  - test/unit/util/test_uri.rb
@@ -504,7 +543,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
504
543
  - !ruby/object:Gem::Version
505
544
  version: '0'
506
545
  requirements: []
507
- rubygems_version: 3.4.22
546
+ rubygems_version: 3.5.22
508
547
  signing_key:
509
548
  specification_version: 4
510
549
  summary: A console-based email client with the best features of GMail, mutt and Emacs
@@ -514,7 +553,9 @@ test_files:
514
553
  - test/fixtures/binary-content-transfer-encoding-2.eml
515
554
  - test/fixtures/blank-header-fields.eml
516
555
  - test/fixtures/contacts.txt
556
+ - test/fixtures/embedded-message-rfc6532.eml
517
557
  - test/fixtures/embedded-message.eml
558
+ - test/fixtures/invalid-date.eml
518
559
  - test/fixtures/mailing-list-header.eml
519
560
  - test/fixtures/malicious-attachment-names.eml
520
561
  - test/fixtures/missing-from-to.eml
@@ -531,7 +572,9 @@ test_files:
531
572
  - test/fixtures/zimbra-quote-with-bottom-post.eml
532
573
  - test/gnupg_test_home/.gpg-v21-migrated
533
574
  - test/gnupg_test_home/gpg.conf
534
- - test/gnupg_test_home/private-keys-v1.d/306D2EE90FF0014B5B9FD07E265C751791674140.key
575
+ - test/gnupg_test_home/private-keys-v1.d/26C05E44706A8E230B3255BB9532B34DC9420232.key
576
+ - test/gnupg_test_home/private-keys-v1.d/D187ADC90EC4DEB7047678EAA37E33A53A465D47.key
577
+ - test/gnupg_test_home/private-keys-v1.d/FB2D9BD3B1BE90B5BCF697781F8404224B0FCF5B.key
535
578
  - test/gnupg_test_home/pubring.gpg
536
579
  - test/gnupg_test_home/receiver_pubring.gpg
537
580
  - test/gnupg_test_home/receiver_secring.gpg
@@ -550,9 +593,11 @@ test_files:
550
593
  - test/test_yaml_regressions.rb
551
594
  - test/unit/service/test_label_service.rb
552
595
  - test/unit/test_contact.rb
596
+ - test/unit/test_edit_message_mode.rb
553
597
  - test/unit/test_horizontal_selector.rb
554
598
  - test/unit/test_locale_fiddler.rb
555
599
  - test/unit/test_person.rb
600
+ - test/unit/test_rmail_message.rb
556
601
  - test/unit/util/test_query.rb
557
602
  - test/unit/util/test_string.rb
558
603
  - test/unit/util/test_uri.rb