midi-smtp-server 3.0.3 → 3.1.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +20 -6
- data/MIT-LICENSE.txt +1 -1
- data/README.md +18 -17
- data/lib/midi-smtp-server/exceptions.rb +2 -2
- data/lib/midi-smtp-server/tls-transport.rb +3 -3
- data/lib/midi-smtp-server/version.rb +3 -3
- data/lib/midi-smtp-server.rb +154 -61
- metadata +8 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0c84077c17fd5b68cffd37292da0e473245f16662041fb9a17218cd897961730
|
4
|
+
data.tar.gz: 299e676ad777d512b53d6c81a2a3b696eca00fb2d11adcaf2a3f24b6075c5fe1
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 68446f6bb248f579689091fa094752f95db702ca6fc9f6de5588b68c1c564ee45499d71acee7b674b8784b78403a275e15afb5249e867571542e96c93ae608bd
|
7
|
+
data.tar.gz: 65c78a6d3ec2241756befc2a80a03691e74b37eed18093c0796ebd5a420f522b44f514e77b904f1a2ab7a97fb5862daaf7da2bcc3a5c2ba20000079111c3e994
|
data/CHANGELOG.md
CHANGED
@@ -4,9 +4,24 @@ We suggest everybody using MidiSmtpServer to switch at least to latest 2.3.y. or
|
|
4
4
|
|
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
|
+
|
8
|
+
#### 3.1.2 (2023-05-06)
|
9
|
+
|
10
|
+
1. Minor fix for backwards compatibility to method `start` when not using pre_fork options.
|
11
|
+
|
12
|
+
|
13
|
+
#### 3.1.1 (2023-02-26)
|
14
|
+
|
15
|
+
1. Add option to additional activate [pre-forking workers](https://midi-smtp-server.readthedocs.io/feature_load_balancing/#pre-forking) (beta) ([check issue 42](https://github.com/4commerce-technologies-AG/midi-smtp-server/issues/42))
|
16
|
+
2. Adjust sleep idle time while in command and data loop to speedup processing ([check issue 47](https://github.com/4commerce-technologies-AG/midi-smtp-server/issues/47))
|
17
|
+
3. Mark the current default value `0.1` for `io_waitreadable_sleep` as deprecated, will become `0.03` in a future version.
|
18
|
+
4. Modify github workflow and apply testing of ruby 3.1 and ruby 3.2
|
19
|
+
5. Generate updated openssl test certificates for TLS tests
|
20
|
+
|
21
|
+
|
7
22
|
#### 3.0.3 (2022-02-12)
|
8
23
|
|
9
|
-
1. Critical fix for thread safety ([check issue
|
24
|
+
1. Critical fix for thread safety ([check issue 39](https://github.com/4commerce-technologies-AG/midi-smtp-server/issues/39))
|
10
25
|
2. Fix tests using net/smtp '>= 0.3.1'
|
11
26
|
|
12
27
|
|
@@ -29,14 +44,13 @@ For upgrades from previous versions or outdated _MiniSmtpServer_ gem you may fol
|
|
29
44
|
10. Dropped deprecated empty wildcard `""` support on initialize - please use specific hostnames and / or ip-addresses or star wildcard `"*"` only
|
30
45
|
11. Align tests with Rubocop style and coding enforcements
|
31
46
|
12. Added `rake` tasks for testing and linting, checkout `bundle exec rake -T`
|
32
|
-
13. Re-defined arguments of methods `new`, `join`, `stop` as keyword arguments, check [minor
|
47
|
+
13. Re-defined arguments of methods `new`, `join`, `stop` as keyword arguments, check [minor incompatibility: upgrade to 3.x](https://midi-smtp-server.readthedocs.io/appendix_upgrade/#upgrade-to-3x)
|
33
48
|
14. Enhance the slack recipe in cookbook for [Docker usage](https://github.com/4commerce-technologies-AG/midi-smtp-server/tree/master/cookbook/recipe-slack)
|
34
49
|
|
35
50
|
|
36
51
|
#### 2.3.3 (2022-02-12)
|
37
52
|
|
38
|
-
1. Critical fix for thread safety ([check issue
|
39
|
-
2. Fix tests using net/smtp '>= 0.3.1'
|
53
|
+
1. Critical fix for thread safety ([check issue 39](https://github.com/4commerce-technologies-AG/midi-smtp-server/issues/39))
|
40
54
|
|
41
55
|
|
42
56
|
#### 2.3.2 (2020-01-21)
|
@@ -61,7 +75,7 @@ For upgrades from previous versions or outdated _MiniSmtpServer_ gem you may fol
|
|
61
75
|
|
62
76
|
1. Support [IPv4 and IPv6 (documentation)](https://midi-smtp-server.readthedocs.io/instantiate/#ipv4-and-ipv6-ready)
|
63
77
|
2. Support binding of [multiple ports and hosts / ip addresses](https://midi-smtp-server.readthedocs.io/instantiate/#ports-and-addresses)
|
64
|
-
3. Handle [utilization of connections and processings](https://midi-smtp-server.readthedocs.io/
|
78
|
+
3. Handle [utilization of connections and processings](https://midi-smtp-server.readthedocs.io/feature_load_balancing/)
|
65
79
|
4. Support of RFC(2)822 [CR LF modes](https://midi-smtp-server.readthedocs.io/feature_cr_lf_modes/)
|
66
80
|
5. Support (optionally) SMTP [PIPELINING](https://tools.ietf.org/html/rfc2920) extension
|
67
81
|
6. Support (optionally) SMTP [8BITMIME](https://midi-smtp-server.readthedocs.io/feature_8bitmime_smtputf8/) extension
|
@@ -104,7 +118,7 @@ For upgrades from previous versions or outdated _MiniSmtpServer_ gem you may fol
|
|
104
118
|
|
105
119
|
#### 2.x
|
106
120
|
|
107
|
-
1.
|
121
|
+
1. Modularized
|
108
122
|
2. Removed dependency to GServer
|
109
123
|
3. Additional events to interact with
|
110
124
|
4. Use logger to log several messages from severity :debug up to :fatal
|
data/MIT-LICENSE.txt
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
The MIT License (MIT)
|
2
2
|
|
3
|
-
Copyright (c) 2014 -
|
3
|
+
Copyright (c) 2014 - 2023 Tom Freudenberg
|
4
4
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
data/README.md
CHANGED
@@ -19,7 +19,7 @@ MidiSmtpServer is the highly customizable ruby SMTP-Server and SMTP-Service libr
|
|
19
19
|
|
20
20
|
As a library it is mainly designed to be integrated into your projects as serving a SMTP-Server service. The lib will do nothing with your mail and you have to create your own event functions to handle and operate on incoming mails. We are using this in conjunction with [Mikel Lindsaar](https://github.com/mikel) great Mail component (https://github.com/mikel/mail). Time to run your own SMTP-Server service.
|
21
21
|
|
22
|
-
Checkout all the features and improvements (3.0.1 Logging enhancement, 2.3.x Multiple ports and addresses, 2.2.x Encryption [StartTLS], 2.1.0 Authentication [AUTH], 2.1.1 significant speed improvement, etc.) and get more details from section [changes and updates](https://github.com/4commerce-technologies-AG/midi-smtp-server#changes-and-updates).
|
22
|
+
Checkout all the features and improvements (3.1.1 Process parallelization, 3.0.1 Logging enhancement, 2.3.x Multiple ports and addresses, 2.2.x Encryption [StartTLS], 2.1.0 Authentication [AUTH], 2.1.1 significant speed improvement, etc.) and get more details from section [changes and updates](https://github.com/4commerce-technologies-AG/midi-smtp-server#changes-and-updates).
|
23
23
|
|
24
24
|
MidiSmtpServer is an extremely flexible library and almost any aspect of SMTP communications can be handled by deriving its events and using its configuration options.
|
25
25
|
|
@@ -40,10 +40,10 @@ class MySmtpd < MidiSmtpServer::Smtpd
|
|
40
40
|
logger.debug("[#{ctx[:envelope][:from]}] for recipient(s): [#{ctx[:envelope][:to]}]...")
|
41
41
|
|
42
42
|
# Just decode message once to make sure, that this message ist readable
|
43
|
-
|
43
|
+
mail = Mail.read_from_string(ctx[:message][:data])
|
44
44
|
|
45
45
|
# handle incoming mail, just show the message subject
|
46
|
-
logger.debug(
|
46
|
+
logger.debug(mail.subject)
|
47
47
|
end
|
48
48
|
|
49
49
|
end
|
@@ -64,10 +64,10 @@ This source code shows the example to receive messages via SMTP and store them t
|
|
64
64
|
# get each message after DATA <message> .
|
65
65
|
def on_message_data_event(ctx)
|
66
66
|
# Just decode message once to make sure, that this message ist readable
|
67
|
-
|
67
|
+
mail = Mail.read_from_string(ctx[:message])
|
68
68
|
|
69
69
|
# Publish to rabbit
|
70
|
-
@bunny_exchange.publish(
|
70
|
+
@bunny_exchange.publish(mail.to_s, :headers => { 'x-smtp' => mail.header.to_s }, :routing_key => "to_queue")
|
71
71
|
end
|
72
72
|
```
|
73
73
|
|
@@ -96,35 +96,35 @@ Read the [MidiSmtpServer Documentation](https://midi-smtp-server.readthedocs.io/
|
|
96
96
|
|
97
97
|
## Reliable code
|
98
98
|
|
99
|
-
Since version 2.3 implementation and integration tests by minitest framework are added to this repository. While the implementation tests are mostly checking the components, the integration tests try to verify the correct exchange of messages for different scenarios. In
|
99
|
+
Since version 2.3 implementation and integration tests by minitest framework are added to this repository. While the implementation tests are mostly checking the components, the integration tests try to verify the correct exchange of messages for different scenarios. Last but not least the stress tests do catch some rare conditions to make sure that no information is leaving its thread and process. In addition all sources are checked by rubocop to ensure they fit to the style guides.
|
100
100
|
|
101
101
|
You may run all rubocop tests through the `rake` helper:
|
102
102
|
|
103
|
-
```
|
103
|
+
```bash
|
104
104
|
bundle exec rake rubocop
|
105
105
|
```
|
106
106
|
|
107
107
|
You may also run all tests through the `rake` helper:
|
108
108
|
|
109
|
-
```
|
109
|
+
```bash
|
110
110
|
bundle exec rake test:all
|
111
111
|
```
|
112
112
|
|
113
113
|
or with more verbose output:
|
114
114
|
|
115
|
-
```
|
115
|
+
```bash
|
116
116
|
bundle exec rake test:all v=1
|
117
117
|
```
|
118
118
|
|
119
|
-
To just run just a part of the tests, you may select the `specs`, `unit` or `
|
119
|
+
To just run just a part of the tests, you may select the `specs`, `unit`, `integration` or `stress` tests:
|
120
120
|
|
121
|
-
```
|
121
|
+
```bash
|
122
122
|
bundle exec rake test:specs
|
123
123
|
```
|
124
124
|
|
125
125
|
To just run some selected (by regular expression) tests, you may use the `T=filter` option. The example will run only the tests and specs containing the word _connections_ in their method_name or describe_text:
|
126
126
|
|
127
|
-
```
|
127
|
+
```bash
|
128
128
|
bundle exec rake test:all v=1 T=connections
|
129
129
|
```
|
130
130
|
|
@@ -146,10 +146,10 @@ We suggest everybody using MidiSmtpServer to switch at least to latest 2.3.y. or
|
|
146
146
|
|
147
147
|
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
148
|
|
149
|
-
#### Latest release: 3.0.3 (2022-02-12)
|
150
149
|
|
151
|
-
|
152
|
-
|
150
|
+
#### Latest release: 3.1.2 (2023-05-06)
|
151
|
+
|
152
|
+
1. Minor fix for backwards compatibility to method `start` when not using pre_fork options.
|
153
153
|
|
154
154
|
|
155
155
|
#### Changelog history
|
@@ -171,6 +171,7 @@ Checkout the [Appendix Upgrade](https://midi-smtp-server.readthedocs.io/appendix
|
|
171
171
|
You may find, use and download the gem package on [RubyGems.org](http://rubygems.org/gems/midi-smtp-server).
|
172
172
|
|
173
173
|
[![Gem Version](https://badge.fury.io/rb/midi-smtp-server.svg)](http://badge.fury.io/rb/midi-smtp-server)
|
174
|
+
[![Ruby](https://github.com/4commerce-technologies-AG/midi-smtp-server/actions/workflows/push-and-pr-testing-ruby-ci.yml/badge.svg)](https://github.com/4commerce-technologies-AG/midi-smtp-server/actions/workflows/push-and-pr-testing-ruby-ci.yml)
|
174
175
|
|
175
176
|
<br>
|
176
177
|
|
@@ -189,6 +190,6 @@ You may find, use and download the gem package on [RubyGems.org](http://rubygems
|
|
189
190
|
|
190
191
|
Author: [Tom Freudenberg](http://about.me/tom.freudenberg)
|
191
192
|
|
192
|
-
[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](
|
193
|
+
[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)
|
193
194
|
|
194
|
-
Copyright (c) 2014-
|
195
|
+
Copyright (c) 2014-2023 [Tom Freudenberg](https://github.com/TomFreudenberg), [4commerce technologies AG](https://www.4commerce.de/), released under the MIT license
|
@@ -10,7 +10,7 @@ module MidiSmtpServer
|
|
10
10
|
class SmtpdIOTimeoutException < RuntimeError
|
11
11
|
end
|
12
12
|
|
13
|
-
# special internal exception to signal buffer size
|
13
|
+
# special internal exception to signal buffer size exceeding
|
14
14
|
# while waiting for incoming data line
|
15
15
|
class SmtpdIOBufferOverrunException < RuntimeError
|
16
16
|
end
|
@@ -286,7 +286,7 @@ module MidiSmtpServer
|
|
286
286
|
|
287
287
|
end
|
288
288
|
|
289
|
-
# Status when
|
289
|
+
# Status when expecting CRLF sequence as line breaks (RFC(2)822)
|
290
290
|
|
291
291
|
# 500 Bad input, missing CRLF line termination
|
292
292
|
class Smtpd500CrLfSequenceException < SmtpdException
|
@@ -69,9 +69,9 @@ module MidiSmtpServer
|
|
69
69
|
@ssl_context.cert.sign @ssl_context.key, OpenSSL::Digest.new('SHA256')
|
70
70
|
logger.debug("SSL: generated test certificate\r\n#{@ssl_context.cert.to_text}")
|
71
71
|
else
|
72
|
-
# if any is set, test the
|
73
|
-
raise "File
|
74
|
-
raise "File
|
72
|
+
# if any is set, test the paths
|
73
|
+
raise "File \"#{@cert_path}\" does not exist or is not a regular file. Could not load certificate." unless File.file?(@cert_path.to_s)
|
74
|
+
raise "File \"#{@key_path}\" does not exist or is not a regular file. Could not load private key." unless @key_path.nil? || File.file?(@key_path.to_s)
|
75
75
|
# try to load certificate and key
|
76
76
|
cert_lines = File.read(@cert_path.to_s).lines
|
77
77
|
# check if the cert file contains a chain of certs
|
data/lib/midi-smtp-server.rb
CHANGED
@@ -17,15 +17,17 @@ module MidiSmtpServer
|
|
17
17
|
# default values
|
18
18
|
DEFAULT_SMTPD_HOST = '127.0.0.1'
|
19
19
|
DEFAULT_SMTPD_PORT = 2525
|
20
|
+
DEFAULT_SMTPD_PRE_FORK = 0
|
20
21
|
DEFAULT_SMTPD_MAX_PROCESSINGS = 4
|
21
22
|
|
22
|
-
# default values for conformity to RFC(2)822 and
|
23
|
+
# default values for conformity to RFC(2)822 and additional
|
23
24
|
# if interested in details, checkout discussion on issue queue at:
|
24
25
|
# https://github.com/4commerce-technologies-AG/midi-smtp-server/issues/16
|
25
26
|
CRLF_MODES = [:CRLF_ENSURE, :CRLF_LEAVE, :CRLF_STRICT].freeze
|
26
27
|
DEFAULT_CRLF_MODE = :CRLF_ENSURE
|
27
28
|
|
28
29
|
# default values for IO operations
|
30
|
+
DEFAULT_IO_WAITREADABLE_SLEEP = 0.1
|
29
31
|
DEFAULT_IO_CMD_TIMEOUT = 30
|
30
32
|
DEFAULT_IO_BUFFER_CHUNK_SIZE = 4 * 1024
|
31
33
|
DEFAULT_IO_BUFFER_MAX_SIZE = 1 * 1024 * 1024
|
@@ -34,7 +36,7 @@ module MidiSmtpServer
|
|
34
36
|
DEFAULT_PIPELINING_EXTENSION_ENABLED = false
|
35
37
|
DEFAULT_INTERNATIONALIZATION_EXTENSIONS_ENABLED = false
|
36
38
|
|
37
|
-
#
|
39
|
+
# Authentication modes
|
38
40
|
AUTH_MODES = [:AUTH_FORBIDDEN, :AUTH_OPTIONAL, :AUTH_REQUIRED].freeze
|
39
41
|
DEFAULT_AUTH_MODE = :AUTH_FORBIDDEN
|
40
42
|
|
@@ -43,31 +45,42 @@ module MidiSmtpServer
|
|
43
45
|
|
44
46
|
public
|
45
47
|
|
46
|
-
#
|
48
|
+
# Create the server
|
47
49
|
def start
|
48
|
-
|
50
|
+
create_service
|
51
|
+
# immediately attach the threads to running single master process (default)
|
52
|
+
attach_threads unless pre_fork?
|
49
53
|
end
|
50
54
|
|
51
55
|
# Stop the server
|
52
56
|
def stop(wait_seconds_before_close: 2, gracefully: true)
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
57
|
+
begin
|
58
|
+
# signal pre_forked workers to stop
|
59
|
+
@workers.each { |worker_pid| Process.kill(:TERM, worker_pid) } if pre_fork? && master?
|
60
|
+
# always signal shutdown
|
61
|
+
shutdown if gracefully
|
62
|
+
# wait if some connection(s) need(s) more time to handle shutdown
|
63
|
+
sleep wait_seconds_before_close if connections?
|
64
|
+
# drop tcp_servers while raising SmtpdStopServiceException
|
65
|
+
@connections_mutex.synchronize do
|
66
|
+
@tcp_server_threads.each do |tcp_server_thread|
|
67
|
+
# use safe navigation (&.) to make sure that obj exists like ... if tcp_server_thread
|
68
|
+
tcp_server_thread&.raise SmtpdStopServiceException
|
69
|
+
end
|
62
70
|
end
|
71
|
+
|
72
|
+
ensure
|
73
|
+
# check for removing TCPServers
|
74
|
+
@tcp_servers.each { |tcp_server| remove_tcp_server(tcp_server) } if master?
|
63
75
|
end
|
76
|
+
|
64
77
|
# wait if some connection(s) still need(s) more time to come down
|
65
78
|
sleep wait_seconds_before_close if connections? || !stopped?
|
66
79
|
end
|
67
80
|
|
68
81
|
# Returns true if the server has stopped.
|
69
82
|
def stopped?
|
70
|
-
@tcp_server_threads.empty? && @tcp_servers.empty?
|
83
|
+
master? ? @workers.empty? && @tcp_server_threads.empty? && @tcp_servers.empty? : @tcp_server_threads.empty?
|
71
84
|
end
|
72
85
|
|
73
86
|
# Schedule a shutdown for the server
|
@@ -100,34 +113,67 @@ module MidiSmtpServer
|
|
100
113
|
@processings.any?
|
101
114
|
end
|
102
115
|
|
116
|
+
# Return if in pre-fork mode
|
117
|
+
def pre_fork?
|
118
|
+
@pre_fork > 1
|
119
|
+
end
|
120
|
+
|
121
|
+
# Return if this is the master process
|
122
|
+
def master?
|
123
|
+
!@is_forked
|
124
|
+
end
|
125
|
+
|
126
|
+
# Return if this is a forked worker process
|
127
|
+
def worker?
|
128
|
+
@is_forked
|
129
|
+
end
|
130
|
+
|
131
|
+
# Return number of forked worker processes
|
132
|
+
def workers
|
133
|
+
@workers.size
|
134
|
+
end
|
135
|
+
|
136
|
+
# Return if has active forked worker processes
|
137
|
+
def workers?
|
138
|
+
@workers.any?
|
139
|
+
end
|
140
|
+
|
103
141
|
# Join with the server thread(s)
|
104
142
|
# before joining the server threads, check and wait optionally a few seconds
|
105
143
|
# to let the service(s) come up
|
106
144
|
def join(sleep_seconds_before_join: 1)
|
107
145
|
# check already existing TCPServers
|
108
146
|
return if @tcp_servers.empty?
|
109
|
-
#
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
147
|
+
# check number of processes to pre-fork
|
148
|
+
|
149
|
+
if pre_fork?
|
150
|
+
# create a number of pre-fork processes and attach and join threads within workers
|
151
|
+
@pre_fork.times do
|
152
|
+
# append worker pid to list of workers
|
153
|
+
@workers << fork do
|
154
|
+
# set state for a forked process
|
155
|
+
@is_forked = true
|
156
|
+
# just attach and join the threads to forked worker process
|
157
|
+
attach_threads
|
158
|
+
join_threads(sleep_seconds_before_join: sleep_seconds_before_join)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
# Blocking wait until each worker process has been finished
|
162
|
+
@workers.each { |pid| Process.waitpid(pid) }
|
118
163
|
|
119
|
-
|
120
|
-
|
164
|
+
else
|
165
|
+
# just join the threads to running single master process (default)
|
166
|
+
join_threads(sleep_seconds_before_join: sleep_seconds_before_join)
|
121
167
|
end
|
122
168
|
end
|
123
169
|
|
124
|
-
# Array of ports on which to bind, set as string
|
170
|
+
# Array of ports on which to bind, set as string separated by commata like '2525, 3535' or '2525:3535, 2525'
|
125
171
|
def ports
|
126
172
|
# prevent original array from being changed
|
127
173
|
@ports.dup
|
128
174
|
end
|
129
175
|
|
130
|
-
# Array of hosts / ip_addresses on which to bind, set as string
|
176
|
+
# Array of hosts / ip_addresses on which to bind, set as string separated by commata like 'name.domain.com, 127.0.0.1, ::1'
|
131
177
|
def hosts
|
132
178
|
# prevent original array from being changed
|
133
179
|
@hosts.dup
|
@@ -139,7 +185,7 @@ module MidiSmtpServer
|
|
139
185
|
@addresses.dup
|
140
186
|
end
|
141
187
|
|
142
|
-
# Current TLS OpenSSL::SSL::SSLContext when
|
188
|
+
# Current TLS OpenSSL::SSL::SSLContext when initialized by :TLS_OPTIONAL, :TLS_REQUIRED
|
143
189
|
def ssl_context
|
144
190
|
@tls&.ssl_context
|
145
191
|
end
|
@@ -150,15 +196,17 @@ module MidiSmtpServer
|
|
150
196
|
attr_reader :max_connections
|
151
197
|
# CRLF handling based on conformity to RFC(2)822
|
152
198
|
attr_reader :crlf_mode
|
199
|
+
# Time in seconds to sleep on IO::WaitReadable exception
|
200
|
+
attr_reader :io_waitreadable_sleep
|
153
201
|
# Maximum time in seconds to wait for a complete incoming data line, as a FixNum
|
154
202
|
attr_reader :io_cmd_timeout
|
155
203
|
# Bytes to read non-blocking from socket into buffer, as a FixNum
|
156
204
|
attr_reader :io_buffer_chunk_size
|
157
|
-
# Maximum bytes to read as buffer before expecting
|
205
|
+
# Maximum bytes to read as buffer before expecting completed incoming data line, as a FixNum
|
158
206
|
attr_reader :io_buffer_max_size
|
159
207
|
# Flag if should do reverse DNS lookups on incoming connections
|
160
208
|
attr_reader :do_dns_reverse_lookup
|
161
|
-
#
|
209
|
+
# Authentication mode
|
162
210
|
attr_reader :auth_mode
|
163
211
|
# Encryption mode
|
164
212
|
attr_reader :encrypt_mode
|
@@ -167,17 +215,19 @@ module MidiSmtpServer
|
|
167
215
|
# handle SMTP 8BITMIME and SMTPUTF8 extension
|
168
216
|
attr_reader :internationalization_extensions
|
169
217
|
|
170
|
-
# logging object, may be
|
218
|
+
# logging object, may be overridden by special loggers like YELL or others
|
171
219
|
attr_reader :logger
|
172
220
|
|
173
221
|
# Initialize SMTP Server class
|
174
222
|
#
|
175
223
|
# +ports+:: ports to listen on. Allows multiple ports like "2525, 3535" or "2525:3535, 2525". Default value = DEFAULT_SMTPD_PORT
|
176
224
|
# +hosts+:: interface ip or hostname to listen on or "*" to listen on all interfaces, allows multiple hostnames and ip_addresses like "name.domain.com, 127.0.0.1, ::1". Default value = DEFAULT_SMTPD_HOST
|
225
|
+
# +pre_fork+:: number of processes to pre-fork to enable load balancing over multiple cores. Default value = DEFAULT_SMTPD_PRE_FORK
|
177
226
|
# +max_processings+:: maximum number of simultaneous processed connections, this does not limit the number of concurrent TCP connections. Default value = DEFAULT_SMTPD_MAX_PROCESSINGS
|
178
227
|
# +max_connections+:: maximum number of connections, this does limit the number of concurrent TCP connections (not set or nil => unlimited)
|
179
228
|
# +crlf_mode+:: CRLF handling support (:CRLF_ENSURE [default], :CRLF_LEAVE, :CRLF_STRICT)
|
180
229
|
# +do_dns_reverse_lookup+:: flag if this smtp server should do reverse DNS lookups on incoming connections
|
230
|
+
# +io_waitreadable_sleep+:: seconds to sleep in loop when no input data is available (DEFAULT_IO_WAITREADABLE_SLEEP)
|
181
231
|
# +io_cmd_timeout+:: time in seconds to wait until complete line of data is expected (DEFAULT_IO_CMD_TIMEOUT, nil => disabled test)
|
182
232
|
# +io_buffer_chunk_size+:: size of chunks (bytes) to read non-blocking from socket (DEFAULT_IO_BUFFER_CHUNK_SIZE)
|
183
233
|
# +io_buffer_max_size+:: max size of buffer (max line length) until \lf ist expected (DEFAULT_IO_BUFFER_MAX_SIZE, nil => disabled test)
|
@@ -185,7 +235,7 @@ module MidiSmtpServer
|
|
185
235
|
# +internationalization_extensions+:: set to true for support of SMTP 8BITMIME and SMTPUTF8 extensions (DEFAULT_INTERNATIONALIZATION_EXTENSIONS_ENABLED)
|
186
236
|
# +auth_mode+:: enable builtin authentication support (:AUTH_FORBIDDEN [default], :AUTH_OPTIONAL, :AUTH_REQUIRED)
|
187
237
|
# +tls_mode+:: enable builtin TLS support (:TLS_FORBIDDEN [default], :TLS_OPTIONAL, :TLS_REQUIRED)
|
188
|
-
# +tls_cert_path+:: path to tls
|
238
|
+
# +tls_cert_path+:: path to tls certificate chain file
|
189
239
|
# +tls_key_path+:: path to tls key file
|
190
240
|
# +tls_ciphers+:: allowed ciphers for connection
|
191
241
|
# +tls_methods+:: allowed methods for protocol
|
@@ -196,10 +246,12 @@ module MidiSmtpServer
|
|
196
246
|
def initialize(
|
197
247
|
ports: DEFAULT_SMTPD_PORT,
|
198
248
|
hosts: DEFAULT_SMTPD_HOST,
|
249
|
+
pre_fork: DEFAULT_SMTPD_PRE_FORK,
|
199
250
|
max_processings: DEFAULT_SMTPD_MAX_PROCESSINGS,
|
200
251
|
max_connections: nil,
|
201
252
|
crlf_mode: nil,
|
202
253
|
do_dns_reverse_lookup: nil,
|
254
|
+
io_waitreadable_sleep: nil,
|
203
255
|
io_cmd_timeout: nil,
|
204
256
|
io_buffer_chunk_size: nil,
|
205
257
|
io_buffer_max_size: nil,
|
@@ -216,7 +268,7 @@ module MidiSmtpServer
|
|
216
268
|
logger: nil,
|
217
269
|
logger_severity: nil
|
218
270
|
)
|
219
|
-
# create an exposed logger to forward
|
271
|
+
# create an exposed logger to forward logging to the on_logging_event
|
220
272
|
@logger = MidiSmtpServer::ForwardingLogger.new(method(:on_logging_event))
|
221
273
|
|
222
274
|
# external logging
|
@@ -231,6 +283,11 @@ module MidiSmtpServer
|
|
231
283
|
logger.warn('Deprecated: "logger" was set on new! Please use "on_logging_event" instead.')
|
232
284
|
end
|
233
285
|
|
286
|
+
# initialize as master process
|
287
|
+
@is_forked = false
|
288
|
+
# no forked worker processes
|
289
|
+
@workers = []
|
290
|
+
|
234
291
|
# list of TCPServers
|
235
292
|
@tcp_servers = []
|
236
293
|
# list of running threads
|
@@ -311,12 +368,16 @@ module MidiSmtpServer
|
|
311
368
|
end
|
312
369
|
end
|
313
370
|
|
371
|
+
# read pre_fork
|
372
|
+
@pre_fork = pre_fork
|
373
|
+
raise 'Number of processes to pre-fork (pre_fork) must be zero or a positive integer greater than 1!' unless @pre_fork.is_a?(Integer) && (@pre_fork.zero? || pre_fork?)
|
314
374
|
# read max_processings
|
315
375
|
@max_processings = max_processings
|
316
376
|
raise 'Number of simultaneous processings (max_processings) must be a positive integer!' unless @max_processings.is_a?(Integer) && @max_processings.positive?
|
317
377
|
# check max_connections
|
318
378
|
@max_connections = max_connections
|
319
|
-
raise 'Number of concurrent connections
|
379
|
+
raise 'Number of concurrent connections (max_connections) must be nil or a positive integer!' unless @max_connections.nil? || (@max_connections.is_a?(Integer) && @max_connections.positive?)
|
380
|
+
raise 'Number of concurrent connections (max_connections) is lower than number of simultaneous processings (max_processings)!' if !@max_connections.nil? && @max_connections < @max_processings
|
320
381
|
|
321
382
|
# check for crlf mode
|
322
383
|
@crlf_mode = crlf_mode.nil? ? DEFAULT_CRLF_MODE : crlf_mode
|
@@ -328,6 +389,7 @@ module MidiSmtpServer
|
|
328
389
|
@do_dns_reverse_lookup = do_dns_reverse_lookup.nil? ? true : do_dns_reverse_lookup
|
329
390
|
|
330
391
|
# io and buffer settings
|
392
|
+
@io_waitreadable_sleep = io_waitreadable_sleep.nil? ? DEFAULT_IO_WAITREADABLE_SLEEP : io_waitreadable_sleep
|
331
393
|
@io_cmd_timeout = io_cmd_timeout.nil? ? DEFAULT_IO_CMD_TIMEOUT : io_cmd_timeout
|
332
394
|
@io_buffer_chunk_size = io_buffer_chunk_size.nil? ? DEFAULT_IO_BUFFER_CHUNK_SIZE : io_buffer_chunk_size
|
333
395
|
@io_buffer_max_size = io_buffer_max_size.nil? ? DEFAULT_IO_BUFFER_MAX_SIZE : io_buffer_max_size
|
@@ -336,9 +398,9 @@ module MidiSmtpServer
|
|
336
398
|
@pipelining_extension = pipelining_extension.nil? ? DEFAULT_PIPELINING_EXTENSION_ENABLED : pipelining_extension
|
337
399
|
@internationalization_extensions = internationalization_extensions.nil? ? DEFAULT_INTERNATIONALIZATION_EXTENSIONS_ENABLED : internationalization_extensions
|
338
400
|
|
339
|
-
# check for
|
401
|
+
# check for authentication
|
340
402
|
@auth_mode = auth_mode.nil? ? DEFAULT_AUTH_MODE : auth_mode
|
341
|
-
raise "Unknown
|
403
|
+
raise "Unknown authentication mode #{@auth_mode} was given!" unless AUTH_MODES.include?(@auth_mode)
|
342
404
|
|
343
405
|
# check for encryption
|
344
406
|
@encrypt_mode = tls_mode.nil? ? DEFAULT_ENCRYPT_MODE : tls_mode
|
@@ -409,7 +471,7 @@ module MidiSmtpServer
|
|
409
471
|
on_logging_event(ctx, Logger::DEBUG, "Client connect from #{ctx[:server][:remote_ip]}:#{ctx[:server][:remote_port]} to #{ctx[:server][:local_ip]}:#{ctx[:server][:local_port]}")
|
410
472
|
end
|
411
473
|
|
412
|
-
# event before
|
474
|
+
# event before DISCONNECT
|
413
475
|
def on_disconnect_event(ctx)
|
414
476
|
on_logging_event(ctx, Logger::DEBUG, "Client disconnect from #{ctx[:server][:remote_ip]}:#{ctx[:server][:remote_port]} on #{ctx[:server][:local_ip]}:#{ctx[:server][:local_port]}")
|
415
477
|
end
|
@@ -420,13 +482,13 @@ module MidiSmtpServer
|
|
420
482
|
# the value is not allowed to return CR nor LF chars and will be stripped
|
421
483
|
def on_helo_event(ctx, helo_data) end
|
422
484
|
|
423
|
-
# check the
|
485
|
+
# check the authentication on AUTH
|
424
486
|
# if any value returned, that will be used for ongoing processing
|
425
487
|
# otherwise the original value will be used for authorization_id
|
426
488
|
def on_auth_event(ctx, authorization_id, authentication_id, authentication)
|
427
|
-
# if
|
489
|
+
# if authentication is used, override this event
|
428
490
|
# and implement your own user management.
|
429
|
-
# otherwise all
|
491
|
+
# otherwise all authentications are blocked per default
|
430
492
|
on_logging_event(ctx, Logger::DEBUG, "Deny access from #{ctx[:server][:remote_ip]}:#{ctx[:server][:remote_port]} for #{authentication_id}" + (authorization_id == '' ? '' : "/#{authorization_id}") + " with #{authentication}")
|
431
493
|
raise Smtpd535Exception
|
432
494
|
end
|
@@ -473,8 +535,8 @@ module MidiSmtpServer
|
|
473
535
|
|
474
536
|
protected
|
475
537
|
|
476
|
-
#
|
477
|
-
def
|
538
|
+
# Prepare all listeners for all hosts
|
539
|
+
def create_service
|
478
540
|
raise 'Service was already started' unless stopped?
|
479
541
|
|
480
542
|
# set flag to signal shutdown by stop / shutdown command
|
@@ -485,12 +547,12 @@ module MidiSmtpServer
|
|
485
547
|
# break address into ip_address and port and serve service
|
486
548
|
ip_address = address.rpartition(':').first
|
487
549
|
port = address.rpartition(':').last
|
488
|
-
|
550
|
+
create_service_on_ip_address_and_port(ip_address, port)
|
489
551
|
end
|
490
552
|
end
|
491
553
|
|
492
|
-
#
|
493
|
-
def
|
554
|
+
# Prepare a listener on single ip_address and port
|
555
|
+
def create_service_on_ip_address_and_port(ip_address, port)
|
494
556
|
# log information
|
495
557
|
logger.info("Starting service on #{ip_address}:#{port}")
|
496
558
|
# check that there is a specific ip_address defined
|
@@ -499,7 +561,47 @@ module MidiSmtpServer
|
|
499
561
|
tcp_server = TCPServer.new(ip_address, port)
|
500
562
|
# append this server to the list of TCPServers
|
501
563
|
@tcp_servers << tcp_server
|
564
|
+
end
|
565
|
+
|
566
|
+
# Close and remove the given tcp_server
|
567
|
+
def remove_tcp_server(tcp_server)
|
568
|
+
begin
|
569
|
+
# drop the service
|
570
|
+
tcp_server.close
|
571
|
+
# remove from list
|
572
|
+
@tcp_servers.delete(tcp_server)
|
573
|
+
rescue StandardError
|
574
|
+
# ignore any error from here
|
575
|
+
end
|
576
|
+
end
|
502
577
|
|
578
|
+
# Join with the server thread(s)
|
579
|
+
# before joining the server threads, wait optionally a few seconds
|
580
|
+
# to let the service(s) come up
|
581
|
+
def join_threads(sleep_seconds_before_join: 1)
|
582
|
+
# wait some seconds before joining the upcoming threads
|
583
|
+
# and check that all TCPServers got one thread
|
584
|
+
while (@tcp_server_threads.length < @tcp_servers.length) && sleep_seconds_before_join.positive?
|
585
|
+
sleep_seconds_before_join -= 1
|
586
|
+
sleep 1
|
587
|
+
end
|
588
|
+
# try to join any thread
|
589
|
+
begin
|
590
|
+
@tcp_server_threads.each(&:join)
|
591
|
+
|
592
|
+
# catch ctrl-c to stop service
|
593
|
+
rescue Interrupt
|
594
|
+
end
|
595
|
+
end
|
596
|
+
|
597
|
+
# Create and attach threads to all listeners
|
598
|
+
def attach_threads
|
599
|
+
# loop for all TCPServers listener
|
600
|
+
@tcp_servers.each { |tcp_server| attach_thread(tcp_server) }
|
601
|
+
end
|
602
|
+
|
603
|
+
# Create and attach a thread to a listener
|
604
|
+
def attach_thread(tcp_server)
|
503
605
|
# run thread until shutdown
|
504
606
|
@tcp_server_threads << Thread.new do
|
505
607
|
begin
|
@@ -527,7 +629,7 @@ module MidiSmtpServer
|
|
527
629
|
ensure
|
528
630
|
begin
|
529
631
|
# always gracefully shutdown connection.
|
530
|
-
# if the io object was
|
632
|
+
# if the io object was overridden by the
|
531
633
|
# result from serve_client() due to ssl
|
532
634
|
# io, the ssl + io socket will be closed
|
533
635
|
io.close
|
@@ -552,16 +654,7 @@ module MidiSmtpServer
|
|
552
654
|
# log fatal error while starting new thread
|
553
655
|
on_logging_event(nil, Logger::FATAL, "#{e} (#{e.class})".strip, err: e.clone)
|
554
656
|
ensure
|
555
|
-
|
556
|
-
# drop the service
|
557
|
-
tcp_server.close
|
558
|
-
# remove from list
|
559
|
-
@tcp_servers.delete(tcp_server)
|
560
|
-
# reset local var
|
561
|
-
tcp_server = nil
|
562
|
-
rescue StandardError
|
563
|
-
# ignore any error from here
|
564
|
-
end
|
657
|
+
# proper connection down?
|
565
658
|
if shutdown?
|
566
659
|
# wait for finishing opened connections
|
567
660
|
@connections_mutex.synchronize do
|
@@ -604,7 +697,7 @@ module MidiSmtpServer
|
|
604
697
|
on_connect_event(session[:ctx])
|
605
698
|
|
606
699
|
# drop connection (respond 421) if too busy
|
607
|
-
raise 'Abort connection while too busy, exceeding max_connections!' if max_connections && connections > max_connections
|
700
|
+
raise Smtpd421Exception, 'Abort connection while too busy, exceeding max_connections!' if !max_connections.nil? && connections > max_connections
|
608
701
|
|
609
702
|
# check active processings for new client
|
610
703
|
@connections_mutex.synchronize do
|
@@ -656,7 +749,7 @@ module MidiSmtpServer
|
|
656
749
|
raise SmtpdIOTimeoutException if @io_cmd_timeout && Time.now.to_i - timestamp_timeout > @io_cmd_timeout
|
657
750
|
# read chunks of input data until line-feed
|
658
751
|
io_buffer << io.read_nonblock(@io_buffer_chunk_size)
|
659
|
-
# check for
|
752
|
+
# check for buffer size
|
660
753
|
raise SmtpdIOBufferOverrunException if @io_buffer_max_size && io_buffer.length > @io_buffer_max_size
|
661
754
|
# check for lf in current io_buffer
|
662
755
|
io_buffer_line_lf = io_buffer.index("\n")
|
@@ -665,7 +758,7 @@ module MidiSmtpServer
|
|
665
758
|
# ignore exception when no input data is available yet
|
666
759
|
rescue IO::WaitReadable
|
667
760
|
# but wait a few moment to slow down system utilization
|
668
|
-
sleep
|
761
|
+
sleep @io_waitreadable_sleep
|
669
762
|
end
|
670
763
|
|
671
764
|
# check if io_buffer is filled and contains already a line-feed
|
@@ -684,7 +777,7 @@ module MidiSmtpServer
|
|
684
777
|
# handle input line based on @crlf_mode
|
685
778
|
case crlf_mode
|
686
779
|
when :CRLF_ENSURE
|
687
|
-
# remove any \r or \n
|
780
|
+
# remove any \r or \n occurrence from line
|
688
781
|
line.delete!("\r\n")
|
689
782
|
# log line, verbosity based on log severity and command sequence
|
690
783
|
on_logging_event(session[:ctx], Logger::DEBUG, +'<<< ' << line << "\n") if session[:cmd_sequence] != :CMD_DATA
|
@@ -1235,7 +1328,7 @@ module MidiSmtpServer
|
|
1235
1328
|
)
|
1236
1329
|
end
|
1237
1330
|
|
1238
|
-
# handle plain
|
1331
|
+
# handle plain authentication
|
1239
1332
|
def process_auth_plain(session, encoded_auth_response)
|
1240
1333
|
begin
|
1241
1334
|
# extract auth id (and password)
|
metadata
CHANGED
@@ -1,17 +1,18 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: midi-smtp-server
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.
|
4
|
+
version: 3.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Tom Freudenberg
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-05-06 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
|
-
description:
|
14
|
-
for AUTH and SSL/STARTTLS
|
13
|
+
description: MidiSmtpServer is the highly customizable ruby SMTP-Server and SMTP-Service
|
14
|
+
library with builtin support for AUTH and SSL/STARTTLS, 8BITMIME and SMTPUTF8, IPv4
|
15
|
+
and IPv6 and additional features.
|
15
16
|
email: develop.rb.midi-smtp-server@4commerce.net
|
16
17
|
executables: []
|
17
18
|
extensions: []
|
@@ -25,7 +26,7 @@ files:
|
|
25
26
|
- lib/midi-smtp-server/logger.rb
|
26
27
|
- lib/midi-smtp-server/tls-transport.rb
|
27
28
|
- lib/midi-smtp-server/version.rb
|
28
|
-
homepage:
|
29
|
+
homepage: https://4commerce-technologies-ag.github.io/midi-smtp-server
|
29
30
|
licenses:
|
30
31
|
- MIT
|
31
32
|
metadata:
|
@@ -33,7 +34,7 @@ metadata:
|
|
33
34
|
source_code_uri: https://github.com/4commerce-technologies-AG/midi-smtp-server
|
34
35
|
changelog_uri: https://github.com/4commerce-technologies-AG/midi-smtp-server#changes-and-updates
|
35
36
|
bug_tracker_uri: https://github.com/4commerce-technologies-AG/midi-smtp-server/issues
|
36
|
-
documentation_uri: https://www.rubydoc.info/gems/midi-smtp-server/3.
|
37
|
+
documentation_uri: https://www.rubydoc.info/gems/midi-smtp-server/3.1.2
|
37
38
|
wiki_uri: https://midi-smtp-server.readthedocs.io/
|
38
39
|
post_install_message:
|
39
40
|
rdoc_options: []
|
@@ -50,7 +51,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
50
51
|
- !ruby/object:Gem::Version
|
51
52
|
version: '0'
|
52
53
|
requirements: []
|
53
|
-
rubygems_version: 3.
|
54
|
+
rubygems_version: 3.4.1
|
54
55
|
signing_key:
|
55
56
|
specification_version: 4
|
56
57
|
summary: MidiSmtpServer Class
|