@@ -720,11 +720,110 @@ class << self
720
720
alias default_imap_port default_port
721
721
alias default_imaps_port default_tls_port
722
722
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
723
821
end
724
822
725
823
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
728
827
end
729
828
730
829
# Disconnects from the server.
@@ -795,7 +894,7 @@ def capabilities
795
894
# servers will drop all <tt>AUTH=</tt> mechanisms from #capabilities after
796
895
# the connection has authenticated.
797
896
#
798
- # imap = Net::IMAP.new(hostname, ssl : false)
897
+ # imap = Net::IMAP.new(hostname, tls : false)
799
898
# imap.capabilities # => ["IMAP4REV1", "LOGINDISABLED"]
800
899
# imap.auth_mechanisms # => []
801
900
#
@@ -970,15 +1069,9 @@ def logout
970
1069
# Server capabilities may change after #starttls, #login, and #authenticate.
971
1070
# Cached #capabilities will be cleared when this method completes.
972
1071
#
973
- def starttls ( options = { } , verify = true )
1072
+ def starttls ( ** options )
974
1073
send_command ( "STARTTLS" ) do |resp |
975
1074
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
982
1075
clear_cached_capabilities
983
1076
clear_responses
984
1077
start_tls_session ( options )
@@ -2213,99 +2306,62 @@ def remove_response_handler(handler)
2213
2306
2214
2307
@@debug = false
2215
2308
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."
2256
2324
end
2257
2325
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
2265
2331
@sock = tcp_socket ( @host , @port )
2266
2332
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
2285
2334
@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
2303
2337
rescue Exception
2304
2338
@sock . close
2305
2339
raise
2306
2340
end
2307
2341
end
2308
2342
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
+
2309
2365
def tcp_socket ( host , port )
2310
2366
s = Socket . tcp ( host , port , :connect_timeout => @open_timeout )
2311
2367
s . setsockopt ( :SOL_SOCKET , :SO_KEEPALIVE , true )
@@ -2592,34 +2648,12 @@ def normalize_searching_criteria(keys)
2592
2648
end
2593
2649
end
2594
2650
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
-
2612
2651
def start_tls_session ( params = { } )
2613
2652
unless defined? ( OpenSSL ::SSL )
2614
- raise "SSL extension not installed"
2653
+ raise "OpenSSL extension not installed"
2615
2654
end
2616
2655
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"
2623
2657
end
2624
2658
context = SSLContext . new
2625
2659
context . set_params ( params )
@@ -2655,3 +2689,6 @@ def self.saslprep(string, **opts)
2655
2689
require_relative "imap/response_data"
2656
2690
require_relative "imap/response_parser"
2657
2691
require_relative "imap/authenticators"
2692
+
2693
+ require_relative "imap/deprecated_client_options"
2694
+ Net ::IMAP . prepend Net ::IMAP ::DeprecatedClientOptions
0 commit comments