Получение писем по протоколу IMAP

Одна из основных особенностей взаимодействия с почтовым сервером по протоколу IMAP состоит в том, что в IMAP поддерживается возможность работы с различными каталогами, в то время как при взаимодействии по протоколу POP3 работа возможна лишь с одним каталогом.

Схема получения писем с почтовых серверов, работающих по протоколу IMAP, выглядит следующим образом:
  1. Задайте параметры подключения к почтовому серверу. Параметры подключения задаются с помощью конструктора:
    пер Параметры = новый ПараметрыПодключенияImap(Сервер = "Сервер", 
                                                   Порт = 993,
                                                   Аутентификация = АутентификацияПочты,
                                                   ПараметрыЗащиты = ПараметрыЗащищенногоСоединения)
  2. Следующим шагом создайте соединение. Для создания соединения с почтовым сервером, работающим по протоколу IMAP, предназначен объект СоединениеImap. Чтобы создать подключение по протоколу IMAP, используется конструктор:
    исп Соединение = СоединениеImap(Параметры = ПараметрыПодключенияImap, 
                                    ОчищатьПриЗакрытииКаталога = Истина)
  3. Протокол IMAP позволяет работать с различными каталогами, расположенными на почтовом сервере. Поэтому следующим шагом следует получить нужный каталог и открыть его. Чтобы получить каталог с письмами/каталогами по имени, следует использовать метод ПолучитьКаталог(Имя: ВидКаталогаПочты|Строка). Наименования одного и того же каталога могут отличаться на различных почтовых серверах. Например, каталог с черновиками может иметь как наименование «Черновики», так и «Draft», «Drafts» или иное (при запросе ВидКаталогаПочты.Черновики на сервер будет отправлена строка «Drafts»).

    Полученный каталог не обязательно существует физически. Чтобы проверить физическое существование каталога, следует вызвать метод КаталогПочтыImap.Существует().

    Затем полученный каталог следует открыть, используя метод КаталогПочтыImap.Открыть(). Открывать каталог можно как в режиме Чтение, так и в режиме ЧтениеИЗапись.

  4. Следующим шагом можно получить письма из открытого каталога. Метод КаталогПочтыImap.ПолучитьПисьма() вернет массив объектов типа ПисьмоВСоединенииImap. Чтобы получить текст, вложения, важность, а также иные атрибуты самого письма, следует использовать свойства и методы типа ПисьмоВСоединенииImap.Письмо.

    Для поиска и выгрузки необходимых писем также удобно вначале использовать поиск по заголовкам, и лишь затем выгружать само тело письма, предварительно получив его индекс из свойства заголовка. Заголовки хранят такую метаинформацию, как:
    • Важность
    • ДатаОтправления
    • ОбратныеАдреса
    • Отправитель
    • Получатели
    • ПолучателиКопии
    • ПолучателиСкрытойКопии
    • Тема

    Чтобы получить заголовки писем, используйте метод КаталогПочтыImap.ПолучитьЗаголовкиПисем(). Метод возвращает ЧитаемыйМассив<ЗаголовкиПисьмаВСоединенииImap>. Затем обратитесь к свойству Заголовки элемента массива типа ЗаголовкиПисьмаВСоединенииImap.

  5. После того как вы нашли заголовки нужных писем, можно выгрузить сами письма, получив их UID либо индекс из свойства заголовка. Для того чтобы получить UID письма из заголовка, обратитесь к свойству Uid объекта типа ЗаголовкиПисьмаВСоединенииImap.

    UID — это уникальное для письма число внутри каталога, которое монотонно (по времени) возрастает от старого письма к новому. В отличии от индекса, значение не обязательно непрерывное. Значение не меняется внутри сессии, а также почтовым серверам рекомендовано не менять данное значение между сессиями. У каталогов также есть свой уникальный идентификатор — UIDVALIDITY. При этом индекс письма может не совпадать с UID того же самого письма.

    Далее передайте массив UID в метод КаталогПочтыImap.НайтиПисьмаПоUid().

  6. Для письма можно установить один или несколько флагов:
    • Новое
    • Отвеченное
    • Отмененное
    • Пересланное
    • Помеченное
    • Прочитанное
    • Удаленное
    • Черновик

    Чтобы удалить письмо, установите ему флаг Удаленное. Далее можно вызвать метод КаталогПочтыImap.ОчиститьУдаленные() для удаления писем. Данный метод навсегда удаляет письма, помеченные на удаление. После удаления писем из каталога, индексы оставшихся в каталоге писем изменятся, однако их UID останется прежним.

    Письма, помеченные флагом Удаленное, будут удалены автоматически после закрытия соединения, если свойство СоединениеImap.ОчищатьПриЗакрытииКаталога равняется Истина.

    Установить флаги можно, передав массив UID писем и необходимые флаги в метод КаталогПочтыImap.УстановитьФлагиПоUid().

