Add roster implementation to Gajim
This commit is contained in:
parent
7ad6a28e12
commit
db77fa1ace
|
@ -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=''):
|
||||
|
|
|
@ -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'
|
|
@ -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(
|
||||
|
|
Loading…
Reference in New Issue