Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[stable10] Let CopyPlugin fall back to default impl if no ICopySource #31805

Merged
merged 1 commit into from
Jun 18, 2018
Merged
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
25 changes: 11 additions & 14 deletions apps/dav/lib/DAV/CopyPlugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,12 @@

/**
* Class CopyPlugin - adds own implementation of the COPY method.
* This is necessary because we don't want the target to be deleted before the move.
*
* Invokes ICopySource->copy() if the source and destination types match.
* If the source doesn't implement ICopySource, fall back to the default behavior.
*
* Currently only used for versions.
* This is necessary because we don't want the target to be deleted before the move.
* Deleting the target will kill the versions which is the wrong behavior.
*
* @package OCA\DAV\DAV
Expand All @@ -57,9 +61,6 @@ function initialize(Server $server) {
/**
* WebDAV HTTP COPY method
*
* This method copies one uri to a different uri, and works much like the MOVE request
* A lot of the actual request processing is done in getCopyMoveInfo
*
* @param RequestInterface $request
* @param ResponseInterface $response
* @return bool
Expand All @@ -71,24 +72,20 @@ function httpCopy(RequestInterface $request, ResponseInterface $response) {

$path = $request->getPath();

$copyInfo = $this->server->getCopyAndMoveInfo($request);
$sourceNode = $this->server->tree->getNodeForPath($path);
if (!$sourceNode instanceof ICopySource) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Will this fall back to put?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it will fall back to whatever the old copy operation does, it is likely a put, yes, with more permission checks

return true;
}

$copyInfo = $this->server->getCopyAndMoveInfo($request);
$destinationNode = $copyInfo['destinationNode'];
if (!$copyInfo['destinationExists'] || !$destinationNode instanceof File || !$sourceNode instanceof IFile) {
return true;
}

if (!$this->server->emit('beforeBind', [$copyInfo['destination']])) return false;

$copySuccess = false;
if ($sourceNode instanceof ICopySource) {
$copySuccess = $sourceNode->copy($destinationNode->getFileInfo()->getPath());
}
if (!$copySuccess) {
$destinationNode->acquireLock(ILockingProvider::LOCK_SHARED);
$destinationNode->put($sourceNode->get());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Won't it break sth? I guess this was introduced with some purpose @DeepDiver1975

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no - because the processing will fallback to the origninal implementation above.

$destinationNode->releaseLock(ILockingProvider::LOCK_SHARED);
}
$sourceNode->copy($destinationNode->getFileInfo()->getPath());

$this->server->emit('afterBind', [$copyInfo['destination']]);

Expand Down
56 changes: 48 additions & 8 deletions apps/dav/tests/unit/DAV/CopyPluginTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
use Sabre\HTTP\RequestInterface;
use Sabre\HTTP\ResponseInterface;
use Test\TestCase;
use OCP\Files\FileInfo;
use OCP\Files\ForbiddenException;
use OCA\DAV\Files\ICopySource;

class CopyPluginTest extends TestCase {

Expand Down Expand Up @@ -71,7 +74,7 @@ public function setUp() {
public function testCopyPluginReturnTrue($destinationExists, $destinationNode, $sourceNode) {

$this->tree->expects($this->once())->method('getNodeForPath')->willReturn($sourceNode);
$this->server->expects($this->once())->method('getCopyAndMoveInfo')->willReturn([
$this->server->expects($this->any())->method('getCopyAndMoveInfo')->willReturn([
'destinationExists' => $destinationExists,
'destinationNode' => $destinationNode
]);
Expand All @@ -82,16 +85,16 @@ public function testCopyPluginReturnTrue($destinationExists, $destinationNode, $

public function providesSourceAndDestinations() {
return [
'destination does not exist' => [false, null, null],
'destination is not a File' => [true, $this->createMock(Directory::class), $this->createMock(IFile::class)],
'source is not a IFile' => [true, $this->createMock(File::class), $this->createMock(ICollection::class)],
'source is not a ICopySource' => [true, null, $this->createMock(IFile::class)],
'destination does not exist' => [false, null, $this->createMock(ICopySource::class)],
'destination is not a File' => [true, $this->createMock(Directory::class), $this->createMock(ICopySource::class)],
];
}

public function testCopyPluginReturnFalse() {

$destinationNode = $this->createMock(File::class);
$sourceNode = $this->createMock(IFile::class);
$sourceNode = $this->createMock([IFile::class, ICopySource::class]);

$this->tree->expects($this->once())->method('getNodeForPath')->willReturn($sourceNode);
$this->server->expects($this->once())->method('getCopyAndMoveInfo')->willReturn([
Expand All @@ -104,9 +107,12 @@ public function testCopyPluginReturnFalse() {
$this->server->expects($this->exactly(2))->method('emit')->withConsecutive(
['beforeBind', ['destination.txt']], ['afterBind', ['destination.txt']])->willReturn(true);

// make sure the file content is actually copied over
$sourceNode->expects($this->once())->method('get')->willReturn('123456');
$destinationNode->expects($this->once())->method('put')->with('123456');
$fileInfo = $this->createMock(FileInfo::class);
$fileInfo->method('getPath')->willReturn('path/to/destination.txt');

$sourceNode->expects($this->once())->method('copy')->with('path/to/destination.txt');

$destinationNode->expects($this->once())->method('getFileInfo')->willReturn($fileInfo);

// make sure http status and content length are properly set
$this->response->expects($this->once())->method('setHeader')->with('Content-Length', '0');
Expand All @@ -115,4 +121,38 @@ public function testCopyPluginReturnFalse() {
$returnValue = $this->plugin->httpCopy($this->request, $this->response);
$this->assertFalse($returnValue);
}

/**
* @expectedException OCA\DAV\Connector\Sabre\Exception\Forbidden
* @expectedExceptionMessage Test exception
*/
public function testCopyPluginRethrowForbidden() {

$destinationNode = $this->createMock(File::class);
$sourceNode = $this->createMock([ICopySource::class, IFile::class]);

$this->tree->expects($this->once())->method('getNodeForPath')->willReturn($sourceNode);
$this->server->expects($this->once())->method('getCopyAndMoveInfo')->willReturn([
'destinationExists' => true,
'destinationNode' => $destinationNode,
'destination' => 'destination.txt'
]);

// make sure the plugin properly emits beforeBind and afterBind
$this->server->expects($this->once(2))
->method('emit')
->with('beforeBind', ['destination.txt'])
->willReturn(true);

$sourceNode->expects($this->once())
->method('copy')
->will($this->throwException(new ForbiddenException('Test exception', false)));

$fileInfo = $this->createMock(FileInfo::class);
$fileInfo->method('isDeletable')->willReturn(true);

$destinationNode->expects($this->once())->method('getFileInfo')->willReturn($fileInfo);

$this->plugin->httpCopy($this->request, $this->response);
}
}