first show to use NEC to handle presence events
This commit is contained in:
parent
c9bc9393d5
commit
c36e00ed87
|
@ -1089,6 +1089,7 @@ ConnectionJingle, ConnectionIBBytestream):
|
||||||
PrivateStorageRosternotesReceivedEvent)
|
PrivateStorageRosternotesReceivedEvent)
|
||||||
gajim.nec.register_incoming_event(RosternotesReceivedEvent)
|
gajim.nec.register_incoming_event(RosternotesReceivedEvent)
|
||||||
gajim.nec.register_incoming_event(StreamConflictReceivedEvent)
|
gajim.nec.register_incoming_event(StreamConflictReceivedEvent)
|
||||||
|
gajim.nec.register_incoming_event(PresenceReceivedEvent)
|
||||||
|
|
||||||
gajim.ged.register_event_handler('http-auth-received', ged.CORE,
|
gajim.ged.register_event_handler('http-auth-received', ged.CORE,
|
||||||
self._nec_http_auth_received)
|
self._nec_http_auth_received)
|
||||||
|
@ -1738,347 +1739,9 @@ ConnectionJingle, ConnectionIBBytestream):
|
||||||
"""
|
"""
|
||||||
Called when we receive a presence
|
Called when we receive a presence
|
||||||
"""
|
"""
|
||||||
|
log.debug('PresenceCB')
|
||||||
gajim.nec.push_incoming_event(NetworkEvent('raw-pres-received',
|
gajim.nec.push_incoming_event(NetworkEvent('raw-pres-received',
|
||||||
conn=con, xmpp_pres=prs))
|
conn=self, iq_obj=prs))
|
||||||
ptype = prs.getType()
|
|
||||||
if ptype == 'available':
|
|
||||||
ptype = None
|
|
||||||
rfc_types = ('unavailable', 'error', 'subscribe', 'subscribed',
|
|
||||||
'unsubscribe', 'unsubscribed')
|
|
||||||
if ptype and not ptype in rfc_types:
|
|
||||||
ptype = None
|
|
||||||
log.debug('PresenceCB: %s' % ptype)
|
|
||||||
if not self.connection or self.connected < 2:
|
|
||||||
log.debug('account is no more connected')
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
who = helpers.get_full_jid_from_iq(prs)
|
|
||||||
except Exception:
|
|
||||||
if prs.getTag('error') and prs.getTag('error').getTag('jid-malformed'):
|
|
||||||
# wrong jid, we probably tried to change our nick in a room to a non
|
|
||||||
# valid one
|
|
||||||
who = str(prs.getFrom())
|
|
||||||
jid_stripped, resource = gajim.get_room_and_nick_from_fjid(who)
|
|
||||||
self.dispatch('GC_MSG', (jid_stripped,
|
|
||||||
_('Nickname not allowed: %s') % resource, None, False, None, []))
|
|
||||||
return
|
|
||||||
jid_stripped, resource = gajim.get_room_and_nick_from_fjid(who)
|
|
||||||
timestamp = None
|
|
||||||
id_ = prs.getID()
|
|
||||||
is_gc = False # is it a GC presence ?
|
|
||||||
sigTag = None
|
|
||||||
ns_muc_user_x = None
|
|
||||||
avatar_sha = None
|
|
||||||
# XEP-0172 User Nickname
|
|
||||||
user_nick = prs.getTagData('nick')
|
|
||||||
if not user_nick:
|
|
||||||
user_nick = ''
|
|
||||||
contact_nickname = None
|
|
||||||
transport_auto_auth = False
|
|
||||||
# XEP-0203
|
|
||||||
delay_tag = prs.getTag('delay', namespace=common.xmpp.NS_DELAY2)
|
|
||||||
if delay_tag:
|
|
||||||
tim = prs.getTimestamp2()
|
|
||||||
tim = helpers.datetime_tuple(tim)
|
|
||||||
timestamp = localtime(timegm(tim))
|
|
||||||
xtags = prs.getTags('x')
|
|
||||||
for x in xtags:
|
|
||||||
namespace = x.getNamespace()
|
|
||||||
if namespace.startswith(common.xmpp.NS_MUC):
|
|
||||||
is_gc = True
|
|
||||||
if namespace == common.xmpp.NS_MUC_USER and x.getTag('destroy'):
|
|
||||||
ns_muc_user_x = x
|
|
||||||
elif namespace == common.xmpp.NS_SIGNED:
|
|
||||||
sigTag = x
|
|
||||||
elif namespace == common.xmpp.NS_VCARD_UPDATE:
|
|
||||||
avatar_sha = x.getTagData('photo')
|
|
||||||
contact_nickname = x.getTagData('nickname')
|
|
||||||
elif namespace == common.xmpp.NS_DELAY and not timestamp:
|
|
||||||
# XEP-0091
|
|
||||||
tim = prs.getTimestamp()
|
|
||||||
tim = helpers.datetime_tuple(tim)
|
|
||||||
timestamp = localtime(timegm(tim))
|
|
||||||
elif namespace == 'http://delx.cjb.net/protocol/roster-subsync':
|
|
||||||
# see http://trac.gajim.org/ticket/326
|
|
||||||
agent = gajim.get_server_from_jid(jid_stripped)
|
|
||||||
if self.connection.getRoster().getItem(agent): # to be sure it's a transport contact
|
|
||||||
transport_auto_auth = True
|
|
||||||
|
|
||||||
if not is_gc and id_ and id_.startswith('gajim_muc_') and \
|
|
||||||
ptype == 'error':
|
|
||||||
# Error presences may not include sent stanza, so we don't detect it's
|
|
||||||
# a muc preence. So detect it by ID
|
|
||||||
h = hmac.new(self.secret_hmac, jid_stripped).hexdigest()[:6]
|
|
||||||
if id_.split('_')[-1] == h:
|
|
||||||
is_gc = True
|
|
||||||
status = prs.getStatus() or ''
|
|
||||||
show = prs.getShow()
|
|
||||||
if show not in ('chat', 'away', 'xa', 'dnd'):
|
|
||||||
show = '' # We ignore unknown show
|
|
||||||
if not ptype and not show:
|
|
||||||
show = 'online'
|
|
||||||
elif ptype == 'unavailable':
|
|
||||||
show = 'offline'
|
|
||||||
|
|
||||||
prio = prs.getPriority()
|
|
||||||
try:
|
|
||||||
prio = int(prio)
|
|
||||||
except Exception:
|
|
||||||
prio = 0
|
|
||||||
keyID = ''
|
|
||||||
if sigTag and self.USE_GPG and ptype != 'error':
|
|
||||||
# error presences contain our own signature
|
|
||||||
# verify
|
|
||||||
sigmsg = sigTag.getData()
|
|
||||||
keyID = self.gpg.verify(status, sigmsg)
|
|
||||||
|
|
||||||
if is_gc:
|
|
||||||
if ptype == 'error':
|
|
||||||
errcon = prs.getError()
|
|
||||||
errmsg = prs.getErrorMsg()
|
|
||||||
errcode = prs.getErrorCode()
|
|
||||||
room_jid, nick = gajim.get_room_and_nick_from_fjid(who)
|
|
||||||
|
|
||||||
gc_control = gajim.interface.msg_win_mgr.get_gc_control(room_jid,
|
|
||||||
self.name)
|
|
||||||
|
|
||||||
# If gc_control is missing - it may be minimized. Try to get it from
|
|
||||||
# there. If it's not there - then it's missing anyway and will
|
|
||||||
# remain set to None.
|
|
||||||
if gc_control is None:
|
|
||||||
minimized = gajim.interface.minimized_controls[self.name]
|
|
||||||
gc_control = minimized.get(room_jid)
|
|
||||||
|
|
||||||
if errcode == '502':
|
|
||||||
# Internal Timeout:
|
|
||||||
self.dispatch('NOTIFY', (jid_stripped, 'error', errmsg, resource,
|
|
||||||
prio, keyID, timestamp, None))
|
|
||||||
elif (errcode == '503'):
|
|
||||||
if gc_control is None or gc_control.autorejoin is None:
|
|
||||||
# maximum user number reached
|
|
||||||
self.dispatch('GC_ERROR', (gc_control,
|
|
||||||
_('Unable to join group chat'),
|
|
||||||
_('Maximum number of users for %s has been '
|
|
||||||
'reached') % room_jid))
|
|
||||||
elif (errcode == '401') or (errcon == 'not-authorized'):
|
|
||||||
# password required to join
|
|
||||||
self.dispatch('GC_PASSWORD_REQUIRED', (room_jid, nick))
|
|
||||||
elif (errcode == '403') or (errcon == 'forbidden'):
|
|
||||||
# we are banned
|
|
||||||
self.dispatch('GC_ERROR', (gc_control,
|
|
||||||
_('Unable to join group chat'),
|
|
||||||
_('You are banned from group chat %s.') % room_jid))
|
|
||||||
elif (errcode == '404') or (errcon in ('item-not-found',
|
|
||||||
'remote-server-not-found')):
|
|
||||||
if gc_control is None or gc_control.autorejoin is None:
|
|
||||||
# group chat does not exist
|
|
||||||
self.dispatch('GC_ERROR', (gc_control,
|
|
||||||
_('Unable to join group chat'),
|
|
||||||
_('Group chat %s does not exist.') % room_jid))
|
|
||||||
elif (errcode == '405') or (errcon == 'not-allowed'):
|
|
||||||
self.dispatch('GC_ERROR', (gc_control,
|
|
||||||
_('Unable to join group chat'),
|
|
||||||
_('Group chat creation is restricted.')))
|
|
||||||
elif (errcode == '406') or (errcon == 'not-acceptable'):
|
|
||||||
self.dispatch('GC_ERROR', (gc_control,
|
|
||||||
_('Unable to join group chat'),
|
|
||||||
_('Your registered nickname must be used in group chat '
|
|
||||||
'%s.') % room_jid))
|
|
||||||
elif (errcode == '407') or (errcon == 'registration-required'):
|
|
||||||
self.dispatch('GC_ERROR', (gc_control,
|
|
||||||
_('Unable to join group chat'),
|
|
||||||
_('You are not in the members list in groupchat %s.') %\
|
|
||||||
room_jid))
|
|
||||||
elif (errcode == '409') or (errcon == 'conflict'):
|
|
||||||
# nick conflict
|
|
||||||
room_jid = gajim.get_room_from_fjid(who)
|
|
||||||
self.dispatch('ASK_NEW_NICK', (room_jid,))
|
|
||||||
else: # print in the window the error
|
|
||||||
self.dispatch('ERROR_ANSWER', ('', jid_stripped,
|
|
||||||
errmsg, errcode))
|
|
||||||
if not ptype or ptype == 'unavailable':
|
|
||||||
if gajim.config.get('log_contact_status_changes') and \
|
|
||||||
gajim.config.should_log(self.name, jid_stripped):
|
|
||||||
gc_c = gajim.contacts.get_gc_contact(self.name, jid_stripped,
|
|
||||||
resource)
|
|
||||||
st = status or ''
|
|
||||||
if gc_c:
|
|
||||||
jid = gc_c.jid
|
|
||||||
else:
|
|
||||||
jid = prs.getJid()
|
|
||||||
if jid:
|
|
||||||
# we know real jid, save it in db
|
|
||||||
st += ' (%s)' % jid
|
|
||||||
try:
|
|
||||||
gajim.logger.write('gcstatus', who, st, show)
|
|
||||||
except exceptions.PysqliteOperationalError, e:
|
|
||||||
self.dispatch('DB_ERROR', (_('Disk Write Error'),
|
|
||||||
str(e)))
|
|
||||||
except exceptions.DatabaseMalformed:
|
|
||||||
pritext = _('Database Error')
|
|
||||||
sectext = _('The database file (%s) cannot be read. Try to '
|
|
||||||
'repair it (see http://trac.gajim.org/wiki/DatabaseBackup)'
|
|
||||||
' or remove it (all history will be lost).') % \
|
|
||||||
common.logger.LOG_DB_PATH
|
|
||||||
self.dispatch('DB_ERROR', (pritext, sectext))
|
|
||||||
if avatar_sha or avatar_sha == '':
|
|
||||||
if avatar_sha == '':
|
|
||||||
# contact has no avatar
|
|
||||||
puny_nick = helpers.sanitize_filename(resource)
|
|
||||||
gajim.interface.remove_avatar_files(jid_stripped, puny_nick)
|
|
||||||
# if it's a gc presence, don't ask vcard here. We may ask it to
|
|
||||||
# real jid in gui part.
|
|
||||||
if ns_muc_user_x:
|
|
||||||
# Room has been destroyed. see
|
|
||||||
# http://www.xmpp.org/extensions/xep-0045.html#destroyroom
|
|
||||||
reason = _('Room has been destroyed')
|
|
||||||
destroy = ns_muc_user_x.getTag('destroy')
|
|
||||||
r = destroy.getTagData('reason')
|
|
||||||
if r:
|
|
||||||
reason += ' (%s)' % r
|
|
||||||
if destroy.getAttr('jid'):
|
|
||||||
try:
|
|
||||||
jid = helpers.parse_jid(destroy.getAttr('jid'))
|
|
||||||
reason += '\n' + _('You can join this room instead: %s') \
|
|
||||||
% jid
|
|
||||||
except common.helpers.InvalidFormat:
|
|
||||||
pass
|
|
||||||
statusCode = ['destroyed']
|
|
||||||
else:
|
|
||||||
reason = prs.getReason()
|
|
||||||
statusCode = prs.getStatusCode()
|
|
||||||
self.dispatch('GC_NOTIFY', (jid_stripped, show, status, resource,
|
|
||||||
prs.getRole(), prs.getAffiliation(), prs.getJid(),
|
|
||||||
reason, prs.getActor(), statusCode, prs.getNewNick(),
|
|
||||||
avatar_sha))
|
|
||||||
return
|
|
||||||
|
|
||||||
if ptype == 'subscribe':
|
|
||||||
log.debug('subscribe request from %s' % who)
|
|
||||||
if who.find('@') <= 0 and who in self.agent_registrations:
|
|
||||||
self.agent_registrations[who]['sub_received'] = True
|
|
||||||
if not self.agent_registrations[who]['roster_push']:
|
|
||||||
# We'll reply after roster push result
|
|
||||||
return
|
|
||||||
if gajim.config.get_per('accounts', self.name, 'autoauth') or \
|
|
||||||
who.find('@') <= 0 or jid_stripped in self.jids_for_auto_auth or \
|
|
||||||
transport_auto_auth:
|
|
||||||
if self.connection:
|
|
||||||
p = common.xmpp.Presence(who, 'subscribed')
|
|
||||||
p = self.add_sha(p)
|
|
||||||
self.connection.send(p)
|
|
||||||
if who.find('@') <= 0 or transport_auto_auth:
|
|
||||||
self.dispatch('NOTIFY', (jid_stripped, 'offline', 'offline',
|
|
||||||
resource, prio, keyID, timestamp, None))
|
|
||||||
if transport_auto_auth:
|
|
||||||
self.automatically_added.append(jid_stripped)
|
|
||||||
self.request_subscription(jid_stripped, name = user_nick)
|
|
||||||
else:
|
|
||||||
if not status:
|
|
||||||
status = _('I would like to add you to my roster.')
|
|
||||||
self.dispatch('SUBSCRIBE', (jid_stripped, status, user_nick))
|
|
||||||
elif ptype == 'subscribed':
|
|
||||||
if jid_stripped in self.automatically_added:
|
|
||||||
self.automatically_added.remove(jid_stripped)
|
|
||||||
else:
|
|
||||||
# detect a subscription loop
|
|
||||||
if jid_stripped not in self.subscribed_events:
|
|
||||||
self.subscribed_events[jid_stripped] = []
|
|
||||||
self.subscribed_events[jid_stripped].append(time_time())
|
|
||||||
block = False
|
|
||||||
if len(self.subscribed_events[jid_stripped]) > 5:
|
|
||||||
if time_time() - self.subscribed_events[jid_stripped][0] < 5:
|
|
||||||
block = True
|
|
||||||
self.subscribed_events[jid_stripped] = self.subscribed_events[jid_stripped][1:]
|
|
||||||
if block:
|
|
||||||
gajim.config.set_per('account', self.name,
|
|
||||||
'dont_ack_subscription', True)
|
|
||||||
else:
|
|
||||||
self.dispatch('SUBSCRIBED', (jid_stripped, resource))
|
|
||||||
# BE CAREFUL: no con.updateRosterItem() in a callback
|
|
||||||
log.debug(_('we are now subscribed to %s') % who)
|
|
||||||
elif ptype == 'unsubscribe':
|
|
||||||
log.debug(_('unsubscribe request from %s') % who)
|
|
||||||
elif ptype == 'unsubscribed':
|
|
||||||
log.debug(_('we are now unsubscribed from %s') % who)
|
|
||||||
# detect a unsubscription loop
|
|
||||||
if jid_stripped not in self.subscribed_events:
|
|
||||||
self.subscribed_events[jid_stripped] = []
|
|
||||||
self.subscribed_events[jid_stripped].append(time_time())
|
|
||||||
block = False
|
|
||||||
if len(self.subscribed_events[jid_stripped]) > 5:
|
|
||||||
if time_time() - self.subscribed_events[jid_stripped][0] < 5:
|
|
||||||
block = True
|
|
||||||
self.subscribed_events[jid_stripped] = self.subscribed_events[jid_stripped][1:]
|
|
||||||
if block:
|
|
||||||
gajim.config.set_per('account', self.name, 'dont_ack_subscription',
|
|
||||||
True)
|
|
||||||
else:
|
|
||||||
self.dispatch('UNSUBSCRIBED', jid_stripped)
|
|
||||||
elif ptype == 'error':
|
|
||||||
errmsg = prs.getError()
|
|
||||||
errcode = prs.getErrorCode()
|
|
||||||
if errcode != '502': # Internal Timeout:
|
|
||||||
# print in the window the error
|
|
||||||
self.dispatch('ERROR_ANSWER', ('', jid_stripped,
|
|
||||||
errmsg, errcode))
|
|
||||||
if errcode != '409': # conflict # See #5120
|
|
||||||
self.dispatch('NOTIFY', (jid_stripped, 'error', errmsg, resource,
|
|
||||||
prio, keyID, timestamp, None))
|
|
||||||
|
|
||||||
if ptype == 'unavailable':
|
|
||||||
for jid in [jid_stripped, who]:
|
|
||||||
if jid not in self.sessions:
|
|
||||||
continue
|
|
||||||
# automatically terminate sessions that they haven't sent a thread
|
|
||||||
# ID in, only if other part support thread ID
|
|
||||||
for sess in self.sessions[jid].values():
|
|
||||||
if not sess.received_thread_id:
|
|
||||||
contact = gajim.contacts.get_contact(self.name, jid)
|
|
||||||
# FIXME: I don't know if this is the correct behavior here.
|
|
||||||
# Anyway, it is the old behavior when we assumed that
|
|
||||||
# not-existing contacts don't support anything
|
|
||||||
contact_exists = bool(contact)
|
|
||||||
session_supported = contact_exists and (
|
|
||||||
contact.supports(common.xmpp.NS_SSN) or
|
|
||||||
contact.supports(common.xmpp.NS_ESESSION))
|
|
||||||
if session_supported:
|
|
||||||
sess.terminate()
|
|
||||||
del self.sessions[jid][sess.thread_id]
|
|
||||||
|
|
||||||
if avatar_sha is not None and ptype != 'error':
|
|
||||||
if jid_stripped not in self.vcard_shas:
|
|
||||||
cached_vcard = self.get_cached_vcard(jid_stripped)
|
|
||||||
if cached_vcard and 'PHOTO' in cached_vcard and \
|
|
||||||
'SHA' in cached_vcard['PHOTO']:
|
|
||||||
self.vcard_shas[jid_stripped] = cached_vcard['PHOTO']['SHA']
|
|
||||||
else:
|
|
||||||
self.vcard_shas[jid_stripped] = ''
|
|
||||||
if avatar_sha != self.vcard_shas[jid_stripped]:
|
|
||||||
# avatar has been updated
|
|
||||||
self.request_vcard(jid_stripped)
|
|
||||||
if not ptype or ptype == 'unavailable':
|
|
||||||
if gajim.config.get('log_contact_status_changes') and \
|
|
||||||
gajim.config.should_log(self.name, jid_stripped):
|
|
||||||
try:
|
|
||||||
gajim.logger.write('status', jid_stripped, status, show)
|
|
||||||
except exceptions.PysqliteOperationalError, e:
|
|
||||||
self.dispatch('DB_ERROR', (_('Disk Write Error'), str(e)))
|
|
||||||
except exceptions.DatabaseMalformed:
|
|
||||||
pritext = _('Database Error')
|
|
||||||
sectext = _('The database file (%s) cannot be read. Try to '
|
|
||||||
'repair it (see http://trac.gajim.org/wiki/DatabaseBackup) '
|
|
||||||
'or remove it (all history will be lost).') % \
|
|
||||||
common.logger.LOG_DB_PATH
|
|
||||||
self.dispatch('DB_ERROR', (pritext, sectext))
|
|
||||||
our_jid = gajim.get_jid_from_account(self.name)
|
|
||||||
if jid_stripped == our_jid and resource == self.server_resource:
|
|
||||||
# We got our own presence
|
|
||||||
self.dispatch('STATUS', show)
|
|
||||||
else:
|
|
||||||
self.dispatch('NOTIFY', (jid_stripped, show, status, resource, prio,
|
|
||||||
keyID, timestamp, contact_nickname))
|
|
||||||
# END presenceCB
|
|
||||||
|
|
||||||
def _StanzaArrivedCB(self, con, obj):
|
def _StanzaArrivedCB(self, con, obj):
|
||||||
self.last_io = gajim.idlequeue.current_time()
|
self.last_io = gajim.idlequeue.current_time()
|
||||||
|
|
|
@ -20,12 +20,16 @@
|
||||||
|
|
||||||
import datetime
|
import datetime
|
||||||
import sys
|
import sys
|
||||||
|
from time import (localtime, time as time_time)
|
||||||
|
from calendar import timegm
|
||||||
|
|
||||||
from common import nec
|
from common import nec
|
||||||
from common import helpers
|
from common import helpers
|
||||||
from common import gajim
|
from common import gajim
|
||||||
from common import xmpp
|
from common import xmpp
|
||||||
from common import dataforms
|
from common import dataforms
|
||||||
|
from common import exceptions
|
||||||
|
from common.logger import LOG_DB_PATH
|
||||||
|
|
||||||
import logging
|
import logging
|
||||||
log = logging.getLogger('gajim.c.connection_handlers_events')
|
log = logging.getLogger('gajim.c.connection_handlers_events')
|
||||||
|
@ -592,3 +596,367 @@ class StreamConflictReceivedEvent(nec.NetworkIncomingEvent):
|
||||||
if self.base_event.iq_obj.getTag('conflict'):
|
if self.base_event.iq_obj.getTag('conflict'):
|
||||||
self.conn = self.base_event.conn
|
self.conn = self.base_event.conn
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
class PresenceReceivedEvent(nec.NetworkIncomingEvent, HelperEvent):
|
||||||
|
name = 'presence-received'
|
||||||
|
base_network_events = ['raw-pres-received']
|
||||||
|
|
||||||
|
def generate(self):
|
||||||
|
self.conn = self.base_event.conn
|
||||||
|
self.iq_obj = self.base_event.iq_obj
|
||||||
|
self.ptype = self.iq_obj.getType()
|
||||||
|
if self.ptype == 'available':
|
||||||
|
self.ptype = None
|
||||||
|
rfc_types = ('unavailable', 'error', 'subscribe', 'subscribed',
|
||||||
|
'unsubscribe', 'unsubscribed')
|
||||||
|
if self.ptype and not self.ptype in rfc_types:
|
||||||
|
self.ptype = None
|
||||||
|
log.debug('PresenceCB: %s' % self.ptype)
|
||||||
|
if not self.conn or self.conn.connected < 2:
|
||||||
|
log.debug('account is no more connected')
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
self.get_jid_resource()
|
||||||
|
except Exception:
|
||||||
|
if self.iq_obj.getTag('error') and self.iq_obj.getTag('error').\
|
||||||
|
getTag('jid-malformed'):
|
||||||
|
# wrong jid, we probably tried to change our nick in a room to a non
|
||||||
|
# valid one
|
||||||
|
who = str(self.iq_obj.getFrom())
|
||||||
|
jid_stripped, resource = gajim.get_room_and_nick_from_fjid(who)
|
||||||
|
self.conn.dispatch('GC_MSG', (jid_stripped,
|
||||||
|
_('Nickname not allowed: %s') % resource, None, False, None,
|
||||||
|
[]))
|
||||||
|
return
|
||||||
|
self.timestamp = None
|
||||||
|
self.get_id()
|
||||||
|
self.is_gc = False # is it a GC presence ?
|
||||||
|
sigTag = None
|
||||||
|
ns_muc_user_x = None
|
||||||
|
avatar_sha = None
|
||||||
|
# XEP-0172 User Nickname
|
||||||
|
self.user_nick = self.iq_obj.getTagData('nick') or ''
|
||||||
|
self.contact_nickname = None
|
||||||
|
transport_auto_auth = False
|
||||||
|
# XEP-0203
|
||||||
|
delay_tag = self.iq_obj.getTag('delay', namespace=xmpp.NS_DELAY2)
|
||||||
|
if delay_tag:
|
||||||
|
tim = self.iq_obj.getTimestamp2()
|
||||||
|
tim = helpers.datetime_tuple(tim)
|
||||||
|
self.timestamp = localtime(timegm(tim))
|
||||||
|
xtags = self.iq_obj.getTags('x')
|
||||||
|
for x in xtags:
|
||||||
|
namespace = x.getNamespace()
|
||||||
|
if namespace.startswith(xmpp.NS_MUC):
|
||||||
|
self.is_gc = True
|
||||||
|
if namespace == xmpp.NS_MUC_USER and x.getTag('destroy'):
|
||||||
|
ns_muc_user_x = x
|
||||||
|
elif namespace == xmpp.NS_SIGNED:
|
||||||
|
sigTag = x
|
||||||
|
elif namespace == xmpp.NS_VCARD_UPDATE:
|
||||||
|
avatar_sha = x.getTagData('photo')
|
||||||
|
self.contact_nickname = x.getTagData('nickname')
|
||||||
|
elif namespace == xmpp.NS_DELAY and not self.timestamp:
|
||||||
|
# XEP-0091
|
||||||
|
tim = self.iq_obj.getTimestamp()
|
||||||
|
tim = helpers.datetime_tuple(tim)
|
||||||
|
self.timestamp = localtime(timegm(tim))
|
||||||
|
elif namespace == 'http://delx.cjb.net/protocol/roster-subsync':
|
||||||
|
# see http://trac.gajim.org/ticket/326
|
||||||
|
agent = gajim.get_server_from_jid(self.jid)
|
||||||
|
if self.conn.connection.getRoster().getItem(agent):
|
||||||
|
# to be sure it's a transport contact
|
||||||
|
transport_auto_auth = True
|
||||||
|
|
||||||
|
if not self.is_gc and self.id_ and self.id_.startswith('gajim_muc_') \
|
||||||
|
and self.ptype == 'error':
|
||||||
|
# Error presences may not include sent stanza, so we don't detect it's
|
||||||
|
# a muc preence. So detect it by ID
|
||||||
|
h = hmac.new(self.conn.secret_hmac, self.jid).hexdigest()[:6]
|
||||||
|
if self.id_.split('_')[-1] == h:
|
||||||
|
self.is_gc = True
|
||||||
|
self.status = self.iq_obj.getStatus() or ''
|
||||||
|
self.show = self.iq_obj.getShow()
|
||||||
|
if self.show not in ('chat', 'away', 'xa', 'dnd'):
|
||||||
|
self.show = '' # We ignore unknown show
|
||||||
|
if not self.ptype and not self.show:
|
||||||
|
self.show = 'online'
|
||||||
|
elif self.ptype == 'unavailable':
|
||||||
|
self.show = 'offline'
|
||||||
|
|
||||||
|
self.prio = self.iq_obj.getPriority()
|
||||||
|
try:
|
||||||
|
self.prio = int(self.prio)
|
||||||
|
except Exception:
|
||||||
|
self.prio = 0
|
||||||
|
self.keyID = ''
|
||||||
|
if sigTag and self.conn.USE_GPG and self.ptype != 'error':
|
||||||
|
# error presences contain our own signature
|
||||||
|
# verify
|
||||||
|
sigmsg = sigTag.getData()
|
||||||
|
self.keyID = self.conn.gpg.verify(self.status, sigmsg)
|
||||||
|
self.keyID = helpers.prepare_and_validate_gpg_keyID(self.conn.name,
|
||||||
|
self.jid, self.keyID)
|
||||||
|
|
||||||
|
if self.is_gc:
|
||||||
|
if self.ptype == 'error':
|
||||||
|
errcon = self.iq_obj.getError()
|
||||||
|
errmsg = self.iq_obj.getErrorMsg()
|
||||||
|
errcode = self.iq_obj.getErrorCode()
|
||||||
|
|
||||||
|
gc_control = gajim.interface.msg_win_mgr.get_gc_control(
|
||||||
|
self.jid, self.conn.name)
|
||||||
|
|
||||||
|
# If gc_control is missing - it may be minimized. Try to get it
|
||||||
|
# from there. If it's not there - then it's missing anyway and
|
||||||
|
# will remain set to None.
|
||||||
|
if gc_control is None:
|
||||||
|
minimized = gajim.interface.minimized_controls[
|
||||||
|
self.conn.name]
|
||||||
|
gc_control = minimized.get(self.jid)
|
||||||
|
|
||||||
|
if errcode == '502':
|
||||||
|
# Internal Timeout:
|
||||||
|
self.show = 'error'
|
||||||
|
self.status = errmsg
|
||||||
|
return True
|
||||||
|
elif errcode == '503':
|
||||||
|
if gc_control is None or gc_control.autorejoin is None:
|
||||||
|
# maximum user number reached
|
||||||
|
self.conn.dispatch('GC_ERROR', (gc_control,
|
||||||
|
_('Unable to join group chat'),
|
||||||
|
_('Maximum number of users for %s has been '
|
||||||
|
'reached') % self.jid))
|
||||||
|
elif (errcode == '401') or (errcon == 'not-authorized'):
|
||||||
|
# password required to join
|
||||||
|
self.conn.dispatch('GC_PASSWORD_REQUIRED', (self.jid,
|
||||||
|
self.resource))
|
||||||
|
elif (errcode == '403') or (errcon == 'forbidden'):
|
||||||
|
# we are banned
|
||||||
|
self.conn.dispatch('GC_ERROR', (gc_control,
|
||||||
|
_('Unable to join group chat'),
|
||||||
|
_('You are banned from group chat %s.') % self.jid))
|
||||||
|
elif (errcode == '404') or (errcon in ('item-not-found',
|
||||||
|
'remote-server-not-found')):
|
||||||
|
if gc_control is None or gc_control.autorejoin is None:
|
||||||
|
# group chat does not exist
|
||||||
|
self.conn.dispatch('GC_ERROR', (gc_control,
|
||||||
|
_('Unable to join group chat'),
|
||||||
|
_('Group chat %s does not exist.') % self.jid))
|
||||||
|
elif (errcode == '405') or (errcon == 'not-allowed'):
|
||||||
|
self.conn.dispatch('GC_ERROR', (gc_control,
|
||||||
|
_('Unable to join group chat'),
|
||||||
|
_('Group chat creation is restricted.')))
|
||||||
|
elif (errcode == '406') or (errcon == 'not-acceptable'):
|
||||||
|
self.conn.dispatch('GC_ERROR', (gc_control,
|
||||||
|
_('Unable to join group chat'),
|
||||||
|
_('Your registered nickname must be used in group chat '
|
||||||
|
'%s.') % self.jid))
|
||||||
|
elif (errcode == '407') or (errcon == 'registration-required'):
|
||||||
|
self.conn.dispatch('GC_ERROR', (gc_control,
|
||||||
|
_('Unable to join group chat'),
|
||||||
|
_('You are not in the members list in groupchat %s.') %\
|
||||||
|
self.jid))
|
||||||
|
elif (errcode == '409') or (errcon == 'conflict'):
|
||||||
|
# nick conflict
|
||||||
|
self.conn.dispatch('ASK_NEW_NICK', (self.jid,))
|
||||||
|
else: # print in the window the error
|
||||||
|
self.conn.dispatch('ERROR_ANSWER', ('', self.jid,
|
||||||
|
errmsg, errcode))
|
||||||
|
elif not self.ptype or self.ptype == 'unavailable':
|
||||||
|
if gajim.config.get('log_contact_status_changes') and \
|
||||||
|
gajim.config.should_log(self.conn.name, self.jid):
|
||||||
|
gc_c = gajim.contacts.get_gc_contact(self.conn.name,
|
||||||
|
self.jid, self.resource)
|
||||||
|
st = status or ''
|
||||||
|
if gc_c:
|
||||||
|
jid = gc_c.jid
|
||||||
|
else:
|
||||||
|
jid = self.iq_obj.getJid()
|
||||||
|
if jid:
|
||||||
|
# we know real jid, save it in db
|
||||||
|
st += ' (%s)' % jid
|
||||||
|
try:
|
||||||
|
gajim.logger.write('gcstatus', self.fjid, st, self.show)
|
||||||
|
except exceptions.PysqliteOperationalError, e:
|
||||||
|
self.conn.dispatch('DB_ERROR', (_('Disk Write Error'),
|
||||||
|
str(e)))
|
||||||
|
except exceptions.DatabaseMalformed:
|
||||||
|
pritext = _('Database Error')
|
||||||
|
sectext = _('The database file (%s) cannot be read. '
|
||||||
|
'Try to repair it (see '
|
||||||
|
'http://trac.gajim.org/wiki/DatabaseBackup) or '
|
||||||
|
'remove it (all history will be lost).') % \
|
||||||
|
LOG_DB_PATH
|
||||||
|
self.conn.dispatch('DB_ERROR', (pritext, sectext))
|
||||||
|
if avatar_sha == '':
|
||||||
|
# contact has no avatar
|
||||||
|
puny_nick = helpers.sanitize_filename(self.resource)
|
||||||
|
gajim.interface.remove_avatar_files(self.jid, puny_nick)
|
||||||
|
# NOTE: if it's a gc presence, don't ask vcard here.
|
||||||
|
# We may ask it to real jid in gui part.
|
||||||
|
if ns_muc_user_x:
|
||||||
|
# Room has been destroyed. see
|
||||||
|
# http://www.xmpp.org/extensions/xep-0045.html#destroyroom
|
||||||
|
reason = _('Room has been destroyed')
|
||||||
|
destroy = ns_muc_user_x.getTag('destroy')
|
||||||
|
r = destroy.getTagData('reason')
|
||||||
|
if r:
|
||||||
|
reason += ' (%s)' % r
|
||||||
|
if destroy.getAttr('jid'):
|
||||||
|
try:
|
||||||
|
jid = helpers.parse_jid(destroy.getAttr('jid'))
|
||||||
|
reason += '\n' + \
|
||||||
|
_('You can join this room instead: %s') % jid
|
||||||
|
except common.helpers.InvalidFormat:
|
||||||
|
pass
|
||||||
|
statusCode = ['destroyed']
|
||||||
|
else:
|
||||||
|
reason = self.iq_obj.getReason()
|
||||||
|
statusCode = self.iq_obj.getStatusCode()
|
||||||
|
role = self.iq_obj.getRole()
|
||||||
|
affiliation = self.iq_obj.getAffiliation()
|
||||||
|
prs_jid = self.iq_obj.getJid()
|
||||||
|
actor = self.iq_obj.getActor()
|
||||||
|
new_nick = self.iq_obj.getNewNick()
|
||||||
|
self.conn.dispatch('GC_NOTIFY', (self.jid, self.show,
|
||||||
|
self.status, self.resource, role, affiliation, prs_jid,
|
||||||
|
reason, actor, statusCode, new_nick, avatar_sha))
|
||||||
|
return
|
||||||
|
|
||||||
|
if self.ptype == 'subscribe':
|
||||||
|
log.debug('subscribe request from %s' % self.jfid)
|
||||||
|
if self.fjid.find('@') <= 0 and self.fjid in \
|
||||||
|
self.agent_registrations:
|
||||||
|
self.agent_registrations[self.fjid]['sub_received'] = True
|
||||||
|
if not self.agent_registrations[self.fjid]['roster_push']:
|
||||||
|
# We'll reply after roster push result
|
||||||
|
return
|
||||||
|
if gajim.config.get_per('accounts', self.conn.name, 'autoauth') or \
|
||||||
|
self.fjid.find('@') <= 0 or self.jid in self.jids_for_auto_auth or \
|
||||||
|
transport_auto_auth:
|
||||||
|
if self.conn.connection:
|
||||||
|
p = xmpp.Presence(self.fjid, 'subscribed')
|
||||||
|
p = self.conn.add_sha(p)
|
||||||
|
self.conn.connection.send(p)
|
||||||
|
if self.fjid.find('@') <= 0 or transport_auto_auth:
|
||||||
|
self.show = 'offline'
|
||||||
|
self.status = 'offline'
|
||||||
|
return True
|
||||||
|
|
||||||
|
if transport_auto_auth:
|
||||||
|
self.conn.automatically_added.append(self.jid)
|
||||||
|
self.conn.request_subscription(self.jid,
|
||||||
|
name=self.user_nick)
|
||||||
|
else:
|
||||||
|
if not self.status:
|
||||||
|
self.status = _('I would like to add you to my roster.')
|
||||||
|
self.conn.dispatch('SUBSCRIBE', (self.jid, self.status,
|
||||||
|
self.user_nick))
|
||||||
|
elif self.ptype == 'subscribed':
|
||||||
|
if self.jid in self.conn.automatically_added:
|
||||||
|
self.conn.automatically_added.remove(self.jid)
|
||||||
|
else:
|
||||||
|
# detect a subscription loop
|
||||||
|
if self.jid not in self.conn.subscribed_events:
|
||||||
|
self.conn.subscribed_events[self.jid] = []
|
||||||
|
self.conn.subscribed_events[self.jid].append(time_time())
|
||||||
|
block = False
|
||||||
|
if len(self.conn.subscribed_events[self.jid]) > 5:
|
||||||
|
if time_time() - self.subscribed_events[self.jid][0] < 5:
|
||||||
|
block = True
|
||||||
|
self.conn.subscribed_events[self.jid] = \
|
||||||
|
self.conn.subscribed_events[self.jid][1:]
|
||||||
|
if block:
|
||||||
|
gajim.config.set_per('account', self.conn.name,
|
||||||
|
'dont_ack_subscription', True)
|
||||||
|
else:
|
||||||
|
self.conn.dispatch('SUBSCRIBED', (self.jid, self.resource))
|
||||||
|
# BE CAREFUL: no con.updateRosterItem() in a callback
|
||||||
|
log.debug(_('we are now subscribed to %s') % self.jid)
|
||||||
|
elif self.ptype == 'unsubscribe':
|
||||||
|
log.debug(_('unsubscribe request from %s') % self.jid)
|
||||||
|
elif self.ptype == 'unsubscribed':
|
||||||
|
log.debug(_('we are now unsubscribed from %s') % self.jid)
|
||||||
|
# detect a unsubscription loop
|
||||||
|
if self.jid not in self.conn.subscribed_events:
|
||||||
|
self.conn.subscribed_events[self.jid] = []
|
||||||
|
self.conn.subscribed_events[self.jid].append(time_time())
|
||||||
|
block = False
|
||||||
|
if len(self.conn.subscribed_events[self.jid]) > 5:
|
||||||
|
if time_time() - self.conn.subscribed_events[self.jid][0] < 5:
|
||||||
|
block = True
|
||||||
|
self.conn.subscribed_events[self.jid] = \
|
||||||
|
self.conn.subscribed_events[self.jid][1:]
|
||||||
|
if block:
|
||||||
|
gajim.config.set_per('account', self.conn.name,
|
||||||
|
'dont_ack_subscription', True)
|
||||||
|
else:
|
||||||
|
self.dispatch('UNSUBSCRIBED', self.jid)
|
||||||
|
elif self.ptype == 'error':
|
||||||
|
errmsg = self.iq_obj.getError()
|
||||||
|
errcode = self.iq_obj.getErrorCode()
|
||||||
|
if errcode != '502': # Internal Timeout:
|
||||||
|
# print in the window the error
|
||||||
|
self.conn.dispatch('ERROR_ANSWER', ('', self.jid, errmsg, errcode))
|
||||||
|
if errcode != '409': # conflict # See #5120
|
||||||
|
self.show = 'error'
|
||||||
|
self.status = errmsg
|
||||||
|
return True
|
||||||
|
|
||||||
|
elif self.ptype == 'unavailable':
|
||||||
|
for jid in [self.jid, self.fjid]:
|
||||||
|
if jid not in self.conn.sessions:
|
||||||
|
continue
|
||||||
|
# automatically terminate sessions that they haven't sent a thread
|
||||||
|
# ID in, only if other part support thread ID
|
||||||
|
for sess in self.conn.sessions[jid].values():
|
||||||
|
if not sess.received_thread_id:
|
||||||
|
contact = gajim.contacts.get_contact(self.conn.name,
|
||||||
|
jid)
|
||||||
|
# FIXME: I don't know if this is the correct behavior here.
|
||||||
|
# Anyway, it is the old behavior when we assumed that
|
||||||
|
# not-existing contacts don't support anything
|
||||||
|
contact_exists = bool(contact)
|
||||||
|
session_supported = contact_exists and (
|
||||||
|
contact.supports(xmpp.NS_SSN) or
|
||||||
|
contact.supports(xmpp.NS_ESESSION))
|
||||||
|
if session_supported:
|
||||||
|
sess.terminate()
|
||||||
|
del self.conn.sessions[jid][sess.thread_id]
|
||||||
|
|
||||||
|
if avatar_sha is not None and self.ptype != 'error':
|
||||||
|
if self.jid not in self.conn.vcard_shas:
|
||||||
|
cached_vcard = self.conn.get_cached_vcard(self.jid)
|
||||||
|
if cached_vcard and 'PHOTO' in cached_vcard and \
|
||||||
|
'SHA' in cached_vcard['PHOTO']:
|
||||||
|
self.conn.vcard_shas[self.jid] = \
|
||||||
|
cached_vcard['PHOTO']['SHA']
|
||||||
|
else:
|
||||||
|
self.conn.vcard_shas[self.jid] = ''
|
||||||
|
if avatar_sha != self.conn.vcard_shas[self.jid]:
|
||||||
|
# avatar has been updated
|
||||||
|
self.conn.request_vcard(self.jid)
|
||||||
|
|
||||||
|
if not self.ptype or self.ptype == 'unavailable':
|
||||||
|
if gajim.config.get('log_contact_status_changes') and \
|
||||||
|
gajim.config.should_log(self.conn.name, self.jid):
|
||||||
|
try:
|
||||||
|
gajim.logger.write('status', self.jid, self.status,
|
||||||
|
self.show)
|
||||||
|
except exceptions.PysqliteOperationalError, e:
|
||||||
|
self.conn.dispatch('DB_ERROR', (_('Disk Write Error'), str(e)))
|
||||||
|
except exceptions.DatabaseMalformed:
|
||||||
|
pritext = _('Database Error')
|
||||||
|
sectext = _('The database file (%s) cannot be read. Try to '
|
||||||
|
'repair it (see '
|
||||||
|
'http://trac.gajim.org/wiki/DatabaseBackup) or remove '
|
||||||
|
'it (all history will be lost).') % LOG_DB_PATH
|
||||||
|
self.conn.dispatch('DB_ERROR', (pritext, sectext))
|
||||||
|
our_jid = gajim.get_jid_from_account(self.conn.name)
|
||||||
|
if self.jid == our_jid and self.resource == \
|
||||||
|
self.conn.server_resource:
|
||||||
|
# We got our own presence
|
||||||
|
self.conn.dispatch('STATUS', self.show)
|
||||||
|
else:
|
||||||
|
return True
|
||||||
|
|
|
@ -284,7 +284,7 @@ class Interface:
|
||||||
profile_window.ProfileWindow(account)
|
profile_window.ProfileWindow(account)
|
||||||
gajim.connections[account].request_vcard(jid)
|
gajim.connections[account].request_vcard(jid)
|
||||||
|
|
||||||
def handle_event_notify(self, account, array):
|
def handle_event_presence(self, obj):
|
||||||
# 'NOTIFY' (account, (jid, status, status message, resource,
|
# 'NOTIFY' (account, (jid, status, status message, resource,
|
||||||
# priority, # keyID, timestamp, contact_nickname))
|
# priority, # keyID, timestamp, contact_nickname))
|
||||||
#
|
#
|
||||||
|
@ -294,39 +294,29 @@ class Interface:
|
||||||
|
|
||||||
statuss = ['offline', 'error', 'online', 'chat', 'away', 'xa', 'dnd',
|
statuss = ['offline', 'error', 'online', 'chat', 'away', 'xa', 'dnd',
|
||||||
'invisible']
|
'invisible']
|
||||||
# Ignore invalid show
|
|
||||||
if array[1] not in statuss:
|
account = obj.conn.name
|
||||||
return
|
jid = obj.jid
|
||||||
old_show = 0
|
show = obj.show
|
||||||
new_show = statuss.index(array[1])
|
status = obj.status
|
||||||
status_message = array[2]
|
resource = obj.resource or ''
|
||||||
jid = array[0].split('/')[0]
|
priority = obj.prio
|
||||||
keyID = array[5]
|
keyID = obj.keyID
|
||||||
contact_nickname = array[7]
|
timestamp = obj.timestamp
|
||||||
|
contact_nickname = obj.contact_nickname
|
||||||
|
|
||||||
|
obj.old_show = 0
|
||||||
|
obj.new_show = statuss.index(show)
|
||||||
|
|
||||||
lcontact = []
|
lcontact = []
|
||||||
|
|
||||||
# Get the proper keyID
|
|
||||||
keyID = helpers.prepare_and_validate_gpg_keyID(account, jid, keyID)
|
|
||||||
|
|
||||||
resource = array[3]
|
|
||||||
if not resource:
|
|
||||||
resource = ''
|
|
||||||
priority = array[4]
|
|
||||||
if gajim.jid_is_transport(jid):
|
|
||||||
# It must be an agent
|
|
||||||
ji = jid.replace('@', '')
|
|
||||||
else:
|
|
||||||
ji = jid
|
|
||||||
|
|
||||||
highest = gajim.contacts.get_contact_with_highest_priority(account, jid)
|
highest = gajim.contacts.get_contact_with_highest_priority(account, jid)
|
||||||
was_highest = (highest and highest.resource == resource)
|
was_highest = (highest and highest.resource == resource)
|
||||||
|
|
||||||
conn = gajim.connections[account]
|
|
||||||
|
|
||||||
# Update contact
|
# Update contact
|
||||||
jid_list = gajim.contacts.get_jid_list(account)
|
jid_list = gajim.contacts.get_jid_list(account)
|
||||||
if ji in jid_list or jid == gajim.get_jid_from_account(account):
|
if jid in jid_list or jid == gajim.get_jid_from_account(account):
|
||||||
lcontact = gajim.contacts.get_contacts(account, ji)
|
lcontact = gajim.contacts.get_contacts(account, jid)
|
||||||
contact1 = None
|
contact1 = None
|
||||||
resources = []
|
resources = []
|
||||||
for c in lcontact:
|
for c in lcontact:
|
||||||
|
@ -337,56 +327,58 @@ class Interface:
|
||||||
|
|
||||||
if contact1:
|
if contact1:
|
||||||
if contact1.show in statuss:
|
if contact1.show in statuss:
|
||||||
old_show = statuss.index(contact1.show)
|
obj.old_show = statuss.index(contact1.show)
|
||||||
# nick changed
|
# nick changed
|
||||||
if contact_nickname is not None and \
|
if contact_nickname is not None and \
|
||||||
contact1.contact_name != contact_nickname:
|
contact1.contact_name != contact_nickname:
|
||||||
contact1.contact_name = contact_nickname
|
contact1.contact_name = contact_nickname
|
||||||
self.roster.draw_contact(jid, account)
|
self.roster.draw_contact(jid, account)
|
||||||
|
|
||||||
if old_show == new_show and contact1.status == status_message \
|
if obj.old_show == obj.new_show and contact1.status == status \
|
||||||
and contact1.priority == priority: # no change
|
and contact1.priority == priority: # no change
|
||||||
return
|
return
|
||||||
else:
|
else:
|
||||||
contact1 = gajim.contacts.get_first_contact_from_jid(account,
|
contact1 = gajim.contacts.get_first_contact_from_jid(account,
|
||||||
ji)
|
jid)
|
||||||
if not contact1:
|
if not contact1:
|
||||||
# Presence of another resource of our
|
# Presence of another resource of our
|
||||||
# jid
|
# jid
|
||||||
# Create self contact and add to roster
|
# Create self contact and add to roster
|
||||||
if resource == conn.server_resource:
|
if resource == obj.conn.server_resource:
|
||||||
return
|
return
|
||||||
# Ignore offline presence of unknown self resource
|
# Ignore offline presence of unknown self resource
|
||||||
if new_show < 2:
|
if obj.new_show < 2:
|
||||||
return
|
return
|
||||||
contact1 = gajim.contacts.create_self_contact(jid=ji,
|
contact1 = gajim.contacts.create_self_contact(jid=jid,
|
||||||
account=account, show=array[1], status=status_message,
|
account=account, show=show, status=status,
|
||||||
priority=priority, keyID=keyID, resource=resource)
|
priority=priority, keyID=keyID, resource=resource)
|
||||||
old_show = 0
|
obj.old_show = 0
|
||||||
gajim.contacts.add_contact(account, contact1)
|
gajim.contacts.add_contact(account, contact1)
|
||||||
lcontact.append(contact1)
|
lcontact.append(contact1)
|
||||||
elif contact1.show in statuss:
|
elif contact1.show in statuss:
|
||||||
old_show = statuss.index(contact1.show)
|
obj.old_show = statuss.index(contact1.show)
|
||||||
if (resources != [''] and (len(lcontact) != 1 or \
|
if (resources != [''] and (len(lcontact) != 1 or \
|
||||||
lcontact[0].show != 'offline')) and jid.find('@') > 0:
|
lcontact[0].show != 'offline')) and \
|
||||||
|
not gajim.jid_is_transport(jid):
|
||||||
# Another resource of an existing contact connected
|
# Another resource of an existing contact connected
|
||||||
old_show = 0
|
obj.old_show = 0
|
||||||
contact1 = gajim.contacts.copy_contact(contact1)
|
contact1 = gajim.contacts.copy_contact(contact1)
|
||||||
lcontact.append(contact1)
|
lcontact.append(contact1)
|
||||||
contact1.resource = resource
|
contact1.resource = resource
|
||||||
|
|
||||||
self.roster.add_contact(contact1.jid, account)
|
self.roster.add_contact(contact1.jid, account)
|
||||||
|
|
||||||
if contact1.jid.find('@') > 0 and len(lcontact) == 1:
|
if not gajim.jid_is_transport(contact1.jid) and len(lcontact) == 1:
|
||||||
# It's not an agent
|
# It's not an agent
|
||||||
if old_show == 0 and new_show > 1:
|
if obj.old_show == 0 and obj.new_show > 1:
|
||||||
if not contact1.jid in gajim.newly_added[account]:
|
if not contact1.jid in gajim.newly_added[account]:
|
||||||
gajim.newly_added[account].append(contact1.jid)
|
gajim.newly_added[account].append(contact1.jid)
|
||||||
if contact1.jid in gajim.to_be_removed[account]:
|
if contact1.jid in gajim.to_be_removed[account]:
|
||||||
gajim.to_be_removed[account].remove(contact1.jid)
|
gajim.to_be_removed[account].remove(contact1.jid)
|
||||||
gobject.timeout_add_seconds(5,
|
gobject.timeout_add_seconds(5,
|
||||||
self.roster.remove_newly_added, contact1.jid, account)
|
self.roster.remove_newly_added, contact1.jid, account)
|
||||||
elif old_show > 1 and new_show == 0 and conn.connected > 1:
|
elif obj.old_show > 1 and obj.new_show == 0 and \
|
||||||
|
obj.conn.connected > 1:
|
||||||
if not contact1.jid in gajim.to_be_removed[account]:
|
if not contact1.jid in gajim.to_be_removed[account]:
|
||||||
gajim.to_be_removed[account].append(contact1.jid)
|
gajim.to_be_removed[account].append(contact1.jid)
|
||||||
if contact1.jid in gajim.newly_added[account]:
|
if contact1.jid in gajim.newly_added[account]:
|
||||||
|
@ -396,17 +388,16 @@ class Interface:
|
||||||
self.roster.remove_to_be_removed, contact1.jid, account)
|
self.roster.remove_to_be_removed, contact1.jid, account)
|
||||||
|
|
||||||
# unset custom status
|
# unset custom status
|
||||||
if (old_show == 0 and new_show > 1) or \
|
if (obj.old_show == 0 and obj.new_show > 1) or \
|
||||||
(old_show > 1 and new_show == 0 and conn.connected > 1):
|
(obj.old_show > 1 and obj.new_show == 0 and obj.conn.connected > 1):
|
||||||
if account in self.status_sent_to_users and \
|
if account in self.status_sent_to_users and \
|
||||||
jid in self.status_sent_to_users[account]:
|
jid in self.status_sent_to_users[account]:
|
||||||
del self.status_sent_to_users[account][jid]
|
del self.status_sent_to_users[account][jid]
|
||||||
|
|
||||||
contact1.show = array[1]
|
contact1.show = show
|
||||||
contact1.status = status_message
|
contact1.status = status
|
||||||
contact1.priority = priority
|
contact1.priority = priority
|
||||||
contact1.keyID = keyID
|
contact1.keyID = keyID
|
||||||
timestamp = array[6]
|
|
||||||
if timestamp:
|
if timestamp:
|
||||||
contact1.last_status_time = timestamp
|
contact1.last_status_time = timestamp
|
||||||
elif not gajim.block_signed_in_notifications[account]:
|
elif not gajim.block_signed_in_notifications[account]:
|
||||||
|
@ -416,41 +407,41 @@ class Interface:
|
||||||
|
|
||||||
if gajim.jid_is_transport(jid):
|
if gajim.jid_is_transport(jid):
|
||||||
# It must be an agent
|
# It must be an agent
|
||||||
if ji in jid_list:
|
if jid in jid_list:
|
||||||
# Update existing iter and group counting
|
# Update existing iter and group counting
|
||||||
self.roster.draw_contact(ji, account)
|
self.roster.draw_contact(jid, account)
|
||||||
self.roster.draw_group(_('Transports'), account)
|
self.roster.draw_group(_('Transports'), account)
|
||||||
if new_show > 1 and ji in gajim.transport_avatar[account]:
|
if obj.new_show > 1 and jid in gajim.transport_avatar[account]:
|
||||||
# transport just signed in.
|
# transport just signed in.
|
||||||
# request avatars
|
# request avatars
|
||||||
for jid_ in gajim.transport_avatar[account][ji]:
|
for jid_ in gajim.transport_avatar[account][jid]:
|
||||||
conn.request_vcard(jid_)
|
obj.conn.request_vcard(jid_)
|
||||||
# transport just signed in/out, don't show
|
# transport just signed in/out, don't show
|
||||||
# popup notifications for 30s
|
# popup notifications for 30s
|
||||||
account_ji = account + '/' + ji
|
account_jid = account + '/' + jid
|
||||||
gajim.block_signed_in_notifications[account_ji] = True
|
gajim.block_signed_in_notifications[account_jid] = True
|
||||||
gobject.timeout_add_seconds(30,
|
gobject.timeout_add_seconds(30,
|
||||||
self.unblock_signed_in_notifications, account_ji)
|
self.unblock_signed_in_notifications, account_jid)
|
||||||
locations = (self.instances, self.instances[account])
|
locations = (self.instances, self.instances[account])
|
||||||
for location in locations:
|
for location in locations:
|
||||||
if 'add_contact' in location:
|
if 'add_contact' in location:
|
||||||
if old_show == 0 and new_show > 1:
|
if obj.old_show == 0 and obj.new_show > 1:
|
||||||
location['add_contact'].transport_signed_in(jid)
|
location['add_contact'].transport_signed_in(jid)
|
||||||
break
|
break
|
||||||
elif old_show > 1 and new_show == 0:
|
elif obj.old_show > 1 and obj.new_show == 0:
|
||||||
location['add_contact'].transport_signed_out(jid)
|
location['add_contact'].transport_signed_out(jid)
|
||||||
break
|
break
|
||||||
elif ji in jid_list:
|
elif jid in jid_list:
|
||||||
# It isn't an agent
|
# It isn't an agent
|
||||||
# reset chatstate if needed:
|
# reset chatstate if needed:
|
||||||
# (when contact signs out or has errors)
|
# (when contact signs out or has errors)
|
||||||
if array[1] in ('offline', 'error'):
|
if show in ('offline', 'error'):
|
||||||
contact1.our_chatstate = contact1.chatstate = \
|
contact1.our_chatstate = contact1.chatstate = \
|
||||||
contact1.composing_xep = None
|
contact1.composing_xep = None
|
||||||
|
|
||||||
# TODO: This causes problems when another
|
# TODO: This causes problems when another
|
||||||
# resource signs off!
|
# resource signs off!
|
||||||
conn.stop_all_active_file_transfers(contact1)
|
obj.conn.stop_all_active_file_transfers(contact1)
|
||||||
|
|
||||||
# disable encryption, since if any messages are
|
# disable encryption, since if any messages are
|
||||||
# lost they'll be not decryptable (note that
|
# lost they'll be not decryptable (note that
|
||||||
|
@ -459,19 +450,18 @@ class Interface:
|
||||||
|
|
||||||
# there won't be any sessions here if the contact terminated
|
# there won't be any sessions here if the contact terminated
|
||||||
# their sessions before going offline (which we do)
|
# their sessions before going offline (which we do)
|
||||||
for sess in conn.get_sessions(ji):
|
for sess in obj.conn.get_sessions(jid):
|
||||||
if (ji + '/' + resource) != str(sess.jid):
|
if obj.fjid != str(sess.jid):
|
||||||
continue
|
continue
|
||||||
if sess.control:
|
if sess.control:
|
||||||
sess.control.no_autonegotiation = False
|
sess.control.no_autonegotiation = False
|
||||||
if sess.enable_encryption:
|
if sess.enable_encryption:
|
||||||
sess.terminate_e2e()
|
sess.terminate_e2e()
|
||||||
conn.delete_session(jid, sess.thread_id)
|
obj.conn.delete_session(jid, sess.thread_id)
|
||||||
|
|
||||||
self.roster.chg_contact_status(contact1, array[1], status_message,
|
self.roster.chg_contact_status(contact1, show, status, account)
|
||||||
account)
|
|
||||||
# Notifications
|
# Notifications
|
||||||
if old_show < 2 and new_show > 1:
|
if obj.old_show < 2 and obj.new_show > 1:
|
||||||
show_notif = True
|
show_notif = True
|
||||||
for c in lcontact:
|
for c in lcontact:
|
||||||
if c.resource == resource:
|
if c.resource == resource:
|
||||||
|
@ -482,7 +472,8 @@ class Interface:
|
||||||
break
|
break
|
||||||
if show_notif:
|
if show_notif:
|
||||||
# no other resource is connected, let's look in metacontacts
|
# no other resource is connected, let's look in metacontacts
|
||||||
family = gajim.contacts.get_metacontacts_family(account, ji)
|
family = gajim.contacts.get_metacontacts_family(account,
|
||||||
|
jid)
|
||||||
for info in family:
|
for info in family:
|
||||||
acct_ = info['account']
|
acct_ = info['account']
|
||||||
jid_ = info['jid']
|
jid_ = info['jid']
|
||||||
|
@ -495,12 +486,9 @@ class Interface:
|
||||||
break
|
break
|
||||||
if show_notif:
|
if show_notif:
|
||||||
notify.notify('contact_connected', jid, account,
|
notify.notify('contact_connected', jid, account,
|
||||||
status_message)
|
status)
|
||||||
if self.remote_ctrl:
|
|
||||||
self.remote_ctrl.raise_signal('ContactPresence', (account,
|
|
||||||
array))
|
|
||||||
|
|
||||||
elif old_show > 1 and new_show < 2:
|
elif obj.old_show > 1 and obj.new_show < 2:
|
||||||
show_notif = True
|
show_notif = True
|
||||||
for c in lcontact:
|
for c in lcontact:
|
||||||
if c.resource == resource:
|
if c.resource == resource:
|
||||||
|
@ -511,7 +499,8 @@ class Interface:
|
||||||
break
|
break
|
||||||
if show_notif:
|
if show_notif:
|
||||||
# no other resource is connected, let's look in metacontacts
|
# no other resource is connected, let's look in metacontacts
|
||||||
family = gajim.contacts.get_metacontacts_family(account, ji)
|
family = gajim.contacts.get_metacontacts_family(account,
|
||||||
|
jid)
|
||||||
for info in family:
|
for info in family:
|
||||||
acct_ = info['account']
|
acct_ = info['account']
|
||||||
jid_ = info['jid']
|
jid_ = info['jid']
|
||||||
|
@ -523,26 +512,18 @@ class Interface:
|
||||||
show_notif = False
|
show_notif = False
|
||||||
break
|
break
|
||||||
if show_notif:
|
if show_notif:
|
||||||
notify.notify('contact_disconnected', jid, account,
|
notify.notify('contact_disconnected', jid, account, status)
|
||||||
status_message)
|
|
||||||
if self.remote_ctrl:
|
|
||||||
self.remote_ctrl.raise_signal('ContactAbsence', (account,
|
|
||||||
array))
|
|
||||||
# FIXME: stop non active file transfers
|
|
||||||
# Status change (not connected/disconnected or
|
# Status change (not connected/disconnected or
|
||||||
# error (<1))
|
# error (<1))
|
||||||
elif new_show > 1:
|
elif obj.new_show > 1:
|
||||||
notify.notify('status_change', jid, account, [new_show,
|
notify.notify('status_change', jid, account, [obj.new_show,
|
||||||
status_message])
|
status])
|
||||||
if self.remote_ctrl:
|
|
||||||
self.remote_ctrl.raise_signal('ContactStatus', (account,
|
|
||||||
array))
|
|
||||||
else:
|
else:
|
||||||
# FIXME: MSN transport (CMSN1.2.1 and PyMSN) don't
|
# FIXME: MSN transport (CMSN1.2.1 and PyMSN) don't
|
||||||
# follow the XEP, still the case in 2008.
|
# follow the XEP, still the case in 2008.
|
||||||
# It's maybe a GC_NOTIFY (specialy for MSN gc)
|
# It's maybe a GC_NOTIFY (specialy for MSN gc)
|
||||||
self.handle_event_gc_notify(account, (jid, array[1], status_message,
|
self.handle_event_gc_notify(account, (jid, show, status,
|
||||||
array[3], None, None, None, None, None, [], None, None))
|
resource, None, None, None, None, None, [], None, None))
|
||||||
|
|
||||||
highest = gajim.contacts.get_contact_with_highest_priority(account, jid)
|
highest = gajim.contacts.get_contact_with_highest_priority(account, jid)
|
||||||
is_highest = (highest and highest.resource == resource)
|
is_highest = (highest and highest.resource == resource)
|
||||||
|
@ -2067,7 +2048,6 @@ class Interface:
|
||||||
'INFORMATION': [self.handle_event_information],
|
'INFORMATION': [self.handle_event_information],
|
||||||
'STATUS': [self.handle_event_status],
|
'STATUS': [self.handle_event_status],
|
||||||
'NEW_JID': [self.handle_event_new_jid],
|
'NEW_JID': [self.handle_event_new_jid],
|
||||||
'NOTIFY': [self.handle_event_notify],
|
|
||||||
'MSGERROR': [self.handle_event_msgerror],
|
'MSGERROR': [self.handle_event_msgerror],
|
||||||
'MSGSENT': [self.handle_event_msgsent],
|
'MSGSENT': [self.handle_event_msgsent],
|
||||||
'MSGNOTSENT': [self.handle_event_msgnotsent],
|
'MSGNOTSENT': [self.handle_event_msgnotsent],
|
||||||
|
@ -2142,6 +2122,7 @@ class Interface:
|
||||||
'last-result-received': [self.handle_event_last_status_time],
|
'last-result-received': [self.handle_event_last_status_time],
|
||||||
'muc-admin-received': [self.handle_event_gc_affiliation],
|
'muc-admin-received': [self.handle_event_gc_affiliation],
|
||||||
'muc-owner-received': [self.handle_event_gc_config],
|
'muc-owner-received': [self.handle_event_gc_config],
|
||||||
|
'presence-received': [self.handle_event_presence],
|
||||||
'roster-info': [self.handle_event_roster_info],
|
'roster-info': [self.handle_event_roster_info],
|
||||||
'roster-item-exchange-received': \
|
'roster-item-exchange-received': \
|
||||||
[self.handle_event_roster_item_exchange],
|
[self.handle_event_roster_item_exchange],
|
||||||
|
|
|
@ -115,6 +115,8 @@ class Remote:
|
||||||
self.on_gmail_notify)
|
self.on_gmail_notify)
|
||||||
gajim.ged.register_event_handler('roster-info', ged.POSTGUI,
|
gajim.ged.register_event_handler('roster-info', ged.POSTGUI,
|
||||||
self.on_roster_info)
|
self.on_roster_info)
|
||||||
|
gajim.ged.register_event_handler('presence-received', ged.POSTGUI,
|
||||||
|
self.on_presence_received)
|
||||||
|
|
||||||
def on_last_status_time(self, obj):
|
def on_last_status_time(self, obj):
|
||||||
self.raise_signal('LastStatusTime', (obj.conn.name, [
|
self.raise_signal('LastStatusTime', (obj.conn.name, [
|
||||||
|
@ -136,6 +138,19 @@ class Remote:
|
||||||
self.raise_signal('RosterInfo', (obj.conn.name, [obj.jid, obj.nickname,
|
self.raise_signal('RosterInfo', (obj.conn.name, [obj.jid, obj.nickname,
|
||||||
obj.sub, obj.ask, obj.groups]))
|
obj.sub, obj.ask, obj.groups]))
|
||||||
|
|
||||||
|
def on_presence_received(self, obj):
|
||||||
|
event = None
|
||||||
|
if obj.old_show < 2 and obj.new_show > 1:
|
||||||
|
event = 'ContactPresence'
|
||||||
|
elif obj.old_show > 1 and obj.new_show < 2:
|
||||||
|
event = 'ContactAbsence'
|
||||||
|
elif obj.new_show > 1:
|
||||||
|
event = 'ContactStatus'
|
||||||
|
if event:
|
||||||
|
self.raise_signal(event, (obj.conn.name, [obj.jid, obj.show,
|
||||||
|
obj.status, obj.resource, obj.prio, obj.keyID, obj.timestamp,
|
||||||
|
obj.contact_nickname]))
|
||||||
|
|
||||||
def raise_signal(self, signal, arg):
|
def raise_signal(self, signal, arg):
|
||||||
if self.signal_object:
|
if self.signal_object:
|
||||||
try:
|
try:
|
||||||
|
|
Loading…
Reference in New Issue