~mario-carrion/gwibber/gwibber-services

« back to all changes in this revision

Viewing changes to gwibber/sharing/yfrog/urllib2_file.py

  • Committer: Mario Carrion
  • Date: 2009-07-27 00:39:14 UTC
  • Revision ID: mario@carrion.ws-20090727003914-84373nqngcph5pmy
Services/Provider implementation

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/env python
 
2
####
 
3
# Version: 0.2.0
 
4
#  - UTF-8 filenames are now allowed (Eli Golovinsky)<br/>
 
5
#  - File object is no more mandatory, Object only needs to have seek() read() attributes (Eli Golovinsky)<br/>
 
6
#
 
7
# Version: 0.1.0
 
8
#  - upload is now done with chunks (Adam Ambrose)
 
9
#
 
10
# Version: older
 
11
# THANKS TO:
 
12
# bug fix: kosh @T aesaeion.com
 
13
# HTTPS support : Ryan Grow <ryangrow @T yahoo.com>
 
14
 
 
15
# Copyright (C) 2004,2005,2006 Fabien SEISEN
 
16
 
17
# This library is free software; you can redistribute it and/or
 
18
# modify it under the terms of the GNU Lesser General Public
 
19
# License as published by the Free Software Foundation; either
 
20
# version 2.1 of the License, or (at your option) any later version.
 
21
 
22
# This library is distributed in the hope that it will be useful,
 
23
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
24
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 
25
# Lesser General Public License for more details.
 
26
 
27
# You should have received a copy of the GNU Lesser General Public
 
28
# License along with this library; if not, write to the Free Software
 
29
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
30
 
31
# you can contact me at: <fabien@seisen.org>
 
32
# http://fabien.seisen.org/python/
 
33
#
 
34
# Also modified by Adam Ambrose (aambrose @T pacbell.net) to write data in
 
35
# chunks (hardcoded to CHUNK_SIZE for now), so the entire contents of the file
 
36
# don't need to be kept in memory.
 
37
#
 
38
"""
 
39
enable to upload files using multipart/form-data
 
40
 
 
41
idea from:
 
42
upload files in python:
 
43
 http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/146306
 
44
 
 
45
timeoutsocket.py: overriding Python socket API:
 
46
 http://www.timo-tasi.org/python/timeoutsocket.py
 
47
 http://mail.python.org/pipermail/python-announce-list/2001-December/001095.html
 
48
 
 
49
import urllib2_files
 
50
import urllib2
 
51
u = urllib2.urlopen('http://site.com/path' [, data])
 
52
 
 
53
data can be a mapping object or a sequence of two-elements tuples
 
54
(like in original urllib2.urlopen())
 
55
varname still need to be a string and
 
56
value can be string of a file object
 
57
eg:
 
58
  ((varname, value),
 
59
   (varname2, value),
 
60
  )
 
61
  or
 
62
  { name:  value,
 
63
    name2: value2
 
64
  }
 
65
 
 
66
"""
 
67
 
 
68
import os
 
69
import socket
 
70
import sys
 
71
import stat
 
72
import mimetypes
 
73
import mimetools
 
74
import httplib
 
75
import urllib
 
76
import urllib2
 
77
 
 
78
CHUNK_SIZE = 65536
 
79
 
 
80
class FileUpload:
 
81
    def __init__(self, fd, content_type):
 
82
        self.fd = fd
 
83
        self.content_type = content_type
 
84
 
 
85
def get_content_type(filename):
 
86
    return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
 
87
 
 
88
# if sock is None, juste return the estimate size
 
89
def send_data(v_vars, v_files, boundary, sock=None):
 
90
    l = 0
 
91
    for (k, v) in v_vars:
 
92
        buffer=''
 
93
        buffer += '--%s\r\n' % boundary
 
94
        buffer += 'Content-Disposition: form-data; name="%s"\r\n' % k
 
95
        buffer += '\r\n'
 
96
        buffer += v + '\r\n'
 
97
        if sock:
 
98
            sock.send(buffer)
 
