Doc Webdav: reply with relative URIs when the request was relative.
[odoo/odoo.git] / addons / document_webdav / webdav.py
index 2406bc1..bb2b8ca 100644 (file)
@@ -3,6 +3,7 @@
 #
 #    OpenERP, Open Source Management Solution
 #    Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
+#    Copyright (c) 1999 Christian Scholz (ruebe@aachen.heimat.de)
 #
 #    This program is free software: you can redistribute it and/or modify
 #    it under the terms of the GNU Affero General Public License as
 
 import xml.dom.minidom
 domimpl = xml.dom.minidom.getDOMImplementation()
+from xml.dom.minicompat import StringTypes
+
 import urlparse
 import urllib
-from DAV import utils
-from DAV.propfind import PROPFIND
+from osv import osv
+
+try:
+    from DAV import utils
+    from DAV.propfind import PROPFIND
+    from DAV.report import REPORT
+except ImportError:
+    raise osv.except_osv('PyWebDAV Import Error!','Please install PyWebDAV \
+from http://code.google.com/p/pywebdav/downloads/detail?name=PyWebDAV-0.9.4.tar.gz&can=2&q=/')
+
 import tools
 
+class Text2(xml.dom.minidom.Text):
+    def writexml(self, writer, indent="", addindent="", newl=""):
+        data = "%s%s%s" % (indent, self.data, newl)
+        data = data.replace("&", "&amp;").replace("<", "&lt;")
+        data = data.replace(">", "&gt;")
+        writer.write(data)
+
+def createText2Node(doc, data):
+    if not isinstance(data, StringTypes):
+        raise TypeError, "node contents must be a string"
+    t = Text2()
+    t.data = data
+    t.ownerDocument = doc
+    return t
+
 
 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 
+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:
+    namespaces = self.namespaces[:]
+    if 'DAV:' in namespaces:
+        namespaces.remove('DAV:')
+    for nsname in namespaces:
         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 <prop>value</prop>
+
+           And a little smarter than that, it will consider namespace and
+           also allow nested properties etc.
+
+           :param ns the namespace of the <prop/> node
+           :param prop the name of the property
+           :param value the value. Can be:
+                    string: text node
+                    tuple ('elem', 'ns') for empty sub-node <ns:elem />
+                    tuple ('elem', 'ns', sub-elems) for sub-node with elements
+                    tuple ('elem', 'ns', sub-elems, {attrs}) for sub-node with 
+                            optional elements and attributes
+                    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:
+                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 v[2] is not None:
+                if isinstance(v[2], (list, tuple)):
+                    # support nested elements like:
+                    # ( 'elem', 'ns:', [('sub-elem1', 'ns1'), ...]
+                    _prop_elem_child(ve, v[1], v[2], ns_prefix)
+                else:
+                    vt=createText2Node(doc,tools.ustr(v[2]))
+                    ve.appendChild(vt)
+            if len(v) > 3 and v[3]:
+                assert isinstance(v[3], dict)
+                for ak, av in v[3].items():
+                    ve.setAttribute(ak, av)
+            pnode.appendChild(ve)
+        else:
+            ve=createText2Node(doc, tools.ustr(v))
+            pnode.appendChild(ve)
+
     # write href information
     uparts=urlparse.urlparse(uri)
     fileloc=uparts[2]
+    if isinstance(fileloc, unicode):
+        fileloc = fileloc.encode('utf-8')
     href=doc.createElement("D:href")
-    huri=doc.createTextNode(uparts[0]+'://'+'/'.join(uparts[1:2]) + urllib.quote(fileloc))
+    davpath = self._dataclass.parent.get_davpath()
+    if uparts[0] and uparts[1]:
+        hurl = '%s://%s%s%s' % (uparts[0], uparts[1], davpath, urllib.quote(fileloc))
+    else:
+        # When the request has been relative, we don't have enough data to
+        # reply with absolute url here.
+        hurl = '%s%s' % (davpath, urllib.quote(fileloc))
+    huri=doc.createTextNode(hurl)
     href.appendChild(huri)
     re.appendChild(href)
 
@@ -56,32 +166,23 @@ def mk_prop_response(self, uri, good_props, bad_props, doc):
     ps=doc.createElement("D:propstat")
     if good_props:
         re.appendChild(ps)
+    s=doc.createElement("D:status")
+    t=doc.createTextNode("HTTP/1.1 200 OK")
+    s.appendChild(t)
+    ps.appendChild(s)
 
     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)
+        if ns == 'DAV:':
+            ns_prefix = 'D:'
+        else:
+            ns_prefix="ns"+str(namespaces.index(ns))+":"
+        for p,v in good_props[ns].items():
+            if v is None:
+                continue
+            _prop_child(gp, ns, p, v)
 
-            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!
@@ -91,25 +192,94 @@ def mk_prop_response(self, uri, good_props, bad_props, doc):
         for ecode in bad_props.keys():
             ps=doc.createElement("D:propstat")
             re.appendChild(ps)
+            s=doc.createElement("D:status")
+            t=doc.createTextNode(utils.gen_estring(ecode))
+            s.appendChild(t)
+            ps.appendChild(s)
             bp=doc.createElement("D:prop")
             ps.appendChild(bp)
 
             for ns in bad_props[ecode].keys():
-                ns_prefix="ns"+str(self.namespaces.index(ns))+":"
-            
+                if ns == 'DAV:':
+                    ns_prefix='D:'
+                else:
+                    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 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
+    uparts=urlparse.urlparse(uri)
+    fileloc=uparts[2]
+    if isinstance(fileloc, unicode):
+        fileloc = fileloc.encode('utf-8')
+    href=doc.createElement("D:href")
+    davpath = self._dataclass.parent.get_davpath()
+    if uparts[0] and uparts[1]:
+        hurl = '%s://%s%s%s' % (uparts[0], uparts[1], davpath, urllib.quote(fileloc))
+    else:
+        # When the request has been relative, we don't have enough data to
+        # reply with absolute url here.
+        hurl = '%s%s' % (davpath, urllib.quote(fileloc))
+    huri=doc.createTextNode(hurl)
+    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")
+        if ns == 'DAV':
+            nsp = 'D'
+        else:
+            nsp="ns"+str(nsnum)
+            ps.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
 
 PROPFIND.mk_prop_response = mk_prop_response
+PROPFIND.mk_propname_response = mk_propname_response
+
+super_create_prop = REPORT.create_prop
+
+def create_prop(self):
+    try:
+        if (self.filter is not None) and self._depth == "0":
+            hrefs = self.filter.getElementsByTagNameNS('DAV:', 'href')
+            if hrefs:
+                self._depth = "1"
+    except Exception:
+        pass
+    return super_create_prop(self)
+
+REPORT.create_prop = create_prop
 
+#eof