# frozen_string_literal: true
# Copyright (c) 1999-2007 Yukihiro Matsumoto.
# Copyright (c) 1999-2007 Minero Aoki.
# Written & maintained by Minero Aoki <aamine@loveruby.net>.
# Documented by William Webber and Minero Aoki.
# This program is free software. You can re-distribute and/or
# modify this program under the same terms as Ruby itself.
# See Net::SMTP for documentation.
# Module mixed in to all SMTP error classes
# This *class* is a module for backward compatibility.
# In later release, this module becomes a class.
# Represents an SMTP authentication error.
class SMTPAuthenticationError < ProtoAuthError
# Represents SMTP error code 4xx, a temporary error.
class SMTPServerBusy < ProtoServerError
# Represents an SMTP command syntax error (error code 500)
class SMTPSyntaxError < ProtoSyntaxError
# Represents a fatal SMTP error (error code 5xx, except for 500)
class SMTPFatalError < ProtoFatalError
# Unexpected reply code returned from server.
class SMTPUnknownError < ProtoUnknownError
# Command is not supported on server.
class SMTPUnsupportedCommand < ProtocolError
# == What is This Library?
# This library provides functionality to send internet
# mail via SMTP, the Simple Mail Transfer Protocol. For details of
# SMTP itself, see [RFC2821] (http://www.ietf.org/rfc/rfc2821.txt).
# == What is This Library NOT?
# This library does NOT provide functions to compose internet mails.
# You must create them by yourself. If you want better mail support,
# try RubyMail or TMail or search for alternatives in
# {RubyGems.org}[https://rubygems.org/] or {The Ruby
# Toolbox}[https://www.ruby-toolbox.com/].
# FYI: the official documentation on internet mail is: [RFC2822] (http://www.ietf.org/rfc/rfc2822.txt).
# You must open a connection to an SMTP server before sending messages.
# The first argument is the address of your SMTP server, and the second
# argument is the port number. Using SMTP.start with a block is the simplest
# way to do this. This way, the SMTP connection is closed automatically
# after the block is executed.
# Net::SMTP.start('your.smtp.server', 25) do |smtp|
# # Use the SMTP object smtp only in this block.
# Replace 'your.smtp.server' with your SMTP server. Normally
# your system manager or internet provider supplies a server
# Then you can send messages.
# msgstr = <<END_OF_MESSAGE
# From: Your Name <your@mail.address>
# To: Destination Address <someone@example.com>
# Date: Sat, 23 Jun 2001 16:26:43 +0900
# Message-Id: <unique.message.id.string@example.com>
# This is a test message.
# Net::SMTP.start('your.smtp.server', 25) do |smtp|
# smtp.send_message msgstr,
# 'his_address@example.com'
# === Closing the Session
# You MUST close the SMTP session after sending messages, by calling
# smtp = Net::SMTP.start('your.smtp.server', 25)
# smtp.send_message msgstr, 'from@address', 'to@address'
# You can also use the block form of SMTP.start/SMTP#start. This closes
# the SMTP session automatically:
# # using block form of SMTP.start
# Net::SMTP.start('your.smtp.server', 25) do |smtp|
# smtp.send_message msgstr, 'from@address', 'to@address'
# I strongly recommend this scheme. This form is simpler and more robust.
# In almost all situations, you must provide a third argument
# to SMTP.start/SMTP#start. This is the domain name which you are on
# (the host to send mail from). It is called the "HELO domain".
# The SMTP server will judge whether it should send or reject
# the SMTP session by inspecting the HELO domain.
# Net::SMTP.start('your.smtp.server', 25,
# 'mail.from.domain') { |smtp| ... }
# === SMTP Authentication
# The Net::SMTP class supports three authentication schemes;
# PLAIN, LOGIN and CRAM MD5. (SMTP Authentication: [RFC2554])
# To use SMTP authentication, pass extra arguments to
# Net::SMTP.start('your.smtp.server', 25, 'mail.from.domain',
# 'Your Account', 'Your Password', :plain)
# Net::SMTP.start('your.smtp.server', 25, 'mail.from.domain',
# 'Your Account', 'Your Password', :login)
# Net::SMTP.start('your.smtp.server', 25, 'mail.from.domain',
# 'Your Account', 'Your Password', :cram_md5)
Revision = %q$Revision$.split[1]
# The default SMTP port number, 25.
# The default mail submission port number, 587.
def SMTP.default_submission_port
# The default SMTPS port number, 465.
def SMTP.default_tls_port
alias default_ssl_port default_tls_port
def SMTP.default_ssl_context
OpenSSL::SSL::SSLContext.new
# Creates a new Net::SMTP object.
# +address+ is the hostname or ip address of your SMTP
# server. +port+ is the port to connect to; it defaults to
# This method does not open the TCP connection. You can use
# SMTP.start instead of SMTP.new if you want to do everything
# at once. Otherwise, follow SMTP.new with SMTP#start.
def initialize(address, port = nil)
@port = (port || SMTP.default_port)
# Provide human-readable stringification of class state.
"#<#{self.class} #{@address}:#{@port} started=#{@started}>"
# Set whether to use ESMTP or not. This should be done before
# calling #start. Note that if #start is called in ESMTP mode,
# and the connection fails due to a ProtocolError, the SMTP
# object will automatically switch to plain SMTP mode and
# retry (but not vice versa).
# +true+ if the SMTP object uses ESMTP (which it does by default).
# true if server advertises STARTTLS.
# You cannot get valid value before opening SMTP session.
return nil unless @capabilities
@capabilities[key] ? true : false
# true if server advertises AUTH PLAIN.
# You cannot get valid value before opening SMTP session.
# true if server advertises AUTH LOGIN.
# You cannot get valid value before opening SMTP session.
# true if server advertises AUTH CRAM-MD5.
# You cannot get valid value before opening SMTP session.
def capable_cram_md5_auth?
auth_capable?('CRAM-MD5')
return nil unless @capabilities
return false unless @capabilities['AUTH']
@capabilities['AUTH'].include?(type)
# Returns supported authentication methods on this server.
# You cannot get valid value before opening SMTP session.
return [] unless @capabilities
return [] unless @capabilities['AUTH']
# true if this object uses SMTP/TLS (SMTPS).
# Enables SMTP/TLS (SMTPS: SMTP over direct TLS connection) for
# this object. Must be called before the connection is established
# to have any effect. +context+ is a OpenSSL::SSL::SSLContext object.
def enable_tls(context = SMTP.default_ssl_context)
raise 'openssl library not installed' unless defined?(OpenSSL)
raise ArgumentError, "SMTPS and STARTTLS is exclusive" if @starttls
alias enable_ssl enable_tls
# Disables SMTP/TLS for this object. Must be called before the
# connection is established to have any effect.
alias disable_ssl disable_tls
# Returns truth value if this object uses STARTTLS.
# If this object always uses STARTTLS, returns :always.
# If this object uses STARTTLS when the server support TLS, returns :auto.
# true if this object uses STARTTLS.
# true if this object uses STARTTLS when server advertises STARTTLS.
# Enables SMTP/TLS (STARTTLS) for this object.
# +context+ is a OpenSSL::SSL::SSLContext object.
def enable_starttls(context = SMTP.default_ssl_context)
raise 'openssl library not installed' unless defined?(OpenSSL)
raise ArgumentError, "SMTPS and STARTTLS is exclusive" if @tls
# Enables SMTP/TLS (STARTTLS) for this object if server accepts.
# +context+ is a OpenSSL::SSL::SSLContext object.
def enable_starttls_auto(context = SMTP.default_ssl_context)
raise 'openssl library not installed' unless defined?(OpenSSL)
raise ArgumentError, "SMTPS and STARTTLS is exclusive" if @tls
# Disables SMTP/TLS (STARTTLS) for this object. Must be called
# before the connection is established to have any effect.
# The address of the SMTP server to connect to.
# The port number of the SMTP server to connect to.
# Seconds to wait while attempting to open a connection.
# If the connection cannot be opened within this time, a
# Net::OpenTimeout is raised. The default value is 30 seconds.
attr_accessor :open_timeout
# Seconds to wait while reading one block (by one read(2) call).
# If the read(2) call does not complete within this time, a
# Net::ReadTimeout is raised. The default value is 60 seconds.
attr_reader :read_timeout
# Set the number of seconds to wait until timing-out a read(2)
@socket.read_timeout = sec if @socket
# WARNING: This method causes serious security holes.
# Use this method for only debugging.
# Set an output stream for debug logging.
# You must call this before #start.
# smtp = Net::SMTP.new(addr, port)
# smtp.set_debug_output $stderr
alias set_debug_output debug_output=
# Creates a new Net::SMTP object and connects to the server.
# This method is equivalent to:
# Net::SMTP.new(address, port).start(helo_domain, account, password, authtype)
# Net::SMTP.start('your.smtp.server') do |smtp|
# smtp.send_message msgstr, 'from@example.com', ['dest@example.com']
# 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
# without a block, the newly-opened Net::SMTP object is returned to
# the caller, and it is the caller's responsibility to close it when
# +address+ is the hostname or ip address of your smtp server.
# +port+ is the port to connect to; it defaults to port 25.
# +helo+ is the _HELO_ _domain_ provided by the client to the
# server (see overview comments); it defaults to 'localhost'.
# The remaining arguments are used for SMTP authentication, if required
# or desired. +user+ is the account name; +secret+ is your password
# or other authentication token; and +authtype+ is the authentication
# type, one of :plain, :login, or :cram_md5. See the discussion of
# SMTP Authentication in the overview notes.
# * Net::SMTPAuthenticationError
# * Net::SMTPUnknownError
def SMTP.start(address, port = nil, helo = 'localhost',
user = nil, secret = nil, authtype = nil,
new(address, port).start(helo, user, secret, authtype, &block)
# +true+ if the SMTP session has been started.
# Opens a TCP connection and starts the SMTP session.
# +helo+ is the _HELO_ _domain_ that you'll dispatch mails from; see
# the discussion in the overview notes.
# If both of +user+ and +secret+ are given, SMTP authentication
# will be attempted using the AUTH command. +authtype+ specifies
# the type of authentication to attempt; it must be one of
# :login, :plain, and :cram_md5. See the notes on SMTP Authentication
# When this methods is called with a block, the newly-started SMTP
# object is yielded to the block, and automatically closed after
# the block call finishes. Otherwise, it is the caller's
# responsibility to close the session when finished.
# This is very similar to the class method SMTP.start.
# smtp = Net::SMTP.new('smtp.mail.server', 25)
# smtp.start(helo_domain, account, password, authtype) do |smtp|
# smtp.send_message msgstr, 'from@example.com', ['dest@example.com']
# The primary use of this method (as opposed to SMTP.start)
# is probably to set debugging (#set_debug_output) or ESMTP
# (#esmtp=), which must be done before the session is