/** * Parses the 'invite-reply' POST request. * * This method returns an array, containing the following properties: * * href - The sharee who is replying * * status - One of the self::STATUS_* constants * * calendarUri - The url of the shared calendar * * inReplyTo - The unique id of the share invitation. * * summary - Optional description of the reply. * * @param DOMDocument $dom * @return array */ protected function parseInviteReplyRequest(DOMDocument $dom) { $xpath = new \DOMXPath($dom); $xpath->registerNamespace('cs', Sabre_CalDAV_Plugin::NS_CALENDARSERVER); $xpath->registerNamespace('d', 'urn:DAV'); $hostHref = $xpath->evaluate('string(cs:hosturl/d:href)'); if (!$hostHref) { throw new Sabre_DAV_Exception_BadRequest('The {' . Sabre_CalDAV_Plugin::NS_CALENDARSERVER . '}hosturl/{DAV:}href element is required'); } return array('href' => $xpath->evaluate('string(d:href)'), 'calendarUri' => $this->server->calculateUri($hostHref), 'inReplyTo' => $xpath->evaluate('string(cs:in-reply-to)'), 'summary' => $xpath->evaluate('string(cs:summary)'), 'status' => $xpath->evaluate('boolean(cs:invite-accepted)') ? self::STATUS_ACCEPTED : self::STATUS_DECLINED); }
/** * 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(Sabre_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); } $this->server->httpResponse->sendStatus(207); $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8'); $this->server->httpResponse->sendBody($this->server->generateMultiStatus($propertyList)); }
/** * This method is responsible for handling the 'ACL' event. * * @param string $uri * @return void */ public function httpACL($uri) { $body = $this->server->httpRequest->getBody(true); $dom = Sabre_DAV_XMLUtil::loadDOMDocument($body); $newAcl = Sabre_DAVACL_Property_Acl::unserialize($dom->firstChild)->getPrivileges(); // Normalizing urls foreach ($newAcl as $k => $newAce) { $newAcl[$k]['principal'] = $this->server->calculateUri($newAce['principal']); } $node = $this->server->tree->getNodeForPath($uri); if (!$node instanceof Sabre_DAVACL_IACL) { throw new Sabre_DAV_Exception_MethodNotAllowed('This node does not support the ACL method'); } $oldAcl = $this->getACL($node); $supportedPrivileges = $this->getFlatPrivilegeSet(); /* Checking if protected principals from the existing principal set are not overwritten. */ foreach ($oldAcl as $k => $oldAce) { if (!isset($oldAce['protected']) || !$oldAce['protected']) { continue; } $found = false; foreach ($newAcl as $newAce) { if ($newAce['privilege'] === $oldAce['privilege'] && $newAce['principal'] === $oldAce['principal'] && $newAce['protected']) { $found = true; } } if (!$found) { throw new Sabre_DAVACL_Exception_AceConflict('This resource contained a protected {DAV:}ace, but this privilege did not occur in the ACL request'); } } foreach ($newAcl as $k => $newAce) { // Do we recognize the privilege if (!isset($supportedPrivileges[$newAce['privilege']])) { throw new Sabre_DAVACL_Exception_NotSupportedPrivilege('The privilege you specified (' . $newAce['privilege'] . ') is not recognized by this server'); } if ($supportedPrivileges[$newAce['privilege']]['abstract']) { throw new Sabre_DAVACL_Exception_NoAbstract('The privilege you specified (' . $newAce['privilege'] . ') is an abstract privilege'); } // Looking up the principal try { $principal = $this->server->tree->getNodeForPath($newAce['principal']); } catch (Sabre_DAV_Exception_FileNotFound $e) { throw new Sabre_DAVACL_Exception_NotRecognizedPrincipal('The specified principal (' . $newAce['principal'] . ') does not exist'); } if (!$principal instanceof Sabre_DAVACL_IPrincipal) { throw new Sabre_DAVACL_Exception_NotRecognizedPrincipal('The specified uri (' . $newAce['principal'] . ') is not a principal'); } } $node->setACL($newAcl); }
/** * 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(Sabre_DAV_XMLUtil::parseProperties($dom->firstChild)); $hrefElems = $dom->getElementsByTagNameNS('urn:DAV', 'href'); $xpath = new DOMXPath($dom); $xpath->registerNameSpace('cal', Sabre_CalDAV_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 Sabre_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 Sabre_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'])); }
/** * validateLock should be called when a write operation is about to happen * It will check if the requested url is locked, and see if the correct lock tokens are passed * * @param mixed $urls List of relevant urls. Can be an array, a string or nothing at all for the current request uri * @param mixed $lastLock This variable will be populated with the last checked lock object (Sabre_DAV_Locks_LockInfo) * @return bool */ protected function validateLock($urls = null, &$lastLock = null) { if (is_null($urls)) { $urls = array($this->server->getRequestUri()); } elseif (is_string($urls)) { $urls = array($urls); } elseif (!is_array($urls)) { throw new Sabre_DAV_Exception('The urls parameter should either be null, a string or an array'); } $conditions = $this->getIfConditions(); // We're going to loop through the urls and make sure all lock conditions are satisfied foreach ($urls as $url) { $locks = $this->getLocks($url); // If there were no conditions, but there were locks, we fail if (!$conditions && $locks) { reset($locks); $lastLock = current($locks); return false; } // If there were no locks or conditions, we go to the next url if (!$locks && !$conditions) { continue; } foreach ($conditions as $condition) { $conditionUri = $condition['uri'] ? $this->server->calculateUri($condition['uri']) : ''; // If the condition has a url, and it isn't part of the affected url at all, check the next condition if ($conditionUri && strpos($url, $conditionUri) !== 0) { continue; } // The tokens array contians arrays with 2 elements. 0=true/false for normal/not condition, 1=locktoken // At least 1 condition has to be satisfied foreach ($condition['tokens'] as $conditionToken) { $etagValid = true; $lockValid = true; // key 2 can contain an etag if ($conditionToken[2]) { $uri = $conditionUri ? $conditionUri : $this->server->getRequestUri(); $node = $this->server->tree->getNodeForPath($uri); $etagValid = $node->getETag() == $conditionToken[2]; } // key 1 can contain a lock token if ($conditionToken[1]) { $lockValid = false; // Match all the locks foreach ($locks as $lockIndex => $lock) { $lockToken = 'opaquelocktoken:' . $lock->token; // Checking NOT if (!$conditionToken[0] && $lockToken != $conditionToken[1]) { // Condition valid, onto the next $lockValid = true; break; } if ($conditionToken[0] && $lockToken == $conditionToken[1]) { $lastLock = $lock; // Condition valid and lock matched unset($locks[$lockIndex]); $lockValid = true; break; } } } // If, after checking both etags and locks they are stil valid, // we can continue with the next condition. if ($etagValid && $lockValid) { continue 2; } } // No conditions matched, so we fail throw new Sabre_DAV_Exception_PreconditionFailed('The tokens provided in the if header did not match', 'If'); } // Conditions were met, we'll also need to check if all the locks are gone if (count($locks)) { reset($locks); // There's still locks, we fail $lastLock = current($locks); return false; } } // We got here, this means every condition was satisfied return true; }