remailer 0.4.8 → 0.4.10

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.4.8
1
+ 0.4.10
@@ -77,9 +77,28 @@ class Remailer::Connection < EventMachine::Connection
77
77
  host_port = proxy_options[:port] || SOCKS5_PORT
78
78
  end
79
79
 
80
+ establish!(host_name, host_port, options)
81
+ end
82
+
83
+ # Warns about supplying a Proc which does not appear to accept the required
84
+ # number of arguments.
85
+ def self.warn_about_arguments(proc, range)
86
+ unless (range.include?(proc.arity) or proc.arity == -1)
87
+ STDERR.puts "Callback must accept #{[ range.min, range.max ].uniq.join(' to ')} arguments but accepts #{proc.arity}"
88
+ end
89
+ end
90
+
91
+ def self.establish!(host_name, host_port, options)
80
92
  EventMachine.connect(host_name, host_port, self, options)
81
93
 
82
94
  rescue EventMachine::ConnectionError => e
95
+ report_exception(e, options)
96
+
97
+ false
98
+ end
99
+
100
+ # Handles callbacks driven by exceptions before an instance could be created.
101
+ def self.report_exception(e)
83
102
  case (options[:connect])
84
103
  when Proc
85
104
  options[:connect].call(false, e.to_s)
@@ -111,14 +130,6 @@ class Remailer::Connection < EventMachine::Connection
111
130
  false
112
131
  end
113
132
 
114
- # Warns about supplying a Proc which does not appear to accept the required
115
- # number of arguments.
116
- def self.warn_about_arguments(proc, range)
117
- unless (range.include?(proc.arity) or proc.arity == -1)
118
- STDERR.puts "Callback must accept #{[ range.min, range.max ].uniq.join(' to ')} arguments but accepts #{proc.arity}"
119
- end
120
- end
121
-
122
133
  # == Instance Methods =====================================================
123
134
 
124
135
  # EventMachine will call this constructor and it is not to be called
@@ -264,17 +275,27 @@ class Remailer::Connection < EventMachine::Connection
264
275
  # This implements the EventMachine::Connection#unbind method to capture
265
276
  # a connection closed event.
266
277
  def unbind
278
+ return if (@unbound)
279
+
267
280
  @connected = false
281
+ @unbound = true
268
282
  @interpreter = nil
269
283
 
270
284
  if (@active_message)
271
285
  if (callback = @active_message[:callback])
272
286
  callback.call(nil)
287
+ @active_message = nil
273
288
  end
274
289
  end
275
290
 
276
291
  send_callback(:on_disconnect)
277
292
  end
293
+
294
+ # Returns true if the connection has been unbound by EventMachine, false
295
+ # otherwise.
296
+ def unbound?
297
+ !!@unbound
298
+ end
278
299
 
279
300
  # This implements the EventMachine::Connection#receive_data method that
280
301
  # is called each time new data is received from the socket.
@@ -284,9 +305,9 @@ class Remailer::Connection < EventMachine::Connection
284
305
  @buffer ||= ''
285
306
  @buffer << data
286
307
 
287
- if (@interpreter)
288
- @interpreter.process(@buffer) do |reply|
289
- debug_notification(:receive, "[#{@interpreter.label}] #{reply.inspect}")
308
+ if (interpreter = @interpreter)
309
+ interpreter.process(@buffer) do |reply|
310
+ debug_notification(:receive, "[#{interpreter.label}] #{reply.inspect}")
290
311
  end
291
312
  else
292
313
  error_notification(:out_of_band, "Receiving data before a protocol has been established.")
@@ -308,7 +329,11 @@ class Remailer::Connection < EventMachine::Connection
308
329
  # Returns the current state of the active interpreter, or nil if no state
309
330
  # is assigned.
310
331
  def state
311
- @interpreter and @interpreter.state
332
+ if (interpreter = @interpreter)
333
+ @interpreter.state
334
+ else
335
+ nil
336
+ end
312
337
  end
313
338
 
314
339
  # Sends a single line to the remote host with the appropriate CR+LF
