DelphiAndroid12 DelphiAndroid12 - 5 months ago 58
Android Question

Socket Error 110: Connection Timed Out - Android Delphi SMTP Gmail

I am writing a program for Android using Delphi to send an email with data. As is, my app is having connectivity issues.

I have


  • set the host to smtp.gmail.com,

  • put in my username and password for my gmail account,

  • put valid information in for the address and body fields of a TIdMessage,

  • made and added an attachment file,

  • set the SSL Options for a TIdSSLIOHandlerSocketOpenSSL according to the online examples, and

  • added all of the SASL Mechanisms Indy offers.



I am using port 587 and have connected to TLS explicitly.

type
TForm1 = class(TForm)
SendBtn: TButton;
IdSMTP1: TIdSMTP;
IdMessage1: TIdMessage;
IdSASLAnonymous1: TIdSASLAnonymous;
IdSASLCRAMMD51: TIdSASLCRAMMD5;
IdSASLCRAMSHA11: TIdSASLCRAMSHA1;
IdSASLDigest1: TIdSASLDigest;
IdSASLExternal1: TIdSASLExternal;
IdSASLLogin1: TIdSASLLogin;
IdSASLOTP1: TIdSASLOTP;
IdSASLOTP2: TIdSASLOTP;
IdSASLPlain1: TIdSASLPlain;
IdSASLSKey1: TIdSASLSKey;
IdSSLIOHandlerSocketOpenSSL1: TIdSSLIOHandlerSocketOpenSSL;

