Skip to content

Commit 373c3ae

Browse files
authored
Merge pull request #46117 from nextcloud/backport/46002/stable29
[stable29] Use guzzle for addressbook federation
2 parents b969174 + b0505d6 commit 373c3ae

File tree

2 files changed

+356
-104
lines changed

2 files changed

+356
-104
lines changed

apps/dav/lib/CardDAV/SyncService.php

Lines changed: 42 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -32,14 +32,14 @@
3232

3333
use OCP\AppFramework\Db\TTransactional;
3434
use OCP\AppFramework\Http;
35+
use OCP\Http\Client\IClientService;
3536
use OCP\IDBConnection;
3637
use OCP\IUser;
3738
use OCP\IUserManager;
39+
use Psr\Http\Client\ClientExceptionInterface;
3840
use Psr\Log\LoggerInterface;
39-
use Sabre\DAV\Client;
4041
use Sabre\DAV\Xml\Response\MultiStatus;
4142
use Sabre\DAV\Xml\Service;
42-
use Sabre\HTTP\ClientHttpException;
4343
use Sabre\VObject\Reader;
4444
use function is_null;
4545

@@ -54,18 +54,21 @@ class SyncService {
5454
private ?array $localSystemAddressBook = null;
5555
private Converter $converter;
5656
protected string $certPath;
57+
private IClientService $clientService;
5758

5859
public function __construct(CardDavBackend $backend,
5960
IUserManager $userManager,
6061
IDBConnection $dbConnection,
6162
LoggerInterface $logger,
62-
Converter $converter) {
63+
Converter $converter,
64+
IClientService $clientService) {
6365
$this->backend = $backend;
6466
$this->userManager = $userManager;
6567
$this->logger = $logger;
6668
$this->converter = $converter;
6769
$this->certPath = '';
6870
$this->dbConnection = $dbConnection;
71+
$this->clientService = $clientService;
6972
}
7073

7174
/**
@@ -79,7 +82,7 @@ public function syncRemoteAddressBook(string $url, string $userName, string $add
7982
// 2. query changes
8083
try {
8184
$response = $this->requestSyncReport($url, $userName, $addressBookUrl, $sharedSecret, $syncToken);
82-
} catch (ClientHttpException $ex) {
85+
} catch (ClientExceptionInterface $ex) {
8386
if ($ex->getCode() === Http::STATUS_UNAUTHORIZED) {
8487
// remote server revoked access to the address book, remove it
8588
$this->backend->deleteAddressBook($addressBookId);
@@ -99,9 +102,9 @@ public function syncRemoteAddressBook(string $url, string $userName, string $add
99102
$this->atomic(function () use ($addressBookId, $cardUri, $vCard) {
100103
$existingCard = $this->backend->getCard($addressBookId, $cardUri);
101104
if ($existingCard === false) {
102-
$this->backend->createCard($addressBookId, $cardUri, $vCard['body']);
105+
$this->backend->createCard($addressBookId, $cardUri, $vCard);
103106
} else {
104-
$this->backend->updateCard($addressBookId, $cardUri, $vCard['body']);
107+
$this->backend->updateCard($addressBookId, $cardUri, $vCard);
105108
}
106109
}, $this->dbConnection);
107110
} else {
@@ -128,63 +131,57 @@ public function ensureSystemAddressBookExists(string $principal, string $uri, ar
128131
}
129132

130133
/**
131-
* Check if there is a valid certPath we should use
134+
* @throws ClientExceptionInterface
132135
*/
133-
protected function getCertPath(): string {
134-
135-
// we already have a valid certPath
136-
if ($this->certPath !== '') {
137-
return $this->certPath;
138-
}
139-
140-
$certManager = \OC::$server->getCertificateManager();
141-
$certPath = $certManager->getAbsoluteBundlePath();
142-
if (file_exists($certPath)) {
143-
$this->certPath = $certPath;
144-
}
136+
protected function requestSyncReport(string $url, string $userName, string $addressBookUrl, string $sharedSecret, ?string $syncToken): array {
137+
$client = $this->clientService->newClient();
145138

146-
return $this->certPath;
147-
}
139+
// the trailing slash is important for merging base_uri and uri
140+
$url = rtrim($url, '/') . '/';
148141

149-
protected function getClient(string $url, string $userName, string $sharedSecret): Client {
150-
$settings = [
151-
'baseUri' => $url . '/',
152-
'userName' => $userName,
153-
'password' => $sharedSecret,
142+
$options = [
143+
'auth' => [$userName, $sharedSecret],
144+
'base_uri' => $url,
145+
'body' => $this->buildSyncCollectionRequestBody($syncToken),
146+
'headers' => ['Content-Type' => 'application/xml']
154147
];
155-
$client = new Client($settings);
156-
$certPath = $this->getCertPath();
157-
$client->setThrowExceptions(true);
158148

159-
if ($certPath !== '' && !str_starts_with($url, 'http://')) {
160-
$client->addCurlSetting(CURLOPT_CAINFO, $this->certPath);
161-
}
149+
$response = $client->request(
150+
'REPORT',
151+
$addressBookUrl,
152+
$options
153+
);
154+
155+
$body = $response->getBody();
156+
assert(is_string($body));
162157

163-
return $client;
158+
return $this->parseMultiStatus($body);
164159
}
165160

166-
protected function requestSyncReport(string $url, string $userName, string $addressBookUrl, string $sharedSecret, ?string $syncToken): array {
167-
$client = $this->getClient($url, $userName, $sharedSecret);
161+
protected function download(string $url, string $userName, string $sharedSecret, string $resourcePath): string {
162+
$client = $this->clientService->newClient();
168163

169-
$body = $this->buildSyncCollectionRequestBody($syncToken);
164+
// the trailing slash is important for merging base_uri and uri
165+
$url = rtrim($url, '/') . '/';
170166

171-
$response = $client->request('REPORT', $addressBookUrl, $body, [
172-
'Content-Type' => 'application/xml'
173-
]);
167+
$options = [
168+
'auth' => [$userName, $sharedSecret],
169+
'base_uri' => $url,
170+
];
174171

175-
return $this->parseMultiStatus($response['body']);
176-
}
172+
$response = $client->get(
173+
$resourcePath,
174+
$options
175+
);
177176

178-
protected function download(string $url, string $userName, string $sharedSecret, string $resourcePath): array {
179-
$client = $this->getClient($url, $userName, $sharedSecret);
180-
return $client->request('GET', $resourcePath);
177+
return (string)$response->getBody();
181178
}
182179

183180
private function buildSyncCollectionRequestBody(?string $syncToken): string {
184181
$dom = new \DOMDocument('1.0', 'UTF-8');
185182
$dom->formatOutput = true;
186183
$root = $dom->createElementNS('DAV:', 'd:sync-collection');
187-
$sync = $dom->createElement('d:sync-token', $syncToken);
184+
$sync = $dom->createElement('d:sync-token', $syncToken ?? '');
188185
$prop = $dom->createElement('d:prop');
189186
$cont = $dom->createElement('d:getcontenttype');
190187
$etag = $dom->createElement('d:getetag');

0 commit comments

Comments
 (0)