diff --git a/addons/caldav/caldav.py b/addons/caldav/caldav.py index 3aa5dfd14dc..2517f88f789 100644 --- a/addons/caldav/caldav.py +++ b/addons/caldav/caldav.py @@ -20,26 +20,8 @@ ############################################################################## from document_webdav import webdav -import tools -from DAV.propfind import PROPFIND import urlparse urlparse.uses_netloc.append('caldav') urlparse.uses_netloc.append('caldavs') -super_mk_prop_response = webdav.mk_prop_response -def mk_prop_response(self,uri,good_props,bad_props,doc): - res = super_mk_prop_response(self, uri,good_props,bad_props,doc) - uris = uri.split('/') - calendar = False - if 'http://calendarserver.org/ns/' in good_props or 'http://calendarserver.org/ns/' in bad_props: - calendar = True - if calendar: - ad = doc.createElement('calendar') - ad.setAttribute('xmlns', 'urn:ietf:params:xml:ns:caldav') - cols = res.getElementsByTagName('D:collection') - if cols: - cols[0].parentNode.appendChild(ad) - #cols[0].parentNode.appendChild(vc) - return res - -PROPFIND.mk_prop_response = mk_prop_response +#EOF diff --git a/addons/caldav/caldav_node.py b/addons/caldav/caldav_node.py index 3ae37ea3e7e..b931b0f0328 100644 --- a/addons/caldav/caldav_node.py +++ b/addons/caldav/caldav_node.py @@ -133,25 +133,10 @@ class node_calendar(nodes.node_class): result = self._get_ttag(cr) + ':' + str(time.time()) return str(result) - def _get_gdav_resourcetype(self, cr): - return (str(self.cal_type + '-collection'), 'http://groupdav.org/') - - def removeme_match_dav_eprop(self, cr, match, ns, prop): - # Why? - if ns == "DAV:" and prop == "getetag": - dirobj = self.context._dirobj - uid = self.context.uid - ctx = self.context.context.copy() - tem, dav_time = tuple(match.split(':')) - model, res_id = tuple(tem.split('_')) - model_obj = dirobj.pool.get(model) - model = model_obj.browse(cr, uid, res_id, context=ctx) - write_time = model.write_date or model.create_date - wtime = time.mktime(time.strptime(write_time,'%Y-%m-%d %H:%M:%S')) - if float(dav_time) == float(wtime): - return True - return False - res = super(node_calendar, self).match_dav_eprop(cr, match, ns, prop) + def get_dav_resourcetype(self, cr): + res = [ ('collection', 'DAV:'), + (str(self.cal_type + '-collection'), 'http://groupdav.org/'), + ('calendar', 'urn:ietf:params:xml:ns:caldav') ] return res def get_domain(self, cr, filters): diff --git a/addons/document/nodes.py b/addons/document/nodes.py index de2a83285f9..228f104574d 100644 --- a/addons/document/nodes.py +++ b/addons/document/nodes.py @@ -314,6 +314,14 @@ class node_class(object): logger.debug('Property %s not supported' % prop, exc_info=True) return None + def get_dav_resourcetype(self, cr): + """ Get the DAV resource type. + + Is here because some nodes may exhibit special behaviour, like + CalDAV/GroupDAV collections + """ + raise NotImplementedError + def move_to(self, cr, ndir_node, new_name=False, fil_obj=None, ndir_obj=None, in_write=False): """ Move this node to a new parent directory. @param ndir_node the collection that this node should be moved under @@ -456,6 +464,9 @@ class node_database(node_class): def _get_ttag(self,cr): return 'db-%s' % cr.dbname + def get_dav_resourcetype(self, cr): + return ('collection', 'DAV:') + def mkdosname(company_name, default='noname'): """ convert a string to a dos-like name""" if not company_name: @@ -778,6 +789,9 @@ class node_res_dir(node_class): def _get_ttag(self,cr): return 'rdir-%d' % self.dir_id + def get_dav_resourcetype(self, cr): + return ('collection', 'DAV:') + class node_res_obj(node_class): """ A special sibling to node_dir, which does only contain dynamically created folders foreach resource in the foreign model. @@ -1034,6 +1048,9 @@ class node_res_obj(node_class): def _get_ttag(self,cr): return 'rodir-%d-%d' % (self.dir_id, self.res_id) + def get_dav_resourcetype(self, cr): + return ('collection', 'DAV:') + class node_file(node_class): our_type = 'file' def __init__(self, path, parent, context, fil): @@ -1168,6 +1185,9 @@ class node_file(node_class): def _get_ttag(self,cr): return 'file-%d' % self.file_id + def get_dav_resourcetype(self, cr): + return '' + def move_to(self, cr, ndir_node, new_name=False, fil_obj=None, ndir_obj=None, in_write=False): if ndir_node.context != self.context: raise NotImplementedError("Cannot move files between contexts") @@ -1313,6 +1333,8 @@ class node_content(node_class): def _get_ttag(self,cr): return 'cnt-%d%s' % (self.cnt_id,(self.act_id and ('-' + str(self.act_id))) or '') + def get_dav_resourcetype(self, cr): + return '' class nodefd_content(StringIO, node_descriptor): """ A descriptor to content nodes diff --git a/addons/document_webdav/dav_fs.py b/addons/document_webdav/dav_fs.py index 78812ff8cf5..af8738ef0da 100644 --- a/addons/document_webdav/dav_fs.py +++ b/addons/document_webdav/dav_fs.py @@ -370,19 +370,22 @@ class openerp_dav_handler(dav_interface): if cr: cr.close() @memoize(CACHE_SIZE) - def _get_dav_resourcetype(self,uri): + def _get_dav_resourcetype(self, uri): """ return type of object """ self.parent.log_message('get RT: %s' % uri) cr, uid, pool, dbname, uri2 = self.get_cr(uri) try: if not dbname: return COLLECTION - node = self.uri2object(cr,uid,pool, uri2) + node = self.uri2object(cr, uid, pool, uri2) if not node: raise DAV_NotFound2(uri2) - if node.type in ('collection','database'): - return COLLECTION - return OBJECT + try: + return node.get_dav_resourcetype(cr) + except NotImplementedError: + if node.type in ('collection','database'): + return ('collection', 'DAV:') + return '' finally: if cr: cr.close() @@ -739,6 +742,17 @@ class openerp_dav_handler(dav_interface): @memoize(CACHE_SIZE) def is_collection(self, uri): """ test if the given uri is a collection """ - return self._get_dav_resourcetype(uri)==COLLECTION + cr, uid, pool, dbname, uri2 = self.get_cr(uri) + try: + if not dbname: + return True + node = self.uri2object(cr,uid,pool, uri2) + if not node: + raise DAV_NotFound2(uri2) + if node.type in ('collection','database'): + return True + return False + finally: + if cr: cr.close() #eof diff --git a/addons/document_webdav/webdav.py b/addons/document_webdav/webdav.py index 1502ff204f8..1fd530cbdca 100644 --- a/addons/document_webdav/webdav.py +++ b/addons/document_webdav/webdav.py @@ -48,6 +48,70 @@ def mk_prop_response(self, uri, good_props, bad_props, doc): re.setAttribute("xmlns:ns"+str(nsnum),nsname) nsnum=nsnum+1 + def _prop_child(xnode, ns, prop, value): + """Append a property xml node to xnode, with value + + And a little smarter than that, it will consider namespace and + also allow nested properties etc. + + :param ns the namespace of the node + :param prop the name of the property + :param value the value. Can be: + string: text node + tuple ('elem', 'ns') for empty sub-node + tuple ('elem', 'ns', sub-elems) for sub-node with elements + list, of above tuples + """ + if ns == 'DAV:': + ns_prefix = 'D:' + else: + ns_prefix="ns"+str(namespaces.index(ns))+":" + + pe=doc.createElement(ns_prefix+str(prop)) + if hasattr(value, '__class__') and value.__class__.__name__ == 'Element': + pe.appendChild(value) + else: + if ns == 'DAV:' and prop=="resourcetype" and isinstance(value, int): + # hack, to go.. + if value == 1: + ve=doc.createElement("D:collection") + pe.appendChild(ve) + else: + _prop_elem_child(pe, ns, value, ns_prefix) + + xnode.appendChild(pe) + + def _prop_elem_child(pnode, pns, v, pns_prefix): + + if isinstance(v, list): + for vit in v: + _prop_elem_child(pnode, pns, vit, pns_prefix) + elif isinstance(v,tuple): + need_ns = False + if v[1] == pns: + ns_prefix = pns_prefix + elif v[1] == 'DAV:': + ns_prefix = 'D:' + elif v[1] in namespaces: + ns_prefix="ns"+str(namespaces.index(v[1]))+":" + else: + # namespaces.append(v[1]) + # nsnum += 1 + ns_prefix="ns"+str(nsnum)+":" + need_ns = True + + ve=doc.createElement(ns_prefix+v[0]) + if need_ns: + ve.setAttribute("xmlns:ns"+str(nsnum), v[1]) + if len(v) > 2 and isinstance(v[2], list): + # support nested elements like: + # ( 'elem', 'ns:', [('sub-elem1', 'ns1'), ...] + _prop_elem_child(ve, v[1], v[2], ns_prefix) + pnode.appendChild(ve) + else: + ve=doc.createTextNode(tools.ustr(v)) + pnode.appendChild(ve) + # write href information uparts=urlparse.urlparse(uri) fileloc=uparts[2] @@ -74,22 +138,7 @@ def mk_prop_response(self, uri, good_props, bad_props, doc): for p,v in good_props[ns].items(): if not v: continue - pe=doc.createElement(ns_prefix+str(p)) - if hasattr(v, '__class__') and v.__class__.__name__ == 'Element': - pe.appendChild(v) - else: - if 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(tools.ustr(v)) - pe.appendChild(ve) - - gp.appendChild(pe) + _prop_child(gp, ns, p, v) ps.appendChild(gp) s=doc.createElement("D:status") diff --git a/addons/document_webdav/webdav_server.py b/addons/document_webdav/webdav_server.py index 0f306a70612..34c255a2b3e 100644 --- a/addons/document_webdav/webdav_server.py +++ b/addons/document_webdav/webdav_server.py @@ -131,6 +131,22 @@ class DAVHandler(HttpOptions, FixSendError, DAVRequestHandler): self.close_connection = 1 DAVRequestHandler.send_header(self, key, value) + def send_body(self, DATA, code = None, msg = None, desc = None, ctype='application/octet-stream', headers=None): + if headers and 'Connection' in headers: + pass + elif self.request_version in ('HTTP/1.0', 'HTTP/0.9'): + pass + elif self.close_connection == 1: # close header already sent + pass + else: + if headers is None: + headers = {} + if self.headers.get('Connection',False) == 'Keep-Alive': + headers['Connection'] = 'keep-alive' + + DAVRequestHandler.send_body(self, DATA, code=code, msg=msg, desc=desc, + ctype=ctype, headers=headers) + def do_PUT(self): dc=self.IFACE_CLASS uri=urlparse.urljoin(self.get_baseuri(dc), self.path) @@ -141,6 +157,8 @@ class DAVHandler(HttpOptions, FixSendError, DAVRequestHandler): etag = None for match in self.headers['If-Match'].split(','): + if match.startswith('"') and match.endswith('"'): + match = match[1:-1] if match == '*': if dc.exists(uri): test = True @@ -150,6 +168,7 @@ class DAVHandler(HttpOptions, FixSendError, DAVRequestHandler): test = True break if not test: + self._get_body() self.send_status(412) return @@ -167,6 +186,7 @@ class DAVHandler(HttpOptions, FixSendError, DAVRequestHandler): test = False break if not test: + self._get_body() self.send_status(412) return @@ -179,10 +199,7 @@ class DAVHandler(HttpOptions, FixSendError, DAVRequestHandler): self._flush() # read the body - body=None - if self.headers.has_key("Content-Length"): - l=self.headers['Content-Length'] - body=self.rfile.read(atoi(l)) + body=self._get_body() # locked resources are not allowed to be overwritten if self._l_isLocked(uri): @@ -208,6 +225,13 @@ class DAVHandler(HttpOptions, FixSendError, DAVRequestHandler): self.send_body(None, '201', 'Created', '', headers=headers) + def _get_body(self): + body = None + if self.headers.has_key("Content-Length"): + l=self.headers['Content-Length'] + body=self.rfile.read(atoi(l)) + return body + def do_DELETE(self): try: DAVRequestHandler.do_DELETE(self)