remailer 0.5.2 → 0.9.1

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,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.