diff options
| -rw-r--r-- | Mailman/Bouncers/BouncerAPI.py | 8 | ||||
| -rw-r--r-- | Mailman/Bouncers/Caiwireless.py | 7 | ||||
| -rw-r--r-- | Mailman/Bouncers/Compuserve.py | 4 | ||||
| -rw-r--r-- | Mailman/Bouncers/DSN.py | 111 | ||||
| -rw-r--r-- | Mailman/Bouncers/Exim.py | 2 | ||||
| -rw-r--r-- | Mailman/Bouncers/GroupWise.py | 8 | ||||
| -rw-r--r-- | Mailman/Bouncers/LLNL.py | 8 | ||||
| -rw-r--r-- | Mailman/Bouncers/Microsoft.py | 2 | ||||
| -rw-r--r-- | Mailman/Bouncers/Netscape.py | 2 | ||||
| -rw-r--r-- | Mailman/Bouncers/Postfix.py | 8 | ||||
| -rw-r--r-- | Mailman/Bouncers/Qmail.py | 4 | ||||
| -rw-r--r-- | Mailman/Bouncers/SMTP32.py | 8 | ||||
| -rw-r--r-- | Mailman/Bouncers/SimpleMatch.py | 9 | ||||
| -rw-r--r-- | Mailman/Bouncers/SimpleWarning.py | 2 | ||||
| -rw-r--r-- | Mailman/Bouncers/Yahoo.py | 4 | ||||
| -rw-r--r-- | Mailman/Bouncers/Yale.py | 8 | ||||
| -rw-r--r-- | Mailman/MailCommandHandler.py | 14 | ||||
| -rw-r--r-- | Mailman/MailList.py | 12 | ||||
| -rw-r--r-- | Mailman/Mailbox.py | 4 | ||||
| -rw-r--r-- | Mailman/Message.py | 10 | ||||
| -rw-r--r-- | Mailman/Utils.py | 9 |
21 files changed, 83 insertions, 161 deletions
diff --git a/Mailman/Bouncers/BouncerAPI.py b/Mailman/Bouncers/BouncerAPI.py index 7bb3b406c..6b5fafc00 100644 --- a/Mailman/Bouncers/BouncerAPI.py +++ b/Mailman/Bouncers/BouncerAPI.py @@ -25,13 +25,13 @@ the filename containing the bounce message. import sys import traceback from types import ListType +from cStringIO import StringIO # testing kludge if __name__ == '__main__': execfile('bin/paths.py') from Mailman.Logging.Syslog import syslog -from Mailman.pythonlib.StringIO import StringIO class _Stop: pass @@ -91,7 +91,7 @@ def ScanMessages(mlist, msg, testing=0): if __name__ == '__main__': from Mailman import Message from Mailman.i18n import _ - from mimelib import Parser + import email def usage(code, msg=''): print >> sys.stderr, _(__doc__) @@ -102,11 +102,9 @@ if __name__ == '__main__': if len(sys.argv) < 2: usage(1, 'required arguments: <file> [, <file> ...]') - p = Parser.Parser(_class=Message.Message) - for filename in sys.argv[1:]: print 'scanning file', filename fp = open(filename) - msg = p.parse(fp) + msg = email.message_from_file(fp, Message.Message) fp.close() ScanMessages(None, msg, testing=1) diff --git a/Mailman/Bouncers/Caiwireless.py b/Mailman/Bouncers/Caiwireless.py index 5db2edfee..bc5ad070c 100644 --- a/Mailman/Bouncers/Caiwireless.py +++ b/Mailman/Bouncers/Caiwireless.py @@ -16,10 +16,9 @@ """Parse mystery style generated by MTA at caiwireless.net.""" -from mimelib import MsgReader import re - -from Mailman.pythonlib.StringIO import StringIO +import email +from cStringIO import StringIO tcre = re.compile(r'the following recipients did not receive this message:', re.IGNORECASE) @@ -31,7 +30,7 @@ def process(msg): if msg.gettype() <> 'multipart/mixed': return None # This format thinks it's a MIME, but it really isn't - mi = MsgReader.MsgReader(msg) + mi = email.Iterators.body_line_iterator(msg) # simple state machine # 0 == nothing seen # 1 == tag line seen diff --git a/Mailman/Bouncers/Compuserve.py b/Mailman/Bouncers/Compuserve.py index 5ef77f2b3..b2f074725 100644 --- a/Mailman/Bouncers/Compuserve.py +++ b/Mailman/Bouncers/Compuserve.py @@ -17,7 +17,7 @@ """Compuserve has its own weird format for bounces.""" import re -from mimelib import MsgReader +import email dcre = re.compile(r'your message could not be delivered', re.IGNORECASE) acre = re.compile(r'Invalid receiver address: (?P<addr>.*)') @@ -25,7 +25,7 @@ acre = re.compile(r'Invalid receiver address: (?P<addr>.*)') def process(msg): - mi = MsgReader.MsgReader(msg) + mi = email.Iterators.body_line_iterator(msg) # simple state machine # 0 = nothing seen yet # 1 = intro line seen diff --git a/Mailman/Bouncers/DSN.py b/Mailman/Bouncers/DSN.py index 1ae0dbd1a..186a94ae3 100644 --- a/Mailman/Bouncers/DSN.py +++ b/Mailman/Bouncers/DSN.py @@ -16,87 +16,46 @@ """Parse RFC 1894 (i.e. DSN) bounce formats.""" -from mimelib import address -from Mailman.pythonlib.StringIO import StringIO - - - -def parseaddr(val): - try: - atype, addr = val.split(';', 1) - except ValueError: - # Bogus format for Original-Recipient: or Final-Recipient: - return None - if atype.lower() <> 'rfc822': - # Don't know what to do with this address type - return None - addr = addr.strip() - if not addr: - return None - return address.unquote(addr) +from email.Iterators import typed_subpart_iterator +from cStringIO import StringIO def check(msg): - if msg.ismultipart(): - # Recursively check the subparts - for subpart in msg.get_payload(): - addrs = check(subpart) - if addrs: - return addrs - return None - # 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 - # 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 = body.readline() - if not line: - break - line = line.strip() - if not line: - # A new recipient block - blocks.append(headers) - headers = {} - try: - hdr, val = line.split(':', 1) - except ValueError: - continue - headers[hdr.lower()] = val.strip() - # Make sure the last one is appended - blocks.append(headers) - # Now go through all the recipient blocks, looking for addresses that - # are reported as bounced. Preference order is Original-Recipient: - # Final-Recipient: + # Iterate over each message/delivery-status subpart addrs = [] - for headers in blocks: - # Should we treat delayed bounces the same? Yes, because if the - # transient problem clears up, they should get unbounced. - if headers.get('action', '').lower() not in ('failed', 'failure', - 'delayed'): - # Some non-permanent failure, so ignore this block + for part in typed_subpart_iterator(msg, 'message', 'delivery-status'): + if not part.is_multipart(): + # Huh? continue - val = headers.get('original-recipient', - headers.get('final-recipient')) - if val: - addrs.append(parseaddr(val)) + # 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', '') + # Should we treat delayed bounces the same? Yes, because if the + # transient problem clears up, they should get unbounced. + if action.lower() not in ('failed', 'failure', 'delayed'): + # Some non-permanent failure, so ignore this block + continue + params = [] + foundp = 0 + for header in ('original-recipient', 'final-recipient'): + for k, v in msgblock.get_params([], header): + if k.lower() == 'rfc822': + foundp = 1 + else: + params.append(k) + if foundp: + # Note that params should already be unquoted. + addrs.extend(params) + break return filter(None, addrs) @@ -105,6 +64,6 @@ 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 not msg.ismultipart() or msg.getsubtype() <> 'report': + if not msg.is_multipart() or msg.get_subtype() <> 'report': return None return check(msg) diff --git a/Mailman/Bouncers/Exim.py b/Mailman/Bouncers/Exim.py index dce093483..29fed39fb 100644 --- a/Mailman/Bouncers/Exim.py +++ b/Mailman/Bouncers/Exim.py @@ -21,7 +21,7 @@ an `addresslist' of failed addresses. """ -from mimelib.address import getaddresses +from email.Utils import getaddresses diff --git a/Mailman/Bouncers/GroupWise.py b/Mailman/Bouncers/GroupWise.py index 564b58792..b2f1396fe 100644 --- a/Mailman/Bouncers/GroupWise.py +++ b/Mailman/Bouncers/GroupWise.py @@ -22,16 +22,16 @@ X-Mailer: Internet Mail Service (5.5.2653.19) """ import re -from Mailman.pythonlib.StringIO import StringIO +from cStringIO import StringIO acre = re.compile(r'<(?P<addr>[^>]*)>') def find_textplain(msg): - if msg.gettype() == 'text/plain': + if msg.get_type() == 'text/plain': return msg - if msg.ismultipart: + if msg.is_multipart: for part in msg.get_payload(): ret = find_textplain(part) if ret: @@ -41,7 +41,7 @@ def find_textplain(msg): def process(msg): - if msg.gettype() <> 'multipart/mixed' or not msg['x-mailer']: + if msg.get_type() <> 'multipart/mixed' or not msg['x-mailer']: return None addrs = {} # find the first text/plain part in the message diff --git a/Mailman/Bouncers/LLNL.py b/Mailman/Bouncers/LLNL.py index 51b4205c4..4c5adc6db 100644 --- a/Mailman/Bouncers/LLNL.py +++ b/Mailman/Bouncers/LLNL.py @@ -17,18 +17,14 @@ """LLNL's custom Sendmail bounce message.""" import re -from mimelib.MsgReader import MsgReader +import email acre = re.compile(r',\s*(?P<addr>\S+@[^,]+),', re.IGNORECASE) def process(msg): - mi = MsgReader(msg) - while 1: - line = mi.readline() - if not line: - break + for line in email.Iterators.body_line_iterator(msg): mo = acre.search(line) if mo: return [mo.group('addr')] diff --git a/Mailman/Bouncers/Microsoft.py b/Mailman/Bouncers/Microsoft.py index 1e2fdb662..b34f0a2cd 100644 --- a/Mailman/Bouncers/Microsoft.py +++ b/Mailman/Bouncers/Microsoft.py @@ -17,7 +17,7 @@ """Microsoft's `SMTPSVC' nears I kin tell.""" import re -from Mailman.pythonlib.StringIO import StringIO +from cStringIO import StringIO scre = re.compile(r'transcript of session follows', re.IGNORECASE) diff --git a/Mailman/Bouncers/Netscape.py b/Mailman/Bouncers/Netscape.py index 038357076..611f8ac78 100644 --- a/Mailman/Bouncers/Netscape.py +++ b/Mailman/Bouncers/Netscape.py @@ -26,7 +26,7 @@ decipher the format here too. """ import re -from Mailman.pythonlib.StringIO import StringIO +from cStringIO import StringIO pcre = re.compile( r'This Message was undeliverable due to the following reason:', diff --git a/Mailman/Bouncers/Postfix.py b/Mailman/Bouncers/Postfix.py index 86fb7be91..f55147a0a 100644 --- a/Mailman/Bouncers/Postfix.py +++ b/Mailman/Bouncers/Postfix.py @@ -27,13 +27,13 @@ It also matches something claiming to be `The BNS Postfix program'. import re -from Mailman.pythonlib.StringIO import StringIO +from cStringIO import StringIO def flatten(msg, leaves): # give us all the leaf (non-multipart) subparts - if msg.ismultipart(): + if msg.is_multipart(): for part in msg.get_payload(): flatten(part, leaves) else: @@ -72,14 +72,14 @@ def findaddr(msg): def process(msg): - if msg.gettype() <> 'multipart/mixed': + if msg.get_type() <> '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 \ + if subpart.get_type() == 'text/plain' and \ subpart.get('content-description', '').lower() == 'notification': # then... return findaddr(subpart) diff --git a/Mailman/Bouncers/Qmail.py b/Mailman/Bouncers/Qmail.py index 5438042e5..e1e7ac2ea 100644 --- a/Mailman/Bouncers/Qmail.py +++ b/Mailman/Bouncers/Qmail.py @@ -26,7 +26,7 @@ This module should be conformant. """ import re -from mimelib import MsgReader +import email introtag = 'Hi. This is the' acre = re.compile(r'<(?P<addr>[^>]*)>:') @@ -34,7 +34,7 @@ acre = re.compile(r'<(?P<addr>[^>]*)>:') def process(msg): - mi = MsgReader.MsgReader(msg) + mi = email.Iterators.body_line_iterator(msg) addrs = [] # simple state machine # 0 = nothing seen yet diff --git a/Mailman/Bouncers/SMTP32.py b/Mailman/Bouncers/SMTP32.py index cf1db6d68..5ad880a98 100644 --- a/Mailman/Bouncers/SMTP32.py +++ b/Mailman/Bouncers/SMTP32.py @@ -28,7 +28,7 @@ Escape character is '^]'. """ import re -from mimelib.MsgReader import MsgReader +import email ecre = re.compile('original message follows', re.IGNORECASE) acre = re.compile(r''' @@ -47,12 +47,8 @@ def process(msg): mailer = msg.get('x-mailer', '') if not mailer.startswith('<SMTP32 v'): return - mi = MsgReader(msg) addrs = {} - while 1: - line = mi.readline() - if not line: - break + for line in email.Iterators.body_line_iterator(msg): if ecre.search(line): break mo = acre.search(line) diff --git a/Mailman/Bouncers/SimpleMatch.py b/Mailman/Bouncers/SimpleMatch.py index c0c2bbc3b..3daa4925f 100644 --- a/Mailman/Bouncers/SimpleMatch.py +++ b/Mailman/Bouncers/SimpleMatch.py @@ -17,7 +17,7 @@ """Recognizes simple heuristically delimited bounces.""" import re -from mimelib import MsgReader +import email.Iterators @@ -72,17 +72,12 @@ patterns = [ def process(msg, patterns=patterns): - mi = MsgReader.MsgReader(msg) # simple state machine # 0 = nothing seen yet # 1 = intro seen addrs = {} state = 0 - while 1: - line = mi.readline() - #print '(%d) line: %s' % (state, line[:-1]) - if not line: - break + for line in email.Iterators.body_line_iterator(msg): if state == 0: for scre, ecre, acre in patterns: if scre.search(line): diff --git a/Mailman/Bouncers/SimpleWarning.py b/Mailman/Bouncers/SimpleWarning.py index 876c85636..108965706 100644 --- a/Mailman/Bouncers/SimpleWarning.py +++ b/Mailman/Bouncers/SimpleWarning.py @@ -16,8 +16,6 @@ """Recognizes simple heuristically delimited warnings.""" -import re -from mimelib import MsgReader from Mailman.Bouncers.SimpleMatch import _c from Mailman.Bouncers.SimpleMatch import process as _process diff --git a/Mailman/Bouncers/Yahoo.py b/Mailman/Bouncers/Yahoo.py index 8ffa52d4a..aed1ea668 100644 --- a/Mailman/Bouncers/Yahoo.py +++ b/Mailman/Bouncers/Yahoo.py @@ -17,7 +17,7 @@ """Yahoo! has its own weird format for bounces.""" import re -from mimelib import MsgReader +import email tcre = re.compile(r'message\s+from\s+yahoo.com', re.IGNORECASE) acre = re.compile(r'<(?P<addr>[^>]*)>:') @@ -30,7 +30,7 @@ def process(msg): # an x-uidl: header, the value of which seems unimportant. if msg.get('from', '').lower() <> 'mailer-daemon@yahoo.com': return None - mi = MsgReader.MsgReader(msg) + mi = email.Iterators.body_line_iterator(msg) addrs = [] # simple state machine # 0 == nothing seen diff --git a/Mailman/Bouncers/Yale.py b/Mailman/Bouncers/Yale.py index 41c4a8e08..23e30182f 100644 --- a/Mailman/Bouncers/Yale.py +++ b/Mailman/Bouncers/Yale.py @@ -24,8 +24,8 @@ their MTA. :( """ import re -from mimelib import address -from Mailman.pythonlib.StringIO import StringIO +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) @@ -34,10 +34,10 @@ acre = re.compile(r'\s+(?P<addr>\S+)\s+') def process(msg): - if msg.ismultipart(): + if msg.is_multipart(): return None try: - whofrom = address.getaddresses([msg.get('from', '')])[0][1] + whofrom = getaddresses([msg.get('from', '')])[0][1] if not whofrom: return None username, domain = whofrom.split('@', 1) diff --git a/Mailman/MailCommandHandler.py b/Mailman/MailCommandHandler.py index 9bcef5f3c..9c09a7ff1 100644 --- a/Mailman/MailCommandHandler.py +++ b/Mailman/MailCommandHandler.py @@ -24,8 +24,8 @@ import os import sys import re import traceback - -from mimelib.MsgReader import MsgReader +import email.Iterators +from cStringIO import StringIO from Mailman import mm_cfg from Mailman import Utils @@ -34,7 +34,6 @@ from Mailman import Message from Mailman import Pending from Mailman.UserDesc import UserDesc from Mailman.Logging.Syslog import syslog -from Mailman.pythonlib.StringIO import StringIO import Mailman.i18n @@ -182,14 +181,7 @@ Subject: %s''', self.internal_name(), msg['from'], subject) if mo: subject = mo.group('cmd') - reader = MsgReader(msg) - # BAW: here's where Python 2.1's xreadlines module would help! - lines = [] - while 1: - line = reader.readline() - if not line: - break - lines.append(line) + lines = email.Iterators.body_line_iterator(msg) # Find out if the subject line has a command on it subjcmd = [] diff --git a/Mailman/MailList.py b/Mailman/MailList.py index dbd2392e5..d9b0bdcec 100644 --- a/Mailman/MailList.py +++ b/Mailman/MailList.py @@ -28,15 +28,12 @@ import re import shutil import socket import urllib +from cStringIO import StringIO from UserDict import UserDict from urlparse import urlparse from types import * -from mimelib.address import getaddresses, dump_address_pair -# We use this explicitly instead of going through mimelib so that we can pick -# up the RFC 2822-conformant version of rfc822.py that will be included in -# Python 2.2. -from Mailman.pythonlib.rfc822 import parseaddr +from email.Utils import getaddresses, dump_address_pair, parseaddr from Mailman import mm_cfg from Mailman import Utils @@ -66,7 +63,6 @@ from Mailman import Message from Mailman import Pending from Mailman.i18n import _ from Mailman.Logging.Syslog import syslog -from Mailman.pythonlib.StringIO import StringIO EMPTYSTRING = '' @@ -917,7 +913,7 @@ class MailList(MailCommandHandler, HTMLFormatter, Deliverer, ListAdmin, # specifically To: Cc: and Resent-to: to = [] for header in ('to', 'cc', 'resent-to', 'resent-cc'): - to.extend(getaddresses(msg.getall(header))) + to.extend(getaddresses(msg.get_all(header))) for fullname, addr in to: # It's possible that if the header doesn't have a valid # (i.e. RFC822) value, we'll get None for the address. So skip @@ -1005,7 +1001,7 @@ bad regexp in bounce_matching_header line: %s matches. """ for header, cre, line in self.parse_matching_header_opt(): - for value in msg.getall(header): + for value in msg.get_all(header): if cre.search(value): return line return 0 diff --git a/Mailman/Mailbox.py b/Mailman/Mailbox.py index a0768673c..df8670fb9 100644 --- a/Mailman/Mailbox.py +++ b/Mailman/Mailbox.py @@ -17,7 +17,7 @@ "Extend mailbox.UnixMailbox." -from mimelib.Generator import Generator +from email.Generator import Generator from Mailman.pythonlib import mailbox @@ -40,4 +40,4 @@ class Mailbox(mailbox.PortableUnixMailbox): self.fp.seek(1, 2) # Create a Generator instance to write the message to the file g = Generator(self.fp) - g.write(msg) + g(msg, unixfrom=1) diff --git a/Mailman/Message.py b/Mailman/Message.py index d27304c2c..550e9dc1e 100644 --- a/Mailman/Message.py +++ b/Mailman/Message.py @@ -20,12 +20,10 @@ This is a subclass of mimeo.Message but provides a slightly extended interface which is more convenient for use inside Mailman. """ +import email.Message +import email.Utils from types import ListType -import mimelib.Message -from mimelib.StringableMixin import StringableMixin -from mimelib.address import getaddresses - from Mailman import mm_cfg from Mailman import Utils @@ -33,7 +31,7 @@ COMMASPACE = ', ' -class Message(mimelib.Message.Message, StringableMixin): +class Message(email.Message.Message): def get_sender(self, use_envelope=None, preserve_case=0): """Return the address considered to be the author of the email. @@ -67,7 +65,7 @@ class Message(mimelib.Message.Message, StringableMixin): fieldval = self[h] if not fieldval: continue - addrs = getaddresses([fieldval]) + addrs = email.Utils.getaddresses([fieldval]) try: realname, address = addrs[0] except IndexError: diff --git a/Mailman/Utils.py b/Mailman/Utils.py index 36913c926..4254870c4 100644 --- a/Mailman/Utils.py +++ b/Mailman/Utils.py @@ -31,10 +31,9 @@ import urlparse import sha import errno import time +import email.Iterators from string import whitespace as WHITESPACE -from mimelib.MsgReader import MsgReader - from Mailman import mm_cfg from Mailman import Errors from Mailman.SafeDict import SafeDict @@ -501,13 +500,9 @@ ADMINDATA = { # unsubscribe, etc). The test must be a good guess -- messages that return # true get sent to the list admin instead of the entire list. def is_administrivia(msg): - reader = MsgReader(msg) linecnt = 0 lines = [] - while 1: - line = reader.readline() - if not line: - break + for line in email.Iterators.body_line_iterator(msg): # Strip out any signatures if line == '-- ': break |
