Средства реализации: php & curl
Репозиторий с исходным кодом решения задачи https://github.com/Byurrer/imap-mail-collector. Есть подробный readme, а код внутри прокомментирован.
Коротко алгоритм довольно прост:
- подключение к почтовому ящику через imap протокол,
- проход по всем директориям на ящике
- скачивание исходников писем и распределение по директориям, как на ящике
Директории
Первая проблема заключалась в получении, разборе и использовании названий директорий на почтовом ящике. При авторизации на imap сервере в ответе будет получен список директорий на ящике в UTF7-IMAP (https://ru.wikipedia.org/wiki/UTF-7) кодировке:
* LIST (\Unmarked \HasNoChildren \Drafts) "|" Drafts * LIST (\Unmarked \NoInferiors) "|" INBOX * LIST (\Unmarked \HasNoChildren) "|" Outbox * LIST (\Unmarked \HasNoChildren \Sent) "|" Sent * LIST (\Unmarked \HasNoChildren \Junk) "|" Spam * LIST (\Unmarked \HasNoChildren \Trash) "|" Trash
Нет проблем пока не появляются директории на кириллице с пробелами (название синтетическое для примера):
* LIST (\Trash) "/" "&BBoEPgRABDcEOAQ9BDA- &BBoEPgRABDcEOAQ9BDA-"
При попытке сканирования такой директории (по адресу: imaps://imap.domain.zone/&BBoEPgRABDcEOAQ9BDA- &BBoEPgRABDcEOAQ9BDA-/), imap сервер выдал: imap URL using bad/illegal format or missing URL
Как видно проблема в недопустимых символах, решение нашлось здесь, надо было просто экранировать эту часть url (название директории) при помощи curl_escape.
Декодировать строку можно так:
$sDir = mb_convert_encoding($value, "UTF-8", "UTF7-IMAP");
Кодировка
Исходник письма может быть в любой кодировке.
Изначально требовалось чтобы скрипт отправлял исходник письма и некоторые другие данные на другой сервер, для этого данные паковал json_encode, пока не пошли письма в windows-1251 кодировке, на что json_encode сказал: Malformed UTF-8 characters, possibly incorrectly encoded
Проблема была решена при помощи base64_encode исходника письма с последующим json_encode, а принимающий сервер делал обратные операции в обратном порядке и получал неизменные данные.
Удаление писем
Удалять письма надо по убыванию порядковых номеров (письмо с номером 1 удалять самым последним), иначе обратный порядок может привести к тому что порядковые номера при удалении будут изменены на imap сервере, а дальше неверная идентификация писем.
Если речь идет о последовательном удалении, тогда для этого (после получения и парсинга ответа после SEARCH ALL UNDELETED) можно просто применить arsort к массиву с порядковыми номерами://получение порядковых номеров всех неудаленных писем в директории curl_setopt($hCurl, CURLOPT_URL, "$sImap/$value"); curl_setopt($hCurl, CURLOPT_CUSTOMREQUEST, "SEARCH ALL UNDELETED"); $sResponse = curl_exec($hCurl); $sResponse = str_replace("\r\n", "\n", $sResponse); $sResponse = mb_substr($sResponse, mb_strlen("* SEARCH ")); //парсим строку извлекая порядковые номера сообщений в директории $aStrs = explode(" ", $sResponse); //сортировка массива по убыванию, чтобы удалять не сбивая порядковые номера arsort($aStrs);