A. Requesting notifications
There are several ways to request a receipt or Delivery Status Notification (DSN) to be send back to you:
When an e-mail message can't be delivered, most SMTP servers send a Non-Delivery Report (NDR) to the sender. This contains the message ID to make it possible to pair the report with the original outgoing e-mail, so make sure to set MailMessage.MessageId
property.
It is possible to use the Disposition-Notification-To
header to request a read-receipt to be sent by the recipient's mail application (check out mail tutorials for more information). To be able to pair the receipt with the original outgoing e-mail, use MailMessage.MessageId
property.
You can also request a DSN to be sent back to you by setting the DeliveryStatusNotificationConditions
property of the Smtp
object to DeliveryStatusNotificationConditions.All
, then use the Smtp
object to mail a MailMessage
for you. If the message is received by a compliant SMTP server, you will get a DSN when the message is delivered to the recipient's mailbox. To be able to pair the notification message with the original outgoing e-mail, use MailMessage.EnvelopeId
property.
These methods can be used together for the same message. However, none of the messages is guaranteed, because either the server or the client (or both) may choose to not fulfil your request.
Please note there are two message IDs:
One is accessible through MailMessage.MessageId
property and is used to track ordinary replies and also automated responses to emails with Disposition-Notification-To
header.
The other ID is accessible through MailMessage.EnvelopeId
property and is used to track replies to (non-)delivery notifications requested using Smtp
object's DeliveryStatusNotificationConditions
property. You have to set EnvelopeId before sending the message for this to take effect (setting it to the same value as MessageId might be a good idea).
B. Detecting and processing notifications
Parsing DSNs is not integrated in Rebex Secure Mail itself yet, but we have written a simple class that makes this task rather easy, although it only works with fully-downloaded messages. The class parses both the Delivery Status Notifications (DSNs) and Message Disposition Notifications (MDNs) that are functionally similar.
/// <summary>
/// Simple delivery status parser.
/// See RFC1894.
/// </summary>
public class DeliveryStatus
{
private Dictionary<string, string> _values;
public string FinalRecipient
{
get { return GetValue("final-recipient"); }
}
public string Disposition
{
get { return GetValue("disposition"); }
}
public string Action
{
get { return GetValue("action"); }
}
public string Status
{
get { return GetValue("status"); }
}
public string OriginalMessageId
{
get { return GetValue("original-message-id"); }
}
public string OriginalEnvelopeId
{
get { return GetValue("original-envelope-id"); }
}
public string ReportingMailTransferAgent
{
get { return GetValue("reporting-mta"); }
}
public string RemoteMailTransferAgent
{
get { return GetValue("remote-mta"); }
}
public string DiagnosticCode
{
get { return GetValue("diagnostic-code"); }
}
private string GetValue(string name)
{
string value;
if (!_values.TryGetValue(name, out value))
return null;
return value;
}
public static DeliveryStatus ParseMessage(MailMessage message)
{
if (message == null)
throw new ArgumentNullException("message");
foreach (Attachment att in message.Attachments)
{
switch (att.MediaType)
{
case "message/delivery-status":
case "message/disposition-notification":
using (StreamReader reader = new StreamReader(att.GetContentStream()))
{
return new DeliveryStatus(reader.ReadToEnd());
}
}
}
return null;
}
public DeliveryStatus(string message)
{
if (message == null)
throw new ArgumentNullException("message");
_values = new Dictionary<string, string>();
message = message.Replace("\r", "");
string[] lines = message.Split('\n');
for (int i = 0; i < lines.Length; i++)
{
string line = lines[i];
int p = line.IndexOf(':');
if (p < 0)
continue;
string name = line.Substring(0, p).ToLowerInvariant();
string value = line.Substring(p + 1).Trim();
_values[name] = value;
}
}
}
Using it in custom applications is simple - for example:
MailMessage mail = imap.GetMailMessage(id);
DeliveryStatus dsn = DeliveryStatus.ParseMessage(mail);
if (dsn != null)
{
// ... the message is an DSN, do something with it
}
else
{
// ... the message is an ordinary e-mail, do something else with it
}