How to search IMAP in .NET
One of many advantages IMAP protocol has over POP3 protocol is the ability to search. IMAP search is done entirely on server side, which means that it’s fast and does not require Mail.dll to download much data over the network.
There are several ways of performing search using Mail.dll IMAP library:
- SearchFlag – for common flag searches (seen, unseen, …).
- SimpleImapQuery – easy one object query, where all conditions are joined with AND operator.
- Expression syntax – most advanced, allows using AND and OR operators and sorting.
Let’s start with the basics: we’ll connect to the IMAP server and login:
// C# code:
using (Imap imap = new Imap())
{
imap.Connect("imap.example.com"); // or ConnectSSL for SSL
imap.Login("user", "password");
imap.SelectInbox();
// Search code goes here
imap.Close();
}
' VB.NET code:
Using imap As New Imap()
imap.Connect("imap.example.com") ' or ConnectSSL for SSL
imap.Login("user", "password")
imap.SelectInbox()
' Search code here
imap.Close()
End Using
SearchFlag
Now the first and the simplest way using Imap.SearchFlag method. The sample finds all unseen email messages (those that have \UNSEEN flag).
// C#
List<long> uidList = imap.SearchFlag(Flag.Unseen);
// now download each message and display the subject
foreach (long uid in uidList)
{
IMail message = new MailBuilder()
.CreateFromEml(imap.GetMessageByUID(uid));
Console.WriteLine(message.Subject);
}
' VB.NET Dim uidList As List(Of Long) = imap.SearchFlag(Flag.Unseen) ' now download each message and display the subject For Each uid As Long In uidList Dim message As IMail = New MailBuilder()_ .CreateFromEml(imap.GetMessageByUID(uid)) Console.WriteLine(message.Subject) Next
SimpleImapQuery
Second approach is to use the IMAP query object. The sample below searches for all unseen emails with certain subject.
// C# SimpleImapQuery query = new SimpleImapQuery(); query.Subject = "subject to search"; query.Unseen = true; List<long> uids = imap.Search(query);
' VB.NET Dim query As New SimpleImapQuery() query.Subject = "subject to search" query.Unseen = True Dim uids As List(Of Long) = imap.Search(query)
Expression syntax
Finally most advanced search option using Expression class. You can use AND, OR and NOT operators in your IMAP search:
// C#
List<long> uids = imap.Search().Where(
Expression.And(
Expression.Not(Expression.Subject("subject not to search")),
Expression.HasFlag(Flag.Unseen)));
' VB.NET
Dim uids As List(Of Long) = imap.Search().Where(_
Expression.[And](_
Expression.[Not](Expression.Subject("subject not to search")),_
Expression.HasFlag(Flag.Unseen)))
Expression syntax with sorting
Expression search syntax also allows sorting. This feature is not available for every IMAP server. You need to check if your IMAP server supports ImapExtension.Sort extension.
// C#
List<long> uids = imap.Search()
.Where(Expression.And(
Expression.Not(Expression.Subject("subject not to search")),
Expression.HasFlag(Flag.Unseen)))
.Sort(SortBy.Multiple(
SortBy.Date(),
SortBy.Subject()));
' VB.NET
Dim uids As List(Of Long) = imap.Search() _
.Where(Expression.[And]( _
Expression.[Not](Expression.Subject("subject not to search")), _
Expression.HasFlag(Flag.Unseen))) _
.Sort(SortBy.Multiple( _
SortBy.[Date](), _
SortBy.Subject()))
)
Notice the neat trick in Mail.dll that allows casting FluentSearch class received from imap.Search() method to List<longs>
public static implicit operator List<long>(FluentSearch search)
{
return search.GetList();
}
We tend to use it very often for builder objects used in unit testing.
If you like it just give it a try and download it at: Mail.dll .NET IMAP component
March 30th, 2010 at 14:29
Can you search by message id? I see you have a header option to search by but not sure how to use it.
March 30th, 2010 at 15:31
Yes, you can search by message-id header:
List<long> uids = imap.Search( Expression.Header("Message-ID", "<message@id.com>"));March 30th, 2010 at 17:52
Worked perfect!!
February 9th, 2011 at 00:24
Can you search the Body? I tried Expression.Body(text to search for) but I get an unhandled error.
February 9th, 2011 at 01:13
@Peter
Yes, you can search the body:
List uids = imap.Search(Expression.Body(“19812345″));
What kind of exception are you getting?
What is the message?
What is the stack trace?
Can you prepare a log file (http://www.limilabs.com/blog/logging-in-mail-dll)?
What server are you using?
Are you using any special characters?
February 9th, 2011 at 01:20
I am using Windows Server 2008 R2, IIS 7.5 with .Net 4.0 (c#).
I can prepare a log file tomorrow morning – the only message I got was an unhandled error as i did not have logging turned on.
I am passing a string with standard HTML formatting to search on so likely I do have some special characters in the string (it is pretty long). I can test with a simpler string in the morning as well.
February 9th, 2011 at 15:35
@Peter
You said you are using pretty long strings.
Most likely the problem is with CR LF characters.
Currently you can not use those characters in searches. Sorry.We’ll try to address this issue in next release.
February 9th, 2011 at 15:42
Good Morning,
It seems like the Expression.Body statement error’s out when the string for the search criteria includes an apostrophe. There may be other characters as well.
It work’s fine when I pass a short string with only alpha characters. [...]
February 9th, 2011 at 16:51
@Peter
Unfortunatelly this is a Gmail bug. There is nothing we can do about it.
Gmail has very similar problem with national characters.
February 9th, 2011 at 19:18
No worries Paul – I can work around this requirement (it is likely better not to search on the Body text anyway for the purposes that I was considering doing it).
Thanks for your help.
February 22nd, 2011 at 18:27
Mail.dll was updated today and now supports any characters in the search, including CR LF.
Mail.dll now uses a bit different IMAP server communication model and we have been able to workaround this Gmail bug. From now on national characters should work correct in Gmail searches
April 20th, 2011 at 21:56
I want to only retrieve a list of emails via IMAP in date range specified (based on the date the email was sent). I have done quite a lot of searching on this site and online and can’t find an example for how to do this. Does anyone have a sample to get me started or even point me to the method or class that I should be using to do this?
April 21st, 2011 at 12:19
Ben,
Unfortunately IMAP protocol does not have such feature.)What can you do is to use Imap.GetEnvelope or GetMessageInfo methods to download informations about all emails (it’s relatively fast) as described here:http://www.limilabs.com/blog/get-email-information-from-imap-fast
Envelope has a Date field that you can use (it corresponds to email’s Date header(See Ben comment below)
April 29th, 2011 at 23:35
Hmmm…
I spent a couple of days now looking at this and unfortunately, I believe you are incorrect. When I do this code with imap:
DateTime dateBegin = DateTime.Now.AddDays(-30);
List listOfUids = imap.Search(Expression.SentSince(dateBegin);
This works and returns back the correct number of Uid’s within that range and is still very quick.
Maybe I asked my question incorrectly?
May 1st, 2011 at 12:19
@Ben,
You are correct.
I totally forgot about Expression.Since and Expression.SentSince.
August 18th, 2011 at 16:49
I’m trying a filter criteria based on the Subject as well as From address with an Expression.OR condition and it always returns only 160 results…Is there a limitation with the trial version?
August 18th, 2011 at 20:11
@Kris
Most likley you are using Yahoo as your email provider.
It seems Yahoo is limiting the search result (for queries that search subject, body) to the newest 160 records.
It also seems that there is no way to get to the previous records using IMAP search.
It’s definitively Yahoo bug, and currently I don’t see a way to workaround it.
September 15th, 2011 at 14:24
Is it possible to nest multiple or expressions, to add more from-criteria to something like
List<long> uidList = imap.Search().Where( Expression.And( Expression.SentSince(DateTime.Parse("01.09.2011")), Expression.Or( Expression.From("a.com"), Expression.From("b.com") ) // or ) // and ); // where?
in other words: where sentsince>20110901 && (from ==”a.com” || from ==”b.com”|| from ==”c.com”) etc etc
Thanks,
knut
September 15th, 2011 at 20:54
@Knut
Yes, it should work exactly like you put it (I just applied some formatting).
September 16th, 2011 at 10:30
Guess I’m getting lost in all the brackets… – I need to put an Expression.Or first?
September 17th, 2011 at 08:54
Knut,
The expression you provided works exactlly like you want:
sentsince>2011/09/01 && (from == “a.com” || from == “b.com”)
November 6th, 2011 at 12:33
When I search for Expression.From(“first name last name”) – it finds the mail.
When I search for Expression.From(“name@domain.com”) – it does not find the mail.
So, it appears to only search the display name of the from text, not the from email address.
Is there any way to search for the from email address?
November 8th, 2011 at 14:54
@Gonzo
The search is done entirely on the server side.
If the server implements search functionality incorrectly there is not much we can do.
Could you prepare a log file for us, so we can make sure that Mail.dll generates correct search query?
December 2nd, 2011 at 07:53
hi,
I am a web developer who is trying to implement a web mail as a part of the project that i am working on. it requires synchronous the local cache and imap server. would you please let me know how would I search mailbox by uid? (i need to search uid greater than a certain value) so that i can only download the email from server which haven’t been downloaded yet.
many thanks
best regards
Louie
December 2nd, 2011 at 13:04
@Louie
You can use Expression.UID method.
It allows you to select a range of uids you are interested in.
To search uids greater that a certain value:
To search uids in specified range (from 55 included to 135 included):
December 5th, 2011 at 04:19
hi
I have just tried to client.Search().Where(Expression.UID(Range.From(100))); it still return me the email with biggest uid on server (25). however, when i use rang.creat(26,100), it give me the correct result (no emails found).
December 6th, 2011 at 21:13
@Louie,
I’m afraid that this may be a server bug (the search is done entirely on the IMAP server side).