summaryrefslogtreecommitdiff
path: root/src/mailman/bouncers/dsn.py
diff options
context:
space:
mode:
authorBarry Warsaw2011-05-27 19:34:44 -0400
committerBarry Warsaw2011-05-27 19:34:44 -0400
commit5f93d80364aea9535c14f9f22c2fd7d02b8dd78d (patch)
treeb60e0c8dc70c195c9f0f97ea900d69065741d579 /src/mailman/bouncers/dsn.py
parent7b7b63c34324efe4055c285106b3d7bf92dd322b (diff)
downloadmailman-5f93d80364aea9535c14f9f22c2fd7d02b8dd78d.tar.gz
mailman-5f93d80364aea9535c14f9f22c2fd7d02b8dd78d.tar.zst
mailman-5f93d80364aea9535c14f9f22c2fd7d02b8dd78d.zip
Diffstat (limited to 'src/mailman/bouncers/dsn.py')
-rw-r--r--src/mailman/bouncers/dsn.py132
1 files changed, 57 insertions, 75 deletions
diff --git a/src/mailman/bouncers/dsn.py b/src/mailman/bouncers/dsn.py
index 49803ef88..c9d7803ee 100644
--- a/src/mailman/bouncers/dsn.py
+++ b/src/mailman/bouncers/dsn.py
@@ -37,84 +37,66 @@ from mailman.interfaces.bounce import IBounceDetector, Stop
-def check(msg):
- # Iterate over each message/delivery-status subpart.
- failed_addresses = []
- delayed_addresses = []
- for part in typed_subpart_iterator(msg, 'message', 'delivery-status'):
- if not part.is_multipart():
- # Huh?
- continue
- # Each message/delivery-status contains a list of Message objects
- # which are the header blocks. Iterate over those too.
- for msgblock in part.get_payload():
- address_set = None
- # We try to dig out the Original-Recipient (which is optional) and
- # Final-Recipient (which is mandatory, but may not exactly match
- # an address on our list). Some MTA's also use X-Actual-Recipient
- # as a synonym for Original-Recipient, but some apparently use
- # that for other purposes :(
- #
- # Also grok out Action so we can do something with that too.
- action = msgblock.get('action', '').lower()
- # Some MTAs have been observed that put comments on the action.
- if action.startswith('delayed'):
- address_set = delayed_addresses
- elif action.startswith('fail'):
- address_set = failed_addresses
- else:
- # Some non-permanent failure, so ignore this block.
- continue
- params = []
- foundp = False
- for header in ('original-recipient', 'final-recipient'):
- for k, v in msgblock.get_params([], header):
- if k.lower() == 'rfc822':
- foundp = True
- else:
- params.append(k)
- if foundp:
- # Note that params should already be unquoted.
- address_set.extend(params)
- break
- else:
- # MAS: This is a kludge, but SMTP-GATEWAY01.intra.home.dk
- # has a final-recipient with an angle-addr and no
- # address-type parameter at all. Non-compliant, but ...
- for param in params:
- if param.startswith('<') and param.endswith('>'):
- address_set.append(param[1:-1])
- # There may be both delayed and failed addresses. If there are any failed
- # addresses, return those, otherwise just stop processing.
- if len(failed_addresses) == 0:
- if len(delayed_addresses) == 0:
- return set()
- else:
- return Stop
- return set(parseaddr(address)[1] for address in failed_addresses
- if address is not None)
-
-
-
class DSN:
"""Parse RFC 3464 (i.e. DSN) bounce formats."""
implements(IBounceDetector)
def process(self, msg):
- return check(msg)
- ## # A DSN has been seen wrapped with a "legal disclaimer" by an outgoing
- ## # MTA in a multipart/mixed outer part.
- ## if msg.is_multipart() and msg.get_content_subtype() == 'mixed':
- ## msg = msg.get_payload()[0]
- ## # The above will suffice if the original message 'parts' were wrapped
- ## # with the disclaimer added, but the original DSN can be wrapped as a
- ## # message/rfc822 part. We need to test that too.
- ## if msg.is_multipart() and msg.get_content_type() == 'message/rfc822':
- ## msg = msg.get_payload()[0]
- ## # The report-type parameter should be "delivery-status", but it seems
- ## # that some DSN generating MTAs don't include this on the
- ## # Content-Type: header, so let's relax the test a bit.
- ## if not msg.is_multipart() or msg.get_content_subtype() <> 'report':
- ## return set()
- ## return check(msg)
+ """See `IBounceDetector`."""
+ # Iterate over each message/delivery-status subpart.
+ failed_addresses = []
+ delayed_addresses = []
+ for part in typed_subpart_iterator(msg, 'message', 'delivery-status'):
+ if not part.is_multipart():
+ # Huh?
+ continue
+ # Each message/delivery-status contains a list of Message objects
+ # which are the header blocks. Iterate over those too.
+ for msgblock in part.get_payload():
+ address_set = None
+ # We try to dig out the Original-Recipient (which is optional)
+ # and Final-Recipient (which is mandatory, but may not exactly
+ # match an address on our list). Some MTA's also use
+ # X-Actual-Recipient as a synonym for Original-Recipient, but
+ # some apparently use that for other purposes :(
+ #
+ # Also grok out Action so we can do something with that too.
+ action = msgblock.get('action', '').lower()
+ # Some MTAs have been observed that put comments on the action.
+ if action.startswith('delayed'):
+ address_set = delayed_addresses
+ elif action.startswith('fail'):
+ address_set = failed_addresses
+ else:
+ # Some non-permanent failure, so ignore this block.
+ continue
+ params = []
+ foundp = False
+ for header in ('original-recipient', 'final-recipient'):
+ for k, v in msgblock.get_params([], header):
+ if k.lower() == 'rfc822':
+ foundp = True
+ else:
+ params.append(k)
+ if foundp:
+ # Note that params should already be unquoted.
+ address_set.extend(params)
+ break
+ else:
+ # MAS: This is a kludge, but
+ # SMTP-GATEWAY01.intra.home.dk has a final-recipient
+ # with an angle-addr and no address-type parameter at
+ # all. Non-compliant, but ...
+ for param in params:
+ if param.startswith('<') and param.endswith('>'):
+ address_set.append(param[1:-1])
+ # There may be both delayed and failed addresses. If there are any
+ # failed addresses, return those, otherwise just stop processing.
+ if len(failed_addresses) == 0:
+ if len(delayed_addresses) == 0:
+ return set()
+ else:
+ return Stop
+ return set(parseaddr(address)[1] for address in failed_addresses
+ if address is not None)