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
):
47 def _read(self
, length
, read_function
):
48 if self
.pos
>= self
.stop
:
50 remaining
= self
.stop
- self
.pos
51 if length
is None or length
< 0 or length
> remaining
:
53 self
.fp
.seek(self
.pos
)
54 data
= read_function(length
)
55 self
.pos
= self
.fp
.tell()
58 def read(self
, length
= None):
59 self
._read
(length
, self
.fp
.read
)
61 def readline(self
, length
= None):
62 self
._read
(length
, self
.fp
.readline
)
64 def readlines(self
, sizehint
= -1):
67 line
= self
.readline()
72 sizehint
= sizehint
- len(line
)
78 return self
.pos
- self
.start
80 def seek(self
, pos
, whence
=0):
82 self
.pos
= self
.start
+ pos
84 self
.pos
= self
.pos
+ pos
86 self
.pos
= self
.stop
+ pos
92 # Recommended to use PortableUnixMailbox instead!
93 class UnixMailbox(_Mailbox
):
95 def _search_start(self
):
98 line
= self
.fp
.readline()
101 if line
[:5] == 'From ' and self
._isrealfromline
(line
):
105 def _search_end(self
):
106 self
.fp
.readline() # Throw away header line
109 line
= self
.fp
.readline()
112 if line
[:5] == 'From ' and self
._isrealfromline
(line
):
116 # An overridable mechanism to test for From-line-ness. You can either
117 # specify a different regular expression or define a whole new
118 # _isrealfromline() method. Note that this only gets called for lines
119 # starting with the 5 characters "From ".
122 #http://home.netscape.com/eng/mozilla/2.0/relnotes/demo/content-length.html
123 # the only portable, reliable way to find message delimiters in a BSD (i.e
124 # Unix mailbox) style folder is to search for "\n\nFrom .*\n", or at the
125 # beginning of the file, "^From .*\n". While _fromlinepattern below seems
126 # like a good idea, in practice, there are too many variations for more
127 # strict parsing of the line to be completely accurate.
129 # _strict_isrealfromline() is the old version which tries to do stricter
130 # parsing of the From_ line. _portable_isrealfromline() simply returns
131 # true, since it's never called if the line doesn't already start with
134 # This algorithm, and the way it interacts with _search_start() and
135 # _search_end() may not be completely correct, because it doesn't check
136 # that the two characters preceding "From " are \n\n or the beginning of
137 # the file. Fixing this would require a more extensive rewrite than is
138 # necessary. For convenience, we've added a PortableUnixMailbox class
139 # which uses the more lenient _fromlinepattern regular expression.
141 _fromlinepattern
= r
"From \s*[^\s]+\s+\w\w\w\s+\w\w\w\s+\d?\d\s+" \
142 r
"\d?\d:\d\d(:\d\d)?(\s+[^\s]+)?\s+\d\d\d\d\s*$"
145 def _strict_isrealfromline(self
, line
):
148 self
._regexp
= re
.compile(self
._fromlinepattern
)
149 return self
._regexp
.match(line
)
151 def _portable_isrealfromline(self
, line
):
154 _isrealfromline
= _strict_isrealfromline
157 class PortableUnixMailbox(UnixMailbox
):
158 _isrealfromline
= UnixMailbox
._portable
_isrealfromline
161 class MmdfMailbox(_Mailbox
):
163 def _search_start(self
):
165 line
= self
.fp
.readline()
168 if line
[:5] == '\001\001\001\001\n':
171 def _search_end(self
):
174 line
= self
.fp
.readline()
177 if line
== '\001\001\001\001\n':
184 def __init__(self
, dirname
, factory
=rfc822
.Message
):
186 pat
= re
.compile('^[1-9][0-9]*$')
187 self
.dirname
= dirname
188 # the three following lines could be combined into:
189 # list = map(long, filter(pat.match, os.listdir(self.dirname)))
190 list = os
.listdir(self
.dirname
)
191 list = filter(pat
.match
, list)
192 list = map(long, list)
194 # This only works in Python 1.6 or later;
195 # before that str() added 'L':
196 self
.boxes
= map(str, list)
198 self
.factory
= factory
201 return iter(self
.next
, None)
206 fn
= self
.boxes
.pop()
207 fp
= open(os
.path
.join(self
.dirname
, fn
))
208 msg
= self
.factory(fp
)
211 except (AttributeError, TypeError):
217 # Qmail directory mailbox
219 def __init__(self
, dirname
, factory
=rfc822
.Message
):
220 self
.dirname
= dirname
221 self
.factory
= factory
224 newdir
= os
.path
.join(self
.dirname
, 'new')
225 boxes
= [os
.path
.join(newdir
, f
)
226 for f
in os
.listdir(newdir
) if f
[0] != '.']
228 # Now check for current mail in this maildir
229 curdir
= os
.path
.join(self
.dirname
, 'cur')
230 boxes
+= [os
.path
.join(curdir
, f
)
231 for f
in os
.listdir(curdir
) if f
[0] != '.']
236 return iter(self
.next
, None)
241 fn
= self
.boxes
.pop()
243 return self
.factory(fp
)
246 class BabylMailbox(_Mailbox
):
248 def _search_start(self
):
250 line
= self
.fp
.readline()
253 if line
== '*** EOOH ***\n':
256 def _search_end(self
):
259 line
= self
.fp
.readline()
262 if line
== '\037\014\n':
272 for key
in 'MAILDIR', 'MAIL', 'LOGNAME', 'USER':
273 if key
in os
.environ
:
274 mbox
= os
.environ
[key
]
277 print "$MAIL, $LOGNAME nor $USER set -- who are you?"
282 mbox
= os
.environ
['HOME'] + '/Mail/' + mbox
[1:]
283 elif not '/' in mbox
:
284 if os
.path
.isfile('/var/mail/' + mbox
):
285 mbox
= '/var/mail/' + mbox
287 mbox
= '/usr/mail/' + mbox
288 if os
.path
.isdir(mbox
):
289 if os
.path
.isdir(os
.path
.join(mbox
, 'cur')):
295 mb
= PortableUnixMailbox(fp
)
307 print 'Message %d body:'%num
310 sys
.stdout
.write(msg
.fp
.read())
312 print 'Mailbox',mbox
,'has',len(msgs
),'messages:'
314 f
= msg
.getheader('from') or ""
315 s
= msg
.getheader('subject') or ""
316 d
= msg
.getheader('date') or ""
317 print '-%20.20s %20.20s %-30.30s'%(f
, d
[5:], s
)
320 if __name__
== '__main__':