Рубрики
Без рубрики

smtplib – Клиент протокола простой передачи почты

Автор оригинала: Doug Hellmann.

Цель:

Взаимодействуйте с SMTP-серверами, включая отправку электронной почты.

smtplib включает класс SMTP , который можно использовать для связи с почтовыми серверами для отправки почты.

Примечание

Адреса электронной почты, имена хостов и IP-адреса в следующих примерах были скрыты, но в остальном стенограммы точно иллюстрируют последовательность команд и ответов.

Отправка электронного сообщения

Чаще всего SMTP используется для подключения к почтовому серверу и отправки сообщения. Имя хоста и порт почтового сервера можно передать в конструктор, или connect () можно вызвать явно. После подключения вызовите sendmail () с параметрами конверта и телом сообщения. Текст сообщения должен быть полностью сформирован и соответствовать RFC 5322, поскольку smtplib вообще не изменяет содержимое или заголовки. Это означает, что заголовки From и To должны быть добавлены вызывающей стороной.

smtplib_sendmail.py

import smtplib
import email.utils
from email.mime.text import MIMEText

# Create the message
msg  MIMEText('This is the body of the message.')
msg['To']  email.utils.formataddr(('Recipient',
                                    'recipient@example.com'))
msg['From']  email.utils.formataddr(('Author',
                                      'author@example.com'))
msg['Subject']  'Simple test message'

server  smtplib.SMTP('localhost', 1025)
server.set_debuglevel(True)  # show communication with the server
try:
    server.sendmail('author@example.com',
                    ['recipient@example.com'],
                    msg.as_string())
finally:
    server.quit()

В этом примере также включена отладка, чтобы показать связь между клиентом и сервером. В противном случае пример вообще не выдал бы никаких результатов.

$ python3 smtplib_sendmail.py

send: 'ehlo 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.
0.0.0.0.0.0.ip6.arpa\r\n'
reply: b'250-1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0
.0.0.0.0.0.0.ip6.arpa\r\n'
reply: b'250-SIZE 33554432\r\n'
reply: b'250 HELP\r\n'
reply: retcode (250); Msg: b'1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0
.0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa\nSIZE 33554432\nHELP'
send: 'mail FROM:
reply: b'250 OK\r\n'
reply: retcode (250); Msg: b'OK'
send: 'rcpt TO:\r\n'
reply: b'250 OK\r\n'
reply: retcode (250); Msg: b'OK'
send: 'data\r\n'
reply: b'354 End data with .\r\n'
reply: retcode (354); Msg: b'End data with .'
data: (354, b'End data with .')
send: b'Content-Type: text/plain;\r\nMIME-Ver
sion: 1.0\r\nContent-Transfer-Encoding: 7bit\r\nTo: Recipient \r\nFrom: Author \r\nSu
bject: Simple test message\r\n\r\nThis is the body of the messag
e.\r\n.\r\n'
reply: b'250 OK\r\n'
reply: retcode (250); Msg: b'OK'
data: (250, b'OK')
send: 'quit\r\n'
reply: b'221 Bye\r\n'
reply: retcode (221); Msg: b'Bye'

Второй аргумент sendmail () , получатели, передается в виде списка. В список можно включить любое количество адресов, чтобы сообщение доставлялось каждому из них по очереди. Поскольку информация о конверте отделена от заголовков сообщений, можно скрыть копию (BCC) кого-либо, включив их в аргумент метода, но не в заголовок сообщения.

Аутентификация и шифрование

Класс SMTP также обрабатывает аутентификацию и шифрование TLS (безопасность транспортного уровня), если сервер поддерживает их. Чтобы определить, поддерживает ли сервер TLS, вызовите ehlo () напрямую, чтобы идентифицировать клиента для сервера и спросить его, какие расширения доступны. Затем вызовите has_extn () , чтобы проверить результаты. После запуска TLS перед аутентификацией необходимо снова вызвать ehlo () . Многие поставщики услуг хостинга почты теперь поддерживают только подключения на основе TLS. Для связи с этими серверами используйте SMTP_SSL , чтобы начать с зашифрованного соединения.

