Hello community, here is the log from the commit of package python-nbxmpp for openSUSE:Factory checked in at 2017-11-09 14:07:42 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-nbxmpp (Old) and /work/SRC/openSUSE:Factory/.python-nbxmpp.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-nbxmpp" Thu Nov 9 14:07:42 2017 rev:15 rq:540013 version:0.6.0 Changes: -------- --- /work/SRC/openSUSE:Factory/python-nbxmpp/python-nbxmpp.changes 2017-06-12 15:29:05.184669150 +0200 +++ /work/SRC/openSUSE:Factory/.python-nbxmpp.new/python-nbxmpp.changes 2017-11-09 14:07:43.781401975 +0100 @@ -1,0 +2,11 @@ +Sun Nov 5 16:38:49 UTC 2017 - [email protected] + +- Update to version 0.6.0: + * Add new getOriginID/SetOriginID method for Messages + * Add new getJid() method for Protocol + * getTagAttr() accepts now a namespace argument + * Add new `protocol` argument for getTag() + * Add new XEP Namespaces + * Support XEP-0198 Version 1.5.2 + +------------------------------------------------------------------- Old: ---- nbxmpp-0.5.5.tar.gz New: ---- nbxmpp-0.6.0.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-nbxmpp.spec ++++++ --- /var/tmp/diff_new_pack.A0ipKn/_old 2017-11-09 14:07:45.101353874 +0100 +++ /var/tmp/diff_new_pack.A0ipKn/_new 2017-11-09 14:07:45.105353729 +0100 @@ -19,7 +19,7 @@ %{?!python_module:%define python_module() python-%{**} python3-%{**}} %define _name nbxmpp Name: python-nbxmpp -Version: 0.5.5 +Version: 0.6.0 Release: 0 Summary: XMPP library by Gajim team License: GPL-3.0+ ++++++ nbxmpp-0.5.5.tar.gz -> nbxmpp-0.6.0.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nbxmpp-0.5.5/ChangeLog new/nbxmpp-0.6.0/ChangeLog --- old/nbxmpp-0.5.5/ChangeLog 2017-01-30 22:34:33.000000000 +0100 +++ new/nbxmpp-0.6.0/ChangeLog 2017-09-25 22:18:45.000000000 +0200 @@ -1,3 +1,16 @@ +python-nbxmpp 0.6.0 (25 September 2017) + + * Add new getOriginID/SetOriginID method for Messages + * Add new getJid() method for Protocol + * getTagAttr() accepts now a namespace argument + * Add new `protocol` argument for getTag() + * Add new XEP Namespaces + +python-nbxmpp 0.5.6 (03 June 2017) + + * Support XEP-0198 Version 1.5.2 + * Add new XEP Namespaces + python-nbxmpp 0.5.5 (30 January 2017) * Some cleanup diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nbxmpp-0.5.5/PKG-INFO new/nbxmpp-0.6.0/PKG-INFO --- old/nbxmpp-0.5.5/PKG-INFO 2017-01-30 22:49:18.000000000 +0100 +++ new/nbxmpp-0.6.0/PKG-INFO 2017-09-25 22:20:36.000000000 +0200 @@ -1,6 +1,6 @@ Metadata-Version: 1.0 Name: nbxmpp -Version: 0.5.5 +Version: 0.6.0 Summary: Non blocking Jabber/XMPP module Home-page: http://dev.gajim.org/gajim/python-nbxmpp Author: Yann Leboulanger diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nbxmpp-0.5.5/nbxmpp/__init__.py new/nbxmpp-0.6.0/nbxmpp/__init__.py --- old/nbxmpp-0.5.5/nbxmpp/__init__.py 2017-01-30 22:40:44.000000000 +0100 +++ new/nbxmpp-0.6.0/nbxmpp/__init__.py 2017-09-25 22:18:45.000000000 +0200 @@ -17,4 +17,4 @@ from .plugin import PlugIn from .smacks import Smacks -__version__ = "0.5.5" +__version__ = "0.6.0" diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nbxmpp-0.5.5/nbxmpp/auth_nb.py new/nbxmpp-0.6.0/nbxmpp/auth_nb.py --- old/nbxmpp-0.5.5/nbxmpp/auth_nb.py 2017-01-01 23:13:23.000000000 +0100 +++ new/nbxmpp-0.6.0/nbxmpp/auth_nb.py 2017-06-04 14:17:22.000000000 +0200 @@ -731,12 +731,13 @@ self.bound.append(resp.getTag('bind').getTagData('jid')) log.info('Successfully bound %s.' % self.bound[-1]) jid = JID(resp.getTag('bind').getTagData('jid')) + self._owner._registered_name = jid 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 = self._owner._caller.sm sm.supports_sm = True sm.set_owner(self._owner) sm.negociate() @@ -745,6 +746,7 @@ if hasattr(self, 'session') and self.session == -1: # Server don't want us to initialize a session log.info('No session required.') + self._owner._caller.sm.resend_queue() #resend old messages still in the smacks queue self.on_bound('ok') else: self._owner.SendAndWaitForResponse(Protocol('iq', typ='set', @@ -763,6 +765,7 @@ if isResultNode(resp): log.info('Successfully opened session.') self.session = 1 + self._owner._caller.sm.resend_queue() #resend old messages still in the smacks queue self.on_bound('ok') else: log.error('Session open failed.') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nbxmpp-0.5.5/nbxmpp/dispatcher_nb.py new/nbxmpp-0.6.0/nbxmpp/dispatcher_nb.py --- old/nbxmpp-0.5.5/nbxmpp/dispatcher_nb.py 2017-01-01 23:13:23.000000000 +0100 +++ new/nbxmpp-0.6.0/nbxmpp/dispatcher_nb.py 2017-06-04 14:17:22.000000000 +0200 @@ -23,12 +23,14 @@ from __future__ import unicode_literals from . import simplexml import sys +import time import locale import re import uuid +import copy from xml.parsers.expat import ExpatError from .plugin import PlugIn -from .protocol import (NS_STREAMS, NS_XMPP_STREAMS, NS_HTTP_BIND, Iq, Presence, +from .protocol import (NS_DELAY2, NS_STREAMS, NS_XMPP_STREAMS, NS_HTTP_BIND, Iq, Presence, Message, Protocol, Node, Error, ERR_FEATURE_NOT_IMPLEMENTED, StreamError) import logging @@ -387,7 +389,7 @@ def UnregisterCycleHandler(self, handler): """ - Unregister handler that will is called on every Dispatcher.Process() call + Unregister handler that will be called on every Dispatcher.Process() call """ if handler in self._cycleHandlers: self._cycleHandlers.remove(handler) @@ -569,14 +571,25 @@ if self._owner._registered_name and not stanza.getAttr('from'): stanza.setAttr('from', self._owner._registered_name) + self._owner.Connection.send(stanza, now) + # If no ID then it is a whitespace if self.sm and self.sm.enabled and ID: + # add timestamp to message stanza in queue + if (stanza.getName() == 'message' and + stanza.getType() in ('chat', 'groupchat')): + timestamp = time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()) + attrs = {'stamp': timestamp} + if stanza.getType() != 'groupchat': + # Dont leak our JID to Groupchats + attrs['from'] = stanza.getAttr('from') + stanza.addChild('delay', namespace=NS_DELAY2, attrs=attrs) self.sm.uqueue.append(stanza) self.sm.out_h += 1 + if len(self.sm.uqueue) > self.sm.max_queue: self.sm.request_ack() - self._owner.Connection.send(stanza, now) return ID diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nbxmpp-0.5.5/nbxmpp/protocol.py new/nbxmpp-0.6.0/nbxmpp/protocol.py --- old/nbxmpp-0.5.5/nbxmpp/protocol.py 2017-01-30 22:24:18.000000000 +0100 +++ new/nbxmpp-0.6.0/nbxmpp/protocol.py 2017-08-05 21:17:49.000000000 +0200 @@ -24,6 +24,7 @@ import time import string import hashlib +from base64 import b64encode def ascii_upper(s): return s.upper() @@ -88,6 +89,7 @@ NS_HTTP_BIND = 'http://jabber.org/protocol/httpbind' # XEP-0124 NS_HTTPUPLOAD = 'urn:xmpp:http:upload' # XEP-0363 NS_IBB = 'http://jabber.org/protocol/ibb' +NS_IDLE = 'urn:xmpp:idle:1' # XEP-0319 NS_INVISIBLE = 'presence-invisible' # Jabberd2 NS_IQ = 'iq' # Jabberd2 NS_JINGLE ='urn:xmpp:jingle:1' # XEP-0166 @@ -95,7 +97,8 @@ NS_JINGLE_RTP = 'urn:xmpp:jingle:apps:rtp:1' # XEP-0167 NS_JINGLE_RTP_AUDIO = 'urn:xmpp:jingle:apps:rtp:audio' # XEP-0167 NS_JINGLE_RTP_VIDEO = 'urn:xmpp:jingle:apps:rtp:video' # XEP-0167 -NS_JINGLE_FILE_TRANSFER ='urn:xmpp:jingle:apps:file-transfer:3' # XEP-0234 +NS_JINGLE_FILE_TRANSFER = 'urn:xmpp:jingle:apps:file-transfer:3' # XEP-0234 +NS_JINGLE_FILE_TRANSFER_5 = 'urn:xmpp:jingle:apps:file-transfer:5' # XEP-0234 NS_JINGLE_XTLS='urn:xmpp:jingle:security:xtls:0' # XTLS: EXPERIMENTAL security layer of jingle NS_JINGLE_RAW_UDP = 'urn:xmpp:jingle:transports:raw-udp:1' # XEP-0177 NS_JINGLE_ICE_UDP = 'urn:xmpp:jingle:transports:ice-udp:1' # XEP-0176 @@ -104,6 +107,8 @@ NS_LAST = 'jabber:iq:last' NS_LOCATION = 'http://jabber.org/protocol/geoloc' # XEP-0080 NS_MAM = 'urn:xmpp:mam:0' # XEP-0313 +NS_MAM_1 = 'urn:xmpp:mam:1' # XEP-0313 +NS_MAM_2 = 'urn:xmpp:mam:2' # XEP-0313 NS_MESSAGE = 'message' # Jabberd2 NS_MOOD = 'http://jabber.org/protocol/mood' # XEP-0107 NS_MUC = 'http://jabber.org/protocol/muc' @@ -140,6 +145,7 @@ NS_SESSION = 'urn:ietf:params:xml:ns:xmpp-session' NS_SI = 'http://jabber.org/protocol/si' # XEP-0096 NS_SI_PUB = 'http://jabber.org/protocol/sipub' # XEP-0137 +NS_SID = 'urn:xmpp:sid:0' # XEP-0359 NS_SIGNED = 'jabber:x:signed' # XEP-0027 NS_SIMS = 'urn:xmpp:sims:1' # XEP-0385 NS_SSN = 'urn:xmpp:ssn' # XEP-0155 @@ -169,12 +175,17 @@ NS_PUBKEY_PUBKEY = 'urn:xmpp:pubkey:2' # XEP-0189 NS_PUBKEY_REVOKE = 'urn:xmpp:revoke:2' NS_PUBKEY_ATTEST = 'urn:xmpp:attest:2' -NS_STREAM_MGMT = 'urn:xmpp:sm:2' # XEP-198 +NS_STREAM_MGMT = 'urn:xmpp:sm:3' # XEP-198 NS_HASHES = 'urn:xmpp:hashes:1' # XEP-300 -NS_HASHES_MD5 = 'urn:xmpp:hash-function-textual-names:md5' -NS_HASHES_SHA1 = 'urn:xmpp:hash-function-textual-names:sha-1' -NS_HASHES_SHA256 = 'urn:xmpp:hash-function-textual-names:sha-256' -NS_HASHES_SHA512 = 'urn:xmpp:hash-function-textual-names:sha-512' +NS_HASHES_2 = 'urn:xmpp:hashes:2' # XEP-300 +NS_HASHES_MD5 = 'urn:xmpp:hash-function-text-names:md5' +NS_HASHES_SHA1 = 'urn:xmpp:hash-function-text-names:sha-1' +NS_HASHES_SHA256 = 'urn:xmpp:hash-function-text-names:sha-256' +NS_HASHES_SHA512 = 'urn:xmpp:hash-function-text-names:sha-512' +NS_HASHES_SHA3_256 = 'urn:xmpp:hash-function-text-names:sha3-256' +NS_HASHES_SHA3_512 = 'urn:xmpp:hash-function-text-names:sha3-512' +NS_HASHES_BLAKE2B_256 = 'urn:xmpp:hash-function-text-names:id-blake2b256' +NS_HASHES_BLAKE2B_512 = 'urn:xmpp:hash-function-text-names:id-blake2b512' #xmpp_stream_error_conditions = ''' #bad-format -- -- -- The entity has sent XML that cannot be processed. @@ -825,6 +836,15 @@ return self.timestamp return time.strftime('%Y-%m-%dT%H:%M:%SZ', time.gmtime()) + def getJid(self): + """ + Return the value of the 'jid' attribute + """ + attr = self.getAttr('jid') + if attr: + return JID(attr) + return attr + def getID(self): """ Return the value of the 'id' attribute @@ -937,6 +957,19 @@ props.append(prop) return props + def getTag(self, name, attrs=None, namespace=None, protocol=False): + """ + Return the Node instance for the tag. + If protocol is True convert to a new Protocol/Message instance. + """ + tag = Node.getTag(self, name, attrs, namespace) + if protocol and tag: + if name == 'message': + return Message(node=tag) + else: + return Protocol(node=tag) + return tag + def __setitem__(self, item, val): """ Set the item 'item' to the value 'val' @@ -1004,6 +1037,12 @@ """ return self.getTagData('thread') + def getOriginID(self): + """ + Return origin-id of the message + """ + return self.getTagAttr('origin-id', namespace=NS_SID, attr='id') + def setBody(self, val): """ Set the text of the message""" @@ -1041,6 +1080,12 @@ """ self.setTagData('thread', val) + def setOriginID(self, val): + """ + Sets the origin-id of the message + """ + self.setTag('origin-id', namespace=NS_SID, attrs={'id': val}) + def buildReply(self, text=None): """ Builds and returns another message object with specified text. The to, @@ -1345,6 +1390,62 @@ hash_ = hl.hexdigest() return hash_ +class Hashes2(Node): + """ + Hash elements for various XEPs as defined in XEP-300 + """ + + """ + RECOMENDED HASH USE: + Algorithm Support + MD2 MUST NOT + MD4 MUST NOT + MD5 MUST NOT + SHA-1 SHOULD NOT + SHA-256 MUST + SHA-512 SHOULD + SHA3-256 MUST + SHA3-512 SHOULD + BLAKE2b256 MUST + BLAKE2b512 SHOULD + """ + + supported = ('sha-256', 'sha-512', 'sha3-256', 'sha3-512', 'blake2b-256', 'blake2b-512') + + def __init__(self, nsp=NS_HASHES): + Node.__init__(self, None, {}, [], None, None, False, None) + self.setNamespace(nsp) + self.setName('hash') + + def calculateHash(self, algo, file_string): + """ + Calculate the hash and add it. It is preferable doing it here + instead of doing it all over the place in Gajim. + """ + hl = None + hash_ = None + if algo == 'sha-256': + hl = hashlib.sha256() + elif algo == 'sha-512': + hl = hashlib.sha512() + elif algo == 'sha3-256': + hl = hashlib.sha3_256() + elif algo == 'sha3-512': + hl = hashlib.sha3_512() + elif algo == 'blake2b-256': + hl = hashlib.blake2b(digest_size=32) + elif algo == 'blake2b-512': + hl = hashlib.blake2b(digest_size=64) + # file_string can be a string or a file + if hl is not None: + if isinstance(file_string, bytes): + hl.update(file_string) + else: # if it is a file + for line in file_string: + hl.update(line) + hash_ = b64encode(hl.digest()).decode('ascii') + return hash_ + def addHash(self, hash_, algo): self.setAttr('algo', algo) self.setData(hash_) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nbxmpp-0.5.5/nbxmpp/simplexml.py new/nbxmpp-0.6.0/nbxmpp/simplexml.py --- old/nbxmpp-0.5.5/nbxmpp/simplexml.py 2017-01-04 14:11:04.000000000 +0100 +++ new/nbxmpp-0.6.0/nbxmpp/simplexml.py 2017-08-05 21:17:49.000000000 +0200 @@ -331,13 +331,13 @@ """ return self.getTags(name, attrs, namespace, one=1) - def getTagAttr(self, tag, attr): + def getTagAttr(self, tag, attr, namespace=None): """ Return attribute value of the child with specified name (or None if no such attribute) """ try: - return self.getTag(tag).attrs[attr] + return self.getTag(tag, namespace=namespace).attrs[attr] except: return None @@ -445,15 +445,15 @@ else: return self.addChild(name, attrs, namespace=namespace) - def setTagAttr(self, tag, attr, val): + def setTagAttr(self, tag, attr, val, namespace=None): """ Create new node (if not already present) with name "tag" and set it's attribute "attr" to value "val" """ try: - self.getTag(tag).attrs[attr] = val + self.getTag(tag, namespace=namespace).attrs[attr] = val except Exception: - self.addChild(tag, attrs={attr: val}) + self.addChild(tag, namespace=namespace, attrs={attr: val}) def setTagData(self, tag, val, attrs=None): """ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nbxmpp-0.5.5/nbxmpp/smacks.py new/nbxmpp-0.6.0/nbxmpp/smacks.py --- old/nbxmpp-0.5.5/nbxmpp/smacks.py 2017-01-01 23:13:23.000000000 +0100 +++ new/nbxmpp-0.6.0/nbxmpp/smacks.py 2017-06-04 14:17:22.000000000 +0200 @@ -1,5 +1,6 @@ from .protocol import Acks from .protocol import NS_STREAM_MGMT +from .transports_nb import CONNECTED, CONNECTING, DISCONNECTED, DISCONNECTING import logging log = logging.getLogger('nbxmpp.smacks') @@ -22,7 +23,8 @@ 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 + #be more agressive here (every message must be acked), this creates fewer message duplicates on failed resume) + self.max_queue = 0 self._owner = None self.resuming = False self.enabled = False # If SM is enabled @@ -47,6 +49,7 @@ def _neg_response(self, disp, stanza): r = stanza.getAttr('resume') + log.info("Session resumption: %s" % r) if r == 'true' or r == 'True' or r == '1': self.resumption = True self.session_id = stanza.getAttr('id') @@ -62,8 +65,8 @@ def negociate(self, resume=True): # Every time we attempt to negociate, we must erase all previous info # about any previous session + log.debug("Clearing smacks uqueue") self.uqueue = [] - self.old_uqueue = [] self.in_h = 0 self.out_h = 0 self.session_id = None @@ -73,12 +76,26 @@ stanza.buildEnable(resume) self._owner.Connection.send(stanza, now=True) + def resend_queue(self): + """ + Resends unsent stanzas when a new session is established. + This way there won't be any lost outgoing messages even on failed smacks resumes (but message duplicates are possible). + If your server supports revision 1.5 of smacks then even message duplicates are eliminated here :) + """ + if self.old_uqueue: + log.info('Session resumption failed, replaying %s stanzas anyways...' % len(self.old_uqueue)) + for i in self.old_uqueue: + self._owner.Dispatcher.send(i, False) #use this send so that our stanzas actually increment out_h + self.old_uqueue = [] + def resume_request(self): if not self.session_id: self.resuming = False log.error('Attempted to resume without a valid session id ') return - self.old_uqueue = self.uqueue #save old messages in an extra "queue" to avoid race conditions + #save old messages in an extra "queue" to avoid race conditions and to make it possible to replay stanzas even when resuming fails + #add messages here (instead of overwriting) so that repeated connection errors don't delete unacked stanzas (uqueue should be empty in this case anyways) + self.old_uqueue += self.uqueue self.uqueue = [] resume = Acks() resume.buildResume(self.in_h, self.session_id) @@ -88,6 +105,14 @@ ack = Acks() ack.buildAnswer(self.in_h) self._owner.Connection.send(ack, False) + + def send_closing_ack(self): + if self._owner.Connection.get_state() != DISCONNECTING: + log.error("Not allowed to send closing smacks ack when not disconnecting!") + return + ack = Acks() + ack.buildAnswer(self.in_h) + self._owner.Connection.send(ack, True) def request_ack(self): r = Acks() @@ -108,13 +133,13 @@ diff = self.out_h - h if diff < 0: - log.error('Server and client number of stanzas handled mismatch (our h: %d, server h: %d)' % (self.out_h, h)) + log.error('Server and client number of stanzas handled mismatch (our h: %d, server h: %d, #queue: %d)' % (self.out_h, h, len(self.uqueue))) while (len(self.uqueue)): #don't accumulate all messages in this case (they would otherwise all be resent on the next reconnect) self.uqueue.pop(0) elif len(self.uqueue) < diff: - log.error('Server and client number of stanzas handled mismatch (our h: %d, server h: %d)' % (self.out_h, h)) + log.error('Server and client number of stanzas handled mismatch (our h: %d, server h: %d, #queue: %d)' % (self.out_h, h, len(self.uqueue))) else: - log.debug('Got ack for outgoing stanzas (our h: %d, server h: %d), removing %d messages from queue...' % (self.out_h, h, len(self.uqueue) - diff)) + log.debug('Got ack for outgoing stanzas (our h: %d, server h: %d, #queue: %d), removing %d messages from queue...' % (self.out_h, h, len(self.uqueue), len(self.uqueue) - diff)) while (len(self.uqueue) > diff): self.uqueue.pop(0) @@ -124,6 +149,7 @@ number of stanzas received by the server. Resends stanzas not received by the server in the last session. """ + log.info("Session resumption succeeded") h = stanza.getAttr('h') if not h: log.error('Server did not send h attribute') @@ -132,12 +158,12 @@ diff = self.out_h - h if diff < 0: - log.error('Server and client number of stanzas handled mismatch on session resumption (our h: %d, server h: %d)' % (self.out_h, h)) + log.error('Server and client number of stanzas handled mismatch on session resumption (our h: %d, server h: %d. #queue: %d)' % (self.out_h, h, len(self.old_uqueue))) self.old_uqueue = [] #that's weird, but we don't resend this stanzas if the server says we don't need to elif len(self.old_uqueue) < diff: - log.error('Server and client number of stanzas handled mismatch on session resumption (our h: %d, server h: %d)' % (self.out_h, h)) + log.error('Server and client number of stanzas handled mismatch on session resumption (our h: %d, server h: %d, #queue: %d)' % (self.out_h, h, len(self.old_uqueue))) else: - log.info('Removing %d already received stanzas from old outgoing queue (our h: %d, server h: %d, remaining in queue: %d)' % (len(self.old_uqueue) - diff, self.out_h, h, diff)) + log.info('Removing %d already acked stanzas from old outgoing queue (our h: %d, server h: %d, #queue: %d, remaining in queue: %d)' % (len(self.old_uqueue) - diff, self.out_h, h, len(self.old_uqueue), diff)) while (len(self.old_uqueue) > diff): self.old_uqueue.pop(0) @@ -160,14 +186,33 @@ self._owner.NonBlockingBind.resuming = False self._owner._on_auth_bind(None) self.failed_resume = True + + h = stanza.getAttr('h') + log.info('Session resumption failed (item-not-found), server h: %s' % str(h)) + if not h: + return + #prepare old_queue to contain only unacked stanzas for later resend (which is happening after our session is established properly) + h = int(h) + diff = self.out_h - h + + if diff < 0: + log.error('Server and client number of stanzas handled mismatch on session resumption (our h: %d, server h: %d, #queue: %d)' % (self.out_h, h, len(self.old_uqueue))) + self.old_uqueue = [] #that's weird, but we don't resend this stanzas if the server says we don't need to + elif len(self.old_uqueue) < diff: + log.error('Server and client number of stanzas handled mismatch on session resumption (our h: %d, server h: %d, #queue: %d)' % (self.out_h, h, len(self.old_uqueue))) + else: + log.info('Removing %d already acked stanzas from old outgoing queue (our h: %d, server h: %d, #queue: %d, remaining in queue: %d)' % (len(self.old_uqueue) - diff, self.out_h, h, len(self.old_uqueue), diff)) + while (len(self.old_uqueue) > diff): + self.old_uqueue.pop(0) return # Doesn't support resumption if stanza.getTag('feature-not-implemented'): + log.info('Session resumption failed (feature-not-implemented)') self.negociate(False) return if stanza.getTag('unexpected-request'): - self.enabled = False log.error('Gajim failed to negociate Stream Management') + self.enabled = False return diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nbxmpp-0.5.5/nbxmpp/transports_nb.py new/nbxmpp-0.6.0/nbxmpp/transports_nb.py --- old/nbxmpp-0.5.5/nbxmpp/transports_nb.py 2017-01-01 23:13:23.000000000 +0100 +++ new/nbxmpp-0.6.0/nbxmpp/transports_nb.py 2017-06-04 14:17:22.000000000 +0200 @@ -304,6 +304,8 @@ # FIXME: where and why does this need to be called def start_disconnect(self): self.set_state(DISCONNECTING) + if self._owner._caller.sm and self._owner._caller.sm.enabled: + self._owner._caller.sm.send_closing_ack() class NonBlockingTCP(NonBlockingTransport, IdleObject): diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/nbxmpp-0.5.5/setup.py new/nbxmpp-0.6.0/setup.py --- old/nbxmpp-0.5.5/setup.py 2017-01-30 22:40:36.000000000 +0100 +++ new/nbxmpp-0.6.0/setup.py 2017-09-25 22:18:45.000000000 +0200 @@ -3,7 +3,7 @@ from distutils.core import setup setup(name='nbxmpp', - version='0.5.5', + version='0.6.0', description='Non blocking Jabber/XMPP module', author='Yann Leboulanger', author_email='[email protected]',
