Results of a rewrite pass
[python/dscho.git] / Lib / mimetools.py
blobf1e20d444f41868deb9fb059ced0273e8e3ce7f5
1 """Various tools used by MIME-reading or MIME-writing programs."""
4 import os
5 import rfc822
6 import tempfile
8 __all__ = ["Message","choose_boundary","encode","decode","copyliteral",
9 "copybinary"]
11 class Message(rfc822.Message):
12 """A derived class of rfc822.Message that knows about MIME headers and
13 contains some hooks for decoding encoded and multipart messages."""
15 def __init__(self, fp, seekable = 1):
16 rfc822.Message.__init__(self, fp, seekable)
17 self.encodingheader = \
18 self.getheader('content-transfer-encoding')
19 self.typeheader = \
20 self.getheader('content-type')
21 self.parsetype()
22 self.parseplist()
24 def parsetype(self):
25 str = self.typeheader
26 if str is None:
27 str = 'text/plain'
28 if ';' in str:
29 i = str.index(';')
30 self.plisttext = str[i:]
31 str = str[:i]
32 else:
33 self.plisttext = ''
34 fields = str.split('/')
35 for i in range(len(fields)):
36 fields[i] = fields[i].strip().lower()
37 self.type = '/'.join(fields)
38 self.maintype = fields[0]
39 self.subtype = '/'.join(fields[1:])
41 def parseplist(self):
42 str = self.plisttext
43 self.plist = []
44 while str[:1] == ';':
45 str = str[1:]
46 if ';' in str:
47 # XXX Should parse quotes!
48 end = str.index(';')
49 else:
50 end = len(str)
51 f = str[:end]
52 if '=' in f:
53 i = f.index('=')
54 f = f[:i].strip().lower() + \
55 '=' + f[i+1:].strip()
56 self.plist.append(f.strip())
57 str = str[end:]
59 def getplist(self):
60 return self.plist
62 def getparam(self, name):
63 name = name.lower() + '='
64 n = len(name)
65 for p in self.plist:
66 if p[:n] == name:
67 return rfc822.unquote(p[n:])
68 return None
70 def getparamnames(self):
71 result = []
72 for p in self.plist:
73 i = p.find('=')
74 if i >= 0:
75 result.append(p[:i].lower())
76 return result
78 def getencoding(self):
79 if self.encodingheader is None:
80 return '7bit'
81 return self.encodingheader.lower()
83 def gettype(self):
84 return self.type
86 def getmaintype(self):
87 return self.maintype
89 def getsubtype(self):
90 return self.subtype
95 # Utility functions
96 # -----------------
99 _prefix = None
101 def choose_boundary():
102 """Return a random string usable as a multipart boundary.
103 The method used is so that it is *very* unlikely that the same
104 string of characters will every occur again in the Universe,
105 so the caller needn't check the data it is packing for the
106 occurrence of the boundary.
108 The boundary contains dots so you have to quote it in the header."""
110 global _prefix
111 import time
112 import random
113 if _prefix is None:
114 import socket
115 hostid = socket.gethostbyname(socket.gethostname())
116 try:
117 uid = `os.getuid()`
118 except AttributeError:
119 uid = '1'
120 try:
121 pid = `os.getpid()`
122 except AttributeError:
123 pid = '1'
124 _prefix = hostid + '.' + uid + '.' + pid
125 timestamp = '%.3f' % time.time()
126 seed = `random.randint(0, 32767)`
127 return _prefix + '.' + timestamp + '.' + seed
130 # Subroutines for decoding some common content-transfer-types
132 def decode(input, output, encoding):
133 """Decode common content-transfer-encodings (base64, quopri, uuencode)."""
134 if encoding == 'base64':
135 import base64
136 return base64.decode(input, output)
137 if encoding == 'quoted-printable':
138 import quopri
139 return quopri.decode(input, output)
140 if encoding in ('uuencode', 'x-uuencode', 'uue', 'x-uue'):
141 import uu
142 return uu.decode(input, output)
143 if encoding in ('7bit', '8bit'):
144 return output.write(input.read())
145 if encoding in decodetab:
146 pipethrough(input, decodetab[encoding], output)
147 else:
148 raise ValueError, \
149 'unknown Content-Transfer-Encoding: %s' % encoding
151 def encode(input, output, encoding):
152 """Encode common content-transfer-encodings (base64, quopri, uuencode)."""
153 if encoding == 'base64':
154 import base64
155 return base64.encode(input, output)
156 if encoding == 'quoted-printable':
157 import quopri
158 return quopri.encode(input, output, 0)
159 if encoding in ('uuencode', 'x-uuencode', 'uue', 'x-uue'):
160 import uu
161 return uu.encode(input, output)
162 if encoding in ('7bit', '8bit'):
163 return output.write(input.read())
164 if encoding in encodetab:
165 pipethrough(input, encodetab[encoding], output)
166 else:
167 raise ValueError, \
168 'unknown Content-Transfer-Encoding: %s' % encoding
170 # The following is no longer used for standard encodings
172 # XXX This requires that uudecode and mmencode are in $PATH
174 uudecode_pipe = '''(
175 TEMP=/tmp/@uu.$$
176 sed "s%^begin [0-7][0-7]* .*%begin 600 $TEMP%" | uudecode
177 cat $TEMP
178 rm $TEMP
179 )'''
181 decodetab = {
182 'uuencode': uudecode_pipe,
183 'x-uuencode': uudecode_pipe,
184 'uue': uudecode_pipe,
185 'x-uue': uudecode_pipe,
186 'quoted-printable': 'mmencode -u -q',
187 'base64': 'mmencode -u -b',
190 encodetab = {
191 'x-uuencode': 'uuencode tempfile',
192 'uuencode': 'uuencode tempfile',
193 'x-uue': 'uuencode tempfile',
194 'uue': 'uuencode tempfile',
195 'quoted-printable': 'mmencode -q',
196 'base64': 'mmencode -b',
199 def pipeto(input, command):
200 pipe = os.popen(command, 'w')
201 copyliteral(input, pipe)
202 pipe.close()
204 def pipethrough(input, command, output):
205 (fd, tempname) = tempfile.mkstemp()
206 temp = os.fdopen(fd, 'w')
207 copyliteral(input, temp)
208 temp.close()
209 pipe = os.popen(command + ' <' + tempname, 'r')
210 copybinary(pipe, output)
211 pipe.close()
212 os.unlink(tempname)
214 def copyliteral(input, output):
215 while 1:
216 line = input.readline()
217 if not line: break
218 output.write(line)
220 def copybinary(input, output):
221 BUFSIZE = 8192
222 while 1:
223 line = input.read(BUFSIZE)
224 if not line: break
225 output.write(line)