Beispiel #1
0
 /**
  * The deserialize method is called during xml parsing.
  *
  * This method is called statictly, this is because in theory this method
  * may be used as a type of constructor, or factory method.
  *
  * Often you want to return an instance of the current class, but you are
  * free to return other data as well.
  *
  * You are responsible for advancing the reader to the next element. Not
  * doing anything will result in a never-ending loop.
  *
  * If you just want to skip parsing for this element altogether, you can
  * just call $reader->next();
  *
  * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
  * the next element.
  *
  * @param Reader $reader
  * @return mixed
  */
 static function xmlDeserialize(Reader $reader)
 {
     $result = ['name' => null, 'is-not-defined' => false, 'comp-filters' => [], 'prop-filters' => [], 'time-range' => false];
     $att = $reader->parseAttributes();
     $result['name'] = $att['name'];
     $elems = $reader->parseInnerTree();
     if (is_array($elems)) {
         foreach ($elems as $elem) {
             switch ($elem['name']) {
                 case '{' . Plugin::NS_CALDAV . '}comp-filter':
                     $result['comp-filters'][] = $elem['value'];
                     break;
                 case '{' . Plugin::NS_CALDAV . '}prop-filter':
                     $result['prop-filters'][] = $elem['value'];
                     break;
                 case '{' . Plugin::NS_CALDAV . '}is-not-defined':
                     $result['is-not-defined'] = true;
                     break;
                 case '{' . Plugin::NS_CALDAV . '}time-range':
                     if ($result['name'] === 'VCALENDAR') {
                         throw new BadRequest('You cannot add time-range filters on the VCALENDAR component');
                     }
                     $result['time-range'] = ['start' => isset($elem['attributes']['start']) ? DateTimeParser::parseDateTime($elem['attributes']['start']) : null, 'end' => isset($elem['attributes']['end']) ? DateTimeParser::parseDateTime($elem['attributes']['end']) : null];
                     if ($result['time-range']['start'] && $result['time-range']['end'] && $result['time-range']['end'] <= $result['time-range']['start']) {
                         throw new BadRequest('The end-date must be larger than the start-date');
                     }
                     break;
             }
         }
     }
     return $result;
 }
Beispiel #2
0
 /**
  * The deserialize method is called during xml parsing.
  *
  * This method is called statictly, this is because in theory this method
  * may be used as a type of constructor, or factory method.
  *
  * Often you want to return an instance of the current class, but you are
  * free to return other data as well.
  *
  * You are responsible for advancing the reader to the next element. Not
  * doing anything will result in a never-ending loop.
  *
  * If you just want to skip parsing for this element altogether, you can
  * just call $reader->next();
  *
  * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
  * the next element.
  *
  * @param Reader $reader
  * @return mixed
  */
 static function xmlDeserialize(Reader $reader)
 {
     $result = ['name' => null, 'is-not-defined' => false, 'param-filters' => [], 'text-match' => null, 'time-range' => false];
     $att = $reader->parseAttributes();
     $result['name'] = $att['name'];
     $elems = $reader->parseInnerTree();
     if (is_array($elems)) {
         foreach ($elems as $elem) {
             switch ($elem['name']) {
                 case '{' . Plugin::NS_CALDAV . '}param-filter':
                     $result['param-filters'][] = $elem['value'];
                     break;
                 case '{' . Plugin::NS_CALDAV . '}is-not-defined':
                     $result['is-not-defined'] = true;
                     break;
                 case '{' . Plugin::NS_CALDAV . '}time-range':
                     $result['time-range'] = ['start' => isset($elem['attributes']['start']) ? DateTimeParser::parseDateTime($elem['attributes']['start']) : null, 'end' => isset($elem['attributes']['end']) ? DateTimeParser::parseDateTime($elem['attributes']['end']) : null];
                     if ($result['time-range']['start'] && $result['time-range']['end'] && $result['time-range']['end'] <= $result['time-range']['start']) {
                         throw new BadRequest('The end-date must be larger than the start-date');
                     }
                     break;
                 case '{' . Plugin::NS_CALDAV . '}text-match':
                     $result['text-match'] = ['negate-condition' => isset($elem['attributes']['negate-condition']) && $elem['attributes']['negate-condition'] === 'yes', 'collation' => isset($elem['attributes']['collation']) ? $elem['attributes']['collation'] : 'i;ascii-casemap', 'value' => $elem['value']];
                     break;
             }
         }
     }
     return $result;
 }
 /**
  * The deserialize method is called during xml parsing.
  *
  * This method is called statictly, this is because in theory this method
  * may be used as a type of constructor, or factory method.
  *
  * Often you want to return an instance of the current class, but you are
  * free to return other data as well.
  *
  * You are responsible for advancing the reader to the next element. Not
  * doing anything will result in a never-ending loop.
  *
  * If you just want to skip parsing for this element altogether, you can
  * just call $reader->next();
  *
  * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
  * the next element.
  *
  * @param Reader $reader
  * @return mixed
  */
 static function xmlDeserialize(Reader $reader)
 {
     $timeRange = '{' . Plugin::NS_CALDAV . '}time-range';
     $start = null;
     $end = null;
     foreach ((array) $reader->parseInnerTree([]) as $elem) {
         if ($elem['name'] !== $timeRange) {
             continue;
         }
         $start = empty($elem['attributes']['start']) ?: $elem['attributes']['start'];
         $end = empty($elem['attributes']['end']) ?: $elem['attributes']['end'];
     }
     if (!$start && !$end) {
         throw new BadRequest('The freebusy report must have a time-range element');
     }
     if ($start) {
         $start = DateTimeParser::parseDateTime($start);
     }
     if ($end) {
         $end = DateTimeParser::parseDateTime($end);
     }
     $result = new self();
     $result->start = $start;
     $result->end = $end;
     return $result;
 }
