From 3eb1669bdefb51190f9f6fb98e581a37c3e6a2fd Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Sun, 22 Feb 2026 12:23:59 +0100 Subject: [PATCH 01/24] add new function (common used): parent_path --- radicale/pathutils.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/radicale/pathutils.py b/radicale/pathutils.py index 3193e4a2..09b2206e 100644 --- a/radicale/pathutils.py +++ b/radicale/pathutils.py @@ -293,6 +293,10 @@ def path_to_filesystem(root: str, sane_path: str) -> str: return safe_path +def parent_path(path: str) -> str: + return unstrip_path(posixpath.dirname(strip_path(path)), True) + + class UnsafePathError(ValueError): def __init__(self, path: str) -> None: From be16a4168181f8ff1186a0870897643b80d56d88 Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Sun, 22 Feb 2026 12:24:28 +0100 Subject: [PATCH 02/24] extend copyright --- radicale/pathutils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radicale/pathutils.py b/radicale/pathutils.py index 09b2206e..488dbfc0 100644 --- a/radicale/pathutils.py +++ b/radicale/pathutils.py @@ -2,7 +2,7 @@ # Copyright © 2014 Jean-Marc Martins # Copyright © 2012-2017 Guillaume Ayoub # Copyright © 2017-2022 Unrud -# Copyright © 2025-2025 Peter Bieringer +# Copyright © 2025-2026 Peter Bieringer # # This library is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by From 54e33d8ca25bb3bc712e04112c75b4599cfb6475 Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Sun, 22 Feb 2026 12:27:14 +0100 Subject: [PATCH 03/24] add optional permissions filter --- radicale/app/base.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/radicale/app/base.py b/radicale/app/base.py index aa0af7a2..33a9ddb5 100644 --- a/radicale/app/base.py +++ b/radicale/app/base.py @@ -20,11 +20,12 @@ import logging import posixpath import sys import xml.etree.ElementTree as ET -from typing import Optional +from typing import Optional, Union from radicale import (auth, config, hook, httputils, pathutils, rights, storage, types, utils, web, xmlutils) from radicale.log import logger +from radicale.rights import intersect # HACK: https://github.com/tiran/defusedxml/issues/54 import defusedxml.ElementTree as DefusedET # isort:skip @@ -106,15 +107,19 @@ class Access: permissions: str _rights: rights.BaseRights _parent_permissions: Optional[str] + _permissions_filter: Union[str | None] = None - def __init__(self, rights: rights.BaseRights, user: str, path: str + def __init__(self, rights: rights.BaseRights, user: str, path: str, permissions_filter: Union[str | None] = None ) -> None: self._rights = rights self.user = user self.path = path - self.parent_path = pathutils.unstrip_path( - posixpath.dirname(pathutils.strip_path(path)), True) + self.parent_path = pathutils.parent_path(path) self.permissions = self._rights.authorization(self.user, self.path) + if permissions_filter is not None: + self._permissions_filter = permissions_filter + permissions_filtered = intersect(self.permissions, permissions_filter) + self.permissions = permissions_filtered self._parent_permissions = None @property @@ -124,6 +129,9 @@ class Access: if self._parent_permissions is None: self._parent_permissions = self._rights.authorization( self.user, self.parent_path) + if self._permissions_filter is not None: + parent_permissions_filtered = intersect(self._parent_permissions, self._permissions_filter) + self._parent_permissions = parent_permissions_filtered return self._parent_permissions def check(self, permission: str, From 2e1f54c7a871fb2e0e45d7ea17c072ae0581dbd9 Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Sun, 22 Feb 2026 12:27:38 +0100 Subject: [PATCH 04/24] extend copyright --- radicale/app/base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radicale/app/base.py b/radicale/app/base.py index 33a9ddb5..a9c9b3b5 100644 --- a/radicale/app/base.py +++ b/radicale/app/base.py @@ -1,6 +1,6 @@ # This file is part of Radicale - CalDAV and CardDAV server # Copyright © 2020 Unrud -# Copyright © 2024-2024 Peter Bieringer +# Copyright © 2024-2026 Peter Bieringer # # This library is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by From 6a43af2cf20c1600c4fc40490fd85f7c8ab88c92 Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Sun, 22 Feb 2026 12:30:28 +0100 Subject: [PATCH 05/24] use optional permissions filter --- radicale/app/delete.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/radicale/app/delete.py b/radicale/app/delete.py index 2201e998..a5beb3f3 100644 --- a/radicale/app/delete.py +++ b/radicale/app/delete.py @@ -57,7 +57,8 @@ class ApplicationPartDelete(ApplicationBase): def do_DELETE(self, environ: types.WSGIEnviron, base_prefix: str, path: str, user: str, remote_host: str, remote_useragent: str) -> types.WSGIResponse: """Manage DELETE request.""" - access = Access(self._rights, user, path) + permissions_filter = None + access = Access(self._rights, user, path, permissions_filter) if not access.check("w"): return httputils.NOT_ALLOWED with self._storage.acquire_lock("w", user, path=path, request="DELETE"): From 8d0447f0c7c49db37d0e27ff122bdefe8e6124cb Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Sun, 22 Feb 2026 12:30:39 +0100 Subject: [PATCH 06/24] extend copyright --- radicale/app/delete.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radicale/app/delete.py b/radicale/app/delete.py index a5beb3f3..cd8b0c3a 100644 --- a/radicale/app/delete.py +++ b/radicale/app/delete.py @@ -3,7 +3,7 @@ # Copyright © 2008 Pascal Halter # Copyright © 2008-2017 Guillaume Ayoub # Copyright © 2017-2020 Unrud -# Copyright © 2024-2025 Peter Bieringer +# Copyright © 2024-2026 Peter Bieringer # # This library is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by From 2ace2e6e8ca9e3082026d1a277c1ff016d8e587a Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Sun, 22 Feb 2026 12:31:36 +0100 Subject: [PATCH 07/24] use optional permissions filter --- radicale/app/get.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/radicale/app/get.py b/radicale/app/get.py index 2eac58f1..848a4daf 100644 --- a/radicale/app/get.py +++ b/radicale/app/get.py @@ -76,7 +76,8 @@ class ApplicationPartGet(ApplicationBase): return httputils.redirect(location, client.MOVED_PERMANENTLY) # Dispatch /.web path to web module return self._web.get(environ, base_prefix, path, user) - access = Access(self._rights, user, path) + permissions_filter = None + access = Access(self._rights, user, path, permissions_filter) if not access.check("r") and "i" not in access.permissions: return httputils.NOT_ALLOWED with self._storage.acquire_lock("r", user): From d71892b616d2e3537d058b0af69ad942e13a1308 Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Sun, 22 Feb 2026 12:31:56 +0100 Subject: [PATCH 08/24] extend copyright --- radicale/app/get.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radicale/app/get.py b/radicale/app/get.py index 848a4daf..98e82f7a 100644 --- a/radicale/app/get.py +++ b/radicale/app/get.py @@ -3,7 +3,7 @@ # Copyright © 2008 Pascal Halter # Copyright © 2008-2017 Guillaume Ayoub # Copyright © 2017-2023 Unrud -# Copyright © 2025-2025 Peter Bieringer +# Copyright © 2025-2026 Peter Bieringer # # This library is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by From 32746d03cfae1f4f0499aee6e3ae5431d57ea478 Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Sun, 22 Feb 2026 12:33:44 +0100 Subject: [PATCH 09/24] use new parent_path --- radicale/app/mkcalendar.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/radicale/app/mkcalendar.py b/radicale/app/mkcalendar.py index 53abcdbd..ccdf850c 100644 --- a/radicale/app/mkcalendar.py +++ b/radicale/app/mkcalendar.py @@ -62,8 +62,7 @@ class ApplicationPartMkcalendar(ApplicationBase): if item: return self._webdav_error_response( client.CONFLICT, "D:resource-must-be-null") - parent_path = pathutils.unstrip_path( - posixpath.dirname(pathutils.strip_path(path)), True) + parent_path = pathutils.parent_path(path) parent_item = next(iter(self._storage.discover(parent_path)), None) if not parent_item: return httputils.CONFLICT From 258426561df7d286c64e382b64707cce23e6bca8 Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Sun, 22 Feb 2026 12:33:57 +0100 Subject: [PATCH 10/24] extend copyright --- radicale/app/mkcalendar.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radicale/app/mkcalendar.py b/radicale/app/mkcalendar.py index ccdf850c..fcb98d14 100644 --- a/radicale/app/mkcalendar.py +++ b/radicale/app/mkcalendar.py @@ -3,7 +3,7 @@ # Copyright © 2008 Pascal Halter # Copyright © 2008-2017 Guillaume Ayoub # Copyright © 2017-2021 Unrud -# Copyright © 2024-2025 Peter Bieringer +# Copyright © 2024-2026 Peter Bieringer # # This library is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by From 37d0bfd227e5271628190ebcc6220f356f449b6c Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Sun, 22 Feb 2026 12:34:59 +0100 Subject: [PATCH 11/24] use new parent_path --- radicale/app/mkcol.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/radicale/app/mkcol.py b/radicale/app/mkcol.py index 45ad7c4a..49768534 100644 --- a/radicale/app/mkcol.py +++ b/radicale/app/mkcol.py @@ -66,8 +66,7 @@ class ApplicationPartMkcol(ApplicationBase): item = next(iter(self._storage.discover(path)), None) if item: return httputils.METHOD_NOT_ALLOWED - parent_path = pathutils.unstrip_path( - posixpath.dirname(pathutils.strip_path(path)), True) + parent_path = pathutils.parent_path(path) parent_item = next(iter(self._storage.discover(parent_path)), None) if not parent_item: return httputils.CONFLICT From 3e68a54242ea0ba7eb4bd0f42fb54d983751f9b5 Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Sun, 22 Feb 2026 12:35:23 +0100 Subject: [PATCH 12/24] extend copyright --- radicale/app/mkcol.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radicale/app/mkcol.py b/radicale/app/mkcol.py index 49768534..9a4d1efa 100644 --- a/radicale/app/mkcol.py +++ b/radicale/app/mkcol.py @@ -3,7 +3,7 @@ # Copyright © 2008 Pascal Halter # Copyright © 2008-2017 Guillaume Ayoub # Copyright © 2017-2021 Unrud -# Copyright © 2024-2025 Peter Bieringer +# Copyright © 2024-2026 Peter Bieringer # # This library is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by From fb058ea112b37fac0b63072029e0bd6366198b40 Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Sun, 22 Feb 2026 12:38:19 +0100 Subject: [PATCH 13/24] use optional permissions filter --- radicale/app/move.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/radicale/app/move.py b/radicale/app/move.py index 168619e3..cb6c11cc 100644 --- a/radicale/app/move.py +++ b/radicale/app/move.py @@ -67,7 +67,8 @@ class ApplicationPartMove(ApplicationBase): # Remote destination server, not supported return httputils.REMOTE_DESTINATION - access = Access(self._rights, user, path) + permissions_filter = None + access = Access(self._rights, user, path, permissions_filter) if not access.check("w"): return httputils.NOT_ALLOWED to_path = pathutils.sanitize_path(to_url.path) @@ -76,7 +77,9 @@ class ApplicationPartMove(ApplicationBase): "start with base prefix", to_path, path) return httputils.NOT_ALLOWED to_path = to_path[len(base_prefix):] - to_access = Access(self._rights, user, to_path) + to_user = user + to_permissions_filter = None + to_access = Access(self._rights, to_user, to_path, to_permissions_filter) if not to_access.check("w"): return httputils.NOT_ALLOWED From b86c460ba17af39174b0b28d36b9eff3b5a359ef Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Sun, 22 Feb 2026 12:38:37 +0100 Subject: [PATCH 14/24] use new parent_path --- radicale/app/move.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/radicale/app/move.py b/radicale/app/move.py index cb6c11cc..1dcba526 100644 --- a/radicale/app/move.py +++ b/radicale/app/move.py @@ -97,8 +97,7 @@ class ApplicationPartMove(ApplicationBase): to_item = next(iter(self._storage.discover(to_path)), None) if isinstance(to_item, storage.BaseCollection): return httputils.FORBIDDEN - to_parent_path = pathutils.unstrip_path( - posixpath.dirname(pathutils.strip_path(to_path)), True) + to_parent_path = pathutils.parent_path(to_path) to_collection = next(iter( self._storage.discover(to_parent_path)), None) if not to_collection: From 2379144772fd49d31d763ab704d1ac76111ee7f7 Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Sun, 22 Feb 2026 12:38:49 +0100 Subject: [PATCH 15/24] extend copyright --- radicale/app/move.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radicale/app/move.py b/radicale/app/move.py index 1dcba526..5fce17c8 100644 --- a/radicale/app/move.py +++ b/radicale/app/move.py @@ -3,7 +3,7 @@ # Copyright © 2008 Pascal Halter # Copyright © 2008-2017 Guillaume Ayoub # Copyright © 2017-2023 Unrud -# Copyright © 2023-2025 Peter Bieringer +# Copyright © 2023-2026 Peter Bieringer # # This library is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by From 39dee28e4a3fd1b866c4c18fa975e78646c4c6a6 Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Sun, 22 Feb 2026 12:46:45 +0100 Subject: [PATCH 16/24] use optional permissions filter / use http_depth as variable --- radicale/app/propfind.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/radicale/app/propfind.py b/radicale/app/propfind.py index 62af2949..8d35a279 100644 --- a/radicale/app/propfind.py +++ b/radicale/app/propfind.py @@ -405,7 +405,9 @@ class ApplicationPartPropfind(ApplicationBase): def do_PROPFIND(self, environ: types.WSGIEnviron, base_prefix: str, path: str, user: str, remote_host: str, remote_useragent: str) -> types.WSGIResponse: """Manage PROPFIND request.""" - access = Access(self._rights, user, path) + http_depth = environ.get("HTTP_DEPTH", "0") + permissions_filter = None + access = Access(self._rights, user, path, permissions_filter) if not access.check("r"): return httputils.NOT_ALLOWED try: @@ -419,7 +421,7 @@ class ApplicationPartPropfind(ApplicationBase): return httputils.REQUEST_TIMEOUT with self._storage.acquire_lock("r", user): items_iter = iter(self._storage.discover( - path, environ.get("HTTP_DEPTH", "0"), + path, http_depth, None, self._rights._user_groups)) # take root item for rights checking item = next(items_iter, None) From 02e48b3841e9b1c81889541004a90f180af26f0f Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Sun, 22 Feb 2026 12:47:32 +0100 Subject: [PATCH 17/24] turn iteration result into list and shorten code covered by lock --- radicale/app/propfind.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/radicale/app/propfind.py b/radicale/app/propfind.py index 8d35a279..ed301b67 100644 --- a/radicale/app/propfind.py +++ b/radicale/app/propfind.py @@ -431,11 +431,11 @@ class ApplicationPartPropfind(ApplicationBase): return httputils.NOT_ALLOWED # put item back items_iter = itertools.chain([item], items_iter) - allowed_items = self._collect_allowed_items(items_iter, user) - headers = {"DAV": httputils.DAV_HEADERS, - "Content-Type": "text/xml; charset=%s" % self._encoding} - xml_answer = xml_propfind(base_prefix, path, xml_content, - allowed_items, user, self._encoding, max_resource_size=self._max_resource_size) - if xml_answer is None: - return httputils.NOT_ALLOWED - return client.MULTI_STATUS, headers, self._xml_response(xml_answer), xmlutils.pretty_xml(xml_content) + allowed_items = list(self._collect_allowed_items(items_iter, user)) + headers = {"DAV": httputils.DAV_HEADERS, + "Content-Type": "text/xml; charset=%s" % self._encoding} + xml_answer = xml_propfind(base_prefix, path, xml_content, + allowed_items, user, self._encoding, max_resource_size=self._max_resource_size) + if xml_answer is None: + return httputils.NOT_ALLOWED + return client.MULTI_STATUS, headers, self._xml_response(xml_answer), xmlutils.pretty_xml(xml_content) From 58bf84e5956b0f3435cb6fa1b28583092ec4e71a Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Sun, 22 Feb 2026 12:48:11 +0100 Subject: [PATCH 18/24] extend copyright --- radicale/app/propfind.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radicale/app/propfind.py b/radicale/app/propfind.py index ed301b67..b006da7b 100644 --- a/radicale/app/propfind.py +++ b/radicale/app/propfind.py @@ -3,7 +3,7 @@ # Copyright © 2008 Pascal Halter # Copyright © 2008-2017 Guillaume Ayoub # Copyright © 2017-2021 Unrud -# Copyright © 2025-2025 Peter Bieringer +# Copyright © 2025-2026 Peter Bieringer # # This library is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by From aa038cad0181d4cefd8434ce23afc12ba7127a2b Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Sun, 22 Feb 2026 12:49:58 +0100 Subject: [PATCH 19/24] use optional permissions filter --- radicale/app/proppatch.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/radicale/app/proppatch.py b/radicale/app/proppatch.py index caaf7b7a..cbc5a711 100644 --- a/radicale/app/proppatch.py +++ b/radicale/app/proppatch.py @@ -75,7 +75,8 @@ class ApplicationPartProppatch(ApplicationBase): def do_PROPPATCH(self, environ: types.WSGIEnviron, base_prefix: str, path: str, user: str, remote_host: str, remote_useragent: str) -> types.WSGIResponse: """Manage PROPPATCH request.""" - access = Access(self._rights, user, path) + permissions_filter = None + access = Access(self._rights, user, path, permissions_filter) if not access.check("w"): return httputils.NOT_ALLOWED try: From bf72f15c7459fe801eb73b264a6ea2d96afb0c93 Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Sun, 22 Feb 2026 12:50:21 +0100 Subject: [PATCH 20/24] extend copyright --- radicale/app/proppatch.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radicale/app/proppatch.py b/radicale/app/proppatch.py index cbc5a711..197591b2 100644 --- a/radicale/app/proppatch.py +++ b/radicale/app/proppatch.py @@ -4,7 +4,7 @@ # Copyright © 2008-2017 Guillaume Ayoub # Copyright © 2017-2020 Unrud # Copyright © 2020-2020 Tuna Celik -# Copyright © 2025-2025 Peter Bieringer +# Copyright © 2025-2026 Peter Bieringer # # This library is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by From ec67588541c681c653b1255d506d804b0cb594a5 Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Sun, 22 Feb 2026 12:53:10 +0100 Subject: [PATCH 21/24] use optional permissions filter --- radicale/app/report.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/radicale/app/report.py b/radicale/app/report.py index 917a2d75..8a925e47 100644 --- a/radicale/app/report.py +++ b/radicale/app/report.py @@ -810,7 +810,8 @@ class ApplicationPartReport(ApplicationBase): def do_REPORT(self, environ: types.WSGIEnviron, base_prefix: str, path: str, user: str, remote_host: str, remote_useragent: str) -> types.WSGIResponse: """Manage REPORT request.""" - access = Access(self._rights, user, path) + permissions_filter = None + access = Access(self._rights, user, path, permissions_filter) if not access.check("r"): return httputils.NOT_ALLOWED try: From f84321584f246f14cfef7a769d2822be8387fe2e Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Sun, 22 Feb 2026 12:53:21 +0100 Subject: [PATCH 22/24] extend copyright --- radicale/app/report.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radicale/app/report.py b/radicale/app/report.py index 8a925e47..526cb4d8 100644 --- a/radicale/app/report.py +++ b/radicale/app/report.py @@ -6,7 +6,7 @@ # Copyright © 2024-2024 Pieter Hijma # Copyright © 2024-2024 Ray # Copyright © 2024-2025 Georgiy -# Copyright © 2024-2025 Peter Bieringer +# Copyright © 2024-2026 Peter Bieringer # Copyright © 2025-2025 David Greaves # # This library is free software: you can redistribute it and/or modify From 8a61b80e966b56a1e862a6c181d42d3a8e3428cb Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Sun, 22 Feb 2026 14:56:31 +0100 Subject: [PATCH 23/24] remove no longer required include related to use new parent_path --- radicale/app/base.py | 1 - radicale/app/mkcalendar.py | 1 - radicale/app/mkcol.py | 1 - 3 files changed, 3 deletions(-) diff --git a/radicale/app/base.py b/radicale/app/base.py index a9c9b3b5..9c6545d2 100644 --- a/radicale/app/base.py +++ b/radicale/app/base.py @@ -17,7 +17,6 @@ import io import logging -import posixpath import sys import xml.etree.ElementTree as ET from typing import Optional, Union diff --git a/radicale/app/mkcalendar.py b/radicale/app/mkcalendar.py index fcb98d14..bf6da1c8 100644 --- a/radicale/app/mkcalendar.py +++ b/radicale/app/mkcalendar.py @@ -19,7 +19,6 @@ # along with Radicale. If not, see . import errno -import posixpath import re import socket from http import client diff --git a/radicale/app/mkcol.py b/radicale/app/mkcol.py index 9a4d1efa..8d3b1755 100644 --- a/radicale/app/mkcol.py +++ b/radicale/app/mkcol.py @@ -19,7 +19,6 @@ # along with Radicale. If not, see . import errno -import posixpath import re import socket from http import client From 2572090cbd6dd6fdcfa9500160ed4c563a91a164 Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Sun, 22 Feb 2026 15:16:39 +0100 Subject: [PATCH 24/24] python 3.9 support --- radicale/app/base.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/radicale/app/base.py b/radicale/app/base.py index 9c6545d2..29ef3b71 100644 --- a/radicale/app/base.py +++ b/radicale/app/base.py @@ -106,9 +106,9 @@ class Access: permissions: str _rights: rights.BaseRights _parent_permissions: Optional[str] - _permissions_filter: Union[str | None] = None + _permissions_filter: Union[str, None] = None - def __init__(self, rights: rights.BaseRights, user: str, path: str, permissions_filter: Union[str | None] = None + def __init__(self, rights: rights.BaseRights, user: str, path: str, permissions_filter: Union[str, None] = None ) -> None: self._rights = rights self.user = user