Skip to content

Commit ede5e57

Browse files
icewind1991backportbot[bot]
authored andcommitted
fix: fix full addressbook sync with truncated results
Signed-off-by: Robin Appelman <robin@icewind.nl>
1 parent 6589b62 commit ede5e57

3 files changed

Lines changed: 57 additions & 12 deletions

File tree

apps/dav/lib/CardDAV/CardDavBackend.php

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

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

484491
$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 & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,6 @@ 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) {
@@ -86,15 +85,6 @@ public function syncRemoteAddressBook(string $url, string $userName, string $add
8685
}
8786
}
8887

89-
// when doing a full sync, remove any items in the local address book that aren't in the remote one
90-
if (!$syncToken) {
91-
$existingCards = $this->backend->getCards($addressBookId);
92-
$removedCards = array_filter($existingCards, fn (array $card) => !in_array($card['uri'], $received));
93-
foreach ($removedCards as $removedCard) {
94-
$this->backend->deleteCard($addressBookId, $removedCard['uri']);
95-
}
96-
}
97-
9888
return [
9989
$response['token'],
10090
$response['truncated'],
@@ -365,4 +355,15 @@ public function syncInstance(?\Closure $progressCallback = null) {
365355
public static function getCardUri(IUser $user): string {
366356
return $user->getBackendClassName() . ':' . $user->getUID() . '.vcf';
367357
}
358+
359+
public function markCardsAsPending(int $addressBookId): void {
360+
$this->backend->markCardsAsPending($addressBookId);
361+
}
362+
363+
public function deletePendingCards(int $addressBookId): void {
364+
$cards = $this->backend->getPendingCards($addressBookId);
365+
foreach ($cards as $card) {
366+
$this->backend->deleteCard($addressBookId, $card['uri']);
367+
}
368+
}
368369
}

apps/federation/lib/SyncFederationAddressBooks.php

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

5353
try {
54-
$syncToken = $oldSyncToken;
54+
$syncToken = $full ? null : $oldSyncToken;
55+
56+
$book = $this->syncService->ensureSystemAddressBookExists($targetPrincipal, $targetBookId, $targetBookProperties);
57+
if ($full) {
58+
$this->syncService->markCardsAsPending($book['id']);
59+
}
5560

5661
do {
5762
[$syncToken, $truncated] = $this->syncService->syncRemoteAddressBook(
5863
$url,
5964
$cardDavUser,
6065
$addressBookUrl,
6166
$sharedSecret,
62-
$full ? null : $syncToken,
67+
$syncToken,
6368
$targetBookId,
6469
$targetPrincipal,
6570
$targetBookProperties
6671
);
6772
} while ($truncated);
6873

74+
if ($full) {
75+
$this->syncService->deletePendingCards($book['id']);
76+
}
77+
6978
if ($syncToken !== $oldSyncToken) {
7079
$this->dbHandler->setServerStatus($url, TrustedServers::STATUS_OK, $syncToken);
7180
} else {

0 commit comments

Comments
 (0)