Sample code for 30+ languages & platforms
Xbase++ Requires Chilkat v11.0.0+

Processing a multipart/report Delivery Status Notification (Bounce Notification)

See more Email Object Examples

This example discusses the format of Delivery Status Notification emails and how to process them.

Chilkat Xbase++ Downloads

Xbase++
LOCAL nSuccess
LOCAL oEmail
LOCAL oH2t
LOCAL oJsonDsnInfo
LOCAL cHeadersText
LOCAL oMime
LOCAL oOrigEmail

nSuccess := 0

//  Here are the MIME structures, showing the content-type and nesting of the MIME parts of three sample
//  multipart/report DSN (Delivery Status Notification) emails.

//  This 1st sample includes a "text/rfc822-headers" MIME subpart. 

//  multipart/report
//      text/plain
//      message/delivery-status
//      text/rfc822-headers
//      message/rfc822

//  This 2nd sample lacks the text/rfc-headers part, but the "report type" information
//  is offered in both plain-text and HTML formats.

//  multipart/report
//      multipart/alternative
//          text/plain
//          text/html
//      message/delivery-status
//      message/rfc822

//  ----------------------------------------------------------------------------------------------------

//  A multipart/report MIME delivery status notification follows a specific format defined by the Internet Engineering Task Force (IETF) in RFC 3464, 
//  which outlines the "An Extensible Message Format for Delivery Status Notifications" standard. The format consists of multiple parts within a 
//  multipart/report structure. Here's an overview of the main parts involved:
//  
//  The 1st sub-part under multipart/report is the body of the DSN to be displayed by the email client (such as Outlook).
//  It can be a simple text/plain body, or it can be multipart/alternative and offer a few alternative format, typically plain-text and HTML.
//  HTML is best for viewing a program such as Outlook. 
//  This part of the multipart/report is not structured for programmatic processing.  It's meant to be viewed by a human.

//  --------------------
//  The "message/delivery-status" part within a multipart/report MIME structure follows a specific format to provide details about the delivery status
//  of an email message.  Here's an overview of the format and the key components within the "message/delivery-status" part:
//  
//  (1) Content-Type and Reporting-UA:
//  The "message/delivery-status" part begins with the Content-Type header specifying "message/delivery-status". 
//  It may also include a Reporting-UA (Reporting User Agent) field that identifies the software or system generating the delivery status notification.
//  
//  For example:
//  
//  Content-Type: message/delivery-status
//  Reporting-UA: Example Mail System 1.0
//  
//  (2) Fields:
//  The "message/delivery-status" part contains a series of fields, each providing specific information about the delivery status. 
//  These fields are structured as key-value pairs.
//  
//  Common fields include:
//  
//      Final-Recipient: Specifies the recipient for whom the delivery status is being reported.
//      Action: Describes the action performed by the reporting system (e.g., failed, delivered, delayed, etc.).
//      Status: Indicates the status code or reason for the delivery attempt result.
//      Remote-MTA: Specifies the host or system that attempted the delivery.
//      Diagnostic-Code: Provides additional diagnostic information, such as error codes or explanations.
//  
//  For example:
//  
//  Final-Recipient: rfc822; john.doe@example.com
//  Action: failed
//  Status: 5.1.1
//  Remote-MTA: smtp.example.com
//  Diagnostic-Code: smtp; 550 Requested action not taken: mailbox unavailable
//  
//  (3) Additional Fields:
//  Additional fields may be included in the "message/delivery-status" part to provide further information about the delivery attempt. 
//  These fields can vary depending on the implementation or specific needs of the system generating the delivery status notification.
//  For example:
//  
//  X-Spam-Flag: YES
//  X-Spam-Score: 7.2
//  
//  Note: The specific fields and their values within the "message/delivery-status" part can vary depending on the implementation
//  or the email server/application generating the delivery status notification. The structure described above represents the standard format
//  as defined in RFC 3464, but variations may exist in practice.

//  --------------------

