diff options
26 files changed, 750 insertions, 130 deletions
diff --git a/3rdparty b/3rdparty -Subproject b65fc869803ac93e2441f53d8c91bd29fbcd82c +Subproject fc0c1159f4e275186b45f8454aaa89f90718b89 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); diff --git a/apps/files_external/appinfo/database.xml b/apps/files_external/appinfo/database.xml index 2c3615a4d4c..54ee642ead6 100644 --- a/apps/files_external/appinfo/database.xml +++ b/apps/files_external/appinfo/database.xml @@ -79,13 +79,16 @@ <type>text</type> <length>64</length> </field> + <index> + <name>applicable_mount</name> <field> <name>mount_id</name> <sorting>ascending</sorting> </field> </index> <index> + <name>applicable_type_value</name> <field> <name>type</name> <sorting>ascending</sorting> @@ -96,6 +99,7 @@ </field> </index> <index> + <name>applicable_type_value_mount</name> <unique>true</unique> <field> <name>type</name> @@ -112,6 +116,7 @@ </index> </declaration> </table> + <table> <name>*dbprefix*external_config</name> <declaration> @@ -144,12 +149,14 @@ </field> <index> + <name>config_mount</name> <field> <name>mount_id</name> <sorting>ascending</sorting> </field> </index> <index> + <name>config_mount_key</name> <unique>true</unique> <field> <name>mount_id</name> @@ -162,6 +169,7 @@ </index> </declaration> </table> + <table> <name>*dbprefix*external_options</name> <declaration> @@ -194,6 +202,7 @@ </field> <index> + <name>option_mount</name> <field> <name>mount_id</name> <sorting>ascending</sorting> diff --git a/apps/files_external/service/storagesservice.php b/apps/files_external/service/storagesservice.php index 678b91c0109..0a4c00c66ad 100644 --- a/apps/files_external/service/storagesservice.php +++ b/apps/files_external/service/storagesservice.php @@ -104,6 +104,13 @@ abstract class StoragesService { \OCP\Util::ERROR ); return null; + } catch (\InvalidArgumentException $e) { + \OCP\Util::writeLog( + 'files_external', + 'Could not load storage: "' . $e->getMessage() . '"', + \OCP\Util::ERROR + ); + return null; } } diff --git a/apps/files_sharing/api/share20ocs.php b/apps/files_sharing/api/share20ocs.php index 688ed5b3ee5..309e1159fff 100644 --- a/apps/files_sharing/api/share20ocs.php +++ b/apps/files_sharing/api/share20ocs.php @@ -20,6 +20,7 @@ */ namespace OCA\Files_Sharing\API; +use OCP\Files\NotFoundException; use OCP\IGroupManager; use OCP\IUserManager; use OCP\IRequest; @@ -83,6 +84,7 @@ class Share20OCS { * * @param \OCP\Share\IShare $share * @return array + * @throws NotFoundException In case the node can't be resolved. */ protected function formatShare(\OCP\Share\IShare $share) { $sharedBy = $this->userManager->get($share->getSharedBy()); @@ -177,11 +179,15 @@ class Share20OCS { } if ($this->canAccessShare($share)) { - $share = $this->formatShare($share); - return new \OC_OCS_Result([$share]); - } else { - return new \OC_OCS_Result(null, 404, 'wrong share ID, share doesn\'t exist.'); + try { + $share = $this->formatShare($share); + return new \OC_OCS_Result([$share]); + } catch (NotFoundException $e) { + //Fall trough + } } + + return new \OC_OCS_Result(null, 404, 'wrong share ID, share doesn\'t exist.'); } /** @@ -368,7 +374,11 @@ class Share20OCS { $formatted = []; foreach ($shares as $share) { if ($this->canAccessShare($share)) { - $formatted[] = $this->formatShare($share); + try { + $formatted[] = $this->formatShare($share); + } catch (NotFoundException $e) { + // Ignore this share + } } } @@ -398,7 +408,11 @@ class Share20OCS { $formatted = []; foreach ($shares as $share) { - $formatted[] = $this->formatShare($share); + try { + $formatted[] = $this->formatShare($share); + } catch (NotFoundException $e) { + //Ignore this share + } } return new \OC_OCS_Result($formatted); @@ -458,7 +472,11 @@ class Share20OCS { $formatted = []; foreach ($shares as $share) { - $formatted[] = $this->formatShare($share); + try { + $formatted[] = $this->formatShare($share); + } catch (NotFoundException $e) { + //Ignore share + } } return new \OC_OCS_Result($formatted); diff --git a/apps/files_sharing/lib/external/storage.php b/apps/files_sharing/lib/external/storage.php index 783eea43c62..ba7fba654a9 100644 --- a/apps/files_sharing/lib/external/storage.php +++ b/apps/files_sharing/lib/external/storage.php @@ -252,6 +252,9 @@ class Storage extends DAV implements ISharedStorage { if ($e->getCode() === 401 || $e->getCode() === 403) { throw new ForbiddenException(); } + if ($e->getCode() === 404) { + throw new NotFoundException(); + } // throw this to be on the safe side: the share will still be visible // in the UI in case the failure is intermittent, and the user will // be able to decide whether to remove it if it's really gone diff --git a/apps/files_sharing/lib/helper.php b/apps/files_sharing/lib/helper.php index cffdc8887ec..e857974ae74 100644 --- a/apps/files_sharing/lib/helper.php +++ b/apps/files_sharing/lib/helper.php @@ -29,7 +29,9 @@ namespace OCA\Files_Sharing; use OC\Files\Filesystem; +use OC\Files\View; use OCP\Files\NotFoundException; +use OCP\User; class Helper { @@ -78,7 +80,7 @@ class Helper { } try { - $path = \OC\Files\Filesystem::getPath($linkItem['file_source']); + $path = Filesystem::getPath($linkItem['file_source']); } catch (NotFoundException $e) { \OCP\Util::writeLog('share', 'could not resolve linkItem', \OCP\Util::DEBUG); \OC_Response::setStatus(404); @@ -103,8 +105,8 @@ class Helper { $basePath = $path; - if ($relativePath !== null && \OC\Files\Filesystem::isReadable($basePath . $relativePath)) { - $path .= \OC\Files\Filesystem::normalizePath($relativePath); + if ($relativePath !== null && Filesystem::isReadable($basePath . $relativePath)) { + $path .= Filesystem::normalizePath($relativePath); } return array( @@ -168,11 +170,11 @@ class Helper { public static function getSharesFromItem($target) { $result = array(); - $owner = \OC\Files\Filesystem::getOwner($target); - \OC\Files\Filesystem::initMountPoints($owner); - $info = \OC\Files\Filesystem::getFileInfo($target); - $ownerView = new \OC\Files\View('/'.$owner.'/files'); - if ( $owner != \OCP\User::getUser() ) { + $owner = Filesystem::getOwner($target); + Filesystem::initMountPoints($owner); + $info = Filesystem::getFileInfo($target); + $ownerView = new View('/'.$owner.'/files'); + if ( $owner != User::getUser() ) { $path = $ownerView->getPath($info['fileid']); } else { $path = $target; @@ -205,8 +207,34 @@ class Helper { return $result; } + /** + * get the UID of the owner of the file and the path to the file relative to + * owners files folder + * + * @param $filename + * @return array + * @throws \OC\User\NoUserException + */ public static function getUidAndFilename($filename) { - return Filesystem::getView()->getUidAndFilename($filename); + $uid = Filesystem::getOwner($filename); + $userManager = \OC::$server->getUserManager(); + // if the user with the UID doesn't exists, e.g. because the UID points + // to a remote user with a federated cloud ID we use the current logged-in + // user. We need a valid local user to create the share + if (!$userManager->userExists($uid)) { + $uid = User::getUser(); + } + Filesystem::initMountPoints($uid); + if ( $uid != User::getUser() ) { + $info = Filesystem::getFileInfo($filename); + $ownerView = new View('/'.$uid.'/files'); + try { + $filename = $ownerView->getPath($info['fileid']); + } catch (NotFoundException $e) { + $filename = null; + } + } + return [$uid, $filename]; } /** @@ -234,7 +262,7 @@ class Helper { * * @param string $path * @param array $excludeList - * @param \OC\Files\View $view + * @param View $view * @return string $path */ public static function generateUniqueTarget($path, $excludeList, $view) { @@ -244,7 +272,7 @@ class Helper { $dir = $pathinfo['dirname']; $i = 2; while ($view->file_exists($path) || in_array($path, $excludeList)) { - $path = \OC\Files\Filesystem::normalizePath($dir . '/' . $name . ' ('.$i.')' . $ext); + $path = Filesystem::normalizePath($dir . '/' . $name . ' ('.$i.')' . $ext); $i++; } @@ -278,15 +306,15 @@ class Helper { */ public static function getShareFolder() { $shareFolder = \OC::$server->getConfig()->getSystemValue('share_folder', '/'); - $shareFolder = \OC\Files\Filesystem::normalizePath($shareFolder); + $shareFolder = Filesystem::normalizePath($shareFolder); - if (!\OC\Files\Filesystem::file_exists($shareFolder)) { + if (!Filesystem::file_exists($shareFolder)) { $dir = ''; $subdirs = explode('/', $shareFolder); foreach ($subdirs as $subdir) { $dir = $dir . '/' . $subdir; - if (!\OC\Files\Filesystem::is_dir($dir)) { - \OC\Files\Filesystem::mkdir($dir); + if (!Filesystem::is_dir($dir)) { + Filesystem::mkdir($dir); } } } diff --git a/apps/files_sharing/tests/api/share20ocstest.php b/apps/files_sharing/tests/api/share20ocstest.php index a1094ce4b22..44d94868a32 100644 --- a/apps/files_sharing/tests/api/share20ocstest.php +++ b/apps/files_sharing/tests/api/share20ocstest.php @@ -21,6 +21,7 @@ namespace OCA\Files_Sharing\Tests\API; use OCA\Files_Sharing\API\Share20OCS; +use OCP\Files\NotFoundException; use OCP\IGroupManager; use OCP\IUserManager; use OCP\IRequest; @@ -28,6 +29,12 @@ use OCP\IURLGenerator; use OCP\IUser; use OCP\Files\IRootFolder; +/** + * Class Share20OCSTest + * + * @package OCA\Files_Sharing\Tests\API + * @group DB + */ class Share20OCSTest extends \Test\TestCase { /** @var \OC\Share20\Manager | \PHPUnit_Framework_MockObject_MockObject */ @@ -398,6 +405,22 @@ class Share20OCSTest extends \Test\TestCase { $this->assertEquals($expected->getData(), $ocs->getShare($share->getId())->getData()); } + public function testGetShareInvalidNode() { + $share = \OC::$server->getShareManager()->newShare(); + $share->setSharedBy('initiator') + ->setSharedWith('recipient') + ->setShareOwner('owner'); + + $this->shareManager + ->expects($this->once()) + ->method('getShareById') + ->with('ocinternal:42') + ->willReturn($share); + + $expected = new \OC_OCS_Result(null, 404, 'wrong share ID, share doesn\'t exist.'); + $this->assertEquals($expected->getMeta(), $this->ocs->getShare(42)->getMeta()); + } + public function testCanAccessShare() { $share = $this->getMock('OCP\Share\IShare'); $share->method('getShareOwner')->willReturn($this->currentUser->getUID()); @@ -1361,4 +1384,312 @@ class Share20OCSTest extends \Test\TestCase { $this->assertEquals($expected->getMeta(), $result->getMeta()); $this->assertEquals($expected->getData(), $result->getData()); } + + public function dataFormatShare() { + $file = $this->getMock('\OCP\Files\File'); + $folder = $this->getMock('\OCP\Files\Folder'); + $parent = $this->getMock('\OCP\Files\Folder'); + + $file->method('getPath')->willReturn('file'); + $folder->method('getPath')->willReturn('folder'); + + $parent->method('getId')->willReturn(1); + $folder->method('getId')->willReturn(2); + $file->method('getId')->willReturn(3); + + $file->method('getParent')->willReturn($parent); + $folder->method('getParent')->willReturn($parent); + + $cache = $this->getMock('OCP\Files\Cache\ICache'); + $cache->method('getNumericStorageId')->willReturn(100); + $storage = $this->getMock('\OCP\Files\Storage'); + $storage->method('getId')->willReturn('storageId'); + $storage->method('getCache')->willReturn($cache); + + $file->method('getStorage')->willReturn($storage); + $folder->method('getStorage')->willReturn($storage); + + $owner = $this->getMock('\OCP\IUser'); + $owner->method('getDisplayName')->willReturn('ownerDN'); + $initiator = $this->getMock('\OCP\IUser'); + $initiator->method('getDisplayName')->willReturn('initiatorDN'); + $recipient = $this->getMock('\OCP\IUser'); + $recipient->method('getDisplayName')->willReturn('recipientDN'); + + $result = []; + + $share = \OC::$server->getShareManager()->newShare(); + $share->setShareType(\OCP\Share::SHARE_TYPE_USER) + ->setSharedWith('recipient') + ->setSharedBy('initiator') + ->setShareOwner('owner') + ->setPermissions(\OCP\Constants::PERMISSION_READ) + ->setNode($file) + ->setShareTime(new \DateTime('2000-01-01T00:01:02')) + ->setTarget('myTarget') + ->setId(42); + + /* User backend down */ + $result[] = [ + [ + 'id' => 42, + 'share_type' => \OCP\Share::SHARE_TYPE_USER, + 'uid_owner' => 'initiator', + 'displayname_owner' => 'initiator', + 'permissions' => 1, + 'stime' => 946684862, + 'parent' => null, + 'expiration' => null, + 'token' => null, + 'uid_file_owner' => 'owner', + 'displayname_file_owner' => 'owner', + 'path' => 'file', + 'item_type' => 'file', + 'storage_id' => 'storageId', + 'storage' => 100, + 'item_source' => 3, + 'file_source' => 3, + 'file_parent' => 1, + 'file_target' => 'myTarget', + 'share_with' => 'recipient', + 'share_with_displayname' => 'recipient', + 'mail_send' => 0, + ], $share, [], false + ]; + + /* User backend up */ + $result[] = [ + [ + 'id' => 42, + 'share_type' => \OCP\Share::SHARE_TYPE_USER, + 'uid_owner' => 'initiator', + 'displayname_owner' => 'initiatorDN', + 'permissions' => 1, + 'stime' => 946684862, + 'parent' => null, + 'expiration' => null, + 'token' => null, + 'uid_file_owner' => 'owner', + 'displayname_file_owner' => 'ownerDN', + 'path' => 'file', + 'item_type' => 'file', + 'storage_id' => 'storageId', + 'storage' => 100, + 'item_source' => 3, + 'file_source' => 3, + 'file_parent' => 1, + 'file_target' => 'myTarget', + 'share_with' => 'recipient', + 'share_with_displayname' => 'recipientDN', + 'mail_send' => 0, + ], $share, [ + ['owner', $owner], + ['initiator', $initiator], + ['recipient', $recipient], + ], false + ]; + + $share = \OC::$server->getShareManager()->newShare(); + $share->setShareType(\OCP\Share::SHARE_TYPE_USER) + ->setSharedWith('recipient') + ->setSharedBy('initiator') + ->setShareOwner('owner') + ->setPermissions(\OCP\Constants::PERMISSION_READ) + ->setNode($file) + ->setShareTime(new \DateTime('2000-01-01T00:01:02')) + ->setTarget('myTarget') + ->setId(42); + + /* User backend down */ + $result[] = [ + [ + 'id' => 42, + 'share_type' => \OCP\Share::SHARE_TYPE_USER, + 'uid_owner' => 'initiator', + 'displayname_owner' => 'initiator', + 'permissions' => 1, + 'stime' => 946684862, + 'parent' => null, + 'expiration' => null, + 'token' => null, + 'uid_file_owner' => 'owner', + 'displayname_file_owner' => 'owner', + 'path' => 'file', + 'item_type' => 'file', + 'storage_id' => 'storageId', + 'storage' => 100, + 'item_source' => 3, + 'file_source' => 3, + 'file_parent' => 1, + 'file_target' => 'myTarget', + 'share_with' => 'recipient', + 'share_with_displayname' => 'recipient', + 'mail_send' => 0, + ], $share, [], false + ]; + + $share = \OC::$server->getShareManager()->newShare(); + $share->setShareType(\OCP\Share::SHARE_TYPE_GROUP) + ->setSharedWith('recipient') + ->setSharedBy('initiator') + ->setShareOwner('owner') + ->setPermissions(\OCP\Constants::PERMISSION_READ) + ->setNode($file) + ->setShareTime(new \DateTime('2000-01-01T00:01:02')) + ->setTarget('myTarget') + ->setId(42); + + $result[] = [ + [ + 'id' => 42, + 'share_type' => \OCP\Share::SHARE_TYPE_GROUP, + 'uid_owner' => 'initiator', + 'displayname_owner' => 'initiator', + 'permissions' => 1, + 'stime' => 946684862, + 'parent' => null, + 'expiration' => null, + 'token' => null, + 'uid_file_owner' => 'owner', + 'displayname_file_owner' => 'owner', + 'path' => 'file', + 'item_type' => 'file', + 'storage_id' => 'storageId', + 'storage' => 100, + 'item_source' => 3, + 'file_source' => 3, + 'file_parent' => 1, + 'file_target' => 'myTarget', + 'share_with' => 'recipient', + 'share_with_displayname' => 'recipient', + 'mail_send' => 0, + ], $share, [], false + ]; + + $share = \OC::$server->getShareManager()->newShare(); + $share->setShareType(\OCP\Share::SHARE_TYPE_LINK) + ->setSharedBy('initiator') + ->setShareOwner('owner') + ->setPermissions(\OCP\Constants::PERMISSION_READ) + ->setNode($file) + ->setShareTime(new \DateTime('2000-01-01T00:01:02')) + ->setTarget('myTarget') + ->setPassword('mypassword') + ->setExpirationDate(new \DateTime('2001-01-02T00:00:00')) + ->setToken('myToken') + ->setId(42); + + $result[] = [ + [ + 'id' => 42, + 'share_type' => \OCP\Share::SHARE_TYPE_LINK, + 'uid_owner' => 'initiator', + 'displayname_owner' => 'initiator', + 'permissions' => 1, + 'stime' => 946684862, + 'parent' => null, + 'expiration' => '2001-01-02 00:00:00', + 'token' => 'myToken', + 'uid_file_owner' => 'owner', + 'displayname_file_owner' => 'owner', + 'path' => 'file', + 'item_type' => 'file', + 'storage_id' => 'storageId', + 'storage' => 100, + 'item_source' => 3, + 'file_source' => 3, + 'file_parent' => 1, + 'file_target' => 'myTarget', + 'share_with' => 'mypassword', + 'share_with_displayname' => 'mypassword', + 'mail_send' => 0, + 'url' => 'myLink' + ], $share, [], false + ]; + + $share = \OC::$server->getShareManager()->newShare(); + $share->setShareType(\OCP\Share::SHARE_TYPE_REMOTE) + ->setSharedBy('initiator') + ->setSharedWith('user@server.com') + ->setShareOwner('owner') + ->setPermissions(\OCP\Constants::PERMISSION_READ) + ->setNode($folder) + ->setShareTime(new \DateTime('2000-01-01T00:01:02')) + ->setTarget('myTarget') + ->setId(42); + + $result[] = [ + [ + 'id' => 42, + 'share_type' => \OCP\Share::SHARE_TYPE_REMOTE, + 'uid_owner' => 'initiator', + 'displayname_owner' => 'initiator', + 'permissions' => 1, + 'stime' => 946684862, + 'parent' => null, + 'expiration' => null, + 'token' => null, + 'uid_file_owner' => 'owner', + 'displayname_file_owner' => 'owner', + 'path' => 'folder', + 'item_type' => 'folder', + 'storage_id' => 'storageId', + 'storage' => 100, + 'item_source' => 2, + 'file_source' => 2, + 'file_parent' => 1, + 'file_target' => 'myTarget', + 'share_with' => 'user@server.com', + 'share_with_displayname' => 'user@server.com', + 'mail_send' => 0, + ], $share, [], false + ]; + + $share = \OC::$server->getShareManager()->newShare(); + $share->setShareType(\OCP\Share::SHARE_TYPE_USER) + ->setSharedBy('initiator') + ->setSharedWith('recipient') + ->setShareOwner('owner') + ->setPermissions(\OCP\Constants::PERMISSION_READ) + ->setShareTime(new \DateTime('2000-01-01T00:01:02')) + ->setTarget('myTarget') + ->setId(42); + + $result[] = [ + [], $share, [], true + ]; + + + + return $result; + } + + /** + * @dataProvider dataFormatShare + * + * @param array $expects + * @param \OCP\Share\IShare $share + * @param array $users + * @param $exception + */ + public function testFormatShare(array $expects, \OCP\Share\IShare $share, array $users, $exception) { + $this->userManager->method('get')->will($this->returnValueMap($users)); + $this->urlGenerator->method('linkToRouteAbsolute') + ->with('files_sharing.sharecontroller.showShare', ['token' => 'myToken']) + ->willReturn('myLink'); + + + $this->rootFolder->method('getUserFolder')->with($share->getShareOwner())->will($this->returnSelf()); + $this->rootFolder->method('getRelativePath')->will($this->returnArgument(0)); + + try { + $result = $this->invokePrivate($this->ocs, 'formatShare', [$share]); + $this->assertFalse($exception); + $this->assertEquals($expects, $result); + } catch (NotFoundException $e) { + $this->assertTrue($exception); + } + + + } } diff --git a/apps/files_trashbin/lib/trashbin.php b/apps/files_trashbin/lib/trashbin.php index 62d70d7356a..3b386b0abd8 100644 --- a/apps/files_trashbin/lib/trashbin.php +++ b/apps/files_trashbin/lib/trashbin.php @@ -43,6 +43,7 @@ use OC\Files\View; use OCA\Files_Trashbin\AppInfo\Application; use OCA\Files_Trashbin\Command\Expire; use OCP\Files\NotFoundException; +use OCP\User; class Trashbin { @@ -71,12 +72,33 @@ class Trashbin { } /** + * get the UID of the owner of the file and the path to the file relative to + * owners files folder + * * @param string $filename * @return array * @throws \OC\User\NoUserException */ public static function getUidAndFilename($filename) { - return Filesystem::getView()->getUidAndFilename($filename); + $uid = Filesystem::getOwner($filename); + $userManager = \OC::$server->getUserManager(); + // if the user with the UID doesn't exists, e.g. because the UID points + // to a remote user with a federated cloud ID we use the current logged-in + // user. We need a valid local user to move the file to the right trash bin + if (!$userManager->userExists($uid)) { + $uid = User::getUser(); + } + Filesystem::initMountPoints($uid); + if ($uid != User::getUser()) { + $info = Filesystem::getFileInfo($filename); + $ownerView = new View('/' . $uid . '/files'); + try { + $filename = $ownerView->getPath($info['fileid']); + } catch (NotFoundException $e) { + $filename = null; + } + } + return [$uid, $filename]; } /** @@ -120,7 +142,7 @@ class Trashbin { } private static function setUpTrash($user) { - $view = new \OC\Files\View('/' . $user); + $view = new View('/' . $user); if (!$view->is_dir('files_trashbin')) { $view->mkdir('files_trashbin'); } @@ -153,7 +175,7 @@ class Trashbin { $sourceFilename = basename($sourcePath); - $view = new \OC\Files\View('/'); + $view = new View('/'); $target = $user . '/files_trashbin/files/' . $targetFilename . '.d' . $timestamp; $source = $owner . '/files_trashbin/files/' . $sourceFilename . '.d' . $timestamp; @@ -182,7 +204,7 @@ class Trashbin { list(, $user) = explode('/', $root); list($owner, $ownerPath) = self::getUidAndFilename($file_path); - $ownerView = new \OC\Files\View('/' . $owner); + $ownerView = new View('/' . $owner); // file has been deleted in between if (!$ownerView->file_exists('/files/' . $ownerPath)) { return true; @@ -234,8 +256,8 @@ class Trashbin { if (!$result) { \OCP\Util::writeLog('files_trashbin', 'trash bin database couldn\'t be updated', \OCP\Util::ERROR); } - \OCP\Util::emitHook('\OCA\Files_Trashbin\Trashbin', 'post_moveToTrash', array('filePath' => \OC\Files\Filesystem::normalizePath($file_path), - 'trashPath' => \OC\Files\Filesystem::normalizePath($filename . '.d' . $timestamp))); + \OCP\Util::emitHook('\OCA\Files_Trashbin\Trashbin', 'post_moveToTrash', array('filePath' => Filesystem::normalizePath($file_path), + 'trashPath' => Filesystem::normalizePath($filename . '.d' . $timestamp))); self::retainVersions($filename, $owner, $ownerPath, $timestamp); @@ -266,8 +288,8 @@ class Trashbin { private static function retainVersions($filename, $owner, $ownerPath, $timestamp) { if (\OCP\App::isEnabled('files_versions') && !empty($ownerPath)) { - $user = \OCP\User::getUser(); - $rootView = new \OC\Files\View('/'); + $user = User::getUser(); + $rootView = new View('/'); if ($rootView->is_dir($owner . '/files_versions/' . $ownerPath)) { if ($owner !== $user) { @@ -341,8 +363,8 @@ class Trashbin { * @return bool true on success, false otherwise */ public static function restore($file, $filename, $timestamp) { - $user = \OCP\User::getUser(); - $view = new \OC\Files\View('/' . $user); + $user = User::getUser(); + $view = new View('/' . $user); $location = ''; if ($timestamp) { @@ -363,8 +385,8 @@ class Trashbin { // we need a extension in case a file/dir with the same name already exists $uniqueFilename = self::getUniqueFilename($location, $filename, $view); - $source = \OC\Files\Filesystem::normalizePath('files_trashbin/files/' . $file); - $target = \OC\Files\Filesystem::normalizePath('files/' . $location . '/' . $uniqueFilename); + $source = Filesystem::normalizePath('files_trashbin/files/' . $file); + $target = Filesystem::normalizePath('files/' . $location . '/' . $uniqueFilename); if (!$view->file_exists($source)) { return false; } @@ -379,8 +401,8 @@ class Trashbin { $view->chroot('/' . $user . '/files'); $view->touch('/' . $location . '/' . $uniqueFilename, $mtime); $view->chroot($fakeRoot); - \OCP\Util::emitHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', array('filePath' => \OC\Files\Filesystem::normalizePath('/' . $location . '/' . $uniqueFilename), - 'trashPath' => \OC\Files\Filesystem::normalizePath($file))); + \OCP\Util::emitHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', array('filePath' => Filesystem::normalizePath('/' . $location . '/' . $uniqueFilename), + 'trashPath' => Filesystem::normalizePath($file))); self::restoreVersions($view, $file, $filename, $uniqueFilename, $location, $timestamp); @@ -398,7 +420,7 @@ class Trashbin { /** * restore versions from trash bin * - * @param \OC\Files\View $view file view + * @param View $view file view * @param string $file complete path to file * @param string $filename name of file once it was deleted * @param string $uniqueFilename new file name to restore the file without overwriting existing files @@ -406,14 +428,14 @@ class Trashbin { * @param int $timestamp deletion time * @return false|null */ - private static function restoreVersions(\OC\Files\View $view, $file, $filename, $uniqueFilename, $location, $timestamp) { + private static function restoreVersions(View $view, $file, $filename, $uniqueFilename, $location, $timestamp) { if (\OCP\App::isEnabled('files_versions')) { - $user = \OCP\User::getUser(); - $rootView = new \OC\Files\View('/'); + $user = User::getUser(); + $rootView = new View('/'); - $target = \OC\Files\Filesystem::normalizePath('/' . $location . '/' . $uniqueFilename); + $target = Filesystem::normalizePath('/' . $location . '/' . $uniqueFilename); list($owner, $ownerPath) = self::getUidAndFilename($target); @@ -429,7 +451,7 @@ class Trashbin { } if ($view->is_dir('/files_trashbin/versions/' . $file)) { - $rootView->rename(\OC\Files\Filesystem::normalizePath($user . '/files_trashbin/versions/' . $file), \OC\Files\Filesystem::normalizePath($owner . '/files_versions/' . $ownerPath)); + $rootView->rename(Filesystem::normalizePath($user . '/files_trashbin/versions/' . $file), Filesystem::normalizePath($owner . '/files_versions/' . $ownerPath)); } else if ($versions = self::getVersionsFromTrash($versionedFile, $timestamp, $user)) { foreach ($versions as $v) { if ($timestamp) { @@ -446,8 +468,8 @@ class Trashbin { * delete all files from the trash */ public static function deleteAll() { - $user = \OCP\User::getUser(); - $view = new \OC\Files\View('/' . $user); + $user = User::getUser(); + $view = new View('/' . $user); $view->deleteAll('files_trashbin'); $query = \OC_DB::prepare('DELETE FROM `*PREFIX*files_trash` WHERE `user`=?'); $query->execute(array($user)); @@ -467,7 +489,7 @@ class Trashbin { * @return int size of deleted files */ public static function delete($filename, $user, $timestamp = null) { - $view = new \OC\Files\View('/' . $user); + $view = new View('/' . $user); $size = 0; if ($timestamp) { @@ -481,7 +503,7 @@ class Trashbin { $size += self::deleteVersions($view, $file, $filename, $timestamp, $user); if ($view->is_dir('/files_trashbin/files/' . $file)) { - $size += self::calculateSize(new \OC\Files\View('/' . $user . '/files_trashbin/files/' . $file)); + $size += self::calculateSize(new View('/' . $user . '/files_trashbin/files/' . $file)); } else { $size += $view->filesize('/files_trashbin/files/' . $file); } @@ -493,18 +515,18 @@ class Trashbin { } /** - * @param \OC\Files\View $view + * @param View $view * @param string $file * @param string $filename * @param integer|null $timestamp * @param string $user * @return int */ - private static function deleteVersions(\OC\Files\View $view, $file, $filename, $timestamp, $user) { + private static function deleteVersions(View $view, $file, $filename, $timestamp, $user) { $size = 0; if (\OCP\App::isEnabled('files_versions')) { if ($view->is_dir('files_trashbin/versions/' . $file)) { - $size += self::calculateSize(new \OC\Files\view('/' . $user . '/files_trashbin/versions/' . $file)); + $size += self::calculateSize(new View('/' . $user . '/files_trashbin/versions/' . $file)); $view->unlink('files_trashbin/versions/' . $file); } else if ($versions = self::getVersionsFromTrash($filename, $timestamp, $user)) { foreach ($versions as $v) { @@ -529,8 +551,8 @@ class Trashbin { * @return bool true if file exists, otherwise false */ public static function file_exists($filename, $timestamp = null) { - $user = \OCP\User::getUser(); - $view = new \OC\Files\View('/' . $user); + $user = User::getUser(); + $view = new View('/' . $user); if ($timestamp) { $filename = $filename . '.d' . $timestamp; @@ -538,7 +560,7 @@ class Trashbin { $filename = $filename; } - $target = \OC\Files\Filesystem::normalizePath('files_trashbin/files/' . $filename); + $target = Filesystem::normalizePath('files_trashbin/files/' . $filename); return $view->file_exists($target); } @@ -568,7 +590,7 @@ class Trashbin { } $quota = $userObject->getQuota(); if ($quota === null || $quota === 'none') { - $quota = \OC\Files\Filesystem::free_space('/'); + $quota = Filesystem::free_space('/'); $softQuota = false; // inf or unknown free space if ($quota < 0) { @@ -710,11 +732,11 @@ class Trashbin { * * @param string $source source path, relative to the users files directory * @param string $destination destination path relative to the users root directoy - * @param \OC\Files\View $view file view for the users root directory + * @param View $view file view for the users root directory * @return int * @throws Exceptions\CopyRecursiveException */ - private static function copy_recursive($source, $destination, \OC\Files\View $view) { + private static function copy_recursive($source, $destination, View $view) { $size = 0; if ($view->is_dir($source)) { $view->mkdir($destination); @@ -751,7 +773,7 @@ class Trashbin { * @return array */ private static function getVersionsFromTrash($filename, $timestamp, $user) { - $view = new \OC\Files\View('/' . $user . '/files_trashbin/versions'); + $view = new View('/' . $user . '/files_trashbin/versions'); $versions = array(); //force rescan of versions, local storage may not have updated the cache @@ -789,10 +811,10 @@ class Trashbin { * * @param string $location where the file should be restored * @param string $filename name of the file - * @param \OC\Files\View $view filesystem view relative to users root directory + * @param View $view filesystem view relative to users root directory * @return string with unique extension */ - private static function getUniqueFilename($location, $filename, \OC\Files\View $view) { + private static function getUniqueFilename($location, $filename, View $view) { $ext = pathinfo($filename, PATHINFO_EXTENSION); $name = pathinfo($filename, PATHINFO_FILENAME); $l = \OC::$server->getL10N('files_trashbin'); @@ -821,7 +843,7 @@ class Trashbin { /** * get the size from a given root folder * - * @param \OC\Files\View $view file view on the root folder + * @param View $view file view on the root folder * @return integer size of the folder */ private static function calculateSize($view) { @@ -856,7 +878,7 @@ class Trashbin { * @return integer trash bin size */ private static function getTrashbinSize($user) { - $view = new \OC\Files\View('/' . $user); + $view = new View('/' . $user); $fileInfo = $view->getFileInfo('/files_trashbin'); return isset($fileInfo['size']) ? $fileInfo['size'] : 0; } @@ -885,10 +907,10 @@ class Trashbin { */ public static function isEmpty($user) { - $view = new \OC\Files\View('/' . $user . '/files_trashbin'); + $view = new View('/' . $user . '/files_trashbin'); if ($view->is_dir('/files') && $dh = $view->opendir('/files')) { while ($file = readdir($dh)) { - if (!\OC\Files\Filesystem::isIgnoredDir($file)) { + if (!Filesystem::isIgnoredDir($file)) { return false; } } diff --git a/apps/files_versions/lib/storage.php b/apps/files_versions/lib/storage.php index d5e21959698..da736c868fc 100644 --- a/apps/files_versions/lib/storage.php +++ b/apps/files_versions/lib/storage.php @@ -42,9 +42,11 @@ namespace OCA\Files_Versions; use OC\Files\Filesystem; +use OC\Files\View; use OCA\Files_Versions\AppInfo\Application; use OCA\Files_Versions\Command\Expire; use OCP\Lock\ILockingProvider; +use OCP\User; class Storage { @@ -80,12 +82,33 @@ class Storage { private static $application; /** + * get the UID of the owner of the file and the path to the file relative to + * owners files folder + * * @param string $filename * @return array * @throws \OC\User\NoUserException */ public static function getUidAndFilename($filename) { - return Filesystem::getView()->getUidAndFilename($filename); + $uid = Filesystem::getOwner($filename); + $userManager = \OC::$server->getUserManager(); + // if the user with the UID doesn't exists, e.g. because the UID points + // to a remote user with a federated cloud ID we use the current logged-in + // user. We need a valid local user to create the versions + if (!$userManager->userExists($uid)) { + $uid = User::getUser(); + } + Filesystem::initMountPoints($uid); + if ( $uid != User::getUser() ) { + $info = Filesystem::getFileInfo($filename); + $ownerView = new View('/'.$uid.'/files'); + try { + $filename = $ownerView->getPath($info['fileid']); + } catch (NotFoundException $e) { + $filename = null; + } + } + return [$uid, $filename]; } /** @@ -123,7 +146,7 @@ class Storage { * @return int versions size */ private static function getVersionsSize($user) { - $view = new \OC\Files\View('/' . $user); + $view = new View('/' . $user); $fileInfo = $view->getFileInfo('/files_versions'); return isset($fileInfo['size']) ? $fileInfo['size'] : 0; } @@ -148,8 +171,8 @@ class Storage { list($uid, $filename) = self::getUidAndFilename($filename); - $files_view = new \OC\Files\View('/'.$uid .'/files'); - $users_view = new \OC\Files\View('/'.$uid); + $files_view = new View('/'.$uid .'/files'); + $users_view = new View('/'.$uid); // no use making versions for empty files if ($files_view->filesize($filename) === 0) { @@ -189,7 +212,7 @@ class Storage { /** * delete the version from the storage and cache * - * @param \OC\Files\View $view + * @param View $view * @param string $path */ protected static function deleteVersion($view, $path) { @@ -212,9 +235,9 @@ class Storage { $uid = $deletedFile['uid']; $filename = $deletedFile['filename']; - if (!\OC\Files\Filesystem::file_exists($path)) { + if (!Filesystem::file_exists($path)) { - $view = new \OC\Files\View('/' . $uid . '/files_versions'); + $view = new View('/' . $uid . '/files_versions'); $versions = self::getVersions($uid, $filename); if (!empty($versions)) { @@ -252,14 +275,14 @@ class Storage { $sourcePath = ltrim($sourcePath, '/'); $targetPath = ltrim($targetPath, '/'); - $rootView = new \OC\Files\View(''); + $rootView = new View(''); // did we move a directory ? if ($rootView->is_dir('/' . $targetOwner . '/files/' . $targetPath)) { // does the directory exists for versions too ? if ($rootView->is_dir('/' . $sourceOwner . '/files_versions/' . $sourcePath)) { // create missing dirs if necessary - self::createMissingDirectories($targetPath, new \OC\Files\View('/'. $targetOwner)); + self::createMissingDirectories($targetPath, new View('/'. $targetOwner)); // move the directory containing the versions $rootView->$operation( @@ -269,7 +292,7 @@ class Storage { } } else if ($versions = Storage::getVersions($sourceOwner, '/' . $sourcePath)) { // create missing dirs if necessary - self::createMissingDirectories($targetPath, new \OC\Files\View('/'. $targetOwner)); + self::createMissingDirectories($targetPath, new View('/'. $targetOwner)); foreach ($versions as $v) { // move each version one by one to the target directory @@ -299,8 +322,8 @@ class Storage { // add expected leading slash $file = '/' . ltrim($file, '/'); list($uid, $filename) = self::getUidAndFilename($file); - $users_view = new \OC\Files\View('/'.$uid); - $files_view = new \OC\Files\View('/'.\OCP\User::getUser().'/files'); + $users_view = new View('/'.$uid); + $files_view = new View('/'. User::getUser().'/files'); $versionCreated = false; //first create a new version @@ -332,7 +355,7 @@ class Storage { /** * Stream copy file contents from $path1 to $path2 * - * @param \OC\Files\View $view view to use for copying + * @param View $view view to use for copying * @param string $path1 source file to copy * @param string $path2 target file * @@ -381,12 +404,12 @@ class Storage { return $versions; } // fetch for old versions - $view = new \OC\Files\View('/' . $uid . '/'); + $view = new View('/' . $uid . '/'); $pathinfo = pathinfo($filename); $versionedFile = $pathinfo['basename']; - $dir = \OC\Files\Filesystem::normalizePath(self::VERSIONS_ROOT . '/' . $pathinfo['dirname']); + $dir = Filesystem::normalizePath(self::VERSIONS_ROOT . '/' . $pathinfo['dirname']); $dirContent = false; if ($view->is_dir($dir)) { @@ -399,7 +422,7 @@ class Storage { if (is_resource($dirContent)) { while (($entryName = readdir($dirContent)) !== false) { - if (!\OC\Files\Filesystem::isIgnoredDir($entryName)) { + if (!Filesystem::isIgnoredDir($entryName)) { $pathparts = pathinfo($entryName); $filename = $pathparts['filename']; if ($filename === $versionedFile) { @@ -414,7 +437,7 @@ class Storage { } else { $versions[$key]['preview'] = \OCP\Util::linkToRoute('core_ajax_versions_preview', array('file' => $userFullPath, 'version' => $timestamp)); } - $versions[$key]['path'] = \OC\Files\Filesystem::normalizePath($pathinfo['dirname'] . '/' . $filename); + $versions[$key]['path'] = Filesystem::normalizePath($pathinfo['dirname'] . '/' . $filename); $versions[$key]['name'] = $versionedFile; $versions[$key]['size'] = $view->filesize($dir . '/' . $entryName); } @@ -451,7 +474,7 @@ class Storage { } } - $view = new \OC\Files\View('/' . $uid . '/files_versions'); + $view = new View('/' . $uid . '/files_versions'); if (!empty($toDelete)) { foreach ($toDelete as $version) { \OC_Hook::emit('\OCP\Versions', 'preDelete', array('path' => $version['path'].'.v'.$version['version'], 'trigger' => self::DELETE_TRIGGER_RETENTION_CONSTRAINT)); @@ -494,7 +517,7 @@ class Storage { * @return array with contains two arrays 'all' which contains all versions sorted by age and 'by_file' which contains all versions sorted by filename */ private static function getAllVersions($uid) { - $view = new \OC\Files\View('/' . $uid . '/'); + $view = new View('/' . $uid . '/'); $dirs = array(self::VERSIONS_ROOT); $versions = array(); @@ -655,14 +678,14 @@ class Storage { // file maybe renamed or deleted return false; } - $versionsFileview = new \OC\Files\View('/'.$uid.'/files_versions'); + $versionsFileview = new View('/'.$uid.'/files_versions'); // get available disk space for user $user = \OC::$server->getUserManager()->get($uid); $softQuota = true; $quota = $user->getQuota(); if ( $quota === null || $quota === 'none' ) { - $quota = \OC\Files\Filesystem::free_space('/'); + $quota = Filesystem::free_space('/'); $softQuota = false; } else { $quota = \OCP\Util::computerFileSize($quota); @@ -675,7 +698,7 @@ class Storage { // subtract size of files and current versions size from quota if ($quota >= 0) { if ($softQuota) { - $files_view = new \OC\Files\View('/' . $uid . '/files'); + $files_view = new View('/' . $uid . '/files'); $rootInfo = $files_view->getFileInfo('/', false); $free = $quota - $rootInfo['size']; // remaining free space for user if ($free > 0) { @@ -752,10 +775,10 @@ class Storage { * * @param string $filename $path to a file, relative to the user's * "files" folder - * @param \OC\Files\View $view view on data/user/ + * @param View $view view on data/user/ */ private static function createMissingDirectories($filename, $view) { - $dirname = \OC\Files\Filesystem::normalizePath(dirname($filename)); + $dirname = Filesystem::normalizePath(dirname($filename)); $dirParts = explode('/', $dirname); $dir = "/files_versions"; foreach ($dirParts as $part) { diff --git a/core/ajax/share.php b/core/ajax/share.php index 987b637cc97..93a7e911c42 100644 --- a/core/ajax/share.php +++ b/core/ajax/share.php @@ -254,7 +254,7 @@ if (isset($_POST['action']) && isset($_POST['itemType']) && isset($_POST['itemSo if (isset($_GET['itemShares'][OCP\Share::SHARE_TYPE_GROUP]) && is_array($_GET['itemShares'][OCP\Share::SHARE_TYPE_GROUP])) { - $sharedGroups = isset($_GET['itemShares'][OCP\Share::SHARE_TYPE_GROUP]); + $sharedGroups = $_GET['itemShares'][OCP\Share::SHARE_TYPE_GROUP]; } } diff --git a/lib/private/db/mdb2schemareader.php b/lib/private/db/mdb2schemareader.php index 6f99206e5c8..94c48d61f06 100644 --- a/lib/private/db/mdb2schemareader.php +++ b/lib/private/db/mdb2schemareader.php @@ -30,6 +30,7 @@ namespace OC\DB; use Doctrine\DBAL\Platforms\AbstractPlatform; +use Doctrine\DBAL\Schema\SchemaConfig; use OCP\IConfig; class MDB2SchemaReader { @@ -48,6 +49,9 @@ class MDB2SchemaReader { */ protected $platform; + /** @var \Doctrine\DBAL\Schema\SchemaConfig $schemaConfig */ + protected $schemaConfig; + /** * @param \OCP\IConfig $config * @param \Doctrine\DBAL\Platforms\AbstractPlatform $platform @@ -56,6 +60,12 @@ class MDB2SchemaReader { $this->platform = $platform; $this->DBNAME = $config->getSystemValue('dbname', 'owncloud'); $this->DBTABLEPREFIX = $config->getSystemValue('dbtableprefix', 'oc_'); + + // Oracle does not support longer index names then 30 characters. + // We use this limit for all DBs to make sure it does not cause a + // problem. + $this->schemaConfig = new SchemaConfig(); + $this->schemaConfig->setMaxIdentifierLength(30); } /** @@ -107,6 +117,7 @@ class MDB2SchemaReader { $name = $this->platform->quoteIdentifier($name); $table = $schema->createTable($name); $table->addOption('collate', 'utf8_bin'); + $table->setSchemaConfig($this->schemaConfig); break; case 'create': case 'overwrite': diff --git a/lib/private/files/filesystem.php b/lib/private/files/filesystem.php index 9d4a2c0aa05..d6fc9648c1e 100644 --- a/lib/private/files/filesystem.php +++ b/lib/private/files/filesystem.php @@ -237,9 +237,9 @@ class Filesystem { * * @return \OC\Files\Mount\Manager */ - public static function getMountManager() { + public static function getMountManager($user = '') { if (!self::$mounts) { - \OC_Util::setupFS(); + \OC_Util::setupFS($user); } return self::$mounts; } diff --git a/lib/private/files/node/root.php b/lib/private/files/node/root.php index 40ed531d5df..a9343e72220 100644 --- a/lib/private/files/node/root.php +++ b/lib/private/files/node/root.php @@ -71,7 +71,7 @@ class Root extends Folder implements IRootFolder { /** * @param \OC\Files\Mount\Manager $manager * @param \OC\Files\View $view - * @param \OC\User\User $user + * @param \OC\User\User|null $user */ public function __construct($manager, $view, $user) { parent::__construct($this, $view, ''); diff --git a/lib/private/files/storage/common.php b/lib/private/files/storage/common.php index 3d5898dcd80..edc570c967d 100644 --- a/lib/private/files/storage/common.php +++ b/lib/private/files/storage/common.php @@ -72,6 +72,7 @@ abstract class Common implements Storage, ILockingStorage { protected $updater; protected $mountOptions = []; + protected $owner = null; public function __construct($parameters) { } @@ -383,7 +384,11 @@ abstract class Common implements Storage, ILockingStorage { * @return string|false uid or false */ public function getOwner($path) { - return \OC_User::getUser(); + if ($this->owner === null) { + $this->owner = \OC_User::getUser(); + } + + return $this->owner; } /** diff --git a/lib/private/files/storage/wrapper/quota.php b/lib/private/files/storage/wrapper/quota.php index 844505679df..500677b092e 100644 --- a/lib/private/files/storage/wrapper/quota.php +++ b/lib/private/files/storage/wrapper/quota.php @@ -141,17 +141,34 @@ class Quota extends Wrapper { */ public function fopen($path, $mode) { $source = $this->storage->fopen($path, $mode); - $free = $this->free_space(''); - if ($source && $free >= 0 && $mode !== 'r' && $mode !== 'rb') { - // only apply quota for files, not metadata, trash or others - if (strpos(ltrim($path, '/'), 'files/') === 0) { - return \OC\Files\Stream\Quota::wrap($source, $free); + + // don't apply quota for part files + if (!$this->isPartFile($path)) { + $free = $this->free_space(''); + if ($source && $free >= 0 && $mode !== 'r' && $mode !== 'rb') { + // only apply quota for files, not metadata, trash or others + if (strpos(ltrim($path, '/'), 'files/') === 0) { + return \OC\Files\Stream\Quota::wrap($source, $free); + } } } return $source; } /** + * Checks whether the given path is a part file + * + * @param string $path Path that may identify a .part file + * @return string File path without .part extension + * @note this is needed for reusing keys + */ + private function isPartFile($path) { + $extension = pathinfo($path, PATHINFO_EXTENSION); + + return ($extension === 'part'); + } + + /** * @param \OCP\Files\Storage $sourceStorage * @param string $sourceInternalPath * @param string $targetInternalPath diff --git a/lib/private/server.php b/lib/private/server.php index ef4764d07cf..7c8ecce71a5 100644 --- a/lib/private/server.php +++ b/lib/private/server.php @@ -40,7 +40,6 @@ namespace OC; use bantu\IniGetWrapper\IniGetWrapper; use OC\AppFramework\Http\Request; use OC\AppFramework\Db\Db; -use OC\AppFramework\Utility\SimpleContainer; use OC\AppFramework\Utility\TimeFactory; use OC\Command\AsyncBus; use OC\Diagnostics\EventLogger; @@ -164,15 +163,10 @@ class Server extends ServerContainer implements IServerContainer { $this->registerService('SystemTagObjectMapper', function (Server $c) { return $c->query('SystemTagManagerFactory')->getObjectMapper(); }); - $this->registerService('RootFolder', function (Server $c) { - // TODO: get user and user manager from container as well - $user = \OC_User::getUser(); - /** @var $c SimpleContainer */ - $userManager = $c->query('UserManager'); - $user = $userManager->get($user); - $manager = \OC\Files\Filesystem::getMountManager(); + $this->registerService('RootFolder', function () { + $manager = \OC\Files\Filesystem::getMountManager(null); $view = new View(); - $root = new Root($manager, $view, $user); + $root = new Root($manager, $view, null); $connector = new HookConnector($root, $view); $connector->viewToNode(); return $root; diff --git a/lib/private/util.php b/lib/private/util.php index 6ad668dedaa..35ae3e3090b 100644 --- a/lib/private/util.php +++ b/lib/private/util.php @@ -128,7 +128,9 @@ class OC_Util { \OC::$server->getEventLogger()->start('setup_fs', 'Setup filesystem'); // If we are not forced to load a specific user we load the one that is logged in - if ($user == "" && OC_User::isLoggedIn()) { + if ($user === null) { + $user = ''; + } else if ($user == "" && OC_User::isLoggedIn()) { $user = OC_User::getUser(); } diff --git a/lib/public/appframework/http/redirectresponse.php b/lib/public/appframework/http/redirectresponse.php index 7208012295f..bb0c8843715 100644 --- a/lib/public/appframework/http/redirectresponse.php +++ b/lib/public/appframework/http/redirectresponse.php @@ -44,7 +44,7 @@ class RedirectResponse extends Response { */ public function __construct($redirectURL) { $this->redirectURL = $redirectURL; - $this->setStatus(Http::STATUS_TEMPORARY_REDIRECT); + $this->setStatus(Http::STATUS_SEE_OTHER); $this->addHeader('Location', $redirectURL); } diff --git a/settings/js/apps.js b/settings/js/apps.js index b6f6363a992..a99df65e3c1 100644 --- a/settings/js/apps.js +++ b/settings/js/apps.js @@ -124,6 +124,7 @@ OC.Settings.Apps = OC.Settings.Apps || { $('.app-level .experimental').tipsy({fallback: t('settings', 'This app is not checked for security issues and is new or known to be unstable. Install at your own risk.')}); }, complete: function() { + var availableUpdates = 0; $('#apps-list').removeClass('icon-loading'); $.ajax(OC.generateUrl('settings/apps/list?category={categoryId}&includeUpdateInfo=1', { categoryId: categoryId @@ -135,8 +136,14 @@ OC.Settings.Apps = OC.Settings.Apps || { var $update = $('#app-' + app.id + ' .update'); $update.removeClass('hidden'); $update.val(t('settings', 'Update to %s').replace(/%s/g, app.update)); + availableUpdates++; + OC.Settings.Apps.State.apps[app.id].update = true; } - }) + }); + + if (availableUpdates > 0) { + OC.Notification.show(n('settings', 'You have %n app update pending', 'You have %n app updates pending', availableUpdates)); + } } }); } diff --git a/settings/templates/apps.php b/settings/templates/apps.php index 489062d5191..ef89a5393cd 100644 --- a/settings/templates/apps.php +++ b/settings/templates/apps.php @@ -97,6 +97,10 @@ script( <div class="app-description-toggle-show"><?php p($l->t("Show description …"));?></div> <div class="app-description-toggle-hide hidden"><?php p($l->t("Hide description …"));?></div> + <div class="app-dependencies update hidden"> + <p><?php p($l->t('This app has an update available.')); ?></p> + </div> + {{#if missingMinOwnCloudVersion}} <div class="app-dependencies"> <p><?php p($l->t('This app has no minimum ownCloud version assigned. This will be an error in ownCloud 11 and later.')); ?></p> diff --git a/tests/lib/appframework/http/RedirectResponseTest.php b/tests/lib/appframework/http/RedirectResponseTest.php index 17db0c0be6c..723f6600c55 100644 --- a/tests/lib/appframework/http/RedirectResponseTest.php +++ b/tests/lib/appframework/http/RedirectResponseTest.php @@ -43,7 +43,7 @@ class RedirectResponseTest extends \Test\TestCase { public function testHeaders() { $headers = $this->response->getHeaders(); $this->assertEquals('/url', $headers['Location']); - $this->assertEquals(Http::STATUS_TEMPORARY_REDIRECT, + $this->assertEquals(Http::STATUS_SEE_OTHER, $this->response->getStatus()); } diff --git a/version.php b/version.php index 240a92cc0b7..dfe5d68300c 100644 --- a/version.php +++ b/version.php @@ -25,10 +25,10 @@ // We only can count up. The 4. digit is only for the internal patchlevel to trigger DB upgrades // between betas, final and RCs. This is _not_ the public version number. Reset minor/patchlevel // when updating major/minor version number. -$OC_Version = array(9, 0, 0, 11); +$OC_Version = array(9, 0, 0, 12); // The human readable string -$OC_VersionString = '9.0 beta 1'; +$OC_VersionString = '9.0.0 beta 2'; $OC_VersionCanBeUpgradedFrom = array(8, 2); |