| ||
Этот документ - часть моего отчёта по лабораторной работе, цель которой состояла в реализации простого клиента IMAP4 rev1. Глубже, чем было нужно по работе, я в IMAP не лез. Когда в тексте упоминается "проект" речь идёт о написанной мною реализации. Также в тексте периодически встречаются упоминания "pochta.ru", это бесплатный почтовый сервис http://pochta.ru, один из немногих, предоставляющих доступ к ящику по протоколу IMAP4. Проект тестировался только с pochta.ru, с ними работает нормально.
Описание несколько упрощено, но зато общая идея становится ясна быстро и без потерь. Заранее признаю - не всё в стандарте IMAP4 мне понятно, но всё было и не нужно. К счастью. То, что описано - понятно.
Чтобы не соблазнять спамеров, встречающиеся в тексте почтовые и IP адреса изменены.
Протокол IMAP4 rev 1 описан в RFC 2060, принятом в 1996 году. IMAP предназначен для управления письмами, хранящимися на почтовом сервере, аббревиатура расшифровывается как Internet Message Access Protocol.
Протокол довольно сложный, имеет 24 стандартные команды (сравните с 12-ю командами POP3), плюс возможны расширения.
Помимо обычных и интуитивно понятных типов данных, RFC 2060 описывает два типа строк:
Более точное определение строк:
string ::= quoted / literal quoted ::= <"> *QUOTED_CHAR <"> QUOTED_CHAR ::= <any TEXT_CHAR except quoted_specials> / "\" quoted_specials quoted_specials ::= <"> / "\" TEXT_CHAR ::= <any CHAR except CR and LF> CHAR ::= <any 7-bit US-ASCII character except NUL, 0x01 - 0x7f> literal ::= "{" number "}" CRLF *CHAR8 ;; Number represents the number of CHAR8 octets CHAR8 ::= <any 8-bit octet except NUL, 0x01 - 0xff> |
Соединение устанавливается клиентом, используется протокол TCP, 143-й порт. Клиент посылает серверу команды (commands), сервер посылает клиенту ответы (responses). Команды выглядят так:
тег команда <аргументы> CRLF
|
Где тег - любая последовательность букв, цифр и ещё некоторых символов, уникальная в пределах сессии. Ответы сервера относятся к одной из трёх групп:
Типичный обмен сообщениями выглядит так:
> произвольный_тег команда <аргументы> CRLF
< * какие-то данные CRLF
< * ещё какие-то данные CRLF
< тот_же_самый_тег OK команда Completed CRLF
|
Неуспешное выполнение:
> произвольный_тег команда <аргументы> CRLF
< * какие-то данные CRLF
< * ещё какие-то данные CRLF
< тот_же_самый_тег NO у вас документов нет CRLF
|
В случае передачи литеральной строки на сервер:
> произвольный_тег команда <аргументы> {длина}CRLF < + go ahead CRLF > <данные, столько, сколько указано> CRLF < * какие-то данные CRLF < тот_же_самый_тег OK команда Completed CRLF |
Если литеральную строчку нужно передать серверу, он не спрашивает разрешения, а просто передаёт.
> произвольный_тег команда <аргументы> CRLF
< * какие-то данные {длина}CRLF
< <данные, сколько указано>CRLF
< тот_же_самый_тег OK команда Completed CRLF
|
Такое взаимодействие имеет две особенности:
Взаимодействуя с сервером, клиент переводит его в одно из пяти состояний. Приведённый в RFC 2060 граф переходов:
+--------------------------------------+ |initial connection and server greeting| +--------------------------------------+ || (1) || (2) || (3) VV || || +-----------------+ || || |non-authenticated| || || +-----------------+ || || || (7) || (4) || || || VV VV || || +----------------+ || || | authenticated |<=++ || || +----------------+ || || || || (7) || (5) || (6) || || || VV || || || || +--------+ || || || || |selected|==++ || || || +--------+ || || || || (7) || VV VV VV VV +--------------------------------------+ | logout and close connection | +--------------------------------------+ |
Состояния:
ПРИМЕЧАНИЕ
Это терминология RFC 2060, на мой взгляд, состояние следовало назвать "До соединения". Потому что "установка соединения и приветствие" это, на мой взгляд, не состояние, а процесс перехода из одного состояния в другое. |
Переходы (цифры соответствуют цифрам на графе):
Очевидно, команды можно посылать только тогда, когда сессия находится в одном из трёх состояний:
В разных состояниях допустимы разные команды, где команду использовать можно, а где нельзя понятно по смыслу команды, здесь эта информация не приводится, в RFC она есть.
Тезисно:
Сразу после успешного соединения, сервер посылает клиенту приветствие. Оно может выглядеть так (типичный случай):
< * OK Привет, юзер! |
Так (с автоматической аутентификацией):
< * PREAUTH Здравствуйте, Иван Иванович! |
Или, если не повезло, так:
< * BYE Ушла на базу, вернусь после пяти |
Аутентификация:
> a000 LOGIN user password
< a000 OK LOGIN COMPLETED
|
Да, пароль идёт открытым текстом. Более защищённый вариант предлагает команда AUTHENTICATE, но с ней я не стал разбираться.
Завершение сессии и выход
> a000 LOGOUT
< * BYE IMAP4rev1 Server logging out
< a000 OK LOGOUT COMPLETED
|
Возвращает список папок
> a000 LIST arg1 arg2
< * LIST (<флаги>) "<символ-разделитель в иерархических именах>" "<имя папки>"
< ...
< * LIST (<флаги>) "<символ-разделитель в иерархических именах>" "<имя папки>"
< a000 OK LIST COMPLETED
|
Аргументы:
Результаты:
В проекте используется только форма LIST "" *
Создаёт папку (см. замечание об именах папок в конце статьи). Сервер SHOULD уметь создавать сразу весь запрашиваемый путь. Но не MUST
ПРИМЕЧАНИЕ
На случай, если вы не в курсе: SHOULD и MUST - стандартные RFC-шные термины. |
> a000 CREATE "qwerty"
< a000 OK CREATE completed
|
Удаляет папку. Сложное поведение в случае существования вложенных папок:
> a000 DELETE "qwerty"
< a000 OK DELETE completed
|
Переименовывает папку
> a000 RENAME "qwerty" "asdfg"
< a000 OK RENAME completed
|
Выделяет папку, возвращает различную информацию о папке.
> a000 SELECT INBOX
< * 172 EXISTS
< * 1 RECENT
< * OK [UNSEEN 12] Message 12 is first unseen
< * OK [UIDVALIDITY 3857529045] UIDs valid
< * FLAGS (\Answered \Flagged \Deleted \Seen \Draft)
< * OK [PERMANENTFLAGS (\Deleted \Seen)] Limited
< a000 OK [READ-WRITE] SELECT completed
|
Разберём ответ построчно (приблизительно и не очень точно):
< * 172 EXISTS |
Количество писем в папке
< * 1 RECENT |
Количество новых писем в папке
< * OK [UNSEEN 12] Message 12 is first unseen |
Необязательная строчка. Первое не просмотренное письмо.
< * OK [UIDVALIDITY 3857529045] UIDs valid |
UIDVALIDITY папки.
< * FLAGS (\Answered \Flagged \Deleted \Seen \Draft) |
Флаги, определённые в папке. Других флагов папке по идее нет.
< * OK [PERMANENTFLAGS (\Deleted \Seen)] Limited |
Флаги, которые можно менять.
< a000 OK [READ-WRITE] SELECT completed |
Команда завершилась успешно, предоставлен доступ на чтение и запись.
Проект обрабатывает только первые две строчки, остальное просто пропускает.
Снимает выделение с текущей папки, удаляя при этом все сообщения, помеченные флагом \Deleted.
> a000 CLOSE
< a000 OK CLOSE COMPLETED
|
Возвращает указанные части указанных сообщений. В проекте применяются два варианта:
> A0028 FETCH 1 (flags body.peek[header.fields (Date from to subject)])
< * 1 FETCH (FLAGS (\Answered \Seen) BODY[HEADER.FIELDS (Date from to subject)] {282}
< From: Ivan Ivanich <ivan@ivanovich.ru>
< To: =?windows-1251?Q?=27=FF=C5=CF=D6=C5=C8_=F3=CD=CA=CD=C4=D5=CA=CD=C1=27?=
< <xxxxx@pisem.net>
< Subject: =?windows-1251?Q?RE=3A_SharePoint_=D5_=C1=DF=C5_=C1=DF=C5_=C1=DF?=
< =?windows-1251?Q?=C5?=
< Date: Tue, 22 Jan 2002 11:07:52 +0300
<
< )
< A0028 OK Completed (0.000 sec)
|
> A0032 FETCH 1 body[]
< * 1 FETCH (BODY[] {2531}
< X-Sieve: cmu-sieve 2.0
< Return-Path: <ivan@ivanovich.ru>
< Received: from imap.front.ru (imap.front.ru [80.68.244.14])
< by imap.pisem.net (8.12.1/8.12.1) with ESMTP id g0M87LQ9090073
< for <xxxxx@imap.pisem.net>; Tue, 22 Jan 2002 11:07:21 +0300 (MSK)?g
< (envelope-from ivan@ivanovich.ru)
< Received: from bull.ivanovich.spb.su (mail.ivanovich.ru [000.000.00.000])
< by imap.front.ru (8.12.2/8.12.2) with ESMTP id g0M85H2X055212
< for <xxxxx@pisem.net>; Tue, 22 Jan 2002 11:05:33 +0300 (MSK)
< (envelope-from ivan@ivanovich.ru)
< Received: by bull.ivanovich.spb.su with Internet Mail Service (5.5.2650.21)
< id <DLLJ6ACF>; Tue, 22 Jan 2002 11:07:56 +0300
< Message-ID: <79816F81D569D5119F5B0060972CEF7C6188E5@bull.ivanovich.spb.su>
< From: Ivan Ivanich <ivan@ivanovich.ru>
< To: =?windows-1251?Q?=27=FF=C5=CF=D6=C5=C8_=F3=CD=CA=CD=C4=D5=CA=CD=C1=27?=
< <xxxxx@pisem.net>
< Subject: =?windows-1251?Q?RE=3A_SharePoint_=D5_=C1=DF=C5_=C1=DF=C5_=C1=DF?=
< =?windows-1251?Q?=C5?=
< Date: Tue, 22 Jan 2002 11:07:52 +0300
< MIME-Version: 1.0
< X-Mailer: Internet Mail Service (5.5.2650.21)
< Content-Type: text/plain;
< charset="windows-1251"
< Content-Transfer-Encoding: base64
<
< 0eXw5e...
... (поскольку это настоящее письмо от настоящего человека, я его не публикую)
< DQoNCtEg8+Lg5uXt6OXsLA0K3vDgDQo=
<
< )
< A0032 OK Completed (0.000 sec)
|
Кратко про аргументы:
А формат ответа проще посмотреть, чем описывать словами.
Записывает письмо в папку.
> a000 APPEND имя-папки {310} < + go ahead > Date: Mon, 7 Feb 1994 21:52:25 -0800 (PST) > From: Fred Foobar <foobar@Blurdybloop.COM> > Subject: afternoon meeting > To: mooch@owatagu.siam.edu > Message-Id: <B27397-0100000@Blurdybloop.COM> > MIME-Version: 1.0 > Content-Type: TEXT/PLAIN; CHARSET=US-ASCII > > Hello Joe, do you think we can meet at 3:30 tomorrow? > < a000 OK APPEND Complete |
ПРИМЕЧАНИЕ
В примере в RFC 2060 пропущен ответ сервера "+ go ahead", это ошибка, клиент не имеет права отправлять данные до получения подтверждения. |
Аргументом команды является имя папки и само письмо, ещё при желании можно передать флаги и дату (здесь не указано, так как в проекте эти аргументы не используются). Письмо должно быть корректным письмом, то есть соответствовать RFC 822.
Если письмо добавляется в текущую папку, и добавление прошло успешно, сервер SHOULD отправить уведомление вида * 123 EXISTS, посчитав и новое письмо. Моя реализация на это очень рассчитывает, pochta.ru соответствует.
Изменяет указанные флаги указанных сообщений. Так - добавляет к списку флагов указанные:
> A003 STORE 1 +FLAGS.SILENT (\Seen \Deleted)
< A003 OK STORE completed
|
Так - удаляет из списка флагов:
> A003 STORE 1 -FLAGS.SILENT (\Deleted)
< A003 OK STORE completed
|
Так - заменяет список флагов:
> A003 STORE 1 FLAGS.SILENT (\Seen \Deleted)
< A003 OK STORE completed
|
Если вместо FLAGS.SILENT использовать просто FLAGS, сервер будет посылать дополнительные ответы с информацией о состоянии флагов затронутых писем. Например так:
> A003 STORE 2:4 +FLAGS (\Deleted)
< * 2 FETCH FLAGS (\Deleted \Seen)
< * 3 FETCH FLAGS (\Deleted)
< * 4 FETCH FLAGS (\Deleted \Flagged \Seen)
< A003 OK STORE completed
|
Копирует сообщение в указанную папку
> A003 COPY 1 "qwerty"
< A003 OK COPY completed
|
Следующие команды не реализованы по причине сложности и/или моей лени. Они добавляют протоколу дополнительные возможности, которые не поддерживаются проектом:
Следующие команды могли быть реализованы, но не понадобились. Никаких особых новых возможностей не добавляют:
Во-первых, про имена. С именами папок всё не просто. С одной стороны, все команды, работающие с папками (LIST, CREATE, DELETE, RENAME, SELECT, EXAMINE, APPEND) доступны в режиме "Клиент аутентифицирован, папка НЕ выбрана". Логично предположить, что в этом режиме должны использоваться полные имена папок. Логично предположить, что в режиме "Выбрана текущая папка" эти команды должны работать так же, то есть использовать полные имена (по принципу наименьшего удивления). Логично предположить, что и единственная оставшаяся команда COPY будет работать с полными именами. С другой стороны, в RFC это явно не оговаривается, зато упоминаются некие "пространства имён". В общем, дело тёмное. Но на любимой pochta.ru все имена должны быть полные, ещё одно очко в пользу логики.
Во-вторых, про строки. Согласно RFC 2060, везде, где можно употребить обычную строку, можно употребить и литерал. Например, можно так:
> a000 LOGIN {31} < + go ahead > длинное русское имя с пробелами {8} < + go ahead > password < a000 OK LOGIN Completed |
Здесь первые два перевода строки являются составной частью литералов, последний - завершает команду. Другой вопрос - поддержит ли такое поведение сервер. И не отреагирует ли он "симметричным ответом"?
В-третьих, про UID. Если вы пишите не лабораторную работу, а настоящего клиента, наверное их придётся использовать. Команды и идеи должны быть те же, а вот детали и подробности.. Ничем не могу помочь, не пробовал.
Закладки на сайте Проследить за страницей |
Created 1996-2024 by Maxim Chirkov Добавить, Поддержать, Вебмастеру |