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__':