@@ -168,7 +168,7 @@ class SMTPUnsupportedCommand < ProtocolError
168
168
# user: 'Your Account', secret: 'Your Password', authtype: :cram_md5)
169
169
#
170
170
class SMTP < Protocol
171
- VERSION = "0.1 .0"
171
+ VERSION = "0.2 .0"
172
172
173
173
Revision = %q$Revision$ . split [ 1 ]
174
174
@@ -191,8 +191,13 @@ class << self
191
191
alias default_ssl_port default_tls_port
192
192
end
193
193
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
196
201
end
197
202
198
203
#
@@ -218,8 +223,9 @@ def initialize(address, port = nil)
218
223
@error_occurred = false
219
224
@debug_output = nil
220
225
@tls = false
221
- @starttls = false
222
- @ssl_context = nil
226
+ @starttls = :auto
227
+ @ssl_context_tls = nil
228
+ @ssl_context_starttls = nil
223
229
end
224
230
225
231
# Provide human-readable stringification of class state.
@@ -294,11 +300,11 @@ def tls?
294
300
# Enables SMTP/TLS (SMTPS: SMTP over direct TLS connection) for
295
301
# this object. Must be called before the connection is established
296
302
# 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 )
298
304
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
300
306
@tls = true
301
- @ssl_context = context
307
+ @ssl_context_tls = context
302
308
end
303
309
304
310
alias enable_ssl enable_tls
@@ -307,7 +313,7 @@ def enable_tls(context = SMTP.default_ssl_context)
307
313
# connection is established to have any effect.
308
314
def disable_tls
309
315
@tls = false
310
- @ssl_context = nil
316
+ @ssl_context_tls = nil
311
317
end
312
318
313
319
alias disable_ssl disable_tls
@@ -331,27 +337,27 @@ def starttls_auto?
331
337
332
338
# Enables SMTP/TLS (STARTTLS) for this object.
333
339
# +context+ is a OpenSSL::SSL::SSLContext object.
334
- def enable_starttls ( context = SMTP . default_ssl_context )
340
+ def enable_starttls ( context = nil )
335
341
raise 'openssl library not installed' unless defined? ( OpenSSL )
336
342
raise ArgumentError , "SMTPS and STARTTLS is exclusive" if @tls
337
343
@starttls = :always
338
- @ssl_context = context
344
+ @ssl_context_starttls = context
339
345
end
340
346
341
347
# Enables SMTP/TLS (STARTTLS) for this object if server accepts.
342
348
# +context+ is a OpenSSL::SSL::SSLContext object.
343
- def enable_starttls_auto ( context = SMTP . default_ssl_context )
349
+ def enable_starttls_auto ( context = nil )
344
350
raise 'openssl library not installed' unless defined? ( OpenSSL )
345
351
raise ArgumentError , "SMTPS and STARTTLS is exclusive" if @tls
346
352
@starttls = :auto
347
- @ssl_context = context
353
+ @ssl_context_starttls = context
348
354
end
349
355
350
356
# Disables SMTP/TLS (STARTTLS) for this object. Must be called
351
357
# before the connection is established to have any effect.
352
358
def disable_starttls
353
359
@starttls = false
354
- @ssl_context = nil
360
+ @ssl_context_starttls = nil
355
361
end
356
362
357
363
# The address of the SMTP server to connect to.
@@ -403,14 +409,14 @@ def debug_output=(arg)
403
409
404
410
#
405
411
# :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| ... }
407
413
# start(address, port = nil, helo = 'localhost', user = nil, secret = nil, authtype = nil) { |smtp| ... }
408
414
#
409
415
# Creates a new Net::SMTP object and connects to the server.
410
416
#
411
417
# This method is equivalent to:
412
418
#
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 )
414
420
#
415
421
# === Example
416
422
#
@@ -440,6 +446,9 @@ def debug_output=(arg)
440
446
# or other authentication token; and +authtype+ is the authentication
441
447
# type, one of :plain, :login, or :cram_md5. See the discussion of
442
448
# 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+.
443
452
#
444
453
# === Errors
445
454
#
@@ -456,13 +465,14 @@ def debug_output=(arg)
456
465
#
457
466
def SMTP . start ( address , port = nil , *args , helo : nil ,
458
467
user : nil , secret : nil , password : nil , authtype : nil ,
468
+ tls_verify : true , tls_hostname : nil ,
459
469
&block )
460
470
raise ArgumentError , "wrong number of arguments (given #{ args . size + 2 } , expected 1..6)" if args . size > 4
461
471
helo ||= args [ 0 ] || 'localhost'
462
472
user ||= args [ 1 ]
463
473
secret ||= password || args [ 2 ]
464
474
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 )
466
476
end
467
477
468
478
# +true+ if the SMTP session has been started.
@@ -472,7 +482,7 @@ def started?
472
482
473
483
#
474
484
# :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| ... }
476
486
# start(helo = 'localhost', user = nil, secret = nil, authtype = nil) { |smtp| ... }
477
487
#
478
488
# Opens a TCP connection and starts the SMTP session.
@@ -487,6 +497,9 @@ def started?
487
497
# the type of authentication to attempt; it must be one of
488
498
# :login, :plain, and :cram_md5. See the notes on SMTP Authentication
489
499
# 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+.
490
503
#
491
504
# === Block Usage
492
505
#
@@ -526,12 +539,19 @@ def started?
526
539
# * IOError
527
540
#
528
541
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 )
530
543
raise ArgumentError , "wrong number of arguments (given #{ args . size } , expected 0..4)" if args . size > 4
531
544
helo ||= args [ 0 ] || 'localhost'
532
545
user ||= args [ 1 ]
533
546
secret ||= password || args [ 2 ]
534
547
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
535
555
if block_given?
536
556
begin
537
557
do_start helo , user , secret , authtype
@@ -568,16 +588,16 @@ def do_start(helo_domain, user, secret, authtype)
568
588
tcp_socket ( @address , @port )
569
589
end
570
590
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 )
572
592
check_response critical { recv_response ( ) }
573
593
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? ) )
575
595
unless capable_starttls?
576
596
raise SMTPUnsupportedCommand ,
577
597
"STARTTLS is not supported on this server"
578
598
end
579
599
starttls
580
- @socket = new_internet_message_io ( tlsconnect ( s ) )
600
+ @socket = new_internet_message_io ( tlsconnect ( s , @ssl_context_starttls ) )
581
601
# helo response may be different after STARTTLS
582
602
do_helo helo_domain
583
603
end
@@ -595,15 +615,15 @@ def ssl_socket(socket, context)
595
615
OpenSSL ::SSL ::SSLSocket . new socket , context
596
616
end
597
617
598
- def tlsconnect ( s )
618
+ def tlsconnect ( s , context )
599
619
verified = false
600
- s = ssl_socket ( s , @ssl_context )
620
+ s = ssl_socket ( s , context )
601
621
logging "TLS connection started"
602
622
s . sync_close = true
603
- s . hostname = @address if s . respond_to? :hostname=
623
+ s . hostname = @tls_hostname || @ address if s . respond_to? :hostname=
604
624
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)
607
627
end
608
628
verified = true
609
629
s
0 commit comments