vmail 1.1.9 → 1.2.0
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/TODO +4 -0
- data/lib/vmail.vim +35 -24
- data/lib/vmail/imap_client.rb +64 -177
- data/lib/vmail/version.rb +1 -1
- data/website/stylesheets-vmail/site.css +9 -0
- data/website/vmail-template.html +12 -1
- metadata +4 -4
data/TODO
CHANGED
@@ -101,4 +101,8 @@ Sat Dec 25 09:07:28 EST 2010
|
|
101
101
|
- change vmail to take a file or files (e.g. globbed) as argument and
|
102
102
|
send them. This will let the user enter password on invocation.
|
103
103
|
|
104
|
+
------------------------------------------------------------------------
|
105
|
+
Thu Dec 30 11:28:05 EST 2010
|
106
|
+
|
107
|
+
1/6 deadline for doing How I Work Screencast
|
104
108
|
|
data/lib/vmail.vim
CHANGED
@@ -75,6 +75,11 @@ 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, '\d\+$')
|
79
|
+
if s:uid == ""
|
80
|
+
return
|
81
|
+
end
|
82
|
+
" mark as read
|
78
83
|
let newline = substitute(line, '^\V*+', '* ', '')
|
79
84
|
let newline = substitute(newline, '^\V+ ', ' ', '')
|
80
85
|
setlocal modifiable
|
@@ -85,13 +90,8 @@ function! s:show_message(stay_in_message_list)
|
|
85
90
|
" moving up when the next echo statement executes:
|
86
91
|
" call feedkeys(":\<cr>")
|
87
92
|
" redraw
|
88
|
-
|
89
|
-
|
90
|
-
if s:current_message_index < 0
|
91
|
-
return
|
92
|
-
endif
|
93
|
-
let command = s:show_message_command . s:current_message_index
|
94
|
-
echom "Loading message. Please wait..."
|
93
|
+
let command = s:show_message_command . s:uid
|
94
|
+
echom "Loading message ". s:uid .". Please wait..."
|
95
95
|
redrawstatus
|
96
96
|
let res = system(command)
|
97
97
|
call s:focus_message_window()
|
@@ -243,15 +243,15 @@ function! s:update()
|
|
243
243
|
endfunction
|
244
244
|
|
245
245
|
function! s:toggle_star() range
|
246
|
-
let uid_set = (a:firstline
|
247
|
-
let nummsgs = (
|
246
|
+
let uid_set = s:collect_uids(a:firstline, a:lastline)
|
247
|
+
let nummsgs = len(uid_set)
|
248
248
|
let flag_symbol = "^*"
|
249
249
|
" check if starred already
|
250
250
|
let action = " +FLAGS"
|
251
251
|
if (match(getline(a:firstline), flag_symbol) != -1)
|
252
252
|
let action = " -FLAGS"
|
253
253
|
endif
|
254
|
-
let command = s:flag_command . uid_set . action . " Flagged"
|
254
|
+
let command = s:flag_command . join(uid_set, ',') . action . " Flagged"
|
255
255
|
if nummsgs == 1
|
256
256
|
echom "toggling flag on message"
|
257
257
|
else
|
@@ -288,13 +288,13 @@ endfunction
|
|
288
288
|
|
289
289
|
" flag can be Deleted or [Gmail]/Spam
|
290
290
|
func! s:delete_messages(flag) range
|
291
|
-
let uid_set = (a:firstline
|
292
|
-
let
|
293
|
-
let
|
291
|
+
let uid_set = s:collect_uids(a:firstline, a:lastline)
|
292
|
+
let nummsgs = len(uid_set)
|
293
|
+
let command = s:flag_command . join(uid_set, ',') . " +FLAGS " . a:flag
|
294
294
|
if nummsgs == 1
|
295
295
|
echom "deleting message"
|
296
296
|
else
|
297
|
-
echom "deleting " .
|
297
|
+
echom "deleting " . nummsgs . " messages"
|
298
298
|
endif
|
299
299
|
let res = system(command)
|
300
300
|
setlocal modifiable
|
@@ -311,9 +311,9 @@ func! s:delete_messages(flag) range
|
|
311
311
|
endfunc
|
312
312
|
|
313
313
|
func! s:archive_messages() range
|
314
|
-
let uid_set = (a:firstline
|
315
|
-
let
|
316
|
-
let
|
314
|
+
let uid_set = s:collect_uids(a:firstline, a:lastline)
|
315
|
+
let nummsgs = len(uid_set)
|
316
|
+
let command = s:move_to_command . join(uid_set, ',') . ' ' . "all"
|
317
317
|
echo "archiving message" . (nummsgs == 1 ? '' : 's')
|
318
318
|
let res = system(command)
|
319
319
|
setlocal modifiable
|
@@ -331,15 +331,15 @@ endfunc
|
|
331
331
|
|
332
332
|
" append text bodies of a set of messages to a file
|
333
333
|
func! s:append_messages_to_file() range
|
334
|
-
let uid_set = (a:firstline
|
335
|
-
let nummsgs = (
|
334
|
+
let uid_set = s:collect_uids(a:firstline, a:lastline)
|
335
|
+
let nummsgs = len(uid_set)
|
336
336
|
let append_file = input("print messages to file: ", s:append_file)
|
337
337
|
if append_file == ''
|
338
338
|
echom "canceled"
|
339
339
|
return
|
340
340
|
endif
|
341
341
|
let s:append_file = append_file
|
342
|
-
let command = s:append_to_file_command . s:append_file . ' ' . uid_set
|
342
|
+
let command = s:append_to_file_command . s:append_file . ' ' . join(uid_set, ',')
|
343
343
|
echo "appending " . nummsgs . " message" . (nummsgs == 1 ? '' : 's') . " to " . s:append_file . ". please wait..."
|
344
344
|
let res = system(command)
|
345
345
|
echo res
|
@@ -350,8 +350,9 @@ endfunc
|
|
350
350
|
" move to another mailbox
|
351
351
|
function! s:move_to_mailbox(copy) range
|
352
352
|
let s:copy_to_mailbox = a:copy
|
353
|
-
let
|
354
|
-
let s:nummsgs = (
|
353
|
+
let uid_set = s:collect_uids(a:firstline, a:lastline)
|
354
|
+
let s:nummsgs = len(uid_set)
|
355
|
+
let s:uid_set = join(uid_set, ',')
|
355
356
|
" now prompt use to select mailbox
|
356
357
|
if !exists("s:mailboxes")
|
357
358
|
call s:get_mailbox_list()
|
@@ -551,7 +552,8 @@ endfunction
|
|
551
552
|
|
552
553
|
function! s:more_messages()
|
553
554
|
let line = getline(line('.'))
|
554
|
-
let
|
555
|
+
let seqno = get(split(matchstr(line, '\d\+:\d\+$'), ':'), 0)
|
556
|
+
let command = s:more_messages_command . seqno
|
555
557
|
echo "fetching more messages. please wait..."
|
556
558
|
let res = system(command)
|
557
559
|
setlocal modifiable
|
@@ -746,7 +748,16 @@ endfunc
|
|
746
748
|
" --------------------------------------------------------------------------------
|
747
749
|
" CONVENIENCE FUNCS
|
748
750
|
|
749
|
-
|
751
|
+
function! s:collect_uids(startline, endline)
|
752
|
+
let uid_set = []
|
753
|
+
let lnum = a:startline
|
754
|
+
while lnum <= a:endline
|
755
|
+
let uid = matchstr(getline(lnum), '\d\+$')
|
756
|
+
call add(uid_set, uid)
|
757
|
+
let lnum += 1
|
758
|
+
endwhile
|
759
|
+
return uid_set
|
760
|
+
endfunc
|
750
761
|
|
751
762
|
" --------------------------------------------------------------------------------
|
752
763
|
" MAPPINGS
|
data/lib/vmail/imap_client.rb
CHANGED
@@ -20,6 +20,8 @@ module Vmail
|
|
20
20
|
'trash' => '[Gmail]/Trash'
|
21
21
|
}
|
22
22
|
|
23
|
+
attr_accessor :max_seqno # of current mailbox
|
24
|
+
|
23
25
|
def initialize(config)
|
24
26
|
@username, @password = config['username'], config['password']
|
25
27
|
@name = config['name']
|
@@ -30,11 +32,10 @@ module Vmail
|
|
30
32
|
@imap_server = config['server'] || 'imap.gmail.com'
|
31
33
|
@imap_port = config['port'] || 993
|
32
34
|
@current_mail = nil
|
33
|
-
@
|
35
|
+
@current_message_uid = nil
|
34
36
|
end
|
35
37
|
|
36
38
|
# holds mail objects keyed by [mailbox, uid]
|
37
|
-
# TODO come up with a way to purge periodically
|
38
39
|
def message_cache
|
39
40
|
@message_cache ||= {}
|
40
41
|
size = @message_cache.values.reduce(0) {|sum, x| sum + x[:size]}
|
@@ -45,20 +46,6 @@ module Vmail
|
|
45
46
|
@message_cache
|
46
47
|
end
|
47
48
|
|
48
|
-
# keys are [mailbox, limit, query]
|
49
|
-
def message_list_cache
|
50
|
-
@message_list_cache ||= {}
|
51
|
-
end
|
52
|
-
|
53
|
-
def current_message_list_cache
|
54
|
-
# third key is the non id_set/range part of query
|
55
|
-
(message_list_cache[[@mailbox, @limit, @query[1..-1], @all_search]] ||= [])
|
56
|
-
end
|
57
|
-
|
58
|
-
def current_message_list_cache=(val)
|
59
|
-
message_list_cache[[@mailbox, @limit, @query[1..-1], @all_search]] ||= val
|
60
|
-
end
|
61
|
-
|
62
49
|
def open
|
63
50
|
@imap = Net::IMAP.new(@imap_server, @imap_port, true, nil, false)
|
64
51
|
log @imap.login(@username, @password)
|
@@ -101,7 +88,7 @@ module Vmail
|
|
101
88
|
def clear_cached_message
|
102
89
|
log "CLEARING CACHED MESSAGE"
|
103
90
|
@current_mail = nil
|
104
|
-
@
|
91
|
+
@current_message_uid = nil
|
105
92
|
@current_message = nil
|
106
93
|
end
|
107
94
|
|
@@ -137,7 +124,6 @@ module Vmail
|
|
137
124
|
# this is just to prime the IMAP connection
|
138
125
|
# It's necessary for some reason before update and deliver.
|
139
126
|
log "priming connection"
|
140
|
-
|
141
127
|
res = @imap.fetch(@ids[-1], ["ENVELOPE"])
|
142
128
|
if res.nil?
|
143
129
|
# just go ahead, just log
|
@@ -193,33 +179,11 @@ module Vmail
|
|
193
179
|
log(error) && raise(error)
|
194
180
|
end
|
195
181
|
log "- extracting headers"
|
196
|
-
log "- current message list cache has #{current_message_list_cache.size} items"
|
197
182
|
new_message_rows = results.map {|x| extract_row_data(x) }
|
198
|
-
if are_uids
|
199
|
-
# replace old row_text values
|
200
|
-
new_message_rows.each {|new_row_data|
|
201
|
-
current_message_list_cache.
|
202
|
-
select {|old_row_data| old_row_data[:uid] == new_row_data[:uid]}.
|
203
|
-
each {|old_row_data| old_row_data[:row_text] = new_row_data[:row_text]}
|
204
|
-
}
|
205
|
-
else
|
206
|
-
if is_update
|
207
|
-
log "- adding messages from update to end of list"
|
208
|
-
current_message_list_cache.concat new_message_rows
|
209
|
-
else
|
210
|
-
# this adds old messages to the top of the list
|
211
|
-
# put new rows before the current ones
|
212
|
-
log "- adding more messages to head of list"
|
213
|
-
self.current_message_list_cache.unshift(*new_message_rows)
|
214
|
-
end
|
215
|
-
end
|
216
|
-
log "- new current message list cache has #{current_message_list_cache.size} items"
|
217
|
-
# current_message_list_cache is automatically cached to keyed message_list_cache
|
218
183
|
log "- returning #{new_message_rows.size} new rows and caching result"
|
219
184
|
new_message_rows
|
220
185
|
end
|
221
186
|
|
222
|
-
|
223
187
|
# TODO extract this to another class or module and write unit tests
|
224
188
|
def extract_row_data(fetch_data)
|
225
189
|
seqno = fetch_data.seqno
|
@@ -262,11 +226,13 @@ module Vmail
|
|
262
226
|
mid_width = @width - 38
|
263
227
|
address_col_width = (mid_width * 0.3).ceil
|
264
228
|
subject_col_width = (mid_width * 0.7).floor
|
229
|
+
seqno_uid = [seqno.to_i, uid.to_i].join(':')
|
265
230
|
row_text = [ flags.col(2),
|
266
231
|
(date_formatted || '').col(14),
|
267
232
|
address.col(address_col_width),
|
268
233
|
subject.col(subject_col_width),
|
269
|
-
number_to_human_size(size).rcol(
|
234
|
+
number_to_human_size(size).rcol(7),
|
235
|
+
seqno_uid.to_s
|
270
236
|
].join(' | ')
|
271
237
|
{:uid => uid, :seqno => seqno, :row_text => row_text}
|
272
238
|
rescue
|
@@ -317,13 +283,6 @@ module Vmail
|
|
317
283
|
@query = query.map {|x| x.to_s.downcase}
|
318
284
|
@limit = limit
|
319
285
|
log "search query: #{@query.inspect}"
|
320
|
-
if !current_message_list_cache.empty?
|
321
|
-
log "- CACHE HIT"
|
322
|
-
res = current_message_list_cache.map {|x| x[:row_text]}.join("\n")
|
323
|
-
@ids = current_message_list_cache.map {|x| x[:seqno]}
|
324
|
-
return add_more_message_line(res, current_message_list_cache[0][:seqno])
|
325
|
-
end
|
326
|
-
log "- CACHE MISS"
|
327
286
|
log "- @all_search #{@all_search}"
|
328
287
|
@query = query
|
329
288
|
@ids = reconnect_if_necessary(180) do # increase timeout to 3 minutes
|
@@ -336,14 +295,18 @@ module Vmail
|
|
336
295
|
@start_index = [@ids.length - limit, 0].max
|
337
296
|
@ids[@start_index..-1]
|
338
297
|
end
|
339
|
-
|
340
|
-
|
341
|
-
current_message_list_cache = []
|
298
|
+
self.max_seqno = @ids[-1]
|
299
|
+
log "- search query got #{@ids.size} results; max seqno: #{self.max_seqno}"
|
342
300
|
clear_cached_message
|
343
301
|
res = fetch_row_text(fetch_ids)
|
344
302
|
add_more_message_line(res, fetch_ids[0])
|
345
303
|
end
|
346
304
|
|
305
|
+
def decrement_max_seqno(num)
|
306
|
+
log "Decremented max seqno from #{self.max_seqno} to #{self.max_seqno - num}"
|
307
|
+
self.max_seqno -= num
|
308
|
+
end
|
309
|
+
|
347
310
|
def update
|
348
311
|
prime_connection
|
349
312
|
old_num_messages = @num_messages
|
@@ -357,11 +320,9 @@ module Vmail
|
|
357
320
|
log "search #update_query"
|
358
321
|
@imap.search(update_query.join(' '))
|
359
322
|
}
|
360
|
-
# TODO change this. will throw error now
|
361
|
-
max_seqno = current_message_list_cache[-1][:seqno]
|
362
323
|
log "- got seqnos: #{ids.inspect}"
|
363
|
-
log "- getting seqnos > #{max_seqno}"
|
364
|
-
new_ids = ids.select {|seqno| seqno > max_seqno}
|
324
|
+
log "- getting seqnos > #{self.max_seqno}"
|
325
|
+
new_ids = ids.select {|seqno| seqno > self.max_seqno}
|
365
326
|
@ids = @ids + new_ids
|
366
327
|
log "- update: new uids: #{new_ids.inspect}"
|
367
328
|
if !new_ids.empty?
|
@@ -373,13 +334,13 @@ module Vmail
|
|
373
334
|
end
|
374
335
|
|
375
336
|
# gets 100 messages prior to id
|
376
|
-
def more_messages(limit=100)
|
377
|
-
message_id = current_message_list_cache[0][:seqno]
|
337
|
+
def more_messages(message_id, limit=100)
|
378
338
|
log "more_messages: message_id #{message_id}"
|
379
339
|
message_id = message_id.to_i
|
380
340
|
if @all_search
|
381
341
|
x = [(message_id - limit), 0].max
|
382
342
|
y = [message_id - 1, 0].max
|
343
|
+
|
383
344
|
res = fetch_row_text((x..y))
|
384
345
|
add_more_message_line(res, x)
|
385
346
|
else # filter search query
|
@@ -392,63 +353,53 @@ module Vmail
|
|
392
353
|
end
|
393
354
|
end
|
394
355
|
|
395
|
-
def add_more_message_line(res,
|
396
|
-
log "add_more_message_line for
|
356
|
+
def add_more_message_line(res, start_seqno)
|
357
|
+
log "add_more_message_line for start_seqno #{start_seqno}"
|
397
358
|
if @all_search
|
398
|
-
return res if
|
399
|
-
remaining =
|
359
|
+
return res if start_seqno.nil?
|
360
|
+
remaining = start_seqno - 1
|
400
361
|
else # filter search
|
401
|
-
remaining = (@ids.index(
|
362
|
+
remaining = (@ids.index(start_seqno) || 1) - 1
|
402
363
|
end
|
403
364
|
if remaining < 1
|
404
365
|
log "none remaining"
|
405
366
|
return "showing all matches\n" + res
|
406
367
|
end
|
407
368
|
log "remaining messages: #{remaining}"
|
408
|
-
"> Load #{[100, remaining].min} more messages. #{remaining} remaining
|
369
|
+
"> Load #{[100, remaining].min} more messages. #{remaining} remaining. end seqno: #{start_seqno}\n" + res
|
409
370
|
end
|
410
371
|
|
411
|
-
def show_message(
|
412
|
-
log "show message: #{
|
413
|
-
return if index.to_i < 0
|
372
|
+
def show_message(uid, raw=false)
|
373
|
+
log "show message: #{uid}"
|
414
374
|
return @current_mail.to_s if raw
|
415
|
-
|
416
|
-
if
|
375
|
+
uid = uid.to_i
|
376
|
+
if uid == @current_message_uid
|
417
377
|
return @current_message
|
418
378
|
end
|
419
379
|
|
420
|
-
|
421
|
-
index -= 1
|
422
|
-
log "index beyond bounds, setting index to #{index}"
|
423
|
-
end
|
424
|
-
prefetch_adjacent(index) # TODO mark these as unread
|
380
|
+
#prefetch_adjacent(index) # deprecated
|
425
381
|
|
426
|
-
|
427
|
-
|
428
|
-
|
429
|
-
end
|
430
|
-
# TODO factor this gsubbing stuff out into own function
|
431
|
-
envelope_data[:row_text] = envelope_data[:row_text].gsub(/^\+ /, ' ').gsub(/^\*\+/, '* ') # mark as read in cache
|
432
|
-
seqno = envelope_data[:seqno]
|
433
|
-
uid = envelope_data[:uid]
|
434
|
-
log "showing message index: #{index} seqno: #{seqno} uid #{uid}"
|
382
|
+
# TODO keep state in vim buffers, instead of on Vmail Ruby client
|
383
|
+
# envelope_data[:row_text] = envelope_data[:row_text].gsub(/^\+ /, ' ').gsub(/^\*\+/, '* ') # mark as read in cache
|
384
|
+
#seqno = envelope_data[:seqno]
|
435
385
|
|
386
|
+
log "showing message uid: #{uid}"
|
436
387
|
data = if x = message_cache[[@mailbox, uid]]
|
437
388
|
log "- message cache hit"
|
438
389
|
x
|
439
390
|
else
|
440
391
|
log "- fetching and storing to message_cache[[#{@mailbox}, #{uid}]]"
|
441
|
-
fetch_and_cache(
|
392
|
+
fetch_and_cache(uid)
|
442
393
|
end
|
443
394
|
if data.nil?
|
444
395
|
# retry, though this is a hack!
|
445
396
|
log "- data is nil. retrying..."
|
446
|
-
return show_message(
|
397
|
+
return show_message(uid, raw)
|
447
398
|
end
|
448
399
|
# make this more DRY later by directly using a ref to the hash
|
449
400
|
mail = data[:mail]
|
450
401
|
size = data[:size]
|
451
|
-
@
|
402
|
+
@current_message_uid = uid
|
452
403
|
log "- setting @current_mail"
|
453
404
|
@current_mail = mail # used later to show raw message or extract attachments if any
|
454
405
|
@current_message = data[:message_text]
|
@@ -457,14 +408,11 @@ module Vmail
|
|
457
408
|
"Error encountered parsing this message:\n#{$!}\n#{$!.backtrace.join("\n")}"
|
458
409
|
end
|
459
410
|
|
460
|
-
def fetch_and_cache(
|
461
|
-
|
462
|
-
|
463
|
-
|
464
|
-
uid = envelope_data[:uid]
|
465
|
-
return if message_cache[[@mailbox, uid]]
|
411
|
+
def fetch_and_cache(uid)
|
412
|
+
if data = message_cache[[@mailbox, uid]]
|
413
|
+
return data
|
414
|
+
end
|
466
415
|
fetch_data = reconnect_if_necessary do
|
467
|
-
# log "@imap.uid_fetch #{uid}"
|
468
416
|
res = @imap.uid_fetch(uid, ["FLAGS", "RFC822", "RFC822.SIZE"])
|
469
417
|
if res.nil?
|
470
418
|
# retry one more time ( find a more elegant way to do this )
|
@@ -472,32 +420,29 @@ module Vmail
|
|
472
420
|
end
|
473
421
|
res[0]
|
474
422
|
end
|
475
|
-
#
|
476
|
-
if envelope_data[:row_text] =~ /^\*?\+/ # not seen
|
477
|
-
log "reflagging index #{index} uid #{uid} as not seen"
|
478
|
-
flag("#{index}..#{index}", '-FLAGS', :Seen) # change flag() method to accept a single index later
|
479
|
-
end
|
423
|
+
# USE THIS
|
480
424
|
size = fetch_data.attr["RFC822.SIZE"]
|
481
425
|
mail = Mail.new(fetch_data.attr['RFC822'])
|
482
426
|
formatter = Vmail::MessageFormatter.new(mail)
|
483
427
|
message_text = <<-EOF
|
484
|
-
#{@mailbox}
|
428
|
+
#{@mailbox} uid:#{uid} #{number_to_human_size size} #{format_parts_info(formatter.list_parts)}
|
485
429
|
#{divider '-'}
|
486
430
|
#{format_headers(formatter.extract_headers)}
|
487
431
|
|
488
432
|
#{formatter.process_body}
|
489
433
|
EOF
|
490
434
|
# log "storing message_cache[[#{@mailbox}, #{uid}]]"
|
491
|
-
d = {:mail => mail, :size => size, :message_text => message_text}
|
435
|
+
d = {:mail => mail, :size => size, :message_text => message_text, :seqno => fetch_data.seqno}
|
492
436
|
message_cache[[@mailbox, uid]] = d
|
493
437
|
rescue
|
494
|
-
msg = "Error encountered parsing message
|
438
|
+
msg = "Error encountered parsing message uid #{uid}:\n#{$!}\n#{$!.backtrace.join("\n")}" +
|
495
439
|
"\n\nRaw message:\n\n" + mail.to_s
|
496
440
|
log msg
|
497
441
|
log message_text
|
498
442
|
{:message_text => msg}
|
499
443
|
end
|
500
444
|
|
445
|
+
# deprecated
|
501
446
|
def prefetch_adjacent(index)
|
502
447
|
Thread.new do
|
503
448
|
[index + 1, index - 1].each do |idx|
|
@@ -515,11 +460,12 @@ EOF
|
|
515
460
|
|
516
461
|
# id_set is a string comming from the vim client
|
517
462
|
# action is -FLAGS or +FLAGS
|
518
|
-
def flag(
|
519
|
-
uid_set =
|
463
|
+
def flag(uid_set, action, flg)
|
464
|
+
uid_set = uid_set.split(',').map(&:to_i)
|
520
465
|
log "flag #{uid_set.inspect} #{flg} #{action}"
|
521
466
|
if flg == 'Deleted'
|
522
|
-
log "Deleting
|
467
|
+
log "Deleting uid_set: #{uid_set.inspect}"
|
468
|
+
decrement_max_seqno(uid_set.size)
|
523
469
|
# for delete, do in a separate thread because deletions are slow
|
524
470
|
Thread.new do
|
525
471
|
unless @mailbox == '[Gmail]/Trash'
|
@@ -528,87 +474,34 @@ EOF
|
|
528
474
|
end
|
529
475
|
log "@imap.uid_store #{uid_set.inspect} #{action} [#{flg.to_sym}]"
|
530
476
|
log @imap.uid_store(uid_set, action, [flg.to_sym])
|
531
|
-
remove_uid_set_from_cached_lists(uid_set)
|
532
477
|
reload_mailbox
|
533
478
|
clear_cached_message
|
534
479
|
end
|
535
480
|
elsif flg == '[Gmail]/Spam'
|
536
|
-
log "Marking as spam
|
481
|
+
log "Marking as spam uid_set: #{uid_set.inspect}"
|
482
|
+
decrement_max_seqno(uid_set.size)
|
537
483
|
Thread.new do
|
538
484
|
log "@imap.uid_copy #{uid_set.inspect} to spam"
|
539
485
|
log @imap.uid_copy(uid_set, "[Gmail]/Spam")
|
540
486
|
log "@imap.uid_store #{uid_set.inspect} #{action} [:Deleted]"
|
541
487
|
log @imap.uid_store(uid_set, action, [:Deleted])
|
542
|
-
remove_uid_set_from_cached_lists(uid_set)
|
543
488
|
reload_mailbox
|
544
489
|
clear_cached_message
|
545
490
|
end
|
546
491
|
"#{id} deleted"
|
547
492
|
else
|
548
|
-
log "Flagging
|
493
|
+
log "Flagging uid_set: #{uid_set.inspect}"
|
549
494
|
Thread.new do
|
550
495
|
log "@imap.uid_store #{uid_set.inspect} #{action} [#{flg.to_sym}]"
|
551
496
|
log @imap.uid_store(uid_set, action, [flg.to_sym])
|
552
497
|
end
|
553
|
-
|
554
|
-
# mark cached versions of the rows with flag/unflag
|
555
|
-
uid_set.each do |uid|
|
556
|
-
envelope_data = current_message_list_cache.detect {|x| x[:uid] == uid}
|
557
|
-
if action == '+FLAGS' && flg == 'Flagged'
|
558
|
-
envelope_data[:row_text] = envelope_data[:row_text].gsub(/^\+ /, '*+').gsub(/^ /, '* ') # mark as read in cache
|
559
|
-
elsif action == '-FLAGS' && flg == 'Flagged'
|
560
|
-
envelope_data[:row_text] = envelope_data[:row_text].gsub(/^\*\+/, '+ ').gsub(/^\* /, ' ') # mark as read in cache
|
561
|
-
end
|
562
|
-
end
|
563
|
-
|
564
|
-
end
|
565
|
-
end
|
566
|
-
|
567
|
-
def uids_from_index_range(index_range_as_string)
|
568
|
-
raise "expecting String" unless index_range_as_string.is_a?(String)
|
569
|
-
raise "expecting a range as string" unless index_range_as_string =~ /^\d+\.\.\d+$/
|
570
|
-
log "converting index_range #{index_range_as_string} to uids"
|
571
|
-
uids = current_message_list_cache[eval(index_range_as_string)].map {|row| row[:uid]}
|
572
|
-
log "converted index_range #{index_range_as_string} to uids #{uids.inspect}"
|
573
|
-
uids
|
574
|
-
end
|
575
|
-
|
576
|
-
def remove_uid_set_from_cached_lists(uid_set)
|
577
|
-
# delete from cached @ids and current_message_list_cache
|
578
|
-
seqnos_to_delete = []
|
579
|
-
uid_set.each {|uid|
|
580
|
-
row = current_message_list_cache.detect {|row| row[:uid] == uid}
|
581
|
-
seqno = row[:seqno]
|
582
|
-
log "deleting seqno #{seqno} from @ids"
|
583
|
-
@ids.delete seqno
|
584
|
-
seqnos_to_delete << seqno
|
585
|
-
}
|
586
|
-
log "seqnos_to_delete: #{seqnos_to_delete.inspect}"
|
587
|
-
seqnos_to_delete.reverse.each do |seqno|
|
588
|
-
startsize = current_message_list_cache.size
|
589
|
-
log "deleting row with seqno #{seqno}"
|
590
|
-
current_message_list_cache.delete_if {|x| x[:seqno] == seqno}
|
591
|
-
endsize = current_message_list_cache.size
|
592
|
-
log "deleted #{startsize - endsize} rows"
|
593
|
-
end
|
594
|
-
# now we need to decrement all the higher sequence numbers!
|
595
|
-
basenum = seqnos_to_delete.min # this is the lowested seqno deleted
|
596
|
-
diff = seqnos_to_delete.size # substract this from all seqnos >= basenum
|
597
|
-
changes = []
|
598
|
-
current_message_list_cache.each do |row|
|
599
|
-
if row[:seqno] >= basenum
|
600
|
-
changes << "#{row[:seqno]}->#{row[:seqno] - diff}"
|
601
|
-
row[:seqno] -= diff
|
602
|
-
end
|
603
498
|
end
|
604
|
-
log "seqno decremented: #{changes.join(";")}"
|
605
|
-
rescue
|
606
|
-
log "error removing uid set from cached lists"
|
607
|
-
log $!
|
608
499
|
end
|
609
500
|
|
610
|
-
def move_to(
|
611
|
-
|
501
|
+
def move_to(uid_set, mailbox)
|
502
|
+
uid_set = uid_set.split(',').map(&:to_i)
|
503
|
+
decrement_max_seqno(uid_set.size)
|
504
|
+
log "move #{uid_set.inspect} to #{mailbox}"
|
612
505
|
if mailbox == 'all'
|
613
506
|
log "archiving messages"
|
614
507
|
end
|
@@ -616,25 +509,22 @@ EOF
|
|
616
509
|
mailbox = MailboxAliases[mailbox]
|
617
510
|
end
|
618
511
|
create_if_necessary mailbox
|
619
|
-
log "getting uids form index range #{id_set}"
|
620
|
-
uid_set = uids_from_index_range(id_set)
|
621
512
|
log "moving uid_set: #{uid_set.inspect} to #{mailbox}"
|
622
513
|
Thread.new do
|
623
514
|
log @imap.uid_copy(uid_set, mailbox)
|
624
515
|
log @imap.uid_store(uid_set, '+FLAGS', [:Deleted])
|
625
|
-
remove_uid_set_from_cached_lists(uid_set)
|
626
516
|
reload_mailbox
|
627
517
|
clear_cached_message
|
628
518
|
log "moved uid_set #{uid_set.inspect} to #{mailbox}"
|
629
519
|
end
|
630
520
|
end
|
631
521
|
|
632
|
-
def copy_to(
|
522
|
+
def copy_to(uid_set, mailbox)
|
523
|
+
uid_set = uid_set.split(',').map(&:to_i)
|
633
524
|
if MailboxAliases[mailbox]
|
634
525
|
mailbox = MailboxAliases[mailbox]
|
635
526
|
end
|
636
527
|
create_if_necessary mailbox
|
637
|
-
uid_set = uids_from_index_range(id_set)
|
638
528
|
log "copying #{uid_set.inspect} to #{mailbox}"
|
639
529
|
Thread.new do
|
640
530
|
log @imap.uid_copy(uid_set, mailbox)
|
@@ -653,20 +543,18 @@ EOF
|
|
653
543
|
end
|
654
544
|
end
|
655
545
|
|
656
|
-
def append_to_file(file,
|
657
|
-
|
658
|
-
|
659
|
-
|
660
|
-
|
661
|
-
message = show_message(idx)
|
546
|
+
def append_to_file(file, uid_set)
|
547
|
+
uid_set = uid_set.split(',').map(&:to_i)
|
548
|
+
log "append to file uid set #{uid_set.inspect} to file: #{file}"
|
549
|
+
uid_set.each do |uid|
|
550
|
+
message = show_message(uid)
|
662
551
|
File.open(file, 'a') {|f| f.puts(divider('=') + "\n" + message + "\n\n")}
|
663
552
|
subject = (message[/^subject:(.*)/,1] || '').strip
|
664
553
|
log "appended message '#{subject}'"
|
665
554
|
end
|
666
|
-
"printed #{
|
555
|
+
"printed #{uid_set.size} message#{uid_set.size == 1 ? '' : 's'} to #{file.strip}"
|
667
556
|
end
|
668
557
|
|
669
|
-
|
670
558
|
def new_message_template(subject = nil, append_signature = true)
|
671
559
|
headers = {'from' => "#{@name} <#{@username}>",
|
672
560
|
'to' => nil,
|
@@ -758,7 +646,6 @@ EOF
|
|
758
646
|
# attachments are added as a snippet of YAML after a blank line
|
759
647
|
# after the headers, and followed by a blank line
|
760
648
|
if (attachments = raw_body.split(/\n\s*\n/, 2)[0]) =~ /^attach(ment|ments)*:/
|
761
|
-
# TODO
|
762
649
|
files = YAML::load(attachments).values.flatten
|
763
650
|
log "attach: #{files}"
|
764
651
|
files.each do |file|
|
data/lib/vmail/version.rb
CHANGED
data/website/vmail-template.html
CHANGED
@@ -38,12 +38,23 @@
|
|
38
38
|
<li><a href="https://github.com/danchoi/vmail/issues">issue tracker</a></li>
|
39
39
|
<li><a href="https://github.com/danchoi/vmail/commits/master">commit history</a></li>
|
40
40
|
<li><a href="https://github.com/danchoi/vmail/wiki">wiki</a></li>
|
41
|
-
<li><a href="http://twitter.com/#!/search/vmail%20gmail">tweets about</a></li>
|
42
41
|
</ul>
|
42
|
+
|
43
43
|
<h4>share this</h4>
|
44
44
|
|
45
45
|
<span class="st_twitter_large" displayText="Tweet"></span><span class="st_facebook_large" displayText="Facebook"></span><span class="st_ybuzz_large" displayText="Yahoo! Buzz"></span><span class="st_gbuzz_large" displayText="Google Buzz"></span><span class="st_email_large" displayText="Email"></span><span class="st_sharethis_large" displayText="ShareThis"></span>
|
46
46
|
|
47
|
+
|
48
|
+
<div class="also-by">
|
49
|
+
<h4>also by this developer</h4>
|
50
|
+
<ul class="cross-promo">
|
51
|
+
<li><a href="http://instantwatcher.com">instantwatcher.com<a/></li>
|
52
|
+
<li><a href="http://openmbta.org">OpenMBTA<a/></li>
|
53
|
+
<li><a href="http://kindlefeeder.com">kindlefeeder.com<a/></li>
|
54
|
+
</ul>
|
55
|
+
</div>
|
56
|
+
|
57
|
+
|
47
58
|
</div>
|
48
59
|
|
49
60
|
</div>
|
metadata
CHANGED
@@ -4,9 +4,9 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 1
|
7
|
-
-
|
8
|
-
-
|
9
|
-
version: 1.
|
7
|
+
- 2
|
8
|
+
- 0
|
9
|
+
version: 1.2.0
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Daniel Choi
|
@@ -14,7 +14,7 @@ autorequire:
|
|
14
14
|
bindir: bin
|
15
15
|
cert_chain: []
|
16
16
|
|
17
|
-
date:
|
17
|
+
date: 2011-01-06 00:00:00 -05:00
|
18
18
|
default_executable:
|
19
19
|
dependencies:
|
20
20
|
- !ruby/object:Gem::Dependency
|