Изображение квадрата Дюрера

ООО АВТОМАТИКА плюс

Rambler's Top100

Рейтинг@Mail.ru

Работа с электронной почтой с использованием компонентов Indy

Версия от 02.10.2011, 06.03.2012

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

Содержание

Предисловие

Вопросы, связанные с использованием компонентов библиотеки Indy для работы с электронной почтой появляются в профессиональных Delphi-форумах с неизменной периодичностью. Это вызвано отсутствием необходимой документации по данной библиотеке, по крайней мере в зоне русскоязычного интернета. При этом фактически единственным способом найти ответы на возникшие вопросы является обращение в профессиональные форумы, такие как http://www.sql.ru, http://www.delphimaster.ru и др. К сожалению на форумах можно найти ответы далеко не на все вопросы, а качество предложенных ответов зачастую оставляет желать лучшего. Но главная проблема, по мнению автора, это непонимание основ работы с электронной почтой, в частности с текстовыми протоколами SMTP и POP3 (необходимо отметить, что это очень старые, проверенные временем протоколы, десятилетиями являющиеся стандартом). В данной статье уделено необходимое внимание описанию принципов работы по протоколам SMTP и POP3 с использованием стандартной утилиты Telnet, а также использованию соответствующих компонентов из библиотеки Indy, в том числе TIdSMTP и TIdPOP3. В статье отсутствует информация, связанная с использованием средств шифрования SSL при работе с электронной почтой. Убедительная просьба не использовать материалы, представленные с данной статье в деструктивных целях (например, для массовой рассылки нежелательной почты).

1. Использование Telnet для работы с текстовыми протоколами

Протоколы SMTP (см. здесь) и POP3 являются текстовыми, поскольку любая команда, а также ответ на любую команду формируются в виде текстовой строки, включающей печатные символы и оканчивающейся символом перевода строки. Кроме того, текстовым протоколом является http и многие другие (в том числе управляющая часть протокола ftp, см. FileZilla). Для отладки текст-ориентированных TCP-протоколов часто используют стандартную утилиту Telnet, входящую в состав многих операционных систем, в том числе Windows и Linux. В Windows может потребоваться установить клиента Telnet как дополнительную компоненту Windows (Пуск\Панель управления\Программы и компоненты\Включение и отключение компонентов Windows).
Запустите командную оболочку ("cmd" для Windows) и выполните команду "telnet". Появится следующая строка приглашения "telnet >". Введите команду "help" для просмотра опций программы. Наиболее интересной является команда "open" или коротко "o", принимающая 2 параметра: имя узла и TCP-порт. Введите следующую команду (см. текст, выделенный жирным шрифтом):
open smtp.mail.ru 25 - подключение к серверу "smtp.mail.ru " по порту 25
и нажмите клавишу Enter
В результате на экране отобразиться сообщение об успешном подключении к одному из серверов исходящей почты, входящих в состав mail.ru:
220 smtp19.mail.ru ESMTP ready - сообщение об успешном подключении
Далее введите следующую команду:
EHLO mycomputer - команда приветствия с указанием произвольной строки (например имени или IP-адреса компьютера)
В ответ сервер пришлет следующее содержимое:
250-smtp19.mail.ru
250-SIZE 31457280
- максимальный размер сообщения, которое можно отправить на данный SMTP-сервер
250-8BITMIME - поддержка 8-битной кодировки (т.е. поддерживаются не только английские символы)
250-AUTH PLAIN LOGIN - сообщение о необходимости авторизации
250 STARTTLS
Введите команду "QUIT".
В ответ сервер вернет "221 2.0.0 Bye" и разорвет соединение.

2. Отправка письма по протоколу SMTP

Мы убедились в том, что с помощью программы Telnet можно подключиться к SMTP-серверу (по заданному потру), выполнить команду и получить ответ. К сожалению, продемонстрировать весь процесс отправки письма с помощью программы Telnet представляется проблематичным, поскольку все "известные" SMTP-сервера требуют авторизации (см. строку AUTH в ответ на команду EHLO), а неизвестные SMTP-сервера для автора также остаются неизвестными (либо они не подходят в качестве примера для данной статьи).