Beispiel #4
0
 /**
  * Returns the value, in the format it should be encoded for json.
  *
  * This method must always return an array.
  *
  * @return array
  */
 public function getJsonValue()
 {
     $return = array();
     foreach ($this->getParts() as $item) {
         list($start, $end) = explode('/', $item, 2);
         $start = DateTimeParser::parseDateTime($start);
         // This is a duration value.
         if ($end[0] === 'P') {
             $return[] = array($start->format('Y-m-d\\TH:i:s'), $end);
         } else {
             $end = DateTimeParser::parseDateTime($end);
             $return[] = array($start->format('Y-m-d\\TH:i:s'), $end->format('Y-m-d\\TH:i:s'));
         }
     }
     return $return;
 }
Beispiel #5
0
 /**
  * The deserialize method is called during xml parsing.
  *
  * This method is called statictly, this is because in theory this method
  * may be used as a type of constructor, or factory method.
  *
  * Often you want to return an instance of the current class, but you are
  * free to return other data as well.
  *
  * You are responsible for advancing the reader to the next element. Not
  * doing anything will result in a never-ending loop.
  *
  * If you just want to skip parsing for this element altogether, you can
  * just call $reader->next();
  *
  * $reader->parseInnerTree() will parse the entire sub-tree, and advance to
  * the next element.
  *
  * @param Reader $reader
  * @return mixed
  */
 static function xmlDeserialize(Reader $reader)
 {
     $result = ['contentType' => $reader->getAttribute('content-type') ?: 'text/calendar', 'version' => $reader->getAttribute('version') ?: '2.0'];
     $elems = (array) $reader->parseInnerTree();
     foreach ($elems as $elem) {
         switch ($elem['name']) {
             case '{' . Plugin::NS_CALDAV . '}expand':
                 $result['expand'] = ['start' => isset($elem['attributes']['start']) ? DateTimeParser::parseDateTime($elem['attributes']['start']) : null, 'end' => isset($elem['attributes']['end']) ? DateTimeParser::parseDateTime($elem['attributes']['end']) : null];
                 if (!$result['expand']['start'] || !$result['expand']['end']) {
                     throw new BadRequest('The "start" and "end" attributes are required when expanding calendar-data');
                 }
                 if ($result['expand']['end'] <= $result['expand']['start']) {
                     throw new BadRequest('The end-date must be larger than the start-date when expanding calendar-data');
                 }
                 break;
         }
     }
     return $result;
 }
 /**
  * Check if a datetime with year > 4000 will not throw an exception. iOS seems to use 45001231T235959 in yearly recurring events
  */
 function testParseICalendarDateTimeGreaterThan4000()
 {
     $dateTime = DateTimeParser::parseDateTime('45001231T235959');
     $expected = new DateTime('4500-12-31 23:59:59', new DateTimeZone('UTC'));
     $this->assertEquals($expected, $dateTime);
     $dateTime = DateTimeParser::parse('45001231T235959');
     $this->assertEquals($expected, $dateTime);
 }
 /**
  * Parses the input data and returns a correct VFREEBUSY object, wrapped in
  * a VCALENDAR.
  *
  * @return Component
  */
 function getResult()
 {
     $busyTimes = [];
     foreach ($this->objects as $key => $object) {
         foreach ($object->getBaseComponents() as $component) {
             switch ($component->name) {
                 case 'VEVENT':
                     $FBTYPE = 'BUSY';
                     if (isset($component->TRANSP) && strtoupper($component->TRANSP) === 'TRANSPARENT') {
                         break;
                     }
                     if (isset($component->STATUS)) {
                         $status = strtoupper($component->STATUS);
                         if ($status === 'CANCELLED') {
                             break;
                         }
                         if ($status === 'TENTATIVE') {
                             $FBTYPE = 'BUSY-TENTATIVE';
                         }
                     }
                     $times = [];
                     if ($component->RRULE) {
                         try {
                             $iterator = new EventIterator($object, (string) $component->uid, $this->timeZone);
                         } catch (NoInstancesException $e) {
                             // This event is recurring, but it doesn't have a single
                             // instance. We are skipping this event from the output
                             // entirely.
                             unset($this->objects[$key]);
                             continue;
                         }
                         if ($this->start) {
                             $iterator->fastForward($this->start);
                         }
                         $maxRecurrences = 200;
                         while ($iterator->valid() && --$maxRecurrences) {
                             $startTime = $iterator->getDTStart();
                             if ($this->end && $startTime > $this->end) {
                                 break;
                             }
                             $times[] = [$iterator->getDTStart(), $iterator->getDTEnd()];
                             $iterator->next();
                         }
                     } else {
                         $startTime = $component->DTSTART->getDateTime($this->timeZone);
                         if ($this->end && $startTime > $this->end) {
                             break;
                         }
                         $endTime = null;
                         if (isset($component->DTEND)) {
                             $endTime = $component->DTEND->getDateTime($this->timeZone);
                         } elseif (isset($component->DURATION)) {
                             $duration = DateTimeParser::parseDuration((string) $component->DURATION);
                             $endTime = clone $startTime;
                             $endTime = $endTime->add($duration);
                         } elseif (!$component->DTSTART->hasTime()) {
                             $endTime = clone $startTime;
                             $endTime = $endTime->modify('+1 day');
                         } else {
                             // The event had no duration (0 seconds)
                             break;
                         }
                         $times[] = [$startTime, $endTime];
                     }
                     foreach ($times as $time) {
                         if ($this->end && $time[0] > $this->end) {
                             break;
                         }
                         if ($this->start && $time[1] < $this->start) {
                             break;
                         }
                         $busyTimes[] = [$time[0], $time[1], $FBTYPE];
                     }
                     break;
                 case 'VFREEBUSY':
                     foreach ($component->FREEBUSY as $freebusy) {
                         $fbType = isset($freebusy['FBTYPE']) ? strtoupper($freebusy['FBTYPE']) : 'BUSY';
                         // Skipping intervals marked as 'free'
                         if ($fbType === 'FREE') {
                             continue;
                         }
                         $values = explode(',', $freebusy);
                         foreach ($values as $value) {
                             list($startTime, $endTime) = explode('/', $value);
                             $startTime = DateTimeParser::parseDateTime($startTime);
                             if (substr($endTime, 0, 1) === 'P' || substr($endTime, 0, 2) === '-P') {
                                 $duration = DateTimeParser::parseDuration($endTime);
                                 $endTime = clone $startTime;
                                 $endTime = $endTime->add($duration);
                             } else {
                                 $endTime = DateTimeParser::parseDateTime($endTime);
                             }
                             if ($this->start && $this->start > $endTime) {
                                 continue;
                             }
                             if ($this->end && $this->end < $startTime) {
                                 continue;
                             }
                             $busyTimes[] = [$startTime, $endTime, $fbType];
                         }
                     }
                     break;
             }
         }
     }
     if ($this->baseObject) {
         $calendar = $this->baseObject;
     } else {
         $calendar = new VCalendar();
     }
     $vfreebusy = $calendar->createComponent('VFREEBUSY');
     $calendar->add($vfreebusy);
     if ($this->start) {
         $dtstart = $calendar->createProperty('DTSTART');
         $dtstart->setDateTime($this->start);
         $vfreebusy->add($dtstart);
     }
     if ($this->end) {
         $dtend = $calendar->createProperty('DTEND');
         $dtend->setDateTime($this->end);
         $vfreebusy->add($dtend);
     }
     $dtstamp = $calendar->createProperty('DTSTAMP');
     $dtstamp->setDateTime(new DateTimeImmutable('now', new \DateTimeZone('UTC')));
     $vfreebusy->add($dtstamp);
     foreach ($busyTimes as $busyTime) {
         $busyTime[0] = $busyTime[0]->setTimeZone(new \DateTimeZone('UTC'));
         $busyTime[1] = $busyTime[1]->setTimeZone(new \DateTimeZone('UTC'));
         $prop = $calendar->createProperty('FREEBUSY', $busyTime[0]->format('Ymd\\THis\\Z') . '/' . $busyTime[1]->format('Ymd\\THis\\Z'));
         $prop['FBTYPE'] = $busyTime[2];
         $vfreebusy->add($prop);
     }
     return $calendar;
 }
 /**
  * Parses the CALDAV:expand element
  *
  * @param \DOMElement $parentNode
  * @return void
  */
 protected function parseExpand(\DOMElement $parentNode)
 {
     $start = $parentNode->getAttribute('start');
     if (!$start) {
         throw new \Sabre\DAV\Exception\BadRequest('The "start" attribute is required for the CALDAV:expand element');
     }
     $start = VObject\DateTimeParser::parseDateTime($start);
     $end = $parentNode->getAttribute('end');
     if (!$end) {
         throw new \Sabre\DAV\Exception\BadRequest('The "end" attribute is required for the CALDAV:expand element');
     }
     $end = VObject\DateTimeParser::parseDateTime($end);
     if ($end <= $start) {
         throw new \Sabre\DAV\Exception\BadRequest('The end-date must be larger than the start-date in the expand element.');
     }
     return array('start' => $start, 'end' => $end);
 }
