blob: 630a1c301b5448d8e2526bdc4fbf3bec198d8814 [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):
Guido van Rossum54f22ed2000-02-04 15:10:34 +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
Guido van Rossum87555821995-08-07 20:13:56 +000013 def __init__(self, fp, seekable = 1):
14 rfc822.Message.__init__(self, fp, seekable)
Guido van Rossum01ca3361992-07-13 14:28:59 +000015 self.encodingheader = \
Guido van Rossum43245361995-08-29 19:25:11 +000016 self.getheader('content-transfer-encoding')
Guido van Rossum01ca3361992-07-13 14:28:59 +000017 self.typeheader = \
18 self.getheader('content-type')
19 self.parsetype()
20 self.parseplist()
Guido van Rossum01ca3361992-07-13 14:28:59 +000021
22 def parsetype(self):
23 str = self.typeheader
Fred Drake8152d322000-12-12 23:20:45 +000024 if str is None:
Guido van Rossum01ca3361992-07-13 14:28:59 +000025 str = 'text/plain'
26 if ';' in str:
Guido van Rossume6e03ea2000-12-15 15:49:08 +000027 i = str.index(';')
Guido van Rossum01ca3361992-07-13 14:28:59 +000028 self.plisttext = str[i:]
29 str = str[:i]
30 else:
31 self.plisttext = ''
Guido van Rossume6e03ea2000-12-15 15:49:08 +000032 fields = str.split('/')
Guido van Rossum01ca3361992-07-13 14:28:59 +000033 for i in range(len(fields)):
Guido van Rossume6e03ea2000-12-15 15:49:08 +000034 fields[i] = fields[i].strip().lower()
35 self.type = '/'.join(fields)
Guido van Rossum01ca3361992-07-13 14:28:59 +000036 self.maintype = fields[0]
Guido van Rossume6e03ea2000-12-15 15:49:08 +000037 self.subtype = '/'.join(fields[1:])
Guido van Rossum01ca3361992-07-13 14:28:59 +000038
39 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!
Guido van Rossume6e03ea2000-12-15 15:49:08 +000046 end = str.index(';')
Guido van Rossum01ca3361992-07-13 14:28:59 +000047 else:
48 end = len(str)
49 f = str[:end]
50 if '=' in f:
Guido van Rossume6e03ea2000-12-15 15:49:08 +000051 i = f.index('=')
52 f = f[:i].strip().lower() + \
53 '=' + f[i+1:].strip()
54 self.plist.append(f.strip())
Guido van Rossumeacce121996-01-25 18:07:08 +000055 str = str[end:]
Guido van Rossum01ca3361992-07-13 14:28:59 +000056
57 def getplist(self):
58 return self.plist
59
60 def getparam(self, name):
Guido van Rossume6e03ea2000-12-15 15:49:08 +000061 name = name.lower() + '='
Guido van Rossum01ca3361992-07-13 14:28:59 +000062 n = len(name)
63 for p in self.plist:
64 if p[:n] == name:
65 return rfc822.unquote(p[n:])
66 return None
67
Guido van Rossum4be63d11996-10-04 20:14:02 +000068 def getparamnames(self):
69 result = []
70 for p in self.plist:
Guido van Rossume6e03ea2000-12-15 15:49:08 +000071 i = p.find('=')
Guido van Rossum4be63d11996-10-04 20:14:02 +000072 if i >= 0:
Guido van Rossume6e03ea2000-12-15 15:49:08 +000073 result.append(p[:i].lower())
Guido van Rossum4be63d11996-10-04 20:14:02 +000074 return result
75
Guido van Rossum01ca3361992-07-13 14:28:59 +000076 def getencoding(self):
Fred Drake8152d322000-12-12 23:20:45 +000077 if self.encodingheader is None:
Guido van Rossum01ca3361992-07-13 14:28:59 +000078 return '7bit'
Guido van Rossume6e03ea2000-12-15 15:49:08 +000079 return self.encodingheader.lower()
Guido van Rossum01ca3361992-07-13 14:28:59 +000080
81 def gettype(self):
82 return self.type
83
84 def getmaintype(self):
85 return self.maintype
86
87 def getsubtype(self):
88 return self.subtype
89
90
91
92
93# Utility functions
94# -----------------
95
96
Guido van Rossum01ca3361992-07-13 14:28:59 +000097_prefix = None
98
99def choose_boundary():
Guido van Rossum54f22ed2000-02-04 15:10:34 +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.
105
106 The boundary contains dots so you have to quote it in the header."""
107
Guido van Rossum8460b941996-05-28 22:59:58 +0000108 global _prefix
Guido van Rossum01ca3361992-07-13 14:28:59 +0000109 import time
Guido van Rossumb26a1b41998-05-20 17:05:52 +0000110 import random
Fred Drake8152d322000-12-12 23:20:45 +0000111 if _prefix is None:
Guido van Rossum01ca3361992-07-13 14:28:59 +0000112 import socket
113 import os
114 hostid = socket.gethostbyname(socket.gethostname())
Guido van Rossum0c8cf881996-08-26 16:40:20 +0000115 try:
116 uid = `os.getuid()`
117 except:
118 uid = '1'
119 try:
120 pid = `os.getpid()`
121 except:
122 pid = '1'
Guido van Rossum01ca3361992-07-13 14:28:59 +0000123 _prefix = hostid + '.' + uid + '.' + pid
Guido van Rossum5c7e8cd1998-04-11 03:06:02 +0000124 timestamp = '%.3f' % time.time()
Guido van Rossumb26a1b41998-05-20 17:05:52 +0000125 seed = `random.randint(0, 32767)`
Guido van Rossum01ca3361992-07-13 14:28:59 +0000126 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):
Guido van Rossum54f22ed2000-02-04 15:10:34 +0000132 """Decode common content-transfer-encodings (base64, quopri, uuencode)."""
Guido van Rossume3cd1511997-07-11 16:33:26 +0000133 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)
Guido van Rossume44a8d91997-12-10 18:54:36 +0000139 if encoding in ('uuencode', 'x-uuencode', 'uue', 'x-uue'):
Guido van Rossume3cd1511997-07-11 16:33:26 +0000140 import uu
141 return uu.decode(input, output)
Guido van Rossum13c8c022000-04-04 20:53:07 +0000142 if encoding in ('7bit', '8bit'):
Martin v. Löwis25d16922000-09-30 16:52:45 +0000143 return output.write(input.read())
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 Rossum54f22ed2000-02-04 15:10:34 +0000151 """Encode common content-transfer-encodings (base64, quopri, uuencode)."""
Guido van Rossume3cd1511997-07-11 16:33:26 +0000152 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)
Guido van Rossume44a8d91997-12-10 18:54:36 +0000158 if encoding in ('uuencode', 'x-uuencode', 'uue', 'x-uue'):
Guido van Rossume3cd1511997-07-11 16:33:26 +0000159 import uu
160 return uu.encode(input, output)
Guido van Rossum13c8c022000-04-04 20:53:07 +0000161 if encoding in ('7bit', '8bit'):
Martin v. Löwis25d16922000-09-30 16:52:45 +0000162 return output.write(input.read())
Guido van Rossumb6775db1994-08-01 11:34:53 +0000163 if encodetab.has_key(encoding):
164 pipethrough(input, encodetab[encoding], output)
165 else:
166 raise ValueError, \
167 'unknown Content-Transfer-Encoding: %s' % encoding
168
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 = {
181 'uuencode': uudecode_pipe,
182 'x-uuencode': uudecode_pipe,
Guido van Rossume44a8d91997-12-10 18:54:36 +0000183 'uue': uudecode_pipe,
184 'x-uue': uudecode_pipe,
Guido van Rossumb6775db1994-08-01 11:34:53 +0000185 'quoted-printable': 'mmencode -u -q',
186 'base64': 'mmencode -u -b',
187}
188
189encodetab = {
190 'x-uuencode': 'uuencode tempfile',
191 'uuencode': 'uuencode tempfile',
Guido van Rossume44a8d91997-12-10 18:54:36 +0000192 'x-uue': 'uuencode tempfile',
193 'uue': 'uuencode tempfile',
Guido van Rossumb6775db1994-08-01 11:34:53 +0000194 'quoted-printable': 'mmencode -q',
195 'base64': 'mmencode -b',
196}
197
198def pipeto(input, command):
199 pipe = os.popen(command, 'w')
200 copyliteral(input, pipe)
201 pipe.close()
202
203def pipethrough(input, command, output):
204 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)
216
217def copyliteral(input, output):
218 while 1:
219 line = input.readline()
220 if not line: break
221 output.write(line)
222
223def copybinary(input, output):
224 BUFSIZE = 8192
225 while 1:
226 line = input.read(BUFSIZE)
227 if not line: break
228 output.write(line)