remailer 0.5.2 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c66f7fc5cd78fcb8115342274f77169e4658cfb575d6b2609d2ae07bbd7278a0
4
+ data.tar.gz: 277347ffde8646bb32f6dc01cddca71be940c21fc46f5cfe32ecb4d02c849065
5
+ SHA512:
6
+ metadata.gz: 6352a8dddf0f8cf1cd16cb810af0993a7bbd3386fe0ccc28c20fc9d891a9a9d4ae69e98b106f6d3a31933620364bb88f77217c2db1103400607333e9f5bcec71
7
+ data.tar.gz: 3b935c992a65b812b268cd616353886b104ba7c3cb97009c6234bae1e57d685d381c466219dd357542d6a424ddfe643f9648a8a644797f35806170e166cdfdd8
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ source 'https://rubygems.org/'
2
+
3
+ gem 'eventmachine'
4
+
5
+ group :development do
6
+ gem 'minitest'
7
+ gem 'minitest-reporters'
8
+ gem 'juwelier'
9
+ end
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2010-2019 Scott Tadman, PostageApp Ltd.
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19
+ SOFTWARE.
@@ -1,8 +1,8 @@
1
- = remailer
1
+ # remailer
2
2
 
3
3
  Client/Server Mail Networking Library for SMTP and IMAP
4
4
 
5
- == Overview
5
+ ## Overview
6
6
 
7
7
  This is an EventMachine Connection implementation of a high-performance
8
8
  asynchronous SMTP client. Although EventMachine ships with a built-in SMTP
@@ -10,7 +10,7 @@ client, that version is limited to sending a single email per client,
10
10
  and since establishing a client can be the majority of the time required
11
11
  to send email, this limits throughput considerably.
12
12
 
13
- == Use
13
+ ## Use
14
14
 
15
15
  The Remailer system consists of the Remailer::Connection class which works
16
16
  within the EventMachine environment. To use it, create a client and then
@@ -21,7 +21,7 @@ make one or more requests to send email messages.
21
21
  # messages to STDERR.
22
22
  client = Remailer::SMTP::Client.open(
23
23
  'smtp.google.com',
24
- :debug => STDERR
24
+ debug: STDERR
25
25
  )
26
26
 
27
27
  # Send a single email message through the client at the earliest
@@ -51,7 +51,7 @@ example is given here where the information is simply dumped on STDOUT:
51
51
 
