Mercurial > libervia-backend
comparison sat/plugins/plugin_xep_0100.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_0100.py@0046283a285d |
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 managing gateways (xep-0100) | |
5 # Copyright (C) 2009-2018 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 sat.core.i18n import _, D_ | |
21 from sat.core.constants import Const as C | |
22 from sat.core import exceptions | |
23 from sat.tools import xml_tools | |
24 from sat.core.log import getLogger | |
25 log = getLogger(__name__) | |
26 from twisted.words.protocols.jabber import jid | |
27 from twisted.internet import reactor, defer | |
28 | |
29 PLUGIN_INFO = { | |
30 C.PI_NAME: "Gateways Plugin", | |
31 C.PI_IMPORT_NAME: "XEP-0100", | |
32 C.PI_TYPE: "XEP", | |
33 C.PI_PROTOCOLS: ["XEP-0100"], | |
34 C.PI_DEPENDENCIES: ["XEP-0077"], | |
35 C.PI_MAIN: "XEP_0100", | |
36 C.PI_DESCRIPTION: _("""Implementation of Gateways protocol""") | |
37 } | |
38 | |
39 WARNING_MSG = D_(u"""Be careful ! Gateways allow you to use an external IM (legacy IM), so you can see your contact as XMPP contacts. | |
40 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).""") | |
41 | |
42 GATEWAY_TIMEOUT = 10 # time to wait before cancelling a gateway disco info, in seconds | |
43 | |
44 TYPE_DESCRIPTIONS = {'irc': D_("Internet Relay Chat"), | |
45 'xmpp': D_("XMPP"), | |
46 'qq': D_("Tencent QQ"), | |
47 'simple': D_("SIP/SIMPLE"), | |
48 'icq': D_("ICQ"), | |
49 'yahoo': D_("Yahoo! Messenger"), | |
50 'gadu-gadu': D_("Gadu-Gadu"), | |
51 'aim': D_("AOL Instant Messenger"), | |
52 'msn': D_("Windows Live Messenger"), | |
53 } | |
54 | |
55 | |
56 class XEP_0100(object): | |
57 | |
58 def __init__(self, host): | |
59 log.info(_("Gateways plugin initialization")) | |
60 self.host = host | |
61 self.__gateways = {} # dict used to construct the answer to findGateways. Key = target jid | |
62 host.bridge.addMethod("findGateways", ".plugin", in_sign='ss', out_sign='s', method=self._findGateways) | |
63 host.bridge.addMethod("gatewayRegister", ".plugin", in_sign='ss', out_sign='s', method=self._gatewayRegister) | |
64 self.__menu_id = host.registerCallback(self._gatewaysMenu, with_data=True) | |
65 self.__selected_id = host.registerCallback(self._gatewaySelectedCb, with_data=True) | |
66 host.importMenu((D_("Service"), D_("Gateways")), self._gatewaysMenu, security_limit=1, help_string=D_("Find gateways")) | |
67 | |
68 def _gatewaysMenu(self, data, profile): | |
69 """ XMLUI activated by menu: return Gateways UI | |
70 | |
71 @param profile: %(doc_profile)s | |
72 """ | |
73 client = self.host.getClient(profile) | |
74 try: | |
75 jid_ = jid.JID(data.get(xml_tools.formEscape('external_jid'), client.jid.host)) | |
76 except RuntimeError: | |
77 raise exceptions.DataError(_("Invalid JID")) | |
78 d = self.findGateways(jid_, profile) | |
79 d.addCallback(self._gatewaysResult2XMLUI, jid_) | |
80 d.addCallback(lambda xmlui: {'xmlui': xmlui.toXml()}) | |
81 return d | |
82 | |
83 def _gatewaysResult2XMLUI(self, result, entity): | |
84 xmlui = xml_tools.XMLUI(title=_('Gateways manager (%s)') % entity.full()) | |
85 xmlui.addText(_(WARNING_MSG)) | |
86 xmlui.addDivider('dash') | |
87 adv_list = xmlui.changeContainer('advanced_list', columns=3, selectable='single', callback_id=self.__selected_id) | |
88 for success, gateway_data in result: | |
89 if not success: | |
90 fail_cond, disco_item = gateway_data | |
91 xmlui.addJid(disco_item.entity) | |
92 xmlui.addText(_('Failed (%s)') % fail_cond) | |
93 xmlui.addEmpty() | |
94 else: | |
95 jid_, data = gateway_data | |
96 for datum in data: | |
97 identity, name = datum | |
98 adv_list.setRowIndex(jid_.full()) | |
99 xmlui.addJid(jid_) | |
100 xmlui.addText(name) | |
101 xmlui.addText(self._getIdentityDesc(identity)) | |
102 adv_list.end() | |
103 xmlui.addDivider('blank') | |
104 xmlui.changeContainer('advanced_list', columns=3) | |
105 xmlui.addLabel(_('Use external XMPP server')) | |
106 xmlui.addString('external_jid') | |
107 xmlui.addButton(self.__menu_id, _(u'Go !'), fields_back=('external_jid',)) | |
108 return xmlui | |
109 | |
110 def _gatewaySelectedCb(self, data, profile): | |
111 try: | |
112 target_jid = jid.JID(data['index']) | |
113 except (KeyError, RuntimeError): | |
114 log.warning(_("No gateway index selected")) | |
115 return {} | |
116 | |
117 d = self.gatewayRegister(target_jid, profile) | |
118 d.addCallback(lambda xmlui: {'xmlui': xmlui.toXml()}) | |
119 return d | |
120 | |
121 def _getIdentityDesc(self, identity): | |
122 """ Return a human readable description of identity | |
123 @param identity: tuple as returned by Disco identities (category, type) | |
124 | |
125 """ | |
126 category, type_ = identity | |
127 if category != 'gateway': | |
128 log.error(_(u'INTERNAL ERROR: identity category should always be "gateway" in _getTypeString, got "%s"') % category) | |
129 try: | |
130 return _(TYPE_DESCRIPTIONS[type_]) | |
131 except KeyError: | |
132 return _("Unknown IM") | |
133 | |
134 def _registrationSuccessful(self, jid_, profile): | |
135 """Called when in_band registration is ok, we must now follow the rest of procedure""" | |
136 log.debug(_("Registration successful, doing the rest")) | |
137 self.host.addContact(jid_, profile_key=profile) | |
138 self.host.setPresence(jid_, profile_key=profile) | |
139 | |
140 def _gatewayRegister(self, target_jid_s, profile_key=C.PROF_KEY_NONE): | |
141 d = self.gatewayRegister(jid.JID(target_jid_s), profile_key) | |
142 d.addCallback(lambda xmlui: xmlui.toXml()) | |
143 return d | |
144 | |
145 def gatewayRegister(self, target_jid, profile_key=C.PROF_KEY_NONE): | |
146 """Register gateway using in-band registration, then log-in to gateway""" | |
147 profile = self.host.memory.getProfileName(profile_key) | |
148 assert(profile) | |
149 d = self.host.plugins["XEP-0077"].inBandRegister(target_jid, self._registrationSuccessful, profile) | |
150 return d | |
151 | |
152 def _infosReceived(self, dl_result, items, target, client): | |
153 """Find disco infos about entity, to check if it is a gateway""" | |
154 | |
155 ret = [] | |
156 for idx, (success, result) in enumerate(dl_result): | |
157 if not success: | |
158 if isinstance(result.value, defer.CancelledError): | |
159 msg = _("Timeout") | |
160 else: | |
161 try: | |
162 msg = result.value.condition | |
163 except AttributeError: | |
164 msg = str(result) | |
165 ret.append((success, (msg, items[idx]))) | |
166 else: | |
167 entity = items[idx].entity | |
168 gateways = [(identity, result.identities[identity]) for identity in result.identities if identity[0] == 'gateway'] | |
169 if gateways: | |
170 log.info(_(u"Found gateway [%(jid)s]: %(identity_name)s") % {'jid': entity.full(), 'identity_name': ' - '.join([gateway[1] for gateway in gateways])}) | |
171 ret.append((success, (entity, gateways))) | |
172 else: | |
173 log.info(_(u"Skipping [%(jid)s] which is not a gateway") % {'jid': entity.full()}) | |
174 return ret | |
175 | |
176 def _itemsReceived(self, disco, target, client): | |
177 """Look for items with disco protocol, and ask infos for each one""" | |
178 | |
179 if len(disco._items) == 0: | |
180 log.debug(_("No gateway found")) | |
181 return [] | |
182 | |
183 _defers = [] | |
184 for item in disco._items: | |
185 log.debug(_(u"item found: %s") % item.entity) | |
186 _defers.append(client.disco.requestInfo(item.entity)) | |
187 dl = defer.DeferredList(_defers) | |
188 dl.addCallback(self._infosReceived, items=disco._items, target=target, client=client) | |
189 reactor.callLater(GATEWAY_TIMEOUT, dl.cancel) | |
190 return dl | |
191 | |
192 def _findGateways(self, target_jid_s, profile_key): | |
193 target_jid = jid.JID(target_jid_s) | |
194 profile = self.host.memory.getProfileName(profile_key) | |
195 if not profile: | |
196 raise exceptions.ProfileUnknownError | |
197 d = self.findGateways(target_jid, profile) | |
198 d.addCallback(self._gatewaysResult2XMLUI, target_jid) | |
199 d.addCallback(lambda xmlui: xmlui.toXml()) | |
200 return d | |
201 | |
202 def findGateways(self, target, profile): | |
203 """Find gateways in the target JID, using discovery protocol | |
204 """ | |
205 client = self.host.getClient(profile) | |
206 log.debug(_(u"find gateways (target = %(target)s, profile = %(profile)s)") % {'target': target.full(), 'profile': profile}) | |
207 d = client.disco.requestItems(target) | |
208 d.addCallback(self._itemsReceived, target=target, client=client) | |
209 return d |