•  

ГлавнаяIndyЧастые вопросы по Indy → Кодировка Темы при работе с почтой в компоненте TIdMessage Indy

Создано: 01.02.2012 0:37:46 · Исправлено: 01.02.2012 0:37:46 · Прочтений: 9971

Добрый день.
Есть замечательный компонент Indy, с не менее замечатльными ошибками. Одна из них просто фатальна. В компоненте TIdMessage есть строковый параметр Subject. При отправке сообщения, если в нем есть не US-ASCII символы, происходит его кодирование.

И вот тут и получаем, что оно происходит да вот ВСЕГДА в одной кодировке (ISO-8859). Параметры ContentDisposition, ContentTransferEncoding и Charset не влияют на способ кодирования никак ;(

--------------
пример неправильного кодирования:
msg : TIdMessage;
msg.Charset := Windows-1251;
msg.Subject := (подтверждено) всем привет, новая тема ;)

получаем:
Subject: =?ISO-8859-1?Q?(=EF=EE=E4=F2=E2=E5=F0=E6=E4=E5=ED=EE)
=E0=E0=E0=E0=E0=E0?=
        =?ISO-8859-1?Q?=E0=E0=E0=E0=E0!?=

а должны получить:
Subject: =?Windows-1251?Q? .................

--------------

Собственно вопрос: если кто уже сталкивался с подобной проблемой или есть опыт исправления ошибок в исходниках Indy, подскажите плз. как это побороть. В баг-листах indyproject, в форумах borland эта тема не раскрыта.

Спасибо.
Тема старовата, но я всё равно здесь отвечу. Т.к. сам долго искал и экспериментировал. В итоге всё работает отлично. Отправка письма в юникоде с прикреплёнными разными типами файлов. Имена файлов, тема, отправитель и получатели нормально отображаются на русском языке. D2007. Indy 10.1.5
uses
...
IdMessage, IdBaseComponent,
  IdComponent, IdTCPConnection, IdTCPClient, IdExplicitTLSClientServerBase,
  IdMessageClient, IdSMTPBase, IdSMTP, IdIOHandler, IdIOHandlerSocket,
  IdIOHandlerStack, IdSSL, IdSSLOpenSSL, IdAttachmentFile, IdGlobal, IdCoderHeader;

procedure TfmEmail.SendMail(aRecordIndex:integer);
Var
att: TIdAttachmentFile;
q: TpFibQuery;
h: TTransfer;
  IdEMailAddressList2: TIdEMailAddressList;
  sFromAddress: string;
begin
IdSMTP1.DisConnect;
  IdMessage1.Clear;
  IdMessage1.From.Name    := ;
  IdMessage1.ContentType  := multipart/related; type=multipart/alternative;
  IdMessage1.CharSet      := UTF-8;
  IdSMTP1.ConnectTimeout  := 60*100;

  IdMessage1.From.Address := my_nick@rambler.ru;
  IdMessage1.From.Name    := AnsiToUtf8(Василий Пупкин);

  //кому
  IdEMailAddressList2 := TIdEMailAddressList.Create(IdMessage1);
  IdEMailAddressList2.EMailAddresses := AnsiToUtf8(Жора , Вера );
  IdMessage1.Recipients.EMailAddresses := EncodeAddress(IdEMailAddressList2, B,  h, UTF-8);

  IdMessage1.Subject    := AnsiToUtf8(Всем привет);
  IdMessage1.Body.Add(AnsiToUtf8(первая строка письма));
  IdMessage1.Body.Add(AnsiToUtf8(вторая строка письма));
  IdMessage1.IsEncoded  := true;
  IdMessage1.Encoding  := meDefault;


  IdSMTP1.Host      := smtp.rambler.ru;
  IdSMTP1.Username  := username;
  IdSMTP1.Password  := password;
  IdSMTP1.UseTLS    := utNoTLSSupport;//utUseImplicitTLS;
  IdSMTP1.Port      := 25;
  IdSMTP1.AuthType  := atDefault;

  IdSSLIOHandlerSocketOpenSSL1.Destination  := IdSMTP1.Host + : + IntToStr(IdSMTP1.Port);
  IdSSLIOHandlerSocketOpenSSL1.Host        := IdSMTP1.Host;
  IdSSLIOHandlerSocketOpenSSL1.MaxLineAction:= maException;
  IdSSLIOHandlerSocketOpenSSL1.Port        := IdSMTP1.Port;
  IdSSLIOHandlerSocketOpenSSL1.DefaultPort  := 0;
  IdSSLIOHandlerSocketOpenSSL1.SSLOptions.Mode        := sslmUnassigned;
  IdSSLIOHandlerSocketOpenSSL1.SSLOptions.VerifyMode  := [];
  IdSSLIOHandlerSocketOpenSSL1.SSLOptions.VerifyDepth := 0;

