@@ -720,11 +720,110 @@ 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+ # create the connection
819+ @sock = nil
820+ start_connection
723821 end
724822
725823 def client_thread # :nodoc:
726- warn "Net::IMAP#client_thread is deprecated and will be removed soon."
727- @client_thread
824+ warn "Net::IMAP#client_thread is deprecated and always returns the " \
825+ "caller's current thread."
826+ Thread . current
728827 end
729828
730829 # Disconnects from the server.
@@ -795,7 +894,7 @@ def capabilities
795894 # servers will drop all <tt>AUTH=</tt> mechanisms from #capabilities after
796895 # the connection has authenticated.
797896 #
798- # imap = Net::IMAP.new(hostname, ssl : false)
897+ # imap = Net::IMAP.new(hostname, tls : false)
799898 # imap.capabilities # => ["IMAP4REV1", "LOGINDISABLED"]
800899 # imap.auth_mechanisms # => []
801900 #
@@ -970,15 +1069,9 @@ def logout
9701069 # Server capabilities may change after #starttls, #login, and #authenticate.
9711070 # Cached #capabilities will be cleared when this method completes.
9721071 #
973- def starttls ( options = { } , verify = true )
1072+ def starttls ( ** options )
9741073 send_command ( "STARTTLS" ) do |resp |
9751074 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
9821075 clear_cached_capabilities
9831076 clear_responses
9841077 start_tls_session ( options )
@@ -2213,99 +2306,62 @@ def remove_response_handler(handler)
22132306
22142307 @@debug = false
22152308
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 )
2309+ def default_tls_and_port ( tls , port )
2310+ if tls . nil? && port
2311+ tls = true if port == SSL_PORT || /\A imaps\z /i === port
2312+ tls = false if port == PORT
2313+ elsif port . nil? && !tls . nil?
2314+ port = tls ? SSL_PORT : PORT
2315+ end
2316+ if tls . nil? && port . nil?
2317+ tls = self . class . default_tls . dup . freeze
2318+ port = tls ? SSL_PORT : PORT
2319+ if tls . nil?
2320+ warn "A future version of Net::IMAP.default_tls " \
2321+ "will default to 'true', for secure connections by default. " \
2322+ "Use 'Net::IMAP.new(host, tls: false)' or set " \
2323+ "Net::IMAP.default_tls = false' to silence this warning."
22562324 end
22572325 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
2326+ tls &&= tls . respond_to? ( :to_hash ) ? tls . to_hash : { }
2327+ [ tls , port ]
2328+ end
2329+
2330+ def start_connection
22652331 @sock = tcp_socket ( @host , @port )
22662332 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-
2333+ start_tls_session ( @tls ) if @tls
22852334 @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
2335+ handle_server_greeting
2336+ @receiver_thread = start_receiver_thread
23032337 rescue Exception
23042338 @sock . close
23052339 raise
23062340 end
23072341 end
23082342
2343+ def handle_server_greeting
2344+ if @greeting . nil?
2345+ raise Error , "connection closed"
2346+ end
2347+ record_untagged_response_code ( @greeting )
2348+ @capabilities = capabilities_from_resp_code @greeting
2349+ if @greeting . name == "BYE"
2350+ raise ByeResponseError , @greeting
2351+ end
2352+ end
2353+
2354+ def start_receiver_thread
2355+ Thread . start do
2356+ receive_responses
2357+ rescue Exception
2358+ # don't exit the thread with an exception
2359+ end
2360+ rescue Exception
2361+ @sock . close
2362+ raise
2363+ end
2364+
23092365 def tcp_socket ( host , port )
23102366 s = Socket . tcp ( host , port , :connect_timeout => @open_timeout )
23112367 s . setsockopt ( :SOL_SOCKET , :SO_KEEPALIVE , true )
@@ -2592,34 +2648,12 @@ def normalize_searching_criteria(keys)
25922648 end
25932649 end
25942650
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-
26122651 def start_tls_session ( params = { } )
26132652 unless defined? ( OpenSSL ::SSL )
2614- raise "SSL extension not installed"
2653+ raise "OpenSSL extension not installed"
26152654 end
26162655 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 = { }
2656+ raise RuntimeError , "already using TLS"
26232657 end
26242658 context = SSLContext . new
26252659 context . set_params ( params )
@@ -2655,3 +2689,6 @@ def self.saslprep(string, **opts)
26552689require_relative "imap/response_data"
26562690require_relative "imap/response_parser"
26572691require_relative "imap/authenticators"
2692+
2693+ require_relative "imap/deprecated_client_options"
2694+ Net ::IMAP . prepend Net ::IMAP ::DeprecatedClientOptions
0 commit comments