blob: 53b2c19f61375b168a90691fdd2c419cf33a285d [file] [log] [blame]
Guido van Rossumf06ee5f1996-11-27 19:52:01 +00001#! /usr/bin/env python
Guido van Rossum62448671996-09-17 21:33:15 +00002
3"""Classes to handle Unix style, MMDF style, and MH style mailboxes."""
4
5
Guido van Rossumc7b68821994-04-28 09:53:33 +00006import rfc822
Jack Jansen97157791995-10-23 13:59:53 +00007import os
Guido van Rossumc7b68821994-04-28 09:53:33 +00008
9class _Mailbox:
Fred Drakedbbf76b2000-07-09 16:44:26 +000010 def __init__(self, fp):
11 self.fp = fp
12 self.seekp = 0
Guido van Rossum8ca84201998-03-26 20:56:10 +000013
Fred Drakedbbf76b2000-07-09 16:44:26 +000014 def seek(self, pos, whence=0):
15 if whence==1: # Relative to current position
16 self.pos = self.pos + pos
17 if whence==2: # Relative to file's end
18 self.pos = self.stop + pos
19 else: # Default - absolute position
20 self.pos = self.start + pos
Guido van Rossumc7b68821994-04-28 09:53:33 +000021
Fred Drakedbbf76b2000-07-09 16:44:26 +000022 def next(self):
23 while 1:
24 self.fp.seek(self.seekp)
25 try:
26 self._search_start()
27 except EOFError:
28 self.seekp = self.fp.tell()
29 return None
30 start = self.fp.tell()
31 self._search_end()
32 self.seekp = stop = self.fp.tell()
Fred Drake8152d322000-12-12 23:20:45 +000033 if start != stop:
Fred Drakedbbf76b2000-07-09 16:44:26 +000034 break
35 return rfc822.Message(_Subfile(self.fp, start, stop))
Guido van Rossumc7b68821994-04-28 09:53:33 +000036
Guido van Rossumc7b68821994-04-28 09:53:33 +000037
38class _Subfile:
Fred Drakedbbf76b2000-07-09 16:44:26 +000039 def __init__(self, fp, start, stop):
40 self.fp = fp
41 self.start = start
42 self.stop = stop
43 self.pos = self.start
Guido van Rossum8ca84201998-03-26 20:56:10 +000044
Fred Drakedbbf76b2000-07-09 16:44:26 +000045 def read(self, length = None):
46 if self.pos >= self.stop:
47 return ''
48 remaining = self.stop - self.pos
49 if length is None or length < 0:
50 length = remaining
51 elif length > remaining:
52 length = remaining
53 self.fp.seek(self.pos)
54 data = self.fp.read(length)
55 self.pos = self.fp.tell()
56 return data
Guido van Rossumc7b68821994-04-28 09:53:33 +000057
Fred Drakedbbf76b2000-07-09 16:44:26 +000058 def readline(self, length = None):
59 if self.pos >= self.stop:
60 return ''
61 if length is None:
62 length = self.stop - self.pos
63 self.fp.seek(self.pos)
64 data = self.fp.readline(length)
65 self.pos = self.fp.tell()
66 return data
Guido van Rossumc7b68821994-04-28 09:53:33 +000067
Fred Drakedbbf76b2000-07-09 16:44:26 +000068 def readlines(self, sizehint = -1):
69 lines = []
70 while 1:
71 line = self.readline()
72 if not line:
73 break
74 lines.append(line)
75 if sizehint >= 0:
76 sizehint = sizehint - len(line)
77 if sizehint <= 0:
78 break
79 return lines
Guido van Rossumc7b68821994-04-28 09:53:33 +000080
Fred Drakedbbf76b2000-07-09 16:44:26 +000081 def tell(self):
82 return self.pos - self.start
Guido van Rossume256a0f1999-03-24 16:20:45 +000083
Fred Drakedbbf76b2000-07-09 16:44:26 +000084 def seek(self, pos, whence=0):
85 if whence == 0:
86 self.pos = self.start + pos
87 elif whence == 1:
88 self.pos = self.pos + pos
89 elif whence == 2:
90 self.pos = self.stop + pos
Guido van Rossumc7b68821994-04-28 09:53:33 +000091
Fred Drakedbbf76b2000-07-09 16:44:26 +000092 def close(self):
93 del self.fp
Guido van Rossumc7b68821994-04-28 09:53:33 +000094
Guido van Rossumc7b68821994-04-28 09:53:33 +000095
96class UnixMailbox(_Mailbox):
Fred Drakedbbf76b2000-07-09 16:44:26 +000097 def _search_start(self):
98 while 1:
99 pos = self.fp.tell()
100 line = self.fp.readline()
101 if not line:
102 raise EOFError
103 if line[:5] == 'From ' and self._isrealfromline(line):
104 self.fp.seek(pos)
105 return
Guido van Rossum8ca84201998-03-26 20:56:10 +0000106
Fred Drakedbbf76b2000-07-09 16:44:26 +0000107 def _search_end(self):
108 self.fp.readline() # Throw away header line
109 while 1:
110 pos = self.fp.tell()
111 line = self.fp.readline()
112 if not line:
113 return
114 if line[:5] == 'From ' and self._isrealfromline(line):
115 self.fp.seek(pos)
116 return
Guido van Rossumc7b68821994-04-28 09:53:33 +0000117
Fred Drakedbbf76b2000-07-09 16:44:26 +0000118 # An overridable mechanism to test for From-line-ness.
119 # You can either specify a different regular expression
120 # or define a whole new _isrealfromline() method.
121 # Note that this only gets called for lines starting with
122 # the 5 characters "From ".
Guido van Rossumc7b68821994-04-28 09:53:33 +0000123
Fred Drakedbbf76b2000-07-09 16:44:26 +0000124 _fromlinepattern = r"From \s*[^\s]+\s+\w\w\w\s+\w\w\w\s+\d?\d\s+" \
125 r"\d?\d:\d\d(:\d\d)?(\s+[^\s]+)?\s+\d\d\d\d\s*$"
126 _regexp = None
Guido van Rossumfbe63de1998-04-03 16:04:05 +0000127
Fred Drakedbbf76b2000-07-09 16:44:26 +0000128 def _isrealfromline(self, line):
129 if not self._regexp:
130 import re
131 self._regexp = re.compile(self._fromlinepattern)
132 return self._regexp.match(line)
Guido van Rossumfbe63de1998-04-03 16:04:05 +0000133
Guido van Rossumfbe63de1998-04-03 16:04:05 +0000134
Guido van Rossumc7b68821994-04-28 09:53:33 +0000135class MmdfMailbox(_Mailbox):
Fred Drakedbbf76b2000-07-09 16:44:26 +0000136 def _search_start(self):
137 while 1:
138 line = self.fp.readline()
139 if not line:
140 raise EOFError
141 if line[:5] == '\001\001\001\001\n':
142 return
Guido van Rossum8ca84201998-03-26 20:56:10 +0000143
Fred Drakedbbf76b2000-07-09 16:44:26 +0000144 def _search_end(self):
145 while 1:
146 pos = self.fp.tell()
147 line = self.fp.readline()
148 if not line:
149 return
150 if line == '\001\001\001\001\n':
151 self.fp.seek(pos)
152 return
Guido van Rossumc7b68821994-04-28 09:53:33 +0000153
Guido van Rossumc7b68821994-04-28 09:53:33 +0000154
Jack Jansen97157791995-10-23 13:59:53 +0000155class MHMailbox:
Fred Drakedbbf76b2000-07-09 16:44:26 +0000156 def __init__(self, dirname):
157 import re
Guido van Rossum0707fea2000-08-10 03:05:26 +0000158 pat = re.compile('^[1-9][0-9]*$')
Fred Drakedbbf76b2000-07-09 16:44:26 +0000159 self.dirname = dirname
Sjoerd Mullenderd2653a92000-08-11 07:48:36 +0000160 # the three following lines could be combined into:
161 # list = map(long, filter(pat.match, os.listdir(self.dirname)))
162 list = os.listdir(self.dirname)
163 list = filter(pat.match, list)
Guido van Rossum0707fea2000-08-10 03:05:26 +0000164 list = map(long, list)
165 list.sort()
166 # This only works in Python 1.6 or later;
167 # before that str() added 'L':
168 self.boxes = map(str, list)
Jack Jansen97157791995-10-23 13:59:53 +0000169
Fred Drakedbbf76b2000-07-09 16:44:26 +0000170 def next(self):
171 if not self.boxes:
172 return None
173 fn = self.boxes[0]
174 del self.boxes[0]
175 fp = open(os.path.join(self.dirname, fn))
176 return rfc822.Message(fp)
Guido van Rossum8ca84201998-03-26 20:56:10 +0000177
Guido van Rossum9a4d6371998-12-23 22:05:42 +0000178
179class Maildir:
Fred Drakedbbf76b2000-07-09 16:44:26 +0000180 # Qmail directory mailbox
Guido van Rossum9a4d6371998-12-23 22:05:42 +0000181
Fred Drakedbbf76b2000-07-09 16:44:26 +0000182 def __init__(self, dirname):
Fred Drakedbbf76b2000-07-09 16:44:26 +0000183 self.dirname = dirname
Guido van Rossum9a4d6371998-12-23 22:05:42 +0000184
Fred Drakedbbf76b2000-07-09 16:44:26 +0000185 # check for new mail
186 newdir = os.path.join(self.dirname, 'new')
Fred Draked9a8dec2000-09-22 18:41:50 +0000187 boxes = [os.path.join(newdir, f)
188 for f in os.listdir(newdir) if f[0] != '.']
Guido van Rossum9a4d6371998-12-23 22:05:42 +0000189
Fred Drakedbbf76b2000-07-09 16:44:26 +0000190 # Now check for current mail in this maildir
191 curdir = os.path.join(self.dirname, 'cur')
Fred Draked9a8dec2000-09-22 18:41:50 +0000192 boxes += [os.path.join(curdir, f)
193 for f in os.listdir(curdir) if f[0] != '.']
Guido van Rossum9a4d6371998-12-23 22:05:42 +0000194
Fred Drakede3518e2000-10-23 13:37:01 +0000195 self.boxes = boxes
196
Fred Drakedbbf76b2000-07-09 16:44:26 +0000197 def next(self):
198 if not self.boxes:
199 return None
200 fn = self.boxes[0]
201 del self.boxes[0]
Fred Drakee108a022000-09-14 14:44:43 +0000202 fp = open(fn)
Fred Drakedbbf76b2000-07-09 16:44:26 +0000203 return rfc822.Message(fp)
Guido van Rossum9a4d6371998-12-23 22:05:42 +0000204
Guido van Rossum9a4d6371998-12-23 22:05:42 +0000205
Guido van Rossumfdf58fe1997-05-15 14:33:09 +0000206class BabylMailbox(_Mailbox):
Fred Drakedbbf76b2000-07-09 16:44:26 +0000207 def _search_start(self):
208 while 1:
209 line = self.fp.readline()
210 if not line:
211 raise EOFError
212 if line == '*** EOOH ***\n':
213 return
Guido van Rossumfdf58fe1997-05-15 14:33:09 +0000214
Fred Drakedbbf76b2000-07-09 16:44:26 +0000215 def _search_end(self):
216 while 1:
217 pos = self.fp.tell()
218 line = self.fp.readline()
219 if not line:
220 return
221 if line == '\037\014\n':
222 self.fp.seek(pos)
223 return
Guido van Rossumfdf58fe1997-05-15 14:33:09 +0000224
225
Guido van Rossum62448671996-09-17 21:33:15 +0000226def _test():
Fred Drakedbbf76b2000-07-09 16:44:26 +0000227 import time
228 import sys
Fred Drakedbbf76b2000-07-09 16:44:26 +0000229 import os
Jack Jansen97157791995-10-23 13:59:53 +0000230
Fred Drakedbbf76b2000-07-09 16:44:26 +0000231 args = sys.argv[1:]
232 if not args:
233 for key in 'MAILDIR', 'MAIL', 'LOGNAME', 'USER':
234 if os.environ.has_key(key):
235 mbox = os.environ[key]
236 break
Fred Drake13a2c272000-02-10 17:17:14 +0000237 else:
Fred Drakedbbf76b2000-07-09 16:44:26 +0000238 print "$MAIL, $LOGNAME nor $USER set -- who are you?"
239 return
240 else:
241 mbox = args[0]
242 if mbox[:1] == '+':
243 mbox = os.environ['HOME'] + '/Mail/' + mbox[1:]
244 elif not '/' in mbox:
245 mbox = '/usr/mail/' + mbox
246 if os.path.isdir(mbox):
247 if os.path.isdir(os.path.join(mbox, 'cur')):
248 mb = Maildir(mbox)
Fred Drake13a2c272000-02-10 17:17:14 +0000249 else:
Fred Drakedbbf76b2000-07-09 16:44:26 +0000250 mb = MHMailbox(mbox)
251 else:
252 fp = open(mbox, 'r')
253 mb = UnixMailbox(fp)
254
255 msgs = []
256 while 1:
257 msg = mb.next()
258 if msg is None:
259 break
260 msgs.append(msg)
261 if len(args) <= 1:
262 msg.fp = None
263 if len(args) > 1:
Fred Drakede3518e2000-10-23 13:37:01 +0000264 num = int(args[1])
Fred Drakedbbf76b2000-07-09 16:44:26 +0000265 print 'Message %d body:'%num
266 msg = msgs[num-1]
267 msg.rewindbody()
268 sys.stdout.write(msg.fp.read())
269 else:
270 print 'Mailbox',mbox,'has',len(msgs),'messages:'
271 for msg in msgs:
272 f = msg.getheader('from') or ""
273 s = msg.getheader('subject') or ""
274 d = msg.getheader('date') or ""
Fred Drakecc4adf22000-09-30 23:59:04 +0000275 print '-%20.20s %20.20s %-30.30s'%(f, d[5:], s)
Guido van Rossum62448671996-09-17 21:33:15 +0000276
277
278if __name__ == '__main__':
Fred Drakedbbf76b2000-07-09 16:44:26 +0000279 _test()