В данный момент нашего гипотетического взаимодействия с веб-приложением PyMailCGI мы просматриваем почтовое сообщение (рис. 16.12), выбранное на странице со списком. Если, находясь на странице просмотра сообщения, выбрать в раскрывающемся списке некоторое действие и щелкнуть на кнопке Next (Далее), на сервере будет вызван сценарий, представленный в примере 16.9, который выполнит операцию создания ответа, пересылки или удаления для просматриваемого сообщения.
Пример 16.9. PP4E\Internet\Web\PyMailCgi\cgi-bin\onViewPageAction.py
#!/usr/bin/python
############################################################################
Вызывается при отправке формы в окне просмотра сообщения: выполняет выбранное действие=(fwd, reply, delete);
в 2.0+ повторно используется логика удаления в пакете mailtools, первоначально реализованная для PyMailGUI;
############################################################################
import cgi, commonhtml, secret
from externs import mailtools, mailconfig from commonhtml import getfield
def quotetext(form):
обратите внимание, что заголовки поступают из формы предыдущей страницы, а не получаются в результате повторного анализа почтового сообщения; это означает, что функция commonhtml.viewpage должна передавать дату в скрытом поле parser = mailtools.MailParser()
addrhdrs = (‘From‘, ‘To‘, ‘Cc‘, ‘Bcc‘) # декодируется только имя
quoted = ‘\n——————- Original Message \n’
for hdr in (‘From’, ‘To’, ‘Date’):
rawhdr = getfield(form, hdr)
if hdr not in addrhdrs:
dechdr = parser.decodeHeader(rawhdr) # 3.0: декод. для отображ.
else: # закодиров. при отправке
dechdr = parser.decodeAddrHeader(rawhdr) # только имена в адресах
quoted += ‘%s: %s\n’ % (hdr, dechdr)
quoted += ‘\n’ + getfield(form, ‘text’)
quoted = ‘\n’ + quoted.replace(‘\n’, ‘\n> ‘)
return quoted
form = cgi.FieldStorage() # извлечь данные из формы или из URL
user, pswd, site = commonhtml.getstandardpopfields(form)
pswd = secret.decode(pswd)
try:
if form[‘action’].value == ‘Reply’:
headers = {‘From’: mailconfig.myaddress, # 3.0: декодирование
‘To’: getfield(form, ‘From’), # выполняет commonhtml
‘Cc’: mailconfig.myaddress,
‘Subject’: ‘Re: ‘ + getfield(form, ‘Subject’)}
commonhtml.editpage(‘Reply’, headers, quotetext(form)) elif form[‘action’].value == ‘Forward’:
headers = {‘From’: mailconfig.myaddress, # 3.0: декодирование
‘To’: », # выполняет commonhtml
‘Cc’: mailconfig.myaddress,
‘Subject’: ‘Fwd: ‘ + getfield(form, ‘Subject’)} commonhtml.editpage(‘Forward’, headers, quotetext(form))
elif form[‘action’].value == ‘Delete’: # поле mnum необходимо здесь msgnum = int(form[‘mnum’].value) # но не eval(): может быть код fetcher = mailtools.SilentMailFetcher(site, user, pswd) fetcher.deleteMessages([msgnum]) commonhtml.confirmationpage(‘Delete’)
else:
assert False, ‘Invalid view action requested’
except:
commonhtml.errorpage(‘Cannot process view action’)
Этот сценарий получает всю информацию о выбранном сообщении в виде полей формы (некоторые из них могут быть скрытыми и зашифрованными) вместе с именем выбранного действия. Следующий шаг зависит от выбранного действия:
Дей ст вия Reply (От ве тить) и Forward (Пере слать)
Генерируют страницу редактирования сообщения, в которой строки исходного сообщения автоматически цитируются с указанием символа > перед каждой из них.
Дей ст вие Delete (Уда лить)
Вызывает немедленное удаление просматриваемого сообщения с помощью инструмента, импортированного из пакета mailtools из главы 13.
Во всех этих действиях используются данные, передаваемые из формы предыдущей страницы, но только для действия Delete (Удалить) требуются имя пользователя и пароль и декодирование полученного пароля (они поступают из скрытых полей формы в разметке HTML, сгенерированной предыдущей страницей).
Ответ и пересылка
Если в качестве очередного действия выбрать операцию Reply (Ответить), сценарий сгенерирует страницу редактирования сообщения, изображенную на рис. 16.16. Текст сообщения на этой странице можно редактировать, а при нажатии кнопки Send (Отправить) запускается сценарий отправки почты, который мы видели в примере 16.4. Если все пойдет удачно, будет получена та же страница подтверждения, которую мы получали раньше при создании нового сообщения (рис. 16.4).
Операция Forward (Переслать) осуществляется практически так же, за исключением некоторых отличий в заголовках сообщений. Всю эту работу мы получаем «бесплатно», так как страницы Reply (Ответить) и Forward (Переслать) генерируются вызовом commonhtml.editpage — той же утилиты, с помощью которой создается страница составления нового сообщения. Мы просто передаем этой утилите готовые строки заголовков (например, при создании ответа к тексту темы добавляется приставка «Re:»). Такого же рода прием с повторным использованием применялся в PyMailGUI, но в ином контексте. В PyMailCGI один сценарий обрабатывает три страницы; в PyMailGUI один суперкласс и одна функция обратного вызова обрабатывает три кнопки, но архитектура аналогична.
Рис. 16.16. Страница PyMailCGI создания ответа
Удаление
При выборе операции Delete (Удалить) в странице просмотра сообщения после щелчка на кнопке Next (Далее) сценарий onViewPageAction немедленно удалит просматриваемое сообщение. Удаление осуществляется путем вызова повторно используемой вспомогательной функции удаления, реализованной в пакете mailtools (см. главу 13). В предыдущей версии вызов этой утилиты был заключен в вызов commonhtml.runsilent, который предотвращает вывод данных функциями print в поток HTML ответа (они являются лишь сообщениями о состоянии, а не кодом разметки HTML). В этой версии тот же эффект достигается за счет использования классов Silent из пакета mailtools. На рис. 16.17 показана операция удаления в действии.
Рис. 16.17. Страница PyMailCGI просмотра сообщений, выбрана операция удаления
Между прочим, обратите внимание на присутствие вложений различных типов на рис. 16.17. В версии 3.0 можно отправлять только текстовые вложения из-за ухудшенной поддержки выгрузки файлов через CGI в Python 3.1, описывавшейся выше, но мы по-прежнему можем просматривать произвольные вложения во входящих сообщениях, получаемых от других отправителей. В этом сообщении присутствуют изображения и документ PDF. Такие вложения открываются в соответствии с настройками броузера — на рис. 16.18 показано, как Chrome обрабатывает щелчок на ссылке monkeys.jpg в нижней части страницы PyMailCGI, изображенной на рис. 16.17. Это то же самое изображение, что мы отправляли по протоколу FTP в главе 13 и с помощью PyMailGUI в главе 14, но здесь оно извлекается с помощью сценария CGI приложения PyMailCGI и возвращается локальным веб-сервером.
Рис. 16.18. В PyMailCGI было выбрано вложение с изображением
Вернемся к операции удаления. Как отмечалось, операция удаления является единственной, использующей данные учетной записи POP (пользователь, пароль и сайт), переданные в скрытых полях из предыдущей страницы просмотра сообщения. Напротив, операции создания ответа и пересылки формируют страницу редактирования, которая в конечном счете отошлет сообщение серверу SMTP — никакие данные POP им не требуются и не передаются.
Но к этому моменту пароль POP накрутил в своем перемещении не одну милю. В действительности он мог пройти по телефонным линиям, спутниковым каналам связи и пересечь целые континенты, путешествуя с компьютера на компьютер. Его маршрут описывается ниже:
1. Ввод (клиент): пароль начинает свою жизнь с ввода на странице регистрации у клиента (или встраивания в явный адрес URL) в незашифрованном виде. При вводе в форму в веб-броузере каждый символ отображается в виде звездочки (*).
2. Загрузка списка сообщений (от клиента через CGI-сервер на POP-сервер): затем он передается от клиента сценарию CGI на сервере, который пересылает его вашему POP-серверу для загрузки списка почтовых сообщений. Клиент посылает пароль в незашифрованном виде.
3. Страница со списком URL (CGI-сервер клиенту): Для управления поведением следующего сценария пароль встраивается в саму веб-страницу со списком для выбора почтовых сообщений в виде параметров запроса URL гиперссылок, зашифрованный и экранированный в соответствии с правилами URL.
4. Загрузка сообщения (от клиента через CGI-сервер на POP-сервер): Когда сообщение выбирается в списке, пароль посылается следующему сценарию, указанному в адресе URL, — сценарий CGI расшифровывает его и посылает POP-серверу для загрузки выбранного сообщения.
5. Поля на странице просмотра (CGI-сервер клиенту): Для управления поведением следующего сценария пароль встраивается в саму страницу просмотра в виде скрытых полей ввода, в зашифрованном виде и экранированный в соответствии с правилами HTML.
6. Удаление (от клиента через CGI-сервер на POP-сервер): Наконец, пароль снова передается от клиента на сервер CGI, на этот раз в виде значений скрытых полей формы — сценарий CGI расшифровывает его и посылает серверу POP для удаления сообщения.
Попутно сценарии передавали пароль между страницами как параметр запроса в адресе URL или как скрытое поле ввода HTML — в том и другом случае всегда передается зашифрованная строка, и никогда в одной операции не передаются одновременно незашифрованный пароль и имя пользователя. При запросе операции удаления перед передачей серверу POP пароль должен быть расшифрован с помощью модуля secret. Если сценарий смог снова обратиться к серверу POP и удалить выбранное сообщение, появляется еще одна страница подтверждения, которая показана на рис. 16.19 (в настоящее время операция удаления не спрашивает подтверждения, так что будьте внимательны).
Рис. 16.19. Подтверждение удаления в PyMailCGI
Одна тонкость: при выполнении операций создания ответа и пересылки сценарий действий с почтой onViewPageAction цитирует исходное сообщение, добавляя символ > в начало каждой строки и вставляя исходные строки заголовков «From:», «To:» и «Date:» перед исходным текстом сообщения. Обратите, однако, внимание, что заголовки исходных сообщений берутся из формы ввода, а не за счет выполнения синтаксического анализа исходного сообщения (в этот момент сообщение недоступно непосредственно). Иными словами, сценарий берет значения заголовков из полей ввода формы на странице просмотра. Так как поля «Date:» на странице просмотра нет, дата исходного сообщения также передается сценарию вместе с сообщением в виде скрытого поля ввода, чтобы не загружать сообщение заново. Попробуйте проследить по программному коду в листингах этой главы, как даты перемещаются с одной страницы на другую.
Использованная литература:
Марк Лутц — Программирование на Python, 4-е издание, II том, 2011