Пример приема и перемещения письма при работе с протоколом IMAP. В данном примере осуществляется:

  • Получение непрочитанных писем
  • Автоматический ответ на письмо, если оно получено от заказчика с вложенным в письмо договором
  • Перемещение письма в каталог «Договоры» для дальнейшей проверки договоров юридическим отделом
метод АвтоматическиОтветитьКлиенту()
    АвтоматическиОтветитьНаПисьмоСЗаголовком("договор")
;
 
метод АвтоматическиОтветитьНаПисьмоСЗаголовком(Заголовок: Строка)
    пер Параметры = новый ПараметрыПодключенияImap(Сервер(), Порт(), Аутентификация())
    исп Соединение = новый СоединениеImap(Параметры)
    пер Входящие = Соединение.ПолучитьКаталог(ВидКаталогаПочты.Входящие)
    Входящие.Открыть(РежимОткрытияКаталогаImap.ЧтениеИЗапись)
 
    пер Письма = ПолучитьНепрочитанныеПисьма(Входящие)
    для НепрочитанноеПисьмо из Письма
        если ((НепрочитанноеПисьмо.Письмо.Тема?.Содержит(Заголовок) ?? Ложь) и (не НепрочитанноеПисьмо.Письмо.Вложения.Пусто()))
            пер Ответ = СформироватьОтветИзВходящего(НепрочитанноеПисьмо.Письмо)
            ОтправитьПисьмоПоSmtp(Ответ)
 
            ПереместитьВПисьмаДляОбработки(НепрочитанноеПисьмо, Входящие, Соединение)
        ;
    ;
    Входящие.Закрыть()
;
 
метод ПолучитьНепрочитанныеПисьма(Каталог: КаталогПочтыImap): ЧитаемыйМассив<ПисьмоВСоединенииImap>
    если не Каталог.Открыт()
        Каталог.Открыть(РежимОткрытияКаталогаImap.ЧтениеИЗапись)
    ;
    если Каталог.МожетСодержатьПисьма()
        возврат Каталог.ПолучитьНепрочитанныеПисьма()
    ;
    выбросить новый ИсключениеНедопустимыйАргумент()
;
 
метод ПереметитьВПисьмаДляОбработки(Письмо: ПисьмоВСоединенииImap, ИсходныйКаталог: КаталогПочтыImap, Соединение: СоединениеImap)
    пер Договоры = Соединение.ПолучитьКаталог("Клиенты" + ИсходныйКаталог.Разделитель + "Договоры")
    если не Договоры.Существует()
        Договоры.СоздатьДляПисем()
    ;
     
    ИсходныйКаталог.ПереместитьПисьма([Письмо.Uid], Договоры)
;
 
метод СформироватьОтветИзВходящего(Письмо: Письмо): Письмо
    пер Ответ = новый Письмо("company@example.ru", Письмо.Отправитель!, "Автоматический ответ: " + Письмо.Тема!)
    Ответ.Важность = ВажностьПисьма.Высокая
    Ответ.УстановитьТекстКакСтроку(ПолучитьHtmlАвтоматическогоОтвета(), ВидТекстаПисьма.Html)
    возврат Ответ
;
 
метод ПолучитьHtmlАвтоматическогоОтвета(): Строка
    // многострочная строка должна быть с новой строки
    возврат 
        "<html>
            <h2>Ваш договор был принят и отправлен в юридический отдел для дальнейшего согласования.</h2>
            <br/><hr><br/>
                С уважением, клиентский отдел фирмы Company
            <br/>
            <img src=\"https://static.company.ru/images/logo.png\">
        </html>"
;
 
метод ОтправитьПисьмоПоSmtp(Письмо: Письмо)
    пер ПараметрыПодключения = ПолучитьПараметрыПодключенияSmtp()
    КлиентSmtp.Отправить(ПараметрыПодключения, Письмо)
;

Пример ответа на письмо и работы с флагами при получении писем по протоколу IMAP. В данном примере осуществляется:

  • Получение непрочитанного письма
  • Поиск подходящего письмо
  • Формирование ответного письма из простого текста с цитатой и его отправка
  • Установка флага Прочитанное на найденном письме
метод ОтветитьНаПисьмоОтКоллеги()
    пер Адрес = "kol@example.ru"
     
    пер Параметры = новый ПараметрыПодключенияImap(Сервер(), Порт(), Аутентификация())
    исп Соединение = новый СоединениеImap(Параметры)
    пер Входящие = Соединение.ПолучитьКаталог(ВидКаталогаПочты.Входящие)
    Входящие.Открыть(РежимОткрытияКаталогаImap.ЧтениеИЗапись)
 
    пер ЗаголовкиНепрочитанных = Входящие.ПолучитьЗаголовкиНепрочитанныхПисем()
    пер ИскомоеПисьмо: ПисьмоВСоединенииImap? = Неопределено
 
    для Заголовок из ЗаголовкиНепрочитанных
        если ИскомоеПисьмо != Неопределено
            прервать
        ;
        
        если Заголовок.Заголовки.Отправитель.Адрес == Адрес и Заголовок.Заголовки.ПолучателиКопии.Пусто()
            пер Найденное = Входящие.НайтиПисьмоПоUid(Заголовок.Uid)
            // Для примера не берем только отмеченные письма
            если не Найденное.Флаги.Содержит(ФлагПисьма.Помеченное)
                ИскомоеПисьмо = Найденное
            ;
        ;
    ;
 
    пер Ответ = ОтветНаПисьмо(ИскомоеПисьмо.Письмо)
    ОтправитьОтветПоSmtp(Ответ)
 
    Входящие.УстановитьФлагиПоUid([ИскомоеПисьмо.Uid], [ФлагПисьма.Прочитанное])
