if (isset($_SERVER['HTTP_OC_CHUNKED'])) {
// exit if we can't create a new file and we don't updatable existing file
- $info = \OC_FileChunking::decodeName($name);
+ $chunkInfo = \OC_FileChunking::decodeName($name);
if (!$this->fileView->isCreatable($this->path) &&
- !$this->fileView->isUpdatable($this->path . '/' . $info['name'])
+ !$this->fileView->isUpdatable($this->path . '/' . $chunkInfo['name'])
) {
throw new \Sabre\DAV\Exception\Forbidden();
}
$this->fileView->verifyPath($this->path, $name);
$path = $this->fileView->getAbsolutePath($this->path) . '/' . $name;
- // using a dummy FileInfo is acceptable here since it will be refreshed after the put is complete
- $info = new \OC\Files\FileInfo($path, null, null, array(), null);
+ // in case the file already exists/overwriting
+ $info = $this->fileView->getFileInfo($this->path . '/' . $name);
+ if (!$info) {
+ // use a dummy FileInfo which is acceptable here since it will be refreshed after the put is complete
+ $info = new \OC\Files\FileInfo($path, null, null, [], null);
+ }
$node = new \OCA\DAV\Connector\Sabre\File($this->fileView, $info);
$node->acquireLock(ILockingProvider::LOCK_SHARED);
return $node->put($data);
use OCP\Files\StorageNotAvailableException;
use OCP\IConfig;
use OCP\IRequest;
+use OCA\DAV\Upload\FutureFile;
class FilesPlugin extends ServerPlugin {
}
});
$this->server->on('beforeMove', [$this, 'checkMove']);
+ $this->server->on('beforeMove', [$this, 'beforeMoveFutureFile']);
}
/**
}
}
}
+
+ /**
+ * Move handler for future file.
+ *
+ * This overrides the default move behavior to prevent Sabre
+ * to delete the target file before moving. Because deleting would
+ * lose the file id and metadata.
+ *
+ * @param string $path source path
+ * @param string $destination destination path
+ * @return bool|void false to stop handling, void to skip this handler
+ */
+ public function beforeMoveFutureFile($path, $destination) {
+ $sourceNode = $this->tree->getNodeForPath($path);
+ if (!$sourceNode instanceof FutureFile) {
+ // skip handling as the source is not a chunked FutureFile
+ return;
+ }
+
+ if (!$this->tree->nodeExists($destination)) {
+ // skip and let the default handler do its work
+ return;
+ }
+
+ // do a move manually, skipping Sabre's default "delete" for existing nodes
+ $this->tree->move($path, $destination);
+
+ // trigger all default events (copied from CorePlugin::move)
+ $this->server->emit('afterMove', [$path, $destination]);
+ $this->server->emit('afterUnbind', [$path]);
+ $this->server->emit('afterBind', [$destination]);
+
+ $response = $this->server->httpResponse;
+ $response->setHeader('Content-Length', '0');
+ $response->setStatus(204);
+
+ return false;
+ }
+
}
use Sabre\HTTP\RequestInterface;
use Sabre\HTTP\ResponseInterface;
use Test\TestCase;
+use OCA\DAV\Upload\FutureFile;
+use OCA\DAV\Connector\Sabre\Directory;
/**
* Copyright (c) 2015 Vincent Petry <pvince81@owncloud.com>
$this->request,
$this->previewManager
);
+
+ $response = $this->getMockBuilder(ResponseInterface::class)
+ ->disableOriginalConstructor()
+ ->getMock();
+ $this->server->httpResponse = $response;
+
$this->plugin->initialize($this->server);
}
$this->assertEquals("false", $propFind->get(self::HAS_PREVIEW_PROPERTYNAME));
}
+
+ public function testBeforeMoveFutureFileSkip() {
+ $node = $this->createMock(Directory::class);
+
+ $this->tree->expects($this->any())
+ ->method('getNodeForPath')
+ ->with('source')
+ ->will($this->returnValue($node));
+ $this->server->httpResponse->expects($this->never())
+ ->method('setStatus');
+
+ $this->assertNull($this->plugin->beforeMoveFutureFile('source', 'target'));
+ }
+
+ public function testBeforeMoveFutureFileSkipNonExisting() {
+ $sourceNode = $this->createMock(FutureFile::class);
+
+ $this->tree->expects($this->any())
+ ->method('getNodeForPath')
+ ->with('source')
+ ->will($this->returnValue($sourceNode));
+ $this->tree->expects($this->any())
+ ->method('nodeExists')
+ ->with('target')
+ ->will($this->returnValue(false));
+ $this->server->httpResponse->expects($this->never())
+ ->method('setStatus');
+
+ $this->assertNull($this->plugin->beforeMoveFutureFile('source', 'target'));
+ }
+
+ public function testBeforeMoveFutureFileMoveIt() {
+ $sourceNode = $this->createMock(FutureFile::class);
+
+ $this->tree->expects($this->any())
+ ->method('getNodeForPath')
+ ->with('source')
+ ->will($this->returnValue($sourceNode));
+ $this->tree->expects($this->any())
+ ->method('nodeExists')
+ ->with('target')
+ ->will($this->returnValue(true));
+ $this->tree->expects($this->once())
+ ->method('move')
+ ->with('source', 'target');
+
+ $this->server->httpResponse->expects($this->once())
+ ->method('setHeader')
+ ->with('Content-Length', '0');
+ $this->server->httpResponse->expects($this->once())
+ ->method('setStatus')
+ ->with(204);
+
+ $this->assertFalse($this->plugin->beforeMoveFutureFile('source', 'target'));
+ }
}