diff --git a/src/common/connection.py b/src/common/connection.py index ffe694dd0..bbb2cb974 100644 --- a/src/common/connection.py +++ b/src/common/connection.py @@ -57,9 +57,9 @@ from common import gajim from common import gpg from common import passwords from common import exceptions - from connection_handlers import * +from xmpp import Smacks from string import Template import logging log = logging.getLogger('gajim.c.connection') @@ -711,6 +711,9 @@ class Connection(CommonConnection, ConnectionHandlers): self.private_storage_supported = True self.streamError = '' self.secret_hmac = str(random.random())[2:] + + self.sm = Smacks(self) + gajim.ged.register_event_handler('privacy-list-received', ged.CORE, self._nec_privacy_list_received) gajim.ged.register_event_handler('agent-info-error-received', ged.CORE, @@ -1587,12 +1590,18 @@ class Connection(CommonConnection, ConnectionHandlers): self.connection.set_send_timeout2(self.pingalives, self.sendPing) self.connection.onreceive(None) - self.request_message_archiving_preferences() + self.privacy_rules_requested = False - self.discoverInfo(gajim.config.get_per('accounts', self.name, 'hostname'), - id_prefix='Gajim_') - + # If we are not resuming, we ask for discovery info + # and archiving preferences + if not self.sm.resuming: + + self.request_message_archiving_preferences() + self.discoverInfo(gajim.config.get_per('accounts', self.name, 'hostname'), + id_prefix='Gajim_') + + self.sm.resuming = False # back to previous state # Discover Stun server(s) gajim.resolver.resolve('_stun._udp.' + helpers.idn_to_ascii( self.connected_hostname), self._on_stun_resolved) diff --git a/src/common/xmpp/__init__.py b/src/common/xmpp/__init__.py index 4ebc4cfa3..46037a1cb 100644 --- a/src/common/xmpp/__init__.py +++ b/src/common/xmpp/__init__.py @@ -15,3 +15,4 @@ import simplexml, protocol, auth_nb, transports_nb, roster_nb import dispatcher_nb, features_nb, idlequeue, bosh, tls_nb, proxy_connectors from client_nb import NonBlockingClient from plugin import PlugIn +from smacks import Smacks diff --git a/src/common/xmpp/auth_nb.py b/src/common/xmpp/auth_nb.py index 90e8a421d..9039b12f5 100644 --- a/src/common/xmpp/auth_nb.py +++ b/src/common/xmpp/auth_nb.py @@ -643,13 +643,18 @@ class NonBlockingBind(PlugIn): self._owner.User = jid.getNode() self._owner.Resource = jid.getResource() # Only negociate stream management after bounded + sm = self._owner._caller.sm if self.supports_sm: # starts negociation - sm = Smacks(self._owner) - self._owner.Dispatcher.supports_sm = True + if sm._owner and sm.resumption: + sm.set_owner(self._owner) + sm.resume_request() + else: + sm.set_owner(self._owner) + sm.negociate() + self._owner.Dispatcher.sm = sm - sm.negociate() - + if hasattr(self, 'session') and self.session == -1: # Server don't want us to initialize a session log.info('No session required.') diff --git a/src/common/xmpp/dispatcher_nb.py b/src/common/xmpp/dispatcher_nb.py index f01f78a0a..e0b5d1538 100644 --- a/src/common/xmpp/dispatcher_nb.py +++ b/src/common/xmpp/dispatcher_nb.py @@ -420,7 +420,8 @@ class XMPPDispatcher(PlugIn): typ = '' stanza.props = stanza.getProperties() ID = stanza.getID() - if self.supports_sm and (stanza.getName() != 'r' and + # If server supports stream management + if self.sm != None and (stanza.getName() != 'r' and stanza.getName() != 'a' and stanza.getName() != 'enabled') : # increments the number of stanzas that has been handled diff --git a/src/common/xmpp/smacks.py b/src/common/xmpp/smacks.py index ad191857e..9f360cd65 100644 --- a/src/common/xmpp/smacks.py +++ b/src/common/xmpp/smacks.py @@ -13,15 +13,21 @@ class Smacks(): ''' - def __init__(self, owner): - self._owner = owner + def __init__(self, con): + self.con = con # Connection object self.out_h = 0 # Outgoing stanzas handled self.in_h = 0 # Incoming stanzas handled self.uqueue = [] # Unhandled stanzas queue - self.sesion_id = None - self.supports_resume = False # If server supports resume + self.session_id = None + self.resumption = False # If server supports resume # Max number of stanzas in queue before making a request self.max_queue = 5 + self._owner = None + self.resuming = False + + def set_owner(self, owner): + self._owner = owner + # Register handlers owner.Dispatcher.RegisterNamespace(NS_STREAM_MGMT) owner.Dispatcher.RegisterHandler('enabled', self._neg_response @@ -30,19 +36,41 @@ class Smacks(): ,xmlns=NS_STREAM_MGMT) owner.Dispatcher.RegisterHandler('a', self.check_ack ,xmlns=NS_STREAM_MGMT) - - - def negociate(self): - stanza = Acks() - stanza.buildEnable(resume=True) - self._owner.Connection.send(stanza, now=True) + owner.Dispatcher.RegisterHandler('resumed', self.check_ack + ,xmlns=NS_STREAM_MGMT) + owner.Dispatcher.RegisterHandler('failed', self.error_handling + ,xmlns=NS_STREAM_MGMT) + def _neg_response(self, disp, stanza): r = stanza.getAttr('resume') - if r == 'true': - self.supports_resume = True - self.sesion_id = stanza.getAttr(id) - + if r == 'true' or r == 'True' or r == '1': + self.resumption = True + self.session_id = stanza.getAttr('id') + + if r == 'false' or r == 'False' or r == '0': + self.negociate(False) + + def negociate(self, resume=True): + # Every time we attempt to negociate, we must erase all previous info + # about any previous session + self.uqueue = [] + self.in_h = 0 + self.out_h = 0 + self.session_id = None + + stanza = Acks() + stanza.buildEnable(resume) + self._owner.Connection.send(stanza, now=True) + + def resume_request(self): + if not self.session_id: + self.resuming = False + log.error('Attempted to resume without a valid session id ') + return + resume = Acks() + resume.buildResume(self.in_h, self.session_id) + self._owner.Connection.send(resume, True) def send_ack(self, disp, stanza): ack = Acks() @@ -70,5 +98,24 @@ class Smacks(): while (len(self.uqueue) > diff): self.uqueue.pop(0) - + if stanza.getName() == 'resumed': + self.resuming = True + if self.uqueue != []: + for i in self.uqueue: + self._owner.Connection.send(i, False) + + def error_handling(self, disp, stanza): # NEEDS TESTING + + tag = stanza.getTag('item-not-found') + # If the server doesn't recognize previd, forget about resuming + # Ask for service discovery, etc.. + if tag: + self.negociate() + self.resuming = False + self.con._discover_server_at_connection(self.con.connection) + + tag = stanza.getTag('feature-not-implemented') + # Doesn't support resumption + if tag: + self.negociate(False) \ No newline at end of file