From 1ef785c8c63412a6f6e2e0caad2f60a8fe5af97c Mon Sep 17 00:00:00 2001 From: Thomas Bruederli Date: Fri, 28 Feb 2014 12:41:31 +0100 Subject: [PATCH] Handle multiple VCALENDAR blocks when reading ics files (#2884) --- plugins/libcalendaring/libvcalendar.php | 38 ++++++--- plugins/libcalendaring/tests/libvcalendar.php | 17 ++++ .../tests/resources/multiple-rdate.ics | 80 +++++++++++++++++++ 3 files changed, 123 insertions(+), 12 deletions(-) create mode 100644 plugins/libcalendaring/tests/resources/multiple-rdate.ics diff --git a/plugins/libcalendaring/libvcalendar.php b/plugins/libcalendaring/libvcalendar.php index 369f08a5..eff85c17 100644 --- a/plugins/libcalendaring/libvcalendar.php +++ b/plugins/libcalendaring/libvcalendar.php @@ -48,6 +48,7 @@ class libvcalendar implements Iterator private $iteratorkey = 0; private $charset; private $forward_exceptions; + private $vhead; private $fp; public $method; @@ -101,6 +102,7 @@ class libvcalendar implements Iterator */ public function reset() { + $this->vhead = ''; $this->method = ''; $this->objects = array(); $this->freebusy = array(); @@ -202,16 +204,7 @@ class libvcalendar implements Iterator return false; } - // read vcalendar header (with timezone defintion) - $this->vhead = ''; fseek($this->fp, 0); - while (($line = fgets($this->fp, 512)) !== false) { - if (preg_match('/BEGIN:(VEVENT|VTODO|VFREEBUSY)/i', $line)) - break; - $this->vhead .= $line; - } - fseek($this->fp, -strlen($line), SEEK_CUR); - return $this->_parse_next(); } @@ -269,10 +262,31 @@ class libvcalendar implements Iterator private function _next_component() { $buffer = ''; + $vcalendar_head = false; while (($line = fgets($this->fp, 1024)) !== false) { - $buffer .= $line; - if (preg_match('/END:(VEVENT|VTODO|VFREEBUSY)/i', $line)) { - break; + // ignore END:VCALENDAR lines + if (preg_match('/END:VCALENDAR/i', $line)) { + continue; + } + // read vcalendar header (with timezone defintion) + if (preg_match('/BEGIN:VCALENDAR/i', $line)) { + $this->vhead = ''; + $vcalendar_head = true; + } + + // end of VCALENDAR header part + if ($vcalendar_head && preg_match('/BEGIN:(VEVENT|VTODO|VFREEBUSY)/i', $line)) { + $vcalendar_head = false; + } + + if ($vcalendar_head) { + $this->vhead .= $line; + } + else { + $buffer .= $line; + if (preg_match('/END:(VEVENT|VTODO|VFREEBUSY)/i', $line)) { + break; + } } } diff --git a/plugins/libcalendaring/tests/libvcalendar.php b/plugins/libcalendaring/tests/libvcalendar.php index a74eaf39..91cb9e8f 100644 --- a/plugins/libcalendaring/tests/libvcalendar.php +++ b/plugins/libcalendaring/tests/libvcalendar.php @@ -74,6 +74,23 @@ class libvcalendar_test extends PHPUnit_Framework_TestCase $this->assertEmpty($events); } + /** + * Test parsing from files with multiple VCALENDAR blocks (#2884) + */ + function test_import_from_file_multiple() + { + $ical = new libvcalendar(); + $ical->fopen(__DIR__ . '/resources/multiple-rdate.ics', 'UTF-8'); + $events = array(); + foreach ($ical as $event) { + $events[] = $event; + } + + $this->assertEquals(2, count($events)); + $this->assertEquals("AAAA6A8C3CCE4EE2C1257B5C00FFFFFF-Lotus_Notes_Generated", $events[0]['uid']); + $this->assertEquals("AAAA1C572093EC3FC125799C004AFFFF-Lotus_Notes_Generated", $events[1]['uid']); + } + function test_invalid_dates() { $ical = new libvcalendar(); diff --git a/plugins/libcalendaring/tests/resources/multiple-rdate.ics b/plugins/libcalendaring/tests/resources/multiple-rdate.ics new file mode 100644 index 00000000..51b938d0 --- /dev/null +++ b/plugins/libcalendaring/tests/resources/multiple-rdate.ics @@ -0,0 +1,80 @@ +BEGIN:VCALENDAR +X-LOTUS-CHARSET:UTF-8 +VERSION:2.0 +PRODID:-//Lotus Development Corporation//NONSGML Notes 8.5.3//EN_C +METHOD:PUBLISH +BEGIN:VTIMEZONE +TZID:W. Europe +BEGIN:STANDARD +DTSTART:19501029T020000 +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +RRULE:FREQ=YEARLY;BYMINUTE=0;BYHOUR=2;BYDAY=-1SU;BYMONTH=10 +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19500326T020000 +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +RRULE:FREQ=YEARLY;BYMINUTE=0;BYHOUR=2;BYDAY=-1SU;BYMONTH=3 +END:DAYLIGHT +END:VTIMEZONE + +BEGIN:VEVENT +DTSTART;TZID="W. Europe":20140520T040000 +DTEND;TZID="W. Europe":20140520T200000 +TRANSP:TRANSPARENT +RDATE;VALUE=PERIOD:20140520T020000Z/20140520T180000Z + ,PERIOD:20150520T020000Z/20150520T180000Z + ,20160520T020000Z/20160520T180000Z,20170520T020000Z/20170520T180000Z + ,20180520T020000Z/20180520T180000Z,20190520T020000Z/20190520T180000Z + ,20200520T020000Z/20200520T180000Z,20210520T020000Z/20210520T180000Z + ,20220520T020000Z/20220520T180000Z +DTSTAMP:20140227T123549Z +CLASS:PUBLIC +SUMMARY:Feiertag - Pfingsmontag +UID:AAAA6A8C3CCE4EE2C1257B5C00FFFFFF-Lotus_Notes_Generated +X-LOTUS-PARITAL-REPEAT:TRUE +X-LOTUS-NOTESVERSION:2 +X-LOTUS-APPTTYPE:1 +END:VEVENT + +END:VCALENDAR + +BEGIN:VCALENDAR +X-LOTUS-CHARSET:UTF-8 +VERSION:2.0 +PRODID:-//Lotus Development Corporation//NONSGML Notes 8.5.3//EN_C +METHOD:PUBLISH +BEGIN:VTIMEZONE +TZID:W. Europe +BEGIN:STANDARD +DTSTART:19501029T020000 +TZOFFSETFROM:+0200 +TZOFFSETTO:+0100 +RRULE:FREQ=YEARLY;BYMINUTE=0;BYHOUR=2;BYDAY=-1SU;BYMONTH=10 +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:19500326T020000 +TZOFFSETFROM:+0100 +TZOFFSETTO:+0200 +RRULE:FREQ=YEARLY;BYMINUTE=0;BYHOUR=2;BYDAY=-1SU;BYMONTH=3 +END:DAYLIGHT +END:VTIMEZONE + +BEGIN:VEVENT +DTSTART;TZID="W. Europe":20120330T040000 +DTEND;TZID="W. Europe":20120330T200000 +TRANSP:TRANSPARENT +RDATE;VALUE=PERIOD:20120330T020000Z/20120330T180000Z + ,20130330T030000Z/20130330T190000Z,20140330T020000Z/20140330T180000Z + ,20150330T020000Z/20150330T180000Z,20160330T020000Z/20160330T180000Z + ,20170330T020000Z/20170330T180000Z,20180330T020000Z/20180330T180000Z + ,20190330T030000Z/20190330T190000Z,20200330T020000Z/20200330T180000Z + ,20210330T020000Z/20210330T180000Z +DTSTAMP:20140227T123547Z +CLASS:PUBLIC +SUMMARY:Another RDATE repeating event +UID:AAAA1C572093EC3FC125799C004AFFFF-Lotus_Notes_Generated +END:VEVENT + +END:VCALENDAR