Add roster implementation to Gajim

This commit is contained in:
Philipp Hörist 2018-07-26 00:12:04 +02:00
parent 7ad6a28e12
commit db77fa1ace
12 changed files with 408 additions and 250 deletions

View File

@ -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()

View File

@ -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

View File

@ -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)

View File

@ -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 = []

View File

@ -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):

View File

@ -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=''):

View File

@ -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'

View File

@ -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)))

View File

@ -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()

View File

@ -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()

View File

@ -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):

View File

@ -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(