1
# Copyright 2012 Canonical Ltd. This software is licensed under the
2
# GNU Affero General Public License version 3 (see the file LICENSE).
4
"""Encoding of MIME multipart data."""
6
from __future__ import (
14
'encode_multipart_data',
22
def make_random_boundary(length=30):
23
"""Create a random string for use in MIME boundary lines."""
24
return b''.join(random.choice(string.letters) for ii in range(length))
27
def get_content_type(filename):
28
"""Return the MIME content type for the file with the given name."""
29
return mimetypes.guess_type(filename)[0] or b'application/octet-stream'
32
def encode_field(field_name, data, boundary):
33
"""MIME-encode a form field."""
34
field_name = field_name.encode('ascii')
37
b'Content-Disposition: form-data; name="%s"' % field_name,
43
def encode_file(name, fileObj, boundary):
44
"""MIME-encode a file upload."""
45
content_type = get_content_type(name)
46
name = name.encode('ascii')
49
b'Content-Disposition: form-data; name="%s"; filename="%s"' %
51
b'Content-Type: %s' % content_type,
57
def encode_multipart_data(data, files):
58
"""Create a MIME multipart payload from L{data} and L{files}.
60
@param data: A mapping of names (ASCII strings) to data (byte string).
61
@param files: A mapping of names (ASCII strings) to file objects ready to
63
@return: A 2-tuple of C{(body, headers)}, where C{body} is a a byte string
64
and C{headers} is a dict of headers to add to the enclosing request in
65
which this payload will travel.
67
boundary = make_random_boundary()
70
for name, content in data.items():
71
lines.extend(encode_field(name, content, boundary))
72
for name, file_obj in files.items():
73
lines.extend(encode_file(name, file_obj, boundary))
74
lines.extend((b'--%s--' % boundary, b''))
75
body = b'\r\n'.join(lines)
78
b'content-type': b'multipart/form-data; boundary=' + boundary,
79
b'content-length': b'%s' % (len(body)),