@@ -168,7 +168,7 @@ class SMTPUnsupportedCommand < ProtocolError
168168 # user: 'Your Account', secret: 'Your Password', authtype: :cram_md5)
169169 #
170170 class SMTP < Protocol
171- VERSION = "0.1 .0"
171+ VERSION = "0.2 .0"
172172
173173 Revision = %q$Revision$ . split [ 1 ]
174174
@@ -191,8 +191,13 @@ class << self
191191 alias default_ssl_port default_tls_port
192192 end
193193
194- def SMTP . default_ssl_context
195- OpenSSL ::SSL ::SSLContext . new
194+ def SMTP . default_ssl_context ( verify_peer = true )
195+ context = OpenSSL ::SSL ::SSLContext . new
196+ context . verify_mode = verify_peer ? OpenSSL ::SSL ::VERIFY_PEER : OpenSSL ::SSL ::VERIFY_NONE
197+ store = OpenSSL ::X509 ::Store . new
198+ store . set_default_paths
199+ context . cert_store = store
200+ context
196201 end
197202
198203 #
@@ -218,8 +223,9 @@ def initialize(address, port = nil)
218223 @error_occurred = false
219224 @debug_output = nil
220225 @tls = false
221- @starttls = false
222- @ssl_context = nil
226+ @starttls = :auto
227+ @ssl_context_tls = nil
228+ @ssl_context_starttls = nil
223229 end
224230
225231 # Provide human-readable stringification of class state.
@@ -294,11 +300,11 @@ def tls?
294300 # Enables SMTP/TLS (SMTPS: SMTP over direct TLS connection) for
295301 # this object. Must be called before the connection is established
296302 # to have any effect. +context+ is a OpenSSL::SSL::SSLContext object.
297- def enable_tls ( context = SMTP . default_ssl_context )
303+ def enable_tls ( context = nil )
298304 raise 'openssl library not installed' unless defined? ( OpenSSL )
299- raise ArgumentError , "SMTPS and STARTTLS is exclusive" if @starttls
305+ raise ArgumentError , "SMTPS and STARTTLS is exclusive" if @starttls == :always
300306 @tls = true
301- @ssl_context = context
307+ @ssl_context_tls = context
302308 end
303309
304310 alias enable_ssl enable_tls
@@ -307,7 +313,7 @@ def enable_tls(context = SMTP.default_ssl_context)
307313 # connection is established to have any effect.
308314 def disable_tls
309315 @tls = false
310- @ssl_context = nil
316+ @ssl_context_tls = nil
311317 end
312318
313319 alias disable_ssl disable_tls
@@ -331,27 +337,27 @@ def starttls_auto?
331337
332338 # Enables SMTP/TLS (STARTTLS) for this object.
333339 # +context+ is a OpenSSL::SSL::SSLContext object.
334- def enable_starttls ( context = SMTP . default_ssl_context )
340+ def enable_starttls ( context = nil )
335341 raise 'openssl library not installed' unless defined? ( OpenSSL )
336342 raise ArgumentError , "SMTPS and STARTTLS is exclusive" if @tls
337343 @starttls = :always
338- @ssl_context = context
344+ @ssl_context_starttls = context
339345 end
340346
341347 # Enables SMTP/TLS (STARTTLS) for this object if server accepts.
342348 # +context+ is a OpenSSL::SSL::SSLContext object.
343- def enable_starttls_auto ( context = SMTP . default_ssl_context )
349+ def enable_starttls_auto ( context = nil )
344350 raise 'openssl library not installed' unless defined? ( OpenSSL )
345351 raise ArgumentError , "SMTPS and STARTTLS is exclusive" if @tls
346352 @starttls = :auto
347- @ssl_context = context
353+ @ssl_context_starttls = context
348354 end
349355
350356 # Disables SMTP/TLS (STARTTLS) for this object. Must be called
351357 # before the connection is established to have any effect.
352358 def disable_starttls
353359 @starttls = false
354- @ssl_context = nil
360+ @ssl_context_starttls = nil
355361 end
356362
357363 # The address of the SMTP server to connect to.
@@ -403,14 +409,14 @@ def debug_output=(arg)
403409
404410 #
405411 # :call-seq:
406- # start(address, port = nil, helo: 'localhost', user: nil, secret: nil, authtype: nil) { |smtp| ... }
412+ # start(address, port = nil, helo: 'localhost', user: nil, secret: nil, authtype: nil, tls_verify: true, tls_hostname: nil ) { |smtp| ... }
407413 # start(address, port = nil, helo = 'localhost', user = nil, secret = nil, authtype = nil) { |smtp| ... }
408414 #
409415 # Creates a new Net::SMTP object and connects to the server.
410416 #
411417 # This method is equivalent to:
412418 #
413- # Net::SMTP.new(address, port).start(helo: helo_domain, user: account, secret: password, authtype: authtype)
419+ # Net::SMTP.new(address, port).start(helo: helo_domain, user: account, secret: password, authtype: authtype, tls_verify: flag, tls_hostname: hostname )
414420 #
415421 # === Example
416422 #
@@ -440,6 +446,9 @@ def debug_output=(arg)
440446 # or other authentication token; and +authtype+ is the authentication
441447 # type, one of :plain, :login, or :cram_md5. See the discussion of
442448 # SMTP Authentication in the overview notes.
449+ # If +tls_verify+ is true, verify the server's certificate. The default is true.
450+ # If the hostname in the server certificate is different from +address+,
451+ # it can be specified with +tls_hostname+.
443452 #
444453 # === Errors
445454 #
@@ -456,13 +465,14 @@ def debug_output=(arg)
456465 #
457466 def SMTP . start ( address , port = nil , *args , helo : nil ,
458467 user : nil , secret : nil , password : nil , authtype : nil ,
468+ tls_verify : true , tls_hostname : nil ,
459469 &block )
460470 raise ArgumentError , "wrong number of arguments (given #{ args . size + 2 } , expected 1..6)" if args . size > 4
461471 helo ||= args [ 0 ] || 'localhost'
462472 user ||= args [ 1 ]
463473 secret ||= password || args [ 2 ]
464474 authtype ||= args [ 3 ]
465- new ( address , port ) . start ( helo : helo , user : user , secret : secret , authtype : authtype , &block )
475+ new ( address , port ) . start ( helo : helo , user : user , secret : secret , authtype : authtype , tls_verify : tls_verify , tls_hostname : tls_hostname , &block )
466476 end
467477
468478 # +true+ if the SMTP session has been started.
@@ -472,7 +482,7 @@ def started?
472482
473483 #
474484 # :call-seq:
475- # start(helo: 'localhost', user: nil, secret: nil, authtype: nil) { |smtp| ... }
485+ # start(helo: 'localhost', user: nil, secret: nil, authtype: nil, tls_verify: true, tls_hostname: nil ) { |smtp| ... }
476486 # start(helo = 'localhost', user = nil, secret = nil, authtype = nil) { |smtp| ... }
477487 #
478488 # Opens a TCP connection and starts the SMTP session.
@@ -487,6 +497,9 @@ def started?
487497 # the type of authentication to attempt; it must be one of
488498 # :login, :plain, and :cram_md5. See the notes on SMTP Authentication
489499 # in the overview.
500+ # If +tls_verify+ is true, verify the server's certificate. The default is true.
501+ # If the hostname in the server certificate is different from +address+,
502+ # it can be specified with +tls_hostname+.
490503 #
491504 # === Block Usage
492505 #
@@ -526,12 +539,19 @@ def started?
526539 # * IOError
527540 #
528541 def start ( *args , helo : nil ,
529- user : nil , secret : nil , password : nil , authtype : nil )
542+ user : nil , secret : nil , password : nil , authtype : nil , tls_verify : true , tls_hostname : nil )
530543 raise ArgumentError , "wrong number of arguments (given #{ args . size } , expected 0..4)" if args . size > 4
531544 helo ||= args [ 0 ] || 'localhost'
532545 user ||= args [ 1 ]
533546 secret ||= password || args [ 2 ]
534547 authtype ||= args [ 3 ]
548+ if @tls && @ssl_context_tls . nil?
549+ @ssl_context_tls = SMTP . default_ssl_context ( tls_verify )
550+ end
551+ if @starttls && @ssl_context_starttls . nil?
552+ @ssl_context_starttls = SMTP . default_ssl_context ( tls_verify )
553+ end
554+ @tls_hostname = tls_hostname
535555 if block_given?
536556 begin
537557 do_start helo , user , secret , authtype
@@ -568,16 +588,16 @@ def do_start(helo_domain, user, secret, authtype)
568588 tcp_socket ( @address , @port )
569589 end
570590 logging "Connection opened: #{ @address } :#{ @port } "
571- @socket = new_internet_message_io ( tls? ? tlsconnect ( s ) : s )
591+ @socket = new_internet_message_io ( tls? ? tlsconnect ( s , @ssl_context_tls ) : s )
572592 check_response critical { recv_response ( ) }
573593 do_helo helo_domain
574- if starttls_always? or ( capable_starttls? and starttls_auto? )
594+ if ! tls? and ( starttls_always? or ( capable_starttls? and starttls_auto? ) )
575595 unless capable_starttls?
576596 raise SMTPUnsupportedCommand ,
577597 "STARTTLS is not supported on this server"
578598 end
579599 starttls
580- @socket = new_internet_message_io ( tlsconnect ( s ) )
600+ @socket = new_internet_message_io ( tlsconnect ( s , @ssl_context_starttls ) )
581601 # helo response may be different after STARTTLS
582602 do_helo helo_domain
583603 end
@@ -595,15 +615,15 @@ def ssl_socket(socket, context)
595615 OpenSSL ::SSL ::SSLSocket . new socket , context
596616 end
597617
598- def tlsconnect ( s )
618+ def tlsconnect ( s , context )
599619 verified = false
600- s = ssl_socket ( s , @ssl_context )
620+ s = ssl_socket ( s , context )
601621 logging "TLS connection started"
602622 s . sync_close = true
603- s . hostname = @address if s . respond_to? :hostname=
623+ s . hostname = @tls_hostname || @ address if s . respond_to? :hostname=
604624 ssl_socket_connect ( s , @open_timeout )
605- if @ssl_context . verify_mode && @ssl_context . verify_mode != OpenSSL ::SSL ::VERIFY_NONE
606- s . post_connection_check ( @address )
625+ if context . verify_mode && context . verify_mode != OpenSSL ::SSL ::VERIFY_NONE
626+ s . post_connection_check ( @tls_hostname || @ address)
607627 end
608628 verified = true
609629 s
0 commit comments