vmail 0.3.8 → 0.3.9

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/bin/vmail CHANGED
File without changes
File without changes
@@ -453,6 +453,7 @@ function! s:select_mailbox()
453
453
  return
454
454
  endif
455
455
  let s:mailbox = mailbox
456
+ let s:query = "100 all"
456
457
  let command = s:select_mailbox_command . shellescape(s:mailbox)
457
458
  call system(command)
458
459
  redraw
@@ -690,9 +691,9 @@ func! s:message_window_mappings()
690
691
  nnoremap <silent> <buffer> q :close<cr>
691
692
 
692
693
  nnoremap <silent> <buffer> <leader># :close<cr>:call <SID>focus_list_window()<cr>:call <SID>delete_messages("Deleted")<cr>
693
- nmap <silent> <buffer> <leader>d <leader>#
694
694
  nnoremap <silent> <buffer> <leader>* :call <SID>focus_list_window()<cr>:call <SID>toggle_star()<cr>
695
- nmap <silent> <buffer> s <leader>*
695
+ noremap <silent> <buffer> <leader>! :call <SID>focus_list_window()<cr>:call <SID>delete_messages("[Gmail]/Spam")<CR>
696
+ noremap <silent> <buffer> <leader>e :call <SID>focus_list_window()<cr>:call <SID>archive_messages()<CR>
696
697
 
697
698
  nnoremap <silent> <buffer> <Leader>b :call <SID>focus_list_window()<cr>call <SID>move_to_mailbox(0)<CR>
698
699
  nnoremap <silent> <buffer> <Leader>B :call <SID>focus_list_window()<cr>call <SID>move_to_mailbox(1)<CR>
@@ -27,7 +27,7 @@ module Vmail
27
27
  @logger = Logger.new(config['logfile'] || STDERR)
28
28
  @logger.level = Logger::DEBUG
29
29
  @current_mail = nil
30
- @current_uid = nil
30
+ @current_id = nil
31
31
  end
32
32
 
33
33
  def open
@@ -52,9 +52,14 @@ module Vmail
52
52
  reconnect_if_necessary do
53
53
  log @imap.select(mailbox)
54
54
  end
55
+ @status = @imap.status(mailbox, ["MESSAGES", "RECENT", "UNSEEN"])
56
+ log "STATUS: #{@status.inspect}"
57
+ # get highest message ID
58
+ res = @imap.fetch([1,"*"], ["ENVELOPE"])
59
+ @num_messages = res[-1].seqno
60
+ log "HIGHEST ID: #@num_messages"
55
61
  @mailbox = mailbox
56
- @all_uids = []
57
- @bad_uids = []
62
+ @bad_ids = []
58
63
  return "OK"
59
64
  end
60
65
 
@@ -83,20 +88,19 @@ module Vmail
83
88
  @mailboxes
84
89
  end
85
90
 
86
- def fetch_headers(uid_set)
87
- if uid_set.is_a?(String)
88
- uid_set = uid_set.split(",").map(&:to_i)
89
- elsif uid_set.is_a?(Integer)
90
- uid_set = [uid_set]
91
+ # id_set may be a range, array, or string
92
+ def fetch_envelopes(id_set)
93
+ if id_set.is_a?(String)
94
+ id_set = id_set.split(',')
91
95
  end
92
- max_uid = uid_set.max
93
- log "fetch headers for #{uid_set.inspect}"
94
- if uid_set.empty?
96
+ max_id = id_set.to_a[-1]
97
+ log "fetch envelopes for #{id_set.inspect}"
98
+ if id_set.to_a.empty?
95
99
  log "empty set"
96
100
  return ""
97
101
  end
98
102
  results = reconnect_if_necessary do
99
- @imap.uid_fetch(uid_set, ["FLAGS", "ENVELOPE", "RFC822.SIZE" ])
103
+ @imap.fetch(id_set, ["FLAGS", "ENVELOPE", "RFC822.SIZE" ])
100
104
  end
101
105
  log "extracting headers"
102
106
  lines = results.
@@ -107,13 +111,13 @@ module Vmail
107
111
  Time.now
108
112
  end
109
113
  }.
110
- map {|x| format_header(x, max_uid)}
114
+ map {|x| format_list_row(x, max_id)}
111
115
  log "returning result"
