1 # -*- coding: utf-8 -*- 
   3 import itertools
, mimetools
, mimetypes
 
   5 from txclib 
import get_version
 
   7 # Helper class to enable urllib2 to handle PUT/DELETE requests as well 
   8 class RequestWithMethod(urllib2
.Request
): 
   9     """Workaround for using DELETE with urllib2""" 
  10     def __init__(self
, url
, method
, data
=None, headers
={}, 
  11         origin_req_host
=None, unverifiable
=False): 
  13         urllib2
.Request
.__init__(self
, url
, data
=data
, headers
=headers
, 
  14                  origin_req_host
=None, unverifiable
=False) 
  21 from cStringIO 
import StringIO
 
  24     def __init__(self
, anycallable
): 
  25         self
.__call__ 
= anycallable
 
  27 # Controls how sequences are uncoded. If true, elements may be given multiple 
  28 # values by assigning a sequence. 
  31 class MultipartPostHandler(urllib2
.BaseHandler
): 
  32     handler_order 
= urllib2
.HTTPHandler
.handler_order 
- 10 # needs to run first 
  34     def http_request(self
, request
): 
  35         data 
= request
.get_data() 
  36         if data 
is not None and type(data
) != str: 
  40                  for(key
, value
) in data
.items(): 
  41                      if type(value
) == file: 
  42                          v_files
.append((key
, value
)) 
  44                          v_vars
.append((key
, value
)) 
  46                 systype
, value
, traceback 
= sys
.exc_info() 
  47                 raise TypeError, "not a valid non-string sequence or mapping object", traceback
 
  50                 data 
= urllib
.urlencode(v_vars
, doseq
) 
  52                 boundary
, data 
= self
.multipart_encode(v_vars
, v_files
) 
  54                 contenttype 
= 'multipart/form-data; boundary=%s' % boundary
 
  55                 if(request
.has_header('Content-Type') 
  56                    and request
.get_header('Content-Type').find('multipart/form-data') != 0): 
  57                     print "Replacing %s with %s" % 
(request
.get_header('content-type'), 'multipart/form-data') 
  58                 request
.add_unredirected_header('Content-Type', contenttype
) 
  60             request
.add_data(data
) 
  64     def multipart_encode(vars, files
, boundary 
= None, buf 
= None): 
  66             boundary 
= mimetools
.choose_boundary() 
  69         for(key
, value
) in vars: 
  70             buf
.write('--%s\r\n' % boundary
) 
  71             buf
.write('Content-Disposition: form-data; name="%s"' % key
) 
  72             buf
.write('\r\n\r\n' + value 
+ '\r\n') 
  73         for(key
, fd
) in files
: 
  74             file_size 
= os
.fstat(fd
.fileno())[stat
.ST_SIZE
] 
  75             filename 
= fd
.name
.split('/')[-1] 
  76             contenttype 
= mimetypes
.guess_type(filename
)[0] or 'application/octet-stream' 
  77             buf
.write('--%s\r\n' % boundary
) 
  78             buf
.write('Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % 
(key
, filename
)) 
  79             buf
.write('Content-Type: %s\r\n' % contenttype
) 
  80             # buffer += 'Content-Length: %s\r\n' % file_size 
  82             buf
.write('\r\n' + fd
.read() + '\r\n') 
  83         buf
.write('--' + boundary 
+ '--\r\n\r\n') 
  86     multipart_encode 
= Callable(multipart_encode
) 
  88     https_request 
= http_request
 
  91 def user_agent_identifier(): 
  92     """Return the user agent for the client.""" 
  93     client_info 
= (get_version(), platform
.system(), platform
.machine()) 
  94     return "txclient/%s (%s %s)" % client_info