Lazarus

Free Pascal => Beginners => Topic started by: justnewbie on April 23, 2019, 01:42:52 pm

Title: Sending email with Indy
Post by: justnewbie on April 23, 2019, 01:42:52 pm
Hi,

I'm trying to send email by using Indy and the Gmail as smtp.
Here is my code:
Code: Pascal  [Select]
  1. unit Unit1;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls,
  9.   ExtCtrls, IdSMTP, IdMessage, IdAttachment, IdAttachmentFile;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.     Bevel1: TBevel;
  17.     Bevel2: TBevel;
  18.     Bevel3: TBevel;
  19.     Button1: TButton;
  20.     SendMailButton: TButton;
  21.     edHost: TEdit;
  22.     edFrom: TEdit;
  23.     edTo: TEdit;
  24.     edCc: TEdit;
  25.     edSubject: TEdit;
  26.     edAttachment: TEdit;
  27.     MailMessage: TIdMessage;
  28.     SMTP: TIdSMTP;
  29.     Label1: TLabel;
  30.     Label2: TLabel;
  31.     Label3: TLabel;
  32.     Label4: TLabel;
  33.     Label5: TLabel;
  34.     Label6: TLabel;
  35.     Label7: TLabel;
  36.     Label8: TLabel;
  37.     meStatus: TMemo;
  38.     meMessage: TMemo;
  39.     procedure SendMailButtonClick(Sender: TObject);
  40.   private
  41.  
  42.   public
  43.  
  44.   end;
  45.  
  46. var
  47.   Form1: TForm1;
  48.   Attachment: TIdAttachment;
  49.  
  50. implementation
  51.  
  52. {$R *.lfm}
  53.  
  54. { TForm1 }
  55.  
  56. procedure TForm1.SendMailButtonClick(Sender: TObject);
  57. begin
  58.   meStatus.Clear;
  59.   //setup SMTP
  60.   SMTP.Host := edHost.Text;  //  smtp.gmail.com is here
  61.   SMTP.Port := 465;
  62.   //setup mail message
  63.   MailMessage.From.Address := edFrom.Text;
  64.   MailMessage.Recipients.EMailAddresses := edTo.Text + ',' + edCc.Text;
  65.   MailMessage.Subject := edSubject.Text;
  66.   MailMessage.Body.Text := meMessage.Text;
  67.   if FileExists(edAttachment.Text) then Attachment := TIdAttachmentFile.Create(MailMessage.MessageParts, edAttachment.Text);
  68.   //send mail
  69.   try
  70.     try
  71.       SMTP.Connect;
  72.       SMTP.Send(MailMessage);
  73.     except on E:Exception do
  74.       meStatus.Lines.Insert(0, 'ERROR: ' + E.Message);
  75.     end;
  76.   finally
  77.     if SMTP.Connected then SMTP.Disconnect;
  78.   end;
  79. end;
  80.  
  81. end.

Email is not sent and I got this error:
ERROR: Connection Closed Gracefully.
How should I fix it?
Title: Re: Sending email with Indy
Post by: sstvmaster on April 23, 2019, 01:50:00 pm
Did you have enabled external smtp usage in your google account and your smtp client is accepted via Oauth2?
Title: Re: Sending email with Indy
Post by: justnewbie on April 23, 2019, 01:52:36 pm
Did you have enabled external smtp usage in your google account and your smtp client is accepted via Oauth2?
Sorry, I don't understand a single word from this.
How can I do these?
Title: Re: Sending email with Indy
Post by: rvk on April 23, 2019, 02:09:37 pm
Did you have enabled external smtp usage in your google account and your smtp client is accepted via Oauth2?
Sorry, I don't understand a single word from this.
How can I do these?
You are not allowed to just use smtp.gmail.com with your normal username and password.

You can either implement OAuth2, which I imagine is beyond your skillset, or you need to use a "App password" instead of your normal gmail password.

You can create a App password for smtp.gmail.com here:
https://myaccount.google.com/apppasswords?utm_source=google-account&utm_medium=web

Choose "App" E-mail and Device "Windows-computer".

(Note that this "App" password is for your gmail-account)
Title: Re: Sending email with Indy
Post by: justnewbie on April 23, 2019, 02:57:25 pm
Pretty frustrating that one can find sample codes like this https://support.embarcadero.com/article/36054 (https://support.embarcadero.com/article/36054) and this https://www.thoughtco.com/sending-email-messages-with-attachments-1058124 (https://www.thoughtco.com/sending-email-messages-with-attachments-1058124) and others, but nowhere mentioned that other "tricks" are also needed to get it working.  :(
And what about to use not use Gmail as SMTP, but my internet provider's SMTP?
Title: Re: Sending email with Indy
Post by: rvk on April 23, 2019, 04:25:14 pm
... and others, but nowhere mentioned that other "tricks" are also needed to get it working.  :(
That's because they used to work fine. But Google keeps restricting the use because of security.

Quote
And what about to use not use Gmail as SMTP, but my internet provider's SMTP?
You probably can use your providers smtp just fine (with or without SSL and authentication).
But that depends on your provider. Not all providers supply an smtp and if they do, it's only accessible connected to that network (which you probably are unless you have a laptop and go on a vacation with it).
Title: Re: Sending email with Indy
Post by: lucamar on April 23, 2019, 04:33:39 pm
[...] but nowhere mentioned that other "tricks" are also needed to get it working.  :(

Simplifying a lot: SMTP, as its name implies, is a very simple protocol created in the days when the internet was all fun and giggles. In consequence, it was also not quite secure enough so email providers have been implementing other measures (OAuth, etc.) to make at least the login to their servers secure before you can even start an SMTP conversation and from there come the "tricks" you talk about. They are really login "tricks", not SMTP tricks.

Quote
And what about to use not use Gmail as SMTP, but my internet provider's SMTP?

That, of course, depends on the provider and the security measures they have implemented, if any. Although nowadays most of them require some kind of secure login for which you'll have to use some "trick" or other :)
Title: Re: Sending email with Indy
Post by: justnewbie on April 23, 2019, 05:00:16 pm
Thank you guys for the thoughts, I will try it yet before giving up totally.  ::)
Title: Re: Sending email with Indy
Post by: justnewbie on April 23, 2019, 05:35:20 pm
I just tried with the PHPMailer and it worked perfectly if I gave these values into it:
$mail->isSMTP();                                     
$mail->Host = 'smtp.gmail.com';                  
$mail->SMTPAuth = true;                               
$mail->Username = 'MY_GOOGLE_EMAIL_ADDRESS HERE';             
$mail->Password = 'MY_GOOGLE_PW HERE';                 
$mail->SMTPSecure = 'tls';                           
$mail->Port = 587;

Is it possible to use Indy with these data?
In my original code only the HOST and PORT have been given. And what about the other data (tls, auth)?
Can I use them as well as with the PHPMailer within the Indy SMTP?

Update: when I try to change (in Object Inspector) the UseTLS input in Indy SMTP from 'utNoTLSSupport' to any other TLS option, I got this error:
SSL IOHandler is required for this setting
Title: Re: Sending email with Indy
Post by: rvk on April 23, 2019, 05:52:14 pm
I just tried with the PHPMailer and it worked perfectly if I gave these values into it:
In that case you still have your gmail account set to insecure.
That's ok, but if in the future you switch to 2-stage authentication it might stop working and you need to use the app-password method. Just a heads-up.

Quote
Is it possible to use Indy with these data?
Yes, it should be possible to use these settings.
(although not sure how in indy, I use Synapse)

You could try the example at https://forum.lazarus.freepascal.org/index.php/topic,18483.msg106986.html#msg106986
You do need to include the OpenSSL dll's.
Title: Re: Sending email with Indy
Post by: john horst on April 23, 2019, 06:21:00 pm
Your Pas code is using port 465 which is SSL, it might even be disabled by Google at this point.

Your PHP code is using port 587 which is TLS.

Code: Pascal  [Select]
  1. SMTP.Port := 465;

Code: PHP  [Select]
  1. $mail->SMTPSecure = 'tls';                          
  2. $mail->Port = 587;

I don't use Indy but you need to use TLSvX. Oauth2 is not a requirement. https://www.indyproject.org/2015/09/07/openssl-changes/
Title: Re: Sending email with Indy
Post by: justnewbie on April 23, 2019, 06:28:11 pm
Thank you guys for the inputs, now it is working!  :)

