This project is read-only.

Advanced IMAP Programming Reference

This section will provide answers to common question and introduce solutions to common problems.

It is recommended to read the Basic IMAP Programming Reference prior to this document since this section will assume knowledge about basic IMAP protocol capabilities.

Searching & Fetching using LINQ

Instructions for using LINQ to query the server can be found in the LINQ to IMAP Reference.

Fetching New Messages

Although new messages are flagged with the RECENT flag, is not wise to request new messages using this flag, especially when multiple clients share a common mailbox.

User 1 may have already loaded the message, thus removing its recent flag. User 2 will not receive the new message, since it is not new anymore from the viewpoint of the server.

The mechanism to get all the messages we don’t locally see or have is to store the UIDNEXT property which is sent by the server as a response to the SELECT command.

Since message uids are ascending sequentially any new mail will have a “higher” uid than the last. This way we can easily fetch “new” messages using this simple search criteria.

var uidNext = 256;
var query = client.Messages.Where(x => x.Uid >= uidNext).Select( ... );

Notifying the User about New Messages

If we need to inform the user about a new arrival it is obviously not a good idea to fetch the entire message on the spot.

Back in the olden days the message was split into two parts. At first a client fetched the message headers in order to display partial data from the associated message. Once the user agreed to view the message the rest was loaded, thus completing the file.

Today this approach is rarely used for it has several downsides. Modern approaches make use of envelopes, headers, body structures and single item retrievals making the entire process very agile and efficient.

On a desktop machine we should fetch the message Headers, along with the Size, BodyStructure and the InternalDate. This way we can detect meta headers, like spam markers, junk markers as well as customized headers and display those to the user.

In addition the user will know the size of the message as well as its layout. The body structure enables us to fetch all message items separately, we can display the message’s body without having to download its attachments. In addition it is recommended to fetch the internal date for the message since the regular date property is set by the sender and may not reflect the true arrival date.

var query = client.Messages.Where(x => ...).Select(x => new Container() 
{
Envelope = x.Envelope,
BodyStructure = x.BodyStructure,
Size = x.Size,
InternalDate = x.InternalDate
}

With this call we get all the information we can about a message that the IMAP protocol has to offer, without actually having to fetch the message.

Fetching Individual Message Parts

Once the user receives notification about a new message he may choose to view it or open one of its attachments. As already mentioned downloading everything is not recommended, since it comes with some annoying side effects.

If a message has large attachments, the user should be able to read its content, without having to fetch the 20MB attachments into memory or to the disk. If a user decided to only view plain text messages, there is no need to fetch the html view.

var bodyStructure = ...

// fetching only the html view
var htmlViewInfo = bodyStructure.Views.Where(x => x.MediaType == "text/html");
var htmlView = client.FetchView(htmlViewInfo);

// fetching the third attachment
var thirdAttachmentInfo = bodyStructure.Attachments.ElementAt(2);
var thirdAttachment = client.FetchAttachment(thirdAttachmentInfo);

// fetching only images
var imageInfos = bodyStructure.Attachments.Where(x => x.MediaType.StartsWith("image"));
var images = imageInfos.Select(client.FetchAttachment).ToList();
Thunderbird uses the last approach to detect attached images and load those initially while leaving zips, pdfs on the server, thus being able to display previews for all attached images.

Another good strategy is to load attachments not by type, but by size. The following query will initially fetch all attachments which sizes do not surpass 100k.

var smallAttachmentInfos = bodyStructure.Attachments.Where(x => CalculateEncodedSize(x.SizeEncoded) <= 100.0);
var smallAttachments = smallAttachmentInfos.Select(client.FetchAttachment).ToList();
Since all attachments need to be encoded for transfer using base64 there encoded size differs from their actual size. In order to calculate the decoded size we can use the following function, which will give return an approximation of the actual size.
 
public static Single CalculateEncodedSize(Size size)
{
return (size.Bytes - 814) / 1.37;
}

Last edited Jul 2, 2011 at 6:46 PM by Krasshirsch, version 8

Comments

No comments yet.