Beispiel #9
0
 /**
  * Validates the node for correctness.
  *
  * The following options are supported:
  *   Node::REPAIR - May attempt to automatically repair the problem.
  *
  * This method returns an array with detected problems.
  * Every element has the following properties:
  *
  *  * level - problem level.
  *  * message - A human-readable string describing the issue.
  *  * node - A reference to the problematic node.
  *
  * The level means:
  *   1 - The issue was repaired (only happens if REPAIR was turned on)
  *   2 - An inconsequential issue
  *   3 - A severe issue.
  *
  * @param int $options
  *
  * @return array
  */
 function validate($options = 0)
 {
     $messages = parent::validate($options);
     $valueType = $this->getValueType();
     $values = $this->getParts();
     try {
         foreach ($values as $value) {
             switch ($valueType) {
                 case 'DATE':
                     DateTimeParser::parseDate($value);
                     break;
                 case 'DATE-TIME':
                     DateTimeParser::parseDateTime($value);
                     break;
             }
         }
     } catch (InvalidDataException $e) {
         $messages[] = ['level' => 3, 'message' => 'The supplied value (' . $value . ') is not a correct ' . $valueType, 'node' => $this];
     }
     return $messages;
 }
 /**
  * This method takes an array of iCalendar objects and applies its busy
  * times on fbData.
  *
  * @param FreeBusyData $fbData
  * @param VCalendar[] $objects
  */
 protected function calculateBusy(FreeBusyData $fbData, array $objects)
 {
     foreach ($objects as $key => $object) {
         foreach ($object->getBaseComponents() as $component) {
             switch ($component->name) {
                 case 'VEVENT':
                     $FBTYPE = 'BUSY';
                     if (isset($component->TRANSP) && strtoupper($component->TRANSP) === 'TRANSPARENT') {
                         break;
                     }
                     if (isset($component->STATUS)) {
                         $status = strtoupper($component->STATUS);
                         if ($status === 'CANCELLED') {
                             break;
                         }
                         if ($status === 'TENTATIVE') {
                             $FBTYPE = 'BUSY-TENTATIVE';
                         }
                     }
                     $times = [];
                     if ($component->RRULE) {
                         try {
                             $iterator = new EventIterator($object, (string) $component->uid, $this->timeZone);
                         } catch (NoInstancesException $e) {
                             // This event is recurring, but it doesn't have a single
                             // instance. We are skipping this event from the output
                             // entirely.
                             unset($this->objects[$key]);
                             continue;
                         }
                         if ($this->start) {
                             $iterator->fastForward($this->start);
                         }
                         $maxRecurrences = Settings::$maxRecurrences;
                         while ($iterator->valid() && --$maxRecurrences) {
                             $startTime = $iterator->getDTStart();
                             if ($this->end && $startTime > $this->end) {
                                 break;
                             }
                             $times[] = [$iterator->getDTStart(), $iterator->getDTEnd()];
                             $iterator->next();
                         }
                     } else {
                         $startTime = $component->DTSTART->getDateTime($this->timeZone);
                         if ($this->end && $startTime > $this->end) {
                             break;
                         }
                         $endTime = null;
                         if (isset($component->DTEND)) {
                             $endTime = $component->DTEND->getDateTime($this->timeZone);
                         } elseif (isset($component->DURATION)) {
                             $duration = DateTimeParser::parseDuration((string) $component->DURATION);
                             $endTime = clone $startTime;
                             $endTime = $endTime->add($duration);
                         } elseif (!$component->DTSTART->hasTime()) {
                             $endTime = clone $startTime;
                             $endTime = $endTime->modify('+1 day');
                         } else {
                             // The event had no duration (0 seconds)
                             break;
                         }
                         $times[] = [$startTime, $endTime];
                     }
                     foreach ($times as $time) {
                         if ($this->end && $time[0] > $this->end) {
                             break;
                         }
                         if ($this->start && $time[1] < $this->start) {
                             break;
                         }
                         $fbData->add($time[0]->getTimeStamp(), $time[1]->getTimeStamp(), $FBTYPE);
                     }
                     break;
                 case 'VFREEBUSY':
                     foreach ($component->FREEBUSY as $freebusy) {
                         $fbType = isset($freebusy['FBTYPE']) ? strtoupper($freebusy['FBTYPE']) : 'BUSY';
                         // Skipping intervals marked as 'free'
                         if ($fbType === 'FREE') {
                             continue;
                         }
                         $values = explode(',', $freebusy);
                         foreach ($values as $value) {
                             list($startTime, $endTime) = explode('/', $value);
                             $startTime = DateTimeParser::parseDateTime($startTime);
                             if (substr($endTime, 0, 1) === 'P' || substr($endTime, 0, 2) === '-P') {
                                 $duration = DateTimeParser::parseDuration($endTime);
                                 $endTime = clone $startTime;
                                 $endTime = $endTime->add($duration);
                             } else {
                                 $endTime = DateTimeParser::parseDateTime($endTime);
                             }
                             if ($this->start && $this->start > $endTime) {
                                 continue;
                             }
                             if ($this->end && $this->end < $startTime) {
                                 continue;
                             }
                             $fbData->add($startTime->getTimeStamp(), $endTime->getTimeStamp(), $fbType);
                         }
                     }
                     break;
             }
         }
     }
 }
