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)
171 fileloc += ';' + uparts[3]
172 if isinstance(fileloc, unicode):
173 fileloc = fileloc.encode('utf-8')
174 href=doc.createElement("D:href")
175 davpath = self._dataclass.parent.get_davpath()
176 if uparts[0] and uparts[1]:
177 hurl = '%s://%s%s%s' % (uparts[0], uparts[1], davpath, urllib.quote(fileloc))
179 # When the request has been relative, we don't have enough data to
180 # reply with absolute url here.
181 hurl = '%s%s' % (davpath, urllib.quote(fileloc))
182 huri=doc.createTextNode(hurl)
183 href.appendChild(huri)
186 # write good properties
187 ps=doc.createElement("D:propstat")
190 s=doc.createElement("D:status")
191 t=doc.createTextNode("HTTP/1.1 200 OK")
195 gp=doc.createElement("D:prop")
196 for ns in good_props.keys():
200 ns_prefix="ns"+str(namespaces.index(ns))+":"
201 for p,v in good_props[ns].items():
204 propgen._prop_child(gp, ns, p, v)
209 # now write the errors!
210 if len(bad_props.items()):
212 # write a propstat for each error code
213 for ecode in bad_props.keys():
214 ps=doc.createElement("D:propstat")
216 s=doc.createElement("D:status")
217 t=doc.createTextNode(utils.gen_estring(ecode))
220 bp=doc.createElement("D:prop")
223 for ns in bad_props[ecode].keys():
227 ns_prefix="ns"+str(self.namespaces.index(ns))+":"
229 for p in bad_props[ecode][ns]:
230 pe=doc.createElement(ns_prefix+str(p))
235 # return the new response element
239 def mk_propname_response(self,uri,propnames,doc):
240 """ make a new <prop> result element for a PROPNAME request
242 This will simply format the propnames list.
243 propnames should have the format {NS1 : [prop1, prop2, ...], NS2: ...}
246 re=doc.createElement("D:response")
248 # write href information
249 uparts=urlparse.urlparse(uri)
252 fileloc += ';' + uparts[3]
253 if isinstance(fileloc, unicode):
254 fileloc = fileloc.encode('utf-8')
255 href=doc.createElement("D:href")
256 davpath = self._dataclass.parent.get_davpath()
257 if uparts[0] and uparts[1]:
258 hurl = '%s://%s%s%s' % (uparts[0], uparts[1], davpath, urllib.quote(fileloc))
260 # When the request has been relative, we don't have enough data to
261 # reply with absolute url here.
262 hurl = '%s%s' % (davpath, urllib.quote(fileloc))
263 huri=doc.createTextNode(hurl)
264 href.appendChild(huri)
267 ps=doc.createElement("D:propstat")
270 for ns,plist in propnames.items():
272 pr=doc.createElement("D:prop")
277 ps.setAttribute("xmlns:"+nsp,ns)
280 # write propertynames
282 pe=doc.createElement(nsp+":"+p)
291 PROPFIND.mk_prop_response = mk_prop_response
292 PROPFIND.mk_propname_response = mk_propname_response
294 def mk_lock_response(self, uri, props):
295 """ Prepare the data response to a DAV LOCK command
297 This function is here, merely to be in the same file as the
298 ones above, that have similar code.
300 doc = domimpl.createDocument('DAV:', "D:prop", None)
301 ms = doc.documentElement
302 ms.setAttribute("xmlns:D", "DAV:")
303 # ms.tagName = 'D:multistatus'
306 propgen = Prop2xml(doc, namespaces, nsnum)
307 # write href information
308 uparts=urlparse.urlparse(uri)
311 fileloc += ';' + uparts[3]
312 if isinstance(fileloc, unicode):
313 fileloc = fileloc.encode('utf-8')
314 davpath = self.parent.get_davpath()
315 if uparts[0] and uparts[1]:
316 hurl = '%s://%s%s%s' % (uparts[0], uparts[1], davpath, urllib.quote(fileloc))
318 # When the request has been relative, we don't have enough data to
319 # reply with absolute url here.
320 hurl = '%s%s' % (davpath, urllib.quote(fileloc))
322 props.append( ('lockroot', 'DAV:', ('href', 'DAV:', (hurl))))
323 pld = doc.createElement('D:lockdiscovery')
325 propgen._prop_child(pld, 'DAV:', 'activelock', props)
327 return doc.toxml(encoding="utf-8")
329 super_create_prop = REPORT.create_prop
331 def create_prop(self):
333 if (self.filter is not None) and self._depth == "0":
334 hrefs = self.filter.getElementsByTagNameNS('DAV:', 'href')
339 return super_create_prop(self)
341 REPORT.create_prop = create_prop
345 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: