sup 0.14.1.1 → 0.15.0
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.
- data/CONTRIBUTORS +14 -11
- data/History.txt +44 -0
- data/README.md +9 -4
- data/ReleaseNotes +8 -0
- data/bin/sup +1 -0
- data/bin/sup-add +2 -1
- data/bin/sup-config +3 -0
- data/bin/sup-sync-back-maildir +127 -0
- data/bin/{sup-sync-back → sup-sync-back-mbox} +3 -3
- data/lib/sup.rb +75 -3
- data/lib/sup/index.rb +54 -16
- data/lib/sup/label.rb +2 -2
- data/lib/sup/maildir.rb +120 -47
- data/lib/sup/mbox.rb +1 -1
- data/lib/sup/message.rb +65 -2
- data/lib/sup/modes/edit_message_mode.rb +16 -8
- data/lib/sup/modes/forward_mode.rb +13 -4
- data/lib/sup/modes/reply_mode.rb +6 -0
- data/lib/sup/modes/search_list_mode.rb +12 -0
- data/lib/sup/modes/thread_index_mode.rb +30 -0
- data/lib/sup/modes/thread_view_mode.rb +15 -5
- data/lib/sup/poll.rb +62 -17
- data/lib/sup/search.rb +18 -0
- data/lib/sup/sent.rb +1 -1
- data/lib/sup/source.rb +32 -4
- data/lib/sup/thread.rb +6 -0
- data/lib/sup/util.rb +12 -2
- data/lib/sup/util/query.rb +5 -2
- data/lib/sup/version.rb +1 -1
- metadata +17 -16
data/lib/sup/index.rb
CHANGED
@@ -168,6 +168,32 @@ EOS
|
|
168
168
|
matchset.matches_estimated
|
169
169
|
end
|
170
170
|
|
171
|
+
## check if a message is part of a killed thread
|
172
|
+
## (warning: duplicates code below)
|
173
|
+
## NOTE: We can be more efficient if we assume every
|
174
|
+
## killed message that hasn't been initially added
|
175
|
+
## to the indexi s this way
|
176
|
+
def message_joining_killed? m
|
177
|
+
return false unless doc = find_doc(m.id)
|
178
|
+
queue = doc.value(THREAD_VALUENO).split(',')
|
179
|
+
seen_threads = Set.new
|
180
|
+
seen_messages = Set.new [m.id]
|
181
|
+
while not queue.empty?
|
182
|
+
thread_id = queue.pop
|
183
|
+
next if seen_threads.member? thread_id
|
184
|
+
return true if thread_killed?(thread_id)
|
185
|
+
seen_threads << thread_id
|
186
|
+
docs = term_docids(mkterm(:thread, thread_id)).map { |x| @xapian.document x }
|
187
|
+
docs.each do |doc|
|
188
|
+
msgid = doc.value MSGID_VALUENO
|
189
|
+
next if seen_messages.member? msgid
|
190
|
+
seen_messages << msgid
|
191
|
+
queue.concat doc.value(THREAD_VALUENO).split(',')
|
192
|
+
end
|
193
|
+
end
|
194
|
+
false
|
195
|
+
end
|
196
|
+
|
171
197
|
## yield all messages in the thread containing 'm' by repeatedly
|
172
198
|
## querying the index. yields pairs of message ids and
|
173
199
|
## message-building lambdas, so that building an unwanted message
|
@@ -248,11 +274,11 @@ EOS
|
|
248
274
|
|
249
275
|
## Yield each message-id matching query
|
250
276
|
EACH_ID_PAGE = 100
|
251
|
-
def each_id query={}
|
277
|
+
def each_id query={}, ignore_neg_terms = true
|
252
278
|
offset = 0
|
253
279
|
page = EACH_ID_PAGE
|
254
280
|
|
255
|
-
xapian_query = build_xapian_query query
|
281
|
+
xapian_query = build_xapian_query query, ignore_neg_terms
|
256
282
|
while true
|
257
283
|
ids = run_query_ids xapian_query, offset, (offset+page)
|
258
284
|
ids.each { |id| yield id }
|
@@ -262,8 +288,12 @@ EOS
|
|
262
288
|
end
|
263
289
|
|
264
290
|
## Yield each message matching query
|
265
|
-
|
266
|
-
|
291
|
+
## The ignore_neg_terms parameter is used to display result even if
|
292
|
+
## it contains "forbidden" labels such as :deleted, it is used in
|
293
|
+
## Poll#poll_from when we need to get the location of a message that
|
294
|
+
## may contain these labels
|
295
|
+
def each_message query={}, ignore_neg_terms = true, &b
|
296
|
+
each_id query, ignore_neg_terms do |id|
|
267
297
|
yield build_message(id)
|
268
298
|
end
|
269
299
|
end
|
@@ -313,9 +343,9 @@ EOS
|
|
313
343
|
## Yields (in lexicographical order) the source infos of all locations from
|
314
344
|
## the given source with the given source_info prefix
|
315
345
|
def each_source_info source_id, prefix='', &b
|
316
|
-
|
317
|
-
each_prefixed_term
|
318
|
-
yield x[
|
346
|
+
p = mkterm :location, source_id, prefix
|
347
|
+
each_prefixed_term p do |x|
|
348
|
+
yield prefix + x[p.length..-1]
|
319
349
|
end
|
320
350
|
end
|
321
351
|
|
@@ -492,7 +522,7 @@ EOS
|
|
492
522
|
raise ParseError, "xapian query parser error: #{e}"
|
493
523
|
end
|
494
524
|
|
495
|
-
debug "parsed xapian query: #{Util::Query.describe(xapian_query)}"
|
525
|
+
debug "parsed xapian query: #{Util::Query.describe(xapian_query, subs)}"
|
496
526
|
|
497
527
|
raise ParseError if xapian_query.nil? or xapian_query.empty?
|
498
528
|
query[:qobj] = xapian_query
|
@@ -500,14 +530,18 @@ EOS
|
|
500
530
|
query
|
501
531
|
end
|
502
532
|
|
533
|
+
def save_message m
|
534
|
+
if @sync_worker
|
535
|
+
@sync_queue << m
|
536
|
+
else
|
537
|
+
update_message_state m
|
538
|
+
end
|
539
|
+
m.clear_dirty
|
540
|
+
end
|
541
|
+
|
503
542
|
def save_thread t
|
504
543
|
t.each_dirty_message do |m|
|
505
|
-
|
506
|
-
@sync_queue << m
|
507
|
-
else
|
508
|
-
update_message_state m
|
509
|
-
end
|
510
|
-
m.clear_dirty
|
544
|
+
save_message m
|
511
545
|
end
|
512
546
|
end
|
513
547
|
|
@@ -614,7 +648,7 @@ EOS
|
|
614
648
|
end
|
615
649
|
|
616
650
|
Q = Xapian::Query
|
617
|
-
def build_xapian_query opts
|
651
|
+
def build_xapian_query opts, ignore_neg_terms = true
|
618
652
|
labels = ([opts[:label]] + (opts[:labels] || [])).compact
|
619
653
|
neglabels = [:spam, :deleted, :killed].reject { |l| (labels.include? l) || opts.member?("load_#{l}".intern) }
|
620
654
|
pos_terms, neg_terms = [], []
|
@@ -630,7 +664,7 @@ EOS
|
|
630
664
|
pos_terms << Q.new(Q::OP_OR, participant_terms)
|
631
665
|
end
|
632
666
|
|
633
|
-
neg_terms.concat(neglabels.map { |l| mkterm(:label,l) })
|
667
|
+
neg_terms.concat(neglabels.map { |l| mkterm(:label,l) }) if ignore_neg_terms
|
634
668
|
|
635
669
|
pos_query = Q.new(Q::OP_AND, pos_terms)
|
636
670
|
neg_query = Q.new(Q::OP_OR, neg_terms)
|
@@ -643,6 +677,10 @@ EOS
|
|
643
677
|
end
|
644
678
|
|
645
679
|
def sync_message m, overwrite
|
680
|
+
## TODO: we should not save the message if the sync_back failed
|
681
|
+
## since it would overwrite the location field
|
682
|
+
m.sync_back
|
683
|
+
|
646
684
|
doc = synchronize { find_doc(m.id) }
|
647
685
|
existed = doc != nil
|
648
686
|
doc ||= Xapian::Document.new
|
data/lib/sup/label.rb
CHANGED
@@ -7,10 +7,10 @@ class LabelManager
|
|
7
7
|
|
8
8
|
## labels that have special semantics. user will be unable to
|
9
9
|
## add/remove these via normal label mechanisms.
|
10
|
-
RESERVED_LABELS = [ :starred, :spam, :draft, :unread, :killed, :sent, :deleted, :inbox, :attachment ]
|
10
|
+
RESERVED_LABELS = [ :starred, :spam, :draft, :unread, :killed, :sent, :deleted, :inbox, :attachment, :forwarded, :replied ]
|
11
11
|
|
12
12
|
## labels that will typically be hidden from the user
|
13
|
-
HIDDEN_RESERVED_LABELS = [ :starred, :unread, :attachment ]
|
13
|
+
HIDDEN_RESERVED_LABELS = [ :starred, :unread, :attachment, :forwarded, :replied ]
|
14
14
|
|
15
15
|
def initialize fn
|
16
16
|
@fn = fn
|
data/lib/sup/maildir.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'uri'
|
2
|
+
require 'set'
|
2
3
|
|
3
4
|
module Redwood
|
4
5
|
|
@@ -7,8 +8,8 @@ class Maildir < Source
|
|
7
8
|
MYHOSTNAME = Socket.gethostname
|
8
9
|
|
9
10
|
## remind me never to use inheritance again.
|
10
|
-
yaml_properties :uri, :usual, :archived, :id, :labels
|
11
|
-
def initialize uri, usual=true, archived=false, id=nil, labels=[]
|
11
|
+
yaml_properties :uri, :usual, :archived, :sync_back, :id, :labels
|
12
|
+
def initialize uri, usual=true, archived=false, sync_back=true, id=nil, labels=[]
|
12
13
|
super uri, usual, archived, id
|
13
14
|
@expanded_uri = Source.expand_filesystem_uri(uri)
|
14
15
|
uri = URI(@expanded_uri)
|
@@ -17,16 +18,28 @@ class Maildir < Source
|
|
17
18
|
raise ArgumentError, "maildir URI cannot have a host: #{uri.host}" if uri.host
|
18
19
|
raise ArgumentError, "maildir URI must have a path component" unless uri.path
|
19
20
|
|
21
|
+
@sync_back = sync_back
|
22
|
+
# sync by default if not specified
|
23
|
+
@sync_back = true if @sync_back.nil?
|
24
|
+
|
20
25
|
@dir = uri.path
|
21
26
|
@labels = Set.new(labels || [])
|
22
27
|
@mutex = Mutex.new
|
23
|
-
@
|
28
|
+
@ctimes = { 'cur' => Time.at(0), 'new' => Time.at(0) }
|
24
29
|
end
|
25
30
|
|
26
31
|
def file_path; @dir end
|
27
32
|
def self.suggest_labels_for path; [] end
|
28
33
|
def is_source_for? uri; super || (uri == @expanded_uri); end
|
29
34
|
|
35
|
+
def supported_labels?
|
36
|
+
[:draft, :starred, :forwarded, :replied, :unread, :deleted]
|
37
|
+
end
|
38
|
+
|
39
|
+
def sync_back_enabled?
|
40
|
+
@sync_back
|
41
|
+
end
|
42
|
+
|
30
43
|
def store_message date, from_email, &block
|
31
44
|
stored = false
|
32
45
|
new_fn = new_maildir_basefn + ':2,S'
|
@@ -44,7 +57,7 @@ class Maildir < Source
|
|
44
57
|
f.fsync
|
45
58
|
end
|
46
59
|
|
47
|
-
File.
|
60
|
+
File.safe_link tmp_path, new_path
|
48
61
|
stored = true
|
49
62
|
ensure
|
50
63
|
File.unlink tmp_path if File.exists? tmp_path
|
@@ -71,6 +84,14 @@ class Maildir < Source
|
|
71
84
|
with_file_for(id) { |f| RMail::Parser.read f }
|
72
85
|
end
|
73
86
|
|
87
|
+
def sync_back id, labels
|
88
|
+
synchronize do
|
89
|
+
debug "syncing back maildir message #{id} with flags #{labels.to_a}"
|
90
|
+
flags = maildir_reconcile_flags id, labels
|
91
|
+
maildir_mark_file id, flags
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
74
95
|
def raw_header id
|
75
96
|
ret = ""
|
76
97
|
with_file_for(id) do |f|
|
@@ -87,41 +108,78 @@ class Maildir < Source
|
|
87
108
|
|
88
109
|
## XXX use less memory
|
89
110
|
def poll
|
90
|
-
|
111
|
+
added = []
|
112
|
+
deleted = []
|
113
|
+
updated = []
|
114
|
+
@ctimes.each do |d,prev_ctime|
|
91
115
|
subdir = File.join @dir, d
|
92
116
|
debug "polling maildir #{subdir}"
|
93
117
|
raise FatalSourceError, "#{subdir} not a directory" unless File.directory? subdir
|
94
|
-
|
95
|
-
next if
|
96
|
-
@
|
118
|
+
ctime = File.ctime subdir
|
119
|
+
next if prev_ctime >= ctime
|
120
|
+
@ctimes[d] = ctime
|
97
121
|
|
98
122
|
old_ids = benchmark(:maildir_read_index) { Enumerator.new(Index.instance, :each_source_info, self.id, "#{d}/").to_a }
|
99
|
-
new_ids = benchmark(:maildir_read_dir) { Dir.glob("#{subdir}/*").map { |x| File.basename
|
100
|
-
added
|
101
|
-
deleted
|
123
|
+
new_ids = benchmark(:maildir_read_dir) { Dir.glob("#{subdir}/*").map { |x| File.join(d,File.basename(x)) }.sort }
|
124
|
+
added += new_ids - old_ids
|
125
|
+
deleted += old_ids - new_ids
|
102
126
|
debug "#{old_ids.size} in index, #{new_ids.size} in filesystem"
|
103
|
-
|
127
|
+
end
|
104
128
|
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
129
|
+
## find updated mails by checking if an id is in both added and
|
130
|
+
## deleted arrays, meaning that its flags changed or that it has
|
131
|
+
## been moved, these ids need to be removed from added and deleted
|
132
|
+
add_to_delete = del_to_delete = []
|
133
|
+
map = Hash.new { |hash, key| hash[key] = [] }
|
134
|
+
deleted.each do |id_del|
|
135
|
+
map[maildir_data(id_del)[0]].push id_del
|
136
|
+
end
|
137
|
+
added.each do |id_add|
|
138
|
+
map[maildir_data(id_add)[0]].each do |id_del|
|
139
|
+
updated.push [ id_del, id_add ]
|
140
|
+
add_to_delete.push id_add
|
141
|
+
del_to_delete.push id_del
|
142
|
+
end
|
143
|
+
end
|
144
|
+
added -= add_to_delete
|
145
|
+
deleted -= del_to_delete
|
146
|
+
debug "#{added.size} added, #{deleted.size} deleted, #{updated.size} updated"
|
147
|
+
total_size = added.size+deleted.size+updated.size
|
111
148
|
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
149
|
+
added.each_with_index do |id,i|
|
150
|
+
yield :add,
|
151
|
+
:info => id,
|
152
|
+
:labels => @labels + maildir_labels(id) + [:inbox],
|
153
|
+
:progress => i.to_f/total_size
|
154
|
+
end
|
155
|
+
|
156
|
+
deleted.each_with_index do |id,i|
|
157
|
+
yield :delete,
|
158
|
+
:info => id,
|
159
|
+
:progress => (i.to_f+added.size)/total_size
|
160
|
+
end
|
161
|
+
|
162
|
+
updated.each_with_index do |id,i|
|
163
|
+
yield :update,
|
164
|
+
:old_info => id[0],
|
165
|
+
:new_info => id[1],
|
166
|
+
:labels => @labels + maildir_labels(id[1]),
|
167
|
+
:progress => (i.to_f+added.size+deleted.size)/total_size
|
117
168
|
end
|
118
169
|
nil
|
119
170
|
end
|
120
171
|
|
172
|
+
def labels? id
|
173
|
+
maildir_labels id
|
174
|
+
end
|
175
|
+
|
121
176
|
def maildir_labels id
|
122
177
|
(seen?(id) ? [] : [:unread]) +
|
123
178
|
(trashed?(id) ? [:deleted] : []) +
|
124
|
-
(flagged?(id) ? [:starred] : [])
|
179
|
+
(flagged?(id) ? [:starred] : []) +
|
180
|
+
(passed?(id) ? [:forwarded] : []) +
|
181
|
+
(replied?(id) ? [:replied] : []) +
|
182
|
+
(draft?(id) ? [:draft] : [])
|
125
183
|
end
|
126
184
|
|
127
185
|
def draft? id; maildir_data(id)[2].include? "D"; end
|
@@ -131,13 +189,6 @@ class Maildir < Source
|
|
131
189
|
def seen? id; maildir_data(id)[2].include? "S"; end
|
132
190
|
def trashed? id; maildir_data(id)[2].include? "T"; end
|
133
191
|
|
134
|
-
def mark_draft id; maildir_mark_file id, "D" unless draft? id; end
|
135
|
-
def mark_flagged id; maildir_mark_file id, "F" unless flagged? id; end
|
136
|
-
def mark_passed id; maildir_mark_file id, "P" unless passed? id; end
|
137
|
-
def mark_replied id; maildir_mark_file id, "R" unless replied? id; end
|
138
|
-
def mark_seen id; maildir_mark_file id, "S" unless seen? id; end
|
139
|
-
def mark_trashed id; maildir_mark_file id, "T" unless trashed? id; end
|
140
|
-
|
141
192
|
def valid? id
|
142
193
|
File.exists? File.join(@dir, id)
|
143
194
|
end
|
@@ -159,25 +210,47 @@ private
|
|
159
210
|
end
|
160
211
|
|
161
212
|
def maildir_data id
|
162
|
-
id
|
213
|
+
id = File.basename id
|
214
|
+
# Flags we recognize are DFPRST
|
215
|
+
id =~ %r{^([^:]+):([12]),([A-Za-z]*)$}
|
163
216
|
[($1 || id), ($2 || "2"), ($3 || "")]
|
164
217
|
end
|
165
218
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
219
|
+
def maildir_reconcile_flags id, labels
|
220
|
+
new_flags = Set.new( maildir_data(id)[2].each_char )
|
221
|
+
|
222
|
+
# Set flags based on labels for the six flags we recognize
|
223
|
+
if labels.member? :draft then new_flags.add?( "D" ) else new_flags.delete?( "D" ) end
|
224
|
+
if labels.member? :starred then new_flags.add?( "F" ) else new_flags.delete?( "F" ) end
|
225
|
+
if labels.member? :forwarded then new_flags.add?( "P" ) else new_flags.delete?( "P" ) end
|
226
|
+
if labels.member? :replied then new_flags.add?( "R" ) else new_flags.delete?( "R" ) end
|
227
|
+
if not labels.member? :unread then new_flags.add?( "S" ) else new_flags.delete?( "S" ) end
|
228
|
+
if labels.member? :deleted or labels.member? :killed then new_flags.add?( "T" ) else new_flags.delete?( "T" ) end
|
229
|
+
|
230
|
+
## Flags must be stored in ASCII order according to Maildir
|
231
|
+
## documentation
|
232
|
+
new_flags.to_a.sort.join
|
233
|
+
end
|
234
|
+
|
235
|
+
def maildir_mark_file orig_path, flags
|
236
|
+
@mutex.synchronize do
|
237
|
+
new_base = (flags.include?("S")) ? "cur" : "new"
|
238
|
+
md_base, md_ver, md_flags = maildir_data orig_path
|
239
|
+
|
240
|
+
return if md_flags == flags
|
241
|
+
|
242
|
+
new_loc = File.join new_base, "#{md_base}:#{md_ver},#{flags}"
|
243
|
+
orig_path = File.join @dir, orig_path
|
244
|
+
new_path = File.join @dir, new_loc
|
245
|
+
tmp_path = File.join @dir, "tmp", "#{md_base}:#{md_ver},#{flags}"
|
246
|
+
|
247
|
+
File.safe_link orig_path, tmp_path
|
248
|
+
File.unlink orig_path
|
249
|
+
File.safe_link tmp_path, new_path
|
250
|
+
File.unlink tmp_path
|
251
|
+
|
252
|
+
new_loc
|
253
|
+
end
|
181
254
|
end
|
182
255
|
end
|
183
256
|
|
data/lib/sup/mbox.rb
CHANGED
@@ -120,7 +120,7 @@ class MBox < Source
|
|
120
120
|
## into memory with raw_message.
|
121
121
|
##
|
122
122
|
## i hoped never to have to move shit around on disk but
|
123
|
-
## sup-sync-back has to do it.
|
123
|
+
## sup-sync-back-mbox has to do it.
|
124
124
|
def each_raw_message_line offset
|
125
125
|
@mutex.synchronize do
|
126
126
|
ensure_open
|
data/lib/sup/message.rb
CHANGED
@@ -291,6 +291,32 @@ EOS
|
|
291
291
|
location.each_raw_message_line &b
|
292
292
|
end
|
293
293
|
|
294
|
+
def sync_back
|
295
|
+
@locations.map { |l| l.sync_back @labels, self }.any? do
|
296
|
+
UpdateManager.relay self, :updated, self
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
def merge_labels_from_locations merge_labels
|
301
|
+
## Get all labels from all locations
|
302
|
+
location_labels = Set.new([])
|
303
|
+
|
304
|
+
@locations.each do |l|
|
305
|
+
if l.valid?
|
306
|
+
location_labels = location_labels.union(l.labels?)
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
## Add to the message labels the intersection between all location
|
311
|
+
## labels and those we want to merge
|
312
|
+
location_labels = location_labels.intersection(merge_labels.to_set)
|
313
|
+
|
314
|
+
if not location_labels.empty?
|
315
|
+
@labels = @labels.union(location_labels)
|
316
|
+
@dirty = true
|
317
|
+
end
|
318
|
+
end
|
319
|
+
|
294
320
|
## returns all the content from a message that will be indexed
|
295
321
|
def indexable_content
|
296
322
|
load_from_source!
|
@@ -545,10 +571,18 @@ private
|
|
545
571
|
## (and possible signed) inline GPG messages
|
546
572
|
def inline_gpg_to_chunks body, encoding_to, encoding_from
|
547
573
|
lines = body.split("\n")
|
574
|
+
|
575
|
+
# First case: Message is enclosed between
|
576
|
+
#
|
577
|
+
# -----BEGIN PGP SIGNED MESSAGE-----
|
578
|
+
# and
|
579
|
+
# -----END PGP SIGNED MESSAGE-----
|
580
|
+
#
|
581
|
+
# In some cases, END PGP SIGNED MESSAGE doesn't appear
|
548
582
|
gpg = lines.between(GPG_SIGNED_START, GPG_SIGNED_END)
|
549
583
|
# between does not check if GPG_END actually exists
|
550
584
|
# Reference: http://permalink.gmane.org/gmane.mail.sup.devel/641
|
551
|
-
if !gpg.empty?
|
585
|
+
if !gpg.empty?
|
552
586
|
msg = RMail::Message.new
|
553
587
|
msg.body = gpg.join("\n")
|
554
588
|
|
@@ -560,14 +594,21 @@ private
|
|
560
594
|
before = startidx != 0 ? lines[0 .. startidx-1] : []
|
561
595
|
after = endidx ? lines[endidx+1 .. lines.size] : []
|
562
596
|
|
597
|
+
# sig contains BEGIN PGP SIGNED MESSAGE and END PGP SIGNATURE, so
|
598
|
+
# we ditch them. sig may also contain the hash used by PGP (with a
|
599
|
+
# newline), so we also skip them
|
600
|
+
sig_start = sig[1].match(/^Hash:/) ? 3 : 1
|
601
|
+
sig_end = sig.size-2
|
563
602
|
payload = RMail::Message.new
|
564
|
-
payload.body = sig[
|
603
|
+
payload.body = sig[sig_start, sig_end].join("\n")
|
565
604
|
return [text_to_chunks(before, false),
|
566
605
|
CryptoManager.verify(nil, msg, false),
|
567
606
|
message_to_chunks(payload),
|
568
607
|
text_to_chunks(after, false)].flatten.compact
|
569
608
|
end
|
570
609
|
|
610
|
+
# Second case: Message is encrypted
|
611
|
+
|
571
612
|
gpg = lines.between(GPG_START, GPG_END)
|
572
613
|
# between does not check if GPG_END actually exists
|
573
614
|
if !gpg.empty? && !lines.index(GPG_END).nil?
|
@@ -704,6 +745,24 @@ class Location
|
|
704
745
|
source.raw_message info
|
705
746
|
end
|
706
747
|
|
748
|
+
def sync_back labels, message
|
749
|
+
synced = false
|
750
|
+
return synced unless sync_back_enabled? and valid?
|
751
|
+
source.synchronize do
|
752
|
+
new_info = source.sync_back(@info, labels)
|
753
|
+
if new_info
|
754
|
+
@info = new_info
|
755
|
+
Index.sync_message message, true
|
756
|
+
synced = true
|
757
|
+
end
|
758
|
+
end
|
759
|
+
synced
|
760
|
+
end
|
761
|
+
|
762
|
+
def sync_back_enabled?
|
763
|
+
source.respond_to? :sync_back and $config[:sync_back_to_maildir] and source.sync_back_enabled?
|
764
|
+
end
|
765
|
+
|
707
766
|
## much faster than raw_message
|
708
767
|
def each_raw_message_line &b
|
709
768
|
source.each_raw_message_line info, &b
|
@@ -717,6 +776,10 @@ class Location
|
|
717
776
|
source.valid? info
|
718
777
|
end
|
719
778
|
|
779
|
+
def labels?
|
780
|
+
source.labels? info
|
781
|
+
end
|
782
|
+
|
720
783
|
def == o
|
721
784
|
o.source.id == source.id and o.info == info
|
722
785
|
end
|