InfiniTec - Henning Krauses Blog

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

Working with the Master Category List–EWS edition

Back in 2008 I wrote an article about accessing the Master Category List using WebDAV. If you are not sure what the Master Category List is, read that article first…

EWS could not be used at that time, since access to the Folder Associated Items via EWS is a Feature of Exchange 2010. So if you are on Exchange 2007, the old article still applies.

The last article contained some code which simplified updating the list. I’ve updated the code and aligned the method naming with that of the EWS managed API.

This is the class diagram for the updated code:

ClassDiagram

The classes are really easy to use:

var service = new ExchangeService(ExchangeVersion.Exchange2010_SP1) { Credentials = new NetworkCredential("someone@infinitec.de", "password") };
service.AutodiscoverUrl("someone@infinitec.de", url => true);

var list = MasterCategoryList.Bind(service);
foreach (var category in list.Categories)
{
    Console.Out.WriteLine("category = {0}", category.Name);
}

The only interesting line in the sample above is line 4. The call to MasterCategoryList.Bind() loads the MasterCategoryList from the users Exchange Mailbox. After that, the name of each console is written to the console.

Adding a new category is equally simple:

var service = new ExchangeService(ExchangeVersion.Exchange2010_SP1) { Credentials = new NetworkCredential("someone@infinitec.de", "password") };
service.AutodiscoverUrl("someone@infinitec.de", url => true);

var list = MasterCategoryList.Bind(service);
list.Categories.Add(new Category("Vacation", CategoryColor.DarkMaroon, CategoryKeyboardShortcut.CtrlF10));
list.Update();

This will add a new category named “Vacation” to the list.

So how does this work? The Master Category List is stored in a hidden message in the calendar folder of a mailbox. EWS in 2010 provides simple access to this message with the UserConfiguration class. This code show how the Master Category List is loaded:

var item = UserConfiguration.Bind(service, "CategoryList", WellKnownFolderName.Calendar,
                                  UserConfigurationProperties.XmlData);

var reader = new StreamReader(new MemoryStream(item.XmlData), Encoding.UTF8, true);

The configuration data is stored in the XmlData property as UTF-8 encoded byte array.

Here is the new code:

Sourcecode Download


Posted by Henning Krause on Thursday, July 28, 2011 4:00 PM, last modified on Tuesday, June 11, 2013 4:52 PM
Permalink | Post RSSRSS comment feed

Working with the MasterCategoryList Via WebDAV

Before Outlook 2007 categories were plain-text. They had a name, but nothing more. Outlook 2007 enhanced this concept by adding colors to categories:

 image

Each category can be given a color (one of 25) and a shortcut (CTRL+F2 through CTRL+F12). Unlike in former Outlook versions, this list of categories is no longer stored in the registry, but in the default calendar of the mailbox. Outlook 2007 creates a hidden message in that folder with the message class IPM.Configuration.CategoryList. The category configuration is stored in the MAPI property 0x7C08 type bin.base64. To query the master category list via WebDAV, two steps are necessary:

  1. Get the URL of the default calendar of a given mailbox (See Getting Well-Known Mailbox Folder URLs on MSDN).
  2. Search the default calendar for items with a message class of IPM.Configuration.CategoryList.
  3. Parse the binary stream

Getting the default calendar

To accomplish the first step, send a PROPFIND request to the root url of the mailbox:

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <propfind xmlns="DAV:">
   3:     <prop>
   4:         <a:calendar xmlns:a="urn:schemas:httpmail:" />
   5:     </prop>
   6: </propfind>

The result might look like this:

   1: <?xml version="1.0"?>
   2: <a:multistatus xmlns:d="urn:schemas:httpmail:" xmlns:a="DAV:">
   3:     <a:response>
   4:         <a:href>http://server/exchange/mailbox/</a:href>
   5:         <a:propstat>
   6:             <a:status>HTTP/1.1 200 OK</a:status>
   7:             <a:prop>
   8:                 <d:calendar>http://server/exchange/mailbox/Calendar</d:calendar>
   9:             </a:prop>
  10:         </a:propstat>
  11:     </a:response>
  12: </a:multistatus>

