How to Reject a Recipient or a Message in a Transport Agent

This content is no longer actively maintained. It is provided as is, for anyone who may still be using these technologies, with no warranties or claims of accuracy with regard to the most recent product version or service release.

Topic Last Modified: 2008-07-25

By Saeed Noursalehi, Software Development Engineer, and Ray Dixon, Programming Writer

One common use for transport agents is to develop various forms of message hygiene features, such as anti-spam and antivirus functionality. These agents often have to reject a recipient or message in various ways. This article discusses the different methods that you can use to reject recipients and messages from an SmtpReceiveAgent or a RoutingAgent.

This article provides information about the following:

Rejecting Recipients or Messages from an SmtpReceiveAgent

An SmtpReceiveAgent handles events that are raised by the SMTP protocol. For example, the agent can handle the OnRcptCommand event to get invoked on each RCPT command, or it can handle the OnEndOfData command to get invoked after all the SMTP commands and the message body have been received.

You can prevent a message from being delivered to a recipient during an SMTP session in several different ways.

Rejecting a Recipient in the OnRcptCommand Event

The agent can handle the OnRcptCommand event if it wants to reject recipients at the protocol level. If the agent decides that it does not want to allow the message to be delivered to this recipient, but it doesn’t want to block the whole message, it can reject the current recipient. This results in a rejection of the recipient at the SMTP protocol level; therefore, a DSN does not have to be generated.

For example, the following agent handles the OnRcptCommand event, determines whether the current recipient should be blocked, and then calls a method on the source object to block the recipient, if necessary.

    public class SampleSmtpReceiveAgent : SmtpReceiveAgent
    {
        private static SmtpResponse rejectResponse =
            new SmtpResponse("500", "", "Recipient blocked");

        SampleSmtpReceiveAgent()
        {
            this.OnRcptCommand += new RcptCommandEventHandler(
                this.OnRcptCommandHandler);
        }

        private void OnRcptCommandHandler(
            ReceiveCommandEventSource source,
            RcptCommandEventArgs e)
        {
            if (this.ShouldBlockRecipient(
                    e.MailItem,
                    e.RecipientAddress))
            {
                source.RejectCommand(
                    SampleSmtpReceiveAgent.rejectResponse);
            }
        }

        private bool ShouldBlockRecipient(
            MailItem mailItem,
            RoutingAddress recipient)
        {
            // TODO: put logic here to decide whether to block 
            // the recipient.

            return false;
        }
    }

Dropping a Recipient in the OnEndOfHeaders or OnEndOfData Event

The agent can handle the OnEndOfHeaders or the OnEndOfData event if it wants to view the message headers or message body before it decides to drop some recipients but keep the whole message. If the agent decides to reject a recipient, it can call the EnvelopeRecipientCollection.Remove Method. This method has three overrides. The first two overrides take a single parameter, with a type of EnvelopeRecipient for the OnEndOfHeaders event or RoutingAddress for the OnEndOfData event. Each of these overrides silently drops the recipient from the message. The third override takes an EnvelopeRecipient, a DsnType, and an SmtpResponse. This override will drop the recipient and then send a DSN back to the sender if one was requested.

For example, the following agent handles the OnEndOfData event, and drops a recipient from the message.

    public class SampleSmtpReceiveAgent : SmtpReceiveAgent
    {
        private static SmtpResponse rejectResponse =
            new SmtpResponse("500", "", "Recipient rejected");

        public SampleSmtpReceiveAgent()
        {
            this.OnEndOfData += new EndOfDataEventHandler(
                this.OnEndOfDataHandler);
        }

        private void OnEndOfDataHandler(
            ReceiveMessageEventSource source, 
            EndOfDataEventArgs e)
        {
            foreach (EnvelopeRecipient recipient in e.MailItem.Recipients)
            {
                if (this.ShouldDropRecipient(e.MailItem, recipient))
                {
                    if (this.ShouldDropSilently())
                    {
                        e.MailItem.Recipients.Remove(recipient);
                    }
                    else
                    {
                        e.MailItem.Recipients.Remove(
                            recipient,
                            DsnType.Failure,
                            SampleSmtpReceiveAgent.rejectResponse);
                    }
                }
            }

        }

        private bool ShouldDropRecipient(
            MailItem mailItem,
            EnvelopeRecipient recipient)
        {
            // TODO: put logic here to decide whether to block
            // the recipient.

            return false;
        }

        private bool ShouldDropSilently()
        {
            // TODO: put logic here to decide whether to block
            // silently or send back a DSN.

            return false;
        }
    }

Rejecting a Message in the OnEndOfHeaders or OnEndOfData Event

The agent can handle the OnEndOfHeaders or OnEndOfData event if it wants to reject messages at the protocol level. If it decides that it does not want to allow the message to be delivered to any recipient, it can reject the message. If the agent wants to drop the message silently without letting the sender know, it can reject the message with a “successful” response code. Otherwise, it can send back an error response to indicate why the message was blocked. This results in a rejection of the message at the SMTP protocol level; therefore, a DSN does not have to be generated.

