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 885
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)
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 912
def quit
getok('QUIT')
end # File lib/net/smtp.rb, line 858
def rcptto(to_addr)
getok("RCPT TO:<#{to_addr}>")
end # File lib/net/smtp.rb, line 837
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
Возвращает значение true, если этот объект использует 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 979
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 973
def check_auth_response(res)
unless res.success?
raise SMTPAuthenticationError, res.message
end
end # File lib/net/smtp.rb, line 967
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 961
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 951
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 935 def get_response(reqline) validate_line reqline @socket.writeline reqline recv_response() end
# File lib/net/smtp.rb, line 925
def getok(reqline)
validate_line reqline
res = critical {
@socket.writeline reqline
recv_response()
}
check_response res
res
end # File lib/net/smtp.rb, line 1064 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 941
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 918
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.