@rvk:
I added the TIdSSLIOHandlerSocketOpenSSL component to the form and set its port. After it I could change the UseTLS to 'utUseRequireTLS' in SMTP.

@john horst:
Indeed, I changed the port. Thanks!
Title: Re: Sending email with Indy
Post by: justnewbie on April 23, 2019, 06:54:20 pm
Guys, new challenge!  :)
I want to send more than 1 attached files and these files can be in different folders.
I'd like to have something similar thing than in Thunderbird (see picture).
I think a Memo or ListBox could be good for showing the selected files, but how can the program "remember" their path during send-process?
What would be the best approach?
Title: Re: Sending email with Indy
Post by: rvk on April 23, 2019, 08:04:58 pm
I think a Memo or ListBox could be good for showing the selected files, but how can the program "remember" their path during send-process?
Create a TStringList with all the files you want to attach (including their path). Fill the TListBox according to this TStringList (in correct order) with only the filename. When the user wants to delete number index 2 of the TListBox, delete the TStringList[2] too. The TStringList will be the basis for sending the attachments.
Title: Re: Sending email with Indy
Post by: justnewbie on April 23, 2019, 08:12:39 pm
I think a Memo or ListBox could be good for showing the selected files, but how can the program "remember" their path during send-process?
Create a TStringList with all the files you want to attach (including their path). Fill the TListBox according to this TStringList (in correct order) with only the filename. When the user wants to delete number index 2 of the TListBox, delete the TStringList[2] too. The TStringList will be the basis for sending the attachments.
Yes, my idea was similar, thank you for the confirmation.
Maybe later when I will be so brilliant in Lazarus that I could make a new component, I will make a TMixedPathFilesListBox.  :D
Title: Re: Sending email with Indy
Post by: Remy Lebeau on April 23, 2019, 09:17:41 pm
You are not allowed to just use smtp.gmail.com with your normal username and password.

You are if you enable "Unsecure apps" in your Gmail settings.  But, of course, that is not advisable.

You can either implement OAuth2, which I imagine is beyond your skillset, or you need to use a "App password" instead of your normal gmail password.

Indy does not support OAuth2 at this time, but generating an App Password works fine, I use app passwords all the time when I am testing Gmail SMTP with Indy.

I added the TIdSSLIOHandlerSocketOpenSSL component to the form and set its port. After it I could change the UseTLS to 'utUseRequireTLS' in SMTP.

The correct UseTLS value to use on the client side is utUseExplicitTLS on port 25 and 587, and utUseImplicitTLS on port 465.  Do not use utUseRequireTLS on the client side, it is meant to be used by servers instead.
Title: Re: Sending email with Indy
Post by: justnewbie on April 23, 2019, 09:38:27 pm
Thank you Remy for the instructions, now I changed it!
Title: Re: Sending email with Indy
Post by: justnewbie on April 24, 2019, 12:15:52 pm
New question guys. I'm using a Memo or SynEdit component to make the email message (html formatted).
If I send it, the recipient gets a plain text, but I want to send it as a html.
How can I achive this?

Update: meanwhile I found this https://www.indyproject.org/2005/08/17/html-messages/ (https://www.indyproject.org/2005/08/17/html-messages/), not seems too simple.
Title: Re: Sending email with Indy
Post by: justnewbie on April 24, 2019, 03:42:40 pm
Based on the link above: there is no TIdText component in my Indy10.  :o
Title: Re: Sending email with Indy
Post by: justnewbie on April 24, 2019, 04:17:43 pm
Wow! I got html-email! :)
Following this: https://stackoverflow.com/questions/14323852/tidtext-missing-from-indy-10 (https://stackoverflow.com/questions/14323852/tidtext-missing-from-indy-10)
Solution: I added these units to the Uses: IdMessage, IdAttachment, IdText, IdAttachmentFile, IdMessageParts
Title: Re: Sending email with Indy
Post by: justnewbie on April 24, 2019, 05:05:29 pm
Remy, I hope you are somewhere near ... would have some questions!
1./ Currently the "From" contains my email address, but my name would be better there. Is it possible?
2./ As I can see, the ipHtmlPanel cannot handle the formatting properly
(for example, this <p style="background-color: #627E20; font-weight: bold; color: #BAF2F2" >This is an other text.</p> does proper font formatting, but not for the background color).
Did I miss something or is it the normal behaviour?
3./ The sending function works great with TLS and port 587 (Gmail). What should I change in the settings to get SSL and port 465 to work? I mean, there are many options in TIdSSLIOHandlerSocketOpenSSL for SSLOptions, bit confusing.
4./ Currently I'm using this method for attachment:
Code: Pascal  [Select]
  1. Attachment := TIdAttachmentFile.Create(MailMessage.MessageParts, attFile);
