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.
@@ -1,3 +1,6 @@
1
+ require 'base64'
2
+ require 'set'
3
+
1
4
  class Remailer::SMTP::Client::Interpreter < Remailer::Interpreter
2
5
  # == Constants ============================================================
3
6
 
@@ -22,7 +25,7 @@ class Remailer::SMTP::Client::Interpreter < Remailer::Interpreter
22
25
  # Encodes the given data for an RFC5321-compliant stream where lines with
23
26
  # leading period chracters are escaped.
24
27
  def self.encode_data(data)
25
- data.gsub(/((?:\r\n|\n)\.)/m, '\\1.')
28
+ data.gsub(/\r?\n/, "\r\n").gsub(/\r\n\./, "\r\n..")
26
29
  end
27
30
 
28
31
  # Encodes a string in Base64 as a single line
@@ -37,6 +40,10 @@ class Remailer::SMTP::Client::Interpreter < Remailer::Interpreter
37
40
  end
38
41
 
39
42
  state :initialized do
43
+ enter do
44
+ @tls = false
45
+ end
46
+
40
47
  interpret(220) do |message, continues|
41
48
  message_parts = message.split(/\s+/)
42
49
  delegate.remote = message_parts.first
@@ -68,6 +75,8 @@ class Remailer::SMTP::Client::Interpreter < Remailer::Interpreter
68
75
  state :helo do
69
76
  enter do
70
77
  delegate.send_line("HELO #{delegate.hostname}")
78
+
79
+ delegate.auth_support = Set.new(%i[ plain ])
71
80
  end
72
81
 
73
82
  interpret(250) do
@@ -85,20 +94,17 @@ class Remailer::SMTP::Client::Interpreter < Remailer::Interpreter
85
94
  end
86
95
 
87
96
  interpret(250) do |message, continues|
88
- message_parts = message.split(/\s+/)
97
+ directive, *message_parts = message.split(/\s+/)
89
98
 
90
- case (message_parts[0].to_s.upcase)
99
+ case (directive.to_s.upcase)
91
100
  when 'SIZE'
92
- delegate.max_size = message_parts[1].to_i
101
+ delegate.max_size = message_parts[0].to_i
93
102
  when 'PIPELINING'
94
103
  delegate.pipelining = true
95
104
  when 'STARTTLS'
96
105
  delegate.tls_support = true
97
106
  when 'AUTH'
98
- delegate.auth_support = message_parts[1, message_parts.length].inject({ }) do |h, v|
99
- h[v] = true
100
- h
101
- end
107
+ delegate.auth_support = Set.new(message_parts.map(&:downcase).map(&:to_sym))
102
108
  end
103
109
 
104
110
  unless (continues)
@@ -140,7 +146,19 @@ class Remailer::SMTP::Client::Interpreter < Remailer::Interpreter
140
146
 
141
147
  state :auth do
142
148
  enter do
143
- delegate.send_line("AUTH PLAIN #{self.class.encode_authentication(delegate.options[:username], delegate.options[:password])}")
149
+ if (delegate.auth_support?(:plain))
150
+ delegate.send_line(
151
+ 'AUTH PLAIN %s' % [
152
+ self.class.encode_authentication(
153
+ delegate.options[:username],
154
+ delegate.options[:password]
155
+ )
156
+ ]
157
+ )
158
+ elsif (delegate.auth_support&.include?(:login))
159
+ delegate.send_line('AUTH LOGIN')
160
+ enter_state(:auth_username)
161
+ end
144
162
  end
145
163
 
146
164
  interpret(235) do
@@ -158,7 +176,62 @@ class Remailer::SMTP::Client::Interpreter < Remailer::Interpreter
158
176
  end
159
177
  end
160
178
  end