Beispiel #11
0
 /**
  * This method is responsible for parsing the request and generating the
  * response for the CALDAV:free-busy-query REPORT.
  *
  * @param \DOMNode $dom
  * @return void
  */
 protected function freeBusyQueryReport(\DOMNode $dom)
 {
     $start = null;
     $end = null;
     foreach ($dom->firstChild->childNodes as $childNode) {
         $clark = DAV\XMLUtil::toClarkNotation($childNode);
         if ($clark == '{' . self::NS_CALDAV . '}time-range') {
             $start = $childNode->getAttribute('start');
             $end = $childNode->getAttribute('end');
             break;
         }
     }
     if ($start) {
         $start = VObject\DateTimeParser::parseDateTime($start);
     }
     if ($end) {
         $end = VObject\DateTimeParser::parseDateTime($end);
     }
     $uri = $this->server->getRequestUri();
     if (!$start && !$end) {
         throw new DAV\Exception\BadRequest('The freebusy report must have a time-range filter');
     }
     $acl = $this->server->getPlugin('acl');
     if ($acl) {
         $acl->checkPrivileges($uri, '{' . self::NS_CALDAV . '}read-free-busy');
     }
     $calendar = $this->server->tree->getNodeForPath($uri);
     if (!$calendar instanceof ICalendar) {
         throw new DAV\Exception\NotImplemented('The free-busy-query REPORT is only implemented on calendars');
     }
     $tzProp = '{' . self::NS_CALDAV . '}calendar-timezone';
     // Figuring out the default timezone for the calendar, for floating
     // times.
     $calendarProps = $this->server->getProperties($uri, [$tzProp]);
     if (isset($calendarProps[$tzProp])) {
         $vtimezoneObj = VObject\Reader::read($calendarProps[$tzProp]);
         $calendarTimeZone = $vtimezoneObj->VTIMEZONE->getTimeZone();
     } else {
         $calendarTimeZone = new DateTimeZone('UTC');
     }
     // Doing a calendar-query first, to make sure we get the most
     // performance.
     $urls = $calendar->calendarQuery(['name' => 'VCALENDAR', 'comp-filters' => [['name' => 'VEVENT', 'comp-filters' => [], 'prop-filters' => [], 'is-not-defined' => false, 'time-range' => ['start' => $start, 'end' => $end]]], 'prop-filters' => [], 'is-not-defined' => false, 'time-range' => null]);
     $objects = array_map(function ($url) use($calendar) {
         $obj = $calendar->getChild($url)->get();
         return $obj;
     }, $urls);
     $generator = new VObject\FreeBusyGenerator();
     $generator->setObjects($objects);
     $generator->setTimeRange($start, $end);
     $generator->setTimeZone($calendarTimeZone);
     $result = $generator->getResult();
     $result = $result->serialize();
     $this->server->httpResponse->setStatus(200);
     $this->server->httpResponse->setHeader('Content-Type', 'text/calendar');
     $this->server->httpResponse->setHeader('Content-Length', strlen($result));
     $this->server->httpResponse->setBody($result);
 }
