You are currently browsing the tag archive for the ‘Exchange’ tag.

Welcome back,

Today I would like to look into creating a Distribution List through the Exchange Webservice. This is not as straight forward as creating a contact, but it should work none the less. According to this book and the documentation found on the internet, it not possible to accomplish this. But there is a way around. Lets take the scenic route to create a Distribution List J

Take a look at the code in the previous post. You can see that – in the method to create the contact – a CreateItemType is instantiated. If you look at the Items property of that variable you can see that the it expects an NonEmptyArrayOfAllItemsType which again has an Items Property. In that property we can store any ItemType we want except a Distribution List, as you can see here. But if we can not insert the DistributionList in a CreateItem, then how the f* are we going to get it to the exchange server. Well we are going to create an ItemType (base class) and use that to set all the properties needed so that exchange and outlook recognizes the Item as a distribution list. Because an ItemType is the base class, it is not provided with any of the properties we need to create a distribution list. That is where the Extended Properties come in handy. Everything needs to be set through those properties.

Here are some links with more information on the properties:

And you can also still use OutlookSpy which was linked in the previous post.
Now for some code.
The first thing I’d like to do is declare the path to extended field variables and set them to point to the properties we’re going to use. I’ll name them all starting with ptef so we can get the quickly through intellisense.

private static PathToExtendedFieldType ptefDisplayName =

new PathToExtendedFieldType

{

PropertyTag = “0x3001”,

PropertyType = MapiPropertyTypeType.String

};

private static PathToExtendedFieldType ptefDistributionListName =

new PathToExtendedFieldType

{

PropertyId = 0x8053,

PropertyIdSpecified = true,

DistinguishedPropertySetId = DistinguishedPropertySetType.Address,

DistinguishedPropertySetIdSpecified = true,

PropertyType = MapiPropertyTypeType.String

};

private static PathToExtendedFieldType ptefFileUnder =

new PathToExtendedFieldType

{

PropertyId = 0x8005,

PropertyIdSpecified = true,

DistinguishedPropertySetId = DistinguishedPropertySetType.Address,

DistinguishedPropertySetIdSpecified = true,

PropertyType = MapiPropertyTypeType.String

};

private static PathToExtendedFieldType ptefMembers =

new PathToExtendedFieldType

{

PropertyId = 0x8055,

PropertyIdSpecified = true,

DistinguishedPropertySetId = DistinguishedPropertySetType.Address,

DistinguishedPropertySetIdSpecified = true,

PropertyType = MapiPropertyTypeType.BinaryArray

};

private static PathToExtendedFieldType ptefOneOffMembers =

new PathToExtendedFieldType

{

PropertyId = 0x8054,

PropertyIdSpecified = true,

DistinguishedPropertySetId = DistinguishedPropertySetType.Address,

DistinguishedPropertySetIdSpecified = true,

PropertyType = MapiPropertyTypeType.BinaryArray

};

Let’s take a look at these PathToExtendedFieldTypes:
  • ptefDisplayName: The name of the Distribution List
  • ptefDistributionListName: The name of the Distribution List
  • ptefFileUnder: Must be the same as the name of the Distribution List
  • ptefMembers:
    • The members extended property: This i a list of EntryIds of the objects corresponding to the members of the personal distribution list. Members of the personal distribution list can be other distribution lists, electronic addresses contained in a contact, global address list users or distribution lists, or one-off e-mail addresses. The format of each EntryId MUST be either a one-off EntryId or a Wrapped Entry Id. When setting this property, be sure to ensure its total size is less than 15,000 bytes. Since this property is a BinaryArray type it expects a string Array of Base64 strings. More info can be found in the Protocol Specifications on the MSDN site.
  • ptefOneOffMembers:
    • The property specifies the list of one-off EntryIds corresponding to the members of the personal distribution list. These one-off EntryIds encapsulate display names and e-mail addresses of the personal distribution list members. For each entry in this property, there should be an entry in the Members property. The size of this property must also be less than 15,000 bytes. Since this property is a BinaryArray type it expects a string Array of Base64 strings.
How the member entry id and the one off member entry id strings are built can also be found in the protocol specifications.
I’ll show you how I created the Member Entry Id:
First off you’ll need the ItemType of the contact we created in the previous post. To do that we can use the FindItemMethod or simply return the ItemType in the previous posts CreateContact method. So let us refactor that method a little bit:

private static ContactItemType CreateContact(string pGivenName, string pSurname, Gender pGender, string pTitle)

