My program uses Indy 10 TIdSMTP and TIdMessage to send error reports via email. The program is used mainly in Germany, but some clients are in the USA.
The tool works flawlessly in Germany, but the USA clients are not able to send anything at all. The output shows that the connection to the German email server (smtp.web.de) is made, but after that, an immediate disconnect follows. The exception "Socket error #10054 connection reset by peer" is thrown.
Here's some code:
procedure SendMail(const PI_sSenderAddress: string; const PI_arrReceiverAddresses, PI_arrCCAddresses, PI_arrBCCAddresses: array of string; const PI_sSubject, PI_sMailText: string; const PI_arrAttachments: array of string; const PI_sHost: string = ''; const PI_sUsername: string = ''; const PI_sPassword: string = ''; const PI_bUseTLS: boolean = False; const PI_nPort: integer = 25; const PI_nTimeoutSeconds: integer = 5 );
var
smtp: TIdSMTP;
Mail: TIdMessage;
i: integer;
arrAttachments: array of TIdAttachmentFile;
io: TIdSSLIOHandlerSocketOpenSSL;
begin
io := nil;
smtp := TIdSMTP.Create(Application);
try
smtp.Password := PI_sPassword;
smtp.Username := PI_sUsername;
smtp.Port := PI_nPort;
smtp.Host := PI_sHost;
if PI_bUseTLS then begin
io := TIdSSLIOHandlerSocketOpenSSL.Create(Application);
smtp.IOHandler := io;
end;
smtp.UseTLS := utUseRequireTLS;
if PI_sUserName <> '' then begin
smtp.AuthType := satDefault;
end else begin
smtp.AuthType := satNone;
end;
smtp.HeloName := Split(PI_sUsername, 1, '#');
mail := TIdMessage.Create( Application );
try
mail.Clear;
mail.From.Address := PI_sSenderAddress;
mail.From.Text := PI_sSenderAddress;
mail.Subject := PI_sSubject;
mail.Recipients.EMailAddresses := ArrayToStr( PI_arrReceiverAddresses, ',' ); //!! only one
mail.CCList.EMailAddresses := ArrayToStr( PI_arrCCAddresses, ',' );
mail.BccList.EMailAddresses := ArrayToStr( PI_arrBCCAddresses, ',' );
mail.ReceiptRecipient.Text := '';
mail.Body.Add( PI_sMailText );
mail.Date := now;
if Length(PI_arrAttachments) > 0 then begin
SetLength(arrAttachments, Length(PI_arrAttachments));
for i := 0 to High(PI_arrAttachments) do begin
arrAttachments[i] := TIdAttachmentFile.Create(mail.MessageParts, PI_arrAttachments[i]);
end;
end;
try
smtp.ConnectTimeout := PI_nTimeoutSeconds * 1000;
smtp.Connect;
if smtp.Connected then begin
smtp.Send(mail);
end;
finally
smtp.disconnect;
if Length(arrAttachments) > 0 then begin
for i := 0 to High(arrAttachments) do begin
arrAttachments[i].Free;
end;
end;
end;
finally
mail.Free;
end;
finally
smtp.free;
if Assigned(io) then io.Free;
end;
end;
I call it like this:
SendMail(
'myaccount#web.de',
['receiver#web.de'],
[],
[],
'Subject',
'Text',
[],
'smtp.web.de',
'myaccount#web.de',
'mypassword',
True,
587
);
Can someone help?
EDIT: I changed to a googlemail account, but getting the same error. Currently, i call it like this:
I call it like this:
SendMail(
'myaccount#gmail.com',
['receiver#web.de'],
[],
[],
'Subject',
'Text',
[],
'smtp.gmail.com',
'myaccount#gmail.com',
'mypassword',
True,
587
);
Related
I try to send eMail with the code below (taken from http://delphiprogrammingdiary.blogspot.com/2014/12/how-to-send-email-in-delphi.html) without success.
Constantly I receive the error ("socket error 10054 connection reset by peer" twice and finally "SSL negotiation failed") at the statement "Send(IdMessage1)" although client is successfully connected with server.
procedure SendEmailClick(Sender: TObject);
var
IdMessage1: TIdMessage;
Attachmentfile: TIdAttachmentFile;
begin
// IO HANDLER SETTINGS //
With TIdSSLIOHandlerSocketOpenSSL.Create(nil) do
begin
Destination := 'mySrver.com:587';
Host := 'mySrver.com';
MaxLineAction := maException;
Port := 587;
SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2];
SSLOptions.Mode := sslmUnassigned;
SSLOptions.VerifyMode := [];
SSLOptions.VerifyDepth := 0;
end;
//SETTING SMTP COMPONENT DATA //
with TIdSMTP.Create(nil) do begin
Host := 'mySrver.com';
Port := 587;
Username := myMailAddress; // please change to your gmail address //
Password := myPassword;
IOHandler := TIdSSLIOHandlerSocketOpenSSL.create;
AuthType := satDefault;
UseTLS := utUseExplicitTLS;
// SETTING email MESSAGE DATA //
IdMessage1:= TIdMessage.Create(nil);
IdMessage1.Clear;
// add recipient list //
with IdMessage1.Recipients.Add do
begin
Name := 'Recipient 1';
Address := recipient1Address; // please change email address as required //
end;
//add Attachment to mail //
Attachmentfile := TIdAttachmentFile.Create(IdMessage1.MessageParts,'Τιμολόγιο 659.PDF');
IdMessage1:= TIdMessage.Create(nil);
IdMessage1.From.Address := myMailAddress; // please change to your gmail address //;
IdMessage1.Subject := 'Test Email Subject';
IdMessage1.Body.Add('Test Email Body');
IdMessage1.Priority := mpHigh;
TRY
Connect(); // no problem here. it connects always
Send(IdMessage1); // raises the error SSL negotiation failed
ShowMessage('Email sent');
Disconnect();
except on e:Exception do
begin
ShowMessage(e.Message);
Disconnect();
end;
END;
IdMessage1.Free;
IOHandler.Free;
end;
AttachmentFile.Free;
end;
Can you help me please ?
PS1. The same problem raises with any MAPI server (myServer, GMail, Yahoo etc)
PS2. The SSL DLLs are present and they are used by the same application to connect for other purpose without this problem.
You are creating a TIdSSLIOHandlerSocketOpenSSL object and configuring it, but then you discard and leak it and then create and use another TIdSSLIOHandlerSocketOpenSSL object with default settings. Get rid of the second object, use the first object instead.
You are also creating 2 TIdMessage objects, as well. Get rid of the second one.
Also, you don't need to set the Host, Port, and Destination properties on the SSLIOHandler. Connect() will handle that for you.
Try this:
procedure SendEmailClick(Sender: TObject);
var
IdSMTP: TIdSMTP;
IdMessage1: TIdMessage;
Attachmentfile: TIdAttachmentFile;
IdSSL: TIdSSLIOHandlerSocketOpenSSL;
begin
//SETTING SMTP COMPONENT DATA //
IdSMTP := TIdSMTP.Create(nil);
try
IdSMTP.Host := 'mySrver.com';
IdSMTP.Port := 587;
IdSMTP.Username := myMailAddress; // please change to your gmail address
// Password := myPassword;
IdSMTP.AuthType := satDefault;
IdSMTP.UseTLS := utUseExplicitTLS;
// IO HANDLER Settings //
IdSSL := TIdSSLIOHandlerSocketOpenSSL.Create(IdSMTP);
IdSSL.SSLOptions.SSLVersions := [sslvTLSv1, sslvTLSv1_1, sslvTLSv1_2];
IdSSL.SSLOptions.Mode := sslmUnassigned;
IdSSL.SSLOptions.VerifyMode := [];
IdSSL.SSLOptions.VerifyDepth := 0;
IdSMTP.IOHandler := IdSSL;
// SETTING email MESSAGE DATA //
IdMessage1 := TIdMessage.Create(nil);
try
// add recipient list //
with IdMessage1.Recipients.Add do
begin
Name := 'Recipient 1';
Address := recipient1Address; // please change email address as required //
end;
//add Attachment to mail //
Attachmentfile := TIdAttachmentFile.Create(IdMessage1.MessageParts, 'Τιμολόγιο 659.PDF');
IdMessage1.From.Address := myMailAddress; // please change to your gmail address //;
IdMessage1.Subject := 'Test Email Subject';
IdMessage1.Body.Add('Test Email Body');
IdMessage1.Priority := mpHigh;
try
IdSMTP.Connect();
try
IdSMTP.Send(IdMessage1);
ShowMessage('Email sent');
finally
IdSMTP.Disconnect();
end;
except
on e: Exception do
begin
ShowMessage(e.Message);
end;
end;
finally
IdMessage1.Free;
end;
finally
IdSMTP.Free;
end;
end;
I am trying to send a request to a REST API using multipart/form-data as the content type.
I always get "HTTP/1.1 500 Internal Error" as a response.
I tried sending requests to methods that require application/x-www-form-urlencoded and had success, though.
How can I achieve getting a success response from my API using multipart/form-data?
Here is my code:
procedure TForm10.Button1Click(Sender: TObject);
var
RESTClient1: TRESTClient;
RESTRequest1: TRESTRequest;
strImageJSON : string;
Input: TIdMultipartFormDataStream;
begin
Input := TIdMultipartFormDataStream.Create;
Input.Clear;
Input.AddFormField('Email', 'tugba.xx#allianz.com.tr');
Input.AddFormField('Password', 'xxxx');
RESTClient1 := TRESTClient.Create('http://192.168.1.172:81/');
RESTRequest1 := TRESTRequest.Create(nil);
RESTRequest1.Method := TRESTRequestMethod.rmPOST;
RESTRequest1.Resource := 'api/Mobile/MobileLoginControl';
RESTRequest1.AddBody(Input,TRESTContentType.ctMULTIPART_FORM_DATA);
RESTRequest1.Client := RESTClient1;
RESTRequest1.Execute;
strImageJSON := RESTRequest1.Response.Content;
end;
Embarcadero's REST component has its own built in multipart/form-data capabilities via the TRESTRequest.AddParameter() method:
procedure TForm10.Button1Click(Sender: TObject);
var
RESTClient1: TRESTClient;
RESTRequest1: TRESTRequest;
strImageJSON : string;
begin
RESTClient1 := TRESTClient.Create('http://192.168.1.172:81/');
try
RESTRequest1 := TRESTRequest.Create(nil);
try
RESTRequest1.Method := TRESTRequestMethod.rmPOST;
RESTRequest1.Resource := 'api/Mobile/MobileLoginControl';
RESTRequest1.AddParameter('Email', 'tugba.xx#allianz.com.tr', TRESTRequestParameterKind.pkREQUESTBODY);
RESTRequest1.AddParameter('Password', 'xxxx', TRESTRequestParameterKind.pkREQUESTBODY);
RESTRequest1.Client := RESTClient1;
RESTRequest1.Execute;
strImageJSON := RESTRequest1.Response.Content;
finally
RESTRequest1.Free;
end;
finally
RESTClient1.Free;
end;
end;
You don't need to use use Indy's TIdMultiPartFormDataStream, especially when you are not using Indy's TIdHTTP.
I want to send some text from Client to server, but the receiver just hangs and nothing goes thru.
Client
program client;
{$mode objfpc}{$H+}
uses
{$IFDEF UNIX}{$IFDEF UseCThreads}
cthreads,
{$ENDIF}{$ENDIF}
Classes , SysUtils, Windows, Winsock
{ you can add units after this };
procedure GetInformation();
var
_wsdata: WSAData;
result:Integer;
serverSocket, S: TSocket;
_addrIn, _addr: sockaddr_in;
addrSize: Integer;
tid: Cardinal;
SendBuf:Array[0..31] of AnsiChar;
data : Integer;
begin
//result := false;
if WSAStartup(MakeWord(2, 2), _wsdata) <> 0 then
Exit;
serverSocket := socket(AF_INET, SOCK_STREAM, 0);
if serverSocket = INVALID_SOCKET then
Exit;
_addrIn.sin_family := AF_INET;
_addrIn.sin_addr.S_addr := INADDR_ANY;
_addrIn.sin_port := htons(8080);
if bind(serverSocket, _addrIn, SizeOf(_addrIn)) = SOCKET_ERROR then
Exit;
if listen(serverSocket, SOMAXCONN) = SOCKET_ERROR then
Exit;
addrSize := SizeOf(_addrIn);
getsockname(serverSocket, _addrIn, addrSize);
Writeln(Format('Listening on port %d' + #13#10, [ntohs(_addrIn.sin_port)]));
while True do
begin
S := accept(serverSocket, #_addr, #addrSize);
data := recv(s,SendBuf,Length(SendBuf),0);
Writeln('Data Received: ',data);
end;
// result := True;
end;
begin
GetInformation();
ReadLn;
end.
Now the sender which is supposed to send the data Works fine, it's just the Receiver. It looks like this
Server
program server;
{$mode delphi}{$H+}
uses
{$IFDEF UNIX}{$IFDEF UseCThreads}
cthreads,
{$ENDIF}{$ENDIF}
Classes , Windows , Winsock , SysUtils
{ you can add units after this };
procedure SendFullnameTest();
var
MyData:WSADATA;
result:Integer;
s:TSocket;
SendBuf:Array[0..31] of AnsiChar;
clientservice:sockaddr_in;
BytesSent:Integer;
begin
try
result:= WSAStartup(MAKEWORD (2,2), MyData);
if result = NO_ERROR then
begin
s := socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
if s <> INVALID_SOCKET then
begin
clientservice.sin_family := AF_INET;
clientservice.sin_addr.s_addr := inet_addr('127.0.0.1');
clientservice.sin_port := htons(8080);
if connect(s,clientservice,sizeof(clientservice)) <> SOCKET_ERROR then
begin
sendbuf := 'I am a Sent message';
bytesSent := send(s,sendbuf,Length(sendbuf),0);
writeln('Bytes send: ',bytesSent);
end else
writeln('Failed to connect');;
end else
writeln('Error at Socket: ',WSAGetLastError);;
end else
writeln('Error at WSAStartup');
finally
Writeln(SysErrorMessage(GetLastError));
WSACleanUp;
Readln;
end;
end;
begin
SendFullnameTest();
ReadLn;
end.
The annoying thing is it just doesn't show if it sends, neither does it show if it received.
I just get this as end point
In my Delphi 10.3.1 application server (which is a "DataSnap REST Application"), I have put some logging as follows:
procedure TWebModule1.DSAuthenticationManager1UserAuthenticate(
Sender: TObject; const Protocol, Context, User, Password: string;
var valid: Boolean; UserRoles: TStrings);
begin
valid := (User > '') and (Password > ''); // pass-through
if Valid then
begin
MyWebUtils.LogMessage('Authentication, ' + User + ', ' + Password +
', ' + protocol + ', ' + context);
TDSSessionManager.GetThreadSession.PutData('USERNAME', User);
end;
end;
In my client (on Android), which uses a dsRestConnection, I have a procedure to test the connection, which I call, for example in FormShow of the main form.
function TClientModuleRest.TestConnect(var ErrorMsg: String): Boolean;
var
returned: String;
begin
Result := True;
try
DSRestConnection.Username := 'Admin';
DSRestConnection.Password := 'Test';
DSRestConnection.TestConnection([toNoLoginPrompt]);
except
on e: exception do
begin
Result := False;
ErrorMsg := e.Message;
end
end;
end;
This works well.
The problem occurs when I call the first Server method.
function TClientModuleRest.ValidUser(username, password: String;
var Response: String): Integer;
var
Server: TServerMethodsRClient;
Valid: Boolean;
IniFile: TStringList;
FileName: TFileName;
begin
Result := 0;
DSRestConnection.UserName := username;
DSRestConnection.Password := password;
try
Server := TServerMethodsRClient.Create(DSRestConnection);
try
Valid := Server.ValidUser(username, password, Response);
if not Valid then
raise Exception.Create(response);
Result := 1;
finally
Server.Free;
end;
except
on e: exception do
begin
Response := e.Message;
Result := -1; // an error occured
end;
end;
end;
So the problem is that the setting of the .username and .password doesn't 'take'. In other words, my conclusion is that the .username and `.password can only be set before the first connect, and not thereafter.
The server log file shows that even though the ValidUser function was called with the username and password values that were passed to it, the authentication is receiving the username and password from the first (test) connect.
Is this by design? Or am I missing something? Thanks.
First of all, code.
procedure TMain.SendEmailIndy(
const SMTPServer: string;
const FromName, FromAddress: string;
const ToAddresses: string; //comma "," separated list of e-mail addresses
const CCAddresses: string; //comma "," separated list of e-mail addresses
const BCCAddresses: string; //comma "," separated list of e-mail addresses
const Subject: string;
const EmailBody: string;
const IsBodyHtml: Boolean);
var
smtp: TIdSMTP; // IdSmtp.pas
msg: TidMessage; // IdMessage.pas
builder: TIdCustomMessageBuilder; //IdMessageBuilder.pas
s: string;
emailAddress: string;
FileToSend: TIdAttachmentfile;
begin
msg := TidMessage.Create(nil);
try
if IsBodyHtml then begin
builder := TIdMessageBuilderHtml.Create;
TIdMessageBuilderHtml(builder).Html.Text := EmailBody
end else begin
builder := TIdMessageBuilderPlain.Create;
end;
if (Realization.AttachmentD.FileName <> '') then begin
msg.IsEncoded := true;
FileToSend := TIdAttachmentFile.Create(msg.MessageParts, Realization.AttachmentD.FileName);
FileToSend.FileName := Realization.AttachmentD.FileName;
//FileToSend.ContentDisposition := 'attachment';
FileToSend.ContentType := 'multipart/mixed';
ShowMessage('Sent: '+Realization.AttachmentD.FileName);
end;
msg.From.Name := FromName;
msg.From.Address := FromAddress;
msg.Subject := Subject;
//If the message is plaintext then we must fill the body outside of the PlainText email builder.
//(the PlainTextBuilder is unable to build plaintext e-mail)
if not IsBodyHtml then begin
msg.Body.Text := EmailBody;
end;
for s in ToAddresses.Split([',']) do
begin
emailAddress := Trim(s);
if emailAddress <> '' then begin
with msg.recipients.Add do
begin
//Name := '<Name of recipient>';
Address := emailAddress;
end;
end;
end;
for s in CCAddresses.Split([',']) do
begin
emailAddress := Trim(s);
if emailAddress <> '' then
msg.CCList.Add.Address := emailAddress;
end;
for s in BCCAddresses.Split([',']) do
begin
emailAddress := Trim(s);
if emailAddress <> '' then
msg.BccList.Add.Address := emailAddress;
end;
smtp := TIdSMTP.Create(nil);
try
msg.Encoding := meMIME;
msg.ContentType := 'text/html';
msg.CharSet := 'UTF-8';
msg.ContentTransferEncoding:= 'quoted-printable';
smtp.Host := SMTPServer; // IP Address of SMTP server
Smtp.UseTLS := utNoTLSSupport;
smtp.Port := 587; //The default already is port 25 (the SMTP port)
smtp.Username := _GlobalData.EMail;
smtp.Password := _GlobalData.Password;
smtp.Connect;
try
smtp.Send(msg);
ShowMessage('Wiadomość wysłana.');
Realization.AttachmentD.FileName := '';
finally
smtp.Disconnect();
end;
finally
smtp.Free;
end;
finally
msg.Free;
end;
end;
I experience problems with sending e-mail message with attached file to it.
When I remove the following line from the code above, the message is sent without html message (e-mail body) that should be there:
FileToSend.ContentType := 'multipart/mixed';
However, when I leave this line in the code and try to send a message, I receive this message:
A policy-violation was found in an Email message you sent.
This Email scanner intercepted it and stopped the entire message
reaching its destination.
The policy-violation was reported to be:
Disallowed breakage found in header name - not valid email
Please contact your IT support personnel with any queries regarding this
policy.
Therefore my question is, how to send an e-mail with attached file properly.
You are misusing the TIdMessageBuilder... classes (TIdMessageBuilderHtml is perfectly capable of creating plain-text emails, but more importantly you are not calling TIdCustomMessageBuilder.FillMessage() to transfer the builder data into the TIdMessage).
You are not populating the TIdMessage correctly (for instance, you are not setting the TIdMessage.ContentType and TIdAttachmentFile.ContentType properties correctly when an attachment is present).
Try something more like this instead:
procedure TMain.SendEmailIndy(
const SMTPServer: string;
const FromName, FromAddress: string;
const ToAddresses: string; //comma separated list of e-mail addresses
const CCAddresses: string; //comma separated list of e-mail addresses
const BCCAddresses: string; //comma separated list of e-mail addresses
const Subject: string;
const EmailBody: string;
const IsBodyHtml: Boolean);
var
smtp: TIdSMTP; // IdSmtp.pas
msg: TidMessage; // IdMessage.pas
builder: TIdMessageBuilderHtml; //IdMessageBuilder.pas
begin
msg := TidMessage.Create(nil);
try
builder := TIdMessageBuilderHtml.Create;
try
if IsBodyHtml then
begin
builder.Html.Text := EmailBody;
builder.HtmlCharSet := 'utf-8';
builder.HtmlContentTransfer := 'quoted-printable';
end else
begin
builder.PlainText.Text := EmailBody;
builder.PlainTextCharSet := 'utf-8';
builder.PlainTextContentTransfer := 'quoted-printable';
end;
if Realization.AttachmentD.FileName <> '' then
begin
builder.Attachments.Add(Realization.AttachmentD.FileName);
ShowMessage('Sending: ' + Realization.AttachmentD.FileName);
end;
builder.FillMessage(msg);
finally
builder.Free;
end;
msg.From.Name := FromName;
msg.From.Address := FromAddress;
msg.Subject := Subject;
msg.Recipients.EmailAddresses := ToAddresses;
msg.CCList.EmailAddresses := CCAddresses;
msg.BccList.EmailAddresses := BCCAddresses;
smtp := TIdSMTP.Create(nil);
try
smtp.Host := SMTPServer; // IP Address of SMTP server
Smtp.UseTLS := utNoTLSSupport;
smtp.Port := 587; //The default already is port 25 (the SMTP port)
smtp.Username := _GlobalData.EMail;
smtp.Password := _GlobalData.Password;
smtp.AuthType := satDefault;
smtp.Connect;
try
smtp.Send(msg);
finally
smtp.Disconnect;
end;
finally
smtp.Free;
end;
finally
msg.Free;
end;
ShowMessage('Wiadomość wysłana.');
Realization.AttachmentD.FileName := '';
end;