smtplib_authenticated.py

import smtplib
import email.utils
from email.mime.text import MIMEText
import getpass

# Prompt the user for connection info
to_email  input('Recipient: ')
servername  input('Mail server name: ')
serverport  input('Server port: ')
if serverport:
    serverport  int(serverport)
else:
    serverport  25
use_tls  input('Use TLS? (yes/no): ').lower()
username  input('Mail username: ')
password  getpass.getpass("%s's password: " % username)

# Create the message
msg  MIMEText('Test message from PyMOTW.')
msg.set_unixfrom('author')
msg['To']  email.utils.formataddr(('Recipient', to_email))
msg['From']  email.utils.formataddr(('Author',
                                      'author@example.com'))
msg['Subject']  'Test from PyMOTW'

if use_tls  'yes':
    print('starting with a secure connection')
    server  smtplib.SMTP_SSL(servername, serverport)
else:
    print('starting with an insecure connection')
    server  smtplib.SMTP(servername, serverport)
try:
    server.set_debuglevel(True)

    # identify ourselves, prompting server for supported features
    server.ehlo()

    # If we can encrypt this session, do it
    if server.has_extn('STARTTLS'):
        print('(starting TLS)')
        server.starttls()
        server.ehlo()  # reidentify ourselves over TLS connection
    else:
        print('(no STARTTLS)')

    if server.has_extn('AUTH'):
        print('(logging in)')
        server.login(username, password)
    else:
        print('(no AUTH)')

    server.sendmail('author@example.com',
                    [to_email],
                    msg.as_string())
finally:
    server.quit()

Расширение STARTTLS не отображается в ответе на EHLO после включения TLS.

$ python3 source/smtplib/smtplib_authenticated.py
Recipient: doug@pymotw.com
Mail server name: localhost
Server port: 1025
Use TLS? (yes/no): no
Mail username: test
test's password:
starting with an insecure connection
send: 'ehlo 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0
.0.0.0.0.0.ip6.arpa\r\n'
reply: b'250-1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.
0.0.0.0.0.0.ip6.arpa\r\n'
reply: b'250-SIZE 33554432\r\n'
reply: b'250 HELP\r\n'
reply: retcode (250); Msg: b'1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.
0.0.0.0.0.0.0.0.0.0.0.0.0.0.ip6.arpa\nSIZE 33554432\nHELP'
(no STARTTLS)
(no AUTH)
send: 'mail FROM:
reply: b'250 OK\r\n'
reply: retcode (250); Msg: b'OK'
send: 'rcpt TO:\r\n'
reply: b'250 OK\r\n'
reply: retcode (250); Msg: b'OK'
send: 'data\r\n'
reply: b'354 End data with .\r\n'
reply: retcode (354); Msg: b'End data with .'
data: (354, b'End data with .')
send: b'Content-Type: text/plain;\r\n
MIME-Version: 1.0\r\nContent-Transfer-Encoding: 7bit\r\nTo:
Recipient \r\nFrom: Author 
\r\nSubject: Test from PyMOTW\r\n\r\nTest message from PyMOTW.
\r\n.\r\n'
reply: b'250 OK\r\n'
reply: retcode (250); Msg: b'OK'
data: (250, b'OK')
send: 'quit\r\n'
reply: b'221 Bye\r\n'
reply: retcode (221); Msg: b'Bye'

