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-05-30 14:33:03
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/Radicale (Old)
 and      /work/SRC/openSUSE:Factory/.Radicale.new.25440 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "Radicale"

Fri May 30 14:33:03 2025 rev:21 rq:1280765 version:3.5.3

Changes:
--------
--- /work/SRC/openSUSE:Factory/Radicale/Radicale.changes        2025-05-08 
18:25:51.574680225 +0200
+++ /work/SRC/openSUSE:Factory/.Radicale.new.25440/Radicale.changes     
2025-05-30 17:19:48.329459133 +0200
@@ -1,0 +2,8 @@
+Wed May 21 06:40:59 UTC 2025 - Ákos Szőts <szots...@gmail.com>
+
+- Update to 3.5.3
+  * Add: [auth] htpasswd: support for Argon2 hashes
+  * Add: [auth] urldecode_username: optional decode provided username (e.g. 
encoded email address)
+  * Improve: catch error on calendar collection upload and display problematic 
item content on debug level
+
+-------------------------------------------------------------------

Old:
----
  v3.5.2.tar.gz

New:
----
  v3.5.3.tar.gz

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

Other differences:
------------------
++++++ Radicale.spec ++++++
--- /var/tmp/diff_new_pack.5VHnKc/_old  2025-05-30 17:19:48.777477675 +0200
+++ /var/tmp/diff_new_pack.5VHnKc/_new  2025-05-30 17:19:48.777477675 +0200
@@ -26,7 +26,7 @@
 %define vo_min_ver 0.9.6
 %define pk_min_ver 1.1.0
 Name:           Radicale
-Version:        3.5.2
+Version:        3.5.3
 Release:        0
 Summary:        A CalDAV calendar and CardDav contact server
 License:        GPL-3.0-or-later

++++++ v3.5.2.tar.gz -> v3.5.3.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.2/.github/workflows/docker-publish.yml 
new/Radicale-3.5.3/.github/workflows/docker-publish.yml
--- old/Radicale-3.5.2/.github/workflows/docker-publish.yml     1970-01-01 
01:00:00.000000000 +0100
+++ new/Radicale-3.5.3/.github/workflows/docker-publish.yml     2025-05-11 
17:15:10.000000000 +0200
@@ -0,0 +1,49 @@
+name: Build and publish Docker image
+
+on:
+  release:
+    types: [published]
+  schedule:
+    - cron: '0 0 * * *'
+  workflow_dispatch:
+
+env:
+  REGISTRY: ghcr.io
+  IMAGE_NAME: ${{ github.repository }}
+
+jobs:
+  build-and-push-image:
+    runs-on: ubuntu-latest
+    permissions:
+      contents: read
+      packages: write
+
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v4
+
+      - name: Log in to the Container registry
+        uses: docker/login-action@v3
+        with:
+          registry: ${{ env.REGISTRY }}
+          username: ${{ github.actor }}
+          password: ${{ secrets.GITHUB_TOKEN }}
+
+      - name: Extract metadata for Docker build
+        id: meta
+        uses: docker/metadata-action@v5
+        with:
+          images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}
+          flavor: latest=true
+          tags: |
+            type=semver,pattern={{version}}
+            type=schedule,prefix=nightly-,pattern={{date 'YYYYMMDD'}}
+            type=raw,enable=${{ github.event_name == 'workflow_dispatch' 
}},value=workflow_dispatch-{{branch}}-{{sha}}
+
+      - name: Build and push Docker image
+        uses: docker/build-push-action@v6
+        with:
+          context: .
+          push: true
+          tags: ${{ steps.meta.outputs.tags }}
+          labels: ${{ steps.meta.outputs.labels }}
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.2/CHANGELOG.md 
new/Radicale-3.5.3/CHANGELOG.md
--- old/Radicale-3.5.2/CHANGELOG.md     2025-04-23 20:38:22.000000000 +0200
+++ new/Radicale-3.5.3/CHANGELOG.md     2025-05-11 17:15:10.000000000 +0200
@@ -1,5 +1,10 @@
 # Changelog
 
+## 3.5.3
+* Add: [auth] htpasswd: support for Argon2 hashes
+* Improve: catch error on calendar collection upload and display problematic 
item content on debug level
+* Add: [auth] urldecode_username: optional decode provided username (e.g. 
encoded email address)
+
 ## 3.5.2
 * Adjust: [auth] ldap: use ldap_user_attr either first element of list or 
