InfiniTec - Henning Krauses Blog

Don't adjust your mind - it's reality that is malfunctioning

Decoding entry ids

The EntryId (stored in the property http://schemas.microsoft.com/mapi/proptag/xfff0102) is a binary structure uniquely identifying elements in the Exchange store. It basically consists of two identifiers (three for folders): The MAPI store where the element is located (either a mailbox or a public folder tree), the folder identifier and the message identifier. Unfortunately, the store identifier cannot be mapped to a mailbox or public folder tree, so you must know in which store the element is located. But this should not be an issue in most cases. The folder and item identifiers consists of a global unique identifier and a sequential number. The combined value of these identifiers can be used to construct a permanent url (found in the property http://schemas.microsoft.com/exchange/permanenturl).

A permanent url looks like this: http://server/exchange/user/-FlatUrlSpace-/8996592620a48a4393901c9368f7c518-1488/8996592620a48a4393901c9368f7c518-1af3

This url never changes unless the item is moved to another folder.

Below is method which constructs such a permanent url based on the url to the store (the http://server/exchange/user in the above example) and an EntryId structure:

    1 using System;

    2 using System.Globalization;

    3 using System.IO;

    4 

    5 namespace InfiniTec.Exchange

    6 {

    7     staticclassExchangeStoreReference

    8     {

    9         publicstaticstring DecodeEntryId(BinaryReader reader, string baseUrl)

   10         {

   11             Guid folderId;

   12             ulong folderCnt;

   13 

   14             // First reserved field

   15             reader.ReadUInt32();

   16 

   17             // Now comes the store guid.

   18             reader.ReadBytes(16);

   19 

   20             // Next reserved field

   21             reader.ReadUInt16();

   22 

   23             folderId = ReadGuid(reader);

   24             folderCnt = SwapUInt64(reader.ReadUInt64());

   25 

   26             if (!baseUrl.EndsWith("/"))

   27             {

   28                 baseUrl += "/";

   29             }

   30 

   31             if (reader.BaseStream.Length - reader.BaseStream.Position >= 24)

   32             {

   33                 Guid messageId;

   34                 messageId = ReadGuid(reader);

   35                 ulong messageCnt;

   36                 messageCnt = SwapUInt64(reader.ReadUInt64());

   37                 baseUrl += string.Format(CultureInfo.CurrentCulture, baseUrl + "/-FlatUrlSpace-/{0:N}-{1:x}/{2:N}-{3:x}", folderId, folderCnt, messageId, messageCnt);

   38             }

   39             else

   40             {

   41                 baseUrl += string.Format(CultureInfo.CurrentCulture, baseUrl + "/-FlatUrlSpace-/{0:N}-{1:x}", folderId, folderCnt);

   42             }

   43             return baseUrl;

   44 

   45         }

   46 

   47         privatestaticGuid ReadGuid(BinaryReader reader)

   48         {

   49             int a;

   50             short b, c;

   51 

   52             a = SwapInt(reader.ReadUInt32());

   53             b = reader.ReadInt16();

   54             c = SwapShort(reader.ReadUInt16());

   55             returnnewGuid(a, b, c, reader.ReadBytes(8));

   56         }

   57 

   58         privatestaticshort SwapShort(ushort value)

   59         {

   60             unchecked

   61             {

   62                 ushort result;

   63                 result = (ushort)​(((value & 0xFF00) >> 8) |

   64                                ((value & 0x00FF) << 8));

   65 

   66                 return (short)result;

   67             }

   68         }

   69 

   70         privatestaticint SwapInt(uint value)

   71         {

   72             uint result;

   73 

   74             result = ((value & 0xFF000000) >> 24) |

   75                     ((value & 0x00FF0000) >> 8) |

   76                     ((value & 0x0000FF00) << 8) |

   77                     ((value & 0x000000FF) << 24);

   78 

   79             unchecked

   80             {

   81                 return (int)result;

   82             }

   83         }

   84 

   85 

   86         privatestaticulong SwapUInt64(ulong value)

   87         {

   88             uint lo;

   89             uint hi;

   90             ulong result;

   91 

   92             lo = (uint)​(value & 0xffffffff);

   93             hi = ((uint)​(value >> 32)) & 0xffffffff;

   94 

   95             lo = ((lo & 0xFF000000) >> 8) |

   96                 ((lo & 0x00FF0000) << 8) |

   97                 ((lo & 0x0000FF00) >> 8) |

   98                 ((lo & 0x000000FF) << 8);

   99 

  100             hi = ((hi & 0xFF000000) >> 8) |

  101                 ((hi & 0x00FF0000) << 8) |

  102                 ((hi & 0x0000FF00) >> 8) |

  103                 ((hi & 0x000000FF) << 8);

  104 

  105             result = (((ulong)lo) << 32) | hi;

  106 

  107             return result;

  108         }

  109     }

  110 }

As you can see, there is some bit-flipping going on during the process. This is because the guids in the EntryId are stored differently than they are used in the permanent url. At last, the folder id, folder sequence number, element id and element sequence number are concatenated and appended to the base url. The result is a permanent url which can be used to access the element.

To use this method, just wrap the binary value of the EntryId in a BinaryReader class and pass that instance to the DecodeEntryId method.

Remarks

  • The method above will not work for all EntryIds. Depending on how the EntryId was obtained, it may have a different structure. This is because EntryIds can either be short-term or long-term identifiers and are usually just passed to the IMsgStore::OpenEntry method within a MAPI session. But an EntryId obtained through the http://schemas.microsoft.com/mapi/proptag/xfff0102 property should work.
  • If you obtained the EntryId via a WebDAV PROPFIND command, you must decode the BASE64 string to a binary array using the Convert.FromBase64String() method.

Technorati:

Posted by Henning Krause on Saturday, March 10, 2007 12:00 AM, last modified on Thursday, March 8, 2007 9:00 PM
Permalink | Post RSSRSS comment feed