Потенциальная ошибка рассинхронизации с почтовым ящиком

potencialnaya oshibka rassinhronizacii s pochtovym yashhikom Сервер PyMailCGI

Как мы видели в главе 14, даже в PyMailGUI номера некоторых сообщений могут оказаться неверными, если удалить почту другой программой в то время, когда открыт графический интерфейс, — например во втором экземпляре программы PyMailGUI или с помощью PyMailCGI. Такое также возможно, если сервер автоматически удаляет сообщения после загрузки списка, например в случае ошибки перемещает сообщения из папки входящих сообщений в папку недоставленных.

Именно по этой причине в PyMailGUI предусмотрен свой способ определения рассинхронизации с почтовым сервером при выполнении операций загрузки и удаления сообщений — с помощью пакета mailtools. Например, при выполнении операции удаления для оценки соответствия она сопоставляет хранящиеся в программе заголовки сообщений с заголовками на сервере. Аналогичная проверка выполняется при загрузке сообщений. При несовпадении автоматически выполняется повторная загрузка списка заголовков. К сожалению, без дополнительной информации PyMailCGI не может определять такие ошибки: в ней нет списка сообщений, который можно было бы сравнить со списком на сервере при выполнении операции просмотра или удаления, — только номер сообщения в ссылке или в скрытом поле формы.

В худшем случае PyMailCGI не сможет гарантировать, что операция удаления удалит именно выбранное сообщение, — маловероятно, но вполне возможно, что сообщение, присутствовавшее в списке, будет удалено в промежутке времени между моментом получения номеров сообщений и моментом вызова операции удаления сообщения на сервере. Без дополнительной информации, хранящейся на сервере, программа PyMailCGI не может использовать инструменты безопасного удаления или проверки синхронизации, имеющиеся в пакете mailtools, чтобы убедиться в корректности номеров сообщений.

Чтобы гарантировать невозможность ошибки при удалении, программе PyMailCGI необходим механизм сохранения информации о состоянии, который позволил бы отображать номера сообщений, передаваемые страницам, на хранящиеся заголовки сообщений, полученные, когда номера были определены в последний раз, или какое-то более широкое решение, позволяющее решить эту проблему полностью. В следующих трех разделах кратко описываются возможные улучшения, которые можно рассматривать как упражнения для самостоятельного решения.

Альтернатива: передача текста заголовка в скрытых полях (PyMailCGI_2.1)

Пожалуй, самый простой способ гарантировать точность операции удаления заключается во встраивании полного текста заголовков отображаемого сообщения в страницу просмотра сообщения в виде скрытого поля формы с применением следующей схемы:

onViewListLink.py

Встраивает текст заголовков в скрытое поле формы, экранируя его в соответствии с соглашениями HTML вызовом функции cgi.escape (с аргументом quote, установленным в значение True, чтобы обеспечить экранирование всех вложенных кавычек в тексте заголовка).

onViewPageAction.py

Извлекает встроенный текст заголовков из поля формы и передает его функции безопасного удаления в пакете mailtools для сопоставления с заголовками на сервере.

Для реализации этого решения не придется существенно изменять программный код, но может потребоваться загружать дополнительные заголовки в первом из этих сценариев (в настоящее время он загружает полный текст сообщения) и конструировать список всех заголовков сообщения (здесь удаляется одно сообщение и нужны заголовки только для одного сообщения). Как вариант текст заголовков можно было бы извлекать из полного текста сообщения, разбивая его по пустой строке, отделяющей заголовки от тела сообщения.

Кроме того, это решение может привести к увеличению объема данных, передаваемых между клиентом и сервером, — объем текста заголовков сообщения обычно превышает 1 Кбайт, а может быть еще больше. Это небольшой объем данных по современным меркам, но есть вероятность, что он может превысить ограничения, действующие на некоторых клиентах или серверах.

И действительно, данная схема недостаточно полна. Она решает только проблему ошибочного удаления не того сообщения и никак не затрагивает проблему рассинхронизации в целом. Например, система по-прежнему может пытаться загружать и отображать не те сообщения, что были выбраны в списке, после удаления на сервере более ранних сообщений, выполненного из другого места. Фактически, этот прием лишь гарантирует, что будет удалено именно то сообщение, которое отображалось в окне просмотра в момент выбора операции удаления. Он не гарантирует, что будет удалено или открыто в окне просмотра сообщение, выбранное в списке.

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

Несмотря на свою неполноту, это решение все же позволяет хотя бы избежать удаления не тех сообщений, если содержимое почтового ящика на сервере изменится во время просмотра сообщения, — удалено может быть только то сообщение, которое отображается в окне просмотра. Экспериментальная, но работающая реализация этой схемы находится в следующем каталоге в пакете с примерами для книги:

PP4E\Internet\Web\dev\PyMailCGI_2.1

Во время разработки эта версия опробовалась в веб-броузере Firefox, и для внедрения описанной схемы потребовалось изменить чуть больше 10 строк программного кода в трех файлах, перечисленных ниже (ищите изменения по строкам, помеченным комментарием «#EXPERI- MENTAL»):

#  onViewListLink.py

hdrstext = fulltext.split(‘\n\n’)[0] # использовать пустую строку commonhtml.viewpage( # шифрует пароль

msgnum, message, content, form, hdrstext, parts)

#  commonhtml.py

def viewpage(msgnum, headers, text, form, hdrstext, parts=[]):

#  операции удаления необходим текст заголовков, чтобы проверить

#  синхронизацию с почтовым ящиком: может иметь объем

#  в несколько килобайтов

hdrstext = cgi.escape(hdrstext, quote=True) # экранировать ‘"’ тоже print(‘<input type=hidden name=Hdrstext value="%s">’ % hdrstext)

# onViewPageAction.py

fetcher = mailtools.SilentMailFetcher(site, user, pswd) #fetcher.deleteMessages([msgnum]) hdrstext = getfield(form, ‘Hdrstext’) + ‘\n’

hdrstext = hdrstext.replace(‘\r\n’, ‘\n’) # получ. \n от top

dummyhdrslist = [None] * msgnum # только 1 заголов.

dummyhdrslist[msgnum-1] = hdrstext # в скрытом поле

fetcher.deleteMessagesSafely([msgnum], dummyhdrslist) # исключение commonhtml.confirmationpage(‘Delete’) # при рассинхрониз.

Чтобы опробовать эту версию локально, запустите сценарий веб-сервера, представленный в примере 15.1 (в главе 15), с именем подкаталога dev и уникальным номером порта, если собираетесь одновременно отработать обе версии, оригинальную и экспериментальную. Например:

C:\\PP4E\Internet\Web> webserver.py dev\PyMailCGI_2.1 9000 команда http://localhost:9000/pymailcgi.html URL для веб-броузера

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

Обратите внимание, что в большинстве случаев заголовка message-id вполне достаточно для идентификации сообщения в почтовом ящике при выполнении операции удаления, и между страницами можно было бы передавать только его. Однако поскольку этот заголовок является необязательным и может быть подделан, он не является надежным способом идентификации сообщений — для большей надежности необходимо сопоставление всех имеющихся заголовков. За дополнительной информацией обращайтесь к обсуждению пакета mailtools в главе 13.

Использованная литература:

Марк Лутц — Программирование на Python, 4-е издание, II том, 2011

Каталог сайтов Всего.ру
Оцените статью
Секреты программирования
Добавить комментарий