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
17 def seek(self
, pos
, whence
=0):
18 if whence
==1: # Relative to current position
19 self
.pos
= self
.pos
+ pos
20 if whence
==2: # Relative to file's end
21 self
.pos
= self
.stop
+ pos
22 else: # Default - absolute position
23 self
.pos
= self
.start
+ pos
27 self
.fp
.seek(self
.seekp
)
31 self
.seekp
= self
.fp
.tell()
33 start
= self
.fp
.tell()
35 self
.seekp
= stop
= self
.fp
.tell()
38 return self
.factory(_Subfile(self
.fp
, start
, stop
))
42 def __init__(self
, fp
, start
, stop
):
48 def read(self
, length
= None):
49 if self
.pos
>= self
.stop
:
51 remaining
= self
.stop
- self
.pos
52 if length
is None or length
< 0:
54 elif length
> remaining
:
56 self
.fp
.seek(self
.pos
)
57 data
= self
.fp
.read(length
)
58 self
.pos
= self
.fp
.tell()
61 def readline(self
, length
= None):
62 if self
.pos
>= self
.stop
:
65 length
= self
.stop
- self
.pos
66 self
.fp
.seek(self
.pos
)
67 data
= self
.fp
.readline(length
)
68 self
.pos
= self
.fp
.tell()
71 def readlines(self
, sizehint
= -1):
74 line
= self
.readline()
79 sizehint
= sizehint
- len(line
)
85 return self
.pos
- self
.start
87 def seek(self
, pos
, whence
=0):
89 self
.pos
= self
.start
+ pos
91 self
.pos
= self
.pos
+ pos
93 self
.pos
= self
.stop
+ pos
99 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 StrictUnixMailbox class which
144 # uses the older, more strict _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
):
167 def _search_start(self
):
169 line
= self
.fp
.readline()
172 if line
[:5] == '\001\001\001\001\n':
175 def _search_end(self
):
178 line
= self
.fp
.readline()
181 if line
== '\001\001\001\001\n':
187 def __init__(self
, dirname
, factory
=rfc822
.Message
):
189 pat
= re
.compile('^[1-9][0-9]*$')
190 self
.dirname
= dirname
191 # the three following lines could be combined into:
192 # list = map(long, filter(pat.match, os.listdir(self.dirname)))
193 list = os
.listdir(self
.dirname
)
194 list = filter(pat
.match
, list)
195 list = map(long, list)
197 # This only works in Python 1.6 or later;
198 # before that str() added 'L':
199 self
.boxes
= map(str, list)
200 self
.factory
= factory
207 fp
= open(os
.path
.join(self
.dirname
, fn
))
208 return self
.factory(fp
)
212 # Qmail directory mailbox
214 def __init__(self
, dirname
, factory
=rfc822
.Message
):
215 self
.dirname
= dirname
216 self
.factory
= factory
219 newdir
= os
.path
.join(self
.dirname
, 'new')
220 boxes
= [os
.path
.join(newdir
, f
)
221 for f
in os
.listdir(newdir
) if f
[0] != '.']
223 # Now check for current mail in this maildir
224 curdir
= os
.path
.join(self
.dirname
, 'cur')
225 boxes
+= [os
.path
.join(curdir
, f
)
226 for f
in os
.listdir(curdir
) if f
[0] != '.']
236 return self
.factory(fp
)
239 class BabylMailbox(_Mailbox
):
240 def _search_start(self
):
242 line
= self
.fp
.readline()
245 if line
== '*** EOOH ***\n':
248 def _search_end(self
):
251 line
= self
.fp
.readline()
254 if line
== '\037\014\n':
266 for key
in 'MAILDIR', 'MAIL', 'LOGNAME', 'USER':
267 if os
.environ
.has_key(key
):
268 mbox
= os
.environ
[key
]
271 print "$MAIL, $LOGNAME nor $USER set -- who are you?"
276 mbox
= os
.environ
['HOME'] + '/Mail/' + mbox
[1:]
277 elif not '/' in mbox
:
278 mbox
= '/usr/mail/' + mbox
279 if os
.path
.isdir(mbox
):
280 if os
.path
.isdir(os
.path
.join(mbox
, 'cur')):
298 print 'Message %d body:'%num
301 sys
.stdout
.write(msg
.fp
.read())
303 print 'Mailbox',mbox
,'has',len(msgs
),'messages:'
305 f
= msg
.getheader('from') or ""
306 s
= msg
.getheader('subject') or ""
307 d
= msg
.getheader('date') or ""
308 print '-%20.20s %20.20s %-30.30s'%(f
, d
[5:], s
)
311 if __name__
== '__main__':