Sending email via GMail with app specific password - delphi

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.

Related

Sending email using TIdSMTP and TIdMessage

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.

Sending emails with Outlook.com using Indy

In the past i could send emails successfully with Indy and Hotmail, but when i try to send using an Outlook account, i always get the 'Authentication unsuccessful' error. My username and password are correct, i can logon on Outlook.com webmail.
Here is my code :
idsmtp1.Host := 'smtp.outlook.com';
idsmtp1.port := 587;
idsmtp1.Username := 'myuser#outlook.com';
idsmtp1.Password := 'mypassword';
idsmtp1.IOHandler := IdSSLIOHandlerSocketOpenSSL1;
idsmtp1.usetls := utUseExplicitTLS;
idsmtp1.UseEhlo := true ;
IdSSLIOHandlerSocketOpenSSL1.SSLOptions.Method := sslvTLSv1;
IdSSLIOHandlerSocketOpenSSL1.SSLOptions.Mode := sslmClient;
idsmtp1.connect;
idsmtp1.Send(idmessage1);
If you've made zero changes to your code, and it decided to stop working, then there is an authentication issue with your Outlook.com account.
It's most likely that you need to enable 2-step verification and create an application password. Email services typically block you from using your standard password unless the app supports more modern login methods, and they force you to set up applications on your account to access it. You'll have to enable 2-step verification first before you can access the app passwords section.
These can be located at Outlook.com > Options > Account Details > Security & Privacy > More Security Settings

SendMail with TIdSMTP Connection Time Out

I am not able to send emails using TIdSMTP I am getting the following message: Socket Error # 10060 / Connection timed out.
I am using version Delphi XE6
Here is my code:
procedure TForm1.Button1Click(Sender: TObject);
var
IdSMTP : TIdSMTP;
IdMessage : TIdMessage;
begin
IdSMTP := TIdSMTP.Create (Nil);
IdMessage := TIdMessage.Create (Nil);
IdSMTP.Host := 'mail.mysmtp.com';
IdSMTP.Port := 25;
IdSMTP.Connect ();
IdMessage.From.Address := 'test#mysmtp.com';
IdMessage.From.Name := 'Contato';
IdMessage.Recipients.EMailAddresses := 'test#hotmail.com';
IdMessage.Subject := 'Contato test';
IdMessage.Body.Text := 'test';
IdSMTP.Send (IdMessage);
IdSMTP.Disconnect ();
FreeAndNil (IdMessage);
FreeAndNil (IdSMTP);
end;
From Google: A socket error in the 10060 range is a Winsock error. It is generally caused by either outgoing connection problems or connection problems on the host end.
I don't know if you sanitized this code to post it or not, but I'd say the culprit is either the hostname or the username on the from address.
Winsock will attempt to create a connection to the hostname. If it fails to get the expected ACK, it'll generate a timeout error. I've also seen this happen when the domain name isn't resolved by DNS.
Also, what was mentioned earlier regarding authentication ... the lack of response from the SMTP host could be due to improper authentication. It all depends on how the host's SMTP service was configured, so it could just ignore unauthorized requests.
You need to see if you must pass in a username/pwd with the SMTP request, or read the mailbox first (read before write, so to speak). I cannot imagine anybody configuring an SMTP server without requiring some kind of authentication, because otherwise you've got what amounts to an "open relay" where any process can send out unlimited traffic through it.
Also, the from address might be required to be valid. That is, 'test#mysmtp.com' would require a user/mailbox for 'test' to exist, as opposed to '*#mysmtp.com' that would work with ANY user/mailbox name.
All of these could result in a timeout because the SMTP host could be configured to simply ignore improper and unauthenticated requests.

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.).

How to Prevent dialog (Basic Authentication prompt) during call of Webservice

In a delphi program (running as a service) i need to call some webservices.
The calls works fine if basic Authentications is not requerired. The calls also works fine if Basic Authentication is requerired and username/password is provided (in BeforePost) using:
InternetSetOption(Data, INTERNET_OPTION_USERNAME,...
InternetSetOption(Data, INTERNET_OPTION_PASSWORD,...
But if Basic Authentication is Requeried, and username/password is not provided, the program brings up af prompt for the username/password (thats a NO-GO in a servcice).
So how can I signal that i DON'T want a prompt, but instead an error?
The problem is, as i can se it, in the SOAPHTTPTrans function THTTPReqResp.Send(const ASrc: TStream): Integer; (line 762 (second call to InternetErrorDlg i that method)).
EDIT1:
if i change the Flags in the beginning of the send method (in SOAPHTTPTRANS) to include INTERNET_FLAG_NO_AUTH, it works as i wanted.
But how do i do that without changing the SAOPHTTPTrans (if possible)?
EDIT2:
ws := THTTPRIO.Create(Self);
ws.URL := 'http://excample.com/ws.asmx';
ws.HTTPWebNode.InvokeOptions := [soIgnoreInvalidCerts];
ws.HTTPWebNode.OnBeforePost := WebServiceCallBeforePost;
AvailabilityWebservice := (ws as AvailabilityServiceSoap);
sTemp := AvailabilityWebservice.GetVersion;
Where AvailabilityServiceSoap is the interface generated using the WSDL importer.
I had this problem when trying to let Windows Live Messenger work through a web filter.
I ended up writing a small program that auto-authenticates every so often.
Hope this helps you too.
uses
... IdHTTP ...;
...
var
httpGetter: TIdHTTP;
...
httpGetter.Request.Username := username;
httpGetter.Request.Password := password;
httpGetter.HandleRedirects := True;
httpGetter.Request.BasicAuthentication := True;
//custom useragent required to let live messenger work
//this part is probably not necessary for your situation
httpGetter.Request.UserAgent := 'MSN Explorer/9.0 (MSN 8.0; TmstmpExt)';
httpGetter.Get(url,MS);
...
You could create a new class which Inherits from THTTPReqResp and override the send method so that you can include your own flags. You should be able to set ws.HTTPWebNode to a new node using the new class.
Something Like
ws := THTTPRIO.Create(Self);
MyNewNode := MyNewClass.Create;
ws.HTTPWebNode := MyNewNode;
ws.URL := 'http://excample.com/ws.asmx';
ws.HTTPWebNode.InvokeOptions := [soIgnoreInvalidCerts];
ws.HTTPWebNode.OnBeforePost := WebServiceCallBeforePost;
AvailabilityWebservice := (ws as AvailabilityServiceSoap);
sTemp := AvailabilityWebservice.GetVersion;
How about checking the servers authentication mode first?
http://en.wikipedia.org/wiki/Basic_access_authentication
The client asks for a page that
requires authentication but does not
provide a user name and password.
Typically this is because the user
simply entered the address or
followed a link to the page.
The server responds with the 401
response code and provides the
authentication realm.
So the client service application could send a Get and see if the response has a header like
WW-Authenticate: Basic realm="Secure Area"

Resources