Parse Bounced Email (delivery-report)


For many email campaign applications, a critical task is to determine if an email is received by recipient or not. Parsing delivery report is a common way to get email status. The following sample demonstrates how to parse delivery-report.

Example

[Visual Basic, C#] The following example demonstrates how to parse the delivery report with EAGetMail POP3 & IMAP4 Component. To get the full samples of EAGetMail, please refer to Samples section.

[Visual Basic]
Imports EAGetMail

Public Sub ParseReport(ByVal emlFile As String)
    Dim oMail As New Mail("TryIt")
    oMail.Load(emlFile, False)
    Dim contentType As String = oMail.Headers.GetValueOfKey("Content-Type").ToLower()

    If contentType.IndexOf("multipart/report") = -1 Then
        'not delivery report
        Console.WriteLine("This is not a delivery report.")
        Exit Sub
    End If

    Dim item As New HeaderItem("Content-Type", oMail.Headers.GetValueOfKey("Content-Type"))
    Dim type As String = item.SubValue("report-type")

    Dim recipient As String
    Dim messageid As String
    Dim status As String = "failed"

    Dim atts() As Attachment = oMail.Attachments
    If atts.Length > 0 Then
        Dim att As Attachment
        Dim src As String
        att = atts(0)
        src = System.Text.Encoding.Default.GetString(att.Content)

        If String.Compare(type, "disposition-notification", True) = 0 Then
            'this is a read receipt
            status = "delivered"
            messageid = GetHeaderValue("Original-Message-ID:", src)
            recipient = GetHeaderValue("Final-Recipient:", src)
        Else
            'this is a delivery report
            status = GetHeaderValue("Action:", src)
            messageid = GetHeaderValue("Original-Message-ID:", src)
            recipient = GetHeaderValue("Final-Recipient:", src)
            If messageid.Length = 0 And atts.Length > 1 Then
                'get message id from original headers/message
                att = atts(1)
                src = System.Text.Encoding.Default.GetString(att.Content)
                messageid = GetHeaderValue("Message-ID:", src)
            End If
        End If
    Else
        'this delivery report doesn't contain the report attachment, parse the body text
        status = "failed"
        Dim src As String = oMail.TextBody
        recipient = SearchFirstEmailAddr(src)
        messageid = GetHeaderValue("Message-ID:", src)
    End If

    Dim pos As Integer = recipient.IndexOf(";")
    If pos <> -1 Then
        recipient = recipient.Substring(pos + 1)
    End If

    recipient = recipient.Trim("<>".ToCharArray())
    Console.WriteLine("This is a delivery report.")
    Console.WriteLine("Recipient: {0}", recipient)
    Console.WriteLine("Message-ID: {0}", messageid)
    Console.WriteLine("Status: {0}", status)
End Sub

Public Function GetHeaderValue(ByVal key As String, _
ByVal src As String) As String

    Dim lines() As String = src.Split(Chr(10))
    Dim count As Integer = lines.Length
    For i As Integer = 0 To count - 1
        Dim s As String = lines(i).Trim((" " & vbTab & vbCrLf).ToCharArray())
        If s.Length > 0 Then
            If (String.Compare(s, 0, key, 0, key.Length, True) = 0) Then
                GetHeaderValue = s.Substring(key.Length).Trim((" " & vbTab & vbCrLf).ToCharArray())
                Exit Function
            End If
        End If
    Next
    GetHeaderValue = ""
End Function

Public Function SearchFirstEmailAddr(ByVal src As String) As String
    SearchFirstEmailAddr = ""
    Dim pos As Integer = src.IndexOf("@")
    If pos = -1 Then
        Exit Function
    End If

    Dim addr As String
    Dim endpos As Integer = src.IndexOfAny(("<> ;,:" & vbTab & vbCrLf).ToCharArray(), pos)
    Dim startpos As Integer = src.LastIndexOfAny(("<> ;,:" & vbTab & vbCrLf).ToCharArray(), pos)
    If endpos > 0 And startpos > 0 Then
        addr = src.Substring(startpos, endpos - startpos).Trim(("<> ;,:" & vbTab & vbCrLf).ToCharArray())
    End If
    SearchFirstEmailAddr = addr
End Function
[C#]
using EAGetMail;

public static void ParseReport( string emlFile )
{
    Mail oMail = new Mail("TryIt");
    oMail.Load(emlFile, false );
    string contentType = oMail.Headers.GetValueOfKey("Content-Type").ToLower();

    if( contentType.IndexOf("multipart/report") == -1 )
    {
        //not delivery report
        Console.WriteLine("This is not a delivery report.");
        return;
    }

    HeaderItem item = new HeaderItem("Content-Type", oMail.Headers.GetValueOfKey("Content-Type"));
    string type = item.SubValue("report-type");
    string recipient = "";
    string messageid = "";
    string status = "failed";
    Attachment[] atts = oMail.Attachments;
    if( atts.Length > 0 )
    {
        Attachment att = atts[0];
        string src = "";
        src = System.Text.Encoding.Default.GetString(att.Content);

        if( String.Compare(type, "disposition-notification", true ) == 0 )
        {
            //this is a read receipt
            status = "delivered";
            messageid = GetHeaderValue("Original-Message-ID:", src);
            recipient = GetHeaderValue("Final-Recipient:", src);
        }
        else
        {
            //this is a delivery report
            status = GetHeaderValue("Action:", src);
            messageid = GetHeaderValue("Original-Message-ID:", src);
            recipient = GetHeaderValue("Final-Recipient:", src);
            if( messageid.Length == 0 && atts.Length > 1 )
            {
                //get message id from original headers/message
                att = atts[1];
                src = System.Text.Encoding.Default.GetString(att.Content);
                messageid = GetHeaderValue("Message-ID:", src);
            }
        }
    }
    else
    {
        //this delivery report doesn't contain the report attachment, parse the body text
        status = "failed";
        string src = oMail.TextBody;
        recipient = SearchFirstEmailAddr(src);
        messageid = GetHeaderValue("Message-ID:", src);
    }

    int pos = recipient.IndexOf(";");
    if( pos != -1 )
        recipient = recipient.Substring(pos + 1);

    recipient = recipient.Trim("<>".ToCharArray());
    Console.WriteLine("This is a delivery report.");
    Console.WriteLine("Recipient: {0}", recipient);
    Console.WriteLine("Message-ID: {0}", messageid);
    Console.WriteLine("Status: {0}", status);
}

private static string GetHeaderValue( 
    string key, 
    string src)
{
    string [] lines  = src.Split('\n');
    int count = lines.Length;
    for( int i = 0; i < count; i++ )
    {
        string s = lines[i].Trim( " \t\r\n".ToCharArray());
        if( s.Length > 0 )
        {
            if( String.Compare( s, 0, key, 0, key.Length, true ) == 0 )
                return s.Substring( key.Length).Trim( " \t\r\n".ToCharArray());
        }
    }
    return "";
}

private static string SearchFirstEmailAddr( string src )
{
    string addr = "";
    int pos = src.IndexOf('@');
    if( pos == -1 )
        return "";

    int endpos  = src.IndexOfAny("<> ;,:\t\r\n".ToCharArray(), pos);
    int startpos = src.LastIndexOfAny("<> ;,:\r\n\t".ToCharArray(), pos);
    if( endpos > 0 && startpos > 0 )
        addr = src.Substring(startpos, endpos - startpos).Trim("<> ;,:\r\n\t".ToCharArray());

    return addr;
}

See Also

Using EAGetMail POP3 & IMAP4 Component
User Authentication and SSL Connection
Digital Signature and E-mail Encryption/Decryption
Unique Identifier (UIDL) in POP3 and IMAP4 protocol
Work with winmail.dat (TNEF Parser)
EAGetMail Namespace References
EAGetMail POP3 & IMAP4 Component Samples