egroupware/doc/REST-CalDAV-CardDAV
2021-09-25 12:38:08 +02:00
..
README.md implement and document PATCH 2021-09-25 12:38:08 +02:00

EGroupware CalDAV/CardDAV server and REST API

CalDAV/CardDAV is build on HTTP and WebDAV, implementing the following additional RFCs containing documentation of the protocol:

Path / URL layout for CalDAV/CardDAV and REST is identical

One can use the following URLs relative (!) to https://example.org/egroupware/groupdav.php

  • / base of Cal|Card|GroupDAV tree, only certain clients (KDE, Apple) can autodetect folders from here
  • /principals/ principal-collection-set for WebDAV ACL
  • /principals/users/<username>/
  • /principals/groups/<groupname>/
  • /<username>/ users home-set with
  • /<username>/addressbook/ addressbook of user or group given the user has rights to view it
  • /<current-username>/addressbook-<other-username>/ shared addressbooks from other user or group
  • /<current-username>/addressbook-accounts/ all accounts current user has rights to see
  • /<username>/calendar/ calendar of user given the user has rights to view it
  • /<username>/calendar/?download download whole calendar as .ics file (GET request!)
  • /<current-username>/calendar-<other-username>/ shared calendar from other user or group (only current !)
  • /<username>/inbox/ scheduling inbox of user
  • /<username>/outbox/ scheduling outbox of user
  • /<username>/infolog/ InfoLog's of user given the user has rights to view it
  • /addressbook/ all addressbooks current user has rights to, announced as directory-gateway now
  • /addressbook-accounts/ all accounts current user has rights to see
  • /calendar/ calendar of current user
  • /infolog/ infologs of current user
  • /(resources|locations)/<resource-name>/calendar calendar of a resource/location, if user has rights to view
  • /<current-username>/(resource|location)-<resource-name> shared calendar from a resource/location

Shared addressbooks or calendars are only shown in the users home-set, if he subscribed to it via his CalDAV preferences!

Calling one of the above collections with a GET request / regular browser generates an automatic index from the data of a allprop PROPFIND, allow browsing CalDAV/CardDAV tree with a regular browser.

REST API: using EGroupware CalDAV/CardDAV server with JSON

currently implemented only for contacts!

Following RFCs / drafts used/planned for JSON encoding of ressources

Supported request methods and examples

  • GET to collections with an Accept: application/json header return all resources (similar to WebDAV PROPFIND)
Example: Getting all entries of a given users addessbook
curl https://example.org/egroupware/groupdav.php/<username>/addressbook/ -H "Accept: application/pretty+json" --user <username>
{
  "responses": {
    "/<username>/addressbook/1833": {
      "uid": "5638-8623c4830472a8ede9f9f8b30d435ea4",
      "prodId": "EGroupware Addressbook 21.1.001",
      "created": "2010-10-21T09:55:42Z",
      "updated": "2014-06-02T14:45:24Z",
      "name": [
        { "@type": "NameComponent", "type": "personal", "value": "Default" },
        { "@type": "NameComponent", "type": "surname", "value": "Tester" }
      ],
      "fullName": { "value": "Default Tester" },
      "organizations": {
        "org": {
          "@type": "Organization", 
          "name": "default.org",
          "units": {
            "org_unit": "department.default.org"
          }
        }
      },
      "emails": {
        "work": { "@type": "EmailAddress", "email": "test@test.com", "contexts": { "work": true }, "pref": 1 }
      },
      "phones": {
        "tel_work": { "@type": "Phone", "phone": "+49 123 4567890", "pref": 1, "features": { "voice": true }, "contexts": { "work": true } },
        "tel_cell": { "@type": "Phone", "phone": "012 3723567", "features": { "cell": true }, "contexts": { "work": true } }
      },
      "online": {
        "url": { "@type": "Resource", "resource": "https://www.test.com/", "type": "uri", "contexts": { "work": true } }
      },
      "notes": [
        "Test test TEST\n\\server\\share\n\\\nother\nblah"
      ],
    },
    "/<username>/addressbook/list-36": {
      "uid": "urn:uuid:dfa5cac5-987b-448b-85d7-6c8b529a835c",
      "name": "Example distribution list",
      "card": {
        "uid": "urn:uuid:dfa5cac5-987b-448b-85d7-6c8b529a835c",
        "prodId": "EGroupware Addressbook 21.1.001",
        "updated": "2018-04-11T14:46:43Z",
        "fullName": { "value": "Example distribution list" }
      },
      "members": {
        "5638-8623c4830472a8ede9f9f8b30d435ea4": true
      }
    }
  }
}

