diff options
| author | Barry Warsaw | 2011-04-27 21:34:28 -0400 |
|---|---|---|
| committer | Barry Warsaw | 2011-04-27 21:34:28 -0400 |
| commit | dbe88c1d0a08999c5209b3a1bc70c458dbe270b4 (patch) | |
| tree | 3a2b6f6cd1e4688089cff14841044947a8eb102b /src | |
| parent | 7181a4d6638605595b043f66eb3cd01dd93a0ff2 (diff) | |
| download | mailman-dbe88c1d0a08999c5209b3a1bc70c458dbe270b4.tar.gz mailman-dbe88c1d0a08999c5209b3a1bc70c458dbe270b4.tar.zst mailman-dbe88c1d0a08999c5209b3a1bc70c458dbe270b4.zip | |
Diffstat (limited to 'src')
| -rw-r--r-- | src/mailman/bouncers/dsn.py | 54 | ||||
| -rw-r--r-- | src/mailman/bouncers/llnl.py | 2 | ||||
| -rw-r--r-- | src/mailman/bouncers/qmail.py | 5 | ||||
| -rw-r--r-- | src/mailman/bouncers/simplewarning.py | 12 | ||||
| -rw-r--r-- | src/mailman/bounces/tests/test_bounces.py | 34 | ||||
| -rw-r--r-- | src/mailman/interfaces/bounce.py | 27 | ||||
| -rw-r--r-- | src/mailman/queue/bounce.py | 6 | ||||
| -rw-r--r-- | src/mailman/tests/test_bounces.py | 227 |
8 files changed, 75 insertions, 292 deletions
diff --git a/src/mailman/bouncers/dsn.py b/src/mailman/bouncers/dsn.py index 7b3021939..49803ef88 100644 --- a/src/mailman/bouncers/dsn.py +++ b/src/mailman/bouncers/dsn.py @@ -33,13 +33,14 @@ from email.iterators import typed_subpart_iterator from email.utils import parseaddr from zope.interface import implements -from mailman.interfaces.bounce import BounceStatus, IBounceDetector +from mailman.interfaces.bounce import IBounceDetector, Stop def check(msg): # Iterate over each message/delivery-status subpart. - addresses = [] + failed_addresses = [] + delayed_addresses = [] for part in typed_subpart_iterator(msg, 'message', 'delivery-status'): if not part.is_multipart(): # Huh? @@ -47,6 +48,7 @@ def check(msg): # 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 @@ -57,8 +59,10 @@ def check(msg): action = msgblock.get('action', '').lower() # Some MTAs have been observed that put comments on the action. if action.startswith('delayed'): - return BounceStatus.non_fatal - if not action.startswith('fail'): + address_set = delayed_addresses + elif action.startswith('fail'): + address_set = failed_addresses + else: # Some non-permanent failure, so ignore this block. continue params = [] @@ -71,7 +75,7 @@ def check(msg): params.append(k) if foundp: # Note that params should already be unquoted. - addresses.extend(params) + address_set.extend(params) break else: # MAS: This is a kludge, but SMTP-GATEWAY01.intra.home.dk @@ -79,8 +83,15 @@ def check(msg): # address-type parameter at all. Non-compliant, but ... for param in params: if param.startswith('<') and param.endswith('>'): - addresses.append(param[1:-1]) - return set(parseaddr(address)[1] for address in addresses + 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) @@ -91,18 +102,19 @@ class DSN: implements(IBounceDetector) def process(self, 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) + ## # 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) diff --git a/src/mailman/bouncers/llnl.py b/src/mailman/bouncers/llnl.py index 1d54faf10..bd255d7c2 100644 --- a/src/mailman/bouncers/llnl.py +++ b/src/mailman/bouncers/llnl.py @@ -48,5 +48,5 @@ class LLNL: for line in body_line_iterator(msg): mo = acre.search(line) if mo: - return set(mo.group('addr')) + return set([mo.group('addr')]) return set() diff --git a/src/mailman/bouncers/qmail.py b/src/mailman/bouncers/qmail.py index 94ea33181..a8063aa46 100644 --- a/src/mailman/bouncers/qmail.py +++ b/src/mailman/bouncers/qmail.py @@ -45,11 +45,12 @@ from mailman.interfaces.bounce import IBounceDetector # Other (non-standard?) intros have been observed in the wild. introtags = [ - 'Hi. This is the', "We're sorry. There's a problem", 'Check your send e-mail address.', + 'Hi. The MTA program at', + 'Hi. This is the', 'This is the mail delivery agent at', - 'Unfortunately, your mail was not delivered' + 'Unfortunately, your mail was not delivered', ] acre = re.compile(r'<(?P<addr>[^>]*)>:') diff --git a/src/mailman/bouncers/simplewarning.py b/src/mailman/bouncers/simplewarning.py index 79173cb21..4da7550fc 100644 --- a/src/mailman/bouncers/simplewarning.py +++ b/src/mailman/bouncers/simplewarning.py @@ -25,7 +25,7 @@ __all__ = [ from mailman.bouncers.simplematch import _c from mailman.bouncers.simplematch import SimpleMatch -from mailman.interfaces.bounce import BounceStatus +from mailman.interfaces.bounce import Stop @@ -55,6 +55,14 @@ PATTERNS = [ (_c('Delivery attempts will continue to be made'), _c('.+'), _c('(?P<addr>.+)')), + # googlemail.com warning + (_c('Delivery to the following recipient has been delayed'), + _c('.+'), + _c('\s*(?P<addr>.+)')), + # Exchange warning message. + (_c('This is an advisory-only email'), + _c('has been postponed'), + _c('"(?P<addr>[^"]+)"')), # Next one goes here... ] @@ -69,6 +77,6 @@ class SimpleWarning(SimpleMatch): """See `SimpleMatch`.""" if super(SimpleWarning, self).process(msg): # It's a recognized warning so stop now. - return BounceStatus.non_fatal + return Stop else: return set() diff --git a/src/mailman/bounces/tests/test_bounces.py b/src/mailman/bounces/tests/test_bounces.py index dc9b22aac..b04d09bf3 100644 --- a/src/mailman/bounces/tests/test_bounces.py +++ b/src/mailman/bounces/tests/test_bounces.py @@ -36,7 +36,7 @@ from mailman.app.finder import scan_module from mailman.bouncers.caiwireless import Caiwireless from mailman.bouncers.microsoft import Microsoft from mailman.bouncers.smtp32 import SMTP32 -from mailman.interfaces.bounce import BounceStatus, IBounceDetector +from mailman.interfaces.bounce import IBounceDetector, Stop COMMASPACE = ', ' @@ -51,8 +51,7 @@ class BounceTestCase(unittest.TestCase): unittest.TestCase.__init__(self) self.bounce_module = bounce_module self.sample_file = sample_file - self.expected = (expected if expected in BounceStatus - else set(expected)) + self.expected = (expected if expected is Stop else set(expected)) def setUp(self): """See `unittest.TestCase`.""" @@ -65,10 +64,8 @@ class BounceTestCase(unittest.TestCase): def shortDescription(self): """See `unittest.TestCase`.""" - if self.expected is BounceStatus.stop: + if self.expected is Stop: expected = 'Stop' - elif self.expected is BounceStatus.non_fatal: - expected = 'NonFatal' else: expected = COMMASPACE.join(sorted(self.expected)) return '{0}: detecting {1} in {2}'.format( @@ -83,8 +80,9 @@ class BounceTestCase(unittest.TestCase): def runTest(self): """Test one bounce detection.""" - for component in scan_module(self.module, IBounceDetector): - found_expected = component().process(self.message) + for component_class in scan_module(self.module, IBounceDetector): + component = component_class() + found_expected = component.process(self.message) self.assertEqual(found_expected, self.expected) @@ -189,11 +187,11 @@ DATA = ( ('simplematch', 'bounce_02.txt', ['acinsp1@midsouth.rr.com']), ('simplematch', 'bounce_03.txt', ['james@jeborall.demon.co.uk']), # SimpleWarning - ('simplewarning', 'simple_03.txt', BounceStatus.stop), - ('simplewarning', 'simple_21.txt', BounceStatus.stop), - ('simplewarning', 'simple_22.txt', BounceStatus.stop), - ('simplewarning', 'simple_28.txt', BounceStatus.stop), - ('simplewarning', 'simple_35.txt', BounceStatus.stop), + ('simplewarning', 'simple_03.txt', Stop), + ('simplewarning', 'simple_21.txt', Stop), + ('simplewarning', 'simple_22.txt', Stop), + ('simplewarning', 'simple_28.txt', Stop), + ('simplewarning', 'simple_35.txt', Stop), # GroupWise ('groupwise', 'groupwise_01.txt', ['thoff@MAINEX1.ASU.EDU']), # This one really sucks 'cause it's text/html. Just make sure it @@ -210,10 +208,10 @@ DATA = ( ('dsn', 'dsn_02.txt', ['zzzzz@zeus.hud.ac.uk']), ('dsn', 'dsn_03.txt', ['ddd.kkk@advalvas.be']), ('dsn', 'dsn_04.txt', ['max.haas@unibas.ch']), - ('dsn', 'dsn_05.txt', BounceStatus.stop), - ('dsn', 'dsn_06.txt', BounceStatus.stop), - ('dsn', 'dsn_07.txt', BounceStatus.stop), - ('dsn', 'dsn_08.txt', BounceStatus.stop), + ('dsn', 'dsn_05.txt', Stop), + ('dsn', 'dsn_06.txt', Stop), + ('dsn', 'dsn_07.txt', Stop), + ('dsn', 'dsn_08.txt', Stop), ('dsn', 'dsn_09.txt', ['pr@allen-heath.com']), ('dsn', 'dsn_10.txt', ['anne.person@dom.ain']), ('dsn', 'dsn_11.txt', ['joem@example.com']), @@ -222,7 +220,7 @@ DATA = ( ('dsn', 'dsn_14.txt', ['artboardregistration@home.dk']), ('dsn', 'dsn_15.txt', ['horu@ccc-ces.com']), ('dsn', 'dsn_16.txt', ['hishealinghand@pastors.com']), - ('dsn', 'dsn_17.txt', BounceStatus.stop), + ('dsn', 'dsn_17.txt', Stop), # Microsoft Exchange ('exchange', 'microsoft_01.txt', ['DJBENNETT@IKON.COM']), ('exchange', 'microsoft_02.txt', ['MDMOORE@BALL.COM']), diff --git a/src/mailman/interfaces/bounce.py b/src/mailman/interfaces/bounce.py index 60c162bb2..22e2467b8 100644 --- a/src/mailman/interfaces/bounce.py +++ b/src/mailman/interfaces/bounce.py @@ -21,8 +21,8 @@ from __future__ import absolute_import, unicode_literals __metaclass__ = type __all__ = [ - 'BounceStatus', 'IBounceDetector', + 'Stop', ] @@ -31,18 +31,11 @@ from zope.interface import Interface -class BounceStatus(Enum): - """Special bounce statuses.""" - - # Matching addresses were found, but they were determined to be non-fatal. - # In this case, processing is halted but no bounces are registered. - non_fatal = 1 - - # If a bounce detector returns Stop, that means to just discard the - # message. An example is warning messages for temporary delivery - # problems. These shouldn't trigger a bounce notification, but we also - # don't want to send them on to the list administrator. - stop = 2 +# If a bounce detector returns Stop, that means to just discard the +# message. An example is warning messages for temporary delivery +# problems. These shouldn't trigger a bounce notification, but we also +# don't want to send them on to the list administrator. +Stop = object() @@ -55,9 +48,7 @@ class IBounceDetector(Interface): :param msg: An email message. :type msg: `Message` :return: The detected bouncing addresses. When bouncing addresses are - found but are determined to be non-fatal, the enum - `BounceStatus.non_fatal` can be returned to halt any bounce - processing pipeline. When bounce processing should stop, a - `BounceStatus.stop` is returned. - :rtype: A set strings, or a `BounceStatus` + found but are determined to be non-fatal, the value `Stop` is + returned to halt any bounce processing pipeline. + :rtype: A set strings, or `Stop` """ diff --git a/src/mailman/queue/bounce.py b/src/mailman/queue/bounce.py index 4c340a821..6c8b90fed 100644 --- a/src/mailman/queue/bounce.py +++ b/src/mailman/queue/bounce.py @@ -29,7 +29,7 @@ from lazr.config import as_timedelta from mailman.config import config from mailman.core.i18n import _ from mailman.email.utils import split_email -from mailman.interfaces.bounce import BounceStatus +from mailman.interfaces.bounce import Stop from mailman.queue import Runner @@ -192,7 +192,7 @@ class BounceRunner(Runner, BounceMixin): addrs = verp_bounce(mlist, msg) if addrs: # We have an address, but check if the message is non-fatal. - if scan_messages(mlist, msg) is BounceStatus.non_fatal: + if scan_messages(mlist, msg) is Stop: return else: # See if this was a probe message. @@ -203,7 +203,7 @@ class BounceRunner(Runner, BounceMixin): # That didn't give us anything useful, so try the old fashion # bounce matching modules. addrs = scan_messages(mlist, msg) - if addrs is BounceStatus.non_fatal: + if addrs is Stop: # This is a recognized, non-fatal notice. Ignore it. return # If that still didn't return us any useful addresses, then send it on diff --git a/src/mailman/tests/test_bounces.py b/src/mailman/tests/test_bounces.py deleted file mode 100644 index 3d8469112..000000000 --- a/src/mailman/tests/test_bounces.py +++ /dev/null @@ -1,227 +0,0 @@ -# Copyright (C) 2001-2011 by the Free Software Foundation, Inc. -# -# This file is part of GNU Mailman. -# -# GNU Mailman is free software: you can redistribute it and/or modify it under -# the terms of the GNU General Public License as published by the Free -# Software Foundation, either version 3 of the License, or (at your option) -# any later version. -# -# GNU Mailman is distributed in the hope that it will be useful, but WITHOUT -# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or -# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for -# more details. -# -# You should have received a copy of the GNU General Public License along with -# GNU Mailman. If not, see <http://www.gnu.org/licenses/>. - -"""Test the bounce detection modules.""" - -from __future__ import absolute_import, unicode_literals - -__metaclass__ = type -__all__ = [ - 'test_suite', - ] - - -import email -import unittest - -from contextlib import closing -from pkg_resources import resource_stream - -from mailman.app.finder import find_components -from mailman.bouncers.caiwireless import Caiwireless -from mailman.bouncers.microsoft import Microsoft -from mailman.bouncers.smtp32 import SMTP32 -from mailman.interfaces.bounce import BounceStatus, IBounceDetector - - - -class BounceTest(unittest.TestCase): - DATA = ( - # Postfix bounces - ('Postfix', 'postfix_01.txt', ['xxxxx@local.ie']), - ('Postfix', 'postfix_02.txt', ['yyyyy@digicool.com']), - ('Postfix', 'postfix_03.txt', ['ttttt@ggggg.com']), - ('Postfix', 'postfix_04.txt', ['davidlowie@mail1.keftamail.com']), - ('Postfix', 'postfix_05.txt', ['bjelf@detectit.net']), - # Exim bounces - ('Exim', 'exim_01.txt', ['delangen@its.tudelft.nl']), - # SimpleMatch bounces - ('SimpleMatch', 'sendmail_01.txt', ['zzzzz@nfg.nl']), - ('SimpleMatch', 'simple_01.txt', ['bbbsss@turbosport.com']), - ('SimpleMatch', 'simple_02.txt', ['chris.ggggmmmm@usa.net']), - ('SimpleMatch', 'simple_04.txt', ['claird@starbase.neosoft.com']), - ('SimpleMatch', 'newmailru_01.txt', ['zzzzz@newmail.ru']), - ('SimpleMatch', 'hotpop_01.txt', ['allensmithee@hotpop.com']), - ('SimpleMatch', 'microsoft_03.txt', ['midica@banknbr.com']), - ('SimpleMatch', 'simple_05.txt', ['rlosardo@sbcglobal.net']), - ('SimpleMatch', 'simple_06.txt', ['dlyle@hamiltonpacific.com']), - ('SimpleMatch', 'simple_07.txt', ['william.xxxx@sbcglobal.net']), - ('SimpleMatch', 'simple_08.txt', ['severin.XXX@t-online.de']), - ('SimpleMatch', 'simple_09.txt', ['RobotMail@auto-walther.de']), - ('SimpleMatch', 'simple_10.txt', ['sais@thehartford.com']), - ('SimpleMatch', 'simple_11.txt', ['carlosr73@hartfordlife.com']), - ('SimpleMatch', 'simple_12.txt', ['charrogar@rhine1.andrew.ac.jp']), - ('SimpleMatch', 'simple_13.txt', ['dycusibreix@ademe.fr']), - ('SimpleMatch', 'simple_14.txt', ['dump@dachamp.com', - 'iqxwmmfauudpo@dachamp.com']), - ('SimpleMatch', 'simple_15.txt', ['isam@kviv.be']), - ('SimpleMatch', 'simple_16.txt', ['xvlogtfsei@the-messenger.com']), - ('SimpleMatch', 'simple_17.txt', ['internetsailing@gmail.com']), - ('SimpleMatch', 'simple_18.txt', ['powell@kesslersupply.com']), - ('SimpleMatch', 'simple_19.txt', ['mcfall@cepi.com.ar']), - ('SimpleMatch', 'simple_20.txt', ['duke@ald.socgen.com']), - ('SimpleMatch', 'simple_23.txt', ['ketchuy@dadoservice.it']), - ('SimpleMatch', 'simple_24.txt', ['liberty@gomaps.com']), - ('SimpleMatch', 'simple_25.txt', ['mahau@cnbearing.com']), - ('SimpleMatch', 'simple_26.txt', ['reilizavet@lar.ieo.it']), - ('SimpleMatch', 'simple_27.txt', ['kulp@webmail.pla.net.py']), - ('SimpleMatch', 'bounce_02.txt', ['acinsp1@midsouth.rr.com']), - ('SimpleMatch', 'bounce_03.txt', ['james@jeborall.demon.co.uk']), - # SimpleWarning - ('SimpleWarning', 'simple_03.txt', BounceStatus.non_fatal), - ('SimpleWarning', 'simple_21.txt', BounceStatus.non_fatal), - ('SimpleWarning', 'simple_22.txt', BounceStatus.non_fatal), - # GroupWise - ('GroupWise', 'groupwise_01.txt', ['thoff@MAINEX1.ASU.EDU']), - # This one really sucks 'cause it's text/html. Just make sure it - # doesn't throw an exception, but we won't get any meaningful - # addresses back from it. - ('GroupWise', 'groupwise_02.txt', []), - # Actually, it's from Exchange, and Exchange does recognize it - ('Exchange', 'groupwise_02.txt', ['omarmo@thebas.com']), - # Yale's own - ('Yale', 'yale_01.txt', ['thomas.dtankengine@cs.yale.edu', - 'thomas.dtankengine@yale.edu']), - # DSN, i.e. RFC 1894 - ('DSN', 'dsn_01.txt', ['JimmyMcEgypt@go.com']), - ('DSN', 'dsn_02.txt', ['zzzzz@zeus.hud.ac.uk']), - ('DSN', 'dsn_03.txt', ['ddd.kkk@advalvas.be']), - ('DSN', 'dsn_04.txt', ['max.haas@unibas.ch']), - ('DSN', 'dsn_05.txt', BounceStatus.non_fatal), - ('DSN', 'dsn_06.txt', BounceStatus.non_fatal), - ('DSN', 'dsn_07.txt', BounceStatus.non_fatal), - ('DSN', 'dsn_08.txt', BounceStatus.non_fatal), - ('DSN', 'dsn_09.txt', ['pr@allen-heath.com']), - ('DSN', 'dsn_10.txt', ['anne.person@dom.ain']), - ('DSN', 'dsn_11.txt', ['joem@example.com']), - ('DSN', 'dsn_12.txt', ['auaauqdgrdz@jtc-con.co.jp']), - ('DSN', 'dsn_13.txt', ['marcooherbst@cardinal.com']), - ('DSN', 'dsn_14.txt', ['artboardregistration@home.dk']), - ('DSN', 'dsn_15.txt', ['horu@ccc-ces.com']), - # Microsoft Exchange - ('Exchange', 'microsoft_01.txt', ['DJBENNETT@IKON.COM']), - ('Exchange', 'microsoft_02.txt', ['MDMOORE@BALL.COM']), - # SMTP32 - ('SMTP32', 'smtp32_01.txt', ['oliver@pcworld.com.ph']), - ('SMTP32', 'smtp32_02.txt', ['lists@mail.spicynoodles.com']), - ('SMTP32', 'smtp32_03.txt', ['borisk@gw.xraymedia.com']), - ('SMTP32', 'smtp32_04.txt', ['after_another@pacbell.net', - 'one_bad_address@pacbell.net']), - ('SMTP32', 'smtp32_05.txt', ['jmrpowersports@jmrpowersports.com']), - ('SMTP32', 'smtp32_06.txt', ['Absolute_garbage_addr@pacbell.net']), - ('SMTP32', 'smtp32_07.txt', ['info@husbyran.com']), - # Qmail - ('Qmail', 'qmail_01.txt', ['psadisc@wwwmail.n-h.de']), - ('Qmail', 'qmail_02.txt', ['rauschlo@frontfin.com']), - ('Qmail', 'qmail_03.txt', ['crown@hbc.co.jp']), - ('Qmail', 'qmail_04.txt', ['merotiia@tennisnsw.com.au']), - ('Qmail', 'qmail_05.txt', ['ivokggrrdvc@caixaforte.freeservers.com']), - # LLNL's custom Sendmail - ('LLNL', 'llnl_01.txt', ['trotts1@llnl.gov']), - # Netscape's server... - ('Netscape', 'netscape_01.txt', ['aaaaa@corel.com', - 'bbbbb@corel.com']), - # Yahoo's proprietary format - ('Yahoo', 'yahoo_01.txt', ['subscribe.motorcycles@listsociety.com']), - ('Yahoo', 'yahoo_02.txt', ['agarciamartiartu@yahoo.es']), - ('Yahoo', 'yahoo_03.txt', ['cresus22@yahoo.com']), - ('Yahoo', 'yahoo_04.txt', ['agarciamartiartu@yahoo.es', - 'open00now@yahoo.co.uk']), - ('Yahoo', 'yahoo_05.txt', ['cresus22@yahoo.com', - 'jjb700@yahoo.com']), - ('Yahoo', 'yahoo_06.txt', ['andrew_polevoy@yahoo.com', - 'baruch_sterin@yahoo.com', - 'rjhoeks@yahoo.com', - 'tritonrugger91@yahoo.com']), - ('Yahoo', 'yahoo_07.txt', ['mark1960_1998@yahoo.com', - 'ovchenkov@yahoo.com', - 'tsa412@yahoo.com', - 'vaxheadroom@yahoo.com']), - ('Yahoo', 'yahoo_08.txt', ['chatrathis@yahoo.com', - 'crownjules01@yahoo.com', - 'cwl_999@yahoo.com', - 'eichaiwiu@yahoo.com', - 'rjhoeks@yahoo.com', - 'yuli_kolesnikov@yahoo.com']), - ('Yahoo', 'yahoo_09.txt', ['hankel_o_fung@yahoo.com', - 'ultravirus2001@yahoo.com']), - ('Yahoo', 'yahoo_10.txt', ['jajcchoo@yahoo.com', - 'lyons94706@yahoo.com', - 'turtle4jne@yahoo.com']), - # sina.com appears to use their own weird SINAEMAIL MTA - ('Sina', 'sina_01.txt', ['boboman76@sina.com', 'alan_t18@sina.com']), - # No address can be detected in these... - # dumbass_01.txt - We love Microsoft. :( - # Done - ) - - def _getmsg(self, filename): - with closing(resource_stream('mailman.tests.bounces', filename)) as fp: - return email.message_from_file(fp) - - def test_bounce(self): - detectors = {} - for detector in find_components('mailman.bouncers', IBounceDetector): - detectors[detector.__name__] = detector() - for detector_name, filename, expected_addresses in self.DATA: - msg = self._getmsg(filename) - found_addresses = detectors[detector_name].process(msg) - # Some modules return None instead of the empty sequence. - if found_addresses is None: - found_addresses = set() - elif found_addresses is not BounceStatus.non_fatal: - found_addresses = set(found_addresses) - if expected_addresses is not BounceStatus.non_fatal: - expected_addresses = set(expected_addresses) - self.assertEqual(found_addresses, expected_addresses) - - def test_SMTP32_failure(self): - # This file has no X-Mailer: header - msg = self._getmsg('postfix_01.txt') - self.failIf(msg['x-mailer'] is not None) - self.failIf(SMTP32().process(msg)) - - def test_caiwireless(self): - # BAW: this is a mostly bogus test; I lost the samples. :( - msg = email.message_from_string("""\ -Content-Type: multipart/report; boundary=BOUNDARY - ---BOUNDARY - ---BOUNDARY-- - -""") - self.assertEqual(len(Caiwireless().process(msg)), 0) - - def test_microsoft(self): - # BAW: similarly as above, I lost the samples. :( - msg = email.message_from_string("""\ -Content-Type: multipart/report; boundary=BOUNDARY - ---BOUNDARY - ---BOUNDARY-- - -""") - self.assertEqual(len(Microsoft().process(msg)), 0) - - - -def test_suite(): - suite = unittest.TestSuite() - suite.addTest(unittest.makeSuite(BounceTest)) - return suite |
