---
name: dav_list_resources
kind: function
lang: py
domain: infra
version: "1.0.0"
purity: impure
signature: "def dav_list_resources(base_url: str, username: str, password: str, collection_path: str, *, timeout_s: float = 20.0, verify_tls: bool = True) -> dict"
description: "Lista los recursos de una coleccion DAV (CardDAV o CalDAV) via PROPFIND Depth:1 con HTTP Basic auth. Construye el header Authorization: Basic base64(user:pass) a mano con stdlib. Parsea el XML multistatus con regex simple (sin parser XML externo) y devuelve los hrefs + getetag de cada recurso, excluyendo la propia coleccion. verify_tls=True por defecto. Maneja errores sin lanzar. Solo stdlib (urllib, base64, re, ssl). Probado contra Xandikos."
tags: [dav, carddav, caldav, propfind, list, 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 a listar (CardDAV '/enmanuel/contacts/addressbook/' o CalDAV '/enmanuel/calendars/calendar/')."
  - name: timeout_s
    desc: "timeout de la peticion HTTP en segundos. Default 20.0."
  - 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, resources:[{href:str, etag:str|None}, ...]} con un elemento por recurso de la coleccion (excluida la propia coleccion). En error (sin lanzar): {status:'error', error:str, http_status:int|None}."
tested: true
tests:
  - "test_construye_request_propfind_depth_1"
  - "test_basic_auth_header_correcto"
  - "test_parsea_hrefs_y_etags_del_multistatus"
  - "test_excluye_la_propia_coleccion"
  - "test_httperror_devuelve_status_error"
test_file_path: "python/functions/infra/dav_list_resources_test.py"
file_path: "python/functions/infra/dav_list_resources.py"
---

## Ejemplo

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

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

res = dav_list_resources(
    base_url="https://dav-eedeb681c4ab89ab8e444ac9.organic-machine.com",
    username="enmanuel",
    password=pw,
    collection_path="/enmanuel/contacts/addressbook/",
)
print(res["status"], len(res["resources"]))
# ok 820
print(res["resources"][0])  # {"href": "/enmanuel/contacts/addressbook/abc.vcf", "etag": '"..."'}
```

## Cuando usarla

Cuando quieres enumerar lo que ya hay en una coleccion CardDAV/CalDAV: contar
contactos/eventos importados, verificar una migracion, o construir un mapa
href->etag para sync incremental. Sirve igual para libretas de direcciones y
calendarios (PROPFIND es generico). Combinala con `dav_get_resource` para
descargar el contenido de cada href.

## Gotchas

- Usa PROPFIND Depth:1 (no addressbook-query REPORT): lista TODOS los recursos
  hijos de la coleccion. Para colecciones enormes la respuesta XML puede ser
  grande; el timeout default es 20s.
- El parseo es regex simple sobre el multistatus, no un parser XML completo: es
  robusto para la salida estandar de Xandikos pero podria fallar con servidores
  que devuelvan XML muy exotico. La intencion es KISS sin dependencias.
- Excluye la propia coleccion comparando el ultimo segmento del href; si tu
  coleccion y un recurso comparten exactamente el ultimo segmento (raro), ese
  recurso se omitiria.
- Lectura remota real sobre TLS; password de `pass`, no se logea.
