多部分/混合消息中的边界字符串不正确



我使用Delphi 2006在一个仅供个人使用的应用程序中创建和发送带有附件的电子邮件消息。我使用TIdSMTP实例发送消息,然后还使用TIdIMAP4实例将副本放入特定的IMAP文件夹中。这在与BDS2006一起发布的Indy 10版本中工作得非常好,只有一个例外:电子邮件头中的时间总是不正确。

我决定修复这个问题,如果我可以,在寻找解决方案后,似乎最合理的是获得最新的Indy 10快照并使用它。

这将正确的时间放入邮件标题中,但是有一个新问题。现在,添加到IMAP文件夹的消息头中的边界字符串与电子邮件正文中的边界字符串不同!(请注意,通过SMTP发送的消息是正确的。)

这是旧版本Indy 10的相关头信息:

Content-Type: multipart/mixed; boundary="XNlC6OyS4QSiHY2U=_jsXyps6TR34pFNsh"
MIME-Version: 1.0
Date: Tue, 22 Nov 2011 09:11:58 +0000
A test of the BDS2006-bundled version of Indy
--XNlC6OyS4QSiHY2U=_jsXyps6TR34pFNsh
Content-Type: application/octet-stream;
        name="20111122.xls"

这是相同的头信息从Indy 10.5.8(快照10_4702,我昨天安装):

Content-Type: multipart/mixed; boundary="CDbEncbFvL7RZdOJ3DOIRoRBs=_nBsbZms"
MIME-Version: 1.0
Date: Tue, 22 Nov 2011 07:33:46 -0600
investigating more deeply, why does the boundary indicator change?
--h=_WzGWJei29fng7SqdPpDh1nkJxJZhiGc
Content-Type: application/octet-stream;
    name="20111122.xls"

时间戳是固定的,但是现在边界字符串不正确。结果是,在我的IMAP文件夹中添加的消息中似乎没有任何内容。

下面是创建电子邮件消息和附件、发送它并将副本放入IMAP文件夹的相关代码:

  FTheMsg.Date := Now;  // FTheMsg is a component dropped onto the form
  FTheMsg.Recipients.EMailAddresses := edMailTo.Text;
  FTheMsg.ClearBody;
  FTheMsg.Subject := 'Glucose Readings ' + FormatDateTime('mm/dd/yy', FStartDate) + ' - ' +
              FormatDateTime('mm/dd/yy', FEndDate);
  FTheMsg.Body.Assign(FMemo.Lines);
  // create the attachment
  TIdAttachmentFile.Create(FTheMsg.MessageParts, fileName);
  // send the mail!
  FSmtp.Host := FSMTPHost;  // values set up elsewhere, FSmtp is a component on the form
  FImap.Host := FIMAPHost;  // FImap is also a component on the form
  FSmtp.Connect;
  try
    FSmtp.Send(FTheMsg);
    FImap.Connect;
    try
      if (not FImap.AppendMsg('Sent Items', FTheMsg, FTheMsg.LastGeneratedHeaders, [mfSeen]))     then
        StatusBar1.Panels[4].Text := 'Failed append msg';
    finally
      FImap.Disconnect;
    end;
  finally
    FSmtp.Disconnect;
  end;

正如我所说,发送的电子邮件是好的,并正确显示。但是添加到我的IMAP文件夹(在上面的FImap.AppendMsg()中)的那个是不正确的。我试图跟踪代码,看看哪里可能出了问题,但坦率地说,我对Indy和各种电子邮件协议/rfc不够熟悉,无法确定哪里出了问题。我能说的是,旧版本在将消息附加到文件夹之前将其保存到临时文件中,而新版本则将其保存到内存流中。显然,有些东西是不同的,但我现在太无知了,无法确定是什么。

是否有一个简单的方法来纠正旧版本的时间戳问题?如果是这样,那对我来说就很好了,因为其他一切似乎都是正确的。如果没有,我还需要做些什么来修复这里显示的错误边界字符串的问题?

(由于这是一个严格为我自己使用的应用程序,如果有必要,我可以忍受错误的日期,但不能忍受我的"已发送项目"文件夹中的"空"副本。)

如果需要更多的信息,我很乐意提供。

