def _dispose(self, mlist, msg, msgdata): # List isn't doing bounce processing? if not mlist.process_bounces: return False # Try VERP detection first, since it's quick and easy context = BounceContext.normal addresses = StandardVERP().get_verp(mlist, msg) if len(addresses) > 0: # Scan the message to see if it contained permanent or temporary # failures. We'll ignore temporary failures, but even if there # are no permanent failures, we'll assume VERP bounces are # permanent. temporary, permanent = all_failures(msg) if len(temporary) > 0: # This was a temporary failure, so just ignore it. return False else: # See if this was a probe message. addresses = ProbeVERP().get_verp(mlist, msg) if len(addresses) > 0: context = BounceContext.probe else: # That didn't give us anything useful, so try the old fashion # bounce matching modules. Since Mailman currently doesn't # score temporary failures, if we get no permanent failures, # we're done, but we do need to check for temporary failures # to know if the bounce was recognized. temporary, addresses = all_failures(msg) if len(addresses) == 0 and len(temporary) > 0: # This is a recognized temp fail so ignore it. return False # If that still didn't return us any useful addresses, then send it on # or discard it. The addresses will come back from flufl.bounce as # bytes/8-bit strings, but we must store them as unicodes in the # database. Assume utf-8 encoding, but be cautious. if len(addresses) > 0: for address in addresses: if isinstance(address, bytes): try: address = address.decode('utf-8') except UnicodeError: log.exception('Ignoring non-UTF-8 encoded ' 'address: {}'.format(address)) continue self._processor.register(mlist, address, msg, context) else: log.info('Bounce message w/no discernable addresses: %s', msg.get('message-id', 'n/a')) maybe_forward(mlist, msg) # Dequeue this message. return False
def setUp(self): self._mlist = create_list('*****@*****.**') self._verper = StandardVERP()
class TestVERP(unittest.TestCase): """Test header VERP detection.""" layer = ConfigLayer def setUp(self): self._mlist = create_list('*****@*****.**') self._verper = StandardVERP() def test_no_verp(self): # The empty set is returned when there is no VERP headers. msg = mfs("""\ From: [email protected] To: [email protected] """) self.assertEqual(self._verper.get_verp(self._mlist, msg), set()) def test_verp_in_to(self): # A VERP address is found in the To header. msg = mfs("""\ From: [email protected] To: [email protected] """) self.assertEqual(self._verper.get_verp(self._mlist, msg), set(['*****@*****.**'])) def test_verp_in_delivered_to(self): # A VERP address is found in the Delivered-To header. msg = mfs("""\ From: [email protected] Delivered-To: [email protected] """) self.assertEqual(self._verper.get_verp(self._mlist, msg), set(['*****@*****.**'])) def test_verp_in_envelope_to(self): # A VERP address is found in the Envelope-To header. msg = mfs("""\ From: [email protected] Envelope-To: [email protected] """) self.assertEqual(self._verper.get_verp(self._mlist, msg), set(['*****@*****.**'])) def test_verp_in_apparently_to(self): # A VERP address is found in the Apparently-To header. msg = mfs("""\ From: [email protected] Apparently-To: [email protected] """) self.assertEqual(self._verper.get_verp(self._mlist, msg), set(['*****@*****.**'])) def test_verp_with_empty_header(self): # A VERP address is found, but there's an empty header. msg = mfs("""\ From: [email protected] To: [email protected] To: """) self.assertEqual(self._verper.get_verp(self._mlist, msg), set(['*****@*****.**'])) def test_no_verp_with_empty_header(self): # There's an empty header, and no VERP address is found. msg = mfs("""\ From: [email protected] To: """) self.assertEqual(self._verper.get_verp(self._mlist, msg), set()) def test_verp_with_non_match(self): # A VERP address is found, but a header had a non-matching pattern. msg = mfs("""\ From: [email protected] To: [email protected] To: [email protected] """) self.assertEqual(self._verper.get_verp(self._mlist, msg), set(['*****@*****.**'])) def test_no_verp_with_non_match(self): # No VERP address is found, and a header had a non-matching pattern. msg = mfs("""\ From: [email protected] To: [email protected] """) self.assertEqual(self._verper.get_verp(self._mlist, msg), set()) def test_multiple_verps(self): # More than one VERP address was found in the same header. msg = mfs("""\ From: [email protected] To: [email protected] To: [email protected] """) self.assertEqual(self._verper.get_verp(self._mlist, msg), set(['*****@*****.**'])) def test_multiple_verps_different_values(self): # More than one VERP address was found in the same header with # different values. msg = mfs("""\ From: [email protected] To: [email protected] To: [email protected] """) self.assertEqual(self._verper.get_verp(self._mlist, msg), set(['*****@*****.**', '*****@*****.**'])) def test_multiple_verps_different_values_different_headers(self): # More than one VERP address was found in different headers with # different values. msg = mfs("""\ From: [email protected] To: [email protected] Apparently-To: [email protected] """) self.assertEqual(self._verper.get_verp(self._mlist, msg), set(['*****@*****.**', '*****@*****.**']))