/**
  * This function handles the calendar-multiget REPORT.
  *
  * prefetch events into calendar container class, to avoid single lookup of events
  *
  * @param \DOMNode $dom
  * @return void
  */
 public function calendarMultiGetReport($dom)
 {
     $properties = array_keys(\Sabre\DAV\XMLUtil::parseProperties($dom->firstChild));
     $hrefElems = $dom->getElementsByTagNameNS('urn:DAV', 'href');
     $filters = array('name' => 'VCALENDAR', 'comp-filters' => array(array('name' => 'VEVENT', 'prop-filters' => array())));
     foreach ($hrefElems as $elem) {
         list($dirName, $baseName) = \Sabre\DAV\URLUtil::splitPath($elem->nodeValue);
         $filters['comp-filters'][0]['prop-filters'][] = array('name' => 'UID', 'text-match' => array('value' => $baseName));
     }
     $node = $this->server->tree->getNodeForPath($this->server->getRequestUri());
     $node->calendarQuery($filters);
 }
 /**
  * Parses the request.
  *
  * @return void
  */
 function parse()
 {
     $filter = $this->xpath->query('/cal:calendar-query/cal:filter');
     if ($filter->length !== 1) {
         throw new \Sabre\DAV\Exception\BadRequest('Only one filter element is allowed');
     }
     $compFilters = $this->parseCompFilters($filter->item(0));
     if (count($compFilters) !== 1) {
         throw new \Sabre\DAV\Exception\BadRequest('There must be exactly 1 top-level comp-filter.');
     }
     $this->filters = $compFilters[0];
     $this->requestedProperties = array_keys(\Sabre\DAV\XMLUtil::parseProperties($this->dom->firstChild));
     $expand = $this->xpath->query('/cal:calendar-query/dav:prop/cal:calendar-data/cal:expand');
     if ($expand->length > 0) {
         $this->expand = $this->parseExpand($expand->item(0));
     }
 }
Ejemplo n.º 3
0
 /**
  * Unserializes the property.
  *
  * This static method should return a an instance of this object.
  *
  * @param \DOMElement $prop
  * @param array $propertyMap
  * @return DAV\IProperty
  */
 static function unserialize(\DOMElement $prop, array $propertyMap)
 {
     $xpath = new \DOMXPath($prop->ownerDocument);
     $xpath->registerNamespace('d', 'urn:DAV');
     // Finding the 'response' element
     $xResponses = $xpath->evaluate('d:response', $prop);
     $result = [];
     for ($jj = 0; $jj < $xResponses->length; $jj++) {
         $xResponse = $xResponses->item($jj);
         // Parsing 'href'
         $href = Href::unserialize($xResponse, $propertyMap);
         $properties = [];
         // Parsing 'status' in 'd:response'
         $responseStatus = $xpath->evaluate('string(d:status)', $xResponse);
         if ($responseStatus) {
             list(, $responseStatus, ) = explode(' ', $responseStatus, 3);
         }
         // Parsing 'propstat'
         $xPropstat = $xpath->query('d:propstat', $xResponse);
         for ($ii = 0; $ii < $xPropstat->length; $ii++) {
             // Parsing 'status'
             $status = $xpath->evaluate('string(d:status)', $xPropstat->item($ii));
             list(, $statusCode, ) = explode(' ', $status, 3);
             $usedPropertyMap = $statusCode == '200' ? $propertyMap : [];
             // Parsing 'prop'
             $properties[$statusCode] = DAV\XMLUtil::parseProperties($xPropstat->item($ii), $usedPropertyMap);
         }
         $result[] = new Response($href->getHref(), $properties, $responseStatus ? $responseStatus : null);
     }
     return new self($result);
 }
