blob: 0ea4a5811d74b06a517c7450ecf59baf5596c848 [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)
55 self.pos = self.pos + length
56 return self.fp.read(length)
57
Guido van Rossum2922c6d1994-05-06 14:28:19 +000058 def readline(self, length = None):
Guido van Rossumc7b68821994-04-28 09:53:33 +000059 if self.pos >= self.stop:
60 return ''
Guido van Rossum2922c6d1994-05-06 14:28:19 +000061 if length is None:
Guido van Rossumc7b68821994-04-28 09:53:33 +000062 length = self.stop - self.pos
Guido van Rossumc7b68821994-04-28 09:53:33 +000063 self.fp.seek(self.pos)
64 data = self.fp.readline(length)
65 if len(data) < length:
66 length = len(data)
67 self.pos = self.pos + length
68 return data
69
70 def tell(self):
71 return self.pos - self.start
72
Jack Jansen97157791995-10-23 13:59:53 +000073 def seek(self, pos, whence=0):
Guido van Rossum8ca84201998-03-26 20:56:10 +000074 if whence == 0:
75 self.pos = self.start + pos
Jack Jansen97157791995-10-23 13:59:53 +000076 elif whence == 1:
Guido van Rossum8ca84201998-03-26 20:56:10 +000077 self.pos = self.pos + pos
Jack Jansen97157791995-10-23 13:59:53 +000078 elif whence == 2:
Guido van Rossum8ca84201998-03-26 20:56:10 +000079 self.pos = self.stop + pos
Guido van Rossumc7b68821994-04-28 09:53:33 +000080
81 def close(self):
82 pass
83
84class UnixMailbox(_Mailbox):
Guido van Rossum8ca84201998-03-26 20:56:10 +000085
Guido van Rossumc7b68821994-04-28 09:53:33 +000086 def _search_start(self):
87 while 1:
88 line = self.fp.readline()
89 if not line:
90 raise EOFError
Guido van Rossumfbe63de1998-04-03 16:04:05 +000091 if line[:5] == 'From ' and self._isrealfromline(line):
Guido van Rossumc7b68821994-04-28 09:53:33 +000092 return
93
94 def _search_end(self):
95 while 1:
96 pos = self.fp.tell()
97 line = self.fp.readline()
98 if not line:
99 return
Guido van Rossumfbe63de1998-04-03 16:04:05 +0000100 if line[:5] == 'From ' and self._isrealfromline(line):
Guido van Rossumc7b68821994-04-28 09:53:33 +0000101 self.fp.seek(pos)
102 return
103
Guido van Rossumfbe63de1998-04-03 16:04:05 +0000104 # An overridable mechanism to test for From-line-ness.
105 # You can either specify a different regular expression
106 # or define a whole new _isrealfromline() method.
107 # Note that this only gets called for lines starting with
108 # the 5 characters "From ".
109
110 _fromlinepattern = r"From \s*[^\s]+\s+\w\w\w\s+\w\w\w\s+\d?\d\s+" \
111 r"\d?\d:\d\d:\d\d(\s+[^\s]+)?\s+\d\d\d\d\s*$"
112 _regexp = None
113
114 def _isrealfromline(self, line):
115 if not self._regexp:
116 import re
117 self._regexp = re.compile(self._fromlinepattern)
118 return self._regexp.match(line)
119
Guido van Rossumc7b68821994-04-28 09:53:33 +0000120class MmdfMailbox(_Mailbox):
Guido van Rossum8ca84201998-03-26 20:56:10 +0000121
Guido van Rossumc7b68821994-04-28 09:53:33 +0000122 def _search_start(self):
123 while 1:
124 line = self.fp.readline()
125 if not line:
126 raise EOFError
127 if line[:5] == '\001\001\001\001\n':
128 return
129
130 def _search_end(self):
131 while 1:
132 pos = self.fp.tell()
133 line = self.fp.readline()
134 if not line:
135 return
136 if line == '\001\001\001\001\n':
137 self.fp.seek(pos)
138 return
139
Jack Jansen97157791995-10-23 13:59:53 +0000140class MHMailbox:
Jack Jansen97157791995-10-23 13:59:53 +0000141
Guido van Rossum8ca84201998-03-26 20:56:10 +0000142 def __init__(self, dirname):
143 import re
144 pat = re.compile('^[0-9][0-9]*$')
145 self.dirname = dirname
146 files = os.listdir(self.dirname)
147 self.boxes = []
148 for f in files:
149 if pat.match(f):
150 self.boxes.append(f)
151
152 def next(self):
153 if not self.boxes:
154 return None
155 fn = self.boxes[0]
156 del self.boxes[0]
157 fp = open(os.path.join(self.dirname, fn))
158 return rfc822.Message(fp)
Jack Jansen97157791995-10-23 13:59:53 +0000159
160
Guido van Rossumfdf58fe1997-05-15 14:33:09 +0000161class BabylMailbox(_Mailbox):
Guido van Rossumfdf58fe1997-05-15 14:33:09 +0000162
Guido van Rossum8ca84201998-03-26 20:56:10 +0000163 def _search_start(self):
164 while 1:
165 line = self.fp.readline()
166 if not line:
167 raise EOFError
168 if line == '*** EOOH ***\n':
169 return
170
171 def _search_end(self):
172 while 1:
173 pos = self.fp.tell()
174 line = self.fp.readline()
175 if not line:
176 return
177 if line == '\037\014\n':
178 self.fp.seek(pos)
179 return
Guido van Rossumfdf58fe1997-05-15 14:33:09 +0000180
181
Guido van Rossum62448671996-09-17 21:33:15 +0000182def _test():
Guido van Rossumc7b68821994-04-28 09:53:33 +0000183 import time
184 import sys
185 import string
Guido van Rossum62448671996-09-17 21:33:15 +0000186 import os
Jack Jansen97157791995-10-23 13:59:53 +0000187
Guido van Rossum62448671996-09-17 21:33:15 +0000188 args = sys.argv[1:]
189 if not args:
Guido van Rossum6dd87831997-05-08 23:11:52 +0000190 for key in 'MAIL', 'LOGNAME', 'USER':
191 if os.environ.has_key(key):
192 mbox = os.environ[key]
193 break
Guido van Rossum62448671996-09-17 21:33:15 +0000194 else:
Guido van Rossum6dd87831997-05-08 23:11:52 +0000195 print "$MAIL, $LOGNAME nor $USER set -- who are you?"
Guido van Rossum62448671996-09-17 21:33:15 +0000196 return
197 else:
198 mbox = args[0]
199 if mbox[:1] == '+':
200 mbox = os.environ['HOME'] + '/Mail/' + mbox[1:]
201 elif not '/' in mbox:
202 mbox = '/usr/mail/' + mbox
203 if os.path.isdir(mbox):
204 mb = MHMailbox(mbox)
205 else:
206 fp = open(mbox, 'r')
207 mb = UnixMailbox(fp)
Jack Jansen97157791995-10-23 13:59:53 +0000208
Guido van Rossumc7b68821994-04-28 09:53:33 +0000209 msgs = []
210 while 1:
211 msg = mb.next()
Guido van Rossumfbe63de1998-04-03 16:04:05 +0000212 if msg is None:
Guido van Rossumc7b68821994-04-28 09:53:33 +0000213 break
214 msgs.append(msg)
Guido van Rossum9694fca1997-10-22 21:00:49 +0000215 msg.fp = None
Guido van Rossum62448671996-09-17 21:33:15 +0000216 if len(args) > 1:
217 num = string.atoi(args[1])
Guido van Rossumc7b68821994-04-28 09:53:33 +0000218 print 'Message %d body:'%num
219 msg = msgs[num-1]
220 msg.rewindbody()
221 sys.stdout.write(msg.fp.read())
Guido van Rossum62448671996-09-17 21:33:15 +0000222 else:
223 print 'Mailbox',mbox,'has',len(msgs),'messages:'
224 for msg in msgs:
Guido van Rossumfbe63de1998-04-03 16:04:05 +0000225 f = msg.getheader('from') or ""
226 s = msg.getheader('subject') or ""
227 d = msg.getheader('date') or ""
Guido van Rossum62448671996-09-17 21:33:15 +0000228 print '%20.20s %18.18s %-30.30s'%(f, d[5:], s)
229
230
231if __name__ == '__main__':
232 _test()