It only send 1 attached file. How can I send more than 1 attached files?

4. is solved with a loop:
Code: Pascal  [Select]
  1. for i := 0 to slAttachedFiles.Count - 1 do
  2.   begin
  3.     if FileExists(slAttachedFiles[i]) then TIdAttachmentFile.Create(MailMessage.MessageParts, slAttachedFiles[i]);
  4.   end;

Title: Re: Sending email with Indy
Post by: john horst on April 24, 2019, 05:46:12 pm
Are you getting no Background color or is it not behaving as expected?

You can't enable SSLv1,2,3, it's an insecure protocol and shouldn't be used. I believe that is why he said use "utUseImplicitTLS on port 465', make sure it uses TLS on both ports.
Title: Re: Sending email with Indy
Post by: justnewbie on April 24, 2019, 06:00:05 pm
1./ Are you getting no Background color or is it not behaving as expected?

2./ You can't enable SSLv1,2,3, it's an insecure protocol and shouldn't be used. I believe that is why he said use "utUseImplicitTLS on port 465', make sure it uses TLS on both ports.
1./ I got no background-color.
2./ With Gmail it is OK (TLS, 587), but I want to use an other SMTP which uses SSL and port 465. When I tried it, did not send the email.
Title: Re: Sending email with Indy
Post by: rvk on April 24, 2019, 07:24:25 pm
2./ As I can see, the ipHtmlPanel cannot handle the formatting properly
(for example, this <p style="background-color: #627E20; font-weight: bold; color: #BAF2F2" >This is an other text.</p> does proper font formatting, but not for the background color).
This is not normal HTML formatting for an E-mail client. You need to lookup how you need to encode correct HTML. If you send an E-mail to yourself and look at the source you'll see why.

Your example should probably be something like this (the equal sign needs to be encoded with =3D):
<p style=3D"background-color: #627E20; font-weight: bold; color: #BAF2F2">This is an other text.</p>

Not sure if Indy does this automatically or has some functions available for it.
Title: Re: Sending email with Indy
Post by: justnewbie on April 24, 2019, 07:49:37 pm
This is not normal HTML formatting for an E-mail client. You need to lookup how you need to encode correct HTML. If you send an E-mail to yourself and look at the source you'll see why.

Your example should probably be something like this (the equal sign needs to be encoded with =3D):
<p style=3D"background-color: #627E20; font-weight: bold; color: #BAF2F2">This is an other text.</p>

Not sure if Indy does this automatically or has some functions available for it.
Uh, thanks. It's new to me, I thought it is the usual html/css thing.
Title: Re: Sending email with Indy
Post by: rvk on April 24, 2019, 08:01:24 pm
Uh, thanks. It's new to me, I thought it is the usual html/css thing.
In essence it is. But using mail the html gets send as printed-quotable. And there the = sign is reserved for encoding. So = needs to be (re)encoded as =3D. $3D is ascii 61 which is the =.
Title: Re: Sending email with Indy
Post by: john horst on April 24, 2019, 08:02:09 pm
You might need to patch to get background-color to work properly. I tried myself even encoding the = to %3D, as I expected it wouldn't work. I don't think it has to be encoded like a URL, plus it Bold etc work with out encoding so it must be reading it.

I found this. https://forum.lazarus.freepascal.org/index.php?topic=43116.5;wap2
Title: Re: Sending email with Indy
Post by: rvk on April 24, 2019, 08:14:23 pm
You might need to patch to get background-color to work properly. I tried myself even encoding the = to %3D, as I expected it wouldn't work.
No, don't encode it as %3D. Encode it as =3D.
Those are two different encodings.
One is url encoding and the other is printed-quotable.
Title: Re: Sending email with Indy
Post by: justnewbie on April 24, 2019, 08:46:43 pm
No, don't encode it as %3D. Encode it as =3D.
Those are two different encodings.
One is url encoding and the other is printed-quotable.
Yes, you are right, this is the proper way: style=3D"background-color: ...
Title: Re: Sending email with Indy
Post by: justnewbie on April 24, 2019, 08:52:41 pm
So, these two questions remained yet:
1./ Currently the "From" contains my email address, but my name would be better there next to the sender email address.
(Example: now in Thunderbird 'from column' it looks like this: myaddress@gmail.com <myaddress@gmail.com>. I need MyName <myaddress@gmail.com>). Is it possible?
2./ The sending function works great with TLS and port 587 (Gmail). What should I change in the settings to get SSL and port 465 to work? I mean, there are many options in TIdSSLIOHandlerSocketOpenSSL for SSLOptions, bit confusing.
Title: Re: Sending email with Indy
Post by: john horst on April 24, 2019, 09:33:06 pm
1)  MSG.From.Name := 'Your Name';

For my own curiosity :

Code: HTML5  [Select]
  1. <p style=3D"background-color:#627E20; font-weight: bold; color: #BAF2F2" >This is an other text.</p>

Is correct? Not working for me.
Title: Re: Sending email with Indy
Post by: justnewbie on April 24, 2019, 09:45:02 pm
1)  MSG.From.Name := 'Your Name';

For my own curiosity :

Code: HTML5  [Select]
  1. <p style=3D"background-color:#627E20; font-weight: bold; color: #BAF2F2" >This is an other text.</p>

Is correct? Not working for me.

Name: perfect!  :)
Html-format: yes, strange. In idHtmlPanel it doesn't work, but I looked at the email source in TB and there was this 3D addition.
Seems that idHtmlPanel cannot handle it.
Title: Re: Sending email with Indy
Post by: Remy Lebeau on April 24, 2019, 09:53:18 pm
Update: meanwhile I found this https://www.indyproject.org/2005/08/17/html-messages/ (https://www.indyproject.org/2005/08/17/html-messages/), not seems too simple.

Also see https://www.indyproject.org/2008/01/16/new-html-message-builder-class/ (https://www.indyproject.org/2008/01/16/new-html-message-builder-class/), which helps simplify things a bit.
Title: Re: Sending email with Indy
Post by: rvk on April 24, 2019, 09:55:46 pm
Html-format: yes, strange. In idHtmlPanel it doesn't work, but I looked at the email source in TB and there was this 3D addition. Seems that idHtmlPanel cannot handle it.
That's because idHtmlPanel just shows pure HTML. It doesn't take the printed-quotable into account. So before you show it in an HTML panel you need to convert the printed-quotable back to pure HTML.