Ejemplo n.º 4
0
 /**
  * This function handles the addressbook-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 addressbookMultiGetReport($dom)
 {
     $properties = array_keys(DAV\XMLUtil::parseProperties($dom->firstChild));
     $hrefElems = $dom->getElementsByTagNameNS('urn:DAV', 'href');
     $propertyList = [];
     $uris = [];
     foreach ($hrefElems as $elem) {
         $uris[] = $this->server->calculateUri($elem->nodeValue);
     }
     $xpath = new \DOMXPath($dom);
     $xpath->registerNameSpace('card', Plugin::NS_CARDDAV);
     $xpath->registerNameSpace('dav', 'urn:DAV');
     $contentType = $xpath->evaluate("string(/card:addressbook-multiget/dav:prop/card:address-data/@content-type)");
     $version = $xpath->evaluate("string(/card:addressbook-multiget/dav:prop/card:address-data/@version)");
     if ($version) {
         $contentType .= '; version=' . $version;
     }
     $vcardType = $this->negotiateVCard($contentType);
     $propertyList = [];
     foreach ($this->server->getPropertiesForMultiplePaths($uris, $properties) as $props) {
         if (isset($props['200']['{' . self::NS_CARDDAV . '}address-data'])) {
             $props['200']['{' . self::NS_CARDDAV . '}address-data'] = $this->convertVCard($props[200]['{' . self::NS_CARDDAV . '}address-data'], $vcardType);
         }
         $propertyList[] = $props;
     }
     $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']));
 }
Ejemplo n.º 5
0
 /**
  * Parses the request.
  *
  * @return void
  */
 function parse()
 {
     $limit = $this->xpath->evaluate('number(/card:addressbook-query/card:limit/card:nresults)');
     if (is_nan($limit)) {
         $limit = null;
     }
     $filter = $this->xpath->query('/card:addressbook-query/card:filter');
     // According to the CardDAV spec there needs to be exactly 1 filter
     // element. However, KDE 4.8.2 contains a bug that will encode 0 filter
     // elements, so this is a workaround for that.
     //
     // See: https://bugs.kde.org/show_bug.cgi?id=300047
     if ($filter->length === 0) {
         $test = null;
         $filter = null;
     } elseif ($filter->length === 1) {
         $filter = $filter->item(0);
         $test = $this->xpath->evaluate('string(@test)', $filter);
     } else {
         throw new DAV\Exception\BadRequest('Only one filter element is allowed');
     }
     if (!$test) {
         $test = self::TEST_ANYOF;
     }
     if ($test !== self::TEST_ANYOF && $test !== self::TEST_ALLOF) {
         throw new DAV\Exception\BadRequest('The test attribute must either hold "anyof" or "allof"');
     }
     $propFilters = [];
     if (!is_null($filter)) {
         $propFilterNodes = $this->xpath->query('card:prop-filter', $filter);
         for ($ii = 0; $ii < $propFilterNodes->length; $ii++) {
             $propFilters[] = $this->parsePropFilterNode($propFilterNodes->item($ii));
         }
     }
     $this->filters = $propFilters;
     $this->limit = $limit;
     $this->requestedProperties = array_keys(DAV\XMLUtil::parseProperties($this->dom->firstChild));
     $this->test = $test;
 }
Ejemplo n.º 6
0
 /**
  * WebDAV MKCOL
  *
  * The MKCOL method is used to create a new collection (directory) on the server
  *
  * @param RequestInterface $request
  * @param ResponseInterface $response
  * @return bool
  */
 function httpMkcol(RequestInterface $request, ResponseInterface $response)
 {
     $requestBody = $request->getBodyAsString();
     $path = $request->getPath();
     if ($requestBody) {
         $contentType = $request->getHeader('Content-Type');
         if (strpos($contentType, 'application/xml') !== 0 && strpos($contentType, 'text/xml') !== 0) {
             // We must throw 415 for unsupported mkcol bodies
             throw new Exception\UnsupportedMediaType('The request body for the MKCOL request must have an xml Content-Type');
         }
         $dom = XMLUtil::loadDOMDocument($requestBody);
         if (XMLUtil::toClarkNotation($dom->firstChild) !== '{DAV:}mkcol') {
             // We must throw 415 for unsupported mkcol bodies
             throw new Exception\UnsupportedMediaType('The request body for the MKCOL request must be a {DAV:}mkcol request construct.');
         }
         $properties = [];
         foreach ($dom->firstChild->childNodes as $childNode) {
             if (XMLUtil::toClarkNotation($childNode) !== '{DAV:}set') {
                 continue;
             }
             $properties = array_merge($properties, XMLUtil::parseProperties($childNode, $this->server->propertyMap));
         }
         if (!isset($properties['{DAV:}resourcetype'])) {
             throw new Exception\BadRequest('The mkcol request must include a {DAV:}resourcetype property');
         }
         $resourceType = $properties['{DAV:}resourcetype']->getValue();
         unset($properties['{DAV:}resourcetype']);
     } else {
         $properties = [];
         $resourceType = ['{DAV:}collection'];
     }
     $result = $this->server->createCollection($path, $resourceType, $properties);
     if (is_array($result)) {
         $response->setStatus(207);
         $response->setHeader('Content-Type', 'application/xml; charset=utf-8');
         $response->setBody($this->server->generateMultiStatus([$result]));
     } else {
         $response->setHeader('Content-Length', '0');
         $response->setStatus(201);
     }
     // Sending back false will interupt the event chain and tell the server
     // we've handled this method.
     return false;
 }
