mailcatcher-ng 1.0.0 → 1.2.0
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/README.md +46 -197
- data/lib/mail_catcher/smtp.rb +46 -2
- data/lib/mail_catcher/version.rb +1 -1
- data/lib/mail_catcher/web/application.rb +4 -0
- data/lib/mail_catcher.rb +132 -3
- data/public/assets/atom-one-light.min.css +1 -0
- data/public/assets/highlight.min.js +1244 -0
- data/public/assets/mailcatcher.js +22 -4
- data/views/index.erb +96 -4
- data/views/server_info.erb +16 -0
- metadata +17 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: c6ce8da5001fe3dbf561a375431bda1fb4a1011dd09286307c05aa4ce0c206dd
|
|
4
|
+
data.tar.gz: 11727e123a3a6395ebf5ecf1eb93fc7258889f203260e2d6fbf9f3c3f67aaa43
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: dfd03e9c5c8e77348c9853004afdd37d23d3c7c1c7ee6dad7e38a38943d7e8e3943be25379835429dcd027ecccf948ddde05b71227f3382fa78ac13fdeebf2fd
|
|
7
|
+
data.tar.gz: dd822cf0ca1c50a307bfb8001e20187690b8ce99a8b1ef0ef043c094b51682e90e103ece5bf4d1d278740ef020cecb77d4632ab165d290bb2a2169634dc4cafa
|
data/README.md
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
# MailCatcher NG (Next Generation)
|
|
2
2
|
|
|
3
3
|
[](https://rubygems.org/gems/mailcatcher-ng)
|
|
4
|
-
[](https://github.com/spaquet/mailcatcher/actions/workflows/ci.yml)
|
|
5
|
+
[](LICENSE)
|
|
6
6
|
|
|
7
7
|
Catches mail and serves it through a dream.
|
|
8
8
|
|
|
@@ -10,226 +10,75 @@ MailCatcher NG runs a super simple SMTP server which catches any message sent to
|
|
|
10
10
|
|
|
11
11
|

|
|
12
12
|
|
|
13
|
+
## Table of Contents
|
|
13
14
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
* Sendmail-analogue command, `catchmail`, makes using mailcatcher from PHP a lot easier.
|
|
25
|
-
* Keyboard navigation between messages
|
|
26
|
-
* Email authentication verification (DMARC, DKIM, SPF)
|
|
27
|
-
* Email encryption and signature support (S/MIME and OpenPGP)
|
|
28
|
-
* BIMI (Brand Indicators for Message Identification) display
|
|
29
|
-
* Advanced preview text extraction with intelligent fallback
|
|
30
|
-
|
|
31
|
-
For a comprehensive list of all features, see [FEATURES.md](FEATURES.md).
|
|
15
|
+
- [Quick Start](#quick-start)
|
|
16
|
+
- [Features](#features)
|
|
17
|
+
- [Documentation](#documentation)
|
|
18
|
+
- [Installation & Setup](reference/INSTALLATION.md)
|
|
19
|
+
- [Usage & Configuration](reference/USAGE.md)
|
|
20
|
+
- [Framework Integration](reference/INTEGRATIONS.md)
|
|
21
|
+
- [REST API](reference/API.md)
|
|
22
|
+
- [Advanced Features](reference/ADVANCED.md)
|
|
23
|
+
- [Credits](reference/CREDITS.md)
|
|
24
|
+
- [License](#license)
|
|
32
25
|
|
|
33
|
-
##
|
|
26
|
+
## Quick Start
|
|
34
27
|
|
|
35
28
|
1. `gem install mailcatcher-ng`
|
|
36
29
|
2. `mailcatcher`
|
|
37
30
|
3. Go to http://127.0.0.1:1080/
|
|
38
31
|
4. Send mail through smtp://127.0.0.1:1025
|
|
39
32
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
To run MailCatcher in development mode with custom ports:
|
|
43
|
-
|
|
44
|
-
```bash
|
|
45
|
-
MAILCATCHER_ENV=development bundle exec mailcatcher --foreground --smtp-port 1025 --http-port 1080
|
|
46
|
-
```
|
|
47
|
-
|
|
48
|
-
Then access the web interface at [http://127.0.0.1:1080](http://127.0.0.1:1080) and send mail to `smtp://127.0.0.1:1025`.
|
|
49
|
-
|
|
50
|
-
Or use the provided test script to send example emails:
|
|
51
|
-
|
|
52
|
-
```bash
|
|
53
|
-
SMTP_HOST=127.0.0.1 SMTP_PORT=20025 ruby send_example_emails.rb
|
|
54
|
-
```
|
|
55
|
-
|
|
56
|
-
### WebSocket Testing
|
|
57
|
-
|
|
58
|
-
To monitor WebSocket connectivity and test the connection status, visit [http://127.0.0.1:1080/websocket-test](http://127.0.0.1:1080/websocket-test). This page provides real-time feedback on WebSocket connection state and helps verify that automatic reconnection with exponential backoff is functioning correctly.
|
|
59
|
-
|
|
60
|
-
### Command Line Options
|
|
61
|
-
|
|
62
|
-
Use `mailcatcher --help` to see the command line options.
|
|
63
|
-
|
|
64
|
-
```
|
|
65
|
-
Usage: mailcatcher [options]
|
|
66
|
-
|
|
67
|
-
MailCatcher v0.11.2
|
|
68
|
-
|
|
69
|
-
--ip IP Set the ip address of both servers
|
|
70
|
-
--smtp-ip IP Set the ip address of the smtp server
|
|
71
|
-
--smtp-port PORT Set the port of the smtp server
|
|
72
|
-
--http-ip IP Set the ip address of the http server
|
|
73
|
-
--http-port PORT Set the port address of the http server
|
|
74
|
-
--messages-limit COUNT Only keep up to COUNT most recent messages
|
|
75
|
-
--http-path PATH Add a prefix to all HTTP paths
|
|
76
|
-
--no-quit Don't allow quitting the process
|
|
77
|
-
-f, --foreground Run in the foreground
|
|
78
|
-
-b, --browse Open web browser
|
|
79
|
-
-v, --verbose Be more verbose
|
|
80
|
-
-h, --help Display this help information
|
|
81
|
-
--version Display the current version
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
### Upgrading
|
|
85
|
-
|
|
86
|
-
Upgrading works the same as installation:
|
|
87
|
-
|
|
88
|
-
```
|
|
89
|
-
gem install mailcatcher
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
### Ruby
|
|
93
|
-
|
|
94
|
-
If you have trouble with the setup commands, make sure you have [Ruby installed](https://www.ruby-lang.org/en/documentation/installation/):
|
|
95
|
-
|
|
96
|
-
```
|
|
97
|
-
ruby -v
|
|
98
|
-
gem environment
|
|
99
|
-
```
|
|
100
|
-
|
|
101
|
-
You might need to install build tools for some of the gem dependencies. On Debian or Ubuntu, `apt install build-essential`. On macOS, `xcode-select --install`.
|
|
102
|
-
|
|
103
|
-
### How to Compile the Gem
|
|
104
|
-
|
|
105
|
-
To compile MailCatcher as a gem from source:
|
|
106
|
-
|
|
107
|
-
1. Clone the repository:
|
|
108
|
-
|
|
109
|
-
```bash
|
|
110
|
-
git clone https://github.com/spaquet/mailcatcher.git
|
|
111
|
-
cd mailcatcher
|
|
112
|
-
```
|
|
113
|
-
|
|
114
|
-
1. Install dependencies:
|
|
115
|
-
|
|
116
|
-
```bash
|
|
117
|
-
bundle install
|
|
118
|
-
```
|
|
119
|
-
|
|
120
|
-
1. Compile assets and build the gem:
|
|
121
|
-
|
|
122
|
-
```bash
|
|
123
|
-
bundle exec rake package
|
|
124
|
-
```
|
|
125
|
-
|
|
126
|
-
This will create a `.gem` file in the project directory. The build process:
|
|
127
|
-
|
|
128
|
-
* Compiles JavaScript assets using Sprockets and Uglifier
|
|
129
|
-
* Creates a gem package with all required files
|
|
130
|
-
|
|
131
|
-
You can then install the compiled gem locally:
|
|
132
|
-
|
|
133
|
-
```bash
|
|
134
|
-
gem install mailcatcher-VERSION.gem
|
|
135
|
-
```
|
|
136
|
-
|
|
137
|
-
### Bundler
|
|
138
|
-
|
|
139
|
-
Please don't put mailcatcher into your Gemfile. It will conflict with your application's gems at some point.
|
|
140
|
-
|
|
141
|
-
Instead, pop a note in your README stating you use mailcatcher, and to run `gem install mailcatcher` then `mailcatcher` to get started.
|
|
142
|
-
|
|
143
|
-
### RVM
|
|
144
|
-
|
|
145
|
-
Under RVM your mailcatcher command may only be available under the ruby you install mailcatcher into. To prevent this, and to prevent gem conflicts, install mailcatcher into a dedicated gemset with a wrapper script:
|
|
146
|
-
|
|
147
|
-
rvm default@mailcatcher --create do gem install mailcatcher
|
|
148
|
-
ln -s "$(rvm default@mailcatcher do rvm wrapper show mailcatcher)" "$rvm_bin_path/"
|
|
149
|
-
|
|
150
|
-
### Rails
|
|
151
|
-
|
|
152
|
-
To set up your rails app, I recommend adding this to your `environments/development.rb`:
|
|
153
|
-
|
|
154
|
-
config.action_mailer.delivery_method = :smtp
|
|
155
|
-
config.action_mailer.smtp_settings = { :address => '127.0.0.1', :port => 1025 }
|
|
156
|
-
config.action_mailer.raise_delivery_errors = false
|
|
157
|
-
|
|
158
|
-
### PHP
|
|
159
|
-
|
|
160
|
-
For projects using PHP, or PHP frameworks and application platforms like Drupal, you can set [PHP's mail configuration](https://www.php.net/manual/en/mail.configuration.php) in your [php.ini](https://www.php.net/manual/en/configuration.file.php) to send via MailCatcher with:
|
|
161
|
-
|
|
162
|
-
sendmail_path = /usr/bin/env catchmail -f some@from.address
|
|
33
|
+
## Features
|
|
163
34
|
|
|
164
|
-
|
|
35
|
+
- Catches all mail and stores it for display
|
|
36
|
+
- Shows HTML, Plain Text, and Source versions of messages
|
|
37
|
+
- Rewrites HTML for safe display with embedded images
|
|
38
|
+
- Downloads original email to view in any mail client
|
|
39
|
+
- WebSockets real-time updates with automatic reconnection
|
|
40
|
+
- Runs as a daemon in the background, or in foreground mode
|
|
41
|
+
- `catchmail` command for PHP and sendmail-compatible systems
|
|
42
|
+
- Keyboard navigation between messages
|
|
43
|
+
- Email authentication verification (DMARC, DKIM, SPF)
|
|
44
|
+
- Email encryption & signature support (S/MIME, OpenPGP)
|
|
45
|
+
- BIMI (Brand Indicators for Message Identification) display
|
|
46
|
+
- Advanced preview text extraction with intelligent fallback
|
|
47
|
+
- Full UTF-8 and 8bit MIME transfer encoding support (SMTPUTF8, 8BITMIME)
|
|
48
|
+
- Multiple encoding support (7bit, 8bit, base64, quoted-printable)
|
|
49
|
+
- SSL/TLS encryption (STARTTLS & direct TLS/SMTPS)
|
|
50
|
+
- Charset preservation for international content
|
|
165
51
|
|
|
166
|
-
|
|
52
|
+
For a comprehensive list of all features, see [FEATURES.md](FEATURES.md).
|
|
167
53
|
|
|
168
|
-
|
|
54
|
+
## Documentation
|
|
169
55
|
|
|
170
|
-
|
|
56
|
+
Detailed documentation is organized by topic:
|
|
171
57
|
|
|
172
|
-
|
|
58
|
+
### [Installation & Setup](reference/INSTALLATION.md)
|
|
173
59
|
|
|
174
|
-
|
|
60
|
+
Get MailCatcher NG up and running. Covers gem installation, source compilation, Docker, and special scenarios like RVM and Bundler.
|
|
175
61
|
|
|
176
|
-
|
|
62
|
+
### [Usage & Configuration](reference/USAGE.md)
|
|
177
63
|
|
|
178
|
-
|
|
179
|
-
if DEBUG:
|
|
180
|
-
EMAIL_HOST = '127.0.0.1'
|
|
181
|
-
EMAIL_HOST_USER = ''
|
|
182
|
-
EMAIL_HOST_PASSWORD = ''
|
|
183
|
-
EMAIL_PORT = 1025
|
|
184
|
-
EMAIL_USE_TLS = False
|
|
185
|
-
```
|
|
64
|
+
Learn how to run MailCatcher NG, command-line options, development mode, and web interface features.
|
|
186
65
|
|
|
187
|
-
###
|
|
66
|
+
### [Framework Integration](reference/INTEGRATIONS.md)
|
|
188
67
|
|
|
189
|
-
|
|
68
|
+
Configure your framework (Rails, Django, PHP, Docker) to send mail through MailCatcher NG.
|
|
190
69
|
|
|
191
|
-
|
|
192
|
-
$ docker run -d -p 1080:1080 -p 1025:1025 stpaquet/alpinemailcatcher
|
|
193
|
-
Unable to find image 'stpaquet/alpinemailcatcher:latest' locally
|
|
194
|
-
latest: Pulling from stpaquet/alpinemailcatcher
|
|
195
|
-
4abcf2090661: Pull complete
|
|
196
|
-
9f403268fa96: Pull complete
|
|
197
|
-
6c9f5f5b4c6d: Pull complete
|
|
198
|
-
Digest: sha256:a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0u1v2w3x4y5z6a7b8c9d0
|
|
199
|
-
Status: Downloaded newer image for stpaquet/alpinemailcatcher:latest
|
|
200
|
-
Starting MailCatcher v0.12.0
|
|
201
|
-
==> smtp://0.0.0.0:1025
|
|
202
|
-
==> http://0.0.0.0:1080
|
|
203
|
-
```
|
|
70
|
+
### [REST API](reference/API.md)
|
|
204
71
|
|
|
205
|
-
|
|
72
|
+
Programmatic access to messages. Query, download, and manage messages via HTTP.
|
|
206
73
|
|
|
207
|
-
###
|
|
74
|
+
### [Advanced Features](reference/ADVANCED.md)
|
|
208
75
|
|
|
209
|
-
|
|
76
|
+
SSL/TLS encryption, UTF-8 and international content, email authentication, and more.
|
|
210
77
|
|
|
211
|
-
|
|
78
|
+
### [Credits](reference/CREDITS.md)
|
|
212
79
|
|
|
213
|
-
|
|
214
|
-
* Encodings are difficult. MailCatcher NG does not completely support utf-8 straight over the wire, you must use a mail library which encodes things properly based on SMTP server capabilities.
|
|
80
|
+
About MailCatcher NG and the original MailCatcher project.
|
|
215
81
|
|
|
216
82
|
## License
|
|
217
83
|
|
|
218
84
|
MailCatcher NG is released under the MIT License, see [LICENSE](LICENSE) for details.
|
|
219
|
-
|
|
220
|
-
## Credits
|
|
221
|
-
|
|
222
|
-
MailCatcher NG is a significantly improved fork of the original MailCatcher project. We've added many advanced features including:
|
|
223
|
-
|
|
224
|
-
* Email authentication verification (DMARC, DKIM, SPF)
|
|
225
|
-
* Email encryption and signature display (S/MIME and OpenPGP)
|
|
226
|
-
* BIMI (Brand Indicators for Message Identification) support
|
|
227
|
-
* Advanced preview text extraction with 3-tier fallback system
|
|
228
|
-
* Enhanced sender/recipient name parsing and display
|
|
229
|
-
* Improved UI/UX with keyboard navigation and better filtering
|
|
230
|
-
* WebSocket real-time updates with automatic reconnection
|
|
231
|
-
* And many more improvements to the original codebase
|
|
232
|
-
|
|
233
|
-
The original MailCatcher project was created by Samuel Cochran and released under the MIT License. We're grateful for this solid foundation and have built upon it to create MailCatcher NG.
|
|
234
|
-
|
|
235
|
-
[websockets]: https://tools.ietf.org/html/rfc6455
|
data/lib/mail_catcher/smtp.rb
CHANGED
|
@@ -5,6 +5,22 @@ require "eventmachine"
|
|
|
5
5
|
require "mail_catcher/mail"
|
|
6
6
|
|
|
7
7
|
class MailCatcher::Smtp < EventMachine::Protocols::SmtpServer
|
|
8
|
+
@@active_connections = 0
|
|
9
|
+
|
|
10
|
+
def self.connection_count
|
|
11
|
+
@@active_connections
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def post_init
|
|
15
|
+
@@active_connections += 1
|
|
16
|
+
super
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def unbind
|
|
20
|
+
@@active_connections -= 1
|
|
21
|
+
super
|
|
22
|
+
end
|
|
23
|
+
|
|
8
24
|
# We override EM's mail from processing to allow multiple mail-from commands
|
|
9
25
|
# per [RFC 2821](https://tools.ietf.org/html/rfc2821#section-4.1.1.2)
|
|
10
26
|
def process_mail_from sender
|
|
@@ -27,12 +43,26 @@ class MailCatcher::Smtp < EventMachine::Protocols::SmtpServer
|
|
|
27
43
|
true
|
|
28
44
|
end
|
|
29
45
|
|
|
46
|
+
def get_server_capabilities
|
|
47
|
+
# Advertise SMTP capabilities per RFC standards
|
|
48
|
+
# SIZE: RFC 1870 - Message size extension
|
|
49
|
+
# 8BITMIME: RFC 6152 - 8bit MIME transport
|
|
50
|
+
# SMTPUTF8: RFC 6531 - UTF-8 support in SMTP
|
|
51
|
+
capabilities = super.to_a
|
|
52
|
+
capabilities << "8BITMIME"
|
|
53
|
+
capabilities << "SMTPUTF8"
|
|
54
|
+
capabilities
|
|
55
|
+
end
|
|
56
|
+
|
|
30
57
|
def receive_sender(sender)
|
|
31
58
|
# EventMachine SMTP advertises size extensions [https://tools.ietf.org/html/rfc1870]
|
|
32
|
-
#
|
|
33
|
-
|
|
59
|
+
# and other SMTP parameters via the MAIL FROM command
|
|
60
|
+
# Strip potential " SIZE=..." and "BODY=..." suffixes from senders
|
|
61
|
+
sender = sender.gsub(/ (?:SIZE|BODY)=\S+/i, "")
|
|
34
62
|
|
|
35
63
|
current_message[:sender] = sender
|
|
64
|
+
# Store the original sender line to track if 8BIT was specified
|
|
65
|
+
current_message[:sender_line] = sender
|
|
36
66
|
|
|
37
67
|
true
|
|
38
68
|
end
|
|
@@ -66,3 +96,17 @@ class MailCatcher::Smtp < EventMachine::Protocols::SmtpServer
|
|
|
66
96
|
@current_message = nil
|
|
67
97
|
end
|
|
68
98
|
end
|
|
99
|
+
|
|
100
|
+
# Direct TLS (SMTPS) handler that starts TLS immediately on connection
|
|
101
|
+
class MailCatcher::SmtpTls < MailCatcher::Smtp
|
|
102
|
+
def post_init
|
|
103
|
+
# Increment connection count
|
|
104
|
+
super
|
|
105
|
+
|
|
106
|
+
# Start TLS immediately on connection for SMTPS (port 1465 behavior)
|
|
107
|
+
# The @@parms hash with starttls_options is already set by configure_smtp_ssl!
|
|
108
|
+
if defined?(@@parms) && @@parms[:starttls_options]
|
|
109
|
+
start_tls(@@parms[:starttls_options])
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
data/lib/mail_catcher/version.rb
CHANGED
|
@@ -81,6 +81,10 @@ module MailCatcher
|
|
|
81
81
|
@http_port = MailCatcher.options[:http_port]
|
|
82
82
|
@http_path = MailCatcher.options[:http_path]
|
|
83
83
|
|
|
84
|
+
# Get current connection counts
|
|
85
|
+
@http_connections = MailCatcher.http_server&.backend&.size || 0
|
|
86
|
+
@smtp_connections = MailCatcher::Smtp.connection_count
|
|
87
|
+
|
|
84
88
|
require "socket"
|
|
85
89
|
if @http_ip == "127.0.0.1"
|
|
86
90
|
@hostname = "localhost"
|
data/lib/mail_catcher.rb
CHANGED
|
@@ -4,6 +4,7 @@ require 'logger'
|
|
|
4
4
|
require 'open3'
|
|
5
5
|
require 'optparse'
|
|
6
6
|
require 'rbconfig'
|
|
7
|
+
require 'openssl'
|
|
7
8
|
|
|
8
9
|
require 'eventmachine'
|
|
9
10
|
require 'thin'
|
|
@@ -75,6 +76,11 @@ module MailCatcher
|
|
|
75
76
|
@defaults = {
|
|
76
77
|
smtp_ip: '127.0.0.1',
|
|
77
78
|
smtp_port: '1025',
|
|
79
|
+
smtp_ssl: false,
|
|
80
|
+
smtp_ssl_cert: nil,
|
|
81
|
+
smtp_ssl_key: nil,
|
|
82
|
+
smtp_ssl_verify_peer: false,
|
|
83
|
+
smtps_port: '1465',
|
|
78
84
|
http_ip: '127.0.0.1',
|
|
79
85
|
http_port: '1080',
|
|
80
86
|
http_path: '/',
|
|
@@ -89,6 +95,10 @@ module MailCatcher
|
|
|
89
95
|
@options
|
|
90
96
|
end
|
|
91
97
|
|
|
98
|
+
def http_server
|
|
99
|
+
@http_server
|
|
100
|
+
end
|
|
101
|
+
|
|
92
102
|
def quittable?
|
|
93
103
|
options[:quit]
|
|
94
104
|
end
|
|
@@ -114,6 +124,26 @@ module MailCatcher
|
|
|
114
124
|
options[:smtp_port] = port
|
|
115
125
|
end
|
|
116
126
|
|
|
127
|
+
parser.on('--smtp-ssl', 'Enable SSL/TLS support for SMTP') do
|
|
128
|
+
options[:smtp_ssl] = true
|
|
129
|
+
end
|
|
130
|
+
|
|
131
|
+
parser.on('--smtp-ssl-cert PATH', 'Path to SSL certificate file (required with --smtp-ssl)') do |path|
|
|
132
|
+
options[:smtp_ssl_cert] = path
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
parser.on('--smtp-ssl-key PATH', 'Path to SSL private key file (required with --smtp-ssl)') do |path|
|
|
136
|
+
options[:smtp_ssl_key] = path
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
parser.on('--smtp-ssl-verify-peer', 'Verify client SSL certificates') do
|
|
140
|
+
options[:smtp_ssl_verify_peer] = true
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
parser.on('--smtps-port PORT', Integer, 'Set the port for direct TLS SMTP server (default: 1465)') do |port|
|
|
144
|
+
options[:smtps_port] = port
|
|
145
|
+
end
|
|
146
|
+
|
|
117
147
|
parser.on('--http-ip IP', 'Set the ip address of the http server') do |ip|
|
|
118
148
|
options[:http_ip] = ip
|
|
119
149
|
end
|
|
@@ -175,15 +205,21 @@ module MailCatcher
|
|
|
175
205
|
# Stash them away for later
|
|
176
206
|
@options = options
|
|
177
207
|
|
|
208
|
+
# Validate SSL configuration if enabled
|
|
209
|
+
validate_ssl_config!
|
|
210
|
+
|
|
178
211
|
# If we're running in the foreground sync the output.
|
|
179
212
|
$stdout.sync = $stderr.sync = true unless options[:daemon]
|
|
180
213
|
|
|
181
|
-
@logger.info("Starting MailCatcher v#{VERSION}")
|
|
214
|
+
@logger.info("Starting MailCatcher NG v#{VERSION}")
|
|
182
215
|
|
|
183
216
|
Thin::Logging.debug = development?
|
|
184
217
|
Thin::Logging.silent = !development?
|
|
185
218
|
@logger.level = development? ? Logger::DEBUG : Logger::INFO
|
|
186
219
|
|
|
220
|
+
# Configure SSL/TLS if enabled
|
|
221
|
+
configure_smtp_ssl!
|
|
222
|
+
|
|
187
223
|
# One EventMachine loop...
|
|
188
224
|
EventMachine.run do
|
|
189
225
|
# Set up an SMTP server to run within EventMachine
|
|
@@ -192,10 +228,19 @@ module MailCatcher
|
|
|
192
228
|
@logger.info("==> #{smtp_url}")
|
|
193
229
|
end
|
|
194
230
|
|
|
231
|
+
# Set up direct TLS (SMTPS) server if SSL is enabled
|
|
232
|
+
if options[:smtp_ssl]
|
|
233
|
+
rescue_port options[:smtps_port] do
|
|
234
|
+
EventMachine.start_server options[:smtp_ip], options[:smtps_port], SmtpTls
|
|
235
|
+
@logger.info("==> #{smtps_url}")
|
|
236
|
+
end
|
|
237
|
+
end
|
|
238
|
+
|
|
195
239
|
# Let Thin set itself up inside our EventMachine loop
|
|
196
240
|
# Faye connections are hijacked but continue to be supervised by thin
|
|
197
241
|
rescue_port options[:http_port] do
|
|
198
|
-
Thin::Server.
|
|
242
|
+
@http_server = Thin::Server.new(options[:http_ip], options[:http_port], Web, signals: false)
|
|
243
|
+
@http_server.start
|
|
199
244
|
@logger.info("==> #{http_url}")
|
|
200
245
|
end
|
|
201
246
|
|
|
@@ -234,8 +279,92 @@ module MailCatcher
|
|
|
234
279
|
|
|
235
280
|
protected
|
|
236
281
|
|
|
282
|
+
def validate_ssl_config!
|
|
283
|
+
return unless @options[:smtp_ssl]
|
|
284
|
+
|
|
285
|
+
# Require certificate and key files
|
|
286
|
+
unless @options[:smtp_ssl_cert] && @options[:smtp_ssl_key]
|
|
287
|
+
@logger.error('SSL/TLS enabled but certificate or key file not specified')
|
|
288
|
+
@logger.error('Use --smtp-ssl-cert and --smtp-ssl-key to provide paths')
|
|
289
|
+
exit(-1)
|
|
290
|
+
end
|
|
291
|
+
|
|
292
|
+
# Check certificate file exists and is readable
|
|
293
|
+
unless File.exist?(@options[:smtp_ssl_cert])
|
|
294
|
+
@logger.error("SSL certificate file not found: #{@options[:smtp_ssl_cert]}")
|
|
295
|
+
exit(-1)
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
unless File.readable?(@options[:smtp_ssl_cert])
|
|
299
|
+
@logger.error("SSL certificate file not readable: #{@options[:smtp_ssl_cert]}")
|
|
300
|
+
exit(-1)
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
# Check key file exists and is readable
|
|
304
|
+
unless File.exist?(@options[:smtp_ssl_key])
|
|
305
|
+
@logger.error("SSL private key file not found: #{@options[:smtp_ssl_key]}")
|
|
306
|
+
exit(-1)
|
|
307
|
+
end
|
|
308
|
+
|
|
309
|
+
unless File.readable?(@options[:smtp_ssl_key])
|
|
310
|
+
@logger.error("SSL private key file not readable: #{@options[:smtp_ssl_key]}")
|
|
311
|
+
exit(-1)
|
|
312
|
+
end
|
|
313
|
+
|
|
314
|
+
# Try to load the certificate to validate format
|
|
315
|
+
begin
|
|
316
|
+
OpenSSL::X509::Certificate.new(File.read(@options[:smtp_ssl_cert]))
|
|
317
|
+
rescue OpenSSL::X509::CertificateError => e
|
|
318
|
+
@logger.error("Invalid SSL certificate file: #{e.message}")
|
|
319
|
+
exit(-1)
|
|
320
|
+
end
|
|
321
|
+
|
|
322
|
+
# Try to load the private key to validate format
|
|
323
|
+
begin
|
|
324
|
+
OpenSSL::PKey.read(File.read(@options[:smtp_ssl_key]))
|
|
325
|
+
rescue OpenSSL::PKey::PKeyError => e
|
|
326
|
+
@logger.error("Invalid SSL private key file: #{e.message}")
|
|
327
|
+
exit(-1)
|
|
328
|
+
end
|
|
329
|
+
|
|
330
|
+
@logger.info('SSL/TLS certificate and key validated successfully')
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
def configure_smtp_ssl!
|
|
334
|
+
return unless @options[:smtp_ssl]
|
|
335
|
+
|
|
336
|
+
# Build TLS options hash for EventMachine
|
|
337
|
+
ssl_options = {
|
|
338
|
+
cert_chain_file: @options[:smtp_ssl_cert],
|
|
339
|
+
private_key_file: @options[:smtp_ssl_key],
|
|
340
|
+
verify_peer: @options[:smtp_ssl_verify_peer]
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
# Configure the STARTTLS Smtp class
|
|
344
|
+
Smtp.class_variable_set(:@@parms, {
|
|
345
|
+
starttls: :required,
|
|
346
|
+
starttls_options: ssl_options
|
|
347
|
+
})
|
|
348
|
+
|
|
349
|
+
# Configure the Direct TLS SmtpTls class
|
|
350
|
+
SmtpTls.class_variable_set(:@@parms, {
|
|
351
|
+
starttls: :required,
|
|
352
|
+
starttls_options: ssl_options
|
|
353
|
+
})
|
|
354
|
+
|
|
355
|
+
@ssl_options = ssl_options
|
|
356
|
+
end
|
|
357
|
+
|
|
237
358
|
def smtp_url
|
|
238
|
-
|
|
359
|
+
if @options[:smtp_ssl]
|
|
360
|
+
"smtp+starttls://#{@options[:smtp_ip]}:#{@options[:smtp_port]}"
|
|
361
|
+
else
|
|
362
|
+
"smtp://#{@options[:smtp_ip]}:#{@options[:smtp_port]}"
|
|
363
|
+
end
|
|
364
|
+
end
|
|
365
|
+
|
|
366
|
+
def smtps_url
|
|
367
|
+
"smtps://#{@options[:smtp_ip]}:#{@options[:smtps_port]}"
|
|
239
368
|
end
|
|
240
369
|
|
|
241
370
|
def http_url
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pre code.hljs{display:block;overflow-x:auto;padding:1em}code.hljs{padding:3px 5px}.hljs{color:#383a42;background:#fafafa}.hljs-comment,.hljs-quote{color:#a0a1a7;font-style:italic}.hljs-doctag,.hljs-formula,.hljs-keyword{color:#a626a4}.hljs-deletion,.hljs-name,.hljs-section,.hljs-selector-tag,.hljs-subst{color:#e45649}.hljs-literal{color:#0184bb}.hljs-addition,.hljs-attribute,.hljs-meta .hljs-string,.hljs-regexp,.hljs-string{color:#50a14f}.hljs-attr,.hljs-number,.hljs-selector-attr,.hljs-selector-class,.hljs-selector-pseudo,.hljs-template-variable,.hljs-type,.hljs-variable{color:#986801}.hljs-bullet,.hljs-link,.hljs-meta,.hljs-selector-id,.hljs-symbol,.hljs-title{color:#4078f2}.hljs-built_in,.hljs-class .hljs-title,.hljs-title.class_{color:#c18401}.hljs-emphasis{font-style:italic}.hljs-strong{font-weight:700}.hljs-link{text-decoration:underline}
|