remailer 0.5.2 → 0.9.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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