Skip to content

[Bug]: AppCalendar CalendarObject returns no ETag, breaking CalDAV calendar-multiget #63537

Description

@bvdbos

⚠️ This issue respects the following points: ⚠️

Bug description

Calendar objects exposed through OCP\Calendar\ICalendarProvider are wrapped for CalDAV by the AppCalendar DAV implementation but the resulting calendar objects don't provide an ETag. This breaks CalDAV clients which expect DAV:getetag to be present in the response to calendar-multiget.

DAVx5, for example, aborts synchronization with:

Received multi-get response without ETag

The problem can be traced to:

apps/dav/lib/CalDAV/AppCalendar/CalendarObject.php

where CalendarObject::getETag() returns null.

This is specifically reproducible with calendars supplied through OCP\Calendar\ICalendarProvider.

Possibly related to #29875, but that issue concerns an older Nextcloud version and an ETag missing after creating an event. However, this report concerns the current AppCalendar wrapper and calendar-multiget responses.

This appears related to #14103 ("Server Provides OC-ETag but not ETag"), which also caused DAVx⁵ synchronization to fail when a standard ETag was missing. However, this is a different code path: the problem occurs specifically for calendars exposed through OCP\Calendar\ICalendarProvider and wrapped as an AppCalendar. On Nextcloud 34.0.2, a calendar-multiget response for such calendar objects does not contain DAV:getetag. A normal GET of the same generated .ics object works correctly.

Cause

apps/dav/lib/CalDAV/AppCalendar/CalendarObject.php currently does not provide an ETag for the generated calendar object:
https://github.com/nextcloud/server/blob/v34.0.2/apps/dav/lib/CalDAV/AppCalendar/CalendarObject.php#L92-L94

Tested workaround

As a proof of concept I changed the AppCalendar CalendarObject to generate an ETag from the serialized calendar object instead of 'null':

public function getETag(): ?string {
    return '"' . md5($this->get()) . '"';
}

After this change:

  • PROPFIND returns an ETag
  • calendar-multiget returns the ETag
  • DAVx5 accepts the response
  • synchronization succeeds

I don't suggest that MD5 of the serialized representation necessarily has to be the final implementation. The test only demonstrates that providing a stable content-dependent ETag fixes the interoperability problem.

Environment

Nextcloud Server: 34.0.2
PHP: 8.4
Web server: nginx
CalDAV client: DAVx5
Calendar components tested: VJOURNAL and VEVENT
Provider API: OCP\Calendar\ICalendarProvider

Steps to reproduce

  1. Create/register an OCP\Calendar\ICalendarProvider.
  2. Have the provider return one or more calendar components.
  3. Perform a calendar-multiget REPORT requesting both DAV:getetag and CALDAV:calendar-data.

Example request:
curl -u ''
-X REPORT
-H 'Depth: 1'
-H 'Content-Type: application/xml; charset=utf-8'
--data '
<c:calendar-multiget
xmlns:d="DAV:"
xmlns:c="urn:ietf:params:xml:ns:caldav">
<d:prop>
<d:getetag/>
<c:calendar-data/>
</d:prop>
<d:href>/remote.php/dav/calendars//app-generated--dav-wrapper--/.ics</d:href>
</c:calendar-multiget>'
'https:///remote.php/dav/calendars//app-generated--dav-wrapper--/'

Expected behavior

The response for every calendar object must contain a stable ETag which should change when the representation of the generated calendar object changes according to RFC4791. This allows CalDAV clients to identify and synchronize changed objects.
https://datatracker.ietf.org/doc/html/rfc4791#section-5.3.4

Actual behavior

calendar-data is returned, but no usable ETag is returned for the calendar object. DAVx5 consequently aborts synchronization with:

Received multi-get response without ETag

Nextcloud Server version

34.0.2

Operating system

Debian 12 Bookworm

PHP engine version

8.4.24

Web server

nginx version: nginx/1.22.1

Database engine version

'dbtype' => 'mysql',
'dbname' => 'nextcloud',
'dbhost' => 'localhost',

Is this bug present after an update or on a fresh install?

Fresh

Are you using the Nextcloud Server Encryption module?

None

What user-backends are you using?

  • Default user-backend (database)
  • LDAP/ Active Directory
  • SSO - SAML
  • Other

Configuration report

{
"system": {
"passwordsalt": "REMOVED SENSITIVE VALUE",
"secret": "REMOVED SENSITIVE VALUE",
"trusted_domains": [
"localhost",
""
],
"datadirectory": "REMOVED SENSITIVE VALUE",
"dbtype": "mysql",
"version": "34.0.2.1",
"overwrite.cli.url": "https:///drive",
"instanceid": "REMOVED SENSITIVE VALUE",
"dbname": "REMOVED SENSITIVE VALUE",
"dbhost": "REMOVED SENSITIVE VALUE",
"dbtableprefix": "oc_",
"mysql.utf8mb4": true,
"dbuser": "REMOVED SENSITIVE VALUE",
"dbpassword": "REMOVED SENSITIVE VALUE",
"installed": true,
"logfile": "/var/log/nextcloud/nextcloud.log",
"trashbin_retention_obligation": "auto, 30",
"versions_retention_obligation": "auto, 30",
"updatechecker": false,
"memcache.local": "\OC\Memcache\APCu",
"integrity.check.disabled": true,
"filelocking.enabled": true,
"memcache.locking": "\OC\Memcache\Redis",
"redis": {
"host": "REMOVED SENSITIVE VALUE",
"port": "6379",
"timeout": "0.0",
"password": "REMOVED SENSITIVE VALUE"
},
"maintenance": false
}
}

List of activated Apps

=== ENABLED APPS ===
Enabled:

  • activity: 7.0.0
  • app_api: 34.0.0
  • appstore: 1.0.0
  • bruteforcesettings: 7.0.0
  • calendar: 6.5.4
  • circles: 34.0.0
  • cloud_federation_api: 1.18.0
  • comments: 1.24.0
  • contacts: 8.7.6
  • contactsinteraction: 1.15.0
  • dashboard: 7.14.0
  • dav: 1.39.0
  • federatedfilesharing: 1.24.0
  • federation: 1.24.0
  • files: 2.6.0
  • files_downloadlimit: 5.2.0
  • files_external: 1.26.0
  • files_lock: 34.0.1
  • files_pdfviewer: 7.0.0-dev.0
  • files_reminders: 1.7.0
  • files_scripts: 5.0.1
  • files_sharing: 1.26.0
  • files_trashbin: 1.24.0
  • files_versions: 1.27.0
  • files_zip: 3.0.1
  • firstrunwizard: 7.0.0-dev.0
  • gpoddersync: 3.17.0
  • journal_caldav: 0.1.0
  • journalnotes: 0.2.0
  • logreader: 7.0.0
  • lookup_server_connector: 1.22.0
  • mail: 5.10.13
  • nextcloud_announcements: 6.0.0
  • notes: 6.0.2
  • notifications: 7.0.0-dev.1
  • oauth2: 1.22.0
  • office: 1.0.0
  • password_policy: 6.0.0-dev.0
  • photos: 7.0.0
  • privacy: 6.0.0-dev.1
  • profile: 1.3.0
  • provisioning_api: 1.24.0
  • recommendations: 7.0.0
  • related_resources: 5.0.0-dev.0
  • repod: 4.2.1
  • richdocuments: 11.1.0
  • serverinfo: 6.0.0
  • settings: 1.17.0
  • sharebymail: 1.24.0
  • sociallogin: 6.5.4
  • support: 6.0.0
  • survey_client: 6.0.0-dev.0
  • systemtags: 1.24.0
  • tasks: 0.18.1
  • text: 8.0.0
  • theming: 2.9.0
  • twofactor_backupcodes: 1.23.0
  • twofactor_totp: 16.0.0
  • user_ldap: 1.25.0
  • user_status: 1.14.0
  • viewer: 7.0.0-dev.0
  • weather_status: 1.14.0
  • webhook_listeners: 1.6.0
  • workflowengine: 2.16.0

Nextcloud Signing status

  • enabled: false
  • defaultModule:

Nextcloud Logs

Additional info

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    0. Needs triagePending check for reproducibility or if it fits our roadmap32-feedbackbug

    Type

    Projects

    Status
    To triage

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions