blob: 5489aa090c4c6c77ca10e438bd236a1a63a3deab [file] [log] [blame]
Guido van Rossum01ca3361992-07-13 14:28:59 +00001# Various tools used by MIME-reading or MIME-writing programs.
2
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 string
7import tempfile
Guido van Rossum01ca3361992-07-13 14:28:59 +00008
9
10# A derived class of rfc822.Message that knows about MIME headers and
11# contains some hooks for decoding encoded and multipart messages.
12
13class Message(rfc822.Message):
14
Guido van Rossum87555821995-08-07 20:13:56 +000015 def __init__(self, fp, seekable = 1):
16 rfc822.Message.__init__(self, fp, seekable)
Guido van Rossum01ca3361992-07-13 14:28:59 +000017 self.encodingheader = \
Guido van Rossum43245361995-08-29 19:25:11 +000018 self.getheader('content-transfer-encoding')
Guido van Rossum01ca3361992-07-13 14:28:59 +000019 self.typeheader = \
20 self.getheader('content-type')
21 self.parsetype()
22 self.parseplist()
Guido van Rossum01ca3361992-07-13 14:28:59 +000023
24 def parsetype(self):
25 str = self.typeheader
26 if str == None:
27 str = 'text/plain'
28 if ';' in str:
29 i = string.index(str, ';')
30 self.plisttext = str[i:]
31 str = str[:i]
32 else:
33 self.plisttext = ''
34 fields = string.splitfields(str, '/')
35 for i in range(len(fields)):
36 fields[i] = string.lower(string.strip(fields[i]))
37 self.type = string.joinfields(fields, '/')
38 self.maintype = fields[0]
39 self.subtype = string.joinfields(fields[1:], '/')
40
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 = string.index(str, ';')
49 else:
50 end = len(str)
51 f = str[:end]
52 if '=' in f:
53 i = string.index(f, '=')
54 f = string.lower(string.strip(f[:i])) + \
55 '=' + string.strip(f[i+1:])
56 self.plist.append(string.strip(f))
Guido van Rossumeacce121996-01-25 18:07:08 +000057 str = str[end:]
Guido van Rossum01ca3361992-07-13 14:28:59 +000058
59 def getplist(self):
60 return self.plist
61
62 def getparam(self, name):
63 name = string.lower(name) + '='
64 n = len(name)
65 for p in self.plist:
66 if p[:n] == name:
67 return rfc822.unquote(p[n:])
68 return None
69
Guido van Rossum4be63d11996-10-04 20:14:02 +000070 def getparamnames(self):
71 result = []
72 for p in self.plist:
73 i = string.find(p, '=')
74 if i >= 0:
75 result.append(string.lower(p[:i]))
76 return result
77
Guido van Rossum01ca3361992-07-13 14:28:59 +000078 def getencoding(self):
79 if self.encodingheader == None:
80 return '7bit'
Guido van Rossumb6775db1994-08-01 11:34:53 +000081 return string.lower(self.encodingheader)
Guido van Rossum01ca3361992-07-13 14:28:59 +000082
83 def gettype(self):
84 return self.type
85
86 def getmaintype(self):
87 return self.maintype
88
89 def getsubtype(self):
90 return self.subtype
91
92
93
94
95# Utility functions
96# -----------------
97
98
99# Return a random string usable as a multipart boundary.
100# The method used is so that it is *very* unlikely that the same
101# string of characters will every occur again in the Universe,
102# so the caller needn't check the data it is packing for the
103# occurrence of the boundary.
104#
105# The boundary contains dots so you have to quote it in the header.
106
107_prefix = None
108
109def choose_boundary():
Guido van Rossum8460b941996-05-28 22:59:58 +0000110 global _prefix
Guido van Rossum01ca3361992-07-13 14:28:59 +0000111 import time
112 import rand
113 if _prefix == None:
114 import socket
115 import os
116 hostid = socket.gethostbyname(socket.gethostname())
Guido van Rossum0c8cf881996-08-26 16:40:20 +0000117 try:
118 uid = `os.getuid()`
119 except:
120 uid = '1'
121 try:
122 pid = `os.getpid()`
123 except:
124 pid = '1'
Guido van Rossum01ca3361992-07-13 14:28:59 +0000125 seed = `rand.rand()`
126 _prefix = hostid + '.' + uid + '.' + pid
Guido van Rossumfea2af11993-01-04 09:16:51 +0000127 timestamp = `int(time.time())`
Guido van Rossum01ca3361992-07-13 14:28:59 +0000128 seed = `rand.rand()`
129 return _prefix + '.' + timestamp + '.' + seed
Guido van Rossumb6775db1994-08-01 11:34:53 +0000130
131
132# Subroutines for decoding some common content-transfer-types
133
Guido van Rossumb6775db1994-08-01 11:34:53 +0000134def decode(input, output, encoding):
Guido van Rossume3cd1511997-07-11 16:33:26 +0000135 if encoding == 'base64':
136 import base64
137 return base64.decode(input, output)
138 if encoding == 'quoted-printable':
139 import quopri
140 return quopri.decode(input, output)
Guido van Rossume44a8d91997-12-10 18:54:36 +0000141 if encoding in ('uuencode', 'x-uuencode', 'uue', 'x-uue'):
Guido van Rossume3cd1511997-07-11 16:33:26 +0000142 import uu
143 return uu.decode(input, output)
Guido van Rossumb6775db1994-08-01 11:34:53 +0000144 if decodetab.has_key(encoding):
145 pipethrough(input, decodetab[encoding], output)
146 else:
147 raise ValueError, \
148 'unknown Content-Transfer-Encoding: %s' % encoding
149
150def encode(input, output, encoding):
Guido van Rossume3cd1511997-07-11 16:33:26 +0000151 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)
Guido van Rossume44a8d91997-12-10 18:54:36 +0000157 if encoding in ('uuencode', 'x-uuencode', 'uue', 'x-uue'):
Guido van Rossume3cd1511997-07-11 16:33:26 +0000158 import uu
159 return uu.encode(input, output)
Guido van Rossumb6775db1994-08-01 11:34:53 +0000160 if encodetab.has_key(encoding):
161 pipethrough(input, encodetab[encoding], output)
162 else:
163 raise ValueError, \
164 'unknown Content-Transfer-Encoding: %s' % encoding
165
Guido van Rossume3cd1511997-07-11 16:33:26 +0000166# The following is no longer used for standard encodings
167
168# XXX This requires that uudecode and mmencode are in $PATH
169
Guido van Rossumb6775db1994-08-01 11:34:53 +0000170uudecode_pipe = '''(
171TEMP=/tmp/@uu.$$
172sed "s%^begin [0-7][0-7]* .*%begin 600 $TEMP%" | uudecode
173cat $TEMP
174rm $TEMP
175)'''
176
177decodetab = {
178 'uuencode': uudecode_pipe,
179 'x-uuencode': uudecode_pipe,
Guido van Rossume44a8d91997-12-10 18:54:36 +0000180 'uue': uudecode_pipe,
181 'x-uue': uudecode_pipe,
Guido van Rossumb6775db1994-08-01 11:34:53 +0000182 'quoted-printable': 'mmencode -u -q',
183 'base64': 'mmencode -u -b',
184}
185
186encodetab = {
187 'x-uuencode': 'uuencode tempfile',
188 'uuencode': 'uuencode tempfile',
Guido van Rossume44a8d91997-12-10 18:54:36 +0000189 'x-uue': 'uuencode tempfile',
190 'uue': 'uuencode tempfile',
Guido van Rossumb6775db1994-08-01 11:34:53 +0000191 'quoted-printable': 'mmencode -q',
192 'base64': 'mmencode -b',
193}
194
195def pipeto(input, command):
196 pipe = os.popen(command, 'w')
197 copyliteral(input, pipe)
198 pipe.close()
199
200def 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)
213
214def copyliteral(input, output):
215 while 1:
216 line = input.readline()
217 if not line: break
218 output.write(line)
219
220def copybinary(input, output):
221 BUFSIZE = 8192
222 while 1:
223 line = input.read(BUFSIZE)
224 if not line: break
225 output.write(line)