diff options
Diffstat (limited to 'vendor/sabre/vobject/lib')
-rw-r--r-- | vendor/sabre/vobject/lib/Component/VCalendar.php | 2 | ||||
-rw-r--r-- | vendor/sabre/vobject/lib/DateTimeParser.php | 4 | ||||
-rw-r--r-- | vendor/sabre/vobject/lib/Document.php | 8 | ||||
-rw-r--r-- | vendor/sabre/vobject/lib/FreeBusyGenerator.php | 4 | ||||
-rw-r--r-- | vendor/sabre/vobject/lib/ITip/Broker.php | 32 | ||||
-rw-r--r-- | vendor/sabre/vobject/lib/Parser/MimeDir.php | 8 | ||||
-rw-r--r-- | vendor/sabre/vobject/lib/Property.php | 24 | ||||
-rw-r--r-- | vendor/sabre/vobject/lib/Property/ICalendar/DateTime.php | 4 | ||||
-rw-r--r-- | vendor/sabre/vobject/lib/Property/ICalendar/Recur.php | 10 | ||||
-rw-r--r-- | vendor/sabre/vobject/lib/Recur/EventIterator.php | 2 | ||||
-rw-r--r-- | vendor/sabre/vobject/lib/Recur/RRuleIterator.php | 89 | ||||
-rw-r--r-- | vendor/sabre/vobject/lib/TimeZoneUtil.php | 4 | ||||
-rw-r--r-- | vendor/sabre/vobject/lib/Version.php | 2 |
13 files changed, 152 insertions, 41 deletions
diff --git a/vendor/sabre/vobject/lib/Component/VCalendar.php b/vendor/sabre/vobject/lib/Component/VCalendar.php index 4db318135..017aed70c 100644 --- a/vendor/sabre/vobject/lib/Component/VCalendar.php +++ b/vendor/sabre/vobject/lib/Component/VCalendar.php @@ -281,7 +281,7 @@ class VCalendar extends VObject\Document * * @return VCalendar */ - public function expand(DateTimeInterface $start, DateTimeInterface $end, DateTimeZone $timeZone = null) + public function expand(DateTimeInterface $start, DateTimeInterface $end, ?DateTimeZone $timeZone = null) { $newChildren = []; $recurringEvents = []; diff --git a/vendor/sabre/vobject/lib/DateTimeParser.php b/vendor/sabre/vobject/lib/DateTimeParser.php index c5dbac97d..69072ef8c 100644 --- a/vendor/sabre/vobject/lib/DateTimeParser.php +++ b/vendor/sabre/vobject/lib/DateTimeParser.php @@ -31,7 +31,7 @@ class DateTimeParser * * @return DateTimeImmutable */ - public static function parseDateTime($dt, DateTimeZone $tz = null) + public static function parseDateTime($dt, ?DateTimeZone $tz = null) { // Format is YYYYMMDD + "T" + hhmmss $result = preg_match('/^([0-9]{4})([0-1][0-9])([0-3][0-9])T([0-2][0-9])([0-5][0-9])([0-5][0-9])([Z]?)$/', $dt, $matches); @@ -61,7 +61,7 @@ class DateTimeParser * * @return DateTimeImmutable */ - public static function parseDate($date, DateTimeZone $tz = null) + public static function parseDate($date, ?DateTimeZone $tz = null) { // Format is YYYYMMDD $result = preg_match('/^([0-9]{4})([0-1][0-9])([0-3][0-9])$/', $date, $matches); diff --git a/vendor/sabre/vobject/lib/Document.php b/vendor/sabre/vobject/lib/Document.php index 14a77c911..d2131f479 100644 --- a/vendor/sabre/vobject/lib/Document.php +++ b/vendor/sabre/vobject/lib/Document.php @@ -157,7 +157,7 @@ abstract class Document extends Component * * @return Component */ - public function createComponent($name, array $children = null, $defaults = true) + public function createComponent($name, ?array $children = null, $defaults = true) { $name = strtoupper($name); $class = Component::class; @@ -186,10 +186,8 @@ abstract class Document extends Component * @param mixed $value * @param array $parameters * @param string $valueType Force a specific valuetype, such as URI or TEXT - * - * @return Property */ - public function createProperty($name, $value = null, array $parameters = null, $valueType = null) + public function createProperty($name, $value = null, ?array $parameters = null, $valueType = null, ?int $lineIndex = null, ?string $lineString = null): Property { // If there's a . in the name, it means it's prefixed by a groupname. if (false !== ($i = strpos($name, '.'))) { @@ -223,7 +221,7 @@ abstract class Document extends Component $parameters = []; } - return new $class($this, $name, $value, $parameters, $group); + return new $class($this, $name, $value, $parameters, $group, $lineIndex, $lineString); } /** diff --git a/vendor/sabre/vobject/lib/FreeBusyGenerator.php b/vendor/sabre/vobject/lib/FreeBusyGenerator.php index 81b8126d5..56ae166fa 100644 --- a/vendor/sabre/vobject/lib/FreeBusyGenerator.php +++ b/vendor/sabre/vobject/lib/FreeBusyGenerator.php @@ -89,7 +89,7 @@ class FreeBusyGenerator * @param mixed $objects * @param DateTimeZone $timeZone */ - public function __construct(DateTimeInterface $start = null, DateTimeInterface $end = null, $objects = null, DateTimeZone $timeZone = null) + public function __construct(?DateTimeInterface $start = null, ?DateTimeInterface $end = null, $objects = null, ?DateTimeZone $timeZone = null) { $this->setTimeRange($start, $end); @@ -158,7 +158,7 @@ class FreeBusyGenerator * @param DateTimeInterface $start * @param DateTimeInterface $end */ - public function setTimeRange(DateTimeInterface $start = null, DateTimeInterface $end = null) + public function setTimeRange(?DateTimeInterface $start = null, ?DateTimeInterface $end = null) { if (!$start) { $start = new DateTimeImmutable(Settings::$minDate); diff --git a/vendor/sabre/vobject/lib/ITip/Broker.php b/vendor/sabre/vobject/lib/ITip/Broker.php index 80d9a5b31..9d68fc4c6 100644 --- a/vendor/sabre/vobject/lib/ITip/Broker.php +++ b/vendor/sabre/vobject/lib/ITip/Broker.php @@ -108,7 +108,7 @@ class Broker * * @return VCalendar|null */ - public function processMessage(Message $itipMessage, VCalendar $existingObject = null) + public function processMessage(Message $itipMessage, ?VCalendar $existingObject = null) { // We only support events at the moment. if ('VEVENT' !== $itipMessage->component) { @@ -266,7 +266,7 @@ class Broker * * @return VCalendar|null */ - protected function processMessageRequest(Message $itipMessage, VCalendar $existingObject = null) + protected function processMessageRequest(Message $itipMessage, ?VCalendar $existingObject = null) { if (!$existingObject) { // This is a new invite, and we're just going to copy over @@ -301,7 +301,7 @@ class Broker * * @return VCalendar|null */ - protected function processMessageCancel(Message $itipMessage, VCalendar $existingObject = null) + protected function processMessageCancel(Message $itipMessage, ?VCalendar $existingObject = null) { if (!$existingObject) { // The event didn't exist in the first place, so we're just @@ -326,7 +326,7 @@ class Broker * * @return VCalendar|null */ - protected function processMessageReply(Message $itipMessage, VCalendar $existingObject = null) + protected function processMessageReply(Message $itipMessage, ?VCalendar $existingObject = null) { // A reply can only be processed based on an existing object. // If the object is not available, the reply is ignored. @@ -338,7 +338,10 @@ class Broker // Finding all the instances the attendee replied to. foreach ($itipMessage->message->VEVENT as $vevent) { - $recurId = isset($vevent->{'RECURRENCE-ID'}) ? $vevent->{'RECURRENCE-ID'}->getValue() : 'master'; + // Use the Unix timestamp returned by getTimestamp as a unique identifier for the recurrence. + // The Unix timestamp will be the same for an event, even if the reply from the attendee + // used a different format/timezone to express the event date-time. + $recurId = isset($vevent->{'RECURRENCE-ID'}) ? $vevent->{'RECURRENCE-ID'}->getDateTime()->getTimestamp() : 'master'; $attendee = $vevent->ATTENDEE; $instances[$recurId] = $attendee['PARTSTAT']->getValue(); if (isset($vevent->{'REQUEST-STATUS'})) { @@ -351,7 +354,8 @@ class Broker // all the instances where we have a reply for. $masterObject = null; foreach ($existingObject->VEVENT as $vevent) { - $recurId = isset($vevent->{'RECURRENCE-ID'}) ? $vevent->{'RECURRENCE-ID'}->getValue() : 'master'; + // Use the Unix timestamp returned by getTimestamp as a unique identifier for the recurrence. + $recurId = isset($vevent->{'RECURRENCE-ID'}) ? $vevent->{'RECURRENCE-ID'}->getDateTime()->getTimestamp() : 'master'; if ('master' === $recurId) { $masterObject = $vevent; } @@ -398,7 +402,10 @@ class Broker $newObject = $recurrenceIterator->getEventObject(); $recurrenceIterator->next(); - if (isset($newObject->{'RECURRENCE-ID'}) && $newObject->{'RECURRENCE-ID'}->getValue() === $recurId) { + // Compare the Unix timestamp returned by getTimestamp with the previously calculated timestamp. + // If they are the same, then this is a matching recurrence, even though its date-time may have + // been expressed in a different format/timezone. + if (isset($newObject->{'RECURRENCE-ID'}) && $newObject->{'RECURRENCE-ID'}->getDateTime()->getTimestamp() === $recurId) { $found = true; } --$iterations; @@ -503,10 +510,11 @@ class Broker $icalMsg->add(clone $timezone); } - if (!$attendee['newInstances']) { - // If there are no instances the attendee is a part of, it - // means the attendee was removed and we need to send him a - // CANCEL. + if (!$attendee['newInstances'] || 'CANCELLED' === $eventInfo['status']) { + // If there are no instances the attendee is a part of, it means + // the attendee was removed and we need to send them a CANCEL message. + // Also If the meeting STATUS property was changed to CANCELLED + // we need to send the attendee a CANCEL message. $message->method = 'CANCEL'; $icalMsg->METHOD = $message->method; @@ -800,7 +808,7 @@ class Broker * * @return array */ - protected function parseEventInfo(VCalendar $calendar = null) + protected function parseEventInfo(?VCalendar $calendar = null) { $uid = null; $organizer = null; diff --git a/vendor/sabre/vobject/lib/Parser/MimeDir.php b/vendor/sabre/vobject/lib/Parser/MimeDir.php index 513f7bd0c..d484d6a39 100644 --- a/vendor/sabre/vobject/lib/Parser/MimeDir.php +++ b/vendor/sabre/vobject/lib/Parser/MimeDir.php @@ -83,6 +83,12 @@ class MimeDir extends Parser $this->setInput($input); } + if (!\is_resource($this->input)) { + // Null was passed as input, but there was no existing input buffer + // There is nothing to parse. + throw new ParseException('No input provided to parse'); + } + if (0 !== $options) { $this->options = $options; } @@ -447,7 +453,7 @@ class MimeDir extends Parser } } - $propObj = $this->root->createProperty($property['name'], null, $namedParameters); + $propObj = $this->root->createProperty($property['name'], null, $namedParameters, null, $this->startLine, $line); foreach ($namelessParameters as $namelessParameter) { $propObj->add(null, $namelessParameter); diff --git a/vendor/sabre/vobject/lib/Property.php b/vendor/sabre/vobject/lib/Property.php index f6550246a..f52760f9c 100644 --- a/vendor/sabre/vobject/lib/Property.php +++ b/vendor/sabre/vobject/lib/Property.php @@ -57,6 +57,20 @@ abstract class Property extends Node public $delimiter = ';'; /** + * The line number in the original iCalendar / vCard file + * that corresponds with the current node + * if the node was read from a file. + */ + public $lineIndex; + + /** + * The line string from the original iCalendar / vCard file + * that corresponds with the current node + * if the node was read from a file. + */ + public $lineString; + + /** * Creates the generic property. * * Parameters must be specified in key=>value syntax. @@ -67,7 +81,7 @@ abstract class Property extends Node * @param array $parameters List of parameters * @param string $group The vcard property group */ - public function __construct(Component $root, $name, $value = null, array $parameters = [], $group = null) + public function __construct(Component $root, $name, $value = null, array $parameters = [], $group = null, ?int $lineIndex = null, ?string $lineString = null) { $this->name = $name; $this->group = $group; @@ -81,6 +95,14 @@ abstract class Property extends Node if (!is_null($value)) { $this->setValue($value); } + + if (!is_null($lineIndex)) { + $this->lineIndex = $lineIndex; + } + + if (!is_null($lineString)) { + $this->lineString = $lineString; + } } /** diff --git a/vendor/sabre/vobject/lib/Property/ICalendar/DateTime.php b/vendor/sabre/vobject/lib/Property/ICalendar/DateTime.php index ca71633b9..3ea21e2ec 100644 --- a/vendor/sabre/vobject/lib/Property/ICalendar/DateTime.php +++ b/vendor/sabre/vobject/lib/Property/ICalendar/DateTime.php @@ -131,7 +131,7 @@ class DateTime extends Property * * @return \DateTimeImmutable */ - public function getDateTime(DateTimeZone $timeZone = null) + public function getDateTime(?DateTimeZone $timeZone = null) { $dt = $this->getDateTimes($timeZone); if (!$dt) { @@ -153,7 +153,7 @@ class DateTime extends Property * @return \DateTimeImmutable[] * @return \DateTime[] */ - public function getDateTimes(DateTimeZone $timeZone = null) + public function getDateTimes(?DateTimeZone $timeZone = null) { // Does the property have a TZID? $tzid = $this['TZID']; diff --git a/vendor/sabre/vobject/lib/Property/ICalendar/Recur.php b/vendor/sabre/vobject/lib/Property/ICalendar/Recur.php index 3d632fec1..cd3d7a5e4 100644 --- a/vendor/sabre/vobject/lib/Property/ICalendar/Recur.php +++ b/vendor/sabre/vobject/lib/Property/ICalendar/Recur.php @@ -2,6 +2,7 @@ namespace Sabre\VObject\Property\ICalendar; +use Sabre\VObject\InvalidDataException; use Sabre\VObject\Property; use Sabre\Xml; @@ -198,7 +199,14 @@ class Recur extends Property if (empty($part)) { continue; } - list($partName, $partValue) = explode('=', $part); + + $parts = explode('=', $part); + + if (2 !== count($parts)) { + throw new InvalidDataException('The supplied iCalendar RRULE part is incorrect: '.$part); + } + + list($partName, $partValue) = $parts; // The value itself had multiple values.. if (false !== strpos($partValue, ',')) { diff --git a/vendor/sabre/vobject/lib/Recur/EventIterator.php b/vendor/sabre/vobject/lib/Recur/EventIterator.php index 61f05d7de..55d6e4779 100644 --- a/vendor/sabre/vobject/lib/Recur/EventIterator.php +++ b/vendor/sabre/vobject/lib/Recur/EventIterator.php @@ -93,7 +93,7 @@ class EventIterator implements \Iterator * @param DateTimeZone $timeZone reference timezone for floating dates and * times */ - public function __construct($input, $uid = null, DateTimeZone $timeZone = null) + public function __construct($input, $uid = null, ?DateTimeZone $timeZone = null) { if (is_null($timeZone)) { $timeZone = new DateTimeZone('UTC'); diff --git a/vendor/sabre/vobject/lib/Recur/RRuleIterator.php b/vendor/sabre/vobject/lib/Recur/RRuleIterator.php index 4f0e9070d..ca53b63ee 100644 --- a/vendor/sabre/vobject/lib/Recur/RRuleIterator.php +++ b/vendor/sabre/vobject/lib/Recur/RRuleIterator.php @@ -173,6 +173,14 @@ class RRuleIterator implements Iterator protected $currentDate; /** + * The number of hours that the next occurrence of an event + * jumped forward, usually because summer time started and + * the requested time-of-day like 0230 did not exist on that + * day. And so the event was scheduled 1 hour later at 0330. + */ + protected $hourJump = 0; + + /** * Frequency is one of: secondly, minutely, hourly, daily, weekly, monthly, * yearly. * @@ -320,11 +328,64 @@ class RRuleIterator implements Iterator /* Functions that advance the iterator {{{ */ /** + * Gets the original start time of the RRULE. + * + * The value is formatted as a string with 24-hour:minute:second + */ + protected function startTime(): string + { + return $this->startDate->format('H:i:s'); + } + + /** + * Advances currentDate by the interval. + * The time is set from the original startDate. + * If the recurrence is on a day when summer time started, then the + * time on that day may have jumped forward, for example, from 0230 to 0330. + * Using the original time means that the next recurrence will be calculated + * based on the original start time and the day/week/month/year interval. + * So the start time of the next occurrence can correctly revert to 0230. + */ + protected function advanceTheDate(string $interval): void + { + $this->currentDate = $this->currentDate->modify($interval.' '.$this->startTime()); + } + + /** + * Does the processing for adjusting the time of multi-hourly events when summer time starts. + */ + protected function adjustForTimeJumpsOfHourlyEvent(DateTimeInterface $previousEventDateTime): void + { + if (0 === $this->hourJump) { + // Remember if the clock time jumped forward on the next occurrence. + // That happens if the next event time is on a day when summer time starts + // and the event time is in the non-existent hour of the day. + // For example, an event that normally starts at 02:30 will + // have to start at 03:30 on that day. + // If the interval is just 1 hour, then there is no "jumping back" to do. + // The events that day will happen, for example, at 0030 0130 0330 0430 0530... + if ($this->interval > 1) { + $expectedHourOfNextDate = ((int) $previousEventDateTime->format('G') + $this->interval) % 24; + $actualHourOfNextDate = (int) $this->currentDate->format('G'); + $this->hourJump = $actualHourOfNextDate - $expectedHourOfNextDate; + } + } else { + // The hour "jumped" for the previous occurrence, to avoid the non-existent time. + // currentDate got set ahead by (usually) 1 hour on that day. + // Adjust it back for this next occurrence. + $this->currentDate = $this->currentDate->sub(new \DateInterval('PT'.$this->hourJump.'H')); + $this->hourJump = 0; + } + } + + /** * Does the processing for advancing the iterator for hourly frequency. */ protected function nextHourly() { + $previousEventDateTime = clone $this->currentDate; $this->currentDate = $this->currentDate->modify('+'.$this->interval.' hours'); + $this->adjustForTimeJumpsOfHourlyEvent($previousEventDateTime); } /** @@ -333,7 +394,7 @@ class RRuleIterator implements Iterator protected function nextDaily() { if (!$this->byHour && !$this->byDay) { - $this->currentDate = $this->currentDate->modify('+'.$this->interval.' days'); + $this->advanceTheDate('+'.$this->interval.' days'); return; } @@ -392,7 +453,7 @@ class RRuleIterator implements Iterator protected function nextWeekly() { if (!$this->byHour && !$this->byDay) { - $this->currentDate = $this->currentDate->modify('+'.$this->interval.' weeks'); + $this->advanceTheDate('+'.$this->interval.' weeks'); return; } @@ -414,7 +475,7 @@ class RRuleIterator implements Iterator if ($this->byHour) { $this->currentDate = $this->currentDate->modify('+1 hours'); } else { - $this->currentDate = $this->currentDate->modify('+1 days'); + $this->advanceTheDate('+1 days'); } // Current day of the week @@ -449,13 +510,13 @@ class RRuleIterator implements Iterator // occur to the next month. We Must skip these invalid // entries. if ($currentDayOfMonth < 29) { - $this->currentDate = $this->currentDate->modify('+'.$this->interval.' months'); + $this->advanceTheDate('+'.$this->interval.' months'); } else { $increase = 0; do { ++$increase; $tempDate = clone $this->currentDate; - $tempDate = $tempDate->modify('+ '.($this->interval * $increase).' months'); + $tempDate = $tempDate->modify('+ '.($this->interval * $increase).' months '.$this->startTime()); } while ($tempDate->format('j') != $currentDayOfMonth); $this->currentDate = $tempDate; } @@ -506,11 +567,15 @@ class RRuleIterator implements Iterator } } + // Set the currentDate to the year and month that we are in, and the day of the month that we have selected. + // That day could be a day when summer time starts, and if the time of the event is, for example, 0230, + // then 0230 will not be a valid time on that day. So always apply the start time from the original startDate. + // The "modify" method will set the time forward to 0330, for example, if needed. $this->currentDate = $this->currentDate->setDate( (int) $this->currentDate->format('Y'), (int) $this->currentDate->format('n'), (int) $occurrence - ); + )->modify($this->startTime()); } /** @@ -627,7 +692,7 @@ class RRuleIterator implements Iterator } // The easiest form - $this->currentDate = $this->currentDate->modify('+'.$this->interval.' years'); + $this->advanceTheDate('+'.$this->interval.' years'); return; } @@ -651,7 +716,11 @@ class RRuleIterator implements Iterator // If we advanced to the next month or year, the first // occurrence is always correct. if ($occurrence > $currentDayOfMonth || $advancedToNewMonth) { - break 2; + // only consider byMonth matches, + // otherwise, we don't follow RRule correctly + if (in_array($currentMonth, $this->byMonth)) { + break 2; + } } } @@ -687,7 +756,7 @@ class RRuleIterator implements Iterator (int) $currentYear, (int) $currentMonth, (int) $occurrence - ); + )->modify($this->startTime()); return; } else { @@ -704,7 +773,7 @@ class RRuleIterator implements Iterator (int) $currentYear, (int) $currentMonth, (int) $currentDayOfMonth - ); + )->modify($this->startTime()); return; } diff --git a/vendor/sabre/vobject/lib/TimeZoneUtil.php b/vendor/sabre/vobject/lib/TimeZoneUtil.php index 6422c0930..0d77e71ed 100644 --- a/vendor/sabre/vobject/lib/TimeZoneUtil.php +++ b/vendor/sabre/vobject/lib/TimeZoneUtil.php @@ -75,7 +75,7 @@ class TimeZoneUtil * Alternatively, if $failIfUncertain is set to true, it will throw an * exception if we cannot accurately determine the timezone. */ - private function findTimeZone(string $tzid, Component $vcalendar = null, bool $failIfUncertain = false): DateTimeZone + private function findTimeZone(string $tzid, ?Component $vcalendar = null, bool $failIfUncertain = false): DateTimeZone { foreach ($this->timezoneFinders as $timezoneFinder) { $timezone = $timezoneFinder->find($tzid, $failIfUncertain); @@ -126,7 +126,7 @@ class TimeZoneUtil * * @return DateTimeZone */ - public static function getTimeZone($tzid, Component $vcalendar = null, $failIfUncertain = false) + public static function getTimeZone($tzid, ?Component $vcalendar = null, $failIfUncertain = false) { return self::getInstance()->findTimeZone($tzid, $vcalendar, $failIfUncertain); } diff --git a/vendor/sabre/vobject/lib/Version.php b/vendor/sabre/vobject/lib/Version.php index c7ca69e14..060c69a30 100644 --- a/vendor/sabre/vobject/lib/Version.php +++ b/vendor/sabre/vobject/lib/Version.php @@ -14,5 +14,5 @@ class Version /** * Full version number. */ - public const VERSION = '4.5.4'; + public const VERSION = '4.5.6'; } |