InfiniTec - Henning Krauses Blog

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

Exchange WebServices Bug with Lineendings

When you fetch the body of a mail via Exchange WebServices you might have noticed the fact that the body of the mail has its line endings, which are normally represented by a CRLF (“\r\n” in C#) replaced with a simple LF (“\n”). For the text and HTML body, this is the expected behavior. But Exchange also enforces this behavior on custom properties, which was not intended.

Below is a small program that demonstrates the problem. It creates a new item in the drafts folder of the current users mailbox and then reads the content back. You’ll notice the missing \r characters when you examine the propertyValue property.

   1: using System;
   2: using System.Linq;
   3: using System.Net;
   4: using Microsoft.Exchange.WebServices.Data;
   5:  
   6: namespace TestApplication
   7: {
   8:     internal class Program
   9:     {
  10:         private static void Main()
  11:         {
  12:             ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, errors) => true;
  13:             var service = new ExchangeService(ExchangeVersion.Exchange2007_SP1)
  14:                           {
  15:                           Url = new Uri("https://w2k3x64/ews/exchange.asmx"),
  16:                           UseDefaultCredentials = true,
  17:                           };
  18:             Folder folder = Folder.Bind(service, WellKnownFolderName.Drafts);
  19:             var item = new PostItem(service) {Subject = "test", Body = "Line1\r\nLine2"};
  20:  
  21:             var definition = new ExtendedPropertyDefinition(DefaultExtendedPropertySet.PublicStrings, "TestProperty",
  22:                                                             MapiPropertyType.String);
  23:             item.ExtendedProperties.Add(definition, "Line1\r\nLine2");
  24:             item.Save(folder.Id);
  25:  
  26:             ItemId id = item.Id;
  27:             item = PostItem.Bind(service, id, new PropertySet(BasePropertySet.FirstClassProperties, definition));
  28:             string propertyValue = (from property in item.ExtendedProperties 
  29:                                     where property.PropertyDefinition == definition 
  30:                                     select property.Value).First();
  31:  
  32:             Console.Out.WriteLine("item.Body = {0}", item.Body);
  33:             Console.Out.WriteLine("propertyValue = {0}", propertyValue);
  34:  
  35:             Console.ReadLine();
  36:         }
  37:     }
  38: }

So, if you are struggling with this ‘anomaly’, you need to open a case with the Microsoft products support (PSS) to get a fix.


Posted by Henning Krause on Tuesday, August 25, 2009 9:04 PM, last modified on Tuesday, August 25, 2009 9:04 PM
Permalink | Post RSSRSS comment feed

Getting the body of an Email with a FindItems request

The FindItem operation (or the corresponding ExchangeService.FindItems method) does not return the body of an email by default. And when trying to explicitly request them via a custom propertyset, the call fails. Consider this method, which uses the EWS Managed API to execute a FindItems method to get every message from the inbox folder of a mailbox, fetching only the item id and the body:

   1: private static void GetAllItems(ExchangeService exchangeService)
   2: {
   3:     var offset = 0;
   4:     const int pageSize = 100;
   5:  
   6:     FindItemsResults<Item> result;
   7:     do
   8:     {
   9:         var view = new ItemView(pageSize, offset)
  10:                        {
  11:                            PropertySet = new PropertySet(BasePropertySet.IdOnly)
  12:                                              {
  13:                                                  ItemSchema.Body
  14:                                              }
  15:                        };
  16:  
  17:         result = exchangeService.FindItems(WellKnownFolderName.Inbox, view);
  18:  
  19:         foreach (var item in result)
  20:         {
  21:             ProcessItem(item);
  22:         }
  23:         offset += pageSize;
  24:     } while (result.MoreAvailable);
  25: }

When executed, the ExchangeService instance throws a ServiceValidationException stating “The property Body cannot be used in FindItem requests”. The official workaround proposed by Microsoft is to use a FindItem request to get the item ids of the items in a folder and afterward issue a GetItem request containing all the item ids and request the body property. However, there is another solution: The body properties can be fetched by requesting the MAPI properties containing the body:

   1: private static ExtendedPropertyDefinition TextBodyProperty = new ExtendedPropertyDefinition(0x1000, MapiPropertyType.String);
   2: private static ExtendedPropertyDefinition HtmlBodyProperty = new ExtendedPropertyDefinition(0x1013, MapiPropertyType.Binary);

The first property definition can be used to fetch the text body of a mail. The second one fetches the Html body. The new GetAllItemsMethod now looks like this:

   1: private static void GetAllItems(ExchangeService exchangeService)
   2: {
   3:     var offset = 0;
   4:     const int pageSize = 100;
   5:  
   6:     FindItemsResults<Item> result;
   7:     do
   8:     {
   9:         var view = new ItemView(pageSize, offset)
  10:                        {
  11:                            PropertySet = new PropertySet(BasePropertySet.IdOnly)
  12:                                              {
  13:                                                  HtmlBodyProperty
  14:                                              }
  15:                        };
  16:  
  17:         result = exchangeService.FindItems(WellKnownFolderName.Inbox, view);
  18:  
  19:         foreach (var item in result)
  20:         {
  21:             object body;
  22:             if (item.ExtendedProperties.TryGetValue(HtmlBodyProperty, out body))
  23:             {
  24:                 Console.Out.WriteLine("item.Body = {0}", Encoding.UTF8.GetString(Convert.FromBase64String((string)body)));
  25:             }
  26:         }
  27:         offset += pageSize;
  28:     } while (result.MoreAvailable);
  29: }

