2018-07-22 19:12:52 +02:00
|
|
|
# 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
|
|
|
|
|
2018-07-23 23:01:39 +02:00
|
|
|
import nbxmpp
|
|
|
|
|
2018-07-22 19:12:52 +02:00
|
|
|
from gajim.common import app
|
|
|
|
from gajim.common.nec import NetworkEvent
|
2018-07-23 23:01:39 +02:00
|
|
|
from gajim.common.modules.user_nickname import parse_nickname
|
2018-07-22 19:12:52 +02:00
|
|
|
|
|
|
|
log = logging.getLogger('gajim.c.m.presence')
|
|
|
|
|
|
|
|
|
|
|
|
class Presence:
|
|
|
|
def __init__(self, con):
|
|
|
|
self._con = con
|
|
|
|
self._account = con.name
|
|
|
|
|
2018-07-23 23:01:39 +02:00
|
|
|
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 = []
|
2018-07-22 19:12:52 +02:00
|
|
|
|
|
|
|
def _presence_received(self, con, stanza):
|
2018-07-23 23:01:39 +02:00
|
|
|
if stanza.getType() in ('subscribe', 'subscribed',
|
|
|
|
'unsubscribe', 'unsubscribed'):
|
|
|
|
# Dont handle that here
|
|
|
|
return
|
|
|
|
|
2018-07-22 19:12:52 +02:00
|
|
|
log.info('Received from %s', stanza.getFrom())
|
|
|
|
app.nec.push_incoming_event(
|
|
|
|
NetworkEvent('raw-pres-received',
|
|
|
|
conn=self._con,
|
|
|
|
stanza=stanza))
|
|
|
|
|
2018-07-23 23:01:39 +02:00
|
|
|
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:
|
|
|
|
presence = nbxmpp.Presence(fjid, 'subscribed')
|
|
|
|
presence = self._add_sha(presence)
|
|
|
|
self._con.connection.send(presence)
|
|
|
|
|
|
|
|
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)
|
|
|
|
presence = nbxmpp.Presence(jid, 'subscribed')
|
|
|
|
presence = self._add_sha(presence)
|
|
|
|
self._con.connection.send(presence)
|
|
|
|
|
|
|
|
def unsubscribed(self, jid):
|
|
|
|
if not app.account_is_connected(self._account):
|
|
|
|
return
|
|
|
|
log.info('Unsubscribed: %s', jid)
|
|
|
|
presence = nbxmpp.Presence(jid, 'unsubscribed')
|
|
|
|
presence = self._add_sha(presence)
|
|
|
|
self._con.connection.send(presence)
|
|
|
|
|
|
|
|
def unsubscribe(self, jid, remove_auth=True):
|
|
|
|
if not app.account_is_connected(self._account):
|
|
|
|
return
|
|
|
|
if remove_auth:
|
2018-07-26 00:12:04 +02:00
|
|
|
self._con.getRoster().delItem(jid)
|
2018-07-23 23:01:39 +02:00
|
|
|
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)
|
2018-07-26 00:12:04 +02:00
|
|
|
self._con.getRoster().Unsubscribe(jid)
|
|
|
|
self._con.getRoster().setItem(jid)
|
2018-07-23 23:01:39 +02:00
|
|
|
|
|
|
|
def subscribe(self, jid, msg='', 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)
|
|
|
|
|
|
|
|
presence = nbxmpp.Presence(jid, 'subscribe')
|
|
|
|
if user_nick:
|
|
|
|
nick = presence.setTag('nick', namespace=nbxmpp.NS_NICK)
|
|
|
|
nick.setData(user_nick)
|
|
|
|
presence = self._add_sha(presence)
|
|
|
|
if msg:
|
|
|
|
presence.setStatus(msg)
|
|
|
|
self._con.connection.send(presence)
|
|
|
|
|
|
|
|
def _add_sha(self, presence, send_caps=True):
|
|
|
|
vcard = self._con.get_module('VCardAvatars')
|
|
|
|
presence = vcard.add_update_node(presence)
|
|
|
|
if send_caps:
|
|
|
|
return self._add_caps(presence)
|
|
|
|
return presence
|
|
|
|
|
|
|
|
def _add_caps(self, presence):
|
|
|
|
c = presence.setTag('c', namespace=nbxmpp.NS_CAPS)
|
|
|
|
c.setAttr('hash', 'sha-1')
|
|
|
|
c.setAttr('node', 'http://gajim.org')
|
|
|
|
c.setAttr('ver', app.caps_hash[self._account])
|
|
|
|
return presence
|
|
|
|
|
2018-07-22 19:12:52 +02:00
|
|
|
|
|
|
|
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'
|