first show to use NEC to handle presence events

This commit is contained in:
Yann Leboulanger 2010-09-17 12:41:30 +02:00
parent c9bc9393d5
commit c36e00ed87
4 changed files with 456 additions and 429 deletions

View File

@ -1089,6 +1089,7 @@ ConnectionJingle, ConnectionIBBytestream):
PrivateStorageRosternotesReceivedEvent)
gajim.nec.register_incoming_event(RosternotesReceivedEvent)
gajim.nec.register_incoming_event(StreamConflictReceivedEvent)
gajim.nec.register_incoming_event(PresenceReceivedEvent)
gajim.ged.register_event_handler('http-auth-received', ged.CORE,
self._nec_http_auth_received)
@ -1738,347 +1739,9 @@ ConnectionJingle, ConnectionIBBytestream):
"""
Called when we receive a presence
"""
log.debug('PresenceCB')
gajim.nec.push_incoming_event(NetworkEvent('raw-pres-received',
conn=con, xmpp_pres=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
conn=self, iq_obj=prs))
def _StanzaArrivedCB(self, con, obj):
self.last_io = gajim.idlequeue.current_time()

View File

@ -20,12 +20,16 @@
import datetime
import sys
from time import (localtime, time as time_time)
from calendar import timegm
from common import nec
from common import helpers
from common import gajim
from common import xmpp
from common import dataforms
from common import exceptions
from common.logger import LOG_DB_PATH
import logging
log = logging.getLogger('gajim.c.connection_handlers_events')
@ -592,3 +596,367 @@ class StreamConflictReceivedEvent(nec.NetworkIncomingEvent):
if self.base_event.iq_obj.getTag('conflict'):
self.conn = self.base_event.conn
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

View File

