From a75e3ebdec1cf0627e9a5254b307d9a0d4a1ebe2 Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Thu, 22 Jan 2026 06:27:04 +0100 Subject: [PATCH 1/3] Testcase for PR#1968 (MOVE with URL-encoded Destination header) --- radicale/tests/test_base.py | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) diff --git a/radicale/tests/test_base.py b/radicale/tests/test_base.py index 8d8e0def..0acc22e4 100644 --- a/radicale/tests/test_base.py +++ b/radicale/tests/test_base.py @@ -1,7 +1,7 @@ # This file is part of Radicale - CalDAV and CardDAV server # Copyright © 2012-2017 Guillaume Ayoub # Copyright © 2017-2022 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 @@ -28,6 +28,7 @@ from typing import Any, Callable, ClassVar, Iterable, List, Optional, Tuple import defusedxml.ElementTree as DefusedET import pytest +import urllib import vobject from radicale import storage, utils, xmlutils @@ -568,6 +569,33 @@ permissions: RrWw""") self.get(path1, check=404) self.get(path2) + def test_move_between_collections_with_at_native(self) -> None: + """Move a item.""" + self.mkcalendar("/calendar1@domain.ics/") + self.mkcalendar("/calendar2@domain.ics/") + event = get_file_content("event1.ics") + path1 = "/calendar1@domain.ics/event1.ics" + path2 = "/calendar2@domain.ics/event2.ics" + self.put(path1, event) + self.request("MOVE", path1, check=201, + HTTP_DESTINATION="http://127.0.0.1/"+path2) + self.get(path1, check=404) + self.get(path2) + + def test_move_between_collections_with_at_encoded(self) -> None: + """Move a item.""" + self.mkcalendar("/calendar1@domain.ics/") + self.mkcalendar("/calendar2@domain.ics/") + event = get_file_content("event1.ics") + path1 = "/calendar1@domain.ics/event1.ics" + path2 = "/calendar2@domain.ics/event2.ics" + path2_encoded = urllib.parse.quote(path2) + self.put(path1, event) + self.request("MOVE", path1, check=201, + HTTP_DESTINATION="http://127.0.0.1/"+path2_encoded) + self.get(path1, check=404) + self.get(path2) + def test_move_between_collections_duplicate_uid(self) -> None: """Move a item to a collection which already contains the UID.""" self.mkcalendar("/calendar1.ics/") From e409d628db5e1d8bc88f5bd56d55275b06f69c82 Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Thu, 22 Jan 2026 06:31:41 +0100 Subject: [PATCH 2/3] changelog for https://github.com/Kozea/Radicale/pull/1968 --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index ef280a36..15706e57 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ # Changelog +## 3.6.1.dev + +* Fix: MOVE failing with URL-encoded destination header + ## 3.6.0 * Extend: logwatch script From 02a6cd55e3a84f0afdb44bfbe876dd9041401644 Mon Sep 17 00:00:00 2001 From: Peter Bieringer Date: Thu, 22 Jan 2026 06:35:12 +0100 Subject: [PATCH 3/3] fix for lint --- radicale/tests/test_base.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radicale/tests/test_base.py b/radicale/tests/test_base.py index 0acc22e4..ab7954bf 100644 --- a/radicale/tests/test_base.py +++ b/radicale/tests/test_base.py @@ -24,11 +24,11 @@ Radicale tests with simple requests. import logging import os import posixpath +import urllib from typing import Any, Callable, ClassVar, Iterable, List, Optional, Tuple import defusedxml.ElementTree as DefusedET import pytest -import urllib import vobject from radicale import storage, utils, xmlutils