99
        l += len(buffer)
 
100
    for (k, v) in v_files:
 
101
        fd = v.fd
 
102
        file_size = os.fstat(fd.fileno())[stat.ST_SIZE]
 
103
        name = fd.name.split('/')[-1]
 
104
        if isinstance(name, unicode):
 
105
            name = name.encode('UTF-8')
 
106
        buffer=''
 
107
        buffer += '--%s\r\n' % boundary
 
108
        buffer += 'Content-Disposition: form-data; name="%s"; filename="%s"\r\n' \
 
109
                  % (k, name)
 
110
 
 
111
        if v.content_type != None:
 
112
            content_type = v.content_type
 
113
        else:
 
114
            content_type = get_content_type(name)
 
115
        buffer += 'Content-Type: %s\r\n' % content_type
 
116
        buffer += 'Content-Length: %ld\r\n' % file_size
 
117
        buffer += '\r\n'
 
118
 
 
119
        l += len(buffer)
 
120
        if sock:
 
121
            sock.send(buffer)
 
122
            if hasattr(fd, 'seek'):
 
123
                fd.seek(0)
 
124
            while True:
 
125
                chunk = fd.read(CHUNK_SIZE)
 
126
                if not chunk: break
 
127
                sock.send(chunk)
 
128
 
 
129
        l += file_size
 
130
        buffer='\r\n'
 
131
        buffer += '--%s--\r\n' % boundary
 
132
        buffer += '\r\n'
 
133
        if sock:
 
134
            sock.send(buffer)
 
135
        l += len(buffer)
 
136
    return l
 
137
 
 
138
# mainly a copy of HTTPHandler from urllib2
 
139
class newHTTPHandler(urllib2.BaseHandler):
 
140
    def http_open(self, req):
 
141
        return self.do_open(httplib.HTTP, req)
 
142
 
 
143
    def do_open(self, http_class, req):
 
144
        data = req.get_data()
 
145
        v_files=[]
 
146
        v_vars=[]
 
147
        # mapping object (dict)
 
148
        if req.has_data() and type(data) != str:
 
149
            if hasattr(data, 'items'):
 
150
                data = data.items()
 
151
            else:
 
152
                try:
 
153
                    if len(data) and not isinstance(data[0], tuple):
 
154
                        raise TypeError
 
155
                except TypeError:
 
156
                    ty, va, tb = sys.exc_info()
 
157
                    raise TypeError, "not a valid non-string sequence or mapping object", tb
 
158
                
 
159
            for (k, v) in data:
 
160
                if isinstance(v, FileUpload):
 
161
                    v_files.append((k, v))
 
162
                else:
 
163
                    v_vars.append( (k, v) )
 
164
        # no file ? convert to string
 
165
        if len(v_vars) > 0 and len(v_files) == 0:
 
166
            data = urllib.urlencode(v_vars)
 
167
            v_files=[]
 
168
            v_vars=[]
 
169
        host = req.get_host()
 
170
        if not host:
 
171
            raise urllib2.URLError('no host given')
 
172
 
 
173
        h = http_class(host) # will parse host:port
 
174
        if req.has_data():
 
175
            h.putrequest('POST', req.get_selector())
 
176
            if not 'Content-type' in req.headers:
 
177
                if len(v_files) > 0:
 
178
                    boundary = mimetools.choose_boundary()
 
179
                    l = send_data(v_vars, v_files, boundary)
 
180
                    h.putheader('Content-Type',
 
181
                                'multipart/form-data; boundary=%s' % boundary)
 
182
                    h.putheader('Content-length', str(l))
 
183
                else:
 
184
                    h.putheader('Content-type',
 
185
                                'application/x-www-form-urlencoded')
 
186
                    if not 'Content-length' in req.headers:
 
187
                        h.putheader('Content-length', '%d' % len(data))
 
188
        else:
 
189
            h.putrequest('GET', req.get_selector())
 
190
 
 
191
        scheme, sel = urllib.splittype(req.get_selector())
 
192
        sel_host, sel_path = urllib.splithost(sel)
 
