Skip to content

Commit 1ffdc90

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

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

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'],
@@ -225,4 +214,15 @@ public function syncInstance(?\Closure $progressCallback = null) {
225214
public static function getCardUri(IUser $user): string {
226215
return $user->getBackendClassName() . ':' . $user->getUID() . '.vcf';
227216
}
217+
218+
public function markCardsAsPending(int $addressBookId): void {
219+
$this->backend->markCardsAsPending($addressBookId);
220+
}
221+
222+
public function deletePendingCards(int $addressBookId): void {
223+
$cards = $this->backend->getPendingCards($addressBookId);
224+
foreach ($cards as $card) {
225+
$this->backend->deleteCard($addressBookId, $card['uri']);
226+
}
227+
}
228228
}

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)