//  The "text/rfc822-headers" MIME part, if included, contains the headers of the original email message
//  for which the delivery status notification is being generated. It provides a subset of the headers from the original message,
//  typically excluding the message body and attachments.
//  
//  The purpose of including the "text/rfc822-headers" part is to provide contextual information about the original message.
//  It allows the recipient to review the original headers, such as the subject, sender, recipients, date, and other relevant information,
//  in order to understand the context and details of the email message for which the delivery status notification is being generated.
//  
//  Note that the specific headers included in the "text/rfc822-headers" part can vary based on the implementation or requirements
//  of the system generating the delivery status notification.

//  --------------------

//  If the message/rfc822 part is present, it contains the full MIME of the email that was not delivered.
//  In Chilkat terminology, this is an attached message.

//  OK, let's write code to process a multipart/report email.

oEmail := CreateObject("Chilkat.Email")
nSuccess := oEmail:LoadEml("qa_data/eml/deliveryStatus.eml")
//  success = email.LoadEml("qa_data/eml/sample_multipart_report.eml");
IF (nSuccess == 0)
    ? oEmail:LastErrorText
    oEmail:destroy()
    RETURN
ENDIF

//  Verify this is a multipart/report email..
IF (oEmail:IsMultipartReport() == 0)
    ? "Not a multipart/report email."
    oEmail:destroy()
    RETURN
ENDIF

//  Get the body that is to be displayed to a human in an email program (such as Outlook).
IF (oEmail:HasPlainTextBody() == 1)
    ? "Plain text body:"
    //  println email.GetPlainTextBody();
ELSE
    IF (oEmail:HasHtmlBody() == 1)
        //  Convert HTML to plain-text..
        oH2t := CreateObject("Chilkat.HtmlToText")
        ? "HTML body converted to plain-text:"
        ? oH2t:ToText(oEmail:GetHtmlBody())
    ELSE
        ? "Has no plain-text or HTML body..."
    ENDIF

ENDIF

? "---------------------------------"

//  Now get information from the message/delivery-status part (or the message/disposition-notification part)
? "--- Delivery Status Information:"
? "Status: " + oEmail:GetDeliveryStatusInfo("Status")
? "Action: " + oEmail:GetDeliveryStatusInfo("Action")
? "Reporting-MTA: " + oEmail:GetDeliveryStatusInfo("Reporting-MTA")

oJsonDsnInfo := CreateObject("Chilkat.JsonObject")
oEmail:GetDsnInfo(oJsonDsnInfo)
oJsonDsnInfo:EmitCompact := 0
? oJsonDsnInfo:Emit()

? "---------------------------------"

//  If the multipart/report contains a text/rfc822-headers, it can be retrieved like this:
cHeadersText := oEmail:GetNthTextPartOfType(0, "text/rfc822-headers", 0, 0)
IF (oEmail:LastMethodSuccess == 1)
    ? "The text/rfc822-headers part exists.."
    ? ""
    ? cHeadersText

    //  If you wish to process the headers, you can load them into a MIME object and use the Chilkat MIME functionality to examine the headers.
    oMime := CreateObject("Chilkat.Mime")
    oMime:LoadMime(cHeadersText)

    //  Do whatever you want.. 
    //  For example, look at the "To" header.
    ? "MIME To header:"
    ? oMime:GetHeaderField("To")
ENDIF

? "---------------------------------"

//  Finally, if the original email was attached, you can load it into another Chilkat Email object instance and
//  do what you want with it..

IF (oEmail:NumAttachedMessages > 0)
    //  Get the 1st attachment message (assume we don't have more than one attached message)
    oOrigEmail := CreateObject("Chilkat.Email")
    nSuccess := oEmail:GetAttachedEmail(0, oOrigEmail)
    IF (nSuccess == 1)
        ? "Attached message subject: " + oOrigEmail:Subject
        //  Do whatever else you want..
    ENDIF

ENDIF

oEmail:destroy()
oH2t:destroy()
oJsonDsnInfo:destroy()
oMime:destroy()
oOrigEmail:destroy()