HOWTO: Return To Sender Transport Agent Sample

  • Comments 2

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));
            }
        }
    }

}
  • String.Concat? Really? FYI: str1 + str2 compiles down to a String.Concat anyway and is a lot more readable!

  • Actually, I wasn't aware that had changed in Framework 3.5 but you are right - str1 + str2 is in fact equivalent to String.Concat(str1, str2) now.  I've always been in the habit of using String.Format and String.Concat due to the inefficiences of adding strings with the '+' operator pre-3.5.  

    Since it's all the same in the IL there's really no performance value.  I guess using '+' is a bit more readable maybe I'll use that in the future.

Page 1 of 1 (2 items)