В этой статье
поговорим об отличии двух почтовых протоколов – POP
и IMAP – и о том, как их можно связать друг с
другом.
Мотивирующая картинка J
Почтовый
протокол POP3 используется только для
загрузки новых сообщений с почтового сервера. Он позволяет почтовому
клиенту подключиться к серверу лишь на период времени, который требуется для
загрузки сообщений.
Не допускается одновременное подключение нескольких почтовых клиентов к определенному ящику (разрешено только одно подключение).
Не допускается одновременное подключение нескольких почтовых клиентов к определенному ящику (разрешено только одно подключение).
При
использовании почтового протокола IMAP соединение не
разрывается до тех пор, пока ведется работа с ящиком в почтовой программе;
загрузка сообщений с сервера производится только по запросу почтового клиента.
Разрешен одновременный доступ нескольких почтовых клиентов к ящику, причем каждый из них может отслеживать изменения, производимые другими подключенными клиентами, а также статус всех сообщений (прочитано, отправлен ответ, удалено).
Разрешен одновременный доступ нескольких почтовых клиентов к ящику, причем каждый из них может отслеживать изменения, производимые другими подключенными клиентами, а также статус всех сообщений (прочитано, отправлен ответ, удалено).
Оба
эти протокола выполняют идентичные задачи по работе с почтой, только Pop3 позволяет скачивать все файлы одновременно, а IMAP – сначала список
файлов, а затем – по необходимости сами файлы.
Одна
из частых проблем при использовании протоколов Pop3 и IMAP, заключается в том, что многие разработчики пробуют реализовать эти протоколы под один интерфейс и с
одинаковой логикой. Это в корне неверно. Дело в том, что при использовании протокола IMAP мы
можем грузить не все письма, а только непрочитанные для примера. Для
того чтобы реализовать передачу данных, мы использовали в проекте библиотеку MailBee.NET. Пример реализации приема с помощью этой
библиотеки вы можете посмотреть здесь.
В примере по ссылке выше приведена реализация протокола POP3 и IMAP с использованием паттерна фабричный метод (Factory Method). В одной из таких реализаций мы использовали для
уникальности письма UID (уникальный идентификатор письма на сервере). Дело в том, что для IMAP и POP3 эти идентификаторы разные.
RFC 1939 (POP3): The unique-id of a message is
an arbitrary server-determined string, consisting of one to 70 characters in
the range 0x21 to 0x7E, which uniquely identifies a message within a maildrop
and which persists across sessions.
RFC 3501
(IMAP): (Unique
Identifier (UID) Message Attribute is) a 32-bit value assigned to each message,
which when used with the unique identifier validity value (see below) forms a
64-bit value that MUST NOT refer to any other message in the mailbox or any
subsequent mailbox with the same name forever.
В итоге имеем,
что для POP3 UID представляет
собой строку от одного до 70 символов, а для IMAP
это
целое 32 битное число.
Давайте
посмотрим примеры отличия UIDs. Примеры
отличия для POP3:
pop.meta.ua − UID8863-1253275464
pop3.ukr.net −
1356790973352505619
Пример UIDs для IMAP:
imap.ukr.net − 1432
Теоретически можно написать почтовый сервер, который поддерживает как IMAP, так и POP3, и использовать тот же UID для обоих протоколов, но я не знаю ни одного сервера,
который будет на самом деле сделать это. На практике вы должны относиться к POP3-идентификаторам и IMAP-идентификаторам как несвязанным значениям. Вроде, все логично, но что делать, если мы работали с одним ящиком, который
поддерживает и POP3 и IMAP. И
вы в своей программе даете возможность указать пользователю, какой протокол для
получения почты он хочет использовать. Если мы использовали, например, pop3.ukr.net, а затем решили
переключиться на использование imap.ukr.net, то мы получим
кучу проблем на свою голову. Потому что если посмотреть выше пример по отличию
в UID для этих двух протоколов для ukr.net, то увидим
основную проблему. А именно: дублирование некоторых писем. Если пользователю не
важен факт загрузки писем на свой диск, то этим фактом можно пренебречь, но
если факт доставки почты является существенным, этот факт нельзя игнорировать.
Поэтому многие разработчики реализуют работу с протоколом IMAP как с протоколом POP3.
Как же можно
"подружить" эти два протокола? После долгих размышлений с коллегами
на работе и безуспешного поиска в интернете решений связывания писем, которые
получены через протокол POP3, с письмами,
которые получены через протокол IMAP, мы пришли к
решению, как можно победить связывание писем, если у них присутствуют вложения.
Первым делом нам необходимо завести базу для хранения идентификатора настроек
почты (Например, ссылка на таблицу настроек почты, в которой хранятся адрес,
порт, пароль и т.д.), уникальный идентификатор сообщения UID и дата получения письма. Этот способ
поможет нам идентифицировать сообщения, полученные от конкретного почтового
сервера для конкретного пользователя.
Следующим шагом
нам необходимо создать таблицудля хранения хеш-файлов, которые идут в письме (attachment). Для того чтобы посчитать хеш, можем
воспользоваться стандартными способами, доступными в .NET
Framework. Например, чтобы использовать MD5, воспользуемся классом MD5CryptoServiceProvider() или другим
хеш-алгоритмом из доступных HashAlgorithm на MSDN, или, по желанию, можно написать свой алгоритм.
Основная суть заключается в том, что если вы храните у себя вложения, которые
идут в письмах, вы можете хранить только те, которые не дублируются. Как это
работает: вы загрузили письмо из сервера, посчитали его хеш, проверили, есть ли
такой в таблице хешей, если такого файла нет в данной таблице, мы сохраняем
посчитанный хеш для данного файла в БД, чтобы при переключении протокола не
грузить этот файл повторно. После этого если мы используем протокол IMAP, можем уведомить сервер о том, что это письмо мы
прочитали, и при последующем запросе он не будет нам его возвращать.
Огромный минус данного подхода − необходимость загрузки полученных писем целиком, это
может занять продолжительное время, в зависимости от пропускной способности
интернет-канала. В таком случае мы выравниваем протокол POP3 с IMAP и можем
использовать лишь малую часть из возможностей, которые предоставляет нам
современный протокол IMAP. Но мы
справляемся с проблемой хранения дубликатов. Если у вас есть возможность не
рассматривать переключение с POP3 на IMAP в рамках одного почтового ящика, вас эта
проблема не коснется. Вы сможете использовать всю мощь протокола IMAP, а для отдельных случаев оставить POP3 таким, каким он есть.
Примечание:
некоторые разработчики могут подумать, почему для таких целей не разрулить по
дате получения DataReceived. В библиотеке MailBee.Net
в
классе MailMessage, который хранит информацию о письме,
есть поле DateReceived, которое содержит дату получения письма
на почту MailMessage.DateReceived Property. Но с этим
полем есть проблема: оно не подходит для протокола IMAP.
Для IMAP необходимо использовать свойство Envelope.DateReceived Property, но если
информация не доступна по какой то причине, то оба эти свойства с DateReceived будет содержаться MinValue. Поэтому даже
если у нас поде DateReceived не равно
минимальной дате, не факт, что после смены протокола с POP3
на IMAP эта дата будет идентична. Одна из
проблем, которая была раньше с MailBee.NET, – что дата DateReceived отличалась на один час от реального времени Incorrect Date Received Time. Поэтому еще
раз хотелось бы вам посоветовать, что если есть возможность рассматривать эти
два протокола как отдельные уникальные протоколы, то у вас станет на одну
проблему меньше. В противном случае вам придется связывать эти два протокола,
что чревато "костылями", которые если и будут работать, то с огромной
натяжкой.
No comments:
Post a Comment