Skip to content

Commit 634854b

Browse files
authored
πŸ”€ Merge pull request #175 from nevans/deprecated-ssl-parameters
πŸ—‘οΈ Deprecate backward compatible parameters to `new` and `starttls`
2 parents cfb8caf + 2066e7f commit 634854b

File tree

4 files changed

+328
-50
lines changed

4 files changed

+328
-50
lines changed

β€Žlib/net/imap.rb

Lines changed: 18 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -760,7 +760,7 @@ class << self
760760
# [idle_response_timeout]
761761
# Seconds to wait until an IDLE response is received
762762
#
763-
# See DeprecatedClientOptions for obsolete backwards compatible arguments.
763+
# See DeprecatedClientOptions.new for deprecated arguments.
764764
#
765765
# ==== Examples
766766
#
@@ -810,16 +810,15 @@ class << self
810810
# [Net::IMAP::ByeResponseError]
811811
# Connected to the host successfully, but it immediately said goodbye.
812812
#
813-
def initialize(host, options = {}, *deprecated)
813+
def initialize(host, port: nil, ssl: nil,
814+
open_timeout: 30, idle_response_timeout: 5)
814815
super()
815-
options = convert_deprecated_options(options, *deprecated)
816-
817816
# Config options
818817
@host = host
819-
@port = options[:port] || (options[:ssl] ? SSL_PORT : PORT)
820-
@open_timeout = options[:open_timeout] || 30
821-
@idle_response_timeout = options[:idle_response_timeout] || 5
822-
@ssl_ctx_params, @ssl_ctx = build_ssl_ctx(options[:ssl])
818+
@port = port || (ssl ? SSL_PORT : PORT)
819+
@open_timeout = Integer(open_timeout)
820+
@idle_response_timeout = Integer(idle_response_timeout)
821+
@ssl_ctx_params, @ssl_ctx = build_ssl_ctx(ssl)
823822

824823
# Basic Client State
825824
@utf8_strings = false
@@ -1076,7 +1075,12 @@ def logout
10761075
# Sends a {STARTTLS command [IMAP4rev1 Β§6.2.1]}[https://www.rfc-editor.org/rfc/rfc3501#section-6.2.1]
10771076
# to start a TLS session.
10781077
#
1079-
# Any +options+ are forwarded to OpenSSL::SSL::SSLContext#set_params.
1078+
# Any +options+ are forwarded directly to
1079+
# {OpenSSL::SSL::SSLContext#set_params}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#method-i-set_params];
1080+
# the keys are names of attribute assignment methods on
1081+
# SSLContext[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html].
1082+
#
1083+
# See DeprecatedClientOptions#starttls for deprecated arguments.
10801084
#
10811085
# This method returns after TLS negotiation and hostname verification are
10821086
# both successful. Any error indicates that the connection has not been
@@ -1098,14 +1102,8 @@ def logout
10981102
# Server capabilities may change after #starttls, #login, and #authenticate.
10991103
# Cached #capabilities will be cleared when this method completes.
11001104
#
1101-
def starttls(options = {}, verify = true)
1102-
begin
1103-
# for backward compatibility
1104-
certs = options.to_str
1105-
options = create_ssl_params(certs, verify)
1106-
rescue NoMethodError
1107-
end
1108-
@ssl_ctx_params, @ssl_ctx = build_ssl_ctx(options || {})
1105+
def starttls(**options)
1106+
@ssl_ctx_params, @ssl_ctx = build_ssl_ctx(options)
11091107
send_command("STARTTLS") do |resp|
11101108
if resp.kind_of?(TaggedResponse) && resp.name == "OK"
11111109
clear_cached_capabilities
@@ -2354,20 +2352,6 @@ def remove_response_handler(handler)
23542352

23552353
@@debug = false
23562354

2357-
def convert_deprecated_options(
2358-
port_or_options = {}, usessl = false, certs = nil, verify = true
2359-
)
2360-
port_or_options.to_hash
2361-
rescue NoMethodError
2362-
# for backward compatibility
2363-
options = {}
2364-
options[:port] = port_or_options
2365-
if usessl
2366-
options[:ssl] = create_ssl_params(certs, verify)
2367-
end
2368-
options
2369-
end
2370-
23712355
def start_imap_connection
23722356
@greeting = get_server_greeting
23732357
@capabilities = capabilities_from_resp_code @greeting
@@ -2695,23 +2679,6 @@ def build_ssl_ctx(ssl)
26952679
end
26962680
end
26972681

