rgrove-larch 0.0.1.4 → 1.0.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/bin/larch +13 -10
- data/lib/larch.rb +3 -1
- data/lib/larch/imap.rb +26 -18
- data/lib/larch/util.rb +2 -1
- data/lib/larch/version.rb +1 -1
- metadata +2 -2
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
|
30
|
-
opt :from_pass, "Source server password (Default: prompt)
|
31
|
-
opt :from_user, "Source server username (Default: prompt)
|
32
|
-
opt :
|
33
|
-
opt :
|
34
|
-
opt :
|
35
|
-
opt :
|
36
|
-
opt :
|
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
|
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
|
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(
|
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
|
-
# [
|
39
|
+
# [:create_mailbox]
|
40
40
|
# If +true+, the specified mailbox will be created if necessary.
|
41
41
|
#
|
42
|
-
# [
|
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
|
-
#
|
48
|
-
#
|
49
|
-
#
|
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
|
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) <=
|
344
|
+
raise unless (retries += 1) <= @options[:max_retries]
|
337
345
|
|
338
346
|
@imap = nil
|
339
|
-
sleep
|
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) <=
|
357
|
+
raise unless (retries += 1) <= @options[:max_retries]
|
350
358
|
|
351
|
-
sleep
|
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
|
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
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
|
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.
|
33
|
+
version: "1.13"
|
34
34
|
version:
|
35
35
|
description:
|
36
36
|
email: ryan@wonko.com
|