Mercurial > libervia-backend
diff src/plugins/plugin_xep_0100.py @ 807:be4c5e24dab9
plugin XEP-0077, plugin XEP-0100, frontends: gateways have been entirely implemented in backend using the new refactored XMLUI and AdvancedListContainer. The now useless code has been removed from frontends.
author | Goffi <goffi@goffi.org> |
---|---|
date | Tue, 04 Feb 2014 18:26:03 +0100 |
parents | bfabeedbf32e |
children | 743b757777d3 |
line wrap: on
line diff
--- a/src/plugins/plugin_xep_0100.py Tue Feb 04 18:24:27 2014 +0100 +++ b/src/plugins/plugin_xep_0100.py Tue Feb 04 18:26:03 2014 +0100 @@ -17,11 +17,13 @@ # You should have received a copy of the GNU Affero General Public License # along with this program. If not, see <http://www.gnu.org/licenses/>. -from sat.core.i18n import _ -from logging import debug, info, error +from sat.core.i18n import _, D_ +from sat.core import exceptions +from sat.tools import xml_tools +from logging import debug, info, warning, error from twisted.words.protocols.jabber import client as jabber_client, jid from twisted.words.protocols.jabber import error as jab_error -import twisted.internet.error +from twisted.internet import reactor, defer PLUGIN_INFO = { "name": "Gateways Plugin", @@ -33,6 +35,22 @@ "description": _("""Implementation of Gateways protocol""") } +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. +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).""") + +GATEWAY_TIMEOUT = 10 # time to wait before cancelling a gateway disco info, in seconds + +TYPE_DESCRIPTIONS = { 'irc': D_("Internet Relay Chat"), + 'xmpp': D_("XMPP"), + 'qq': D_("Tencent QQ"), + 'simple': D_("SIP/SIMPLE"), + 'icq': D_("ICQ"), + 'yahoo': D_("Yahoo! Messenger"), + 'gadu-gadu': D_("Gadu-Gadu"), + 'aim': D_("AOL Instant Messenger"), + 'msn': D_("Windows Live Messenger"), + } + class XEP_0100(object): @@ -40,82 +58,155 @@ info(_("Gateways plugin initialization")) self.host = host self.__gateways = {} # dict used to construct the answer to findGateways. Key = target jid - host.bridge.addMethod("findGateways", ".plugin", in_sign='ss', out_sign='s', method=self.findGateways) - host.bridge.addMethod("gatewayRegister", ".plugin", in_sign='ssa(ss)s', out_sign='s', method=self.gatewayRegister) + host.bridge.addMethod("findGateways", ".plugin", in_sign='ss', out_sign='s', method=self._findGateways) + host.bridge.addMethod("gatewayRegister", ".plugin", in_sign='ss', out_sign='s', method=self._gatewayRegister) + self.__menu_id = host.registerCallback(self._gatewaysMenu, with_data=True) + self.__selected_id = host.registerCallback(self._gatewaySelectedCb, with_data=True) + host.importMenu((D_("Service"), D_("gateways")), self._gatewaysMenu, help_string=D_("Find gateways")) + + + def _gatewaysMenu(self, data, profile): + """ XMLUI activated by menu: return Gateways UI + @param profile: %(doc_profile)s - def __inc_handled_items(self, request_id, profile): - self.__gateways[request_id]['__handled_items'] += 1 + """ + client = self.host.getClient(profile) + try: + jid_ = jid.JID(data.get(xml_tools.formEscape('external_jid'), client.jid.host)) + except RuntimeError: + raise exceptions.DataError(_("Invalid JID")) + d = self.findGateways(jid_, profile) + d.addCallback(self._gatewaysResult2XMLUI, jid_) + d.addCallback(lambda xmlui: {'xmlui': xmlui.toXml()}) + return d - if self.__gateways[request_id]['__total_items'] == self.__gateways[request_id]['__handled_items']: - debug(_("All items checked for id [%s]") % str(request_id)) + def _gatewaysResult2XMLUI(self, result, entity): + xmlui = xml_tools.XMLUI(title=_('Gateways manager (%s)') % entity.full()) + xmlui.addText(_(WARNING_MSG)) + xmlui.addDivider('dash') + adv_list = xmlui.changeContainer('advanced_list', columns=3, selectable='single', callback_id=self.__selected_id) + for success, gateway_data in result: + if not success: + fail_cond, disco_item = gateway_data + xmlui.addJid(disco_item.entity) + xmlui.addText(_('Failed (%s)') % fail_cond) + xmlui.addEmpty() + else: + jid_, data = gateway_data + for datum in data: + identity, name = datum + adv_list.setRowIndex(jid_.full()) + xmlui.addJid(jid_) + xmlui.addText(name) + xmlui.addText(self._getIdentityDesc(identity)) + adv_list.end() + xmlui.addDivider('blank') + xmlui.changeContainer('advanced_list', columns=3) + xmlui.addLabel(_('Use external XMPP server')) + xmlui.addString('external_jid') + xmlui.addButton(self.__menu_id, _(u'Go !'), fields_back=('external_jid',)) + return xmlui + + def _gatewaySelectedCb(self, data, profile): + try: + target_jid = jid.JID(data['index']) + except (KeyError, RuntimeError): + warning(_("No gateway index selected")) + return {} - del self.__gateways[request_id]['__total_items'] - del self.__gateways[request_id]['__handled_items'] - self.host.actionResultExt(request_id, "DICT_DICT", self.__gateways[request_id], profile) + d = self.gatewayRegister(target_jid, profile) + d.addCallback(lambda xmlui: {'xmlui': xmlui.toXml()}) + return d + + def _getIdentityDesc(self, identity): + """ Return a human readable description of identity + @param identity: tuple as returned by Disco identities (category, type) + + """ + category, type_ = identity + if category != 'gateway': + error(_('INTERNAL ERROR: identity category should always be "gateway" in _getTypeString, got "%s"') % category) + try: + return _(TYPE_DESCRIPTIONS[type_]) + except KeyError: + return _("Unknown IM") - def discoInfo(self, disco, entity, request_id, profile): + def _registrationSuccessful(self, jid_, profile): + """Called when in_band registration is ok, we must now follow the rest of procedure""" + debug(_("Registration successful, doing the rest")) + self.host.addContact(jid_, profile_key=profile) + self.host.setPresence(jid_, profile_key=profile) + + def _gatewayRegister(self, target_jid_s, profile_key='@NONE@'): + d = self.gatewayRegister(jid.JID(target_jid_s), profile_key) + d.addCallback(lambda xmlui: xmlui.toXml()) + return d + + def gatewayRegister(self, target_jid, profile_key='@NONE@'): + """Register gateway using in-band registration, then log-in to gateway""" + profile = self.host.memory.getProfileName(profile_key) + assert(profile) + d = self.host.plugins["XEP-0077"].inBandRegister(target_jid, self._registrationSuccessful, profile) + return d + + def _infosReceived(self, dl_result, items, target, client): """Find disco infos about entity, to check if it is a gateway""" - for identity in disco.identities: - if identity[0] == 'gateway': - print (_("Found gateway (%(jid)s): %(identity)s") % {'jid': entity.full(), 'identity': disco.identities[identity]}) - self.__gateways[request_id][entity.full()] = { - 'name': disco.identities[identity], - 'type': identity[1] - } - - self.__inc_handled_items(request_id, profile) + ret = [] + for idx, (success, result) in enumerate(dl_result): + if not success: + if isinstance(result.value, defer.CancelledError): + msg = _("Timeout") + else: + try: + msg = result.value.condition + except AttibuteError: + msg = str(result) + ret.append((success, (msg, items[idx]))) + else: + entity = items[idx].entity + gateways = [(identity, result.identities[identity]) for identity in result.identities if identity[0] == 'gateway'] + if gateways: + info (_("Found gateway [%(jid)s]: %(identity_name)s") % {'jid': entity.full(), 'identity_name': ' - '.join([gateway[1] for gateway in gateways])}) + ret.append((success, (entity, gateways))) + else: + info(_("Skipping [%(jid)s] which is not a gateway") % {'jid': entity.full()}) + return ret - def discoInfoErr(self, failure, entity, request_id, profile): - """Something is going wrong with disco""" - failure.trap(jab_error.StanzaError, twisted.internet.error.ConnectionLost) - error(_("Error when discovering [%(jid)s]: %(error)s") % {'jid': entity.full(), 'error': failure.getErrorMessage()}) - self.__inc_handled_items(request_id, profile) + def _itemsReceived(self, disco, target, client): + """Look for items with disco protocol, and ask infos for each one""" - def discoItems(self, disco, request_id, target, client): - """Look for items with disco protocol, and ask infos for each one""" - #FIXME: target is used as we can't find the original iq node (parent is None) - # an other way would avoid this useless parameter (is there a way with wokkel ?) if len(disco._items) == 0: debug(_("No gateway found")) - self.host.actionResultExt(request_id, "DICT_DICT", {}) - return - - self.__gateways[request_id] = {'__total_items': len(disco._items), '__handled_items': 0, '__private__': {'target': target.full()}} - for item in disco._items: - #TODO: need to set a timeout for theses requests - debug(_("item found: %s"), item.name) - client.disco.requestInfo(item.entity).addCallback(self.discoInfo, entity=item.entity, request_id=request_id, profile=client.profile).addErrback(self.discoInfoErr, entity=item.entity, request_id=request_id, profile=client.profile) + return [] - def discoItemsErr(self, failure, request_id, target, client): - """Something is going wrong with disco""" - error(_("Error when discovering [%(target)s]: %(condition)s") % {'target': target.full(), 'condition': unicode(failure.value)}) - message_data = {"reason": "connection error", "message": _(u"Error while trying to discover %(target)s gateways: %(error_mess)s") % {'target': target.full(), 'error_mess': unicode(failure.value)}} - self.host.bridge.actionResult("ERROR", request_id, message_data) + _defers = [] + for item in disco._items: + debug(_("item found: %s"), item.entity) + _defers.append(client.disco.requestInfo(item.entity)) + dl = defer.DeferredList(_defers) + dl.addCallback(self._infosReceived, items=disco._items, target=target, client=client) + reactor.callLater(GATEWAY_TIMEOUT, dl.cancel) + return dl - def registrationSuccessful(self, target, profile): - """Called when in_band registration is ok, we must now follow the rest of procedure""" - debug(_("Registration successful, doing the rest")) - self.host.addContact(target, profile) - self.host.setPresence(target, profile) - def gatewayRegister(self, action, target, fields, profile_key='@DEFAULT@'): - """Register gateway using in-band registration, then log-in to gateway""" + def _findGateways(self, target_jid_s, profile_key): + target_jid = jid.JID(target_jid_s) profile = self.host.memory.getProfileName(profile_key) - assert(profile) # FIXME: return an error here - if action == 'SUBMIT': - self.host.plugins["XEP-0077"].addTrigger(target, self.registrationSuccessful, profile) - return self.host.plugins["XEP-0077"].in_band_submit(action, target, fields, profile) + if not profile: + raise exceptions.ProfileUnknownError + d = self.findGateways(target_jid, profile) + d.addCallback(self._gatewaysResult2XMLUI, target_jid) + d.addCallback(lambda xmlui: xmlui.toXml()) + return d - def findGateways(self, target, profile_key): + + def findGateways(self, target, profile): """Find gateways in the target JID, using discovery protocol - Return an id used for retrieving the list of gateways """ - profile = self.host.memory.getProfileName(profile_key) - client = self.host.getClient(profile_key) + client = self.host.getClient(profile) assert(client) - to_jid = jid.JID(target) - debug(_("find gateways (target = %(target)s, profile = %(profile)s)") % {'target': to_jid.full(), 'profile': profile}) - request_id = self.host.get_next_id() - client.disco.requestItems(to_jid).addCallback(self.discoItems, request_id=request_id, target=to_jid, client=client).addErrback(self.discoItemsErr, request_id=request_id, target=to_jid, client=client) - return request_id + debug(_("find gateways (target = %(target)s, profile = %(profile)s)") % {'target': target.full(), 'profile': profile}) + d = client.disco.requestItems(target) + d.addCallback(self._itemsReceived , target=target, client=client) + return d