For example, the following agent handles the OnEndOfData event, determines whether the message should be blocked and which response to block it with, and then calls a method on the source object to block the message, if necessary.

    public class SampleSmtpReceiveAgent : SmtpReceiveAgent
    {
        private static SmtpResponse silentRejectResponse =
            new SmtpResponse("250", "", "OK");

        private static SmtpResponse normalRejectResponse =
            new SmtpResponse("500", "", "Message rejected");

        SampleSmtpReceiveAgent()
        {
            this.OnEndOfData += new EndOfDataEventHandler(
                this.OnEndOfDataHandler);
        }

        private void OnEndOfDataHandler(
            ReceiveMessageEventSource source,
            EndOfDataEventArgs e)
        {
            if (this.ShouldBlockMessage(e.MailItem))
            {
                source.RejectMessage(
                    this.GetRejectResponse());
            }
        }

        private bool ShouldBlockMessage(MailItem mailItem)
        {
            // TODO: put logic here to decide whether to block 
            // the message.

            return false;
        }

        private SmtpResponse GetRejectResponse()
        {
            bool silentReject = false;

            // TODO: put logic here to decide which response to use.

            return
                silentReject
                ? SampleSmtpReceiveAgent.silentRejectResponse
                : SampleSmtpReceiveAgent.normalRejectResponse;
        }
    }

Rejecting Recipients or Messages from a RoutingAgent

Routing agents handle messages after they have entered the transport pipeline. The following are the ways in which an agent can reject recipients and messages. Each of these actions is available on any of the RoutingAgent class events.

Drop a Recipient from a Message, With or Without a Delivery Status Notification

The agent can drop a recipient from a message by using the EnvelopeRecipientCollection.Remove Method. This method has three overrides. The first two take a single parameter, with a type of EnvelopeRecipient or RoutingAddress, respectively. Each of these methods silently drops the recipient from the message. The third override takes an EnvelopeRecipient, a DsnType, and an SmtpResponse. This override will drop the recipient and then send a DSN back to the sender if one was requested.

For example, the following agent handles the OnSubmittedMessage event and drops a recipient from the message.

    public class SampleRoutingAgent : RoutingAgent
    {
        private static SmtpResponse rejectResponse =
            new SmtpResponse("500", "", "Recipient rejected");

        public SampleRoutingAgent()
        {
            this.OnSubmittedMessage += new SubmittedMessageEventHandler(
                this.OnSubmittedMessageHandler);
        }

        private void OnSubmittedMessageHandler(
            SubmittedMessageEventSource source, 
            QueuedMessageEventArgs e)
        {
            foreach (EnvelopeRecipient recipient in e.MailItem.Recipients)
            {
                if (this.ShouldDropRecipient(e.MailItem, recipient))
                {
                    if (this.ShouldDropSilently())
                    {
                        e.MailItem.Recipients.Remove(recipient);
                    }
                    else
                    {
                        e.MailItem.Recipients.Remove(
                            recipient,
                            DsnType.Failure,
                            SampleRoutingAgent.rejectResponse);
                    }
                }
            }
        }

        private bool ShouldDropRecipient(
            MailItem mailItem,
            EnvelopeRecipient recipient)
        {
            // TODO: put logic here to decide whether to block
            // the recipient.

            return false;
        }

        private bool ShouldDropSilently()
        {
            // TODO: put logic here to decide whether to block
            // silently or send back a DSN.

            return false;
        }
    }

Delete the Message Without a Delivery Status Notification

If the agent decides to reject the message, it can do so either by rejecting each recipient, or by calling the QueuedMessageEventSource.Delete Method, which achieves the same thing. Deleting the message in this manner does not generate a DSN back to the sender of the message. This method exists on the event sender of each RoutingAgent event. Deleting a message does not generate a DSN.

For example, the following agent handles the RoutingAgent.OnSubmittedMessage Event and deletes the current message.

    public class SampleRoutingAgent : RoutingAgent
    {
        public SampleRoutingAgent()
        {
            this.OnSubmittedMessage += new SubmittedMessageEventHandler(
                this.OnSubmittedMessageHandler);
        }

        private void OnSubmittedMessageHandler(
            SubmittedMessageEventSource source, 
            QueuedMessageEventArgs e)
        {
            if (this.ShouldDeleteMessage(e.MailItem))
            {
                source.Delete();
            }
        }

        private bool ShouldDeleteMessage(MailItem mailItem)
        {
            // TODO: put logic here to decide whether to block
            // the message.

            return false;
        }
    }

Conclusion

Now that you've read about the various ways in which a transport agent can reject a message or recipient, go code! Reject some messages - with or without notifying the sender.