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):
content-type: text/plaina human readable reportcontent-type: message/delivery-statusa machine readable reportcontent-type: message/rfc822optionally 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.
- Is this the correct way to build a DSN using this module?
- How can I fix this policy attribute problem?