PS. If you got incoming mail, the HTML could also be base64 encoded. Then you would also first need to decode it back to HTML before you could show it. So the encoding could be done several ways depending on the preferred choice of the sender-client. And don't get me started about Outlook/Exchange which might even give you a winmail.dat with the text inside encoded (which not many other E-mail clients besides Outlook itself can decipher).
Title: Re: Sending email with Indy
Post by: Remy Lebeau on April 24, 2019, 10:04:35 pm
1./ Currently the "From" contains my email address, but my name would be better there. Is it possible?

Yes, it is possible:

Code: Pascal  [Select]
  1. IdMessage.From.Name := 'My Name';
  2. IdMessage.From.Address := 'me@domain.com';

3./ The sending function works great with TLS and port 587 (Gmail). What should I change in the settings to get SSL and port 465 to work? I mean, there are many options in TIdSSLIOHandlerSocketOpenSSL for SSLOptions, bit confusing.

Implicit vs Explicit is a completely different thing than using SSL v2.0/3.0 vs TLS 1.0/1.1/1.2.

Port 465 uses Implicit SSL.  That means the client must initiate an SSL/TLS handshake (using SSL 2.0/3.0 or TLS 1.0/1.1/1.2 as needed) immediately upon establishing the socket connection, before then performing any STMP related I/O.  Reading the server's greeting, sending commands, reading responses, they will ALL be encrypted.  This scenario is handled by setting the TIdSMTP.Port to 465 and the TIdSMTP.UseTLS to utUseImplicitTLS, and configuring the SSLIOHandler as desired.

Port 587 uses Explicit TLS.  That means the client must NOT initiate an SSL/TLS handshake until it has first issued an unencrypted STARTTLS command and received a success reply back.  Prior to receiving a STARTTLS success response, everything will NOT be encrypted.  This scenario is handled by setting the TIdSMTP.Port to 587 and the TIdSMTP.UseTLS to utUseExplicitTLS, and configuring the SSLIOHandler as desired.

4./ Currently I'm using this method for attachment:
Code: Pascal  [Select]
  1. Attachment := TIdAttachmentFile.Create(MailMessage.MessageParts, attFile);
It only send 1 attached file. How can I send more than 1 attached files?

Simply create additional TIdAttachmentFile objects as needed, one per file.  The TIdMessage.MessageParts property is a collection, it can hold multiple TIdMessageParts-derived objects.

4. is solved with a loop:
Code: Pascal  [Select]
  1. for i := 0 to slAttachedFiles.Count - 1 do
  2.   begin
  3.     if FileExists(slAttachedFiles[i]) then TIdAttachmentFile.Create(MailMessage.MessageParts, slAttachedFiles[i]);
  4.   end;

Yes, that would be the way to go.
Title: Re: Sending email with Indy
Post by: Remy Lebeau on April 24, 2019, 10:06:20 pm
Your example should probably be something like this (the equal sign needs to be encoded with =3D):
<p style=3D"background-color: #627E20; font-weight: bold; color: #BAF2F2">This is an other text.</p>

Not sure if Indy does this automatically or has some functions available for it.

Yes, by default Indy encodes plain-text using the "quoted-printable" algorithm (which is what encoded '=' as '=3D'), so DO NOT encode the HTML text manually.
Title: Re: Sending email with Indy
Post by: rvk on April 24, 2019, 10:09:05 pm
Yes, by default Indy encodes plain-text using the "quoted-printable" algorithm (which is what encoded '=' as '=3D'), so DO NOT encode the HTML text manually.
Then why didn't it work for justnewbie? And why did it work when doing it manually?

@justnewbie, could you show how you added the HTML-part. Maybe that's done incorrectly.
Title: Re: Sending email with Indy
Post by: Remy Lebeau on April 24, 2019, 10:10:05 pm
That's because idHtmlPanel just shows pure HTML. It doesn't take the printed-quotable into account.

No, but Indy does.

So before you show it in an HTML panel you need to convert the printed-quotable back to pure HTML.

That should not be necessary, as Indy encodes with "quoted-printable" when sending an email, and decodes "quoted-printable" when receiving an email.  That way, your code can focus on just the text in its "natural" encoding (ie, in this case, the plain HTML as-is).

PS. If you got incoming mail, the HTML could also be base64 encoded.

When Indy also decodes for you.

And don't get me started about Outlook/Exchange which might even give you a winmail.dat with the text inside encoded (which not many other E-mail clients besides Outlook itself can decipher).

Indy has a standalone TIdCoderTNEF class for parsing winmail.dat attachments.
Title: Re: Sending email with Indy
Post by: Remy Lebeau on April 24, 2019, 10:12:53 pm
Then why didn't it work for justnewbie? And why did it work when doing it manually?

I can't answer that without seeing how exactly @justnewbie setup the TIdMessage, and what the raw email data actually looks like on the wire.
Title: Re: Sending email with Indy
Post by: justnewbie on April 24, 2019, 10:23:45 pm
@rvk:
(@justnewbie, could you show how you added the HTML-part. Maybe that's done incorrectly.)
Based on this http://wiki.freepascal.org/TIpHtmlPanel (http://wiki.freepascal.org/TIpHtmlPanel), I made an on-the-fly stuff by using this html code:
Quote
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
</head>
<body>

<h1>This is a Heading</h1>
<p style="background-color: #A7E615;">This is a text.</p>
<p style="background-color: #627E20; font-weight: bold; color: #BAF2F2" >This is an other text.</p>

</body>
</html>
When I put the 3D, nothing changed in the idHtmlPanel.

Guys, thank you for all inputs!  :) I am out for today, tomorrow will check all of these.
Title: Re: Sending email with Indy
Post by: Remy Lebeau on April 25, 2019, 05:07:45 am
Based on this http://wiki.freepascal.org/TIpHtmlPanel (http://wiki.freepascal.org/TIpHtmlPanel), I made an on-the-fly stuff by using this html code:
Quote
<!DOCTYPE html>
<html>
<head>
<title>Page Title</title>
</head>
<body>