193
        h.putheader('Host', sel_host or host)
 
194
        for name, value in self.parent.addheaders:
 
195
            name = name.capitalize()
 
196
            if name not in req.headers:
 
197
                h.putheader(name, value)
 
198
        for k, v in req.headers.items():
 
199
            h.putheader(k, v)
 
200
        # httplib will attempt to connect() here.  be prepared
 
201
        # to convert a socket error to a URLError.
 
202
        try:
 
203
            h.endheaders()
 
204
        except socket.error, err:
 
205
            raise urllib2.URLError(err)
 
206
 
 
207
        if req.has_data():
 
208
            if len(v_files) >0:
 
209
                l = send_data(v_vars, v_files, boundary, h)
 
210
            elif len(v_vars) > 0:
 
211
                # if data is passed as dict ...
 
212
                data = urllib.urlencode(v_vars)
 
213
                h.send(data)
 
214
            else:
 
215
                # "normal" urllib2.urlopen()
 
216
                h.send(data)
 
217
 
 
218
        code, msg, hdrs = h.getreply()
 
219
        fp = h.getfile()
 
220
        if code == 200:
 
221
            resp = urllib.addinfourl(fp, hdrs, req.get_full_url())
 
222
            resp.code = code
 
223
            resp.msg = msg
 
224
            return resp
 
225
        else:
 
226
            return self.parent.error('http', req, fp, code, msg, hdrs)
 
227
 
 
228
#urllib2._old_HTTPHandler = urllib2.HTTPHandler
 
229
#urllib2.HTTPHandler = newHTTPHandler
 
230
 
 
231
class newHTTPSHandler(newHTTPHandler):
 
232
    def https_open(self, req):
 
233
        return self.do_open(httplib.HTTPS, req)
 
234
    
 
235
#urllib2.HTTPSHandler = newHTTPSHandler
 
236
 
 
237
#if __name__ == '__main__':
 
238
#    import getopt
 
239
#    import urllib2
 
240
#    import urllib2_file
 
241
#    import string
 
242
#    import sys
 
243
#
 
244
#    def usage(progname):
 
245
#        print """
 
246
#SYNTAX: %s -u url -f file [-v]
 
247
#""" % progname
 
248
#    
 
249
#    try:
 
250
#        opts, args = getopt.getopt(sys.argv[1:], 'hvu:f:')
 
251
#    except getopt.GetoptError, errmsg:
 
252
#        print "ERROR:", errmsg
 
253
#        sys.exit(1)
 
254
#
 
255
#    v_url = ''
 
256
#    v_verbose = 0
 
257
#    v_file = ''
 
258
 
 
259
#    for name, value in opts:
 
260
#        if name in ('-h',):
 
261
#            usage(sys.argv[0])
 
262
#            sys.exit(0)
 
263
#        elif name in ('-v',):
 
264
#            v_verbose += 1
 
265
#        elif name in ('-u',):
 
266
#            v_url = value
 
267
#        elif name in ('-f',):
 
268
#            v_file = value
 
269
#        else:
 
270
#            print "invalid argument:", name
 
271
#            sys.exit(2)
 
272
#
 
273
#    error = 0
 
274
#    if v_url == '':
 
275
#        print "need -u"
 
276
#        error += 1
 
277
#    if v_file == '':
 
278
#        print "need -f"
 
279
#        error += 1
 
280
#
 
281
#    if error > 0:
 
282
#        sys.exit(3)
 
283
#        
 
284
#    fd = open(v_file, 'r')
 
285
#    data = {
 
286
#        'filename' : fd,
 
287
#        }
 
288
#    # u = urllib2.urlopen(v_url, data)
 
289
#    req = urllib2.Request(v_url, data, {})
 
290
#    try:
 
291
#        u = urllib2.urlopen(req)
 
292
#    except urllib2.HTTPError, errobj:
 
293
#        print "HTTPError:", errobj.code
 
294
#        
 
295
#    else:
 
296
#        buf = u.read()
 
297
#        print "OK"