uri-smtp 0.3.0 → 0.5.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: 534a3148493353560954ff1399ed0bcc5972fb84a0a72f95389827e9b2e59c66
4
- data.tar.gz: 0701b94ea526034678a4cd2cdd72996ca3e21e8e2a91d1ce4ea120a4ccc09c7b
3
+ metadata.gz: 457b3741b5ef4678c7f1c6e19574f24d68d5f515ef58c6879f64516c8d472049
4
+ data.tar.gz: ecd3d5e89df1874095936cd804fd4241feefed5f2e8f6eb0f3015906d1f63cf6
5
5
  SHA512:
6
- metadata.gz: 59430a383a5a47c243dfe487d51f5749c0a58ffd1ba836c6fe1fb53c3b51927fed39329ba83f5a7853456a0f48abe13e7402cf33d182c6ed7d9881c53606366b
7
- data.tar.gz: ab4b650b59960e5d6b1859ec78ea266fa1b50b4cc3d01c592662be8f3562bfc40a7ca70e4296a7b5a4fba9e1df5f6797b79851236223c9fcfec23d5f409d2345
6
+ metadata.gz: 98b76dad4efd9c9c53f674ef06139844f8845415592bb9927ad90a7d903c62ef388a944779f55987d219035fac120e67247f50251228016db86c346e4d3bc8d3
7
+ data.tar.gz: 530215f54f3c2e1e1c78d99f476b30c11438136e1313d6abc26e7e830ee885f999c0dc90ce3e4874ac9ca11c7e45c3606a156e236ccf676bba33917b58534e18
data/CHANGELOG.md CHANGED
@@ -1,5 +1,26 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.5.0] - 2025-07-25
4
+
5
+ - Add: `uri#read_timeout`, `uri#open_timeout`
6
+ Coerced integers from query:
7
+ ```ruby
8
+ URI("smtp://foo?read_timeout=1").read_timeout #=> 1
9
+ ```
10
+ Included in `to_h`.
11
+ - Add: `uri.decoded_userinfo(format: ...)`
12
+ `:format` can be one of `[:string :array :hash]`
13
+ - Add: domain can appear in fragment
14
+ ```ruby
15
+ URI("smtp://foo.org#sender.org").domain #=> "sender.org"
16
+ ```
17
+ NOTE Any domain from the query takes precedence.
18
+ - Fix: "smtps+foo://..." having no tls
19
+
20
+ ## [0.4.0] - 2025-07-23
21
+
22
+ - FIX: correct settings for action_mailer using mail v2.8.1
23
+
3
24
  ## [0.3.0] - 2025-07-23
4
25
 
5
26
  - FIX: Kernel.URI should accept URI's
@@ -7,3 +28,4 @@
7
28
  ## [0.2.0] - 2025-07-18
8
29
 
9
30
  - Feature complete
31
+
data/README.md CHANGED
@@ -1,6 +1,25 @@
1
1
  # URI::SMTP
2
2
 
3
- This library allows for parsing SMTP-URIs.
3
+ Extends Ruby's `URI` with support for SMTP-uri's.
4
+ This allows for more concise SMTP-config:
5
+ ```diff
6
+ # config/environments/production.rb
7
+ config.action_mailer.delivery_method = :smtp
8
+ - config.action_mailer.smtp_settings = {
9
+ - address: "smtp.gmail.com",
10
+ - port: 587,
11
+ - domain: "example.com",
12
+ - user_name: Rails.application.credentials.dig(:smtp, :user_name),
13
+ - password: Rails.application.credentials.dig(:smtp, :password),
14
+ - authentication: "plain",
15
+ - enable_starttls: true,
16
+ - open_timeout: 5,
17
+ - read_timeout: 5
18
+ - }
19
+ # given ENV["SMPT_URL"]:
20
+ # "smtp://user_name:password@smtp.gmail.com?open_timeout=5&read_timeout=5#example.com"
21
+ + config.action_mailer.smtp_settings = URI(ENV.fetch("SMTP_URL")).to_h(format: :am)
22
+ ```
4
23
 
5
24
  ## Installation
6
25
 
@@ -21,27 +40,29 @@ gem install uri-smtp
21
40
  ### parse
22
41
 
