uri-smtp 0.7.3 → 0.7.4
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 +16 -7
- data/README.md +47 -2
- data/lib/uri/smtp/version.rb +1 -1
- data/lib/uri/smtp.rb +67 -20
- metadata +1 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 9a91cda84c048c652dc1da97c080fa6df16320d6c6fbac55563413f2b73b07e3
|
|
4
|
+
data.tar.gz: 984ec2c5976e7e3b8bf51de646961769c9c89df20b1a76c29cc5885cab3cb1d9
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ab85f5367934a7b56e1b9899c5addbdf27f9cd2774b8e9593cf4a24b6f3afcbfa7920d3c887e1afe32d8e22e75a4954c1aaeb9cb8b9b5041ba6499509b93d84d
|
|
7
|
+
data.tar.gz: 0b3d5d09a8247d7113908eb355da91106fb384f4743fce4d40429bc5f0081d88be8532ed7bcaefa4b9d271746c269761833f2debcf35aa2573a5a8f2dafb58b3
|
data/CHANGELOG.md
CHANGED
|
@@ -7,16 +7,25 @@ Changes can be:
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
-
|
|
10
|
+
- ✨ `smtps+insecure://`
|
|
11
|
+
TLS, skipping certificate verification.
|
|
12
|
+
Adds `#tls_verify`; `to_h` emits `tls_verify: false` (default format) / `openssl_verify_mode: "none"` (`:am` format).
|
|
13
|
+
- ✨ `to_h` accepts `user:`/`password:` overrides
|
|
14
|
+
provide credentials separately (no uri-escaping) and `auth` still resolves from the scheme, e.g.
|
|
15
|
+
```ruby
|
|
16
|
+
URI("smtps+login://smtp.gmail.com").to_h(format: :am, user: ENV["U"], password: ENV["P"])
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## [v0.7.3](https://github.com/eval/uri-smtp/releases/tag/v0.7.3)
|
|
11
20
|
|
|
12
21
|
- 🛡️ MFA required to publish this gem
|
|
13
22
|
|
|
14
|
-
## [
|
|
23
|
+
## [v0.7.2](https://github.com/eval/uri-smtp/releases/tag/v0.7.2)
|
|
15
24
|
|
|
16
25
|
- 🛡️ Push and sign gem via GH Actions (i.e. trusted publisher)
|
|
17
26
|
See "Provenance" at https://rubygems.org/gems/uri-smtp
|
|
18
27
|
|
|
19
|
-
## [
|
|
28
|
+
## [v0.6.0](https://github.com/eval/uri-smtp/releases/tag/v0.6.0)
|
|
20
29
|
|
|
21
30
|
- ✨ API-docs at https://eval.github.io/uri-smtp/
|
|
22
31
|
- 🐛 "smtp+insecure+foo://..." not considered `#insecure?`
|
|
@@ -24,7 +33,7 @@ Changes can be:
|
|
|
24
33
|
- 🐛 "smtp+insecure://..." having auth "insecure"
|
|
25
34
|
- ⚠️ remove `#starttls?`
|
|
26
35
|
|
|
27
|
-
## [
|
|
36
|
+
## [v0.5.0](https://github.com/eval/uri-smtp/releases/tag/v0.5.0)
|
|
28
37
|
|
|
29
38
|
- Add: `uri#read_timeout`, `uri#open_timeout`
|
|
30
39
|
Coerced integers from query:
|
|
@@ -41,15 +50,15 @@ Changes can be:
|
|
|
41
50
|
NOTE Any domain from the query takes precedence.
|
|
42
51
|
- Fix: "smtps+foo://..." having no tls
|
|
43
52
|
|
|
44
|
-
## [
|
|
53
|
+
## [v0.4.0](https://github.com/eval/uri-smtp/releases/tag/v0.4.0)
|
|
45
54
|
|
|
46
55
|
- FIX: correct settings for action_mailer using mail v2.8.1
|
|
47
56
|
|
|
48
|
-
## [
|
|
57
|
+
## [v0.3.0](https://github.com/eval/uri-smtp/releases/tag/v0.3.0)
|
|
49
58
|
|
|
50
59
|
- FIX: Kernel.URI should accept URI's
|
|
51
60
|
|
|
52
|
-
## [
|
|
61
|
+
## [v0.2.0](https://github.com/eval/uri-smtp/releases/tag/v0.2.0)
|
|
53
62
|
|
|
54
63
|
- Feature complete
|
|
55
64
|
|
data/README.md
CHANGED
|
@@ -76,7 +76,7 @@ URI("smtps+login://user%40gmail.com:p%40ss@smtp.gmail.com?domain=sender.org").to
|
|
|
76
76
|
|
|
77
77
|
For [ActionMailer configuration](https://guides.rubyonrails.org/action_mailer_basics.html#action-mailer-configuration), use `format: :action_mailer` (or `:am`):
|
|
78
78
|
```ruby
|
|
79
|
-
URI("smtps+login://user%40gmail.com:p%40ss@smtp.gmail.com
|
|
79
|
+
URI("smtps+login://user%40gmail.com:p%40ss@smtp.gmail.com#sender.org").to_h(format: :am)
|
|
80
80
|
#=>
|
|
81
81
|
{address: "smtp.gmail.com",
|
|
82
82
|
authentication: "login",
|
|
@@ -92,11 +92,32 @@ Besides renaming some keys, this also works around a quirk in `v2.8.1` of the ma
|
|
|
92
92
|
|
|
93
93
|
Full Rails config:
|
|
94
94
|
```ruby
|
|
95
|
+
# config/environments/development.rb
|
|
95
96
|
config.action_mailer.delivery_method = :smtp
|
|
96
97
|
# [mailcatcher](https://github.com/sj26/mailcatcher) fallback:
|
|
97
98
|
config.action_mailer.smtp_settings = URI(ENV.fetch("SMTP_URL", "smtp://127.0.0.1:1025")).to_h(format: :am)
|
|
98
99
|
```
|
|
99
100
|
|
|
101
|
+
#### Credentials separately
|
|
102
|
+
|
|
103
|
+
Credentials in the URL must be uri-escaped (e.g. `user@gmail.com` → `user%40gmail.com`), which is easy to get wrong and makes the (env-var) value hard to read. To avoid this, leave them out of the URL and pass them to `to_h` instead:
|
|
104
|
+
|
|
105
|
+
```ruby
|
|
106
|
+
config.action_mailer.smtp_settings =
|
|
107
|
+
URI("smtps+login://smtp.gmail.com#sender.org")
|
|
108
|
+
.to_h(format: :am, user: ENV["SMTP_USERNAME"], password: ENV["SMTP_PASSWORD"])
|
|
109
|
+
#=>
|
|
110
|
+
{address: "smtp.gmail.com",
|
|
111
|
+
authentication: "login",
|
|
112
|
+
domain: "sender.org",
|
|
113
|
+
port: 465,
|
|
114
|
+
tls: true,
|
|
115
|
+
user_name: "...",
|
|
116
|
+
password: "..."}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
The URL carries the connection shape; the env-vars carry the secrets. Note `auth` still resolves from the scheme (`+login`) even though the URL has no userinfo, and the overrides take precedence over any credentials in the URL.
|
|
120
|
+
|
|
100
121
|
## SMTP-URI
|
|
101
122
|
|
|
102
123
|
There's no official specification for SMTP-URIs. There's some prior work though. This implementation is heavily inspired by [aerc](https://git.sr.ht/~rjarry/aerc/tree/master/item/doc/aerc-smtp.5.scd).
|
|
@@ -111,6 +132,8 @@ There's no official specification for SMTP-URIs. There's some prior work though.
|
|
|
111
132
|
SMTP without STARTTLS (i.e. `url.starttls #=> false`)..
|
|
112
133
|
- `smtps`
|
|
113
134
|
SMTP with TLS.
|
|
135
|
+
- `smtps+insecure`
|
|
136
|
+
SMTP with TLS, skipping certificate verification (i.e. `url.tls_verify #=> false`).
|
|
114
137
|
|
|
115
138
|
> [!NOTE]
|
|
116
139
|
> to get `url.starttls #=> :auto`, provide it in the query: `smtp://user:pw@foo?auth=auto`. In that case `net-smtp` uses STARTTLS when the server supports it (but won't halt like when using `:always`).
|
|
@@ -141,6 +164,7 @@ Any value for auth that passes the URI-parser is acceptable. Though the followin
|
|
|
141
164
|
| `smtp+login://user:pass@mail.example.com` | ❌ | 587 | ⚡ | login | STARTTLS `:always` with LOGIN auth |
|
|
142
165
|
| `smtp+none://mail.example.com` | ❌ | 587 | 🔄 | none | Explicit no authentication |
|
|
143
166
|
| `smtps://mail.example.com` | ✅ | 465 | ❌ | none | Direct TLS connection |
|
|
167
|
+
| `smtps+insecure://mail.example.com` | ✅ | 465 | ❌ | none | Direct TLS, skipping certificate verification |
|
|
144
168
|
| `smtps://mail.example.com?domain=sender.org&read_timeout=5&open_timeout=5` | ✅ | 465 | ❌ | none | `domain`, `read_timeout` and `open_timeout` set |
|
|
145
169
|
| `smtps+login://user@imap.gmail.com` | ✅ | 465 | ❌ | login | Direct TLS with LOGIN auth |
|
|
146
170
|
| `smtps://user%40gmail.com:p%40ss@imap.gmail.com` | ✅ | 465 | ❌ | login | Direct TLS with encoded userinfo `user@gmail.com:p@ss` |
|
|
@@ -164,7 +188,28 @@ Any value for auth that passes the URI-parser is acceptable. Though the followin
|
|
|
164
188
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
|
165
189
|
Use `bin/yard server --reload` when working on documentation.
|
|
166
190
|
|
|
167
|
-
To install this gem onto your local machine, run `
|
|
191
|
+
To install this gem onto your local machine, run `bin/rake install`.
|
|
192
|
+
|
|
193
|
+
### Releasing
|
|
194
|
+
|
|
195
|
+
The gem is published automatically by GitHub Actions ([`.github/workflows/release.yml`](.github/workflows/release.yml)) when a `v*` tag is pushed: CI builds the gem, publishes it to [rubygems.org](https://rubygems.org) as a [trusted publisher](https://guides.rubygems.org/trusted-publishing/) (with build provenance), and creates the GitHub release. **The gem is never pushed from your machine**, and CI never writes back to the repo.
|
|
196
|
+
|
|
197
|
+
To cut a release:
|
|
198
|
+
|
|
199
|
+
1. Bump the version:
|
|
200
|
+
```sh
|
|
201
|
+
bin/rake 'gem:write_version[0.7.3]'
|
|
202
|
+
```
|
|
203
|
+
This rewrites `lib/uri/smtp/version.rb` and updates `Gemfile.lock`.
|
|
204
|
+
2. Finalize `CHANGELOG.md` (rename `## [Unreleased]` to the new version + date).
|
|
205
|
+
3. Commit and push, then confirm CI is green.
|
|
206
|
+
4. Build, tag and push:
|
|
207
|
+
```sh
|
|
208
|
+
bin/rake release
|
|
209
|
+
```
|
|
210
|
+
This builds the gem locally (verifying it builds), creates the `v0.7.3` tag, and pushes it to `origin` — which triggers the release workflow. Pushing to rubygems is disabled via `gem_push=no` (set in `mise.toml`); outside mise, run `gem_push=no bin/rake release`.
|
|
211
|
+
|
|
212
|
+
Tags use a `v` prefix; a fourth version segment (e.g. `v0.7.3.rc1`) is published as a pre-release.
|
|
168
213
|
|
|
169
214
|
## Contributing
|
|
170
215
|
|
data/lib/uri/smtp/version.rb
CHANGED
data/lib/uri/smtp.rb
CHANGED
|
@@ -45,14 +45,7 @@ module URI
|
|
|
45
45
|
# @return [String] 'auth via scheme' when present, e.g. `"smtp+login://foo.org"`.
|
|
46
46
|
# @return [String] else `"plain"`
|
|
47
47
|
def auth
|
|
48
|
-
|
|
49
|
-
return nil unless userinfo
|
|
50
|
-
return nil if parsed_query["auth"] == "none"
|
|
51
|
-
return parsed_query["auth"] if parsed_query.has_key?("auth")
|
|
52
|
-
return nil if scheme_auth == "none"
|
|
53
|
-
return scheme_auth if scheme_auth
|
|
54
|
-
|
|
55
|
-
"plain"
|
|
48
|
+
auth_for(credentials: !userinfo.nil?)
|
|
56
49
|
end
|
|
57
50
|
|
|
58
51
|
# Decoded userinfo formatted as String, Array or Hash.
|
|
@@ -142,20 +135,41 @@ module URI
|
|
|
142
135
|
end
|
|
143
136
|
alias_method :tls?, :tls
|
|
144
137
|
|
|
145
|
-
# Whether or not the scheme
|
|
138
|
+
# Whether or not the scheme carries the `insecure` modifier.
|
|
139
|
+
#
|
|
140
|
+
# `insecure` relaxes the transport's security: with plaintext `smtp` it skips
|
|
141
|
+
# `STARTTLS` (see {#starttls}); with `smtps` it skips TLS certificate
|
|
142
|
+
# verification (see {#tls_verify}).
|
|
146
143
|
#
|
|
147
144
|
# @see #starttls
|
|
145
|
+
# @see #tls_verify
|
|
148
146
|
#
|
|
149
147
|
# @example
|
|
150
148
|
# URI("smtp+insecure://foo.org").insecure? #=> true
|
|
151
|
-
# #
|
|
149
|
+
# # `smtp+insecure` is equivalent (though shorter and more descriptive) to
|
|
152
150
|
# URI("smtp://foo.org?starttls=false")
|
|
153
151
|
#
|
|
152
|
+
# # TLS without certificate verification
|
|
153
|
+
# URI("smtps+insecure://foo.org").insecure? #=> true
|
|
154
|
+
#
|
|
154
155
|
# # combine with authentication
|
|
155
156
|
# URI("smtp+insecure+login://user:pw@foo.org").insecure? #=> true
|
|
156
|
-
# @return [Boolean] whether `scheme`
|
|
157
|
+
# @return [Boolean] whether `scheme` includes the `insecure` modifier.
|
|
157
158
|
def insecure?
|
|
158
|
-
scheme.
|
|
159
|
+
scheme.split("+").include?("insecure")
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
# Whether or not to verify the server's TLS certificate.
|
|
163
|
+
#
|
|
164
|
+
# @see #insecure?
|
|
165
|
+
#
|
|
166
|
+
# @example
|
|
167
|
+
# URI("smtps://foo.org").tls_verify #=> true
|
|
168
|
+
# URI("smtps+insecure://foo.org").tls_verify #=> false
|
|
169
|
+
# @return [false] when the scheme combines TLS with `insecure`, e.g. `smtps+insecure://`.
|
|
170
|
+
# @return [true] otherwise.
|
|
171
|
+
def tls_verify
|
|
172
|
+
!(tls? && insecure?)
|
|
159
173
|
end
|
|
160
174
|
|
|
161
175
|
# Whether or not `host` is considered local.
|
|
@@ -221,25 +235,44 @@ module URI
|
|
|
221
235
|
# # file: config/environments/development.rb
|
|
222
236
|
# # Config via env-var SMTP_URL or fallback to mailcatcher.
|
|
223
237
|
# config.action_mailer.smtp_settings = URI(ENV.fetch("SMTP_URL", "http://127.0.0.1:1025")).to_h(format: :am)
|
|
238
|
+
# @example provide credentials separately
|
|
239
|
+
# # Avoids uri-escaping secrets in the URL, and still sets `authentication`
|
|
240
|
+
# # from the scheme even though the URL has no userinfo.
|
|
241
|
+
# URI("smtps+login://smtp.gmail.com#sender.org").to_h(format: :am, user: "user@gmail.com", password: "p@ss")
|
|
242
|
+
# # =>
|
|
243
|
+
# # {address: "smtp.gmail.com",
|
|
244
|
+
# # authentication: "login",
|
|
245
|
+
# # domain: "sender.org",
|
|
246
|
+
# # port: 465,
|
|
247
|
+
# # tls: true,
|
|
248
|
+
# # user_name: "user@gmail.com",
|
|
249
|
+
# # password: "p@ss"}
|
|
224
250
|
# @param format [Symbol] the format type, `nil` (default), `:action_mailer`/`:am`.
|
|
251
|
+
# @param user [String, nil] override the user; takes precedence over any user in the URL. When given, `auth` resolves even without userinfo in the URL.
|
|
252
|
+
# @param password [String, nil] override the password; takes precedence over any password in the URL.
|
|
225
253
|
# @return [Hash]
|
|
226
|
-
def to_h(format: nil)
|
|
254
|
+
def to_h(format: nil, user: nil, password: nil)
|
|
255
|
+
authentication = auth_for(credentials: !user.nil? || !password.nil? || !userinfo.nil?)
|
|
256
|
+
user ||= decoded_user
|
|
257
|
+
password ||= decoded_password
|
|
258
|
+
|
|
227
259
|
case format
|
|
228
260
|
when :am, :action_mailer
|
|
229
261
|
{
|
|
230
262
|
address: host,
|
|
231
|
-
authentication
|
|
263
|
+
authentication:,
|
|
232
264
|
domain:,
|
|
233
265
|
enable_starttls: starttls == :always,
|
|
234
266
|
enable_starttls_auto: starttls == :auto,
|
|
235
267
|
open_timeout:,
|
|
268
|
+
openssl_verify_mode: ("none" unless tls_verify),
|
|
236
269
|
port:,
|
|
237
270
|
read_timeout:,
|
|
238
271
|
tls:
|
|
239
272
|
}.tap do
|
|
240
273
|
unless _1[:authentication].nil?
|
|
241
|
-
_1[:user_name] =
|
|
242
|
-
_1[:password] =
|
|
274
|
+
_1[:user_name] = user
|
|
275
|
+
_1[:password] = password
|
|
243
276
|
end
|
|
244
277
|
# mail 2.8.1 logic is faulty in that it shortcuts
|
|
245
278
|
# (start)tls-settings when they are false.
|
|
@@ -252,7 +285,7 @@ module URI
|
|
|
252
285
|
end.delete_if { |_k, v| v.nil? }
|
|
253
286
|
else
|
|
254
287
|
{
|
|
255
|
-
auth
|
|
288
|
+
auth: authentication,
|
|
256
289
|
domain:,
|
|
257
290
|
host:,
|
|
258
291
|
open_timeout:,
|
|
@@ -260,11 +293,12 @@ module URI
|
|
|
260
293
|
read_timeout:,
|
|
261
294
|
scheme:,
|
|
262
295
|
starttls:,
|
|
263
|
-
tls
|
|
296
|
+
tls:,
|
|
297
|
+
tls_verify: (false unless tls_verify)
|
|
264
298
|
}.tap do
|
|
265
299
|
unless _1[:auth].nil?
|
|
266
|
-
_1[:user] =
|
|
267
|
-
_1[:password] =
|
|
300
|
+
_1[:user] = user
|
|
301
|
+
_1[:password] = password
|
|
268
302
|
end
|
|
269
303
|
end.delete_if { |_k, v| v.nil? }
|
|
270
304
|
end
|
|
@@ -281,6 +315,19 @@ module URI
|
|
|
281
315
|
|
|
282
316
|
private
|
|
283
317
|
|
|
318
|
+
# Resolve the authentication mechanism given whether credentials are
|
|
319
|
+
# available — either from `userinfo` in the URL or from `#to_h` overrides.
|
|
320
|
+
# net-smtp: passing authtype without user/pw raises an error, hence the guard.
|
|
321
|
+
def auth_for(credentials:)
|
|
322
|
+
return nil unless credentials
|
|
323
|
+
return nil if parsed_query["auth"] == "none"
|
|
324
|
+
return parsed_query["auth"] if parsed_query.has_key?("auth")
|
|
325
|
+
return nil if scheme_auth == "none"
|
|
326
|
+
return scheme_auth if scheme_auth
|
|
327
|
+
|
|
328
|
+
"plain"
|
|
329
|
+
end
|
|
330
|
+
|
|
284
331
|
def scheme_auth
|
|
285
332
|
string_absense_in(scheme.split("+").last, %w[smtp smtps insecure])
|
|
286
333
|
end
|