1--- MultipartPostHandler.py    2017-05-18 12:42:04.369440354 +0200
  2+++ MultipartPostHandler.py    2017-05-30 18:27:15.085714022 +0200
  3@@ -51,15 +51,15 @@
  4   opener.open("http://wwww.bobsite.com/upload/", params)
  5 """
  6 
  7-import urllib.request, urllib.parse, urllib.error
  8-import urllib.request, urllib.error, urllib.parse
  9-import mimetools, mimetypes
 10-import os, stat
 11-from io import StringIO
 12-
 13-class Callable:
 14-    def __init__(self, anycallable):
 15-        self.__call__ = anycallable
 16+import os
 17+import io
 18+import stat
 19+import urllib.error
 20+import urllib.parse
 21+import urllib.request
 22+from email.generator import _make_boundary
 23+
 24+import mimetypes
 25 
 26 # Controls how sequences are uncoded. If true, elements may be given multiple values by
 27 #  assigning a sequence.
 28@@ -69,13 +69,13 @@
 29     handler_order = urllib.request.HTTPHandler.handler_order - 10 # needs to run first
 30 
 31     def http_request(self, request):
 32-        data = request.get_data()
 33+        data = request.data
 34         if data is not None and type(data) != str:
 35             v_files = []
 36             v_vars = []
 37             try:
 38                  for(key, value) in list(data.items()):
 39-                     if type(value) == file:
 40+                     if isinstance(value, io.IOBase):
 41                          v_files.append((key, value))
 42                      else:
 43                          v_vars.append((key, value))
 44@@ -93,35 +93,44 @@
 45                     print("Replacing %s with %s" % (request.get_header('content-type'), 'multipart/form-data'))
 46                 request.add_unredirected_header('Content-Type', contenttype)
 47 
 48-            request.add_data(data)
 49+            if isinstance(data, str):
 50+                request.data = data.encode()
 51+
 52+            else:
 53+                request.data = data
 54         return request
 55 
 56-    def multipart_encode(vars, files, boundary = None, buffer = None):
 57+    def multipart_encode(self, vars, files, boundary = None, buffer = None):
 58         if boundary is None:
 59-            boundary = mimetools.choose_boundary()
 60+            boundary = _make_boundary()
 61         if buffer is None:
 62-            buffer = StringIO()
 63+            buffer = io.BytesIO()
 64         for(key, value) in vars:
 65-            buffer.write('--%s\r\n' % boundary)
 66-            buffer.write('Content-Disposition: form-data; name="%s"' % key)
 67+            buffer.write(b'--%s\r\n' % boundary.encode())
 68+            buffer.write(b'Content-Disposition: form-data; name="%s"' % key.encode())
 69             if value is None:
 70-                value = ""
 71+                value = b""
 72+            else:
 73+                value = value.encode()
 74             # if type(value) is not str, we need str(value) to not error with cannot concatenate 'str'
 75             # and 'dict' or 'tuple' or somethingelse objects
 76-            buffer.write('\r\n\r\n' + str(value) + '\r\n')
 77+            buffer.write(b'\r\n\r\n' + value + b'\r\n')
 78         for(key, fd) in files:
 79             file_size = os.fstat(fd.fileno())[stat.ST_SIZE]
 80             filename = fd.name.split('/')[-1]
 81             contenttype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
 82-            buffer.write('--%s\r\n' % boundary)
 83-            buffer.write('Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (key, filename))
 84-            buffer.write('Content-Type: %s\r\n' % contenttype)
 85-            buffer.write('Content-Length: %s\r\n' % file_size)
 86+            buffer.write(b'--%s\r\n' % boundary.encode())
 87+            buffer.write(b'Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (key.encode(), filename.encode()))
 88+            buffer.write(b'Content-Type: %s\r\n' % contenttype.encode())
 89+            # do not send Content-Length header as it makes UCM corrupt the file
 90+            # buffer.write(b'Content-Length: %s\r\n' % str(file_size).encode())
 91             fd.seek(0)
 92-            buffer.write('\r\n' + fd.read() + '\r\n')
 93-        buffer.write('--' + boundary + '--\r\n')
 94+            content = fd.read()
 95+            if isinstance(content, str):
 96+                content = content.encode()
 97+            buffer.write(b'\r\n' + content + b'\r\n')
 98+        buffer.write(b'--' + boundary.encode() + b'--\r\n')
 99         buffer = buffer.getvalue()
100         return boundary, buffer
101-    multipart_encode = Callable(multipart_encode)
102 
103     https_request = http_request