I am getting interesting rejections from my clients mail server when sending a mail with indy-10's tidMessage component saying:
550 Rejected: Message does not contain a Message-ID
I get this even when using indy's own demo app
http://www.indyproject.org/DemoDownloads/Indy_10_MailClient.zip
what do I do to fix this. thanks!
It works with Indy9, maybe things haven't cahnged too much in 10:
procedure AddMsgID(AMsg: TIdMessage);
var
id: AnsiString;
begin
id := GenerateUniqueMsgID;
AMsg.MsgId := id;
AMsg.AddHeader('Message-ID=' + id);
// AMsg.ExtraHeaders.Values['Message-ID'] := id;
end; // AddMsgID
TIdMessage in Indy 10 intentionally omits the 'Message-Id' header when encoding an email to a socket or TStream. You will have to use the TIdMessage.ExtraHeaders property, eg:
IdMessage1.MsgId := '...';
IdMessage1.ExtraHeaders.Values['Message-Id'] := IdMessage1.MsgId;
EDIT:
As a followup for this - TIdMessage has now been updated with logic changes in how it handles the "Message-ID" and "In-Reply-To" headers:
https://www.indyproject.org/2016/09/12/logic-changes-in-tidmessage-regarding-message-id-and-in-reply-to-headers/
The TIdMessage.MsgId property now generates a "Message-ID" header regardless of whether the email is being saved, streamed, or transmitted. So you do not need to use the ExtraHeaders property anymore.
Related
I use the code that follows to send an email from a Delphi application using Indy.
I just left the core part of my routine:
MailMessage.From.Name:= UserName;
MailMessage.Encoding := meDefault;
MailMessage.Subject := MessageObject;
with TIdText.Create(MailMessage.MessageParts, nil) do begin
ContentType := 'multipart/alternative';
end;
with TIdText.Create(MailMessage.MessageParts, nil) do
begin
Body.Text:= MessageText; // MessageText could be eithr HTML or TEXT
ContentType := 'text/html';
end;
// recipients lists are populated
PrepareToList;
PrepareccList;
PrepareBccList;
// attachments are added to the MailMessage
AddAttachments;
MailMessage.ContentType := 'multipart/mixed';
//usage of TIdSMTP to send mail
SMTP.Connect;
SMTP.Send(MailMessage);
The code above somehow works
with most SMTP servers, the message recieved in Outlook looks like this:
...but when i user a specific "problematic" SMTP server, I see:
The first case is ok, the second is odd.
The strange thing is that when using a specific SMTP server, I reproduce this problem, with all the others it is fine.
Could you please give me soem hint to further understand what goes on?
I am trying to develop an SMTP relay server using the Delphi help, Indy examples and Google.
I am just beginning and have a dummy SMTP client which sends a test message to an SMTP server which will later process it and forward it using an SMTP relay.
At the server, I receive one OnRcpt for each recipient (I test with one To, one CC and one Bcc).
I also see the To and CC recipients in AMsg when I handle OnMsgReceive and convert AMsg to a TStringStream (suitably prefixed with To: and CC:), but I don't see the BCC there.
Question: how do I tell which addresses were To, CC and BCC?
Here's some exceedingly simple code. I am setting breakpoints & examining received parameters to try to see what is happing
procedure TEmailMonitorMainForm.IdSMTPServerMsgReceive(ASender: TIdSMTPServerContext;
AMsg: TStream; var VAction: TIdDataReply);
var
LMsg : TIdMessage;
begin
LMsg := TIdMessage.Create(Nil);
LMsg.LoadFromStream(AMsg);
LMsg.Free(); // breakpoint here examine Lmsg
end; // IdSMTPServerMsgReceive()
[Update] when I send, then <message>.BCCList.Count = 1, but when I Receive, it is 0, whereas the received CClist.Count is 1, as it should be.
At the server, I receive one OnRcpt for each recipient (I test with one To, one CC and one Bcc).
The SMTP protocol does not distinguish between To, CC, and BCC recipients. That is an artifact of the email format via its To: and Cc: headers (there is no Bcc: header, though TIdMessage.SaveToFile() creates one so TIdMessage.LoadFromFile() can re-load it). When an SMTP client sends an email to an SMTP server, the client determines the intended recipients and specifies each one in a separate RCPT TO command to the server (that is what allows BCC to work - there is no Bcc: header in the email itself, but the server is given a RCPT TO command for each Bcc recipient). The RCPT TO addresses are the values specified in the TIdSMTPServer.OnRcptTo event, and are collected in the TIdSMTPServerContext.RCPTList property. These addresses are explicitly requested by the SMTP client, and are the only addresses you should deliver the email to (not the recipients specified in the email headers).
I also see the To and CC recipients in AMsg when I handle OnMsgReceive and convert AMsg to a TStringStream (suitably prefixed with To: and CC:), but I don't see the BCC there.
The actual email itself is just arbitrary data as far as an SMTP server is concerned. It is meant to be delivered as-is to each requested recipient. That is why the email data is provided as a TStream in the TIdSMTPServer.OnMsgReceive event, and why TIdSMTPServer in Indy 10 does not provide TIdMessage-based events like it did in Indy 9 and earlier.
If the email is in an RFC 822/2822/5322 format (which it usually is), it will have To: and Cc: headers. When receiving/loading such an email into a TIdMessage, those headers are used to fill in the TIdMessage.Recipients and TIdMessage.CCList properties, respectively. But since there is usually no Bcc: header (unless the email is being loaded from a file created by TIdMessage.SaveToFile()), the TIdMessage.BCCList will not be filled in.
how do I tell which addresses were To, CC and BCC?
Since the SMTP protocol itself does not distinguish between the different recipient types, the only way for you to distinguish between them is to parse the email, looking at its To: and Cc: headers. Any address in the TIdSMTPServerContext.RCPTList property that is not in one of those two headers must be a Bcc recipient. However, there is no guarantee that the other addresses in the TIdSMTPServerContext.RCPTList property will always match the To: and Cc: headers (though they usually will), since technically an SMTP client can send whatever raw data it wants and the SMTP will deliver it as-is without caring what it actually contains.
Here's some exceedingly simple code.
DO NOT use TIdMessage.LoadFromStream() to parse the TStream object provided by the TIdSMTPServer.OnMsgReceive event! It will not always work correctly. The technical reason has been discussed in detail multiple times in the Embarcadero and AToZed forums, for example in this discussion. You need to use the following workaround for the time being. The issue will be addressed in Indy 11:
procedure TEmailMonitorMainForm.IdSMTPServerMsgReceive(ASender: TIdSMTPServerContext; AMsg: TStream; var VAction: TIdDataReply);
var
LMsg : TIdMessage;
LClient: TIdMessageClient;
LIO: TIdIOHandlerStreamMsg;
begin
LMsg := TIdMessage.Create(Nil);
try
//LMsg.LoadFromStream(AMsg);
LClient := TIdMessageClient.Create;
try
LIO := TIdIOHandlerStreamMsg.Create(LClient, AMsg);
LIO.FreeStreams := False;
LIO.EscapeLines := True;
LIO.Open;
LClient.IOHandler := LIO;
LClient.ProcessMessage(LMsg, False);
finally
LClient.Free;
end;
// now you can use LMsg as needed...
finally
LMsg.Free;
end;
end;
I am trying to add to a program of mine the capability of sending html email via SMTP with Indy 9. If the program only contains text (the text will be in Hebrew so I need to display it right to left, which means that I am using HTML statements), then the email is sent correctly. My problem lays with embedding pictures into the HTML stream.
The HTML stream will use a command like
<IMG SRC="cid:foo4atfoo1atbar.net" ALT="IETF logo">
Whilst the Indy 10 component TIdAttachmentFile has a ComponentID property whose value has to be set to the value that 'cid' references, I can't find where to set the ComponentID property in Indy 9.
At the moment, the code which deals with adding the picture (whose name is in laPicture.text) looks like this
if laPicture.text <> '' then
with TIdAttachment.Create (email.MessageParts, laPicture.text) do
begin
ContentDisposition:= 'inline';
ContentType:= 'image/jpeg';
DisplayName:= ExtractFileName (laPicture.text);
filename:= ExtractFileName (laPicture.text);
end;
Where do I define the ContentID?
And, although this is a stupid question, how do I know which version of Indy I have?
TIdAttachment derives from TIdMessagePart, which has a public ContentID property. If your installed version of Indy 9 does not have that property, then you are using an outdated version, so use the ExtraHeaders property instead to add a Content-ID header manually.
Have a look at the following blog article on Indy's website for more information about working with HTML emails:
HTML Messages
Update: so, if the HTML says cid:foo4atfoo1atbar.net then you need to do this in your code to match it:
with TIdAttachment.Create (email.MessageParts, laPicture.text) do
begin
...
ContentID := '<foo4atfoo1atbar.net>';
// or this, if you do not have the ContentID property available:
// ExtraHeaders.Values['Content-ID'] := '<foo4atfoo1atbar.net>';
end;
Note that in Indy 9, you have to provide the brackets manually. Indy 10 inserts them for you if they are omitted, eg:
ContentID := 'foo4atfoo1atbar.net';
I found a solution - I didn't need Indy10 not the Content-ID field.
The code which I showed in my question was fine, the problem was probably in the HTML code which displayed the picture. I thought that the "cid" variable had to 'point' to the value of Content-ID; it transpires that it can be set to the name of the file (TIDAttachment.filename), as follows
<img src="cid:' + ExtractFileName (laPicture.text) + '"><br>
The above line gets inserted into the html stream at the appropriate place.
This works for me:
function SendEmail(SMTP: TIdSMTP; CONST AdrTo, AdrFrom, Subject, Body, HtmlImage, DownloadableAttachment: string; SendAsHtml: Boolean= FALSE): Boolean;
VAR MailMessage: TIdMessage;
begin
Result:= FALSE;
Assert(SMTP <> NIL, 'SMTP in NIL!');
MailMessage:= TIdMessage.Create(NIL);
TRY
MailMessage.ConvertPreamble:= TRUE;
MailMessage.Encoding := meDefault;
MailMessage.Subject := Subject;
MailMessage.From.Address := AdrFrom;
MailMessage.Priority := mpNormal;
MailMessage.Recipients.EMailAddresses := AdrTo;
{How to send multi-part/attachment emails with Indy:
www.indyproject.org/2005/08/17/html-messages
www.indyproject.org/2008/01/16/new-html-message-builder-class }
WITH IdMessageBuilder.TIdMessageBuilderHtml.Create DO
TRY
if SendAsHtml
then Html.Text := Body
else PlainText.Text := Body;
{ This will be visible ONLY if the email contains HTML! }
if SendAsHtml AND FileExists(HtmlImage)
then HtmlFiles.Add(HtmlImage);
if FileExists(DownloadableAttachment)
then Attachments.Add(DownloadableAttachment);
FillMessage(MailMessage);
FINALLY
Free;
END;
{ Connect }
TRY
if NOT SMTP.Connected
then SMTP.Connect;
EXCEPT
on E: Exception DO
begin
AppLog.AddError('Cannot connect to the email server.');
AppLog.AddError(E.Message);
end;
END;
{ Send mail }
if SMTP.Connected then
TRY
SMTP.Send(MailMessage);
Result:= TRUE;
EXCEPT
on E:Exception DO
begin
AppLog.AddError('Connected to server but could not send email!');
AppLog.AddError(E.Message);
end;
END;
if SMTP.Connected
then SMTP.Disconnect;
FINALLY
FreeAndNil(MailMessage);
END;
end;
Note: Replace AppLog with your personal logging system or with ShowMessage.
You need of course the libeay32.dll + ssleay32.dll. I would have posted a link to them, but could not find them anymore.
I'm using Indy to do a Post to an SMS service that will send the SMS, but the SMS text ends up on my phone with %20 instead of spaces, here is the code:
url,text:string;
IdHTTP1: TIdHTTP;
IdSSLIOHandlerSocketOpenSSL2: TIdSSLIOHandlerSocketOpenSSL;
begin
IdSSLIOHandlerSocketOpenSSL2 := TIdSSLIOHandlerSocketOpenSSL.Create;
IdHTTP1 := TIdHTTP.Create;
IdSSLIOHandlerSocketOpenSSL2.SSLOptions.Method := sslvSSLv23;
IdHTTP1.IOHandler := IdSSLIOHandlerSocketOpenSSL2;
IdHTTP1.HandleRedirects := true;
IdHTTP1.ReadTimeout := 5000;
param:=TStringList.create;
param.Clear;
param.Add('action=create');
param.Add('token=' + SMSToken);
param.Add('to=' + Phone);
param.Add('msg=' + MessageText);
url:='https://api.tropo.com/1.0/sessions';
try
text:=IdHTTP1.Post(url, param);
thanks
The TStrings version of TIdHTTP.Post() sends an application/x-www-form-urlencoded request to the server. The posted data is url-encoded by default. The server needs to decode the posted data before processing it. It sounds like the server-side code is not doing that correctly. You can remove the hoForceEncodeParams flag from the TIdHTTP.HTTPOptions property to disable the url-encoding of the posted data, but I would advise you to report the bug to Tropo instead so they can fix their server-side code.
TIdHTTP itself does not apply quoted-printable encoding to posted data, so the data being posted has to be quoted-printable encoded beforehand.
In Indy 10, you can use the TIdFormDataField.Charset property to specify how strings are converted to bytes, and then use the TIdFormDataField.ContentTransfer property to specify how the bytes are encoded. For the ContentTransfer, you can specify '7bit', '8bit', 'binary', 'quoted-printable', 'base64', or a blank string (which is equivilent to '7bit', but without stating as much in the MIME header).
Set the TIdFormDataField.CharSet property to a charset that matches what your OS is using, and then set the TIdFormDataField.ContentTransfer property to '8bit'.
Alternatively, use the TStream overloaded version of TIdMultipartFormDataStream.AddFormField() instead of the String overloaded version, then you can store data in your input TStream any way you wish and it will be encoded as-is based on the value of the TIdFormDataField.ContentTransfer property. This should remove the %20 you are getting.
Here is my little code:
curMessage:TIdMessage;
tidImap: TIdIMAP4;
...
tidImap.UIDRetrieve('123', curMessage);
That works fine! Now when i try to read
curMessage.Body
Then it is empty sometimes. I've understand that it is empty when message IsMsgSinglePartMime is False. So then i can't read message's body from Body property.
I've searched in curMessage's every property, but nowhere could i found the body text. What makes it even more odd, is that when i save curMessage
curMessage.Savefile('...');
then i can see all the body there.
I don't want to make another request to fetch for the body (eg UIDRetrieveText(2)) because i understand that the body data is there somewhere, i just could not find it or is Savefile/SaveStream making some internal requests to server?
Thank you guys in advance!
You need to be checking TIdMessage.MessageParts.
var
Msg: TIdMessage;
i: Integer;
begin
// Code to retrieve message from server
for i := to Msg.MessageParts.Count - 1 do
begin
if (Msg.MessageParts.Items[i] is TIdAttachment) then
// Handle attachment
else
begin
if Msg.MessageParts.Items[i] is TIdText then
HandleText(TIdText(Msg.MessageParts.Items[i]).Body);
end;
end;
end;
In Indy 10, TIdMessageParts has been moved into it's own unit, so you may have to add IdMessageParts to your uses clause.