summaryrefslogtreecommitdiff
path: root/mailman/Bouncers
diff options
context:
space:
mode:
Diffstat (limited to 'mailman/Bouncers')
-rw-r--r--mailman/Bouncers/BouncerAPI.py64
-rw-r--r--mailman/Bouncers/Caiwireless.py45
-rw-r--r--mailman/Bouncers/Compuserve.py46
-rw-r--r--mailman/Bouncers/DSN.py99
-rw-r--r--mailman/Bouncers/Exchange.py48
-rw-r--r--mailman/Bouncers/Exim.py31
-rw-r--r--mailman/Bouncers/GroupWise.py71
-rw-r--r--mailman/Bouncers/LLNL.py32
-rw-r--r--mailman/Bouncers/Microsoft.py53
-rw-r--r--mailman/Bouncers/Netscape.py89
-rw-r--r--mailman/Bouncers/Postfix.py86
-rw-r--r--mailman/Bouncers/Qmail.py72
-rw-r--r--mailman/Bouncers/SMTP32.py60
-rw-r--r--mailman/Bouncers/SimpleMatch.py204
-rw-r--r--mailman/Bouncers/SimpleWarning.py62
-rw-r--r--mailman/Bouncers/Sina.py48
-rw-r--r--mailman/Bouncers/Yahoo.py54
-rw-r--r--mailman/Bouncers/Yale.py80
-rw-r--r--mailman/Bouncers/__init__.py0
19 files changed, 0 insertions, 1244 deletions
diff --git a/mailman/Bouncers/BouncerAPI.py b/mailman/Bouncers/BouncerAPI.py
deleted file mode 100644
index f4712ec20..000000000
--- a/mailman/Bouncers/BouncerAPI.py
+++ /dev/null
@@ -1,64 +0,0 @@
-# Copyright (C) 1998-2009 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/>.
-
-"""Contains all the common functionality for msg bounce scanning API.
-
-This module can also be used as the basis for a bounce detection testing
-framework. When run as a script, it expects two arguments, the listname and
-the filename containing the bounce message.
-"""
-
-import sys
-
-# 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()
-
-
-BOUNCE_PIPELINE = [
- 'DSN',
- 'Qmail',
- 'Postfix',
- 'Yahoo',
- 'Caiwireless',
- 'Exchange',
- 'Exim',
- 'Netscape',
- 'Compuserve',
- 'Microsoft',
- 'GroupWise',
- 'SMTP32',
- 'SimpleMatch',
- 'SimpleWarning',
- 'Yale',
- 'LLNL',
- ]
-
-
-
-# msg must be a mimetools.Message
-def ScanMessages(mlist, msg):
- for module in BOUNCE_PIPELINE:
- modname = 'mailman.Bouncers.' + module
- __import__(modname)
- addrs = sys.modules[modname].process(msg)
- if addrs:
- # Return addrs even if it is Stop. BounceRunner needs this info.
- return addrs
- return []
diff --git a/mailman/Bouncers/Caiwireless.py b/mailman/Bouncers/Caiwireless.py
deleted file mode 100644
index 3bf03cc62..000000000
--- a/mailman/Bouncers/Caiwireless.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# Copyright (C) 1998-2009 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/>.
-
-"""Parse mystery style generated by MTA at caiwireless.net."""
-
-import re
-import email
-
-tcre = re.compile(r'the following recipients did not receive this message:',
- re.IGNORECASE)
-acre = re.compile(r'<(?P<addr>[^>]*)>')
-
-
-
-def process(msg):
- if msg.get_content_type() <> 'multipart/mixed':
- return None
- # simple state machine
- # 0 == nothing seen
- # 1 == tag line seen
- state = 0
- # This format thinks it's a MIME, but it really isn't
- for line in email.Iterators.body_line_iterator(msg):
- line = line.strip()
- if state == 0 and tcre.match(line):
- state = 1
- elif state == 1 and line:
- mo = acre.match(line)
- if not mo:
- return None
- return [mo.group('addr')]
diff --git a/mailman/Bouncers/Compuserve.py b/mailman/Bouncers/Compuserve.py
deleted file mode 100644
index 2297a72a9..000000000
--- a/mailman/Bouncers/Compuserve.py
+++ /dev/null
@@ -1,46 +0,0 @@
-# Copyright (C) 1998-2009 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/>.
-
-"""Compuserve has its own weird format for bounces."""
-
-import re
-import email
-
-dcre = re.compile(r'your message could not be delivered', re.IGNORECASE)
-acre = re.compile(r'Invalid receiver address: (?P<addr>.*)')
-
-
-
-def process(msg):
- # simple state machine
- # 0 = nothing seen yet
- # 1 = intro line seen
- state = 0
- addrs = []
- for line in email.Iterators.body_line_iterator(msg):
- if state == 0:
- mo = dcre.search(line)
- if mo:
- state = 1
- elif state == 1:
- mo = dcre.search(line)
- if mo:
- break
- mo = acre.search(line)
- if mo:
- addrs.append(mo.group('addr'))
- return addrs
diff --git a/mailman/Bouncers/DSN.py b/mailman/Bouncers/DSN.py
deleted file mode 100644
index 37e5bcb83..000000000
--- a/mailman/Bouncers/DSN.py
+++ /dev/null
@@ -1,99 +0,0 @@
-# Copyright (C) 1998-2009 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/>.
-
-"""Parse RFC 3464 (i.e. DSN) bounce formats.
-
-RFC 3464 obsoletes 1894 which was the old DSN standard. This module has not
-been audited for differences between the two.
-"""
-
-from email.Iterators import typed_subpart_iterator
-from email.Utils import parseaddr
-
-from mailman.Bouncers.BouncerAPI import Stop
-
-
-
-def check(msg):
- # Iterate over each message/delivery-status subpart
- addrs = []
- for part in typed_subpart_iterator(msg, 'message', 'delivery-status'):
- if not part.is_multipart():
- # Huh?
- continue
- # 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():
- # 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
- # as a synonym for Original-Recipient, but some apparently use
- # that for other purposes :(
- #
- # Also grok out Action so we can do something with that too.
- action = msgblock.get('action', '').lower()
- # Some MTAs have been observed that put comments on the action.
- if action.startswith('delayed'):
- return Stop
- if not action.startswith('fail'):
- # Some non-permanent failure, so ignore this block
- continue
- params = []
- foundp = False
- for header in ('original-recipient', 'final-recipient'):
- for k, v in msgblock.get_params([], header):
- if k.lower() == 'rfc822':
- foundp = True
- else:
- params.append(k)
- if foundp:
- # Note that params should already be unquoted.
- addrs.extend(params)
- break
- else:
- # MAS: This is a kludge, but SMTP-GATEWAY01.intra.home.dk
- # has a final-recipient with an angle-addr and no
- # address-type parameter at all. Non-compliant, but ...
- for param in params:
- if param.startswith('<') and param.endswith('>'):
- addrs.append(param[1:-1])
- # Uniquify
- rtnaddrs = {}
- for a in addrs:
- if a is not None:
- realname, a = parseaddr(a)
- rtnaddrs[a] = True
- return rtnaddrs.keys()
-
-
-
-def process(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 None
- return check(msg)
diff --git a/mailman/Bouncers/Exchange.py b/mailman/Bouncers/Exchange.py
deleted file mode 100644
index cf8beefce..000000000
--- a/mailman/Bouncers/Exchange.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# Copyright (C) 2002-2009 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/>.
-
-"""Recognizes (some) Microsoft Exchange formats."""
-
-import re
-import email.Iterators
-
-scre = re.compile('did not reach the following recipient')
-ecre = re.compile('MSEXCH:')
-a1cre = re.compile('SMTP=(?P<addr>[^;]+); on ')
-a2cre = re.compile('(?P<addr>[^ ]+) on ')
-
-
-
-def process(msg):
- addrs = {}
- it = email.Iterators.body_line_iterator(msg)
- # Find the start line
- for line in it:
- if scre.search(line):
- break
- else:
- return []
- # Search each line until we hit the end line
- for line in it:
- if ecre.search(line):
- break
- mo = a1cre.search(line)
- if not mo:
- mo = a2cre.search(line)
- if mo:
- addrs[mo.group('addr')] = 1
- return addrs.keys()
diff --git a/mailman/Bouncers/Exim.py b/mailman/Bouncers/Exim.py
deleted file mode 100644
index 0f4e7f4cf..000000000
--- a/mailman/Bouncers/Exim.py
+++ /dev/null
@@ -1,31 +0,0 @@
-# Copyright (C) 1998-2009 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/>.
-
-"""Parse bounce messages generated by Exim.
-
-Exim adds an X-Failed-Recipients: header to bounce messages containing
-an `addresslist' of failed addresses.
-
-"""
-
-from email.Utils import getaddresses
-
-
-
-def process(msg):
- all = msg.get_all('x-failed-recipients', [])
- return [a for n, a in getaddresses(all)]
diff --git a/mailman/Bouncers/GroupWise.py b/mailman/Bouncers/GroupWise.py
deleted file mode 100644
index e74291217..000000000
--- a/mailman/Bouncers/GroupWise.py
+++ /dev/null
@@ -1,71 +0,0 @@
-# Copyright (C) 1998-2009 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/>.
-
-"""This appears to be the format for Novell GroupWise and NTMail
-
-X-Mailer: Novell GroupWise Internet Agent 5.5.3.1
-X-Mailer: NTMail v4.30.0012
-X-Mailer: Internet Mail Service (5.5.2653.19)
-"""
-
-import re
-from email.Message import Message
-from cStringIO import StringIO
-
-acre = re.compile(r'<(?P<addr>[^>]*)>')
-
-
-
-def find_textplain(msg):
- if msg.get_content_type() == 'text/plain':
- return msg
- if msg.is_multipart:
- for part in msg.get_payload():
- if not isinstance(part, Message):
- continue
- ret = find_textplain(part)
- if ret:
- return ret
- return None
-
-
-
-def process(msg):
- if msg.get_content_type() <> 'multipart/mixed' or not msg['x-mailer']:
- return None
- addrs = {}
- # find the first text/plain part in the message
- textplain = find_textplain(msg)
- if not textplain:
- return None
- body = StringIO(textplain.get_payload())
- while 1:
- line = body.readline()
- if not line:
- break
- mo = acre.search(line)
- if mo:
- addrs[mo.group('addr')] = 1
- elif '@' in line:
- i = line.find(' ')
- if i == 0:
- continue
- if i < 0:
- addrs[line] = 1
- else:
- addrs[line[:i]] = 1
- return addrs.keys()
diff --git a/mailman/Bouncers/LLNL.py b/mailman/Bouncers/LLNL.py
deleted file mode 100644
index cc1a08542..000000000
--- a/mailman/Bouncers/LLNL.py
+++ /dev/null
@@ -1,32 +0,0 @@
-# Copyright (C) 2001-2009 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/>.
-
-"""LLNL's custom Sendmail bounce message."""
-
-import re
-import email
-
-acre = re.compile(r',\s*(?P<addr>\S+@[^,]+),', re.IGNORECASE)
-
-
-
-def process(msg):
- for line in email.Iterators.body_line_iterator(msg):
- mo = acre.search(line)
- if mo:
- return [mo.group('addr')]
- return []
diff --git a/mailman/Bouncers/Microsoft.py b/mailman/Bouncers/Microsoft.py
deleted file mode 100644
index 98d27d4ee..000000000
--- a/mailman/Bouncers/Microsoft.py
+++ /dev/null
@@ -1,53 +0,0 @@
-# Copyright (C) 1998-2009 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/>.
-
-"""Microsoft's `SMTPSVC' nears I kin tell."""
-
-import re
-from cStringIO import StringIO
-
-scre = re.compile(r'transcript of session follows', re.IGNORECASE)
-
-
-
-def process(msg):
- if msg.get_content_type() <> 'multipart/mixed':
- return None
- # Find the first subpart, which has no MIME type
- try:
- subpart = msg.get_payload(0)
- except IndexError:
- # The message *looked* like a multipart but wasn't
- return None
- data = subpart.get_payload()
- if isinstance(data, list):
- # The message is a multi-multipart, so not a matching bounce
- return None
- body = StringIO(data)
- state = 0
- addrs = []
- while 1:
- line = body.readline()
- if not line:
- break
- if state == 0:
- if scre.search(line):
- state = 1
- if state == 1:
- if '@' in line:
- addrs.append(line)
- return addrs
diff --git a/mailman/Bouncers/Netscape.py b/mailman/Bouncers/Netscape.py
deleted file mode 100644
index 319329e84..000000000
--- a/mailman/Bouncers/Netscape.py
+++ /dev/null
@@ -1,89 +0,0 @@
-# Copyright (C) 1998-2009 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/>.
-
-"""Netscape Messaging Server bounce formats.
-
-I've seen at least one NMS server version 3.6 (envy.gmp.usyd.edu.au) bounce
-messages of this format. Bounces come in DSN MIME format, but don't include
-any -Recipient: headers. Gotta just parse the text :(
-
-NMS 4.1 (dfw-smtpin1.email.verio.net) seems even worse, but we'll try to
-decipher the format here too.
-
-"""
-
-import re
-from cStringIO import StringIO
-
-pcre = re.compile(
- r'This Message was undeliverable due to the following reason:',
- re.IGNORECASE)
-
-acre = re.compile(
- r'(?P<reply>please reply to)?.*<(?P<addr>[^>]*)>',
- re.IGNORECASE)
-
-
-
-def flatten(msg, leaves):
- # give us all the leaf (non-multipart) subparts
- if msg.is_multipart():
- for part in msg.get_payload():
- flatten(part, leaves)
- else:
- leaves.append(msg)
-
-
-
-def process(msg):
- # Sigh. Some show NMS 3.6's show
- # multipart/report; report-type=delivery-status
- # and some show
- # multipart/mixed;
- if not msg.is_multipart():
- return None
- # We're looking for a text/plain subpart occuring before a
- # message/delivery-status subpart.
- plainmsg = None
- leaves = []
- flatten(msg, leaves)
- for i, subpart in zip(range(len(leaves)-1), leaves):
- if subpart.get_content_type() == 'text/plain':
- plainmsg = subpart
- break
- if not plainmsg:
- return None
- # Total guesswork, based on captured examples...
- body = StringIO(plainmsg.get_payload())
- addrs = []
- while 1:
- line = body.readline()
- if not line:
- break
- mo = pcre.search(line)
- if mo:
- # We found a bounce section, but I have no idea what the official
- # format inside here is. :( We'll just search for <addr>
- # strings.
- while 1:
- line = body.readline()
- if not line:
- break
- mo = acre.search(line)
- if mo and not mo.group('reply'):
- addrs.append(mo.group('addr'))
- return addrs
diff --git a/mailman/Bouncers/Postfix.py b/mailman/Bouncers/Postfix.py
deleted file mode 100644
index cfc97a05e..000000000
--- a/mailman/Bouncers/Postfix.py
+++ /dev/null
@@ -1,86 +0,0 @@
-# Copyright (C) 1998-2009 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/>.
-
-"""Parse bounce messages generated by Postfix.
-
-This also matches something called `Keftamail' which looks just like Postfix
-bounces with the word Postfix scratched out and the word `Keftamail' written
-in in crayon.
-
-It also matches something claiming to be `The BNS Postfix program', and
-`SMTP_Gateway'. Everybody's gotta be different, huh?
-"""
-
-import re
-from cStringIO import StringIO
-
-
-
-def flatten(msg, leaves):
- # give us all the leaf (non-multipart) subparts
- if msg.is_multipart():
- for part in msg.get_payload():
- flatten(part, leaves)
- else:
- leaves.append(msg)
-
-
-
-# are these heuristics correct or guaranteed?
-pcre = re.compile(r'[ \t]*the\s*(bns)?\s*(postfix|keftamail|smtp_gateway)',
- re.IGNORECASE)
-rcre = re.compile(r'failure reason:$', re.IGNORECASE)
-acre = re.compile(r'<(?P<addr>[^>]*)>:')
-
-def findaddr(msg):
- addrs = []
- body = StringIO(msg.get_payload())
- # simple state machine
- # 0 == nothing found
- # 1 == salutation found
- state = 0
- while 1:
- line = body.readline()
- if not line:
- break
- # preserve leading whitespace
- line = line.rstrip()
- # yes use match to match at beginning of string
- if state == 0 and (pcre.match(line) or rcre.match(line)):
- state = 1
- elif state == 1 and line:
- mo = acre.search(line)
- if mo:
- addrs.append(mo.group('addr'))
- # probably a continuation line
- return addrs
-
-
-
-def process(msg):
- if msg.get_content_type() not in ('multipart/mixed', 'multipart/report'):
- return None
- # We're looking for the plain/text subpart with a Content-Description: of
- # `notification'.
- leaves = []
- flatten(msg, leaves)
- for subpart in leaves:
- if subpart.get_content_type() == 'text/plain' and \
- subpart.get('content-description', '').lower() == 'notification':
- # then...
- return findaddr(subpart)
- return None
diff --git a/mailman/Bouncers/Qmail.py b/mailman/Bouncers/Qmail.py
deleted file mode 100644
index 2431da653..000000000
--- a/mailman/Bouncers/Qmail.py
+++ /dev/null
@@ -1,72 +0,0 @@
-# Copyright (C) 1998-2009 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/>.
-
-"""Parse bounce messages generated by qmail.
-
-Qmail actually has a standard, called QSBMF (qmail-send bounce message
-format), as described in
-
- http://cr.yp.to/proto/qsbmf.txt
-
-This module should be conformant.
-
-"""
-
-import re
-import email.Iterators
-
-# 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.',
- 'This is the mail delivery agent at',
- 'Unfortunately, your mail was not delivered'
- ]
-acre = re.compile(r'<(?P<addr>[^>]*)>:')
-
-
-
-def process(msg):
- addrs = []
- # simple state machine
- # 0 = nothing seen yet
- # 1 = intro paragraph seen
- # 2 = recip paragraphs seen
- state = 0
- for line in email.Iterators.body_line_iterator(msg):
- line = line.strip()
- if state == 0:
- for introtag in introtags:
- if line.startswith(introtag):
- state = 1
- break
- elif state == 1 and not line:
- # Looking for the end of the intro paragraph
- state = 2
- elif state == 2:
- if line.startswith('-'):
- # We're looking at the break paragraph, so we're done
- break
- # At this point we know we must be looking at a recipient
- # paragraph
- mo = acre.match(line)
- if mo:
- addrs.append(mo.group('addr'))
- # Otherwise, it must be a continuation line, so just ignore it
- # Not looking at anything in particular
- return addrs
diff --git a/mailman/Bouncers/SMTP32.py b/mailman/Bouncers/SMTP32.py
deleted file mode 100644
index a7fff2ed3..000000000
--- a/mailman/Bouncers/SMTP32.py
+++ /dev/null
@@ -1,60 +0,0 @@
-# Copyright (C) 1998-2009 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/>.
-
-"""Something which claims
-X-Mailer: <SMTP32 vXXXXXX>
-
-What the heck is this thing? Here's a recent host:
-
-% telnet 207.51.255.218 smtp
-Trying 207.51.255.218...
-Connected to 207.51.255.218.
-Escape character is '^]'.
-220 X1 NT-ESMTP Server 208.24.118.205 (IMail 6.00 45595-15)
-
-"""
-
-import re
-import email
-
-ecre = re.compile('original message follows', re.IGNORECASE)
-acre = re.compile(r'''
- ( # several different prefixes
- user\ mailbox[^:]*: # have been spotted in the
- |delivery\ failed[^:]*: # wild...
- |unknown\ user[^:]*:
- |undeliverable\ +to
- |delivery\ userid[^:]*:
- )
- \s* # space separator
- (?P<addr>[^\s]*) # and finally, the address
- ''', re.IGNORECASE | re.VERBOSE)
-
-
-
-def process(msg):
- mailer = msg.get('x-mailer', '')
- if not mailer.startswith('<SMTP32 v'):
- return
- addrs = {}
- for line in email.Iterators.body_line_iterator(msg):
- if ecre.search(line):
- break
- mo = acre.search(line)
- if mo:
- addrs[mo.group('addr')] = 1
- return addrs.keys()
diff --git a/mailman/Bouncers/SimpleMatch.py b/mailman/Bouncers/SimpleMatch.py
deleted file mode 100644
index 338f52a19..000000000
--- a/mailman/Bouncers/SimpleMatch.py
+++ /dev/null
@@ -1,204 +0,0 @@
-# Copyright (C) 1998-2009 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/>.
-
-"""Recognizes simple heuristically delimited bounces."""
-
-import re
-import email.Iterators
-
-
-
-def _c(pattern):
- return re.compile(pattern, re.IGNORECASE)
-
-# This is a list of tuples of the form
-#
-# (start cre, end cre, address cre)
-#
-# where `cre' means compiled regular expression, start is the line just before
-# the bouncing address block, end is the line just after the bouncing address
-# block, and address cre is the regexp that will recognize the addresses. It
-# must have a group called `addr' which will contain exactly and only the
-# address that bounced.
-PATTERNS = [
- # sdm.de
- (_c('here is your list of failed recipients'),
- _c('here is your returned mail'),
- _c(r'<(?P<addr>[^>]*)>')),
- # sz-sb.de, corridor.com, nfg.nl
- (_c('the following addresses had'),
- _c('transcript of session follows'),
- _c(r'<(?P<fulladdr>[^>]*)>|\(expanded from: <?(?P<addr>[^>)]*)>?\)')),
- # robanal.demon.co.uk
- (_c('this message was created automatically by mail delivery software'),
- _c('original message follows'),
- _c('rcpt to:\s*<(?P<addr>[^>]*)>')),
- # s1.com (InterScan E-Mail VirusWall NT ???)
- (_c('message from interscan e-mail viruswall nt'),
- _c('end of message'),
- _c('rcpt to:\s*<(?P<addr>[^>]*)>')),
- # Smail
- (_c('failed addresses follow:'),
- _c('message text follows:'),
- _c(r'\s*(?P<addr>\S+@\S+)')),
- # newmail.ru
- (_c('This is the machine generated message from mail service.'),
- _c('--- Below the next line is a copy of the message.'),
- _c('<(?P<addr>[^>]*)>')),
- # turbosport.com runs something called `MDaemon 3.5.2' ???
- (_c('The following addresses did NOT receive a copy of your message:'),
- _c('--- Session Transcript ---'),
- _c('[>]\s*(?P<addr>.*)$')),
- # usa.net
- (_c('Intended recipient:\s*(?P<addr>.*)$'),
- _c('--------RETURNED MAIL FOLLOWS--------'),
- _c('Intended recipient:\s*(?P<addr>.*)$')),
- # hotpop.com
- (_c('Undeliverable Address:\s*(?P<addr>.*)$'),
- _c('Original message attached'),
- _c('Undeliverable Address:\s*(?P<addr>.*)$')),
- # Another demon.co.uk format
- (_c('This message was created automatically by mail delivery'),
- _c('^---- START OF RETURNED MESSAGE ----'),
- _c("addressed to '(?P<addr>[^']*)'")),
- # Prodigy.net full mailbox
- (_c("User's mailbox is full:"),
- _c('Unable to deliver mail.'),
- _c("User's mailbox is full:\s*<(?P<addr>[^>]*)>")),
- # Microsoft SMTPSVC
- (_c('The email below could not be delivered to the following user:'),
- _c('Old message:'),
- _c('<(?P<addr>[^>]*)>')),
- # Yahoo on behalf of other domains like sbcglobal.net
- (_c('Unable to deliver message to the following address\(es\)\.'),
- _c('--- Original message follows\.'),
- _c('<(?P<addr>[^>]*)>:')),
- # googlemail.com
- (_c('Delivery to the following recipient failed'),
- _c('----- Original message -----'),
- _c('^\s*(?P<addr>[^\s@]+@[^\s@]+)\s*$')),
- # kundenserver.de
- (_c('A message that you sent could not be delivered'),
- _c('^---'),
- _c('<(?P<addr>[^>]*)>')),
- # another kundenserver.de
- (_c('A message that you sent could not be delivered'),
- _c('^---'),
- _c('^(?P<addr>[^\s@]+@[^\s@:]+):')),
- # thehartford.com
- (_c('Delivery to the following recipients failed'),
- # this one may or may not have the original message, but there's nothing
- # unique to stop on, so stop on the first line of at least 3 characters
- # that doesn't start with 'D' (to not stop immediately) and has no '@'.
- _c('^[^D][^@]{2,}$'),
- _c('^\s*(?P<addr>[^\s@]+@[^\s@]+)\s*$')),
- # and another thehartfod.com/hartfordlife.com
- (_c('^Your message\s*$'),
- _c('^because:'),
- _c('^\s*(?P<addr>[^\s@]+@[^\s@]+)\s*$')),
- # kviv.be (InterScan NT)
- (_c('^Unable to deliver message to'),
- _c(r'\*+\s+End of message\s+\*+'),
- _c('<(?P<addr>[^>]*)>')),
- # earthlink.net supported domains
- (_c('^Sorry, unable to deliver your message to'),
- _c('^A copy of the original message'),
- _c('\s*(?P<addr>[^\s@]+@[^\s@]+)\s+')),
- # ademe.fr
- (_c('^A message could not be delivered to:'),
- _c('^Subject:'),
- _c('^\s*(?P<addr>[^\s@]+@[^\s@]+)\s*$')),
- # andrew.ac.jp
- (_c('^Invalid final delivery userid:'),
- _c('^Original message follows.'),
- _c('\s*(?P<addr>[^\s@]+@[^\s@]+)\s*$')),
- # E500_SMTP_Mail_Service@lerctr.org
- (_c('------ Failed Recipients ------'),
- _c('-------- Returned Mail --------'),
- _c('<(?P<addr>[^>]*)>')),
- # cynergycom.net
- (_c('A message that you sent could not be delivered'),
- _c('^---'),
- _c('(?P<addr>[^\s@]+@[^\s@)]+)')),
- # LSMTP for Windows
- (_c('^--> Error description:\s*$'),
- _c('^Error-End:'),
- _c('^Error-for:\s+(?P<addr>[^\s@]+@[^\s@]+)')),
- # Qmail with a tri-language intro beginning in spanish
- (_c('Your message could not be delivered'),
- _c('^-'),
- _c('<(?P<addr>[^>]*)>:')),
- # socgen.com
- (_c('Your message could not be delivered to'),
- _c('^\s*$'),
- _c('(?P<addr>[^\s@]+@[^\s@]+)')),
- # dadoservice.it
- (_c('Your message has encountered delivery problems'),
- _c('Your message reads'),
- _c('addressed to\s*(?P<addr>[^\s@]+@[^\s@)]+)')),
- # gomaps.com
- (_c('Did not reach the following recipient'),
- _c('^\s*$'),
- _c('\s(?P<addr>[^\s@]+@[^\s@]+)')),
- # EYOU MTA SYSTEM
- (_c('This is the deliver program at'),
- _c('^-'),
- _c('^(?P<addr>[^\s@]+@[^\s@<>]+)')),
- # A non-standard qmail at ieo.it
- (_c('this is the email server at'),
- _c('^-'),
- _c('\s(?P<addr>[^\s@]+@[^\s@]+)[\s,]')),
- # pla.net.py (MDaemon.PRO ?)
- (_c('- no such user here'),
- _c('There is no user'),
- _c('^(?P<addr>[^\s@]+@[^\s@]+)\s')),
- # Next one goes here...
- ]
-
-
-
-def process(msg, patterns=None):
- if patterns is None:
- patterns = PATTERNS
- # simple state machine
- # 0 = nothing seen yet
- # 1 = intro seen
- addrs = {}
- # MAS: This is a mess. The outer loop used to be over the message
- # so we only looped through the message once. Looping through the
- # message for each set of patterns is obviously way more work, but
- # if we don't do it, problems arise because scre from the wrong
- # pattern set matches first and then acre doesn't match. The
- # alternative is to split things into separate modules, but then
- # we process the message multiple times anyway.
- for scre, ecre, acre in patterns:
- state = 0
- for line in email.Iterators.body_line_iterator(msg):
- if state == 0:
- if scre.search(line):
- state = 1
- if state == 1:
- mo = acre.search(line)
- if mo:
- addr = mo.group('addr')
- if addr:
- addrs[mo.group('addr')] = 1
- elif ecre.search(line):
- break
- if addrs:
- break
- return addrs.keys()
diff --git a/mailman/Bouncers/SimpleWarning.py b/mailman/Bouncers/SimpleWarning.py
deleted file mode 100644
index ab18d2530..000000000
--- a/mailman/Bouncers/SimpleWarning.py
+++ /dev/null
@@ -1,62 +0,0 @@
-# Copyright (C) 2001-2009 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/>.
-
-"""Recognizes simple heuristically delimited warnings."""
-
-from mailman.Bouncers.BouncerAPI import Stop
-from mailman.Bouncers.SimpleMatch import _c
-from mailman.Bouncers.SimpleMatch import process as _process
-
-
-
-# This is a list of tuples of the form
-#
-# (start cre, end cre, address cre)
-#
-# where `cre' means compiled regular expression, start is the line just before
-# the bouncing address block, end is the line just after the bouncing address
-# block, and address cre is the regexp that will recognize the addresses. It
-# must have a group called `addr' which will contain exactly and only the
-# address that bounced.
-patterns = [
- # pop3.pta.lia.net
- (_c('The address to which the message has not yet been delivered is'),
- _c('No action is required on your part'),
- _c(r'\s*(?P<addr>\S+@\S+)\s*')),
- # This is from MessageSwitch. It is a kludge because the text that
- # identifies it as a warning only comes after the address. We can't
- # use ecre, because it really isn't significant, so we fake it. Once
- # we see the start, we know it's a warning, and we're going to return
- # Stop anyway, so we match anything for the address and end.
- (_c('This is just a warning, you do not need to take any action'),
- _c('.+'),
- _c('(?P<addr>.+)')),
- # Symantec_AntiVirus_for_SMTP_Gateways - see comments for MessageSwitch
- (_c('Delivery attempts will continue to be made'),
- _c('.+'),
- _c('(?P<addr>.+)')),
- # Next one goes here...
- ]
-
-
-
-def process(msg):
- if _process(msg, patterns):
- # It's a recognized warning so stop now
- return Stop
- else:
- return []
diff --git a/mailman/Bouncers/Sina.py b/mailman/Bouncers/Sina.py
deleted file mode 100644
index a6b8e0911..000000000
--- a/mailman/Bouncers/Sina.py
+++ /dev/null
@@ -1,48 +0,0 @@
-# Copyright (C) 2002-2009 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/>.
-
-"""sina.com bounces"""
-
-import re
-from email import Iterators
-
-acre = re.compile(r'<(?P<addr>[^>]*)>')
-
-
-
-def process(msg):
- if msg.get('from', '').lower() <> 'mailer-daemon@sina.com':
- print 'out 1'
- return []
- if not msg.is_multipart():
- print 'out 2'
- return []
- # The interesting bits are in the first text/plain multipart
- part = None
- try:
- part = msg.get_payload(0)
- except IndexError:
- pass
- if not part:
- print 'out 3'
- return []
- addrs = {}
- for line in Iterators.body_line_iterator(part):
- mo = acre.match(line)
- if mo:
- addrs[mo.group('addr')] = 1
- return addrs.keys()
diff --git a/mailman/Bouncers/Yahoo.py b/mailman/Bouncers/Yahoo.py
deleted file mode 100644
index b0480b818..000000000
--- a/mailman/Bouncers/Yahoo.py
+++ /dev/null
@@ -1,54 +0,0 @@
-# Copyright (C) 1998-2009 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/>.
-
-"""Yahoo! has its own weird format for bounces."""
-
-import re
-import email
-from email.Utils import parseaddr
-
-tcre = re.compile(r'message\s+from\s+yahoo\.\S+', re.IGNORECASE)
-acre = re.compile(r'<(?P<addr>[^>]*)>:')
-ecre = re.compile(r'--- Original message follows')
-
-
-
-def process(msg):
- # Yahoo! bounces seem to have a known subject value and something called
- # an x-uidl: header, the value of which seems unimportant.
- sender = parseaddr(msg.get('from', '').lower())[1] or ''
- if not sender.startswith('mailer-daemon@yahoo'):
- return None
- addrs = []
- # simple state machine
- # 0 == nothing seen
- # 1 == tag line seen
- state = 0
- for line in email.Iterators.body_line_iterator(msg):
- line = line.strip()
- if state == 0 and tcre.match(line):
- state = 1
- elif state == 1:
- mo = acre.match(line)
- if mo:
- addrs.append(mo.group('addr'))
- continue
- mo = ecre.match(line)
- if mo:
- # we're at the end of the error response
- break
- return addrs
diff --git a/mailman/Bouncers/Yale.py b/mailman/Bouncers/Yale.py
deleted file mode 100644
index 956dfb838..000000000
--- a/mailman/Bouncers/Yale.py
+++ /dev/null
@@ -1,80 +0,0 @@
-# Copyright (C) 2000-2009 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/>.
-
-"""Yale's mail server is pretty dumb.
-
-Its reports include the end user's name, but not the full domain. I think we
-can usually guess it right anyway. This is completely based on examination of
-the corpse, and is subject to failure whenever Yale even slightly changes
-their MTA. :(
-
-"""
-
-import re
-from cStringIO import StringIO
-from email.Utils import getaddresses
-
-scre = re.compile(r'Message not delivered to the following', re.IGNORECASE)
-ecre = re.compile(r'Error Detail', re.IGNORECASE)
-acre = re.compile(r'\s+(?P<addr>\S+)\s+')
-
-
-
-def process(msg):
- if msg.is_multipart():
- return None
- try:
- whofrom = getaddresses([msg.get('from', '')])[0][1]
- if not whofrom:
- return None
- username, domain = whofrom.split('@', 1)
- except (IndexError, ValueError):
- return None
- if username.lower() <> 'mailer-daemon':
- return None
- parts = domain.split('.')
- parts.reverse()
- for part1, part2 in zip(parts, ('edu', 'yale')):
- if part1 <> part2:
- return None
- # Okay, we've established that the bounce came from the mailer-daemon at
- # yale.edu. Let's look for a name, and then guess the relevant domains.
- names = {}
- body = StringIO(msg.get_payload())
- state = 0
- # simple state machine
- # 0 == init
- # 1 == intro found
- while 1:
- line = body.readline()
- if not line:
- break
- if state == 0 and scre.search(line):
- state = 1
- elif state == 1 and ecre.search(line):
- break
- elif state == 1:
- mo = acre.search(line)
- if mo:
- names[mo.group('addr')] = 1
- # Now we have a bunch of names, these are either @yale.edu or
- # @cs.yale.edu. Add them both.
- addrs = []
- for name in names.keys():
- addrs.append(name + '@yale.edu')
- addrs.append(name + '@cs.yale.edu')
- return addrs
diff --git a/mailman/Bouncers/__init__.py b/mailman/Bouncers/__init__.py
deleted file mode 100644
index e69de29bb..000000000
--- a/mailman/Bouncers/__init__.py
+++ /dev/null