Beispiel #12
0
 /**
  * This method is responsible for parsing the request and generating the
  * response for the CALDAV:free-busy-query REPORT.
  *
  * @param \DOMNode $dom
  * @return void
  */
 protected function freeBusyQueryReport(\DOMNode $dom)
 {
     $start = null;
     $end = null;
     foreach ($dom->firstChild->childNodes as $childNode) {
         $clark = DAV\XMLUtil::toClarkNotation($childNode);
         if ($clark == '{' . self::NS_CALDAV . '}time-range') {
             $start = $childNode->getAttribute('start');
             $end = $childNode->getAttribute('end');
             break;
         }
     }
     if ($start) {
         $start = VObject\DateTimeParser::parseDateTime($start);
     }
     if ($end) {
         $end = VObject\DateTimeParser::parseDateTime($end);
     }
     if (!$start && !$end) {
         throw new DAV\Exception\BadRequest('The freebusy report must have a time-range filter');
     }
     $acl = $this->server->getPlugin('acl');
     if (!$acl) {
         throw new DAV\Exception('The ACL plugin must be loaded for free-busy queries to work');
     }
     $uri = $this->server->getRequestUri();
     $acl->checkPrivileges($uri, '{' . self::NS_CALDAV . '}read-free-busy');
     $calendar = $this->server->tree->getNodeForPath($uri);
     if (!$calendar instanceof ICalendar) {
         throw new DAV\Exception\NotImplemented('The free-busy-query REPORT is only implemented on calendars');
     }
     // Doing a calendar-query first, to make sure we get the most
     // performance.
     $urls = $calendar->calendarQuery(array('name' => 'VCALENDAR', 'comp-filters' => array(array('name' => 'VEVENT', 'comp-filters' => array(), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => array('start' => $start, 'end' => $end))), 'prop-filters' => array(), 'is-not-defined' => false, 'time-range' => null));
     $objects = array_map(function ($url) use($calendar) {
         $obj = $calendar->getChild($url)->get();
         return $obj;
     }, $urls);
     $generator = new VObject\FreeBusyGenerator();
     $generator->setObjects($objects);
     $generator->setTimeRange($start, $end);
     $result = $generator->getResult();
     $result = $result->serialize();
     $this->server->httpResponse->sendStatus(200);
     $this->server->httpResponse->setHeader('Content-Type', 'text/calendar');
     $this->server->httpResponse->setHeader('Content-Length', strlen($result));
     $this->server->httpResponse->sendBody($result);
 }