//прикрепляем файлы
  ATT := TIdAttachmentFile.Create(IdMessage1.MessageParts, D:\На отправку.xls);
  ATT := TIdAttachmentFile.Create(IdMessage1.MessageParts, D:\Grid1.txt);

  IdSMTP1.Connect;
  IdSMTP1.Send(IdMessage1);
  IdSMTP1.DisConnect;
  FreeAndNil(ATT);
  FreeAndNil(IdEMailAddressList2);
end;

К сожалению не могу понять, почему Thunderbird отображает текст письма русские имена файлов кракозябрами, хотя получателей и отправителя отображает корректно, на русском.
Благодаря функции EncodeSubj() удалось избавиться от абракадабры в теме письма. Но обнаружилась другая проблема. Если к письму приложить файлы с помощью TIdAttachment.Create(...), то русские буквы неправильно отображаются самом теле письма. Причем такой эффект наблюдается только в MS Outlook. The Bat и веб-интерефейсные почтовики прекрасно справляются с русскими буквами. Кто-нибудь сталкивался с подобной проблемой? Если да, то как ее решил?
Есть решение в использовании функции - EncodeSubj:
function EncodeSubj(instr:string):string;
var
  IdEncoderMIME: TIdEncoderMIME;
begin
  IdEncoderMIME := TIdEncoderMIME.Create;
  Result := =?+Windows-1251+?B?+IdEncoderMIME.Encode(instr)+?=;
  IdEncoderMIME.Free;
end;
Предварительно в разделе uses необходимо добавить модуль IdCoderMIME.
Существует ещё один ньюанс, название темы нельзя указывать непосредственно свойству IdMessage.Subject. Тему необходимо указывать через заголовки:
IdMessage.ExtraHeaders.Add(Subject: +EncodeSubj(Тема письма на русском или английском языке));

Я пробовал передавать в фунцию EncodeSubj текст как на английском, так и на русском языках. Текст в любом случае конвертируется и отображается верно.
Добрый день
Вопрос по перекодировке в русский при использовании компонента INDY
Сделал все как вы написали - 1.Копируем файлы idCoderHeader . pas , idCompilerDefines.inc, idGlobal. pas , idVers.inc в папку с проектом и т.д.....
Результат- без изменений- абракадабра
Может эти файлы нужно положить обраьно в папку C:\Program Files\Borland\Delphi7\Source\Indy ? или что-то еще в них исправить ? Может что-то нужно исправить в файлах .inc ?
Мне помогло твое решение)
В принципе, самое разумное решение было в самом начале - первые два поста. Не нужно править исходники библиотек Delphi/Indy и при этом всё работает.
И наконец самое интересное:
длина строки, установленная разработчиками Indy соответствует RFC 881 и 882.
Первый RFC от 1982г., с тех пор многое что изменилось и я думаю, что поменяв длину строки в исходниках мы особо ничего не нарушим... так что вперед надеюсь что пара часов, потраченные на выявления этой особенности работы с индями не пройдут даром и кому-то эта информация пригодится
Как оказалось base64 тут собсно мало причем, на длину поля Subject влияет константа, определенная в процедуре
procedure EncodeWord(P: Integer);
const
MaxEncLen = 75;<-вот тут и ограничение
если его установить например 2000, то длинные сабджекты пойдут на ура.
спасибо за подсказку - как только дойдут руки - попробую загнать в тему длинный текст...
Есть только одна поправочка.
idCoderHeader.pas в процедуру InitializeISO добавляем строки:
begin
  TransferHeader := bit8;    { header part conversion type }
//ВОТ ЭТО МЕНЯЕМ
  HeaderEncoding := Q;    { base64 / quoted-printable }    {Do not Localize}
//ВОТ ЭТО МЕНЯЕМ

Так как в случае base64 кодирования сабжекта длинные русские сабжекты не обрабатываются.
Здравствуйте.
Попробовал я вышенаписанные варианты и что-то ничего не получилось, увы...
Начал сам пробовать переделать и получилось.
D7 + Indy9
Делаем так:
1.Копируем файлы idCoderHeader.pas, idCompilerDefines.inc, idGlobal.pas, idVers.inc в папку с проектом
2.В idCoderHeader.pas в процедуру InitializeISO добавляем строки:
...
  case GetSystemLocale of
    csGB2312: CharSet := GB2312; {Do not Localize}
    csBig5: CharSet := Big5;    {Do not Localize}
    csIso2022jp:
      begin
        CharSet := ISO-2022-JP;  {Do not Localize}
        TransferHeader := iso2022jp { header needs conversion }
      end;
    csEUCKR: CharSet := EUC-KR;  {Do not Localize}
