diff options
95 files changed, 2989 insertions, 299 deletions
diff --git a/apps/admin_audit/appinfo/info.xml b/apps/admin_audit/appinfo/info.xml index 054ed9580f2..3006551b409 100644 --- a/apps/admin_audit/appinfo/info.xml +++ b/apps/admin_audit/appinfo/info.xml @@ -17,4 +17,7 @@ <dependencies> <nextcloud min-version="14" max-version="14" /> </dependencies> + <background-jobs> + <job>OCA\AdminAudit\BackgroundJobs\Rotate</job> + </background-jobs> </info> diff --git a/apps/admin_audit/composer/composer/autoload_classmap.php b/apps/admin_audit/composer/composer/autoload_classmap.php index c08200c7c20..95ddaac7452 100644 --- a/apps/admin_audit/composer/composer/autoload_classmap.php +++ b/apps/admin_audit/composer/composer/autoload_classmap.php @@ -18,4 +18,5 @@ return array( 'OCA\\AdminAudit\\Actions\\UserManagement' => $baseDir . '/../lib/Actions/UserManagement.php', 'OCA\\AdminAudit\\Actions\\Versions' => $baseDir . '/../lib/Actions/Versions.php', 'OCA\\AdminAudit\\AppInfo\\Application' => $baseDir . '/../lib/AppInfo/Application.php', + 'OCA\\AdminAudit\\BackgroundJobs\\Rotate' => $baseDir . '/../lib/BackgroundJobs/Rotate.php', ); diff --git a/apps/admin_audit/composer/composer/autoload_static.php b/apps/admin_audit/composer/composer/autoload_static.php index ef088bd22d9..1c01a35ceb2 100644 --- a/apps/admin_audit/composer/composer/autoload_static.php +++ b/apps/admin_audit/composer/composer/autoload_static.php @@ -33,6 +33,7 @@ class ComposerStaticInitAdminAudit 'OCA\\AdminAudit\\Actions\\UserManagement' => __DIR__ . '/..' . '/../lib/Actions/UserManagement.php', 'OCA\\AdminAudit\\Actions\\Versions' => __DIR__ . '/..' . '/../lib/Actions/Versions.php', 'OCA\\AdminAudit\\AppInfo\\Application' => __DIR__ . '/..' . '/../lib/AppInfo/Application.php', + 'OCA\\AdminAudit\\BackgroundJobs\\Rotate' => __DIR__ . '/..' . '/../lib/BackgroundJobs/Rotate.php', ); public static function getInitializer(ClassLoader $loader) diff --git a/apps/admin_audit/lib/AppInfo/Application.php b/apps/admin_audit/lib/AppInfo/Application.php index df39e3eb111..77b76885a20 100644 --- a/apps/admin_audit/lib/AppInfo/Application.php +++ b/apps/admin_audit/lib/AppInfo/Application.php @@ -53,8 +53,26 @@ use OCP\Share; class Application extends App { + /** @var ILogger */ + protected $logger; + public function __construct() { parent::__construct('admin_audit'); + $this->initLogger(); + } + + public function initLogger() { + $c = $this->getContainer()->getServer(); + $config = $c->getConfig(); + + $default = $config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/audit.log'; + $logFile = $config->getAppValue('admin_audit', 'logfile', $default); + if($logFile === null) { + $this->logger = $c->getLogger(); + return; + } + $this->logger = $c->getLogFactory()->getCustomLogger($logFile); + } public function register() { @@ -65,26 +83,24 @@ class Application extends App { * Register hooks in order to log them */ protected function registerHooks() { - $logger = $this->getContainer()->getServer()->getLogger(); - - $this->userManagementHooks($logger); - $this->groupHooks($logger); - $this->authHooks($logger); + $this->userManagementHooks(); + $this->groupHooks(); + $this->authHooks(); - $this->consoleHooks($logger); - $this->appHooks($logger); + $this->consoleHooks(); + $this->appHooks(); - $this->sharingHooks($logger); + $this->sharingHooks(); - $this->fileHooks($logger); - $this->trashbinHooks($logger); - $this->versionsHooks($logger); + $this->fileHooks(); + $this->trashbinHooks(); + $this->versionsHooks(); - $this->securityHooks($logger); + $this->securityHooks(); } - protected function userManagementHooks(ILogger $logger) { - $userActions = new UserManagement($logger); + protected function userManagementHooks() { + $userActions = new UserManagement($this->logger); Util::connectHook('OC_User', 'post_createUser', $userActions, 'create'); Util::connectHook('OC_User', 'post_deleteUser', $userActions, 'delete'); @@ -97,8 +113,8 @@ class Application extends App { $userSession->listen('\OC\User', 'postUnassignedUserId', [$userActions, 'unassign']); } - protected function groupHooks(ILogger $logger) { - $groupActions = new GroupManagement($logger); + protected function groupHooks() { + $groupActions = new GroupManagement($this->logger); /** @var IGroupManager|Manager $groupManager */ $groupManager = $this->getContainer()->getServer()->getGroupManager(); @@ -108,8 +124,8 @@ class Application extends App { $groupManager->listen('\OC\Group', 'postCreate', [$groupActions, 'createGroup']); } - protected function sharingHooks(ILogger $logger) { - $shareActions = new Sharing($logger); + protected function sharingHooks() { + $shareActions = new Sharing($this->logger); Util::connectHook(Share::class, 'post_shared', $shareActions, 'shared'); Util::connectHook(Share::class, 'post_unshare', $shareActions, 'unshare'); @@ -119,42 +135,42 @@ class Application extends App { Util::connectHook(Share::class, 'share_link_access', $shareActions, 'shareAccessed'); } - protected function authHooks(ILogger $logger) { - $authActions = new Auth($logger); + protected function authHooks() { + $authActions = new Auth($this->logger); Util::connectHook('OC_User', 'pre_login', $authActions, 'loginAttempt'); Util::connectHook('OC_User', 'post_login', $authActions, 'loginSuccessful'); Util::connectHook('OC_User', 'logout', $authActions, 'logout'); } - protected function appHooks(ILogger $logger) { + protected function appHooks() { $eventDispatcher = $this->getContainer()->getServer()->getEventDispatcher(); - $eventDispatcher->addListener(ManagerEvent::EVENT_APP_ENABLE, function(ManagerEvent $event) use ($logger) { - $appActions = new AppManagement($logger); + $eventDispatcher->addListener(ManagerEvent::EVENT_APP_ENABLE, function(ManagerEvent $event) { + $appActions = new AppManagement($this->logger); $appActions->enableApp($event->getAppID()); }); - $eventDispatcher->addListener(ManagerEvent::EVENT_APP_ENABLE_FOR_GROUPS, function(ManagerEvent $event) use ($logger) { - $appActions = new AppManagement($logger); + $eventDispatcher->addListener(ManagerEvent::EVENT_APP_ENABLE_FOR_GROUPS, function(ManagerEvent $event) { + $appActions = new AppManagement($this->logger); $appActions->enableAppForGroups($event->getAppID(), $event->getGroups()); }); - $eventDispatcher->addListener(ManagerEvent::EVENT_APP_DISABLE, function(ManagerEvent $event) use ($logger) { - $appActions = new AppManagement($logger); + $eventDispatcher->addListener(ManagerEvent::EVENT_APP_DISABLE, function(ManagerEvent $event) { + $appActions = new AppManagement($this->logger); $appActions->disableApp($event->getAppID()); }); } - protected function consoleHooks(ILogger $logger) { + protected function consoleHooks() { $eventDispatcher = $this->getContainer()->getServer()->getEventDispatcher(); - $eventDispatcher->addListener(ConsoleEvent::EVENT_RUN, function(ConsoleEvent $event) use ($logger) { - $appActions = new Console($logger); + $eventDispatcher->addListener(ConsoleEvent::EVENT_RUN, function(ConsoleEvent $event) { + $appActions = new Console($this->logger); $appActions->runCommand($event->getArguments()); }); } - protected function fileHooks(ILogger $logger) { - $fileActions = new Files($logger); + protected function fileHooks() { + $fileActions = new Files($this->logger); $eventDispatcher = $this->getContainer()->getServer()->getEventDispatcher(); $eventDispatcher->addListener( IPreview::EVENT, @@ -215,26 +231,26 @@ class Application extends App { ); } - protected function versionsHooks(ILogger $logger) { - $versionsActions = new Versions($logger); + protected function versionsHooks() { + $versionsActions = new Versions($this->logger); Util::connectHook('\OCP\Versions', 'rollback', $versionsActions, 'rollback'); Util::connectHook('\OCP\Versions', 'delete',$versionsActions, 'delete'); } - protected function trashbinHooks(ILogger $logger) { - $trashActions = new Trashbin($logger); + protected function trashbinHooks() { + $trashActions = new Trashbin($this->logger); Util::connectHook('\OCP\Trashbin', 'preDelete', $trashActions, 'delete'); Util::connectHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', $trashActions, 'restore'); } - protected function securityHooks(ILogger $logger) { + protected function securityHooks() { $eventDispatcher = $this->getContainer()->getServer()->getEventDispatcher(); - $eventDispatcher->addListener(IProvider::EVENT_SUCCESS, function(GenericEvent $event) use ($logger) { - $security = new Security($logger); + $eventDispatcher->addListener(IProvider::EVENT_SUCCESS, function(GenericEvent $event) { + $security = new Security($this->logger); $security->twofactorSuccess($event->getSubject(), $event->getArguments()); }); - $eventDispatcher->addListener(IProvider::EVENT_FAILED, function(GenericEvent $event) use ($logger) { - $security = new Security($logger); + $eventDispatcher->addListener(IProvider::EVENT_FAILED, function(GenericEvent $event) { + $security = new Security($this->logger); $security->twofactorFailed($event->getSubject(), $event->getArguments()); }); } diff --git a/apps/admin_audit/lib/BackgroundJobs/Rotate.php b/apps/admin_audit/lib/BackgroundJobs/Rotate.php new file mode 100644 index 00000000000..421ee65d643 --- /dev/null +++ b/apps/admin_audit/lib/BackgroundJobs/Rotate.php @@ -0,0 +1,52 @@ +<?php +/** + * @copyright Copyright (c) 2018 Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @author Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\AdminAudit\BackgroundJobs; + +use OC\BackgroundJob\TimedJob; +use OCP\Log\RotationTrait; + +class Rotate extends TimedJob { + use RotationTrait; + + public function __construct() { + $this->setInterval(60*60*3); + } + + protected function run($argument) { + $config = \OC::$server->getConfig(); + $default = $config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/audit.log'; + $this->filePath = $config->getAppValue('admin_audit', 'logfile', $default); + + if($this->filePath === '') { + // default log file, nothing to do + return; + } + + $this->maxSize = $config->getSystemValue('log_rotate_size', 100 * 1024 * 1024); + + if($this->shouldRotateBySize()) { + $this->rotate(); + } + } +} diff --git a/apps/dav/appinfo/info.xml b/apps/dav/appinfo/info.xml index 3afef530e7a..d31851fe17e 100644 --- a/apps/dav/appinfo/info.xml +++ b/apps/dav/appinfo/info.xml @@ -5,7 +5,7 @@ <name>WebDAV</name> <summary>WebDAV endpoint</summary> <description>WebDAV endpoint</description> - <version>1.5.0</version> + <version>1.5.2</version> <licence>agpl</licence> <author>owncloud.org</author> <namespace>DAV</namespace> @@ -19,6 +19,10 @@ <nextcloud min-version="14" max-version="14" /> </dependencies> + <background-jobs> + <job>OCA\DAV\BackgroundJob\CleanupDirectLinksJob</job> + </background-jobs> + <repair-steps> <post-migration> <step>OCA\DAV\Migration\FixBirthdayCalendarComponent</step> diff --git a/apps/dav/appinfo/routes.php b/apps/dav/appinfo/routes.php index e6785bfd4e6..2aaeda98964 100644 --- a/apps/dav/appinfo/routes.php +++ b/apps/dav/appinfo/routes.php @@ -25,5 +25,8 @@ return [ 'routes' => [ ['name' => 'birthday_calendar#enable', 'url' => '/enableBirthdayCalendar', 'verb' => 'POST'], ['name' => 'birthday_calendar#disable', 'url' => '/disableBirthdayCalendar', 'verb' => 'POST'], - ] + ], + 'ocs' => [ + ['name' => 'direct#getUrl', 'url' => '/api/v1/direct', 'verb' => 'POST'], + ], ]; diff --git a/apps/dav/appinfo/v2/direct.php b/apps/dav/appinfo/v2/direct.php new file mode 100644 index 00000000000..3762a628303 --- /dev/null +++ b/apps/dav/appinfo/v2/direct.php @@ -0,0 +1,47 @@ +<?php +declare(strict_types=1); +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +// no php execution timeout for webdav +if (strpos(@ini_get('disable_functions'), 'set_time_limit') === false) { + @set_time_limit(0); +} +ignore_user_abort(true); + +// Turn off output buffering to prevent memory problems +\OC_Util::obEnd(); + +$requestUri = \OC::$server->getRequest()->getRequestUri(); + +$serverFactory = new \OCA\DAV\Direct\ServerFactory(\OC::$server->getConfig()); +$server = $serverFactory->createServer( + $baseuri, + $requestUri, + \OC::$server->getRootFolder(), + \OC::$server->query(\OCA\DAV\Db\DirectMapper::class), + \OC::$server->query(\OCP\AppFramework\Utility\ITimeFactory::class), + \OC::$server->getBruteForceThrottler(), + \OC::$server->getRequest() +); + +$server->exec(); diff --git a/apps/dav/composer/composer/autoload_classmap.php b/apps/dav/composer/composer/autoload_classmap.php index 0bf6b25751e..50689568ebb 100644 --- a/apps/dav/composer/composer/autoload_classmap.php +++ b/apps/dav/composer/composer/autoload_classmap.php @@ -11,6 +11,7 @@ return array( 'OCA\\DAV\\Avatars\\AvatarHome' => $baseDir . '/../lib/Avatars/AvatarHome.php', 'OCA\\DAV\\Avatars\\AvatarNode' => $baseDir . '/../lib/Avatars/AvatarNode.php', 'OCA\\DAV\\Avatars\\RootCollection' => $baseDir . '/../lib/Avatars/RootCollection.php', + 'OCA\\DAV\\BackgroundJob\\CleanupDirectLinksJob' => $baseDir . '/../lib/BackgroundJob/CleanupDirectLinksJob.php', 'OCA\\DAV\\BackgroundJob\\GenerateBirthdayCalendarBackgroundJob' => $baseDir . '/../lib/BackgroundJob/GenerateBirthdayCalendarBackgroundJob.php', 'OCA\\DAV\\CalDAV\\Activity\\Backend' => $baseDir . '/../lib/CalDAV/Activity/Backend.php', 'OCA\\DAV\\CalDAV\\Activity\\Filter\\Calendar' => $baseDir . '/../lib/CalDAV/Activity/Filter/Calendar.php', @@ -109,6 +110,7 @@ return array( 'OCA\\DAV\\Connector\\Sabre\\TagList' => $baseDir . '/../lib/Connector/Sabre/TagList.php', 'OCA\\DAV\\Connector\\Sabre\\TagsPlugin' => $baseDir . '/../lib/Connector/Sabre/TagsPlugin.php', 'OCA\\DAV\\Controller\\BirthdayCalendarController' => $baseDir . '/../lib/Controller/BirthdayCalendarController.php', + 'OCA\\DAV\\Controller\\DirectController' => $baseDir . '/../lib/Controller/DirectController.php', 'OCA\\DAV\\DAV\\CustomPropertiesBackend' => $baseDir . '/../lib/DAV/CustomPropertiesBackend.php', 'OCA\\DAV\\DAV\\GroupPrincipalBackend' => $baseDir . '/../lib/DAV/GroupPrincipalBackend.php', 'OCA\\DAV\\DAV\\PublicAuth' => $baseDir . '/../lib/DAV/PublicAuth.php', @@ -118,6 +120,12 @@ return array( 'OCA\\DAV\\DAV\\Sharing\\Xml\\Invite' => $baseDir . '/../lib/DAV/Sharing/Xml/Invite.php', 'OCA\\DAV\\DAV\\Sharing\\Xml\\ShareRequest' => $baseDir . '/../lib/DAV/Sharing/Xml/ShareRequest.php', 'OCA\\DAV\\DAV\\SystemPrincipalBackend' => $baseDir . '/../lib/DAV/SystemPrincipalBackend.php', + 'OCA\\DAV\\Db\\Direct' => $baseDir . '/../lib/Db/Direct.php', + 'OCA\\DAV\\Db\\DirectMapper' => $baseDir . '/../lib/Db/DirectMapper.php', + 'OCA\\DAV\\Direct\\DirectFile' => $baseDir . '/../lib/Direct/DirectFile.php', + 'OCA\\DAV\\Direct\\DirectHome' => $baseDir . '/../lib/Direct/DirectHome.php', + 'OCA\\DAV\\Direct\\Server' => $baseDir . '/../lib/Direct/Server.php', + 'OCA\\DAV\\Direct\\ServerFactory' => $baseDir . '/../lib/Direct/ServerFactory.php', 'OCA\\DAV\\Files\\BrowserErrorPagePlugin' => $baseDir . '/../lib/Files/BrowserErrorPagePlugin.php', 'OCA\\DAV\\Files\\FileSearchBackend' => $baseDir . '/../lib/Files/FileSearchBackend.php', 'OCA\\DAV\\Files\\FilesHome' => $baseDir . '/../lib/Files/FilesHome.php', @@ -133,6 +141,7 @@ return array( 'OCA\\DAV\\Migration\\Version1004Date20170919104507' => $baseDir . '/../lib/Migration/Version1004Date20170919104507.php', 'OCA\\DAV\\Migration\\Version1004Date20170924124212' => $baseDir . '/../lib/Migration/Version1004Date20170924124212.php', 'OCA\\DAV\\Migration\\Version1004Date20170926103422' => $baseDir . '/../lib/Migration/Version1004Date20170926103422.php', + 'OCA\\DAV\\Migration\\Version1005Date20180413093149' => $baseDir . '/../lib/Migration/Version1005Date20180413093149.php', 'OCA\\DAV\\RootCollection' => $baseDir . '/../lib/RootCollection.php', 'OCA\\DAV\\Server' => $baseDir . '/../lib/Server.php', 'OCA\\DAV\\Settings\\CalDAVSettings' => $baseDir . '/../lib/Settings/CalDAVSettings.php', diff --git a/apps/dav/composer/composer/autoload_static.php b/apps/dav/composer/composer/autoload_static.php index 4be9b759695..760ca3426f7 100644 --- a/apps/dav/composer/composer/autoload_static.php +++ b/apps/dav/composer/composer/autoload_static.php @@ -26,6 +26,7 @@ class ComposerStaticInitDAV 'OCA\\DAV\\Avatars\\AvatarHome' => __DIR__ . '/..' . '/../lib/Avatars/AvatarHome.php', 'OCA\\DAV\\Avatars\\AvatarNode' => __DIR__ . '/..' . '/../lib/Avatars/AvatarNode.php', 'OCA\\DAV\\Avatars\\RootCollection' => __DIR__ . '/..' . '/../lib/Avatars/RootCollection.php', + 'OCA\\DAV\\BackgroundJob\\CleanupDirectLinksJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/CleanupDirectLinksJob.php', 'OCA\\DAV\\BackgroundJob\\GenerateBirthdayCalendarBackgroundJob' => __DIR__ . '/..' . '/../lib/BackgroundJob/GenerateBirthdayCalendarBackgroundJob.php', 'OCA\\DAV\\CalDAV\\Activity\\Backend' => __DIR__ . '/..' . '/../lib/CalDAV/Activity/Backend.php', 'OCA\\DAV\\CalDAV\\Activity\\Filter\\Calendar' => __DIR__ . '/..' . '/../lib/CalDAV/Activity/Filter/Calendar.php', @@ -124,6 +125,7 @@ class ComposerStaticInitDAV 'OCA\\DAV\\Connector\\Sabre\\TagList' => __DIR__ . '/..' . '/../lib/Connector/Sabre/TagList.php', 'OCA\\DAV\\Connector\\Sabre\\TagsPlugin' => __DIR__ . '/..' . '/../lib/Connector/Sabre/TagsPlugin.php', 'OCA\\DAV\\Controller\\BirthdayCalendarController' => __DIR__ . '/..' . '/../lib/Controller/BirthdayCalendarController.php', + 'OCA\\DAV\\Controller\\DirectController' => __DIR__ . '/..' . '/../lib/Controller/DirectController.php', 'OCA\\DAV\\DAV\\CustomPropertiesBackend' => __DIR__ . '/..' . '/../lib/DAV/CustomPropertiesBackend.php', 'OCA\\DAV\\DAV\\GroupPrincipalBackend' => __DIR__ . '/..' . '/../lib/DAV/GroupPrincipalBackend.php', 'OCA\\DAV\\DAV\\PublicAuth' => __DIR__ . '/..' . '/../lib/DAV/PublicAuth.php', @@ -133,6 +135,12 @@ class ComposerStaticInitDAV 'OCA\\DAV\\DAV\\Sharing\\Xml\\Invite' => __DIR__ . '/..' . '/../lib/DAV/Sharing/Xml/Invite.php', 'OCA\\DAV\\DAV\\Sharing\\Xml\\ShareRequest' => __DIR__ . '/..' . '/../lib/DAV/Sharing/Xml/ShareRequest.php', 'OCA\\DAV\\DAV\\SystemPrincipalBackend' => __DIR__ . '/..' . '/../lib/DAV/SystemPrincipalBackend.php', + 'OCA\\DAV\\Db\\Direct' => __DIR__ . '/..' . '/../lib/Db/Direct.php', + 'OCA\\DAV\\Db\\DirectMapper' => __DIR__ . '/..' . '/../lib/Db/DirectMapper.php', + 'OCA\\DAV\\Direct\\DirectFile' => __DIR__ . '/..' . '/../lib/Direct/DirectFile.php', + 'OCA\\DAV\\Direct\\DirectHome' => __DIR__ . '/..' . '/../lib/Direct/DirectHome.php', + 'OCA\\DAV\\Direct\\Server' => __DIR__ . '/..' . '/../lib/Direct/Server.php', + 'OCA\\DAV\\Direct\\ServerFactory' => __DIR__ . '/..' . '/../lib/Direct/ServerFactory.php', 'OCA\\DAV\\Files\\BrowserErrorPagePlugin' => __DIR__ . '/..' . '/../lib/Files/BrowserErrorPagePlugin.php', 'OCA\\DAV\\Files\\FileSearchBackend' => __DIR__ . '/..' . '/../lib/Files/FileSearchBackend.php', 'OCA\\DAV\\Files\\FilesHome' => __DIR__ . '/..' . '/../lib/Files/FilesHome.php', @@ -148,6 +156,7 @@ class ComposerStaticInitDAV 'OCA\\DAV\\Migration\\Version1004Date20170919104507' => __DIR__ . '/..' . '/../lib/Migration/Version1004Date20170919104507.php', 'OCA\\DAV\\Migration\\Version1004Date20170924124212' => __DIR__ . '/..' . '/../lib/Migration/Version1004Date20170924124212.php', 'OCA\\DAV\\Migration\\Version1004Date20170926103422' => __DIR__ . '/..' . '/../lib/Migration/Version1004Date20170926103422.php', + 'OCA\\DAV\\Migration\\Version1005Date20180413093149' => __DIR__ . '/..' . '/../lib/Migration/Version1005Date20180413093149.php', 'OCA\\DAV\\RootCollection' => __DIR__ . '/..' . '/../lib/RootCollection.php', 'OCA\\DAV\\Server' => __DIR__ . '/..' . '/../lib/Server.php', 'OCA\\DAV\\Settings\\CalDAVSettings' => __DIR__ . '/..' . '/../lib/Settings/CalDAVSettings.php', diff --git a/apps/dav/lib/BackgroundJob/CleanupDirectLinksJob.php b/apps/dav/lib/BackgroundJob/CleanupDirectLinksJob.php new file mode 100644 index 00000000000..ba1049071ad --- /dev/null +++ b/apps/dav/lib/BackgroundJob/CleanupDirectLinksJob.php @@ -0,0 +1,50 @@ +<?php +declare(strict_types=1); +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\DAV\BackgroundJob; + +use OC\BackgroundJob\TimedJob; +use OCA\DAV\Db\DirectMapper; +use OCP\AppFramework\Utility\ITimeFactory; + +class CleanupDirectLinksJob extends TimedJob { + /** @var ITimeFactory */ + private $timeFactory; + + /** @var DirectMapper */ + private $mapper; + + public function __construct(ITimeFactory $timeFactory, DirectMapper $mapper) { + $this->setInterval(60*60*24); + + $this->timeFactory = $timeFactory; + $this->mapper = $mapper; + } + + protected function run($argument) { + // Delete all shares expired 24 hours ago + $this->mapper->deleteExpired($this->timeFactory->getTime() - 60*60*24); + } + +} diff --git a/apps/dav/lib/Connector/Sabre/Directory.php b/apps/dav/lib/Connector/Sabre/Directory.php index a7b8ea1755e..aaf5f54ec26 100644 --- a/apps/dav/lib/Connector/Sabre/Directory.php +++ b/apps/dav/lib/Connector/Sabre/Directory.php @@ -28,6 +28,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OCA\DAV\Connector\Sabre; use OC\Files\View; @@ -147,8 +148,16 @@ class Directory extends \OCA\DAV\Connector\Sabre\Node $info = new \OC\Files\FileInfo($path, null, null, [], null); } $node = new \OCA\DAV\Connector\Sabre\File($this->fileView, $info); + + // only allow 1 process to upload a file at once but still allow reading the file while writing the part file $node->acquireLock(ILockingProvider::LOCK_SHARED); - return $node->put($data); + $this->fileView->lockFile($path . '.upload.part', ILockingProvider::LOCK_EXCLUSIVE); + + $result = $node->put($data); + + $this->fileView->unlockFile($path . '.upload.part', ILockingProvider::LOCK_EXCLUSIVE); + $node->releaseLock(ILockingProvider::LOCK_SHARED); + return $result; } catch (\OCP\Files\StorageNotAvailableException $e) { throw new \Sabre\DAV\Exception\ServiceUnavailable($e->getMessage(), $e->getCode(), $e); } catch (InvalidPathException $ex) { diff --git a/apps/dav/lib/Connector/Sabre/File.php b/apps/dav/lib/Connector/Sabre/File.php index 45b31c2e11d..bbd6717d94f 100644 --- a/apps/dav/lib/Connector/Sabre/File.php +++ b/apps/dav/lib/Connector/Sabre/File.php @@ -51,6 +51,7 @@ use OCP\Files\InvalidContentException; use OCP\Files\InvalidPathException; use OCP\Files\LockNotAcquiredException; use OCP\Files\NotPermittedException; +use OCP\Files\Storage; use OCP\Files\StorageNotAvailableException; use OCP\Lock\ILockingProvider; use OCP\Lock\LockedException; @@ -135,8 +136,9 @@ class File extends Node implements IFile { } } + /** @var Storage $partStorage */ list($partStorage) = $this->fileView->resolvePath($this->path); - $needsPartFile = $this->needsPartFile($partStorage) && (strlen($this->path) > 1); + $needsPartFile = $partStorage->needsPartFile() && (strlen($this->path) > 1); if ($needsPartFile) { // mark file as partial while uploading (ignored by the scanner) @@ -443,8 +445,9 @@ class File extends Node implements IFile { } if ($chunk_handler->isComplete()) { + /** @var Storage $storage */ list($storage,) = $this->fileView->resolvePath($path); - $needsPartFile = $this->needsPartFile($storage); + $needsPartFile = $storage->needsPartFile(); $partFile = null; $targetPath = $path . '/' . $info['name']; @@ -530,21 +533,6 @@ class File extends Node implements IFile { } /** - * Returns whether a part file is needed for the given storage - * or whether the file can be assembled/uploaded directly on the - * target storage. - * - * @param \OCP\Files\Storage $storage - * @return bool true if the storage needs part file handling - */ - private function needsPartFile($storage) { - // TODO: in the future use ChunkHandler provided by storage - return !$storage->instanceOfStorage('OCA\Files_Sharing\External\Storage') && - !$storage->instanceOfStorage('OC\Files\Storage\OwnCloud') && - $storage->needsPartFile(); - } - - /** * Convert the given exception to a SabreException instance * * @param \Exception $e diff --git a/apps/dav/lib/Connector/Sabre/Node.php b/apps/dav/lib/Connector/Sabre/Node.php index fd237b604a9..9e78d21a39d 100644 --- a/apps/dav/lib/Connector/Sabre/Node.php +++ b/apps/dav/lib/Connector/Sabre/Node.php @@ -272,7 +272,7 @@ abstract class Node implements \Sabre\DAV\INode { $mountpointpath = substr($mountpointpath, 0, -1); } - if ($mountpointpath === $this->info->getPath()) { + if (!$mountpoint->getOption('readonly', false) && $mountpointpath === $this->info->getPath()) { $permissions |= \OCP\Constants::PERMISSION_DELETE | \OCP\Constants::PERMISSION_UPDATE; } } diff --git a/apps/dav/lib/Controller/DirectController.php b/apps/dav/lib/Controller/DirectController.php new file mode 100644 index 00000000000..2a14e4db2c7 --- /dev/null +++ b/apps/dav/lib/Controller/DirectController.php @@ -0,0 +1,113 @@ +<?php +declare(strict_types=1); +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\DAV\Controller; + +use OCA\DAV\Db\Direct; +use OCA\DAV\Db\DirectMapper; +use OCP\AppFramework\Http\DataResponse; +use OCP\AppFramework\OCS\OCSBadRequestException; +use OCP\AppFramework\OCS\OCSNotFoundException; +use OCP\AppFramework\OCSController; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Files\File; +use OCP\Files\IRootFolder; +use OCP\IRequest; +use OCP\IURLGenerator; +use OCP\Security\ISecureRandom; + +class DirectController extends OCSController { + + /** @var IRootFolder */ + private $rootFolder; + + /** @var string */ + private $userId; + + /** @var DirectMapper */ + private $mapper; + + /** @var ISecureRandom */ + private $random; + + /** @var ITimeFactory */ + private $timeFactory; + + /** @var IURLGenerator */ + private $urlGenerator; + + + public function __construct(string $appName, + IRequest $request, + IRootFolder $rootFolder, + string $userId, + DirectMapper $mapper, + ISecureRandom $random, + ITimeFactory $timeFactory, + IURLGenerator $urlGenerator) { + parent::__construct($appName, $request); + + $this->rootFolder = $rootFolder; + $this->userId = $userId; + $this->mapper = $mapper; + $this->random = $random; + $this->timeFactory = $timeFactory; + $this->urlGenerator = $urlGenerator; + } + + /** + * @NoAdminRequired + */ + public function getUrl(int $fileId): DataResponse { + $userFolder = $this->rootFolder->getUserFolder($this->userId); + + $files = $userFolder->getById($fileId); + + if ($files === []) { + throw new OCSNotFoundException(); + } + + $file = array_shift($files); + if (!($file instanceof File)) { + throw new OCSBadRequestException('Direct download only works for files'); + } + + //TODO: at some point we should use the directdownlaod function of storages + $direct = new Direct(); + $direct->setUserId($this->userId); + $direct->setFileId($fileId); + + $token = $this->random->generate(60, ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS); + $direct->setToken($token); + $direct->setExpiration($this->timeFactory->getTime() + 60 * 60 * 8); + + $this->mapper->insert($direct); + + $url = $this->urlGenerator->getAbsoluteURL('remote.php/direct/'.$token); + + return new DataResponse([ + 'url' => $url, + ]); + } +} diff --git a/apps/dav/lib/Db/Direct.php b/apps/dav/lib/Db/Direct.php new file mode 100644 index 00000000000..ef588b1ec3a --- /dev/null +++ b/apps/dav/lib/Db/Direct.php @@ -0,0 +1,58 @@ +<?php +declare(strict_types=1); +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\DAV\Db; + +use OCP\AppFramework\Db\Entity; + +/** + * @method string getUserId() + * @method void setUserId(string $userId) + * @method int getFileId() + * @method void setFileId(int $fileId) + * @method string getToken() + * @method void setToken(string $token) + * @method int getExpiration() + * @method void setExpiration(int $expiration) + */ +class Direct extends Entity { + /** @var string */ + protected $userId; + + /** @var int */ + protected $fileId; + + /** @var string */ + protected $token; + + /** @var int */ + protected $expiration; + + public function __construct() { + $this->addType('userId', 'string'); + $this->addType('fileId', 'int'); + $this->addType('token', 'string'); + $this->addType('expiration', 'int'); + } +} diff --git a/apps/dav/lib/Db/DirectMapper.php b/apps/dav/lib/Db/DirectMapper.php new file mode 100644 index 00000000000..d0db4b82879 --- /dev/null +++ b/apps/dav/lib/Db/DirectMapper.php @@ -0,0 +1,72 @@ +<?php +declare(strict_types=1); +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\DAV\Db; + +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Db\Mapper; +use OCP\IDBConnection; + +class DirectMapper extends Mapper { + + public function __construct(IDBConnection $db) { + parent::__construct($db, 'directlink', Direct::class); + } + + /** + * @param string $token + * @return Direct + * @throws DoesNotExistException + */ + public function getByToken(string $token): Direct { + $qb = $this->db->getQueryBuilder(); + + $qb->select('*') + ->from('directlink') + ->where( + $qb->expr()->eq('token', $qb->createNamedParameter($token)) + ); + + $cursor = $qb->execute(); + $data = $cursor->fetch(); + $cursor->closeCursor(); + + if ($data === false) { + throw new DoesNotExistException('Direct link with token does not exist'); + } + + return Direct::fromRow($data); + } + + public function deleteExpired(int $expiration) { + $qb = $this->db->getQueryBuilder(); + + $qb->delete('directlink') + ->where( + $qb->expr()->lt('expiration', $qb->createNamedParameter($expiration)) + ); + + $qb->execute(); + } +} diff --git a/apps/dav/lib/Direct/DirectFile.php b/apps/dav/lib/Direct/DirectFile.php new file mode 100644 index 00000000000..947352c5148 --- /dev/null +++ b/apps/dav/lib/Direct/DirectFile.php @@ -0,0 +1,110 @@ +<?php +declare(strict_types=1); +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\DAV\Direct; + +use OCA\DAV\Db\Direct; +use OCP\Files\File; +use OCP\Files\IRootFolder; +use Sabre\DAV\Exception\Forbidden; +use Sabre\DAV\Exception\NotFound; +use Sabre\DAV\IFile; + +class DirectFile implements IFile { + /** @var Direct */ + private $direct; + + /** @var IRootFolder */ + private $rootFolder; + + /** @var File */ + private $file; + + public function __construct(Direct $direct, IRootFolder $rootFolder) { + $this->direct = $direct; + $this->rootFolder = $rootFolder; + } + + public function put($data) { + throw new Forbidden(); + } + + public function get() { + $this->getFile(); + + return $this->file->fopen('rb'); + } + + public function getContentType() { + $this->getFile(); + + return $this->file->getMimeType(); + } + + public function getETag() { + $this->getFile(); + + return $this->file->getEtag(); + } + + public function getSize() { + $this->getFile(); + + return $this->file->getSize(); + } + + public function delete() { + throw new Forbidden(); + } + + public function getName() { + return $this->direct->getToken(); + } + + public function setName($name) { + throw new Forbidden(); + } + + public function getLastModified() { + $this->getFile(); + + return $this->file->getMTime(); + } + + private function getFile() { + if ($this->file === null) { + $userFolder = $this->rootFolder->getUserFolder($this->direct->getUserId()); + $files = $userFolder->getById($this->direct->getFileId()); + + if ($files === []) { + throw new NotFound(); + } + + $this->file = array_shift($files); + } + + return $this->file; + } + +} diff --git a/apps/dav/lib/Direct/DirectHome.php b/apps/dav/lib/Direct/DirectHome.php new file mode 100644 index 00000000000..e0246c83de9 --- /dev/null +++ b/apps/dav/lib/Direct/DirectHome.php @@ -0,0 +1,118 @@ +<?php +declare(strict_types=1); +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\DAV\Direct; + +use OC\Security\Bruteforce\Throttler; +use OCA\DAV\Db\DirectMapper; +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Files\IRootFolder; +use OCP\IRequest; +use Sabre\DAV\Exception\Forbidden; +use Sabre\DAV\Exception\MethodNotAllowed; +use Sabre\DAV\Exception\NotFound; +use Sabre\DAV\ICollection; + +class DirectHome implements ICollection { + + /** @var IRootFolder */ + private $rootFolder; + + /** @var DirectMapper */ + private $mapper; + + /** @var ITimeFactory */ + private $timeFactory; + + /** @var Throttler */ + private $throttler; + + /** @var IRequest */ + private $request; + + public function __construct(IRootFolder $rootFolder, + DirectMapper $mapper, + ITimeFactory $timeFactory, + Throttler $throttler, + IRequest $request) { + $this->rootFolder = $rootFolder; + $this->mapper = $mapper; + $this->timeFactory = $timeFactory; + $this->throttler = $throttler; + $this->request = $request; + } + + public function createFile($name, $data = null) { + throw new Forbidden(); + } + + public function createDirectory($name) { + throw new Forbidden(); + } + + public function getChild($name): DirectFile { + try { + $direct = $this->mapper->getByToken($name); + + // Expired + if ($direct->getExpiration() < $this->timeFactory->getTime()) { + throw new NotFound(); + } + + return new DirectFile($direct, $this->rootFolder); + } catch (DoesNotExistException $e) { + // Since the token space is so huge only throttle on non exsisting token + $this->throttler->registerAttempt('directlink', $this->request->getRemoteAddress()); + $this->throttler->sleepDelay($this->request->getRemoteAddress(), 'directlink'); + + throw new NotFound(); + } + } + + public function getChildren() { + throw new MethodNotAllowed('Listing members of this collection is disabled'); + } + + public function childExists($name): bool { + return false; + } + + public function delete() { + throw new Forbidden(); + } + + public function getName(): string { + return 'direct'; + } + + public function setName($name) { + throw new Forbidden(); + } + + public function getLastModified(): int { + return 0; + } + +} diff --git a/apps/dav/lib/Direct/Server.php b/apps/dav/lib/Direct/Server.php new file mode 100644 index 00000000000..373dc2dca50 --- /dev/null +++ b/apps/dav/lib/Direct/Server.php @@ -0,0 +1,33 @@ +<?php +declare(strict_types=1); +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\DAV\Direct; + +class Server extends \Sabre\DAV\Server { + public function __construct($treeOrNode = null) { + parent::__construct($treeOrNode); + self::$exposeVersion = false; + $this->enablePropfindDepthInfinityf = false; + } +} diff --git a/apps/dav/lib/Direct/ServerFactory.php b/apps/dav/lib/Direct/ServerFactory.php new file mode 100644 index 00000000000..618f6889fd0 --- /dev/null +++ b/apps/dav/lib/Direct/ServerFactory.php @@ -0,0 +1,61 @@ +<?php +declare(strict_types=1); +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\DAV\Direct; + +use OC\Security\Bruteforce\Throttler; +use OCA\DAV\Db\DirectMapper; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Files\IRootFolder; +use OCP\IConfig; +use OCP\IRequest; + +class ServerFactory { + /** @var IConfig */ + private $config; + + public function __construct(IConfig $config) { + $this->config = $config; + } + + public function createServer(string $baseURI, + string $requestURI, + IRootFolder $rootFolder, + DirectMapper $mapper, + ITimeFactory $timeFactory, + Throttler $throttler, + IRequest $request): Server { + $home = new DirectHome($rootFolder, $mapper, $timeFactory, $throttler, $request); + $server = new Server($home); + + $server->httpRequest->setUrl($requestURI); + $server->setBaseUri($baseURI); + + $server->addPlugin(new \OCA\DAV\Connector\Sabre\MaintenancePlugin($this->config)); + + return $server; + + + } +} diff --git a/apps/dav/lib/Migration/Version1005Date20180413093149.php b/apps/dav/lib/Migration/Version1005Date20180413093149.php new file mode 100644 index 00000000000..cd6aeca6b48 --- /dev/null +++ b/apps/dav/lib/Migration/Version1005Date20180413093149.php @@ -0,0 +1,80 @@ +<?php +declare(strict_types=1); +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\DAV\Migration; + +use Doctrine\DBAL\Types\Type; +use OCP\DB\ISchemaWrapper; +use OCP\Migration\SimpleMigrationStep; +use OCP\Migration\IOutput; + +class Version1005Date20180413093149 extends SimpleMigrationStep { + + /** + * @param IOutput $output + * @param \Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper` + * @param array $options + * @return null|ISchemaWrapper + */ + public function changeSchema(IOutput $output, \Closure $schemaClosure, array $options) { + + /** @var ISchemaWrapper $schema */ + $schema = $schemaClosure(); + + if (!$schema->hasTable('directlink')) { + $table = $schema->createTable('directlink'); + + $table->addColumn('id',Type::BIGINT, [ + 'autoincrement' => true, + 'notnull' => true, + 'length' => 11, + 'unsigned' => true, + ]); + $table->addColumn('user_id', Type::STRING, [ + 'notnull' => false, + 'length' => 64, + ]); + $table->addColumn('file_id', Type::BIGINT, [ + 'notnull' => true, + 'length' => 11, + 'unsigned' => true, + ]); + $table->addColumn('token', Type::STRING, [ + 'notnull' => false, + 'length' => 60, + ]); + $table->addColumn('expiration', Type::BIGINT, [ + 'notnull' => true, + 'length' => 11, + 'unsigned' => true, + ]); + + $table->setPrimaryKey(['id'], 'directlink_id_idx'); + $table->addIndex(['token'], 'directlink_token_idx'); + $table->addIndex(['expiration'], 'directlink_expiration_idx'); + + return $schema; + } + } +} diff --git a/apps/dav/tests/unit/Connector/Sabre/ExceptionLoggerPluginTest.php b/apps/dav/tests/unit/Connector/Sabre/ExceptionLoggerPluginTest.php index c1d48a7ce5d..1de9333207f 100644 --- a/apps/dav/tests/unit/Connector/Sabre/ExceptionLoggerPluginTest.php +++ b/apps/dav/tests/unit/Connector/Sabre/ExceptionLoggerPluginTest.php @@ -69,7 +69,7 @@ class ExceptionLoggerPluginTest extends TestCase { }); $this->server = new Server(); - $this->logger = new TestLogger(Log\File::class, $config); + $this->logger = new TestLogger(new Log\File(\OC::$SERVERROOT.'/data/nextcloud.log', '', $config), $config); $this->plugin = new PluginToTest('unit-test', $this->logger); $this->plugin->initialize($this->server); } diff --git a/apps/dav/tests/unit/Controller/DirectControllerTest.php b/apps/dav/tests/unit/Controller/DirectControllerTest.php new file mode 100644 index 00000000000..e52c67ac30c --- /dev/null +++ b/apps/dav/tests/unit/Controller/DirectControllerTest.php @@ -0,0 +1,155 @@ +<?php +declare(strict_types=1); +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\DAV\Tests\Unit\DAV\Controller; + +use OCA\DAV\Controller\DirectController; +use OCA\DAV\Db\Direct; +use OCA\DAV\Db\DirectMapper; +use OCP\AppFramework\Http\DataResponse; +use OCP\AppFramework\OCS\OCSBadRequestException; +use OCP\AppFramework\OCS\OCSNotFoundException; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Files\File; +use OCP\Files\Folder; +use OCP\Files\IRootFolder; +use OCP\IRequest; +use OCP\IURLGenerator; +use OCP\Security\ISecureRandom; +use Test\TestCase; + +class DirectControllerTest extends TestCase { + + /** @var IRootFolder|\PHPUnit_Framework_MockObject_MockObject */ + private $rootFolder; + + /** @var DirectMapper|\PHPUnit_Framework_MockObject_MockObject */ + private $directMapper; + + /** @var ISecureRandom|\PHPUnit_Framework_MockObject_MockObject */ + private $random; + + /** @var ITimeFactory|\PHPUnit_Framework_MockObject_MockObject */ + private $timeFactory; + + /** @var IURLGenerator|\PHPUnit_Framework_MockObject_MockObject */ + private $urlGenerator; + + /** @var DirectController */ + private $controller; + + public function setUp() { + parent::setUp(); + + $this->rootFolder = $this->createMock(IRootFolder::class); + $this->directMapper = $this->createMock(DirectMapper::class); + $this->random = $this->createMock(ISecureRandom::class); + $this->timeFactory = $this->createMock(ITimeFactory::class); + $this->urlGenerator = $this->createMock(IURLGenerator::class); + + $this->controller = new DirectController( + 'dav', + $this->createMock(IRequest::class), + $this->rootFolder, + 'awesomeUser', + $this->directMapper, + $this->random, + $this->timeFactory, + $this->urlGenerator + ); + } + + public function testGetUrlNonExistingFileId() { + $userFolder = $this->createMock(Folder::class); + $this->rootFolder->method('getUserFolder') + ->with('awesomeUser') + ->willReturn($userFolder); + + $userFolder->method('getById') + ->with(101) + ->willReturn([]); + + $this->expectException(OCSNotFoundException::class); + $this->controller->getUrl(101); + } + + public function testGetUrlForFolder() { + $userFolder = $this->createMock(Folder::class); + $this->rootFolder->method('getUserFolder') + ->with('awesomeUser') + ->willReturn($userFolder); + + $folder = $this->createMock(Folder::class); + + $userFolder->method('getById') + ->with(101) + ->willReturn([$folder]); + + $this->expectException(OCSBadRequestException::class); + $this->controller->getUrl(101); + } + + public function testGetUrlValid() { + $userFolder = $this->createMock(Folder::class); + $this->rootFolder->method('getUserFolder') + ->with('awesomeUser') + ->willReturn($userFolder); + + $file = $this->createMock(File::class); + + $this->timeFactory->method('getTime') + ->willReturn(42); + + $userFolder->method('getById') + ->with(101) + ->willReturn([$file]); + + $this->random->method('generate') + ->with( + 60, + ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS + )->willReturn('superduperlongtoken'); + + $this->directMapper->expects($this->once()) + ->method('insert') + ->willReturnCallback(function (Direct $direct) { + $this->assertSame('awesomeUser', $direct->getUserId()); + $this->assertSame(101, $direct->getFileId()); + $this->assertSame('superduperlongtoken', $direct->getToken()); + $this->assertSame(42 + 60*60*8, $direct->getExpiration()); + }); + + $this->urlGenerator->method('getAbsoluteURL') + ->willReturnCallback(function(string $url) { + return 'https://my.nextcloud/'.$url; + }); + + $result = $this->controller->getUrl(101); + + $this->assertInstanceOf(DataResponse::class, $result); + $this->assertSame([ + 'url' => 'https://my.nextcloud/remote.php/direct/superduperlongtoken', + ], $result->getData()); + } +} diff --git a/apps/dav/tests/unit/Direct/DirectFileTest.php b/apps/dav/tests/unit/Direct/DirectFileTest.php new file mode 100644 index 00000000000..2203e7c7686 --- /dev/null +++ b/apps/dav/tests/unit/Direct/DirectFileTest.php @@ -0,0 +1,132 @@ +<?php +declare(strict_types=1); +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\DAV\Tests\Unit\Direct; + +use OCA\DAV\Db\Direct; +use OCA\DAV\Direct\DirectFile; +use OCP\Files\File; +use OCP\Files\Folder; +use OCP\Files\IRootFolder; +use Sabre\DAV\Exception\Forbidden; +use Test\TestCase; + +class DirectFileTest extends TestCase { + + /** @var Direct */ + private $direct; + + /** @var IRootFolder|\PHPUnit_Framework_MockObject_MockObject */ + private $rootFolder; + + /** @var Folder|\PHPUnit_Framework_MockObject_MockObject */ + private $userFolder; + + /** @var File|\PHPUnit_Framework_MockObject_MockObject */ + private $file; + + /** @var DirectFile */ + private $directFile; + + public function setUp() { + parent::setUp(); + + $this->direct = Direct::fromParams([ + 'userId' => 'directUser', + 'token' => 'directToken', + 'fileId' => 42, + ]); + + $this->rootFolder = $this->createMock(IRootFolder::class); + + $this->userFolder = $this->createMock(Folder::class); + $this->rootFolder->method('getUserFolder') + ->with('directUser') + ->willReturn($this->userFolder); + + $this->file = $this->createMock(File::class); + $this->userFolder->method('getById') + ->with(42) + ->willReturn([$this->file]); + + $this->directFile = new DirectFile($this->direct, $this->rootFolder); + } + + public function testPut() { + $this->expectException(Forbidden::class); + + $this->directFile->put('foo'); + } + + public function testGet() { + $this->file->expects($this->once()) + ->method('fopen') + ->with('rb'); + $this->directFile->get(); + } + + public function testGetContentType() { + $this->file->method('getMimeType') + ->willReturn('direct/type'); + + $this->assertSame('direct/type', $this->directFile->getContentType()); + } + + public function testGetETag() { + $this->file->method('getEtag') + ->willReturn('directEtag'); + + $this->assertSame('directEtag', $this->directFile->getETag()); + } + + public function testGetSize() { + $this->file->method('getSize') + ->willReturn(42); + + $this->assertSame(42, $this->directFile->getSize()); + } + + public function testDelete() { + $this->expectException(Forbidden::class); + + $this->directFile->delete(); + } + + public function testGetName() { + $this->assertSame('directToken', $this->directFile->getName()); + } + + public function testSetName() { + $this->expectException(Forbidden::class); + + $this->directFile->setName('foobar'); + } + + public function testGetLastModified() { + $this->file->method('getMTime') + ->willReturn(42); + + $this->assertSame(42, $this->directFile->getLastModified()); + } +} diff --git a/apps/dav/tests/unit/Direct/DirectHomeTest.php b/apps/dav/tests/unit/Direct/DirectHomeTest.php new file mode 100644 index 00000000000..dbbfb1fe1ff --- /dev/null +++ b/apps/dav/tests/unit/Direct/DirectHomeTest.php @@ -0,0 +1,182 @@ +<?php +declare(strict_types=1); +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\DAV\Tests\Unit\Direct; + +use OC\Security\Bruteforce\Throttler; +use OCA\DAV\Db\Direct; +use OCA\DAV\Db\DirectMapper; +use OCA\DAV\Direct\DirectFile; +use OCA\DAV\Direct\DirectHome; +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Files\IRootFolder; +use OCP\IRequest; +use Sabre\DAV\Exception\Forbidden; +use Sabre\DAV\Exception\MethodNotAllowed; +use Sabre\DAV\Exception\NotFound; +use Test\TestCase; + +class DirectHomeTest extends TestCase { + + /** @var DirectMapper|\PHPUnit_Framework_MockObject_MockObject */ + private $directMapper; + + /** @var IRootFolder|\PHPUnit_Framework_MockObject_MockObject */ + private $rootFolder; + + /** @var ITimeFactory|\PHPUnit_Framework_MockObject_MockObject */ + private $timeFactory; + + /** @var Throttler|\PHPUnit_Framework_MockObject_MockObject */ + private $throttler; + + /** @var IRequest */ + private $request; + + /** @var DirectHome */ + private $directHome; + + public function setUp() { + parent::setUp(); + + $this->directMapper = $this->createMock(DirectMapper::class); + $this->rootFolder = $this->createMock(IRootFolder::class); + $this->timeFactory = $this->createMock(ITimeFactory::class); + $this->throttler = $this->createMock(Throttler::class); + $this->request = $this->createMock(IRequest::class); + + $this->timeFactory->method('getTime') + ->willReturn(42); + + $this->request->method('getRemoteAddress') + ->willReturn('1.2.3.4'); + + $this->directHome = new DirectHome( + $this->rootFolder, + $this->directMapper, + $this->timeFactory, + $this->throttler, + $this->request + ); + } + + public function testCreateFile() { + $this->expectException(Forbidden::class); + + $this->directHome->createFile('foo', 'bar'); + } + + public function testCreateDirectory() { + $this->expectException(Forbidden::class); + + $this->directHome->createDirectory('foo'); + } + + public function testGetChildren() { + $this->expectException(MethodNotAllowed::class); + + $this->directHome->getChildren(); + } + + public function testChildExists() { + $this->assertFalse($this->directHome->childExists('foo')); + } + + public function testDelete() { + $this->expectException(Forbidden::class); + + $this->directHome->delete(); + } + + public function testGetName() { + $this->assertSame('direct', $this->directHome->getName()); + } + + public function testSetName() { + $this->expectException(Forbidden::class); + + $this->directHome->setName('foo'); + } + + public function testGetLastModified() { + $this->assertSame(0, $this->directHome->getLastModified()); + } + + public function testGetChildValid() { + $direct = Direct::fromParams([ + 'expiration' => 100, + ]); + + $this->directMapper->method('getByToken') + ->with('longtoken') + ->willReturn($direct); + + $this->throttler->expects($this->never()) + ->method($this->anything()); + + $result = $this->directHome->getChild('longtoken'); + $this->assertInstanceOf(DirectFile::class, $result); + } + + public function testGetChildExpired() { + $direct = Direct::fromParams([ + 'expiration' => 41, + ]); + + $this->directMapper->method('getByToken') + ->with('longtoken') + ->willReturn($direct); + + $this->throttler->expects($this->never()) + ->method($this->anything()); + + $this->expectException(NotFound::class); + + $this->directHome->getChild('longtoken'); + } + + public function testGetChildInvalid() { + $this->directMapper->method('getByToken') + ->with('longtoken') + ->willThrowException(new DoesNotExistException('not found')); + + $this->throttler->expects($this->once()) + ->method('registerAttempt') + ->with( + 'directlink', + '1.2.3.4' + ); + $this->throttler->expects($this->once()) + ->method('sleepDelay') + ->with( + '1.2.3.4', + 'directlink' + ); + + $this->expectException(NotFound::class); + + $this->directHome->getChild('longtoken'); + } +} diff --git a/apps/files/css/files.scss b/apps/files/css/files.scss index b89332248f7..d2d810c1e6c 100644 --- a/apps/files/css/files.scss +++ b/apps/files/css/files.scss @@ -105,9 +105,6 @@ .nav-icon-trashbin { background-image: url('../img/delete.svg?v=1'); } -.nav-icon-quota { - background-image: url('../img/quota.svg?v=1'); -} #app-navigation .nav-files a.nav-icon-files { width: auto; diff --git a/apps/files/templates/appnavigation.php b/apps/files/templates/appnavigation.php index a85e2515a92..c811ace8abe 100644 --- a/apps/files/templates/appnavigation.php +++ b/apps/files/templates/appnavigation.php @@ -15,7 +15,7 @@ if ($_['quota'] !== \OCP\Files\FileInfo::SPACE_UNLIMITED) { ?>has-tooltip" title="<?php p($_['usage_relative'] . '%'); } ?>"> - <a href="#" class="nav-icon-quota svg"> + <a href="#" class="icon-quota svg"> <p id="quotatext"><?php if ($_['quota'] !== \OCP\Files\FileInfo::SPACE_UNLIMITED) { p($l->t('%s of %s used', [$_['usage'], $_['total_space']])); diff --git a/apps/files_external/js/settings.js b/apps/files_external/js/settings.js index ffe1e972071..983348397d1 100644 --- a/apps/files_external/js/settings.js +++ b/apps/files_external/js/settings.js @@ -48,6 +48,12 @@ var MOUNT_OPTIONS_DROPDOWN_TEMPLATE = ' <label for="mountOptionsEncoding">{{mountOptionsEncodingLabel}}</label>'+ ' </span>'+ ' </li>'+ + ' <li class="optionRow">' + + ' <span class="menuitem">' + + ' <input id="mountOptionsReadOnly" class="checkbox" name="readonly" type="checkbox" value="true"/>' + + ' <label for="mountOptionsReadOnly">{{t "files_external" "Read only"}}</label>' + + ' </span>' + + ' </li>' + ' </ul>'+ '</div>'; @@ -916,7 +922,8 @@ MountConfigListView.prototype = _.extend({ 'previews': true, 'enable_sharing': false, 'filesystem_check_changes': 1, - 'encoding_compatibility': false + 'encoding_compatibility': false, + 'readonly': false, })); } @@ -1303,7 +1310,8 @@ MountConfigListView.prototype = _.extend({ 'previews', 'filesystem_check_changes', 'enable_sharing', - 'encoding_compatibility' + 'encoding_compatibility', + 'readonly' ]; if (this._encryptionEnabled) { visibleOptions.push('encrypt'); diff --git a/apps/files_external/l10n/de.js b/apps/files_external/l10n/de.js index 6fafc3e5489..eefbbe8c409 100644 --- a/apps/files_external/l10n/de.js +++ b/apps/files_external/l10n/de.js @@ -112,6 +112,7 @@ OC.L10N.register( "Check for changes" : "Auf Änderungen prüfen", "Never" : "Nie", "Once every direct access" : "Einmal bei jedem Direktzugriff", + "Read only" : "Schreibgeschützt", "Folder name" : "Ordnername", "External storage" : "Externer Speicher", "Authentication" : "Authentifizierung", diff --git a/apps/files_external/l10n/de.json b/apps/files_external/l10n/de.json index 89f089605b9..50c07260636 100644 --- a/apps/files_external/l10n/de.json +++ b/apps/files_external/l10n/de.json @@ -110,6 +110,7 @@ "Check for changes" : "Auf Änderungen prüfen", "Never" : "Nie", "Once every direct access" : "Einmal bei jedem Direktzugriff", + "Read only" : "Schreibgeschützt", "Folder name" : "Ordnername", "External storage" : "Externer Speicher", "Authentication" : "Authentifizierung", diff --git a/apps/files_external/l10n/de_DE.js b/apps/files_external/l10n/de_DE.js index ca2d37523b2..13e44ab0cb9 100644 --- a/apps/files_external/l10n/de_DE.js +++ b/apps/files_external/l10n/de_DE.js @@ -112,6 +112,7 @@ OC.L10N.register( "Check for changes" : "Auf Änderungen prüfen", "Never" : "Nie", "Once every direct access" : "Einmal bei jedem Direktzugriff", + "Read only" : "Schreibgeschützt", "Folder name" : "Ordnername", "External storage" : "Externer Speicher", "Authentication" : "Authentifizierung", diff --git a/apps/files_external/l10n/de_DE.json b/apps/files_external/l10n/de_DE.json index 4ab53e25cbc..1f19a325427 100644 --- a/apps/files_external/l10n/de_DE.json +++ b/apps/files_external/l10n/de_DE.json @@ -110,6 +110,7 @@ "Check for changes" : "Auf Änderungen prüfen", "Never" : "Nie", "Once every direct access" : "Einmal bei jedem Direktzugriff", + "Read only" : "Schreibgeschützt", "Folder name" : "Ordnername", "External storage" : "Externer Speicher", "Authentication" : "Authentifizierung", diff --git a/apps/files_external/l10n/fr.js b/apps/files_external/l10n/fr.js index 7533b976a14..a3b12150d45 100644 --- a/apps/files_external/l10n/fr.js +++ b/apps/files_external/l10n/fr.js @@ -112,6 +112,7 @@ OC.L10N.register( "Check for changes" : "Rechercher les modifications", "Never" : "Jamais", "Once every direct access" : "Une fois à chaque accès direct", + "Read only" : "Lecture seule", "Folder name" : "Nom du dossier", "External storage" : "Stockage externe", "Authentication" : "Authentification", diff --git a/apps/files_external/l10n/fr.json b/apps/files_external/l10n/fr.json index 73b8bc28c5b..8b0bc077c0e 100644 --- a/apps/files_external/l10n/fr.json +++ b/apps/files_external/l10n/fr.json @@ -110,6 +110,7 @@ "Check for changes" : "Rechercher les modifications", "Never" : "Jamais", "Once every direct access" : "Une fois à chaque accès direct", + "Read only" : "Lecture seule", "Folder name" : "Nom du dossier", "External storage" : "Stockage externe", "Authentication" : "Authentification", diff --git a/apps/files_external/l10n/it.js b/apps/files_external/l10n/it.js index 26cf94da6d5..f8ac97f6f68 100644 --- a/apps/files_external/l10n/it.js +++ b/apps/files_external/l10n/it.js @@ -112,6 +112,7 @@ OC.L10N.register( "Check for changes" : "Controlla le modifiche", "Never" : "Mai", "Once every direct access" : "Una volta per ogni accesso diretto", + "Read only" : "Sola lettura", "Folder name" : "Nome della cartella", "External storage" : "Archiviazione esterna", "Authentication" : "Autenticazione", diff --git a/apps/files_external/l10n/it.json b/apps/files_external/l10n/it.json index a8151db2395..6495f32fc8d 100644 --- a/apps/files_external/l10n/it.json +++ b/apps/files_external/l10n/it.json @@ -110,6 +110,7 @@ "Check for changes" : "Controlla le modifiche", "Never" : "Mai", "Once every direct access" : "Una volta per ogni accesso diretto", + "Read only" : "Sola lettura", "Folder name" : "Nome della cartella", "External storage" : "Archiviazione esterna", "Authentication" : "Autenticazione", diff --git a/apps/files_external/l10n/pt_BR.js b/apps/files_external/l10n/pt_BR.js index f1624a0f9ea..a04fc14c8a3 100644 --- a/apps/files_external/l10n/pt_BR.js +++ b/apps/files_external/l10n/pt_BR.js @@ -112,6 +112,7 @@ OC.L10N.register( "Check for changes" : "Verifique se há alterações", "Never" : "Nunca", "Once every direct access" : "Uma vez a cada acesso direto", + "Read only" : "Somente leitura", "Folder name" : "Nome da pasta", "External storage" : "Armazenamento Externo", "Authentication" : "Autenticação", diff --git a/apps/files_external/l10n/pt_BR.json b/apps/files_external/l10n/pt_BR.json index 80d6ba18e09..bfe10a37539 100644 --- a/apps/files_external/l10n/pt_BR.json +++ b/apps/files_external/l10n/pt_BR.json @@ -110,6 +110,7 @@ "Check for changes" : "Verifique se há alterações", "Never" : "Nunca", "Once every direct access" : "Uma vez a cada acesso direto", + "Read only" : "Somente leitura", "Folder name" : "Nome da pasta", "External storage" : "Armazenamento Externo", "Authentication" : "Autenticação", diff --git a/apps/files_external/l10n/ru.js b/apps/files_external/l10n/ru.js index 39e02bdabde..fbb90ba4a89 100644 --- a/apps/files_external/l10n/ru.js +++ b/apps/files_external/l10n/ru.js @@ -112,6 +112,7 @@ OC.L10N.register( "Check for changes" : "Проверять изменения", "Never" : "Никогда", "Once every direct access" : "Каждый раз при прямом доступе", + "Read only" : "Только чтение", "Folder name" : "Имя папки", "External storage" : "Внешнее хранилище", "Authentication" : "Способ авторизации", diff --git a/apps/files_external/l10n/ru.json b/apps/files_external/l10n/ru.json index 387b9087209..ff6df5b9a55 100644 --- a/apps/files_external/l10n/ru.json +++ b/apps/files_external/l10n/ru.json @@ -110,6 +110,7 @@ "Check for changes" : "Проверять изменения", "Never" : "Никогда", "Once every direct access" : "Каждый раз при прямом доступе", + "Read only" : "Только чтение", "Folder name" : "Имя папки", "External storage" : "Внешнее хранилище", "Authentication" : "Способ авторизации", diff --git a/apps/files_external/l10n/tr.js b/apps/files_external/l10n/tr.js index 9d854556b97..bcc1c64cc0c 100644 --- a/apps/files_external/l10n/tr.js +++ b/apps/files_external/l10n/tr.js @@ -112,6 +112,7 @@ OC.L10N.register( "Check for changes" : "Değişiklikleri denetle", "Never" : "Asla", "Once every direct access" : "Her doğrudan erişimde bir kez", + "Read only" : "Salt okunur", "Folder name" : "Klasör adı", "External storage" : "Dış depolama", "Authentication" : "Kimlik Doğrulama", diff --git a/apps/files_external/l10n/tr.json b/apps/files_external/l10n/tr.json index 4376d41228c..73ebf0c7759 100644 --- a/apps/files_external/l10n/tr.json +++ b/apps/files_external/l10n/tr.json @@ -110,6 +110,7 @@ "Check for changes" : "Değişiklikleri denetle", "Never" : "Asla", "Once every direct access" : "Her doğrudan erişimde bir kez", + "Read only" : "Salt okunur", "Folder name" : "Klasör adı", "External storage" : "Dış depolama", "Authentication" : "Kimlik Doğrulama", diff --git a/apps/files_external/lib/Command/ListCommand.php b/apps/files_external/lib/Command/ListCommand.php index efb2669e281..89bdcf5e5d6 100644 --- a/apps/files_external/lib/Command/ListCommand.php +++ b/apps/files_external/lib/Command/ListCommand.php @@ -192,7 +192,8 @@ class ListCommand extends Base { 'previews' => true, 'filesystem_check_changes' => 1, 'enable_sharing' => false, - 'encoding_compatibility' => false + 'encoding_compatibility' => false, + 'readonly' => false, ]; $rows = array_map(function (StorageConfig $config) use ($userId, $defaultMountOptions, $full) { $storageConfig = $config->getBackendOptions(); diff --git a/apps/files_external/lib/Lib/Storage/OwnCloud.php b/apps/files_external/lib/Lib/Storage/OwnCloud.php index d56e6b66145..3ee2b70ef22 100644 --- a/apps/files_external/lib/Lib/Storage/OwnCloud.php +++ b/apps/files_external/lib/Lib/Storage/OwnCloud.php @@ -73,4 +73,8 @@ class OwnCloud extends \OC\Files\Storage\DAV{ parent::__construct($params); } + + public function needsPartFile() { + return false; + } } diff --git a/apps/files_external/templates/settings.php b/apps/files_external/templates/settings.php index 895be719ab4..236faf37d6d 100644 --- a/apps/files_external/templates/settings.php +++ b/apps/files_external/templates/settings.php @@ -12,6 +12,7 @@ $l->t("Check for changes"); $l->t("Never"); $l->t("Once every direct access"); + $l->t('Read only'); script('files_external', 'settings'); style('files_external', 'settings'); diff --git a/apps/files_external/tests/js/settingsSpec.js b/apps/files_external/tests/js/settingsSpec.js index 56bdcff8345..fbbb341c307 100644 --- a/apps/files_external/tests/js/settingsSpec.js +++ b/apps/files_external/tests/js/settingsSpec.js @@ -376,7 +376,8 @@ describe('OCA.External.Settings tests', function() { previews: true, enable_sharing: false, filesystem_check_changes: 0, - encoding_compatibility: false + encoding_compatibility: false, + readonly: false }); }); }); diff --git a/apps/files_sharing/lib/External/Storage.php b/apps/files_sharing/lib/External/Storage.php index 638f82f7027..a631a029aba 100644 --- a/apps/files_sharing/lib/External/Storage.php +++ b/apps/files_sharing/lib/External/Storage.php @@ -366,4 +366,7 @@ class Storage extends DAV implements ISharedStorage { return $permissions; } + public function needsPartFile() { + return false; + } } diff --git a/apps/files_sharing/tests/js/sharedfilelistSpec.js b/apps/files_sharing/tests/js/sharedfilelistSpec.js index 492a7255c61..0897e9c956d 100644 --- a/apps/files_sharing/tests/js/sharedfilelistSpec.js +++ b/apps/files_sharing/tests/js/sharedfilelistSpec.js @@ -727,7 +727,8 @@ describe('OCA.Sharing.FileList tests', function() { etag: 'abc', shareOwner: 'User One', recipients: 'User Two', - mountType: 'external-root' + mountType: 'external-root', + sharePermissions: OC.PERMISSION_READ, }]); $tr = fileList.$el.find('tr:first'); @@ -749,7 +750,8 @@ describe('OCA.Sharing.FileList tests', function() { etag: 'abc', shareOwner: 'User One', recipients: 'User Two', - mountType: 'external-root' + mountType: 'external-root', + sharePermissions: OC.PERMISSION_READ | OC.PERMISSION_SHARE, }]); $tr = fileList.$el.find('tr:first'); diff --git a/apps/files_trashbin/appinfo/info.xml b/apps/files_trashbin/appinfo/info.xml index 585e61fe1e8..4ca60c0e269 100644 --- a/apps/files_trashbin/appinfo/info.xml +++ b/apps/files_trashbin/appinfo/info.xml @@ -9,13 +9,14 @@ This application enables users to restore files that were deleted from the syste To prevent a user from running out of disk space, the Deleted files app will not utilize more than 50% of the currently available free quota for deleted files. If the deleted files exceed this limit, the app deletes the oldest files until it gets below this limit. More information is available in the Deleted Files documentation. </description> - <version>1.4.0</version> + <version>1.4.1</version> <licence>agpl</licence> <author>Bjoern Schiessle</author> <namespace>Files_Trashbin</namespace> <default_enable/> <types> <filesystem/> + <dav/> </types> <documentation> <user>user-trashbin</user> @@ -34,4 +35,13 @@ To prevent a user from running out of disk space, the Deleted files app will not <command>OCA\Files_Trashbin\Command\CleanUp</command> <command>OCA\Files_Trashbin\Command\ExpireTrash</command> </commands> + + <sabre> + <collections> + <collection>OCA\Files_Trashbin\Sabre\RootCollection</collection> + </collections> + <plugins> + <plugin>OCA\Files_Trashbin\Sabre\PropfindPlugin</plugin> + </plugins> + </sabre> </info> diff --git a/apps/files_trashbin/composer/composer/autoload_classmap.php b/apps/files_trashbin/composer/composer/autoload_classmap.php index 2e58c7b1c6c..3713de530c6 100644 --- a/apps/files_trashbin/composer/composer/autoload_classmap.php +++ b/apps/files_trashbin/composer/composer/autoload_classmap.php @@ -18,6 +18,16 @@ return array( 'OCA\\Files_Trashbin\\Expiration' => $baseDir . '/../lib/Expiration.php', 'OCA\\Files_Trashbin\\Helper' => $baseDir . '/../lib/Helper.php', 'OCA\\Files_Trashbin\\Hooks' => $baseDir . '/../lib/Hooks.php', + 'OCA\\Files_Trashbin\\Sabre\\ITrash' => $baseDir . '/../lib/Sabre/ITrash.php', + 'OCA\\Files_Trashbin\\Sabre\\PropfindPlugin' => $baseDir . '/../lib/Sabre/PropfindPlugin.php', + 'OCA\\Files_Trashbin\\Sabre\\RestoreFolder' => $baseDir . '/../lib/Sabre/RestoreFolder.php', + 'OCA\\Files_Trashbin\\Sabre\\RootCollection' => $baseDir . '/../lib/Sabre/RootCollection.php', + 'OCA\\Files_Trashbin\\Sabre\\TrashFile' => $baseDir . '/../lib/Sabre/TrashFile.php', + 'OCA\\Files_Trashbin\\Sabre\\TrashFolder' => $baseDir . '/../lib/Sabre/TrashFolder.php', + 'OCA\\Files_Trashbin\\Sabre\\TrashFolderFile' => $baseDir . '/../lib/Sabre/TrashFolderFile.php', + 'OCA\\Files_Trashbin\\Sabre\\TrashFolderFolder' => $baseDir . '/../lib/Sabre/TrashFolderFolder.php', + 'OCA\\Files_Trashbin\\Sabre\\TrashHome' => $baseDir . '/../lib/Sabre/TrashHome.php', + 'OCA\\Files_Trashbin\\Sabre\\TrashRoot' => $baseDir . '/../lib/Sabre/TrashRoot.php', 'OCA\\Files_Trashbin\\Storage' => $baseDir . '/../lib/Storage.php', 'OCA\\Files_Trashbin\\Trashbin' => $baseDir . '/../lib/Trashbin.php', ); diff --git a/apps/files_trashbin/composer/composer/autoload_static.php b/apps/files_trashbin/composer/composer/autoload_static.php index 01520e2d14d..b00778741b3 100644 --- a/apps/files_trashbin/composer/composer/autoload_static.php +++ b/apps/files_trashbin/composer/composer/autoload_static.php @@ -33,6 +33,16 @@ class ComposerStaticInitFiles_Trashbin 'OCA\\Files_Trashbin\\Expiration' => __DIR__ . '/..' . '/../lib/Expiration.php', 'OCA\\Files_Trashbin\\Helper' => __DIR__ . '/..' . '/../lib/Helper.php', 'OCA\\Files_Trashbin\\Hooks' => __DIR__ . '/..' . '/../lib/Hooks.php', + 'OCA\\Files_Trashbin\\Sabre\\ITrash' => __DIR__ . '/..' . '/../lib/Sabre/ITrash.php', + 'OCA\\Files_Trashbin\\Sabre\\PropfindPlugin' => __DIR__ . '/..' . '/../lib/Sabre/PropfindPlugin.php', + 'OCA\\Files_Trashbin\\Sabre\\RestoreFolder' => __DIR__ . '/..' . '/../lib/Sabre/RestoreFolder.php', + 'OCA\\Files_Trashbin\\Sabre\\RootCollection' => __DIR__ . '/..' . '/../lib/Sabre/RootCollection.php', + 'OCA\\Files_Trashbin\\Sabre\\TrashFile' => __DIR__ . '/..' . '/../lib/Sabre/TrashFile.php', + 'OCA\\Files_Trashbin\\Sabre\\TrashFolder' => __DIR__ . '/..' . '/../lib/Sabre/TrashFolder.php', + 'OCA\\Files_Trashbin\\Sabre\\TrashFolderFile' => __DIR__ . '/..' . '/../lib/Sabre/TrashFolderFile.php', + 'OCA\\Files_Trashbin\\Sabre\\TrashFolderFolder' => __DIR__ . '/..' . '/../lib/Sabre/TrashFolderFolder.php', + 'OCA\\Files_Trashbin\\Sabre\\TrashHome' => __DIR__ . '/..' . '/../lib/Sabre/TrashHome.php', + 'OCA\\Files_Trashbin\\Sabre\\TrashRoot' => __DIR__ . '/..' . '/../lib/Sabre/TrashRoot.php', 'OCA\\Files_Trashbin\\Storage' => __DIR__ . '/..' . '/../lib/Storage.php', 'OCA\\Files_Trashbin\\Trashbin' => __DIR__ . '/..' . '/../lib/Trashbin.php', ); diff --git a/apps/files_trashbin/lib/AppInfo/Application.php b/apps/files_trashbin/lib/AppInfo/Application.php index e9d4e6ecc1d..ea27c8c16ea 100644 --- a/apps/files_trashbin/lib/AppInfo/Application.php +++ b/apps/files_trashbin/lib/AppInfo/Application.php @@ -23,6 +23,7 @@ namespace OCA\Files_Trashbin\AppInfo; +use OCA\DAV\Connector\Sabre\Principal; use OCP\AppFramework\App; use OCA\Files_Trashbin\Expiration; use OCP\AppFramework\Utility\ITimeFactory; @@ -47,5 +48,17 @@ class Application extends App { $c->query(ITimeFactory::class) ); }); + + /* + * Register $principalBackend for the DAV collection + */ + $container->registerService('principalBackend', function () { + return new Principal( + \OC::$server->getUserManager(), + \OC::$server->getGroupManager(), + \OC::$server->getShareManager(), + \OC::$server->getUserSession() + ); + }); } } diff --git a/apps/files_trashbin/lib/Sabre/ITrash.php b/apps/files_trashbin/lib/Sabre/ITrash.php new file mode 100644 index 00000000000..b0ff2b1570a --- /dev/null +++ b/apps/files_trashbin/lib/Sabre/ITrash.php @@ -0,0 +1,32 @@ +<?php +declare(strict_types=1); +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +namespace OCA\Files_Trashbin\Sabre; + +interface ITrash { + public function restore(): bool; + + public function getFilename(): string; + + public function getOriginalLocation(): string; +} diff --git a/apps/files_trashbin/lib/Sabre/PropfindPlugin.php b/apps/files_trashbin/lib/Sabre/PropfindPlugin.php new file mode 100644 index 00000000000..e50ee08590a --- /dev/null +++ b/apps/files_trashbin/lib/Sabre/PropfindPlugin.php @@ -0,0 +1,64 @@ +<?php +declare(strict_types=1); +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\Files_Trashbin\Sabre; + +use Sabre\DAV\INode; +use Sabre\DAV\PropFind; +use Sabre\DAV\Server; +use Sabre\DAV\ServerPlugin; + +class PropfindPlugin extends ServerPlugin { + + const TRASHBIN_FILENAME = '{http://nextcloud.org/ns}trashbin-filename'; + const TRASHBIN_ORIGINAL_LOCATION = '{http://nextcloud.org/ns}trashbin-original-location'; + + /** @var Server */ + private $server; + + public function __construct() { + } + + public function initialize(Server $server) { + $this->server = $server; + + $this->server->on('propFind', [$this, 'propFind']); + } + + + public function propFind(PropFind $propFind, INode $node) { + if (!($node instanceof ITrash)) { + return; + } + + $propFind->handle(self::TRASHBIN_FILENAME, function() use ($node) { + return $node->getFilename(); + }); + + $propFind->handle(self::TRASHBIN_ORIGINAL_LOCATION, function() use ($node) { + return $node->getOriginalLocation(); + }); + } + +} diff --git a/apps/files_trashbin/lib/Sabre/RestoreFolder.php b/apps/files_trashbin/lib/Sabre/RestoreFolder.php new file mode 100644 index 00000000000..04f23db0ed4 --- /dev/null +++ b/apps/files_trashbin/lib/Sabre/RestoreFolder.php @@ -0,0 +1,86 @@ +<?php +declare(strict_types=1); +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCA\Files_Trashbin\Sabre; + +use Sabre\DAV\Exception\Forbidden; +use Sabre\DAV\ICollection; +use Sabre\DAV\IMoveTarget; +use Sabre\DAV\INode; + + +class RestoreFolder implements ICollection, IMoveTarget { + + /** @var string */ + protected $userId; + + public function __construct(string $userId) { + $this->userId = $userId; + } + + public function createFile($name, $data = null) { + throw new Forbidden(); + } + + public function createDirectory($name) { + throw new Forbidden(); + } + + public function getChild($name) { + return null; + } + + public function delete() { + throw new Forbidden(); + } + + public function getName() { + return 'restore'; + } + + public function setName($name) { + throw new Forbidden(); + } + + public function getLastModified(): int { + return 0; + } + + public function getChildren(): array { + return []; + } + + public function childExists($name): bool { + return false; + } + + public function moveInto($targetName, $sourcePath, INode $sourceNode): bool { + if (!($sourceNode instanceof ITrash)) { + return false; + } + + return $sourceNode->restore(); + } + +} diff --git a/apps/files_trashbin/lib/Sabre/RootCollection.php b/apps/files_trashbin/lib/Sabre/RootCollection.php new file mode 100644 index 00000000000..e425fb448e2 --- /dev/null +++ b/apps/files_trashbin/lib/Sabre/RootCollection.php @@ -0,0 +1,59 @@ +<?php +declare(strict_types=1); +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +namespace OCA\Files_Trashbin\Sabre; + +use Sabre\DAV\INode; +use Sabre\DAVACL\AbstractPrincipalCollection; +use Sabre\DAVACL\PrincipalBackend; + +class RootCollection extends AbstractPrincipalCollection { + + public function __construct(PrincipalBackend\BackendInterface $principalBackend) { + parent::__construct($principalBackend, 'principals/users'); + } + + /** + * This method returns a node for a principal. + * + * The passed array contains principal information, and is guaranteed to + * at least contain a uri item. Other properties may or may not be + * supplied by the authentication backend. + * + * @param array $principalInfo + * @return INode + */ + public function getChildForPrincipal(array $principalInfo): TrashHome { + list(,$name) = \Sabre\Uri\split($principalInfo['uri']); + $user = \OC::$server->getUserSession()->getUser(); + if (is_null($user) || $name !== $user->getUID()) { + throw new \Sabre\DAV\Exception\Forbidden(); + } + return new TrashHome($principalInfo); + } + + public function getName(): string { + return 'trashbin'; + } + +} diff --git a/apps/files_trashbin/lib/Sabre/TrashFile.php b/apps/files_trashbin/lib/Sabre/TrashFile.php new file mode 100644 index 00000000000..29e7a955623 --- /dev/null +++ b/apps/files_trashbin/lib/Sabre/TrashFile.php @@ -0,0 +1,91 @@ +<?php +declare(strict_types=1); +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +namespace OCA\Files_Trashbin\Sabre; + +use OCP\Files\FileInfo; +use Sabre\DAV\Exception\Forbidden; +use Sabre\DAV\IFile; + +class TrashFile implements IFile, ITrash { + /** @var string */ + private $userId; + + /** @var FileInfo */ + private $data; + + public function __construct(string $userId, FileInfo $data) { + $this->userId = $userId; + $this->data = $data; + } + + public function put($data) { + throw new Forbidden(); + } + + public function get() { + return $this->data->getStorage()->fopen($this->data->getInternalPath().'.d'.$this->getLastModified(), 'rb'); + } + + public function getContentType(): string { + return $this->data->getMimetype(); + } + + public function getETag(): string { + return $this->data->getEtag(); + } + + public function getSize(): int { + return $this->data->getSize(); + } + + public function delete() { + \OCA\Files_Trashbin\Trashbin::delete($this->data->getName(), $this->userId, $this->getLastModified()); + } + + public function getName(): string { + return $this->data->getName() . '.d' . $this->getLastModified(); + } + + public function setName($name) { + throw new Forbidden(); + } + + public function getLastModified(): int { + return $this->data->getMtime(); + } + + public function restore(): bool { + return \OCA\Files_Trashbin\Trashbin::restore($this->getName(), $this->data->getName(), $this->getLastModified()); + } + + public function getFilename(): string { + return $this->data->getName(); + } + + public function getOriginalLocation(): string { + return $this->data['extraData']; + } + + +} diff --git a/apps/files_trashbin/lib/Sabre/TrashFolder.php b/apps/files_trashbin/lib/Sabre/TrashFolder.php new file mode 100644 index 00000000000..33236eea262 --- /dev/null +++ b/apps/files_trashbin/lib/Sabre/TrashFolder.php @@ -0,0 +1,120 @@ +<?php +declare(strict_types=1); +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +namespace OCA\Files_Trashbin\Sabre; + +use OCP\Files\FileInfo; +use Sabre\DAV\Exception\Forbidden; +use Sabre\DAV\Exception\NotFound; +use Sabre\DAV\ICollection; + +class TrashFolder implements ICollection, ITrash { + /** @var string */ + private $userId; + + /** @var FileInfo */ + private $data; + + public function __construct(string $root, string $userId, FileInfo $data) { + $this->userId = $userId; + $this->data = $data; + } + + public function createFile($name, $data = null) { + throw new Forbidden(); + } + + public function createDirectory($name) { + throw new Forbidden(); + } + + public function getChild($name): ITrash { + $entries = \OCA\Files_Trashbin\Helper::getTrashFiles($this->getName(), $this->userId); + + foreach ($entries as $entry) { + if ($entry->getName() === $name) { + if ($entry->getType() === FileInfo::TYPE_FOLDER) { + return new TrashFolderFolder($this->getName(), $this->userId, $entry, $this->getOriginalLocation()); + } + return new TrashFolderFile($this->getName(), $this->userId, $entry, $this->getOriginalLocation()); + } + } + + throw new NotFound(); + } + + public function getChildren(): array { + $entries = \OCA\Files_Trashbin\Helper::getTrashFiles($this->getName(), $this->userId); + + $children = array_map(function (FileInfo $entry) { + if ($entry->getType() === FileInfo::TYPE_FOLDER) { + return new TrashFolderFolder($this->getName(), $this->userId, $entry, $this->getOriginalLocation()); + } + return new TrashFolderFile($this->getName(), $this->userId, $entry, $this->getOriginalLocation()); + }, $entries); + + return $children; + } + + public function childExists($name): bool { + $entries = \OCA\Files_Trashbin\Helper::getTrashFiles($this->getName(), $this->userId); + + foreach ($entries as $entry) { + if ($entry->getName() === $name) { + return true; + } + } + + return false; + } + + public function delete() { + \OCA\Files_Trashbin\Trashbin::delete($this->data->getName(), $this->userId, $this->getLastModified()); + } + + public function getName(): string { + return $this->data->getName() . '.d' . $this->getLastModified(); + } + + public function setName($name) { + throw new Forbidden(); + } + + public function getLastModified(): int { + return $this->data->getMtime(); + } + + public function restore(): bool { + return \OCA\Files_Trashbin\Trashbin::restore($this->getName(), $this->data->getName(), $this->getLastModified()); + } + + public function getFilename(): string { + return $this->data->getName(); + } + + public function getOriginalLocation(): string { + return $this->data['extraData']; + } + + +} diff --git a/apps/files_trashbin/lib/Sabre/TrashFolderFile.php b/apps/files_trashbin/lib/Sabre/TrashFolderFile.php new file mode 100644 index 00000000000..95e82d95a6e --- /dev/null +++ b/apps/files_trashbin/lib/Sabre/TrashFolderFile.php @@ -0,0 +1,102 @@ +<?php +declare(strict_types=1); +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +namespace OCA\Files_Trashbin\Sabre; + +use OCP\Files\FileInfo; +use Sabre\DAV\Exception\Forbidden; +use Sabre\DAV\IFile; + +class TrashFolderFile implements IFile, ITrash { + /** @var string */ + private $root; + + /** @var string */ + private $userId; + + /** @var FileInfo */ + private $data; + + /** @var string */ + private $location; + + public function __construct(string $root, + string $userId, + FileInfo $data, + string $location) { + $this->root = $root; + $this->userId = $userId; + $this->data = $data; + $this->location = $location; + } + + public function put($data) { + throw new Forbidden(); + } + + public function get() { + return $this->data->getStorage()->fopen($this->data->getInternalPath(), 'rb'); + } + + public function getContentType(): string { + return $this->data->getMimetype(); + } + + public function getETag(): string { + return $this->data->getEtag(); + } + + public function getSize(): int { + return $this->data->getSize(); + } + + public function delete() { + \OCA\Files_Trashbin\Trashbin::delete($this->root . '/' . $this->getName(), $this->userId, null); + } + + public function getName(): string { + return $this->data->getName(); + } + + public function setName($name) { + throw new Forbidden(); + } + + public function getLastModified(): int { + return $this->data->getMtime(); + } + + public function restore(): bool { + return \OCA\Files_Trashbin\Trashbin::restore($this->root . '/' . $this->getName(), $this->data->getName(), null); + } + + public function getFilename(): string { + return $this->data->getName(); + } + + public function getOriginalLocation(): string { + return $this->location . '/' . $this->getFilename(); + } + + +} diff --git a/apps/files_trashbin/lib/Sabre/TrashFolderFolder.php b/apps/files_trashbin/lib/Sabre/TrashFolderFolder.php new file mode 100644 index 00000000000..d2923c58916 --- /dev/null +++ b/apps/files_trashbin/lib/Sabre/TrashFolderFolder.php @@ -0,0 +1,133 @@ +<?php +declare(strict_types=1); +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +namespace OCA\Files_Trashbin\Sabre; + +use OCP\Files\FileInfo; +use Sabre\DAV\Exception\Forbidden; +use Sabre\DAV\Exception\NotFound; +use Sabre\DAV\ICollection; + +class TrashFolderFolder implements ICollection, ITrash { + + /** @var string */ + private $root; + + /** @var string */ + private $userId; + + /** @var FileInfo */ + private $data; + + /** @var string */ + private $location; + + public function __construct(string $root, + string $userId, + FileInfo $data, + string $location) { + $this->root = $root; + $this->userId = $userId; + $this->data = $data; + $this->location = $location; + } + + public function createFile($name, $data = null) { + throw new Forbidden(); + } + + public function createDirectory($name) { + throw new Forbidden(); + } + + public function getChild($name): ITrash { + $entries = \OCA\Files_Trashbin\Helper::getTrashFiles($this->root . '/' . $this->getName(), $this->userId); + + foreach ($entries as $entry) { + if ($entry->getName() === $name) { + if ($entry->getType() === FileInfo::TYPE_FOLDER) { + return new TrashFolderFolder($this->root . '/' . $this->getName(), $this->userId, $entry, $this->getOriginalLocation()); + } + return new TrashFolderFile($this->root . '/' . $this->getName(), $this->userId, $entry, $this->getOriginalLocation()); + } + } + + throw new NotFound(); + } + + public function getChildren(): array { + $entries = \OCA\Files_Trashbin\Helper::getTrashFiles($this->root . '/' . $this->getName(), $this->userId); + + $children = array_map(function (FileInfo $entry) { + if ($entry->getType() === FileInfo::TYPE_FOLDER) { + return new TrashFolderFolder($this->root.'/'.$this->getName(), $this->userId, $entry, $this->getOriginalLocation()); + } + return new TrashFolderFile($this->root.'/'.$this->getName(), $this->userId, $entry, $this->getOriginalLocation()); + }, $entries); + + return $children; + } + + public function childExists($name): bool { + $entries = \OCA\Files_Trashbin\Helper::getTrashFiles($this->root . '/' . $this->getName(), $this->userId); + + foreach ($entries as $entry) { + if ($entry->getName() === $name) { + return true; + } + } + + return false; + } + + public function delete() { + \OCA\Files_Trashbin\Trashbin::delete($this->root . '/' . $this->getName(), $this->userId, null); + } + + public function getName(): string { + return $this->data->getName(); + + } + + public function setName($name) { + throw new Forbidden(); + } + + public function getLastModified(): int { + return $this->data->getMtime(); + } + + public function restore(): bool { + return \OCA\Files_Trashbin\Trashbin::restore($this->root . '/' . $this->getName(), $this->data->getName(), null); + } + + public function getFilename(): string { + return $this->data->getName(); + } + + public function getOriginalLocation(): string { + return $this->location . '/' . $this->getFilename(); + } + + +} diff --git a/apps/files_trashbin/lib/Sabre/TrashHome.php b/apps/files_trashbin/lib/Sabre/TrashHome.php new file mode 100644 index 00000000000..d1c50c9c6a3 --- /dev/null +++ b/apps/files_trashbin/lib/Sabre/TrashHome.php @@ -0,0 +1,89 @@ +<?php +declare(strict_types=1); +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +namespace OCA\Files_Trashbin\Sabre; + +use Sabre\DAV\Exception\Forbidden; +use Sabre\DAV\Exception\NotFound; +use Sabre\DAV\ICollection; + +class TrashHome implements ICollection { + + /** @var array */ + private $principalInfo; + + public function __construct(array $principalInfo) { + $this->principalInfo = $principalInfo; + } + + public function delete() { + throw new Forbidden(); + } + + public function getName(): string { + list(,$name) = \Sabre\Uri\split($this->principalInfo['uri']); + return $name; + } + + public function setName($name) { + throw new Forbidden('Permission denied to rename this trashbin'); + } + + public function createFile($name, $data = null) { + throw new Forbidden('Not allowed to create files in the trashbin'); + } + + public function createDirectory($name) { + throw new Forbidden('Not allowed to create folders in the trashbin'); + } + + public function getChild($name) { + list(,$userId) = \Sabre\Uri\split($this->principalInfo['uri']); + + if ($name === 'restore') { + return new RestoreFolder($userId); + } + if ($name === 'trash') { + return new TrashRoot($userId); + } + + throw new NotFound(); + } + + public function getChildren(): array { + list(,$userId) = \Sabre\Uri\split($this->principalInfo['uri']); + + return [ + new RestoreFolder($userId), + new TrashRoot($userId), + ]; + } + + public function childExists($name): bool { + return $name === 'restore' || $name === 'trash'; + } + + public function getLastModified(): int { + return 0; + } +} diff --git a/apps/files_trashbin/lib/Sabre/TrashRoot.php b/apps/files_trashbin/lib/Sabre/TrashRoot.php new file mode 100644 index 00000000000..73b9d44d7e1 --- /dev/null +++ b/apps/files_trashbin/lib/Sabre/TrashRoot.php @@ -0,0 +1,103 @@ +<?php +declare(strict_types=1); +/** + * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ +namespace OCA\Files_Trashbin\Sabre; + +use OCP\Files\FileInfo; +use Sabre\DAV\Exception\Forbidden; +use Sabre\DAV\Exception\NotFound; +use Sabre\DAV\ICollection; + +class TrashRoot implements ICollection { + + /** @var string */ + private $userId; + + public function __construct(string $userId) { + $this->userId = $userId; + } + + public function delete() { + \OCA\Files_Trashbin\Trashbin::deleteAll(); + } + + public function getName(): string { + return 'trash'; + } + + public function setName($name) { + throw new Forbidden('Permission denied to rename this trashbin'); + } + + public function createFile($name, $data = null) { + throw new Forbidden('Not allowed to create files in the trashbin'); + } + + public function createDirectory($name) { + throw new Forbidden('Not allowed to create folders in the trashbin'); + } + + public function getChild($name): ITrash { + $entries = \OCA\Files_Trashbin\Helper::getTrashFiles('/', $this->userId); + + foreach ($entries as $entry) { + if ($entry->getName() . '.d'.$entry->getMtime() === $name) { + if ($entry->getType() === FileInfo::TYPE_FOLDER) { + return new TrashFolder('/', $this->userId, $entry); + } + return new TrashFile($this->userId, $entry); + } + } + + throw new NotFound(); + } + + public function getChildren(): array { + $entries = \OCA\Files_Trashbin\Helper::getTrashFiles('/', $this->userId); + + $children = array_map(function (FileInfo $entry) { + if ($entry->getType() === FileInfo::TYPE_FOLDER) { + return new TrashFolder('/', $this->userId, $entry); + } + return new TrashFile($this->userId, $entry); + }, $entries); + + return $children; + } + + public function childExists($name): bool { + $entries = \OCA\Files_Trashbin\Helper::getTrashFiles('/', $this->userId); + + foreach ($entries as $entry) { + if ($entry->getName() . '.d'.$entry->getMtime() === $name) { + return true; + } + } + + return false; + } + + public function getLastModified(): int { + return 0; + } +} diff --git a/core/css/icons.scss b/core/css/icons.scss index 775decfa3a8..8f2d4748754 100644 --- a/core/css/icons.scss +++ b/core/css/icons.scss @@ -300,6 +300,10 @@ img, object, video, button, textarea, input, select, div[contenteditable=true] { background-image: url('../img/actions/public.svg?v=1'); } +.icon-quota { + background-image: url('../img/actions/quota.svg?v=1'); +} + .icon-rename { background-image: url('../img/actions/rename.svg?v=1'); } diff --git a/core/css/styles.scss b/core/css/styles.scss index 0bbd89f075b..736713c3e7a 100644 --- a/core/css/styles.scss +++ b/core/css/styles.scss @@ -927,40 +927,6 @@ code { font-family: 'Lucida Console', 'Lucida Sans Typewriter', 'DejaVu Sans Mono', monospace; } -#body-settings #quota { - cursor: default; - margin: 30px !important; - position: relative; - padding: 0 !important; - progress { - height: 36px; - &::-moz-progress-bar { - border-radius: 3px 0 0 3px; - } - &::-webkit-progress-value { - border-radius: 3px 0 0 3px; - } - } - div { - font-weight: normal; - white-space: nowrap; - position: absolute;; - top: 0; - &.quotatext-bg { - mix-blend-mode: luminosity; - } - &.quotatext-fg { - color: $color-primary-text; - overflow: hidden; - z-index: 50; - max-width: 100%; - } - .quotatext { - padding: .6em 1em; - } - } -} - .pager { list-style: none; float: right; diff --git a/apps/files/img/quota.svg b/core/img/actions/quota.svg index 691cb29efce..691cb29efce 100644 --- a/apps/files/img/quota.svg +++ b/core/img/actions/quota.svg diff --git a/core/l10n/da.js b/core/l10n/da.js index 882685fb124..005c28c0d43 100644 --- a/core/l10n/da.js +++ b/core/l10n/da.js @@ -266,6 +266,8 @@ OC.L10N.register( "More apps" : "Flere apps", "Search" : "Søg", "Reset search" : "Nulstil søgning", + "Contacts" : "Kontakter", + "Contacts menu" : "Menuen kontakter", "Confirm your password" : "Bekræft dit password", "Server side authentication failed!" : "Server side godkendelse mislykkedes!", "Please contact your administrator." : "Kontakt venligst din administrator.", diff --git a/core/l10n/da.json b/core/l10n/da.json index 620bec0372f..b5863376fc1 100644 --- a/core/l10n/da.json +++ b/core/l10n/da.json @@ -264,6 +264,8 @@ "More apps" : "Flere apps", "Search" : "Søg", "Reset search" : "Nulstil søgning", + "Contacts" : "Kontakter", + "Contacts menu" : "Menuen kontakter", "Confirm your password" : "Bekræft dit password", "Server side authentication failed!" : "Server side godkendelse mislykkedes!", "Please contact your administrator." : "Kontakt venligst din administrator.", diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index 19cb583cc93..6f8c4131546 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -239,6 +239,10 @@ return array( 'OCP\\Lock\\ILockingProvider' => $baseDir . '/lib/public/Lock/ILockingProvider.php', 'OCP\\Lock\\LockedException' => $baseDir . '/lib/public/Lock/LockedException.php', 'OCP\\Lockdown\\ILockdownManager' => $baseDir . '/lib/public/Lockdown/ILockdownManager.php', + 'OCP\\Log\\IFileBased' => $baseDir . '/lib/public/Log/IFileBased.php', + 'OCP\\Log\\ILogFactory' => $baseDir . '/lib/public/Log/ILogFactory.php', + 'OCP\\Log\\IWriter' => $baseDir . '/lib/public/Log/IWriter.php', + 'OCP\\Log\\RotationTrait' => $baseDir . '/lib/public/Log/RotationTrait.php', 'OCP\\Mail\\IAttachment' => $baseDir . '/lib/public/Mail/IAttachment.php', 'OCP\\Mail\\IEMailTemplate' => $baseDir . '/lib/public/Mail/IEMailTemplate.php', 'OCP\\Mail\\IMailer' => $baseDir . '/lib/public/Mail/IMailer.php', @@ -750,6 +754,7 @@ return array( 'OC\\Log\\Errorlog' => $baseDir . '/lib/private/Log/Errorlog.php', 'OC\\Log\\ExceptionSerializer' => $baseDir . '/lib/private/Log/ExceptionSerializer.php', 'OC\\Log\\File' => $baseDir . '/lib/private/Log/File.php', + 'OC\\Log\\LogFactory' => $baseDir . '/lib/private/Log/LogFactory.php', 'OC\\Log\\Rotate' => $baseDir . '/lib/private/Log/Rotate.php', 'OC\\Log\\Syslog' => $baseDir . '/lib/private/Log/Syslog.php', 'OC\\Mail\\Attachment' => $baseDir . '/lib/private/Mail/Attachment.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index a913b0498b9..92726f7789d 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -269,6 +269,10 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OCP\\Lock\\ILockingProvider' => __DIR__ . '/../../..' . '/lib/public/Lock/ILockingProvider.php', 'OCP\\Lock\\LockedException' => __DIR__ . '/../../..' . '/lib/public/Lock/LockedException.php', 'OCP\\Lockdown\\ILockdownManager' => __DIR__ . '/../../..' . '/lib/public/Lockdown/ILockdownManager.php', + 'OCP\\Log\\IFileBased' => __DIR__ . '/../../..' . '/lib/public/Log/IFileBased.php', + 'OCP\\Log\\ILogFactory' => __DIR__ . '/../../..' . '/lib/public/Log/ILogFactory.php', + 'OCP\\Log\\IWriter' => __DIR__ . '/../../..' . '/lib/public/Log/IWriter.php', + 'OCP\\Log\\RotationTrait' => __DIR__ . '/../../..' . '/lib/public/Log/RotationTrait.php', 'OCP\\Mail\\IAttachment' => __DIR__ . '/../../..' . '/lib/public/Mail/IAttachment.php', 'OCP\\Mail\\IEMailTemplate' => __DIR__ . '/../../..' . '/lib/public/Mail/IEMailTemplate.php', 'OCP\\Mail\\IMailer' => __DIR__ . '/../../..' . '/lib/public/Mail/IMailer.php', @@ -780,6 +784,7 @@ class ComposerStaticInit53792487c5a8370acc0b06b1a864ff4c 'OC\\Log\\Errorlog' => __DIR__ . '/../../..' . '/lib/private/Log/Errorlog.php', 'OC\\Log\\ExceptionSerializer' => __DIR__ . '/../../..' . '/lib/private/Log/ExceptionSerializer.php', 'OC\\Log\\File' => __DIR__ . '/../../..' . '/lib/private/Log/File.php', + 'OC\\Log\\LogFactory' => __DIR__ . '/../../..' . '/lib/private/Log/LogFactory.php', 'OC\\Log\\Rotate' => __DIR__ . '/../../..' . '/lib/private/Log/Rotate.php', 'OC\\Log\\Syslog' => __DIR__ . '/../../..' . '/lib/private/Log/Syslog.php', 'OC\\Mail\\Attachment' => __DIR__ . '/../../..' . '/lib/private/Mail/Attachment.php', diff --git a/lib/private/AppFramework/DependencyInjection/DIContainer.php b/lib/private/AppFramework/DependencyInjection/DIContainer.php index 37a34106dbe..c82ac5255dd 100644 --- a/lib/private/AppFramework/DependencyInjection/DIContainer.php +++ b/lib/private/AppFramework/DependencyInjection/DIContainer.php @@ -46,6 +46,7 @@ use OC\AppFramework\Middleware\Security\RateLimitingMiddleware; use OC\AppFramework\Middleware\Security\SecurityMiddleware; use OC\AppFramework\Middleware\SessionMiddleware; use OC\AppFramework\Utility\SimpleContainer; +use OC\Collaboration\Collaborators\SearchResult; use OC\Core\Middleware\TwoFactorMiddleware; use OC\RichObjectStrings\Validator; use OC\ServerContainer; @@ -53,6 +54,7 @@ use OCP\AppFramework\Http\IOutput; use OCP\AppFramework\IAppContainer; use OCP\AppFramework\QueryException; use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Collaboration\Collaborators\ISearchResult; use OCP\Files\Folder; use OCP\Files\IAppData; use OCP\GlobalScale\IConfig; @@ -62,7 +64,6 @@ use OCP\IRequest; use OCP\IServerContainer; use OCP\IUserSession; use OCP\RichObjectStrings\IValidator; -use OCP\Util; use OCP\Encryption\IManager; use OCA\WorkflowEngine\Manager; @@ -144,6 +145,8 @@ class DIContainer extends SimpleContainer implements IAppContainer { return $c; }); + $this->registerAlias(ISearchResult::class, SearchResult::class); + // commonly used attributes $this->registerService('UserId', function ($c) { return $c->query(IUserSession::class)->getSession()->get('user_id'); diff --git a/lib/private/Log.php b/lib/private/Log.php index ffe8c665c6f..69705c49e87 100644 --- a/lib/private/Log.php +++ b/lib/private/Log.php @@ -38,7 +38,8 @@ namespace OC; use InterfaSys\LogNormalizer\Normalizer; use OC\Log\ExceptionSerializer; -use OC\Log\File; +use OCP\Log\IFileBased; +use OCP\Log\IWriter; use OCP\ILogger; use OCP\Support\CrashReport\IRegistry; use OCP\Util; @@ -54,7 +55,7 @@ use OCP\Util; */ class Log implements ILogger { - /** @var string */ + /** @var IWriter */ private $logger; /** @var SystemConfig */ @@ -70,27 +71,19 @@ class Log implements ILogger { private $crashReporters; /** - * @param string $logger The logger that should be used + * @param IWriter $logger The logger that should be used * @param SystemConfig $config the system config object * @param Normalizer|null $normalizer * @param IRegistry|null $registry */ - public function __construct($logger = null, SystemConfig $config = null, $normalizer = null, IRegistry $registry = null) { + public function __construct(IWriter $logger, SystemConfig $config = null, $normalizer = null, IRegistry $registry = null) { // FIXME: Add this for backwards compatibility, should be fixed at some point probably if ($config === null) { $config = \OC::$server->getSystemConfig(); } $this->config = $config; - - // FIXME: Add this for backwards compatibility, should be fixed at some point probably - if ($logger === null) { - $logType = $this->config->getValue('log_type', 'file'); - $this->logger = static::getLogClass($logType); - call_user_func([$this->logger, 'init']); - } else { - $this->logger = $logger; - } + $this->logger = $logger; if ($normalizer === null) { $this->normalizer = new Normalizer(); } else { @@ -302,7 +295,7 @@ class Log implements ILogger { array_walk($context, [$this->normalizer, 'format']); if ($level >= $minLevel) { - if ($this->logger !== File::class) { + if (!$this->logger instanceof IFileBased) { $data = json_encode($data, JSON_PARTIAL_OUTPUT_ON_ERROR); } $this->writeLog($app, $data, $level); @@ -320,28 +313,13 @@ class Log implements ILogger { * @param int $level */ protected function writeLog(string $app, $entry, int $level) { - call_user_func([$this->logger, 'write'], $app, $entry, $level); + $this->logger->write($app, $entry, $level); } - /** - * @param string $logType - * @return string - * @internal - */ - public static function getLogClass(string $logType): string { - switch (strtolower($logType)) { - case 'errorlog': - return \OC\Log\Errorlog::class; - case 'syslog': - return \OC\Log\Syslog::class; - case 'file': - return \OC\Log\File::class; - - // Backwards compatibility for old and fallback for unknown log types - case 'owncloud': - case 'nextcloud': - default: - return \OC\Log\File::class; + public function getLogPath():string { + if($this->logger instanceof IFileBased) { + return $this->logger->getLogFilePath(); } + throw new \RuntimeException('Log implementation has no path'); } } diff --git a/lib/private/Log/Errorlog.php b/lib/private/Log/Errorlog.php index 37498c36aba..9dc8b2cc49c 100644 --- a/lib/private/Log/Errorlog.php +++ b/lib/private/Log/Errorlog.php @@ -25,14 +25,9 @@ namespace OC\Log; -class Errorlog { +use OCP\Log\IWriter; - - /** - * Init class data - */ - public static function init() { - } +class Errorlog implements IWriter { /** * write a message in the log @@ -40,7 +35,7 @@ class Errorlog { * @param string $message * @param int $level */ - public static function write($app, $message, $level) { + public function write(string $app, $message, int $level) { error_log('[owncloud]['.$app.']['.$level.'] '.$message); } } diff --git a/lib/private/Log/File.php b/lib/private/Log/File.php index 755c4729c7a..597cb54e402 100644 --- a/lib/private/Log/File.php +++ b/lib/private/Log/File.php @@ -36,7 +36,9 @@ */ namespace OC\Log; - +use OC\SystemConfig; +use OCP\Log\IFileBased; +use OCP\Log\IWriter; use OCP\ILogger; /** @@ -45,30 +47,26 @@ use OCP\ILogger; * Log is saved at data/nextcloud.log (on default) */ -class File { - static protected $logFile; - - /** - * Init class data - */ - public static function init() { - $systemConfig = \OC::$server->getSystemConfig(); - $defaultLogFile = $systemConfig->getValue("datadirectory", \OC::$SERVERROOT.'/data').'/nextcloud.log'; - self::$logFile = $systemConfig->getValue("logfile", $defaultLogFile); +class File implements IWriter, IFileBased { + /** @var string */ + protected $logFile; + /** @var SystemConfig */ + private $config; - /** - * Fall back to default log file if specified logfile does not exist - * and can not be created. - */ - if (!file_exists(self::$logFile)) { - if(!is_writable(dirname(self::$logFile))) { - self::$logFile = $defaultLogFile; - } else { - if(!touch(self::$logFile)) { - self::$logFile = $defaultLogFile; - } + public function __construct(string $path, string $fallbackPath = '', SystemConfig $config) { + $this->logFile = $path; + if (!file_exists($this->logFile)) { + if( + ( + !is_writable(dirname($this->logFile)) + || !touch($this->logFile) + ) + && $fallbackPath !== '' + ) { + $this->logFile = $fallbackPath; } } + $this->config = $config; } /** @@ -77,12 +75,10 @@ class File { * @param string|array $message * @param int $level */ - public static function write($app, $message, $level) { - $config = \OC::$server->getSystemConfig(); - + public function write(string $app, $message, int $level) { // default to ISO8601 - $format = $config->getValue('logdateformat', \DateTime::ATOM); - $logTimeZone = $config->getValue('logtimezone', 'UTC'); + $format = $this->config->getValue('logdateformat', \DateTime::ATOM); + $logTimeZone = $this->config->getValue('logtimezone', 'UTC'); try { $timezone = new \DateTimeZone($logTimeZone); } catch (\Exception $e) { @@ -102,7 +98,7 @@ class File { $time = $time->format($format); $url = ($request->getRequestUri() !== '') ? $request->getRequestUri() : '--'; $method = is_string($request->getMethod()) ? $request->getMethod() : '--'; - if($config->getValue('installed', false)) { + if($this->config->getValue('installed', false)) { $user = \OC_User::getUser() ? \OC_User::getUser() : '--'; } else { $user = '--'; @@ -111,7 +107,7 @@ class File { if ($userAgent === '') { $userAgent = '--'; } - $version = $config->getValue('version', ''); + $version = $this->config->getValue('version', ''); $entry = compact( 'reqId', 'level', @@ -137,9 +133,9 @@ class File { } } $entry = json_encode($entry, JSON_PARTIAL_OUTPUT_ON_ERROR); - $handle = @fopen(self::$logFile, 'a'); - if ((fileperms(self::$logFile) & 0777) != 0640) { - @chmod(self::$logFile, 0640); + $handle = @fopen($this->logFile, 'a'); + if ((fileperms($this->logFile) & 0777) != 0640) { + @chmod($this->logFile, 0640); } if ($handle) { fwrite($handle, $entry."\n"); @@ -159,11 +155,10 @@ class File { * @param int $offset * @return array */ - public static function getEntries($limit=50, $offset=0) { - self::init(); - $minLevel = \OC::$server->getSystemConfig()->getValue("loglevel", ILogger::WARN); + public function getEntries(int $limit=50, int $offset=0):array { + $minLevel = $this->config->getValue("loglevel", ILogger::WARN); $entries = array(); - $handle = @fopen(self::$logFile, 'rb'); + $handle = @fopen($this->logFile, 'rb'); if ($handle) { fseek($handle, 0, SEEK_END); $pos = ftell($handle); @@ -205,7 +200,7 @@ class File { /** * @return string */ - public static function getLogFilePath() { - return self::$logFile; + public function getLogFilePath():string { + return $this->logFile; } } diff --git a/lib/private/Log/LogFactory.php b/lib/private/Log/LogFactory.php new file mode 100644 index 00000000000..9b9d12abfa8 --- /dev/null +++ b/lib/private/Log/LogFactory.php @@ -0,0 +1,78 @@ +<?php +/** + * @copyright Copyright (c) 2018 Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @author Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OC\Log; + +use OC\Log; +use OC\SystemConfig; +use OCP\ILogger; +use OCP\IServerContainer; +use OCP\Log\ILogFactory; +use OCP\Log\IWriter; + +class LogFactory implements ILogFactory { + /** @var IServerContainer */ + private $c; + /** @var SystemConfig */ + private $systemConfig; + + public function __construct(IServerContainer $c, SystemConfig $systemConfig) { + $this->c = $c; + $this->systemConfig = $systemConfig; + } + + /** + * @throws \OCP\AppFramework\QueryException + */ + public function get(string $type):IWriter { + switch (strtolower($type)) { + case 'errorlog': + return new Errorlog(); + case 'syslog': + return $this->c->resolve(Syslog::class); + case 'file': + return $this->buildLogFile(); + + // Backwards compatibility for old and fallback for unknown log types + case 'owncloud': + case 'nextcloud': + default: + return $this->buildLogFile(); + } + } + + public function getCustomLogger(string $path):ILogger { + $log = $this->buildLogFile($path); + return new Log($log, $this->systemConfig); + } + + protected function buildLogFile(string $logFile = ''):File { + $defaultLogFile = $this->systemConfig->getValue('datadirectory', \OC::$SERVERROOT.'/data').'/nextcloud.log'; + if($logFile === '') { + $logFile = $this->systemConfig->getValue('logfile', $defaultLogFile); + } + $fallback = $defaultLogFile !== $logFile ? $defaultLogFile : ''; + + return new File($logFile, $fallback, $this->systemConfig); + } +} diff --git a/lib/private/Log/Rotate.php b/lib/private/Log/Rotate.php index 48b96c98a8d..cc41c804ad3 100644 --- a/lib/private/Log/Rotate.php +++ b/lib/private/Log/Rotate.php @@ -24,7 +24,7 @@ */ namespace OC\Log; -use OCP\ILogger; +use OCP\Log\RotationTrait; /** * This rotates the current logfile to a new name, this way the total log usage @@ -33,23 +33,17 @@ use OCP\ILogger; * location and manage that with your own tools. */ class Rotate extends \OC\BackgroundJob\Job { - private $max_log_size; + use RotationTrait; + public function run($dummy) { $systemConfig = \OC::$server->getSystemConfig(); - $logFile = $systemConfig->getValue('logfile', $systemConfig->getValue('datadirectory', \OC::$SERVERROOT . '/data') . '/nextcloud.log'); - $this->max_log_size = \OC::$server->getConfig()->getSystemValue('log_rotate_size', 100 * 1024 * 1024); - if ($this->max_log_size) { - $filesize = @filesize($logFile); - if ($filesize >= $this->max_log_size) { - $this->rotate($logFile); - } - } - } + $this->filePath = $systemConfig->getValue('logfile', $systemConfig->getValue('datadirectory', \OC::$SERVERROOT . '/data') . '/nextcloud.log'); - protected function rotate($logfile) { - $rotatedLogfile = $logfile.'.1'; - rename($logfile, $rotatedLogfile); - $msg = 'Log file "'.$logfile.'" was over '.$this->max_log_size.' bytes, moved to "'.$rotatedLogfile.'"'; - \OCP\Util::writeLog(Rotate::class, $msg, ILogger::WARN); + $this->maxSize = \OC::$server->getConfig()->getSystemValue('log_rotate_size', 100 * 1024 * 1024); + if($this->shouldRotateBySize()) { + $rotatedFile = $this->rotate(); + $msg = 'Log file "'.$this->filePath.'" was over '.$this->maxSize.' bytes, moved to "'.$rotatedFile.'"'; + \OC::$server->getLogger()->warning($msg, ['app' => Rotate::class]); + } } } diff --git a/lib/private/Log/Syslog.php b/lib/private/Log/Syslog.php index 7b3d931ef31..90a20026f0e 100644 --- a/lib/private/Log/Syslog.php +++ b/lib/private/Log/Syslog.php @@ -26,23 +26,24 @@ namespace OC\Log; use OCP\ILogger; +use OCP\IConfig; +use OCP\Log\IWriter; -class Syslog { - static protected $levels = array( +class Syslog implements IWriter { + protected $levels = [ ILogger::DEBUG => LOG_DEBUG, ILogger::INFO => LOG_INFO, ILogger::WARN => LOG_WARNING, ILogger::ERROR => LOG_ERR, ILogger::FATAL => LOG_CRIT, - ); + ]; - /** - * Init class data - */ - public static function init() { - openlog(\OC::$server->getSystemConfig()->getValue("syslog_tag", "ownCloud"), LOG_PID | LOG_CONS, LOG_USER); - // Close at shutdown - register_shutdown_function('closelog'); + public function __construct(IConfig $config) { + openlog($config->getSystemValue('syslog_tag', 'ownCloud'), LOG_PID | LOG_CONS, LOG_USER); + } + + public function __destruct() { + closelog(); } /** @@ -51,8 +52,8 @@ class Syslog { * @param string $message * @param int $level */ - public static function write($app, $message, $level) { - $syslog_level = self::$levels[$level]; + public function write(string $app, $message, int $level) { + $syslog_level = $this->levels[$level]; syslog($syslog_level, '{'.$app.'} '.$message); } } diff --git a/lib/private/Server.php b/lib/private/Server.php index 3786486c2b2..a879c65bb9b 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -87,6 +87,7 @@ use OC\Lock\DBLockingProvider; use OC\Lock\MemcacheLockingProvider; use OC\Lock\NoopLockingProvider; use OC\Lockdown\LockdownManager; +use OC\Log\LogFactory; use OC\Mail\Mailer; use OC\Memcache\ArrayCache; use OC\Memcache\Factory; @@ -134,6 +135,7 @@ use OCP\ITempManager; use OCP\Contacts\ContactsMenu\IActionFactory; use OCP\IUser; use OCP\Lock\ILockingProvider; +use OCP\Log\ILogFactory; use OCP\Remote\Api\IApiFactory; use OCP\Remote\IInstanceFactory; use OCP\RichObjectStrings\IValidator; @@ -546,15 +548,18 @@ class Server extends ServerContainer implements IServerContainer { $this->registerService(\OCP\ILogger::class, function (Server $c) { $logType = $c->query('AllConfig')->getSystemValue('log_type', 'file'); - $logger = Log::getLogClass($logType); - call_user_func(array($logger, 'init')); - $config = $this->getSystemConfig(); + $factory = new LogFactory($c, $this->getSystemConfig()); + $logger = $factory->get($logType); $registry = $c->query(\OCP\Support\CrashReport\IRegistry::class); - return new Log($logger, $config, null, $registry); + return new Log($logger, $this->getSystemConfig(), null, $registry); }); $this->registerAlias('Logger', \OCP\ILogger::class); + $this->registerService(ILogFactory::class, function (Server $c) { + return new LogFactory($c, $this->getSystemConfig()); + }); + $this->registerService(\OCP\BackgroundJob\IJobList::class, function (Server $c) { $config = $c->getConfig(); return new \OC\BackgroundJob\JobList( @@ -1529,6 +1534,14 @@ class Server extends ServerContainer implements IServerContainer { } /** + * @return ILogFactory + * @throws \OCP\AppFramework\QueryException + */ + public function getLogFactory() { + return $this->query(ILogFactory::class); + } + + /** * Returns a router for generating and matching urls * * @return \OCP\Route\IRouter diff --git a/lib/private/Template/SCSSCacher.php b/lib/private/Template/SCSSCacher.php index 6fb9d11953b..6e25e1fa5de 100644 --- a/lib/private/Template/SCSSCacher.php +++ b/lib/private/Template/SCSSCacher.php @@ -273,7 +273,11 @@ class SCSSCacher { $appDirectory = $this->appData->getDirectoryListing(); foreach ($appDirectory as $folder) { foreach ($folder->getDirectoryListing() as $file) { - $file->delete(); + try { + $file->delete(); + } catch(NotPermittedException $e) { + $this->logger->logException($e, ['message' => 'SCSSCacher: unable to delete file: ' . $file->getName()]); + } } } } diff --git a/lib/private/legacy/app.php b/lib/private/legacy/app.php index 7931eecc502..70f1b16e3c6 100644 --- a/lib/private/legacy/app.php +++ b/lib/private/legacy/app.php @@ -464,7 +464,7 @@ class OC_App { } else { $versionToLoad = []; foreach ($possibleApps as $possibleApp) { - $version = self::getAppVersionByPath($possibleApp['path']); + $version = self::getAppVersionByPath($possibleApp['path'] . '/' . $appId); if (empty($versionToLoad) || version_compare($version, $versionToLoad['version'], '>')) { $versionToLoad = array( 'dir' => $possibleApp, diff --git a/lib/private/legacy/util.php b/lib/private/legacy/util.php index d3599d14e7a..356d336f687 100644 --- a/lib/private/legacy/util.php +++ b/lib/private/legacy/util.php @@ -259,6 +259,23 @@ class OC_Util { return $storage; }); + \OC\Files\Filesystem::addStorageWrapper('readonly', function ($mountPoint, \OCP\Files\Storage\IStorage $storage, \OCP\Files\Mount\IMountPoint $mount) { + /* + * Do not allow any operations that modify the storage + */ + if ($mount->getOption('readonly', false)) { + return new \OC\Files\Storage\Wrapper\PermissionsMask([ + 'storage' => $storage, + 'mask' => \OCP\Constants::PERMISSION_ALL & ~( + \OCP\Constants::PERMISSION_UPDATE | + \OCP\Constants::PERMISSION_CREATE | + \OCP\Constants::PERMISSION_DELETE + ), + ]); + } + return $storage; + }); + OC_Hook::emit('OC_Filesystem', 'preSetup', array('user' => $user)); \OC\Files\Filesystem::logWarningWhenAddingStorageWrapper(true); diff --git a/lib/public/IServerContainer.php b/lib/public/IServerContainer.php index 19c9578ee23..c38aaf9f2cb 100644 --- a/lib/public/IServerContainer.php +++ b/lib/public/IServerContainer.php @@ -44,6 +44,7 @@ // use OCP namespace for all classes that are considered public. // This means that they should be used by apps instead of the internal ownCloud classes namespace OCP; +use OCP\Log\ILogFactory; use OCP\Security\IContentSecurityPolicyManager; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -315,6 +316,14 @@ interface IServerContainer extends IContainer { public function getLogger(); /** + * returns a log factory instance + * + * @return ILogFactory + * @since 14.0.0 + */ + public function getLogFactory(); + + /** * Returns a router for generating and matching urls * * @return \OCP\Route\IRouter diff --git a/lib/public/Log/IFileBased.php b/lib/public/Log/IFileBased.php new file mode 100644 index 00000000000..c0eef472975 --- /dev/null +++ b/lib/public/Log/IFileBased.php @@ -0,0 +1,43 @@ +<?php +/** + * @copyright Copyright (c) 2018 Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @author Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\Log; + +/** + * Interface IFileBased + * + * @package OCP\Log + * + * @since 14.0.0 + */ +interface IFileBased { + /** + * @since 14.0.0 + */ + public function getLogFilePath():string; + + /** + * @since 14.0.0 + */ + public function getEntries(int $limit=50, int $offset=0): array; +} diff --git a/lib/public/Log/ILogFactory.php b/lib/public/Log/ILogFactory.php new file mode 100644 index 00000000000..d8e1ab4ee76 --- /dev/null +++ b/lib/public/Log/ILogFactory.php @@ -0,0 +1,48 @@ +<?php +/** + * @copyright Copyright (c) 2018 Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @author Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\Log; + +use OCP\ILogger; + +/** + * Interface ILogFactory + * + * @package OCP\Log + * @since 14.0.0 + */ +interface ILogFactory { + /** + * @param string $type - one of: file, errorlog, syslog + * @return IWriter + * @since 14.0.0 + */ + public function get(string $type): IWriter; + + /** + * @param string $path + * @return ILogger + * @since 14.0.0 + */ + public function getCustomLogger(string $path): ILogger; +} diff --git a/lib/public/Log/IWriter.php b/lib/public/Log/IWriter.php new file mode 100644 index 00000000000..c9b906bf4a3 --- /dev/null +++ b/lib/public/Log/IWriter.php @@ -0,0 +1,37 @@ +<?php +/** + * @copyright Copyright (c) 2018 Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @author Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\Log; + +/** + * Interface IWriter + * + * @package OCP\Log + * @since 14.0.0 + */ +interface IWriter { + /** + * @since 14.0.0 + */ + public function write(string $app, $message, int $level); +} diff --git a/lib/public/Log/RotationTrait.php b/lib/public/Log/RotationTrait.php new file mode 100644 index 00000000000..df42bfeff1f --- /dev/null +++ b/lib/public/Log/RotationTrait.php @@ -0,0 +1,71 @@ +<?php +/** + * @copyright Copyright (c) 2018 Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @author Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OCP\Log; + +/** + * Trait RotationTrait + * + * @package OCP\Log + * + * @since 14.0.0 + */ +trait RotationTrait { + + /** + * @var string + * @since 14.0.0 + */ + protected $filePath; + + /** + * @var int + * @since 14.0.0 + */ + protected $maxSize; + + /** + * @return string the resulting new filepath + * @since 14.0.0 + */ + protected function rotate():string { + $rotatedFile = $this->filePath.'.1'; + rename($this->filePath, $rotatedFile); + return $rotatedFile; + } + + /** + * @return bool + * @since 14.0.0 + */ + protected function shouldRotateBySize():bool { + if ((int)$this->maxSize > 0) { + $filesize = @filesize($this->filePath); + if ($filesize >= (int)$this->maxSize) { + return true; + } + } + return false; + } + +} diff --git a/remote.php b/remote.php index 8879a300296..a14ff6ac308 100644 --- a/remote.php +++ b/remote.php @@ -100,6 +100,7 @@ function resolveService($service) { 'carddav' => 'dav/appinfo/v1/carddav.php', 'contacts' => 'dav/appinfo/v1/carddav.php', 'files' => 'dav/appinfo/v1/webdav.php', + 'direct' => 'dav/appinfo/v2/direct.php', ]; if (isset($services[$service])) { return $services[$service]; diff --git a/settings/Controller/LogSettingsController.php b/settings/Controller/LogSettingsController.php index 6405ff9ec73..ef195edce63 100644 --- a/settings/Controller/LogSettingsController.php +++ b/settings/Controller/LogSettingsController.php @@ -26,8 +26,11 @@ namespace OC\Settings\Controller; +use OC\Log; use OCP\AppFramework\Controller; use OCP\AppFramework\Http\StreamResponse; +use OCP\ILogger; +use OCP\IRequest; /** * Class LogSettingsController @@ -35,6 +38,15 @@ use OCP\AppFramework\Http\StreamResponse; * @package OC\Settings\Controller */ class LogSettingsController extends Controller { + + /** @var ILogger */ + private $log; + + public function __construct(string $appName, IRequest $request, ILogger $logger) { + parent::__construct($appName, $request); + $this->log = $logger; + } + /** * download logfile * @@ -43,7 +55,10 @@ class LogSettingsController extends Controller { * @return StreamResponse */ public function download() { - $resp = new StreamResponse(\OC\Log\File::getLogFilePath()); + if(!$this->log instanceof Log) { + throw new \UnexpectedValueException('Log file not available'); + } + $resp = new StreamResponse($this->log->getLogPath()); $resp->addHeader('Content-Type', 'application/octet-stream'); $resp->addHeader('Content-Disposition', 'attachment; filename="nextcloud.log"'); return $resp; diff --git a/settings/css/settings.scss b/settings/css/settings.scss index a040ee6195a..3e7e5576640 100644 --- a/settings/css/settings.scss +++ b/settings/css/settings.scss @@ -52,9 +52,6 @@ input { #displayavatar { text-align: center; - p { - text-align: left; - } } #uploadavatarbutton, #selectavatar, #removeavatar { @@ -119,6 +116,7 @@ input { background-size: 16px; background-position: left 8px; opacity: .3; + margin-top: -2px; margin-left: 10px; cursor: pointer; @@ -162,6 +160,16 @@ input { .section { padding: 10px 10px; } + + .personal-info { + margin-right: 10%; + margin-bottom: 20px; + } + .personal-info[class^='icon-'], .personal-info[class*=' icon-'] { + background-position: 0px 2px; + padding-left: 30px; + opacity: 0.7; + } } @@ -234,7 +242,7 @@ input { margin-bottom: 12px; display: inline-flex; flex-wrap: nowrap; - justify-content: space-between; + justify-content: flex-start; width: 100%; > label { white-space: nowrap; @@ -272,6 +280,26 @@ input { } } + +#body-settings #quota { + cursor: default; + position: relative; + progress { + height: 6px; + &::-moz-progress-bar { + border-radius: 3px 0 0 3px; + } + &::-webkit-progress-value { + border-radius: 3px 0 0 3px; + } + } + div { + font-weight: normal; + white-space: nowrap; + } +} + + /* verify accounts */ /* only show pointer cursor when popup will be there */ .verification-dialog { diff --git a/settings/l10n/el.js b/settings/l10n/el.js index dbd8f865939..4f5a4d45c25 100644 --- a/settings/l10n/el.js +++ b/settings/l10n/el.js @@ -258,6 +258,7 @@ OC.L10N.register( "Restrict users to only share with users in their groups" : "Να επιτρέπεται σε χρήστες ο διαμοιρασμός μόνο με χρήστες που ανήκουν στις ομάδες τους", "Exclude groups from sharing" : "Εξαίρεση ομάδων από τον διαμοιρασμό", "These groups will still be able to receive shares, but not to initiate them." : "Αυτές οι ομάδες θα συνεχίσουν να λαμβάνουν διαμοιρασμούς, αλλά δεν θα είναι δυνατό να τους δημιουργήσουν.", + "Allow username autocompletion in share dialog. If this is disabled the full username or email address needs to be entered." : "Να επιτρέπεται η χρήση αυτόματης συμπλήρωσης του ονόματος χρήστη στο πεδίο διαμοιρασμού. Αν η ρύθμιση αυτή είναι απενεργοποιημένη θα πρέπει να εισαχθεί το πλήρες όνομα χρήστη ή το email του. ", "This text will be shown on the public link upload page when the file list is hidden." : "Αυτό το κείμενο θα ", "Tips & tricks" : "Συμβουλές & τεχνάσματα", "How to do backups" : "Πώς να κάνετε αντίγραφα ασφαλείας", diff --git a/settings/l10n/el.json b/settings/l10n/el.json index 30c4fb7829e..9a440e76d72 100644 --- a/settings/l10n/el.json +++ b/settings/l10n/el.json @@ -256,6 +256,7 @@ "Restrict users to only share with users in their groups" : "Να επιτρέπεται σε χρήστες ο διαμοιρασμός μόνο με χρήστες που ανήκουν στις ομάδες τους", "Exclude groups from sharing" : "Εξαίρεση ομάδων από τον διαμοιρασμό", "These groups will still be able to receive shares, but not to initiate them." : "Αυτές οι ομάδες θα συνεχίσουν να λαμβάνουν διαμοιρασμούς, αλλά δεν θα είναι δυνατό να τους δημιουργήσουν.", + "Allow username autocompletion in share dialog. If this is disabled the full username or email address needs to be entered." : "Να επιτρέπεται η χρήση αυτόματης συμπλήρωσης του ονόματος χρήστη στο πεδίο διαμοιρασμού. Αν η ρύθμιση αυτή είναι απενεργοποιημένη θα πρέπει να εισαχθεί το πλήρες όνομα χρήστη ή το email του. ", "This text will be shown on the public link upload page when the file list is hidden." : "Αυτό το κείμενο θα ", "Tips & tricks" : "Συμβουλές & τεχνάσματα", "How to do backups" : "Πώς να κάνετε αντίγραφα ασφαλείας", diff --git a/settings/templates/settings/personal/personal.info.php b/settings/templates/settings/personal/personal.info.php index 24f89076275..e581e6d39e6 100644 --- a/settings/templates/settings/personal/personal.info.php +++ b/settings/templates/settings/personal/personal.info.php @@ -37,34 +37,6 @@ vendor_style('jcrop/css/jquery.Jcrop'); ?> -<div id="quota" class="section"> - <progress value="<?php p($_['usage_relative']); ?>" max="100" - <?php if($_['usage_relative'] > 80): ?> class="warn" <?php endif; ?>></progress> - - <div style="width:<?php p($_['usage_relative']);?>%" class="quotatext-fg"> - <p class="quotatext"> - <?php if ($_['quota'] === \OCP\Files\FileInfo::SPACE_UNLIMITED): ?> - <?php print_unescaped($l->t('You are using <strong>%s</strong> of <strong>%s</strong>', - [$_['usage'], $_['total_space']]));?> - <?php else: ?> - <?php print_unescaped($l->t('You are using <strong>%s</strong> of <strong>%s</strong> (<strong>%s %%</strong>)', - [$_['usage'], $_['total_space'], $_['usage_relative']]));?> - <?php endif ?> - </p> - </div> - <div class="quotatext-bg"> - <p class="quotatext"> - <?php if ($_['quota'] === \OCP\Files\FileInfo::SPACE_UNLIMITED): ?> - <?php print_unescaped($l->t('You are using <strong>%s</strong> of <strong>%s</strong>', - [$_['usage'], $_['total_space']]));?> - <?php else: ?> - <?php print_unescaped($l->t('You are using <strong>%s</strong> of <strong>%s</strong> (<strong>%s %%</strong>)', - [$_['usage'], $_['total_space'], $_['usage_relative']]));?> - <?php endif ?> - </p> - </div> -</div> - <div id="personal-settings"> <div id="personal-settings-avatar-container" class="personal-settings-container"> <div> @@ -100,14 +72,28 @@ vendor_style('jcrop/css/jquery.Jcrop'); <?php } ?> </form> </div> - <div class="personal-settings-setting-box personal-settings-group-box"> - <div id="groups" class="section"> - <h2><?php p($l->t('Groups')); ?></h2> - <p><?php p($l->t('You are member of the following groups:')); ?></p> + <div class="personal-settings-setting-box personal-settings-group-box section"> + <h2><?php p($l->t('Details')); ?></h2> + <div id="groups" class="personal-info icon-user"> + <p class="icon-groups"><?php p($l->t('You are member of the following groups:')); ?></p> <p id="groups-groups"> <strong><?php p(implode(', ', $_['groups'])); ?></strong> </p> </div> + <div id="quota" class="personal-info icon-quota"> + <div class="quotatext-bg"> + <p class="quotatext"> + <?php if ($_['quota'] === \OCP\Files\FileInfo::SPACE_UNLIMITED): ?> + <?php print_unescaped($l->t('You are using <strong>%s</strong>', + [$_['usage']]));?> + <?php else: ?> + <?php print_unescaped($l->t('You are using <strong>%s</strong> of <strong>%s</strong> (<strong>%s %%</strong>)', + [$_['usage'], $_['total_space'], $_['usage_relative']]));?> + <?php endif ?> + </p> + </div> + <progress value="<?php p($_['usage_relative']); ?>" max="100"<?php if($_['usage_relative'] > 80): ?> class="warn" <?php endif; ?>></progress> + </div> </div> </div> diff --git a/tests/lib/Log/FileTest.php b/tests/lib/Log/FileTest.php index 53b5c62e81a..d5e550a7e8d 100644 --- a/tests/lib/Log/FileTest.php +++ b/tests/lib/Log/FileTest.php @@ -31,28 +31,31 @@ class FileTest extends TestCase private $restore_logfile; private $restore_logdateformat; + /** @var File */ + protected $logFile; + protected function setUp() { parent::setUp(); - $config = \OC::$server->getConfig(); - $this->restore_logfile = $config->getSystemValue("logfile"); - $this->restore_logdateformat = $config->getSystemValue('logdateformat'); + $config = \OC::$server->getSystemConfig(); + $this->restore_logfile = $config->getValue("logfile"); + $this->restore_logdateformat = $config->getValue('logdateformat'); - $config->setSystemValue("logfile", $config->getSystemValue('datadirectory') . "/logtest"); - File::init(); + $config->setValue("logfile", $config->getValue('datadirectory') . "/logtest.log"); + $this->logFile = new File($config->getValue('datadirectory') . '/logtest.log', '', $config); } protected function tearDown() { - $config = \OC::$server->getConfig(); + $config = \OC::$server->getSystemConfig(); if (isset($this->restore_logfile)) { - $config->getSystemValue("logfile", $this->restore_logfile); + $config->getValue("logfile", $this->restore_logfile); } else { - $config->deleteSystemValue("logfile"); + $config->deleteValue("logfile"); } if (isset($this->restore_logdateformat)) { - $config->getSystemValue("logdateformat", $this->restore_logdateformat); + $config->getValue("logdateformat", $this->restore_logdateformat); } else { - $config->deleteSystemValue("logdateformat"); - } - File::init(); + $config->deleteValue("logdateformat"); + } + $this->logFile = new File($this->restore_logfile, '', $config); parent::tearDown(); } @@ -63,7 +66,7 @@ class FileTest extends TestCase # set format & write log line $config->setSystemValue('logdateformat', 'u'); - File::write('test', 'message', ILogger::ERROR); + $this->logFile->write('test', 'message', ILogger::ERROR); # read log line $handle = @fopen($config->getSystemValue('logfile'), 'r'); diff --git a/tests/lib/Log/LogFactoryTest.php b/tests/lib/Log/LogFactoryTest.php new file mode 100644 index 00000000000..08139f54df4 --- /dev/null +++ b/tests/lib/Log/LogFactoryTest.php @@ -0,0 +1,144 @@ +<?php +/** + * @copyright Copyright (c) 2018 Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @author Arthur Schiwon <blizzz@arthur-schiwon.de> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * 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 + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace Test\Log; +use OC\Log\Errorlog; +use OC\Log\File; +use OC\Log\LogFactory; +use OC\Log\Syslog; +use OC\SystemConfig; +use OCP\IConfig; +use OCP\IServerContainer; +use Test\TestCase; + +/** + * Class LogFactoryTest + * + * @package Test\Log + */ +class LogFactoryTest extends TestCase { + /** @var IServerContainer|\PHPUnit_Framework_MockObject_MockObject */ + protected $c; + + /** @var LogFactory */ + protected $factory; + + /** @var SystemConfig|\PHPUnit_Framework_MockObject_MockObject */ + protected $systemConfig; + + protected function setUp() { + parent::setUp(); + + $this->c = $this->createMock(IServerContainer::class); + $this->systemConfig = $this->createMock(SystemConfig::class); + + $this->factory = new LogFactory($this->c, $this->systemConfig); + } + + public function fileTypeProvider(): array { + return [ + [ + 'file' + ], + [ + 'nextcloud' + ], + [ + 'owncloud' + ], + [ + 'krzxkyr_default' + ] + ]; + } + + /** + * @param string $type + * @dataProvider fileTypeProvider + * @throws \OCP\AppFramework\QueryException + */ + public function testFile(string $type) { + $datadir = \OC::$SERVERROOT.'/data'; + $defaultLog = $datadir . '/nextcloud.log'; + + $this->systemConfig->expects($this->exactly(2)) + ->method('getValue') + ->withConsecutive(['datadirectory', $datadir], ['logfile', $defaultLog]) + ->willReturnOnConsecutiveCalls($datadir, $defaultLog); + + $log = $this->factory->get($type); + $this->assertInstanceOf(File::class, $log); + } + + public function logFilePathProvider():array { + return [ + [ + '/dev/null', + '/dev/null' + ], + [ + '/xdev/youshallfallback', + \OC::$SERVERROOT.'/data/nextcloud.log' + ] + ]; + } + + /** + * @dataProvider logFilePathProvider + * @throws \OCP\AppFramework\QueryException + */ + public function testFileCustomPath($path, $expected) { + $datadir = \OC::$SERVERROOT.'/data'; + $defaultLog = $datadir . '/nextcloud.log'; + + $this->systemConfig->expects($this->exactly(2)) + ->method('getValue') + ->withConsecutive(['datadirectory', $datadir], ['logfile', $defaultLog]) + ->willReturnOnConsecutiveCalls($datadir, $path); + + $log = $this->factory->get('file'); + $this->assertInstanceOf(File::class, $log); + $this->assertSame($expected, $log->getLogFilePath()); + } + + /** + * @throws \OCP\AppFramework\QueryException + */ + public function testErrorLog() { + $log = $this->factory->get('errorlog'); + $this->assertInstanceOf(Errorlog::class, $log); + } + + /** + * @throws \OCP\AppFramework\QueryException + */ + public function testSystemLog() { + $this->c->expects($this->once()) + ->method('resolve') + ->with(Syslog::class) + ->willReturn($this->createMock(Syslog::class)); + + $log = $this->factory->get('syslog'); + $this->assertInstanceOf(Syslog::class, $log); + } +} diff --git a/tests/lib/LoggerTest.php b/tests/lib/LoggerTest.php index 6b9c9af8639..83cb87b8733 100644 --- a/tests/lib/LoggerTest.php +++ b/tests/lib/LoggerTest.php @@ -10,8 +10,9 @@ namespace Test; use OC\Log; use OCP\ILogger; +use OCP\Log\IWriter; -class LoggerTest extends TestCase { +class LoggerTest extends TestCase implements IWriter { /** @var \OC\SystemConfig|\PHPUnit_Framework_MockObject_MockObject */ private $config; @@ -23,15 +24,15 @@ class LoggerTest extends TestCase { private $logger; /** @var array */ - static private $logs = array(); + private $logs = []; protected function setUp() { parent::setUp(); - self::$logs = array(); + $this->logs = []; $this->config = $this->createMock(\OC\SystemConfig::class); $this->registry = $this->createMock(\OCP\Support\CrashReport\IRegistry::class); - $this->logger = new Log('Test\LoggerTest', $this->config, null, $this->registry); + $this->logger = new Log($this, $this->config, null, $this->registry); } public function testInterpolation() { @@ -63,11 +64,11 @@ class LoggerTest extends TestCase { } private function getLogs() { - return self::$logs; + return $this->logs; } - public static function write($app, $message, $level) { - self::$logs[]= "$level $message"; + public function write(string $app, $message, int $level) { + $this->logs[]= "$level $message"; } public function userAndPasswordData() { @@ -202,23 +203,4 @@ class LoggerTest extends TestCase { $this->assertContains('*** sensitive parameters replaced ***', $logLine); } } - - public function dataGetLogClass() { - return [ - ['file', \OC\Log\File::class], - ['errorlog', \OC\Log\Errorlog::class], - ['syslog', \OC\Log\Syslog::class], - - ['owncloud', \OC\Log\File::class], - ['nextcloud', \OC\Log\File::class], - ['foobar', \OC\Log\File::class], - ]; - } - - /** - * @dataProvider dataGetLogClass - */ - public function testGetLogClass($type, $class) { - $this->assertEquals($class, Log::getLogClass($type)); - } } |