В том случае, если SMTP-сервер не требует авторизации, процесс отправки email весьма прост и состоит из следующих действий (далее в статье символ ">" означает отправку команды пользователем, "<" - ответ от сервера):



> EHLO mycomputer
< 250-PIPELINING
< 250-SIZE 10240000
< 250-ETRN
< 250 8BITMIME
  - Отсылается команда приветствия EHLO, в ответ на которую сервер должен вернуть список предоставляемых им возможностей. Если SMTP-сервер старый и не понимает такой команды (т.е. выдает сообщение об ошибке), то следует дать команду HELO.
> MAIL FROM: <vasyapupkin@example.com>
< 250 Ok
- Указывается адрес отправителя. В ответ на него сервер вышлет письмо, если произошла какая-либо ошибка. Адрес отправителя можно подделать, указав в поле MAIL FROM чужой или несуществующий email (злоумышленники этим пользуются постоянно). Хорошие SMTP-сервера, в том числе mail.ru, таких вольностей не допускают.
> RCPT TO: <sveta@devki.ru>
< 250 Ok
- Дается команда RCPT TO с указанием адреса получателя в угловых скобках. В том случае, если письмо должно быть доставлено нескольким адресатам (в случае массовой рассылки), команда RCPT TO выполняется отдельно для каждого адресата (максимальное количество адресатов при отправке письма ограничено).
> DATA
< 354 End data with <CR><LF>.<CR><LF>
- Дается команда DATA, сообщающая серверу, что весь последующий текст относится непосредственно к отправляемому письму. Сервер должен вернуть сообщение о готовности приема текста письма. Ответ сервера означает, что для окончания набора письма следует дать команду, состоящую из одной точки.
> ввод строк заголовка
> ввод пустой строки
> ввод строк тела письма
> .
< 250 Ok: queued as 4E1D4CE83772D4B4
- Пользователь набирает заголовок (header) и текст (body) письма. Для окончания письма пользователь дает команду, состоящую из одной точки.
> QUIT
< 221 Bye
- Отправляется команда QUIT, разрывающая соединение с сервером.

Если Вы хотите более тесно познакомиться с данным протоколом, то в интернете есть отличная статья, подробным образом описывающая все основные возможности протокола SMTP.

3. Проверка входящей почты по протоколу POP3

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

> open pop.mail.ru 110
< +OK
  - Подключение к POP3-серверу по порту 110. В ответ сервер возвращает строку "+OK". Обратите внимание, что протокол POP3 не возвращает числовые ответы, в отличие от STMP.
> USER myname
< +OK
- Вводится имя пользователя. Пользователь с таким именем должен быть зарегистрирован на почтовом сервере, в противном случае он не сможет пройти процедуру авторизации.
> PASS mypassword
< +OK Welcome!
- Вводится пароль пользователя (в открытом виде). POP3-сервер может поддерживать также более безопасные механизмы авторизации. В ответ сервер сообщает об успешной авторизации.
> STAT
< +OK 17 2908160
- Вводится команда STAT, позволяющая определить количество сообщений и их общий размер.
> LIST
< +OK 17 messages (2908160 octets)
< 1 5649
< 2 11982
< 3 1002
.......
< 17 11987
< .
- С помощью команды LIST можно получить список всех писем с их примерным размера. Дело в том, что истинный размер письма после его получения будет другим, поскольку POP3-сервер в этот момент может скорректировать заголовки письма по своему усмотрению.
> LIST 16
< +OK 16 12608
- С помощью команды LIST <номер_сообщения> можно узнать примерный размер любого заданного письма.
> TOP 15 5 - C помощью команды TOP <номер_сообщения> <количество_строк> можно загрузить заголовок и несколько строк из тела письма. В ответ POP3-сервер вернет все строки заголовка письма, затем пустую строку, затем указанное количество строк из тела письма.
> RETR 14 - С помощью команды RETR <номер_сообщения> можно целиком загрузить указанное письмо. В ответ POP3-сервер вернет все строки заголовка письма, затем пустую строку, затем все строки письма.
> DELE 1
< +OK message 1 deleted
- С помощью команды DELE <номер_сообщения> можно удалить сообщение из почтового ящика. Следует отметить, что фактическое удаление сообщения произойдет только после разрыва соединения с POP3-сервером. До этого момента все удаленные письма можно восстановить с помощью команды RSET.
> QUIT
< +OK POP3 server at mail.ru signing off
- В конце работы следует разорвать соединение с POP3-сервером с помощью команды QUIT.

Следует отметить, что любая из команд доступа к письмам (STAT, TOP, LIST и т.д.) стартует транзакцию. Пока транзакция активна, параллельный доступ к почтовому ящику с любого другого компьютера блокируется. Это означает, что нельзя проверять входящую почту одновременно из нескольких мест.
Как видите, в протоколе POP3 ничего сложного нет, для его изучения достаточно 15 минут.
Более подробную информацию о протоколе POP3 ищите в интернете, например здесь.

4. Какие компоненты в Delphi можно использовать для работы с электронной почтой?

Весьма непросто найти идеальные компоненты Delphi для работы с электронной почтой. При этом ситуация осложняется с каждым годом. Среда и язык Delphi развиваются, однако большинство компонентов, разработанных для старых версий Delphi, адаптировать под новые версии чаще всего некому (или некогда). Похоронены очень многие проекты. Вторая проблема связана с тем, что люди, занимающиеся разработкой компонентов в какой-либо области иногда делают это "на свой вкус", далеко не всегда идеально. Либо же строят ошибочные предположения о возможных условиях использования разработанных ими решений. Простой пример: вспомним библиотеку APRO - среди многих программистов когда-то были вполне одобрительные возгласы насчет этой библиотеки. В типичном однопоточном приложении она смотрелась весьма неплохо. Однако данная библиотека была разработана именно для типичных однопоточных приложений. В сложных коммерческих многопоточных приложениях ее использование крайне ограничено (библиотека APRO в таких приложениях, судя по опыту автора, становится практически бесполезной).

Что касается компонентов для работы с электронной почтой, то достаточную популярность у программистов получила библиотека Overbyte ICS. В ней есть все для создания электронного сообщения, его отправки и приема. Но в ней, по мнению автора, имеется огромный недостаток (по мнению некоторых других программистов - великое достоинство) - она является асинхронной (собственно, как и APRO). Попробуйте самостоятельно отправить какое-либо письмо с ее помощью. Вряд ли у Вас это получится. Необходимо делать все также, как это сделано в демонстрационном примере, т.е. реализовать все необходимые обработчики (OnCommand, OnDisplay, OnGetData, OnHeaderLine, OnRequestDone и т.д.), при этом ничего не упустив из виду. При этом для отправки письма с использованием "готовых" компонентов ICS от программиста потребуется "всего лишь" несколько сотен строк кода. Вот она - автоматизация! Более того, библиотека ICS рассчитана для типичных однопоточных приложений. Авторы библиотеки посчитали, что вы будете работать с ней только из основного потока. Если логика разрабатываемого Вами приложения предполагает организацию работы с электронной почтой из дополнительных потоков, то маловероятно, что Вам удастся "прикрутить" данную библиотеку к своему приложению.

На фоне всех прочих вариантов наиболее ярко выделяется знаменитая библиотека Indy, являющаяся к тому же стандартной для Delphi. Тот факт, что библиотека Indy всегда устанавливается вместе с Delphi, наложило особые требования и определило отношение к ней у разных программистов. Библиотека Indy является весьма надежной (ее тестировала и отлаживала целая армия программистов) и высокопроизводительной. Она подходит для решения сетевых задач практически любой сложности. При этом решать такие задачи с использованием Indy просто как дважды два. Разработанные с использованием Indy решения зачастую работают годами без каких-либо сбоев. Благодаря реализованному "Indy way" (путь Indy) программист практически полностью освобождается от необходимости решения рутинных сетевых задач и может акцентировать все свое внимание именно на то, что ему действительно нужно. Можно отметить, пожалуй, единственное серьезное ограничение Indy - данная библиотека не предназначена для работы в качестве высоконагруженного HTTP-сервера, поддерживающего десятки тысяч одновременных соединений. На каждое соединение сервер Indy создает отдельный поток, при этом выделяется около 1 МБ памяти, поэтому при наличии большого количества одновременных подключений можно упереться в лимит памяти. К тому же задача сделать все по максимуму высокопроизводительным перед авторами, возможно, не стояла, но если и была, то на втором месте после обеспечения максимального комфорта для программиста. Рекомендуется использовать юникодные версии Delphi (например, Delphi 2010), поскольку было внесено множество улучшений в части работы с электронной почтой. Если анализировать исходники Indy, то может сложиться впечатление, что они давно не развиваются (аж с 2005г.), однако по факту - это не так. Необходимые изменения и доработки периодически вносятся.

Все прочие библиотеки компонентов Delphi для работы с электронной почтой в данной статье не упоминаются. В интернете с помощью поиска Вы сможете найти их немалое количество. Кроме того, на Torry.net есть хорошая подборка компонентов для работы с электронной почтой. Но по мнению автора, не следует ставить чужие компоненты сломя голову (сколько раз народ на этом обжигался - Delphi развивается, а компоненты никто адаптировать не собирается). Кроме того, цель данной статьи - рассказать читателю об особенностях работы с электронной почтой с использованием библиотеки Indy.

5. Отправка электронной почты с использованием компонента TIdSMTP

Для отправки электронной почты нам потребуются:
- компонент TIdSMTP (расположен в палитре компонентов на вкладке "Indy Clients")
- компонент TIdMessage (расположен на вкладке "Indy Misc")
- подключить вручную следующие модули:

IdSMTP   - содержит реализацию класса TIdSMTP
IdMessage - содержит реализацию класса TIdMessage
IdIOHandlerStack - содержит реализацию класса TIdIOHandlerStack
IdGlobal - содержит некоторые константы и функции, использующиеся другими модулями Indy
IdObjs - требуется для старых Delphi. В новых версиях Delphi данный модуль отсутствует
IdCharsets - содержит массив возможных кодировок (есть ли в старых Delphi?)
IdAttachmentFile - содержит реализацию класса TIdAttachmentFile
IdText - содержит реализацию класса TIdText


Перед отправкой письма его необходимо подготовить. Для этого следует задать соответствующие свойства компонента TIdMessage. Рассмотрим их более подробно:

IdMessage1.OnInitializeISO =
IdMessageInitializeISO;
  - Обработчик события, в котором будет запрашиваться кодировка для заголовков (header). В этом обработчике события достаточно прописать строку:
VCharSet := IdCharsetNames[idcsUTF_8];
В этом случае компоненты Indy будут автоматически перекодировать элементы заголовка письма таким образом, чтобы они не содержали символов с кодами > 127. Строка "Получатель <target@example.org>" (1) после перекодировки в UTF8 (например с помощью AnsiToUtf8) и (2) после представления в транспортном формате Base64* будет выглядеть следующим образом:
=?UTF-8?B?0J/QvtC70YPRh9Cw0YLQtdC70Yw=?= <target@example.org>
Внимание! Компоненты Indy в старых (неюникодных) версиях Delphi (в том числе Delphi 2007) не делают перекодировки в UTF8 автоматически. Вы должны для этого использовать функцию AnsiToUtf8(). Для юникодных Delphi необходимость в этом отсутствует (AnsiToUtf8 ни на что не влияет).
Внимание2! В юникодных версиях Delphi (2009 и выше) осуществляется автоматическая перекодировка заголовков письма в KOI8R (это стандарт для электнонной почты в интернете), поэтому нет необходимости пользоваться OnInitializeISO! Рекомендуется использовать именно KOI8R для кодирования заголовков, т.к. не все почтовые программы могут корректно отображать UTF8. Пример перекодировки в KOI8R для старых Delphi приведен ЗДЕСЬ. Некоторые символы (например, "№") в KOI8R НЕ КОДИРУЮТСЯ!
IdMessage1.From.Text :=
AnsiToUtf8('Отправитель <sender@example.org>');

