~openerp-commiter/openobject-addons/extra-6.0

« back to all changes in this revision

Viewing changes to document_webdav_old/webdav/DAV/WebDAVServer.py

  • Committer: Fabien Pinckaers
  • Date: 2008-12-12 09:09:57 UTC
  • Revision ID: fp@tinyerp.com-20081212090957-cson0n0jove7dt7i
document_webdav

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
"""
 
2
    Python WebDAV Server.
 
3
    Copyright (C) 1999 Christian Scholz (ruebe@aachen.heimat.de)
 
4
 
 
5
    This library is free software; you can redistribute it and/or
 
6
    modify it under the terms of the GNU Library General Public
 
7
    License as published by the Free Software Foundation; either
 
8
    version 2 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
    Library General Public License for more details.
 
14
 
 
15
    You should have received a copy of the GNU Library General Public
 
16
    License along with this library; if not, write to the Free
 
17
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
18
 
 
19
This module builds on AuthServer by implementing the standard DAV
 
20
methods.
 
21
 
 
22
Subclass this class and specify an IFACE_CLASS. See example.
 
23
 
 
24
"""
 
25
 
 
26
DEBUG=None
 
27
 
 
28
from utils import VERSION, AUTHOR
 
29
__version__ = VERSION
 
30
__author__  = AUTHOR
 
31
 
 
32
 
 
33
import os
 
34
import sys
 
35
import time
 
36
import socket
 
37
import string
 
38
import posixpath
 
39
import base64
 
40
import AuthServer
 
41
import urlparse
 
42
import urllib
 
43
 
 
44
from propfind import PROPFIND
 
45
from delete import DELETE
 
46
from davcopy import COPY
 
47
from davmove import MOVE
 
48
 
 
49
from string import atoi,split
 
50
from status import STATUS_CODES
 
51
from errors import *
 
52
 
 
53
class DAVRequestHandler(AuthServer.BufferedAuthRequestHandler):
 
54
    """Simple DAV request handler with
 
55
 
 
56
    - GET
 
57
    - HEAD
 
58
    - PUT
 
59
    - OPTIONS
 
60
    - PROPFIND
 
61
    - PROPPATCH
 
62
    - MKCOL
 
63
 
 
64
    It uses the resource/collection classes for serving and
 
65
    storing content.
 
66
 
 
67
    """
 
68
 
 
69
    server_version = "DAV/" + __version__
 
70
 
 
71
    ### utility functions
 
72
    def _log(self, message):
 
73
        pass
 
74
 
 
75
    def send_body(self,DATA,code,msg,desc,ctype='application/octet-stream',headers={}):
 
76
        """ send a body in one part """
 
77
 
 
78
        self.send_response(code,message=msg)
 
79
        self.send_header("Connection", "close")
 
80
        self.send_header("Accept-Ranges", "bytes")
 
81
 
 
82
        for a,v in headers.items():
 
83
            self.send_header(a,v)
 
84
 
 
85
        if DATA:
 
86
            self.send_header("Content-Length", str(len(DATA)))
 
87
            self.send_header("Content-Type", ctype)
 
88
        else:
 
89
            self.send_header("Content-Length", "0")
 
90
 
 
91
        self.end_headers()
 
92
        if DATA:
 
93
            self._append(DATA)
 
94
 
 
95
    def send_body_chunks(self,DATA,code,msg,desc,ctype='text/xml; encoding="utf-8"'):
 
96
        """ send a body in chunks """
 
97
 
 
98
        self.responses[207]=(msg,desc)
 
99
        self.send_response(code,message=msg)
 
100
        self.send_header("Content-type", ctype)
 
101
        self.send_header("Connection", "close")
 
102
        self.send_header("Transfer-Encoding", "chunked")
 
103
        self.end_headers()
 
104
        self._append(hex(len(DATA))[2:]+"\r\n")
 
105
        self._append(DATA)
 
106
        self._append("\r\n")
 
107
        self._append("0\r\n")
 
108
        self._append("\r\n")
 
109
 
 
110
    ### HTTP METHODS
 
111
 
 
112
    def do_OPTIONS(self):
 
113
        """return the list of capabilities """
 
114
        self.send_response(200)
 
115
        self.send_header("Allow", "GET, HEAD, COPY, MOVE, POST, PUT, PROPFIND, PROPPATCH, OPTIONS, MKCOL, DELETE, TRACE")
 
116
        self.send_header("Content-Type", "text/plain")
 
117
        self.send_header("DAV", "1")
 
118
        self.end_headers()
 
119
 
 
120
    def do_PROPFIND(self):
 
121
 
 
122
        dc=self.IFACE_CLASS
 
