blob: bb8a29934e7141b9eceb92ae62b7998504461803 [file] [log] [blame]
Guido van Rossum54f22ed2000-02-04 15:10:34 +00001"""Various tools used by MIME-reading or MIME-writing programs."""
Guido van Rossum01ca3361992-07-13 14:28:59 +00002
3
Guido van Rossumb6775db1994-08-01 11:34:53 +00004import os
Guido van Rossum01ca3361992-07-13 14:28:59 +00005import rfc822
Guido van Rossumb6775db1994-08-01 11:34:53 +00006import tempfile
Guido van Rossum01ca3361992-07-13 14:28:59 +00007
8
Guido van Rossum01ca3361992-07-13 14:28:59 +00009class Message(rfc822.Message):
Tim Peters07e99cb2001-01-14 23:47:14 +000010 """A derived class of rfc822.Message that knows about MIME headers and
11 contains some hooks for decoding encoded and multipart messages."""
Guido van Rossum01ca3361992-07-13 14:28:59 +000012
Tim Peters07e99cb2001-01-14 23:47:14 +000013 def __init__(self, fp, seekable = 1):
14 rfc822.Message.__init__(self, fp, seekable)
15 self.encodingheader = \
16 self.getheader('content-transfer-encoding')
17 self.typeheader = \
18 self.getheader('content-type')
19 self.parsetype()
20 self.parseplist()
Guido van Rossum01ca3361992-07-13 14:28:59 +000021
Tim Peters07e99cb2001-01-14 23:47:14 +000022 def parsetype(self):
23 str = self.typeheader
24 if str is None:
25 str = 'text/plain'
26 if ';' in str:
27 i = str.index(';')
28 self.plisttext = str[i:]
29 str = str[:i]
30 else:
31 self.plisttext = ''
32 fields = str.split('/')
33 for i in range(len(fields)):
34 fields[i] = fields[i].strip().lower()
35 self.type = '/'.join(fields)
36 self.maintype = fields[0]
37 self.subtype = '/'.join(fields[1:])
Guido van Rossum01ca3361992-07-13 14:28:59 +000038
Tim Peters07e99cb2001-01-14 23:47:14 +000039 def parseplist(self):
40 str = self.plisttext
41 self.plist = []
42 while str[:1] == ';':
43 str = str[1:]
44 if ';' in str:
45 # XXX Should parse quotes!
46 end = str.index(';')
47 else:
48 end = len(str)
49 f = str[:end]
50 if '=' in f:
51 i = f.index('=')
52 f = f[:i].strip().lower() + \
53 '=' + f[i+1:].strip()
54 self.plist.append(f.strip())
55 str = str[end:]
Guido van Rossum01ca3361992-07-13 14:28:59 +000056
Tim Peters07e99cb2001-01-14 23:47:14 +000057 def getplist(self):
58 return self.plist
Guido van Rossum01ca3361992-07-13 14:28:59 +000059
Tim Peters07e99cb2001-01-14 23:47:14 +000060 def getparam(self, name):
61 name = name.lower() + '='
62 n = len(name)
63 for p in self.plist:
64 if p[:n] == name:
65 return rfc822.unquote(p[n:])
66 return None
Guido van Rossum01ca3361992-07-13 14:28:59 +000067
Tim Peters07e99cb2001-01-14 23:47:14 +000068 def getparamnames(self):
69 result = []
70 for p in self.plist:
71 i = p.find('=')
72 if i >= 0:
73 result.append(p[:i].lower())
74 return result
Guido van Rossum4be63d11996-10-04 20:14:02 +000075
Tim Peters07e99cb2001-01-14 23:47:14 +000076 def getencoding(self):
77 if self.encodingheader is None:
78 return '7bit'
79 return self.encodingheader.lower()
Guido van Rossum01ca3361992-07-13 14:28:59 +000080
Tim Peters07e99cb2001-01-14 23:47:14 +000081 def gettype(self):
82 return self.type
Guido van Rossum01ca3361992-07-13 14:28:59 +000083
Tim Peters07e99cb2001-01-14 23:47:14 +000084 def getmaintype(self):
85 return self.maintype
Guido van Rossum01ca3361992-07-13 14:28:59 +000086
Tim Peters07e99cb2001-01-14 23:47:14 +000087 def getsubtype(self):
88 return self.subtype
Guido van Rossum01ca3361992-07-13 14:28:59 +000089
90
91
92
93# Utility functions
94# -----------------
95
96
Guido van Rossum01ca3361992-07-13 14:28:59 +000097_prefix = None
98
99def choose_boundary():
Tim Peters07e99cb2001-01-14 23:47:14 +0000100 """Return a random string usable as a multipart boundary.
101 The method used is so that it is *very* unlikely that the same
102 string of characters will every occur again in the Universe,
103 so the caller needn't check the data it is packing for the
104 occurrence of the boundary.
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000105
Tim Peters07e99cb2001-01-14 23:47:14 +0000106 The boundary contains dots so you have to quote it in the header."""
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000107
Tim Peters07e99cb2001-01-14 23:47:14 +0000108 global _prefix
109 import time
110 import random
111 if _prefix is None:
112 import socket
113 import os
114 hostid = socket.gethostbyname(socket.gethostname())
115 try:
116 uid = `os.getuid()`
117 except:
118 uid = '1'
119 try:
120 pid = `os.getpid()`
121 except:
122 pid = '1'
123 _prefix = hostid + '.' + uid + '.' + pid
124 timestamp = '%.3f' % time.time()
125 seed = `random.randint(0, 32767)`
126 return _prefix + '.' + timestamp + '.' + seed
Guido van Rossumb6775db1994-08-01 11:34:53 +0000127
128
129# Subroutines for decoding some common content-transfer-types
130
Guido van Rossumb6775db1994-08-01 11:34:53 +0000131def decode(input, output, encoding):
Tim Peters07e99cb2001-01-14 23:47:14 +0000132 """Decode common content-transfer-encodings (base64, quopri, uuencode)."""
133 if encoding == 'base64':
134 import base64
135 return base64.decode(input, output)
136 if encoding == 'quoted-printable':
137 import quopri
138 return quopri.decode(input, output)
139 if encoding in ('uuencode', 'x-uuencode', 'uue', 'x-uue'):
140 import uu
141 return uu.decode(input, output)
142 if encoding in ('7bit', '8bit'):
143 return output.write(input.read())
144 if decodetab.has_key(encoding):
145 pipethrough(input, decodetab[encoding], output)
146 else:
147 raise ValueError, \
148 'unknown Content-Transfer-Encoding: %s' % encoding
Guido van Rossumb6775db1994-08-01 11:34:53 +0000149
150def encode(input, output, encoding):
Tim Peters07e99cb2001-01-14 23:47:14 +0000151 """Encode common content-transfer-encodings (base64, quopri, uuencode)."""
152 if encoding == 'base64':
153 import base64
154 return base64.encode(input, output)
155 if encoding == 'quoted-printable':
156 import quopri
157 return quopri.encode(input, output, 0)
158 if encoding in ('uuencode', 'x-uuencode', 'uue', 'x-uue'):
159 import uu
160 return uu.encode(input, output)
161 if encoding in ('7bit', '8bit'):
162 return output.write(input.read())
163 if encodetab.has_key(encoding):
164 pipethrough(input, encodetab[encoding], output)
165 else:
166 raise ValueError, \
167 'unknown Content-Transfer-Encoding: %s' % encoding
Guido van Rossumb6775db1994-08-01 11:34:53 +0000168
Guido van Rossume3cd1511997-07-11 16:33:26 +0000169# The following is no longer used for standard encodings
170
171# XXX This requires that uudecode and mmencode are in $PATH
172
Guido van Rossumb6775db1994-08-01 11:34:53 +0000173uudecode_pipe = '''(
174TEMP=/tmp/@uu.$$
175sed "s%^begin [0-7][0-7]* .*%begin 600 $TEMP%" | uudecode
176cat $TEMP
177rm $TEMP
178)'''
179
180decodetab = {
Tim Peters07e99cb2001-01-14 23:47:14 +0000181 'uuencode': uudecode_pipe,
182 'x-uuencode': uudecode_pipe,
183 'uue': uudecode_pipe,
184 'x-uue': uudecode_pipe,
185 'quoted-printable': 'mmencode -u -q',
186 'base64': 'mmencode -u -b',
Guido van Rossumb6775db1994-08-01 11:34:53 +0000187}
188
189encodetab = {
Tim Peters07e99cb2001-01-14 23:47:14 +0000190 'x-uuencode': 'uuencode tempfile',
191 'uuencode': 'uuencode tempfile',
192 'x-uue': 'uuencode tempfile',
193 'uue': 'uuencode tempfile',
194 'quoted-printable': 'mmencode -q',
195 'base64': 'mmencode -b',
Guido van Rossumb6775db1994-08-01 11:34:53 +0000196}
197
198def pipeto(input, command):
Tim Peters07e99cb2001-01-14 23:47:14 +0000199 pipe = os.popen(command, 'w')
200 copyliteral(input, pipe)
201 pipe.close()
Guido van Rossumb6775db1994-08-01 11:34:53 +0000202
203def pipethrough(input, command, output):
Tim Peters07e99cb2001-01-14 23:47:14 +0000204 tempname = tempfile.mktemp()
205 try:
206 temp = open(tempname, 'w')
207 except IOError:
208 print '*** Cannot create temp file', `tempname`
209 return
210 copyliteral(input, temp)
211 temp.close()
212 pipe = os.popen(command + ' <' + tempname, 'r')
213 copybinary(pipe, output)
214 pipe.close()
215 os.unlink(tempname)
Guido van Rossumb6775db1994-08-01 11:34:53 +0000216
217def copyliteral(input, output):
Tim Peters07e99cb2001-01-14 23:47:14 +0000218 while 1:
219 line = input.readline()
220 if not line: break
221 output.write(line)
Guido van Rossumb6775db1994-08-01 11:34:53 +0000222
223def copybinary(input, output):
Tim Peters07e99cb2001-01-14 23:47:14 +0000224 BUFSIZE = 8192
225 while 1:
226 line = input.read(BUFSIZE)
227 if not line: break
228 output.write(line)