Reading and Extracting OLM Messages

After you open an OLM file and locate a folder, you can list its messages, read short message information, filter and page through large folders, and extract individual messages as MapiMessage objects. This article covers all of these operations.

List Messages in a Folder

The OlmFolder class provides two ways to iterate over the messages it contains:

Using EnumerateMessages

using (var olm = OlmStorage.FromFile(fileName))
{
    var folder = olm.GetFolder("Inbox", true);
    foreach (var messageInfo in folder.EnumerateMessages())
    {
        Console.WriteLine(messageInfo.Subject);
    }
}

Using EnumerateMapiMessages

using (var olm = OlmStorage.FromFile(fileName))
{
    var folder = olm.GetFolder("Inbox", true);

    foreach (var msg in folder.EnumerateMapiMessages())
    {
        // save the message in MSG format
        msg.Save($"{msg.Subject}.msg");
    }
}

Read Message Information

Each OlmMessageInfo object exposes a set of read-only properties that describe a message without loading its full contents. This is the most efficient way to build a list view or to decide which messages to extract.

Property Description
Subject The message subject.
From The sender address.
To The collection of recipient addresses.
Date The date of the message.
ModifiedDate The date the message was last modified.
HasAttachments Indicates whether the message has attachments.
MessageClass The sender-defined message class, such as IPM.Note.
EntryId The message entry identifier.
using (var olm = OlmStorage.FromFile(fileName))
{
    var folder = olm.GetFolder("Inbox", true);

    foreach (OlmMessageInfo messageInfo in folder.EnumerateMessages())
    {
        Console.WriteLine($"Subject: {messageInfo.Subject}");
        Console.WriteLine($"From: {messageInfo.From}");
        Console.WriteLine($"To: {string.Join("; ", messageInfo.To)}");
        Console.WriteLine($"Date: {messageInfo.Date}");
        Console.WriteLine($"Modified: {messageInfo.ModifiedDate}");
        Console.WriteLine($"Has attachments: {messageInfo.HasAttachments}");
        Console.WriteLine($"Message class: {messageInfo.MessageClass}");
        Console.WriteLine(new string('-', 30));
    }
}

Get the Modified Date of a Message

The modified date represents the date and time when the OLM message was last modified. Use the OlmMessageInfo.ModifiedDate property to read it:

foreach (OlmMessageInfo messageInfo in inboxFolder.EnumerateMessages())
{
    DateTime modifiedDate = messageInfo.ModifiedDate;
}

Filter Messages with a Query

For large folders, you can retrieve only the messages that match a set of criteria instead of iterating over everything. Pass a MailQuery to the EnumerateMessages(MailQuery query) overload. Build the query with a MailQueryBuilder.

The example below enumerates only the messages received during the last seven days whose subject contains the word “Invoice”:

using (var olm = OlmStorage.FromFile(fileName))
{
    var folder = olm.GetFolder("Inbox", true);

    var builder = new MailQueryBuilder();
    builder.InternalDate.Since(DateTime.Now.AddDays(-7));
    builder.Subject.Contains("Invoice");
    MailQuery query = builder.GetQuery();

    foreach (var messageInfo in folder.EnumerateMessages(query))
    {
        Console.WriteLine(messageInfo.Subject);
    }
}

Page Through Messages

When a folder contains a large number of messages, you can read them in batches with the EnumerateMessages(int skip, int count) overload. The skip parameter is the number of messages to bypass, and count is the number of messages to return.

using (var olm = OlmStorage.FromFile(fileName))
{
    var folder = olm.GetFolder("Inbox", true);

    const int pageSize = 50;
    int pageIndex = 0;

    while (true)
    {
        var page = folder.EnumerateMessages(pageIndex * pageSize, pageSize).ToList();
        if (page.Count == 0)
        {
            break;
        }

        foreach (var messageInfo in page)
        {
            Console.WriteLine(messageInfo.Subject);
        }

        pageIndex++;
    }
}

Extract a Message as a MapiMessage

The OlmStorage class provides the ExtractMapiMessage method, which extracts a full MapiMessage from an OlmMessageInfo object. This is useful when you first scan a folder with EnumerateMessages and then extract only the messages you actually need.

using (var olm = OlmStorage.FromFile(fileName))
{
    var folder = olm.GetFolder("Inbox", true);

    foreach (var messageInfo in folder.EnumerateMessages())
    {
        if (messageInfo.Date.Date == DateTime.Today)
        {
            // extract today's messages from Inbox
            var msg = olm.ExtractMapiMessage(messageInfo);
            msg.Save($"{msg.Subject}.msg");
        }
    }
}

Extract Messages by Identifier

Sometimes you need to extract selected messages by their identifiers. For example, an application can store identifiers in a database and extract a message on demand instead of traversing the entire storage each time. The EntryId property of OlmMessageInfo provides the message entry identifier, and the overloaded ExtractMapiMessage(string id) method retrieves the message by that identifier.

using (var olm = OlmStorage.FromFile(fileName))
{
    var olmFolder = olm.GetFolder("Inbox", true);

    foreach (OlmMessageInfo msgInfo in olmFolder.EnumerateMessages())
    {
        // store msgInfo.EntryId somewhere, then later:
        MapiMessage msg = olm.ExtractMapiMessage(msgInfo.EntryId);
    }
}

Extract Items Using the Traversal API

You can extract every item from an OLM file - as far as possible - without throwing exceptions, even when some data in the original file is corrupted. To do this, create the OlmStorage instance with the OlmStorage(TraversalExceptionsCallback callback) constructor and load the file with the Load method. The callback exposes the loading and traversal exceptions.

The Load method returns true if the file was loaded successfully and traversal is possible, and false if the file is corrupted and no traversal is possible. Note that when traversing items folder by folder, you call the storage’s EnumerateMessages(OlmFolder folder) method, passing the folder as an argument.

The steps are:

  1. Create a new OlmStorage instance, passing an exception-handling callback.
  2. Load the OLM file by calling the Load method.
  3. If the file loads successfully, obtain the folder hierarchy by calling GetFolders.
  4. Recursively iterate over each folder. For folders that have messages, enumerate them with the storage’s EnumerateMessages method and process each one.
  5. Recurse into any sub-folders.
using (var olm = new OlmStorage((exception, id) => { /* Exception handling code. */ }))
{
    if (olm.Load(fileName))
    {
        var folderHierarchy = olm.GetFolders();
        ExtractItems(olm, folderHierarchy);
    }
}

private static void ExtractItems(OlmStorage olm, IEnumerable<OlmFolder> folders)
{
    foreach (var folder in folders)
    {
        if (folder.HasMessages)
        {
            Console.WriteLine(folder);

            foreach (var msg in olm.EnumerateMessages(folder))
            {
                Console.WriteLine(msg.Subject);
            }
        }

        ExtractItems(olm, folder.SubFolders);
    }
}