Files
odoo_source/addons/document_webdav/webdav.py
T
P. Christeas 9b0993e74c webdav: ';' in name not handled correctly [Bug 704444]
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
2011-01-18 18:50:21 +02:00

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("&", "&amp;").replace("<", "&lt;")
data = data.replace(">", "&gt;")
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