- Имя и адрес отправителя. Перекодировка в Base64 с добавлением "?UTF-8?B?" будет выполнена автоматически для всех нелатинских символов. То же самое относится к любым элементам, составляющим основной заголовок письма (From, To, Subject и т.п.) и заголовки дополнительных частей письма (например filename).
Внимание! Функцию AnsiToUtf8 следует применять только для старых (неюникодных) Delphi. Также рекомендуется вместо UTF8 использовать KOI8R.

IdMessage1.Recipients.
EMailAddresses := AnsiToUtf8('Получатель <target@example.org>');
- Имя и адрес получателя. Если получателей несколько, то имена и адреса следует отделять друг от друга символом ";".
IdMessage1.Subject :=
AnsiToUtf8('Тест INDY');
- Тема письма
IdMessage1.Encoding :=
  meMIME;
- Указывает на поддержку MIME (принятый на сегодняшний момент способ кодирования и обработки писем)
IdMessage1.ContentType := 'text/plain';
или
IdMessage1.ContentType := 'multipart/mixed';
- Свойство ContentType требуется настраивать только для Delphi XE2 (причем ДО настройки CharSet). В предыдущих версий Delphi оно настраивается автоматически. Для простого текстового письма используется 'text/plain'. Для сложного письма с файловыми вложениями или дополнительными частями используется 'multipart/mixed'.
IdMessage1.CharSet :=
  IdCharsetNames[idcsUTF_8];
- Кодировка, в которой записано тело письма Body. Программист должен указывать кодировку явно.
Внимание! Компоненты Indy в неюникодных версиях Delphi не делают перекодировки в UTF8 автоматически. Вы должны для этого использовать функцию AnsiToUtf8().
В Delphi XE2 константу idcsUTF_8 переименовали в idcs_UTF_8.
IdMessage1.
ContentTransferEncoding := 'base64';
- Транспортный формат для текста, указанного в TIdMessage.Body. В данном случае текст Body, представленный в кодировке UTF8, будет дополнительно перекодирован в транспортный формат Base64 (для избежания в теле письма символов с кодами > 127). Indy игнорирует параметр ContentTransferEncoding при наличии файловых вложений и дополнительных частей письма (при этом использует другой транспортный формат: 'quoted-printable').
IdMessage1.Body.Text :=
  AnsiToUtf8('Hello world, peaples!' +
  sLineBreak + 'Привет, мир!');
- Тело письма. Современный стандарт обработки писем (MIME) допускает наличие в письме произвольного количества дополнительных (вложенных) частей, в том числе файловых вложений и текстовых частей того или иного формата (например HTML). При этом части письма отделяются друг от друга границами (boundary).
В том случае, если письмо не содержит никаких дополнительных частей, то действуют ранее приведенные настройки: CharSet и ContentTransferEncoding, а границы не используются.
В том случае, если письмо содержит файловые вложения или дополнительные текстовые части, то текст, заданный в TIdMessage.Body.Text, на самом деле будет оформлен как дополнительная текстовая часть (но самая первая). "Стандартное" тело письма в таком случае будет состоять из одной единственной строки:
"This is a multi-part message in MIME format".

Внимание! Если планируется добавление дополнительных текстовых частей (TIdText), то свойство TIdMessage1.Body трогать нельзя, т.к. это "убъёт" стандартную строку "This is a multi-part message in MIME format" и почтовые программы не смогут корректно обработать такое письмо.
TIdAttachmentFile.Create(
  IdMessage1.MessageParts,
  'C:\myfile.zip');