<h1>This is a Heading</h1>
<p style="background-color: #A7E615;">This is a text.</p>
<p style="background-color: #627E20; font-weight: bold; color: #BAF2F2" >This is an other text.</p>

</body>
</html>
When I put the 3D, nothing changed in the idHtmlPanel.

You are not supposed to add the '=3D' manually at all.  It should be just '=' in the actual HTML, and then Indy will encode it as '=3D' when generating the email for transmission.
Title: Re: Sending email with Indy
Post by: justnewbie on April 25, 2019, 04:51:46 pm
Mystical problem here:
I use this in my code:
Code: Pascal  [Select]
  1. SMTP.UseTLS := utUseExplicitTLS;
But, I got this error: Error: Identifier not found "utUseExplicitTLS"
(TIdSMTP (SMTP) component dropped onto the form.)

Update: solved, uhhh. I had to add the IdExplicitTLSClientServerBase to the Uses list.
Title: Re: Sending email with Indy
Post by: justnewbie on April 25, 2019, 11:54:46 pm
Remy, I sent a message, please look at that. Thanks
Title: Re: Sending email with Indy
Post by: justnewbie on April 26, 2019, 12:55:02 pm
I experienced a few hours difference between the real sending time (my local time) and the email's date/time.
How can I solve this?

Update: I found this http://www.delphipages.com/forum/showthread.php?t=182465 (http://www.delphipages.com/forum/showthread.php?t=182465), but it is wrong.
None of the Time(), Date() or Now() gave proper value.
Is it a bug?
Title: Re: Sending email with Indy
Post by: Remy Lebeau on April 26, 2019, 08:42:00 pm
I experienced a few hours difference between the real sending time (my local time) and the email's date/time.
How can I solve this?

Update: I found this http://www.delphipages.com/forum/showthread.php?t=182465 (http://www.delphipages.com/forum/showthread.php?t=182465), but it is wrong.
None of the Time(), Date() or Now() gave proper value.
Is it a bug?

The Date(), Time(), and Now() functions return the calling machine's local clock time.  The TIdMessage.Date property expects local time (also note that the TIdMessage.UseNowForDate property is True by default, unless you set the TIdMessage.Date explicitly).

TIdMessage uses Indy's LocalDateTimeToGMT() function to get the string for the email's 'Date' header.  Internally, LocalDateTimeToGMT() relies on Indy's OffsetFromUTC() function to calculate the local machine's offset from UTC time.  Did you check to make sure that OffsetFromUTC() is returning the correct offset for your local machine?

Or, are you saying that the Time(), Date() and/or Now() functions themselves are returning wrong values that do not match your local machine's actual clock?
Title: Re: Sending email with Indy
Post by: justnewbie on April 26, 2019, 09:49:16 pm
Something extremely strange happened with my Lazarus.
I got errors when I type the code. For example: I type that Label1 and when I type the dot (ie: Label1.) the following error occurs when the code hint bubble should be appeared, see the picture.
Also, I dropped any component on the form, it doesn't appear in the Form's class definition and I got "Identifier not found" error, although the component is visible on the form.
?!?

Furthermore, DateTimeToStr(OffsetFromUTC()) gives 30-12-99.
Title: Re: Sending email with Indy
Post by: lucamar on April 27, 2019, 12:40:20 am
Furthermore, DateTimeToStr(OffsetFromUTC()) gives 30-12-99.

That's quite logical: the offset is a time delta, without date portion, so the date is set to zero, which means ... you guessed it: 30-12-99. See for yourself what DateTimeToStr(0.5) gives you.
Title: Re: Sending email with Indy
Post by: Remy Lebeau on April 27, 2019, 10:16:42 pm
Furthermore, DateTimeToStr(OffsetFromUTC()) gives 30-12-99.

FYI, Indy has a UTCOffsetToStr() function, which would make more sense to use than DateTimeToStr().

But, in any case, OffsetFromUTC() returns a TDateTime which is used to adjust other TDateTime values from local to UTC and vice versa.  TDateTime represents dates as an integral number of days relative to December 30 1899.  Which means that OffsetFromUTC() is returning 0, either because:


What platform is your app compiled for when OffsetFromUTC() is returning 0?
Title: Re: Sending email with Indy
Post by: lucamar on April 28, 2019, 01:54:18 am
TDateTime represents dates as an integral number of days relative to December 30 1899.  Which means that OffsetFromUTC() is returning 0, either because:
[... etc ...]

IIRC, TDateTime is a floating point value: the date is represented in the integral part (as days after 1899-12-30) and the time in the decimal part as a fraction of the day.

If OffsetFromUTC() returns a TDateTime or TTime value, then the "date" part will be 0, i.e. 1899-12-30 no matter what is in the "time" part.

Unless the offset is greater than 24 hours, of course :D
Title: Re: Sending email with Indy
Post by: justnewbie on April 28, 2019, 12:29:50 pm
My machine runs a Linux system and all Time(), Date() and Now() works well.
So, how can I get the proper "email-time"? If I send the email to myself for example at 14:32 (my local time), my email client shows 15:32. What should I set to get the proper 14:32?

And, as I mentioned above, something strange happens in my editor.
There is a TIdSMTP component on the form named SMTP. When I type SMTP in the editor, and after it I type a dot, instead of code completition bubble I got this idGlobal error, see picture. Why? 
Title: Re: Sending email with Indy
Post by: Cyrax on April 28, 2019, 09:54:34 pm
My machine runs a Linux system and all Time(), Date() and Now() works well.
So, how can I get the proper "email-time"? If I send the email to myself for example at 14:32 (my local time), my email client shows 15:32. What should I set to get the proper 14:32?

And, as I mentioned above, something strange happens in my editor.
There is a TIdSMTP component on the form named SMTP. When I type SMTP in the editor, and after it I type a dot, instead of code completition bubble I got this idGlobal error, see picture. Why?

Somehow Lazarus CodeTools barfs on that line. Solution is to modify that line so it looks like (or close) this:

