~ubuntu-branches/ubuntu/saucy/boa-constructor/saucy

« back to all changes in this revision

Viewing changes to .pc/string_exceptions.patch/ExternalLib/WebDAV/client.py

  • Committer: Bazaar Package Importer
  • Author(s): Charlie Smotherman
  • Date: 2011-02-15 17:48:34 UTC
  • mfrom: (7.1.5 sid)
  • Revision ID: james.westby@ubuntu.com-20110215174834-0mm22h9ffz05jaub
Tags: 0.6.1-11
* debian/source:
  - switched to using source format 3.0 (quilt).
* debian/control:
  - increase min python version to (>=2.6.6-11~).
  - after a discussion with Luca Falavigna I am taking over maintenance of 
    the package.  Added my info to Maintainers: field
  - bumped Standards-Version to 3.9.1, no changes needed.
  - removed dependency on quilt.
  - removed dependency on python-support.
  - use X-P-V instead of XS-P-V, increase version to 2.6
* debian/rules:
  - removed "--with quilt", added "--with python2".
  - changed override to "dh_python2" and corrected syntax.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""HTTP 1.1 / WebDAV client library."""
 
2
 
 
3
__version__='$Revision: 1.4 $'[11:-2]
 
4
 
 
5
import sys, os, string,  time, types,re
 
6
import socket, httplib, mimetools
 
7
from types import FileType
 
8
from mimetypes import guess_type
 
9
from base64 import encodestring
 
10
from common import rfc1123_date
 
11
from cStringIO import StringIO
 
12
from random import random
 
13
from urllib import quote
 
14
 
 
15
 
 
16
 
 
17
 
 
18
class HTTP(httplib.HTTP):
 
19
    # A revised version of the HTTP class that can do basic
 
20
    # HTTP 1.1 connections, and also compensates for a bug
 
21
    # that occurs on some platforms in 1.5 and 1.5.1 with
 
22
    # socket.makefile().read()
 
23
 
 
24
    read_bug=sys.version[:5] in ('1.5 (','1.5.1')
 
25
 
 
26
    def putrequest(self, request, selector, ver='1.1'):
 
27
        selector=selector or '/'
 
28
        str = '%s %s HTTP/%s\r\n' % (request, selector, ver)
 
29
        self.send(str)
 
30
 
 
31
    def getreply(self):
 
32
        file=self.sock.makefile('rb')
 
33
        data=string.join(file.readlines(), '')
 
34
        file.close()
 
35
        self.file=StringIO(data)
 
36
        line = self.file.readline()
 
37
        try:
 
38
            [ver, code, msg] = string.split(line, None, 2)
 
39
        except ValueError:
 
40
            try:
 
41
                [ver, code] = string.split(line, None, 1)
 
42
                msg = ""
 
43
            except ValueError:
 
44
                return -1, line, None
 
45
        if ver[:5] != 'HTTP/':
 
46
            return -1, line, None
 
47
        code=string.atoi(code)
 
48
        msg =string.strip(msg)
 
49
        headers =mimetools.Message(self.file, 0)
 
50
        return ver, code, msg, headers
 
51
 
 
52
 
 
53
class Resource:
 
54
    """An object representing a web resource."""
 
55
 
 
56
    def __init__(self, url, username=None, password=None):
 
57
        self.username=username
 
58
        self.password=password
 
59
        self.url=url
 
60
 
 
61
        mo = urlreg.match(url)
 
62
        if mo:
 
63
            host,port,uri=mo.group(1,2,3)
 
64
            self.host=host
 
65
            self.port=port and string.atoi(port[1:]) or 80
 
66
            self.uri=uri or '/'
 
67
        else: raise ValueError, url
 
68
 
 
69
    def __getattr__(self, name):
 
70
        url=os.path.join(self.url, name)
 
71
        return self.__class__(url, username=self.username,
 
72
                              password=self.password)
 
73
 
 
74
    def __get_headers(self, kw={}):
 
75
        headers={}
 
76
        headers=self.__set_authtoken(headers)
 
77
        headers['User-Agent']='WebDAV.client %s' % __version__
 
78
        headers['Host']=self.host
 
79
        headers['Connection']='close'
 
80
        headers['Accept']='*/*'
 
81
        if kw.has_key('headers'):
 
82
            for name, val in kw['headers'].items():
 
83
                headers[name]=val
 
84
            del kw['headers']
 
85
        return headers
 
86
 
 
87
    def __set_authtoken(self, headers, atype='Basic'):
 
88
        if not (self.username and self.password):
 
89
            return headers
 
90
        if headers.has_key('Authorization'):
 
91
            return headers
 
92
        if atype=='Basic':
 
93
            headers['Authorization']=(
 
94
                "Basic %s" % string.replace(encodestring('%s:%s' % (self.username,self.password)),
 
95
                                            '\012',''))
 
96
            return headers
 
97
        raise ValueError, 'Unknown authentication scheme: %s' % atype
 
98
 
 
99
    def __enc_formdata(self, args={}):
 
100
        formdata=[]
 
101
        for key, val in args.items():
 
102
            n=string.rfind(key, '__')
 
103
            if n > 0:
 
104
                tag=key[n+2:]
 
105
                key=key[:n]
 
106
            else: tag='string'
 
107
            func=varfuncs.get(tag, marshal_string)
 
108
            formdata.append(func(key, val))
 
109
        return string.join(formdata, '&')
 
110
 
 
111
    def __enc_multipart(self, args={}):
 
112
        return MultiPart(args).render()
 
113
 
 
114
    def __snd_request(self, method, uri, headers={}, body='', eh=1):
 
115
        try:
 
116
            h=HTTP()
 
117
            h.connect(self.host, self.port)
 
118
            h.putrequest(method, uri)
 
119
            for n, v in headers.items():
 
120
                h.putheader(n, v)
 
121
            if eh: h.endheaders()
 
122
            if body: h.send(body)
 
123
            ver, code, msg, hdrs=h.getreply()
 
124
            data=h.getfile().read()
 
125
            h.close()
 
126
        except:
 
127
            raise 'NotAvailable', sys.exc_value
 
128
        return http_response(ver, code, msg, hdrs, data)
 
129
 
 
130
    # HTTP methods
 
131
 
 
132
    def get(self, **kw):
 
133
        headers=self.__get_headers(kw)
 
134
        query=self.__enc_formdata(kw)
 
135
        uri=query and '%s?%s' % (self.uri, query) or self.uri
 
136
        return self.__snd_request('GET', uri, headers)
 
137
 
 
138
    def head(self, **kw):
 
139
        headers=self.__get_headers(kw)
 
140
        query=self.__enc_formdata(kw)
 
141
        uri=query and '%s?%s' % (self.uri, query) or self.uri
 
142
        return self.__snd_request('HEAD', uri, headers)
 
143
 
 
144
    def post(self, **kw):
 
145
        headers=self.__get_headers(kw)
 
146
        content_type=None
 
147
        for key, val in kw.items():
 
148
            if (key[-6:]=='__file') or hasattr(val, 'read'):
 
149
                content_type='multipart/form-data'
 
150
                break
 
151
        if content_type=='multipart/form-data':
 
152
            body=self.__enc_multipart(kw)
 
153
            return self.__snd_request('POST', self.uri, headers, body, eh=0)
 
154
        else:
 
155
            body=self.__enc_formdata(kw)
 
156
            headers['Content-Type']='application/x-www-form-urlencoded'
 
157
            headers['Content-Length']=str(len(body))
 
158
            return self.__snd_request('POST', self.uri, headers, body)
 
159
 
 
160
    def put(self, file='', content_type='', content_enc='',
 
161
            isbin=re.compile(r'[\000-\006\177-\277]').search,
 
162
            **kw):
 
163
        headers=self.__get_headers(kw)
 
164
        filetype=type(file)
 
165
        if filetype is type('') and (isbin(file) is None) and \
 
166
           os.path.exists(file):
 
167
            ob=open(file, 'rb')
 
168
            body=ob.read()
 
169
            ob.close()
 
170
            c_type, c_enc=guess_type(file)
 
171
        elif filetype is FileType:
 
172
            body=file.read()
 
173
            c_type, c_enc=guess_type(file.name)
 
174
        elif filetype is type(''):
 
175
            body=file
 
176
            c_type, c_enc=guess_type(self.url)
 
177
        else:
 
178
            raise ValueError, 'File must be a filename, file or string.'
 
179
        content_type=content_type or c_type
 
180
        content_enc =content_enc or c_enc
 
181
        if content_type: headers['Content-Type']=content_type
 
182
        if content_enc:  headers['Content-Encoding']=content_enc
 
183
        headers['Content-Length']=str(len(body))
 
184
        return self.__snd_request('PUT', self.uri, headers, body)
 
185
 
 
186
    def options(self, **kw):
 
187
        headers=self.__get_headers(kw)
 
188
        return self.__snd_request('OPTIONS', self.uri, headers)
 
189
 
 
190
    def trace(self, **kw):
 
191
        headers=self.__get_headers(kw)
 
192
        return self.__snd_request('TRACE', self.uri, headers)
 
193
 
 
194
    def delete(self, **kw):
 
195
        headers=self.__get_headers(kw)
 
196
        return self.__snd_request('DELETE', self.uri, headers)
 
197
 
 
198
    def propfind(self, body='', depth=0, **kw):
 
199
        headers=self.__get_headers(kw)
 
200
        headers['Depth']=str(depth)
 
201
        headers['Content-Type']='text/xml; charset="utf-8"'
 
202
        headers['Content-Length']=str(len(body))
 
203
        return self.__snd_request('PROPFIND', self.uri, headers, body)
 
204
 
 
205
    def proppatch(self, body, **kw):
 
206
        headers=self.__get_headers(kw)
 
207
        if body: headers['Content-Type']='text/xml; charset="utf-8"'
 
208
        headers['Content-Length']=str(len(body))
 
209
        return self.__snd_request('PROPPATCH', self.uri, headers, body)
 
210
 
 
211
    def copy(self, dest, depth='infinity', overwrite=0, **kw):
 
212
        """Copy a resource to the specified destination."""
 
213
        headers=self.__get_headers(kw)
 
214
        headers['Overwrite']=overwrite and 'T' or 'F'
 
215
        headers['Destination']=dest
 
216
        headers['Depth']=depth
 
217
        return self.__snd_request('COPY', self.uri, headers)
 
218
 
 
219
    def move(self, dest, depth='infinity', overwrite=0, **kw):
 
220
        """Move a resource to the specified destination."""
 
221
        headers=self.__get_headers(kw)
 
222
        headers['Overwrite']=overwrite and 'T' or 'F'
 
223
        headers['Destination']=dest
 
224
        headers['Depth']=depth
 
225
        return self.__snd_request('MOVE', self.uri, headers)
 
226
 
 
227
    def mkcol(self, **kw):
 
228
        headers=self.__get_headers(kw)
 
229
        return self.__snd_request('MKCOL', self.uri, headers)
 
230
 
 
231
    # class 2 support
 
232
 
 
233
    def lock(self, scope='exclusive', type='write', owner='',
 
234
             depth='infinity', timeout='Infinite', **kw):
 
235
        """Create a lock with the specified scope, type, owner, depth
 
