Пример #1
0
 /**
  * Sends the response to a sync-collection request.
  *
  * @param string $syncToken
  * @param string $collectionUrl
  * @param array $added
  * @param array $modified
  * @param array $deleted
  * @param array $properties
  * @return void
  */
 protected function sendSyncCollectionResponse($syncToken, $collectionUrl, array $added, array $modified, array $deleted, array $properties)
 {
     $fullPaths = [];
     // Pre-fetching children, if this is possible.
     foreach (array_merge($added, $modified) as $item) {
         $fullPath = $collectionUrl . '/' . $item;
         $fullPaths[] = $fullPath;
     }
     $responses = [];
     foreach ($this->server->getPropertiesForMultiplePaths($fullPaths, $properties) as $fullPath => $props) {
         // The 'Property_Response' class is responsible for generating a
         // single {DAV:}response xml element.
         $responses[] = new DAV\Xml\Element\Response($fullPath, $props);
     }
     // Deleted items also show up as 'responses'. They have no properties,
     // and a single {DAV:}status element set as 'HTTP/1.1 404 Not Found'.
     foreach ($deleted as $item) {
         $fullPath = $collectionUrl . '/' . $item;
         $responses[] = new DAV\Xml\Element\Response($fullPath, [], 404);
     }
     $multiStatus = new DAV\Xml\Response\MultiStatus($responses, self::SYNCTOKEN_PREFIX . $syncToken);
     $this->server->httpResponse->setStatus(207);
     $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
     $this->server->httpResponse->setBody($this->server->xml->write('{DAV:}multistatus', $multiStatus, $this->server->getBaseUri()));
 }
Пример #2
0
 /**
  * Sends the response to a sync-collection request.
  *
  * @param string $syncToken
  * @param string $collectionUrl
  * @param array $added
  * @param array $modified
  * @param array $deleted
  * @param array $properties
  * @return void
  */
 protected function sendSyncCollectionResponse($syncToken, $collectionUrl, array $added, array $modified, array $deleted, array $properties)
 {
     $dom = new \DOMDocument('1.0', 'utf-8');
     $dom->formatOutput = true;
     $multiStatus = $dom->createElement('d:multistatus');
     $dom->appendChild($multiStatus);
     // Adding in default namespaces
     foreach ($this->server->xmlNamespaces as $namespace => $prefix) {
         $multiStatus->setAttribute('xmlns:' . $prefix, $namespace);
     }
     $fullPaths = [];
     // Pre-fetching children, if this is possible.
     foreach (array_merge($added, $modified) as $item) {
         $fullPath = $collectionUrl . '/' . $item;
         $fullPaths[] = $fullPath;
     }
     foreach ($this->server->getPropertiesForMultiplePaths($fullPaths, $properties) as $fullPath => $props) {
         // The 'Property_Response' class is responsible for generating a
         // single {DAV:}response xml element.
         $response = new DAV\Property\Response($fullPath, $props);
         $response->serialize($this->server, $multiStatus);
     }
     // Deleted items also show up as 'responses'. They have no properties,
     // and a single {DAV:}status element set as 'HTTP/1.1 404 Not Found'.
     foreach ($deleted as $item) {
         $fullPath = $collectionUrl . '/' . $item;
         $response = new DAV\Property\Response($fullPath, [], 404);
         $response->serialize($this->server, $multiStatus);
     }
     $syncToken = $dom->createElement('d:sync-token', self::SYNCTOKEN_PREFIX . $syncToken);
     $multiStatus->appendChild($syncToken);
     $this->server->httpResponse->setStatus(207);
     $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
     $this->server->httpResponse->setBody($dom->saveXML());
 }
Пример #3
0
 /**
  * This method is responsible for generating the actual, full response.
  *
  * @param string $path
  * @param DateTime|null $start
  * @param DateTime|null $end
  * @param bool $expand
  * @param string $componentType
  * @param string $format
  * @param array $properties
  * @param ResponseInterface $response
  */
 protected function generateResponse($path, $start, $end, $expand, $componentType, $format, $properties, ResponseInterface $response)
 {
     $calDataProp = '{' . Plugin::NS_CALDAV . '}calendar-data';
     $blobs = [];
     if ($start || $end || $componentType) {
         // If there was a start or end filter, we need to enlist
         // calendarQuery for speed.
         $calendarNode = $this->server->tree->getNodeForPath($path);
         $queryResult = $calendarNode->calendarQuery(['name' => 'VCALENDAR', 'comp-filters' => [['name' => $componentType, 'comp-filters' => [], 'prop-filters' => [], 'is-not-defined' => false, 'time-range' => ['start' => $start, 'end' => $end]]], 'prop-filters' => [], 'is-not-defined' => false, 'time-range' => null]);
         // queryResult is just a list of base urls. We need to prefix the
         // calendar path.
         $queryResult = array_map(function ($item) use($path) {
             return $path . '/' . $item;
         }, $queryResult);
         $nodes = $this->server->getPropertiesForMultiplePaths($queryResult, [$calDataProp]);
         unset($queryResult);
     } else {
         $nodes = $this->server->getPropertiesForPath($path, [$calDataProp], 1);
     }
     // Flattening the arrays
     foreach ($nodes as $node) {
         if (isset($node[200][$calDataProp])) {
             $blobs[$node['href']] = $node[200][$calDataProp];
         }
     }
     unset($nodes);
     $mergedCalendar = $this->mergeObjects($properties, $blobs);
     if ($expand) {
         $calendarTimeZone = null;
         // We're expanding, and for that we need to figure out the
         // calendar's timezone.
         $tzProp = '{' . Plugin::NS_CALDAV . '}calendar-timezone';
         $tzResult = $this->server->getProperties($path, [$tzProp]);
         if (isset($tzResult[$tzProp])) {
             // This property contains a VCALENDAR with a single
             // VTIMEZONE.
             $vtimezoneObj = VObject\Reader::read($tzResult[$tzProp]);
             $calendarTimeZone = $vtimezoneObj->VTIMEZONE->getTimeZone();
             // Destroy circular references to PHP will GC the object.
             $vtimezoneObj->destroy();
             unset($vtimezoneObj);
         } else {
             // Defaulting to UTC.
             $calendarTimeZone = new DateTimeZone('UTC');
         }
         $mergedCalendar->expand($start, $end, $calendarTimeZone);
     }
     $response->setHeader('Content-Type', $format);
     switch ($format) {
         case 'text/calendar':
             $mergedCalendar = $mergedCalendar->serialize();
             break;
         case 'application/calendar+json':
             $mergedCalendar = json_encode($mergedCalendar->jsonSerialize());
             break;
     }
     $response->setStatus(200);
     $response->setBody($mergedCalendar);
 }
