summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Mailman/Bouncers/BouncerAPI.py8
-rw-r--r--Mailman/Bouncers/Caiwireless.py7
-rw-r--r--Mailman/Bouncers/Compuserve.py4
-rw-r--r--Mailman/Bouncers/DSN.py111
-rw-r--r--Mailman/Bouncers/Exim.py2
-rw-r--r--Mailman/Bouncers/GroupWise.py8
-rw-r--r--Mailman/Bouncers/LLNL.py8
-rw-r--r--Mailman/Bouncers/Microsoft.py2
-rw-r--r--Mailman/Bouncers/Netscape.py2
-rw-r--r--Mailman/Bouncers/Postfix.py8
-rw-r--r--Mailman/Bouncers/Qmail.py4
-rw-r--r--Mailman/Bouncers/SMTP32.py8
-rw-r--r--Mailman/Bouncers/SimpleMatch.py9
-rw-r--r--Mailman/Bouncers/SimpleWarning.py2
-rw-r--r--Mailman/Bouncers/Yahoo.py4
-rw-r--r--Mailman/Bouncers/Yale.py8
-rw-r--r--Mailman/MailCommandHandler.py14
-rw-r--r--Mailman/MailList.py12
-rw-r--r--Mailman/Mailbox.py4
-rw-r--r--Mailman/Message.py10
-rw-r--r--Mailman/Utils.py9
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