$ python3 source/smtplib/smtplib_authenticated.py
Recipient: doug@pymotw.com
Mail server name: mail.isp.net
Server port: 465
Use TLS? (yes/no): yes
Mail username: doughellmann@isp.net
doughellmann@isp.net's password:
starting with a secure connection
send: 'ehlo 1.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0.0
.0.0.0.0.0.ip6.arpa\r\n'
reply: b'250-mail.isp.net\r\n'
reply: b'250-PIPELINING\r\n'
reply: b'250-SIZE 71000000\r\n'
reply: b'250-ENHANCEDSTATUSCODES\r\n'
reply: b'250-8BITMIME\r\n'
reply: b'250-AUTH PLAIN LOGIN\r\n'
reply: b'250
reply: retcode (250); Msg: b'mail.isp.net\nPIPELINING\nSIZE
71000000\nENHANCEDSTATUSCODES\n8BITMIME\nAUTH PLAIN LOGIN\n
(no STARTTLS)
(logging in)
send: 'AUTH PLAIN AGRvdWdoZWxsbWFubkBmYXN0bWFpbC5mbQBUTUZ3MDBmZmF
reply: b'235 2.0.0 OK\r\n'
reply: retcode (235); Msg: b'2.0.0 OK'
send: 'mail FROM:
reply: b'250 2.1.0 Ok\r\n'
reply: retcode (250); Msg: b'2.1.0 Ok'
send: 'rcpt TO:\r\n'
reply: b'250 2.1.5 Ok\r\n'
reply: retcode (250); Msg: b'2.1.5 Ok'
send: 'data\r\n'
reply: b'354 End data with .\r\n'
reply: retcode (354); Msg: b'End data with .'
data: (354, b'End data with .')
send: b'Content-Type: text/plain;\r\n
MIME-Version: 1.0\r\nContent-Transfer-Encoding: 7bit\r\nTo:
Recipient \r\nFrom: Author 
\r\nSubject: Test from PyMOTW\r\n\r\nTest message from PyMOTW.
\r\n.\r\n'
reply: b'250 2.0.0 Ok: queued as A0EF7F2983\r\n'
reply: retcode (250); Msg: b'2.0.0 Ok: queued as A0EF7F2983'
data: (250, b'2.0.0 Ok: queued as A0EF7F2983')
send: 'quit\r\n'
reply: b'221 2.0.0 Bye\r\n'
reply: retcode (221); Msg: b'2.0.0 Bye'

Проверка адреса электронной почты

Протокол SMTP включает команду, чтобы спросить сервер, действителен ли адрес. Обычно VRFY отключен, чтобы спамеры не могли найти допустимые адреса электронной почты, но если он включен, клиент может запросить у сервера адрес и получить код состояния, указывающий действительность вместе с полным именем пользователя, если он доступен.

smtplib_verify.py

import smtplib

server  smtplib.SMTP('mail')
server.set_debuglevel(True)  # show communication with the server
try:
    dhellmann_result  server.verify('dhellmann')
    notthere_result  server.verify('notthere')
finally:
    server.quit()

print('dhellmann:', dhellmann_result)
print('notthere :', notthere_result)

Как показывают последние две строки вывода, адрес dhellmann действителен, а notthere – нет.

$ python3 smtplib_verify.py

send: 'vrfy \r\n'
reply: '250 2.1.5 Doug Hellmann \r\n'
reply: retcode (250); Msg: 2.1.5 Doug Hellmann 
send: 'vrfy \r\n'
reply: '550 5.1.1 ... User unknown\r\n'
reply: retcode (550); Msg: 5.1.1 ... User unknown
send: 'quit\r\n'
reply: '221 2.0.0 mail closing connection\r\n'
reply: retcode (221); Msg: 2.0.0 mail closing connection
dhellmann: (250, '2.1.5 Doug Hellmann ')
notthere : (550, '5.1.1 ... User unknown')

Смотрите также

  • стандартная библиотечная документация для smtplib
  • RFC 821 – спецификация простого протокола передачи почты (SMTP).
  • RFC 1869 – расширения службы SMTP для базового протокола.
  • RFC 822 – «Стандарт для формата текстовых интернет-сообщений ARPA», оригинал спецификация формата сообщения электронной почты.
  • RFC 5322 – «Формат Интернет-сообщения», обновление формата сообщения электронной почты.
  • email – стандартный библиотечный модуль для создания и анализа сообщений электронной почты.
  • smtpd – реализует простой SMTP-сервер.