Code: Pascal  [Select]
  1. Index: IdGlobal.pas
  2. ===================================================================
  3. --- IdGlobal.pas        (revision 5496)
  4. +++ IdGlobal.pas        (working copy)
  5. @@ -1905,7 +1905,8 @@
  6.    {$IFDEF UNIX}
  7.  
  8.    // For linux the user needs to set this variable to be accurate where used (mail, etc)
  9. -  GOffsetFromUTC: TDateTime = 0{$IFDEF HAS_DEPRECATED}{$IFDEF USE_SEMICOLON_BEFORE_DEPRECATED};{$ENDIF} deprecated{$ENDIF};
  10. +(*  GOffsetFromUTC: TDateTime = 0{$IFDEF HAS_DEPRECATED}{$IFDEF USE_SEMICOLON_BEFORE_DEPRECATED};{$ENDIF} deprecated{$ENDIF}; *)
  11. +  GOffsetFromUTC: TDateTime = 0;(*{$IFDEF HAS_DEPRECATED}{$IFDEF USE_SEMICOLON_BEFORE_DEPRECATED};{$ENDIF} deprecated{$ENDIF}; *)
  12.  
  13.      {$IFDEF DARWIN}
  14.    GMachTimeBaseInfo: TTimebaseInfoData;
  15.  
Title: Re: Sending email with Indy
Post by: justnewbie on April 29, 2019, 03:04:13 pm
@Cyrax:
I changed it and now I got no that error. Thanks!

So, this question remained:
How can I get the proper "email-time"? If I send the email to myself for example at 14:32 (my local time), my email client shows 15:32. What should I set to get the proper 14:32?

The UTCOffsetToStr(OffsetFromUTC()) gives +0000, which is incorrect.
I'm using Linux and its time display is good.
Title: Re: Sending email with Indy
Post by: lucamar on April 29, 2019, 06:24:15 pm
The UTCOffsetToStr(OffsetFromUTC()) gives +0000, which is incorrect.
I'm using Linux and its time display is good.

