rgrove-larch 1.0.0.1 → 1.0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- data/bin/larch +7 -2
- data/lib/larch/imap.rb +86 -55
- data/lib/larch.rb +9 -7
- metadata +1 -1
data/bin/larch
CHANGED
@@ -83,8 +83,13 @@ EOS
|
|
83
83
|
:fast_scan => options[:fast_scan],
|
84
84
|
:max_retries => options[:max_retries])
|
85
85
|
|
86
|
-
|
87
|
-
|
86
|
+
unless RUBY_PLATFORM =~ /mswin|mingw|bccwin|wince|java/
|
87
|
+
begin
|
88
|
+
for sig in [:SIGINT, :SIGQUIT, :SIGTERM]
|
89
|
+
trap(sig) { @log.fatal "Interrupted (#{sig})"; exit }
|
90
|
+
end
|
91
|
+
rescue => e
|
92
|
+
end
|
88
93
|
end
|
89
94
|
|
90
95
|
copy(source, dest)
|
data/lib/larch/imap.rb
CHANGED
@@ -11,14 +11,6 @@ class IMAP
|
|
11
11
|
# Maximum number of messages to fetch at once.
|
12
12
|
MAX_FETCH_COUNT = 1024
|
13
13
|
|
14
|
-
# Recoverable connection errors.
|
15
|
-
RECOVERABLE_ERRORS = [
|
16
|
-
Errno::EPIPE,
|
17
|
-
Errno::ETIMEDOUT,
|
18
|
-
Net::IMAP::NoResponseError,
|
19
|
-
OpenSSL::SSL::SSLError
|
20
|
-
]
|
21
|
-
|
22
14
|
# Regex to capture the individual fields in an IMAP fetch command.
|
23
15
|
REGEX_FIELDS = /([0-9A-Z\.]+\[[^\]]+\](?:<[0-9\.]+>)?|[0-9A-Z\.]+)/
|
24
16
|
|
@@ -126,7 +118,12 @@ class IMAP
|
|
126
118
|
return unless @imap
|
127
119
|
|
128
120
|
synchronize do
|
129
|
-
|
121
|
+
begin
|
122
|
+
@imap.disconnect
|
123
|
+
rescue Errno::ENOTCONN => e
|
124
|
+
debug "#{e.class.name}: #{e.message}"
|
125
|
+
end
|
126
|
+
|
130
127
|
@imap = nil
|
131
128
|
end
|
132
129
|
|
@@ -359,17 +356,23 @@ class IMAP
|
|
359
356
|
good_results
|
360
357
|
end
|
361
358
|
|
362
|
-
|
363
|
-
# recoverable error occurs, die if an unrecoverable error occurs.
|
364
|
-
def safely
|
365
|
-
retries = 0
|
366
|
-
|
359
|
+
def safe_connect
|
367
360
|
synchronize do
|
361
|
+
return if @imap
|
362
|
+
|
363
|
+
retries = 0
|
364
|
+
|
368
365
|
begin
|
369
|
-
unsafe_connect
|
370
|
-
|
371
|
-
|
366
|
+
unsafe_connect
|
367
|
+
|
368
|
+
rescue Errno::EPIPE,
|
369
|
+
Errno::ETIMEDOUT,
|
370
|
+
IOError,
|
371
|
+
Net::IMAP::NoResponseError,
|
372
|
+
OpenSSL::SSL::SSLError => e
|
373
|
+
|
372
374
|
raise unless (retries += 1) <= @options[:max_retries]
|
375
|
+
info "#{e.class.name}: #{e.message} (will retry)"
|
373
376
|
|
374
377
|
@imap = nil
|
375
378
|
sleep 1 * retries
|
@@ -377,70 +380,98 @@ class IMAP
|
|
377
380
|
end
|
378
381
|
end
|
379
382
|
|
383
|
+
rescue => e
|
384
|
+
raise FatalError, "#{e.class.name}: #{e.message} (cannot recover)"
|
385
|
+
end
|
386
|
+
|
387
|
+
# Connect if necessary, execute the given block, retry up to 3 times if a
|
388
|
+
# recoverable error occurs, die if an unrecoverable error occurs.
|
389
|
+
def safely
|
390
|
+
safe_connect
|
391
|
+
|
392
|
+
synchronize do
|
393
|
+
# Explicitly set Net::IMAP's client thread to the current thread to ensure
|
394
|
+
# that exceptions aren't raised in a dead thread.
|
395
|
+
@imap.client_thread = Thread.current
|
396
|
+
end
|
397
|
+
|
380
398
|
retries = 0
|
381
399
|
|
382
400
|
begin
|
383
401
|
yield
|
384
|
-
|
385
|
-
|
402
|
+
|
403
|
+
rescue EOFError,
|
404
|
+
Errno::ENOTCONN,
|
405
|
+
Errno::EPIPE,
|
406
|
+
Errno::ETIMEDOUT,
|
407
|
+
IOError,
|
408
|
+
Net::IMAP::ByeResponseError,
|
409
|
+
OpenSSL::SSL::SSLError => e
|
410
|
+
|
386
411
|
raise unless (retries += 1) <= @options[:max_retries]
|
387
412
|
|
413
|
+
info "#{e.class.name}: #{e.message} (reconnecting)"
|
414
|
+
|
415
|
+
synchronize { @imap = nil }
|
416
|
+
sleep 1 * retries
|
417
|
+
safe_connect
|
418
|
+
retry
|
419
|
+
|
420
|
+
rescue Net::IMAP::BadResponseError,
|
421
|
+
Net::IMAP::NoResponseError,
|
422
|
+
Net::IMAP::ResponseParseError => e
|
423
|
+
|
424
|
+
raise unless (retries += 1) <= @options[:max_retries]
|
425
|
+
|
426
|
+
info "#{e.class.name}: #{e.message} (will retry)"
|
427
|
+
|
388
428
|
sleep 1 * retries
|
389
429
|
retry
|
390
430
|
end
|
391
431
|
|
392
|
-
rescue Net::IMAP::
|
432
|
+
rescue Net::IMAP::Error => e
|
393
433
|
raise Error, "#{e.class.name}: #{e.message} (giving up)"
|
394
434
|
|
395
|
-
rescue
|
396
|
-
raise
|
435
|
+
rescue Larch::Error => e
|
436
|
+
raise
|
437
|
+
|
438
|
+
rescue => e
|
439
|
+
raise FatalError, "#{e.class.name}: #{e.message} (cannot recover)"
|
397
440
|
end
|
398
441
|
|
399
442
|
def unsafe_connect
|
400
443
|
info "connecting..."
|
401
444
|
|
402
|
-
|
445
|
+
@imap = Net::IMAP.new(host, port, ssl?)
|
403
446
|
|
404
|
-
|
405
|
-
begin
|
406
|
-
@imap = Net::IMAP.new(host, port, ssl?)
|
447
|
+
info "connected on port #{port}" << (ssl? ? ' using SSL' : '')
|
407
448
|
|
408
|
-
|
449
|
+
auth_methods = ['PLAIN']
|
450
|
+
tried = []
|
409
451
|
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
['LOGIN', 'CRAM-MD5'].each do |method|
|
414
|
-
auth_methods << method if @imap.capability.include?("AUTH=#{method}")
|
415
|
-
end
|
416
|
-
|
417
|
-
begin
|
418
|
-
tried << method = auth_methods.pop
|
419
|
-
|
420
|
-
debug "authenticating using #{method}"
|
452
|
+
['LOGIN', 'CRAM-MD5'].each do |method|
|
453
|
+
auth_methods << method if @imap.capability.include?("AUTH=#{method}")
|
454
|
+
end
|
421
455
|
|
422
|
-
|
423
|
-
|
424
|
-
else
|
425
|
-
@imap.authenticate(method, @username, @password)
|
426
|
-
end
|
456
|
+
begin
|
457
|
+
tried << method = auth_methods.pop
|
427
458
|
|
428
|
-
|
459
|
+
debug "authenticating using #{method}"
|
429
460
|
|
430
|
-
|
431
|
-
|
432
|
-
|
461
|
+
if method == 'PLAIN'
|
462
|
+
@imap.login(@username, @password)
|
463
|
+
else
|
464
|
+
@imap.authenticate(method, @username, @password)
|
465
|
+
end
|
433
466
|
|
434
|
-
|
435
|
-
end
|
467
|
+
info "authenticated using #{method}"
|
436
468
|
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
end
|
441
|
-
end.join
|
469
|
+
rescue Net::IMAP::BadResponseError, Net::IMAP::NoResponseError => e
|
470
|
+
debug "#{method} auth failed: #{e.message}"
|
471
|
+
retry unless auth_methods.empty?
|
442
472
|
|
443
|
-
|
473
|
+
raise e, "#{e.message} (tried #{tried.join(', ')})"
|
474
|
+
end
|
444
475
|
end
|
445
476
|
end
|
446
477
|
|
data/lib/larch.rb
CHANGED
@@ -42,13 +42,11 @@ module Larch
|
|
42
42
|
|
43
43
|
@log.info "copying messages from #{source.uri} to #{dest.uri}"
|
44
44
|
|
45
|
-
|
46
|
-
|
47
|
-
end
|
45
|
+
source.connect
|
46
|
+
dest.connect
|
48
47
|
|
49
|
-
|
50
|
-
|
51
|
-
end
|
48
|
+
source_scan = Thread.new { source.scan_mailbox }
|
49
|
+
dest_scan = Thread.new { dest.scan_mailbox }
|
52
50
|
|
53
51
|
source_scan.join
|
54
52
|
dest_scan.join
|
@@ -96,7 +94,7 @@ module Larch
|
|
96
94
|
mutex.synchronize { @copied += 1 }
|
97
95
|
end
|
98
96
|
|
99
|
-
rescue IMAP::FatalError => e
|
97
|
+
rescue Larch::IMAP::FatalError => e
|
100
98
|
@log.fatal e.message
|
101
99
|
|
102
100
|
rescue => e
|
@@ -111,6 +109,10 @@ module Larch
|
|
111
109
|
source.disconnect
|
112
110
|
dest.disconnect
|
113
111
|
|
112
|
+
rescue => e
|
113
|
+
@log.fatal e.message
|
114
|
+
|
115
|
+
ensure
|
114
116
|
summary
|
115
117
|
end
|
116
118
|
|