vmail 1.6.8 → 1.6.9
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/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
|