blob: 06c38281e343d0bbe91aadadc1acf7d2e32b83b8 [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 Rossuma5cf1791995-08-29 19:19:51 +000018 self.getheader('content-transfer-encoding') or \
19 self.getheader('content-encoding')
Guido van Rossum01ca3361992-07-13 14:28:59 +000020 self.typeheader = \
21 self.getheader('content-type')
22 self.parsetype()
23 self.parseplist()
Guido van Rossum01ca3361992-07-13 14:28:59 +000024
25 def parsetype(self):
26 str = self.typeheader
27 if str == None:
28 str = 'text/plain'
29 if ';' in str:
30 i = string.index(str, ';')
31 self.plisttext = str[i:]
32 str = str[:i]
33 else:
34 self.plisttext = ''
35 fields = string.splitfields(str, '/')
36 for i in range(len(fields)):
37 fields[i] = string.lower(string.strip(fields[i]))
38 self.type = string.joinfields(fields, '/')
39 self.maintype = fields[0]
40 self.subtype = string.joinfields(fields[1:], '/')
41
42 def parseplist(self):
43 str = self.plisttext
44 self.plist = []
45 while str[:1] == ';':
46 str = str[1:]
47 if ';' in str:
48 # XXX Should parse quotes!
49 end = string.index(str, ';')
50 else:
51 end = len(str)
52 f = str[:end]
53 if '=' in f:
54 i = string.index(f, '=')
55 f = string.lower(string.strip(f[:i])) + \
56 '=' + string.strip(f[i+1:])
57 self.plist.append(string.strip(f))
58
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
70 def getencoding(self):
71 if self.encodingheader == None:
72 return '7bit'
Guido van Rossumb6775db1994-08-01 11:34:53 +000073 return string.lower(self.encodingheader)
Guido van Rossum01ca3361992-07-13 14:28:59 +000074
75 def gettype(self):
76 return self.type
77
78 def getmaintype(self):
79 return self.maintype
80
81 def getsubtype(self):
82 return self.subtype
83
84
85
86
87# Utility functions
88# -----------------
89
90
91# Return a random string usable as a multipart boundary.
92# The method used is so that it is *very* unlikely that the same
93# string of characters will every occur again in the Universe,
94# so the caller needn't check the data it is packing for the
95# occurrence of the boundary.
96#
97# The boundary contains dots so you have to quote it in the header.
98
99_prefix = None
100
101def choose_boundary():
102 global _generation, _prefix, _timestamp
103 import time
104 import rand
105 if _prefix == None:
106 import socket
107 import os
108 hostid = socket.gethostbyname(socket.gethostname())
109 uid = `os.getuid()`
110 pid = `os.getpid()`
111 seed = `rand.rand()`
112 _prefix = hostid + '.' + uid + '.' + pid
Guido van Rossumfea2af11993-01-04 09:16:51 +0000113 timestamp = `int(time.time())`
Guido van Rossum01ca3361992-07-13 14:28:59 +0000114 seed = `rand.rand()`
115 return _prefix + '.' + timestamp + '.' + seed
Guido van Rossumb6775db1994-08-01 11:34:53 +0000116
117
118# Subroutines for decoding some common content-transfer-types
119
120# XXX This requires that uudecode and mmencode are in $PATH
121
122def decode(input, output, encoding):
123 if decodetab.has_key(encoding):
124 pipethrough(input, decodetab[encoding], output)
125 else:
126 raise ValueError, \
127 'unknown Content-Transfer-Encoding: %s' % encoding
128
129def encode(input, output, encoding):
130 if encodetab.has_key(encoding):
131 pipethrough(input, encodetab[encoding], output)
132 else:
133 raise ValueError, \
134 'unknown Content-Transfer-Encoding: %s' % encoding
135
136uudecode_pipe = '''(
137TEMP=/tmp/@uu.$$
138sed "s%^begin [0-7][0-7]* .*%begin 600 $TEMP%" | uudecode
139cat $TEMP
140rm $TEMP
141)'''
142
143decodetab = {
144 'uuencode': uudecode_pipe,
145 'x-uuencode': uudecode_pipe,
146 'quoted-printable': 'mmencode -u -q',
147 'base64': 'mmencode -u -b',
148}
149
150encodetab = {
151 'x-uuencode': 'uuencode tempfile',
152 'uuencode': 'uuencode tempfile',
153 'quoted-printable': 'mmencode -q',
154 'base64': 'mmencode -b',
155}
156
157def pipeto(input, command):
158 pipe = os.popen(command, 'w')
159 copyliteral(input, pipe)
160 pipe.close()
161
162def pipethrough(input, command, output):
163 tempname = tempfile.mktemp()
164 try:
165 temp = open(tempname, 'w')
166 except IOError:
167 print '*** Cannot create temp file', `tempname`
168 return
169 copyliteral(input, temp)
170 temp.close()
171 pipe = os.popen(command + ' <' + tempname, 'r')
172 copybinary(pipe, output)
173 pipe.close()
174 os.unlink(tempname)
175
176def copyliteral(input, output):
177 while 1:
178 line = input.readline()
179 if not line: break
180 output.write(line)
181
182def copybinary(input, output):
183 BUFSIZE = 8192
184 while 1:
185 line = input.read(BUFSIZE)
186 if not line: break
187 output.write(line)