send a DSN (Non-delivery report) using the python email module

Viewed 97

I'm trying to send a DSN (Delivery Status Notification, also known as a Non-Delivery Report) from python3 using the email module.

A DSN is a MIME message with Content-Type: multipart/report; report-type=delivery-status

The messages contain 2 attachments (and an optional 3rd):

  1. content-type: text/plain a human readable report
  2. content-type: message/delivery-status a machine readable report
  3. content-type: message/rfc822 optionally the original message
#
# Get data from msg
#
headers = Parser(policy=default).parsestr(msg)
recipient = headers['to'].addresses[0].addr_spec
domain = headers['to'].addresses[0].domain
date = email.utils.formatdate(localtime=True)

#
# Create a new email message
#
dsn = EmailMessage()
dsn.policy = policy.SMTP  # <-- this didn't help
dsn.make_mixed()

dsn['From'] = headers['to']
dsn['Date'] = email.utils.localtime(dt=None)
dsn['Message-Id'] = email.utils.make_msgid(idstring=None, domain=None)
dsn['Subject'] = 'Returned Mail: Refused'
dsn['To'] = headers['return-path']

#
# The human readable part
#
dsn.add_attachment("""\
   ----- The following address had delivery problems -----
   <{}> (unrecoverable error: Refused)
""".format(recipient).encode(),
                   maintype="text",
                   subtype="plain",
                   cte=None)

#
# The machine readable part
#
dsn.add_attachment("""\
Reporting-MTA: dns; {}

Original-Recipient: rfc822;{}
Final-Recipient: rfc822;{}
Action: failed
Status: 5.7.1
Diagnostic-Code: smtp; 571 Delivery not authorized, message returned
Last-Attempt-Date: {}
""".format(domain, recipient, recipient, date).encode('us-ascii'),
                   maintype="message",           # <--- these 2 lines cause
                   subtype="delivery-status",    # <--- the issue
                   cte=None)

#
# The original message
#
dsn.add_attachment(msg.encode(),
                   maintype="message",
                   subtype="rfc822",
                   cte=None)

#
# Set the Content-Type header in the message headers
#
dsn.replace_header('Content-Type', 'multipart/report')
dsn.set_param('report-type', 'delivery-status')

print(dsn)  # <--- Dies in here

When the DSN is printed, I receive the following traceback:

Traceback (most recent call last):
  File "./send-dsn.py", line 97, in <module>
    print(dsn)
  File "/usr/lib/python3.9/email/message.py", line 971, in __str__
    return self.as_string(policy=self.policy.clone(utf8=True))
  File "/usr/lib/python3.9/email/message.py", line 968, in as_string
    return super().as_string(unixfrom, maxheaderlen, policy)
  File "/usr/lib/python3.9/email/message.py", line 158, in as_string
    g.flatten(self, unixfrom=unixfrom)
  File "/usr/lib/python3.9/email/generator.py", line 116, in flatten
    self._write(msg)
  File "/usr/lib/python3.9/email/generator.py", line 181, in _write
    self._dispatch(msg)
  File "/usr/lib/python3.9/email/generator.py", line 218, in _dispatch
    meth(msg)
  File "/usr/lib/python3.9/email/generator.py", line 276, in _handle_multipart
    g.flatten(part, unixfrom=False, linesep=self._NL)
  File "/usr/lib/python3.9/email/generator.py", line 116, in flatten
    self._write(msg)
  File "/usr/lib/python3.9/email/generator.py", line 181, in _write
    self._dispatch(msg)
  File "/usr/lib/python3.9/email/generator.py", line 218, in _dispatch
    meth(msg)
  File "/usr/lib/python3.9/email/generator.py", line 335, in _handle_message_delivery_status
    g.flatten(part, unixfrom=False, linesep=self._NL)
  File "/usr/lib/python3.9/email/generator.py", line 107, in flatten
    old_msg_policy = msg.policy
AttributeError: 'str' object has no attribute 'policy'

The problem seems to be the maintype and subtype of the second attachment, the content-type: message/delivery-status attachment. If I change this to text/plain, the DSN prints except that the second attachment has the wrong content-type.

  1. Is this the correct way to build a DSN using this module?
  2. How can I fix this policy attribute problem?
0 Answers
Related