vmail 0.0.6 → 0.0.7
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 +19 -8
- data/bin/vmail +1 -1
- data/lib/vmail.rb +15 -4
- data/lib/vmail.vim +91 -48
- data/lib/{contacts_extractor.rb → vmail/contacts_extractor.rb} +14 -17
- data/lib/vmail/imap_client.rb +25 -3
- data/lib/vmail/options.rb +129 -0
- data/lib/vmail/version.rb +1 -1
- data/test/message_formatter_test.rb +5 -5
- data/vmail.gemspec +1 -0
- metadata +17 -3
data/NOTES
CHANGED
@@ -481,13 +481,20 @@ DONE
|
|
481
481
|
- can specify a directory, then all the items in the directory get
|
482
482
|
attached and sent
|
483
483
|
- extract and append text of all selected messages into file (append)
|
484
|
-
|
485
|
-
|
484
|
+
- ,s s confusion? Star vs search
|
485
|
+
- ,* is star now
|
486
|
+
- archive function (shortcut for move to all mail)
|
487
|
+
- remember searches?
|
488
|
+
- perma-delete from Trash
|
489
|
+
- put move to> and copy to> prompts
|
490
|
+
- create mailboxes if they don't exist on move or copy
|
486
491
|
- tweak contacts list script (plus instruction for how to use)
|
492
|
+
- .vmailrc is a yaml file, first in current dir, then check home, can
|
493
|
+
also use command line opts
|
494
|
+
- fix tests
|
495
|
+
|
496
|
+
NEXT:
|
487
497
|
- help doc: just return readme file, or a vim version of it (vim filetype later)
|
488
|
-
- follow mysql and use -u and -p flags on startup of server?
|
489
|
-
- omitting -p flag forces prompt
|
490
|
-
- ,s s confusion? Star vs search
|
491
498
|
- note dependence on lynx in README
|
492
499
|
|
493
500
|
later:
|
@@ -495,8 +502,6 @@ later:
|
|
495
502
|
- mvim redrawstatus line bug
|
496
503
|
http://vim.1045645.n5.nabble.com/Redrawing-bug-in-MacVim-Command-T-since-commit-ba44868-td3248742.html
|
497
504
|
- allow one daemon, multiple clients (select mailbox?)
|
498
|
-
- remember searches?
|
499
|
-
- archive function (shortcut for move to all mail)
|
500
505
|
- crate to package app as a OS X app?
|
501
506
|
http://rubyconf2008.confreaks.com/crate-packaging-your-ruby-application.html
|
502
507
|
- need to bundle macvim
|
@@ -523,7 +528,9 @@ later:
|
|
523
528
|
- enhance contacts auto fix with more advanced vim script
|
524
529
|
- create contacts database on first startup
|
525
530
|
|
526
|
-
|
531
|
+
won't do
|
532
|
+
- follow mysql and use -u and -p flags on startup of server?
|
533
|
+
- omitting -p flag forces prompt
|
527
534
|
|
528
535
|
------------------------------------------------------------------------
|
529
536
|
|
@@ -536,4 +543,8 @@ imap.search ["HEADER", "MESSAGE-ID", "<757FC46C-9394-494E-91B4-B051F48419DA@prx.
|
|
536
543
|
|
537
544
|
% is filename
|
538
545
|
|
546
|
+
" TODO the range doesn't quite work as expect, need <line1> <line2>
|
547
|
+
" trying to make user defined commands that work from : prompt
|
548
|
+
" command -buffer -range VmailDelete call s:toggle_star("Deleted")
|
549
|
+
" command -buffer -range VmailStar call s:toggle_star("Flagged")
|
539
550
|
|
data/bin/vmail
CHANGED
data/lib/vmail.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require 'vmail/options'
|
1
2
|
require 'vmail/imap_client'
|
2
3
|
|
3
4
|
module Vmail
|
@@ -6,13 +7,23 @@ module Vmail
|
|
6
7
|
def start
|
7
8
|
vim = ENV['VMAIL_VIM'] || 'vim'
|
8
9
|
|
9
|
-
|
10
|
+
opts = Vmail::Options.new(ARGV)
|
11
|
+
opts.config
|
12
|
+
|
13
|
+
config = opts.config
|
14
|
+
contacts_file = opts.contacts_file
|
15
|
+
|
10
16
|
logfile = (vim == 'mvim') ? STDERR : 'vmail.log'
|
11
17
|
config.merge! 'logfile' => logfile
|
12
18
|
|
13
|
-
puts "starting vmail imap client
|
19
|
+
puts "starting vmail imap client for #{config['username']}"
|
14
20
|
|
15
|
-
drb_uri =
|
21
|
+
drb_uri = begin
|
22
|
+
Vmail::ImapClient.daemon config
|
23
|
+
rescue
|
24
|
+
puts "Failure:", $!
|
25
|
+
exit(1)
|
26
|
+
end
|
16
27
|
|
17
28
|
server = DRbObject.new_with_uri drb_uri
|
18
29
|
|
@@ -34,7 +45,7 @@ module Vmail
|
|
34
45
|
|
35
46
|
# invoke vim
|
36
47
|
vimscript = File.expand_path("../vmail.vim", __FILE__)
|
37
|
-
vim_command = "DRB_URI='#{drb_uri}' VMAIL_MAILBOX=#{String.shellescape(mailbox)} VMAIL_QUERY=#{String.shellescape(query.join(' '))} #{vim} -S #{vimscript} #{buffer_file}"
|
48
|
+
vim_command = "DRB_URI='#{drb_uri}' VMAIL_CONTACTS_FILE=#{contacts_file} VMAIL_MAILBOX=#{String.shellescape(mailbox)} VMAIL_QUERY=#{String.shellescape(query.join(' '))} #{vim} -S #{vimscript} #{buffer_file}"
|
38
49
|
puts vim_command
|
39
50
|
system(vim_command)
|
40
51
|
|
data/lib/vmail.vim
CHANGED
@@ -16,6 +16,7 @@ let s:more_messages_command = s:client_script . "more_messages "
|
|
16
16
|
let s:flag_command = s:client_script . "flag "
|
17
17
|
let s:append_to_file_command = s:client_script . "append_to_file "
|
18
18
|
let s:move_to_command = s:client_script . "move_to "
|
19
|
+
let s:copy_to_command = s:client_script . "copy_to "
|
19
20
|
let s:new_message_template_command = s:client_script . "new_message_template "
|
20
21
|
let s:reply_template_command = s:client_script . "reply_template "
|
21
22
|
let s:forward_template_command = s:client_script . "forward_template "
|
@@ -24,7 +25,6 @@ let s:save_draft_command = s:client_script . "save_draft "
|
|
24
25
|
let s:save_attachments_command = s:client_script . "save_attachments "
|
25
26
|
let s:open_html_command = s:client_script . "open_html_part "
|
26
27
|
let s:message_bufname = "MessageWindow"
|
27
|
-
let s:list_bufname = "MessageListWindow"
|
28
28
|
|
29
29
|
function! VmailStatusLine()
|
30
30
|
return "%<%f\ " . s:mailbox . " " . s:query . "%r%=%-14.(%l,%c%V%)\ %P"
|
@@ -256,8 +256,33 @@ func! s:delete_messages(flag) range
|
|
256
256
|
if len(uids) > 2
|
257
257
|
call feedkeys("\<cr>")
|
258
258
|
endif
|
259
|
+
redraw
|
260
|
+
echo len(uids) . " message" . (len(uids) == 1 ? '' : 's') . " marked " . a:flag
|
261
|
+
endfunc
|
262
|
+
|
263
|
+
func! s:archive_messages() range
|
264
|
+
let lnum = a:firstline
|
265
|
+
let n = 0
|
266
|
+
let uids = []
|
267
|
+
while lnum <= a:lastline
|
268
|
+
let line = getline(lnum)
|
269
|
+
let message_uid = matchstr(line, '^\d\+')
|
270
|
+
call add(uids, message_uid)
|
271
|
+
let lnum = lnum + 1
|
272
|
+
endwhile
|
273
|
+
let uid_set = join(uids, ",")
|
274
|
+
let command = s:move_to_command . uid_set . ' ' . shellescape("[Gmail]/All Mail")
|
275
|
+
echo "archiving message" . (len(uids) == 1 ? '' : 's')
|
276
|
+
let res = system(command)
|
277
|
+
setlocal modifiable
|
278
|
+
exec a:firstline . "," . a:lastline . "delete"
|
279
|
+
setlocal nomodifiable
|
280
|
+
write
|
281
|
+
redraw
|
282
|
+
echo len(uids) . " message" . (len(uids) == 1 ? '' : 's') . " archived"
|
259
283
|
endfunc
|
260
284
|
|
285
|
+
|
261
286
|
" --------------------------------------------------------------------------------
|
262
287
|
" append text bodies of a set of messages to a file
|
263
288
|
func! s:append_messages_to_file() range
|
@@ -281,7 +306,8 @@ endfunc
|
|
281
306
|
|
282
307
|
" --------------------------------------------------------------------------------
|
283
308
|
" move to another mailbox
|
284
|
-
function! s:move_to_mailbox() range
|
309
|
+
function! s:move_to_mailbox(copy) range
|
310
|
+
let s:copy_to_mailbox = a:copy
|
285
311
|
let lnum = a:firstline
|
286
312
|
let n = 0
|
287
313
|
let uids = []
|
@@ -301,31 +327,38 @@ function! s:move_to_mailbox() range
|
|
301
327
|
setlocal noswapfile
|
302
328
|
setlocal modifiable
|
303
329
|
resize 1
|
330
|
+
let prompt = "select mailbox to " . (a:copy ? 'copy' : 'move') . " to: "
|
331
|
+
call setline(1, prompt)
|
332
|
+
normal $
|
304
333
|
inoremap <silent> <buffer> <cr> <Esc>:call <SID>complete_move_to_mailbox()<CR>
|
305
334
|
inoremap <silent> <buffer> <esc> <Esc>:q<cr>
|
306
335
|
set completefunc=CompleteMoveMailbox
|
307
336
|
" c-p clears the line
|
308
337
|
let s:firstline = a:firstline
|
309
338
|
let s:lastline = a:lastline
|
310
|
-
call feedkeys("
|
339
|
+
call feedkeys("a\<c-x>\<c-u>\<c-p>", 't')
|
311
340
|
" save these in script scope to delete the lines when move completes
|
312
341
|
endfunction
|
313
342
|
|
314
|
-
" Open command window to choose a mailbox to move a message to.
|
315
|
-
" Very similar to mailbox_window() function
|
316
343
|
function! s:complete_move_to_mailbox()
|
317
|
-
let mailbox = getline(line('.'))
|
344
|
+
let mailbox = get(split(getline(line('.')), ": "), 1)
|
318
345
|
close
|
319
|
-
|
320
|
-
|
321
|
-
|
346
|
+
if s:copy_to_mailbox
|
347
|
+
let command = s:copy_to_command . s:uid_set . ' ' . shellescape(mailbox)
|
348
|
+
else
|
349
|
+
let command = s:move_to_command . s:uid_set . ' ' . shellescape(mailbox)
|
322
350
|
endif
|
323
|
-
|
324
|
-
echo
|
351
|
+
redraw
|
352
|
+
echo "moving uids ". s:uid_set . " to mailbox " . mailbox
|
325
353
|
let res = system(command)
|
326
354
|
setlocal modifiable
|
327
|
-
|
355
|
+
if !s:copy_to_mailbox
|
356
|
+
exec s:firstline . "," . s:lastline . "delete"
|
357
|
+
end
|
328
358
|
setlocal nomodifiable
|
359
|
+
write
|
360
|
+
redraw
|
361
|
+
echo "done"
|
329
362
|
endfunction
|
330
363
|
|
331
364
|
function! CompleteMoveMailbox(findstart, base)
|
@@ -334,7 +367,12 @@ function! CompleteMoveMailbox(findstart, base)
|
|
334
367
|
endif
|
335
368
|
if a:findstart
|
336
369
|
" locate the start of the word
|
337
|
-
|
370
|
+
let line = getline('.')
|
371
|
+
let start = col('.') - 1
|
372
|
+
while start > 0 && line[start - 1] =~ '\a'
|
373
|
+
let start -= 1
|
374
|
+
endwhile
|
375
|
+
return start
|
338
376
|
else
|
339
377
|
" find months matching with "a:base"
|
340
378
|
let res = []
|
@@ -359,13 +397,37 @@ function! s:get_mailbox_list()
|
|
359
397
|
let s:mailboxes = split(res, "\n", '')
|
360
398
|
endfunction
|
361
399
|
|
400
|
+
" -------------------------------------------------------------------------------
|
401
|
+
" select mailbox
|
402
|
+
|
403
|
+
function! s:mailbox_window()
|
404
|
+
call s:get_mailbox_list()
|
405
|
+
topleft split MailboxSelect
|
406
|
+
setlocal buftype=nofile
|
407
|
+
setlocal noswapfile
|
408
|
+
setlocal modifiable
|
409
|
+
resize 1
|
410
|
+
inoremap <silent> <buffer> <cr> <Esc>:call <SID>select_mailbox()<CR>
|
411
|
+
inoremap <silent> <buffer> <esc> <Esc>:q<cr>
|
412
|
+
set completefunc=CompleteMailbox
|
413
|
+
" c-p clears the line
|
414
|
+
call setline(1, "select mailbox to switch to: ")
|
415
|
+
normal $
|
416
|
+
call feedkeys("a\<c-x>\<c-u>\<c-p>", 't')
|
417
|
+
endfunction
|
418
|
+
|
362
419
|
function! CompleteMailbox(findstart, base)
|
363
420
|
if !exists("s:mailboxes")
|
364
421
|
call s:get_mailbox_list()
|
365
422
|
endif
|
366
423
|
if a:findstart
|
367
424
|
" locate the start of the word
|
368
|
-
|
425
|
+
let line = getline('.')
|
426
|
+
let start = col('.') - 1
|
427
|
+
while start > 0 && line[start - 1] =~ '\a'
|
428
|
+
let start -= 1
|
429
|
+
endwhile
|
430
|
+
return start
|
369
431
|
else
|
370
432
|
" find mailboxes matching with "a:base"
|
371
433
|
let res = []
|
@@ -378,34 +440,11 @@ function! CompleteMailbox(findstart, base)
|
|
378
440
|
endif
|
379
441
|
endfun
|
380
442
|
|
381
|
-
" -------------------------------------------------------------------------------
|
382
|
-
" select mailbox
|
383
|
-
|
384
|
-
function! s:mailbox_window()
|
385
|
-
if !exists("s:mailboxes")
|
386
|
-
call s:get_mailbox_list()
|
387
|
-
endif
|
388
|
-
topleft split MailboxSelect
|
389
|
-
setlocal buftype=nofile
|
390
|
-
setlocal noswapfile
|
391
|
-
setlocal modifiable
|
392
|
-
resize 1
|
393
|
-
inoremap <silent> <buffer> <cr> <Esc>:call <SID>select_mailbox()<CR>
|
394
|
-
inoremap <silent> <buffer> <esc> <Esc>:q<cr>
|
395
|
-
set completefunc=CompleteMailbox
|
396
|
-
" c-p clears the line
|
397
|
-
call feedkeys("i\<c-x>\<c-u>\<c-p>", 't')
|
398
|
-
endfunction
|
399
|
-
|
400
443
|
function! s:select_mailbox()
|
401
|
-
let mailbox = getline(line('.'))
|
444
|
+
let mailbox = get(split(getline(line('.')), ": "), 1)
|
402
445
|
close
|
403
446
|
call s:focus_message_window()
|
404
447
|
close
|
405
|
-
" check if mailbox is a real mailbox
|
406
|
-
if (index(s:mailboxes, mailbox) == -1)
|
407
|
-
return
|
408
|
-
endif
|
409
448
|
let s:mailbox = mailbox
|
410
449
|
let command = s:select_mailbox_command . shellescape(s:mailbox)
|
411
450
|
call system(command)
|
@@ -421,6 +460,7 @@ function! s:select_mailbox()
|
|
421
460
|
execute "normal Gdd\<c-y>"
|
422
461
|
normal G
|
423
462
|
setlocal nomodifiable
|
463
|
+
write
|
424
464
|
normal z.
|
425
465
|
endfunction
|
426
466
|
|
@@ -460,6 +500,7 @@ function! s:do_search()
|
|
460
500
|
execute "normal Gdd\<c-y>"
|
461
501
|
normal z.
|
462
502
|
setlocal nomodifiable
|
503
|
+
write
|
463
504
|
endfunction
|
464
505
|
|
465
506
|
function! s:more_messages()
|
@@ -519,6 +560,7 @@ func! s:open_compose_window(command)
|
|
519
560
|
close!
|
520
561
|
1,$delete
|
521
562
|
put! =res
|
563
|
+
call feedkeys("\<cr>")
|
522
564
|
normal 1G
|
523
565
|
call s:compose_window_mappings()
|
524
566
|
set completefunc=CompleteContact
|
@@ -540,7 +582,7 @@ function! CompleteContact(findstart, base)
|
|
540
582
|
return start
|
541
583
|
else
|
542
584
|
" find contacts matching with "a:base"
|
543
|
-
let matches = system("grep -i " . shellescape(a:base) . "
|
585
|
+
let matches = system("grep -i " . shellescape(a:base) . " " . $VMAIL_CONTACTS_FILE)
|
544
586
|
return split(matches, "\n")
|
545
587
|
endif
|
546
588
|
endfun
|
@@ -620,8 +662,11 @@ func! s:message_window_mappings()
|
|
620
662
|
noremap <silent> <buffer> <Leader>c :call <SID>compose_message()<CR>
|
621
663
|
noremap <silent> <buffer> <Leader>h :call <SID>open_html_part()<CR><cr>
|
622
664
|
nnoremap <silent> <buffer> q :close<cr>
|
623
|
-
nnoremap <silent> <buffer> <leader
|
624
|
-
nnoremap <silent> <buffer>
|
665
|
+
nnoremap <silent> <buffer> <leader># :call <SID>focus_list_window()<cr>:call <SID>delete_messages("Deleted")<cr>
|
666
|
+
nnoremap <silent> <buffer> <leader>* :call <SID>focus_list_window()<cr>:call <SID>toggle_star()<cr>
|
667
|
+
nnoremap <silent> <buffer> <Leader>b :call <SID>focus_list_window()<cr>call <SID>move_to_mailbox(0)<CR>
|
668
|
+
nnoremap <silent> <buffer> <Leader>B :call <SID>focus_list_window()<cr>call <SID>move_to_mailbox(1)<CR>
|
669
|
+
nnoremap <silent> <buffer> <leader>e :call <SID>focus_list_window()<cr>:call <SID>archive_messages()<cr>
|
625
670
|
nnoremap <silent> <buffer> u :call <SID>focus_list_window()<cr>:call <SID>update()<CR>
|
626
671
|
nnoremap <silent> <buffer> <Leader>m :call <SID>focus_list_window()<cr>:call <SID>mailbox_window()<CR>
|
627
672
|
nnoremap <silent> <buffer> <Leader>A :call <SID>save_attachments()<cr>
|
@@ -632,20 +677,18 @@ endfunc
|
|
632
677
|
func! s:message_list_window_mappings()
|
633
678
|
noremap <silent> <buffer> <cr> :call <SID>show_message()<CR>
|
634
679
|
noremap <silent> <buffer> q :qal!<cr>
|
635
|
-
noremap <silent> <buffer>
|
636
|
-
noremap <silent> <buffer> <leader
|
637
|
-
" TODO the range doesn't quite work as expect, need <line1> <line2>
|
638
|
-
" trying to make user defined commands that work from : prompt
|
639
|
-
" command -buffer -range VmailDelete call s:toggle_star("Deleted")
|
640
|
-
" command -buffer -range VmailStar call s:toggle_star("Flagged")
|
680
|
+
noremap <silent> <buffer> <leader>* :call <SID>toggle_star()<CR>
|
681
|
+
noremap <silent> <buffer> <leader># :call <SID>delete_messages("Deleted")<CR>
|
641
682
|
noremap <silent> <buffer> <leader>! :call <SID>delete_messages("[Gmail]/Spam")<CR>
|
683
|
+
noremap <silent> <buffer> <leader>e :call <SID>archive_messages()<CR>
|
642
684
|
"open a link browser (os x)
|
643
685
|
"autocmd CursorMoved <buffer> call <SID>show_message()
|
644
686
|
noremap <silent> <buffer> <leader>vp :call <SID>append_messages_to_file()<CR>
|
645
687
|
noremap <silent> <buffer> u :call <SID>update()<CR>
|
646
688
|
noremap <silent> <buffer> <Leader>s :call <SID>search_query()<CR>
|
647
689
|
noremap <silent> <buffer> <Leader>m :call <SID>mailbox_window()<CR>
|
648
|
-
noremap <silent> <buffer> <Leader>
|
690
|
+
noremap <silent> <buffer> <Leader>b :call <SID>move_to_mailbox(0)<CR>
|
691
|
+
noremap <silent> <buffer> <Leader>B :call <SID>move_to_mailbox(1)<CR>
|
649
692
|
noremap <silent> <buffer> <Leader>c :call <SID>compose_message()<CR>
|
650
693
|
noremap <silent> <buffer> <Leader>r :call <SID>show_message()<cr>:call <SID>compose_reply(0)<CR>
|
651
694
|
noremap <silent> <buffer> <Leader>a :call <SID>show_message()<cr>:call <SID>compose_reply(1)<CR>
|
@@ -1,27 +1,30 @@
|
|
1
1
|
require 'net/imap'
|
2
2
|
|
3
|
+
module Vmail
|
3
4
|
class ContactsExtractor
|
4
|
-
def initialize(
|
5
|
-
|
5
|
+
def initialize(username, password)
|
6
|
+
puts "logging as #{username}"
|
7
|
+
@username, @password = username, password
|
6
8
|
end
|
7
9
|
|
8
10
|
def open
|
9
11
|
@imap = Net::IMAP.new('imap.gmail.com', 993, true, nil, false)
|
10
|
-
@imap.login(@username, @password)
|
12
|
+
puts @imap.login(@username, @password)
|
11
13
|
yield @imap
|
12
14
|
@imap.close
|
13
15
|
@imap.disconnect
|
14
16
|
end
|
15
17
|
|
16
|
-
def extract
|
17
|
-
@contacts = []
|
18
|
+
def extract(limit = 500)
|
18
19
|
open do |imap|
|
19
20
|
mailbox = '[Gmail]/Sent Mail'
|
20
21
|
STDERR.puts "selecting #{mailbox}"
|
21
22
|
imap.select(mailbox)
|
22
|
-
STDERR.puts "fetching last
|
23
|
+
STDERR.puts "fetching last #{limit} sent messages"
|
23
24
|
all_uids = imap.uid_search('ALL')
|
24
|
-
|
25
|
+
STDERR.puts "total messages: #{all_uids.size}"
|
26
|
+
limit = [limit, all_uids.size].min
|
27
|
+
STDERR.puts "extracting addresses from #{limit} of them"
|
25
28
|
uids = all_uids[-limit ,limit]
|
26
29
|
imap.uid_fetch(uids, ["FLAGS", "ENVELOPE"]).each do |fetch_data|
|
27
30
|
recipients = fetch_data.attr["ENVELOPE"].to
|
@@ -30,21 +33,15 @@ class ContactsExtractor
|
|
30
33
|
email = [address_struct.mailbox, address_struct.host].join('@')
|
31
34
|
name = address_struct.name
|
32
35
|
if name
|
33
|
-
|
36
|
+
name = Mail::Encodings.unquote_and_convert_to(name, 'utf-8')
|
37
|
+
yield "#{name} <#{email}>"
|
34
38
|
else
|
35
|
-
|
39
|
+
yield email
|
36
40
|
end
|
37
41
|
end
|
38
42
|
end
|
39
43
|
end
|
40
|
-
@contacts
|
41
44
|
end
|
42
45
|
end
|
43
|
-
|
44
|
-
if __FILE__ == $0
|
45
|
-
require 'yaml'
|
46
|
-
config = YAML::load(File.read(File.expand_path("../../config/gmail.yml", __FILE__)))
|
47
|
-
extractor = ContactsExtractor.new(config)
|
48
|
-
# pipe through uniq | sort
|
49
|
-
extractor.extract!
|
50
46
|
end
|
47
|
+
|
data/lib/vmail/imap_client.rb
CHANGED
@@ -269,7 +269,9 @@ EOF
|
|
269
269
|
if flg == 'Deleted'
|
270
270
|
# for delete, do in a separate thread because deletions are slow
|
271
271
|
Thread.new do
|
272
|
-
@
|
272
|
+
unless @mailbox == '[Gmail]/Trash'
|
273
|
+
@imap.uid_copy(uid_set, "[Gmail]/Trash")
|
274
|
+
end
|
273
275
|
res = @imap.uid_store(uid_set, action, [flg.to_sym])
|
274
276
|
end
|
275
277
|
uid_set.each { |uid| @all_uids.delete(uid) }
|
@@ -285,11 +287,11 @@ EOF
|
|
285
287
|
end
|
286
288
|
end
|
287
289
|
|
288
|
-
# uid_set is a string comming from the vim client
|
289
290
|
def move_to(uid_set, mailbox)
|
290
291
|
if MailboxAliases[mailbox]
|
291
292
|
mailbox = MailboxAliases[mailbox]
|
292
293
|
end
|
294
|
+
create_if_necessary mailbox
|
293
295
|
log "move_to #{uid_set.inspect} #{mailbox}"
|
294
296
|
if uid_set.is_a?(String)
|
295
297
|
uid_set = uid_set.split(",").map(&:to_i)
|
@@ -298,6 +300,26 @@ EOF
|
|
298
300
|
log @imap.uid_store(uid_set, '+FLAGS', [:Deleted])
|
299
301
|
end
|
300
302
|
|
303
|
+
def copy_to(uid_set, mailbox)
|
304
|
+
if MailboxAliases[mailbox]
|
305
|
+
mailbox = MailboxAliases[mailbox]
|
306
|
+
end
|
307
|
+
create_if_necessary mailbox
|
308
|
+
log "copy #{uid_set.inspect} #{mailbox}"
|
309
|
+
if uid_set.is_a?(String)
|
310
|
+
uid_set = uid_set.split(",").map(&:to_i)
|
311
|
+
end
|
312
|
+
log @imap.uid_copy(uid_set, mailbox)
|
313
|
+
end
|
314
|
+
|
315
|
+
def create_if_necessary(mailbox)
|
316
|
+
if !@mailboxes.include?(mailbox)
|
317
|
+
log "creating mailbox #{mailbox}"
|
318
|
+
log @imap.create(mailbox)
|
319
|
+
@mailboxes = nil # force reload next fime list_mailboxes() called
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
301
323
|
def append_to_file(file, uid_set)
|
302
324
|
if uid_set.is_a?(String)
|
303
325
|
uid_set = uid_set.split(",").map(&:to_i)
|
@@ -512,7 +534,7 @@ EOF
|
|
512
534
|
|
513
535
|
def self.daemon(config)
|
514
536
|
$gmail = self.start(config)
|
515
|
-
|
537
|
+
DRb.start_service(nil, $gmail)
|
516
538
|
uri = DRb.uri
|
517
539
|
puts "starting gmail service at #{uri}"
|
518
540
|
uri
|
@@ -0,0 +1,129 @@
|
|
1
|
+
require 'optparse'
|
2
|
+
require 'highline/import'
|
3
|
+
|
4
|
+
module Vmail
|
5
|
+
class Options
|
6
|
+
DEFAULT_CONTACTS_FILENAME = "vmail-contacts.txt"
|
7
|
+
attr_accessor :config
|
8
|
+
attr_accessor :contacts_file
|
9
|
+
def initialize(argv)
|
10
|
+
config_file_locations = ['.vmailrc', "#{ENV['HOME']}/.vmailrc"]
|
11
|
+
@config_file = config_file_locations.detect do |path|
|
12
|
+
File.exists?(File.expand_path(path))
|
13
|
+
end
|
14
|
+
@contacts_file = [DEFAULT_CONTACTS_FILENAME, "#{ENV['HOME']}/#{DEFAULT_CONTACTS_FILENAME}"].detect do |path|
|
15
|
+
File.exists?(File.expand_path(path))
|
16
|
+
end
|
17
|
+
@config = {}
|
18
|
+
parse argv
|
19
|
+
end
|
20
|
+
|
21
|
+
def parse(argv)
|
22
|
+
OptionParser.new do |opts|
|
23
|
+
opts.banner = "Usage: vmail [ options ] [ limit ] [ imap search query ]"
|
24
|
+
opts.separator ""
|
25
|
+
opts.separator "Specific options:"
|
26
|
+
opts.on("-c", "--config path", String, "Path to config file") do |config_file|
|
27
|
+
@config_file = config_file
|
28
|
+
end
|
29
|
+
opts.on("-t", "--contacts path", String, "Path to contacts file") do |file|
|
30
|
+
@contacts_file = file
|
31
|
+
end
|
32
|
+
opts.on("-g[n]", "--getcontacts[n]", Integer, "Generate contacts file. n is number of emails to scan (default 500).") do |n|
|
33
|
+
@get_contacts = true
|
34
|
+
@max_messages_to_scan = n || 500
|
35
|
+
end
|
36
|
+
opts.on("-h", "--help", "Show this message") do
|
37
|
+
puts opts
|
38
|
+
exit
|
39
|
+
end
|
40
|
+
opts.separator ""
|
41
|
+
opts.separator INSTRUCTIONS
|
42
|
+
|
43
|
+
begin
|
44
|
+
opts.parse!(argv)
|
45
|
+
if @config_file && File.exists?(@config_file)
|
46
|
+
puts "Using config file #{@config_file}"
|
47
|
+
else
|
48
|
+
puts <<EOF
|
49
|
+
|
50
|
+
Missing config file!
|
51
|
+
|
52
|
+
#{INSTRUCTIONS}
|
53
|
+
EOF
|
54
|
+
exit(1)
|
55
|
+
end
|
56
|
+
|
57
|
+
if @contacts_file.nil?
|
58
|
+
puts "No contacts file found for auto-completion. See help for how to generate it."
|
59
|
+
sleep 0.5
|
60
|
+
end
|
61
|
+
|
62
|
+
@config = YAML::load(File.read(@config_file))
|
63
|
+
if @config['password'].nil?
|
64
|
+
@config['password'] = ask("Enter gmail password (won't be visible & won't be persisted):") {|q| q.echo = false}
|
65
|
+
end
|
66
|
+
|
67
|
+
if @get_contacts
|
68
|
+
require 'vmail/contacts_extractor'
|
69
|
+
extractor = ContactsExtractor.new(@config['username'], @config['password'])
|
70
|
+
File.open(DEFAULT_CONTACTS_FILENAME, 'w') do |file|
|
71
|
+
extractor.extract(@max_messages_to_scan) do |address|
|
72
|
+
STDERR.print '.'
|
73
|
+
file.puts(address.strip)
|
74
|
+
STDERR.flush
|
75
|
+
end
|
76
|
+
end
|
77
|
+
STDERR.print "\n"
|
78
|
+
puts "saved file to #{DEFAULT_CONTACTS_FILENAME}"
|
79
|
+
puts "sorting address..."
|
80
|
+
cmd = "sort #{DEFAULT_CONTACTS_FILENAME} | uniq > vmail-tmp.txt"
|
81
|
+
cmd2 = "mv vmail-tmp.txt #{DEFAULT_CONTACTS_FILENAME}"
|
82
|
+
`#{cmd}`
|
83
|
+
`#{cmd2}`
|
84
|
+
puts "done"
|
85
|
+
exit
|
86
|
+
end
|
87
|
+
|
88
|
+
rescue OptionParser::ParseError => e
|
89
|
+
STDERR.puts e.message, "\n", opts
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
INSTRUCTIONS = <<-EOF
|
97
|
+
CONFIGURATION FILE
|
98
|
+
|
99
|
+
To run vmail, you need to create a yaml file called .vmailrc and save it
|
100
|
+
either in the current directory (the directory from which you launch
|
101
|
+
vmail) or in your home directory.
|
102
|
+
|
103
|
+
This file should look like this, except using your settings:
|
104
|
+
|
105
|
+
username: dhchoi@gmail.com
|
106
|
+
password: password
|
107
|
+
name: Daniel Choi
|
108
|
+
signature: |
|
109
|
+
--
|
110
|
+
Sent via vmail. https://github.com/danchoi/vmail
|
111
|
+
|
112
|
+
This file should be formatted according to the rules of YAML.
|
113
|
+
http://www.yaml.org/spec/1.2/spec.html
|
114
|
+
|
115
|
+
You can omit the password key-value pair if you'd rather not have the password
|
116
|
+
on disk. In that case, you'll prompted for the password each time you
|
117
|
+
start vmail.
|
118
|
+
|
119
|
+
CONTACTS AUTOCOMPLETION
|
120
|
+
|
121
|
+
Vmail uses vim autocompletion to help you auto-complete email addresses.
|
122
|
+
To use this feature, generate a vim-contacts.txt file in the current or
|
123
|
+
home directory. This is a simple list of all the email addresses.
|
124
|
+
Invoking vmail with the -g option generates this file for you by
|
125
|
+
collecting all the recipients and cc's from your last 500 or so sent
|
126
|
+
emails.
|
127
|
+
|
128
|
+
EOF
|
129
|
+
end
|
data/lib/vmail/version.rb
CHANGED
@@ -7,7 +7,7 @@ describe Vmail::MessageFormatter do
|
|
7
7
|
before do
|
8
8
|
@raw = File.read(File.expand_path('../fixtures/google-affiliate.eml', __FILE__))
|
9
9
|
@mail = Mail.new(@raw)
|
10
|
-
@formatter = MessageFormatter.new(@mail)
|
10
|
+
@formatter = Vmail::MessageFormatter.new(@mail)
|
11
11
|
end
|
12
12
|
|
13
13
|
it "should extract name along with email address" do
|
@@ -22,7 +22,7 @@ describe Vmail::MessageFormatter do
|
|
22
22
|
before do
|
23
23
|
@raw = File.read(File.expand_path('../fixtures/textbody-nocontenttype.eml', __FILE__))
|
24
24
|
@mail = Mail.new(@raw)
|
25
|
-
@formatter = MessageFormatter.new(@mail)
|
25
|
+
@formatter = Vmail::MessageFormatter.new(@mail)
|
26
26
|
end
|
27
27
|
it "should return the text body" do
|
28
28
|
@formatter.process_body.wont_be_nil
|
@@ -34,7 +34,7 @@ describe Vmail::MessageFormatter do
|
|
34
34
|
before do
|
35
35
|
@raw = File.read(File.expand_path('../fixtures/htmlbody.eml', __FILE__))
|
36
36
|
@mail = Mail.new(@raw)
|
37
|
-
@formatter = MessageFormatter.new(@mail)
|
37
|
+
@formatter = Vmail::MessageFormatter.new(@mail)
|
38
38
|
end
|
39
39
|
it "should turn the body into readable text" do
|
40
40
|
@formatter.process_body.must_match %r{\n Web 1 new result for instantwatcher.com netflix}
|
@@ -45,7 +45,7 @@ describe Vmail::MessageFormatter do
|
|
45
45
|
before do
|
46
46
|
@raw = File.read(File.expand_path('../fixtures/euc-kr-header.eml', __FILE__))
|
47
47
|
@mail = Mail.new(@raw)
|
48
|
-
@formatter = MessageFormatter.new(@mail)
|
48
|
+
@formatter = Vmail::MessageFormatter.new(@mail)
|
49
49
|
end
|
50
50
|
it "should know its encoding" do
|
51
51
|
@mail.header.charset.must_equal 'euc-kr'
|
@@ -60,7 +60,7 @@ describe Vmail::MessageFormatter do
|
|
60
60
|
before do
|
61
61
|
@raw = File.read(File.expand_path('../fixtures/moleskine-html.eml', __FILE__))
|
62
62
|
@mail = Mail.new(@raw)
|
63
|
-
@formatter = MessageFormatter.new(@mail)
|
63
|
+
@formatter = Vmail::MessageFormatter.new(@mail)
|
64
64
|
end
|
65
65
|
it "should process body" do
|
66
66
|
@formatter.process_body.wont_be_nil
|
data/vmail.gemspec
CHANGED
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
8
|
+
- 7
|
9
|
+
version: 0.0.7
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Daniel Choi
|
@@ -30,6 +30,19 @@ dependencies:
|
|
30
30
|
version: "0"
|
31
31
|
type: :runtime
|
32
32
|
version_requirements: *id001
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: highline
|
35
|
+
prerelease: false
|
36
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
37
|
+
none: false
|
38
|
+
requirements:
|
39
|
+
- - ">="
|
40
|
+
- !ruby/object:Gem::Version
|
41
|
+
segments:
|
42
|
+
- 0
|
43
|
+
version: "0"
|
44
|
+
type: :runtime
|
45
|
+
version_requirements: *id002
|
33
46
|
description: Manage your email with Vim
|
34
47
|
email:
|
35
48
|
- dhchoi@gmail.com
|
@@ -48,11 +61,12 @@ files:
|
|
48
61
|
- bin/vmail
|
49
62
|
- bin/vmail_client
|
50
63
|
- gmail.vim
|
51
|
-
- lib/contacts_extractor.rb
|
52
64
|
- lib/vmail.rb
|
53
65
|
- lib/vmail.vim
|
66
|
+
- lib/vmail/contacts_extractor.rb
|
54
67
|
- lib/vmail/imap_client.rb
|
55
68
|
- lib/vmail/message_formatter.rb
|
69
|
+
- lib/vmail/options.rb
|
56
70
|
- lib/vmail/string_ext.rb
|
57
71
|
- lib/vmail/version.rb
|
58
72
|
- test/base64_test.rb
|