Mercurial > libervia-backend
comparison libervia/backend/plugins/plugin_xep_0100.py @ 4071:4b842c1fb686
refactoring: renamed `sat` package to `libervia.backend`
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 02 Jun 2023 11:49:51 +0200 |
parents | sat/plugins/plugin_xep_0100.py@524856bd7b19 |
children | 0d7bb4df2343 |
comparison
equal
deleted
inserted
replaced
4070:d10748475025 | 4071:4b842c1fb686 |
---|---|
1 #!/usr/bin/env python3 | |
2 | |
3 | |
4 # SAT plugin for managing gateways (xep-0100) | |
5 # Copyright (C) 2009-2021 Jérôme Poisson (goffi@goffi.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 libervia.backend.core.i18n import _, D_ | |
21 from libervia.backend.core.constants import Const as C | |
22 from libervia.backend.core import exceptions | |
23 from libervia.backend.tools import xml_tools | |
24 from libervia.backend.core.log import getLogger | |
25 | |
26 log = getLogger(__name__) | |
27 from twisted.words.protocols.jabber import jid | |
28 from twisted.internet import reactor, defer | |
29 | |
30 PLUGIN_INFO = { | |
31 C.PI_NAME: "Gateways Plugin", | |
32 C.PI_IMPORT_NAME: "XEP-0100", | |
33 C.PI_TYPE: "XEP", | |
34 C.PI_PROTOCOLS: ["XEP-0100"], | |
35 C.PI_DEPENDENCIES: ["XEP-0077"], | |
36 C.PI_MAIN: "XEP_0100", | |
37 C.PI_DESCRIPTION: _("""Implementation of Gateways protocol"""), | |
38 } | |
39 | |
40 WARNING_MSG = D_( | |
41 """Be careful ! Gateways allow you to use an external IM (legacy IM), so you can see your contact as XMPP contacts. | |
42 But when you do this, all your messages go throught the external legacy IM server, it is a huge privacy issue (i.e.: all your messages throught the gateway can be monitored, recorded, analysed by the external server, most of time a private company).""" | |
43 ) | |
44 | |
45 GATEWAY_TIMEOUT = 10 # time to wait before cancelling a gateway disco info, in seconds | |
46 | |
47 TYPE_DESCRIPTIONS = { | |
48 "irc": D_("Internet Relay Chat"), | |
49 "xmpp": D_("XMPP"), | |
50 "qq": D_("Tencent QQ"), | |
51 "simple": D_("SIP/SIMPLE"), | |
52 "icq": D_("ICQ"), | |
53 "yahoo": D_("Yahoo! Messenger"), | |
54 "gadu-gadu": D_("Gadu-Gadu"), | |
55 "aim": D_("AOL Instant Messenger"), | |
56 "msn": D_("Windows Live Messenger"), | |
57 } | |
58 | |
59 | |
60 class XEP_0100(object): | |
61 def __init__(self, host): | |
62 log.info(_("Gateways plugin initialization")) | |
63 self.host = host | |
64 self.__gateways = {} # dict used to construct the answer to gateways_find. Key = target jid | |
65 host.bridge.add_method( | |
66 "gateways_find", | |
67 ".plugin", | |
68 in_sign="ss", | |
69 out_sign="s", | |
70 method=self._find_gateways, | |
71 ) | |
72 host.bridge.add_method( | |
73 "gateway_register", | |
74 ".plugin", | |
75 in_sign="ss", | |
76 out_sign="s", | |
77 method=self._gateway_register, | |
78 ) | |
79 self.__menu_id = host.register_callback(self._gateways_menu, with_data=True) | |
80 self.__selected_id = host.register_callback( | |
81 self._gateway_selected_cb, with_data=True | |
82 ) | |
83 host.import_menu( | |
84 (D_("Service"), D_("Gateways")), | |
85 self._gateways_menu, | |
86 security_limit=1, | |
87 help_string=D_("Find gateways"), | |
88 ) | |
89 | |
90 def _gateways_menu(self, data, profile): | |
91 """ XMLUI activated by menu: return Gateways UI | |
92 | |
93 @param profile: %(doc_profile)s | |
94 """ | |
95 client = self.host.get_client(profile) | |
96 try: | |
97 jid_ = jid.JID( | |
98 data.get(xml_tools.form_escape("external_jid"), client.jid.host) | |
99 ) | |
100 except RuntimeError: | |
101 raise exceptions.DataError(_("Invalid JID")) | |
102 d = self.gateways_find(jid_, profile) | |
103 d.addCallback(self._gateways_result_2_xmlui, jid_) | |
104 d.addCallback(lambda xmlui: {"xmlui": xmlui.toXml()}) | |
105 return d | |
106 | |
107 def _gateways_result_2_xmlui(self, result, entity): | |
108 xmlui = xml_tools.XMLUI(title=_("Gateways manager (%s)") % entity.full()) | |
109 xmlui.addText(_(WARNING_MSG)) | |
110 xmlui.addDivider("dash") | |
111 adv_list = xmlui.change_container( | |
112 "advanced_list", | |
113 columns=3, | |
114 selectable="single", | |
115 callback_id=self.__selected_id, | |
116 ) | |
117 for success, gateway_data in result: | |
118 if not success: | |
119 fail_cond, disco_item = gateway_data | |
120 xmlui.addJid(disco_item.entity) | |
121 xmlui.addText(_("Failed (%s)") % fail_cond) | |
122 xmlui.addEmpty() | |
123 else: | |
124 jid_, data = gateway_data | |
125 for datum in data: | |
126 identity, name = datum | |
127 adv_list.set_row_index(jid_.full()) | |
128 xmlui.addJid(jid_) | |
129 xmlui.addText(name) | |
130 xmlui.addText(self._get_identity_desc(identity)) | |
131 adv_list.end() | |
132 xmlui.addDivider("blank") | |
133 xmlui.change_container("advanced_list", columns=3) | |
134 xmlui.addLabel(_("Use external XMPP server")) | |
135 xmlui.addString("external_jid") | |
136 xmlui.addButton(self.__menu_id, _("Go !"), fields_back=("external_jid",)) | |
137 return xmlui | |
138 | |
139 def _gateway_selected_cb(self, data, profile): | |
140 try: | |
141 target_jid = jid.JID(data["index"]) | |
142 except (KeyError, RuntimeError): | |
143 log.warning(_("No gateway index selected")) | |
144 return {} | |
145 | |
146 d = self.gateway_register(target_jid, profile) | |
147 d.addCallback(lambda xmlui: {"xmlui": xmlui.toXml()}) | |
148 return d | |
149 | |
150 def _get_identity_desc(self, identity): | |
151 """ Return a human readable description of identity | |
152 @param identity: tuple as returned by Disco identities (category, type) | |
153 | |
154 """ | |
155 category, type_ = identity | |
156 if category != "gateway": | |
157 log.error( | |
158 _( | |
159 'INTERNAL ERROR: identity category should always be "gateway" in _getTypeString, got "%s"' | |
160 ) | |
161 % category | |
162 ) | |
163 try: | |
164 return _(TYPE_DESCRIPTIONS[type_]) | |
165 except KeyError: | |
166 return _("Unknown IM") | |
167 | |
168 def _registration_successful(self, jid_, profile): | |
169 """Called when in_band registration is ok, we must now follow the rest of procedure""" | |
170 log.debug(_("Registration successful, doing the rest")) | |
171 self.host.contact_add(jid_, profile_key=profile) | |
172 self.host.presence_set(jid_, profile_key=profile) | |
173 | |
174 def _gateway_register(self, target_jid_s, profile_key=C.PROF_KEY_NONE): | |
175 d = self.gateway_register(jid.JID(target_jid_s), profile_key) | |
176 d.addCallback(lambda xmlui: xmlui.toXml()) | |
177 return d | |
178 | |
179 def gateway_register(self, target_jid, profile_key=C.PROF_KEY_NONE): | |
180 """Register gateway using in-band registration, then log-in to gateway""" | |
181 profile = self.host.memory.get_profile_name(profile_key) | |
182 assert profile | |
183 d = self.host.plugins["XEP-0077"].in_band_register( | |
184 target_jid, self._registration_successful, profile | |
185 ) | |
186 return d | |
187 | |
188 def _infos_received(self, dl_result, items, target, client): | |
189 """Find disco infos about entity, to check if it is a gateway""" | |
190 | |
191 ret = [] | |
192 for idx, (success, result) in enumerate(dl_result): | |
193 if not success: | |
194 if isinstance(result.value, defer.CancelledError): | |
195 msg = _("Timeout") | |
196 else: | |
197 try: | |
198 msg = result.value.condition | |
199 except AttributeError: | |
200 msg = str(result) | |
201 ret.append((success, (msg, items[idx]))) | |
202 else: | |
203 entity = items[idx].entity | |
204 gateways = [ | |
205 (identity, result.identities[identity]) | |
206 for identity in result.identities | |
207 if identity[0] == "gateway" | |
208 ] | |
209 if gateways: | |
210 log.info( | |
211 _("Found gateway [%(jid)s]: %(identity_name)s") | |
212 % { | |
213 "jid": entity.full(), | |
214 "identity_name": " - ".join( | |
215 [gateway[1] for gateway in gateways] | |
216 ), | |
217 } | |
218 ) | |
219 ret.append((success, (entity, gateways))) | |
220 else: | |
221 log.info( | |
222 _("Skipping [%(jid)s] which is not a gateway") | |
223 % {"jid": entity.full()} | |
224 ) | |
225 return ret | |
226 | |
227 def _items_received(self, disco, target, client): | |
228 """Look for items with disco protocol, and ask infos for each one""" | |
229 | |
230 if len(disco._items) == 0: | |
231 log.debug(_("No gateway found")) | |
232 return [] | |
233 | |
234 _defers = [] | |
235 for item in disco._items: | |
236 log.debug(_("item found: %s") % item.entity) | |
237 _defers.append(client.disco.requestInfo(item.entity)) | |
238 dl = defer.DeferredList(_defers) | |
239 dl.addCallback( | |
240 self._infos_received, items=disco._items, target=target, client=client | |
241 ) | |
242 reactor.callLater(GATEWAY_TIMEOUT, dl.cancel) | |
243 return dl | |
244 | |
245 def _find_gateways(self, target_jid_s, profile_key): | |
246 target_jid = jid.JID(target_jid_s) | |
247 profile = self.host.memory.get_profile_name(profile_key) | |
248 if not profile: | |
249 raise exceptions.ProfileUnknownError | |
250 d = self.gateways_find(target_jid, profile) | |
251 d.addCallback(self._gateways_result_2_xmlui, target_jid) | |
252 d.addCallback(lambda xmlui: xmlui.toXml()) | |
253 return d | |
254 | |
255 def gateways_find(self, target, profile): | |
256 """Find gateways in the target JID, using discovery protocol | |
257 """ | |
258 client = self.host.get_client(profile) | |
259 log.debug( | |
260 _("find gateways (target = %(target)s, profile = %(profile)s)") | |
261 % {"target": target.full(), "profile": profile} | |
262 ) | |
263 d = client.disco.requestItems(target) | |
264 d.addCallback(self._items_received, target=target, client=client) | |
265 return d |