161
-
179
+
180
+ state :auth_username do
181
+ interpret(334) do
182
+ delegate.send_line(Base64.strict_encode64(delegate.options[:username]))
183
+
184
+ enter_state(:auth_password)
185
+ end
186
+
187
+ interpret(535) do |reply_message, continues|
188
+ handle_reply_continuation(535, reply_message, continues) do |reply_code, reply_message|
189
+ @error = reply_message
190
+
191
+ delegate.debug_notification(:error, "[#{@state}] #{reply_code} #{reply_message}")
192
+ delegate.error_notification(reply_code, reply_message)
193
+
194
+ enter_state(:quit)
195
+ end
196
+ end
197
+ end
198
+
199
+ state :auth_password do
200
+ interpret(334) do
201
+ delegate.send_line(Base64.strict_encode64(delegate.options[:password]))
202
+
203
+ enter_state(:auth_acknowledge)
204
+ end
205
+
206
+ interpret(535) do |reply_message, continues|
207
+ handle_reply_continuation(535, reply_message, continues) do |reply_code, reply_message|
208
+ @error = reply_message
209
+
210
+ delegate.debug_notification(:error, "[#{@state}] #{reply_code} #{reply_message}")
211
+ delegate.error_notification(reply_code, reply_message)
212
+
213
+ enter_state(:quit)
214
+ end
215
+ end
216
+ end
217
+
218
+ state :auth_acknowledge do
219
+ interpret(235) do
220
+ enter_state(:established)
221
+ end
222
+
223
+ interpret(535) do |reply_message, continues|
224
+ handle_reply_continuation(535, reply_message, continues) do |reply_code, reply_message|
225
+ @error = reply_message
226
+
227
+ delegate.debug_notification(:error, "[#{@state}] #{reply_code} #{reply_message}")
228
+ delegate.error_notification(reply_code, reply_message)
229
+
230
+ enter_state(:quit)
231
+ end
232
+ end
233
+ end
234
+
162
235
  state :established do
163
236
  enter do
164
237
  delegate.connect_notification(true)
@@ -251,7 +324,7 @@ class Remailer::SMTP::Client::Interpreter < Remailer::Interpreter
251
324
  end
252
325
  end
253
326
 
254
- interpret(500..599) do |reply_code, reply_message, continues|
327
+ interpret(400..599) do |reply_code, reply_message, continues|
255
328
  handle_reply_continuation(reply_code, reply_message, continues) do |reply_code, reply_message|
256
329
  delegate_call(:after_message_sent, reply_code, reply_message)
257
330
 
@@ -281,7 +354,7 @@ class Remailer::SMTP::Client::Interpreter < Remailer::Interpreter
281
354
  # Ensure that a blank line is sent after the last bit of email content
282
355
  # to ensure that the dot is on its own line.
283
356
  delegate.send_line
284
- delegate.send_line(".")
357
+ delegate.send_line('.')
285
358
  end
286
359
 
287
360
  default do |reply_code, reply_message, continues|
@@ -19,7 +19,7 @@ class Remailer::SMTP::Server < EventMachine::Protocols::LineAndTextProtocol
19
19
 
20
20
  attr_accessor :logger
21
21
  attr_reader :server_name, :quirks
22
- attr_reader :remote_ip, :remote_port
22
+ attr_reader :remote_ip, :remote_port, :remote_name
23
23
  attr_reader :local_ip, :local_port
24
24
  attr_reader :local_config
25
25
 
@@ -60,6 +60,11 @@ class Remailer::SMTP::Server < EventMachine::Protocols::LineAndTextProtocol
60
60
 
61
61
  @server_name = @options[:server_name] || self.class.hostname(@local_ip) || @local_ip
62
62
 
63
+ @logger = nil
64
+ @remote_host = nil
65
+ @tls_support = false
66
+ @interpreter_class = options && options[:interpreter] || Interpreter
67
+
63
68
  log(:debug, "Connection from #{@remote_ip}:#{@remote_port} to #{@local_ip}:#{@local_port}")
64
69
 
65
70
  @on_transaction = @options[:on_transaction]
@@ -69,15 +74,15 @@ class Remailer::SMTP::Server < EventMachine::Protocols::LineAndTextProtocol
69
74
  def post_init
70
75
  super
71
76
 
72
- @interpreter = Interpreter.new(:delegate => self)
77
+ @interpreter = @interpreter_class.new(delegate: self)
73
78
 
74
79
  if (@on_connect)
75
80
  @on_connect.call(@remote_ip)
76
81
  end
77
82
  end
78
83
 
79
- def on_transaction
80
- @on_transaction = Proc.new
84
+ def on_transaction(&block)
85
+ @on_transaction = block
81
86
  end
82
87
 
83
88
  def receive_line(line)
@@ -40,7 +40,7 @@ class Remailer::SMTP::Server::Interpreter < Remailer::Interpreter
40
40
  end
41
41
  end
42
42
 