- Осуществляет добавление к письму файлового вложения. Свойство MessageParts предоставляет доступ к файловым вложениям и дополнительным частям письма.
Внимание! При использовании неюникодных версий Delphi имя файла должно содержать только латинские символы и другие знаки с кодами <= 127. Indy не сможет корректно обработать файл, имя которого содержит символы кириллицы. В Indy не предусмотрена возможность задать имя файла в кодировке UTF8. Также нет возможности указать нужную кодировку (в юникодных версиях Delphi эта проблема полностью устранена!).
Следует отметить, что для кодирования файловых вложений по умолчанию всегда используется Base64.
with TIdText.Create(
  IdMessage1.MessageParts) do
begin
  Body.Text := '<h1>HTML-текст!</h1>'+
    '<p>Hello, мир!</p>';
  ContentType := 'text/html';
  CharSet := IdCharsetNames[idcsUTF_8];
  ContentTransfer := 'base64';
end;
- Добавляет дополнительную текстовую часть к письму. В том случае, если текстовая часть содержит HTML-код, то необходимо выставить соответствующее значение для свойства ContentType. Для каждого текстового вложения можно указать индивидуальный транспортный формат, например 'base64', 'quoted-printable', '8bit' и т.п.
Внимание! Вы не должны трогать свойство TIdMessage.Body, если планируете использование дополнительных текстовых частей!

* Подробное описание Base64 смотрите в Википедии.

Рассмотрим свойства компонента TIdSMTP, необходимые для отправки письма:

IdSMTP1.Host :=
  'smtp.inbox.ru';

- Адрес SMTP-сервера, с помощью которого планируется выполнить отправку письма
IdSMTP1.Port := 25; - TCP-порт SMTP-сервера
IdSMTP1.Username :=
  'sender';
IdSMTP1.Password :=
  'mypassw';
- Имя и пароль пользователя. Нужны только в том случае, если SMTP-сервер требует авторизацию пользователя для отправки письма. Если авторизация не требуется, то данные параметры игнорируются.
IdSMTP1.MailAgent :=
'LDS Mail Agent';
   - Имя почтового агента (например наименование вашей программы).
Внимание! Имя следует указывать с использованием латинского алфавита (кириллица обрабатывается некорректно).
IdSMTP1.IOHandler :=
  MyHandler;

где
MyHandler :=
  TMyIOHandler.Create(nil);
  - Позволяет указать объект, через который проходит вся исходящая и входящая информация. Класс объекта MyHandler должен быть наследником от TIdIOHandlerStack (модуль IdIOHandlerStack). В классе TIdIOHandlerStack реализованы методы отправки и приема данных (WriteXXX и ReadXXX), объявленные виртуальными. Их можно переопределить в наследнике и использовать, например, для логгирования всей передаваемой и принимаемой информации.

Пример реализации TMyIOHandler для Delphi 2007:

type
  TMyIOHandler = class(TIdIOHandlerStack)      
 
public
    procedure Write(ABuffer: TIdBytes); overload; override;

    function ReadLn(ATerminator: string; ATimeout,
      AMaxLineLength: Integer): string; overload; override;
  end;

.....................

procedure TMyIOHandler.Write(ABuffer: TIdBytes);
var
  S: string;
begin
  inherited Write(ABuffer);
  SetString(S, PChar(@ABuffer[0]), Length(ABuffer));
  // Вывод строки S в лог или на экран
end;

function TMyIOHandler.ReadLn(ATerminator: string; ATimeout,
  AMaxLineLength: Integer): string;
begin
  Result := inherited ReadLn(ATerminator, ATimeout, AMaxLineLength);
  // Вывод строки Result в лог или на экран
end;

Пример реализации TMyIOHandler для Delphi 2010:
type
  TMyIOHandler = class(TIdIOHandlerStack)      
  public
    function ReadDataFromSource(var VBuffer: TIdBytes): Integer; override;

    function WriteDataToTarget(const ABuffer: TIdBytes;
      const AOffset, ALength: Integer): Integer; override;
  end;

