nextcloud / nextcloud/integration_davc

Initial CalDAV harmonization may store sync-token without importing existing events

Open
#169 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

bug
Dominant language
PHP
Stars
12
Forks
2
Avg merge
1d 21h
Merged PRs (30d)
16

Description

Disclaimer : I heavily used AI (ChatGPT) to resolve this. I may not understand everything (or anything) to this as Im not an IT pro.

Steps to reproduce
  1. Configure DAV Connector with a Radicale CalDAV server.
  2. Add a remote calendar that already contains events.
  3. Enable the calendar in DAV Connector.
  4. Run Harmonize for the first time.
  5. The calendar collection is created in Nextcloud, but the existing remote events are not imported.
  6. Run Harmonize again: the events are still not imported because a remote sync token has already been stored.

This was discovered while investigating #168, but appears to be a separate issue.

For this test, the empty resourcetype issue described in #168 was already patched, so DAV Connector was able to correctly enumerate individual calendar resources.

Expected behaviour

When a remote CalDAV calendar is synchronized for the first time and no previous remote synchronization state exists, DAV Connector should import all existing events.

Once the initial synchronization is complete, subsequent harmonizations can use the remote sync token for incremental synchronization.

Actual behaviour

On the initial synchronization, DAV Connector calls entityDelta() even though no previous remote synchronization state exists.

In EventsService::harmonizeCollection(), the synchronization starts with:

$remoteEntityDelta = $this->remoteEventsService->entityDelta(
    $remoteCollectionId,
    $remoteCollectionSignature,
    'B'
);

and falls back to a full comparison only if entityDelta() throws a RuntimeException.

For a newly added calendar, the remote synchronization state is initially empty.

Radicale accepts the sync-collection REPORT and returns a new sync token, but the already existing events are not returned as changes.

DAV Connector then stores the returned sync token.

In my test, after the first Harmonize:

hesn = http://radicale.org/ns/sync/...
local events = 0

Subsequent Harmonize operations use this stored token. Since there are no new remote changes, the existing events remain missing indefinitely.

Tested workaround

I tested forcing an initial full synchronization when no remote synchronization state exists.

In RemoteEventsService::entityDelta(), I added:

if ($state === '') {
    throw new RuntimeException('No remote synchronization state available.');
}

This causes the existing fallback in EventsService to use the full remote/local comparison instead of attempting an incremental sync-collection REPORT without a previous state.

After resetting the previously stored remote synchronization state and running Harmonize again, the existing calendar events were successfully imported.

A second Harmonize, without making any remote changes, did not create duplicates.

In my test:

After initial full synchronization:
DAVC event entities = 106

After a second Harmonize:
DAVC event entities = 106

This suggests that the full synchronization is only required for initialization and that subsequent incremental synchronization works correctly once a valid synchronization state has been established.

Possible fix

Avoid using incremental sync-collection when no previous remote synchronization state is available.

For example:

public function entityDelta(string $location, string $state): DeltaObject {
    if ($state === '') {
        throw new RuntimeException('No remote synchronization state available.');
    }

    // existing implementation
}

Alternatively, the initial/full synchronization decision could be handled at a higher level in EventsService::harmonizeCollection().

The important point seems to be that an empty synchronization state should trigger an initial full synchronization rather than an incremental sync-collection request.

Server configuration

Web server: Nginx (LinuxServer.io Nextcloud Docker image)

Database: MariaDB 11.4.8

PHP version: 8.4.16

Nextcloud version: 34.0.3.2

DAV Connector version: 1.1.0

Remote DAV server: Radicale 3.2.3.1 with radicale_storage_decsync

List of activated apps
Not included initially. Can be provided if required.
Nextcloud configuration
Not included initially. Can be provided if required.
Browser

Browser name: Firefox

Browser version: 153

Operating system: Kubuntu

Browser log
No relevant browser-side error observed. The issue occurs during server-side DAV synchronization.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in EventsService::harmonizeCollection() and RemoteEventsService::entityDelta(), following how an empty remote synchronization state is handled before the sync-collection request. Reproduce the first and second Harmonize runs with a calendar containing existing events. Done means the first run imports existing events and a second unchanged run neither loses nor duplicates them.

Written by the indexing model from the issue text.

Assessment

Tech stack
php
Domain
backend
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
74/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.