Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 39 additions & 8 deletions lib/net/imap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,12 @@ module Net
# - #connection_state: Returns the connection state.
# - #disconnected?: True if the connection has been closed.
# - #tls_verified?: Returns whether TLS is used and #host has been verified.
# - #tls_connected?: Returns +true+ after TLS negotiation has completed.
#
# <em>*NOTE:* This does _NOT_ indicate a secure TLS connection.</em>
# - #tls_socket?: Returns +true+ after TLS negotiation has started.
#
# <em>*NOTE:* This does _NOT_ indicate a secure TLS connection.</em>
# - #ssl_ctx: Returns the {SSLContext}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html]
# after attempting to start TLS.
#
Expand Down Expand Up @@ -1035,8 +1041,10 @@ def max_response_size=(val) config.max_response_size = val end

# Returns the
# {SSLContext}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html]
# used by the SSLSocket when TLS is attempted, even when the TLS handshake
# is unsuccessful. The context object will be frozen.
# used by the
# {OpenSSL::SSL::SSLSocket}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLSocket.html].
# when TLS is attempted, even when the TLS handshake is unsuccessful. The
# context object will be frozen.
#
# Returns +nil+ for a plaintext connection.
#
Expand Down Expand Up @@ -1299,12 +1307,11 @@ def inspect
end

private def inspect_tls_state
if tls_verified?
"TLS"
elsif ssl_ctx && @sock.kind_of?(OpenSSL::SSL::SSLSocket)
"TLS (#{@tls_connected ? "NOT VERIFIED" : "NOT ESTABLISHED"})"
else
"PLAINTEXT#{" (TLS NOT STARTED)" if ssl_ctx}"
if tls_verified? then "TLS"
elsif tls_connected? then "TLS (NOT VERIFIED)"
elsif tls_socket? then "TLS (NOT ESTABLISHED)"
elsif ssl_ctx then "PLAINTEXT (TLS NOT STARTED)"
else "PLAINTEXT"
end
end

Expand All @@ -1313,6 +1320,30 @@ def inspect
# but peer verification was disabled.
def tls_verified?; @tls_verified end

# Returns +true+ after
# {OpenSSL::SSL::SSLSocket#connect}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLSocket.html#method-i-connect]
# completes successfully.
#
# <em>*NOTE:* This does _NOT_ indicate that the remote hostname has been
# verified.</em>
#
# This does _not_ indicate current connection state. It will continue to
# return +true+ even after a successful connection has disconnected.
#
# See #tls_verified?
def tls_connected?; @tls_connected end

# Returns +true+ when the connection is a
# {OpenSSL::SSL::SSLSocket}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLSocket.html]
#
# <em>*NOTE:* This does _NOT_ indicate that a TLS session has been
# established or that remote hostname has been verified.</em>
#
# This only indicates that TLS negotiation has started.
#
# See #tls_verified?
def tls_socket?; @sock.kind_of?(OpenSSL::SSL::SSLSocket) end

# Disconnects from the server.
#
# Waits for receiver thread to close before returning, except when called
Expand Down
8 changes: 7 additions & 1 deletion test/net/imap/test_imap_tls.rb
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ def assert_tls_incomplete(imap) = assert_tls_stage imap, :incomplete
def assert_tls_unverified(imap) = assert_tls_stage imap, :unverified
def assert_tls_verified(imap) = assert_tls_stage imap, :verified

STAGES = %i[unstarted incomplete unverified verified].freeze

INSPECT_INCLUDES = {
unstarted: " PLAINTEXT (TLS NOT STARTED) disconnected",
incomplete: " TLS (NOT ESTABLISHED) disconnected",
Expand All @@ -43,12 +45,16 @@ def assert_tls_verified(imap) = assert_tls_stage imap, :verified
}

def assert_tls_stage(imap, stage)
verified = stage == :verified
socket, connected, verified = Array.new(3) { _1 < STAGES.index(stage) }
assert imap.ssl_ctx_params.frozen?, "#ssl_ctx_params should be frozen"
assert_kind_of Hash, imap.ssl_ctx_params,
"#ssl_ctx_params should be a Hash"
assert_kind_of OpenSSL::SSL::SSLContext, imap.ssl_ctx,
"#ssl_ctx should be an OpenSSL::SSL::SSLContext"
assert_equal socket, imap.tls_socket?,
"#tls_socket? should be #{socket}"
assert_equal connected, imap.tls_connected?,
"#tls_connected? should be #{connected}"
assert_equal verified, imap.tls_verified?,
"#tls_verified? should be #{verified}"
assert_include imap.inspect, INSPECT_INCLUDES.fetch(stage)
Expand Down
Loading