How to search IMAP in .NET

One of many advantages IMAP protocol has over POP3 protocol is the ability to search.

There are several ways of performing search using Mail.dll .NET IMAP library:

  • Search(Flag) – for common flag searches (seen, unseen, …).
  • SimpleImapQuery – easy one object query, where all conditions are joined with an AND operator.
  • Expression syntax – most advanced, allows using AND and OR operators and sorting.

IMAP search is done entirely on the server side, which means that it’s fast and doesn’t require Mail.dll client to download much data over the network.

Installation

The easiest way to install Mail.dll IMAP client for .NET is to download it from nuget via Package Manager:

PM> Install-Package Mail.dll

Alternatively you can download Mail.dll directly from our website.

Connect to IMAP

Let’s start with the basics: we’ll connect to the IMAP server in .NET app and authenticate:

// C# code:

using (Imap imap = new Imap())
{
    imap.ConnectSSL("imap.example.com");
    imap.UseBestLogin("user", "password");
    imap.SelectInbox();

    // Search code goes here

    imap.Close();
}
' VB.NET code:

Using imap As New Imap()
    imap.ConnectSSL("imap.example.com")
    imap.UseBestLogin("user", "password")
    imap.SelectInbox()

    ' Search code here

    imap.Close()
End Using

When using Gmail you can use application passwords, with Office 365 you’ll need to use OAuth 2.0 to authenticate.

Mail.dll fully supports OAuth 2.0 for authentication, you can find OAuth2 samples for Office365 and Gmail on the Mail.dll samples page.

Search(Flag)

Now, let’s look at the search code. The first and the simplest way to search is by using Imap.Search(Flag) method. This sample below finds all unseen email messages (those that have \UNSEEN flag).

// C#

List<long> uidList = imap.Search(Flag.Unseen);

// now download each message and get the subject
foreach (long uid in uidList)
{
    IMail message = new MailBuilder()
        .CreateFromEml(imap.GetMessageByUID(uid));

    string subject = message.Subject;
}
' VB.NET

Dim uidList As List(Of Long) = imap.Search(Flag.Unseen)

' now download each message and get the subject
For Each uid As Long In uidList
	Dim message As IMail = New MailBuilder()_
		.CreateFromEml(imap.GetMessageByUID(uid))

	Dim subject As String = message.Subject
Next

SimpleImapQuery

Second approach is to use an IMAP query object. The sample below searches for all unseen emails with a certain subject. All non-null SimpleImapQuery properties are combined using and operator.

// 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, the most advanced search option using Expression class. You can use AND, OR and NOT operators in your IMAP search.

Note that Mail.dll fully encapsulates IMAP search syntax, with easy to use and very readable .NET classes:

// 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 (defined in RFC 5256).

This feature is not available for every IMAP server: you need to check if your IMAP server supports ImapExtension.Sort extension first.

// 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 .NET client, 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 our unit tests.

Suggested reading

If you like it just give it a try and download it at: Mail.dll .NET IMAP component


Get Mail.dll

Tags:    

Questions?

Consider using our Q&A forum for asking questions.

36 Responses to “How to search IMAP in .NET”

  1. Gene Says:

    Can you search by message id? I see you have a header option to search by but not sure how to use it.

  2. Limilabs support Says:

    Yes, you can search by message-id header:

    List<long> uids =  imap.Search(
         Expression.Header("Message-ID", "<message@id.com>"));
    
  3. Gene Says:

    Worked perfect!!

  4. Peter Gordon Says:

    Can you search the Body? I tried Expression.Body(text to search for) but I get an unhandled error.

  5. Limilabs support Says:

    @Peter
    Yes, you can search the body:

    List<long> 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?

  6. Peter Gordon Says:

    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.

  7. Limilabs support Says:

    @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.

  8. Peter Gordon Says:

    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. […]

  9. Limilabs support Says:

    @Peter
    Unfortunatelly this is a Gmail bug. There is nothing we can do about it.
    Gmail has very similar problem with national characters.

  10. Peter Gordon Says:

    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.

  11. Limilabs support Says:

    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 correctly in Gmail searches

  12. Ben Says:

    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?

  13. Limilabs support Says:

    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)

  14. Ben Says:

    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?

  15. Limilabs support Says:

    @Ben,

    You are correct.
    I totally forgot about Expression.Since and Expression.SentSince.

  16. Kris Says:

    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?

  17. Limilabs support Says:

    @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.

  18. Knut Bøhn Says:

    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

  19. Limilabs support Says:

    @Knut

    Yes, it should work exactly like you put it (I just applied some formatting).

  20. Knut Bøhn Says:

    Guess I’m getting lost in all the brackets… – I need to put an Expression.Or first?

  21. Limilabs support Says:

    @Knut,

    The expression you provided works exactlly like you want:
    sentsince>2011/09/01 && (from == “a.com” || from == “b.com”)

  22. Gonzo Says:

    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?

  23. Limilabs support Says:

    @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?

  24. Louie Says:

    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

  25. Limilabs support Says:

    @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:

    List<long> uids = client.Search().Where(Expression.UID(Range.From(55)));
    

    To search uids in specified range (from 55 included to 135 included):

    List<long> uids = client.Search().Where(Expression.UID(Range.Create(55, 135)));
    
  26. Louie Says:

    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).

  27. Limilabs support Says:

    @Louie,

    I’m afraid that this may be a server bug (the search is done entirely on the IMAP server side).

  28. Hasitha Says:

    Hi, there is a small problem i am facing..i am using your fantastic product to retrieve from mails from ms outlook. when I use,

    List uids = imap.Search(Flag.New);

    i can access new recieved mails..but if i just open outlook and try that command, it returns 0, also, i tried seen, unseen and All also..but returns 0 but i can see unread n read mails in my inbox.. why is this?? please help..

  29. Limilabs support Says:

    @Hasitha

    Search is done always on the server side. Exchange can decide that a message is no longer \NEW or \UNSEEN when it has been downloaded by Outlook.
    You can remember the last uid you have downloaded to be 100% sure you get all new messages.
    It is a good solution in those cases when multiple users/programs access single mailbox/folder.

  30. Hasitha Says:

    okey, thank you very much for you help.. i will try that out 🙂

  31. Harold Garcia Says:

    I am using the SimpleImapQuery to filter the email From and To but how can I also limit the search for 5 days etc.

  32. Limilabs support Says:

    @Harold Garcia

    You can’t. Consider using searching using Expression syntax.
    Remember that you can use SimpleImapQuery object as part of your Expression query.

  33. Elena V Says:

    Search with subject search criteria containing Unicode fails with exception “Tried to read a line. No data received.”

  34. Limilabs support Says:

    @Elana V,

    Please make sure you are using the latest version (http://www.limilabs.com/static/mail/ReleaseNotes.txt).

    Mail.dll correctly escapes all unicode characters, but there are many IMAP servers out there, that act incorrectly and violate IMAP protocol. “Tried to read a line. No data received.” error means that the server suddenly disconnected or failed to send anything.

    What server are you using? Can you turn on logging (http://www.limilabs.com/blog/logging-in-mail-dll) and contact us directly (http://www.limilabs.com/support)?

  35. Elena V Says:

    Thanks for the fast reply, this helped a lot!
    I sent all requested stuff to your support (along with logs and tech details).
    The server is Yahoo, imap.mail.yahoo.com.

    Today I got
    [CLIENTBUG] UID SEARCH Command arguments invalid.
    for SUBJECT search with “письмо” literal (Cyrillic).
    This is exactly the same error which we got in our product before to try yours until the issue will be resolved. Seems Yahoo IMAP server bug for Unicode literals. ASCII works ok, this is only about Unicode literals.

    Seems Yahoo IMAP bug. Definitely this search worked in past.

    Again thanks!

  36. Limilabs support Says:

    @Elena V,

    This definitely Yahoo’s bug. Yahoo search doesn’t work with national (unicode) characters.

    As far as I remember, previously Yahoo disconnected when search phrase included unicode characters; now it sends BAD [CLIENTBUG] UID SEARCH Command arguments invalid”, which is ridiculous as this is server side problem.

    As a side note: it seems to me that they updated their server recently and now it fails to create/select folders with unicode characters.