.....................

function TMyIOHandler.ReadDataFromSource(var VBuffer: TIdBytes): Integer;
var
  S: AnsiString;
begin
  Result := inherited ReadDataFromSource(VBuffer);
  if Length(VBuffer) > 0 then
    SetString(S, PAnsiChar(@VBuffer[0]), Length(VBuffer));
  // Вывод строки S в лог или на экран
end;

function TMyIOHandler.WriteDataToTarget(const ABuffer: TIdBytes; const AOffset,
  ALength: Integer): Integer;
var
  S: AnsiString;
begin
  Result := inherited WriteDataToTarget(ABuffer, AOffset, ALength);
  SetString(S, PAnsiChar(@ABuffer[AOffset]), ALength);
  // Вывод строки S в лог или на экран
end;


Для отправки письма можно использовать следующий код:

IdSMTP1.Connect; // Подключение к SMTP-серверу
try
  IdSMTP1.Send(IdMessage1); // Отправка сообщения
finally
  IdSMTP1.Disconnect(); // Разрыв соединения с сервером
end;


Следует отметить, что метод Disconnect в ряде случаев не разрывает (или разрывает некорректно) соединение с сервером (видимо, в исходных кодах Indy имеются некоторые проблемы). По опыту автора, более надежным (по крайней мере для Delphi 2007) является создание отдельного экземпляра TIdSMTP при каждой отправке письма, т.е. с использованием следующего кода:

IdSMTP1 := TIdSMTP.Create(nil); // Создание объекта
try
  // Настройка параметров.....
  IdSMTP1.Connect; // Подключение к SMTP-серверу
  IdSMTP1.Send(IdMessage1); // Отправка сообщения
  IdSMTP1.Disconnect(); // Разрыв соединения с сервером
finally
  IdSMTP1.Free; // Удаление объекта
end;


Мы рассмотрели основные возможности, предлагаемые библиотекой Indy для отправки электронной почты. На самом деле возможностей в Indy гораздо больше, но нельзя их все охватить в рамках одной статьи.

6. Проверка и получение электронной почты с использованием компонента TIdPOP3

Задача получения и обработки электронной почты встает перед программистом несколько реже, чем задача отправки электронной почты. В связи с этим материал будет представлен в минимальном объеме. При подготовке материала по TIdPOP3 использовалась только Delphi 2010, поэтому автор не гарантирует, что всё в неизменном виде будет работать со старыми версиями Delphi.

Для проверки и получения электронной почты нам потребуются:
- компонент TIdPOP3 (расположен в палитре компонентов на вкладке "Indy Clients")
- компонент TIdMessage (расположен на вкладке "Indy Misc")
- подключить те же модули, что и для SMTP, за исключением модулей IdSMTP и IdAttachmentFile. Также понадобятся дополнительные модули: IdPOP3, содержащий реализацию класса TIdPOP3, IdAttachment, содержащий реализацию класса TIdAttachment и IdMessageParts, содержащий реализацию класса TIdMessagePart.

Рассмотрим свойства и методы компонента TIdPOP3, необходимые для проверки почтового ящика и получения писем:

POP3.Host := pop.inbox.ru';
  - Адрес POP3-сервера, с помощью которого планируется выполнить проверку почтового ящика и получение почты.
POP3.Port := 110; - Порт POP3-сервера
POP3.Username := 'user'; - Имя пользователя
POP3.Password := 'mypassw'; - Пароль пользователя
POP3.IOHandler := MyHandler; - Позволяет указать объект, через который проходит вся исходящая и входящая информация. См. предыдущий раздел.
POP3.Connect; - Осуществляет подключение к POP3-серверу. При этом автоматически выполняются следующие действия:
1) с помощью команды CAPA запрашивается список возможностей POP3-сервера;
2) осуществляется авторизация с помощью команд USER и PASS.
Cnt := POP3.CheckMessages; - Возвращает количество писем в почтовом ящике (с помощью команды STAT). При этом стартует транзакцию.
Sz := POP3.RetrieveMailBoxSize; - Возвращает общий размер писем в почтовом ящике. Для этого отсылает команду LIST, а затем последовательно суммирует размер каждого из писем. Обычно в данной функции нет необходимости, поскольку все равно приходится перебирать все письма и для каждого определять размер с помощью RetrieveMsgSize.
Sz :=
POP3.RetrieveMsgSize(5);
- Возвращает приблизительный размер указанного письма (в примере - с номером 5). Для этого отсылает команду: LIST 5. Точный размер письма заранее определить невозможно, поскольку POP3-сервер может скорректировать его заголовки в процессе загрузки.
POP3.RetrieveHeader(
  5, IdMessage1);