43
- interpret(/^\s*HELO\s+(\S+)\s*$/) do |remote_host|
43
+ interpret(/^\s*HELO\s+(\S+)\s*$/i) do |remote_host|
44
44
  delegate.validate_hostname(remote_host) do |valid|
45
45
  if (valid)
46
46
  delegate.log(:debug, "#{delegate.remote_ip}:#{delegate.remote_port} to #{delegate.local_ip}:#{delegate.local_port} Accepting connection from #{remote_host}")
@@ -54,7 +54,7 @@ class Remailer::SMTP::Server::Interpreter < Remailer::Interpreter
54
54
  end
55
55
  end
56
56
 
57
- interpret(/^\s*MAIL\s+FROM:\s*<([^>]+)>\s*/) do |address|
57
+ interpret(/^\s*MAIL\s+FROM:\s*<([^>]+)>\s*/i) do |address|
58
58
  if (Remailer::EmailAddress.valid?(address))
59
59
  accept, message = will_accept_sender(address)
60
60
 
@@ -68,7 +68,7 @@ class Remailer::SMTP::Server::Interpreter < Remailer::Interpreter
68
68
  end
69
69
  end
70
70
 
71
- interpret(/^\s*RCPT\s+TO:\s*<([^>]+)>\s*/) do |address|
71
+ interpret(/^\s*RCPT\s+TO:\s*<([^>]+)>\s*/i) do |address|
72
72
  if (@transaction.sender)
73
73
  if (Remailer::EmailAddress.valid?(address))
74
74
  accept, message = will_accept_recipient(address)
@@ -87,24 +87,24 @@ class Remailer::SMTP::Server::Interpreter < Remailer::Interpreter
87
87
  end
88
88
  end
89
89
 
90
- interpret(/^\s*AUTH\s+PLAIN\s+(.*)\s*$/) do |auth|
90
+ interpret(/^\s*AUTH\s+PLAIN\s+(.*)\s*$/i) do |auth|
91
91
  # 235 2.7.0 Authentication successful
92
- delegate.send("235 whatever")
92
+ delegate.send("235 If you insist")
93
93
  end
94
94
 
95
- interpret(/^\s*AUTH\s+PLAIN\s*$/) do
95
+ interpret(/^\s*AUTH\s+PLAIN\s*$/i) do
96
96
  # Multi-line authentication method
97
97
  enter_state(:auth_plain)
98
98
  end
99
99
 
100
- interpret(/^\s*STARTTLS\s*$/) do
100
+ interpret(/^\s*STARTTLS\s*$/i) do
101
101
  if (@tls_started)
102
102
  delegate.send_line("454 TLS already started")
103
103
  elsif (delegate.tls?)
104
104
  delegate.send_line("220 TLS ready to start")
105
105
  delegate.start_tls(
106
- :private_key_file => Remailer::SMTP::Server.private_key_path,
107
- :cert_chain_file => Remailer::SMTP::Server.ssl_cert_path
106
+ private_key_file: Remailer::SMTP::Server.private_key_path,
107
+ cert_chain_file: Remailer::SMTP::Server.ssl_cert_path
108
108
  )
109
109
 
110
110
  @tls_started = true
@@ -113,27 +113,27 @@ class Remailer::SMTP::Server::Interpreter < Remailer::Interpreter
113
113
  end
114
114
  end
115
115
 
116
- interpret(/^\s*DATA\s*$/) do
116
+ interpret(/^\s*DATA\s*$/i) do
117
117
  if (@transaction.sender)
118
118
  else
119
- delegate.send_line("503 valid RCPT command must precede DATA")
119
+ delegate.send_line("503 Valid RCPT command must precede DATA")
120
120
  end
121
121
 
122
122
  enter_state(:data)
123
123
  delegate.send_line("354 Supply message data")
124
124
  end
125
125
 
126
- interpret(/^\s*NOOP\s*$/) do |remote_host|
126
+ interpret(/^\s*NOOP\s*$/i) do |remote_host|
127
127
  delegate.send_line("250 OK")
128
128
  end
129
129
 
130
- interpret(/^\s*RSET\s*$/) do |remote_host|
130
+ interpret(/^\s*RSET\s*$/i) do |remote_host|
131
131
  delegate.send_line("250 Reset OK")
132
132
 
133
133
  enter_state(:reset)
134
134
  end
135
135
 