Beispiel #13
0
 /**
  * Validates the node for correctness.
  *
  * The following options are supported:
  *   Node::REPAIR - May attempt to automatically repair the problem.
  *
  * This method returns an array with detected problems.
  * Every element has the following properties:
  *
  *  * level - problem level.
  *  * message - A human-readable string describing the issue.
  *  * node - A reference to the problematic node.
  *
  * The level means:
  *   1 - The issue was repaired (only happens if REPAIR was turned on)
  *   2 - An inconsequential issue
  *   3 - A severe issue.
  *
  * @param int $options
  * @return array
  */
 public function validate($options = 0)
 {
     $messages = parent::validate($options);
     $valueType = $this->getValueType();
     $value = $this->getValue();
     try {
         switch ($valueType) {
             case 'DATE':
                 $foo = DateTimeParser::parseDate($value);
                 break;
             case 'DATE-TIME':
                 $foo = DateTimeParser::parseDateTime($value);
                 break;
         }
     } catch (\LogicException $e) {
         $messages[] = array('level' => 3, 'message' => 'The supplied value (' . $value . ') is not a correct ' . $valueType, 'node' => $this);
     }
     return $messages;
 }
 /**
  * @depends testParseICalendarDateTime
  */
 function testParseICalendarDateTimeCustomTimeZone()
 {
     $dateTime = DateTimeParser::parseDateTime('20100316T141405', new DateTimeZone('Europe/Amsterdam'));
     $compare = new DateTime('2010-03-16 13:14:05', new DateTimeZone('UTC'));
     $this->assertEquals($compare, $dateTime);
 }
