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.
Files changed (4) hide show
  1. data/bin/larch +7 -2
  2. data/lib/larch/imap.rb +86 -55
  3. data/lib/larch.rb +9 -7
  4. 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
- for sig in [:SIGINT, :SIGQUIT, :SIGTERM]
87
- trap(sig) { @log.fatal "Interrupted (#{sig})"; summary; exit }
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
- @imap.disconnect
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
- # Connect if necessary, execute the given block, retry up to 3 times if a
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 unless @imap
370
- rescue *RECOVERABLE_ERRORS => e
371
- info "#{e.class.name}: #{e.message} (will retry)"
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
- rescue *RECOVERABLE_ERRORS => e
385
- info "#{e.class.name}: #{e.message} (will retry)"
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::NoResponseError => e
432
+ rescue Net::IMAP::Error => e
393
433
  raise Error, "#{e.class.name}: #{e.message} (giving up)"
394
434
 
395
- rescue IOError, Net::IMAP::Error, OpenSSL::SSL::SSLError, SocketError, SystemCallError => e
396
- raise FatalError, "#{e.class.name}: #{e.message} (giving up)"
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
- exception = nil
445
+ @imap = Net::IMAP.new(host, port, ssl?)
403
446
 
404
- Thread.new do
405
- begin
406
- @imap = Net::IMAP.new(host, port, ssl?)
447
+ info "connected on port #{port}" << (ssl? ? ' using SSL' : '')
407
448
 
408
- info "connected on port #{port}" << (ssl? ? ' using SSL' : '')
449
+ auth_methods = ['PLAIN']
450
+ tried = []
409
451
 
410
- auth_methods = ['PLAIN']
411
- tried = []
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
- if method == 'PLAIN'
423
- @imap.login(@username, @password)
424
- else
425
- @imap.authenticate(method, @username, @password)
426
- end
456
+ begin
457
+ tried << method = auth_methods.pop
427
458
 
428
- info "authenticated using #{method}"
459
+ debug "authenticating using #{method}"
429
460
 
430
- rescue Net::IMAP::BadResponseError, Net::IMAP::NoResponseError => e
431
- debug "#{method} auth failed: #{e.message}"
432
- retry unless auth_methods.empty?
461
+ if method == 'PLAIN'
462
+ @imap.login(@username, @password)
463
+ else
464
+ @imap.authenticate(method, @username, @password)
465
+ end
433
466
 
434
- raise e, "#{e.message} (tried #{tried.join(', ')})"
435
- end
467
+ info "authenticated using #{method}"
436
468
 
437
- rescue => e
438
- exception = e
439
- error e.message
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
- raise exception if exception
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
- source_scan = Thread.new do
46
- source.scan_mailbox
47
- end
45
+ source.connect
46
+ dest.connect
48
47
 
49
- dest_scan = Thread.new do
50
- dest.scan_mailbox
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
 
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: 1.0.0.1
4
+ version: 1.0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryan Grove