summaryrefslogtreecommitdiff
path: root/addons/document_webdav/DAV/WebDAVServer.py
diff options
context:
space:
mode:
authornch@tinyerp.com <>2009-12-08 09:45:17 +0530
committernch@tinyerp.com <>2009-12-08 09:45:17 +0530
commit3f9ed76869c572b42641e4e22e5435bb10fa5e46 (patch)
treed957a2fe852c383a7c22148ccc15c20aff028d67 /addons/document_webdav/DAV/WebDAVServer.py
parentd7a580e0bf1d031f3daa0bf657055ec50e307cfd (diff)
parentb1eb90aa06b4c74dd623f50f716b936a3b33a09a (diff)
[MERGE]
bzr revid: nch@tinyerp.com-20091208041517-n8932hvnsprkcme8
Diffstat (limited to 'addons/document_webdav/DAV/WebDAVServer.py')
-rw-r--r--addons/document_webdav/DAV/WebDAVServer.py379
1 files changed, 379 insertions, 0 deletions
diff --git a/addons/document_webdav/DAV/WebDAVServer.py b/addons/document_webdav/DAV/WebDAVServer.py
new file mode 100644
index 00000000000..cb4db2c0204
--- /dev/null
+++ b/addons/document_webdav/DAV/WebDAVServer.py
@@ -0,0 +1,379 @@
+"""
+ Python WebDAV Server.
+ Copyright (C) 1999 Christian Scholz (ruebe@aachen.heimat.de)
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public
+ License along with this library; if not, write to the Free
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+
+This module builds on AuthServer by implementing the standard DAV
+methods.
+
+Subclass this class and specify an IFACE_CLASS. See example.
+
+"""
+
+DEBUG=None
+
+from utils import VERSION, AUTHOR
+__version__ = VERSION
+__author__ = AUTHOR
+
+
+import os
+import sys
+import time
+import socket
+import string
+import posixpath
+import base64
+import urlparse
+import urllib
+
+from propfind import PROPFIND
+from delete import DELETE
+from davcopy import COPY
+from davmove import MOVE
+
+from string import atoi,split
+from status import STATUS_CODES
+from errors import *
+
+import BaseHTTPServer
+
+class DAVRequestHandler(BaseHTTPServer.BaseHTTPRequestHandler):
+ """Simple DAV request handler with
+
+ - GET
+ - HEAD
+ - PUT
+ - OPTIONS
+ - PROPFIND
+ - PROPPATCH
+ - MKCOL
+
+ It uses the resource/collection classes for serving and
+ storing content.
+
+ """
+
+ server_version = "DAV/" + __version__
+ protocol_version = 'HTTP/1.1'
+
+ ### utility functions
+ def _log(self, message):
+ pass
+
+ def _append(self,s):
+ """ write the string to wfile """
+ self.wfile.write(s)
+
+ def send_body(self,DATA,code,msg,desc,ctype='application/octet-stream',headers=None):
+ """ send a body in one part """
+
+ if not headers:
+ headers = {}
+ self.send_response(code,message=msg)
+ self.send_header("Connection", "keep-alive")
+ self.send_header("Accept-Ranges", "bytes")
+
+ for a,v in headers.items():
+ self.send_header(a,v)
+
+ if DATA:
+ self.send_header("Content-Length", str(len(DATA)))
+ self.send_header("Content-Type", ctype)
+ else:
+ self.send_header("Content-Length", "0")
+
+ self.end_headers()
+ if DATA:
+ self._append(DATA)
+
+ def send_body_chunks(self,DATA,code,msg,desc,ctype='text/xml; encoding="utf-8"'):
+ """ send a body in chunks """
+
+ self.responses[207]=(msg,desc)
+ self.send_response(code,message=msg)
+ self.send_header("Content-type", ctype)
+ self.send_header("Connection", "keep-alive")
+ self.send_header("Transfer-Encoding", "chunked")
+ self.end_headers()
+ self._append(hex(len(DATA))[2:]+"\r\n")
+ self._append(DATA)
+ self._append("\r\n")
+ self._append("0\r\n")
+ self._append("\r\n")
+
+ ### HTTP METHODS
+
+ def do_OPTIONS(self):
+ """return the list of capabilities """
+ self.send_response(200)
+ self.send_header("Allow", "GET, HEAD, COPY, MOVE, POST, PUT, PROPFIND, PROPPATCH, OPTIONS, MKCOL, DELETE, TRACE")
+ self.send_header("Content-Type", "text/plain")
+ self.send_header("Connection", "keep-alive")
+ self.send_header("DAV", "1")
+ self.end_headers()
+
+ def do_PROPFIND(self):
+
+ dc=self.IFACE_CLASS
+ # read the body
+ body=None
+ if self.headers.has_key("Content-Length"):
+ l=self.headers['Content-Length']
+ body=self.rfile.read(atoi(l))
+ alt_body = """<?xml version="1.0" encoding="utf-8"?>
+ <propfind xmlns="DAV:"><prop>
+ <getcontentlength xmlns="DAV:"/>
+ <getlastmodified xmlns="DAV:"/>
+ <getcreationdate xmlns="DAV:"/>
+ <checked-in xmlns="DAV:"/>
+ <executable xmlns="http://apache.org/dav/props/"/>
+ <displayname xmlns="DAV:"/>
+ <resourcetype xmlns="DAV:"/>
+ <checked-out xmlns="DAV:"/>
+ </prop></propfind>"""
+ #self.wfile.write(body)
+
+ # which Depth?
+ if self.headers.has_key('Depth'):
+ d=self.headers['Depth']
+ else:
+ d="infinity"
+
+ uri=self.geturi()
+ pf=PROPFIND(uri,dc,d)
+
+ if body:
+ pf.read_propfind(body)
+
+ try:
+ DATA=pf.createResponse()
+ DATA=DATA+"\n"
+ # print "Data:", DATA
+ except DAV_NotFound,(ec,dd):
+ return self.send_notFound(dd, uri)
+ except DAV_Error, (ec,dd):
+ return self.send_error(ec,dd)
+
+ self.send_body_chunks(DATA,207,"Multi-Status","Multiple responses")
+
+ def geturi(self):
+ buri = self.IFACE_CLASS.baseuri
+ if buri[-1] == '/':
+ return urllib.unquote(buri[:-1]+self.path)
+ else:
+ return urllib.unquote(buri+self.path)
+
+ def do_GET(self):
+ """Serve a GET request."""
+ dc=self.IFACE_CLASS
+ uri=self.geturi()
+
+ # get the last modified date
+ try:
+ lm=dc.get_prop(uri,"DAV:","getlastmodified")
+ except:
+ lm="Sun, 01 Dec 2014 00:00:00 GMT" # dummy!
+ headers={"Last-Modified":lm , "Connection": "keep-alive"}
+
+ # get the content type
+ try:
+ ct=dc.get_prop(uri,"DAV:","getcontenttype")
+ except:
+ ct="application/octet-stream"
+
+ # get the data
+ try:
+ data=dc.get_data(uri)
+ except DAV_Error, (ec,dd):
+ self.send_status(ec)
+ return
+
+ # send the data
+ self.send_body(data,200,"OK","OK",ct,headers)
+
+ def do_HEAD(self):
+ """ Send a HEAD response """
+ dc=self.IFACE_CLASS
+ uri=self.geturi()
+
+ # get the last modified date
+ try:
+ lm=dc.get_prop(uri,"DAV:","getlastmodified")
+ except:
+ lm="Sun, 01 Dec 2014 00:00:00 GMT" # dummy!
+
+ headers={"Last-Modified":lm, "Connection": "keep-alive"}
+
+ # get the content type
+ try:
+ ct=dc.get_prop(uri,"DAV:","getcontenttype")
+ except:
+ ct="application/octet-stream"
+
+ try:
+ data=dc.get_data(uri)
+ headers["Content-Length"]=str(len(data))
+ except DAV_NotFound:
+ self.send_body(None,404,"Not Found","")
+ return
+
+ self.send_body(None,200,"OK","OK",ct,headers)
+
+ def do_POST(self):
+ self.send_error(404,"File not found")
+
+ def do_MKCOL(self):
+ """ create a new collection """
+
+ dc=self.IFACE_CLASS
+ uri=self.geturi()
+ try:
+ res = dc.mkcol(uri)
+ if res:
+ self.send_body(None,201,"Created",'')
+ else:
+ self.send_body(None,415,"Cannot create",'')
+ #self.send_header("Connection", "keep-alive")
+ # Todo: some content, too
+ except DAV_Error, (ec,dd):
+ self.send_body(None,int(ec),dd,dd)
+
+ def do_DELETE(self):
+ """ delete an resource """
+ dc=self.IFACE_CLASS
+ uri=self.geturi()
+ dl=DELETE(uri,dc)
+ if dc.is_collection(uri):
+ res=dl.delcol()
+ else:
+ res=dl.delone()
+
+ if res:
+ self.send_status(207,body=res)
+ else:
+ self.send_status(204)
+
+ def do_PUT(self):
+ dc=self.IFACE_CLASS
+
+ # read the body
+ body=None
+ if self.headers.has_key("Content-Length"):
+ l=self.headers['Content-Length']
+ body=self.rfile.read(atoi(l))
+ uri=self.geturi()
+
+ ct=None
+ if self.headers.has_key("Content-Type"):
+ ct=self.headers['Content-Type']
+ try:
+ dc.put(uri,body,ct)
+ except DAV_Error, (ec,dd):
+ self.send_status(ec)
+ return
+ self.send_status(201)
+
+ def do_COPY(self):
+ """ copy one resource to another """
+ try:
+ self.copymove(COPY)
+ except DAV_Error, (ec,dd):
+ self.send_status(ec)
+
+ def do_MOVE(self):
+ """ move one resource to another """
+ try:
+ self.copymove(MOVE)
+ except DAV_Error, (ec,dd):
+ self.send_status(ec)
+
+ def copymove(self,CLASS):
+ """ common method for copying or moving objects """
+ dc=self.IFACE_CLASS
+
+ # get the source URI
+ source_uri=self.geturi()
+
+ # get the destination URI
+ dest_uri=self.headers['Destination']
+ dest_uri=urllib.unquote(dest_uri)
+
+ # Overwrite?
+ overwrite=1
+ result_code=204
+ if self.headers.has_key("Overwrite"):
+ if self.headers['Overwrite']=="F":
+ overwrite=None
+ result_code=201
+
+ # instanciate ACTION class
+ cp=CLASS(dc,source_uri,dest_uri,overwrite)
+
+ # Depth?
+ d="infinity"
+ if self.headers.has_key("Depth"):
+ d=self.headers['Depth']
+
+ if d!="0" and d!="infinity":
+ self.send_status(400)
+ return
+
+ if d=="0":
+ res=cp.single_action()
+ self.send_status(res)
+ return
+
+ # now it only can be "infinity" but we nevertheless check for a collection
+ if dc.is_collection(source_uri):
+ try:
+ res=cp.tree_action()
+ except DAV_Error, (ec,dd):
+ self.send_status(ec)
+ return
+ else:
+ try:
+ res=cp.single_action()
+ except DAV_Error, (ec,dd):
+ self.send_status(ec)
+ return
+
+ if res:
+ self.send_body_chunks(res,207,STATUS_CODES[207],STATUS_CODES[207],
+ ctype='text/xml; charset="utf-8"')
+ else:
+ self.send_status(result_code)
+
+ def get_userinfo(self,user,pw):
+ """ Dummy method which lets all users in """
+
+ return 1
+
+ def send_status(self,code=200,mediatype='text/xml; charset="utf-8"', \
+ msg=None,body=None):
+
+ if not msg: msg=STATUS_CODES[code]
+ self.send_body(body,code,STATUS_CODES[code],msg,mediatype)
+
+ def send_notFound(self,descr,uri):
+ body = """<?xml version="1.0" encoding="utf-8" ?>
+ <D:response xmlns:D="DAV:">
+ <D:href>%s</D:href>
+ <D:error/>
+ <D:responsedescription>%s</D:responsedescription>
+ </D:response>
+ """
+ return self.send_status(404,descr, body=body % (uri,descr))