/** * This function handles the calendar-query REPORT * * This report is used by clients to request calendar objects based on * complex conditions. * * @param DOMNode $dom * @return void */ public function calendarQueryReport($dom) { $requestedProperties = array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild)); $filterNode = $dom->getElementsByTagNameNS('urn:ietf:params:xml:ns:caldav', 'filter'); if ($filterNode->length !== 1) { throw new Sabre_DAV_Exception_BadRequest('The calendar-query report must have a filter element'); } $filters = Sabre_CalDAV_XMLUtil::parseCalendarQueryFilters($filterNode->item(0)); $requestedCalendarData = true; if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar-data', $requestedProperties)) { // We always retrieve calendar-data, as we need it for filtering. $requestedProperties[] = '{urn:ietf:params:xml:ns:caldav}calendar-data'; // If calendar-data wasn't explicitly requested, we need to remove // it after processing. $requestedCalendarData = false; } // These are the list of nodes that potentially match the requirement $candidateNodes = $this->server->getPropertiesForPath($this->server->getRequestUri(), $requestedProperties, $this->server->getHTTPDepth(0)); $verifiedNodes = array(); foreach ($candidateNodes as $node) { // If the node didn't have a calendar-data property, it must not be a calendar object if (!isset($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data'])) { continue; } if ($this->validateFilters($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data'], $filters)) { if (!$requestedCalendarData) { unset($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); } $verifiedNodes[] = $node; } } $this->server->httpResponse->sendStatus(207); $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); $this->server->httpResponse->sendBody($this->server->generateMultiStatus($verifiedNodes)); }
protected function parsePrincipalPropertySearchReportRequest($dom) { $httpDepth = $this->server->getHTTPDepth(0); if ($httpDepth !== 0) { throw new Sabre_DAV_Exception_BadRequest('This report is only defined when Depth: 0'); } $searchProperties = array(); // Parsing the search request foreach ($dom->firstChild->childNodes as $searchNode) { if (Sabre_DAV_XMLUtil::toClarkNotation($searchNode) !== '{DAV:}property-search') { continue; } $propertyName = null; $propertyValue = null; foreach ($searchNode->childNodes as $childNode) { switch (Sabre_DAV_XMLUtil::toClarkNotation($childNode)) { case '{DAV:}prop': $property = Sabre_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 Sabre_DAV_Exception_BadRequest('Invalid search request. propertyname: ' . $propertyName . '. propertvvalue: ' . $propertyValue); } $searchProperties[$propertyName] = $propertyValue; } return array($searchProperties, array_keys(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild))); }
/** * Locks an uri * * The WebDAV lock request can be operated to either create a new lock on a file, or to refresh an existing lock * If a new lock is created, a full XML body should be supplied, containing information about the lock such as the type * of lock (shared or exclusive) and the owner of the lock * * If a lock is to be refreshed, no body should be supplied and there should be a valid If header containing the lock * * Additionally, a lock can be requested for a non-existent file. In these case we're obligated to create an empty file as per RFC4918:S7.3 * * @param string $uri * @return void */ protected function httpLock($uri) { $lastLock = null; if (!$this->validateLock($uri, $lastLock)) { // If the existing lock was an exclusive lock, we need to fail if (!$lastLock || $lastLock->scope == Sabre_DAV_Locks_LockInfo::EXCLUSIVE) { //var_dump($lastLock); throw new Sabre_DAV_Exception_ConflictingLock($lastLock); } } if ($body = $this->server->httpRequest->getBody(true)) { // This is a new lock request $lockInfo = $this->parseLockRequest($body); $lockInfo->depth = $this->server->getHTTPDepth(); $lockInfo->uri = $uri; if ($lastLock && $lockInfo->scope != Sabre_DAV_Locks_LockInfo::SHARED) { throw new Sabre_DAV_Exception_ConflictingLock($lastLock); } } elseif ($lastLock) { // This must have been a lock refresh $lockInfo = $lastLock; // The resource could have been locked through another uri. if ($uri != $lockInfo->uri) { $uri = $lockInfo->uri; } } else { // There was neither a lock refresh nor a new lock request throw new Sabre_DAV_Exception_BadRequest('An xml body is required for lock requests'); } if ($timeout = $this->getTimeoutHeader()) { $lockInfo->timeout = $timeout; } $newFile = false; // If we got this far.. we should go check if this node actually exists. If this is not the case, we need to create it first try { $this->server->tree->getNodeForPath($uri); // We need to call the beforeWriteContent event for RFC3744 // Edit: looks like this is not used, and causing problems now. // // See Issue 222 // $this->server->broadcastEvent('beforeWriteContent',array($uri)); } catch (Sabre_DAV_Exception_NotFound $e) { // It didn't, lets create it $this->server->createFile($uri, fopen('php://memory', 'r')); $newFile = true; } $this->lockNode($uri, $lockInfo); $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); $this->server->httpResponse->setHeader('Lock-Token', '<opaquelocktoken:' . $lockInfo->token . '>'); $this->server->httpResponse->sendStatus($newFile ? 201 : 200); $this->server->httpResponse->sendBody($this->generateLockResponse($lockInfo)); }
/** * This function handles the addressbook-query REPORT * * This report is used by the client to filter an addressbook based on a * complex query. * * @param DOMNode $dom * @return void */ protected function addressbookQueryReport($dom) { $query = new Sabre_CardDAV_AddressBookQueryParser($dom); $query->parse(); $depth = $this->server->getHTTPDepth(0); if ($depth == 0) { $candidateNodes = array($this->server->tree->getNodeForPath($this->server->getRequestUri())); } else { $candidateNodes = $this->server->tree->getChildren($this->server->getRequestUri()); } $validNodes = array(); foreach ($candidateNodes as $node) { if (!$node instanceof Sabre_CardDAV_ICard) { continue; } $blob = $node->get(); if (is_resource($blob)) { $blob = stream_get_contents($blob); } if (!$this->validateFilters($blob, $query->filters, $query->test)) { continue; } $validNodes[] = $node; if ($query->limit && $query->limit <= count($validNodes)) { // We hit the maximum number of items, we can stop now. break; } } $result = array(); foreach ($validNodes as $validNode) { if ($depth == 0) { $href = $this->server->getRequestUri(); } else { $href = $this->server->getRequestUri() . '/' . $validNode->getName(); } list($result[]) = $this->server->getPropertiesForPath($href, $query->requestedProperties, 0); } $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'])); }
/** * This function handles the calendar-query REPORT * * This report is used by clients to request calendar objects based on * complex conditions. * * @param DOMNode $dom * @return void */ public function calendarQueryReport($dom) { $parser = new Sabre_CalDAV_CalendarQueryParser($dom); $parser->parse(); $requestedCalendarData = true; $requestedProperties = $parser->requestedProperties; if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar-data', $requestedProperties)) { // We always retrieve calendar-data, as we need it for filtering. $requestedProperties[] = '{urn:ietf:params:xml:ns:caldav}calendar-data'; // If calendar-data wasn't explicitly requested, we need to remove // it after processing. $requestedCalendarData = false; } // These are the list of nodes that potentially match the requirement $candidateNodes = $this->server->getPropertiesForPath($this->server->getRequestUri(), $requestedProperties, $this->server->getHTTPDepth(0)); $verifiedNodes = array(); $validator = new Sabre_CalDAV_CalendarQueryValidator(); foreach ($candidateNodes as $node) { // If the node didn't have a calendar-data property, it must not be a calendar object if (!isset($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data'])) { continue; } $vObject = Sabre_VObject_Reader::read($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); if ($validator->validate($vObject, $parser->filters)) { if (!$requestedCalendarData) { unset($node[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); } if ($parser->expand) { $vObject->expand($parser->expand['start'], $parser->expand['end']); $node[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); } $verifiedNodes[] = $node; } } $this->server->httpResponse->sendStatus(207); $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); $this->server->httpResponse->sendBody($this->server->generateMultiStatus($verifiedNodes)); }
/** * This function handles the calendar-query REPORT * * This report is used by clients to request calendar objects based on * complex conditions. * * @param DOMNode $dom * @return void */ public function calendarQueryReport($dom) { $parser = new Sabre_CalDAV_CalendarQueryParser($dom); $parser->parse(); $node = $this->server->tree->getNodeForPath($this->server->getRequestUri()); $depth = $this->server->getHTTPDepth(0); // The default result is an empty array $result = array(); // The calendarobject was requested directly. In this case we handle // this locally. if ($depth == 0 && $node instanceof Sabre_CalDAV_ICalendarObject) { $requestedCalendarData = true; $requestedProperties = $parser->requestedProperties; if (!in_array('{urn:ietf:params:xml:ns:caldav}calendar-data', $requestedProperties)) { // We always retrieve calendar-data, as we need it for filtering. $requestedProperties[] = '{urn:ietf:params:xml:ns:caldav}calendar-data'; // If calendar-data wasn't explicitly requested, we need to remove // it after processing. $requestedCalendarData = false; } $properties = $this->server->getPropertiesForPath($this->server->getRequestUri(), $requestedProperties, 0); // This array should have only 1 element, the first calendar // object. $properties = current($properties); // If there wasn't any calendar-data returned somehow, we ignore // this. if (isset($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data'])) { $validator = new Sabre_CalDAV_CalendarQueryValidator(); $vObject = Sabre_VObject_Reader::read($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); if ($validator->validate($vObject, $parser->filters)) { // If the client didn't require the calendar-data property, // we won't give it back. if (!$requestedCalendarData) { unset($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); } else { if ($parser->expand) { $vObject->expand($parser->expand['start'], $parser->expand['end']); $properties[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); } } $result = array($properties); } } } // If we're dealing with a calendar, the calendar itself is responsible // for the calendar-query. if ($node instanceof Sabre_CalDAV_ICalendar && ($depth = 1)) { $nodePaths = $node->calendarQuery($parser->filters); foreach ($nodePaths as $path) { list($properties) = $this->server->getPropertiesForPath($this->server->getRequestUri() . '/' . $path, $parser->requestedProperties); if ($parser->expand) { // We need to do some post-processing $vObject = Sabre_VObject_Reader::read($properties[200]['{urn:ietf:params:xml:ns:caldav}calendar-data']); $vObject->expand($parser->expand['start'], $parser->expand['end']); $properties[200]['{' . self::NS_CALDAV . '}calendar-data'] = $vObject->serialize(); } $result[] = $properties; } } $this->server->httpResponse->sendStatus(207); $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); $this->server->httpResponse->sendBody($this->server->generateMultiStatus($result)); }