From 48c7b69a1eab80b87f4f1b58570a81eec7808c92 Mon Sep 17 00:00:00 2001 From: Evert Pot Date: Mon, 24 Oct 2016 01:06:21 -0400 Subject: [PATCH 1/2] Catching another infinite loop condition. Fixes #329. Thanks for the research @PHPGangsta --- CHANGELOG.md | 2 + lib/Recur/RRuleIterator.php | 20 ++++++++-- .../EventIterator/InfiniteLoopProblemTest.php | 39 +++++++++++++++++++ 3 files changed, 58 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 80ea0bb78..116188c41 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,8 @@ ChangeLog * #345: Auto-detecting more Outlook 365-generated timezone identifiers. (@jpirkey) * #348: `FreeBusyGenerator` can now accept streams. +* #329: Infinite loop when using `BYMONTHDAY`, `BYDAY` and/or `BYSETPOS` to + expand a `BYMONTH` rule. 4.1.1 (2016-07-15) diff --git a/lib/Recur/RRuleIterator.php b/lib/Recur/RRuleIterator.php index 20f34ef42..5149e7e45 100644 --- a/lib/Recur/RRuleIterator.php +++ b/lib/Recur/RRuleIterator.php @@ -620,12 +620,14 @@ protected function nextYearly() { } - $currentMonth = $this->currentDate->format('n'); - $currentYear = $this->currentDate->format('Y'); - $currentDayOfMonth = $this->currentDate->format('j'); + //$currentMonth = $this->currentDate->format('n'); + //$currentYear = $this->currentDate->format('Y'); + //$currentDayOfMonth = $this->currentDate->format('j'); $advancedToNewMonth = false; + $noOccurrenceCounter = 0; + // If we got a byDay or getMonthDay filter, we must first expand // further. if ($this->byDay || $this->byMonthDay) { @@ -633,6 +635,18 @@ protected function nextYearly() { while (true) { $occurrences = $this->getMonthlyOccurrences(); + if(!$occurrences) { + // We are counting subsequent months where there weren't + // occurrences. If we exceed the treshold we might be + // dealing with a recurrence rule that doesn't generate + // valid instances. + $noOccurrenceCounter++; + if (++$noOccurrenceCounter > 10) { + throw new InvalidDataException('BYDAY, BYMONTHDAY and/or BYSETPOS rules don\'t generate valid recurrence instances'); + } + } else { + $noOccurrenceCounter = 0; + } foreach ($occurrences as $occurrence) { diff --git a/tests/VObject/Recur/EventIterator/InfiniteLoopProblemTest.php b/tests/VObject/Recur/EventIterator/InfiniteLoopProblemTest.php index 491b0e879..759808d68 100644 --- a/tests/VObject/Recur/EventIterator/InfiniteLoopProblemTest.php +++ b/tests/VObject/Recur/EventIterator/InfiniteLoopProblemTest.php @@ -6,6 +6,7 @@ use DateTimeZone; use Sabre\VObject\Component\VCalendar; use Sabre\VObject\Recur; +use Sabre\VObject\Reader; class InfiniteLoopProblemTest extends \PHPUnit_Framework_TestCase { @@ -95,4 +96,42 @@ function testZeroInterval() { } + /** + * Another infinite loop, from Issue #329. + * + * This was triggered due to a BYMONTHDAY rule that was using a value that + * never occurred in the BYMONTH rule. + * + * This bug surfaced similar issues with BYDAY and BYSETPOS. + * + * @expectedException \Sabre\VObject\InvalidDataException + */ + function testBadByMonthday() { + + $input = Reader::read(<<expand( + new DateTimeImmutable('2015-01-01'), + new DateTimeImmutable('2016-01-01') + ); + + } + } From ac13086b01cc62cd184affaa83376c175016dcb5 Mon Sep 17 00:00:00 2001 From: Evert Pot Date: Mon, 24 Oct 2016 01:10:46 -0400 Subject: [PATCH 2/2] CS --- lib/Recur/RRuleIterator.php | 2 +- tests/VObject/Recur/EventIterator/InfiniteLoopProblemTest.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/Recur/RRuleIterator.php b/lib/Recur/RRuleIterator.php index 5149e7e45..efa300a9c 100644 --- a/lib/Recur/RRuleIterator.php +++ b/lib/Recur/RRuleIterator.php @@ -635,7 +635,7 @@ protected function nextYearly() { while (true) { $occurrences = $this->getMonthlyOccurrences(); - if(!$occurrences) { + if (!$occurrences) { // We are counting subsequent months where there weren't // occurrences. If we exceed the treshold we might be // dealing with a recurrence rule that doesn't generate diff --git a/tests/VObject/Recur/EventIterator/InfiniteLoopProblemTest.php b/tests/VObject/Recur/EventIterator/InfiniteLoopProblemTest.php index 759808d68..425fd68a5 100644 --- a/tests/VObject/Recur/EventIterator/InfiniteLoopProblemTest.php +++ b/tests/VObject/Recur/EventIterator/InfiniteLoopProblemTest.php @@ -5,8 +5,8 @@ use DateTimeImmutable; use DateTimeZone; use Sabre\VObject\Component\VCalendar; -use Sabre\VObject\Recur; use Sabre\VObject\Reader; +use Sabre\VObject\Recur; class InfiniteLoopProblemTest extends \PHPUnit_Framework_TestCase {