Ejemplo n.º 7
0
 /**
  * This function handles the addressbook-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
  */
 public function addressbookMultiGetReport($dom)
 {
     $properties = array_keys(DAV\XMLUtil::parseProperties($dom->firstChild));
     $hrefElems = $dom->getElementsByTagNameNS('urn:DAV', 'href');
     $propertyList = array();
     foreach ($hrefElems as $elem) {
         $uri = $this->server->calculateUri($elem->nodeValue);
         list($propertyList[]) = $this->server->getPropertiesForPath($uri, $properties);
     }
     $prefer = $this->server->getHTTPPRefer();
     $this->server->httpResponse->sendStatus(207);
     $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
     $this->server->httpResponse->setHeader('Vary', 'Brief,Prefer');
     $this->server->httpResponse->sendBody($this->server->generateMultiStatus($propertyList, $prefer['return-minimal']));
 }
Ejemplo n.º 8
0
 /**
  * parsePrincipalPropertySearchReportRequest
  *
  * This method parses the request body from a
  * {DAV:}principal-property-search report.
  *
  * This method returns an array with two elements:
  *  1. an array with properties to search on, and their values
  *  2. a list of propertyvalues that should be returned for the request.
  *
  * @param \DOMDocument $dom
  * @return array
  */
 protected function parsePrincipalPropertySearchReportRequest($dom)
 {
     $httpDepth = $this->server->getHTTPDepth(0);
     if ($httpDepth !== 0) {
         throw new DAV\Exception\BadRequest('This report is only defined when Depth: 0');
     }
     $searchProperties = [];
     $applyToPrincipalCollectionSet = false;
     $test = $dom->firstChild->getAttribute('test') === 'anyof' ? 'anyof' : 'allof';
     // Parsing the search request
     foreach ($dom->firstChild->childNodes as $searchNode) {
         if (DAV\XMLUtil::toClarkNotation($searchNode) == '{DAV:}apply-to-principal-collection-set') {
             $applyToPrincipalCollectionSet = true;
         }
         if (DAV\XMLUtil::toClarkNotation($searchNode) !== '{DAV:}property-search') {
             continue;
         }
         $propertyName = null;
         $propertyValue = null;
         foreach ($searchNode->childNodes as $childNode) {
             switch (DAV\XMLUtil::toClarkNotation($childNode)) {
                 case '{DAV:}prop':
                     $property = DAV\XMLUtil::parseProperties($searchNode);
                     reset($property);
                     $propertyName = key($property);
                     break;
                 case '{DAV:}match':
                     $propertyValue = $childNode->textContent;
                     break;
             }
         }
         if (is_null($propertyName) || is_null($propertyValue)) {
             throw new DAV\Exception\BadRequest('Invalid search request. propertyname: ' . $propertyName . '. propertvvalue: ' . $propertyValue);
         }
         $searchProperties[$propertyName] = $propertyValue;
     }
     return [$searchProperties, array_keys(DAV\XMLUtil::parseProperties($dom->firstChild)), $applyToPrincipalCollectionSet, $test];
 }
Ejemplo n.º 9
0
 /**
  * This method parses the PROPFIND request and returns its information
  *
  * This will either be a list of properties, or an empty array; in which case
  * an {DAV:}allprop was requested.
  *
  * @param string $body
  * @return array
  */
 function parsePropFindRequest($body)
 {
     // If the propfind body was empty, it means IE is requesting 'all' properties
     if (!$body) {
         return [];
     }
     $dom = XMLUtil::loadDOMDocument($body);
     $elem = $dom->getElementsByTagNameNS('urn:DAV', 'propfind')->item(0);
     if (is_null($elem)) {
         throw new Exception\UnsupportedMediaType('We could not find a {DAV:}propfind element in the xml request body');
     }
     return array_keys(XMLUtil::parseProperties($elem));
 }
