|
Работа с электронной почтой с использованием компонентов 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
|