Mercurial > libervia-backend
comparison sat/plugins/plugin_xep_0033.py @ 2562:26edcf3a30eb
core, setup: huge cleaning:
- moved directories from src and frontends/src to sat and sat_frontends, which is the recommanded naming convention
- move twisted directory to root
- removed all hacks from setup.py, and added missing dependencies, it is now clean
- use https URL for website in setup.py
- removed "Environment :: X11 Applications :: GTK", as wix is deprecated and removed
- renamed sat.sh to sat and fixed its installation
- added python_requires to specify Python version needed
- replaced glib2reactor which use deprecated code by gtk3reactor
sat can now be installed directly from virtualenv without using --system-site-packages anymore \o/
author | Goffi <goffi@goffi.org> |
---|---|
date | Mon, 02 Apr 2018 19:44:50 +0200 |
parents | src/plugins/plugin_xep_0033.py@6a004a22dd9e |
children | 56f94936df1e |
comparison
equal
deleted
inserted
replaced
2561:bd30dc3ffe5a | 2562:26edcf3a30eb |
---|---|
1 #!/usr/bin/env python2 | |
2 # -*- coding: utf-8 -*- | |
3 | |
4 # SAT plugin for Extended Stanza Addressing (xep-0033) | |
5 # Copyright (C) 2013-2016 Adrien Cossa (souliane@mailoo.org) | |
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 | |
20 from sat.core.i18n import _ | |
21 from sat.core.constants import Const as C | |
22 from sat.core.log import getLogger | |
23 log = getLogger(__name__) | |
24 from sat.core import exceptions | |
25 from wokkel import disco, iwokkel | |
26 from zope.interface import implements | |
27 from twisted.words.protocols.jabber.jid import JID | |
28 from twisted.python import failure | |
29 import copy | |
30 try: | |
31 from twisted.words.protocols.xmlstream import XMPPHandler | |
32 except ImportError: | |
33 from wokkel.subprotocols import XMPPHandler | |
34 from twisted.words.xish import domish | |
35 from twisted.internet import defer | |
36 | |
37 from sat.tools import trigger | |
38 from time import time | |
39 | |
40 # TODO: fix Prosody "addressing" plugin to leave the concerned bcc according to the spec: | |
41 # | |
42 # http://xmpp.org/extensions/xep-0033.html#addr-type-bcc | |
43 # "This means that the server MUST remove these addresses before the stanza is delivered to anyone other than the given bcc addressee or the multicast service of the bcc addressee." | |
44 # | |
45 # http://xmpp.org/extensions/xep-0033.html#multicast | |
46 # "Each 'bcc' recipient MUST receive only the <address type='bcc'/> associated with that addressee." | |
47 | |
48 # TODO: fix Prosody "addressing" plugin to determine itself if remote servers supports this XEP | |
49 | |
50 | |
51 NS_XMPP_CLIENT = "jabber:client" | |
52 NS_ADDRESS = "http://jabber.org/protocol/address" | |
53 ATTRIBUTES = ["jid", "uri", "node", "desc", "delivered", "type"] | |
54 ADDRESS_TYPES = ["to", "cc", "bcc", "replyto", "replyroom", "noreply"] | |
55 | |
56 PLUGIN_INFO = { | |
57 C.PI_NAME: "Extended Stanza Addressing Protocol Plugin", | |
58 C.PI_IMPORT_NAME: "XEP-0033", | |
59 C.PI_TYPE: "XEP", | |
60 C.PI_PROTOCOLS: ["XEP-0033"], | |
61 C.PI_DEPENDENCIES: [], | |
62 C.PI_MAIN: "XEP_0033", | |
63 C.PI_HANDLER: "yes", | |
64 C.PI_DESCRIPTION: _("""Implementation of Extended Stanza Addressing""") | |
65 } | |
66 | |
67 | |
68 class XEP_0033(object): | |
69 """ | |
70 Implementation for XEP 0033 | |
71 """ | |
72 def __init__(self, host): | |
73 log.info(_("Extended Stanza Addressing plugin initialization")) | |
74 self.host = host | |
75 self.internal_data = {} | |
76 host.trigger.add("sendMessage", self.sendMessageTrigger, trigger.TriggerManager.MIN_PRIORITY) | |
77 host.trigger.add("MessageReceived", self.messageReceivedTrigger) | |
78 | |
79 def sendMessageTrigger(self, client, mess_data, pre_xml_treatments, post_xml_treatments): | |
80 """Process the XEP-0033 related data to be sent""" | |
81 profile = client.profile | |
82 | |
83 def treatment(mess_data): | |
84 if not 'address' in mess_data['extra']: | |
85 return mess_data | |
86 | |
87 def discoCallback(entities): | |
88 if not entities: | |
89 log.warning(_("XEP-0033 is being used but the server doesn't support it!")) | |
90 raise failure.Failure(exceptions.CancelError(u'Cancelled by XEP-0033')) | |
91 if mess_data["to"] not in entities: | |
92 expected = _(' or ').join([entity.userhost() for entity in entities]) | |
93 log.warning(_(u"Stanzas using XEP-0033 should be addressed to %(expected)s, not %(current)s!") % {'expected': expected, 'current': mess_data["to"]}) | |
94 log.warning(_(u"TODO: addressing has been fixed by the backend... fix it in the frontend!")) | |
95 mess_data["to"] = list(entities)[0].userhostJID() | |
96 element = mess_data['xml'].addElement('addresses', NS_ADDRESS) | |
97 entries = [entry.split(':') for entry in mess_data['extra']['address'].split('\n') if entry != ''] | |
98 for type_, jid_ in entries: | |
99 element.addChild(domish.Element((None, 'address'), None, {'type': type_, 'jid': jid_})) | |
100 # when the prosody plugin is completed, we can immediately return mess_data from here | |
101 self.sendAndStoreMessage(mess_data, entries, profile) | |
102 log.debug("XEP-0033 took over") | |
103 raise failure.Failure(exceptions.CancelError(u'Cancelled by XEP-0033')) | |
104 d = self.host.findFeaturesSet(client, [NS_ADDRESS]) | |
105 d.addCallbacks(discoCallback, lambda dummy: discoCallback(None)) | |
106 return d | |
107 | |
108 post_xml_treatments.addCallback(treatment) | |
109 return True | |
110 | |
111 def sendAndStoreMessage(self, mess_data, entries, profile): | |
112 """Check if target servers support XEP-0033, send and store the messages | |
113 @return: a friendly failure to let the core know that we sent the message already | |
114 | |
115 Later we should be able to remove this method because: | |
116 # XXX: sending the messages should be done by the local server | |
117 # FIXME: for now we duplicate the messages in the history for each recipient, this should change | |
118 # FIXME: for now we duplicate the echoes to the sender, this should also change | |
119 Ideas: | |
120 - fix Prosody plugin to check if target server support the feature | |
121 - redesign the database to save only one entry to the database | |
122 - change the messageNew signal to eventually pass more than one recipient | |
123 """ | |
124 client = self.host.getClient(profile) | |
125 def send(mess_data, skip_send=False): | |
126 d = defer.Deferred() | |
127 if not skip_send: | |
128 d.addCallback(client.sendMessageData) | |
129 d.addCallback(client.messageAddToHistory) | |
130 d.addCallback(client.messageSendToBridge) | |
131 d.addErrback(lambda failure: failure.trap(exceptions.CancelError)) | |
132 return d.callback(mess_data) | |
133 | |
134 def discoCallback(entities, to_jid_s): | |
135 history_data = copy.deepcopy(mess_data) | |
136 history_data['to'] = JID(to_jid_s) | |
137 history_data['xml']['to'] = to_jid_s | |
138 if entities: | |
139 if entities not in self.internal_data[timestamp]: | |
140 sent_data = copy.deepcopy(mess_data) | |
141 sent_data['to'] = JID(JID(to_jid_s).host) | |
142 sent_data['xml']['to'] = JID(to_jid_s).host | |
143 send(sent_data) | |
144 self.internal_data[timestamp].append(entities) | |
145 # we still need to fill the history and signal the echo... | |
146 send(history_data, skip_send=True) | |
147 else: | |
148 # target server misses the addressing feature | |
149 send(history_data) | |
150 | |
151 def errback(failure, to_jid): | |
152 discoCallback(None, to_jid) | |
153 | |
154 timestamp = time() | |
155 self.internal_data[timestamp] = [] | |
156 defer_list = [] | |
157 for type_, jid_ in entries: | |
158 d = defer.Deferred() | |
159 d.addCallback(self.host.findFeaturesSet, client=client, jid_=JID(JID(jid_).host)) | |
160 d.addCallbacks(discoCallback, errback, callbackArgs=[jid_], errbackArgs=[jid_]) | |
161 d.callback([NS_ADDRESS]) | |
162 defer_list.append(d) | |
163 d = defer.Deferred().addCallback(lambda dummy: self.internal_data.pop(timestamp)) | |
164 defer.DeferredList(defer_list).chainDeferred(d) | |
165 | |
166 def messageReceivedTrigger(self, client, message, post_treat): | |
167 """In order to save the addressing information in the history""" | |
168 def post_treat_addr(data, addresses): | |
169 data['extra']['addresses'] = "" | |
170 for address in addresses: | |
171 # Depending how message has been constructed, we could get here | |
172 # some noise like "\n " instead of an address element. | |
173 if isinstance(address, domish.Element): | |
174 data['extra']['addresses'] += '%s:%s\n' % (address['type'], address['jid']) | |
175 return data | |
176 | |
177 try: | |
178 addresses = message.elements(NS_ADDRESS, 'addresses').next() | |
179 except StopIteration: | |
180 pass # no addresses | |
181 else: | |
182 post_treat.addCallback(post_treat_addr, addresses.children) | |
183 return True | |
184 | |
185 def getHandler(self, client): | |
186 return XEP_0033_handler(self, client.profile) | |
187 | |
188 | |
189 class XEP_0033_handler(XMPPHandler): | |
190 implements(iwokkel.IDisco) | |
191 | |
192 def __init__(self, plugin_parent, profile): | |
193 self.plugin_parent = plugin_parent | |
194 self.host = plugin_parent.host | |
195 self.profile = profile | |
196 | |
197 def getDiscoInfo(self, requestor, target, nodeIdentifier=''): | |
198 return [disco.DiscoFeature(NS_ADDRESS)] | |
199 | |
200 def getDiscoItems(self, requestor, target, nodeIdentifier=''): | |
201 return [] |