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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b76a8adf417f26e41c5f224b9fc7d521c4be7ac4c23daec6b43d3e8eefbc041d
4
- data.tar.gz: 50bdf55859d53c445390ee4c423388220ce339315bd05f2433825114f6777071
3
+ metadata.gz: c6ce8da5001fe3dbf561a375431bda1fb4a1011dd09286307c05aa4ce0c206dd
4
+ data.tar.gz: 11727e123a3a6395ebf5ecf1eb93fc7258889f203260e2d6fbf9f3c3f67aaa43
5
5
  SHA512:
6
- metadata.gz: a5593236368a57dc543bf39639e84671ff12982de1f24761053188569dad2c441f858f494486def40d1f46ffa499fe2f4b5e9fd63effa22d42b88b11e9565ce9
7
- data.tar.gz: b3e78add1e2465484218a86b6e8ff4be0ac99ddf276c18d7192e2588c595b43833a3e9198a5e998ff2309fac4ccbedd18f795d0a97bcc2fc4c1ef08773e92e28
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
  [![Gem Version](https://img.shields.io/gem/v/mailcatcher-ng)](https://rubygems.org/gems/mailcatcher-ng)
4
- [![CI](https://github.com/spaquet/mailcatcher-ng/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/spaquet/mailcatcher-ng/actions/workflows/ci.yml)
5
- [![GitHub License](https://img.shields.io/github/license/spaquet/mailcatcher-ng)](LICENSE)
4
+ [![CI](https://github.com/spaquet/mailcatcher/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/spaquet/mailcatcher/actions/workflows/ci.yml)
5
+ [![GitHub License](https://img.shields.io/github/license/spaquet/mailcatcher)](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
  ![MailCatcher screenshot](screenshots/inbox.webp)
12
12
 
13
+ ## Table of Contents
13
14
 
14
- ## Features
15
-
16
- * Catches all mail and stores it for display.
17
- * Shows HTML, Plain Text and Source version of messages, as applicable.
18
- * Rewrites HTML enabling display of embedded, inline images/etc and opens links in a new window.
19
- * Lists attachments and allows separate downloading of parts.
20
- * Download original email to view in your native mail client(s).
21
- * Command line options to override the default SMTP/HTTP IP and port settings.
22
- * Mail appears instantly if your browser supports [WebSockets][websockets] with automatic reconnection and exponential backoff, otherwise updates every thirty seconds.
23
- * Runs as a daemon in the background, optionally in foreground.
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
- ## How
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
- ### Development Mode
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
- You can do this in your [Apache configuration](https://www.php.net/manual/en/configuration.changes.php) like so:
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
- php_admin_value sendmail_path "/usr/bin/env catchmail -f some@from.address"
52
+ For a comprehensive list of all features, see [FEATURES.md](FEATURES.md).
167
53
 
168
- If you've installed via RVM this probably won't work unless you've manually added your RVM bin paths to your system environment's PATH. In that case, run `which catchmail` and put that path into the `sendmail_path` directive above instead of `/usr/bin/env catchmail`.
54
+ ## Documentation
169
55
 
170
- If starting `mailcatcher` on alternative SMTP IP and/or port with parameters like `--smtp-ip 192.168.0.1 --smtp-port 10025`, add the same parameters to your `catchmail` command:
56
+ Detailed documentation is organized by topic:
171
57
 
172
- sendmail_path = /usr/bin/env catchmail --smtp-ip 192.160.0.1 --smtp-port 10025 -f some@from.address
58
+ ### [Installation & Setup](reference/INSTALLATION.md)
173
59
 
174
- ### Django
60
+ Get MailCatcher NG up and running. Covers gem installation, source compilation, Docker, and special scenarios like RVM and Bundler.
175
61
 
176
- For use in Django, add the following configuration to your projects' settings.py
62
+ ### [Usage & Configuration](reference/USAGE.md)
177
63
 
178
- ```python
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
- ### Docker
66
+ ### [Framework Integration](reference/INTEGRATIONS.md)
188
67
 
189
- The official MailCatcher Docker image is available [on Docker Hub](https://hub.docker.com/r/stpaquet/alpinemailcatcher):
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
- How those ports appear and can be accessed may vary based on your Docker configuration. For example, you may need to use `http://127.0.0.1:1080` or `smtp://127.0.0.1:1025` instead of the listed address. The image is Alpine Linux based for minimal size and quick startup.
72
+ Programmatic access to messages. Query, download, and manage messages via HTTP.
206
73
 
207
- ### API
74
+ ### [Advanced Features](reference/ADVANCED.md)
208
75
 
209
- A fairly RESTful URL schema means you can download a list of messages in JSON from `/messages`, each message's metadata with `/messages/:id.json`, and then the pertinent parts with `/messages/:id.html` and `/messages/:id.plain` for the default HTML and plain text version, `/messages/:id/parts/:cid` for individual attachments by CID, or the whole message with `/messages/:id.source`.
76
+ SSL/TLS encryption, UTF-8 and international content, email authentication, and more.
210
77
 
211
- ## Caveats
78
+ ### [Credits](reference/CREDITS.md)
212
79
 
213
- * Mail processing is fairly basic but easily modified. If something doesn't work for you, fork and fix it or [file an issue](https://github.com/spaquet/mailcatcher-ng/issues). Include the whole message you're having problems with.
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
@@ -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
- # so strip potential " SIZE=..." suffixes from senders
33
- sender = $` if sender =~ / SIZE=\d+\z/
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module MailCatcher
4
- VERSION = '1.0.0'
4
+ VERSION = '1.2.0'
5
5
  end
@@ -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.start(options[:http_ip], options[:http_port], Web, signals: false)
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
- "smtp://#{@options[:smtp_ip]}:#{@options[:smtp_port]}"
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}