136
- interpret(/^\s*QUIT\s*$/) do
136
+ interpret(/^\s*QUIT\s*$/i) do
137
137
  delegate.send_line("221 #{delegate.server_name} closing connection")
138
138
 
139
139
  delegate.close_connection(true)
@@ -142,6 +142,8 @@ class Remailer::SMTP::Server::Interpreter < Remailer::Interpreter
142
142
 
143
143
  state :data do
144
144
  interpret(/^\.$/) do
145
+ @transaction.remote_ip = delegate.remote_ip
146
+
145
147
  accept, message = will_accept_transaction(@transaction)
146
148
 
147
149
  if (accept)
@@ -159,7 +161,6 @@ class Remailer::SMTP::Server::Interpreter < Remailer::Interpreter
159
161
 
160
162
  default do |line|
161
163
  # RFC5321 4.5.2 - Leading dot is removed if line has content
162
-
163
164
  @transaction.data << (line.sub(/^\./, '') << Remailer::Constants::CRLF)
164
165
  end
165
166
  end
@@ -1,11 +1,18 @@
1
1
  class Remailer::SMTP::Server::Transaction
2
2
  # == Constants ============================================================
3
3
 
4
- ATTRIBUTES = [ :sender, :recipients, :data ].freeze
4
+ ATTRIBUTES = [
5
+ :sender,
6
+ :remote_ip,
7
+ :remote_name,
8
+ :auth,
9
+ :recipients,
10
+ :data
11
+ ].freeze
5
12
 
6
13
  # == Properties ===========================================================
7
14
 
8
- attr_accessor *ATTRIBUTES
15
+ attr_accessor(*ATTRIBUTES)
9
16
 
10
17
  # == Class Methods ========================================================
11
18
 
@@ -5,14 +5,14 @@ class Remailer::SOCKS5::Client::Interpreter < Remailer::Interpreter
5
5
  SOCKS5_VERSION = 5
6
6
 
7
7
  SOCKS5_METHOD = {
8
- :no_auth => 0,
9
- :gssapi => 1,
10
- :username_password => 2
8
+ no_auth: 0,
9
+ gssapi: 1,
10
+ username_password: 2
11
11
  }.freeze
12
12
 
13
13
  SOCKS5_COMMAND = {
14
- :connect => 1,
15
- :bind => 2
14
+ connect: 1,
15
+ bind: 2
16
16
  }.freeze
17
17
 
18
18
  SOCKS5_REPLY = {
@@ -28,9 +28,9 @@ class Remailer::SOCKS5::Client::Interpreter < Remailer::Interpreter
28
28
  }.freeze
29
29
 
30
30
  SOCKS5_ADDRESS_TYPE = {
31
- :ipv4 => 1,
32
- :domainname => 3,
33
- :ipv6 => 4
31
+ ipv4: 1,
32
+ domainname: 3,
33
+ ipv6: 4
34
34
  }.freeze
35
35
 
36
36
  # == State Mapping ========================================================
@@ -60,7 +60,7 @@ class Remailer::SOCKS5::Client::Interpreter < Remailer::Interpreter
60
60
 
61
61
  parse do |s|
62
62
  if (s.length >= 2)
63
- version, method = s.slice!(0,2).unpack('CC')
63
+ _version, method = s.slice!(0,2).unpack('CC')
64
64
 
65
65
  method
66
66
  end
@@ -109,14 +109,14 @@ class Remailer::SOCKS5::Client::Interpreter < Remailer::Interpreter
109
109
 
110
110
  parse do |s|
111
111
  if (s.length >= 10)
112
- version, reply, reserved, address_type, address, port = s.slice!(0,10).unpack('CCCCNn')
112
+ _version, reply, _reserved, address_type, address, port = s.slice!(0,10).unpack('CCCCNn')
113
113
 
114
114
  [
115
115
  reply,
116
116
  {
117
- :address => address,
118
- :port => port,
119
- :address_type => address_type
117
+ address: address,
118
+ port: port,
119
+ address_type: address_type
120
120
  }
121
121
  ]
122
122
  end
@@ -128,6 +128,7 @@ class Remailer::SOCKS5::Client::Interpreter < Remailer::Interpreter
128
128
 
129
129
  default do |reply|
130
130
  @reply = reply
131
+
131
132
  enter_state(:failed)
132
133
  end
133
134
  end
