Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package Radicale for openSUSE:Factory 
checked in at 2025-12-09 12:48:20
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/Radicale (Old)
 and      /work/SRC/openSUSE:Factory/.Radicale.new.1939 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "Radicale"

Tue Dec  9 12:48:20 2025 rev:27 rq:1321525 version:3.5.9

Changes:
--------
--- /work/SRC/openSUSE:Factory/Radicale/Radicale.changes        2025-11-17 
12:24:05.551576792 +0100
+++ /work/SRC/openSUSE:Factory/.Radicale.new.1939/Radicale.changes      
2025-12-09 12:55:55.698003482 +0100
@@ -1,0 +2,8 @@
+Mon Dec  1 08:40:59 UTC 2025 - Ákos Szőts <[email protected]>
+
+- Update to 3.5.9
+  * Extend: [auth] add support for type http_remote_user
+  * Extend: logging of invalid sync-token with user, path, remote host and 
useragent
+  * Fix: typo related to collection delete hook (can cause OOM error) 
+
+-------------------------------------------------------------------

Old:
----
  v3.5.8.tar.gz

New:
----
  v3.5.9.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ Radicale.spec ++++++
--- /var/tmp/diff_new_pack.cPaMZu/_old  2025-12-09 12:55:57.978099664 +0100
+++ /var/tmp/diff_new_pack.cPaMZu/_new  2025-12-09 12:55:57.978099664 +0100
@@ -27,7 +27,7 @@
 %define pk_min_ver 1.1.0
 %define pt_min_ver 7
 Name:           Radicale
-Version:        3.5.8
+Version:        3.5.9
 Release:        0
 Summary:        A CalDAV calendar and CardDav contact server
 License:        GPL-3.0-or-later

++++++ v3.5.8.tar.gz -> v3.5.9.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.8/.github/workflows/test.yml 
new/Radicale-3.5.9/.github/workflows/test.yml
--- old/Radicale-3.5.8/.github/workflows/test.yml       2025-11-06 
06:29:21.000000000 +0100
+++ new/Radicale-3.5.9/.github/workflows/test.yml       2025-11-29 
15:33:06.000000000 +0100
@@ -6,7 +6,7 @@
     strategy:
       matrix:
         os: [ubuntu-latest, macos-latest, windows-latest]
-        python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', 'pypy-3.9', 
'pypy-3.10', 'pypy-3.11']
+        python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14', 
'pypy-3.9', 'pypy-3.10', 'pypy-3.11']
         exclude:
           - os: windows-latest
             python-version: 'pypy-3.9'
@@ -72,7 +72,7 @@
       - uses: actions/checkout@v4
       - uses: actions/setup-python@v5
         with:
-          python-version: '3.13.0'
+          python-version: '3.13'
       - name: Install tox
         run: pip install tox
       - name: Lint
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.8/CHANGELOG.md 
new/Radicale-3.5.9/CHANGELOG.md
--- old/Radicale-3.5.8/CHANGELOG.md     2025-11-06 06:29:21.000000000 +0100
+++ new/Radicale-3.5.9/CHANGELOG.md     2025-11-29 15:33:06.000000000 +0100
@@ -1,7 +1,12 @@
 # Changelog
 
+## 3.5.9
+* Extend: [auth] add support for type http_remote_user
+* Extend: logging of invalid sync-token with user, path, remote host and 
useragent
+* Fix: typo related to collection delete hook
+
 ## 3.5.8
-* Extend [auth]: re-factor & overhaul LDAP authentication, especially for 
Python's ldap module
+* Extend: [auth] re-factor & overhaul LDAP authentication, especially for 
Python's ldap module
 * Fix: out-of-range timestamp on 32-bit systems
 * Feature: extend logging with response size in bytes and flag served as plain 
or gzip
 * Feature: [storage] strict_preconditions: new config option to enforce strict 
