rgrove-larch 0.0.1.4 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/bin/larch CHANGED
@@ -26,14 +26,15 @@ EOS
26
26
 
27
27
  # opt :dry_run, "Don't actually do anything.", :short => '-n'
28
28
  opt :fast_scan, "Use a faster (but less accurate) method to scan mailboxes. This may result in messages being re-copied.", :short => :none
29
- opt :from_folder, "Source folder to copy from.", :short => :none, :default => 'INBOX'
30
- opt :from_pass, "Source server password (Default: prompt).", :short => :none, :type => :string
31
- opt :from_user, "Source server username (Default: prompt).", :short => :none, :type => :string
32
- opt :no_create_folder, "Don't create destination folders that don't already exist.", :short => :none
33
- opt :to_folder, "Destination folder to copy to.", :short => :none, :default => 'INBOX'
34
- opt :to_pass, "Destination server password (Default: prompt).", :short => :none, :type => :string
35
- opt :to_user, "Destination server username (Default: prompt).", :short => :none, :type => :string
36
- opt :verbosity, "Output verbosity: debug, info, warn, error, or fatal.", :short => '-V', :default => 'info'
29
+ opt :from_folder, "Source folder to copy from", :short => :none, :default => 'INBOX'
30
+ opt :from_pass, "Source server password (Default: prompt)", :short => :none, :type => :string
31
+ opt :from_user, "Source server username (Default: prompt)", :short => :none, :type => :string
32
+ opt :max_retries, "Maximum number of times to retry after a recoverable error", :short => :none, :default => 3
33
+ opt :no_create_folder, "Don't create destination folders that don't already exist", :short => :none
34
+ opt :to_folder, "Destination folder to copy to", :short => :none, :default => 'INBOX'
35
+ opt :to_pass, "Destination server password (Default: prompt)", :short => :none, :type => :string
36
+ opt :to_user, "Destination server username (Default: prompt)", :short => :none, :type => :string
37
+ opt :verbosity, "Output verbosity: debug, info, warn, error, or fatal", :short => '-V', :default => 'info'
37
38
  end
38
39
 
39
40
  # Validate command-line options.
@@ -74,11 +75,13 @@ EOS
74
75
  init(options[:verbosity])
75
76
 
76
77
  source = IMAP.new(options[:from], options[:from_user], options[:from_pass],
77
- :fast_scan => options[:fast_scan])
78
+ :fast_scan => options[:fast_scan],
79
+ :max_retries => options[:max_retries])
78
80
 
79
81
  dest = IMAP.new(options[:to], options[:to_user], options[:to_pass],
80
82
  :create_mailbox => !options[:no_create_folder],
81
- :fast_scan => options[:fast_scan])
83
+ :fast_scan => options[:fast_scan],
84
+ :max_retries => options[:max_retries])
82
85
 
83
86
  for sig in [:SIGINT, :SIGQUIT, :SIGTERM]
84
87
  trap(sig) { @log.fatal "Interrupted (#{sig})"; summary; exit }
data/lib/larch.rb CHANGED
@@ -34,7 +34,7 @@ module Larch
34
34
  raise ArgumentError, "source must be a Larch::IMAP instance" unless source.is_a?(IMAP)
35
35
  raise ArgumentError, "dest must be a Larch::IMAP instance" unless dest.is_a?(IMAP)
36
36
 
37
- msgq = SizedQueue.new(32)
37
+ msgq = SizedQueue.new(8)
38
38
  mutex = Mutex.new
39
39
 
40
40
  @copied = 0
@@ -64,7 +64,9 @@ module Larch
64
64
  begin
65
65
  msgq << source.peek(id)
66
66
  rescue Larch::IMAP::Error => e
67
+ # TODO: Keep failed message envelopes in a buffer for later output?
67
68
  mutex.synchronize { @failed += 1 }
69
+ @log.error e.message
68
70
  next
69
71
  end
70
72
  end
data/lib/larch/imap.rb CHANGED
@@ -4,7 +4,7 @@ module Larch
4
4
  #
5
5
  # This class borrows heavily from Sup, the source code of which should be
6
6
  # required reading if you're doing anything with IMAP in Ruby:
7
- # http://sup.rubyforge.org/
7
+ # http://sup.rubyforge.org
8
8
  class IMAP
9
9
 
10
10
  # Recoverable connection errors.
@@ -36,17 +36,20 @@ class IMAP
36
36
  #
37
37
  # The following options may also be specified:
38
38
  #
39
- # [+create_mailbox+]
39
+ # [:create_mailbox]
40
40
  # If +true+, the specified mailbox will be created if necessary.
41
41
  #
42
- # [+fast_scan+]
42
+ # [:fast_scan]
43
43
  # If +true+, a faster but less accurate method will be used to scan
44
44
  # mailboxes. This will speed up the initial mailbox scan, but will also
45
- # reduce the effectiveness of the message unique id generator.
45
+ # reduce the effectiveness of the message unique id generator. This is
46
+ # probably acceptable when copying a very large mailbox to an empty mailbox,
47
+ # but if the destination already contains messages, using this option is not
48
+ # advised.
46
49
  #
