@@ -720,6 +720,107 @@ class << self
720720 alias default_imap_port default_port
721721 alias default_imaps_port default_tls_port
722722 alias default_ssl_port default_tls_port
723+
724+ # The default value for the +tls+ option of ::new, when +port+ is
725+ # unspecified or non-standard.
726+ #
727+ # *Note*: A future release of Net::IMAP will set the default to +true+, as
728+ # per RFC7525[https://tools.ietf.org/html/rfc7525],
729+ # RFC7817[https://tools.ietf.org/html/rfc7817], and
730+ # RFC8314[https://tools.ietf.org/html/rfc8314].
731+ #
732+ # Set to +true+ for the secure default without warnings. Set to
733+ # +false+ to globally silence warnings and use insecure defaults.
734+ attr_accessor :default_tls
735+ alias default_ssl default_tls
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+ # [tls]
746+ # When +true+, the connection will use TLS with the default params set by
747+ # {OpenSSL::SSL::SSLContext#set_params}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#method-i-set_params].
748+ # Assign a hash to override TLS params—the keys are assignment methods on
749+ # SSLContext[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html].
750+ #
751+ # When <tt>port: 993</tt>, +tls+ defaults to +true+.
752+ # When <tt>port: 143</tt>, +tls+ defaults to +false+.
753+ # When port is unspecified or non-standard, +tls+ defaults to
754+ # ::default_tls. When ::default_tls is also +nil+, a warning is printed
755+ # and the connection does _not_ use TLS.
756+ #
757+ # When +nil+ or unassigned a default value is assigned: the default is
758+ # +true+ if <tt>port: 993</tt>, +false+ if <tt>port: 143</tt>, and
759+ # ::default_tls when +port+ is unspecified or non-standard. When
760+ # ::default_tls is +nil+, a back
761+ #
762+ # [open_timeout]
763+ # Seconds to wait until a connection is opened
764+ # [idle_response_timeout]
765+ # Seconds to wait until an IDLE response is received
766+ #
767+ # The most common errors are:
768+ #
769+ # Errno::ECONNREFUSED:: Connection refused by +host+ or an intervening
770+ # firewall.
771+ # Errno::ETIMEDOUT:: Connection timed out (possibly due to packets
772+ # being dropped by an intervening firewall).
773+ # Errno::ENETUNREACH:: There is no route to that network.
774+ # SocketError:: Hostname not known or other socket error.
775+ # Net::IMAP::ByeResponseError:: Connected to the host successfully, but
776+ # it immediately said goodbye.
777+ def initialize ( host ,
778+ port : nil ,
779+ tls : nil ,
780+ open_timeout : 30 ,
781+ idle_response_timeout : 5 )
782+ # Basic configuration
783+ @host = host
784+ @tls , @port = default_tls_and_port ( tls , port )
785+ @open_timeout = Integer ( open_timeout )
786+ @idle_response_timeout = Integer ( idle_response_timeout )
787+
788+ # Basic Client state
789+ super ( ) # Mutex and condition vars (MonitorMixin#initialize)
790+ @greeting = nil
791+ @capabilities = nil
792+ @utf8_strings = false # TODO: use @enabled instead
793+ @debug_output_bol = true
794+
795+ # Client Protocol Reciever
796+ @parser = ResponseParser . new
797+ @receiver_thread = nil
798+ @receiver_thread_terminating = false
799+ @exception = nil
800+
801+ # Client Protocol Sender
802+ @tag_prefix = "RUBY"
803+ @tagno = 0
804+
805+ # Response handlers
806+ @continuation_request_arrival = new_cond
807+ @continuation_request_exception = nil
808+ @tagged_response_arrival = new_cond
809+ @tagged_responses = { }
810+ @response_handlers = [ ]
811+ @responses = Hash . new { |h , k | h [ k ] = [ ] }
812+
813+ # Command execution state
814+ @logout_command_tag = nil
815+ @continued_command_tag = nil
816+ @idle_done_cond = nil
817+
818+ # DEPRECATED
819+ @client_thread = Thread . current
820+
821+ # create the connection
822+ @sock = nil
823+ start_connection
723824 end
724825
725826 def client_thread # :nodoc:
@@ -795,7 +896,7 @@ def capabilities
795896 # servers will drop all <tt>AUTH=</tt> mechanisms from #capabilities after
796897 # the connection has authenticated.
797898 #
798- # imap = Net::IMAP.new(hostname, ssl : false)
899+ # imap = Net::IMAP.new(hostname, tls : false)
799900 # imap.capabilities # => ["IMAP4REV1", "LOGINDISABLED"]
800901 # imap.auth_mechanisms # => []
801902 #
@@ -970,15 +1071,9 @@ def logout
9701071 # Server capabilities may change after #starttls, #login, and #authenticate.
9711072 # Cached #capabilities will be cleared when this method completes.
9721073 #
973- def starttls ( options = { } , verify = true )
1074+ def starttls ( ** options )
9741075 send_command ( "STARTTLS" ) do |resp |
9751076 if resp . kind_of? ( TaggedResponse ) && resp . name == "OK"
976- begin
977- # for backward compatibility
978- certs = options . to_str
979- options = create_ssl_params ( certs , verify )
980- rescue NoMethodError
981- end
9821077 clear_cached_capabilities
9831078 clear_responses
9841079 start_tls_session ( options )
@@ -2213,99 +2308,62 @@ def remove_response_handler(handler)
22132308
22142309 @@debug = false
22152310
2216- # :call-seq:
2217- # Net::IMAP.new(host, options = {})
2218- #
2219- # Creates a new Net::IMAP object and connects it to the specified
2220- # +host+.
2221- #
2222- # +options+ is an option hash, each key of which is a symbol.
2223- #
2224- # The available options are:
2225- #
2226- # port:: Port number (default value is 143 for imap, or 993 for imaps)
2227- # ssl:: If +options[:ssl]+ is true, then an attempt will be made
2228- # to use SSL (now TLS) to connect to the server.
2229- # If +options[:ssl]+ is a hash, it's passed to
2230- # OpenSSL::SSL::SSLContext#set_params as parameters.
2231- # open_timeout:: Seconds to wait until a connection is opened
2232- # idle_response_timeout:: Seconds to wait until an IDLE response is received
2233- #
2234- # The most common errors are:
2235- #
2236- # Errno::ECONNREFUSED:: Connection refused by +host+ or an intervening
2237- # firewall.
2238- # Errno::ETIMEDOUT:: Connection timed out (possibly due to packets
2239- # being dropped by an intervening firewall).
2240- # Errno::ENETUNREACH:: There is no route to that network.
2241- # SocketError:: Hostname not known or other socket error.
2242- # Net::IMAP::ByeResponseError:: The connected to the host was successful, but
2243- # it immediately said goodbye.
2244- def initialize ( host , port_or_options = { } ,
2245- usessl = false , certs = nil , verify = true )
2246- super ( )
2247- @host = host
2248- begin
2249- options = port_or_options . to_hash
2250- rescue NoMethodError
2251- # for backward compatibility
2252- options = { }
2253- options [ :port ] = port_or_options
2254- if usessl
2255- options [ :ssl ] = create_ssl_params ( certs , verify )
2311+ def default_tls_and_port ( tls , port )
2312+ if tls . nil? && port
2313+ tls = true if port == SSL_PORT || /\A imaps\z /i === port
2314+ tls = false if port == PORT
2315+ elsif port . nil? && !tls . nil?
2316+ port = tls ? SSL_PORT : PORT
2317+ end
2318+ if tls . nil? && port . nil?
2319+ tls = self . class . default_tls . dup . freeze
2320+ port = tls ? SSL_PORT : PORT
2321+ if tls . nil?
2322+ warn "A future version of Net::IMAP.default_tls " \
2323+ "will default to 'true', for secure connections by default. " \
2324+ "Use 'Net::IMAP.new(host, tls: false)' or set " \
2325+ "Net::IMAP.default_tls = false' to silence this warning."
22562326 end
22572327 end
2258- @port = options [ :port ] || ( options [ :ssl ] ? SSL_PORT : PORT )
2259- @tag_prefix = "RUBY"
2260- @tagno = 0
2261- @utf8_strings = false
2262- @open_timeout = options [ :open_timeout ] || 30
2263- @idle_response_timeout = options [ :idle_response_timeout ] || 5
2264- @parser = ResponseParser . new
2328+ tls &&= tls . respond_to? ( :to_hash ) ? tls . to_hash : { }
2329+ [ tls , port ]
2330+ end
2331+
2332+ def start_connection
22652333 @sock = tcp_socket ( @host , @port )
22662334 begin
2267- if options [ :ssl ]
2268- start_tls_session ( options [ :ssl ] )
2269- @usessl = true
2270- else
2271- @usessl = false
2272- end
2273- @responses = Hash . new { |h , k | h [ k ] = [ ] }
2274- @tagged_responses = { }
2275- @response_handlers = [ ]
2276- @tagged_response_arrival = new_cond
2277- @continued_command_tag = nil
2278- @continuation_request_arrival = new_cond
2279- @continuation_request_exception = nil
2280- @idle_done_cond = nil
2281- @logout_command_tag = nil
2282- @debug_output_bol = true
2283- @exception = nil
2284-
2335+ start_tls_session ( @tls ) if @tls
22852336 @greeting = get_response
2286- if @greeting . nil?
2287- raise Error , "connection closed"
2288- end
2289- record_untagged_response_code @greeting
2290- @capabilities = capabilities_from_resp_code @greeting
2291- if @greeting . name == "BYE"
2292- raise ByeResponseError , @greeting
2293- end
2294-
2295- @client_thread = Thread . current
2296- @receiver_thread = Thread . start {
2297- begin
2298- receive_responses
2299- rescue Exception
2300- end
2301- }
2302- @receiver_thread_terminating = false
2337+ handle_server_greeting
2338+ @receiver_thread = start_receiver_thread
23032339 rescue Exception
23042340 @sock . close
23052341 raise
23062342 end
23072343 end
23082344
2345+ def handle_server_greeting
2346+ if @greeting . nil?
2347+ raise Error , "connection closed"
2348+ end
2349+ record_untagged_response_code ( @greeting )
2350+ @capabilities = capabilities_from_resp_code @greeting
2351+ if @greeting . name == "BYE"
2352+ raise ByeResponseError , @greeting
2353+ end
2354+ end
2355+
2356+ def start_receiver_thread
2357+ Thread . start do
2358+ receive_responses
2359+ rescue Exception
2360+ # don't exit the thread with an exception
2361+ end
2362+ rescue Exception
2363+ @sock . close
2364+ raise
2365+ end
2366+
23092367 def tcp_socket ( host , port )
23102368 s = Socket . tcp ( host , port , :connect_timeout => @open_timeout )
23112369 s . setsockopt ( :SOL_SOCKET , :SO_KEEPALIVE , true )
@@ -2592,34 +2650,12 @@ def normalize_searching_criteria(keys)
25922650 end
25932651 end
25942652
2595- def create_ssl_params ( certs = nil , verify = true )
2596- params = { }
2597- if certs
2598- if File . file? ( certs )
2599- params [ :ca_file ] = certs
2600- elsif File . directory? ( certs )
2601- params [ :ca_path ] = certs
2602- end
2603- end
2604- if verify
2605- params [ :verify_mode ] = VERIFY_PEER
2606- else
2607- params [ :verify_mode ] = VERIFY_NONE
2608- end
2609- return params
2610- end
2611-
26122653 def start_tls_session ( params = { } )
26132654 unless defined? ( OpenSSL ::SSL )
2614- raise "SSL extension not installed"
2655+ raise "OpenSSL extension not installed"
26152656 end
26162657 if @sock . kind_of? ( OpenSSL ::SSL ::SSLSocket )
2617- raise RuntimeError , "already using SSL"
2618- end
2619- begin
2620- params = params . to_hash
2621- rescue NoMethodError
2622- params = { }
2658+ raise RuntimeError , "already using TLS"
26232659 end
26242660 context = SSLContext . new
26252661 context . set_params ( params )
@@ -2655,3 +2691,6 @@ def self.saslprep(string, **opts)
26552691require_relative "imap/response_data"
26562692require_relative "imap/response_parser"
26572693require_relative "imap/authenticators"
2694+
2695+ require_relative "imap/deprecated_client_options"
2696+ Net ::IMAP . prepend Net ::IMAP ::DeprecatedClientOptions
0 commit comments