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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 32bf646636db9537ce1c5a5a044f9d467a9e231c0a43f59fe5e109e9866716f8
4
- data.tar.gz: 76781018aa0ac3b6b8567b3547cae5350ac2611272154d7fb7dce22dd3859cde
3
+ metadata.gz: 9a91cda84c048c652dc1da97c080fa6df16320d6c6fbac55563413f2b73b07e3
4
+ data.tar.gz: 984ec2c5976e7e3b8bf51de646961769c9c89df20b1a76c29cc5885cab3cb1d9
5
5
  SHA512:
6
- metadata.gz: d94721883773f26c4edeb39fbc884d4a78dd601877d7e6ba0f2ead74c2876e8ebac21f5637a1012dc9cde9bba5ca679bf01c1e7e3f954f6d05b56dde0d92a2ef
7
- data.tar.gz: 7d68939c2782f247335486f4fa918ad7f96a79c3cb8d3853355bac5dd8221b906a0a664be0eaefa6117c79e9d317ca78c5461255a9f33a60457ac38a568bd76f
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
- ## v0.7.3
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
- ## [0.7.x] - 2025-07-28
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
- ## [0.6.0] - 2025-07-27
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
- ## [0.5.0] - 2025-07-25
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
- ## [0.4.0] - 2025-07-23
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
- ## [0.3.0] - 2025-07-23
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
- ## [0.2.0] - 2025-07-18
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?domain=sender.org").to_h(format: :am)
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 `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
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
 
@@ -4,6 +4,6 @@ require "uri"
4
4
 
5
5
  module URI
6
6
  class SMTP < URI::Generic
7
- VERSION = "0.7.3"
7
+ VERSION = "0.7.4"
8
8
  end
9
9
  end
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
- # net-smtp: passing authtype without user/pw raises error
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 indicates to skip STARTTLS.
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
- # # This is equivalent (though shorter and more descriptive) to
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` starts with `"smtp+insecure"`.
157
+ # @return [Boolean] whether `scheme` includes the `insecure` modifier.
157
158
  def insecure?
158
- scheme.start_with?("smtp+insecure")
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: auth,
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] = decoded_user
242
- _1[:password] = decoded_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] = decoded_user
267
- _1[:password] = decoded_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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: uri-smtp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.7.3
4
+ version: 0.7.4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gert Goet