Mercurial > libervia-backend
diff libervia/backend/plugins/plugin_xep_0359.py @ 4071:4b842c1fb686
refactoring: renamed `sat` package to `libervia.backend`
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 02 Jun 2023 11:49:51 +0200 |
parents | sat/plugins/plugin_xep_0359.py@524856bd7b19 |
children | 2729d424dee7 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libervia/backend/plugins/plugin_xep_0359.py Fri Jun 02 11:49:51 2023 +0200 @@ -0,0 +1,143 @@ +#!/usr/bin/env python3 + + +# SAT plugin for Message Archive Management (XEP-0359) +# Copyright (C) 2009-2021 Jérôme Poisson (goffi@goffi.org) +# Copyright (C) 2013-2016 Adrien Cossa (souliane@mailoo.org) + +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. + +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +from typing import Optional +import uuid +from zope.interface import implementer +from twisted.words.protocols.jabber import xmlstream +from wokkel import disco +from libervia.backend.core.constants import Const as C +from libervia.backend.core import exceptions +from libervia.backend.core.i18n import _ +from libervia.backend.core.log import getLogger +from twisted.words.xish import domish + +log = getLogger(__name__) + + +PLUGIN_INFO = { + C.PI_NAME: "Unique and Stable Stanza IDs", + C.PI_IMPORT_NAME: "XEP-0359", + C.PI_TYPE: "XEP", + C.PI_PROTOCOLS: ["XEP-0359"], + C.PI_MAIN: "XEP_0359", + C.PI_HANDLER: "yes", + C.PI_DESCRIPTION: _("""Implementation of Unique and Stable Stanza IDs"""), +} + +NS_SID = "urn:xmpp:sid:0" + + +class XEP_0359(object): + + def __init__(self, host): + log.info(_("Unique and Stable Stanza IDs plugin initialization")) + self.host = host + host.register_namespace("stanza_id", NS_SID) + host.trigger.add("message_parse", self._message_parse_trigger) + host.trigger.add("send_message_data", self._send_message_data_trigger) + + def _message_parse_trigger(self, client, message_elt, mess_data): + """Check if message has a stanza-id""" + stanza_id = self.get_stanza_id(message_elt, client.jid.userhostJID()) + if stanza_id is not None: + mess_data['extra']['stanza_id'] = stanza_id + origin_id = self.get_origin_id(message_elt) + if origin_id is not None: + mess_data['extra']['origin_id'] = origin_id + return True + + def _send_message_data_trigger(self, client, mess_data): + origin_id = mess_data["extra"].get("origin_id") + if not origin_id: + origin_id = str(uuid.uuid4()) + mess_data["extra"]["origin_id"] = origin_id + message_elt = mess_data["xml"] + self.add_origin_id(message_elt, origin_id) + + def get_stanza_id(self, element, by): + """Return stanza-id if found in element + + @param element(domish.Element): element to parse + @param by(jid.JID): entity which should have set a stanza-id + @return (unicode, None): stanza-id if found + """ + stanza_id = None + for stanza_elt in element.elements(NS_SID, "stanza-id"): + if stanza_elt.getAttribute("by") == by.full(): + if stanza_id is not None: + # we must not have more than one element (§3 #4) + raise exceptions.DataError( + "More than one corresponding stanza-id found!") + stanza_id = stanza_elt.getAttribute("id") + # we don't break to be sure that there is no more than one element + # with this "by" attribute + + return stanza_id + + def add_stanza_id(self, client, element, stanza_id, by=None): + """Add a <stanza-id/> to a stanza + + @param element(domish.Element): stanza where the <stanza-id/> must be added + @param stanza_id(unicode): id to use + @param by(jid.JID, None): jid to use or None to use client.jid + """ + sid_elt = element.addElement((NS_SID, "stanza-id")) + sid_elt["by"] = client.jid.userhost() if by is None else by.userhost() + sid_elt["id"] = stanza_id + + def get_origin_id(self, element: domish.Element) -> Optional[str]: + """Return origin-id if found in element + + @param element: element to parse + @return: origin-id if found + """ + try: + origin_elt = next(element.elements(NS_SID, "origin-id")) + except StopIteration: + return None + else: + return origin_elt.getAttribute("id") + + def add_origin_id(self, element, origin_id=None): + """Add a <origin-id/> to a stanza + + @param element(domish.Element): stanza where the <origin-id/> must be added + @param origin_id(str): id to use, None to automatically generate + @return (str): origin_id + """ + if origin_id is None: + origin_id = str(uuid.uuid4()) + sid_elt = element.addElement((NS_SID, "origin-id")) + sid_elt["id"] = origin_id + return origin_id + + def get_handler(self, client): + return XEP_0359_handler() + + +@implementer(disco.IDisco) +class XEP_0359_handler(xmlstream.XMPPHandler): + + def getDiscoInfo(self, requestor, target, nodeIdentifier=""): + return [disco.DiscoFeature(NS_SID)] + + def getDiscoItems(self, requestor, target, nodeIdentifier=""): + return []