Пример #4
0
    /**
     * This function handles the calendar-multiget REPORT.
     *
     * This report is used by the client to fetch the content of a series
     * of urls. Effectively avoiding a lot of redundant requests.
     *
     * @param CalendarMultiGetReport $report
     * @return void
     */
    function calendarMultiGetReport($report) {

        $needsJson = $report->contentType === 'application/calendar+json';

        $timeZones = [];
        $propertyList = [];

        $paths = array_map(
            [$this->server, 'calculateUri'],
            $report->hrefs
        );

        foreach ($this->server->getPropertiesForMultiplePaths($paths, $report->properties) as $uri => $objProps) {

            if (($needsJson || $report->expand) && isset($objProps[200]['{' . self::NS_CALDAV . '}calendar-data'])) {
                $vObject = VObject\Reader::read($objProps[200]['{' . self::NS_CALDAV . '}calendar-data']);

                if ($report->expand) {
                    // We're expanding, and for that we need to figure out the
                    // calendar's timezone.
                    list($calendarPath) = Uri\split($uri);
                    if (!isset($timeZones[$calendarPath])) {
                        // Checking the calendar-timezone property.
                        $tzProp = '{' . self::NS_CALDAV . '}calendar-timezone';
                        $tzResult = $this->server->getProperties($calendarPath, [$tzProp]);
                        if (isset($tzResult[$tzProp])) {
                            // This property contains a VCALENDAR with a single
                            // VTIMEZONE.
                            $vtimezoneObj = VObject\Reader::read($tzResult[$tzProp]);
                            $timeZone = $vtimezoneObj->VTIMEZONE->getTimeZone();
                        } else {
                            // Defaulting to UTC.
                            $timeZone = new DateTimeZone('UTC');
                        }
                        $timeZones[$calendarPath] = $timeZone;
                    }

                    $vObject->expand($report->expand['start'], $report->expand['end'], $timeZones[$calendarPath]);
                }
                if ($needsJson) {
                    $objProps[200]['{' . self::NS_CALDAV . '}calendar-data'] = json_encode($vObject->jsonSerialize());
                } else {
                    $objProps[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize();
                }
            }

            $propertyList[] = $objProps;

        }

        $prefer = $this->server->getHTTPPrefer();

        $this->server->httpResponse->setStatus(207);
        $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
        $this->server->httpResponse->setHeader('Vary', 'Brief,Prefer');
        $this->server->httpResponse->setBody($this->server->generateMultiStatus($propertyList, $prefer['return'] === 'minimal'));

    }
Пример #5
0
 /**
  * This function handles the calendar-multiget REPORT.
  *
  * This report is used by the client to fetch the content of a series
  * of urls. Effectively avoiding a lot of redundant requests.
  *
  * @param \DOMNode $dom
  * @return void
  */
 function calendarMultiGetReport($dom)
 {
     $properties = array_keys(DAV\XMLUtil::parseProperties($dom->firstChild));
     $hrefElems = $dom->getElementsByTagNameNS('urn:DAV', 'href');
     $xpath = new \DOMXPath($dom);
     $xpath->registerNameSpace('cal', Plugin::NS_CALDAV);
     $xpath->registerNameSpace('dav', 'urn:DAV');
     $expand = $xpath->query('/cal:calendar-multiget/dav:prop/cal:calendar-data/cal:expand');
     if ($expand->length > 0) {
         $expandElem = $expand->item(0);
         $start = $expandElem->getAttribute('start');
         $end = $expandElem->getAttribute('end');
         if (!$start || !$end) {
             throw new DAV\Exception\BadRequest('The "start" and "end" attributes are required for the CALDAV:expand element');
         }
         $start = VObject\DateTimeParser::parseDateTime($start);
         $end = VObject\DateTimeParser::parseDateTime($end);
         if ($end <= $start) {
             throw new DAV\Exception\BadRequest('The end-date must be larger than the start-date in the expand element.');
         }
         $expand = true;
     } else {
         $expand = false;
     }
     $needsJson = $xpath->evaluate("boolean(/cal:calendar-multiget/dav:prop/cal:calendar-data[@content-type='application/calendar+json'])");
     $uris = [];
     foreach ($hrefElems as $elem) {
         $uris[] = $this->server->calculateUri($elem->nodeValue);
     }
     foreach ($this->server->getPropertiesForMultiplePaths($uris, $properties) as $uri => $objProps) {
         if (($needsJson || $expand) && isset($objProps[200]['{' . self::NS_CALDAV . '}calendar-data'])) {
             $vObject = VObject\Reader::read($objProps[200]['{' . self::NS_CALDAV . '}calendar-data']);
             if ($expand) {
                 $vObject->expand($start, $end);
             }
             if ($needsJson) {
                 $objProps[200]['{' . self::NS_CALDAV . '}calendar-data'] = json_encode($vObject->jsonSerialize());
             } else {
                 $objProps[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize();
             }
         }
         $propertyList[] = $objProps;
     }
     $prefer = $this->server->getHTTPPRefer();
     $this->server->httpResponse->setStatus(207);
     $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
     $this->server->httpResponse->setHeader('Vary', 'Brief,Prefer');
     $this->server->httpResponse->setBody($this->server->generateMultiStatus($propertyList, $prefer['return-minimal']));
 }
Пример #6
0
 /**
  * This method is responsible for generating the actual, full response.
  *
  * @param string $path
  * @param DateTime|null $start
  * @param DateTime|null $end
  * @param bool $expand
  * @param string $format
  * @param array $properties
  * @param ResponseInterface $response
  */
 protected function generateResponse($path, $start, $end, $expand, $format, $properties, ResponseInterface $response)
 {
     $calDataProp = '{' . Plugin::NS_CALDAV . '}calendar-data';
     $blobs = [];
     if ($start || $end) {
         // If there was a start or end filter, we need to enlist
         // calendarQuery for speed.
         $calendarNode = $this->server->tree->getNodeForPath($path);
         $queryResult = $calendarNode->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]);
         // queryResult is just a list of base urls. We need to prefix the
         // calendar path.
         $queryResult = array_map(function ($item) use($path) {
             return $path . '/' . $item;
         }, $queryResult);
         $nodes = $this->server->getPropertiesForMultiplePaths($queryResult, [$calDataProp]);
         unset($queryResult);
     } else {
         $nodes = $this->server->getPropertiesForPath($path, [$calDataProp], 1);
     }
     // Flattening the arrays
     foreach ($nodes as $node) {
         if (isset($node[200][$calDataProp])) {
             $blobs[] = $node[200][$calDataProp];
         }
     }
     unset($nodes);
     $mergedCalendar = $this->mergeObjects($properties, $blobs);
     if ($expand) {
         $mergedCalendar->expand($start, $end);
     }
     $response->setHeader('Content-Type', $format);
     switch ($format) {
         case 'text/calendar':
             $mergedCalendar = $mergedCalendar->serialize();
             break;
         case 'application/calendar+json':
             $mergedCalendar = json_encode($mergedCalendar->jsonSerialize());
             break;
     }
     $response->setStatus(200);
     $response->setBody($mergedCalendar);
 }