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.
Related
in a TWebModule procedure/function how get the current request?
I have tried:
procedure TWebModule1.DoSomething;
var
aRequest : TWebRequest;
begin
aRequest := Request;
end;
but it seems the first request produced on TWebModule creation.
I know i'm able to pass the request to subsequent Procedures/Functions from each TWebActionItem, but i want avoid to pass the request every where. Any tips?
Update
After digging into the code, i found WebContext and it seems the solution, eg.:
uses Web.WebCntxt;
procedure TWebModule1.DoSomething;
var
aRequest : TWebRequest;
begin
if WebContext <> nil then
aRequest := WebContext.Request;
end;
is it the right way? WebContext seems always nil.
I'm on Delphi Berlin update 2.
Every Request goes through a TWebActionItem defined in the TWebModule.Actions. The TWebActionItem has an event OnAction. There you will get the TWebRequest Object of the current Request.
Then you are able to pass it to subsequent Procedures/Functions.
I am using IP*Works! V9. I try to restrict the returned emails to only the one matching a restriction using SearchMailbox. My code looks like this:
lIMap.Mailbox := 'INBOX';
lIMap.SelectMailbox;
lIMap.CheckMailbox;
lIMap.Config('FETCHAFTERSEARCH=True');
lIMap.SearchMailbox('SUBJECT Diessenhofen UNSEEN');
if (lIMap.MessageCount > 0) then
begin
...
end;
MessageCount always reflects the total number of emails instead of one (there is one match in my inbox).
The IMAP server is Kereo
The documentation says it doesn't work like that. SearchMailbox doesn't restrict what's available to you, instead it calls a user-supplied function and fires an even once for each message in the search result.
Thanks to the answer of #arnt, I figured out a solution that works for me.
Yes, for every Message that corresponds to the search criteria, the event OnMessageInfo is fired.
Since I need to go through all messages in a loop, I ended up doing this:
procedure TReadIMapObjectsFavFktProperty.MessageInfo(Sender: TObject;
const MessageId, Subject, MessageDate, From, Flags: String;
Size:Int64);
begin
if (MessageList.IndexOf(MessageId) < 0) then
begin
MessageList.Add(MessageId);
end;
end;
where MessageList is a TStringList with delimiter ',';
I can then get all messages using either
lIMap.MessageSet := MessageList.Text;
again firing the same event or loop through them using the size of the MessageList like this:
for aa := 0 to MessageList.Count - 1 do
begin
lIMap.MessageSet := MessageList.Strings[aa];
lIMap.FetchMessageInfo;
...
end;
I have a message client, written in delphi using Indy libraries, that receives email messages. I am having difficulties decoding an MMS text message email.
These messages come as multipart/mixed emails with one message part (an attachment) that of text/plain (that is base64 encoded) with a filename like text0.txt.
My TIdMessageClient calls ProcessMessage (using the stream-based version) to populate a TidMessage that I'm going to display on the screen. But as I go through the message parts and try to unravel them, that attached file is a thorn in my side. Currently, I have it printing out the name of the attachment into a string which works fine (see code snippet below, FBody is a string type), but can't get the text file's contents.
Here's the bit that does work:
FBody := 'Attachment: ['+TidAttachment(Msg.MessageParts.Items[0]).FileName+']';
(Edited:) Originally when I wrote this question I wasn't sure if the attachment was stored in a TidAttachmentFile or TidAttachmentMemory object. But with the right debugger commands, I've determined it's a TidAttachmentFile. I suppose it would be possible to use TidAttachmentFile.SaveToFile() to save the attachment to a file on disk and then read the file back from disk, but that seems wasteful and slow (especially for a 200 character text message). I would really prefer to do this all "in memory" without temp files if possible.
What do I need to do (a) make TidMessageClient return a TidAttachmentMemory object rather than a TidAttachmentObject (in ProcessMessage), and (b) read the attached text file into a string?
Based on the indy documentation, the start I have at how this code would look is roughly like this:
TidAttachmentMemory(Msg.MessageParts.Items[0]).PrepareTempStream();
FBody := FBody + TidAttachmentMemory(Msg.MessageParts.Items[0]).DataString;
TidAttachmentMemory(Msg.MessageParts.Items[0]).FinishTempStream;
Please feel free to point me in the right direction if this is not the right way to go or use TidAttachment(s).
I suppose it would be possible to use TidAttachmentFile.SaveToFile() to save the attachment to a file on disk and then read the file back from disk, but that seems wasteful and slow (especially for a 200 character text message).
When using TIdAttachmentFile, the file is always on disk. The TIdAttachmentFile.StoredPathName property specifies the path to the actual file. The TIdAttachmentFile.SaveToFile() method merely copies the file to the specified location.
I would really prefer to do this all "in memory" without temp files if possible.
It is possible.
What do I need to do (a) make TidMessageClient return a TidAttachmentMemory object rather than a TidAttachmentObject (in ProcessMessage)
In the TIdMessage.OnCreateAttachment event, return a TIdAttachmentMemory object, eg:
procedure TMyForm.IdMessage1CreateAttachment(const AMsg: TIdMessage; const AHeaders: TStrings; var AAttachment: TIdAttachment);
begin
AAttachment := TIdAttachmentMemory.Create(AMsg.MessageParts);
end;
If no handler is assigned to the TIdMessage.OnCreateAttachment event, or if it does not assign anything to AAttachment, then a TIdAttachmentFile is created by default.
You could optionally implement your own custom TIdAttachment-derived class instead, say one that uses TStringStream internally if you know the attachment contains textual data (which the AHeaders parameter will tell you).
and (b) read the attached text file into a string?
Based on the indy documentation, the start I have at how this code would look is roughly like this:
You are close. You need to use the TIdAttachment.OpenLoadStream() method instead of TIdAttachment.PrepareTempStream(), and you need to read the data from the TStream that TIdAttachment.OpenLoadStream() returns. In your example, you could use Indy's ReadStringFromStream() function for that, eg:
// if using Indy 10.6 or later...
var
Attachment: TIdAttachment;
Strm: TStream;
begin
...
Attachment := TIdAttachment(Msg.MessageParts.Items[0]);
Strm := Attachment.OpenLoadStream;
try
FBody := FBody + ReadStringFromStream(Strm, -1, CharsetToEncoding(Attachment.Charset));
finally
Attachment.CloseLoadStream;
end;
...
end;
Or:
// if using Indy 10.5.x or earlier...
var
Attachment: TIdAttachment;
Strm: TStream;
Enc: TIdTextEncoding;
begin
...
Attachment := TIdAttachment(Msg.MessageParts.Items[0]);
Strm := Attachment.OpenLoadStream;
try
Enc := CharsetToEncoding(Attachment.Charset);
try
FBody := FBody + ReadStringFromStream(Strm, -1, Enc);
finally
Enc.Free;
end;
finally
Attachment.CloseLoadStream;
end;
...
end;
I'm doing just for fun an unread messages checker application in Delphi. I'm using Indy 10. I can connect with Gmail and can retrieve all the messages but I'm facing a problem here: I cannot tell if a message is already read or not. There is a flag property in the TidMessage component that should tell me if the message has been read.
The code looks like this:
procedure TForm1.btTestConnectionClick(Sender: TObject);
var
i: Integer;
count: Integer;
flag: TIdMessageFlags;
begin
if (pop3Test.Connected) then begin
pop3Test.Disconnect;
end;
pop3Test.Username := edAccount.Text;
pop3Test.Password := edPassword.Text;
pop3Test.Host := HOST;
pop3Test.AuthType := patUserPass;
pop3Test.Port := PORT;
pop3Test.Connect;
Count := 0;
for i := pop3Test.CheckMessages downto 1 do begin
pop3Test.Retrieve(i, IdMessage1);
if (mfSeen in IdMessage1.Flags) then begin
Count := Count + 1;
end;
end;
ShowMessage(IntToStr(Count));
pop3Test.Disconnect;
end;
In the test mailbox there is one unread message but all the retrieved messages have the flags enum property empty so the result is always 0. Am I doing something wrong? Is it a problem of Indy/Gmail compatibility?
Thanks.
EDIT: I'm definitely doing something wrong as testing with a Hotmail account shows the same empty-flags property problem.
the POP3 protocol does not support Message state information on the server like read, replied to, or deleted . try using IMAP for Gmail instead.
The best (and quickest) way to find this answer would be to search the Indy sourcecode for "mfSeen" You should find it only utilized in idIMAP* units. RRUZ is correct - POP3 doesn't offer this inherent ability. In POP3 you need to track this on the client side. This flag was added to IdMessage for IMAP purposes, and not necessarily for POP3.
TIdMessageFlags should likely have been named TIdIMAPMessageFlags
I need to send PUT and DELETE along with POST, GET to a REST API how can I do it?
Delphi 7 comes with Indy. See the TIdHTTP component and specificly the Get and Put methods.
Or look at the open source Synapse library. There are some simple function calls in the HTTPSend unit which make implementing this completely painless. Just use the sample functions/procedures as your model for the PUT/DELETE. The existing routines already supply the POST and GET. The difference is in the method passed.
Personally I have found this library to be perfectly matched for working with REST. Its simple, well written and easy to extend.
For example, here is a simple put that sends and receives a stream:
function HttpPutBinary(const URL: string; const Data: TStream): Boolean;
var
HTTP: THTTPSend;
begin
HTTP := THTTPSend.Create;
try
HTTP.Document.CopyFrom(Data, 0);
HTTP.MimeType := 'Application/octet-stream';
Result := HTTP.HTTPMethod('PUT', URL); // changed method from 'POST'
Data.Size := 0;
if Result then
begin
Data.Seek(0, soFromBeginning);
Data.CopyFrom(HTTP.Document, 0);
end;
finally
HTTP.Free;
end;
end;
Check out the ICS components, they are suitable for the job.