{

ContactItemType retval = null;

ContactItemType contact = new ContactItemType();

contact.GivenName = pGivenName;

contact.Surname = pSurname;

contact.DisplayName = contact.Subject = pGivenName + ” “ + pSurname;

contact.EmailAddresses = new EmailAddressDictionaryEntryType[1];

EmailAddressDictionaryEntryType email1 = new EmailAddressDictionaryEntryType();

email1.Key = EmailAddressKeyType.EmailAddress1;

email1.Value = “testUser@example.com”;

contact.EmailAddresses[0] = email1;

contact.ExtendedProperty = new ExtendedPropertyType[2];

ExtendedPropertyType gender = new ExtendedPropertyType();

gender.ExtendedFieldURI = new PathToExtendedFieldType();

gender.ExtendedFieldURI.PropertyTag = “0x3a4d”;

gender.ExtendedFieldURI.PropertyType = MapiPropertyTypeType.Short;

gender.Item = ((int)pGender).ToString();

ExtendedPropertyType title = new ExtendedPropertyType();

title.ExtendedFieldURI = new PathToExtendedFieldType();

title.ExtendedFieldURI.PropertyTag = “0x3a45”;

title.ExtendedFieldURI.PropertyType = MapiPropertyTypeType.String;

title.Item = pTitle;

contact.ExtendedProperty[0] = gender;

contact.ExtendedProperty[1] = title;

CreateItemType createItem = new CreateItemType();

createItem.Items = new NonEmptyArrayOfAllItemsType { Items = new[] { contact } };

DistinguishedFolderIdType folder = new DistinguishedFolderIdType();

folder.Id = DistinguishedFolderIdNameType.contacts;

TargetFolderIdType targetFolder = new TargetFolderIdType();

targetFolder.Item = folder;

createItem.SavedItemFolderId = targetFolder;

var response = esb.CreateItem(createItem);

//There is only one item in the createItem so the line below should work in this case

if (response.ResponseMessages.Items[0].ResponseClass == ResponseClassType.Success)

{

ItemInfoResponseMessageType rmt = (ItemInfoResponseMessageType)response.ResponseMessages.Items[0];

retval = (ContactItemType)rmt.Items.Items[0];

}

return retval;

}

To create a Distribution List with members you will need to find the EntryId when all we have is the ItemId and we want it in a Hex format. To achieve this I want to use the ConvertId Method on the ExchangeServiceBinding. The convertId Method accepts a ConvertIdType as a parameter. The ConvertIdType properties that are used here are the sourceIds and the destinationFormat. They kinda speak for themselves.
Lets take a look at some code again.

private static string CreateMemberEntryId(ItemType item)

{

byte[] retval = new byte[0];

string WrappedEntryIDPrefix = “00000000C091ADD3519DCF11A4A900AA0047FAA4C3”;

ConvertIdType convertReq = new ConvertIdType();

convertReq.DestinationFormat = IdFormatType.HexEntryId;

convertReq.SourceIds = new[]

{

new AlternateIdType()

{

Format = IdFormatType.EntryId,

Id = item.ItemId.Id

}

};

try

{

ConvertIdResponseType response = esb.ConvertId(convertReq);

ArrayOfResponseMessagesType aormt = response.ResponseMessages;

ResponseMessageType[] rmta = aormt.Items;

foreach (ConvertIdResponseMessageType resp in rmta)

{

if (resp.ResponseClass == ResponseClassType.Success)

{

ConvertIdResponseMessageType cirmt = resp;

AlternateIdType myId = (cirmt.AlternateId as AlternateIdType);

if (myId != null)

{

//Strip the last 48 bytes (96 chars) – this is actually the

folderId.

//Strip the first 7 bytes.

//then remove 1 byte right after the actual entryId (40 bytes)

//then the ChangeKey follows (in the rest of the string).

StringBuilder sb = new StringBuilder();

sb.Append(WrappedEntryIDPrefix);

sb.Append(“00000000”);

sb.Append(myId.Id.Substring(14, 84));

sb.Append(myId.Id.Substring(100, myId.Id.Length – 196));

retval = sb.ToString().ToByteArray();

}

}

else if (resp.ResponseClass == ResponseClassType.Error)

{

//TODO error Logging

}

else

{

//TODO warning logging

}

}

}

catch (Exception e)

{

//TODO error logging

}

return Convert.ToBase64String(retval);

}

You can see in previous code block that there is some comment:
Strip the last 48 bytes (96 chars) – this is actually the folderId.

Strip the first 7 bytes.

