Skip to main content

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)]
[System.ComponentModel.ToolboxItem(false)]
// 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
{

[WebMethod]
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);
emlwriter.WriteLine(emlMessage);
emlwriter.Close();
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;
}
else
{
//Deal with error
}
}
return omOrigMessage;
}
}
}

Popular posts from this blog

The MailboxConcurrency limit and using Batching in the Microsoft Graph API

If your getting an error such as Application is over its MailboxConcurrency limit while using the Microsoft Graph API this post may help you understand why. Background   The Mailbox  concurrency limit when your using the Graph API is 4 as per https://docs.microsoft.com/en-us/graph/throttling#outlook-service-limits . This is evaluated for each app ID and mailbox combination so this means you can have different apps running under the same credentials and the poor behavior of one won't cause the other to be throttled. If you compared that to EWS you could have up to 27 concurrent connections but they are shared across all apps on a first come first served basis. Batching Batching in the Graph API is a way of combining multiple requests into a single HTTP request. Batching in the Exchange Mail API's EWS and MAPI has been around for a long time and its common, for email Apps to process large numbers of smaller items for a variety of reasons.  Batching in the Gr...

Sending a MimeMessage via the Microsoft Graph using the Graph SDK, MimeKit and MSAL

One of the new features added to the Microsoft Graph recently was the ability to create and send Mime Messages (you have been able to get Message as Mime for a while). This is useful in a number of different scenarios especially when trying to create a Message with inline Images which has historically been hard to do with both the Graph and EWS (if you don't use MIME). It also opens up using SMIME for encryption and a more easy migration path for sending using SMTP in some apps. MimeKit is a great open source library for parsing and creating MIME messages so it offers a really easy solution for tackling this issue. The current documentation on Send message via MIME lacks any real sample so I've put together a quick console app that use MSAL, MIME kit and the Graph SDK to send a Message via MIME. As the current Graph SDK also doesn't support sending via MIME either there is a workaround for this in the future my guess is this will be supported.

Export calendar Items to a CSV file using Microsoft Graph and Powershell

For the last couple of years the most constantly popular post by number of views on this blog has been  Export calendar Items to a CSV file using EWS and Powershell closely followed by the contact exports scripts. It goes to show this is just a perennial issue that exists around Mail servers, I think the first VBS script I wrote to do this type of thing was late 90's against Exchange 5.5 using cdo 1.2. Now it's 2020 and if your running Office365 you should really be using the Microsoft Graph API to do this. So what I've done is create a PowerShell Module (and I made it a one file script for those that are more comfortable with that format) that's a port of the EWS script above that is so popular. This script uses the ADAL library for Modern Authentication (which if you grab the library from the PowerShell gallery will come down with the module). Most EWS properties map one to one with the Graph and the Graph actually provides better information on recurrences then...
All sample scripts and source code is provided by for illustrative purposes only. All examples are untested in different environments and therefore, I cannot guarantee or imply reliability, serviceability, or function of these programs.

All code contained herein is provided to you "AS IS" without any warranties of any kind. The implied warranties of non-infringement, merchantability and fitness for a particular purpose are expressly disclaimed.