following GET parameters are supported to customize the returned properties:

  • props[]= eg. props[]=getetag to return only the ETAG (multiple DAV properties can be specified) Default for addressbook collections is to only return address-data (JsContact), other collections return all props.
  • sync-token= to only request change since last sync-token, like rfc6578 sync-collection REPORT
  • nresults=N limit number of responses (only for sync-collection / given sync-token parameter!) this will return a "more-results"=true attribute and a new "sync-token" attribute to query for the next chunk
Example: Getting just ETAGs and displayname of all contacts in a given AB
curl -i 'https://example.org/egroupware/groupdav.php/<username>/addressbook/?props[]=getetag&props[]=displayname' -H "Accept: application/pretty+json" --user <username>
{
  "responses": {
    "/addressbook/1833": {
      "displayname": "Default Tester",
      "getetag": "\"1833:24\""
    },
    "/addressbook/1838": {
      "displayname": "Test Tester",
      "getetag": "\"1838:19\""
    }
  }
}
Example: Start using a sync-token to get only changed entries since last sync

Initial request with empty sync-token and only requesting 10 entries per chunk:

curl 'https://example.org/egroupware/groupdav.php/addressbook/?sync-token=&nresults=10&props[]=displayname' -H "Accept: application/pretty+json" --user <username>
{
  "responses": {
    "/addressbook/2050": "Frau Margot Test-Notifikation",
    "/addressbook/2384": "Test Tester",
    "/addressbook/5462": "Margot Testgedöns",
    "/addressbook/2380": "Frau Test Defaulterin",
    "/addressbook/5474": "Noch ein Neuer",
    "/addressbook/5575": "Mr New Name",
    "/addressbook/5461": "Herr Hugo Kurt Müller Senior",
    "/addressbook/5601": "Steve Jobs",
    "/addressbook/5603": "Ralf Becker",
    "/addressbook/1838": "Test Tester"
  },
  "more-results": true,
  "sync-token": "https://example.org/egroupware/groupdav.php/addressbook/1400867824"
}

Requesting next chunk:

curl 'https://example.org/egroupware/groupdav.php/addressbook/?sync-token=https://example.org/egroupware/groupdav.php/addressbook/1400867824&nresults=10&props[]=displayname' -H "Accept: application/pretty+json" --user <username>
{
  "responses": {
    "/addressbook/1833": "Default Tester",
    "/addressbook/5597": "Neuer Testschnuffi",
    "/addressbook/5593": "Muster Max",
    "/addressbook/5628": "2. Test Contact",
    "/addressbook/5629": "Testen Tester",
    "/addressbook/5630": "Testen Tester",
    "/addressbook/5633": "Testen Tester",
    "/addressbook/5635": "Test4 Tester",
    "/addressbook/5638": "Test Kontakt",
    "/addressbook/5636": "Test Default"
  },
  "more-results": true,
  "sync-token": "https://example.org/egroupware/groupdav.php/addressbook/1427103057"
}
Example: Requesting only changes since last sync

sync-token from last sync need to be specified (note the null for a deleted resource!)

curl 'https://example.org/egroupware/groupdav.php/addressbook/?sync-token=https://example.org/egroupware/groupdav.php/addressbook/1400867824' -H "Accept: application/pretty+json" --user <username>
{
  "responses": {
    "/addressbook/5597": null,
    "/addressbook/5593": {
      "uid": "5638-8623c4830472a8ede9f9f8b30d435ea4",
      "prodId": "EGroupware Addressbook 21.1.001",
      "created": "2010-10-21T09:55:42Z",
      "updated": "2014-06-02T14:45:24Z",
      "name": [
        { "@type": "NameComponent", "type": "personal", "value": "Default" },
        { "@type": "NameComponent", "type": "surname", "value": "Tester" }
      ],
      "fullName": "Default Tester",
....
    }
  },
  "sync-token": "https://example.org/egroupware/groupdav.php/addressbook/1427103057"
}
  • GET requests with an Accept: application/json header can be used to retrieve single resources / JsContact or JsCalendar schema
