[FIX] yaml: more explicit error if !record has missing parameters
[odoo/odoo.git] / openerp / tools / html_sanitize.py
1 # -*- coding: utf-8 -*-
2 ##############################################################################
3 #
4 #    OpenERP, Open Source Business Applications
5 #    Copyright (C) 2012 OpenERP S.A. (<http://openerp.com>).
6 #
7 #    This program is free software: you can redistribute it and/or modify
8 #    it under the terms of the GNU Affero General Public License as
9 #    published by the Free Software Foundation, either version 3 of the
10 #    License, or (at your option) any later version.
11 #
12 #    This program is distributed in the hope that it will be useful,
13 #    but WITHOUT ANY WARRANTY; without even the implied warranty of
14 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 #    GNU Affero General Public License for more details.
16 #
17 #    You should have received a copy of the GNU Affero General Public License
18 #    along with this program.  If not, see <http://www.gnu.org/licenses/>.
19 #
20 ##############################################################################
21
22 import lxml.html
23 import operator
24 import re
25
26 from openerp.loglevels import ustr
27
28 def html_sanitize(src):
29     if not src:
30         return src
31     src = ustr(src, errors='replace')
32     root = lxml.html.fromstring(u"<div>%s</div>" % src)
33     result = handle_element(root)
34     res = []
35     for element in children(result[0]):
36         if isinstance(element, basestring):
37             res.append(element)
38         else:
39             element.tail = ""
40             res.append(lxml.html.tostring(element))
41     return ''.join(res)
42
43 # FIXME: shouldn't this be a whitelist rather than a blacklist?!
44 to_remove = set(["script", "head", "meta", "title", "link", "img"])
45 to_unwrap = set(["html", "body"])
46
47 javascript_regex = re.compile(r"^\s*javascript\s*:.*$", re.IGNORECASE)
48
49 def handle_a(el, new):
50     href = el.get("href", "#")
51     if javascript_regex.search(href):
52         href = "#"
53     new.set("href", href)
54
55 special = {
56     "a": handle_a,
57 }
58
59 def handle_element(element):
60     if isinstance(element, basestring):
61         return [element]
62     if element.tag in to_remove:
63         return []
64     if element.tag in to_unwrap:
65         return reduce(operator.add, [handle_element(x) for x in children(element)])
66     result = lxml.html.fromstring("<%s />" % element.tag)
67     for c in children(element):
68         append_to(handle_element(c), result)
69     if element.tag in special:
70         special[element.tag](element, result)
71     return [result]
72
73 def children(node):
74     res = []
75     if node.text is not None:
76         res.append(node.text)
77     for child_node in node.getchildren():
78         res.append(child_node)
79         if child_node.tail is not None:
80             res.append(child_node.tail)
81     return res
82
83 def append_to(elements, dest_node):
84     for element in elements:
85         if isinstance(element, basestring):
86             children = dest_node.getchildren()
87             if len(children) == 0:
88                 dest_node.text = element
89             else:
90                 children[-1].tail = element
91         else:
92             dest_node.append(element)