I've posted various questions trying to figure out all my issues with trying to send an email using TLS with Office365.
My last question here: How do I solve [EIdSMTPReplyError] Authentication unsuccessful?
Since I couldn't solve the latter I got credentials of one of our clients and try testing the sample with their office credentials which brought me one step closer. With my own office credentials I'm just unable to authenticate (see previous link). In PowerShell there is no issue and I can send an email with the same credentials but not programmatically.
I'm now trying to figure out why the server is returning "Invalid Address" using the client's credentials. Again, I'm able to use the exact same credentials in PowerShell and generate a sample email which works. But not with this component.
Slightly adjusted code from previous link:
procedure TForm28.SendEmail(poSMTP:TIdSMTP);
var
loSMTPMessage : TIdMessage;
begin
loSMTPMessage := TIdMessage.Create(nil);
with loSMTPMessage do
begin
Recipients.Add.Address := 'to address';
ReplyTo.Add.Text := edtUsername.Text;
From.Address := edtUsername.Text;
From.Name := 'xxx';
From.Text := 'Test';
Subject := 'Test';
end;
poSMTP.Send(loSMTPMessage);
loSMTPMessage.Free;
end;
procedure TForm28.Method2Click(Sender: TObject);
var
idSMTP1: TIdSMTP;
idSASLLogin: TIdSASLLogin;
idUserPassProvider: TIdUserPassProvider;
lp:PWideChar;
liSize:Cardinal;
begin
idSMTP1 := TIdSMTP.Create(nil);
idSMTP1.OnFailedRecipient := IdSMTP1FailedRecipient;
//have tried all these variations in trying to solve authentication issue
// IdSMTP1.HeloName := GetComputerNameExString(ComputerNameNetBIOS);
// IdSMTP1.HeloName := GetComputerNameExString(ComputerNameDnsHostname);
// IdSMTP1.HeloName := GetComputerNameExString(ComputerNameDnsDomain);
// IdSMTP1.HeloName := GetComputerNameExString(ComputerNamePhysicalNetBIOS);
// IdSMTP1.HeloName := GetComputerNameExString(ComputerNamePhysicalDnsHostname);
// IdSMTP1.HeloName := GetComputerNameExString(ComputerNamePhysicalDnsDomain);
// IdSMTP1.HeloName := GetComputerNameExString(ComputerNamePhysicalDnsFullyQualified);
// IdSMTP1.HeloName := GetComputerNameExString(ComputerNameMax);
try
idSMTP1.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(idSMTP1);
idSMTP1.UseTLS := utUseExplicitTLS;
TIdSSLIOHandlerSocketOpenSSL(idSMTP1.IOHandler).SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2];
idSMTP1.Host := edtSMTP.Text;
idSMTP1.Port := StrToInt(cbPort.Text);
idSASLLogin := TIdSASLLogin.Create(idSMTP1);
idUserPassProvider := TIdUserPassProvider.Create(idSASLLogin);
idSASLLogin.UserPassProvider := idUserPassProvider;
idUserPassProvider.Username := edtUsername.Text;
idUserPassProvider.Password := edtPassword.Text;
idSMTP1.AuthType := satSASL;
idSMTP1.SASLMechanisms.Add.SASL := idSASLLogin;
try
idSMTP1.Connect;
try
if idSMTP1.Authenticate then
SendEmail(idSMTP1);
finally
idSMTP1.Disconnect;
end;
ShowMessage('OK');
except
on E: Exception do
begin
ShowMessage(Format('Failed!'#13'[%s] %s', [E.ClassName, E.Message]));
raise;
end;
end;
finally
idSMTP1.Free;
end;
end;
This gives me an error: Project SMTP_SSL_Example.exe raised exception class EIdSMTPReplyError with message 'Invalid address
'.
Oh my gosh.
From.Text := 'Test';
called after
From.Address := edtUsername.Text
was changing my email address to 'test'.
Cannot believe I've wasted time with something stupid like this.
Related
A while ago I wrote a method in Delphi 2010 to get the OAuth2 token using the Indy components (TidHttp). See code below.
I am now doing something new in Delphi 10.4 and would like to use the REST components such as RESTClient, RESTRequest, TOAuth2Authenticator, etc.
Our grant type is Client Credentials but in none of the examples on the net could I find on how to use TOAuth2Authenticator with Client Credentials. Is it even possible?
We have a client id, client secret and token URL. We do not have authorization or redirect endpoints. In Insomnia, the setup will look like this:
Does somebody know how to get the token using TOAuth2Authenticator with grant type = client_credentials?
Here is the Delphi 2010 code:
procedure TfrmToken.btnGetTokenClick(Sender: TObject);
var
IdHTTP: TidHttp;
lsHttpError: string;
loRequest: TStringStream;
loRespJson: TMemoryStream;
liSuper: iSuperObject;
ldtExpiry: TDateTime;
begin
IdHTTP := TIdHTTP.Create();
loRespJson := TMemoryStream.Create();
try
IdHTTP.HandleRedirects := False;
loRequest := TStringStream.Create('grant_type=client_credentials&client_id=' +
edtKey.Text + '&client_secret='+edtSecret.Text);
try
IdHttp.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(IdHttp);
IdHttp.Request.ContentType := 'application/x-www-form-urlencoded';
try
IdHTTP.Post(edtURL.Text, loRequest, loRespJson);
except
on E: EIdHTTPProtocolException do
begin
lsHttpError := E.ErrorMessage;
end;
on E: Exception do
begin
lsHttpError := E.Message;
end;
end;
if idHTTP.ResponseCode = 200 then
begin
liSuper := SO(StreamToString(loRespJSon));
edtToken.Text := liSuper.S['access_token'];
ldtExpiry := IncSecond(Now, liSuper.i['expires_in']);
edtExpiry.Text := 'Expires in ' + liSuper.S['expires_in'] +
' seconds. Time: ' +
FormatDateTime('yyyy/dd/mm hh:nn:ss', ldtExpiry);
end
else
begin
liSuper := SO(lsHttpError);
edtToken.Text := IdHTTP.ResponseText;
edtExpiry.Text := '';
end;
finally
FreeAndNil(loRequest);
end;
finally
FreeAndNil(IdHTTP);
FreeAndNil(loRespJson);
end;
end;
Windows 7 64 bit
Delphi 5
Indy 10.6.2.5499
I know most of us have nervous ticking with this problem.
But in any case I lost some hours already without result to try different ssl dlls one by one.
In every case I get my favorite "Could Not Load SSL Library" exception.
I reinstalled Indy with previously deleted old Indy and so on.
I went this way more than 10 times in my life but here again...
Maybe you see some specific in my environment or so...
Code:
SSLHandler := TIdSSLIOHandlerSocketOpenSSL.Create(nil);
SSLHandler.SSLOptions.Method := sslvTLSv1;
FIdSMTP := TIdSMTP.Create(nil);
FIdSMTP.SASLMechanisms.Clear;
FIdSMTP.IOHandler := SSLHandler;
FIdSMTP.AuthType := satDefault;
FIdSMTP.UseTLS := utUseExplicitTLS;
FIdSMTP.Host := 'mx.freenet.de';
FIdSMTP.Port := 587;
FIdSMTP.Password := 'xxxxx';
FIdSMTP.Username := 'xxxxx';
FIdMessage := TIdMessage.Create(nil);
FIdMessage.CharSet := 'windows-1252';
FIdMessage.From.Address := 'xxxxx';
FIdMessage.ContentType := 'multipart/mixed';
FIdMessage.subject := subject;
FIdMessage.Recipients.EMailAddresses := email;
FHTML := TIdText.Create(FIdMessage.MessageParts, html);
FHTML.ContentType := 'text/html';
FIdSMTP.ConnectTimeout := 5000;
FIdSMTP.ReadTimeout := 5000;
try
FIdSMTP.Connect;
FIdSMTP.Send(FIdMessage);
FIdSMTP.Disconnect;
except
on e: Exception do
begin
ShowMessage(WhichFailedToLoad());
showmessage('Cannot send mail: ' + #13 + e.Message);
end;
end;
SSLHandler.Free;
FIdSMTP.Free;
FIdMessage.Free;
FHTML.Free;
A bit of an odd one here, when setting "UseTLS" to utUseExplicitTLS and then connecting to a mail server on its Implicit TLS port, the first attempt allows the connection and sends the email, subsequent attempts on that port correctly fail.
Just wondering if anyone has any ideas on how to avoid the false positive on the initial connect and send.
The check is to handle that non-standard ports may be getting used for a user's mail server. Pretty much all examples I've seen assume that the correct information will always be provided.
Below is the code portion that handles it (excluding error logging):
function SendTestEmail(EmailAddress: String): Boolean;
var
EmailMessage: TidMessage;
begin
IdSMTPEmail.AuthType := satDefault
IdSMTPEmail.Username := ...;
IdSMTPEmail.Password := ...;
IdSMTPEmail.Port := 465;
IdSMTPEmail.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(IdSMTPEmail);
IdSMTPEmail.UseTLS := utUseExplicitTLS;
TIdSSLIOHandlerSocketOpenSSL(IdSMTPEmail.IOHandler).SSLOptions.Method := sslvTLSv1_2;
try
// Connect
IdSMTPEmail.Connect('smtp.gmail.com');
try
// Create
EmailMessage := TidMessage.Create(nil);
try
// Set values
EmailMessage.Body.Add('Test Email');
EmailMessage.Subject := 'Test Email';
// Set sender details
EmailMessage.From.Address := 'test#test.com';
EmailMessage.From.Name := 'SSL Test';
// Set recipient
EmailMessage.Recipients.Add.Address := EmailAddress;
try
// Send message
IdSMTPEmail.Send(EmailMessage);
except
// Exception
on E: EIdSMTPReplyError do
begin
// Result
Result := False;
end;
end;
finally
// Free email
EmailMessage.Free;
end;
finally
// Disconnect
IdSMTPEmail.Disconnect;
end;
except
// Exception
on E: Exception do
begin
IdSMTPEmail.Disconnect;
// Result
Result := False;
end;
end;
end;
Using the below code, correctly failed on all attempts of trying to email to port 465.
I don't even pretend to understand why this works but my original didn't.
function SendTestEmail(EmailAddress: String): Boolean;
var
EmailMessage: TidMessage;
IdSSLHandler: TIdSSLIOHandlerSocketOpenSSL;
begin
IdSMTPEmail.AuthType := satDefault;
IdSSLHandler := TIdSSLIOHandlerSocketOpenSSL.Create;
IdSSLHandler.SSLOptions.Method := sslvTLSv1_2;
IdSMTPEmail.IOHandler := IdSSLHandler;
IdSMTPEmail.UseTLS := utUseExplicitTLS;
IdSMTPEmail.Username := ...;
IdSMTPEmail.Password := ...;
IdSMTPEmail.Port := 465;
try
// Connect
IdSMTPEmail.Connect('smtp.gmail.com');
try
// Create
EmailMessage := TidMessage.Create(nil);
try
// Set values
EmailMessage.Body.Add('Test Email');
EmailMessage.Subject := 'Test Email';
// Set sender details
EmailMessage.From.Address := 'test#test.com';
EmailMessage.From.Name := 'SSL Test';
// Set recipient
EmailMessage.Recipients.Add.Address := EmailAddress;
try
// Send message
IdSMTPEmail.Send(EmailMessage);
except
// Exception
on E: EIdSMTPReplyError do
begin
// Result
Result := False;
end;
end;
finally
// Free email
EmailMessage.Free;
end;
finally
// Disconnect
IdSMTPEmail.Disconnect;
end;
except
// Exception
on E: Exception do
begin
IdSMTPEmail.Disconnect;
// Result
Result := False;
end;
end;
end;
I tried this code to save some fields and an image.
I use MySQL and zzeos for connectinf to the database.
How to fix this code ?
procedure Tfbiodata.btnSaveClick(Sender: TObject);
var
gambar : TMemoryStream;
begin
if (edtnis.Text='') or (edtname.Text='') or (cmbjk.Text='') or (edtempat.Text='') or (edtgl.Text='') or (cmbtingkatan.Text='') then
begin
ShowMessage('Maaf !!! Data Anda Belum Lengkap ....');
exit;
end;
begin
zbiodata2.Open;
zbiodata2.Append;
zbiodata2.FieldByName('NIS').AsString := edtnis.Text;
zbiodata2.FieldByName('Nama_siswa').AsString := edtname.Text;
zbiodata2.FieldByName('Jenis_kelamin').AsString := cmbjk.Text;
zbiodata2.FieldByName('Tempat_lahir').AsString := edtempat.Text;
zbiodata2.FieldByName('Tanggal_lahir').AsString := edtgl.Text;
zbiodata2.FieldByName('Tingkatan').AsString := cmbtingkatan.Text;
zbiodata2.FieldByName('Hasil_indentifkasi').AsString := lblhasil.Caption;
zbiodata2.FieldByName('Metode_pembeaaran').AsString := memo1.Text;
try
convertobmp(openpicture.FileName);
gambar := TMemorystream.Create;
image1.Picture.Graphic.SaveToStream(gambar);
zbiodata2.SQL.Text := 'insert into biodata (gambar) values (:p0)';
zbiodata2.Params[0].LoadFromStream(gambar,ftBlob);
zbiodata2.Post;
zbiodata2.ExecSQL;
except
on E:Exception do
ShowMessage('sorry this a problem .' + #13 + 'Error : ' + E.Message);
end;
end;
end;
When I run this code, I get the error "sorry this is a problem . Error: List index out of bounds(2)"
After calling image1.Picture.Graphic.SaveToStream(gambar), set gambar.Position back to 0 before then calling zbiodata2.Params[0].LoadFromStream(gambar,ftBlob):
image1.Picture.Graphic.SaveToStream(gambar);
gambar.Position := 0; // <-- add this
zbiodata2.Params[0].LoadFromStream(gambar,ftBlob);
With that said, you are using zbiodata2 for two different operations at the same time - editing a new row that is being appended, and executing an SQL statement. Don't do that! Use separate components for each operation.
If the image is being saved into the same row that is being appended, don't bother executing a separate SQL INSERT statement at all. Save the image data directly into the row's gambar TField before then calling zbiodata2.Post():
procedure Tfbiodata.btnSaveClick(Sender: TObject);
var
gambar : TStream;
begin
if (edtnis.Text='') or (edtname.Text='') or (cmbjk.Text='') or (edtempat.Text='') or (edtgl.Text='') or (cmbtingkatan.Text='') then
begin
ShowMessage('Maaf !!! Data Anda Belum Lengkap ....');
Exit;
end;
try
convertobmp(openpicture.FileName);
zbiodata2.Open;
zbiodata2.Append;
try
zbiodata2.FieldByName('NIS').AsString := edtnis.Text;
zbiodata2.FieldByName('Nama_siswa').AsString := edtname.Text;
zbiodata2.FieldByName('Jenis_kelamin').AsString := cmbjk.Text;
zbiodata2.FieldByName('Tempat_lahir').AsString := edtempat.Text;
zbiodata2.FieldByName('Tanggal_lahir').AsString := edtgl.Text;
zbiodata2.FieldByName('Tingkatan').AsString := cmbtingkatan.Text;
zbiodata2.FieldByName('Hasil_indentifkasi').AsString := lblhasil.Caption;
zbiodata2.FieldByName('Metode_pembeaaran').AsString := memo1.Text;
if (image1.Picture.Graphic <> nil) and (not image1.Picture.Graphic.Empty) then
begin
gambar := TMemoryStream.Create;
try
image1.Picture.Graphic.SaveToStream(gambar);
gambar.Position := 0;
(zbiodata2.FieldByName('gambar') as TBlobField).LoadFromStream(gambar);
finally
gambar.Free;
end;
{
Alternatively:
gambar := zbiodata2.CreateBlobStream(zbiodata2.FieldByName('gambar'), bmWrite);
try
image1.Picture.Graphic.SaveToStream(gambar);
finally
gambar.Free;
end;
}
end;
zbiodata2.Post;
except
zbiodata2.Cancel;
raise;
end;
except
on E:Exception do
ShowMessage('sorry this a problem .' + #13 + 'Error : ' + E.Message);
end;
end;
If you are still having problems after that, you need to explain what is actually going wrong, what errors you are seeing, etc.
I have created a program that can read email from Exchange 2007. However, it can only read the body of the email in plain-text format. When I tried to retrieve email in HTML format, my software cannot read the body and it always blank. I am using Delphi 2007 and IMAP 9.
Update:
Here is my code:
procedure TForm1.tmrCekTimer(Sender: TObject);
var
TheFlags: TIdMessageFlagsSet;
TheUID: string;
TheMsg: TIdMessage;
MailBoxName: string;
MyClass: TComponent;
begin
MailBoxName := 'INBOX';
if TheImap.SelectMailBox(MailBoxName) = False then
begin
Screen.Cursor := crDefault;
ShowMessage('Error selecting '+MailBoxName);
Exit;
end;
TheMsg := TIdMessage.Create(nil);
nCount := TheImap.MailBox.TotalMsgs;
TheMsg.ContentType := 'multipart/alternative';
TheMsg.Encoding := meMime;
if nCount = 0 then begin
StringGrid1.RowCount := 2;
StringGrid1.Cells[0, 1] := '';
StringGrid1.Cells[1, 1] := '';
StringGrid1.Cells[2, 1] := '';
StringGrid1.Cells[3, 1] := '';
ShowMessage('There are no messages in '+MailBoxName);
end else begin
StringGrid1.RowCount := nCount + 1;
for i := 0 to nCount-1 do begin
TheImap.GetUID(i+1, TheUID);
TheImap.UIDRetrieveFlags(TheUID, TheFlags);
TheImap.UIDRetrieve(TheUID, TheMsg);
//TheImap.UIDRetrieveHeader(TheUID, TheMsg);
StringGrid1.Cells[0, i+1] := IntToStr(i+1);
StringGrid1.Cells[1, i+1] := TheMsg.From.Address;
//StringGrid1.Cells[1, i+1] := TheUID;
if mfSeen in TheFlags then
StringGrid1.Cells[2, i+1] := 'Yes'
else begin
StringGrid1.Cells[2, i+1] := 'No';
end;
end;
end;
The contents of MIME-encoded emails, such as HTML emails (if plain-text and/or attachments are also present) are stored in the TIdMessage.MessageParts property, not in the TIdMessage.Body property. You need to look at the email's actual ContentType property to know which property TIdMessage parsed the email into.
Using MAPI, I usually try to get the PR_BODY_HTML property as string; if that’s empty, I retrieve the PR_HTML property.
const
PR_HTML = $10130102;
PR_BODY_HTML = $1013001E;
This usually works for me. Of course, maybe you’re using different technology altogether, but you’re not giving us much to work with...