Launchpad automatic translations update.
[odoo/odoo.git] / addons / document_webdav / webdav.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
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)
7 #
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.
12 #
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.
17 #
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/>.
20 #
21 ##############################################################################
22
23 import logging
24
25 _logger = logging.getLogger(__name__)
26 import xml.dom.minidom
27 domimpl = xml.dom.minidom.getDOMImplementation()
28 from xml.dom.minicompat import StringTypes
29
30 import urlparse
31 import urllib
32 from openerp.osv import osv
33 from openerp.tools.translate import _
34
35 try:
36     from pywebdav.lib import utils
37     from pywebdav.lib.propfind import PROPFIND
38     from pywebdav.lib.report import REPORT
39 except ImportError:
40     from DAV import utils
41     from DAV.propfind import PROPFIND
42     from DAV.report import REPORT
43
44 from openerp import tools
45
46 class Text2(xml.dom.minidom.Text):
47     def writexml(self, writer, indent="", addindent="", newl=""):
48         data = "%s%s%s" % (indent, self.data, newl)
49         data = data.replace("&", "&amp;").replace("<", "&lt;")
50         data = data.replace(">", "&gt;")
51         writer.write(data)
52
53 class Prop2xml(object):
54     """ A helper class to convert property structs to DAV:XML
55     
56         Written to generalize the use of _prop_child(), a class is 
57         needed to hold some persistent data accross the recursions 
58         of _prop_elem_child().
59     """
60     
61     def __init__(self, doc, namespaces, nsnum):
62         """ Init the structure
63         @param doc the xml doc element
64         @param namespaces a dict of namespaces
65         @param nsnum the next namespace number to define
66         """
67         self.doc = doc
68         self.namespaces = namespaces
69         self.nsnum = nsnum
70
71     def createText2Node(self, data):
72         if not isinstance(data, StringTypes):
73             raise TypeError, "Node contents must be a string."
74         t = Text2()
75         t.data = data
76         t.ownerDocument = self.doc
77         return t
78
79     def _prop_child(self, xnode, ns, prop, value):
80         """Append a property xml node to xnode, with <prop>value</prop>
81
82            And a little smarter than that, it will consider namespace and
83            also allow nested properties etc.
84
85            :param ns the namespace of the <prop/> node
86            :param prop the name of the property
87            :param value the value. Can be:
88                     string: text node
89                     tuple ('elem', 'ns') for empty sub-node <ns:elem />
90                     tuple ('elem', 'ns', sub-elems) for sub-node with elements
91                     tuple ('elem', 'ns', sub-elems, {attrs}) for sub-node with 
92                             optional elements and attributes
93                     list, of above tuples
94         """
95         if ns == 'DAV:':
96             ns_prefix = 'D:'
97         else:
98             ns_prefix="ns"+str(self.namespaces.index(ns))+":"
99
100         pe = self.doc.createElement(ns_prefix+str(prop))
101         if hasattr(value, '__class__') and value.__class__.__name__ == 'Element':
102             pe.appendChild(value)
103         else:
104             if ns == 'DAV:' and prop=="resourcetype" and isinstance(value, int):
105                 # hack, to go..
106                 if value == 1:
107                     ve = self.doc.createElement("D:collection")
108                     pe.appendChild(ve)
109             else:
110                 self._prop_elem_child(pe, ns, value, ns_prefix)
111
112             xnode.appendChild(pe)
113
114     def _prop_elem_child(self, pnode, pns, v, pns_prefix):
115
116         if isinstance(v, list):
117             for vit in v:
118                 self._prop_elem_child(pnode, pns, vit, pns_prefix)
119         elif isinstance(v,tuple):
120             need_ns = False
121             if v[1] == pns:
122                 ns_prefix = pns_prefix
123             elif v[1] == 'DAV:':
124                 ns_prefix = 'D:'
125             elif v[1] in self.namespaces:
126                 ns_prefix="ns"+str(self.namespaces.index(v[1]))+":"
127             else:
128                 ns_prefix="ns"+str(self.nsnum)+":"
129                 need_ns = True
130
131             ve = self.doc.createElement(ns_prefix+v[0])
132             if need_ns:
133                 ve.setAttribute("xmlns:ns"+str(self.nsnum), v[1])
134             if len(v) > 2 and v[2] is not None:
135                 if isinstance(v[2], (list, tuple)):
136                     # support nested elements like:
137                     # ( 'elem', 'ns:', [('sub-elem1', 'ns1'), ...]
138                     self._prop_elem_child(ve, v[1], v[2], ns_prefix)
139                 else:
140                     vt = self.createText2Node(tools.ustr(v[2]))
141                     ve.appendChild(vt)
142             if len(v) > 3 and v[3]:
143                 assert isinstance(v[3], dict)
144                 for ak, av in v[3].items():
145                     ve.setAttribute(ak, av)
146             pnode.appendChild(ve)
147         else:
148             ve = self.createText2Node(tools.ustr(v))
149             pnode.appendChild(ve)
150
151
152 super_mk_prop_response = PROPFIND.mk_prop_response
153 def mk_prop_response(self, uri, good_props, bad_props, doc):
154     """ make a new <prop> result element
155
156     We differ between the good props and the bad ones for
157     each generating an extra <propstat>-Node (for each error
158     one, that means).
159
160     """
161     re=doc.createElement("D:response")
162     # append namespaces to response
163     nsnum=0
164     namespaces = self.namespaces[:]
165     if 'DAV:' in namespaces:
166         namespaces.remove('DAV:')
167     for nsname in namespaces:
168         re.setAttribute("xmlns:ns"+str(nsnum),nsname)
169         nsnum=nsnum+1
170
171     propgen = Prop2xml(doc, namespaces, nsnum)
172     # write href information
173     uparts=urlparse.urlparse(uri)
174     fileloc=uparts[2]
175     if uparts[3]:
176         fileloc += ';' + uparts[3]
177     if isinstance(fileloc, unicode):
178         fileloc = fileloc.encode('utf-8')
179     href=doc.createElement("D:href")
180     davpath = self._dataclass.parent.get_davpath()
181     if uparts[0] and uparts[1]:
182         hurl = '%s://%s%s%s' % (uparts[0], uparts[1], davpath, urllib.quote(fileloc))
183     else:
184         # When the request has been relative, we don't have enough data to
185         # reply with absolute url here.
186         hurl = '%s%s' % (davpath, urllib.quote(fileloc))
187     huri=doc.createTextNode(hurl)
188     href.appendChild(huri)
189     re.appendChild(href)
190
191     # write good properties
192     ps=doc.createElement("D:propstat")
193     if good_props:
194         re.appendChild(ps)
195     s=doc.createElement("D:status")
196     t=doc.createTextNode("HTTP/1.1 200 OK")
197     s.appendChild(t)
198     ps.appendChild(s)
199
200     gp=doc.createElement("D:prop")
201     for ns in good_props.keys():
202         if ns == 'DAV:':
203             ns_prefix = 'D:'
204         else:
205             ns_prefix="ns"+str(namespaces.index(ns))+":"
206         for p,v in good_props[ns].items():
207             if v is None:
208                 continue
209             propgen._prop_child(gp, ns, p, v)
210
211     ps.appendChild(gp)
212     re.appendChild(ps)
213
214     # now write the errors!
215     if len(bad_props.items()):
216
217         # write a propstat for each error code
218         for ecode in bad_props.keys():
219             ps=doc.createElement("D:propstat")
220             re.appendChild(ps)
221             s=doc.createElement("D:status")
222             t=doc.createTextNode(utils.gen_estring(ecode))
223             s.appendChild(t)
224             ps.appendChild(s)
225             bp=doc.createElement("D:prop")
226             ps.appendChild(bp)
227
228             for ns in bad_props[ecode].keys():
229                 if ns == 'DAV:':
230                     ns_prefix='D:'
231                 else:
232                     ns_prefix="ns"+str(self.namespaces.index(ns))+":"
233
234             for p in bad_props[ecode][ns]:
235                 pe=doc.createElement(ns_prefix+str(p))
236                 bp.appendChild(pe)
237
238             re.appendChild(ps)
239
240     # return the new response element
241     return re
242
243
244 def mk_propname_response(self, uri, propnames, doc):
245     """ make a new <prop> result element for a PROPNAME request
246
247     This will simply format the propnames list.
248     propnames should have the format {NS1 : [prop1, prop2, ...], NS2: ...}
249
250     """
251     re=doc.createElement("D:response")
252
253     # write href information
254     uparts=urlparse.urlparse(uri)
255     fileloc=uparts[2]
256     if uparts[3]:
257         fileloc += ';' + uparts[3]
258     if isinstance(fileloc, unicode):
259         fileloc = fileloc.encode('utf-8')
260     href=doc.createElement("D:href")
261     davpath = self._dataclass.parent.get_davpath()
262     if uparts[0] and uparts[1]:
263         hurl = '%s://%s%s%s' % (uparts[0], uparts[1], davpath, urllib.quote(fileloc))
264     else:
265         # When the request has been relative, we don't have enough data to
266         # reply with absolute url here.
267         hurl = '%s%s' % (davpath, urllib.quote(fileloc))
268     huri=doc.createTextNode(hurl)
269     href.appendChild(huri)
270     re.appendChild(href)
271
272     ps=doc.createElement("D:propstat")
273     nsnum=0
274
275     for ns,plist in propnames.items():
276         # write prop element
277         pr=doc.createElement("D:prop")
278         if ns == 'DAV':
279             nsp = 'D'
280         else:
281             nsp="ns"+str(nsnum)
282             ps.setAttribute("xmlns:"+nsp,ns)
283             nsnum=nsnum+1
284
285         # write propertynames
286         for p in plist:
287             pe=doc.createElement(nsp+":"+p)
288             pr.appendChild(pe)
289
290         ps.appendChild(pr)
291
292     re.appendChild(ps)
293
294     return re
295
296 PROPFIND.mk_prop_response = mk_prop_response
297 PROPFIND.mk_propname_response = mk_propname_response
298
299 def mk_lock_response(self, uri, props):
300     """ Prepare the data response to a DAV LOCK command
301     
302     This function is here, merely to be in the same file as the
303     ones above, that have similar code.
304     """
305     doc = domimpl.createDocument('DAV:', "D:prop", None)
306     ms = doc.documentElement
307     ms.setAttribute("xmlns:D", "DAV:")
308     # ms.tagName = 'D:multistatus'
309     namespaces = []
310     nsnum = 0
311     propgen = Prop2xml(doc, namespaces, nsnum)
312     # write href information
313     uparts=urlparse.urlparse(uri)
314     fileloc=uparts[2]
315     if uparts[3]:
316         fileloc += ';' + uparts[3]
317     if isinstance(fileloc, unicode):
318         fileloc = fileloc.encode('utf-8')
319     davpath = self.parent.get_davpath()
320     if uparts[0] and uparts[1]:
321         hurl = '%s://%s%s%s' % (uparts[0], uparts[1], davpath, urllib.quote(fileloc))
322     else:
323         # When the request has been relative, we don't have enough data to
324         # reply with absolute url here.
325         hurl = '%s%s' % (davpath, urllib.quote(fileloc))
326         
327     props.append( ('lockroot', 'DAV:', ('href', 'DAV:', (hurl))))
328     pld = doc.createElement('D:lockdiscovery')
329     ms.appendChild(pld)
330     propgen._prop_child(pld, 'DAV:', 'activelock', props)
331
332     return doc.toxml(encoding="utf-8")
333
334 super_create_prop = REPORT.create_prop
335
336 def create_prop(self):
337     try:
338         if (self.filter is not None) and self._depth == "0":
339             hrefs = self.filter.getElementsByTagNameNS('DAV:', 'href')
340             if hrefs:
341                 self._depth = "1"
342     except Exception:
343         pass
344     return super_create_prop(self)
345
346 REPORT.create_prop = create_prop
347
348 #eof
349
350 # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: