[Git][gajim/gajim][master] Add roster implementation to Gajim

Philipp Hörist gitlab at dev.gajim.org
Thu Jul 26 20:51:16 CEST 2018


Philipp Hörist pushed to branch master at gajim / gajim


Commits:
db77fa1a by Philipp Hörist at 2018-07-26T18:38:00Z
Add roster implementation to Gajim

- - - - -


12 changed files:

- gajim/accounts_window.py
- gajim/common/connection.py
- gajim/common/connection_handlers.py
- gajim/common/connection_handlers_events.py
- gajim/common/modules/__init__.py
- gajim/common/modules/presence.py
- + gajim/common/modules/roster.py
- gajim/common/zeroconf/connection_zeroconf.py
- gajim/common/zeroconf/roster_zeroconf.py
- gajim/gui_interface.py
- gajim/roster_window.py
- gajim/tooltips.py


Changes:

=====================================
gajim/accounts_window.py
=====================================
--- 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()
 


=====================================
gajim/common/connection.py
=====================================
--- 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


=====================================
gajim/common/connection_handlers.py
=====================================
--- 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)


=====================================
gajim/common/connection_handlers_events.py
=====================================
--- 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 = []


=====================================
gajim/common/modules/__init__.py
=====================================
--- 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):


=====================================
gajim/common/modules/presence.py
=====================================
--- 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=''):


=====================================
gajim/common/modules/roster.py
=====================================
--- /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 <http://www.gnu.org/licenses/>.
+
+# 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'


=====================================
gajim/common/zeroconf/connection_zeroconf.py
=====================================
--- 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)))


=====================================
gajim/common/zeroconf/roster_zeroconf.py
=====================================
--- 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()


=====================================
gajim/gui_interface.py
=====================================
--- 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()


=====================================
gajim/roster_window.py
=====================================
--- 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):


=====================================
gajim/tooltips.py
=====================================
--- 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(



View it on GitLab: https://dev.gajim.org/gajim/gajim/commit/db77fa1aceb0364ad98521c38a5164997f3cd6c8

-- 
View it on GitLab: https://dev.gajim.org/gajim/gajim/commit/db77fa1aceb0364ad98521c38a5164997f3cd6c8
You're receiving this email because of your account on dev.gajim.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.gajim.org/pipermail/commits/attachments/20180726/eeed23d4/attachment-0001.html>


More information about the Commits mailing list