From 60df4ef7aa6e17da329777f9dc66f9bc8ebbe901 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Wed, 16 Mar 2022 16:12:20 +0100 Subject: [PATCH 1/9] xep_0045: Require JID when setting outcast affiliation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Found out when reading poezio/poezio#3536. “An admin or owner can ban one or more users from a room. The ban MUST be performed based on the occupant's bare JID.” Signed-off-by: Maxime “pep” Buquet --- slixmpp/plugins/xep_0045/muc.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/slixmpp/plugins/xep_0045/muc.py b/slixmpp/plugins/xep_0045/muc.py index e5971bee..90cb73d7 100644 --- a/slixmpp/plugins/xep_0045/muc.py +++ b/slixmpp/plugins/xep_0045/muc.py @@ -493,6 +493,8 @@ class XEP_0045(BasePlugin): """ if affiliation not in AFFILIATIONS: raise ValueError('%s is not a valid affiliation' % affiliation) + if affiliation == 'outcast' and not jid: + raise ValueError('Outcast affiliation requires a using a jid') if not any((jid, nick)): raise ValueError('One of jid or nick must be set') iq = self.xmpp.make_iq_set(ito=room, ifrom=ifrom) From 2b45c22fcba5f23813379b4ffb6c0ff3db57cb62 Mon Sep 17 00:00:00 2001 From: Nicoco K Date: Thu, 19 May 2022 14:40:45 +0200 Subject: [PATCH 2/9] Add xep_0356 to plugins.__all__ --- slixmpp/plugins/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/slixmpp/plugins/__init__.py b/slixmpp/plugins/__init__.py index 55627113..cc98255e 100644 --- a/slixmpp/plugins/__init__.py +++ b/slixmpp/plugins/__init__.py @@ -1,4 +1,3 @@ - # Slixmpp: The Slick XMPP Library # Copyright (C) 2010 Nathanael C. Fritz # This file is part of Slixmpp. @@ -93,6 +92,7 @@ __all__ = [ 'xep_0335', # JSON Containers 'xep_0352', # Client State Indication 'xep_0353', # Jingle Message Initiation + 'xep_0356', # Privileged entity 'xep_0359', # Unique and Stable Stanza IDs 'xep_0363', # HTTP File Upload 'xep_0369', # MIX-CORE From a1a5f3984dbb44163d3b107d502e010793b56cff Mon Sep 17 00:00:00 2001 From: Nicolas Cedilnik Date: Thu, 9 Jun 2022 16:45:36 +0200 Subject: [PATCH 3/9] XEP-0356: namespace version bump --- slixmpp/plugins/xep_0356/stanza.py | 11 +++++++---- tests/test_stanza_xep_0356.py | 2 +- tests/test_stream_xep_0356.py | 4 ++-- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/slixmpp/plugins/xep_0356/stanza.py b/slixmpp/plugins/xep_0356/stanza.py index ef01ee3e..46f1523a 100644 --- a/slixmpp/plugins/xep_0356/stanza.py +++ b/slixmpp/plugins/xep_0356/stanza.py @@ -7,7 +7,7 @@ from slixmpp.plugins.xep_0297 import Forwarded class Privilege(ElementBase): - namespace = "urn:xmpp:privilege:1" + namespace = "urn:xmpp:privilege:2" name = "privilege" plugin_attrib = "privilege" @@ -24,7 +24,10 @@ class Privilege(ElementBase): def presence(self): return self.permission("presence") - + + def iq(self): + return self.permission("iq") + def add_perm(self, access, type): # This should only be needed for servers, so maybe out of scope for slixmpp perm = Perm() @@ -34,7 +37,7 @@ class Privilege(ElementBase): class Perm(ElementBase): - namespace = "urn:xmpp:privilege:1" + namespace = "urn:xmpp:privilege:2" name = "perm" plugin_attrib = "perm" plugin_multi_attrib = "perms" @@ -44,4 +47,4 @@ class Perm(ElementBase): def register(): register_stanza_plugin(Message, Privilege) register_stanza_plugin(Privilege, Forwarded) - register_stanza_plugin(Privilege, Perm, iterable=True) \ No newline at end of file + register_stanza_plugin(Privilege, Perm, iterable=True) diff --git a/tests/test_stanza_xep_0356.py b/tests/test_stanza_xep_0356.py index ef116db2..cf14ccba 100644 --- a/tests/test_stanza_xep_0356.py +++ b/tests/test_stanza_xep_0356.py @@ -13,7 +13,7 @@ class TestPermissions(SlixTest): def testAdvertisePermission(self): xmlstring = """ - + diff --git a/tests/test_stream_xep_0356.py b/tests/test_stream_xep_0356.py index 2949daad..e2ce9569 100644 --- a/tests/test_stream_xep_0356.py +++ b/tests/test_stream_xep_0356.py @@ -31,7 +31,7 @@ class TestPermissions(SlixTest): self.recv( """ - + @@ -95,7 +95,7 @@ class TestPermissions(SlixTest): def testMakeOutgoingMessage(self): xmlstring = """ - + I do not hate you From b44ab17c8f2243a065d80755ce0492bfabb9b29d Mon Sep 17 00:00:00 2001 From: Georg Lukas Date: Wed, 22 Jun 2022 11:39:44 +0200 Subject: [PATCH 4/9] Fix delayed reconnect after DNS failure The XML stream will re-schedule a reconnect on socket errors, except for DNS failures. If a user has no uplink connection, then DNS will also fail, preventing an automatic reconnection. This patch consolidates the two code paths and sets a maximum back-off time of 5min (300s). --- slixmpp/xmlstream/xmlstream.py | 23 ++++++++++++++++------- 1 file changed, 16 insertions(+), 7 deletions(-) diff --git a/slixmpp/xmlstream/xmlstream.py b/slixmpp/xmlstream/xmlstream.py index 18143f87..06d48f5c 100644 --- a/slixmpp/xmlstream/xmlstream.py +++ b/slixmpp/xmlstream/xmlstream.py @@ -493,16 +493,11 @@ class XMLStream(asyncio.BaseProtocol): except Socket.gaierror as e: self.event('connection_failed', 'No DNS record available for %s' % self.default_domain) + self.reschedule_connection_attempt() except OSError as e: log.debug('Connection failed: %s', e) self.event("connection_failed", e) - if self._current_connection_attempt is None: - return - self._connect_loop_wait = self._connect_loop_wait * 2 + 1 - self._current_connection_attempt = asyncio.ensure_future( - self._connect_routine(), - loop=self.loop, - ) + self.reschedule_connection_attempt() def process(self, *, forever: bool = True, timeout: Optional[int] = None) -> None: """Process all the available XMPP events (receiving or sending data on the @@ -638,6 +633,20 @@ class XMLStream(asyncio.BaseProtocol): self._set_disconnected_future() self.event("disconnected", self.disconnect_reason or exception) + def reschedule_connection_attempt(self) -> None: + """ + Increase the exponential back-off and initate another background + _connect_routine call to connect to the server. + """ + # abort if there is no ongoing connection attempt + if self._current_connection_attempt is None: + return + self._connect_loop_wait = min(300, self._connect_loop_wait * 2 + 1) + self._current_connection_attempt = asyncio.ensure_future( + self._connect_routine(), + loop=self.loop, + ) + def cancel_connection_attempt(self) -> None: """ Immediately cancel the current create_connection() Future. From 90e79af18a1115b389d13f143e9d36984b2e02a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maxime=20=E2=80=9Cpep=E2=80=9D=20Buquet?= Date: Mon, 11 Jul 2022 14:46:00 +0200 Subject: [PATCH 5/9] xmlstream: load default CA store by default MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Maxime “pep” Buquet --- slixmpp/xmlstream/xmlstream.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/slixmpp/xmlstream/xmlstream.py b/slixmpp/xmlstream/xmlstream.py index 18143f87..426d5472 100644 --- a/slixmpp/xmlstream/xmlstream.py +++ b/slixmpp/xmlstream/xmlstream.py @@ -800,6 +800,8 @@ class XMLStream(asyncio.BaseProtocol): self.ssl_context.verify_mode = ssl.CERT_REQUIRED self.ssl_context.load_verify_locations(cafile=ca_cert) + else: + self.ssl_context.set_default_verify_paths() return self.ssl_context From fc63768cfc5bdd98c6e57a027b7006b6cbcb1366 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 12 Jul 2022 12:46:50 +0200 Subject: [PATCH 6/9] XEP-0082: Move from mini_dateutil to datetime MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Since datetime got merged into Python (probably around py3k), it’s now usable for all of our needs and so we can do away with the old fallback. --- slixmpp/plugins/xep_0082.py | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/slixmpp/plugins/xep_0082.py b/slixmpp/plugins/xep_0082.py index e8050286..0cdee465 100644 --- a/slixmpp/plugins/xep_0082.py +++ b/slixmpp/plugins/xep_0082.py @@ -6,7 +6,6 @@ import datetime as dt from slixmpp.plugins import BasePlugin, register_plugin -from slixmpp.thirdparty import tzutc, tzoffset, parse_iso # ===================================================================== @@ -21,7 +20,10 @@ def parse(time_str): Arguments: time_str -- A formatted timestamp string. """ - return parse_iso(time_str) + try: + return dt.datetime.strptime(time_str, '%Y-%m-%dT%H:%M:%S.%f%z') + except ValueError: + return dt.datetime.strptime(time_str, '%Y-%m-%dT%H:%M:%S%z') def format_date(time_obj): @@ -52,7 +54,7 @@ def format_time(time_obj): if isinstance(time_obj, dt.datetime): time_obj = time_obj.timetz() timestamp = time_obj.isoformat() - if time_obj.tzinfo == tzutc(): + if time_obj.tzinfo == dt.timezone.utc: timestamp = timestamp[:-6] return '%sZ' % timestamp return timestamp @@ -69,7 +71,7 @@ def format_datetime(time_obj): time_obj -- A datetime object. """ timestamp = time_obj.isoformat('T') - if time_obj.tzinfo == tzutc(): + if time_obj.tzinfo == dt.timezone.utc: timestamp = timestamp[:-6] return '%sZ' % timestamp return timestamp @@ -128,9 +130,9 @@ def time(hour=None, min=None, sec=None, micro=None, offset=None, obj=False): if micro is None: micro = now.microsecond if offset in (None, 0): - offset = tzutc() + offset = dt.timezone.utc elif not isinstance(offset, dt.tzinfo): - offset = tzoffset(None, offset) + offset = dt.timezone(dt.timedelta(seconds=offset)) value = dt.time(hour, min, sec, micro, offset) if obj: return value @@ -175,9 +177,9 @@ def datetime(year=None, month=None, day=None, hour=None, if micro is None: micro = now.microsecond if offset in (None, 0): - offset = tzutc() + offset = dt.timezone.utc elif not isinstance(offset, dt.tzinfo): - offset = tzoffset(None, offset) + offset = dt.timezone(dt.timedelta(seconds=offset)) value = dt.datetime(year, month, day, hour, min, sec, micro, offset) From 78a5f792400feeccac91700509d9854afcfe1f90 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 12 Jul 2022 12:54:35 +0200 Subject: [PATCH 7/9] XEP-0202: Remove usage of mini_dateutil Like the previous commit, we now use the builtin datetime module always. --- slixmpp/plugins/xep_0202/stanza.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/slixmpp/plugins/xep_0202/stanza.py b/slixmpp/plugins/xep_0202/stanza.py index 7d09de50..faa230f9 100644 --- a/slixmpp/plugins/xep_0202/stanza.py +++ b/slixmpp/plugins/xep_0202/stanza.py @@ -8,7 +8,6 @@ import datetime as dt from slixmpp.xmlstream import ElementBase from slixmpp.plugins import xep_0082 -from slixmpp.thirdparty import tzutc, tzoffset class EntityTime(ElementBase): @@ -87,7 +86,7 @@ class EntityTime(ElementBase): seconds (positive or negative) to offset. """ time = xep_0082.time(offset=value) - if xep_0082.parse(time).tzinfo == tzutc(): + if xep_0082.parse(time).tzinfo == dt.timezone.utc: self._set_sub_text('tzo', 'Z') else: self._set_sub_text('tzo', time[-6:]) @@ -111,6 +110,6 @@ class EntityTime(ElementBase): date = value if not isinstance(value, dt.datetime): date = xep_0082.parse(value) - date = date.astimezone(tzutc()) + date = date.astimezone(dt.timezone.utc) value = xep_0082.format_datetime(date) self._set_sub_text('utc', value) From 108a25653793d76e6ab564b5c066a056cbc58590 Mon Sep 17 00:00:00 2001 From: Emmanuel Gil Peyrot Date: Tue, 12 Jul 2022 12:55:16 +0200 Subject: [PATCH 8/9] thirdparty: Remove the mini_dateutil module The builtin datetime module already provides the same features, there is no need to carry that code any longer. --- slixmpp/thirdparty/__init__.py | 1 - slixmpp/thirdparty/mini_dateutil.py | 273 ---------------------------- 2 files changed, 274 deletions(-) delete mode 100644 slixmpp/thirdparty/mini_dateutil.py diff --git a/slixmpp/thirdparty/__init__.py b/slixmpp/thirdparty/__init__.py index d950f4f9..216a7b79 100644 --- a/slixmpp/thirdparty/__init__.py +++ b/slixmpp/thirdparty/__init__.py @@ -3,5 +3,4 @@ try: except: from slixmpp.thirdparty.gnupg import GPG -from slixmpp.thirdparty.mini_dateutil import tzutc, tzoffset, parse_iso from slixmpp.thirdparty.orderedset import OrderedSet diff --git a/slixmpp/thirdparty/mini_dateutil.py b/slixmpp/thirdparty/mini_dateutil.py deleted file mode 100644 index 882a531f..00000000 --- a/slixmpp/thirdparty/mini_dateutil.py +++ /dev/null @@ -1,273 +0,0 @@ -# This module is a very stripped down version of the dateutil -# package for when dateutil has not been installed. As a replacement -# for dateutil.parser.parse, the parsing methods from -# http://blog.mfabrik.com/2008/06/30/relativity-of-time-shortcomings-in-python-datetime-and-workaround/ - -#As such, the following copyrights and licenses applies: - - -# dateutil - Extensions to the standard python 2.3+ datetime module. -# -# Copyright (c) 2003-2011 - Gustavo Niemeyer -# -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# -# * Redistributions of source code must retain the above copyright notice, -# this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright notice, -# this list of conditions and the following disclaimer in the documentation -# and/or other materials provided with the distribution. -# * Neither the name of the copyright holder nor the names of its -# contributors may be used to endorse or promote products derived from -# this software without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR -# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, -# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, -# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR -# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF -# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING -# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - -# fixed_dateime -# -# Copyright (c) 2008, Red Innovation Ltd., Finland -# All rights reserved. -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions are met: -# * Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# * Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# * Neither the name of Red Innovation nor the names of its contributors -# may be used to endorse or promote products derived from this software -# without specific prior written permission. -# -# THIS SOFTWARE IS PROVIDED BY RED INNOVATION ``AS IS'' AND ANY -# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED -# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE -# DISCLAIMED. IN NO EVENT SHALL RED INNOVATION BE LIABLE FOR ANY -# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES -# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND -# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS -# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - - -import re -import math -import datetime - - -ZERO = datetime.timedelta(0) - - -try: - from dateutil.parser import parse as parse_iso - from dateutil.tz import tzoffset, tzutc -except: - # As a stopgap, define the two timezones here based - # on the dateutil code. - - class tzutc(datetime.tzinfo): - - def utcoffset(self, dt): - return ZERO - - def dst(self, dt): - return ZERO - - def tzname(self, dt): - return "UTC" - - def __eq__(self, other): - return (isinstance(other, tzutc) or - (isinstance(other, tzoffset) and other._offset == ZERO)) - - def __ne__(self, other): - return not self.__eq__(other) - - def __repr__(self): - return "%s()" % self.__class__.__name__ - - __reduce__ = object.__reduce__ - - class tzoffset(datetime.tzinfo): - - def __init__(self, name, offset): - self._name = name - self._offset = datetime.timedelta(minutes=offset) - - def utcoffset(self, dt): - return self._offset - - def dst(self, dt): - return ZERO - - def tzname(self, dt): - return self._name - - def __eq__(self, other): - return (isinstance(other, tzoffset) and - self._offset == other._offset) - - def __ne__(self, other): - return not self.__eq__(other) - - def __repr__(self): - return "%s(%s, %s)" % (self.__class__.__name__, - repr(self._name), - self._offset.days*86400+self._offset.seconds) - - __reduce__ = object.__reduce__ - - - _fixed_offset_tzs = { } - UTC = tzutc() - - def _get_fixed_offset_tz(offsetmins): - """For internal use only: Returns a tzinfo with - the given fixed offset. This creates only one instance - for each offset; the zones are kept in a dictionary""" - - if offsetmins == 0: - return UTC - - if not offsetmins in _fixed_offset_tzs: - if offsetmins < 0: - sign = '-' - absoff = -offsetmins - else: - sign = '+' - absoff = offsetmins - - name = "UTC%s%02d:%02d" % (sign, int(absoff / 60), absoff % 60) - inst = tzoffset(name,offsetmins) - _fixed_offset_tzs[offsetmins] = inst - - return _fixed_offset_tzs[offsetmins] - - - _iso8601_parser = re.compile(r""" - ^ - (?P [0-9]{4})?(?P-?)? - (?P[0-9]{2})?(?P=ymdsep)? - (?P [0-9]{2})? - - (?P