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 +1 -1
- data/lib/remailer/connection.rb +66 -20
- data/lib/remailer/connection/smtp_interpreter.rb +49 -31
- data/lib/remailer/connection/socks5_interpreter.rb +13 -7
- data/lib/remailer/interpreter.rb +4 -0
- data/remailer.gemspec +3 -3
- metadata +3 -3
data/VERSION
CHANGED
@@ -1 +1 @@
|
|
1
|
-
0.4.
|
1
|
+
0.4.10
|
data/lib/remailer/connection.rb
CHANGED
@@ -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
|
-
|
289
|
-
debug_notification(:receive, "[#{
|
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
|
-
|
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
|
-
|
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
|
-
|
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 #{
|
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
|
-
|
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
|
-
|
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
|
-
|
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 |
|
142
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
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
|
-
|
86
|
-
|
87
|
-
|
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
|
-
|
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
|
-
|
173
|
-
|
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
|
|
data/lib/remailer/interpreter.rb
CHANGED
@@ -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
|
+
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
|
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.
|
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.
|
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
|
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.
|
77
|
+
rubygems_version: 1.8.5
|
78
78
|
signing_key:
|
79
79
|
specification_version: 3
|
80
80
|
summary: Reactor-Ready SMTP Mailer
|