123
        # read the body
 
124
        body=None
 
125
        if self.headers.has_key("Content-Length"):
 
126
            l=self.headers['Content-Length']
 
127
            body=self.rfile.read(atoi(l))
 
128
        body = """<?xml version="1.0" encoding="utf-8"?>
 
129
                    <propfind xmlns="DAV:"><prop>
 
130
                    <getcontentlength xmlns="DAV:"/>
 
131
                    <getlastmodified xmlns="DAV:"/>
 
132
                    <getcreationdate xmlns="DAV:"/>
 
133
                    <checked-in xmlns="DAV:"/>
 
134
                    <executable xmlns="http://apache.org/dav/props/"/>
 
135
                    <displayname xmlns="DAV:"/>
 
136
                    <resourcetype xmlns="DAV:"/>
 
137
                    <checked-out xmlns="DAV:"/>
 
138
                    </prop></propfind>"""
 
139
        self.write_infp(body)
 
140
 
 
141
        # which Depth?
 
142
        if self.headers.has_key('Depth'):
 
143
            d=self.headers['Depth']
 
144
        else:
 
145
            d="infinity"
 
146
 
 
147
        uri=urlparse.urljoin(dc.baseuri,self.path)
 
148
        uri=urllib.unquote(uri)
 
149
        print 'PROPFIND', uri, dc, d
 
150
        pf=PROPFIND(uri,dc,d)
 
151
        print 'PROPFIND', pf
 
152
 
 
153
        if body:
 
154
            pf.read_propfind(body)
 
155
 
 
156
        try:
 
157
            DATA=pf.createResponse()
 
158
            DATA=DATA+"\n"
 
159
        except DAV_Error, (ec,dd):
 
160
            return self.send_status(ec)
 
161
 
 
162
        #print DATA
 
163
        self.send_body_chunks(DATA,"207","Multi-Status","Multiple responses")
 
164
 
 
165
    def do_GET(self):
 
166
        """Serve a GET request."""
 
167
        dc=self.IFACE_CLASS
 
168
        uri=urlparse.urljoin(dc.baseuri,self.path)
 
169
        uri=urllib.unquote(uri)
 
170
 
 
171
        # get the last modified date
 
172
        try:
 
173
            lm=dc.get_prop(uri,"DAV:","getlastmodified")
 
174
        except:
 
175
            lm="Sun, 01 Dec 2014 00:00:00 GMT"  # dummy!
 
176
        headers={"Last-Modified":lm}
 
177
 
 
178
        # get the content type
 
179
        try:
 
180
            ct=dc.get_prop(uri,"DAV:","getcontenttype")
 
181
        except:
 
182
            ct="application/octet-stream"
 
183
 
 
184
        # get the data
 
185
        try:
 
186
            data=dc.get_data(uri)
 
187
        except DAV_Error, (ec,dd):
 
188
            self.send_status(ec)
 
189
            return
 
190
 
 
191
        # send the data
 
192
        self.send_body(data,"200","OK","OK",ct,headers)
 
193
 
 
194
    def do_HEAD(self):
 
195
        """ Send a HEAD response """
 
196
        dc=self.IFACE_CLASS
 
197
        uri=urlparse.urljoin(dc.baseuri,self.path)
 
198
        uri=urllib.unquote(uri)
 
199
 
 
200
        # get the last modified date
 
201
        try:
 
202
            lm=dc.get_prop(uri,"DAV:","getlastmodified")
 
203
        except:
 
204
            lm="Sun, 01 Dec 2014 00:00:00 GMT"  # dummy!
 
205
 
 
206
        headers={"Last-Modified":lm}
 
207
 
 
208
        # get the content type
 
209
        try:
 
210
            ct=dc.get_prop(uri,"DAV:","getcontenttype")
 
211
        except:
 
212
            ct="application/octet-stream"
 
213
 
 
214
        try:
 
215
            data=dc.get_data(uri)
 
216
            headers["Content-Length"]=str(len(data))
 
217
        except DAV_NotFound:
 
218
            self.send_body(None,"404","Not Found","")
 
219
            return
 
220
 
 
221
        self.send_body(None,"200","OK","OK",ct,headers)
 
222
 
 
223
    def do_POST(self):
 
224
        self.send_error(404,"File not found")
 
225
 
 
226
    def do_MKCOL(self):
 
227
        """ create a new collection """
 
228
 
 
229
        dc=self.IFACE_CLASS
 
230
        uri=urlparse.urljoin(dc.baseuri,self.path)
 
231
        uri=urllib.unquote(uri)
 
232
        try:
 
233
            dc.mkcol(uri)
 
