diff options
| author | Barry Warsaw | 2011-05-01 12:44:23 -0400 |
|---|---|---|
| committer | Barry Warsaw | 2011-05-01 12:44:23 -0400 |
| commit | cab8aea12c63fe4bcf8930e9b2f53012d2a74ee8 (patch) | |
| tree | b348573e40c969c3ae1f24387c1a6ebbf78c5d35 /src/mailman/app/bounces.py | |
| parent | a90ca4ce77d99460ffd36a366cea0162869df783 (diff) | |
| download | mailman-cab8aea12c63fe4bcf8930e9b2f53012d2a74ee8.tar.gz mailman-cab8aea12c63fe4bcf8930e9b2f53012d2a74ee8.tar.zst mailman-cab8aea12c63fe4bcf8930e9b2f53012d2a74ee8.zip | |
Diffstat (limited to 'src/mailman/app/bounces.py')
| -rw-r--r-- | src/mailman/app/bounces.py | 53 |
1 files changed, 53 insertions, 0 deletions
diff --git a/src/mailman/app/bounces.py b/src/mailman/app/bounces.py index 7f6507a14..8d2526a66 100644 --- a/src/mailman/app/bounces.py +++ b/src/mailman/app/bounces.py @@ -22,21 +22,28 @@ from __future__ import absolute_import, unicode_literals __metaclass__ = type __all__ = [ 'bounce_message', + 'get_verp', 'scan_message', ] + +import re import logging from email.mime.message import MIMEMessage from email.mime.text import MIMEText +from email.utils import parseaddr from mailman.app.finder import find_components +from mailman.config import config from mailman.core.i18n import _ from mailman.email.message import UserNotification from mailman.interfaces.bounce import IBounceDetector +from mailman.utilities.email import split_email from mailman.utilities.string import oneline log = logging.getLogger('mailman.config') +elog = logging.getLogger('mailman.error') @@ -93,3 +100,49 @@ def scan_message(mlist, msg): if addresses: return set(addresses) return set() + + + +def get_verp(mlist, msg): + """Extract a set of VERP bounce addresses. + + Sadly not every MTA bounces VERP messages correctly, or consistently. + First, the To: header is checked, then Delivered-To: (Postfix), + Envelope-To: (Exim) and Apparently-To:. Note that there can be multiple + Delivered-To: headers so we need to search them all (and we don't worry + about false positives for forwarded email, because only one should match + `verp_regexp`). + + :param mlist: The mailing list being checked. + :type mlist: `IMailingList` + :param msg: The message being parsed. + :type msg: `email.message.Message` + :return: The set of addresses extracted from the VERP headers. + :rtype: set of strings + """ + cre = re.compile(config.mta.verp_regexp, re.IGNORECASE) + blocal, bdomain = split_email(mlist.bounces_address) + values = set() + verp_matches = set() + for header in ('to', 'delivered-to', 'envelope-to', 'apparently-to'): + values.update(msg.get_all(header, [])) + for field in values: + address = parseaddr(field)[1] + if not address: + # This header was empty. + continue + mo = cre.search(address) + if not mo: + # This did not match the VERP regexp. + continue + try: + if blocal != mo.group('bounces'): + # This was not a bounce to our mailing list. + continue + original_address = '{0}@{1}'.format(*mo.group('local', 'domain')) + except IndexError: + elog.error("verp_regexp doesn't yield the right match groups") + return set() + else: + verp_matches.add(original_address) + return verp_matches |
