Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/dav/lib/Capabilities.php
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public function getCapabilities() {
$cap = [
'dav' => [
'chunking' => '1.0',
'trashbin' => '1.0',
'reports' => [
'search-files',
]
Expand Down
15 changes: 10 additions & 5 deletions apps/dav/lib/Connector/Sabre/CopyEtagHeaderPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@

namespace OCA\DAV\Connector\Sabre;

use Sabre\DAV\Exception\NotFound;
use Sabre\HTTP\RequestInterface;
use Sabre\HTTP\ResponseInterface;

Expand Down Expand Up @@ -71,11 +72,15 @@ public function afterMethod(RequestInterface $request, ResponseInterface $respon
* @return void
*/
public function afterMove($source, $destination) {
$node = $this->server->tree->getNodeForPath($destination);
if ($node instanceof File) {
$eTag = $node->getETag();
$this->server->httpResponse->setHeader('OC-ETag', $eTag);
$this->server->httpResponse->setHeader('ETag', $eTag);
try {
$node = $this->server->tree->getNodeForPath($destination);
if ($node instanceof File) {
$eTag = $node->getETag();
$this->server->httpResponse->setHeader('OC-ETag', $eTag);
$this->server->httpResponse->setHeader('ETag', $eTag);
}
} catch (NotFound $ex) {
// nothing to do then ....
}
}
}
4 changes: 4 additions & 0 deletions apps/dav/lib/Connector/Sabre/Directory.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
use OCA\DAV\Connector\Sabre\Exception\FileLocked;
use OCA\DAV\Connector\Sabre\Exception\Forbidden;
use OCA\DAV\Connector\Sabre\Exception\InvalidPath;
use OCA\DAV\TrashBin\ITrashBinNode;
use OCA\DAV\Upload\FutureFile;
use OCP\Files\FileContentNotAllowedException;
use OCP\Files\ForbiddenException;
Expand Down Expand Up @@ -401,6 +402,9 @@ public function getQuotaInfo() {
*/
public function moveInto($targetName, $fullSourcePath, INode $sourceNode) {
if (!$sourceNode instanceof Node) {
if ($sourceNode instanceof ITrashBinNode) {
return $sourceNode->restore($this->path . '/' . $targetName);
}
// it's a file of another kind, like FutureFile
if ($sourceNode instanceof IFile) {
// fallback to default copy+delete handling
Expand Down
3 changes: 3 additions & 0 deletions apps/dav/lib/RootCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ public function __construct() {
$systemPrincipals->disableListing = $disableListing;
$filesCollection = new Files\RootCollection($userPrincipalBackend, 'principals/users');
$filesCollection->disableListing = $disableListing;
$trashBinCollection = new TrashBin\RootCollection($userPrincipalBackend, 'principals/users');
$trashBinCollection->disableListing = $disableListing;
$caldavBackend = new CalDavBackend($db, $userPrincipalBackend, $groupPrincipalBackend, $random);
$calendarRoot = new CalendarRoot($userPrincipalBackend, $caldavBackend, 'principals/users');
$calendarRoot->disableListing = $disableListing;
Expand Down Expand Up @@ -100,6 +102,7 @@ public function __construct() {
$groupPrincipals,
$systemPrincipals]),
$filesCollection,
$trashBinCollection,
$calendarRoot,
$publicCalendarRoot,
new SimpleCollection('addressbooks', [
Expand Down
4 changes: 3 additions & 1 deletion apps/dav/lib/Server.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
use OCA\DAV\Files\PreviewPlugin;
use OCA\DAV\JobStatus\Entity\JobStatusMapper;
use OCA\DAV\SystemTag\SystemTagPlugin;
use OCA\DAV\TrashBin\TrashBinPlugin;
use OCA\DAV\Upload\ChunkingPlugin;
use OCP\IRequest;
use OCP\SabrePluginEvent;
Expand Down Expand Up @@ -187,6 +188,7 @@ public function __construct(IRequest $request, $baseUri) {

$this->server->addPlugin(new CopyEtagHeaderPlugin());
$this->server->addPlugin(new ChunkingPlugin());
$this->server->addPlugin(new TrashBinPlugin());

// Allow view-only plugin for webdav requests
$this->server->addPlugin(new ViewOnlyPlugin(
Expand Down Expand Up @@ -215,7 +217,7 @@ public function __construct(IRequest $request, $baseUri) {
)
);

if ($this->isRequestForSubtree(['files', 'uploads'])) {
if ($this->isRequestForSubtree(['files', 'uploads', 'trash-bin'])) {
//For files only
$filePropertiesPlugin = new FileCustomPropertiesPlugin(
new FileCustomPropertiesBackend(
Expand Down
153 changes: 153 additions & 0 deletions apps/dav/lib/TrashBin/AbstractTrashBinNode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
<?php
/**
* @author Thomas Müller <thomas.mueller@tmit.eu>
*
* @copyright Copyright (c) 2019, ownCloud GmbH
* @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\TrashBin;

use OCA\Files_Trashbin\Trashbin;
use OCP\Files\FileInfo;
use Sabre\DAV\Exception\Forbidden;

abstract class AbstractTrashBinNode implements ITrashBinNode {

/**
* @var FileInfo
*/
protected $fileInfo;
/**
* @var TrashBinManager
*/
protected $trashBinManager;
/**
* @var string
*/
protected $user;

public function __construct(string $user, FileInfo $fileInfo, TrashBinManager $trashBinManager) {
$this->fileInfo = $fileInfo;
$this->trashBinManager = $trashBinManager;
$this->user = $user;
}

/**
* Returns the name of the node.
*
* This is used to generate the url.
*
* @return string
*/
public function getName() {
return (string)$this->fileInfo->getId();
}

/**
* Returns the mime-type for a file
*
* If null is returned, we'll assume application/octet-stream
*
* @return string|null
*/
public function getContentType() {
return $this->fileInfo->getMimetype();
}

public function getETag() {
return $this->fileInfo->getEtag();
}

public function getLastModified() {
return $this->fileInfo->getMtime();
}
public function getSize() {
return $this->fileInfo->getSize();
}

public function getOriginalFileName() : string {
$path = $this->getPathInTrash();
if (\count($path) === 1) {
$path = \end($path);
$pathInfo = \pathinfo($path);
return $pathInfo['filename'];
}
return \end($path);
}

public function getOriginalLocation() : string {
$pathElements = $this->getPathInTrash();
$path = $pathElements[0];
$pathParts = \pathinfo($path);
$timestamp = (int)\substr($pathParts['extension'], 1);

$pathElements[0] = $pathParts['filename'];
$originalPath = \implode('/', $pathElements);

$location = $this->trashBinManager->getLocation($this->user, $pathParts['filename'], $timestamp);
if ($location !== '.') {
$originalPath = $location . '/' . $originalPath;
}

return $originalPath;
}

public function getDeleteTimestamp() : int {
$path = $this->getPathInTrash();
$path = $path[0];
$pathParts = \pathinfo($path);
return (int)\substr($pathParts['extension'], 1);
}

/**
* @codeCoverageIgnore
* @param string $targetLocation
* @return bool
*/
public function restore(string $targetLocation): bool {
return $this->trashBinManager->restore($this->user, $this, $targetLocation);
}

/**
* @codeCoverageIgnore
*/
public function delete() : void {
$path = $this->fileInfo->getPath();
$path = \explode('/', $path);
$user = $path[1];
$elements = \array_splice($path, 4);
$path = \implode('/', $elements);

$delimiter = \strrpos($path, '.d');
$path = \substr($path, 0, $delimiter);

Trashbin::delete($path, $user, $this->getDeleteTimestamp());
}

/**
* @return array
*/
public function getPathInTrash() {
$path = $this->fileInfo->getPath();
$path = \explode('/', $path);
return \array_splice($path, 4);
}

public function setName($name) {
throw new Forbidden('Permission denied to rename this resource');
}
}
51 changes: 51 additions & 0 deletions apps/dav/lib/TrashBin/ITrashBinNode.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
<?php
/**
* @author Thomas Müller <thomas.mueller@tmit.eu>
*
* @copyright Copyright (c) 2019, ownCloud GmbH
* @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\TrashBin;

use Sabre\DAV\INode;

/**
* Interface ITrashBinNode
*
* A common interface for all trash bin items
*
* @package OCA\DAV\TrashBin
*/
interface ITrashBinNode extends INode {
/**
* @return string
*/
public function getOriginalFileName() : string;
/**
* @return string
*/
public function getOriginalLocation() : string;
/**
* @return int
*/
public function getDeleteTimestamp() : int;

/**
* @param string $targetLocation
* @return bool
*/
public function restore(string $targetLocation) : bool;
}
45 changes: 45 additions & 0 deletions apps/dav/lib/TrashBin/RootCollection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<?php
/**
* @author Thomas Müller <thomas.mueller@tmit.eu>
*
* @copyright Copyright (c) 2019, ownCloud GmbH
* @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\TrashBin;

use Sabre\DAVACL\AbstractPrincipalCollection;

class RootCollection extends AbstractPrincipalCollection {

/**
* This method returns a node for a principal.
*
* The passed array contains principal information, and is guaranteed to
* at least contain a uri item. Other properties may or may not be
* supplied by the authentication backend.
*
* @param array $principalInfo
* @return TrashBinHome
*/
public function getChildForPrincipal(array $principalInfo) {
return new TrashBinHome($principalInfo, new TrashBinManager());
}

public function getName() {
return 'trash-bin';
}
}
35 changes: 35 additions & 0 deletions apps/dav/lib/TrashBin/TrashBinFile.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<?php
/**
* @author Thomas Müller <thomas.mueller@tmit.eu>
*
* @copyright Copyright (c) 2019, ownCloud GmbH
* @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\TrashBin;

use Sabre\DAV\Exception\Forbidden;
use Sabre\DAV\IFile;

class TrashBinFile extends AbstractTrashBinNode implements IFile {
public function put($data) {
throw new Forbidden('Permission denied to write this file');
}

public function get() {
throw new Forbidden('Permission denied to read this file');
}
}
Loading