* lib/net/smtp.rb: merge SMTP-TLS patch. This patch is contributed by Daniel Hob. [ruby-core:02789]
git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@6190 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
This commit is contained in:
parent
ef36d75472
commit
51ef45c6f3
@ -1,3 +1,8 @@
|
|||||||
|
Wed Apr 21 18:39:46 2004 Minero Aoki <aamine@loveruby.net>
|
||||||
|
|
||||||
|
* lib/net/smtp.rb: merge SMTP-TLS patch. This patch is
|
||||||
|
contributed by Daniel Hob. [ruby-core:02789]
|
||||||
|
|
||||||
Wed Apr 21 18:23:45 2004 Minero Aoki <aamine@loveruby.net>
|
Wed Apr 21 18:23:45 2004 Minero Aoki <aamine@loveruby.net>
|
||||||
|
|
||||||
* lib/net/smtp.rb: change coding style: def m( a ) -> def m(a).
|
* lib/net/smtp.rb: change coding style: def m( a ) -> def m(a).
|
||||||
|
171
lib/net/smtp.rb
171
lib/net/smtp.rb
@ -104,7 +104,8 @@
|
|||||||
# The Net::SMTP class supports three authentication schemes;
|
# The Net::SMTP class supports three authentication schemes;
|
||||||
# PLAIN, LOGIN and CRAM MD5. (SMTP Authentication: [RFC2554])
|
# PLAIN, LOGIN and CRAM MD5. (SMTP Authentication: [RFC2554])
|
||||||
# To use SMTP authentication, pass extra arguments to
|
# To use SMTP authentication, pass extra arguments to
|
||||||
# SMTP.start/SMTP#start.
|
# SMTP.start/SMTP#start. Use in conjunction with STARTTLS to
|
||||||
|
# prevent authentication information passing in the clear.
|
||||||
#
|
#
|
||||||
# # PLAIN
|
# # PLAIN
|
||||||
# Net::SMTP.start('your.smtp.server', 25, 'mail.from,domain',
|
# Net::SMTP.start('your.smtp.server', 25, 'mail.from,domain',
|
||||||
@ -116,10 +117,45 @@
|
|||||||
# # CRAM MD5
|
# # CRAM MD5
|
||||||
# Net::SMTP.start('your.smtp.server', 25, 'mail.from,domain',
|
# Net::SMTP.start('your.smtp.server', 25, 'mail.from,domain',
|
||||||
# 'Your Account', 'Your Password', :cram_md5)
|
# 'Your Account', 'Your Password', :cram_md5)
|
||||||
|
#
|
||||||
|
# === STARTTLS support
|
||||||
|
#
|
||||||
|
# The Net::SMTP class supports STARTTLS.
|
||||||
|
#
|
||||||
|
# # Per Instance STARTTLS
|
||||||
|
# smtp = Net::SMTP.new('smtp.example.com',25)
|
||||||
|
# smtp.enable_tls(verify, certs) if $use_tls #(1)
|
||||||
|
# smtp.start('your host','username','password') { |s|
|
||||||
|
# s.send_message msgstr,
|
||||||
|
# 'your@mail.address',
|
||||||
|
# 'recipient@example.com'
|
||||||
|
# }
|
||||||
|
# smtp.finish
|
||||||
|
#
|
||||||
|
# 1. +verify+ tells the openssl library how to verify the server
|
||||||
|
# certificate. Defaults to OpenSSL::SSL::VERIFY_PEER
|
||||||
|
# +certs+ is a file or directory holding CA certs to use to verify the
|
||||||
|
# server cert; Defaults to nil.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
# # USE STARTTLS for all subsequent instances
|
||||||
|
# Net::SMTP.enable_tls
|
||||||
|
# # We will now use starttls for all connections.
|
||||||
|
# Net::SMTP.start('your.smtp.server', 25, 'mail.from,domain',
|
||||||
|
# 'Your Account', 'Your Password', :plain) {|smtp|
|
||||||
|
# smtp.send_message msgstr,
|
||||||
|
# 'your@mail.address',
|
||||||
|
# 'his_addess@example.com'
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
|
||||||
require 'net/protocol'
|
require 'net/protocol'
|
||||||
require 'digest/md5'
|
require 'digest/md5'
|
||||||
require 'timeout'
|
require 'timeout'
|
||||||
|
begin
|
||||||
|
require "openssl"
|
||||||
|
rescue LoadError
|
||||||
|
end
|
||||||
|
|
||||||
module Net # :nodoc:
|
module Net # :nodoc:
|
||||||
|
|
||||||
@ -162,12 +198,46 @@ module Net # :nodoc:
|
|||||||
class SMTP
|
class SMTP
|
||||||
|
|
||||||
Revision = %q$Revision$.split[1]
|
Revision = %q$Revision$.split[1]
|
||||||
|
|
||||||
# The default SMTP port, port 25.
|
# The default SMTP port, port 25.
|
||||||
def SMTP.default_port
|
def SMTP.default_port
|
||||||
25
|
25
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@use_tls = false
|
||||||
|
@verify = nil
|
||||||
|
@certs = nil
|
||||||
|
|
||||||
|
# Enable SSL for all new instances.
|
||||||
|
# +verify+ is the type of verification to do on the Server Cert; Defaults
|
||||||
|
# to OpenSSL::SSL::VERIFY_PEER.
|
||||||
|
# +certs+ is a file or directory holding CA certs to use to verify the
|
||||||
|
# server cert; Defaults to nil.
|
||||||
|
def SMTP.enable_tls(verify = OpenSSL::SSL::VERIFY_PEER, certs = nil)
|
||||||
|
@use_tls = true
|
||||||
|
@verify = verify
|
||||||
|
@certs = certs
|
||||||
|
end
|
||||||
|
|
||||||
|
# Disable SSL for all new instances.
|
||||||
|
def SMTP.disable_tls
|
||||||
|
@use_tls = nil
|
||||||
|
@verify = nil
|
||||||
|
@certs = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def SMTP.use_tls?
|
||||||
|
@use_tls
|
||||||
|
end
|
||||||
|
|
||||||
|
def SMTP.verify
|
||||||
|
@verify
|
||||||
|
end
|
||||||
|
|
||||||
|
def SMTP.certs
|
||||||
|
@certs
|
||||||
|
end
|
||||||
|
|
||||||
# Creates a new Net::SMTP object. +address+ is the hostname
|
# Creates a new Net::SMTP object. +address+ is the hostname
|
||||||
# or ip address of your SMTP server. +port+ is the port to
|
# or ip address of your SMTP server. +port+ is the port to
|
||||||
# connect to; it defaults to port 25.
|
# connect to; it defaults to port 25.
|
||||||
@ -182,8 +252,11 @@ module Net # :nodoc:
|
|||||||
@read_timeout = 60
|
@read_timeout = 60
|
||||||
@error_occured = false
|
@error_occured = false
|
||||||
@debug_output = nil
|
@debug_output = nil
|
||||||
|
@use_tls = SMTP.use_tls?
|
||||||
|
@certs = SMTP.certs
|
||||||
|
@verify = SMTP.verify
|
||||||
end
|
end
|
||||||
|
|
||||||
# Provide human-readable stringification of class state.
|
# Provide human-readable stringification of class state.
|
||||||
def inspect
|
def inspect
|
||||||
"#<#{self.class} #{@address}:#{@port} started=#{@started}>"
|
"#<#{self.class} #{@address}:#{@port} started=#{@started}>"
|
||||||
@ -205,6 +278,28 @@ module Net # :nodoc:
|
|||||||
|
|
||||||
alias esmtp esmtp?
|
alias esmtp esmtp?
|
||||||
|
|
||||||
|
# does this instance use SSL?
|
||||||
|
def use_tls?
|
||||||
|
@use_tls
|
||||||
|
end
|
||||||
|
|
||||||
|
# Enables STARTTLS for this instance.
|
||||||
|
# +verify+ is the type of verification to do on the Server Cert; Defaults
|
||||||
|
# to OpenSSL::SSL::VERIFY_PEER.
|
||||||
|
# +certs+ is a file or directory holding CA certs to use to verify the
|
||||||
|
# server cert; Defaults to nil.
|
||||||
|
def enable_tls(verify = OpenSSL::SSL::VERIFY_PEER, certs = nil)
|
||||||
|
@use_tls = true
|
||||||
|
@verify = verify
|
||||||
|
@certs = certs
|
||||||
|
end
|
||||||
|
|
||||||
|
def disable_tls
|
||||||
|
@use_tls = false
|
||||||
|
@verify = nil
|
||||||
|
@certs = nil
|
||||||
|
end
|
||||||
|
|
||||||
# The address of the SMTP server to connect to.
|
# The address of the SMTP server to connect to.
|
||||||
attr_reader :address
|
attr_reader :address
|
||||||
|
|
||||||
@ -252,12 +347,12 @@ module Net # :nodoc:
|
|||||||
#
|
#
|
||||||
# This method is equivalent to:
|
# This method is equivalent to:
|
||||||
#
|
#
|
||||||
# Net::SMTP.new(address,port).start(helo_domain,account,password,authtype)
|
# Net::SMTP.new(address,port).start(helo_domain,account,password,authtype)
|
||||||
#
|
#
|
||||||
# # example
|
# # example
|
||||||
# Net::SMTP.start('your.smtp.server') {
|
# Net::SMTP.start('your.smtp.server') {
|
||||||
# smtp.send_message msgstr, 'from@example.com', ['dest@example.com']
|
# smtp.send_message msgstr, 'from@example.com', ['dest@example.com']
|
||||||
# }
|
# }
|
||||||
#
|
#
|
||||||
# If called with a block, the newly-opened Net::SMTP object is yielded
|
# If called with a block, the newly-opened Net::SMTP object is yielded
|
||||||
# to the block, and automatically closed when the block finishes. If called
|
# to the block, and automatically closed when the block finishes. If called
|
||||||
@ -340,15 +435,56 @@ module Net # :nodoc:
|
|||||||
def do_start(helodomain, user, secret, authtype)
|
def do_start(helodomain, user, secret, authtype)
|
||||||
raise IOError, 'SMTP session already started' if @started
|
raise IOError, 'SMTP session already started' if @started
|
||||||
check_auth_args user, secret, authtype if user or secret
|
check_auth_args user, secret, authtype if user or secret
|
||||||
|
s = timeout(@open_timeout) { TCPSocket.open(@address, @port) }
|
||||||
|
@socket = InternetMessageIO.new(s)
|
||||||
|
|
||||||
@socket = InternetMessageIO.new(timeout(@open_timeout) {
|
|
||||||
TCPSocket.open(@address, @port)
|
|
||||||
})
|
|
||||||
logging "SMTP session opened: #{@address}:#{@port}"
|
logging "SMTP session opened: #{@address}:#{@port}"
|
||||||
@socket.read_timeout = @read_timeout
|
@socket.read_timeout = @read_timeout
|
||||||
@socket.debug_output = @debug_output
|
@socket.debug_output = @debug_output
|
||||||
check_response(critical { recv_response() })
|
check_response(critical { recv_response() })
|
||||||
begin
|
do_helo(helodomain)
|
||||||
|
|
||||||
|
if @use_tls
|
||||||
|
raise 'openssl library not installed' unless defined?(OpenSSL)
|
||||||
|
context = OpenSSL::SSL::SSLContext.new
|
||||||
|
context.verify_mode = @verify
|
||||||
|
if @certs
|
||||||
|
if File.file?(@certs)
|
||||||
|
context.ca_file = @certs
|
||||||
|
elsif File.directory?(@certs)
|
||||||
|
context.ca_path = @certs
|
||||||
|
else
|
||||||
|
raise ArgumentError, "certs given but is not file or directory: #{@certs}"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
s = OpenSSL::SSL::SSLSocket.new(s, context)
|
||||||
|
s.sync_close = true
|
||||||
|
starttls
|
||||||
|
s.connect
|
||||||
|
logging 'TLS started'
|
||||||
|
@socket = InternetMessageIO.new(s)
|
||||||
|
@socket.read_timeout = @read_timeout
|
||||||
|
@socket.debug_output = @debug_output
|
||||||
|
# helo response may be different after STARTTLS
|
||||||
|
do_helo(helodomain)
|
||||||
|
end
|
||||||
|
|
||||||
|
authenticate user, secret, authtype if user
|
||||||
|
@started = true
|
||||||
|
ensure
|
||||||
|
unless @started
|
||||||
|
# authentication failed, cancel connection.
|
||||||
|
s.close if s and not s.closed?
|
||||||
|
@socket = nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
private :do_start
|
||||||
|
|
||||||
|
# method to send helo or ehlo based on defaults and to
|
||||||
|
# retry with helo if server doesn't like ehlo.
|
||||||
|
#
|
||||||
|
def do_helo(helodomain)
|
||||||
|
begin
|
||||||
if @esmtp
|
if @esmtp
|
||||||
ehlo helodomain
|
ehlo helodomain
|
||||||
else
|
else
|
||||||
@ -362,12 +498,8 @@ module Net # :nodoc:
|
|||||||
end
|
end
|
||||||
raise
|
raise
|
||||||
end
|
end
|
||||||
authenticate user, secret, authtype if user
|
|
||||||
@started = true
|
|
||||||
ensure
|
|
||||||
@socket.close if not @started and @socket and not @socket.closed?
|
|
||||||
end
|
end
|
||||||
private :do_start
|
|
||||||
|
|
||||||
# Finishes the SMTP session and closes TCP connection.
|
# Finishes the SMTP session and closes TCP connection.
|
||||||
# Raises IOError if not started.
|
# Raises IOError if not started.
|
||||||
@ -579,6 +711,9 @@ module Net # :nodoc:
|
|||||||
getok('QUIT')
|
getok('QUIT')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def starttls
|
||||||
|
getok('STARTTLS')
|
||||||
|
end
|
||||||
#
|
#
|
||||||
# row level library
|
# row level library
|
||||||
#
|
#
|
||||||
@ -631,7 +766,7 @@ module Net # :nodoc:
|
|||||||
end
|
end
|
||||||
|
|
||||||
def logging(msg)
|
def logging(msg)
|
||||||
@debug_output << msg if @debug_output
|
@debug_output << msg + "\n" if @debug_output
|
||||||
end
|
end
|
||||||
|
|
||||||
end # class SMTP
|
end # class SMTP
|
||||||
|
Loading…
x
Reference in New Issue
Block a user