From db77fa1aceb0364ad98521c38a5164997f3cd6c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philipp=20H=C3=B6rist?= Date: Thu, 26 Jul 2018 00:12:04 +0200 Subject: [PATCH] Add roster implementation to Gajim --- gajim/accounts_window.py | 2 + gajim/common/connection.py | 36 +- gajim/common/connection_handlers.py | 78 +---- gajim/common/connection_handlers_events.py | 87 ----- gajim/common/modules/__init__.py | 5 +- gajim/common/modules/presence.py | 6 +- gajim/common/modules/roster.py | 351 +++++++++++++++++++ gajim/common/zeroconf/connection_zeroconf.py | 28 +- gajim/common/zeroconf/roster_zeroconf.py | 9 +- gajim/gui_interface.py | 2 +- gajim/roster_window.py | 26 +- gajim/tooltips.py | 28 -- 12 files changed, 408 insertions(+), 250 deletions(-) create mode 100644 gajim/common/modules/roster.py diff --git a/gajim/accounts_window.py b/gajim/accounts_window.py index 118143619..3c8ee8051 100644 --- a/gajim/accounts_window.py +++ b/gajim/accounts_window.py @@ -301,6 +301,8 @@ class AccountsWindow(Gtk.ApplicationWindow): app.interface.roster.regroup = app.config.get('mergeaccounts') else: app.interface.roster.regroup = False + app.config.set_per( + 'accounts', account, 'roster_version', '') app.interface.roster.setup_and_draw_roster() gui_menu_builder.build_accounts_menu() diff --git a/gajim/common/connection.py b/gajim/common/connection.py index bcb75b1d3..30a6bb94e 100644 --- a/gajim/common/connection.py +++ b/gajim/common/connection.py @@ -387,15 +387,15 @@ class CommonConnection: raise NotImplementedError def update_contact(self, jid, name, groups): - if self.connection and self.roster_supported: - self.connection.getRoster().setItem(jid=jid, name=name, groups=groups) + if self.connection: + self.getRoster().setItem(jid=jid, name=name, groups=groups) def update_contacts(self, contacts): """ Update multiple roster items """ - if self.connection and self.roster_supported: - self.connection.getRoster().setItemMulti(contacts) + if self.connection: + self.getRoster().setItemMulti(contacts) def new_account(self, name, config, sync=False): """ @@ -443,10 +443,6 @@ class CommonConnection: return self.gpg.get_secret_keys() return None - def load_roster_from_db(self): - # Do nothing by default - return - def _event_dispatcher(self, realm, event, data): if realm == '': if event == 'STANZA_RECEIVED': @@ -1622,7 +1618,7 @@ class Connection(CommonConnection, ConnectionHandlers): iq.setID(id_) self.awaiting_answers[id_] = (AGENT_REMOVED, agent) self.connection.send(iq) - self.connection.getRoster().delItem(agent) + self.getRoster().delItem(agent) def send_new_account_infos(self, form, is_form): if is_form: @@ -1747,6 +1743,9 @@ class Connection(CommonConnection, ConnectionHandlers): iq3.addChild(name='meta', attrs=dict_) self.connection.send(iq) + def getRoster(self): + return self.get_module('Roster') + def request_roster(self, resume=False): version = None features = self.connection.Dispatcher.Stream.features @@ -1754,18 +1753,8 @@ class Connection(CommonConnection, ConnectionHandlers): version = app.config.get_per( 'accounts', self.name, 'roster_version') - iq_id = self.connection.initRoster(version=version, - request=not resume) - if resume: - self._init_roster_from_db() - else: - self.awaiting_answers[iq_id] = (ROSTER_ARRIVED, ) - - def _init_roster_from_db(self): - account_jid = app.get_jid_from_account(self.name) - roster_data = app.logger.get_roster(account_jid) - roster = self.connection.getRoster(force=True) - roster.setRaw(roster_data) + if not resume: + self.get_module('Roster').request_roster(version) def send_agent_status(self, agent, ptype): if not app.account_is_connected(self.name): @@ -2056,8 +2045,3 @@ class Connection(CommonConnection, ConnectionHandlers): self.reconnect() else: self.time_to_reconnect = None - - def load_roster_from_db(self): - app.nec.push_incoming_event(RosterReceivedEvent(None, conn=self)) - -# END Connection diff --git a/gajim/common/connection_handlers.py b/gajim/common/connection_handlers.py index 44a5580e5..1575438c6 100644 --- a/gajim/common/connection_handlers.py +++ b/gajim/common/connection_handlers.py @@ -468,19 +468,11 @@ class ConnectionHandlers(ConnectionSocks5Bytestream, ConnectionDisco, app.nec.register_incoming_event(StreamConflictReceivedEvent) app.nec.register_incoming_event(NotificationEvent) - app.ged.register_event_handler('roster-set-received', - ged.CORE, self._nec_roster_set_received) - app.ged.register_event_handler('roster-received', ged.CORE, - self._nec_roster_received) app.ged.register_event_handler('agent-removed', ged.CORE, self._nec_agent_removed) def cleanup(self): ConnectionHandlersBase.cleanup(self) - app.ged.remove_event_handler('roster-set-received', - ged.CORE, self._nec_roster_set_received) - app.ged.remove_event_handler('roster-received', ged.CORE, - self._nec_roster_received) app.ged.remove_event_handler('agent-removed', ged.CORE, self._nec_agent_removed) @@ -555,41 +547,6 @@ class ConnectionHandlers(ConnectionSocks5Bytestream, ConnectionDisco, # We can now continue connection by requesting the roster self.request_roster() - elif self.awaiting_answers[id_][0] == ROSTER_ARRIVED: - if iq_obj.getType() == 'result': - if not iq_obj.getTag('query'): - self._init_roster_from_db() - self._getRoster() - elif iq_obj.getType() == 'error': - self.roster_supported = False - self.get_module('Discovery').discover_server_items() - if app.config.get_per('accounts', self.name, - 'use_ft_proxies'): - self.discover_ft_proxies() - app.nec.push_incoming_event(RosterReceivedEvent(None, - conn=self)) - del self.awaiting_answers[id_] - - def _rosterSetCB(self, con, iq_obj): - log.debug('rosterSetCB') - app.nec.push_incoming_event(RosterSetReceivedEvent(None, conn=self, - stanza=iq_obj)) - raise nbxmpp.NodeProcessed - - def _nec_roster_set_received(self, obj): - if obj.conn.name != self.name: - return - for jid in obj.items: - item = obj.items[jid] - app.nec.push_incoming_event(RosterInfoEvent(None, conn=self, - jid=jid, nickname=item['name'], sub=item['sub'], - ask=item['ask'], groups=item['groups'])) - account_jid = app.get_jid_from_account(self.name) - app.logger.add_or_update_contact(account_jid, jid, item['name'], - item['sub'], item['ask'], item['groups']) - if obj.version: - app.config.set_per('accounts', self.name, 'roster_version', - obj.version) def _dispatch_gc_msg_with_captcha(self, stanza, msg_obj): msg_obj.stanza = stanza @@ -657,15 +614,6 @@ class ConnectionHandlers(ConnectionSocks5Bytestream, ConnectionDisco, # This way we'll really remove it app.to_be_removed[self.name].remove(jid) - def _getRoster(self): - log.debug('getRosterCB') - if not self.connection: - return - self.connection.getRoster(self._on_roster_set) - self.get_module('Discovery').discover_server_items() - if app.config.get_per('accounts', self.name, 'use_ft_proxies'): - self.discover_ft_proxies() - def discover_ft_proxies(self): cfg_proxies = app.config.get_per('accounts', self.name, 'file_transfer_proxies') @@ -679,15 +627,7 @@ class ConnectionHandlers(ConnectionSocks5Bytestream, ConnectionDisco, app.proxy65_manager.resolve(proxy, self.connection, our_jid, testit=testit) - def _on_roster_set(self, roster): - app.nec.push_incoming_event(RosterReceivedEvent(None, conn=self, - xmpp_roster=roster)) - - def _nec_roster_received(self, obj): - if obj.conn.name != self.name: - return - our_jid = app.get_jid_from_account(self.name) - + def send_first_presence(self): if self.connected > 1 and self.continue_connect_info: msg = self.continue_connect_info[1] sign_msg = self.continue_connect_info[2] @@ -705,20 +645,6 @@ class ConnectionHandlers(ConnectionSocks5Bytestream, ConnectionDisco, if send_first_presence: self._send_first_presence(signed) - app.logger.replace_roster(self.name, obj.version, obj.roster) - - for contact in app.contacts.iter_contacts(self.name): - if not contact.is_groupchat() and contact.jid not in obj.roster\ - and contact.jid != our_jid: - app.nec.push_incoming_event(RosterInfoEvent(None, - conn=self, jid=contact.jid, nickname=None, sub=None, - ask=None, groups=())) - for jid, info in obj.roster.items(): - app.nec.push_incoming_event(RosterInfoEvent(None, - conn=self, jid=jid, nickname=info['name'], - sub=info['subscription'], ask=info['ask'], - groups=info['groups'], avatar_sha=info['avatar_sha'])) - def _send_first_presence(self, signed=''): show = self.continue_connect_info[0] msg = self.continue_connect_info[1] @@ -786,7 +712,7 @@ class ConnectionHandlers(ConnectionSocks5Bytestream, ConnectionDisco, def _register_handlers(self, con, con_type): # try to find another way to register handlers in each class # that defines handlers - con.RegisterHandler('iq', self._rosterSetCB, 'set', nbxmpp.NS_ROSTER) + con.RegisterHandler('iq', self._siSetCB, 'set', nbxmpp.NS_SI) con.RegisterHandler('iq', self._siErrorCB, 'error', nbxmpp.NS_SI) con.RegisterHandler('iq', self._siResultCB, 'result', nbxmpp.NS_SI) diff --git a/gajim/common/connection_handlers_events.py b/gajim/common/connection_handlers_events.py index f65b21ab3..81216256f 100644 --- a/gajim/common/connection_handlers_events.py +++ b/gajim/common/connection_handlers_events.py @@ -179,93 +179,6 @@ class HelperEvent: self.muc_pm = muc_user.getChildren() == [] return self.muc_pm -class RosterReceivedEvent(nec.NetworkIncomingEvent): - name = 'roster-received' - base_network_events = [] - - def generate(self): - if hasattr(self, 'xmpp_roster'): - self.version = self.xmpp_roster.version - self.received_from_server = self.xmpp_roster.received_from_server - self.roster = {} - raw_roster = self.xmpp_roster.getRaw() - our_jid = app.get_jid_from_account(self.conn.name) - - for jid in raw_roster: - try: - j = helpers.parse_jid(jid) - except Exception: - print(_('JID %s is not RFC compliant. It will not be added ' - 'to your roster. Use roster management tools such as ' - 'http://jru.jabberstudio.org/ to remove it') % jid, - file=sys.stderr) - else: - infos = raw_roster[jid] - if jid != our_jid and (not infos['subscription'] or \ - infos['subscription'] == 'none') and (not infos['ask'] or \ - infos['ask'] == 'none') and not infos['name'] and \ - not infos['groups']: - # remove this useless item, it won't be shown in roster - # anyway - self.conn.connection.getRoster().delItem(jid) - elif jid != our_jid: # don't add our jid - self.roster[j] = raw_roster[jid] - self.roster[j]['avatar_sha'] = None - else: - # Roster comes from DB - self.received_from_server = False - self.version = app.config.get_per('accounts', self.conn.name, - 'roster_version') - self.roster = app.logger.get_roster(app.get_jid_from_account( - self.conn.name)) - if not self.roster: - app.config.set_per( - 'accounts', self.conn.name, 'roster_version', '') - return True - -class RosterSetReceivedEvent(nec.NetworkIncomingEvent): - name = 'roster-set-received' - base_network_events = [] - - def generate(self): - frm = self.stanza.getFrom() - our_jid = self.conn.get_own_jid() - if frm is not None and not frm.bareMatch(our_jid): - return - self.version = self.stanza.getTagAttr('query', 'ver') - self.items = {} - for item in self.stanza.getTag('query').getChildren(): - try: - jid = helpers.parse_jid(item.getAttr('jid')) - except helpers.InvalidFormat: - log.warning('Invalid JID: %s, ignoring it' % item.getAttr('jid')) - continue - name = item.getAttr('name') - sub = item.getAttr('subscription') - ask = item.getAttr('ask') - groups = [] - for group in item.getTags('group'): - groups.append(group.getData()) - self.items[jid] = {'name': name, 'sub': sub, 'ask': ask, - 'groups': groups} - if len(self.items) > 1: - reply = nbxmpp.Iq(typ='error', attrs={'id': self.stanza.getID()}, - to=self.stanza.getFrom(), frm=self.stanza.getTo(), xmlns=None) - self.conn.connection.send(reply) - return - if self.conn.connection and self.conn.connected > 1: - reply = nbxmpp.Iq(typ='result', attrs={'id': self.stanza.getID()}, - to=self.stanza.getFrom(), frm=self.stanza.getTo(), xmlns=None) - self.conn.connection.send(reply) - return True - -class RosterInfoEvent(nec.NetworkIncomingEvent): - name = 'roster-info' - base_network_events = [] - - def init(self): - self.avatar_sha = None - class IqErrorReceivedEvent(nec.NetworkIncomingEvent, HelperEvent): name = 'iq-error-received' base_network_events = [] diff --git a/gajim/common/modules/__init__.py b/gajim/common/modules/__init__.py index 880e607d9..5c5a0b2dc 100644 --- a/gajim/common/modules/__init__.py +++ b/gajim/common/modules/__init__.py @@ -15,6 +15,7 @@ import logging from importlib import import_module from pathlib import Path +from unittest.mock import MagicMock log = logging.getLogger('gajim.c.m') @@ -60,9 +61,7 @@ class ModuleMock: self.supported = False def __getattr__(self, key): - def _mock(self, *args, **kwargs): - return - return _mock + return MagicMock() def register(con, *args, **kwargs): diff --git a/gajim/common/modules/presence.py b/gajim/common/modules/presence.py index 39f2a87d1..7f7bf969b 100644 --- a/gajim/common/modules/presence.py +++ b/gajim/common/modules/presence.py @@ -140,15 +140,15 @@ class Presence: if not app.account_is_connected(self._account): return if remove_auth: - self._con.connection.getRoster().delItem(jid) + self._con.getRoster().delItem(jid) jid_list = app.config.get_per('contacts') for j in jid_list: if j.startswith(jid): app.config.del_per('contacts', j) else: log.info('Unsubscribe from %s', jid) - self._con.connection.getRoster().Unsubscribe(jid) - self._con.connection.getRoster().setItem(jid) + self._con.getRoster().Unsubscribe(jid) + self._con.getRoster().setItem(jid) def subscribe(self, jid, msg='', name='', groups=None, auto_auth=False, user_nick=''): diff --git a/gajim/common/modules/roster.py b/gajim/common/modules/roster.py new file mode 100644 index 000000000..735fb65b2 --- /dev/null +++ b/gajim/common/modules/roster.py @@ -0,0 +1,351 @@ +# 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 . + +# Roster + +import logging +from collections import namedtuple + +import nbxmpp + +from gajim.common import app +from gajim.common.nec import NetworkEvent + +log = logging.getLogger('gajim.c.m.roster') + + +# TODO: Error IQs +# What if roster not supported on server -> error + +RosterItem = namedtuple('RosterItem', 'jid data') + + +class Roster: + def __init__(self, con): + self._con = con + self._account = con.name + + self.handlers = [ + ('iq', self._roster_push_received, 'set', nbxmpp.NS_ROSTER), + ('presence', self._presence_received) + ] + + self._data = {} + self._set = None + + def load_roster(self): + log.info('Load from database') + account_jid = self._con.get_own_jid().getStripped() + data = app.logger.get_roster(account_jid) + if data: + self.setRaw(data) + for jid, item in self._data.items(): + app.nec.push_incoming_event(NetworkEvent( + 'roster-info', + conn=self._con, + jid=jid, + nickname=item['name'], + sub=item['subscription'], + ask=item['ask'], + groups=item['groups'], + avatar_sha=item['avatar_sha'])) + else: + log.info('Database empty, reset roster version') + app.config.set_per( + 'accounts', self._account, 'roster_version', '') + + def request_roster(self, version): + log.info('Requested from server') + iq = nbxmpp.Iq('get', nbxmpp.NS_ROSTER) + if version is not None: + iq.setTagAttr('query', 'ver', version) + log.info('Request version: %s', version) + self._con.connection.SendAndCallForResponse( + iq, self._roster_received) + + def _roster_received(self, stanza): + if not nbxmpp.isResultNode(stanza): + log.warning('Unable to retrive roster: %s', stanza.getError()) + return + + log.info('Received Roster') + received_from_server = False + if stanza.getTag('query') is not None: + # clear Roster + self._data = {} + version = self._parse_roster(stanza) + + log.info('New version: %s', version) + app.logger.replace_roster(self._account, version, self._data) + + received_from_server = True + + app.nec.push_incoming_event(NetworkEvent( + 'roster-received', + conn=self._con, + roster=self._data.copy(), + received_from_server=received_from_server)) + + self._con.send_first_presence() + + def _roster_push_received(self, con, stanza): + log.info('Push received') + + sender = stanza.getFrom() + if sender is not None: + if not self._con.get_own_jid().bareMatch(sender): + log.warning('Wrong JID %s', stanza.getFrom()) + return + + push_items, version = self._parse_push(stanza) + + self._ack_roster_push(stanza) + + for item in push_items: + attrs = item.data + app.nec.push_incoming_event(NetworkEvent( + 'roster-info', + conn=self._con, + jid=item.jid, + nickname=attrs['name'], + sub=attrs['subscription'], + ask=attrs['ask'], + groups=attrs['groups'], + avatar_sha=None)) + account_jid = self._con.get_own_jid().getStripped() + app.logger.add_or_update_contact( + account_jid, item.jid, attrs['name'], + attrs['subscription'], attrs['ask'], attrs['groups']) + + log.info('New version: %s', version) + app.config.set_per( + 'accounts', self._account, 'roster_version', version) + + raise nbxmpp.NodeProcessed + + def _parse_roster(self, stanza): + query = stanza.getTag('query') + version = query.getAttr('ver') + + for item in query.getTags('item'): + jid = item.getAttr('jid') + self._data[jid] = self._get_item_attrs(item, update=False) + log.info('Item %s: %s', jid, self._data[jid]) + return version + + @staticmethod + def _get_item_attrs(item, update=True): + ''' + update: True + returns only the attrs that are present in the item + + update: False + returns the attrs of the item but fills missing + attrs with default values + ''' + + default_attrs = {'name': None, + 'ask': None, + 'subscription': None, + 'groups': [], + 'avatar_sha': None} + + attrs = item.getAttrs() + del attrs['jid'] + groups = set([group.getData() for group in item.getTags('group')]) + attrs['groups'] = list(groups) + + if update: + return attrs + default_attrs.update(attrs) + return default_attrs + + def _parse_push(self, stanza): + query = stanza.getTag('query') + version = query.getAttr('ver') + push_items = [] + + for item in query.getTags('item'): + push_items.append(self._update_roster_item(item)) + for item in push_items: + log.info('Push: %s', item) + return push_items, version + + def _update_roster_item(self, item): + jid = item.getAttr('jid') + + if item.getAttr('subscription') == 'remove': + self._data.pop(jid, None) + attrs = self._get_item_attrs(item, update=False) + return RosterItem(jid, attrs) + + else: + if jid not in self._data: + self._data[jid] = self._get_item_attrs(item, update=False) + else: + self._data[jid].update(self._get_item_attrs(item)) + + return RosterItem(jid, self._data[jid]) + + def _ack_roster_push(self, stanza): + iq = nbxmpp.Iq('result', + to=stanza.getFrom(), + frm=stanza.getTo(), + attrs={'id': stanza.getID()}) + self._con.connection.send(iq) + + def _presence_received(self, con, pres): + ''' + Add contacts that request subscription to our internal + roster and also to the database. The contact is put into the + 'Not in roster' group and because we save it to the database + it is also after a restart available. + ''' + + if pres.getType() != 'subscribe': + return + + jid = pres.getFrom().getStripped() + + if jid in self._data: + return + + log.info('Add Contact from presence %s', jid) + self._data[jid] = {'name': None, + 'ask': None, + 'subscription': + 'none', + 'groups': ['Not in roster']} + account_jid = self._con.get_own_jid().getStripped() + app.logger.add_or_update_contact( + account_jid, jid, + self._data[jid]['name'], + self._data[jid]['subscription'], + self._data[jid]['ask'], + self._data[jid]['groups']) + + def _getItemData(self, jid, dataname): + """ + Return specific jid's representation in internal format. + """ + jid = jid[:(jid + '/').find('/')] + return self._data[jid][dataname] + + def delItem(self, jid): + """ + Delete contact 'jid' from roster + """ + self._con.connection.send( + nbxmpp.Iq('set', nbxmpp.NS_ROSTER, payload=[ + nbxmpp.Node('item', {'jid': jid, 'subscription': 'remove'})])) + + def getGroups(self, jid): + """ + Return groups list that contact 'jid' belongs to + """ + return self._getItemData(jid, 'groups') + + def getName(self, jid): + """ + Return name of contact 'jid' + """ + return self._getItemData(jid, 'name') + + def setItem(self, jid, name=None, groups=None): + """ + Rename contact 'jid' and sets the groups list that it now belongs to + """ + iq = nbxmpp.Iq('set', nbxmpp.NS_ROSTER) + query = iq.getTag('query') + attrs = {'jid': jid} + if name: + attrs['name'] = name + item = query.setTag('item', attrs) + if groups is not None: + for group in groups: + item.addChild(node=nbxmpp.Node('group', payload=[group])) + self._con.connection.send(iq) + + def setItemMulti(self, items): + """ + Rename multiple contacts and sets their group lists + """ + for i in items: + iq = nbxmpp.Iq('set', nbxmpp.NS_ROSTER) + query = iq.getTag('query') + attrs = {'jid': i['jid']} + if i['name']: + attrs['name'] = i['name'] + item = query.setTag('item', attrs) + for group in i['groups']: + item.addChild(node=nbxmpp.Node('group', payload=[group])) + self._con.connection.send(iq) + + def getItems(self): + """ + Return list of all [bare] JIDs that the roster is currently tracks + """ + return list(self._data.keys()) + + def keys(self): + """ + Same as getItems. Provided for the sake of dictionary interface + """ + return list(self._data.keys()) + + def __getitem__(self, item): + """ + Get the contact in the internal format. + Raises KeyError if JID 'item' is not in roster + """ + return self._data[item] + + def getItem(self, item): + """ + Get the contact in the internal format (or None if JID 'item' is not in + roster) + """ + if item in self._data: + return self._data[item] + + def Unsubscribe(self, jid): + """ + Ask for removing our subscription for JID 'jid' + """ + self._con.connection.send(nbxmpp.Presence(jid, 'unsubscribe')) + + def getRaw(self): + """ + Return the internal data representation of the roster + """ + return self._data + + def setRaw(self, data): + """ + Set the internal data representation of the roster + """ + own_jid = self._con.get_own_jid().getStripped() + self._data = data + self._data[own_jid] = { + 'resources': {}, + 'name': None, + 'ask': None, + 'subscription': None, + 'groups': None, + 'avatar_sha': None + } + + +def get_instance(*args, **kwargs): + return Roster(*args, **kwargs), 'Roster' diff --git a/gajim/common/zeroconf/connection_zeroconf.py b/gajim/common/zeroconf/connection_zeroconf.py index 615055178..af3d9298c 100644 --- a/gajim/common/zeroconf/connection_zeroconf.py +++ b/gajim/common/zeroconf/connection_zeroconf.py @@ -135,9 +135,11 @@ class ConnectionZeroconf(CommonConnection, ConnectionHandlersZeroconf): diffs = self.roster.getDiffs() for key in diffs: self.roster.setItem(key) - app.nec.push_incoming_event(RosterInfoEvent(None, conn=self, - jid=key, nickname=self.roster.getName(key), sub='both', - ask='no', groups=self.roster.getGroups(key))) + app.nec.push_incoming_event(NetworkEvent( + 'roster-info', conn=self,jid=key, + nickname=self.roster.getName(key), sub='both', + ask='no', groups=self.roster.getGroups(key), + avatar_sha=None)) app.nec.push_incoming_event(ZeroconfPresenceReceivedEvent( None, conn=self, fjid=key, show=self.roster.getStatus(key), status=self.roster.getMessage(key))) @@ -147,9 +149,11 @@ class ConnectionZeroconf(CommonConnection, ConnectionHandlersZeroconf): # callbacks called from zeroconf def _on_new_service(self, jid): self.roster.setItem(jid) - app.nec.push_incoming_event(RosterInfoEvent(None, conn=self, - jid=jid, nickname=self.roster.getName(jid), sub='both', - ask='no', groups=self.roster.getGroups(jid))) + app.nec.push_incoming_event(NetworkEvent( + 'roster-info', conn=self, jid=jid, + nickname=self.roster.getName(jid), sub='both', + ask='no', groups=self.roster.getGroups(jid), + avatar_sha=None)) app.nec.push_incoming_event(ZeroconfPresenceReceivedEvent( None, conn=self, fjid=jid, show=self.roster.getStatus(jid), status=self.roster.getMessage(jid))) @@ -219,14 +223,16 @@ class ConnectionZeroconf(CommonConnection, ConnectionHandlersZeroconf): else: self.connection.announce() self.roster = self.connection.getRoster() - app.nec.push_incoming_event(RosterReceivedEvent(None, conn=self, - xmpp_roster=self.roster)) + app.nec.push_incoming_event(NetworkEvent('roster-received', conn=self, + roster=self.roster.copy(), received_from_server=True)) # display contacts already detected and resolved for jid in self.roster.keys(): - app.nec.push_incoming_event(RosterInfoEvent(None, conn=self, - jid=jid, nickname=self.roster.getName(jid), sub='both', - ask='no', groups=self.roster.getGroups(jid))) + app.nec.push_incoming_event(NetworkEvent( + 'roster-info', conn=self, jid=jid, + nickname=self.roster.getName(jid), sub='both', + ask='no', groups=self.roster.getGroups(jid), + avatar_sha=None)) app.nec.push_incoming_event(ZeroconfPresenceReceivedEvent( None, conn=self, fjid=jid, show=self.roster.getStatus(jid), status=self.roster.getMessage(jid))) diff --git a/gajim/common/zeroconf/roster_zeroconf.py b/gajim/common/zeroconf/roster_zeroconf.py index ef1a91307..93e4de5c6 100644 --- a/gajim/common/zeroconf/roster_zeroconf.py +++ b/gajim/common/zeroconf/roster_zeroconf.py @@ -18,15 +18,14 @@ ## -from gajim.common.zeroconf import zeroconf from gajim.common.zeroconf.zeroconf import Constant, ConstantRI + class Roster: def __init__(self, zeroconf): self._data = None self.zeroconf = zeroconf # our zeroconf instance self.version = '' - self.received_from_server = True def update_roster(self): for val in self.zeroconf.contacts.values(): @@ -109,6 +108,9 @@ class Roster: def __getitem__(self, jid): return self._data[jid] + def __setitem__(self, jid, value): + self._data[jid] = value + def getItems(self): # Return list of all [bare] JIDs that the roster currently tracks. return self._data.keys() @@ -157,3 +159,6 @@ class Roster: def Unauthorize(self, jid): pass + + def copy(self): + return self._data.copy() diff --git a/gajim/gui_interface.py b/gajim/gui_interface.py index c68ba874f..d24bae907 100644 --- a/gajim/gui_interface.py +++ b/gajim/gui_interface.py @@ -2628,7 +2628,7 @@ class Interface: self.roster._before_fill() for account in app.connections: - app.connections[account].load_roster_from_db() + app.connections[account].get_module('Roster').load_roster() self.roster._after_fill() # get instances for windows/dialogs that will show_all()/hide() diff --git a/gajim/roster_window.py b/gajim/roster_window.py index b0310c8c5..9d130ccea 100644 --- a/gajim/roster_window.py +++ b/gajim/roster_window.py @@ -2593,8 +2593,8 @@ class RosterWindow: return if obj.nick == gc_ctrl.nick: - contact = app.contacts.get_contact_with_highest_priority(account, - obj.room_jid) + contact = app.contacts.get_contact_with_highest_priority( + account, obj.room_jid) if contact: contact.show = obj.show self.draw_contact(obj.room_jid, account) @@ -2615,28 +2615,28 @@ class RosterWindow: if app.connections[account].server_resource: resource = app.connections[account].server_resource sha = app.config.get_per('accounts', account, 'avatar_sha') - contact = app.contacts.create_contact(jid=self_jid, - account=account, name=app.nicks[account], + contact = app.contacts.create_contact( + jid=self_jid, account=account, name=app.nicks[account], groups=['self_contact'], show='offline', sub='both', ask='none', resource=resource, avatar_sha=sha) app.contacts.add_contact(account, contact) self.add_contact(self_jid, account) if app.config.get('remember_opened_chat_controls'): account = obj.conn.name - controls = app.config.get_per('accounts', account, - 'opened_chat_controls') + controls = app.config.get_per( + 'accounts', account, 'opened_chat_controls') if controls: for jid in controls.split(','): contact = \ app.contacts.get_contact_with_highest_priority( - account, jid) + account, jid) if not contact: - contact = self.add_to_not_in_the_roster(account, - jid) - app.interface.on_open_chat_window(None, contact, - account) - app.config.set_per('accounts', account, - 'opened_chat_controls', '') + contact = self.add_to_not_in_the_roster( + account, jid) + app.interface.on_open_chat_window( + None, contact, account) + app.config.set_per( + 'accounts', account, 'opened_chat_controls', '') GLib.idle_add(self.refilter_shown_roster_items) def _nec_anonymous_auth(self, obj): diff --git a/gajim/tooltips.py b/gajim/tooltips.py index 6ab4a3af5..f4074e8c6 100644 --- a/gajim/tooltips.py +++ b/gajim/tooltips.py @@ -384,34 +384,6 @@ class RosterTooltip(Gtk.Window, StatusTable): contact.keyID = app.config.get_per('accounts', connection.name, 'keyid') contacts.append(contact) - # if we're online ... - if connection.connection: - roster = connection.connection.getRoster() - # in threadless connection when no roster stanza is sent - # 'roster' is None - if roster and roster.getItem(jid): - resources = roster.getResources(jid) - # ...get the contact info for our other online - # resources - for resource in resources: - # Check if we already have this resource - found = False - for contact_ in contacts: - if contact_.resource == resource: - found = True - break - if found: - continue - show = roster.getShow(jid + '/' + resource) - if not show: - show = 'online' - contact = app.contacts.create_self_contact( - jid=jid, account=account, show=show, - status=roster.getStatus( - jid + '/' + resource), - priority=roster.getPriority( - jid + '/' + resource), resource=resource) - contacts.append(contact) # Username/Account/Groupchat self.prim_contact = app.contacts.get_highest_prio_contact_from_contacts(