IMAP IDLE: instant push email notifications

IDLE is an IMAP protocol extension described in RFC 2177. It allows an IMAP client to indicate to the server that it is ready to accept real-time notifications about changes (e.g new message) in the currently selected folder.

This feature is not available for every IMAP server. You need to check if your IMAP server supports ImapExtension.Idle extension.

Mail.dll IMAP client supports IDLE command.

There are two Imap class methods, that allow client to receive notifications:

  • Idle() – starts accepting real-time notifications. The method hangs until new notification is received.
  • StopIdle() – stops accepting real-time notifications.

The following sample connects to IMAP server and starts to receive notifications. When new message arrives (or an old message is deleted) it displays new message count. Then searches IMAP server for unseen messages, downloads them, and displays subjects of all new messages.

using (Imap client = new Imap())
{
    client.ConnectSSL("imap.server.com");
    client.Login("user@server.com", "password");

    FolderStatus folderStatus = client.SelectInbox();
    Console.WriteLine("Total message count: {0}",
        folderStatus.MessageCount);

    while(true)
    {
        FolderStatus currentStatus = client.Idle();
        Console.WriteLine("Total message count: {0}",
                currentStatus.MessageCount);
        foreach(long uid in client.Search(Flag.Unseen))
        {
            IMail email = new MailBuilder().CreateFromEml(
                client.GetHeadersByUID(uid));
            Console.WriteLine(email.Subject);
        }
    }
    client.Close();
}
' VB.NET code

Using client As New Imap()
	client.ConnectSSL("imap.server.com")
	client.Login("user@server.com", "password")

	Dim folderStatus As FolderStatus = client.SelectInbox()
	Console.WriteLine("Total message count: {0}",_
		folderStatus.MessageCount)

	While True
		Dim currentStatus As FolderStatus = client.Idle()
		Console.WriteLine("Total message count: {0}",_
			currentStatus.MessageCount)
		For Each uid As Long In client.Search(Flag.Unseen)
			Dim email As IMail = New MailBuilder()_
 				.CreateFromEml(client.GetHeadersByUID(uid))
			Console.WriteLine(email.Subject)
		Next
	End While
	client.Close()
End Using

In this next sample we’ll handle stop gracefully. As you can see StopIdle method is thread safe. This means that it can be used from any other thread (for example UI thread).

// C# code

using (Imap client = new Imap())
{
    client.ConnectSSL("imap.server.com");
    client.Login("user@server.com", "password");

    FolderStatus folderStatus = client.SelectInbox();
    Console.WriteLine("Total message count: {0}",
        currentStatus.MessageCount);

    bool stop = false;
    // We start a new thread to handle user input, enter = stop idle
    new Thread(() =>
	{
		Console.ReadLine();
		client.StopIdle();
		stop = true;
	}).Start();

    while(stop == false)
    {
        FolderStatus currentStatus = client.Idle();
        if (stop == true)
            break;

        Console.WriteLine("Total message count: {0}",
            currentStatus.MessageCount);

        foreach(long uid in client.Search(Flag.Unseen))
        {
            IMail email = new MailBuilder().CreateFromEml(
                client.GetHeadersByUID(uid));
            Console.WriteLine(email.Subject);
        }
    }
    client.Close();
}
' VB.NET code

Using client As New Imap()
	client.ConnectSSL("imap.server.com")
	client.Login("user@server.com", "password")

	Dim folderStatus As FolderStatus = client.SelectInbox()
	Console.WriteLine("Total message count: {0}",_
		currentStatus.MessageCount)

	Dim [stop] As Boolean = False
	' We start a new thread to handle user input, enter = stop idle
	New Thread(Function() Do
		Console.ReadLine()
		client.StopIdle()
		[stop] = True
	End Function).Start()

	While [stop] = False
		Dim currentStatus As FolderStatus = client.Idle()
		If [stop] = True Then
			Exit While
		End If

		Console.WriteLine("Total message count: {0}",_
			currentStatus.MessageCount)

		For Each uid As Long In client.Search(Flag.Unseen)
			Dim email As IMail = New MailBuilder()_
				.CreateFromEml(client.GetHeadersByUID(uid))
			Console.WriteLine(email.Subject)
		Next
	End While
	client.Close()
End Using

IDLE command leaves TCP/IP connection unused for a long time. In some cases it may cause routers to decide that this connection is no longer needed. By default Mail.dll reissues IDLE command automatically every 29 minutes. You can shorten this time by using Idle(TimeSpan).

Tags: , , , ,

6 Responses to “IMAP IDLE: instant push email notifications”

  1. Scott Says:

    Is there a command where client.SelectInbox() can be replaced so that you can get the all mail folder instead? I figure that would be better so that only one IDLE connection is required for an account. Is there a way to find out which email goes with which folder in this case?

  2. Limilabs support Says:

    @Scott

    You can use any folder name:

    client.Select("All Mail")
    

    In case of Gmail you may want to use CommonFolders class to get the All Mail folder name:
    http://www.limilabs.com/blog/localized-gmail-imap-folders

    > Is there a way to find out which email goes with which folder in this case?
    In case of Gmail you can get all labels for specified message:
    http://www.limilabs.com/blog/get-gmail-labels-for-specified-messages

  3. Download emails from Gmail via POP3 Says:

    [...] use push email [...]

  4. saqib Says:

    hello

    sir,when i access email through email.idle notification,i want also mark the email to read
    can you tell me how can i do this

  5. Limilabs support Says:

    @Saqib,

    If you download the message (GetMessageByUID) usually server marks it as seen automatically.

    You can also use MarkMessageSeenByUID method:
    http://www.limilabs.com/blog/mark-emails-as-read-with-imap

    Take a look at all Mail.dll samples, most likely you’ll find the code you need there.

  6. Gmail’s POP3 behavior | Blog | Limilabs Says:

    [...] use push email [...]

Leave a Reply