3
"""Classes to handle Unix style, MMDF style, and MH style mailboxes."""
9
__all__ = ["UnixMailbox","MmdfMailbox","MHMailbox","Maildir","BabylMailbox"]
12
def __init__(self, fp, factory=rfc822.Message):
15
self.factory = factory
19
self.fp.seek(self.seekp)
23
self.seekp = self.fp.tell()
25
start = self.fp.tell()
27
self.seekp = stop = self.fp.tell()
30
return self.factory(_Subfile(self.fp, start, stop))
34
def __init__(self, fp, start, stop):
40
def read(self, length = None):
41
if self.pos >= self.stop:
43
remaining = self.stop - self.pos
44
if length is None or length < 0:
46
elif length > remaining:
48
self.fp.seek(self.pos)
49
data = self.fp.read(length)
50
self.pos = self.fp.tell()
53
def readline(self, length = None):
54
if self.pos >= self.stop:
57
length = self.stop - self.pos
58
self.fp.seek(self.pos)
59
data = self.fp.readline(length)
60
self.pos = self.fp.tell()
63
def readlines(self, sizehint = -1):
66
line = self.readline()
71
sizehint = sizehint - len(line)
77
return self.pos - self.start
79
def seek(self, pos, whence=0):
81
self.pos = self.start + pos
83
self.pos = self.pos + pos
85
self.pos = self.stop + pos
91
class UnixMailbox(_Mailbox):
92
def _search_start(self):
95
line = self.fp.readline()
98
if line[:5] == 'From ' and self._isrealfromline(line):
102
def _search_end(self):
103
self.fp.readline() # Throw away header line
106
line = self.fp.readline()
109
if line[:5] == 'From ' and self._isrealfromline(line):
113
# An overridable mechanism to test for From-line-ness. You can either
114
# specify a different regular expression or define a whole new
115
# _isrealfromline() method. Note that this only gets called for lines
116
# starting with the 5 characters "From ".
119
#http://home.netscape.com/eng/mozilla/2.0/relnotes/demo/content-length.html
120
# the only portable, reliable way to find message delimiters in a BSD (i.e
121
# Unix mailbox) style folder is to search for "\n\nFrom .*\n", or at the
122
# beginning of the file, "^From .*\n". While _fromlinepattern below seems
123
# like a good idea, in practice, there are too many variations for more
124
# strict parsing of the line to be completely accurate.
126
# _strict_isrealfromline() is the old version which tries to do stricter
127
# parsing of the From_ line. _portable_isrealfromline() simply returns
128
# true, since it's never called if the line doesn't already start with
131
# This algorithm, and the way it interacts with _search_start() and
132
# _search_end() may not be completely correct, because it doesn't check
133
# that the two characters preceding "From " are \n\n or the beginning of
134
# the file. Fixing this would require a more extensive rewrite than is
135
# necessary. For convenience, we've added a StrictUnixMailbox class which
136
# uses the older, more strict _fromlinepattern regular expression.
138
_fromlinepattern = r"From \s*[^\s]+\s+\w\w\w\s+\w\w\w\s+\d?\d\s+" \
139
r"\d?\d:\d\d(:\d\d)?(\s+[^\s]+)?\s+\d\d\d\d\s*$"
142
def _strict_isrealfromline(self, line):
145
self._regexp = re.compile(self._fromlinepattern)
146
return self._regexp.match(line)
148
def _portable_isrealfromline(self, line):
151
_isrealfromline = _strict_isrealfromline
154
class PortableUnixMailbox(UnixMailbox):
155
_isrealfromline = UnixMailbox._portable_isrealfromline
158
class MmdfMailbox(_Mailbox):
159
def _search_start(self):
161
line = self.fp.readline()
164
if line[:5] == '\001\001\001\001\n':
167
def _search_end(self):
170
line = self.fp.readline()
173
if line == '\001\001\001\001\n':
179
def __init__(self, dirname, factory=rfc822.Message):
181
pat = re.compile('^[1-9][0-9]*$')
182
self.dirname = dirname
183
# the three following lines could be combined into:
184
# list = map(long, filter(pat.match, os.listdir(self.dirname)))
185
list = os.listdir(self.dirname)
186
list = filter(pat.match, list)
187
list = map(long, list)
189
# This only works in Python 1.6 or later;
190
# before that str() added 'L':
191
self.boxes = map(str, list)
192
self.factory = factory
199
fp = open(os.path.join(self.dirname, fn))
200
return self.factory(fp)
204
# Qmail directory mailbox
206
def __init__(self, dirname, factory=rfc822.Message):
207
self.dirname = dirname
208
self.factory = factory
211
newdir = os.path.join(self.dirname, 'new')
212
boxes = [os.path.join(newdir, f)
213
for f in os.listdir(newdir) if f[0] != '.']
215
# Now check for current mail in this maildir
216
curdir = os.path.join(self.dirname, 'cur')
217
boxes += [os.path.join(curdir, f)
218
for f in os.listdir(curdir) if f[0] != '.']
228
return self.factory(fp)
231
class BabylMailbox(_Mailbox):
232
def _search_start(self):
234
line = self.fp.readline()
237
if line == '*** EOOH ***\n':
240
def _search_end(self):
243
line = self.fp.readline()
246
if line == '\037\014\n':
258
for key in 'MAILDIR', 'MAIL', 'LOGNAME', 'USER':
259
if os.environ.has_key(key):
260
mbox = os.environ[key]
263
print "$MAIL, $LOGNAME nor $USER set -- who are you?"
268
mbox = os.environ['HOME'] + '/Mail/' + mbox[1:]
269
elif not '/' in mbox:
270
mbox = '/usr/mail/' + mbox
271
if os.path.isdir(mbox):
272
if os.path.isdir(os.path.join(mbox, 'cur')):
290
print 'Message %d body:'%num
293
sys.stdout.write(msg.fp.read())
295
print 'Mailbox',mbox,'has',len(msgs),'messages:'
297
f = msg.getheader('from') or ""
298
s = msg.getheader('subject') or ""
299
d = msg.getheader('date') or ""
300
print '-%20.20s %20.20s %-30.30s'%(f, d[5:], s)
303
if __name__ == '__main__':