Two changes:

- All constructors grow an optional argument `factory' which is a
  callable used when new message instances are created by the next()
  methods.  Defaults to the rfc822.Message class.

- A new subclass of UnixMailbox is added, called PortableUnixMailbox.
  It's identical to UnixMailbox, but uses a more portable test for
  From_ delimiter lines.  With PortableUnixMailbox, any line that
  starts with "From " is considered a delimiter (this should really
  check for two newlines before the F, but it doesn't.
This commit is contained in:
Barry Warsaw 2001-01-31 22:13:15 +00:00
parent b416290d5d
commit 81ad67cdc6
1 changed files with 43 additions and 12 deletions

View File

@ -9,9 +9,10 @@ import os
__all__ = ["UnixMailbox","MmdfMailbox","MHMailbox","Maildir","BabylMailbox"]
class _Mailbox:
def __init__(self, fp):
def __init__(self, fp, factory=rfc822.Message):
self.fp = fp
self.seekp = 0
self.factory = factory
def seek(self, pos, whence=0):
if whence==1: # Relative to current position
@ -34,7 +35,7 @@ class _Mailbox:
self.seekp = stop = self.fp.tell()
if start != stop:
break
return rfc822.Message(_Subfile(self.fp, start, stop))
return self.factory(_Subfile(self.fp, start, stop))
class _Subfile:
@ -117,22 +118,50 @@ class UnixMailbox(_Mailbox):
self.fp.seek(pos)
return
# An overridable mechanism to test for From-line-ness.
# You can either specify a different regular expression
# or define a whole new _isrealfromline() method.
# Note that this only gets called for lines starting with
# the 5 characters "From ".
# An overridable mechanism to test for From-line-ness. You can either
# specify a different regular expression or define a whole new
# _isrealfromline() method. Note that this only gets called for lines
# starting with the 5 characters "From ".
#
# BAW: According to
#http://home.netscape.com/eng/mozilla/2.0/relnotes/demo/content-length.html
# the only portable, reliable way to find message delimiters in a BSD (i.e
# Unix mailbox) style folder is to search for "\n\nFrom .*\n", or at the
# beginning of the file, "^From .*\n". While _fromlinepattern below seems
# like a good idea, in practice, there are too many variations for more
# strict parsing of the line to be completely accurate.
#
# _strict_isrealfromline() is the old version which tries to do stricter
# parsing of the From_ line. _portable_isrealfromline() simply returns
# true, since it's never called if the line doesn't already start with
# "From ".
#
# This algorithm, and the way it interacts with _search_start() and
# _search_end() may not be completely correct, because it doesn't check
# that the two characters preceding "From " are \n\n or the beginning of
# the file. Fixing this would require a more extensive rewrite than is
# necessary. For convenience, we've added a StrictUnixMailbox class which
# uses the older, more strict _fromlinepattern regular expression.
_fromlinepattern = r"From \s*[^\s]+\s+\w\w\w\s+\w\w\w\s+\d?\d\s+" \
r"\d?\d:\d\d(:\d\d)?(\s+[^\s]+)?\s+\d\d\d\d\s*$"
_regexp = None
def _isrealfromline(self, line):
def _strict_isrealfromline(self, line):
if not self._regexp:
import re
self._regexp = re.compile(self._fromlinepattern)
return self._regexp.match(line)
def _portable_isrealfromline(self, line):
return 1
_isrealfromline = _strict_isrealfromline
class PortableUnixMailbox(UnixMailbox):
_isrealfromline = UnixMailbox._portable_isrealfromline
class MmdfMailbox(_Mailbox):
def _search_start(self):
@ -155,7 +184,7 @@ class MmdfMailbox(_Mailbox):
class MHMailbox:
def __init__(self, dirname):
def __init__(self, dirname, factory=rfc822.Message):
import re
pat = re.compile('^[1-9][0-9]*$')
self.dirname = dirname
@ -168,6 +197,7 @@ class MHMailbox:
# This only works in Python 1.6 or later;
# before that str() added 'L':
self.boxes = map(str, list)
self.factory = factory
def next(self):
if not self.boxes:
@ -175,14 +205,15 @@ class MHMailbox:
fn = self.boxes[0]
del self.boxes[0]
fp = open(os.path.join(self.dirname, fn))
return rfc822.Message(fp)
return self.factory(fp)
class Maildir:
# Qmail directory mailbox
def __init__(self, dirname):
def __init__(self, dirname, factory=rfc822.Message):
self.dirname = dirname
self.factory = factory
# check for new mail
newdir = os.path.join(self.dirname, 'new')
@ -202,7 +233,7 @@ class Maildir:
fn = self.boxes[0]
del self.boxes[0]
fp = open(fn)
return rfc822.Message(fp)
return self.factory(fp)
class BabylMailbox(_Mailbox):