multipart.py 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. #!/usr/bin/python
  2. ####
  3. # 02/2006 Will Holcomb <wholcomb@gmail.com>
  4. #
  5. # This library is free software; you can redistribute it and/or
  6. # modify it under the terms of the GNU Lesser General Public
  7. # License as published by the Free Software Foundation; either
  8. # version 2.1 of the License, or (at your option) any later version.
  9. #
  10. # This library is distributed in the hope that it will be useful,
  11. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  13. # Lesser General Public License for more details.
  14. #
  15. # 7/26/07 Slightly modified by Brian Schneider
  16. # in order to support unicode files ( multipart_encode function )
  17. #
  18. # 10/17/07 Dirk: inherit from urllib2.HTTPHandler instead of BaseHandler to support wsgi_intercept
  19. """
  20. Usage:
  21. Enables the use of multipart/form-data for posting forms
  22. Inspirations:
  23. Upload files in python:
  24. http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/146306
  25. urllib2_file:
  26. Fabien Seisen: <fabien@seisen.org>
  27. Example:
  28. import MultipartPostHandler, urllib2, cookielib
  29. cookies = cookielib.CookieJar()
  30. opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cookies),
  31. MultipartPostHandler.MultipartPostHandler)
  32. params = { "username" : "bob", "password" : "riviera",
  33. "file" : open("filename", "rb") }
  34. opener.open("http://wwww.bobsite.com/upload/", params)
  35. Further Example:
  36. The main function of this file is a sample which downloads a page and
  37. then uploads it to the W3C validator.
  38. """
  39. import urllib
  40. import urllib2
  41. import mimetools, mimetypes
  42. import os, stat
  43. from cStringIO import StringIO
  44. #~ from wsgi_intercept import WSGI_HTTPConnection
  45. class Callable:
  46. def __init__(self, anycallable):
  47. self.__call__ = anycallable
  48. # Controls how sequences are uncoded. If true, elements may be given multiple values by
  49. # assigning a sequence.
  50. doseq = 1
  51. class MultipartPostHandler(urllib2.HTTPHandler):
  52. handler_order = urllib2.HTTPHandler.handler_order - 10 # needs to run first
  53. def http_request(self, request):
  54. data = request.get_data()
  55. if data is not None and type(data) != str:
  56. v_files = []
  57. v_vars = []
  58. try:
  59. for(key, value) in data.items():
  60. if type(value) == file:
  61. v_files.append((key, value))
  62. else:
  63. v_vars.append((key, value))
  64. except TypeError:
  65. tb = sys.exc_info()[2]
  66. try:
  67. raise TypeError, "not a valid non-string sequence or mapping object", tb
  68. finally:
  69. tb = None
  70. if len(v_files) == 0:
  71. data = urllib.urlencode(v_vars, doseq)
  72. else:
  73. boundary, data = self.multipart_encode(v_vars, v_files)
  74. contenttype = 'multipart/form-data; boundary=%s' % boundary
  75. if(request.has_header('Content-Type')
  76. and request.get_header('Content-Type').find('multipart/form-data') != 0):
  77. print "Replacing %s with %s" % (request.get_header('content-type'), 'multipart/form-data')
  78. request.add_unredirected_header('Content-Type', contenttype)
  79. request.add_data(data)
  80. return request
  81. #~ def http_open(self, req):
  82. #~ return self.do_open(WSGI_HTTPConnection, req)
  83. def multipart_encode(vars, files, boundary=None, buf=None):
  84. if boundary is None:
  85. boundary = mimetools.choose_boundary()
  86. if buf is None:
  87. buf = StringIO()
  88. for(key, value) in vars:
  89. buf.write('--%s\r\n' % boundary)
  90. buf.write('Content-Disposition: form-data; name="%s"' % key)
  91. buf.write('\r\n\r\n' + value + '\r\n')
  92. for(key, fd) in files:
  93. file_size = os.fstat(fd.fileno())[stat.ST_SIZE]
  94. filename = fd.name.split('/')[-1]
  95. contenttype = mimetypes.guess_type(filename)[0] or 'application/octet-stream'
  96. buf.write('--%s\r\n' % boundary)
  97. buf.write('Content-Disposition: form-data; name="%s"; filename="%s"\r\n' % (key, filename))
  98. buf.write('Content-Type: %s\r\n' % contenttype)
  99. # buffer += 'Content-Length: %s\r\n' % file_size
  100. fd.seek(0)
  101. buf.write('\r\n' + fd.read() + '\r\n')
  102. buf.write('--' + boundary + '--\r\n\r\n')
  103. buf = buf.getvalue()
  104. return boundary, buf
  105. multipart_encode = Callable(multipart_encode)
  106. https_request = http_request
  107. def main():# pragma: no cover
  108. import tempfile, sys
  109. validatorURL = "http://validator.w3.org/check"
  110. opener = urllib2.build_opener(MultipartPostHandler)
  111. def validateFile(url):
  112. temp = tempfile.mkstemp(suffix=".html")
  113. os.write(temp[0], opener.open(url).read())
  114. params = { "ss" : "0", # show source
  115. "doctype" : "Inline",
  116. "uploaded_file" : open(temp[1], "rb") }
  117. print opener.open(validatorURL, params).read()
  118. os.remove(temp[1])
  119. if len(sys.argv[1:]) > 0:
  120. for arg in sys.argv[1:]:
  121. validateFile(arg)
  122. else:
  123. validateFile("http://www.google.com")
  124. if __name__ == "__main__":# pragma: no cover
  125. main()