Mercurial > libervia-backend
diff libervia/frontends/tools/jid.py @ 4074:26b7ed2817da
refactoring: rename `sat_frontends` to `libervia.frontends`
author | Goffi <goffi@goffi.org> |
---|---|
date | Fri, 02 Jun 2023 14:12:38 +0200 |
parents | sat_frontends/tools/jid.py@199473ffe4ea |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/libervia/frontends/tools/jid.py Fri Jun 02 14:12:38 2023 +0200 @@ -0,0 +1,104 @@ +#!/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 <http://www.gnu.org/licenses/>. + +from typing import Optional, Tuple + + +class JID(str): + """This class helps manage JID (<local>@<domain>/<resource>)""" + + 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) -> Tuple[Optional[str], str, Optional[str]]: + """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) -> Optional[str]: + return self._node + + @property + def local(self) -> Optional[str]: + return self._node + + @property + def domain(self) -> str: + return self._domain + + @property + def resource(self) -> Optional[str]: + 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)