diff -Nru slixmpp-1.8.2/debian/changelog slixmpp-1.8.3/debian/changelog --- slixmpp-1.8.2/debian/changelog 2022-11-01 07:50:56.000000000 +0000 +++ slixmpp-1.8.3/debian/changelog 2022-11-12 21:28:49.000000000 +0000 @@ -1,3 +1,10 @@ +slixmpp (1.8.3-1) unstable; urgency=medium + + * New upstream release. + * Drop latest patch, applied upstream. + + -- Martin Sat, 12 Nov 2022 21:28:49 +0000 + slixmpp (1.8.2-2) unstable; urgency=medium * Add patch to fix emoji 2.0.0 compatibility (Closes: #1022492) diff -Nru slixmpp-1.8.2/debian/patches/fix-emoji-2.0.0-compatibility.patch slixmpp-1.8.3/debian/patches/fix-emoji-2.0.0-compatibility.patch --- slixmpp-1.8.2/debian/patches/fix-emoji-2.0.0-compatibility.patch 2022-11-01 07:50:56.000000000 +0000 +++ slixmpp-1.8.3/debian/patches/fix-emoji-2.0.0-compatibility.patch 1970-01-01 00:00:00.000000000 +0000 @@ -1,20 +0,0 @@ -Description: fix emoji 2.0.0 compatibility -Author: nicoco -Origin: upstream -Applied-Upstream: fbdff30ddabefc6beff90205b870194f3158d3f5 -Last-Update: 2022-11-01 ---- -This patch header follows DEP-3: http://dep.debian.net/deps/dep3/ ---- a/slixmpp/plugins/xep_0444/stanza.py -+++ b/slixmpp/plugins/xep_0444/stanza.py -@@ -6,9 +6,7 @@ - from typing import Set, Iterable - from slixmpp.xmlstream import ElementBase - try: -- from emoji import UNICODE_EMOJI -- if UNICODE_EMOJI.get('en'): -- UNICODE_EMOJI = UNICODE_EMOJI['en'] -+ from emoji import EMOJI_DATA as UNICODE_EMOJI - except ImportError: - UNICODE_EMOJI = None - diff -Nru slixmpp-1.8.2/debian/patches/series slixmpp-1.8.3/debian/patches/series --- slixmpp-1.8.2/debian/patches/series 2022-11-01 07:50:56.000000000 +0000 +++ slixmpp-1.8.3/debian/patches/series 2022-11-12 21:28:49.000000000 +0000 @@ -1,3 +1,2 @@ -fix-emoji-2.0.0-compatibility.patch increase-test-timeout.patch replace-furo-theme.patch diff -Nru slixmpp-1.8.2/doap.xml slixmpp-1.8.3/doap.xml --- slixmpp-1.8.2/doap.xml 2022-04-06 20:44:40.000000000 +0000 +++ slixmpp-1.8.3/doap.xml 2022-11-12 20:39:50.000000000 +0000 @@ -457,6 +457,14 @@ + + complete + 1.2 + 1.0 + + + + complete 1.4.0 @@ -1041,5 +1049,12 @@ + + + 1.8.3 + 2022-11-12 + + + diff -Nru slixmpp-1.8.2/docs/api/plugins/xep_0055.rst slixmpp-1.8.3/docs/api/plugins/xep_0055.rst --- slixmpp-1.8.2/docs/api/plugins/xep_0055.rst 1970-01-01 00:00:00.000000000 +0000 +++ slixmpp-1.8.3/docs/api/plugins/xep_0055.rst 2022-11-12 20:39:50.000000000 +0000 @@ -0,0 +1,18 @@ + +XEP-0055: Jabber search +======================= + +.. module:: slixmpp.plugins.xep_0055 + +.. autoclass:: XEP_0055 + :members: + :exclude-members: session_bind, plugin_init, plugin_end + + +Stanza elements +--------------- + +.. automodule:: slixmpp.plugins.xep_0055.stanza + :members: + :undoc-members: + diff -Nru slixmpp-1.8.2/slixmpp/basexmpp.py slixmpp-1.8.3/slixmpp/basexmpp.py --- slixmpp-1.8.2/slixmpp/basexmpp.py 2022-04-06 20:44:40.000000000 +0000 +++ slixmpp-1.8.3/slixmpp/basexmpp.py 2022-11-12 20:39:50.000000000 +0000 @@ -140,7 +140,7 @@ self.use_presence_ids = True #: XEP-0359 tag that gets added to stanzas. - self.use_origin_id = True + self.use_origin_id = False #: The API registry is a way to process callbacks based on #: JID+node combinations. Each callback in the registry is diff -Nru slixmpp-1.8.2/slixmpp/__init__.py slixmpp-1.8.3/slixmpp/__init__.py --- slixmpp-1.8.2/slixmpp/__init__.py 2022-04-06 20:44:40.000000000 +0000 +++ slixmpp-1.8.3/slixmpp/__init__.py 2022-11-12 20:39:50.000000000 +0000 @@ -5,7 +5,6 @@ # See the file LICENSE for copying permission. import logging from os import getenv -logging.getLogger(__name__).addHandler(logging.NullHandler()) # Use defusedxml if wanted # Since enabling it can have adverse consequences for the programs using diff -Nru slixmpp-1.8.2/slixmpp/jid.py slixmpp-1.8.3/slixmpp/jid.py --- slixmpp-1.8.2/slixmpp/jid.py 2022-04-06 20:44:40.000000000 +0000 +++ slixmpp-1.8.3/slixmpp/jid.py 2022-11-12 20:39:50.000000000 +0000 @@ -368,7 +368,7 @@ return self._node @node.setter - def node(self, value: str): + def node(self, value: Optional[str]): self._node = _validate_node(value) self._update_bare_full() @@ -386,7 +386,7 @@ return self._resource @resource.setter - def resource(self, value: str): + def resource(self, value: Optional[str]): self._resource = _validate_resource(value) self._update_bare_full() diff -Nru slixmpp-1.8.2/slixmpp/plugins/__init__.py slixmpp-1.8.3/slixmpp/plugins/__init__.py --- slixmpp-1.8.2/slixmpp/plugins/__init__.py 2022-04-06 20:44:40.000000000 +0000 +++ slixmpp-1.8.3/slixmpp/plugins/__init__.py 2022-11-12 20:39:50.000000000 +0000 @@ -1,4 +1,3 @@ - # Slixmpp: The Slick XMPP Library # Copyright (C) 2010 Nathanael C. Fritz # This file is part of Slixmpp. @@ -24,6 +23,7 @@ 'xep_0049', # Private XML Storage 'xep_0050', # Ad-hoc Commands 'xep_0054', # vcard-temp + 'xep_0055', # Jabber Search 'xep_0059', # Result Set Management 'xep_0060', # Pubsub (Client) 'xep_0065', # SOCKS5 Bytestreams @@ -93,6 +93,7 @@ '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 diff -Nru slixmpp-1.8.2/slixmpp/plugins/xep_0030/disco.py slixmpp-1.8.3/slixmpp/plugins/xep_0030/disco.py --- slixmpp-1.8.2/slixmpp/plugins/xep_0030/disco.py 2022-04-06 20:44:40.000000000 +0000 +++ slixmpp-1.8.3/slixmpp/plugins/xep_0030/disco.py 2022-11-12 20:39:50.000000000 +0000 @@ -385,6 +385,8 @@ local = True ifrom = kwargs.pop('ifrom', None) + if self.xmpp.is_component and ifrom is None: + ifrom = self.xmpp.boundjid if local: log.debug("Looking up local disco#info data " "for %s, node %s.", jid, node) diff -Nru slixmpp-1.8.2/slixmpp/plugins/xep_0045/muc.py slixmpp-1.8.3/slixmpp/plugins/xep_0045/muc.py --- slixmpp-1.8.2/slixmpp/plugins/xep_0045/muc.py 2022-04-06 20:44:40.000000000 +0000 +++ slixmpp-1.8.3/slixmpp/plugins/xep_0045/muc.py 2022-11-12 20:39:50.000000000 +0000 @@ -493,6 +493,8 @@ """ 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) diff -Nru slixmpp-1.8.2/slixmpp/plugins/xep_0050/adhoc.py slixmpp-1.8.3/slixmpp/plugins/xep_0050/adhoc.py --- slixmpp-1.8.2/slixmpp/plugins/xep_0050/adhoc.py 2022-04-06 20:44:40.000000000 +0000 +++ slixmpp-1.8.3/slixmpp/plugins/xep_0050/adhoc.py 2022-11-12 20:39:50.000000000 +0000 @@ -620,6 +620,8 @@ async def _await_if_needed(handler, *args): + if handler is None: + raise XMPPError("bad-request", text="The command is completed") if asyncio.iscoroutinefunction(handler): log.debug(f"%s is async", handler) return await handler(*args) diff -Nru slixmpp-1.8.2/slixmpp/plugins/xep_0055/__init__.py slixmpp-1.8.3/slixmpp/plugins/xep_0055/__init__.py --- slixmpp-1.8.2/slixmpp/plugins/xep_0055/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ slixmpp-1.8.3/slixmpp/plugins/xep_0055/__init__.py 2022-11-12 20:39:50.000000000 +0000 @@ -0,0 +1,6 @@ +from slixmpp.plugins.base import register_plugin + +from .search import XEP_0055 + + +register_plugin(XEP_0055) diff -Nru slixmpp-1.8.2/slixmpp/plugins/xep_0055/search.py slixmpp-1.8.3/slixmpp/plugins/xep_0055/search.py --- slixmpp-1.8.2/slixmpp/plugins/xep_0055/search.py 1970-01-01 00:00:00.000000000 +0000 +++ slixmpp-1.8.3/slixmpp/plugins/xep_0055/search.py 2022-11-12 20:39:50.000000000 +0000 @@ -0,0 +1,89 @@ +import logging + +from slixmpp import CoroutineCallback, StanzaPath, Iq, register_stanza_plugin +from slixmpp.plugins import BasePlugin +from slixmpp.xmlstream import StanzaBase + +from . import stanza + + +class XEP_0055(BasePlugin): + """ + XEP-0055: Jabber Search + + The config options are only useful for a "server-side" search feature, + and if the ``provide_search`` option is set to True. + + API + === + + ``search_get_form``: customize the search form content (ie fields) + + ``search_query``: return search results + """ + name = "xep_0055" + description = "XEP-0055: Jabber search" + dependencies = {"xep_0004", "xep_0030"} + stanza = stanza + default_config = { + "form_fields": {"first", "last"}, + "form_instructions": "", + "form_title": "", + "provide_search": True + } + + def plugin_init(self): + register_stanza_plugin(Iq, stanza.Search) + register_stanza_plugin(stanza.Search, self.xmpp["xep_0004"].stanza.Form) + + if self.provide_search: + self.xmpp["xep_0030"].add_feature(stanza.Search.namespace) + self.xmpp.register_handler( + CoroutineCallback( + "search", + StanzaPath("/iq/search"), + self._handle_search, + ) + ) + self.api.register(self._get_form, "search_get_form") + self.api.register(self._get_results, "search_query") + + async def _handle_search(self, iq: StanzaBase): + if iq["search"]["form"].get_values(): + reply = await self.api["search_query"](None, None, iq.get_from(), iq) + reply["search"]["form"]["type"] = "result" + else: + reply = await self.api["search_get_form"](None, None, iq.get_from(), iq) + reply["search"]["form"].add_field( + "FORM_TYPE", value=stanza.Search.namespace, ftype="hidden" + ) + reply.send() + + async def _get_form(self, jid, node, ifrom, iq): + reply = iq.reply() + form = reply["search"]["form"] + form["title"] = self.form_title + form["instructions"] = self.form_instructions + for field in self.form_fields: + form.add_field(field) + return reply + + async def _get_results(self, jid, node, ifrom, iq): + reply = iq.reply() + form = reply["search"]["form"] + form["type"] = "result" + + for field in self.form_fields: + form.add_reported(field) + return reply + + def make_search_iq(self, **kwargs): + iq = self.xmpp.make_iq(itype="set", **kwargs) + iq["search"]["form"].set_type("submit") + iq["search"]["form"].add_field( + "FORM_TYPE", value=stanza.Search.namespace, ftype="hidden" + ) + return iq + + +log = logging.getLogger(__name__) diff -Nru slixmpp-1.8.2/slixmpp/plugins/xep_0055/stanza.py slixmpp-1.8.3/slixmpp/plugins/xep_0055/stanza.py --- slixmpp-1.8.2/slixmpp/plugins/xep_0055/stanza.py 1970-01-01 00:00:00.000000000 +0000 +++ slixmpp-1.8.3/slixmpp/plugins/xep_0055/stanza.py 2022-11-12 20:39:50.000000000 +0000 @@ -0,0 +1,10 @@ +from typing import Set, ClassVar + +from slixmpp.xmlstream import ElementBase + + +class Search(ElementBase): + namespace = "jabber:iq:search" + name = "query" + plugin_attrib = "search" + interfaces: ClassVar[Set[str]] = set() diff -Nru slixmpp-1.8.2/slixmpp/plugins/xep_0082.py slixmpp-1.8.3/slixmpp/plugins/xep_0082.py --- slixmpp-1.8.2/slixmpp/plugins/xep_0082.py 2022-04-06 20:44:40.000000000 +0000 +++ slixmpp-1.8.3/slixmpp/plugins/xep_0082.py 2022-11-12 20:39:50.000000000 +0000 @@ -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 @@ 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 @@ 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 @@ 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 @@ 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 @@ 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) diff -Nru slixmpp-1.8.2/slixmpp/plugins/xep_0084/stanza.py slixmpp-1.8.3/slixmpp/plugins/xep_0084/stanza.py --- slixmpp-1.8.2/slixmpp/plugins/xep_0084/stanza.py 2022-04-06 20:44:40.000000000 +0000 +++ slixmpp-1.8.3/slixmpp/plugins/xep_0084/stanza.py 2022-11-12 20:39:50.000000000 +0000 @@ -80,16 +80,16 @@ self._set_int('bytes', value) def get_height(self) -> int: - self._get_int('height') + return self._get_int('height') def set_height(self, value: int): self._set_int('height', value) def get_width(self) -> int: - self._get_int(self, 'width') + return self._get_int('width') def set_width(self, value: int): - self._set_int('with', value) + self._set_int('width', value) class Pointer(ElementBase): diff -Nru slixmpp-1.8.2/slixmpp/plugins/xep_0115/static.py slixmpp-1.8.3/slixmpp/plugins/xep_0115/static.py --- slixmpp-1.8.2/slixmpp/plugins/xep_0115/static.py 2022-04-06 20:44:40.000000000 +0000 +++ slixmpp-1.8.3/slixmpp/plugins/xep_0115/static.py 2022-11-12 20:39:50.000000000 +0000 @@ -60,7 +60,7 @@ return False if node in (None, ''): - info = self.caps.get_caps(jid) + info = await self.caps.get_caps(jid) if info and feature in info['features']: return True @@ -134,7 +134,7 @@ def get_verstring(self, jid, node, ifrom, data): return self.jid_vers.get(jid, None) - def get_caps(self, jid, node, ifrom, data): + async def get_caps(self, jid, node, ifrom, data): verstring = data.get('verstring', None) if verstring is None: return None diff -Nru slixmpp-1.8.2/slixmpp/plugins/xep_0202/stanza.py slixmpp-1.8.3/slixmpp/plugins/xep_0202/stanza.py --- slixmpp-1.8.2/slixmpp/plugins/xep_0202/stanza.py 2022-04-06 20:44:40.000000000 +0000 +++ slixmpp-1.8.3/slixmpp/plugins/xep_0202/stanza.py 2022-11-12 20:39:50.000000000 +0000 @@ -8,7 +8,6 @@ from slixmpp.xmlstream import ElementBase from slixmpp.plugins import xep_0082 -from slixmpp.thirdparty import tzutc, tzoffset class EntityTime(ElementBase): @@ -87,7 +86,7 @@ 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 @@ 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) diff -Nru slixmpp-1.8.2/slixmpp/plugins/xep_0203/stanza.py slixmpp-1.8.3/slixmpp/plugins/xep_0203/stanza.py --- slixmpp-1.8.2/slixmpp/plugins/xep_0203/stanza.py 2022-04-06 20:44:40.000000000 +0000 +++ slixmpp-1.8.3/slixmpp/plugins/xep_0203/stanza.py 2022-11-12 20:39:50.000000000 +0000 @@ -30,6 +30,10 @@ def set_stamp(self, value): if isinstance(value, dt.datetime): + if value.tzinfo is None: + raise ValueError(f'Datetime provided without timezone information: {value}') + if value.tzinfo != dt.timezone.utc: + value = value.astimezone(dt.timezone.utc) value = xep_0082.format_datetime(value) self._set_attr('stamp', value) diff -Nru slixmpp-1.8.2/slixmpp/plugins/xep_0333/markers.py slixmpp-1.8.3/slixmpp/plugins/xep_0333/markers.py --- slixmpp-1.8.2/slixmpp/plugins/xep_0333/markers.py 2022-04-06 20:44:40.000000000 +0000 +++ slixmpp-1.8.3/slixmpp/plugins/xep_0333/markers.py 2022-11-12 20:39:50.000000000 +0000 @@ -1,4 +1,3 @@ - # slixmpp: The Slick XMPP Library # Copyright (C) 2016 Emmanuel Gil Peyrot # This file is part of slixmpp. @@ -68,11 +67,11 @@ :param JID mto: recipient of the marker :param str id: Identifier of the marked message :param str marker: Marker to send (one of - displayed, retrieved, or acknowledged) + displayed, received, or acknowledged) :param str thread: Message thread :param str mfrom: Use a specific JID to send the message """ - if marker not in ('displayed', 'retrieved', 'acknowledged'): + if marker not in ('displayed', 'received', 'acknowledged'): raise ValueError('Invalid marker: %s' % marker) msg = self.xmpp.make_message(mto=mto, mfrom=mfrom) if thread: diff -Nru slixmpp-1.8.2/slixmpp/plugins/xep_0356/stanza.py slixmpp-1.8.3/slixmpp/plugins/xep_0356/stanza.py --- slixmpp-1.8.2/slixmpp/plugins/xep_0356/stanza.py 2022-04-06 20:44:40.000000000 +0000 +++ slixmpp-1.8.3/slixmpp/plugins/xep_0356/stanza.py 2022-11-12 20:39:50.000000000 +0000 @@ -7,7 +7,7 @@ class Privilege(ElementBase): - namespace = "urn:xmpp:privilege:1" + namespace = "urn:xmpp:privilege:2" name = "privilege" plugin_attrib = "privilege" @@ -24,7 +24,10 @@ 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 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 @@ 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 -Nru slixmpp-1.8.2/slixmpp/plugins/xep_0363/http_upload.py slixmpp-1.8.3/slixmpp/plugins/xep_0363/http_upload.py --- slixmpp-1.8.2/slixmpp/plugins/xep_0363/http_upload.py 2022-04-06 20:44:40.000000000 +0000 +++ slixmpp-1.8.3/slixmpp/plugins/xep_0363/http_upload.py 2022-11-12 20:39:50.000000000 +0000 @@ -101,12 +101,17 @@ :param domain: Domain to disco to find a service. """ + if domain is None and self.xmpp.is_component: + domain = self.xmpp.server_host + results = await self.xmpp['xep_0030'].get_info_from_domain( domain=domain, **iqkwargs ) candidates = [] for info in results: + if not info['disco_info']: + continue for identity in info['disco_info']['identities']: if identity[0] == 'store' and identity[1] == 'file': candidates.append(info) diff -Nru slixmpp-1.8.2/slixmpp/plugins/xep_0444/stanza.py slixmpp-1.8.3/slixmpp/plugins/xep_0444/stanza.py --- slixmpp-1.8.2/slixmpp/plugins/xep_0444/stanza.py 2022-04-06 20:44:40.000000000 +0000 +++ slixmpp-1.8.3/slixmpp/plugins/xep_0444/stanza.py 2022-11-12 20:39:50.000000000 +0000 @@ -6,9 +6,7 @@ from typing import Set, Iterable from slixmpp.xmlstream import ElementBase try: - from emoji import UNICODE_EMOJI - if UNICODE_EMOJI.get('en'): - UNICODE_EMOJI = UNICODE_EMOJI['en'] + from emoji import EMOJI_DATA as UNICODE_EMOJI except ImportError: UNICODE_EMOJI = None diff -Nru slixmpp-1.8.2/slixmpp/plugins/xep_0461/__init__.py slixmpp-1.8.3/slixmpp/plugins/xep_0461/__init__.py --- slixmpp-1.8.2/slixmpp/plugins/xep_0461/__init__.py 1970-01-01 00:00:00.000000000 +0000 +++ slixmpp-1.8.3/slixmpp/plugins/xep_0461/__init__.py 2022-11-12 20:39:50.000000000 +0000 @@ -0,0 +1,6 @@ +from slixmpp.plugins.base import register_plugin + +from .reply import XEP_0461 +from . import stanza + +register_plugin(XEP_0461) diff -Nru slixmpp-1.8.2/slixmpp/plugins/xep_0461/reply.py slixmpp-1.8.3/slixmpp/plugins/xep_0461/reply.py --- slixmpp-1.8.2/slixmpp/plugins/xep_0461/reply.py 1970-01-01 00:00:00.000000000 +0000 +++ slixmpp-1.8.3/slixmpp/plugins/xep_0461/reply.py 2022-11-12 20:39:50.000000000 +0000 @@ -0,0 +1,48 @@ +from slixmpp.plugins import BasePlugin +from slixmpp.types import JidStr +from slixmpp.xmlstream import StanzaBase +from slixmpp.xmlstream.handler import Callback +from slixmpp.xmlstream.matcher import StanzaPath + +from . import stanza + + +class XEP_0461(BasePlugin): + """XEP-0461: Message Replies""" + + name = "xep_0461" + description = "XEP-0461: Message Replies" + + dependencies = {"xep_0030"} + stanza = stanza + namespace = stanza.NS + + def plugin_init(self) -> None: + stanza.register_plugins() + self.xmpp.register_handler( + Callback( + "Message replied to", + StanzaPath("message/reply"), + self._handle_reply_to_message, + ) + ) + + def plugin_end(self): + self.xmpp.plugin["xep_0030"].del_feature(feature=stanza.NS) + + def session_bind(self, jid): + self.xmpp.plugin["xep_0030"].add_feature(feature=stanza.NS) + + def _handle_reply_to_message(self, msg: StanzaBase): + self.xmpp.event("message_reply", msg) + + def send_reply(self, reply_to: JidStr, reply_id: str, **msg_kwargs): + """ + + :param reply_to: Full JID of the quoted author + :param reply_id: ID of the message to reply to + """ + msg = self.xmpp.make_message(**msg_kwargs) + msg["reply"]["to"] = reply_to + msg["reply"]["id"] = reply_id + msg.send() diff -Nru slixmpp-1.8.2/slixmpp/plugins/xep_0461/stanza.py slixmpp-1.8.3/slixmpp/plugins/xep_0461/stanza.py --- slixmpp-1.8.2/slixmpp/plugins/xep_0461/stanza.py 1970-01-01 00:00:00.000000000 +0000 +++ slixmpp-1.8.3/slixmpp/plugins/xep_0461/stanza.py 2022-11-12 20:39:50.000000000 +0000 @@ -0,0 +1,47 @@ +from slixmpp.stanza import Message +from slixmpp.xmlstream import ElementBase, register_stanza_plugin + +NS = "urn:xmpp:reply:0" + + +class Reply(ElementBase): + namespace = NS + name = "reply" + plugin_attrib = "reply" + interfaces = {"id", "to"} + + +class FeatureFallBack(ElementBase): + # should also be a multi attrib + namespace = "urn:xmpp:feature-fallback:0" + name = "fallback" + plugin_attrib = "feature_fallback" + interfaces = {"for"} + + def get_stripped_body(self): + # only works for a single fallback_body attrib + start = self["fallback_body"]["start"] + end = self["fallback_body"]["end"] + body = self.parent()["body"] + try: + start = int(start) + end = int(end) + except ValueError: + return body + else: + return body[:start] + body[end:] + + +class FallBackBody(ElementBase): + # According to https://xmpp.org/extensions/inbox/compatibility-fallback.html + # this should be a multi_attrib *but* since it's a protoXEP, we'll see... + namespace = FeatureFallBack.namespace + name = "body" + plugin_attrib = "fallback_body" + interfaces = {"start", "end"} + + +def register_plugins(): + register_stanza_plugin(Message, Reply) + register_stanza_plugin(Message, FeatureFallBack) + register_stanza_plugin(FeatureFallBack, FallBackBody) diff -Nru slixmpp-1.8.2/slixmpp/stanza/message.py slixmpp-1.8.3/slixmpp/stanza/message.py --- slixmpp-1.8.2/slixmpp/stanza/message.py 2022-04-06 20:44:40.000000000 +0000 +++ slixmpp-1.8.3/slixmpp/stanza/message.py 2022-11-12 20:39:50.000000000 +0000 @@ -64,9 +64,9 @@ if self.stream: use_ids = getattr(self.stream, 'use_message_ids', None) if use_ids: - self['id'] = self.stream.new_id() + self.set_id(self.stream.new_id()) else: - del self['origin_id'] + self.del_origin_id() def get_type(self): """ @@ -96,8 +96,8 @@ self.xml.attrib['id'] = value if self.stream: - use_orig_ids = getattr(self.stream, 'use_origin_id', None) - if not use_orig_ids: + if not getattr(self.stream, 'use_origin_id', False): + self.del_origin_id() return None sub = self.xml.find(ORIGIN_NAME) diff -Nru slixmpp-1.8.2/slixmpp/thirdparty/__init__.py slixmpp-1.8.3/slixmpp/thirdparty/__init__.py --- slixmpp-1.8.2/slixmpp/thirdparty/__init__.py 2022-04-06 20:44:40.000000000 +0000 +++ slixmpp-1.8.3/slixmpp/thirdparty/__init__.py 2022-11-12 20:39:50.000000000 +0000 @@ -3,5 +3,4 @@ except: from slixmpp.thirdparty.gnupg import GPG -from slixmpp.thirdparty.mini_dateutil import tzutc, tzoffset, parse_iso from slixmpp.thirdparty.orderedset import OrderedSet diff -Nru slixmpp-1.8.2/slixmpp/thirdparty/mini_dateutil.py slixmpp-1.8.3/slixmpp/thirdparty/mini_dateutil.py --- slixmpp-1.8.2/slixmpp/thirdparty/mini_dateutil.py 2022-04-06 20:44:40.000000000 +0000 +++ slixmpp-1.8.3/slixmpp/thirdparty/mini_dateutil.py 1970-01-01 00:00:00.000000000 +0000 @@ -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