Tuesday, October 14, 2008

WebService to release Quarantined messages in Exchange 2007

This post follows on from the quarantine digest message post last month and looks at the challeges around resubmitting a Quarantined message. Outlook 2007 and OWA 2007 have the ability to resend a message from the Quarantine mailbox using the Resend facilities that are documented in http://msdn.microsoft.com/en-us/library/ms527630(EXCHG.10).aspx. Unfortunately you can't do this using Exchange Web Services because EWS doesn't give you full access to to the recipient collection so this means its not possible to set the PR_RECIPIENT_TYPE for each recipient you want the message delivered to and to also mark those message recipients you dont want the message delivered to. One interesting piece of functionality the quarantine mailbox has is the ability to sendas any email address be it Internal or External eg all you need to do is set the following Mapi property on the message your sending and this will become the sender of the message when you send it. Note this will only work for the quarantine mailbox

ExtendedPropertyType osOrginalSender = new ExtendedPropertyType();
PathToExtendedFieldType epExPath = new PathToExtendedFieldType();
epExPath.DistinguishedPropertySetIdSpecified = true;
epExPath.DistinguishedPropertySetId = DistinguishedPropertySetType.PublicStrings;
epExPath.PropertyName = "quarantine-original-sender";
epExPath.PropertyType = MapiPropertyTypeType.String;
osOrginalSender.ExtendedFieldURI = epExPath;
osOrginalSender.Item = "";
Message.ExtendedProperty = new ExtendedPropertyType[1];
Message.ExtendedProperty[0] = osOrginalSender;

Back on track if you want to release a quarantined message you have the option of using Mapi or using the Replay directory on a Hub Role Server. There is a good description of the Replay directory here what makes the replay directory usefull for the task of resubmitting a quarantined message is that it allows specifying X-Sender and X-Reciever to allow you to specify who this message is delievered to ignoring the existing To and From Mail headers. With Quarantined messages the actual message that is quarantined is an attachment of the NDR message that is delivered to the quarantined mailbox. To to be able to submit the message to the replay directory I've come up with a WebService that uses EWS and takes one parameter which is the ItemID of the quarantine report message. It then Grabs this message using EWS looks at the attachments on the message and grabs the original message and then writes X-headers to represent the Sender and Recipients the message is to be delivered to. The messsage is then saved to the replay directory on the Hub Server.

This is still pretty rough and needs more work but this is the skeleton I've put a download here the code looks like.

using System;
using System.Collections.Generic;
using System.Web;
using System.Net;
using System.Web.Services;
using System.Security.Cryptography.X509Certificates;
using System.Net.Security;
using WebService1.ews;
using System.IO;

namespace WebService1
/// Summary description for Service1

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
// To allow this Web Service to be called from script, using ASP.NET AJAX, uncomment the following line.
// [System.Web.Script.Services.ScriptService]
public class Service1 : System.Web.Services.WebService

