summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBarry Warsaw2011-04-27 21:34:28 -0400
committerBarry Warsaw2011-04-27 21:34:28 -0400
commitdbe88c1d0a08999c5209b3a1bc70c458dbe270b4 (patch)
tree3a2b6f6cd1e4688089cff14841044947a8eb102b /src
parent7181a4d6638605595b043f66eb3cd01dd93a0ff2 (diff)
downloadmailman-dbe88c1d0a08999c5209b3a1bc70c458dbe270b4.tar.gz
mailman-dbe88c1d0a08999c5209b3a1bc70c458dbe270b4.tar.zst
mailman-dbe88c1d0a08999c5209b3a1bc70c458dbe270b4.zip
Diffstat (limited to 'src')
-rw-r--r--src/mailman/bouncers/dsn.py54
-rw-r--r--src/mailman/bouncers/llnl.py2
-rw-r--r--src/mailman/bouncers/qmail.py5
-rw-r--r--src/mailman/bouncers/simplewarning.py12
-rw-r--r--src/mailman/bounces/tests/test_bounces.py34
-rw-r--r--src/mailman/interfaces/bounce.py27
-rw-r--r--src/mailman/queue/bounce.py6
-rw-r--r--src/mailman/tests/test_bounces.py227
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