@ -284,7 +284,7 @@ class Interface:
profile_window.ProfileWindow(account)
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,
# priority, # keyID, timestamp, contact_nickname))
#
@ -294,39 +294,29 @@ class Interface:
statuss = ['offline', 'error', 'online', 'chat', 'away', 'xa', 'dnd',
'invisible']
# Ignore invalid show
if array[1] not in statuss:
return
old_show = 0
new_show = statuss.index(array[1])
status_message = array[2]
jid = array[0].split('/')[0]
keyID = array[5]
contact_nickname = array[7]
account = obj.conn.name
jid = obj.jid
show = obj.show
status = obj.status
resource = obj.resource or ''
priority = obj.prio
keyID = obj.keyID
timestamp = obj.timestamp
contact_nickname = obj.contact_nickname
obj.old_show = 0
obj.new_show = statuss.index(show)
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)
was_highest = (highest and highest.resource == resource)
conn = gajim.connections[account]
# Update contact
jid_list = gajim.contacts.get_jid_list(account)
if ji in jid_list or jid == gajim.get_jid_from_account(account):
lcontact = gajim.contacts.get_contacts(account, ji)
if jid in jid_list or jid == gajim.get_jid_from_account(account):
lcontact = gajim.contacts.get_contacts(account, jid)
contact1 = None
resources = []
for c in lcontact:
@ -337,56 +327,58 @@ class Interface:
if contact1:
if contact1.show in statuss:
old_show = statuss.index(contact1.show)
obj.old_show = statuss.index(contact1.show)
# nick changed
if contact_nickname is not None and \
contact1.contact_name != contact_nickname:
contact1.contact_name = contact_nickname
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
return
else:
contact1 = gajim.contacts.get_first_contact_from_jid(account,
ji)
jid)
if not contact1:
# Presence of another resource of our
# jid
# Create self contact and add to roster
if resource == conn.server_resource:
if resource == obj.conn.server_resource:
return
# Ignore offline presence of unknown self resource
if new_show < 2:
if obj.new_show < 2:
return
contact1 = gajim.contacts.create_self_contact(jid=ji,
account=account, show=array[1], status=status_message,
contact1 = gajim.contacts.create_self_contact(jid=jid,
account=account, show=show, status=status,
priority=priority, keyID=keyID, resource=resource)
old_show = 0
obj.old_show = 0
gajim.contacts.add_contact(account, contact1)
lcontact.append(contact1)
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 \
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
old_show = 0
obj.old_show = 0
contact1 = gajim.contacts.copy_contact(contact1)
lcontact.append(contact1)
contact1.resource = resource
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
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]:
gajim.newly_added[account].append(contact1.jid)
if contact1.jid in gajim.to_be_removed[account]:
gajim.to_be_removed[account].remove(contact1.jid)
gobject.timeout_add_seconds(5,
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]:
gajim.to_be_removed[account].append(contact1.jid)
if contact1.jid in gajim.newly_added[account]:
@ -396,17 +388,16 @@ class Interface:
self.roster.remove_to_be_removed, contact1.jid, account)
# unset custom status
if (old_show == 0 and new_show > 1) or \
(old_show > 1 and new_show == 0 and conn.connected > 1):
if (obj.old_show == 0 and obj.new_show > 1) or \
(obj.old_show > 1 and obj.new_show == 0 and obj.conn.connected > 1):
if account in self.status_sent_to_users and \
jid in self.status_sent_to_users[account]:
del self.status_sent_to_users[account][jid]
contact1.show = array[1]
contact1.status = status_message
contact1.show = show
contact1.status = status
contact1.priority = priority
contact1.keyID = keyID
timestamp = array[6]
if timestamp:
contact1.last_status_time = timestamp
elif not gajim.block_signed_in_notifications[account]:
@ -416,41 +407,41 @@ class Interface:
if gajim.jid_is_transport(jid):
# It must be an agent
if ji in jid_list:
if jid in jid_list:
# Update existing iter and group counting
self.roster.draw_contact(ji, account)
self.roster.draw_contact(jid, 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.
# request avatars
for jid_ in gajim.transport_avatar[account][ji]:
conn.request_vcard(jid_)
for jid_ in gajim.transport_avatar[account][jid]:
obj.conn.request_vcard(jid_)
# transport just signed in/out, don't show
# popup notifications for 30s
account_ji = account + '/' + ji
gajim.block_signed_in_notifications[account_ji] = True
account_jid = account + '/' + jid
gajim.block_signed_in_notifications[account_jid] = True
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])
for location in locations:
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)
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)
break
elif ji in jid_list:
elif jid in jid_list:
# It isn't an agent
# reset chatstate if needed:
# (when contact signs out or has errors)
if array[1] in ('offline', 'error'):
if show in ('offline', 'error'):
contact1.our_chatstate = contact1.chatstate = \
contact1.composing_xep = None
# TODO: This causes problems when another
# resource signs off!
conn.stop_all_active_file_transfers(contact1)
# resource signs off!
obj.conn.stop_all_active_file_transfers(contact1)
# disable encryption, since if any messages are
# 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
# their sessions before going offline (which we do)
for sess in conn.get_sessions(ji):
if (ji + '/' + resource) != str(sess.jid):
for sess in obj.conn.get_sessions(jid):
if obj.fjid != str(sess.jid):
continue
if sess.control:
sess.control.no_autonegotiation = False
if sess.enable_encryption:
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,
account)
self.roster.chg_contact_status(contact1, show, status, account)
# Notifications
if old_show < 2 and new_show > 1:
if obj.old_show < 2 and obj.new_show > 1:
show_notif = True
for c in lcontact:
if c.resource == resource:
@ -482,7 +472,8 @@ class Interface:
break
if show_notif:
# 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:
acct_ = info['account']
jid_ = info['jid']
@ -495,12 +486,9 @@ class Interface:
break
if show_notif:
notify.notify('contact_connected', jid, account,
status_message)
if self.remote_ctrl:
self.remote_ctrl.raise_signal('ContactPresence', (account,
array))
status)
elif old_show > 1 and new_show < 2:
elif obj.old_show > 1 and obj.new_show < 2:
show_notif = True
for c in lcontact:
if c.resource == resource:
@ -511,7 +499,8 @@ class Interface:
break
if show_notif:
# 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:
acct_ = info['account']
jid_ = info['jid']
@ -523,26 +512,18 @@ class Interface:
show_notif = False
break
if show_notif:
notify.notify('contact_disconnected', jid, account,
status_message)
if self.remote_ctrl:
self.remote_ctrl.raise_signal('ContactAbsence', (account,
array))
# FIXME: stop non active file transfers
notify.notify('contact_disconnected', jid, account, status)
# Status change (not connected/disconnected or
# error (<1))
elif new_show > 1:
notify.notify('status_change', jid, account, [new_show,
status_message])
if self.remote_ctrl:
self.remote_ctrl.raise_signal('ContactStatus', (account,
array))
elif obj.new_show > 1:
notify.notify('status_change', jid, account, [obj.new_show,
status])
else:
# FIXME: MSN transport (CMSN1.2.1 and PyMSN) don't
# follow the XEP, still the case in 2008.
# It's maybe a GC_NOTIFY (specialy for MSN gc)
self.handle_event_gc_notify(account, (jid, array[1], status_message,
array[3], None, None, None, None, None, [], None, None))
self.handle_event_gc_notify(account, (jid, show, status,
resource, None, None, None, None, None, [], None, None))
highest = gajim.contacts.get_contact_with_highest_priority(account, jid)
is_highest = (highest and highest.resource == resource)
@ -2067,7 +2048,6 @@ class Interface:
'INFORMATION': [self.handle_event_information],
'STATUS': [self.handle_event_status],
'NEW_JID': [self.handle_event_new_jid],
'NOTIFY': [self.handle_event_notify],
'MSGERROR': [self.handle_event_msgerror],
'MSGSENT': [self.handle_event_msgsent],
'MSGNOTSENT': [self.handle_event_msgnotsent],
@ -2142,6 +2122,7 @@ class Interface:
'last-result-received': [self.handle_event_last_status_time],
'muc-admin-received': [self.handle_event_gc_affiliation],
'muc-owner-received': [self.handle_event_gc_config],
'presence-received': [self.handle_event_presence],
'roster-info': [self.handle_event_roster_info],
'roster-item-exchange-received': \
[self.handle_event_roster_item_exchange],

View File

@ -115,6 +115,8 @@ class Remote:
self.on_gmail_notify)
gajim.ged.register_event_handler('roster-info', ged.POSTGUI,
self.on_roster_info)
gajim.ged.register_event_handler('presence-received', ged.POSTGUI,
self.on_presence_received)
def on_last_status_time(self, obj):
self.raise_signal('LastStatusTime', (obj.conn.name, [
@ -136,6 +138,19 @@ class Remote:
self.raise_signal('RosterInfo', (obj.conn.name, [obj.jid, obj.nickname,
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):
if self.signal_object:
try: