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 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