[IOS] Send email with Indy

This is the forum for miscellaneous technical/programming questions.

Moderator: 2ffat

[IOS] Send email with Indy

Postby Lena » Tue Oct 16, 2018 3:19 am

Hello.
I use this steps for send emails on android:
//Download
libcrypto.so and libssl.so

//Components
IdSSLIOHandlerSocketOpenSSL
Destination=smtp.yandex.ru:465
Host=smtp.yandex.ru
Port=465

IdSMTP
Password=***
Username=***
Host=smtp.yandex.ru
Port=465

IdMessage
CharSet=windows-1251
ContentType=text/plain
Encoding=meDefault
Subject=Hello

Code: Select all
#include <IdSSLOpenSSLHeaders.hpp>
#include "IdAttachmentFile.hpp"
#include <IdMessageBuilder.hpp>

__fastcall TForm1::TForm1(TComponent* Owner)
   : TForm(Owner)
{
  IdOpenSSLSetLibPath(System::Ioutils::TPath::GetDocumentsPath());
}

//*********************************************
//*********************************************

void __fastcall TFormServis::IdMessage1InitializeISO(System::WideChar &VHeaderEncoding,
        UnicodeString &VCharSet)
{
   VCharSet = L"UTF-8";
   VHeaderEncoding = L'B';
}


void __fastcall TFormServis::Button3Click(TObject *Sender)
{
          try
      {
        IdMessage1->Clear();
        IdMessage1->Subject = L"Hello";

        IdMessage1->From->Address = L"lenailicheva@yandex.ru";
        IdMessage1->From->Domain = L"yandex.ru";
        IdMessage1->From->Text = L"lenailicheva@yandex.ru";
        IdMessage1->From->User = L"lenailicheva";

        TIdEMailAddressItem *Item = IdMessage1->Recipients->Add();
        Item->User = Trim(Edit3->Text);
        Item->Domain = L"yandex.ru";

                  //if need  attachment
        std::unique_ptr<TIdMessageBuilderPlain> builder(new TIdMessageBuilderPlain);

        builder->PlainText->Text = L"From: " + Edit2->Text;
        builder->PlainTextCharSet = L"utf-8";
        builder->PlainTextContentTransfer = L"binary";

        String att = System::Ioutils::TPath::Combine(System::Ioutils::TPath::GetDocumentsPath(), L"pic.jpg");
        builder->Attachments->Add(att);

        builder->FillMessage(IdMessage1);

        IdSMTP1->Connect();
        try {
          IdSMTP1->Send(IdMessage1);
        }
        __finally {
          IdSMTP1->Disconnect();
        }

        ShowMessage(L"Email successfully sent.");
      }
             catch (const EIdOSSLCouldNotLoadSSLLibrary &)
        {
         String ErMessage = WhichFailedToLoad();
         String path = System::Ioutils::TPath::Combine(System::Ioutils::TPath::GetSharedDownloadsPath(), L"error.ini");
         if(FileExists(path))
          {
                          std::unique_ptr<TIniFile> FileINI(new TIniFile(path));
           FileINI->WriteString(L"ERROR",L"error",ErMessage);
                          ShowMessage(L"Write in error.ini");
                         }
                else
                      {
                       ShowMessage(L"No file error.ini");
                                }

        }
                   catch (const Exception &E)
         {
                    String MES =  E.Message;
          ShowMessage(MES);
         }
  }


What steps are needed to send emails to the platform IOS with Indy?
Thanks.
Lena
BCBJ Master
BCBJ Master
 
Posts: 596
Joined: Sun Feb 06, 2011 1:28 pm

Re: [IOS] Send email with Indy

Postby rlebeau » Tue Oct 16, 2018 11:41 am

Lena wrote:I use this steps for send emails on android
...
What steps are needed to send emails to the platform IOS with Indy?


The same setup, just with some slight changes:

  • Use the OpenSSL static-link .a files instead of the dynamic-link .so files.
  • add an #include "IdSSLOpenSSLHeaders_Static.hpp" statement, and wrap it inside an #ifdef so it is included only for iOS.
  • wrap the call to IdOpenSSLSetLibPath() in an #ifdef to skip it on iOS.
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1560
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: [IOS] Send email with Indy

Postby Lena » Fri Oct 19, 2018 12:44 am

Thank you!

Use the OpenSSL static-link .a files instead of the dynamic-link .so files.


I downloaded OpenSSLStaticLibs.7z from https://indy.fulgan.com/SSL/
In IDE Project Manager add libcrypto.a and libssl.a Are my steps right?

