Sending email using TIdSMTP and TIdMessage - delphi

I'm using an action from my ActionManager to send an email to a client in my DBGrid.
I select the row with the client details in my DBGrid, right click, a Popupactionbar comes up, I click the send mail action and the mail should be sending.
This is the code for the mail send action:
procedure TForm2.actSendEmailExecute(Sender: TObject);
begin
IdSMTP1.Host := 'smtp.mail.yahoo.com';
IdSMTP1.Port := 465;
//setup mail message
IdMessage1.From.Address := 'my email address is here';
IdMessage1.Recipients.EMailAddresses := DBGrid1.DataSource.DataSet['email'];
IdMessage1.Subject := 'test subject';
IdMessage1.Body.Text := 'test email body' + DBGrid1.DataSource.DataSet['details'];
//send mail try
IdSMTP1.Connect ;
IdSMTP1.Send(IdMessage1) ;
IdSMTP1.Disconnect;
end;
I'm using TIdSMTP and TIdMessage (Indy components).
I've also set the hostname, port, username and password in the TIdSMTP Properties using Object Inspector, all details are as yahoo provided them on their website (hostname, port, my email for login and my email password)
In the row I select in DBGrid there is a field named email which contains the mail address of the client, hence that is where the email should go.
Also, I'm adding some information contained in a field named details, which should go in the body section of the email (see above code)
Once I start my software up, select the row in DBGrid and hit send on the action button, the software freezes (not responding) for somewhere around 20-40 seconds than it returns an error saying: Connection closed gracefully.
I'm using yahoo smtp just to test things out, I would eventually be using the smtp from my hosting provider that hosts my website (and where the db is located also)
Indy version 10.6.2.5311
Using Delphi 10 Seattle
Any thoughts as to what I'm doing wrong so far?

Could this relate to missing SSL libraries? I just note that you're using port 465; isn't that for SSL connections?
I've never worked with the TIdSMTP component, but I've seen a similar situation occur with the TIdHTTP component (the connection times out without any obvious indication as to why) when attempting to use use an SSL connection unless the appropriate DLLs have been made available. They aren't an integral part of Indy (I imagine it's a legal issue regarding the use of encryption in some countries) but they are freely available elsewhere.
Edit: The component also has a "UseTLS" property which may not have been set.

Related

Sending email via GMail with app specific password

I maintain and develop a program that (amongst other things) sends emails via GMail.
Until now, there have been no problems with sending emails, but a few days ago this functionality ceased to work with a message 'Bad credentials'. I looked through the help of GMail and found this explanation/warning/what-have-you:
To help keep your account secure, from May 30, 2022, ​​Google no longer supports the use of third-party apps or devices which ask you to sign in to your Google Account using only your username and password.
The solution is to use the 'app specific' password.
I looked at this question that states:
My most recent try was to create an 'app specific' password on Gmail
But the attached code doesn't actually show how the password is sent.
Reading the question and its answers, I made some changes to my program:
Port := 995
UseTLS := utUseImplicitTLS
SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2]
After these changes, in an attempt to send a test mail (still no app specific password), I get the response
Reply code is not valid: +OK
which might mean that my program fails, as it doesn't send the app specific password.
What I want to know is: how to send that password?
Below is my code that executes as a thread, hence all components are defined in code:
email:= TIdMessage.Create (nil);
try
email.LoadFromFile (FFileName);
email.OnInitializeISO:= DoInitializeISO;
// DeleteFile (FFileName);
smtp:= TIdSMTP.Create (nil);
try
smtp.OnFailedRecipient:= FailedRecipient;
ssl:= TIdSSLIOHandlerSocketOpenSSL.Create (smtp);
ssl.SSLOptions.SSLVersions:= [sslvTLSv1];
smtp.OnStatus := DoStatus;
smtp.IOHandler:= ssl;
smtp.Host:= FHost;
smtp.Password:= FPassword; // now app passord
smtp.Username:= FUsername;
smtp.UseTLS:= utUseExplicitTLS;
smtp.Port:= 587;
smtp.Connect;
try
smtp.Send (email);
finally
smtp.Disconnect;
end;
finally
ssl.free;
smtp.Free;
end;
finally
email.Free;
end;
An application-specific password is simply a password that Gmail generates for you, and then you use it instead of your normal password (ie, in the TIdSMTP.Password property). This is explained in Gmail's documentation:
Sign in with App Passwords
You can just replace the password with the apps password. Assuming your code id similar to that below. Just remember you need to have 2fa enabled in order to create an apps password.
smtp.OnStatus := DoStatus;
smtp.IOHandler:= ssl;
smtp.Host:= FHost;
smtp.Password:= AppsPassword; // Password from Apps Password
smtp.Username:= FUsername;
smtp.UseTLS:= utUseExplicitTLS;
smtp.Port:= 587;
Another option would be to use Xoauth2.

Indy SMTP server - how to detect CC & BCC recipients?

I am trying to develop an SMTP relay server using the Delphi help, Indy examples and Google.
I am just beginning and have a dummy SMTP client which sends a test message to an SMTP server which will later process it and forward it using an SMTP relay.
At the server, I receive one OnRcpt for each recipient (I test with one To, one CC and one Bcc).
I also see the To and CC recipients in AMsg when I handle OnMsgReceive and convert AMsg to a TStringStream (suitably prefixed with To: and CC:), but I don't see the BCC there.
Question: how do I tell which addresses were To, CC and BCC?
Here's some exceedingly simple code. I am setting breakpoints & examining received parameters to try to see what is happing
procedure TEmailMonitorMainForm.IdSMTPServerMsgReceive(ASender: TIdSMTPServerContext;
AMsg: TStream; var VAction: TIdDataReply);
var
LMsg : TIdMessage;
begin
LMsg := TIdMessage.Create(Nil);
LMsg.LoadFromStream(AMsg);
LMsg.Free(); // breakpoint here examine Lmsg
end; // IdSMTPServerMsgReceive()
[Update] when I send, then <message>.BCCList.Count = 1, but when I Receive, it is 0, whereas the received CClist.Count is 1, as it should be.
At the server, I receive one OnRcpt for each recipient (I test with one To, one CC and one Bcc).
The SMTP protocol does not distinguish between To, CC, and BCC recipients. That is an artifact of the email format via its To: and Cc: headers (there is no Bcc: header, though TIdMessage.SaveToFile() creates one so TIdMessage.LoadFromFile() can re-load it). When an SMTP client sends an email to an SMTP server, the client determines the intended recipients and specifies each one in a separate RCPT TO command to the server (that is what allows BCC to work - there is no Bcc: header in the email itself, but the server is given a RCPT TO command for each Bcc recipient). The RCPT TO addresses are the values specified in the TIdSMTPServer.OnRcptTo event, and are collected in the TIdSMTPServerContext.RCPTList property. These addresses are explicitly requested by the SMTP client, and are the only addresses you should deliver the email to (not the recipients specified in the email headers).
I also see the To and CC recipients in AMsg when I handle OnMsgReceive and convert AMsg to a TStringStream (suitably prefixed with To: and CC:), but I don't see the BCC there.
The actual email itself is just arbitrary data as far as an SMTP server is concerned. It is meant to be delivered as-is to each requested recipient. That is why the email data is provided as a TStream in the TIdSMTPServer.OnMsgReceive event, and why TIdSMTPServer in Indy 10 does not provide TIdMessage-based events like it did in Indy 9 and earlier.
If the email is in an RFC 822/2822/5322 format (which it usually is), it will have To: and Cc: headers. When receiving/loading such an email into a TIdMessage, those headers are used to fill in the TIdMessage.Recipients and TIdMessage.CCList properties, respectively. But since there is usually no Bcc: header (unless the email is being loaded from a file created by TIdMessage.SaveToFile()), the TIdMessage.BCCList will not be filled in.
how do I tell which addresses were To, CC and BCC?
Since the SMTP protocol itself does not distinguish between the different recipient types, the only way for you to distinguish between them is to parse the email, looking at its To: and Cc: headers. Any address in the TIdSMTPServerContext.RCPTList property that is not in one of those two headers must be a Bcc recipient. However, there is no guarantee that the other addresses in the TIdSMTPServerContext.RCPTList property will always match the To: and Cc: headers (though they usually will), since technically an SMTP client can send whatever raw data it wants and the SMTP will deliver it as-is without caring what it actually contains.
Here's some exceedingly simple code.
DO NOT use TIdMessage.LoadFromStream() to parse the TStream object provided by the TIdSMTPServer.OnMsgReceive event! It will not always work correctly. The technical reason has been discussed in detail multiple times in the Embarcadero and AToZed forums, for example in this discussion. You need to use the following workaround for the time being. The issue will be addressed in Indy 11:
procedure TEmailMonitorMainForm.IdSMTPServerMsgReceive(ASender: TIdSMTPServerContext; AMsg: TStream; var VAction: TIdDataReply);
var
LMsg : TIdMessage;
LClient: TIdMessageClient;
LIO: TIdIOHandlerStreamMsg;
begin
LMsg := TIdMessage.Create(Nil);
try
//LMsg.LoadFromStream(AMsg);
LClient := TIdMessageClient.Create;
try
LIO := TIdIOHandlerStreamMsg.Create(LClient, AMsg);
LIO.FreeStreams := False;
LIO.EscapeLines := True;
LIO.Open;
LClient.IOHandler := LIO;
LClient.ProcessMessage(LMsg, False);
finally
LClient.Free;
end;
// now you can use LMsg as needed...
finally
LMsg.Free;
end;
end;

