Sending emails with Outlook.com using Indy - delphi

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

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.

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

Outlook automation with Delphi - Queue

I currently have the following code:
while not (sqlMailMergeData.Eof) do
begin
if sqlMailMergeData.FieldByName('Email').AsString <> '' then
begin
Inc(Count);
{Connect to Outlook}
MailItem := OpOutlook1.CreateMailItem;
MailItem.MsgTo := sqlMailMergeData.FieldByName('Email').AsString;
MailItem.Body := Form48.Memo1.Text;
MailItem.Subject := Form48.Edit3.Text;
MailItem.Send;
end;
Form34.sqlMailMergeData.next;
end;
However Outlook prompts you to allow ever email with a delay of 5 seconds. Sending after the loop overwrite the same MailItem.
MailItem.Save;
Saves all the items to draft without prompting. This is not a bad solution and could be an extra feature but requires more user input to move the items to outbox.
Is there a function to send each mail item to the outbox? or should I consider creating a string of all the email address e.g.
MailItem.MsgTo := "example#email.com; example2#email.com"
Thanks
When working with outlook, you might consider using outlook redemption1, this way you can bypass the security prompt and send the mail directly from your code.
Then your only option is to do what Redemption (I am its author) is doing under the hood - use Extended MAPI.
This is the code which works fine for me:
Outlook := CreateOleObject ('Outlook.Application');
// Repet the code below for each mail:
OutlookMail := Outlook.CreateItem (olMailItem); // olMailItem = 0;
OutlookMail.Recipients.Add ('example#email.com').Resolve;
OutlookMail.Recipients.Add ('example2#email.com').Resolve;
OutlookMail.Subject := Form48.Edit3.Text;
OutlookMail.Body := Form48.Memo1.Text;
OutlookMail.BodyFormat := olFormatHTML;
// OutlookMail.SendUsingAccount := OutlookAccount; // If you need to select the acount
OutlookMail.Send;

delphi post 'illegal access' error

I'm making some simple Delphi software using the IdHttp module in Indy. I want to access this page by using IdHttp's post function to open this webpage.
Firstly I have to log in, (at http://user.buddybuddy.co.kr/Login/Login.asp), so I logged in, but after I logged in to the webpage I can see another login page. But when I try to login (at http://user.buddybuddy.co.kr/usercheck/UserCheckPWExec.asp), I encountered an error message, "Illegal access."
If anyone can help me I would appreciate it!
begin
sl.Clear;
sl.Add('ID=ph896011');
sl.add('PWD=pk1089');
sl.add('SECCHK=0');
IdHTTP1.HandleRedirects := True;
IdHTTP1.Request.ContentType := 'application/x-www-form-urlencoded';
memo1.Text:=idhttp1.Post('http://user.buddybuddy.co.kr/Login/Login.asp',sl);
if pos('top.location =',Memo1.Text)> 0 then
begin
application.ProcessMessages;
sleep(1000);
Memo1.Text:= '';
sleep(300);
sl2.Clear;
sl2.Add('PASSWD=pk1089' );
Memo2.Text := IdHTTP1.Post('http://user.buddybuddy.co.kr/usercheck/UserCheckPWExec.asp', sl2);
sleep(300);
Memo1.Text := '';
//memo1.Text := IdHTTP1.Get('https://user.buddybuddy.co.kr/Login/Logout.asp');
//Sleep(1000);
end;
The server almost certainly keeps track of whether you're logged in by returning a cookie with the response from the request for Login.asp. You need to send that cookie back with any subsequent requests. To keep track of cookies, create a TIdCookieManager object and assign it to your HTTP object's CookieManager property. The first request will store the response's cookies there, and the next request on that same HTTP object will send them back, indicating to the server that you have already logged in.

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