add an #include "IdSSLOpenSSLHeaders_Static.hpp" statement, and wrap it inside an #ifdef so it is included only for iOS.
wrap the call to IdOpenSSLSetLibPath() in an #ifdef to skip it on iOS.


Please show how to add correctly #ifdef for IOS.
Lena
BCBJ Master
BCBJ Master
 
Posts: 596
Joined: Sun Feb 06, 2011 1:28 pm

Re: [IOS] Send email with Indy

Postby rlebeau » Fri Oct 19, 2018 10:26 am

Lena wrote:I downloaded OpenSSLStaticLibs.7z from https://indy.fulgan.com/SSL/
In IDE Project Manager add libcrypto.a and libssl.a Are my steps right?


Yes. Though, in a multi-platform project, it would make more sense to use a #pragma statement instead, wrapped in an #ifdef, to link to the .a files only when compiling for iOS.

Lena wrote:Please show how to add correctly #ifdef for IOS.


That information is in Embarcadero's documentation:

http://docwiki.embarcadero.com/RADStudio/en/BCCIOSARM

http://docwiki.embarcadero.com/RADStudio/en/BCCIOSARM64

http://docwiki.embarcadero.com/RADStudi ... r_iOS_Only
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1560
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: [IOS] Send email with Indy

Postby Lena » Wed Oct 24, 2018 6:40 am

I added your recommendation. Thank you.
I try to use Indy in only project IOS. I do not need an attachment to the email. I use a customer new email settings.
Code: Select all
bool __fastcall CheckInet2()
{
   bool result = false;
   THTTPClient *aHTTP = THTTPClient::Create();
   try {
      try {
         _di_IHTTPResponse aResp = aHTTP->Head("http://google.com");
         result = (aResp->StatusCode < 400);
      }
      catch (const System::Sysutils::Exception &E) {
         result = false;
      }
   }
   __finally {
      delete aHTTP;
   }
   return result;
}



//---------------------------------------------------------------------------
//new email from customer
void __fastcall TForm2::Button2Click(TObject *Sender)
{

   if(Edit1->Text == "")
   {
    ShowMessage(L"Please enter your name.");
     Edit1->SetFocus();
    return;
    }

   bool result = CheckInet2();

   if (result == false)
   {
    ShowMessage(L"Unable to sent. Check the availability of the Internet.");
    return;
   }


    try
      {
        IdMessage1->Clear();
      IdMessage1->Subject = L"Review";

      IdMessage1->From->Address = L"vlad44b@yandex.ru";//new
        IdMessage1->From->Domain = L"yandex.ru";
      IdMessage1->From->Text = L"vlad44b@yandex.ru";
      IdMessage1->From->User = L"vlad44b";

        TIdEMailAddressItem *Item = IdMessage1->Recipients->Add();
      Item->User = L"tracklogic";
      Item->Domain = L"yandex.ru";

      String body = L"Name: " + Trim(Edit1->Text) +
      L"Date: " + DateTimeToStr(Calendar1->Date) +
      L"Review: " + ComboBox1->Items->Strings[ComboBox1->ItemIndex];

      IdMessage1->Body->Text = body;

        IdSMTP1->Connect();
        try {
        Button2->Enabled = false;
        IdSMTP1->Send(IdMessage1);
        }
      __finally {
           Button2->Enabled = true;
          IdSMTP1->Disconnect();
        }

      ShowMessage(L"Thank You! Review successfully sent.");
        Close();
      }
     catch (const Exception &E)
         {
        String MES =  E.Message;
          ShowMessage(MES);
       }

}


EidSocetError #60 Connection time out.
(wait a long time...)

Please tell me where can be my mistake?
Attachments
tcp.jpg
tcp.jpg (116.95 KiB) Viewed 2915 times
Lena
BCBJ Master
BCBJ Master
 
Posts: 596
Joined: Sun Feb 06, 2011 1:28 pm

Re: [IOS] Send email with Indy

Postby rlebeau » Wed Oct 24, 2018 10:53 am

Lena wrote:EidSocetError #60 Connection time out. (wait a long time...)

Please tell me where can be my mistake?


You have the TIdSMTP::UseTLS property set to utNoTLSSupport, but you are connecting to port 465, which requires UseTLS be set to utUseImplicitTLS instead. As such, if the socket is successful in connecting to the server, TIdSMTP::Connect() will end up waiting for a server greeting that never arrives, because the server is waiting for your client to initiate an SSL/TLS handshake first.

When using utNoTLSSupport, you need to connect to port 25 instead.

It is also possible that the SMTP server in question might not even support port 465. Port 587 is preferred nowadays, in which case you would need to set UseTLS to utUseExplicitTLS instead.

Also, try setting TIdSMTP's ConnectTimeout and ReadTimeout properties to reasonable values. By default, Indy uses infinite timeouts, so operations will timeout whenever the OS decides to time them out, which may take a long time. I usually find that 5-30 seconds is reasonable.

On a side note, in your CheckInet2() function, there is no reason to check the HTTP StatusCode. The fact that you get any StatusCode at all, even values >= 400, means you have a valid Internet connection:

Code: Select all
bool __fastcall CheckInet2()
{
   bool result = false;
   try
   {
      THTTPClient *aHTTP = THTTPClient::Create();
      try
      {
         aHTTP->Head("http://google.com");
         result = true;
      }
      __finally {
         delete aHTTP;
      }
   }
   catch (const System::Sysutils::Exception &)
   {
   }
   return result;
}


But, you don't really need CheckInet2(), as you could just catch a connection error from TIdSMTP::Connect() instead.

Code: Select all
void __fastcall TForm2::Button2Click(TObject *Sender)
{
   String SenderName = Trim(Edit1->Text);
   if (SenderName == "")
   {
      ShowMessage(L"Please enter your name.");
      Edit1->SetFocus();
      return;
   }

   Button2->Enabled = false;
   try
   {
      try
      {
         IdMessage1->Clear();
         IdMessage1->Subject = L"Review";
         IdMessage1->From->Address = L"vlad44b@yandex.ru";
         IdMessage1->Recipients->Add()->Address = L"tracklogic@yandex.ru";

         IdMessage1->Body->Add(L"Name: " + SenderName);
         IdMessage1->Body->Add(L"Date: " + DateTimeToStr(Calendar1->Date));
         IdMessage1->Body->Add(L"Review: " + ComboBox1->Items->Strings[ComboBox1->ItemIndex]);

         try
         {
            IdSMTP1->Connect();
         }
         catch (const System::Sysutils::Exception &)
         {
            throw System::Sysutils::Exception(L"Unable to send. Check the availability of the Internet.");
         }

         try
         {
            IdSMTP1->Send(IdMessage1);
         }
         __finally
         {
            IdSMTP1->Disconnect();
         }

         ShowMessage(L"Thank You! Review successfully sent.");
         Close();
      }
      catch (const System::Sysutils::Exception &E)
      {
         ShowMessage(E.Message);
      }
   }
   __finally
   {
      Button2->Enabled = true;
   }
}
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1560
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: [IOS] Send email with Indy

Postby Lena » Mon Oct 29, 2018 3:37 am

