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.
8894d3a4-24c6-4c6c-b54b-aa7d13acedc7|2|5.0
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.
d72fd6cc-1106-4a01-9228-7c39da272d12|4|5.0
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.
fd3af5ac-d25b-4a49-a4e7-c584539ad1b2|2|2.0
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;
0e4fa802-b6af-4da4-9921-f901a3dfa57a|10|4.7
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>
5ef42b1d-a0f3-485f-87b2-070e95e5a8a3|0|.0
Tags:
exchange 2007, service pack 1, owa, exchange webservices, entryid, mailbox
Technorati:
exchange+2007, service+pack+1, owa, exchange+webservices, entryid, mailbox