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