From 74bd2d311c2c910ad22aad40007887719b094cf3 Mon Sep 17 00:00:00 2001 From: Michael Stilkerich Date: Wed, 15 Jul 2020 19:53:00 +0200 Subject: [PATCH 1/3] RFC 4918 compliance: Remove unexpected propstat According to RFC 4918, there is two types of response elements: Type 1 contains one or more href and one status child elements. Type 2 contains one href and one or more propstat child elements. Both types may contain further optional elements. For Type 1, sabre/dav inserts a spurious propstat element, which violated the element definition by RFC 4918. --- lib/DAV/Xml/Element/Response.php | 4 +++- tests/Sabre/DAVACL/PrincipalMatchTest.php | 4 ---- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/lib/DAV/Xml/Element/Response.php b/lib/DAV/Xml/Element/Response.php index 45c161fa4f..f03f31872d 100644 --- a/lib/DAV/Xml/Element/Response.php +++ b/lib/DAV/Xml/Element/Response.php @@ -112,12 +112,14 @@ public function getResponseProperties() */ public function xmlSerialize(Writer $writer) { + $empty = true; + if ($status = $this->getHTTPStatus()) { + $empty = false; $writer->writeElement('{DAV:}status', 'HTTP/1.1 '.$status.' '.\Sabre\HTTP\Response::$statusCodes[$status]); } $writer->writeElement('{DAV:}href', $writer->contextUri.\Sabre\HTTP\encodePath($this->getHref())); - $empty = true; foreach ($this->getResponseProperties() as $status => $properties) { // Skipping empty lists diff --git a/tests/Sabre/DAVACL/PrincipalMatchTest.php b/tests/Sabre/DAVACL/PrincipalMatchTest.php index 0746d2e50e..7862f2c59d 100644 --- a/tests/Sabre/DAVACL/PrincipalMatchTest.php +++ b/tests/Sabre/DAVACL/PrincipalMatchTest.php @@ -30,10 +30,6 @@ public function testPrincipalMatch() HTTP/1.1 200 OK /principals/user1 - - - HTTP/1.1 418 I'm a teapot - XML; From facb0993209b56a530f4f9a9af2ea1c88903c328 Mon Sep 17 00:00:00 2001 From: Michael Stilkerich Date: Wed, 22 Jul 2020 20:15:41 +0200 Subject: [PATCH 2/3] Remove an empty line that phpstan rejected --- lib/DAV/Xml/Element/Response.php | 1 - 1 file changed, 1 deletion(-) diff --git a/lib/DAV/Xml/Element/Response.php b/lib/DAV/Xml/Element/Response.php index f03f31872d..5a4834e230 100644 --- a/lib/DAV/Xml/Element/Response.php +++ b/lib/DAV/Xml/Element/Response.php @@ -120,7 +120,6 @@ public function xmlSerialize(Writer $writer) } $writer->writeElement('{DAV:}href', $writer->contextUri.\Sabre\HTTP\encodePath($this->getHref())); - foreach ($this->getResponseProperties() as $status => $properties) { // Skipping empty lists if (!$properties || (!ctype_digit($status) && !is_int($status))) { From 6c12a73eddb243205f3d7652fe4ab229829f0931 Mon Sep 17 00:00:00 2001 From: Michael Stilkerich Date: Fri, 20 Nov 2020 22:48:53 +0100 Subject: [PATCH 3/3] Support filtering returned address object properties in multiget report Analog to addressbook-query report, multiget also supports returning only partial address objects. This can be useful to carddav clients to have fast syncs by excluding large properties like PHOTO and fetching those only when needed. --- lib/CardDAV/Plugin.php | 3 +- tests/Sabre/CardDAV/MultiGetTest.php | 46 ++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+), 1 deletion(-) diff --git a/lib/CardDAV/Plugin.php b/lib/CardDAV/Plugin.php index 09d1f593da..0a87ef47c9 100644 --- a/lib/CardDAV/Plugin.php +++ b/lib/CardDAV/Plugin.php @@ -242,7 +242,8 @@ public function addressbookMultiGetReport($report) if (isset($props['200']['{'.self::NS_CARDDAV.'}address-data'])) { $props['200']['{'.self::NS_CARDDAV.'}address-data'] = $this->convertVCard( $props[200]['{'.self::NS_CARDDAV.'}address-data'], - $vcardType + $vcardType, + $report->addressDataProperties ); } $propertyList[] = $props; diff --git a/tests/Sabre/CardDAV/MultiGetTest.php b/tests/Sabre/CardDAV/MultiGetTest.php index ac0cd5e910..b078b1e6ab 100644 --- a/tests/Sabre/CardDAV/MultiGetTest.php +++ b/tests/Sabre/CardDAV/MultiGetTest.php @@ -96,4 +96,50 @@ public function testMultiGetVCard4() ], ], $result); } + + public function testMultiGetAddressObjectProperties() + { + $request = HTTP\Sapi::createFromServerArray([ + 'REQUEST_METHOD' => 'REPORT', + 'REQUEST_URI' => '/addressbooks/user1/book3', + ]); + + $request->setBody( +' + + + + + + + + + /addressbooks/user1/book3/card3 +' + ); + + $response = new HTTP\ResponseMock(); + + $this->server->httpRequest = $request; + $this->server->httpResponse = $response; + + $this->server->exec(); + + $bodyAsString = $response->getBodyAsString(); + $this->assertEquals(207, $response->status, 'Incorrect status code. Full response body:'.$bodyAsString); + + // using the client for parsing + $client = new DAV\Client(['baseUri' => '/']); + + $result = $client->parseMultiStatus($bodyAsString); + + $this->assertEquals([ + '/addressbooks/user1/book3/card3' => [ + 200 => [ + '{DAV:}getetag' => '"'.md5("BEGIN:VCARD\nVERSION:3.0\nUID:12345\nFN:Test-Card\nEMAIL;TYPE=home:bar@example.org\nEND:VCARD").'"', + '{urn:ietf:params:xml:ns:carddav}address-data' => "BEGIN:VCARD\r\nVERSION:3.0\r\nUID:12345\r\nFN:Test-Card\r\nEND:VCARD\r\n", + ], + ], + ], $result); + } }