Searching for the hidden message

Given the correct calendar folder path, a SEARCH query can be constructed to retrieve the property in question from the hidden message:

   1: <?xml version="1.0" encoding="utf-8"?>
   2: <searchrequest xmlns="DAV:">
   3:     <sql>
   3:         SELECT 
   4:             "http://schemas.microsoft.com/mapi/proptag/x7c080102", 
   5:             "http://schemas.microsoft.com/exchange/permanenturl" 
   6:             FROM SCOPE ('SHALLOW TRAVERSAL OF "http://server/exchange/mailbox/Calendar"') 
   7:             WHERE 
   8:                 ("DAV:isfolder" = false AND 
   9:                 ("http://schemas.microsoft.com/mapi/proptag/x001a001f" = 'IPM.Configuration.CategoryList')) 
  11:     </sql>
  12: </searchrequest>

adssdfdsf

If successful, the server returns something similar like this:

   1: <?xml version="1.0"?>
   2: <a:multistatus xmlns:b="urn:uuid:c2f41010-65b3-11d1-a29f-00aa00c14882/" 
   3:                xmlns:d="http://schemas.microsoft.com/mapi/proptag/" 
   4:                xmlns:e="http://schemas.microsoft.com/exchange/" 
   5:                xmlns:a="DAV:">
   6:     <a:contentrange>0-0</a:contentrange>
   7:     <a:response>
   8:         <a:href>http://server/exchange/mailbox/Calendar/IPM.Configuration.CategoryList.EML</a:href>
   9:         <a:propstat>
  10:             <a:status>HTTP/1.1 200 OK</a:status>
  11:             <a:prop>
  12:                 <d:x7c080102 b:dt="bin.base64">Base64 content ommited to improve readability</d:x7c080102>
  13:                 <e:permanenturl>http://server/exchange/mailbox/-FlatUrlSpace-/155ae68de4aeda4987585833042471bc-8a5e2/ac5d26d07c067f4ea4f38a18f64786a3-3a1c8a</e:permanenturl>
  14:             </a:prop>
  15:         </a:propstat>
  16:     </a:response>
  17: </a:multistatus>

sdfssdfsdfdsf

Decoding the Property value

The content can now be extracted by loading the result into a System.Xml.XmlDocument or System.Xml.Linq.XDocument and select the appropiate node using an XPath expression. To decode the value, use the System.Convert.FromBase64String and the System.Text.Encoding.UTF8.GetString method. This will yield the XML representation of the Master Category List. Since manipulation of raw XML data is not exactly fun, I've created a sample solution which uses the System.Xml.Serialization.XmlSerializer to construct a strong-typed object from the stream. Below is a class diagram of the available classes:

image

The MasterCategoryList has a static Load method which takes a System.IO.TextReader instance and reads the the value decoded earlier:

   1: using (var reader = new StringReader(masterCategoryListContent))
   2: {
   3:     var masterCategoryList = MasterCategoryList.Load(reader);
   4:     foreach (var category in masterCategoryList.Categories)
   5:     {
   6:         Console.Out.WriteLine("{0}: Color {1}, Shortcut {2}", category.Name, category.Color, category.KeyboardShortcut);
   7:     }
   8: }

This code assumes that the string representation of the Master Category List is stored in the masterCategoryListContent field.

Once done with manipulating the instance, it can be saved to a System.IO.TextWriter. The Master Category List in the mailbox can then be updated using a PROPPATCH request on the address used above.

Download: Source files

Links

Fellow MVP Glen Scales has an article on his blog about this topic as well: Adding Categories to the Master categories list in Outlook 2007 with a CDO 1.2 script


Posted by Henning Krause on Thursday, May 22, 2008 2:36 PM, last modified on Thursday, May 22, 2008 2:39 PM
Permalink | Post RSSRSS comment feed