Indy SMTP和Exchange Server

时间:2015-10-09 18:15:59

标签: delphi delphi-xe3 indy10 reportbuilder

我在我的程序中通过两种不同的机制通过indy发送电子邮件时遇到了一个非常奇怪的问题。这个问题类似于[这一个] [1],但不完全相同。我正在使用Indy 10.5和最新的OpenSSL库和Delphi XE3。

第一个有效的代码片段是我写的一个简单的SMTP客户端。这是我如何设置它的一个例子。这不是确切的代码,但它应该给你一个想法。

  FIndySMTP.Intercept := FIndyLogFile;
  FIndySMTP.IOHandler := FIndySSLHandler;

  FIndyMessage.From.Address := FEmailAddress;

  FIndySSLHandler.Destination := FSMTPAddress + ':' + IntToStr(FSMTPPort);
  FIndySSLHandler.Host := FSMTPAddress;
  FIndySSLHandler.Port := FSMTPPort;
  FIndySMTP.Host := FSMTPAddress;
  FIndySMTP.Port := FSMTPPort;
  FIndySMTP.Username := FAccountName;
  FIndySMTP.Password := FAccountPass;
  FIndySMTP.AuthType := satDefault;
  FIndySMTP.UseEhlo := True;
  FIndySMTP.UseTLS := utUseExplicitTLS

  FIndySMTP.Connect;
  try
    if FIndySMTP.Connected = True then
      FIndySMTP.Send(FIndyMessage);
  finally
    FIndySMTP.Disconnect;
  end;

这会生成包含日志的成功电子邮件:

Recv 10/9/2015 10:41:02 AM: 220 server.server1.local Microsoft ESMTP MAIL Service ready at Fri, 9 Oct 2015 13:41:01 -0400<EOL>
Sent 10/9/2015 10:41:02 AM: EHLO DEIMOS<EOL>
Recv 10/9/2015 10:41:02 AM: 250-server.server1.local Hello [68.14.239.173]<EOL>250-SIZE 36700160<EOL>250-PIPELINING<EOL>250-DSN<EOL>250-ENHANCEDSTATUSCODES<EOL>250-AUTH<EOL>250-8BITMIME<EOL>250-BINARYMIME<EOL>250 CHUNKING<EOL>
Sent 10/9/2015 10:41:02 AM: RSET<EOL>
Recv 10/9/2015 10:41:02 AM: 250 2.0.0 Resetting<EOL>
....(The rest snipped)

现在是第二种方法,它使用内置电子邮件组件的报表生成器通过电子邮件发送报表(同样,为简洁起见,一些代码被剪断,真正的线索在日志中):

    lIOHandler.Destination := Host + ':' + IntToStr(Port);
    lIOHandler.Host := Host;
    lIOHandler.Port := Port;

    TheReport.EmailSettings.HostAddress := Host;
    TheReport.EmailSettings.UserName := UserName;
    TheReport.EmailSettings.Password := Password;
    lEmail.SMTP.OnEmailError := FMain.EmailErrorEvent;
    TppSMTPIndy(lEmail.SMTP).IndySMTP.OnFailedRecipient := FMain.idSMTPFailedRecipient;
    TppSMTPIndy(lEmail.SMTP).IndySMTP.Intercept := FMain.IdLogFile1;
    TppSMTPIndy(lEmail.SMTP).IndySMTP.Port := Port;
    TppSMTPIndy(lEmail.SMTP).IndySMTP.IOHandler := lIOHandler;
    TppSMTPIndy(lEmail.SMTP).IndySMTP.UseTLS := utUseExplicitTLS; 
    TppSMTPIndy(lEmail.SMTP).IndySMTP.AuthType := satDefault;
    TppSMTPIndy(lEmail.SMTP).IndySMTP.UseEhlo := True;

    TheReport.SendMail;

