diff --git a/.patches/check-for-instanceof-INode-instead-of-Node-1595.patch b/.patches/check-for-instanceof-INode-instead-of-Node-1595.patch deleted file mode 100644 index 7c5516664..000000000 --- a/.patches/check-for-instanceof-INode-instead-of-Node-1595.patch +++ /dev/null @@ -1,26 +0,0 @@ -From 5d7501ddcf23f98da84af06e23a2413f4fc9b03f Mon Sep 17 00:00:00 2001 -From: Carl Schwan -Date: Fri, 12 Sep 2025 00:03:26 +0200 -Subject: [PATCH 1/2] feat: Check for instanceof INode instead of Node (#1595) - -Signed-off-by: Carl Schwan ---- - lib/DAV/Tree.php | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/lib/DAV/Tree.php b/lib/DAV/Tree.php -index 1483e1bc..f8a996b5 100644 ---- a/lib/DAV/Tree.php -+++ b/lib/DAV/Tree.php -@@ -84,7 +84,7 @@ class Tree implements INodeByPath - - if ($node instanceof INodeByPath) { - $targetNode = $node->getNodeForPath(implode('/', $parts)); -- if ($targetNode instanceof Node) { -+ if ($targetNode instanceof INode) { - $node = $targetNode; - break; - } --- -2.52.0 - diff --git a/.patches/sabre-vobject-iTipBroker-replies.patch b/.patches/sabre-vobject-iTipBroker-replies.patch deleted file mode 100644 index b335b84eb..000000000 --- a/.patches/sabre-vobject-iTipBroker-replies.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 49d53f187089ec9a67917e564a87c6dea89ea839 Mon Sep 17 00:00:00 2001 -From: SebastianKrupinski -Date: Tue, 30 Dec 2025 13:48:29 -0500 -Subject: [PATCH] fix: send participation reply on fresh event - -Signed-off-by: SebastianKrupinski ---- - lib/ITip/Broker.php | 25 ++++++--- - .../VObject/ITip/BrokerAttendeeReplyTest.php | 52 +++++++++++++++++++ - 2 files changed, 71 insertions(+), 6 deletions(-) - -diff --git a/lib/ITip/Broker.php b/lib/ITip/Broker.php -index 76ee0c71..e621c6e2 100644 ---- a/lib/ITip/Broker.php -+++ b/lib/ITip/Broker.php -@@ -246,16 +246,29 @@ public function parseEvent($calendar, $userHref, $oldCalendar = null): array - $baseCalendar = $oldCalendar; - } - -+ // Check if the user is the organizer - if (in_array($eventInfo['organizer'], $userHref)) { - return $this->parseEventForOrganizer($baseCalendar, $eventInfo, $oldEventInfo); -- } elseif ($oldCalendar) { -- // We need to figure out if the user is an attendee, but we're only -- // doing so if there's an oldCalendar, because we only want to -- // process updates, not creation of new events. -- foreach ($eventInfo['attendees'] as $attendee) { -- if (in_array($attendee['href'], $userHref)) { -+ } -+ -+ // Check if the user is an attendee -+ foreach ($eventInfo['attendees'] as $attendee) { -+ if (in_array($attendee['href'], $userHref)) { -+ // If this is a event update, we always generate a reply -+ if ($oldCalendar) { - return $this->parseEventForAttendee($baseCalendar, $eventInfo, $oldEventInfo, $attendee['href']); - } -+ -+ // If this is a new event, we only generate a reply if the participation status is set -+ foreach ($attendee['instances'] as $instance) { -+ if (isset($instance['partstat']) && 'NEEDS-ACTION' !== $instance['partstat']) { -+ // Attendee has responded (ACCEPTED/DECLINED/TENTATIVE) - generate REPLY -+ return $this->parseEventForAttendee($baseCalendar, $eventInfo, $oldEventInfo, $attendee['href']); -+ } -+ } -+ -+ // User is attendee but no response to process -+ break; - } - } - \ No newline at end of file diff --git a/.patches/sabre-vobject-rdate.patch b/.patches/sabre-vobject-rdate.patch deleted file mode 100644 index e18bf1bd4..000000000 --- a/.patches/sabre-vobject-rdate.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 099a37744a3aaf368bdd339f9c01e34553e8828e Mon Sep 17 00:00:00 2001 -From: SebastianKrupinski -Date: Wed, 19 Nov 2025 19:19:32 -0500 -Subject: [PATCH] fix: use RDATE in time range check and use all instances - -Signed-off-by: SebastianKrupinski ---- - lib/Component/VEvent.php | 2 +- - lib/Recur/EventIterator.php | 6 ++++- - tests/VObject/Component/VEventTest.php | 35 ++++++++++++++++++++++++++ - 3 files changed, 41 insertions(+), 2 deletions(-) - -diff --git a/lib/Component/VEvent.php b/lib/Component/VEvent.php -index e29e56322..6ca196d66 100644 ---- a/lib/Component/VEvent.php -+++ b/lib/Component/VEvent.php -@@ -41,7 +41,7 @@ class VEvent extends VObject\Component - */ - public function isInTimeRange(\DateTimeInterface $start, \DateTimeInterface $end): bool - { -- if ($this->RRULE) { -+ if ($this->RRULE || $this->RDATE) { - try { - $it = new EventIterator($this, null, $start->getTimezone()); - } catch (NoInstancesException $e) { -diff --git a/lib/Recur/EventIterator.php b/lib/Recur/EventIterator.php -index 2c93bcf4e..79a070ee4 100644 ---- a/lib/Recur/EventIterator.php -+++ b/lib/Recur/EventIterator.php -@@ -164,8 +164,12 @@ public function __construct($input, ?string $uid = null, ?\DateTimeZone $timeZon - } - - if (isset($this->masterEvent->RDATE)) { -+ $rdateValues = []; -+ foreach ($this->masterEvent->RDATE as $rdate) { -+ $rdateValues = array_merge($rdateValues, $rdate->getParts()); -+ } - $this->recurIterator = new RDateIterator( -- $this->masterEvent->RDATE->getParts(), -+ $rdateValues, - $this->startDate - ); - } elseif (isset($this->masterEvent->RRULE)) { diff --git a/composer.json b/composer.json index 13de58c4e..277d50b09 100644 --- a/composer.json +++ b/composer.json @@ -55,7 +55,7 @@ "psr/http-client": "^1.0.3", "psr/log": "^3.0.2", "punic/punic": "^3.8", - "sabre/dav": "^4.7.0", + "sabre/dav": "^4.7.1", "stecman/symfony-console-completion": "^0.14.0", "symfony/console": "^6.4.12", "symfony/event-dispatcher": "^6.4.8", diff --git a/composer.lock b/composer.lock index 9b0499273..7706682b4 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c1dfd2be160fe23f31a9c9a4e0d6392e", + "content-hash": "b9fa342d17bfb7a529105a564d924d86", "packages": [ { "name": "aws/aws-crt-php", @@ -3701,16 +3701,16 @@ }, { "name": "sabre/dav", - "version": "4.7.0", + "version": "4.7.1", "source": { "type": "git", "url": "https://github.com/sabre-io/dav.git", - "reference": "074373bcd689a30bcf5aaa6bbb20a3395964ce7a" + "reference": "f36f002dce082e1d425c4a0dc8c71a6b176c3b07" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sabre-io/dav/zipball/074373bcd689a30bcf5aaa6bbb20a3395964ce7a", - "reference": "074373bcd689a30bcf5aaa6bbb20a3395964ce7a", + "url": "https://api.github.com/repos/sabre-io/dav/zipball/f36f002dce082e1d425c4a0dc8c71a6b176c3b07", + "reference": "f36f002dce082e1d425c4a0dc8c71a6b176c3b07", "shasum": "" }, "require": { @@ -3780,27 +3780,27 @@ "issues": "https://github.com/sabre-io/dav/issues", "source": "https://github.com/fruux/sabre-dav" }, - "time": "2024-10-29T11:46:02+00:00" + "time": "2026-07-07T08:39:09+00:00" }, { "name": "sabre/event", - "version": "5.1.7", + "version": "5.1.9", "source": { "type": "git", "url": "https://github.com/sabre-io/event.git", - "reference": "86d57e305c272898ba3c28e9bd3d65d5464587c2" + "reference": "743f1d04811fd5b89f67878d002f6a273ccb089f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sabre-io/event/zipball/86d57e305c272898ba3c28e9bd3d65d5464587c2", - "reference": "86d57e305c272898ba3c28e9bd3d65d5464587c2", + "url": "https://api.github.com/repos/sabre-io/event/zipball/743f1d04811fd5b89f67878d002f6a273ccb089f", + "reference": "743f1d04811fd5b89f67878d002f6a273ccb089f", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "~2.17.1||^3.63", + "friendsofphp/php-cs-fixer": "~2.17.1||^3.95", "phpstan/phpstan": "^0.12", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6" }, @@ -3846,20 +3846,20 @@ "issues": "https://github.com/sabre-io/event/issues", "source": "https://github.com/fruux/sabre-event" }, - "time": "2024-08-27T11:23:05+00:00" + "time": "2026-07-07T09:13:04+00:00" }, { "name": "sabre/http", - "version": "5.1.12", + "version": "5.1.13", "source": { "type": "git", "url": "https://github.com/sabre-io/http.git", - "reference": "dedff73f3995578bc942fa4c8484190cac14f139" + "reference": "7c2a14097d1a0de2347dcbdc91a02f38e338f4db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sabre-io/http/zipball/dedff73f3995578bc942fa4c8484190cac14f139", - "reference": "dedff73f3995578bc942fa4c8484190cac14f139", + "url": "https://api.github.com/repos/sabre-io/http/zipball/7c2a14097d1a0de2347dcbdc91a02f38e338f4db", + "reference": "7c2a14097d1a0de2347dcbdc91a02f38e338f4db", "shasum": "" }, "require": { @@ -3871,7 +3871,7 @@ "sabre/uri": "^2.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "~2.17.1||^3.63", + "friendsofphp/php-cs-fixer": "~2.17.1||3.63.2", "phpstan/phpstan": "^0.12", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6" }, @@ -3909,7 +3909,7 @@ "issues": "https://github.com/sabre-io/http/issues", "source": "https://github.com/fruux/sabre-http" }, - "time": "2024-08-27T16:07:41+00:00" + "time": "2025-09-09T10:21:47+00:00" }, { "name": "sabre/uri", @@ -3973,16 +3973,16 @@ }, { "name": "sabre/vobject", - "version": "4.5.6", + "version": "4.6.1", "source": { "type": "git", "url": "https://github.com/sabre-io/vobject.git", - "reference": "900266bb3bd448a9f7f41f82344ad0aba237cb27" + "reference": "63613f6c53a0a2bddfe22caba0d052e6c59f7d0e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sabre-io/vobject/zipball/900266bb3bd448a9f7f41f82344ad0aba237cb27", - "reference": "900266bb3bd448a9f7f41f82344ad0aba237cb27", + "url": "https://api.github.com/repos/sabre-io/vobject/zipball/63613f6c53a0a2bddfe22caba0d052e6c59f7d0e", + "reference": "63613f6c53a0a2bddfe22caba0d052e6c59f7d0e", "shasum": "" }, "require": { @@ -3992,7 +3992,7 @@ }, "require-dev": { "friendsofphp/php-cs-fixer": "~2.17.1", - "phpstan/phpstan": "^0.12 || ^1.11", + "phpstan/phpstan": "^0.12 || ^1.12 || ^2.0", "phpunit/php-invoker": "^2.0 || ^3.1", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6" }, @@ -4073,7 +4073,7 @@ "issues": "https://github.com/sabre-io/vobject/issues", "source": "https://github.com/fruux/sabre-vobject" }, - "time": "2024-10-14T11:53:54+00:00" + "time": "2026-07-07T03:20:17+00:00" }, { "name": "sabre/xml", diff --git a/composer.patches.json b/composer.patches.json index 366d01a7d..e703a4212 100644 --- a/composer.patches.json +++ b/composer.patches.json @@ -4,13 +4,6 @@ "Break frame parsing on invalid frame": ".patches/mp3info-break-frame-parsing.patch", "fix incorrect lookup for mpeg header": ".patches/mp3info-fix-incorrect-lookup-for-mpeg-header.patch" }, - "sabre/dav": { - "Check for instanceof INode instead of Node": ".patches/check-for-instanceof-INode-instead-of-Node-1595.patch" - }, - "sabre/vobject": { - "fix use RDATE in time range check and use all instances": ".patches/sabre-vobject-rdate.patch", - "fix send participation reply on fresh event": ".patches/sabre-vobject-iTipBroker-replies.patch" - }, "sabre/xml": { "Remove callable serialization": ".patches/sabre-xml-remove-callable-serialization.patch" } diff --git a/composer/LICENSE b/composer/LICENSE index f27399a04..62ecfd8d0 100644 --- a/composer/LICENSE +++ b/composer/LICENSE @@ -1,4 +1,3 @@ - Copyright (c) Nils Adermann, Jordi Boggiano Permission is hereby granted, free of charge, to any person obtaining a copy @@ -18,4 +17,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - diff --git a/composer/installed.json b/composer/installed.json index 7344b235a..9c968e87a 100644 --- a/composer/installed.json +++ b/composer/installed.json @@ -3866,17 +3866,17 @@ }, { "name": "sabre/dav", - "version": "4.7.0", - "version_normalized": "4.7.0.0", + "version": "4.7.1", + "version_normalized": "4.7.1.0", "source": { "type": "git", "url": "https://github.com/sabre-io/dav.git", - "reference": "074373bcd689a30bcf5aaa6bbb20a3395964ce7a" + "reference": "f36f002dce082e1d425c4a0dc8c71a6b176c3b07" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sabre-io/dav/zipball/074373bcd689a30bcf5aaa6bbb20a3395964ce7a", - "reference": "074373bcd689a30bcf5aaa6bbb20a3395964ce7a", + "url": "https://api.github.com/repos/sabre-io/dav/zipball/f36f002dce082e1d425c4a0dc8c71a6b176c3b07", + "reference": "f36f002dce082e1d425c4a0dc8c71a6b176c3b07", "shasum": "" }, "require": { @@ -3910,17 +3910,12 @@ "ext-imap": "*", "ext-pdo": "*" }, - "time": "2024-10-29T11:46:02+00:00", + "time": "2026-07-07T08:39:09+00:00", "bin": [ "bin/sabredav", "bin/naturalselection" ], "type": "library", - "extra": { - "patches_applied": { - "Check for instanceof INode instead of Node": ".patches/check-for-instanceof-INode-instead-of-Node-1595.patch" - } - }, "installation-source": "dist", "autoload": { "psr-4": { @@ -3957,28 +3952,28 @@ }, { "name": "sabre/event", - "version": "5.1.7", - "version_normalized": "5.1.7.0", + "version": "5.1.9", + "version_normalized": "5.1.9.0", "source": { "type": "git", "url": "https://github.com/sabre-io/event.git", - "reference": "86d57e305c272898ba3c28e9bd3d65d5464587c2" + "reference": "743f1d04811fd5b89f67878d002f6a273ccb089f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sabre-io/event/zipball/86d57e305c272898ba3c28e9bd3d65d5464587c2", - "reference": "86d57e305c272898ba3c28e9bd3d65d5464587c2", + "url": "https://api.github.com/repos/sabre-io/event/zipball/743f1d04811fd5b89f67878d002f6a273ccb089f", + "reference": "743f1d04811fd5b89f67878d002f6a273ccb089f", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "~2.17.1||^3.63", + "friendsofphp/php-cs-fixer": "~2.17.1||^3.95", "phpstan/phpstan": "^0.12", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6" }, - "time": "2024-08-27T11:23:05+00:00", + "time": "2026-07-07T09:13:04+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -4026,17 +4021,17 @@ }, { "name": "sabre/http", - "version": "5.1.12", - "version_normalized": "5.1.12.0", + "version": "5.1.13", + "version_normalized": "5.1.13.0", "source": { "type": "git", "url": "https://github.com/sabre-io/http.git", - "reference": "dedff73f3995578bc942fa4c8484190cac14f139" + "reference": "7c2a14097d1a0de2347dcbdc91a02f38e338f4db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sabre-io/http/zipball/dedff73f3995578bc942fa4c8484190cac14f139", - "reference": "dedff73f3995578bc942fa4c8484190cac14f139", + "url": "https://api.github.com/repos/sabre-io/http/zipball/7c2a14097d1a0de2347dcbdc91a02f38e338f4db", + "reference": "7c2a14097d1a0de2347dcbdc91a02f38e338f4db", "shasum": "" }, "require": { @@ -4048,14 +4043,14 @@ "sabre/uri": "^2.0" }, "require-dev": { - "friendsofphp/php-cs-fixer": "~2.17.1||^3.63", + "friendsofphp/php-cs-fixer": "~2.17.1||3.63.2", "phpstan/phpstan": "^0.12", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6" }, "suggest": { "ext-curl": " to make http requests with the Client class" }, - "time": "2024-08-27T16:07:41+00:00", + "time": "2025-09-09T10:21:47+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -4155,17 +4150,17 @@ }, { "name": "sabre/vobject", - "version": "4.5.6", - "version_normalized": "4.5.6.0", + "version": "4.6.1", + "version_normalized": "4.6.1.0", "source": { "type": "git", "url": "https://github.com/sabre-io/vobject.git", - "reference": "900266bb3bd448a9f7f41f82344ad0aba237cb27" + "reference": "63613f6c53a0a2bddfe22caba0d052e6c59f7d0e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sabre-io/vobject/zipball/900266bb3bd448a9f7f41f82344ad0aba237cb27", - "reference": "900266bb3bd448a9f7f41f82344ad0aba237cb27", + "url": "https://api.github.com/repos/sabre-io/vobject/zipball/63613f6c53a0a2bddfe22caba0d052e6c59f7d0e", + "reference": "63613f6c53a0a2bddfe22caba0d052e6c59f7d0e", "shasum": "" }, "require": { @@ -4175,14 +4170,14 @@ }, "require-dev": { "friendsofphp/php-cs-fixer": "~2.17.1", - "phpstan/phpstan": "^0.12 || ^1.11", + "phpstan/phpstan": "^0.12 || ^1.12 || ^2.0", "phpunit/php-invoker": "^2.0 || ^3.1", "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6" }, "suggest": { "hoa/bench": "If you would like to run the benchmark scripts" }, - "time": "2024-10-14T11:53:54+00:00", + "time": "2026-07-07T03:20:17+00:00", "bin": [ "bin/vobject", "bin/generate_vcards" @@ -4191,10 +4186,6 @@ "extra": { "branch-alias": { "dev-master": "4.0.x-dev" - }, - "patches_applied": { - "fix use RDATE in time range check and use all instances": ".patches/sabre-vobject-rdate.patch", - "fix send participation reply on fresh event": ".patches/sabre-vobject-iTipBroker-replies.patch" } }, "installation-source": "dist", diff --git a/composer/installed.php b/composer/installed.php index b0a4ed767..437133521 100644 --- a/composer/installed.php +++ b/composer/installed.php @@ -605,27 +605,27 @@ ), ), 'sabre/dav' => array( - 'pretty_version' => '4.7.0', - 'version' => '4.7.0.0', - 'reference' => '074373bcd689a30bcf5aaa6bbb20a3395964ce7a', + 'pretty_version' => '4.7.1', + 'version' => '4.7.1.0', + 'reference' => 'f36f002dce082e1d425c4a0dc8c71a6b176c3b07', 'type' => 'library', 'install_path' => __DIR__ . '/../sabre/dav', 'aliases' => array(), 'dev_requirement' => false, ), 'sabre/event' => array( - 'pretty_version' => '5.1.7', - 'version' => '5.1.7.0', - 'reference' => '86d57e305c272898ba3c28e9bd3d65d5464587c2', + 'pretty_version' => '5.1.9', + 'version' => '5.1.9.0', + 'reference' => '743f1d04811fd5b89f67878d002f6a273ccb089f', 'type' => 'library', 'install_path' => __DIR__ . '/../sabre/event', 'aliases' => array(), 'dev_requirement' => false, ), 'sabre/http' => array( - 'pretty_version' => '5.1.12', - 'version' => '5.1.12.0', - 'reference' => 'dedff73f3995578bc942fa4c8484190cac14f139', + 'pretty_version' => '5.1.13', + 'version' => '5.1.13.0', + 'reference' => '7c2a14097d1a0de2347dcbdc91a02f38e338f4db', 'type' => 'library', 'install_path' => __DIR__ . '/../sabre/http', 'aliases' => array(), @@ -641,9 +641,9 @@ 'dev_requirement' => false, ), 'sabre/vobject' => array( - 'pretty_version' => '4.5.6', - 'version' => '4.5.6.0', - 'reference' => '900266bb3bd448a9f7f41f82344ad0aba237cb27', + 'pretty_version' => '4.6.1', + 'version' => '4.6.1.0', + 'reference' => '63613f6c53a0a2bddfe22caba0d052e6c59f7d0e', 'type' => 'library', 'install_path' => __DIR__ . '/../sabre/vobject', 'aliases' => array(), diff --git a/sabre/dav/PATCHES.txt b/sabre/dav/PATCHES.txt deleted file mode 100644 index 61140f3cf..000000000 --- a/sabre/dav/PATCHES.txt +++ /dev/null @@ -1,7 +0,0 @@ -This file was automatically generated by Composer Patches (https://github.com/cweagans/composer-patches) -Patches applied to this directory: - -Check for instanceof INode instead of Node -Source: .patches/check-for-instanceof-INode-instead-of-Node-1595.patch - - diff --git a/sabre/dav/lib/CalDAV/Backend/PDO.php b/sabre/dav/lib/CalDAV/Backend/PDO.php index 634b9828c..528fba347 100644 --- a/sabre/dav/lib/CalDAV/Backend/PDO.php +++ b/sabre/dav/lib/CalDAV/Backend/PDO.php @@ -182,8 +182,8 @@ public function getCalendarsForUser($principalUri) 'id' => [(int) $row['calendarid'], (int) $row['id']], 'uri' => $row['uri'], 'principaluri' => $row['principaluri'], - '{'.CalDAV\Plugin::NS_CALENDARSERVER.'}getctag' => 'http://sabre.io/ns/sync/'.($row['synctoken'] ? $row['synctoken'] : '0'), - '{http://sabredav.org/ns}sync-token' => $row['synctoken'] ? $row['synctoken'] : '0', + '{'.CalDAV\Plugin::NS_CALENDARSERVER.'}getctag' => 'http://sabre.io/ns/sync/'.($row['synctoken'] ?: '0'), + '{http://sabredav.org/ns}sync-token' => $row['synctoken'] ?: '0', '{'.CalDAV\Plugin::NS_CALDAV.'}supported-calendar-component-set' => new CalDAV\Xml\Property\SupportedCalendarComponentSet($components), '{'.CalDAV\Plugin::NS_CALDAV.'}schedule-calendar-transp' => new CalDAV\Xml\Property\ScheduleCalendarTransp($row['transparent'] ? 'transparent' : 'opaque'), 'share-resource-uri' => '/ns/share/'.$row['calendarid'], @@ -306,7 +306,7 @@ public function updateCalendar($calendarId, PropPatch $propPatch) switch ($propertyName) { case '{'.CalDAV\Plugin::NS_CALDAV.'}schedule-calendar-transp': $fieldName = 'transparent'; - $newValues[$fieldName] = 'transparent' === $propertyValue->getValue(); + $newValues[$fieldName] = 'transparent' === $propertyValue->getValue() ? 1 : 0; break; default: $fieldName = $this->propertyMap[$propertyName]; @@ -544,19 +544,19 @@ public function createCalendarObject($calendarId, $objectUri, $calendarData) $extraData = $this->getDenormalizedData($calendarData); - $stmt = $this->pdo->prepare('INSERT INTO '.$this->calendarObjectTableName.' (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES (?,?,?,?,?,?,?,?,?,?)'); - $stmt->execute([ - $calendarId, - $objectUri, - $calendarData, - time(), - $extraData['etag'], - $extraData['size'], - $extraData['componentType'], - $extraData['firstOccurence'], - $extraData['lastOccurence'], - $extraData['uid'], - ]); + $stmt = $this->pdo->prepare('INSERT INTO '.$this->calendarObjectTableName.' (calendarid, uri, calendardata, lastmodified, etag, size, componenttype, firstoccurence, lastoccurence, uid) VALUES (:calendarid, :uri, :calendardata, :lastmodified, :etag, :size, :componenttype, :firstoccurence, :lastoccurence, :uid)'); + $lastmodified = time(); + $stmt->bindParam('calendarid', $calendarId, \PDO::PARAM_INT); + $stmt->bindParam('uri', $objectUri, \PDO::PARAM_STR); + $stmt->bindParam('calendardata', $calendarData, \PDO::PARAM_LOB); + $stmt->bindParam('lastmodified', $lastmodified, \PDO::PARAM_INT); + $stmt->bindParam('etag', $extraData['etag'], \PDO::PARAM_STR); + $stmt->bindParam('size', $extraData['size'], \PDO::PARAM_INT); + $stmt->bindParam('componenttype', $extraData['componentType'], \PDO::PARAM_STR); + $stmt->bindParam('firstoccurence', $extraData['firstOccurence'], \PDO::PARAM_INT); + $stmt->bindParam('lastoccurence', $extraData['lastOccurence'], \PDO::PARAM_INT); + $stmt->bindParam('uid', $extraData['uid'], \PDO::PARAM_STR); + $stmt->execute(); $this->addChange($calendarId, $objectUri, 1); return '"'.$extraData['etag'].'"'; @@ -590,8 +590,19 @@ public function updateCalendarObject($calendarId, $objectUri, $calendarData) $extraData = $this->getDenormalizedData($calendarData); - $stmt = $this->pdo->prepare('UPDATE '.$this->calendarObjectTableName.' SET calendardata = ?, lastmodified = ?, etag = ?, size = ?, componenttype = ?, firstoccurence = ?, lastoccurence = ?, uid = ? WHERE calendarid = ? AND uri = ?'); - $stmt->execute([$calendarData, time(), $extraData['etag'], $extraData['size'], $extraData['componentType'], $extraData['firstOccurence'], $extraData['lastOccurence'], $extraData['uid'], $calendarId, $objectUri]); + $stmt = $this->pdo->prepare('UPDATE '.$this->calendarObjectTableName.' SET calendardata = :calendardata, lastmodified = :lastmodified, etag = :etag, size = :size, componenttype = :componenttype, firstoccurence = :firstoccurence, lastoccurence = :lastoccurence, uid = :uid WHERE calendarid = :calendarid AND uri = :uri'); + $lastmodified = time(); + $stmt->bindParam('calendardata', $calendarData, \PDO::PARAM_LOB); + $stmt->bindParam('lastmodified', $lastmodified, \PDO::PARAM_INT); + $stmt->bindParam('etag', $extraData['etag'], \PDO::PARAM_STR); + $stmt->bindParam('size', $extraData['size'], \PDO::PARAM_INT); + $stmt->bindParam('componenttype', $extraData['componentType'], \PDO::PARAM_STR); + $stmt->bindParam('firstoccurence', $extraData['firstOccurence'], \PDO::PARAM_INT); + $stmt->bindParam('lastoccurence', $extraData['lastOccurence'], \PDO::PARAM_INT); + $stmt->bindParam('uid', $extraData['uid'], \PDO::PARAM_STR); + $stmt->bindParam('calendarid', $calendarId, \PDO::PARAM_INT); + $stmt->bindParam('uri', $objectUri, \PDO::PARAM_STR); + $stmt->execute(); $this->addChange($calendarId, $objectUri, 2); @@ -1311,13 +1322,22 @@ public function deleteSchedulingObject($principalUri, $objectUri) */ public function createSchedulingObject($principalUri, $objectUri, $objectData) { - $stmt = $this->pdo->prepare('INSERT INTO '.$this->schedulingObjectTableName.' (principaluri, calendardata, uri, lastmodified, etag, size) VALUES (?, ?, ?, ?, ?, ?)'); + $stmt = $this->pdo->prepare('INSERT INTO '.$this->schedulingObjectTableName.' (principaluri, calendardata, uri, lastmodified, etag, size) VALUES (:principaluri, :calendardata, :uri, :lastmodified, :etag, :size)'); if (is_resource($objectData)) { $objectData = stream_get_contents($objectData); } - $stmt->execute([$principalUri, $objectData, $objectUri, time(), md5($objectData), strlen($objectData)]); + $lastmodified = time(); + $etag = md5($objectData); + $size = strlen($objectData); + $stmt->bindParam('principaluri', $principalUri, \PDO::PARAM_STR); + $stmt->bindParam('calendardata', $objectData, \PDO::PARAM_LOB); + $stmt->bindParam('uri', $objectUri, \PDO::PARAM_STR); + $stmt->bindParam('lastmodified', $lastmodified, \PDO::PARAM_INT); + $stmt->bindParam('etag', $etag, \PDO::PARAM_STR); + $stmt->bindParam('size', $size, \PDO::PARAM_INT); + $stmt->execute(); } /** diff --git a/sabre/dav/lib/CalDAV/Notifications/Plugin.php b/sabre/dav/lib/CalDAV/Notifications/Plugin.php index 56b2fe938..e6949101f 100644 --- a/sabre/dav/lib/CalDAV/Notifications/Plugin.php +++ b/sabre/dav/lib/CalDAV/Notifications/Plugin.php @@ -68,7 +68,7 @@ public function initialize(Server $server) $server->on('propFind', [$this, 'propFind']); $server->xml->namespaceMap[self::NS_CALENDARSERVER] = 'cs'; - $server->resourceTypeMapping['\\Sabre\\CalDAV\\Notifications\\ICollection'] = '{'.self::NS_CALENDARSERVER.'}notification'; + $server->resourceTypeMapping[\Sabre\CalDAV\Notifications\ICollection::class] = '{'.self::NS_CALENDARSERVER.'}notification'; array_push($server->protectedProperties, '{'.self::NS_CALENDARSERVER.'}notification-URL', diff --git a/sabre/dav/lib/CalDAV/Plugin.php b/sabre/dav/lib/CalDAV/Plugin.php index ccb722f85..167025e67 100644 --- a/sabre/dav/lib/CalDAV/Plugin.php +++ b/sabre/dav/lib/CalDAV/Plugin.php @@ -200,18 +200,18 @@ public function initialize(DAV\Server $server) $server->xml->namespaceMap[self::NS_CALDAV] = 'cal'; $server->xml->namespaceMap[self::NS_CALENDARSERVER] = 'cs'; - $server->xml->elementMap['{'.self::NS_CALDAV.'}supported-calendar-component-set'] = 'Sabre\\CalDAV\\Xml\\Property\\SupportedCalendarComponentSet'; - $server->xml->elementMap['{'.self::NS_CALDAV.'}calendar-query'] = 'Sabre\\CalDAV\\Xml\\Request\\CalendarQueryReport'; - $server->xml->elementMap['{'.self::NS_CALDAV.'}calendar-multiget'] = 'Sabre\\CalDAV\\Xml\\Request\\CalendarMultiGetReport'; - $server->xml->elementMap['{'.self::NS_CALDAV.'}free-busy-query'] = 'Sabre\\CalDAV\\Xml\\Request\\FreeBusyQueryReport'; - $server->xml->elementMap['{'.self::NS_CALDAV.'}mkcalendar'] = 'Sabre\\CalDAV\\Xml\\Request\\MkCalendar'; - $server->xml->elementMap['{'.self::NS_CALDAV.'}schedule-calendar-transp'] = 'Sabre\\CalDAV\\Xml\\Property\\ScheduleCalendarTransp'; - $server->xml->elementMap['{'.self::NS_CALDAV.'}supported-calendar-component-set'] = 'Sabre\\CalDAV\\Xml\\Property\\SupportedCalendarComponentSet'; - - $server->resourceTypeMapping['\\Sabre\\CalDAV\\ICalendar'] = '{urn:ietf:params:xml:ns:caldav}calendar'; - - $server->resourceTypeMapping['\\Sabre\\CalDAV\\Principal\\IProxyRead'] = '{http://calendarserver.org/ns/}calendar-proxy-read'; - $server->resourceTypeMapping['\\Sabre\\CalDAV\\Principal\\IProxyWrite'] = '{http://calendarserver.org/ns/}calendar-proxy-write'; + $server->xml->elementMap['{'.self::NS_CALDAV.'}supported-calendar-component-set'] = \Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet::class; + $server->xml->elementMap['{'.self::NS_CALDAV.'}calendar-query'] = \Sabre\CalDAV\Xml\Request\CalendarQueryReport::class; + $server->xml->elementMap['{'.self::NS_CALDAV.'}calendar-multiget'] = \Sabre\CalDAV\Xml\Request\CalendarMultiGetReport::class; + $server->xml->elementMap['{'.self::NS_CALDAV.'}free-busy-query'] = \Sabre\CalDAV\Xml\Request\FreeBusyQueryReport::class; + $server->xml->elementMap['{'.self::NS_CALDAV.'}mkcalendar'] = \Sabre\CalDAV\Xml\Request\MkCalendar::class; + $server->xml->elementMap['{'.self::NS_CALDAV.'}schedule-calendar-transp'] = \Sabre\CalDAV\Xml\Property\ScheduleCalendarTransp::class; + $server->xml->elementMap['{'.self::NS_CALDAV.'}supported-calendar-component-set'] = \Sabre\CalDAV\Xml\Property\SupportedCalendarComponentSet::class; + + $server->resourceTypeMapping[\Sabre\CalDAV\ICalendar::class] = '{urn:ietf:params:xml:ns:caldav}calendar'; + + $server->resourceTypeMapping[\Sabre\CalDAV\Principal\IProxyRead::class] = '{http://calendarserver.org/ns/}calendar-proxy-read'; + $server->resourceTypeMapping[\Sabre\CalDAV\Principal\IProxyWrite::class] = '{http://calendarserver.org/ns/}calendar-proxy-write'; array_push($server->protectedProperties, '{'.self::NS_CALDAV.'}supported-calendar-component-set', diff --git a/sabre/dav/lib/CalDAV/Schedule/Plugin.php b/sabre/dav/lib/CalDAV/Schedule/Plugin.php index 5bca56d47..8577ae9f7 100644 --- a/sabre/dav/lib/CalDAV/Schedule/Plugin.php +++ b/sabre/dav/lib/CalDAV/Schedule/Plugin.php @@ -113,8 +113,8 @@ public function initialize(Server $server) * This information ensures that the {DAV:}resourcetype property has * the correct values. */ - $server->resourceTypeMapping['\\Sabre\\CalDAV\\Schedule\\IOutbox'] = $ns.'schedule-outbox'; - $server->resourceTypeMapping['\\Sabre\\CalDAV\\Schedule\\IInbox'] = $ns.'schedule-inbox'; + $server->resourceTypeMapping[\Sabre\CalDAV\Schedule\IOutbox::class] = $ns.'schedule-outbox'; + $server->resourceTypeMapping[\Sabre\CalDAV\Schedule\IInbox::class] = $ns.'schedule-inbox'; /* * Properties we protect are made read-only by the server. diff --git a/sabre/dav/lib/CalDAV/SharingPlugin.php b/sabre/dav/lib/CalDAV/SharingPlugin.php index bacfe0441..56962fc14 100644 --- a/sabre/dav/lib/CalDAV/SharingPlugin.php +++ b/sabre/dav/lib/CalDAV/SharingPlugin.php @@ -83,8 +83,8 @@ public function initialize(DAV\Server $server) '{'.Plugin::NS_CALENDARSERVER.'}shared-url' ); - $this->server->xml->elementMap['{'.Plugin::NS_CALENDARSERVER.'}share'] = 'Sabre\\CalDAV\\Xml\\Request\\Share'; - $this->server->xml->elementMap['{'.Plugin::NS_CALENDARSERVER.'}invite-reply'] = 'Sabre\\CalDAV\\Xml\\Request\\InviteReply'; + $this->server->xml->elementMap['{'.Plugin::NS_CALENDARSERVER.'}share'] = \Sabre\CalDAV\Xml\Request\Share::class; + $this->server->xml->elementMap['{'.Plugin::NS_CALENDARSERVER.'}invite-reply'] = \Sabre\CalDAV\Xml\Request\InviteReply::class; $this->server->on('propFind', [$this, 'propFindEarly']); $this->server->on('propFind', [$this, 'propFindLate'], 150); diff --git a/sabre/dav/lib/CalDAV/Subscriptions/Plugin.php b/sabre/dav/lib/CalDAV/Subscriptions/Plugin.php index 6cfcc1a7b..f9b4ae74c 100644 --- a/sabre/dav/lib/CalDAV/Subscriptions/Plugin.php +++ b/sabre/dav/lib/CalDAV/Subscriptions/Plugin.php @@ -31,11 +31,11 @@ class Plugin extends ServerPlugin */ public function initialize(Server $server) { - $server->resourceTypeMapping['Sabre\\CalDAV\\Subscriptions\\ISubscription'] = + $server->resourceTypeMapping[\Sabre\CalDAV\Subscriptions\ISubscription::class] = '{http://calendarserver.org/ns/}subscribed'; $server->xml->elementMap['{http://calendarserver.org/ns/}source'] = - 'Sabre\\DAV\\Xml\\Property\\Href'; + \Sabre\DAV\Xml\Property\Href::class; $server->on('propFind', [$this, 'propFind'], 150); } diff --git a/sabre/dav/lib/CalDAV/Xml/Request/CalendarMultiGetReport.php b/sabre/dav/lib/CalDAV/Xml/Request/CalendarMultiGetReport.php index 4771a2070..228210de3 100644 --- a/sabre/dav/lib/CalDAV/Xml/Request/CalendarMultiGetReport.php +++ b/sabre/dav/lib/CalDAV/Xml/Request/CalendarMultiGetReport.php @@ -86,8 +86,8 @@ class CalendarMultiGetReport implements XmlDeserializable public static function xmlDeserialize(Reader $reader) { $elems = $reader->parseInnerTree([ - '{urn:ietf:params:xml:ns:caldav}calendar-data' => 'Sabre\\CalDAV\\Xml\\Filter\\CalendarData', - '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue', + '{urn:ietf:params:xml:ns:caldav}calendar-data' => \Sabre\CalDAV\Xml\Filter\CalendarData::class, + '{DAV:}prop' => \Sabre\Xml\Element\KeyValue::class, ]); $newProps = [ diff --git a/sabre/dav/lib/CalDAV/Xml/Request/CalendarQueryReport.php b/sabre/dav/lib/CalDAV/Xml/Request/CalendarQueryReport.php index 5a4df4674..d68fc3f4f 100644 --- a/sabre/dav/lib/CalDAV/Xml/Request/CalendarQueryReport.php +++ b/sabre/dav/lib/CalDAV/Xml/Request/CalendarQueryReport.php @@ -15,7 +15,7 @@ * This class parses the {urn:ietf:params:xml:ns:caldav}calendar-query * REPORT, as defined in: * - * https://tools.ietf.org/html/rfc4791#section-7.9 + * https://tools.ietf.org/html/rfc4791#section-7.8 * * @copyright Copyright (C) fruux GmbH (https://fruux.com/) * @author Evert Pot (http://www.rooftopsolutions.nl/) @@ -86,11 +86,11 @@ class CalendarQueryReport implements XmlDeserializable public static function xmlDeserialize(Reader $reader) { $elems = $reader->parseInnerTree([ - '{urn:ietf:params:xml:ns:caldav}comp-filter' => 'Sabre\\CalDAV\\Xml\\Filter\\CompFilter', - '{urn:ietf:params:xml:ns:caldav}prop-filter' => 'Sabre\\CalDAV\\Xml\\Filter\\PropFilter', - '{urn:ietf:params:xml:ns:caldav}param-filter' => 'Sabre\\CalDAV\\Xml\\Filter\\ParamFilter', - '{urn:ietf:params:xml:ns:caldav}calendar-data' => 'Sabre\\CalDAV\\Xml\\Filter\\CalendarData', - '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue', + '{urn:ietf:params:xml:ns:caldav}comp-filter' => \Sabre\CalDAV\Xml\Filter\CompFilter::class, + '{urn:ietf:params:xml:ns:caldav}prop-filter' => \Sabre\CalDAV\Xml\Filter\PropFilter::class, + '{urn:ietf:params:xml:ns:caldav}param-filter' => \Sabre\CalDAV\Xml\Filter\ParamFilter::class, + '{urn:ietf:params:xml:ns:caldav}calendar-data' => \Sabre\CalDAV\Xml\Filter\CalendarData::class, + '{DAV:}prop' => \Sabre\Xml\Element\KeyValue::class, ]); $newProps = [ diff --git a/sabre/dav/lib/CalDAV/Xml/Request/MkCalendar.php b/sabre/dav/lib/CalDAV/Xml/Request/MkCalendar.php index b5701e2ea..58017d8c2 100644 --- a/sabre/dav/lib/CalDAV/Xml/Request/MkCalendar.php +++ b/sabre/dav/lib/CalDAV/Xml/Request/MkCalendar.php @@ -62,8 +62,8 @@ public static function xmlDeserialize(Reader $reader) $self = new self(); $elementMap = $reader->elementMap; - $elementMap['{DAV:}prop'] = 'Sabre\DAV\Xml\Element\Prop'; - $elementMap['{DAV:}set'] = 'Sabre\Xml\Element\KeyValue'; + $elementMap['{DAV:}prop'] = \Sabre\DAV\Xml\Element\Prop::class; + $elementMap['{DAV:}set'] = \Sabre\Xml\Element\KeyValue::class; $elems = $reader->parseInnerTree($elementMap); foreach ($elems as $elem) { diff --git a/sabre/dav/lib/CalDAV/Xml/Request/Share.php b/sabre/dav/lib/CalDAV/Xml/Request/Share.php index d597b76f1..c3ef0f4e3 100644 --- a/sabre/dav/lib/CalDAV/Xml/Request/Share.php +++ b/sabre/dav/lib/CalDAV/Xml/Request/Share.php @@ -62,8 +62,8 @@ public function __construct(array $sharees) public static function xmlDeserialize(Reader $reader) { $elems = $reader->parseGetElements([ - '{'.Plugin::NS_CALENDARSERVER.'}set' => 'Sabre\\Xml\\Element\\KeyValue', - '{'.Plugin::NS_CALENDARSERVER.'}remove' => 'Sabre\\Xml\\Element\\KeyValue', + '{'.Plugin::NS_CALENDARSERVER.'}set' => \Sabre\Xml\Element\KeyValue::class, + '{'.Plugin::NS_CALENDARSERVER.'}remove' => \Sabre\Xml\Element\KeyValue::class, ]); $sharees = []; diff --git a/sabre/dav/lib/CardDAV/Backend/PDO.php b/sabre/dav/lib/CardDAV/Backend/PDO.php index 7b935a4ae..bf5103fb1 100644 --- a/sabre/dav/lib/CardDAV/Backend/PDO.php +++ b/sabre/dav/lib/CardDAV/Backend/PDO.php @@ -73,7 +73,7 @@ public function getAddressBooksForUser($principalUri) '{DAV:}displayname' => $row['displayname'], '{'.CardDAV\Plugin::NS_CARDDAV.'}addressbook-description' => $row['description'], '{http://calendarserver.org/ns/}getctag' => $row['synctoken'], - '{http://sabredav.org/ns}sync-token' => $row['synctoken'] ? $row['synctoken'] : '0', + '{http://sabredav.org/ns}sync-token' => $row['synctoken'] ?: '0', ]; } @@ -316,18 +316,18 @@ public function getMultipleCards($addressBookId, array $uris) */ public function createCard($addressBookId, $cardUri, $cardData) { - $stmt = $this->pdo->prepare('INSERT INTO '.$this->cardsTableName.' (carddata, uri, lastmodified, addressbookid, size, etag) VALUES (?, ?, ?, ?, ?, ?)'); + $stmt = $this->pdo->prepare('INSERT INTO '.$this->cardsTableName.' (carddata, uri, lastmodified, addressbookid, size, etag) VALUES (:carddata, :uri, :lastmodified, :addressbookid, :size, :etag)'); $etag = md5($cardData); - - $stmt->execute([ - $cardData, - $cardUri, - time(), - $addressBookId, - strlen($cardData), - $etag, - ]); + $lastmodified = time(); + $size = strlen($cardData); + $stmt->bindParam('carddata', $cardData, \PDO::PARAM_LOB); + $stmt->bindParam('uri', $cardUri, \PDO::PARAM_STR); + $stmt->bindParam('lastmodified', $lastmodified, \PDO::PARAM_INT); + $stmt->bindParam('addressbookid', $addressBookId, \PDO::PARAM_INT); + $stmt->bindParam('size', $size, \PDO::PARAM_INT); + $stmt->bindParam('etag', $etag, \PDO::PARAM_STR); + $stmt->execute(); $this->addChange($addressBookId, $cardUri, 1); @@ -362,17 +362,18 @@ public function createCard($addressBookId, $cardUri, $cardData) */ public function updateCard($addressBookId, $cardUri, $cardData) { - $stmt = $this->pdo->prepare('UPDATE '.$this->cardsTableName.' SET carddata = ?, lastmodified = ?, size = ?, etag = ? WHERE uri = ? AND addressbookid =?'); + $stmt = $this->pdo->prepare('UPDATE '.$this->cardsTableName.' SET carddata = :carddata, lastmodified = :lastmodified, size = :size, etag = :etag WHERE uri = :uri AND addressbookid = :addressbookid'); $etag = md5($cardData); - $stmt->execute([ - $cardData, - time(), - strlen($cardData), - $etag, - $cardUri, - $addressBookId, - ]); + $lastmodified = time(); + $size = strlen($cardData); + $stmt->bindParam('carddata', $cardData, \PDO::PARAM_LOB); + $stmt->bindParam('lastmodified', $lastmodified, \PDO::PARAM_INT); + $stmt->bindParam('size', $size, \PDO::PARAM_INT); + $stmt->bindParam('etag', $etag, \PDO::PARAM_STR); + $stmt->bindParam('uri', $cardUri, \PDO::PARAM_STR); + $stmt->bindParam('addressbookid', $addressBookId, \PDO::PARAM_INT); + $stmt->execute(); $this->addChange($addressBookId, $cardUri, 2); diff --git a/sabre/dav/lib/CardDAV/Plugin.php b/sabre/dav/lib/CardDAV/Plugin.php index 810ae3a1a..663c561c0 100644 --- a/sabre/dav/lib/CardDAV/Plugin.php +++ b/sabre/dav/lib/CardDAV/Plugin.php @@ -73,12 +73,12 @@ public function initialize(DAV\Server $server) $server->xml->namespaceMap[self::NS_CARDDAV] = 'card'; - $server->xml->elementMap['{'.self::NS_CARDDAV.'}addressbook-query'] = 'Sabre\\CardDAV\\Xml\\Request\\AddressBookQueryReport'; - $server->xml->elementMap['{'.self::NS_CARDDAV.'}addressbook-multiget'] = 'Sabre\\CardDAV\\Xml\\Request\\AddressBookMultiGetReport'; + $server->xml->elementMap['{'.self::NS_CARDDAV.'}addressbook-query'] = \Sabre\CardDAV\Xml\Request\AddressBookQueryReport::class; + $server->xml->elementMap['{'.self::NS_CARDDAV.'}addressbook-multiget'] = \Sabre\CardDAV\Xml\Request\AddressBookMultiGetReport::class; /* Mapping Interfaces to {DAV:}resourcetype values */ - $server->resourceTypeMapping['Sabre\\CardDAV\\IAddressBook'] = '{'.self::NS_CARDDAV.'}addressbook'; - $server->resourceTypeMapping['Sabre\\CardDAV\\IDirectory'] = '{'.self::NS_CARDDAV.'}directory'; + $server->resourceTypeMapping[\Sabre\CardDAV\IAddressBook::class] = '{'.self::NS_CARDDAV.'}addressbook'; + $server->resourceTypeMapping[\Sabre\CardDAV\IDirectory::class] = '{'.self::NS_CARDDAV.'}directory'; /* Adding properties that may never be changed */ $server->protectedProperties[] = '{'.self::NS_CARDDAV.'}supported-address-data'; @@ -86,7 +86,7 @@ public function initialize(DAV\Server $server) $server->protectedProperties[] = '{'.self::NS_CARDDAV.'}addressbook-home-set'; $server->protectedProperties[] = '{'.self::NS_CARDDAV.'}supported-collation-set'; - $server->xml->elementMap['{http://calendarserver.org/ns/}me-card'] = 'Sabre\\DAV\\Xml\\Property\\Href'; + $server->xml->elementMap['{http://calendarserver.org/ns/}me-card'] = \Sabre\DAV\Xml\Property\Href::class; $this->server = $server; } diff --git a/sabre/dav/lib/CardDAV/Xml/Request/AddressBookMultiGetReport.php b/sabre/dav/lib/CardDAV/Xml/Request/AddressBookMultiGetReport.php index 491f96908..2a6728248 100644 --- a/sabre/dav/lib/CardDAV/Xml/Request/AddressBookMultiGetReport.php +++ b/sabre/dav/lib/CardDAV/Xml/Request/AddressBookMultiGetReport.php @@ -83,8 +83,8 @@ class AddressBookMultiGetReport implements XmlDeserializable public static function xmlDeserialize(Reader $reader) { $elems = $reader->parseInnerTree([ - '{urn:ietf:params:xml:ns:carddav}address-data' => 'Sabre\\CardDAV\\Xml\\Filter\\AddressData', - '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue', + '{urn:ietf:params:xml:ns:carddav}address-data' => \Sabre\CardDAV\Xml\Filter\AddressData::class, + '{DAV:}prop' => \Sabre\Xml\Element\KeyValue::class, ]); $newProps = [ diff --git a/sabre/dav/lib/CardDAV/Xml/Request/AddressBookQueryReport.php b/sabre/dav/lib/CardDAV/Xml/Request/AddressBookQueryReport.php index 02402f6c7..241fd3a84 100644 --- a/sabre/dav/lib/CardDAV/Xml/Request/AddressBookQueryReport.php +++ b/sabre/dav/lib/CardDAV/Xml/Request/AddressBookQueryReport.php @@ -120,10 +120,10 @@ class AddressBookQueryReport implements XmlDeserializable public static function xmlDeserialize(Reader $reader) { $elems = (array) $reader->parseInnerTree([ - '{urn:ietf:params:xml:ns:carddav}prop-filter' => 'Sabre\\CardDAV\\Xml\\Filter\\PropFilter', - '{urn:ietf:params:xml:ns:carddav}param-filter' => 'Sabre\\CardDAV\\Xml\\Filter\\ParamFilter', - '{urn:ietf:params:xml:ns:carddav}address-data' => 'Sabre\\CardDAV\\Xml\\Filter\\AddressData', - '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue', + '{urn:ietf:params:xml:ns:carddav}prop-filter' => \Sabre\CardDAV\Xml\Filter\PropFilter::class, + '{urn:ietf:params:xml:ns:carddav}param-filter' => \Sabre\CardDAV\Xml\Filter\ParamFilter::class, + '{urn:ietf:params:xml:ns:carddav}address-data' => \Sabre\CardDAV\Xml\Filter\AddressData::class, + '{DAV:}prop' => \Sabre\Xml\Element\KeyValue::class, ]); $newProps = [ diff --git a/sabre/dav/lib/DAV/Browser/Plugin.php b/sabre/dav/lib/DAV/Browser/Plugin.php index a8a6f430e..5b453ac75 100644 --- a/sabre/dav/lib/DAV/Browser/Plugin.php +++ b/sabre/dav/lib/DAV/Browser/Plugin.php @@ -258,7 +258,7 @@ public function escapeHTML($value) */ public function generateDirectoryIndex($path) { - $html = $this->generateHeader($path ? $path : '/', $path); + $html = $this->generateHeader($path ?: '/', $path); $node = $this->server->tree->getNodeForPath($path); if ($node instanceof DAV\ICollection) { @@ -499,7 +499,7 @@ public function htmlActionsPanel(DAV\INode $node, &$output, $path) // We also know fairly certain that if an object is a non-extended // SimpleCollection, we won't need to show the panel either. - if ('Sabre\\DAV\\SimpleCollection' === get_class($node)) { + if (\Sabre\DAV\SimpleCollection::class === get_class($node)) { return; } diff --git a/sabre/dav/lib/DAV/CorePlugin.php b/sabre/dav/lib/DAV/CorePlugin.php index dbd8976b1..ce1e01ba9 100644 --- a/sabre/dav/lib/DAV/CorePlugin.php +++ b/sabre/dav/lib/DAV/CorePlugin.php @@ -154,7 +154,7 @@ public function httpGet(RequestInterface $request, ResponseInterface $response) // Determining the exact byte offsets if (!is_null($range[0])) { $start = $range[0]; - $end = $range[1] ? $range[1] : $nodeSize - 1; + $end = $range[1] ?: $nodeSize - 1; if ($start >= $nodeSize) { throw new Exception\RequestedRangeNotSatisfiable('The start offset ('.$range[0].') exceeded the size of the entity ('.$nodeSize.')'); } @@ -589,6 +589,11 @@ public function httpMove(RequestInterface $request, ResponseInterface $response) $moveInfo = $this->server->getCopyAndMoveInfo($request); + // MOVE does only allow "infinity" every other header value is considered invalid + if (Server::DEPTH_INFINITY !== $moveInfo['depth']) { + throw new BadRequest('The HTTP Depth header must only contain "infinity" for MOVE'); + } + if ($moveInfo['destinationExists']) { if (!$this->server->emit('beforeUnbind', [$moveInfo['destination']])) { return false; @@ -645,7 +650,7 @@ public function httpCopy(RequestInterface $request, ResponseInterface $response) if (!$this->server->emit('beforeBind', [$copyInfo['destination']])) { return false; } - if (!$this->server->emit('beforeCopy', [$path, $copyInfo['destination']])) { + if (!$this->server->emit('beforeCopy', [$path, $copyInfo['destination'], $copyInfo['depth']])) { return false; } @@ -656,8 +661,8 @@ public function httpCopy(RequestInterface $request, ResponseInterface $response) $this->server->tree->delete($copyInfo['destination']); } - $this->server->tree->copy($path, $copyInfo['destination']); - $this->server->emit('afterCopy', [$path, $copyInfo['destination']]); + $this->server->tree->copy($path, $copyInfo['destination'], $copyInfo['depth']); + $this->server->emit('afterCopy', [$path, $copyInfo['destination'], $copyInfo['depth']]); $this->server->emit('afterBind', [$copyInfo['destination']]); // If a resource was overwritten we should send a 204, otherwise a 201 diff --git a/sabre/dav/lib/DAV/FSExt/Directory.php b/sabre/dav/lib/DAV/FSExt/Directory.php index d6aea0094..d07c9ed54 100644 --- a/sabre/dav/lib/DAV/FSExt/Directory.php +++ b/sabre/dav/lib/DAV/FSExt/Directory.php @@ -143,8 +143,6 @@ public function getChildren() /** * Deletes all files in this directory, and then itself. - * - * @return bool */ public function delete() { @@ -155,8 +153,6 @@ public function delete() // Removing the directory itself rmdir($this->path); - - return true; } /** diff --git a/sabre/dav/lib/DAV/FSExt/File.php b/sabre/dav/lib/DAV/FSExt/File.php index 74849b564..c1c406881 100644 --- a/sabre/dav/lib/DAV/FSExt/File.php +++ b/sabre/dav/lib/DAV/FSExt/File.php @@ -102,12 +102,10 @@ public function get() /** * Delete the current file. - * - * @return bool */ public function delete() { - return unlink($this->path); + unlink($this->path); } /** diff --git a/sabre/dav/lib/DAV/ICopyTarget.php b/sabre/dav/lib/DAV/ICopyTarget.php index 47227138a..57d79a038 100644 --- a/sabre/dav/lib/DAV/ICopyTarget.php +++ b/sabre/dav/lib/DAV/ICopyTarget.php @@ -31,8 +31,11 @@ interface ICopyTarget extends ICollection * @param string $targetName new local file/collection name * @param string $sourcePath Full path to source node * @param INode $sourceNode Source node itself + * @param int $depth How many level of children to copy. + * The value can be 'infinity' (Sabre\DAV\Server::DEPTH_INFINITY) or a positive number including zero. + * Zero means to only copy a shallow collection with props but without children. * * @return bool */ - public function copyInto($targetName, $sourcePath, INode $sourceNode); + public function copyInto($targetName, $sourcePath, INode $sourceNode, int $depth); } diff --git a/sabre/dav/lib/DAV/Locks/Plugin.php b/sabre/dav/lib/DAV/Locks/Plugin.php index 2443f204c..57ca719b1 100644 --- a/sabre/dav/lib/DAV/Locks/Plugin.php +++ b/sabre/dav/lib/DAV/Locks/Plugin.php @@ -55,7 +55,7 @@ public function initialize(DAV\Server $server) { $this->server = $server; - $this->server->xml->elementMap['{DAV:}lockinfo'] = 'Sabre\\DAV\\Xml\\Request\\Lock'; + $this->server->xml->elementMap['{DAV:}lockinfo'] = \Sabre\DAV\Xml\Request\Lock::class; $server->on('method:LOCK', [$this, 'httpLock']); $server->on('method:UNLOCK', [$this, 'httpUnlock']); @@ -179,7 +179,6 @@ public function httpLock(RequestInterface $request, ResponseInterface $response) } } else { // Gonna check if this was a lock refresh. - $existingLocks = $this->getLocks($uri); $conditions = $this->server->getIfConditions($request); $found = null; diff --git a/sabre/dav/lib/DAV/Server.php b/sabre/dav/lib/DAV/Server.php index 3133e54ad..e122b2447 100644 --- a/sabre/dav/lib/DAV/Server.php +++ b/sabre/dav/lib/DAV/Server.php @@ -152,7 +152,7 @@ class Server implements LoggerAwareInterface, EmitterInterface * @var array */ public $resourceTypeMapping = [ - 'Sabre\\DAV\\ICollection' => '{DAV:}collection', + \Sabre\DAV\ICollection::class => '{DAV:}collection', ]; /** @@ -580,9 +580,10 @@ public function calculateUri($uri) /** * Returns the HTTP depth header. * - * This method returns the contents of the HTTP depth request header. If the depth header was 'infinity' it will return the Sabre\DAV\Server::DEPTH_INFINITY object + * This method returns the contents of the HTTP depth request header. If the depth header was 'infinity' it will return the Sabre\DAV\Server::DEPTH_INFINITY constant. * It is possible to supply a default depth value, which is used when the depth header has invalid content, or is completely non-existent * + * @param mixed $default default value to use if no header is set or has invalid value * @param mixed $default * * @return int @@ -624,11 +625,17 @@ public function getHTTPDepth($default = self::DEPTH_INFINITY) */ public function getHTTPRange() { - $range = $this->httpRequest->getHeader('range'); + $range = $this->httpRequest->getHeader('Range'); if (is_null($range)) { return null; } + // MUST ignore Range on non-GET requests (RFC 7233 ยง3.1) + $method = $this->httpRequest->getHeader('X-Sabre-Original-Method') ?: $this->httpRequest->getMethod(); + if ('GET' !== strtoupper($method)) { + return null; + } + // Matching "Range: bytes=1234-5678: both numbers are optional if (!preg_match('/^bytes=([0-9]*)-([0-9]*)$/i', $range, $matches)) { @@ -725,10 +732,18 @@ public function getCopyAndMoveInfo(RequestInterface $request) throw new Exception\BadRequest('The destination header was not supplied'); } $destination = $this->calculateUri($request->getHeader('Destination')); - $overwrite = $request->getHeader('Overwrite'); - if (!$overwrite) { - $overwrite = 'T'; + + // Depth of infinity is valid for MOVE and COPY. If it is not set the RFC requires to act like it was 'infinity'. + $depth = $request->getHeader('Depth') ?? 'infinity'; + if ('infinity' === strtolower($depth)) { + $depth = self::DEPTH_INFINITY; + } elseif (!ctype_digit($depth) || ((int) $depth) < 0) { + throw new Exception\BadRequest('The HTTP Depth header may only be "infinity", 0 or a positive integer'); + } else { + $depth = (int) $depth; } + + $overwrite = $request->getHeader('Overwrite') ?? 'T'; if ('T' == strtoupper($overwrite)) { $overwrite = true; } elseif ('F' == strtoupper($overwrite)) { @@ -773,6 +788,7 @@ public function getCopyAndMoveInfo(RequestInterface $request) // These are the three relevant properties we need to return return [ + 'depth' => $depth, 'destination' => $destination, 'destinationExists' => (bool) $destinationNode, 'destinationNode' => $destinationNode, diff --git a/sabre/dav/lib/DAV/Sharing/Plugin.php b/sabre/dav/lib/DAV/Sharing/Plugin.php index d766ae0de..ea79f4436 100644 --- a/sabre/dav/lib/DAV/Sharing/Plugin.php +++ b/sabre/dav/lib/DAV/Sharing/Plugin.php @@ -84,7 +84,7 @@ public function initialize(Server $server) { $this->server = $server; - $server->xml->elementMap['{DAV:}share-resource'] = 'Sabre\\DAV\\Xml\\Request\\ShareResource'; + $server->xml->elementMap['{DAV:}share-resource'] = \Sabre\DAV\Xml\Request\ShareResource::class; array_push( $server->protectedProperties, diff --git a/sabre/dav/lib/DAV/Sync/Plugin.php b/sabre/dav/lib/DAV/Sync/Plugin.php index 8609f759e..adce62b80 100644 --- a/sabre/dav/lib/DAV/Sync/Plugin.php +++ b/sabre/dav/lib/DAV/Sync/Plugin.php @@ -52,7 +52,7 @@ public function getPluginName() public function initialize(DAV\Server $server) { $this->server = $server; - $server->xml->elementMap['{DAV:}sync-collection'] = 'Sabre\\DAV\\Xml\\Request\\SyncCollectionReport'; + $server->xml->elementMap['{DAV:}sync-collection'] = \Sabre\DAV\Xml\Request\SyncCollectionReport::class; $self = $this; diff --git a/sabre/dav/lib/DAV/Tree.php b/sabre/dav/lib/DAV/Tree.php index f8a996b5a..46ddc6bb0 100644 --- a/sabre/dav/lib/DAV/Tree.php +++ b/sabre/dav/lib/DAV/Tree.php @@ -137,8 +137,11 @@ public function nodeExists($path) * * @param string $sourcePath The source location * @param string $destinationPath The full destination path + * @param int $depth How many levels of children to copy. + * The value can be 'infinity' (\Sabre\DAV\Server::DEPTH_INFINITY) or a positive integer, including zero. + * Zero means only copy the collection without children but with its properties. */ - public function copy($sourcePath, $destinationPath) + public function copy($sourcePath, $destinationPath, int $depth = Server::DEPTH_INFINITY) { $sourceNode = $this->getNodeForPath($sourcePath); @@ -147,8 +150,8 @@ public function copy($sourcePath, $destinationPath) $destinationParent = $this->getNodeForPath($destinationDir); // Check if the target can handle the copy itself. If not, we do it ourselves. - if (!$destinationParent instanceof ICopyTarget || !$destinationParent->copyInto($destinationName, $sourcePath, $sourceNode)) { - $this->copyNode($sourceNode, $destinationParent, $destinationName); + if (!$destinationParent instanceof ICopyTarget || !$destinationParent->copyInto($destinationName, $sourcePath, $sourceNode, $depth)) { + $this->copyNode($sourceNode, $destinationParent, $destinationName, $depth); } $this->markDirty($destinationDir); @@ -178,7 +181,8 @@ public function move($sourcePath, $destinationPath) $moveSuccess = $newParentNode->moveInto($destinationName, $sourcePath, $sourceNode); } if (!$moveSuccess) { - $this->copy($sourcePath, $destinationPath); + // Move is a copy with depth = infinity and deleting the source afterwards + $this->copy($sourcePath, $destinationPath, Server::DEPTH_INFINITY); $this->getNodeForPath($sourcePath)->delete(); } } @@ -215,9 +219,13 @@ public function getChildren($path) $basePath .= '/'; } - foreach ($node->getChildren() as $child) { - $this->cache[$basePath.$child->getName()] = $child; - yield $child; + if ($node instanceof ICollection) { + foreach ($node->getChildren() as $child) { + $this->cache[$basePath.$child->getName()] = $child; + yield $child; + } + } else { + yield from []; } } @@ -303,8 +311,9 @@ public function getMultipleNodes($paths) * copyNode. * * @param string $destinationName + * @param int $depth How many children of the node to copy */ - protected function copyNode(INode $source, ICollection $destinationParent, $destinationName = null) + protected function copyNode(INode $source, ICollection $destinationParent, ?string $destinationName = null, int $depth = Server::DEPTH_INFINITY) { if ('' === (string) $destinationName) { $destinationName = $source->getName(); @@ -326,10 +335,16 @@ protected function copyNode(INode $source, ICollection $destinationParent, $dest $destination = $destinationParent->getChild($destinationName); } elseif ($source instanceof ICollection) { $destinationParent->createDirectory($destinationName); - $destination = $destinationParent->getChild($destinationName); - foreach ($source->getChildren() as $child) { - $this->copyNode($child, $destination); + + // Copy children if depth is not zero + if (0 !== $depth) { + // Adjust next depth for children (keep 'infinity' or decrease) + $depth = Server::DEPTH_INFINITY === $depth ? Server::DEPTH_INFINITY : $depth - 1; + $destination = $destinationParent->getChild($destinationName); + foreach ($source->getChildren() as $child) { + $this->copyNode($child, $destination, null, $depth); + } } } if ($source instanceof IProperties && $destination instanceof IProperties) { diff --git a/sabre/dav/lib/DAV/Version.php b/sabre/dav/lib/DAV/Version.php index e6aee097c..3171aa18b 100644 --- a/sabre/dav/lib/DAV/Version.php +++ b/sabre/dav/lib/DAV/Version.php @@ -16,5 +16,5 @@ class Version /** * Full version number. */ - public const VERSION = '4.7.0'; + public const VERSION = '4.7.1'; } diff --git a/sabre/dav/lib/DAV/Xml/Element/Prop.php b/sabre/dav/lib/DAV/Xml/Element/Prop.php index 9c214792f..0ea47d669 100644 --- a/sabre/dav/lib/DAV/Xml/Element/Prop.php +++ b/sabre/dav/lib/DAV/Xml/Element/Prop.php @@ -85,7 +85,7 @@ private static function parseCurrentElement(Reader $reader) if (array_key_exists($name, $reader->elementMap)) { $deserializer = $reader->elementMap[$name]; - if (is_subclass_of($deserializer, 'Sabre\\Xml\\XmlDeserializable')) { + if (is_subclass_of($deserializer, \Sabre\Xml\XmlDeserializable::class)) { $value = call_user_func([$deserializer, 'xmlDeserialize'], $reader); } elseif (is_callable($deserializer)) { $value = call_user_func($deserializer, $reader); diff --git a/sabre/dav/lib/DAV/Xml/Element/Response.php b/sabre/dav/lib/DAV/Xml/Element/Response.php index df9291465..53467fd2c 100644 --- a/sabre/dav/lib/DAV/Xml/Element/Response.php +++ b/sabre/dav/lib/DAV/Xml/Element/Response.php @@ -184,7 +184,7 @@ public static function xmlDeserialize(Reader $reader) { $reader->pushContext(); - $reader->elementMap['{DAV:}propstat'] = 'Sabre\\Xml\\Element\\KeyValue'; + $reader->elementMap['{DAV:}propstat'] = \Sabre\Xml\Element\KeyValue::class; // We are overriding the parser for {DAV:}prop. This deserializer is // almost identical to the one for Sabre\Xml\Element\KeyValue. diff --git a/sabre/dav/lib/DAV/Xml/Element/Sharee.php b/sabre/dav/lib/DAV/Xml/Element/Sharee.php index 33564d8f2..e42358748 100644 --- a/sabre/dav/lib/DAV/Xml/Element/Sharee.php +++ b/sabre/dav/lib/DAV/Xml/Element/Sharee.php @@ -159,7 +159,7 @@ public static function xmlDeserialize(Reader $reader) { // Temporarily override configuration $reader->pushContext(); - $reader->elementMap['{DAV:}share-access'] = 'Sabre\DAV\Xml\Property\ShareAccess'; + $reader->elementMap['{DAV:}share-access'] = \Sabre\DAV\Xml\Property\ShareAccess::class; $reader->elementMap['{DAV:}prop'] = 'Sabre\Xml\Deserializer\keyValue'; $elems = Deserializer\keyValue($reader, 'DAV:'); diff --git a/sabre/dav/lib/DAV/Xml/Request/Lock.php b/sabre/dav/lib/DAV/Xml/Request/Lock.php index 57d12ef93..70fa46d63 100644 --- a/sabre/dav/lib/DAV/Xml/Request/Lock.php +++ b/sabre/dav/lib/DAV/Xml/Request/Lock.php @@ -61,7 +61,7 @@ class Lock implements XmlDeserializable public static function xmlDeserialize(Reader $reader) { $reader->pushContext(); - $reader->elementMap['{DAV:}owner'] = 'Sabre\\Xml\\Element\\XmlFragment'; + $reader->elementMap['{DAV:}owner'] = \Sabre\Xml\Element\XmlFragment::class; $values = KeyValue::xmlDeserialize($reader); diff --git a/sabre/dav/lib/DAV/Xml/Request/MkCol.php b/sabre/dav/lib/DAV/Xml/Request/MkCol.php index e0d7e90a2..6504aebc0 100644 --- a/sabre/dav/lib/DAV/Xml/Request/MkCol.php +++ b/sabre/dav/lib/DAV/Xml/Request/MkCol.php @@ -63,9 +63,9 @@ public static function xmlDeserialize(Reader $reader) $self = new self(); $elementMap = $reader->elementMap; - $elementMap['{DAV:}prop'] = 'Sabre\DAV\Xml\Element\Prop'; - $elementMap['{DAV:}set'] = 'Sabre\Xml\Element\KeyValue'; - $elementMap['{DAV:}remove'] = 'Sabre\Xml\Element\KeyValue'; + $elementMap['{DAV:}prop'] = \Sabre\DAV\Xml\Element\Prop::class; + $elementMap['{DAV:}set'] = \Sabre\Xml\Element\KeyValue::class; + $elementMap['{DAV:}remove'] = \Sabre\Xml\Element\KeyValue::class; $elems = $reader->parseInnerTree($elementMap); diff --git a/sabre/dav/lib/DAV/Xml/Request/PropFind.php b/sabre/dav/lib/DAV/Xml/Request/PropFind.php index 505e7c79c..e01dd62f5 100644 --- a/sabre/dav/lib/DAV/Xml/Request/PropFind.php +++ b/sabre/dav/lib/DAV/Xml/Request/PropFind.php @@ -60,7 +60,7 @@ public static function xmlDeserialize(Reader $reader) $self = new self(); $reader->pushContext(); - $reader->elementMap['{DAV:}prop'] = 'Sabre\Xml\Element\Elements'; + $reader->elementMap['{DAV:}prop'] = \Sabre\Xml\Element\Elements::class; foreach (KeyValue::xmlDeserialize($reader) as $k => $v) { switch ($k) { diff --git a/sabre/dav/lib/DAV/Xml/Request/PropPatch.php b/sabre/dav/lib/DAV/Xml/Request/PropPatch.php index 4a2709502..44c4f3fec 100644 --- a/sabre/dav/lib/DAV/Xml/Request/PropPatch.php +++ b/sabre/dav/lib/DAV/Xml/Request/PropPatch.php @@ -86,9 +86,9 @@ public static function xmlDeserialize(Reader $reader) $self = new self(); $elementMap = $reader->elementMap; - $elementMap['{DAV:}prop'] = 'Sabre\DAV\Xml\Element\Prop'; - $elementMap['{DAV:}set'] = 'Sabre\Xml\Element\KeyValue'; - $elementMap['{DAV:}remove'] = 'Sabre\Xml\Element\KeyValue'; + $elementMap['{DAV:}prop'] = \Sabre\DAV\Xml\Element\Prop::class; + $elementMap['{DAV:}set'] = \Sabre\Xml\Element\KeyValue::class; + $elementMap['{DAV:}remove'] = \Sabre\Xml\Element\KeyValue::class; $elems = $reader->parseInnerTree($elementMap); diff --git a/sabre/dav/lib/DAV/Xml/Request/ShareResource.php b/sabre/dav/lib/DAV/Xml/Request/ShareResource.php index 79d7dc826..20614a536 100644 --- a/sabre/dav/lib/DAV/Xml/Request/ShareResource.php +++ b/sabre/dav/lib/DAV/Xml/Request/ShareResource.php @@ -61,8 +61,8 @@ public function __construct(array $sharees) public static function xmlDeserialize(Reader $reader) { $elems = $reader->parseInnerTree([ - '{DAV:}sharee' => 'Sabre\DAV\Xml\Element\Sharee', - '{DAV:}share-access' => 'Sabre\DAV\Xml\Property\ShareAccess', + '{DAV:}sharee' => \Sabre\DAV\Xml\Element\Sharee::class, + '{DAV:}share-access' => \Sabre\DAV\Xml\Property\ShareAccess::class, '{DAV:}prop' => 'Sabre\Xml\Deserializer\keyValue', ]); diff --git a/sabre/dav/lib/DAV/Xml/Request/SyncCollectionReport.php b/sabre/dav/lib/DAV/Xml/Request/SyncCollectionReport.php index 8dd95765b..4db06b9ef 100644 --- a/sabre/dav/lib/DAV/Xml/Request/SyncCollectionReport.php +++ b/sabre/dav/lib/DAV/Xml/Request/SyncCollectionReport.php @@ -76,7 +76,7 @@ public static function xmlDeserialize(Reader $reader) $reader->pushContext(); - $reader->elementMap['{DAV:}prop'] = 'Sabre\Xml\Element\Elements'; + $reader->elementMap['{DAV:}prop'] = \Sabre\Xml\Element\Elements::class; $elems = KeyValue::xmlDeserialize($reader); $reader->popContext(); diff --git a/sabre/dav/lib/DAV/Xml/Response/MultiStatus.php b/sabre/dav/lib/DAV/Xml/Response/MultiStatus.php index e824cda42..bbb454324 100644 --- a/sabre/dav/lib/DAV/Xml/Response/MultiStatus.php +++ b/sabre/dav/lib/DAV/Xml/Response/MultiStatus.php @@ -114,7 +114,7 @@ public function xmlSerialize(Writer $writer) public static function xmlDeserialize(Reader $reader) { $elementMap = $reader->elementMap; - $elementMap['{DAV:}prop'] = 'Sabre\\DAV\\Xml\\Element\\Prop'; + $elementMap['{DAV:}prop'] = \Sabre\DAV\Xml\Element\Prop::class; $elements = $reader->parseInnerTree($elementMap); $responses = []; diff --git a/sabre/dav/lib/DAV/Xml/Service.php b/sabre/dav/lib/DAV/Xml/Service.php index 4406b022d..33ff7f9a2 100644 --- a/sabre/dav/lib/DAV/Xml/Service.php +++ b/sabre/dav/lib/DAV/Xml/Service.php @@ -20,16 +20,16 @@ class Service extends \Sabre\Xml\Service * be mapped to Sabre\DAV\Xml\Request\PropFind */ public $elementMap = [ - '{DAV:}multistatus' => 'Sabre\\DAV\\Xml\\Response\\MultiStatus', - '{DAV:}response' => 'Sabre\\DAV\\Xml\\Element\\Response', + '{DAV:}multistatus' => \Sabre\DAV\Xml\Response\MultiStatus::class, + '{DAV:}response' => \Sabre\DAV\Xml\Element\Response::class, // Requests - '{DAV:}propfind' => 'Sabre\\DAV\\Xml\\Request\\PropFind', - '{DAV:}propertyupdate' => 'Sabre\\DAV\\Xml\\Request\\PropPatch', - '{DAV:}mkcol' => 'Sabre\\DAV\\Xml\\Request\\MkCol', + '{DAV:}propfind' => \Sabre\DAV\Xml\Request\PropFind::class, + '{DAV:}propertyupdate' => \Sabre\DAV\Xml\Request\PropPatch::class, + '{DAV:}mkcol' => \Sabre\DAV\Xml\Request\MkCol::class, // Properties - '{DAV:}resourcetype' => 'Sabre\\DAV\\Xml\\Property\\ResourceType', + '{DAV:}resourcetype' => \Sabre\DAV\Xml\Property\ResourceType::class, ]; /** diff --git a/sabre/dav/lib/DAVACL/Plugin.php b/sabre/dav/lib/DAVACL/Plugin.php index f0497844d..03068451e 100644 --- a/sabre/dav/lib/DAVACL/Plugin.php +++ b/sabre/dav/lib/DAVACL/Plugin.php @@ -808,17 +808,17 @@ public function initialize(DAV\Server $server) // Automatically mapping nodes implementing IPrincipal to the // {DAV:}principal resourcetype. - $server->resourceTypeMapping['Sabre\\DAVACL\\IPrincipal'] = '{DAV:}principal'; + $server->resourceTypeMapping[\Sabre\DAVACL\IPrincipal::class] = '{DAV:}principal'; // Mapping the group-member-set property to the HrefList property // class. - $server->xml->elementMap['{DAV:}group-member-set'] = 'Sabre\\DAV\\Xml\\Property\\Href'; - $server->xml->elementMap['{DAV:}acl'] = 'Sabre\\DAVACL\\Xml\\Property\\Acl'; - $server->xml->elementMap['{DAV:}acl-principal-prop-set'] = 'Sabre\\DAVACL\\Xml\\Request\\AclPrincipalPropSetReport'; - $server->xml->elementMap['{DAV:}expand-property'] = 'Sabre\\DAVACL\\Xml\\Request\\ExpandPropertyReport'; - $server->xml->elementMap['{DAV:}principal-property-search'] = 'Sabre\\DAVACL\\Xml\\Request\\PrincipalPropertySearchReport'; - $server->xml->elementMap['{DAV:}principal-search-property-set'] = 'Sabre\\DAVACL\\Xml\\Request\\PrincipalSearchPropertySetReport'; - $server->xml->elementMap['{DAV:}principal-match'] = 'Sabre\\DAVACL\\Xml\\Request\\PrincipalMatchReport'; + $server->xml->elementMap['{DAV:}group-member-set'] = \Sabre\DAV\Xml\Property\Href::class; + $server->xml->elementMap['{DAV:}acl'] = \Sabre\DAVACL\Xml\Property\Acl::class; + $server->xml->elementMap['{DAV:}acl-principal-prop-set'] = \Sabre\DAVACL\Xml\Request\AclPrincipalPropSetReport::class; + $server->xml->elementMap['{DAV:}expand-property'] = \Sabre\DAVACL\Xml\Request\ExpandPropertyReport::class; + $server->xml->elementMap['{DAV:}principal-property-search'] = \Sabre\DAVACL\Xml\Request\PrincipalPropertySearchReport::class; + $server->xml->elementMap['{DAV:}principal-search-property-set'] = \Sabre\DAVACL\Xml\Request\PrincipalSearchPropertySetReport::class; + $server->xml->elementMap['{DAV:}principal-match'] = \Sabre\DAVACL\Xml\Request\PrincipalMatchReport::class; } /* {{{ Event handlers */ diff --git a/sabre/dav/lib/DAVACL/Xml/Property/Acl.php b/sabre/dav/lib/DAVACL/Xml/Property/Acl.php index c6e236dc5..be645bf42 100644 --- a/sabre/dav/lib/DAVACL/Xml/Property/Acl.php +++ b/sabre/dav/lib/DAVACL/Xml/Property/Acl.php @@ -161,9 +161,9 @@ public function toHtml(HtmlOutputHelper $html) public static function xmlDeserialize(Reader $reader) { $elementMap = [ - '{DAV:}ace' => 'Sabre\Xml\Element\KeyValue', - '{DAV:}privilege' => 'Sabre\Xml\Element\Elements', - '{DAV:}principal' => 'Sabre\DAVACL\Xml\Property\Principal', + '{DAV:}ace' => \Sabre\Xml\Element\KeyValue::class, + '{DAV:}privilege' => \Sabre\Xml\Element\Elements::class, + '{DAV:}principal' => \Sabre\DAVACL\Xml\Property\Principal::class, ]; $privileges = []; diff --git a/sabre/dav/lib/DAVACL/Xml/Property/CurrentUserPrivilegeSet.php b/sabre/dav/lib/DAVACL/Xml/Property/CurrentUserPrivilegeSet.php index e38a45c61..28ef54f2e 100644 --- a/sabre/dav/lib/DAVACL/Xml/Property/CurrentUserPrivilegeSet.php +++ b/sabre/dav/lib/DAVACL/Xml/Property/CurrentUserPrivilegeSet.php @@ -111,7 +111,7 @@ public static function xmlDeserialize(Reader $reader) { $result = []; - $tree = $reader->parseInnerTree(['{DAV:}privilege' => 'Sabre\\Xml\\Element\\Elements']); + $tree = $reader->parseInnerTree(['{DAV:}privilege' => \Sabre\Xml\Element\Elements::class]); foreach ($tree as $element) { if ('{DAV:}privilege' !== $element['name']) { continue; diff --git a/sabre/dav/lib/DAVACL/Xml/Request/PrincipalPropertySearchReport.php b/sabre/dav/lib/DAVACL/Xml/Request/PrincipalPropertySearchReport.php index bddceca8d..1283be102 100644 --- a/sabre/dav/lib/DAVACL/Xml/Request/PrincipalPropertySearchReport.php +++ b/sabre/dav/lib/DAVACL/Xml/Request/PrincipalPropertySearchReport.php @@ -86,8 +86,8 @@ public static function xmlDeserialize(Reader $reader) } $elemMap = [ - '{DAV:}property-search' => 'Sabre\\Xml\\Element\\KeyValue', - '{DAV:}prop' => 'Sabre\\Xml\\Element\\KeyValue', + '{DAV:}property-search' => \Sabre\Xml\Element\KeyValue::class, + '{DAV:}prop' => \Sabre\Xml\Element\KeyValue::class, ]; foreach ($reader->parseInnerTree($elemMap) as $elem) { diff --git a/sabre/event/lib/Promise.php b/sabre/event/lib/Promise.php index 66903fb9f..0e13752e3 100644 --- a/sabre/event/lib/Promise.php +++ b/sabre/event/lib/Promise.php @@ -184,11 +184,15 @@ public function wait() if (self::FULFILLED === $this->state) { // If the state of this promise is fulfilled, we can return the value. return $this->value; - } else { - // If we got here, it means that the asynchronous operation - // errored. Therefore we need to throw an exception. + } + // If we got here, it means that the asynchronous operation + // errored. Therefore, we need to throw an exception. + if ($this->value instanceof \Throwable) { throw $this->value; } + // The state should have been REJECTED, with "value" a Throwable + // But "value" was not a Throwable. So throw a more general exception. + throw new \LogicException('The Promise was not fulfilled but no exception was specified'); } /** diff --git a/sabre/event/lib/Promise/functions.php b/sabre/event/lib/Promise/functions.php index 67e80cbe4..49b1a2122 100644 --- a/sabre/event/lib/Promise/functions.php +++ b/sabre/event/lib/Promise/functions.php @@ -105,12 +105,11 @@ function resolve($value): Promise { if ($value instanceof Promise) { return $value->then(); - } else { - $promise = new Promise(); - $promise->fulfill($value); - - return $promise; } + $promise = new Promise(); + $promise->fulfill($value); + + return $promise; } /** diff --git a/sabre/event/lib/Version.php b/sabre/event/lib/Version.php index 10a98c607..f5fad3782 100644 --- a/sabre/event/lib/Version.php +++ b/sabre/event/lib/Version.php @@ -16,5 +16,5 @@ class Version /** * Full version number. */ - public const VERSION = '5.1.7'; + public const VERSION = '5.1.9'; } diff --git a/sabre/event/lib/WildcardEmitterTrait.php b/sabre/event/lib/WildcardEmitterTrait.php index 69243ff83..1de2ef416 100644 --- a/sabre/event/lib/WildcardEmitterTrait.php +++ b/sabre/event/lib/WildcardEmitterTrait.php @@ -126,11 +126,9 @@ public function listeners(string $eventName): array if (!\array_key_exists($eventName, $this->listenerIndex)) { // Create a new index. $listeners = []; - $listenersPriority = []; if (isset($this->listeners[$eventName])) { foreach ($this->listeners[$eventName] as $listener) { - $listenersPriority[] = $listener[0]; - $listeners[] = $listener[1]; + $listeners[] = $listener; } } @@ -138,17 +136,18 @@ public function listeners(string $eventName): array // Wildcard match if (\substr($eventName, 0, \strlen($wcEvent)) === $wcEvent) { foreach ($wcListeners as $listener) { - $listenersPriority[] = $listener[0]; - $listeners[] = $listener[1]; + $listeners[] = $listener; } } } // Sorting by priority - \array_multisort($listenersPriority, SORT_NUMERIC, $listeners); + \usort($listeners, static function ($l1, $l2) { + return $l1[0] <=> $l2[0]; + }); // Creating index - $this->listenerIndex[$eventName] = $listeners; + $this->listenerIndex[$eventName] = \array_column($listeners, 1); } return $this->listenerIndex[$eventName]; diff --git a/sabre/event/lib/coroutine.php b/sabre/event/lib/coroutine.php index f664efa78..adf55bb35 100644 --- a/sabre/event/lib/coroutine.php +++ b/sabre/event/lib/coroutine.php @@ -43,7 +43,7 @@ * * @psalm-template TReturn * - * @psalm-param callable():\Generator $gen + * @psalm-param callable():Generator $gen * * @psalm-return Promise * @@ -87,10 +87,9 @@ function (\Throwable $reason) use ($generator, $advanceGenerator) { // We need to break out of the loop, because $advanceGenerator // will be called asynchronously when the promise has a result. break; - } else { - // If the value was not a promise, we'll just let it pass through. - $generator->send($yieldedValue); } + // If the value was not a promise, we'll just let it pass through. + $generator->send($yieldedValue); } // If the generator is at the end, and we didn't run into an exception, diff --git a/sabre/http/lib/Request.php b/sabre/http/lib/Request.php index 99a13d25a..c0ab31cd4 100644 --- a/sabre/http/lib/Request.php +++ b/sabre/http/lib/Request.php @@ -110,10 +110,17 @@ public function setAbsoluteUrl(string $url) public function getAbsoluteUrl(): string { if (!$this->absoluteUrl) { - // Guessing we're a http endpoint. - $this->absoluteUrl = 'http://'. - ($this->getHeader('Host') ?? 'localhost'). - $this->getUrl(); + $url = $this->getUrl(); + if (parse_url($url, PHP_URL_SCHEME)) { + // It's already an absolute URL + $this->absoluteUrl = $url; + } else { + $host = $this->getHeader('Host') + ?? parse_url($url, PHP_URL_HOST) + ?? 'localhost'; + // Guessing we're a http endpoint. + $this->absoluteUrl = "http://$host$url"; + } } return $this->absoluteUrl; diff --git a/sabre/http/lib/Version.php b/sabre/http/lib/Version.php index 4ac82f6d7..84c877bc8 100644 --- a/sabre/http/lib/Version.php +++ b/sabre/http/lib/Version.php @@ -16,5 +16,5 @@ class Version /** * Full version number. */ - public const VERSION = '5.1.12'; + public const VERSION = '5.1.13'; } diff --git a/sabre/vobject/PATCHES.txt b/sabre/vobject/PATCHES.txt deleted file mode 100644 index 358e87a0c..000000000 --- a/sabre/vobject/PATCHES.txt +++ /dev/null @@ -1,11 +0,0 @@ -This file was automatically generated by Composer Patches (https://github.com/cweagans/composer-patches) -Patches applied to this directory: - -fix use RDATE in time range check and use all instances -Source: .patches/sabre-vobject-rdate.patch - - -fix send participation reply on fresh event -Source: .patches/sabre-vobject-iTipBroker-replies.patch - - diff --git a/sabre/vobject/lib/Component/VCard.php b/sabre/vobject/lib/Component/VCard.php index 82fab82bb..e2b440812 100644 --- a/sabre/vobject/lib/Component/VCard.php +++ b/sabre/vobject/lib/Component/VCard.php @@ -430,6 +430,34 @@ public function getByType($propertyName, $type) } } + /** + * Returns a property with a specific TYPE value (ADR, TEL, or EMAIL). + * + * This function will return null if the exact property list does not exist. + * + * For example to get the property of `TEL;TYPE=HOME,CELL` + * you would call `getByTypes('TEL', ['HOME', 'CELL'])` + * + * @param string[] $types + * + * @return \ArrayAccess|array|null + */ + public function getByTypes(string $propertyName, array $types) + { + $types = array_map('strtolower', $types); + foreach ($this->select($propertyName) as $field) { + if (isset($field['TYPE'])) { + $parts = array_map('strtolower', $field['TYPE']->getParts()); + + if (!array_diff($types, $parts) && !array_diff($parts, $types)) { + return $field; + } + } + } + + return null; + } + /** * This method should return a list of default property values. * diff --git a/sabre/vobject/lib/Document.php b/sabre/vobject/lib/Document.php index d2131f479..36f20ddd0 100644 --- a/sabre/vobject/lib/Document.php +++ b/sabre/vobject/lib/Document.php @@ -200,23 +200,30 @@ public function createProperty($name, $value = null, ?array $parameters = null, $class = null; + // If a VALUE parameter is supplied, we have to use that + // According to https://datatracker.ietf.org/doc/html/rfc5545#section-3.2.20 + // If the property's value is the default value type, then this + // parameter need not be specified. However, if the property's + // default value type is overridden by some other allowable value + // type, then this parameter MUST be specified. + if (!$valueType) { + $valueType = $parameters['VALUE'] ?? null; + } + if ($valueType) { // The valueType argument comes first to figure out the correct // class. $class = $this->getClassNameForPropertyValue($valueType); } + // If the value parameter is not set or set to something we do not recognize + // we do not attempt to interpret or parse the datass value as specified in + // https://datatracker.ietf.org/doc/html/rfc5545#section-3.2.20 + // So when we so far did not get a class-name, we use the default for the property if (is_null($class)) { - // If a VALUE parameter is supplied, we should use that. - if (isset($parameters['VALUE'])) { - $class = $this->getClassNameForPropertyValue($parameters['VALUE']); - if (is_null($class)) { - throw new InvalidDataException('Unsupported VALUE parameter for '.$name.' property. You supplied "'.$parameters['VALUE'].'"'); - } - } else { - $class = $this->getClassNameForPropertyName($name); - } + $class = $this->getClassNameForPropertyName($name); } + if (is_null($parameters)) { $parameters = []; } diff --git a/sabre/vobject/lib/ElementList.php b/sabre/vobject/lib/ElementList.php index 860512649..e419d480c 100644 --- a/sabre/vobject/lib/ElementList.php +++ b/sabre/vobject/lib/ElementList.php @@ -24,6 +24,8 @@ class ElementList extends ArrayIterator * * @param int $offset * @param mixed $value + * + * @return void */ #[\ReturnTypeWillChange] public function offsetSet($offset, $value) @@ -37,6 +39,8 @@ public function offsetSet($offset, $value) * This method just forwards the request to the inner iterator * * @param int $offset + * + * @return void */ #[\ReturnTypeWillChange] public function offsetUnset($offset) diff --git a/sabre/vobject/lib/ITip/Broker.php b/sabre/vobject/lib/ITip/Broker.php index 80be2a852..e89b7a4e6 100644 --- a/sabre/vobject/lib/ITip/Broker.php +++ b/sabre/vobject/lib/ITip/Broker.php @@ -499,7 +499,6 @@ protected function parseEventForOrganizer(VCalendar $calendar, array $eventInfo, } $messages = []; - foreach ($attendees as $attendee) { // An organizer can also be an attendee. We should not generate any // messages for those. @@ -599,6 +598,9 @@ protected function parseEventForOrganizer(VCalendar $calendar, array $eventInfo, )); } else { $currentEvent->EXDATE = $exceptions; + if ($currentEvent->DTSTART['TZID']) { + $currentEvent->EXDATE['TZID'] = clone $currentEvent->DTSTART['TZID']; + } } } @@ -607,14 +609,14 @@ protected function parseEventForOrganizer(VCalendar $calendar, array $eventInfo, unset($currentEvent->ORGANIZER['SCHEDULE-FORCE-SEND']); unset($currentEvent->ORGANIZER['SCHEDULE-STATUS']); - foreach ($currentEvent->ATTENDEE as $attendee) { - unset($attendee['SCHEDULE-FORCE-SEND']); - unset($attendee['SCHEDULE-STATUS']); + foreach ($currentEvent->ATTENDEE as $currentEventAttendee) { + unset($currentEventAttendee['SCHEDULE-FORCE-SEND']); + unset($currentEventAttendee['SCHEDULE-STATUS']); // We're adding PARTSTAT=NEEDS-ACTION to ensure that // iOS shows an "Inbox Item" - if (!isset($attendee['PARTSTAT'])) { - $attendee['PARTSTAT'] = 'NEEDS-ACTION'; + if (!isset($currentEventAttendee['PARTSTAT'])) { + $currentEventAttendee['PARTSTAT'] = 'NEEDS-ACTION'; } } } @@ -683,7 +685,7 @@ protected function parseEventForAttendee(VCalendar $calendar, array $eventInfo, // We only need to do that though, if the master event is not declined. if (isset($instances['master']) && 'DECLINED' !== $instances['master']['newstatus']) { foreach ($eventInfo['exdate'] as $exDate) { - if (!in_array($exDate, $oldEventInfo['exdate'])) { + if (!in_array($exDate, $oldEventInfo['exdate'] ?? [])) { if (isset($instances[$exDate])) { $instances[$exDate]['newstatus'] = 'DECLINED'; } else { @@ -913,6 +915,9 @@ protected function parseEventInfo(?VCalendar $calendar = null) $timezone = $vevent->{'RECURRENCE-ID'}->getDateTime()->getTimeZone(); } } + + $instances[$recurId] = $vevent; + if (isset($vevent->ATTENDEE)) { foreach ($vevent->ATTENDEE as $attendee) { if ($this->scheduleAgentServerRules && @@ -951,7 +956,6 @@ protected function parseEventInfo(?VCalendar $calendar = null) ]; } } - $instances[$recurId] = $vevent; } foreach ($this->significantChangeProperties as $prop) { diff --git a/sabre/vobject/lib/Node.php b/sabre/vobject/lib/Node.php index 852ffced0..d7cd9caab 100644 --- a/sabre/vobject/lib/Node.php +++ b/sabre/vobject/lib/Node.php @@ -212,6 +212,8 @@ public function offsetGet($offset) * * @param int $offset * @param mixed $value + * + * @return void */ #[\ReturnTypeWillChange] public function offsetSet($offset, $value) @@ -233,6 +235,8 @@ public function offsetSet($offset, $value) * This method just forwards the request to the inner iterator * * @param int $offset + * + * @return void */ #[\ReturnTypeWillChange] public function offsetUnset($offset) diff --git a/sabre/vobject/lib/Parser/MimeDir.php b/sabre/vobject/lib/Parser/MimeDir.php index d484d6a39..5520530d0 100644 --- a/sabre/vobject/lib/Parser/MimeDir.php +++ b/sabre/vobject/lib/Parser/MimeDir.php @@ -25,6 +25,11 @@ */ class MimeDir extends Parser { + public const TOKEN_PROPNAME = 1; + public const TOKEN_PROPVALUE = 2; + public const TOKEN_PARAMNAME = 3; + public const TOKEN_PARAMVALUE = 4; + /** * The input stream. * @@ -362,6 +367,12 @@ protected function readProperty($line) 'value' => null, ]; + /* + * Keep track on the last token we parsed in order to do + * better error checking + */ + $lastToken = null; + $lastParam = null; /* @@ -387,10 +398,16 @@ protected function readProperty($line) // option is set to ignore invalid lines, we ignore this line // This can happen when servers provide faulty data as iCloud // frequently does with X-APPLE-STRUCTURED-LOCATION + $lastToken = self::TOKEN_PARAMVALUE; continue; } throw new ParseException('Invalid Mimedir file. Line starting at '.$this->startLine.' did not follow iCalendar/vCard conventions'); } + + if ('=' == $match[0][0] && self::TOKEN_PARAMNAME != $lastToken) { + throw new ParseException('Invalid Mimedir file. Line starting at '.$this->startLine.': Missing parameter name for parameter value "'.$match['paramValue'].'"'); + } + if (is_null($property['parameters'][$lastParam])) { $property['parameters'][$lastParam] = $value; } elseif (is_array($property['parameters'][$lastParam])) { @@ -404,6 +421,7 @@ protected function readProperty($line) $value, ]; } + $lastToken = self::TOKEN_PARAMVALUE; continue; } if (isset($match['paramName'])) { @@ -411,14 +429,17 @@ protected function readProperty($line) if (!isset($property['parameters'][$lastParam])) { $property['parameters'][$lastParam] = null; } + $lastToken = self::TOKEN_PARAMNAME; continue; } if (isset($match['propValue'])) { $property['value'] = $match['propValue']; + $lastToken = self::TOKEN_PROPVALUE; continue; } - if (isset($match['name']) && $match['name']) { + if (isset($match['name']) && 0 < strlen($match['name'])) { $property['name'] = strtoupper($match['name']); + $lastToken = self::TOKEN_PROPNAME; continue; } @@ -430,7 +451,7 @@ protected function readProperty($line) if (is_null($property['value'])) { $property['value'] = ''; } - if (!$property['name']) { + if (!isset($property['name']) || 0 == strlen($property['name'])) { if ($this->options & self::OPTION_IGNORE_INVALID_LINES) { return false; } diff --git a/sabre/vobject/lib/Property.php b/sabre/vobject/lib/Property.php index f52760f9c..7cf591486 100644 --- a/sabre/vobject/lib/Property.php +++ b/sabre/vobject/lib/Property.php @@ -457,6 +457,8 @@ public function offsetGet($name) * * @param string $name * @param mixed $value + * + * @return void */ #[\ReturnTypeWillChange] public function offsetSet($name, $value) @@ -478,6 +480,8 @@ public function offsetSet($name, $value) * Removes one or more parameters with the specified name. * * @param string $name + * + * @return void */ #[\ReturnTypeWillChange] public function offsetUnset($name) diff --git a/sabre/vobject/lib/Property/ICalendar/DateTime.php b/sabre/vobject/lib/Property/ICalendar/DateTime.php index 3ea21e2ec..12f8cd951 100644 --- a/sabre/vobject/lib/Property/ICalendar/DateTime.php +++ b/sabre/vobject/lib/Property/ICalendar/DateTime.php @@ -299,6 +299,8 @@ function ($item) { * * @param string $name * @param mixed $value + * + * @return void */ #[\ReturnTypeWillChange] public function offsetSet($name, $value) diff --git a/sabre/vobject/lib/Property/ICalendar/Period.php b/sabre/vobject/lib/Property/ICalendar/Period.php index ae8a78911..d9adc39cc 100644 --- a/sabre/vobject/lib/Property/ICalendar/Period.php +++ b/sabre/vobject/lib/Property/ICalendar/Period.php @@ -79,6 +79,11 @@ function ($item) { parent::setJsonValue($value); } + public function appendUtc(string $strDate) + { + return !str_ends_with($strDate, 'Z') ? '' : 'Z'; + } + /** * Returns the value, in the format it should be encoded for json. * @@ -92,19 +97,19 @@ public function getJsonValue() foreach ($this->getParts() as $item) { list($start, $end) = explode('/', $item, 2); - $start = DateTimeParser::parseDateTime($start); + $startDt = DateTimeParser::parseDateTime($start)->format('Y-m-d\\TH:i:s').$this->appendUtc($start); // This is a duration value. if ('P' === $end[0]) { $return[] = [ - $start->format('Y-m-d\\TH:i:s'), + $startDt, $end, ]; } else { - $end = DateTimeParser::parseDateTime($end); + $endDt = DateTimeParser::parseDateTime($end)->format('Y-m-d\\TH:i:s').$this->appendUtc($end); $return[] = [ - $start->format('Y-m-d\\TH:i:s'), - $end->format('Y-m-d\\TH:i:s'), + $startDt, + $endDt, ]; } } diff --git a/sabre/vobject/lib/TimezoneGuesser/FindFromTimezoneMap.php b/sabre/vobject/lib/TimezoneGuesser/FindFromTimezoneMap.php index b52ba6a19..799283e01 100644 --- a/sabre/vobject/lib/TimezoneGuesser/FindFromTimezoneMap.php +++ b/sabre/vobject/lib/TimezoneGuesser/FindFromTimezoneMap.php @@ -18,11 +18,18 @@ class FindFromTimezoneMap implements TimezoneFinder '/^\((UTC|GMT)(\+|\-)[\d]{2}\.[\d]{2}\) (.*)/', ]; + /** + * @throws void + */ public function find(string $tzid, bool $failIfUncertain = false): ?DateTimeZone { // Next, we check if the tzid is somewhere in our tzid map. if ($this->hasTzInMap($tzid)) { - return new DateTimeZone($this->getTzFromMap($tzid)); + try { + return new DateTimeZone($this->getTzFromMap($tzid)); + } catch (\Exception $e) { + return null; + } } // Some Microsoft products prefix the offset first, so let's strip that off @@ -34,7 +41,11 @@ public function find(string $tzid, bool $failIfUncertain = false): ?DateTimeZone } $tzidAlternate = $matches[3]; if ($this->hasTzInMap($tzidAlternate)) { - return new DateTimeZone($this->getTzFromMap($tzidAlternate)); + try { + return new DateTimeZone($this->getTzFromMap($tzidAlternate)); + } catch (\Exception $e) { + return null; + } } } diff --git a/sabre/vobject/lib/Version.php b/sabre/vobject/lib/Version.php index 060c69a30..5bee64427 100644 --- a/sabre/vobject/lib/Version.php +++ b/sabre/vobject/lib/Version.php @@ -14,5 +14,5 @@ class Version /** * Full version number. */ - public const VERSION = '4.5.6'; + public const VERSION = '4.6.1'; } diff --git a/sabre/vobject/lib/timezonedata/exchangezones.php b/sabre/vobject/lib/timezonedata/exchangezones.php index 89bddc27c..f42944b3b 100644 --- a/sabre/vobject/lib/timezonedata/exchangezones.php +++ b/sabre/vobject/lib/timezonedata/exchangezones.php @@ -17,6 +17,7 @@ 'Greenwich Mean Time: Dublin, Edinburgh, Lisbon, London' => 'Europe/Lisbon', 'Greenwich Mean Time; Dublin, Edinburgh, London' => 'Europe/London', 'Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna' => 'Europe/Berlin', + 'Amsterdam, Berlin, Bern, Rom, Stockholm, Wien' => 'Europe/Berlin', 'Belgrade, Pozsony, Budapest, Ljubljana, Prague' => 'Europe/Prague', 'Brussels, Copenhagen, Madrid, Paris' => 'Europe/Paris', 'Paris, Madrid, Brussels, Copenhagen' => 'Europe/Paris', @@ -39,12 +40,12 @@ 'Kabul' => 'Asia/Kabul', 'Ekaterinburg' => 'Asia/Yekaterinburg', 'Islamabad, Karachi, Tashkent' => 'Asia/Karachi', - 'Kolkata, Chennai, Mumbai, New Delhi, India Standard Time' => 'Asia/Calcutta', + 'Kolkata, Chennai, Mumbai, New Delhi, India Standard Time' => 'Asia/Kolkata', 'Kathmandu, Nepal' => 'Asia/Kathmandu', 'Almaty, Novosibirsk, North Central Asia' => 'Asia/Almaty', 'Astana, Dhaka' => 'Asia/Dhaka', 'Sri Jayawardenepura, Sri Lanka' => 'Asia/Colombo', - 'Rangoon' => 'Asia/Rangoon', + 'Rangoon' => 'Asia/Yangon', 'Bangkok, Hanoi, Jakarta' => 'Asia/Bangkok', 'Krasnoyarsk' => 'Asia/Krasnoyarsk', 'Beijing, Chongqing, Hong Kong SAR, Urumqi' => 'Asia/Shanghai', @@ -71,7 +72,7 @@ 'Mid-Atlantic' => 'America/Noronha', 'Brasilia' => 'America/Sao_Paulo', // Best guess 'Buenos Aires' => 'America/Argentina/Buenos_Aires', - 'Greenland' => 'America/Godthab', + 'Greenland' => 'America/Nuuk', 'Newfoundland' => 'America/St_Johns', 'Atlantic Time (Canada)' => 'America/Halifax', 'Caracas, La Paz' => 'America/Caracas', diff --git a/sabre/vobject/lib/timezonedata/lotuszones.php b/sabre/vobject/lib/timezonedata/lotuszones.php index 4b50808f9..08c37afdd 100644 --- a/sabre/vobject/lib/timezonedata/lotuszones.php +++ b/sabre/vobject/lib/timezonedata/lotuszones.php @@ -34,7 +34,7 @@ 'Newfoundland' => 'America/St_Johns', 'Argentina' => 'America/Argentina/Buenos_Aires', 'E. South America' => 'America/Belem', - 'Greenland' => 'America/Godthab', + 'Greenland' => 'America/Nuuk', 'Montevideo' => 'America/Montevideo', 'SA Eastern' => 'America/Belem', // 'Mid-Atlantic' => 'Etc/GMT-2', // conflict with windows timezones. @@ -71,12 +71,12 @@ 'Ekaterinburg' => 'Asia/Yekaterinburg', 'Pakistan' => 'Asia/Karachi', 'West Asia' => 'Asia/Tashkent', - 'India' => 'Asia/Calcutta', + 'India' => 'Asia/Kolkata', 'Sri Lanka' => 'Asia/Colombo', 'Nepal' => 'Asia/Kathmandu', 'Central Asia' => 'Asia/Dhaka', 'N. Central Asia' => 'Asia/Almaty', - 'Myanmar' => 'Asia/Rangoon', + 'Myanmar' => 'Asia/Yangon', 'North Asia' => 'Asia/Krasnoyarsk', 'SE Asia' => 'Asia/Bangkok', 'China' => 'Asia/Shanghai', diff --git a/sabre/vobject/lib/timezonedata/windowszones.php b/sabre/vobject/lib/timezonedata/windowszones.php index 2049a95c1..7ff61b52d 100644 --- a/sabre/vobject/lib/timezonedata/windowszones.php +++ b/sabre/vobject/lib/timezonedata/windowszones.php @@ -20,7 +20,7 @@ 'Arab Standard Time' => 'Asia/Riyadh', 'Arabian Standard Time' => 'Asia/Dubai', 'Arabic Standard Time' => 'Asia/Baghdad', - 'Argentina Standard Time' => 'America/Buenos_Aires', + 'Argentina Standard Time' => 'America/Argentina/Buenos_Aires', 'Astrakhan Standard Time' => 'Europe/Astrakhan', 'Atlantic Standard Time' => 'America/Halifax', 'Aus Central W. Standard Time' => 'Australia/Eucla', @@ -55,16 +55,16 @@ 'Eastern Standard Time (Mexico)' => 'America/Cancun', 'Egypt Standard Time' => 'Africa/Cairo', 'Ekaterinburg Standard Time' => 'Asia/Yekaterinburg', - 'FLE Standard Time' => 'Europe/Kiev', + 'FLE Standard Time' => 'Europe/Kyiv', 'Fiji Standard Time' => 'Pacific/Fiji', 'GMT Standard Time' => 'Europe/London', 'GTB Standard Time' => 'Europe/Bucharest', 'Georgian Standard Time' => 'Asia/Tbilisi', - 'Greenland Standard Time' => 'America/Godthab', + 'Greenland Standard Time' => 'America/Nuuk', 'Greenwich Standard Time' => 'Atlantic/Reykjavik', 'Haiti Standard Time' => 'America/Port-au-Prince', 'Hawaiian Standard Time' => 'Pacific/Honolulu', - 'India Standard Time' => 'Asia/Calcutta', + 'India Standard Time' => 'Asia/Kolkata', 'Iran Standard Time' => 'Asia/Tehran', 'Israel Standard Time' => 'Asia/Jerusalem', 'Jordan Standard Time' => 'Asia/Amman', @@ -82,10 +82,10 @@ 'Morocco Standard Time' => 'Africa/Casablanca', 'Mountain Standard Time' => 'America/Denver', 'Mountain Standard Time (Mexico)' => 'America/Chihuahua', - 'Myanmar Standard Time' => 'Asia/Rangoon', + 'Myanmar Standard Time' => 'Asia/Yangon', 'N. Central Asia Standard Time' => 'Asia/Novosibirsk', 'Namibia Standard Time' => 'Africa/Windhoek', - 'Nepal Standard Time' => 'Asia/Katmandu', + 'Nepal Standard Time' => 'Asia/Kathmandu', 'New Zealand Standard Time' => 'Pacific/Auckland', 'Newfoundland Standard Time' => 'America/St_Johns', 'Norfolk Standard Time' => 'Pacific/Norfolk', @@ -127,7 +127,7 @@ 'Transbaikal Standard Time' => 'Asia/Chita', 'Turkey Standard Time' => 'Europe/Istanbul', 'Turks And Caicos Standard Time' => 'America/Grand_Turk', - 'US Eastern Standard Time' => 'America/Indianapolis', + 'US Eastern Standard Time' => 'America/Indiana/Indianapolis', 'US Mountain Standard Time' => 'America/Phoenix', 'UTC' => 'Etc/GMT', 'UTC+12' => 'Etc/GMT-12',