midi-smtp-server 3.1.2 → 3.3.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +15 -0
- data/README.md +20 -4
- data/lib/midi-smtp-server/exceptions.rb +25 -25
- data/lib/midi-smtp-server/version.rb +3 -3
- data/lib/midi-smtp-server.rb +144 -14
- metadata +35 -9
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c8ae466dfca7a6bde0fcc780096da505a675ac4d703641097aa9980423c4976d
|
|
4
|
+
data.tar.gz: 92be87d1e1f857ba910a53f357519dc9a97edf0c89747d875c394f270d587d0b
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 6b57ce0eb75d4c6cdfde8e417e0a93214834c811937dbe093aed941285dc577c4d96e65ee4dfa2639db8349199e1de70d19de14609d423e914cee16a6ee36868
|
|
7
|
+
data.tar.gz: 9aee1d4d53f0b3878022a44ed267a3b9f3b9e570cf3eb96fd2ace0e598c8cb6fc7eb6ca1446948be01e3fa4d12170c9b3d91aade46d826c307e92c3ae6e2c479
|
data/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,21 @@ We suggest everybody using MidiSmtpServer to switch at least to latest 2.3.y. or
|
|
|
5
5
|
For upgrades from previous versions or outdated _MiniSmtpServer_ gem you may follow the guides at [Appendix Upgrade](https://midi-smtp-server.readthedocs.io/appendix_upgrade/) to get your code ready for the latest releases.
|
|
6
6
|
|
|
7
7
|
|
|
8
|
+
#### 3.3.1 (2026-09-16)
|
|
9
|
+
|
|
10
|
+
1. Updates and fixes for Ruby versions 3.3, 3.4 and 4.0
|
|
11
|
+
2. New feature and optimization for [IO wait](https://midi-smtp-server.readthedocs.io/feature_load_balancing) by event mode
|
|
12
|
+
3. Deprecate `io_waitreadable_sleep` in favor of `io_wait_mode` and `io_wait_available`
|
|
13
|
+
4. Test fixes for newer Ruby (3.3+) and OS-specific socket behaviour
|
|
14
|
+
5. mkdocs update for readthedocs
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
#### 3.2.1 (2023-08-15)
|
|
18
|
+
|
|
19
|
+
1. New feature [proxy](https://midi-smtp-server.readthedocs.io/feature_proxy) ([check issue 49](https://github.com/4commerce-technologies-AG/midi-smtp-server/issues/49))
|
|
20
|
+
2. mkdocs update for readthedocs
|
|
21
|
+
|
|
22
|
+
|
|
8
23
|
#### 3.1.2 (2023-05-06)
|
|
9
24
|
|
|
10
25
|
1. Minor fix for backwards compatibility to method `start` when not using pre_fork options.
|
data/README.md
CHANGED
|
@@ -76,7 +76,11 @@ This source code shows the example to receive messages via SMTP and store them t
|
|
|
76
76
|
|
|
77
77
|
## Installation
|
|
78
78
|
|
|
79
|
-
MidiSmtpServer is packaged as a RubyGem
|
|
79
|
+
MidiSmtpServer is packaged as a RubyGem and hosted on rubygems.
|
|
80
|
+
|
|
81
|
+
#### CLI
|
|
82
|
+
|
|
83
|
+
You can easily install the package by entering following at your command line:
|
|
80
84
|
|
|
81
85
|
`gem install midi-smtp-server`
|
|
82
86
|
|
|
@@ -84,6 +88,14 @@ Use the component in your project sources by:
|
|
|
84
88
|
|
|
85
89
|
`require 'midi-smtp-server'`
|
|
86
90
|
|
|
91
|
+
#### Gemfile
|
|
92
|
+
|
|
93
|
+
When a `Gemfile` handles your dependencies, please consider to use the [pessimistic operator](https://thoughtbot.com/blog/rubys-pessimistic-operator) at least:
|
|
94
|
+
|
|
95
|
+
`gem 'midi-smtp-server', '~> 3.1.2''`
|
|
96
|
+
|
|
97
|
+
All changes by PATCH versions are always functional and compatible with no issues on update!
|
|
98
|
+
|
|
87
99
|
<br>
|
|
88
100
|
|
|
89
101
|
|
|
@@ -147,9 +159,13 @@ We suggest everybody using MidiSmtpServer to switch at least to latest 2.3.y. or
|
|
|
147
159
|
For upgrades from previous versions or outdated _MiniSmtpServer_ gem you may follow the guides (see appendix) how to change your existing code to be compatible with the latest releases.
|
|
148
160
|
|
|
149
161
|
|
|
150
|
-
#### Latest release: 3.1
|
|
162
|
+
#### Latest release: 3.3.1 (2026-09-16)
|
|
151
163
|
|
|
152
|
-
1.
|
|
164
|
+
1. Updates and fixes for Ruby versions 3.3, 3.4 and 4.0
|
|
165
|
+
2. New feature and optimization for [IO wait](https://midi-smtp-server.readthedocs.io/feature_load_balancing) by event mode
|
|
166
|
+
3. Deprecate `io_waitreadable_sleep` in favor of `io_wait_mode` and `io_wait_available`
|
|
167
|
+
4. Test fixes for newer Ruby (3.3+) and OS-specific socket behaviour
|
|
168
|
+
5. mkdocs update for readthedocs
|
|
153
169
|
|
|
154
170
|
|
|
155
171
|
#### Changelog history
|
|
@@ -192,4 +208,4 @@ Author: [Tom Freudenberg](http://about.me/tom.freudenberg)
|
|
|
192
208
|
|
|
193
209
|
[MidiSmtpServer Class](https://github.com/4commerce-technologies-AG/midi-smtp-server/) is inspired from [MiniSmtpServer Class](https://github.com/aarongough/mini-smtp-server) and code written by [Aaron Gough](https://github.com/aarongough) and [Peter Cooper](https://github.com/4commerce-technologies-AG/midi-smtp-server#author--credits)
|
|
194
210
|
|
|
195
|
-
Copyright (c) 2014-
|
|
211
|
+
Copyright (c) 2014-2026 [Tom Freudenberg](https://github.com/TomFreudenberg), [4commerce technologies AG](https://www.4commerce.de/), released under the MIT license
|
|
@@ -38,7 +38,7 @@ module MidiSmtpServer
|
|
|
38
38
|
@smtpd_return_code = smtpd_return_code
|
|
39
39
|
@smtpd_return_text = smtpd_return_text
|
|
40
40
|
# call inherited constructor
|
|
41
|
-
super
|
|
41
|
+
super(msg)
|
|
42
42
|
end
|
|
43
43
|
|
|
44
44
|
def smtpd_result
|
|
@@ -52,7 +52,7 @@ module MidiSmtpServer
|
|
|
52
52
|
|
|
53
53
|
def initialize(msg = nil)
|
|
54
54
|
# call inherited constructor
|
|
55
|
-
super
|
|
55
|
+
super(msg, 421, 'Service too busy or not available, closing transmission channel')
|
|
56
56
|
end
|
|
57
57
|
|
|
58
58
|
end
|
|
@@ -63,7 +63,7 @@ module MidiSmtpServer
|
|
|
63
63
|
|
|
64
64
|
def initialize(msg = nil)
|
|
65
65
|
# call inherited constructor
|
|
66
|
-
super
|
|
66
|
+
super(msg, 450, 'Requested mail action not taken: mailbox unavailable')
|
|
67
67
|
end
|
|
68
68
|
|
|
69
69
|
end
|
|
@@ -73,7 +73,7 @@ module MidiSmtpServer
|
|
|
73
73
|
|
|
74
74
|
def initialize(msg = nil)
|
|
75
75
|
# call inherited constructor
|
|
76
|
-
super
|
|
76
|
+
super(msg, 451, 'Requested action aborted: local error in processing')
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
end
|
|
@@ -83,7 +83,7 @@ module MidiSmtpServer
|
|
|
83
83
|
|
|
84
84
|
def initialize(msg = nil)
|
|
85
85
|
# call inherited constructor
|
|
86
|
-
super
|
|
86
|
+
super(msg, 452, 'Requested action not taken: insufficient system storage')
|
|
87
87
|
end
|
|
88
88
|
|
|
89
89
|
end
|
|
@@ -94,7 +94,7 @@ module MidiSmtpServer
|
|
|
94
94
|
|
|
95
95
|
def initialize(msg = nil)
|
|
96
96
|
# call inherited constructor
|
|
97
|
-
super
|
|
97
|
+
super(msg, 500, 'Syntax error, command unrecognised or error in parameters or arguments')
|
|
98
98
|
end
|
|
99
99
|
|
|
100
100
|
end
|
|
@@ -104,7 +104,7 @@ module MidiSmtpServer
|
|
|
104
104
|
|
|
105
105
|
def initialize(msg = nil)
|
|
106
106
|
# call inherited constructor
|
|
107
|
-
super
|
|
107
|
+
super(msg, 501, 'Syntax error in parameters or arguments')
|
|
108
108
|
end
|
|
109
109
|
|
|
110
110
|
end
|
|
@@ -114,7 +114,7 @@ module MidiSmtpServer
|
|
|
114
114
|
|
|
115
115
|
def initialize(msg = nil)
|
|
116
116
|
# call inherited constructor
|
|
117
|
-
super
|
|
117
|
+
super(msg, 502, 'Command not implemented')
|
|
118
118
|
end
|
|
119
119
|
|
|
120
120
|
end
|
|
@@ -124,7 +124,7 @@ module MidiSmtpServer
|
|
|
124
124
|
|
|
125
125
|
def initialize(msg = nil)
|
|
126
126
|
# call inherited constructor
|
|
127
|
-
super
|
|
127
|
+
super(msg, 503, 'Bad sequence of commands')
|
|
128
128
|
end
|
|
129
129
|
|
|
130
130
|
end
|
|
@@ -134,7 +134,7 @@ module MidiSmtpServer
|
|
|
134
134
|
|
|
135
135
|
def initialize(msg = nil)
|
|
136
136
|
# call inherited constructor
|
|
137
|
-
super
|
|
137
|
+
super(msg, 504, 'Command parameter not implemented')
|
|
138
138
|
end
|
|
139
139
|
|
|
140
140
|
end
|
|
@@ -144,7 +144,7 @@ module MidiSmtpServer
|
|
|
144
144
|
|
|
145
145
|
def initialize(msg = nil)
|
|
146
146
|
# call inherited constructor
|
|
147
|
-
super
|
|
147
|
+
super(msg, 521, 'Service does not accept mail')
|
|
148
148
|
end
|
|
149
149
|
|
|
150
150
|
end
|
|
@@ -155,7 +155,7 @@ module MidiSmtpServer
|
|
|
155
155
|
|
|
156
156
|
def initialize(msg = nil)
|
|
157
157
|
# call inherited constructor
|
|
158
|
-
super
|
|
158
|
+
super(msg, 550, 'Requested action not taken: mailbox unavailable')
|
|
159
159
|
end
|
|
160
160
|
|
|
161
161
|
end
|
|
@@ -165,7 +165,7 @@ module MidiSmtpServer
|
|
|
165
165
|
|
|
166
166
|
def initialize(msg = nil)
|
|
167
167
|
# call inherited constructor
|
|
168
|
-
super
|
|
168
|
+
super(msg, 552, 'Requested mail action aborted: exceeded storage allocation')
|
|
169
169
|
end
|
|
170
170
|
|
|
171
171
|
end
|
|
@@ -175,7 +175,7 @@ module MidiSmtpServer
|
|
|
175
175
|
|
|
176
176
|
def initialize(msg = nil)
|
|
177
177
|
# call inherited constructor
|
|
178
|
-
super
|
|
178
|
+
super(msg, 553, 'Requested action not taken: mailbox name not allowed')
|
|
179
179
|
end
|
|
180
180
|
|
|
181
181
|
end
|
|
@@ -185,7 +185,7 @@ module MidiSmtpServer
|
|
|
185
185
|
|
|
186
186
|
def initialize(msg = nil)
|
|
187
187
|
# call inherited constructor
|
|
188
|
-
super
|
|
188
|
+
super(msg, 554, 'Transaction failed')
|
|
189
189
|
end
|
|
190
190
|
|
|
191
191
|
end
|
|
@@ -197,7 +197,7 @@ module MidiSmtpServer
|
|
|
197
197
|
|
|
198
198
|
def initialize(msg = nil)
|
|
199
199
|
# call inherited constructor
|
|
200
|
-
super
|
|
200
|
+
super(msg, 432, 'Password transition is needed')
|
|
201
201
|
end
|
|
202
202
|
|
|
203
203
|
end
|
|
@@ -207,7 +207,7 @@ module MidiSmtpServer
|
|
|
207
207
|
|
|
208
208
|
def initialize(msg = nil)
|
|
209
209
|
# call inherited constructor
|
|
210
|
-
super
|
|
210
|
+
super(msg, 454, 'Temporary authentication failure')
|
|
211
211
|
end
|
|
212
212
|
|
|
213
213
|
end
|
|
@@ -217,7 +217,7 @@ module MidiSmtpServer
|
|
|
217
217
|
|
|
218
218
|
def initialize(msg = nil)
|
|
219
219
|
# call inherited constructor
|
|
220
|
-
super
|
|
220
|
+
super(msg, 530, 'Authentication required')
|
|
221
221
|
end
|
|
222
222
|
|
|
223
223
|
end
|
|
@@ -227,7 +227,7 @@ module MidiSmtpServer
|
|
|
227
227
|
|
|
228
228
|
def initialize(msg = nil)
|
|
229
229
|
# call inherited constructor
|
|
230
|
-
super
|
|
230
|
+
super(msg, 534, 'Authentication mechanism is too weak')
|
|
231
231
|
end
|
|
232
232
|
|
|
233
233
|
end
|
|
@@ -237,7 +237,7 @@ module MidiSmtpServer
|
|
|
237
237
|
|
|
238
238
|
def initialize(msg = nil)
|
|
239
239
|
# call inherited constructor
|
|
240
|
-
super
|
|
240
|
+
super(msg, 535, 'Authentication credentials invalid')
|
|
241
241
|
end
|
|
242
242
|
|
|
243
243
|
end
|
|
@@ -247,7 +247,7 @@ module MidiSmtpServer
|
|
|
247
247
|
|
|
248
248
|
def initialize(msg = nil)
|
|
249
249
|
# call inherited constructor
|
|
250
|
-
super
|
|
250
|
+
super(msg, 538, 'Encryption required for requested authentication mechanism')
|
|
251
251
|
end
|
|
252
252
|
|
|
253
253
|
end
|
|
@@ -259,7 +259,7 @@ module MidiSmtpServer
|
|
|
259
259
|
|
|
260
260
|
def initialize(msg = nil)
|
|
261
261
|
# call inherited constructor
|
|
262
|
-
super
|
|
262
|
+
super(msg, 454, 'TLS not available')
|
|
263
263
|
end
|
|
264
264
|
|
|
265
265
|
end
|
|
@@ -269,7 +269,7 @@ module MidiSmtpServer
|
|
|
269
269
|
|
|
270
270
|
def initialize(msg = nil)
|
|
271
271
|
# call inherited constructor
|
|
272
|
-
super
|
|
272
|
+
super(msg, 530, 'Encryption required, must issue STARTTLS command first')
|
|
273
273
|
end
|
|
274
274
|
|
|
275
275
|
end
|
|
@@ -281,7 +281,7 @@ module MidiSmtpServer
|
|
|
281
281
|
|
|
282
282
|
def initialize(msg = nil)
|
|
283
283
|
# call inherited constructor
|
|
284
|
-
super
|
|
284
|
+
super(msg, 500, 'Bad input, PIPELINING is not allowed')
|
|
285
285
|
end
|
|
286
286
|
|
|
287
287
|
end
|
|
@@ -293,7 +293,7 @@ module MidiSmtpServer
|
|
|
293
293
|
|
|
294
294
|
def initialize(msg = nil)
|
|
295
295
|
# call inherited constructor
|
|
296
|
-
super
|
|
296
|
+
super(msg, 500, 'Bad input, Lines must be terminated by CRLF sequence')
|
|
297
297
|
end
|
|
298
298
|
|
|
299
299
|
end
|
data/lib/midi-smtp-server.rb
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
require 'logger'
|
|
4
4
|
require 'socket'
|
|
5
|
+
require 'io/wait'
|
|
5
6
|
require 'resolv'
|
|
6
7
|
require 'base64'
|
|
7
8
|
|
|
@@ -27,12 +28,15 @@ module MidiSmtpServer
|
|
|
27
28
|
DEFAULT_CRLF_MODE = :CRLF_ENSURE
|
|
28
29
|
|
|
29
30
|
# default values for IO operations
|
|
30
|
-
|
|
31
|
+
IO_WAIT_MODES = [:IO_WAIT_SLEEP, :IO_WAIT_EVENT].freeze
|
|
32
|
+
DEFAULT_IO_WAIT_MODE = :IO_WAIT_EVENT
|
|
33
|
+
DEFAULT_IO_WAIT_AVAILABLE = 0.1
|
|
31
34
|
DEFAULT_IO_CMD_TIMEOUT = 30
|
|
32
35
|
DEFAULT_IO_BUFFER_CHUNK_SIZE = 4 * 1024
|
|
33
36
|
DEFAULT_IO_BUFFER_MAX_SIZE = 1 * 1024 * 1024
|
|
34
37
|
|
|
35
38
|
# default value for SMTPD extensions support
|
|
39
|
+
DEFAULT_PROXY_EXTENSION_ENABLED = false
|
|
36
40
|
DEFAULT_PIPELINING_EXTENSION_ENABLED = false
|
|
37
41
|
DEFAULT_INTERNATIONALIZATION_EXTENSIONS_ENABLED = false
|
|
38
42
|
|
|
@@ -196,8 +200,10 @@ module MidiSmtpServer
|
|
|
196
200
|
attr_reader :max_connections
|
|
197
201
|
# CRLF handling based on conformity to RFC(2)822
|
|
198
202
|
attr_reader :crlf_mode
|
|
199
|
-
#
|
|
200
|
-
attr_reader :
|
|
203
|
+
# how to wait on IO::WaitReadable exception
|
|
204
|
+
attr_reader :io_wait_mode
|
|
205
|
+
# Time in fraction of a second to wait on IO::WaitReadable exception
|
|
206
|
+
attr_reader :io_wait_available
|
|
201
207
|
# Maximum time in seconds to wait for a complete incoming data line, as a FixNum
|
|
202
208
|
attr_reader :io_cmd_timeout
|
|
203
209
|
# Bytes to read non-blocking from socket into buffer, as a FixNum
|
|
@@ -214,6 +220,8 @@ module MidiSmtpServer
|
|
|
214
220
|
attr_reader :pipelining_extension
|
|
215
221
|
# handle SMTP 8BITMIME and SMTPUTF8 extension
|
|
216
222
|
attr_reader :internationalization_extensions
|
|
223
|
+
# handle PROXY connections
|
|
224
|
+
attr_reader :proxy_extension
|
|
217
225
|
|
|
218
226
|
# logging object, may be overridden by special loggers like YELL or others
|
|
219
227
|
attr_reader :logger
|
|
@@ -227,12 +235,14 @@ module MidiSmtpServer
|
|
|
227
235
|
# +max_connections+:: maximum number of connections, this does limit the number of concurrent TCP connections (not set or nil => unlimited)
|
|
228
236
|
# +crlf_mode+:: CRLF handling support (:CRLF_ENSURE [default], :CRLF_LEAVE, :CRLF_STRICT)
|
|
229
237
|
# +do_dns_reverse_lookup+:: flag if this smtp server should do reverse DNS lookups on incoming connections
|
|
230
|
-
# +
|
|
238
|
+
# +io_wait_mode+:: how to wait when no input data is available (DEFAULT_IO_WAIT_MODE)
|
|
239
|
+
# +io_wait_available+:: fraction of a second to wait in loop when no input data is available (DEFAULT_IO_WAIT_AVAILABLE)
|
|
231
240
|
# +io_cmd_timeout+:: time in seconds to wait until complete line of data is expected (DEFAULT_IO_CMD_TIMEOUT, nil => disabled test)
|
|
232
241
|
# +io_buffer_chunk_size+:: size of chunks (bytes) to read non-blocking from socket (DEFAULT_IO_BUFFER_CHUNK_SIZE)
|
|
233
242
|
# +io_buffer_max_size+:: max size of buffer (max line length) until \lf ist expected (DEFAULT_IO_BUFFER_MAX_SIZE, nil => disabled test)
|
|
234
243
|
# +pipelining_extension+:: set to true for support of SMTP PIPELINING extension (DEFAULT_PIPELINING_EXTENSION_ENABLED)
|
|
235
244
|
# +internationalization_extensions+:: set to true for support of SMTP 8BITMIME and SMTPUTF8 extensions (DEFAULT_INTERNATIONALIZATION_EXTENSIONS_ENABLED)
|
|
245
|
+
# +proxy_extension+:: set to true for supporting PROXY connections (DEFAULT_PROXY_EXTENSION_ENABLED)
|
|
236
246
|
# +auth_mode+:: enable builtin authentication support (:AUTH_FORBIDDEN [default], :AUTH_OPTIONAL, :AUTH_REQUIRED)
|
|
237
247
|
# +tls_mode+:: enable builtin TLS support (:TLS_FORBIDDEN [default], :TLS_OPTIONAL, :TLS_REQUIRED)
|
|
238
248
|
# +tls_cert_path+:: path to tls certificate chain file
|
|
@@ -251,12 +261,15 @@ module MidiSmtpServer
|
|
|
251
261
|
max_connections: nil,
|
|
252
262
|
crlf_mode: nil,
|
|
253
263
|
do_dns_reverse_lookup: nil,
|
|
254
|
-
|
|
264
|
+
io_wait_mode: nil,
|
|
265
|
+
io_wait_available: nil,
|
|
266
|
+
io_waitreadable_sleep: nil, # deprecated, but for compatibility
|
|
255
267
|
io_cmd_timeout: nil,
|
|
256
268
|
io_buffer_chunk_size: nil,
|
|
257
269
|
io_buffer_max_size: nil,
|
|
258
270
|
pipelining_extension: nil,
|
|
259
271
|
internationalization_extensions: nil,
|
|
272
|
+
proxy_extension: nil,
|
|
260
273
|
auth_mode: nil,
|
|
261
274
|
tls_mode: nil,
|
|
262
275
|
tls_cert_path: nil,
|
|
@@ -338,7 +351,7 @@ module MidiSmtpServer
|
|
|
338
351
|
# test for all local valid ipv4 and ipv6 ip_addresses
|
|
339
352
|
# check question on stackoverflow for details
|
|
340
353
|
# https://stackoverflow.com/questions/59770803/identify-all-relevant-ip-addresses-from-ruby-socket-ip-address-list
|
|
341
|
-
ip_addresses_for_host << a.ip_address if
|
|
354
|
+
ip_addresses_for_host << a.ip_address if
|
|
342
355
|
(a.ipv4? &&
|
|
343
356
|
(a.ipv4_loopback? || a.ipv4_private? ||
|
|
344
357
|
!(a.ipv4_loopback? || a.ipv4_private? || a.ipv4_multicast?)
|
|
@@ -386,10 +399,21 @@ module MidiSmtpServer
|
|
|
386
399
|
# always prevent auto resolving hostnames to prevent a delay on socket connect
|
|
387
400
|
BasicSocket.do_not_reverse_lookup = true
|
|
388
401
|
# do reverse lookups manually if enabled by io.addr and io.peeraddr
|
|
402
|
+
# rubocop: disable-next Style/RedundantCondition
|
|
389
403
|
@do_dns_reverse_lookup = do_dns_reverse_lookup.nil? ? true : do_dns_reverse_lookup
|
|
390
404
|
|
|
405
|
+
# deprecated compatibility
|
|
406
|
+
raise 'Not allowed to use io_wait_available and deprecated io_waitreadable_sleep at the same time!' unless io_waitreadable_sleep.nil? || io_wait_available.nil?
|
|
407
|
+
unless io_waitreadable_sleep.nil?
|
|
408
|
+
io_wait_available = io_waitreadable_sleep
|
|
409
|
+
io_wait_mode = :IO_WAIT_SLEEP
|
|
410
|
+
@logger_protected.warn('Deprecated: "io_waitreadable_sleep" was replaced! Please use "io_wait_available" and "io_wait_mode" instead.')
|
|
411
|
+
end
|
|
391
412
|
# io and buffer settings
|
|
392
|
-
@
|
|
413
|
+
@io_wait_mode = io_wait_mode.nil? ? DEFAULT_IO_WAIT_MODE : io_wait_mode
|
|
414
|
+
raise "Unknown io wait mode #{@io_wait_mode} was given!" unless IO_WAIT_MODES.include?(@io_wait_mode)
|
|
415
|
+
@io_wait_available = io_wait_available.nil? ? DEFAULT_IO_WAIT_AVAILABLE : io_wait_available
|
|
416
|
+
raise 'Value of time must be positive (io_wait_available)' unless @io_wait_available.positive?
|
|
393
417
|
@io_cmd_timeout = io_cmd_timeout.nil? ? DEFAULT_IO_CMD_TIMEOUT : io_cmd_timeout
|
|
394
418
|
@io_buffer_chunk_size = io_buffer_chunk_size.nil? ? DEFAULT_IO_BUFFER_CHUNK_SIZE : io_buffer_chunk_size
|
|
395
419
|
@io_buffer_max_size = io_buffer_max_size.nil? ? DEFAULT_IO_BUFFER_MAX_SIZE : io_buffer_max_size
|
|
@@ -397,6 +421,8 @@ module MidiSmtpServer
|
|
|
397
421
|
# smtp extensions
|
|
398
422
|
@pipelining_extension = pipelining_extension.nil? ? DEFAULT_PIPELINING_EXTENSION_ENABLED : pipelining_extension
|
|
399
423
|
@internationalization_extensions = internationalization_extensions.nil? ? DEFAULT_INTERNATIONALIZATION_EXTENSIONS_ENABLED : internationalization_extensions
|
|
424
|
+
@proxy_extension = proxy_extension.nil? ? DEFAULT_PROXY_EXTENSION_ENABLED : proxy_extension
|
|
425
|
+
require 'ipaddr' if @proxy_extension
|
|
400
426
|
|
|
401
427
|
# check for authentication
|
|
402
428
|
@auth_mode = auth_mode.nil? ? DEFAULT_AUTH_MODE : auth_mode
|
|
@@ -482,6 +508,13 @@ module MidiSmtpServer
|
|
|
482
508
|
# the value is not allowed to return CR nor LF chars and will be stripped
|
|
483
509
|
def on_helo_event(ctx, helo_data) end
|
|
484
510
|
|
|
511
|
+
# event on PROXY
|
|
512
|
+
# you may raise an exception if you want to block some addresses
|
|
513
|
+
# you also may change or add any value of the hash:
|
|
514
|
+
# {proto, source_ip, source_host, source_port, dest_ip, dest_host, dest_port}
|
|
515
|
+
# a returned value hash is set as ctx[:server][:proxy]
|
|
516
|
+
def on_proxy_event(ctx, proxy_data) end
|
|
517
|
+
|
|
485
518
|
# check the authentication on AUTH
|
|
486
519
|
# if any value returned, that will be used for ongoing processing
|
|
487
520
|
# otherwise the original value will be used for authorization_id
|
|
@@ -755,10 +788,25 @@ module MidiSmtpServer
|
|
|
755
788
|
io_buffer_line_lf = io_buffer.index("\n")
|
|
756
789
|
end
|
|
757
790
|
|
|
758
|
-
#
|
|
791
|
+
# handle no input data is available yet
|
|
759
792
|
rescue IO::WaitReadable
|
|
760
|
-
|
|
761
|
-
|
|
793
|
+
if @io_wait_mode == :IO_WAIT_SLEEP
|
|
794
|
+
# reduce the processing speed and thereby the system
|
|
795
|
+
# utilization by sleeping the configured time
|
|
796
|
+
sleep @io_wait_available
|
|
797
|
+
else
|
|
798
|
+
# block on the underlying socket until new data has arrived,
|
|
799
|
+
# but wake up at least after wait time so that the loop
|
|
800
|
+
# keeps checking shutdown? and io_cmd_timeout
|
|
801
|
+
# (io.to_io resolves the plain socket behind an
|
|
802
|
+
# OpenSSL::SSL::SSLSocket, otherwise wait_readable is not defined
|
|
803
|
+
io.to_io.wait_readable(@io_wait_available)
|
|
804
|
+
end
|
|
805
|
+
|
|
806
|
+
# during TLS (re)negotiation a read may require the socket
|
|
807
|
+
# to become writable first. this always waits by event
|
|
808
|
+
rescue IO::WaitWritable
|
|
809
|
+
io.to_io.wait_writable(@io_wait_available)
|
|
762
810
|
end
|
|
763
811
|
|
|
764
812
|
# check if io_buffer is filled and contains already a line-feed
|
|
@@ -772,7 +820,7 @@ module MidiSmtpServer
|
|
|
772
820
|
# process commands and handle special SmtpdExceptions
|
|
773
821
|
begin
|
|
774
822
|
# check for pipelining extension or violation
|
|
775
|
-
raise Smtpd500PipeliningException unless @pipelining_extension ||
|
|
823
|
+
raise Smtpd500PipeliningException unless !io_buffer_line_lf || @pipelining_extension || (proxy_extension && (session[:cmd_sequence] == :CMD_HELO)) || (session[:cmd_sequence] == :CMD_DATA)
|
|
776
824
|
|
|
777
825
|
# handle input line based on @crlf_mode
|
|
778
826
|
case crlf_mode
|
|
@@ -932,8 +980,8 @@ module MidiSmtpServer
|
|
|
932
980
|
# check whether to answer as HELO or EHLO
|
|
933
981
|
case line
|
|
934
982
|
when (/^EHLO/i)
|
|
935
|
-
# rubocop:disable Style/StringConcatenation
|
|
936
983
|
# reply supported extensions
|
|
984
|
+
# rubocop: disable-next Style/StringConcatenation
|
|
937
985
|
return "250-#{session[:ctx][:server][:helo_response].to_s.strip}\r\n" +
|
|
938
986
|
# respond with 8BITMIME extension
|
|
939
987
|
(@internationalization_extensions ? "250-8BITMIME\r\n" : '') +
|
|
@@ -946,12 +994,93 @@ module MidiSmtpServer
|
|
|
946
994
|
# respond with STARTTLS if available and not already enabled
|
|
947
995
|
(@encrypt_mode == :TLS_FORBIDDEN || encrypted?(session[:ctx]) ? '' : "250-STARTTLS\r\n") +
|
|
948
996
|
'250 OK'
|
|
949
|
-
# rubocop:enable all
|
|
950
997
|
else
|
|
951
998
|
# reply ok only
|
|
952
999
|
return "250 OK #{session[:ctx][:server][:helo_response].to_s.strip}".strip
|
|
953
1000
|
end
|
|
954
1001
|
|
|
1002
|
+
when @proxy_extension && (/^PROXY(\s+)/i)
|
|
1003
|
+
# PROXY
|
|
1004
|
+
# 250 Requested mail action okay, completed
|
|
1005
|
+
# 421 <domain> Service not available, closing transmission channel
|
|
1006
|
+
# 500 Syntax error, command unrecognised
|
|
1007
|
+
# 501 Syntax error in parameters or arguments
|
|
1008
|
+
# ---------
|
|
1009
|
+
# Documentation at https://github.com/haproxy/haproxy/blob/master/doc/proxy-protocol.txt
|
|
1010
|
+
# syntax
|
|
1011
|
+
# PROXY PROTO source-ip dest-ip source-port dest-port
|
|
1012
|
+
# supported commands:
|
|
1013
|
+
# PROXY TCP4 255.255.255.255 255.255.255.255 65535 65535
|
|
1014
|
+
# PROXY TCP6 ffff:f...f:ffff ffff:f...f:ffff 65535 65535
|
|
1015
|
+
# PROXY UNKNOWN
|
|
1016
|
+
# PROXY UNKNOWN ffff:f...f:ffff ffff:f...f:ffff 65535 65535
|
|
1017
|
+
# ---------
|
|
1018
|
+
# check valid command sequence
|
|
1019
|
+
raise Smtpd503Exception if session[:cmd_sequence] != :CMD_HELO
|
|
1020
|
+
# check valid command
|
|
1021
|
+
raise Smtpd421Exception, 'Abort connection while illegal PROXY command!' unless line.match?(/^PROXY(\s+)(UNKNOWN(|(\s+).*)|TCP(4|6)(\s+)([0-9a-f.:]+)(\s+)([0-9a-f.:]+)(\s+)([0-9]+)(\s+)([0-9]+)(\s*))$/i)
|
|
1022
|
+
# check command usage is allowed only once
|
|
1023
|
+
raise Smtpd421Exception, 'Abort connection while PROXY already set!' if session[:ctx][:server][:proxy]
|
|
1024
|
+
# get values from proxy command
|
|
1025
|
+
cmd_data = line.gsub(/^PROXY\ /i, '').strip.gsub(/\s+/, ' ').split
|
|
1026
|
+
# create an empty hash to inspect by event
|
|
1027
|
+
proxy_data = {
|
|
1028
|
+
proto: cmd_data[0].upcase,
|
|
1029
|
+
source_ip: nil,
|
|
1030
|
+
source_host: nil,
|
|
1031
|
+
source_port: nil,
|
|
1032
|
+
dest_ip: nil,
|
|
1033
|
+
dest_host: nil,
|
|
1034
|
+
dest_port: nil
|
|
1035
|
+
}
|
|
1036
|
+
# test proto
|
|
1037
|
+
unless proxy_data[:proto] == 'UNKNOWN'
|
|
1038
|
+
begin
|
|
1039
|
+
# try to build valid addresses from given strings
|
|
1040
|
+
proxy_data[:source_ip] = IPAddr.new(cmd_data[1])
|
|
1041
|
+
proxy_data[:source_port] = cmd_data[3].to_i
|
|
1042
|
+
proxy_data[:dest_ip] = IPAddr.new(cmd_data[2])
|
|
1043
|
+
proxy_data[:dest_port] = cmd_data[4].to_i
|
|
1044
|
+
# check that given addresses correct by type
|
|
1045
|
+
if proxy_data[:proto] == 'TCP4'
|
|
1046
|
+
raise unless proxy_data[:source_ip].ipv4?
|
|
1047
|
+
raise unless proxy_data[:dest_ip].ipv4?
|
|
1048
|
+
else
|
|
1049
|
+
raise unless proxy_data[:source_ip].ipv6?
|
|
1050
|
+
raise unless proxy_data[:dest_ip].ipv6?
|
|
1051
|
+
end
|
|
1052
|
+
# check that ports within valid ranges
|
|
1053
|
+
raise unless proxy_data[:source_port].between?(1, 65_535)
|
|
1054
|
+
raise unless proxy_data[:dest_port].between?(1, 65_535)
|
|
1055
|
+
|
|
1056
|
+
# update hash to inspect by event
|
|
1057
|
+
# normalize ip addresses
|
|
1058
|
+
proxy_data[:source_ip] = proxy_data[:source_ip].to_s
|
|
1059
|
+
proxy_data[:source_host] = proxy_data[:source_ip]
|
|
1060
|
+
proxy_data[:dest_ip] = proxy_data[:dest_ip].to_s
|
|
1061
|
+
proxy_data[:dest_host] = proxy_data[:dest_ip]
|
|
1062
|
+
|
|
1063
|
+
rescue StandardError
|
|
1064
|
+
# change exception into Smtpd exception and drop connection
|
|
1065
|
+
raise Smtpd421Exception, 'Abort connection for unsupported PROXY parameters!'
|
|
1066
|
+
end
|
|
1067
|
+
end
|
|
1068
|
+
|
|
1069
|
+
# call event to handle data
|
|
1070
|
+
return_value = on_proxy_event(session[:ctx], proxy_data)
|
|
1071
|
+
if return_value
|
|
1072
|
+
# overwrite data with returned value
|
|
1073
|
+
proxy_data = return_value
|
|
1074
|
+
end
|
|
1075
|
+
# if no error raised, append to server hash
|
|
1076
|
+
session[:ctx][:server][:proxy] = proxy_data
|
|
1077
|
+
|
|
1078
|
+
# reply nothing
|
|
1079
|
+
# otherwise on buffering clients or enabled feature pipelining
|
|
1080
|
+
# the original client will receive unhandleable responses
|
|
1081
|
+
# and gets interrupted
|
|
1082
|
+
return ''
|
|
1083
|
+
|
|
955
1084
|
when /^STARTTLS\s*$/i
|
|
956
1085
|
# STARTTLS
|
|
957
1086
|
# 220 Ready to start TLS
|
|
@@ -1206,7 +1335,7 @@ module MidiSmtpServer
|
|
|
1206
1335
|
# If we are in date mode then ...
|
|
1207
1336
|
|
|
1208
1337
|
# call event to signal beginning of message data transfer
|
|
1209
|
-
on_message_data_start_event(session[:ctx])
|
|
1338
|
+
on_message_data_start_event(session[:ctx]) if session[:ctx][:message][:data].empty?
|
|
1210
1339
|
|
|
1211
1340
|
# ... and the entire new message data (line) does NOT consists
|
|
1212
1341
|
# solely of a period (.) on a line by itself then we are being
|
|
@@ -1294,6 +1423,7 @@ module MidiSmtpServer
|
|
|
1294
1423
|
remote_host: +'',
|
|
1295
1424
|
remote_ip: +'',
|
|
1296
1425
|
remote_port: +'',
|
|
1426
|
+
proxy: nil,
|
|
1297
1427
|
helo: +'',
|
|
1298
1428
|
helo_response: +'',
|
|
1299
1429
|
connected: +'',
|
metadata
CHANGED
|
@@ -1,15 +1,42 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: midi-smtp-server
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.1
|
|
4
|
+
version: 3.3.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Tom Freudenberg
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
12
|
-
dependencies:
|
|
10
|
+
date: 2026-09-16 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: base64
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - "~>"
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '0.2'
|
|
19
|
+
type: :runtime
|
|
20
|
+
prerelease: false
|
|
21
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
22
|
+
requirements:
|
|
23
|
+
- - "~>"
|
|
24
|
+
- !ruby/object:Gem::Version
|
|
25
|
+
version: '0.2'
|
|
26
|
+
- !ruby/object:Gem::Dependency
|
|
27
|
+
name: logger
|
|
28
|
+
requirement: !ruby/object:Gem::Requirement
|
|
29
|
+
requirements:
|
|
30
|
+
- - "~>"
|
|
31
|
+
- !ruby/object:Gem::Version
|
|
32
|
+
version: '1.6'
|
|
33
|
+
type: :runtime
|
|
34
|
+
prerelease: false
|
|
35
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
36
|
+
requirements:
|
|
37
|
+
- - "~>"
|
|
38
|
+
- !ruby/object:Gem::Version
|
|
39
|
+
version: '1.6'
|
|
13
40
|
description: MidiSmtpServer is the highly customizable ruby SMTP-Server and SMTP-Service
|
|
14
41
|
library with builtin support for AUTH and SSL/STARTTLS, 8BITMIME and SMTPUTF8, IPv4
|
|
15
42
|
and IPv6 and additional features.
|
|
@@ -34,9 +61,9 @@ metadata:
|
|
|
34
61
|
source_code_uri: https://github.com/4commerce-technologies-AG/midi-smtp-server
|
|
35
62
|
changelog_uri: https://github.com/4commerce-technologies-AG/midi-smtp-server#changes-and-updates
|
|
36
63
|
bug_tracker_uri: https://github.com/4commerce-technologies-AG/midi-smtp-server/issues
|
|
37
|
-
documentation_uri: https://www.rubydoc.info/gems/midi-smtp-server/3.1
|
|
64
|
+
documentation_uri: https://www.rubydoc.info/gems/midi-smtp-server/3.3.1
|
|
38
65
|
wiki_uri: https://midi-smtp-server.readthedocs.io/
|
|
39
|
-
|
|
66
|
+
rubygems_mfa_required: 'true'
|
|
40
67
|
rdoc_options: []
|
|
41
68
|
require_paths:
|
|
42
69
|
- lib
|
|
@@ -44,15 +71,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
44
71
|
requirements:
|
|
45
72
|
- - ">="
|
|
46
73
|
- !ruby/object:Gem::Version
|
|
47
|
-
version: 2.
|
|
74
|
+
version: 2.7.0
|
|
48
75
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
49
76
|
requirements:
|
|
50
77
|
- - ">="
|
|
51
78
|
- !ruby/object:Gem::Version
|
|
52
79
|
version: '0'
|
|
53
80
|
requirements: []
|
|
54
|
-
rubygems_version: 3.
|
|
55
|
-
signing_key:
|
|
81
|
+
rubygems_version: 3.6.9
|
|
56
82
|
specification_version: 4
|
|
57
83
|
summary: MidiSmtpServer Class
|
|
58
84
|
test_files: []
|