| """Various tools used by MIME-reading or MIME-writing programs.""" | 
 |  | 
 |  | 
 | import os | 
 | import rfc822 | 
 | import tempfile | 
 |  | 
 |  | 
 | class Message(rfc822.Message): | 
 |     """A derived class of rfc822.Message that knows about MIME headers and | 
 |     contains some hooks for decoding encoded and multipart messages.""" | 
 |  | 
 |     def __init__(self, fp, seekable = 1): | 
 |         rfc822.Message.__init__(self, fp, seekable) | 
 |         self.encodingheader = \ | 
 |                 self.getheader('content-transfer-encoding') | 
 |         self.typeheader = \ | 
 |                 self.getheader('content-type') | 
 |         self.parsetype() | 
 |         self.parseplist() | 
 |  | 
 |     def parsetype(self): | 
 |         str = self.typeheader | 
 |         if str is None: | 
 |             str = 'text/plain' | 
 |         if ';' in str: | 
 |             i = str.index(';') | 
 |             self.plisttext = str[i:] | 
 |             str = str[:i] | 
 |         else: | 
 |             self.plisttext = '' | 
 |         fields = str.split('/') | 
 |         for i in range(len(fields)): | 
 |             fields[i] = fields[i].strip().lower() | 
 |         self.type = '/'.join(fields) | 
 |         self.maintype = fields[0] | 
 |         self.subtype = '/'.join(fields[1:]) | 
 |  | 
 |     def parseplist(self): | 
 |         str = self.plisttext | 
 |         self.plist = [] | 
 |         while str[:1] == ';': | 
 |             str = str[1:] | 
 |             if ';' in str: | 
 |                 # XXX Should parse quotes! | 
 |                 end = str.index(';') | 
 |             else: | 
 |                 end = len(str) | 
 |             f = str[:end] | 
 |             if '=' in f: | 
 |                 i = f.index('=') | 
 |                 f = f[:i].strip().lower() + \ | 
 |                         '=' + f[i+1:].strip() | 
 |             self.plist.append(f.strip()) | 
 |             str = str[end:] | 
 |  | 
 |     def getplist(self): | 
 |         return self.plist | 
 |  | 
 |     def getparam(self, name): | 
 |         name = name.lower() + '=' | 
 |         n = len(name) | 
 |         for p in self.plist: | 
 |             if p[:n] == name: | 
 |                 return rfc822.unquote(p[n:]) | 
 |         return None | 
 |  | 
 |     def getparamnames(self): | 
 |         result = [] | 
 |         for p in self.plist: | 
 |             i = p.find('=') | 
 |             if i >= 0: | 
 |                 result.append(p[:i].lower()) | 
 |         return result | 
 |  | 
 |     def getencoding(self): | 
 |         if self.encodingheader is None: | 
 |             return '7bit' | 
 |         return self.encodingheader.lower() | 
 |  | 
 |     def gettype(self): | 
 |         return self.type | 
 |  | 
 |     def getmaintype(self): | 
 |         return self.maintype | 
 |  | 
 |     def getsubtype(self): | 
 |         return self.subtype | 
 |  | 
 |  | 
 |  | 
 |  | 
 | # Utility functions | 
 | # ----------------- | 
 |  | 
 |  | 
 | _prefix = None | 
 |  | 
 | def choose_boundary(): | 
 |     """Return a random string usable as a multipart boundary. | 
 |     The method used is so that it is *very* unlikely that the same | 
 |     string of characters will every occur again in the Universe, | 
 |     so the caller needn't check the data it is packing for the | 
 |     occurrence of the boundary. | 
 |  | 
 |     The boundary contains dots so you have to quote it in the header.""" | 
 |  | 
 |     global _prefix | 
 |     import time | 
 |     import random | 
 |     if _prefix is None: | 
 |         import socket | 
 |         import os | 
 |         hostid = socket.gethostbyname(socket.gethostname()) | 
 |         try: | 
 |             uid = `os.getuid()` | 
 |         except: | 
 |             uid = '1' | 
 |         try: | 
 |             pid = `os.getpid()` | 
 |         except: | 
 |             pid = '1' | 
 |         _prefix = hostid + '.' + uid + '.' + pid | 
 |     timestamp = '%.3f' % time.time() | 
 |     seed = `random.randint(0, 32767)` | 
 |     return _prefix + '.' + timestamp + '.' + seed | 
 |  | 
 |  | 
 | # Subroutines for decoding some common content-transfer-types | 
 |  | 
 | def decode(input, output, encoding): | 
 |     """Decode common content-transfer-encodings (base64, quopri, uuencode).""" | 
 |     if encoding == 'base64': | 
 |         import base64 | 
 |         return base64.decode(input, output) | 
 |     if encoding == 'quoted-printable': | 
 |         import quopri | 
 |         return quopri.decode(input, output) | 
 |     if encoding in ('uuencode', 'x-uuencode', 'uue', 'x-uue'): | 
 |         import uu | 
 |         return uu.decode(input, output) | 
 |     if encoding in ('7bit', '8bit'): | 
 |         return output.write(input.read()) | 
 |     if decodetab.has_key(encoding): | 
 |         pipethrough(input, decodetab[encoding], output) | 
 |     else: | 
 |         raise ValueError, \ | 
 |               'unknown Content-Transfer-Encoding: %s' % encoding | 
 |  | 
 | def encode(input, output, encoding): | 
 |     """Encode common content-transfer-encodings (base64, quopri, uuencode).""" | 
 |     if encoding == 'base64': | 
 |         import base64 | 
 |         return base64.encode(input, output) | 
 |     if encoding == 'quoted-printable': | 
 |         import quopri | 
 |         return quopri.encode(input, output, 0) | 
 |     if encoding in ('uuencode', 'x-uuencode', 'uue', 'x-uue'): | 
 |         import uu | 
 |         return uu.encode(input, output) | 
 |     if encoding in ('7bit', '8bit'): | 
 |         return output.write(input.read()) | 
 |     if encodetab.has_key(encoding): | 
 |         pipethrough(input, encodetab[encoding], output) | 
 |     else: | 
 |         raise ValueError, \ | 
 |               'unknown Content-Transfer-Encoding: %s' % encoding | 
 |  | 
 | # The following is no longer used for standard encodings | 
 |  | 
 | # XXX This requires that uudecode and mmencode are in $PATH | 
 |  | 
 | uudecode_pipe = '''( | 
 | TEMP=/tmp/@uu.$$ | 
 | sed "s%^begin [0-7][0-7]* .*%begin 600 $TEMP%" | uudecode | 
 | cat $TEMP | 
 | rm $TEMP | 
 | )''' | 
 |  | 
 | decodetab = { | 
 |         'uuencode':             uudecode_pipe, | 
 |         'x-uuencode':           uudecode_pipe, | 
 |         'uue':                  uudecode_pipe, | 
 |         'x-uue':                uudecode_pipe, | 
 |         'quoted-printable':     'mmencode -u -q', | 
 |         'base64':               'mmencode -u -b', | 
 | } | 
 |  | 
 | encodetab = { | 
 |         'x-uuencode':           'uuencode tempfile', | 
 |         'uuencode':             'uuencode tempfile', | 
 |         'x-uue':                'uuencode tempfile', | 
 |         'uue':                  'uuencode tempfile', | 
 |         'quoted-printable':     'mmencode -q', | 
 |         'base64':               'mmencode -b', | 
 | } | 
 |  | 
 | def pipeto(input, command): | 
 |     pipe = os.popen(command, 'w') | 
 |     copyliteral(input, pipe) | 
 |     pipe.close() | 
 |  | 
 | def pipethrough(input, command, output): | 
 |     tempname = tempfile.mktemp() | 
 |     try: | 
 |         temp = open(tempname, 'w') | 
 |     except IOError: | 
 |         print '*** Cannot create temp file', `tempname` | 
 |         return | 
 |     copyliteral(input, temp) | 
 |     temp.close() | 
 |     pipe = os.popen(command + ' <' + tempname, 'r') | 
 |     copybinary(pipe, output) | 
 |     pipe.close() | 
 |     os.unlink(tempname) | 
 |  | 
 | def copyliteral(input, output): | 
 |     while 1: | 
 |         line = input.readline() | 
 |         if not line: break | 
 |         output.write(line) | 
 |  | 
 | def copybinary(input, output): | 
 |     BUFSIZE = 8192 | 
 |     while 1: | 
 |         line = input.read(BUFSIZE) | 
 |         if not line: break | 
 |         output.write(line) |