sup 0.14.1.1 → 0.15.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|