@@ -716,11 +716,104 @@ class << self
716716 alias default_imap_port default_port
717717 alias default_imaps_port default_tls_port
718718 alias default_ssl_port default_tls_port
719+
720+ # The default value for the +ssl+ option of ::new, when +port+ is
721+ # unspecified or non-standard.
722+ #
723+ # Defaults to +nil+ for backward compatibility, which prints a warning and
724+ # does _not_ use TLS.
725+ #
726+ # >>>
727+ # *Note*: A future version of Net::IMAP will default to +true+, as per
728+ # RFC7525[https://tools.ietf.org/html/rfc7525],
729+ # RFC7817[https://tools.ietf.org/html/rfc7817],
730+ # and RFC8314[https://tools.ietf.org/html/rfc8314].
731+ #
732+ # Set to +false+ to *globally* use insecure defaults and silence warnings.
733+ # Send <tt>ssl: false</tt> to ::new to explicitly silence warnings for a
734+ # single connection.
735+ attr_accessor :default_ssl
736+ end
737+
738+ # Creates a new Net::IMAP object and connects it to the specified
739+ # +host+.
740+ #
741+ # Accepts the following options:
742+ #
743+ # [port]
744+ # Port number (default value is 143 for imap, or 993 for imaps)
745+ # [ssl]
746+ # When +true+, the connection will use TLS using the defaults chosen by
747+ # {OpenSSL::SSL::SSLContext#set_params}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#method-i-set_params].
748+ # Use a hash to override the defaults---the keys are assignment methods on
749+ # SSLContext[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html].
750+ # Defaults to +true+ or +false+ to match +port+, or to ::default_ssl when
751+ # +port+ is unspecified or non-standard.
752+ # [open_timeout]
753+ # Seconds to wait until a connection is opened
754+ # [idle_response_timeout]
755+ # Seconds to wait until an IDLE response is received
756+ #
757+ # The most common errors are:
758+ #
759+ # Errno::ECONNREFUSED:: Connection refused by +host+ or an intervening
760+ # firewall.
761+ # Errno::ETIMEDOUT:: Connection timed out (possibly due to packets
762+ # being dropped by an intervening firewall).
763+ # Errno::ENETUNREACH:: There is no route to that network.
764+ # SocketError:: Hostname not known or other socket error.
765+ # Net::IMAP::ByeResponseError:: Connected to the host successfully, but
766+ # it immediately said goodbye.
767+ def initialize ( host ,
768+ port : nil ,
769+ ssl : nil ,
770+ open_timeout : 30 ,
771+ idle_response_timeout : 5 )
772+ # Basic configuration
773+ @host = host
774+ @ssl , @port = default_ssl_and_port ( ssl , port )
775+ @open_timeout = Integer ( open_timeout )
776+ @idle_response_timeout = Integer ( idle_response_timeout )
777+
778+ # Basic Client state
779+ super ( ) # Mutex and condition vars (MonitorMixin#initialize)
780+ @greeting = nil
781+ @capabilities = nil
782+ @utf8_strings = false # TODO: use @enabled instead
783+ @debug_output_bol = true
784+
785+ # Client Protocol Reciever
786+ @parser = ResponseParser . new
787+ @receiver_thread = nil
788+ @receiver_thread_terminating = false
789+ @exception = nil
790+
791+ # Client Protocol Sender
792+ @tag_prefix = "RUBY"
793+ @tagno = 0
794+
795+ # Response handlers
796+ @continuation_request_arrival = new_cond
797+ @continuation_request_exception = nil
798+ @tagged_response_arrival = new_cond
799+ @tagged_responses = { }
800+ @response_handlers = [ ]
801+ @responses = Hash . new { |h , k | h [ k ] = [ ] }
802+
803+ # Command execution state
804+ @logout_command_tag = nil
805+ @continued_command_tag = nil
806+ @idle_done_cond = nil
807+
808+ # create the connection
809+ @sock = nil
810+ start_connection
719811 end
720812
721813 def client_thread # :nodoc:
722- warn "Net::IMAP#client_thread is deprecated and will be removed soon."
723- @client_thread
814+ warn "Net::IMAP#client_thread is deprecated and always returns the " \
815+ "caller's current thread."
816+ Thread . current
724817 end
725818
726819 # Disconnects from the server.
@@ -966,15 +1059,9 @@ def logout
9661059 # Server capabilities may change after #starttls, #login, and #authenticate.
9671060 # Cached #capabilities will be cleared when this method completes.
9681061 #
969- def starttls ( options = { } , verify = true )
1062+ def starttls ( options = { } )
9701063 send_command ( "STARTTLS" ) do |resp |
9711064 if resp . kind_of? ( TaggedResponse ) && resp . name == "OK"
972- begin
973- # for backward compatibility
974- certs = options . to_str
975- options = create_ssl_params ( certs , verify )
976- rescue NoMethodError
977- end
9781065 clear_cached_capabilities
9791066 clear_responses
9801067 start_tls_session ( options )
@@ -2190,99 +2277,62 @@ def remove_response_handler(handler)
21902277
21912278 @@debug = false
21922279
2193- # :call-seq:
2194- # Net::IMAP.new(host, options = {})
2195- #
2196- # Creates a new Net::IMAP object and connects it to the specified
2197- # +host+.
2198- #
2199- # +options+ is an option hash, each key of which is a symbol.
2200- #
2201- # The available options are:
2202- #
2203- # port:: Port number (default value is 143 for imap, or 993 for imaps)
2204- # ssl:: If +options[:ssl]+ is true, then an attempt will be made
2205- # to use SSL (now TLS) to connect to the server.
2206- # If +options[:ssl]+ is a hash, it's passed to
2207- # OpenSSL::SSL::SSLContext#set_params as parameters.
2208- # open_timeout:: Seconds to wait until a connection is opened
2209- # idle_response_timeout:: Seconds to wait until an IDLE response is received
2210- #
2211- # The most common errors are:
2212- #
2213- # Errno::ECONNREFUSED:: Connection refused by +host+ or an intervening
2214- # firewall.
2215- # Errno::ETIMEDOUT:: Connection timed out (possibly due to packets
2216- # being dropped by an intervening firewall).
2217- # Errno::ENETUNREACH:: There is no route to that network.
2218- # SocketError:: Hostname not known or other socket error.
2219- # Net::IMAP::ByeResponseError:: The connected to the host was successful, but
2220- # it immediately said goodbye.
2221- def initialize ( host , port_or_options = { } ,
2222- usessl = false , certs = nil , verify = true )
2223- super ( )
2224- @host = host
2225- begin
2226- options = port_or_options . to_hash
2227- rescue NoMethodError
2228- # for backward compatibility
2229- options = { }
2230- options [ :port ] = port_or_options
2231- if usessl
2232- options [ :ssl ] = create_ssl_params ( certs , verify )
2280+ def default_ssl_and_port ( ssl , port )
2281+ if ssl . nil? && port
2282+ ssl = true if port == SSL_PORT || /\A imaps\z /i === port
2283+ ssl = false if port == PORT
2284+ elsif port . nil? && !ssl . nil?
2285+ port = ssl ? SSL_PORT : PORT
2286+ end
2287+ if ssl . nil? && port . nil?
2288+ ssl = self . class . default_ssl . dup . freeze
2289+ port = ssl ? SSL_PORT : PORT
2290+ if ssl . nil?
2291+ warn "A future version of Net::IMAP.default_ssl " \
2292+ "will default to 'true', for secure connections by default. " \
2293+ "Use 'Net::IMAP.new(host, ssl: false)' or set " \
2294+ "Net::IMAP.default_ssl = false' to silence this warning."
22332295 end
22342296 end
2235- @port = options [ :port ] || ( options [ :ssl ] ? SSL_PORT : PORT )
2236- @tag_prefix = "RUBY"
2237- @tagno = 0
2238- @utf8_strings = false
2239- @open_timeout = options [ :open_timeout ] || 30
2240- @idle_response_timeout = options [ :idle_response_timeout ] || 5
2241- @parser = ResponseParser . new
2297+ ssl &&= ssl . respond_to? ( :to_hash ) ? ssl . to_hash : { }
2298+ [ ssl , port ]
2299+ end
2300+
2301+ def start_connection
22422302 @sock = tcp_socket ( @host , @port )
22432303 begin
2244- if options [ :ssl ]
2245- start_tls_session ( options [ :ssl ] )
2246- @usessl = true
2247- else
2248- @usessl = false
2249- end
2250- @responses = Hash . new { |h , k | h [ k ] = [ ] }
2251- @tagged_responses = { }
2252- @response_handlers = [ ]
2253- @tagged_response_arrival = new_cond
2254- @continued_command_tag = nil
2255- @continuation_request_arrival = new_cond
2256- @continuation_request_exception = nil
2257- @idle_done_cond = nil
2258- @logout_command_tag = nil
2259- @debug_output_bol = true
2260- @exception = nil
2261-
2304+ start_tls_session ( @ssl ) if @ssl
22622305 @greeting = get_response
2263- if @greeting . nil?
2264- raise Error , "connection closed"
2265- end
2266- record_untagged_response_code @greeting
2267- @capabilities = capabilities_from_resp_code @greeting
2268- if @greeting . name == "BYE"
2269- raise ByeResponseError , @greeting
2270- end
2271-
2272- @client_thread = Thread . current
2273- @receiver_thread = Thread . start {
2274- begin
2275- receive_responses
2276- rescue Exception
2277- end
2278- }
2279- @receiver_thread_terminating = false
2306+ handle_server_greeting
2307+ @receiver_thread = start_receiver_thread
22802308 rescue Exception
22812309 @sock . close
22822310 raise
22832311 end
22842312 end
22852313
2314+ def handle_server_greeting
2315+ if @greeting . nil?
2316+ raise Error , "connection closed"
2317+ end
2318+ record_untagged_response_code ( @greeting )
2319+ @capabilities = capabilities_from_resp_code @greeting
2320+ if @greeting . name == "BYE"
2321+ raise ByeResponseError , @greeting
2322+ end
2323+ end
2324+
2325+ def start_receiver_thread
2326+ Thread . start do
2327+ receive_responses
2328+ rescue Exception
2329+ # don't exit the thread with an exception
2330+ end
2331+ rescue Exception
2332+ @sock . close
2333+ raise
2334+ end
2335+
22862336 def tcp_socket ( host , port )
22872337 s = Socket . tcp ( host , port , :connect_timeout => @open_timeout )
22882338 s . setsockopt ( :SOL_SOCKET , :SO_KEEPALIVE , true )
@@ -2569,35 +2619,13 @@ def normalize_searching_criteria(keys)
25692619 end
25702620 end
25712621
2572- def create_ssl_params ( certs = nil , verify = true )
2573- params = { }
2574- if certs
2575- if File . file? ( certs )
2576- params [ :ca_file ] = certs
2577- elsif File . directory? ( certs )
2578- params [ :ca_path ] = certs
2579- end
2580- end
2581- if verify
2582- params [ :verify_mode ] = VERIFY_PEER
2583- else
2584- params [ :verify_mode ] = VERIFY_NONE
2585- end
2586- return params
2587- end
2588-
25892622 def start_tls_session ( params = { } )
25902623 unless defined? ( OpenSSL ::SSL )
25912624 raise "SSL extension not installed"
25922625 end
25932626 if @sock . kind_of? ( OpenSSL ::SSL ::SSLSocket )
25942627 raise RuntimeError , "already using SSL"
25952628 end
2596- begin
2597- params = params . to_hash
2598- rescue NoMethodError
2599- params = { }
2600- end
26012629 context = SSLContext . new
26022630 context . set_params ( params )
26032631 if defined? ( VerifyCallbackProc )
@@ -2632,3 +2660,6 @@ def self.saslprep(string, **opts)
26322660require_relative "imap/response_data"
26332661require_relative "imap/response_parser"
26342662require_relative "imap/authenticators"
2663+
2664+ require_relative "imap/deprecated_client_options"
2665+ Net ::IMAP . prepend Net ::IMAP ::DeprecatedClientOptions
0 commit comments