Ejemplo n.º 10
0
 /**
  * This method parses the PROPFIND request and returns its information
  *
  * This will either be a list of properties, or an empty array; in which case
  * an {DAV:}allprop was requested.
  *
  * @param string $body
  * @return array
  */
 public function parsePropFindRequest($body)
 {
     // If the propfind body was empty, it means IE is requesting 'all' properties
     if (!$body) {
         return array();
     }
     $dom = XMLUtil::loadDOMDocument($body);
     $elem = $dom->getElementsByTagNameNS('urn:DAV', 'propfind')->item(0);
     return array_keys(XMLUtil::parseProperties($elem));
 }
Ejemplo n.º 11
0
 /**
  * Parses the {DAV:}sync-collection REPORT request body.
  *
  * This method returns an array with 3 values:
  *   0 - the value of the {DAV:}sync-token element
  *   1 - the value of the {DAV:}sync-level element
  *   2 - The value of the {DAV:}limit element
  *   3 - A list of requested properties
  *
  * @param \DOMDocument $dom
  * @param int $depth
  * @return void
  */
 protected function parseSyncCollectionRequest(\DOMDocument $dom, $depth)
 {
     $xpath = new \DOMXPath($dom);
     $xpath->registerNamespace('d', 'urn:DAV');
     $syncToken = $xpath->query("//d:sync-token");
     if ($syncToken->length !== 1) {
         throw new DAV\Exception\BadRequest('You must specify a {DAV:}sync-token element, and it must appear exactly once');
     }
     $syncToken = $syncToken->item(0)->nodeValue;
     // Initial sync
     if (!$syncToken) {
         $syncToken = null;
     }
     $syncLevel = $xpath->query("//d:sync-level");
     if ($syncLevel->length === 0) {
         // In case there was no sync-level, it could mean that we're dealing
         // with an old client. For these we must use the depth header
         // instead.
         $syncLevel = $depth;
     } else {
         $syncLevel = $syncLevel->item(0)->nodeValue;
         if ($syncLevel === 'infinite') {
             $syncLevel = DAV\Server::DEPTH_INFINITY;
         }
     }
     $limit = $xpath->query("//d:limit/d:nresults");
     if ($limit->length === 0) {
         $limit = null;
     } else {
         $limit = $limit->item(0)->nodeValue;
     }
     $prop = $xpath->query('d:prop');
     if ($prop->length !== 1) {
         throw new DAV\Exception\BadRequest('The {DAV:}sync-collection must contain extactly 1 {DAV:}prop');
     }
     $properties = array_keys(DAV\XMLUtil::parseProperties($dom->documentElement));
     return [$syncToken, $syncLevel, $limit, $properties];
 }
Ejemplo n.º 12
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);
     }
     $tz = null;
     $timeZones = [];
     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) {
                 // We're expanding, and for that we need to figure out the
                 // calendar's timezone.
                 list($calendarPath) = URLUtil::splitPath($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($start, $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']));
 }
Ejemplo n.º 13
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
  */
 public 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;
     }
     foreach ($hrefElems as $elem) {
         $uri = $this->server->calculateUri($elem->nodeValue);
         list($objProps) = $this->server->getPropertiesForPath($uri, $properties);
         if ($expand && isset($objProps[200]['{' . self::NS_CALDAV . '}calendar-data'])) {
             $vObject = VObject\Reader::read($objProps[200]['{' . self::NS_CALDAV . '}calendar-data']);
             $vObject->expand($start, $end);
             $objProps[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize();
         }
         $propertyList[] = $objProps;
     }
     $prefer = $this->server->getHTTPPRefer();
     $this->server->httpResponse->sendStatus(207);
     $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
     $this->server->httpResponse->setHeader('Vary', 'Brief,Prefer');
     $this->server->httpResponse->sendBody($this->server->generateMultiStatus($propertyList, $prefer['return-minimal']));
 }
 protected function _principalSearchReport(\DOMDocument $dom)
 {
     $requestedProperties = array_keys(\Sabre\DAV\XMLUtil::parseProperties($dom->firstChild));
     $searchTokens = $dom->firstChild->getElementsByTagName('search-token');
     $searchProperties = array();
     if ($searchTokens->length > 0) {
         $searchProperties['{http://calendarserver.org/ns/}search-token'] = $searchTokens->item(0)->nodeValue;
     }
     $result = $this->server->getPlugin('acl')->principalSearch($searchProperties, $requestedProperties);
     $prefer = $this->server->getHTTPPRefer();
     $this->server->httpResponse->sendStatus(207);
     $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
     $this->server->httpResponse->setHeader('Vary', 'Brief,Prefer');
     $this->server->httpResponse->sendBody($this->server->generateMultiStatus($result, $prefer['return-minimal']));
 }