sup 0.8.1 → 0.9
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of sup might be problematic. Click here for more details.
- data/CONTRIBUTORS +13 -6
- data/History.txt +19 -0
- data/ReleaseNotes +35 -0
- data/bin/sup +82 -77
- data/bin/sup-add +7 -7
- data/bin/sup-config +104 -85
- data/bin/sup-dump +4 -5
- data/bin/sup-recover-sources +9 -10
- data/bin/sup-sync +121 -100
- data/bin/sup-sync-back +18 -15
- data/bin/sup-tweak-labels +24 -21
- data/lib/sup.rb +53 -33
- data/lib/sup/account.rb +0 -2
- data/lib/sup/buffer.rb +47 -22
- data/lib/sup/colormap.rb +6 -6
- data/lib/sup/contact.rb +0 -2
- data/lib/sup/crypto.rb +34 -23
- data/lib/sup/draft.rb +6 -14
- data/lib/sup/ferret_index.rb +471 -0
- data/lib/sup/hook.rb +30 -43
- data/lib/sup/hook.rb.BACKUP.8625.rb +158 -0
- data/lib/sup/hook.rb.BACKUP.8681.rb +158 -0
- data/lib/sup/hook.rb.BASE.8625.rb +155 -0
- data/lib/sup/hook.rb.BASE.8681.rb +155 -0
- data/lib/sup/hook.rb.LOCAL.8625.rb +142 -0
- data/lib/sup/hook.rb.LOCAL.8681.rb +142 -0
- data/lib/sup/hook.rb.REMOTE.8625.rb +145 -0
- data/lib/sup/hook.rb.REMOTE.8681.rb +145 -0
- data/lib/sup/imap.rb +18 -8
- data/lib/sup/index.rb +70 -528
- data/lib/sup/interactive-lock.rb +74 -0
- data/lib/sup/keymap.rb +26 -26
- data/lib/sup/label.rb +2 -4
- data/lib/sup/logger.rb +54 -35
- data/lib/sup/maildir.rb +41 -6
- data/lib/sup/mbox.rb +1 -1
- data/lib/sup/mbox/loader.rb +18 -6
- data/lib/sup/mbox/ssh-file.rb +1 -7
- data/lib/sup/message-chunks.rb +36 -23
- data/lib/sup/message.rb +126 -46
- data/lib/sup/mode.rb +3 -2
- data/lib/sup/modes/console-mode.rb +108 -0
- data/lib/sup/modes/edit-message-mode.rb +15 -5
- data/lib/sup/modes/inbox-mode.rb +2 -4
- data/lib/sup/modes/label-list-mode.rb +1 -1
- data/lib/sup/modes/line-cursor-mode.rb +18 -18
- data/lib/sup/modes/log-mode.rb +29 -16
- data/lib/sup/modes/poll-mode.rb +7 -9
- data/lib/sup/modes/reply-mode.rb +5 -3
- data/lib/sup/modes/scroll-mode.rb +2 -2
- data/lib/sup/modes/search-results-mode.rb +9 -11
- data/lib/sup/modes/text-mode.rb +2 -2
- data/lib/sup/modes/thread-index-mode.rb +26 -16
- data/lib/sup/modes/thread-view-mode.rb +84 -39
- data/lib/sup/person.rb +6 -8
- data/lib/sup/poll.rb +46 -47
- data/lib/sup/rfc2047.rb +1 -5
- data/lib/sup/sent.rb +27 -20
- data/lib/sup/source.rb +90 -13
- data/lib/sup/textfield.rb +4 -4
- data/lib/sup/thread.rb +15 -13
- data/lib/sup/undo.rb +0 -1
- data/lib/sup/update.rb +0 -1
- data/lib/sup/util.rb +51 -43
- data/lib/sup/xapian_index.rb +566 -0
- metadata +57 -46
- data/lib/sup/suicide.rb +0 -36
data/lib/sup/mbox/ssh-file.rb
CHANGED
@@ -16,12 +16,6 @@ class SSHFileError < StandardError; end
|
|
16
16
|
## all of the methods here can throw SSHFileErrors, SocketErrors,
|
17
17
|
## Net::SSH::Exceptions and Errno::ENOENTs.
|
18
18
|
|
19
|
-
## debugging TODO: remove me
|
20
|
-
def debug s
|
21
|
-
Redwood::log s
|
22
|
-
end
|
23
|
-
module_function :debug
|
24
|
-
|
25
19
|
## a simple buffer of contiguous data
|
26
20
|
class Buffer
|
27
21
|
def initialize
|
@@ -154,7 +148,7 @@ private
|
|
154
148
|
## TODO: share this code with imap
|
155
149
|
def say s
|
156
150
|
@say_id = BufferManager.say s, @say_id if BufferManager.instantiated?
|
157
|
-
|
151
|
+
info s
|
158
152
|
end
|
159
153
|
|
160
154
|
def shutup
|
data/lib/sup/message-chunks.rb
CHANGED
@@ -50,7 +50,8 @@ directly in Sup. For attachments that you wish to use a separate program
|
|
50
50
|
to view (e.g. images), you should use the mime-view hook instead.
|
51
51
|
|
52
52
|
Variables:
|
53
|
-
content_type: the content-type of the
|
53
|
+
content_type: the content-type of the attachment
|
54
|
+
charset: the charset of the attachment, if applicable
|
54
55
|
filename: the filename of the attachment as saved to disk
|
55
56
|
sibling_types: if this attachment is part of a multipart MIME attachment,
|
56
57
|
an array of content-types for all attachments. Otherwise,
|
@@ -85,7 +86,7 @@ EOS
|
|
85
86
|
bool_reader :quotable
|
86
87
|
|
87
88
|
def initialize content_type, filename, encoded_content, sibling_types
|
88
|
-
@content_type = content_type
|
89
|
+
@content_type = content_type.downcase
|
89
90
|
@filename = filename
|
90
91
|
@quotable = false # changed to true if we can parse it through the
|
91
92
|
# mime-decode hook, or if it's plain text
|
@@ -96,15 +97,15 @@ EOS
|
|
96
97
|
"For some bizarre reason, RubyMail was unable to parse this attachment.\n"
|
97
98
|
end
|
98
99
|
|
99
|
-
text =
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
100
|
+
text = case @content_type
|
101
|
+
when /^text\/plain\b/
|
102
|
+
Iconv.easy_decode $encoding, encoded_content.charset || $encoding, @raw_content
|
103
|
+
else
|
104
|
+
HookManager.run "mime-decode", :content_type => content_type,
|
105
|
+
:filename => lambda { write_to_disk },
|
106
|
+
:charset => encoded_content.charset,
|
107
|
+
:sibling_types => sibling_types
|
108
|
+
end
|
108
109
|
|
109
110
|
@lines = nil
|
110
111
|
if text
|
@@ -131,9 +132,9 @@ EOS
|
|
131
132
|
def initial_state; :open end
|
132
133
|
def viewable?; @lines.nil? end
|
133
134
|
def view_default! path
|
134
|
-
cmd = "/usr/bin/run-mailcap --action=view '#{@content_type}:#{path}'
|
135
|
-
|
136
|
-
|
135
|
+
cmd = "/usr/bin/run-mailcap --action=view '#{@content_type}:#{path}'"
|
136
|
+
debug "running: #{cmd.inspect}"
|
137
|
+
BufferManager.shell_out(cmd)
|
137
138
|
$? == 0
|
138
139
|
end
|
139
140
|
|
@@ -208,13 +209,25 @@ EOS
|
|
208
209
|
|
209
210
|
class EnclosedMessage
|
210
211
|
attr_reader :lines
|
211
|
-
def initialize from,
|
212
|
-
@from = from
|
213
|
-
@
|
214
|
-
|
212
|
+
def initialize from, to, cc, date, subj
|
213
|
+
@from = from ? "unknown sender" : from.full_adress
|
214
|
+
@to = to ? "" : to.map { |p| p.full_address }.join(", ")
|
215
|
+
@cc = cc ? "" : cc.map { |p| p.full_address }.join(", ")
|
216
|
+
if date
|
217
|
+
@date = date.rfc822
|
218
|
+
else
|
219
|
+
@date = ""
|
220
|
+
end
|
215
221
|
|
216
|
-
|
217
|
-
|
222
|
+
@subj = subj
|
223
|
+
|
224
|
+
@lines = "\nFrom: #{from}\n"
|
225
|
+
@lines += "To: #{to}\n"
|
226
|
+
if !cc.empty?
|
227
|
+
@lines += "Cc: #{cc}\n"
|
228
|
+
end
|
229
|
+
@lines += "Date: #{date}\n"
|
230
|
+
@lines += "Subject: #{subj}\n\n"
|
218
231
|
end
|
219
232
|
|
220
233
|
def inlineable?; false end
|
@@ -224,7 +237,7 @@ EOS
|
|
224
237
|
def viewable?; false end
|
225
238
|
|
226
239
|
def patina_color; :generic_notice_patina_color end
|
227
|
-
def patina_text; "Begin enclosed message
|
240
|
+
def patina_text; "Begin enclosed message sent on #{@date}" end
|
228
241
|
|
229
242
|
def color; :quote_color end
|
230
243
|
end
|
@@ -240,8 +253,8 @@ EOS
|
|
240
253
|
|
241
254
|
def patina_color
|
242
255
|
case status
|
243
|
-
when :valid
|
244
|
-
when :invalid
|
256
|
+
when :valid then :cryptosig_valid_color
|
257
|
+
when :invalid then :cryptosig_invalid_color
|
245
258
|
else :cryptosig_unknown_color
|
246
259
|
end
|
247
260
|
end
|
data/lib/sup/message.rb
CHANGED
@@ -46,7 +46,7 @@ class Message
|
|
46
46
|
@snippet = opts[:snippet]
|
47
47
|
@snippet_contains_encrypted_content = false
|
48
48
|
@have_snippet = !(opts[:snippet].nil? || opts[:snippet].empty?)
|
49
|
-
@labels = (opts[:labels] || [])
|
49
|
+
@labels = Set.new(opts[:labels] || [])
|
50
50
|
@dirty = false
|
51
51
|
@encrypted = false
|
52
52
|
@chunks = nil
|
@@ -73,7 +73,7 @@ class Message
|
|
73
73
|
else
|
74
74
|
id = "sup-faked-" + Digest::MD5.hexdigest(raw_header)
|
75
75
|
from = header["from"]
|
76
|
-
#
|
76
|
+
#debug "faking non-existent message-id for message from #{from}: #{id}"
|
77
77
|
id
|
78
78
|
end
|
79
79
|
|
@@ -81,7 +81,7 @@ class Message
|
|
81
81
|
header["from"]
|
82
82
|
else
|
83
83
|
name = "Sup Auto-generated Fake Sender <sup@fake.sender.example.com>"
|
84
|
-
#
|
84
|
+
#debug "faking non-existent sender for message #@id: #{name}"
|
85
85
|
name
|
86
86
|
end)
|
87
87
|
|
@@ -92,11 +92,11 @@ class Message
|
|
92
92
|
begin
|
93
93
|
Time.parse date
|
94
94
|
rescue ArgumentError => e
|
95
|
-
#
|
95
|
+
#debug "faking mangled date header for #{@id} (orig #{header['date'].inspect} gave error: #{e.message})"
|
96
96
|
Time.now
|
97
97
|
end
|
98
98
|
else
|
99
|
-
#
|
99
|
+
#debug "faking non-existent date header for #{@id}"
|
100
100
|
Time.now
|
101
101
|
end
|
102
102
|
|
@@ -127,6 +127,31 @@ class Message
|
|
127
127
|
@list_unsubscribe = header["list-unsubscribe"]
|
128
128
|
end
|
129
129
|
|
130
|
+
## Expected index entry format:
|
131
|
+
## :message_id, :subject => String
|
132
|
+
## :date => Time
|
133
|
+
## :refs, :replytos => Array of String
|
134
|
+
## :from => Person
|
135
|
+
## :to, :cc, :bcc => Array of Person
|
136
|
+
def load_from_index! entry
|
137
|
+
@id = entry[:message_id]
|
138
|
+
@from = entry[:from]
|
139
|
+
@date = entry[:date]
|
140
|
+
@subj = entry[:subject]
|
141
|
+
@to = entry[:to]
|
142
|
+
@cc = entry[:cc]
|
143
|
+
@bcc = entry[:bcc]
|
144
|
+
@refs = (@refs + entry[:refs]).uniq
|
145
|
+
@replytos = entry[:replytos]
|
146
|
+
|
147
|
+
@replyto = nil
|
148
|
+
@list_address = nil
|
149
|
+
@recipient_email = nil
|
150
|
+
@source_marked_read = false
|
151
|
+
@list_subscribe = nil
|
152
|
+
@list_unsubscribe = nil
|
153
|
+
end
|
154
|
+
|
130
155
|
def add_ref ref
|
131
156
|
@refs << ref
|
132
157
|
@dirty = true
|
@@ -136,7 +161,7 @@ class Message
|
|
136
161
|
@dirty = true if @refs.delete ref
|
137
162
|
end
|
138
163
|
|
139
|
-
|
164
|
+
attr_reader :snippet
|
140
165
|
def is_list_message?; !@list_address.nil?; end
|
141
166
|
def is_draft?; @source.is_a? DraftLoader; end
|
142
167
|
def draft_filename
|
@@ -157,22 +182,24 @@ class Message
|
|
157
182
|
## don't tempt me.
|
158
183
|
def sanitize_message_id mid; mid.gsub(/(\s|[^\000-\177])+/, "")[0..254] end
|
159
184
|
|
160
|
-
def
|
185
|
+
def save_state index
|
161
186
|
return unless @dirty
|
162
|
-
index.
|
187
|
+
index.update_message_state self
|
163
188
|
@dirty = false
|
164
189
|
true
|
165
190
|
end
|
166
191
|
|
167
192
|
def has_label? t; @labels.member? t; end
|
168
|
-
def add_label
|
169
|
-
|
170
|
-
|
193
|
+
def add_label l
|
194
|
+
l = l.to_sym
|
195
|
+
return if @labels.member? l
|
196
|
+
@labels << l
|
171
197
|
@dirty = true
|
172
198
|
end
|
173
|
-
def remove_label
|
174
|
-
|
175
|
-
@labels.
|
199
|
+
def remove_label l
|
200
|
+
l = l.to_sym
|
201
|
+
return unless @labels.member? l
|
202
|
+
@labels.delete l
|
176
203
|
@dirty = true
|
177
204
|
end
|
178
205
|
|
@@ -181,7 +208,10 @@ class Message
|
|
181
208
|
end
|
182
209
|
|
183
210
|
def labels= l
|
184
|
-
|
211
|
+
raise ArgumentError, "not a set" unless l.is_a?(Set)
|
212
|
+
raise ArgumentError, "not a set of labels" unless l.all? { |ll| ll.is_a?(Symbol) }
|
213
|
+
return if @labels == l
|
214
|
+
@labels = l
|
185
215
|
@dirty = true
|
186
216
|
end
|
187
217
|
|
@@ -208,7 +238,7 @@ class Message
|
|
208
238
|
parse_header @source.load_header(@source_info)
|
209
239
|
message_to_chunks @source.load_message(@source_info)
|
210
240
|
rescue SourceError, SocketError => e
|
211
|
-
|
241
|
+
warn "problem getting messages from #{@source}: #{e.message}"
|
212
242
|
## we need force_to_top here otherwise this window will cover
|
213
243
|
## up the error message one
|
214
244
|
@source.error ||= e
|
@@ -242,7 +272,7 @@ EOS
|
|
242
272
|
begin
|
243
273
|
yield
|
244
274
|
rescue SourceError => e
|
245
|
-
|
275
|
+
warn "problem getting messages from #{@source}: #{e.message}"
|
246
276
|
@source.error ||= e
|
247
277
|
Redwood::report_broken_sources :force_to_top => true
|
248
278
|
error_message e.message
|
@@ -270,11 +300,23 @@ EOS
|
|
270
300
|
to.map { |p| p.indexable_content },
|
271
301
|
cc.map { |p| p.indexable_content },
|
272
302
|
bcc.map { |p| p.indexable_content },
|
273
|
-
|
274
|
-
|
303
|
+
indexable_chunks.map { |c| c.lines },
|
304
|
+
indexable_subject,
|
275
305
|
].flatten.compact.join " "
|
276
306
|
end
|
277
307
|
|
308
|
+
def indexable_body
|
309
|
+
indexable_chunks.map { |c| c.lines }.flatten.compact.join " "
|
310
|
+
end
|
311
|
+
|
312
|
+
def indexable_chunks
|
313
|
+
chunks.select { |c| c.is_a? Chunk::Text }
|
314
|
+
end
|
315
|
+
|
316
|
+
def indexable_subject
|
317
|
+
Message.normalize_subj(subj)
|
318
|
+
end
|
319
|
+
|
278
320
|
def quotable_body_lines
|
279
321
|
chunks.find_all { |c| c.quotable? }.map { |c| c.lines }.flatten
|
280
322
|
end
|
@@ -288,6 +330,12 @@ EOS
|
|
288
330
|
"Subject: #{@subj}"]
|
289
331
|
end
|
290
332
|
|
333
|
+
def self.build_from_source source, source_info
|
334
|
+
m = Message.new :source => source, :source_info => source_info
|
335
|
+
m.load_from_source!
|
336
|
+
m
|
337
|
+
end
|
338
|
+
|
291
339
|
private
|
292
340
|
|
293
341
|
## here's where we handle decoding mime attachments. unfortunately
|
@@ -315,25 +363,25 @@ private
|
|
315
363
|
|
316
364
|
def multipart_signed_to_chunks m
|
317
365
|
if m.body.size != 2
|
318
|
-
|
366
|
+
warn "multipart/signed with #{m.body.size} parts (expecting 2)"
|
319
367
|
return
|
320
368
|
end
|
321
369
|
|
322
370
|
payload, signature = m.body
|
323
371
|
if signature.multipart?
|
324
|
-
|
372
|
+
warn "multipart/signed with payload multipart #{payload.multipart?} and signature multipart #{signature.multipart?}"
|
325
373
|
return
|
326
374
|
end
|
327
375
|
|
328
376
|
## this probably will never happen
|
329
|
-
if payload.header.content_type == "application/pgp-signature"
|
330
|
-
|
377
|
+
if payload.header.content_type && payload.header.content_type.downcase == "application/pgp-signature"
|
378
|
+
warn "multipart/signed with payload content type #{payload.header.content_type}"
|
331
379
|
return
|
332
380
|
end
|
333
381
|
|
334
|
-
if signature.header.content_type != "application/pgp-signature"
|
382
|
+
if signature.header.content_type && signature.header.content_type.downcase != "application/pgp-signature"
|
335
383
|
## unknown signature type; just ignore.
|
336
|
-
#
|
384
|
+
#warn "multipart/signed with signature content type #{signature.header.content_type}"
|
337
385
|
return
|
338
386
|
end
|
339
387
|
|
@@ -342,36 +390,40 @@ private
|
|
342
390
|
|
343
391
|
def multipart_encrypted_to_chunks m
|
344
392
|
if m.body.size != 2
|
345
|
-
|
393
|
+
warn "multipart/encrypted with #{m.body.size} parts (expecting 2)"
|
346
394
|
return
|
347
395
|
end
|
348
396
|
|
349
397
|
control, payload = m.body
|
350
398
|
if control.multipart?
|
351
|
-
|
399
|
+
warn "multipart/encrypted with control multipart #{control.multipart?} and payload multipart #{payload.multipart?}"
|
352
400
|
return
|
353
401
|
end
|
354
402
|
|
355
|
-
if payload.header.content_type != "application/octet-stream"
|
356
|
-
|
403
|
+
if payload.header.content_type && payload.header.content_type.downcase != "application/octet-stream"
|
404
|
+
warn "multipart/encrypted with payload content type #{payload.header.content_type}"
|
357
405
|
return
|
358
406
|
end
|
359
407
|
|
360
|
-
if control.header.content_type != "application/pgp-encrypted"
|
361
|
-
|
408
|
+
if control.header.content_type && control.header.content_type.downcase != "application/pgp-encrypted"
|
409
|
+
warn "multipart/encrypted with control content type #{signature.header.content_type}"
|
362
410
|
return
|
363
411
|
end
|
364
412
|
|
365
|
-
|
366
|
-
|
367
|
-
|
413
|
+
notice, sig, decryptedm = CryptoManager.decrypt payload
|
414
|
+
if decryptedm # managed to decrypt
|
415
|
+
children = message_to_chunks(decryptedm, true)
|
416
|
+
[notice, sig].compact + children
|
417
|
+
else
|
418
|
+
[notice]
|
419
|
+
end
|
368
420
|
end
|
369
421
|
|
370
422
|
## takes a RMail::Message, breaks it into Chunk:: classes.
|
371
423
|
def message_to_chunks m, encrypted=false, sibling_types=[]
|
372
424
|
if m.multipart?
|
373
425
|
chunks =
|
374
|
-
case m.header.content_type
|
426
|
+
case m.header.content_type.downcase
|
375
427
|
when "multipart/signed"
|
376
428
|
multipart_signed_to_chunks m
|
377
429
|
when "multipart/encrypted"
|
@@ -384,29 +436,57 @@ private
|
|
384
436
|
end
|
385
437
|
|
386
438
|
chunks
|
387
|
-
elsif m.header.content_type == "message/rfc822"
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
439
|
+
elsif m.header.content_type && m.header.content_type.downcase == "message/rfc822"
|
440
|
+
if m.body
|
441
|
+
payload = RMail::Parser.read(m.body)
|
442
|
+
from = payload.header.from.first ? payload.header.from.first.format : ""
|
443
|
+
to = payload.header.to.map { |p| p.format }.join(", ")
|
444
|
+
cc = payload.header.cc.map { |p| p.format }.join(", ")
|
445
|
+
subj = payload.header.subject
|
446
|
+
subj = subj ? Message.normalize_subj(payload.header.subject.gsub(/\s+/, " ").gsub(/\s+$/, "")) : subj
|
447
|
+
if Rfc2047.is_encoded? subj
|
448
|
+
subj = Rfc2047.decode_to $encoding, subj
|
449
|
+
end
|
450
|
+
msgdate = payload.header.date
|
451
|
+
from_person = from ? Person.from_address(from) : nil
|
452
|
+
to_people = to ? Person.from_address_list(to) : nil
|
453
|
+
cc_people = cc ? Person.from_address_list(cc) : nil
|
454
|
+
[Chunk::EnclosedMessage.new(from_person, to_people, cc_people, msgdate, subj)] + message_to_chunks(payload, encrypted)
|
455
|
+
else
|
456
|
+
debug "no body for message/rfc822 enclosure; skipping"
|
457
|
+
[]
|
458
|
+
end
|
459
|
+
elsif m.header.content_type && m.header.content_type.downcase == "application/pgp" && m.body
|
460
|
+
## apparently some versions of Thunderbird generate encryped email that
|
461
|
+
## does not follow RFC3156, e.g. messages with X-Enigmail-Version: 0.95.0
|
462
|
+
## they have no MIME multipart and just set the body content type to
|
463
|
+
## application/pgp. this handles that.
|
464
|
+
##
|
465
|
+
## TODO: unduplicate code between here and multipart_encrypted_to_chunks
|
466
|
+
notice, sig, decryptedm = CryptoManager.decrypt m.body
|
467
|
+
if decryptedm # managed to decrypt
|
468
|
+
children = message_to_chunks decryptedm, true
|
469
|
+
[notice, sig].compact + children
|
470
|
+
else
|
471
|
+
[notice]
|
472
|
+
end
|
393
473
|
else
|
394
474
|
filename =
|
395
475
|
## first, paw through the headers looking for a filename
|
396
476
|
if m.header["Content-Disposition"] && m.header["Content-Disposition"] =~ /filename="?(.*?[^\\])("|;|$)/
|
397
477
|
$1
|
398
|
-
elsif m.header["Content-Type"] && m.header["Content-Type"] =~ /name="?(.*?[^\\])("|;|$)/
|
478
|
+
elsif m.header["Content-Type"] && m.header["Content-Type"] =~ /name="?(.*?[^\\])("|;|$)/i
|
399
479
|
$1
|
400
480
|
|
401
481
|
## haven't found one, but it's a non-text message. fake
|
402
482
|
## it.
|
403
483
|
##
|
404
484
|
## TODO: make this less lame.
|
405
|
-
elsif m.header["Content-Type"] && m.header["Content-Type"] !~ /^text\/plain/
|
485
|
+
elsif m.header["Content-Type"] && m.header["Content-Type"] !~ /^text\/plain/i
|
406
486
|
extension =
|
407
487
|
case m.header["Content-Type"]
|
408
|
-
when /text\/html
|
409
|
-
when /image\/(.*)
|
488
|
+
when /text\/html/ then "html"
|
489
|
+
when /image\/(.*)/ then $1
|
410
490
|
end
|
411
491
|
|
412
492
|
["sup-attachment-#{Time.now.to_i}-#{rand 10000}", extension].join(".")
|
@@ -419,7 +499,7 @@ private
|
|
419
499
|
# Lowercase the filename because searches are easier that way
|
420
500
|
@attachments.push filename.downcase unless filename =~ /^sup-attachment-/
|
421
501
|
add_label :attachment unless filename =~ /^sup-attachment-/
|
422
|
-
content_type = m.header.content_type || "application/unknown" # sometimes RubyMail gives us nil
|
502
|
+
content_type = m.header.content_type.downcase || "application/unknown" # sometimes RubyMail gives us nil
|
423
503
|
[Chunk::Attachment.new(content_type, filename, m, sibling_types)]
|
424
504
|
|
425
505
|
## otherwise, it's body text
|