public string ResubmitMessage(String MessageID)
ServicePointManager.ServerCertificateValidationCallback =
delegate(Object obj, X509Certificate certificate, X509Chain chain, SslPolicyErrors errors)
// Replace this line with code to validate server certificate.
return true;
string res = "";
ExchangeServiceBinding esb = new ExchangeServiceBinding();
esb.RequestServerVersionValue = new RequestServerVersion();
esb.RequestServerVersionValue.Version = ExchangeVersionType.Exchange2007_SP1;
esb.Credentials = new NetworkCredential("user", "password", "domain");
esb.Url = @"https://casserver/EWS/Exchange.asmx";
MessageType[] oMessages = GetQuantined(esb, MessageID);
if (oMessages[0] != null & oMessages[1] != null)
byte[] MimeContent = Convert.FromBase64String(oMessages[1].MimeContent.Value);
System.Text.ASCIIEncoding asEncoding = new System.Text.ASCIIEncoding();
String xsXSender = "X-Sender: <" + oMessages[0].ExtendedProperty[0].Item.ToString() + ">";
String xrXreciever = "";
foreach (EmailAddressType recp in oMessages[0].ToRecipients)
xrXreciever = xrXreciever + "X-Receiver: <" + recp.EmailAddress + ">" + "\r\n";
String emlMessage = xsXSender + "\r\n" + xrXreciever + asEncoding.GetString(MimeContent);
String MessageGuid = Guid.NewGuid().ToString();
MessageGuid = MessageGuid + DateTime.Now.Ticks.ToString();
StreamWriter emlwriter = new StreamWriter((@"C:\Program Files\Microsoft\Exchange Server\TransportRoles\replay\" + MessageGuid + "eml"), true);
res = "Email Written to Replay Directory";

else {
res = "Error geting message";
return res;
private MessageType[] GetQuantined(ExchangeServiceBinding esb,String MessageID) {
MessageType[] msMessages = new MessageType[2];
ItemIdType iiItemId = new ItemIdType();
iiItemId.Id = MessageID;
ItemIdType[] giGetItemArray = new ItemIdType[1];
giGetItemArray[0] = iiItemId;
BasePathToElementType[] beAdditionproperteis = new BasePathToElementType[1];
PathToExtendedFieldType osOriginalSenderEmail = new PathToExtendedFieldType();
osOriginalSenderEmail.PropertyTag = "0x0067";
osOriginalSenderEmail.PropertyType = MapiPropertyTypeType.String;
beAdditionproperteis[0] = osOriginalSenderEmail;
ItemResponseShapeType isItemResponseShape = new ItemResponseShapeType();
isItemResponseShape.BaseShape = DefaultShapeNamesType.AllProperties;
GetItemType giGetItem = new GetItemType();
isItemResponseShape.AdditionalProperties = beAdditionproperteis;
giGetItem.ItemShape = isItemResponseShape;
giGetItem.ItemIds = giGetItemArray;
GetItemResponseType giGetItemResponse = esb.GetItem(giGetItem);
if (giGetItemResponse.ResponseMessages.Items[0].ResponseClass == ResponseClassType.Success)
ItemType miNDRItem = ((ItemInfoResponseMessageType)giGetItemResponse.ResponseMessages.Items[0]).Items.Items[0];
msMessages[0] = (MessageType)miNDRItem;
foreach (AttachmentType atAttachment in miNDRItem.Attachments)
if (atAttachment.ContentType == "message/rfc822")
msMessages[1] = GetOrigMsg(esb, atAttachment.AttachmentId);


else {
// Handle Error
return msMessages;
private MessageType GetOrigMsg(ExchangeServiceBinding esb, AttachmentIdType atAttachID)
MessageType omOrigMessage = null;
GetAttachmentType gaGetAttachment = new GetAttachmentType();
AttachmentIdType[] atArray = new AttachmentIdType[1];
atArray[0] = atAttachID;
gaGetAttachment.AttachmentShape = new AttachmentResponseShapeType();
gaGetAttachment.AttachmentShape.IncludeMimeContent = true;
gaGetAttachment.AttachmentShape.IncludeMimeContentSpecified = true;
gaGetAttachment.AttachmentIds = atArray;
GetAttachmentResponseType gaGetAttachmentResponse = esb.GetAttachment(gaGetAttachment);
foreach (AttachmentInfoResponseMessageType atAttachmentResponse in gaGetAttachmentResponse.ResponseMessages.Items)
if (atAttachmentResponse.ResponseClass == ResponseClassType.Success)
ItemAttachmentType itIemAttachmentType = atAttachmentResponse.Attachments[0] as ItemAttachmentType;
omOrigMessage = (MessageType)itIemAttachmentType.Item;
//Deal with error
return omOrigMessage;


Anonymous said...

Do you have power shell command that will grant right weather they are delegate or full access on a mailbox for mult users with out using a pile csv file.

Glen said...

What type of delegate are you talking about Outlook delegates ?