summaryrefslogtreecommitdiffstats
path: root/apps/dav/lib
diff options
context:
space:
mode:
authorThomas Müller <thomas.mueller@tmit.eu>2016-02-08 21:41:40 +0100
committerThomas Müller <thomas.mueller@tmit.eu>2016-02-08 21:41:40 +0100
commitb77a4b0c6a3c34587316350dbf0ee222fd681b49 (patch)
treedb8937d969d168f58a3f26d2871cccddf1a62e7a /apps/dav/lib
parent75bbd4f8b64209eaf120f1382d7cec6286e68c84 (diff)
parent2f1a60a64dc94f80a664639c1c0dddc399035fd8 (diff)
downloadnextcloud-server-b77a4b0c6a3c34587316350dbf0ee222fd681b49.tar.gz
nextcloud-server-b77a4b0c6a3c34587316350dbf0ee222fd681b49.zip
Merge pull request #22112 from owncloud/dav-filesreport
Provide REPORT method on files DAV to filter/search files
Diffstat (limited to 'apps/dav/lib')
-rw-r--r--apps/dav/lib/connector/sabre/filesreportplugin.php321
-rw-r--r--apps/dav/lib/connector/sabre/serverfactory.php11
2 files changed, 331 insertions, 1 deletions
diff --git a/apps/dav/lib/connector/sabre/filesreportplugin.php b/apps/dav/lib/connector/sabre/filesreportplugin.php
new file mode 100644
index 00000000000..5bdd7a71ddc
--- /dev/null
+++ b/apps/dav/lib/connector/sabre/filesreportplugin.php
@@ -0,0 +1,321 @@
+<?php
+/**
+ * @author Vincent Petry <pvince81@owncloud.com>
+ *
+ * @copyright Copyright (c) 2016, ownCloud, Inc.
+ * @license AGPL-3.0
+ *
+ * This code is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License, version 3,
+ * as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License, version 3,
+ * along with this program. If not, see <http://www.gnu.org/licenses/>
+ *
+ */
+
+namespace OCA\DAV\Connector\Sabre;
+
+use OC\Files\View;
+use Sabre\DAV\Exception\NotFound;
+use Sabre\DAV\Exception\PreconditionFailed;
+use Sabre\DAV\Exception\ReportNotSupported;
+use Sabre\DAV\Exception\BadRequest;
+use Sabre\DAV\ServerPlugin;
+use Sabre\DAV\Tree;
+use Sabre\DAV\Xml\Element\Response;
+use Sabre\DAV\Xml\Response\MultiStatus;
+use Sabre\DAV\PropFind;
+use OCP\SystemTag\ISystemTagObjectMapper;
+use OCP\IUserSession;
+use OCP\Files\Folder;
+use OCP\IGroupManager;
+use OCP\SystemTag\ISystemTagManager;
+use OCP\SystemTag\TagNotFoundException;
+
+class FilesReportPlugin extends ServerPlugin {
+
+ // namespace
+ const NS_OWNCLOUD = 'http://owncloud.org/ns';
+ const REPORT_NAME = '{http://owncloud.org/ns}filter-files';
+ const SYSTEMTAG_PROPERTYNAME = '{http://owncloud.org/ns}systemtag';
+
+ /**
+ * Reference to main server object
+ *
+ * @var \Sabre\DAV\Server
+ */
+ private $server;
+
+ /**
+ * @var Tree
+ */
+ private $tree;
+
+ /**
+ * @var View
+ */
+ private $fileView;
+
+ /**
+ * @var ISystemTagManager
+ */
+ private $tagManager;
+
+ /**
+ * @var ISystemTagObjectMapper
+ */
+ private $tagMapper;
+
+ /**
+ * @var IUserSession
+ */
+ private $userSession;
+
+ /**
+ * @var IGroupManager
+ */
+ private $groupManager;
+
+ /**
+ * @var Folder
+ */
+ private $userFolder;
+
+ /**
+ * @param Tree $tree
+ * @param View $view
+ */
+ public function __construct(Tree $tree,
+ View $view,
+ ISystemTagManager $tagManager,
+ ISystemTagObjectMapper $tagMapper,
+ IUserSession $userSession,
+ IGroupManager $groupManager,
+ Folder $userFolder
+ ) {
+ $this->tree = $tree;
+ $this->fileView = $view;
+ $this->tagManager = $tagManager;
+ $this->tagMapper = $tagMapper;
+ $this->userSession = $userSession;
+ $this->groupManager = $groupManager;
+ $this->userFolder = $userFolder;
+ }
+
+ /**
+ * This initializes the plugin.
+ *
+ * This function is called by \Sabre\DAV\Server, after
+ * addPlugin is called.
+ *
+ * This method should set up the required event subscriptions.
+ *
+ * @param \Sabre\DAV\Server $server
+ * @return void
+ */
+ public function initialize(\Sabre\DAV\Server $server) {
+
+ $server->xml->namespaceMap[self::NS_OWNCLOUD] = 'oc';
+
+ $this->server = $server;
+ $this->server->on('report', array($this, 'onReport'));
+ }
+
+ /**
+ * Returns a list of reports this plugin supports.
+ *
+ * This will be used in the {DAV:}supported-report-set property.
+ *
+ * @param string $uri
+ * @return array
+ */
+ public function getSupportedReportSet($uri) {
+ return [self::REPORT_NAME];
+ }
+
+ /**
+ * REPORT operations to look for files
+ *
+ * @param string $reportName
+ * @param [] $report
+ * @param string $uri
+ * @return bool
+ * @throws NotFound
+ * @throws ReportNotSupported
+ */
+ public function onReport($reportName, $report, $uri) {
+ $reportTargetNode = $this->server->tree->getNodeForPath($uri);
+ if (!$reportTargetNode instanceof Directory || $reportName !== self::REPORT_NAME) {
+ throw new ReportNotSupported();
+ }
+
+ $ns = '{' . $this::NS_OWNCLOUD . '}';
+ $requestedProps = [];
+ $filterRules = [];
+
+ // parse report properties and gather filter info
+ foreach ($report as $reportProps) {
+ $name = $reportProps['name'];
+ if ($name === $ns . 'filter-rules') {
+ $filterRules = $reportProps['value'];
+ } else if ($name === '{DAV:}prop') {
+ // propfind properties
+ foreach ($reportProps['value'] as $propVal) {
+ $requestedProps[] = $propVal['name'];
+ }
+ }
+ }
+
+ if (empty($filterRules)) {
+ // an empty filter would return all existing files which would be slow
+ throw new BadRequest('Missing filter-rule block in request');
+ }
+
+ // gather all file ids matching filter
+ try {
+ $resultFileIds = $this->processFilterRules($filterRules);
+ } catch (TagNotFoundException $e) {
+ throw new PreconditionFailed('Cannot filter by non-existing tag', 0, $e);
+ }
+
+ // find sabre nodes by file id, restricted to the root node path
+ $results = $this->findNodesByFileIds($reportTargetNode, $resultFileIds);
+
+ $responses = $this->prepareResponses($requestedProps, $results);
+
+ $xml = $this->server->xml->write(
+ '{DAV:}multistatus',
+ new MultiStatus($responses)
+ );
+
+ $this->server->httpResponse->setStatus(207);
+ $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
+ $this->server->httpResponse->setBody($xml);
+
+ return false;
+ }
+
+ /**
+ * Find file ids matching the given filter rules
+ *
+ * @param array $filterRules
+ * @return array array of unique file id results
+ *
+ * @throws TagNotFoundException whenever a tag was not found
+ */
+ public function processFilterRules($filterRules) {
+ $ns = '{' . $this::NS_OWNCLOUD . '}';
+ $resultFileIds = [];
+ $systemTagIds = [];
+ foreach ($filterRules as $filterRule) {
+ if ($filterRule['name'] === $ns . 'systemtag') {
+ $systemTagIds[] = $filterRule['value'];
+ }
+ }
+
+ // check user permissions, if applicable
+ if (!$this->isAdmin()) {
+ // check visibility/permission
+ $tags = $this->tagManager->getTagsByIds($systemTagIds);
+ $unknownTagIds = [];
+ foreach ($tags as $tag) {
+ if (!$tag->isUserVisible()) {
+ $unknownTagIds[] = $tag->getId();
+ }
+ }
+
+ if (!empty($unknownTagIds)) {
+ throw new TagNotFoundException('Tag with ids ' . implode(', ', $unknownTagIds) . ' not found');
+ }
+ }
+
+ // fetch all file ids and intersect them
+ foreach ($systemTagIds as $systemTagId) {
+ $fileIds = $this->tagMapper->getObjectIdsForTags($systemTagId, 'files');
+
+ if (empty($resultFileIds)) {
+ $resultFileIds = $fileIds;
+ } else {
+ $resultFileIds = array_intersect($resultFileIds, $fileIds);
+ }
+ }
+ return $resultFileIds;
+ }
+
+ /**
+ * Prepare propfind response for the given nodes
+ *
+ * @param string[] $requestedProps requested properties
+ * @param Node[] nodes nodes for which to fetch and prepare responses
+ * @return Response[]
+ */
+ public function prepareResponses($requestedProps, $nodes) {
+ $responses = [];
+ foreach ($nodes as $node) {
+ $propFind = new PropFind($node->getPath(), $requestedProps);
+
+ $this->server->getPropertiesByNode($propFind, $node);
+ // copied from Sabre Server's getPropertiesForPath
+ $result = $propFind->getResultForMultiStatus();
+ $result['href'] = $propFind->getPath();
+
+ $resourceType = $this->server->getResourceTypeForNode($node);
+ if (in_array('{DAV:}collection', $resourceType) || in_array('{DAV:}principal', $resourceType)) {
+ $result['href'] .= '/';
+ }
+
+ $responses[] = new Response(
+ rtrim($this->server->getBaseUri(), '/') . $node->getPath(),
+ $result,
+ 200
+ );
+ }
+ return $responses;
+ }
+
+ /**
+ * Find Sabre nodes by file ids
+ *
+ * @param Node $rootNode root node for search
+ * @param array $fileIds file ids
+ * @return Node[] array of Sabre nodes
+ */
+ public function findNodesByFileIds($rootNode, $fileIds) {
+ $folder = $this->userFolder;
+ if (trim($rootNode->getPath(), '/') !== '') {
+ $folder = $folder->get($rootNode->getPath());
+ }
+
+ $results = [];
+ foreach ($fileIds as $fileId) {
+ $entry = $folder->getById($fileId);
+ if ($entry) {
+ $entry = current($entry);
+ if ($entry instanceof \OCP\Files\File) {
+ $results[] = new File($this->fileView, $entry);
+ } else if ($entry instanceof \OCP\Files\Folder) {
+ $results[] = new Directory($this->fileView, $entry);
+ }
+ }
+ }
+
+ return $results;
+ }
+
+ /**
+ * Returns whether the currently logged in user is an administrator
+ */
+ private function isAdmin() {
+ $user = $this->userSession->getUser();
+ if ($user !== null) {
+ return $this->groupManager->isAdmin($user->getUID());
+ }
+ return false;
+ }
+}
diff --git a/apps/dav/lib/connector/sabre/serverfactory.php b/apps/dav/lib/connector/sabre/serverfactory.php
index fa4fda46870..9a828787a0d 100644
--- a/apps/dav/lib/connector/sabre/serverfactory.php
+++ b/apps/dav/lib/connector/sabre/serverfactory.php
@@ -115,7 +115,7 @@ class ServerFactory {
// wait with registering these until auth is handled and the filesystem is setup
$server->on('beforeMethod', function () use ($server, $objectTree, $viewCallBack) {
// ensure the skeleton is copied
- \OC::$server->getUserFolder();
+ $userFolder = \OC::$server->getUserFolder();
/** @var \OC\Files\View $view */
$view = $viewCallBack();
@@ -135,6 +135,15 @@ class ServerFactory {
if($this->userSession->isLoggedIn()) {
$server->addPlugin(new \OCA\DAV\Connector\Sabre\TagsPlugin($objectTree, $this->tagManager));
$server->addPlugin(new \OCA\DAV\Connector\Sabre\CommentPropertiesPlugin(\OC::$server->getCommentsManager(), $this->userSession));
+ $server->addPlugin(new \OCA\DAV\Connector\Sabre\FilesReportPlugin(
+ $objectTree,
+ $view,
+ \OC::$server->getSystemTagManager(),
+ \OC::$server->getSystemTagObjectMapper(),
+ $this->userSession,
+ \OC::$server->getGroupManager(),
+ $userFolder
+ ));
// custom properties plugin must be the last one
$server->addPlugin(
new \Sabre\DAV\PropertyStorage\Plugin(