Quick update to the README file. For intros and books we now point to
[python/dscho.git] / Lib / mimetools.py
blobb519c54cdbe075ee28225804223514fd2eec8ee7
1 """Various tools used by MIME-reading or MIME-writing programs."""
4 import os
5 import rfc822
6 import string
7 import tempfile
10 class Message(rfc822.Message):
11 """A derived class of rfc822.Message that knows about MIME headers and
12 contains some hooks for decoding encoded and multipart messages."""
14 def __init__(self, fp, seekable = 1):
15 rfc822.Message.__init__(self, fp, seekable)
16 self.encodingheader = \
17 self.getheader('content-transfer-encoding')
18 self.typeheader = \
19 self.getheader('content-type')
20 self.parsetype()
21 self.parseplist()
23 def parsetype(self):
24 str = self.typeheader
25 if str == None:
26 str = 'text/plain'
27 if ';' in str:
28 i = string.index(str, ';')
29 self.plisttext = str[i:]
30 str = str[:i]
31 else:
32 self.plisttext = ''
33 fields = string.splitfields(str, '/')
34 for i in range(len(fields)):
35 fields[i] = string.lower(string.strip(fields[i]))
36 self.type = string.joinfields(fields, '/')
37 self.maintype = fields[0]
38 self.subtype = string.joinfields(fields[1:], '/')
40 def parseplist(self):
41 str = self.plisttext
42 self.plist = []
43 while str[:1] == ';':
44 str = str[1:]
45 if ';' in str:
46 # XXX Should parse quotes!
47 end = string.index(str, ';')
48 else:
49 end = len(str)
50 f = str[:end]
51 if '=' in f:
52 i = string.index(f, '=')
53 f = string.lower(string.strip(f[:i])) + \
54 '=' + string.strip(f[i+1:])
55 self.plist.append(string.strip(f))
56 str = str[end:]
58 def getplist(self):
59 return self.plist
61 def getparam(self, name):
62 name = string.lower(name) + '='
63 n = len(name)
64 for p in self.plist:
65 if p[:n] == name:
66 return rfc822.unquote(p[n:])
67 return None
69 def getparamnames(self):
70 result = []
71 for p in self.plist:
72 i = string.find(p, '=')
73 if i >= 0:
74 result.append(string.lower(p[:i]))
75 return result
77 def getencoding(self):
78 if self.encodingheader == None:
79 return '7bit'
80 return string.lower(self.encodingheader)
82 def gettype(self):
83 return self.type
85 def getmaintype(self):
86 return self.maintype
88 def getsubtype(self):
89 return self.subtype
94 # Utility functions
95 # -----------------
98 _prefix = None
100 def choose_boundary():
101 """Return a random string usable as a multipart boundary.
102 The method used is so that it is *very* unlikely that the same
103 string of characters will every occur again in the Universe,
104 so the caller needn't check the data it is packing for the
105 occurrence of the boundary.
107 The boundary contains dots so you have to quote it in the header."""
109 global _prefix
110 import time
111 import random
112 if _prefix == None:
113 import socket
114 import os
115 hostid = socket.gethostbyname(socket.gethostname())
116 try:
117 uid = `os.getuid()`
118 except:
119 uid = '1'
120 try:
121 pid = `os.getpid()`
122 except:
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 decodetab.has_key(encoding):
144 pipethrough(input, decodetab[encoding], output)
145 else:
146 raise ValueError, \
147 'unknown Content-Transfer-Encoding: %s' % encoding
149 def encode(input, output, encoding):
150 """Encode common content-transfer-encodings (base64, quopri, uuencode)."""
151 if encoding == 'base64':
152 import base64
153 return base64.encode(input, output)
154 if encoding == 'quoted-printable':
155 import quopri
156 return quopri.encode(input, output, 0)
157 if encoding in ('uuencode', 'x-uuencode', 'uue', 'x-uue'):
158 import uu
159 return uu.encode(input, output)
160 if encodetab.has_key(encoding):
161 pipethrough(input, encodetab[encoding], output)
162 else:
163 raise ValueError, \
164 'unknown Content-Transfer-Encoding: %s' % encoding
166 # The following is no longer used for standard encodings
168 # XXX This requires that uudecode and mmencode are in $PATH
170 uudecode_pipe = '''(
171 TEMP=/tmp/@uu.$$
172 sed "s%^begin [0-7][0-7]* .*%begin 600 $TEMP%" | uudecode
173 cat $TEMP
174 rm $TEMP
175 )'''
177 decodetab = {
178 'uuencode': uudecode_pipe,
179 'x-uuencode': uudecode_pipe,
180 'uue': uudecode_pipe,
181 'x-uue': uudecode_pipe,
182 'quoted-printable': 'mmencode -u -q',
183 'base64': 'mmencode -u -b',
186 encodetab = {
187 'x-uuencode': 'uuencode tempfile',
188 'uuencode': 'uuencode tempfile',
189 'x-uue': 'uuencode tempfile',
190 'uue': 'uuencode tempfile',
191 'quoted-printable': 'mmencode -q',
192 'base64': 'mmencode -b',
195 def pipeto(input, command):
196 pipe = os.popen(command, 'w')
197 copyliteral(input, pipe)
198 pipe.close()
200 def pipethrough(input, command, output):
201 tempname = tempfile.mktemp()
202 try:
203 temp = open(tempname, 'w')
204 except IOError:
205 print '*** Cannot create temp file', `tempname`
206 return
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)