236
        and timeout on the resource. A locked resource prevents a principal
 
237
        without the lock from executing a PUT, POST, PROPPATCH, LOCK, UNLOCK,
 
238
        MOVE, DELETE, or MKCOL on the locked resource."""
 
239
        if not scope in ('shared', 'exclusive'):
 
240
            raise ValueError, 'Invalid lock scope.'
 
241
        if not type in ('write',):
 
242
            raise ValueError, 'Invalid lock type.'
 
243
        if not depth in ('0', 'infinity'):
 
244
            raise ValueError, 'Invalid depth.'
 
245
        headers=self.__get_headers(kw)
 
246
        body='<?xml version="1.0" encoding="utf-8"?>\n' \
 
247
             '<d:lockinfo xmlns:d="DAV:">\n' \
 
248
             '  <d:lockscope><d:%s/></d:lockscope>\n' \
 
249
             '  <d:locktype><d:%s/></d:locktype>\n' \
 
250
             '  <d:depth>%s</d:depth>\n' \
 
251
             '  <d:owner>\n' \
 
252
             '  <d:href>%s</d:href>\n' \
 
253
             '  </d:owner>\n' \
 
254
             '</d:lockinfo>' % (scope, type, depth, owner)
 
255
        headers['Content-Type']='text/xml; charset="utf-8"'
 
256
        headers['Content-Length']=str(len(body))
 
257
        headers['Timeout']=timeout
 
258
        headers['Depth']=depth
 
259
        return self.__snd_request('LOCK', self.uri, headers, body)
 
260
 
 
261
    def unlock(self, token, **kw):
 
262
        """Remove the lock identified by token from the resource and all
 
