Merge pull request #1968 from laurisvr/fix-move-urlencode-destination

Fix MOVE failing with URL-encoded Destination header
This commit is contained in:
Peter Bieringer
2026-01-22 06:29:48 +01:00
committed by GitHub

View File

@@ -22,7 +22,7 @@ import errno
import posixpath import posixpath
import re import re
from http import client from http import client
from urllib.parse import urlparse from urllib.parse import unquote, urlparse
from radicale import httputils, pathutils, storage, types from radicale import httputils, pathutils, storage, types
from radicale.app.base import Access, ApplicationBase from radicale.app.base import Access, ApplicationBase
@@ -51,15 +51,22 @@ class ApplicationPartMove(ApplicationBase):
path: str, user: str, remote_host: str, remote_useragent: str) -> types.WSGIResponse: path: str, user: str, remote_host: str, remote_useragent: str) -> types.WSGIResponse:
"""Manage MOVE request.""" """Manage MOVE request."""
raw_dest = environ.get("HTTP_DESTINATION", "") raw_dest = environ.get("HTTP_DESTINATION", "")
to_url = urlparse(raw_dest)
to_netloc_with_port = to_url.netloc # Decode URL-encoded characters (e.g. %40 -> @) before parsing
if to_url.port is None: raw_dest_decoded = unquote(raw_dest)
to_netloc_with_port += (":443" if to_url.scheme == "https" to_url = urlparse(raw_dest_decoded)
else ":80")
if to_netloc_with_port != get_server_netloc(environ, force_port=True): # Only check netloc for absolute URLs
logger.info("Unsupported destination address: %r", raw_dest) if to_url.netloc:
# Remote destination server, not supported to_netloc_with_port = to_url.netloc
return httputils.REMOTE_DESTINATION if to_url.port is None:
to_netloc_with_port += (":443" if to_url.scheme == "https"
else ":80")
if to_netloc_with_port != get_server_netloc(environ, force_port=True):
logger.info("Unsupported destination address: %r", raw_dest)
# Remote destination server, not supported
return httputils.REMOTE_DESTINATION
access = Access(self._rights, user, path) access = Access(self._rights, user, path)
if not access.check("w"): if not access.check("w"):
return httputils.NOT_ALLOWED return httputils.NOT_ALLOWED