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 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
- " substract 2: because lines numbers start at 1 & messages start at line 2
89
- let s:current_message_index = line('.') - 2
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 - 2) . '..' . (a:lastline - 2)
247
- let nummsgs = (a:lastline - a:firstline + 1)
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 - 2) . '..' . (a:lastline - 2)
292
- let command = s:flag_command . uid_set . " +FLAGS " . a:flag
293
- let nummsgs = (a:lastline - a:firstline + 1)
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 " . (a:lastline - a:firstline + 1) . " messages"
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 - 2) . '..' . (a:lastline - 2)
315
- let command = s:move_to_command . uid_set . ' ' . "all"
316
- let nummsgs = (a:lastline - a:firstline + 1)
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 - 2) . '..' . (a:lastline - 2)
335
- let nummsgs = (a:lastline - a:firstline + 1)
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 s:uid_set = (a:firstline - 2) . '..' . (a:lastline - 2)
354
- let s:nummsgs = (a:lastline - a:firstline + 1)
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 command = s:more_messages_command
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
@@ -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
- @current_message_index = nil
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
- @current_message_index = nil
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(6)
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
- log "- search query got #{@ids.size} results"
340
- # this will hold all the data extracted from these message envelopes
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, start_id)
396
- log "add_more_message_line for start_id #{start_id}"
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 start_id.nil?
399
- remaining = start_id - 1
359
+ return res if start_seqno.nil?
360
+ remaining = start_seqno - 1
400
361
  else # filter search
401
- remaining = (@ids.index(start_id) || 1) - 1
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.\n" + res
369
+ "> Load #{[100, remaining].min} more messages. #{remaining} remaining. end seqno: #{start_seqno}\n" + res
409
370
  end
410
371
 
411
- def show_message(index, raw=false)
412
- log "show message: #{index}; current message list cache size: #{current_message_list_cache.size}"
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
- index = index.to_i
416
- if index == @current_message_index
375
+ uid = uid.to_i
376
+ if uid == @current_message_uid
417
377
  return @current_message
418
378
  end
419
379
 
420
- if index >= current_message_list_cache.size
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
- envelope_data = current_message_list_cache[index]
427
- if envelope_data.nil?
428
- log "missing envelope_data at index #{index}"
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(index)
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(index, raw)
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
- @current_message_index = index
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(index)
461
- envelope_data = current_message_list_cache[index]
462
- return unless envelope_data
463
- seqno = envelope_data[:seqno]
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
- # TODO keep these marked unread; test this
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} seqno:#{envelope_data[:seqno]} uid:#{uid} #{number_to_human_size size} #{format_parts_info(formatter.list_parts)}
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 index #{index} seqno #{seqno} uid #{uid}:\n#{$!}\n#{$!.backtrace.join("\n")}" +
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(index_range, action, flg)
519
- uid_set = uids_from_index_range(index_range)
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 index_range: #{index_range.inspect}; uid_set: #{uid_set.inspect}"
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 index_range: #{index_range.inspect}; uid_set: #{uid_set.inspect}"
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 index_range: #{index_range.inspect}; uid_set: #{uid_set.inspect}"
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(id_set, mailbox)
611
- log "move #{id_set.inspect} to #{mailbox}"
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(id_set, mailbox)
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, index_range_as_string)
657
- raise "expecting a range as string" unless index_range_as_string =~ /^\d+\.\.\d+$/
658
- index_range = eval(index_range_as_string)
659
- log "append to file range #{index_range.inspect} to file: #{file}"
660
- index_range.each do |idx|
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 #{index_range.to_a.size} message#{index_range.to_a.size == 1 ? '' : 's'} to #{file.strip}"
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
@@ -1,3 +1,3 @@
1
1
  module Vmail
2
- VERSION = "1.1.9"
2
+ VERSION = "1.2.0"
3
3
  end
@@ -56,3 +56,12 @@ code {
56
56
  font-size: 15px;
57
57
  }
58
58
 
59
+
60
+ .also-by {
61
+ font-size: 11px;
62
+ margin-top: 20px;
63
+ }
64
+
65
+ .also-by h4 {
66
+ font-size: 13px;
67
+ }
@@ -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
- - 1
8
- - 9
9
- version: 1.1.9
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: 2010-12-29 00:00:00 -05:00
17
+ date: 2011-01-06 00:00:00 -05:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency