class Net::SMTP
Что представляет собой эта библиотека?
Эта библиотека предоставляет функциональность для отправки интернет-почты через SMTP, Простой протокол передачи почты. Для получения подробной информации о SMTP см. [RFC2821] (www.ietf.org/rfc/rfc2821.txt).
Что ЭТА библиотека НЕ делает?
Эта библиотека НЕ предоставляет функции для составления интернет-письма. Вы должны создавать их самостоятельно. Если вам требуется лучшая поддержка почты, попробуйте RubyMail или TMail, или найдите альтернативы на RubyGems.org или The Ruby Toolbox.
Для справки: официальная документация по интернет-почте: [RFC2822] (www.ietf.org/rfc/rfc2822.txt).
Примеры
Отправка сообщений
Перед отправкой сообщений необходимо открыть подключение к серверу 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
Вы также можете использовать форму блока ::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 домен
Практически во всех ситуациях вы должны предоставить третий аргумент для ::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, передайте дополнительные аргументы в ::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 177 def SMTP.default_port 25 end
Номер порта SMTP по умолчанию, 25.
# File lib/net/smtp.rb, line 195 def SMTP.default_ssl_context OpenSSL::SSL::SSLContext.new end
# File lib/net/smtp.rb, line 182 def SMTP.default_submission_port 587 end
Номер порта отправки почты по умолчанию, 587.
# File lib/net/smtp.rb, line 187 def SMTP.default_tls_port 465 end
Номер порта SMTPS по умолчанию, 465.
# File lib/net/smtp.rb, line 210 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-соединение. Вы можете использовать ::start вместо ::new, если хотите сделать всё сразу. В противном случае, следуйте за ::new методом #start.
# File lib/net/smtp.rb, line 454
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 755
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 744
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 735
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 729 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 282 def capable_auth_types return [] unless @capabilities return [] unless @capabilities['AUTH'] @capabilities['AUTH'] end
Возвращает поддерживаемые методы аутентификации на этом сервере. Вы не можете получить действительное значение до открытия сессии SMTP.
# File lib/net/smtp.rb, line 269
def capable_cram_md5_auth?
auth_capable?('CRAM-MD5')
end true, если сервер рекламирует AUTH CRAM-MD5. Вы не можете получить действительное значение до открытия сессии SMTP.
# File lib/net/smtp.rb, line 263
def capable_login_auth?
auth_capable?('LOGIN')
end true, если сервер рекламирует AUTH LOGIN. Вы не можете получить действительное значение до открытия сессии SMTP.
# File lib/net/smtp.rb, line 257
def capable_plain_auth?
auth_capable?('PLAIN')
end true, если сервер рекламирует AUTH PLAIN. Вы не можете получить действительное значение до открытия сессии SMTP.
# File lib/net/smtp.rb, line 245
def capable_starttls?
capable?('STARTTLS')
end true, если сервер рекламирует STARTTLS. Вы не можете получить действительное значение до открытия сессии SMTP сессии.
# File lib/net/smtp.rb, line 895
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("From: john@example.com
To: betty@example.com
Subject: I found a bug
Check vm.c:58879.
")
# 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 395 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 353 def disable_starttls @starttls = false @ssl_context = nil end
Отключает SMTP/TLS (STARTTLS) для этого объекта. Должен быть вызван до установления соединения, чтобы иметь какой-либо эффект.
# File lib/net/smtp.rb, line 309 def disable_tls @tls = false @ssl_context = nil end
Отключает SMTP/TLS для этого объекта. Должен быть вызван до установления соединения, чтобы иметь какой-либо эффект.
# File lib/net/smtp.rb, line 833
def ehlo(domain)
getok("EHLO #{domain}")
end # File lib/net/smtp.rb, line 335 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 344 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 298 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 829
def helo(domain)
getok("HELO #{domain}")
end # File lib/net/smtp.rb, line 227
def inspect
"#<#{self.class} #{@address}:#{@port} started=#{@started}>"
end Предоставляет удобочитаемую строковую визуализацию состояния класса.
# File lib/net/smtp.rb, line 837
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 713
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.
Если в сообщении встречается одиночный символ переноса строки (“r”) или новой строки (“n”), он преобразуется в пару CR LF. Вы не можете отправить бинарное сообщение с помощью этого метода.
Параметры
from_addr — это строка, представляющая адрес исходной почты.
to_addr — это строка или строки или массив строк, представляющие адрес или адреса назначения почты.
Пример
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 922
def quit
getok('QUIT')
end # File lib/net/smtp.rb, line 865
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 844
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 376 def read_timeout=(sec) @socket.read_timeout = sec if @socket @read_timeout = sec end
Устанавливает количество секунд ожидания при истечении времени ожидания вызова read(2).
# File lib/net/smtp.rb, line 821
def rset
getok('RSET')
end Прерывает текущую транзакцию почты.
# File lib/net/smtp.rb, line 660
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 в качестве сообщения. Одиночные символы переноса строки (“r”) и новой строки (“n”), найденные в msgstr, преобразуются в пару CR LF. Вы не можете отправить бинарное сообщение с помощью этого метода. msgstr должно содержать как заголовки сообщения, так и тело.
from_addr — это строка, представляющая адрес исходной почты.
to_addr — это строка или строки или массив строк, представляющие адрес или адреса назначения почты.
Пример
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 516
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 передается в блок, и автоматически закрывается после завершения вызова блока. В противном случае, ответственность за закрытие сеанса лежит на вызывающей стороне.
Пример
Это очень похоже на метод класса ::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
Основное использование этого метода (в отличие от ::start) — вероятно, установить отладку (#set_debug_output) или ESMTP (#esmtp=), что должно быть сделано до запуска сеанса.
Ошибки
Если сеанс уже запущен, будет вызвано исключение IOError.
Этот метод может вызвать следующие исключения:
# File lib/net/smtp.rb, line 461 def started? @started end
Возвращает true, если сеанс SMTP был запущен.
# File lib/net/smtp.rb, line 825
def starttls
getok('STARTTLS')
end # File lib/net/smtp.rb, line 319 def starttls? @starttls end
Возвращает логическое значение, если для этого объекта используется STARTTLS. Если для этого объекта всегда используется STARTTLS, возвращает :always. Если для этого объекта используется STARTTLS, когда сервер поддерживает TLS, возвращает :auto.
# File lib/net/smtp.rb, line 324 def starttls_always? @starttls == :always end
true, если для этого объекта используется STARTTLS.
# File lib/net/smtp.rb, line 329 def starttls_auto? @starttls == :auto end
true, если для этого объекта используется STARTTLS, когда сервер рекламирует STARTTLS.
# File lib/net/smtp.rb, line 289 def tls? @tls end
true, если этот объект использует SMTP/TLS (SMTPS).
Приватные методы экземпляра
# File lib/net/smtp.rb, line 273 def auth_capable?(type) return nil unless @capabilities return false unless @capabilities['AUTH'] @capabilities['AUTH'].include?(type) end
# File lib/net/smtp.rb, line 775
def auth_method(type)
"auth_#{type.to_s.downcase}".intern
end # File lib/net/smtp.rb, line 788
def base64_encode(str)
# expects "str" may not become too long
[str].pack('m').gsub(/\s+/, '')
end # File lib/net/smtp.rb, line 249 def capable?(key) return nil unless @capabilities @capabilities[key] ? true : false end
# File lib/net/smtp.rb, line 779
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 980
def check_auth_continue(res)
unless res.continue?
raise res.exception_class, res.message
end
end # File lib/net/smtp.rb, line 769
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 974
def check_auth_response(res)
unless res.success?
raise SMTPAuthenticationError, res.message
end
end # File lib/net/smtp.rb, line 968
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 962
def check_response(res)
unless res.success?
raise res.exception_class, res.message
end
end # File lib/net/smtp.rb, line 797 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 804
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 952
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 615 def do_finish quit if @socket and not @socket.closed? and not @error_occurred ensure @started = false @error_occurred = false @socket.close if @socket and not @socket.closed? @socket = nil end
# File lib/net/smtp.rb, line 603
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 544
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 and not s.closed?
@socket = nil
end
end # File lib/net/smtp.rb, line 937 def get_response(reqline) @socket.writeline reqline recv_response() end
# File lib/net/smtp.rb, line 928
def getok(reqline)
res = critical {
@socket.writeline reqline
recv_response()
}
check_response res
res
end # File lib/net/smtp.rb, line 1065 def logging(msg) @debug_output << msg + "\n" if @debug_output end
# File lib/net/smtp.rb, line 596 def new_internet_message_io(s) io = InternetMessageIO.new(s) io.read_timeout = @read_timeout io.debug_output = @debug_output io end
# File lib/net/smtp.rb, line 942
def recv_response
buf = ''
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 577 def ssl_socket(socket, context) OpenSSL::SSL::SSLSocket.new socket, context end
# File lib/net/smtp.rb, line 540 def tcp_socket(address, port) TCPSocket.open address, port end
# File lib/net/smtp.rb, line 581
def tlsconnect(s)
verified = false
s = ssl_socket(s, @ssl_context)
logging "TLS connection started"
s.sync_close = true
s.connect
if @ssl_context.verify_mode != OpenSSL::SSL::VERIFY_NONE
s.post_connection_check(@address)
end
verified = true
s
ensure
s.close unless verified
end
Ruby Core © 1993–2017 Yukihiro Matsumoto
Licensed under the Ruby License.
Ruby Standard Library © contributors
Licensed under their own licenses.