summaryrefslogtreecommitdiffstats
path: root/apps
diff options
context:
space:
mode:
authorThomas Müller <thomas.mueller@tmit.eu>2016-02-18 14:25:32 +0100
committerThomas Müller <thomas.mueller@tmit.eu>2016-02-18 14:25:32 +0100
commit6c4cdddba85a18bee0bd62d971891ce92e0d2a2d (patch)
treeb60bc6754d6ebf258be44144c4537947ee0026a2 /apps
parentcdb1aea809dd0b14271dd4e38ebdb18996dfbff2 (diff)
parentceaefc2c54f4a5d4528bf2e7ba4607297d2453f4 (diff)
downloadnextcloud-server-6c4cdddba85a18bee0bd62d971891ce92e0d2a2d.tar.gz
nextcloud-server-6c4cdddba85a18bee0bd62d971891ce92e0d2a2d.zip
Merge pull request #21610 from owncloud/quota-sharedfile
Fix quota check for single shared files
Diffstat (limited to 'apps')
-rw-r--r--apps/dav/lib/connector/sabre/quotaplugin.php17
-rw-r--r--apps/dav/tests/unit/connector/sabre/quotaplugin.php109
-rw-r--r--apps/files/ajax/upload.php8
-rw-r--r--apps/files/js/file-upload.js43
4 files changed, 158 insertions, 19 deletions
diff --git a/apps/dav/lib/connector/sabre/quotaplugin.php b/apps/dav/lib/connector/sabre/quotaplugin.php
index a02827da499..b1c3bbfbbb9 100644
--- a/apps/dav/lib/connector/sabre/quotaplugin.php
+++ b/apps/dav/lib/connector/sabre/quotaplugin.php
@@ -95,12 +95,14 @@ class QuotaPlugin extends \Sabre\DAV\ServerPlugin {
$req = $this->server->httpRequest;
if ($req->getHeader('OC-Chunked')) {
$info = \OC_FileChunking::decodeName($newName);
- $chunkHandler = new \OC_FileChunking($info);
+ $chunkHandler = $this->getFileChunking($info);
// subtract the already uploaded size to see whether
// there is still enough space for the remaining chunks
$length -= $chunkHandler->getCurrentSize();
+ // use target file name for free space check in case of shared files
+ $uri = rtrim($parentUri, '/') . '/' . $info['name'];
}
- $freeSpace = $this->getFreeSpace($parentUri);
+ $freeSpace = $this->getFreeSpace($uri);
if ($freeSpace !== \OCP\Files\FileInfo::SPACE_UNKNOWN && $length > $freeSpace) {
if (isset($chunkHandler)) {
$chunkHandler->cleanup();
@@ -111,6 +113,11 @@ class QuotaPlugin extends \Sabre\DAV\ServerPlugin {
return true;
}
+ public function getFileChunking($info) {
+ // FIXME: need a factory for better mocking support
+ return new \OC_FileChunking($info);
+ }
+
public function getLength() {
$req = $this->server->httpRequest;
$length = $req->getHeader('X-Expected-Entity-Length');
@@ -127,12 +134,12 @@ class QuotaPlugin extends \Sabre\DAV\ServerPlugin {
}
/**
- * @param string $parentUri
+ * @param string $uri
* @return mixed
*/
- public function getFreeSpace($parentUri) {
+ public function getFreeSpace($uri) {
try {
- $freeSpace = $this->view->free_space($parentUri);
+ $freeSpace = $this->view->free_space(ltrim($uri, '/'));
return $freeSpace;
} catch (\OCP\Files\StorageNotAvailableException $e) {
throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage());
diff --git a/apps/dav/tests/unit/connector/sabre/quotaplugin.php b/apps/dav/tests/unit/connector/sabre/quotaplugin.php
index cc4339ecc1a..b5a8bfef31c 100644
--- a/apps/dav/tests/unit/connector/sabre/quotaplugin.php
+++ b/apps/dav/tests/unit/connector/sabre/quotaplugin.php
@@ -39,10 +39,13 @@ class QuotaPlugin extends \Test\TestCase {
*/
private $plugin;
- private function init($quota) {
- $view = $this->buildFileViewMock($quota);
+ private function init($quota, $checkedPath = '') {
+ $view = $this->buildFileViewMock($quota, $checkedPath);
$this->server = new \Sabre\DAV\Server();
- $this->plugin = new \OCA\DAV\Connector\Sabre\QuotaPlugin($view);
+ $this->plugin = $this->getMockBuilder('\OCA\DAV\Connector\Sabre\QuotaPlugin')
+ ->setConstructorArgs([$view])
+ ->setMethods(['getFileChunking'])
+ ->getMock();
$this->plugin->initialize($this->server);
}
@@ -51,6 +54,8 @@ class QuotaPlugin extends \Test\TestCase {
*/
public function testLength($expected, $headers) {
$this->init(0);
+ $this->plugin->expects($this->never())
+ ->method('getFileChunking');
$this->server->httpRequest = new \Sabre\HTTP\Request(null, null, $headers);
$length = $this->plugin->getLength();
$this->assertEquals($expected, $length);
@@ -61,6 +66,8 @@ class QuotaPlugin extends \Test\TestCase {
*/
public function testCheckQuota($quota, $headers) {
$this->init($quota);
+ $this->plugin->expects($this->never())
+ ->method('getFileChunking');
$this->server->httpRequest = new \Sabre\HTTP\Request(null, null, $headers);
$result = $this->plugin->checkQuota('');
@@ -73,11 +80,26 @@ class QuotaPlugin extends \Test\TestCase {
*/
public function testCheckExceededQuota($quota, $headers) {
$this->init($quota);
+ $this->plugin->expects($this->never())
+ ->method('getFileChunking');
$this->server->httpRequest = new \Sabre\HTTP\Request(null, null, $headers);
$this->plugin->checkQuota('');
}
+ /**
+ * @dataProvider quotaOkayProvider
+ */
+ public function testCheckQuotaOnPath($quota, $headers) {
+ $this->init($quota, 'sub/test.txt');
+ $this->plugin->expects($this->never())
+ ->method('getFileChunking');
+
+ $this->server->httpRequest = new \Sabre\HTTP\Request(null, null, $headers);
+ $result = $this->plugin->checkQuota('/sub/test.txt');
+ $this->assertTrue($result);
+ }
+
public function quotaOkayProvider() {
return array(
array(1024, array()),
@@ -110,12 +132,89 @@ class QuotaPlugin extends \Test\TestCase {
);
}
- private function buildFileViewMock($quota) {
+ public function quotaChunkedOkProvider() {
+ return array(
+ array(1024, 0, array('X-EXPECTED-ENTITY-LENGTH' => '1024')),
+ array(1024, 0, array('CONTENT-LENGTH' => '512')),
+ array(1024, 0, array('OC-TOTAL-LENGTH' => '1024', 'CONTENT-LENGTH' => '512')),
+ // with existing chunks (allowed size = total length - chunk total size)
+ array(400, 128, array('X-EXPECTED-ENTITY-LENGTH' => '512')),
+ array(400, 128, array('CONTENT-LENGTH' => '512')),
+ array(400, 128, array('OC-TOTAL-LENGTH' => '512', 'CONTENT-LENGTH' => '500')),
+ // \OCP\Files\FileInfo::SPACE-UNKNOWN = -2
+ array(-2, 0, array('X-EXPECTED-ENTITY-LENGTH' => '1024')),
+ array(-2, 0, array('CONTENT-LENGTH' => '512')),
+ array(-2, 0, array('OC-TOTAL-LENGTH' => '1024', 'CONTENT-LENGTH' => '512')),
+ array(-2, 128, array('X-EXPECTED-ENTITY-LENGTH' => '1024')),
+ array(-2, 128, array('CONTENT-LENGTH' => '512')),
+ array(-2, 128, array('OC-TOTAL-LENGTH' => '1024', 'CONTENT-LENGTH' => '512')),
+ );
+ }
+
+ /**
+ * @dataProvider quotaChunkedOkProvider
+ */
+ public function testCheckQuotaChunkedOk($quota, $chunkTotalSize, $headers) {
+ $this->init($quota, 'sub/test.txt');
+
+ $mockChunking = $this->getMockBuilder('\OC_FileChunking')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $mockChunking->expects($this->once())
+ ->method('getCurrentSize')
+ ->will($this->returnValue($chunkTotalSize));
+
+ $this->plugin->expects($this->once())
+ ->method('getFileChunking')
+ ->will($this->returnValue($mockChunking));
+
+ $headers['OC-CHUNKED'] = 1;
+ $this->server->httpRequest = new \Sabre\HTTP\Request(null, null, $headers);
+ $result = $this->plugin->checkQuota('/sub/test.txt-chunking-12345-3-1');
+ $this->assertTrue($result);
+ }
+
+ public function quotaChunkedFailProvider() {
+ return array(
+ array(400, 0, array('X-EXPECTED-ENTITY-LENGTH' => '1024')),
+ array(400, 0, array('CONTENT-LENGTH' => '512')),
+ array(400, 0, array('OC-TOTAL-LENGTH' => '1024', 'CONTENT-LENGTH' => '512')),
+ // with existing chunks (allowed size = total length - chunk total size)
+ array(380, 128, array('X-EXPECTED-ENTITY-LENGTH' => '512')),
+ array(380, 128, array('CONTENT-LENGTH' => '512')),
+ array(380, 128, array('OC-TOTAL-LENGTH' => '512', 'CONTENT-LENGTH' => '500')),
+ );
+ }
+
+ /**
+ * @dataProvider quotaChunkedFailProvider
+ * @expectedException \Sabre\DAV\Exception\InsufficientStorage
+ */
+ public function testCheckQuotaChunkedFail($quota, $chunkTotalSize, $headers) {
+ $this->init($quota, 'sub/test.txt');
+
+ $mockChunking = $this->getMockBuilder('\OC_FileChunking')
+ ->disableOriginalConstructor()
+ ->getMock();
+ $mockChunking->expects($this->once())
+ ->method('getCurrentSize')
+ ->will($this->returnValue($chunkTotalSize));
+
+ $this->plugin->expects($this->once())
+ ->method('getFileChunking')
+ ->will($this->returnValue($mockChunking));
+
+ $headers['OC-CHUNKED'] = 1;
+ $this->server->httpRequest = new \Sabre\HTTP\Request(null, null, $headers);
+ $this->plugin->checkQuota('/sub/test.txt-chunking-12345-3-1');
+ }
+
+ private function buildFileViewMock($quota, $checkedPath) {
// mock filesysten
$view = $this->getMock('\OC\Files\View', array('free_space'), array(), '', false);
$view->expects($this->any())
->method('free_space')
- ->with($this->identicalTo(''))
+ ->with($this->identicalTo($checkedPath))
->will($this->returnValue($quota));
return $view;
diff --git a/apps/files/ajax/upload.php b/apps/files/ajax/upload.php
index 5bf69d3e304..36aaed5ad9e 100644
--- a/apps/files/ajax/upload.php
+++ b/apps/files/ajax/upload.php
@@ -136,8 +136,12 @@ $maxUploadFileSize = $storageStats['uploadMaxFilesize'];
$maxHumanFileSize = OCP\Util::humanFileSize($maxUploadFileSize);
$totalSize = 0;
-foreach ($files['size'] as $size) {
- $totalSize += $size;
+$isReceivedShare = \OC::$server->getRequest()->getParam('isReceivedShare', false) === 'true';
+// defer quota check for received shares
+if (!$isReceivedShare) {
+ foreach ($files['size'] as $size) {
+ $totalSize += $size;
+ }
}
if ($maxUploadFileSize >= 0 and $totalSize > $maxUploadFileSize) {
OCP\JSON::error(array('data' => array('message' => $l->t('Not enough storage available'),
diff --git a/apps/files/js/file-upload.js b/apps/files/js/file-upload.js
index d419cb3a2c0..8ba294e2a7f 100644
--- a/apps/files/js/file-upload.js
+++ b/apps/files/js/file-upload.js
@@ -251,7 +251,26 @@ OC.Upload = {
$('#file_upload_start').trigger(new $.Event('resized'));
},
+ /**
+ * Returns whether the given file is known to be a received shared file
+ *
+ * @param {Object} file file
+ * @return {bool} true if the file is a shared file
+ */
+ _isReceivedSharedFile: function(file) {
+ if (!window.FileList) {
+ return false;
+ }
+ var $tr = window.FileList.findFileEl(file.name);
+ if (!$tr.length) {
+ return false;
+ }
+
+ return ($tr.attr('data-mounttype') === 'shared-root' && $tr.attr('data-mime') !== 'httpd/unix-directory');
+ },
+
init: function() {
+ var self = this;
if ( $('#file_upload_start').exists() ) {
var file_upload_param = {
dropZone: $('#content'), // restrict dropZone to content div
@@ -341,10 +360,15 @@ OC.Upload = {
}
}
- // add size
- selection.totalBytes += file.size;
- // update size of biggest file
- selection.biggestFileBytes = Math.max(selection.biggestFileBytes, file.size);
+ // only count if we're not overwriting an existing shared file
+ if (self._isReceivedSharedFile(file)) {
+ file.isReceivedShare = true;
+ } else {
+ // add size
+ selection.totalBytes += file.size;
+ // update size of biggest file
+ selection.biggestFileBytes = Math.max(selection.biggestFileBytes, file.size);
+ }
// check PHP upload limit against biggest file
if (selection.biggestFileBytes > $('#upload_limit').val()) {
@@ -430,11 +454,16 @@ OC.Upload = {
fileDirectory = data.files[0].relativePath;
}
- addFormData(data.formData, {
+ var params = {
requesttoken: oc_requesttoken,
dir: data.targetDir || FileList.getCurrentDirectory(),
- file_directory: fileDirectory
- });
+ file_directory: fileDirectory,
+ };
+ if (data.files[0].isReceivedShare) {
+ params.isReceivedShare = true;
+ }
+
+ addFormData(data.formData, params);
},
fail: function(e, data) {
OC.Upload.log('fail', e, data);