then remove 1 byte right after the actual entryId (40 bytes)

then the ChangeKey follows (in the rest of the string).

Don’t pay to much attention to this for the moment. If you want more information about this check out [MS-OXOCNTC]: Contact Object Protocol Specification.

The WrappedEntryIDPrefix is a fixed string except the last 2 characters. What it stands for you see in the protocol specification but in the MS-OXOMSG pdf.

They represent a bit settings which specifies what kind of email address the entry id is going to be. So if you change that you could be talking about another distribution list for instance.

We are going to use this MemberEntryId and Members extended property of a distribution list. As said before we’ll need the OneOffMembers entry’s also so we can put them in the same position as the MemberEntryId. The OneOffMembers entry is a bit less complicated.

private static string CreateOneOffMemberEntryId(ContactItemType contact)

{

var retval = new List<byte>();

var flags = Encoding.Unicode.GetBytes(“”);

var version = Encoding.Unicode.GetBytes(“”);

var pad = Encoding.Unicode.GetBytes(“”);

var muid = new byte[]

{

0x81, 0x2b, 0x1f, 0xa4, 0xbe,

0xa3, 0x10, 0x19, 0x9d, 0x6e,

0x00, 0xdd, 0x01, 0x0f, 0x54, 0x02

};

var wFlags = new byte[] {0x01, 0x90};

var first = Encoding.Unicode.GetBytes(contact.Subject + “(“ + contact.EmailAddresses[0].Value + “)”);

var middle = Encoding.Unicode.GetBytes(“UNKNOWN”);

var last = Encoding.Unicode.GetBytes(contact.EmailAddresses[0].Value);

retval.AddRange(flags);

retval.AddRange(muid);

retval.AddRange(version);

retval.AddRange(wFlags);

retval.AddRange(first);

retval.AddRange(pad);

retval.AddRange(middle);

retval.AddRange(pad);

retval.AddRange(last);

retval.AddRange(pad);

return Convert.ToBase64String(retval.ToArray());

}

Here we only need some actual strings from the contact to create the One Off Members entry. All the values except for wFlags, first, middle and last have fixed values. The wFlags should be changed to 0x8001 if you want to specify a distribution list. So it should read like this:

var wFlags = new byte[] {0x01, 0x80};

var first = Encoding.Unicode.GetBytes(distributionlist.Subject);

var middle = Encoding.Unicode.GetBytes(“MAPIPDL”);

var last = Encoding.Unicode.GetBytes(“Unknown”);

Now to actually create a distribution list with members we’ll need to create an Item that has all the correct Extended Properties:

private static void CreateDistributionList(string name, string description, ContactItemType contact)

{

ItemType list = new ItemType();

list.ItemClass = “IPM.DistList”;

list.Subject = name;

list.Body = new BodyType { BodyType1 = BodyTypeType.Text, Value = description };

List<string> members = new List<string>();

List<string> oneOffMembers = new List<string>();

members.Add(CreateMemberEntryId(contact));

members.Add(CreateOneOffMemberEntryId(contact));

List<ExtendedPropertyType> eProps = new List<ExtendedPropertyType>();

eProps.Add(new ExtendedPropertyType

{

ExtendedFieldURI = ptefDisplayName,

Item = name

});

eProps.Add(new ExtendedPropertyType

{

ExtendedFieldURI = ptefDistributionListName,

Item = name

});

eProps.Add(new ExtendedPropertyType

{

ExtendedFieldURI = ptefFileUnder,

Item = name

});

eProps.Add(new ExtendedPropertyType

{

ExtendedFieldURI = ptefMembers,

Item = new NonEmptyArrayOfPropertyValuesType

{

Items = members.ToArray()

}

});

eProps.Add(new ExtendedPropertyType

{

ExtendedFieldURI = ptefOneOffMembers,

Item = new NonEmptyArrayOfPropertyValuesType

{

Items = oneOffMembers.ToArray()

}

});

list.ExtendedProperty = eProps.ToArray();

}

I hope this was helpfull. Feel free to post comments and / or questions. I won’t bite

As you probably could see in the code this is not ready to ship…

There is alot more exception handling to be done and so forth, but in order to keep this document a bit more to the point I’ve skipped those parts.
Advertisements

During the past 2 weeks or so I’ve been experimenting with the Exchange Webservice (in Exchange 2007 SP1).

I’ve been trying to create a contact in the contacts folder of a user (testuser). I came accross some problems while trying to accomplish this. Specifically when I tried to set properties that are actually used by Outlook (client properties) and which are not necessarily used by Exchange 2007. But you can still access and even create properties all you want. Lets kick this blog off with the basics of working with the ews (Exchange WebService).

