"""Plain authenticated WebDAV file fetch -- whiteboard frame mode's way of pulling one specific file (a Nextcloud Whiteboard .whiteboard, or any other WebDAV server's file, this isn't Nextcloud-specific) out of a user's account. Deliberately just "GET this URL with Basic auth", the same shape as calendar_feed.py's plain ICS fetch -- no discovery, no account-wide browsing, since the caller already has (or pastes) the exact file URL, unlike caldav_client.py's calendar-account discovery flow which exists because a CalDAV account can hold several calendars worth picking between. Pure functions -- no ORM, no FastAPI Depends -- same testability philosophy as calendar_feed.py/caldav_client.py. """ from __future__ import annotations import httpx HTTP_TIMEOUT_S = 15.0 FETCH_MAX_BYTES = 10 * 1024 * 1024 # a whiteboard scene is KB, not MB -- sanity cap, not an expected size class WebDavError(Exception): """Fetch failed -- network, auth, a missing file, or an oversized response. Raised loudly; callers decide what to do.""" def fetch_file(url: str, username: str, password: str) -> bytes: """The raw bytes of one WebDAV file, HTTP Basic auth. That's the whole protocol surface whiteboard mode needs -- Basic auth over plain HTTP GET is what WebDAV file access boils down to once you already have the exact URL, no PROPFIND/discovery involved.""" try: with httpx.stream("GET", url, auth=(username, password), timeout=HTTP_TIMEOUT_S, follow_redirects=True) as resp: resp.raise_for_status() chunks = [] total = 0 for chunk in resp.iter_bytes(): total += len(chunk) if total > FETCH_MAX_BYTES: raise WebDavError(f"File exceeds {FETCH_MAX_BYTES} bytes") chunks.append(chunk) return b"".join(chunks) except httpx.HTTPError as e: raise WebDavError(str(e)) from e