preconditions check on PUT in case item already exists [RFC6352#9.2]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.8/DOCUMENTATION.md 
new/Radicale-3.5.9/DOCUMENTATION.md
--- old/Radicale-3.5.8/DOCUMENTATION.md 2025-11-06 06:29:21.000000000 +0100
+++ new/Radicale-3.5.9/DOCUMENTATION.md 2025-11-29 15:33:06.000000000 +0100
@@ -272,7 +272,7 @@
 Recommendation: check support by [Linux Distribution 
Packages](#linux-distribution-packages)
 instead of manual setup / initial configuration.
 
-Create the **radicale** user and group for the Radicale service by running (as 
`root`:
+Create the **radicale** user and group for the Radicale service by running (as 
`root`):
 ```bash
 useradd --system --user-group --home-dir / --shell /sbin/nologin radicale
 ```
@@ -902,8 +902,15 @@
   Requires validation, otherwise clients can supply the header themselves,
   which then is unconditionally trusted.
 
+* `http_remote_user` _(>= 3.5.9)_
+  Takes the username from the Remote-User HTTP header `HTTP_REMOTE_USER` and 
disables
+  Radicale's internal HTTP authentication. This can be used to provide the
+  username from a reverse proxy which authenticated the client upfront.
+  Requires validation, otherwise clients can supply the header themselves,
+  which then is unconditionally trusted.
+
 * `http_x_remote_user`  
-  Takes the username from the `X-Remote-User` HTTP header and disables
+  Takes the username from the X-Remote-User HTTP header `HTTP_X_REMOTE_USER` 
and disables
   Radicale's internal HTTP authentication. This can be used to provide the
   username from a reverse proxy which authenticated the client upfront.
   Requires validation, otherwise clients can supply the header themselves,
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.8/config new/Radicale-3.5.9/config
--- old/Radicale-3.5.8/config   2025-11-06 06:29:21.000000000 +0100
+++ new/Radicale-3.5.9/config   2025-11-29 15:33:06.000000000 +0100
@@ -63,7 +63,7 @@
 [auth]
 
 # Authentication method
-# Value: none | htpasswd | remote_user | http_x_remote_user | dovecot | ldap | 
oauth2 | pam | denyall
+# Value: none | htpasswd | remote_user | http_remote_user | http_x_remote_user 
| dovecot | ldap | oauth2 | pam | denyall
 #type = denyall
 
 # Cache logins for until expiration time
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.8/contrib/caddy/radicale.caddyfile 
new/Radicale-3.5.9/contrib/caddy/radicale.caddyfile
--- old/Radicale-3.5.8/contrib/caddy/radicale.caddyfile 2025-11-06 
06:29:21.000000000 +0100
+++ new/Radicale-3.5.9/contrib/caddy/radicale.caddyfile 2025-11-29 
15:33:06.000000000 +0100
@@ -16,11 +16,17 @@
         not path /.web/*
     }
 
+    # disable this in case authentication is handled by Radicale
     basic_auth @not-webui {
         USER HASH
     }
 
     reverse_proxy localhost:5232 {
+        # disable this in case authentication is handled by Radicale
         header_up X-Remote-User {http.auth.user.id}
+        # replace "HOST" with configured hostname of URL (FQDN) in client
+        header_up Host HOST
+        # replace "PORT" with configured port of URL in client
+        header_up X-Forwarded-Port PORT
     }
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.8/pyproject.toml 
new/Radicale-3.5.9/pyproject.toml
--- old/Radicale-3.5.8/pyproject.toml   2025-11-06 06:29:21.000000000 +0100
+++ new/Radicale-3.5.9/pyproject.toml   2025-11-29 15:33:06.000000000 +0100
@@ -3,7 +3,7 @@
 # When the version is updated, a new section in the CHANGELOG.md file must be
 # added too.
 readme = "README.md"
-version = "3.5.8"
+version = "3.5.9"
 authors = [{name = "Guillaume Ayoub", email = "[email protected]"}, 
{name = "Unrud", email = "[email protected]"}, {name = "Peter Bieringer", email 
= "[email protected]"}]
 license = {text = "GNU GPL v3"}
 description = "CalDAV and CardDAV Server"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.8/radicale/app/__init__.py 
new/Radicale-3.5.9/radicale/app/__init__.py
--- old/Radicale-3.5.8/radicale/app/__init__.py 2025-11-06 06:29:21.000000000 
+0100
+++ new/Radicale-3.5.9/radicale/app/__init__.py 2025-11-29 15:33:06.000000000 
+0100
@@ -275,7 +275,7 @@
                 logger.debug("Called by reverse proxy, remove base prefix %r 
from path: %r => %r", base_prefix, path, path_new)
                 path = path_new
             else:
-                if self._auth_type in ['remote_user', 'http_x_remote_user'] 
and self._web_type == 'internal':
+                if self._auth_type in ['remote_user', 'http_remote_user', 
'http_x_remote_user'] and self._web_type == 'internal':
                     logger.warning("Called by reverse proxy, cannot remove 
base prefix %r from path: %r as not matching (may cause authentication issues 
using internal WebUI)", base_prefix, path)
                 else:
                     logger.debug("Called by reverse proxy, cannot remove base 
prefix %r from path: %r as not matching", base_prefix, path)
@@ -371,7 +371,7 @@
 
         if not login or user:
             status, headers, answer = function(
-                environ, base_prefix, path, user)
+                environ, base_prefix, path, user, remote_host, 
remote_useragent)
             if (status, headers, answer) == httputils.NOT_ALLOWED:
                 logger.info("Access to %r denied for %s", path,
                             repr(user) if user else "anonymous user")
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.8/radicale/app/delete.py 
new/Radicale-3.5.9/radicale/app/delete.py
--- old/Radicale-3.5.8/radicale/app/delete.py   2025-11-06 06:29:21.000000000 
+0100
+++ new/Radicale-3.5.9/radicale/app/delete.py   2025-11-29 15:33:06.000000000 
+0100
@@ -55,7 +55,7 @@
 class ApplicationPartDelete(ApplicationBase):
 
     def do_DELETE(self, environ: types.WSGIEnviron, base_prefix: str,
-                  path: str, user: str) -> types.WSGIResponse:
+                  path: str, user: str, remote_host: str, remote_useragent: 
str) -> types.WSGIResponse:
         """Manage DELETE request."""
         access = Access(self._rights, user, path)
         if not access.check("w"):
@@ -87,7 +87,7 @@
                             path=access.path,
                             content=i.uid,
                             uid=i.uid,
-                            old_content=item.serialize(),  # type: ignore
+                            old_content=i.serialize(),  # type: ignore
                             new_content=None
                         )
                     )
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.8/radicale/app/get.py 
new/Radicale-3.5.9/radicale/app/get.py
--- old/Radicale-3.5.8/radicale/app/get.py      2025-11-06 06:29:21.000000000 
+0100
+++ new/Radicale-3.5.9/radicale/app/get.py      2025-11-29 15:33:06.000000000 
+0100
@@ -2,7 +2,8 @@
 # Copyright © 2008 Nicolas Kandel
 # Copyright © 2008 Pascal Halter
 # Copyright © 2008-2017 Guillaume Ayoub
-# Copyright © 2017-2018 Unrud <[email protected]>
+# Copyright © 2017-2023 Unrud <[email protected]>
+# Copyright © 2025-2025 Peter Bieringer <[email protected]>
 #
 # 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
@@ -58,7 +59,7 @@
         return value
 
     def do_GET(self, environ: types.WSGIEnviron, base_prefix: str, path: str,
-               user: str) -> types.WSGIResponse:
+               user: str, remote_host: str, remote_useragent: str) -> 
types.WSGIResponse:
         """Manage GET request."""
         # Redirect to /.web if the root path is requested
         if not pathutils.strip_path(path):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.8/radicale/app/head.py 
new/Radicale-3.5.9/radicale/app/head.py
--- old/Radicale-3.5.8/radicale/app/head.py     2025-11-06 06:29:21.000000000 
+0100
+++ new/Radicale-3.5.9/radicale/app/head.py     2025-11-29 15:33:06.000000000 
+0100
@@ -2,7 +2,8 @@
 # Copyright © 2008 Nicolas Kandel
 # Copyright © 2008 Pascal Halter
 # Copyright © 2008-2017 Guillaume Ayoub
-# Copyright © 2017-2018 Unrud <[email protected]>
+# Copyright © 2017-2022 Unrud <[email protected]>
+# Copyright © 2025-2025 Peter Bieringer <[email protected]>
 #
 # 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
@@ -25,7 +26,7 @@
 class ApplicationPartHead(ApplicationPartGet, ApplicationBase):
 
     def do_HEAD(self, environ: types.WSGIEnviron, base_prefix: str, path: str,
-                user: str) -> types.WSGIResponse:
+                user: str, remote_host: str, remote_useragent: str) -> 
types.WSGIResponse:
         """Manage HEAD request."""
         # Body is dropped in `Application.__call__` for HEAD requests
-        return self.do_GET(environ, base_prefix, path, user)
+        return self.do_GET(environ, base_prefix, path, user, remote_host, 
remote_useragent)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.8/radicale/app/mkcalendar.py 
new/Radicale-3.5.9/radicale/app/mkcalendar.py
--- old/Radicale-3.5.8/radicale/app/mkcalendar.py       2025-11-06 
06:29:21.000000000 +0100
+++ new/Radicale-3.5.9/radicale/app/mkcalendar.py       2025-11-29 
15:33:06.000000000 +0100
@@ -33,7 +33,7 @@
 class ApplicationPartMkcalendar(ApplicationBase):
 
     def do_MKCALENDAR(self, environ: types.WSGIEnviron, base_prefix: str,
-                      path: str, user: str) -> types.WSGIResponse:
+                      path: str, user: str, remote_host: str, 
remote_useragent: str) -> types.WSGIResponse:
         """Manage MKCALENDAR request."""
         if "w" not in self._rights.authorization(user, path):
             return httputils.NOT_ALLOWED
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.8/radicale/app/mkcol.py 
new/Radicale-3.5.9/radicale/app/mkcol.py
--- old/Radicale-3.5.8/radicale/app/mkcol.py    2025-11-06 06:29:21.000000000 
+0100
+++ new/Radicale-3.5.9/radicale/app/mkcol.py    2025-11-29 15:33:06.000000000 
+0100
@@ -33,7 +33,7 @@
 class ApplicationPartMkcol(ApplicationBase):
 
     def do_MKCOL(self, environ: types.WSGIEnviron, base_prefix: str,
-                 path: str, user: str) -> types.WSGIResponse:
+                 path: str, user: str, remote_host: str, remote_useragent: 
str) -> types.WSGIResponse:
         """Manage MKCOL request."""
         permissions = self._rights.authorization(user, path)
         if not rights.intersect(permissions, "Ww"):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.8/radicale/app/move.py 
new/Radicale-3.5.9/radicale/app/move.py
--- old/Radicale-3.5.8/radicale/app/move.py     2025-11-06 06:29:21.000000000 
+0100
+++ new/Radicale-3.5.9/radicale/app/move.py     2025-11-29 15:33:06.000000000 
+0100
@@ -48,7 +48,7 @@
 class ApplicationPartMove(ApplicationBase):
 
     def do_MOVE(self, environ: types.WSGIEnviron, base_prefix: str,
-                path: str, user: str) -> types.WSGIResponse:
+                path: str, user: str, remote_host: str, remote_useragent: str) 
-> types.WSGIResponse:
         """Manage MOVE request."""
         raw_dest = environ.get("HTTP_DESTINATION", "")
         to_url = urlparse(raw_dest)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.8/radicale/app/options.py 
new/Radicale-3.5.9/radicale/app/options.py
--- old/Radicale-3.5.8/radicale/app/options.py  2025-11-06 06:29:21.000000000 
+0100
+++ new/Radicale-3.5.9/radicale/app/options.py  2025-11-29 15:33:06.000000000 
+0100
@@ -2,7 +2,8 @@
 # Copyright © 2008 Nicolas Kandel
 # Copyright © 2008 Pascal Halter
 # Copyright © 2008-2017 Guillaume Ayoub
-# Copyright © 2017-2018 Unrud <[email protected]>
+# Copyright © 2017-2021 Unrud <[email protected]>
+# Copyright © 2025-2025 Peter Bieringer <[email protected]>
 #
 # 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
@@ -26,7 +27,7 @@
 class ApplicationPartOptions(ApplicationBase):
 
     def do_OPTIONS(self, environ: types.WSGIEnviron, base_prefix: str,
-                   path: str, user: str) -> types.WSGIResponse:
+                   path: str, user: str, remote_host: str, remote_useragent: 
str) -> types.WSGIResponse:
         """Manage OPTIONS request."""
         headers = {
             "Allow": ", ".join(
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.8/radicale/app/post.py 
new/Radicale-3.5.9/radicale/app/post.py
--- old/Radicale-3.5.8/radicale/app/post.py     2025-11-06 06:29:21.000000000 
+0100
+++ new/Radicale-3.5.9/radicale/app/post.py     2025-11-29 15:33:06.000000000 
+0100
@@ -2,8 +2,9 @@
 # Copyright © 2008 Nicolas Kandel
 # Copyright © 2008 Pascal Halter
 # Copyright © 2008-2017 Guillaume Ayoub
-# Copyright © 2017-2018 Unrud <[email protected]>
-# Copyright © 2020 Tom Hacohen <[email protected]>
+# Copyright © 2017-2021 Unrud <[email protected]>
+# Copyright © 2020-2020 Tom Hacohen <[email protected]>
+# Copyright © 2025-2025 Peter Bieringer <[email protected]>
 #
 # 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
@@ -25,7 +26,7 @@
 class ApplicationPartPost(ApplicationBase):
 
     def do_POST(self, environ: types.WSGIEnviron, base_prefix: str,
-                path: str, user: str) -> types.WSGIResponse:
+                path: str, user: str, remote_host: str, remote_useragent: str) 
-> types.WSGIResponse:
         """Manage POST request."""
         if path == "/.web" or path.startswith("/.web/"):
             return self._web.post(environ, base_prefix, path, user)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.8/radicale/app/propfind.py 
new/Radicale-3.5.9/radicale/app/propfind.py
--- old/Radicale-3.5.8/radicale/app/propfind.py 2025-11-06 06:29:21.000000000 
+0100
+++ new/Radicale-3.5.9/radicale/app/propfind.py 2025-11-29 15:33:06.000000000 
+0100
@@ -2,7 +2,8 @@
 # Copyright © 2008 Nicolas Kandel
 # Copyright © 2008 Pascal Halter
 # Copyright © 2008-2017 Guillaume Ayoub
-# Copyright © 2017-2018 Unrud <[email protected]>
+# Copyright © 2017-2021 Unrud <[email protected]>
+# Copyright © 2025-2025 Peter Bieringer <[email protected]>
 #
 # 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
@@ -376,7 +377,7 @@
                 yield item, permission
 
     def do_PROPFIND(self, environ: types.WSGIEnviron, base_prefix: str,
-                    path: str, user: str) -> types.WSGIResponse:
+                    path: str, user: str, remote_host: str, remote_useragent: 
str) -> types.WSGIResponse:
         """Manage PROPFIND request."""
         access = Access(self._rights, user, path)
         if not access.check("r"):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.8/radicale/app/proppatch.py 
new/Radicale-3.5.9/radicale/app/proppatch.py
--- old/Radicale-3.5.8/radicale/app/proppatch.py        2025-11-06 
06:29:21.000000000 +0100
+++ new/Radicale-3.5.9/radicale/app/proppatch.py        2025-11-29 
15:33:06.000000000 +0100
@@ -73,7 +73,7 @@
 class ApplicationPartProppatch(ApplicationBase):
 
     def do_PROPPATCH(self, environ: types.WSGIEnviron, base_prefix: str,
-                     path: str, user: str) -> types.WSGIResponse:
+                     path: str, user: str, remote_host: str, remote_useragent: 
str) -> types.WSGIResponse:
         """Manage PROPPATCH request."""
         access = Access(self._rights, user, path)
         if not access.check("w"):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.8/radicale/app/put.py 
new/Radicale-3.5.9/radicale/app/put.py
--- old/Radicale-3.5.8/radicale/app/put.py      2025-11-06 06:29:21.000000000 
+0100
+++ new/Radicale-3.5.9/radicale/app/put.py      2025-11-29 15:33:06.000000000 
+0100
@@ -142,7 +142,7 @@
 class ApplicationPartPut(ApplicationBase):
 
     def do_PUT(self, environ: types.WSGIEnviron, base_prefix: str,
-               path: str, user: str) -> types.WSGIResponse:
+               path: str, user: str, remote_host: str, remote_useragent: str) 
-> types.WSGIResponse:
         """Manage PUT request."""
         access = Access(self._rights, user, path)
         if not access.check("w"):
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.8/radicale/app/report.py 
new/Radicale-3.5.9/radicale/app/report.py
--- old/Radicale-3.5.8/radicale/app/report.py   2025-11-06 06:29:21.000000000 
+0100
+++ new/Radicale-3.5.9/radicale/app/report.py   2025-11-29 15:33:06.000000000 
+0100
@@ -149,7 +149,7 @@
 def xml_report(base_prefix: str, path: str, xml_request: Optional[ET.Element],
                collection: storage.BaseCollection, encoding: str,
                unlock_storage_fn: Callable[[], None],
-               max_occurrence: int = 0,
+               max_occurrence: int = 0, user: str = "", remote_addr: str = "", 
remote_useragent: str = ""
                ) -> Tuple[int, ET.Element]:
     """Read and answer REPORT requests that return XML.
 
@@ -213,8 +213,8 @@
             sync_token, names = collection.sync(old_sync_token)
         except ValueError as e:
             # Invalid sync token
-            logger.warning("Client provided invalid sync token %r: %s",
-                           old_sync_token, e, exc_info=True)
+            logger.warning("Client provided invalid sync token for path %r 
(user %r from %s%s): %s",
+                           path, user, remote_addr, remote_useragent, e, 
exc_info=True)
             # client.CONFLICT doesn't work with some clients (e.g. InfCloud)
             return (client.FORBIDDEN,
                     xmlutils.webdav_error("D:valid-sync-token"))
@@ -776,7 +776,7 @@
 class ApplicationPartReport(ApplicationBase):
 
     def do_REPORT(self, environ: types.WSGIEnviron, base_prefix: str,
-                  path: str, user: str) -> types.WSGIResponse:
+                  path: str, user: str, remote_host: str, remote_useragent: 
str) -> types.WSGIResponse:
         """Manage REPORT request."""
         access = Access(self._rights, user, path)
         if not access.check("r"):
@@ -820,7 +820,7 @@
                 try:
                     status, xml_answer = xml_report(
                         base_prefix, path, xml_content, collection, 
self._encoding,
-                        lock_stack.close, max_occurrence)
+                        lock_stack.close, max_occurrence, user, remote_host, 
remote_useragent)
                 except ValueError as e:
                     logger.warning(
                         "Bad REPORT request on %r: %s", path, e, exc_info=True)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.8/radicale/auth/__init__.py 
new/Radicale-3.5.9/radicale/auth/__init__.py
--- old/Radicale-3.5.8/radicale/auth/__init__.py        2025-11-06 
06:29:21.000000000 +0100
+++ new/Radicale-3.5.9/radicale/auth/__init__.py        2025-11-29 
15:33:06.000000000 +0100
@@ -23,7 +23,7 @@
 
 Authentication is based on usernames and passwords. If something more
 advanced is needed an external WSGI server or reverse proxy can be used
-(see ``remote_user`` or ``http_x_remote_user`` backend).
+(see ``remote_user``, ``http_remote_user`` or ``http_x_remote_user`` backend).
 
 Take a look at the class ``BaseAuth`` if you want to implement your own.
 
@@ -40,6 +40,7 @@
 from radicale.log import logger
 
 INTERNAL_TYPES: Sequence[str] = ("none", "remote_user", "http_x_remote_user",
+                                 "http_remote_user",
                                  "denyall",
                                  "htpasswd",
                                  "ldap",
@@ -59,6 +60,7 @@
 
 INSECURE_IF_NO_LOOPBACK_TYPES: Sequence[str] = (
                                     "remote_user",
+                                    "http_remote_user",
                                     "http_x_remote_user",
                                    )
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.8/radicale/auth/http_remote_user.py 
new/Radicale-3.5.9/radicale/auth/http_remote_user.py
--- old/Radicale-3.5.8/radicale/auth/http_remote_user.py        1970-01-01 
01:00:00.000000000 +0100
+++ new/Radicale-3.5.9/radicale/auth/http_remote_user.py        2025-11-29 
15:33:06.000000000 +0100
@@ -0,0 +1,36 @@
+# This file is part of Radicale - CalDAV and CardDAV server
+# Copyright © 2025-2025 Peter Bieringer <[email protected]>
+#
+# 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
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This library 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with Radicale.  If not, see <http://www.gnu.org/licenses/>.
+
+"""
+Authentication backend that takes the username from the
+``HTTP_REMOTE_USER`` header.
+
+It's intended for use with a reverse proxy. Be aware as this will be insecure
+if the reverse proxy is not configured properly.
+
+"""
+
+from typing import Tuple, Union
+
+from radicale import types
+from radicale.auth import none
+
+
+class Auth(none.Auth):
+
+    def get_external_login(self, environ: types.WSGIEnviron) -> Union[
+            Tuple[()], Tuple[str, str]]:
+        return environ.get("HTTP_REMOTE_USER", ""), ""
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.8/radicale/tests/__init__.py 
new/Radicale-3.5.9/radicale/tests/__init__.py
--- old/Radicale-3.5.8/radicale/tests/__init__.py       2025-11-06 
06:29:21.000000000 +0100
+++ new/Radicale-3.5.9/radicale/tests/__init__.py       2025-11-29 
15:33:06.000000000 +0100
@@ -1,6 +1,7 @@
 # This file is part of Radicale - CalDAV and CardDAV server
 # Copyright © 2012-2017 Guillaume Ayoub
-# Copyright © 2017-2018 Unrud <[email protected]>
+# Copyright © 2017-2023 Unrud <[email protected]>
+# Copyright © 2024-2025 Peter Bieringer <[email protected]>
 #
 # 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
@@ -79,6 +80,8 @@
         if http_if_match is not None and not isinstance(http_if_match, str):
             raise TypeError("http_if_match argument must be %r, not %r" %
                             (str, type(http_if_match)))
+        remote_useragent = kwargs.pop("remote_useragent", None)
+        remote_host = kwargs.pop("remote_host", None)
         environ: Dict[str, Any] = {k.upper(): v for k, v in kwargs.items()}
         for k, v in environ.items():
             if not isinstance(v, str):
@@ -90,6 +93,10 @@
                     login.encode(encoding)).decode()
         if http_if_match:
             environ["HTTP_IF_MATCH"] = http_if_match
+        if remote_useragent:
+            environ["HTTP_USER_AGENT"] = remote_useragent
+        if remote_host:
+            environ["REMOTE_ADDR"] = remote_host
         environ["REQUEST_METHOD"] = method.upper()
         environ["PATH_INFO"] = path
         if data is not None:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.8/radicale/tests/test_auth.py 
new/Radicale-3.5.9/radicale/tests/test_auth.py
--- old/Radicale-3.5.8/radicale/tests/test_auth.py      2025-11-06 
06:29:21.000000000 +0100
+++ new/Radicale-3.5.9/radicale/tests/test_auth.py      2025-11-29 
15:33:06.000000000 +0100
@@ -263,6 +263,23 @@
         href_element = prop.find(xmlutils.make_clark("D:href"))
         assert href_element is not None and href_element.text == "/test/"
 
+    def test_http_remote_user(self) -> None:
+        self.configure({"auth": {"type": "http_remote_user"}})
+        _, responses = self.propfind("/", """\
+<?xml version="1.0" encoding="utf-8"?>
+<propfind xmlns="DAV:">
+    <prop>
+        <current-user-principal />
+    </prop>
+</propfind>""", HTTP_REMOTE_USER="test")
+        assert responses is not None
+        response = responses["/"]
+        assert not isinstance(response, int)
+        status, prop = response["D:current-user-principal"]
+        assert status == 200
+        href_element = prop.find(xmlutils.make_clark("D:href"))
+        assert href_element is not None and href_element.text == "/test/"
+
     def test_http_x_remote_user(self) -> None:
         self.configure({"auth": {"type": "http_x_remote_user"}})
         _, responses = self.propfind("/", """\
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.8/radicale/tests/test_base.py 
new/Radicale-3.5.9/radicale/tests/test_base.py
--- old/Radicale-3.5.8/radicale/tests/test_base.py      2025-11-06 
06:29:21.000000000 +0100
+++ new/Radicale-3.5.9/radicale/tests/test_base.py      2025-11-29 
15:33:06.000000000 +0100
@@ -1687,7 +1687,7 @@
 </C:free-busy-query>""", 400, is_xml=False)
 
     def _report_sync_token(
-            self, calendar_path: str, sync_token: Optional[str] = None
+            self, calendar_path: str, sync_token: Optional[str] = None, 
**kwargs
             ) -> Tuple[str, RESPONSES]:
         sync_token_xml = (
             "<sync-token><![CDATA[%s]]></sync-token>" % sync_token
@@ -1699,7 +1699,7 @@
         <getetag />
     </prop>
     %s
-</sync-collection>""" % sync_token_xml)
+</sync-collection>""" % sync_token_xml, **kwargs)
         xml = DefusedET.fromstring(answer)
         if status in (403, 409):
             assert xml.tag == xmlutils.make_clark("D:error")
@@ -1847,6 +1847,15 @@
             calendar_path, "http://radicale.org/ns/sync/INVALID";)
         assert not sync_token
 
+    def test_report_sync_collection_invalid_sync_token_with_user(self) -> None:
+        """Test sync-collection report with an invalid sync token and 
user+host+useragent"""
+        self.configure({"auth": {"type": "none"}})
+        calendar_path = "/calendar.ics/"
+        self.mkcalendar(calendar_path)
+        sync_token, _ = self._report_sync_token(
+            calendar_path, "http://radicale.org/ns/sync/INVALID";, 
login="testuser:", remote_host="192.0.2.1", remote_useragent="Testclient/1.0")
+        assert not sync_token
+
     def test_propfind_sync_token(self) -> None:
         """Retrieve the sync-token with a propfind request"""
         calendar_path = "/calendar.ics/"
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.8/setup.py.legacy 
new/Radicale-3.5.9/setup.py.legacy
--- old/Radicale-3.5.8/setup.py.legacy  2025-11-06 06:29:21.000000000 +0100
+++ new/Radicale-3.5.9/setup.py.legacy  2025-11-29 15:33:06.000000000 +0100
@@ -20,7 +20,7 @@
 
 # When the version is updated, a new section in the CHANGELOG.md file must be
 # added too.
-VERSION = "3.5.8"
+VERSION = "3.5.9"
 
 with open("README.md", encoding="utf-8") as f:
     long_description = f.read()

Reply via email to