;

метод ОтправитьОтветПоSmtp(Письмо: Письмо)
    пер ПараметрыПодключения = ПолучитьПараметрыПодключенияSmtp()
    КлиентSmtp.Отправить(ПараметрыПодключения, Письмо)
;

метод ОтветНаПисьмо(Письмо: Письмо): Письмо
    пер Текст = Письмо.ПолучитьТекст()[0].Текст
 
    пер ОбразецНовойСтроки = новый Образец("\n")
    пер Цитата = Текст.Заменить(ОбразецНовойСтроки, "\n>")
 
 
    пер ИтоговыйТекст = ПолучитьТекстОтвета() + ПустаяСтрока() + Цитата
    пер ТемаОтвета = "Re: "    
    если Письмо.Тема != Неопределено
        ТемаОтвета += Письмо.Тема
    ;
     
    пер Ответ = новый Письмо("sender@1c.ru", Письмо.Отправитель, ТемаОтвета)

    Ответ.УстановитьТекстКакСтроку(ИтоговыйТекст, ВидТекстаПисьма.ПростойТекст)
    
    возврат Ответ
;
 
метод ПолучитьТекстОтвета(): Строка
    возврат "Это письмо автоматически отметится прочитанным, получатель ограничил круг лиц, чьи письма не помечаются прочитанными. Спасибо за понимание"
;
 
метод ПустаяСтрока(): Строка
    возврат "\н"
;

Пример отправка письма с inline-вложением из каталога «Черновиков». В данном примере осуществляется:

  • Формирование письма из изображения, которое должно быть отображено в тексте письма
  • Сохранение письма в папку «Черновики»
  • Получение письма из папки «Черновики»
  • Отправка по SMTP
метод ОтправитьКоммерческоеПредложение()
    пер Письмо = новый Письмо(новый АдресПочты("sender@example.ru", "no-reply"))
    Письмо.ДобавитьПолучателей([новый АдресПочты("receiver@example.ru")])
    Письмо.Тема = "Коммерческое предложение"
 
    Письмо.УстановитьТекстКакСтроку(ПолучитьТекст(), ВидТекстаПисьма.Html)
    Письмо.ПриложитьФайл(Файл = "img.png",
        Название = "my_image.png",
        ТипСодержимого = "image/png",
        Ид = "my_image.png",
        ВстроенноеВТекст = Истина)
 
    пер Параметры = новый ПараметрыПодключенияImap("imap.example.ru", 993, new АутентификацияПочтыПоПаролю("sender@example.ru",
        "пароль"))
    исп Соединение = новый СоединениеImap(Параметры)
 
    пер Черновики = Соединение.ПолучитьКаталог("Черновики")
    Черновики.ДобавитьПисьма([Письмо])
 
    ВернутьсяКПисьмуИОтправить(Соединение)
;
 
метод ВернутьсяКПисьмуИОтправить(Соединение: СоединениеImap)
    пер Черновики = Соединение.ПолучитьКаталог("Черновики").Открыть(РежимОткрытияКаталогаImap.ЧтениеИЗапись)
    пер Письмо = Черновики.ПолучитьПисьма(Черновики.КоличествоПисем() - 1, Черновики.КоличествоПисем() - 1)[0]
 
    пер ПараметрыОтправки = новый ПараметрыПодключенияSmtp("smtp.example.ru", 465,
        новый АутентификацияПочтыПоПаролю("sender@example.ru", "пароль"))
    КлиентSmtp.Отправить(ПараметрыОтправки, Письмо.Письмо)
 
    пер Отправленные = Соединение.ПолучитьКаталог("Отправленные")
    Черновики.ПереместитьПисьма([Письмо.Uid], Отправленные)
;


метод ПолучитьТекст(): Строка
    возврат 
        "<div>
            <img border=\"0\" 
                hspace=\"0\" 
                alt=\"\" 
                align=\"baseline\" 
                apple-inline=\"yes\" 
                id=\"my_image.png\" 
                height=\"836\"
                width=\"643\" 
                apple-width=\"yes\"
                apple-height=\"yes\" 
                style=\"display:block\" 
                src=\"cid:my_image.png\">
        </div>"
;