234
            self.send_status(200)
 
235
        except DAV_Error, (ec,dd):
 
236
            self.send_status(ec)
 
237
 
 
238
    def do_DELETE(self):
 
239
        """ delete an resource """
 
240
        dc=self.IFACE_CLASS
 
241
        uri=urlparse.urljoin(dc.baseuri,self.path)
 
242
        uri=urllib.unquote(uri)
 
243
        dl=DELETE(uri,dc)
 
244
        if dc.is_collection(uri):
 
245
            res=dl.delcol()
 
246
        else:
 
247
            res=dl.delone()
 
248
 
 
249
        if res:
 
250
            self.send_status(207,body=res)
 
251
        else:
 
252
            self.send_status(204)
 
253
 
 
254
    def do_PUT(self):
 
255
        dc=self.IFACE_CLASS
 
256
 
 
257
        # read the body
 
258
        body=None
 
259
        if self.headers.has_key("Content-Length"):
 
260
            l=self.headers['Content-Length']
 
261
            body=self.rfile.read(atoi(l))
 
262
        uri=urlparse.urljoin(dc.baseuri,self.path)
 
263
        uri=urllib.unquote(uri)
 
264
 
 
265
        ct=None
 
266
        if self.headers.has_key("Content-Type"):
 
267
            ct=self.headers['Content-Type']
 
268
        try:
 
269
            dc.put(uri,body,ct)
 
270
        except DAV_Error, (ec,dd):
 
271
            self.send_status(ec)
 
272
            return
 
273
        self.send_status(201)
 
274
 
 
275
    def do_COPY(self):
 
276
        """ copy one resource to another """
 
277
        try:
 
278
            self.copymove(COPY)
 
279
        except DAV_Error, (ec,dd):
 
280
            self.send_status(ec)
 
281
 
 
282
    def do_MOVE(self):
 
283
        """ move one resource to another """
 
284
        try:
 
285
            self.copymove(MOVE)
 
286
        except DAV_Error, (ec,dd):
 
287
            self.send_status(ec)
 
288
 
 
289
    def copymove(self,CLASS):
 
290
        """ common method for copying or moving objects """
 
291
        dc=self.IFACE_CLASS
 
292
 
 
293
        # get the source URI
 
294
        source_uri=urlparse.urljoin(dc.baseuri,self.path)
 
295
        source_uri=urllib.unquote(source_uri)
 
296
 
 
297
        # get the destination URI
 
298
        dest_uri=self.headers['Destination']
 
299
        dest_uri=urllib.unquote(dest_uri)
 
300
 
 
301
        # Overwrite?
 
302
        overwrite=1
 
303
        result_code=204
 
304
        if self.headers.has_key("Overwrite"):
 
305
            if self.headers['Overwrite']=="F":
 
306
                overwrite=None
 
307
                result_code=201
 
308
 
 
309
        # instanciate ACTION class
 
310
        cp=CLASS(dc,source_uri,dest_uri,overwrite)
 
311
 
 
312
        # Depth?
 
313
        d="infinity"
 
314
        if self.headers.has_key("Depth"):
 
315
            d=self.headers['Depth']
 
316
 
 
317
            if d!="0" and d!="infinity":
 
318
                self.send_status(400)
 
319
                return
 
320
 
 
321
            if d=="0":
 
322
                res=cp.single_action()
 
323
                self.send_status(res)
 
324
                return
 
325
 
 
326
        # now it only can be "infinity" but we nevertheless check for a collection
 
327
        if dc.is_collection(source_uri):
 
328
            try:
 
329
                res=cp.tree_action()
 
330
            except DAV_Error, (ec,dd):
 
331
                self.send_status(ec)
 
332
                return
 
333
        else:
 
334
            try:
 
335
                res=cp.single_action()
 
336
            except DAV_Error, (ec,dd):
 
337
                self.send_status(ec)
 
338
                return
 
339
 
 
340
        if res:
 
341
            self.send_body_chunks(res,207,STATUS_CODES[207],STATUS_CODES[207],
 
342
                            ctype='text/xml; charset="utf-8"')
 
343
        else:
 
344
            self.send_status(result_code)
 
345
 
 
346
    def get_userinfo(self,user,pw):
 
347
        """ Dummy method which lets all users in """
 
348
 
 
349
        return 1
 
350
 
 
351
    def send_status(self,code=200,mediatype='text/xml;  charset="utf-8"', \
 
352
                                msg=None,body=None):
 
353
 
 
354
        if not msg: msg=STATUS_CODES[code]
 
355
        self.send_body(body,code,STATUS_CODES[code],msg,mediatype)
 
356