This transport agent identifies a message it wants to reject by looking for “[TEST]” in the subject. If it finds a message to reject, it creates a new message to be sent back to the sender and attaches the original message being rejected to the new message. To send the new message back to the sender of the original message it saves the new message to the pickup folder and deletes the original message so it won’t be sent.
Enjoy…
using System; using System.Collections.Generic; using System.Diagnostics; using System.Text; using System.IO; using Microsoft.Exchange.Data.Transport; using Microsoft.Exchange.Data.Transport.Email; using Microsoft.Exchange.Data.Transport.Routing; namespace CustomNDR { public sealed class CustomNDRAgentFactory : RoutingAgentFactory { public override RoutingAgent CreateAgent(SmtpServer server) { return new CustomNDR(); } } public class CustomNDR : RoutingAgent { // Place this text in the subject to test the agent public const string TestToken = "[TEST]"; public const string NdrSubject = "Rejected Message - Original Attached"; public const string OutputPath = @"C:\Users\Administrator\Desktop\CustomNDR\output\"; public const string PickupPath = @"C:\Program Files\Microsoft\Exchange Server\TransportRoles\Pickup\"; public CustomNDR() { LogMessage("CustomNDR{}", "Enter"); this.OnRoutedMessage += new RoutedMessageEventHandler(CustomNDR_OnRoutedMessage); this.OnSubmittedMessage += new SubmittedMessageEventHandler(CustomNDR_OnSubmittedMessage); this.OnResolvedMessage += new ResolvedMessageEventHandler(CustomNDR_OnResolvedMessage); LogMessage("CustomNDR()", "Leave"); } void CustomNDR_OnSubmittedMessage( SubmittedMessageEventSource source, QueuedMessageEventArgs e) { LogMessage("OnSubmittedMessage", "Enter"); try { // Don't process EVERY message... if (e.MailItem.Message.Subject.Contains(TestToken)) { EmailMessage origMsg = e.MailItem.Message; LogMessage("OnSubmittedMessage", String.Concat("Message: ", origMsg.Subject)); if (Directory.Exists(OutputPath)) { // Save a copy of the original message for debug //SaveMessage(origMsg, String.Concat(OutputPath, "Original.eml")); EmailMessage newMsg = EmailMessage.Create(); newMsg.To.Add(new EmailRecipient( origMsg.Sender.DisplayName, origMsg.Sender.SmtpAddress)); newMsg.From = new EmailRecipient("Service Account", "sa@zilladom12.extest.microsoft.com"); newMsg.Subject = NdrSubject; // Setting the contentType to 'message/rfc822' // is the key to avoid an InvalidOperationException // with a message 'Cannot set the property, // the attachment is not an embedded message.' Attachment attach = newMsg.Attachments.Add( "RejectedMessage", "message/rfc822"); attach.EmbeddedMessage = origMsg; // Save a copy of the NDR message for debug //SaveMessage(newMsg, String.Concat(OutputPath, "newMsg.eml")); // Save message to the pickup directory to send it SaveMessage(newMsg, String.Concat(PickupPath, "newMsg.eml")); // Cancel the original message source.Delete(); } else { LogMessage("OnSubmittedMessage", "OutputPath does not exist."); } } } catch (Exception ex) { LogMessage("OnSubmittedMessage", String.Format("EXCEPTION: Type={0}, Message='{1}'", ex.GetType().FullName, ex.Message)); } LogMessage("OnSubmittedMessage", "Leave"); } void CustomNDR_OnResolvedMessage( ResolvedMessageEventSource source, QueuedMessageEventArgs e) { LogMessage("OnResolvedMessage", "Enter"); LogMessage("OnResolvedMessage", "Leave"); } void CustomNDR_OnRoutedMessage( RoutedMessageEventSource source, QueuedMessageEventArgs e) { LogMessage("OnRoutedMessage", "Enter"); LogMessage("OnRoutedMessage", "Leave"); } public static void SaveMessage(EmailMessage msg, string filePath) { try { FileStream file = new FileStream(filePath, System.IO.FileMode.Create); msg.MimeDocument.WriteTo(file); file.Close(); } catch { LogMessage("SaveMessage", String.Format("Failed to save message, {0}, to {1}.", msg.Subject, filePath)); throw; } } public static void LogMessage(string methodName, string message) { try { StringBuilder traceMessage = new StringBuilder(); traceMessage.Append(System.DateTime.Now.ToString("s")); traceMessage.Append("|"); traceMessage.Append(methodName); traceMessage.Append("|"); traceMessage.Append(message); traceMessage.Append("\r\n"); System.Diagnostics.Debug.WriteLine(traceMessage.ToString()); File.AppendAllText( String.Concat(OutputPath, "log.txt"), traceMessage.ToString()); } catch (Exception ex) { Debug.WriteLine(String.Format("Failed to log message, {0}, to 'log.txt' in {1}.", message, OutputPath)); System.Diagnostics.Debug.WriteLine(String.Format("Exception: {0}", ex.Message)); } } } }
EWSEditor 1.6.1 is now available for download at the EWSEditor Code Gallery project site!
API coverage is a major focus of the 1.6.* releases of EWSEditor. The 1.6.1 release adds an OOF Settings and Availability form to this end. There were several bugs addressed in this release was well as some changes in the error dialog and about information. These changes will make the information displayed and logged by EWSEditor more useful.
Here is the change list from the download page for this release…
Issues BUG: Untrapped exception when using a self signed certificate on Exchange 2010 BUG: NullReferenceException in ServiceDialog BUG: Autodiscover URL cleared out BUG: ConvertFormToExtendedPropDef makes too many assumptions about control state BUG: Contents grid for attachments should refresh after deleting an attachment BUG: Version dialog says 1.5.0.2. Contrast with Filever output BUG: Double click TreeViewNode opens wrong folder Features FEATURE: OOF Settings Form FEATURE: Add availability form Tasks DCR: EWS Managed API dialog warning should have download link DCR: About EWSEditor doesn't give the EWSEditor website DCR: Distribute EWS API binary as part of release DCR: Simplify copying an exception to the clipboard from the ErrorDialog DCR: F1 should bring up the About dialog. DCR: The AboutDialog should have links to blog content and project site DCR: New Error dialog with "copyable" text.
Issues BUG: Untrapped exception when using a self signed certificate on Exchange 2010 BUG: NullReferenceException in ServiceDialog BUG: Autodiscover URL cleared out BUG: ConvertFormToExtendedPropDef makes too many assumptions about control state BUG: Contents grid for attachments should refresh after deleting an attachment BUG: Version dialog says 1.5.0.2. Contrast with Filever output BUG: Double click TreeViewNode opens wrong folder
Features FEATURE: OOF Settings Form FEATURE: Add availability form
Tasks DCR: EWS Managed API dialog warning should have download link DCR: About EWSEditor doesn't give the EWSEditor website DCR: Distribute EWS API binary as part of release DCR: Simplify copying an exception to the clipboard from the ErrorDialog DCR: F1 should bring up the About dialog. DCR: The AboutDialog should have links to blog content and project site DCR: New Error dialog with "copyable" text.