fix: CalDAV fuer DAVx5 - Well-Known intern dispatchen, mehr Properties

Aenderungen fuer besseren DAVx5-Support:

* /.well-known/caldav reagiert jetzt direkt auf PROPFIND/OPTIONS
  ohne Redirect-Zickerei. GET/HEAD redirecten weiterhin auf /dav/
  als visuelle Fallback.
* strict_slashes app-weit aus: /dav und /dav/ sind gleichwertig,
  ebenso die Unterpfade. DAVx5 nutzt beides gemischt.
* Jede DAV-Response traegt jetzt den DAV-Header (1, 2, 3,
  calendar-access), nicht nur OPTIONS.
* Kalender-Response enthaelt jetzt supported-report-set mit
  calendar-query + calendar-multiget (DAVx5 prueft das).
* current-user-privilege-set wird mit konkreten Privilegien gefuellt
  (read, write, write-properties, write-content, bind, unbind)
  statt leer.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Stefan Hacker
2026-04-12 13:50:50 +02:00
parent c4b381c5e9
commit 3f0d823dbf
2 changed files with 36 additions and 10 deletions
+19 -8
View File
@@ -69,6 +69,9 @@ def create_app(config_class=Config):
app = Flask(__name__)
app.config.from_object(config_class)
# DAV-Clients setzen Trailing-Slashes uneinheitlich - daher deaktivieren
# wir die strikte Pruefung app-weit. Betrifft alle Blueprints.
app.url_map.strict_slashes = False
# Ensure data directories exist
Path(app.config['UPLOAD_PATH']).mkdir(parents=True, exist_ok=True)
@@ -92,16 +95,24 @@ def create_app(config_class=Config):
app.register_blueprint(dav_bp)
# Well-known URLs for CalDAV/CardDAV auto-discovery (iOS, DAVx5, etc.).
# RFC 6764 requires 301; PROPFIND auf /.well-known/caldav gibt manche
# Clients direkt ab (Method-Preservation) - also auch dort akzeptieren
# und auf /dav/ weiterleiten. Alle HTTP-Methoden werden registriert.
@app.route('/.well-known/caldav', methods=['GET', 'PROPFIND', 'OPTIONS', 'HEAD'])
def wellknown_caldav():
# 301-Redirect bei PROPFIND ist bei einigen Clients zickig, deshalb
# delegieren wir intern direkt an die DAV-Handler, statt zu redirecten.
from app.dav.caldav import propfind as dav_propfind, options as dav_options
@app.route('/.well-known/caldav', methods=['GET', 'HEAD'])
@app.route('/.well-known/carddav', methods=['GET', 'HEAD'])
def wellknown_redirect():
return redirect('/dav/', code=301)
@app.route('/.well-known/carddav', methods=['GET', 'PROPFIND', 'OPTIONS', 'HEAD'])
def wellknown_carddav():
return redirect('/dav/', code=301)
@app.route('/.well-known/caldav', methods=['PROPFIND'])
@app.route('/.well-known/carddav', methods=['PROPFIND'])
def wellknown_propfind():
return dav_propfind(subpath='')
@app.route('/.well-known/caldav', methods=['OPTIONS'])
@app.route('/.well-known/carddav', methods=['OPTIONS'])
def wellknown_options():
return dav_options()
# iCal export (public, no auth)
@app.route('/ical/<token>')