Sending email through Delphi

I have never worked with Delphi (my girlfriend have some experience), but there is a small tool that I want to make and I think it should be made on Delphi. After hours of searching and testing we found only this guide (http://delphi.about.com/od/indy/a/email-send-indy.htm) which doesn't work, it gives a huge error when pressing the Send email button.
I am trying to make a tool that will be distributed with my game and will allow the user to send me an email (to my Gmail) in case of a problem or for feedback. He will enter his email, attach a screenshot and fill in a comment box, doesn't need to have something else. Any help will be highly appreciated as I am stuck, thank you.
Here's a simple routine to send an email. I guess you can modify it to fit your needs:
procedure SendImage(const Comment, AImage: String);
var
SMTP: TIdSMTP;
Msg: TIdMessage;
begin
if not FileExists(AImage) then
Exit;
Msg := TIdMessage.Create(nil);
try
Msg.From.Address := 'xxxx#gmail.com';
Msg.Recipients.EMailAddresses := 'xxxx#gmail.com';
Msg.Body.Text := Comment;
TIdAttachmentFile.Create(Msg.MessageParts, AImage);
Msg.Subject := AImage;
SMTP := TIdSMTP.Create(nil);
try
SMTP.Host := 'smtp.gmail.com';
SMTP.Port := 25;
SMTP.AuthType := satDefault;
SMTP.Username := 'xxxx#gmail.com';
SMTP.Password := '##$%';
SMTP.Connect;
SMTP.Send(Msg);
finally
SMTP.Free;
end;
finally
Msg.Free;
end;
end;
PS: Note that you have to replace xxxx#gmail.com with your own email address, and not the user's. You could include their email address in the body of the crash report.
IMO, email is not going to work 100% of the time. You'll be lucky to get 80%. If you use your SMTP, firewalls and ISPs must permit it. If you use their existing email client, you rely on proper configuration. A typical scenario is that they use Gmail or Yahoo, and then you try to send something via "imap email already on system" and the user gets confronted with an old copy of Outlook Express or Windows Mail that they didn't even know they had, but it's "registered" with windows as the default email handler. This gets ugly.
I recommend sending via HTTP to an app on your web page. A PHP script that records the info and image into a MySQL database. Actually, I recommend using a bug tracking database like Mantis or FogBugz (from our patron!). I'd check them out, and then if you like what they offer, it's fairly straightforward to submit reports via HTTP post (or email, etc.).

Delphi Indy Sharepoint Windows Login

I am including these in uses clause
IdAuthentication
,
IdAuthenticationDigest
,
IdAuthenticationNTLM
,
IdAuthenticationSSPI
Currently I have code that does this:
W.Request.BasicAuthentication := True;
W.Request.Username := AOptionsPtr^.AuthUsername;
W.Request.Password := AOptionsPtr^.AuthPassword;
And if I have access to OpenSSL:
TmpOpenSSL := TIdSSLIOHandlerSocketOpenSSL.Create;
TmpOpenSSL.SSLOptions.Method := sslvSSLv23;
TmpOpenSSL.SSLOptions.Mode := sslmClient;
TmpOpenSSL.SSLOptions.VerifyMode := [];
TmpOpenSSL.SSLOptions.VerifyDepth := 0;
//--
W.IOHandler := TmpOpenSSL;
From skimming the documentation for WinINet (yes, I know it is not Indy) it seems persistent connections is also required for authentication. I suppose this also goes for Indy? URL:
http://msdn.microsoft.com/en-us/library/windows/desktop/aa384220(v=vs.85).aspx
I need to get this to work with SharePoint. The problem, however, is hat I have no intranet SharePoint server to test against. Thus I can not step through the code and see what works and what does not. However, I have a potential customer that can test it for me.
What more do I need to do to get above code working with SharePoint using Windows Authentication (NTML? SPPI?)
Will Indy automatically test and use proper auhentication?
do I need to set W.Request.BasicAuthentication := False; for auto authentication/detection to work?
If multiple requests are necessary (with first response being 401) I assume I need to add support for this in my own code when making a GET request? (To set authentication mode and make a new request?)
You can request a persistent connection by setting the Request.Connection property to 'keep-alive'.
TIdHTTP will check the server's WWW-Authorization header and compare it to the TIdAthentication classes you have included in your uses clause. The TIdHTTP.OnSelectAuthorization event will tell you which class was picked, and allow you to override it if needed. The TIdHTTP.OnAuthorization event will be triggered if authentication fails and different credentials are needed.
The BasicAuthrnication property simply allows TIdHTTP to fall back to TIdBasicAuthentication if no other TIdAuthentication class is assigned.
No, you do not need to handle multi-request authentications manually, like NTLM. TIdHTTP and TIdAuthentication handle those details for you.

Send Email in HTML Format

At the moment we are using MAPI to send a plain text email from our application. We specify the dialog flag when the user invokes this function, so that the email will appear in their email client and they can then modify it and send it.
We would like to embelish the email and send it in an HTML format.
According to this link MSDN link MAPI is not sutiable for this http://support.microsoft.com/kb/268440
I have seen an article on ExpertsExchange that say you can use MAPI to do it but I can't get the example to work with Outlook (not tried anyother client yet)
procedure ShowMailDlg(ToName,Address,HTMLMessage: string);
var
li: integer;
lMessage: TMapiMessage;
lRecipArray: array of TMapiRecipDesc;
lREs: DWord;
begin
SetLength(lRecipArray,1);
lRecipArray[0].ulRecipClass:=MAPI_TO;
lRecipArray[0].lpszName:=pChar(ToName);
lRecipArray[0].lpszAddress:=pChar(Address);
lMessage.ulReserved:=0;
lMessage.lpszSubject:=nil;
lMessage.lpszNoteText:=pChar(HTMLMessage);
lMessage.lpszMessageType:= nil;//pChar('HTML');
lMessage.lpszDateReceived:=nil;
lMessage.lpszConversationID:=nil;
lMessage.flFlags:=0;
lMessage.lpOriginator:=nil;
lMessage.nRecipCount:=length(lRecipArray);
lMessage.lpRecips:=PMapiRecipDesc(lRecipArray);
lMessage.nFileCount:=0;
lMessage.lpFiles:=PMapiFileDesc(nil);
lRes:=MapiSendMail(0, 0 , lMessage,MAPI_DIALOG, 0);
end;
Anyone have any ideas how I can do this. I could probably automate Outlook but I would like to keep it fairly independant of email client (hence MAPI)
Thanks
Update: thanks to everyone for the suggestions. The feature is question is not that heavily used, so asking the user to configure SMTP details is not really an option. I think we will just stick to the plain text email.
Thanks
MAPI doesn't support HTML formatted messages. From Microsoft : "Extended Messaging Application Programming Interface (MAPI) should not be used to generate HTML-formatted messages. As an alternative, consider using the Microsoft Outlook Object Model, CDONTS, CDOSYS, CDOEX, or a third-party SMTP control."
I would echo the comments about sending via Indy. I published a unit that works to send HTML messsages with Indy very simply here or feel free to write your own. If you really want to make the messages editable, try a combination of WPTools and Indy. WPTools has good support for HTML markup and then you can send the resulting message via Indy.
I don't have any experience with Synapse so I can't say how easy/hard it is with that project.
If you only have to serve Outlook clients you could try accessing Outlook by OLE:
procedure SendMail(const aRecipient, aSubject, aNote, aFile: string; Silent, HTML: boolean);
const
olMailItem = 0;
var
ii: integer;
MyOutlook, MyMail: variant;
begin
//*** Send something via OLE/Outlook...
//*** Outlook- und Mail-Objekt erstellen...
MyOutlook := CreateOLEObject('Outlook.Application');
MyMail := MyOutlook.CreateItem(olMailItem);
//*** create a mail message...
MyMail.To := aRecipient;
MyMail.Subject := aSubject;
if aNote <> '' then begin
if HTML then
MyMail.HTMLBody := aNote
else begin
MyMail.Body := aNote;
end;
end;
//*** Add Attachment...
if aFile <> '' then begin
MyMail.Attachments.Add(aFile);
end;
if Silent then
MyMail.Send
else
MyMail.Display;
MyOutlook := UnAssigned;
end;
This is also possible using the Synapse library. A specific example is available from the howto page titled "About MIME and its MIME Parts". I personally have used this technique in several programs to send HTML email.
Unfortunately, this doesn't work over MAPI, you will need to get the users SMTP or IMAP information and handle that communication yourself (the Synapse library has routines to do just that).
If you decide to download Synapse, I strongly suggest getting the latest version from the subversion repository. The update available there includes support for Delphi 2009.
For Delphi emailing I would recommend SakEmail
http://groups.yahoo.com/group/sakemail/
If you're using a version of delphi higher then 7,
you should add the version definition to the .inc file.
that's supplied with SakEmail, else it will fall back to
Delphi4 compatibility mode. After patching the inc file it seems
fine with Delphi 2005.
also, it seems HTML over MAPI works in Thunderbird, but no other client.
There is an undocumented feature of MAPISendMail for including an HTML body:
set lpszNoteText to nil (or a pointer to an empty string)
add an HTML attachment
MAPI will use the html attachment as the body of the e-mail (and not include the attachment).
You can use SMTP with Indy:
HTML Messages
New HTML Message Builder class (Indy 10)
You should consider using an SMTP component, Indy for example, and adding the user doing the sending to the CC or BCC field of the message. This largely satisfies the need to have such sent messages appear in the user's own mail client, which is the primary advantage of MAPI. The user may even set up a separate account specifically for receiving such copies.
Doing it this way allows you to completely customize every detail related to sending mail (MHTML being one such example), including caching all mail and doing the sending in a separate thread, or at a different time, and so on. Also, this method is more client-agnostic than even MAPI; for example, this still works even if the user is using web-based email such as Gmail.

Resources