---
name: dav_collection_ctag
kind: function
lang: py
domain: infra
version: "1.0.0"
purity: impure
signature: "def dav_collection_ctag(base_url: str, username: str, password: str, collection_path: str, *, timeout_s: float = 10.0, verify_tls: bool = True) -> dict"
description: "Lee el ctag (token de version) de una coleccion DAV en UNA peticion PROPFIND Depth:0 barata. Pide el getctag de CalendarServer (http://calendarserver.org/ns/) y, como respaldo, el getetag DAV de la propia coleccion. El ctag cambia solo cuando cambia algun recurso de la coleccion: comparandolo con un ctag cacheado se decide si recargar el contenido (REPORT) o servir de cache sin tocar la red. Construye el header Authorization: Basic base64(user:pass) a mano con stdlib y parsea el multistatus con regex simple. verify_tls=True por defecto. Maneja errores sin lanzar. Solo stdlib (urllib, base64, re, ssl). Probado contra Xandikos."
tags: [dav, carddav, caldav, ctag, getctag, propfind, cache, sync, http, infra]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: [base64, re, ssl, urllib.error, urllib.request]
params:
  - name: base_url
    desc: "URL base del servidor DAV (p.ej. 'https://dav-eedeb681c4ab89ab8e444ac9.organic-machine.com')."
  - name: username
    desc: "usuario para HTTP Basic auth (p.ej. 'enmanuel')."
  - name: password
    desc: "contrasena para HTTP Basic auth. Resolver desde pass con pass_get_secret, nunca hardcodear."
  - name: collection_path
    desc: "ruta de la coleccion (CardDAV '/enmanuel/contacts/addressbook/' o CalDAV '/enmanuel/calendars/calendar/')."
  - name: timeout_s
    desc: "timeout de la peticion HTTP en segundos. Default 10.0 (la respuesta es minuscula)."
  - name: verify_tls
    desc: "si True (default) verifica el certificado TLS. No desactivar salvo entorno de prueba."
output: "dict. En exito: {status:'ok', http_status:int, ctag:str} donde ctag es el getctag de CalendarServer si el servidor lo expone, o el getetag DAV de la coleccion como respaldo. En error (sin lanzar): {status:'error', error:str, http_status:int|None}."
tested: true
tests:
  - "test_construye_propfind_depth_0"
  - "test_basic_auth_header_correcto"
  - "test_devuelve_getctag"
  - "test_fallback_a_getetag"
  - "test_sin_ctag_ni_etag_devuelve_error"
  - "test_httperror_devuelve_status_error"
test_file_path: "python/functions/infra/dav_collection_ctag_test.py"
file_path: "python/functions/infra/dav_collection_ctag.py"
---

## Ejemplo

```python
import sys
sys.path.insert(0, "python/functions")
from infra.pass_get_secret import pass_get_secret
from infra.dav_collection_ctag import dav_collection_ctag

pw = pass_get_secret("dav/xandikos-enmanuel")["value"]  # NO logear

res = dav_collection_ctag(
    base_url="https://dav-eedeb681c4ab89ab8e444ac9.organic-machine.com",
    username="enmanuel",
    password=pw,
    collection_path="/enmanuel/calendars/calendar/",
)
print(res["status"], res["ctag"])  # ok e8b39a8b180d25a674b35f0fee3013992b59e51e

# Patron de cache: si el ctag no cambio, sirve del disco sin descargar.
if res["ctag"] == cached_ctag:
    return cached_payload
# ...si cambio, recargar con dav_get_collection y guardar el nuevo ctag.
```

## Cuando usarla

Antes de descargar una coleccion DAV completa: pides el ctag (peticion
minuscula, ~10ms) y lo comparas con el ctag de tu cache. Si coincide, sirves de
cache sin tocar la red (arranque instantaneo); si difiere, recargas con
`dav_get_collection` y guardas el nuevo ctag. Es el primitivo de validacion de
cache para CardDAV/CalDAV: una sola comprobacion barata decide si la copia local
sigue vigente.

## Gotchas

- El `getctag` es la extension de CalendarServer (`http://calendarserver.org/ns/`),
  ampliamente soportada (Xandikos la expone). Si el servidor no la implementa, la
  funcion cae al `getetag` DAV de la coleccion, que en Xandikos tambien cambia al
  cambiar cualquier recurso — sirve igual como token de version.
- El ctag es OPACO: no lo interpretes, solo comparalo por igualdad con el que
  guardaste. No asumas orden ni formato (Xandikos usa un hash hex; otros
  servidores usan timestamps u otros formatos).
- No garantiza deteccion de cambios sub-recurso (etag por recurso): solo dice si
  ALGO cambio en la coleccion. Para sync incremental fino combina con
  `dav_list_resources` (mapa href->etag).
- Lectura remota real sobre TLS; password de `pass`, no se logea.
