--- /dev/null
+# -*- coding: utf-8 -*-
+import urllib2
+import itertools, mimetools, mimetypes
+import platform
+from txclib import get_version
+
+# Helper class to enable urllib2 to handle PUT/DELETE requests as well
+class RequestWithMethod(urllib2.Request):
+ """Workaround for using DELETE with urllib2"""
+ def __init__(self, url, method, data=None, headers={},
+ origin_req_host=None, unverifiable=False):
+ self._method = method
+ urllib2.Request.__init__(self, url, data=data, headers=headers,
+ origin_req_host=None, unverifiable=False)
+
+ def get_method(self):
+ return self._method
+
+import urllib
+import os, stat
+from cStringIO import StringIO
+
+class Callable:
+ def __init__(self, anycallable):
+ self.__call__ = anycallable
+
+# Controls how sequences are uncoded. If true, elements may be given multiple
+# values by assigning a sequence.
+doseq = 1
+
+class MultipartPostHandler(urllib2.BaseHandler):
+ handler_order = urllib2.HTTPHandler.handler_order - 10 # needs to run first
+
+ def http_request(self, request):
+ data = request.get_data()
+ if data is not None and type(data) != str:
+ v_files = []
+ v_vars = []
+ try:
+ for(key, value) in data.items():
+ if type(value) == file:
+ v_files.append((key, value))
+ else:
+ v_vars.append((key, value))
+ except TypeError:
+ systype, value, traceback = sys.exc_info()
+ raise TypeError, "not a valid non-string sequence or mapping object", traceback
+
+ if len(v_files) == 0:
+ data = urllib.urlencode(v_vars, doseq)
+ else:
+ boundary, data = self.multipart_encode(v_vars, v_files)
+
+ contenttype = 'multipart/form-data; boundary=%s' % boundary
+ if(request.has_header('Content-Type')
+ and request.get_header('Content-Type').find('multipart/form-data') != 0):
+ print "Replacing %s with %s" % (request.get_header('content-type'), 'multipart/form-data')
+ request.add_unredirected_header('Content-Type', contenttype)
+
+ request.add_data(data)
+
+ return request
+
+ def multipart_encode(vars, files, boundary = None, buf = None):
+ if boundary is None:
+ boundary = mimetools.choose_boundary()
+ if buf is None:
+ buf = StringIO()
+ for(key, value) in vars:
+ buf.write('--%s\r\n' % boundary)
+ buf.write('Content-Disposition: form-data; name="%s"' % key)
+ buf.write('\r\n\r\n' + value + '\r\n')
+ for(key, fd) in files:
+ file_size = os.fstat(fd.fileno())[stat.ST_SIZE]
+ filename = fd.name.split('/')[-1]
+ contenttype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
+ buf.write('--%s\r\n' % boundary)
+ buf.write('Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (key, filename))
+ buf.write('Content-Type: %s\r\n' % contenttype)
+ # buffer += 'Content-Length: %s\r\n' % file_size
+ fd.seek(0)
+ buf.write('\r\n' + fd.read() + '\r\n')
+ buf.write('--' + boundary + '--\r\n\r\n')
+ buf = buf.getvalue()
+ return boundary, buf
+ multipart_encode = Callable(multipart_encode)
+
+ https_request = http_request
+
+
+def user_agent_identifier():
+ """Return the user agent for the client."""
+ client_info = (get_version(), platform.system(), platform.machine())
+ return "txclient/%s (%s %s)" % client_info