266 lines
8.8 KiB
Python
266 lines
8.8 KiB
Python
|
# This file is part of Gajim.
|
||
|
#
|
||
|
# Gajim is free software; you can redistribute it and/or modify
|
||
|
# it under the terms of the GNU General Public License as published
|
||
|
# by the Free Software Foundation; version 3 only.
|
||
|
#
|
||
|
# Gajim is distributed in the hope that it will be useful,
|
||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
# GNU General Public License for more details.
|
||
|
#
|
||
|
# You should have received a copy of the GNU General Public License
|
||
|
# along with Gajim. If not, see <http://www.gnu.org/licenses/>.
|
||
|
|
||
|
# XEP-0030: Service Discovery
|
||
|
|
||
|
import nbxmpp
|
||
|
from nbxmpp.namespaces import Namespace
|
||
|
from nbxmpp.structs import StanzaHandler
|
||
|
from nbxmpp.errors import StanzaError
|
||
|
from nbxmpp.errors import is_error
|
||
|
|
||
|
from gajim.common import app
|
||
|
from gajim.common.nec import NetworkIncomingEvent
|
||
|
from gajim.common.nec import NetworkEvent
|
||
|
from gajim.common.modules.util import as_task
|
||
|
from gajim.common.modules.base import BaseModule
|
||
|
|
||
|
|
||
|
class Discovery(BaseModule):
|
||
|
|
||
|
_nbxmpp_extends = 'Discovery'
|
||
|
_nbxmpp_methods = [
|
||
|
'disco_info',
|
||
|
'disco_items',
|
||
|
]
|
||
|
|
||
|
def __init__(self, con):
|
||
|
BaseModule.__init__(self, con)
|
||
|
|
||
|
self.handlers = [
|
||
|
StanzaHandler(name='iq',
|
||
|
callback=self._answer_disco_info,
|
||
|
typ='get',
|
||
|
ns=Namespace.DISCO_INFO),
|
||
|
StanzaHandler(name='iq',
|
||
|
callback=self._answer_disco_items,
|
||
|
typ='get',
|
||
|
ns=Namespace.DISCO_ITEMS),
|
||
|
]
|
||
|
|
||
|
self._account_info = None
|
||
|
self._server_info = None
|
||
|
|
||
|
@property
|
||
|
def account_info(self):
|
||
|
return self._account_info
|
||
|
|
||
|
@property
|
||
|
def server_info(self):
|
||
|
return self._server_info
|
||
|
|
||
|
def discover_server_items(self):
|
||
|
server = self._con.get_own_jid().domain
|
||
|
self.disco_items(server, callback=self._server_items_received)
|
||
|
|
||
|
def _server_items_received(self, task):
|
||
|
try:
|
||
|
result = task.finish()
|
||
|
except StanzaError as error:
|
||
|
self._log.warning('Server disco failed')
|
||
|
self._log.error(error)
|
||
|
return
|
||
|
|
||
|
self._log.info('Server items received')
|
||
|
self._log.debug(result)
|
||
|
for item in result.items:
|
||
|
if item.node is not None:
|
||
|
# Only disco components
|
||
|
continue
|
||
|
self.disco_info(item.jid, callback=self._server_items_info_received)
|
||
|
|
||
|
def _server_items_info_received(self, task):
|
||
|
try:
|
||
|
result = task.finish()
|
||
|
except StanzaError as error:
|
||
|
self._log.warning('Server item disco info failed')
|
||
|
self._log.warning(error)
|
||
|
return
|
||
|
|
||
|
self._log.info('Server item info received: %s', result.jid)
|
||
|
self._parse_transports(result)
|
||
|
try:
|
||
|
self._con.get_module('MUC').pass_disco(result)
|
||
|
self._con.get_module('HTTPUpload').pass_disco(result)
|
||
|
self._con.get_module('Bytestream').pass_disco(result)
|
||
|
except nbxmpp.NodeProcessed:
|
||
|
pass
|
||
|
|
||
|
app.nec.push_incoming_event(
|
||
|
NetworkIncomingEvent('server-disco-received'))
|
||
|
|
||
|
def discover_account_info(self):
|
||
|
own_jid = self._con.get_own_jid().bare
|
||
|
self.disco_info(own_jid, callback=self._account_info_received)
|
||
|
|
||
|
def _account_info_received(self, task):
|
||
|
try:
|
||
|
result = task.finish()
|
||
|
except StanzaError as error:
|
||
|
self._log.warning('Account disco info failed')
|
||
|
self._log.warning(error)
|
||
|
return
|
||
|
|
||
|
self._log.info('Account info received: %s', result.jid)
|
||
|
|
||
|
self._account_info = result
|
||
|
|
||
|
self._con.get_module('MAM').pass_disco(result)
|
||
|
self._con.get_module('PEP').pass_disco(result)
|
||
|
self._con.get_module('PubSub').pass_disco(result)
|
||
|
self._con.get_module('Bookmarks').pass_disco(result)
|
||
|
self._con.get_module('VCardAvatars').pass_disco(result)
|
||
|
|
||
|
self._con.get_module('Caps').update_caps()
|
||
|
|
||
|
def discover_server_info(self):
|
||
|
# Calling this method starts the connect_maschine()
|
||
|
server = self._con.get_own_jid().domain
|
||
|
self.disco_info(server, callback=self._server_info_received)
|
||
|
|
||
|
def _server_info_received(self, task):
|
||
|
try:
|
||
|
result = task.finish()
|
||
|
except StanzaError as error:
|
||
|
self._log.error('Server disco info failed')
|
||
|
self._log.error(error)
|
||
|
return
|
||
|
|
||
|
self._log.info('Server info received: %s', result.jid)
|
||
|
|
||
|
self._server_info = result
|
||
|
|
||
|
self._con.get_module('SecLabels').pass_disco(result)
|
||
|
self._con.get_module('Blocking').pass_disco(result)
|
||
|
self._con.get_module('VCardTemp').pass_disco(result)
|
||
|
self._con.get_module('Carbons').pass_disco(result)
|
||
|
self._con.get_module('HTTPUpload').pass_disco(result)
|
||
|
self._con.get_module('Register').pass_disco(result)
|
||
|
|
||
|
self._con.connect_machine(restart=True)
|
||
|
|
||
|
def _parse_transports(self, info):
|
||
|
for identity in info.identities:
|
||
|
if identity.category not in ('gateway', 'headline'):
|
||
|
continue
|
||
|
|
||
|
self._log.info('Found transport: %s %s %s',
|
||
|
info.jid, identity.category, identity.type)
|
||
|
|
||
|
jid = str(info.jid)
|
||
|
if jid not in app.transport_type:
|
||
|
app.transport_type[jid] = identity.type
|
||
|
|
||
|
if identity.type in self._con.available_transports:
|
||
|
self._con.available_transports[identity.type].append(jid)
|
||
|
else:
|
||
|
self._con.available_transports[identity.type] = [jid]
|
||
|
|
||
|
def _answer_disco_items(self, _con, stanza, _properties):
|
||
|
from_ = stanza.getFrom()
|
||
|
self._log.info('Answer disco items to %s', from_)
|
||
|
|
||
|
if self._con.get_module('AdHocCommands').command_items_query(stanza):
|
||
|
raise nbxmpp.NodeProcessed
|
||
|
|
||
|
node = stanza.getTagAttr('query', 'node')
|
||
|
if node is None:
|
||
|
result = stanza.buildReply('result')
|
||
|
self._con.connection.send(result)
|
||
|
raise nbxmpp.NodeProcessed
|
||
|
|
||
|
if node == Namespace.COMMANDS:
|
||
|
self._con.get_module('AdHocCommands').command_list_query(stanza)
|
||
|
raise nbxmpp.NodeProcessed
|
||
|
|
||
|
def _answer_disco_info(self, _con, stanza, _properties):
|
||
|
from_ = stanza.getFrom()
|
||
|
self._log.info('Answer disco info %s', from_)
|
||
|
if str(from_).startswith('echo.'):
|
||
|
# Service that echos all stanzas, ignore it
|
||
|
raise nbxmpp.NodeProcessed
|
||
|
|
||
|
if self._con.get_module('AdHocCommands').command_info_query(stanza):
|
||
|
raise nbxmpp.NodeProcessed
|
||
|
|
||
|
@as_task
|
||
|
def disco_muc(self,
|
||
|
jid,
|
||
|
request_vcard=False,
|
||
|
allow_redirect=False):
|
||
|
|
||
|
_task = yield
|
||
|
|
||
|
self._log.info('Request MUC info for %s', jid)
|
||
|
|
||
|
result = yield self._nbxmpp('MUC').request_info(
|
||
|
jid,
|
||
|
request_vcard=request_vcard,
|
||
|
allow_redirect=allow_redirect)
|
||
|
|
||
|
if is_error(result):
|
||
|
raise result
|
||
|
|
||
|
if result.redirected:
|
||
|
self._log.info('MUC info received after redirect: %s -> %s',
|
||
|
jid, result.info.jid)
|
||
|
else:
|
||
|
self._log.info('MUC info received: %s', result.info.jid)
|
||
|
|
||
|
app.storage.cache.set_last_disco_info(result.info.jid, result.info)
|
||
|
|
||
|
if result.vcard is not None:
|
||
|
avatar, avatar_sha = result.vcard.get_avatar()
|
||
|
if avatar is not None:
|
||
|
if not app.interface.avatar_exists(avatar_sha):
|
||
|
app.interface.save_avatar(avatar)
|
||
|
|
||
|
app.storage.cache.set_muc_avatar_sha(result.info.jid,
|
||
|
avatar_sha)
|
||
|
app.interface.avatar_storage.invalidate_cache(result.info.jid)
|
||
|
|
||
|
self._con.get_module('VCardAvatars').muc_disco_info_update(result.info)
|
||
|
app.nec.push_incoming_event(NetworkEvent(
|
||
|
'muc-disco-update',
|
||
|
account=self._account,
|
||
|
room_jid=result.info.jid))
|
||
|
|
||
|
yield result
|
||
|
|
||
|
@as_task
|
||
|
def disco_contact(self, contact):
|
||
|
_task = yield
|
||
|
|
||
|
fjid = contact.get_full_jid()
|
||
|
|
||
|
result = yield self.disco_info(fjid)
|
||
|
if is_error(result):
|
||
|
raise result
|
||
|
|
||
|
self._log.info('Disco Info received: %s', fjid)
|
||
|
|
||
|
app.storage.cache.set_last_disco_info(result.jid,
|
||
|
result,
|
||
|
cache_only=True)
|
||
|
|
||
|
app.nec.push_incoming_event(
|
||
|
NetworkEvent('caps-update',
|
||
|
account=self._account,
|
||
|
fjid=fjid,
|
||
|
jid=contact.jid))
|
||
|
|
||
|
|
||
|
def get_instance(*args, **kwargs):
|
||
|
return Discovery(*args, **kwargs), 'Discovery'
|