vmail 1.1.9 → 1.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|