diff options
| author | P. Christeas <p_christ@hol.gr> | 2010-12-07 15:40:41 +0200 |
|---|---|---|
| committer | P. Christeas <p_christ@hol.gr> | 2010-12-07 15:40:41 +0200 |
| commit | 6b2c832f5f9cc1667cd50230f8f0a0139d352201 (patch) | |
| tree | 61e04e42c975e98e93e061bd6c7b2f50e6cfb2cd /addons/document_webdav/webdav_server.py | |
| parent | 9a4c99980315d80e2da5a3af9a1d870bde4cf734 (diff) | |
doc webdav: Implement locking support
This replaces the pseydo-locking of python-webdav library with a real
db-based locking. Locks are stored as DAV properties, which will
effectively also be listed in the PROPFIND response of the nodes.
With locking in place, Office suites can collaborate on documents online.
bzr revid: p_christ@hol.gr-20101207134041-8negkvxrbscv7fs7
Diffstat (limited to 'addons/document_webdav/webdav_server.py')
| -rw-r--r-- | addons/document_webdav/webdav_server.py | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/addons/document_webdav/webdav_server.py b/addons/document_webdav/webdav_server.py index 0e5973c95b3..5d73ae292f2 100644 --- a/addons/document_webdav/webdav_server.py +++ b/addons/document_webdav/webdav_server.py @@ -38,7 +38,9 @@ import urllib import re from string import atoi from DAV.errors import * +from DAV.utils import IfParser, TagList # from DAV.constants import DAV_VERSION_1, DAV_VERSION_2 +from xml.dom import minidom khtml_re = re.compile(r' KHTML/([0-9\.]+) ') @@ -103,6 +105,7 @@ class DAVHandler(HttpOptions, FixSendError, DAVRequestHandler): self.headers['Destination'] = up.path[len(self.davpath):] else: raise DAV_Forbidden("Not allowed to copy/move outside webdav path") + # TODO: locks DAVRequestHandler.copymove(self, CLASS) def get_davpath(self): @@ -262,6 +265,139 @@ class DAVHandler(HttpOptions, FixSendError, DAVRequestHandler): except DAV_Error, (ec, dd): return self.send_status(ec) + def do_UNLOCK(self): + """ Unlocks given resource """ + + dc = self.IFACE_CLASS + self.log_message('UNLOCKing resource %s' % self.headers) + + uri = urlparse.urljoin(self.get_baseuri(dc), self.path) + uri = urllib.unquote(uri) + + token = self.headers.get('Lock-Token', False) + if token: + token = token.strip() + if token[0] == '<' and token[-1] == '>': + token = token[1:-1] + else: + token = False + + if not token: + return self.send_status(400, 'Bad lock token') + + try: + res = dc.unlock(uri, token) + except DAV_Error, (ec, dd): + return self.send_status(ec, dd) + + if res == True: + self.send_body(None, '204', 'OK', 'Resource unlocked.') + else: + # We just differentiate the description, for debugging purposes + self.send_body(None, '204', 'OK', 'Resource not locked.') + + def do_LOCK(self): + """ Attempt to place a lock on the given resource. + """ + + dc = self.IFACE_CLASS + lock_data = {} + + self.log_message('LOCKing resource %s' % self.headers) + + body = None + if self.headers.has_key('Content-Length'): + l = self.headers['Content-Length'] + body = self.rfile.read(atoi(l)) + + depth = self.headers.get('Depth', 'infinity') + + uri = urlparse.urljoin(self.get_baseuri(dc), self.path) + uri = urllib.unquote(uri) + self.log_message('do_LOCK: uri = %s' % uri) + + ifheader = self.headers.get('If') + + if ifheader: + ldif = IfParser(ifheader) + if isinstance(ldif, list): + if len(ldif) !=1 or (not isinstance(ldif[0], TagList)) \ + or len(ldif[0].list) != 1: + raise DAV_Error(400, "Cannot accept multiple tokens") + ldif = ldif[0].list[0] + if ldif[0] == '<' and ldif[-1] == '>': + ldif = ldif[1:-1] + + lock_data['token'] = ldif + + if not body: + lock_data['refresh'] = True + else: + lock_data['refresh'] = False + lock_data.update(self._lock_unlock_parse(body)) + + if lock_data['refresh'] and not lock_data.get('token', False): + raise DAV_Error(400, 'Lock refresh must specify token') + + lock_data['depth'] = depth + + try: + created, data, lock_token = dc.lock(uri, lock_data) + except DAV_Error, (ec, dd): + return self.send_status(ec, dd) + + headers = {} + if not lock_data['refresh']: + headers['Lock-Token'] = '<%s>' % lock_token + + if created: + self.send_body(data, '201', 'Created', ctype='text/xml', headers=headers) + else: + self.send_body(data, '200', 'OK', ctype='text/xml', headers=headers) + + def _lock_unlock_parse(self, body): + # Override the python-webdav function, with some improvements + # Unlike the py-webdav one, we also parse the owner minidom elements into + # pure pythonic struct. + doc = minidom.parseString(body) + + data = {} + owners = [] + for info in doc.getElementsByTagNameNS('DAV:', 'lockinfo'): + for scope in info.getElementsByTagNameNS('DAV:', 'lockscope'): + for scc in scope.childNodes: + if scc.nodeType == info.ELEMENT_NODE \ + and scc.namespaceURI == 'DAV:': + data['lockscope'] = scc.localName + break + for ltype in info.getElementsByTagNameNS('DAV:', 'locktype'): + for ltc in ltype.childNodes: + if ltc.nodeType == info.ELEMENT_NODE \ + and ltc.namespaceURI == 'DAV:': + data['locktype'] = ltc.localName + break + for own in info.getElementsByTagNameNS('DAV:', 'owner'): + for ono in own.childNodes: + if ono.nodeType == info.TEXT_NODE: + if ono.data: + owners.append(ono.data) + elif ono.nodeType == info.ELEMENT_NODE \ + and ono.namespaceURI == 'DAV:' \ + and ono.localName == 'href': + href = '' + for hno in ono.childNodes: + if hno.nodeType == info.TEXT_NODE: + href += hno.data + owners.append(('href','DAV:', href)) + + if len(owners) == 1: + data['lockowner'] = owners[0] + elif not owners: + pass + else: + data['lockowner'] = owners + return data + from service.http_server import reg_http_service,OpenERPAuthProvider class DAVAuthProvider(OpenERPAuthProvider): |