112
116
  return lines.join("\n")
113
117
  end
114
118
 
115
- def format_header(fetch_data, max_uid=nil)
116
- uid = fetch_data.attr["UID"]
119
+ def format_list_row(fetch_data, max_id=nil)
120
+ id = fetch_data.seqno
117
121
  envelope = fetch_data.attr["ENVELOPE"]
118
122
  size = fetch_data.attr["RFC822.SIZE"]
119
123
  flags = fetch_data.attr["FLAGS"]
@@ -148,18 +152,18 @@ module Vmail
148
152
  subject = envelope.subject || ''
149
153
  subject = Mail::Encodings.unquote_and_convert_to(subject, 'UTF-8')
150
154
  flags = format_flags(flags)
151
- first_col_width = max_uid.to_s.length
155
+ first_col_width = max_id.to_s.length
152
156
  mid_width = @width - (first_col_width + 33)
153
157
  address_col_width = (mid_width * 0.3).ceil
154
158
  subject_col_width = (mid_width * 0.7).floor
155
- [uid.to_s.col(first_col_width),
159
+ [id.to_s.col(first_col_width),
156
160
  (date_formatted || '').col(14),
157
161
  address.col(address_col_width),
158
162
  subject.col(subject_col_width),
159
163
  number_to_human_size(size).rcol(6),
160
164
  flags.rcol(7)].join(' ')
161
165
  rescue
162
- "#{uid.to_s} : error extracting this header"
166
+ "#{id.to_s} : error extracting this header"
163
167
  end
164
168
 
165
169
  UNITS = [:b, :kb, :mb, :gb].freeze
@@ -189,17 +193,36 @@ module Vmail
189
193
  end
190
194
 
191
195
  def search(limit, *query)
192
- log "uid_search limit: #{limit} query: #{@query.inspect}"
193
- limit = 25 if limit.to_s !~ /^\d+$/
196
+ limit = limit.to_i
197
+ limit = 100 if limit.to_s !~ /^\d+$/
194
198
  query = ['ALL'] if query.empty?
195
- @query = query.join(' ')
196
- log "uid_search #@query #{limit}"
197
- @all_uids = reconnect_if_necessary do
198
- @imap.uid_search(@query)
199
+ if query.size == 1 && query[0].downcase == 'all'
200
+ # form a sequence range
201
+ query.unshift [[@num_messages - limit.to_i + 1 , 1].max, @num_messages].join(':')
202
+ @all_search = true
203
+ else
204
+ # this is a special query search
205
+ # set the target range to the whole set
206
+ query.unshift "1:#@num_messages"
207
+ @all_search = false
199
208
  end
200
- uids = @all_uids[-([limit.to_i, @all_uids.size].min)..-1] || []
201
- res = fetch_headers(uids)
202
- add_more_message_line(res, uids)
209
+ @query = query.join(' ')
210
+ log "search query: #@query"
211
+ ids = reconnect_if_necessary do
212
+ @imap.search(@query)
213
+ end
214
+ fetch_ids = if ids.size > limit
215
+ log "truncating returned set to #{limit}"
216
+ @start_index = ids.index(ids[-1]) - limit
217
+ # save ids in @ids
218
+ @ids = ids
219
+ ids[@start_index..ids[-1]]
220
+ else
221
+ ids
222
+ end
223
+ log "search query result: #{fetch_ids.inspect}"
224
+ res = fetch_envelopes(fetch_ids)
225
+ add_more_message_line(res, fetch_ids[0])
203
226
  end
204
227
 
205
228
  def update
@@ -207,65 +230,80 @@ module Vmail
207
230
  # this is just to prime the IMAP connection
208
231
  # It's necessary for some reason.
209
232
  log "priming connection for update"
210
- res = @imap.uid_fetch(@all_uids[-1], ["ENVELOPE"])
233
+ res = @imap.fetch(@all_ids[-1], ["ENVELOPE"])
211
234
  if res.nil?
212
235
  raise IOError, "IMAP connection seems broken"
213
236
  end
214
237
  end
215
- uids = reconnect_if_necessary {
216
- log "uid_search #@query"
217
- @imap.uid_search(@query)
238
+ ids = reconnect_if_necessary {
239
+ log "search #@query"
240
+ @imap.search(@query)
218
241
  }
219
- new_uids = uids - @all_uids
220
- log "UPDATE: NEW UIDS: #{new_uids.inspect}"
221
- if !new_uids.empty?
222
- res = fetch_headers(new_uids)
223
- @all_uids = uids
242
+ # TODO change this. will throw error now
243
+ new_ids = ids - @all_ids
244
+ log "UPDATE: NEW UIDS: #{new_ids.inspect}"
245
+ if !new_ids.empty?
246
+ res = fetch_envelopes(new_ids)
247
+ @all_ids = ids
224
248
  res
225
249
  end
226
250
  end
227
251
 
228
- # gets 100 messages prior to uid
229
- def more_messages(uid, limit=100)
230
- uid = uid.to_i
231
- x = [(@all_uids.index(uid) - limit), 0].max
232
- y = [@all_uids.index(uid) - 1, 0].max
233
- uids = @all_uids[x..y]
234
- res = fetch_headers(uids)
235
- add_more_message_line(res, uids)
252
+ # gets 100 messages prior to id
253
+ def more_messages(message_id, limit=100)
254
+ message_id = message_id.to_i
255
+ if @all_search
256
+ x = [(message_id - limit), 0].max
257
+ y = [message_id - 1, 0].max
258
+ res = fetch_envelopes((x..y))
259
+ add_more_message_line(res, x)
260
+ else
261
+ # filter search query
262
+ x = [(@start_index - limit), 0].max
263
+ y = [@start_index - 1, 0].max
264
+ @start_index = x
265
+ res = fetch_envelopes(@ids[x..y])
266
+ add_more_message_line(res, @ids[x])
267
+ end
236
268
  end
237
269
 
238
- def add_more_message_line(res, uids)
239
- return res if uids.empty?
240
- start_index = @all_uids.index(uids[0])
241
- if start_index > 0
242
- remaining = start_index
243
- res = "> Load #{[100, remaining].min} more messages. #{remaining} remaining.\n" + res
270
+ def add_more_message_line(res, start_id)
271
+ if @all_search
272
+ return res if start_id.nil?
273
+ if start_id <= 1
274
+ return res
275
+ end
276
+ log "remaining = start_id - 1: #{start_id} - 1"
277
+ remaining = start_id - 1
278
+ else # filter search
279
+ log "remaining = @ids.index(#{start_id}) - 1; @ids.size: #{@ids.size}"
280
+ remaining = @ids.index(start_id) - 1
244
281
  end
245
- res
282
+ log "remaining messages: #{remaining}"
283
+ "> Load #{[100, remaining].min} more messages. #{remaining} remaining.\n" + res
246
284
  end
247
285
 
248
- def show_message(uid, raw=false, forwarded=false)
249
- uid = uid.to_i
286
+ def show_message(id, raw=false, forwarded=false)
287
+ id = id.to_i
250
288
  if forwarded
251
289
  return @current_message.split(/\n-{20,}\n/, 2)[1]
252
290
  end
253
291
  return @current_mail.to_s if raw
254
- return @current_message if uid == @current_uid
255
- log "fetching #{uid.inspect}"
292
+ return @current_message if id == @current_id
293
+ log "fetching #{id.inspect}"
256
294
  fetch_data = reconnect_if_necessary do
257
- @imap.uid_fetch(uid, ["FLAGS", "RFC822", "RFC822.SIZE"])[0]
295
+ @imap.fetch(id, ["FLAGS", "RFC822", "RFC822.SIZE"])[0]
258
296
  end
259
297
  res = fetch_data.attr["RFC822"]
260
298
  mail = Mail.new(res)
261
- @current_uid = uid
299
+ @current_id = id
262
300
  @current_mail = mail # used later to show raw message or extract attachments if any
263
301
  log "saving current mail with parts: #{@current_mail.parts.inspect}"
264
302
  formatter = Vmail::MessageFormatter.new(mail)
265
303
  out = formatter.process_body
266
304
  size = fetch_data.attr["RFC822.SIZE"]
267
305
  @current_message = <<-EOF
268
- #{@mailbox} #{uid} #{number_to_human_size size} #{format_parts_info(formatter.list_parts)}
306
+ #{@mailbox} #{id} #{number_to_human_size size} #{format_parts_info(formatter.list_parts)}
269
307
  ---------------------------------------
270
308
  #{format_headers(formatter.extract_headers)}
271
309
 
@@ -283,36 +321,36 @@ EOF
283
321
  end
284
322
  end
285
323
 
286
- # uid_set is a string comming from the vim client
324
+ # id_set is a string comming from the vim client
287
325
  # action is -FLAGS or +FLAGS
288
- def flag(uid_set, action, flg)
289
- if uid_set.is_a?(String)
290
- uid_set = uid_set.split(",").map(&:to_i)
326
+ def flag(id_set, action, flg)
327
+ if id_set.is_a?(String)
328
+ id_set = id_set.split(",").map(&:to_i)
291
329
  end
292
330
  # #<struct Net::IMAP::FetchData seqno=17423, attr={"FLAGS"=>[:Seen, "Flagged"], "UID"=>83113}>
293
- log "flag #{uid_set} #{flg} #{action}"
331
+ log "flag #{id_set} #{flg} #{action}"
294
332
  if flg == 'Deleted'
295
333
  # for delete, do in a separate thread because deletions are slow
296
334
  Thread.new do
297
335
  unless @mailbox == '[Gmail]/Trash'
298
- @imap.uid_copy(uid_set, "[Gmail]/Trash")
336
+ @imap.copy(id_set, "[Gmail]/Trash")
299
337
  end
300
- res = @imap.uid_store(uid_set, action, [flg.to_sym])
338
+ res = @imap.store(id_set, action, [flg.to_sym])
301
339
  end
302
- uid_set.each { |uid| @all_uids.delete(uid) }
340
+ id_set.each { |id| @all_ids.delete(id) }
303
341
  elsif flg == '[Gmail]/Spam'
304
- @imap.uid_copy(uid_set, "[Gmail]/Spam")
305
- res = @imap.uid_store(uid_set, action, [:Deleted])
306
- "#{uid} deleted"
342
+ @imap.copy(id_set, "[Gmail]/Spam")
343
+ res = @imap.store(id_set, action, [:Deleted])
344
+ "#{id} deleted"
307
345
  else
308
346
  log "Flagging"
309
- res = @imap.uid_store(uid_set, action, [flg.to_sym])
347
+ res = @imap.store(id_set, action, [flg.to_sym])
310
348
  # log res.inspect
311
- fetch_headers(uid_set)
349
+ fetch_envelopes(id_set)
312
350
  end
313
351
  end
314
352
 
315
- def move_to(uid_set, mailbox)
353
+ def move_to(id_set, mailbox)
316
354
  if mailbox == 'all'
317
355
  log "archiving messages"
318
356
  end
@@ -320,24 +358,24 @@ EOF
320
358
  mailbox = MailboxAliases[mailbox]
321
359
  end
322
360
  create_if_necessary mailbox
323
- if uid_set.is_a?(String)
324
- uid_set = uid_set.split(",").map(&:to_i)
361
+ if id_set.is_a?(String)
362
+ id_set = id_set.split(",").map(&:to_i)
325
363
  end
326
- log "move_to #{uid_set.inspect} #{mailbox}"
327
- log @imap.uid_copy(uid_set, mailbox)
328
- log @imap.uid_store(uid_set, '+FLAGS', [:Deleted])
364
+ log "move_to #{id_set.inspect} #{mailbox}"
365
+ log @imap.copy(id_set, mailbox)
366
+ log @imap.store(id_set, '+FLAGS', [:Deleted])
329
367
  end
330
368
 
331
- def copy_to(uid_set, mailbox)
369
+ def copy_to(id_set, mailbox)
332
370
  if MailboxAliases[mailbox]
333
371
  mailbox = MailboxAliases[mailbox]
334
372
  end
335
373
  create_if_necessary mailbox
336
- log "copy #{uid_set.inspect} #{mailbox}"
337
- if uid_set.is_a?(String)
338
- uid_set = uid_set.split(",").map(&:to_i)
374
+ log "copy #{id_set.inspect} #{mailbox}"
375
+ if id_set.is_a?(String)
376
+ id_set = id_set.split(",").map(&:to_i)
339
377
  end
340
- log @imap.uid_copy(uid_set, mailbox)
378
+ log @imap.copy(id_set, mailbox)
341
379
  end
342
380
 
343
381
  def create_if_necessary(mailbox)
@@ -350,18 +388,18 @@ EOF
350
388
  end
351
389
  end
352
390
 
353
- def append_to_file(file, uid_set)
354
- if uid_set.is_a?(String)
355
- uid_set = uid_set.split(",").map(&:to_i)
391
+ def append_to_file(file, id_set)
392
+ if id_set.is_a?(String)
393
+ id_set = id_set.split(",").map(&:to_i)
356
394
  end
357
395
  log "append messages to file: #{file}"
358
- uid_set.each do |uid|
359
- message = show_message(uid)
396
+ id_set.each do |id|
397
+ message = show_message(id)
360
398
  divider = "#{'=' * 39}\n"
361
399
  File.open(file, 'a') {|f| f.puts(divider + message + "\n\n")}
362
- log "appended uid #{uid}"
400
+ log "appended id #{id}"
363
401
  end
364
- "printed #{uid_set.size} message#{uid_set.size == 1 ? '' : 's'} to #{file.strip}"
402
+ "printed #{id_set.size} message#{id_set.size == 1 ? '' : 's'} to #{file.strip}"
365
403
  end
366
404
 
367
405
 
@@ -384,9 +422,9 @@ EOF
384
422
  lines.join("\n")
385
423
  end
386
424
 
387
- def reply_template(uid, replyall=false)
388
- log "sending reply template for #{uid}"
389
- fetch_data = @imap.uid_fetch(uid.to_i, ["FLAGS", "ENVELOPE", "RFC822"])[0]
425
+ def reply_template(id, replyall=false)
426
+ log "sending reply template for #{id}"
427
+ fetch_data = @imap.fetch(id.to_i, ["FLAGS", "ENVELOPE", "RFC822"])[0]
390
428
  envelope = fetch_data.attr['ENVELOPE']
391
429
  recipient = [envelope.reply_to, envelope.from].flatten.map {|x| address_to_string(x)}[0]
392
430
  cc = [envelope.to, envelope.cc]
@@ -417,8 +455,8 @@ EOF
417
455
  "\n\n#@signature"
418
456
  end
419
457
 
420
- def forward_template(uid)
421
- original_body = show_message(uid, false, true)
458
+ def forward_template(id)
459
+ original_body = show_message(id, false, true)
422
460
  new_message_template +
423
461
  "\n---------- Forwarded message ----------\n" +
424
462
  original_body + signature
@@ -431,7 +469,7 @@ EOF
431
469
  # this is just to prime the IMAP connection
432
470
  # It's necessary for some reason.
433
471
  log "priming connection for delivering"
434
- res = @imap.uid_fetch(@all_uids[-1], ["ENVELOPE"])
472
+ res = @imap.fetch(@all_ids[-1], ["ENVELOPE"])
435
473
  if res.nil?
436
474
  raise IOError, "IMAP connection seems broken"
437
475
  end
@@ -518,8 +556,8 @@ EOF
518
556
  "saved:\n" + saved.map {|x| "- #{x}"}.join("\n")
519
557
  end
520
558
 
521
- def open_html_part(uid)
522
- log "open_html_part #{uid}"
559
+ def open_html_part(id)
560
+ log "open_html_part #{id}"
523
561
  log @current_mail.parts.inspect
524
562
  multipart = @current_mail.parts.detect {|part| part.multipart?}
525
563
  html_part = if multipart
@@ -571,6 +609,9 @@ EOF
571
609
  log(revive_connection)
572
610
  # try just once
573
611
  block.call
612
+ rescue
613
+ log "error: #{$!}"
614
+ raise
574
615
  end
575
616
 
576
617
  def self.start(config)
@@ -1,3 +1,3 @@
1
1
  module Vmail
2
- VERSION = "0.3.8"
2
+ VERSION = "0.3.9"
3
3
  end
metadata CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
5
5
  segments:
6
6
  - 0
7
7
  - 3
8
- - 8
9
- version: 0.3.8
8
+ - 9
9
+ version: 0.3.9
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-15 00:00:00 -05:00
17
+ date: 2010-12-16 00:00:00 -05:00
18
18
  default_executable:
19
19
  dependencies:
20
20
  - !ruby/object:Gem::Dependency