# HG changeset patch # User Goffi # Date 1686259967 -7200 # Node ID b338c31d525179734022c488e06a3b732f96c135 # Parent de7e468e2d44f456fc19cfa9a26b4295c48d8709 browser: integrate the `jid` module diff -r de7e468e2d44 -r b338c31d5251 libervia/web/pages/_browser/jid.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libervia/web/pages/_browser/jid.py Thu Jun 08 23:32:47 2023 +0200 @@ -0,0 +1,106 @@ +#!/usr/bin/env python3 + +# Libervia XMPP +# Copyright (C) 2009-2023 Jérôme Poisson (goffi@goffi.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 . + +# XXX: this is the same `jid` class as in `libervia.frontends.tools`, but typing hints +# have been removed as they are a bit slow to load with Brython, and this class is used +# a lot. + + +class JID(str): + """This class helps manage JID (@/)""" + + def __new__(cls, jid_str: str) -> "JID": + return str.__new__(cls, cls._normalize(jid_str)) + + def __init__(self, jid_str: str): + self._node, self._domain, self._resource = self._parse() + + @staticmethod + def _normalize(jid_str: str) -> str: + """Naive normalization before instantiating and parsing the JID""" + if not jid_str: + return jid_str + tokens = jid_str.split("/") + tokens[0] = tokens[0].lower() # force node and domain to lower-case + return "/".join(tokens) + + def _parse(self): + """Find node, domain, and resource from JID""" + node_end = self.find("@") + if node_end < 0: + node_end = 0 + domain_end = self.find("/") + if domain_end == 0: + raise ValueError("a jid can't start with '/'") + if domain_end == -1: + domain_end = len(self) + node = self[:node_end] or None + domain = self[(node_end + 1) if node_end else 0 : domain_end] + resource = self[domain_end + 1 :] or None + return node, domain, resource + + @property + def node(self): + return self._node + + @property + def local(self): + return self._node + + @property + def domain(self) -> str: + return self._domain + + @property + def resource(self): + return self._resource + + @property + def bare(self) -> "JID": + if not self.node: + return JID(self.domain) + return JID(f"{self.node}@{self.domain}") + + def change_resource(self, resource: str) -> "JID": + """Build a new JID with the same node and domain but a different resource. + + @param resource: The new resource for the JID. + @return: A new JID instance with the updated resource. + """ + return JID(f"{self.bare}/{resource}") + + def is_valid(self) -> bool: + """ + @return: True if the JID is XMPP compliant + """ + # Simple check for domain part + if not self.domain or self.domain.startswith(".") or self.domain.endswith("."): + return False + if ".." in self.domain: + return False + return True + + +def new_resource(entity: JID, resource: str) -> JID: + """Build a new JID from the given entity and resource. + + @param entity: original JID + @param resource: new resource + @return: a new JID instance + """ + return entity.change_resource(resource)