@@ -725,6 +725,107 @@ class << self
725725 alias default_imap_port default_port
726726 alias default_imaps_port default_tls_port
727727 alias default_ssl_port default_tls_port
728+
729+ # The default value for the +tls+ option of ::new, when +port+ is
730+ # unspecified or non-standard.
731+ #
732+ # *Note*: A future release of Net::IMAP will set the default to +true+, as
733+ # per RFC7525[https://tools.ietf.org/html/rfc7525],
734+ # RFC7817[https://tools.ietf.org/html/rfc7817], and
735+ # RFC8314[https://tools.ietf.org/html/rfc8314].
736+ #
737+ # Set to +true+ for the secure default without warnings. Set to
738+ # +false+ to globally silence warnings and use insecure defaults.
739+ attr_accessor :default_tls
740+ alias default_ssl default_tls
741+ end
742+
743+ # Creates a new Net::IMAP object and connects it to the specified
744+ # +host+.
745+ #
746+ # Accepts the following options:
747+ #
748+ # [port]
749+ # Port number (default value is 143 for imap, or 993 for imaps)
750+ # [tls]
751+ # When +true+, the connection will use TLS with the default params set by
752+ # {OpenSSL::SSL::SSLContext#set_params}[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html#method-i-set_params].
753+ # Assign a hash to override TLS params—the keys are assignment methods on
754+ # SSLContext[https://docs.ruby-lang.org/en/master/OpenSSL/SSL/SSLContext.html].
755+ #
756+ # When <tt>port: 993</tt>, +tls+ defaults to +true+.
757+ # When <tt>port: 143</tt>, +tls+ defaults to +false+.
758+ # When port is unspecified or non-standard, +tls+ defaults to
759+ # ::default_tls. When ::default_tls is also +nil+, a warning is printed
760+ # and the connection does _not_ use TLS.
761+ #
762+ # When +nil+ or unassigned a default value is assigned: the default is
763+ # +true+ if <tt>port: 993</tt>, +false+ if <tt>port: 143</tt>, and
764+ # ::default_tls when +port+ is unspecified or non-standard. When
765+ # ::default_tls is +nil+, a back
766+ #
767+ # [open_timeout]
768+ # Seconds to wait until a connection is opened
769+ # [idle_response_timeout]
770+ # Seconds to wait until an IDLE response is received
771+ #
772+ # The most common errors are:
773+ #
774+ # Errno::ECONNREFUSED:: Connection refused by +host+ or an intervening
775+ # firewall.
776+ # Errno::ETIMEDOUT:: Connection timed out (possibly due to packets
777+ # being dropped by an intervening firewall).
778+ # Errno::ENETUNREACH:: There is no route to that network.
779+ # SocketError:: Hostname not known or other socket error.
780+ # Net::IMAP::ByeResponseError:: Connected to the host successfully, but
781+ # it immediately said goodbye.
782+ def initialize ( host ,
783+ port : nil ,
784+ tls : nil ,
785+ open_timeout : 30 ,
786+ idle_response_timeout : 5 )
787+ super ( ) # (MonitorMixin)
788+ @host = host
789+ @tls , @port = default_tls_and_port ( tls , port )
790+ @open_timeout = Integer ( open_timeout )
791+ @idle_response_timeout = Integer ( idle_response_timeout )
792+
793+ # Basic Client state
794+ @tls_verified = false
795+ @greeting = nil
796+ @capabilities = nil
797+ @utf8_strings = false # TODO: use @enabled instead
798+ @debug_output_bol = true
799+
800+ # Client Protocol Reciever
801+ @parser = ResponseParser . new
802+ @receiver_thread = nil
803+ @receiver_thread_terminating = false
804+ @exception = nil
805+
806+ # Client Protocol Sender
807+ @tag_prefix = "RUBY"
808+ @tagno = 0
809+
810+ # Response handlers
811+ @continuation_request_arrival = new_cond
812+ @continuation_request_exception = nil
813+ @tagged_response_arrival = new_cond
814+ @tagged_responses = { }
815+ @response_handlers = [ ]
816+ @responses = Hash . new { |h , k | h [ k ] = [ ] }
817+
818+ # Command execution state
819+ @logout_command_tag = nil
820+ @continued_command_tag = nil
821+ @idle_done_cond = nil
822+
823+ # DEPRECATED
824+ @client_thread = Thread . current
825+
826+ # create the connection
827+ @sock = nil
828+ start_connection
728829 end
729830
730831 def client_thread # :nodoc:
@@ -800,7 +901,7 @@ def capabilities
800901 # servers will drop all <tt>AUTH=</tt> mechanisms from #capabilities after
801902 # the connection has authenticated.
802903 #
803- # imap = Net::IMAP.new(hostname, ssl : false)
904+ # imap = Net::IMAP.new(hostname, tls : false)
804905 # imap.capabilities # => ["IMAP4REV1", "LOGINDISABLED"]
805906 # imap.auth_mechanisms # => []
806907 #
@@ -975,15 +1076,9 @@ def logout
9751076 # Server capabilities may change after #starttls, #login, and #authenticate.
9761077 # Cached #capabilities will be cleared when this method completes.
9771078 #
978- def starttls ( options = { } , verify = true )
1079+ def starttls ( ** options )
9791080 send_command ( "STARTTLS" ) do |resp |
9801081 if resp . kind_of? ( TaggedResponse ) && resp . name == "OK"
981- begin
982- # for backward compatibility
983- certs = options . to_str
984- options = create_ssl_params ( certs , verify )
985- rescue NoMethodError
986- end
9871082 clear_cached_capabilities
9881083 clear_responses
9891084 start_tls_session ( options )
@@ -2218,100 +2313,62 @@ def remove_response_handler(handler)
22182313
22192314 @@debug = false
22202315
2221- # :call-seq:
2222- # Net::IMAP.new(host, options = {})
2223- #
2224- # Creates a new Net::IMAP object and connects it to the specified
2225- # +host+.
2226- #
2227- # +options+ is an option hash, each key of which is a symbol.
2228- #
2229- # The available options are:
2230- #
2231- # port:: Port number (default value is 143 for imap, or 993 for imaps)
2232- # ssl:: If +options[:ssl]+ is true, then an attempt will be made
2233- # to use SSL (now TLS) to connect to the server.
2234- # If +options[:ssl]+ is a hash, it's passed to
2235- # OpenSSL::SSL::SSLContext#set_params as parameters.
2236- # open_timeout:: Seconds to wait until a connection is opened
2237- # idle_response_timeout:: Seconds to wait until an IDLE response is received
2238- #
2239- # The most common errors are:
2240- #
2241- # Errno::ECONNREFUSED:: Connection refused by +host+ or an intervening
2242- # firewall.
2243- # Errno::ETIMEDOUT:: Connection timed out (possibly due to packets
2244- # being dropped by an intervening firewall).
2245- # Errno::ENETUNREACH:: There is no route to that network.
2246- # SocketError:: Hostname not known or other socket error.
2247- # Net::IMAP::ByeResponseError:: The connected to the host was successful, but
2248- # it immediately said goodbye.
2249- def initialize ( host , port_or_options = { } ,
2250- usessl = false , certs = nil , verify = true )
2251- super ( )
2252- @host = host
2253- begin
2254- options = port_or_options . to_hash
2255- rescue NoMethodError
2256- # for backward compatibility
2257- options = { }
2258- options [ :port ] = port_or_options
2259- if usessl
2260- options [ :ssl ] = create_ssl_params ( certs , verify )
2316+ def default_tls_and_port ( tls , port )
2317+ if tls . nil? && port
2318+ tls = true if port == SSL_PORT || /\A imaps\z /i === port
2319+ tls = false if port == PORT
2320+ elsif port . nil? && !tls . nil?
2321+ port = tls ? SSL_PORT : PORT
2322+ end
2323+ if tls . nil? && port . nil?
2324+ tls = self . class . default_tls . dup . freeze
2325+ port = tls ? SSL_PORT : PORT
2326+ if tls . nil?
2327+ warn "A future version of Net::IMAP.default_tls " \
2328+ "will default to 'true', for secure connections by default. " \
2329+ "Use 'Net::IMAP.new(host, tls: false)' or set " \
2330+ "Net::IMAP.default_tls = false' to silence this warning."
22612331 end
22622332 end
2263- @port = options [ :port ] || ( options [ :ssl ] ? SSL_PORT : PORT )
2264- @tag_prefix = "RUBY"
2265- @tagno = 0
2266- @utf8_strings = false
2267- @open_timeout = options [ :open_timeout ] || 30
2268- @idle_response_timeout = options [ :idle_response_timeout ] || 5
2269- @tls_verified = false
2270- @parser = ResponseParser . new
2333+ tls &&= tls . respond_to? ( :to_hash ) ? tls . to_hash : { }
2334+ [ tls , port ]
2335+ end
2336+
2337+ def start_connection
22712338 @sock = tcp_socket ( @host , @port )
22722339 begin
2273- if options [ :ssl ]
2274- start_tls_session ( options [ :ssl ] )
2275- @usessl = true
2276- else
2277- @usessl = false
2278- end
2279- @responses = Hash . new { |h , k | h [ k ] = [ ] }
2280- @tagged_responses = { }
2281- @response_handlers = [ ]
2282- @tagged_response_arrival = new_cond
2283- @continued_command_tag = nil
2284- @continuation_request_arrival = new_cond
2285- @continuation_request_exception = nil
2286- @idle_done_cond = nil
2287- @logout_command_tag = nil
2288- @debug_output_bol = true
2289- @exception = nil
2290-
2340+ start_tls_session ( @tls ) if @tls
22912341 @greeting = get_response
2292- if @greeting . nil?
2293- raise Error , "connection closed"
2294- end
2295- record_untagged_response_code @greeting
2296- @capabilities = capabilities_from_resp_code @greeting
2297- if @greeting . name == "BYE"
2298- raise ByeResponseError , @greeting
2299- end
2300-
2301- @client_thread = Thread . current
2302- @receiver_thread = Thread . start {
2303- begin
2304- receive_responses
2305- rescue Exception
2306- end
2307- }
2308- @receiver_thread_terminating = false
2342+ handle_server_greeting
2343+ @receiver_thread = start_receiver_thread
23092344 rescue Exception
23102345 @sock . close
23112346 raise
23122347 end
23132348 end
23142349
2350+ def handle_server_greeting
2351+ if @greeting . nil?
2352+ raise Error , "connection closed"
2353+ end
2354+ record_untagged_response_code ( @greeting )
2355+ @capabilities = capabilities_from_resp_code @greeting
2356+ if @greeting . name == "BYE"
2357+ raise ByeResponseError , @greeting
2358+ end
2359+ end
2360+
2361+ def start_receiver_thread
2362+ Thread . start do
2363+ receive_responses
2364+ rescue Exception
2365+ # don't exit the thread with an exception
2366+ end
2367+ rescue Exception
2368+ @sock . close
2369+ raise
2370+ end
2371+
23152372 def tcp_socket ( host , port )
23162373 s = Socket . tcp ( host , port , :connect_timeout => @open_timeout )
23172374 s . setsockopt ( :SOL_SOCKET , :SO_KEEPALIVE , true )
@@ -2598,34 +2655,12 @@ def normalize_searching_criteria(keys)
25982655 end
25992656 end
26002657
2601- def create_ssl_params ( certs = nil , verify = true )
2602- params = { }
2603- if certs
2604- if File . file? ( certs )
2605- params [ :ca_file ] = certs
2606- elsif File . directory? ( certs )
2607- params [ :ca_path ] = certs
2608- end
2609- end
2610- if verify
2611- params [ :verify_mode ] = VERIFY_PEER
2612- else
2613- params [ :verify_mode ] = VERIFY_NONE
2614- end
2615- return params
2616- end
2617-
26182658 def start_tls_session ( params = { } )
26192659 unless defined? ( OpenSSL ::SSL )
2620- raise "SSL extension not installed"
2660+ raise "OpenSSL extension not installed"
26212661 end
26222662 if @sock . kind_of? ( OpenSSL ::SSL ::SSLSocket )
2623- raise RuntimeError , "already using SSL"
2624- end
2625- begin
2626- params = params . to_hash
2627- rescue NoMethodError
2628- params = { }
2663+ raise RuntimeError , "already using TLS"
26292664 end
26302665 context = SSLContext . new
26312666 context . set_params ( params )
@@ -2662,3 +2697,6 @@ def self.saslprep(string, **opts)
26622697require_relative "imap/response_data"
26632698require_relative "imap/response_parser"
26642699require_relative "imap/authenticators"
2700+
2701+ require_relative "imap/deprecated_client_options"
2702+ Net ::IMAP . prepend Net ::IMAP ::DeprecatedClientOptions
0 commit comments