我正在尝试使用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;
其中Subject
是TLabel
而MsgBody
是TMemo
。
如果电子邮件是多部分消息,例如通过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编码的单部分电子邮件的邮件正文的正确方法是什么?即如何正确解码它们?
答案 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;