@@ -322,14 +347,20 @@ class Remailer::Connection < EventMachine::Connection
322
347
  end
323
348
 
324
349
  def resolve_hostname(hostname)
325
- # FIXME: Elminitate this potentially blocking call by using an async
326
- # resolver if available.
327
350
  record = Socket.gethostbyname(hostname)
328
351
 
329
352
  # FIXME: IPv6 Support here
330
- debug_notification(:resolved, record && record.last.unpack('CCCC').join('.'))
353
+ address = (record and record[3])
354
+
355
+ if (address)
356
+ debug_notification(:resolver, "Address #{hostname} resolved as #{address.unpack('CCCC').join('.')}")
357
+ else
358
+ debug_notification(:resolver, "Address #{hostname} could not be resolved")
359
+ end
360
+
361
+ yield(address) if (block_given?)
331
362
 
332
- record and record.last
363
+ address
333
364
  rescue
334
365
  nil
335
366
  end
@@ -339,6 +370,12 @@ class Remailer::Connection < EventMachine::Connection
339
370
  @timeout_at = Time.now + @timeout
340
371
  end
341
372
 
373
+ # Returns the number of seconds remaining until a timeout will occur, or
374
+ # nil if no time-out is pending.
375
+ def time_remaning
376
+ @timeout_at and (@timeout_at.to_i - Time.now.to_i)
377
+ end
378
+
342
379
  # Checks for a timeout condition, and if one is detected, will close the
343
380
  # connection and send appropriate callbacks.
344
381
  def check_for_timeouts!
@@ -362,8 +399,8 @@ class Remailer::Connection < EventMachine::Connection
362
399
 
363
400
  message = "Timed out before a connection could be established to #{remote_options[:host]}:#{remote_options[:port]}"
364
401
 
365
- if (@interpreter)
366
- message << " using #{@interpreter.label}"
402
+ if (interpreter = @interpreter)
403
+ message << " using #{interpreter.label}"
367
404
  end
368
405
 
369
406
  connect_notification(false, message)
@@ -408,13 +445,20 @@ class Remailer::Connection < EventMachine::Connection
408
445
 
409
446
  # EventMachine: Closes down the connection.
410
447
  def close_connection
411
- send_callback(:on_disconnect)
448
+ return if (@closed)
449
+
450
+ unless (@timed_out)
451
+ send_callback(:on_disconnect)
452
+ end
453
+
412
454
  debug_notification(:closed, "Connection closed")
455
+
413
456
  super
414
457
 
415
458
  @connected = false
416
459
  @closed = true
417
460
  @timeout_at = nil
461
+ @interpreter = nil
418
462
  end
419
463
  alias_method :close, :close_connection
420
464
 
@@ -493,7 +537,9 @@ class Remailer::Connection < EventMachine::Connection
493
537
  end
494
538
 
495
539
  def message_callback(reply_code, reply_message)
496
- if (callback = (@active_message and @active_message[:callback]))
540
+ active_message = @active_message
541
+
542
+ if (callback = (active_message and active_message[:callback]))
497
543
  # The callback is screened in advance when assigned to ensure that it
498
544
  # has only 1 or 2 arguments. There should be no else here.
499
545
  case (callback.arity)
@@ -121,11 +121,7 @@ class Remailer::Connection::SmtpInterpreter < Remailer::Interpreter
121
121
  interpret(220) do
122
122
  delegate.start_tls
123
123
 
124
- if (delegate.requires_authentication?)
125
- enter_state(:auth)
126
- else
127
- enter_state(:established)
128
- end
124
+ enter_state(:helo)
129
125
  end
130
126
  end
131
127
 
@@ -138,20 +134,10 @@ class Remailer::Connection::SmtpInterpreter < Remailer::Interpreter
138
134
  enter_state(:established)
139
135
  end
140
136
 