Some ground work:

  1. Create a new console app.
  2. Add the Web Reference to your Exchange Webservice (eg.: http://myMailServer/ews/Exchange.Asmx) and lets name it ExchangeService.
  3. Add the using to the ExchangeService

Now we can go and add a private member

private static ExchangeServiceBinding esb;

 

Initialize the ExchangeServiceBinding. You can insert credentials here in case of a https connection as seen in the example below.

esb = new ExchangeServiceBinding

{

    Credentials = new NetworkCredential

    {

        Domain = “example.com”,

        UserName = “testUser”,

        Password = “p455w0rd”

    },

    Url = https://192.168.2.143/EWS/Exchange.asmx&#8221;

};

 

esb.RequestServerVersionValue = new RequestServerVersion();

esb.RequestServerVersionValue.Version = ExchangeVersionType.Exchange2007_SP1;

 

As you can see in above code you need to set the server version, please do not ommit these 2 lines. If you do your code probably won’t work. In some cases you will get an error like this: 

System.Web.Services.Protocols.SoapException: The request is valid but does not specify the correct server version in the RequestServerVersion SOAP header. Ensure that the RequestServerVersion SOAP header is set with the correct RequestServerVersionValue.

Now that we have the base of the application we can start concentrating on creating a contact, so we’re going to create a method that does just that for us.

private static void CreateContact(string givenName, string surname)

{

    ContactItemType contact = new ContactItemType();

 

    contact.GivenName = givenName;

    contact.Surname = surname;

    contact.DisplayName = contact.Subject = givenName + ” “ + surname;

 

    contact.EmailAddresses = new EmailAddressDictionaryEntryType[1];

    EmailAddressDictionaryEntryType email1 = new EmailAddressDictionaryEntryType();

    email1.Key = EmailAddressKeyType.EmailAddress1;

    email1.Value = “testUser@example.com”;

    contact.EmailAddresses[0] = email1;

 

    CreateItemType createItem = new CreateItemType();

    createItem.Items = new NonEmptyArrayOfAllItemsType {Items = new[] {contact}};

 

    DistinguishedFolderIdType folder = new DistinguishedFolderIdType();

    folder.Id = DistinguishedFolderIdNameType.contacts;

 

    TargetFolderIdType targetFolder = new TargetFolderIdType();

    targetFolder.Item = folder;

 

    createItem.SavedItemFolderId = targetFolder;

 

    var response = esb.CreateItem(createItem);

}

 

You’ll see the contact being created in you testUser’s contacts folder.
If you don’t fill out the DisplayName, the contact will complain about it in outlook when you open the contact and then close it without editing anything. Outlook will try to save the contact item although we did not change anything. 

This was fairly easy to achieve, but just try to fill out all the contact’s porperties you need. You’ll soon find out that you need to use Extended Properties, because not all the properties are available on the ContactItemType type. I’ll show you how to add 2 Extended Properties. Firstly the Gender of the contact and secondly the Title. Insert following somewhere before the instantiation of the CreateItemType createItem.

 

    contact.ExtendedProperty = new ExtendedPropertyType[2];

 

    ExtendedPropertyType gender = new ExtendedPropertyType();

    gender.ExtendedFieldURI = new PathToExtendedFieldType();

    gender.ExtendedFieldURI.PropertyTag = “0x3a4d”;

    gender.ExtendedFieldURI.PropertyType = MapiPropertyTypeType.Short;

    gender.Item = “1”;

 

    ExtendedPropertyType title = new ExtendedPropertyType();

    title.ExtendedFieldURI = new PathToExtendedFieldType();

    title.ExtendedFieldURI.PropertyTag = “0x3a45”;

    title.ExtendedFieldURI.PropertyType = MapiPropertyTypeType.String;

    title.Item = “Dr.”;

 

    contact.ExtendedProperty[0] = gender;

    contact.ExtendedProperty[1] = title;

 

As you can see both these Extended Properties have a PropertyTag. This is not always the case. In other properties you’ll need to use the PropertyId and DistinguisedPropertyId, but we’ll see samples of that in a later post in which I am going to discuss how you need to create a DistributionList with the contact created here as the only member.

The Property Tags and Ids can be found in the pdf’s concerning the exchange protocol on msdn or through a nice tool that helped me alot these past weeks: OutlookSpy.

That’s it for now. I hope it was clear and helpfull in some way.
Until next time…