From 5b1edd03b86330a5b410612278b535357624738c Mon Sep 17 00:00:00 2001 From: Jefry Lagrange <jefry.reyes@gmail.com> Date: Mon, 30 May 2011 20:14:26 -0400 Subject: [PATCH] Smacks class added, responds to ack requests --- src/common/xmpp/auth_nb.py | 24 ++++++++++++++--- src/common/xmpp/dispatcher_nb.py | 14 +++++++--- src/common/xmpp/protocol.py | 2 +- src/common/xmpp/smacks.py | 44 ++++++++++++++++++++++++++++++++ 4 files changed, 76 insertions(+), 8 deletions(-) create mode 100644 src/common/xmpp/smacks.py diff --git a/src/common/xmpp/auth_nb.py b/src/common/xmpp/auth_nb.py index 670526bf96..a379c6bcdc 100644 --- a/src/common/xmpp/auth_nb.py +++ b/src/common/xmpp/auth_nb.py @@ -21,7 +21,7 @@ Can be used both for client and transport authentication See client_nb.py """ -from protocol import NS_SASL, NS_SESSION, NS_STREAMS, NS_BIND, NS_AUTH +from protocol import NS_SASL, NS_SESSION, NS_STREAMS, NS_BIND, NS_AUTH, NS_STREAM_MGMT from protocol import Node, NodeProcessed, isResultNode, Iq, Protocol, JID from plugin import PlugIn import base64 @@ -31,7 +31,7 @@ import dispatcher_nb import hashlib import hmac import hashlib - +from smacks import Smacks import logging log = logging.getLogger('gajim.c.x.auth_nb') @@ -491,7 +491,8 @@ class NonBlockingNonSASL(PlugIn): self.password = password self.resource = resource self.on_auth = on_auth - + + def plugin(self, owner): """ Determine the best auth method (digest/0k/plain) and use it for auth. @@ -564,6 +565,7 @@ class NonBlockingBind(PlugIn): def __init__(self): PlugIn.__init__(self) self.bound = None + self.supports_sm = False def plugin(self, owner): ''' Start resource binding, if allowed at this time. Used internally. ''' @@ -580,8 +582,14 @@ class NonBlockingBind(PlugIn): def FeaturesHandler(self, conn, feats): """ Determine if server supports resource binding and set some internal - attributes accordingly + attributes accordingly. + + It also checks if server supports stream management """ + + if feats.getTag('sm', namespace=NS_STREAM_MGMT): + self.supports_sm = True # server supports stream management + if not feats.getTag('bind', namespace=NS_BIND): log.info('Server does not requested binding.') # we try to bind resource anyway @@ -625,6 +633,14 @@ class NonBlockingBind(PlugIn): jid = JID(resp.getTag('bind').getTagData('jid')) self._owner.User = jid.getNode() self._owner.Resource = jid.getResource() + # Only negociate stream management after bounded + if self.supports_sm: + # starts negociation + sm = Smacks(self._owner) + self._owner.Dispatcher.supports_sm = True + 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 cca56f33ab..31fa86e3a3 100644 --- a/src/common/xmpp/dispatcher_nb.py +++ b/src/common/xmpp/dispatcher_nb.py @@ -75,7 +75,7 @@ class XMPPDispatcher(PlugIn): stream headers (used by SASL f.e.). """ - def __init__(self): + def __init__(self): PlugIn.__init__(self) self.handlers = {} self._expected = {} @@ -89,6 +89,9 @@ class XMPPDispatcher(PlugIn): self.UnregisterHandler, self.RegisterProtocol, self.SendAndWaitForResponse, self.SendAndCallForResponse, self.getAnID, self.Event, self.send] + + # Let the dispatcher know if there is support for stream management + self.supports_sm = False def getAnID(self): global outgoingID @@ -110,6 +113,7 @@ class XMPPDispatcher(PlugIn): """ self.handlers = handlers + def _init(self): """ Register default namespaces/protocols/handlers. Used internally @@ -125,7 +129,7 @@ class XMPPDispatcher(PlugIn): self.RegisterDefaultHandler(self.returnStanzaHandler) self.RegisterEventHandler(self._owner._caller._event_dispatcher) self.on_responses = {} - + def plugin(self, owner): """ Plug the Dispatcher instance into Client class instance and send initial @@ -416,7 +420,11 @@ class XMPPDispatcher(PlugIn): typ = '' stanza.props = stanza.getProperties() ID = stanza.getID() - + if self.supports_sm and (stanza.getName() != 'r' and + stanza.getName() != 'a' and + stanza.getName() != 'enabled') : + # increments the number of stanzas that has been handled + self.sm.in_h = self.sm.in_h + 1 list_ = ['default'] # we will use all handlers: if typ in self.handlers[xmlns][name]: list_.append(typ) # from very common... diff --git a/src/common/xmpp/protocol.py b/src/common/xmpp/protocol.py index e17ac9315d..c6cecbd010 100644 --- a/src/common/xmpp/protocol.py +++ b/src/common/xmpp/protocol.py @@ -143,7 +143,7 @@ NS_DATA_LAYOUT = 'http://jabber.org/protocol/xdata-layout' # XEP-0141 NS_DATA_VALIDATE = 'http://jabber.org/protocol/xdata-validate' # XEP-0122 NS_XMPP_STREAMS = 'urn:ietf:params:xml:ns:xmpp-streams' NS_RECEIPTS = 'urn:xmpp:receipts' -NS_STREAM_MGMT = 'urn:xmpp:sm:3' # XEP-198 +NS_STREAM_MGMT = 'urn:xmpp:sm:2' # XEP-198 xmpp_stream_error_conditions = ''' bad-format -- -- -- The entity has sent XML that cannot be processed. diff --git a/src/common/xmpp/smacks.py b/src/common/xmpp/smacks.py new file mode 100644 index 0000000000..7c99ab5b80 --- /dev/null +++ b/src/common/xmpp/smacks.py @@ -0,0 +1,44 @@ +from protocol import Acks +from protocol import NS_STREAM_MGMT + +class Smacks(): + ''' + This is Smacks is the Stream Management class. It takes care of requesting + and sending acks. Also, it keeps track of the unhandled outgoing stanzas. + + The dispatcher has to be able to access this class to increment the + number of handled stanzas + ''' + + + def __init__(self, owner): + self._owner = owner + self.out_h = 0 # Outgoing stanzas handled + self.in_h = 0 # Incoming stanzas handled + self.uqueue = [] # Unhandled stanzas queue + + #Register handlers + owner.Dispatcher.RegisterNamespace(NS_STREAM_MGMT) + owner.Dispatcher.RegisterHandler('enabled', self._neg_response + ,xmlns=NS_STREAM_MGMT) + owner.Dispatcher.RegisterHandler('r', self.send_ack + ,xmlns=NS_STREAM_MGMT) + + + def negociate(self): + stanza = Acks() + stanza.buildEnable() + self._owner.Connection.send(stanza, True) + + def _neg_response(self, disp, stanza): + pass + + def send_ack(self, disp, stanza): + ack = Acks() + ack.buildAnswer(self.in_h) + self._owner.Connection.send(ack, False) + + def request_ack(self): + r = Acks() + r.buildRequest() + self._owner.Connection.send(r, False) \ No newline at end of file -- GitLab