class Net::SMTP
Что представляет собой эта библиотека?
Эта библиотека предоставляет функциональность для отправки электронной почты через SMTP, протокол Simple Mail Transfer Protocol. Подробную информацию о самом SMTP см. в [RFC2821] (www.ietf.org/rfc/rfc2821.txt).
Что эта библиотека НЕ предоставляет?
Эта библиотека НЕ предоставляет функций для составления сообщений электронной почты. Вы должны создавать их самостоятельно. Если вам требуется более продвинутая поддержка почты, попробуйте RubyMail или TMail или найдите альтернативы на RubyGems.org или The Ruby Toolbox.
Для справки: официальная документация по электронной почте: [RFC2822] (www.ietf.org/rfc/rfc2822.txt).
Примеры
Отправка сообщений
Перед отправкой сообщений необходимо установить соединение с сервером SMTP. Первым аргументом является адрес сервера SMTP, а вторым — номер порта. Использование SMTP.start с блоком является наиболее простым способом. Таким образом, соединение с сервером SMTP автоматически закрывается после выполнения блока.
require 'net/smtp'
Net::SMTP.start('your.smtp.server', 25) do |smtp|
# Use the SMTP object smtp only in this block.
end
Замените 'your.smtp.server' на адрес вашего сервера SMTP. Обычно сервер предоставляется системным администратором или вашим интернет-провайдером.
Затем вы можете отправлять сообщения.
msgstr = <<END_OF_MESSAGE
From: Your Name <your@mail.address>
To: Destination Address <someone@example.com>
Subject: test message
Date: Sat, 23 Jun 2001 16:26:43 +0900
Message-Id: <unique.message.id.string@example.com>
This is a test message.
END_OF_MESSAGE
require 'net/smtp'
Net::SMTP.start('your.smtp.server', 25) do |smtp|
smtp.send_message msgstr,
'your@mail.address',
'his_address@example.com'
end
Закрытие сессии
Вы ОБЯЗАТЕЛЬНО должны закрыть сессию SMTP после отправки сообщений, вызвав метод finish:
# using SMTP#finish
smtp = Net::SMTP.start('your.smtp.server', 25)
smtp.send_message msgstr, 'from@address', 'to@address'
smtp.finish
Вы также можете использовать блочный формат SMTP.start/SMTP#start. Это автоматически закрывает сессию SMTP:
# using block form of SMTP.start
Net::SMTP.start('your.smtp.server', 25) do |smtp|
smtp.send_message msgstr, 'from@address', 'to@address'
end
Настоятельно рекомендуем этот подход. Он проще и надежнее.
HELO домен
Почти во всех случаях необходимо указать третий аргумент для SMTP.start/SMTP#start. Это имя домена, в котором вы находитесь (хост, отправляющий почту). Это называется «HELO домен». Сервер SMTP будет судить о том, следует ли отправлять или отклонять сессию SMTP, анализируя HELO домен.
Net::SMTP.start('your.smtp.server', 25,
'mail.from.domain') { |smtp| ... }
SMTP Аутентификация
Класс Net::SMTP поддерживает три схемы аутентификации: PLAIN, LOGIN и CRAM MD5. (SMTP Аутентификация: [RFC2554]) Чтобы использовать аутентификацию SMTP, передайте дополнительные аргументы в SMTP.start/SMTP#start.
# PLAIN
Net::SMTP.start('your.smtp.server', 25, 'mail.from.domain',
'Your Account', 'Your Password', :plain)
# LOGIN
Net::SMTP.start('your.smtp.server', 25, 'mail.from.domain',
'Your Account', 'Your Password', :login)
# CRAM MD5
Net::SMTP.start('your.smtp.server', 25, 'mail.from.domain',
'Your Account', 'Your Password', :cram_md5)
Константы
- CRAM_BUFSIZE
- DEFAULT_AUTH_TYPE
-
Аутентификация
- IMASK
- OMASK
- Ревизия
Атрибуты
Адрес сервера SMTP для подключения.
Время ожидания при попытке открытия соединения в секундах. Если соединение не может быть открыто в течение этого времени, генерируется исключение Net::OpenTimeout. Значение по умолчанию — 30 секунд.
Порт сервера SMTP для подключения.
Время ожидания при чтении блока (одним вызовом read(2)) в секундах. Если вызов read(2) не завершается в течение этого времени, генерируется исключение Net::ReadTimeout. Значение по умолчанию — 60 секунд.
Публичные методы класса
# File lib/net/smtp.rb, line 175 def SMTP.default_port 25 end
Порт по умолчанию для сервера SMTP, 25.
# File lib/net/smtp.rb, line 193 def SMTP.default_ssl_context OpenSSL::SSL::SSLContext.new end
# File lib/net/smtp.rb, line 180 def SMTP.default_submission_port 587 end
Порт по умолчанию для отправки почты, 587.
# File lib/net/smtp.rb, line 185 def SMTP.default_tls_port 465 end
Порт SMTPS по умолчанию, 465.
# File lib/net/smtp.rb, line 208 def initialize(address, port = nil) @address = address @port = (port || SMTP.default_port) @esmtp = true @capabilities = nil @socket = nil @started = false @open_timeout = 30 @read_timeout = 60 @error_occurred = false @debug_output = nil @tls = false @starttls = false @ssl_context = nil end
Создаёт новый объект Net::SMTP.
address — имя хоста или IP-адрес сервера SMTP. port — порт для подключения; по умолчанию 25.
Этот метод не открывает TCP-соединение. Для выполнения всего сразу используйте SMTP.start вместо SMTP.new. В противном случае, после SMTP.new вызовите SMTP#start.
# File lib/net/smtp.rb, line 452
def SMTP.start(address, port = nil, helo = 'localhost',
user = nil, secret = nil, authtype = nil,
&block) # :yield: smtp
new(address, port).start(helo, user, secret, authtype, &block)
end Создаёт новый объект Net::SMTP и подключается к серверу.
Этот метод эквивалентен:
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']
end
Использование блока
Если вызывается с блоком, новый открытый объект Net::SMTP передаётся в блок и автоматически закрывается по завершении блока. Если вызывается без блока, новый открытый объект Net::SMTP возвращается вызывающей стороне, и вызывающая сторона отвечает за его закрытие по завершении.
Параметры
address — имя хоста или IP-адрес вашего сервера smtp.
port — порт для подключения; по умолчанию 25.
helo — домен HELO, предоставляемый клиентом серверу (см. комментарии к обзору); по умолчанию 'localhost'.
Остальные аргументы используются для аутентификации SMTP, если это необходимо или желательно. user — имя пользователя; secret — ваш пароль или другой токен аутентификации; authtype — тип аутентификации, один из :plain, :login или :cram_md5. См. обсуждение аутентификации SMTP в заметках к обзору.
Ошибки
Этот метод может вызывать:
Общедоступные методы экземпляров
# File lib/net/smtp.rb, line 751
def auth_cram_md5(user, secret)
check_auth_args user, secret
res = critical {
res0 = get_response('AUTH CRAM-MD5')
check_auth_continue res0
crammed = cram_md5_response(secret, res0.cram_md5_challenge)
get_response(base64_encode("#{user} #{crammed}"))
}
check_auth_response res
res
end # File lib/net/smtp.rb, line 740
def auth_login(user, secret)
check_auth_args user, secret
res = critical {
check_auth_continue get_response('AUTH LOGIN')
check_auth_continue get_response(base64_encode(user))
get_response(base64_encode(secret))
}
check_auth_response res
res
end # File lib/net/smtp.rb, line 731
def auth_plain(user, secret)
check_auth_args user, secret
res = critical {
get_response('AUTH PLAIN ' + base64_encode("\0#{user}\0#{secret}"))
}
check_auth_response res
res
end # File lib/net/smtp.rb, line 725 def authenticate(user, secret, authtype = DEFAULT_AUTH_TYPE) check_auth_method authtype check_auth_args user, secret send auth_method(authtype), user, secret end
# File lib/net/smtp.rb, line 280 def capable_auth_types return [] unless @capabilities return [] unless @capabilities['AUTH'] @capabilities['AUTH'] end
Возвращает поддерживаемые методы аутентификации на этом сервере. Вы не можете получить действительное значение до открытия сессии SMTP.
# File lib/net/smtp.rb, line 267
def capable_cram_md5_auth?
auth_capable?('CRAM-MD5')
end true, если сервер поддерживает AUTH CRAM-MD5. Вы не можете получить действительное значение до открытия сессии SMTP.
# File lib/net/smtp.rb, line 261
def capable_login_auth?
auth_capable?('LOGIN')
end true, если сервер поддерживает AUTH LOGIN. Вы не можете получить действительное значение до открытия сессии SMTP.
# File lib/net/smtp.rb, line 255
def capable_plain_auth?
auth_capable?('PLAIN')
end true, если сервер поддерживает AUTH PLAIN. Вы не можете получить действительное значение до открытия сессии SMTP.
# File lib/net/smtp.rb, line 243
def capable_starttls?
capable?('STARTTLS')
end true, если сервер поддерживает STARTTLS. Вы не можете получить действительное значение до открытия сессии SMTP сессии.
# File lib/net/smtp.rb, line 891
def data(msgstr = nil, &block) #:yield: stream
if msgstr and block
raise ArgumentError, "message and block are exclusive"
end
unless msgstr or block
raise ArgumentError, "message or block is required"
end
res = critical {
check_continue get_response('DATA')
socket_sync_bak = @socket.io.sync
begin
@socket.io.sync = false
if msgstr
@socket.write_message msgstr
else
@socket.write_message_by_block(&block)
end
ensure
@socket.io.flush
@socket.io.sync = socket_sync_bak
end
recv_response()
}
check_response res
res
end Этот метод отправляет сообщение. Если msgstr задан, отправляет его как сообщение. Если задан блок, передаёт поток записи сообщения. Вы должны записать сообщение перед закрытием блока.
# Example 1 (by string)
smtp.data(<<EndMessage)
From: john@example.com
To: betty@example.com
Subject: I found a bug
Check vm.c:58879.
EndMessage
# Example 2 (by block)
smtp.data {|f|
f.puts "From: john@example.com"
f.puts "To: betty@example.com"
f.puts "Subject: I found a bug"
f.puts ""
f.puts "Check vm.c:58879."
}
# File lib/net/smtp.rb, line 393 def debug_output=(arg) @debug_output = arg end
ПРЕДУПРЕЖДЕНИЕ: Этот метод вызывает серьёзные уязвимости безопасности. Используйте его только для отладки.
Устанавливает поток вывода для отладочной регистрации. Вы должны вызвать этот метод до start.
# example smtp = Net::SMTP.new(addr, port) smtp.set_debug_output $stderr smtp.start do |smtp| .... end
# File lib/net/smtp.rb, line 351 def disable_starttls @starttls = false @ssl_context = nil end
Отключает SMTP/TLS (STARTTLS) для этого объекта. Необходимо вызвать этот метод до установления соединения, чтобы он имел эффект.
# File lib/net/smtp.rb, line 307 def disable_tls @tls = false @ssl_context = nil end
Отключает SMTP/TLS для этого объекта. Необходимо вызвать этот метод до установления соединения, чтобы он имел эффект.
# File lib/net/smtp.rb, line 829
def ehlo(domain)
getok("EHLO #{domain}")
end # File lib/net/smtp.rb, line 333 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 @starttls = :always @ssl_context = context end
Включает SMTP/TLS (STARTTLS) для этого объекта. context — это объект OpenSSL::SSL::SSLContext.
# File lib/net/smtp.rb, line 342 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 @starttls = :auto @ssl_context = context end
Включает SMTP/TLS (STARTTLS) для этого объекта, если сервер его поддерживает. context — это объект OpenSSL::SSL::SSLContext.
# File lib/net/smtp.rb, line 296 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 @tls = true @ssl_context = context end
Включает SMTP/TLS (SMTPS: SMTP по прямому TLS-соединению) для этого объекта. Необходимо вызвать этот метод до установления соединения, чтобы он имел эффект. context — это объект OpenSSL::SSL::SSLContext.
# File lib/net/smtp.rb, line 825
def helo(domain)
getok("HELO #{domain}")
end # File lib/net/smtp.rb, line 225
def inspect
"#<#{self.class} #{@address}:#{@port} started=#{@started}>"
end Предоставляет строковое представление состояния класса для чтения человеком.
# File lib/net/smtp.rb, line 833
def mailfrom(from_addr)
if $SAFE > 0
raise SecurityError, 'tainted from_addr' if from_addr.tainted?
end
getok("MAIL FROM:<#{from_addr}>")
end # File lib/net/smtp.rb, line 709
def open_message_stream(from_addr, *to_addrs, &block) # :yield: stream
raise IOError, 'closed session' unless @socket
mailfrom from_addr
rcptto_list(to_addrs) {data(&block)}
end Открывает поток записи сообщений и передает его в блок. Поток действителен только в блоке и имеет следующие методы:
- puts(str = '')
-
выводит STR и CR LF.
- print(str)
-
выводит STR.
- printf(fmt, *args)
-
выводит sprintf(fmt,*args).
- write(str)
-
выводит STR и возвращает длину записанных байтов.
- <<(str)
-
выводит STR и возвращает self.
Если в сообщении встречается одиночный символ CR (“r”) или LF (“n”), он преобразуется в пару CR LF. С помощью этого метода нельзя отправлять двоичные сообщения.
Параметры
from_addr — это String, представляющий адрес источника электронной почты.
to_addr — это String или строки или Array строк, представляющие адрес или адреса назначения электронной почты.
Пример
Net::SMTP.start('smtp.example.com', 25) do |smtp|
smtp.open_message_stream('from@example.com', ['dest@example.com']) do |f|
f.puts 'From: from@example.com'
f.puts 'To: dest@example.com'
f.puts 'Subject: test message'
f.puts
f.puts 'This is a test message.'
end
end
Ошибки
Этот метод может вызвать:
# File lib/net/smtp.rb, line 918
def quit
getok('QUIT')
end # File lib/net/smtp.rb, line 861
def rcptto(to_addr)
if $SAFE > 0
raise SecurityError, 'tainted to_addr' if to_addr.tainted?
end
getok("RCPT TO:<#{to_addr}>")
end # File lib/net/smtp.rb, line 840
def rcptto_list(to_addrs)
raise ArgumentError, 'mail destination not given' if to_addrs.empty?
ok_users = []
unknown_users = []
to_addrs.flatten.each do |addr|
begin
rcptto addr
rescue SMTPAuthenticationError
unknown_users << addr.dump
else
ok_users << addr
end
end
raise ArgumentError, 'mail destination not given' if ok_users.empty?
ret = yield
unless unknown_users.empty?
raise SMTPAuthenticationError, "failed to deliver for #{unknown_users.join(', ')}"
end
ret
end # File lib/net/smtp.rb, line 374 def read_timeout=(sec) @socket.read_timeout = sec if @socket @read_timeout = sec end
Устанавливает количество секунд ожидания при таймауте вызова read(2).
# File lib/net/smtp.rb, line 817
def rset
getok('RSET')
end Прерывает текущую транзакцию отправки почты.
# File lib/net/smtp.rb, line 656
def send_message(msgstr, from_addr, *to_addrs)
raise IOError, 'closed session' unless @socket
mailfrom from_addr
rcptto_list(to_addrs) {data msgstr}
end Отправляет msgstr в качестве сообщения. Встречающиеся одиночные CR (“r”) и LF (“n”) в msgstr, преобразуются в пару CR LF. С помощью этого метода нельзя отправлять двоичные сообщения. msgstr должен включать заголовки и тело сообщения.
from_addr — это String, представляющий адрес источника электронной почты.
to_addr — это String или строки или Array строк, представляющие адрес или адреса назначения электронной почты.
Пример
Net::SMTP.start('smtp.example.com') do |smtp|
smtp.send_message msgstr,
'from@example.com',
['dest@example.com', 'dest2@example.com']
end
Ошибки
Этот метод может вызвать:
# File lib/net/smtp.rb, line 514
def start(helo = 'localhost',
user = nil, secret = nil, authtype = nil) # :yield: smtp
if block_given?
begin
do_start helo, user, secret, authtype
return yield(self)
ensure
do_finish
end
else
do_start helo, user, secret, authtype
return self
end
end Устанавливает TCP-соединение и запускает сеанс SMTP.
Параметры
helo — это домен HELO, с которого будут отправляться письма; см. обсуждение в общих заметках.
Если заданы и user, и secret, будет выполнена попытка аутентификации SMTP с помощью команды AUTH. authtype указывает тип аутентификации; он должен быть одним из :login, :plain и :cram_md5. См. заметки об аутентификации SMTP в общих заметках.
Использование блока
При вызове этого метода с блоком вновь созданный объект SMTP передается в блок и автоматически закрывается после завершения вызова блока. В противном случае ответственность за закрытие сеанса лежит на вызывающей стороне.
Пример
Это очень похоже на метод класса SMTP.start.
require 'net/smtp'
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']
end
Основное назначение этого метода (в отличие от SMTP.start) — вероятно, установка отладки (#set_debug_output) или ESMTP (#esmtp=), которые должны быть выполнены до начала сеанса.
Ошибки
Если сеанс уже запущен, будет вызвано исключение IOError.
Этот метод может вызвать:
# File lib/net/smtp.rb, line 459 def started? @started end
Возвращает true, если сеанс SMTP запущен.
# File lib/net/smtp.rb, line 821
def starttls
getok('STARTTLS')
end # File lib/net/smtp.rb, line 317 def starttls? @starttls end
Возвращает значение истинности, если для этого объекта используется STARTTLS. Если для этого объекта всегда используется STARTTLS, возвращает :always. Если для этого объекта используется STARTTLS, когда сервер поддерживает TLS, возвращает :auto.
# File lib/net/smtp.rb, line 322 def starttls_always? @starttls == :always end
true, если этот объект использует STARTTLS.
# File lib/net/smtp.rb, line 327 def starttls_auto? @starttls == :auto end
true, если этот объект использует STARTTLS, когда сервер объявляет STARTTLS.
# File lib/net/smtp.rb, line 287 def tls? @tls end
true, если этот объект использует SMTP/TLS (SMTPS).
Приватные методы экземпляра
# File lib/net/smtp.rb, line 271 def auth_capable?(type) return nil unless @capabilities return false unless @capabilities['AUTH'] @capabilities['AUTH'].include?(type) end
# File lib/net/smtp.rb, line 771
def auth_method(type)
"auth_#{type.to_s.downcase}".intern
end # File lib/net/smtp.rb, line 784
def base64_encode(str)
# expects "str" may not become too long
[str].pack('m0')
end # File lib/net/smtp.rb, line 247 def capable?(key) return nil unless @capabilities @capabilities[key] ? true : false end
# File lib/net/smtp.rb, line 775
def check_auth_args(user, secret, authtype = DEFAULT_AUTH_TYPE)
unless user
raise ArgumentError, 'SMTP-AUTH requested but missing user name'
end
unless secret
raise ArgumentError, 'SMTP-AUTH requested but missing secret phrase'
end
end # File lib/net/smtp.rb, line 985
def check_auth_continue(res)
unless res.continue?
raise res.exception_class, res.message
end
end # File lib/net/smtp.rb, line 765
def check_auth_method(type)
unless respond_to?(auth_method(type), true)
raise ArgumentError, "wrong authentication type #{type}"
end
end # File lib/net/smtp.rb, line 979
def check_auth_response(res)
unless res.success?
raise SMTPAuthenticationError, res.message
end
end # File lib/net/smtp.rb, line 973
def check_continue(res)
unless res.continue?
raise SMTPUnknownError, "could not get 3xx (#{res.status}: #{res.string})"
end
end # File lib/net/smtp.rb, line 967
def check_response(res)
unless res.success?
raise res.exception_class, res.message
end
end # File lib/net/smtp.rb, line 793 def cram_md5_response(secret, challenge) tmp = Digest::MD5.digest(cram_secret(secret, IMASK) + challenge) Digest::MD5.hexdigest(cram_secret(secret, OMASK) + tmp) end
CRAM-MD5: [RFC2195]
# File lib/net/smtp.rb, line 800
def cram_secret(secret, mask)
secret = Digest::MD5.digest(secret) if secret.size > CRAM_BUFSIZE
buf = secret.ljust(CRAM_BUFSIZE, "\0")
0.upto(buf.size - 1) do |i|
buf[i] = (buf[i].ord ^ mask).chr
end
buf
end # File lib/net/smtp.rb, line 957
def critical
return Response.parse('200 dummy reply code') if @error_occurred
begin
return yield()
rescue Exception
@error_occurred = true
raise
end
end # File lib/net/smtp.rb, line 611 def do_finish quit if @socket and not @socket.closed? and not @error_occurred ensure @started = false @error_occurred = false @socket.close if @socket @socket = nil end
# File lib/net/smtp.rb, line 599
def do_helo(helo_domain)
res = @esmtp ? ehlo(helo_domain) : helo(helo_domain)
@capabilities = res.capabilities
rescue SMTPError
if @esmtp
@esmtp = false
@error_occurred = false
retry
end
raise
end # File lib/net/smtp.rb, line 542
def do_start(helo_domain, user, secret, authtype)
raise IOError, 'SMTP session already started' if @started
if user or secret
check_auth_method(authtype || DEFAULT_AUTH_TYPE)
check_auth_args user, secret
end
s = Timeout.timeout(@open_timeout, Net::OpenTimeout) do
tcp_socket(@address, @port)
end
logging "Connection opened: #{@address}:#{@port}"
@socket = new_internet_message_io(tls? ? tlsconnect(s) : s)
check_response critical { recv_response() }
do_helo helo_domain
if starttls_always? or (capable_starttls? and starttls_auto?)
unless capable_starttls?
raise SMTPUnsupportedCommand,
"STARTTLS is not supported on this server"
end
starttls
@socket = new_internet_message_io(tlsconnect(s))
# helo response may be different after STARTTLS
do_helo helo_domain
end
authenticate user, secret, (authtype || DEFAULT_AUTH_TYPE) if user
@started = true
ensure
unless @started
# authentication failed, cancel connection.
s.close if s
@socket = nil
end
end # File lib/net/smtp.rb, line 941 def get_response(reqline) validate_line reqline @socket.writeline reqline recv_response() end
# File lib/net/smtp.rb, line 931
def getok(reqline)
validate_line reqline
res = critical {
@socket.writeline reqline
recv_response()
}
check_response res
res
end # File lib/net/smtp.rb, line 1070 def logging(msg) @debug_output << msg + "\n" if @debug_output end
# File lib/net/smtp.rb, line 594
def new_internet_message_io(s)
InternetMessageIO.new(s, read_timeout: @read_timeout,
debug_output: @debug_output)
end # File lib/net/smtp.rb, line 947
def recv_response
buf = ''.dup
while true
line = @socket.readline
buf << line << "\n"
break unless line[3,1] == '-' # "210-PIPELINING"
end
Response.parse(buf)
end # File lib/net/smtp.rb, line 575 def ssl_socket(socket, context) OpenSSL::SSL::SSLSocket.new socket, context end
# File lib/net/smtp.rb, line 538 def tcp_socket(address, port) TCPSocket.open address, port end
# File lib/net/smtp.rb, line 579
def tlsconnect(s)
verified = false
s = ssl_socket(s, @ssl_context)
logging "TLS connection started"
s.sync_close = true
ssl_socket_connect(s, @open_timeout)
if @ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE
s.post_connection_check(@address)
end
verified = true
s
ensure
s.close unless verified
end # File lib/net/smtp.rb, line 924
def validate_line(line)
# A bare CR or LF is not allowed in RFC5321.
if /[\r\n]/ =~ line
raise ArgumentError, "A line must not contain CR or LF"
end
end
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.