Exchange Web Services (2007) – Part Two

This is the second part of a two part series describing how to connect to an Exchange Server, monitor for new mail, and locally download attachments.  The previous post described how to connect to the server and create a subscription detailing what notifications you wanted to receive from the server.  This post will continue on with actually requesting those notifications.

So every poll tick, we want to get any new notifications.  The GetSubscriptionItems() function gets any new event notifications, loops through them and retrieves the actual mail message, then if it has attachments, retrieves the attachments.

protected void GetSubscriptionItems()
{
BaseNotificationEventType[] events = GetEvents(SubscriptionInfo);

// Go through the event list that was returned. The server always returns at least one Status event
// which let's the caller know that the subscription is still alive (in push subscriptions scenarios,
// the Status even is sent periodically as a heart beat signal).
// In this example, we are only interested in ObjectChanged events, so we'll ignore any other event type.
foreach (BaseNotificationEventType evt in events)
{
BaseObjectChangedEventType objectChangedEvent = evt as BaseObjectChangedEventType;
if (objectChangedEvent != null)
{
ItemType item = GetItem(((ItemIdType)objectChangedEvent.Item).Id);
Logger.WriteLog("Received new message: " + item.Subject);
AttachmentType[] attachments = item.Attachments;
if (attachments != null && attachments.Length > 0)
GetAttachments((MessageType)item, attachments);
}
}
}

The GetEvents() function of course is the initial request to the Exchange service to get any new updates. In it you first create a GetEventsType object, similarly to all the other requests. Then you assign it the most recent watermark and the subscriptionId – the watermark updates with every request. Then after checking for errors, you use the GetEventsResponseType property Notifications.Items array as the return value with any actual notifications.

///<summary> /// Gets the latest events for a specific subscription.
/// </summary>
/// Subscription for whic to get the latest events.
/// Array of notification events.
public BaseNotificationEventType[] GetEvents(SubscriptionInformation subscriptionInfo)
{
// Create a GetEvents request
GetEventsType request = new GetEventsType();

// Setup the request
request.SubscriptionId = subscriptionInfo.Id;
request.Watermark = subscriptionInfo.Watermark;

// Call the GetEvents EWS method
GetEventsResponseType response = ServiceBinding.GetEvents(request);

// Extract the first response message that contains the information we need.
GetEventsResponseMessageType responseMessage = response.ResponseMessages.Items[0] as GetEventsResponseMessageType;

this.ThrowOnError("GetEvents", responseMessage);

// Update the watermark of the subscription info.
// GetEvents returns an updated watermark with every event. The latest watermark has to be passed back the next
// time GetEvents is called.
subscriptionInfo.Watermark = responseMessage.Notification.Items[responseMessage.Notification.Items.Length - 1].Watermark;

// Retrun the array of events.
return responseMessage.Notification.Items;
}

Back in GetSubscriptionItems, each notification is checked to ensure it is the correct type and the corresponding actual email is retrieved from the server, via the GetItem function. An ItemType actual encompasses emails, calendar items, tasks, and contacts. In this case we’re only interested in MessageType objects, so the results of this get cast later.

You’ll also notice that I’m adding an AdditionalProperty to the BaseShape. By adding the itemMimeContent to the BaseShape, the retrieved item will be able to be serialized to an .eml file and saved locally as a backup. .msg files cannot be saved. The rest of the code follows the fairly standard Exchange service procedure – make a GetX object, set the properties, get a GetXReponse from the function call on the ServiceBinding, check for errors, then return the result.

///<summary>
/// Retrieves the details of an item on the server.
/// </summary>
///Id of the item to retrieve the details of.
/// ItemType object containing the details of the item.
public ItemType GetItem(string id)
{
// Create a GetItem request object
GetItemType request = new GetItemType();

// Setup the request object:
// Set the response shape so it includes all of the item's properties.
// Two other base shapes are provided: IdOnly and Default
request.ItemShape = new ItemResponseShapeType();
request.ItemShape.BaseShape = DefaultShapeNamesType.AllProperties;
request.ItemShape.AdditionalProperties = new BasePathToElementType[1];
PathToUnindexedFieldType prop = new PathToUnindexedFieldType();
prop.FieldURI = UnindexedFieldURIType.itemMimeContent;
request.ItemShape.AdditionalProperties[0] = prop;

// Setup the array of item ids that we want to retrieve (only one item in this
// example)
ItemIdType itemId = new ItemIdType();
itemId.Id = id;

request.ItemIds = new BaseItemIdType[1];
request.ItemIds[0] = itemId;

// Call the GetItem EWS method, passing it the request that we just set up.
GetItemResponseType response = ServiceBinding.GetItem(request);

// Extract the first response message that contains the information we need.
ItemInfoResponseMessageType responseMessage = response.ResponseMessages.Items[0] as ItemInfoResponseMessageType;

// Fail if that response message indicates an error.
this.ThrowOnError("GetItem", responseMessage);

// Finally, return the item detail.
return responseMessage.Items.Items[0];
}

After getting the actual email message, then any attachments to that email are retrieved. This is done by creating a GetAttachmentType object and setting IncludeMimeContent to true on the AttachmentResponseShapeType. Then you set the array of RequestAttachentIdType to the AttachmentIds property of the GetAttachmentType object, and make the actual request to the ServiceBinding object.

Cycle through the results and verify that each does indeed have a file attachment and that it is the desired type (pdf in my case), then save each pdf to disk.

protected void GetAttachments(MessageType item, AttachmentType[] attachments)
{
GetAttachmentType getAttachment = new GetAttachmentType();

AttachmentResponseShapeType shape = new AttachmentResponseShapeType();
shape.IncludeMimeContent = true;
shape.IncludeMimeContentSpecified = true;
getAttachment.AttachmentShape = shape;

Listids = new List();

foreach (AttachmentType attachment in attachments)
{
if (attachment.ContentType == "application/pdf")
{
FileAttachmentType pdf = (FileAttachmentType)attachment;
Logger.WriteLog("PDF found: " + pdf.Name);
AttachmentIdType id = new AttachmentIdType();
id.Id = pdf.AttachmentId.Id;
ids.Add(id);
}
}

getAttachment.AttachmentIds = ids.ToArray();
Logger.WriteLog("Requesting PDFs (" + ids.Count + ")");
GetAttachmentResponseType response = ServiceBinding.GetAttachment(getAttachment);

foreach (AttachmentInfoResponseMessageType attachmentInfo in response.ResponseMessages.Items)
{
// Ensure attachment is File
if (attachmentInfo.Attachments[0].GetType().Name == "FileAttachmentType")
{
FileAttachmentType file = (FileAttachmentType)attachmentInfo.Attachments[0];
if (file.ContentType == "application/pdf")
{
string attachmentName = attachmentInfo.Attachments[0].Name.Substring(0, attachmentInfo.Attachments[0].Name.LastIndexOf('.'));
SaveFile(file.Content, attachmentName);
}
}
}
}

Voila! You’ve successfully retrieved and downloaded pdf attachments from all incoming email that you’ve subscribed to. Hopefully these two posts have been useful to you if you’re trying to set up a connection to and Exchange server and still back on 2007. I did skim over a few area – GetFolderByPath comes to mind – and I plan on addressing that in a later post.

Leave a Reply

Your email address will not be published. Required fields are marked *