- If the server implements XEP-0398 we dont need to add the avatar sha anymore, the server adds it for us. - It also means we dont have to query our own avatar from vcard at start because the server tells us the avatar sha that is published with the inital presence reflection
237 lines
8 KiB
Python
237 lines
8 KiB
Python
# 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/>.
|
|
|
|
# Presence handler
|
|
|
|
import logging
|
|
|
|
import nbxmpp
|
|
|
|
from gajim.common import app
|
|
from gajim.common.nec import NetworkEvent
|
|
from gajim.common.modules.user_nickname import parse_nickname
|
|
|
|
log = logging.getLogger('gajim.c.m.presence')
|
|
|
|
|
|
class Presence:
|
|
def __init__(self, con):
|
|
self._con = con
|
|
self._account = con.name
|
|
|
|
self.handlers = [
|
|
('presence', self._presence_received),
|
|
('presence', self._subscribe_received, 'subscribe'),
|
|
('presence', self._subscribed_received, 'subscribed'),
|
|
('presence', self._unsubscribe_received, 'unsubscribe'),
|
|
('presence', self._unsubscribed_received, 'unsubscribed'),
|
|
]
|
|
|
|
# keep the jids we auto added (transports contacts) to not send the
|
|
# SUBSCRIBED event to GUI
|
|
self.automatically_added = []
|
|
|
|
# list of jid to auto-authorize
|
|
self.jids_for_auto_auth = []
|
|
|
|
def _presence_received(self, con, stanza):
|
|
if stanza.getType() in ('subscribe', 'subscribed',
|
|
'unsubscribe', 'unsubscribed'):
|
|
# Dont handle that here
|
|
return
|
|
|
|
log.info('Received from %s', stanza.getFrom())
|
|
app.nec.push_incoming_event(
|
|
NetworkEvent('raw-pres-received',
|
|
conn=self._con,
|
|
stanza=stanza))
|
|
|
|
def _subscribe_received(self, con, stanza):
|
|
from_ = stanza.getFrom()
|
|
jid = from_.getStripped()
|
|
fjid = str(from_)
|
|
status = stanza.getStatus()
|
|
is_transport = app.jid_is_transport(fjid)
|
|
auto_auth = app.config.get_per('accounts', self._account, 'autoauth')
|
|
user_nick = parse_nickname(stanza)
|
|
|
|
log.info('Received Subscribe: %s, transport: %s, auto_auth: %s, '
|
|
'user_nick: %s', from_, is_transport, auto_auth, user_nick)
|
|
if is_transport and fjid in self._con.agent_registrations:
|
|
self._con.agent_registrations[fjid]['sub_received'] = True
|
|
if not self.agent_registrations[fjid]['roster_push']:
|
|
# We'll reply after roster push result
|
|
raise nbxmpp.NodeProcessed
|
|
|
|
if auto_auth or is_transport or jid in self.jids_for_auto_auth:
|
|
self.send_presence(fjid, 'subscribed')
|
|
|
|
if not status:
|
|
status = _('I would like to add you to my roster.')
|
|
|
|
app.nec.push_incoming_event(NetworkEvent(
|
|
'subscribe-presence-received',
|
|
conn=self._con,
|
|
jid=jid,
|
|
fjid=fjid,
|
|
status=status,
|
|
user_nick=user_nick,
|
|
is_transport=is_transport))
|
|
|
|
raise nbxmpp.NodeProcessed
|
|
|
|
def _subscribed_received(self, con, stanza):
|
|
from_ = stanza.getFrom()
|
|
jid = from_.getStripped()
|
|
resource = from_.getResource()
|
|
log.info('Received Subscribed: %s', from_)
|
|
if jid in self.automatically_added:
|
|
self.automatically_added.remove(jid)
|
|
raise nbxmpp.NodeProcessed
|
|
|
|
app.nec.push_incoming_event(NetworkEvent(
|
|
'subscribed-presence-received',
|
|
conn=self._con, jid=jid, resource=resource))
|
|
raise nbxmpp.NodeProcessed
|
|
|
|
def _unsubscribe_received(self, con, stanza):
|
|
log.info('Received Unsubscribe: %s', stanza.getFrom())
|
|
raise nbxmpp.NodeProcessed
|
|
|
|
def _unsubscribed_received(self, con, stanza):
|
|
from_ = stanza.getFrom()
|
|
jid = from_.getStripped()
|
|
log.info('Received Unsubscribed: %s', from_)
|
|
app.nec.push_incoming_event(NetworkEvent(
|
|
'unsubscribed-presence-received',
|
|
conn=self._con, jid=jid))
|
|
raise nbxmpp.NodeProcessed
|
|
|
|
def subscribed(self, jid):
|
|
if not app.account_is_connected(self._account):
|
|
return
|
|
log.info('Subscribed: %s', jid)
|
|
self.send_presence(jid, 'subscribed')
|
|
|
|
def unsubscribed(self, jid):
|
|
if not app.account_is_connected(self._account):
|
|
return
|
|
log.info('Unsubscribed: %s', jid)
|
|
self.send_presence(jid, 'unsubscribed')
|
|
|
|
def unsubscribe(self, jid, remove_auth=True):
|
|
if not app.account_is_connected(self._account):
|
|
return
|
|
if remove_auth:
|
|
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.getRoster().Unsubscribe(jid)
|
|
self._con.getRoster().setItem(jid)
|
|
|
|
def subscribe(self, jid, msg=None, name='', groups=None,
|
|
auto_auth=False, user_nick=''):
|
|
if not app.account_is_connected(self._account):
|
|
return
|
|
if groups is None:
|
|
groups = []
|
|
|
|
log.info('Request Subscription to %s', jid)
|
|
|
|
if auto_auth:
|
|
self.jids_for_auto_auth.append(jid)
|
|
|
|
infos = {'jid': jid}
|
|
if name:
|
|
infos['name'] = name
|
|
iq = nbxmpp.Iq('set', nbxmpp.NS_ROSTER)
|
|
query = iq.setQuery()
|
|
item = query.addChild('item', attrs=infos)
|
|
for group in groups:
|
|
item.addChild('group').setData(group)
|
|
self._con.connection.send(iq)
|
|
|
|
self.send_presence(jid, 'subscribe', msg)
|
|
|
|
def get_presence(self, to=None, typ=None, priority=None,
|
|
show=None, status=None, nick=None, caps=True,
|
|
sign=None, idle_time=None):
|
|
presence = nbxmpp.Presence(to, typ, priority, show, status)
|
|
if nick is not None:
|
|
nick_tag = presence.setTag('nick', namespace=nbxmpp.NS_NICK)
|
|
nick_tag.setData(nick)
|
|
|
|
if sign:
|
|
presence.setTag(nbxmpp.NS_SIGNED + ' x').setData(sign)
|
|
|
|
if idle_time is not None:
|
|
idle_node = presence.setTag('idle', namespace=nbxmpp.NS_IDLE)
|
|
idle_node.setAttr('since', idle_time)
|
|
|
|
if not self._con.avatar_conversion:
|
|
# XEP-0398 not supported by server so
|
|
# we add the avatar sha to our presence
|
|
self._con.get_module('VCardAvatars').add_update_node(presence)
|
|
|
|
if caps:
|
|
attrs = {'hash': 'sha-1',
|
|
'node': 'http://gajim.org',
|
|
'ver': app.caps_hash[self._account]}
|
|
presence.setTag('c', namespace=nbxmpp.NS_CAPS, attrs=attrs)
|
|
|
|
return presence
|
|
|
|
def send_presence(self, *args, **kwargs):
|
|
if not app.account_is_connected(self._account):
|
|
return
|
|
presence = self.get_presence(*args, **kwargs)
|
|
log.debug('Send presence:\n%s', presence)
|
|
self._con.connection.send(presence)
|
|
|
|
|
|
def parse_show(stanza):
|
|
show = stanza.getShow()
|
|
type_ = parse_type(stanza)
|
|
if show is None and type_ is None:
|
|
return 'online'
|
|
|
|
if type_ == 'unavailable':
|
|
return 'offline'
|
|
|
|
if show not in (None, 'chat', 'away', 'xa', 'dnd'):
|
|
log.warning('Invalid show element: %s', stanza)
|
|
if type_ is None:
|
|
return 'online'
|
|
return 'offline'
|
|
|
|
if show is None:
|
|
return 'online'
|
|
return show
|
|
|
|
|
|
def parse_type(stanza):
|
|
type_ = stanza.getType()
|
|
if type_ not in (None, 'unavailable', 'error', 'subscribe',
|
|
'subscribed', 'unsubscribe', 'unsubscribed'):
|
|
log.warning('Invalid type: %s', stanza)
|
|
return None
|
|
return type_
|
|
|
|
|
|
def get_instance(*args, **kwargs):
|
|
return Presence(*args, **kwargs), 'Presence'
|