Example: GET request for a single resource
curl 'https://example.org/egroupware/groupdav.php/addressbook/5593' -H "Accept: application/pretty+json" --user <username>
{
  "uid": "5638-8623c4830472a8ede9f9f8b30d435ea4",
  "prodId": "EGroupware Addressbook 21.1.001",
  "created": "2010-10-21T09:55:42Z",
  "updated": "2014-06-02T14:45:24Z",
  "name": [
    { "@type": "NameComponent", "type": "personal", "value": "Default" },
    { "@type": "NameComponent", "type": "surname", "value": "Tester" }
  ],
  "fullName": "Default Tester",
....
}
  • POST requests to collection with a Content-Type: application/json header add new entries in addressbook or calendar collections (Location header in response gives URL of new resource)
Example: POST request to create a new resource
cat <<EOF | curl -i 'https://example.org/egroupware/groupdav.php/<username>/addressbook/' -X POST -d @- -H "Content-Type: application/json" --user <username>
{
  "uid": "5638-8623c4830472a8ede9f9f8b30d435ea4",
  "prodId": "EGroupware Addressbook 21.1.001",
  "created": "2010-10-21T09:55:42Z",
  "updated": "2014-06-02T14:45:24Z",
  "name": [
    { "type": "@type": "NameComponent", "personal", "value": "Default" },
    { "type": "@type": "NameComponent", "surname", "value": "Tester" }
  ],
  "fullName": { "value": "Default Tester" },
....
}
EOF

HTTP/1.1 201 Created
Location: https://example.org/egroupware/groupdav.php/<username>/addressbook/1234
Example: POST request to create a new resource using flat attributes (JSON patch syntax) eg. for a simple Wordpress contact-form
cat <<EOF | curl -i 'https://example.org/egroupware/groupdav.php/<username>/addressbook/' -X POST -d @- -H "Content-Type: application/json" --user <username>
{
  "name/personal": "First",
  "name/surname":  "Tester",
  "organizations/org/name": "Test Organization",
  "emails/work": "test.user@test-user.org",
  "addresses/work/locality": "Test-Town",
  "addresses/work/postcode": "12345",
  "addresses/work/street": "Teststr. 123",
  "phones/tel_work": "+49 123 4567890",
  "online/url": "https://www.example.org/"
}
EOF

HTTP/1.1 201 Created
Location: https://example.org/egroupware/groupdav.php/<username>/addressbook/1234
  • PUT requests with a Content-Type: application/json header allow modifying single resources (requires to specify all attributes!)

  • PATCH request with a Content-Type: application/json header allow to modify a single resource by only specifying changed attributes as a PatchObject

Example: PATCH request to modify a contact with partial data
cat <<EOF | curl -i 'https://example.org/egroupware/groupdav.php/<username>/addressbook/1234' -X PATCH -d @- -H "Content-Type: application/json" --user <username>
{
  "name": [
    {
      "@type": "NameComponent",
      "type": "personal",
      "value": "Testfirst"
    },
    {
      "@type": "NameComponent",
      "type": "surname",
      "value": "Username"
    }
  ],
  "fullName": "Testfirst Username",
  "organizations/org/name": "Test-User.org",
  "emails/work/email": "test.user@test-user.org"
}
EOF

HTTP/1.1 204 No content
  • DELETE requests delete single resources

  • one can use Accept: application/pretty+json to receive pretty-printed JSON eg. for debugging and exploring the API

Implemented changes from JsContact draft 08:

ToDos

  • Addressbook
    • update of photos, keys, attachments
  • InfoLog
  • Calendar
  • relatedTo / links
  • storing not native supported attributes eg. localization