Hello. I tried your recommendations but I haven’t succeeded yet. :(

It works:
Port 587 is preferred nowadays, in which case you would need to set UseTLS to utUseExplicitTLS instead.


But emails are sent not stable. Sometimes I get successful but often I see a debug windows:
Socket Error #53 Software caused connection abort
Connetion time out
Connetion time out
Unable to send. Check the availability of the Internet (<- my catch)
Project Tetra raised exception class EAccessViolation with message 'Access violation at address 0000000101183698, accessing address 0000000000000010'.


Code: Select all
__fastcall TForm2::TForm2(TComponent* Owner)
   : TForm(Owner)
{
 IdSMTP1->ConnectTimeout = 10000;
 IdSMTP1->ReadTimeout = 10000;
}

void __fastcall TForm2::Button2Click(TObject *Sender)
{
 String SenderName = Trim(Edit1->Text);
   if (SenderName == "")
   {
      ShowMessage(L"Please enter your name.");
      Edit1->SetFocus();
      return;
   }

   Button2->Enabled = false;
   Application->ProcessMessages();
   try
   {
      try
      {
         IdMessage1->Clear();
         IdMessage1->Subject = L"Review";
         IdMessage1->From->Address = L"vlad44b@yandex.ru";
         IdMessage1->Recipients->Add()->Address = L"tracklogic@yandex.ru";

         IdMessage1->Body->Add(L"Name: " + SenderName);
         IdMessage1->Body->Add(L"Date: " + DateTimeToStr(Calendar1->Date));
         IdMessage1->Body->Add(L"Review: " + ComboBox1->Items->Strings[ComboBox1->ItemIndex]);

         try
         {
            IdSMTP1->Connect();
         }
         catch (const System::Sysutils::Exception &)
         {
            throw System::Sysutils::Exception(L"Unable to send. Check the availability of the Internet.");
         }

         try
         {
            IdSMTP1->Send(IdMessage1);
         }
         __finally
         {
            IdSMTP1->Disconnect();
         }

         ShowMessage(L"Thank You! Review successfully sent.");
         Close();
      }
      catch (const System::Sysutils::Exception &E)
      {
         ShowMessage(E.Message);
      }
   }
   __finally
   {
      Button2->Enabled = true;
   }

}
Lena
BCBJ Master
BCBJ Master
 
Posts: 596
Joined: Sun Feb 06, 2011 1:28 pm

Re: [IOS] Send email with Indy

Postby rlebeau » Mon Oct 29, 2018 11:33 am

Lena wrote:It works:
Port 587 is preferred nowadays, in which case you would need to set UseTLS to utUseExplicitTLS instead.


But emails are sent not stable. Sometimes I get successful but often I see a debug windows:
Socket Error #53 Software caused connection abort
Connetion time out
Connetion time out



It is very important that you get the Port and UseTLS properties setup correctly, or you can cause deadlocks in the connection, triggering timeouts.

Are you ABSOLUTELY SURE your app is actually using the correct Port? Did you verify with a packet sniffer that the underlying TCP connection matches your Port property? Are you setting the Port property before or after setting the UseTLS property? Setting the UseTLS property MAY change the Port property.

Also, 10 second timeouts is a bit on the low side, especially for a mobile connection. I would suggest at least 30 seconds instead.

Lena wrote:Project Tetra raised exception class EAccessViolation with message 'Access violation at address 0000000101183698, accessing address 0000000000000010'.


Sounds like maybe a nil pointer is being accessed somewhere. Hopefully the iOS compiler does support raising an exception inside an exception handler.
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1560
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: [IOS] Send email with Indy

Postby Lena » Tue Oct 30, 2018 5:25 am

Are you ABSOLUTELY SURE your app is actually using the correct Port?


My steps:
I set in object inspector (see attachment). I was able to send emails today only twice. But almost very often I get debug window:
Socket Error #53 Software caused connection abort
In this debug window I press Сontinue and I got new debug window twice:
Connetion time out
I press again Сontinue and I got new debug window:
Unable to send. Check the availability of the Internet (<- my catch)
I press again Сontinue and got last debug window:
Project Tetra raised exception class EAccessViolation with message 'Access violation at address 0000000101183698, accessing address 0000000000000010'.

My code:
Code: Select all
//main form
void __fastcall TForm1::Button3Click(TObject *Sender)
{
 Form2->Show();
}


Code: Select all
//Form2 all code
#include <fmx.h>
#pragma hdrstop

#include<IdSSLOpenSSLHeaders_Static.hpp>
#include <System.Net.HttpClient.hpp>

#include "UnitFeedback.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.fmx"
TForm2 *Form2;
//---------------------------------------------------------------------------
__fastcall TForm2::TForm2(TComponent* Owner)
   : TForm(Owner)
{
 IdSMTP1->ConnectTimeout = 30000;
 IdSMTP1->ReadTimeout = 30000;
}
//---------------------------------------------------------------------------
void __fastcall TForm2::Button1Click(TObject *Sender)
{
  Close();
}
//---------------------------------------------------------------------------
void __fastcall TForm2::Button2Click(TObject *Sender)
{
 String SenderName = Trim(Edit1->Text);
   if (SenderName == "")
   {
      ShowMessage(L"Please enter your name.");
      Edit1->SetFocus();
      return;
   }

   Button2->Enabled = false;
   Application->ProcessMessages();
   try
   {
      try
      {
         IdMessage1->Clear();
         IdMessage1->Subject = L"Review";
         IdMessage1->From->Address = L"vlad44b@yandex.ru";//new
         IdMessage1->Recipients->Add()->Address = L"tracklogic@yandex.ru";

         IdMessage1->Body->Add(L"Name: " + SenderName);
         IdMessage1->Body->Add(L"Date: " + DateTimeToStr(Calendar1->Date));
         IdMessage1->Body->Add(L"Review: " + ComboBox1->Items->Strings[ComboBox1->ItemIndex]);

         try
         {
            IdSMTP1->Connect();
         }
         catch (const System::Sysutils::Exception &)
         {
            throw System::Sysutils::Exception(L"Unable to send. Check the availability of the Internet.");
         }

         try
         {
            IdSMTP1->Send(IdMessage1);
         }
         __finally
         {
            IdSMTP1->Disconnect();
         }

         ShowMessage(L"Thank You! Review successfully sent.");
         Close();
      }
      catch (const System::Sysutils::Exception &E)
      {
         ShowMessage(E.Message);
      }
   }
   __finally
   {
      Button2->Enabled = true;
   }

}
//---------------------------------------------------------------------------
void __fastcall TForm2::FormShow(TObject *Sender)
{
  Calendar1->DateTime = Now();
}
//---------------------------------------------------------------------------
void __fastcall TForm2::IdMessage1InitializeISO(System::WideChar &VHeaderEncoding,
          UnicodeString &VCharSet)
{
//if the English language I can comment   
   //VCharSet = L"UTF-8";
   //VHeaderEncoding = L'B';
}


I can not understand why I was able to send emails twice successfully. :o
P.S.
I found link (Code=53): https://github.com/AFNetworking/AFNetwo ... ssues/4279
But it not for Delphi/C++Builder and I have IPpone 6S and MasOS High Siera 10.13.5 IOS SDK 11.2 in IDE Tokio v.25.0.29899.2631 XCode 9.2(9C4Ob).
Attachments
setup.jpg
setup.jpg (104.66 KiB) Viewed 2668 times
Lena
BCBJ Master
BCBJ Master
 
Posts: 596
Joined: Sun Feb 06, 2011 1:28 pm

Re: [IOS] Send email with Indy

Postby rlebeau » Tue Oct 30, 2018 4:00 pm

Lena wrote:
Are you ABSOLUTELY SURE your app is actually using the correct Port?


My steps:
I set in object inspector (see attachment).


That is not what I asked. AT RUNTIME, did you verify the actual value of the TIdSMTP::Port property is correct, given the info I provided earlier, at the time that TIdSMTP::Connect() is being called?

Lena wrote:I was able to send emails today only twice. But almost very often I get debug window:


And what does the call stack look like what that 1st error happens?
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1560
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: [IOS] Send email with Indy

Postby Lena » Wed Oct 31, 2018 5:25 am

Did you verify with a packet sniffer that the underlying TCP connection matches your Port property?


Sorry, I have no experience using sniffer.

And what does the call stack look like what that 1st error happens?


I create images for all debug windows:
Socket Error #53 Software caused connection abort
Connetion time out
Connetion time out
Unable to send. Check the availability of the Internet (<- my catch)
Project Tetra raised exception class EAccessViolation with message 'Access violation at address 0000000101183698, accessing address 0000000000000010'.


Code: Select all
try
         {
         IdSMTP1->Port = 587;
         IdSMTP1->UseTLS = utUseExplicitTLS;
         IdSMTP1->Connect();//start debug windows/breakpoint here
         }
         catch (const System::Sysutils::Exception &)
         {
            throw System::Sysutils::Exception(L"Unable to send. Check the availability of the Internet.");
         }


Images for all debug windows:
http://sampsony.um.la/call.png
Lena
BCBJ Master
BCBJ Master
 
Posts: 596
Joined: Sun Feb 06, 2011 1:28 pm

Re: [IOS] Send email with Indy

Postby rlebeau » Wed Oct 31, 2018 12:33 pm

Lena wrote:Sorry, I have no experience using sniffer.


To be an effective network programmer, you need to learn how to use network sniffers.

Lena wrote:I create images for all debug windows


The underlying POSIX connect() function, which Indy calls in a worker thread, is not connecting to the target server in a timely manner before Indy's ConnectTimeout elapses. Why, I couldn't say.

Indy detects that the thread is still running when the timeout elapses, so it closes the socket to cancel the connection, causing the socket error #53, which Indy raises as an EIdSocketError to terminate the thread.

Indy waits for the thread to fully terminate, then raises an EIdConnectTimeout into your code, which you catch to throw the "Check the availability of the Internet" Exception, which you then catch and try to display with ShowMessage().

Up to this point, everything is working as expected (though you will have to figure out why your iOS device can't connect to the target server to begin with. Maybe the Host resolves to an unreachable IP, or maybe you just need to increase the ConnectTimeout. Who knows. This is where a network sniffer comes in handy).

My only concern is the EAccessViolation, which your screenshots suggest is coming from ShowMessage() itself, but you did not show the call stack for that exception. If ShowMessage() is accessing invalid memory, that is a bug that needs to be reported to Embarcadero.
Remy Lebeau (TeamB)
Lebeau Software
User avatar
rlebeau
BCBJ Author
BCBJ Author
 
Posts: 1560
Joined: Wed Jun 01, 2005 3:21 am
Location: California, USA

Re: [IOS] Send email with Indy

Postby Lena » Thu Nov 01, 2018 1:49 am

Thanks for the detailed explanations!
P.S.
I will temporarily return to sending through OpenURL+mailto.
Lena
BCBJ Master
BCBJ Master
 
Posts: 596
Joined: Sun Feb 06, 2011 1:28 pm


Return to Technical

Who is online

Users browsing this forum: Bing [Bot], Google [Bot] and 18 guests

cron