directly 
 * Fix: use value of property for time range filter
@@ -8,7 +13,6 @@
 * Extend: log PYTHONPATH on startup if found in environment
 
 ## 3.5.1
-
 * Fix: auth/htpasswd related to detection and use of bcrypt
 * Add: option [auth] ldap_ignore_attribute_create_modify_timestamp for support 
of Authentik LDAP server
 * Extend: [storage] hook supports now placeholder for "cwd" and "path" (and 
catches unsupported placeholders)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.2/DOCUMENTATION.md 
new/Radicale-3.5.3/DOCUMENTATION.md
--- old/Radicale-3.5.2/DOCUMENTATION.md 2025-04-23 20:38:22.000000000 +0200
+++ new/Radicale-3.5.3/DOCUMENTATION.md 2025-05-11 17:15:10.000000000 +0200
@@ -99,11 +99,6 @@
 python3 -m radicale --storage-filesystem-folder=/var/lib/radicale/collections 
--auth-type none
 ```
 
-##### common
-
-Victory! Open <http://localhost:5232> in your browser!
-You can log in with any username and password (no authentication is required 
as long as not proper configured - INSECURE).
-
 #### Windows
 
 The first step is to install Python. Go to
@@ -119,9 +114,14 @@
 python -m radicale --storage-filesystem-folder=~/radicale/collections 
--auth-type none
 ```
 
+##### Common
+
 Victory! Open <http://localhost:5232> in your browser!
 You can log in with any username and password (no authentication is required 
as long as not proper configured - INSECURE).
 
