1 # -*- coding: utf-8 -*-
2 ##############################################################################
4 # OpenERP, Open Source Management Solution
5 # Copyright (C) 2004-2010 Tiny SPRL (<http://tiny.be>).
6 # Copyright (c) 1999 Christian Scholz (ruebe@aachen.heimat.de)
8 # This program is free software: you can redistribute it and/or modify
9 # it under the terms of the GNU Affero General Public License as
10 # published by the Free Software Foundation, either version 3 of the
11 # License, or (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU Affero General Public License for more details.
18 # You should have received a copy of the GNU Affero General Public License
19 # along with this program. If not, see <http://www.gnu.org/licenses/>.
21 ##############################################################################
23 import xml.dom.minidom
24 domimpl = xml.dom.minidom.getDOMImplementation()
25 from xml.dom.minicompat import StringTypes
30 from tools.translate import _
34 from DAV.propfind import PROPFIND
35 from DAV.report import REPORT
37 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=/'))
41 class Text2(xml.dom.minidom.Text):
42 def writexml(self, writer, indent="", addindent="", newl=""):
43 data = "%s%s%s" % (indent, self.data, newl)
44 data = data.replace("&", "&").replace("<", "<")
45 data = data.replace(">", ">")
48 class Prop2xml(object):
49 """ A helper class to convert property structs to DAV:XML
51 Written to generalize the use of _prop_child(), a class is
52 needed to hold some persistent data accross the recursions
53 of _prop_elem_child().
56 def __init__(self, doc, namespaces, nsnum):
57 """ Init the structure
58 @param doc the xml doc element
59 @param namespaces a dict of namespaces
60 @param nsnum the next namespace number to define
63 self.namespaces = namespaces
66 def createText2Node(self, data):
67 if not isinstance(data, StringTypes):
68 raise TypeError, "node contents must be a string"
71 t.ownerDocument = self.doc
74 def _prop_child(self, xnode, ns, prop, value):
75 """Append a property xml node to xnode, with <prop>value</prop>
77 And a little smarter than that, it will consider namespace and
78 also allow nested properties etc.
80 :param ns the namespace of the <prop/> node
81 :param prop the name of the property
82 :param value the value. Can be:
84 tuple ('elem', 'ns') for empty sub-node <ns:elem />
85 tuple ('elem', 'ns', sub-elems) for sub-node with elements
86 tuple ('elem', 'ns', sub-elems, {attrs}) for sub-node with
87 optional elements and attributes
93 ns_prefix="ns"+str(self.namespaces.index(ns))+":"
95 pe = self.doc.createElement(ns_prefix+str(prop))
96 if hasattr(value, '__class__') and value.__class__.__name__ == 'Element':
99 if ns == 'DAV:' and prop=="resourcetype" and isinstance(value, int):
102 ve = self.doc.createElement("D:collection")
105 self._prop_elem_child(pe, ns, value, ns_prefix)
107 xnode.appendChild(pe)
109 def _prop_elem_child(self, pnode, pns, v, pns_prefix):
111 if isinstance(v, list):
113 self._prop_elem_child(pnode, pns, vit, pns_prefix)
114 elif isinstance(v,tuple):
117 ns_prefix = pns_prefix
120 elif v[1] in self.namespaces:
121 ns_prefix="ns"+str(self.namespaces.index(v[1]))+":"
123 ns_prefix="ns"+str(self.nsnum)+":"
126 ve = self.doc.createElement(ns_prefix+v[0])
128 ve.setAttribute("xmlns:ns"+str(self.nsnum), v[1])
129 if len(v) > 2 and v[2] is not None:
130 if isinstance(v[2], (list, tuple)):
131 # support nested elements like:
132 # ( 'elem', 'ns:', [('sub-elem1', 'ns1'), ...]
133 self._prop_elem_child(ve, v[1], v[2], ns_prefix)
135 vt = self.createText2Node(tools.ustr(v[2]))
137 if len(v) > 3 and v[3]:
138 assert isinstance(v[3], dict)
139 for ak, av in v[3].items():
140 ve.setAttribute(ak, av)
141 pnode.appendChild(ve)
143 ve = self.createText2Node(tools.ustr(v))
144 pnode.appendChild(ve)
147 super_mk_prop_response = PROPFIND.mk_prop_response
148 def mk_prop_response(self, uri, good_props, bad_props, doc):
149 """ make a new <prop> result element
151 We differ between the good props and the bad ones for
152 each generating an extra <propstat>-Node (for each error
156 re=doc.createElement("D:response")
157 # append namespaces to response
159 namespaces = self.namespaces[:]
160 if 'DAV:' in namespaces:
161 namespaces.remove('DAV:')
162 for nsname in namespaces:
163 re.setAttribute("xmlns:ns"+str(nsnum),nsname)
166 propgen = Prop2xml(doc, namespaces, nsnum)
167 # write href information
168 uparts=urlparse.urlparse(uri)
170 if isinstance(fileloc, unicode):
171 fileloc = fileloc.encode('utf-8')
172 href=doc.createElement("D:href")
173 davpath = self._dataclass.parent.get_davpath()
174 if uparts[0] and uparts[1]:
175 hurl = '%s://%s%s%s' % (uparts[0], uparts[1], davpath, urllib.quote(fileloc))
177 # When the request has been relative, we don't have enough data to
178 # reply with absolute url here.
179 hurl = '%s%s' % (davpath, urllib.quote(fileloc))
180 huri=doc.createTextNode(hurl)
181 href.appendChild(huri)
184 # write good properties
185 ps=doc.createElement("D:propstat")
188 s=doc.createElement("D:status")
189 t=doc.createTextNode("HTTP/1.1 200 OK")
193 gp=doc.createElement("D:prop")
194 for ns in good_props.keys():
198 ns_prefix="ns"+str(namespaces.index(ns))+":"
199 for p,v in good_props[ns].items():
202 propgen._prop_child(gp, ns, p, v)
207 # now write the errors!
208 if len(bad_props.items()):
210 # write a propstat for each error code
211 for ecode in bad_props.keys():
212 ps=doc.createElement("D:propstat")
214 s=doc.createElement("D:status")
215 t=doc.createTextNode(utils.gen_estring(ecode))
218 bp=doc.createElement("D:prop")
221 for ns in bad_props[ecode].keys():
225 ns_prefix="ns"+str(self.namespaces.index(ns))+":"
227 for p in bad_props[ecode][ns]:
228 pe=doc.createElement(ns_prefix+str(p))
233 # return the new response element
237 def mk_propname_response(self,uri,propnames,doc):
238 """ make a new <prop> result element for a PROPNAME request
240 This will simply format the propnames list.
241 propnames should have the format {NS1 : [prop1, prop2, ...], NS2: ...}
244 re=doc.createElement("D:response")
246 # write href information
247 uparts=urlparse.urlparse(uri)
249 if isinstance(fileloc, unicode):
250 fileloc = fileloc.encode('utf-8')
251 href=doc.createElement("D:href")
252 davpath = self._dataclass.parent.get_davpath()
253 if uparts[0] and uparts[1]:
254 hurl = '%s://%s%s%s' % (uparts[0], uparts[1], davpath, urllib.quote(fileloc))
256 # When the request has been relative, we don't have enough data to
257 # reply with absolute url here.
258 hurl = '%s%s' % (davpath, urllib.quote(fileloc))
259 huri=doc.createTextNode(hurl)
260 href.appendChild(huri)
263 ps=doc.createElement("D:propstat")
266 for ns,plist in propnames.items():
268 pr=doc.createElement("D:prop")
273 ps.setAttribute("xmlns:"+nsp,ns)
276 # write propertynames
278 pe=doc.createElement(nsp+":"+p)
287 PROPFIND.mk_prop_response = mk_prop_response
288 PROPFIND.mk_propname_response = mk_propname_response
290 def mk_lock_response(self, uri, props):
291 """ Prepare the data response to a DAV LOCK command
293 This function is here, merely to be in the same file as the
294 ones above, that have similar code.
296 doc = domimpl.createDocument('DAV:', "D:prop", None)
297 ms = doc.documentElement
298 ms.setAttribute("xmlns:D", "DAV:")
299 # ms.tagName = 'D:multistatus'
302 propgen = Prop2xml(doc, namespaces, nsnum)
303 # write href information
304 uparts=urlparse.urlparse(uri)
306 if isinstance(fileloc, unicode):
307 fileloc = fileloc.encode('utf-8')
308 davpath = self.parent.get_davpath()
309 if uparts[0] and uparts[1]:
310 hurl = '%s://%s%s%s' % (uparts[0], uparts[1], davpath, urllib.quote(fileloc))
312 # When the request has been relative, we don't have enough data to
313 # reply with absolute url here.
314 hurl = '%s%s' % (davpath, urllib.quote(fileloc))
316 props.append( ('lockroot', 'DAV:', ('href', 'DAV:', (hurl))))
317 pld = doc.createElement('D:lockdiscovery')
319 propgen._prop_child(pld, 'DAV:', 'activelock', props)
321 return doc.toxml(encoding="utf-8")
323 super_create_prop = REPORT.create_prop
325 def create_prop(self):
327 if (self.filter is not None) and self._depth == "0":
328 hrefs = self.filter.getElementsByTagNameNS('DAV:', 'href')
333 return super_create_prop(self)
335 REPORT.create_prop = create_prop