remailer 0.2.1 → 0.3.0

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.
@@ -0,0 +1,347 @@
1
+ require File.expand_path(File.join(*%w[ .. helper ]), File.dirname(__FILE__))
2
+
3
+ class SmtpDelegate
4
+ attr_accessor :options, :active_message
5
+
6
+ def initialize(options = { })
7
+ @sent = [ ]
8
+ @options = options
9
+ end
10
+
11
+ def hostname
12
+ 'localhost.local'
13
+ end
14
+
15
+ def requires_authentication?
16
+ !!@options[:username]
17
+ end
18
+
19
+ def use_tls?
20
+ !!@options[:use_tls]
21
+ end
22
+
23
+ def send_line(data = '')
24
+ @sent << data
25
+ end
26
+
27
+ def start_tls
28
+ @started_tls = true
29
+ end
30
+
31
+ def started_tls?
32
+ !!@started_tls
33
+ end
34
+
35
+ def close_connection
36
+ @closed = true
37
+ end
38
+
39
+ def closed?
40
+ !!@closed
41
+ end
42
+
43
+ def clear!
44
+ @sent = [ ]
45
+ end
46
+
47
+ def size
48
+ @sent.size
49
+ end
50
+
51
+ def read
52
+ @sent.shift
53
+ end
54
+
55
+ def method_missing(*args)
56
+ end
57
+ end
58
+
59
+ class RemailerConnectionSmtpInterpreterTest < Test::Unit::TestCase
60
+ def test_split_reply
61
+ assert_mapping(
62
+ '250 OK' => [ 250, 'OK' ],
63
+ '250 Long message' => [ 250, 'Long message' ],
64
+ 'OK' => nil,
65
+ '100-Example' => [ 100, 'Example', :continued ]
66
+ ) do |reply|
67
+ Remailer::Connection::SmtpInterpreter.split_reply(reply)
68
+ end
69
+ end
70
+
71
+ def test_parser
72
+ interpreter = Remailer::Connection::SmtpInterpreter.new
73
+
74
+ assert_mapping(
75
+ "250 OK\r\n" => [ 250, 'OK' ],
76
+ "250 Long message\r\n" => [ 250, 'Long message' ],
77
+ "OK\r\n" => nil,
78
+ "100-Example\r\n" => [ 100, 'Example', :continued ],
79
+ "100-Example" => nil
80
+ ) do |reply|
81
+ interpreter.parse(reply.dup)
82
+ end
83
+ end
84
+
85
+ def test_encode_data
86
+ sample_data = "Line 1\r\nLine 2\r\n.\r\nLine 3\r\n.Line 4\r\n"
87
+
88
+ assert_equal "Line 1\r\nLine 2\r\n..\r\nLine 3\r\n..Line 4\r\n", Remailer::Connection::SmtpInterpreter.encode_data(sample_data)
89
+ end
90
+
91
+ def test_base64
92
+ assert_mapping(
93
+ 'example' => 'example',
94
+ "\x7F" => "\x7F",
95
+ nil => ''
96
+ ) do |example|
97
+ Remailer::Connection::SmtpInterpreter.base64(example).unpack('m')[0]
98
+ end
99
+ end
100
+
101
+ def test_encode_authentication
102
+ assert_mapping(
103
+ %w[ tester tester ] => 'AHRlc3RlcgB0ZXN0ZXI='
104
+ ) do |username, password|
105
+ Remailer::Connection::SmtpInterpreter.encode_authentication(username, password)
106
+ end
107
+ end
108
+
109
+ def test_defaults
110
+ interpreter = Remailer::Connection::SmtpInterpreter.new
111
+
112
+ assert_equal :initialized, interpreter.state
113
+ end
114
+
115
+ def test_delegate_default_state
116
+ delegate = SmtpDelegate.new
117
+
118
+ assert_equal false, delegate.closed?
119
+ assert_equal nil, delegate.read
120
+ assert_equal 0, delegate.size
121
+ end
122
+
123
+ def test_delegate_options
124
+ delegate = SmtpDelegate.new(:use_tls => true)
125
+
126
+ assert_equal true, delegate.use_tls?
127
+ assert_equal false, delegate.requires_authentication?
128
+
129
+ delegate = SmtpDelegate.new(:username => 'test@example.com', :password => 'tester')
130
+
131
+ assert_equal false, delegate.use_tls?
132
+ assert_equal true, delegate.requires_authentication?
133
+ end
134
+
135
+ def test_standard_smtp_connection
136
+ delegate = SmtpDelegate.new
137
+ interpreter = Remailer::Connection::SmtpInterpreter.new(:delegate => delegate)
138
+
139
+ assert_equal :initialized, interpreter.state
140
+
141
+ interpreter.process("220 mail.example.com SMTP Example\r\n")
142
+
143
+ assert_equal :helo, interpreter.state
144
+ assert_equal 'HELO localhost.local', delegate.read
145
+
146
+ interpreter.process("250 mail.example.com Hello\r\n")
147
+ assert_equal :ready, interpreter.state
148
+
149
+ interpreter.enter_state(:quit)
150
+
151
+ assert_equal :quit, interpreter.state
152
+ assert_equal 'QUIT', delegate.read
153
+
154
+ interpreter.process("221 mail.example.com closing connection\r\n")
155
+ assert_equal true, delegate.closed?
156
+ end
157
+
158
+ def test_standard_smtp_connection_send_email
159
+ delegate = SmtpDelegate.new
160
+ interpreter = Remailer::Connection::SmtpInterpreter.new(:delegate => delegate)
161
+
162
+ assert_equal :initialized, interpreter.state
163
+
164
+ interpreter.process("220 mail.example.com SMTP Example\r\n")
165
+
166
+ assert_equal :helo, interpreter.state
167
+ assert_equal 'HELO localhost.local', delegate.read
168
+
169
+ interpreter.process("250 mail.example.com Hello\r\n")
170
+ assert_equal :ready, interpreter.state
171
+
172
+ interpreter.enter_state(:quit)
173
+
174
+ assert_equal :quit, interpreter.state
175
+ assert_equal 'QUIT', delegate.read
176
+
177
+ interpreter.process("221 mail.example.com closing connection\r\n")
178
+ assert_equal true, delegate.closed?
179
+
180
+ delegate.active_message = {
181
+ :from => 'from@example.com',
182
+ :to => 'to@example.com',
183
+ :data => "Subject: Test Message\r\n\r\nThis is a message!\r\n"
184
+ }
185
+
186
+ interpreter.enter_state(:send)
187
+
188
+ assert_equal :mail_from, interpreter.state
189
+
190
+ assert_equal 'MAIL FROM:from@example.com', delegate.read
191
+
192
+ interpreter.process("250 OK\r\n")
193
+
194
+ assert_equal :rcpt_to, interpreter.state
195
+
196
+ assert_equal 'RCPT TO:to@example.com', delegate.read
197
+
198
+ interpreter.process("250 Accepted\r\n")
199
+
200
+ assert_equal :data, interpreter.state
201
+
202
+ assert_equal 'DATA', delegate.read
203
+
204
+ interpreter.process("354 Enter message, ending with \".\" on a line by itself\r\n")
205
+
206
+ assert_equal :sending, interpreter.state
207
+
208
+ interpreter.process("250 OK id=1PN95Q-00072L-Uw\r\n")
209
+
210
+ assert_equal :ready, interpreter.state
211
+ end
212
+
213
+ def test_standard_esmtp_connection
214
+ delegate = SmtpDelegate.new
215
+ interpreter = Remailer::Connection::SmtpInterpreter.new(:delegate => delegate)
216
+
217
+ assert_equal :initialized, interpreter.state
218
+
219
+ interpreter.process("220 mail.example.com ESMTP Exim 4.63\r\n")
220
+
221
+ assert_equal :ehlo, interpreter.state
222
+ assert_equal 'EHLO localhost.local', delegate.read
223
+
224
+ interpreter.process("250-mail.example.com Hello\r\n")
225
+ assert_equal :ehlo, interpreter.state
226
+
227
+ interpreter.process("250-SIZE 52428800\r\n")
228
+ assert_equal :ehlo, interpreter.state
229
+
230
+ interpreter.process("250-PIPELINING\r\n")
231
+ assert_equal :ehlo, interpreter.state
232
+
233
+ interpreter.process("250-STARTTLS\r\n")
234
+ assert_equal :ehlo, interpreter.state
235
+
236
+ interpreter.process("250 HELP\r\n")
237
+ assert_equal :ready, interpreter.state
238
+
239
+ interpreter.enter_state(:quit)
240
+
241
+ assert_equal :quit, interpreter.state
242
+ assert_equal 'QUIT', delegate.read
243
+
244
+ interpreter.process("221 mail.example.com closing connection\r\n")
245
+
246
+ assert_equal :terminated, interpreter.state
247
+ assert_equal true, delegate.closed?
248
+ end
249
+
250
+ def test_tls_connection_with_support
251
+ delegate = SmtpDelegate.new(:use_tls => true)
252
+ interpreter = Remailer::Connection::SmtpInterpreter.new(:delegate => delegate)
253
+
254
+ interpreter.process("220 mail.example.com ESMTP Exim 4.63\r\n")
255
+ assert_equal 'EHLO localhost.local', delegate.read
256
+
257
+ interpreter.process("250-mail.example.com Hello\r\n")
258
+ interpreter.process("250-RANDOMCOMMAND\r\n")
259
+ interpreter.process("250-EXAMPLECOMMAND\r\n")
260
+ interpreter.process("250-SIZE 52428800\r\n")
261
+ interpreter.process("250-PIPELINING\r\n")
262
+ interpreter.process("250-STARTTLS\r\n")
263
+ interpreter.process("250 HELP\r\n")
264
+
265
+ assert_equal :starttls, interpreter.state
266
+ assert_equal 'STARTTLS', delegate.read
267
+ assert_equal false, delegate.started_tls?
268
+
269
+ interpreter.process("220 TLS go ahead\r\n")
270
+ assert_equal true, delegate.started_tls?
271
+
272
+ assert_equal :ready, interpreter.state
273
+ end
274
+
275
+ def test_tls_connection_without_support
276
+ delegate = SmtpDelegate.new(:use_tls => true)
277
+ interpreter = Remailer::Connection::SmtpInterpreter.new(:delegate => delegate)
278
+
279
+ interpreter.process("220 mail.example.com ESMTP Exim 4.63\r\n")
280
+ assert_equal 'EHLO localhost.local', delegate.read
281
+
282
+ interpreter.process("250-mail.example.com Hello\r\n")
283
+ interpreter.process("250 HELP\r\n")
284
+
285
+ assert_equal false, delegate.started_tls?
286
+
287
+ assert_equal :ready, interpreter.state
288
+ end
289
+
290
+ def test_basic_plaintext_auth_accepted
291
+ delegate = SmtpDelegate.new(:username => 'tester@example.com', :password => 'tester')
292
+ interpreter = Remailer::Connection::SmtpInterpreter.new(:delegate => delegate)
293
+
294
+ interpreter.process("220 mail.example.com ESMTP Exim 4.63\r\n")
295
+ assert_equal 'EHLO localhost.local', delegate.read
296
+
297
+ interpreter.process("250-mail.example.com Hello\r\n")
298
+ interpreter.process("250 HELP\r\n")
299
+
300
+ assert_equal false, delegate.started_tls?
301
+
302
+ assert_equal :auth, interpreter.state
303
+ assert_equal "AUTH PLAIN AHRlc3RlckBleGFtcGxlLmNvbQB0ZXN0ZXI=", delegate.read
304
+
305
+ interpreter.process("235 Accepted\r\n")
306
+
307
+ assert_equal :ready, interpreter.state
308
+ end
309
+
310
+ def test_basic_plaintext_auth_rejected
311
+ delegate = SmtpDelegate.new(:username => 'tester@example.com', :password => 'tester')
312
+ interpreter = Remailer::Connection::SmtpInterpreter.new(:delegate => delegate)
313
+
314
+ interpreter.process("220 mx.google.com ESMTP\r\n")
315
+ assert_equal 'EHLO localhost.local', delegate.read
316
+
317
+ interpreter.process("250-mx.google.com at your service\r\n")
318
+ interpreter.process("250 HELP\r\n")
319
+
320
+ assert_equal false, delegate.started_tls?
321
+
322
+ assert_equal :auth, interpreter.state
323
+ assert_equal "AUTH PLAIN AHRlc3RlckBleGFtcGxlLmNvbQB0ZXN0ZXI=", delegate.read
324
+
325
+ interpreter.process("535-5.7.1 Username and Password not accepted. Learn more at\r\n")
326
+ interpreter.process("535 5.7.1 http://mail.google.com/support/bin/answer.py?answer=14257\r\n")
327
+
328
+ assert_equal '5.7.1 Username and Password not accepted. Learn more at http://mail.google.com/support/bin/answer.py?answer=14257', interpreter.error
329
+
330
+ assert_equal :quit, interpreter.state
331
+
332
+ interpreter.process("221 2.0.0 closing connection\r\n")
333
+
334
+ assert_equal :terminated, interpreter.state
335
+ assert_equal true, delegate.closed?
336
+ end
337
+
338
+ def test_unexpected_response
339
+ delegate = SmtpDelegate.new(:username => 'tester@example.com', :password => 'tester')
340
+ interpreter = Remailer::Connection::SmtpInterpreter.new(:delegate => delegate)
341
+
342
+ interpreter.process("530 Go away\r\n")
343
+
344
+ assert_equal :terminated, interpreter.state
345
+ assert_equal true, delegate.closed?
346
+ end
347
+ end
@@ -0,0 +1,116 @@
1
+ require File.expand_path(File.join(*%w[ .. helper ]), File.dirname(__FILE__))
2
+
3
+ class Socks5Delegate
4
+ attr_reader :options
5
+
6
+ def initialize(options = nil)
7
+ @sent = [ ]
8
+ @options = (options or { })
9
+ end
10
+
11
+ def resolve_hostname(hostname)
12
+ record = Socket.gethostbyname(hostname)
13
+
14
+ record and record.last
15
+ end
16
+
17
+ def hostname
18
+ 'localhost.local'
19
+ end
20
+
21
+ def send_data(data)
22
+ @sent << data
23
+ end
24
+
25
+ def close_connection
26
+ @closed = true
27
+ end
28
+
29
+ def closed?
30
+ !!@closed
31
+ end
32
+
33
+ def clear!
34
+ @sent = [ ]
35
+ end
36
+
37
+ def size
38
+ @sent.size
39
+ end
40
+
41
+ def read
42
+ @sent.shift
43
+ end
44
+
45
+ def method_missing(*args)
46
+ end
47
+ end
48
+
49
+ class RemailerConnectionSocks5InterpreterTest < Test::Unit::TestCase
50
+ def test_defaults
51
+ delegate = Socks5Delegate.new(
52
+ :proxy => {
53
+ :host => 'example.net'
54
+ }
55
+ )
56
+ interpreter = Remailer::Connection::Socks5Interpreter.new(:delegate => delegate)
57
+
58
+ assert_equal :connect_to_proxy, interpreter.state
59
+ assert_equal false, delegate.closed?
60
+ end
61
+
62
+ def test_simple_connection
63
+ delegate = Socks5Delegate.new(
64
+ :host => '1.2.3.4',
65
+ :port => 4321,
66
+ :proxy => {
67
+ :host => 'example.net'
68
+ }
69
+ )
70
+ interpreter = Remailer::Connection::Socks5Interpreter.new(:delegate => delegate)
71
+
72
+ assert_equal :connect_to_proxy, interpreter.state
73
+ assert_equal false, delegate.closed?
74
+
75
+ sent = delegate.read
76
+
77
+ assert_equal 2, sent.length
78
+
79
+ assert_equal [ Remailer::Connection::Socks5Interpreter::SOCKS5_VERSION, 0 ], sent.unpack('CC')
80
+
81
+ reply = [
82
+ Remailer::Connection::Socks5Interpreter::SOCKS5_VERSION,
83
+ Remailer::Connection::Socks5Interpreter::SOCKS5_METHOD[:no_auth]
84
+ ].pack('CC')
85
+
86
+ interpreter.process(reply)
87
+
88
+ assert_equal false, interpreter.error?
89
+ assert_equal :connect_through_proxy, interpreter.state
90
+ assert_equal '', reply
91
+
92
+ sent = delegate.read
93
+
94
+ assert_equal 10, sent.length
95
+
96
+ assert_equal [
97
+ Remailer::Connection::Socks5Interpreter::SOCKS5_VERSION,
98
+ Remailer::Connection::Socks5Interpreter::SOCKS5_COMMAND[:connect],
99
+ 0,
100
+ Remailer::Connection::Socks5Interpreter::SOCKS5_ADDRESS_TYPE[:ipv4],
101
+ [ 1, 2, 3, 4 ].pack('CCCC'),
102
+ 4321
103
+ ], sent.unpack('CCCCA4n')
104
+
105
+ interpreter.process([
106
+ Remailer::Connection::Socks5Interpreter::SOCKS5_VERSION,
107
+ 0, # No error
108
+ 0,
109
+ Remailer::Connection::Socks5Interpreter::SOCKS5_ADDRESS_TYPE[:ipv4],
110
+ [ 1, 2, 3, 4 ].pack('CCCC'),
111
+ 4321
112
+ ].pack('CCCCA4n'))
113
+
114
+ assert_equal :connected, interpreter.state
115
+ end
116
+ end