Mercurial > libervia-backend
comparison 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 |
comparison
equal
deleted
inserted
replaced
806:5d6c45d6ee1b | 807:be4c5e24dab9 |
---|---|
15 # GNU Affero General Public License for more details. | 15 # GNU Affero General Public License for more details. |
16 | 16 |
17 # You should have received a copy of the GNU Affero General Public License | 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/>. | 18 # along with this program. If not, see <http://www.gnu.org/licenses/>. |
19 | 19 |
20 from sat.core.i18n import _ | 20 from sat.core.i18n import _, D_ |
21 from logging import debug, info, error | 21 from sat.core import exceptions |
22 from sat.tools import xml_tools | |
23 from logging import debug, info, warning, error | |
22 from twisted.words.protocols.jabber import client as jabber_client, jid | 24 from twisted.words.protocols.jabber import client as jabber_client, jid |
23 from twisted.words.protocols.jabber import error as jab_error | 25 from twisted.words.protocols.jabber import error as jab_error |
24 import twisted.internet.error | 26 from twisted.internet import reactor, defer |
25 | 27 |
26 PLUGIN_INFO = { | 28 PLUGIN_INFO = { |
27 "name": "Gateways Plugin", | 29 "name": "Gateways Plugin", |
28 "import_name": "XEP-0100", | 30 "import_name": "XEP-0100", |
29 "type": "XEP", | 31 "type": "XEP", |
31 "dependencies": ["XEP-0077"], | 33 "dependencies": ["XEP-0077"], |
32 "main": "XEP_0100", | 34 "main": "XEP_0100", |
33 "description": _("""Implementation of Gateways protocol""") | 35 "description": _("""Implementation of Gateways protocol""") |
34 } | 36 } |
35 | 37 |
38 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. | |
39 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).""") | |
40 | |
41 GATEWAY_TIMEOUT = 10 # time to wait before cancelling a gateway disco info, in seconds | |
42 | |
43 TYPE_DESCRIPTIONS = { 'irc': D_("Internet Relay Chat"), | |
44 'xmpp': D_("XMPP"), | |
45 'qq': D_("Tencent QQ"), | |
46 'simple': D_("SIP/SIMPLE"), | |
47 'icq': D_("ICQ"), | |
48 'yahoo': D_("Yahoo! Messenger"), | |
49 'gadu-gadu': D_("Gadu-Gadu"), | |
50 'aim': D_("AOL Instant Messenger"), | |
51 'msn': D_("Windows Live Messenger"), | |
52 } | |
53 | |
36 | 54 |
37 class XEP_0100(object): | 55 class XEP_0100(object): |
38 | 56 |
39 def __init__(self, host): | 57 def __init__(self, host): |
40 info(_("Gateways plugin initialization")) | 58 info(_("Gateways plugin initialization")) |
41 self.host = host | 59 self.host = host |
42 self.__gateways = {} # dict used to construct the answer to findGateways. Key = target jid | 60 self.__gateways = {} # dict used to construct the answer to findGateways. Key = target jid |
43 host.bridge.addMethod("findGateways", ".plugin", in_sign='ss', out_sign='s', method=self.findGateways) | 61 host.bridge.addMethod("findGateways", ".plugin", in_sign='ss', out_sign='s', method=self._findGateways) |
44 host.bridge.addMethod("gatewayRegister", ".plugin", in_sign='ssa(ss)s', out_sign='s', method=self.gatewayRegister) | 62 host.bridge.addMethod("gatewayRegister", ".plugin", in_sign='ss', out_sign='s', method=self._gatewayRegister) |
45 | 63 self.__menu_id = host.registerCallback(self._gatewaysMenu, with_data=True) |
46 def __inc_handled_items(self, request_id, profile): | 64 self.__selected_id = host.registerCallback(self._gatewaySelectedCb, with_data=True) |
47 self.__gateways[request_id]['__handled_items'] += 1 | 65 host.importMenu((D_("Service"), D_("gateways")), self._gatewaysMenu, help_string=D_("Find gateways")) |
48 | 66 |
49 if self.__gateways[request_id]['__total_items'] == self.__gateways[request_id]['__handled_items']: | 67 |
50 debug(_("All items checked for id [%s]") % str(request_id)) | 68 def _gatewaysMenu(self, data, profile): |
51 | 69 """ XMLUI activated by menu: return Gateways UI |
52 del self.__gateways[request_id]['__total_items'] | 70 @param profile: %(doc_profile)s |
53 del self.__gateways[request_id]['__handled_items'] | 71 |
54 self.host.actionResultExt(request_id, "DICT_DICT", self.__gateways[request_id], profile) | 72 """ |
55 | 73 client = self.host.getClient(profile) |
56 def discoInfo(self, disco, entity, request_id, 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 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 error(_('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 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='@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='@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): | |
57 """Find disco infos about entity, to check if it is a gateway""" | 153 """Find disco infos about entity, to check if it is a gateway""" |
58 | 154 |
59 for identity in disco.identities: | 155 ret = [] |
60 if identity[0] == 'gateway': | 156 for idx, (success, result) in enumerate(dl_result): |
61 print (_("Found gateway (%(jid)s): %(identity)s") % {'jid': entity.full(), 'identity': disco.identities[identity]}) | 157 if not success: |
62 self.__gateways[request_id][entity.full()] = { | 158 if isinstance(result.value, defer.CancelledError): |
63 'name': disco.identities[identity], | 159 msg = _("Timeout") |
64 'type': identity[1] | 160 else: |
65 } | 161 try: |
66 | 162 msg = result.value.condition |
67 self.__inc_handled_items(request_id, profile) | 163 except AttibuteError: |
68 | 164 msg = str(result) |
69 def discoInfoErr(self, failure, entity, request_id, profile): | 165 ret.append((success, (msg, items[idx]))) |
70 """Something is going wrong with disco""" | 166 else: |
71 failure.trap(jab_error.StanzaError, twisted.internet.error.ConnectionLost) | 167 entity = items[idx].entity |
72 error(_("Error when discovering [%(jid)s]: %(error)s") % {'jid': entity.full(), 'error': failure.getErrorMessage()}) | 168 gateways = [(identity, result.identities[identity]) for identity in result.identities if identity[0] == 'gateway'] |
73 self.__inc_handled_items(request_id, profile) | 169 if gateways: |
74 | 170 info (_("Found gateway [%(jid)s]: %(identity_name)s") % {'jid': entity.full(), 'identity_name': ' - '.join([gateway[1] for gateway in gateways])}) |
75 def discoItems(self, disco, request_id, target, client): | 171 ret.append((success, (entity, gateways))) |
172 else: | |
173 info(_("Skipping [%(jid)s] which is not a gateway") % {'jid': entity.full()}) | |
174 return ret | |
175 | |
176 def _itemsReceived(self, disco, target, client): | |
76 """Look for items with disco protocol, and ask infos for each one""" | 177 """Look for items with disco protocol, and ask infos for each one""" |
77 #FIXME: target is used as we can't find the original iq node (parent is None) | 178 |
78 # an other way would avoid this useless parameter (is there a way with wokkel ?) | |
79 if len(disco._items) == 0: | 179 if len(disco._items) == 0: |
80 debug(_("No gateway found")) | 180 debug(_("No gateway found")) |
81 self.host.actionResultExt(request_id, "DICT_DICT", {}) | 181 return [] |
82 return | 182 |
83 | 183 _defers = [] |
84 self.__gateways[request_id] = {'__total_items': len(disco._items), '__handled_items': 0, '__private__': {'target': target.full()}} | |
85 for item in disco._items: | 184 for item in disco._items: |
86 #TODO: need to set a timeout for theses requests | 185 debug(_("item found: %s"), item.entity) |
87 debug(_("item found: %s"), item.name) | 186 _defers.append(client.disco.requestInfo(item.entity)) |
88 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) | 187 dl = defer.DeferredList(_defers) |
89 | 188 dl.addCallback(self._infosReceived, items=disco._items, target=target, client=client) |
90 def discoItemsErr(self, failure, request_id, target, client): | 189 reactor.callLater(GATEWAY_TIMEOUT, dl.cancel) |
91 """Something is going wrong with disco""" | 190 return dl |
92 error(_("Error when discovering [%(target)s]: %(condition)s") % {'target': target.full(), 'condition': unicode(failure.value)}) | 191 |
93 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)}} | 192 |
94 self.host.bridge.actionResult("ERROR", request_id, message_data) | 193 def _findGateways(self, target_jid_s, profile_key): |
95 | 194 target_jid = jid.JID(target_jid_s) |
96 def registrationSuccessful(self, target, profile): | |
97 """Called when in_band registration is ok, we must now follow the rest of procedure""" | |
98 debug(_("Registration successful, doing the rest")) | |
99 self.host.addContact(target, profile) | |
100 self.host.setPresence(target, profile) | |
101 | |
102 def gatewayRegister(self, action, target, fields, profile_key='@DEFAULT@'): | |
103 """Register gateway using in-band registration, then log-in to gateway""" | |
104 profile = self.host.memory.getProfileName(profile_key) | 195 profile = self.host.memory.getProfileName(profile_key) |
105 assert(profile) # FIXME: return an error here | 196 if not profile: |
106 if action == 'SUBMIT': | 197 raise exceptions.ProfileUnknownError |
107 self.host.plugins["XEP-0077"].addTrigger(target, self.registrationSuccessful, profile) | 198 d = self.findGateways(target_jid, profile) |
108 return self.host.plugins["XEP-0077"].in_band_submit(action, target, fields, profile) | 199 d.addCallback(self._gatewaysResult2XMLUI, target_jid) |
109 | 200 d.addCallback(lambda xmlui: xmlui.toXml()) |
110 def findGateways(self, target, profile_key): | 201 return d |
202 | |
203 | |
204 def findGateways(self, target, profile): | |
111 """Find gateways in the target JID, using discovery protocol | 205 """Find gateways in the target JID, using discovery protocol |
112 Return an id used for retrieving the list of gateways | |
113 """ | 206 """ |
114 profile = self.host.memory.getProfileName(profile_key) | 207 client = self.host.getClient(profile) |
115 client = self.host.getClient(profile_key) | |
116 assert(client) | 208 assert(client) |
117 to_jid = jid.JID(target) | 209 debug(_("find gateways (target = %(target)s, profile = %(profile)s)") % {'target': target.full(), 'profile': profile}) |
118 debug(_("find gateways (target = %(target)s, profile = %(profile)s)") % {'target': to_jid.full(), 'profile': profile}) | 210 d = client.disco.requestItems(target) |
119 request_id = self.host.get_next_id() | 211 d.addCallback(self._itemsReceived , target=target, client=client) |
120 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) | 212 return d |
121 return request_id |