home *** CD-ROM | disk | FTP | other *** search
/ PC World 2001 April / PCWorld_2001-04_cd.bin / Software / TemaCD / webclean / !!!python!!! / BeOpen-Python-2.0.exe / MAILBOX.PY < prev    next >
Encoding:
Python Source  |  2000-09-30  |  7.8 KB  |  280 lines

  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. class _Mailbox:
  10.     def __init__(self, fp):
  11.         self.fp = fp
  12.         self.seekp = 0
  13.  
  14.     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
  21.  
  22.     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()
  33.             if start <> stop:
  34.                 break
  35.         return rfc822.Message(_Subfile(self.fp, start, stop))
  36.  
  37.  
  38. class _Subfile:
  39.     def __init__(self, fp, start, stop):
  40.         self.fp = fp
  41.         self.start = start
  42.         self.stop = stop
  43.         self.pos = self.start
  44.  
  45.     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
  57.  
  58.     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
  67.  
  68.     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
  80.  
  81.     def tell(self):
  82.         return self.pos - self.start
  83.  
  84.     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
  91.  
  92.     def close(self):
  93.         del self.fp
  94.  
  95.  
  96. class UnixMailbox(_Mailbox):
  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.
  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 ".
  123.  
  124.     _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
  127.  
  128.     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)
  133.  
  134.  
  135. class MmdfMailbox(_Mailbox):
  136.     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
  143.  
  144.     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
  153.  
  154.  
  155. class MHMailbox:
  156.     def __init__(self, dirname):
  157.         import re
  158.         pat = re.compile('^[1-9][0-9]*$')
  159.         self.dirname = dirname
  160.         # 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)
  164.         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)
  169.  
  170.     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)
  177.  
  178.  
  179. class Maildir:
  180.     # Qmail directory mailbox
  181.  
  182.     def __init__(self, dirname):
  183.         import string
  184.         self.dirname = dirname
  185.  
  186.         # check for new mail
  187.         newdir = os.path.join(self.dirname, 'new')
  188.         boxes = [os.path.join(newdir, f)
  189.                  for f in os.listdir(newdir) if f[0] != '.']
  190.  
  191.         # Now check for current mail in this maildir
  192.         curdir = os.path.join(self.dirname, 'cur')
  193.         boxes += [os.path.join(curdir, f)
  194.                   for f in os.listdir(curdir) if f[0] != '.']
  195.  
  196.     def next(self):
  197.         if not self.boxes:
  198.             return None
  199.         fn = self.boxes[0]
  200.         del self.boxes[0]
  201.         fp = open(fn)
  202.         return rfc822.Message(fp)
  203.  
  204.  
  205. class BabylMailbox(_Mailbox):
  206.     def _search_start(self):
  207.         while 1:
  208.             line = self.fp.readline()
  209.             if not line:
  210.                 raise EOFError
  211.             if line == '*** EOOH ***\n':
  212.                 return
  213.  
  214.     def _search_end(self):
  215.         while 1:
  216.             pos = self.fp.tell()
  217.             line = self.fp.readline()
  218.             if not line:
  219.                 return
  220.             if line == '\037\014\n':
  221.                 self.fp.seek(pos)
  222.                 return
  223.  
  224.  
  225. def _test():
  226.     import time
  227.     import sys
  228.     import string
  229.     import os
  230.  
  231.     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
  237.         else:
  238.             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)
  249.         else:
  250.             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:
  264.         num = string.atoi(args[1])
  265.         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 ""
  275.             print '-%20.20s   %20.20s   %-30.30s'%(f, d[5:], s)
  276.  
  277.  
  278. if __name__ == '__main__':
  279.     _test()
  280.