[编辑:我确实在我的代码中加入了一些拼凑,使用旧版本的Indy。我只是在发送消息之前将消息的日期/时间设置为UTC/GMT时间,这样至少可以让消息在接收方端包含正确的时间。我不是特别关心这个修复,但它确实奏效了[/p>

当附件存在时,不要使用TIdMessage.Body属性来保存文本。将文本放入TIdText对象中。更好的是,使用TIdMessageBuilder...类,如TIdMessageBuilderPlain,为您准备TIdMessage主体。

试试这个:

FTheMsg.Clear; 
FTheMsg.Date := Now;  // FTheMsg is a component dropped onto the form 
FTheMsg.Recipients.EMailAddresses := edMailTo.Text; 
FTheMsg.Subject := 'Glucose Readings ' + FormatDateTime('mm/dd/yy', FStartDate) + ' - ' + FormatDateTime('mm/dd/yy', FEndDate); 
FTheMsg.ContentType := 'multipart/mixed'; 
TIdText.Create(FTheMsg.MessageParts, FMemo.Lines).ContentType := 'text/plain';
TIdAttachmentFile.Create(FTheMsg.MessageParts, fileName); 
FSmtp.Connect; 
try 
  FSmtp.Send(FTheMsg); 
  FImap.Connect; 
  try 
    if (not FImap.AppendMsg('Sent Items', FTheMsg, nil, [mfSeen])) then 
      StatusBar1.Panels[4].Text := 'Failed append msg'; 
  finally 
    FImap.Disconnect; 
  end; 
finally 
  FSmtp.Disconnect; 
end; 

或:

FTheMsg.Clear; 
FTheMsg.Date := Now;  // FTheMsg is a component dropped onto the form 
FTheMsg.Recipients.EMailAddresses := edMailTo.Text; 
FTheMsg.Subject := 'Glucose Readings ' + FormatDateTime('mm/dd/yy', FStartDate) + ' - ' + FormatDateTime('mm/dd/yy', FEndDate); 
with TIdMessageBuilderPlain.Create do
try
  PlainText.Assign(FMemo.Lines);
  Attachments.Add(fileName); 
  FillMessage(FTheMsg);
finally
  Free;
end;
FSmtp.Connect; 
try 
  FSmtp.Send(FTheMsg); 
  FImap.Connect; 
  try 
    if (not FImap.AppendMsg('Sent Items', FTheMsg, nil, [mfSeen])) then 
      StatusBar1.Panels[4].Text := 'Failed append msg'; 
  finally 
    FImap.Disconnect; 
  end; 
finally 
  FSmtp.Disconnect; 
end; 

现在,话虽如此,它很可能仍然无法正常工作。TIdIMAP4.AppendMsg()在内部调用TIdMessage.SaveToStream(),它重新生成新的电子邮件内容(从而改变正文中使用的边界)。无论你是传入已经存在的TIdMessage.LastGeneratedHeaders,还是让TIdIMAP4.AppendMsg()抓取当前的TIdMessage.Headers,它们都将与TIdMessage.SaveToStream()生成的新边界不同步。

要确保SMTP和IMAP4同步,它们需要接收相同的数据。尝试手动调用TIdMessage.SaveToStream(),首先将TIdMessage.NoEncode属性设置为False,然后将TIdMessage.NoDecode属性设置为True,然后调用TIdMessage.LoadFromStream()将保存的数据按原样重新加载到TIdMessage.HeadersTIdMessage.Body属性中,然后调用TIdSMTP.Send()TIdIMAP4.AppendMsg(),将TIdMessage.NoEncode属性设置为True,以便TIdMessage.HeadersTIdMessage.Body按原样发送。

我知道,这违背了TIdIMAP4.AppendMsg()注释/文档所说的去做。AppendMsg()目前根本不考虑MIME,所以它不能确保报头和正文中的MIME边界相互匹配。我会试着去修复它。对于Indy 11, Indy的整个MIME处理系统将被重新设计,因此我将确保可以保留边界,和/或指定自定义边界,因此AppendMsg()可以更好地匹配正文边界和标题边界。

IMAP4通常是一个非常棘手的协议。

最新更新