4

I would like to get the latest 100 UIDs from the inbox using MailKit. I am accessing a Gmail mailbox which does not appear to support the SORT extension so I am unable to use OrderBy.

Here is my code. The problem is that it appears to retrieve the oldest 100 emails rather the latest ones (which is how I would expect it to work). Is there a way to do this?

Option A - looks promising only gets the 100 oldest emails UIDs and I want the 100 newest:

            imap.Inbox.Open(FolderAccess.ReadOnly);
            var orderBy = new [] { OrderBy.ReverseArrival };
            var items = imap.Inbox.Fetch(0, limit, MessageSummaryItems.UniqueId);

Option B - gets all UIDs by date order (but does not work on Gmail anyway):

            imap.Inbox.Open(FolderAccess.ReadOnly);
            var orderBy = new [] { OrderBy.ReverseArrival };
            SearchQuery query = SearchQuery.All;
            var items = imap.Inbox.Search(query, orderBy);

The IMAP server does not support the SORT extension.

The reason is to quickly scan the mailbox in order to improve responsiveness to user.

jstedfast
  • 35,744
  • 5
  • 97
  • 110
mike nelson
  • 21,218
  • 14
  • 66
  • 75

2 Answers2

15

You were pretty close in Option A, you just used the wrong values for the first 2 arguments.

This is what you want:

imap.Inbox.Open (FolderAccess.ReadOnly);
if (imap.Inbox.Count > 0) {
    // fetch the UIDs of the newest 100 messages
    int index = Math.Max (imap.Inbox.Count - 100, 0);
    var items = imap.Inbox.Fetch (index, -1, MessageSummaryItems.UniqueId);
    ...
}

The way that IMailFolder.Fetch (int, int, MessageSummaryItems) works is that the first int argument is the first message index and the second argument is the last message index in the range (-1 is a special case that just means "the last message in the folder").

Since once we open the folder, we can use the IMailFolder.Count property to get the total number of messages in the folder, we can use that to count backwards from the end to get our starting index. We want the last 100, so we can do folder.Count - 100. We use Math.Max() to make sure we don't get a negative value if the number of messages in the folder is less than 100.

Hope that helps.

jstedfast
  • 35,744
  • 5
  • 97
  • 110
  • Yes this is great, thanks so much. I then just reverse the results to get them in order by newest email first. – mike nelson Mar 18 '15 at 19:05
  • Yep! Forgot to mention that, but you obviously figured it out. :-) – jstedfast Mar 18 '15 at 22:11
  • Thanks jstedfast. By the way it seems to take about 5 seconds to retreive the UIDs whether I get only 1 or 500. All the time is in IMAP connect and authenticate. So get UIDs is very fast but not the login. Is that what you would expect in terms of speed? – mike nelson Mar 19 '15 at 06:00
  • 1
    That might all depend on the server. Opening a connection can take a while due to DNS lookups, network latency, server load, etc. Authentication might be purposely slowed to make brute force login attacks less efficient. Fetching UIDs is likely very fast because the server already has all that information cached. – jstedfast Mar 23 '15 at 13:04
  • Right. It was gmail, so probably the slowing down to prevent brute force attacks is most likely. Google like to encourage people to use OAuth. – mike nelson Mar 26 '15 at 06:05
  • Anyway to fetch UID instead? I intend to retrieve and delete X oldest message. I see another code that says UID is better than ordinal index for this case. – iroel Mar 31 '20 at 13:40
  • Every API in MailKit that can take an index can also take a UID. – jstedfast Mar 31 '20 at 13:42
3

If you want to download each Message individually, you can do something simple like this.

for (int i = inbox.Count-1; i > inbox.Count-101; i--)
{
    var message = inbox.GetMessage(i);
    Console.WriteLine($"Subject: {message.Subject}");
}

If you would like to receive it all in one request, try this.

var lastHundredMessages = Enumerable.Range(inbox.Count - 100, 100).ToList();
var messages = inbox.Fetch(lastHundredMessages, MailKit.MessageSummaryItems.UniqueId);
foreach (var message in messages)
{
    //To something here with this
}
Yossi Sternlicht
  • 740
  • 1
  • 6
  • 17
  • That's about a hundred times as slow since it issues a hundred single-message requests instead of one hundred-message request, and it downloads every attachment in order to top print the subject. – arnt Mar 09 '21 at 11:50
  • You are right. I am gonna update my answer and let me know what you think – Yossi Sternlicht Mar 09 '21 at 12:07