141
- interpret(535) do |message, continues|
142
- if (@error)
143
- @error << ' '
144
-
145
- if (message.match(/^(\S+)/).to_s == @error.match(/^(\S+)/).to_s)
146
- @error << message.sub(/^\S+/, '')
147
- else
148
- @error << message
149
- end
150
- else
137
+ interpret(535) do |reply_message, continues|
138
+ handle_reply_continuation(535, reply_message, continues) do |reply_code, reply_message|
151
139
  @error = message
152
- end
153
140
 
154
- unless (continues)
155
141
  enter_state(:quit)
156
142
  end
157
143
  end
@@ -218,11 +204,23 @@ class Remailer::Connection::SmtpInterpreter < Remailer::Interpreter
218
204
  end
219
205
  end
220
206
 
221
- interpret(250) do
222
- if (delegate.active_message[:test])
207
+ interpret(250) do |reply_message, continues|
208
+ handle_reply_continuation(250, reply_message, continues) do |reply_code, reply_message|
209
+ if (delegate.active_message[:test])
210
+ delegate_call(:after_message_sent, reply_code, reply_message)
211
+
212
+ enter_state(:reset)
213
+ else
214
+ enter_state(:data)
215
+ end
216
+ end
217
+ end
218
+
219
+ interpret(500..599) do |reply_code, reply_message, continues|
220
+ handle_reply_continuation(reply_code, reply_message, continues) do |reply_code, reply_message|
221
+ delegate_call(:after_message_sent, reply_code, reply_message)
222
+
223
223
  enter_state(:reset)
224
- else
225
- enter_state(:data)
226
224
  end
227
225
  end
228
226
  end
@@ -251,8 +249,10 @@ class Remailer::Connection::SmtpInterpreter < Remailer::Interpreter
251
249
  delegate.send_line(".")
252
250
  end
253
251
 
254
- default do |reply_code, reply_message|
255
- delegate_call(:after_message_sent, reply_code, reply_message)
252
+ default do |reply_code, reply_message, continues|
253
+ handle_reply_continuation(reply_code, reply_message, continues) do |reply_code, reply_message|
254
+ delegate_call(:after_message_sent, reply_code, reply_message)
255
+ end
256
256
 
257
257
  enter_state(:sent)
258
258
  end
@@ -300,14 +300,16 @@ class Remailer::Connection::SmtpInterpreter < Remailer::Interpreter
300
300
  end
301
301
  end
302
302
 
303
- on_error do |reply_code, reply_message|
304
- delegate.message_callback(reply_code, reply_message)
305
- delegate.debug_notification(:error, "[#{@state}] #{reply_code} #{reply_message}")
306
- delegate.error_notification(reply_code, reply_message)
307
-
308
- delegate.active_message = nil
309
-
310
- enter_state(delegate.protocol ? :reset : :terminated)
303
+ on_error do |reply_code, reply_message, continues|
304
+ handle_reply_continuation(reply_code, reply_message, continues) do |reply_code, reply_message|
305
+ delegate.message_callback(reply_code, reply_message)
306
+ delegate.debug_notification(:error, "[#{@state}] #{reply_code} #{reply_message}")
307
+ delegate.error_notification(reply_code, reply_message)
308
+
309
+ delegate.active_message = nil
310
+
311
+ enter_state(@state == :initialized ? :terminated : :reset)
312
+ end
311
313
  end
312
314
 
313
315
  # == Instance Methods =====================================================
@@ -315,6 +317,22 @@ class Remailer::Connection::SmtpInterpreter < Remailer::Interpreter
315
317
  def label
316
318
  'SMTP'
317
319
  end