263
        other resources included in the lock.  If all resources which have
 
264
        been locked under the submitted lock token can not be unlocked the
 
265
        unlock method will fail."""
 
266
        headers=self.__get_headers(kw)
 
267
        token='<opaquelocktoken:%s>' % str(token)
 
268
        headers['Lock-Token']=token
 
269
        return self.__snd_request('UNLOCK', self.uri, headers)
 
270
 
 
271
    def allprops(self, depth=0):
 
272
        return self.propfind('', depth)
 
273
 
 
274
    def propnames(self, depth=0):
 
275
        body='<?xml version="1.0" encoding="utf-8"?>\n' \
 
276
              '<d:propfind xmlns:d="DAV:">\n' \
 
277
              '  <d:propname/>\n' \
 
278
              '</d:propfind>'
 
279
        return self.propfind(body, depth)
 
280
 
 
281
    def getprops(self, *names):
 
282
        if not names: return self.propfind()
 
283
        tags=string.join(names, '/>\n  <')
 
284
        body='<?xml version="1.0" encoding="utf-8"?>\n' \
 
285
              '<d:propfind xmlns:d="DAV:">\n' \
 
286
              '  <d:prop>\n' \
 
287
              '  <%s>\n' \
 
288
              '  </d:prop>\n' \
 
289
              '</d:propfind>' % tags
 
290
        return self.propfind(body, 0)
 
291
 
 
292
    def setprops(self, **props):
 
293
        if not props:
 
294
            raise ValueError, 'No properties specified.'
 
295
        tags=[]
 
296
        for key, val in props.items():
 
297
            tags.append('  <%s>%s</%s>' % (key, val, key))
 
298
        tags=string.join(tags, '\n')
 
299
        body='<?xml version="1.0" encoding="utf-8"?>\n' \
 
300
              '<d:propertyupdate xmlns:d="DAV:">\n' \
 
301
              '<d:set>\n' \
 
302
              '  <d:prop>\n' \
 
303
              '  %s\n' \
 
304
              '  </d:prop>\n' \
 
305
              '</d:set>\n' \
 
306
              '</d:propertyupdate>' % tags
 
307
        return self.proppatch(body)
 
308
 
 
309
    def delprops(self, *names):
 
310
        if not names:
 
311
            raise ValueError, 'No property names specified.'
 
312
        tags=string.join(names, '/>\n  <')
 
313
        body='<?xml version="1.0" encoding="utf-8"?>\n' \
 
314
              '<d:propertyupdate xmlns:d="DAV:">\n' \
 
315
              '<d:remove>\n' \
 
316
              '  <d:prop>\n' \
 
317
              '  <%s>\n' \
 
318
              '  </d:prop>\n' \
 
319
              '</d:remove>\n' \
 
320
              '</d:propfind>' % tags
 
321
        return self.proppatch(body)
 
322
 
 
323
    def __str__(self):
 
324
        return '<HTTP resource %s>' % self.url
 
325
 
 
326
    __repr__=__str__
 
327
 
 
328
 
 
329
 
 
330
 
 
331
 
 
332
 
 
333
 
 
334
 
 
335
class http_response:
 
336
    def __init__(self, ver, code, msg, headers, body):
 
337
        self.version=ver
 
338
        self.code=code
 
339
        self.msg=msg
 
340
        self.headers=headers
 
341
        self.body=body
 
342
 
 
343
    def get_status(self):
 
344
        return '%s %s' % (self.code, self.msg)
 
345
 
 
346
    def get_header(self, name, val=None):
 
347
        return self.headers.dict.get(string.lower(name), val)
 
348
 
 
349
    def get_headers(self):
 
350
        return self.headers.dict
 
351
 
 
352
    def get_body(self):
 
353
        return self.body
 
354
 
 
355
    def __str__(self):
 
356
        data=[]
 
357
        data.append('%s %s %s\r\n' % (self.version, self.code, self.msg))
 
358
        map(data.append, self.headers.headers)
 
359
        data.append('\r\n')
 
360
        data.append(self.body)
 
361
        return string.join(data, '')
 
362
 
 
363
 
 
364
set_xml="""<?xml version="1.0" encoding="utf-8"?>
 
