net-smtp 0.3.1 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: fbf12984985ffef0271f09805060b2cfa5e72eb3955c8ce51ee8076b427e1a02
4
- data.tar.gz: 17c6420b495abe8b17446b1e667681b2680b02d894b3ce1e4ef6cffaa26ad1df
3
+ metadata.gz: 1fcccbe34b29672c33aa7515de88e88dba31882e26a4b6c0a5fc30c5a4eb697d
4
+ data.tar.gz: 5c47e6453d2cb69365edc7b4e31b17106b73e254e1bc10258f4b14ea780ba889
5
5
  SHA512:
6
- metadata.gz: 4d9372a9673256cc280600a1367a0c92bed91250209736b4b54c7219c115b1006e9fc66d465367729f53ef393b9a8077b33f11f7578365d9cb84051e44b86976
7
- data.tar.gz: 0f7da80c1d40216684ff0927637b14dfb5597638913a0aa3a8f2ca68a3fc89724926736c719715171b7bcab915a9504016f10356b2622bc6da9e5f51238f8e52
6
+ metadata.gz: 6576c5b7dc21982576fa6c3a6e08641e973136148d5d5bcb27fe1e17b4e5fdd802c5f5a6f9f116dd15bf4f2a0e5f95f071ca19d47119cf60c7da9230a64d3a4c
7
+ data.tar.gz: 9b1c54b2d9a48e384d3b1a67f1aa8b8a2614a383dc31b83d49170f3ed93d69a2058f3c310f72d443f54a0947c1066249105087a1a3addc92965c289354c8156a
data/NEWS.md ADDED
@@ -0,0 +1,133 @@
1
+ # NEWS
2
+
3
+ ## Version 0.5.0 (2024-03-27)
4
+
5
+ ### Improvements
6
+
7
+ * Allow case-insensitive strings for SASL mechanism <https://github.com/ruby/net-smtp/pull/64>
8
+ * Make #auth_capable? public <https://github.com/ruby/net-smtp/pull/63>
9
+ * Add XOAUTH2 authenticator <https://github.com/ruby/net-smtp/pull/80>
10
+
11
+ ### Others
12
+
13
+ * Remove unused private auth_method <https://github.com/ruby/net-smtp/pull/67>
14
+ * Delegate checking auth args to the authenticator <https://github.com/ruby/net-smtp/pull/73>
15
+ * Updated docs, especially TLS and SASL-related <https://github.com/ruby/net-smtp/pull/66>
16
+ * Renew test certificates <https://github.com/ruby/net-smtp/pull/75>
17
+ * Fix version extraction to work with non ASCII characters with any LANG <https://github.com/ruby/net-smtp/pull/76>
18
+ * Replace non-ASCII EM DASH (U+2014) with ASCII hyphen (U+002D) <https://github.com/ruby/net-smtp/pull/78>
19
+ * Use reusing workflow for Ruby versions <https://github.com/ruby/net-smtp/pull/79>
20
+ * Make the test suite compatible with --enable-frozen-string-literal <https://github.com/ruby/net-smtp/pull/81>
21
+
22
+ ## Version 0.4.0 (2023-09-20)
23
+
24
+ ### Improvements
25
+
26
+ * add Net::SMTP::Authenticator class and auth_* methods are separated from the Net::SMTP class. <https://github.com/ruby/net-smtp/pull/53>
27
+ This allows you to add a new authentication method to Net::SMTP.
28
+ Create a class with an `auth` method that inherits Net::SMTP::Authenticator.
29
+ The `auth` method has two arguments, `user` and `secret`.
30
+ Send an instruction to the SMTP server by using the `continue` or `finish` method.
31
+ For more information, see lib/net/smtp/auto _*.rb.
32
+ * Add SMTPUTF8 support <https://github.com/ruby/net-smtp/pull/49>
33
+
34
+ ### Fixes
35
+
36
+ * Revert "Replace Timeout.timeout with socket timeout" <https://github.com/ruby/net-smtp/pull/51>
37
+ * Fixed issue sending emails to unaffected recipients on 53x error <https://github.com/ruby/net-smtp/pull/56>
38
+
39
+ ### Others
40
+
41
+ * Removed unnecessary Subversion keywords <https://github.com/ruby/net-smtp/pull/57>
42
+
43
+ ## Version 0.3.3 (2022-10-29)
44
+
45
+ * No timeout library required <https://github.com/ruby/net-smtp/pull/44>
46
+ * Make the digest library optional <https://github.com/ruby/net-smtp/pull/45>
47
+
48
+ ## Version 0.3.2 (2022-09-28)
49
+
50
+ * Make exception API compatible with what Ruby expects <https://github.com/ruby/net-smtp/pull/42>
51
+
52
+ ## Version 0.3.1 (2021-12-12)
53
+
54
+ ### Improvements
55
+
56
+ * add Net::SMTP::Address.
57
+ * add Net::SMTP#capable? and Net::SMTP#capabilities.
58
+ * add Net::SMTP#tls_verify, Net::SMTP#tls_hostname, Net::SMTP#ssl_context_params
59
+
60
+ ## Version 0.3.0 (2021-10-14)
61
+
62
+ ### Improvements
63
+
64
+ * Add `tls`, `starttls` keyword arguments.
65
+ ```ruby
66
+ # always use TLS connection for port 465.
67
+ Net::SMTP.start(hostname, 465, tls: true)
68
+
69
+ # do not use starttls for localhost
70
+ Net::SMTP.start('localhost', starttls: false)
71
+ ```
72
+
73
+ ### Incompatible changes
74
+
75
+ * The tls_* paramter has been moved from start() to initialize().
76
+
77
+ ## Version 0.2.2 (2021-10-09)
78
+
79
+ * Add `response` to SMTPError exceptions.
80
+ * `Net::SMTP.start()` and `#start()` accepts `ssl_context_params` keyword argument.
81
+ * Replace `Timeout.timeout` with socket timeout.
82
+ * Remove needless files from gem.
83
+ * Add dependency on digest, timeout.
84
+
85
+ ## Version 0.2.1 (2020-11-18)
86
+
87
+ ### Fixes
88
+
89
+ * Update the license for the default gems to dual licenses.
90
+ * Add dependency for net-protocol.
91
+
92
+ ## Version 0.2.0 (2020-11-15)
93
+
94
+ ### Incompatible changes
95
+
96
+ * Verify the server's certificate by default.
97
+ If you don't want verification, specify `start(tls_verify: false)`.
98
+ <https://github.com/ruby/net-smtp/pull/12>
99
+
100
+ * Use STARTTLS by default if possible.
101
+ If you don't want starttls, specify:
102
+ ```
103
+ smtp = Net::SMTP.new(hostname, port)
104
+ smtp.disable_starttls
105
+ smtp.start do |s|
106
+ s.send_message ....
107
+ end
108
+ ```
109
+ <https://github.com/ruby/net-smtp/pull/9>
110
+
111
+ ### Improvements
112
+
113
+ * Net::SMTP.start and Net::SMTP#start arguments are keyword arguments.
114
+ ```
115
+ start(address, port = nil, helo: 'localhost', user: nil, secret: nil, authtype: nil) { |smtp| ... }
116
+ ```
117
+ `password` is an alias of `secret`.
118
+ <https://github.com/ruby/net-smtp/pull/7>
119
+
120
+ * Add `tls_hostname` parameter to `start()`.
121
+ If you want to use a different hostname than the certificate for the connection, you can specify the certificate hostname with `tls_hostname`.
122
+ <https://github.com/ruby/net-smtp/pull/14>
123
+
124
+ * Add SNI support to net/smtp <https://github.com/ruby/net-smtp/pull/4>
125
+
126
+ ### Fixes
127
+
128
+ * enable_starttls before disable_tls causes an error. <https://github.com/ruby/net-smtp/pull/10>
129
+ * TLS should not check the hostname when verify_mode is disabled. <https://github.com/ruby/net-smtp/pull/6>
130
+
131
+ ## Version 0.1.0 (2019-12-03)
132
+
133
+ This is the first release of net-smtp gem.
data/README.md ADDED
@@ -0,0 +1,97 @@
1
+ # Net::SMTP
2
+
3
+ This library provides functionality to send internet mail via SMTP, the Simple Mail Transfer Protocol.
4
+
5
+ For details of SMTP itself, see [RFC2821](http://www.ietf.org/rfc/rfc2821.txt).
6
+
7
+ ## Installation
8
+
9
+ Add this line to your application's Gemfile:
10
+
11
+ ```ruby
12
+ gem 'net-smtp'
13
+ ```
14
+
15
+ And then execute:
16
+
17
+ $ bundle
18
+
19
+ Or install it yourself as:
20
+
21
+ $ gem install net-smtp
22
+
23
+ ## Usage
24
+
25
+ ### Sending Messages
26
+
27
+ You must open a connection to an SMTP server before sending messages.
28
+ The first argument is the address of your SMTP server, and the second
29
+ argument is the port number. Using SMTP.start with a block is the simplest
30
+ way to do this. This way, the SMTP connection is closed automatically
31
+ after the block is executed.
32
+
33
+ ```ruby
34
+ require 'net/smtp'
35
+ Net::SMTP.start('your.smtp.server', 25) do |smtp|
36
+ # Use the SMTP object smtp only in this block.
37
+ end
38
+ ```
39
+
40
+ Replace 'your.smtp.server' with your SMTP server. Normally
41
+ your system manager or internet provider supplies a server
42
+ for you.
43
+
44
+ Then you can send messages.
45
+
46
+ ```ruby
47
+ msgstr = <<END_OF_MESSAGE
48
+ From: Your Name <your@mail.address>
49
+ To: Destination Address <someone@example.com>
50
+ Subject: test message
51
+ Date: Sat, 23 Jun 2001 16:26:43 +0900
52
+ Message-Id: <unique.message.id.string@example.com>
53
+
54
+ This is a test message.
55
+ END_OF_MESSAGE
56
+
57
+ require 'net/smtp'
58
+ Net::SMTP.start('your.smtp.server', 25) do |smtp|
59
+ smtp.send_message msgstr,
60
+ 'your@mail.address',
61
+ 'his_address@example.com'
62
+ end
63
+ ```
64
+
65
+ ### Closing the Session
66
+
67
+ You MUST close the SMTP session after sending messages, by calling
68
+ the #finish method:
69
+
70
+ ```ruby
71
+ # using SMTP#finish
72
+ smtp = Net::SMTP.start('your.smtp.server', 25)
73
+ smtp.send_message msgstr, 'from@address', 'to@address'
74
+ smtp.finish
75
+ ```
76
+
77
+ You can also use the block form of SMTP.start/SMTP#start. This closes
78
+ the SMTP session automatically:
79
+
80
+ ```ruby
81
+ # using block form of SMTP.start
82
+ Net::SMTP.start('your.smtp.server', 25) do |smtp|
83
+ smtp.send_message msgstr, 'from@address', 'to@address'
84
+ end
85
+ ```
86
+
87
+ I strongly recommend this scheme. This form is simpler and more robust.
88
+
89
+ ## Development
90
+
91
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
92
+
93
+ 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 tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
94
+
95
+ ## Contributing
96
+
97
+ Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/net-smtp.
@@ -0,0 +1,48 @@
1
+ unless defined? OpenSSL
2
+ begin
3
+ require 'digest/md5'
4
+ rescue LoadError
5
+ end
6
+ end
7
+
8
+ class Net::SMTP
9
+ class AuthCramMD5 < Net::SMTP::Authenticator
10
+ auth_type :cram_md5
11
+
12
+ def auth(user, secret)
13
+ challenge = continue('AUTH CRAM-MD5')
14
+ crammed = cram_md5_response(secret, challenge.unpack1('m'))
15
+ finish(base64_encode("#{user} #{crammed}"))
16
+ end
17
+
18
+ IMASK = 0x36
19
+ OMASK = 0x5c
20
+
21
+ # CRAM-MD5: [RFC2195]
22
+ def cram_md5_response(secret, challenge)
23
+ tmp = digest_class::MD5.digest(cram_secret(secret, IMASK) + challenge)
24
+ digest_class::MD5.hexdigest(cram_secret(secret, OMASK) + tmp)
25
+ end
26
+
27
+ CRAM_BUFSIZE = 64
28
+
29
+ def cram_secret(secret, mask)
30
+ secret = digest_class::MD5.digest(secret) if secret.size > CRAM_BUFSIZE
31
+ buf = secret.ljust(CRAM_BUFSIZE, "\0")
32
+ 0.upto(buf.size - 1) do |i|
33
+ buf[i] = (buf[i].ord ^ mask).chr
34
+ end
35
+ buf
36
+ end
37
+
38
+ def digest_class
39
+ @digest_class ||= if defined?(OpenSSL::Digest)
40
+ OpenSSL::Digest
41
+ elsif defined?(::Digest)
42
+ ::Digest
43
+ else
44
+ raise '"openssl" or "digest" library is required'
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,11 @@
1
+ class Net::SMTP
2
+ class AuthLogin < Net::SMTP::Authenticator
3
+ auth_type :login
4
+
5
+ def auth(user, secret)
6
+ continue('AUTH LOGIN')
7
+ continue(base64_encode(user))
8
+ finish(base64_encode(secret))
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,9 @@
1
+ class Net::SMTP
2
+ class AuthPlain < Net::SMTP::Authenticator
3
+ auth_type :plain
4
+
5
+ def auth(user, secret)
6
+ finish('AUTH PLAIN ' + base64_encode("\0#{user}\0#{secret}"))
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,17 @@
1
+ class Net::SMTP
2
+ class AuthXoauth2 < Net::SMTP::Authenticator
3
+ auth_type :xoauth2
4
+
5
+ def auth(user, secret)
6
+ token = xoauth2_string(user, secret)
7
+
8
+ finish("AUTH XOAUTH2 #{base64_encode(token)}")
9
+ end
10
+
11
+ private
12
+
13
+ def xoauth2_string(user, secret)
14
+ "user=#{user}\1auth=Bearer #{secret}\1\1"
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,57 @@
1
+ module Net
2
+ class SMTP
3
+ class Authenticator
4
+ def self.auth_classes
5
+ @classes ||= {}
6
+ end
7
+
8
+ def self.auth_type(type)
9
+ type = type.to_s.upcase.tr(?_, ?-).to_sym
10
+ Authenticator.auth_classes[type] = self
11
+ end
12
+
13
+ def self.auth_class(type)
14
+ type = type.to_s.upcase.tr(?_, ?-).to_sym
15
+ Authenticator.auth_classes[type]
16
+ end
17
+
18
+ def self.check_args(user_arg = nil, secret_arg = nil, *, **)
19
+ unless user_arg
20
+ raise ArgumentError, 'SMTP-AUTH requested but missing user name'
21
+ end
22
+ unless secret_arg
23
+ raise ArgumentError, 'SMTP-AUTH requested but missing secret phrase'
24
+ end
25
+ end
26
+
27
+ attr_reader :smtp
28
+
29
+ def initialize(smtp)
30
+ @smtp = smtp
31
+ end
32
+
33
+ # @param arg [String] message to server
34
+ # @return [String] message from server
35
+ def continue(arg)
36
+ res = smtp.get_response arg
37
+ raise res.exception_class.new(res) unless res.continue?
38
+ res.string.split[1]
39
+ end
40
+
41
+ # @param arg [String] message to server
42
+ # @return [Net::SMTP::Response] response from server
43
+ def finish(arg)
44
+ res = smtp.get_response arg
45
+ raise SMTPAuthenticationError.new(res) unless res.success?
46
+ res
47
+ end
48
+
49
+ # @param str [String]
50
+ # @return [String] Base64 encoded string
51
+ def base64_encode(str)
52
+ # expects "str" may not become too long
53
+ [str].pack('m0')
54
+ end
55
+ end
56
+ end
57
+ end
data/lib/net/smtp.rb CHANGED
@@ -1,4 +1,5 @@
1
1
  # frozen_string_literal: true
2
+
2
3
  # = net/smtp.rb
3
4
  #
4
5
  # Copyright (c) 1999-2007 Yukihiro Matsumoto.
@@ -12,21 +13,16 @@
12
13
  # This program is free software. You can re-distribute and/or
13
14
  # modify this program under the same terms as Ruby itself.
14
15
  #
15
- # $Id$
16
- #
17
16
  # See Net::SMTP for documentation.
18
17
  #
19
18
 
20
19
  require 'net/protocol'
21
- require 'digest/md5'
22
- require 'timeout'
23
20
  begin
24
21
  require 'openssl'
25
22
  rescue LoadError
26
23
  end
27
24
 
28
25
  module Net
29
-
30
26
  # Module mixed in to all SMTP error classes
31
27
  module SMTPError
32
28
  # This *class* is a module for backward compatibility.
@@ -35,8 +31,13 @@ module Net
35
31
  attr_reader :response
36
32
 
37
33
  def initialize(response, message: nil)
38
- @response = response
39
- @message = message
34
+ if response.is_a?(::Net::SMTP::Response)
35
+ @response = response
36
+ @message = message
37
+ else
38
+ @response = nil
39
+ @message = message || response
40
+ end
40
41
  end
41
42
 
42
43
  def message
@@ -78,25 +79,34 @@ module Net
78
79
  # == What is This Library?
79
80
  #
80
81
  # This library provides functionality to send internet
81
- # mail via SMTP, the Simple Mail Transfer Protocol. For details of
82
- # SMTP itself, see [RFC2821] (http://www.ietf.org/rfc/rfc2821.txt).
82
+ # mail via \SMTP, the Simple Mail Transfer Protocol. For details of
83
+ # \SMTP itself, see [RFC5321[https://www.rfc-editor.org/rfc/rfc5321.txt]].
84
+ # This library also implements \SMTP authentication, which is often
85
+ # necessary for message composers to submit messages to their
86
+ # outgoing \SMTP server, see
87
+ # [RFC6409[https://www.rfc-editor.org/rfc/rfc6409.html]],
88
+ # and [SMTPUTF8[https://www.rfc-editor.org/rfc/rfc6531.txt]], which is
89
+ # necessary to send messages to/from addresses containing characters
90
+ # outside the ASCII range.
83
91
  #
84
92
  # == What is This Library NOT?
85
93
  #
86
94
  # This library does NOT provide functions to compose internet mails.
87
95
  # You must create them by yourself. If you want better mail support,
88
- # try RubyMail or TMail or search for alternatives in
96
+ # try the mail[https://rubygems.org/gems/mail] or
97
+ # rmail[https://rubygems.org/gems/rmail] gems, or search for alternatives in
89
98
  # {RubyGems.org}[https://rubygems.org/] or {The Ruby
90
99
  # Toolbox}[https://www.ruby-toolbox.com/].
91
100
  #
92
- # FYI: the official documentation on internet mail is: [RFC2822] (http://www.ietf.org/rfc/rfc2822.txt).
101
+ # FYI: the official specification on internet mail is:
102
+ # [RFC5322[https://www.rfc-editor.org/rfc/rfc5322.txt]].
93
103
  #
94
104
  # == Examples
95
105
  #
96
106
  # === Sending Messages
97
107
  #
98
- # You must open a connection to an SMTP server before sending messages.
99
- # The first argument is the address of your SMTP server, and the second
108
+ # You must open a connection to an \SMTP server before sending messages.
109
+ # The first argument is the address of your \SMTP server, and the second
100
110
  # argument is the port number. Using SMTP.start with a block is the simplest
101
111
  # way to do this. This way, the SMTP connection is closed automatically
102
112
  # after the block is executed.
@@ -106,7 +116,7 @@ module Net
106
116
  # # Use the SMTP object smtp only in this block.
107
117
  # end
108
118
  #
109
- # Replace 'your.smtp.server' with your SMTP server. Normally
119
+ # Replace 'your.smtp.server' with your \SMTP server. Normally
110
120
  # your system manager or internet provider supplies a server
111
121
  # for you.
112
122
  #
@@ -139,7 +149,7 @@ module Net
139
149
  # smtp.send_message msgstr, 'from@address', 'to@address'
140
150
  # smtp.finish
141
151
  #
142
- # You can also use the block form of SMTP.start/SMTP#start. This closes
152
+ # You can also use the block form of SMTP.start or SMTP#start. This closes
143
153
  # the SMTP session automatically:
144
154
  #
145
155
  # # using block form of SMTP.start
@@ -152,36 +162,37 @@ module Net
152
162
  # === HELO domain
153
163
  #
154
164
  # In almost all situations, you must provide a third argument
155
- # to SMTP.start/SMTP#start. This is the domain name which you are on
165
+ # to SMTP.start or SMTP#start. This is the domain name which you are on
156
166
  # (the host to send mail from). It is called the "HELO domain".
157
- # The SMTP server will judge whether it should send or reject
167
+ # The \SMTP server will judge whether it should send or reject
158
168
  # the SMTP session by inspecting the HELO domain.
159
169
  #
160
- # Net::SMTP.start('your.smtp.server', 25
161
- # helo: 'mail.from.domain') { |smtp| ... }
170
+ # Net::SMTP.start('your.smtp.server', 25, helo: 'mail.from.domain') do |smtp|
171
+ # smtp.send_message msgstr, 'from@address', 'to@address'
172
+ # end
173
+ #
174
+ # === \SMTP Authentication
162
175
  #
163
- # === SMTP Authentication
176
+ # The Net::SMTP class supports the \SMTP extension for SASL Authentication
177
+ # [RFC4954[https://www.rfc-editor.org/rfc/rfc4954.html]] and the following
178
+ # SASL mechanisms: +PLAIN+, +LOGIN+ _(deprecated)_, and +CRAM-MD5+
179
+ # _(deprecated)_.
164
180
  #
165
- # The Net::SMTP class supports three authentication schemes;
166
- # PLAIN, LOGIN and CRAM MD5. (SMTP Authentication: [RFC2554])
167
- # To use SMTP authentication, pass extra arguments to
168
- # SMTP.start/SMTP#start.
181
+ # To use \SMTP authentication, pass extra arguments to
182
+ # SMTP.start or SMTP#start.
169
183
  #
170
184
  # # PLAIN
171
- # Net::SMTP.start('your.smtp.server', 25
185
+ # Net::SMTP.start('your.smtp.server', 25,
172
186
  # user: 'Your Account', secret: 'Your Password', authtype: :plain)
173
- # # LOGIN
174
- # Net::SMTP.start('your.smtp.server', 25
175
- # user: 'Your Account', secret: 'Your Password', authtype: :login)
176
187
  #
177
- # # CRAM MD5
178
- # Net::SMTP.start('your.smtp.server', 25
179
- # user: 'Your Account', secret: 'Your Password', authtype: :cram_md5)
188
+ # Support for other SASL mechanisms-such as +EXTERNAL+, +OAUTHBEARER+,
189
+ # +SCRAM-SHA-256+, and +XOAUTH2+-will be added in a future release.
190
+ #
191
+ # The +LOGIN+ and +CRAM-MD5+ mechanisms are still available for backwards
192
+ # compatibility, but are deprecated and should be avoided.
180
193
  #
181
194
  class SMTP < Protocol
182
- VERSION = "0.3.1"
183
-
184
- Revision = %q$Revision$.split[1]
195
+ VERSION = "0.5.0"
185
196
 
186
197
  # The default SMTP port number, 25.
187
198
  def SMTP.default_port
@@ -204,7 +215,7 @@ module Net
204
215
 
205
216
  def SMTP.default_ssl_context(ssl_context_params = nil)
206
217
  context = OpenSSL::SSL::SSLContext.new
207
- context.set_params(ssl_context_params ? ssl_context_params : {})
218
+ context.set_params(ssl_context_params || {})
208
219
  context
209
220
  end
210
221
 
@@ -223,10 +234,13 @@ module Net
223
234
  # If the hostname in the server certificate is different from +address+,
224
235
  # it can be specified with +tls_hostname+.
225
236
  #
226
- # Additional SSLContext params can be added to +ssl_context_params+ hash argument and are passed to
227
- # +OpenSSL::SSL::SSLContext#set_params+
237
+ # Additional SSLContext[https://ruby.github.io/openssl/OpenSSL/SSL/SSLContext.html]
238
+ # params can be added to the +ssl_context_params+ hash argument and are
239
+ # passed to {OpenSSL::SSL::SSLContext#set_params}[https://ruby.github.io/openssl/OpenSSL/SSL/SSLContext.html#method-i-set_params].
240
+ #
241
+ # <tt>tls_verify: true</tt> is equivalent to <tt>ssl_context_params: {
242
+ # verify_mode: OpenSSL::SSL::VERIFY_PEER }</tt>.
228
243
  #
229
- # +tls_verify: true+ is equivalent to +ssl_context_params: { verify_mode: OpenSSL::SSL::VERIFY_PEER }+.
230
244
  # This method does not open the TCP connection. You can use
231
245
  # SMTP.start instead of SMTP.new if you want to do everything
232
246
  # at once. Otherwise, follow SMTP.new with SMTP#start.
@@ -275,7 +289,7 @@ module Net
275
289
  attr_accessor :esmtp
276
290
 
277
291
  # +true+ if the SMTP object uses ESMTP (which it does by default).
278
- alias :esmtp? :esmtp
292
+ alias esmtp? esmtp
279
293
 
280
294
  # true if server advertises STARTTLS.
281
295
  # You cannot get valid value before opening SMTP session.
@@ -310,12 +324,13 @@ module Net
310
324
  auth_capable?('CRAM-MD5')
311
325
  end
312
326
 
327
+ # Returns whether the server advertises support for the authentication type.
328
+ # You cannot get valid result before opening SMTP session.
313
329
  def auth_capable?(type)
314
330
  return nil unless @capabilities
315
331
  return false unless @capabilities['AUTH']
316
332
  @capabilities['AUTH'].include?(type)
317
333
  end
318
- private :auth_capable?
319
334
 
320
335
  # Returns supported authentication methods on this server.
321
336
  # You cannot get valid value before opening SMTP session.
@@ -332,7 +347,7 @@ module Net
332
347
 
333
348
  alias ssl? tls?
334
349
 
335
- # Enables SMTP/TLS (SMTPS: SMTP over direct TLS connection) for
350
+ # Enables SMTP/TLS (SMTPS: \SMTP over direct TLS connection) for
336
351
  # this object. Must be called before the connection is established
337
352
  # to have any effect. +context+ is a OpenSSL::SSL::SSLContext object.
338
353
  def enable_tls(context = nil)
@@ -451,7 +466,10 @@ module Net
451
466
  #
452
467
  # This method is equivalent to:
453
468
  #
454
- # Net::SMTP.new(address, port).start(helo: helo_domain, user: account, secret: password, authtype: authtype, tls_verify: flag, tls_hostname: hostname, ssl_context_params: nil)
469
+ # Net::SMTP.new(address, port, tls_verify: flag, tls_hostname: hostname, ssl_context_params: nil)
470
+ # .start(helo: helo_domain, user: account, secret: password, authtype: authtype)
471
+ #
472
+ # See also: Net::SMTP.new, #start
455
473
  #
456
474
  # === Example
457
475
  #
@@ -476,12 +494,6 @@ module Net
476
494
  # +helo+ is the _HELO_ _domain_ provided by the client to the
477
495
  # server (see overview comments); it defaults to 'localhost'.
478
496
  #
479
- # The remaining arguments are used for SMTP authentication, if required
480
- # or desired. +user+ is the account name; +secret+ is your password
481
- # or other authentication token; and +authtype+ is the authentication
482
- # type, one of :plain, :login, or :cram_md5. See the discussion of
483
- # SMTP Authentication in the overview notes.
484
- #
485
497
  # If +tls+ is true, enable TLS. The default is false.
486
498
  # If +starttls+ is :always, enable STARTTLS, if +:auto+, use STARTTLS when the server supports it,
487
499
  # if false, disable STARTTLS.
@@ -490,10 +502,26 @@ module Net
490
502
  # If the hostname in the server certificate is different from +address+,
491
503
  # it can be specified with +tls_hostname+.
492
504
  #
493
- # Additional SSLContext params can be added to +ssl_context_params+ hash argument and are passed to
494
- # +OpenSSL::SSL::SSLContext#set_params+
505
+ # Additional SSLContext[https://ruby.github.io/openssl/OpenSSL/SSL/SSLContext.html]
506
+ # params can be added to the +ssl_context_params+ hash argument and are
507
+ # passed to {OpenSSL::SSL::SSLContext#set_params}[https://ruby.github.io/openssl/OpenSSL/SSL/SSLContext.html#method-i-set_params].
508
+ #
509
+ # <tt>tls_verify: true</tt> is equivalent to <tt>ssl_context_params: {
510
+ # verify_mode: OpenSSL::SSL::VERIFY_PEER }</tt>.
511
+ #
512
+ # The remaining arguments are used for \SMTP authentication, if required or
513
+ # desired.
514
+ #
515
+ # +authtype+ is the SASL authentication mechanism.
516
+ #
517
+ # +user+ is the authentication or authorization identity.
518
+ #
519
+ # +secret+ or +password+ is your password or other authentication token.
495
520
  #
496
- # +tls_verify: true+ is equivalent to +ssl_context_params: { verify_mode: OpenSSL::SSL::VERIFY_PEER }+.
521
+ # These will be sent to #authenticate as positional arguments-the exact
522
+ # semantics are dependent on the +authtype+.
523
+ #
524
+ # See the discussion of Net::SMTP@SMTP+Authentication in the overview notes.
497
525
  #
498
526
  # === Errors
499
527
  #
@@ -521,7 +549,7 @@ module Net
521
549
  new(address, port, tls: tls, starttls: starttls, tls_verify: tls_verify, tls_hostname: tls_hostname, ssl_context_params: ssl_context_params).start(helo: helo, user: user, secret: secret, authtype: authtype, &block)
522
550
  end
523
551
 
524
- # +true+ if the SMTP session has been started.
552
+ # +true+ if the \SMTP session has been started.
525
553
  def started?
526
554
  @started
527
555
  end
@@ -538,11 +566,21 @@ module Net
538
566
  # +helo+ is the _HELO_ _domain_ that you'll dispatch mails from; see
539
567
  # the discussion in the overview notes.
540
568
  #
541
- # If both of +user+ and +secret+ are given, SMTP authentication
542
- # will be attempted using the AUTH command. +authtype+ specifies
543
- # the type of authentication to attempt; it must be one of
544
- # :login, :plain, and :cram_md5. See the notes on SMTP Authentication
545
- # in the overview.
569
+ # The remaining arguments are used for \SMTP authentication, if required or
570
+ # desired.
571
+ #
572
+ # +authtype+ is the SASL authentication mechanism.
573
+ #
574
+ # +user+ is the authentication or authorization identity.
575
+ #
576
+ # +secret+ or +password+ is your password or other authentication token.
577
+ #
578
+ # These will be sent to #authenticate as positional arguments-the exact
579
+ # semantics are dependent on the +authtype+.
580
+ #
581
+ # See the discussion of Net::SMTP@SMTP+Authentication in the overview notes.
582
+ #
583
+ # See also: Net::SMTP.start
546
584
  #
547
585
  # === Block Usage
548
586
  #
@@ -622,28 +660,24 @@ module Net
622
660
  private
623
661
 
624
662
  def tcp_socket(address, port)
625
- begin
626
- Socket.tcp address, port, nil, nil, connect_timeout: @open_timeout
627
- rescue Errno::ETIMEDOUT #raise Net:OpenTimeout instead for compatibility with previous versions
628
- raise Net::OpenTimeout, "Timeout to open TCP connection to "\
629
- "#{address}:#{port} (exceeds #{@open_timeout} seconds)"
630
- end
663
+ TCPSocket.open address, port
631
664
  end
632
665
 
633
666
  def do_start(helo_domain, user, secret, authtype)
634
667
  raise IOError, 'SMTP session already started' if @started
635
- if user or secret
636
- check_auth_method(authtype || DEFAULT_AUTH_TYPE)
637
- check_auth_args user, secret
668
+ if user || secret || authtype
669
+ check_auth_args authtype, user, secret
670
+ end
671
+ s = Timeout.timeout(@open_timeout, Net::OpenTimeout) do
672
+ tcp_socket(@address, @port)
638
673
  end
639
- s = tcp_socket(@address, @port)
640
674
  logging "Connection opened: #{@address}:#{@port}"
641
675
  @socket = new_internet_message_io(tls? ? tlsconnect(s, @ssl_context_tls) : s)
642
676
  check_response critical { recv_response() }
643
677
  do_helo helo_domain
644
678
  if ! tls? and (starttls_always? or (capable_starttls? and starttls_auto?))
645
679
  unless capable_starttls?
646
- raise SMTPUnsupportedCommand.new(nil, message: "STARTTLS is not supported on this server")
680
+ raise SMTPUnsupportedCommand, "STARTTLS is not supported on this server"
647
681
  end
648
682
  starttls
649
683
  @socket = new_internet_message_io(tlsconnect(s, @ssl_context_starttls))
@@ -703,6 +737,18 @@ module Net
703
737
  @socket = nil
704
738
  end
705
739
 
740
+ def requires_smtputf8(address)
741
+ if address.kind_of? Address
742
+ !address.address.ascii_only?
743
+ else
744
+ !address.ascii_only?
745
+ end
746
+ end
747
+
748
+ def any_require_smtputf8(addresses)
749
+ addresses.any?{ |a| requires_smtputf8(a) }
750
+ end
751
+
706
752
  #
707
753
  # Message Sending
708
754
  #
@@ -746,7 +792,9 @@ module Net
746
792
  # * IOError
747
793
  #
748
794
  def send_message(msgstr, from_addr, *to_addrs)
795
+ to_addrs.flatten!
749
796
  raise IOError, 'closed session' unless @socket
797
+ from_addr = Address.new(from_addr, 'SMTPUTF8') if any_require_smtputf8(to_addrs) && capable?('SMTPUTF8')
750
798
  mailfrom from_addr
751
799
  rcptto_list(to_addrs) {data msgstr}
752
800
  end
@@ -799,7 +847,9 @@ module Net
799
847
  # * IOError
800
848
  #
801
849
  def open_message_stream(from_addr, *to_addrs, &block) # :yield: stream
850
+ to_addrs.flatten!
802
851
  raise IOError, 'closed session' unless @socket
852
+ from_addr = Address.new(from_addr, 'SMTPUTF8') if any_require_smtputf8(to_addrs) && capable?('SMTPUTF8')
803
853
  mailfrom from_addr
804
854
  rcptto_list(to_addrs) {data(&block)}
805
855
  end
@@ -810,92 +860,28 @@ module Net
810
860
  # Authentication
811
861
  #
812
862
 
813
- public
814
-
815
863
  DEFAULT_AUTH_TYPE = :plain
816
864
 
865
+ # Authenticates with the server, using the "AUTH" command.
866
+ #
867
+ # +authtype+ is the name of a SASL authentication mechanism.
868
+ #
869
+ # All arguments-other than +authtype+-are forwarded to the authenticator.
870
+ # Different authenticators may interpret the +user+ and +secret+
871
+ # arguments differently.
817
872
  def authenticate(user, secret, authtype = DEFAULT_AUTH_TYPE)
818
- check_auth_method authtype
819
- check_auth_args user, secret
820
- public_send auth_method(authtype), user, secret
821
- end
822
-
823
- def auth_plain(user, secret)
824
- check_auth_args user, secret
825
- res = critical {
826
- get_response('AUTH PLAIN ' + base64_encode("\0#{user}\0#{secret}"))
827
- }
828
- check_auth_response res
829
- res
830
- end
831
-
832
- def auth_login(user, secret)
833
- check_auth_args user, secret
834
- res = critical {
835
- check_auth_continue get_response('AUTH LOGIN')
836
- check_auth_continue get_response(base64_encode(user))
837
- get_response(base64_encode(secret))
838
- }
839
- check_auth_response res
840
- res
841
- end
842
-
843
- def auth_cram_md5(user, secret)
844
- check_auth_args user, secret
845
- res = critical {
846
- res0 = get_response('AUTH CRAM-MD5')
847
- check_auth_continue res0
848
- crammed = cram_md5_response(secret, res0.cram_md5_challenge)
849
- get_response(base64_encode("#{user} #{crammed}"))
850
- }
851
- check_auth_response res
852
- res
873
+ check_auth_args authtype, user, secret
874
+ authenticator = Authenticator.auth_class(authtype).new(self)
875
+ authenticator.auth(user, secret)
853
876
  end
854
877
 
855
878
  private
856
879
 
857
- def check_auth_method(type)
858
- unless respond_to?(auth_method(type), true)
880
+ def check_auth_args(type, *args, **kwargs)
881
+ type ||= DEFAULT_AUTH_TYPE
882
+ klass = Authenticator.auth_class(type) or
859
883
  raise ArgumentError, "wrong authentication type #{type}"
860
- end
861
- end
862
-
863
- def auth_method(type)
864
- "auth_#{type.to_s.downcase}".intern
865
- end
866
-
867
- def check_auth_args(user, secret, authtype = DEFAULT_AUTH_TYPE)
868
- unless user
869
- raise ArgumentError, 'SMTP-AUTH requested but missing user name'
870
- end
871
- unless secret
872
- raise ArgumentError, 'SMTP-AUTH requested but missing secret phrase'
873
- end
874
- end
875
-
876
- def base64_encode(str)
877
- # expects "str" may not become too long
878
- [str].pack('m0')
879
- end
880
-
881
- IMASK = 0x36
882
- OMASK = 0x5c
883
-
884
- # CRAM-MD5: [RFC2195]
885
- def cram_md5_response(secret, challenge)
886
- tmp = Digest::MD5.digest(cram_secret(secret, IMASK) + challenge)
887
- Digest::MD5.hexdigest(cram_secret(secret, OMASK) + tmp)
888
- end
889
-
890
- CRAM_BUFSIZE = 64
891
-
892
- def cram_secret(secret, mask)
893
- secret = Digest::MD5.digest(secret) if secret.size > CRAM_BUFSIZE
894
- buf = secret.ljust(CRAM_BUFSIZE, "\0")
895
- 0.upto(buf.size - 1) do |i|
896
- buf[i] = (buf[i].ord ^ mask).chr
897
- end
898
- buf
884
+ klass.check_args(*args, **kwargs)
899
885
  end
900
886
 
901
887
  #
@@ -924,29 +910,20 @@ module Net
924
910
 
925
911
  # +from_addr+ is +String+ or +Net::SMTP::Address+
926
912
  def mailfrom(from_addr)
927
- addr = Address.new(from_addr)
913
+ addr = if requires_smtputf8(from_addr) && capable?("SMTPUTF8")
914
+ Address.new(from_addr, "SMTPUTF8")
915
+ else
916
+ Address.new(from_addr)
917
+ end
928
918
  getok((["MAIL FROM:<#{addr.address}>"] + addr.parameters).join(' '))
929
919
  end
930
920
 
931
921
  def rcptto_list(to_addrs)
932
922
  raise ArgumentError, 'mail destination not given' if to_addrs.empty?
933
- ok_users = []
934
- unknown_users = []
935
923
  to_addrs.flatten.each do |addr|
936
- begin
937
- rcptto addr
938
- rescue SMTPAuthenticationError
939
- unknown_users << addr.to_s.dump
940
- else
941
- ok_users << addr
942
- end
924
+ rcptto addr
943
925
  end
944
- raise ArgumentError, 'mail destination not given' if ok_users.empty?
945
- ret = yield
946
- unless unknown_users.empty?
947
- raise SMTPAuthenticationError, "failed to deliver for #{unknown_users.join(', ')}"
948
- end
949
- ret
926
+ yield
950
927
  end
951
928
 
952
929
  # +to_addr+ is +String+ or +Net::SMTP::Address+
@@ -1009,6 +986,12 @@ module Net
1009
986
  getok('QUIT')
1010
987
  end
1011
988
 
989
+ def get_response(reqline)
990
+ validate_line reqline
991
+ @socket.writeline reqline
992
+ recv_response()
993
+ end
994
+
1012
995
  private
1013
996
 
1014
997
  def validate_line(line)
@@ -1028,12 +1011,6 @@ module Net
1028
1011
  res
1029
1012
  end
1030
1013
 
1031
- def get_response(reqline)
1032
- validate_line reqline
1033
- @socket.writeline reqline
1034
- recv_response()
1035
- end
1036
-
1037
1014
  def recv_response
1038
1015
  buf = ''.dup
1039
1016
  while true
@@ -1066,18 +1043,6 @@ module Net
1066
1043
  end
1067
1044
  end
1068
1045
 
1069
- def check_auth_response(res)
1070
- unless res.success?
1071
- raise SMTPAuthenticationError.new(res)
1072
- end
1073
- end
1074
-
1075
- def check_auth_continue(res)
1076
- unless res.continue?
1077
- raise res.exception_class.new(res)
1078
- end
1079
- end
1080
-
1081
1046
  # This class represents a response received by the SMTP server. Instances
1082
1047
  # of this class are created by the SMTP class; they should not be directly
1083
1048
  # created by the user. For more information on SMTP responses, view
@@ -1165,7 +1130,7 @@ module Net
1165
1130
  class Address
1166
1131
  # mail address [String]
1167
1132
  attr_reader :address
1168
- # paramters [Array<String>]
1133
+ # parameters [Array<String>]
1169
1134
  attr_reader :parameters
1170
1135
 
1171
1136
  # :call-seq:
@@ -1179,17 +1144,21 @@ module Net
1179
1144
  @parameters = address.parameters
1180
1145
  else
1181
1146
  @address = address
1182
- @parameters = (args + [kw_args]).map{|param| Array(param)}.flatten(1).map{|param| Array(param).compact.join('=')}
1147
+ @parameters = []
1183
1148
  end
1149
+ @parameters = (parameters + args + [kw_args]).map{|param| Array(param)}.flatten(1).map{|param| Array(param).compact.join('=')}.uniq
1184
1150
  end
1185
1151
 
1186
1152
  def to_s
1187
1153
  @address
1188
1154
  end
1189
1155
  end
1190
-
1191
1156
  end # class SMTP
1192
1157
 
1193
1158
  SMTPSession = SMTP # :nodoc:
1159
+ end
1194
1160
 
1161
+ require_relative 'smtp/authenticator'
1162
+ Dir.glob("#{__dir__}/smtp/auth_*.rb") do |r|
1163
+ require_relative r
1195
1164
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: net-smtp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.1
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yukihiro Matsumoto
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-12-12 00:00:00.000000000 Z
11
+ date: 2024-03-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: net-protocol
@@ -24,34 +24,6 @@ dependencies:
24
24
  - - ">="
25
25
  - !ruby/object:Gem::Version
26
26
  version: '0'
27
- - !ruby/object:Gem::Dependency
28
- name: digest
29
- requirement: !ruby/object:Gem::Requirement
30
- requirements:
31
- - - ">="
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :runtime
35
- prerelease: false
36
- version_requirements: !ruby/object:Gem::Requirement
37
- requirements:
38
- - - ">="
39
- - !ruby/object:Gem::Version
40
- version: '0'
41
- - !ruby/object:Gem::Dependency
42
- name: timeout
43
- requirement: !ruby/object:Gem::Requirement
44
- requirements:
45
- - - ">="
46
- - !ruby/object:Gem::Version
47
- version: '0'
48
- type: :runtime
49
- prerelease: false
50
- version_requirements: !ruby/object:Gem::Requirement
51
- requirements:
52
- - - ">="
53
- - !ruby/object:Gem::Version
54
- version: '0'
55
27
  description: Simple Mail Transfer Protocol client library for Ruby.
56
28
  email:
57
29
  - matz@ruby-lang.org
@@ -60,8 +32,14 @@ extensions: []
60
32
  extra_rdoc_files: []
61
33
  files:
62
34
  - LICENSE.txt
35
+ - NEWS.md
36
+ - README.md
63
37
  - lib/net/smtp.rb
64
- - net-smtp.gemspec
38
+ - lib/net/smtp/auth_cram_md5.rb
39
+ - lib/net/smtp/auth_login.rb
40
+ - lib/net/smtp/auth_plain.rb
41
+ - lib/net/smtp/auth_xoauth2.rb
42
+ - lib/net/smtp/authenticator.rb
65
43
  homepage: https://github.com/ruby/net-smtp
66
44
  licenses:
67
45
  - Ruby
@@ -84,7 +62,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
84
62
  - !ruby/object:Gem::Version
85
63
  version: '0'
86
64
  requirements: []
87
- rubygems_version: 3.3.0.dev
65
+ rubygems_version: 3.5.3
88
66
  signing_key:
89
67
  specification_version: 4
90
68
  summary: Simple Mail Transfer Protocol client library for Ruby.
data/net-smtp.gemspec DELETED
@@ -1,35 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- name = File.basename(__FILE__, ".gemspec")
4
- version = ["lib", Array.new(name.count("-"), "..").join("/")].find do |dir|
5
- break File.foreach(File.join(__dir__, dir, "#{name.tr('-', '/')}.rb")) do |line|
6
- /^\s*VERSION\s*=\s*"(.*)"/ =~ line and break $1
7
- end rescue nil
8
- end
9
-
10
- Gem::Specification.new do |spec|
11
- spec.name = name
12
- spec.version = version
13
- spec.authors = ["Yukihiro Matsumoto"]
14
- spec.email = ["matz@ruby-lang.org"]
15
-
16
- spec.summary = %q{Simple Mail Transfer Protocol client library for Ruby.}
17
- spec.description = %q{Simple Mail Transfer Protocol client library for Ruby.}
18
- spec.homepage = "https://github.com/ruby/net-smtp"
19
- spec.licenses = ["Ruby", "BSD-2-Clause"]
20
- spec.required_ruby_version = ">= 2.6.0"
21
-
22
- spec.metadata["homepage_uri"] = spec.homepage
23
- spec.metadata["source_code_uri"] = spec.homepage
24
-
25
- spec.files = %w[
26
- LICENSE.txt
27
- lib/net/smtp.rb
28
- net-smtp.gemspec
29
- ]
30
- spec.require_paths = ["lib"]
31
-
32
- spec.add_dependency "net-protocol"
33
- spec.add_dependency "digest"
34
- spec.add_dependency "timeout"
35
- end