320
+
321
+ def handle_reply_continuation(reply_code, reply_message, continues)
322
+ @reply_message ||= ''
323
+
324
+ if (preamble = @reply_message.split(/\s/).first)
325
+ reply_message.sub!(/^#{preamble}/, '')
326
+ end
327
+
328
+ @reply_message << reply_message.gsub(/\s+/, ' ')
329
+
330
+ unless (continues)
331
+ yield(reply_code, @reply_message)
332
+
333
+ @reply_message = nil
334
+ end
335
+ end
318
336
 
319
337
  def will_interpret?(proc, args)
320
338
  # Can only interpret blocks if the last part of the message has been
@@ -82,9 +82,10 @@ class Remailer::Connection::Socks5Interpreter < Remailer::Interpreter
82
82
 
83
83
  state :resolving_destination do
84
84
  enter do
85
- # FIX: Use an async resolver here
86
- @destination_address = delegate.resolve_hostname(delegate.options[:host])
87
- enter_state(:connect_through_proxy)
85
+ delegate.resolve_hostname(delegate.options[:host]) do |address|
86
+ @destination_address = address
87
+ enter_state(:connect_through_proxy)
88
+ end
88
89
  end
89
90
  end
90
91
 
@@ -104,7 +105,7 @@ class Remailer::Connection::Socks5Interpreter < Remailer::Interpreter
104
105
  ].pack('CCCCA4n')
105
106
  )
106
107
  else
107
- delegate.send_callback(:error_connecting, "Could not resolve hostname #{delegate.options[:host]}")
108
+ @error_message = "Could not resolve hostname #{delegate.options[:host]}"
108
109
  enter_state(:failed)
109
110
  end
110
111
  end
@@ -169,10 +170,15 @@ class Remailer::Connection::Socks5Interpreter < Remailer::Interpreter
169
170
 
170
171
  state :failed do
171
172
  enter do
172
- message = "Proxy server returned error code #{@reply}: #{SOCKS5_REPLY[@reply]}"
173
- delegate.debug_notification(:error, message)
173
+ if (@error_message)
174
+ delegate.debug_notification(:error, @error_message)
175
+ delegate.error_notification("SOCKS5", @error_message)
176
+ else
177
+ message = "Proxy server returned error code #{@reply}: #{SOCKS5_REPLY[@reply]}"
178
+ delegate.debug_notification(:error, message)
179
+ delegate.error_notification("SOCKS5_#{@reply}", message)
180
+ end
174
181
  delegate.connect_notification(false, message)
175
- delegate.error_notification("SOCKS5_#{@reply}", message)
176
182
  delegate.close_connection
177
183
  end
178
184
 
@@ -231,6 +231,8 @@ class Remailer::Interpreter
231
231
  case (response)
232
232
  when Regexp
233
233
  match_result = response.match(object)
234
+ when Range
235
+ response.include?(object)
234
236
  else
235
237
  response === object
236
238
  end
@@ -249,6 +251,8 @@ class Remailer::Interpreter
249
251
  end
250
252
  when String
251
253
  args[0].sub!(matched, '')
254
+ when Range
255
+ # Keep as-is
252
256
  else
253
257
  args.shift
254
258
  end
data/remailer.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{remailer}
8
- s.version = "0.4.8"
8
+ s.version = "0.4.10"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = [%q{Scott Tadman}]
12
- s.date = %q{2011-05-15}
12
+ s.date = %q{2011-07-05}
13
13
  s.description = %q{EventMachine SMTP Mail User Agent}
14
14
  s.email = %q{scott@twg.ca}
15
15
  s.extra_rdoc_files = [
@@ -38,7 +38,7 @@ Gem::Specification.new do |s|
38
38
  ]
39
39
  s.homepage = %q{http://github.com/twg/remailer}
40
40
  s.require_paths = [%q{lib}]
41
- s.rubygems_version = %q{1.8.2}
41
+ s.rubygems_version = %q{1.8.5}
42
42
  s.summary = %q{Reactor-Ready SMTP Mailer}
43
43
  s.test_files = [
44
44
  "test/config.example.rb",
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: remailer
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.4.8
5
+ version: 0.4.10
6
6
  platform: ruby
7
7
  authors:
8
8
  - Scott Tadman
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-05-15 00:00:00 Z
13
+ date: 2011-07-05 00:00:00 Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: eventmachine
@@ -74,7 +74,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
74
74
  requirements: []
75
75
 
76
76
  rubyforge_project:
77
- rubygems_version: 1.8.2
77
+ rubygems_version: 1.8.5
78
78
  signing_key:
79
79
  specification_version: 3
80
80
  summary: Reactor-Ready SMTP Mailer