From 81761b87e4d29989329b398d4f797f0ccb47b144 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Fri, 10 Jun 2016 11:03:19 +0200 Subject: DAV now returns file name with Content-Disposition header --- apps/dav/lib/connector/sabre/filesplugin.php | 29 +++++++++++++++++++--- apps/dav/lib/connector/sabre/serverfactory.php | 6 ++++- apps/dav/lib/server.php | 6 ++++- .../dav/tests/unit/connector/sabre/filesplugin.php | 13 ++++++++-- .../unit/connector/sabre/filesreportplugin.php | 6 ++++- build/integration/features/webdav-related.feature | 4 +-- 6 files changed, 53 insertions(+), 11 deletions(-) diff --git a/apps/dav/lib/connector/sabre/filesplugin.php b/apps/dav/lib/connector/sabre/filesplugin.php index bf9bee02578..857c98247bd 100644 --- a/apps/dav/lib/connector/sabre/filesplugin.php +++ b/apps/dav/lib/connector/sabre/filesplugin.php @@ -76,16 +76,26 @@ class FilesPlugin extends \Sabre\DAV\ServerPlugin { */ private $fileView; + /** + * @var IRequest + */ + private $request; + /** * @param \Sabre\DAV\Tree $tree * @param \OC\Files\View $view + * @param \OCP\IRequest $request * @param bool $isPublic */ - public function __construct(\Sabre\DAV\Tree $tree, - \OC\Files\View $view, - $isPublic = false) { + public function __construct( + \Sabre\DAV\Tree $tree, + \OC\Files\View $view, + \OCP\IRequest $request, + $isPublic = false + ) { $this->tree = $tree; $this->fileView = $view; + $this->request = $request; $this->isPublic = $isPublic; } @@ -193,7 +203,18 @@ class FilesPlugin extends \Sabre\DAV\ServerPlugin { if (!($node instanceof IFile)) return; // adds a 'Content-Disposition: attachment' header - $response->addHeader('Content-Disposition', 'attachment'); + $filename = $node->getName(); + if ($this->request->isUserAgent( + [ + \OC\AppFramework\Http\Request::USER_AGENT_IE, + \OC\AppFramework\Http\Request::USER_AGENT_ANDROID_MOBILE_CHROME, + \OC\AppFramework\Http\Request::USER_AGENT_FREEBOX, + ])) { + $response->addHeader('Content-Disposition', 'attachment; filename="' . rawurlencode($filename) . '"'); + } else { + $response->addHeader('Content-Disposition', 'attachment; filename*=UTF-8\'\'' . rawurlencode($filename) + . '; filename="' . rawurlencode($filename) . '"'); + } if ($node instanceof \OCA\DAV\Connector\Sabre\File) { //Add OC-Checksum header diff --git a/apps/dav/lib/connector/sabre/serverfactory.php b/apps/dav/lib/connector/sabre/serverfactory.php index c0b45c36a00..8462f624552 100644 --- a/apps/dav/lib/connector/sabre/serverfactory.php +++ b/apps/dav/lib/connector/sabre/serverfactory.php @@ -137,7 +137,11 @@ class ServerFactory { } $objectTree->init($root, $view, $this->mountManager); - $server->addPlugin(new \OCA\DAV\Connector\Sabre\FilesPlugin($objectTree, $view)); + $server->addPlugin(new \OCA\DAV\Connector\Sabre\FilesPlugin( + $objectTree, + $view, + $this->request + )); $server->addPlugin(new \OCA\DAV\Connector\Sabre\QuotaPlugin($view)); if($this->userSession->isLoggedIn()) { diff --git a/apps/dav/lib/server.php b/apps/dav/lib/server.php index 4711a80800d..791bd1ba7e8 100644 --- a/apps/dav/lib/server.php +++ b/apps/dav/lib/server.php @@ -125,7 +125,11 @@ class Server { $user = \OC::$server->getUserSession()->getUser(); if (!is_null($user)) { $view = \OC\Files\Filesystem::getView(); - $this->server->addPlugin(new FilesPlugin($this->server->tree, $view)); + $this->server->addPlugin(new FilesPlugin( + $this->server->tree, + $view, + $this->request + )); $this->server->addPlugin( new \Sabre\DAV\PropertyStorage\Plugin( diff --git a/apps/dav/tests/unit/connector/sabre/filesplugin.php b/apps/dav/tests/unit/connector/sabre/filesplugin.php index 0a790ec6fc9..ecad56d004d 100644 --- a/apps/dav/tests/unit/connector/sabre/filesplugin.php +++ b/apps/dav/tests/unit/connector/sabre/filesplugin.php @@ -72,8 +72,13 @@ class FilesPlugin extends \Test\TestCase { $this->view = $this->getMockBuilder('\OC\Files\View') ->disableOriginalConstructor() ->getMock(); + $request = $this->getMock('\OCP\IRequest'); - $this->plugin = new \OCA\DAV\Connector\Sabre\FilesPlugin($this->tree, $this->view); + $this->plugin = new \OCA\DAV\Connector\Sabre\FilesPlugin( + $this->tree, + $this->view, + $request + ); $this->plugin->initialize($this->server); } @@ -237,7 +242,11 @@ class FilesPlugin extends \Test\TestCase { } public function testGetPublicPermissions() { - $this->plugin = new \OCA\DAV\Connector\Sabre\FilesPlugin($this->tree, $this->view, true); + $this->plugin = new \OCA\DAV\Connector\Sabre\FilesPlugin( + $this->tree, + $this->view, + $this->getMock('\OCP\IRequest'), + true); $this->plugin->initialize($this->server); $propFind = new \Sabre\DAV\PropFind( diff --git a/apps/dav/tests/unit/connector/sabre/filesreportplugin.php b/apps/dav/tests/unit/connector/sabre/filesreportplugin.php index 87973ef0071..c17cc0f30a3 100644 --- a/apps/dav/tests/unit/connector/sabre/filesreportplugin.php +++ b/apps/dav/tests/unit/connector/sabre/filesreportplugin.php @@ -336,7 +336,11 @@ class FilesReportPlugin extends \Test\TestCase { ->method('getSize') ->will($this->returnValue(1024)); - $this->server->addPlugin(new \OCA\DAV\Connector\Sabre\FilesPlugin($this->tree, $this->view)); + $this->server->addPlugin(new \OCA\DAV\Connector\Sabre\FilesPlugin( + $this->tree, + $this->view, + $this->getMock('\OCP\IRequest') + )); $this->plugin->initialize($this->server); $responses = $this->plugin->prepareResponses($requestedProps, [$node1, $node2]); diff --git a/build/integration/features/webdav-related.feature b/build/integration/features/webdav-related.feature index ee841f9eb5b..45a36499251 100644 --- a/build/integration/features/webdav-related.feature +++ b/build/integration/features/webdav-related.feature @@ -73,7 +73,7 @@ Feature: webdav-related And As an "admin" When Downloading file "/welcome.txt" Then The following headers should be set - |Content-Disposition|attachment| + |Content-Disposition|attachment; filename*=UTF-8''welcome.txt; filename="welcome.txt"| |Content-Security-Policy|default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; frame-src *; img-src * data: blob:; font-src 'self' data:; media-src *; connect-src *| |X-Content-Type-Options |nosniff| |X-Download-Options|noopen| @@ -88,7 +88,7 @@ Feature: webdav-related And As an "admin" When Downloading file "/welcome.txt" Then The following headers should be set - |Content-Disposition|attachment| + |Content-Disposition|attachment; filename*=UTF-8''welcome.txt; filename="welcome.txt"| |Content-Security-Policy|default-src 'self'; script-src 'self' 'unsafe-eval'; style-src 'self' 'unsafe-inline'; frame-src *; img-src * data: blob:; font-src 'self' data:; media-src *; connect-src *| |X-Content-Type-Options |nosniff| |X-Download-Options|noopen| -- cgit v1.2.3 From 9e3cf794ea9af6bd0138e48ca9e1cb68a9157019 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Mon, 13 Jun 2016 17:47:57 +0200 Subject: Use capped cache for encryption's user access list --- lib/private/encryption/file.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/lib/private/encryption/file.php b/lib/private/encryption/file.php index ec55c2cea00..bd045c20294 100644 --- a/lib/private/encryption/file.php +++ b/lib/private/encryption/file.php @@ -22,6 +22,8 @@ namespace OC\Encryption; +use OC\Cache\CappedMemoryCache; + class File implements \OCP\Encryption\IFile { /** @var Util */ @@ -36,6 +38,7 @@ class File implements \OCP\Encryption\IFile { public function __construct(Util $util) { $this->util = $util; + $this->cache = new CappedMemoryCache(); } -- cgit v1.2.3 From 3e846a4821de32396bc33120633b85abc47e2373 Mon Sep 17 00:00:00 2001 From: Thomas Müller Date: Tue, 14 Jun 2016 12:11:35 +0200 Subject: Use an explicit version of sabre/dav to allow caching on the jenkins slaves - fixes #25087 (#25089) --- build/integration/composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build/integration/composer.json b/build/integration/composer.json index a9516391a41..29f73f2064a 100644 --- a/build/integration/composer.json +++ b/build/integration/composer.json @@ -4,6 +4,6 @@ "behat/behat": "^3.0", "guzzlehttp/guzzle": "~5.0", "jarnaiz/behat-junit-formatter": "^1.3", - "sabre/dav": "3.0.x-dev" + "sabre/dav": "3.0.9" } } -- cgit v1.2.3 From a0215b191d91501835e8387fb791143a596864bc Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Tue, 7 Jun 2016 11:40:04 +0200 Subject: decrease initial users to load to 50 Prevents timeouts on the initial loading of users. proper fix will be in https://github.com/owncloud/core/pull/10994 Workaround for https://github.com/owncloud/core/issues/24734 --- settings/js/users/users.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/settings/js/users/users.js b/settings/js/users/users.js index 02d3a25be70..eb041054cdc 100644 --- a/settings/js/users/users.js +++ b/settings/js/users/users.js @@ -14,7 +14,7 @@ var UserList = { availableGroups: [], offset: 0, usersToLoad: 10, //So many users will be loaded when user scrolls down - initialUsersToLoad: 250, //initial number of users to load + initialUsersToLoad: 50, //initial number of users to load currentGid: '', filter: '', -- cgit v1.2.3 From ea330dfa6a249985422728a66ccc2a7722a2223f Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Fri, 10 Jun 2016 11:16:32 +0200 Subject: Allow empty host when installing on oracle via CLI (#25034) --- core/command/maintenance/install.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/core/command/maintenance/install.php b/core/command/maintenance/install.php index b1b63b9b3bd..12a61d6341a 100644 --- a/core/command/maintenance/install.php +++ b/core/command/maintenance/install.php @@ -106,7 +106,12 @@ class Install extends Command { $dbUser = $input->getOption('database-user'); $dbPass = $input->getOption('database-pass'); $dbName = $input->getOption('database-name'); - $dbHost = $input->getOption('database-host'); + if ($db === 'oci') { + // an empty hostname needs to be read from the raw parameters + $dbHost = $input->getParameterOption('--database-host', ''); + } else { + $dbHost = $input->getOption('database-host'); + } $dbTablePrefix = 'oc_'; if ($input->hasParameterOption('--database-table-prefix')) { $dbTablePrefix = (string) $input->getOption('database-table-prefix'); -- cgit v1.2.3 From a9c98daffb0e6633b3050c1944e6d6ca652abd9f Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Wed, 15 Jun 2016 13:02:39 +0200 Subject: Capped cache for cache info in UserMountCache --- lib/private/files/config/usermountcache.php | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/lib/private/files/config/usermountcache.php b/lib/private/files/config/usermountcache.php index 78b19972787..76281ef62e1 100644 --- a/lib/private/files/config/usermountcache.php +++ b/lib/private/files/config/usermountcache.php @@ -35,6 +35,7 @@ use OCP\IDBConnection; use OCP\ILogger; use OCP\IUser; use OCP\IUserManager; +use OC\Cache\CappedMemoryCache; /** * Cache mounts points per user in the cache so we can easilly look them up @@ -50,15 +51,23 @@ class UserMountCache implements IUserMountCache { */ private $userManager; - /** @var ICachedMountInfo[][] [$userId => [$cachedMountInfo, ....], ...] */ - private $mountsForUsers = []; + /** + * Cached mount info. + * Map of $userId to ICachedMountInfo. + * + * @var ICache + **/ + private $mountsForUsers; /** * @var ILogger */ private $logger; - private $cacheInfoCache = []; + /** + * @var ICache + */ + private $cacheInfoCache; /** * UserMountCache constructor. @@ -71,6 +80,8 @@ class UserMountCache implements IUserMountCache { $this->connection = $connection; $this->userManager = $userManager; $this->logger = $logger; + $this->cacheInfoCache = new CappedMemoryCache(); + $this->mountsForUsers = new CappedMemoryCache(); } public function registerMounts(IUser $user, array $mounts) { -- cgit v1.2.3 From 2656f68d608fafa422be144c0aeccaff9af25a7e Mon Sep 17 00:00:00 2001 From: Christoph Wurst Date: Wed, 15 Jun 2016 21:37:01 +0200 Subject: load authentication apps first --- ocs/v1.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/ocs/v1.php b/ocs/v1.php index 32fcfdd46d1..74b6ac13895 100644 --- a/ocs/v1.php +++ b/ocs/v1.php @@ -43,6 +43,8 @@ use Symfony\Component\Routing\Exception\ResourceNotFoundException; use Symfony\Component\Routing\Exception\MethodNotAllowedException; try { + OC_App::loadApps(['session']); + OC_App::loadApps(['authentication']); // load all apps to get all api routes properly setup OC_App::loadApps(); -- cgit v1.2.3 From 416d4c5141bc83970b94926f85e2423a60ba22a2 Mon Sep 17 00:00:00 2001 From: Jan-Christoph Borchardt Date: Thu, 16 Jun 2016 11:43:57 +0200 Subject: fix grouped input fields, make sure they take precedence --- core/css/styles.css | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/core/css/styles.css b/core/css/styles.css index a429d55560e..46607388a2f 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -365,26 +365,26 @@ body { } #body-login .grouptop input, .grouptop input { - margin-bottom: 0; - border-bottom: 0; - border-bottom-left-radius: 0; - border-bottom-right-radius: 0; + margin-bottom: 0 !important; + border-bottom: 0 !important; + border-bottom-left-radius: 0 !important; + border-bottom-right-radius: 0 !important; } #body-login .groupmiddle input, .groupmiddle input { - margin-top: 0; - margin-bottom: 0; - border-top: 0; - border-bottom: 0; - border-radius: 0; + margin-top: 0 !important; + margin-bottom: 0 !important; + border-top: 0 !important; + border-bottom: 0 !important; + border-radius: 0 !important; box-shadow: 0 1px 0 rgba(0,0,0,.1) inset !important; } #body-login .groupbottom input, .groupbottom input { - margin-top: 0; - border-top: 0; - border-top-right-radius: 0; - border-top-left-radius: 0; + margin-top: 0 !important; + border-top: 0 !important; + border-top-right-radius: 0 !important; + border-top-left-radius: 0 !important; box-shadow: 0 1px 0 rgba(0,0,0,.1) inset !important; } #body-login .groupbottom input[type=submit] { -- cgit v1.2.3 From 5e17e992b0f2848969ee53369c6bcd7cf52d4dfa Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Thu, 16 Jun 2016 16:05:14 +0200 Subject: Convert Dropbox Forbidden exception to StorageNotAvailableException --- apps/files_external/lib/dropbox.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/files_external/lib/dropbox.php b/apps/files_external/lib/dropbox.php index 8381ccbae59..bfddc98f5a3 100644 --- a/apps/files_external/lib/dropbox.php +++ b/apps/files_external/lib/dropbox.php @@ -32,6 +32,7 @@ namespace OC\Files\Storage; use GuzzleHttp\Exception\RequestException; use Icewind\Streams\IteratorDirectory; use Icewind\Streams\RetryWrapper; +use OCP\Files\StorageNotAvailableException; require_once __DIR__ . '/../3rdparty/Dropbox/autoload.php'; @@ -94,6 +95,8 @@ class Dropbox extends \OC\Files\Storage\Common { if ($list) { try { $response = $this->dropbox->getMetaData($path); + } catch (\Dropbox_Exception_Forbidden $e) { + throw new StorageNotAvailableException('Dropbox API rate limit exceeded', StorageNotAvailableException::STATUS_ERROR, $e); } catch (\Exception $exception) { \OCP\Util::writeLog('files_external', $exception->getMessage(), \OCP\Util::ERROR); return false; @@ -127,6 +130,8 @@ class Dropbox extends \OC\Files\Storage\Common { return $response; } return null; + } catch (\Dropbox_Exception_Forbidden $e) { + throw new StorageNotAvailableException('Dropbox API rate limit exceeded', StorageNotAvailableException::STATUS_ERROR, $e); } catch (\Exception $exception) { if ($exception instanceof \Dropbox_Exception_NotFound) { // don't log, might be a file_exist check -- cgit v1.2.3 From 3ac02c9032ee1b28f55c937e61c7f920e682649b Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Thu, 16 Jun 2016 17:17:02 +0200 Subject: –emit correct signal when disabling an app MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/private/app/appmanager.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/private/app/appmanager.php b/lib/private/app/appmanager.php index fba5bb19f61..608c69dd54a 100644 --- a/lib/private/app/appmanager.php +++ b/lib/private/app/appmanager.php @@ -256,7 +256,7 @@ class AppManager implements IAppManager { } unset($this->installedAppsCache[$appId]); $this->appConfig->setValue($appId, 'enabled', 'no'); - $this->dispatcher->dispatch(ManagerEvent::EVENT_APP_ENABLE, new ManagerEvent( + $this->dispatcher->dispatch(ManagerEvent::EVENT_APP_DISABLE, new ManagerEvent( ManagerEvent::EVENT_APP_DISABLE, $appId )); $this->clearAppsCache(); -- cgit v1.2.3 From 2ce078e7c3ffe3e3901f8ae7f44c8741ef5fa370 Mon Sep 17 00:00:00 2001 From: Daniel Molkentin Date: Thu, 16 Jun 2016 18:23:42 +0200 Subject: ownCloud 9.0.3 RC1 --- version.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version.php b/version.php index c90e84a79c7..5e9923f9572 100644 --- a/version.php +++ b/version.php @@ -26,10 +26,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, 2, 2); +$OC_Version = array(9, 0, 3, 1); // The human readable string -$OC_VersionString = '9.0.2'; +$OC_VersionString = '9.0.3 RC1'; $OC_VersionCanBeUpgradedFrom = array(8, 2); -- cgit v1.2.3 From b5d3e877f1554a261da9d5a1fe0381f30607815b Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Fri, 17 Jun 2016 10:06:08 +0200 Subject: Remove shares of the root folder (#25138) --- lib/private/repair.php | 2 + lib/private/repair/removerootshares.php | 145 ++++++++++++++++++++++ tests/lib/repair/removerootsharestest.php | 194 ++++++++++++++++++++++++++++++ 3 files changed, 341 insertions(+) create mode 100644 lib/private/repair/removerootshares.php create mode 100644 tests/lib/repair/removerootsharestest.php diff --git a/lib/private/repair.php b/lib/private/repair.php index 098d4e7eb93..b2a1e5a0705 100644 --- a/lib/private/repair.php +++ b/lib/private/repair.php @@ -48,6 +48,7 @@ use OC\Repair\RepairMimeTypes; use OC\Repair\SearchLuceneTables; use OC\Repair\UpdateOutdatedOcsIds; use OC\Repair\RepairInvalidShares; +use OC\Repair\RemoveRootShares; class Repair extends BasicEmitter { /** @@ -118,6 +119,7 @@ class Repair extends BasicEmitter { new UpdateOutdatedOcsIds(\OC::$server->getConfig()), new RepairInvalidShares(\OC::$server->getConfig(), \OC::$server->getDatabaseConnection()), new AvatarPermissions(\OC::$server->getDatabaseConnection()), + new RemoveRootShares(\OC::$server->getDatabaseConnection(), \OC::$server->getUserManager(), \OC::$server->getRootFolder()), ]; } diff --git a/lib/private/repair/removerootshares.php b/lib/private/repair/removerootshares.php new file mode 100644 index 00000000000..bf7061c0838 --- /dev/null +++ b/lib/private/repair/removerootshares.php @@ -0,0 +1,145 @@ + + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ +namespace OC\Repair; + +use OCP\Files\IRootFolder; +use OCP\IDBConnection; +use OCP\IUser; +use OCP\IUserManager; +use OCP\Migration\IOutput; + +/** + * Class RemoveRootShares + * + * @package OC\Repair + */ +class RemoveRootShares implements \OC\RepairStep { + + /** @var IDBConnection */ + protected $connection; + + /** @var IUserManager */ + protected $userManager; + + /** @var IRootFolder */ + protected $rootFolder; + + /** + * RemoveRootShares constructor. + * + * @param IDBConnection $connection + * @param IUserManager $userManager + * @param IRootFolder $rootFolder + */ + public function __construct(IDBConnection $connection, + IUserManager $userManager, + IRootFolder $rootFolder) { + $this->connection = $connection; + $this->userManager = $userManager; + $this->rootFolder = $rootFolder; + } + + /** + * @return string + */ + public function getName() { + return 'Remove shares of a users root folder'; + } + + public function run() { + if ($this->rootSharesExist()) { + $this->removeRootShares(); + } + } + + private function removeRootShares() { + $function = function(IUser $user) { + $userFolder = $this->rootFolder->getUserFolder($user->getUID()); + $fileId = $userFolder->getId(); + + $qb = $this->connection->getQueryBuilder(); + $qb->delete('share') + ->where($qb->expr()->eq('file_source', $qb->createNamedParameter($fileId))) + ->andWhere($qb->expr()->orX( + $qb->expr()->eq('item_type', $qb->expr()->literal('file')), + $qb->expr()->eq('item_type', $qb->expr()->literal('folder')) + )); + + $qb->execute(); + }; + + $userCount = $this->countUsers(); + + $this->userManager->callForAllUsers($function); + } + + /** + * Count all the users + * + * @return int + */ + private function countUsers() { + $allCount = $this->userManager->countUsers(); + + $totalCount = 0; + foreach ($allCount as $backend => $count) { + $totalCount += $count; + } + + return $totalCount; + } + + /** + * Verify if this repair steps is required + * It *should* not be necessary in most cases and it can be very + * costly. + * + * @return bool + */ + private function rootSharesExist() { + $qb = $this->connection->getQueryBuilder(); + $qb2 = $this->connection->getQueryBuilder(); + + $qb->select('fileid') + ->from('filecache') + ->where($qb->expr()->eq('path', $qb->expr()->literal('files'))); + + $qb2->select('id') + ->from('share') + ->where($qb2->expr()->in('file_source', $qb2->createFunction($qb->getSQL()))) + ->andWhere($qb2->expr()->orX( + $qb2->expr()->eq('item_type', $qb->expr()->literal('file')), + $qb2->expr()->eq('item_type', $qb->expr()->literal('folder')) + )) + ->setMaxResults(1); + + $cursor = $qb2->execute(); + $data = $cursor->fetch(); + $cursor->closeCursor(); + + if ($data === false) { + return false; + } + + return true; + } +} + diff --git a/tests/lib/repair/removerootsharestest.php b/tests/lib/repair/removerootsharestest.php new file mode 100644 index 00000000000..bf255fc7e9b --- /dev/null +++ b/tests/lib/repair/removerootsharestest.php @@ -0,0 +1,194 @@ + + * + * @copyright Copyright (c) 2016, ownCloud, Inc. + * @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 + * + */ +namespace Test\Repair; + +use OC\Repair\RemoveRootShares; +use OCP\Files\IRootFolder; +use OCP\IDBConnection; +use OCP\IUserManager; +use OCP\Migration\IOutput; +use Test\Traits\UserTrait; + +/** + * Class RemoveOldSharesTest + * + * @package Test\Repair + * @group DB + */ +class RemoveRootSharesTest extends \Test\TestCase { + use UserTrait; + + /** @var RemoveRootShares */ + protected $repair; + + /** @var IDBConnection */ + protected $connection; + + /** @var IOutput */ + private $outputMock; + + /** @var IUserManager */ + private $userManager; + + /** @var IRootFolder */ + private $rootFolder; + + protected function setUp() { + parent::setUp(); + + $this->outputMock = $this->getMockBuilder('\OCP\Migration\IOutput') + ->disableOriginalConstructor() + ->getMock(); + + $this->userManager = \OC::$server->getUserManager(); + $this->rootFolder = \OC::$server->getRootFolder(); + + $this->connection = \OC::$server->getDatabaseConnection(); + $this->repair = new RemoveRootShares($this->connection, $this->userManager, $this->rootFolder); + } + + protected function tearDown() { + $qb = $this->connection->getQueryBuilder(); + $qb->delete('share'); + $qb->execute(); + + return parent::tearDown(); + } + + public function testRootSharesExist() { + //Add test user + $user = $this->userManager->createUser('test', 'test'); + $userFolder = $this->rootFolder->getUserFolder('test'); + $fileId = $userFolder->getId(); + + //Now insert cyclic share + $qb = $this->connection->getQueryBuilder(); + $qb->insert('share') + ->values([ + 'share_type' => $qb->createNamedParameter(0), + 'share_with' => $qb->createNamedParameter('foo'), + 'uid_owner' => $qb->createNamedParameter('owner'), + 'item_type' => $qb->createNamedParameter('file'), + 'item_source' => $qb->createNamedParameter($fileId), + 'item_target' => $qb->createNamedParameter('/target'), + 'file_source' => $qb->createNamedParameter($fileId), + 'file_target' => $qb->createNamedParameter('/target'), + 'permissions' => $qb->createNamedParameter(1), + ]); + $qb->execute(); + + $res = $this->invokePrivate($this->repair, 'rootSharesExist', []); + $this->assertTrue($res); + + $user->delete(); + } + + public function testRootSharesDontExist() { + //Add test user + $user = $this->userManager->createUser('test', 'test'); + $userFolder = $this->rootFolder->getUserFolder('test'); + $fileId = $userFolder->getId(); + + //Now insert cyclic share + $qb = $this->connection->getQueryBuilder(); + $qb->insert('share') + ->values([ + 'share_type' => $qb->createNamedParameter(0), + 'share_with' => $qb->createNamedParameter('foo'), + 'uid_owner' => $qb->createNamedParameter('owner'), + 'item_type' => $qb->createNamedParameter('file'), + 'item_source' => $qb->createNamedParameter($fileId+1), + 'item_target' => $qb->createNamedParameter('/target'), + 'file_source' => $qb->createNamedParameter($fileId+1), + 'file_target' => $qb->createNamedParameter('/target'), + 'permissions' => $qb->createNamedParameter(1), + ]); + $qb->execute(); + + $res = $this->invokePrivate($this->repair, 'rootSharesExist', []); + $this->assertFalse($res); + + $user->delete(); + } + + public function testRun() { + //Add test user + $user1 = $this->userManager->createUser('test1', 'test1'); + $userFolder = $this->rootFolder->getUserFolder('test1'); + $fileId = $userFolder->getId(); + + //Now insert cyclic share + $qb = $this->connection->getQueryBuilder(); + $qb->insert('share') + ->values([ + 'share_type' => $qb->createNamedParameter(0), + 'share_with' => $qb->createNamedParameter('foo'), + 'uid_owner' => $qb->createNamedParameter('owner'), + 'item_type' => $qb->createNamedParameter('file'), + 'item_source' => $qb->createNamedParameter($fileId), + 'item_target' => $qb->createNamedParameter('/target'), + 'file_source' => $qb->createNamedParameter($fileId), + 'file_target' => $qb->createNamedParameter('/target'), + 'permissions' => $qb->createNamedParameter(1), + ]); + $qb->execute(); + + //Add test user + $user2 = $this->userManager->createUser('test2', 'test2'); + $userFolder = $this->rootFolder->getUserFolder('test2'); + $folder = $userFolder->newFolder('foo'); + $fileId = $folder->getId(); + + //Now insert cyclic share + $qb = $this->connection->getQueryBuilder(); + $qb->insert('share') + ->values([ + 'share_type' => $qb->createNamedParameter(0), + 'share_with' => $qb->createNamedParameter('foo'), + 'uid_owner' => $qb->createNamedParameter('owner'), + 'item_type' => $qb->createNamedParameter('file'), + 'item_source' => $qb->createNamedParameter($fileId), + 'item_target' => $qb->createNamedParameter('/target'), + 'file_source' => $qb->createNamedParameter($fileId), + 'file_target' => $qb->createNamedParameter('/target'), + 'permissions' => $qb->createNamedParameter(1), + ]); + $qb->execute(); + + $this->repair->run($this->outputMock); + + //Verify + $qb = $this->connection->getQueryBuilder(); + $qb->selectAlias($qb->createFunction('COUNT(*)'), 'count') + ->from('share'); + + $cursor = $qb->execute(); + $data = $cursor->fetch(); + $cursor->closeCursor(); + + $count = (int)$data['count']; + + $this->assertEquals(1, $count); + + $user1->delete(); + $user2->delete(); + } +} -- cgit v1.2.3 From 7aa825f7dce21791f17dbfe7a516f9958d06102e Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Fri, 17 Jun 2016 11:08:26 +0200 Subject: Revert "[stable9] Remove shares of the root folder" (#25157) --- lib/private/repair.php | 2 - lib/private/repair/removerootshares.php | 145 ---------------------- tests/lib/repair/removerootsharestest.php | 194 ------------------------------ 3 files changed, 341 deletions(-) delete mode 100644 lib/private/repair/removerootshares.php delete mode 100644 tests/lib/repair/removerootsharestest.php diff --git a/lib/private/repair.php b/lib/private/repair.php index b2a1e5a0705..098d4e7eb93 100644 --- a/lib/private/repair.php +++ b/lib/private/repair.php @@ -48,7 +48,6 @@ use OC\Repair\RepairMimeTypes; use OC\Repair\SearchLuceneTables; use OC\Repair\UpdateOutdatedOcsIds; use OC\Repair\RepairInvalidShares; -use OC\Repair\RemoveRootShares; class Repair extends BasicEmitter { /** @@ -119,7 +118,6 @@ class Repair extends BasicEmitter { new UpdateOutdatedOcsIds(\OC::$server->getConfig()), new RepairInvalidShares(\OC::$server->getConfig(), \OC::$server->getDatabaseConnection()), new AvatarPermissions(\OC::$server->getDatabaseConnection()), - new RemoveRootShares(\OC::$server->getDatabaseConnection(), \OC::$server->getUserManager(), \OC::$server->getRootFolder()), ]; } diff --git a/lib/private/repair/removerootshares.php b/lib/private/repair/removerootshares.php deleted file mode 100644 index bf7061c0838..00000000000 --- a/lib/private/repair/removerootshares.php +++ /dev/null @@ -1,145 +0,0 @@ - - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ -namespace OC\Repair; - -use OCP\Files\IRootFolder; -use OCP\IDBConnection; -use OCP\IUser; -use OCP\IUserManager; -use OCP\Migration\IOutput; - -/** - * Class RemoveRootShares - * - * @package OC\Repair - */ -class RemoveRootShares implements \OC\RepairStep { - - /** @var IDBConnection */ - protected $connection; - - /** @var IUserManager */ - protected $userManager; - - /** @var IRootFolder */ - protected $rootFolder; - - /** - * RemoveRootShares constructor. - * - * @param IDBConnection $connection - * @param IUserManager $userManager - * @param IRootFolder $rootFolder - */ - public function __construct(IDBConnection $connection, - IUserManager $userManager, - IRootFolder $rootFolder) { - $this->connection = $connection; - $this->userManager = $userManager; - $this->rootFolder = $rootFolder; - } - - /** - * @return string - */ - public function getName() { - return 'Remove shares of a users root folder'; - } - - public function run() { - if ($this->rootSharesExist()) { - $this->removeRootShares(); - } - } - - private function removeRootShares() { - $function = function(IUser $user) { - $userFolder = $this->rootFolder->getUserFolder($user->getUID()); - $fileId = $userFolder->getId(); - - $qb = $this->connection->getQueryBuilder(); - $qb->delete('share') - ->where($qb->expr()->eq('file_source', $qb->createNamedParameter($fileId))) - ->andWhere($qb->expr()->orX( - $qb->expr()->eq('item_type', $qb->expr()->literal('file')), - $qb->expr()->eq('item_type', $qb->expr()->literal('folder')) - )); - - $qb->execute(); - }; - - $userCount = $this->countUsers(); - - $this->userManager->callForAllUsers($function); - } - - /** - * Count all the users - * - * @return int - */ - private function countUsers() { - $allCount = $this->userManager->countUsers(); - - $totalCount = 0; - foreach ($allCount as $backend => $count) { - $totalCount += $count; - } - - return $totalCount; - } - - /** - * Verify if this repair steps is required - * It *should* not be necessary in most cases and it can be very - * costly. - * - * @return bool - */ - private function rootSharesExist() { - $qb = $this->connection->getQueryBuilder(); - $qb2 = $this->connection->getQueryBuilder(); - - $qb->select('fileid') - ->from('filecache') - ->where($qb->expr()->eq('path', $qb->expr()->literal('files'))); - - $qb2->select('id') - ->from('share') - ->where($qb2->expr()->in('file_source', $qb2->createFunction($qb->getSQL()))) - ->andWhere($qb2->expr()->orX( - $qb2->expr()->eq('item_type', $qb->expr()->literal('file')), - $qb2->expr()->eq('item_type', $qb->expr()->literal('folder')) - )) - ->setMaxResults(1); - - $cursor = $qb2->execute(); - $data = $cursor->fetch(); - $cursor->closeCursor(); - - if ($data === false) { - return false; - } - - return true; - } -} - diff --git a/tests/lib/repair/removerootsharestest.php b/tests/lib/repair/removerootsharestest.php deleted file mode 100644 index bf255fc7e9b..00000000000 --- a/tests/lib/repair/removerootsharestest.php +++ /dev/null @@ -1,194 +0,0 @@ - - * - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @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 - * - */ -namespace Test\Repair; - -use OC\Repair\RemoveRootShares; -use OCP\Files\IRootFolder; -use OCP\IDBConnection; -use OCP\IUserManager; -use OCP\Migration\IOutput; -use Test\Traits\UserTrait; - -/** - * Class RemoveOldSharesTest - * - * @package Test\Repair - * @group DB - */ -class RemoveRootSharesTest extends \Test\TestCase { - use UserTrait; - - /** @var RemoveRootShares */ - protected $repair; - - /** @var IDBConnection */ - protected $connection; - - /** @var IOutput */ - private $outputMock; - - /** @var IUserManager */ - private $userManager; - - /** @var IRootFolder */ - private $rootFolder; - - protected function setUp() { - parent::setUp(); - - $this->outputMock = $this->getMockBuilder('\OCP\Migration\IOutput') - ->disableOriginalConstructor() - ->getMock(); - - $this->userManager = \OC::$server->getUserManager(); - $this->rootFolder = \OC::$server->getRootFolder(); - - $this->connection = \OC::$server->getDatabaseConnection(); - $this->repair = new RemoveRootShares($this->connection, $this->userManager, $this->rootFolder); - } - - protected function tearDown() { - $qb = $this->connection->getQueryBuilder(); - $qb->delete('share'); - $qb->execute(); - - return parent::tearDown(); - } - - public function testRootSharesExist() { - //Add test user - $user = $this->userManager->createUser('test', 'test'); - $userFolder = $this->rootFolder->getUserFolder('test'); - $fileId = $userFolder->getId(); - - //Now insert cyclic share - $qb = $this->connection->getQueryBuilder(); - $qb->insert('share') - ->values([ - 'share_type' => $qb->createNamedParameter(0), - 'share_with' => $qb->createNamedParameter('foo'), - 'uid_owner' => $qb->createNamedParameter('owner'), - 'item_type' => $qb->createNamedParameter('file'), - 'item_source' => $qb->createNamedParameter($fileId), - 'item_target' => $qb->createNamedParameter('/target'), - 'file_source' => $qb->createNamedParameter($fileId), - 'file_target' => $qb->createNamedParameter('/target'), - 'permissions' => $qb->createNamedParameter(1), - ]); - $qb->execute(); - - $res = $this->invokePrivate($this->repair, 'rootSharesExist', []); - $this->assertTrue($res); - - $user->delete(); - } - - public function testRootSharesDontExist() { - //Add test user - $user = $this->userManager->createUser('test', 'test'); - $userFolder = $this->rootFolder->getUserFolder('test'); - $fileId = $userFolder->getId(); - - //Now insert cyclic share - $qb = $this->connection->getQueryBuilder(); - $qb->insert('share') - ->values([ - 'share_type' => $qb->createNamedParameter(0), - 'share_with' => $qb->createNamedParameter('foo'), - 'uid_owner' => $qb->createNamedParameter('owner'), - 'item_type' => $qb->createNamedParameter('file'), - 'item_source' => $qb->createNamedParameter($fileId+1), - 'item_target' => $qb->createNamedParameter('/target'), - 'file_source' => $qb->createNamedParameter($fileId+1), - 'file_target' => $qb->createNamedParameter('/target'), - 'permissions' => $qb->createNamedParameter(1), - ]); - $qb->execute(); - - $res = $this->invokePrivate($this->repair, 'rootSharesExist', []); - $this->assertFalse($res); - - $user->delete(); - } - - public function testRun() { - //Add test user - $user1 = $this->userManager->createUser('test1', 'test1'); - $userFolder = $this->rootFolder->getUserFolder('test1'); - $fileId = $userFolder->getId(); - - //Now insert cyclic share - $qb = $this->connection->getQueryBuilder(); - $qb->insert('share') - ->values([ - 'share_type' => $qb->createNamedParameter(0), - 'share_with' => $qb->createNamedParameter('foo'), - 'uid_owner' => $qb->createNamedParameter('owner'), - 'item_type' => $qb->createNamedParameter('file'), - 'item_source' => $qb->createNamedParameter($fileId), - 'item_target' => $qb->createNamedParameter('/target'), - 'file_source' => $qb->createNamedParameter($fileId), - 'file_target' => $qb->createNamedParameter('/target'), - 'permissions' => $qb->createNamedParameter(1), - ]); - $qb->execute(); - - //Add test user - $user2 = $this->userManager->createUser('test2', 'test2'); - $userFolder = $this->rootFolder->getUserFolder('test2'); - $folder = $userFolder->newFolder('foo'); - $fileId = $folder->getId(); - - //Now insert cyclic share - $qb = $this->connection->getQueryBuilder(); - $qb->insert('share') - ->values([ - 'share_type' => $qb->createNamedParameter(0), - 'share_with' => $qb->createNamedParameter('foo'), - 'uid_owner' => $qb->createNamedParameter('owner'), - 'item_type' => $qb->createNamedParameter('file'), - 'item_source' => $qb->createNamedParameter($fileId), - 'item_target' => $qb->createNamedParameter('/target'), - 'file_source' => $qb->createNamedParameter($fileId), - 'file_target' => $qb->createNamedParameter('/target'), - 'permissions' => $qb->createNamedParameter(1), - ]); - $qb->execute(); - - $this->repair->run($this->outputMock); - - //Verify - $qb = $this->connection->getQueryBuilder(); - $qb->selectAlias($qb->createFunction('COUNT(*)'), 'count') - ->from('share'); - - $cursor = $qb->execute(); - $data = $cursor->fetch(); - $cursor->closeCursor(); - - $count = (int)$data['count']; - - $this->assertEquals(1, $count); - - $user1->delete(); - $user2->delete(); - } -} -- cgit v1.2.3 From 2d7bbf4a63722ea4be7517356301859c9fc915f3 Mon Sep 17 00:00:00 2001 From: Roeland Jago Douma Date: Tue, 8 Mar 2016 16:12:17 +0100 Subject: Do not recurse link share fetching * Might fix an issue on oracle --- lib/private/share20/manager.php | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/lib/private/share20/manager.php b/lib/private/share20/manager.php index 9e04d57b289..7a48de6a11c 100644 --- a/lib/private/share20/manager.php +++ b/lib/private/share20/manager.php @@ -906,6 +906,11 @@ class Manager implements IManager { break; } + // If there was no limit on the select we are done + if ($limit === -1) { + break; + } + $offset += $added; // Fetch again $limit shares -- cgit v1.2.3 From 0e3682d810d9d5ddeb714ba337976cec5f7b2010 Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Fri, 17 Jun 2016 12:46:28 +0200 Subject: Capped cache for user config --- lib/private/allconfig.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/private/allconfig.php b/lib/private/allconfig.php index b4888fde022..e982b064736 100644 --- a/lib/private/allconfig.php +++ b/lib/private/allconfig.php @@ -27,6 +27,7 @@ */ namespace OC; +use OC\Cache\CappedMemoryCache; use OCP\IDBConnection; use OCP\PreConditionNotMetException; @@ -58,14 +59,15 @@ class AllConfig implements \OCP\IConfig { * - deleteAllUserValues * - deleteAppFromAllUsers * - * @var array $userCache + * @var CappedMemoryCache $userCache */ - private $userCache = array(); + private $userCache; /** * @param SystemConfig $systemConfig */ function __construct(SystemConfig $systemConfig) { + $this->userCache = new CappedMemoryCache(); $this->systemConfig = $systemConfig; } -- cgit v1.2.3 From 0129437cd50a5099d27efe4a16cc9e93fc36b4f6 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Mon, 20 Jun 2016 12:02:04 +0200 Subject: Make getShareFolder use given view instead of static FS (#25150) --- apps/files_sharing/lib/helper.php | 12 ++++++++---- apps/files_sharing/lib/sharedmount.php | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/apps/files_sharing/lib/helper.php b/apps/files_sharing/lib/helper.php index ee6af9939ed..612df1b207a 100644 --- a/apps/files_sharing/lib/helper.php +++ b/apps/files_sharing/lib/helper.php @@ -302,19 +302,23 @@ class Helper { /** * get default share folder * + * @param \OC\Files\View * @return string */ - public static function getShareFolder() { + public static function getShareFolder($view = null) { + if ($view === null) { + $view = Filesystem::getView(); + } $shareFolder = \OC::$server->getConfig()->getSystemValue('share_folder', '/'); $shareFolder = Filesystem::normalizePath($shareFolder); - if (!Filesystem::file_exists($shareFolder)) { + if (!$view->file_exists($shareFolder)) { $dir = ''; $subdirs = explode('/', $shareFolder); foreach ($subdirs as $subdir) { $dir = $dir . '/' . $subdir; - if (!Filesystem::is_dir($dir)) { - Filesystem::mkdir($dir); + if (!$view->is_dir($dir)) { + $view->mkdir($dir); } } } diff --git a/apps/files_sharing/lib/sharedmount.php b/apps/files_sharing/lib/sharedmount.php index 1e554cc5948..3a65794b606 100644 --- a/apps/files_sharing/lib/sharedmount.php +++ b/apps/files_sharing/lib/sharedmount.php @@ -75,7 +75,7 @@ class SharedMount extends MountPoint implements MoveableMount { $parent = dirname($share['file_target']); if (!$this->recipientView->is_dir($parent)) { - $parent = Helper::getShareFolder(); + $parent = Helper::getShareFolder($this->recipientView); } $newMountPoint = \OCA\Files_Sharing\Helper::generateUniqueTarget( -- cgit v1.2.3 From 2f61c2963c6c777e402adf7424e08d4029b25d1d Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Fri, 17 Jun 2016 11:00:09 +0200 Subject: Delay files_sharing's registerMountProviders This moves registerMountProviders until after the sharing backends were registered. In some situations registerMountProviders will trigger listeners which might require filesystem access which itself would mount shares, which itself requires the sharing backends to be initialized. --- apps/files_sharing/appinfo/app.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/apps/files_sharing/appinfo/app.php b/apps/files_sharing/appinfo/app.php index 29202c15b22..628921c3248 100644 --- a/apps/files_sharing/appinfo/app.php +++ b/apps/files_sharing/appinfo/app.php @@ -41,8 +41,6 @@ $l = \OC::$server->getL10N('files_sharing'); \OC::$CLASSPATH['OCA\Files\Share\Maintainer'] = 'files_sharing/lib/maintainer.php'; \OC::$CLASSPATH['OCA\Files\Share\Proxy'] = 'files_sharing/lib/proxy.php'; -$application = new Application(); -$application->registerMountProviders(); \OCP\App::registerAdmin('files_sharing', 'settings-admin'); \OCP\App::registerPersonal('files_sharing', 'settings-personal'); @@ -52,6 +50,9 @@ $application->registerMountProviders(); \OCP\Share::registerBackend('file', 'OC_Share_Backend_File'); \OCP\Share::registerBackend('folder', 'OC_Share_Backend_Folder', 'file'); +$application = new Application(); +$application->registerMountProviders(); + $eventDispatcher = \OC::$server->getEventDispatcher(); $eventDispatcher->addListener( 'OCA\Files::loadAdditionalScripts', -- cgit v1.2.3 From 11c1d399afa1a18e817482b38eb9aeb4d6492b53 Mon Sep 17 00:00:00 2001 From: Jörn Friedrich Dreyer Date: Fri, 10 Jun 2016 16:40:44 +0200 Subject: Fix null pointer exception in user_ldap --- apps/user_ldap/lib/access.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/user_ldap/lib/access.php b/apps/user_ldap/lib/access.php index f92ded64797..dd4aeee3b24 100644 --- a/apps/user_ldap/lib/access.php +++ b/apps/user_ldap/lib/access.php @@ -730,7 +730,14 @@ class Access extends LDAPUtility implements user\IUserTools { $user->unmark(); $user = $this->userManager->get($ocName); } - $user->processAttributes($userRecord); + if ($user !== null) { + $user->processAttributes($userRecord); + } else { + \OC::$server->getLogger()->debug( + "The ldap user manager returned null for $ocName", + ['app'=>'user_ldap'] + ); + } } } -- cgit v1.2.3 From fa9ba64552a43448d7394d3e11a60997b00eca48 Mon Sep 17 00:00:00 2001 From: Robin Appelman Date: Mon, 20 Jun 2016 22:11:05 +0200 Subject: Catch exceptions while creating shared mounts --- apps/files_sharing/appinfo/application.php | 3 ++- apps/files_sharing/lib/mountprovider.php | 33 +++++++++++++++++++++--------- 2 files changed, 25 insertions(+), 11 deletions(-) diff --git a/apps/files_sharing/appinfo/application.php b/apps/files_sharing/appinfo/application.php index 64c7517456d..311250ce195 100644 --- a/apps/files_sharing/appinfo/application.php +++ b/apps/files_sharing/appinfo/application.php @@ -111,7 +111,8 @@ class Application extends App { /** @var \OCP\IServerContainer $server */ $server = $c->query('ServerContainer'); return new MountProvider( - $server->getConfig() + $server->getConfig(), + $server->getLogger() ); }); diff --git a/apps/files_sharing/lib/mountprovider.php b/apps/files_sharing/lib/mountprovider.php index 4a60e44bb26..d1d54a21ab3 100644 --- a/apps/files_sharing/lib/mountprovider.php +++ b/apps/files_sharing/lib/mountprovider.php @@ -27,6 +27,7 @@ use OC\User\NoUserException; use OCP\Files\Config\IMountProvider; use OCP\Files\Storage\IStorageFactory; use OCP\IConfig; +use OCP\ILogger; use OCP\IUser; class MountProvider implements IMountProvider { @@ -35,11 +36,18 @@ class MountProvider implements IMountProvider { */ protected $config; + /** + * @var ILogger + */ + protected $logger; + /** * @param \OCP\IConfig $config + * @param ILogger $logger */ - public function __construct(IConfig $config) { + public function __construct(IConfig $config, ILogger $logger) { $this->config = $config; + $this->logger = $logger; } @@ -57,15 +65,20 @@ class MountProvider implements IMountProvider { }); $shares = array_map(function ($share) use ($user, $storageFactory) { - return new SharedMount( - '\OC\Files\Storage\Shared', - '/' . $user->getUID() . '/' . $share['file_target'], - array( - 'share' => $share, - 'user' => $user->getUID() - ), - $storageFactory - ); + try { + return new SharedMount( + '\OC\Files\Storage\Shared', + '/' . $user->getUID() . '/' . $share['file_target'], + array( + 'share' => $share, + 'user' => $user->getUID() + ), + $storageFactory + ); + } catch (\Exception $e) { + $this->logger->logException($e); + $this->logger->error('Error while trying to create shared mount'); + } }, $shares); // array_filter removes the null values from the array return array_filter($shares); -- cgit v1.2.3 From bf7a08f62dafd24932b93ffd213f4e40f151ced0 Mon Sep 17 00:00:00 2001 From: Juan Pablo Villafañez Date: Tue, 21 Jun 2016 15:04:08 +0200 Subject: dd support to know where the storage test comes from (#25166) --- apps/files_external/controller/globalstoragescontroller.php | 6 ++++-- apps/files_external/controller/storagescontroller.php | 11 +++++++---- .../controller/userglobalstoragescontroller.php | 11 +++++++---- apps/files_external/controller/userstoragescontroller.php | 10 ++++++---- apps/files_external/js/settings.js | 5 ++++- apps/files_external/js/statusmanager.js | 1 + apps/files_external/lib/config.php | 4 ++-- apps/files_external/tests/js/settingsSpec.js | 3 ++- 8 files changed, 33 insertions(+), 18 deletions(-) diff --git a/apps/files_external/controller/globalstoragescontroller.php b/apps/files_external/controller/globalstoragescontroller.php index b443cf4ea8f..dc7e8c0c24c 100644 --- a/apps/files_external/controller/globalstoragescontroller.php +++ b/apps/files_external/controller/globalstoragescontroller.php @@ -131,6 +131,7 @@ class GlobalStoragesController extends StoragesController { * @param array $applicableUsers users for which to mount the storage * @param array $applicableGroups groups for which to mount the storage * @param int $priority priority + * @param bool $testOnly whether to storage should only test the connection or do more things * * @return DataResponse */ @@ -143,7 +144,8 @@ class GlobalStoragesController extends StoragesController { $mountOptions, $applicableUsers, $applicableGroups, - $priority + $priority, + $testOnly = true ) { $storage = $this->createStorage( $mountPoint, @@ -176,7 +178,7 @@ class GlobalStoragesController extends StoragesController { ); } - $this->updateStorageStatus($storage); + $this->updateStorageStatus($storage, $testOnly); return new DataResponse( $storage, diff --git a/apps/files_external/controller/storagescontroller.php b/apps/files_external/controller/storagescontroller.php index 09b83104700..b26e85cc9af 100644 --- a/apps/files_external/controller/storagescontroller.php +++ b/apps/files_external/controller/storagescontroller.php @@ -240,8 +240,9 @@ abstract class StoragesController extends Controller { * on whether the remote storage is available or not. * * @param StorageConfig $storage storage configuration + * @param bool $testOnly whether to storage should only test the connection or do more things */ - protected function updateStorageStatus(StorageConfig &$storage) { + protected function updateStorageStatus(StorageConfig &$storage, $testOnly = true) { try { $this->manipulateStorageConfig($storage); @@ -252,7 +253,8 @@ abstract class StoragesController extends Controller { \OC_Mount_Config::getBackendStatus( $backend->getStorageClass(), $storage->getBackendOptions(), - false + false, + $testOnly ) ); } catch (InsufficientDataForMeaningfulAnswerException $e) { @@ -293,14 +295,15 @@ abstract class StoragesController extends Controller { * Get an external storage entry. * * @param int $id storage id + * @param bool $testOnly whether to storage should only test the connection or do more things * * @return DataResponse */ - public function show($id) { + public function show($id, $testOnly = true) { try { $storage = $this->service->getStorage($id); - $this->updateStorageStatus($storage); + $this->updateStorageStatus($storage, $testOnly); } catch (NotFoundException $e) { return new DataResponse( [ diff --git a/apps/files_external/controller/userglobalstoragescontroller.php b/apps/files_external/controller/userglobalstoragescontroller.php index 36c3740eed3..f89022ee468 100644 --- a/apps/files_external/controller/userglobalstoragescontroller.php +++ b/apps/files_external/controller/userglobalstoragescontroller.php @@ -106,15 +106,16 @@ class UserGlobalStoragesController extends StoragesController { * Get an external storage entry. * * @param int $id storage id + * @param bool $testOnly whether to storage should only test the connection or do more things * @return DataResponse * * @NoAdminRequired */ - public function show($id) { + public function show($id, $testOnly = true) { try { $storage = $this->service->getStorage($id); - $this->updateStorageStatus($storage); + $this->updateStorageStatus($storage, $testOnly); } catch (NotFoundException $e) { return new DataResponse( [ @@ -138,6 +139,7 @@ class UserGlobalStoragesController extends StoragesController { * * @param int $id storage id * @param array $backendOptions backend-specific options + * @param bool $testOnly whether to storage should only test the connection or do more things * * @return DataResponse * @@ -145,7 +147,8 @@ class UserGlobalStoragesController extends StoragesController { */ public function update( $id, - $backendOptions + $backendOptions, + $testOnly = true ) { try { $storage = $this->service->getStorage($id); @@ -170,7 +173,7 @@ class UserGlobalStoragesController extends StoragesController { ); } - $this->updateStorageStatus($storage); + $this->updateStorageStatus($storage, $testOnly); $this->sanitizeStorage($storage); return new DataResponse( diff --git a/apps/files_external/controller/userstoragescontroller.php b/apps/files_external/controller/userstoragescontroller.php index e53ea21f005..8ab22341225 100644 --- a/apps/files_external/controller/userstoragescontroller.php +++ b/apps/files_external/controller/userstoragescontroller.php @@ -104,8 +104,8 @@ class UserStoragesController extends StoragesController { * * {@inheritdoc} */ - public function show($id) { - return parent::show($id); + public function show($id, $testOnly = true) { + return parent::show($id, $testOnly); } /** @@ -162,6 +162,7 @@ class UserStoragesController extends StoragesController { * @param string $authMechanism authentication mechanism identifier * @param array $backendOptions backend-specific options * @param array $mountOptions backend-specific mount options + * @param bool $testOnly whether to storage should only test the connection or do more things * * @return DataResponse * @@ -173,7 +174,8 @@ class UserStoragesController extends StoragesController { $backend, $authMechanism, $backendOptions, - $mountOptions + $mountOptions, + $testOnly = true ) { $storage = $this->createStorage( $mountPoint, @@ -203,7 +205,7 @@ class UserStoragesController extends StoragesController { ); } - $this->updateStorageStatus($storage); + $this->updateStorageStatus($storage, $testOnly); return new DataResponse( $storage, diff --git a/apps/files_external/js/settings.js b/apps/files_external/js/settings.js index 0b33458bec2..6262245688b 100644 --- a/apps/files_external/js/settings.js +++ b/apps/files_external/js/settings.js @@ -305,7 +305,8 @@ StorageConfig.prototype = { mountPoint: this.mountPoint, backend: this.backend, authMechanism: this.authMechanism, - backendOptions: this.backendOptions + backendOptions: this.backendOptions, + testOnly: true }; if (this.id) { data.id = this.id; @@ -332,6 +333,7 @@ StorageConfig.prototype = { $.ajax({ type: 'GET', url: OC.generateUrl(this._url + '/{id}', {id: this.id}), + data: {'testOnly': true}, success: options.success, error: options.error }); @@ -911,6 +913,7 @@ MountConfigListView.prototype = _.extend({ $.ajax({ type: 'GET', url: OC.generateUrl('apps/files_external/userglobalstorages'), + data: {'testOnly': true}, contentType: 'application/json', success: function(result) { var onCompletion = jQuery.Deferred(); diff --git a/apps/files_external/js/statusmanager.js b/apps/files_external/js/statusmanager.js index 33d2ea104be..c51562558cf 100644 --- a/apps/files_external/js/statusmanager.js +++ b/apps/files_external/js/statusmanager.js @@ -78,6 +78,7 @@ OCA.External.StatusManager = { defObj = $.ajax({ type: 'GET', url: OC.webroot + '/index.php/apps/files_external/' + ((mountData.type === 'personal') ? 'userstorages' : 'userglobalstorages') + '/' + mountData.id, + data: {'testOnly': false}, success: function (response) { if (response && response.status === 0) { self.mountStatus[mountData.mount_point] = response; diff --git a/apps/files_external/lib/config.php b/apps/files_external/lib/config.php index 70f8550f39b..11a111bd8ff 100644 --- a/apps/files_external/lib/config.php +++ b/apps/files_external/lib/config.php @@ -215,7 +215,7 @@ class OC_Mount_Config { * @return int see self::STATUS_* * @throws Exception */ - public static function getBackendStatus($class, $options, $isPersonal) { + public static function getBackendStatus($class, $options, $isPersonal, $testOnly = true) { if (self::$skipTest) { return StorageNotAvailableException::STATUS_SUCCESS; } @@ -228,7 +228,7 @@ class OC_Mount_Config { $storage = new $class($options); try { - $result = $storage->test($isPersonal); + $result = $storage->test($isPersonal, $testOnly); $storage->setAvailability($result); if ($result) { return StorageNotAvailableException::STATUS_SUCCESS; diff --git a/apps/files_external/tests/js/settingsSpec.js b/apps/files_external/tests/js/settingsSpec.js index 462407e9540..e4ee7a61aa9 100644 --- a/apps/files_external/tests/js/settingsSpec.js +++ b/apps/files_external/tests/js/settingsSpec.js @@ -223,7 +223,8 @@ describe('OCA.External.Settings tests', function() { applicableGroups: [], mountOptions: { 'previews': true - } + }, + testOnly: true }); // TODO: respond and check data-id -- cgit v1.2.3 From 880ff122f11b51585d115892b8ba79b6900237bd Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Wed, 22 Jun 2016 15:18:19 +0200 Subject: Rollback version must also adjust cached size --- apps/files_versions/lib/storage.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/files_versions/lib/storage.php b/apps/files_versions/lib/storage.php index a1069410e70..1062f49cf69 100644 --- a/apps/files_versions/lib/storage.php +++ b/apps/files_versions/lib/storage.php @@ -337,9 +337,16 @@ class Storage { // Restore encrypted version of the old file for the newly restored file // This has to happen manually here since the file is manually copied below $oldVersion = $users_view->getFileInfo($fileToRestore)->getEncryptedVersion(); + $oldFileInfo = $users_view->getFileInfo($fileToRestore); $newFileInfo = $files_view->getFileInfo($filename); $cache = $newFileInfo->getStorage()->getCache(); - $cache->update($newFileInfo->getId(), ['encrypted' => $oldVersion, 'encryptedVersion' => $oldVersion]); + $cache->update( + $newFileInfo->getId(), [ + 'encrypted' => $oldVersion, + 'encryptedVersion' => $oldVersion, + 'size' => $oldFileInfo->getSize() + ] + ); // rollback if (self::copyFileContents($users_view, $fileToRestore, 'files' . $filename)) { -- cgit v1.2.3 From b6192c39d840c46631cb8e3f4ab69aec4b04a479 Mon Sep 17 00:00:00 2001 From: Roeland Douma Date: Thu, 14 Apr 2016 11:50:27 +0200 Subject: On mount make sure multiple shares with same target map to unique ones (#23937) Scenario: user0 shares a folder 'foo' with user2 user1 shares a folder 'foo' with user2 user2 logs in Before: show only the 'foo' from user1 After: show both. * Added intergration tests --- apps/files_sharing/lib/mountprovider.php | 17 +++++----- apps/files_sharing/lib/sharedmount.php | 47 +++++++++++++++++++++++---- build/integration/features/sharing-v1.feature | 13 ++++++++ 3 files changed, 62 insertions(+), 15 deletions(-) diff --git a/apps/files_sharing/lib/mountprovider.php b/apps/files_sharing/lib/mountprovider.php index d1d54a21ab3..b386c6704f9 100644 --- a/apps/files_sharing/lib/mountprovider.php +++ b/apps/files_sharing/lib/mountprovider.php @@ -63,24 +63,25 @@ class MountProvider implements IMountProvider { $shares = array_filter($shares, function ($share) { return $share['permissions'] > 0; }); - $shares = array_map(function ($share) use ($user, $storageFactory) { - + $mounts = []; + foreach ($shares as $share) { try { - return new SharedMount( + $mounts[] = new SharedMount( '\OC\Files\Storage\Shared', - '/' . $user->getUID() . '/' . $share['file_target'], - array( + $mounts, + [ 'share' => $share, 'user' => $user->getUID() - ), + ], $storageFactory ); } catch (\Exception $e) { $this->logger->logException($e); $this->logger->error('Error while trying to create shared mount'); } - }, $shares); + } + // array_filter removes the null values from the array - return array_filter($shares); + return array_filter($mounts); } } diff --git a/apps/files_sharing/lib/sharedmount.php b/apps/files_sharing/lib/sharedmount.php index 3a65794b606..8eaf5469646 100644 --- a/apps/files_sharing/lib/sharedmount.php +++ b/apps/files_sharing/lib/sharedmount.php @@ -25,6 +25,7 @@ namespace OCA\Files_Sharing; +use OC\Files\Filesystem; use OC\Files\Mount\MountPoint; use OC\Files\Mount\MoveableMount; use OC\Files\View; @@ -50,14 +51,14 @@ class SharedMount extends MountPoint implements MoveableMount { /** * @param string $storage - * @param string $mountpoint + * @param SharedMount[] $mountpoints * @param array|null $arguments * @param \OCP\Files\Storage\IStorageFactory $loader */ - public function __construct($storage, $mountpoint, $arguments = null, $loader = null) { + public function __construct($storage, array $mountpoints, $arguments = null, $loader = null) { $this->user = $arguments['user']; $this->recipientView = new View('/' . $this->user . '/files'); - $newMountPoint = $this->verifyMountPoint($arguments['share']); + $newMountPoint = $this->verifyMountPoint($arguments['share'], $mountpoints); $absMountPoint = '/' . $this->user . '/files' . $newMountPoint; $arguments['ownerView'] = new View('/' . $arguments['share']['uid_owner'] . '/files'); parent::__construct($storage, $absMountPoint, $arguments, $loader); @@ -67,9 +68,10 @@ class SharedMount extends MountPoint implements MoveableMount { * check if the parent folder exists otherwise move the mount point up * * @param array $share + * @param SharedMount[] $mountpoints * @return string */ - private function verifyMountPoint(&$share) { + private function verifyMountPoint(&$share, array $mountpoints) { $mountPoint = basename($share['file_target']); $parent = dirname($share['file_target']); @@ -78,10 +80,10 @@ class SharedMount extends MountPoint implements MoveableMount { $parent = Helper::getShareFolder($this->recipientView); } - $newMountPoint = \OCA\Files_Sharing\Helper::generateUniqueTarget( + $newMountPoint = $this->generateUniqueTarget( \OC\Files\Filesystem::normalizePath($parent . '/' . $mountPoint), - [], - $this->recipientView + $this->recipientView, + $mountpoints ); if ($newMountPoint !== $share['file_target']) { @@ -93,6 +95,37 @@ class SharedMount extends MountPoint implements MoveableMount { return $newMountPoint; } + /** + * @param string $path + * @param View $view + * @param SharedMount[] $mountpoints + * @return mixed + */ + private function generateUniqueTarget($path, $view, array $mountpoints) { + $pathinfo = pathinfo($path); + $ext = (isset($pathinfo['extension'])) ? '.'.$pathinfo['extension'] : ''; + $name = $pathinfo['filename']; + $dir = $pathinfo['dirname']; + + // Helper function to find existing mount points + $mountpointExists = function($path) use ($mountpoints) { + foreach ($mountpoints as $mountpoint) { + if ($mountpoint->getShare()['file_target'] === $path) { + return true; + } + } + return false; + }; + + $i = 2; + while ($view->file_exists($path) || $mountpointExists($path)) { + $path = Filesystem::normalizePath($dir . '/' . $name . ' ('.$i.')' . $ext); + $i++; + } + + return $path; + } + /** * update fileTarget in the database if the mount point changed * diff --git a/build/integration/features/sharing-v1.feature b/build/integration/features/sharing-v1.feature index ba535e83aab..56399eefdb0 100644 --- a/build/integration/features/sharing-v1.feature +++ b/build/integration/features/sharing-v1.feature @@ -566,3 +566,16 @@ Feature: sharing | path | welcome.txt | | shareType | 3 | Then share ids should match + + Scenario: unique target names for incomming shares + Given user "user0" exists + And user "user1" exists + And user "user2" exists + And user "user0" created a folder "/foo" + And user "user1" created a folder "/foo" + When file "/foo" of user "user0" is shared with user "user2" + And file "/foo" of user "user1" is shared with user "user2" + Then user "user2" should see following elements + | /foo/ | + | /foo%20(2)/ | + -- cgit v1.2.3 From e366ed6485a987cc7a77573bf3e46cba854b07b5 Mon Sep 17 00:00:00 2001 From: Vincent Petry Date: Thu, 23 Jun 2016 11:54:49 +0200 Subject: Don't reload page in case of auth errors during setup checks If an error occurs during setup checks, do not let the global ajax error handler reload the page. --- core/js/setupchecks.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/js/setupchecks.js b/core/js/setupchecks.js index ee93781b074..9c601e7bd5f 100644 --- a/core/js/setupchecks.js +++ b/core/js/setupchecks.js @@ -76,7 +76,8 @@ $.ajax({ type: 'PROPFIND', url: url, - complete: afterCall + complete: afterCall, + allowAuthErrors: true }); return deferred.promise(); }, @@ -209,7 +210,8 @@ $.ajax({ type: 'GET', url: OC.linkTo('', oc_dataURL+'/htaccesstest.txt?t=' + (new Date()).getTime()), - complete: afterCall + complete: afterCall, + allowAuthErrors: true }); return deferred.promise(); }, -- cgit v1.2.3