diff options
Diffstat (limited to 'addons/document_webdav/DAV/WebDAVServer.py')
| -rw-r--r-- | addons/document_webdav/DAV/WebDAVServer.py | 379 |
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)) |
