Reported by: Dr. Ferdinand Gassauer Locations that contain a semicolon are split by the urlparse.urlparse(), thus setting their 'path' and 'params' return fields (rather than just the 'path'). In our syntax, there is no params, so the part after the semicolon needs to be appended back to the patch. bzr revid: p_christ@hol.gr-20110118165021-4knt76njdqnt1eln
344 lines
11 KiB
Python
344 lines
11 KiB
Python
# -*- coding: utf-8 -*-
|
|
##############################################################################
|
|
#
|
|
# 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
|
|
# published by the Free Software Foundation, either version 3 of the
|
|
# License, or (at your option) any later version.
|
|
#
|
|
# This program 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 Affero General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
##############################################################################
|
|
|
|
import xml.dom.minidom
|
|
domimpl = xml.dom.minidom.getDOMImplementation()
|
|
from xml.dom.minicompat import StringTypes
|
|
|
|
import urlparse
|
|
import urllib
|
|
from osv import osv
|
|
from tools.translate import _
|
|
|
|
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("&", "&").replace("<", "<")
|
|
data = data.replace(">", ">")
|
|
writer.write(data)
|
|
|
|
class Prop2xml(object):
|
|
""" A helper class to convert property structs to DAV:XML
|
|
|
|
Written to generalize the use of _prop_child(), a class is
|
|
needed to hold some persistent data accross the recursions
|
|
of _prop_elem_child().
|
|
"""
|
|
|
|
def __init__(self, doc, namespaces, nsnum):
|
|
""" Init the structure
|
|
@param doc the xml doc element
|
|
@param namespaces a dict of namespaces
|
|
@param nsnum the next namespace number to define
|
|
"""
|
|
self.doc = doc
|
|
self.namespaces = namespaces
|
|
self.nsnum = nsnum
|
|
|
|
def createText2Node(self, data):
|
|
if not isinstance(data, StringTypes):
|
|
raise TypeError, "node contents must be a string"
|
|
t = Text2()
|
|
t.data = data
|
|
t.ownerDocument = self.doc
|
|
return t
|
|
|
|
def _prop_child(self, 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(self.namespaces.index(ns))+":"
|
|
|
|
pe = self.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 = self.doc.createElement("D:collection")
|
|
pe.appendChild(ve)
|
|
else:
|
|
self._prop_elem_child(pe, ns, value, ns_prefix)
|
|
|
|
xnode.appendChild(pe)
|
|
|
|
def _prop_elem_child(self, pnode, pns, v, pns_prefix):
|
|
|
|
if isinstance(v, list):
|
|
for vit in v:
|
|
self._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 self.namespaces:
|
|
ns_prefix="ns"+str(self.namespaces.index(v[1]))+":"
|
|
else:
|
|
ns_prefix="ns"+str(self.nsnum)+":"
|
|
need_ns = True
|
|
|
|
ve = self.doc.createElement(ns_prefix+v[0])
|
|
if need_ns:
|
|
ve.setAttribute("xmlns:ns"+str(self.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'), ...]
|
|
self._prop_elem_child(ve, v[1], v[2], ns_prefix)
|
|
else:
|
|
vt = self.createText2Node(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 = self.createText2Node(tools.ustr(v))
|
|
pnode.appendChild(ve)
|
|
|
|
|
|
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
|
|
namespaces = self.namespaces[:]
|
|
if 'DAV:' in namespaces:
|
|
namespaces.remove('DAV:')
|
|
for nsname in namespaces:
|
|
re.setAttribute("xmlns:ns"+str(nsnum),nsname)
|
|
nsnum=nsnum+1
|
|
|
|
propgen = Prop2xml(doc, namespaces, nsnum)
|
|
# write href information
|
|
uparts=urlparse.urlparse(uri)
|
|
fileloc=uparts[2]
|
|
if uparts[3]:
|
|
fileloc += ';' + uparts[3]
|
|
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)
|
|
|
|
# write good properties
|
|
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():
|
|
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
|
|
propgen._prop_child(gp, ns, p, v)
|
|
|
|
ps.appendChild(gp)
|
|
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)
|
|
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():
|
|
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)
|
|
|
|
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 uparts[3]:
|
|
fileloc += ';' + uparts[3]
|
|
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
|
|
|
|
def mk_lock_response(self, uri, props):
|
|
""" Prepare the data response to a DAV LOCK command
|
|
|
|
This function is here, merely to be in the same file as the
|
|
ones above, that have similar code.
|
|
"""
|
|
doc = domimpl.createDocument('DAV:', "D:prop", None)
|
|
ms = doc.documentElement
|
|
ms.setAttribute("xmlns:D", "DAV:")
|
|
# ms.tagName = 'D:multistatus'
|
|
namespaces = []
|
|
nsnum = 0
|
|
propgen = Prop2xml(doc, namespaces, nsnum)
|
|
# write href information
|
|
uparts=urlparse.urlparse(uri)
|
|
fileloc=uparts[2]
|
|
if uparts[3]:
|
|
fileloc += ';' + uparts[3]
|
|
if isinstance(fileloc, unicode):
|
|
fileloc = fileloc.encode('utf-8')
|
|
davpath = self.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))
|
|
|
|
props.append( ('lockroot', 'DAV:', ('href', 'DAV:', (hurl))))
|
|
pld = doc.createElement('D:lockdiscovery')
|
|
ms.appendChild(pld)
|
|
propgen._prop_child(pld, 'DAV:', 'activelock', props)
|
|
|
|
return doc.toxml(encoding="utf-8")
|
|
|
|
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
|