23
42
  ```ruby
24
- u = URI("smtps+login://user%40gmail.com:p%40ss@smtp.gmail.com?domain=sender.org")
43
+ u = URI("smtps+login://user%40gmail.com:p%40ss@smtp.gmail.com#sender.org")
25
44
 
26
45
  url.scheme #=> "smtps+login"
27
46
  url.auth #=> "login"
28
47
  url.starttls #=> false
48
+ url.starttls? #=> false
29
49
  url.tls? #=> true
30
50
  url.userinfo #=> "user%40gmail.com:p%40ss"
51
+ url.decoded_userinfo #=> ["user@gmail.com", "p@ss"]
31
52
  url.decoded_user #=> "user@gmail.com"
32
53
  url.user #=> "user%40gmail.com"
33
54
  url.decoded_password #=> "p@ss"
34
55
  url.password #=> "p%40ss"
35
56
  url.host #=> "smtp.gmail.com"
36
57
  url.port #=> 465
37
- url.domain #=> s"ender.org"
38
- url.query #=> "domain=sender.org"
58
+ url.domain #=> "sender.org"
39
59
  ```
40
60
 
41
61
  ### to_h
42
62
 
43
63
  ```ruby
44
64
  URI("smtps+login://user%40gmail.com:p%40ss@smtp.gmail.com?domain=sender.org").to_h
65
+ #=>
45
66
  {auth: "login",
46
67
  domain: "sender.org",
47
68
  host: "smtp.gmail.com",
@@ -56,6 +77,7 @@ URI("smtps+login://user%40gmail.com:p%40ss@smtp.gmail.com?domain=sender.org").to
56
77
  Formatting for action_mailer configuration, use `to_h(format: :am)`:
57
78
  ```ruby
58
79
  URI("smtps+login://user%40gmail.com:p%40ss@smtp.gmail.com?domain=sender.org").to_h(format: :am)
