все что связано с моей работой
Главная » Програмирование » Отправка письма и проблемы кодировки

Отправка письма и проблемы кодировки

Задача: отправить письмо из консоли, и вложить файл переданный в параметрах. Первая строка первого файла содержит наименование организации откуда отправляется письмо.Имеем Lazarus и Synapse. Вообщем задача отправки письма средствами synapse давно решенная и хорошо описанная. Например тут очень хорошо все расписано, именно эта статья и была взята за основу. Вот результат:

program sendmail;
{$mode objfpc}{$H+}
uses
  Classes,
  SysUtils,
  smtpsend, // отправка почты
  mimemess, // обработка писем с вложениями
  mimepart,  // обработка вложений
  synachar;

const
  Server = 'xxx.ru';
  UserName = 'updater@xxx.ru';
  Password = 'МегаПароль';
  Reciepient = 'admin@xxx.ru';
  From:shortstring = 'updater@xxx.ru';

var
   Subject:string;
   TempSL,Body:TStrings;
   Msg:TMimeMess;
   i:integer;
   S:string;
   Part,Root:TMimePart;

{$R *.res}

//  Тело программы
begin
  Body:=TStringList.Create;
  Msg:=TMimeMess.Create;
  TempSL:=TStringList.Create;
  try
//  если без параметров отправляем тестовое письмо
//  если есть параметры то название администрации берется из
//  первой строки первого файла
    IdealCharsets:=[CP1251];
    if Paramcount=0 then begin
      Subject:='Test';
      Body.Text:='Test message';
    end else begin
      if not FileExists(ParamStr(1)) then halt(10); // Не найден файл
      TempSL.Clear;
      TempSL.LoadFromFile(ParamStr(1));
      if TempSL.Count=0 then halt(20); // Пустой файл
      S:=TempSL[0];
      S:=AnsiToUtf8(S);
      if Pos('поселени',S)<=0 then halt(30); // Не верный файл
      Subject:=S;
      From:=S+' <'+From+'>';
      Body.Text:=S+#10+FormatDateTime('dd.mm.yyyy hh:nn:ss',Now);
    end;
    //  Создаем сообщение
    TempSL.Clear;
    Msg.Header.From:=From;
    Msg.Header.ToList.Add(Reciepient);
    Msg.Header.Subject:=Subject;
    Root:=Msg.AddPartMultipart('mixed',nil);
    Part:=Msg.AddPartTextEx(Body,Root,UTF_8,false,ME_BASE64);
    // Формируем список вложений и сразу проверяем наличие их
    for i:=1 to Paramcount do
      if FileExists(ParamStr(i)) then
        Msg.AddPartBinaryFromFile(ParamStr(i),Root);
    // отправить письмо
    Msg.EncodeMessage;
    try
      SendToRaw(From,Reciepient,Server,Msg.Lines,UserName,Password);
      writeln('OK');
    except
      on E:Exception do begin
        writeln('ERROR');
        writeln(E.Message);
      end;
    end;
  finally
    // очистить созданные объекты
    FreeAndNil(Body);
    FreeAndNil(Msg);
    FreeAndNil(TempSL);
  end;
end.

Все бы нечего, да вот с кодировками беда:

  1. заголовки, я пользуюсь Thunderbird и тема и отправитель абсолютно не читаемы.
  2. в тело письма тоже не прочитать.

При этом вложенные файлы смотрятся нормально (Thunderbird умеет показывать вложеные тексты, картинки и тд)

После несколько часов гугленья, тут в комментариях нашел это:

Тоже долго мучился этим вопросом и нашел таки решение.
Сам разработчик (Lukas Gebauer) написал, что Header.CharsetCode указывает на исходную кодировку, а не на ту в которую нужно перевести заголовки.
А выходная кодировка выбирается автоматически из IdealCharsets (synachar.pas):

IdealCharsets: TMimeSetChar =
[ISO_8859_1, ISO_8859_2, ISO_8859_3, ISO_8859_4, ISO_8859_5,
ISO_8859_6, ISO_8859_7, ISO_8859_8, ISO_8859_9, ISO_8859_10,
KOI8_R, KOI8_U
{$IFNDEF CIL} //error URW778 ??? :-O
, GB2312, EUC_KR, ISO_2022_JP, EUC_TW
{$ENDIF}
];

Мне нужно было использовать Windows-1251, так что я заменил код:

IdealCharsets: TMimeSetChar =
[CP1251
{$IFNDEF CIL} //error URW778 ??? :-O
, GB2312, EUC_KR, ISO_2022_JP, EUC_TW
{$ENDIF}
];

Аналогично думаю сработает и для UTF_8.

Так кастрировать исходники я не стал, мало ли что и когда понадобится потом ищи где что исправил учитывая, что IdealCharsets это переменная – значит ей можно присвоить то что нам надо. Что собственно и сделали сразу после создания объектов:

IdealCharsets:=[CP1251];

Еще маленькая деталь преобразование текста после загрузки в UTF8 – обязательно!

      TempSL.LoadFromFile(ParamStr(1));
      if TempSL.Count=0 then halt(20); // Пустой файл
      S:=TempSL[0];
      S:=AnsiToUtf8(S);

Lazarus по умолчанию использует “юникод-строки”, поэтому загрузка из файла ANSI-строки (Windows) необходимо преобразовать в UTF8, чтоб с нем можно было работать. Это по сути рождает вторую нашу проблему. Решение оказалось простым вместо Msg.AddPart использовать Msg.AddPartTextEx, которой нужно передать (const Value: TStrings; const PartParent: TMimePart; PartCharset: TMimeChar; Raw: Boolean; PartEncoding: TMimeEncoding).

  • Первый и второй параметры с ними все ясно
  • PartCharset – в какой кодировке находится текст
  • Raw – текст надо кодировать? как я понял false это когда вы сами его закодировали и в письмо его надо вставить “как есть”.
  • PartEncoding – как кодировать текст base64 вроде как стандарт …

В таком виде меня все устроило, и собственно задача решена.

Комментариев нет

No comments yet.

RSS feed for comments on this post.

Sorry, the comment form is closed at this time.