sup 1.2 → 1.4

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 (102) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/checks.yml +19 -4
  3. data/.gitmodules +1 -0
  4. data/.rubocop.yml +1 -1
  5. data/History.txt +33 -0
  6. data/Manifest.txt +21 -2
  7. data/README.md +5 -3
  8. data/Rakefile +1 -1
  9. data/bin/sup +5 -3
  10. data/contrib/nix/Gemfile +2 -0
  11. data/contrib/nix/Gemfile.lock +62 -34
  12. data/contrib/nix/gem-install-shell.nix +2 -0
  13. data/contrib/nix/gemset.nix +130 -56
  14. data/contrib/nix/ruby2.4-Gemfile.lock +6 -2
  15. data/contrib/nix/ruby2.4-gemset.nix +24 -4
  16. data/contrib/nix/ruby2.4-shell.nix +2 -6
  17. data/contrib/nix/ruby2.5-Gemfile.lock +6 -2
  18. data/contrib/nix/ruby2.5-gemset.nix +24 -4
  19. data/contrib/nix/ruby2.5-shell.nix +2 -6
  20. data/contrib/nix/ruby2.6-Gemfile.lock +6 -2
  21. data/contrib/nix/ruby2.6-gemset.nix +24 -4
  22. data/contrib/nix/ruby2.6-shell.nix +2 -6
  23. data/contrib/nix/ruby2.7-Gemfile.lock +91 -0
  24. data/contrib/nix/ruby2.7-gemset.nix +359 -0
  25. data/contrib/nix/ruby2.7-shell.nix +2 -11
  26. data/contrib/nix/ruby3.0-Gemfile.lock +91 -0
  27. data/contrib/nix/ruby3.0-gemset.nix +359 -0
  28. data/contrib/nix/ruby3.0-shell.nix +2 -11
  29. data/contrib/nix/ruby3.1-Gemfile.lock +101 -0
  30. data/contrib/nix/ruby3.1-gemset.nix +391 -0
  31. data/contrib/nix/ruby3.1-shell.nix +3 -12
  32. data/contrib/nix/ruby3.2-Gemfile.lock +101 -0
  33. data/contrib/nix/ruby3.2-gemset.nix +391 -0
  34. data/contrib/nix/ruby3.2-shell.nix +3 -12
  35. data/contrib/nix/ruby3.3-shell.nix +1 -10
  36. data/contrib/nix/ruby3.4-shell.nix +27 -0
  37. data/contrib/nix/ruby4.0-shell.nix +40 -0
  38. data/contrib/nix/test-all-rubies.sh +1 -1
  39. data/lib/sup/account.rb +2 -0
  40. data/lib/sup/buffer.rb +1 -0
  41. data/lib/sup/contact.rb +3 -4
  42. data/lib/sup/crypto.rb +7 -5
  43. data/lib/sup/draft.rb +15 -11
  44. data/lib/sup/index.rb +8 -4
  45. data/lib/sup/maildir.rb +4 -0
  46. data/lib/sup/mbox.rb +25 -7
  47. data/lib/sup/message.rb +13 -10
  48. data/lib/sup/message_chunks.rb +0 -24
  49. data/lib/sup/mode.rb +1 -0
  50. data/lib/sup/modes/contact_list_mode.rb +0 -1
  51. data/lib/sup/modes/edit_message_mode.rb +6 -6
  52. data/lib/sup/modes/label_search_results_mode.rb +1 -2
  53. data/lib/sup/modes/line_cursor_mode.rb +22 -20
  54. data/lib/sup/modes/search_results_mode.rb +0 -1
  55. data/lib/sup/modes/thread_view_mode.rb +1 -2
  56. data/lib/sup/rfc2047.rb +5 -2
  57. data/lib/sup/source.rb +2 -0
  58. data/lib/sup/util.rb +9 -2
  59. data/lib/sup/version.rb +1 -1
  60. data/lib/sup.rb +1 -1
  61. data/man/sup-add.1 +18 -18
  62. data/man/sup-config.1 +12 -12
  63. data/man/sup-dump.1 +15 -14
  64. data/man/sup-import-dump.1 +24 -24
  65. data/man/sup-psych-ify-config-files.1 +11 -11
  66. data/man/sup-recover-sources.1 +20 -20
  67. data/man/sup-sync-back-maildir.1 +24 -23
  68. data/man/sup-sync.1 +35 -35
  69. data/man/sup-tweak-labels.1 +27 -26
  70. data/man/sup.1 +27 -27
  71. data/sup.gemspec +3 -1
  72. data/test/dummy_buffer.rb +34 -0
  73. data/test/dummy_source.rb +6 -0
  74. data/test/fixtures/contacts.txt +2 -1
  75. data/test/fixtures/embedded-message-rfc6532.eml +33 -0
  76. data/test/fixtures/invalid-date.eml +8 -0
  77. data/test/fixtures/rfc2047-header-encoding.eml +1 -1
  78. data/test/gnupg_test_home/private-keys-v1.d/26C05E44706A8E230B3255BB9532B34DC9420232.key +42 -0
  79. data/test/gnupg_test_home/private-keys-v1.d/D187ADC90EC4DEB7047678EAA37E33A53A465D47.key +5 -0
  80. data/test/gnupg_test_home/private-keys-v1.d/FB2D9BD3B1BE90B5BCF697781F8404224B0FCF5B.key +5 -0
  81. data/test/gnupg_test_home/pubring.gpg +0 -0
  82. data/test/gnupg_test_home/receiver_pubring.gpg +0 -0
  83. data/test/gnupg_test_home/receiver_secring.gpg +0 -0
  84. data/test/gnupg_test_home/regen_keys.sh +11 -2
  85. data/test/gnupg_test_home/secring.gpg +0 -0
  86. data/test/gnupg_test_home/sup-test-2@foo.bar.asc +20 -20
  87. data/test/integration/test_draft.rb +128 -0
  88. data/test/integration/test_maildir.rb +17 -1
  89. data/test/integration/test_mbox.rb +12 -0
  90. data/test/integration/test_sup-add.rb +4 -0
  91. data/test/test_crypto.rb +114 -72
  92. data/test/test_message.rb +43 -0
  93. data/test/unit/test_contact.rb +16 -4
  94. data/test/unit/test_edit_message_mode.rb +99 -0
  95. data/test/unit/test_index.rb +65 -0
  96. data/test/unit/test_line_cursor_mode.rb +208 -0
  97. data/test/unit/test_person.rb +3 -3
  98. data/test/unit/test_rmail_message.rb +36 -0
  99. data/test/unit/util/test_string.rb +3 -3
  100. metadata +64 -9
  101. data/shell.nix +0 -1
  102. data/test/gnupg_test_home/private-keys-v1.d/306D2EE90FF0014B5B9FD07E265C751791674140.key +0 -0
