Commit db77fa1a authored by Philipp Hörist's avatar Philipp Hörist

Add roster implementation to Gajim

parent 7ad6a28e
......@@ -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()
......
......@@ -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
......@@ -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)
......
......@@ -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 = []
......
......@@ -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):
......
......@@ -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=''):
......
This diff is collapsed.
......@@ -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)))
......
......@@ -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()
......@@ -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()
......
......@@ -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):
......
......@@ -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(
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment