3 """Classes to handle Unix style, MMDF style, and MH style mailboxes."""
9 __all__
= ["UnixMailbox","MmdfMailbox","MHMailbox","Maildir","BabylMailbox",
10 "PortableUnixMailbox"]
14 def __init__(self
, fp
, factory
=rfc822
.Message
):
17 self
.factory
= factory
20 return iter(self
.next
, None)
24 self
.fp
.seek(self
.seekp
)
28 self
.seekp
= self
.fp
.tell()
30 start
= self
.fp
.tell()
32 self
.seekp
= stop
= self
.fp
.tell()
35 return self
.factory(_Subfile(self
.fp
, start
, stop
))
40 def __init__(self
, fp
, start
, stop
):
46 def read(self
, length
= None):
47 if self
.pos
>= self
.stop
:
49 remaining
= self
.stop
- self
.pos
50 if length
is None or length
< 0:
52 elif length
> remaining
:
54 self
.fp
.seek(self
.pos
)
55 data
= self
.fp
.read(length
)
56 self
.pos
= self
.fp
.tell()
59 def readline(self
, length
= None):
60 if self
.pos
>= self
.stop
:
63 length
= self
.stop
- self
.pos
64 self
.fp
.seek(self
.pos
)
65 data
= self
.fp
.readline(length
)
66 self
.pos
= self
.fp
.tell()
69 def readlines(self
, sizehint
= -1):
72 line
= self
.readline()
77 sizehint
= sizehint
- len(line
)
83 return self
.pos
- self
.start
85 def seek(self
, pos
, whence
=0):
87 self
.pos
= self
.start
+ pos
89 self
.pos
= self
.pos
+ pos
91 self
.pos
= self
.stop
+ pos
97 # Recommended to use PortableUnixMailbox instead!
98 class UnixMailbox(_Mailbox
):
100 def _search_start(self
):
103 line
= self
.fp
.readline()
106 if line
[:5] == 'From ' and self
._isrealfromline
(line
):
110 def _search_end(self
):
111 self
.fp
.readline() # Throw away header line
114 line
= self
.fp
.readline()
117 if line
[:5] == 'From ' and self
._isrealfromline
(line
):
121 # An overridable mechanism to test for From-line-ness. You can either
122 # specify a different regular expression or define a whole new
123 # _isrealfromline() method. Note that this only gets called for lines
124 # starting with the 5 characters "From ".
127 #http://home.netscape.com/eng/mozilla/2.0/relnotes/demo/content-length.html
128 # the only portable, reliable way to find message delimiters in a BSD (i.e
129 # Unix mailbox) style folder is to search for "\n\nFrom .*\n", or at the
130 # beginning of the file, "^From .*\n". While _fromlinepattern below seems
131 # like a good idea, in practice, there are too many variations for more
132 # strict parsing of the line to be completely accurate.
134 # _strict_isrealfromline() is the old version which tries to do stricter
135 # parsing of the From_ line. _portable_isrealfromline() simply returns
136 # true, since it's never called if the line doesn't already start with
139 # This algorithm, and the way it interacts with _search_start() and
140 # _search_end() may not be completely correct, because it doesn't check
141 # that the two characters preceding "From " are \n\n or the beginning of
142 # the file. Fixing this would require a more extensive rewrite than is
143 # necessary. For convenience, we've added a PortableUnixMailbox class
144 # which uses the more lenient _fromlinepattern regular expression.
146 _fromlinepattern
= r
"From \s*[^\s]+\s+\w\w\w\s+\w\w\w\s+\d?\d\s+" \
147 r
"\d?\d:\d\d(:\d\d)?(\s+[^\s]+)?\s+\d\d\d\d\s*$"
150 def _strict_isrealfromline(self
, line
):
153 self
._regexp
= re
.compile(self
._fromlinepattern
)
154 return self
._regexp
.match(line
)
156 def _portable_isrealfromline(self
, line
):
159 _isrealfromline
= _strict_isrealfromline
162 class PortableUnixMailbox(UnixMailbox
):
163 _isrealfromline
= UnixMailbox
._portable
_isrealfromline
166 class MmdfMailbox(_Mailbox
):
168 def _search_start(self
):
170 line
= self
.fp
.readline()
173 if line
[:5] == '\001\001\001\001\n':
176 def _search_end(self
):
179 line
= self
.fp
.readline()
182 if line
== '\001\001\001\001\n':
189 def __init__(self
, dirname
, factory
=rfc822
.Message
):
191 pat
= re
.compile('^[1-9][0-9]*$')
192 self
.dirname
= dirname
193 # the three following lines could be combined into:
194 # list = map(long, filter(pat.match, os.listdir(self.dirname)))
195 list = os
.listdir(self
.dirname
)
196 list = filter(pat
.match
, list)
197 list = map(long, list)
199 # This only works in Python 1.6 or later;
200 # before that str() added 'L':
201 self
.boxes
= map(str, list)
202 self
.factory
= factory
205 return iter(self
.next
, None)
210 fn
= self
.boxes
.pop(0)
211 fp
= open(os
.path
.join(self
.dirname
, fn
))
212 msg
= self
.factory(fp
)
215 except (AttributeError, TypeError):
221 # Qmail directory mailbox
223 def __init__(self
, dirname
, factory
=rfc822
.Message
):
224 self
.dirname
= dirname
225 self
.factory
= factory
228 newdir
= os
.path
.join(self
.dirname
, 'new')
229 boxes
= [os
.path
.join(newdir
, f
)
230 for f
in os
.listdir(newdir
) if f
[0] != '.']
232 # Now check for current mail in this maildir
233 curdir
= os
.path
.join(self
.dirname
, 'cur')
234 boxes
+= [os
.path
.join(curdir
, f
)
235 for f
in os
.listdir(curdir
) if f
[0] != '.']
240 return iter(self
.next
, None)
245 fn
= self
.boxes
.pop(0)
247 return self
.factory(fp
)
250 class BabylMailbox(_Mailbox
):
252 def _search_start(self
):
254 line
= self
.fp
.readline()
257 if line
== '*** EOOH ***\n':
260 def _search_end(self
):
263 line
= self
.fp
.readline()
266 if line
== '\037\014\n':
276 for key
in 'MAILDIR', 'MAIL', 'LOGNAME', 'USER':
277 if key
in os
.environ
:
278 mbox
= os
.environ
[key
]
281 print "$MAIL, $LOGNAME nor $USER set -- who are you?"
286 mbox
= os
.environ
['HOME'] + '/Mail/' + mbox
[1:]
287 elif not '/' in mbox
:
288 if os
.path
.isfile('/var/mail/' + mbox
):
289 mbox
= '/var/mail/' + mbox
291 mbox
= '/usr/mail/' + mbox
292 if os
.path
.isdir(mbox
):
293 if os
.path
.isdir(os
.path
.join(mbox
, 'cur')):
299 mb
= PortableUnixMailbox(fp
)
311 print 'Message %d body:'%num
314 sys
.stdout
.write(msg
.fp
.read())
316 print 'Mailbox',mbox
,'has',len(msgs
),'messages:'
318 f
= msg
.getheader('from') or ""
319 s
= msg
.getheader('subject') or ""
320 d
= msg
.getheader('date') or ""
321 print '-%20.20s %20.20s %-30.30s'%(f
, d
[5:], s
)
324 if __name__
== '__main__':