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