+1 vote
by (420 points)
edited

So far the best description i found about Thread-Index is this. There is also an MSDN entry about it but if you check the first link you will know that MSDN description differs from reality. I managed to write a class that will parse the Thread-Index header:


public class OutlookThreadIndex
{
    public DateTime Date { get; private set; }
    public Guid Id { get; private set; }
    public string Raw { get; private set; }

    public bool IsValid 
    {
        get { return Date != default(DateTime) && Id != default(Guid); }
    }

    public OutlookThreadIndex(string threadIndex)
    {
        Raw = threadIndex;

        var bytes = Convert.FromBase64String(threadIndex);

        // thread index length should be 22 plus extra 5 bytes per reply
        if (bytes.Length < 22 || (bytes.Length - 22) % 5 != 0)
            return;

        Id = new Guid(bytes.Skip(6).Take(16).ToArray());

        var headerTicks = bytes
            .Take(6)
            .Select(b => (long) b)
            .Aggregate((l1, l2) => (l1 << 8) + l2) 
            << 16;

        Date = new DateTime(headerTicks, DateTimeKind.Utc).AddYears(1600);

        var childBlockCount = (bytes.Length - 22) / 5;

        for (var i = 0; i < childBlockCount; i++)
        {
            var childTicks = bytes
                .Skip(22 + i*5).Take(4)
                .Select(b => (long) b)
                .Aggregate((l1, l2) => (l1 << 8) + l2)
                << 18;

            childTicks &= ~((long) 1 << 50);
            Date = Date.AddTicks(childTicks);
        }
    }

    public override string ToString()
    {
        return string.Format("Id: {0}, Date: {1}", Id, Date.ToLocalTime());
    }
}

public static class MimeHeaderCollectionExtensions
{
    public static OutlookThreadIndex ThreadIndex(this MimeHeaderCollection headers)
    {
        var header = headers["Thread-Index"];
        return header != null
            ? new OutlookThreadIndex(header.Raw)
            : null;
    }
}

This is a sample result:

  • Thread-Index: Ac52TvSLqL2n1+xFTLyFv0/vTSUlWgyF0E9Q
  • Parsed Id: d7a7bda8-45ec-bc4c-85bf-4fef4d25255a
  • Parsed Date: 2013-09-03 07:26:58

Thread-Index can be useful in two cases:

  • mail is missing Date in header - we can use thread date
  • mail is missing In-Reply-To or References in header - we can use thread id

I have tested this against a series of email and found out that a lot of them are parsed incorrectly. Here is an example:

  • Thread-Index: AQLNahwAkMF4P9Bl8t7dPDytf5aNnA==
  • Parsed Id: 3f78c190-65d0-def2-dd3c-3cad7f968d9c
  • Parsed Date: 1831-11-05 00:06:01

And here is another one:

  • Thread-Index: Ac6ocXoGclflrAlgQg2VFL1JFq6hyg==
  • Parsed Date: 2013-09-03 08:47:37
  • Parsed Id: ace55772-6009-0d42-9514-bd4916aea1ca

The first Thread-Index is invalid (see year 1831). The second one is ok. What is interesing is that both Thread-Indexes are from the same mail message but the first one is from senders Sent folder and the other one is from recipients Inbox folder. I'm using Outlook 2013. Also interesting fact, all indexes that seem invalid have date around year 1800. Has anyone faced this problem before and/or can suggest any solution?

1 Answer

0 votes
by (58.9k points)
edited

Although Rebex Secure Mail can be used to get and set the Thread-Index header of a mail easily, we have not dived into Thread-Index problematics while programming Rebex Secure Mail component.

Just one simple idea which occured to me - isn't the old date (e.g. 1831-11-05 00:06:01) a way how Outlook shows that the Thread-Index is invalid? One does not expect a mail to be working back then in the 19th century.

I have also searched and found this question about Thread-Index email header at Stackoverflow forum. Maybe you could ask there to get a broader audience...?

by (420 points)
edited

If Outlook would like to create invalid value for Thread-Index (for whatever reason) i guess that it would be easier to use a fixed value rather than some random date.

Of course I'm aware that Thread-Index parsing is not Rebex components area of responsibility but people using it (like me) might be interested in knowing how to use it. Would it be possible that you run the code I posted against any mailbox to see how many emails give you valid date and how many don't? Maybe you will see any pattern. I'm stuck with this and after several hours i think disassembling outlook is the best option ;)

by (73.5k points)
edited

OK, I will take your code and run it against our mailbox. I will send you the result when it completes. Hope this will help you to move further.

by (58.9k points)
edited

We have run your parser code over a big inbox which contains emails back from 2001 up to now. There starts to appear the strange old dates (year 1830 - 1854). But the very first email with such a Thread-Index date was actually received in the year 2009. Since 2009 strange Thread-Index dates around 1840 appears to be a normal issue. (In a smaller inbox with mail from 2011-2013 the bad date percentage was around 8% of email with Thread-Index).

...