在服务器上,我为每个收件人收到一个 OnRcpt(我使用一个 To、一个 CC 和一个 Bcc 进行测试)。
SMTP 协议不区分To、CC 和BCC 收件人。这是电子邮件格式通过其To: 和Cc: 标头(没有Bcc: 标头,尽管TIdMessage.SaveToFile() 创建一个因此TIdMessage.LoadFromFile() 可以重新加载它)的工件。当 SMTP 客户端向 SMTP 服务器发送电子邮件时,客户端会确定预期的收件人,并在单独的 RCPT TO 命令中指定每个收件人(这就是允许 BCC 工作的原因 - 没有 Bcc: 标头在电子邮件本身中,但为每个 Bcc 收件人提供了一个 RCPT TO 命令)。 RCPT TO 地址是在TIdSMTPServer.OnRcptTo 事件中指定的值,并收集在TIdSMTPServerContext.RCPTList 属性中。这些地址由 SMTP 客户端明确请求,并且是您应该将电子邮件发送到的唯一地址(不是电子邮件标头中指定的收件人)。
当我处理 OnMsgReceive 并将 AMsg 转换为 TStringStream(适当地以 To: 和 CC: 为前缀)时,我还在 AMsg 中看到了 To 和 CC 收件人,但我在那里看不到 BCC。
就 SMTP 服务器而言,实际的电子邮件本身只是任意数据。它旨在按原样交付给每个请求的收件人。这就是为什么电子邮件数据在 TIdSMTPServer.OnMsgReceive 事件中作为 TStream 提供,以及为什么 Indy 10 中的 TIdSMTPServer 不像在 Indy 9 和更早版本中那样提供基于 TIdMessage 的事件。
如果电子邮件采用RFC 822/2822/5322 格式(通常是这样),它将具有To: 和Cc: 标头。当接收/加载此类电子邮件到TIdMessage 时,这些标题分别用于填写TIdMessage.Recipients 和TIdMessage.CCList 属性。但是由于通常没有Bcc: 标头(除非电子邮件是从TIdMessage.SaveToFile() 创建的文件中加载的),所以TIdMessage.BCCList 不会被填写。
我如何知道哪些地址是 To、CC 和 BCC?
由于 SMTP 协议本身不区分不同的收件人类型,因此区分它们的唯一方法是解析电子邮件,查看其 To: 和 Cc: 标头。 TIdSMTPServerContext.RCPTList 属性中不在这两个标头之一中的任何地址都必须是Bcc 收件人。但是,不能保证TIdSMTPServerContext.RCPTList 属性中的其他地址将始终与To: 和Cc: 标头匹配(尽管它们通常会匹配),因为从技术上讲,SMTP 客户端可以发送它想要的任何原始数据,而 SMTP将按原样交付,而不关心它实际包含的内容。
这里有一些非常简单的代码。
请勿使用TIdMessage.LoadFromStream() 解析TIdSMTPServer.OnMsgReceive 事件提供的TStream 对象!它不会总是正常工作。技术原因已在 Embarcadero 和 AToZed 论坛中多次详细讨论,例如 this discussion。您暂时需要使用以下解决方法。该问题将在 Indy 11 中解决:
procedure TEmailMonitorMainForm.IdSMTPServerMsgReceive(ASender: TIdSMTPServerContext; AMsg: TStream; var VAction: TIdDataReply);
var
LMsg : TIdMessage;
LClient: TIdMessageClient;
LIO: TIdIOHandlerStreamMsg;
begin
LMsg := TIdMessage.Create(Nil);
try
//LMsg.LoadFromStream(AMsg);
LClient := TIdMessageClient.Create;
try
LIO := TIdIOHandlerStreamMsg.Create(LClient, AMsg);
LIO.FreeStreams := False;
LIO.EscapeLines := True;
LIO.Open;
LClient.IOHandler := LIO;
LClient.ProcessMessage(LMsg, False);
finally
LClient.Free;
end;
// now you can use LMsg as needed...
finally
LMsg.Free;
end;
end;