2698-
def create_ssl_params(certs = nil, verify = true)
2699-
params = {}
2700-
if certs
2701-
if File.file?(certs)
2702-
params[:ca_file] = certs
2703-
elsif File.directory?(certs)
2704-
params[:ca_path] = certs
2705-
end
2706-
end
2707-
if verify
2708-
params[:verify_mode] = VERIFY_PEER
2709-
else
2710-
params[:verify_mode] = VERIFY_NONE
2711-
end
2712-
return params
2713-
end
2714-
27152682
def start_tls_session
27162683
raise "SSL extension not installed" unless defined?(OpenSSL::SSL)
27172684
raise "already using SSL" if @sock.kind_of?(OpenSSL::SSL::SSLSocket)
@@ -2746,3 +2713,6 @@ def self.saslprep(string, **opts)
27462713
require_relative "imap/response_data"
27472714
require_relative "imap/response_parser"
27482715
require_relative "imap/authenticators"
2716+
2717+
require_relative "imap/deprecated_client_options"
2718+
Net::IMAP.prepend Net::IMAP::DeprecatedClientOptions
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
# frozen_string_literal: true
2+
3+
module Net
4+
class IMAP < Protocol
5+
6+
# This module handles deprecated arguments to various Net::IMAP methods.
7+
module DeprecatedClientOptions
8+
9+
# :call-seq:
10+
# Net::IMAP.new(host, **options) # standard keyword options
11+
# Net::IMAP.new(host, options) # obsolete hash options
12+
# Net::IMAP.new(host, port) # obsolete port argument
13+
# Net::IMAP.new(host, port, usessl, certs = nil, verify = true) # deprecated SSL arguments
14+
#
15+
# Translates Net::IMAP.new arguments for backward compatibility.
16+
#
17+
# ==== Obsolete arguments
18+
#
19+
# Using obsolete arguments does not a warning. Obsolete arguments will be
20+
# deprecated by a future release.
21+
#
22+
# If a second positional argument is given and it is a hash (or is
23+
# convertable via +#to_hash+), it is converted to keyword arguments.
24+
#
25+
# # Obsolete:
26+
# Net::IMAP.new("imap.example.com", options_hash)
27+
# # Use instead:
28+
# Net::IMAP.new("imap.example.com", **options_hash)
29+
#
30+
# If a second positional argument is given and it is not a hash, it is
31+
# converted to the +port+ keyword argument.
32+
# # Obsolete:
33+
# Net::IMAP.new("imap.example.com", 114433)
34+
# # Use instead:
35+
# Net::IMAP.new("imap.example.com", port: 114433)
36+
#
37+
# ==== Deprecated arguments
38+
#
39+
# Using deprecated arguments prints a warning. Convert to keyword
40+
# arguments to avoid the warning. Deprecated arguments will be removed in
41+
# a future release.
42+
#
43+
# If +usessl+ is false, +certs+, and +verify+ are ignored. When it true,
44+
# all three arguments are converted to the +ssl+ keyword argument.
45+
# Without +certs+ or +verify+, it is converted to <tt>ssl: true</tt>.
46+
# # DEPRECATED:
47+
# Net::IMAP.new("imap.example.com", nil, true) # => prints a warning
48+
# # Use instead:
49+
# Net::IMAP.new("imap.example.com", ssl: true)
50+
#
51+
# When +certs+ is a path to a directory, it is converted to <tt>ca_path:
52+
# certs</tt>.
53+
# # DEPRECATED:
54+
# Net::IMAP.new("imap.example.com", nil, true, "/path/to/certs") # => prints a warning
55+
# # Use instead:
56+
# Net::IMAP.new("imap.example.com", ssl: {ca_path: "/path/to/certs"})
57+
#
58+
# When +certs+ is a path to a file, it is converted to <tt>ca_file:
59+
# certs</tt>.
60+
# # DEPRECATED:
61+
# Net::IMAP.new("imap.example.com", nil, true, "/path/to/cert.pem") # => prints a warning
62+
# # Use instead:
63+
# Net::IMAP.new("imap.example.com", ssl: {ca_file: "/path/to/cert.pem"})
64+
#
65+
# When +verify+ is +false+, it is converted to <tt>verify_mode:
66+
# OpenSSL::SSL::VERIFY_NONE</tt>.
67+
# # DEPRECATED:
68+
# Net::IMAP.new("imap.example.com", nil, true, nil, false) # => prints a warning
69+
# # Use instead:
70+
# Net::IMAP.new("imap.example.com", ssl: {verify_mode: OpenSSL::SSL::VERIFY_NONE})
71+
#
72+
def initialize(host, port_or_options = nil, *deprecated, **options)
73+
if port_or_options.nil? && deprecated.empty?
74+
super host, **options
75+
elsif options.any?
76+
# Net::IMAP.new(host, *__invalid__, **options)
77+
raise ArgumentError, "Do not combine deprecated and keyword arguments"
78+
elsif port_or_options.respond_to?(:to_hash) and deprecated.any?
79+
# Net::IMAP.new(host, options, *__invalid__)
80+
raise ArgumentError, "Do not use deprecated SSL params with options hash"
81+
elsif port_or_options.respond_to?(:to_hash)
82+
super host, **Hash.try_convert(port_or_options)
83+
elsif deprecated.empty?
84+
super host, port: port_or_options
85+
elsif deprecated.shift
86+
warn "DEPRECATED: Call Net::IMAP.new with keyword options", uplevel: 1
87+
super host, port: port_or_options, ssl: create_ssl_params(*deprecated)
88+
else
89+
warn "DEPRECATED: Call Net::IMAP.new with keyword options", uplevel: 1
90+
super host, port: port_or_options, ssl: false
91+
end
92+
end
93+
94+
# :call-seq:
95+
# starttls(**options) # standard
96+
# starttls(options = {}) # obsolete
97+
# starttls(certs = nil, verify = true) # deprecated
98+
#
99+
# Translates Net::IMAP#starttls arguments for backward compatibility.
100+
#
101+
# Support for +certs+ and +verify+ will be dropped in a future release.
102+
#
103+
# See ::new for interpretation of +certs+ and +verify+.
104+
def starttls(*deprecated, **options)
105+
if deprecated.empty?
106+
super(**options)
107+
elsif options.any?
108+
# starttls(*__invalid__, **options)
109+
raise ArgumentError, "Do not combine deprecated and keyword options"
110+
elsif deprecated.first.respond_to?(:to_hash) && deprecated.length > 1
111+
# starttls(*__invalid__, **options)
112+
raise ArgumentError, "Do not use deprecated verify param with options hash"
113+
elsif deprecated.first.respond_to?(:to_hash)
114+
super(**Hash.try_convert(deprecated.first))
115+
else
116+
warn "DEPRECATED: Call Net::IMAP#starttls with keyword options", uplevel: 1
117+
super(**create_ssl_params(*deprecated))
118+
end
119+
end
120+
121+
private
122+
123+
def create_ssl_params(certs = nil, verify = true)
124+
params = {}
125+
if certs
126+
if File.file?(certs)
127+
params[:ca_file] = certs
128+
elsif File.directory?(certs)
129+
params[:ca_path] = certs
130+
end
131+
end
132+
params[:verify_mode] =
133+
verify ? OpenSSL::SSL::VERIFY_PEER : OpenSSL::SSL::VERIFY_NONE
134+
params
135+
end
136+
137+
end
138+
end
139+
end

β€Žtest/net/imap/fake_server/test_helper.rb

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,14 @@
44

55
module Net::IMAP::FakeServer::TestHelper
66

7-
def run_fake_server_in_thread(timeout: 5, **opts)
7+
def run_fake_server_in_thread(ignore_io_error: false, timeout: 5, **opts)
88
Timeout.timeout(timeout) do
99
server = Net::IMAP::FakeServer.new(timeout: timeout, **opts)
10-
@threads << Thread.new do server.run end if @threads
10+
@threads << Thread.new do
11+
server.run
12+
rescue IOError
13+
raise unless ignore_io_error
14+
end
1115
yield server
1216
ensure
1317
server&.shutdown

0 commit comments

Comments
Β (0)