procedure SendBtnClick(Sender: TObject);
procedure FormCreate(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;
Attachment : TIdAttachmentFile;

implementation

{$R *.fmx}

procedure TForm1.FormCreate(Sender: TObject);
begin
IdSMTP1.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(idSMTP1);
IdSMTP1.UseTLS := utUseExplicitTLS;
IdSMTP1.AuthType := satSASL;
IdSMTP1.SASLMechanisms.Add.SASL := IdSASLCRAMSHA11;
IdSMTP1.SASLMechanisms.Add.SASL := IdSASLAnonymous1;
IdSMTP1.SASLMechanisms.Add.SASL := IdSASLCRAMMD51;
IdSMTP1.SASLMechanisms.Add.SASL := IdSASLDigest1;
IdSMTP1.SASLMechanisms.Add.SASL := IdSASLExternal1;
IdSMTP1.SASLMechanisms.Add.SASL := IdSASLLogin1;
IdSMTP1.SASLMechanisms.Add.SASL := IdSASLOTP1;
IdSMTP1.SASLMechanisms.Add.SASL := IdSASLOTP2;
IdSMTP1.SASLMechanisms.Add.SASL := IdSASLPlain1;
IdSMTP1.SASLMechanisms.Add.SASL := IdSASLSKey1;
IdSSLIOHandlerSocketOpenSSL1.SSLOptions.Method := sslvTLSv1;
IdSSLIOHandlerSocketOpenSSL1.SSLOptions.Mode := sslmUnassigned;
IdSSLIOHandlerSocketOpenSSL1.SSLOptions.VerifyMode := [];
IdSSLIOHandlerSocketOpenSSL1.SSLOptions.VerifyDepth := 0;
end;

procedure TForm1.SendBtnClick(Sender: TObject);
begin
if IdSMTP1.Connected=True then IdSMTP1.Disconnect;
IdMessage1.From.Address := 'myemail@gmail.com';
IdMessage1.Recipients.EMailAddresses := 'other@gmail.com';
IdMessage1.BccList.Add.Address := '';
IdMessage1.CCList.Add.Address := '';
IdMessage1.Priority := mpHigh;
IdMessage1.Sender.Address := 'myemail@gmail.com';
IdMessage1.Subject := 'Test Data'; //Add Date/time
IdMessage1.Body.Add('Hello!');
Attachment := TIdAttachmentFile.Create(IdMessage1.MessageParts, (GethomePath+'/Test.txt'));
IdSMTP1.Connect;
IdSMTP1.Authenticate;
IdSMTP1.Send(IdMessage1);
IdSMTP1.Disconnect;
end;


Fails at:

IdSMTP1.Connect;


Is there a know problem with connecting to Android in this manner?

Answer

On this line:

IdSMTP1.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(idSMTP1);

You are creating and assigning a new default-initialized SSLIOHandler, instead of using your existing SSLIOHandler that is on the Form.

The line should be this instead:

IdSMTP1.IOHandler := IdSSLIOHandlerSocketOpenSSL1;

In fact, everything you are doing in FormCreate() can (and should) be done in the Form Designer at design-time instead. You don't need to do it in code.

Also, I do not see a TIdUserPassProvider on your Form. Most of the SASL components you are using require one for their username/password. The TIdSMTP.UserName and TIdSMTP.Password properties are only used when AuthType is satDefault, not satSASL.

Other than that, I would suggest some additional changes in SendBtnClick():

  1. You should call IdMessage1.Clear() so you don't carry over existing data from previous sends. You are appending new values to IdMessage1.BccList, IdMessage1.CCList, IdMessage1.Body, and IdMessage1.MessageParts without clearing out the old values first.

  2. You do not need to call Authenticate(), Send() does that internally for you.

  3. Send() should be in a try/finally or try/except block so you can call Disconnect() even if Send() fails.

  4. you are not configuring TIdMessage correctly for mixing text and attachments together. You should add a TIdText to the MessageParts instead of using TIdMessage.Body (however, if TIdMessage.ConvertPreamble is true, it will convert TIdMessage.Body into a TIdText for you if there are any attachments present). But either way, you need to set the TIdMessage.ContentType property to 'multipart/mixed' so the receiver knows there are multiple pieces.

Try this instead:

type
  TForm1 = class(TForm)
    SendBtn: TButton;
    IdSMTP1: TIdSMTP;
    IdMessage1: TIdMessage;
    IdSASLAnonymous1: TIdSASLAnonymous;
    IdSASLCRAMMD51: TIdSASLCRAMMD5;
    IdSASLCRAMSHA11: TIdSASLCRAMSHA1;
    IdSASLDigest1: TIdSASLDigest;
    IdSASLExternal1: TIdSASLExternal;
    IdSASLLogin1: TIdSASLLogin;
    IdSASLOTP1: TIdSASLOTP;
    IdSASLOTP2: TIdSASLOTP;
    IdSASLPlain1: TIdSASLPlain;
    IdSASLSKey1: TIdSASLSKey;
    IdUserPassProvider1: TIdUserPassProvider;
    IdSSLIOHandlerSocketOpenSSL1: TIdSSLIOHandlerSocketOpenSSL;

    procedure SendBtnClick(Sender: TObject);
    procedure FormCreate(Sender: TObject);
  private
    { Private declarations }
  public
    { Public declarations }
  end;

var
  Form1: TForm1;

implementation

{$R *.fmx}

procedure TForm1.FormCreate(Sender: TObject);
begin
  // all of this code can be handled at design-time instead!

  IdSSLIOHandlerSocketOpenSSL1.SSLOptions.Method := sslvTLSv1;
  IdSSLIOHandlerSocketOpenSSL1.SSLOptions.Mode := sslmUnassigned;
  IdSSLIOHandlerSocketOpenSSL1.SSLOptions.VerifyMode := [];
  IdSSLIOHandlerSocketOpenSSL1.SSLOptions.VerifyDepth := 0;

  IdSMTP1.IOHandler := IdSSLIOHandlerSocketOpenSSL1;
  IdSMTP1.UseTLS := utUseExplicitTLS;
  IdSMTP1.AuthType := satSASL;

  IdSASLCRAMSHA11.UserPassProvider := IdUserPassProvider1;
  IdSASLCRAMMD51.UserPassProvider := IdUserPassProvider1;
  IdSASLDigest1.UserPassProvider := IdUserPassProvider1;
  IdSASLLogin1.UserPassProvider := IdUserPassProvider1;
  IdSASLOTP1.UserPassProvider := IdUserPassProvider1;
  IdSASLOTP2.UserPassProvider := IdUserPassProvider1;
  IdSASLPlain1.UserPassProvider := IdUserPassProvider1;
  IdSASLSKey1.UserPassProvider := IdUserPassProvider1;

  IdSMTP1.SASLMechanisms.Add.SASL := IdSASLCRAMSHA11;
  IdSMTP1.SASLMechanisms.Add.SASL := IdSASLAnonymous1;
  IdSMTP1.SASLMechanisms.Add.SASL := IdSASLCRAMMD51;
  IdSMTP1.SASLMechanisms.Add.SASL := IdSASLDigest1;
  IdSMTP1.SASLMechanisms.Add.SASL := IdSASLExternal1;
  IdSMTP1.SASLMechanisms.Add.SASL := IdSASLLogin1;
  IdSMTP1.SASLMechanisms.Add.SASL := IdSASLOTP1;
  IdSMTP1.SASLMechanisms.Add.SASL := IdSASLOTP2;
  IdSMTP1.SASLMechanisms.Add.SASL := IdSASLPlain1;
  IdSMTP1.SASLMechanisms.Add.SASL := IdSASLSKey1;

  // end design-time capable hookups

  IdSMTP1.Host := ...;
  IdSMTP1.Port := ...;
  IdUserPassProvider1.UserName := ...;
  IdUserPassProvider1.Password := ...;
end;

procedure TForm1.SendBtnClick(Sender: TObject);
var
  Text: TIdText;
  Attachment : TIdAttachmentFile;
begin
  if IdSMTP1.Connected then IdSMTP1.Disconnect;

  IdMessage1.Clear;
  IdMessage1.From.Address := 'myemail@gmail.com';
  IdMessage1.Recipients.EMailAddresses := 'other@gmail.com';
  IdMessage1.Priority := mpHigh;
  IdMessage1.Sender.Address := 'myemail@gmail.com';
  IdMessage1.Subject := 'Test Data';   //Add Date/time
  //IdMessage1.Body.Add('Hello!');
  Text := TIdText.Create(IdMessage1.MessageParts);
  Text.ContentType := 'text/plain';
  Text.Body.Add('Hello!');
  Attachment := TIdAttachmentFile.Create(IdMessage1.MessageParts, (GethomePath+'/Test.txt'));
  IdMessage1.ContextType := 'multipart/mixed';             

  IdSMTP1.Connect;
  try
    IdSMTP1.Send(IdMessage1);
  finally
    IdSMTP1.Disconnect;
  end;
end;