Vega strike Python Modules doc  0.5.1
Documentation of the " Modules " folder of Vega strike
 All Data Structures Namespaces Files Functions Variables
mailbox.py
Go to the documentation of this file.
1 #! /usr/bin/env python
2 
3 """Classes to handle Unix style, MMDF style, and MH style mailboxes."""
4 
5 
6 import rfc822
7 import os
8 
9 __all__ = ["UnixMailbox","MmdfMailbox","MHMailbox","Maildir","BabylMailbox",
10  "PortableUnixMailbox"]
11 
12 class _Mailbox:
13  def __init__(self, fp, factory=rfc822.Message):
14  self.fp = fp
15  self.seekp = 0
16  self.factory = factory
17 
18  def __iter__(self):
19  return iter(self.next, None)
20 
21  def next(self):
22  while 1:
23  self.fp.seek(self.seekp)
24  try:
25  self._search_start()
26  except EOFError:
27  self.seekp = self.fp.tell()
28  return None
29  start = self.fp.tell()
30  self._search_end()
31  self.seekp = stop = self.fp.tell()
32  if start != stop:
33  break
34  return self.factory(_Subfile(self.fp, start, stop))
35 
36 
37 class _Subfile:
38  def __init__(self, fp, start, stop):
39  self.fp = fp
40  self.start = start
41  self.stop = stop
42  self.pos = self.start
43 
44  def read(self, length = None):
45  if self.pos >= self.stop:
46  return ''
47  remaining = self.stop - self.pos
48  if length is None or length < 0:
49  length = remaining
50  elif length > remaining:
51  length = remaining
52  self.fp.seek(self.pos)
53  data = self.fp.read(length)
54  self.pos = self.fp.tell()
55  return data
56 
57  def readline(self, length = None):
58  if self.pos >= self.stop:
59  return ''
60  if length is None:
61  length = self.stop - self.pos
62  self.fp.seek(self.pos)
63  data = self.fp.readline(length)
64  self.pos = self.fp.tell()
65  return data
66 
67  def readlines(self, sizehint = -1):
68  lines = []
69  while 1:
70  line = self.readline()
71  if not line:
72  break
73  lines.append(line)
74  if sizehint >= 0:
75  sizehint = sizehint - len(line)
76  if sizehint <= 0:
77  break
78  return lines
79 
80  def tell(self):
81  return self.pos - self.start
82 
83  def seek(self, pos, whence=0):
84  if whence == 0:
85  self.pos = self.start + pos
86  elif whence == 1:
87  self.pos = self.pos + pos
88  elif whence == 2:
89  self.pos = self.stop + pos
90 
91  def close(self):
92  del self.fp
93 
94 
95 # Recommended to use PortableUnixMailbox instead!
97  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
106 
107  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
117 
118  # An overridable mechanism to test for From-line-ness. You can either
119  # specify a different regular expression or define a whole new
120  # _isrealfromline() method. Note that this only gets called for lines
121  # starting with the 5 characters "From ".
122  #
123  # BAW: According to
124  #http://home.netscape.com/eng/mozilla/2.0/relnotes/demo/content-length.html
125  # the only portable, reliable way to find message delimiters in a BSD (i.e
126  # Unix mailbox) style folder is to search for "\n\nFrom .*\n", or at the
127  # beginning of the file, "^From .*\n". While _fromlinepattern below seems
128  # like a good idea, in practice, there are too many variations for more
129  # strict parsing of the line to be completely accurate.
130  #
131  # _strict_isrealfromline() is the old version which tries to do stricter
132  # parsing of the From_ line. _portable_isrealfromline() simply returns
133  # true, since it's never called if the line doesn't already start with
134  # "From ".
135  #
136  # This algorithm, and the way it interacts with _search_start() and
137  # _search_end() may not be completely correct, because it doesn't check
138  # that the two characters preceding "From " are \n\n or the beginning of
139  # the file. Fixing this would require a more extensive rewrite than is
140  # necessary. For convenience, we've added a StrictUnixMailbox class which
141  # uses the older, more strict _fromlinepattern regular expression.
142 
143  _fromlinepattern = r"From \s*[^\s]+\s+\w\w\w\s+\w\w\w\s+\d?\d\s+" \
144  r"\d?\d:\d\d(:\d\d)?(\s+[^\s]+)?\s+\d\d\d\d\s*$"
145  _regexp = None
146 
147  def _strict_isrealfromline(self, line):
148  if not self._regexp:
149  import re
150  self._regexp = re.compile(self._fromlinepattern)
151  return self._regexp.match(line)
152 
153  def _portable_isrealfromline(self, line):
154  return 1
155 
156  _isrealfromline = _strict_isrealfromline
157 
158 
160  _isrealfromline = UnixMailbox._portable_isrealfromline
161 
162 
164  def _search_start(self):
165  while 1:
166  line = self.fp.readline()
167  if not line:
168  raise EOFError
169  if line[:5] == '\001\001\001\001\n':
170  return
171 
172  def _search_end(self):
173  while 1:
174  pos = self.fp.tell()
175  line = self.fp.readline()
176  if not line:
177  return
178  if line == '\001\001\001\001\n':
179  self.fp.seek(pos)
180  return
181 
182 
183 class MHMailbox:
184  def __init__(self, dirname, factory=rfc822.Message):
185  import re
186  pat = re.compile('^[1-9][0-9]*$')
187  self.dirname = dirname
188  # the three following lines could be combined into:
189  # list = map(long, filter(pat.match, os.listdir(self.dirname)))
190  list = os.listdir(self.dirname)
191  list = filter(pat.match, list)
192  list = map(long, list)
193  list.sort()
194  # This only works in Python 1.6 or later;
195  # before that str() added 'L':
196  self.boxes = map(str, list)
197  self.factory = factory
198 
199  def __iter__(self):
200  return iter(self.next, None)
201 
202  def next(self):
203  if not self.boxes:
204  return None
205  fn = self.boxes[0]
206  del self.boxes[0]
207  fp = open(os.path.join(self.dirname, fn))
208  return self.factory(fp)
209 
210 
211 class Maildir:
212  # Qmail directory mailbox
213 
214  def __init__(self, dirname, factory=rfc822.Message):
215  self.dirname = dirname
216  self.factory = factory
217 
218  # check for new mail
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] != '.']
222 
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] != '.']
227 
228  self.boxes = boxes
229 
230  def __iter__(self):
231  return iter(self.next, None)
232 
233  def next(self):
234  if not self.boxes:
235  return None
236  fn = self.boxes[0]
237  del self.boxes[0]
238  fp = open(fn)
239  return self.factory(fp)
240 
241 
243  def _search_start(self):
244  while 1:
245  line = self.fp.readline()
246  if not line:
247  raise EOFError
248  if line == '*** EOOH ***\n':
249  return
250 
251  def _search_end(self):
252  while 1:
253  pos = self.fp.tell()
254  line = self.fp.readline()
255  if not line:
256  return
257  if line == '\037\014\n':
258  self.fp.seek(pos)
259  return
260 
261 
262 def _test():
263  import sys
264 
265  args = sys.argv[1:]
266  if not args:
267  for key in 'MAILDIR', 'MAIL', 'LOGNAME', 'USER':
268  if os.environ.has_key(key):
269  mbox = os.environ[key]
270  break
271  else:
272  print "$MAIL, $LOGNAME nor $USER set -- who are you?"
273  return
274  else:
275  mbox = args[0]
276  if mbox[:1] == '+':
277  mbox = os.environ['HOME'] + '/Mail/' + mbox[1:]
278  elif not '/' in mbox:
279  mbox = '/usr/mail/' + mbox
280  if os.path.isdir(mbox):
281  if os.path.isdir(os.path.join(mbox, 'cur')):
282  mb = Maildir(mbox)
283  else:
284  mb = MHMailbox(mbox)
285  else:
286  fp = open(mbox, 'r')
287  mb = PortableUnixMailbox(fp)
288 
289  msgs = []
290  while 1:
291  msg = mb.next()
292  if msg is None:
293  break
294  msgs.append(msg)
295  if len(args) <= 1:
296  msg.fp = None
297  if len(args) > 1:
298  num = int(args[1])
299  print 'Message %d body:'%num
300  msg = msgs[num-1]
301  msg.rewindbody()
302  sys.stdout.write(msg.fp.read())
303  else:
304  print 'Mailbox',mbox,'has',len(msgs),'messages:'
305  for msg in msgs:
306  f = msg.getheader('from') or ""
307  s = msg.getheader('subject') or ""
308  d = msg.getheader('date') or ""
309  print '-%20.20s %20.20s %-30.30s'%(f, d[5:], s)
310 
311 
312 if __name__ == '__main__':
313  _test()