diff options
| author | Harry (Open ERP) <hmo@tinyerp.com> | 2010-04-06 16:41:32 +0530 |
|---|---|---|
| committer | Harry (Open ERP) <hmo@tinyerp.com> | 2010-04-06 16:41:32 +0530 |
| commit | aa70854c09a1e287342e830318c873131bf9744f (patch) | |
| tree | f5621ffb465a5f5422774565da9fa090ceedc511 /addons/document_webdav | |
| parent | 74650c60cd107a29f66bd661066139fc1d18a7ce (diff) | |
[IMP] document_webdav: removed DAV and refactore code
bzr revid: hmo@tinyerp.com-20100406111132-9bscvnb6aojdvioo
Diffstat (limited to 'addons/document_webdav')
| -rw-r--r-- | addons/document_webdav/DAV/BufferingHTTPServer.py | 99 | ||||
| -rw-r--r-- | addons/document_webdav/DAV/WebDAVServer.py | 379 | ||||
| -rw-r--r-- | addons/document_webdav/DAV/__init__.py | 20 | ||||
| -rw-r--r-- | addons/document_webdav/DAV/constants.py | 18 | ||||
| -rw-r--r-- | addons/document_webdav/DAV/davcmd.py | 218 | ||||
| -rwxr-xr-x | addons/document_webdav/DAV/davcopy.py | 133 | ||||
| -rwxr-xr-x | addons/document_webdav/DAV/davmove.py | 102 | ||||
| -rwxr-xr-x | addons/document_webdav/DAV/delete.py | 63 | ||||
| -rwxr-xr-x | addons/document_webdav/DAV/errors.py | 56 | ||||
| -rw-r--r-- | addons/document_webdav/DAV/iface.py | 263 | ||||
| -rwxr-xr-x | addons/document_webdav/DAV/propfind.py | 372 | ||||
| -rw-r--r-- | addons/document_webdav/DAV/status.py | 31 | ||||
| -rwxr-xr-x | addons/document_webdav/DAV/utils.py | 160 | ||||
| -rw-r--r-- | addons/document_webdav/dav_fs.py | 183 | ||||
| -rw-r--r-- | addons/document_webdav/webdav_server.py | 140 |
15 files changed, 214 insertions, 2023 deletions
diff --git a/addons/document_webdav/DAV/BufferingHTTPServer.py b/addons/document_webdav/DAV/BufferingHTTPServer.py deleted file mode 100644 index 37520421432..00000000000 --- a/addons/document_webdav/DAV/BufferingHTTPServer.py +++ /dev/null @@ -1,99 +0,0 @@ -#!/usr/bin/env python - -""" - Buffering HTTP 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 - -""" - - -from utils import VERSION, AUTHOR -__version__ = VERSION -__author__ = AUTHOR - -from BaseHTTPServer import BaseHTTPRequestHandler -import os -class BufferedHTTPRequestHandler(BaseHTTPRequestHandler): - """ - Buffering HTTP Request Handler - - This class is an extension to the BaseHTTPRequestHandler - class which buffers the whole output and sends it at once - after the processing if the request is finished. - - This makes it possible to work together with some clients - which otherwise would break (e.g. cadaver) - - """ - - - def _init_buffer(self): - """initialize the buffer. - - If you override the handle() method remember to call - this (see below) - """ - self.__buffer="" - self.__outfp=os.tmpfile() - - def _append(self,s): - """ append a string to the buffer """ - self.__buffer=self.__buffer+s - - def _flush(self): - """ flush the buffer to wfile """ - self.wfile.write(self.__buffer) - self.__outfp.write(self.__buffer) - self.__outfp.flush() - self.wfile.flush() - self.__buffer="" - - def handle(self): - """ Handle a HTTP request """ - self._init_buffer() - BaseHTTPRequestHandler.handle(self) - self._flush() - - def send_header(self, keyword, value): - """Send a MIME header.""" - if self.request_version != 'HTTP/0.9': - self._append("%s: %s\r\n" % (keyword, value)) - - def end_headers(self): - """Send the blank line ending the MIME headers.""" - if self.request_version != 'HTTP/0.9': - self._append("\r\n") - - def send_response(self, code, message=None): - self.log_request(code) - - if message is None: - if self.responses.has_key(code): - message = self.responses[code][0] - else: - message = '' - - if self.request_version != 'HTTP/0.9': - self._append("%s %s %s\r\n" % - (self.protocol_version, str(code), message)) - - self.send_header('Server', self.version_string()) - self.send_header('Connection', 'close') - self.send_header('Date', self.date_time_string()) - - protocol_version="HTTP/1.1" - diff --git a/addons/document_webdav/DAV/WebDAVServer.py b/addons/document_webdav/DAV/WebDAVServer.py deleted file mode 100644 index 5f409773c18..00000000000 --- a/addons/document_webdav/DAV/WebDAVServer.py +++ /dev/null @@ -1,379 +0,0 @@ -""" - 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)) diff --git a/addons/document_webdav/DAV/__init__.py b/addons/document_webdav/DAV/__init__.py deleted file mode 100644 index 10abdb40014..00000000000 --- a/addons/document_webdav/DAV/__init__.py +++ /dev/null @@ -1,20 +0,0 @@ -""" - python davserver - 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 -""" - - diff --git a/addons/document_webdav/DAV/constants.py b/addons/document_webdav/DAV/constants.py deleted file mode 100644 index e858ac519c2..00000000000 --- a/addons/document_webdav/DAV/constants.py +++ /dev/null @@ -1,18 +0,0 @@ -""" - -constants definition - - -""" - -# definition for resourcetype -COLLECTION=1 -OBJECT=None -DAV_PROPS=['creationdate', 'displayname', 'getcontentlanguage', 'getcontentlength', 'getcontenttype', 'getetag', 'getlastmodified', 'lockdiscovery', 'resourcetype', 'source', 'supportedlock'] - - -# Request classes in propfind - -RT_ALLPROP=1 -RT_PROPNAME=2 -RT_PROP=3 diff --git a/addons/document_webdav/DAV/davcmd.py b/addons/document_webdav/DAV/davcmd.py deleted file mode 100644 index 5c6be3ff9f7..00000000000 --- a/addons/document_webdav/DAV/davcmd.py +++ /dev/null @@ -1,218 +0,0 @@ -""" - -davcmd.py ---------- - -containts commands like copy, move, delete for normal -resources and collections - -""" - -from string import split,replace,joinfields -import urlparse - -from utils import create_treelist, is_prefix -from errors import * - -def deltree(dc,uri,exclude={}): - """ delete a tree of resources - - dc -- dataclass to use - uri -- root uri to delete - exclude -- an optional list of uri:error_code pairs which should not - be deleted. - - returns dict of uri:error_code tuples from which - another method can create a multistatus xml element. - - Also note that we only know Depth=infinity thus we don't have - to test for it. - - """ - - tlist=create_treelist(dc,uri) - result={} - - for i in range(len(tlist),0,-1): - problem_uris=result.keys() - element=tlist[i-1] - - # test here, if an element is a prefix of an uri which - # generated an error before. - # note that we walk here from childs to parents, thus - # we cannot delete a parent if a child made a problem. - # (see example in 8.6.2.1) - ok=1 - for p in problem_uris: - if is_prefix(element,p): - ok=None - break - - if not ok: continue - - # here we test for the exclude list which is the other way round! - for p in exclude.keys(): - if is_prefix(p,element): - ok=None - break - - if not ok: continue - - # now delete stuff - try: - delone(dc,element) - except DAV_Error, (ec,dd): - result[element]=ec - - return result - -def delone(dc,uri): - """ delete a single object """ - if dc.is_collection(uri): - dc.rmcol(uri) # should be empty - else: - dc.rm(uri) - -### -### COPY -### - -# helper function - -def copy(dc,src,dst): - """ only copy the element - - This is just a helper method factored out from copy and - copytree. It will not handle the overwrite or depth header. - - """ - - # destination should have been deleted before - if dc.exists(dst): raise DAV_Error, 412 - - # source should exist also - if not dc.exists(src): raise DAV_NotFound - - if dc.is_collection(src): - dc.copycol(src,dst) # an exception will be passed thru - else: - dc.copy(src,dst) # an exception will be passed thru - - -# the main functions - -def copyone(dc,src,dst,overwrite=None): - """ copy one resource to a new destination """ - - if overwrite and dc.exists(dst): - delres=deltree(dc,dst) - else: - delres={} - - # if we cannot delete everything, then do not copy! - if delres: return delres - - try: - copy(dc,src,dst) # pass thru exceptions - except DAV_Error, (ec,dd): - return ec - -def copytree(dc,src,dst,overwrite=None): - """ copy a tree of resources to another location - - dc -- dataclass to use - src -- src uri from where to copy - dst -- dst uri - overwrite -- if 1 then delete dst uri before - - returns dict of uri:error_code tuples from which - another method can create a multistatus xml element. - - """ - - - # first delete the destination resource - if overwrite and dc.exists(dst): - delres=deltree(dc,dst) - else: - delres={} - - # if we cannot delete everything, then do not copy! - if delres: return delres - - # get the tree we have to copy - tlist=create_treelist(dc,src) - result={} - - # prepare destination URIs (get the prefix) - dpath=urlparse.urlparse(dst)[2] - - for element in tlist: - problem_uris=result.keys() - - # now URIs get longer and longer thus we have - # to test if we had a parent URI which we were not - # able to copy in problem_uris which is the prefix - # of the actual element. If it is, then we cannot - # copy this as well but do not generate another error. - ok=1 - for p in problem_uris: - if is_prefix(p,element): - ok=None - break - - if not ok: continue - - # now create the destination URI which corresponds to - # the actual source URI. -> actual_dst - # ("subtract" the base src from the URI and prepend the - # dst prefix to it.) - esrc=replace(element,src,"") - actual_dst=dpath+esrc - - # now copy stuff - try: - copy(dc,element,actual_dst) - except DAV_Error, (ec,dd): - result[element]=ec - - return result - - - -### -### MOVE -### - - -def moveone(dc,src,dst,overwrite=None): - """ move a single resource - - This is done by first copying it and then deleting - the original. - """ - - # first copy it - copyone(dc,src,dst,overwrite) - - # then delete it - dc.rm(src) - -def movetree(dc,src,dst,overwrite=None): - """ move a collection - - This is done by first copying it and then deleting - the original. - - PROBLEM: if something did not copy then we have a problem - when deleting as the original might get deleted! - """ - - # first copy it - res=copytree(dc,src,dst,overwrite) - - # then delete it - res=deltree(dc,src,exclude=res) - - return res - diff --git a/addons/document_webdav/DAV/davcopy.py b/addons/document_webdav/DAV/davcopy.py deleted file mode 100755 index ec44931b107..00000000000 --- a/addons/document_webdav/DAV/davcopy.py +++ /dev/null @@ -1,133 +0,0 @@ -#!/usr/bin/env python - -""" - python davserver - 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 - - -""" - - -from xml.dom import ext -from xml.dom.Document import Document - -import sys -import string -import urlparse -import urllib -from StringIO import StringIO - -import utils -from constants import COLLECTION, OBJECT, DAV_PROPS, RT_ALLPROP, RT_PROPNAME, RT_PROP -from errors import * -from utils import create_treelist, quote_uri, gen_estring - -class COPY: - """ copy resources and eventually create multistatus responses - - This module implements the COPY class which is responsible for - copying resources. Usually the normal copy work is done in the - interface class. This class only creates error messages if error - occur. - - """ - - - def __init__(self,dataclass,src_uri,dst_uri,overwrite): - self.__dataclass=dataclass - self.__src=src_uri - self.__dst=dst_uri - self.__overwrite=overwrite - - - def single_action(self): - """ copy a normal resources. - - We try to copy it and return the result code. - This is for Depth==0 - - """ - - dc=self.__dataclass - base=self.__src - - ### some basic tests - # test if dest exists and overwrite is false - if dc.exists(self.__dst) and not self.__overwrite: raise DAV_Error, 412 - # test if src and dst are the same - # (we assume that both uris are on the same server!) - ps=urlparse.urlparse(self.__src)[2] - pd=urlparse.urlparse(self.__dst)[2] - if ps==pd: raise DAV_Error, 403 - - return dc.copyone(self.__src,self.__dst,self.__overwrite) - - #return copyone(dc,self.__src,self.__dst,self.__overwrite) - - def tree_action(self): - """ copy a tree of resources (a collection) - - Here we return a multistatus xml element. - - """ - dc=self.__dataclass - base=self.__src - - ### some basic tests - # test if dest exists and overwrite is false - if dc.exists(self.__dst) and not self.__overwrite: raise DAV_Error, 412 - # test if src and dst are the same - # (we assume that both uris are on the same server!) - ps=urlparse.urlparse(self.__src)[2] - pd=urlparse.urlparse(self.__dst)[2] - if ps==pd: raise DAV_Error, 403 - - - result=dc.copytree(self.__src,self.__dst,self.__overwrite) - #result=copytree(dc,self.__src,self.__dst,self.__overwrite) - - if not result: return None - - ### - ### create the multistatus XML element - ### (this is also the same as in delete.py. - ### we might make a common method out of it) - ### - - doc = Document(None) - ms=doc.createElement("D:multistatus") - ms.setAttribute("xmlns:D","DAV:") - doc.appendChild(ms) - - for el,ec in result.items(): - re=doc.createElement("D:response") - hr=doc.createElement("D:href") - st=doc.createElement("D:status") - huri=doc.createTextNode(quote_uri(el)) - t=doc.createTextNode(gen_estring(ec)) - st.appendChild(t) - hr.appendChild(huri) - re.appendChild(hr) - re.appendChild(st) - ms.appendChild(re) - - sfile=StringIO() - ext.PrettyPrint(doc,stream=sfile) - s=sfile.getvalue() - sfile.close() - return s - diff --git a/addons/document_webdav/DAV/davmove.py b/addons/document_webdav/DAV/davmove.py deleted file mode 100755 index 78c1e649389..00000000000 --- a/addons/document_webdav/DAV/davmove.py +++ /dev/null @@ -1,102 +0,0 @@ -#!/usr/bin/env python - -""" - python davserver - 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 - - -""" - - -import sys -import string -import urlparse -import urllib -from StringIO import StringIO - -import utils -from constants import COLLECTION, OBJECT, DAV_PROPS -from constants import RT_ALLPROP, RT_PROPNAME, RT_PROP -from errors import * -from utils import create_treelist, quote_uri, gen_estring, make_xmlresponse -from davcmd import moveone, movetree - -class MOVE: - """ move resources and eventually create multistatus responses - - This module implements the MOVE class which is responsible for - moving resources. - - MOVE is implemented by a COPY followed by a DELETE of the old - resource. - - """ - - - def __init__(self,dataclass,src_uri,dst_uri,overwrite): - self.__dataclass=dataclass - self.__src=src_uri - self.__dst=dst_uri - self.__overwrite=overwrite - - - def single_action(self): - """ move a normal resources. - - We try to move it and return the result code. - This is for Depth==0 - - """ - - dc=self.__dataclass - base=self.__src - - ### some basic tests - # test if dest exists and overwrite is false - if dc.exists(self.__dst) and not self.__overwrite: raise DAV_Error, 412 - # test if src and dst are the same - # (we assume that both uris are on the same server!) - ps=urlparse.urlparse(self.__src)[2] - pd=urlparse.urlparse(self.__dst)[2] - if ps==pd: raise DAV_Error, 403 - - return dc.moveone(self.__src,self.__dst,self.__overwrite) - - def tree_action(self): - """ move a tree of resources (a collection) - - Here we return a multistatus xml element. - - """ - dc=self.__dataclass - base=self.__src - - ### some basic tests - # test if dest exists and overwrite is false - if dc.exists(self.__dst) and not self.__overwrite: raise DAV_Error, 412 - # test if src and dst are the same - # (we assume that both uris are on the same server!) - ps=urlparse.urlparse(self.__src)[2] - pd=urlparse.urlparse(self.__dst)[2] - if ps==pd: raise DAV_Error, 403 - - result=dc.movetree(self.__src,self.__dst,self.__overwrite) - if not result: return None - - # create the multistatus XML element - return make_xmlresponse(result) - diff --git a/addons/document_webdav/DAV/delete.py b/addons/document_webdav/DAV/delete.py deleted file mode 100755 index ed017e53b6d..00000000000 --- a/addons/document_webdav/DAV/delete.py +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env python - -""" - - python davserver - 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 - - -""" -import os -import string -import urllib -from StringIO import StringIO - -from status import STATUS_CODES -from utils import gen_estring, quote_uri, make_xmlresponse -from davcmd import deltree - -class DELETE: - - def __init__(self,uri,dataclass): - self.__dataclass=dataclass - self.__uri=uri - - def delcol(self): - """ delete a collection """ - - dc=self.__dataclass - result=dc.deltree(self.__uri) - - if not len(result.items()): - return None # everything ok - - # create the result element - return make_xmlresponse(result) - - def delone(self): - """ delete a resource """ - - dc=self.__dataclass - result=dc.delone(self.__uri) - - if not result: return None - if not len(result.items()): - return None # everything ok - - # create the result element - return make_xmlresponse(result) - diff --git a/addons/document_webdav/DAV/errors.py b/addons/document_webdav/DAV/errors.py deleted file mode 100755 index 9c0e7667913..00000000000 --- a/addons/document_webdav/DAV/errors.py +++ /dev/null @@ -1,56 +0,0 @@ -#!/usr/bin/env python -""" - - Exceptions for the DAVserver implementation - -""" - -class DAV_Error(Exception): - """ in general we can have the following arguments: - - 1. the error code - 2. the error result element, e.g. a <multistatus> element - """ - - def __init__(self,*args): - if len(args)==1: - self.args=(args[0],"") - else: - self.args=args - -class DAV_Secret(DAV_Error): - """ the user is not allowed to know anything about it - - returning this for a property value means to exclude it - from the response xml element. - """ - - def __init__(self): - DAV_Error.__init__(self,0) - pass - -class DAV_NotFound(DAV_Error): - """ a requested property was not found for a resource """ - - def __init__(self,*args): - if len(args): - if isinstance(args[0],list): - stre = "Path %s not found!"%('/'.join(args[0])) - else: - stre = args[0] - DAV_Error.__init__(self,404,stre) - else: - DAV_Error.__init__(self,404) - - pass - -class DAV_Forbidden(DAV_Error): - """ a method on a resource is not allowed """ - - def __init__(self,*args): - if len(args): - DAV_Error.__init__(self,403,args[0]) - else: - DAV_Error.__init__(self,403) - pass - diff --git a/addons/document_webdav/DAV/iface.py b/addons/document_webdav/DAV/iface.py deleted file mode 100644 index c8f5fe2594d..00000000000 --- a/addons/document_webdav/DAV/iface.py +++ /dev/null @@ -1,263 +0,0 @@ -""" - -basic interface class - -use this for subclassing when writing your own interface -class. - -""" - -from errors import * - -import time -from string import lower - -class dav_interface: - """ interface class for implementing DAV servers """ - - ### defined properties (modify this but let the DAV stuff there!) - ### the format is namespace: [list of properties] - - PROPS={"DAV:" : ('creationdate', - 'displayname', - 'getcontentlanguage', - 'getcontentlength', - 'getcontenttype', - 'getetag', - 'getlastmodified', - 'lockdiscovery', - 'resourcetype', - 'source', - 'supportedlock'), - "NS2" : ("p1","p2") - } - - # here we define which methods handle which namespace - # the first item is the namespace URI and the second one - # the method prefix - # e.g. for DAV:getcontenttype we call dav_getcontenttype() - M_NS={"DAV:" : "_get_dav", - "NS2" : "ns2" } - - def get_propnames(self,uri): - """ return the property names allowed for the given URI - - In this method we simply return the above defined properties - assuming that they are valid for any resource. - You can override this in order to return a different set - of property names for each resource. - - """ - return self.PROPS - - def get_prop2(self,uri,ns,pname): - """ return the value of a property - """ - if lower(ns)=="dav:": return self.get_dav(uri,pname) - - raise DAV_NotFound - - def get_prop(self,uri,ns,propname): - """ return the value of a given property - - uri -- uri of the object to get the property of - ns -- namespace of the property - pname -- name of the property - """ - if self.M_NS.has_key(ns): - prefix=self.M_NS[ns] - else: - print "No namespace:",ns, "( for prop:", propname,")" - raise DAV_NotFound - mname=prefix+"_"+propname - if not hasattr(self,mname): - raise DAV_NotFound - - try: - m=getattr(self,mname) - r=m(uri) - return r - except AttributeError, e: - print 'Property %s not supported' % propname - print "Exception:", e - raise DAV_NotFound - - ### - ### DATA methods (for GET and PUT) - ### - - def get_data(self,uri): - """ return the content of an object - - return data or raise an exception - - """ - raise DAV_NotFound - - def put(self,uri,data): - """ write an object to the repository - - return a result code or raise an exception - """ - - raise DAV_Forbidden - - ### - ### Methods for DAV properties - ### - - def _get_dav_creationdate(self,uri): - """ return the creationdate of a resource """ - d=self.get_creationdate(uri) - # format it - if isinstance(d, int) or isinstance(d, float): - d = time.localtimetime(d) - return time.strftime("%Y-%m-%dT%H:%M:%S%Z",d) - - def _get_dav_getlastmodified(self,uri): - """ return the last modified date of a resource """ - d=self.get_lastmodified(uri) - if isinstance(d, int) or isinstance(d, float): - d = time.localtime(d) - # format it - return time.asctime(d) - - - ### - ### OVERRIDE THESE! - ### - - def get_creationdate(self,uri): - """ return the creationdate of the resource """ - return time.time() - - def get_lastmodified(self,uri): - """ return the last modification date of the resource """ - return time.time() - - - ### - ### COPY MOVE DELETE - ### - - ### methods for deleting a resource - - def rmcol(self,uri): - """ delete a collection - - This should not delete any children! This is automatically done - before by the DELETE class in DAV/delete.py - - return a success code or raise an exception - - """ - raise DAV_NotFound - - def rm(self,uri): - """ delete a single resource - - return a success code or raise an exception - - """ - raise DAV_NotFound - - """ - - COPY/MOVE HANDLER - - These handler are called when a COPY or MOVE method is invoked by - a client. In the default implementation it works as follows: - - - the davserver receives a COPY/MOVE method - - the davcopy or davmove module will be loaded and the corresponding - class will be initialized - - this class parses the query and decides which method of the interface class - to call: - - copyone for a single resource to copy - copytree for a tree to copy (collection) - (the same goes for move of course). - - - the interface class has now two options: - 1. to handle the action directly (e.g. cp or mv on filesystems) - 2. to let it handle via the copy/move methods in davcmd. - - ad 1) The first approach can be used when we know that no error can - happen inside a tree or when the action can exactly tell which - element made which error. We have to collect these and return - it in a dict of the form {uri: error_code, ...} - - ad 2) The copytree/movetree/... methods of davcmd.py will do the recursion - themselves and call for each resource the copy/move method of the - interface class. Thus method will then only act on a single resource. - (Thus a copycol on a normal unix filesystem actually only needs to do - an mkdir as the content will be copied by the davcmd.py function. - The davcmd.py method will also automatically collect all errors and - return the dictionary described above. - When you use 2) you also have to implement the copy() and copycol() - methods in your interface class. See the example for details. - - To decide which approach is the best you have to decide if your application - is able to generate errors inside a tree. E.g. a function which completely - fails on a tree if one of the tree's childs fail is not what we need. Then - 2) would be your way of doing it. - Actually usually 2) is the better solution and should only be replaced by - 1) if you really need it. - - The remaining question is if we should do the same for the DELETE method. - - """ - - ### MOVE handlers - - def moveone(self,src,dst,overwrite): - """ move one resource with Depth=0 """ - return moveone(self,src,dst,overwrite) - - def movetree(self,src,dst,overwrite): - """ move a collection with Depth=infinity """ - return movetree(self,src,dst,overwrite) - - ### COPY handlers - - def copyone(self,src,dst,overwrite): - """ copy one resource with Depth=0 """ - return copyone(self,src,dst,overwrite) - - def copytree(self,src,dst,overwrite): - """ copy a collection with Depth=infinity """ - return copytree(self,src,dst,overwrite) - - - ### low level copy methods (you only need these for method 2) - def copy(self,src,dst): - """ copy a resource with depth==0 - - You don't need to bother about overwrite or not. - This has been done already. - - return a success code or raise an exception if something fails - """ - return 201 - - - def copycol(self,src,dst): - """ copy a resource with depth==infinity - - You don't need to bother about overwrite or not. - This has been done already. - - return a success code or raise an exception if something fails - """ - return 201 - - ### some utility functions you need to implement - - def exists(self,uri): - """ return 1 or None depending on if a resource exists """ - return None # no - - def is_collection(self,uri): - """ return 1 or None depending on if a resource is a collection """ - return None # no - diff --git a/addons/document_webdav/DAV/propfind.py b/addons/document_webdav/DAV/propfind.py deleted file mode 100755 index 0fb9c229c03..00000000000 --- a/addons/document_webdav/DAV/propfind.py +++ /dev/null @@ -1,372 +0,0 @@ -#!/usr/bin/env python - -""" - python davserver - 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 - -""" - - -from xml.dom import ext -from xml.dom.Document import Document - -import sys -import string -import urlparse -import urllib -from StringIO import StringIO - -import utils -from constants import COLLECTION, OBJECT, DAV_PROPS, RT_ALLPROP, RT_PROPNAME, RT_PROP -from errors import * - -def utf8str(st): - if isinstance(st,unicode): - return st.encode('utf8') - else: - return str(st) - -class PROPFIND: - """ parse a propfind xml element and extract props - - It will set the following instance vars: - - request_class : ALLPROP | PROPNAME | PROP - proplist : list of properties - nsmap : map of namespaces - - The list of properties will contain tuples of the form - (element name, ns_prefix, ns_uri) - - - """ - - - def __init__(self,uri,dataclass,depth): - self.request_type=None - self.nsmap={} - self.proplist={} - self.default_ns=None - self.__dataclass=dataclass - self.__depth=str(depth) - self.__uri=uri - self.use_full_urls=True - self.__has_body=None # did we parse a body? - - def read_propfind(self,xml_doc): - self.request_type,self.proplist,self.namespaces=utils.parse_propfind(xml_doc) - - # a violation of the expected logic: client (korganizer) will ask for DAV:resourcetype - # but we also have to return the http://groupdav.org/:resourcetype property! - if self.proplist.has_key('DAV:') and 'resourcetype' in self.proplist['DAV:']: - if not self.proplist.has_key('http://groupdav.org/'): - self.proplist['http://groupdav.org/'] = [] - self.proplist['http://groupdav.org/'].append('resourcetype') - if 'DAV:' in self.namespaces: #TMP - self.namespaces.append('http://groupdav.org/') - - def createResponse(self): - """ create the multistatus response - - This will be delegated to the specific method - depending on which request (allprop, propname, prop) - was found. - - If we get a PROPNAME then we simply return the list with empty - values which we get from the interface class - - If we get an ALLPROP we first get the list of properties and then - we do the same as with a PROP method. - - If the uri doesn't exist, return an xml response with a 404 status - - """ - - if not self.__dataclass.exists(self.__uri): - raise DAV_NotFound("Path %s doesn't exist" % self.__uri) - - if self.request_type==RT_ALLPROP: - return self.create_allprop() - - if self.request_type==RT_PROPNAME: - return self.create_propname() - - if self.request_type==RT_PROP: - return self.create_prop() - - # no body means ALLPROP! - return self.create_allprop() - - def create_propname(self): - """ create a multistatus response for the prop names """ - - dc=self.__dataclass - # create the document generator - doc = Document(None) - ms=doc.createElement("D:multistatus") - ms.setAttribute("xmlns:D","DAV:") - doc.appendChild(ms) - - if self.__depth=="0": - pnames=dc.get_propnames(self.__uri) - re=self.mk_propname_response(self.__uri,pnames,doc) - ms.appendChild(re) - - elif self.__depth=="1": - pnames=dc.get_propnames(self.__uri) - re=self.mk_propname_response(self.__uri,pnames,doc) - ms.appendChild(re) - - for newuri in dc.get_childs(self.__uri): - pnames=dc.get_propnames(newuri) - re=self.mk_propname_response(newuri,pnames,doc) - ms.appendChild(re) - # *** depth=="infinity" - - sfile=StringIO() - ext.PrettyPrint(doc,stream=sfile) - s=sfile.getvalue() - sfile.close() - return s - - def create_allprop(self): - """ return a list of all properties """ - self.proplist={} - self.namespaces=[] - for ns,plist in self.__dataclass.get_propnames(self.__uri).items(): - self.proplist[ns]=plist - self.namespaces.append(ns) - - return self.create_prop() - - def create_prop(self): - """ handle a <prop> request - - This will - - 1. set up the <multistatus>-Framework - - 2. read the property values for each URI - (which is dependant on the Depth header) - This is done by the get_propvalues() method. - - 3. For each URI call the append_result() method - to append the actual <result>-Tag to the result - document. - - We differ between "good" properties, which have been - assigned a value by the interface class and "bad" - properties, which resulted in an error, either 404 - (Not Found) or 403 (Forbidden). - - """ - - - # create the document generator - doc = Document(None) - ms=doc.createElement("D:multistatus") - ms.setAttribute("xmlns:D","DAV:") - doc.appendChild(ms) - - if self.__depth=="0": - gp,bp=self.get_propvalues(self.__uri) - res=self.mk_prop_response(self.__uri,gp,bp,doc) - ms.appendChild(res) - - elif self.__depth=="1": - gp,bp=self.get_propvalues(self.__uri) - res=self.mk_prop_response(self.__uri,gp,bp,doc) - ms.appendChild(res) - - try: - for newuri in self.__dataclass.get_childs(self.__uri): - gp,bp=self.get_propvalues(newuri) - res=self.mk_prop_response(newuri,gp,bp,doc) - ms.appendChild(res) - except DAV_NotFound: - # If no children, never mind. - pass - - sfile=StringIO() - ext.PrettyPrint(doc,stream=sfile) - s=sfile.getvalue() - sfile.close() - return s - - - def mk_propname_response(self,uri,propnames,doc): - """ make a new <prop> result element for a PROPNAME request - - This will simply format the propnames list. - propnames should have the format {NS1 : [prop1, prop2, ...], NS2: ...} - - """ - re=doc.createElement("D:response") - - # write href information - href=doc.createElement("D:href") - if self.use_full_urls: - huri=doc.createTextNode(uri) - else: - uparts=urlparse.urlparse(uri) - fileloc=uparts[2] - huri=doc.createTextNode(urllib.quote(fileloc.encode('utf8'))) - href.appendChild(huri) - re.appendChild(href) - - ps=doc.createElement("D:propstat") - nsnum=0 - - for ns,plist in propnames.items(): - # write prop element - pr=doc.createElement("D:prop") - nsp="ns"+str(nsnum) - pr.setAttribute("xmlns:"+nsp,ns) - nsnum=nsnum+1 - - # write propertynames - for p in plist: - pe=doc.createElement(nsp+":"+p) - pr.appendChild(pe) - - ps.appendChild(pr) - - re.appendChild(ps) - - return re - - def mk_prop_response(self,uri,good_props,bad_props,doc): - """ make a new <prop> result element - - We differ between the good props and the bad ones for - each generating an extra <propstat>-Node (for each error - one, that means). - - """ - re=doc.createElement("D:response") - # append namespaces to response - nsnum=0 - for nsname in self.namespaces: - re.setAttribute("xmlns:ns"+str(nsnum),nsname) - nsnum=nsnum+1 - - # write href information - href=doc.createElement("D:href") - if self.use_full_urls: - huri=doc.createTextNode(uri) - else: - uparts=urlparse.urlparse(uri) - fileloc=uparts[2] - huri=doc.createTextNode(urllib.quote(fileloc.encode('utf8'))) - href.appendChild(huri) - re.appendChild(href) - - # write good properties - if good_props and len(good_props.items()): - ps=doc.createElement("D:propstat") - - gp=doc.createElement("D:prop") - for ns in good_props.keys(): - ns_prefix="ns"+str(self.namespaces.index(ns))+":" - for p,v in good_props[ns].items(): - pe=doc.createElement(ns_prefix+str(p)) - if v == None: - pass - elif ns=='DAV:' and p=="resourcetype": - if v == 1: - ve=doc.createElement("D:collection") - pe.appendChild(ve) - elif isinstance(v,tuple) and v[1] == ns: - ve=doc.createElement(ns_prefix+v[0]) - pe.appendChild(ve) - else: - ve=doc.createTextNode(utf8str(v)) - pe.appendChild(ve) - - gp.appendChild(pe) - if gp.hasChildNodes(): - re.appendChild(ps) - ps.appendChild(gp) - s=doc.createElement("D:status") - t=doc.createTextNode("HTTP/1.1 200 OK") - s.appendChild(t) - ps.appendChild(s) - re.appendChild(ps) - - # now write the errors! - if len(bad_props.items()): - - # write a propstat for each error code - for ecode in bad_props.keys(): - ps=doc.createElement("D:propstat") - re.appendChild(ps) - bp=doc.createElement("D:prop") - ps.appendChild(bp) - - for ns in bad_props[ecode].keys(): - ns_prefix="ns"+str(self.namespaces.index(ns))+":" - - for p in bad_props[ecode][ns]: - pe=doc.createElement(ns_prefix+str(p)) - bp.appendChild(pe) - - s=doc.createElement("D:status") - t=doc.createTextNode(utils.gen_estring(ecode)) - s.appendChild(t) - ps.appendChild(s) - re.appendChild(ps) - - # return the new response element - return re - - def get_propvalues(self,uri): - """ create lists of property values for an URI - - We create two lists for an URI: the properties for - which we found a value and the ones for which we - only got an error, either because they haven't been - found or the user is not allowed to read them. - - """ - good_props={} - bad_props={} - - for (ns,plist) in self.proplist.items(): - good_props[ns]={} - bad_props={} - ec = 0 - for prop in plist: - try: - ec = 0 - r=self.__dataclass.get_prop(uri,ns,prop) - good_props[ns][prop]=r - except DAV_Error, error_code: - ec=error_code[0] - - # ignore props with error_code if 0 (invisible) - if ec==0: continue - - if bad_props.has_key(ec): - if bad_props[ec].has_key(ns): - bad_props[ec][ns].append(prop) - else: - bad_props[ec][ns]=[prop] - else: - bad_props[ec]={ns:[prop]} - - return good_props, bad_props - diff --git a/addons/document_webdav/DAV/status.py b/addons/document_webdav/DAV/status.py deleted file mode 100644 index 62cb323c4fb..00000000000 --- a/addons/document_webdav/DAV/status.py +++ /dev/null @@ -1,31 +0,0 @@ -""" - -status codes for DAV services - - -""" - - -STATUS_CODES={ - 102: "Processing", - 200: "Ok", - 201: "Created", - 204: "No Content", - 207: "Multi-Status", - 201: "Created", - 400: "Bad Request", - 403: "Forbidden", - 404: "Not Found", - 405: "Method Not Allowed", - 409: "Conflict", - 412: "Precondition failed", - 423: "Locked", - 415: "Unsupported Media Type", - 507: "Insufficient Storage", - 422: "Unprocessable Entity", - 423: "Locked", - 424: "Failed Dependency", - 502: "Bad Gateway", - 507: "Insufficient Storage", - 999: "Some error in Create Method please check the data of create method" -} diff --git a/addons/document_webdav/DAV/utils.py b/addons/document_webdav/DAV/utils.py deleted file mode 100755 index c4bb4d1a4c0..00000000000 --- a/addons/document_webdav/DAV/utils.py +++ /dev/null @@ -1,160 +0,0 @@ -#!/usr/bin/env python - -""" - -UTILITIES - -- parse a propfind request body into a list of props - -""" - -from xml.dom import ext -from xml.dom.Document import Document -from xml.dom.ext.reader import PyExpat -from xml.dom import Node -from xml.dom import NodeIterator, NodeFilter - -from string import lower, split, atoi, joinfields -import urlparse -from StringIO import StringIO - -from constants import RT_ALLPROP, RT_PROPNAME, RT_PROP -from status import STATUS_CODES - -VERSION = '0.6' -AUTHOR = 'Simon Pamies <s.pamies@banality.de>' - - -def gen_estring(ecode): - """ generate an error string from the given code """ - ec=atoi(str(ecode)) - if STATUS_CODES.has_key(ec): - return "HTTP/1.1 %s %s" %(ec,STATUS_CODES[ec]) - else: - return "HTTP/1.1 %s" %(ec) - -def parse_propfind(xml_doc): - """ parse an propfind xml file and return a list of props - - returns: - - request_type -- ALLPROP, PROPNAME, PROP - proplist -- list of properties found - namespaces -- list of namespaces found - - """ - doc = PyExpat.Reader().fromString(xml_doc) - snit = doc.createNodeIterator(doc, NodeFilter.NodeFilter.SHOW_ELEMENT, None, None) - - request_type=None - props={} - namespaces=[] - - while 1: - curr_elem = snit.nextNode() - if not curr_elem: break - ename=fname=lower(curr_elem.nodeName) - if ":" in fname: - ename=split(fname,":")[1] - if ename=="prop": request_type=RT_PROP; continue - if ename=="propfind": continue - if ename=="allprop": request_type=RT_ALLPROP; continue - if ename=="propname": request_type=RT_PROPNAME; continue - - # rest should be names of attributes - - ns = curr_elem.namespaceURI - if props.has_key(ns): - props[ns].append(ename) - else: - props[ns]=[ename] - namespaces.append(ns) - - return request_type,props,namespaces - - -def create_treelist(dataclass,uri): - """ create a list of resources out of a tree - - This function is used for the COPY, MOVE and DELETE methods - - uri - the root of the subtree to flatten - - It will return the flattened tree as list - - """ - queue=[uri] - list=[uri] - while len(queue): - element=queue[-1] - if dataclass.is_collection(element): - childs=dataclass.get_childs(element) - else: - childs=[] - if len(childs): - list=list+childs - # update queue - del queue[-1] - if len(childs): - queue=queue+childs - return list - -def is_prefix(uri1,uri2): - """ returns 1 of uri1 is a prefix of uri2 """ - if uri2[:len(uri1)]==uri1: - return 1 - else: - return None - -def quote_uri(uri): - """ quote an URL but not the protocol part """ - import urlparse - import urllib - - up=urlparse.urlparse(uri) - np=urllib.quote(up[2]) - return urlparse.urlunparse((up[0],up[1],np,up[3],up[4],up[5])) - -def get_uriparentpath(uri): - """ extract the uri path and remove the last element """ - up=urlparse.urlparse(uri) - return joinfields(split(up[2],"/")[:-1],"/") - -def get_urifilename(uri): - """ extract the uri path and return the last element """ - up=urlparse.urlparse(uri) - return split(up[2],"/")[-1] - -def get_parenturi(uri): - """ return the parent of the given resource""" - up=urlparse.urlparse(uri) - np=joinfields(split(up[2],"/")[:-1],"/") - return urlparse.urlunparse((up[0],up[1],np,up[3],up[4],up[5])) - -### XML utilities - -def make_xmlresponse(result): - """ construct a response from a dict of uri:error_code elements """ - doc = Document(None) - ms=doc.createElement("D:multistatus") - ms.setAttribute("xmlns:D","DAV:") - doc.appendChild(ms) - - for el,ec in result.items(): - re=doc.createElement("D:response") - hr=doc.createElement("D:href") - st=doc.createElement("D:status") - huri=doc.createTextNode(quote_uri(el)) - t=doc.createTextNode(gen_estring(ec)) - st.appendChild(t) - hr.appendChild(huri) - re.appendChild(hr) - re.appendChild(st) - ms.appendChild(re) - - sfile=StringIO() - ext.PrettyPrint(doc,stream=sfile) - s=sfile.getvalue() - sfile.close() - return s - diff --git a/addons/document_webdav/dav_fs.py b/addons/document_webdav/dav_fs.py index 1acbfd7d5ec..9ef91e0f9a6 100644 --- a/addons/document_webdav/dav_fs.py +++ b/addons/document_webdav/dav_fs.py @@ -59,67 +59,47 @@ class tinydav_handler(dav_interface): """ This class models a Tiny ERP interface for the DAV server """ - PROPS={'DAV:': dav_interface.PROPS['DAV:'], } + PROPS={'DAV:': dav_interface.PROPS['DAV:'],} - M_NS={ "DAV:" : dav_interface.M_NS['DAV:'], } + M_NS={ "DAV:" : dav_interface.M_NS['DAV:'],} - def __init__(self, parent, verbose=False): + def __init__(self, parent, verbose=False): self.db_name = False self.directory_id=False self.db_name_list=[] self.parent = parent self.baseuri = parent.baseuri - - def get_propnames(self,uri): - props = self.PROPS - self.parent.log_message('get propnames: %s' % uri) - if uri[-1]=='/':uri=uri[:-1] - cr, uid, pool, dbname, uri2 = self.get_cr(uri) - if not dbname: - cr.close() - return props - node = self.uri2object(cr,uid,pool, uri2) - if node: - props.update(node.get_dav_props(cr)) - cr.close() - return props - - def get_prop(self,uri,ns,propname): - """ return the value of a given property - - uri -- uri of the object to get the property of - ns -- namespace of the property - pname -- name of the property - """ - if self.M_NS.has_key(ns): - return dav_interface.get_prop(self,uri,ns,propname) - - if uri[-1]=='/':uri=uri[:-1] - cr, uid, pool, dbname, uri2 = self.get_cr(uri) - if not dbname: - cr.close() - raise DAV_NotFound - node = self.uri2object(cr,uid,pool, uri2) - if not node: - cr.close() - raise DAV_NotFound - res = node.get_dav_eprop(cr,ns,propname) - cr.close() - return res - -# -# def get_db(self,uri): -# names=self.uri2local(uri).split('/') -# self.db_name=False -# if len(names) > 1: -# self.db_name=self.uri2local(uri).split('/')[1] -# if self.db_name=='': -# raise Exception,'Plese specify Database name in folder' -# return self.db_name -# - - def later_get_db_from_path(self,path): - return "aaa" + self.verbose = verbose + + def get_propnames(self,uri): + props = self.PROPS + return props + + def _get_dav_lockdiscovery(self, uri): + raise DAV_NotFound + + def _get_dav_supportedlock(self, uri): + raise DAV_NotFound + def get_prop(self,uri,ns,propname): + """ return the value of a given property + + uri -- uri of the object to get the property of + ns -- namespace of the property + pname -- name of the property + """ + if self.M_NS.has_key(ns): + return dav_interface.get_prop(self,uri,ns,propname) + raise DAV_NotFound + + def get_db(self,uri): + names=self.uri2local(uri).split('/') + self.db_name=False + if len(names) > 1: + self.db_name=self.uri2local(uri).split('/')[1] + if self.db_name=='': + raise Exception,'Plese specify Database name in folder' + return self.db_name + def urijoin(self,*ajoin): """ Return the base URI of this request, or even join it with the @@ -130,7 +110,7 @@ class tinydav_handler(dav_interface): @memoize(4) def db_list(self): s = netsvc.ExportService.getService('db') - result = s.list() + result = s.exp_list() self.db_name_list=[] for db_name in result: db = pooler.get_db_only(db_name) @@ -146,16 +126,17 @@ class tinydav_handler(dav_interface): """ return the child objects as self.baseuris for the given URI """ self.parent.log_message('get childs: %s' % uri) if uri[-1]=='/':uri=uri[:-1] - cr, uid, pool, dbname, uri2 = self.get_cr(uri) + cr, uid, pool, dbname, uri2 = self.get_cr(uri) - if not dbname: - s = netsvc.ExportService.getService('db') - cr.close() - return map(lambda x: self.urijoin(x), self.db_list()) + if not dbname: + if cr: cr.close() + res = map(lambda x: self.urijoin(x), self.db_list()) + return res result = [] node = self.uri2object(cr,uid,pool, uri2[:]) + if not node: - cr.close() + if cr: cr.close() raise DAV_NotFound(uri2) else: fp = node.full_path() @@ -165,12 +146,13 @@ class tinydav_handler(dav_interface): else: fp = None for d in node.children(cr): - self.parent.log_message('child: %s' % d.path) + self.parent.log_message('child: %s' % d.path) if fp: result.append( self.urijoin(dbname,fp,d.path) ) else: result.append( self.urijoin(dbname,d.path) ) - cr.close() + if cr: cr.close() + return result def uri2local(self, uri): @@ -183,24 +165,28 @@ class tinydav_handler(dav_interface): # # pos: -1 to get the parent of the uri # - def get_cr(self, uri): + def get_cr(self, uri): pdb = self.parent.auth_proxy.last_auth reluri = self.uri2local(uri) try: - dbname = reluri.split('/')[2] + dbname = reluri.split('/')[1] except: - dbname = False + dbname = False if not dbname: return None, None, None, False, None if not pdb and dbname: # if dbname was in our uri, we should have authenticated # against that. raise Exception("Programming error") - assert pdb == dbname, " %s != %s" %(pdb, dbname) - user, passwd, dbn2, uid = self.parent.auth_proxy.auth_creds[pdb] + #assert pdb == dbname, " %s != %s" %(pdb, dbname) + res = self.parent.auth_proxy.auth_creds.get(dbname, False) + if not res: + self.parent.auth_proxy.checkRequest(self.parent, uri, dbname) + res = self.parent.auth_proxy.auth_creds[dbname] + user, passwd, dbn2, uid = res db,pool = pooler.get_db_and_pool(dbname) cr = db.cursor() - uri2 = reluri.split('/')[3:] + uri2 = reluri.split('/')[2:] return cr, uid, pool, dbname, uri2 def uri2object(self, cr,uid, pool,uri): @@ -208,20 +194,20 @@ class tinydav_handler(dav_interface): return None return pool.get('document.directory').get_object(cr, uid, uri) - def get_data(self,uri): + def get_data(self,uri): self.parent.log_message('GET: %s' % uri) if uri[-1]=='/':uri=uri[:-1] cr, uid, pool, dbname, uri2 = self.get_cr(uri) try: if not dbname: raise DAV_Error, 409 - node = self.uri2object(cr,uid,pool, uri2) + node = self.uri2object(cr,uid,pool, uri2) if not node: raise DAV_NotFound(uri2) try: datas = node.get_data(cr) except TypeError,e: - import traceback + import traceback self.parent.log_error("GET typeError: %s", str(e)) self.parent.log_message("Exc: %s",traceback.format_exc()) raise DAV_Forbidden @@ -235,11 +221,11 @@ class tinydav_handler(dav_interface): raise DAV_Error, 409 return datas finally: - cr.close() + if cr: cr.close() @memoize(CACHE_SIZE) def _get_dav_resourcetype(self,uri): - """ return type of object """ + """ return type of object """ self.parent.log_message('get RT: %s' % uri) if uri[-1]=='/':uri=uri[:-1] cr, uid, pool, dbname, uri2 = self.get_cr(uri) @@ -253,7 +239,7 @@ class tinydav_handler(dav_interface): return COLLECTION return OBJECT finally: - cr.close() + if cr: cr.close() def _get_dav_displayname(self,uri): self.parent.log_message('get DN: %s' % uri) @@ -271,14 +257,14 @@ class tinydav_handler(dav_interface): @memoize(CACHE_SIZE) def _get_dav_getcontentlength(self,uri): - """ return the content length of an object """ + """ return the content length of an object """ self.parent.log_message('get length: %s' % uri) if uri[-1]=='/':uri=uri[:-1] result = 0 - cr, uid, pool, dbname, uri2 = self.get_cr(uri) + cr, uid, pool, dbname, uri2 = self.get_cr(uri) if not dbname: - cr.close() - return '0' + if cr: cr.close() + return str(result) node = self.uri2object(cr, uid, pool, uri2) if not node: cr.close() @@ -311,9 +297,9 @@ class tinydav_handler(dav_interface): if uri[-1]=='/':uri=uri[:-1] today = time.time() cr, uid, pool, dbname, uri2 = self.get_cr(uri) - try: - if not dbname: - return today + if not dbname: + return today + try: node = self.uri2object(cr,uid,pool, uri2) if not node: raise DAV_NotFound(uri2) @@ -322,36 +308,35 @@ class tinydav_handler(dav_interface): else: return today finally: - cr.close() + if cr: cr.close() @memoize(CACHE_SIZE) def get_creationdate(self,uri): - """ return the last modified date of the object """ - + """ return the last modified date of the object """ if uri[-1]=='/':uri=uri[:-1] cr, uid, pool, dbname, uri2 = self.get_cr(uri) - try: - if not dbname: - raise DAV_Error, 409 + if not dbname: + raise DAV_Error, 409 + try: node = self.uri2object(cr,uid,pool, uri2) if not node: - raise DAV_NotFound(uri2) + raise DAV_NotFound(uri2) if node.create_date: - result = time.strptime(node.create_date,'%Y-%m-%d %H:%M:%S') + result = time.mktime(time.strptime(node.create_date,'%Y-%m-%d %H:%M:%S')) else: - result = time.gmtime() + result = time.time() return result finally: - cr.close() + if cr: cr.close() @memoize(CACHE_SIZE) def _get_dav_getcontenttype(self,uri): self.parent.log_message('get contenttype: %s' % uri) if uri[-1]=='/':uri=uri[:-1] cr, uid, pool, dbname, uri2 = self.get_cr(uri) - try: - if not dbname: - return 'httpd/unix-directory' + if not dbname: + return 'httpd/unix-directory' + try: node = self.uri2object(cr,uid,pool, uri2) if not node: raise DAV_NotFound(uri2) @@ -359,8 +344,8 @@ class tinydav_handler(dav_interface): return result #raise DAV_NotFound, 'Could not find %s' % path finally: - cr.close() - + if cr: cr.close() + def mkcol(self,uri): """ create a new collection """ self.parent.log_message('MKCOL: %s' % uri) @@ -401,7 +386,7 @@ class tinydav_handler(dav_interface): def put(self,uri,data,content_type=None): """ put the object into the filesystem """ - self.parent.log_message('Putting %s (%d), %s'%( misc.ustr(uri), len(data), content_type)) + self.parent.log_message('Putting %s (%d), %s'%( misc.ustr(uri), data and len(data) or 0, content_type)) parent='/'.join(uri.split('/')[:-1]) cr, uid, pool,dbname, uri2 = self.get_cr(uri) if not dbname: @@ -633,7 +618,7 @@ class tinydav_handler(dav_interface): result = False cr, uid, pool,dbname, uri2 = self.get_cr(uri) if not dbname: - cr.close() + if cr: cr.close() return True try: node = self.uri2object(cr,uid,pool, uri2) diff --git a/addons/document_webdav/webdav_server.py b/addons/document_webdav/webdav_server.py index 15e8e030824..f2c129dd608 100644 --- a/addons/document_webdav/webdav_server.py +++ b/addons/document_webdav/webdav_server.py @@ -28,10 +28,113 @@ import netsvc +import tools from dav_fs import tinydav_handler from tools.config import config from DAV.WebDAVServer import DAVRequestHandler from service.websrv_lib import HTTPDir,FixSendError +from DAV.propfind import PROPFIND + +import xml.dom.minidom +domimpl = xml.dom.minidom.getDOMImplementation() +import urlparse +import urllib +from DAV import utils + +super_mk_prop_response = PROPFIND.mk_prop_response +def mk_prop_response(self,uri,good_props,bad_props,doc): + """ make a new <prop> result element + + We differ between the good props and the bad ones for + each generating an extra <propstat>-Node (for each error + one, that means). + + """ + re=doc.createElement("D:response") + # append namespaces to response + nsnum=0 + for nsname in self.namespaces: + re.setAttribute("xmlns:ns"+str(nsnum),nsname) + nsnum=nsnum+1 + + # write href information + uparts=urlparse.urlparse(uri) + fileloc=uparts[2] + href=doc.createElement("D:href") + huri=doc.createTextNode(uparts[0]+'://'+'/'.join(uparts[1:2]) + urllib.quote(fileloc)) + href.appendChild(huri) + re.appendChild(href) + + # write good properties + ps=doc.createElement("D:propstat") + if good_props: + re.appendChild(ps) + + gp=doc.createElement("D:prop") + for ns in good_props.keys(): + ns_prefix="ns"+str(self.namespaces.index(ns))+":" + for p,v in good_props[ns].items(): + if not v: + pass + pe=doc.createElement(ns_prefix+str(p)) + if hasattr(v, '__class__') and v.__class__.__name__ == 'Element': + pe.appendChild(v) + else: + if p=="resourcetype": + if v==1: + ve=doc.createElement("D:collection") + pe.appendChild(ve) + else: + ve=doc.createTextNode(tools.ustr(v)) + pe.appendChild(ve) + + gp.appendChild(pe) + + ps.appendChild(gp) + s=doc.createElement("D:status") + t=doc.createTextNode("HTTP/1.1 200 OK") + s.appendChild(t) + ps.appendChild(s) + re.appendChild(ps) + + # now write the errors! + if len(bad_props.items()): + + # write a propstat for each error code + for ecode in bad_props.keys(): + ps=doc.createElement("D:propstat") + re.appendChild(ps) + bp=doc.createElement("D:prop") + ps.appendChild(bp) + + for ns in bad_props[ecode].keys(): + ns_prefix="ns"+str(self.namespaces.index(ns))+":" + + for p in bad_props[ecode][ns]: + pe=doc.createElement(ns_prefix+str(p)) + bp.appendChild(pe) + + s=doc.createElement("D:status") + t=doc.createTextNode(utils.gen_estring(ecode)) + s.appendChild(t) + ps.appendChild(s) + re.appendChild(ps) + + # return the new response element + return re + + +PROPFIND.mk_prop_response = mk_prop_response + +def OpenDAVConfig(**kw): + class OpenDAV: + def __init__(self, **kw): + self.__dict__.update(**kw) + + class Config: + DAV = OpenDAV(**kw) + + return Config() class DAVHandler(FixSendError,DAVRequestHandler): verbose = False @@ -43,16 +146,24 @@ class DAVHandler(FixSendError,DAVRequestHandler): netsvc.Logger().notifyChannel("webdav",netsvc.LOG_DEBUG,message) def handle(self): - pass + self._init_buffer() def finish(self): pass + def get_db_from_path(self, uri): + if uri or uri == '/': + dbs = self.IFACE_CLASS.db_list() + res = len(dbs) and dbs[0] or False + else: + res = self.IFACE_CLASS.get_db(uri) + return res + def setup(self): - davpath = '/'+config.get_misc('webdav','vdir','webdav')+'/' - self.baseuri = "http://%s:%d%s"% (self.server.server_name,self.server.server_port,davpath) - self.IFACE_CLASS = tinydav_handler(self) - pass + davpath = '/'+config.get_misc('webdav','vdir','webdav') + self.baseuri = "http://%s:%d/"% (self.server.server_name,self.server.server_port) + self.IFACE_CLASS = tinydav_handler(self, self.verbose) + def log_message(self, format, *args): netsvc.Logger().notifyChannel('webdav',netsvc.LOG_DEBUG_RPC,format % args) @@ -62,14 +173,23 @@ class DAVHandler(FixSendError,DAVRequestHandler): try: - from service.http_server import reg_http_service,OpenERPAuthProvider + from service.http_server import reg_http_service,OpenERPAuthProvider + if (config.get_misc('webdav','enable',True)): - davpath = '/'+config.get_misc('webdav','vdir','webdav')+'/' + directory = '/'+config.get_misc('webdav','vdir','webdav') handler = DAVHandler - handler.verbose = config.get_misc('webdav','verbose',True) + verbose = config.get_misc('webdav','verbose',True) handler.debug = config.get_misc('webdav','debug',True) - reg_http_service(HTTPDir(davpath,DAVHandler,OpenERPAuthProvider())) - netsvc.Logger().notifyChannel('webdav',netsvc.LOG_INFO,"WebDAV service registered at path: %s/ "% davpath) + _dc = { 'verbose' : verbose, + 'directory' : directory, + 'lockemulation' : False, + + } + + conf = OpenDAVConfig(**_dc) + handler._config = conf + reg_http_service(HTTPDir(directory,DAVHandler,OpenERPAuthProvider())) + netsvc.Logger().notifyChannel('webdav',netsvc.LOG_INFO,"WebDAV service registered at path: %s/ "% directory) except Exception, e: logger = netsvc.Logger() logger.notifyChannel('webdav', netsvc.LOG_ERROR, 'Cannot launch webdav: %s' % e) |