- Возвращает заголовки указанного письма и записывает их в указанный объект TIdMessage. Для этого отсылает команду: TOP 5 0 (в данном случае 5 - это порядковый номер сообщения).
POP3.Top(5, AList, 20); - Считывает заголовки указанного письма, а также первые 20 строк из тела письма и записывает в указанный объект TStrings (команда TOP 5 20). Может использоваться для предварительного ознакомления с содержимым письма без необходимости загружать его целиком.
POP3.Retrieve(
  10, IdMessage1);
- Полностью загружает письмо с заданным номером в указанный объект TIdMessage (команда RETR 10)
POP3.Delete(8); - Удаляет на почтовом ящике письмо с заданным номером (команда DELE 8). Обычно после получения письма его следует удалить с сервера для избежания накопления в нем ненужных писем.
POP3.Disconnect; - Разрывает соединение с POP3-сервером.

Для загрузки письма с указанным номером можно использовать следующий код:

POP3.Connect; // Подключение к POP3-серверу
try
  POP3.Retrieve(1, IdMessage1); // Загрузка сообщения
finally
  POP3.Disconnect(); // Разрыв соединения с сервером
end;


Для снятия всех писем и их последующего удаления можно использовать следующий код:

POP3.Connect; // Подключение к POP3-серверу
try
  for
I := 1 to POP3.CheckMessages do
  begin

    POP3.Retrieve(I, IdMessage1); // Загрузка письма
    // Обработка полученного письма (его сохранение)
    POP3.Delete(I); // Удаление письма на сервере
  end;
finally
  POP3.Disconnect(); // Разрыв соединения с сервером
end;


Считаю, что нет необходимости подробно останавливаться на вопросе обработки полученного письма. Основные свойства класса TIdMessage были описаны в предыдущем разделе. Еще раз напомню, что если письмо содержит файловые вложения или дополнительные текстовые части, то в принятом письме свойство TIdMessage.Body.Text вероятнее всего будет содержать единственную строку "This is a multi-part message in MIME format". Для извлечения текста из таких писем следует воспользоваться классом TIdText. Ниже представлен пример извлечения из принятого письма файловых вложений и дополнительных текстовых частей:

// Перебираем все части письма
for I := 0 to IdMessage1.MessageParts.Count - 1 do
begin  
  // var MessPart: TIdMessagePart
  MessPart := IdMessage1.MessageParts[I];

  if MessPart.PartType = mptText then
  begin // Если это текстовая часть
    // Извлекаем текст и помещаем его в TMemo
    Memo1.Lines.Add('Текстовая часть: ' +
      TIdText(MessPart).Body.Text);
  end
  else 
  begin // Если это файловое вложение (PartType=mptAttachment)
    // Запоминаем имя файла (var AttachFile: string)
    AttachFile := TIdAttachment(MessPart).FileName;

    // Содержимое файлового вложения будем сохранять в TFileStream
    // (var AFileStream: TFileStream) 

    // Предполагается, что объект создан где-то заранее
    // В качестве имени файла может использоваться AttachFile
    TIdAttachment(MessPart).SaveToStream(AFileStream);
  end;
end;


7. Ссылки

Наиболее полная документация на проекте Indy http://www.indyproject.org/sockets/docs/index.en.aspx

Логинов Дмитрий © 2005-2015