Mercurial > libervia-backend
annotate src/plugins/plugin_xep_0071.py @ 1596:b7ee113183fc
jp: better profile commands:
- new "profile/default" command
- info doesn't show password anymore by default, need to be explicitly requested
- info and modify don't need to connect anymore
- modify can now set default profile. As use_profile is set, at least a profile session need to be started when it would not be mandatory technicaly (if just setting the profile as default is needed). But this option should not be used often, and it's not a big side effect, so I don't feel the need to create a new dedicated command, or to do complicated checks to avoid the session start.
author | Goffi <goffi@goffi.org> |
---|---|
date | Sat, 14 Nov 2015 19:18:10 +0100 |
parents | 94901070478e |
children | cf11cfc87ef9 |
rev | line source |
---|---|
668 | 1 #!/usr/bin/python |
2 # -*- coding: utf-8 -*- | |
3 | |
4 # SAT plugin for Publish-Subscribe (xep-0071) | |
1396 | 5 # Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Jérôme Poisson (goffi@goffi.org) |
668 | 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 published by | |
9 # the Free Software Foundation, either version 3 of the License, or | |
10 # (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 | |
771 | 20 from sat.core.i18n import _ |
832
c4b22aedb7d7
plugin groupblog, XEP-0071, XEP-0277, text_syntaxes: manage raw/rich/xhtml data for content/title:
souliane <souliane@mailoo.org>
parents:
811
diff
changeset
|
21 from sat.core import exceptions |
993
301b342c697a
core: use of the new core.log module:
Goffi <goffi@goffi.org>
parents:
922
diff
changeset
|
22 from sat.core.log import getLogger |
301b342c697a
core: use of the new core.log module:
Goffi <goffi@goffi.org>
parents:
922
diff
changeset
|
23 log = getLogger(__name__) |
668 | 24 |
993
301b342c697a
core: use of the new core.log module:
Goffi <goffi@goffi.org>
parents:
922
diff
changeset
|
25 from wokkel import disco, iwokkel |
668 | 26 from zope.interface import implements |
27 # from lxml import etree | |
1542
94901070478e
plugins: added new MissingModule exceptions to plugins using third party modules
Goffi <goffi@goffi.org>
parents:
1396
diff
changeset
|
28 try: |
94901070478e
plugins: added new MissingModule exceptions to plugins using third party modules
Goffi <goffi@goffi.org>
parents:
1396
diff
changeset
|
29 from lxml import html |
94901070478e
plugins: added new MissingModule exceptions to plugins using third party modules
Goffi <goffi@goffi.org>
parents:
1396
diff
changeset
|
30 except ImportError: |
94901070478e
plugins: added new MissingModule exceptions to plugins using third party modules
Goffi <goffi@goffi.org>
parents:
1396
diff
changeset
|
31 raise exceptions.MissingModule(u"Missing module lxml, please download/install it from http://lxml.de/") |
668 | 32 try: |
33 from twisted.words.protocols.xmlstream import XMPPHandler | |
34 except ImportError: | |
35 from wokkel.subprotocols import XMPPHandler | |
36 | |
37 NS_XHTML_IM = 'http://jabber.org/protocol/xhtml-im' | |
38 NS_XHTML = 'http://www.w3.org/1999/xhtml' | |
39 | |
40 PLUGIN_INFO = { | |
41 "name": "XHTML-IM Plugin", | |
42 "import_name": "XEP-0071", | |
43 "type": "XEP", | |
44 "protocols": ["XEP-0071"], | |
45 "dependencies": ["TEXT-SYNTAXES"], | |
46 "main": "XEP_0071", | |
47 "handler": "yes", | |
48 "description": _("""Implementation of XHTML-IM""") | |
49 } | |
50 | |
51 allowed = { | |
52 "a": set(["href", "style", "type"]), | |
53 "blockquote": set(["style"]), | |
54 "body": set(["style"]), | |
55 "br": set([]), | |
56 "cite": set(["style"]), | |
57 "em": set([]), | |
58 "img": set(["alt", "height", "src", "style", "width"]), | |
59 "li": set(["style"]), | |
60 "ol": set(["style"]), | |
61 "p": set(["style"]), | |
62 "span": set(["style"]), | |
63 "strong": set([]), | |
64 "ul": set(["style"]), | |
65 } | |
66 | |
67 styles_allowed = ["background-color", "color", "font-family", "font-size", "font-style", "font-weight", "margin-left", "margin-right", "text-align", "text-decoration"] | |
68 | |
69 blacklist = ['script'] # tag that we have to kill (we don't keep content) | |
70 | |
71 | |
72 class XEP_0071(object): | |
73 SYNTAX_XHTML_IM = "XHTML-IM" | |
74 | |
75 def __init__(self, host): | |
993
301b342c697a
core: use of the new core.log module:
Goffi <goffi@goffi.org>
parents:
922
diff
changeset
|
76 log.info(_("XHTML-IM plugin initialization")) |
668 | 77 self.host = host |
702
a25db3fe3959
plugin XEP-0071: rich messages management for sendMessage
Goffi <goffi@goffi.org>
parents:
701
diff
changeset
|
78 self.synt_plg = self.host.plugins["TEXT-SYNTAXES"] |
a25db3fe3959
plugin XEP-0071: rich messages management for sendMessage
Goffi <goffi@goffi.org>
parents:
701
diff
changeset
|
79 self.synt_plg.addSyntax(self.SYNTAX_XHTML_IM, lambda xhtml: xhtml, self.XHTML2XHTML_IM, [self.synt_plg.OPT_HIDDEN]) |
668 | 80 host.trigger.add("MessageReceived", self.messageReceivedTrigger) |
702
a25db3fe3959
plugin XEP-0071: rich messages management for sendMessage
Goffi <goffi@goffi.org>
parents:
701
diff
changeset
|
81 host.trigger.add("sendMessage", self.sendMessageTrigger) |
668 | 82 |
83 def getHandler(self, profile): | |
84 return XEP_0071_handler(self) | |
85 | |
86 def _messagePostTreat(self, data, body_elt): | |
87 """ Callback which manage the post treatment of the message in case of XHTML-IM found | |
88 @param data: data send by MessageReceived trigger through post_treat deferred | |
89 @param xhtml_im: XHTML-IM body element found | |
90 @return: the data with the extra parameter updated | |
91 """ | |
92 #TODO: check if text only body is empty, then try to convert XHTML-IM to pure text and show a warning message | |
93 def converted(xhtml): | |
94 data['extra']['xhtml'] = xhtml | |
95 return data | |
702
a25db3fe3959
plugin XEP-0071: rich messages management for sendMessage
Goffi <goffi@goffi.org>
parents:
701
diff
changeset
|
96 d = self.synt_plg.convert(body_elt.toXml(), self.SYNTAX_XHTML_IM, safe=True) |
668 | 97 d.addCallback(converted) |
98 return d | |
99 | |
702
a25db3fe3959
plugin XEP-0071: rich messages management for sendMessage
Goffi <goffi@goffi.org>
parents:
701
diff
changeset
|
100 def _sendMessageAddRich(self, mess_data, profile): |
a25db3fe3959
plugin XEP-0071: rich messages management for sendMessage
Goffi <goffi@goffi.org>
parents:
701
diff
changeset
|
101 """ Construct XHTML-IM node and add it XML element |
a25db3fe3959
plugin XEP-0071: rich messages management for sendMessage
Goffi <goffi@goffi.org>
parents:
701
diff
changeset
|
102 @param mess_data: message data as sended by sendMessage callback |
a25db3fe3959
plugin XEP-0071: rich messages management for sendMessage
Goffi <goffi@goffi.org>
parents:
701
diff
changeset
|
103 """ |
a25db3fe3959
plugin XEP-0071: rich messages management for sendMessage
Goffi <goffi@goffi.org>
parents:
701
diff
changeset
|
104 def syntax_converted(xhtml_im): |
a25db3fe3959
plugin XEP-0071: rich messages management for sendMessage
Goffi <goffi@goffi.org>
parents:
701
diff
changeset
|
105 message_elt = mess_data['xml'] |
a25db3fe3959
plugin XEP-0071: rich messages management for sendMessage
Goffi <goffi@goffi.org>
parents:
701
diff
changeset
|
106 html_elt = message_elt.addElement('html', NS_XHTML_IM) |
a25db3fe3959
plugin XEP-0071: rich messages management for sendMessage
Goffi <goffi@goffi.org>
parents:
701
diff
changeset
|
107 body_elt = html_elt.addElement('body', NS_XHTML) |
a25db3fe3959
plugin XEP-0071: rich messages management for sendMessage
Goffi <goffi@goffi.org>
parents:
701
diff
changeset
|
108 body_elt.addRawXml(xhtml_im) |
a25db3fe3959
plugin XEP-0071: rich messages management for sendMessage
Goffi <goffi@goffi.org>
parents:
701
diff
changeset
|
109 mess_data['extra']['xhtml'] = xhtml_im |
a25db3fe3959
plugin XEP-0071: rich messages management for sendMessage
Goffi <goffi@goffi.org>
parents:
701
diff
changeset
|
110 return mess_data |
a25db3fe3959
plugin XEP-0071: rich messages management for sendMessage
Goffi <goffi@goffi.org>
parents:
701
diff
changeset
|
111 |
a25db3fe3959
plugin XEP-0071: rich messages management for sendMessage
Goffi <goffi@goffi.org>
parents:
701
diff
changeset
|
112 syntax = self.synt_plg.getCurrentSyntax(profile) |
832
c4b22aedb7d7
plugin groupblog, XEP-0071, XEP-0277, text_syntaxes: manage raw/rich/xhtml data for content/title:
souliane <souliane@mailoo.org>
parents:
811
diff
changeset
|
113 rich = mess_data['extra'].get('rich', '') |
c4b22aedb7d7
plugin groupblog, XEP-0071, XEP-0277, text_syntaxes: manage raw/rich/xhtml data for content/title:
souliane <souliane@mailoo.org>
parents:
811
diff
changeset
|
114 xhtml = mess_data['extra'].get('xhtml', '') |
c4b22aedb7d7
plugin groupblog, XEP-0071, XEP-0277, text_syntaxes: manage raw/rich/xhtml data for content/title:
souliane <souliane@mailoo.org>
parents:
811
diff
changeset
|
115 if rich: |
c4b22aedb7d7
plugin groupblog, XEP-0071, XEP-0277, text_syntaxes: manage raw/rich/xhtml data for content/title:
souliane <souliane@mailoo.org>
parents:
811
diff
changeset
|
116 d = self.synt_plg.convert(rich, syntax, self.SYNTAX_XHTML_IM) |
c4b22aedb7d7
plugin groupblog, XEP-0071, XEP-0277, text_syntaxes: manage raw/rich/xhtml data for content/title:
souliane <souliane@mailoo.org>
parents:
811
diff
changeset
|
117 if xhtml: |
c4b22aedb7d7
plugin groupblog, XEP-0071, XEP-0277, text_syntaxes: manage raw/rich/xhtml data for content/title:
souliane <souliane@mailoo.org>
parents:
811
diff
changeset
|
118 raise exceptions.DataError(_("Can't have xhtml and rich content at the same time")) |
c4b22aedb7d7
plugin groupblog, XEP-0071, XEP-0277, text_syntaxes: manage raw/rich/xhtml data for content/title:
souliane <souliane@mailoo.org>
parents:
811
diff
changeset
|
119 if xhtml: |
c4b22aedb7d7
plugin groupblog, XEP-0071, XEP-0277, text_syntaxes: manage raw/rich/xhtml data for content/title:
souliane <souliane@mailoo.org>
parents:
811
diff
changeset
|
120 d = self.synt_plg.clean_xhtml(xhtml) |
702
a25db3fe3959
plugin XEP-0071: rich messages management for sendMessage
Goffi <goffi@goffi.org>
parents:
701
diff
changeset
|
121 d.addCallback(syntax_converted) |
a25db3fe3959
plugin XEP-0071: rich messages management for sendMessage
Goffi <goffi@goffi.org>
parents:
701
diff
changeset
|
122 return d |
a25db3fe3959
plugin XEP-0071: rich messages management for sendMessage
Goffi <goffi@goffi.org>
parents:
701
diff
changeset
|
123 |
668 | 124 def messageReceivedTrigger(self, message, post_treat, profile): |
125 """ Check presence of XHTML-IM in message | |
126 """ | |
127 try: | |
128 html_elt = message.elements(NS_XHTML_IM, 'html').next() | |
129 body_elt = html_elt.elements(NS_XHTML, 'body').next() | |
130 # OK, we have found rich text | |
131 post_treat.addCallback(self._messagePostTreat, body_elt) | |
132 except StopIteration: | |
133 # No XHTML-IM | |
134 pass | |
135 return True | |
136 | |
922
c897c8d321b3
core: sendMessageTrigger now manage pre and post treatments, which happen before or after XML generation
Goffi <goffi@goffi.org>
parents:
832
diff
changeset
|
137 def sendMessageTrigger(self, mess_data, pre_xml_treatments, post_xml_treatments, profile): |
702
a25db3fe3959
plugin XEP-0071: rich messages management for sendMessage
Goffi <goffi@goffi.org>
parents:
701
diff
changeset
|
138 """ Check presence of rich text in extra |
a25db3fe3959
plugin XEP-0071: rich messages management for sendMessage
Goffi <goffi@goffi.org>
parents:
701
diff
changeset
|
139 """ |
832
c4b22aedb7d7
plugin groupblog, XEP-0071, XEP-0277, text_syntaxes: manage raw/rich/xhtml data for content/title:
souliane <souliane@mailoo.org>
parents:
811
diff
changeset
|
140 if 'rich' in mess_data['extra'] or 'xhtml' in mess_data['extra']: |
922
c897c8d321b3
core: sendMessageTrigger now manage pre and post treatments, which happen before or after XML generation
Goffi <goffi@goffi.org>
parents:
832
diff
changeset
|
141 post_xml_treatments.addCallback(self._sendMessageAddRich, profile) |
702
a25db3fe3959
plugin XEP-0071: rich messages management for sendMessage
Goffi <goffi@goffi.org>
parents:
701
diff
changeset
|
142 return True |
a25db3fe3959
plugin XEP-0071: rich messages management for sendMessage
Goffi <goffi@goffi.org>
parents:
701
diff
changeset
|
143 |
668 | 144 def _purgeStyle(self, styles_raw): |
145 """ Remove unauthorised styles according to the XEP-0071 | |
146 @param styles_raw: raw styles (value of the style attribute) | |
147 """ | |
148 purged = [] | |
149 | |
150 styles = [style.strip().split(':') for style in styles_raw.split(';')] | |
151 | |
152 for style_tuple in styles: | |
153 if len(style_tuple) != 2: | |
154 continue | |
155 name, value = style_tuple | |
156 name = name.strip() | |
157 if name not in styles_allowed: | |
158 continue | |
159 purged.append((name, value.strip())) | |
160 | |
161 return u'; '.join([u"%s: %s" % data for data in purged]) | |
162 | |
163 def XHTML2XHTML_IM(self, xhtml): | |
164 """ Convert XHTML document to XHTML_IM subset | |
165 @param xhtml: raw xhtml to convert | |
166 """ | |
167 # TODO: more clever tag replacement (replace forbidden tags with equivalents when possible) | |
168 | |
169 parser = html.HTMLParser(remove_comments=True, encoding='utf-8') | |
170 root = html.fromstring(xhtml, parser=parser) | |
171 body_elt = root.find('body') | |
172 if body_elt is None: | |
173 # we use the whole XML as body if no body element is found | |
174 body_elt = html.Element('body') | |
175 body_elt.append(root) | |
176 else: | |
177 body_elt.attrib.clear() | |
178 | |
179 allowed_tags = allowed.keys() | |
180 to_strip = [] | |
181 for elem in body_elt.iter(): | |
182 if elem.tag not in allowed_tags: | |
183 to_strip.append(elem) | |
184 else: | |
185 # we remove unallowed attributes | |
186 attrib = elem.attrib | |
187 att_to_remove = set(attrib).difference(allowed[elem.tag]) | |
188 for att in att_to_remove: | |
189 del(attrib[att]) | |
190 if "style" in attrib: | |
191 attrib["style"] = self._purgeStyle(attrib["style"]) | |
192 | |
193 for elem in to_strip: | |
194 if elem.tag in blacklist: | |
195 #we need to remove the element and all descendants | |
993
301b342c697a
core: use of the new core.log module:
Goffi <goffi@goffi.org>
parents:
922
diff
changeset
|
196 log.debug(u"removing black listed tag: %s" % (elem.tag)) |
668 | 197 elem.drop_tree() |
198 else: | |
199 elem.drop_tag() | |
701
98b2400e17d6
plugin XEP-0071: XHTML2XHTML_IM don't return the <body> root tag anymore.
Goffi <goffi@goffi.org>
parents:
668
diff
changeset
|
200 if len(body_elt) !=1: |
98b2400e17d6
plugin XEP-0071: XHTML2XHTML_IM don't return the <body> root tag anymore.
Goffi <goffi@goffi.org>
parents:
668
diff
changeset
|
201 root_elt = body_elt |
98b2400e17d6
plugin XEP-0071: XHTML2XHTML_IM don't return the <body> root tag anymore.
Goffi <goffi@goffi.org>
parents:
668
diff
changeset
|
202 body_elt.tag = "p" |
98b2400e17d6
plugin XEP-0071: XHTML2XHTML_IM don't return the <body> root tag anymore.
Goffi <goffi@goffi.org>
parents:
668
diff
changeset
|
203 else: |
98b2400e17d6
plugin XEP-0071: XHTML2XHTML_IM don't return the <body> root tag anymore.
Goffi <goffi@goffi.org>
parents:
668
diff
changeset
|
204 root_elt = body_elt[0] |
668 | 205 |
701
98b2400e17d6
plugin XEP-0071: XHTML2XHTML_IM don't return the <body> root tag anymore.
Goffi <goffi@goffi.org>
parents:
668
diff
changeset
|
206 return html.tostring(root_elt, encoding='unicode', method='xml') |
668 | 207 |
208 class XEP_0071_handler(XMPPHandler): | |
209 implements(iwokkel.IDisco) | |
210 | |
211 def __init__(self, plugin_parent): | |
212 self.plugin_parent = plugin_parent | |
213 self.host = plugin_parent.host | |
214 | |
215 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): | |
216 return [disco.DiscoFeature(NS_XHTML_IM)] | |
217 | |
218 def getDiscoItems(self, requestor, target, nodeIdentifier=''): | |
219 return [] |