How to search IMAP in .NET

Recently Mail.dll email component got new IMAP client.

I must say that I really enjoyed implementing IMAP search.

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

Let’s start with the basics: we’ll connect to the IMAP server and login:

// C# code:

using (Imap imap = new Imap())
{
    imap.Connect("server");
    imap.Login("user", "password");
    imap.SelectInbox();

    // Search code goes here

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

Using imap As New Imap()
    imap.Connect("server")
    imap.Login("user", "password")
    imap.SelectInbox()

    ' Search code here

    imap.Close()
End Using

Now the first and the simplest way using Imap.SearchFlag method:

// C# code:

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

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

Second approach is to use the IMAP query object.
We will search all unseen emails with certain subject:

// C# code:

SimpleImapQuery query = new SimpleImapQuery();
query.Subject = "subject to search";
query.Unseen = true;

List<long> uids = imap.Search(query);
' VB.NET code:

Dim query As New SimpleImapQuery()
query.Subject = "subject to search"
query.Unseen = True

Dim uids As List(Of Long) = imap.Search(query)

Finally most advanced search option using Expression class.
You can use And, Or and Not operators in your IMAP search:

// C# code:

List<long> uids = imap.Search(
    Expression.And(
        Expression.Not(Expression.Subject("subject not to search")),
        Expression.HasFlag(Flag.Unseen)));
' VB.NET code:

Dim uids As List(Of Long) = imap.Search(_
    Expression.[And](_
        Expression.[Not](Expression.Subject("subject not to search")),_
        Expression.HasFlag(Flag.Unseen)))

Notice the neat trick in Mail.dll that allows casting FluentSearch class
received from imap.Search(…) to List

public static implicit operator List<long>(FluentSearch search)
{
        return search.GetList();
}

I 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 email component

Tags: , , ,

27 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. Pawel Lesnikowski 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. Pawel Lesnikowski Says:

    @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.lesnikowski.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. Pawel Lesnikowski 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. Pawel Lesnikowski 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. Pawel Lesnikowski 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 correct 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. Pawel Lesnikowski 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.lesnikowski.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. Pawel Lesnikowski 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. Pawel Lesnikowski 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. Pawel Lesnikowski 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. Pawel Lesnikowski 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. Pawel Lesnikowski 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. Pawel Lesnikowski 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. Pawel Lesnikowski Says:

    @Louie,

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

Leave a Reply