365
   <d:propertyupdate xmlns:d="DAV:"
 
366
   xmlns:z="http://www.zope.org/propsets/default">
 
367
   <d:set>
 
368
   <d:prop>
 
369
   <z:author>Brian Lloyd</z:author>
 
370
   <z:title>My New Title</z:title>
 
371
   </d:prop>
 
372
   </d:set>
 
373
   </d:propertyupdate>
 
374
"""
 
375
 
 
376
funny="""<?xml version="1.0" encoding="utf-8"?>
 
377
 <d:propertyupdate xmlns:d="DAV:"
 
378
    xmlns:z="http://www.zope.org/propsets/default"
 
379
    xmlns:q="http://www.something.com/foo/bar">
 
380
 <d:set>
 
381
 <d:prop>
 
382
   <z:author>Brian Lloyd</z:author>
 
383
   <z:color>blue</z:color>
 
384
   <z:count>72</z:count>
 
385
   <q:Authors q:type="authorthing" z:type="string" xmlns:k="FOO:" xml:lang="en">
 
386
     <q:Author>
 
387
       <q:Person k:thing="Im a thing!">
 
388
         <q:Name>Brian Lloyd</q:Name>
 
389
       </q:Person>
 
390
     </q:Author>
 
391
   </q:Authors>
 