The HtmlBody is requested with the addtion of the HtmlBodyProperty to the ItemView in line 13. Since the Html body is stored in binary form and returned in Base64 encoded format, it needs to be decoded before it can be displayed. This is done in line 24. If the plaintext body is requested, the value of the body property in line 24 can simply be converted to a string.


Posted by Henning Krause on Tuesday, June 9, 2009 9:00 PM, last modified on Wednesday, April 20, 2011 6:19 PM
Permalink | Post RSSRSS comment feed

Setting the Task owner on new items using Exchange WebServices

If you try to set the owner of a task using Exchange WebServices, the server will tell you that this is not allowed. To circumvent this restriction, you’ll have to set the MAPI property directly. This is done by creating an ExtendedPropertyType instance and adding that to an UpdateItemType.

   1: var modification = new PathToExtendedFieldType
   2:     {
   3:         DistinguishedPropertySetId = DistinguishedPropertySetType.Task,
   4:         DistinguishedPropertySetIdSpecified = true,
   5:         PropertyId = 0x811f,
   6:         PropertyIdSpecified = true,
   7:         PropertyType = MapiPropertyTypeType.String
   8:     }, value);

However, this won’t work when creating new items. Exchange will return an error complaining about an invalid PropertySet id. The only solution I found is to issue a CreateItem request first. On this request, the task owner is omitted. After the item has been created, use the ItemId from the CreateItem response and execute an UpdateItem request setting only the task owner.


Posted by Henning Krause on Thursday, March 19, 2009 7:36 PM, last modified on Monday, November 29, 2010 9:31 PM
Permalink | Post RSSRSS comment feed

ExchangeWebServices / WebDAV and untrusted server certificates

Exchange 2007 has requires SSL for its WebServices, and event for Exchange 2003 some administrators have enabled this requirement on the IIS. If you are dealing with a self-signed certificate on the server and want to use .NET, you will stumble across this error message:

The underlying connection was closed: Could not establish trust relationship for the SSL/TLS secure channel.

or

The remote certificate is invalid according to the validation procedure.

By default, .NET checks whether SSL certificates are signed by a certificate from the Trusted Root Certificate store. To override this behavior, use the System.Net.ServicePointManager.ServerCertificateValidationCallback property:

   1: ServicePointManager.ServerCertificateValidationCallback = RemoteCertificateValidationCallback;

The callback looks like this:

   1: private static bool RemoteCertificateValidationCallback(object sender, X509Certificate certificate, X509Chain chain, SslPolicyErrors sslPolicyErrors)
   2: {
   3:     return true;
   4: }

This will accept all certificates, regardless of why they are invalid. One option here is to display a warning similar to the Internet Explorer one.

Using C# 3.0, this can even be written with less code:

   1: ServicePointManager.ServerCertificateValidationCallback = (sender, certificate, chain, sslPolicyErrors) => true;

Posted by Henning Krause on Wednesday, November 26, 2008 5:57 PM, last modified on Wednesday, November 26, 2008 5:57 PM
Permalink | Post RSSRSS comment feed

Exchange 2007 SP1 Item ids

Exchange 2007 introduced a new URL format (Constructing OWA 2007 item ids from WebDAV items) which contained an arbitrary item id, which was based on the EntryId of the item. The format was this:

Length Meaning
1 Length of the structure
sizeof(EntryId) EntryId
1 Item type

This format changed with Exchange Service Pack 1. The layout is now this:

Length Meaning
4 Length of the user's email address
sizeof(EmailAddress) Email address specifying the mailbox which contains the item
4 Size of the EntryId
sizeof(EntryId) The EntryId of the item

This layout also applies to folder ids within a mailbox.

 

   1: <UIRefId="WixUI_Minimal"/>
   2:         <BinaryId="ManagedCustomAction"SourceFile="Include\ManagedCustomAction.dll" />
   3:         <ManagedCustomActionId="test"BinaryKey="ManagedCustomAction"Type="ManagedCustomAction.CustomAction"Execute="immediate"xmlns="http://schemas.infinitec.de/wix/MCAExtension" />
   4:         <ManagedActionSequencexmlns="http://schemas.infinitec.de/wix/MCAExtension">
   5:         <ManagedAction="test"After="CostFinalize"SequenceTable="InstallUISequence" />
   6:     </ManagedActionSequence>
   7: </Product>

Posted by Henning Krause on Thursday, May 15, 2008 6:42 PM, last modified on Thursday, May 15, 2008 8:08 PM
Permalink | Post RSSRSS comment feed