Exemple #1
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']));
 }
Exemple #2
0
 /**
  * This method is responsible for handling the 'ACL' event.
  *
  * @param RequestInterface $request
  * @param ResponseInterface $response
  * @return bool
  */
 function httpAcl(RequestInterface $request, ResponseInterface $response)
 {
     $path = $request->getPath();
     $body = $request->getBodyAsString();
     if (!$body) {
         throw new DAV\Exception\BadRequest('XML body expected in ACL request');
     }
     $acl = $this->server->xml->expect('{DAV:}acl', $body);
     $newAcl = $acl->getPrivileges();
     // Normalizing urls
     foreach ($newAcl as $k => $newAce) {
         $newAcl[$k]['principal'] = $this->server->calculateUri($newAce['principal']);
     }
     $node = $this->server->tree->getNodeForPath($path);
     if (!$node instanceof IACL) {
         throw new DAV\Exception\MethodNotAllowed('This node does not support the ACL method');
     }
     $oldAcl = $this->getACL($node);
     $supportedPrivileges = $this->getFlatPrivilegeSet($node);
     /* Checking if protected principals from the existing principal set are
        not overwritten. */
     foreach ($oldAcl as $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 Exception\AceConflict('This resource contained a protected {DAV:}ace, but this privilege did not occur in the ACL request');
         }
     }
     foreach ($newAcl as $newAce) {
         // Do we recognize the privilege
         if (!isset($supportedPrivileges[$newAce['privilege']])) {
             throw new Exception\NotSupportedPrivilege('The privilege you specified (' . $newAce['privilege'] . ') is not recognized by this server');
         }
         if ($supportedPrivileges[$newAce['privilege']]['abstract']) {
             throw new 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 (DAV\Exception\NotFound $e) {
             throw new Exception\NotRecognizedPrincipal('The specified principal (' . $newAce['principal'] . ') does not exist');
         }
         if (!$principal instanceof IPrincipal) {
             throw new Exception\NotRecognizedPrincipal('The specified uri (' . $newAce['principal'] . ') is not a principal');
         }
     }
     $node->setACL($newAcl);
     $response->setStatus(200);
     // Breaking the event chain, because we handled this method.
     return false;
 }
 /**
  * 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', Plugin::NS_CALENDARSERVER);
     $xpath->registerNamespace('d', 'urn:DAV');
     $hostHref = $xpath->evaluate('string(cs:hosturl/d:href)');
     if (!$hostHref) {
         throw new DAV\Exception\BadRequest('The {' . 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);
 }
Exemple #4
0
 /**
  * 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 = DAV\XMLUtil::loadDOMDocument($body);
     $newAcl = 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 IACL) {
         throw new DAV\Exception\MethodNotAllowed('This node does not support the ACL method');
     }
     $oldAcl = $this->getACL($node);
     $supportedPrivileges = $this->getFlatPrivilegeSet($node);
     /* Checking if protected principals from the existing principal set are
        not overwritten. */
     foreach ($oldAcl as $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 Exception\AceConflict('This resource contained a protected {DAV:}ace, but this privilege did not occur in the ACL request');
         }
     }
     foreach ($newAcl as $newAce) {
         // Do we recognize the privilege
         if (!isset($supportedPrivileges[$newAce['privilege']])) {
             throw new Exception\NotSupportedPrivilege('The privilege you specified (' . $newAce['privilege'] . ') is not recognized by this server');
         }
         if ($supportedPrivileges[$newAce['privilege']]['abstract']) {
             throw new 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 (DAV\Exception\NotFound $e) {
             throw new Exception\NotRecognizedPrincipal('The specified principal (' . $newAce['principal'] . ') does not exist');
         }
         if (!$principal instanceof IPrincipal) {
             throw new Exception\NotRecognizedPrincipal('The specified uri (' . $newAce['principal'] . ') is not a principal');
         }
     }
     $node->setACL($newAcl);
 }
Exemple #5
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']));
 }