Strictly as a workaround, you can use this:
Code: Pascal  [Select]
  1. if OffsetFromUTC = 0 {and you know that's not true} then
  2.   FakedTime := LocalToUniversal(Now); {uses dateutils}

Indy will use OffsetFromUTC, so if you see it fails you pass the UTC hour instead of your local one. With your example hours above, LocalToUniversal()  should convert 14:32 to 13:32, which is set in the email as if it were the UTC hour, so your email client see this, reverts the conversion and gets 14:32 again.

Note that this just a workaround while you investigate why OffsetFromUTC is failing. If it's v.g. a system misconfiguration, nothing will help you because any function will return the same bad offset.
Title: Re: Sending email with Indy
Post by: justnewbie on April 29, 2019, 07:01:03 pm
Thank you Lucamar, I'm trying to translate it.

Well, I found that Remy already wrote this: https://github.com/IndySockets/Indy/issues/245 (https://github.com/IndySockets/Indy/issues/245)
But, to be honest, I dunno what should I do based on this info.
GetLocalTimeOffset() gives the proper offset (in minutes). How can I pass this value to the Indy?
Title: Re: Sending email with Indy
Post by: lucamar on April 29, 2019, 07:34:04 pm
Thank you Lucamar, I'm trying to translate it.

Well, I found that Remy already wrote this: https://github.com/IndySockets/Indy/issues/245 (https://github.com/IndySockets/Indy/issues/245)
But, to be honest, I dunno what should I do based on this info.
GetLocalTimeOffset() gives the proper offset (in minutes). How can I pass this value to the Indy?

Look into the Indy source to see where OfssetFromUTC is set (if it's a var) or implemented (if it's a function) and change whatever is there to use GetLocalTimeOffset when it's compiling under FPC.

Or use the workaround above in your program until Remy fixes it in Indy. (This is what I would do, btw).
Title: Re: Sending email with Indy
Post by: justnewbie on April 29, 2019, 07:42:31 pm
Yes, it will be a workaround.
Title: Re: Sending email with Indy
Post by: Remy Lebeau on April 30, 2019, 10:11:18 pm
IIRC, TDateTime is a floating point value: the date is represented in the integral part (as days after 1899-12-30) and the time in the decimal part as a fraction of the day.

That is correct.

If OffsetFromUTC() returns a TDateTime or TTime value, then the "date" part will be 0, i.e. 1899-12-30 no matter what is in the "time" part.

That is a good point.  And yes, it does returns a TDateTime with the date portion set to 0 and the time portion set to the offset.

Unless the offset is greater than 24 hours, of course :D

Which it never should be, since the lowest/highest UTC offsets defined are only ±12 hours from UTC.

So, how can I get the proper "email-time"? If I send the email to myself for example at 14:32 (my local time), my email client shows 15:32. What should I set to get the proper 14:32?

Emails use UTC timestamps, which means Indy is calculating your machine's UTC as ±1 hour from UTC.  What is your machines ACTUAL UTC offset? 

Somehow Lazarus CodeTools barfs on that line. Solution is to modify that line so it looks like (or close) this:

Code: Pascal  [Select]
  1. Index: IdGlobal.pas
  2. ===================================================================
  3. --- IdGlobal.pas        (revision 5496)
  4. +++ IdGlobal.pas        (working copy)
  5. @@ -1905,7 +1905,8 @@
  6.    {$IFDEF UNIX}
  7.  
  8.    // For linux the user needs to set this variable to be accurate where used (mail, etc)
  9. -  GOffsetFromUTC: TDateTime = 0{$IFDEF HAS_DEPRECATED}{$IFDEF USE_SEMICOLON_BEFORE_DEPRECATED};{$ENDIF} deprecated{$ENDIF};
  10. +(*  GOffsetFromUTC: TDateTime = 0{$IFDEF HAS_DEPRECATED}{$IFDEF USE_SEMICOLON_BEFORE_DEPRECATED};{$ENDIF} deprecated{$ENDIF}; *)
  11. +  GOffsetFromUTC: TDateTime = 0;(*{$IFDEF HAS_DEPRECATED}{$IFDEF USE_SEMICOLON_BEFORE_DEPRECATED};{$ENDIF} deprecated{$ENDIF}; *)
  12.  
  13.      {$IFDEF DARWIN}
  14.    GMachTimeBaseInfo: TTimebaseInfoData;
  15.  

I'm not having that discussion again.  I've covered that issue MANY times before in this forum, search the archives.  The whole reason USE_SEMICOLON_BEFORE_DEPRECATED even exists is because of FPC.  But if you look in Indy's IdCompilerDefines.inc file, you will notice that USE_SEMICOLON_BEFORE_DEPRECATED is defined only for FPC 2.4.4 through 2.6.1.  From FPC 2.6.2 onwards, it is no longer defined, effectively making the declaration:

Code: Pascal  [Select]
  1. GOffsetFromUTC: TDateTime = 0 deprecated;

Which has already been proven to work fine in modern FPC versions.  If Lazarus's *editor* has a problem with it, that is for the Lazarus team to fix.  That is all I'm saying about that issue.

The UTCOffsetToStr(OffsetFromUTC()) gives +0000, which is incorrect.
I'm using Linux and its time display is good.

Can you please confirm whether both UNIX and either KYLIXCOMPAT or USE_BASEUNIX are defined while compiling IdGlobal.pas when building Indy?

If both are defined, OffsetFromUTC() calculates an offset based on the Posix gettimeofday() function.

If either are not defined, OffsetFromUTC() falls back to Indy's GOffsetFromUTC variable, which is 0 by default and must be set manually.  In which case, this is a bug in Indy, as those conditionals SHOULD be defined when compiling for Linux.

Well, I found that Remy already wrote this: https://github.com/IndySockets/Indy/issues/245 (https://github.com/IndySockets/Indy/issues/245)

Beat me to it, I was just about to point that out.  As you can see from this discussion (https://www.atozed.com/forums/showthread.php?tid=889) referenced by that ticket, I was made aware of the issue only a few months ago, but as you can read in that discussion, I have already considered using FPC's offset functions and am hesitant to use them because FPC's own documentation states that they return stale data after DST changes.  So I haven't applied any fixes to Indy yet.

But, to be honest, I dunno what should I do based on this info.
GetLocalTimeOffset() gives the proper offset (in minutes). How can I pass this value to the Indy?

If Indy is actually calling gettimeofday() (as it should be), and the function is just not returning timezone info back to Indy, then there is nothing you can do about that without altering Indy's source code.  Only when Indy has to fallback to its own GOffsetFromUTC variable can you assign your own timezone offset to Indy.
Title: Re: Sending email with Indy
Post by: justnewbie on April 30, 2019, 11:14:27 pm

Can you please confirm whether both UNIX and either KYLIXCOMPAT or USE_BASEUNIX are defined while compiling IdGlobal.pas when building Indy?

If both are defined, OffsetFromUTC() calculates an offset based on the Posix gettimeofday() function.

If either are not defined, OffsetFromUTC() falls back to Indy's GOffsetFromUTC variable, which is 0 by default and must be set manually.  In which case, this is a bug in Indy, as those conditionals SHOULD be defined when compiling for Linux.


Uhh, it is not my level, yet.  %)
All I can say that I did not modify anything in Indy, it is as it was originally.
I made this workaround:
Code: Pascal  [Select]
  1. if OffsetFromUTC() = 0 then mMessage.Date := LocalTimeToUniversal(Now);
By using this the time is ready in my email client.
Title: Re: Sending email with Indy
Post by: lucamar on May 01, 2019, 06:52:51 am
I have already considered using FPC's offset functions and am hesitant to use them because FPC's own documentation states that they return stale data after DST changes.  So I haven't applied any fixes to Indy yet.

It also shows the solution: caling unix.ReReadLocalTime before GetLocalTimeOffset.

I guess they don't do so automatically because it's only a couple times per year and the conditions are (usually) very constrained. In Europe, for example, you would have to have the program running on the last Sunday of March/October before 2:00 and call GetLocalTimeOffset after 2:00 to get a bad offset.
Title: Re: Sending email with Indy
Post by: Remy Lebeau on May 01, 2019, 06:32:07 pm
It also shows the solution: caling unix.ReReadLocalTime before GetLocalTimeOffset.

Yes, I know, but WHEN should that be called?  The documentation only says to call ReReadLocalTime() to reinitialize the timezone info, but it doesn't actually say to call it before GetLocalTimeOffset(), and I'm guessing ReReadLocalTime() should not be called every time GetLocalTimeOffset() is being called, right?  Otherwise GetLocalTimeOffset() could have just done that internally (and why doesn't it?).

Remember, Indy is a library, so should it even try to call ReReadLocalTime() periodically, or should it require the user's app to call it?  And then people forget to call it, the data goes stale, and again Indy gets blamed for being buggy.

Oh well, better than nothing, right?  I'll look into adding use of GetLocalTimeOffset(), LocalTimeToUniversal(), and UniversalTimeToLocal() into Indy when I have some free time.
Title: Re: Sending email with Indy
Post by: lucamar on May 01, 2019, 10:57:04 pm
It also shows the solution: caling unix.ReReadLocalTime before GetLocalTimeOffset.

Yes, I know, but WHEN should that be called?

Well, the docs say:
Quote
Note that on Linux/Unix, this information may be inaccurate around the DST time changes (for optimization). In that case, the unix.ReReadLocalTime unit must be used to re-initialize the timezone information.
and, for ReReadLocalTime:
Quote
To speed up conversion of epoch (UTC) time to local time, the timezone information is loaded only once, at program startup. Calling this routine re-reads the timezone information using current timezone settings.

It doesn't matter (much) because it's not a value that changes with frequency (hey, two times a year!) but to be absolutely sure it's never out of synch one should do something like:
Code: Pascal  [Select]
  1. function GetRealLocalTimeOffset: Integer;
  2. begin
  3.   unix.ReReadLocalTime; {ifdefed for UNIX, of course}
  4.   Result := GetLoaclTimeOffset;
  5. end;

In a normal application one could use a Timer and call RereadLocalTime every hour or so, but I guess that doesn't apply for a library like Indy :)
Title: Re: Sending email with Indy
Post by: Remy Lebeau on May 02, 2019, 03:56:37 am
to be absolutely sure it's never out of synch one should do something like:
Code: Pascal  [Select]
  1. function GetRealLocalTimeOffset: Integer;
  2. begin
  3.   unix.ReReadLocalTime; {ifdefed for UNIX, of course}
  4.   Result := GetLoaclTimeOffset;
  5. end;

Is that thread-safe, though?
Title: Re: Sending email with Indy
Post by: lucamar on May 02, 2019, 07:28:24 am
Is that thread-safe, though?

I haven't seen the implementation but probably not. It has to ask the system and only the gods know what the OS may decide to do while it is at it. :)
Title: Re: Sending email with Indy
Post by: justnewbie on May 19, 2019, 03:53:04 pm
New question:
I want to send an email to some recipients. The email is the same for all, except a random number within it, it is unique (simplified example of course).
How can I send the same emails to all with this unique part inside (ie. how can I update only the message body part)?
This is my bad code:
Code: Pascal  [Select]
  1. ...
  2.  
  3. with TIdText.Create(MailMessage.MessageParts, nil) do
  4. begin
  5.       Body.Text := Memo1.Text;
  6.       ContentType := 'text/plain';
  7.       ParentPart := 0;
  8. end;  
  9.  
  10. ...
  11.  
  12. try
  13.   try      
  14.     SMTP.Connect;
  15.  
  16.     for i := 0 to ListBox1.Count - 1 do
  17.       begin
  18.         MailMessage.Recipients.EMailAddresses := ListBox1.Items[i];
  19.         Body.Text := Body.Text + InToStr(Random(1000)); // This line has no effect
  20.         SMTP.Send(MailMessage);        
  21.       end;
  22.  
  23.     except on E:Exception do
  24.       Memo2.Lines.Insert(0, 'ERROR: ' + E.Message);
  25.     end;
  26. finally
  27.   if SMTP.Connected then SMTP.Disconnect;
  28. end;
  29.  
  30. ...

Can you help me?
Title: Re: Sending email with Indy
Post by: Remy Lebeau on May 20, 2019, 08:01:53 am
How can I send the same emails to all with this unique part inside (ie. how can I update only the message body part)?

Since you are putting the random number into a TIdText object, simply store a reference to that object in a local variable so you can refer back to it on each loop iteration, eg:

Code: Pascal  [Select]
  1. var
  2.   IdText: TIdText;
  3.   MyText: string;
  4.  
  5. ...
  6.  
  7. MyText := Memo1.Text;
  8.  
  9. IdText := TIdText.Create(MailMessage.MessageParts, nil);
  10. IdText.ContentType := 'text/plain';
  11. IdText.ParentPart := 0;
  12.  
  13. ...
  14.  
  15. for i := 0 to ListBox1.Count - 1 do
  16. begin
  17.   MailMessage.Recipients.EMailAddresses := ListBox1.Items[i];
  18.   IdText.Body.Text := MyText + IntToStr(Random(1000));
  19.   SMTP.Send(MailMessage);        
  20. end;
  21.  
  22. ...
Title: Re: Sending email with Indy
Post by: justnewbie on May 20, 2019, 12:57:01 pm
Thank you Remy, it solved the original issue.
But unfortunately generated a new one.
Now I cannot send attachments (it worked before this modification you gave).
Now I got a '... raised exception class 'External: SIGKILL'.'
What does cause this?
This is my code:
Code: Pascal  [Select]
  1. ...
  2.  
  3. IdText := TIdText.Create(MailMessage.MessageParts, nil);
  4. IdText.ContentType := 'multipart/alternative';
  5.  
  6. IdText.ContentType := 'text/plain';
  7. IdText.ParentPart := 0;
  8.  
  9. IdText.Body.LoadFromFile(sendHTMLFile);
  10. IdText.ContentType := 'text/html';
  11. IdText.ParentPart := 0;
  12.  
  13. if slAttachedFiles.Count > 0 then
  14.   begin
  15.     for i := 0 to slAttachedFiles.Count - 1 do
  16.       begin
  17.         if FileExists(slAttachedFiles[i]) then TIdAttachmentFile.Create(MailMessage.MessageParts, slAttachedFiles[i]);
  18.       end;
  19.   end;
  20.  
  21. MailMessage.ContentType := 'multipart/mixed';
  22.  
  23. ...
  24.  
  25. try
  26.   try      
  27.     SMTP.Connect;
  28.  
  29.     for i := 0 to ListBox1.Count - 1 do
  30.       begin
  31.         MailMessage.Recipients.EMailAddresses := ListBox1.Items[i];
  32.         IdText.Body.Text := MyText + IntToStr(Random(1000));
  33.         SMTP.Send(MailMessage);        
  34.       end;
  35.  
  36.     except on E:Exception do
  37.       Memo2.Lines.Insert(0, 'ERROR: ' + E.Message);
  38.     end;
  39. finally
  40.   if SMTP.Connected then SMTP.Disconnect;
  41. end;
  42.  
  43. ...
Title: Re: Sending email with Indy
Post by: Remy Lebeau on May 20, 2019, 06:50:56 pm
Now I cannot send attachments (it worked before this modification you gave).
Now I got a '... raised exception class 'External: SIGKILL'.'
What does cause this?

Offhand, I see nothing in the code you have shown that can cause an SIGKILL signal. You are just going to have to debug your code line by line until you find the error.

However, I do see some mistakes (ie missing code) in the way you are managing the structure of the TIdMessage. I suggest you read the following blog articles on Indy's website, if you have not already done so yet:

HTML Messages (https://www.indyproject.org/2005/08/17/html-messages/)

New HTML Message Builder class (https://www.indyproject.org/2008/01/16/new-html-message-builder-class/)
Title: Re: Sending email with Indy
Post by: justnewbie on May 20, 2019, 10:24:01 pm
My code was made based on those webpages.
What are those missing parts? Cannot see them, I think my code is complete.
As I wrote, sending attachments before the modification was fine.
Title: Re: Sending email with Indy
Post by: Remy Lebeau on May 21, 2019, 09:27:40 pm
My code was made based on those webpages.
What are those missing parts? Cannot see them, I think my code is complete.

The code you have shown is NOT complete, as it is not creating enough TIdText objects as required for your MIME emails.  The code you have shown is creating only 1 TIdText object, but you actually need 3 separate TIdText objects instead, eg:

Code: Pascal  [Select]
  1. IdAlternative := TIdText.Create(MailMessage.MessageParts, nil);
  2. IdText.ContentType := 'multipart/alternative';
  3.  
  4. IdPlainText := TIdText.Create(MailMessage.MessageParts, nil); // <-- add this!
  5. IdPlainText.ContentType := 'text/plain';
  6. IdPlainText.ParentPart := IdAlternative.Index;
  7.  
  8. IdHTML := TIdText.Create(MailMessage.MessageParts, nil); // <-- add this!
  9. IdHTML.Body.LoadFromFile(sendHTMLFile);
  10. IdHTML.ContentType := 'text/html';
  11. IdHTML.ParentPart := IdAlternative.Index;

Then your send loop can update IdPlainText.Body and IdHTML.Body as needed.

This is clearly explained in my blog articles.  So, either the code you have shown is really missing those extra steps, or you have simply not shown everything you are really doing.
Title: Re: Sending email with Indy
Post by: justnewbie on May 22, 2019, 04:07:07 pm
Remy, thank you very much! Your example solved the issue perfectly!  :)