From 342cd96994f6e017c96b7046d5d2e04a6b18f8af Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 18 Jul 2026 01:07:11 +0000 Subject: [PATCH 1/2] build(deps): bump sabre/dav from 4.7.0 to 4.7.1 Bumps [sabre/dav](https://github.com/sabre-io/dav) from 4.7.0 to 4.7.1. - [Release notes](https://github.com/sabre-io/dav/releases) - [Changelog](https://github.com/sabre-io/dav/blob/master/CHANGELOG.md) - [Commits](https://github.com/sabre-io/dav/compare/4.7.0...4.7.1) --- updated-dependencies: - dependency-name: sabre/dav dependency-version: 4.7.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- composer.json | 2 +- composer.lock | 48 ++++++++++++++++++++++++------------------------ 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/composer.json b/composer.json index fbe885302..06063e14c 100644 --- a/composer.json +++ b/composer.json @@ -51,7 +51,7 @@ "psr/event-dispatcher": "^1.0", "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.41", "symfony/event-dispatcher": "^6.4.37", diff --git a/composer.lock b/composer.lock index f0dfafb40..e5d48ac5f 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": "8eb4f186d00c0474308f4cce0fe59422", + "content-hash": "06689907920869765e305970899d596e", "packages": [ { "name": "aws/aws-crt-php", @@ -3501,16 +3501,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": { @@ -3580,27 +3580,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" }, @@ -3646,20 +3646,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": { @@ -3671,7 +3671,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" }, @@ -3709,7 +3709,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", @@ -3773,16 +3773,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": { @@ -3792,7 +3792,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" }, @@ -3873,7 +3873,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", From ff61822150ccd189a407b1d76309ac900baffa7b Mon Sep 17 00:00:00 2001 From: Carl Schwan Date: Wed, 22 Jul 2026 10:03:14 +0200 Subject: [PATCH 2/2] chore: Update vendored files Signed-off-by: Carl Schwan --- ...nstanceof-INode-instead-of-Node-1595.patch | 26 ------- .../sabre-vobject-iTipBroker-replies.patch | 51 -------------- .patches/sabre-vobject-rdate.patch | 43 ------------ composer.patches.json | 7 -- composer/installed.json | 68 +++++++++---------- composer/installed.php | 24 +++---- sabre/dav/PATCHES.txt | 7 -- sabre/dav/lib/CalDAV/Backend/PDO.php | 60 ++++++++++------ sabre/dav/lib/CalDAV/Notifications/Plugin.php | 2 +- sabre/dav/lib/CalDAV/Plugin.php | 24 +++---- sabre/dav/lib/CalDAV/Schedule/Plugin.php | 4 +- sabre/dav/lib/CalDAV/SharingPlugin.php | 4 +- sabre/dav/lib/CalDAV/Subscriptions/Plugin.php | 4 +- .../Xml/Request/CalendarMultiGetReport.php | 4 +- .../Xml/Request/CalendarQueryReport.php | 12 ++-- .../dav/lib/CalDAV/Xml/Request/MkCalendar.php | 4 +- sabre/dav/lib/CalDAV/Xml/Request/Share.php | 4 +- sabre/dav/lib/CardDAV/Backend/PDO.php | 41 +++++------ sabre/dav/lib/CardDAV/Plugin.php | 10 +-- .../Xml/Request/AddressBookMultiGetReport.php | 4 +- .../Xml/Request/AddressBookQueryReport.php | 8 +-- sabre/dav/lib/DAV/Browser/Plugin.php | 4 +- sabre/dav/lib/DAV/CorePlugin.php | 13 ++-- sabre/dav/lib/DAV/FSExt/Directory.php | 4 -- sabre/dav/lib/DAV/FSExt/File.php | 4 +- sabre/dav/lib/DAV/ICopyTarget.php | 5 +- sabre/dav/lib/DAV/Locks/Plugin.php | 3 +- sabre/dav/lib/DAV/Server.php | 28 ++++++-- sabre/dav/lib/DAV/Sharing/Plugin.php | 2 +- sabre/dav/lib/DAV/Sync/Plugin.php | 2 +- sabre/dav/lib/DAV/Tree.php | 37 +++++++--- sabre/dav/lib/DAV/Version.php | 2 +- sabre/dav/lib/DAV/Xml/Element/Prop.php | 2 +- sabre/dav/lib/DAV/Xml/Element/Response.php | 2 +- sabre/dav/lib/DAV/Xml/Element/Sharee.php | 2 +- sabre/dav/lib/DAV/Xml/Request/Lock.php | 2 +- sabre/dav/lib/DAV/Xml/Request/MkCol.php | 6 +- sabre/dav/lib/DAV/Xml/Request/PropFind.php | 2 +- sabre/dav/lib/DAV/Xml/Request/PropPatch.php | 6 +- .../dav/lib/DAV/Xml/Request/ShareResource.php | 4 +- .../DAV/Xml/Request/SyncCollectionReport.php | 2 +- .../dav/lib/DAV/Xml/Response/MultiStatus.php | 2 +- sabre/dav/lib/DAV/Xml/Service.php | 12 ++-- sabre/dav/lib/DAVACL/Plugin.php | 16 ++--- sabre/dav/lib/DAVACL/Xml/Property/Acl.php | 6 +- .../Xml/Property/CurrentUserPrivilegeSet.php | 2 +- .../Request/PrincipalPropertySearchReport.php | 4 +- sabre/event/lib/Promise.php | 10 ++- sabre/event/lib/Promise/functions.php | 9 ++- sabre/event/lib/Version.php | 2 +- sabre/event/lib/WildcardEmitterTrait.php | 13 ++-- sabre/event/lib/coroutine.php | 7 +- sabre/http/lib/Request.php | 15 ++-- sabre/http/lib/Version.php | 2 +- sabre/vobject/PATCHES.txt | 11 --- sabre/vobject/lib/Component/VCard.php | 28 ++++++++ sabre/vobject/lib/Document.php | 25 ++++--- sabre/vobject/lib/ElementList.php | 4 ++ sabre/vobject/lib/ITip/Broker.php | 20 +++--- sabre/vobject/lib/Node.php | 4 ++ sabre/vobject/lib/Parser/MimeDir.php | 25 ++++++- sabre/vobject/lib/Property.php | 4 ++ .../lib/Property/ICalendar/DateTime.php | 2 + .../vobject/lib/Property/ICalendar/Period.php | 15 ++-- .../TimezoneGuesser/FindFromTimezoneMap.php | 15 +++- sabre/vobject/lib/Version.php | 2 +- .../lib/timezonedata/exchangezones.php | 7 +- sabre/vobject/lib/timezonedata/lotuszones.php | 6 +- .../vobject/lib/timezonedata/windowszones.php | 14 ++-- 69 files changed, 414 insertions(+), 411 deletions(-) delete mode 100644 .patches/check-for-instanceof-INode-instead-of-Node-1595.patch delete mode 100644 .patches/sabre-vobject-iTipBroker-replies.patch delete mode 100644 .patches/sabre-vobject-rdate.patch delete mode 100644 sabre/dav/PATCHES.txt delete mode 100644 sabre/vobject/PATCHES.txt 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.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/installed.json b/composer/installed.json index 99e05d935..843985968 100644 --- a/composer/installed.json +++ b/composer/installed.json @@ -3657,17 +3657,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": { @@ -3701,17 +3701,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": { @@ -3748,28 +3743,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": { @@ -3817,17 +3812,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": { @@ -3839,14 +3834,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": { @@ -3946,17 +3941,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": { @@ -3966,14 +3961,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" @@ -3982,10 +3977,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", @@ -4085,6 +4076,11 @@ }, "time": "2024-09-06T07:37:46+00:00", "type": "library", + "extra": { + "patches_applied": { + "Remove callable serialization": ".patches/sabre-xml-remove-callable-serialization.patch" + } + }, "installation-source": "dist", "autoload": { "files": [ diff --git a/composer/installed.php b/composer/installed.php index ba5b801df..13212b5b4 100644 --- a/composer/installed.php +++ b/composer/installed.php @@ -572,27 +572,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(), @@ -608,9 +608,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',