blob: f965b0fd86725a98b59233d396ed68e11c270f74 [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:
Guido van Rossum8ca84201998-03-26 20:56:10 +000010
Guido van Rossumc7b68821994-04-28 09:53:33 +000011 def __init__(self, fp):
12 self.fp = fp
13 self.seekp = 0
14
Guido van Rossum44a4d591996-01-25 18:40:41 +000015 def seek(self, pos, whence=0):
16 if whence==1: # Relative to current position
17 self.pos = self.pos + pos
18 if whence==2: # Relative to file's end
19 self.pos = self.stop + pos
20 else: # Default - absolute position
21 self.pos = self.start + pos
Guido van Rossumc7b68821994-04-28 09:53:33 +000022
23 def next(self):
24 while 1:
25 self.fp.seek(self.seekp)
26 try:
27 self._search_start()
28 except EOFError:
29 self.seekp = self.fp.tell()
30 return None
31 start = self.fp.tell()
32 self._search_end()
33 self.seekp = stop = self.fp.tell()
34 if start <> stop:
35 break
36 return rfc822.Message(_Subfile(self.fp, start, stop))
37
38class _Subfile:
Guido van Rossum8ca84201998-03-26 20:56:10 +000039
Guido van Rossumc7b68821994-04-28 09:53:33 +000040 def __init__(self, fp, start, stop):
41 self.fp = fp
42 self.start = start
43 self.stop = stop
44 self.pos = self.start
45
Guido van Rossum2922c6d1994-05-06 14:28:19 +000046 def read(self, length = None):
Guido van Rossumc7b68821994-04-28 09:53:33 +000047 if self.pos >= self.stop:
48 return ''
Guido van Rossume50b0a41998-06-17 18:34:40 +000049 remaining = self.stop - self.pos
50 if length is None or length < 0:
51 length = remaining
52 elif length > remaining:
53 length = remaining
Guido van Rossumc7b68821994-04-28 09:53:33 +000054 self.fp.seek(self.pos)
Guido van Rossum7333c4c1998-07-20 15:24:01 +000055 data = self.fp.read(length)
56 self.pos = self.fp.tell()
57 return data
Guido van Rossumc7b68821994-04-28 09:53:33 +000058
Guido van Rossum2922c6d1994-05-06 14:28:19 +000059 def readline(self, length = None):
Guido van Rossumc7b68821994-04-28 09:53:33 +000060 if self.pos >= self.stop:
61 return ''
Guido van Rossum2922c6d1994-05-06 14:28:19 +000062 if length is None:
Guido van Rossumc7b68821994-04-28 09:53:33 +000063 length = self.stop - self.pos
Guido van Rossumc7b68821994-04-28 09:53:33 +000064 self.fp.seek(self.pos)
65 data = self.fp.readline(length)
Guido van Rossum7333c4c1998-07-20 15:24:01 +000066 self.pos = self.fp.tell()
Guido van Rossumc7b68821994-04-28 09:53:33 +000067 return data
68
69 def tell(self):
70 return self.pos - self.start
71
Jack Jansen97157791995-10-23 13:59:53 +000072 def seek(self, pos, whence=0):
Guido van Rossum8ca84201998-03-26 20:56:10 +000073 if whence == 0:
74 self.pos = self.start + pos
Jack Jansen97157791995-10-23 13:59:53 +000075 elif whence == 1:
Guido van Rossum8ca84201998-03-26 20:56:10 +000076 self.pos = self.pos + pos
Jack Jansen97157791995-10-23 13:59:53 +000077 elif whence == 2:
Guido van Rossum8ca84201998-03-26 20:56:10 +000078 self.pos = self.stop + pos
Guido van Rossumc7b68821994-04-28 09:53:33 +000079
80 def close(self):
Guido van Rossum7333c4c1998-07-20 15:24:01 +000081 del self.fp
Guido van Rossumc7b68821994-04-28 09:53:33 +000082
83class UnixMailbox(_Mailbox):
Guido van Rossum8ca84201998-03-26 20:56:10 +000084
Guido van Rossumc7b68821994-04-28 09:53:33 +000085 def _search_start(self):
86 while 1:
87 line = self.fp.readline()
88 if not line:
89 raise EOFError
Guido van Rossumfbe63de1998-04-03 16:04:05 +000090 if line[:5] == 'From ' and self._isrealfromline(line):
Guido van Rossumc7b68821994-04-28 09:53:33 +000091 return
92
93 def _search_end(self):
94 while 1:
95 pos = self.fp.tell()
96 line = self.fp.readline()
97 if not line:
98 return
Guido van Rossumfbe63de1998-04-03 16:04:05 +000099 if line[:5] == 'From ' and self._isrealfromline(line):
Guido van Rossumc7b68821994-04-28 09:53:33 +0000100 self.fp.seek(pos)
101 return
102
Guido van Rossumfbe63de1998-04-03 16:04:05 +0000103 # An overridable mechanism to test for From-line-ness.
104 # You can either specify a different regular expression
105 # or define a whole new _isrealfromline() method.
106 # Note that this only gets called for lines starting with
107 # the 5 characters "From ".
108
109 _fromlinepattern = r"From \s*[^\s]+\s+\w\w\w\s+\w\w\w\s+\d?\d\s+" \
Guido van Rossum8ba40361998-07-02 23:05:32 +0000110 r"\d?\d:\d\d(:\d\d)?(\s+[^\s]+)?\s+\d\d\d\d\s*$"
Guido van Rossumfbe63de1998-04-03 16:04:05 +0000111 _regexp = None
112
113 def _isrealfromline(self, line):
114 if not self._regexp:
115 import re
116 self._regexp = re.compile(self._fromlinepattern)
117 return self._regexp.match(line)
118
Guido van Rossumc7b68821994-04-28 09:53:33 +0000119class MmdfMailbox(_Mailbox):
Guido van Rossum8ca84201998-03-26 20:56:10 +0000120
Guido van Rossumc7b68821994-04-28 09:53:33 +0000121 def _search_start(self):
122 while 1:
123 line = self.fp.readline()
124 if not line:
125 raise EOFError
126 if line[:5] == '\001\001\001\001\n':
127 return
128
129 def _search_end(self):
130 while 1:
131 pos = self.fp.tell()
132 line = self.fp.readline()
133 if not line:
134 return
135 if line == '\001\001\001\001\n':
136 self.fp.seek(pos)
137 return
138
Jack Jansen97157791995-10-23 13:59:53 +0000139class MHMailbox:
Jack Jansen97157791995-10-23 13:59:53 +0000140
Guido van Rossum8ca84201998-03-26 20:56:10 +0000141 def __init__(self, dirname):
142 import re
143 pat = re.compile('^[0-9][0-9]*$')
144 self.dirname = dirname
145 files = os.listdir(self.dirname)
146 self.boxes = []
147 for f in files:
148 if pat.match(f):
149 self.boxes.append(f)
150
151 def next(self):
152 if not self.boxes:
153 return None
154 fn = self.boxes[0]
155 del self.boxes[0]
156 fp = open(os.path.join(self.dirname, fn))
157 return rfc822.Message(fp)
Guido van Rossum9a4d6371998-12-23 22:05:42 +0000158
159class Maildir:
160
161 # Qmail directory mailbox
162
163 def __init__(self, dirname):
164 import string
165 self.dirname = dirname
166 self.boxes = []
167
168 # check for new mail
169 newdir = os.path.join(self.dirname, 'new')
170 for file in os.listdir(newdir):
171 if len(string.split(file, '.')) > 2:
172 self.boxes.append(os.path.join(newdir, file))
173
174 # Now check for current mail in this maildir
175 curdir = os.path.join(self.dirname, 'cur')
176 for file in os.listdir(curdir):
177 if len(string.split(file, '.')) > 2:
178 self.boxes.append(os.path.join(curdir, file))
179
180 def next(self):
181 if not self.boxes:
182 return None
183 fn = self.boxes[0]
184 del self.boxes[0]
185 fp = open(os.path.join(self.dirname, fn))
186 return rfc822.Message(fp)
187
Guido van Rossumfdf58fe1997-05-15 14:33:09 +0000188class BabylMailbox(_Mailbox):
Guido van Rossumfdf58fe1997-05-15 14:33:09 +0000189
Guido van Rossum8ca84201998-03-26 20:56:10 +0000190 def _search_start(self):
191 while 1:
192 line = self.fp.readline()
193 if not line:
194 raise EOFError
195 if line == '*** EOOH ***\n':
196 return
197
198 def _search_end(self):
199 while 1:
200 pos = self.fp.tell()
201 line = self.fp.readline()
202 if not line:
203 return
204 if line == '\037\014\n':
205 self.fp.seek(pos)
206 return
Guido van Rossumfdf58fe1997-05-15 14:33:09 +0000207
208
Guido van Rossum62448671996-09-17 21:33:15 +0000209def _test():
Guido van Rossumc7b68821994-04-28 09:53:33 +0000210 import time
211 import sys
212 import string
Guido van Rossum62448671996-09-17 21:33:15 +0000213 import os
Jack Jansen97157791995-10-23 13:59:53 +0000214
Guido van Rossum62448671996-09-17 21:33:15 +0000215 args = sys.argv[1:]
216 if not args:
Guido van Rossum9a4d6371998-12-23 22:05:42 +0000217 for key in 'MAILDIR', 'MAIL', 'LOGNAME', 'USER':
Guido van Rossum6dd87831997-05-08 23:11:52 +0000218 if os.environ.has_key(key):
219 mbox = os.environ[key]
220 break
Guido van Rossum62448671996-09-17 21:33:15 +0000221 else:
Guido van Rossum6dd87831997-05-08 23:11:52 +0000222 print "$MAIL, $LOGNAME nor $USER set -- who are you?"
Guido van Rossum62448671996-09-17 21:33:15 +0000223 return
224 else:
225 mbox = args[0]
226 if mbox[:1] == '+':
227 mbox = os.environ['HOME'] + '/Mail/' + mbox[1:]
228 elif not '/' in mbox:
229 mbox = '/usr/mail/' + mbox
230 if os.path.isdir(mbox):
Guido van Rossum9a4d6371998-12-23 22:05:42 +0000231 if os.path.isdir(os.path.join(mbox, 'cur')):
232 mb = Maildir(mbox)
233 else:
234 mb = MHMailbox(mbox)
Guido van Rossum62448671996-09-17 21:33:15 +0000235 else:
236 fp = open(mbox, 'r')
237 mb = UnixMailbox(fp)
Jack Jansen97157791995-10-23 13:59:53 +0000238
Guido van Rossumc7b68821994-04-28 09:53:33 +0000239 msgs = []
240 while 1:
241 msg = mb.next()
Guido van Rossumfbe63de1998-04-03 16:04:05 +0000242 if msg is None:
Guido van Rossumc7b68821994-04-28 09:53:33 +0000243 break
244 msgs.append(msg)
Guido van Rossum9694fca1997-10-22 21:00:49 +0000245 msg.fp = None
Guido van Rossum62448671996-09-17 21:33:15 +0000246 if len(args) > 1:
247 num = string.atoi(args[1])
Guido van Rossumc7b68821994-04-28 09:53:33 +0000248 print 'Message %d body:'%num
249 msg = msgs[num-1]
250 msg.rewindbody()
251 sys.stdout.write(msg.fp.read())
Guido van Rossum62448671996-09-17 21:33:15 +0000252 else:
253 print 'Mailbox',mbox,'has',len(msgs),'messages:'
254 for msg in msgs:
Guido van Rossumfbe63de1998-04-03 16:04:05 +0000255 f = msg.getheader('from') or ""
256 s = msg.getheader('subject') or ""
257 d = msg.getheader('date') or ""
Guido van Rossum62448671996-09-17 21:33:15 +0000258 print '%20.20s %18.18s %-30.30s'%(f, d[5:], s)
259
260
261if __name__ == '__main__':
262 _test()