+Just note that default configuration for security reason binds the server to 
`localhost` (IPv4: `127.0.0.1`, IPv6: `::1`).
+See [Addresses](#addresses) and [Configuration/Server](#server) for more.
+
 ### Basic Configuration
 
 Installation instructions can be found in the
@@ -943,6 +943,10 @@
 `sha512` _(>= 3.1.9)_
 : This uses an iterated SHA-512 digest of the password with a salt.
 
+`argon2` _(>= 3.5.3)_
+: This uses an iterated ARGON2 digest of the password with a salt.
+  The installation of **argon2-cffi** is required for this.
+
 `autodetect` _(>= 3.1.9)_
 : This selects autodetection of method per entry.
 
@@ -1183,6 +1187,16 @@
 
 Default: `False`
 
+##### urldecode_username
+
+_(>= 3.5.3)_
+
+URL Decode the username.  When the username is an email, some clients send the 
username URL-encoded (notably iOS devices)
+breaking the authentication process (u...@example.com becomes 
user%40example.com).  This setting will force decoding the username.
+
+Default: `False`
+
+
 #### rights
 
 ##### type
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.2/README.md new/Radicale-3.5.3/README.md
--- old/Radicale-3.5.2/README.md        2025-04-23 20:38:22.000000000 +0200
+++ new/Radicale-3.5.3/README.md        2025-05-11 17:15:10.000000000 +0200
@@ -25,4 +25,4 @@
 * [Radicale Discussions](https://github.com/Kozea/Radicale/discussions)
 
 Before reporting an issue, please check
-* [Radicale Wiki / Reporting 
Issues](https://github.com/Kozea/Radicale/wiki/Reporting-Issues)
+* [Radicale Wiki / Reporting 
Issues](https://github.com/Kozea/Radicale/wiki/01-‐-Reporting-Issues)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.2/config new/Radicale-3.5.3/config
--- old/Radicale-3.5.2/config   2025-04-23 20:38:22.000000000 +0200
+++ new/Radicale-3.5.3/config   2025-05-11 17:15:10.000000000 +0200
@@ -148,8 +148,9 @@
 #htpasswd_filename = /etc/radicale/users
 
 # Htpasswd encryption method
-# Value: plain | bcrypt | md5 | sha256 | sha512 | autodetect
+# Value: plain | bcrypt | md5 | sha256 | sha512 | argon2 | autodetect
 # bcrypt requires the installation of 'bcrypt' module.
+# argon2 requires the installation of 'argon2-cffi' module.
 #htpasswd_encryption = autodetect
 
 # Enable caching of htpasswd file based on size and mtime_ns
@@ -183,6 +184,8 @@
 # Permit overwrite of a collection (global)
 #permit_overwrite_collection = True
 
+# URL Decode the given username (when URL-encoded by the client - useful for 
iOS devices when using email address)
+# urldecode_username = False
 
 [storage]
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.2/pyproject.toml 
new/Radicale-3.5.3/pyproject.toml
--- old/Radicale-3.5.2/pyproject.toml   2025-04-23 20:38:22.000000000 +0200
+++ new/Radicale-3.5.3/pyproject.toml   2025-05-11 17:15:10.000000000 +0200
@@ -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.2"
+version = "3.5.3"
 authors = [{name = "Guillaume Ayoub", email = "guillaume.ay...@kozea.fr"}, 
{name = "Unrud", email = "un...@outlook.com"}, {name = "Peter Bieringer", email 
= "p...@bieringer.de"}]
 license = {text = "GNU GPL v3"}
 description = "CalDAV and CardDAV Server"
@@ -38,8 +38,9 @@
 
 
 [project.optional-dependencies]
-test = ["pytest>=7", "waitress", "bcrypt"]
+test = ["pytest>=7", "waitress", "bcrypt", "argon2-cffi"]
 bcrypt = ["bcrypt"]
+argon2 = ["argon2-cffi"]
 ldap = ["ldap3"]
 
 [project.scripts]
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.2/radicale/app/put.py 
new/Radicale-3.5.3/radicale/app/put.py
--- old/Radicale-3.5.2/radicale/app/put.py      2025-04-23 20:38:22.000000000 
+0200
+++ new/Radicale-3.5.3/radicale/app/put.py      2025-05-11 17:15:10.000000000 
+0200
@@ -21,6 +21,7 @@
 
 import errno
 import itertools
+import logging
 import posixpath
 import re
 import socket
@@ -89,7 +90,15 @@
                     vobject_collection.add(vobject.base.ContentLine("PRODID", 
[], PRODID))
                     item = radicale_item.Item(collection_path=collection_path,
                                               vobject_item=vobject_collection)
-                    item.prepare()
+                    logger.debug("Prepare item with UID '%s'", item.uid)
+                    try:
+                        item.prepare()
+                    except ValueError as e:
+                        if logger.isEnabledFor(logging.DEBUG):
+                            logger.warning("Problem during prepare item with 
UID '%s' (content below): %s\n%s", item.uid, e, item._text)
+                        else:
+                            logger.warning("Problem during prepare item with 
UID '%s' (content suppressed in this loglevel): %s", item.uid, e)
+                        raise
                     items.append(item)
             elif write_whole_collection and tag == "VADDRESSBOOK":
                 for vobject_item in vobject_items:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.2/radicale/auth/__init__.py 
new/Radicale-3.5.3/radicale/auth/__init__.py
--- old/Radicale-3.5.2/radicale/auth/__init__.py        2025-04-23 
20:38:22.000000000 +0200
+++ new/Radicale-3.5.3/radicale/auth/__init__.py        2025-05-11 
17:15:10.000000000 +0200
@@ -34,6 +34,7 @@
 import threading
 import time
 from typing import List, Sequence, Set, Tuple, Union, final
+from urllib.parse import unquote
 
 from radicale import config, types, utils
 from radicale.log import logger
@@ -93,6 +94,7 @@
 class BaseAuth:
 
     _ldap_groups: Set[str] = set([])
+    _urldecode_username: bool
     _lc_username: bool
     _uc_username: bool
     _strip_domain: bool
@@ -119,9 +121,11 @@
         self._lc_username = configuration.get("auth", "lc_username")
         self._uc_username = configuration.get("auth", "uc_username")
         self._strip_domain = configuration.get("auth", "strip_domain")
+        self._urldecode_username = configuration.get("auth", 
"urldecode_username")
         logger.info("auth.strip_domain: %s", self._strip_domain)
         logger.info("auth.lc_username: %s", self._lc_username)
         logger.info("auth.uc_username: %s", self._uc_username)
+        logger.info("auth.urldecode_username: %s", self._urldecode_username)
         if self._lc_username is True and self._uc_username is True:
             raise RuntimeError("auth.lc_username and auth.uc_username cannot 
be enabled together")
         self._auth_delay = configuration.get("auth", "delay")
@@ -219,6 +223,8 @@
             login = login.lower()
         if self._uc_username:
             login = login.upper()
+        if self._urldecode_username:
+            login = unquote(login)
         if self._strip_domain:
             login = login.split('@')[0]
         if self._cache_logins is True:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.2/radicale/auth/htpasswd.py 
new/Radicale-3.5.3/radicale/auth/htpasswd.py
--- old/Radicale-3.5.2/radicale/auth/htpasswd.py        2025-04-23 
20:38:22.000000000 +0200
+++ new/Radicale-3.5.3/radicale/auth/htpasswd.py        2025-05-11 
17:15:10.000000000 +0200
@@ -46,6 +46,9 @@
 When bcrypt is installed:
     - BCRYPT     (htpasswd -B ...) -- Requires htpasswd 2.4.x
 
+When argon2 is installed:
+    - ARGON2     (python -c 'from passlib.hash import argon2; 
print(argon2.using(type="ID").hash("password"))')
+
 """
 
 import functools
@@ -72,8 +75,10 @@
     _htpasswd_not_ok_time: float
     _htpasswd_not_ok_reminder_seconds: int
     _htpasswd_bcrypt_use: int
+    _htpasswd_argon2_use: int
     _htpasswd_cache: bool
     _has_bcrypt: bool
+    _has_argon2: bool
     _encryption: str
     _lock: threading.Lock
 
@@ -89,9 +94,10 @@
         logger.info("auth htpasswd encryption is 
'radicale.auth.htpasswd_encryption.%s'", self._encryption)
 
         self._has_bcrypt = False
+        self._has_argon2 = False
         self._htpasswd_ok = False
         self._htpasswd_not_ok_reminder_seconds = 60 # currently hardcoded
-        (self._htpasswd_ok, self._htpasswd_bcrypt_use, self._htpasswd, 
self._htpasswd_size, self._htpasswd_mtime_ns) = self._read_htpasswd(True, False)
+        (self._htpasswd_ok, self._htpasswd_bcrypt_use, 
self._htpasswd_argon2_use, self._htpasswd, self._htpasswd_size, 
self._htpasswd_mtime_ns) = self._read_htpasswd(True, False)
         self._lock = threading.Lock()
 
         if self._encryption == "plain":
@@ -102,7 +108,8 @@
             self._verify = self._sha256
         elif self._encryption == "sha512":
             self._verify = self._sha512
-        elif self._encryption == "bcrypt" or self._encryption == "autodetect":
+
+        if self._encryption == "bcrypt" or self._encryption == "autodetect":
             try:
                 import bcrypt
             except ImportError as e:
@@ -125,7 +132,33 @@
                 self._verify = self._autodetect
                 if self._htpasswd_bcrypt_use:
                     self._verify_bcrypt = functools.partial(self._bcrypt, 
bcrypt)
-        else:
+
+        if self._encryption == "argon2" or self._encryption == "autodetect":
+            try:
+                import argon2
+                from passlib.hash import argon2  # noqa: F811
+            except ImportError as e:
+                if (self._encryption == "autodetect") and 
(self._htpasswd_argon2_use == 0):
+                    logger.warning("auth htpasswd encryption is 
'radicale.auth.htpasswd_encryption.%s' which can require argon2 module, but 
currently no entries found", self._encryption)
+                else:
+                    raise RuntimeError(
+                        "The htpasswd encryption method 'argon2' or 
'autodetect' requires "
+                        "the argon2 module (entries found: %d)." % 
self._htpasswd_argon2_use) from e
+            else:
+                self._has_argon2 = True
+                if self._encryption == "autodetect":
+                    if self._htpasswd_argon2_use == 0:
+                        logger.info("auth htpasswd encryption is 
'radicale.auth.htpasswd_encryption.%s' and argon2 module found, but currently 
not required", self._encryption)
+                    else:
+                        logger.info("auth htpasswd encryption is 
'radicale.auth.htpasswd_encryption.%s' and argon2 module found (argon2 entries 
found: %d)", self._encryption, self._htpasswd_argon2_use)
+            if self._encryption == "argon2":
+                self._verify = functools.partial(self._argon2, argon2)
+            else:
+                self._verify = self._autodetect
+                if self._htpasswd_argon2_use:
+                    self._verify_argon2 = functools.partial(self._argon2, 
argon2)
+
+        if not hasattr(self, '_verify'):
             raise RuntimeError("The htpasswd encryption method %r is not "
                                "supported." % self._encryption)
 
@@ -144,6 +177,9 @@
         else:
             return ("BCRYPT", 
bcrypt.checkpw(password=password.encode('utf-8'), 
hashed_password=hash_value.encode()))
 
+    def _argon2(self, argon2: Any, hash_value: str, password: str) -> 
tuple[str, bool]:
+        return ("ARGON2", argon2.verify(password, hash_value.strip()))
+
     def _md5apr1(self, hash_value: str, password: str) -> tuple[str, bool]:
         if self._encryption == "autodetect" and len(hash_value) != 37:
             return self._plain_fallback("MD5-APR1", hash_value, password)
@@ -169,6 +205,9 @@
         elif re.match(r"^\$2(a|b|x|y)?\$", hash_value):
             # BCRYPT
             return self._verify_bcrypt(hash_value, password)
+        elif re.match(r"^\$argon2(i|d|id)\$", hash_value):
+            # ARGON2
+            return self._verify_argon2(hash_value, password)
         elif hash_value.startswith("$5$", 0, 3):
             # SHA-256
             return self._sha256(hash_value, password)
@@ -178,7 +217,7 @@
         else:
             return self._plain(hash_value, password)
 
-    def _read_htpasswd(self, init: bool, suppress: bool) -> Tuple[bool, int, 
dict, int, int]:
+    def _read_htpasswd(self, init: bool, suppress: bool) -> Tuple[bool, int, 
int, dict, int, int]:
         """Read htpasswd file
 
         init == True: stop on error
@@ -189,6 +228,7 @@
         """
         htpasswd_ok = True
         bcrypt_use = 0
+        argon2_use = 0
         if (init is True) or (suppress is True):
             info = "Read"
         else:
@@ -237,6 +277,14 @@
                                                 logger.warning("htpasswd file 
contains bcrypt digest login: '%s' (line: %d / ignored because module is not 
loaded)", login, line_num)
                                                 skip = True
                                                 htpasswd_ok = False
+                                    if re.match(r"^\$argon2(i|d|id)\$", 
digest):
+                                        if init is True:
+                                            argon2_use += 1
+                                        else:
+                                            if self._has_argon2 is False:
+                                                logger.warning("htpasswd file 
contains argon2 digest login: '%s' (line: %d / ignored because module is not 
loaded)", login, line_num)
+                                                skip = True
+                                                htpasswd_ok = False
                             if skip is False:
                                 htpasswd[login] = digest
                                 entries += 1
@@ -259,7 +307,7 @@
             self._htpasswd_not_ok_time = 0
         else:
             self._htpasswd_not_ok_time = time.time()
-        return (htpasswd_ok, bcrypt_use, htpasswd, htpasswd_size, 
htpasswd_mtime_ns)
+        return (htpasswd_ok, bcrypt_use, argon2_use, htpasswd, htpasswd_size, 
htpasswd_mtime_ns)
 
     def _login(self, login: str, password: str) -> str:
         """Validate credentials.
@@ -280,7 +328,7 @@
                 htpasswd_size = os.stat(self._filename).st_size
                 htpasswd_mtime_ns = os.stat(self._filename).st_mtime_ns
                 if (htpasswd_size != self._htpasswd_size) or 
(htpasswd_mtime_ns != self._htpasswd_mtime_ns):
-                    (self._htpasswd_ok, self._htpasswd_bcrypt_use, 
self._htpasswd, self._htpasswd_size, self._htpasswd_mtime_ns) = 
self._read_htpasswd(False, False)
+                    (self._htpasswd_ok, self._htpasswd_bcrypt_use, 
self._htpasswd_argon2_use, self._htpasswd, self._htpasswd_size, 
self._htpasswd_mtime_ns) = self._read_htpasswd(False, False)
                     self._htpasswd_not_ok_time = 0
 
             # log reminder of problemantic file every interval
@@ -298,7 +346,7 @@
                 login_ok = True
         else:
             # read file on every request
-            (htpasswd_ok, htpasswd_bcrypt_use, htpasswd, htpasswd_size, 
htpasswd_mtime_ns) = self._read_htpasswd(False, True)
+            (htpasswd_ok, htpasswd_bcrypt_use, htpasswd_argon2_use, htpasswd, 
htpasswd_size, htpasswd_mtime_ns) = self._read_htpasswd(False, True)
             if htpasswd.get(login):
                 digest = htpasswd[login]
                 login_ok = True
@@ -307,7 +355,7 @@
             try:
                 (method, password_ok) = self._verify(digest, password)
             except ValueError as e:
-                logger.error("Login verification failed for user: '%s' 
(htpasswd/%s) with errror '%s'", login, self._encryption, e)
+                logger.error("Login verification failed for user: '%s' 
(htpasswd/%s) with error '%s'", login, self._encryption, e)
                 return ""
             if password_ok:
                 logger.debug("Login verification successful for user: '%s' 
(htpasswd/%s/%s)", login, self._encryption, method)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.2/radicale/config.py 
new/Radicale-3.5.3/radicale/config.py
--- old/Radicale-3.5.2/radicale/config.py       2025-04-23 20:38:22.000000000 
+0200
+++ new/Radicale-3.5.3/radicale/config.py       2025-05-11 17:15:10.000000000 
+0200
@@ -342,6 +342,10 @@
         ("lc_username", {
             "value": "False",
             "help": "convert username to lowercase, must be true for 
case-insensitive auth providers",
+            "type": bool}),
+        ("urldecode_username", {
+            "value": "False",
+            "help": "url-decode the username, set to True when clients send 
url-encoded email address as username",
             "type": bool})])),
     ("rights", OrderedDict([
         ("type", {
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' 
old/Radicale-3.5.2/radicale/storage/multifilesystem/__init__.py 
new/Radicale-3.5.3/radicale/storage/multifilesystem/__init__.py
--- old/Radicale-3.5.2/radicale/storage/multifilesystem/__init__.py     
2025-04-23 20:38:22.000000000 +0200
+++ new/Radicale-3.5.3/radicale/storage/multifilesystem/__init__.py     
2025-05-11 17:15:10.000000000 +0200
@@ -148,11 +148,11 @@
         super().__init__(configuration)
         logger.info("Storage location: %r", self._filesystem_folder)
         if not os.path.exists(self._filesystem_folder):
-            logger.warning("Storage location: %r not existing, create now", 
self._filesystem_folder)
+            logger.warning("Storage location: %r does not exist, creating 
now", self._filesystem_folder)
             self._makedirs_synced(self._filesystem_folder)
         logger.info("Storage location subfolder: %r", 
self._get_collection_root_folder())
         if not os.path.exists(self._get_collection_root_folder()):
-            logger.warning("Storage location subfolder: %r not existing, 
create now", self._get_collection_root_folder())
+            logger.warning("Storage location subfolder: %r does not exist, 
creating now", self._get_collection_root_folder())
             self._makedirs_synced(self._get_collection_root_folder())
         logger.info("Storage cache subfolder usage for 'item': %s", 
self._use_cache_subfolder_for_item)
         logger.info("Storage cache subfolder usage for 'history': %s", 
self._use_cache_subfolder_for_history)
@@ -176,7 +176,7 @@
         if self._use_cache_subfolder_for_item is True or 
self._use_cache_subfolder_for_history is True or 
self._use_cache_subfolder_for_synctoken is True:
             logger.info("Storage cache subfolder: %r", 
self._get_collection_cache_folder())
             if not os.path.exists(self._get_collection_cache_folder()):
-                logger.warning("Storage cache subfolder: %r not existing, 
create now", self._get_collection_cache_folder())
+                logger.warning("Storage cache subfolder: %r does not exist, 
creating now", self._get_collection_cache_folder())
                 self._makedirs_synced(self._get_collection_cache_folder())
         if sys.platform != "win32":
             if not self._folder_umask:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.2/radicale/tests/test_auth.py 
new/Radicale-3.5.3/radicale/tests/test_auth.py
--- old/Radicale-3.5.2/radicale/tests/test_auth.py      2025-04-23 
20:38:22.000000000 +0200
+++ new/Radicale-3.5.3/radicale/tests/test_auth.py      2025-05-11 
17:15:10.000000000 +0200
@@ -49,6 +49,15 @@
     else:
         has_bcrypt = 1
 
+    # test for available argon2 module
+    try:
+        import argon2
+        from passlib.hash import argon2  # noqa: F811
+    except ImportError:
+        has_argon2 = 0
+    else:
+        has_argon2 = 1
+
     def _test_htpasswd(self, htpasswd_encryption: str, htpasswd_content: str,
                        test_matrix: Union[str, Iterable[Tuple[str, str, bool]]]
                        = "ascii") -> None:
@@ -147,6 +156,18 @@
     def test_htpasswd_bcrypt_unicode(self) -> None:
         self._test_htpasswd("bcrypt", 
"😀:$2y$10$Oyz5aHV4MD9eQJbk6GPemOs4T6edK6U9Sqlzr.W1mMVCS8wJUftnW", "unicode")
 
+    @pytest.mark.skipif(has_argon2 == 0, reason="No argon2 module installed")
+    def test_htpasswd_argon2_i(self) -> None:
+        self._test_htpasswd("argon2", 
"tmp:$argon2i$v=19$m=65536,t=3,p=4$NgZg7F1rzRkDoNSaMwag9A$qmsvMKEn5zOXHm8e3O5fKzzcRo0UESwaDr/cETe5YPI")
+
+    @pytest.mark.skipif(has_argon2 == 0, reason="No argon2 module installed")
+    def test_htpasswd_argon2_d(self) -> None:
+        self._test_htpasswd("argon2", 
"tmp:$argon2d$v=19$m=65536,t=3,p=4$ufe+txYiJKR0zlkLwVirVQ$MjGqRyVLes38hA6CEOkloMcTYCuLjxCKgIjtfYZ3iSM")
+
+    @pytest.mark.skipif(has_argon2 == 0, reason="No argon2 module installed")
+    def test_htpasswd_argon2_id(self) -> None:
+        self._test_htpasswd("argon2", 
"tmp:$argon2id$v=19$m=65536,t=3,p=4$t7bWuneOkdIa45xTqjXGmA$ORnRJyz9kHogJs6bDgZrTBPlzi4+p023PSEABb3xX1g")
+
     def test_htpasswd_multi(self) -> None:
         self._test_htpasswd("plain", "ign:ign\ntmp:bepo")
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.2/radicale/utils.py 
new/Radicale-3.5.3/radicale/utils.py
--- old/Radicale-3.5.2/radicale/utils.py        2025-04-23 20:38:22.000000000 
+0200
+++ new/Radicale-3.5.3/radicale/utils.py        2025-05-11 17:15:10.000000000 
+0200
@@ -28,8 +28,8 @@
 _T_co = TypeVar("_T_co", covariant=True)
 
 RADICALE_MODULES: Sequence[str] = ("radicale", "vobject", "passlib", 
"defusedxml",
-                                   "dateutil",
                                    "bcrypt",
+                                   "argon2-cffi",
                                    "pika",
                                    "ldap",
                                    "ldap3",
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/Radicale-3.5.2/setup.py.legacy 
new/Radicale-3.5.3/setup.py.legacy
--- old/Radicale-3.5.2/setup.py.legacy  2025-04-23 20:38:22.000000000 +0200
+++ new/Radicale-3.5.3/setup.py.legacy  2025-05-11 17:15:10.000000000 +0200
@@ -20,7 +20,7 @@
 
 # When the version is updated, a new section in the CHANGELOG.md file must be
 # added too.
-VERSION = "3.5.2"
+VERSION = "3.5.3"
 
 with open("README.md", encoding="utf-8") as f:
     long_description = f.read()
@@ -41,8 +41,9 @@
                     "requests",
                     ]
 bcrypt_requires = ["bcrypt"]
+argon2_requires = ["argon2-cffi"]
 ldap_requires = ["ldap3"]
-test_requires = ["pytest>=7", "waitress", *bcrypt_requires]
+test_requires = ["pytest>=7", "waitress", *bcrypt_requires, *argon2_requires]
 
 setup(
     name="Radicale",
@@ -60,7 +61,7 @@
     package_data={"radicale": [*web_files, "py.typed"]},
     entry_points={"console_scripts": ["radicale = radicale.__main__:run"]},
     install_requires=install_requires,
-    extras_require={"test": test_requires, "bcrypt": bcrypt_requires, "ldap": 
ldap_requires},
+    extras_require={"test": test_requires, "bcrypt": bcrypt_requires, 
"argon2": argon2_requires, "ldap": ldap_requires},
     keywords=["calendar", "addressbook", "CalDAV", "CardDAV"],
     python_requires=">=3.9.0",
     classifiers=[

Reply via email to