Как обычно, страница HTML редактирования сообщения, изображенная на рис. 16.3, ссылается на свой сценарий-обработчик. Если щелкнуть на кнопке Send (Отправить), на сервере будет запущен сценарий, представленный в примере 16.4, который обработает введенные нами данные и отправит почтовое сообщение.
Пример 16.4. PP4E\Internet\Web\PyMailCgi\cgi-bin\onEditPageSend.py
#!/usr/bin/python
############################################################################ Вызывается при отправке формы в окне редактирования: завершает составление нового сообщения, ответа или пересылаемого сообщения;
в 2.0+: мы повторно используем инструменты из пакета mailtools
для конструирования и отправки сообщения вместо устаревшей схемы, основанной на строковых методах; из этого модуля мы также наследуем возможность добавлять вложения и преобразовывать отправляемые сообщения в формат MIME;
3.0: выгрузка через CGI двоичных вложений и текстовых вложений
в несовместимой кодировке не допускается из-за ограничений модуля cgi
в py3.1, поэтому мы просто используем здесь кодировку по умолчанию
для данной платформы (механизм синтаксического анализа, используемый модулем cgi, не может предложить ничего лучше);
3.0: кроме того, для основного текста и для вложений используются простые правила кодирования Юникода;
############################################################################
import cgi, sys, commonhtml, os
from externs import mailtools
savedir = ‘partsupload’
if not os.path.exists(savedir):
os.mkdir(savedir)
def saveAttachments(form, maxattach=3, savedir=savedir)
сохраняет выгруженные файлы вложений в локальных файлах на сервере, откуда mailtools будет добавлять их в сообщение; класс FieldStorage в 3.1 и другие компоненты модуля cgi могут вызывать появление ошибки для многих типов выгружаемых файлов, поэтому мы не будем прилагать особых усилий, чтобы попытаться определить корректную кодировку символов;
partnames = []
for i in range(1, maxattach+1):
fieldname = ‘attach%d’ % i
if fieldname in form and form[fieldname].filename:
fileinfo = form[fieldname] # передана и заполнена?
filedata = fileinfo.value # прочитать в строку
filename = fileinfo.filename # путь на стороне клиента
if ‘\\’ in filename:
basename = filename.split(‘\\’)[-1] # для клиентов DOS
elif ‘/’ in filename:
basename = filename.split(‘/’)[-1] # для клиентов Unix else:
basename = filename # видимо, путь отсутствует
pathname = os.path.join(savedir, if isinstance(filedata, str):
filedata = filedata.encode() savefile = open(pathname, ‘wb’) savefile.write(filedata) savefile.close() os.chmod(pathname, 0o666) partnames.append(pathname)
return partnames
#commonhtml.dumpstatepage(0)
form = cgi.FieldStorage() # извлечь данные из формы
attaches = saveAttachments(form) # cgi.print_form(form), чтобы посмотреть
# имя сервера из модуля или из URL, полученного методом GET smtpservername = commonhtml.getstandardsmtpfields(form)
# здесь предполагается, что параметры получены из формы или из URL from commonhtml import getfield # для получения значений атрибутов From = getfield(form, ‘From’) # пустые поля не должны отправляться To = getfield(form, ‘To’)
Cc = getfield(form, ‘Cc’)
Subj = getfield(form, ‘Subject’)
text = getfield(form, ‘text’)
if Cc == ‘?’: Cc = »
# 3.0: не—ascii заголовки кодируются в utf8 в пакете mailtools
parser = mailtools.MailParser()
Tos = parser.splitAddresses(To) # списки получателей: разделитель ‘,’
Ccs = (Cc and parser.splitAddresses(Cc)) or »
extraHdrs = [(‘Cc’, Ccs), (‘X-Mailer’, ‘PyMailCGI 3.0’)]
# 3.0: определить кодировку для основного текста и текстовых вложений;
# по умолчанию=ascii в mailtools
bodyencoding = ‘ascii’
try:
text.encode(bodyencoding) # сначала попробовать ascii (или latin-1?) except (UnicodeError, LookupError): # иначе использ. utf8 (или из настроек?) bodyencoding = ‘utf-8’ # что сделать: это более ограниченное
# решение, чем в PyMailGUI
# 3.0: использовать utf8 для всех вложений;
# здесь мы не можем спросить у пользователя
attachencodings = [‘utf-8’] * len(attaches) # игнорировать
# нетекстовые части
# кодировать и отправить
sender = mailtools.SilentMailSender(smtpservername)
try:
sender.sendMessage(From, Tos, Subj, extraHdrs, text, attaches, bodytextEncoding=bodyencoding, attachesEncodings=attachencodings)
except:
commonhtml.errorpage(‘Send mail error’)
else:
commonhtml.confirmationpage(‘Send mail’)
Этот сценарий получает входную информацию из заголовков почты и текст из формы страницы редактирования (или из параметров запроса в адресе URL) и отправляет сообщение с помощью стандартного модуля Python smtplib посредством пакета mailtools. Мы подробно изучили пакет mailtools в главе 13, поэтому я не стану подробно говорить о нем сейчас. Однако обратите внимание, что благодаря повторному использованию его функции отправки отправленное сообщение автоматически сохраняется в файле sentmail.txt на сервере — в PyMailCGI отсутствуют инструменты для просмотра содержимого этого файла, но он может служить своеобразным журналом.
Появившаяся в версии 2.0 функция saveAttachments получает все файлы, отправленные броузером, и сохраняет их в локальных файлах во временном каталоге на сервере. Позднее, при отправке, эти файлы будут вложены в почтовое сообщение. Мы подробно рассматривали механизм выгрузки файлов на сервер через CGI в конце главы 15 — там вы найдете объяснение, как действует программный код здесь (а также ограничения в Python 3.1 и в этом издании — из-за этих ограничений мы можем прикреплять к сообщениям только простой текст). Непосредственное прикрепление файлов к электронному письму осуществляется пакетом mailtools автоматически.
Утилита commonhtml в конечном счете получает имя сервера SMTP, который примет сообщение, из модуля mailconfig или входных данных сценария (в поле формы или в параметре запроса URL). Если все пройдет успешно, мы получим сгенерированную страницу подтверждения, как на рис. 16.4.
Откройте файл sentmail.txt в исходном каталоге программы PyMailCGI, если у вас появится желание увидеть, как выглядит полный исходный текст отправленного сообщения (или получите сообщение в клиенте электронной почты с возможностью просмотра исходного текста письма, в таком как PyMailGUI). В этой версии каждое вложение кодируется в кодировке UTF-8 и преобразуется в формат MIME Base64, но основная текстовая часть отправляется как простой текст ASCII, если это возможно.
Как будет показано далее, этот же сценарий используется для отправки от ве та и пере сыл ки входящего письма по другому адресу. Пользовательский интерфейс этих операций немного отличается от интерфейса составления нового письма. Но, как и в программе PyMailGUI, логика обработчика, выполняющего отправку, была оформлена в виде общего совместно используемого программного кода — отправка ответа и пересылка по другому адресу, по сути, такие же операции отправки писем, включающих цитированный текст и предустановленные заголовки.
Ыэ| а I ‘ в PyMailCGI: Confirm- х
<- С ft i? http://localhost:8000/cgi-bin/onEditPageSend.py ► □’ Л’
PyMailCGI Confirmation
Send mail operation was successful
Press the link below to return to the main page.
Back to root page
Рис. 16.4. Страница PyMailCGI подтверждения отправки
Обратите внимание, что здесь не видно ни имени пользователя, ни пароля: как отмечалось в главе 13, для SMTP обычно нужен только сервер, который прослушивает порт SMTP, а не учетная запись пользователя или пароль. В той же главе мы видели, что неудачные операции отправки по протоколу SMTP либо возбуждают исключительную ситуацию Python (например, если нельзя установить соединение с хостом сервера), либо возвращают словарь, содержащий получателей, почту которым не удалось отправить. Пакет mailtools избавляет нас от необходимости помнить эти тонкости, всегда возбуждая исключение.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, II том, 2011