Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
47 changes: 28 additions & 19 deletions apps/dav/lib/CardDAV/SyncService.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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);
}
}
}

Expand Down
23 changes: 19 additions & 4 deletions apps/federation/lib/SyncFederationAddressBooks.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

use OCA\DAV\CardDAV\SyncService;
use OCP\AppFramework\Http;
use OCP\IConfig;
use OCP\OCS\IDiscoveryService;
use Psr\Log\LoggerInterface;

Expand All @@ -18,13 +19,18 @@ public function __construct(
private SyncService $syncService,
private IDiscoveryService $ocsDiscoveryService,
private LoggerInterface $logger,
private IConfig $config,
) {
}

/**
* @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'];
Expand All @@ -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 {
Expand All @@ -63,7 +77,8 @@ public function syncThemAll(\Closure $callback, bool $full = false) {
$syncToken,
$targetBookId,
$targetPrincipal,
$targetBookProperties
$targetBookProperties,
$syncAddressBookData
);
} while ($truncated);

Expand Down