@@ -189,4 +190,8 @@ class Remailer::SOCKS5::Client::Interpreter < Remailer::Interpreter
189
190
  def label
190
191
  'SOCKS5'
191
192
  end
193
+
194
+ def finished?
195
+ self.state == :connected
196
+ end
192
197
  end
@@ -1,23 +1,28 @@
1
- # Generated by jeweler
1
+ # Generated by juwelier
2
2
  # DO NOT EDIT THIS FILE DIRECTLY
3
- # Instead, edit Jeweler::Tasks in Rakefile, and run 'rake gemspec'
3
+ # Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
4
4
  # -*- encoding: utf-8 -*-
5
+ # stub: remailer 0.9.1 ruby lib
5
6
 
6
7
  Gem::Specification.new do |s|
7
- s.name = "remailer"
8
- s.version = "0.5.2"
8
+ s.name = "remailer".freeze
9
+ s.version = "0.9.1"
9
10
 
10
- s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
- s.authors = ["Scott Tadman"]
12
- s.date = "2012-11-30"
13
- s.description = "EventMachine SMTP Mail User Agent"
14
- s.email = "scott@twg.ca"
11
+ s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
+ s.require_paths = ["lib".freeze]
13
+ s.authors = ["Scott Tadman".freeze]
14
+ s.date = "2020-09-22"
15
+ s.description = "EventMachine Mail Agent for SMTP and IMAP".freeze
16
+ s.email = "tadman@postageapp.com".freeze
15
17
  s.extra_rdoc_files = [
16
- "README.rdoc"
18
+ "LICENSE.txt",
19
+ "README.md"
17
20
  ]
18
21
  s.files = [
19
22
  ".document",
20
- "README.rdoc",
23
+ "Gemfile",
24
+ "LICENSE.txt",
25
+ "README.md",
21
26
  "Rakefile",
22
27
  "VERSION",
23
28
  "lib/remailer.rb",
@@ -42,7 +47,9 @@ Gem::Specification.new do |s|
42
47
  "lib/remailer/socks5/client/interpreter.rb",
43
48
  "lib/remailer/support.rb",
44
49
  "remailer.gemspec",
45
- "test/config.example.rb",
50
+ "test/bin/exercise",
51
+ "test/config.example.yml",
52
+ "test/config.rb",
46
53
  "test/helper.rb",
47
54
  "test/unit/remailer_imap_client_interpreter_test.rb",
48
55
  "test/unit/remailer_imap_client_test.rb",
@@ -54,21 +61,24 @@ Gem::Specification.new do |s|
54
61
  "test/unit/remailer_socks5_client_interpreter_test.rb",
55
62
  "test/unit/remailer_test.rb"
56
63
  ]
57
- s.homepage = "http://github.com/twg/remailer"
58
- s.require_paths = ["lib"]
59
- s.rubygems_version = "1.8.24"
60
- s.summary = "Reactor-Ready SMTP Mailer"
64
+ s.homepage = "http://github.com/postageapp/remailer".freeze
65
+ s.rubygems_version = "3.1.4".freeze
66
+ s.summary = "Reactor-Ready SMTP Mailer".freeze
61
67
 
62
68
  if s.respond_to? :specification_version then
63
- s.specification_version = 3
69
+ s.specification_version = 4
70
+ end
64
71
 
65
- if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
66
- s.add_runtime_dependency(%q<eventmachine>, [">= 0"])
67
- else
68
- s.add_dependency(%q<eventmachine>, [">= 0"])
69
- end
72
+ if s.respond_to? :add_runtime_dependency then
73
+ s.add_runtime_dependency(%q<eventmachine>.freeze, [">= 0"])
74
+ s.add_development_dependency(%q<minitest>.freeze, [">= 0"])
75
+ s.add_development_dependency(%q<minitest-reporters>.freeze, [">= 0"])
76
+ s.add_development_dependency(%q<juwelier>.freeze, [">= 0"])
70
77
  else
71
- s.add_dependency(%q<eventmachine>, [">= 0"])
78
+ s.add_dependency(%q<eventmachine>.freeze, [">= 0"])
79
+ s.add_dependency(%q<minitest>.freeze, [">= 0"])
80
+ s.add_dependency(%q<minitest-reporters>.freeze, [">= 0"])
81
+ s.add_dependency(%q<juwelier>.freeze, [">= 0"])
72
82
  end
73
83
  end
74
84