Getting instant notifications on new emails with IMAP IDLE

If your application uses IMAP and it needs to know when a new message arrives to mailbox, there are two approaches which let you do that.

One of the methods is known as polling, your application connects to mail server every once in a while and checks for new messages. This is how most web-based email applications work, they can actually sync the list of messages between IMAP server and their database, or simply check for recent messages, IMAP protocol has built-in support for that – you can either search for UNSEEN messages, which means those you didn’t download yet, or NEW messages which just arrived.

The other approach assumes that your application stays connected to the mail server, and once a new message arrives, the mail server would immediately notify your application. This approach is made available by IMAP IDLE extension, it’s supported by the majority of mail servers out there, and as long as your application can maintain the persistent connection to the mail server, you should be able to use IMAP IDLE. Note that while this method can’t really be used with web applications which must return the response as fast as possible and terminate, it’s fairly simple to utilize it for desktop or server apps which can afford long-lived connections.

MailBee.NET Objects, email library for .NET platform, supports both the methods. To locate mails which just arrived, you simply run IMAP search for NEW messages:

Imap imp = new Imap();
imp.Connect("mail.domain.com");
imp.Login("john.doe@company.com", "secret");
imp.SelectFolder("Inbox");
UidCollection uids = (UidCollection)imp.Search(true, "NEW", null);
if (uids.Count > 0)
{
    MailMessageCollection msgs = imp.DownloadEntireMessages(uids.ToString(), true);
    foreach (MailMessage msg in msgs)
    {
        Console.WriteLine("Message #" + msg.IndexOnServer.ToString() +
            " has " + msg.Attachments.Count + " attachment(s)");
    }
}
else
{
    Console.WriteLine("No new messages");
}
imp.Disconnect();

As for the second approach, documentation for MailBee.NET Objects includes a tutorial on using IMAP IDLE, with two samples, one of them is designed for .NET 4.5 and uses async/await methods.

Below is a very simple example that demonstrates the idea. To run this sample, put button1 button on the form and attach Form1_Closing method to Closing event of the form.

// To use the code below, import MailBee namespaces at the top of your code
using MailBee;
using MailBee.ImapMail;

// Put the code below inside your class.

// Imap object declared global because it's accessed from several methods.
Imap imp = null;

bool finished = false;
bool started = false;

// Idling event is used to keep UI responsive and stop idling is desired.
private void imp_Idling(object sender, ImapIdlingEventArgs e)
{
    Application.DoEvents();
    if (finished)
    {
        ((Imap)sender).StopIdle();
        button1.Text = "Go idle";
    }
}

// Monitor folder changes and save them in the log file.
private void imp_MessageStatus(object sender, ImapMessageStatusEventArgs e)
{
    ((Imap)sender).Log.WriteLine("Got " + e.StatusID + " status update");
}

// Start/stop idling.
// Add button1 on the form to make this sample working.
private void button1_Click(object sender, System.EventArgs e)
{
    if (started)
    {
        finished = true;
    }
    else
    {
        started = true;

        imp = new Imap();

        // Enable logging into a file.
        imp.Log.Filename = @"C:\Temp\log.txt";
        imp.Log.Enabled = true;
        imp.Log.Clear();

        // Connect to the server and check if IDLE is supported.
        imp.Connect("mail.domain.com");
        if (imp.GetExtension("IDLE") == null)
        {
            MessageBox.Show("IDLE not supported");
        }
        else
        {
            // Login and select inbox.
            imp.Login("jdoe", "secret");
            imp.SelectFolder("Inbox");

            // Attach event handlers.
            imp.Idling += new ImapIdlingEventHandler(imp_Idling);
            imp.MessageStatus +=new ImapMessageStatusEventHandler(imp_MessageStatus);

            button1.Text = "Stop idle";

            // Go idle. This call will block until imp.StopIdle()
            // is called from elsewhere.
            imp.Idle();
        }

        // Disconnect from the server.
        imp.Disconnect();

        started = false;
        finished = false;
    }
}

// Finish idling if the user closes the application.
// To make this method work, attach it to Closing event of the form.
private void Form1_Closing(object sender,
    System.ComponentModel.CancelEventArgs e)
{
    if (imp != null && imp.IsBusy)
    {
        imp.StopIdle();
    }
}

The sample takes care of checking if IDLE is supported by the server, stopping idling if requested or if the application is closed by the user. 

The tricky part about using IDLE is that the single IDLE session cannot be longer than 30 minutes, so your application will need to reconnect to server periodically.

From my experience, the interval of 15 minutes is sufficient for most servers. But I’ve recently found out that, for example, GoDaddy IMAP server terminates IDLE session after 2 minutes, and MailBee logs show this:

[05/05/2021 16:48:38.64] [INFO] Will go idle.
[05/05/2021 16:49:07.07] [SEND] MBN00000008 IDLE\r\n
[05/05/2021 16:49:08.10] [RECV] + idling\r\n [Total 10 bytes received.]
[05/05/2021 16:51:08.20] [INFO] Error: Socket connection was aborted by remote host.
[05/05/2021 16:51:08.20] [INFO] Will disconnect from host "imap.secureserver.net".
[05/05/2021 16:51:08.21] [INFO] Disconnected from host "imap.secureserver.net".
[05/05/2021 16:51:08.23] [INFO] Error: Not yet connected to the server. Call Connect first.

So if you encounter a similar issue, it would make sense to try restarting IDLE every 60-90 seconds rather than every 15-25 minutes for a typical scenario.

Getting instant notifications on new emails with IMAP IDLE

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s