vmail 1.6.8 → 1.6.9
Sign up to get free protection for your applications and to get access to all the features.
- data/NOTES +27 -0
- data/db/create.sql +25 -0
- data/lib/vmail.rb +12 -12
- data/lib/vmail.vim +19 -23
- data/lib/vmail/database.rb +30 -0
- data/lib/vmail/flagging_and_moving.rb +91 -0
- data/lib/vmail/imap_client.rb +58 -416
- data/lib/vmail/message_formatter.rb +19 -46
- data/lib/vmail/query.rb +7 -7
- data/lib/vmail/reply_template.rb +1 -1
- data/lib/vmail/searching.rb +61 -0
- data/lib/vmail/showing_headers.rb +103 -0
- data/lib/vmail/showing_message.rb +91 -0
- data/lib/vmail/version.rb +1 -1
- data/vmail.gemspec +2 -0
- metadata +31 -2
data/NOTES
ADDED
@@ -0,0 +1,27 @@
|
|
1
|
+
|
2
|
+
------------------------------------------------------------------------
|
3
|
+
Sun Jun 26 18:07:59 EDT 2011
|
4
|
+
|
5
|
+
Sqlite3 branch
|
6
|
+
|
7
|
+
Use sqlite3 as a cache.
|
8
|
+
fetch UIDS, and then check and cache.
|
9
|
+
|
10
|
+
Also, log output to primary thread and let Vmail echo via Vim. Change vmail.vim
|
11
|
+
How do make this output progressive?
|
12
|
+
|
13
|
+
What if there is a queue for IMAP tasks instead of multiple untracked threads?
|
14
|
+
|
15
|
+
Check for new messages can just reload the current mailbox
|
16
|
+
=> simplify the code
|
17
|
+
|
18
|
+
Build queue of stuff for Vmail to do
|
19
|
+
=> this may lead to fewer errors than multithreaded delete (maybe too many
|
20
|
+
threads are spawned)
|
21
|
+
|
22
|
+
TODO
|
23
|
+
|
24
|
+
No number number limit inbox 100. NOTE IN DOCUMENTATION
|
25
|
+
|
26
|
+
|
27
|
+
|
data/db/create.sql
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
drop table if exists version;
|
2
|
+
create table version (
|
3
|
+
vmail_version text
|
4
|
+
);
|
5
|
+
create table if not exists messages (
|
6
|
+
message_id text PRIMARY KEY,
|
7
|
+
size integer,
|
8
|
+
flags text,
|
9
|
+
subject text,
|
10
|
+
sender text,
|
11
|
+
recipients text,
|
12
|
+
date text,
|
13
|
+
plaintext text,
|
14
|
+
rfc822 text
|
15
|
+
);
|
16
|
+
create table if not exists labelings (
|
17
|
+
label_id integer,
|
18
|
+
message_id text,
|
19
|
+
uid integer
|
20
|
+
);
|
21
|
+
create table if not exists labels (
|
22
|
+
label_id integer PRIMARY KEY,
|
23
|
+
name text UNIQUE
|
24
|
+
);
|
25
|
+
|
data/lib/vmail.rb
CHANGED
@@ -58,20 +58,18 @@ module Vmail
|
|
58
58
|
server.select_mailbox mailbox
|
59
59
|
|
60
60
|
STDERR.puts "Mailbox: #{mailbox}"
|
61
|
-
STDERR.puts "Query: #{query.inspect}
|
61
|
+
STDERR.puts "Query: #{query.inspect}"
|
62
|
+
STDERR.puts "Query String: #{String.shellescape(query_string)}"
|
62
63
|
|
63
64
|
buffer_file = "vmailbuffer"
|
64
65
|
# invoke vim
|
65
66
|
vimscript = File.expand_path("../vmail.vim", __FILE__)
|
66
|
-
vim_command = "DRB_URI=#{drb_uri} VMAIL_CONTACTS_FILE=#{contacts_file} VMAIL_MAILBOX=#{String.shellescape(mailbox)} VMAIL_QUERY
|
67
|
+
vim_command = "DRB_URI=#{drb_uri} VMAIL_CONTACTS_FILE=#{contacts_file} VMAIL_MAILBOX=#{String.shellescape(mailbox)} VMAIL_QUERY=\"#{query_string}\" #{vim} -S #{vimscript} #{buffer_file}"
|
67
68
|
STDERR.puts vim_command
|
68
69
|
STDERR.puts "Using buffer file: #{buffer_file}"
|
69
70
|
File.open(buffer_file, "w") do |file|
|
70
|
-
file.puts "Vmail
|
71
|
-
file.puts "
|
72
|
-
file.puts "- mailbox: #{mailbox}"
|
73
|
-
file.puts "- query: #{query_string}\n"
|
74
|
-
file.puts "Fetching messages. please wait..."
|
71
|
+
file.puts "\n\nVmail #{Vmail::VERSION}\n\n"
|
72
|
+
file.puts "Please wait while I fetch your messages.\n\n\n"
|
75
73
|
end
|
76
74
|
|
77
75
|
system(vim_command)
|
@@ -157,13 +155,15 @@ module Vmail
|
|
157
155
|
end
|
158
156
|
|
159
157
|
def parse_query
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
end
|
158
|
+
if ARGV[0] =~ /^\d+/
|
159
|
+
ARGV.shift
|
160
|
+
end
|
161
|
+
mailbox = ARGV.shift || 'INBOX'
|
165
162
|
query = Vmail::Query.parse(ARGV)
|
166
163
|
[mailbox, query]
|
167
164
|
end
|
168
165
|
end
|
169
166
|
|
167
|
+
if __FILE__ == $0
|
168
|
+
Vmail.start
|
169
|
+
end
|
data/lib/vmail.vim
CHANGED
@@ -75,7 +75,7 @@ function! s:show_message(stay_in_message_list)
|
|
75
75
|
call s:more_messages()
|
76
76
|
return
|
77
77
|
endif
|
78
|
-
let s:uid = matchstr(line, '\
|
78
|
+
let s:uid = shellescape(matchstr(line, '\S\+$'))
|
79
79
|
if s:uid == ""
|
80
80
|
return
|
81
81
|
end
|
@@ -219,7 +219,6 @@ func! s:close_message_window()
|
|
219
219
|
endif
|
220
220
|
endfunc
|
221
221
|
|
222
|
-
|
223
222
|
" gets new messages since last update
|
224
223
|
function! s:update()
|
225
224
|
let command = s:update_command
|
@@ -227,13 +226,11 @@ function! s:update()
|
|
227
226
|
let res = system(command)
|
228
227
|
if len(split(res, "\n", '')) > 0
|
229
228
|
setlocal modifiable
|
230
|
-
|
231
|
-
silent $put =res
|
229
|
+
call append(0, res)
|
232
230
|
setlocal nomodifiable
|
233
231
|
write!
|
234
232
|
let num = len(split(res, '\n', ''))
|
235
|
-
call cursor(
|
236
|
-
normal z.
|
233
|
+
call cursor(0, 0)
|
237
234
|
redraw
|
238
235
|
echom "You have " . num . " new message" . (num == 1 ? '' : 's') . "!"
|
239
236
|
else
|
@@ -251,7 +248,7 @@ function! s:toggle_star() range
|
|
251
248
|
if (match(getline(a:firstline), flag_symbol) != -1)
|
252
249
|
let action = " -FLAGS"
|
253
250
|
endif
|
254
|
-
let command = s:flag_command . join(uid_set, ',') . action . " Flagged"
|
251
|
+
let command = s:flag_command . shellescape(join(uid_set, ',')) . action . " Flagged"
|
255
252
|
if nummsgs == 1
|
256
253
|
echom "Toggling flag on message"
|
257
254
|
else
|
@@ -287,7 +284,7 @@ endfunction
|
|
287
284
|
func! s:delete_messages(flag) range
|
288
285
|
let uid_set = s:collect_uids(a:firstline, a:lastline)
|
289
286
|
let nummsgs = len(uid_set)
|
290
|
-
let command = s:flag_command . join(uid_set, ',') . " +FLAGS " . a:flag
|
287
|
+
let command = s:flag_command . shellescape(join(uid_set, ',')) . " +FLAGS " . a:flag
|
291
288
|
if nummsgs == 1
|
292
289
|
echom "Deleting message"
|
293
290
|
else
|
@@ -305,7 +302,7 @@ endfunc
|
|
305
302
|
func! s:archive_messages() range
|
306
303
|
let uid_set = s:collect_uids(a:firstline, a:lastline)
|
307
304
|
let nummsgs = len(uid_set)
|
308
|
-
let command = s:move_to_command . join(uid_set, ',') . ' ' . "all"
|
305
|
+
let command = s:move_to_command . shellescape(join(uid_set, ',')) . ' ' . "all"
|
309
306
|
echo "Archiving message" . (nummsgs == 1 ? '' : 's')
|
310
307
|
let res = system(command)
|
311
308
|
setlocal modifiable
|
@@ -328,7 +325,7 @@ func! s:append_messages_to_file() range
|
|
328
325
|
return
|
329
326
|
endif
|
330
327
|
let s:append_file = append_file
|
331
|
-
let command = s:append_to_file_command . join(uid_set, ',') . ' ' . s:append_file
|
328
|
+
let command = s:append_to_file_command . shellescape(join(uid_set, ',')) . ' ' . s:append_file
|
332
329
|
echo "Appending " . nummsgs . " message" . (nummsgs == 1 ? '' : 's') . " to " . s:append_file . ". Please wait..."
|
333
330
|
let res = system(command)
|
334
331
|
echo res
|
@@ -341,7 +338,7 @@ function! s:move_to_mailbox(copy) range
|
|
341
338
|
let s:copy_to_mailbox = a:copy
|
342
339
|
let uid_set = s:collect_uids(a:firstline, a:lastline)
|
343
340
|
let s:nummsgs = len(uid_set)
|
344
|
-
let s:uid_set = join(uid_set, ',')
|
341
|
+
let s:uid_set = shellescape(join(uid_set, ','))
|
345
342
|
" now prompt use to select mailbox
|
346
343
|
if !exists("s:mailboxes")
|
347
344
|
call s:get_mailbox_list()
|
@@ -474,7 +471,7 @@ function! s:select_mailbox()
|
|
474
471
|
return
|
475
472
|
endif
|
476
473
|
let s:mailbox = mailbox
|
477
|
-
let s:query = "
|
474
|
+
let s:query = "all"
|
478
475
|
let command = s:select_mailbox_command . shellescape(s:mailbox)
|
479
476
|
redraw
|
480
477
|
echom "Selecting mailbox: ". s:mailbox . ". Please wait..."
|
@@ -483,16 +480,15 @@ function! s:select_mailbox()
|
|
483
480
|
" now get latest 100 messages
|
484
481
|
call s:focus_list_window()
|
485
482
|
setlocal modifiable
|
486
|
-
let command = s:search_command . shellescape("
|
483
|
+
let command = s:search_command . shellescape("all")
|
487
484
|
echo "Loading messages..."
|
488
485
|
let res = system(command)
|
489
486
|
silent 1,$delete
|
490
487
|
silent! put! =res
|
491
488
|
execute "normal Gdd\<c-y>"
|
492
|
-
normal G
|
493
489
|
setlocal nomodifiable
|
494
490
|
write
|
495
|
-
normal
|
491
|
+
normal gg
|
496
492
|
redraw
|
497
493
|
echom "Current mailbox: ". s:mailbox
|
498
494
|
endfunction
|
@@ -524,20 +520,19 @@ function! s:do_search()
|
|
524
520
|
execute "silent normal Gdd\<c-y>"
|
525
521
|
setlocal nomodifiable
|
526
522
|
write
|
527
|
-
normal
|
523
|
+
normal gg
|
528
524
|
endfunction
|
529
525
|
|
530
526
|
function! s:more_messages()
|
531
|
-
let
|
532
|
-
let seqno = get(split(matchstr(line, '\d\+:\d\+$'), ':'), 0)
|
533
|
-
let command = s:more_messages_command . seqno
|
527
|
+
let command = s:more_messages_command
|
534
528
|
echo "Fetching more messages. Please wait..."
|
535
529
|
let res = system(command)
|
536
530
|
setlocal modifiable
|
537
531
|
let lines = split(res, "\n")
|
538
|
-
call append(
|
532
|
+
call append(line('$'), lines)
|
539
533
|
" execute "normal Gdd\<c-y>"
|
540
534
|
setlocal nomodifiable
|
535
|
+
normal j
|
541
536
|
endfunction
|
542
537
|
|
543
538
|
" --------------------------------------------------------------------------------
|
@@ -576,8 +571,9 @@ func! s:open_compose_window(command)
|
|
576
571
|
setlocal modifiable
|
577
572
|
" TODO maybe later save backups?
|
578
573
|
setlocal buftype=nowrite
|
574
|
+
"
|
579
575
|
if winnr('$') > 1
|
580
|
-
|
576
|
+
call s:focus_list_window()
|
581
577
|
close!
|
582
578
|
endif
|
583
579
|
silent 1,$delete
|
@@ -736,7 +732,7 @@ function! s:collect_uids(startline, endline)
|
|
736
732
|
let uid_set = []
|
737
733
|
let lnum = a:startline
|
738
734
|
while lnum <= a:endline
|
739
|
-
let uid = matchstr(getline(lnum), '\
|
735
|
+
let uid = matchstr(getline(lnum), '\S\+$')
|
740
736
|
call add(uid_set, uid)
|
741
737
|
let lnum += 1
|
742
738
|
endwhile
|
@@ -857,7 +853,7 @@ call system(s:set_window_width_command . winwidth(1))
|
|
857
853
|
autocmd VimResized <buffer> call system(s:set_window_width_command . winwidth(1))
|
858
854
|
|
859
855
|
autocmd bufreadpost *.txt call <SID>turn_into_compose_window()
|
860
|
-
|
856
|
+
normal G
|
861
857
|
call system(s:select_mailbox_command . shellescape(s:mailbox))
|
862
858
|
call s:do_search()
|
863
859
|
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'sequel'
|
2
|
+
|
3
|
+
DB = Sequel.connect 'sqlite://vmail.db'
|
4
|
+
|
5
|
+
|
6
|
+
if !File.exists?("vmail.db")
|
7
|
+
create_table_script = File.expand_path("../db/create.sql", __FILE__)
|
8
|
+
DB.run create_table_script
|
9
|
+
end
|
10
|
+
|
11
|
+
if DB[:version].count == 0
|
12
|
+
DB[:version].insert(:vmail_version => Vmail::VERSION)
|
13
|
+
end
|
14
|
+
|
15
|
+
class Vmail::Message < Sequel::Model
|
16
|
+
set_primary_key :message_id
|
17
|
+
one_to_many :labelings
|
18
|
+
many_to_many :labels, :join_table => 'labelings'
|
19
|
+
end
|
20
|
+
|
21
|
+
class Vmail::Label < Sequel::Model
|
22
|
+
set_primary_key :label_id
|
23
|
+
one_to_many :labelings
|
24
|
+
many_to_many :messages, :join_table => 'labelings'
|
25
|
+
end
|
26
|
+
|
27
|
+
class Vmail::Labeling < Sequel::Model
|
28
|
+
end
|
29
|
+
|
30
|
+
|
@@ -0,0 +1,91 @@
|
|
1
|
+
module Vmail
|
2
|
+
module FlaggingAndMoving
|
3
|
+
|
4
|
+
def convert_to_message_ids(message_ids)
|
5
|
+
message_ids.split(',').map {|message_id|
|
6
|
+
labeling = Labeling[message_id: message_id, label_id: @label.label_id]
|
7
|
+
labeling.uid
|
8
|
+
}
|
9
|
+
end
|
10
|
+
|
11
|
+
# uid_set is a string comming from the vim client
|
12
|
+
# action is -FLAGS or +FLAGS
|
13
|
+
def flag(message_ids, action, flg)
|
14
|
+
uid_set = convert_to_message_ids(message_ids)
|
15
|
+
log "Flag #{uid_set} #{flg} #{action}"
|
16
|
+
if flg == 'Deleted'
|
17
|
+
log "Deleting uid_set: #{uid_set.inspect}"
|
18
|
+
decrement_max_seqno(uid_set.size)
|
19
|
+
# for delete, do in a separate thread because deletions are slow
|
20
|
+
spawn_thread_if_tty do
|
21
|
+
unless @mailbox == mailbox_aliases['trash']
|
22
|
+
log "imap.uid_copy #{uid_set.inspect} to #{mailbox_aliases['trash']}"
|
23
|
+
log @imap.uid_copy(uid_set, mailbox_aliases['trash'])
|
24
|
+
end
|
25
|
+
log "imap.uid_store #{uid_set.inspect} #{action} [#{flg.to_sym}]"
|
26
|
+
log @imap.uid_store(uid_set, action, [flg.to_sym])
|
27
|
+
reload_mailbox
|
28
|
+
clear_cached_message
|
29
|
+
end
|
30
|
+
elsif flg == 'spam' || flg == mailbox_aliases['spam']
|
31
|
+
log "Marking as spam uid_set: #{uid_set.inspect}"
|
32
|
+
decrement_max_seqno(uid_set.size)
|
33
|
+
spawn_thread_if_tty do
|
34
|
+
log "imap.uid_copy #{uid_set.inspect} to #{mailbox_aliases['spam']}"
|
35
|
+
log @imap.uid_copy(uid_set, mailbox_aliases['spam'])
|
36
|
+
log "imap.uid_store #{uid_set.inspect} #{action} [:Deleted]"
|
37
|
+
log @imap.uid_store(uid_set, action, [:Deleted])
|
38
|
+
reload_mailbox
|
39
|
+
clear_cached_message
|
40
|
+
end
|
41
|
+
else
|
42
|
+
log "Flagging uid_set: #{uid_set.inspect}"
|
43
|
+
spawn_thread_if_tty do
|
44
|
+
log "imap.uid_store #{uid_set.inspect} #{action} [#{flg.to_sym}]"
|
45
|
+
log @imap.uid_store(uid_set, action, [flg.to_sym])
|
46
|
+
end
|
47
|
+
end
|
48
|
+
rescue
|
49
|
+
log $!
|
50
|
+
end
|
51
|
+
|
52
|
+
def move_to(message_ids, mailbox)
|
53
|
+
uid_set = convert_to_message_ids(message_ids)
|
54
|
+
decrement_max_seqno(uid_set.size)
|
55
|
+
log "Move #{uid_set.inspect} to #{mailbox}"
|
56
|
+
if mailbox == 'all'
|
57
|
+
log "Archiving messages"
|
58
|
+
end
|
59
|
+
if mailbox_aliases[mailbox]
|
60
|
+
mailbox = mailbox_aliases[mailbox]
|
61
|
+
end
|
62
|
+
create_if_necessary mailbox
|
63
|
+
log "Moving uid_set: #{uid_set.inspect} to #{mailbox}"
|
64
|
+
spawn_thread_if_tty do
|
65
|
+
log @imap.uid_copy(uid_set, mailbox)
|
66
|
+
log @imap.uid_store(uid_set, '+FLAGS', [:Deleted])
|
67
|
+
reload_mailbox
|
68
|
+
clear_cached_message
|
69
|
+
log "Moved uid_set #{uid_set.inspect} to #{mailbox}"
|
70
|
+
end
|
71
|
+
rescue
|
72
|
+
log $!
|
73
|
+
end
|
74
|
+
|
75
|
+
def copy_to(message_ids, mailbox)
|
76
|
+
uid_set = convert_to_message_ids(message_ids)
|
77
|
+
if mailbox_aliases[mailbox]
|
78
|
+
mailbox = mailbox_aliases[mailbox]
|
79
|
+
end
|
80
|
+
create_if_necessary mailbox
|
81
|
+
log "Copying #{uid_set.inspect} to #{mailbox}"
|
82
|
+
spawn_thread_if_tty do
|
83
|
+
log @imap.uid_copy(uid_set, mailbox)
|
84
|
+
log "Copied uid_set #{uid_set.inspect} to #{mailbox}"
|
85
|
+
end
|
86
|
+
rescue
|
87
|
+
log $!
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
data/lib/vmail/imap_client.rb
CHANGED
@@ -6,13 +6,22 @@ require 'mail'
|
|
6
6
|
require 'net/imap'
|
7
7
|
require 'time'
|
8
8
|
require 'logger'
|
9
|
+
require 'vmail/helpers'
|
9
10
|
require 'vmail/address_quoter'
|
11
|
+
require 'vmail/database'
|
12
|
+
require 'vmail/searching'
|
13
|
+
require 'vmail/showing_headers'
|
14
|
+
require 'vmail/showing_message'
|
15
|
+
require 'vmail/flagging_and_moving'
|
10
16
|
|
11
17
|
module Vmail
|
12
18
|
class ImapClient
|
19
|
+
include Vmail::Helpers
|
13
20
|
include Vmail::AddressQuoter
|
14
|
-
|
15
|
-
|
21
|
+
include Vmail::Searching
|
22
|
+
include Vmail::ShowingHeaders
|
23
|
+
include Vmail::ShowingMessage
|
24
|
+
include Vmail::FlaggingAndMoving
|
16
25
|
|
17
26
|
attr_accessor :max_seqno # of current mailbox
|
18
27
|
|
@@ -26,22 +35,10 @@ module Vmail
|
|
26
35
|
@logger.level = Logger::DEBUG
|
27
36
|
@imap_server = config['server'] || 'imap.gmail.com'
|
28
37
|
@imap_port = config['port'] || 993
|
29
|
-
|
30
|
-
@current_message_uid = nil
|
31
|
-
@width = 140
|
32
|
-
end
|
33
|
-
|
34
|
-
# holds mail objects keyed by [mailbox, uid]
|
35
|
-
def message_cache
|
36
|
-
@message_cache ||= {}
|
37
|
-
size = @message_cache.values.reduce(0) {|sum, x| sum + x[:size]}
|
38
|
-
if size > 2_000_000 # TODO make this configurable
|
39
|
-
log "Pruning message cache; message cache is consuming #{number_to_human_size size}"
|
40
|
-
@message_cache.keys[0, @message_cache.size / 2].each {|k| @message_cache.delete(k)}
|
41
|
-
end
|
42
|
-
@message_cache
|
38
|
+
current_message = nil
|
43
39
|
end
|
44
40
|
|
41
|
+
|
45
42
|
def open
|
46
43
|
@imap = Net::IMAP.new(@imap_server, @imap_port, true, nil, false)
|
47
44
|
log @imap.login(@username, @password)
|
@@ -69,19 +66,23 @@ module Vmail
|
|
69
66
|
if mailbox_aliases[mailbox]
|
70
67
|
mailbox = mailbox_aliases[mailbox]
|
71
68
|
end
|
72
|
-
if mailbox == @mailbox && !force
|
73
|
-
return
|
74
|
-
end
|
75
69
|
log "Selecting mailbox #{mailbox.inspect}"
|
76
70
|
reconnect_if_necessary(15) do
|
77
71
|
log @imap.select(mailbox)
|
78
72
|
end
|
79
73
|
log "Done"
|
74
|
+
|
80
75
|
@mailbox = mailbox
|
76
|
+
@label = Label[name: @mailbox] || Label.create(name: @mailbox)
|
77
|
+
|
81
78
|
log "Getting mailbox status"
|
82
79
|
get_mailbox_status
|
83
80
|
log "Getting highest message id"
|
84
81
|
get_highest_message_id
|
82
|
+
if @next_window_width
|
83
|
+
@width = @next_window_width
|
84
|
+
end
|
85
|
+
|
85
86
|
return "OK"
|
86
87
|
end
|
87
88
|
|
@@ -90,12 +91,12 @@ module Vmail
|
|
90
91
|
select_mailbox(@mailbox, true)
|
91
92
|
end
|
92
93
|
|
94
|
+
# TODO no need for this if all shown messages are stored in SQLITE3
|
95
|
+
# and keyed by UID.
|
93
96
|
def clear_cached_message
|
94
97
|
return unless STDIN.tty?
|
95
|
-
log "
|
96
|
-
|
97
|
-
@current_message_uid = nil
|
98
|
-
@current_message = nil
|
98
|
+
log "Clearing cached message"
|
99
|
+
current_message = nil
|
99
100
|
end
|
100
101
|
|
101
102
|
def get_highest_message_id
|
@@ -103,7 +104,7 @@ module Vmail
|
|
103
104
|
res = @imap.fetch([1,"*"], ["ENVELOPE"])
|
104
105
|
if res
|
105
106
|
@num_messages = res[-1].seqno
|
106
|
-
log "
|
107
|
+
log "Highest seqno: #@num_messages"
|
107
108
|
else
|
108
109
|
@num_messages = 1
|
109
110
|
log "NO HIGHEST ID: setting @num_messages to 1"
|
@@ -180,178 +181,18 @@ module Vmail
|
|
180
181
|
@mailboxes
|
181
182
|
end
|
182
183
|
|
183
|
-
# id_set may be a range, array, or string
|
184
|
-
def fetch_row_text(id_set, are_uids=false, is_update=false)
|
185
|
-
log "Fetch_row_text: #{id_set.inspect}"
|
186
|
-
if id_set.is_a?(String)
|
187
|
-
id_set = id_set.split(',')
|
188
|
-
end
|
189
|
-
if id_set.to_a.empty?
|
190
|
-
log "- empty set"
|
191
|
-
return ""
|
192
|
-
end
|
193
|
-
new_message_rows = fetch_envelopes(id_set, are_uids, is_update)
|
194
|
-
new_message_rows.map {|x| x[:row_text]}.join("\n")
|
195
|
-
rescue # Encoding::CompatibilityError (only in 1.9.2)
|
196
|
-
log "Error in fetch_row_text:\n#{$!}\n#{$!.backtrace}"
|
197
|
-
new_message_rows.map {|x| Iconv.conv('US-ASCII//TRANSLIT//IGNORE', 'UTF-8', x[:row_text])}.join("\n")
|
198
|
-
end
|
199
|
-
|
200
|
-
def fetch_envelopes(id_set, are_uids, is_update)
|
201
|
-
results = reconnect_if_necessary do
|
202
|
-
if are_uids
|
203
|
-
@imap.uid_fetch(id_set, ["FLAGS", "ENVELOPE", "RFC822.SIZE", "UID" ])
|
204
|
-
else
|
205
|
-
@imap.fetch(id_set, ["FLAGS", "ENVELOPE", "RFC822.SIZE", "UID" ])
|
206
|
-
end
|
207
|
-
end
|
208
|
-
if results.nil?
|
209
|
-
error = "Expected fetch results but got nil"
|
210
|
-
log(error) && raise(error)
|
211
|
-
end
|
212
|
-
log "- extracting headers"
|
213
|
-
new_message_rows = results.map {|x| extract_row_data(x) }
|
214
|
-
log "- returning #{new_message_rows.size} new rows and caching result"
|
215
|
-
new_message_rows
|
216
|
-
end
|
217
|
-
|
218
|
-
# TODO extract this to another class or module and write unit tests
|
219
|
-
def extract_row_data(fetch_data)
|
220
|
-
seqno = fetch_data.seqno
|
221
|
-
uid = fetch_data.attr['UID']
|
222
|
-
# log "fetched seqno #{seqno} uid #{uid}"
|
223
|
-
envelope = fetch_data.attr["ENVELOPE"]
|
224
|
-
size = fetch_data.attr["RFC822.SIZE"]
|
225
|
-
flags = fetch_data.attr["FLAGS"]
|
226
|
-
address_struct = if @mailbox == mailbox_aliases['sent']
|
227
|
-
structs = envelope.to || envelope.cc
|
228
|
-
structs.nil? ? nil : structs.first
|
229
|
-
else
|
230
|
-
envelope.from.first
|
231
|
-
end
|
232
|
-
address = if address_struct.nil?
|
233
|
-
"Unknown"
|
234
|
-
elsif address_struct.name
|
235
|
-
"#{Mail::Encodings.unquote_and_convert_to(address_struct.name, 'UTF-8')} <#{[address_struct.mailbox, address_struct.host].join('@')}>"
|
236
|
-
else
|
237
|
-
[Mail::Encodings.unquote_and_convert_to(address_struct.mailbox, 'UTF-8'), Mail::Encodings.unquote_and_convert_to(address_struct.host, 'UTF-8')].join('@')
|
238
|
-
end
|
239
|
-
if @mailbox == mailbox_aliases['sent'] && envelope.to && envelope.cc
|
240
|
-
total_recips = (envelope.to + envelope.cc).size
|
241
|
-
address += " + #{total_recips - 1}"
|
242
|
-
end
|
243
|
-
date = begin
|
244
|
-
Time.parse(envelope.date).localtime
|
245
|
-
rescue ArgumentError
|
246
|
-
Time.now
|
247
|
-
end
|
248
|
-
|
249
|
-
date_formatted = if date.year != Time.now.year
|
250
|
-
date.strftime "%b %d %Y" rescue envelope.date.to_s
|
251
|
-
else
|
252
|
-
date.strftime "%b %d %I:%M%P" rescue envelope.date.to_s
|
253
|
-
end
|
254
|
-
subject = envelope.subject || ''
|
255
|
-
subject = Mail::Encodings.unquote_and_convert_to(subject, 'UTF-8')
|
256
|
-
flags = format_flags(flags)
|
257
|
-
mid_width = @width - 38
|
258
|
-
address_col_width = (mid_width * 0.3).ceil
|
259
|
-
subject_col_width = (mid_width * 0.7).floor
|
260
|
-
identifier = [seqno.to_i, uid.to_i].join(':')
|
261
|
-
row_text = [ flags.col(2),
|
262
|
-
(date_formatted || '').col(14),
|
263
|
-
address.col(address_col_width),
|
264
|
-
subject.col(subject_col_width),
|
265
|
-
number_to_human_size(size).rcol(7),
|
266
|
-
identifier.to_s
|
267
|
-
].join(' | ')
|
268
|
-
{:uid => uid, :seqno => seqno, :row_text => row_text}
|
269
|
-
rescue
|
270
|
-
log "Error extracting header for uid #{uid} seqno #{seqno}: #$!\n#{$!.backtrace}"
|
271
|
-
row_text = "#{seqno.to_s} : error extracting this header"
|
272
|
-
{:uid => uid, :seqno => seqno, :row_text => row_text}
|
273
|
-
end
|
274
|
-
|
275
|
-
UNITS = [:b, :kb, :mb, :gb].freeze
|
276
|
-
|
277
|
-
# borrowed from ActionView/Helpers
|
278
|
-
def number_to_human_size(number)
|
279
|
-
if number.to_i < 1024
|
280
|
-
"<1kb" # round up to 1kh
|
281
|
-
else
|
282
|
-
max_exp = UNITS.size - 1
|
283
|
-
exponent = (Math.log(number) / Math.log(1024)).to_i # Convert to base 1024
|
284
|
-
exponent = max_exp if exponent > max_exp # we need this to avoid overflow for the highest unit
|
285
|
-
number /= 1024 ** exponent
|
286
|
-
unit = UNITS[exponent]
|
287
|
-
"#{number}#{unit}"
|
288
|
-
end
|
289
|
-
end
|
290
|
-
|
291
|
-
FLAGMAP = {:Flagged => '*'}
|
292
|
-
# flags is an array like [:Flagged, :Seen]
|
293
|
-
def format_flags(flags)
|
294
|
-
# other flags like "Old" should be hidden here
|
295
|
-
flags = flags.map {|flag| FLAGMAP[flag] || flag}
|
296
|
-
flags.delete("Old")
|
297
|
-
if flags.delete(:Seen).nil?
|
298
|
-
flags << '+' # unread
|
299
|
-
end
|
300
|
-
flags.join('')
|
301
|
-
end
|
302
|
-
|
303
|
-
def search(query)
|
304
|
-
query = Vmail::Query.parse(query)
|
305
|
-
@limit = query.shift.to_i
|
306
|
-
# a limit of zero is effectively no limit
|
307
|
-
if @limit == 0
|
308
|
-
@limit = @num_messages
|
309
|
-
end
|
310
|
-
if query.size == 1 && query[0].downcase == 'all'
|
311
|
-
# form a sequence range
|
312
|
-
query.unshift [[@num_messages - @limit + 1 , 1].max, @num_messages].join(':')
|
313
|
-
@all_search = true
|
314
|
-
else # this is a special query search
|
315
|
-
# set the target range to the whole set
|
316
|
-
query.unshift "1:#@num_messages"
|
317
|
-
@all_search = false
|
318
|
-
end
|
319
|
-
@query = query.map {|x| x.to_s.downcase}
|
320
|
-
query_string = Vmail::Query.args2string(@query)
|
321
|
-
log "Search query: #{@query} > #{query_string.inspect}"
|
322
|
-
log "- @all_search #{@all_search}"
|
323
|
-
@query = query
|
324
|
-
@ids = reconnect_if_necessary(180) do # increase timeout to 3 minutes
|
325
|
-
@imap.search(query_string)
|
326
|
-
end
|
327
|
-
# save ids in @ids, because filtered search relies on it
|
328
|
-
fetch_ids = if @all_search
|
329
|
-
@ids
|
330
|
-
else #filtered search
|
331
|
-
@start_index = [@ids.length - @limit, 0].max
|
332
|
-
@ids[@start_index..-1]
|
333
|
-
end
|
334
|
-
self.max_seqno = @ids[-1]
|
335
|
-
log "- search query got #{@ids.size} results; max seqno: #{self.max_seqno}"
|
336
|
-
clear_cached_message
|
337
|
-
res = fetch_row_text(fetch_ids)
|
338
|
-
if STDOUT.tty?
|
339
|
-
add_more_message_line(res, fetch_ids[0])
|
340
|
-
else
|
341
|
-
# non interactive mode
|
342
|
-
puts [@mailbox, res].join("\n")
|
343
|
-
end
|
344
|
-
rescue
|
345
|
-
log "ERROR:\n#{$!.inspect}\n#{$!.backtrace.join("\n")}"
|
346
|
-
end
|
347
|
-
|
348
184
|
def decrement_max_seqno(num)
|
349
185
|
return unless STDIN.tty?
|
350
186
|
log "Decremented max seqno from #{self.max_seqno} to #{self.max_seqno - num}"
|
351
187
|
self.max_seqno -= num
|
352
188
|
end
|
353
189
|
|
190
|
+
# TODO why not just reload the current page?
|
354
191
|
def update
|
192
|
+
if search_query?
|
193
|
+
log "Update aborted because query is search query: #{@query.inspect}"
|
194
|
+
return ""
|
195
|
+
end
|
355
196
|
prime_connection
|
356
197
|
old_num_messages = @num_messages
|
357
198
|
# we need to re-select the mailbox to get the new highest id
|
@@ -371,7 +212,7 @@ module Vmail
|
|
371
212
|
log "- update: new uids: #{new_ids.inspect}"
|
372
213
|
if !new_ids.empty?
|
373
214
|
self.max_seqno = new_ids[-1]
|
374
|
-
res =
|
215
|
+
res = get_message_headers(new_ids.reverse, false, true)
|
375
216
|
res
|
376
217
|
else
|
377
218
|
''
|
@@ -379,202 +220,15 @@ module Vmail
|
|
379
220
|
end
|
380
221
|
|
381
222
|
# gets 100 messages prior to id
|
382
|
-
def more_messages
|
383
|
-
log "
|
384
|
-
|
385
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
else # filter search query
|
392
|
-
log "@start_index #@start_index"
|
393
|
-
x = [(@start_index - limit), 0].max
|
394
|
-
y = [@start_index - 1, 0].max
|
395
|
-
@start_index = x
|
396
|
-
res = fetch_row_text(@ids[x..y])
|
397
|
-
add_more_message_line(res, @ids[x])
|
398
|
-
end
|
399
|
-
end
|
400
|
-
|
401
|
-
def add_more_message_line(res, start_seqno)
|
402
|
-
log "Add_more_message_line for start_seqno #{start_seqno}"
|
403
|
-
if @all_search
|
404
|
-
return res if start_seqno.nil?
|
405
|
-
remaining = start_seqno - 1
|
406
|
-
else # filter search
|
407
|
-
remaining = (@ids.index(start_seqno) || 1) - 1
|
408
|
-
end
|
409
|
-
if remaining < 1
|
410
|
-
log "None remaining"
|
411
|
-
return "Showing all matches\n" + res
|
412
|
-
end
|
413
|
-
log "Remaining messages: #{remaining}"
|
414
|
-
"> Load #{[100, remaining].min} more messages. #{remaining} remaining.\n" + res
|
415
|
-
end
|
416
|
-
|
417
|
-
def show_message(uid, raw=false)
|
418
|
-
log "Show message: #{uid}"
|
419
|
-
return @current_mail.to_s if raw
|
420
|
-
uid = uid.to_i
|
421
|
-
if uid == @current_message_uid
|
422
|
-
return @current_message
|
423
|
-
end
|
424
|
-
|
425
|
-
#prefetch_adjacent(index) # deprecated
|
426
|
-
|
427
|
-
# TODO keep state in vim buffers, instead of on Vmail Ruby client
|
428
|
-
# envelope_data[:row_text] = envelope_data[:row_text].gsub(/^\+ /, ' ').gsub(/^\*\+/, '* ') # mark as read in cache
|
429
|
-
#seqno = envelope_data[:seqno]
|
430
|
-
|
431
|
-
log "Showing message uid: #{uid}"
|
432
|
-
data = if x = message_cache[[@mailbox, uid]]
|
433
|
-
log "- message cache hit"
|
434
|
-
x
|
435
|
-
else
|
436
|
-
log "- fetching and storing to message_cache[[#{@mailbox}, #{uid}]]"
|
437
|
-
fetch_and_cache(uid)
|
438
|
-
end
|
439
|
-
if data.nil?
|
440
|
-
# retry, though this is a hack!
|
441
|
-
log "- data is nil. retrying..."
|
442
|
-
return show_message(uid, raw)
|
443
|
-
end
|
444
|
-
# make this more DRY later by directly using a ref to the hash
|
445
|
-
mail = data[:mail]
|
446
|
-
size = data[:size]
|
447
|
-
@current_message_uid = uid
|
448
|
-
log "- setting @current_mail"
|
449
|
-
@current_mail = mail # used later to show raw message or extract attachments if any
|
450
|
-
@current_message = data[:message_text]
|
451
|
-
rescue
|
452
|
-
log "Parsing error"
|
453
|
-
"Error encountered parsing this message:\n#{$!}\n#{$!.backtrace.join("\n")}"
|
454
|
-
end
|
455
|
-
|
456
|
-
def fetch_and_cache(uid)
|
457
|
-
if data = message_cache[[@mailbox, uid]]
|
458
|
-
return data
|
459
|
-
end
|
460
|
-
fetch_data = reconnect_if_necessary do
|
461
|
-
res = @imap.uid_fetch(uid, ["FLAGS", "RFC822", "RFC822.SIZE"])
|
462
|
-
if res.nil?
|
463
|
-
# retry one more time ( find a more elegant way to do this )
|
464
|
-
res = @imap.uid_fetch(uid, ["FLAGS", "RFC822", "RFC822.SIZE"])
|
465
|
-
end
|
466
|
-
res[0]
|
467
|
-
end
|
468
|
-
# USE THIS
|
469
|
-
size = fetch_data.attr["RFC822.SIZE"]
|
470
|
-
flags = fetch_data.attr["FLAGS"]
|
471
|
-
mail = Mail.new(fetch_data.attr['RFC822'])
|
472
|
-
formatter = Vmail::MessageFormatter.new(mail)
|
473
|
-
message_text = <<-EOF
|
474
|
-
#{@mailbox} uid:#{uid} #{number_to_human_size size} #{flags.inspect} #{format_parts_info(formatter.list_parts)}
|
475
|
-
#{divider '-'}
|
476
|
-
#{format_headers(formatter.extract_headers)}
|
477
|
-
|
478
|
-
#{formatter.process_body}
|
479
|
-
EOF
|
480
|
-
# log "Storing message_cache[[#{@mailbox}, #{uid}]]"
|
481
|
-
d = {:mail => mail, :size => size, :message_text => message_text, :seqno => fetch_data.seqno, :flags => flags}
|
482
|
-
message_cache[[@mailbox, uid]] = d
|
483
|
-
rescue
|
484
|
-
msg = "Error encountered parsing message uid #{uid}:\n#{$!}\n#{$!.backtrace.join("\n")}" +
|
485
|
-
"\n\nRaw message:\n\n" + mail.to_s
|
486
|
-
log msg
|
487
|
-
log message_text
|
488
|
-
{:message_text => msg}
|
489
|
-
end
|
490
|
-
|
491
|
-
# deprecated
|
492
|
-
def prefetch_adjacent(index)
|
493
|
-
Thread.new do
|
494
|
-
[index + 1, index - 1].each do |idx|
|
495
|
-
fetch_and_cache(idx)
|
496
|
-
end
|
497
|
-
end
|
498
|
-
end
|
499
|
-
|
500
|
-
def format_parts_info(parts)
|
501
|
-
lines = parts.select {|part| part !~ %r{text/plain}}
|
502
|
-
if lines.size > 0
|
503
|
-
"\n#{lines.join("\n")}"
|
504
|
-
end
|
505
|
-
end
|
506
|
-
|
507
|
-
# id_set is a string comming from the vim client
|
508
|
-
# action is -FLAGS or +FLAGS
|
509
|
-
def flag(uid_set, action, flg)
|
510
|
-
log "Flag #{uid_set} #{flg} #{action}"
|
511
|
-
uid_set = uid_set.split(',').map(&:to_i)
|
512
|
-
if flg == 'Deleted'
|
513
|
-
log "Deleting uid_set: #{uid_set.inspect}"
|
514
|
-
decrement_max_seqno(uid_set.size)
|
515
|
-
# for delete, do in a separate thread because deletions are slow
|
516
|
-
spawn_thread_if_tty do
|
517
|
-
unless @mailbox == mailbox_aliases['trash']
|
518
|
-
log "@imap.uid_copy #{uid_set.inspect} to #{mailbox_aliases['trash']}"
|
519
|
-
log @imap.uid_copy(uid_set, mailbox_aliases['trash'])
|
520
|
-
end
|
521
|
-
log "@imap.uid_store #{uid_set.inspect} #{action} [#{flg.to_sym}]"
|
522
|
-
log @imap.uid_store(uid_set, action, [flg.to_sym])
|
523
|
-
reload_mailbox
|
524
|
-
clear_cached_message
|
525
|
-
end
|
526
|
-
elsif flg == 'spam' || flg == mailbox_aliases['spam']
|
527
|
-
log "Marking as spam uid_set: #{uid_set.inspect}"
|
528
|
-
decrement_max_seqno(uid_set.size)
|
529
|
-
spawn_thread_if_tty do
|
530
|
-
log "@imap.uid_copy #{uid_set.inspect} to #{mailbox_aliases['spam']}"
|
531
|
-
log @imap.uid_copy(uid_set, mailbox_aliases['spam'])
|
532
|
-
log "@imap.uid_store #{uid_set.inspect} #{action} [:Deleted]"
|
533
|
-
log @imap.uid_store(uid_set, action, [:Deleted])
|
534
|
-
reload_mailbox
|
535
|
-
clear_cached_message
|
536
|
-
end
|
537
|
-
else
|
538
|
-
log "Flagging uid_set: #{uid_set.inspect}"
|
539
|
-
spawn_thread_if_tty do
|
540
|
-
log "@imap.uid_store #{uid_set.inspect} #{action} [#{flg.to_sym}]"
|
541
|
-
log @imap.uid_store(uid_set, action, [flg.to_sym])
|
542
|
-
end
|
543
|
-
end
|
544
|
-
end
|
545
|
-
|
546
|
-
def move_to(uid_set, mailbox)
|
547
|
-
uid_set = uid_set.split(',').map(&:to_i)
|
548
|
-
decrement_max_seqno(uid_set.size)
|
549
|
-
log "Move #{uid_set.inspect} to #{mailbox}"
|
550
|
-
if mailbox == 'all'
|
551
|
-
log "Archiving messages"
|
552
|
-
end
|
553
|
-
if mailbox_aliases[mailbox]
|
554
|
-
mailbox = mailbox_aliases[mailbox]
|
555
|
-
end
|
556
|
-
create_if_necessary mailbox
|
557
|
-
log "Moving uid_set: #{uid_set.inspect} to #{mailbox}"
|
558
|
-
spawn_thread_if_tty do
|
559
|
-
log @imap.uid_copy(uid_set, mailbox)
|
560
|
-
log @imap.uid_store(uid_set, '+FLAGS', [:Deleted])
|
561
|
-
reload_mailbox
|
562
|
-
clear_cached_message
|
563
|
-
log "Moved uid_set #{uid_set.inspect} to #{mailbox}"
|
564
|
-
end
|
565
|
-
end
|
566
|
-
|
567
|
-
def copy_to(uid_set, mailbox)
|
568
|
-
uid_set = uid_set.split(',').map(&:to_i)
|
569
|
-
if mailbox_aliases[mailbox]
|
570
|
-
mailbox = mailbox_aliases[mailbox]
|
571
|
-
end
|
572
|
-
create_if_necessary mailbox
|
573
|
-
log "Copying #{uid_set.inspect} to #{mailbox}"
|
574
|
-
spawn_thread_if_tty do
|
575
|
-
log @imap.uid_copy(uid_set, mailbox)
|
576
|
-
log "Copied uid_set #{uid_set.inspect} to #{mailbox}"
|
577
|
-
end
|
223
|
+
def more_messages
|
224
|
+
log "Getting more_messages"
|
225
|
+
x = [(@start_index - @limit), 0].max
|
226
|
+
y = [@start_index - 1, 0].max
|
227
|
+
@start_index = x
|
228
|
+
fetch_ids = search_query? ? @ids[x..y] : (x..y).to_a
|
229
|
+
message_ids = fetch_and_cache_headers(fetch_ids)
|
230
|
+
res = get_message_headers message_ids
|
231
|
+
with_more_message_line(res)
|
578
232
|
end
|
579
233
|
|
580
234
|
def spawn_thread_if_tty(&block)
|
@@ -632,11 +286,7 @@ EOF
|
|
632
286
|
|
633
287
|
def reply_template(replyall=false)
|
634
288
|
log "Sending reply template"
|
635
|
-
|
636
|
-
log "- missing @current mail!"
|
637
|
-
return nil
|
638
|
-
end
|
639
|
-
reply_headers = Vmail::ReplyTemplate.new(@current_mail, @username, @name, replyall, @always_cc).reply_headers
|
289
|
+
reply_headers = Vmail::ReplyTemplate.new(current_message.rfc822, @username, @name, replyall, @always_cc).reply_headers
|
640
290
|
body = reply_headers.delete(:body)
|
641
291
|
format_headers(reply_headers) + "\n\n\n" + body + signature
|
642
292
|
end
|
@@ -647,8 +297,8 @@ EOF
|
|
647
297
|
end
|
648
298
|
|
649
299
|
def forward_template
|
650
|
-
original_body =
|
651
|
-
formatter = Vmail::MessageFormatter.new(
|
300
|
+
original_body = current_message.split(/\n-{20,}\n/, 2)[1]
|
301
|
+
formatter = Vmail::MessageFormatter.new(current_mail)
|
652
302
|
headers = formatter.extract_headers
|
653
303
|
subject = headers['subject']
|
654
304
|
if subject !~ /Fwd: /
|
@@ -660,12 +310,6 @@ EOF
|
|
660
310
|
original_body + signature
|
661
311
|
end
|
662
312
|
|
663
|
-
def divider(str)
|
664
|
-
str * DIVIDER_WIDTH
|
665
|
-
end
|
666
|
-
|
667
|
-
SENT_MESSAGES_FILE = "sent-messages.txt"
|
668
|
-
|
669
313
|
def format_sent_message(mail)
|
670
314
|
formatter = Vmail::MessageFormatter.new(mail)
|
671
315
|
message_text = <<-EOF
|
@@ -673,14 +317,10 @@ Sent Message #{self.format_parts_info(formatter.list_parts)}
|
|
673
317
|
|
674
318
|
#{format_headers(formatter.extract_headers)}
|
675
319
|
|
676
|
-
#{formatter.
|
320
|
+
#{formatter.plaintext_part}
|
677
321
|
EOF
|
678
322
|
end
|
679
323
|
|
680
|
-
def backup_sent_message(message)
|
681
|
-
File.open(SENT_MESSAGES_FILE, "a") {|f| f.write(divider('-') + "\n" + format_sent_message(message))}
|
682
|
-
end
|
683
|
-
|
684
324
|
def deliver(text)
|
685
325
|
# parse the text. The headers are yaml. The rest is text body.
|
686
326
|
require 'net/smtp'
|
@@ -691,8 +331,7 @@ EOF
|
|
691
331
|
log res.inspect
|
692
332
|
log "\n"
|
693
333
|
msg = if res.is_a?(Mail::Message)
|
694
|
-
|
695
|
-
"Message '#{mail.subject}' sent and saved to #{SENT_MESSAGES_FILE}"
|
334
|
+
"Message '#{mail.subject}' sent"
|
696
335
|
else
|
697
336
|
"Failed to deliver message '#{mail.subject}'!"
|
698
337
|
end
|
@@ -746,11 +385,11 @@ EOF
|
|
746
385
|
|
747
386
|
def save_attachments(dir)
|
748
387
|
log "Save_attachments #{dir}"
|
749
|
-
if
|
388
|
+
if !current_mail
|
750
389
|
log "Missing a current message"
|
751
390
|
end
|
752
|
-
return unless dir &&
|
753
|
-
attachments =
|
391
|
+
return unless dir && current_mail
|
392
|
+
attachments = current_mail.attachments
|
754
393
|
`mkdir -p #{dir}`
|
755
394
|
saved = attachments.map do |x|
|
756
395
|
path = File.join(dir, x.filename)
|
@@ -763,14 +402,14 @@ EOF
|
|
763
402
|
|
764
403
|
def open_html_part
|
765
404
|
log "Open_html_part"
|
766
|
-
log
|
767
|
-
multipart =
|
405
|
+
log current_mail.parts.inspect
|
406
|
+
multipart = current_mail.parts.detect {|part| part.multipart?}
|
768
407
|
html_part = if multipart
|
769
408
|
multipart.parts.detect {|part| part.header["Content-Type"].to_s =~ /text\/html/}
|
770
|
-
elsif !
|
771
|
-
|
409
|
+
elsif ! current_mail.parts.empty?
|
410
|
+
current_mail.parts.detect {|part| part.header["Content-Type"].to_s =~ /text\/html/}
|
772
411
|
else
|
773
|
-
|
412
|
+
current_mail.body
|
774
413
|
end
|
775
414
|
return if html_part.nil?
|
776
415
|
outfile = 'part.html'
|
@@ -780,8 +419,11 @@ EOF
|
|
780
419
|
end
|
781
420
|
|
782
421
|
def window_width=(width)
|
783
|
-
|
784
|
-
@width
|
422
|
+
@next_window_width = width.to_i
|
423
|
+
if @width.nil?
|
424
|
+
@width = @next_window_width
|
425
|
+
end
|
426
|
+
log "Setting next window width to #{width}"
|
785
427
|
end
|
786
428
|
|
787
429
|
def smtp_settings
|