How can I retrieve a specific e-mail based on a certain text contained in the message ? For example how Gmail search works. If you search for a specific text, which is in the e-mail, then the Gmail will retrieve the message that is associated with the text. Preferably without any looping.
Asked
Active
Viewed 5,202 times
1 Answers
6
You're looking for the SearchMailBox
method. Here's a simple example expecting that you have the IMAP client (in this case, the IMAPClient
variable of the TIdIMAP4
type) already connected to the Gmail server. For those looking for how to do so, take a look for instance at this post
and put this code inside the try..finally
block near IMAPClient.Connect
and IMAPClient.Disconnect
.
var
// in this example is not shown how to connect to Gmail IMAP server but
// it's expected that the IMAPClient object is already connected there
IMAPClient: TIdIMAP4;
procedure TForm1.Button1Click(Sender: TObject);
var
I: Integer;
MsgObject: TIdMessage;
SearchInfo: array of TIdIMAP4SearchRec;
begin
// if the mailbox selection succeed, then...
if IMAPClient.SelectMailBox('INBOX') then
begin
// set length of the search criteria to 1
SetLength(SearchInfo, 1);
// the SearchKey set to skBody means to search only in message body texts
// for more options and explanation, see comments at the TIdIMAP4SearchKey
// enumeration in the IdIMAP4.pas unit
SearchInfo[0].SearchKey := skBody;
// term you want to search
SearchInfo[0].Text := 'Search term';
// if the search in the selected mailbox succeed, then...
if IMAPClient.SearchMailBox(SearchInfo) then
begin
// iterate the search results
for I := 0 to High(IMAPClient.MailBox.SearchResult) do
begin
// make an instance of the message object
MsgObject := TIdMessage.Create(nil);
try
// try to retrieve currently iterated message from search results
// and if this succeed you can work with the MsgObject
if IMAPClient.Retrieve(IMAPClient.MailBox.SearchResult[I],
MsgObject) then
begin
// here you have retrieved message in the MsgObject variable, so
// let's do what what you need with the >> MsgObject <<
end;
finally
MsgObject.Free;
end;
end;
end;
end;
end;
Here's the quick implementation of the IMAP search for UTF-8 charset. It uses interposed class due to protected ParseSearchResult
method:
type
TBasicSearchKey = (bskBcc, bskBody, bskCc, bskFrom, bskHeader, bskKeyword,
bskSubject, bskText, bskTo);
const
IMAPSearchKeys: array [TBasicSearchKey] of string = ('BCC', 'BODY', 'CC',
'FROM', 'HEADER', 'KEYWORD', 'SUBJECT', 'TEXT', 'TO');
type
TIdIMAP4 = class(IdIMAP4.TIdIMAP4)
public
function SearchMailBoxUTF8(const ASearchText: string;
ASearchKey: TBasicSearchKey): Boolean;
end;
implementation
{ TIdIMAP4 }
function TIdIMAP4.SearchMailBoxUTF8(const ASearchText: string;
ASearchKey: TBasicSearchKey): Boolean;
var
SearchText: RawByteString;
begin
Result := False;
CheckConnectionState(csSelected);
SearchText := UTF8Encode(ASearchText);
SendCmd(Format('SEARCH CHARSET UTF-8 %s {%d}', [IMAPSearchKeys[ASearchKey],
Length(SearchText)]), ['SEARCH']);
if LastCmdResult.Code = IMAP_CONT then
IOHandler.WriteLn(SearchText, TEncoding.UTF8);
if GetInternalResponse(LastCmdCounter, ['SEARCH'], False) = IMAP_OK then
begin
ParseSearchResult(FMailBox, LastCmdResult.Text);
Result := True;
end;
end;
And the usage:
procedure TForm1.Button1Click(Sender: TObject);
var
I: Integer;
MsgObject: TIdMessage;
begin
if IMAPClient.SelectMailBox('INBOX') and
IMAPClient.SearchMailBoxUTF8('Search term', bskText) then
begin
for I := 0 to High(IMAPClient.MailBox.SearchResult) do
begin
MsgObject := TIdMessage.Create(nil);
try
if IMAPClient.Retrieve(IMAPClient.MailBox.SearchResult[I],
MsgObject) then
begin
// here you have retrieved message in the MsgObject variable, so
// let's do what what you need with the >> MsgObject <<
end;
finally
MsgObject.Free;
end;
end;
end;
end;
-
Does this return HTML or pure text? Because if you search for something in emails that are pure text it works OK. If they are HTML search won't work. – Nov 28 '12 at 21:49
-
It depends on target IMAP server implementation of the [`SEARCH`](http://tools.ietf.org/html/rfc1730#section-6.4.4) command, but in this case it doesn't search in text message parts unfortunately. Good point to mention. – TLama Nov 28 '12 at 22:15
-
I think there could be a way to set to return pure html or text in gmail settings. – Nov 28 '12 at 22:16
-
I don't think so. When you call `SearchMailBox` you just send the command to the server, something like `SEARCH BODY "Search term"` and it's upon server how it takes that and what returns you. I'd surprised if that would be configurable individually. Their search might work different, they can loop all the messages in the blink of an eye since they're onsite, so there might not be used `SEARCH` command. – TLama Nov 28 '12 at 22:40
-
Well i was looking for a more universal solution. Is there other IMAP servers that do allow this? Hotmail/Yahoo etc. Because i can adopt to other service. – Nov 28 '12 at 22:49
-
Well, the `SEARCH` seems to work for all messages; I've just tested it with the search term containing diacritic and those won't work with the code as it is now. The search term should be encoded somehow. – TLama Nov 29 '12 at 02:53
-
There's also the [`IMAP SEARCH`](https://developers.google.com/google-apps/gmail/imap_extensions#extension_of_the_search_command_x-gm-raw) extension which allows the [`extended search`](http://support.google.com/mail/bin/answer.py?hl=en&answer=7190) just like the web interface does. Remy implemented the Gmail IMAP extensions to Indy few moments ago, so there are new Gmail specific search keys (search key for Gmail search is `skGmailRaw`), but as the `skBody`, the search term must be encoded somehow. I'll update the post if Remy won't be faster by posting own answer. – TLama Nov 29 '12 at 02:58
-
can the message be retireved in MsgObject.Body.Text; ? :) – Nov 29 '12 at 12:39
-
@Joe, I've added a quick implementation of the UTF-8 search (inspired mainly by [`this post`](http://stackoverflow.com/q/7426661/960757)). – TLama Dec 09 '12 at 06:18
-
FYI, for anyone finding this years later, the `TIdIMAP4.(UID)SearchMailBox()` methods support searching for non-ASCII text. They have an optional `ACharset` parameter which defaults to UTF-8, and also UTF-8 is used internally regardless of `ACharset` if the server claims to support UTF-8 quoted strings. This functionality was added to `TIdIMAP4` way back in late Dec 2012 just a few weeks after TLama posted their "quick implementation" update above. – Remy Lebeau Aug 10 '23 at 17:12