summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Mailman/Bouncers/Caiwireless.py14
-rw-r--r--Mailman/Bouncers/Compuserve.py9
-rw-r--r--Mailman/Bouncers/DSN.py111
-rw-r--r--Mailman/Bouncers/Exim.py10
-rw-r--r--Mailman/Bouncers/GroupWise.py66
-rw-r--r--Mailman/Bouncers/Microsoft.py54
-rw-r--r--Mailman/Bouncers/Netscape.py63
-rw-r--r--Mailman/Bouncers/Postfix.py67
-rw-r--r--Mailman/Bouncers/Qmail.py28
-rw-r--r--Mailman/Bouncers/SMTP32.py9
-rw-r--r--Mailman/Bouncers/SimpleMatch.py26
-rw-r--r--Mailman/Bouncers/Yahoo.py20
-rw-r--r--Mailman/Bouncers/Yale.py4
13 files changed, 223 insertions, 258 deletions
diff --git a/Mailman/Bouncers/Caiwireless.py b/Mailman/Bouncers/Caiwireless.py
index bed6a7902..5db2edfee 100644
--- a/Mailman/Bouncers/Caiwireless.py
+++ b/Mailman/Bouncers/Caiwireless.py
@@ -1,4 +1,4 @@
-# Copyright (C) 1998,1999,2000 by the Free Software Foundation, Inc.
+# Copyright (C) 1998,1999,2000,2001 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -16,8 +16,10 @@
"""Parse mystery style generated by MTA at caiwireless.net."""
+from mimelib import MsgReader
import re
-import string
+
+from Mailman.pythonlib.StringIO import StringIO
tcre = re.compile(r'the following recipients did not receive this message:',
re.IGNORECASE)
@@ -28,17 +30,17 @@ acre = re.compile(r'<(?P<addr>[^>]*)>')
def process(msg):
if msg.gettype() <> 'multipart/mixed':
return None
- # this format thinks it's a MIME but it really isn't
- msg.rewindbody()
+ # This format thinks it's a MIME, but it really isn't
+ mi = MsgReader.MsgReader(msg)
# simple state machine
# 0 == nothing seen
# 1 == tag line seen
state = 0
while 1:
- line = msg.fp.readline()
+ line = mi.readline()
if not line:
return None
- line = string.strip(line)
+ line = line.strip()
if state == 0 and tcre.match(line):
state = 1
elif state == 1 and line:
diff --git a/Mailman/Bouncers/Compuserve.py b/Mailman/Bouncers/Compuserve.py
index bbb5e49b8..5ef77f2b3 100644
--- a/Mailman/Bouncers/Compuserve.py
+++ b/Mailman/Bouncers/Compuserve.py
@@ -1,4 +1,4 @@
-# Copyright (C) 1998,1999,2000 by the Free Software Foundation, Inc.
+# Copyright (C) 1998,1999,2000,2001 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,6 +17,7 @@
"""Compuserve has its own weird format for bounces."""
import re
+from mimelib import MsgReader
dcre = re.compile(r'your message could not be delivered', re.IGNORECASE)
acre = re.compile(r'Invalid receiver address: (?P<addr>.*)')
@@ -24,14 +25,14 @@ acre = re.compile(r'Invalid receiver address: (?P<addr>.*)')
def process(msg):
- msg.rewindbody()
+ mi = MsgReader.MsgReader(msg)
# simple state machine
# 0 = nothing seen yet
# 1 = intro line seen
state = 0
addrs = []
while 1:
- line = msg.fp.readline()
+ line = mi.readline()
if not line:
break
if state == 0:
@@ -45,4 +46,4 @@ def process(msg):
mo = acre.search(line)
if mo:
addrs.append(mo.group('addr'))
- return addrs or None
+ return addrs
diff --git a/Mailman/Bouncers/DSN.py b/Mailman/Bouncers/DSN.py
index 4b82f6750..07e5751f7 100644
--- a/Mailman/Bouncers/DSN.py
+++ b/Mailman/Bouncers/DSN.py
@@ -1,4 +1,4 @@
-# Copyright (C) 1998,1999,2000 by the Free Software Foundation, Inc.
+# Copyright (C) 1998,1999,2000,2001 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -16,99 +16,90 @@
"""Parse RFC 1894 (i.e. DSN) bounce formats."""
-import string
-import multifile
-import mimetools
-
+from mimelib import address
from Mailman.pythonlib.StringIO import StringIO
def parseaddr(val):
try:
- atype, addr = string.split(val, ';')
+ atype, addr = val.split(';', 1)
except ValueError:
- # Bogus format for Original-Recipient: or Final-Recipient: fields
+ # Bogus format for Original-Recipient: or Final-Recipient:
return None
- if string.lower(string.strip(atype)) <> 'rfc822':
+ if atype.lower() <> 'rfc822':
+ # Don't know what to do with this address type
return None
- addr = string.strip(addr)
+ addr = addr.strip()
if not addr:
return None
- # strip off <>
- if addr[0] == '<' and addr[-1] == '>':
- addr = addr[1:-1]
- return addr
+ return address.unquote(addr)
-def process(msg):
- if msg.gettype() <> 'multipart/report' or \
- msg.getparam('report-type') <> 'delivery-status':
- # then
+def check(msg):
+ if msg.ismultipart():
+ # Better check the subparts
+ for subpart in msg.get_payload():
+ addrs = check(subpart)
+ if addrs:
+ return addrs
return None
- boundary = msg.getparam('boundary')
- msg.fp.seek(0)
- mfile = multifile.MultiFile(msg.fp)
- mfile.push(boundary)
- # find the subpart with message/delivery-status information
- while 1:
- try:
- more = mfile.next()
- except multifile.Error:
- # the message *looked* like a DSN, but it really wasn't :(
- return None
- if not more:
- # we didn't find it
- return None
- try:
- s = StringIO(mfile.read())
- except multifile.Error:
- # It is a mis-formated or incomplete message
- return None
- msg2 = mimetools.Message(s)
- if msg2.gettype() == 'message/delivery-status':
- # hmm, could there be more than one DSN per message?
- break
- # now parse out the per-recipient fields, which are separated by blank
- # lines. the first block is actually the per-notification fields, but
+ # It's not a multipart/* object, so see if it's got the content-type
+ # specified in the DSN spec.
+ if msg.gettype() <> 'message/delivery-status':
+ # This content-type doesn't help us
+ return None
+ # BAW: could there be more than one DSN per message?
+ #
+ # Now parse out the per-recipient fields, which are separated by blank
+ # lines. The first block is actually the per-notification fields, but
# those can be safely ignored
#
- # we try to dig out the Original-Recipient (which is optional) and
- # Final-Recipient (which is mandatory, but may not exactly match an addr
- # on our list). Some MTA's also use X-Actual-Recipeint as a synonym for
- # Original-Recipeint, but some apparently use that for other purposes :(
+ # 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-Recipeint as a
+ # synonym for Original-Recipeint, but some apparently use that for
+ # other purposes :(
#
# Also grok out Action so we can do something with that too.
+ body = StringIO(msg.get_payload())
blocks = []
headers = {}
while 1:
- line = msg2.fp.readline()
+ line = body.readline()
if not line:
break
- line = string.strip(line)
+ line = line.strip()
if not line:
- # a new recipient block
+ # A new recipient block
blocks.append(headers)
headers = {}
try:
- hdr, val = string.split(line, ':', 1)
+ hdr, val = line.split(':', 1)
except ValueError:
continue
- headers[string.lower(hdr)] = string.strip(val)
- # now go through all the blocks, finding the recip address that is being
- # reported.
+ headers[hdr.lower()] = val.strip()
+ # Now go through all the recipient blocks, looking for addresses that
+ # are reported as bounced. Preference order is Original-Recipient:
+ # Final-Recipient:
addrs = []
for headers in blocks:
- if string.lower(headers.get('action', '')) <> 'failed':
- # ignore this block
+ if headers.get('action', '').lower() <> 'failed':
+ # Some non-permanent failure, so ignore this block
continue
- # preference order is original-recipient, final-recipient
-## val = headers.get('original-recipient',
-## headers.get('x-actual-recipient',
-## headers.get('final-recipient')))
val = headers.get('original-recipient',
headers.get('final-recipient'))
if val:
addrs.append(parseaddr(val))
- return filter(None, addrs) or None
+ return filter(None, addrs)
+
+
+
+def process(msg):
+ # 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 msg.gettype() <> 'multipart/report':
+ return None
+ return check(msg)
diff --git a/Mailman/Bouncers/Exim.py b/Mailman/Bouncers/Exim.py
index 604b52b02..dce093483 100644
--- a/Mailman/Bouncers/Exim.py
+++ b/Mailman/Bouncers/Exim.py
@@ -1,4 +1,4 @@
-# Copyright (C) 1998,1999,2000 by the Free Software Foundation, Inc.
+# Copyright (C) 1998,1999,2000,2001 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -21,9 +21,9 @@ an `addresslist' of failed addresses.
"""
+from mimelib.address import getaddresses
+
+
def process(msg):
- addrs = []
- for fullname, addr in msg.getaddrlist('x-failed-recipients'):
- addrs.append(addr)
- return addrs or None
+ return [a for n, a in getaddresses(msg.getall('x-failed-recipients'))]
diff --git a/Mailman/Bouncers/GroupWise.py b/Mailman/Bouncers/GroupWise.py
index f5ab205c2..76d506c6b 100644
--- a/Mailman/Bouncers/GroupWise.py
+++ b/Mailman/Bouncers/GroupWise.py
@@ -1,4 +1,4 @@
-# Copyright (C) 1998,1999,2000 by the Free Software Foundation, Inc.
+# Copyright (C) 1998,1999,2000,2001 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -20,47 +20,45 @@ X-Mailer: Novell GroupWise Internet Agent 5.5.3.1
X-Mailer: NTMail v4.30.0012
"""
-import string
import re
-import mimetools
-import multifile
-
from Mailman.pythonlib.StringIO import StringIO
acre = re.compile(r'<(?P<addr>[^>]*)>')
+def find_textplain(msg):
+ if msg.gettype() == 'text/plain':
+ return msg
+ if msg.ismultipart:
+ for part in msg.get_payload():
+ ret = find_textplain(part)
+ if ret:
+ return ret
+ return None
+
+
+
def process(msg):
if msg.gettype() <> 'multipart/mixed':
return None
addrs = {}
- boundary = msg.getparam('boundary')
- msg.rewindbody()
- mfile = multifile.MultiFile(msg.fp)
- try:
- mfile.push(boundary)
- while 1:
- if not mfile.next():
- return None
- msg2 = mimetools.Message(StringIO(mfile.read()))
- if msg2.gettype() == 'text/plain':
- # Hmm, could there be more than one part per message?
- break
- msg2.rewindbody()
- while 1:
- line = string.strip(msg2.fp.readline())
- if not line:
- break
- mo = acre.search(line)
- if mo:
- addrs[mo.group('addr')] = 1
- elif '@' in line:
- i = string.find(line, ' ')
- if i < 0:
- addrs[line] = 1
- else:
- addrs[line[:i]] = 1
- except multifile.Error:
- pass
- return addrs.keys() or None
+ # 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:
+ addrs[line] = 1
+ else:
+ addrs[line[:i]] = 1
+ return addrs.keys()
diff --git a/Mailman/Bouncers/Microsoft.py b/Mailman/Bouncers/Microsoft.py
index d8ddb876a..1e2fdb662 100644
--- a/Mailman/Bouncers/Microsoft.py
+++ b/Mailman/Bouncers/Microsoft.py
@@ -1,4 +1,4 @@
-# Copyright (C) 1998,1999,2000 by the Free Software Foundation, Inc.
+# Copyright (C) 1998,1999,2000,2001 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -16,10 +16,7 @@
"""Microsoft's `SMTPSVC' nears I kin tell."""
-import string
import re
-import multifile
-
from Mailman.pythonlib.StringIO import StringIO
scre = re.compile(r'transcript of session follows', re.IGNORECASE)
@@ -29,36 +26,23 @@ scre = re.compile(r'transcript of session follows', re.IGNORECASE)
def process(msg):
if msg.gettype() <> 'multipart/mixed':
return None
- boundary = msg.getparam('boundary')
- msg.fp.seek(0)
- addrs = []
+ # Find the first subpart, which has no MIME type
try:
- mfile = multifile.MultiFile(msg.fp)
- mfile.push(boundary)
- # find the first subpart, which has no mime type
- try:
- more = mfile.next()
- except multifile.Error:
- # the message *looked* like a DSN, but it really wasn't :(
- return None
- if not more:
- # we didn't find it
- return None
- # simple state machine
- # 0 == nothng seen yet
- # 1 == tag line seen
- state = 0
- while 1:
- line = mfile.readline()
- if not line:
- break
- line = string.strip(line)
- if state == 0:
- if scre.search(line):
- state = 1
- if state == 1:
- if '@' in line:
- addrs.append(line)
- except multifile.Error:
- pass
+ subpart = msg.get_payload(0)
+ except IndexError:
+ # The message *looked* like a multipart but wasn't
+ return None
+ body = StringIO(subpart.get_payload())
+ 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
index 26fd89e7a..038357076 100644
--- a/Mailman/Bouncers/Netscape.py
+++ b/Mailman/Bouncers/Netscape.py
@@ -1,4 +1,4 @@
-# Copyright (C) 1998,1999,2000 by the Free Software Foundation, Inc.
+# Copyright (C) 1998,1999,2000,2001 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,7 +17,7 @@
"""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
+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
@@ -25,11 +25,7 @@ decipher the format here too.
"""
-import string
import re
-import multifile
-import mimetools
-
from Mailman.pythonlib.StringIO import StringIO
pcre = re.compile(
@@ -42,49 +38,40 @@ acre = re.compile(
+def flatten(msg, leaves):
+ # give us all the leaf (non-multipart) subparts
+ if msg.ismultipart():
+ 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;
- # TBD: should we tighten this check?
- if msg.getmaintype() <> 'multipart':
+ if not msg.ismultipart():
return None
- boundary = msg.getparam('boundary')
- msg.fp.seek(0)
- mfile = multifile.MultiFile(msg.fp)
- mfile.push(boundary)
+ # We're looking for a text/plain subpart occuring before a
+ # message/delivery-status subpart.
plainmsg = None
- # find the text/plain subpart which must occur before a
- # message/delivery-status part
- while 1:
- try:
- more = mfile.next()
- except multifile.Error:
- # Not properly formatted MIME
- return None
- if not more:
- # we didn't find it
- return None
- try:
- s = StringIO(mfile.read())
- except multifile.Error:
- # Not properly formatted MIME
- return None
- msg = mimetools.Message(s)
- if msg.getmaintype() == 'message':
+ leaves = []
+ flatten(msg, leaves)
+ for i, subpart in zip(range(len(leaves)-1), leaves):
+ if subpart.gettype() == 'text/plain' and \
+ leaves[i+1].getmaintype() == 'message':
+ plainmsg = subpart
break
- elif msg.gettype() <> 'text/plain':
- # we're looking at something else entirely
- return None
- plainmsg = msg
- # Did we find a text/plain part?
if not plainmsg:
return None
# Total guesswork, based on captured examples...
+ body = StringIO(plainmsg.get_payload())
addrs = []
while 1:
- line = plainmsg.fp.readline()
+ line = body.readline()
if not line:
break
mo = pcre.search(line)
@@ -93,10 +80,10 @@ def process(msg):
# format inside here is. :( We'll just search for <addr>
# strings.
while 1:
- line = plainmsg.fp.readline()
+ 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 or None
+ return addrs
diff --git a/Mailman/Bouncers/Postfix.py b/Mailman/Bouncers/Postfix.py
index 61993d76a..1a261ab7e 100644
--- a/Mailman/Bouncers/Postfix.py
+++ b/Mailman/Bouncers/Postfix.py
@@ -1,4 +1,4 @@
-# Copyright (C) 1998,1999,2000 by the Free Software Foundation, Inc.
+# Copyright (C) 1998,1999,2000,2001 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,44 +17,18 @@
"""Parse bounce messages generated by Postfix."""
-import string
import re
-import multifile
-import mimetools
-
from Mailman.pythonlib.StringIO import StringIO
-def process(msg):
- if msg.gettype() <> 'multipart/mixed':
- return None
- boundary = msg.getparam('boundary')
- msg.fp.seek(0)
- mfile = multifile.MultiFile(msg.fp)
- mfile.push(boundary)
- # find the subpart with message/delivery-status information
- while 1:
- try:
- more = mfile.next()
- except multifile.Error:
- # looked like a multipart, but really wasn't
- return None
- if not more:
- # we didn't find it
- return None
- try:
- s = StringIO(mfile.read())
- except multifile.Error:
- # It is a mis-formated or incomplete message
- return None
- msg2 = mimetools.Message(s)
- if msg2.gettype() == 'text/plain':
- desc = msg2.get('content-description')
- if desc and string.lower(desc) == 'notification':
- return findaddr(msg2.fp)
- # probably not a Postfix bounce
- return None
+def flatten(msg, leaves):
+ # give us all the leaf (non-multipart) subparts
+ if msg.ismultipart():
+ for part in msg.get_payload():
+ flatten(part, leaves)
+ else:
+ leaves.append(msg)
@@ -63,18 +37,19 @@ pcre = re.compile(r'\t\t\tthe postfix program$', re.IGNORECASE)
rcre = re.compile(r'failure reason:$', re.IGNORECASE)
acre = re.compile(r'<(?P<addr>[^>]*)>:')
-def findaddr(fp):
+def findaddr(msg):
addrs = []
+ body = StringIO(msg.get_payload())
# simple state machine
# 0 == nothing found
# 1 == salutation found
state = 0
while 1:
- line = fp.readline()
+ line = body.readline()
if not line:
break
# preserve leading whitespace
- line = string.rstrip(line)
+ 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
@@ -83,4 +58,20 @@ def findaddr(fp):
if mo:
addrs.append(mo.group('addr'))
# probably a continuation line
- return addrs or None
+ return addrs
+
+
+
+def process(msg):
+ if msg.gettype() <> 'multipart/mixed':
+ 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.gettype() == '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
index 77b439f41..5438042e5 100644
--- a/Mailman/Bouncers/Qmail.py
+++ b/Mailman/Bouncers/Qmail.py
@@ -1,4 +1,4 @@
-# Copyright (C) 1998,1999,2000 by the Free Software Foundation, Inc.
+# Copyright (C) 1998,1999,2000,2001 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -25,43 +25,41 @@ This module should be conformant.
"""
-import string
import re
+from mimelib import MsgReader
introtag = 'Hi. This is the'
-
acre = re.compile(r'<(?P<addr>[^>]*)>:')
def process(msg):
- msg.rewindbody()
+ mi = MsgReader.MsgReader(msg)
+ addrs = []
# simple state machine
# 0 = nothing seen yet
# 1 = intro paragraph seen
# 2 = recip paragraphs seen
state = 0
- addrs = []
while 1:
- line = msg.fp.readline()
+ line = mi.readline()
if not line:
break
- line = string.strip(line)
- if state == 0 and line[:len(introtag)] == introtag:
+ line = line.strip()
+ if state == 0 and line.startswith(introtag):
state = 1
elif state == 1 and not line:
+ # Looking for the end of the intro paragraph
state = 2
elif state == 2:
- if line and line[0] == '-':
- # we're looking at the break paragraph, so we're done
+ 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
- #
- # we've parse everything we need to
- return addrs or None
+ # 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
index ea9693ba7..9e7732a63 100644
--- a/Mailman/Bouncers/SMTP32.py
+++ b/Mailman/Bouncers/SMTP32.py
@@ -1,4 +1,4 @@
-# Copyright (C) 1998,1999,2000 by the Free Software Foundation, Inc.
+# Copyright (C) 1998,1999,2000,2001 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -21,6 +21,7 @@ What the heck is this thing?
"""
import re
+from mimelib import MsgReader
ecre = re.compile('original message follows', re.IGNORECASE)
acre = re.compile(r'user mailbox[^:]*:\s*(?P<addr>.*)', re.IGNORECASE)
@@ -28,10 +29,10 @@ acre = re.compile(r'user mailbox[^:]*:\s*(?P<addr>.*)', re.IGNORECASE)
def process(msg):
- msg.rewindbody()
+ mi = MsgReader.MsgReader(msg)
addrs = {}
while 1:
- line = msg.fp.readline()
+ line = mi.readline()
if not line:
break
if ecre.search(line):
@@ -39,4 +40,4 @@ def process(msg):
mo = acre.search(line)
if mo:
addrs[mo.group('addr')] = 1
- return addrs.keys() or None
+ return addrs.keys()
diff --git a/Mailman/Bouncers/SimpleMatch.py b/Mailman/Bouncers/SimpleMatch.py
index 675017df4..c4d7eafdb 100644
--- a/Mailman/Bouncers/SimpleMatch.py
+++ b/Mailman/Bouncers/SimpleMatch.py
@@ -1,4 +1,4 @@
-# Copyright (C) 1998,1999,2000 by the Free Software Foundation, Inc.
+# Copyright (C) 1998,1999,2000,2001 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -17,7 +17,10 @@
"""Recognizes simple heuristically delimited bounces."""
import re
+from mimelib import MsgReader
+
+
def _c(pattern):
return re.compile(pattern, re.IGNORECASE)
@@ -26,10 +29,10 @@ patterns = [
(_c('here is your list of failed recipients'),
_c('here is your returned mail'),
_c(r'<(?P<addr>[^>]*)>')),
- # sz-sb.de, corridor.com
+ # sz-sb.de, corridor.com, nfg.nl
(_c('the following addresses had'),
_c('transcript of session follows'),
- _c(r'<(?P<addr>[^>]*)>')),
+ _c(r'<(?P<addr>[^>]*)>|\(expanded from: (?P<addr>[^)]*)\)')),
# robanal.demon.co.uk
(_c('this message was created automatically by mail delivery software'),
_c('original message follows'),
@@ -38,22 +41,31 @@ patterns = [
(_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('<(?P<addr>[^>]*)>')),
+ # 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>[^>]*)>')),
+ # Next one goes here...
]
-
def process(msg):
- msg.rewindbody()
+ mi = MsgReader.MsgReader(msg)
# simple state machine
# 0 = nothing seen yet
# 1 = intro seen
addrs = {}
state = 0
while 1:
- line = msg.fp.readline()
+ line = mi.readline()
if not line:
break
+
if state == 0:
for scre, ecre, acre in patterns:
if scre.search(line):
@@ -65,4 +77,4 @@ def process(msg):
addrs[mo.group('addr')] = 1
elif ecre.search(line):
break
- return addrs.keys() or None
+ return addrs.keys()
diff --git a/Mailman/Bouncers/Yahoo.py b/Mailman/Bouncers/Yahoo.py
index 6168a7551..8ffa52d4a 100644
--- a/Mailman/Bouncers/Yahoo.py
+++ b/Mailman/Bouncers/Yahoo.py
@@ -1,4 +1,4 @@
-# Copyright (C) 1998,1999,2000 by the Free Software Foundation, Inc.
+# Copyright (C) 1998,1999,2000,2001 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -14,10 +14,10 @@
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-"""Yahoo has its own weird format for bounces."""
+"""Yahoo! has its own weird format for bounces."""
-import string
import re
+from mimelib import MsgReader
tcre = re.compile(r'message\s+from\s+yahoo.com', re.IGNORECASE)
acre = re.compile(r'<(?P<addr>[^>]*)>:')
@@ -26,21 +26,21 @@ 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
- if string.lower(msg.get('from', '')) <> 'mailer-daemon@yahoo.com':
+ # Yahoo! bounces seem to have a known subject value and something called
+ # an x-uidl: header, the value of which seems unimportant.
+ if msg.get('from', '').lower() <> 'mailer-daemon@yahoo.com':
return None
- msg.rewindbody()
+ mi = MsgReader.MsgReader(msg)
addrs = []
# simple state machine
# 0 == nothing seen
# 1 == tag line seen
state = 0
while 1:
- line = msg.fp.readline()
+ line = mi.readline()
if not line:
break
- line = string.strip(line)
+ line = line.strip()
if state == 0 and tcre.match(line):
state = 1
elif state == 1:
@@ -52,4 +52,4 @@ def process(msg):
if mo:
# we're at the end of the error response
break
- return addrs or None
+ return addrs
diff --git a/Mailman/Bouncers/Yale.py b/Mailman/Bouncers/Yale.py
index d87ce3a01..7314729a2 100644
--- a/Mailman/Bouncers/Yale.py
+++ b/Mailman/Bouncers/Yale.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2000 by the Free Software Foundation, Inc.
+# Copyright (C) 2000,2001 by the Free Software Foundation, Inc.
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
@@ -22,7 +22,7 @@ and is subject to failure whenever Yale even slightly changes their MTA. :(
"""
import re
-from mimeo import address
+from mimelib import address
from Mailman.pythonlib.StringIO import StringIO
scre = re.compile(r'Message not delivered to the following', re.IGNORECASE)