Get new emails using IMAP
This article describes how to receive new email messages using Mail.dll .NET IMAP library.
In many situations you can relay on IMAP server to keep track which messages are unseen and just download unseen messages from IMAP server. However when user interaction or other program connecting and downloading emails may remove UNSEEN flag from the message, you may be forced to manually track which messages where already downloaded and which are new.
You could of course download all Unique IDs (UIDs) of the messages that are in the mailbox, but there is a more elegant way.
Unique IDs (UIDs) in IMAP have some very interesting feature – they are assigned in incremental order. This means that new messages always have higher UIDs than old ones.
This is the reason you only need to remember the newest (greatest) UID that was downloaded during a previous session (connection to the IMAP server). On the next session you need to search IMAP for UIDs that are greater than the one remembered.
First we need to load largest uid from the previous session and connect to the IMAP server. In this exmaple we select INBOX, but you can use Select method to select a different folder.
// C# long? largestUID = LoadLargestFromPreviousRun(); using (Imap imap = new Imap ()) { imap.Connect("imap.example.com"); // or ConnectSSL for SSL imap.UseBestLogin("user", "password"); imap.SelectInbox();
' VB.NET Dim largestUID As System.Nullable(Of Long) = LoadLargestFromPreviousRun() Using imap As New Imap () imap.Connect("imap.example.com") ' or ConnectSSL for SSL imap.UseBestLogin("user", "password") imap.SelectInbox()
Next step is to select folder, download unique ids (UIDs) larger than largestUID value that was stored on the previous run. This way we obtain uids of the new emails.
// C# List<long> uids; if (largestUID == null) { uids = imap.GetAll(); } else { uids = imap.Search().Where( Expression.UID(Range.From(largestUID.Value))); uids.Remove(largestUID); }
' VB.NET Dim uids As List(Of Long) If largestUID Is Nothing Then uids = imap.GetAll() Else uids = imap.Search().Where(Expression.UID(Range.From(largestUID.Value))) uids.Remove(largestUID) End If
You should note uids.Remove in the code above. It is used because Range.From creates a range of uids greater or equal to the specified value and * represents the largest value in the mailbox, so the largest uid is always included.
Finally we’ll iterate through the UID list and use GetMessageByUID method to download each message from the server. You can use MailBuilder class to parse those email messages, extract attachments and process them anyway you like. The last step is to save the last UID that was processed:
// C# foreach (long uid in uids) { var eml = imap.GetMessageByUID(uid); IMail email = new MailBuilder().CreateFromEml(eml); Console.WriteLine(email.Subject); Console.WriteLine(email.Text); SaveLargestUID(uid.Value); }
' VB.NET For Each uid As Long In uids Dim eml = imap.GetMessageByUID(uid) Dim email As IMail = New MailBuilder().CreateFromEml(eml) Console.WriteLine(email.Subject) Console.WriteLine(email.Text) SaveLargestUID(uid.Value) Next
SaveLargestUID, LoadLargest… methods
You need to write those methods on your own. They should save/load largest uid from/to your preferred storage (such as database, file, registry).
// C# private void SaveLargestUID(long uid) { // Your code that saves the uid. } private long LoadLargestFromPreviousRun() { // Your code that loads the largest uid (null on the first run). }
' VB.NET Private Sub SaveLargestUID(uid As Long) ' Your code that saves the uid. End Sub Private Function LoadLargestFromPreviousRun() As Long ' Your code that loads the largest uid (null on the first run). End Sub
UIDs across sessions – UIDValidity
It is advised for IMAP servers to not change UIDs between sessions. There are however situations when server may invalidate all UIDs that were in the mailbox previously.
Consider a user that deletes a folder (mailbox) including all its messages and creates new one with exactly the same name. All uids that were downloaded previously are no longer valid – they simply don’t exist in this folder anymore.
You can recognize such situation by comparing FolderStatus.UIDValidity value. If the FolderStatus.UIDValidity number hasn’t changed, then the UIDs are still valid. Below is the sample showing how to print UIDValidity:
// C# FolderStatus status = client.SelectInbox(); Console.WriteLine(status.UIDValidity);
' VB.NET Dim status As FolderStatus = client.SelectInbox() Console.WriteLine(status.UIDValidity)
Entire samples
Below you can find full samples in both C# and VB.NET:
// C# using Limilabs.Mail; using Limilabs.Client.IMAP; internal class LastRun { public long UIDValidity { get; set; } public long LargestUID { get; set; } } private static void Main(string[] args) { LastRun last = LoadPreviousRun(); using (Imap imap = new Imap()) { imap.Connect("imap.example.com"); // or ConnectSSL for SSL imap.UseBestLogin("user", "password"); FolderStatus status = imap.SelectInbox(); List<long> uids; if (last == null || last.UIDValidity != status.UIDValidity) { uids = imap.GetAll(); } else { uids = imap.Search().Where( Expression.UID(Range.From(last.LargestUID))); uids.Remove(last.LargestUID); } foreach (long uid in uids) { var eml = imap.GetMessageByUID(uid); IMail email = new MailBuilder() .CreateFromEml(eml); Console.WriteLine(email.Subject); Console.WriteLine(email.Text); LastRun current = new LastRun { UIDValidity = status.UIDValidity, LargestUID = uid }; SaveThisRun(current); } imap.Close(); } }
' VB.NET Imports Limilabs.Mail Imports Limilabs.Client.IMAP Friend Class LastRun Public Property UIDValidity () As Long Get Return m_UIDValidity End Get Set m_UIDValidity = Value End Set End Property Private m_UIDValidity As Long Public Property LargestUID() As Long Get Return m_LargestUID End Get Set m_LargestUID = Value End Set End Property Private m_LargestUID As Long End Class Private Shared Sub Main(args As String()) Dim last As LastRun = LoadPreviousRun() Using imap As New Imap() imap.Connect("imap.example.com") ' or ConnectSSL for SSL imap.UseBestLogin("user", "password") Dim status As FolderStatus = imap.SelectInbox() If last Is Nothing OrElse last.UIDValidity <> status.UIDValidity Then uids = imap.GetAll() Else uids = imap.Search().Where(Expression.UID(Range.From(last.LargestUID))) uids.Remove(last.LargestUID) End If For Each uid As Long In uids Dim eml = imap.GetMessageByUID(uid) Dim email As IMail = New MailBuilder().CreateFromEml(eml) Console.WriteLine(email.Subject) Console.WriteLine(email.Text) Dim current As New LastRun() With { _ .UIDValidity = status.UIDValidity, _ .LargestUID = uid _ } SaveThisRun(current) Next imap.Close() End Using End Sub
SaveThisRun, LoadPreviousRun methods
You need to write those methods on your own. They should save/load last run data from/to your preferred storage (such as database, file, registry).
// C# private void SaveThisRun(LastRun run) { // Your code that saves run data. } private LastRun LoadPreviousRun() { // Your code that loads last run data (null on the first run). }
' VB.NET Private Sub SaveThisRun(uid As LastRun ) ' Your code that saves run data. End Sub Private Function LoadPreviousRun() As LastRun ' Your code that loads last run data (null on the first run). End Function
August 22nd, 2016 at 23:09
is there a way to know the value of higher uid a folder?
without traversing all uid and the first execution
August 23rd, 2016 at 08:40
@Matias,
To be 100% sure you need to invoke GetAll method and take the last number from the returned list.
October 17th, 2016 at 06:45
How to Get new emails using IMAP with Lesnikowski?
Is there a way like in Limilabs?
October 17th, 2016 at 07:10
@Vijay
Why don’t you want to use the latest version?
You can download it at:
http://www.limilabs.com/mail/download
April 3rd, 2017 at 08:42
How can I get the function LoadLargestFromPreviousRun()?
April 3rd, 2017 at 09:23
@net123
You need to write this method.
This method should load the largest uid from your preferred storage (file, database), that was saved, during previous application run.
You should save this uid using SaveLargestUID method – also created by you.
April 3rd, 2017 at 13:02
can u provide this methods?
April 3rd, 2017 at 13:15
@net123,
No, we can’t, it would be impossible to provide implementations for every possible scenario.
Those methods depend on your infrastructure, database type, version, and what you want to do exactly. Maybe you don’t even require a database. You need to create those methods yourself.
September 12th, 2017 at 12:51
Hi,
We use following code:
uids = imap.Search().Where(Expression.UID(Range.From(last.LargestUID + 1)
When there are no new mails, this expression returns uids={ LargestUID }, and not null? Why?
September 12th, 2017 at 18:40
@Nick,
This query should return empty list (not null), when there are no new emails. Please make sure that ‘+1’ is actually there (off-by-one errors are quite common).
If the problem persists, I’m afraid this may be the server side problem, and the only way would be to create a simple workaround in your code.
November 11th, 2017 at 12:41
Is there any way to select the top 100 emails for example. I’m having to load in thousands of unread emails and it’s very slow at the moment
November 11th, 2017 at 14:27
@ML,
Search(Flag.Unseen) itself is very fast, it just returns UID list (list of longs).
This list is sorted from oldest to newest. You can use Linq to get first, last, or any 100 uids you plan to download.
The other option would be to download only most common email properties (from, to, subject, attachment count and names).
December 5th, 2017 at 16:30
I try to run that code. But I have problem: when I run the code I always get the last mail from my inbox.
December 5th, 2017 at 19:26
@Semir
Are you sure you are adding +1 (
last.LargestUID + 1
) when you are searching?If so, than most likely your server is performing search incorrectly.
You can simply remove LargestUID from the search results:
December 6th, 2017 at 08:22
Yes I’m adding 1 (last.LargestUID + 1) when I search. Removing the largerst UID may cause a problem (that not see the last mail) with different mail servers.
December 6th, 2017 at 11:14
@Semir
Remove last.LargestUID not the largest UID. This will not cause any problems as last.LargestUID was already processed last time you run the code.
December 6th, 2017 at 11:37
okey i got it 🙂 thank you for your interest
April 8th, 2018 at 16:12
I want to get latest 40 emails from my Inbox folder descending order by Date or may be uid. Kindly let me know how can I get this.
When I am using Flag.All and foreach (long uid in uids.Take(40)). I am getting 40 emails, but all emails are very old. Not the latest emails.
Kindly redirect me to some C# code sample.
April 9th, 2018 at 12:17
@Rashmi Ranjan,
Use Reverse: