Skip to content

Commit 78d2541

Browse files
committed
fix: fix full addressbook sync with truncated results
Signed-off-by: Robin Appelman <robin@icewind.nl>
1 parent d43f25b commit 78d2541

3 files changed

Lines changed: 57 additions & 13 deletions

File tree

apps/dav/lib/CardDAV/CardDavBackend.php

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,13 @@ public function getCards($addressbookId) {
478478
->from($this->dbCardsTable)
479479
->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressbookId)));
480480

481+
return $this->getCardsFromQuery($query);
482+
}
483+
484+
/**
485+
* @return array[]
486+
*/
487+
private function getCardsFromQuery(IQueryBuilder $query): array {
481488
$cards = [];
482489

483490
$result = $query->executeQuery();
@@ -1532,4 +1539,32 @@ private function getUID(string $cardData): string {
15321539
// should already be handled, but just in case
15331540
throw new BadRequest('vCard can not be empty');
15341541
}
1542+
1543+
/**
1544+
* Mark all cards in an address book as needing to be validated
1545+
*
1546+
* This is done by setting the modified date to `null`, once a sync runs
1547+
* the mtime will be set to a non-null value. Leaving all deleted items with
1548+
* a null modified date.
1549+
*/
1550+
public function markCardsAsPending(int $addressBookId): void {
1551+
$query = $this->db->getTypedQueryBuilder();
1552+
$query->update($this->dbCardsTable)
1553+
->set('lastmodified', $query->createNamedParameter(null))
1554+
->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId)))
1555+
->executeStatement();
1556+
}
1557+
1558+
/**
1559+
* @return array[]
1560+
*/
1561+
public function getPendingCards(int $addressBookId): array {
1562+
$query = $this->db->getQueryBuilder();
1563+
$query->select(['id', 'addressbookid', 'uri', 'lastmodified', 'etag', 'size', 'carddata', 'uid'])
1564+
->from($this->dbCardsTable)
1565+
->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId)))
1566+
->andWhere($query->expr()->isNull('lastmodified'));
1567+
1568+
return $this->getCardsFromQuery($query);
1569+
}
15351570
}

apps/dav/lib/CardDAV/SyncService.php

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -66,13 +66,11 @@ public function syncRemoteAddressBook(string $url, string $userName, string $add
6666
throw $ex;
6767
}
6868

69-
$received = [];
7069
// 3. apply changes
7170
// TODO: use multi-get for download
7271
foreach ($response['response'] as $resource => $status) {
7372
$cardUri = basename($resource);
7473
if (isset($status[200])) {
75-
$received[] = $cardUri;
7674
$absoluteUrl = $this->prepareUri($url, $resource);
7775
$vCard = $this->download($absoluteUrl, $userName, $sharedSecret);
7876
$this->atomic(function () use ($addressBookId, $cardUri, $vCard): void {
@@ -88,15 +86,6 @@ public function syncRemoteAddressBook(string $url, string $userName, string $add
8886
}
8987
}
9088

91-
// when doing a full sync, remove any items in the local address book that aren't in the remote one
92-
if (!$syncToken) {
93-
$existingCards = $this->backend->getCards($addressBookId);
94-
$removedCards = array_filter($existingCards, fn (array $card) => !in_array($card['uri'], $received));
95-
foreach ($removedCards as $removedCard) {
96-
$this->backend->deleteCard($addressBookId, $removedCard['uri']);
97-
}
98-
}
99-
10089
return [
10190
$response['token'],
10291
$response['truncated'],
@@ -228,4 +217,15 @@ public function syncInstance(?\Closure $progressCallback = null) {
228217
public static function getCardUri(IUser $user): string {
229218
return $user->getBackendClassName() . ':' . $user->getUID() . '.vcf';
230219
}
220+
221+
public function markCardsAsPending(int $addressBookId): void {
222+
$this->backend->markCardsAsPending($addressBookId);
223+
}
224+
225+
public function deletePendingCards(int $addressBookId): void {
226+
$cards = $this->backend->getPendingCards($addressBookId);
227+
foreach ($cards as $card) {
228+
$this->backend->deleteCard($addressBookId, $card['uri']);
229+
}
230+
}
231231
}

apps/federation/lib/SyncFederationAddressBooks.php

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,21 +47,30 @@ public function syncThemAll(\Closure $callback, bool $full = false) {
4747
];
4848

4949
try {
50-
$syncToken = $oldSyncToken;
50+
$syncToken = $full ? null : $oldSyncToken;
51+
52+
$book = $this->syncService->ensureSystemAddressBookExists($targetPrincipal, $targetBookId, $targetBookProperties);
53+
if ($full) {
54+
$this->syncService->markCardsAsPending($book['id']);
55+
}
5156

5257
do {
5358
[$syncToken, $truncated] = $this->syncService->syncRemoteAddressBook(
5459
$url,
5560
$cardDavUser,
5661
$addressBookUrl,
5762
$sharedSecret,
58-
$full ? null : $syncToken,
63+
$syncToken,
5964
$targetBookId,
6065
$targetPrincipal,
6166
$targetBookProperties
6267
);
6368
} while ($truncated);
6469

70+
if ($full) {
71+
$this->syncService->deletePendingCards($book['id']);
72+
}
73+
6574
if ($syncToken !== $oldSyncToken) {
6675
$this->dbHandler->setServerStatus($url, TrustedServers::STATUS_OK, $syncToken);
6776
} else {

0 commit comments

Comments
 (0)