80
+ #=>
59
81
  {address: "smtp.gmail.com",
60
82
  authentication: "login",
61
83
  domain: "sender.org",
@@ -89,7 +111,7 @@ There's no official specification for SMTP-URIs. There's some prior work though.
89
111
  SMTP with TLS.
90
112
 
91
113
  > [!NOTE]
92
- > to get `url.starttls #=> :auto`, provide it in the query: `smtp://foo?auth=auto`. In that case `net-smtp` uses STARTTLS when the server supports it (but won't halt like when using `:always`).
114
+ > 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`).
93
115
 
94
116
 
95
117
  ### auth
@@ -4,6 +4,6 @@ require "uri"
4
4
 
5
5
  module URI
6
6
  class SMTP < URI::Generic
7
- VERSION = "0.3.0"
7
+ VERSION = "0.5.0"
8
8
  end
9
9
  end
data/lib/uri/smtp.rb CHANGED
@@ -21,6 +21,7 @@ module URI
21
21
  return @port if @port
22
22
  return 25 if host_local?
23
23
  return 465 if tls?
24
+
24
25
  587
25
26
  end
26
27
 
@@ -30,15 +31,56 @@ module URI
30
31
  return parsed_query["auth"] if parsed_query.has_key?("auth")
31
32
  return nil if scheme_auth == "none"
32
33
  return scheme_auth if scheme_auth
34
+
33
35
  "plain"
34
36
  end
35
37
 
38
+ # all formats: return nil when userinfo == nil.
39
+ #
40
+ # format: :array
41
+ # Never contains empty strings (as opposed to [#user, #password]).
42
+ # Example:
43
+ # Given #<URI::SMTP smtps://:token@smtp.gmail.com>
44
+ # Then:
45
+ # [uri.user, uri.password] #=> ["", "token"]
46
+ # uri.decoded_userinfo #=> [nil, "token"]
47
+ #
48
+ # format: :hash
49
+ # Keys are absent when value would be `nil`.
50
+ # Example:
51
+ # Given #<URI::SMTP smtps://:token@smtp.gmail.com>
52
+ # Then:
53
+ # uri.decoded_userinfo(format: :array) #=> [nil, "token"]
54
+ # uri.decoded_userinfo(format: :to_h) #=> {password: "token"}
55
+ def decoded_userinfo(format: :string)
56
+ return if userinfo.nil?
57
+
58
+ case format
59
+ when :string
60
+ [decoded_user, decoded_password].compact.join(":")
61
+ when :array
62
+ [string_presence(decoded_user), string_presence(decoded_password)]
63
+ when :hash
64
+ {
65
+ user: string_presence(decoded_user),
66
+ password: string_presence(decoded_password)
67
+ }.delete_if { |_k, v| v.nil? }
68
+ else
69
+ raise ArgumentError,
70
+ "Unknown format #{format.inspect}. Should be one of #{%i[string array hash].inspect}."
71
+ end
72
+ end
73
+
36
74
  def domain
37
- parsed_query["domain"]
75
+ parsed_query["domain"] || fragment
38
76
  end
39
77
 
40
- def scheme_auth
41
- scheme[/.*(?:\+(.+))/, 1]
78
+ def read_timeout
79
+ parsed_query["read_timeout"]
80
+ end
81
+
82
+ def open_timeout
83
+ parsed_query["open_timeout"]
42
84
  end
43
85
 
44
86
  def starttls
@@ -46,12 +88,13 @@ module URI
46
88
  return parsed_query["starttls"] if parsed_query.has_key?("starttls")
47
89
  return false if host_local?
48
90
  return false if insecure?
91
+
49
92
  :always
50
93
  end
51
94
  alias_method :starttls?, :starttls
52
95
 
53
96
  def tls
54
- scheme == "smtps"
97
+ !!scheme[/^smtps/]
55
98
  end
56
99
  alias_method :tls?, :tls
57
100
 
@@ -64,14 +107,18 @@ module URI
64
107
  end
65
108
 
66
109
  def parsed_query
67
- @parsed_query ||= URI.decode_www_form(query.to_s).to_h.tap do
68
- _1["starttls"] &&= case _1["starttls"]
69
- when "always", "auto" then _1["starttls"].to_sym
70
- when "false" then false
71
- else
72
- :always
110
+ @parsed_query ||= URI.decode_www_form(query.to_s).to_h
111
+ .delete_if { |_k, v| !string_presence(v) }
112
+ .tap do
113
+ _1["read_timeout"] &&= _1["read_timeout"].to_i
114
+ _1["open_timeout"] &&= _1["open_timeout"].to_i
115
+ _1["starttls"] &&= case _1["starttls"]
116
+ when "always", "auto" then _1["starttls"].to_sym
117
+ when "false" then false
118
+ else
119
+ :always
120
+ end
73
121
  end
74
- end
75
122
  end
76
123
 
77
124
  def to_h(format: nil)
@@ -81,20 +128,34 @@ module URI
81
128
  address: host,
82
129
  authentication: auth,
83
130
  domain:,
84
- enable_starttls: starttls,
85
- port:
131
+ enable_starttls: starttls == :always,
132
+ enable_starttls_auto: starttls == :auto,
133
+ open_timeout:,
134
+ port:,
135
+ read_timeout:,
136
+ tls:
86
137
  }.tap do
87
138
  unless _1[:authentication].nil?
88
139
  _1[:user_name] = decoded_user
89
140
  _1[:password] = decoded_password
90
141
  end
142
+ # mail 2.8.1 logic is faulty in that it shortcuts
143
+ # (start)tls-settings when they are false.
144
+ # So we delete these flags.
145
+ _1.delete(:tls) unless _1[:tls]
146
+ _1.delete(:enable_starttls) unless _1[:enable_starttls]
147
+ _1.delete(:enable_starttls) if _1[:tls]
148
+ _1.delete(:enable_starttls_auto) unless _1[:enable_starttls_auto]
149
+ _1.delete(:enable_starttls_auto) if _1[:tls]
91
150
  end.delete_if { |_k, v| v.nil? }
92
151
  else
93
152
  {
94
153
  auth:,
95
154
  domain:,
96
155
  host:,
156
+ open_timeout:,
97
157
  port:,
158
+ read_timeout:,
98
159
  scheme:,
99
160
  starttls:,
100
161
  tls:
@@ -110,6 +171,16 @@ module URI
110
171
  def self.parse(uri)
111
172
  new(*URI.split(uri))
112
173
  end
174
+
175
+ private
176
+
177
+ def scheme_auth
178
+ scheme[/.*(?:\+(.+))/, 1]
179
+ end
180
+
181
+ def string_presence(s)
182
+ s.to_s.strip.then { _1 unless _1.empty? }
183
+ end
113
184
  end
114
185
 
115
186
  register_scheme "SMTP", SMTP
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.3.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Gert Goet