diff options
| -rw-r--r-- | Mailman/Bouncers/Caiwireless.py | 14 | ||||
| -rw-r--r-- | Mailman/Bouncers/Compuserve.py | 9 | ||||
| -rw-r--r-- | Mailman/Bouncers/DSN.py | 111 | ||||
| -rw-r--r-- | Mailman/Bouncers/Exim.py | 10 | ||||
| -rw-r--r-- | Mailman/Bouncers/GroupWise.py | 66 | ||||
| -rw-r--r-- | Mailman/Bouncers/Microsoft.py | 54 | ||||
| -rw-r--r-- | Mailman/Bouncers/Netscape.py | 63 | ||||
| -rw-r--r-- | Mailman/Bouncers/Postfix.py | 67 | ||||
| -rw-r--r-- | Mailman/Bouncers/Qmail.py | 28 | ||||
| -rw-r--r-- | Mailman/Bouncers/SMTP32.py | 9 | ||||
| -rw-r--r-- | Mailman/Bouncers/SimpleMatch.py | 26 | ||||
| -rw-r--r-- | Mailman/Bouncers/Yahoo.py | 20 | ||||
| -rw-r--r-- | Mailman/Bouncers/Yale.py | 4 |
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) |
