From 3bb01de1201501d759c6aadb08ecd9ccf0bdc47c Mon Sep 17 00:00:00 2001 From: mathieui Date: Sun, 18 Apr 2021 21:15:22 +0200 Subject: [PATCH 1/5] stanza: add a Handshake class and use it in componentxmpp --- slixmpp/componentxmpp.py | 8 +++++--- slixmpp/stanza/__init__.py | 7 ++++++- slixmpp/stanza/handshake.py | 25 +++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 4 deletions(-) create mode 100644 slixmpp/stanza/handshake.py diff --git a/slixmpp/componentxmpp.py b/slixmpp/componentxmpp.py index 3a16f942..8927712e 100644 --- a/slixmpp/componentxmpp.py +++ b/slixmpp/componentxmpp.py @@ -10,6 +10,7 @@ import logging import hashlib from slixmpp.basexmpp import BaseXMPP +from slixmpp.stanza import Handshake from slixmpp.xmlstream import XMLStream from slixmpp.xmlstream import ET from slixmpp.xmlstream.matcher import MatchXPath @@ -123,9 +124,10 @@ class ComponentXMPP(BaseXMPP): sid = xml.get('id', '') pre_hash = bytes('%s%s' % (sid, self.secret), 'utf-8') - handshake = ET.Element('{jabber:component:accept}handshake') - handshake.text = hashlib.sha1(pre_hash).hexdigest().lower() - self.send_xml(handshake) + handshake = Handshake() + handshake['value'] = hashlib.sha1(pre_hash).hexdigest().lower() + + self.send(handshake) def _handle_handshake(self, xml): """The handshake has been accepted. diff --git a/slixmpp/stanza/__init__.py b/slixmpp/stanza/__init__.py index 6e2e9718..1371fad2 100644 --- a/slixmpp/stanza/__init__.py +++ b/slixmpp/stanza/__init__.py @@ -1,4 +1,3 @@ - # Slixmpp: The Slick XMPP Library # Copyright (C) 2010 Nathanael C. Fritz # This file is part of Slixmpp. @@ -10,3 +9,9 @@ from slixmpp.stanza.message import Message from slixmpp.stanza.presence import Presence from slixmpp.stanza.stream_features import StreamFeatures from slixmpp.stanza.stream_error import StreamError +from slixmpp.stanza.handshake import Handshake + +__all__ = [ + 'Error', 'Iq', 'Message', 'Presence', 'StreamFeatures', 'StreamError', + 'Handshake' +] diff --git a/slixmpp/stanza/handshake.py b/slixmpp/stanza/handshake.py new file mode 100644 index 00000000..c58f69aa --- /dev/null +++ b/slixmpp/stanza/handshake.py @@ -0,0 +1,25 @@ +# Slixmpp: The Slick XMPP Library +# Copyright (C) 2021 Mathieu Pasquet +# This file is part of Slixmpp. +# See the file LICENSE for copying permission. + +from slixmpp.xmlstream import StanzaBase + + +class Handshake(StanzaBase): + + """ + Jabber Component protocol handshake + """ + namespace = 'jabber:component:accept' + name = 'handshake' + interfaces = {'value'} + + def set_value(self, value: str): + self.xml.text = value + + def get_value(self) -> str: + return self.xml.text + + def del_value(self): + self.xml.text = '' From d4e1b68534203f5da1d1adbfb4b5cd2efa6694a8 Mon Sep 17 00:00:00 2001 From: mathieui Date: Sun, 18 Apr 2021 21:16:03 +0200 Subject: [PATCH 2/5] Allow handshake in stream.send() when not connected yet fix #3464 --- slixmpp/xmlstream/xmlstream.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/slixmpp/xmlstream/xmlstream.py b/slixmpp/xmlstream/xmlstream.py index 9fd21c58..4cb51b94 100644 --- a/slixmpp/xmlstream/xmlstream.py +++ b/slixmpp/xmlstream/xmlstream.py @@ -1140,9 +1140,12 @@ class XMLStream(asyncio.BaseProtocol): if not self._always_send_everything and not self._session_started: # Avoid circular imports from slixmpp.stanza.rootstanza import RootStanza - from slixmpp.stanza import Iq - is_bind = isinstance(data, Iq) and data.get_plugin('bind', check=True) - if isinstance(data, (RootStanza, str)) and not is_bind: + from slixmpp.stanza import Iq, Handshake + passthrough = ( + (isinstance(data, Iq) and data.get_plugin('bind', check=True)) + or isinstance(data, Handshake) + ) + if isinstance(data, (RootStanza, str)) and not passthrough: self.__queued_stanzas.append(data) log.debug('NOT SENT: %s %s', type(data), data) return From 99c6fc923a06c1d73297e09d26396b0548a86b05 Mon Sep 17 00:00:00 2001 From: mathieui Date: Sun, 18 Apr 2021 23:01:01 +0200 Subject: [PATCH 3/5] itests: do not compare strings, compare xml --- itests/test_privatestorage.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/itests/test_privatestorage.py b/itests/test_privatestorage.py index fd7f3f1b..55fa941b 100644 --- a/itests/test_privatestorage.py +++ b/itests/test_privatestorage.py @@ -22,7 +22,8 @@ class TestPrivateStorage(SlixIntegration): el, ) result = await self.clients[0]['xep_0049'].retrieve('bookmarks') - self.assertEqual(str(result['private']['bookmarks']), str(el)) + + self.assertEqual(result['private']['bookmarks'], el) # Purge bookmarks await self.clients[0]['xep_0049'].store( From eee185ff9087267c84fb1a5eec2b573db2c29a92 Mon Sep 17 00:00:00 2001 From: mathieui Date: Sun, 18 Apr 2021 23:01:36 +0200 Subject: [PATCH 4/5] stanza: rework the .append() and __eq__ methods This was very much broken on plugin iterables and other reasons. --- slixmpp/xmlstream/stanzabase.py | 27 +++++++++++++++++---------- 1 file changed, 17 insertions(+), 10 deletions(-) diff --git a/slixmpp/xmlstream/stanzabase.py b/slixmpp/xmlstream/stanzabase.py index 207ae588..7679f73a 100644 --- a/slixmpp/xmlstream/stanzabase.py +++ b/slixmpp/xmlstream/stanzabase.py @@ -487,7 +487,7 @@ class ElementBase(object): else: return None if check else self.init_plugin(name, lang) - def init_plugin(self, attrib, lang=None, existing_xml=None, reuse=True): + def init_plugin(self, attrib, lang=None, existing_xml=None, element=None, reuse=True): """Enable and initialize a stanza plugin. :param string attrib: The :attr:`plugin_attrib` value of the @@ -504,7 +504,10 @@ class ElementBase(object): if reuse and (attrib, lang) in self.plugins: return self.plugins[(attrib, lang)] - plugin = plugin_class(parent=self, xml=existing_xml) + if element is not None: + plugin = element + else: + plugin = plugin_class(parent=self, xml=existing_xml) if plugin.is_extension: self.plugins[(attrib, None)] = plugin @@ -1172,14 +1175,18 @@ class ElementBase(object): else: raise TypeError self.xml.append(item.xml) - self.iterables.append(item) - if item.__class__ in self.plugin_iterables: - if item.__class__.plugin_multi_attrib: - self.init_plugin(item.__class__.plugin_multi_attrib) - elif item.__class__ == self.plugin_tag_map.get(item.tag_name(), None): + if item.__class__ == self.plugin_tag_map.get(item.tag_name(), None): self.init_plugin(item.plugin_attrib, existing_xml=item.xml, + element=item, reuse=False) + elif item.__class__ in self.plugin_iterables: + self.iterables.append(item) + if item.__class__.plugin_multi_attrib: + self.init_plugin(item.__class__.plugin_multi_attrib) + else: + self.iterables.append(item) + return self def appendxml(self, xml): @@ -1269,14 +1276,14 @@ class ElementBase(object): # Check that this stanza is a superset of the other stanza. values = self.values + other_values = other.values for key in other.keys(): - if key not in values or values[key] != other[key]: + if key not in values or values.get(key) != other_values.get(key): return False # Check that the other stanza is a superset of this stanza. - values = other.values for key in self.keys(): - if key not in values or values[key] != self[key]: + if key not in values or other_values.get(key) != values.get(key): return False # Both stanzas are supersets of each other, therefore they From 768089d45719aecc6a18606b2e9b30d80074a3ac Mon Sep 17 00:00:00 2001 From: mathieui Date: Mon, 19 Apr 2021 19:42:32 +0200 Subject: [PATCH 5/5] tests: fix bogus test case stanza.match worked accidentally, but the two elements used in that test actually conflict with each other (not overriding the plugin_attrib, which makes it then default to 'plugin', leading to shenanigans). --- tests/test_stanza_element.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_stanza_element.py b/tests/test_stanza_element.py index 8ebc9b23..1172e885 100644 --- a/tests/test_stanza_element.py +++ b/tests/test_stanza_element.py @@ -456,6 +456,7 @@ class TestElementBase(SlixTest): class TestSubStanza(ElementBase): name = "sub" + plugin_attrib = name namespace = "baz" interfaces = {'attrib'}