Catch situations where currentframe() returns None. See SF patch #1447410, this is...
[python.git] / Lib / mimetools.py
blob0b698ac679260b6c1140a175cc828776fea7b76c
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 # -----------------
98 try:
99 import thread
100 except ImportError:
101 import dummy_thread as thread
102 _counter_lock = thread.allocate_lock()
103 del thread
105 _counter = 0
106 def _get_next_counter():
107 global _counter
108 _counter_lock.acquire()
109 _counter += 1
110 result = _counter
111 _counter_lock.release()
112 return result
114 _prefix = None
116 def choose_boundary():
117 """Return a string usable as a multipart boundary.
119 The string chosen is unique within a single program run, and
120 incorporates the user id (if available), process id (if available),
121 and current time. So it's very unlikely the returned string appears
122 in message text, but there's no guarantee.
124 The boundary contains dots so you have to quote it in the header."""
126 global _prefix
127 import time
128 if _prefix is None:
129 import socket
130 hostid = socket.gethostbyname(socket.gethostname())
131 try:
132 uid = repr(os.getuid())
133 except AttributeError:
134 uid = '1'
135 try:
136 pid = repr(os.getpid())
137 except AttributeError:
138 pid = '1'
139 _prefix = hostid + '.' + uid + '.' + pid
140 return "%s.%.3f.%d" % (_prefix, time.time(), _get_next_counter())
143 # Subroutines for decoding some common content-transfer-types
145 def decode(input, output, encoding):
146 """Decode common content-transfer-encodings (base64, quopri, uuencode)."""
147 if encoding == 'base64':
148 import base64
149 return base64.decode(input, output)
150 if encoding == 'quoted-printable':
151 import quopri
152 return quopri.decode(input, output)
153 if encoding in ('uuencode', 'x-uuencode', 'uue', 'x-uue'):
154 import uu
155 return uu.decode(input, output)
156 if encoding in ('7bit', '8bit'):
157 return output.write(input.read())
158 if encoding in decodetab:
159 pipethrough(input, decodetab[encoding], output)
160 else:
161 raise ValueError, \
162 'unknown Content-Transfer-Encoding: %s' % encoding
164 def encode(input, output, encoding):
165 """Encode common content-transfer-encodings (base64, quopri, uuencode)."""
166 if encoding == 'base64':
167 import base64
168 return base64.encode(input, output)
169 if encoding == 'quoted-printable':
170 import quopri
171 return quopri.encode(input, output, 0)
172 if encoding in ('uuencode', 'x-uuencode', 'uue', 'x-uue'):
173 import uu
174 return uu.encode(input, output)
175 if encoding in ('7bit', '8bit'):
176 return output.write(input.read())
177 if encoding in encodetab:
178 pipethrough(input, encodetab[encoding], output)
179 else:
180 raise ValueError, \
181 'unknown Content-Transfer-Encoding: %s' % encoding
183 # The following is no longer used for standard encodings
185 # XXX This requires that uudecode and mmencode are in $PATH
187 uudecode_pipe = '''(
188 TEMP=/tmp/@uu.$$
189 sed "s%^begin [0-7][0-7]* .*%begin 600 $TEMP%" | uudecode
190 cat $TEMP
191 rm $TEMP
192 )'''
194 decodetab = {
195 'uuencode': uudecode_pipe,
196 'x-uuencode': uudecode_pipe,
197 'uue': uudecode_pipe,
198 'x-uue': uudecode_pipe,
199 'quoted-printable': 'mmencode -u -q',
200 'base64': 'mmencode -u -b',
203 encodetab = {
204 'x-uuencode': 'uuencode tempfile',
205 'uuencode': 'uuencode tempfile',
206 'x-uue': 'uuencode tempfile',
207 'uue': 'uuencode tempfile',
208 'quoted-printable': 'mmencode -q',
209 'base64': 'mmencode -b',
212 def pipeto(input, command):
213 pipe = os.popen(command, 'w')
214 copyliteral(input, pipe)
215 pipe.close()
217 def pipethrough(input, command, output):
218 (fd, tempname) = tempfile.mkstemp()
219 temp = os.fdopen(fd, 'w')
220 copyliteral(input, temp)
221 temp.close()
222 pipe = os.popen(command + ' <' + tempname, 'r')
223 copybinary(pipe, output)
224 pipe.close()
225 os.unlink(tempname)
227 def copyliteral(input, output):
228 while 1:
229 line = input.readline()
230 if not line: break
231 output.write(line)
233 def copybinary(input, output):
234 BUFSIZE = 8192
235 while 1:
236 line = input.read(BUFSIZE)
237 if not line: break
238 output.write(line)