//******Начало Добавлений
    csRussian: CharSet := WINDOWS-1251;
//******Конец Добавлений
    else
      CharSet := ISO-8859-1;  {Do not Localize}
    HeaderEncoding := Q;    {Do not Localize}
  end;
...

3. В idGlobal.pas добавляем :
3.1.
было:
...
TIdCharSet = (csGB2312, csBig5, csIso2022jp, csEucKR, csIso88591,);
...
стало:
...
TIdCharSet = (csGB2312, csBig5, csIso2022jp, csEucKR, csIso88591,csRussian);
...
3.2.
...
function GetSystemLocale: TIdCharSet;
begin
{$IFDEF LINUX}
  Result := GSystemLocale;
{$ENDIF}
{$IFDEF MSWINDOWS}
  case SysLocale.PriLangID of
    LANG_CHINESE:
      if SysLocale.SubLangID = SUBLANG_CHINESE_SIMPLIFIED then
        Result := csGB2312
      else
        Result := csBig5;
    LANG_JAPANESE: Result := csIso2022jp;
    LANG_KOREAN: Result := csEucKR;
//Начало Добавлений
    LANG_RUSSIAN: Result :=csRussian;
//Конец Добавлений
    else
      Result := csIso88591;
  end;
{$ENDIF}
end;
...
4.После всех манипуляций сохраняем файлы и компилим проект, пробуем отправить - Subject русскими буквами и тело русскими буквами. Попробуйте, сообщите получилось ли... вдруг чего забыл написать...
Я решил эту проблему так (Delphi 7, Indy 10.0 52, старые Indy 9 убрать):
1.
Надо написать обработчик события OnInitializeISO:
procedure SMTP_Thread.MyInitializeISO(var VTransferHeader: TTransfer;
  var VHeaderEncoding: Char; var VCharSet: String);
begin
    VCharSet:=windows-1251;
    VTransferHeader := bit8;
    VHeaderEncoding := B;
end;
И подправить файлик IdMessageClient.
Чтобы не мучится и не перекомпилировать инсталляцию, просто скопировать его в папку с программой, вот он и будет использоваться.
Правим внутр. процедуру
  procedure WriteTextPart(ATextPart: TIdText);
  var
    LData: string;
    LDestStream: TIdStream;
    LStrStream: TIdStreamVCL;
    LBodyLine: String;
    i: Integer;
  begin
    if ATextPart.ContentType =  then begin
      ATextPart.ContentType := text/plain; {do not localize}
    end;
    if ATextPart.ContentTransfer =  then begin
      ATextPart.ContentTransfer := quoted-printable; {do not localize}
    end;
/////////////////////// А ЭТО ДОБАВИЛИ start
    if ATextPart.CharSet =  then begin
      ATextPart.CharSet := ISOCharSet; {do not localize}
    end;
/////////////////////// А ЭТО ДОБАВИЛИ end

    IOHandler.WriteLn(GenerateTextPartContentType(ATextPart.ContentType, ATextPart.CharSet));
........
В delphi 7 немного отличается:
файл IdHeaderCoder.pas -> IdCoderHeader.pas;
метод InitializeMime -> InitializeISO;
переменная MimeCharSet -> CharSet.
Если посмотреть исходный текст заголовка, в начале Subject указывается кодировка. Indy игнорирует русскоязычные.
Можно подкорретировать IdHeaderCoder.pas (лучше предварительно скопировав его в свой проект) метод InitializeMime.
Например добавить
MimeCharSet := Windows-1251;
HeaderEncoding := B;
вместо
MimeCharSet := ISO-8859-1;
HeaderEncoding := Q;
Мне это помогло для отправки сообщений, иначе все получали кривой Subject.
Правильная функция: ---------------------------------------------------
Function TDm_Pop3.EncodeSubj(instr:string):string;
begin
  result:==?+Windows-1251+?B?+IdEncoderMIME1.Encode(instr)+?=;
end;
Свойство charset- относится ко всему письму(body)т.е. к строке ContentType, если оно(письмо) не multipart.
решить проблему кодировки можно следуюшим образом:
------------------------------
[code[type TDm_Pop3 = class(TDataModule) .. IdEncoderMIME1: TIdEncoderMIME; ..... mess.Subject:=EncodeSubj(subj); ..... Function TDm_Pop3.EncodeSubj(instr:string):string; begin result:==?+Windows-1251+IdEncoderMIME1.Encode(instr)+?=; end;[/code] --------------------------------
т.е закодировать cтроку в base64;
subj должен быть в указаной (в EncodeSubj) кодировке.
есть такая проблема. :( решения не нашёл, но обход есть такой: если в тело запихать страничку в KOI8-R, то получатель прочитает. :)