diff --git a/apps/dav/lib/CardDAV/SyncService.php b/apps/dav/lib/CardDAV/SyncService.php index 5ba0a1e529de7..78c02a9414e02 100644 --- a/apps/dav/lib/CardDAV/SyncService.php +++ b/apps/dav/lib/CardDAV/SyncService.php @@ -46,19 +46,26 @@ public function __construct( * @psalm-return list{0: ?string, 1: boolean} * @throws \Exception */ - public function syncRemoteAddressBook(string $url, string $userName, string $addressBookUrl, string $sharedSecret, ?string $syncToken, string $targetBookHash, string $targetPrincipal, array $targetProperties): array { + public function syncRemoteAddressBook(string $url, string $userName, string $addressBookUrl, string $sharedSecret, ?string $syncToken, string $targetBookHash, string $targetPrincipal, array $targetProperties, bool $syncAddressBookData = true): array { // 1. create addressbook - $book = $this->ensureSystemAddressBookExists($targetPrincipal, $targetBookHash, $targetProperties); - $addressBookId = $book['id']; + $addressBookId = null; + if ($syncAddressBookData) { + $book = $this->ensureSystemAddressBookExists($targetPrincipal, $targetBookHash, $targetProperties); + $addressBookId = $book['id']; + } // 2. query changes + // This keeps validating the shared secret and advancing the sync token + // against the remote even when applying the changes locally is disabled. try { $absoluteUri = $this->prepareUri($url, $addressBookUrl); $response = $this->requestSyncReport($absoluteUri, $userName, $sharedSecret, $syncToken); } catch (ClientExceptionInterface $ex) { if ($ex->getCode() === Http::STATUS_UNAUTHORIZED) { // remote server revoked access to the address book, remove it - $this->backend->deleteAddressBook($addressBookId); + if ($addressBookId !== null) { + $this->backend->deleteAddressBook($addressBookId); + } $this->logger->error('Authorization failed, remove address book: ' . $url, ['app' => 'dav']); throw $ex; } @@ -68,21 +75,23 @@ public function syncRemoteAddressBook(string $url, string $userName, string $add // 3. apply changes // TODO: use multi-get for download - foreach ($response['response'] as $resource => $status) { - $cardUri = basename($resource); - if (isset($status[200])) { - $absoluteUrl = $this->prepareUri($url, $resource); - $vCard = $this->download($absoluteUrl, $userName, $sharedSecret); - $this->atomic(function () use ($addressBookId, $cardUri, $vCard): void { - $existingCard = $this->backend->getCard($addressBookId, $cardUri); - if ($existingCard === false) { - $this->backend->createCard($addressBookId, $cardUri, $vCard); - } else { - $this->backend->updateCard($addressBookId, $cardUri, $vCard); - } - }, $this->dbConnection); - } else { - $this->backend->deleteCard($addressBookId, $cardUri); + if ($syncAddressBookData) { + foreach ($response['response'] as $resource => $status) { + $cardUri = basename($resource); + if (isset($status[200])) { + $absoluteUrl = $this->prepareUri($url, $resource); + $vCard = $this->download($absoluteUrl, $userName, $sharedSecret); + $this->atomic(function () use ($addressBookId, $cardUri, $vCard): void { + $existingCard = $this->backend->getCard($addressBookId, $cardUri); + if ($existingCard === false) { + $this->backend->createCard($addressBookId, $cardUri, $vCard); + } else { + $this->backend->updateCard($addressBookId, $cardUri, $vCard); + } + }, $this->dbConnection); + } else { + $this->backend->deleteCard($addressBookId, $cardUri); + } } } diff --git a/apps/federation/lib/SyncFederationAddressBooks.php b/apps/federation/lib/SyncFederationAddressBooks.php index 5ad64708bb737..09960370ecbd3 100644 --- a/apps/federation/lib/SyncFederationAddressBooks.php +++ b/apps/federation/lib/SyncFederationAddressBooks.php @@ -9,6 +9,7 @@ use OCA\DAV\CardDAV\SyncService; use OCP\AppFramework\Http; +use OCP\IConfig; use OCP\OCS\IDiscoveryService; use Psr\Log\LoggerInterface; @@ -18,6 +19,7 @@ public function __construct( private SyncService $syncService, private IDiscoveryService $ocsDiscoveryService, private LoggerInterface $logger, + private IConfig $config, ) { } @@ -25,6 +27,10 @@ public function __construct( * @param \Closure $callback */ public function syncThemAll(\Closure $callback, bool $full = false) { + // When disabled, the sync-report request against the remote still runs + // each cycle (validating the shared secret and advancing the sync token), + // but the returned cards are not applied to the local address book. + $syncAddressBookData = $this->config->getSystemValueBool('federation_sync_addressbook_data', true); $trustedServers = $this->dbHandler->getAllServer(); foreach ($trustedServers as $trustedServer) { $url = $trustedServer['url']; @@ -47,11 +53,19 @@ public function syncThemAll(\Closure $callback, bool $full = false) { ]; try { + // A full resync marks all existing cards as pending and then deletes + // whichever ones are not confirmed by the remote. That only makes + // sense if card data is actually being applied, otherwise it would + // just wipe out the local address book. + $full = $full && $syncAddressBookData; $syncToken = $full ? null : $oldSyncToken; - $book = $this->syncService->ensureSystemAddressBookExists($targetPrincipal, $targetBookId, $targetBookProperties); - if ($full) { - $this->syncService->markCardsAsPending($book['id']); + $book = null; + if ($syncAddressBookData) { + $book = $this->syncService->ensureSystemAddressBookExists($targetPrincipal, $targetBookId, $targetBookProperties); + if ($full) { + $this->syncService->markCardsAsPending($book['id']); + } } do { @@ -63,7 +77,8 @@ public function syncThemAll(\Closure $callback, bool $full = false) { $syncToken, $targetBookId, $targetPrincipal, - $targetBookProperties + $targetBookProperties, + $syncAddressBookData ); } while ($truncated);