| 
									
										
										
										
											2000-02-04 15:10:34 +00:00
										 |  |  | """Various tools used by MIME-reading or MIME-writing programs.""" | 
					
						
							| 
									
										
										
										
											1992-07-13 14:28:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1994-08-01 11:34:53 +00:00
										 |  |  | import os | 
					
						
							| 
									
										
										
										
											1992-07-13 14:28:59 +00:00
										 |  |  | import rfc822 | 
					
						
							| 
									
										
										
										
											1994-08-01 11:34:53 +00:00
										 |  |  | import tempfile | 
					
						
							| 
									
										
										
										
											1992-07-13 14:28:59 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2001-01-25 15:29:22 +00:00
										 |  |  | __all__ = ["Message","choose_boundary","encode","decode","copyliteral", | 
					
						
							|  |  |  |            "copybinary"] | 
					
						
							| 
									
										
										
										
											1992-07-13 14:28:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | class Message(rfc822.Message): | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     """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 | 
					
						
							| 
									
										
										
										
											1992-07-13 14:28:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Utility functions | 
					
						
							|  |  |  | # ----------------- | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2003-06-15 22:05:58 +00:00
										 |  |  | try: | 
					
						
							|  |  |  |     import thread | 
					
						
							|  |  |  | except ImportError: | 
					
						
							|  |  |  |     import dummy_thread as thread | 
					
						
							|  |  |  | _counter_lock = thread.allocate_lock() | 
					
						
							|  |  |  | del thread | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | _counter = 0 | 
					
						
							|  |  |  | def _get_next_counter(): | 
					
						
							|  |  |  |     global _counter | 
					
						
							|  |  |  |     _counter_lock.acquire() | 
					
						
							|  |  |  |     _counter += 1 | 
					
						
							|  |  |  |     result = _counter | 
					
						
							|  |  |  |     _counter_lock.release() | 
					
						
							|  |  |  |     return result | 
					
						
							| 
									
										
										
										
											1992-07-13 14:28:59 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | _prefix = None | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def choose_boundary(): | 
					
						
							| 
									
										
										
										
											2003-06-15 22:05:58 +00:00
										 |  |  |     """Return a string usable as a multipart boundary.
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     The string chosen is unique within a single program run, and | 
					
						
							|  |  |  |     incorporates the user id (if available), process id (if available), | 
					
						
							|  |  |  |     and current time.  So it's very unlikely the returned string appears | 
					
						
							|  |  |  |     in message text, but there's no guarantee. | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  |     The boundary contains dots so you have to quote it in the header."""
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     global _prefix | 
					
						
							|  |  |  |     import time | 
					
						
							|  |  |  |     if _prefix is None: | 
					
						
							|  |  |  |         import socket | 
					
						
							| 
									
										
										
										
											2006-03-31 17:18:06 +00:00
										 |  |  |         try: | 
					
						
							|  |  |  |             hostid = socket.gethostbyname(socket.gethostname()) | 
					
						
							|  |  |  |         except socket.gaierror: | 
					
						
							|  |  |  |             hostid = '127.0.0.1' | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         try: | 
					
						
							| 
									
										
										
										
											2004-02-12 17:35:32 +00:00
										 |  |  |             uid = repr(os.getuid()) | 
					
						
							| 
									
										
										
										
											2002-03-23 05:58:52 +00:00
										 |  |  |         except AttributeError: | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             uid = '1' | 
					
						
							|  |  |  |         try: | 
					
						
							| 
									
										
										
										
											2004-02-12 17:35:32 +00:00
										 |  |  |             pid = repr(os.getpid()) | 
					
						
							| 
									
										
										
										
											2002-03-23 05:58:52 +00:00
										 |  |  |         except AttributeError: | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |             pid = '1' | 
					
						
							|  |  |  |         _prefix = hostid + '.' + uid + '.' + pid | 
					
						
							| 
									
										
										
										
											2003-06-15 22:05:58 +00:00
										 |  |  |     return "%s.%.3f.%d" % (_prefix, time.time(), _get_next_counter()) | 
					
						
							| 
									
										
										
										
											1994-08-01 11:34:53 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # Subroutines for decoding some common content-transfer-types | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def decode(input, output, encoding): | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     """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()) | 
					
						
							| 
									
										
										
										
											2002-06-01 14:18:47 +00:00
										 |  |  |     if encoding in decodetab: | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         pipethrough(input, decodetab[encoding], output) | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         raise ValueError, \ | 
					
						
							|  |  |  |               'unknown Content-Transfer-Encoding: %s' % encoding | 
					
						
							| 
									
										
										
										
											1994-08-01 11:34:53 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def encode(input, output, encoding): | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     """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()) | 
					
						
							| 
									
										
										
										
											2002-06-01 14:18:47 +00:00
										 |  |  |     if encoding in encodetab: | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         pipethrough(input, encodetab[encoding], output) | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         raise ValueError, \ | 
					
						
							|  |  |  |               'unknown Content-Transfer-Encoding: %s' % encoding | 
					
						
							| 
									
										
										
										
											1994-08-01 11:34:53 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											1997-07-11 16:33:26 +00:00
										 |  |  | # The following is no longer used for standard encodings | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # XXX This requires that uudecode and mmencode are in $PATH | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											1994-08-01 11:34:53 +00:00
										 |  |  | uudecode_pipe = '''(
 | 
					
						
							|  |  |  | TEMP=/tmp/@uu.$$ | 
					
						
							|  |  |  | sed "s%^begin [0-7][0-7]* .*%begin 600 $TEMP%" | uudecode | 
					
						
							|  |  |  | cat $TEMP | 
					
						
							|  |  |  | rm $TEMP | 
					
						
							|  |  |  | )'''
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | decodetab = { | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         'uuencode':             uudecode_pipe, | 
					
						
							|  |  |  |         'x-uuencode':           uudecode_pipe, | 
					
						
							|  |  |  |         'uue':                  uudecode_pipe, | 
					
						
							|  |  |  |         'x-uue':                uudecode_pipe, | 
					
						
							|  |  |  |         'quoted-printable':     'mmencode -u -q', | 
					
						
							|  |  |  |         'base64':               'mmencode -u -b', | 
					
						
							| 
									
										
										
										
											1994-08-01 11:34:53 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | encodetab = { | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |         'x-uuencode':           'uuencode tempfile', | 
					
						
							|  |  |  |         'uuencode':             'uuencode tempfile', | 
					
						
							|  |  |  |         'x-uue':                'uuencode tempfile', | 
					
						
							|  |  |  |         'uue':                  'uuencode tempfile', | 
					
						
							|  |  |  |         'quoted-printable':     'mmencode -q', | 
					
						
							|  |  |  |         'base64':               'mmencode -b', | 
					
						
							| 
									
										
										
										
											1994-08-01 11:34:53 +00:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def pipeto(input, command): | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     pipe = os.popen(command, 'w') | 
					
						
							|  |  |  |     copyliteral(input, pipe) | 
					
						
							|  |  |  |     pipe.close() | 
					
						
							| 
									
										
										
										
											1994-08-01 11:34:53 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def pipethrough(input, command, output): | 
					
						
							| 
									
										
										
										
											2002-08-09 16:38:32 +00:00
										 |  |  |     (fd, tempname) = tempfile.mkstemp() | 
					
						
							|  |  |  |     temp = os.fdopen(fd, 'w') | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     copyliteral(input, temp) | 
					
						
							|  |  |  |     temp.close() | 
					
						
							|  |  |  |     pipe = os.popen(command + ' <' + tempname, 'r') | 
					
						
							|  |  |  |     copybinary(pipe, output) | 
					
						
							|  |  |  |     pipe.close() | 
					
						
							|  |  |  |     os.unlink(tempname) | 
					
						
							| 
									
										
										
										
											1994-08-01 11:34:53 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def copyliteral(input, output): | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     while 1: | 
					
						
							|  |  |  |         line = input.readline() | 
					
						
							|  |  |  |         if not line: break | 
					
						
							|  |  |  |         output.write(line) | 
					
						
							| 
									
										
										
										
											1994-08-01 11:34:53 +00:00
										 |  |  | 
 | 
					
						
							|  |  |  | def copybinary(input, output): | 
					
						
							| 
									
										
										
										
											2001-01-14 23:47:14 +00:00
										 |  |  |     BUFSIZE = 8192 | 
					
						
							|  |  |  |     while 1: | 
					
						
							|  |  |  |         line = input.read(BUFSIZE) | 
					
						
							|  |  |  |         if not line: break | 
					
						
							|  |  |  |         output.write(line) |