From 5e1b1f652cf8e4f8b3c9b5c9aaf6c2f0b1441b49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Fri, 20 Sep 2013 23:21:48 +0200 Subject: [PATCH 1/2] Contains ownCloud's own implementation of the Sabre_DAV_Server regarding the handling of depth infinity The clean way would have been to submit proper patches to the upstream project and reincorporate the code once released. This will and has to follow! --- apps/files/appinfo/remote.php | 2 +- lib/connector/sabre/server.php | 221 +++++++++++++++++++++++++++++++++ 2 files changed, 222 insertions(+), 1 deletion(-) create mode 100644 lib/connector/sabre/server.php diff --git a/apps/files/appinfo/remote.php b/apps/files/appinfo/remote.php index 9b114ca2e3..c89757775b 100644 --- a/apps/files/appinfo/remote.php +++ b/apps/files/appinfo/remote.php @@ -39,7 +39,7 @@ $rootDir = new OC_Connector_Sabre_Directory(''); $objectTree = new \OC\Connector\Sabre\ObjectTree($rootDir); // Fire up server -$server = new Sabre_DAV_Server($objectTree); +$server = new OC_Connector_Sabre_Server($objectTree); $server->httpRequest = $requestBackend; $server->setBaseUri($baseuri); diff --git a/lib/connector/sabre/server.php b/lib/connector/sabre/server.php new file mode 100644 index 0000000000..f6741ee294 --- /dev/null +++ b/lib/connector/sabre/server.php @@ -0,0 +1,221 @@ +parsePropFindRequest($this->httpRequest->getBody(true)); + + $depth = $this->getHTTPDepth(1); + // The only two options for the depth of a propfind is 0 or 1 + // if ($depth!=0) $depth = 1; + + $newProperties = $this->getPropertiesForPath($uri,$requestedProperties,$depth); + + // This is a multi-status response + $this->httpResponse->sendStatus(207); + $this->httpResponse->setHeader('Content-Type','application/xml; charset=utf-8'); + $this->httpResponse->setHeader('Vary','Brief,Prefer'); + + // Normally this header is only needed for OPTIONS responses, however.. + // iCal seems to also depend on these being set for PROPFIND. Since + // this is not harmful, we'll add it. + $features = array('1','3', 'extended-mkcol'); + foreach($this->plugins as $plugin) { + $features = array_merge($features,$plugin->getFeatures()); + } + + $this->httpResponse->setHeader('DAV',implode(', ',$features)); + + $prefer = $this->getHTTPPrefer(); + $minimal = $prefer['return-minimal']; + + $data = $this->generateMultiStatus($newProperties, $minimal); + $this->httpResponse->sendBody($data); + + } + + /** + * Small helper to support PROPFIND with DEPTH_INFINITY. + */ + private function addPathNodesRecursively(&$nodes, $path) { + foreach($this->tree->getChildren($path) as $childNode) { + $nodes[$path . '/' . $childNode->getName()] = $childNode; + if ($childNode instanceof Sabre_DAV_ICollection) + $this->addPathNodesRecursively($nodes, $path . '/' . $childNode->getName()); + } + } + + public function getPropertiesForPath($path, $propertyNames = array(), $depth = 0) { + +// if ($depth!=0) $depth = 1; + + $path = rtrim($path,'/'); + + $returnPropertyList = array(); + + $parentNode = $this->tree->getNodeForPath($path); + $nodes = array( + $path => $parentNode + ); + if ($depth==1 && $parentNode instanceof Sabre_DAV_ICollection) { + foreach($this->tree->getChildren($path) as $childNode) + $nodes[$path . '/' . $childNode->getName()] = $childNode; + } else if ($depth == self::DEPTH_INFINITY && $parentNode instanceof Sabre_DAV_ICollection) { + $this->addPathNodesRecursively($nodes, $path); + } + + // If the propertyNames array is empty, it means all properties are requested. + // We shouldn't actually return everything we know though, and only return a + // sensible list. + $allProperties = count($propertyNames)==0; + + foreach($nodes as $myPath=>$node) { + + $currentPropertyNames = $propertyNames; + + $newProperties = array( + '200' => array(), + '404' => array(), + ); + + if ($allProperties) { + // Default list of propertyNames, when all properties were requested. + $currentPropertyNames = array( + '{DAV:}getlastmodified', + '{DAV:}getcontentlength', + '{DAV:}resourcetype', + '{DAV:}quota-used-bytes', + '{DAV:}quota-available-bytes', + '{DAV:}getetag', + '{DAV:}getcontenttype', + ); + } + + // If the resourceType was not part of the list, we manually add it + // and mark it for removal. We need to know the resourcetype in order + // to make certain decisions about the entry. + // WebDAV dictates we should add a / and the end of href's for collections + $removeRT = false; + if (!in_array('{DAV:}resourcetype',$currentPropertyNames)) { + $currentPropertyNames[] = '{DAV:}resourcetype'; + $removeRT = true; + } + + $result = $this->broadcastEvent('beforeGetProperties',array($myPath, $node, &$currentPropertyNames, &$newProperties)); + // If this method explicitly returned false, we must ignore this + // node as it is inaccessible. + if ($result===false) continue; + + if (count($currentPropertyNames) > 0) { + + if ($node instanceof Sabre_DAV_IProperties) { + $nodeProperties = $node->getProperties($currentPropertyNames); + + // The getProperties method may give us too much, + // properties, in case the implementor was lazy. + // + // So as we loop through this list, we will only take the + // properties that were actually requested and discard the + // rest. + foreach($currentPropertyNames as $k=>$currentPropertyName) { + if (isset($nodeProperties[$currentPropertyName])) { + unset($currentPropertyNames[$k]); + $newProperties[200][$currentPropertyName] = $nodeProperties[$currentPropertyName]; + } + } + + } + + } + + foreach($currentPropertyNames as $prop) { + + if (isset($newProperties[200][$prop])) continue; + + switch($prop) { + case '{DAV:}getlastmodified' : if ($node->getLastModified()) $newProperties[200][$prop] = new Sabre_DAV_Property_GetLastModified($node->getLastModified()); break; + case '{DAV:}getcontentlength' : + if ($node instanceof Sabre_DAV_IFile) { + $size = $node->getSize(); + if (!is_null($size)) { + $newProperties[200][$prop] = (int)$node->getSize(); + } + } + break; + case '{DAV:}quota-used-bytes' : + if ($node instanceof Sabre_DAV_IQuota) { + $quotaInfo = $node->getQuotaInfo(); + $newProperties[200][$prop] = $quotaInfo[0]; + } + break; + case '{DAV:}quota-available-bytes' : + if ($node instanceof Sabre_DAV_IQuota) { + $quotaInfo = $node->getQuotaInfo(); + $newProperties[200][$prop] = $quotaInfo[1]; + } + break; + case '{DAV:}getetag' : if ($node instanceof Sabre_DAV_IFile && $etag = $node->getETag()) $newProperties[200][$prop] = $etag; break; + case '{DAV:}getcontenttype' : if ($node instanceof Sabre_DAV_IFile && $ct = $node->getContentType()) $newProperties[200][$prop] = $ct; break; + case '{DAV:}supported-report-set' : + $reports = array(); + foreach($this->plugins as $plugin) { + $reports = array_merge($reports, $plugin->getSupportedReportSet($myPath)); + } + $newProperties[200][$prop] = new Sabre_DAV_Property_SupportedReportSet($reports); + break; + case '{DAV:}resourcetype' : + $newProperties[200]['{DAV:}resourcetype'] = new Sabre_DAV_Property_ResourceType(); + foreach($this->resourceTypeMapping as $className => $resourceType) { + if ($node instanceof $className) $newProperties[200]['{DAV:}resourcetype']->add($resourceType); + } + break; + + } + + // If we were unable to find the property, we will list it as 404. + if (!$allProperties && !isset($newProperties[200][$prop])) $newProperties[404][$prop] = null; + + } + + $this->broadcastEvent('afterGetProperties',array(trim($myPath,'/'),&$newProperties, $node)); + + $newProperties['href'] = trim($myPath,'/'); + + // Its is a WebDAV recommendation to add a trailing slash to collectionnames. + // Apple's iCal also requires a trailing slash for principals (rfc 3744), though this is non-standard. + if ($myPath!='' && isset($newProperties[200]['{DAV:}resourcetype'])) { + $rt = $newProperties[200]['{DAV:}resourcetype']; + if ($rt->is('{DAV:}collection') || $rt->is('{DAV:}principal')) { + $newProperties['href'] .='/'; + } + } + + // If the resourcetype property was manually added to the requested property list, + // we will remove it again. + if ($removeRT) unset($newProperties[200]['{DAV:}resourcetype']); + + $returnPropertyList[] = $newProperties; + + } + + return $returnPropertyList; + + } +} From 61f4c2fbc441c009487e81678f6b9a91a8b2edb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Thomas=20M=C3=BCller?= Date: Mon, 23 Sep 2013 15:59:39 +0200 Subject: [PATCH 2/2] adding copyright and PHPDocs --- lib/connector/sabre/server.php | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/lib/connector/sabre/server.php b/lib/connector/sabre/server.php index f6741ee294..5a8a8b52e7 100644 --- a/lib/connector/sabre/server.php +++ b/lib/connector/sabre/server.php @@ -1,19 +1,33 @@