Skip to content

Commit a5cf440

Browse files
committed
Config smtp-delivery via url
1 parent aba0b5f commit a5cf440

File tree

3 files changed

+151
-2
lines changed

3 files changed

+151
-2
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,14 @@ Mail.defaults do
265265
end
266266
```
267267

268+
An url is also accepted for smtp:
269+
```ruby
270+
Mail.defaults do
271+
# note the URL-encoded userinfo:
272+
delivery_method :smtp, url: 'smtps://user%40gmail.com:[email protected]'
273+
end
274+
```
275+
268276

269277
Exim requires its own delivery manager, and can be used like so:
270278

lib/mail/network/delivery_methods/smtp.rb

Lines changed: 83 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# frozen_string_literal: true
22
require 'mail/smtp_envelope'
3+
require 'mail/utilities'
34

45
module Mail
56
# == Sending Email with SMTP
@@ -23,6 +24,7 @@ module Mail
2324
# :password => '<password>',
2425
# :authentication => 'plain',
2526
# :enable_starttls_auto => true }
27+
#
2628
# end
2729
#
2830
# === Sending via GMail
@@ -35,8 +37,20 @@ module Mail
3537
# :password => '<password>',
3638
# :authentication => 'plain',
3739
# :enable_starttls_auto => true }
40+
#
41+
# # ...or using the url-attribute (note the URL-encoded userinfo):
42+
# delivery_method :smtp,
43+
# { :url => 'smtps://user%40gmail.com:[email protected]' }
3844
# end
3945
#
46+
# === Sending via Fastmail
47+
#
48+
# Mail.defaults do
49+
# delivery_method :smtp,
50+
# { :url => 'smtps://user%40fastmail.fm:[email protected]' }
51+
# end
52+
#
53+
#
4054
# === Certificate verification
4155
#
4256
# When using TLS, some mail servers provide certificates that are self-signed
@@ -74,6 +88,68 @@ module Mail
7488
#
7589
# mail.deliver!
7690
class SMTP
91+
class UrlResolver
92+
93+
DEFAULTS = {
94+
"smtp" => {
95+
:address => 'localhost',
96+
:port => 25,
97+
:domain => 'localhost.localdomain',
98+
:enable_starttls_auto => true
99+
},
100+
"smtps" => {
101+
:address => 'localhost',
102+
:port => 465,
103+
:domain => 'localhost.localdomain',
104+
:enable_starttls_auto => false,
105+
:tls => true
106+
}
107+
}
108+
109+
def initialize(url)
110+
@uri = url.is_a?(URI) ? url : uri_parser.parse(url)
111+
unless DEFAULTS.has_key?(@uri.scheme)
112+
raise ArgumentError, "#{url} is not a valid SMTP-url. Required format: smtp(s)://host?domain=sender.org"
113+
end
114+
@query = uri.query
115+
end
116+
117+
def to_hash
118+
config = raw_config
119+
config.map { |key, value| config[key] = uri_parser.unescape(value) if value.is_a? String }
120+
config
121+
end
122+
123+
private
124+
attr_reader :uri
125+
126+
def raw_config
127+
scheme_defaults.merge(query_hash).merge({
128+
:address => uri.host,
129+
:port => uri.port,
130+
:user_name => uri.user,
131+
:password => uri.password
132+
}.delete_if {|_key, value| Utilities.blank?(value) })
133+
end
134+
135+
def uri_parser
136+
Utilities.uri_parser
137+
end
138+
139+
def query_hash
140+
@query_hash = begin
141+
result = Hash[(@query || "").split("&").map { |pair| k,v = pair.split("="); [k.to_sym, v] }]
142+
result[:open_timeout] &&= result[:open_timeout].to_i
143+
result[:read_timeout] &&= result[:read_timeout].to_i
144+
result
145+
end
146+
end
147+
148+
def scheme_defaults
149+
DEFAULTS[uri.scheme]
150+
end
151+
end
152+
77153
attr_accessor :settings
78154

79155
DEFAULTS = {
@@ -92,8 +168,13 @@ class SMTP
92168
:read_timeout => nil
93169
}
94170

95-
def initialize(values)
96-
self.settings = DEFAULTS.merge(values)
171+
def initialize(config)
172+
settings = DEFAULTS
173+
174+
if config.has_key?(:url)
175+
settings = settings.merge(UrlResolver.new(config.delete(:url)).to_hash)
176+
end
177+
self.settings = settings.merge(config)
97178
end
98179

99180
def deliver!(mail)

spec/mail/network/delivery_methods/smtp_spec.rb

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,66 @@
2525
MockSMTP.clear_deliveries
2626
end
2727

28+
describe 'settings via url' do
29+
def smtp_delivery_settings_for_url(url, options = {})
30+
Mail.defaults { delivery_method :smtp, {:url => url}.merge(options) }
31+
32+
Mail.delivery_method.settings
33+
end
34+
35+
it 'provides smtp defaults' do
36+
expect(smtp_delivery_settings_for_url('smtp:///')).to \
37+
include(:address => 'localhost',
38+
:port => 25,
39+
:domain => 'localhost.localdomain',
40+
:enable_starttls_auto => true)
41+
end
42+
43+
it 'provides smtps defaults' do
44+
expect(smtp_delivery_settings_for_url('smtps:///')).to \
45+
include(:address => 'localhost',
46+
:port => 465,
47+
:domain => 'localhost.localdomain',
48+
:tls => true)
49+
end
50+
51+
it 'allows for all attributes to be set' do
52+
url = 'smtp://mailer.org:12345?domain=sender.org&authentication=plain&openssl_verify_mode=peer&tls=true&open_timeout=1&read_timeout=2'
53+
expect(smtp_delivery_settings_for_url(url)).to \
54+
include(:address => 'mailer.org',
55+
:port => 12345,
56+
:domain => 'sender.org',
57+
:authentication => 'plain',
58+
:openssl_verify_mode => 'peer',
59+
:tls => 'true',
60+
:open_timeout => 1,
61+
:read_timeout => 2)
62+
end
63+
64+
it 'allows for url-attributes to be overriden' do
65+
overrides = {:address => 'real-host', :domain => 'real-domain'}
66+
expect(smtp_delivery_settings_for_url('smtp://user:pw@host?domain=sender.org', overrides)).to \
67+
include(overrides)
68+
end
69+
70+
it 'escapes credentials' do
71+
expect(smtp_delivery_settings_for_url('smtps://user%40host:pw-with-%[email protected]')).to \
72+
include(:user_name => 'user@host',
73+
:password => 'pw-with-/')
74+
end
75+
76+
it 'accepts an URI' do
77+
expect(smtp_delivery_settings_for_url(URI.parse('smtps://host'))).to \
78+
include(:address => 'host')
79+
end
80+
81+
it 'should raise an error for url without smtp(s) scheme' do
82+
expect {
83+
smtp_delivery_settings_for_url('foo://host')
84+
}.to raise_error(/is not a valid SMTP-url/)
85+
end
86+
end
87+
2888
describe "general usage" do
2989
it "dot-stuff unterminated last line of the message" do
3090
Mail.deliver do

0 commit comments

Comments
 (0)