Hello Sergey
I hope you can help me.
Unfortunately, I am still using Delphi 5 (I updated twice to D2009 or 10,
don't remember, and to XE. I never used them b/c it required to update quite a bit of other costly tools. So I stopped throwing away money for now).
Richview Version 11.0.8.
Some time ago, you showed me/us a demo for sending text+images by email using Indy 9.
I updated to Indy 10 and now I can't send anything. I always get "socket error 10054".It seems as if the content being sent is not Correct, or as if there is a mix-up in the encoding schemes, and the smtp server gives up.
I tried to use the demo you posted here
http://www.trichview.com/support/files/mime.zip
The Indy version (not Indy.old) but VERY strangely I get an AV error
on line 278
htmpart := TIdText.Create(IdMessage1.MessageParts);
very strange b/c on line 265 I have this, working perfectly
txtpart := TIdText.Create(IdMessage1.MessageParts);
The error happens on the "begin" line of the TidText.Create constructor.
Do you have a sample code for Indy 10 that works?
And since wen use the buildemail (i.e.not requiring the Indy MIME encoding), it is stated that Indy "thinks" is a plain text message,
should I set the "NoEncode" property of the TIdMessage to True?
The same for "NOdecode"?
And the ExtraHeaders, do we need to add them? (it seems that buildemail() already adds those lines).
Any detailed info would help, I hope.
Thanks very much for any Help you could give me
Enrico
Heml text+images with Indy 10
-
- Site Admin
- Posts: 17559
- Joined: Sat Aug 27, 2005 10:28 am
- Contact:
The demos in Indy.old and Indy folders are very different.
"Indy.old" really tricks Indy components: the components think that this is a plain text message, but actually it contains a MIME-encoded contents
"Indy" does not use any tricks - it creates a MIME message using Indy's classes and methods for this task.
The problem is in non-Unicode version of Delphi.
See the comments at the beginning of the unit:
" Requires a Unicode version of Delphi (2009 or newer; created in XE2)."
Try changing in this function ("Indy" demo, TfrmMain.BuildEmail) change
ws: String;
to
ws: TRVUnicodeString
It should solve this problem (memory corruption). But this demo works properly only if htmpart.Body.Text and txtpart.Body.Text are Unicode strings. I am not sure what happens in non-Unicode version of Delphi.
If these properties are Unicode in Indy 10 even in Delphi 5, everething is ok. If not, some foreign characters may be lost on conversion.
"Indy.old" really tricks Indy components: the components think that this is a plain text message, but actually it contains a MIME-encoded contents
"Indy" does not use any tricks - it creates a MIME message using Indy's classes and methods for this task.
The problem is in non-Unicode version of Delphi.
See the comments at the beginning of the unit:
" Requires a Unicode version of Delphi (2009 or newer; created in XE2)."
Try changing in this function ("Indy" demo, TfrmMain.BuildEmail) change
ws: String;
to
ws: TRVUnicodeString
It should solve this problem (memory corruption). But this demo works properly only if htmpart.Body.Text and txtpart.Body.Text are Unicode strings. I am not sure what happens in non-Unicode version of Delphi.
If these properties are Unicode in Indy 10 even in Delphi 5, everething is ok. If not, some foreign characters may be lost on conversion.
maybe is not your field anymore....
Thanks Sergey
The change to ws: TRVUnicodeString worked fine.
But now when I use the code to send my own message with
text and images, if I run it in the Ide I get the "socket error 10054".
However, I noticed that when I stepped through the code (F8)
of IdMessageclient.pas in then
TIdMessageClient.SendBody(),it works!
So I add a sleep(1000) in the " for i := 0 to LLastPart do begin", it works ok
even outside the IDE.
Any idea why?
And I also noticed that the code (in the aforementioned) at this point
" if AMsg.MessageParts.Items is TIdAttachment then begin"
gets executed although I do not have any attachment. is that ok?
Thanks
The change to ws: TRVUnicodeString worked fine.
But now when I use the code to send my own message with
text and images, if I run it in the Ide I get the "socket error 10054".
However, I noticed that when I stepped through the code (F8)
of IdMessageclient.pas in then
TIdMessageClient.SendBody(),it works!
So I add a sleep(1000) in the " for i := 0 to LLastPart do begin", it works ok
even outside the IDE.
Any idea why?
And I also noticed that the code (in the aforementioned) at this point
" if AMsg.MessageParts.Items is TIdAttachment then begin"
gets executed although I do not have any attachment. is that ok?
Thanks
-
- Site Admin
- Posts: 17559
- Joined: Sat Aug 27, 2005 10:28 am
- Contact:
uhm, socket error 10054 (while encoding)
Hello sergey
I thought the change you suggested solved the problem..
However, in the most randomly way I get getting the dreaded
socket error 10054. This always happens in one of these Indy parts
a) Encoding text
b) encoding attacment
c) TIdMessageEncoderMIME.Encode
It seems it get stuck in the encoding loops. As if the body or the images
that are encoded in buildemail() are corrupted.
But then all of a sudden, after 15 20 times that it did not work, it
does work 2 or 3 times
This is my code 9a couple of variation in buildemail()
procedure TMyEditor.BuildEmail;
var Stream: TMemoryStream;
ws: Widestring;//TRVUnicodeString;
s: TRVRawByteString;
i: Integer;
txtpart, htmpart, txthtmpart, txthtmlimgpart: TIdText;
imgpart: TIdAttachmentMemory;
begin
// saving text
txtpart := TIdText.Create(IdMsgSend.MessageParts);
Stream := TMemoryStream.Create;
RichViewEdit1.SaveTextToStreamW('', Stream, 80, False, False);
Stream.Position := 0;
SetLength(ws, Stream.Size div 2);
if Stream.Size<>0 then
Stream.ReadBuffer(Pointer(ws)^, Stream.Size);
Stream.Free;
txtpart.Body.Text := ws;
txtpart.ContentType := 'text/plain';
txtpart.ContentTransfer := 'quoted-printable';
txtpart.CharSet := 'utf-8';
// saving HTML
htmpart := TIdText.Create(IdMsgSend.MessageParts);
Stream := TMemoryStream.Create;
RichViewEdit1.SaveHTMLToStreamEx(Stream, '', subjecttext, '', '', '', '',
[rvsoUseCheckpointsNames, rvsoUTF8]);
Stream.Position := 0;
SetLength(s, Stream.Size);
if Stream.Size<>0 then
Stream.ReadBuffer(Pointer(s)^, Stream.Size);
Stream.Free;
htmpart.Body.Text := UTF8StringToWideString(s);//UTF8ToUnicodeString(s);
htmpart.ContentType := 'text/html';
htmpart.ContentTransfer := 'quoted-printable';
htmpart.CharSet := 'utf-8';
// combining text and HTML
IdMsgSend.ContentType := 'multipart/alternative';
if ((Attachments=Nil) or (Attachments.Count=0)) and ((HTMLImages=NIL) or (HTMLImages.Count=0)) then
exit;
txthtmpart := TIdText.Create(IdMsgSend.MessageParts);
txthtmpart.ContentType := 'multipart/alternative';
txthtmpart.Index := 0;
txtpart.ParentPart := txthtmpart.Index;
htmpart.ParentPart := txthtmpart.Index;
// images
for i := 0 to HTMLImages.Count-1 do begin
HTMLImages.Stream.Position := 0;
imgpart := TIdAttachmentMemory.Create(IdMsgSend.MessageParts, HTMLImages.Stream);
imgpart.ContentType := HTMLImages.ContentType;
imgpart.ContentID := '<'+HTMLImages.Name+'>';
imgpart.ContentDisposition := 'inline';
end;
// combining images and text+html
IdMsgSend.ContentType := 'multipart/related; type="multipart/alternative"';
if (Attachments=Nil) or (Attachments.Count=0) then
exit;
if HTMLImages.Count>0 then begin
txthtmlimgpart := TIdText.Create(IdMsgSend.MessageParts);
txthtmlimgpart.ContentType := 'multipart/related; type="multipart/alternative"';
txthtmlimgpart.Index := 0;
txthtmpart.ParentPart := txthtmlimgpart.Index;
for i := IdMsgSend.MessageParts.Count-1-HTMLImages.Count to IdMsgSend.MessageParts.Count-1 do
IdMsgSend.MessageParts.ParentPart := txthtmlimgpart.Index;
txtpart.ParentPart := txthtmpart.Index;
htmpart.ParentPart := txthtmpart.Index;
end;
// files
IdMsgSend.ContentType := 'multipart/mixed';
for i := 0 to Attachments.Count-1 do
TIdAttachmentFile.Create(IdMsgSend.MessageParts, Attachments);
end;
This is the sending part
//// **** setup smtp
SMTP.Password := emailfile.fieldbyname('password').AsString;
SMTP.Username := emailfile.fieldbyname('email').AsString;
SMTP.Host := emailfile.fieldbyname('host').AsString;
SMTP.AuthType := satDefault;
IdUserPassProvider1.Password := emailfile.fieldbyname('password').AsString;
IdUserPassProvider1.Username := emailfile.fieldbyname('userid').AsString;
SMTP.SASLMechanisms.Add.SASL:=IdSASLLogin1;
SMTP.SASLMechanisms.Add.SASL:=IdSASLCRAMMD51;
try
TIdSSLContext.Create.Free; //try to create a SSL context (immediately disposes of it) - need OpenSSL to support this, if it fails, it will fall into the exception
smtp.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(smtp); //if we succeed, then we can create a SSL socket handler and assign it to the IOHandler
if (emailfile.fieldbyname('port').AsInteger =465) then
Smtp.UseTLS := utUseImplicitTLS
else begin
Smtp.UseTLS := utUseExplicitTLS;
end;
except
smtp.IOHandler := TIdIOHandler.MakeDefaultIOHandler(smtp); //the SSL failed to create, so make the default IO handler
smtp.UseTLS := utNoTLSSupport; //mark that we have no TLS support
end;
smtp.ManagedIOHandler := True;
end;
end;
SMTP.Port := emailfile.fieldbyname('port').AsInteger;
// send the message
IdMsgSend.Clear;
buildemail;
with IdMsgSend do begin
From.Text :=emailfile.Fieldbyname('email').AsString;
sender.text:=_busname;
ReplyTo.EMailAddresses := emailfile.fieldbyname('email').AsString;
Recipients.EMailAddresses := toemail;
Subject := subjecttext; { Subject: header }
end;
{now we send the message}
try
SMTP.Authenticate;
except
//authenticate with the server - this will automatically use SASL if configured and supported
MessageDlg('An error occurred attempting to send your e-mail. The error was : Unable to authenticate.', mtError, [mbOK], 0);
Result:=false;
StopButton.tag:=1;
exit;
end;
try
StatusBar1.SimpleText := 'Sending message to ' +custname + ' '+toemail;
SMTP.Send(IdMsgSend);
Result:=True;
StatusBar1.SimpleText:=lg('Mail sent');StatusBar1.update;
except
on E: Exception do begin
Result:=False;
showmessage(lg('Error sending')+#10#13+E.message);
end;
end;
I thought the change you suggested solved the problem..
However, in the most randomly way I get getting the dreaded
socket error 10054. This always happens in one of these Indy parts
a) Encoding text
b) encoding attacment
c) TIdMessageEncoderMIME.Encode
It seems it get stuck in the encoding loops. As if the body or the images
that are encoded in buildemail() are corrupted.
But then all of a sudden, after 15 20 times that it did not work, it
does work 2 or 3 times
This is my code 9a couple of variation in buildemail()
procedure TMyEditor.BuildEmail;
var Stream: TMemoryStream;
ws: Widestring;//TRVUnicodeString;
s: TRVRawByteString;
i: Integer;
txtpart, htmpart, txthtmpart, txthtmlimgpart: TIdText;
imgpart: TIdAttachmentMemory;
begin
// saving text
txtpart := TIdText.Create(IdMsgSend.MessageParts);
Stream := TMemoryStream.Create;
RichViewEdit1.SaveTextToStreamW('', Stream, 80, False, False);
Stream.Position := 0;
SetLength(ws, Stream.Size div 2);
if Stream.Size<>0 then
Stream.ReadBuffer(Pointer(ws)^, Stream.Size);
Stream.Free;
txtpart.Body.Text := ws;
txtpart.ContentType := 'text/plain';
txtpart.ContentTransfer := 'quoted-printable';
txtpart.CharSet := 'utf-8';
// saving HTML
htmpart := TIdText.Create(IdMsgSend.MessageParts);
Stream := TMemoryStream.Create;
RichViewEdit1.SaveHTMLToStreamEx(Stream, '', subjecttext, '', '', '', '',
[rvsoUseCheckpointsNames, rvsoUTF8]);
Stream.Position := 0;
SetLength(s, Stream.Size);
if Stream.Size<>0 then
Stream.ReadBuffer(Pointer(s)^, Stream.Size);
Stream.Free;
htmpart.Body.Text := UTF8StringToWideString(s);//UTF8ToUnicodeString(s);
htmpart.ContentType := 'text/html';
htmpart.ContentTransfer := 'quoted-printable';
htmpart.CharSet := 'utf-8';
// combining text and HTML
IdMsgSend.ContentType := 'multipart/alternative';
if ((Attachments=Nil) or (Attachments.Count=0)) and ((HTMLImages=NIL) or (HTMLImages.Count=0)) then
exit;
txthtmpart := TIdText.Create(IdMsgSend.MessageParts);
txthtmpart.ContentType := 'multipart/alternative';
txthtmpart.Index := 0;
txtpart.ParentPart := txthtmpart.Index;
htmpart.ParentPart := txthtmpart.Index;
// images
for i := 0 to HTMLImages.Count-1 do begin
HTMLImages.Stream.Position := 0;
imgpart := TIdAttachmentMemory.Create(IdMsgSend.MessageParts, HTMLImages.Stream);
imgpart.ContentType := HTMLImages.ContentType;
imgpart.ContentID := '<'+HTMLImages.Name+'>';
imgpart.ContentDisposition := 'inline';
end;
// combining images and text+html
IdMsgSend.ContentType := 'multipart/related; type="multipart/alternative"';
if (Attachments=Nil) or (Attachments.Count=0) then
exit;
if HTMLImages.Count>0 then begin
txthtmlimgpart := TIdText.Create(IdMsgSend.MessageParts);
txthtmlimgpart.ContentType := 'multipart/related; type="multipart/alternative"';
txthtmlimgpart.Index := 0;
txthtmpart.ParentPart := txthtmlimgpart.Index;
for i := IdMsgSend.MessageParts.Count-1-HTMLImages.Count to IdMsgSend.MessageParts.Count-1 do
IdMsgSend.MessageParts.ParentPart := txthtmlimgpart.Index;
txtpart.ParentPart := txthtmpart.Index;
htmpart.ParentPart := txthtmpart.Index;
end;
// files
IdMsgSend.ContentType := 'multipart/mixed';
for i := 0 to Attachments.Count-1 do
TIdAttachmentFile.Create(IdMsgSend.MessageParts, Attachments);
end;
This is the sending part
//// **** setup smtp
SMTP.Password := emailfile.fieldbyname('password').AsString;
SMTP.Username := emailfile.fieldbyname('email').AsString;
SMTP.Host := emailfile.fieldbyname('host').AsString;
SMTP.AuthType := satDefault;
IdUserPassProvider1.Password := emailfile.fieldbyname('password').AsString;
IdUserPassProvider1.Username := emailfile.fieldbyname('userid').AsString;
SMTP.SASLMechanisms.Add.SASL:=IdSASLLogin1;
SMTP.SASLMechanisms.Add.SASL:=IdSASLCRAMMD51;
try
TIdSSLContext.Create.Free; //try to create a SSL context (immediately disposes of it) - need OpenSSL to support this, if it fails, it will fall into the exception
smtp.IOHandler := TIdSSLIOHandlerSocketOpenSSL.Create(smtp); //if we succeed, then we can create a SSL socket handler and assign it to the IOHandler
if (emailfile.fieldbyname('port').AsInteger =465) then
Smtp.UseTLS := utUseImplicitTLS
else begin
Smtp.UseTLS := utUseExplicitTLS;
end;
except
smtp.IOHandler := TIdIOHandler.MakeDefaultIOHandler(smtp); //the SSL failed to create, so make the default IO handler
smtp.UseTLS := utNoTLSSupport; //mark that we have no TLS support
end;
smtp.ManagedIOHandler := True;
end;
end;
SMTP.Port := emailfile.fieldbyname('port').AsInteger;
// send the message
IdMsgSend.Clear;
buildemail;
with IdMsgSend do begin
From.Text :=emailfile.Fieldbyname('email').AsString;
sender.text:=_busname;
ReplyTo.EMailAddresses := emailfile.fieldbyname('email').AsString;
Recipients.EMailAddresses := toemail;
Subject := subjecttext; { Subject: header }
end;
{now we send the message}
try
SMTP.Authenticate;
except
//authenticate with the server - this will automatically use SASL if configured and supported
MessageDlg('An error occurred attempting to send your e-mail. The error was : Unable to authenticate.', mtError, [mbOK], 0);
Result:=false;
StopButton.tag:=1;
exit;
end;
try
StatusBar1.SimpleText := 'Sending message to ' +custname + ' '+toemail;
SMTP.Send(IdMsgSend);
Result:=True;
StatusBar1.SimpleText:=lg('Mail sent');StatusBar1.update;
except
on E: Exception do begin
Result:=False;
showmessage(lg('Error sending')+#10#13+E.message);
end;
end;