Приветствую всех читателей блога WebDelphi.ru! Очень надеюсь, что затянувшаяся вынужденная пауза в работе блога подходит к концу. И в скором времени те, кто ожидает продолжение постов про DevExpress и Google Календарь получат пищу для размышлений в виде статьи (черновой вариант уже недели две как пылится в блоге). Этот пост я решил снова посвятить библиотеке Synapse и снова рассмотреть несколько вопросов относительно работы с почтой. В качестве отправной точки для написания статьи я решил использовать тему с форума и один из последних комментариев в статье, посвященной использованию Synapse для работы с GMail.
Про Synapse я рассказываю в блоге уже довольно давно (и, что интересно, в «пиаре» Synapse обвинен ещё не был). И хоть и стараюсь обычно рассказывать так, чтобы было не только интересно и с картинками, но и понятно, вопросы все равно появляются самые разные. На некоторые вопросы отвечаю сразу, где-то сам долго «туплю» прежде, чем смогу найти решение, а какие-то вопросы по Synapse коплю до тех пор, пока не наберется «критическая масса» для отдельного поста и свободная минутка как, например, для этого поста.
Прежде всего, избегая вопросов и претензий типа «а у меня тут ошибка«, «а код не работает» и т.д. пишу жирными буквами и выделяю особо следующие моменты, которые следует учитывать при копипасте всего исходного кода, рассмотренного ниже:
-
-
- используется последний на момент написания статьи релиз Synapse (Release 40, rev. 182)
- для работы с почтой используются сервера mail.ru, yandex.ru и gmail.com. Работу с другими серверами проверяйте самостоятельно.
- Весь исходный код написан в Delphi XE3 со всеми установленными апдейтами.
- НЕТ я НЕ использую Delphi 7 и версии Delphi младше XE2. Нет, у меня нет установленной Delphi 7
- Код проверялся на Windows 7
-
Для работы нам потребуются следующие модули Synapse: smtpsend, pop3send, blcksock, ssl_openssl, mimemess, mimepart, synautil, synachar.
Отправка писем с вложениями
Отправка писем с использованием Synapse рассматривалась в следующих статьях:
- Synapse. Отправка писем, используя SMTP.
- Synapse в Delphi. Отправка писем с вложениями.
- Компонент для отправки почты с GMail.com.
Начнем с отправки c самого простого. Открываем Delphi (желательно XE3, т.к. см. выше), создаем новый проект VCL Application и бросаем на форму компоненты, как показано на рисунке ниже:
Теперь разберемся как происходит отправка писем с помощью Synapse. По большому счёту, отправку любого письма (хоть с вложением, хоть без) выполняется в пять шагов, которые можно представить в виде вот такой небольшой таблички:
Для того, чтобы облегчить нам работу с Synapse, разработчики написали несколько вспомогательных методов для отправки писем и поместили их все в тот же модуль SmtpSend.pas. В этих методах нет ничего «сверхъестественного», все они используют в работе TsmtpSend:
function SendToRaw(const MailFrom, MailTo, SMTPHost: string; const MailData: TStrings; const Username, Password: string): Boolean; function SendTo(const MailFrom, MailTo, Subject, SMTPHost: string; const MailData: TStrings): Boolean; function SendToEx(const MailFrom, MailTo, Subject, SMTPHost: string;const MailData: TStrings; const Username, Password: string): Boolean;
Удобно пользоваться этими функциями — пользуйтесь. Хочется, чтобы все было «по уму» — отображение прогресса отправки писем, лог работы клиента и все такое прочее — используем класс TSmtpSend без всяких функций-обёрток.
Сегодня мы будем пользоваться только классом TSmtpSend. Для начала, напишем обработчик события OnClick для кнопки «Проверить подключение». Вполне возможно, что, если Вы впервые сталкиваетесь с SMTP в Synapse, то могли проверять подключение так:
procedure Tfmain.btnLoginClick(Sender: TObject); begin SMTPClient.TargetHost:=edHost.Text; SMTPClient.TargetPort:=edPort.Text; SMTPClient.UserName:=edLogin.Text; SMTPClient.Password:=edPassword.Text; SMTPClient.AutoTLS:=CheckBox1.Checked; if SMTPClient.Login then begin ShowMessage('Проверка соединения прошла успешно'); SMTPClient.Logout; end else raise Exception.Create('Ошибка соединения с сервером'); end;
НО мы, используя код выше проверили только соединение с сервером (хотя и задали и логин и пароль пользователя), но не авторизацию пользователя на сервере. В итоге можно получить такую ситуацию: пользователь вводит правильный хост/порт/логин, но в пароле случайно допускает небольшую ошибочку. Ваша программа сообщает на проверке, что все в порядке, а письмо не отправляется…Чтобы такого не произошло и Вы были уверены, что заданные пользователем данный корректны, а письма будут отправляться
То есть, окончательный вариант кода проверки соединения с сервером должен выглядеть так:
procedure Tfmain.btnLoginClick(Sender: TObject); begin SMTPClient.TargetHost:=edHost.Text; SMTPClient.TargetPort:=edPort.Text; SMTPClient.UserName:=edLogin.Text; SMTPClient.Password:=edPassword.Text; SMTPClient.AutoTLS:=CheckBox1.Checked; if SMTPClient.Login then if SMTPClient.AuthDone then //--> проверили авторизацию пользователя begin ShowMessage('Проверка соединения прошла успешно'); SMTPClient.Logout; Exit; end; raise Exception.Create('Ошибка соединения с сервером'); end;
Вот теперь можно двигаться далее. А далее пройдемся сразу по нескольким вопросам, которые в разное время задавались по несколько раз в разных постах разными людьми :)
Как отправить письмо с вложением нескольким адресатам?
Здесь, на самом деле, нет ничего сложного. Про то как собрать и закодировать письмо с вложением я рассказывал в статье «Synapse в Delphi. Отправка писем с вложениями«. И, если посмотреть внимательно на приведенный в статье код, то мы можем там увидеть такие строчки:
Msg:=TMimeMess.Create; try Msg.Header.From:=edEmail.Text; Msg.Header.ToList.Add(edName.Text);//записали получателя Msg.Header.Subject:=edTheme.Text; [...] finally Msg.Free end;
В Synapse, как и полагается, список получателей — это действительно список. ToList — это обычный TStringList и создается без каких-либо изменений, т.е. в качестве разделителя используется запятая. Так что, никаких проблем, по факту-то нет. Если список получателей письма у вас также как и у меня в программке пишется в Edit, то достаточно записать список строку с получателями вот так:
«Иван Иванов» <Ivan@mail.ru>, «Петя Петров» <Petya@mail.ru>
Или вообще так:
Ivan@mail.ru, Petya@mail.ru
И при отправке письма записать эту строку с свойство ToList вот так:
Msg.Header.ToList.DelimitedText:=edName.Text;
С адресами разобрались, теперь о самой отправке. Как было сказано в табличке выше, если получателей несколько, то на каждый адрес получателя должна выполняться команда MAIL TO. Так мы и сделаем. Пишем код обработчика OnClick для кнопки «Отправить»:
procedure Tfmain.btnSendClick(Sender: TObject); var Msg : TMimeMess; MimePart: TMimePart; S,T: string; sended: boolean; begin {задаем настройки для smtp} with SMTPClient do begin TargetHost:=edHost.Text; TargetPort:=edPort.Text; UserName:=edLogin.Text; Password:=edPassword.Text; AutoTLS:=CheckBox1.Checked; end; {собираем сообщение} Msg:=TMimeMess.Create; try Msg.Header.From:=edEmail.Text;//определяем отправителя Msg.Header.ToList.DelimitedText:=edName.Text;//список получателей Msg.Header.Subject:=edTheme.Text;//тема письма MimePart:=Msg.AddPartMultipart('mixed', nil);//создаем "корень" сообщения if rbHtml.Checked then //будем отправлять текст письма как HTML-код Msg.AddPartHTML(memText.Lines, MimePart) else Msg.AddPartText(memText.Lines, MimePart);//отправляем как Plain text {проверяем и добавляем вложения} if FileExists(edFile.Text) then Msg.AddPartBinaryFromFile(edFile.Text,MimePart); if FileExists(edFile2.Text) then Msg.AddPartBinaryFromFile(edFile2.Text,MimePart); //кодируем Msg.EncodeMessage; //пробуем отправить if (SMTPClient.Login)and(SMTPClient.AuthDone) then //соединились с сервером и авторизовались begin //устанавливаем обратный адрес if SMTPClient.MailFrom(GetEmailAddr(Msg.Header.From), Length(Msg.Lines.Text)) then begin s := edName.Text;//получаем список получателей {для каждого получателя выполняем команду MAIL TO} repeat t := GetEmailAddr(Trim(FetchEx(s, ',', '"')));//получаем email-адрес if t <> '' then //есть адрес sended := SMTPClient.MailTo(t); if not sended then //не смогли выполнить команду - прерываем выполнение Break; until s = ''; if sended then //все в порядке - выполняем команду DATA sended := SMTPClient.MailData(Msg.Lines); end; if sended then ShowMessage('Письмо успешно отправлено') else ShowMessage('Во время отправки письма произошла ошибка'); SMTPClient.Logout;//уходим end; finally Msg.Free; end; end;
К меня эти библиотеки прописались в системе уже где только можно было, поэтому у меня проблем с отправкой никаких нет, а у вас или у ваших пользователей проблемы могут возникнуть. Кстати, скачать dll-ки, которыми пользуюсь я можно со страницы с исходниками. Переходим к следующему вопросу.
Как отправлять копии писем (использовать поля CC и BCC в email)?
Вначале небольшая справка о том, чем отличается поле CC от BCC в Email.
cc: (от англ. carbon copy). Содержит имена и адреса вторичных получателей письма, к которым направляется копия.
bcc: (от англ. blind carbon copy). Содержит имена и адреса получателей письма, чьи адреса не следует показывать другим получателям. Это поле обычно обрабатывается почтовым сервером (и приводит к появлению нескольких разных сообщений, у которых bcc содержит только того получателя, кому фактически адресовано письмо). Каждый из получателей не будет видеть в этом поле других получателей из поля bcc.
Есть определенные «правила хорошего тона» когда и какие поля следует заполнять, но их мы оставим в покое, т.к. сейчас речь про то как этими полями пользоваться вообще.
С полями To все понятно — все получатели у нас пишутся в свойство Header.ToList у класса TMimeMess. И этими полями мы пользовались выше при отправке писем.
С полями CC тоже проблем быть не должно — у того же класса есть свойство Header.ССList. Заполняется свойство также как и ToList.
Остается вопрос по тому как записать поля BCC? В принципе, я уже много-много раз говорил, но повторюсь ещё раз (повторение — мать учения) — Synapse очень простая библиотека. Достаточно взглянуть чуть по-внимательнее на класс TMessHeader (заголовки сообщения), чтобы увидеть такое свойство:
property CustomHeaders: TStringList read FCustomHeaders;
И никто нам не запрещает им пользоваться. Ради проверки, изменим в нашей процедуре отправки письма строку:
Msg.Header.ToList.DelimitedText:=edName.Text;
на такую
Msg.Header.CustomHeaders.Add('BCC: '+edName.Text);
и снова попробуем отправить письмо на несколько адресов. В итоге на каждый ящик, указанный в BCC придёт отдельная копия письма. Видео-ролик я выкладывать здесь уже не буду, просто поверьте, что это работает :) Двигаемся далее.
Как получать отчёты о доставке писем?
Вот этот вопрос уже по-интереснее. Дело в том, что есть как минимум три возможности сделать доставку уведомлений:
- с использованием заголовков типа Return-Receipt-To, Errors-To, Status и т.д. Все эти заголовки указаны в RFC 2076, но помечены как не стандартные и не рекомендуемые к использованию. Следовательно сервер может просто «не понимать», что от него хотят.
- Использование заголовка Disposition-Notification-To, которому посвящен RFC 2298. Вроде бы и стандарт есть и использовать можно, но элементарная проверка без Delphi и Synapse показала, что то же mail.ru никаких уведомлений не получает, хотя при отправке писем и на yandex.ru и на gmail.com я ставил в интерфейсе mail.ru галку «С уведомлением»
- Использовать DSN (Delivery Status Notifications). Опять же, этот механизм хоть и может использоваться почтовыми серверами, но используется он далеко не всегда…
В общем, в поисках ответа на этот вопрос я перерыл довольно не малое количество всяческих RFC, админских форумов и т.д. и т.п., но абсолютно надежного, со 100% гарантией способа получать уведомления о доставке писем не нашел — практически с каждым почтовым сервером приходится разбираться отдельно и выбирать тот или иной способ работы. Вот такая вот печалька
Но нет худа без добра. Зато появилась возможность по-глубже закопаться в Synapse и посмотреть как можно «прикрутить» использование DSN к TSMTPSend.
Итак, как использовать DSN? Для того, чтобы мы могли воспользоваться этой возможностью, чтобы получать уведомления о доставке/задержке/ошибке сообщения мы, в первую очередь обязаны убедиться в том, что сервер поддерживает DSN.
Чтобы запросить дополнительные возможности SMTP-сервера мы можем отправить на сервер команду EHLO. Если сервер понимает эту команду, то он вернет нам список дополнительных расширений, например, максимальный размер сообщения (SIZE), способы авторизации пользователя и, в том числе, «скажет» поддерживается ли DSN.
Я уже говорил, что Synapse это очень простая библиотека? По-моему говорил. Дело в том, что команда EHLO используется при выполнении метода Login у TSmtpSend. И, если сервер поддерживает эту команду, то заполняется свойство:
property ESMTPcap: TStringList read FESMTPcap;
В котором и содержатся все поддерживаемые сервером расширения. Если сервер не поддерживает EHLO, то список будет пуст. Посмотрим, что «скажут» о себе сервера mail.ru, yandex.ru и gmail.com.
Ответ mail.ru
SIZE 73400320
8BITMIME
AUTH PLAIN LOGIN
STARTTLS
Ответ Gmail.com
SIZE 35882577
8BITMIME
AUTH LOGIN PLAIN XOAUTH XOAUTH2
ENHANCEDSTATUSCODES
8BITMIME
PIPELINING
SIZE 42991616
STARTTLS
AUTH LOGIN PLAIN
DSN
ENHANCEDSTATUSCODES
Оказывается наш отечественный почтовик по количеству дополнительных «фич» по-круче gmail будет…ну ладно. Из приведенных выше ответов становится понятно, что далее мы будем работать с SMTP yandex.ru, т.к. на других серверах тестить DSN бесполезно — не поддерживается.
Определившись с сервером переходим к главному — как получать уведомления о состоянии нашего письма? Открываем RFC 3461 и начинаем вникать в суть вопроса. Буквально в первых абзацах RFC нам говорят, что DSN расширяет команды RCPT и MAIL четырьмя дополнительным параметрами. Так, например, чтобы получить отчёт о доставке письма адресату от нас требуется включить в команду RCPT параметр NOTIFY=SUCCESS, чтобы получить отчёт об ошибке — NOTIFY=FAILURE и т.д. А можем и вообще включить все возможные значения параметра NOTIFY, например, так:
RCPT TO:<Dana@Ivory.EDU> NOTIFY=SUCCESS,FAILURE,DELAY
Проблема в том, что у TSMTPSend есть только один метод, отсылающий на сервер команду RCPT и называется он MailTo. Менять исходник Synapse — не серьезно. Но Synapse простая библиотека, а Delphi уже довольно давно позволяет нам расширять классы без всяких наследников — через хэлперы. Этой возможностью Delphi мы и воспользуемся — напишем простенький class helper для класса TSMTPSend.
Итак, возвращаемся из RFC в Delphi и пишем в главном (и единственном) модуле нашего приложения такой хэлпер:
объявляем хэлпер
type TSMTPClientHelper = class helper for TSMTPSend function MailToEx(const AEmail:string; ANotifyString: string):boolean; end;
пишем код метода MailToEx:
function TSMTPClientHelper.MailToEx(const AEmail: string; ANotifyString: string): boolean; var S: string; begin Sock.SendString('RCPT TO:<' + AEmail + '> ' +ANotifyString + CRLF); repeat s := Sock.RecvString(FTimeout); if Sock.LastError <> 0 then Break; until Pos('-', s) <> 4; Result := StrToIntDef(Copy(s, 1, 3), 0) div 100 = 2; end;
Здесь стоит обратить внимание на один важным момент. Дело в том, что
Сейчас же нам диагностика по большому счёту не требуется, нам надо научиться получать уведомление о доставке. Поэтому теперь возвращаемся в обработчик кнопки «Отправить» и меняем строку:
sended := SMTPClient.MailTo(t);
на вот такую
sended := SMTPClient.MailToEx(t,'NOTIFY=SUCCESS,FAILURE');
Вот и всё. Теперь при отправке письма с почты Яндекса мы будем получать уведомления о том, что письмо доставлено адресату или, если при доставке произошла ошибка.
С DSN чуть-чуть разобрались, но как быть, например, с mail.ru, который DSN не поддерживает? На этот вопрос у меня пока ответ один — используйте заголовки о которых я говорил выше и надейтесь на то, что принимающий сервер их понимает и всячески поддерживает…хотя этот вариант очень не надежный, но другого у меня нет.
Ну вот. С отправкой писем более менее разобрались. Как видите, ничего сверхсложного нет, все, о чем было рассказано выше, работает. Переходим к следующему моменту работы с почтой в Synapse.
Визуализация отправки почты
Что касается обработки событий у TSMTPSend, то она ничем не отличается от той же обработки событий у другого класса Synapse — THTTPSend. Про обработку событий я также рассказывал несколько раз:
Что тут можно добавить? Визуализировать абсолютно весь процесс отправки письма с помощью заполнения того же ProgressBar’а можно, но не нужно. Поясню почему для тех, кто хоть убейся, но желает вытащить в progressbar все до последнего байта. Если Вы внимательно прочитали все, что сказано выше, то наверняка уже усвоили, что перед тем как мы начнем отправлять на сервер тело письма (выполнять команду DATA) мы выполняем ещё ряд команд типа EHLO/HELO, RCPT, MAIL и т.д. Да, эти команды «жрут» трафик, но сколько? Пару килобайт от силы (если только вы не занимаетесь спамом и не шлете свое письмо сразу миллиарду получателей) так не проще ли все эти небольшие команды просто выводить в тот же лог или просто выводить текст в тот же Label типа «подключаемся к серверу», «отправляем команду X на сервер Y» и т.д? По-моему, вполне нормальная практика. А вот уже непосредственную отправку письма весом, скажем в несколько мегабайт визуализировать стОит. Остается только вовремя назначить обработчик события и вовремя же его убрать. И заниматься визуализацией у нас будет, также как и в этой статье, обработчик события OnStatus.
Теперь от слов к делу. Открываем Delphi и пишем вот такую процедуру:
type Tfmain = class(TForm) [...] private procedure OnStatusEvent(Sender: TObject; Reason: THookSocketReason; const Value: String); public end; procedure Tfmain.OnStatusEvent(Sender: TObject; Reason: THookSocketReason; const Value: String); begin if Reason=HR_WriteCount then ProgressBar1.Position:=ProgressBar1.Position+StrToInt(Value) end;
Всё. Кто-то ожидал чего-то жутко навороченного? Сорри, если разочаровал :) Теперь назначаем обработчик. Чтобы наш ProgressBar не «дёргался» при каждой отправки данных на сервер, обработчик надо назначить непосредственно перед выполнением метода MailData и освободить после выполнения метода. То есть возвращаемся в обработчик кнопки «Отправить» и дописываем четыре новых строки:
[...] if SMTPClient.MailFrom(GetEmailAddr(Msg.Header.From), Length(Msg.Lines.Text)) then begin ProgressBar1.Max:=Length(Msg.Lines.Text);//максимальное значение - размер уже собранного письма ProgressBar1.Position:=0;//сбрасываем позицию прогрессбара на ноль [...] if sended then begin SMTPClient.Sock.OnStatus:=OnStatusEvent;//назначили обработчик sended := SMTPClient.MailData(Msg.Lines); SMTPClient.Sock.OnStatus:=nil;//убрали обработчик end; [...]
Больше ничего не надо. Запускаем приложение, создаем письмо с большим вложением и отправляем — увидите, что прогресс бар заполняется идеально ровно.
В этой же части статьи стоит обратить внимание (уже наверное в тысячный раз) на один момент работы Synapse о котором почему-то приходится постоянно напоминать новичкам, предъявляющим претензии к работе Synapse:
Отсюда и все якобы «проблемы» с Synapse, что дескать при работе с Synapse программа «зависает». А как должна вести себя программа при синхронной работе? Именно, что «висеть» пока не закончится работа метода Synapse. Про синхронную и асинхронную работу с сокетами очень емко и толково рассказано, кстати, в книге «Глубины Indy« (Indy тоже, кстати, использует синхронную работу). Те, кто до сих пор считает, что Synapse работает не правильно и «вешает» программу — очень советую скачать эту книгу и зазубрить наизусть главу «Блокирующий режим против неблокирующего«, а потом скачать книгу про Delphi и заучить наизусть другую главу — про потоки (TThread) и уже потом начинать работать над программой. Это не злая шутка, говорю абсолютно серьезно — прочитайте! Очень сильно пригодиться в дальнейшем, тем более, что ссылочку я дал на русский перевод книги. Мне по, крайней мере, в свое время эта книга очень хорошо помогла вникнуть в основы работы с Сетью вообще. Ну, а если лень читать книги, то тогда взять в руки набор компонентов ICS и радоваться жизни — эти компоненты работают асинхронно.
С визуализациями тоже вроде бы разобрались. Переходим к следующему моменту работы с почтой — диагностике работы TSmtpSend.
Диагностика работы TSmtpSend.
Для правильной диагностики работы с SMTP нам опять же никуда не деться без RFC. Для работы нам потребуются RFC 821 — здесь содержится информация об SMTP вообще и о кодах статуса в частности, а также RFC 1893 — содержит информацию о расширенных кодах состояния (Enhanced Mail System Status Codes).
Чтобы разобраться где какие коды используются сделаем в нашей программе следующее — напишем обработчик для события сокета OnReadFilter. Обработчик очень простой:
procedure Tfmain.OnDataFilterEvent(Sender: TObject; var Value: AnsiString); begin memLog.Lines.Add(Value) end;
Теперь назначим этот обработчик:
procedure Tfmain.FormCreate(Sender: TObject); begin SMTPClient:=TSMTPSend.Create; SMTPClient.Sock.OnReadFilter:=OnDataFilterEvent; end;
Теперь попробуем отправить письмо, скажем, с mail.ru на yandex.ru. Получим вот такой лог работы:
220 smtp20.mail.ru ESMTP ready
250-smtp20.mail.ru
250-SIZE 73400320
250-8BITMIME
250-AUTH PLAIN LOGIN
250 STARTTLS
220 2.0.0 Start TLS
250-smtp20.mail.ru
250-SIZE 73400320
250-8BITMIME
250 AUTH PLAIN LOGIN
235 Authentication succeeded
250 OK
250 Accepted
354 Enter message, ending with «.» on a line by itself
250 OK id=1UFpz1-0007UK-0Y
221 smtp20.mail.ru closing connection
Здесь представлены все ответы сервера. Первые три цифры в ответе — это обычный код статуса, информацию по каждому из которых можно получить из RFC 821.
Код статуса пишется после выполнения каждой команды в свойство ResultCode TSmtpSend:
property ResultCode: Integer read FResultCode;
Теперь попробуем сделать наоборот — отправим письмо с yandex.ru на mail.ru. Как было показано выше, Yandex помимо всего прочего поддерживает отправку расширенных кодов состояния (строка ENHANCEDSTATUSCODES в списке). Вот как будет выглядеть лог в этом случае:
220 smtp2.mail.yandex.net ESMTP (Want to use Yandex.Mail for your domain? Visit http://pdd.yandex.ru)
250-smtp2.mail.yandex.net
[…] 250-PIPELINING
250-SIZE 42991616
250-STARTTLS
250-AUTH LOGIN PLAIN
250-DSN
250 ENHANCEDSTATUSCODES
235 2.7.0 Authentication successful.
250 2.1.0 <bvv36@yandex.ru> ok
250 2.1.5 <vlad383@mail.ru> recipient ok
354 Enter mail, end with «.» on a line by itself
250 2.0.0 Ok: queued on smtp2.mail.yandex.net as 7hgu804R-7kginmdR
221 2.0.0 Closing connection.
Обратите внимание на строки, начиная с 235 2.7.0 Authentication successful.. Вторая тройка чисел, разделенная точками — это и есть расширенный код. Эти коды помогают нам, в случае необходимости, более детально разобраться в проблеме при отправке писем. Если сервер поддерживаем расширенные коды, то слаться они начинают, начиная с момента авторизации пользователя и вплоть до закрытия сессии. Информацию по расширенным кодам черпаем из RFC 1893. Для хранения расширенных кодов у TSMTPSend используются сразу несколько свойств:
property EnhCode1: Integer read FEnhCode1; property EnhCode2: Integer read FEnhCode2; property EnhCode3: Integer read FEnhCode3;
Эти свойства также меняют свои значения каждый раз после выполнения команды, результат которой содержит расширенный код. Для того, чтобы получить описание расширенного кода у TSMTPSend имеется специальный метод:
function EnhCodeString: string;
Этот метод использует значения свойств EnhCode1, EnhCode2 ,EnhCode3 и возвращает строку с описанием кода. Вызывается метод Вами, соответственно, сразу же после выполнения команды. К примеру, если мы вызовем метод EnhCodeString сразу же после выполнения метода MailTo, то получим такое описание:
Success-Destination mailbox address valid
Точно также можно диагностировать работу и других методов TSmtpSend или, если необходимо, то анализировать самостоятельно все коды.
Ну вот, пожалуй и все, о чем хотелось рассказать сегодня. Тридцать девятая статья про Synapse в блоге WebDelphi.ru закончена. Впору уже какой-нибудь сборник статей что ли издавать…Спонсоры найдутся? ;). Шучу.
Всем удачной работы с Synapse и до новых встреч!
Книжная полка
Описание: Рассмотрены практические вопросы по разработке клиент-серверных приложений в среде Delphi 7 и Delphi 2005 с использованием СУБД MS SQL Server 2000, InterBase и Firebird. Приведена информация о теории построения реляционных баз данных и языке SQL. Освещены вопросы эксплуатации и администрирования СУБД.
|
||
Название: О чем не пишут в книгах по Delphi
Описание: Рассмотрены малоосвещенные вопросы программирования в Delphi. Описаны методы интеграции VCL и API. Показаны внутренние механизмы VCL и приведены примеры вмешательства в эти механизмы. Рассмотрено использование сокетов в Delphi: различные режимы их работы, особенности для протоколов TCP и UDP и др.
|
Влад,великолепная статья. Вы затронули проблему с которой я столкнулся. Да возможно нет никакой проблемы, просто перечислить всех получателей прикрепить файлы и сделать рассылку из листа. Но Зачем каждому получателю видеть куда ещё уходило письмо?
Поэтому я пытался организовать рассылку индивидуально каждому из списка рассылки…
P.S. Продолжу читать статью, остановился чтобы написать комент…
Dozent, в принципе ваша проблема и была одной из отправных точек к написанию этой статьи :) Просто собрал все неотвеченные вопросы в кучу и решил как-нибудь их систематизировать и вот, что из этого вышло :)
А как поступить если на сервере нет АВТОРИЗАЦИИ (да вот такая вот штука) т.е.
250-PIPELINING
250-SIZE 42991616
250-STARTTLS
250-DSN
250 ENHANCEDSTATUSCODES
как видим AUTH нет , в Indy у IdSMTP есть свойство AuthenticationType, которое как я понимаю можно установить в atNone в таких случаях, а как быть с synapse? Просто если пройти по шагам код функции Login (в smtpsend),там происходит поиск AUTH (s := FindCap(‘AUTH ‘)) и естественно ничего не находит и делает Logout.
Простите 2 недели не занимался проектом, были другие дела сейчас опять приступил и ни как не могу понять вот этот код:
procedure Tfmain.btnLoginClick(Sender: TObject);
begin
SMTPClient.TargetHost:=edHost.Text;
SMTPClient.TargetPort:=edPort.Text;
SMTPClient.UserName:=edLogin.Text;
SMTPClient.Password:=edPassword.Text;
SMTPClient.AutoTLS:=CheckBox1.Checked;
if SMTPClient.Login then
if SMTPClient.AuthDone then //—> проверили авторизацию пользователя
begin
ShowMessage(‘Проверка соединения прошла успешно’);
SMTPClient.Logout;
Exit;
end;
raise Exception.Create(‘Ошибка соединения с сервером’);
end;
SMTPClient что это? где описанО?
Всё перемешалось… Попытался пройтись по всем статьям связаным с этой темой, элементарно отправка письма без компанента не получается, хочется работать только с библиотекой… Да я понимаю что это мои заморочки и незнание:)
P.S. Было бы очен ьхорошо сделать одну статью в которой будит от начало и до конца и с проектиком рабочим… Это будит просто великолемно.
Все статьи, связанные с SMTP лежат в верхнем меню «Synapse —> SMTP».
SMTPClient: TSMTPSend
…другого класса для работы с SMTP вроде бы в Synapse никогда не было.
SMTPClient.TargetHost:=’smtp.gmail.com’;
SMTPClient.TargetPort:=’587′;
При выполнении, на шаге присвоения порта выдаёт ошибку доступа к памяти, библиотеки добавлены опенссл в юсес подключён
быть такого не может — TargetHost и TargetPort относятся к одному и тому же классу-предку TSMTPSend. Исходник в студию
Всё норм, я просто глупый и не создал обьект
SMTPClient:=TSMTPSend.Create;
Вообще удалит эту цепочку коментов, ибо это только мои глупости)
Пусть будут…в назедание другим :))
Влад, добрался до CC и BCC. При отправке получается следующее
«кому: скрытая копия: мне, скрытая копия: Andrey»
Т.е. накладывается маска, но получатель всё таки видит скольким людям ещё ушло такое письмо, это не совсем я считаю правильно и для идивидуальной рассылке придётся отправлять письмо каждому в отдельной рассылке, что как мне кажется утяжелит работу кода…
Какой-то выход ещё можете подсказать?
>>но получатель всё таки видит скольким людям ещё ушло такое письмо
С использованием BCC?…нет, не верю :). Как другой пользователь может увидеть, что письмо ушло кому-то ещё, если для него создается совершенно отдельная копия?
Не не я о другом говорю.
«кому: скрытая копия: мне, скрытая копия: Andrey» — Вот такую строку я вижу в списке получателе на почте!
Т.е. получается, что получатель видит что письмо было разослано не только ему, но и ещё комуто, хоть и не видит кому точно!!!
Я сделал расылку на две свои почты, всё дошло без проблем (указал сразу два адреса) и в обоих я вижу что письмо пришло не только на одну из моих почт, но и на другую тоже, вижу что ещё есть получатель, хоть и не вижу его адреса!
«кому: скрытая копия: мне, скрытая копия: Andrey»
Спасибо за статью ,ну очень познавательна.
можно ли отправить письмо с вложением получать уведомление о доставке?
отправляю так…
aMIMEPart : TMimePart;
Msg : TMimeMess;
Msg := TMimeMess.Create;
aMIMEPart := Msg.AddPartMultipart('alternate',nil);
Msg.AddPartBinaryFromFile(FileForSend.Text, aMIMEPart);
smtpsend.SendToRaw(FromMail.Text, ToMail.Text, HostSMTP.Text, Msg.Lines, LoginSMTP.Text, PassSMTP.Text);
Msg.Free;
или можно отправлять вложение используя MailToEx ?
Нет никакой разницы с вложением письмо или нет. Прочитайте, пожалуйста внимательнее, то, что написано вот в этой части статьи.
Спасибо, разобрался.
возникла другая проблема, используя функцию:
sended := SMTPClient.MailToEx(t,’NOTIFY=SUCCESS,FAILURE’);
возвращается:
501 <bla-bla-bla@yandex.ru> NOTIFY=SUCCESS,FAILURE,DELAY: missing or malformed local part (expected word or «<")
при этом письмо не уходит
osiris11, может проблема в том, что в статье, в листинге функции TSMTPClientHelper.MailToEx парсер в блоге вместо символов «<" и ">» поставил
& lt ;
и'& gt ; '
, а вы при копировании их просто не учли? Судя по ответу сервера проблема именно в этих символах.Дайте плиззз рабочий исходник, не получается самому, ибо мои руки кривые((
Спасибо за статью.
Столкнулся с такой проблемой. Пишу ISAPI расширение, которое должно отправлять сообщения на почту после успешной регистрации пользователя на сайте. Если использовать, например, mail.ru (т.е. без SSL/TLS), то письма отправляются. С gmail — ни в какую. Волшебные dll’ки лежат рядом с приложением (тоже dll’кой) в рабочей папке IIS 7. Может их надо куда то в другое место положить…
Таки кинул dll’ки в SysWOW64 и отправка с gmail заработала. Только что то меня это не радует. Потом то это ISAPI расширение надо будет на хостинг кидать и, наверное, возможности поместить dll’ки куда нужно не будет.
Влад, зачем использовать repeat в отправке сообщений, Не проще использовать for?
if SMTPClient.MailFrom(GetEmailAddr(Msg.Header.From), Length(Msg.Lines.Text)) then
for I := 0 to Msg.Header.ToList.Count - 1 do
begin
Result := Integer(SMTPClient.MailTo(Msg.Header.ToList[I]));
if not Result then
Break;
end;
Просто привычка и не более — если тело цикла должно выполниться хотя бы раз, то repeat, если до тела дело может не дойти — while, счётчик — for :)
За статью большое СПАСИБО!!! Проглотил и не заметил….:)
Но есть вопрос или точнее:
Как отправить письмо так чтобы копия письма осталась в папочке ОТПРАВЛЕННЫЕ на серваке (с вложениями)?