Beispiel #15
0
 /**
  * Parse an event update for an attendee.
  *
  * This function figures out if we need to send a reply to an organizer.
  *
  * @param VCalendar $calendar
  * @param array $eventInfo
  * @param array $oldEventInfo
  * @param string $attendee
  * @return Message[]
  */
 protected function parseEventForAttendee(VCalendar $calendar, array $eventInfo, array $oldEventInfo, $attendee)
 {
     $instances = array();
     foreach ($oldEventInfo['attendees'][$attendee]['instances'] as $instance) {
         $instances[$instance['id']] = array('id' => $instance['id'], 'oldstatus' => $instance['partstat'], 'newstatus' => null);
     }
     foreach ($eventInfo['attendees'][$attendee]['instances'] as $instance) {
         if (isset($instances[$instance['id']])) {
             $instances[$instance['id']]['newstatus'] = $instance['partstat'];
         } else {
             $instances[$instance['id']] = array('id' => $instance['id'], 'oldstatus' => null, 'newstatus' => $instance['partstat']);
         }
     }
     // We need to also look for differences in EXDATE. If there are new
     // items in EXDATE, it means that an attendee deleted instances of an
     // event, which means we need to send DECLINED specifically for those
     // instances.
     // We only need to do that though, if the master event is not declined.
     if ($instances['master']['newstatus'] !== 'DECLINED') {
         foreach ($eventInfo['exdate'] as $exDate) {
             if (!in_array($exDate, $oldEventInfo['exdate'])) {
                 $instances[$exDate] = array('id' => $exDate, 'oldstatus' => $instances['master']['oldstatus'], 'newstatus' => 'DECLINED');
             }
         }
     }
     $message = new Message();
     $message->uid = $eventInfo['uid'];
     $message->method = 'REPLY';
     $message->component = 'VEVENT';
     $message->sequence = $eventInfo['sequence'];
     $message->sender = $attendee;
     $message->senderName = $eventInfo['attendees'][$attendee]['name'];
     $message->recipient = $eventInfo['organizer'];
     $message->recipientName = $eventInfo['organizerName'];
     $icalMsg = new VCalendar();
     $icalMsg->METHOD = 'REPLY';
     $hasReply = false;
     foreach ($instances as $instance) {
         if ($instance['oldstatus'] == $instance['newstatus']) {
             // Skip
             continue;
         }
         $event = $icalMsg->add('VEVENT', array('UID' => $message->uid, 'SEQUENCE' => $message->sequence));
         if ($instance['id'] !== 'master') {
             $event->{'RECURRENCE-ID'} = DateTimeParser::parseDateTime($instance['id'], $eventInfo['timezone']);
         }
         $organizer = $event->add('ORGANIZER', $message->recipient);
         if ($message->recipientName) {
             $organizer['CN'] = $message->recipientName;
         }
         $attendee = $event->add('ATTENDEE', $message->sender, array('PARTSTAT' => $instance['newstatus']));
         if ($message->senderName) {
             $attendee['CN'] = $message->senderName;
         }
         $hasReply = true;
     }
     if ($hasReply) {
         $message->message = $icalMsg;
         return array($message);
     } else {
         return array();
     }
 }
