Как отправить электронное письмо на адреса с не-ASCII символами в Python? - PullRequest
0 голосов
/ 02 сентября 2018

Используя модули email и smtplib в Python 3.x, после большого объема исследований я могу отправлять электронные письма с темами Unicode, текстовыми телами и именами (как для отправителя, так и для получателей), которые это здорово, но это не позволит мне отправлять электронные письма на адреса, которые сами содержат символы Unicode (или другие символы, не входящие в ASCII). Похоже, что он не поддерживается (если вы посмотрите на комментарии в email.utils, он говорит так же: т.е. «Адрес ДОЛЖЕН (в соответствии с RFC) быть ascii, поэтому выведите UnicodeError, если это не так».) Любые попытки сделать это в любом случае (включая, но не только, получателей BCC - чтобы обойти ограничения заголовка сообщения) не удалось с той или иной формой ошибки Unicode. В комментарии не говорится, какой RFC (я не думаю, что все они указывают, что адреса электронной почты должны использовать только ASCII.)

Есть ли другой способ сделать это, так как, по слухам, подобные адреса могут существовать в некоторых местах: úßerñame@dómain.com? Я имею в виду, есть ли другие почтовые модули, которые поддерживают это?

Если предпосылка моего вопроса неверна, предназначены ли адреса электронной почты для всего мира как ASCII (несмотря на то, что, по слухам, некоторые из них используют другие символы)?

Я вижу этот вопрос для других языков, но не для Python.

1 Ответ

0 голосов
/ 02 сентября 2018

предназначены ли адреса электронной почты для всего мира как ASCII?

Нет; на самом деле, с точностью до наоборот. Адрес электронной почты был только для ASCII. Они предназначены , чтобы стать Юникодом, и мы на пути к этому; это был медленный переход.


В современной электронной почте адрес электронной почты состоит из двух частей: 1 имя хоста DNS (часть после @) и почтовый ящик на этом хосте (часть до @) ). Они регулируются совершенно другими стандартами, потому что DNS должен работать для HTTP и всех других вещей, кроме только электронной почты.


DNS был последний раз обновлен в 1987 году в RFC 1035 , который предписывает ограниченное подмножество ASCII (а также нечувствительность к регистру).

Однако IDNA (интернационализированные доменные имена для приложений), указанные в RFC 5890 , позволяет приложениям дополнительно отображать гораздо большую часть набора символов Unicode в DNS-имена для представления пользователю.

Итак, вы не можете иметь доменное имя dómain.com. Но вы можете иметь доменное имя xn--dmain-0ta.com. И многие приложения будут принимать dómain.com от пользовательского ввода и автоматически переводить его, а также принимать xn--dmain-0ta.com из сети и отображать его как dómain.com. 2

В Python некоторые библиотеки для интернет-протоколов будут автоматически кодировать доменные имена для вас; иначе не будет. Если они этого не делают, вы можете сделать это вручную, например так:

>>> 'dómain.com'.encode('idna')
b'xn--dmain-0ta.com'

Обратите внимание, что в 3.x это bytes, а не str; если вам нужен str, вы всегда можете сделать это:

>>> 'dómain.com'.encode('idna').decode('ascii')
'xn--dmain-0ta.com'

Имена почтовых ящиков определяются SMTP, последние определены в RFC 5321 и RFC 5322 , которые дают понять, что принимающий хост должен полностью интерпретировать "локальный" часть "адреса. Например, большинство почтовых серверов используют имена без учета регистра; многие допускают «добавление тегов» (например, shule@gmail.com и shule+so@gmail.com - это один и тот же почтовый ящик); некоторые (например, gmail) игнорируют все точки; и т.д.

Проблема в том, что SMTP никогда не указывал, какой набор символов используется для заголовков. Традиционные SMTP-серверы были только 7-битными ASCII, поэтому до недавнего времени ASCII можно было использовать только в заголовках и, следовательно, в именах почтовых ящиков.