47
- # This is probably acceptable when copying a very large mailbox to an empty
48
- # mailbox, but if the destination already contains messages, using this
49
- # option is not advised.
50
+ # [:max_retries]
51
+ # After a recoverable error occurs, retry the operation up to this many
52
+ # times. Default is 3.
50
53
  #
51
54
  def initialize(uri, username, password, options = {})
52
55
  raise ArgumentError, "not an IMAP URI: #{uri}" unless uri.is_a?(URI) || uri =~ REGEX_URI
@@ -56,7 +59,7 @@ class IMAP
56
59
  @uri = uri.is_a?(URI) ? uri : URI(uri)
57
60
  @username = username
58
61
  @password = password
59
- @options = options
62
+ @options = {:max_retries => 3}.merge(options)
60
63
 
61
64
  @ids = {}
62
65
  @imap = nil
@@ -148,7 +151,7 @@ class IMAP
148
151
  imap_uid_fetch([uid], 'ENVELOPE').first.attr['ENVELOPE']
149
152
  end
150
153
 
151
- # Fetches a Larch::Message instance representing the message with the
154
+ # Fetches a Larch::IMAP::Message struct representing the message with the
152
155
  # specified Larch message id.
153
156
  def fetch(message_id, peek = false)
154
157
  scan_mailbox
@@ -171,6 +174,7 @@ class IMAP
171
174
  @ids.has_key?(message_id)
172
175
  end
173
176
 
177
+ # Gets the IMAP hostname.
174
178
  def host
175
179
  @uri.host
176
180
  end
@@ -182,20 +186,22 @@ class IMAP
182
186
  end
183
187
  alias size length
184
188
 
189
+ # Gets the IMAP mailbox.
190
+ def mailbox
191
+ mb = @uri.path[1..-1]
192
+ mb.nil? || mb.empty? ? 'INBOX' : CGI.unescape(mb)
193
+ end
194
+
185
195
  # Same as fetch, but doesn't mark the message as seen.
186
196
  def peek(message_id)
187
197
  fetch(message_id, true)
188
198
  end
189
199
 
200
+ # Gets the IMAP port number.
190
201
  def port
191
202
  @uri.port || (ssl? ? 993 : 143)
192
203
  end
193
204
 
194
- def mailbox
195
- mb = @uri.path[1..-1]
196
- mb.nil? || mb.empty? ? 'INBOX' : CGI.unescape(mb)
197
- end
198
-
199
205
  # Fetches message headers from the current mailbox.
200
206
  def scan_mailbox
201
207
  return if @last_scan && (Time.now - @last_scan) < SCAN_INTERVAL
@@ -244,10 +250,12 @@ class IMAP
244
250
  end
245
251
  synchronized :scan_mailbox
246
252
 
253
+ # Gets the SSL status.
247
254
  def ssl?
248
255
  @uri.scheme == 'imaps'
249
256
  end
250
257
 
258
+ # Gets the IMAP URI.
251
259
  def uri
252
260
  @uri.to_s
253
261
  end
@@ -333,10 +341,10 @@ class IMAP
333
341
  unsafe_connect unless @imap
334
342
  rescue *RECOVERABLE_ERRORS => e
335
343
  info "#{e.class.name}: #{e.message} (will retry)"
336
- raise unless (retries += 1) <= 3
344
+ raise unless (retries += 1) <= @options[:max_retries]
337
345
 
338
346
  @imap = nil
339
- sleep 2 * retries
347
+ sleep 1 * retries
340
348
  retry
341
349
  end
342
350
 
@@ -346,9 +354,9 @@ class IMAP
346
354
  yield
347
355
  rescue *RECOVERABLE_ERRORS => e
348
356
  info "#{e.class.name}: #{e.message} (will retry)"
349
- raise unless (retries += 1) <= 3
357
+ raise unless (retries += 1) <= @options[:max_retries]
350
358
 
351
- sleep 2 * retries
359
+ sleep 1 * retries
352
360
  retry
353
361
  end
354
362
 
data/lib/larch/util.rb CHANGED
@@ -1,7 +1,8 @@
1
1
  class Module
2
2
 
3
3
  # Java-style whole-method synchronization, shamelessly stolen from Sup:
4
- # http://sup.rubyforge.org/. Assumes the existence of a @mutex variable.
4
+ # http://sup.rubyforge.org. Assumes the existence of a <tt>@mutex</tt>
5
+ # variable.
5
6
  def synchronized(*methods)
6
7
  methods.each do |method|
7
8
  class_eval <<-EOF
data/lib/larch/version.rb CHANGED
@@ -1,6 +1,6 @@
1
1
  module Larch
2
2
  APP_NAME = 'Larch'
3
- APP_VERSION = '0.0.1'
3
+ APP_VERSION = '1.0.0'
4
4
  APP_AUTHOR = 'Ryan Grove'
5
5
  APP_EMAIL = 'ryan@wonko.com'
6
6
  APP_URL = 'http://github.com/rgrove/larch/'
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rgrove-larch
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1.4
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Grove
@@ -30,7 +30,7 @@ dependencies:
30
30
  requirements:
31
31
  - - ~>
32
32
  - !ruby/object:Gem::Version
33
- version: "1.12"
33
+ version: "1.13"
34
34
  version:
35
35
  description:
36
36
  email: ryan@wonko.com