---
name: dav_list_addressbooks
kind: function
lang: py
domain: infra
version: "1.0.0"
purity: impure
signature: "def dav_list_addressbooks(base_url: str, username: str, password: str, contacts_home: str, *, timeout_s: float = 20.0, verify_tls: bool = True) -> dict"
description: "Lista las colecciones de libreta de contactos CardDAV bajo un contacts-home en UNA peticion PROPFIND Depth:1. Devuelve solo las colecciones hijas que son libretas CardDAV de verdad (resourcetype {urn:ietf:params:xml:ns:carddav}addressbook), cada una con su href, su displayname (DAV) y su descripcion (addressbook-description de CardDAV) si el servidor la expone. El propio contacts-home (coleccion plana sin el resourcetype addressbook) y cualquier coleccion no-addressbook se excluyen. Considera solo los propstat con estado 2xx para no leer props marcadas 404. Construye Authorization: Basic base64(user:pass) a mano y parsea el multistatus con regex (parser puro _parse_multistatus aislado para testeo sin red). verify_tls=True por defecto. Maneja errores sin lanzar. Solo stdlib (urllib, base64, re, ssl, html). Es la analoga CardDAV de dav_list_calendars. Probada contra Xandikos."
tags: [dav, carddav, addressbook, addressbooks, contacts, propfind, displayname, description, ingest, http, infra]
uses_functions: []
uses_types: []
returns: []
returns_optional: false
error_type: "error_go_core"
imports: [base64, html, 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: contacts_home
    desc: "ruta del contacts-home del usuario (p.ej. '/enmanuel/contacts/'). Las libretas de contactos cuelgan de el."
  - 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, addressbooks:[{href:str, name:str, description:str|None}, ...]} con un elemento por libreta de contactos, ordenadas por nombre. description es el addressbook-description de CardDAV o None. En error (sin lanzar): {status:'error', error:str, http_status:int|None}."
tested: true
tests:
  - "test_devuelve_dos_libretas_descarta_home"
  - "test_ordenadas_por_nombre"
  - "test_extrae_nombre_y_descripcion"
  - "test_descripcion_ausente_es_none"
  - "test_ignora_props_404"
  - "test_multistatus_vacio_devuelve_lista_vacia"
  - "test_home_sin_libretas_solo_home"
test_file_path: "python/functions/infra/dav_list_addressbooks_test.py"
file_path: "python/functions/infra/dav_list_addressbooks.py"
---

## Ejemplo

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

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

res = dav_list_addressbooks(
    base_url="https://dav-eedeb681c4ab89ab8e444ac9.organic-machine.com",
    username="enmanuel",
    password=pw,
    contacts_home="/enmanuel/contacts/",
)
for a in res["addressbooks"]:
    print(a["name"], a["href"], a["description"])
# Personal /enmanuel/contacts/personal/ Contactos personales & familia
# contacts /enmanuel/contacts/contacts/ None
```

## Cuando usarla

Cuando un ingest necesita recorrer **todas** las libretas de contactos del usuario,
no solo la principal: el contacts-home CardDAV puede tener varias colecciones
(personal, trabajo, familia, ...) y necesitas el href de cada una para luego
volcar sus vCards con `dav_list_resources` / `dav_get_collection`. Tambien sirve
para un selector de libreta en una UI (nombre + descripcion). Devuelve los hrefs
sin que el caller los conozca de antemano ni tenga que distinguir el contacts-home
de las libretas reales. Es la analoga CardDAV de `dav_list_calendars`.

## Gotchas

- **Impura: red + auth.** Lectura remota real sobre TLS; el password viene de
  `pass` (`dav/xandikos-enmanuel`) y no se logea. `verify_tls=True` por defecto;
  no desactivar salvo entorno de prueba.
- **Depende de que el servidor exponga `resourcetype` en el PROPFIND.** Filtra por
  el resourcetype `carddav:addressbook`: el propio contacts-home es una coleccion
  plana (sin ese resourcetype) y queda fuera, igual que carpetas intermedias. Si
  un servidor no devuelve `resourcetype`, ninguna coleccion pasa el filtro y la
  lista sale vacia. Si tu servidor anida libretas mas profundo que Depth:1, llama
  con `contacts_home` apuntando al nivel correcto.
- La `description` es el `addressbook-description` de CardDAV
  (`urn:ietf:params:xml:ns:carddav`), que Xandikos puede no tener seteado
  (devuelve None).
- Solo lee los `<propstat>` con estado 2xx para no confundir un
  `<addressbook-description/>` vacio de un propstat 404 con una descripcion real.
