Tuesday, November 18, 2008

Exchange web services 2007

A few snippets from my weekly status database that show how to interact with EWS 2007.

First, you need to generate EWS 2007 proxy. In Visual Studio it is relatively easy: Project->Add service reference, enter the address of your exchange web server, for example, https://mail.microsoft.com/EWS/Exchange.asmx (you won't be able to use this one because it is obviously secured). Hit Go, then name the namespace and hit OK.

One problem with EWS 2007 is that it unfortunately requires user credentials - cleartext (they are sent using HTTPS, so the wire security is not a problem, but the user has to enter the password/user name, or the software needs to CryptProtectData the credentials and store them. Windows auth is not available.

See here for a snippet of code that stores the password SECURELY: http://1-800-magic.blogspot.com/2008/11/encrypting-data-in-net.html. Make sure you're running under real user account (not LocalService, and definitely not NetworkSerivce), otherwise anybody will be able to decrypt the password.

Once the proxy has been generated, and put in the namespace, and the namespace has been included in the file with "using" directive, we can start coding.

First, we need to bind to the service. Binding is required for all examples below:
const string EXCHANGE_URL = @"https://mail.mydomain.com/EWS/Exchange.asmx";
ExchangeServiceBinding binding = new ExchangeServiceBinding();
binding.Credentials = new NetworkCredential(User, Password, Domain);
binding.Url = EXCHANGE_URL;
Looking up a subfolder of inbox:
DistinguishedFolderIdType inbox = new DistinguishedFolderIdType();
inbox.Id = DistinguishedFolderIdNameType.inbox;
BaseFolderIdType[] parentFolder = new BaseFolderIdType[1];
parentFolder[0] = inbox;

FindFolderType folderRequest = new FindFolderType();
folderRequest.FolderShape = new FolderResponseShapeType();
folderRequest.FolderShape.BaseShape = DefaultShapeNamesType.AllProperties;
folderRequest.Traversal = FolderQueryTraversalType.Shallow;
folderRequest.ParentFolderIds = parentFolder;

FindFolderResponseType folderResponse = binding.FindFolder(folderRequest);
foreach (ResponseMessageType responseMessage in folderResponse.ResponseMessages.Items)
{
FindFolderResponseMessageType findFolderResponseMessage =
responseMessage as FindFolderResponseMessageType;
if (findFolderResponseMessage.ResponseClass == ResponseClassType.Success)
{
BaseFolderType[] folders = findFolderResponseMessage.RootFolder.Folders;
foreach (BaseFolderType folder in folders)
{
if (folder.DisplayName.Equals(folderWeAreLookingFor))
{
...do stuff
}
}
}
}
Here's how to get all email (we're only getting the ids of messages, the contents needs to be queried using a different request, covered later:

DistinguishedFolderIdType inbox = new DistinguishedFolderIdType();
inbox.Id = DistinguishedFolderIdNameType.inbox;
BaseFolderIdType[] parentFolder = new BaseFolderIdType[1];
parentFolder[0] = inbox;

FindItemType listRequest = new FindItemType();
listRequest.ItemShape = new ItemResponseShapeType();
listRequest.ItemShape.BaseShape = DefaultShapeNamesType.AllProperties;
listRequest.Traversal = ItemQueryTraversalType.Shallow;
listRequest.ParentFolderIds = parentFolder;

FindItemResponseType listResponse = binding.FindItem(listRequest);

List items = new List();

foreach (ResponseMessageType responseMessage in listResponse.ResponseMessages.Items)
{
FindItemResponseMessageType findItemResponseMessage =
responseMessage as FindItemResponseMessageType;
if (findItemResponseMessage.ResponseClass == ResponseClassType.Success)
{
ArrayOfRealItemsType realItems =
findItemResponseMessage.RootFolder.Item as ArrayOfRealItemsType;
if (realItems != null && realItems.Items != null)
{
foreach (ItemType item in realItems.Items)
{
if (item is MessageType)
{
MessageType message = item as MessageType;
items.Add(message.ItemId);
}
}
}
}
}
Now, suppose we accumulated all message ids that we want to get in the items list, (we presume that items.Count() > 0). We will now get all the messages as follows:
GetItemType getRequest = new GetItemType();
getRequest.ItemShape = new ItemResponseShapeType();
getRequest.ItemShape.BaseShape = DefaultShapeNamesType.AllProperties;
getRequest.ItemIds = items.ToArray();

SnippetsDataContext snippets = new SnippetsDataContext(Database);

GetItemResponseType getResponse = binding.GetItem(getRequest);
foreach (ResponseMessageType responseMessage in getResponse.ResponseMessages.Items)
{
ItemInfoResponseMessageType getItemResponseMessage =
responseMessage as ItemInfoResponseMessageType;
if (getItemResponseMessage.ResponseClass == ResponseClassType.Success)
{
ArrayOfRealItemsType realItems =
getItemResponseMessage.Items as ArrayOfRealItemsType;
if (realItems != null && realItems.Items != null)
{
foreach (ItemType item in realItems.Items)
{
if (item is MessageType)
{
MessageType message = item as MessageType;
// Message now contains everything, except for attachments.
...
}
}
}
}
}
Suppose we want to move a message to a different folder. We need to find folder's id (see above for how to enumerate folders). If we do have this id, and the id of the message we're trying to move:

MoveItemType moveRequest = new MoveItemType();
moveRequest.ToFolderId = new TargetFolderIdType();
moveRequest.ToFolderId.Item = targetFolder;
moveRequest.ItemIds = new BaseItemIdType[1];
moveRequest.ItemIds[0] = messageId;
MoveItemResponseType moveResponse = binding.MoveItem(moveRequest);
Ok, enough moving stuff around. Let's send something!

ExchangeServiceBinding binding = new ExchangeServiceBinding();
binding.Credentials = new NetworkCredential(User, Password, Domain);
binding.Url = EXCHANGE_URL;

DistinguishedFolderIdType folder = new DistinguishedFolderIdType();
folder.Id = DistinguishedFolderIdNameType.sentitems;

TargetFolderIdType targetFolder = new TargetFolderIdType();
targetFolder.Item = folder;

CreateItemType createItem = new CreateItemType();
createItem.MessageDisposition = MessageDispositionType.SendAndSaveCopy;
createItem.MessageDispositionSpecified = true;
createItem.SavedItemFolderId = targetFolder;

MessageType message = new MessageType();

EmailAddressType recipient = new EmailAddressType();
recipient.EmailAddress = "email@domain.com";

message.ToRecipients = new EmailAddressType[1];
message.ToRecipients[0] = recipient;
message.Subject = "Hello, World!";
message.Sensitivity = SensitivityChoicesType.Normal;

message.Body = new BodyType();
message.Body.BodyType1 = BodyTypeType.Text;
message.Body.Value = "This is my first automated email!";

createItem.Items = new NonEmptyArrayOfAllItemsType();
createItem.Items.Items = new MessageType[1];
createItem.Items.Items[0] = message;
binding.CreateItem(createItem);
Many messages can be acted upon (moved, sent, deleted) by one command - all it takes is putting multiple messages or ids in Items.Items array.

Dealing with the web service is A LOT of code - even with the help of the proxy. Comare this to the Office interop, where most of the above tasks take 10-15 lines of code. http://1-800-magic.blogspot.com/2008/11/fun-with-outlook-and-net.html

More on EWS 2007 here: http://msdn.microsoft.com/en-us/library/bb418726.aspx

1 comment:

Alex said...

At work with outlook usually I use easy passwords,but once I used difficult and forget it.In this situation for myself helped next tool-outlook 2000 pst recover password.It myself advised familiar.As he said it is free and can also recover forgotten or lost passwords for email accounts as well as for files with *.pst extension.