data/man/sup.1 CHANGED
@@ -1,51 +1,51 @@
1
- .\" Automatically generated by Pandoc 3.1.9
1
+ .\" Automatically generated by Pandoc 3.7.0.2
2
2
  .\"
3
- .TH "SUP" "1" "April 8, 2014" "Sup User Manual" ""
3
+ .TH "SUP" "1" "April 8, 2014" "Sup User Manual"
4
4
  .SH NAME
5
- sup - a curses-based email client
5
+ sup \- a curses\-based email client
6
6
  .SH SYNOPSIS
7
7
  sup [\f[I]options\f[R]]
8
8
  .SH DESCRIPTION
9
- Sup is a console-based email client for people with a lot of email.
9
+ Sup is a console\-based email client for people with a lot of email.
10
10
  .PP
11
11
  It presents an interface of a list of threads, which are each
12
12
  hierarchical collections email messages.
13
13
  Threads can have multiple tags applied to them.
14
- It supports a very fast full-text search, automatic contact-list
14
+ It supports a very fast full\-text search, automatic contact\-list
15
15
  management, custom code insertion via a Ruby hook system, and more.
16
- If you\[cq]re the type of person who treats email as an extension of
17
- your long-term memory, Sup is for you.
16
+ If you\(cqre the type of person who treats email as an extension of your
17
+ long\-term memory, Sup is for you.
18
18
  .SH OPTIONS
19
19
  .TP
20
- -l, --list-hooks
20
+ \-l, \-\-list\-hooks
21
21
  List all hooks and descriptions, and quit.
22
- Use \[en]hooks-matching to filter
22
+ Use \(enhooks\-matching to filter
23
23
  .TP
24
- -m, --hooks-matching=<s>
24
+ \-m, \-\-hooks\-matching=<s>
25
25
  If given, list all hooks and descriptions matching the given pattern.
26
- Needs the \[en]list-hooks option (default: )
26
+ Needs the \(enlist\-hooks option (default: )
27
27
  .TP
28
- -n, --no-threads
28
+ \-n, \-\-no\-threads
29
29
  Turn off threading.
30
30
  Helps with debugging.
31
31
  (Necessarily disables background polling for new messages.)
32
32
  .TP
33
- -o, --no-initial-poll
34
- Don\[cq]t poll for new messages when starting.
33
+ \-o, \-\-no\-initial\-poll
34
+ Don\(cqt poll for new messages when starting.
35
35
  .TP
36
- -s \f[I]QUERY\f[R], --search \f[I]QUERY\f[R]
36
+ \-s \f[I]QUERY\f[R], \-\-search \f[I]QUERY\f[R]
37
37
  Search for this query upon startup
38
38
  .TP
39
- -c \f[I]STRING\f[R], --compose \f[I]STRING\f[R]
39
+ \-c \f[I]STRING\f[R], \-\-compose \f[I]STRING\f[R]
40
40
  Compose message to this recipient upon startup
41
41
  .TP
42
- -j \f[I]STRING\f[R], --subject \f[I]STRING\f[R]
42
+ \-j \f[I]STRING\f[R], \-\-subject \f[I]STRING\f[R]
43
43
  When composing, use this subject
44
44
  .TP
45
- -v, --version
45
+ \-v, \-\-version
46
46
  Print version and exit
47
47
  .TP
48
- -h, --help
48
+ \-h, \-\-help
49
49
  Show brief help message
50
50
  .SH ENVIRONMENT
51
51
  .TP
@@ -54,7 +54,7 @@ Set log level verbosity.
54
54
  Valid values ordered by decresing verbosity: debug info warn error.
55
55
  Default log level is info.
56
56
  .TP
57
- SUP_BASE\[rs]
57
+ SUP_BASE\(rs
58
58
  Specify home directory for configuration files and xapian index,
59
59
  defaults to: $HOME/.sup.
60
60
  .SH FILES
@@ -68,9 +68,9 @@ Configuration file for Sup mail sources
68
68
  $HOME/.sup/colors.yaml
69
69
  Color theme for Sup
70
70
  .SH SEE ALSO
71
- mail(1), sup-add(1), sup-config(1), sup-dump(1), sup-import-dump(1),
72
- sup-recover-sources(1), sup-sync(1), sup-sync-back-maildir(1),
73
- sup-tweak-labels(1)
71
+ mail(1), sup\-add(1), sup\-config(1), sup\-dump(1),
72
+ sup\-import\-dump(1), sup\-recover\-sources(1), sup\-sync(1),
73
+ sup\-sync\-back\-maildir(1), sup\-tweak\-labels(1)
74
74
  .SH REPORTING BUGS
75
75
  You are welcome to submit bug reports to the Sup issue tracker, located
76
76
  at
@@ -96,21 +96,21 @@ Sup Wiki:
96
96
  .UE \c
97
97
  .TP
98
98
  Mailing list:
99
- supmua\[at]googlegroups.com
99
+ supmua\(atgooglegroups.com
100
100
  .RS
101
101
  .PP
102
- supmua+subscribe\[at]googlegroups.com
102
+ supmua+subscribe\(atgooglegroups.com
103
103
  .PP
104
104
  Archives: \c
105
105
  .UR https://groups.google.com/d/forum/supmua/
106
106
  .UE \c
107
107
  .RE
108
108
  .SH COPYRIGHT
109
- Copyright © 2006-2009 William Morgan \c
109
+ Copyright © 2006\-2009 William Morgan \c
110
110
  .MT mworgan-sup@masanjin.net
111
111
  .ME \c
112
112
  .PP
113
- Copyright \[at] 2013-2014 Sup developers
113
+ Copyright \(at 2013\-2014 Sup developers
114
114
  .PP
115
115
  Permission is granted to copy and distribute this manual under the terms
116
116
  of the GNU General Public License; either version 2 or (at your option)
data/sup.gemspec CHANGED
@@ -9,7 +9,7 @@ Gem::Specification.new do |s|
9
9
  s.authors = ["William Morgan", "Gaute Hope", "Hamish Downer", "Matthieu Rakotojaona"]
10
10
  s.email = "supmua@googlegroups.com"
11
11
  s.summary = "A console-based email client with the best features of GMail, mutt and Emacs"
12
- s.homepage = "https://sup-heliotrope.github.io/"
12
+ s.homepage = "https://supmua.dev/"
13
13
  s.license = 'GPL-2.0'
14
14
  s.description = <<-DESC
15
15
  Sup is a console-based email client for people with a lot of email.
@@ -61,6 +61,8 @@ SUP: please note that our old mailing lists have been shut down,
61
61
  s.add_runtime_dependency "unicode", "~> 0.4.4"
62
62
  s.add_runtime_dependency "unicode-display_width"
63
63
  s.add_runtime_dependency "string-scrub" if /^2\.0\./ =~ RUBY_VERSION
64
+ s.add_runtime_dependency "benchmark"
65
+ s.add_runtime_dependency "fiddle"
64
66
 
65
67
  s.add_development_dependency "bundler", ">= 1.3", "< 3"
66
68
  s.add_development_dependency "rake"
@@ -0,0 +1,34 @@
1
+ require "sup"
2
+
3
+ module Redwood
4
+
5
+ class DummyBuffer
6
+ attr_reader :width, :height, :dirty_count, :commit_count
7
+
8
+ def initialize width=80, height=25
9
+ @width = width
10
+ @height = height
11
+ @dirty = false
12
+ @dirty_count = 0
13
+ @commit_count = 0
14
+ end
15
+
16
+ def content_height; @height - 1; end
17
+ def content_width; @width; end
18
+
19
+ def mark_dirty
20
+ @dirty = true
21
+ @dirty_count += 1
22
+ end
23
+
24
+ def dirty?; @dirty; end
25
+
26
+ def commit
27
+ @dirty = false
28
+ @commit_count += 1
29
+ end
30
+
31
+ def write(*args); end
32
+ end
33
+
34
+ end
data/test/dummy_source.rb CHANGED
@@ -10,10 +10,12 @@ module Redwood
10
10
  class DummySource < Source
11
11
 
12
12
  attr_accessor :messages
13
+ attr_writer :fallback_date
13
14
 
14
15
  def initialize uri, last_date=nil, usual=true, archived=false, id=nil, labels=[]
15
16
  super uri, usual, archived, id
16
17
  @messages = nil
18
+ @fallback_date = Time.utc 2001, 2, 3, 4, 56, 57
17
19
  end
18
20
 
19
21
  def start_offset
@@ -59,6 +61,10 @@ class DummySource < Source
59
61
  end
60
62
  end
61
63
  end
64
+
65
+ def fallback_date_for_message id
66
+ @fallback_date
67
+ end
62
68
  end
63
69
 
64
70
  end
@@ -1 +1,2 @@
1
- RC: Random Contact <random_dude@gmail.com>
1
+ RC: Random Contact <random_dude@gmail.com>
2
+ : Unaliased Contact <unaliased@example.invalid>
@@ -0,0 +1,33 @@
1
+ Return-Path: <return@example.com>
2
+ From: Sender <sender@example.com>
3
+ To: <recipient@example.invalid>
4
+ Subject: Email with embedded message
5
+ MIME-Version: 1.0
6
+ Content-Type: multipart/mixed; boundary="----------=_4F506AC2.EE281DC4"
7
+ Message-Id: <9181f493-df49-4af5-8ad2-e1a8eb692a98>
8
+ Date: Wed, 15 Jul 2020 19:48:41 +0100
9
+
10
+ This is a multi-part message in MIME format.
11
+
12
+ ------------=_4F506AC2.EE281DC4
13
+ Content-Type: text/plain; charset=iso-8859-1
14
+ Content-Disposition: inline
15
+ Content-Transfer-Encoding: 8bit
16
+
17
+ Example outer message.
18
+
19
+
20
+ ------------=_4F506AC2.EE281DC4
21
+ Content-Type: message/global
22
+ Content-Transfer-Encoding: 8bit
23
+
24
+ From: "Embed sender" <embed@example.com>
25
+ To: <rcpt2@example.invalid>
26
+ Subject: Embedded subject line with emoji ✨
27
+ X-heävy-mëtal-ümlaut: yes
28
+ Date: Sun, 12 May 2024 17:34:29 +1000
29
+
30
+ Example embedded message, with UTF-8 headers.
31
+
32
+ ------------=_4F506AC2.EE281DC4--
33
+
@@ -0,0 +1,8 @@
1
+ To: person@example.invalid
2
+ Subject: pre-RFC2822 Date header
3
+ Content-type: text/plain
4
+ From: anotherperson@example.invalid
5
+ Message-Id: <6965470922807932796@example.invalid>
6
+ Date: ons, 26 maj 1999 11:00:34 +0200 (CEST)
7
+
8
+ A message body.
@@ -1,4 +1,4 @@
1
- From: test@example.invalid
1
+ From: =?utf-8?q?YouTube-tj=E4nst?= <service@youtube.com>
2
2
  To: test@example.invalid
3
3
  Date: Sun, 19 Jul 2020 17:03:56 +1000
4
4
  Subject:
@@ -0,0 +1,42 @@
1
+ Created: 20241014T073358
2
+ Key: (private-key (rsa (n #00C0EF24D44D17ABD33603EDED46A2F639BDB90C9A72
3
+ E6AD03E83B597169BF7B8F6DEF42A5EED6F9BBDD0BDE326075969886D11D2875FB6972
4
+ D8D73ECC98F6A14AB083E93990567C78EEDA09260096B4B99C67824B39005DEA7D75B5
5
+ ECF3F03D042EEEB49E9871AA3B2DC4552FC7598B8DDAD6454EF0CCD11D8C4BAEEFEBCC
6
+ CF4BCDB4FBB05BCC026910D4249E24BB657BD2287E6999FD2C499C54C29CE079437CB3
7
+ 8AE685C6D733749B046FF2235A29B22822C23EFFF38302B3A8EEC81CE9505BF8D4CCF5
8
+ EB958AFBDE50488F56E14B3CC116E413CE5E449D65537DDFB90F841A3B6580BEA20783
9
+ 002699B584F53EE4C29C7381071536659E50BE9AA445068F5B2B2B0185E850DF4790FD
10
+ 455A91135FE29A79A0E7EF73A8C6B6328603C5B46466B1A674AE205256C79B8A39E73D
11
+ 15587A123C2FEC23C14CD2ADD2D41344408E713A10D986766F17F93011998788DEF294
12
+ DA6C3E86CEC1562FB574014CA2937FA7ACF7C57D80E9925994FCF8EA55102FC987FAD6
13
+ 7393E2011515856AE9FEB6DDB9#)(e #010001#)(d
14
+ #1AC7D73BE4EB993D3B2D36748E2E86B12325B19B2AC1DC58437F217E3DA887B53414
15
+ 5713036131CF22F2C7C8045DAE26C3E7F8FD29CB592AC8F0AFD5E491AF77D7A2A7D806
16
+ B20A9B1AC4872A90405B1BBC9B1D99E572A896A90D92D02E5835900417BC589B5E83BB
17
+ AD31A885764D76B29D22CB021728C70A2BF441772A2B06C745E98E5A3F93C4EDE2991F
18
+ F017B72F720D8CF3672329E260E1635E943A107B0EAF3963A04426910CE98BA7225C47
19
+ E2ABC92872001EB52585557CE8903C7B225D342894EFB6CCA4ED2397E970EAB675BE69
20
+ 70F1009E87396923D13A6B4B1E941EA9433C386EFA94843C67C5C5D08B688BDCD3075D
21
+ 29ACB0D3D3F156562ABB19345B547040782FA7DD199F20CA7042D8548FF72FAB96394C
22
+ 2ADFCAFA4E38196AED778C777BA8BF4646A8E4F5DA3E44C0330AC71AFAD1FCF1C792F2
23
+ 34C7D0BFA59E1435813695CC813C1395DDCD61BE60B1693B89DC5B8480DE5BD5065AED
24
+ 3822B865574D55155FCACDB51807679040D6952E073982C6075E07111A205D7EBEA015
25
+ #)(p #00D8DF87835F964777CC15BA16559A04CD40D570084AF06117224D9B6DE56CB5
26
+ DCC0F5A66DEAF9DBA1542475808017B318D152FA7425376508A48E1591C75605504799
27
+ 184C0F11059B5625C799356CC895314DD684A25CC36775DFB2270DBB1E92E7457773BB
28
+ 9E2C1BCDB93BD7A9E4AE316F23FC06D9AC6E538A7A9A53D454B1548A80291777704EA7
29
+ B5D798C8378C4C7B040A4BD64875755A8CC092D4D1C340D22CA798F177F0A0A9893BC7
30
+ 026A9023660CBF155F22CC295880965E75832AA01F#)(q
31
+ #00E3BDF8A4560327A98A63D6B00B7474FC0F918F043CF0A99253F41EEEF4895BFAF8
32
+ 50AFDC107C4A741958667F7B567F280632C8B3454494CA1B253A33DD4E45ACC5194F53
33
+ 1B67DD975D15470FA52CF2C71CEA669B1471CF1B8B76EE0E5925F5CC1103D75184D7C3
34
+ BB8EE7498ECC34483EEDD5516AF5F234ADA9C0BFDE2E3A15F7027B109E136C56C4779C
35
+ 6E032DC25B5D2E68D7079C5CEBC15B25ADE6481A1E1E78305B067C452C677908BE6D16
36
+ EC7B1869689FC9F70FB4D45449C336D9B46727#)(u
37
+ #009C3200C269390035DB78F0259D81B22712470EE1FBA278564DDDE0C667EA48A511
38
+ 7B8AB17C2DAEFD2078BF99CE548A72661E8FCA2288BE39447854F79AFDAFB4415BB60A
39
+ C72562E2DDB31D52DDE9D638EEDBF9636AB6BCDD2870E6C9B59AFAB04C169CF51433BE
40
+ C75EC697575686029F36FA2537896ACCD35EB766BB3A2EBC84DBFC24C0D049699813B1
41
+ 1A7679D87D38B222ECD1238DCFB84AB58D5C4D8B5E90D627D9C1BEA3793AC2AEA225FC
42
+ 7C7021EA0FA08CAF614286537134929A47097D#)))
@@ -0,0 +1,5 @@
1
+ Created: 20241014T073400
2
+ Key: (private-key (ecc (curve Ed25519)(flags eddsa)(q
3
+ #4049C4EFC8EEB81658624601387C3ECCDE9A3D76476C10773DE55E025580B9AE17#)
4
+ (d #C9A9E5DAE378425DDCFBC4B565845883A198F96B86E31D3F4763665633D9F51E#)
5
+ ))
@@ -0,0 +1,5 @@
1
+ Created: 20241014T073400
2
+ Key: (private-key (ecc (curve Curve25519)(flags djb-tweak)(q
3
+ #404F9277B8281579076BE50AF2D30484B4B72E56035A3F36A99F38A6FA06065521#)
4
+ (d #5390C4E04165F6AAF4CDCA43C6FF4C5B6FF3F55137B11A5BC64CE8C7B67E3948#)
5
+ ))
Binary file
@@ -67,6 +67,15 @@ gpg2 \
67
67
  --passphrase '' \
68
68
  --quick-generate-key sup-test-1@foo.bar rsa encrypt,sign 0
69
69
 
70
+ echo "Generating key pair for sender alternate address (email sup-fake-ecc@fake.fake)"
71
+ gpg2 \
72
+ --homedir . \
73
+ --batch \
74
+ --pinentry-mode loopback \
75
+ --passphrase '' \
76
+ --default-new-key-algo "ed25519/cert,sign+cv25519/encr" \
77
+ --quick-generate-key sup-fake-ecc@fake.fake default default 0
78
+
70
79
  echo "Importing public key for receiver, into sender's key store"
71
80
  gpg2 \
72
81
  --homedir . \
@@ -80,10 +89,10 @@ gpg2 \
80
89
 
81
90
  echo "Done."
82
91
 
83
- echo "We now have two non-expiring public keys (receiver & sender):"
92
+ echo "We now have some non-expiring public keys:"
84
93
  gpg2 --homedir . --list-keys
85
94
 
86
- echo "And we also have only *one* corresponding private key (sender only):"
95
+ echo "And we also have some corresponding private keys:"
87
96
  gpg2 --homedir . --list-secret-keys
88
97
 
89
98
  popd
Binary file
@@ -1,23 +1,23 @@
1
1
  -----BEGIN PGP PUBLIC KEY BLOCK-----
2
2
 
3
- mQGNBF7leTkBDAC3auy8xodH6jxoISylFZTpVqy/0L2ul879YUb/QbC58+F/H36S
4
- CjLfPxFlq0FAOXHelOvktxaybg+BG5UpSvTgBLbcArq5nctee+04TMXCzQzrG2V1
5
- zb9gIRT665fX3+WYncSIXdr4LAp7r8Jw3RT3tTOZqbaencumCWaJblnvfFwPrMKf
6
- AXWa/NVndNMAXmJ5uBf1MRr45KXaQ2tczPIeHqSOKhKNnKZPRqPs0fg4i3d0Vb6G
7
- yItgtJapfBo50FV+PvtodMHo3LDlz/BBjdEJHSvghqEjb1S7xGo+hdXs+lfCMfa0
8
- 3PAWoj+OeHNorbK0YbVKOtS0E0xYvScbyC7bfwtA9yb3LZYmy7VHsKJmQfygCNQ6
9
- wIKQGAVN1NcQcJsvWyAwk9+WMN5oqB5lb76u40beoWlUjSJRlph2VvWvkGuh/huU
10
- sVGqcN7EO4SFkwi2YQLoWfQRGur3mids/PQTBywpGE1SyziPZK76pT6SqP8b+OpI
11
- CG1QbcTZzYpbv6kAEQEAAbQSc3VwLXRlc3QtMkBmb28uYmFyiQHOBBMBCgA4FiEE
12
- e0oXvVeqMzUcfd1s2bF8xbTizW8FAl7leTkCGw8FCwkIBwIGFQoJCAsCBBYCAwEC
13
- HgECF4AACgkQ2bF8xbTizW92TAv/WGlYfDTKNEmJ0K+kxt33T2ldmZXaJKL04Mft
14
- h5s5KlRZWDNpkCC/L55uyaeEg+Uy+BEEQKLAEeJrrLMV8UMJwMPDOizSTT9uLyiz
15
- b8RjnQw4iMT8wt9TQboXGaTMslwdXvFPii7w44KgCimE7VuPetJuLMLMbnl147G8
16
- +QhkNUsrB51TuPS8xZJ4qjbH+K/Y2NlvwLtJrxNE3SRQuy2ApYJxKPZIj1KpUL8M
17
- 7Jy/2hI8DaRm/0Fpu8HwRIVsd6/dgdkqdj1uVyLj+wyhgdzqV5WrPLFCRVhd3icd
18
- lPNRIDjg8YKCh353LVHjKwefOW4SnkOPn4uVMdCP9gUFd9zpMP9lMFpjk0o0tcYO
19
- NiFrOclS4q5qZ5jrj1MnBF0NaGhuC83DDgRfKV+p5noVeJxg0nXYZSlsSMfAT/K7
20
- FbdNEg0XUsrLgWVzhvWv/ebMetFPSfGHIveZ7lhiq1qpA5hLBNfSSBb1JJsFmtQt
21
- cEUluymdNe5W7Y6UGs1CpvcIvbj+
22
- =Cy9S
3
+ mQGNBGcMyWQBDADDsDnBPhnEKSH2qSJq00/nxe1zFhwc5yiD5kwutfOCG/46Wtqs
4
+ vETmZfAaWOrfFectT51zLlG2/zju63NEiImt8KBxB02lI8uanLdKxNpAuN3OuNTF
5
+ RapqLTCwThlNZlGXM4fvC7uuFxtSV3yE5wIOQIT4IvpGc2/P7NuDg+aYoAkfNH5Y
6
+ 1E3II/tRUYZn63t5ihmOH1b2Gc8qyD05gc7O836ZgKHL1FfCf4A8tgQEOFgkkYdH
7
+ fX8x57RbZAqfzpH5oxcZNa5LAC+m+3EmLF5cvNWqLh8wAnYb+krDvZ9sg8OjY5YN
8
+ K4SDTnKfg0zofdEHeZFqDOg1N/0Ri79EYJBINBeERj1OMPUD8uim+AeJjLbL1XiH
9
+ l31zEQOoTCSutGeA7sKS3YTaNcpvKfneW3MHN4y4jqLLI2g1PGGGTXYpr/t3/8uG
10
+ fzlkcxBDzl3IX1lvIBEhF4P1uSSc2Oa+IizzfDVPoKzy7+bLP/JvF3cTKKZHe63P
11
+ p/9hy34IOdKKw7sAEQEAAbQSc3VwLXRlc3QtMkBmb28uYmFyiQHRBBMBCAA7FiEE
12
+ IJRwaSVB2pkxecFGpaPGNeoyUYoFAmcMyWQCGw8FCwkIBwICIgIGFQoJCAsCBBYC
13
+ AwECHgcCF4AACgkQpaPGNeoyUYq1NwwAuurXiq3hnmTbg/Lxn1FtcwW0xKdBmUbB
14
+ LWg+fWZvo87wU5TxsdAu1Y8YiWqusSX9ZYg8po0/dgBKdrPREF3rCWOw6wVwWg4E
15
+ aydajoefUa10CPf4VeOLbVReQLsueWycp5uw3Pm8Zc78RyKL9QmY+k3ez6kspHS6
16
+ gWeiLv9XazBll/o1YYLQX7gI29aseBkbLS81Z1ea+a6cqvzm/MzAYiDf8ETQ3/Sm
17
+ Nd/HoPWJ9/aHuuBUQsX2uWCI70YgvTh/kKfaNr3up5BmCjwgQCo2zjZOGDUqnajd
18
+ 2I5qV3uabNXTwgdjWoqN9nmBeEpW5haw+NwuhQT0H5tWfiL4wyXv01KZxPxv1tAq
19
+ Tu9frSOk4ziRWbUQMelIiGbIRB6VNHEIyM3F/UpboDDPn9lIVuG6ARNfPemHsErI
20
+ rIdC2PXMibdO5U1d+0oY5xf7YxnWWgWstsjlgyXBUnX3R5xXGm5Sf66VwR+0Vo9J
21
+ EFqKZ/vYa3YwJ+hfzIBVKGxBKXpXQv83
22
+ =kQ2o
23
23
  -----END PGP PUBLIC KEY BLOCK-----
@@ -0,0 +1,128 @@
1
+ require "sup"
2
+ require "test_helper"
3
+
4
+ class TestDraft < Minitest::Test
5
+ include Redwood
6
+
7
+ def setup
8
+ @path = Dir.mktmpdir
9
+ start
10
+ Logger.remove_sink $stderr
11
+ @draft_dir = File.join @path, "drafts"
12
+ @test_message_1 = <<EOS
13
+ From: Some Person <someone@example.invalid>
14
+ To:
15
+ Cc:
16
+ Bcc:
17
+ Subject: draft
18
+ Date: Fri, 11 Apr 2025 22:34:05 +1000
19
+ Message-ID: <123@example.invalid>
20
+
21
+ My incomplete message
22
+ EOS
23
+ DraftManager.instance.instance_eval "@dir = '#{@draft_dir}'"
24
+ Index.init @path
25
+ Index.load
26
+ SourceManager.instance.instance_eval "@sources = {}"
27
+ @draft_source = DraftManager.new_source
28
+ SourceManager.add_source @draft_source
29
+ end
30
+
31
+ def teardown
32
+ Index.save_index
33
+ ObjectSpace.each_object(Class).select {|a| a < Redwood::Singleton}.each do |klass|
34
+ klass.deinstantiate! unless klass == Redwood::Logger
35
+ end
36
+ FileUtils.rm_r @path
37
+ end
38
+
39
+ def test_write_draft
40
+ DraftManager.write_draft { |f| f.write @test_message_1 }
41
+
42
+ draft_filename = File.join @draft_dir, "0"
43
+ assert File.exist? draft_filename
44
+ assert_equal @test_message_1, (File.read draft_filename)
45
+
46
+ ## Check that it is loaded back into the index successfully too.
47
+ messages_in_index = Index.instance.enum_for(:each_message).to_a
48
+ assert_equal @test_message_1, messages_in_index.first.raw_message
49
+ assert_equal [:draft, :inbox].to_set, messages_in_index.first.labels
50
+ end
51
+
52
+ def test_discard_draft
53
+ DraftManager.write_draft { |f| f.write @test_message_1 }
54
+ draft_filename = File.join @draft_dir, "0"
55
+ assert File.exist? draft_filename
56
+ message_in_index = Index.instance.enum_for(:each_message).to_a.first
57
+
58
+ DraftManager.discard message_in_index
59
+ refute File.exist? draft_filename
60
+ end
61
+
62
+ def test_discard_already_deleted_from_disk
63
+ DraftManager.write_draft { |f| f.write @test_message_1 }
64
+ draft_filename = File.join @draft_dir, "0"
65
+ assert File.exist? draft_filename
66
+ message_in_index = Index.instance.enum_for(:each_message).to_a.first
67
+
68
+ File.delete draft_filename
69
+
70
+ DraftManager.discard message_in_index
71
+ refute File.exist? draft_filename
72
+ end
73
+
74
+ def test_load_malformed_draft
75
+ ## Sup always writes drafts by serialising a Message, meaning the draft is
76
+ ## guaranteed to have certain headers like Date. But it's always possible
77
+ ## for the user to edit the draft directly on the filesystem and leave it
78
+ ## in some kind of malformed state. Sup should handle it without crashing.
79
+ draft_filename = File.join @draft_dir, "0"
80
+ fallback_date = Time.new 2025, 5, 3, 15, 47, 41
81
+ File.write draft_filename, <<EOS
82
+ Some-Header: Value
83
+
84
+ body
85
+ EOS
86
+ File.utime fallback_date, fallback_date, draft_filename
87
+ PollManager.poll_from @draft_source
88
+ messages_in_index = Index.instance.enum_for(:each_message).to_a
89
+ assert_equal "", messages_in_index[0].subj
90
+ assert_equal fallback_date, messages_in_index[0].date
91
+
92
+ File.write (File.join @draft_dir, "1"), <<EOS
93
+ missing a header!
94
+ EOS
95
+ PollManager.poll_from @draft_source
96
+ messages_in_index = Index.instance.enum_for(:each_message).to_a
97
+ assert_equal "", messages_in_index[0].subj
98
+
99
+ File.write (File.join @draft_dir, "2"), ""
100
+ PollManager.poll_from @draft_source
101
+ messages_in_index = Index.instance.enum_for(:each_message).to_a
102
+ assert_equal "", messages_in_index[0].subj
103
+ end
104
+
105
+ def test_draft_with_non_ascii_chars
106
+ message = <<EOS
107
+ From: Some Person <someone@example.invalid>
108
+ To:
109
+ Cc:
110
+ Bcc:
111
+ Subject: UTF-8 draft 🤐
112
+ Date: Fri, 11 Apr 2025 22:34:05 +1000
113
+ Message-ID: <123@example.invalid>
114
+
115
+ ¡Buen día! Доброго ранку! おはよう!
116
+ EOS
117
+ DraftManager.write_draft { |f| f.write message }
118
+ draft_filename = File.join @draft_dir, "0"
119
+ assert_equal message, (File.read draft_filename)
120
+
121
+ PollManager.poll_from @draft_source
122
+ messages_in_index = Index.instance.enum_for(:each_message).to_a
123
+ assert_equal "UTF-8 draft 🤐", messages_in_index[0].subj
124
+ assert_equal message, messages_in_index.first.raw_message
125
+ assert_equal "¡Buen día! Доброго ранку! おはよう!", \
126
+ messages_in_index[0].chunks[0].lines[0]
127
+ end
128
+ end
@@ -34,16 +34,20 @@ EOS
34
34
  end
35
35
 
36
36
  def create_a_maildir_email(folder, content)
37
- File.write(File.join(folder, "#{Time.now.to_f}.hostname:2,S"), content)
37
+ filename = File.join folder, "#{Time.now.to_f}.hostname:2,S"
38
+ File.write filename, content
39
+ filename
38
40
  end
39
41
 
40
42
  def start_sup_and_add_source(source)
41
43
  start
44
+ Logger.remove_sink $stderr
42
45
  Index.init @path
43
46
  Index.load
44
47
  SourceManager.instance.instance_eval '@sources = {}'
45
48
  SourceManager.instance.add_source source
46
49
  PollManager.poll_from source
50
+ Index.save_index
47
51
  end
48
52
 
49
53
  # and now, let the tests begin!
@@ -74,5 +78,17 @@ EOS
74
78
 
75
79
  end
76
80
 
81
+ def test_missing_date_header
82
+ ## The message is missing a Date header so we should use its modtime
83
+ ## as a fallback.
84
+ fallback_date = Time.new 2004, 4, 19, 11, 12, 13
85
+ maildir = create_a_maildir
86
+ filename = create_a_maildir_email(File.join(maildir, 'cur'), @test_message_1)
87
+ File.utime fallback_date, fallback_date, filename
88
+ start_sup_and_add_source Maildir.new "maildir:#{maildir}"
89
+
90
+ messages_in_index = Index.instance.enum_for(:each_message).to_a
91
+ assert_equal fallback_date, messages_in_index.first.date
92
+ end
77
93
  end
78
94
 
@@ -34,11 +34,13 @@ EOS
34
34
 
35
35
  def start_sup_and_add_source(source)
36
36
  start
37
+ Logger.remove_sink $stderr
37
38
  Index.init @path
38
39
  Index.load
39
40
  SourceManager.instance.instance_eval '@sources = {}'
40
41
  SourceManager.instance.add_source source
41
42
  PollManager.poll_from source
43
+ Index.save_index
42
44
  end
43
45
 
44
46
  # and now, let the tests begin!
@@ -69,4 +71,14 @@ EOS
69
71
 
70
72
  end
71
73
 
74
+ def test_missing_date_header
75
+ ## The message is missing a Date header so we should use envelope date
76
+ ## stored in the From line as a fallback.
77
+ fallback_date = Time.new 2009, 4, 27, 12, 56, 18
78
+ mbox = create_a_mbox
79
+ start_sup_and_add_source MBox.new "mbox:#{mbox}"
80
+
81
+ messages_in_index = Index.instance.enum_for(:each_message).to_a
82
+ assert_equal fallback_date, messages_in_index.first.date
83
+ end
72
84
  end
@@ -26,6 +26,8 @@ class TestSupAdd < Minitest::Test
26
26
  EOS
27
27
  end
28
28
 
29
+ ## https://github.com/sup-heliotrope/sup/issues/577
30
+ ## https://github.com/sup-heliotrope/sup/issues/516
29
31
  def test_fixes_old_tag_uri_syntax
30
32
  File.write "#{@path}/sources.yaml", <<EOS
31
33
  ---
@@ -36,6 +38,7 @@ EOS
36
38
  sync_back: true
37
39
  id: 1
38
40
  labels: []
41
+ - !supmua.org,2006-10-01/Redwood/SentLoader {}
39
42
  EOS
40
43
  _out, _err = capture_subprocess_io do
41
44
  assert system({"SUP_BASE" => @path}, "bin/sup-add", "maildir:///other/path")
@@ -58,6 +61,7 @@ EOS
58
61
  sync_back: true
59
62
  id: 2
60
63
  labels: []
64
+ - !<tag:supmua.org,2006-10-01/Redwood/SentLoader> {}
61
65
  EOS
62
66
  end
63
67