EAI (Интернационализация адресов электронной почты), как указано в RFC 6530 и связанных предложениях, позволяет согласовывать UTF-8 в сеансах SMTP. В сеансе UTF-8 заголовки и адреса в этих заголовках интерпретируются как UTF-8. (Кодирование IDNA имени хоста не обязательно, но все же разрешено.)

Это замечательно, но что если ваш клиент, ваш сервер, сервер вашего получателя или какие-либо ретранслирующие серверы по пути не говорят по SMTPUTF8? Чтобы справиться с этим делом, каждый, у кого есть почтовый ящик UTF-8, также имеет имя ASCII для этого почтового ящика. В идеале это отправляется вместе с сообщением, и последняя программа SMTPUTF8 в цепочке переключается на замену ASCII, когда она встречает первую программу не-SMTPUTF8. Чаще всего он просто получает сообщение об ошибке и передает его обратно пользователю для обработки вручную. 3

Идея состоит в том, что в конечном итоге большинство хостов в Интернете будут использовать SMTPUTF8, поэтому вы можете быть úßerñame@dómain.com, но в то же время ваш сервер на dómain.com имеет úßerñame и ussernyame в качестве псевдонимов для одного и того же почтового ящика. Любой, кто не может справиться с SMTPUTF8, увидит вас (и должен будет обратиться к вам) как ussernyame. (Их почтовый клиент, на самом деле, будет видеть вас как ussernyame@xn--dmain-0ta.com, но он может исправить эту последнюю часть; он ничего не может сделать с первой частью, если он был потерян при транспортировке.)

По состоянию на середину 2018 года большинство хостов не говорят на SMTPUTF8, как и многие клиентские библиотеки.

Начиная с Python 3.5, 4 стандартная библиотека smtplib поддерживает SMTPUTF8. Если вы используете высокоуровневую функцию sendmail:

Если SMTPUTF8 включено в mail_options и сервер поддерживает его, from_addr и to_addrs могут содержать символы не ASCII.

Итак, вы делаете что-то вроде этого:

try:
    server.sendmail([fromaddr], [toaddr], msg, mail_options=['SMTPUTF8'])
except SMTPNotSupportedError:
    server.sendmail([fromaddr_ascii], [toaddr_ascii], msg)

(Теоретически лучше проверить ответ EHLO с помощью has_extn, но на практике просто попытка сделать это кажется более плавной. Это может измениться с будущими улучшениями в серверной экосистеме и / или smptlib.)

Где вы взяли это fromaddr_ascii и toaddr_ascii? Это зависит от вашей программы. Часть DNS, вы просто используете IDNA, но для части почтового ящика такого правила нет; Вы должны знать альтернативное имя почтового ящика ASCII. Может быть, вы спросите пользователя. Возможно, у вас есть база данных, в которой хранятся контакты как с EAI, так и с традиционными адресами. Возможно, вас беспокоит только один конкретный домен, и вы знаете, что он использует какое-то правило, которое вы можете реализовать.


1. На самом деле, в addr-spec есть две части; адрес - это addr-spec плюс необязательное отображаемое имя и комментарий. Но не бери в голову.

2. Есть несколько исключений. Например, если вы наберете http://staсkoverflow.com, ваш браузер может предупредить вас, что кириллический строчный Es вместо латинского строчного Cee может быть попыткой угона. Или, если вы попытаетесь перейти к http://dómain.com, на странице ошибки, сообщающей, что домен не существует, вероятно, будет отображаться xn--dmain-0ta.com, поскольку это более полезно для отладки.

3. Это одна из тех вещей, которые, будем надеяться, со временем станут лучше, но вполне могут стать недостаточно хорошими, пока после этого все равно не будет иметь значения…

4. Что если вы используете Python 3.4 или 2.7? Тогда у вас нет поддержки SMTPUTF8. Обновите, найдите вместо smtplib стороннюю библиотеку или напишите собственный SMTP-код.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...