如何通过TIdIMAP4接收UTF-8编码的消息?

时间:2014-12-16 14:44:06

标签: delphi imap indy

我正在尝试使用Indy的TIdIMAP4组件接收电子邮件。

以下是我如何检索消息的示例代码:

procedure TEmailForm.GetMessage(ID: string);
var
  IMAP: TIdIMAP4;
  Msg: TIdMessage;
begin
  IMAP := TIdIMAP4.Create(Self);
  IMAP.Host := 'mailserver';
  IMAP.Username := 'username';
  IMAP.Password := 'password';
  IMAP.Connect(True);
  IMAP.SelectMailBox('INBOX');
  Msg := TIdMessage.Create(Self);
  IMAP.UIDRetrieve(ID, Msg);
  Subject.Caption := Msg.Subject;
  if (Msg.MessageParts.Count = 0) then
    MsgBody.Text := Msg.Body.Text
  else if (Msg.MessageParts[0] is TIdText) then
    MsgBody.Text := TIdText(Msg.MessageParts[0]).Body.Text;
  IMAP.Disconnect();
  IMAP.Free;
end;

其中SubjectTLabelMsgBodyTMemo

如果电子邮件是多部分消息,例如通过Gmail发送的消息,则TIdText(Msg.MessageParts[0]).Body.Text返回的正文已正确编码,并且正确显示德语元音等特殊字符。

虽然如果电子邮件是单件邮件,例如通过GMX发送的邮件,Msg.Body.Text返回的邮件正文中的所有特殊字符都会被问号(?)替换。

通过

检索邮件正文
IMAP.UIDRetrieveText(ID, MsgText);

导致像äöüÃñ²一样的Mojibake。

预期字符为äöüßñ²

FWIW通过Telnet获取邮件正文我得到了:

a06 UID FETCH 100 (BODY)
* 244 FETCH (BODY ("text" "html" ("charset" "UTF-8") NIL NIL "7BIT" 206 2) UID 1
00)
a06 OK FETCH completed.
a07 UID FETCH 100 BODY[TEXT]
* 244 FETCH (BODY[TEXT] {206}
<html><head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"></head><body>
<div style="font-family: Verdana;font-size: 12.0px;"><div>├ñ├Â├╝├ƒ
</div></div></body></html>
 UID 100 FLAGS (\Seen))
a07 OK FETCH completed.

那么检索这种UTF-8编码的单部分电子邮件的邮件正文的正确方法是什么?即如何正确解码它们?

2 个答案:

答案 0 :(得分:4)

TIdIMAP4.UIDRetrieve()使用电子邮件中指定的任何字符集。如果单部分电子邮件未正确解码,则可能不会开始指定正确的字符集。您必须查看原始电子邮件数据以确认。 MIME编码的电子邮件比纯文本电子邮件更容易指定字符集。非ASCII字符需要进行字符集处理,因此TIdMessage知道编码电子邮件的字符集非常重要。

对于UIDRetrieveText(),它不知道正在检索的文本/部件的字符集(即TODO项),因此它无法解码非ASCII字符。另一方面,UIDRetrieveText2()首先获取电子邮件的BODYSTRUCTURE,并找到正在检索的文本/部分的charset,以便在解码过程中使用该字符集。

答案 1 :(得分:0)

虽然UIDRetrieve()在修订版5022中按预期工作,但它无法在单部分消息案例中对UTF-8中的消息进行编码。

我最终使用UIDRetrieveText2(),它正确识别UTF-8编码,但我必须修复InternalRetrieveText()以避免无限循环。
相关代码是:

{Get the info we want out of LParts...}
repeat
  LThePart := LParts.Items[LTextPart];   {Part 1 is index 0}
  if LThePart.FSize = 0 then begin
    {Some emails have part 0 empty, they intend you to use part 1}
    if LTextPart = 0 then begin
      LTextPart := 1;
      Continue;
    end else begin
      Break;
    end;
  end;
until False;

如果LThePart.FSize不为null,则该循环永远不会中断。所以我改成了这个:

{Get the info we want out of LParts...}
LThePart := LParts.Items[LTextPart];   {Part 1 is index 0}
if LThePart.FSize = 0 then begin
  {Some emails have part 0 empty, they intend you to use part 1}
  LTextPart := 1;
  LThePart := LParts.Items[LTextPart];
end;