Exemple #6
0
 /**
  * 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)
  * @param bool $checkChildLocks If set to true, this function will also look for any locks set on child resources of the supplied urls. This is needed for for example deletion of entire trees.
  * @return bool
  */
 protected function validateLock($urls = null, &$lastLock = null, $checkChildLocks = false)
 {
     if (is_null($urls)) {
         $urls = array($this->server->getRequestUri());
     } elseif (is_string($urls)) {
         $urls = array($urls);
     } elseif (!is_array($urls)) {
         throw new 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, $checkChildLocks);
         // 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) {
             if (!$condition['uri']) {
                 $conditionUri = $this->server->getRequestUri();
             } else {
                 $conditionUri = $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 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;
 }
Exemple #7
0
 /**
  * The validateTokens event is triggered before every request.
  *
  * It's a moment where this plugin can check all the supplied lock tokens
  * in the If: header, and check if they are valid.
  *
  * In addition, it will also ensure that it checks any missing lokens that
  * must be present in the request, and reject requests without the proper
  * tokens.
  *
  * @param RequestInterface $request
  * @param mixed $conditions
  * @return void
  */
 function validateTokens(RequestInterface $request, &$conditions)
 {
     // First we need to gather a list of locks that must be satisfied.
     $mustLocks = [];
     $method = $request->getMethod();
     // Methods not in that list are operations that doesn't alter any
     // resources, and we don't need to check the lock-states for.
     switch ($method) {
         case 'DELETE':
             $mustLocks = array_merge($mustLocks, $this->getLocks($request->getPath(), true));
             break;
         case 'MKCOL':
         case 'MKCALENDAR':
         case 'PROPPATCH':
         case 'PUT':
         case 'PATCH':
             $mustLocks = array_merge($mustLocks, $this->getLocks($request->getPath(), false));
             break;
         case 'MOVE':
             $mustLocks = array_merge($mustLocks, $this->getLocks($request->getPath(), true));
             $mustLocks = array_merge($mustLocks, $this->getLocks($this->server->calculateUri($request->getHeader('Destination')), false));
             break;
         case 'COPY':
             $mustLocks = array_merge($mustLocks, $this->getLocks($this->server->calculateUri($request->getHeader('Destination')), false));
             break;
         case 'LOCK':
             //Temporary measure.. figure out later why this is needed
             // Here we basically ignore all incoming tokens...
             foreach ($conditions as $ii => $condition) {
                 foreach ($condition['tokens'] as $jj => $token) {
                     $conditions[$ii]['tokens'][$jj]['validToken'] = true;
                 }
             }
             return;
     }
     // It's possible that there's identical locks, because of shared
     // parents. We're removing the duplicates here.
     $tmp = [];
     foreach ($mustLocks as $lock) {
         $tmp[$lock->token] = $lock;
     }
     $mustLocks = array_values($tmp);
     foreach ($conditions as $kk => $condition) {
         foreach ($condition['tokens'] as $ii => $token) {
             // Lock tokens always start with opaquelocktoken:
             if (substr($token['token'], 0, 16) !== 'opaquelocktoken:') {
                 continue;
             }
             $checkToken = substr($token['token'], 16);
             // Looping through our list with locks.
             foreach ($mustLocks as $jj => $mustLock) {
                 if ($mustLock->token == $checkToken) {
                     // We have a match!
                     // Removing this one from mustlocks
                     unset($mustLocks[$jj]);
                     // Marking the condition as valid.
                     $conditions[$kk]['tokens'][$ii]['validToken'] = true;
                     // Advancing to the next token
                     continue 2;
                 }
             }
             // If we got here, it means that there was a
             // lock-token, but it was not in 'mustLocks'.
             //
             // This is an edge-case, as it could mean that token
             // was specified with a url that was not 'required' to
             // check. So we're doing one extra lookup to make sure
             // we really don't know this token.
             //
             // This also gets triggered when the user specified a
             // lock-token that was expired.
             $oddLocks = $this->getLocks($condition['uri']);
             foreach ($oddLocks as $oddLock) {
                 if ($oddLock->token === $checkToken) {
                     // We have a hit!
                     $conditions[$kk]['tokens'][$ii]['validToken'] = true;
                     continue 2;
                 }
             }
             // If we get all the way here, the lock-token was
             // really unknown.
         }
     }
     // If there's any locks left in the 'mustLocks' array, it means that
     // the resource was locked and we must block it.
     if ($mustLocks) {
         throw new DAV\Exception\Locked(reset($mustLocks));
     }
 }