Moving to GitHub.
[platal.git] / bin / newsletter.bounces.processor.py
index 3206ef7..8933349 100755 (executable)
@@ -165,16 +165,24 @@ def findAddressInBounce(bounce):
         print('! Not a valid bounce (expected text/plain, found %s).' % content.get_content_type())
         return None
     # Extract the faulty email address
-    recipient_match = _recipient_re.search(content['Final-Recipient'])
+    # Some MTA don't set Final-Recipient but use Remote-Recipient instead
+    if 'Final-Recipient' in content:
+        final_recipient = content['Final-Recipient']
+    elif 'Remote-Recipient' in content:
+        final_recipient = content['Remote-Recipient']
+    else:
+        print('! Not a valid bounce (no Final-Recipient).')
+        return None
+    recipient_match = _recipient_re.search(final_recipient)
     if recipient_match is None:
         # Be nice, test another regexp
-        recipient_match = _recipient_re2.search(content['Final-Recipient'])
+        recipient_match = _recipient_re2.search(final_recipient)
         if recipient_match is None:
             print('! Missing final recipient.')
             return None
     email = recipient_match.group(1)
     # Check the action field
-    if content['Action'].lower() != 'failed':
+    if content['Action'].lower().strip() != 'failed':
         print('! Not a failed action (%s).' % content['Action'])
         return None
 
@@ -192,6 +200,8 @@ def findAddressInBounce(bounce):
     failure_hints = [
         "insufficient system storage",
         "mailbox full",
+        "mailbox recipient does not have a mailbox database",
+        "over quota",
         "requested action aborted: local error in processing",
         "user unknown",
         ]
@@ -341,6 +351,7 @@ def findAddressInPlainBounce(bounce, real_bounce=None):
         "I'm sorry to have to inform you that your message could not",
         "I wasn't able to deliver your message",
         "try to send your message again at a later time",
+        "User unknown in local recipient table",
         "> was undeliverable.",
         "we were unable to deliver your message",
     ]
@@ -355,7 +366,9 @@ def findAddressInPlainBounce(bounce, real_bounce=None):
     permanent_error_hints = [
         "Delivery to the following recipient failed permanently",
         "failed due to an unavailable mailbox",
+        "following addresses had permanent fatal errors",
         "I'm sorry to have to inform you that your message could not",
+        "The email account that you tried to reach does not exist",
         "This is a permanent error",
         "Unknown address error",
         "unreachable for too long",
@@ -407,7 +420,7 @@ class DirectBouncesFilter(MboxFilter):
             # Additionnal checks, just to be sure
             elif message['From'] != 'MAILER-DAEMON@polytechnique.org (Mail Delivery System)' \
             or message['Subject'] != 'Undelivered Mail Returned to Sender':
-                print('! Not an usual direct bounce (From="%s", Subject="%s").' % (message['From'], message['Subject']))
+                print('! Not an usual direct bounce (From=%r, Subject=%r).' % (message['From'], message['Subject']))
             else:
                 email = findAddressInBounce(message)
                 if email is not None:
@@ -601,8 +614,8 @@ class DeliveryStatusNotificationFilter(MboxFilter):
 
         # Detect ill-formatted reports, sent as plain text email
         if report_message.get_content_type() == 'text/plain' and (
-            'MAILER-DAEMON@' in message['From'].upper() or
-            'mail delivery failure' == message['Subject'].lower()
+            'MAILER-DAEMON@' in message.get('From', '').upper() or
+            'mail delivery failure' == message.get('Subject', '').lower()
             ):
             email = findAddressInPlainBounce(report_message)
             if email is not None: