# vCard von iCloud auf Radicale (CalDAV/CardDAV) hochladen

<article id="bkmrk-dieses-skript-liest-">Dieses Skript liest Kontakte aus einer `.vcf`-Datei (z. B. iCloud-Export) ein, entfernt alle nicht standardkonformen Felder und lädt jeden Kontakt als einzelne `.vcf`-Datei in einen bestehenden Radicale CardDAV-Server hoch.

Radicale speichert Adressbücher im CardDAV-Standard über WebDAV. Apple-exportierte vCards enthalten oft proprietäre Felder wie `item1.X-ADDRESSING-GRAMMAR` oder `X-APPLE-*`, die von Radicale nicht akzeptiert werden. Das Skript sorgt dafür, dass nur die Felder übrigbleiben, die auch RFC-konform sind.

Es installiert automatisch die benötigten Python-Pakete (`vobject` und `requests`), bereinigt die Einträge und führt den Upload per `PUT`-Anfrage aus. Für die Ausführung empfiehlt sich ein virtuelles Environment, damit Abhängigkeiten voneinander getrennt bleiben.

---

### Vorbereitung

Lege die exportierte vCard-Datei (z. B. `icloud_contacts.vcf`) in dasselbe Verzeichnis wie das Skript. Stelle sicher, dass die Datei lesbar ist und gültige `BEGIN:VCARD ... END:VCARD`-Blöcke enthält.

---

### Ablauf

```

┌─────────────────────┐
│   CardDAV-Server    │
│   REPORT vCards     │
└─────────────────────┘
           │
           ▼
┌─────────────────────┐
│   Python-Skript     │
│  vCards parsen      │
│  X-Felder entfernen │
│  UID vergeben       │
└─────────────────────┘
           │
           ▼
┌─────────────────────┐
│   CardDAV-Server    │
│     PUT vCards      │
└─────────────────────┘
  
```

---

### Das Python-Skript

```python

import subprocess
import sys

def install(package):
    subprocess.check_call([sys.executable, "-m", "pip", "install", package])

try:
    import vobject
except ImportError:
    install("vobject")
    import vobject

try:
    import requests
except ImportError:
    install("requests")
    import requests

import uuid
from requests.auth import HTTPBasicAuth

# Konfiguration
vcf_datei = "icloud_contacts.vcf"
carddav_url = "https://deinserver.de/benutzer/contacts/"
username = "DEIN_USERNAME"
password = "DEIN_PASSWORT"

def ist_standard_property(name):
    name = name.upper()
    return name in ["FN", "N", "TEL", "EMAIL", "ORG", "UID"]

def lade_und_bereinige_vcards(pfad):
    with open(pfad, "r", encoding="utf-8") as f:
        content = f.read()
    rohkarten = content.strip().split("END:VCARD")
    rohkarten = [c + "END:VCARD" for c in rohkarten if "BEGIN:VCARD" in c]

    bereinigt = []
    for raw in rohkarten:
        try:
            card = vobject.readOne(raw)
            new_card = vobject.vCard()
            new_card.add("uid")
            new_card.uid.value = str(uuid.uuid4())
            for prop in card.getChildren():
                name = prop.name.upper()
                if name.startswith("ITEM") or name.startswith("X-") or name in ["PRODID", "REV", "PHOTO"]:
                    continue
                if ist_standard_property(name):
                    new_card.add(prop)
            bereinigt.append(new_card.serialize())
        except Exception as e:
            print("Fehler beim Parsen eines Kontakts:", e)
    return bereinigt

def upload_to_carddav(cards):
    hochgeladen = 0
    for card_data in cards:
        uid = str(uuid.uuid4())
        ziel_url = carddav_url + uid + ".vcf"
        response = requests.put(
            ziel_url,
            data=card_data,
            headers={"Content-Type": "text/vcard"},
            auth=HTTPBasicAuth(username, password)
        )
        if response.status_code in [200, 201, 204]:
            print("Hochgeladen:", ziel_url)
            hochgeladen += 1
        else:
            print("Fehler (", response.status_code, ") bei", ziel_url)
            print(response.text)
    print(hochgeladen, "Kontakte erfolgreich hochgeladen.")

def main():
    bereinigt = lade_und_bereinige_vcards(vcf_datei)
    if not bereinigt:
        print("Keine gültigen vCards gefunden.")
        return
    upload_to_carddav(bereinigt)

if __name__ == "__main__":
    main()
  
```

---

### Verwendung

Führe das Skript in einem Terminal oder einer Konsole aus:

```
python carddav_upload.py
```

Es bereinigt alle Kontakte und lädt sie anschließend einzeln hoch. Nicht standardkonforme Felder werden automatisch entfernt.

---

### Hintergrund

vCard (RFC 6350) ist der Standard für den Austausch elektronischer Visitenkarten. CardDAV erweitert WebDAV um dieses Format und erlaubt das Speichern von `.vcf`-Dateien in Adressbüchern.

Radicale implementiert CardDAV und akzeptiert nur RFC-konforme vCard-Felder. Das Skript stellt sicher, dass alle importierten Kontakte diesen Vorgaben entsprechen und nicht durch Apple-spezifische Erweiterungen fehlschlagen.

---

Wenn die CardDAV-URL nicht bekannt ist, hilft ein Client wie DAVx⁵ oder das Radicale-Debug-Log, den korrekten Pfad zu ermitteln. Die URL endet meist auf `/username/contacts/`.

</article>