52
52
  client = Remailer::SMTP::Client.open(
53
53
  'smtp.google.com',
54
- :debug => lambda { |type, message|
54
+ debug: lambda { |type, message|
55
55
  puts "#{type}> #{message.inspect}"
56
56
  }
57
57
  )
@@ -90,18 +90,19 @@ server. Success is defined as 250, errors vary:
90
90
 
91
91
  A status code of nil is sent if the server timed out or the connection failed.
92
92
 
93
- == Tests
93
+ ## Tests
94
94
 
95
- In order to run tests, copy `test/config.example.rb` to `test/config.rb` and
95
+ In order to run tests, copy `test/config.example.yml` to `test/config.yml` and
96
96
  adjust as required. For obvious reasons, passwords to SMTP test accounts are
97
- not included in the source code of this library.
97
+ not included in the source code of this library. Any Gmail-type account should
98
+ serve as a useful test target.
98
99
 
99
- == Status
100
+ ## Status
100
101
 
101
102
  This software is currently experimental and is not recommended for production
102
103
  use. Many of the internals may change significantly before a proper beta
103
104
  release is made.
104
105
 
105
- == Copyright
106
+ ## Copyright
106
107
 
107
- Copyright (c) 2010-2012 Scott Tadman, The Working Group
108
+ Copyright (c) 2010-2019 Scott Tadman, PostageApp Ltd.
data/Rakefile CHANGED
@@ -1,29 +1,39 @@
1
1
  require 'rubygems'
2
2
  require 'rake'
3
3
 
4
+ require 'bundler/setup'
5
+
6
+ Bundler.require
7
+
4
8
  begin
5
- require 'jeweler'
6
- Jeweler::Tasks.new do |gem|
9
+ require 'juwelier'
10
+
11
+ Juwelier::Tasks.new do |gem|
7
12
  gem.name = "remailer"
8
13
  gem.summary = %Q{Reactor-Ready SMTP Mailer}
9
- gem.description = %Q{EventMachine SMTP Mail User Agent}
10
- gem.email = "scott@twg.ca"
11
- gem.homepage = "http://github.com/twg/remailer"
14
+ gem.description = %Q{EventMachine Mail Agent for SMTP and IMAP}
15
+ gem.email = "tadman@postageapp.com"
16
+ gem.homepage = "http://github.com/postageapp/remailer"
12
17
  gem.authors = [ "Scott Tadman" ]
13
- gem.add_runtime_dependency 'eventmachine'
18
+
19
+ gem.files.exclude(
20
+ '.travis.yml',
21
+ 'test/config.yml.enc'
22
+ )
14
23
  end
15
- Jeweler::GemcutterTasks.new
24
+
25
+ Juwelier::GemcutterTasks.new
26
+
16
27
  rescue LoadError
17
- puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
28
+ puts "Juwelier (or a dependency) not available. Install it with: gem install Juwelier"
18
29
  end
19
30
 
20
31
  require 'rake/testtask'
32
+
21
33
  Rake::TestTask.new(:test) do |test|
22
34
  test.libs << 'lib' << 'test'
23
35
  test.pattern = 'test/**/*_test.rb'
24
36
  test.verbose = true
25
37
  end
26
38
 
27
- task :test => :check_dependencies
28
-
29
- task :default => :test
39
+ task default: :test
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.2
1
+ 0.9.1
@@ -83,7 +83,7 @@ class Remailer::AbstractConnection < EventMachine::Connection
83
83
  EventMachine.connect(host_name, host_port, self, options)
84
84
 
85
85
  rescue EventMachine::ConnectionError => e
86
- report_exception(e, options)
86
+ self.report_exception(e, options)
87
87
 
88
88
  false
89
89
  end
@@ -133,6 +133,14 @@ class Remailer::AbstractConnection < EventMachine::Connection
133
133
  @options = options
134
134
  @hostname = @options[:hostname] || Socket.gethostname
135
135
  @timeout = @options[:timeout] || self.class.default_timeout
136
+ @timed_out = false
137
+
138
+ @active_message = nil
139
+ @established = false
140
+ @connected = false
141
+ @closed = false
142
+ @unbound = false
143
+ @connecting_to_proxy = false
136
144
 
137
145
  @messages = [ ]
138
146
 
@@ -151,9 +159,19 @@ class Remailer::AbstractConnection < EventMachine::Connection
151
159
  self.after_initialize
152
160
 
153
161
  rescue Object => e
154
- STDERR.puts "#{e.class}: #{e}"
162
+ self.class.report_exception(e, @options)
163
+
164
+ STDERR.puts "#{e.class}: #{e}" rescue nil
155
165
  end
156
-
166
+
167
+ def after_complete(&block)
168
+ if (block_given?)
169
+ @options[:after_complete] = block
170
+ elsif (@options[:after_complete])
171
+ @options[:after_complete].call
172
+ end
173
+ end
174
+
157
175
  # Returns true if the connection requires TLS support, or false otherwise.
158
176
  def use_tls?
159
177
  !!@options[:use_tls]
@@ -166,9 +184,15 @@ class Remailer::AbstractConnection < EventMachine::Connection
166
184
  end
167
185
 
168
186
  # Returns true if the connection has advertised authentication support, or
169
- # false if not availble or could not be detected.
170
- def auth_support?
171
- !!@auth_support
187
+ # false if not availble or could not be detected. If type is specified,
188
+ # returns true only if that type is supported, false otherwise.
189
+ def auth_support?(type = nil)
190
+ case (type)
191
+ when nil
192
+ !!@auth_support
193
+ else
194
+ !!(@auth_support&.include?(type))
195
+ end
172
196
  end
173
197
 
174
198
  # Returns true if the connection will be using a proxy to connect, false
@@ -229,11 +253,11 @@ class Remailer::AbstractConnection < EventMachine::Connection
229
253
 
230
254
  # This implements the EventMachine::Connection#receive_data method that
231
255
  # is called each time new data is received from the socket.
232
- def receive_data(data)
256
+ def receive_data(data = nil)
233
257
  reset_timeout!
234
258
 
235
259
  @buffer ||= ''
236
- @buffer << data
260
+ @buffer << data if (data)
237
261
 
238
262
  if (interpreter = @interpreter)
239
263
  interpreter.process(@buffer) do |reply|
@@ -244,7 +268,10 @@ class Remailer::AbstractConnection < EventMachine::Connection
244
268
  end
245
269
 
246
270
  rescue Object => e
247
- STDERR.puts("[#{e.class}] #{e}")
271
+ self.class.report_exception(e, @options)
272
+ STDERR.puts("[#{e.class}] #{e}") rescue nil
273
+
274
+ raise e
248
275
  end
249
276
 
250
277
  def post_init
@@ -368,12 +395,6 @@ class Remailer::AbstractConnection < EventMachine::Connection
368
395
  def error?
369
396
  !!@error
370
397
  end
371
-
372
- # EventMachine: Enables TLS support on the connection.
373
- def start_tls
374
- debug_notification(:tls, "Started")
375
- super
376
- end
377
398
 
378
399
  # EventMachine: Closes down the connection.
379
400
  def close_connection
@@ -399,11 +420,6 @@ class Remailer::AbstractConnection < EventMachine::Connection
399
420
 
400
421
  reset_timeout!
401
422
  end
402
-
403
- # Switches to use the SOCKS5 interpreter for all subsequent communication
404
- def use_socks5_interpreter!
405
- @interpreter = Remailer::SOCKS5::Client::Interpreter.new(:delegate => self)
406
- end
407
423
 
408
424
  # -- Callbacks and Notifications ------------------------------------------
409
425
 
@@ -192,7 +192,7 @@ class Remailer::IMAP::Client < Remailer::AbstractConnection
192
192
  protected
193
193
  # Switches to use the IMAP interpreter for all subsequent communication
194
194
  def use_imap_interpreter!
195
- @interpreter = Interpreter.new(:delegate => self)
195
+ @interpreter = Interpreter.new(delegate: self)
196
196
  end
197
197
 
198
198
  def next_tag
@@ -19,7 +19,7 @@ class Remailer::Interpreter
19
19
 
20
20
  # Defines the initial state for objects of this class.
21
21
  def self.initial_state
22
- @initial_state || :initialized
22
+ @initial_state ||= :initialized
23
23
  end
24
24
 
25
25
  # Can be used to reassign the initial state for this class. May be easier
@@ -27,21 +27,37 @@ class Remailer::Interpreter
27
27
  def self.initial_state=(state)
28
28
  @initial_state = state
29
29
  end
30
+
31
+ def self.config
32
+ {
33
+ states: @states,
34
+ default_interpreter: @default,
35
+ default_parser: @parser,
36
+ on_error_handler: @on_error
37
+ }
38
+ end
30
39
 
40
+ def self.states_default
41
+ {
42
+ :initialized => { },
43
+ :terminated => { }
44
+ }
45
+ end
46
+
31
47
  # Returns the states that are defined as a has with their associated
32
48
  # options. The default keys are :initialized and :terminated.
33
49
  def self.states
34
50
  @states ||=
35
- case (superclass.respond_to?(:states))
36
- when true
51
+ if (superclass.respond_to?(:states))
37
52
  superclass.states.dup
38
53
  else
39
- {
40
- :initialized => { },
41
- :terminated => { }
42
- }
54
+ self.states_default
43
55
  end
44
56
  end
57
+
58
+ def self.states_empty?
59
+ self.states == self.states_default
60
+ end
45
61
 
46
62
  # Returns true if a given state is defined, false otherwise.
47
63
  def self.state_defined?(state)
@@ -66,15 +82,15 @@ class Remailer::Interpreter
66
82
  end
67
83
 
68
84
  # This is a method to convert a spec and a block into a proper parser
69
- # method. If spec is specified, it should be a Fixnum, or a Regexp. A
70
- # Fixnum defines a minimum size to process, useful for packed binary
85
+ # method. If spec is specified, it should be a Integer, or a Regexp. A
86
+ # Integer defines a minimum size to process, useful for packed binary
71
87
  # streams, while a Regexp defines a pattern that must match before the
72
88
  # parser is engaged.
73
89
  def self.create_parser_for_spec(spec, &block)
74
90
  case (spec)
75
91
  when nil
76
92
  block
77
- when Fixnum
93
+ when Integer
78
94
  lambda do |s|
79
95
  if (s.length >= spec)
80
96
  part = s.slice!(0, spec)
@@ -114,8 +130,7 @@ class Remailer::Interpreter
114
130
  # Returns the parser used when no state-specific parser has been defined.
115
131
  def self.default_parser
116
132
  @parser ||=
117
- case (superclass.respond_to?(:default_parser))
118
- when true
133
+ if (superclass.respond_to?(:default_parser))
119
134
  superclass.default_parser
120
135
  else
121
136
  lambda { |s| _s = s.dup; s.replace(''); _s }
@@ -125,8 +140,7 @@ class Remailer::Interpreter
125
140
  # Returns the current default_interpreter.
126
141
  def self.default_interpreter
127
142
  @default ||=
128
- case (superclass.respond_to?(:default_interpreter))
129
- when true
143
+ if (superclass.respond_to?(:default_interpreter))
130
144
  superclass.default_interpreter
131
145
  else
132
146
  nil
@@ -136,8 +150,7 @@ class Remailer::Interpreter
136
150
  # Returns the defined error handler
137
151
  def self.on_error_handler
138
152
  @on_error ||=
139
- case (superclass.respond_to?(:on_error_handler))
140
- when true
153
+ if (superclass.respond_to?(:on_error_handler))
141
154
  superclass.on_error_handler
142
155
  else
143
156
  nil
@@ -155,6 +168,8 @@ class Remailer::Interpreter
155
168
  # before the first state is entered.
156
169
  def initialize(options = nil)
157
170
  @delegate = (options and options[:delegate])
171
+ @state = nil
172
+ @error = nil
158
173
 
159
174
  yield(self) if (block_given?)
160
175
 
@@ -212,8 +227,8 @@ class Remailer::Interpreter
212
227
  yield(parsed) if (block_given?)
213
228
 
214
229
  interpret(*parsed)
215
-
216
- break if (s.empty?)
230
+
231
+ break if (s.empty? or self.finished?)
217
232
  end
218
233
  end
219
234
 
@@ -245,7 +260,7 @@ class Remailer::Interpreter
245
260
  match_result = match_result.to_a
246
261
 
247
262
  if (match_result.length > 1)
248
- match_string = match_result.shift
263
+ match_result.shift
249
264
  args[0, 1] = match_result
250
265
  else
251
266
  args[0].sub!(match_result[0], '')
@@ -290,6 +305,12 @@ class Remailer::Interpreter
290
305
  def will_interpret?(proc, args)
291
306
  true
292
307
  end
308
+
309
+ # Should return true if this interpreter no longer wants any data, false
310
+ # otherwise. Subclasses should implement their own behavior here.
311
+ def finished?
312
+ false
313
+ end
293
314
 
294
315
  # Returns true if an error has been generated, false otherwise. The error
295
316
  # content can be retrived by calling error.
@@ -14,7 +14,7 @@ class Remailer::Interpreter::StateProxy
14
14
 
15
15
  # Defines a parser specification.
16
16
  def parse(spec = nil, &block)
17
- @options[:parser] = Remailer::Interpreter.parse(spec, &block)
17
+ @options[:parser] = Remailer::Interpreter.create_parser_for_spec(spec, &block)
18
18
  end
19
19
 
20
20
  # Defines a block that will execute when the state is entered.
@@ -60,6 +60,8 @@ class Remailer::SMTP::Client < Remailer::AbstractConnection
60
60
  # Called by AbstractConnection at the end of the initialize procedure
61
61
  def after_initialize
62
62
  @protocol = :smtp
63
+ @error = nil
64
+ @tls_support = nil
63
65
 
64
66
  if (using_proxy?)
65
67
  proxy_connection_initiated!
@@ -69,6 +71,11 @@ class Remailer::SMTP::Client < Remailer::AbstractConnection
69
71
  end
70
72
  end
71
73
 
74
+ # Switches to use the SOCKS5 interpreter for all subsequent communication
75
+ def use_socks5_interpreter!
76
+ @interpreter = Remailer::SOCKS5::Client::Interpreter.new(delegate: self)
77
+ end
78
+
72
79
  # Closes the connection after all of the queued messages have been sent.
73
80
  def close_when_complete!
74
81
  @options[:close] = true
@@ -83,10 +90,10 @@ class Remailer::SMTP::Client < Remailer::AbstractConnection
83
90
  end
84
91
 
85
92
  message = {
86
- :from => from,
87
- :to => to,
88
- :data => data,
89
- :callback => block
93
+ from: from,
94
+ to: to,
95
+ data: data,
96
+ callback: block
90
97
  }
91
98
 
92
99
  @messages << message
@@ -108,10 +115,10 @@ class Remailer::SMTP::Client < Remailer::AbstractConnection
108
115
  end
109
116
 
110
117
  message = {
111
- :from => from,
112
- :to => to,
113
- :test => true,
114
- :callback => block
118
+ from: from,
119
+ to: to,
120
+ test: true,
121
+ callback: block
115
122
  }
116
123
 
117
124
  @messages << message
@@ -146,23 +153,6 @@ class Remailer::SMTP::Client < Remailer::AbstractConnection
146
153
  def unbound?
147
154
  !!@unbound
148
155
  end
149
-
150
- # This implements the EventMachine::Connection#receive_data method that
151
- # is called each time new data is received from the socket.
152
- def receive_data(data)
153
- reset_timeout!
154
-
155
- @buffer ||= ''
156
- @buffer << data
157
-
158
- if (interpreter = @interpreter)
159
- interpreter.process(@buffer) do |reply|
160
- debug_notification(:receive, "[#{interpreter.label}] #{reply.inspect}")
161
- end
162
- else
163
- error_notification(:out_of_band, "Receiving data before a protocol has been established.")
164
- end
165
- end
166
156
 
167
157
  # Returns the current state of the active interpreter, or nil if no state
168
158
  # is assigned.
@@ -227,7 +217,11 @@ class Remailer::SMTP::Client < Remailer::AbstractConnection
227
217
 
228
218
  # Switches to use the SMTP interpreter for all subsequent communication
229
219
  def use_smtp_interpreter!
230
- @interpreter = Interpreter.new(:delegate => self)
220
+ @interpreter = Interpreter.new(delegate: self)
221
+
222
+ # Trigger processing using the new interpreter as some SMTP data might
223
+ # already be buffered by this point.
224
+ receive_data
231
225
  end
232
226
 
233
227
  # Callback receiver for when the proxy connection has been completed.