remailer 0.4.8 → 0.4.10
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/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
|