예제 #1
0
 /**
  * 'beforeMethod' event handles. This event handles intercepts GET requests ending
  * with ?export
  *
  * @param string $method
  * @param string $uri
  * @return bool
  */
 public function beforeMethod($method, $uri)
 {
     if ($method != 'GET') {
         return;
     }
     if ($this->server->httpRequest->getQueryString() != 'export') {
         return;
     }
     // splitting uri
     list($uri) = explode('?', $uri, 2);
     $node = $this->server->tree->getNodeForPath($uri);
     if (!$node instanceof IAddressBook) {
         return;
     }
     // Checking ACL, if available.
     if ($aclPlugin = $this->server->getPlugin('acl')) {
         $aclPlugin->checkPrivileges($uri, '{DAV:}read');
     }
     $this->server->httpResponse->setHeader('Content-Type', 'text/directory');
     $this->server->httpResponse->sendStatus(200);
     $nodes = $this->server->getPropertiesForPath($uri, array('{' . Plugin::NS_CARDDAV . '}address-data'), 1);
     $this->server->httpResponse->sendBody($this->generateVCF($nodes));
     // Returning false to break the event chain
     return false;
 }
 /**
  * This method intercepts GET requests to non-files, and changes it into an HTTP PROPFIND request
  *
  * @param string $method
  * @param string $uri
  * @return bool
  */
 public function httpGetInterceptor($method, $uri)
 {
     if ($method != 'GET') {
         return true;
     }
     $node = $this->server->tree->getNodeForPath($uri);
     if ($node instanceof DAV\IFile) {
         return;
     }
     $this->server->invokeMethod('PROPFIND', $uri);
     return false;
 }
예제 #3
0
 /**
  * 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);
 }
예제 #4
0
 /**
  * This method allows us to intercept the 'mkcalendar' sabreAction. This
  * action enables the user to create new calendars from the browser plugin.
  *
  * @param string $uri
  * @param string $action
  * @param array $postVars
  * @return bool
  */
 public function browserPostAction($uri, $action, array $postVars)
 {
     if ($action !== 'mkaddressbook') {
         return;
     }
     $resourceType = array('{DAV:}collection', '{urn:ietf:params:xml:ns:carddav}addressbook');
     $properties = array();
     if (isset($postVars['{DAV:}displayname'])) {
         $properties['{DAV:}displayname'] = $postVars['{DAV:}displayname'];
     }
     $this->server->createCollection($uri . '/' . $postVars['name'], $resourceType, $properties);
     return false;
 }
 /**
  * This method handles the PROPFIND method.
  *
  * It's a very lazy method, it won't bother checking the request body
  * for which properties were requested, and just sends back a default
  * set of properties.
  *
  * @param string $tempLocation
  * @param string $uri
  * @return bool
  */
 public function httpPropfind($tempLocation, $uri)
 {
     if (!file_exists($tempLocation)) {
         return true;
     }
     $hR = $this->server->httpResponse;
     $hR->setHeader('X-Sabre-Temp', 'true');
     $hR->sendStatus(207);
     $hR->setHeader('Content-Type', 'application/xml; charset=utf-8');
     $this->server->parsePropFindRequest($this->server->httpRequest->getBody(true));
     $properties = array('href' => $uri, 200 => array('{DAV:}getlastmodified' => new Property\GetLastModified(filemtime($tempLocation)), '{DAV:}getcontentlength' => filesize($tempLocation), '{DAV:}resourcetype' => new Property\ResourceType(null), '{' . Server::NS_SABREDAV . '}tempFile' => true));
     $data = $this->server->generateMultiStatus(array($properties));
     $hR->sendBody($data);
     return false;
 }
예제 #6
0
 /**
  * Patch an uri
  *
  * The WebDAV patch request can be used to modify only a part of an 
  * existing resource. If the resource does not exist yet and the first
  * offset is not 0, the request fails
  *
  * @param string $uri
  * @return void
  */
 protected function httpPatch($uri)
 {
     // Get the node. Will throw a 404 if not found
     $node = $this->server->tree->getNodeForPath($uri);
     if (!$node instanceof IFile) {
         throw new DAV\Exception\MethodNotAllowed('The target resource does not support the PATCH method.');
     }
     $range = $this->getHTTPUpdateRange();
     if (!$range) {
         throw new DAV\Exception\BadRequest('No valid "X-Update-Range" found in the headers');
     }
     $contentType = strtolower($this->server->httpRequest->getHeader('Content-Type'));
     if ($contentType != 'application/x-sabredav-partialupdate') {
         throw new DAV\Exception\UnsupportedMediaType('Unknown Content-Type header "' . $contentType . '"');
     }
     $len = $this->server->httpRequest->getHeader('Content-Length');
     // Load the begin and end data
     $start = $range[0] ? $range[0] : 0;
     $end = $range[1] ? $range[1] : $len - 1;
     // Check consistency
     if ($end < $start) {
         throw new DAV\Exception\RequestedRangeNotSatisfiable('The end offset (' . $range[1] . ') is lower than the start offset (' . $range[0] . ')');
     }
     if ($end - $start + 1 != $len) {
         throw new DAV\Exception\RequestedRangeNotSatisfiable('Actual data length (' . $len . ') is not consistent with begin (' . $range[0] . ') and end (' . $range[1] . ') offsets');
     }
     // Checking If-None-Match and related headers.
     if (!$this->server->checkPreconditions()) {
         return;
     }
     if (!$this->server->broadcastEvent('beforeWriteContent', array($uri, $node, null))) {
         return;
     }
     $body = $this->server->httpRequest->getBody();
     $etag = $node->putRange($body, $start - 1);
     $this->server->broadcastEvent('afterWriteContent', array($uri, $node));
     $this->server->httpResponse->setHeader('Content-Length', '0');
     if ($etag) {
         $this->server->httpResponse->setHeader('ETag', $etag);
     }
     $this->server->httpResponse->sendStatus(204);
     return false;
 }
예제 #7
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 = array();
     $applyToPrincipalCollectionSet = false;
     // 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 array($searchProperties, array_keys(DAV\XMLUtil::parseProperties($dom->firstChild)), $applyToPrincipalCollectionSet);
 }
예제 #8
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 (SabreForRainLoop\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;
 }
예제 #9
0
 /**
  * Initializes the plugin and registers event handles
  *
  * @param DAV\Server $server
  * @return void
  */
 public function initialize(DAV\Server $server)
 {
     $this->server = $server;
     $this->server->subscribeEvent('beforeMethod', array($this, 'beforeMethod'), 90);
 }
예제 #10
0
 /**
  * This method takes a path/name of an asset and turns it into url
  * suiteable for http access.
  *
  * @param string $assetName
  * @return string
  */
 protected function getAssetUrl($assetName)
 {
     return $this->server->getBaseUri() . '?sabreAction=asset&assetName=' . urlencode($assetName);
 }