FLDelphi
FLDelphi

Reputation: 608

Indy SMTP and Exchange Server

I'm having a very strange problem sending emails via indy through two different mechanisms in my program. This problem is similar to [this one][1], but not exactly the same. I'm using Indy 10.5 with the latest OpenSSL libraries and Delphi XE3.

The first code snippet, which works, is a simple SMTP client I wrote. Here's an example of how I set it up. This isn't the exact code but it should give you an idea.

  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;

This generates a successful email with the log:

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)

Now the second method, which uses Report Builder built in Email components to send a Report via email (again, some code is snipped for brevity, the real clue is in the logs):

    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;

You can see that with the exception of using the Report Builder TppSMTPIndy, every setting is the same. Yet the email does not get sent, and the log looks like this:

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.

You can see that a QUIT is sent immediately after receiving HELLO. This is why my question is different than the link above. That person was at least receiving a STARTTLS request.

What could be causing Indy to send a QUIT immediately after receiving a HELLO? I am not getting any errors. It just silently fails and the program moves on.

Now here's a kicker hint. If I set the AuthType to satNone in the Report Builder example it works. While in my first example I can set the AuthType to satNone and satDefault and both work.

Any ideas?

Thanks a lot for your time.

Upvotes: 0

Views: 2695

Answers (1)

Remy Lebeau
Remy Lebeau

Reputation: 595732

What could be causing Indy to send a QUIT immediately after receiving a HELLO?

The only time QUIT is sent is when TIdSMTP.Disconnect() is called.

The only times that TIdSMTP itself calls Disconnect() are when:

  1. an exception is raised inside of TIdSMTP.Connect(), such as if the server's greeting has an error code, or there is a unexpected problem parsing the server's greeting or EHLO response.

  2. an exception is raised inside of TIdSMTP.StartTLS(), which is called by TIdSMTP.Authenticate() (which is called by TIdSMTP.Send() if not already called beforehand). However, since you have set UseTLS=utUseExplicitTLS and the server's EHLO response does not advertise support for STARTTLS, TIdSMTP.StartTLS() is effectively a no-op on this server.

I am not getting any errors. It just silently fails and the program moves on.

Unless Report Builder is catching exceptions internally and not passing them into your code, then the most likely scenario is that Report Builder itself is calling TIdSMTP.Disconnect() without calling TIdSMTP.Send() first. The RSET command shown in your log is sent by TIdSMTP.Send() at the beginning of the email (BTW, more recent versions of Indy no longer send RSET unless the email fails with an SMTP error code). Report Builder is likely just skipping Send(), and I can think of one possible reason why it might do that.

AuthType=satDefault uses the AUTH LOGIN command (which is not a secure command), but your server's EHLO response is not advertising support for LOGIN authentication (in fact, it is not advertising support for any authentications at all). As such, TIdSMTP.Authenticate() will skip authentication on this server by default and return False, and also set the TIdSMTP.DidAuthenticate property to False. Maybe Report Builder is calling TIdSMTP.Authenticate() directly and checking the result before calling TIdSMTP.Send(). Your non-ReportBuilder example does not do that validation. Setting AuthType=satNone will cause TIdSMTP.Authenticate() to return True and set the TIdSMTP.DidAuthenticate property to True.

If the server happens to support LOGIN authentication (some servers do support it without advertising it), you can set the TIdSMTP.ValidateAuthLoginCapability property to False (it is True by default) to make satDefault attempt authentication as long as the TIdSMTP.Username property has been assigned a non-blank string.

Upvotes: 3

Related Questions