Beispiel #16
0
 /**
  * This method is responsible for parsing the request and generating the
  * response for the CALDAV:free-busy-query REPORT.
  *
  * @param DOMNode $dom
  * @return void
  */
 protected function freeBusyQueryReport(DOMNode $dom)
 {
     $start = null;
     $end = null;
     foreach ($dom->firstChild->childNodes as $childNode) {
         $clark = Sabre_DAV_XMLUtil::toClarkNotation($childNode);
         if ($clark == '{' . self::NS_CALDAV . '}time-range') {
             $start = $childNode->getAttribute('start');
             $end = $childNode->getAttribute('end');
             break;
         }
     }
     if ($start) {
         $start = VObject\DateTimeParser::parseDateTime($start);
     }
     if ($end) {
         $end = VObject\DateTimeParser::parseDateTime($end);
     }
     if (!$start && !$end) {
         throw new Sabre_DAV_Exception_BadRequest('The freebusy report must have a time-range filter');
     }
     $acl = $this->server->getPlugin('acl');
     if (!$acl) {
         throw new Sabre_DAV_Exception('The ACL plugin must be loaded for free-busy queries to work');
     }
     $uri = $this->server->getRequestUri();
     $acl->checkPrivileges($uri, '{' . self::NS_CALDAV . '}read-free-busy');
     $calendar = $this->server->tree->getNodeForPath($uri);
     if (!$calendar instanceof Sabre_CalDAV_ICalendar) {
         throw new Sabre_DAV_Exception_NotImplemented('The free-busy-query REPORT is only implemented on calendars');
     }
     $objects = array_map(function ($child) {
         $obj = $child->get();
         if (is_resource($obj)) {
             $obj = stream_get_contents($obj);
         }
         return $obj;
     }, $calendar->getChildren());
     $generator = new VObject\FreeBusyGenerator();
     $generator->setObjects($objects);
     $generator->setTimeRange($start, $end);
     $result = $generator->getResult();
     $result = $result->serialize();
     $this->server->httpResponse->sendStatus(200);
     $this->server->httpResponse->setHeader('Content-Type', 'text/calendar');
     $this->server->httpResponse->setHeader('Content-Length', strlen($result));
     $this->server->httpResponse->sendBody($result);
 }