您可以看到,除了使用Report Builder TppSMTPIndy之外,每个设置都是相同的。然而,电子邮件没有被发送,日志看起来像这样:

Stat Connected.
Recv 10/9/2015 10:44:31 AM: 220 server.server1.local Microsoft ESMTP MAIL Service ready at Fri, 9 Oct 2015 13:44:28 -0400<EOL>
Sent 10/9/2015 10:44:31 AM: EHLO DEIMOS<EOL>
Recv 10/9/2015 10:44:31 AM: 250-server.server1.local Hello [68.14.239.173]<EOL>250-SIZE 36700160<EOL>250-PIPELINING<EOL>250-DSN<EOL>250-ENHANCEDSTATUSCODES<EOL>250-AUTH<EOL>250-8BITMIME<EOL>250-BINARYMIME<EOL>250 CHUNKING<EOL>
Sent 10/9/2015 10:44:31 AM: QUIT<EOL>
Recv 10/9/2015 10:44:31 AM: 221 2.0.0 Service closing transmission channel<EOL>
Stat Disconnected.

您可以看到在收到HELLO后立即发送QUIT。这就是我的问题与上面的链接不同的原因。该人至少收到了STARTTLS请求。

什么可能导致Indy在收到HELLO后立即发送退出?我没有收到任何错误。它只是默默地失败,程序继续前进。

现在这里有一个踢球提示。如果我在报表生成器示例中将AuthType设置为satNone,则它可以正常工作。在我的第一个例子中,我可以将AuthType设置为satNone和satDefault,两者都有效。

有什么想法吗?

非常感谢你的时间。

1 个答案:

答案 0 :(得分:3)

  

什么可能导致Indy在收到HELLO后立即发送退出?

发送QUIT的唯一时间是TIdSMTP.Disconnect()被调用。

TIdSMTP本身调用Disconnect()的唯一时间是:

  1. TIdSMTP.Connect()内引发异常,例如,如果服务器的问候语有错误代码,或解析服务器的问候语或EHLO响应时出现意外问题。

  2. TIdSMTP.StartTLS()内部引发异常,由TIdSMTP.Authenticate()调用(如果事先未调用,则由TIdSMTP.Send()调用)。但是,由于您设置了UseTLS=utUseExplicitTLS且服务器的EHLO响应未宣传对STARTTLS的支持,因此TIdSMTP.StartTLS()实际上是此服务器上的无操作。

  3.   

    我没有收到任何错误。它只是默默地失败,程序继续前进。

    除非Report Builder在内部捕获异常而不将其传递到您的代码中,否则最有可能的情况是Report Builder本身正在调用TIdSMTP.Disconnect()而不先调用TIdSMTP.Send()。日志中显示的RSET命令由TIdSMTP.Send()在电子邮件开头发送(BTW,更新版本的Indy不再发送RSET,除非电子邮件失败,并显示SMTP错误代码)。报表生成器可能只是跳过Send(),我可以想到它可能会这样做的一个可能原因。

    AuthType=satDefault使用AUTH LOGIN命令(这不是安全命令),但您服务器的EHLO响应并未宣传对LOGIN身份验证的支持(事实上,它并非广告支持任何身份验证。因此,TIdSMTP.Authenticate()将默认跳过此服务器上的身份验证并返回False,并将TIdSMTP.DidAuthenticate属性设置为False。也许Report Builder直接调用TIdSMTP.Authenticate()并在调用TIdSMTP.Send()之前检查结果。您的非ReportBuilder示例不执行该验证。设置AuthType=satNone会导致TIdSMTP.Authenticate()返回True并将TIdSMTP.DidAuthenticate属性设置为True。

    如果服务器恰好支持LOGIN身份验证(某些服务器支持它而不通告它),您可以将TIdSMTP.ValidateAuthLoginCapability属性设置为False(它是True默认)只要satDefault属性被分配了非空字符串,就TIdSMTP.Username尝试进行身份验证。