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.
- checksums.yaml +4 -4
- data/.github/workflows/checks.yml +19 -4
- data/.gitmodules +1 -0
- data/.rubocop.yml +1 -1
- data/History.txt +33 -0
- data/Manifest.txt +21 -2
- data/README.md +5 -3
- data/Rakefile +1 -1
- data/bin/sup +5 -3
- data/contrib/nix/Gemfile +2 -0
- data/contrib/nix/Gemfile.lock +62 -34
- data/contrib/nix/gem-install-shell.nix +2 -0
- data/contrib/nix/gemset.nix +130 -56
- data/contrib/nix/ruby2.4-Gemfile.lock +6 -2
- data/contrib/nix/ruby2.4-gemset.nix +24 -4
- data/contrib/nix/ruby2.4-shell.nix +2 -6
- data/contrib/nix/ruby2.5-Gemfile.lock +6 -2
- data/contrib/nix/ruby2.5-gemset.nix +24 -4
- data/contrib/nix/ruby2.5-shell.nix +2 -6
- data/contrib/nix/ruby2.6-Gemfile.lock +6 -2
- data/contrib/nix/ruby2.6-gemset.nix +24 -4
- data/contrib/nix/ruby2.6-shell.nix +2 -6
- data/contrib/nix/ruby2.7-Gemfile.lock +91 -0
- data/contrib/nix/ruby2.7-gemset.nix +359 -0
- data/contrib/nix/ruby2.7-shell.nix +2 -11
- data/contrib/nix/ruby3.0-Gemfile.lock +91 -0
- data/contrib/nix/ruby3.0-gemset.nix +359 -0
- data/contrib/nix/ruby3.0-shell.nix +2 -11
- data/contrib/nix/ruby3.1-Gemfile.lock +101 -0
- data/contrib/nix/ruby3.1-gemset.nix +391 -0
- data/contrib/nix/ruby3.1-shell.nix +3 -12
- data/contrib/nix/ruby3.2-Gemfile.lock +101 -0
- data/contrib/nix/ruby3.2-gemset.nix +391 -0
- data/contrib/nix/ruby3.2-shell.nix +3 -12
- data/contrib/nix/ruby3.3-shell.nix +1 -10
- data/contrib/nix/ruby3.4-shell.nix +27 -0
- data/contrib/nix/ruby4.0-shell.nix +40 -0
- data/contrib/nix/test-all-rubies.sh +1 -1
- data/lib/sup/account.rb +2 -0
- data/lib/sup/buffer.rb +1 -0
- data/lib/sup/contact.rb +3 -4
- data/lib/sup/crypto.rb +7 -5
- data/lib/sup/draft.rb +15 -11
- data/lib/sup/index.rb +8 -4
- data/lib/sup/maildir.rb +4 -0
- data/lib/sup/mbox.rb +25 -7
- data/lib/sup/message.rb +13 -10
- data/lib/sup/message_chunks.rb +0 -24
- data/lib/sup/mode.rb +1 -0
- data/lib/sup/modes/contact_list_mode.rb +0 -1
- data/lib/sup/modes/edit_message_mode.rb +6 -6
- data/lib/sup/modes/label_search_results_mode.rb +1 -2
- data/lib/sup/modes/line_cursor_mode.rb +22 -20
- data/lib/sup/modes/search_results_mode.rb +0 -1
- data/lib/sup/modes/thread_view_mode.rb +1 -2
- data/lib/sup/rfc2047.rb +5 -2
- data/lib/sup/source.rb +2 -0
- data/lib/sup/util.rb +9 -2
- data/lib/sup/version.rb +1 -1
- data/lib/sup.rb +1 -1
- data/man/sup-add.1 +18 -18
- data/man/sup-config.1 +12 -12
- data/man/sup-dump.1 +15 -14
- data/man/sup-import-dump.1 +24 -24
- data/man/sup-psych-ify-config-files.1 +11 -11
- data/man/sup-recover-sources.1 +20 -20
- data/man/sup-sync-back-maildir.1 +24 -23
- data/man/sup-sync.1 +35 -35
- data/man/sup-tweak-labels.1 +27 -26
- data/man/sup.1 +27 -27
- data/sup.gemspec +3 -1
- data/test/dummy_buffer.rb +34 -0
- data/test/dummy_source.rb +6 -0
- data/test/fixtures/contacts.txt +2 -1
- data/test/fixtures/embedded-message-rfc6532.eml +33 -0
- data/test/fixtures/invalid-date.eml +8 -0
- data/test/fixtures/rfc2047-header-encoding.eml +1 -1
- data/test/gnupg_test_home/private-keys-v1.d/26C05E44706A8E230B3255BB9532B34DC9420232.key +42 -0
- data/test/gnupg_test_home/private-keys-v1.d/D187ADC90EC4DEB7047678EAA37E33A53A465D47.key +5 -0
- data/test/gnupg_test_home/private-keys-v1.d/FB2D9BD3B1BE90B5BCF697781F8404224B0FCF5B.key +5 -0
- data/test/gnupg_test_home/pubring.gpg +0 -0
- data/test/gnupg_test_home/receiver_pubring.gpg +0 -0
- data/test/gnupg_test_home/receiver_secring.gpg +0 -0
- data/test/gnupg_test_home/regen_keys.sh +11 -2
- data/test/gnupg_test_home/secring.gpg +0 -0
- data/test/gnupg_test_home/sup-test-2@foo.bar.asc +20 -20
- data/test/integration/test_draft.rb +128 -0
- data/test/integration/test_maildir.rb +17 -1
- data/test/integration/test_mbox.rb +12 -0
- data/test/integration/test_sup-add.rb +4 -0
- data/test/test_crypto.rb +114 -72
- data/test/test_message.rb +43 -0
- data/test/unit/test_contact.rb +16 -4
- data/test/unit/test_edit_message_mode.rb +99 -0
- data/test/unit/test_index.rb +65 -0
- data/test/unit/test_line_cursor_mode.rb +208 -0
- data/test/unit/test_person.rb +3 -3
- data/test/unit/test_rmail_message.rb +36 -0
- data/test/unit/util/test_string.rb +3 -3
- metadata +64 -9
- data/shell.nix +0 -1
- data/test/gnupg_test_home/private-keys-v1.d/306D2EE90FF0014B5B9FD07E265C751791674140.key +0 -0
data/lib/sup/index.rb
CHANGED
|
@@ -142,7 +142,7 @@ EOS
|
|
|
142
142
|
|
|
143
143
|
def save_index
|
|
144
144
|
info "Flushing Xapian updates to disk. This may take a while..."
|
|
145
|
-
@xapian.
|
|
145
|
+
@xapian.commit
|
|
146
146
|
end
|
|
147
147
|
|
|
148
148
|
def contains_id? id
|
|
@@ -516,9 +516,13 @@ EOS
|
|
|
516
516
|
qp.stemmer = Xapian::Stem.new($config[:stem_language])
|
|
517
517
|
qp.stemming_strategy = Xapian::QueryParser::STEM_SOME
|
|
518
518
|
qp.default_op = Xapian::Query::OP_AND
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
519
|
+
begin
|
|
520
|
+
rangeprocessor = Xapian::NumberRangeProcessor.new DATE_VALUENO, 'date:'
|
|
521
|
+
qp.add_rangeprocessor rangeprocessor
|
|
522
|
+
rescue NameError # xapian < 1.3
|
|
523
|
+
valuerangeprocessor = Xapian::NumberValueRangeProcessor.new DATE_VALUENO, 'date:', true
|
|
524
|
+
qp.add_valuerangeprocessor valuerangeprocessor
|
|
525
|
+
end
|
|
522
526
|
NORMAL_PREFIX.each { |k,info| info[:prefix].each {
|
|
523
527
|
|v| qp.add_prefix k, v }
|
|
524
528
|
}
|
data/lib/sup/maildir.rb
CHANGED
data/lib/sup/mbox.rb
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
require 'uri'
|
|
2
2
|
require 'set'
|
|
3
|
+
require 'time'
|
|
3
4
|
|
|
4
5
|
module Redwood
|
|
5
6
|
|
|
@@ -85,7 +86,7 @@ class MBox < Source
|
|
|
85
86
|
begin
|
|
86
87
|
## don't use RMail::Mailbox::MBoxReader because it doesn't properly ignore
|
|
87
88
|
## "From" at the start of a message body line.
|
|
88
|
-
string = ""
|
|
89
|
+
string = +""
|
|
89
90
|
until @f.eof? || MBox::is_break_line?(l = @f.gets)
|
|
90
91
|
string << l
|
|
91
92
|
end
|
|
@@ -97,7 +98,7 @@ class MBox < Source
|
|
|
97
98
|
end
|
|
98
99
|
|
|
99
100
|
def raw_header offset
|
|
100
|
-
ret = ""
|
|
101
|
+
ret = +""
|
|
101
102
|
@mutex.synchronize do
|
|
102
103
|
ensure_open
|
|
103
104
|
@f.seek offset
|
|
@@ -109,9 +110,7 @@ class MBox < Source
|
|
|
109
110
|
end
|
|
110
111
|
|
|
111
112
|
def raw_message offset
|
|
112
|
-
|
|
113
|
-
each_raw_message_line(offset) { |l| ret << l }
|
|
114
|
-
ret
|
|
113
|
+
enum_for(:each_raw_message_line, offset).reduce(:+)
|
|
115
114
|
end
|
|
116
115
|
|
|
117
116
|
def store_message date, from_email, &block
|
|
@@ -137,6 +136,26 @@ class MBox < Source
|
|
|
137
136
|
end
|
|
138
137
|
end
|
|
139
138
|
|
|
139
|
+
def fallback_date_for_message offset
|
|
140
|
+
## This is a bit awkward... We treat the From line as a delimiter,
|
|
141
|
+
## not part of the message. So the offset is pointing *after* the
|
|
142
|
+
## From line for the desired message. With a bit of effort we can
|
|
143
|
+
## scan backwards to find its From line and extract a date from it.
|
|
144
|
+
buf = @mutex.synchronize do
|
|
145
|
+
ensure_open
|
|
146
|
+
start = offset
|
|
147
|
+
loop do
|
|
148
|
+
start = (start - 200).clamp 0, 2**64
|
|
149
|
+
@f.seek start
|
|
150
|
+
buf = @f.read (offset - start)
|
|
151
|
+
break buf if buf.include? ?\n or start == 0
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
BREAK_RE.match buf.lines.last do |m|
|
|
155
|
+
Time.strptime m[1], "%a %b %d %H:%M:%S %Y"
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
|
|
140
159
|
def default_labels
|
|
141
160
|
[:inbox, :unread]
|
|
142
161
|
end
|
|
@@ -179,8 +198,7 @@ class MBox < Source
|
|
|
179
198
|
l =~ BREAK_RE or return false
|
|
180
199
|
time = $1
|
|
181
200
|
begin
|
|
182
|
-
|
|
183
|
-
Time.parse time, Time.at(0)
|
|
201
|
+
Time.strptime time, "%a %b %d %H:%M:%S %Y"
|
|
184
202
|
true
|
|
185
203
|
rescue NoMethodError, ArgumentError
|
|
186
204
|
warn "found invalid date in potential mbox split line, not splitting: #{l.inspect}"
|
data/lib/sup/message.rb
CHANGED
|
@@ -103,16 +103,10 @@ class Message
|
|
|
103
103
|
when Time
|
|
104
104
|
date
|
|
105
105
|
when String
|
|
106
|
-
|
|
107
|
-
Time.parse date
|
|
108
|
-
rescue ArgumentError
|
|
109
|
-
#debug "faking mangled date header for #{@id} (orig #{header['date'].inspect} gave error: #{e.message})"
|
|
110
|
-
Time.now
|
|
111
|
-
end
|
|
112
|
-
else
|
|
113
|
-
#debug "faking non-existent date header for #{@id}"
|
|
114
|
-
Time.now
|
|
106
|
+
Time.rfc2822 date rescue nil
|
|
115
107
|
end
|
|
108
|
+
@date = location.fallback_date if @date.nil?
|
|
109
|
+
@date = Time.utc 1970, 1, 1 if @date.nil?
|
|
116
110
|
|
|
117
111
|
subj = header["subject"]
|
|
118
112
|
subj = subj ? subj.fix_encoding! : nil
|
|
@@ -466,6 +460,11 @@ private
|
|
|
466
460
|
end
|
|
467
461
|
end
|
|
468
462
|
|
|
463
|
+
def has_embedded_message? m
|
|
464
|
+
return false unless m.header.content_type
|
|
465
|
+
%w(message/rfc822 message/global).include? m.header.content_type.downcase
|
|
466
|
+
end
|
|
467
|
+
|
|
469
468
|
## takes a RMail::Message, breaks it into Chunk:: classes.
|
|
470
469
|
def message_to_chunks m, encrypted=false, sibling_types=[]
|
|
471
470
|
if m.multipart?
|
|
@@ -483,7 +482,7 @@ private
|
|
|
483
482
|
end
|
|
484
483
|
|
|
485
484
|
chunks
|
|
486
|
-
elsif
|
|
485
|
+
elsif has_embedded_message? m
|
|
487
486
|
encoding = m.header["Content-Transfer-Encoding"]
|
|
488
487
|
if m.body
|
|
489
488
|
body =
|
|
@@ -808,6 +807,10 @@ class Location
|
|
|
808
807
|
source.load_message info
|
|
809
808
|
end
|
|
810
809
|
|
|
810
|
+
def fallback_date
|
|
811
|
+
source.fallback_date_for_message info
|
|
812
|
+
end
|
|
813
|
+
|
|
811
814
|
def valid?
|
|
812
815
|
source.valid? info
|
|
813
816
|
end
|
data/lib/sup/message_chunks.rb
CHANGED
|
@@ -35,30 +35,6 @@ require 'shellwords'
|
|
|
35
35
|
## included as quoted text during a reply. Text, Quotes, and mime-parsed
|
|
36
36
|
## attachments are quotable; Signatures are not.
|
|
37
37
|
|
|
38
|
-
## monkey-patch time: make temp files have the right extension
|
|
39
|
-
## Backport from Ruby 1.9.2 for versions lower than 1.8.7
|
|
40
|
-
if RUBY_VERSION < '1.8.7'
|
|
41
|
-
class Tempfile
|
|
42
|
-
def make_tmpname(prefix_suffix, n)
|
|
43
|
-
case prefix_suffix
|
|
44
|
-
when String
|
|
45
|
-
prefix = prefix_suffix
|
|
46
|
-
suffix = ""
|
|
47
|
-
when Array
|
|
48
|
-
prefix = prefix_suffix[0]
|
|
49
|
-
suffix = prefix_suffix[1]
|
|
50
|
-
else
|
|
51
|
-
raise ArgumentError, "unexpected prefix_suffix: #{prefix_suffix.inspect}"
|
|
52
|
-
end
|
|
53
|
-
t = Time.now.strftime("%Y%m%d")
|
|
54
|
-
path = "#{prefix}#{t}-#{$$}-#{rand(0x100000000).to_s(36)}"
|
|
55
|
-
path << "-#{n}" if n
|
|
56
|
-
path << suffix
|
|
57
|
-
end
|
|
58
|
-
end
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
|
|
62
38
|
module Redwood
|
|
63
39
|
module Chunk
|
|
64
40
|
class Attachment
|
data/lib/sup/mode.rb
CHANGED
|
@@ -83,7 +83,6 @@ class ContactListMode < LineCursorMode
|
|
|
83
83
|
def multi_search people
|
|
84
84
|
mode = PersonSearchResultsMode.new people
|
|
85
85
|
BufferManager.spawn "search for #{people.map { |p| p.name }.join(', ')}", mode
|
|
86
|
-
mode.load_threads :num => mode.buffer.content_height
|
|
87
86
|
end
|
|
88
87
|
|
|
89
88
|
def search
|
|
@@ -538,7 +538,7 @@ protected
|
|
|
538
538
|
BufferManager.kill_buffer buffer
|
|
539
539
|
BufferManager.flash "Message sent!"
|
|
540
540
|
true
|
|
541
|
-
rescue SystemCallError, SendmailCommandFailed, CryptoManager::Error => e
|
|
541
|
+
rescue SystemCallError, SendmailCommandFailed, CryptoManager::Error, TypeError => e
|
|
542
542
|
warn "Problem sending mail: #{e.message}"
|
|
543
543
|
BufferManager.flash "Problem sending mail: #{e.message}"
|
|
544
544
|
false
|
|
@@ -563,7 +563,7 @@ protected
|
|
|
563
563
|
## there are attachments, so wrap body in an attachment of its own
|
|
564
564
|
unless @attachments.empty?
|
|
565
565
|
body_m = m
|
|
566
|
-
body_m.header["Content-Disposition"] = "inline"
|
|
566
|
+
body_m.header["Content-Disposition"] = +"inline"
|
|
567
567
|
m = RMail::Message.new
|
|
568
568
|
|
|
569
569
|
m.add_part body_m
|
|
@@ -601,8 +601,8 @@ protected
|
|
|
601
601
|
m.header["Date"] = date.rfc2822
|
|
602
602
|
m.header["Message-Id"] = @message_id
|
|
603
603
|
m.header["User-Agent"] = "Sup/#{Redwood::VERSION}"
|
|
604
|
-
m.header["Content-Transfer-Encoding"] ||=
|
|
605
|
-
m.header["MIME-Version"] = "1.0" if m.multipart?
|
|
604
|
+
m.header["Content-Transfer-Encoding"] ||= +"8bit"
|
|
605
|
+
m.header["MIME-Version"] = +"1.0" if m.multipart?
|
|
606
606
|
m
|
|
607
607
|
end
|
|
608
608
|
|
|
@@ -716,10 +716,10 @@ private
|
|
|
716
716
|
## encode to quoted-printable for all text/* MIME types,
|
|
717
717
|
## use base64 otherwise
|
|
718
718
|
if msg_part.header["Content-Type"] =~ /text\/.*/
|
|
719
|
-
msg_part.header
|
|
719
|
+
msg_part.header.set "Content-Transfer-Encoding", +"quoted-printable"
|
|
720
720
|
msg_part.body = [msg_part.body].pack('M')
|
|
721
721
|
else
|
|
722
|
-
msg_part.header
|
|
722
|
+
msg_part.header.set "Content-Transfer-Encoding", +"base64"
|
|
723
723
|
msg_part.body = [msg_part.body].pack('m')
|
|
724
724
|
end
|
|
725
725
|
msg_part
|
|
@@ -29,8 +29,7 @@ class LabelSearchResultsMode < ThreadIndexMode
|
|
|
29
29
|
when :inbox
|
|
30
30
|
BufferManager.raise_to_front InboxMode.instance.buffer
|
|
31
31
|
else
|
|
32
|
-
|
|
33
|
-
b.mode.load_threads :num => b.content_height if new
|
|
32
|
+
BufferManager.spawn_unless_exists("All threads with label '#{label}'") { LabelSearchResultsMode.new [label] }
|
|
34
33
|
end
|
|
35
34
|
end
|
|
36
35
|
end
|
|
@@ -20,14 +20,19 @@ class LineCursorMode < ScrollMode
|
|
|
20
20
|
while true
|
|
21
21
|
e = @load_more_q.pop
|
|
22
22
|
@load_more_callbacks.each { |c| c.call e }
|
|
23
|
-
|
|
24
|
-
|
|
23
|
+
hysteresis = $config[:load_more_threads_hysteresis] || 0.5
|
|
24
|
+
if hysteresis > 0 then
|
|
25
|
+
sleep hysteresis
|
|
26
|
+
@load_more_q.pop until @load_more_q.empty?
|
|
27
|
+
end
|
|
25
28
|
end
|
|
26
29
|
end
|
|
27
30
|
|
|
28
31
|
super opts
|
|
29
32
|
end
|
|
30
33
|
|
|
34
|
+
def spawned; call_load_more_callbacks buffer.content_height + 1; end
|
|
35
|
+
|
|
31
36
|
def cleanup
|
|
32
37
|
@load_more_thread.kill
|
|
33
38
|
super
|
|
@@ -157,26 +162,22 @@ protected
|
|
|
157
162
|
end
|
|
158
163
|
end
|
|
159
164
|
|
|
160
|
-
|
|
165
|
+
def half_page_up
|
|
166
|
+
super
|
|
167
|
+
set_cursor_pos [botline, @curpos].min
|
|
168
|
+
end
|
|
169
|
+
|
|
161
170
|
def page_down
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
## if we're on the last page, and it's a full page, try and load
|
|
169
|
-
## more lines via the callbacks and then shift the page down
|
|
170
|
-
elsif topline == lines - buffer.content_height
|
|
171
|
-
call_load_more_callbacks buffer.content_height
|
|
172
|
-
super
|
|
171
|
+
relpos = @curpos - topline
|
|
172
|
+
super
|
|
173
|
+
set_cursor_pos [topline + relpos, lines - 1].min
|
|
174
|
+
call_load_more_callbacks buffer.content_height if lines < topline + buffer.content_height
|
|
175
|
+
end
|
|
173
176
|
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
set_cursor_pos [topline + relpos, lines - 1].min
|
|
179
|
-
end
|
|
177
|
+
def half_page_down
|
|
178
|
+
super
|
|
179
|
+
set_cursor_pos [topline, @curpos].max
|
|
180
|
+
call_load_more_callbacks buffer.content_height if lines < topline + buffer.content_height
|
|
180
181
|
end
|
|
181
182
|
|
|
182
183
|
def jump_to_start
|
|
@@ -187,6 +188,7 @@ protected
|
|
|
187
188
|
def jump_to_end
|
|
188
189
|
super if topline < (lines - buffer.content_height)
|
|
189
190
|
set_cursor_pos(lines - 1)
|
|
191
|
+
call_load_more_callbacks buffer.content_height
|
|
190
192
|
end
|
|
191
193
|
|
|
192
194
|
private
|
|
@@ -49,7 +49,6 @@ class SearchResultsMode < ThreadIndexMode
|
|
|
49
49
|
short_text = text.length < 20 ? text : text[0 ... 20] + "..."
|
|
50
50
|
mode = SearchResultsMode.new query
|
|
51
51
|
BufferManager.spawn "search: \"#{short_text}\"", mode
|
|
52
|
-
mode.load_threads :num => mode.buffer.content_height
|
|
53
52
|
rescue Index::ParseError => e
|
|
54
53
|
BufferManager.flash "Problem: #{e.message}!"
|
|
55
54
|
end
|
|
@@ -205,7 +205,7 @@ EOS
|
|
|
205
205
|
@layout[m].state = (@layout[m].state == :detailed ? :open : :detailed)
|
|
206
206
|
update
|
|
207
207
|
end
|
|
208
|
-
|
|
208
|
+
|
|
209
209
|
def reload
|
|
210
210
|
update
|
|
211
211
|
end
|
|
@@ -298,7 +298,6 @@ EOS
|
|
|
298
298
|
p = @person_lines[curpos] or return
|
|
299
299
|
mode = PersonSearchResultsMode.new [p]
|
|
300
300
|
BufferManager.spawn "Search for #{p.name}", mode
|
|
301
|
-
mode.load_threads :num => mode.buffer.content_height
|
|
302
301
|
end
|
|
303
302
|
|
|
304
303
|
def compose
|
data/lib/sup/rfc2047.rb
CHANGED
|
@@ -62,10 +62,13 @@ module Rfc2047
|
|
|
62
62
|
end
|
|
63
63
|
|
|
64
64
|
begin
|
|
65
|
-
text.force_encoding
|
|
65
|
+
text.force_encoding charset
|
|
66
|
+
text.encode! target
|
|
66
67
|
rescue ArgumentError, EncodingError
|
|
67
|
-
word
|
|
68
|
+
next word
|
|
68
69
|
end
|
|
70
|
+
next word unless text.valid_encoding?
|
|
71
|
+
text
|
|
69
72
|
end
|
|
70
73
|
end
|
|
71
74
|
end
|
data/lib/sup/source.rb
CHANGED
data/lib/sup/util.rb
CHANGED
|
@@ -80,7 +80,14 @@ module RMail
|
|
|
80
80
|
def self.make_file_attachment fn
|
|
81
81
|
bfn = File.basename fn
|
|
82
82
|
t = MIME::Types.type_for(bfn).first || MIME::Types.type_for("exe").first
|
|
83
|
-
|
|
83
|
+
payload = IO.read fn
|
|
84
|
+
## Need to encode as base64 or quoted-printable if any lines are longer than 998 chars.
|
|
85
|
+
encoding = if t.encoding != t.default_encoding and payload.each_line.any? { |l| l.length > 998 }
|
|
86
|
+
t.default_encoding
|
|
87
|
+
else
|
|
88
|
+
t.encoding
|
|
89
|
+
end
|
|
90
|
+
make_attachment payload, t.content_type, encoding, bfn.to_s
|
|
84
91
|
end
|
|
85
92
|
|
|
86
93
|
def charset
|
|
@@ -206,7 +213,7 @@ class String
|
|
|
206
213
|
end
|
|
207
214
|
|
|
208
215
|
def slice_by_display_length len
|
|
209
|
-
each_char.each_with_object "" do |c, buffer|
|
|
216
|
+
each_char.each_with_object (+"") do |c, buffer|
|
|
210
217
|
len -= Unicode::DisplayWidth.of(c)
|
|
211
218
|
return buffer if len < 0
|
|
212
219
|
buffer << c
|
data/lib/sup/version.rb
CHANGED
data/lib/sup.rb
CHANGED
|
@@ -142,7 +142,7 @@ module Redwood
|
|
|
142
142
|
File::open(fn) { |f| f.read }
|
|
143
143
|
end
|
|
144
144
|
## fix up malformed tag URIs created by earlier versions of sup
|
|
145
|
-
raw_contents.gsub!(/!supmua.org,2006-10-01\/(\S
|
|
145
|
+
raw_contents.gsub!(/!supmua.org,2006-10-01\/(\S+)/) { |m| "!<tag:supmua.org,2006-10-01/#{$1}>" }
|
|
146
146
|
if YAML.respond_to?(:unsafe_load) # Ruby 3.1+
|
|
147
147
|
YAML::unsafe_load raw_contents
|
|
148
148
|
else
|
data/man/sup-add.1
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
.\" Automatically generated by Pandoc 3.
|
|
1
|
+
.\" Automatically generated by Pandoc 3.7.0.2
|
|
2
2
|
.\"
|
|
3
|
-
.TH "SUP
|
|
3
|
+
.TH "SUP\-ADD" "1" "April 9, 2012" "Sup User Manual"
|
|
4
4
|
.SH NAME
|
|
5
|
-
sup
|
|
5
|
+
sup\-add \- add a source to the Sup source list
|
|
6
6
|
.SH SYNOPSIS
|
|
7
|
-
sup
|
|
7
|
+
sup\-add [\f[I]options\f[R]] [\f[I]source uri\&...\f[R]]
|
|
8
8
|
.SH DESCRIPTION
|
|
9
9
|
Add one ore more sources to the Sup source list
|
|
10
10
|
.PP
|
|
@@ -25,33 +25,33 @@ maildir://<path to Maildir directory>
|
|
|
25
25
|
.EE
|
|
26
26
|
.SH OPTIONS
|
|
27
27
|
.TP
|
|
28
|
-
|
|
28
|
+
\-a, \-\-archive
|
|
29
29
|
Automatically archive all new messages from thesesources.
|
|
30
30
|
.TP
|
|
31
|
-
|
|
31
|
+
\-u, \-\-unusual
|
|
32
32
|
Do not automatically poll these sources for new messages.
|
|
33
33
|
.TP
|
|
34
|
-
|
|
35
|
-
A comma
|
|
34
|
+
\-l \f[I]STRING\f[R], \-\-labels \f[I]STRING\f[R]
|
|
35
|
+
A comma\-separated set of labels to apply to all messages from this
|
|
36
36
|
source
|
|
37
37
|
.TP
|
|
38
|
-
|
|
38
|
+
\-f, \-\-force\-new
|
|
39
39
|
Create a new account for this source, even if one already exists
|
|
40
40
|
.TP
|
|
41
|
-
|
|
42
|
-
Reuse previously defined account user\
|
|
41
|
+
\-o \f[I]STRING\f[R], \-\-force\-account \f[I]STRING\f[R]
|
|
42
|
+
Reuse previously defined account user\(athostname
|
|
43
43
|
.TP
|
|
44
|
-
|
|
44
|
+
\-v, \-\-version
|
|
45
45
|
Print version and exit
|
|
46
46
|
.TP
|
|
47
|
-
|
|
47
|
+
\-h, \-\-help
|
|
48
48
|
Show help message
|
|
49
49
|
.SH FILES
|
|
50
50
|
.TP
|
|
51
51
|
$HOME/.sup/sources.yaml
|
|
52
52
|
Configuration file for Sup mail sources
|
|
53
53
|
.SH SEE ALSO
|
|
54
|
-
sup(1), sup
|
|
54
|
+
sup(1), sup\-config(1)
|
|
55
55
|
.SH REPORTING BUGS
|
|
56
56
|
You are welcome to submit bug reports to the Sup issue tracker, located
|
|
57
57
|
at
|
|
@@ -77,21 +77,21 @@ Sup Wiki:
|
|
|
77
77
|
.UE \c
|
|
78
78
|
.TP
|
|
79
79
|
Mailing list:
|
|
80
|
-
supmua\
|
|
80
|
+
supmua\(atgooglegroups.com
|
|
81
81
|
.RS
|
|
82
82
|
.PP
|
|
83
|
-
supmua+subscribe\
|
|
83
|
+
supmua+subscribe\(atgooglegroups.com
|
|
84
84
|
.PP
|
|
85
85
|
Archives: \c
|
|
86
86
|
.UR https://groups.google.com/d/forum/supmua/
|
|
87
87
|
.UE \c
|
|
88
88
|
.RE
|
|
89
89
|
.SH COPYRIGHT
|
|
90
|
-
Copyright © 2006
|
|
90
|
+
Copyright © 2006\-2009 William Morgan \c
|
|
91
91
|
.MT mworgan-sup@masanjin.net
|
|
92
92
|
.ME \c
|
|
93
93
|
.PP
|
|
94
|
-
Copyright \
|
|
94
|
+
Copyright \(at 2013\-2014 Sup developers
|
|
95
95
|
.PP
|
|
96
96
|
Permission is granted to copy and distribute this manual under the terms
|
|
97
97
|
of the GNU General Public License; either version 2 or (at your option)
|
data/man/sup-config.1
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
.\" Automatically generated by Pandoc 3.
|
|
1
|
+
.\" Automatically generated by Pandoc 3.7.0.2
|
|
2
2
|
.\"
|
|
3
|
-
.TH "SUP
|
|
3
|
+
.TH "SUP\-CONFIG" "1" "April 9, 2012" "Sup User Manual"
|
|
4
4
|
.SH NAME
|
|
5
|
-
sup
|
|
5
|
+
sup\-config \- interactive configuration tool for Sup
|
|
6
6
|
.SH SYNOPSIS
|
|
7
|
-
sup
|
|
7
|
+
sup\-config [\f[I]options\f[R]]
|
|
8
8
|
.SH DESCRIPTION
|
|
9
9
|
Interactive configuration tool for Sup.
|
|
10
|
-
Won\
|
|
10
|
+
Won\(cqt destroy existing configuration.
|
|
11
11
|
.SH OPTIONS
|
|
12
12
|
.TP
|
|
13
|
-
|
|
13
|
+
\-v, \-\-version
|
|
14
14
|
Print versian and exit
|
|
15
15
|
.TP
|
|
16
|
-
|
|
16
|
+
\-h, \-\-help
|
|
17
17
|
Show help message
|
|
18
18
|
.SH FILES
|
|
19
19
|
.TP
|
|
@@ -23,7 +23,7 @@ Configuration file for Sup
|
|
|
23
23
|
$HOME/.sup/sources.yaml
|
|
24
24
|
Configuration file for Sup mail sources
|
|
25
25
|
.SH SEE ALSO
|
|
26
|
-
sup(1), sup
|
|
26
|
+
sup(1), sup\-add(1)
|
|
27
27
|
.SH REPORTING BUGS
|
|
28
28
|
You are welcome to submit bug reports to the Sup issue tracker, located
|
|
29
29
|
at
|
|
@@ -49,21 +49,21 @@ Sup Wiki:
|
|
|
49
49
|
.UE \c
|
|
50
50
|
.TP
|
|
51
51
|
Mailing list:
|
|
52
|
-
supmua\
|
|
52
|
+
supmua\(atgooglegroups.com
|
|
53
53
|
.RS
|
|
54
54
|
.PP
|
|
55
|
-
supmua+subscribe\
|
|
55
|
+
supmua+subscribe\(atgooglegroups.com
|
|
56
56
|
.PP
|
|
57
57
|
Archives: \c
|
|
58
58
|
.UR https://groups.google.com/d/forum/supmua/
|
|
59
59
|
.UE \c
|
|
60
60
|
.RE
|
|
61
61
|
.SH COPYRIGHT
|
|
62
|
-
Copyright © 2006
|
|
62
|
+
Copyright © 2006\-2009 William Morgan \c
|
|
63
63
|
.MT mworgan-sup@masanjin.net
|
|
64
64
|
.ME \c
|
|
65
65
|
.PP
|
|
66
|
-
Copyright \
|
|
66
|
+
Copyright \(at 2013\-2014 Sup developers
|
|
67
67
|
.PP
|
|
68
68
|
Permission is granted to copy and distribute this manual under the terms
|
|
69
69
|
of the GNU General Public License; either version 2 or (at your option)
|
data/man/sup-dump.1
CHANGED
|
@@ -1,37 +1,38 @@
|
|
|
1
|
-
.\" Automatically generated by Pandoc 3.
|
|
1
|
+
.\" Automatically generated by Pandoc 3.7.0.2
|
|
2
2
|
.\"
|
|
3
|
-
.TH "SUP
|
|
3
|
+
.TH "SUP\-DUMP" "1" "April 9, 2012" "Sup User Manual"
|
|
4
4
|
.SH NAME
|
|
5
|
-
sup
|
|
5
|
+
sup\-dump \- dumps message state from Sup index
|
|
6
6
|
.SH SYNOPSIS
|
|
7
|
-
sup
|
|
7
|
+
sup\-dump [\f[I]options\f[R]]
|
|
8
8
|
.SH DESCRIPTION
|
|
9
9
|
Dumps all message state from the Sup index to standard out.
|
|
10
|
-
You can later use sup
|
|
10
|
+
You can later use sup\-sync \-\-restored \-\-restore to recover the
|
|
11
|
+
index.
|
|
11
12
|
.PP
|
|
12
13
|
This tool is primarily useful in the event that a Sup upgrade breaks
|
|
13
14
|
index format compatibility.
|
|
14
15
|
.SH OPTIONS
|
|
15
16
|
.TP
|
|
16
|
-
|
|
17
|
+
\-v, \-\-version
|
|
17
18
|
Print version and exit
|
|
18
19
|
.TP
|
|
19
|
-
|
|
20
|
+
\-h, \-\-help
|
|
20
21
|
Show help message
|
|
21
22
|
.SH EXAMPLES
|
|
22
23
|
Dump message state and store in file
|
|
23
24
|
.IP
|
|
24
25
|
.EX
|
|
25
|
-
sup
|
|
26
|
+
sup\-dump > filename
|
|
26
27
|
.EE
|
|
27
28
|
.PP
|
|
28
29
|
Dump message state and compress output to store in file
|
|
29
30
|
.IP
|
|
30
31
|
.EX
|
|
31
|
-
sup
|
|
32
|
+
sup\-dump | bzip2 > filename.bz2
|
|
32
33
|
.EE
|
|
33
34
|
.SH SEE ALSO
|
|
34
|
-
sup(1), sup
|
|
35
|
+
sup(1), sup\-sync(1), sup\-import\-dump(1)
|
|
35
36
|
.SH REPORTING BUGS
|
|
36
37
|
You are welcome to submit bug reports to the Sup issue tracker, located
|
|
37
38
|
at
|
|
@@ -57,21 +58,21 @@ Sup Wiki:
|
|
|
57
58
|
.UE \c
|
|
58
59
|
.TP
|
|
59
60
|
Mailing list:
|
|
60
|
-
supmua\
|
|
61
|
+
supmua\(atgooglegroups.com
|
|
61
62
|
.RS
|
|
62
63
|
.PP
|
|
63
|
-
supmua+subscribe\
|
|
64
|
+
supmua+subscribe\(atgooglegroups.com
|
|
64
65
|
.PP
|
|
65
66
|
Archives: \c
|
|
66
67
|
.UR https://groups.google.com/d/forum/supmua/
|
|
67
68
|
.UE \c
|
|
68
69
|
.RE
|
|
69
70
|
.SH COPYRIGHT
|
|
70
|
-
Copyright © 2006
|
|
71
|
+
Copyright © 2006\-2009 William Morgan \c
|
|
71
72
|
.MT mworgan-sup@masanjin.net
|
|
72
73
|
.ME \c
|
|
73
74
|
.PP
|
|
74
|
-
Copyright \
|
|
75
|
+
Copyright \(at 2013\-2014 Sup developers
|
|
75
76
|
.PP
|
|
76
77
|
Permission is granted to copy and distribute this manual under the terms
|
|
77
78
|
of the GNU General Public License; either version 2 or (at your option)
|