392
   <q:color>
 
393
     red
 
394
   </q:color>
 
395
 </d:prop>
 
396
 </d:set>
 
397
</d:propertyupdate>
 
398
"""
 
399
 
 
400
 
 
401
rem_xml="""<?xml version="1.0" encoding="utf-8"?>
 
402
   <d:propertyupdate xmlns:d="DAV:"
 
403
   xmlns:z="http://www.zope.org/propsets/default">
 
404
   <d:remove>
 
405
   <d:prop>
 
406
   <z:author/>
 
407
   <z:title/>
 
408
   </d:prop>
 
409
   </d:remove>
 
410
   </d:propertyupdate>
 
411
"""
 
412
 
 
413
find_xml="""<?xml version="1.0" encoding="utf-8" ?>
 
414
   <D:propfind xmlns:D="DAV:">
 
415
     <D:prop xmlns:z="http://www.zope.org/propsets/default">
 
416
          <z:title/>
 
417
          <z:author/>
 
418
          <z:content_type/>
 
419
     </D:prop>
 
420
   </D:propfind>
 
421
"""
 
422
 
 
423
 
 
424
 
 
425
##############################################################################
 
426
# Implementation details below here
 
427
 
 
428
 
 
429
urlreg=re.compile(r'http://([^:/]+)(:[0-9]+)?(/.+)?', re.I)
 
430
 
 
431
def marshal_string(name, val):
 
432
    return '%s=%s' % (name, quote(str(val)))
 
433
 
 
434
def marshal_float(name, val):
 
435
    return '%s:float=%s' % (name, val)
 
436
 
 
437
def marshal_int(name, val):
 
438
    return '%s:int=%s' % (name, val)
 
439
 
 
440
def marshal_long(name, val):
 
441
    value = '%s:long=%s' % (name, val)
 
442
    if value[-1] == 'L':
 
443
        value = value[:-1]
 
444
    return value
 
445
 
 
446
def marshal_list(name, seq, tname='list', lt=type([]), tt=type(())):
 
447
    result=[]
 
448
    for v in seq:
 
449
        tp=type(v)
 
450
        if tp in (lt, tt):
 
451
            raise TypeError, 'Invalid recursion in data to be marshaled.'
 
452
        result.append(marshal_var("%s:%s" % (name, tname), v))
 
453
    return string.join(result, '&')
 
454
 
 
455
def marshal_tuple(name, seq):
 
456
    return marshal_list(name, seq, 'tuple')
 
457
 
 
458
varfuncs={}
 
459
vartypes=(('int',    type(1), marshal_int),
 
460
          ('float',  type(1.0), marshal_float),
 
461
          ('long',   type(1L), marshal_long),
 
462
          ('list',   type([]), marshal_list),
 
463
          ('tuple',  type(()), marshal_tuple),
 
464
          ('string', type(''), marshal_string),
 
465
          ('file',   types.FileType, None),
 
466
          )
 
467
for name, tp, func in vartypes:
 
468
    varfuncs[name]=func
 
469
    varfuncs[tp]=func
 
470
 
 
471
def marshal_var(name, val):
 
472
    return varfuncs.get(type(val), marshal_string)(name, val)
 
473
 
 
474
 
 
475
 
 
476
class MultiPart:
 
477
    def __init__(self,*args):
 
478
        c=len(args)
 
479
        if c==1:    name,val=None,args[0]
 
480
        elif c==2:  name,val=args[0],args[1]
 
481
        else:       raise ValueError, 'Invalid arguments'
 
482
 
 
483
        h={'Content-Type':              {'_v':''},
 
484
           'Content-Transfer-Encoding': {'_v':''},
 
485
           'Content-Disposition':       {'_v':''},
 
486
           }
 
487
        dt=type(val)
 
488
        b=t=None
 
489
 
 
490
        if dt==types.DictType:
 
491
            t=1
 
492
            b=self.boundary()
 
493
            d=[]
 
494
            h['Content-Type']['_v']='multipart/form-data; boundary=%s' % b
 
495
            for n,v in val.items():
 
496
                d.append(MultiPart(n,v))
 
497
 
 
498
        elif (dt==types.ListType) or (dt==types.TupleType):
 
499
            raise ValueError, 'Sorry, nested multipart is not done yet!'
 
500
 
 
501
        elif dt==types.FileType or hasattr(val,'read'):
 
502
            if hasattr(val,'name'):
 
503
                ct, enc=guess_type(val.name)
 
504
                if not ct: ct='application/octet-stream'
 
505
                fn=string.replace(val.name,'\\','/')
 
506
                fn=fn[(string.rfind(fn,'/')+1):]
 
507
            else:
 
508
                ct='application/octet-stream'
 
509
                enc=''
 
510
                fn=''
 
511
 
 
512
            enc=enc or (ct[:6] in ('image/', 'applic') and 'binary' or '')
 
513
 
 
514
            h['Content-Disposition']['_v']      ='form-data'
 
515
            h['Content-Disposition']['name']    ='"%s"' % name
 
516
            h['Content-Disposition']['filename']='"%s"' % fn
 
517
            h['Content-Transfer-Encoding']['_v']=enc
 
518
            h['Content-Type']['_v']             =ct
 
519
            d=[]
 
520
            l=val.read(8192)
 
521
            while l:
 
522
                d.append(l)
 
523
                l=val.read(8192)
 
524
        else:
 
525
            n=string.rfind(name, '__')
 
526
            if n > 0: name='%s:%s' % (name[:n], name[n+2:])
 
527
            h['Content-Disposition']['_v']='form-data'
 
528
            h['Content-Disposition']['name']='"%s"' % name
 
529
            d=[str(val)]
 
530
 
 
531
        self._headers =h
 
532
        self._data    =d
 
533
        self._boundary=b
 
534
        self._top     =t
 
535
 
 
536
 
 
537
    def boundary(self):
 
538
        return '%s_%s_%s' % (int(time.time()), os.getpid(), random())
 
539
 
 
540
    def render(self):
 
541
        join=string.join
 
542
        h=self._headers
 
543
        s=[]
 
544
 
 
545
        if self._top:
 
546
            for n,v in h.items():
 
547
                if v['_v']:
 
548
                    s.append('%s: %s' % (n,v['_v']))
 
549
                    for k in v.keys():
 
550
                        if k != '_v': s.append('; %s=%s' % (k, v[k]))
 
551
                    s.append('\n')
 
552
            p=[]
 
553
            t=[]
 
554
            b=self._boundary
 
555
            for d in self._data:
 
556
                p.append(d.render())
 
557
            t.append('--%s\n' % b)
 
558
            t.append(join(p,'\n--%s\n' % b))
 
559
            t.append('\n--%s--\n' % b)
 
560
            t=join(t,'')
 
561
            s.append('Content-Length: %s\n\n' % len(t))
 
562
            s.append(t)
 
563
            return join(s,'')
 
564
 
 
565
        else:
 
566
            for n,v in h.items():
 
567
                if v['_v']:
 
568
                    s.append('%s: %s' % (n,v['_v']))
 
569
                    for k in v.keys():
 
570
                        if k != '_v': s.append('; %s=%s' % (k, v[k]))
 
571
                    s.append('\n')
 
572
            s.append('\n')
 
573
 
 
574
            if self._boundary:
 
575
                p=[]
 
576
                b=self._boundary
 
577
                for d in self._data:
 
578
                    p.append(d.render())
 
579
                s.append('--%s\n' % b)
 
580
                s.append(join(p,'\n--%s\n' % b))
 
581
                s.append('\n--%s--\n' % b)
 
582
                return join(s,'')
 
583
            else:
 
584
                return join(s+self._data,'')
 
585
 
 
586
 
 
587
    _extmap={'':     'text/plain',
 
588
             'rdb':  'text/plain',
 
589
             'html': 'text/html',
 
590
             'dtml': 'text/html',
 
591
             'htm':  'text/html',
 
592
             'dtm':  'text/html',
 
593
             'gif':  'image/gif',
 
594
             'jpg':  'image/jpeg',
 
595
             'exe':  'application/octet-stream',
 
596
             None :  'application/octet-stream',
 
597
             }
 
598
 
 
599
    _encmap={'image/gif': 'binary',
 
600
             'image/jpg': 'binary',
 
601
             'application/octet-stream': 'binary',
 
602
             }
 
603
 
 
604
 
 
605
bri =Resource('http://tarzan.digicool.com/dev/brian3/',
 
606
              username='brian',
 
607
              password='123')
 
608
abri=Resource('http://tarzan.digicool.com/dev/brian3/')
 
609
 
 
610
dav =Resource('http://tarzan.digicool.com/dev/dav/',
 
611
              username='brian',
 
612
              password='123')
 
613
adav=Resource('http://tarzan.digicool.com/dev/dav/')