вторник, 21 августа 2012 г.

Delphi Indy 10 SMTP Кодировки и SSL Решение все в одном


При отправке писем через SMTP компоненты Indy в Delphi 7 наталкивался периодически на разные проблемы.
Варианты проблем:

  • отправка писем от машин, у которых - есть русские буквы в названиях
  • отправка писем через SSL
  • проблемы с кодировками в теме и теле письма

В интернете общего решения не находил - только разрозненные части.

Все решения свел здесь. может кому-то пригодится

Конфигурация системы:

  • Delphi 7, 
  • Версия компонентов Indy:10.5.9.4797 (переустанавливал вручную по статье http://conferences.embarcadero.com/article/32160  файл: Indy10_4798.zip)
  • ssl - библиотеки: openssl-1.0.0d-i386-win32.zip
    брал отсюда http://indy.fulgan.com/SSL/
    (в директорию с exe-файлом подложил libeay32.dll и ssleay32.dll)


Ключевая процедура в коде - SendEmail
Важные места подкомментировал.

uses
...

IdCoderMIME , IdMessage, IdSMTP,
IdSSLOpenSSL , IdIOHandler, IdAttachmentFile, IdExplicitTLSClientServerBase, idCharSets, idText
...

type

  //-- вспомогательный класс-заглушка для подвязки события компонента indy
  Tindystub = class(tobject)
  public
    procedure OnInitISO(var VHeaderEncoding: Char; var VCharSet: string);
  end;

procedure Tindystub.OnInitISO(var VHeaderEncoding: Char;
  var VCharSet: string);
begin
  VCharSet := IdCharsetNames[ idcs_UTF_8 ];
end;


procedure SendEmail(
    aSmtpServer: string;
    aSmtpPort: integer;
    aSmtpLogin: string;
    aSmtpPassword: string;
    aFromAddress: string;
    aFromName: string;
    aToAddress: string;
    aMessageSubject: string;
    aMessageText: string;
    aAttachmentFileFullName: string;
    aUseSSL: boolean = false
    );
var
  lMessage : TIDMessage;
  lSMTP    : TIdSMTP;
  attach   : TIdAttachmentFile;
  sslHandler: TIdSSLIOHandlerSocketOpenSSL;
  lindystub: Tindystub;
  idtTextPart: TIdText;
begin
  lMessage:= TIDMessage.Create(nil);
  lSmtp:= TIDSmtp.Create(nil);
  sslHandler:= TIdSSLIOHandlerSocketOpenSSL.Create(nil);
  lindystub:= Tindystub.Create();
  try
    lSMTP.AuthType:= satDefault;

    lSMTP.Host     := aSmtpServer;   //Хост
    lSMTP.Port     := aSmtpPort;     //Порт (25 - по умолчанию)

    lSMTP.Username := aSmtpLogin;
    lSMTP.Password := aSmtpPassword;

    //-- решаем проблему отправки писем, если у компьютера руссоке имя
    lSMTP.HeloName := 'tfo';

    //-- отправляем почту через SSL, если надо (пока тестировал только на ukr.net)
    if (aUseSSL) then
    begin
      SSLHandler.Destination := aSmtpServer+':'+IntToStr(aSmtpPort);
      SSLHandler.Host := aSmtpServer;
      SSLHandler.Port := aSmtpPort;
      SSLHandler.DefaultPort := 0;
      SSLHandler.SSLOptions.Method := sslvSSLv23;
      SSLHandler.SSLOptions.Mode := sslmUnassigned;
      lSMTP.IOHandler := SSLHandler;
      lSMTP.UseTLS := utUseImplicitTLS;
    end;

    //-- решаем вопрос кодировки темы письма
    lMessage.OnInitializeISO := lindystub.OnInitISO;

    lMessage.Subject      := aMessageSubject;
    lMessage.From.Address := aFromAddress;
    lMessage.From.Name    := aFromName;
    lMessage.Recipients.EMailAddresses:= aToAddress;

    //-- решаем вопрос кодировки тела письма
    idtTextPart := TIdText.Create(lMessage.MessageParts, nil);
    idtTextPart.ContentType := 'text/plain';
    idtTextPart.CharSet := 'Windows-1251';

    idtTextPart.Body.Text:= aMessageText;

    //-- работа со вложенными файлами
    if (aAttachmentFileFullName <> '') then
    begin
      attach:=TIdAttachmentFile.Create(lMessage.MessageParts, aAttachmentFileFullName); //Вложение
    end;

    //-- собственно отправка
    lSMTP.Connect;
    try
      lSMTP.Send(lMessage);
    finally
      lSMTP.Disconnect;
    end;

  finally
    sslHandler.Free;
    lMessage.Free;
    lSmtp.Free;
    lindystub.Free;
  end;
end;


Вопросы/пожелания/предложения оставляйте в комментариях

см. также

Обработка почты через POP3 компоненты Indy10 в Delphi 7
http://dev-doc.blogspot.com/2013/02/delphi-indy-10-pop3.html

EMAIL MESSAGES FROM DELPHI. USING INDY TO SEND E-MAILS
https://community.embarcadero.com/article/technical-articles/162-programming/5913-email-messages-from-delphi-using-indy-to-send-emails
---------------------------------
UPD
Если хотите отправить почту через ukr.net
проверьте чтобы были включен параметр "POP3 и SMTP" согласно статье
http://www.qdpro.com.ua/node/17758
(если не сделать, будет выскакивать ошибка SYSERR 417)

11 комментариев:

  1. Вот мой кривой вариант. Вроди все нормально, только не могу отправить с mail.ru (рамблер и яндекс нормально):

    procedure TMessage.sendmailmessage;
    var
    i:integer;
    begin
    ////подключение
    IdSMTP1.Host:='smtp.mail.ru';
    IdSMTP1.Port := 25;
    IdSMTP1.Username:='help';
    IdSMTP1.Password:='пароль';

    ////тело сообщения
    IdMessage1.ContentType := 'multipart/mixed';
    IdMessage1.Body.Text:=Memo1.Text;
    IdMessage1.From.Name := 'help';
    IdMessage1.From.Text:='help@mail.ru';
    IdMessage1.Recipients.Add.Text:='motoarhangel@bk.ru';

    // добавляем текст
    TidText.Create(IdMessage1.MessageParts, idMessage1.Body);

    //добавляем файлы
    if ListBox1.Items.Count > 0 then
    for i := 0 to listbox1.Items.Count - 1 do
    begin
    if (FileExists(listbox1.Items[i])) then
    begin
    TIdAttachmentFile.Create(IdMessage1.MessageParts, listbox1.Items[i]);
    end;
    end;
    // указываем тип текстового блока и его кодировку
    IdMessage1.MessageParts.Items[0].ContentType := 'text/html';
    IdMessage1.MessageParts.Items[0].CharSet := 'Windows-1251';

    ////Конект и отправка
    try
    try
    IdSMTP1.Connect();
    Application.ProcessMessages;
    IdSMTP1.Send(IdMessage1);
    MessageBox(0, 'Письмо успешно отправленно', 'Информация', 0);
    except on E:Exception do
    begin
    MessageBox(0, Pwidechar('Письмо не было отправленно по причине: '+E.Message), 'Информация', 0);

    end;
    end;
    finally
    if IdSMTP1.Connected then IdSMTP1.Disconnect;
    end;
    IdMessage1.free;
    IdSMTP1.Free;
    end;

    Приму все пожелания и исправления =)

    ОтветитьУдалить
  2. На яндекс и рамблер на какой порт отправляете? если не на 25-ый, то скорее всего дело в том, что 25-ый порт у вас закрыт провайдером.

    для mail.ru есть альтернаивные порты без шифрования: 587 и 2525

    ОтветитьУдалить
  3. А какие настройки для укр.нет Вы используете?

    у меня выбивает ошибку

    Socket Error # 10013 Access denied.

    логин и пароль правильный

    ОтветитьУдалить
    Ответы
    1. проверьте настройки здесь:

      http://wiki.ukr.net/%D0%94%D0%BB%D1%8F_%D0%B4%D1%80%D1%83%D0%B3%D0%B8%D1%85_%D0%BF%D0%BE%D1%87%D1%82%D0%BE%D0%B2%D1%8B%D1%85_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC_%D0%B8_%D0%BF%D0%BE%D1%87%D1%82%D0%BE%D0%B2%D1%8B%D1%85_%D0%BF%D1%80%D0%BE%D0%B3%D1%80%D0%B0%D0%BC%D0%BC_%D0%BC%D0%BE%D0%B1%D0%B8%D0%BB%D1%8C%D0%BD%D1%8B%D1%85_%D1%82%D0%B5%D0%BB%D0%B5%D1%84%D0%BE%D0%BD%D0%BE%D0%B2

      Удалить
  4. странно, но все вроде бы так. с другими почтовыми ящиками работает нормально.

    правда, должен сказать что это единственный мануал, по которому удалось решить проблеммы с кодировком письма

    ОтветитьУдалить
    Ответы
    1. для укр.нет должен быть обязательно включен SSL.
      в остальном же только могу посоветовать сделать новый тестовый почтовый ящик и на нем попробовать отправку писем.
      Т.к. у меня этот код рабочий и используется для отправки писем в том числе и через ящики укр. нет.

      Удалить
    2. По ukr.net:
      в настройках ящика включите использование "POP3 и SMTP"
      по статье http://www.qdpro.com.ua/node/17758

      если не сделать, будет выскакивать ошибка SYSERR 417

      Удалить
  5. Спасибо! Из всего, что было в инете на данную тему - Ваш пример реально работает

    ОтветитьУдалить
  6. Отличный пример! Спасибо! Всё заработало с полу-тыка, правда на ukr.net )

    ОтветитьУдалить