diff options
author | MichaIng <micha@dietpi.com> | 2019-12-04 12:42:55 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-12-04 12:42:55 +0100 |
commit | 4384806f616cf7b9f6a4492ba2fd094afd064f86 (patch) | |
tree | c653df3fcea2c8fce6101d066e0bf42db8026d6e /lib/private | |
parent | 5afd7abf4481f019ad0044393b4734645e40f5af (diff) | |
parent | 76b78edd40fcb5dbe7f0434cbc41d2e291acfec1 (diff) | |
download | nextcloud-server-4384806f616cf7b9f6a4492ba2fd094afd064f86.tar.gz nextcloud-server-4384806f616cf7b9f6a4492ba2fd094afd064f86.zip |
Merge branch 'master' into patch-1
Diffstat (limited to 'lib/private')
254 files changed, 2496 insertions, 1102 deletions
diff --git a/lib/private/Accounts/AccountManager.php b/lib/private/Accounts/AccountManager.php index 408f070dc0d..ada1228954c 100644 --- a/lib/private/Accounts/AccountManager.php +++ b/lib/private/Accounts/AccountManager.php @@ -23,9 +23,9 @@ * */ - namespace OC\Accounts; +use OCA\Settings\BackgroundJobs\VerifyUserData; use OCP\Accounts\IAccount; use OCP\Accounts\IAccountManager; use OCP\BackgroundJob\IJobList; @@ -33,7 +33,6 @@ use OCP\IDBConnection; use OCP\IUser; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\GenericEvent; -use OC\Settings\BackgroundJobs\VerifyUserData; /** * Class AccountManager diff --git a/lib/private/Accounts/Hooks.php b/lib/private/Accounts/Hooks.php index df0b80e8229..2226b1e0b55 100644 --- a/lib/private/Accounts/Hooks.php +++ b/lib/private/Accounts/Hooks.php @@ -21,7 +21,6 @@ * */ - namespace OC\Accounts; use OCP\ILogger; diff --git a/lib/private/Activity/EventMerger.php b/lib/private/Activity/EventMerger.php index ed062d094e5..6b4846164ed 100644 --- a/lib/private/Activity/EventMerger.php +++ b/lib/private/Activity/EventMerger.php @@ -111,7 +111,8 @@ class EventMerger implements IEventMerger { $event->setRichSubject($newSubject, $parameters) ->setParsedSubject($parsedSubject) - ->setChildEvent($previousEvent); + ->setChildEvent($previousEvent) + ->setTimestamp(max($event->getTimestamp(), $previousEvent->getTimestamp())); } catch (\UnexpectedValueException $e) { return $event; } diff --git a/lib/private/Activity/Manager.php b/lib/private/Activity/Manager.php index ba2d1147bc8..29c3c7bbc4d 100644 --- a/lib/private/Activity/Manager.php +++ b/lib/private/Activity/Manager.php @@ -23,6 +23,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Activity; diff --git a/lib/private/App/AppManager.php b/lib/private/App/AppManager.php index 322731d677c..19242245600 100644 --- a/lib/private/App/AppManager.php +++ b/lib/private/App/AppManager.php @@ -13,6 +13,7 @@ * @author Robin Appelman <robin@icewind.nl> * @author Thomas Müller <thomas.mueller@tmit.eu> * @author Vincent Petry <pvince81@owncloud.com> + * @author Daniel Rudolf <nextcloud.com@daniel-rudolf.de> * * @license AGPL-3.0 * @@ -404,6 +405,21 @@ class AppManager implements IAppManager { } /** + * Get the web path for the given app. + * + * @param string $appId + * @return string + * @throws AppPathNotFoundException if app path can't be found + */ + public function getAppWebPath(string $appId): string { + $appWebPath = \OC_App::getAppWebPath($appId); + if($appWebPath === false) { + throw new AppPathNotFoundException('Could not find web path for ' . $appId); + } + return $appWebPath; + } + + /** * Clear the cached list of apps when enabling/disabling an app */ public function clearAppsCache() { diff --git a/lib/private/App/AppStore/Fetcher/AppFetcher.php b/lib/private/App/AppStore/Fetcher/AppFetcher.php index ea69c37f32c..badc4edb444 100644 --- a/lib/private/App/AppStore/Fetcher/AppFetcher.php +++ b/lib/private/App/AppStore/Fetcher/AppFetcher.php @@ -115,6 +115,7 @@ class AppFetcher extends Fetcher { if (empty($releases)) { // Remove apps that don't have a matching release + $response['data'][$dataKey] = []; continue; } @@ -136,7 +137,7 @@ class AppFetcher extends Fetcher { } } - $response['data'] = array_values($response['data']); + $response['data'] = array_values(array_filter($response['data'])); return $response; } diff --git a/lib/private/App/AppStore/Fetcher/Fetcher.php b/lib/private/App/AppStore/Fetcher/Fetcher.php index db8c38ac686..b7679685b40 100644 --- a/lib/private/App/AppStore/Fetcher/Fetcher.php +++ b/lib/private/App/AppStore/Fetcher/Fetcher.php @@ -27,8 +27,8 @@ namespace OC\App\AppStore\Fetcher; -use OC\Files\AppData\Factory; use GuzzleHttp\Exception\ConnectException; +use OC\Files\AppData\Factory; use OCP\AppFramework\Http; use OCP\AppFramework\Utility\ITimeFactory; use OCP\Files\IAppData; diff --git a/lib/private/App/CodeChecker/CodeChecker.php b/lib/private/App/CodeChecker/CodeChecker.php index 8f539897c97..ba0cac3d765 100644 --- a/lib/private/App/CodeChecker/CodeChecker.php +++ b/lib/private/App/CodeChecker/CodeChecker.php @@ -92,7 +92,7 @@ class CodeChecker extends BasicEmitter { }, $excludedDirectories); $iterator = new RecursiveDirectoryIterator($folder, RecursiveDirectoryIterator::SKIP_DOTS); - $iterator = new RecursiveCallbackFilterIterator($iterator, function($item) use ($folder, $excludes){ + $iterator = new RecursiveCallbackFilterIterator($iterator, function($item) use ($excludes){ /** @var SplFileInfo $item */ foreach($excludes as $exclude) { if (substr($item->getPath(), 0, strlen($exclude)) === $exclude) { diff --git a/lib/private/App/CodeChecker/DeprecationCheck.php b/lib/private/App/CodeChecker/DeprecationCheck.php index e672038f347..357876e7385 100644 --- a/lib/private/App/CodeChecker/DeprecationCheck.php +++ b/lib/private/App/CodeChecker/DeprecationCheck.php @@ -119,7 +119,6 @@ class DeprecationCheck extends AbstractCheck { 'OCP\App::getAppInfo' => '14.0.0', 'OC_App::getAppVersion' => '14.0.0', 'OCP\App::getAppVersion' => '14.0.0', - 'OCP\App::registerPersonal' => '14.0.0', 'OCP\AppFramework\Controller::params' => '7.0.0', 'OCP\AppFramework\Controller::getParams' => '7.0.0', diff --git a/lib/private/App/CodeChecker/EmptyCheck.php b/lib/private/App/CodeChecker/EmptyCheck.php index 02fbb46d7de..0a2217212a2 100644 --- a/lib/private/App/CodeChecker/EmptyCheck.php +++ b/lib/private/App/CodeChecker/EmptyCheck.php @@ -19,6 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\App\CodeChecker; class EmptyCheck implements ICheck { diff --git a/lib/private/App/CodeChecker/ICheck.php b/lib/private/App/CodeChecker/ICheck.php index 4dfaa23c538..d34d01cd55d 100644 --- a/lib/private/App/CodeChecker/ICheck.php +++ b/lib/private/App/CodeChecker/ICheck.php @@ -19,6 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\App\CodeChecker; interface ICheck { diff --git a/lib/private/App/PlatformRepository.php b/lib/private/App/PlatformRepository.php index f94fb3ad504..a264a4dd6c5 100644 --- a/lib/private/App/PlatformRepository.php +++ b/lib/private/App/PlatformRepository.php @@ -19,6 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\App; /** diff --git a/lib/private/AppConfig.php b/lib/private/AppConfig.php index 8e921dbb7cf..2888cbb9dde 100644 --- a/lib/private/AppConfig.php +++ b/lib/private/AppConfig.php @@ -43,8 +43,21 @@ class AppConfig implements IAppConfig { /** @var array[] */ protected $sensitiveValues = [ + 'external' => [ + '/^sites$/', + ], 'spreed' => [ + '/^signaling_ticket_secret$/', '/^turn_server_secret$/', + '/^stun_servers$/', + '/^turn_servers$/', + '/^signaling_servers$/', + ], + 'theming' => [ + '/^imprintUrl$/', + '/^privacyUrl$/', + '/^slogan$/', + '/^url$/', ], 'user_ldap' => [ '/^(s..)?ldap_agent_password$/', diff --git a/lib/private/AppFramework/App.php b/lib/private/AppFramework/App.php index 6185a35d1d7..122f4684558 100644 --- a/lib/private/AppFramework/App.php +++ b/lib/private/AppFramework/App.php @@ -26,16 +26,15 @@ declare(strict_types=1); * */ - namespace OC\AppFramework; -use OC\AppFramework\Http\Dispatcher; use OC\AppFramework\DependencyInjection\DIContainer; +use OC\AppFramework\Http\Dispatcher; use OC\HintException; use OCP\AppFramework\Http; -use OCP\AppFramework\QueryException; use OCP\AppFramework\Http\ICallbackResponse; use OCP\AppFramework\Http\IOutput; +use OCP\AppFramework\QueryException; use OCP\IRequest; /** @@ -104,8 +103,6 @@ class App { if ($appName === 'core') { $appNameSpace = 'OC\\Core'; - } else if ($appName === 'settings') { - $appNameSpace = 'OC\\Settings'; } else { $appNameSpace = self::buildAppNamespace($appName); } diff --git a/lib/private/AppFramework/DependencyInjection/DIContainer.php b/lib/private/AppFramework/DependencyInjection/DIContainer.php index 89ebc60b226..b41be947adb 100644 --- a/lib/private/AppFramework/DependencyInjection/DIContainer.php +++ b/lib/private/AppFramework/DependencyInjection/DIContainer.php @@ -32,7 +32,6 @@ * */ - namespace OC\AppFramework\DependencyInjection; use OC; @@ -40,14 +39,15 @@ use OC\AppFramework\Http; use OC\AppFramework\Http\Dispatcher; use OC\AppFramework\Http\Output; use OC\AppFramework\Middleware\MiddlewareDispatcher; -use OC\AppFramework\Middleware\Security\CORSMiddleware; use OC\AppFramework\Middleware\OCSMiddleware; +use OC\AppFramework\Middleware\Security\CORSMiddleware; use OC\AppFramework\Middleware\Security\RateLimitingMiddleware; use OC\AppFramework\Middleware\Security\SecurityMiddleware; use OC\AppFramework\Middleware\SessionMiddleware; use OC\AppFramework\Utility\SimpleContainer; use OC\Core\Middleware\TwoFactorMiddleware; use OC\ServerContainer; +use OCA\WorkflowEngine\Manager; use OCP\AppFramework\Http\IOutput; use OCP\AppFramework\IAppContainer; use OCP\AppFramework\QueryException; @@ -65,7 +65,6 @@ use OCP\IServerContainer; use OCP\ISession; use OCP\IURLGenerator; use OCP\IUserSession; -use OCA\WorkflowEngine\Manager; class DIContainer extends SimpleContainer implements IAppContainer { @@ -279,12 +278,11 @@ class DIContainer extends SimpleContainer implements IAppContainer { ); foreach($this->middleWares as $middleWare) { - $dispatcher->registerMiddleware($c[$middleWare]); + $dispatcher->registerMiddleware($c->query($middleWare)); } $dispatcher->registerMiddleware( new SessionMiddleware( - $c->query(IRequest::class), $c->query(IControllerMethodReflector::class), $c->query(ISession::class) ) diff --git a/lib/private/AppFramework/Http.php b/lib/private/AppFramework/Http.php index e126c8a1cf0..0b3b31fe541 100644 --- a/lib/private/AppFramework/Http.php +++ b/lib/private/AppFramework/Http.php @@ -27,7 +27,6 @@ * */ - namespace OC\AppFramework; use OCP\AppFramework\Http as BaseHttp; @@ -152,5 +151,3 @@ class Http extends BaseHttp { } - - diff --git a/lib/private/AppFramework/Http/Dispatcher.php b/lib/private/AppFramework/Http/Dispatcher.php index 7b9ad015de6..6a04de76adc 100644 --- a/lib/private/AppFramework/Http/Dispatcher.php +++ b/lib/private/AppFramework/Http/Dispatcher.php @@ -27,19 +27,17 @@ declare(strict_types=1); * */ - namespace OC\AppFramework\Http; -use \OC\AppFramework\Middleware\MiddlewareDispatcher; -use \OC\AppFramework\Http; -use \OC\AppFramework\Utility\ControllerMethodReflector; +use OC\AppFramework\Http; +use OC\AppFramework\Middleware\MiddlewareDispatcher; +use OC\AppFramework\Utility\ControllerMethodReflector; use OCP\AppFramework\Controller; -use OCP\AppFramework\Http\Response; use OCP\AppFramework\Http\DataResponse; +use OCP\AppFramework\Http\Response; use OCP\IRequest; - /** * Class to dispatch the request to the middleware dispatcher */ @@ -105,6 +103,10 @@ class Dispatcher { } catch(\Exception $exception){ $response = $this->middlewareDispatcher->afterException( $controller, $methodName, $exception); + } catch(\Throwable $throwable) { + $exception = new \Exception($throwable->getMessage(), $throwable->getCode(), $throwable); + $response = $this->middlewareDispatcher->afterException( + $controller, $methodName, $exception); } $response = $this->middlewareDispatcher->afterController( diff --git a/lib/private/AppFramework/Http/Request.php b/lib/private/AppFramework/Http/Request.php index c205cc5f81b..9d210cd8419 100644 --- a/lib/private/AppFramework/Http/Request.php +++ b/lib/private/AppFramework/Http/Request.php @@ -323,11 +323,7 @@ class Request implements \ArrayAccess, \Countable, IRequest { switch ($name) { case 'CONTENT_TYPE' : case 'CONTENT_LENGTH' : - if (isset($this->server[$name])) { - return $this->server[$name]; - } - break; - case 'REMOTE_ADDR' : + case 'REMOTE_ADDR': if (isset($this->server[$name])) { return $this->server[$name]; } @@ -857,6 +853,10 @@ class Request implements \ArrayAccess, \Countable, IRequest { * @return string Server host */ public function getInsecureServerHost(): string { + if ($this->fromTrustedProxy() && $this->getOverwriteHost() !== null) { + return $this->getOverwriteHost(); + } + $host = 'localhost'; if ($this->fromTrustedProxy() && isset($this->server['HTTP_X_FORWARDED_HOST'])) { if (strpos($this->server['HTTP_X_FORWARDED_HOST'], ',') !== false) { @@ -872,6 +872,7 @@ class Request implements \ArrayAccess, \Countable, IRequest { $host = $this->server['SERVER_NAME']; } } + return $host; } diff --git a/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php b/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php index e1262b6c712..70440e9623f 100644 --- a/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php +++ b/lib/private/AppFramework/Middleware/MiddlewareDispatcher.php @@ -26,7 +26,6 @@ declare(strict_types=1); * */ - namespace OC\AppFramework\Middleware; use OCP\AppFramework\Controller; diff --git a/lib/private/AppFramework/Middleware/OCSMiddleware.php b/lib/private/AppFramework/Middleware/OCSMiddleware.php index ad1d953f372..2174693a1d7 100644 --- a/lib/private/AppFramework/Middleware/OCSMiddleware.php +++ b/lib/private/AppFramework/Middleware/OCSMiddleware.php @@ -21,6 +21,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ + namespace OC\AppFramework\Middleware; use OC\AppFramework\Http; @@ -32,10 +33,10 @@ use OCP\AppFramework\Controller; use OCP\AppFramework\Http\DataResponse; use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\Response; +use OCP\AppFramework\Middleware; use OCP\AppFramework\OCS\OCSException; use OCP\AppFramework\OCSController; use OCP\IRequest; -use OCP\AppFramework\Middleware; class OCSMiddleware extends Middleware { diff --git a/lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php b/lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php index d752a68cf32..9f005448387 100644 --- a/lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php +++ b/lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php @@ -20,6 +20,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ + namespace OC\AppFramework\Middleware\Security; use OC\AppFramework\Middleware\Security\Exceptions\NotConfirmedException; diff --git a/lib/private/AppFramework/Middleware/Security/SameSiteCookieMiddleware.php b/lib/private/AppFramework/Middleware/Security/SameSiteCookieMiddleware.php index 22a9246d661..3c011945477 100644 --- a/lib/private/AppFramework/Middleware/Security/SameSiteCookieMiddleware.php +++ b/lib/private/AppFramework/Middleware/Security/SameSiteCookieMiddleware.php @@ -20,6 +20,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ + namespace OC\AppFramework\Middleware\Security; use OC\AppFramework\Http\Request; diff --git a/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php b/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php index 4f380f07d91..16725b1f5d6 100644 --- a/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php +++ b/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php @@ -28,36 +28,30 @@ declare(strict_types=1); * */ - namespace OC\AppFramework\Middleware\Security; use OC\AppFramework\Middleware\Security\Exceptions\AppNotEnabledException; use OC\AppFramework\Middleware\Security\Exceptions\CrossSiteRequestForgeryException; use OC\AppFramework\Middleware\Security\Exceptions\NotAdminException; use OC\AppFramework\Middleware\Security\Exceptions\NotLoggedInException; +use OC\AppFramework\Middleware\Security\Exceptions\SecurityException; use OC\AppFramework\Middleware\Security\Exceptions\StrictCookieMissingException; use OC\AppFramework\Utility\ControllerMethodReflector; -use OC\Security\CSP\ContentSecurityPolicyManager; -use OC\Security\CSP\ContentSecurityPolicyNonceManager; -use OC\Security\CSRF\CsrfTokenManager; use OCP\App\AppPathNotFoundException; use OCP\App\IAppManager; -use OCP\AppFramework\Http\ContentSecurityPolicy; -use OCP\AppFramework\Http\EmptyContentSecurityPolicy; +use OCP\AppFramework\Controller; +use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\Http\RedirectResponse; +use OCP\AppFramework\Http\Response; use OCP\AppFramework\Http\TemplateResponse; use OCP\AppFramework\Middleware; -use OCP\AppFramework\Http\Response; -use OCP\AppFramework\Http\JSONResponse; use OCP\AppFramework\OCSController; use OCP\IL10N; +use OCP\ILogger; use OCP\INavigationManager; -use OCP\IURLGenerator; use OCP\IRequest; -use OCP\ILogger; -use OCP\AppFramework\Controller; +use OCP\IURLGenerator; use OCP\Util; -use OC\AppFramework\Middleware\Security\Exceptions\SecurityException; /** * Used to do all the authentication and checking stuff for a controller method @@ -121,6 +115,8 @@ class SecurityMiddleware extends Middleware { * @param Controller $controller the controller * @param string $methodName the name of the method * @throws SecurityException when a security check fails + * + * @suppress PhanUndeclaredClassConstant */ public function beforeController($controller, $methodName) { @@ -128,6 +124,10 @@ class SecurityMiddleware extends Middleware { // for normal HTML requests and not for AJAX requests $this->navigationManager->setActiveEntry($this->appName); + if ($controller === \OCA\Talk\Controller\PageController::class && $methodName === 'showCall') { + $this->navigationManager->setActiveEntry('spreed'); + } + // security checks $isPublicPage = $this->reflector->hasAnnotation('PublicPage'); if(!$isPublicPage) { diff --git a/lib/private/AppFramework/Middleware/SessionMiddleware.php b/lib/private/AppFramework/Middleware/SessionMiddleware.php index 7b52cc1e319..cc927e6875c 100644 --- a/lib/private/AppFramework/Middleware/SessionMiddleware.php +++ b/lib/private/AppFramework/Middleware/SessionMiddleware.php @@ -27,32 +27,21 @@ namespace OC\AppFramework\Middleware; use OC\AppFramework\Utility\ControllerMethodReflector; use OCP\AppFramework\Controller; -use OCP\IRequest; use OCP\AppFramework\Http\Response; use OCP\AppFramework\Middleware; +use OCP\IRequest; use OCP\ISession; class SessionMiddleware extends Middleware { - /** - * @var IRequest - */ - private $request; - - /** - * @var ControllerMethodReflector - */ + /** @var ControllerMethodReflector */ private $reflector; - /** - * @param IRequest $request - * @param ControllerMethodReflector $reflector - */ - public function __construct(IRequest $request, - ControllerMethodReflector $reflector, - ISession $session -) { - $this->request = $request; + /** @var ISession */ + private $session; + + public function __construct(ControllerMethodReflector $reflector, + ISession $session) { $this->reflector = $reflector; $this->session = $session; } diff --git a/lib/private/AppFramework/OCS/BaseResponse.php b/lib/private/AppFramework/OCS/BaseResponse.php index 90ea084dd99..19567f8fdaf 100644 --- a/lib/private/AppFramework/OCS/BaseResponse.php +++ b/lib/private/AppFramework/OCS/BaseResponse.php @@ -20,6 +20,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ + namespace OC\AppFramework\OCS; use OCP\AppFramework\Http; diff --git a/lib/private/AppFramework/OCS/V1Response.php b/lib/private/AppFramework/OCS/V1Response.php index 08b11788110..02405b5edbe 100644 --- a/lib/private/AppFramework/OCS/V1Response.php +++ b/lib/private/AppFramework/OCS/V1Response.php @@ -20,6 +20,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ + namespace OC\AppFramework\OCS; use OCP\API; diff --git a/lib/private/AppFramework/OCS/V2Response.php b/lib/private/AppFramework/OCS/V2Response.php index 7e98efe867d..54e9e8bd9a7 100644 --- a/lib/private/AppFramework/OCS/V2Response.php +++ b/lib/private/AppFramework/OCS/V2Response.php @@ -20,10 +20,11 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ + namespace OC\AppFramework\OCS; -use OCP\AppFramework\Http; use OCP\API; +use OCP\AppFramework\Http; class V2Response extends BaseResponse { diff --git a/lib/private/AppFramework/Routing/RouteActionHandler.php b/lib/private/AppFramework/Routing/RouteActionHandler.php index bcbe2eb400b..7e1c089d2a8 100644 --- a/lib/private/AppFramework/Routing/RouteActionHandler.php +++ b/lib/private/AppFramework/Routing/RouteActionHandler.php @@ -25,8 +25,8 @@ namespace OC\AppFramework\Routing; -use \OC\AppFramework\App; -use \OC\AppFramework\DependencyInjection\DIContainer; +use OC\AppFramework\App; +use OC\AppFramework\DependencyInjection\DIContainer; class RouteActionHandler { private $controllerName; diff --git a/lib/private/AppFramework/Utility/ControllerMethodReflector.php b/lib/private/AppFramework/Utility/ControllerMethodReflector.php index ef4a1959d66..187fb43a9fc 100644 --- a/lib/private/AppFramework/Utility/ControllerMethodReflector.php +++ b/lib/private/AppFramework/Utility/ControllerMethodReflector.php @@ -29,7 +29,7 @@ declare(strict_types=1); namespace OC\AppFramework\Utility; -use \OCP\AppFramework\Utility\IControllerMethodReflector; +use OCP\AppFramework\Utility\IControllerMethodReflector; /** * Reads and parses annotations from doc comments @@ -72,13 +72,10 @@ class ControllerMethodReflector implements IControllerMethodReflector { } foreach ($reflection->getParameters() as $param) { - // extract type information from PHP 7 scalar types and prefer them - // over phpdoc annotations - if (method_exists($param, 'getType')) { - $type = $param->getType(); - if ($type !== null) { - $this->types[$param->getName()] = (string) $type; - } + // extract type information from PHP 7 scalar types and prefer them over phpdoc annotations + $type = $param->getType(); + if ($type instanceof \ReflectionNamedType) { + $this->types[$param->getName()] = $type->getName(); } $default = null; diff --git a/lib/private/AppFramework/Utility/SimpleContainer.php b/lib/private/AppFramework/Utility/SimpleContainer.php index 6f50523bbf6..b8398623490 100644 --- a/lib/private/AppFramework/Utility/SimpleContainer.php +++ b/lib/private/AppFramework/Utility/SimpleContainer.php @@ -28,12 +28,12 @@ namespace OC\AppFramework\Utility; -use ReflectionClass; -use ReflectionException; use Closure; -use Pimple\Container; use OCP\AppFramework\QueryException; use OCP\IContainer; +use Pimple\Container; +use ReflectionClass; +use ReflectionException; /** * Class SimpleContainer diff --git a/lib/private/AppFramework/Utility/TimeFactory.php b/lib/private/AppFramework/Utility/TimeFactory.php index 4526f9b1abb..841d1838354 100644 --- a/lib/private/AppFramework/Utility/TimeFactory.php +++ b/lib/private/AppFramework/Utility/TimeFactory.php @@ -27,7 +27,6 @@ namespace OC\AppFramework\Utility; use OCP\AppFramework\Utility\ITimeFactory; - /** * Needed to mock calls to time() */ diff --git a/lib/private/Authentication/Exceptions/ExpiredTokenException.php b/lib/private/Authentication/Exceptions/ExpiredTokenException.php index d5b2e2cbca7..ddc611b5ce3 100644 --- a/lib/private/Authentication/Exceptions/ExpiredTokenException.php +++ b/lib/private/Authentication/Exceptions/ExpiredTokenException.php @@ -21,6 +21,7 @@ declare(strict_types=1); * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ + namespace OC\Authentication\Exceptions; use OC\Authentication\Token\IToken; diff --git a/lib/private/User/Events/PostLoginEvent.php b/lib/private/Authentication/Exceptions/TokenPasswordExpiredException.php index d14030b5294..6719037b4a0 100644 --- a/lib/private/User/Events/PostLoginEvent.php +++ b/lib/private/Authentication/Exceptions/TokenPasswordExpiredException.php @@ -22,42 +22,8 @@ declare(strict_types=1); * */ -namespace OC\User\Events; +namespace OC\Authentication\Exceptions; -use OCP\EventDispatcher\Event; -use OCP\IUser; +class TokenPasswordExpiredException extends ExpiredTokenException { -class PostLoginEvent extends Event { - - /** @var IUser */ - private $user; - /** @var string */ - private $password; - /** @var bool */ - private $isTokenLogin; - - - public function __construct(IUser $user, string $password, bool $isTokenLogin) { - parent::__construct(); - - $this->user = $user; - $this->password = $password; - $this->isTokenLogin = $isTokenLogin; - } - - public function getUser(): IUser { - return $this->user; - } - - public function hasPassword(): bool { - return $this->password !== ''; - } - - public function getPassword(): string { - return $this->password; - } - - public function getIsTokenLogin(): bool { - return $this->isTokenLogin; - } } diff --git a/lib/private/Authentication/Exceptions/WipeTokenException.php b/lib/private/Authentication/Exceptions/WipeTokenException.php index c56059fd7b9..03b679596e2 100644 --- a/lib/private/Authentication/Exceptions/WipeTokenException.php +++ b/lib/private/Authentication/Exceptions/WipeTokenException.php @@ -21,6 +21,7 @@ declare(strict_types=1); * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ + namespace OC\Authentication\Exceptions; use OC\Authentication\Token\IToken; diff --git a/lib/private/Authentication/Login/CreateSessionTokenCommand.php b/lib/private/Authentication/Login/CreateSessionTokenCommand.php index 14ad6d18b30..59d5c68a24b 100644 --- a/lib/private/Authentication/Login/CreateSessionTokenCommand.php +++ b/lib/private/Authentication/Login/CreateSessionTokenCommand.php @@ -59,7 +59,7 @@ class CreateSessionTokenCommand extends ALoginCommand { ); $this->userSession->updateTokens( $loginData->getUser()->getUID(), - $loginData->getUsername() + $loginData->getPassword() ); return $this->processNextOrFinishSuccessfully($loginData); diff --git a/lib/private/Authentication/Login/PreLoginHookCommand.php b/lib/private/Authentication/Login/PreLoginHookCommand.php index 87c52c6fef1..e8b5c0b6aaa 100644 --- a/lib/private/Authentication/Login/PreLoginHookCommand.php +++ b/lib/private/Authentication/Login/PreLoginHookCommand.php @@ -51,4 +51,4 @@ class PreLoginHookCommand extends ALoginCommand { return $this->processNextOrFinishSuccessfully($loginData); } -}
\ No newline at end of file +} diff --git a/lib/private/Authentication/LoginCredentials/Store.php b/lib/private/Authentication/LoginCredentials/Store.php index 0ed19a2dd07..5f8be9b769f 100644 --- a/lib/private/Authentication/LoginCredentials/Store.php +++ b/lib/private/Authentication/LoginCredentials/Store.php @@ -1,4 +1,5 @@ -<?php +<?php declare(strict_types=1); + /** * @copyright 2016 Christoph Wurst <christoph@winzerhof-wurst.at> * @@ -82,8 +83,8 @@ class Store implements IStore { * @return ICredentials the login credentials of the current user * @throws CredentialsUnavailableException */ - public function getLoginCredentials() { - if (is_null($this->tokenProvider)) { + public function getLoginCredentials(): ICredentials { + if ($this->tokenProvider === null) { throw new CredentialsUnavailableException(); } diff --git a/lib/private/Authentication/Token/DefaultTokenProvider.php b/lib/private/Authentication/Token/DefaultTokenProvider.php index 98609a3f14b..6bd7c2c6dc8 100644 --- a/lib/private/Authentication/Token/DefaultTokenProvider.php +++ b/lib/private/Authentication/Token/DefaultTokenProvider.php @@ -196,8 +196,9 @@ class DefaultTokenProvider implements IProvider { * @param string $oldSessionId * @param string $sessionId * @throws InvalidTokenException + * @return IToken */ - public function renewSessionToken(string $oldSessionId, string $sessionId) { + public function renewSessionToken(string $oldSessionId, string $sessionId): IToken { $token = $this->getToken($oldSessionId); $newToken = new DefaultToken(); @@ -214,6 +215,8 @@ class DefaultTokenProvider implements IProvider { $newToken->setLastActivity($this->time->getTime()); $this->mapper->insert($newToken); $this->mapper->delete($token); + + return $newToken; } /** diff --git a/lib/private/Authentication/Token/INamedToken.php b/lib/private/Authentication/Token/INamedToken.php index ede075a21c0..403116cb660 100644 --- a/lib/private/Authentication/Token/INamedToken.php +++ b/lib/private/Authentication/Token/INamedToken.php @@ -31,4 +31,4 @@ interface INamedToken extends IToken { * @return void */ public function setName(string $name): void; -}
\ No newline at end of file +} diff --git a/lib/private/Authentication/Token/IProvider.php b/lib/private/Authentication/Token/IProvider.php index 860b93e16c2..ba8df30eb8d 100644 --- a/lib/private/Authentication/Token/IProvider.php +++ b/lib/private/Authentication/Token/IProvider.php @@ -84,8 +84,9 @@ interface IProvider { * @param string $sessionId * @throws InvalidTokenException * @throws \RuntimeException when OpenSSL reports a problem + * @return IToken The new token */ - public function renewSessionToken(string $oldSessionId, string $sessionId); + public function renewSessionToken(string $oldSessionId, string $sessionId): IToken; /** * Invalidate (delete) the given session token diff --git a/lib/private/Authentication/Token/Manager.php b/lib/private/Authentication/Token/Manager.php index 76c0dfb8695..0642d4426c4 100644 --- a/lib/private/Authentication/Token/Manager.php +++ b/lib/private/Authentication/Token/Manager.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace OC\Authentication\Token; +use Doctrine\DBAL\Exception\UniqueConstraintViolationException; use OC\Authentication\Exceptions\ExpiredTokenException; use OC\Authentication\Exceptions\InvalidTokenException; use OC\Authentication\Exceptions\PasswordlessTokenException; @@ -60,15 +61,29 @@ class Manager implements IProvider { string $name, int $type = IToken::TEMPORARY_TOKEN, int $remember = IToken::DO_NOT_REMEMBER): IToken { - return $this->publicKeyTokenProvider->generateToken( - $token, - $uid, - $loginName, - $password, - $name, - $type, - $remember - ); + try { + return $this->publicKeyTokenProvider->generateToken( + $token, + $uid, + $loginName, + $password, + $name, + $type, + $remember + ); + } catch (UniqueConstraintViolationException $e) { + // It's rare, but if two requests of the same session (e.g. env-based SAML) + // try to create the session token they might end up here at the same time + // because we use the session ID as token and the db token is created anew + // with every request. + // + // If the UIDs match, then this should be fine. + $existing = $this->getToken($token); + if ($existing->getUID() !== $uid) { + throw new \Exception('Token conflict handled, but UIDs do not match. This should not happen', 0, $e); + } + return $existing; + } } /** @@ -158,14 +173,15 @@ class Manager implements IProvider { * @param string $oldSessionId * @param string $sessionId * @throws InvalidTokenException + * @return IToken */ - public function renewSessionToken(string $oldSessionId, string $sessionId) { + public function renewSessionToken(string $oldSessionId, string $sessionId): IToken { try { - $this->publicKeyTokenProvider->renewSessionToken($oldSessionId, $sessionId); + return $this->publicKeyTokenProvider->renewSessionToken($oldSessionId, $sessionId); } catch (ExpiredTokenException $e) { throw $e; } catch (InvalidTokenException $e) { - $this->defaultTokenProvider->renewSessionToken($oldSessionId, $sessionId); + return $this->defaultTokenProvider->renewSessionToken($oldSessionId, $sessionId); } } diff --git a/lib/private/Authentication/Token/PublicKeyTokenMapper.php b/lib/private/Authentication/Token/PublicKeyTokenMapper.php index df91066c44f..62fbf1c6d7a 100644 --- a/lib/private/Authentication/Token/PublicKeyTokenMapper.php +++ b/lib/private/Authentication/Token/PublicKeyTokenMapper.php @@ -163,7 +163,8 @@ class PublicKeyTokenMapper extends QBMapper { $qb = $this->db->getQueryBuilder(); $qb->delete('authtoken') - ->where($qb->expr()->eq('type', $qb->createNamedParameter(IToken::TEMPORARY_TOKEN))) + ->where($qb->expr()->eq('uid', $qb->createNamedParameter($except->getUID()))) + ->andWhere($qb->expr()->eq('type', $qb->createNamedParameter(IToken::TEMPORARY_TOKEN))) ->andWhere($qb->expr()->neq('id', $qb->createNamedParameter($except->getId()))) ->andWhere($qb->expr()->eq('version', $qb->createNamedParameter(PublicKeyToken::VERSION, IQueryBuilder::PARAM_INT))); diff --git a/lib/private/Authentication/Token/PublicKeyTokenProvider.php b/lib/private/Authentication/Token/PublicKeyTokenProvider.php index 318d4468ddc..19987bec253 100644 --- a/lib/private/Authentication/Token/PublicKeyTokenProvider.php +++ b/lib/private/Authentication/Token/PublicKeyTokenProvider.php @@ -25,8 +25,10 @@ namespace OC\Authentication\Token; use OC\Authentication\Exceptions\ExpiredTokenException; use OC\Authentication\Exceptions\InvalidTokenException; +use OC\Authentication\Exceptions\TokenPasswordExpiredException; use OC\Authentication\Exceptions\PasswordlessTokenException; use OC\Authentication\Exceptions\WipeTokenException; +use OC\Cache\CappedMemoryCache; use OCP\AppFramework\Db\DoesNotExistException; use OCP\AppFramework\Utility\ITimeFactory; use OCP\IConfig; @@ -49,6 +51,9 @@ class PublicKeyTokenProvider implements IProvider { /** @var ITimeFactory $time */ private $time; + /** @var CappedMemoryCache */ + private $cache; + public function __construct(PublicKeyTokenMapper $mapper, ICrypto $crypto, IConfig $config, @@ -59,6 +64,8 @@ class PublicKeyTokenProvider implements IProvider { $this->config = $config; $this->logger = $logger; $this->time = $time; + + $this->cache = new CappedMemoryCache(); } /** @@ -72,17 +79,26 @@ class PublicKeyTokenProvider implements IProvider { int $type = IToken::TEMPORARY_TOKEN, int $remember = IToken::DO_NOT_REMEMBER): IToken { $dbToken = $this->newToken($token, $uid, $loginName, $password, $name, $type, $remember); - $this->mapper->insert($dbToken); + // Add the token to the cache + $this->cache[$dbToken->getToken()] = $dbToken; + return $dbToken; } public function getToken(string $tokenId): IToken { - try { - $token = $this->mapper->getToken($this->hashToken($tokenId)); - } catch (DoesNotExistException $ex) { - throw new InvalidTokenException(); + $tokenHash = $this->hashToken($tokenId); + + if (isset($this->cache[$tokenHash])) { + $token = $this->cache[$tokenHash]; + } else { + try { + $token = $this->mapper->getToken($this->hashToken($tokenId)); + $this->cache[$token->getToken()] = $token; + } catch (DoesNotExistException $ex) { + throw new InvalidTokenException(); + } } if ((int)$token->getExpires() !== 0 && $token->getExpires() < $this->time->getTime()) { @@ -93,6 +109,11 @@ class PublicKeyTokenProvider implements IProvider { throw new WipeTokenException($token); } + if ($token->getPasswordInvalid() === true) { + //The password is invalid we should throw an TokenPasswordExpiredException + throw new TokenPasswordExpiredException($token); + } + return $token; } @@ -111,10 +132,17 @@ class PublicKeyTokenProvider implements IProvider { throw new WipeTokenException($token); } + if ($token->getPasswordInvalid() === true) { + //The password is invalid we should throw an TokenPasswordExpiredException + throw new TokenPasswordExpiredException($token); + } + return $token; } - public function renewSessionToken(string $oldSessionId, string $sessionId) { + public function renewSessionToken(string $oldSessionId, string $sessionId): IToken { + $this->cache->clear(); + $token = $this->getToken($oldSessionId); if (!($token instanceof PublicKeyToken)) { @@ -127,7 +155,7 @@ class PublicKeyTokenProvider implements IProvider { $password = $this->decryptPassword($token->getPassword(), $privateKey); } - $this->generateToken( + $newToken = $this->generateToken( $sessionId, $token->getUID(), $token->getLoginName(), @@ -138,17 +166,25 @@ class PublicKeyTokenProvider implements IProvider { ); $this->mapper->delete($token); + + return $newToken; } public function invalidateToken(string $token) { + $this->cache->clear(); + $this->mapper->invalidate($this->hashToken($token)); } public function invalidateTokenById(string $uid, int $id) { + $this->cache->clear(); + $this->mapper->deleteById($uid, $id); } public function invalidateOldTokens() { + $this->cache->clear(); + $olderThan = $this->time->getTime() - (int) $this->config->getSystemValue('session_lifetime', 60 * 60 * 24); $this->logger->debug('Invalidating session tokens older than ' . date('c', $olderThan), ['app' => 'cron']); $this->mapper->invalidateOld($olderThan, IToken::DO_NOT_REMEMBER); @@ -158,6 +194,8 @@ class PublicKeyTokenProvider implements IProvider { } public function updateToken(IToken $token) { + $this->cache->clear(); + if (!($token instanceof PublicKeyToken)) { throw new InvalidTokenException(); } @@ -165,6 +203,8 @@ class PublicKeyTokenProvider implements IProvider { } public function updateTokenActivity(IToken $token) { + $this->cache->clear(); + if (!($token instanceof PublicKeyToken)) { throw new InvalidTokenException(); } @@ -198,6 +238,8 @@ class PublicKeyTokenProvider implements IProvider { } public function setPassword(IToken $token, string $tokenId, string $password) { + $this->cache->clear(); + if (!($token instanceof PublicKeyToken)) { throw new InvalidTokenException(); } @@ -215,6 +257,8 @@ class PublicKeyTokenProvider implements IProvider { } public function rotate(IToken $token, string $oldTokenId, string $newTokenId): IToken { + $this->cache->clear(); + if (!($token instanceof PublicKeyToken)) { throw new InvalidTokenException(); } @@ -274,6 +318,8 @@ class PublicKeyTokenProvider implements IProvider { * @throws \RuntimeException when OpenSSL reports a problem */ public function convertToken(DefaultToken $defaultToken, string $token, $password): PublicKeyToken { + $this->cache->clear(); + $pkToken = $this->newToken( $token, $defaultToken->getUID(), @@ -344,6 +390,8 @@ class PublicKeyTokenProvider implements IProvider { } public function markPasswordInvalid(IToken $token, string $tokenId) { + $this->cache->clear(); + if (!($token instanceof PublicKeyToken)) { throw new InvalidTokenException(); } @@ -353,6 +401,8 @@ class PublicKeyTokenProvider implements IProvider { } public function updatePasswords(string $uid, string $password) { + $this->cache->clear(); + if (!$this->mapper->hasExpiredTokens($uid)) { // Nothing to do here return; diff --git a/lib/private/Authentication/TwoFactorAuth/Registry.php b/lib/private/Authentication/TwoFactorAuth/Registry.php index 2f905441953..791e9eb9954 100644 --- a/lib/private/Authentication/TwoFactorAuth/Registry.php +++ b/lib/private/Authentication/TwoFactorAuth/Registry.php @@ -30,20 +30,19 @@ use OC\Authentication\TwoFactorAuth\Db\ProviderUserAssignmentDao; use OCP\Authentication\TwoFactorAuth\IProvider; use OCP\Authentication\TwoFactorAuth\IRegistry; use OCP\Authentication\TwoFactorAuth\RegistryEvent; +use OCP\EventDispatcher\IEventDispatcher; use OCP\IUser; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use Symfony\Component\EventDispatcher\GenericEvent; class Registry implements IRegistry { /** @var ProviderUserAssignmentDao */ private $assignmentDao; - /** @var EventDispatcherInterface */ + /** @var IEventDispatcher */ private $dispatcher; public function __construct(ProviderUserAssignmentDao $assignmentDao, - EventDispatcherInterface $dispatcher) { + IEventDispatcher $dispatcher) { $this->assignmentDao = $assignmentDao; $this->dispatcher = $dispatcher; } diff --git a/lib/private/Avatar/Avatar.php b/lib/private/Avatar/Avatar.php index 172f47d12a8..e7baa10fbb3 100644 --- a/lib/private/Avatar/Avatar.php +++ b/lib/private/Avatar/Avatar.php @@ -32,12 +32,12 @@ declare(strict_types=1); namespace OC\Avatar; +use Imagick; use OC\Color; +use OC_Image; use OCP\Files\NotFoundException; use OCP\IAvatar; use OCP\ILogger; -use OC_Image; -use Imagick; /** * This class gets and sets users avatars. diff --git a/lib/private/Avatar/AvatarManager.php b/lib/private/Avatar/AvatarManager.php index 567ed3aec1e..b3f475ac698 100644 --- a/lib/private/Avatar/AvatarManager.php +++ b/lib/private/Avatar/AvatarManager.php @@ -34,8 +34,8 @@ use OCP\Files\NotFoundException; use OCP\IAvatar; use OCP\IAvatarManager; use OCP\IConfig; -use OCP\ILogger; use OCP\IL10N; +use OCP\ILogger; /** * This class implements methods to access Avatar functionality diff --git a/lib/private/Avatar/UserAvatar.php b/lib/private/Avatar/UserAvatar.php index 2db65634e8c..f96ec93d5d0 100644 --- a/lib/private/Avatar/UserAvatar.php +++ b/lib/private/Avatar/UserAvatar.php @@ -97,7 +97,7 @@ class UserAvatar extends Avatar { $this->validateAvatar($img); - $this->remove(); + $this->remove(true); $type = $this->getAvatarImageType($img); $file = $this->folder->newFile('avatar.' . $type); $file->putContent($data); @@ -193,7 +193,7 @@ class UserAvatar extends Avatar { * @throws \OCP\Files\NotPermittedException * @throws \OCP\PreConditionNotMetException */ - public function remove() { + public function remove(bool $silent = false) { $avatars = $this->folder->getDirectoryListing(); $this->config->setUserValue($this->user->getUID(), 'avatar', 'version', @@ -203,7 +203,9 @@ class UserAvatar extends Avatar { $avatar->delete(); } $this->config->setUserValue($this->user->getUID(), 'avatar', 'generated', 'true'); - $this->user->triggerChange('avatar', ''); + if(!$silent) { + $this->user->triggerChange('avatar', ''); + } } /** diff --git a/lib/private/BackgroundJob/JobList.php b/lib/private/BackgroundJob/JobList.php index 44b6786841f..7b5469c48f4 100644 --- a/lib/private/BackgroundJob/JobList.php +++ b/lib/private/BackgroundJob/JobList.php @@ -30,9 +30,9 @@ namespace OC\BackgroundJob; use OCP\AppFramework\QueryException; use OCP\AppFramework\Utility\ITimeFactory; +use OCP\AutoloadNotAllowedException; use OCP\BackgroundJob\IJob; use OCP\BackgroundJob\IJobList; -use OCP\AutoloadNotAllowedException; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IConfig; use OCP\IDBConnection; diff --git a/lib/private/Broadcast/Events/BroadcastEvent.php b/lib/private/Broadcast/Events/BroadcastEvent.php new file mode 100644 index 00000000000..f3282b5207c --- /dev/null +++ b/lib/private/Broadcast/Events/BroadcastEvent.php @@ -0,0 +1,59 @@ +<?php declare(strict_types=1); + +/** + * @copyright 2019 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @author 2019 Christoph Wurst <christoph@winzerhof-wurst.at> + * + * @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\Broadcast\Events; + +use JsonSerializable; +use OCP\Broadcast\Events\IBroadcastEvent; +use OCP\EventDispatcher\ABroadcastedEvent; +use OCP\EventDispatcher\Event; + +class BroadcastEvent extends Event implements IBroadcastEvent { + + /** @var ABroadcastedEvent */ + private $event; + + public function __construct(ABroadcastedEvent $event) { + parent::__construct(); + + $this->event = $event; + } + + public function getName(): string { + return $this->event->broadcastAs(); + } + + public function getUids(): array { + return $this->event->getUids(); + } + + public function getPayload(): JsonSerializable { + return $this->event; + } + + public function setBroadcasted(): void { + $this->event->setBroadcasted(); + } + +} diff --git a/lib/private/CapabilitiesManager.php b/lib/private/CapabilitiesManager.php index ceb8205a7bb..b7ecce144c8 100644 --- a/lib/private/CapabilitiesManager.php +++ b/lib/private/CapabilitiesManager.php @@ -22,7 +22,6 @@ declare(strict_types=1); * */ - namespace OC; use OCP\AppFramework\QueryException; diff --git a/lib/private/Collaboration/Collaborators/RemoteGroupPlugin.php b/lib/private/Collaboration/Collaborators/RemoteGroupPlugin.php index 6e0979fe418..d9e1f2fd49a 100644 --- a/lib/private/Collaboration/Collaborators/RemoteGroupPlugin.php +++ b/lib/private/Collaboration/Collaborators/RemoteGroupPlugin.php @@ -57,11 +57,15 @@ class RemoteGroupPlugin implements ISearchPlugin { $resultType = new SearchResultType('remote_groups'); if ($this->enabled && $this->cloudIdManager->isValidCloudId($search) && $offset === 0) { + list($remoteGroup, $serverUrl) = $this->splitGroupRemote($search); $result['exact'][] = [ - 'label' => $search, + 'label' => $remoteGroup . " ($serverUrl)", + 'guid' => $remoteGroup, + 'name' => $remoteGroup, 'value' => [ 'shareType' => Share::SHARE_TYPE_REMOTE_GROUP, 'shareWith' => $search, + 'server' => $serverUrl, ], ]; } @@ -71,4 +75,20 @@ class RemoteGroupPlugin implements ISearchPlugin { return true; } + /** + * split group and remote from federated cloud id + * + * @param string $address federated share address + * @return array [user, remoteURL] + * @throws \InvalidArgumentException + */ + public function splitGroupRemote($address) { + try { + $cloudId = $this->cloudIdManager->resolveCloudId($address); + return [$cloudId->getUser(), $cloudId->getRemote()]; + } catch (\InvalidArgumentException $e) { + throw new \InvalidArgumentException('Invalid Federated Cloud ID', 0, $e); + } + } + } diff --git a/lib/private/Collaboration/Collaborators/RemotePlugin.php b/lib/private/Collaboration/Collaborators/RemotePlugin.php index d877346b155..fd14e7e03be 100644 --- a/lib/private/Collaboration/Collaborators/RemotePlugin.php +++ b/lib/private/Collaboration/Collaborators/RemotePlugin.php @@ -152,10 +152,13 @@ class RemotePlugin implements ISearchPlugin { $localUser = $this->userManager->get($remoteUser); if ($localUser === null || $search !== $localUser->getCloudId()) { $result['exact'][] = [ - 'label' => $search, + 'label' => $remoteUser . " ($serverUrl)", + 'uuid' => $remoteUser, + 'name' => $remoteUser, 'value' => [ 'shareType' => Share::SHARE_TYPE_REMOTE, 'shareWith' => $search, + 'server' => $serverUrl, ], ]; } diff --git a/lib/private/Collaboration/Collaborators/UserPlugin.php b/lib/private/Collaboration/Collaborators/UserPlugin.php index 971b7025564..9de269224f5 100644 --- a/lib/private/Collaboration/Collaborators/UserPlugin.php +++ b/lib/private/Collaboration/Collaborators/UserPlugin.php @@ -71,7 +71,7 @@ class UserPlugin implements ISearchPlugin { foreach ($userGroups as $userGroup) { $usersTmp = $this->groupManager->displayNamesInGroup($userGroup, $search, $limit, $offset); foreach ($usersTmp as $uid => $userDisplayName) { - $users[$uid] = $userDisplayName; + $users[(string) $uid] = $userDisplayName; } } } else { @@ -80,7 +80,7 @@ class UserPlugin implements ISearchPlugin { foreach ($usersTmp as $user) { if ($user->isEnabled()) { // Don't keep deactivated users - $users[$user->getUID()] = $user->getDisplayName(); + $users[(string) $user->getUID()] = $user->getDisplayName(); } } } @@ -94,6 +94,7 @@ class UserPlugin implements ISearchPlugin { $foundUserById = false; $lowerSearch = strtolower($search); foreach ($users as $uid => $userDisplayName) { + $uid = (string) $uid; if (strtolower($uid) === $lowerSearch || strtolower($userDisplayName) === $lowerSearch) { if (strtolower($uid) === $lowerSearch) { $foundUserById = true; diff --git a/lib/private/Collaboration/Resources/Collection.php b/lib/private/Collaboration/Resources/Collection.php index 6d37cebdc2f..beef8ba9bd8 100644 --- a/lib/private/Collaboration/Resources/Collection.php +++ b/lib/private/Collaboration/Resources/Collection.php @@ -24,10 +24,10 @@ namespace OC\Collaboration\Resources; use Doctrine\DBAL\Exception\ConstraintViolationException; -use OCP\Collaboration\Resources\IManager; -use OCP\Collaboration\Resources\ResourceException; use OCP\Collaboration\Resources\ICollection; +use OCP\Collaboration\Resources\IManager; use OCP\Collaboration\Resources\IResource; +use OCP\Collaboration\Resources\ResourceException; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; use OCP\IUser; diff --git a/lib/private/Collaboration/Resources/Manager.php b/lib/private/Collaboration/Resources/Manager.php index d670c3a846d..ef369b18fe3 100644 --- a/lib/private/Collaboration/Resources/Manager.php +++ b/lib/private/Collaboration/Resources/Manager.php @@ -353,12 +353,15 @@ class Manager implements IManager { return $access; } - $access = false; + $access = null; + // Access is granted when a user can access all resources foreach ($collection->getResources() as $resource) { - if ($resource->canAccess($user)) { - $access = true; + if (!$resource->canAccess($user)) { + $access = false; break; } + + $access = true; } $this->cacheAccessForCollection($collection, $user, $access); @@ -461,6 +464,14 @@ class Manager implements IManager { } } + public function invalidateAccessCacheForAllCollections(): void { + $query = $this->connection->getQueryBuilder(); + + $query->delete(self::TABLE_ACCESS_CACHE) + ->where($query->expr()->neq('collection_id', $query->createNamedParameter(0))); + $query->execute(); + } + public function invalidateAccessCacheForCollection(ICollection $collection): void { $query = $this->connection->getQueryBuilder(); diff --git a/lib/private/Comments/Comment.php b/lib/private/Comments/Comment.php index c9862c64ca6..ef7538d140d 100644 --- a/lib/private/Comments/Comment.php +++ b/lib/private/Comments/Comment.php @@ -22,6 +22,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Comments; use OCP\Comments\IComment; @@ -299,12 +300,12 @@ class Comment implements IComment { public function setActor($actorType, $actorId) { if( !is_string($actorType) || !trim($actorType) - || !is_string($actorId) || !trim($actorId) + || !is_string($actorId) || $actorId === '' ) { throw new \InvalidArgumentException('String expected.'); } $this->data['actorType'] = trim($actorType); - $this->data['actorId'] = trim($actorId); + $this->data['actorId'] = $actorId; return $this; } @@ -385,7 +386,7 @@ class Comment implements IComment { public function setObject($objectType, $objectId) { if( !is_string($objectType) || !trim($objectType) - || !is_string($objectId) || !trim($objectId) + || !is_string($objectId) || trim($objectId) === '' ) { throw new \InvalidArgumentException('String expected.'); } diff --git a/lib/private/Comments/Manager.php b/lib/private/Comments/Manager.php index e54218509dc..5c36d5e2091 100644 --- a/lib/private/Comments/Manager.php +++ b/lib/private/Comments/Manager.php @@ -34,8 +34,8 @@ use OCP\Comments\ICommentsEventHandler; use OCP\Comments\ICommentsManager; use OCP\Comments\NotFoundException; use OCP\DB\QueryBuilder\IQueryBuilder; -use OCP\IDBConnection; use OCP\IConfig; +use OCP\IDBConnection; use OCP\ILogger; use OCP\IUser; @@ -118,9 +118,9 @@ class Manager implements ICommentsManager { */ protected function prepareCommentForDatabaseWrite(IComment $comment) { if (!$comment->getActorType() - || !$comment->getActorId() + || $comment->getActorId() === '' || !$comment->getObjectType() - || !$comment->getObjectId() + || $comment->getObjectId() === '' || !$comment->getVerb() ) { throw new \UnexpectedValueException('Actor, Object and Verb information must be provided for saving'); diff --git a/lib/private/Comments/ManagerFactory.php b/lib/private/Comments/ManagerFactory.php index 21926912b10..1fc5fb2730b 100644 --- a/lib/private/Comments/ManagerFactory.php +++ b/lib/private/Comments/ManagerFactory.php @@ -21,6 +21,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Comments; use OCP\Comments\ICommentsManager; diff --git a/lib/private/Config.php b/lib/private/Config.php index f462bebaf58..4d24643c22f 100644 --- a/lib/private/Config.php +++ b/lib/private/Config.php @@ -270,4 +270,3 @@ class Config { } } } - diff --git a/lib/private/Console/Application.php b/lib/private/Console/Application.php index d7c047527f1..0260d5f7a3c 100644 --- a/lib/private/Console/Application.php +++ b/lib/private/Console/Application.php @@ -27,6 +27,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Console; use OC\MemoryInfo; diff --git a/lib/private/Contacts/ContactsMenu/ContactsStore.php b/lib/private/Contacts/ContactsMenu/ContactsStore.php index 05ef9cca53a..b3843b17a9e 100644 --- a/lib/private/Contacts/ContactsMenu/ContactsStore.php +++ b/lib/private/Contacts/ContactsMenu/ContactsStore.php @@ -27,13 +27,13 @@ namespace OC\Contacts\ContactsMenu; +use OCP\Contacts\ContactsMenu\IContactsStore; use OCP\Contacts\ContactsMenu\IEntry; use OCP\Contacts\IManager; use OCP\IConfig; use OCP\IGroupManager; use OCP\IUser; use OCP\IUserManager; -use OCP\Contacts\ContactsMenu\IContactsStore; class ContactsStore implements IContactsStore { diff --git a/lib/private/DB/AdapterMySQL.php b/lib/private/DB/AdapterMySQL.php index aa784bb83dc..d9af077da70 100644 --- a/lib/private/DB/AdapterMySQL.php +++ b/lib/private/DB/AdapterMySQL.php @@ -22,7 +22,6 @@ * */ - namespace OC\DB; class AdapterMySQL extends Adapter { diff --git a/lib/private/DB/AdapterOCI8.php b/lib/private/DB/AdapterOCI8.php index 359e4ba1b67..68e73ac8c44 100644 --- a/lib/private/DB/AdapterOCI8.php +++ b/lib/private/DB/AdapterOCI8.php @@ -23,7 +23,6 @@ * */ - namespace OC\DB; class AdapterOCI8 extends Adapter { diff --git a/lib/private/DB/AdapterPgSql.php b/lib/private/DB/AdapterPgSql.php index 42e57cd45f7..840d270d25f 100644 --- a/lib/private/DB/AdapterPgSql.php +++ b/lib/private/DB/AdapterPgSql.php @@ -21,7 +21,6 @@ * */ - namespace OC\DB; use Doctrine\DBAL\DBALException; diff --git a/lib/private/DB/AdapterSqlite.php b/lib/private/DB/AdapterSqlite.php index 0a482259b98..caf6a70b097 100644 --- a/lib/private/DB/AdapterSqlite.php +++ b/lib/private/DB/AdapterSqlite.php @@ -24,7 +24,6 @@ * */ - namespace OC\DB; use Doctrine\DBAL\Exception\UniqueConstraintViolationException; diff --git a/lib/private/DB/Connection.php b/lib/private/DB/Connection.php index 506f4bcd4c9..65e1d84bfb1 100644 --- a/lib/private/DB/Connection.php +++ b/lib/private/DB/Connection.php @@ -30,14 +30,15 @@ namespace OC\DB; +use Doctrine\Common\EventManager; +use Doctrine\DBAL\Cache\QueryCacheProfile; +use Doctrine\DBAL\Configuration; use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Driver; -use Doctrine\DBAL\Configuration; -use Doctrine\DBAL\Cache\QueryCacheProfile; -use Doctrine\Common\EventManager; -use Doctrine\DBAL\Platforms\MySqlPlatform; use Doctrine\DBAL\Exception\ConstraintViolationException; +use Doctrine\DBAL\Platforms\MySqlPlatform; use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\TransactionIsolationLevel; use OC\DB\QueryBuilder\QueryBuilder; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; @@ -148,7 +149,7 @@ class Connection extends ReconnectWrapper implements IDBConnection { $this->adapter = new $params['adapter']($this); $this->tablePrefix = $params['tablePrefix']; - parent::setTransactionIsolation(parent::TRANSACTION_READ_COMMITTED); + $this->setTransactionIsolation(TransactionIsolationLevel::READ_COMMITTED); } /** diff --git a/lib/private/DB/MigrationService.php b/lib/private/DB/MigrationService.php index 269400c3ba9..329500fa081 100644 --- a/lib/private/DB/MigrationService.php +++ b/lib/private/DB/MigrationService.php @@ -30,6 +30,7 @@ use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\SchemaException; use Doctrine\DBAL\Schema\Sequence; use Doctrine\DBAL\Schema\Table; +use Doctrine\DBAL\Types\Type; use OC\App\InfoParser; use OC\IntegrityCheck\Helpers\AppLocator; use OC\Migration\SimpleOutput; @@ -38,7 +39,6 @@ use OCP\AppFramework\QueryException; use OCP\IDBConnection; use OCP\Migration\IMigrationStep; use OCP\Migration\IOutput; -use Doctrine\DBAL\Types\Type; class MigrationService { diff --git a/lib/private/DB/Migrator.php b/lib/private/DB/Migrator.php index a853ab1ea1b..7f1db4b15d2 100644 --- a/lib/private/DB/Migrator.php +++ b/lib/private/DB/Migrator.php @@ -29,12 +29,12 @@ namespace OC\DB; -use \Doctrine\DBAL\DBALException; -use \Doctrine\DBAL\Schema\Index; -use \Doctrine\DBAL\Schema\Table; -use \Doctrine\DBAL\Schema\Schema; -use \Doctrine\DBAL\Schema\SchemaConfig; -use \Doctrine\DBAL\Schema\Comparator; +use Doctrine\DBAL\DBALException; +use Doctrine\DBAL\Schema\Comparator; +use Doctrine\DBAL\Schema\Index; +use Doctrine\DBAL\Schema\Schema; +use Doctrine\DBAL\Schema\SchemaConfig; +use Doctrine\DBAL\Schema\Table; use Doctrine\DBAL\Types\StringType; use Doctrine\DBAL\Types\Type; use OCP\IConfig; diff --git a/lib/private/DB/MissingIndexInformation.php b/lib/private/DB/MissingIndexInformation.php index 0cb9852d0d4..bcf8465d8aa 100644 --- a/lib/private/DB/MissingIndexInformation.php +++ b/lib/private/DB/MissingIndexInformation.php @@ -36,4 +36,4 @@ class MissingIndexInformation { public function getListOfMissingIndexes(): array { return $this->listOfMissingIndexes; } -}
\ No newline at end of file +} diff --git a/lib/private/DB/OracleMigrator.php b/lib/private/DB/OracleMigrator.php index be90a4bb97f..9c6c9fd156a 100644 --- a/lib/private/DB/OracleMigrator.php +++ b/lib/private/DB/OracleMigrator.php @@ -31,10 +31,10 @@ namespace OC\DB; use Doctrine\DBAL\DBALException; use Doctrine\DBAL\Schema\Column; use Doctrine\DBAL\Schema\ColumnDiff; +use Doctrine\DBAL\Schema\ForeignKeyConstraint; use Doctrine\DBAL\Schema\Index; use Doctrine\DBAL\Schema\Schema; use Doctrine\DBAL\Schema\Table; -use Doctrine\DBAL\Schema\ForeignKeyConstraint; class OracleMigrator extends Migrator { diff --git a/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php b/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php index a9d2f6f9a35..ffa758e4da7 100644 --- a/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php +++ b/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php @@ -72,8 +72,25 @@ class FunctionBuilder implements IFunctionBuilder { return new QueryFunction($this->helper->quoteColumnName($x) . ' - ' . $this->helper->quoteColumnName($y)); } - public function count($count, $alias = '') { + public function count($count = '', $alias = '') { $alias = $alias ? (' AS ' . $this->helper->quoteColumnName($alias)) : ''; - return new QueryFunction('COUNT(' . $this->helper->quoteColumnName($count) . ')' . $alias); + $quotedName = $count === '' ? '*' : $this->helper->quoteColumnName($count); + return new QueryFunction('COUNT(' . $quotedName . ')' . $alias); + } + + public function max($field) { + return new QueryFunction('MAX(' . $this->helper->quoteColumnName($field) . ')'); + } + + public function min($field) { + return new QueryFunction('MIN(' . $this->helper->quoteColumnName($field) . ')'); + } + + public function greatest($x, $y) { + return new QueryFunction('GREATEST(' . $this->helper->quoteColumnName($x) . ', ' . $this->helper->quoteColumnName($y) . ')'); + } + + public function least($x, $y) { + return new QueryFunction('LEAST(' . $this->helper->quoteColumnName($x) . ', ' . $this->helper->quoteColumnName($y) . ')'); } } diff --git a/lib/private/DB/QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php b/lib/private/DB/QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php index 21898cf3f93..f37ac20ecab 100644 --- a/lib/private/DB/QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php +++ b/lib/private/DB/QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php @@ -30,4 +30,13 @@ class SqliteFunctionBuilder extends FunctionBuilder { public function concat($x, $y) { return new QueryFunction('(' . $this->helper->quoteColumnName($x) . ' || ' . $this->helper->quoteColumnName($y) . ')'); } + + public function greatest($x, $y) { + return new QueryFunction('MAX(' . $this->helper->quoteColumnName($x) . ', ' . $this->helper->quoteColumnName($y) . ')'); + } + + public function least($x, $y) { + return new QueryFunction('MIN(' . $this->helper->quoteColumnName($x) . ', ' . $this->helper->quoteColumnName($y) . ')'); + } + } diff --git a/lib/private/DB/QueryBuilder/QueryBuilder.php b/lib/private/DB/QueryBuilder/QueryBuilder.php index 25d59fb7d7d..b9fada8c4f0 100644 --- a/lib/private/DB/QueryBuilder/QueryBuilder.php +++ b/lib/private/DB/QueryBuilder/QueryBuilder.php @@ -39,9 +39,9 @@ use OC\DB\QueryBuilder\FunctionBuilder\OCIFunctionBuilder; use OC\DB\QueryBuilder\FunctionBuilder\PgSqlFunctionBuilder; use OC\DB\QueryBuilder\FunctionBuilder\SqliteFunctionBuilder; use OC\SystemConfig; +use OCP\DB\QueryBuilder\IParameter; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\DB\QueryBuilder\IQueryFunction; -use OCP\DB\QueryBuilder\IParameter; use OCP\IDBConnection; use OCP\ILogger; @@ -245,7 +245,7 @@ class QueryBuilder implements IQueryBuilder { * @param mixed $value The parameter value. * @param string|null|int $type One of the IQueryBuilder::PARAM_* constants. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function setParameter($key, $value, $type = null) { $this->queryBuilder->setParameter($key, $value, $type); @@ -270,7 +270,7 @@ class QueryBuilder implements IQueryBuilder { * @param array $params The query parameters to set. * @param array $types The query parameters types to set. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function setParameters(array $params, array $types = array()) { $this->queryBuilder->setParameters($params, $types); @@ -323,7 +323,7 @@ class QueryBuilder implements IQueryBuilder { * * @param integer $firstResult The first result to return. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function setFirstResult($firstResult) { $this->queryBuilder->setFirstResult($firstResult); @@ -350,7 +350,7 @@ class QueryBuilder implements IQueryBuilder { * * @param integer $maxResults The maximum number of results to retrieve. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function setMaxResults($maxResults) { $this->queryBuilder->setMaxResults($maxResults); @@ -381,7 +381,7 @@ class QueryBuilder implements IQueryBuilder { * * @param mixed ...$selects The selection expressions. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * '@return $this This QueryBuilder instance. */ public function select(...$selects) { if (count($selects) === 1 && is_array($selects[0])) { @@ -408,7 +408,7 @@ class QueryBuilder implements IQueryBuilder { * @param mixed $select The selection expressions. * @param string $alias The column alias used in the constructed query. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function selectAlias($select, $alias) { @@ -430,7 +430,7 @@ class QueryBuilder implements IQueryBuilder { * * @param mixed $select The selection expressions. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function selectDistinct($select) { @@ -454,7 +454,7 @@ class QueryBuilder implements IQueryBuilder { * * @param mixed ...$selects The selection expression. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function addSelect(...$selects) { if (count($selects) === 1 && is_array($selects[0])) { @@ -482,7 +482,7 @@ class QueryBuilder implements IQueryBuilder { * @param string $delete The table whose rows are subject to the deletion. * @param string $alias The table alias used in the constructed query. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function delete($delete = null, $alias = null) { $this->queryBuilder->delete( @@ -507,7 +507,7 @@ class QueryBuilder implements IQueryBuilder { * @param string $update The table whose rows are subject to the update. * @param string $alias The table alias used in the constructed query. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function update($update = null, $alias = null) { $this->queryBuilder->update( @@ -535,7 +535,7 @@ class QueryBuilder implements IQueryBuilder { * * @param string $insert The table into which the rows should be inserted. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function insert($insert = null) { $this->queryBuilder->insert( @@ -560,7 +560,7 @@ class QueryBuilder implements IQueryBuilder { * @param string $from The table. * @param string|null $alias The alias of the table. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function from($from, $alias = null) { $this->queryBuilder->from( @@ -586,7 +586,7 @@ class QueryBuilder implements IQueryBuilder { * @param string $alias The alias of the join table. * @param string $condition The condition for the join. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function join($fromAlias, $join, $alias, $condition = null) { $this->queryBuilder->join( @@ -614,7 +614,7 @@ class QueryBuilder implements IQueryBuilder { * @param string $alias The alias of the join table. * @param string $condition The condition for the join. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function innerJoin($fromAlias, $join, $alias, $condition = null) { $this->queryBuilder->innerJoin( @@ -642,7 +642,7 @@ class QueryBuilder implements IQueryBuilder { * @param string $alias The alias of the join table. * @param string $condition The condition for the join. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function leftJoin($fromAlias, $join, $alias, $condition = null) { $this->queryBuilder->leftJoin( @@ -670,7 +670,7 @@ class QueryBuilder implements IQueryBuilder { * @param string $alias The alias of the join table. * @param string $condition The condition for the join. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function rightJoin($fromAlias, $join, $alias, $condition = null) { $this->queryBuilder->rightJoin( @@ -696,7 +696,7 @@ class QueryBuilder implements IQueryBuilder { * @param string $key The column to set. * @param string $value The value, expression, placeholder, etc. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function set($key, $value) { $this->queryBuilder->set( @@ -731,7 +731,7 @@ class QueryBuilder implements IQueryBuilder { * * @param mixed ...$predicates The restriction predicates. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function where(...$predicates) { call_user_func_array( @@ -756,7 +756,7 @@ class QueryBuilder implements IQueryBuilder { * * @param mixed ...$where The query restrictions. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * * @see where() */ @@ -783,7 +783,7 @@ class QueryBuilder implements IQueryBuilder { * * @param mixed ...$where The WHERE statement. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. * * @see where() */ @@ -809,7 +809,7 @@ class QueryBuilder implements IQueryBuilder { * * @param mixed ...$groupBys The grouping expression. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function groupBy(...$groupBys) { if (count($groupBys) === 1 && is_array($groupBys[0])) { @@ -837,7 +837,7 @@ class QueryBuilder implements IQueryBuilder { * * @param mixed ...$groupBy The grouping expression. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function addGroupBy(...$groupBys) { if (count($groupBys) === 1 && is_array($groupBys[0])) { @@ -869,7 +869,7 @@ class QueryBuilder implements IQueryBuilder { * @param string $column The column into which the value should be inserted. * @param string $value The value that should be inserted into the column. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function setValue($column, $value) { $this->queryBuilder->setValue( @@ -897,7 +897,7 @@ class QueryBuilder implements IQueryBuilder { * * @param array $values The values to specify for the insert query indexed by column names. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function values(array $values) { $quotedValues = []; @@ -916,7 +916,7 @@ class QueryBuilder implements IQueryBuilder { * * @param mixed ...$having The restriction over the groups. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function having(...$having) { call_user_func_array( @@ -933,7 +933,7 @@ class QueryBuilder implements IQueryBuilder { * * @param mixed ...$having The restriction to append. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function andHaving(...$having) { call_user_func_array( @@ -950,7 +950,7 @@ class QueryBuilder implements IQueryBuilder { * * @param mixed ...$having The restriction to add. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function orHaving(...$having) { call_user_func_array( @@ -968,7 +968,7 @@ class QueryBuilder implements IQueryBuilder { * @param string $sort The ordering expression. * @param string $order The ordering direction. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function orderBy($sort, $order = null) { $this->queryBuilder->orderBy( @@ -985,7 +985,7 @@ class QueryBuilder implements IQueryBuilder { * @param string $sort The ordering expression. * @param string $order The ordering direction. * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function addOrderBy($sort, $order = null) { $this->queryBuilder->addOrderBy( @@ -1021,7 +1021,7 @@ class QueryBuilder implements IQueryBuilder { * * @param array|null $queryPartNames * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function resetQueryParts($queryPartNames = null) { $this->queryBuilder->resetQueryParts($queryPartNames); @@ -1034,7 +1034,7 @@ class QueryBuilder implements IQueryBuilder { * * @param string $queryPartName * - * @return \OCP\DB\QueryBuilder\IQueryBuilder This QueryBuilder instance. + * @return $this This QueryBuilder instance. */ public function resetQueryPart($queryPartName) { $this->queryBuilder->resetQueryPart($queryPartName); diff --git a/lib/private/DB/SQLiteSessionInit.php b/lib/private/DB/SQLiteSessionInit.php index 635a5d98a9e..826bf8e1952 100644 --- a/lib/private/DB/SQLiteSessionInit.php +++ b/lib/private/DB/SQLiteSessionInit.php @@ -24,9 +24,9 @@ namespace OC\DB; +use Doctrine\Common\EventSubscriber; use Doctrine\DBAL\Event\ConnectionEventArgs; use Doctrine\DBAL\Events; -use Doctrine\Common\EventSubscriber; class SQLiteSessionInit implements EventSubscriber { /** diff --git a/lib/private/Dashboard/DashboardManager.php b/lib/private/Dashboard/DashboardManager.php index 712d56641fa..c2ec8f25288 100644 --- a/lib/private/Dashboard/DashboardManager.php +++ b/lib/private/Dashboard/DashboardManager.php @@ -27,7 +27,6 @@ declare(strict_types=1); * */ - namespace OC\Dashboard; @@ -37,7 +36,6 @@ use OCP\Dashboard\Model\IWidgetConfig; use OCP\Dashboard\Service\IEventsService; use OCP\Dashboard\Service\IWidgetsService; - /** * Class DashboardManager * @@ -145,4 +143,3 @@ class DashboardManager implements IDashboardManager { } } - diff --git a/lib/private/DirectEditing/Manager.php b/lib/private/DirectEditing/Manager.php new file mode 100644 index 00000000000..26adad9e572 --- /dev/null +++ b/lib/private/DirectEditing/Manager.php @@ -0,0 +1,236 @@ +<?php +/** + * @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net> + * + * @author Julius Härtl <jus@bitgrid.net> + * + * @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\DirectEditing; + +use Doctrine\DBAL\FetchMode; +use OCP\AppFramework\Http\NotFoundResponse; +use OCP\AppFramework\Http\Response; +use OCP\AppFramework\Http\TemplateResponse; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\DirectEditing\ACreateFromTemplate; +use OCP\DirectEditing\IEditor; +use \OCP\DirectEditing\IManager; +use OCP\DirectEditing\IToken; +use OCP\DirectEditing\RegisterDirectEditorEvent; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\Files\File; +use OCP\Files\IRootFolder; +use OCP\Files\NotFoundException; +use OCP\IDBConnection; +use OCP\IUserSession; +use OCP\Security\ISecureRandom; +use OCP\Share\IShare; + +class Manager implements IManager { + + private const TOKEN_CLEANUP_TIME = 12 * 60 * 60 ; + + public const TABLE_TOKENS = 'direct_edit'; + + /** @var IEditor[] */ + private $editors = []; + + /** @var IDBConnection */ + private $connection; + /** + * @var ISecureRandom + */ + private $random; + private $userId; + private $rootFolder; + + public function __construct( + ISecureRandom $random, + IDBConnection $connection, + IUserSession $userSession, + IRootFolder $rootFolder + ) { + $this->random = $random; + $this->connection = $connection; + $this->userId = $userSession->getUser() ? $userSession->getUser()->getUID() : null; + $this->rootFolder = $rootFolder; + } + + public function registerDirectEditor(IEditor $directEditor): void { + $this->editors[$directEditor->getId()] = $directEditor; + } + + public function getEditors(): array { + return $this->editors; + } + + public function getTemplates(string $editor, string $type): array { + if (!array_key_exists($editor, $this->editors)) { + throw new \RuntimeException('No matching editor found'); + } + $templates = []; + foreach ($this->editors[$editor]->getCreators() as $creator) { + if ($creator instanceof ACreateFromTemplate && $creator->getId() === $type) { + $templates = $creator->getTemplates(); + } + } + $return = []; + $return['templates'] = $templates; + return $return; + } + + public function create(string $path, string $editorId, string $creatorId, $templateId = null): string { + $userFolder = $this->rootFolder->getUserFolder($this->userId); + $file = $userFolder->newFile($path); + $editor = $this->getEditor($editorId); + $creators = $editor->getCreators(); + foreach ($creators as $creator) { + if ($creator->getId() === $creatorId) { + $creator->create($file, $creatorId, $templateId); + return $this->createToken($editorId, $file); + } + } + throw new \RuntimeException('No creator found'); + } + + public function open(int $fileId, string $editorId = null): string { + $file = $this->rootFolder->getUserFolder($this->userId)->getById($fileId); + if (count($file) === 0 || !($file[0] instanceof File) || $file === null) { + throw new NotFoundException(); + } + /** @var File $file */ + $file = $file[0]; + + if ($editorId === null) { + $editorId = $this->findEditorForFile($file); + } + + return $this->createToken($editorId, $file); + } + + private function findEditorForFile(File $file) { + foreach ($this->editors as $editor) { + if (in_array($file->getMimeType(), $editor->getMimetypes())) { + return $editor->getId(); + } + } + throw new \RuntimeException('No default editor found for files mimetype'); + } + + public function edit(string $token): Response { + try { + /** @var IEditor $editor */ + $tokenObject = $this->getToken($token); + if ($tokenObject->hasBeenAccessed()) { + throw new \RuntimeException('Token has already been used and can only be used for followup requests'); + } + $editor = $this->getEditor($tokenObject->getEditor()); + $this->accessToken($token); + + } catch (\Throwable $throwable) { + $this->invalidateToken($token); + return new NotFoundResponse(); + } + return $editor->open($tokenObject); + } + + public function editSecure(File $file, string $editorId): TemplateResponse { + // TODO: Implementation in follow up + } + + private function getEditor($editorId): IEditor { + if (!array_key_exists($editorId, $this->editors)) { + throw new \RuntimeException('No editor found'); + } + return $this->editors[$editorId]; + } + + public function getToken(string $token): IToken { + $query = $this->connection->getQueryBuilder(); + $query->select('*')->from(self::TABLE_TOKENS) + ->where($query->expr()->eq('token', $query->createNamedParameter($token, IQueryBuilder::PARAM_STR))); + $result = $query->execute(); + if ($tokenRow = $result->fetch(FetchMode::ASSOCIATIVE)) { + return new Token($this, $tokenRow); + } + throw new \RuntimeException('Failed to validate the token'); + } + + public function cleanup(): int { + $query = $this->connection->getQueryBuilder(); + $query->delete(self::TABLE_TOKENS) + ->where($query->expr()->lt('timestamp', $query->createNamedParameter(time() - self::TOKEN_CLEANUP_TIME))); + return $query->execute(); + } + + public function refreshToken(string $token): bool { + $query = $this->connection->getQueryBuilder(); + $query->update(self::TABLE_TOKENS) + ->set('timestamp', $query->createNamedParameter(time(), IQueryBuilder::PARAM_INT)) + ->where($query->expr()->eq('token', $query->createNamedParameter($token, IQueryBuilder::PARAM_STR))); + $result = $query->execute(); + return $result !== 0; + } + + + public function invalidateToken(string $token): bool { + $query = $this->connection->getQueryBuilder(); + $query->delete(self::TABLE_TOKENS) + ->where($query->expr()->eq('token', $query->createNamedParameter($token, IQueryBuilder::PARAM_STR))); + $result = $query->execute(); + return $result !== 0; + } + + public function accessToken(string $token): bool { + $query = $this->connection->getQueryBuilder(); + $query->update(self::TABLE_TOKENS) + ->set('accessed', $query->createNamedParameter(true, IQueryBuilder::PARAM_BOOL)) + ->set('timestamp', $query->createNamedParameter(time(), IQueryBuilder::PARAM_INT)) + ->where($query->expr()->eq('token', $query->createNamedParameter($token, IQueryBuilder::PARAM_STR))); + $result = $query->execute(); + return $result !== 0; + } + + public function invokeTokenScope($userId): void { + \OC_User::setIncognitoMode(true); + \OC_User::setUserId($userId); + } + + public function createToken($editorId, File $file, IShare $share = null): string { + $token = $this->random->generate(64, ISecureRandom::CHAR_HUMAN_READABLE); + $query = $this->connection->getQueryBuilder(); + $query->insert(self::TABLE_TOKENS) + ->values([ + 'token' => $query->createNamedParameter($token), + 'editor_id' => $query->createNamedParameter($editorId), + 'file_id' => $query->createNamedParameter($file->getId()), + 'user_id' => $query->createNamedParameter($this->userId), + 'share_id' => $query->createNamedParameter($share !== null ? $share->getId(): null), + 'timestamp' => $query->createNamedParameter(time()) + ]); + $query->execute(); + return $token; + } + + public function getFileForToken($userId, $fileId) { + $userFolder = $this->rootFolder->getUserFolder($userId); + return $userFolder->getById($fileId)[0]; + } + +} diff --git a/lib/private/DirectEditing/Token.php b/lib/private/DirectEditing/Token.php new file mode 100644 index 00000000000..148621a2cf3 --- /dev/null +++ b/lib/private/DirectEditing/Token.php @@ -0,0 +1,76 @@ +<?php +/** + * @copyright Copyright (c) 2019 Julius Härtl <jus@bitgrid.net> + * + * @author Julius Härtl <jus@bitgrid.net> + * + * @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\DirectEditing; + + +use OCP\DirectEditing\IToken; +use OCP\Files\File; + +class Token implements IToken { + + /** @var Manager */ + private $manager; + private $data; + + public function __construct(Manager $manager, $data) { + $this->manager = $manager; + $this->data = $data; + } + + public function extend(): void { + $this->manager->refreshToken($this->data['token']); + } + + public function invalidate(): void { + $this->manager->invalidateToken($this->data['token']); + } + + public function getFile(): File { + if ($this->data['share_id'] !== null) { + return $this->manager->getShareForToken($this->data['share_id']); + } + return $this->manager->getFileForToken($this->data['user_id'], $this->data['file_id']); + } + + public function getToken(): string { + return $this->data['token']; + } + + public function useTokenScope(): void { + $this->manager->invokeTokenScope($this->data['user_id']); + } + + public function hasBeenAccessed(): bool { + return (bool) $this->data['accessed']; + } + + public function getEditor(): string { + return $this->data['editor_id']; + } + + public function getUser(): string { + return $this->data['user_id']; + } + +} diff --git a/lib/private/Encryption/DecryptAll.php b/lib/private/Encryption/DecryptAll.php index d95a8660762..86164f072d2 100644 --- a/lib/private/Encryption/DecryptAll.php +++ b/lib/private/Encryption/DecryptAll.php @@ -24,12 +24,11 @@ * */ - namespace OC\Encryption; use OC\Encryption\Exceptions\DecryptionFailedException; use OC\Files\View; -use \OCP\Encryption\IEncryptionModule; +use OCP\Encryption\IEncryptionModule; use OCP\IUserManager; use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Input\InputInterface; diff --git a/lib/private/Encryption/EncryptionWrapper.php b/lib/private/Encryption/EncryptionWrapper.php index c4650d39ee3..30fee808097 100644 --- a/lib/private/Encryption/EncryptionWrapper.php +++ b/lib/private/Encryption/EncryptionWrapper.php @@ -21,15 +21,14 @@ * */ - namespace OC\Encryption; -use OC\Memcache\ArrayCache; use OC\Files\Filesystem; use OC\Files\Storage\Wrapper\Encryption; -use OCP\Files\Mount\IMountPoint; use OC\Files\View; +use OC\Memcache\ArrayCache; +use OCP\Files\Mount\IMountPoint; use OCP\Files\Storage; use OCP\ILogger; diff --git a/lib/private/Encryption/Exceptions/DecryptionFailedException.php b/lib/private/Encryption/Exceptions/DecryptionFailedException.php index de13536b0c5..946bd47b2c4 100644 --- a/lib/private/Encryption/Exceptions/DecryptionFailedException.php +++ b/lib/private/Encryption/Exceptions/DecryptionFailedException.php @@ -21,7 +21,6 @@ * */ - namespace OC\Encryption\Exceptions; use OCP\Encryption\Exceptions\GenericEncryptionException; diff --git a/lib/private/Encryption/Exceptions/EmptyEncryptionDataException.php b/lib/private/Encryption/Exceptions/EmptyEncryptionDataException.php index 3e93e633ce1..73c02ac8d4c 100644 --- a/lib/private/Encryption/Exceptions/EmptyEncryptionDataException.php +++ b/lib/private/Encryption/Exceptions/EmptyEncryptionDataException.php @@ -21,7 +21,6 @@ * */ - namespace OC\Encryption\Exceptions; use OCP\Encryption\Exceptions\GenericEncryptionException; diff --git a/lib/private/Encryption/Exceptions/EncryptionFailedException.php b/lib/private/Encryption/Exceptions/EncryptionFailedException.php index fefd8d4f0ba..d351476ffcd 100644 --- a/lib/private/Encryption/Exceptions/EncryptionFailedException.php +++ b/lib/private/Encryption/Exceptions/EncryptionFailedException.php @@ -21,7 +21,6 @@ * */ - namespace OC\Encryption\Exceptions; use OCP\Encryption\Exceptions\GenericEncryptionException; diff --git a/lib/private/Encryption/Exceptions/EncryptionHeaderToLargeException.php b/lib/private/Encryption/Exceptions/EncryptionHeaderToLargeException.php index 00bb0cf4262..b06087f0829 100644 --- a/lib/private/Encryption/Exceptions/EncryptionHeaderToLargeException.php +++ b/lib/private/Encryption/Exceptions/EncryptionHeaderToLargeException.php @@ -21,7 +21,6 @@ * */ - namespace OC\Encryption\Exceptions; use OCP\Encryption\Exceptions\GenericEncryptionException; diff --git a/lib/private/Encryption/Exceptions/UnknownCipherException.php b/lib/private/Encryption/Exceptions/UnknownCipherException.php index d2c2eb6289c..21dc6639f0d 100644 --- a/lib/private/Encryption/Exceptions/UnknownCipherException.php +++ b/lib/private/Encryption/Exceptions/UnknownCipherException.php @@ -21,7 +21,6 @@ * */ - namespace OC\Encryption\Exceptions; use OCP\Encryption\Exceptions\GenericEncryptionException; diff --git a/lib/private/Encryption/Keys/Storage.php b/lib/private/Encryption/Keys/Storage.php index 223b959e661..77284be1b9d 100644 --- a/lib/private/Encryption/Keys/Storage.php +++ b/lib/private/Encryption/Keys/Storage.php @@ -29,8 +29,8 @@ namespace OC\Encryption\Keys; use OC\Encryption\Util; use OC\Files\Filesystem; use OC\Files\View; -use OCP\Encryption\Keys\IStorage; use OC\User\NoUserException; +use OCP\Encryption\Keys\IStorage; class Storage implements IStorage { diff --git a/lib/private/Encryption/Update.php b/lib/private/Encryption/Update.php index 9d9f5e4d954..65788424906 100644 --- a/lib/private/Encryption/Update.php +++ b/lib/private/Encryption/Update.php @@ -25,8 +25,8 @@ namespace OC\Encryption; use OC\Files\Filesystem; -use \OC\Files\Mount; -use \OC\Files\View; +use OC\Files\Mount; +use OC\Files\View; /** * update encrypted files, e.g. because a file was shared diff --git a/lib/private/EventDispatcher/EventDispatcher.php b/lib/private/EventDispatcher/EventDispatcher.php index 8db2f3101be..1f4a1ba9ab4 100644 --- a/lib/private/EventDispatcher/EventDispatcher.php +++ b/lib/private/EventDispatcher/EventDispatcher.php @@ -25,6 +25,10 @@ declare(strict_types=1); namespace OC\EventDispatcher; +use function get_class; +use OC\Broadcast\Events\BroadcastEvent; +use OCP\Broadcast\Events\IBroadcastEvent; +use OCP\EventDispatcher\ABroadcastedEvent; use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventDispatcher; use OCP\IContainer; @@ -71,8 +75,19 @@ class EventDispatcher implements IEventDispatcher { public function dispatch(string $eventName, Event $event): void { + $this->dispatcher->dispatch($event, $eventName); + + if ($event instanceof ABroadcastedEvent && !$event->isPropagationStopped()) { + // Propagate broadcast + $this->dispatch( + IBroadcastEvent::class, + new BroadcastEvent($event) + ); + } + } - $this->dispatcher->dispatch($eventName, $event); + public function dispatchTyped(Event $event): void { + $this->dispatch(get_class($event), $event); } /** diff --git a/lib/private/EventDispatcher/SymfonyAdapter.php b/lib/private/EventDispatcher/SymfonyAdapter.php index f2f2fbf59fd..70c03fcb1ff 100644 --- a/lib/private/EventDispatcher/SymfonyAdapter.php +++ b/lib/private/EventDispatcher/SymfonyAdapter.php @@ -27,7 +27,7 @@ namespace OC\EventDispatcher; use function is_callable; use OCP\EventDispatcher\Event; -use Symfony\Component\EventDispatcher\Event as SymfonyEvent; +use OCP\ILogger; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\EventSubscriberInterface; @@ -35,9 +35,12 @@ class SymfonyAdapter implements EventDispatcherInterface { /** @var EventDispatcher */ private $eventDispatcher; + /** @var ILogger */ + private $logger; - public function __construct(EventDispatcher $eventDispatcher) { + public function __construct(EventDispatcher $eventDispatcher, ILogger $logger) { $this->eventDispatcher = $eventDispatcher; + $this->logger = $logger; } /** @@ -46,16 +49,21 @@ class SymfonyAdapter implements EventDispatcherInterface { * @param string $eventName The name of the event to dispatch. The name of * the event is the name of the method that is * invoked on listeners. - * @param SymfonyEvent|null $event The event to pass to the event handlers/listeners + * @param Event|null $event The event to pass to the event handlers/listeners * If not supplied, an empty Event instance is created * - * @return SymfonyEvent + * @return void */ - public function dispatch($eventName, SymfonyEvent $event = null) { + public function dispatch($eventName, $event = null) { + // type hinting is not possible, due to usage of GenericEvent if ($event instanceof Event) { $this->eventDispatcher->dispatch($eventName, $event); } else { // Legacy event + $this->logger->info( + 'Deprecated event type for {name}: {class}', + [ 'name' => $eventName, 'class' => is_object($event) ? get_class($event) : 'null' ] + ); $this->eventDispatcher->getSymfonyDispatcher()->dispatch($eventName, $event); } } diff --git a/lib/private/Federation/CloudFederationProviderManager.php b/lib/private/Federation/CloudFederationProviderManager.php index c2944f410b3..772bc0aeb05 100644 --- a/lib/private/Federation/CloudFederationProviderManager.php +++ b/lib/private/Federation/CloudFederationProviderManager.php @@ -19,7 +19,6 @@ * */ - namespace OC\Federation; use OC\AppFramework\Http; diff --git a/lib/private/Files/AppData/AppData.php b/lib/private/Files/AppData/AppData.php index 7ce29bd0e00..4f526bf04cf 100644 --- a/lib/private/Files/AppData/AppData.php +++ b/lib/private/Files/AppData/AppData.php @@ -26,10 +26,10 @@ namespace OC\Files\AppData; use OC\Cache\CappedMemoryCache; use OC\Files\SimpleFS\SimpleFolder; +use OC\SystemConfig; +use OCP\Files\Folder; use OCP\Files\IAppData; use OCP\Files\IRootFolder; -use OCP\Files\Folder; -use OC\SystemConfig; use OCP\Files\Node; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; diff --git a/lib/private/Files/AppData/Factory.php b/lib/private/Files/AppData/Factory.php index 5c7d554ba5b..3a880d5e29b 100644 --- a/lib/private/Files/AppData/Factory.php +++ b/lib/private/Files/AppData/Factory.php @@ -21,6 +21,7 @@ declare(strict_types=1); * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ + namespace OC\Files\AppData; use OC\SystemConfig; diff --git a/lib/private/Files/Cache/AbstractCacheEvent.php b/lib/private/Files/Cache/AbstractCacheEvent.php index c8a41ce54d8..d4990f17743 100644 --- a/lib/private/Files/Cache/AbstractCacheEvent.php +++ b/lib/private/Files/Cache/AbstractCacheEvent.php @@ -21,9 +21,9 @@ namespace OC\Files\Cache; +use OCP\EventDispatcher\Event; use OCP\Files\Cache\ICacheEvent; use OCP\Files\Storage\IStorage; -use Symfony\Component\EventDispatcher\Event; class AbstractCacheEvent extends Event implements ICacheEvent { protected $storage; diff --git a/lib/private/Files/Cache/Cache.php b/lib/private/Files/Cache/Cache.php index f6139d8abed..a90617f5c53 100644 --- a/lib/private/Files/Cache/Cache.php +++ b/lib/private/Files/Cache/Cache.php @@ -38,14 +38,15 @@ namespace OC\Files\Cache; +use Doctrine\DBAL\Driver\Statement; use Doctrine\DBAL\Exception\UniqueConstraintViolationException; use OCP\DB\QueryBuilder\IQueryBuilder; -use Doctrine\DBAL\Driver\Statement; use OCP\Files\Cache\CacheInsertEvent; use OCP\Files\Cache\CacheUpdateEvent; use OCP\Files\Cache\ICache; use OCP\Files\Cache\ICacheEntry; -use \OCP\Files\IMimeTypeLoader; +use OCP\Files\FileInfo; +use OCP\Files\IMimeTypeLoader; use OCP\Files\Search\ISearchQuery; use OCP\Files\Storage\IStorage; use OCP\IDBConnection; @@ -68,7 +69,7 @@ class Cache implements ICache { /** * @var array partial data for the cache */ - protected $partial = array(); + protected $partial = []; /** * @var string @@ -112,6 +113,15 @@ class Cache implements ICache { $this->querySearchHelper = new QuerySearchHelper($this->mimetypeLoader); } + private function getQueryBuilder() { + return new CacheQueryBuilder( + $this->connection, + \OC::$server->getSystemConfig(), + \OC::$server->getLogger(), + $this + ); + } + /** * Get the numeric storage id for this cache's storage * @@ -128,34 +138,24 @@ class Cache implements ICache { * @return ICacheEntry|false the cache entry as array of false if the file is not found in the cache */ public function get($file) { + $query = $this->getQueryBuilder(); + $query->selectFileCache(); + if (is_string($file) or $file == '') { // normalize file $file = $this->normalize($file); - $where = 'WHERE `storage` = ? AND `path_hash` = ?'; - $params = array($this->getNumericStorageId(), md5($file)); + $query->whereStorageId() + ->wherePath($file); } else { //file id - $where = 'WHERE `fileid` = ?'; - $params = array($file); + $query->whereFileId($file); } - $sql = 'SELECT `fileid`, `storage`, `path`, `path_hash`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, - `storage_mtime`, `encrypted`, `etag`, `permissions`, `checksum` - FROM `*PREFIX*filecache` ' . $where; - $result = $this->connection->executeQuery($sql, $params); - $data = $result->fetch(); - //FIXME hide this HACK in the next database layer, or just use doctrine and get rid of MDB2 and PDO - //PDO returns false, MDB2 returns null, oracle always uses MDB2, so convert null to false - if ($data === null) { - $data = false; - } + $data = $query->execute()->fetch(); //merge partial data - if (!$data and is_string($file)) { - if (isset($this->partial[$file])) { - $data = $this->partial[$file]; - } - return $data; + if (!$data and is_string($file) and isset($this->partial[$file])) { + return $this->partial[$file]; } else if (!$data) { return $data; } else { @@ -187,6 +187,12 @@ class Cache implements ICache { $data['storage_mtime'] = $data['mtime']; } $data['permissions'] = (int)$data['permissions']; + if (isset($data['creation_time'])) { + $data['creation_time'] = (int) $data['creation_time']; + } + if (isset($data['upload_time'])) { + $data['upload_time'] = (int) $data['upload_time']; + } return new CacheEntry($data); } @@ -209,11 +215,12 @@ class Cache implements ICache { */ public function getFolderContentsById($fileId) { if ($fileId > -1) { - $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `mtime`, - `storage_mtime`, `encrypted`, `etag`, `permissions`, `checksum` - FROM `*PREFIX*filecache` WHERE `parent` = ? ORDER BY `name` ASC'; - $result = $this->connection->executeQuery($sql, [$fileId]); - $files = $result->fetchAll(); + $query = $this->getQueryBuilder(); + $query->selectFileCache() + ->whereParent($fileId) + ->orderBy('name', 'ASC'); + + $files = $query->execute()->fetchAll(); return array_map(function (array $data) { return self::cacheEntryFromData($data, $this->mimetypeLoader); }, $files); @@ -259,7 +266,7 @@ class Cache implements ICache { unset($this->partial[$file]); } - $requiredFields = array('size', 'mtime', 'mimetype'); + $requiredFields = ['size', 'mtime', 'mimetype']; foreach ($requiredFields as $field) { if (!isset($data[$field])) { //data not complete save as partial and return $this->partial[$file] = $data; @@ -271,14 +278,8 @@ class Cache implements ICache { $data['parent'] = $this->getParentId($file); $data['name'] = basename($file); - list($queryParts, $params) = $this->buildParts($data); - $queryParts[] = '`storage`'; - $params[] = $this->getNumericStorageId(); - - $queryParts = array_map(function ($item) { - return trim($item, "`"); - }, $queryParts); - $values = array_combine($queryParts, $params); + [$values, $extensionValues] = $this->normalizeData($data); + $values['storage'] = $this->getNumericStorageId(); try { $builder = $this->connection->getQueryBuilder(); @@ -289,7 +290,19 @@ class Cache implements ICache { } if ($builder->execute()) { - $fileId = (int)$this->connection->lastInsertId('*PREFIX*filecache'); + $fileId = $builder->getLastInsertId(); + + if (count($extensionValues)) { + $query = $this->getQueryBuilder(); + $query->insert('filecache_extended'); + + $query->setValue('fileid', $query->createNamedParameter($fileId, IQueryBuilder::PARAM_INT)); + foreach ($extensionValues as $column => $value) { + $query->setValue($column, $query->createNamedParameter($value)); + } + $query->execute(); + } + $this->eventDispatcher->dispatch(CacheInsertEvent::class, new CacheInsertEvent($this->storage, $file, $fileId)); return $fileId; } @@ -324,20 +337,56 @@ class Cache implements ICache { $data['name'] = $this->normalize($data['name']); } - list($queryParts, $params) = $this->buildParts($data); - // duplicate $params because we need the parts twice in the SQL statement - // once for the SET part, once in the WHERE clause - $params = array_merge($params, $params); - $params[] = $id; + [$values, $extensionValues] = $this->normalizeData($data); + + if (count($values)) { + $query = $this->getQueryBuilder(); + + $query->update('filecache') + ->whereFileId($id) + ->andWhere($query->expr()->orX(...array_map(function ($key, $value) use ($query) { + return $query->expr()->orX( + $query->expr()->neq($key, $query->createNamedParameter($value)), + $query->expr()->isNull($key) + ); + }, array_keys($values), array_values($values)))); + + foreach ($values as $key => $value) { + $query->set($key, $query->createNamedParameter($value)); + } + + $query->execute(); + } + + if (count($extensionValues)) { + try { + $query = $this->getQueryBuilder(); + $query->insert('filecache_extended'); - // don't update if the data we try to set is the same as the one in the record - // some databases (Postgres) don't like superfluous updates - $sql = 'UPDATE `*PREFIX*filecache` SET ' . implode(' = ?, ', $queryParts) . '=? ' . - 'WHERE (' . - implode(' <> ? OR ', $queryParts) . ' <> ? OR ' . - implode(' IS NULL OR ', $queryParts) . ' IS NULL' . - ') AND `fileid` = ? '; - $this->connection->executeQuery($sql, $params); + $query->setValue('fileid', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT)); + foreach ($extensionValues as $column => $value) { + $query->setValue($column, $query->createNamedParameter($value)); + } + + $query->execute(); + } catch (UniqueConstraintViolationException $e) { + $query = $this->getQueryBuilder(); + $query->update('filecache_extended') + ->whereFileId($id) + ->andWhere($query->expr()->orX(...array_map(function ($key, $value) use ($query) { + return $query->expr()->orX( + $query->expr()->neq($key, $query->createNamedParameter($value)), + $query->expr()->isNull($key) + ); + }, array_keys($extensionValues), array_values($extensionValues)))); + + foreach ($extensionValues as $key => $value) { + $query->set($key, $query->createNamedParameter($value)); + } + + $query->execute(); + } + } $path = $this->getPathById($id); // path can still be null if the file doesn't exist @@ -350,14 +399,13 @@ class Cache implements ICache { * extract query parts and params array from data array * * @param array $data - * @return array [$queryParts, $params] - * $queryParts: string[], the (escaped) column names to be set in the query - * $params: mixed[], the new values for the columns, to be passed as params to the query + * @return array */ - protected function buildParts(array $data) { - $fields = array( + protected function normalizeData(array $data): array { + $fields = [ 'path', 'parent', 'name', 'mimetype', 'size', 'mtime', 'storage_mtime', 'encrypted', - 'etag', 'permissions', 'checksum', 'storage'); + 'etag', 'permissions', 'checksum', 'storage']; + $extensionFields = ['metadata_etag', 'creation_time', 'upload_time']; $doNotCopyStorageMTime = false; if (array_key_exists('mtime', $data) && $data['mtime'] === null) { @@ -366,23 +414,20 @@ class Cache implements ICache { $doNotCopyStorageMTime = true; } - $params = array(); - $queryParts = array(); + $params = []; + $extensionParams = []; foreach ($data as $name => $value) { if (array_search($name, $fields) !== false) { if ($name === 'path') { - $params[] = md5($value); - $queryParts[] = '`path_hash`'; - } elseif ($name === 'mimetype') { - $params[] = $this->mimetypeLoader->getId(substr($value, 0, strpos($value, '/'))); - $queryParts[] = '`mimepart`'; + $params['path_hash'] = md5($value); + } else if ($name === 'mimetype') { + $params['mimepart'] = $this->mimetypeLoader->getId(substr($value, 0, strpos($value, '/'))); $value = $this->mimetypeLoader->getId($value); - } elseif ($name === 'storage_mtime') { + } else if ($name === 'storage_mtime') { if (!$doNotCopyStorageMTime && !isset($data['mtime'])) { - $params[] = $value; - $queryParts[] = '`mtime`'; + $params['mtime'] = $value; } - } elseif ($name === 'encrypted') { + } else if ($name === 'encrypted') { if (isset($data['encryptedVersion'])) { $value = $data['encryptedVersion']; } else { @@ -390,11 +435,13 @@ class Cache implements ICache { $value = $value ? 1 : 0; } } - $params[] = $value; - $queryParts[] = '`' . $name . '`'; + $params[$name] = $value; + } + if (array_search($name, $extensionFields) !== false) { + $extensionParams[$name] = $value; } } - return array($queryParts, $params); + return [$params, array_filter($extensionParams)]; } /** @@ -411,15 +458,14 @@ class Cache implements ICache { // normalize file $file = $this->normalize($file); - $pathHash = md5($file); + $query = $this->getQueryBuilder(); + $query->select('fileid') + ->from('filecache') + ->whereStorageId() + ->wherePath($file); - $sql = 'SELECT `fileid` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` = ?'; - $result = $this->connection->executeQuery($sql, array($this->getNumericStorageId(), $pathHash)); - if ($row = $result->fetch()) { - return (int)$row['fileid']; - } else { - return -1; - } + $id = $query->execute()->fetchColumn(); + return $id === false ? -1 : (int)$id; } /** @@ -464,39 +510,64 @@ class Cache implements ICache { */ public function remove($file) { $entry = $this->get($file); - $sql = 'DELETE FROM `*PREFIX*filecache` WHERE `fileid` = ?'; - $this->connection->executeQuery($sql, array($entry['fileid'])); - if ($entry['mimetype'] === 'httpd/unix-directory') { - $this->removeChildren($entry); + + if ($entry) { + $query = $this->getQueryBuilder(); + $query->delete('filecache') + ->whereFileId($entry->getId()); + $query->execute(); + + $query = $this->getQueryBuilder(); + $query->delete('filecache_extended') + ->whereFileId($entry->getId()); + $query->execute(); + + if ($entry->getMimeType() == FileInfo::MIMETYPE_FOLDER) { + $this->removeChildren($entry); + } } } /** * Get all sub folders of a folder * - * @param array $entry the cache entry of the folder to get the subfolders for - * @return array[] the cache entries for the subfolders + * @param ICacheEntry $entry the cache entry of the folder to get the subfolders for + * @return ICacheEntry[] the cache entries for the subfolders */ - private function getSubFolders($entry) { - $children = $this->getFolderContentsById($entry['fileid']); + private function getSubFolders(ICacheEntry $entry) { + $children = $this->getFolderContentsById($entry->getId()); return array_filter($children, function ($child) { - return $child['mimetype'] === 'httpd/unix-directory'; + return $child->getMimeType() == FileInfo::MIMETYPE_FOLDER; }); } /** * Recursively remove all children of a folder * - * @param array $entry the cache entry of the folder to remove the children of + * @param ICacheEntry $entry the cache entry of the folder to remove the children of * @throws \OC\DatabaseException */ - private function removeChildren($entry) { - $subFolders = $this->getSubFolders($entry); - foreach ($subFolders as $folder) { + private function removeChildren(ICacheEntry $entry) { + $children = $this->getFolderContentsById($entry->getId()); + $childIds = array_map(function(ICacheEntry $cacheEntry) { + return $cacheEntry->getId(); + }, $children); + $childFolders = array_filter($children, function ($child) { + return $child->getMimeType() == FileInfo::MIMETYPE_FOLDER; + }); + foreach ($childFolders as $folder) { $this->removeChildren($folder); } - $sql = 'DELETE FROM `*PREFIX*filecache` WHERE `parent` = ?'; - $this->connection->executeQuery($sql, array($entry['fileid'])); + + $query = $this->getQueryBuilder(); + $query->delete('filecache') + ->whereParent($entry->getId()); + $query->execute(); + + $query = $this->getQueryBuilder(); + $query->delete('filecache_extended') + ->where($query->expr()->in('fileid', $query->createNamedParameter($childIds, IQueryBuilder::PARAM_INT_ARRAY))); + $query->execute(); } /** @@ -575,8 +646,16 @@ class Cache implements ICache { } } - $sql = 'UPDATE `*PREFIX*filecache` SET `storage` = ?, `path` = ?, `path_hash` = ?, `name` = ?, `parent` = ? WHERE `fileid` = ?'; - $this->connection->executeQuery($sql, array($targetStorageId, $targetPath, md5($targetPath), basename($targetPath), $newParentId, $sourceId)); + $query = $this->getQueryBuilder(); + $query->update('filecache') + ->set('storage', $query->createNamedParameter($targetStorageId)) + ->set('path', $query->createNamedParameter($targetPath)) + ->set('path_hash', $query->createNamedParameter(md5($targetPath))) + ->set('name', $query->createNamedParameter(basename($targetPath))) + ->set('parent', $query->createNamedParameter($newParentId, IQueryBuilder::PARAM_INT)) + ->whereFileId($sourceId); + $query->execute(); + $this->connection->commit(); } else { $this->moveFromCacheFallback($sourceCache, $sourcePath, $targetPath); @@ -587,11 +666,15 @@ class Cache implements ICache { * remove all entries for files that are stored on the storage from the cache */ public function clear() { - $sql = 'DELETE FROM `*PREFIX*filecache` WHERE `storage` = ?'; - $this->connection->executeQuery($sql, array($this->getNumericStorageId())); + $query = $this->getQueryBuilder(); + $query->delete('filecache') + ->whereStorageId(); + $query->execute(); - $sql = 'DELETE FROM `*PREFIX*storages` WHERE `id` = ?'; - $this->connection->executeQuery($sql, array($this->storageId)); + $query = $this->connection->getQueryBuilder(); + $query->delete('storages') + ->where($query->expr()->eq('id', $query->createNamedParameter($this->storageId))); + $query->execute(); } /** @@ -610,11 +693,14 @@ class Cache implements ICache { // normalize file $file = $this->normalize($file); - $pathHash = md5($file); - $sql = 'SELECT `size` FROM `*PREFIX*filecache` WHERE `storage` = ? AND `path_hash` = ?'; - $result = $this->connection->executeQuery($sql, array($this->getNumericStorageId(), $pathHash)); - if ($row = $result->fetch()) { - if ((int)$row['size'] === -1) { + $query = $this->getQueryBuilder(); + $query->select('size') + ->from('filecache') + ->whereStorageId() + ->wherePath($file); + $size = $query->execute()->fetchColumn(); + if ($size !== false) { + if ((int)$size === -1) { return self::SHALLOW; } else { return self::COMPLETE; @@ -642,18 +728,14 @@ class Cache implements ICache { return []; } + $query = $this->getQueryBuilder(); + $query->selectFileCache() + ->whereStorageId() + ->andWhere($query->expr()->iLike('name', $query->createNamedParameter($pattern))); - $sql = ' - SELECT `fileid`, `storage`, `path`, `parent`, `name`, - `mimetype`, `storage_mtime`, `mimepart`, `size`, `mtime`, - `encrypted`, `etag`, `permissions`, `checksum` - FROM `*PREFIX*filecache` - WHERE `storage` = ? AND `name` ILIKE ?'; - $result = $this->connection->executeQuery($sql, - [$this->getNumericStorageId(), $pattern] - ); - - return $this->searchResultToCacheEntries($result); + return array_map(function (array $data) { + return self::cacheEntryFromData($data, $this->mimetypeLoader); + }, $query->execute()->fetchAll()); } /** @@ -676,26 +758,29 @@ class Cache implements ICache { * @return ICacheEntry[] an array of cache entries where the mimetype matches the search */ public function searchByMime($mimetype) { + $mimeId = $this->mimetypeLoader->getId($mimetype); + + $query = $this->getQueryBuilder(); + $query->selectFileCache() + ->whereStorageId(); + if (strpos($mimetype, '/')) { - $where = '`mimetype` = ?'; + $query->andWhere($query->expr()->eq('mimetype', $query->createNamedParameter($mimeId, IQueryBuilder::PARAM_INT))); } else { - $where = '`mimepart` = ?'; + $query->andWhere($query->expr()->eq('mimepart', $query->createNamedParameter($mimeId, IQueryBuilder::PARAM_INT))); } - $sql = 'SELECT `fileid`, `storage`, `path`, `parent`, `name`, `mimetype`, `mimepart`, `size`, `storage_mtime`, `mtime`, `encrypted`, `etag`, `permissions`, `checksum` - FROM `*PREFIX*filecache` WHERE ' . $where . ' AND `storage` = ?'; - $mimetype = $this->mimetypeLoader->getId($mimetype); - $result = $this->connection->executeQuery($sql, array($mimetype, $this->getNumericStorageId())); - return $this->searchResultToCacheEntries($result); + return array_map(function (array $data) { + return self::cacheEntryFromData($data, $this->mimetypeLoader); + }, $query->execute()->fetchAll()); } public function searchQuery(ISearchQuery $searchQuery) { - $builder = \OC::$server->getDatabaseConnection()->getQueryBuilder(); + $builder = $this->getQueryBuilder(); - $query = $builder->select(['fileid', 'storage', 'path', 'parent', 'name', 'mimetype', 'mimepart', 'size', 'mtime', 'storage_mtime', 'encrypted', 'etag', 'permissions', 'checksum']) - ->from('filecache', 'file'); + $query = $builder->selectFileCache('file'); - $query->where($builder->expr()->eq('storage', $builder->createNamedParameter($this->getNumericStorageId()))); + $query->whereStorageId(); if ($this->querySearchHelper->shouldJoinTags($searchQuery->getSearchOperation())) { $query @@ -755,10 +840,13 @@ class Cache implements ICache { */ public function getIncompleteChildrenCount($fileId) { if ($fileId > -1) { - $sql = 'SELECT count(*) - FROM `*PREFIX*filecache` WHERE `parent` = ? AND size = -1'; - $result = $this->connection->executeQuery($sql, [$fileId]); - return (int)$result->fetchColumn(); + $query = $this->getQueryBuilder(); + $query->select($query->func()->count()) + ->from('filecache') + ->whereParent($fileId) + ->andWhere($query->expr()->lt('size', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT))); + + return (int)$query->execute()->fetchColumn(); } return -1; } @@ -775,14 +863,17 @@ class Cache implements ICache { if (is_null($entry) or !isset($entry['fileid'])) { $entry = $this->get($path); } - if (isset($entry['mimetype']) && $entry['mimetype'] === 'httpd/unix-directory') { + if (isset($entry['mimetype']) && $entry['mimetype'] === FileInfo::MIMETYPE_FOLDER) { $id = $entry['fileid']; - $sql = 'SELECT SUM(`size`) AS f1, MIN(`size`) AS f2 ' . - 'FROM `*PREFIX*filecache` ' . - 'WHERE `parent` = ? AND `storage` = ?'; - $result = $this->connection->executeQuery($sql, array($id, $this->getNumericStorageId())); - if ($row = $result->fetch()) { - $result->closeCursor(); + + $query = $this->getQueryBuilder(); + $query->selectAlias($query->func()->sum('size'), 'f1') + ->selectAlias($query->func()->min('size'), 'f2') + ->from('filecache') + ->whereStorageId() + ->whereParent($id); + + if ($row = $query->execute()->fetch()) { list($sum, $min) = array_values($row); $sum = 0 + $sum; $min = 0 + $min; @@ -791,15 +882,9 @@ class Cache implements ICache { } else { $totalSize = $sum; } - $update = array(); if ($entry['size'] !== $totalSize) { - $update['size'] = $totalSize; - } - if (count($update) > 0) { - $this->update($id, $update); + $this->update($id, ['size' => $totalSize]); } - } else { - $result->closeCursor(); } } return $totalSize; @@ -811,13 +896,14 @@ class Cache implements ICache { * @return int[] */ public function getAll() { - $sql = 'SELECT `fileid` FROM `*PREFIX*filecache` WHERE `storage` = ?'; - $result = $this->connection->executeQuery($sql, array($this->getNumericStorageId())); - $ids = array(); - while ($row = $result->fetch()) { - $ids[] = $row['fileid']; - } - return $ids; + $query = $this->getQueryBuilder(); + $query->select('fileid') + ->from('filecache') + ->whereStorageId(); + + return array_map(function ($id) { + return (int)$id; + }, $query->execute()->fetchAll(\PDO::FETCH_COLUMN)); } /** @@ -830,14 +916,14 @@ class Cache implements ICache { * @return string|bool the path of the folder or false when no folder matched */ public function getIncomplete() { - $query = $this->connection->prepare('SELECT `path` FROM `*PREFIX*filecache`' - . ' WHERE `storage` = ? AND `size` = -1 ORDER BY `fileid` DESC', 1); - $query->execute([$this->getNumericStorageId()]); - if ($row = $query->fetch()) { - return $row['path']; - } else { - return false; - } + $query = $this->getQueryBuilder(); + $query->select('path') + ->from('filecache') + ->whereStorageId() + ->andWhere($query->expr()->lt('size', $query->createNamedParameter(0, IQueryBuilder::PARAM_INT))) + ->orderBy('fileid', 'DESC'); + + return $query->execute()->fetchColumn(); } /** @@ -847,17 +933,14 @@ class Cache implements ICache { * @return string|null the path of the file (relative to the storage) or null if a file with the given id does not exists within this cache */ public function getPathById($id) { - $sql = 'SELECT `path` FROM `*PREFIX*filecache` WHERE `fileid` = ? AND `storage` = ?'; - $result = $this->connection->executeQuery($sql, array($id, $this->getNumericStorageId())); - if ($row = $result->fetch()) { - // Oracle stores empty strings as null... - if ($row['path'] === null) { - return ''; - } - return $row['path']; - } else { - return null; - } + $query = $this->getQueryBuilder(); + $query->select('path') + ->from('filecache') + ->whereStorageId() + ->whereFileId($id); + + $path = $query->execute()->fetchColumn(); + return $path === false ? null : $path; } /** @@ -866,14 +949,15 @@ class Cache implements ICache { * instead does a global search in the cache table * * @param int $id - * @deprecated use getPathById() instead * @return array first element holding the storage id, second the path + * @deprecated use getPathById() instead */ static public function getById($id) { - $connection = \OC::$server->getDatabaseConnection(); - $sql = 'SELECT `storage`, `path` FROM `*PREFIX*filecache` WHERE `fileid` = ?'; - $result = $connection->executeQuery($sql, array($id)); - if ($row = $result->fetch()) { + $query = \OC::$server->getDatabaseConnection()->getQueryBuilder(); + $query->select('path', 'storage') + ->from('filecache') + ->where($query->expr()->eq('fileid', $query->createNamedParameter($id, IQueryBuilder::PARAM_INT))); + if ($row = $query->execute()->fetch()) { $numericId = $row['storage']; $path = $row['path']; } else { @@ -881,7 +965,7 @@ class Cache implements ICache { } if ($id = Storage::getStorageId($numericId)) { - return array($id, $path); + return [$id, $path]; } else { return null; } diff --git a/lib/private/Files/Cache/CacheEntry.php b/lib/private/Files/Cache/CacheEntry.php index 4a2579a88f8..176a0bf27ed 100644 --- a/lib/private/Files/Cache/CacheEntry.php +++ b/lib/private/Files/Cache/CacheEntry.php @@ -109,6 +109,18 @@ class CacheEntry implements ICacheEntry, \ArrayAccess { return isset($this->data['encrypted']) && $this->data['encrypted']; } + public function getMetadataEtag(): ?string { + return $this->data['metadata_etag']; + } + + public function getCreationTime(): ?int { + return $this->data['creation_time']; + } + + public function getUploadTime(): ?int { + return $this->data['upload_time']; + } + public function getData() { return $this->data; } diff --git a/lib/private/Files/Cache/CacheQueryBuilder.php b/lib/private/Files/Cache/CacheQueryBuilder.php new file mode 100644 index 00000000000..a5ff2129de8 --- /dev/null +++ b/lib/private/Files/Cache/CacheQueryBuilder.php @@ -0,0 +1,92 @@ +<?php declare(strict_types=1); +/** + * @copyright Copyright (c) 2019 Robin Appelman <robin@icewind.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 OC\Files\Cache; + +use OC\DB\QueryBuilder\QueryBuilder; +use OC\SystemConfig; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IDBConnection; +use OCP\ILogger; + +/** + * Query builder with commonly used helpers for filecache queries + */ +class CacheQueryBuilder extends QueryBuilder { + private $cache; + private $alias = null; + + public function __construct(IDBConnection $connection, SystemConfig $systemConfig, ILogger $logger, Cache $cache) { + parent::__construct($connection, $systemConfig, $logger); + + $this->cache = $cache; + } + + public function selectFileCache(string $alias = null) { + $name = $alias ? $alias : 'filecache'; + $this->select("$name.fileid", 'storage', 'path', 'path_hash', "$name.parent", 'name', 'mimetype', 'mimepart', 'size', 'mtime', + 'storage_mtime', 'encrypted', 'etag', 'permissions', 'checksum', 'metadata_etag', 'creation_time', 'upload_time') + ->from('filecache', $name) + ->leftJoin($name, 'filecache_extended', 'fe', $this->expr()->eq("$name.fileid", 'fe.fileid')); + + $this->alias = $name; + + return $this; + } + + public function whereStorageId() { + $this->andWhere($this->expr()->eq('storage', $this->createNamedParameter($this->cache->getNumericStorageId(), IQueryBuilder::PARAM_INT))); + + return $this; + } + + public function whereFileId(int $fileId) { + $alias = $this->alias; + if ($alias) { + $alias .= '.'; + } else { + $alias = ''; + } + + $this->andWhere($this->expr()->eq("{$alias}fileid", $this->createNamedParameter($fileId, IQueryBuilder::PARAM_INT))); + + return $this; + } + + public function wherePath(string $path) { + $this->andWhere($this->expr()->eq('path_hash', $this->createNamedParameter(md5($path)))); + + return $this; + } + + public function whereParent(int $parent) { + $alias = $this->alias; + if ($alias) { + $alias .= '.'; + } else { + $alias = ''; + } + + $this->andWhere($this->expr()->eq("{$alias}parent", $this->createNamedParameter($parent, IQueryBuilder::PARAM_INT))); + + return $this; + } +} diff --git a/lib/private/Files/Cache/MoveFromCacheTrait.php b/lib/private/Files/Cache/MoveFromCacheTrait.php index a814a081dca..e9b41bd9b37 100644 --- a/lib/private/Files/Cache/MoveFromCacheTrait.php +++ b/lib/private/Files/Cache/MoveFromCacheTrait.php @@ -82,7 +82,10 @@ trait MoveFromCacheTrait { 'mimepart' => $entry->getMimePart(), 'etag' => $entry->getEtag(), 'permissions' => $entry->getPermissions(), - 'encrypted' => $entry->isEncrypted() + 'encrypted' => $entry->isEncrypted(), + 'creation_time' => $entry->getCreationTime(), + 'upload_time' => $entry->getUploadTime(), + 'metadata_etag' => $entry->getMetadataEtag(), ]; } } diff --git a/lib/private/Files/Cache/Propagator.php b/lib/private/Files/Cache/Propagator.php index 989a4d0c7d5..41b4c2bb070 100644 --- a/lib/private/Files/Cache/Propagator.php +++ b/lib/private/Files/Cache/Propagator.php @@ -91,7 +91,7 @@ class Propagator implements IPropagator { }, $parentHashes); $builder->update('filecache') - ->set('mtime', $builder->createFunction('GREATEST(' . $builder->getColumnName('mtime') . ', ' . $builder->createNamedParameter((int)$time, IQueryBuilder::PARAM_INT) . ')')) + ->set('mtime', $builder->func()->greatest('mtime', $builder->createNamedParameter((int)$time, IQueryBuilder::PARAM_INT))) ->set('etag', $builder->createNamedParameter($etag, IQueryBuilder::PARAM_STR)) ->where($builder->expr()->eq('storage', $builder->createNamedParameter($storageId, IQueryBuilder::PARAM_INT))) ->andWhere($builder->expr()->in('path_hash', $hashParams)); @@ -102,7 +102,10 @@ class Propagator implements IPropagator { // we need to do size separably so we can ignore entries with uncalculated size $builder = $this->connection->getQueryBuilder(); $builder->update('filecache') - ->set('size', $builder->func()->add('size', $builder->createNamedParameter($sizeDifference))) + ->set('size', $builder->func()->greatest( + $builder->createNamedParameter(-1, IQueryBuilder::PARAM_INT), + $builder->func()->add('size', $builder->createNamedParameter($sizeDifference))) + ) ->where($builder->expr()->eq('storage', $builder->createNamedParameter($storageId, IQueryBuilder::PARAM_INT))) ->andWhere($builder->expr()->in('path_hash', $hashParams)) ->andWhere($builder->expr()->gt('size', $builder->expr()->literal(-1, IQueryBuilder::PARAM_INT))); diff --git a/lib/private/Files/Cache/QuerySearchHelper.php b/lib/private/Files/Cache/QuerySearchHelper.php index ac64ba5c592..2d9d8f374f7 100644 --- a/lib/private/Files/Cache/QuerySearchHelper.php +++ b/lib/private/Files/Cache/QuerySearchHelper.php @@ -136,16 +136,19 @@ class QuerySearchHelper { $type = $operator->getType(); if ($field === 'mimetype') { if ($operator->getType() === ISearchComparison::COMPARE_EQUAL) { - $value = $this->mimetypeLoader->getId($value); + $value = (int)$this->mimetypeLoader->getId($value); } else if ($operator->getType() === ISearchComparison::COMPARE_LIKE) { // transform "mimetype='foo/%'" to "mimepart='foo'" if (preg_match('|(.+)/%|', $value, $matches)) { $field = 'mimepart'; - $value = $this->mimetypeLoader->getId($matches[1]); + $value = (int)$this->mimetypeLoader->getId($matches[1]); $type = ISearchComparison::COMPARE_EQUAL; - } - if (strpos($value, '%') !== false) { + } else if (strpos($value, '%') !== false) { throw new \InvalidArgumentException('Unsupported query value for mimetype: ' . $value . ', only values in the format "mime/type" or "mime/%" are supported'); + } else { + $field = 'mimetype'; + $value = (int)$this->mimetypeLoader->getId($value); + $type = ISearchComparison::COMPARE_EQUAL; } } } else if ($field === 'favorite') { diff --git a/lib/private/Files/Cache/Storage.php b/lib/private/Files/Cache/Storage.php index 5b37c1f43f8..c076cc28359 100644 --- a/lib/private/Files/Cache/Storage.php +++ b/lib/private/Files/Cache/Storage.php @@ -81,7 +81,7 @@ class Storage { if ($row = self::getStorageById($this->storageId)) { $this->numericId = (int)$row['numeric_id']; } else { - throw new \RuntimeException('Storage could neither be inserted nor be selected from the database'); + throw new \RuntimeException('Storage could neither be inserted nor be selected from the database: ' . $this->storageId); } } } @@ -166,11 +166,12 @@ class Storage { /** * @param bool $isAvailable + * @param int $delay amount of seconds to delay reconsidering that storage further */ - public function setAvailability($isAvailable) { + public function setAvailability($isAvailable, int $delay = 0) { $sql = 'UPDATE `*PREFIX*storages` SET `available` = ?, `last_checked` = ? WHERE `id` = ?'; $available = $isAvailable ? 1 : 0; - \OC_DB::executeAudited($sql, array($available, time(), $this->storageId)); + \OC_DB::executeAudited($sql, [$available, time() + $delay, $this->storageId]); } /** diff --git a/lib/private/Files/Cache/StorageGlobal.php b/lib/private/Files/Cache/StorageGlobal.php index 65f689f1874..076b5b8f53d 100644 --- a/lib/private/Files/Cache/StorageGlobal.php +++ b/lib/private/Files/Cache/StorageGlobal.php @@ -69,7 +69,15 @@ class StorageGlobal { */ public function getStorageInfo($storageId) { if (!isset($this->cache[$storageId])) { - $this->loadForStorageIds([$storageId]); + $builder = $this->connection->getQueryBuilder(); + $query = $builder->select(['id', 'numeric_id', 'available', 'last_checked']) + ->from('storages') + ->where($builder->expr()->eq('id', $builder->createNamedParameter($storageId))); + + $row = $query->execute()->fetch(); + if ($row) { + $this->cache[$storageId] = $row; + } } return isset($this->cache[$storageId]) ? $this->cache[$storageId] : null; } diff --git a/lib/private/Files/Cache/Watcher.php b/lib/private/Files/Cache/Watcher.php index 47dde26d7b7..8c5cb0db182 100644 --- a/lib/private/Files/Cache/Watcher.php +++ b/lib/private/Files/Cache/Watcher.php @@ -85,7 +85,7 @@ class Watcher implements IWatcher { if (is_null($cachedEntry)) { $cachedEntry = $this->cache->get($path); } - if ($this->needsUpdate($path, $cachedEntry)) { + if ($cachedEntry === false || $this->needsUpdate($path, $cachedEntry)) { $this->update($path, $cachedEntry); return true; } else { @@ -105,7 +105,7 @@ class Watcher implements IWatcher { } else { $this->scanner->scanFile($path); } - if ($cachedData['mimetype'] === 'httpd/unix-directory') { + if (is_array($cachedData) && $cachedData['mimetype'] === 'httpd/unix-directory') { $this->cleanFolder($path); } if ($this->cache instanceof Cache) { diff --git a/lib/private/Files/Cache/Wrapper/CacheWrapper.php b/lib/private/Files/Cache/Wrapper/CacheWrapper.php index 6d7f1876e29..157bb083b73 100644 --- a/lib/private/Files/Cache/Wrapper/CacheWrapper.php +++ b/lib/private/Files/Cache/Wrapper/CacheWrapper.php @@ -28,8 +28,8 @@ namespace OC\Files\Cache\Wrapper; use OC\Files\Cache\Cache; -use OCP\Files\Cache\ICacheEntry; use OCP\Files\Cache\ICache; +use OCP\Files\Cache\ICacheEntry; use OCP\Files\Search\ISearchQuery; class CacheWrapper extends Cache { diff --git a/lib/private/Files/Config/CachedMountFileInfo.php b/lib/private/Files/Config/CachedMountFileInfo.php index e315493a693..331e28d3d5e 100644 --- a/lib/private/Files/Config/CachedMountFileInfo.php +++ b/lib/private/Files/Config/CachedMountFileInfo.php @@ -45,4 +45,4 @@ class CachedMountFileInfo extends CachedMountInfo implements ICachedMountFileInf public function getPath() { return $this->getMountPoint() . $this->getInternalPath(); } -}
\ No newline at end of file +} diff --git a/lib/private/Files/Config/MountProviderCollection.php b/lib/private/Files/Config/MountProviderCollection.php index a3c88dd6b4b..fbb455c5063 100644 --- a/lib/private/Files/Config/MountProviderCollection.php +++ b/lib/private/Files/Config/MountProviderCollection.php @@ -26,8 +26,8 @@ namespace OC\Files\Config; use OC\Hooks\Emitter; use OC\Hooks\EmitterTrait; use OCP\Files\Config\IHomeMountProvider; -use OCP\Files\Config\IMountProviderCollection; use OCP\Files\Config\IMountProvider; +use OCP\Files\Config\IMountProviderCollection; use OCP\Files\Config\IUserMountCache; use OCP\Files\Mount\IMountManager; use OCP\Files\Mount\IMountPoint; diff --git a/lib/private/Files/Config/UserMountCache.php b/lib/private/Files/Config/UserMountCache.php index 63abdf5fdeb..53bd52a2e6c 100644 --- a/lib/private/Files/Config/UserMountCache.php +++ b/lib/private/Files/Config/UserMountCache.php @@ -25,6 +25,7 @@ namespace OC\Files\Config; +use OC\Cache\CappedMemoryCache; use OCA\Files_Sharing\SharedMount; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Files\Config\ICachedMountFileInfo; @@ -37,7 +38,6 @@ use OCP\IDBConnection; use OCP\ILogger; use OCP\IUser; use OCP\IUserManager; -use OC\Cache\CappedMemoryCache; /** * Cache mounts points per user in the cache so we can easilly look them up diff --git a/lib/private/Files/FileInfo.php b/lib/private/Files/FileInfo.php index 19b95cd0355..93f876db17b 100644 --- a/lib/private/Files/FileInfo.php +++ b/lib/private/Files/FileInfo.php @@ -406,4 +406,12 @@ class FileInfo implements \OCP\Files\FileInfo, \ArrayAccess { public function getExtension(): string { return pathinfo($this->getName(), PATHINFO_EXTENSION); } + + public function getCreationTime(): int { + return (int) $this->data['creation_time']; + } + + public function getUploadTime(): int { + return (int) $this->data['upload_time']; + } } diff --git a/lib/private/Files/Mount/MountPoint.php b/lib/private/Files/Mount/MountPoint.php index 36e10bfb45a..1e67eecb9dc 100644 --- a/lib/private/Files/Mount/MountPoint.php +++ b/lib/private/Files/Mount/MountPoint.php @@ -29,9 +29,9 @@ namespace OC\Files\Mount; -use \OC\Files\Filesystem; -use OC\Files\Storage\StorageFactory; +use OC\Files\Filesystem; use OC\Files\Storage\Storage; +use OC\Files\Storage\StorageFactory; use OCP\Files\Mount\IMountPoint; use OCP\ILogger; diff --git a/lib/private/Files/Node/File.php b/lib/private/Files/Node/File.php index a3eabbcc446..e4669f70709 100644 --- a/lib/private/Files/Node/File.php +++ b/lib/private/Files/Node/File.php @@ -28,6 +28,7 @@ namespace OC\Files\Node; use OCP\Files\GenericFileException; use OCP\Files\NotPermittedException; +use OCP\Lock\LockedException; class File extends Node implements \OCP\Files\File { /** @@ -42,7 +43,8 @@ class File extends Node implements \OCP\Files\File { /** * @return string - * @throws \OCP\Files\NotPermittedException + * @throws NotPermittedException + * @throws LockedException */ public function getContent() { if ($this->checkPermissions(\OCP\Constants::PERMISSION_READ)) { @@ -57,8 +59,9 @@ class File extends Node implements \OCP\Files\File { /** * @param string|resource $data - * @throws \OCP\Files\NotPermittedException + * @throws NotPermittedException * @throws \OCP\Files\GenericFileException + * @throws LockedException */ public function putContent($data) { if ($this->checkPermissions(\OCP\Constants::PERMISSION_UPDATE)) { @@ -76,7 +79,8 @@ class File extends Node implements \OCP\Files\File { /** * @param string $mode * @return resource - * @throws \OCP\Files\NotPermittedException + * @throws NotPermittedException + * @throws LockedException */ public function fopen($mode) { $preHooks = array(); @@ -113,13 +117,18 @@ class File extends Node implements \OCP\Files\File { } } + /** + * @throws NotPermittedException + * @throws \OCP\Files\InvalidPathException + * @throws \OCP\Files\NotFoundException + */ public function delete() { if ($this->checkPermissions(\OCP\Constants::PERMISSION_DELETE)) { $this->sendHooks(array('preDelete')); $fileInfo = $this->getFileInfo(); $this->view->unlink($this->path); $nonExisting = new NonExistingFile($this->root, $this->view, $this->path, $fileInfo); - $this->root->emit('\OC\Files', 'postDelete', array($nonExisting)); + $this->sendHooks(['postDelete'], [$nonExisting]); $this->exists = false; $this->fileInfo = null; } else { diff --git a/lib/private/Files/Node/Folder.php b/lib/private/Files/Node/Folder.php index 19f04048779..4a134cdcdbf 100644 --- a/lib/private/Files/Node/Folder.php +++ b/lib/private/Files/Node/Folder.php @@ -156,14 +156,12 @@ class Folder extends Node implements \OCP\Files\Folder { if ($this->checkPermissions(\OCP\Constants::PERMISSION_CREATE)) { $fullPath = $this->getFullPath($path); $nonExisting = new NonExistingFolder($this->root, $this->view, $fullPath); - $this->root->emit('\OC\Files', 'preWrite', array($nonExisting)); - $this->root->emit('\OC\Files', 'preCreate', array($nonExisting)); + $this->sendHooks(['preWrite', 'preCreate'], [$nonExisting]); if(!$this->view->mkdir($fullPath)) { throw new NotPermittedException('Could not create folder'); } $node = new Folder($this->root, $this->view, $fullPath); - $this->root->emit('\OC\Files', 'postWrite', array($node)); - $this->root->emit('\OC\Files', 'postCreate', array($node)); + $this->sendHooks(['postWrite', 'postCreate'], [$node]); return $node; } else { throw new NotPermittedException('No create permission for folder'); @@ -179,14 +177,12 @@ class Folder extends Node implements \OCP\Files\Folder { if ($this->checkPermissions(\OCP\Constants::PERMISSION_CREATE)) { $fullPath = $this->getFullPath($path); $nonExisting = new NonExistingFile($this->root, $this->view, $fullPath); - $this->root->emit('\OC\Files', 'preWrite', array($nonExisting)); - $this->root->emit('\OC\Files', 'preCreate', array($nonExisting)); + $this->sendHooks(['preWrite', 'preCreate'], [$nonExisting]); if (!$this->view->touch($fullPath)) { throw new NotPermittedException('Could not create path'); } $node = new File($this->root, $this->view, $fullPath); - $this->root->emit('\OC\Files', 'postWrite', array($node)); - $this->root->emit('\OC\Files', 'postCreate', array($node)); + $this->sendHooks(['postWrite', 'postCreate'], [$node]); return $node; } throw new NotPermittedException('No create permission for path'); @@ -303,6 +299,9 @@ class Folder extends Node implements \OCP\Files\Folder { })); if (count($mountsContainingFile) === 0) { + if ($user === $this->getAppDataDirectoryName()) { + return $this->getByIdInRootMount((int) $id); + } return []; } @@ -331,6 +330,47 @@ class Folder extends Node implements \OCP\Files\Folder { }); } + protected function getAppDataDirectoryName(): string { + $instanceId = \OC::$server->getConfig()->getSystemValueString('instanceid'); + return 'appdata_' . $instanceId; + } + + /** + * In case the path we are currently in is inside the appdata_* folder, + * the original getById method does not work, because it can only look inside + * the user's mount points. But the user has no mount point for the root storage. + * + * So in that case we directly check the mount of the root if it contains + * the id. If it does we check if the path is inside the path we are working + * in. + * + * @param int $id + * @return array + */ + protected function getByIdInRootMount(int $id): array { + $mount = $this->root->getMount(''); + $cacheEntry = $mount->getStorage()->getCache($this->path)->get($id); + if (!$cacheEntry) { + return []; + } + + $absolutePath = '/' . ltrim($cacheEntry->getPath(), '/'); + $currentPath = rtrim($this->path, '/') . '/'; + + if (strpos($absolutePath, $currentPath) !== 0) { + return []; + } + + return [$this->root->createNode( + $absolutePath, new \OC\Files\FileInfo( + $absolutePath, + $mount->getStorage(), + $cacheEntry->getPath(), + $cacheEntry, + $mount + ))]; + } + public function getFreeSpace() { return $this->view->free_space($this->path); } @@ -341,7 +381,7 @@ class Folder extends Node implements \OCP\Files\Folder { $fileInfo = $this->getFileInfo(); $this->view->rmdir($this->path); $nonExisting = new NonExistingFolder($this->root, $this->view, $this->path, $fileInfo); - $this->root->emit('\OC\Files', 'postDelete', array($nonExisting)); + $this->sendHooks(['postDelete'], [$nonExisting]); $this->exists = false; } else { throw new NotPermittedException('No delete permission for path'); diff --git a/lib/private/Files/Node/HookConnector.php b/lib/private/Files/Node/HookConnector.php index f5adcde4a00..417bb4980ee 100644 --- a/lib/private/Files/Node/HookConnector.php +++ b/lib/private/Files/Node/HookConnector.php @@ -22,10 +22,12 @@ namespace OC\Files\Node; -use OCP\Files\FileInfo; use OC\Files\Filesystem; use OC\Files\View; +use OCP\EventDispatcher\GenericEvent; +use OCP\Files\FileInfo; use OCP\Util; +use Symfony\Component\EventDispatcher\EventDispatcherInterface; class HookConnector { /** @@ -42,6 +44,8 @@ class HookConnector { * @var FileInfo[] */ private $deleteMetaCache = []; + /** @var EventDispatcherInterface */ + private $dispatcher; /** * HookConnector constructor. @@ -49,9 +53,10 @@ class HookConnector { * @param Root $root * @param View $view */ - public function __construct(Root $root, View $view) { + public function __construct(Root $root, View $view, EventDispatcherInterface $dispatcher) { $this->root = $root; $this->view = $view; + $this->dispatcher = $dispatcher; } public function viewToNode() { @@ -79,72 +84,85 @@ class HookConnector { public function write($arguments) { $node = $this->getNodeForPath($arguments['path']); $this->root->emit('\OC\Files', 'preWrite', [$node]); + $this->dispatcher->dispatch('\OCP\Files::preWrite', new GenericEvent($node)); } public function postWrite($arguments) { $node = $this->getNodeForPath($arguments['path']); $this->root->emit('\OC\Files', 'postWrite', [$node]); + $this->dispatcher->dispatch('\OCP\Files::postWrite', new GenericEvent($node)); } public function create($arguments) { $node = $this->getNodeForPath($arguments['path']); $this->root->emit('\OC\Files', 'preCreate', [$node]); + $this->dispatcher->dispatch('\OCP\Files::preCreate', new GenericEvent($node)); } public function postCreate($arguments) { $node = $this->getNodeForPath($arguments['path']); $this->root->emit('\OC\Files', 'postCreate', [$node]); + $this->dispatcher->dispatch('\OCP\Files::postCreate', new GenericEvent($node)); } public function delete($arguments) { $node = $this->getNodeForPath($arguments['path']); $this->deleteMetaCache[$node->getPath()] = $node->getFileInfo(); $this->root->emit('\OC\Files', 'preDelete', [$node]); + $this->dispatcher->dispatch('\OCP\Files::preDelete', new GenericEvent($node)); } public function postDelete($arguments) { $node = $this->getNodeForPath($arguments['path']); unset($this->deleteMetaCache[$node->getPath()]); $this->root->emit('\OC\Files', 'postDelete', [$node]); + $this->dispatcher->dispatch('\OCP\Files::postDelete', new GenericEvent($node)); } public function touch($arguments) { $node = $this->getNodeForPath($arguments['path']); $this->root->emit('\OC\Files', 'preTouch', [$node]); + $this->dispatcher->dispatch('\OCP\Files::preTouch', new GenericEvent($node)); } public function postTouch($arguments) { $node = $this->getNodeForPath($arguments['path']); $this->root->emit('\OC\Files', 'postTouch', [$node]); + $this->dispatcher->dispatch('\OCP\Files::postTouch', new GenericEvent($node)); } public function rename($arguments) { $source = $this->getNodeForPath($arguments['oldpath']); $target = $this->getNodeForPath($arguments['newpath']); $this->root->emit('\OC\Files', 'preRename', [$source, $target]); + $this->dispatcher->dispatch('\OCP\Files::preRename', new GenericEvent([$source, $target])); } public function postRename($arguments) { $source = $this->getNodeForPath($arguments['oldpath']); $target = $this->getNodeForPath($arguments['newpath']); $this->root->emit('\OC\Files', 'postRename', [$source, $target]); + $this->dispatcher->dispatch('\OCP\Files::postRename', new GenericEvent([$source, $target])); } public function copy($arguments) { $source = $this->getNodeForPath($arguments['oldpath']); $target = $this->getNodeForPath($arguments['newpath']); $this->root->emit('\OC\Files', 'preCopy', [$source, $target]); + $this->dispatcher->dispatch('\OCP\Files::preCopy', new GenericEvent([$source, $target])); } public function postCopy($arguments) { $source = $this->getNodeForPath($arguments['oldpath']); $target = $this->getNodeForPath($arguments['newpath']); $this->root->emit('\OC\Files', 'postCopy', [$source, $target]); + $this->dispatcher->dispatch('\OCP\Files::postCopy', new GenericEvent([$source, $target])); } public function read($arguments) { $node = $this->getNodeForPath($arguments['path']); $this->root->emit('\OC\Files', 'read', [$node]); + $this->dispatcher->dispatch('\OCP\Files::read', new GenericEvent([$node])); } private function getNodeForPath($path) { diff --git a/lib/private/Files/Node/LazyRoot.php b/lib/private/Files/Node/LazyRoot.php index 01b4ca52765..acf288860d9 100644 --- a/lib/private/Files/Node/LazyRoot.php +++ b/lib/private/Files/Node/LazyRoot.php @@ -20,6 +20,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Files\Node; use OCP\Files\IRootFolder; @@ -480,4 +481,18 @@ class LazyRoot implements IRootFolder { public function getRecent($limit, $offset = 0) { return $this->__call(__FUNCTION__, func_get_args()); } + + /** + * @inheritDoc + */ + public function getCreationTime(): int { + return $this->__call(__FUNCTION__, func_get_args()); + } + + /** + * @inheritDoc + */ + public function getUploadTime(): int { + return $this->__call(__FUNCTION__, func_get_args()); + } } diff --git a/lib/private/Files/Node/Node.php b/lib/private/Files/Node/Node.php index dc025b79575..7f50524f28d 100644 --- a/lib/private/Files/Node/Node.php +++ b/lib/private/Files/Node/Node.php @@ -33,6 +33,8 @@ use OCP\Files\FileInfo; use OCP\Files\InvalidPathException; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; +use OCP\Lock\LockedException; +use Symfony\Component\EventDispatcher\GenericEvent; // FIXME: this class really should be abstract class Node implements \OCP\Files\Node { @@ -74,6 +76,7 @@ class Node implements \OCP\Files\Node { * * @param string $path path * @return string non-existing node class + * @throws \Exception */ protected function createNonExistingNode($path) { throw new \Exception('Must be implemented by subclasses'); @@ -104,15 +107,20 @@ class Node implements \OCP\Files\Node { /** * @param string[] $hooks */ - protected function sendHooks($hooks) { + protected function sendHooks($hooks, array $args = null) { + $args = !empty($args) ? $args : [$this]; + $dispatcher = \OC::$server->getEventDispatcher(); foreach ($hooks as $hook) { - $this->root->emit('\OC\Files', $hook, array($this)); + $this->root->emit('\OC\Files', $hook, $args); + $dispatcher->dispatch('\OCP\Files::' . $hook, new GenericEvent($args)); } } /** * @param int $permissions * @return bool + * @throws InvalidPathException + * @throws NotFoundException */ protected function checkPermissions($permissions) { return ($this->getPermissions() & $permissions) === $permissions; @@ -123,7 +131,9 @@ class Node implements \OCP\Files\Node { /** * @param int $mtime - * @throws \OCP\Files\NotPermittedException + * @throws InvalidPathException + * @throws NotFoundException + * @throws NotPermittedException */ public function touch($mtime = null) { if ($this->checkPermissions(\OCP\Constants::PERMISSION_UPDATE)) { @@ -362,7 +372,7 @@ class Node implements \OCP\Files\Node { /** * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE - * @throws \OCP\Lock\LockedException + * @throws LockedException */ public function lock($type) { $this->view->lockFile($this->path, $type); @@ -370,7 +380,7 @@ class Node implements \OCP\Files\Node { /** * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE - * @throws \OCP\Lock\LockedException + * @throws LockedException */ public function changeLock($type) { $this->view->changeLock($this->path, $type); @@ -378,7 +388,7 @@ class Node implements \OCP\Files\Node { /** * @param int $type \OCP\Lock\ILockingProvider::LOCK_SHARED or \OCP\Lock\ILockingProvider::LOCK_EXCLUSIVE - * @throws \OCP\Lock\LockedException + * @throws LockedException */ public function unlock($type) { $this->view->unlockFile($this->path, $type); @@ -386,22 +396,24 @@ class Node implements \OCP\Files\Node { /** * @param string $targetPath - * @throws \OCP\Files\NotPermittedException if copy not allowed or failed * @return \OC\Files\Node\Node + * @throws InvalidPathException + * @throws NotFoundException + * @throws NotPermittedException if copy not allowed or failed */ public function copy($targetPath) { $targetPath = $this->normalizePath($targetPath); $parent = $this->root->get(dirname($targetPath)); if ($parent instanceof Folder and $this->isValidPath($targetPath) and $parent->isCreatable()) { $nonExisting = $this->createNonExistingNode($targetPath); - $this->root->emit('\OC\Files', 'preCopy', [$this, $nonExisting]); - $this->root->emit('\OC\Files', 'preWrite', [$nonExisting]); + $this->sendHooks(['preCopy'], [$this, $nonExisting]); + $this->sendHooks(['preWrite'], [$nonExisting]); if (!$this->view->copy($this->path, $targetPath)) { throw new NotPermittedException('Could not copy ' . $this->path . ' to ' . $targetPath); } $targetNode = $this->root->get($targetPath); - $this->root->emit('\OC\Files', 'postCopy', [$this, $targetNode]); - $this->root->emit('\OC\Files', 'postWrite', [$targetNode]); + $this->sendHooks(['postCopy'], [$this, $targetNode]); + $this->sendHooks(['postWrite'], [$targetNode]); return $targetNode; } else { throw new NotPermittedException('No permission to copy to path ' . $targetPath); @@ -410,8 +422,11 @@ class Node implements \OCP\Files\Node { /** * @param string $targetPath - * @throws \OCP\Files\NotPermittedException if move not allowed or failed * @return \OC\Files\Node\Node + * @throws InvalidPathException + * @throws NotFoundException + * @throws NotPermittedException if move not allowed or failed + * @throws LockedException */ public function move($targetPath) { $targetPath = $this->normalizePath($targetPath); @@ -425,14 +440,14 @@ class Node implements \OCP\Files\Node { ) ) { $nonExisting = $this->createNonExistingNode($targetPath); - $this->root->emit('\OC\Files', 'preRename', [$this, $nonExisting]); - $this->root->emit('\OC\Files', 'preWrite', [$nonExisting]); + $this->sendHooks(['preRename'], [$this, $nonExisting]); + $this->sendHooks(['preWrite'], [$nonExisting]); if (!$this->view->rename($this->path, $targetPath)) { throw new NotPermittedException('Could not move ' . $this->path . ' to ' . $targetPath); } $targetNode = $this->root->get($targetPath); - $this->root->emit('\OC\Files', 'postRename', [$this, $targetNode]); - $this->root->emit('\OC\Files', 'postWrite', [$targetNode]); + $this->sendHooks(['postRename'], [$this, $targetNode]); + $this->sendHooks(['postWrite'], [$targetNode]); $this->path = $targetPath; return $targetNode; } else { @@ -440,4 +455,12 @@ class Node implements \OCP\Files\Node { } } + public function getCreationTime(): int { + return $this->getFileInfo()->getCreationTime(); + } + + public function getUploadTime(): int { + return $this->getFileInfo()->getUploadTime(); + } + } diff --git a/lib/private/Files/Node/Root.php b/lib/private/Files/Node/Root.php index 390d29edb31..ad0935ebaf7 100644 --- a/lib/private/Files/Node/Root.php +++ b/lib/private/Files/Node/Root.php @@ -33,11 +33,11 @@ namespace OC\Files\Node; use OC\Cache\CappedMemoryCache; use OC\Files\Mount\Manager; use OC\Files\Mount\MountPoint; +use OC\Hooks\PublicEmitter; use OCP\Files\Config\IUserMountCache; +use OCP\Files\IRootFolder; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; -use OC\Hooks\PublicEmitter; -use OCP\Files\IRootFolder; use OCP\ILogger; use OCP\IUserManager; diff --git a/lib/private/Files/ObjectStore/HomeObjectStoreStorage.php b/lib/private/Files/ObjectStore/HomeObjectStoreStorage.php index fb5e8d83ab1..fd6c9a2ebc1 100644 --- a/lib/private/Files/ObjectStore/HomeObjectStoreStorage.php +++ b/lib/private/Files/ObjectStore/HomeObjectStoreStorage.php @@ -66,4 +66,4 @@ class HomeObjectStoreStorage extends ObjectStoreStorage implements \OCP\Files\IH } -}
\ No newline at end of file +} diff --git a/lib/private/Files/ObjectStore/Mapper.php b/lib/private/Files/ObjectStore/Mapper.php index 2b5cf6ea4ab..c4b5e5d7778 100644 --- a/lib/private/Files/ObjectStore/Mapper.php +++ b/lib/private/Files/ObjectStore/Mapper.php @@ -20,6 +20,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Files\ObjectStore; use OCP\IUser; diff --git a/lib/private/Files/ObjectStore/NoopScanner.php b/lib/private/Files/ObjectStore/NoopScanner.php index a3a396651a2..1ee868c49d4 100644 --- a/lib/private/Files/ObjectStore/NoopScanner.php +++ b/lib/private/Files/ObjectStore/NoopScanner.php @@ -24,8 +24,8 @@ */ namespace OC\Files\ObjectStore; -use \OC\Files\Cache\Scanner; -use \OC\Files\Storage\Storage; +use OC\Files\Cache\Scanner; +use OC\Files\Storage\Storage; class NoopScanner extends Scanner { diff --git a/lib/private/Files/ObjectStore/ObjectStoreStorage.php b/lib/private/Files/ObjectStore/ObjectStoreStorage.php index fbfbcfaa409..80f438d762c 100644 --- a/lib/private/Files/ObjectStore/ObjectStoreStorage.php +++ b/lib/private/Files/ObjectStore/ObjectStoreStorage.php @@ -161,7 +161,9 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common { return false; } - $this->rmObjects($path); + if (!$this->rmObjects($path)) { + return false; + } $this->getCache()->remove($path); @@ -172,11 +174,17 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common { $children = $this->getCache()->getFolderContents($path); foreach ($children as $child) { if ($child['mimetype'] === 'httpd/unix-directory') { - $this->rmObjects($child['path']); + if (!$this->rmObjects($child['path'])) { + return false; + } } else { - $this->unlink($child['path']); + if(!$this->unlink($child['path'])) { + return false; + } } } + + return true; } public function unlink($path) { diff --git a/lib/private/Files/ObjectStore/SwiftFactory.php b/lib/private/Files/ObjectStore/SwiftFactory.php index f6270d36e7a..430d628d3ce 100644 --- a/lib/private/Files/ObjectStore/SwiftFactory.php +++ b/lib/private/Files/ObjectStore/SwiftFactory.php @@ -31,15 +31,15 @@ use OCP\Files\StorageAuthException; use OCP\Files\StorageNotAvailableException; use OCP\ICache; use OCP\ILogger; -use OpenStack\Common\Error\BadResponseError; use OpenStack\Common\Auth\Token; +use OpenStack\Common\Error\BadResponseError; +use OpenStack\Common\Transport\Utils as TransportUtils; use OpenStack\Identity\v2\Models\Catalog; use OpenStack\Identity\v2\Service as IdentityV2Service; use OpenStack\Identity\v3\Service as IdentityV3Service; +use OpenStack\ObjectStore\v1\Models\Container; use OpenStack\OpenStack; -use OpenStack\Common\Transport\Utils as TransportUtils; use Psr\Http\Message\RequestInterface; -use OpenStack\ObjectStore\v1\Models\Container; class SwiftFactory { private $cache; diff --git a/lib/private/Files/SimpleFS/SimpleFile.php b/lib/private/Files/SimpleFS/SimpleFile.php index 9c9ca10650c..85a1a64a854 100644 --- a/lib/private/Files/SimpleFS/SimpleFile.php +++ b/lib/private/Files/SimpleFS/SimpleFile.php @@ -20,6 +20,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ + namespace OC\Files\SimpleFS; use OCP\Files\File; diff --git a/lib/private/Files/SimpleFS/SimpleFolder.php b/lib/private/Files/SimpleFS/SimpleFolder.php index 5b55fe0f157..9375a3c5545 100644 --- a/lib/private/Files/SimpleFS/SimpleFolder.php +++ b/lib/private/Files/SimpleFS/SimpleFolder.php @@ -20,6 +20,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ + namespace OC\Files\SimpleFS; use OCP\Files\File; diff --git a/lib/private/Files/Storage/Common.php b/lib/private/Files/Storage/Common.php index 4e95c594cfa..3bc14d35cd0 100644 --- a/lib/private/Files/Storage/Common.php +++ b/lib/private/Files/Storage/Common.php @@ -44,8 +44,10 @@ use OC\Files\Cache\Cache; use OC\Files\Cache\Propagator; use OC\Files\Cache\Scanner; use OC\Files\Cache\Updater; -use OC\Files\Filesystem; use OC\Files\Cache\Watcher; +use OC\Files\Filesystem; +use OC\Files\Storage\Wrapper\Jail; +use OC\Files\Storage\Wrapper\Wrapper; use OCP\Files\EmptyFileNameException; use OCP\Files\FileNameTooLongException; use OCP\Files\InvalidCharacterInPathException; @@ -636,13 +638,39 @@ abstract class Common implements Storage, ILockingStorage, IWriteStreamStorage { } /** + * Check if a storage is the same as the current one, including wrapped storages + * + * @param IStorage $storage + * @return bool + */ + private function isSameStorage(IStorage $storage): bool { + while ($storage->instanceOfStorage(Wrapper::class)) { + /** + * @var Wrapper $sourceStorage + */ + $storage = $storage->getWrapperStorage(); + } + + return $storage === $this; + } + + /** * @param IStorage $sourceStorage * @param string $sourceInternalPath * @param string $targetInternalPath * @return bool */ public function moveFromStorage(IStorage $sourceStorage, $sourceInternalPath, $targetInternalPath) { - if ($sourceStorage === $this) { + if ($this->isSameStorage($sourceStorage)) { + // resolve any jailed paths + while ($sourceStorage->instanceOfStorage(Jail::class)) { + /** + * @var Jail $sourceStorage + */ + $sourceInternalPath = $sourceStorage->getUnjailedPath($sourceInternalPath); + $sourceStorage = $sourceStorage->getUnjailedStorage(); + } + return $this->rename($sourceInternalPath, $targetInternalPath); } diff --git a/lib/private/Files/Storage/DAV.php b/lib/private/Files/Storage/DAV.php index 52ed8900569..ca96e6b7434 100644 --- a/lib/private/Files/Storage/DAV.php +++ b/lib/private/Files/Storage/DAV.php @@ -34,19 +34,21 @@ namespace OC\Files\Storage; use Exception; -use GuzzleHttp\Exception\RequestException; -use OCP\ILogger; -use Psr\Http\Message\ResponseInterface; use Icewind\Streams\CallbackWrapper; -use OC\Files\Filesystem; use Icewind\Streams\IteratorDirectory; +use OC\Files\Filesystem; use OC\MemCache\ArrayCache; use OCP\AppFramework\Http; use OCP\Constants; use OCP\Files\FileInfo; +use OCP\Files\ForbiddenException; use OCP\Files\StorageInvalidException; use OCP\Files\StorageNotAvailableException; +use OCP\Http\Client\IClientService; +use OCP\ICertificateManager; +use OCP\ILogger; use OCP\Util; +use Psr\Http\Message\ResponseInterface; use Sabre\DAV\Client; use Sabre\DAV\Xml\Property\ResourceType; use Sabre\HTTP\ClientException; @@ -78,8 +80,10 @@ class DAV extends Common { protected $client; /** @var ArrayCache */ protected $statCache; - /** @var \OCP\Http\Client\IClientService */ + /** @var IClientService */ protected $httpClientService; + /** @var ICertificateManager */ + protected $certManager; /** * @param array $params @@ -110,13 +114,9 @@ class DAV extends Common { } if ($this->secure === true) { // inject mock for testing - $certManager = \OC::$server->getCertificateManager(); - if (is_null($certManager)) { //no user - $certManager = \OC::$server->getCertificateManager(null); - } - $certPath = $certManager->getAbsoluteBundlePath(); - if (file_exists($certPath)) { - $this->certPath = $certPath; + $this->certManager = \OC::$server->getCertificateManager(); + if (is_null($this->certManager)) { //no user + $this->certManager = \OC::$server->getCertificateManager(null); } } $this->root = $params['root'] ?? '/'; @@ -149,8 +149,15 @@ class DAV extends Common { $this->client = new Client($settings); $this->client->setThrowExceptions(true); - if ($this->secure === true && $this->certPath) { - $this->client->addCurlSetting(CURLOPT_CAINFO, $this->certPath); + + if($this->secure === true) { + $certPath = $this->certManager->getAbsoluteBundlePath(); + if (file_exists($certPath)) { + $this->certPath = $certPath; + } + if ($this->certPath) { + $this->client->addCurlSetting(CURLOPT_CAINFO, $this->certPath); + } } } @@ -268,7 +275,7 @@ class DAV extends Common { ); $this->statCache->set($path, $response); } catch (ClientHttpException $e) { - if ($e->getHttpStatus() === 404) { + if ($e->getHttpStatus() === 404 || $e->getHttpStatus() === 405) { $this->statCache->clear($path . '/'); $this->statCache->set($path, false); return false; @@ -829,6 +836,7 @@ class DAV extends Common { * when the authentication expired or is invalid * @throws StorageNotAvailableException if the storage is not available, * which might be temporary + * @throws ForbiddenException if the action is not allowed */ protected function convertException(Exception $e, $path = '') { \OC::$server->getLogger()->logException($e, ['app' => 'files_external', 'level' => ILogger::DEBUG]); @@ -842,6 +850,9 @@ class DAV extends Common { } else if ($e->getHttpStatus() === Http::STATUS_METHOD_NOT_ALLOWED) { // ignore exception for MethodNotAllowed, false will be returned return; + } else if ($e->getHttpStatus() === Http::STATUS_FORBIDDEN){ + // The operation is forbidden. Fail somewhat gracefully + throw new ForbiddenException(get_class($e) . ':' . $e->getMessage()); } throw new StorageNotAvailableException(get_class($e) . ': ' . $e->getMessage()); } else if ($e instanceof ClientException) { @@ -859,4 +870,3 @@ class DAV extends Common { // TODO: only log for now, but in the future need to wrap/rethrow exception } } - diff --git a/lib/private/Files/Storage/FailedStorage.php b/lib/private/Files/Storage/FailedStorage.php index 9ed09e82e7d..4ee9b21694a 100644 --- a/lib/private/Files/Storage/FailedStorage.php +++ b/lib/private/Files/Storage/FailedStorage.php @@ -27,8 +27,8 @@ namespace OC\Files\Storage; use OC\Files\Cache\FailedCache; use OCP\Files\Storage\IStorage; -use \OCP\Lock\ILockingProvider; -use \OCP\Files\StorageNotAvailableException; +use OCP\Files\StorageNotAvailableException; +use OCP\Lock\ILockingProvider; /** * Storage placeholder to represent a missing precondition, storage unavailable diff --git a/lib/private/Files/Storage/Local.php b/lib/private/Files/Storage/Local.php index e9a9e8e9885..aade0cdaf25 100644 --- a/lib/private/Files/Storage/Local.php +++ b/lib/private/Files/Storage/Local.php @@ -424,12 +424,26 @@ class Local extends \OC\Files\Storage\Common { public function getETag($path) { if ($this->is_file($path)) { $stat = $this->stat($path); - return md5( - $stat['mtime'] . - $stat['ino'] . - $stat['dev'] . - $stat['size'] - ); + + if ($stat === false) { + return md5(''); + } + + $toHash = ''; + if (isset($stat['mtime'])) { + $toHash .= $stat['mtime']; + } + if (isset($stat['ino'])) { + $toHash .= $stat['ino']; + } + if (isset($stat['dev'])) { + $toHash .= $stat['dev']; + } + if (isset($stat['size'])) { + $toHash .= $stat['size']; + } + + return md5($toHash); } else { return parent::getETag($path); } diff --git a/lib/private/Files/Storage/Wrapper/Availability.php b/lib/private/Files/Storage/Wrapper/Availability.php index 5b957ae036b..0c01595d306 100644 --- a/lib/private/Files/Storage/Wrapper/Availability.php +++ b/lib/private/Files/Storage/Wrapper/Availability.php @@ -21,9 +21,13 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Files\Storage\Wrapper; use OCP\Files\Storage\IStorage; +use OCP\Files\StorageAuthException; +use OCP\Files\StorageNotAvailableException; +use OCP\IConfig; /** * Availability checker for storages @@ -33,6 +37,14 @@ use OCP\Files\Storage\IStorage; class Availability extends Wrapper { const RECHECK_TTL_SEC = 600; // 10 minutes + /** @var IConfig */ + protected $config; + + public function __construct($parameters) { + $this->config = $parameters['config'] ?? \OC::$server->getConfig(); + parent::__construct($parameters); + } + public static function shouldRecheck($availability) { if (!$availability['available']) { // trigger a recheck if TTL reached @@ -72,11 +84,11 @@ class Availability extends Wrapper { } /** - * @throws \OCP\Files\StorageNotAvailableException + * @throws StorageNotAvailableException */ private function checkAvailability() { if (!$this->isAvailable()) { - throw new \OCP\Files\StorageNotAvailableException(); + throw new StorageNotAvailableException(); } } @@ -85,9 +97,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::mkdir($path); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -96,9 +107,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::rmdir($path); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -107,9 +117,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::opendir($path); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -118,9 +127,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::is_dir($path); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -129,9 +137,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::is_file($path); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -140,9 +147,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::stat($path); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -151,9 +157,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::filetype($path); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -162,9 +167,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::filesize($path); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -173,9 +177,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::isCreatable($path); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -184,9 +187,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::isReadable($path); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -195,9 +197,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::isUpdatable($path); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -206,9 +207,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::isDeletable($path); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -217,9 +217,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::isSharable($path); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -228,9 +227,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::getPermissions($path); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -242,9 +240,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::file_exists($path); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -253,9 +250,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::filemtime($path); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -264,9 +260,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::file_get_contents($path); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -275,9 +270,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::file_put_contents($path, $data); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -286,9 +280,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::unlink($path); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -297,9 +290,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::rename($path1, $path2); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -308,9 +300,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::copy($path1, $path2); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -319,9 +310,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::fopen($path, $mode); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -330,9 +320,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::getMimeType($path); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -341,9 +330,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::hash($type, $path, $raw); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -352,9 +340,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::free_space($path); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -363,9 +350,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::search($query); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -374,9 +360,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::touch($path, $mtime); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -385,9 +370,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::getLocalFile($path); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -396,9 +380,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::hasUpdated($path, $time); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -406,9 +389,8 @@ class Availability extends Wrapper { public function getOwner($path) { try { return parent::getOwner($path); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -417,9 +399,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::getETag($path); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -428,9 +409,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::getDirectDownload($path); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -439,9 +419,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::copyFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -450,9 +429,8 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::moveFromStorage($sourceStorage, $sourceInternalPath, $targetInternalPath); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } @@ -461,9 +439,24 @@ class Availability extends Wrapper { $this->checkAvailability(); try { return parent::getMetaData($path); - } catch (\OCP\Files\StorageNotAvailableException $e) { - $this->setAvailability(false); - throw $e; + } catch (StorageNotAvailableException $e) { + $this->setUnavailable($e); } } + + /** + * @throws StorageNotAvailableException + */ + protected function setUnavailable(StorageNotAvailableException $e) { + $delay = self::RECHECK_TTL_SEC; + if($e instanceof StorageAuthException) { + $delay = max( + // 30min + $this->config->getSystemValueInt('external_storage.auth_availability_delay', 1800), + self::RECHECK_TTL_SEC + ); + } + $this->getStorageCache()->setAvailability(false, $delay); + throw $e; + } } diff --git a/lib/private/Files/Storage/Wrapper/Encoding.php b/lib/private/Files/Storage/Wrapper/Encoding.php index bc8519bf42c..f143c8b8c92 100644 --- a/lib/private/Files/Storage/Wrapper/Encoding.php +++ b/lib/private/Files/Storage/Wrapper/Encoding.php @@ -23,9 +23,9 @@ namespace OC\Files\Storage\Wrapper; +use OC\Cache\CappedMemoryCache; use OCP\Files\Storage\IStorage; use OCP\ICache; -use OC\Cache\CappedMemoryCache; /** * Encoding wrapper that deals with file names that use unsupported encodings like NFD. diff --git a/lib/private/Files/Storage/Wrapper/Encryption.php b/lib/private/Files/Storage/Wrapper/Encryption.php index 5485b80985b..7741f820c63 100644 --- a/lib/private/Files/Storage/Wrapper/Encryption.php +++ b/lib/private/Files/Storage/Wrapper/Encryption.php @@ -41,10 +41,10 @@ use OCP\Encryption\Exceptions\GenericEncryptionException; use OCP\Encryption\IFile; use OCP\Encryption\IManager; use OCP\Encryption\Keys\IStorage; +use OCP\Files\Cache\ICacheEntry; use OCP\Files\Mount\IMountPoint; use OCP\Files\Storage; use OCP\ILogger; -use OCP\Files\Cache\ICacheEntry; class Encryption extends Wrapper { diff --git a/lib/private/Files/Storage/Wrapper/Jail.php b/lib/private/Files/Storage/Wrapper/Jail.php index f21b5716467..35bf8449fd7 100644 --- a/lib/private/Files/Storage/Wrapper/Jail.php +++ b/lib/private/Files/Storage/Wrapper/Jail.php @@ -62,10 +62,18 @@ class Jail extends Wrapper { } } + /** + * This is separate from Wrapper::getWrapperStorage so we can get the jailed storage consistently even if the jail is inside another wrapper + */ + public function getUnjailedStorage() { + return $this->storage; + } + + public function getJailedPath($path) { $root = rtrim($this->rootPath, '/') . '/'; - if (strpos($path, $root) !== 0) { + if ($path !== $this->rootPath && strpos($path, $root) !== 0) { return null; } else { $path = substr($path, strlen($this->rootPath)); diff --git a/lib/private/Files/Type/Loader.php b/lib/private/Files/Type/Loader.php index d1419dca4dc..46588a74d0d 100644 --- a/lib/private/Files/Type/Loader.php +++ b/lib/private/Files/Type/Loader.php @@ -25,9 +25,9 @@ namespace OC\Files\Type; +use Doctrine\DBAL\Exception\UniqueConstraintViolationException; use OCP\Files\IMimeTypeLoader; use OCP\IDBConnection; -use Doctrine\DBAL\Exception\UniqueConstraintViolationException; /** * Mimetype database loader diff --git a/lib/private/Files/Type/TemplateManager.php b/lib/private/Files/Type/TemplateManager.php index 8b30eb6b4e8..08c4593bf87 100644 --- a/lib/private/Files/Type/TemplateManager.php +++ b/lib/private/Files/Type/TemplateManager.php @@ -24,6 +24,9 @@ namespace OC\Files\Type; +/** + * @deprecated 18.0.0 + */ class TemplateManager { protected $templates = array(); @@ -34,6 +37,7 @@ class TemplateManager { /** * get the path of the template for a mimetype * + * @deprecated 18.0.0 * @param string $mimetype * @return string|null */ @@ -48,6 +52,7 @@ class TemplateManager { /** * get the template content for a mimetype * + * @deprecated 18.0.0 * @param string $mimetype * @return string */ diff --git a/lib/private/Files/Utils/Scanner.php b/lib/private/Files/Utils/Scanner.php index 28921973fcf..8dee7bf7423 100644 --- a/lib/private/Files/Utils/Scanner.php +++ b/lib/private/Files/Utils/Scanner.php @@ -30,6 +30,7 @@ namespace OC\Files\Utils; use OC\Files\Cache\Cache; use OC\Files\Filesystem; +use OC\Files\Storage\FailedStorage; use OC\ForbiddenException; use OC\Hooks\PublicEmitter; use OC\Lock\DBLockingProvider; @@ -38,7 +39,6 @@ use OCP\Files\NotFoundException; use OCP\Files\Storage\IStorage; use OCP\Files\StorageNotAvailableException; use OCP\ILogger; -use OC\Files\Storage\FailedStorage; /** * Class Scanner @@ -279,4 +279,3 @@ class Scanner extends PublicEmitter { } } } - diff --git a/lib/private/Files/View.php b/lib/private/Files/View.php index 8322576c5c2..ed962bde1a4 100644 --- a/lib/private/Files/View.php +++ b/lib/private/Files/View.php @@ -42,7 +42,6 @@ * */ - namespace OC\Files; use Icewind\Streams\CallbackWrapper; @@ -590,6 +589,7 @@ class View { /** * @param string $path * @return mixed + * @throws LockedException */ public function file_get_contents($path) { return $this->basicOperation('file_get_contents', $path, array('read')); @@ -641,7 +641,7 @@ class View { * @param string $path * @param string|resource $data * @return bool|mixed - * @throws \Exception + * @throws LockedException */ public function file_put_contents($path, $data) { if (is_resource($data)) { //not having to deal with streams in file_put_contents makes life easier @@ -740,6 +740,7 @@ class View { * @param string $path2 target path * * @return bool|mixed + * @throws LockedException */ public function rename($path1, $path2) { $absolutePath1 = Filesystem::normalizePath($this->getAbsolutePath($path1)); @@ -963,6 +964,7 @@ class View { * @param string $path * @param string $mode 'r' or 'w' * @return resource + * @throws LockedException */ public function fopen($path, $mode) { $mode = str_replace('b', '', $mode); // the binary flag is a windows only feature which we do not support @@ -1118,7 +1120,7 @@ class View { * @param array $hooks (optional) * @param mixed $extraParam (optional) * @return mixed - * @throws \Exception + * @throws LockedException * * This method takes requests for basic filesystem functions (e.g. reading & writing * files), processes hooks and proxies, sanitises paths, and finally passes them on to @@ -1920,7 +1922,7 @@ class View { * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage * * @return bool False if the path is excluded from locking, true otherwise - * @throws \OCP\Lock\LockedException if the path is already locked + * @throws LockedException if the path is already locked */ private function lockPath($path, $type, $lockMountPoint = false) { $absolutePath = $this->getAbsolutePath($path); @@ -1940,9 +1942,9 @@ class View { $this->lockingProvider ); } - } catch (\OCP\Lock\LockedException $e) { + } catch (LockedException $e) { // rethrow with the a human-readable path - throw new \OCP\Lock\LockedException( + throw new LockedException( $this->getPathRelativeToFiles($absolutePath), $e ); @@ -1960,7 +1962,7 @@ class View { * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage * * @return bool False if the path is excluded from locking, true otherwise - * @throws \OCP\Lock\LockedException if the path is already locked + * @throws LockedException if the path is already locked */ public function changeLock($path, $type, $lockMountPoint = false) { $path = Filesystem::normalizePath($path); @@ -1981,15 +1983,15 @@ class View { $this->lockingProvider ); } - } catch (\OCP\Lock\LockedException $e) { + } catch (LockedException $e) { try { // rethrow with the a human-readable path - throw new \OCP\Lock\LockedException( + throw new LockedException( $this->getPathRelativeToFiles($absolutePath), $e ); } catch (\InvalidArgumentException $e) { - throw new \OCP\Lock\LockedException( + throw new LockedException( $absolutePath, $e ); @@ -2008,6 +2010,7 @@ class View { * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage * * @return bool False if the path is excluded from locking, true otherwise + * @throws LockedException */ private function unlockPath($path, $type, $lockMountPoint = false) { $absolutePath = $this->getAbsolutePath($path); @@ -2039,6 +2042,7 @@ class View { * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage * * @return bool False if the path is excluded from locking, true otherwise + * @throws LockedException */ public function lockFile($path, $type, $lockMountPoint = false) { $absolutePath = $this->getAbsolutePath($path); @@ -2065,6 +2069,7 @@ class View { * @param bool $lockMountPoint true to lock the mount point, false to lock the attached mount/storage * * @return bool False if the path is excluded from locking, true otherwise + * @throws LockedException */ public function unlockFile($path, $type, $lockMountPoint = false) { $absolutePath = $this->getAbsolutePath($path); diff --git a/lib/private/FullTextSearch/FullTextSearchManager.php b/lib/private/FullTextSearch/FullTextSearchManager.php index 8ab1af05b0e..3aec4638a21 100644 --- a/lib/private/FullTextSearch/FullTextSearchManager.php +++ b/lib/private/FullTextSearch/FullTextSearchManager.php @@ -27,7 +27,6 @@ declare(strict_types=1); * */ - namespace OC\FullTextSearch; @@ -39,7 +38,6 @@ use OCP\FullTextSearch\Service\IIndexService; use OCP\FullTextSearch\Service\IProviderService; use OCP\FullTextSearch\Service\ISearchService; - /** * Class FullTextSearchManager * @@ -239,4 +237,3 @@ class FullTextSearchManager implements IFullTextSearchManager { } - diff --git a/lib/private/FullTextSearch/Model/DocumentAccess.php b/lib/private/FullTextSearch/Model/DocumentAccess.php index 088bf075ae6..ce195eb8a2e 100644 --- a/lib/private/FullTextSearch/Model/DocumentAccess.php +++ b/lib/private/FullTextSearch/Model/DocumentAccess.php @@ -27,14 +27,12 @@ declare(strict_types=1); * */ - namespace OC\FullTextSearch\Model; use JsonSerializable; use OCP\FullTextSearch\Model\IDocumentAccess; - /** * Class IDocumentAccess * @@ -362,4 +360,3 @@ final class DocumentAccess implements IDocumentAccess, JsonSerializable { ]; } } - diff --git a/lib/private/FullTextSearch/Model/IndexDocument.php b/lib/private/FullTextSearch/Model/IndexDocument.php index d5bef906021..1efa55b5ca1 100644 --- a/lib/private/FullTextSearch/Model/IndexDocument.php +++ b/lib/private/FullTextSearch/Model/IndexDocument.php @@ -27,7 +27,6 @@ declare(strict_types=1); * */ - namespace OC\FullTextSearch\Model; @@ -36,7 +35,6 @@ use OCP\FullTextSearch\Model\IDocumentAccess; use OCP\FullTextSearch\Model\IIndex; use OCP\FullTextSearch\Model\IIndexDocument; - /** * Class IndexDocument * @@ -997,4 +995,3 @@ class IndexDocument implements IIndexDocument, JsonSerializable { } } - diff --git a/lib/private/FullTextSearch/Model/SearchOption.php b/lib/private/FullTextSearch/Model/SearchOption.php index 6a67c9fe7df..d0eb0eab2ed 100644 --- a/lib/private/FullTextSearch/Model/SearchOption.php +++ b/lib/private/FullTextSearch/Model/SearchOption.php @@ -27,14 +27,12 @@ declare(strict_types=1); * */ - namespace OC\FullTextSearch\Model; use JsonSerializable; use OCP\FullTextSearch\Model\ISearchOption; - /** * @since 15.0.0 * diff --git a/lib/private/FullTextSearch/Model/SearchRequestSimpleQuery.php b/lib/private/FullTextSearch/Model/SearchRequestSimpleQuery.php index a01fb232916..8796f28a249 100644 --- a/lib/private/FullTextSearch/Model/SearchRequestSimpleQuery.php +++ b/lib/private/FullTextSearch/Model/SearchRequestSimpleQuery.php @@ -27,14 +27,12 @@ declare(strict_types=1); * */ - namespace OC\FullTextSearch\Model; use JsonSerializable; use OCP\FullTextSearch\Model\ISearchRequestSimpleQuery; - /** * @since 17.0.0 * diff --git a/lib/private/FullTextSearch/Model/SearchTemplate.php b/lib/private/FullTextSearch/Model/SearchTemplate.php index 22333e712ce..a4c21d30bcc 100644 --- a/lib/private/FullTextSearch/Model/SearchTemplate.php +++ b/lib/private/FullTextSearch/Model/SearchTemplate.php @@ -27,7 +27,6 @@ declare(strict_types=1); * */ - namespace OC\FullTextSearch\Model; @@ -36,7 +35,6 @@ use OCP\FullTextSearch\IFullTextSearchProvider; use OCP\FullTextSearch\Model\ISearchOption; use OCP\FullTextSearch\Model\ISearchTemplate; - /** * Class ISearchTemplate * @@ -257,4 +255,3 @@ final class SearchTemplate implements ISearchTemplate, JsonSerializable { ]; } } - diff --git a/lib/private/GlobalScale/Config.php b/lib/private/GlobalScale/Config.php index da7a459741b..2ab862a0e4e 100644 --- a/lib/private/GlobalScale/Config.php +++ b/lib/private/GlobalScale/Config.php @@ -21,7 +21,6 @@ * */ - namespace OC\GlobalScale; diff --git a/lib/private/Group/Database.php b/lib/private/Group/Database.php index 7a5728b957d..7028b1fa81b 100644 --- a/lib/private/Group/Database.php +++ b/lib/private/Group/Database.php @@ -49,7 +49,10 @@ use OCP\Group\Backend\ICountDisabledInGroup; use OCP\Group\Backend\ICountUsersBackend; use OCP\Group\Backend\ICreateGroupBackend; use OCP\Group\Backend\IDeleteGroupBackend; +use OCP\Group\Backend\IGetDisplayNameBackend; +use OCP\Group\Backend\IGroupDetailsBackend; use OCP\Group\Backend\IRemoveFromGroupBackend; +use OCP\Group\Backend\ISetDisplayNameBackend; use OCP\IDBConnection; /** @@ -61,7 +64,10 @@ class Database extends ABackend ICountUsersBackend, ICreateGroupBackend, IDeleteGroupBackend, - IRemoveFromGroupBackend { + IGetDisplayNameBackend, + IGroupDetailsBackend, + IRemoveFromGroupBackend, + ISetDisplayNameBackend { /** @var string[] */ private $groupCache = []; @@ -103,6 +109,7 @@ class Database extends ABackend $builder = $this->dbConn->getQueryBuilder(); $result = $builder->insert('groups') ->setValue('gid', $builder->createNamedParameter($gid)) + ->setValue('displayname', $builder->createNamedParameter($gid)) ->execute(); } catch(UniqueConstraintViolationException $e) { $result = 0; @@ -322,7 +329,7 @@ class Database extends ABackend * @param int $offset * @return array an array of user ids */ - public function usersInGroup($gid, $search = '', $limit = null, $offset = null) { + public function usersInGroup($gid, $search = '', $limit = -1, $offset = 0) { $this->fixDI(); $query = $this->dbConn->getQueryBuilder(); @@ -337,8 +344,13 @@ class Database extends ABackend ))); } - $query->setMaxResults($limit) - ->setFirstResult($offset); + if ($limit !== -1) { + $query->setMaxResults($limit); + } + if ($offset !== 0) { + $query->setFirstResult($offset); + } + $result = $query->execute(); $users = []; @@ -391,7 +403,7 @@ class Database extends ABackend */ public function countDisabledInGroup(string $gid): int { $this->fixDI(); - + $query = $this->dbConn->getQueryBuilder(); $query->select($query->createFunction('COUNT(DISTINCT ' . $query->getColumnName('uid') . ')')) ->from('preferences', 'p') @@ -400,11 +412,11 @@ class Database extends ABackend ->andWhere($query->expr()->eq('configkey', $query->createNamedParameter('enabled'))) ->andWhere($query->expr()->eq('configvalue', $query->createNamedParameter('false'), IQueryBuilder::PARAM_STR)) ->andWhere($query->expr()->eq('gid', $query->createNamedParameter($gid), IQueryBuilder::PARAM_STR)); - + $result = $query->execute(); $count = $result->fetchColumn(); $result->closeCursor(); - + if ($count !== false) { $count = (int)$count; } else { @@ -414,4 +426,49 @@ class Database extends ABackend return $count; } + public function getDisplayName(string $gid): string { + $this->fixDI(); + + $query = $this->dbConn->getQueryBuilder(); + $query->select('displayname') + ->from('groups') + ->where($query->expr()->eq('gid', $query->createNamedParameter($gid))); + + $result = $query->execute(); + $displayName = $result->fetchColumn(); + $result->closeCursor(); + + return (string) $displayName; + } + + public function getGroupDetails(string $gid): array { + $displayName = $this->getDisplayName($gid); + if ($displayName !== '') { + return ['displayName' => $displayName]; + } + + return []; + } + + public function setDisplayName(string $gid, string $displayName): bool { + if (!$this->groupExists($gid)) { + return false; + } + + $this->fixDI(); + + $displayName = trim($displayName); + if ($displayName === '') { + $displayName = $gid; + } + + $query = $this->dbConn->getQueryBuilder(); + $query->update('groups') + ->set('displayname', $query->createNamedParameter($displayName)) + ->where($query->expr()->eq('gid', $query->createNamedParameter($gid))); + $query->execute(); + + return true; + } + } diff --git a/lib/private/Group/Group.php b/lib/private/Group/Group.php index a50a5ffde78..fa14e4e9932 100644 --- a/lib/private/Group/Group.php +++ b/lib/private/Group/Group.php @@ -30,13 +30,14 @@ namespace OC\Group; +use OC\Hooks\PublicEmitter; +use OCP\Group\Backend\ICountDisabledInGroup; use OCP\Group\Backend\IGetDisplayNameBackend; use OCP\Group\Backend\IHideFromCollaborationBackend; -use OC\Hooks\PublicEmitter; +use OCP\Group\Backend\ISetDisplayNameBackend; use OCP\GroupInterface; use OCP\IGroup; use OCP\IUser; -use OCP\Group\Backend\ICountDisabledInGroup; use OCP\IUserManager; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\GenericEvent; @@ -101,6 +102,20 @@ class Group implements IGroup { return $this->displayName; } + public function setDisplayName(string $displayName): bool { + $displayName = trim($displayName); + if ($displayName !== '') { + foreach ($this->backends as $backend) { + if (($backend instanceof ISetDisplayNameBackend) + && $backend->setDisplayName($this->gid, $displayName)) { + $this->displayName = $displayName; + return true; + } + } + } + return false; + } + /** * get all users in the group * diff --git a/lib/private/Group/Manager.php b/lib/private/Group/Manager.php index 7567f719b0a..dc4a7fbd11c 100644 --- a/lib/private/Group/Manager.php +++ b/lib/private/Group/Manager.php @@ -93,8 +93,8 @@ class Manager extends PublicEmitter implements IGroupManager { $this->dispatcher = $dispatcher; $this->logger = $logger; - $cachedGroups = & $this->cachedGroups; - $cachedUserGroups = & $this->cachedUserGroups; + $cachedGroups = &$this->cachedGroups; + $cachedUserGroups = &$this->cachedUserGroups; $this->listen('\OC\Group', 'postDelete', function ($group) use (&$cachedGroups, &$cachedUserGroups) { /** * @var \OC\Group\Group $group @@ -149,6 +149,7 @@ class Manager extends PublicEmitter implements IGroupManager { /** * Get the active backends + * * @return \OCP\GroupInterface[] */ public function getBackends() { @@ -163,7 +164,7 @@ class Manager extends PublicEmitter implements IGroupManager { /** * @param string $gid - * @return \OC\Group\Group + * @return IGroup|null */ public function get($gid) { if (isset($this->cachedGroups[$gid])) { @@ -175,12 +176,12 @@ class Manager extends PublicEmitter implements IGroupManager { /** * @param string $gid * @param string $displayName - * @return \OCP\IGroup + * @return \OCP\IGroup|null */ protected function getGroupObject($gid, $displayName = null) { $backends = []; foreach ($this->backends as $backend) { - if ($backend->implementsActions(\OC\Group\Backend::GROUP_DETAILS)) { + if ($backend->implementsActions(Backend::GROUP_DETAILS)) { $groupData = $backend->getGroupDetails($gid); if (is_array($groupData) && !empty($groupData)) { // take the display name from the first backend that has a non-null one @@ -210,21 +211,22 @@ class Manager extends PublicEmitter implements IGroupManager { /** * @param string $gid - * @return \OC\Group\Group + * @return IGroup|null */ public function createGroup($gid) { if ($gid === '' || $gid === null) { - return false; + return null; } else if ($group = $this->get($gid)) { return $group; } else { - $this->emit('\OC\Group', 'preCreate', array($gid)); + $this->emit('\OC\Group', 'preCreate', [$gid]); foreach ($this->backends as $backend) { - if ($backend->implementsActions(\OC\Group\Backend::CREATE_GROUP)) { - $backend->createGroup($gid); - $group = $this->getGroupObject($gid); - $this->emit('\OC\Group', 'postCreate', array($group)); - return $group; + if ($backend->implementsActions(Backend::CREATE_GROUP)) { + if ($backend->createGroup($gid)) { + $group = $this->getGroupObject($gid); + $this->emit('\OC\Group', 'postCreate', [$group]); + return $group; + } } } return null; @@ -260,7 +262,7 @@ class Manager extends PublicEmitter implements IGroupManager { * @param IUser|null $user * @return \OC\Group\Group[] */ - public function getUserGroups(IUser $user= null) { + public function getUserGroups(IUser $user = null) { if (!$user instanceof IUser) { return []; } @@ -295,12 +297,13 @@ class Manager extends PublicEmitter implements IGroupManager { /** * Checks if a userId is in the admin group + * * @param string $userId * @return bool if admin */ public function isAdmin($userId) { foreach ($this->backends as $backend) { - if ($backend->implementsActions(\OC\Group\Backend::IS_ADMIN) && $backend->isAdmin($userId)) { + if ($backend->implementsActions(Backend::IS_ADMIN) && $backend->isAdmin($userId)) { return true; } } @@ -309,6 +312,7 @@ class Manager extends PublicEmitter implements IGroupManager { /** * Checks if a userId is in a group + * * @param string $userId * @param string $group * @return bool if in group @@ -319,28 +323,31 @@ class Manager extends PublicEmitter implements IGroupManager { /** * get a list of group ids for a user + * * @param IUser $user * @return array with group ids */ public function getUserGroupIds(IUser $user) { - return array_map(function($value) { - return (string) $value; + return array_map(function ($value) { + return (string)$value; }, array_keys($this->getUserGroups($user))); } /** * get an array of groupid and displayName for a user + * * @param IUser $user * @return array ['displayName' => displayname] */ public function getUserGroupNames(IUser $user) { - return array_map(function($group) { + return array_map(function ($group) { return array('displayName' => $group->getDisplayName()); }, $this->getUserGroups($user)); } /** * get a list of all display names in a group + * * @param string $gid * @param string $search * @param int $limit @@ -349,32 +356,32 @@ class Manager extends PublicEmitter implements IGroupManager { */ public function displayNamesInGroup($gid, $search = '', $limit = -1, $offset = 0) { $group = $this->get($gid); - if(is_null($group)) { + if (is_null($group)) { return []; } $search = trim($search); $groupUsers = []; - if(!empty($search)) { + if (!empty($search)) { // only user backends have the capability to do a complex search for users $searchOffset = 0; $searchLimit = $limit * 100; - if($limit === -1) { + if ($limit === -1) { $searchLimit = 500; } do { $filteredUsers = $this->userManager->searchDisplayName($search, $searchLimit, $searchOffset); - foreach($filteredUsers as $filteredUser) { - if($group->inGroup($filteredUser)) { - $groupUsers[]= $filteredUser; + foreach ($filteredUsers as $filteredUser) { + if ($group->inGroup($filteredUser)) { + $groupUsers[] = $filteredUser; } } $searchOffset += $searchLimit; - } while(count($groupUsers) < $searchLimit+$offset && count($filteredUsers) >= $searchLimit); + } while (count($groupUsers) < $searchLimit + $offset && count($filteredUsers) >= $searchLimit); - if($limit === -1) { + if ($limit === -1) { $groupUsers = array_slice($groupUsers, $offset); } else { $groupUsers = array_slice($groupUsers, $offset, $limit); @@ -384,8 +391,8 @@ class Manager extends PublicEmitter implements IGroupManager { } $matchingUsers = []; - foreach($groupUsers as $groupUser) { - $matchingUsers[$groupUser->getUID()] = $groupUser->getDisplayName(); + foreach ($groupUsers as $groupUser) { + $matchingUsers[(string) $groupUser->getUID()] = $groupUser->getDisplayName(); } return $matchingUsers; } diff --git a/lib/private/Group/MetaData.php b/lib/private/Group/MetaData.php index f4877237ec7..cda208f3f58 100644 --- a/lib/private/Group/MetaData.php +++ b/lib/private/Group/MetaData.php @@ -28,8 +28,8 @@ namespace OC\Group; -use OCP\IUserSession; use OCP\IGroupManager; +use OCP\IUserSession; class MetaData { const SORT_NONE = 0; diff --git a/lib/private/Http/Client/Client.php b/lib/private/Http/Client/Client.php index 993b83917fd..28694f38585 100644 --- a/lib/private/Http/Client/Client.php +++ b/lib/private/Http/Client/Client.php @@ -63,6 +63,7 @@ class Client implements IClient { $defaults = [ RequestOptions::PROXY => $this->getProxyUri(), RequestOptions::VERIFY => $this->getCertBundle(), + RequestOptions::TIMEOUT => 30, ]; $options = array_merge($defaults, $options); diff --git a/lib/private/L10N/Factory.php b/lib/private/L10N/Factory.php index 80f12c0a832..2e1652e9a1b 100644 --- a/lib/private/L10N/Factory.php +++ b/lib/private/L10N/Factory.php @@ -469,7 +469,6 @@ class Factory implements IFactory { if (($this->isSubDirectory($transFile, $this->serverRoot . '/core/l10n/') || $this->isSubDirectory($transFile, $this->serverRoot . '/lib/l10n/') - || $this->isSubDirectory($transFile, $this->serverRoot . '/settings/l10n/') || $this->isSubDirectory($transFile, \OC_App::getAppPath($app) . '/l10n/') ) && file_exists($transFile)) { @@ -496,7 +495,7 @@ class Factory implements IFactory { * @return string directory */ protected function findL10nDir($app = null) { - if (in_array($app, ['core', 'lib', 'settings'])) { + if (in_array($app, ['core', 'lib'])) { if (file_exists($this->serverRoot . '/' . $app . '/l10n/')) { return $this->serverRoot . '/' . $app . '/l10n/'; } diff --git a/lib/private/L10N/LanguageNotFoundException.php b/lib/private/L10N/LanguageNotFoundException.php index 317e0629b60..0f3b8140e08 100644 --- a/lib/private/L10N/LanguageNotFoundException.php +++ b/lib/private/L10N/LanguageNotFoundException.php @@ -20,6 +20,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ + namespace OC\L10N; class LanguageNotFoundException extends \Exception { diff --git a/lib/private/Lock/MemcacheLockingProvider.php b/lib/private/Lock/MemcacheLockingProvider.php index 4d1b3dc0bca..368c672dbb2 100644 --- a/lib/private/Lock/MemcacheLockingProvider.php +++ b/lib/private/Lock/MemcacheLockingProvider.php @@ -23,9 +23,9 @@ declare(strict_types=1); namespace OC\Lock; +use OCP\IMemcache; use OCP\IMemcacheTTL; use OCP\Lock\LockedException; -use OCP\IMemcache; class MemcacheLockingProvider extends AbstractLockingProvider { /** diff --git a/lib/private/Log.php b/lib/private/Log.php index 4af833d778f..22792fe4020 100644 --- a/lib/private/Log.php +++ b/lib/private/Log.php @@ -39,9 +39,9 @@ use function array_merge; use InterfaSys\LogNormalizer\Normalizer; use OC\Log\ExceptionSerializer; +use OCP\ILogger; use OCP\Log\IFileBased; use OCP\Log\IWriter; -use OCP\ILogger; use OCP\Support\CrashReport\IRegistry; /** @@ -214,24 +214,27 @@ class Log implements ILogger { } $message = strtr($message, $replace); - if ($level >= $minLevel) { - $this->writeLog($app, $message, $level); - - if ($this->crashReporters !== null) { - $messageContext = array_merge( - $context, - [ - 'level' => $level - ] - ); - $this->crashReporters->delegateMessage($message, $messageContext); - } - } else { - if ($this->crashReporters !== null) { - $this->crashReporters->delegateBreadcrumb($message, 'log', $context); + try { + if ($level >= $minLevel) { + $this->writeLog($app, $message, $level); + + if ($this->crashReporters !== null) { + $messageContext = array_merge( + $context, + [ + 'level' => $level + ] + ); + $this->crashReporters->delegateMessage($message, $messageContext); + } + } else { + if ($this->crashReporters !== null) { + $this->crashReporters->delegateBreadcrumb($message, 'log', $context); + } } + } catch (\Throwable $e) { + // make sure we dont hard crash if logging fails } - } private function getLogLevel($context) { @@ -318,16 +321,20 @@ class Log implements ILogger { array_walk($context, [$this->normalizer, 'format']); - if ($level >= $minLevel) { - if (!$this->logger instanceof IFileBased) { - $data = json_encode($data, JSON_PARTIAL_OUTPUT_ON_ERROR); + try { + if ($level >= $minLevel) { + if (!$this->logger instanceof IFileBased) { + $data = json_encode($data, JSON_PARTIAL_OUTPUT_ON_ERROR | JSON_UNESCAPED_SLASHES); + } + $this->writeLog($app, $data, $level); } - $this->writeLog($app, $data, $level); - } - $context['level'] = $level; - if (!is_null($this->crashReporters)) { - $this->crashReporters->delegateReport($exception, $context); + $context['level'] = $level; + if (!is_null($this->crashReporters)) { + $this->crashReporters->delegateReport($exception, $context); + } + } catch (\Throwable $e) { + // make sure we dont hard crash if logging fails } } diff --git a/lib/private/Log/Errorlog.php b/lib/private/Log/Errorlog.php index 9dc8b2cc49c..ebcb73be4ce 100644 --- a/lib/private/Log/Errorlog.php +++ b/lib/private/Log/Errorlog.php @@ -39,4 +39,3 @@ class Errorlog implements IWriter { error_log('[owncloud]['.$app.']['.$level.'] '.$message); } } - diff --git a/lib/private/Log/ExceptionSerializer.php b/lib/private/Log/ExceptionSerializer.php index 8cfdb57b225..a3b855aea26 100644 --- a/lib/private/Log/ExceptionSerializer.php +++ b/lib/private/Log/ExceptionSerializer.php @@ -92,7 +92,9 @@ class ExceptionSerializer { ]; private function editTrace(array &$sensitiveValues, array $traceLine): array { - $sensitiveValues = array_merge($sensitiveValues, $traceLine['args']); + if (isset($traceLine['args'])) { + $sensitiveValues = array_merge($sensitiveValues, $traceLine['args']); + } $traceLine['args'] = ['*** sensitive parameters replaced ***']; return $traceLine; } diff --git a/lib/private/Log/File.php b/lib/private/Log/File.php index fe2bbf30a26..b982836b8ef 100644 --- a/lib/private/Log/File.php +++ b/lib/private/Log/File.php @@ -37,9 +37,9 @@ namespace OC\Log; use OC\SystemConfig; +use OCP\ILogger; use OCP\Log\IFileBased; use OCP\Log\IWriter; -use OCP\ILogger; /** * logging utilities diff --git a/lib/private/Log/LogDetails.php b/lib/private/Log/LogDetails.php index 712b5403ca0..e833221e222 100644 --- a/lib/private/Log/LogDetails.php +++ b/lib/private/Log/LogDetails.php @@ -90,12 +90,12 @@ abstract class LogDetails { // them manually. foreach($entry as $key => $value) { if(is_string($value)) { - $testEncode = json_encode($value); + $testEncode = json_encode($value, JSON_UNESCAPED_SLASHES); if($testEncode === false) { $entry[$key] = utf8_encode($value); } } } - return json_encode($entry, JSON_PARTIAL_OUTPUT_ON_ERROR); + return json_encode($entry, JSON_PARTIAL_OUTPUT_ON_ERROR | JSON_UNESCAPED_SLASHES); } } diff --git a/lib/private/Log/Systemdlog.php b/lib/private/Log/Systemdlog.php index c40e4860f95..1fd0a6a419b 100644 --- a/lib/private/Log/Systemdlog.php +++ b/lib/private/Log/Systemdlog.php @@ -24,8 +24,8 @@ namespace OC\Log; use OC\HintException; use OC\SystemConfig; -use OCP\ILogger; use OCP\IConfig; +use OCP\ILogger; use OCP\Log\IWriter; // The following fields are understood by systemd/journald, see diff --git a/lib/private/Mail/Mailer.php b/lib/private/Mail/Mailer.php index d103e1380c5..e3547c84da3 100644 --- a/lib/private/Mail/Mailer.php +++ b/lib/private/Mail/Mailer.php @@ -31,11 +31,11 @@ use Egulias\EmailValidator\Validation\RFCValidation; use OCP\Defaults; use OCP\IConfig; use OCP\IL10N; +use OCP\ILogger; use OCP\IURLGenerator; use OCP\Mail\IAttachment; use OCP\Mail\IEMailTemplate; use OCP\Mail\IMailer; -use OCP\ILogger; use OCP\Mail\IMessage; /** diff --git a/lib/private/Memcache/Redis.php b/lib/private/Memcache/Redis.php index adf3c5050bd..8d0d1679e80 100644 --- a/lib/private/Memcache/Redis.php +++ b/lib/private/Memcache/Redis.php @@ -178,4 +178,3 @@ class Redis extends Cache implements IMemcacheTTL { return \OC::$server->getGetRedisFactory()->isAvailable(); } } - diff --git a/lib/private/Migration/BackgroundRepair.php b/lib/private/Migration/BackgroundRepair.php index 7ef90301cf3..7cab14f5ccc 100644 --- a/lib/private/Migration/BackgroundRepair.php +++ b/lib/private/Migration/BackgroundRepair.php @@ -20,6 +20,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Migration; use OC\BackgroundJob\JobList; diff --git a/lib/private/Migration/ConsoleOutput.php b/lib/private/Migration/ConsoleOutput.php index 74c8c7340cc..d3487a771cc 100644 --- a/lib/private/Migration/ConsoleOutput.php +++ b/lib/private/Migration/ConsoleOutput.php @@ -20,7 +20,6 @@ * */ - namespace OC\Migration; diff --git a/lib/private/Migration/SimpleOutput.php b/lib/private/Migration/SimpleOutput.php index ea61c44fcac..c3426a426f8 100644 --- a/lib/private/Migration/SimpleOutput.php +++ b/lib/private/Migration/SimpleOutput.php @@ -20,7 +20,6 @@ * */ - namespace OC\Migration; diff --git a/lib/private/NavigationManager.php b/lib/private/NavigationManager.php index 2c41fbd88c2..2f6befc632c 100644 --- a/lib/private/NavigationManager.php +++ b/lib/private/NavigationManager.php @@ -199,7 +199,7 @@ class NavigationManager implements INavigationManager { 'type' => 'settings', 'id' => 'help', 'order' => 5, - 'href' => $this->urlGenerator->linkToRoute('settings_help'), + 'href' => $this->urlGenerator->linkToRoute('settings.Help.help'), 'name' => $l->t('Help'), 'icon' => $this->urlGenerator->imagePath('settings', 'help.svg'), ]); diff --git a/lib/private/OCS/DiscoveryService.php b/lib/private/OCS/DiscoveryService.php index 6662263fb76..f084dae35ed 100644 --- a/lib/private/OCS/DiscoveryService.php +++ b/lib/private/OCS/DiscoveryService.php @@ -22,7 +22,6 @@ declare(strict_types=1); * */ - namespace OC\OCS; use OCP\AppFramework\Http; diff --git a/lib/private/Preview/Bitmap.php b/lib/private/Preview/Bitmap.php index 000b1f8277f..fe309376296 100644 --- a/lib/private/Preview/Bitmap.php +++ b/lib/private/Preview/Bitmap.php @@ -26,9 +26,9 @@ namespace OC\Preview; use Imagick; +use OCP\Files\File; use OCP\IImage; use OCP\ILogger; -use OCP\Files\File; /** * Creates a PNG preview using ImageMagick via the PECL extension diff --git a/lib/private/Preview/Generator.php b/lib/private/Preview/Generator.php index 79c512c84aa..981c820ccf3 100644 --- a/lib/private/Preview/Generator.php +++ b/lib/private/Preview/Generator.php @@ -36,8 +36,8 @@ use OCP\IConfig; use OCP\IImage; use OCP\IPreview; use OCP\Preview\IProvider; -use OCP\Preview\IVersionedPreviewFile; use OCP\Preview\IProviderV2; +use OCP\Preview\IVersionedPreviewFile; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\GenericEvent; diff --git a/lib/private/Preview/GeneratorHelper.php b/lib/private/Preview/GeneratorHelper.php index 7e35b5360d4..4389e269c8b 100644 --- a/lib/private/Preview/GeneratorHelper.php +++ b/lib/private/Preview/GeneratorHelper.php @@ -20,6 +20,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ + namespace OC\Preview; use OC\Files\View; diff --git a/lib/private/Preview/HEIC.php b/lib/private/Preview/HEIC.php index d23e6c6bd1f..9f4f9be38df 100644 --- a/lib/private/Preview/HEIC.php +++ b/lib/private/Preview/HEIC.php @@ -23,9 +23,9 @@ declare(strict_types=1); namespace OC\Preview; +use OCP\Files\File; use OCP\IImage; use OCP\ILogger; -use OCP\Files\File; /** * Creates a JPG preview using ImageMagick via the PECL extension diff --git a/lib/private/Preview/Image.php b/lib/private/Preview/Image.php index 6f82904a6a7..b8767d3f639 100644 --- a/lib/private/Preview/Image.php +++ b/lib/private/Preview/Image.php @@ -25,6 +25,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Preview; use OCP\Files\File; diff --git a/lib/private/Preview/MP3.php b/lib/private/Preview/MP3.php index f560f100109..6b19602b5c9 100644 --- a/lib/private/Preview/MP3.php +++ b/lib/private/Preview/MP3.php @@ -24,6 +24,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Preview; use ID3Parser\ID3Parser; diff --git a/lib/private/Preview/MSOffice2003.php b/lib/private/Preview/MSOffice2003.php index 5af66f72720..67559224528 100644 --- a/lib/private/Preview/MSOffice2003.php +++ b/lib/private/Preview/MSOffice2003.php @@ -19,6 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Preview; //.docm, .dotm, .xls(m), .xlt(m), .xla(m), .ppt(m), .pot(m), .pps(m), .ppa(m) diff --git a/lib/private/Preview/MSOffice2007.php b/lib/private/Preview/MSOffice2007.php index 0d1177d9de9..125e0ecfcad 100644 --- a/lib/private/Preview/MSOffice2007.php +++ b/lib/private/Preview/MSOffice2007.php @@ -19,6 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Preview; //.docx, .dotx, .xlsx, .xltx, .pptx, .potx, .ppsx diff --git a/lib/private/Preview/MSOfficeDoc.php b/lib/private/Preview/MSOfficeDoc.php index 24d1a9d5b6a..e1359c6b903 100644 --- a/lib/private/Preview/MSOfficeDoc.php +++ b/lib/private/Preview/MSOfficeDoc.php @@ -19,6 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Preview; //.doc, .dot diff --git a/lib/private/Preview/MarkDown.php b/lib/private/Preview/MarkDown.php index ae24c4f4419..bdf16ae2bc5 100644 --- a/lib/private/Preview/MarkDown.php +++ b/lib/private/Preview/MarkDown.php @@ -19,6 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Preview; class MarkDown extends TXT { diff --git a/lib/private/Preview/Movie.php b/lib/private/Preview/Movie.php index 2f64811b45e..17916896f97 100644 --- a/lib/private/Preview/Movie.php +++ b/lib/private/Preview/Movie.php @@ -23,6 +23,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Preview; use OCP\Files\File; @@ -88,8 +89,8 @@ class Movie extends ProviderV2 { if ($returnCode === 0) { $image = new \OC_Image(); $image->loadFromFile($tmpPath); - unlink($tmpPath); if ($image->valid()) { + unlink($tmpPath); $image->scaleDownToFit($maxX, $maxY); return $image; diff --git a/lib/private/Preview/Office.php b/lib/private/Preview/Office.php index f51023c5a83..49437aec186 100644 --- a/lib/private/Preview/Office.php +++ b/lib/private/Preview/Office.php @@ -23,11 +23,12 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Preview; +use OCP\Files\File; use OCP\IImage; use OCP\ILogger; -use OCP\Files\File; abstract class Office extends ProviderV2 { private $cmd; diff --git a/lib/private/Preview/OpenDocument.php b/lib/private/Preview/OpenDocument.php index 929fecffb21..ab3394ffea0 100644 --- a/lib/private/Preview/OpenDocument.php +++ b/lib/private/Preview/OpenDocument.php @@ -19,6 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Preview; //.odt, .ott, .oth, .odm, .odg, .otg, .odp, .otp, .ods, .ots, .odc, .odf, .odb, .odi, .oxt diff --git a/lib/private/Preview/Provider.php b/lib/private/Preview/Provider.php index d0dd259891f..ed128638e4b 100644 --- a/lib/private/Preview/Provider.php +++ b/lib/private/Preview/Provider.php @@ -23,6 +23,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Preview; use OCP\Preview\IProvider; diff --git a/lib/private/Preview/ProviderV1Adapter.php b/lib/private/Preview/ProviderV1Adapter.php index 903a7904bf8..0685193eb5b 100644 --- a/lib/private/Preview/ProviderV1Adapter.php +++ b/lib/private/Preview/ProviderV1Adapter.php @@ -36,11 +36,11 @@ class ProviderV1Adapter implements IProviderV2 { } public function getMimeType(): string { - return $this->providerV1->getMimeType(); + return (string)$this->providerV1->getMimeType(); } public function isAvailable(FileInfo $file): bool { - return $this->providerV1->isAvailable($file); + return (bool)$this->providerV1->isAvailable($file); } public function getThumbnail(File $file, int $maxX, int $maxY): ?IImage { diff --git a/lib/private/Preview/SVG.php b/lib/private/Preview/SVG.php index 3f313f4bb9b..2fe4c464575 100644 --- a/lib/private/Preview/SVG.php +++ b/lib/private/Preview/SVG.php @@ -22,11 +22,12 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Preview; +use OCP\Files\File; use OCP\IImage; use OCP\ILogger; -use OCP\Files\File; class SVG extends ProviderV2 { /** diff --git a/lib/private/Preview/StarOffice.php b/lib/private/Preview/StarOffice.php index 2c9542b22e6..1300bdc7b41 100644 --- a/lib/private/Preview/StarOffice.php +++ b/lib/private/Preview/StarOffice.php @@ -19,6 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Preview; //.sxw, .stw, .sxc, .stc, .sxd, .std, .sxi, .sti, .sxg, .sxm diff --git a/lib/private/Preview/TXT.php b/lib/private/Preview/TXT.php index 3e2032df2de..d1ee1c025ea 100644 --- a/lib/private/Preview/TXT.php +++ b/lib/private/Preview/TXT.php @@ -24,6 +24,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Preview; use OCP\Files\File; diff --git a/lib/private/Preview/Watcher.php b/lib/private/Preview/Watcher.php index 0c0531dff94..30708445c76 100644 --- a/lib/private/Preview/Watcher.php +++ b/lib/private/Preview/Watcher.php @@ -21,11 +21,12 @@ declare(strict_types=1); * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ + namespace OC\Preview; -use OCP\Files\Node; use OCP\Files\Folder; use OCP\Files\IAppData; +use OCP\Files\Node; use OCP\Files\NotFoundException; /** diff --git a/lib/private/Preview/WatcherConnector.php b/lib/private/Preview/WatcherConnector.php index f374b909d8f..346634ccf3a 100644 --- a/lib/private/Preview/WatcherConnector.php +++ b/lib/private/Preview/WatcherConnector.php @@ -21,11 +21,12 @@ declare(strict_types=1); * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ + namespace OC\Preview; use OC\SystemConfig; -use OCP\Files\Node; use OCP\Files\IRootFolder; +use OCP\Files\Node; class WatcherConnector { diff --git a/lib/private/PreviewManager.php b/lib/private/PreviewManager.php index 621c45f28c5..45b1529648b 100644 --- a/lib/private/PreviewManager.php +++ b/lib/private/PreviewManager.php @@ -24,6 +24,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC; use OC\Preview\Generator; @@ -149,34 +150,6 @@ class PreviewManager implements IPreview { } /** - * return a preview of a file - * - * @param string $file The path to the file where you want a thumbnail from - * @param int $maxX The maximum X size of the thumbnail. It can be smaller depending on the shape of the image - * @param int $maxY The maximum Y size of the thumbnail. It can be smaller depending on the shape of the image - * @param boolean $scaleUp Scale smaller images up to the thumbnail size or not. Might look ugly - * @return \OCP\IImage - * @deprecated 11 Use getPreview - */ - public function createPreview($file, $maxX = 100, $maxY = 75, $scaleUp = false) { - try { - $userRoot = $this->rootFolder->getUserFolder($this->userId)->getParent(); - $node = $userRoot->get($file); - if (!($file instanceof File)) { - throw new NotFoundException(); - } - - $preview = $this->getPreview($node, $maxX, $maxY); - } catch (\Exception $e) { - return new \OC_Image(); - } - - $previewImage = new \OC_Image(); - $previewImage->loadFromData($preview->getContent()); - return $previewImage; - } - - /** * Returns a preview of a file * * The cache is searched first and if nothing usable was found then a preview is diff --git a/lib/private/Repair.php b/lib/private/Repair.php index 35e6856e429..2b6080ca985 100644 --- a/lib/private/Repair.php +++ b/lib/private/Repair.php @@ -42,7 +42,7 @@ use OC\Repair\NC13\AddLogRotateJob; use OC\Repair\NC14\AddPreviewBackgroundCleanupJob; use OC\Repair\NC16\AddClenupLoginFlowV2BackgroundJob; use OC\Repair\NC16\CleanupCardDAVPhotoCache; -use OC\Repair\NC16\RemoveCypressFiles; +use OC\Repair\NC16\ClearCollectionsAccessCache; use OC\Repair\NC17\SetEnterpriseLogo; use OC\Repair\NC17\SwitchUpdateChannel; use OC\Repair\OldGroupMembershipShares; @@ -56,6 +56,7 @@ use OC\Template\JSCombiner; use OC\Template\SCSSCacher; use OCP\AppFramework\QueryException; use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Collaboration\Resources\IManager; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -150,7 +151,7 @@ class Repair implements IOutput { new CleanupCardDAVPhotoCache(\OC::$server->getConfig(), \OC::$server->getAppDataDir('dav-photocache'), \OC::$server->getLogger()), new AddClenupLoginFlowV2BackgroundJob(\OC::$server->getJobList()), new RemoveLinkShares(\OC::$server->getDatabaseConnection(), \OC::$server->getConfig(), \OC::$server->getGroupManager(), \OC::$server->getNotificationManager(), \OC::$server->query(ITimeFactory::class)), - \OC::$server->query(RemoveCypressFiles::class), + new ClearCollectionsAccessCache(\OC::$server->getConfig(), \OC::$server->query(IManager::class)), \OC::$server->query(SwitchUpdateChannel::class), \OC::$server->query(SetEnterpriseLogo::class), ]; diff --git a/lib/private/Repair/AddCleanupUpdaterBackupsJob.php b/lib/private/Repair/AddCleanupUpdaterBackupsJob.php index 1574e665fb6..cf35bc1fda5 100644 --- a/lib/private/Repair/AddCleanupUpdaterBackupsJob.php +++ b/lib/private/Repair/AddCleanupUpdaterBackupsJob.php @@ -45,4 +45,3 @@ class AddCleanupUpdaterBackupsJob implements IRepairStep { $this->jobList->add(BackgroundCleanupUpdaterBackupsJob::class); } } - diff --git a/lib/private/Repair/ClearFrontendCaches.php b/lib/private/Repair/ClearFrontendCaches.php index 22add525adb..90fdaf6b70f 100644 --- a/lib/private/Repair/ClearFrontendCaches.php +++ b/lib/private/Repair/ClearFrontendCaches.php @@ -23,11 +23,11 @@ namespace OC\Repair; +use OC\Template\JSCombiner; +use OC\Template\SCSSCacher; use OCP\ICacheFactory; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; -use OC\Template\JSCombiner; -use OC\Template\SCSSCacher; class ClearFrontendCaches implements IRepairStep { diff --git a/lib/private/Repair/Collation.php b/lib/private/Repair/Collation.php index f2cc9373176..a7a53720db5 100644 --- a/lib/private/Repair/Collation.php +++ b/lib/private/Repair/Collation.php @@ -151,4 +151,3 @@ class Collation implements IRepairStep { return array_keys($result); } } - diff --git a/lib/private/Repair/MoveUpdaterStepFile.php b/lib/private/Repair/MoveUpdaterStepFile.php index feb8a291282..adeabea9672 100644 --- a/lib/private/Repair/MoveUpdaterStepFile.php +++ b/lib/private/Repair/MoveUpdaterStepFile.php @@ -77,4 +77,3 @@ class MoveUpdaterStepFile implements IRepairStep { } } } - diff --git a/lib/private/Repair/NC11/FixMountStorages.php b/lib/private/Repair/NC11/FixMountStorages.php index d57a356dff9..c3c239d11ba 100644 --- a/lib/private/Repair/NC11/FixMountStorages.php +++ b/lib/private/Repair/NC11/FixMountStorages.php @@ -20,6 +20,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ + namespace OC\Repair\NC11; use OCP\DB\QueryBuilder\IQueryBuilder; diff --git a/lib/private/Repair/NC13/AddLogRotateJob.php b/lib/private/Repair/NC13/AddLogRotateJob.php index c65ea47f02b..0d258aa3779 100644 --- a/lib/private/Repair/NC13/AddLogRotateJob.php +++ b/lib/private/Repair/NC13/AddLogRotateJob.php @@ -20,6 +20,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ + namespace OC\Repair\NC13; use OC\Log\Rotate; diff --git a/lib/private/Repair/NC14/AddPreviewBackgroundCleanupJob.php b/lib/private/Repair/NC14/AddPreviewBackgroundCleanupJob.php index b58fabcba50..7bef59a9d37 100644 --- a/lib/private/Repair/NC14/AddPreviewBackgroundCleanupJob.php +++ b/lib/private/Repair/NC14/AddPreviewBackgroundCleanupJob.php @@ -21,6 +21,7 @@ declare(strict_types=1); * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ + namespace OC\Repair\NC14; use OC\Preview\BackgroundCleanupJob; diff --git a/lib/private/Repair/NC16/ClearCollectionsAccessCache.php b/lib/private/Repair/NC16/ClearCollectionsAccessCache.php new file mode 100644 index 00000000000..7945fffb822 --- /dev/null +++ b/lib/private/Repair/NC16/ClearCollectionsAccessCache.php @@ -0,0 +1,58 @@ +<?php +declare(strict_types=1); +/** + * @copyright Copyright (c) 2019 Joas Schilling <coding@schilljs.com> + * + * @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\Repair\NC16; + +use OC\Collaboration\Resources\Manager; +use OCP\Collaboration\Resources\IManager; +use OCP\IConfig; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; + +class ClearCollectionsAccessCache implements IRepairStep { + + /** @var IConfig */ + private $config; + + /** @var IManager|Manager */ + private $manager; + + public function __construct(IConfig $config, IManager $manager) { + $this->config = $config; + $this->manager = $manager; + } + + public function getName(): string { + return 'Clear access cache of projects'; + } + + private function shouldRun(): bool { + $versionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0.0'); + return version_compare($versionFromBeforeUpdate, '17.0.0.3', '<='); + } + + public function run(IOutput $output): void { + if ($this->shouldRun()) { + $this->manager->invalidateAccessCacheForAllCollections(); + } + } +} diff --git a/lib/private/Repair/NC16/RemoveCypressFiles.php b/lib/private/Repair/NC16/RemoveCypressFiles.php deleted file mode 100644 index 4b6108d0232..00000000000 --- a/lib/private/Repair/NC16/RemoveCypressFiles.php +++ /dev/null @@ -1,82 +0,0 @@ -<?php -declare(strict_types=1); -/** - * @copyright Copyright (c) 2019, Morris Jobke <hey@morrisjobke.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\Repair\NC16; - -use OC\IntegrityCheck\Checker; -use OCP\Migration\IOutput; -use OCP\Migration\IRepairStep; - -/** - * Class CleanupCypressFiles - * - * This repair step removes "cypress" files and folder created by viewer app in 16.0.1 - * - * See https://github.com/nextcloud/server/issues/16229 for more details. - * - * @deprecated - can be removed in 18 because this is the first version where no migration from 16 can happen - */ -class RemoveCypressFiles implements IRepairStep { - - /** @var Checker $checker */ - private $checker; - - private $pathToViewerApp = __DIR__ . '/../../../../apps/viewer'; - - public function getName(): string { - return 'Cleanup cypress files from viewer app'; - } - - public function __construct(Checker $checker) { - $this->checker = $checker; - } - - public function run(IOutput $output): void { - $file = $this->pathToViewerApp . '/cypress.json'; - if (file_exists($file)) { - unlink($file); - } - - $dir = $this->pathToViewerApp . '/cypress'; - if (is_dir($dir)) { - $files = new \RecursiveIteratorIterator( - new \RecursiveDirectoryIterator($dir, \RecursiveDirectoryIterator::SKIP_DOTS), - \RecursiveIteratorIterator::CHILD_FIRST - ); - - foreach ($files as $fileInfo) { - /** @var \SplFileInfo $fileInfo */ - if ($fileInfo->isLink()) { - unlink($fileInfo->getPathname()); - } else if ($fileInfo->isDir()) { - rmdir($fileInfo->getRealPath()); - } else { - unlink($fileInfo->getRealPath()); - } - } - rmdir($dir); - } - - // re-run the instance verification - $this->checker->runInstanceVerification(); - } -} diff --git a/lib/private/Repair/NC17/SwitchUpdateChannel.php b/lib/private/Repair/NC17/SwitchUpdateChannel.php index cfa72d86b1e..8a2850080a6 100644 --- a/lib/private/Repair/NC17/SwitchUpdateChannel.php +++ b/lib/private/Repair/NC17/SwitchUpdateChannel.php @@ -26,6 +26,7 @@ use OCP\IConfig; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; use OCP\Support\Subscription\IRegistry; + /** * @deprecated - can be removed in 18 */ diff --git a/lib/private/Repair/Owncloud/DropAccountTermsTable.php b/lib/private/Repair/Owncloud/DropAccountTermsTable.php index 0b1b64c3a3a..9c18ff809ae 100644 --- a/lib/private/Repair/Owncloud/DropAccountTermsTable.php +++ b/lib/private/Repair/Owncloud/DropAccountTermsTable.php @@ -57,4 +57,3 @@ class DropAccountTermsTable implements IRepairStep { $this->db->dropTable('account_terms'); } } - diff --git a/lib/private/Repair/Owncloud/SaveAccountsTableData.php b/lib/private/Repair/Owncloud/SaveAccountsTableData.php index f62f8b56b26..13f618b6747 100644 --- a/lib/private/Repair/Owncloud/SaveAccountsTableData.php +++ b/lib/private/Repair/Owncloud/SaveAccountsTableData.php @@ -189,4 +189,3 @@ class SaveAccountsTableData implements IRepairStep { } } - diff --git a/lib/private/Repair/SqliteAutoincrement.php b/lib/private/Repair/SqliteAutoincrement.php index 814f955dc70..93f9961d565 100644 --- a/lib/private/Repair/SqliteAutoincrement.php +++ b/lib/private/Repair/SqliteAutoincrement.php @@ -24,10 +24,10 @@ namespace OC\Repair; use Doctrine\DBAL\Platforms\SqlitePlatform; -use Doctrine\DBAL\Schema\SchemaException; +use Doctrine\DBAL\Schema\ColumnDiff; use Doctrine\DBAL\Schema\SchemaDiff; +use Doctrine\DBAL\Schema\SchemaException; use Doctrine\DBAL\Schema\TableDiff; -use Doctrine\DBAL\Schema\ColumnDiff; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; @@ -98,4 +98,3 @@ class SqliteAutoincrement implements IRepairStep { $this->connection->commit(); } } - diff --git a/lib/private/Route/CachingRouter.php b/lib/private/Route/CachingRouter.php index cb1b98d77e6..a37bbf5b701 100644 --- a/lib/private/Route/CachingRouter.php +++ b/lib/private/Route/CachingRouter.php @@ -58,7 +58,9 @@ class CachingRouter extends Router { return $cachedKey; } else { $url = parent::generate($name, $parameters, $absolute); - $this->cache->set($key, $url, 3600); + if ($url) { + $this->cache->set($key, $url, 3600); + } return $url; } } diff --git a/lib/private/Route/Router.php b/lib/private/Route/Router.php index 1839b356424..125c5ff2742 100644 --- a/lib/private/Route/Router.php +++ b/lib/private/Route/Router.php @@ -33,16 +33,16 @@ namespace OC\Route; +use OCP\AppFramework\App; use OCP\ILogger; use OCP\Route\IRouter; -use OCP\AppFramework\App; use OCP\Util; +use Symfony\Component\Routing\Exception\ResourceNotFoundException; use Symfony\Component\Routing\Exception\RouteNotFoundException; -use Symfony\Component\Routing\Matcher\UrlMatcher; use Symfony\Component\Routing\Generator\UrlGenerator; +use Symfony\Component\Routing\Matcher\UrlMatcher; use Symfony\Component\Routing\RequestContext; use Symfony\Component\Routing\RouteCollection; -use Symfony\Component\Routing\Exception\ResourceNotFoundException; class Router implements IRouter { /** @var RouteCollection[] */ @@ -162,7 +162,6 @@ class Router implements IRouter { if (!isset($this->loadedApps['core'])) { $this->loadedApps['core'] = true; $this->useCollection('root'); - require_once __DIR__ . '/../../../settings/routes.php'; require_once __DIR__ . '/../../../core/routes.php'; // Also add the OCS collection @@ -258,7 +257,9 @@ class Router implements IRouter { $app = \OC_App::cleanAppId($app); \OC::$REQUESTEDAPP = $app; $this->loadRoutes($app); - } else if (substr($url, 0, 6) === '/core/' or substr($url, 0, 10) === '/settings/') { + } else if (substr($url, 0, 10) === '/settings/') { + $this->loadRoutes('settings'); + } else if (substr($url, 0, 6) === '/core/') { \OC::$REQUESTEDAPP = $url; if (!\OC::$server->getConfig()->getSystemValueBool('maintenance') && !Util::needUpgrade()) { \OC_App::loadApps(); @@ -370,7 +371,7 @@ class Router implements IRouter { $applicationClassName = $appNameSpace . '\\AppInfo\\Application'; if (class_exists($applicationClassName)) { - $application = new $applicationClassName(); + $application = \OC::$server->query($applicationClassName); } else { $application = new App($appName); } diff --git a/lib/private/Search.php b/lib/private/Search.php index 0cfdf24ac97..2ef7eeda4ad 100644 --- a/lib/private/Search.php +++ b/lib/private/Search.php @@ -25,9 +25,9 @@ */ namespace OC; +use OCP\ISearch; use OCP\Search\PagedProvider; use OCP\Search\Provider; -use OCP\ISearch; /** * Provide an interface to all search providers diff --git a/lib/private/Security/Bruteforce/Capabilities.php b/lib/private/Security/Bruteforce/Capabilities.php index 3df0458c694..d6e08c399ed 100644 --- a/lib/private/Security/Bruteforce/Capabilities.php +++ b/lib/private/Security/Bruteforce/Capabilities.php @@ -20,6 +20,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ + namespace OC\Security\Bruteforce; use OCP\Capabilities\IPublicCapability; diff --git a/lib/private/Security/CSP/ContentSecurityPolicy.php b/lib/private/Security/CSP/ContentSecurityPolicy.php index 9d1d043a165..c912b820eba 100644 --- a/lib/private/Security/CSP/ContentSecurityPolicy.php +++ b/lib/private/Security/CSP/ContentSecurityPolicy.php @@ -21,6 +21,7 @@ declare(strict_types=1); * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Security\CSP; /** diff --git a/lib/private/Security/CredentialsManager.php b/lib/private/Security/CredentialsManager.php index 0ac9b30c6ce..7c58cb3b1c9 100644 --- a/lib/private/Security/CredentialsManager.php +++ b/lib/private/Security/CredentialsManager.php @@ -22,9 +22,9 @@ namespace OC\Security; -use OCP\Security\ICrypto; use OCP\IDBConnection; use OCP\Security\ICredentialsManager; +use OCP\Security\ICrypto; /** * Store and retrieve credentials for external services diff --git a/lib/private/Security/Crypto.php b/lib/private/Security/Crypto.php index 876f159950c..2eb20d41456 100644 --- a/lib/private/Security/Crypto.php +++ b/lib/private/Security/Crypto.php @@ -24,14 +24,13 @@ declare(strict_types=1); * */ - namespace OC\Security; -use phpseclib\Crypt\AES; -use phpseclib\Crypt\Hash; +use OCP\IConfig; use OCP\Security\ICrypto; use OCP\Security\ISecureRandom; -use OCP\IConfig; +use phpseclib\Crypt\AES; +use phpseclib\Crypt\Hash; /** * Class Crypto provides a high-level encryption layer using AES-CBC. If no key has been provided diff --git a/lib/private/Security/IdentityProof/Manager.php b/lib/private/Security/IdentityProof/Manager.php index fb27f04d873..6db5d4ab2eb 100644 --- a/lib/private/Security/IdentityProof/Manager.php +++ b/lib/private/Security/IdentityProof/Manager.php @@ -29,6 +29,7 @@ namespace OC\Security\IdentityProof; use OC\Files\AppData\Factory; use OCP\Files\IAppData; use OCP\IConfig; +use OCP\ILogger; use OCP\IUser; use OCP\Security\ICrypto; @@ -39,19 +40,18 @@ class Manager { private $crypto; /** @var IConfig */ private $config; + /** @var ILogger */ + private $logger; - /** - * @param Factory $appDataFactory - * @param ICrypto $crypto - * @param IConfig $config - */ public function __construct(Factory $appDataFactory, ICrypto $crypto, - IConfig $config + IConfig $config, + ILogger $logger ) { $this->appData = $appDataFactory->get('identityproof'); $this->crypto = $crypto; $this->config = $config; + $this->logger = $logger; } /** @@ -59,6 +59,7 @@ class Manager { * In a separate function for unit testing purposes. * * @return array [$publicKey, $privateKey] + * @throws \RuntimeException */ protected function generateKeyPair(): array { $config = [ @@ -68,7 +69,16 @@ class Manager { // Generate new key $res = openssl_pkey_new($config); - openssl_pkey_export($res, $privateKey); + + if ($res === false) { + $this->logOpensslError(); + throw new \RuntimeException('OpenSSL reported a problem'); + } + + if (openssl_pkey_export($res, $privateKey, null, $config) === false) { + $this->logOpensslError(); + throw new \RuntimeException('OpenSSL reported a problem'); + } // Extract the public key from $res to $pubKey $publicKey = openssl_pkey_get_details($res); @@ -83,6 +93,7 @@ class Manager { * * @param string $id key id * @return Key + * @throws \RuntimeException */ protected function generateKey(string $id): Key { list($publicKey, $privateKey) = $this->generateKeyPair(); @@ -105,6 +116,7 @@ class Manager { * * @param string $id * @return Key + * @throws \RuntimeException */ protected function retrieveKey(string $id): Key { try { @@ -124,6 +136,7 @@ class Manager { * * @param IUser $user * @return Key + * @throws \RuntimeException */ public function getKey(IUser $user): Key { $uid = $user->getUID(); @@ -144,5 +157,13 @@ class Manager { return $this->retrieveKey('system-' . $instanceId); } + private function logOpensslError(): void { + $errors = []; + while ($error = openssl_error_string()) { + $errors[] = $error; + } + $this->logger->critical('Something is wrong with your openssl setup: ' . implode(', ', $errors)); + } + } diff --git a/lib/private/Security/RateLimiting/Limiter.php b/lib/private/Security/RateLimiting/Limiter.php index 5267497f86f..c272120a898 100644 --- a/lib/private/Security/RateLimiting/Limiter.php +++ b/lib/private/Security/RateLimiting/Limiter.php @@ -28,9 +28,7 @@ use OC\Security\Normalizer\IpAddress; use OC\Security\RateLimiting\Backend\IBackend; use OC\Security\RateLimiting\Exception\RateLimitExceededException; use OCP\AppFramework\Utility\ITimeFactory; -use OCP\IRequest; use OCP\IUser; -use OCP\IUserSession; class Limiter { /** @var IBackend */ @@ -39,14 +37,10 @@ class Limiter { private $timeFactory; /** - * @param IUserSession $userSession - * @param IRequest $request * @param ITimeFactory $timeFactory * @param IBackend $backend */ - public function __construct(IUserSession $userSession, - IRequest $request, - ITimeFactory $timeFactory, + public function __construct(ITimeFactory $timeFactory, IBackend $backend) { $this->backend = $backend; $this->timeFactory = $timeFactory; @@ -62,7 +56,7 @@ class Limiter { private function register(string $methodIdentifier, string $userIdentifier, int $period, - int $limit) { + int $limit): void { $existingAttempts = $this->backend->getAttempts($methodIdentifier, $userIdentifier, $period); if ($existingAttempts >= $limit) { throw new RateLimitExceededException(); @@ -83,7 +77,7 @@ class Limiter { public function registerAnonRequest(string $identifier, int $anonLimit, int $anonPeriod, - string $ip) { + string $ip): void { $ipSubnet = (new IpAddress($ip))->getSubnet(); $anonHashIdentifier = hash('sha512', 'anon::' . $identifier . $ipSubnet); @@ -102,7 +96,7 @@ class Limiter { public function registerUserRequest(string $identifier, int $userLimit, int $userPeriod, - IUser $user) { + IUser $user): void { $userHashIdentifier = hash('sha512', 'user::' . $identifier . $user->getUID()); $this->register($identifier, $userHashIdentifier, $userPeriod, $userLimit); } diff --git a/lib/private/Security/TrustedDomainHelper.php b/lib/private/Security/TrustedDomainHelper.php index 5237767d8ea..22a75158294 100644 --- a/lib/private/Security/TrustedDomainHelper.php +++ b/lib/private/Security/TrustedDomainHelper.php @@ -90,7 +90,7 @@ class TrustedDomainHelper { if (gettype($trusted) !== 'string') { break; } - $regex = '/^' . implode('[-\.a-zA-Z0-9]*', array_map(function($v) { return preg_quote($v, '/'); }, explode('*', $trusted))) . '$/'; + $regex = '/^' . implode('[-\.a-zA-Z0-9]*', array_map(function($v) { return preg_quote($v, '/'); }, explode('*', $trusted))) . '$/i'; if (preg_match($regex, $domain) || preg_match($regex, $domainWithPort)) { return true; } diff --git a/lib/private/Server.php b/lib/private/Server.php index f919e0b4efb..fa2a521b6b1 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -199,6 +199,8 @@ class Server extends ServerContainer implements IServerContainer { $this->registerAlias(\OCP\Contacts\IManager::class, \OC\ContactsManager::class); $this->registerAlias('ContactsManager', \OCP\Contacts\IManager::class); + $this->registerAlias(\OCP\DirectEditing\IManager::class, \OC\DirectEditing\Manager::class); + $this->registerAlias(IActionFactory::class, ActionFactory::class); @@ -298,7 +300,7 @@ class Server extends ServerContainer implements IServerContainer { $this->getLogger(), $this->getUserManager() ); - $connector = new HookConnector($root, $view); + $connector = new HookConnector($root, $view, $c->getEventDispatcher()); $connector->viewToNode(); $previewConnector = new \OC\Preview\WatcherConnector($root, $c->getSystemConfig()); @@ -315,9 +317,6 @@ class Server extends ServerContainer implements IServerContainer { }); $this->registerAlias('LazyRootFolder', \OCP\Files\IRootFolder::class); - $this->registerService(\OC\User\Manager::class, function (Server $c) { - return new \OC\User\Manager($c->getConfig(), $c->getEventDispatcher()); - }); $this->registerAlias('UserManager', \OC\User\Manager::class); $this->registerAlias(\OCP\IUserManager::class, \OC\User\Manager::class); @@ -597,14 +596,6 @@ class Server extends ServerContainer implements IServerContainer { }); $this->registerAlias('Search', \OCP\ISearch::class); - $this->registerService(\OC\Security\RateLimiting\Limiter::class, function (Server $c) { - return new \OC\Security\RateLimiting\Limiter( - $this->getUserSession(), - $this->getRequest(), - new \OC\AppFramework\Utility\TimeFactory(), - $c->query(\OC\Security\RateLimiting\Backend\IBackend::class) - ); - }); $this->registerService(\OC\Security\RateLimiting\Backend\IBackend::class, function ($c) { return new \OC\Security\RateLimiting\Backend\MemoryCache( $this->getMemCacheFactory(), @@ -1194,14 +1185,6 @@ class Server extends ServerContainer implements IServerContainer { $this->registerAlias(IDashboardManager::class, DashboardManager::class); $this->registerAlias(IFullTextSearchManager::class, FullTextSearchManager::class); - $this->registerService(\OC\Security\IdentityProof\Manager::class, function (Server $c) { - return new \OC\Security\IdentityProof\Manager( - $c->query(\OC\Files\AppData\Factory::class), - $c->getCrypto(), - $c->getConfig() - ); - }); - $this->registerAlias(ISubAdmin::class, SubAdmin::class); $this->registerAlias(IInitialStateService::class, InitialStateService::class); diff --git a/lib/private/ServerContainer.php b/lib/private/ServerContainer.php index b67b4d1e701..704d207223b 100644 --- a/lib/private/ServerContainer.php +++ b/lib/private/ServerContainer.php @@ -100,8 +100,9 @@ class ServerContainer extends SimpleContainer { if (!isset($this->hasNoAppContainer[$namespace])) { $applicationClassName = 'OCA\\' . $sensitiveNamespace . '\\AppInfo\\Application'; if (class_exists($applicationClassName)) { - new $applicationClassName(); + $app = new $applicationClassName(); if (isset($this->appContainers[$namespace])) { + $this->appContainers[$namespace]->offsetSet($applicationClassName, $app); return $this->appContainers[$namespace]; } } diff --git a/lib/private/Settings/Manager.php b/lib/private/Settings/Manager.php index 7e3edfa9df0..1205cb81525 100644 --- a/lib/private/Settings/Manager.php +++ b/lib/private/Settings/Manager.php @@ -30,15 +30,16 @@ namespace OC\Settings; use Closure; +use OC\Settings\Personal\PersonalInfo; use OCP\AppFramework\QueryException; use OCP\IL10N; use OCP\ILogger; use OCP\IServerContainer; use OCP\IURLGenerator; use OCP\L10N\IFactory; -use OCP\Settings\ISettings; use OCP\Settings\IManager; use OCP\Settings\ISection; +use OCP\Settings\ISettings; use OCP\Settings\ISubAdminSettings; class Manager implements IManager { @@ -245,32 +246,32 @@ class Manager implements IManager { if ($section === 'overview') { /** @var ISettings $form */ - $form = $this->container->query(Admin\Overview::class); + $form = $this->container->query(\OCA\Settings\Admin\Overview::class); if ($filter === null || $filter($form)) { $forms[$form->getPriority()] = [$form]; } } if ($section === 'server') { /** @var ISettings $form */ - $form = $this->container->query(Admin\Server::class); + $form = $this->container->query(\OCA\Settings\Admin\Server::class); if ($filter === null || $filter($form)) { $forms[$form->getPriority()] = [$form]; } - $form = $this->container->query(Admin\Mail::class); + $form = $this->container->query(\OCA\Settings\Admin\Mail::class); if ($filter === null || $filter($form)) { $forms[$form->getPriority()] = [$form]; } } if ($section === 'security') { /** @var ISettings $form */ - $form = $this->container->query(Admin\Security::class); + $form = $this->container->query(\OCA\Settings\Admin\Security::class); if ($filter === null || $filter($form)) { $forms[$form->getPriority()] = [$form]; } } if ($section === 'sharing') { /** @var ISettings $form */ - $form = $this->container->query(Admin\Sharing::class); + $form = $this->container->query(\OCA\Settings\Admin\Sharing::class); if ($filter === null || $filter($form)) { $forms[$form->getPriority()] = [$form]; } @@ -289,19 +290,23 @@ class Manager implements IManager { if ($section === 'personal-info') { /** @var ISettings $form */ - $form = $this->container->query(Personal\PersonalInfo::class); + $form = $this->container->query(\OCA\Settings\Personal\PersonalInfo::class); $forms[$form->getPriority()] = [$form]; - $form = new Personal\ServerDevNotice(); + $form = new \OCA\Settings\Personal\ServerDevNotice(); $forms[$form->getPriority()] = [$form]; } if ($section === 'security') { /** @var ISettings $form */ - $form = $this->container->query(Personal\Security::class); + $form = $this->container->query(\OCA\Settings\Personal\Security::class); + $forms[$form->getPriority()] = [$form]; + + /** @var ISettings $form */ + $form = $this->container->query(\OCA\Settings\Personal\Security\Authtokens::class); $forms[$form->getPriority()] = [$form]; } if ($section === 'additional') { /** @var ISettings $form */ - $form = $this->container->query(Personal\Additional::class); + $form = $this->container->query(\OCA\Settings\Personal\Additional::class); $forms[$form->getPriority()] = [$form]; } @@ -343,7 +348,7 @@ class Manager implements IManager { } $sections = [ - 0 => [new Section('personal-info', $this->l->t('Personal info'), 0, $this->url->imagePath('core', 'actions/info.svg'))], + 0 => [new Section('personal-info', $this->l->t('Personal info'), 0, $this->url->imagePath('core', 'actions/user.svg'))], 5 => [new Section('security', $this->l->t('Security'), 0, $this->url->imagePath('settings', 'password.svg'))], 15 => [new Section('sync-clients', $this->l->t('Mobile & desktop'), 0, $this->url->imagePath('core', 'clients/phone.svg'))], ]; diff --git a/lib/private/Setup.php b/lib/private/Setup.php index e360319c5b6..32a85264f57 100644 --- a/lib/private/Setup.php +++ b/lib/private/Setup.php @@ -49,7 +49,9 @@ use OC\Authentication\Token\DefaultTokenCleanupJob; use OC\Authentication\Token\DefaultTokenProvider; use OC\Log\Rotate; use OC\Preview\BackgroundCleanupJob; +use OCP\AppFramework\Utility\ITimeFactory; use OCP\Defaults; +use OCP\IGroup; use OCP\IL10N; use OCP\ILogger; use OCP\IUser; @@ -80,14 +82,15 @@ class Setup { * @param ISecureRandom $random * @param Installer $installer */ - public function __construct(SystemConfig $config, - IniGetWrapper $iniWrapper, - IL10N $l10n, - Defaults $defaults, - ILogger $logger, - ISecureRandom $random, - Installer $installer - ) { + public function __construct( + SystemConfig $config, + IniGetWrapper $iniWrapper, + IL10N $l10n, + Defaults $defaults, + ILogger $logger, + ISecureRandom $random, + Installer $installer + ) { $this->config = $config; $this->iniWrapper = $iniWrapper; $this->l10n = $l10n; @@ -100,13 +103,14 @@ class Setup { static protected $dbSetupClasses = [ 'mysql' => \OC\Setup\MySQL::class, 'pgsql' => \OC\Setup\PostgreSQL::class, - 'oci' => \OC\Setup\OCI::class, + 'oci' => \OC\Setup\OCI::class, 'sqlite' => \OC\Setup\Sqlite::class, 'sqlite3' => \OC\Setup\Sqlite::class, ]; /** * Wrapper around the "class_exists" PHP function to be able to mock it + * * @param string $name * @return bool */ @@ -116,6 +120,7 @@ class Setup { /** * Wrapper around the "is_callable" PHP function to be able to mock it + * * @param string $name * @return bool */ @@ -141,7 +146,7 @@ class Setup { */ public function getSupportedDatabases($allowAllDatabases = false) { $availableDatabases = [ - 'sqlite' => [ + 'sqlite' => [ 'type' => 'pdo', 'call' => 'sqlite', 'name' => 'SQLite', @@ -168,24 +173,24 @@ class Setup { $configuredDatabases = $this->config->getValue('supportedDatabases', ['sqlite', 'mysql', 'pgsql']); } - if(!is_array($configuredDatabases)) { + if (!is_array($configuredDatabases)) { throw new Exception('Supported databases are not properly configured.'); } $supportedDatabases = array(); - foreach($configuredDatabases as $database) { - if(array_key_exists($database, $availableDatabases)) { + foreach ($configuredDatabases as $database) { + if (array_key_exists($database, $availableDatabases)) { $working = false; $type = $availableDatabases[$database]['type']; $call = $availableDatabases[$database]['call']; if ($type === 'function') { $working = $this->is_callable($call); - } elseif($type === 'pdo') { + } elseif ($type === 'pdo') { $working = in_array($call, $this->getAvailableDbDriversForPdo(), true); } - if($working) { + if ($working) { $supportedDatabases[$database] = $availableDatabases[$database]['name']; } } @@ -204,14 +209,14 @@ class Setup { public function getSystemInfo($allowAllDatabases = false) { $databases = $this->getSupportedDatabases($allowAllDatabases); - $dataDir = $this->config->getValue('datadirectory', \OC::$SERVERROOT.'/data'); + $dataDir = $this->config->getValue('datadirectory', \OC::$SERVERROOT . '/data'); $errors = []; // Create data directory to test whether the .htaccess works // Notice that this is not necessarily the same data directory as the one // that will effectively be used. - if(!file_exists($dataDir)) { + if (!file_exists($dataDir)) { @mkdir($dataDir); } $htAccessWorking = true; @@ -242,7 +247,7 @@ class Setup { ]; } - if($this->iniWrapper->getString('open_basedir') !== '' && PHP_INT_SIZE === 4) { + if ($this->iniWrapper->getString('open_basedir') !== '' && PHP_INT_SIZE === 4) { $errors[] = [ 'error' => $this->l10n->t( 'It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. ' . @@ -275,14 +280,14 @@ class Setup { $error = array(); $dbType = $options['dbtype']; - if(empty($options['adminlogin'])) { + if (empty($options['adminlogin'])) { $error[] = $l->t('Set an admin username.'); } - if(empty($options['adminpass'])) { + if (empty($options['adminpass'])) { $error[] = $l->t('Set an admin password.'); } - if(empty($options['directory'])) { - $options['directory'] = \OC::$SERVERROOT."/data"; + if (empty($options['directory'])) { + $options['directory'] = \OC::$SERVERROOT . "/data"; } if (!isset(self::$dbSetupClasses[$dbType])) { @@ -310,8 +315,8 @@ class Setup { $request = \OC::$server->getRequest(); //no errors, good - if(isset($options['trusted_domains']) - && is_array($options['trusted_domains'])) { + if (isset($options['trusted_domains']) + && is_array($options['trusted_domains'])) { $trustedDomains = $options['trusted_domains']; } else { $trustedDomains = [$request->getInsecureServerHost()]; @@ -329,12 +334,12 @@ class Setup { //write the config file $newConfigValues = [ - 'passwordsalt' => $salt, - 'secret' => $secret, - 'trusted_domains' => $trustedDomains, - 'datadirectory' => $dataDir, - 'dbtype' => $dbType, - 'version' => implode('.', \OCP\Util::getVersion()), + 'passwordsalt' => $salt, + 'secret' => $secret, + 'trusted_domains' => $trustedDomains, + 'datadirectory' => $dataDir, + 'dbtype' => $dbType, + 'version' => implode('.', \OCP\Util::getVersion()), ]; if ($this->config->getValue('overwrite.cli.url', null) === null) { @@ -363,13 +368,13 @@ class Setup { } //create the user and group - $user = null; + $user = null; try { $user = \OC::$server->getUserManager()->createUser($username, $password); if (!$user) { $error[] = "User <$username> could not be created."; } - } catch(Exception $exception) { + } catch (Exception $exception) { $error[] = $exception->getMessage(); } @@ -379,22 +384,25 @@ class Setup { $config->setAppValue('core', 'lastupdatedat', microtime(true)); $config->setAppValue('core', 'vendor', $this->getVendor()); - $group =\OC::$server->getGroupManager()->createGroup('admin'); - $group->addUser($user); + $group = \OC::$server->getGroupManager()->createGroup('admin'); + if ($group instanceof IGroup) { + $group->addUser($user); + } // Install shipped apps and specified app bundles Installer::installShippedApps(); $bundleFetcher = new BundleFetcher(\OC::$server->getL10N('lib')); $defaultInstallationBundles = $bundleFetcher->getDefaultInstallationBundle(); - foreach($defaultInstallationBundles as $bundle) { + foreach ($defaultInstallationBundles as $bundle) { try { $this->installer->installAppBundle($bundle); - } catch (Exception $e) {} + } catch (Exception $e) { + } } // create empty file in data dir, so we can later find // out that this is indeed an ownCloud data directory - file_put_contents($config->getSystemValue('datadirectory', \OC::$SERVERROOT.'/data').'/.ocdata', ''); + file_put_contents($config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/.ocdata', ''); // Update .htaccess files self::updateHtaccess(); @@ -414,6 +422,9 @@ class Setup { $userSession->login($username, $password); $userSession->createSessionToken($request, $userSession->getUser()->getUID(), $username, $password); + $session = $userSession->getSession(); + $session->set('last-password-confirm', \OC::$server->query(ITimeFactory::class)->getTime()); + // Set email for admin if (!empty($options['adminemail'])) { $config->setUserValue($user->getUID(), 'settings', 'email', $options['adminemail']); @@ -434,7 +445,7 @@ class Setup { * @return string Absolute path to htaccess */ private function pathToHtaccess() { - return \OC::$SERVERROOT.'/.htaccess'; + return \OC::$SERVERROOT . '/.htaccess'; } /** @@ -499,7 +510,7 @@ class Setup { // Add rewrite rules if the RewriteBase is configured $rewriteBase = $config->getValue('htaccess.RewriteBase', ''); - if($rewriteBase !== '') { + if ($rewriteBase !== '') { $content .= "\n<IfModule mod_rewrite.c>"; $content .= "\n Options -MultiViews"; $content .= "\n RewriteRule ^core/js/oc.js$ index.php [PT,E=PATH_INFO:$1]"; @@ -532,7 +543,7 @@ class Setup { if ($content !== '') { //suppress errors in case we don't have permissions for it - return (bool) @file_put_contents($setupHelper->pathToHtaccess(), $htaccessContent.$content . "\n"); + return (bool)@file_put_contents($setupHelper->pathToHtaccess(), $htaccessContent . $content . "\n"); } return false; @@ -582,6 +593,6 @@ class Setup { // this should really be a JSON file require \OC::$SERVERROOT . '/version.php'; /** @var string $vendor */ - return (string) $vendor; + return (string)$vendor; } } diff --git a/lib/private/Setup/AbstractDatabase.php b/lib/private/Setup/AbstractDatabase.php index 903f8b6e386..459848d1c79 100644 --- a/lib/private/Setup/AbstractDatabase.php +++ b/lib/private/Setup/AbstractDatabase.php @@ -26,6 +26,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Setup; use OC\DB\ConnectionFactory; diff --git a/lib/private/Setup/MySQL.php b/lib/private/Setup/MySQL.php index eb4fedefd4b..8a38f889c74 100644 --- a/lib/private/Setup/MySQL.php +++ b/lib/private/Setup/MySQL.php @@ -27,6 +27,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Setup; use OC\DB\MySqlTools; @@ -100,9 +101,9 @@ class MySQL extends AbstractDatabase { $password = $this->dbPassword; // we need to create 2 accounts, one for global use and one for local user. if we don't specify the local one, // the anonymous user would take precedence when there is one. - $query = "CREATE USER '$name'@'localhost' IDENTIFIED BY '$password'"; + $query = "CREATE USER '$name'@'localhost' IDENTIFIED WITH mysql_native_password BY '$password'"; $connection->executeUpdate($query); - $query = "CREATE USER '$name'@'%' IDENTIFIED BY '$password'"; + $query = "CREATE USER '$name'@'%' IDENTIFIED WITH mysql_native_password BY '$password'"; $connection->executeUpdate($query); } catch (\Exception $ex){ diff --git a/lib/private/Setup/PostgreSQL.php b/lib/private/Setup/PostgreSQL.php index 6d1b5ca2927..020c2e555d9 100644 --- a/lib/private/Setup/PostgreSQL.php +++ b/lib/private/Setup/PostgreSQL.php @@ -24,6 +24,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Setup; use OC\DatabaseException; diff --git a/lib/private/Share/Constants.php b/lib/private/Share/Constants.php index 72dc5cd43be..a76208655a3 100644 --- a/lib/private/Share/Constants.php +++ b/lib/private/Share/Constants.php @@ -27,18 +27,47 @@ namespace OC\Share; +use OCP\Share\IShare; + class Constants { + /** + * @deprecated 17.0.0 - use IShare::TYPE_USER instead + */ const SHARE_TYPE_USER = 0; + /** + * @deprecated 17.0.0 - use IShare::TYPE_GROUP instead + */ const SHARE_TYPE_GROUP = 1; // const SHARE_TYPE_USERGROUP = 2; // Internal type used by DefaultShareProvider + /** + * @deprecated 17.0.0 - use IShare::TYPE_LINK instead + */ const SHARE_TYPE_LINK = 3; + /** + * @deprecated 17.0.0 - use IShare::TYPE_EMAIL instead + */ const SHARE_TYPE_EMAIL = 4; const SHARE_TYPE_CONTACT = 5; // ToDo Check if it is still in use otherwise remove it + /** + * @deprecated 17.0.0 - use IShare::TYPE_REMOTE instead + */ const SHARE_TYPE_REMOTE = 6; + /** + * @deprecated 17.0.0 - use IShare::TYPE_CIRCLE instead + */ const SHARE_TYPE_CIRCLE = 7; + /** + * @deprecated 17.0.0 - use IShare::TYPE_GUEST instead + */ const SHARE_TYPE_GUEST = 8; + /** + * @deprecated 17.0.0 - use IShare::REMOTE_GROUP instead + */ const SHARE_TYPE_REMOTE_GROUP = 9; + /** + * @deprecated 17.0.0 - use IShare::TYPE_ROOM instead + */ const SHARE_TYPE_ROOM = 10; // const SHARE_TYPE_USERROOM = 11; // Internal type used by RoomShareProvider diff --git a/lib/private/Share/SearchResultSorter.php b/lib/private/Share/SearchResultSorter.php index da63dd33b8e..8418a1e4119 100644 --- a/lib/private/Share/SearchResultSorter.php +++ b/lib/private/Share/SearchResultSorter.php @@ -21,6 +21,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Share; use OCP\ILogger; @@ -73,4 +74,3 @@ class SearchResultSorter { } } } - diff --git a/lib/private/Share/Share.php b/lib/private/Share/Share.php index 86101707dbf..64d59334aad 100644 --- a/lib/private/Share/Share.php +++ b/lib/private/Share/Share.php @@ -84,9 +84,6 @@ class Share extends Constants { 'collectionOf' => $collectionOf, 'supportedFileExtensions' => $supportedFileExtensions ); - if(count(self::$backendTypes) === 1) { - Util::addScript('core', 'dist/share_backend'); - } return true; } \OCP\Util::writeLog('OCP\Share', diff --git a/lib/private/Share20/DefaultShareProvider.php b/lib/private/Share20/DefaultShareProvider.php index aea50dfcdb6..152e5d55394 100644 --- a/lib/private/Share20/DefaultShareProvider.php +++ b/lib/private/Share20/DefaultShareProvider.php @@ -27,29 +27,30 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Share20; use OC\Files\Cache\Cache; +use OC\Share20\Exception\BackendError; +use OC\Share20\Exception\InvalidShare; +use OC\Share20\Exception\ProviderException; +use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Defaults; use OCP\Files\Folder; +use OCP\Files\IRootFolder; +use OCP\Files\Node; +use OCP\IDBConnection; +use OCP\IGroup; +use OCP\IGroupManager; use OCP\IL10N; use OCP\IURLGenerator; use OCP\IUser; +use OCP\IUserManager; use OCP\Mail\IMailer; +use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\IShare; use OCP\Share\IShareHelper; use OCP\Share\IShareProvider; -use OC\Share20\Exception\InvalidShare; -use OC\Share20\Exception\ProviderException; -use OCP\Share\Exceptions\ShareNotFound; -use OC\Share20\Exception\BackendError; -use OCP\DB\QueryBuilder\IQueryBuilder; -use OCP\IGroup; -use OCP\IGroupManager; -use OCP\IUserManager; -use OCP\Files\IRootFolder; -use OCP\IDBConnection; -use OCP\Files\Node; /** * Class DefaultShareProvider @@ -142,9 +143,20 @@ class DefaultShareProvider implements IShareProvider { if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { //Set the UID of the user we share with $qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith())); + $qb->setValue('accepted', $qb->createNamedParameter(IShare::STATUS_PENDING)); + + //If an expiration date is set store it + if ($share->getExpirationDate() !== null) { + $qb->setValue('expiration', $qb->createNamedParameter($share->getExpirationDate(), 'datetime')); + } } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { //Set the GID of the group we share with $qb->setValue('share_with', $qb->createNamedParameter($share->getSharedWith())); + + //If an expiration date is set store it + if ($share->getExpirationDate() !== null) { + $qb->setValue('expiration', $qb->createNamedParameter($share->getExpirationDate(), 'datetime')); + } } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { //set label for public link $qb->setValue('label', $qb->createNamedParameter($share->getLabel())); @@ -252,6 +264,7 @@ class DefaultShareProvider implements IShareProvider { ->set('file_source', $qb->createNamedParameter($share->getNode()->getId())) ->set('expiration', $qb->createNamedParameter($share->getExpirationDate(), IQueryBuilder::PARAM_DATE)) ->set('note', $qb->createNamedParameter($share->getNote())) + ->set('accepted', $qb->createNamedParameter($share->getStatus())) ->execute(); } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { $qb = $this->dbConn->getQueryBuilder(); @@ -319,6 +332,72 @@ class DefaultShareProvider implements IShareProvider { } /** + * Accept a share. + * + * @param IShare $share + * @param string $recipient + * @return IShare The share object + * @since 9.0.0 + */ + public function acceptShare(IShare $share, string $recipient): IShare { + if ($share->getShareType() === IShare::TYPE_GROUP) { + $group = $this->groupManager->get($share->getSharedWith()); + $user = $this->userManager->get($recipient); + + if (is_null($group)) { + throw new ProviderException('Group "' . $share->getSharedWith() . '" does not exist'); + } + + if (!$group->inGroup($user)) { + throw new ProviderException('Recipient not in receiving group'); + } + + // Try to fetch user specific share + $qb = $this->dbConn->getQueryBuilder(); + $stmt = $qb->select('*') + ->from('share') + ->where($qb->expr()->eq('share_type', $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP))) + ->andWhere($qb->expr()->eq('share_with', $qb->createNamedParameter($recipient))) + ->andWhere($qb->expr()->eq('parent', $qb->createNamedParameter($share->getId()))) + ->andWhere($qb->expr()->orX( + $qb->expr()->eq('item_type', $qb->createNamedParameter('file')), + $qb->expr()->eq('item_type', $qb->createNamedParameter('folder')) + )) + ->execute(); + + $data = $stmt->fetch(); + $stmt->closeCursor(); + + /* + * Check if there already is a user specific group share. + * If there is update it (if required). + */ + if ($data === false) { + $id = $this->createUserSpecificGroupShare($share, $recipient); + } else { + $id = $data['id']; + } + + } else if ($share->getShareType() === IShare::TYPE_USER) { + if ($share->getSharedWith() !== $recipient) { + throw new ProviderException('Recipient does not match'); + } + + $id = $share->getId(); + } else { + throw new ProviderException('Invalid shareType'); + } + + $qb = $this->dbConn->getQueryBuilder(); + $qb->update('share') + ->set('accepted', $qb->createNamedParameter(IShare::STATUS_ACCEPTED)) + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) + ->execute(); + + return $share; + } + + /** * Get all children of this share * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in * @@ -382,13 +461,13 @@ class DefaultShareProvider implements IShareProvider { * Unshare a share from the recipient. If this is a group share * this means we need a special entry in the share db. * - * @param \OCP\Share\IShare $share + * @param IShare $share * @param string $recipient UserId of recipient * @throws BackendError * @throws ProviderException */ - public function deleteFromSelf(\OCP\Share\IShare $share, $recipient) { - if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { + public function deleteFromSelf(IShare $share, $recipient) { + if ($share->getShareType() === IShare::TYPE_GROUP) { $group = $this->groupManager->get($share->getSharedWith()); $user = $this->userManager->get($recipient); @@ -421,37 +500,23 @@ class DefaultShareProvider implements IShareProvider { * If there is update it (if required). */ if ($data === false) { - $qb = $this->dbConn->getQueryBuilder(); - - $type = $share->getNodeType(); - - //Insert new share - $qb->insert('share') - ->values([ - 'share_type' => $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP), - 'share_with' => $qb->createNamedParameter($recipient), - 'uid_owner' => $qb->createNamedParameter($share->getShareOwner()), - 'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()), - 'parent' => $qb->createNamedParameter($share->getId()), - 'item_type' => $qb->createNamedParameter($type), - 'item_source' => $qb->createNamedParameter($share->getNodeId()), - 'file_source' => $qb->createNamedParameter($share->getNodeId()), - 'file_target' => $qb->createNamedParameter($share->getTarget()), - 'permissions' => $qb->createNamedParameter(0), - 'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()), - ])->execute(); - - } else if ($data['permissions'] !== 0) { + $id = $this->createUserSpecificGroupShare($share, $recipient); + $permissions = $share->getPermissions(); + } else { + $permissions = $data['permissions']; + $id = $data['id']; + } + if ($permissions !== 0) { // Update existing usergroup share $qb = $this->dbConn->getQueryBuilder(); $qb->update('share') ->set('permissions', $qb->createNamedParameter(0)) - ->where($qb->expr()->eq('id', $qb->createNamedParameter($data['id']))) + ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))) ->execute(); } - } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { + } else if ($share->getShareType() === IShare::TYPE_USER) { if ($share->getSharedWith() !== $recipient) { throw new ProviderException('Recipient does not match'); @@ -464,6 +529,28 @@ class DefaultShareProvider implements IShareProvider { } } + protected function createUserSpecificGroupShare(IShare $share, string $recipient): int { + $type = $share->getNodeType(); + + $qb = $this->dbConn->getQueryBuilder(); + $qb->insert('share') + ->values([ + 'share_type' => $qb->createNamedParameter(self::SHARE_TYPE_USERGROUP), + 'share_with' => $qb->createNamedParameter($recipient), + 'uid_owner' => $qb->createNamedParameter($share->getShareOwner()), + 'uid_initiator' => $qb->createNamedParameter($share->getSharedBy()), + 'parent' => $qb->createNamedParameter($share->getId()), + 'item_type' => $qb->createNamedParameter($type), + 'item_source' => $qb->createNamedParameter($share->getNodeId()), + 'file_source' => $qb->createNamedParameter($share->getNodeId()), + 'file_target' => $qb->createNamedParameter($share->getTarget()), + 'permissions' => $qb->createNamedParameter($share->getPermissions()), + 'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()), + ])->execute(); + + return $qb->getLastInsertId(); + } + /** * @inheritdoc * @@ -932,6 +1019,7 @@ class DefaultShareProvider implements IShareProvider { ->setTarget($data['file_target']) ->setNote($data['note']) ->setMailSend((bool)$data['mail_send']) + ->setStatus((int)$data['accepted']) ->setLabel($data['label']); $shareTime = new \DateTime(); @@ -1020,6 +1108,7 @@ class DefaultShareProvider implements IShareProvider { while($data = $stmt->fetch()) { $shareMap[$data['parent']]->setPermissions((int)$data['permissions']); + $shareMap[$data['parent']]->setStatus((int)$data['accepted']); $shareMap[$data['parent']]->setTarget($data['file_target']); $shareMap[$data['parent']]->setParent($data['parent']); } @@ -1382,4 +1471,30 @@ class DefaultShareProvider implements IShareProvider { } } + + public function getAllShares(): iterable { + $qb = $this->dbConn->getQueryBuilder(); + + $qb->select('*') + ->from('share') + ->where( + $qb->expr()->orX( + $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share\IShare::TYPE_USER)), + $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share\IShare::TYPE_GROUP)), + $qb->expr()->eq('share_type', $qb->createNamedParameter(\OCP\Share\IShare::TYPE_LINK)) + ) + ); + + $cursor = $qb->execute(); + while($data = $cursor->fetch()) { + try { + $share = $this->createShare($data); + } catch (InvalidShare $e) { + continue; + } + + yield $share; + } + $cursor->closeCursor(); + } } diff --git a/lib/private/Share20/Exception/BackendError.php b/lib/private/Share20/Exception/BackendError.php index 10dae34cc34..f270dd53147 100644 --- a/lib/private/Share20/Exception/BackendError.php +++ b/lib/private/Share20/Exception/BackendError.php @@ -19,6 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Share20\Exception; class BackendError extends \Exception { diff --git a/lib/private/Share20/Exception/InvalidShare.php b/lib/private/Share20/Exception/InvalidShare.php index 1e7337b0418..f4a88ebc188 100644 --- a/lib/private/Share20/Exception/InvalidShare.php +++ b/lib/private/Share20/Exception/InvalidShare.php @@ -19,6 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Share20\Exception; class InvalidShare extends \Exception { diff --git a/lib/private/Share20/Exception/ProviderException.php b/lib/private/Share20/Exception/ProviderException.php index f7ada6d48fd..89bd28a60eb 100644 --- a/lib/private/Share20/Exception/ProviderException.php +++ b/lib/private/Share20/Exception/ProviderException.php @@ -19,10 +19,10 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Share20\Exception; class ProviderException extends \Exception { } - diff --git a/lib/private/Share20/Hooks.php b/lib/private/Share20/Hooks.php index 4519b709a8c..4318fd57cf1 100644 --- a/lib/private/Share20/Hooks.php +++ b/lib/private/Share20/Hooks.php @@ -19,6 +19,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Share20; class Hooks { diff --git a/lib/private/Share20/LegacyHooks.php b/lib/private/Share20/LegacyHooks.php index 4cc748aa418..4554ab64a14 100644 --- a/lib/private/Share20/LegacyHooks.php +++ b/lib/private/Share20/LegacyHooks.php @@ -26,10 +26,10 @@ namespace OC\Share20; use OCP\Files\File; +use OCP\Share; use OCP\Share\IShare; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\GenericEvent; -use OCP\Share; class LegacyHooks { diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php index 2a2c64cf383..db9c704871d 100644 --- a/lib/private/Share20/Manager.php +++ b/lib/private/Share20/Manager.php @@ -55,17 +55,18 @@ use OCP\IUser; use OCP\IUserManager; use OCP\L10N\IFactory; use OCP\Mail\IMailer; +use OCP\Security\Events\ValidatePasswordPolicyEvent; use OCP\Security\IHasher; use OCP\Security\ISecureRandom; +use OCP\Share; use OCP\Share\Exceptions\GenericShareException; use OCP\Share\Exceptions\ShareNotFound; use OCP\Share\IManager; use OCP\Share\IProviderFactory; use OCP\Share\IShare; +use OCP\Share\IShareProvider; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\GenericEvent; -use OCP\Share\IShareProvider; -use OCP\Share; /** * This class is the communication hub for all sharing related operations. @@ -191,8 +192,7 @@ class Manager implements IManager { // Let others verify the password try { - $event = new GenericEvent($password); - $this->eventDispatcher->dispatch('OCP\PasswordPolicy::validate', $event); + $this->eventDispatcher->dispatch(new ValidatePasswordPolicyEvent($password)); } catch (HintException $e) { throw new \Exception($e->getHint()); } @@ -302,6 +302,7 @@ class Manager implements IManager { /* Check if this is an incoming share */ $incomingShares = $this->getSharedWith($share->getSharedBy(), Share::SHARE_TYPE_USER, $userMountPoint, -1, 0); $incomingShares = array_merge($incomingShares, $this->getSharedWith($share->getSharedBy(), Share::SHARE_TYPE_GROUP, $userMountPoint, -1, 0)); + $incomingShares = array_merge($incomingShares, $this->getSharedWith($share->getSharedBy(), Share::SHARE_TYPE_CIRCLE, $userMountPoint, -1, 0)); $incomingShares = array_merge($incomingShares, $this->getSharedWith($share->getSharedBy(), Share::SHARE_TYPE_ROOM, $userMountPoint, -1, 0)); /** @var \OCP\Share\IShare[] $incomingShares */ @@ -359,6 +360,77 @@ class Manager implements IManager { * @throws \InvalidArgumentException * @throws \Exception */ + protected function validateExpirationDateInternal(\OCP\Share\IShare $share) { + $expirationDate = $share->getExpirationDate(); + + if ($expirationDate !== null) { + //Make sure the expiration date is a date + $expirationDate->setTime(0, 0, 0); + + $date = new \DateTime(); + $date->setTime(0, 0, 0); + if ($date >= $expirationDate) { + $message = $this->l->t('Expiration date is in the past'); + throw new GenericShareException($message, $message, 404); + } + } + + // If expiredate is empty set a default one if there is a default + $fullId = null; + try { + $fullId = $share->getFullId(); + } catch (\UnexpectedValueException $e) { + // This is a new share + } + + if ($fullId === null && $expirationDate === null && $this->shareApiInternalDefaultExpireDate()) { + $expirationDate = new \DateTime(); + $expirationDate->setTime(0,0,0); + $expirationDate->add(new \DateInterval('P'.$this->shareApiInternalDefaultExpireDays().'D')); + } + + // If we enforce the expiration date check that is does not exceed + if ($this->shareApiInternalDefaultExpireDateEnforced()) { + if ($expirationDate === null) { + throw new \InvalidArgumentException('Expiration date is enforced'); + } + + $date = new \DateTime(); + $date->setTime(0, 0, 0); + $date->add(new \DateInterval('P' . $this->shareApiInternalDefaultExpireDays() . 'D')); + if ($date < $expirationDate) { + $message = $this->l->t('Can’t set expiration date more than %s days in the future', [$this->shareApiInternalDefaultExpireDays()]); + throw new GenericShareException($message, $message, 404); + } + } + + $accepted = true; + $message = ''; + \OCP\Util::emitHook('\OC\Share', 'verifyExpirationDate', [ + 'expirationDate' => &$expirationDate, + 'accepted' => &$accepted, + 'message' => &$message, + 'passwordSet' => $share->getPassword() !== null, + ]); + + if (!$accepted) { + throw new \Exception($message); + } + + $share->setExpirationDate($expirationDate); + + return $share; + } + + /** + * Validate if the expiration date fits the system settings + * + * @param \OCP\Share\IShare $share The share to validate the expiration date of + * @return \OCP\Share\IShare The modified share object + * @throws GenericShareException + * @throws \InvalidArgumentException + * @throws \Exception + */ protected function validateExpirationDate(\OCP\Share\IShare $share) { $expirationDate = $share->getExpirationDate(); @@ -635,8 +707,16 @@ class Manager implements IManager { //Verify share type if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { $this->userCreateChecks($share); + + //Verify the expiration date + $share = $this->validateExpirationDateInternal($share); + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { $this->groupCreateChecks($share); + + //Verify the expiration date + $share = $this->validateExpirationDateInternal($share); + } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { $this->linkCreateChecks($share); $this->setLinkParent($share); @@ -652,7 +732,7 @@ class Manager implements IManager { ); //Verify the expiration date - $this->validateExpirationDate($share); + $share = $this->validateExpirationDate($share); //Verify the password $this->verifyPassword($share->getPassword()); @@ -849,8 +929,20 @@ class Manager implements IManager { if ($share->getShareType() === \OCP\Share::SHARE_TYPE_USER) { $this->userCreateChecks($share); + + if ($share->getExpirationDate() != $originalShare->getExpirationDate()) { + //Verify the expiration date + $this->validateExpirationDate($share); + $expirationDateUpdated = true; + } } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_GROUP) { $this->groupCreateChecks($share); + + if ($share->getExpirationDate() != $originalShare->getExpirationDate()) { + //Verify the expiration date + $this->validateExpirationDate($share); + $expirationDateUpdated = true; + } } else if ($share->getShareType() === \OCP\Share::SHARE_TYPE_LINK) { $this->linkCreateChecks($share); @@ -928,6 +1020,30 @@ class Manager implements IManager { } /** + * Accept a share. + * + * @param IShare $share + * @param string $recipientId + * @return IShare The share object + * @throws \InvalidArgumentException + * @since 9.0.0 + */ + public function acceptShare(IShare $share, string $recipientId): IShare { + [$providerId, ] = $this->splitFullId($share->getFullId()); + $provider = $this->factory->getProvider($providerId); + + if (!method_exists($provider, 'acceptShare')) { + // TODO FIX ME + throw new \InvalidArgumentException('Share provider does not support accepting'); + } + $provider->acceptShare($share, $recipientId); + $event = new GenericEvent($share); + $this->eventDispatcher->dispatch('OCP\Share::postAcceptShare', $event); + + return $share; + } + + /** * Updates the password of the given share if it is not the same as the * password of the original share. * @@ -1313,8 +1429,7 @@ class Manager implements IManager { } protected function checkExpireDate($share) { - if ($share->getExpirationDate() !== null && - $share->getExpirationDate() <= new \DateTime()) { + if ($share->isExpired()) { $this->deleteShare($share); throw new ShareNotFound($this->l->t('The requested share does not exist anymore')); } @@ -1551,7 +1666,7 @@ class Manager implements IManager { } /** - * Is default expire date enabled + * Is default link expire date enabled * * @return bool */ @@ -1560,7 +1675,7 @@ class Manager implements IManager { } /** - * Is default expire date enforced + * Is default link expire date enforced *` * @return bool */ @@ -1569,9 +1684,9 @@ class Manager implements IManager { $this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes'; } + /** - * Number of default expire days - *shareApiLinkAllowPublicUpload + * Number of default link expire days * @return int */ public function shareApiLinkDefaultExpireDays() { @@ -1579,6 +1694,34 @@ class Manager implements IManager { } /** + * Is default internal expire date enabled + * + * @return bool + */ + public function shareApiInternalDefaultExpireDate(): bool { + return $this->config->getAppValue('core', 'shareapi_default_internal_expire_date', 'no') === 'yes'; + } + + /** + * Is default expire date enforced + *` + * @return bool + */ + public function shareApiInternalDefaultExpireDateEnforced(): bool { + return $this->shareApiInternalDefaultExpireDate() && + $this->config->getAppValue('core', 'shareapi_enforce_internal_expire_date', 'no') === 'yes'; + } + + + /** + * Number of default expire days + * @return int + */ + public function shareApiInternalDefaultExpireDays(): int { + return (int)$this->config->getAppValue('core', 'shareapi_internal_expire_after_n_days', '7'); + } + + /** * Allow public upload on link shares * * @return bool @@ -1672,4 +1815,11 @@ class Manager implements IManager { return true; } + public function getAllShares(): iterable { + $providers = $this->factory->getAllProviders(); + + foreach ($providers as $provider) { + yield from $provider->getAllShares(); + } + } } diff --git a/lib/private/Share20/ProviderFactory.php b/lib/private/Share20/ProviderFactory.php index 6cb6c082df5..dc77a9fbcbb 100644 --- a/lib/private/Share20/ProviderFactory.php +++ b/lib/private/Share20/ProviderFactory.php @@ -26,9 +26,11 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Share20; use OC\CapabilitiesManager; +use OC\Share20\Exception\ProviderException; use OCA\FederatedFileSharing\AddressHandler; use OCA\FederatedFileSharing\FederatedShareProvider; use OCA\FederatedFileSharing\Notifications; @@ -37,9 +39,8 @@ use OCA\FederatedFileSharing\TokenHandler; use OCA\ShareByMail\Settings\SettingsManager; use OCA\ShareByMail\ShareByMailProvider; use OCP\Defaults; -use OCP\Share\IProviderFactory; -use OC\Share20\Exception\ProviderException; use OCP\IServerContainer; +use OCP\Share\IProviderFactory; /** * Class ProviderFactory @@ -60,7 +61,7 @@ class ProviderFactory implements IProviderFactory { private $shareByCircleProvider = null; /** @var bool */ private $circlesAreNotAvailable = false; - /** @var \OCA\Spreed\Share\RoomShareProvider */ + /** @var \OCA\Talk\Share\RoomShareProvider */ private $roomShareProvider = null; /** @@ -112,7 +113,7 @@ class ProviderFactory implements IProviderFactory { /* * TODO: add factory to federated sharing app */ - $l = $this->serverContainer->getL10N('federatedfilessharing'); + $l = $this->serverContainer->getL10N('federatedfilesharing'); $addressHandler = new AddressHandler( $this->serverContainer->getURLGenerator(), $l, @@ -239,7 +240,7 @@ class ProviderFactory implements IProviderFactory { } try { - $this->roomShareProvider = $this->serverContainer->query('\OCA\Spreed\Share\RoomShareProvider'); + $this->roomShareProvider = $this->serverContainer->query('\OCA\Talk\Share\RoomShareProvider'); } catch (\OCP\AppFramework\QueryException $e) { return null; } diff --git a/lib/private/Share20/Share.php b/lib/private/Share20/Share.php index f9b548c1adf..3ac324b40ce 100644 --- a/lib/private/Share20/Share.php +++ b/lib/private/Share20/Share.php @@ -21,6 +21,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\Share20; use OCP\Files\Cache\ICacheEntry; @@ -58,6 +59,8 @@ class Share implements \OCP\Share\IShare { private $shareOwner; /** @var int */ private $permissions; + /** @var int */ + private $status; /** @var string */ private $note = ''; /** @var \DateTime */ @@ -321,6 +324,21 @@ class Share implements \OCP\Share\IShare { /** * @inheritdoc */ + public function setStatus(int $status): IShare { + $this->status = $status; + return $this; + } + + /** + * @inheritdoc + */ + public function getStatus(): int { + return $this->status; + } + + /** + * @inheritdoc + */ public function setNote($note) { $this->note = $note; return $this; @@ -371,6 +389,14 @@ class Share implements \OCP\Share\IShare { /** * @inheritdoc */ + public function isExpired() { + return $this->getExpirationDate() !== null && + $this->getExpirationDate() <= new \DateTime(); + } + + /** + * @inheritdoc + */ public function setSharedBy($sharedBy) { if (!is_string($sharedBy)) { throw new \InvalidArgumentException(); diff --git a/lib/private/Share20/ShareHelper.php b/lib/private/Share20/ShareHelper.php index 5f692c6a62b..f94546d19ed 100644 --- a/lib/private/Share20/ShareHelper.php +++ b/lib/private/Share20/ShareHelper.php @@ -21,6 +21,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ + namespace OC\Share20; use OCP\Files\InvalidPathException; diff --git a/lib/private/SubAdmin.php b/lib/private/SubAdmin.php index f79a0b1ae5a..ec959e0e4ea 100644 --- a/lib/private/SubAdmin.php +++ b/lib/private/SubAdmin.php @@ -30,11 +30,11 @@ namespace OC; use OC\Hooks\PublicEmitter; use OCP\Group\ISubAdmin; -use OCP\IUser; -use OCP\IUserManager; +use OCP\IDBConnection; use OCP\IGroup; use OCP\IGroupManager; -use OCP\IDBConnection; +use OCP\IUser; +use OCP\IUserManager; class SubAdmin extends PublicEmitter implements ISubAdmin { diff --git a/lib/private/SystemConfig.php b/lib/private/SystemConfig.php index 444e5842437..64cc805f9f3 100644 --- a/lib/private/SystemConfig.php +++ b/lib/private/SystemConfig.php @@ -64,13 +64,24 @@ class SystemConfig { ], 'objectstore' => [ 'arguments' => [ - 'password' => true, + // Legacy Swift (https://github.com/nextcloud/server/pull/17696#discussion_r341302207) 'options' => [ 'credentials' => [ 'key' => true, 'secret' => true, ] - ] + ], + // S3 + 'key' => true, + 'secret' => true, + // Swift v2 + 'username' => true, + 'password' => true, + // Swift v3 + 'user' => [ + 'name' => true, + 'password' => true, + ], ], ], ]; diff --git a/lib/private/SystemTag/ManagerFactory.php b/lib/private/SystemTag/ManagerFactory.php index 839a819a04a..ab17ae69ac8 100644 --- a/lib/private/SystemTag/ManagerFactory.php +++ b/lib/private/SystemTag/ManagerFactory.php @@ -21,6 +21,7 @@ declare(strict_types=1); * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC\SystemTag; use OCP\IServerContainer; diff --git a/lib/private/SystemTag/SystemTagManager.php b/lib/private/SystemTag/SystemTagManager.php index 055b744e5c3..85a12da67f2 100644 --- a/lib/private/SystemTag/SystemTagManager.php +++ b/lib/private/SystemTag/SystemTagManager.php @@ -29,14 +29,14 @@ namespace OC\SystemTag; use Doctrine\DBAL\Exception\UniqueConstraintViolationException; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\IDBConnection; +use OCP\IGroupManager; +use OCP\IUser; +use OCP\SystemTag\ISystemTag; use OCP\SystemTag\ISystemTagManager; use OCP\SystemTag\ManagerEvent; use OCP\SystemTag\TagAlreadyExistsException; use OCP\SystemTag\TagNotFoundException; use Symfony\Component\EventDispatcher\EventDispatcherInterface; -use OCP\IGroupManager; -use OCP\SystemTag\ISystemTag; -use OCP\IUser; /** * Manager class for system tags diff --git a/lib/private/SystemTag/SystemTagObjectMapper.php b/lib/private/SystemTag/SystemTagObjectMapper.php index f8a1b03cd01..f1f39fd7d0a 100644 --- a/lib/private/SystemTag/SystemTagObjectMapper.php +++ b/lib/private/SystemTag/SystemTagObjectMapper.php @@ -154,20 +154,26 @@ class SystemTagObjectMapper implements ISystemTagObjectMapper { 'systemtagid' => $query->createParameter('tagid'), ]); + $tagsAssigned = []; foreach ($tagIds as $tagId) { try { $query->setParameter('tagid', $tagId); $query->execute(); + $tagsAssigned[] = $tagId; } catch (UniqueConstraintViolationException $e) { // ignore existing relations } } + if (empty($tagsAssigned)) { + return; + } + $this->dispatcher->dispatch(MapperEvent::EVENT_ASSIGN, new MapperEvent( MapperEvent::EVENT_ASSIGN, $objectType, $objId, - $tagIds + $tagsAssigned )); } diff --git a/lib/private/Tagging/Tag.php b/lib/private/Tagging/Tag.php index de5ab0a485c..f20f22955a3 100644 --- a/lib/private/Tagging/Tag.php +++ b/lib/private/Tagging/Tag.php @@ -23,7 +23,7 @@ namespace OC\Tagging; -use \OCP\AppFramework\Db\Entity; +use OCP\AppFramework\Db\Entity; /** * Class to represent a tag. diff --git a/lib/private/Tagging/TagMapper.php b/lib/private/Tagging/TagMapper.php index e5ca41f69e3..49361ff181a 100644 --- a/lib/private/Tagging/TagMapper.php +++ b/lib/private/Tagging/TagMapper.php @@ -24,9 +24,9 @@ namespace OC\Tagging; -use \OCP\AppFramework\Db\Mapper, - \OCP\AppFramework\Db\DoesNotExistException, - \OCP\IDBConnection; +use OCP\AppFramework\Db\DoesNotExistException; +use OCP\AppFramework\Db\Mapper; +use OCP\IDBConnection; /** * Mapper for Tag entity @@ -76,4 +76,3 @@ class TagMapper extends Mapper { return true; } } - diff --git a/lib/private/TempManager.php b/lib/private/TempManager.php index 6e8683d2f7c..479c41ff7fd 100644 --- a/lib/private/TempManager.php +++ b/lib/private/TempManager.php @@ -30,8 +30,8 @@ namespace OC; -use OCP\ILogger; use OCP\IConfig; +use OCP\ILogger; use OCP\ITempManager; class TempManager implements ITempManager { diff --git a/lib/private/Template/IconsCacher.php b/lib/private/Template/IconsCacher.php index 3c0a270d3f2..75175ad6373 100644 --- a/lib/private/Template/IconsCacher.php +++ b/lib/private/Template/IconsCacher.php @@ -24,14 +24,14 @@ declare (strict_types = 1); namespace OC\Template; +use OC\Files\AppData\Factory; use OCP\AppFramework\Utility\ITimeFactory; use OCP\Files\IAppData; use OCP\Files\NotFoundException; -use OCP\Files\SimpleFS\ISimpleFolder; use OCP\Files\SimpleFS\ISimpleFile; +use OCP\Files\SimpleFS\ISimpleFolder; use OCP\ILogger; use OCP\IURLGenerator; -use OC\Files\AppData\Factory; class IconsCacher { diff --git a/lib/private/Template/JSCombiner.php b/lib/private/Template/JSCombiner.php index 93683753899..224a7a9b213 100644 --- a/lib/private/Template/JSCombiner.php +++ b/lib/private/Template/JSCombiner.php @@ -22,14 +22,15 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ + namespace OC\Template; use OC\SystemConfig; -use OCP\ICache; use OCP\Files\IAppData; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; use OCP\Files\SimpleFS\ISimpleFolder; +use OCP\ICache; use OCP\ICacheFactory; use OCP\ILogger; use OCP\IURLGenerator; diff --git a/lib/private/Template/JSConfigHelper.php b/lib/private/Template/JSConfigHelper.php index 15e2fd76c48..065bf2545e0 100644 --- a/lib/private/Template/JSConfigHelper.php +++ b/lib/private/Template/JSConfigHelper.php @@ -25,6 +25,7 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. * */ + namespace OC\Template; use bantu\IniGetWrapper\IniGetWrapper; @@ -148,6 +149,13 @@ class JSConfigHelper { } $outgoingServer2serverShareEnabled = $this->config->getAppValue('files_sharing', 'outgoing_server2server_share_enabled', 'yes') === 'yes'; + $defaultInternalExpireDateEnabled = $this->config->getAppValue('core', 'shareapi_default_internal_expire_date', 'no') === 'yes'; + $defaultInternalExpireDate = $defaultInternalExpireDateEnforced = null; + if ($defaultInternalExpireDateEnabled) { + $defaultInternalExpireDate = (int) $this->config->getAppValue('core', 'shareapi_internal_expire_after_n_days', '7'); + $defaultInternalExpireDateEnforced = $this->config->getAppValue('core', 'shareapi_internal_enforce_expire_date', 'no') === 'yes'; + } + $countOfDataLocation = 0; $dataLocation = str_replace(\OC::$SERVERROOT .'/', '', $this->config->getSystemValue('datadirectory', ''), $countOfDataLocation); if($countOfDataLocation !== 1 || !$this->groupManager->isAdmin($uid)) { @@ -254,7 +262,10 @@ class JSConfigHelper { 'resharingAllowed' => \OC\Share\Share::isResharingAllowed(), 'remoteShareAllowed' => $outgoingServer2serverShareEnabled, 'federatedCloudShareDoc' => $this->urlGenerator->linkToDocs('user-sharing-federated'), - 'allowGroupSharing' => \OC::$server->getShareManager()->allowGroupSharing() + 'allowGroupSharing' => \OC::$server->getShareManager()->allowGroupSharing(), + 'defaultInternalExpireDateEnabled' => $defaultInternalExpireDateEnabled, + 'defaultInternalExpireDate' => $defaultInternalExpireDate, + 'defaultInternalExpireDateEnforced' => $defaultInternalExpireDateEnforced, ] ]), "_theme" => json_encode([ diff --git a/lib/private/Template/SCSSCacher.php b/lib/private/Template/SCSSCacher.php index 9bdaca3a674..df90fca9817 100644 --- a/lib/private/Template/SCSSCacher.php +++ b/lib/private/Template/SCSSCacher.php @@ -32,7 +32,9 @@ use Leafo\ScssPhp\Compiler; use Leafo\ScssPhp\Exception\ParserException; use Leafo\ScssPhp\Formatter\Crunched; use Leafo\ScssPhp\Formatter\Expanded; +use OC\Files\AppData\Factory; use OC\Memcache\NullCache; +use OC\Template\IconsCacher; use OCP\AppFramework\Utility\ITimeFactory; use OCP\Files\IAppData; use OCP\Files\NotFoundException; @@ -45,8 +47,6 @@ use OCP\IConfig; use OCP\ILogger; use OCP\IMemcache; use OCP\IURLGenerator; -use OC\Files\AppData\Factory; -use OC\Template\IconsCacher; class SCSSCacher { @@ -117,7 +117,7 @@ class SCSSCacher { $this->serverRoot = $serverRoot; $this->cacheFactory = $cacheFactory; $this->depsCache = $cacheFactory->createDistributed('SCSS-deps-' . md5($this->urlGenerator->getBaseUrl())); - $this->isCachedCache = $cacheFactory->createLocal('SCSS-cached-' . md5($this->urlGenerator->getBaseUrl())); + $this->isCachedCache = $cacheFactory->createDistributed('SCSS-cached-' . md5($this->urlGenerator->getBaseUrl())); $lockingCache = $cacheFactory->createDistributed('SCSS-locks-' . md5($this->urlGenerator->getBaseUrl())); if (!($lockingCache instanceof IMemcache)) { $lockingCache = new NullCache(); @@ -269,8 +269,8 @@ class SCSSCacher { private function variablesChanged(): bool { $injectedVariables = $this->getInjectedVariables(); if ($this->config->getAppValue('core', 'theming.variables') !== md5($injectedVariables)) { - $this->resetCache(); $this->config->setAppValue('core', 'theming.variables', md5($injectedVariables)); + $this->resetCache(); return true; } return false; @@ -364,6 +364,9 @@ class SCSSCacher { * We need to regenerate all files when variables change */ public function resetCache() { + if (!$this->lockingCache->add('resetCache', 'locked!', 120)) { + return; + } $this->injectedVariables = null; // do not clear locks @@ -381,6 +384,7 @@ class SCSSCacher { } } $this->logger->debug('SCSSCacher: css cache cleared!'); + $this->lockingCache->remove('resetCache'); } /** diff --git a/lib/private/TemplateLayout.php b/lib/private/TemplateLayout.php index f23ec97119f..e476ddd275d 100644 --- a/lib/private/TemplateLayout.php +++ b/lib/private/TemplateLayout.php @@ -36,13 +36,15 @@ * along with this program. If not, see <http://www.gnu.org/licenses/> * */ + namespace OC; +use OC\AppFramework\Http\Request; use OC\Template\JSCombiner; use OC\Template\JSConfigHelper; use OC\Template\SCSSCacher; use OCP\Defaults; -use OC\AppFramework\Http\Request; +use OCP\Support\Subscription\IRegistry; class TemplateLayout extends \OC_Template { @@ -113,6 +115,8 @@ class TemplateLayout extends \OC_Template { $this->assign('themingInvertMenu', $util->invertTextColor(\OC::$server->getThemingDefaults()->getColorPrimary())); } catch (\OCP\AppFramework\QueryException $e) { $this->assign('themingInvertMenu', false); + } catch (\OCP\AutoloadNotAllowedException $e) { + $this->assign('themingInvertMenu', false); } } else if ($renderAs === 'error') { @@ -132,7 +136,14 @@ class TemplateLayout extends \OC_Template { parent::__construct('core', 'layout.public'); $this->assign( 'appid', $appId ); $this->assign('bodyid', 'body-public'); - $this->assign('showSimpleSignUpLink', $this->config->getSystemValue('simpleSignUpLink.shown', true) !== false); + + /** @var IRegistry $subscription */ + $subscription = \OC::$server->query(IRegistry::class); + $showSimpleSignup = $this->config->getSystemValueBool('simpleSignUpLink.shown', true); + if ($showSimpleSignup && $subscription->delegateHasValidSubscription()) { + $showSimpleSignup = false; + } + $this->assign('showSimpleSignUpLink', $showSimpleSignup); } else { parent::__construct('core', 'layout.base'); @@ -140,7 +151,6 @@ class TemplateLayout extends \OC_Template { // Send the language and the locale to our layouts $lang = \OC::$server->getL10NFactory()->findLanguage(); $locale = \OC::$server->getL10NFactory()->findLocale($lang); - $localeLang = \OC::$server->getL10NFactory()->findLanguageFromLocale('lib', $locale); $lang = str_replace('_', '-', $lang); $this->assign('language', $lang); @@ -162,7 +172,7 @@ class TemplateLayout extends \OC_Template { if ($this->config->getSystemValue('installed', false) && $renderAs != 'error') { if (\OC::$server->getContentSecurityPolicyNonceManager()->browserSupportsCspV3()) { $jsConfigHelper = new JSConfigHelper( - \OC::$server->getL10N('lib', $localeLang ?: $lang), + \OC::$server->getL10N('lib'), \OC::$server->query(Defaults::class), \OC::$server->getAppManager(), \OC::$server->getSession(), diff --git a/lib/private/URLGenerator.php b/lib/private/URLGenerator.php index 167690f3a6e..1fb69031f03 100644 --- a/lib/private/URLGenerator.php +++ b/lib/private/URLGenerator.php @@ -94,8 +94,9 @@ class URLGenerator implements IURLGenerator { public function linkToOCSRouteAbsolute(string $routeName, array $arguments = []): string { $route = \OC::$server->getRouter()->generate('ocs.'.$routeName, $arguments, false); - if (strpos($route, '/index.php') === 0) { - $route = substr($route, 10); + $indexPhpPos = strpos($route, '/index.php/'); + if ($indexPhpPos !== false) { + $route = substr($route, $indexPhpPos + 10); } $route = substr($route, 7); diff --git a/lib/private/Updater/VersionCheck.php b/lib/private/Updater/VersionCheck.php index 3cbd7061fe0..079ffe7ff44 100644 --- a/lib/private/Updater/VersionCheck.php +++ b/lib/private/Updater/VersionCheck.php @@ -33,7 +33,7 @@ class VersionCheck { /** @var IClientService */ private $clientService; - + /** @var IConfig */ private $config; @@ -54,6 +54,11 @@ class VersionCheck { * @return array|bool */ public function check() { + // If this server is set to have no internet connection this is all not needed + if (!$this->config->getSystemValueBool('has_internet_connection', true)) { + return false; + } + // Look up the cache - it is invalidated all 30 minutes if (((int)$this->config->getAppValue('core', 'lastupdatedat') + 1800) > time()) { return json_decode($this->config->getAppValue('core', 'lastupdateResult'), true); @@ -122,4 +127,3 @@ class VersionCheck { return $response->getBody(); } } - diff --git a/lib/private/User/Backend.php b/lib/private/User/Backend.php index 30ed962555d..2b9b4d27ea3 100644 --- a/lib/private/User/Backend.php +++ b/lib/private/User/Backend.php @@ -23,7 +23,7 @@ namespace OC\User; -use \OCP\UserInterface; +use OCP\UserInterface; /** * Abstract base class for user management. Provides methods for querying backend diff --git a/lib/private/User/Database.php b/lib/private/User/Database.php index 3db96fa02e2..a4c35deb2b8 100644 --- a/lib/private/User/Database.php +++ b/lib/private/User/Database.php @@ -58,7 +58,9 @@ declare(strict_types=1); namespace OC\User; use OC\Cache\CappedMemoryCache; +use OCP\EventDispatcher\IEventDispatcher; use OCP\IDBConnection; +use OCP\Security\Events\ValidatePasswordPolicyEvent; use OCP\User\Backend\ABackend; use OCP\User\Backend\ICheckPasswordBackend; use OCP\User\Backend\ICountUsersBackend; @@ -68,7 +70,6 @@ use OCP\User\Backend\IGetHomeBackend; use OCP\User\Backend\IGetRealUIDBackend; use OCP\User\Backend\ISetDisplayNameBackend; use OCP\User\Backend\ISetPasswordBackend; -use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\GenericEvent; /** @@ -86,7 +87,7 @@ class Database extends ABackend /** @var CappedMemoryCache */ private $cache; - /** @var EventDispatcherInterface */ + /** @var IEventDispatcher */ private $eventDispatcher; /** @var IDBConnection */ @@ -98,13 +99,13 @@ class Database extends ABackend /** * \OC\User\Database constructor. * - * @param EventDispatcherInterface $eventDispatcher + * @param IEventDispatcher $eventDispatcher * @param string $table */ public function __construct($eventDispatcher = null, $table = 'users') { $this->cache = new CappedMemoryCache(); $this->table = $table; - $this->eventDispatcher = $eventDispatcher ? $eventDispatcher : \OC::$server->getEventDispatcher(); + $this->eventDispatcher = $eventDispatcher ? $eventDispatcher : \OC::$server->query(IEventDispatcher::class); } /** @@ -130,8 +131,7 @@ class Database extends ABackend $this->fixDI(); if (!$this->userExists($uid)) { - $event = new GenericEvent($password); - $this->eventDispatcher->dispatch('OCP\PasswordPolicy::validate', $event); + $this->eventDispatcher->dispatchTyped(new ValidatePasswordPolicyEvent($password)); $qb = $this->dbConn->getQueryBuilder(); $qb->insert($this->table) @@ -199,8 +199,7 @@ class Database extends ABackend $this->fixDI(); if ($this->userExists($uid)) { - $event = new GenericEvent($password); - $this->eventDispatcher->dispatch('OCP\PasswordPolicy::validate', $event); + $this->eventDispatcher->dispatchTyped(new ValidatePasswordPolicyEvent($password)); $hasher = \OC::$server->getHasher(); $hashedPassword = $hasher->hash($password); @@ -259,6 +258,8 @@ class Database extends ABackend * @return array an array of all displayNames (value) and the corresponding uids (key) */ public function getDisplayNames($search = '', $limit = null, $offset = null) { + $limit = $this->fixLimit($limit); + $this->fixDI(); $query = $this->dbConn->getQueryBuilder(); @@ -380,6 +381,8 @@ class Database extends ABackend * @return string[] an array of all uids */ public function getUsers($search = '', $limit = null, $offset = null) { + $limit = $this->fixLimit($limit); + $users = $this->getDisplayNames($search, $limit, $offset); $userIds = array_map(function ($uid) { return (string)$uid; @@ -485,5 +488,11 @@ class Database extends ABackend return $this->cache[$uid]['uid']; } + private function fixLimit($limit) { + if (is_int($limit) && $limit >= 0) { + return $limit; + } + return null; + } } diff --git a/lib/private/User/Manager.php b/lib/private/User/Manager.php index 7f9691073c9..29cae3da79b 100644 --- a/lib/private/User/Manager.php +++ b/lib/private/User/Manager.php @@ -33,12 +33,15 @@ namespace OC\User; use OC\Hooks\PublicEmitter; use OCP\DB\QueryBuilder\IQueryBuilder; -use OCP\IUser; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\IConfig; use OCP\IGroup; +use OCP\IUser; use OCP\IUserBackend; use OCP\IUserManager; -use OCP\IConfig; use OCP\User\Backend\IGetRealUIDBackend; +use OCP\User\Events\CreateUserEvent; +use OCP\User\Events\UserCreatedEvent; use OCP\UserInterface; use Symfony\Component\EventDispatcher\EventDispatcherInterface; @@ -72,17 +75,24 @@ class Manager extends PublicEmitter implements IUserManager { /** @var IConfig */ private $config; + /** @var EventDispatcherInterface */ private $dispatcher; - public function __construct(IConfig $config, EventDispatcherInterface $dispatcher) { + /** @var IEventDispatcher */ + private $eventDispatcher; + + public function __construct(IConfig $config, + EventDispatcherInterface $oldDispatcher, + IEventDispatcher $eventDispatcher) { $this->config = $config; - $this->dispatcher = $dispatcher; + $this->dispatcher = $oldDispatcher; $cachedUsers = &$this->cachedUsers; $this->listen('\OC\User', 'postDelete', function ($user) use (&$cachedUsers) { /** @var \OC\User\User $user */ unset($cachedUsers[$user->getUID()]); }); + $this->eventDispatcher = $eventDispatcher; } /** @@ -349,6 +359,7 @@ class Manager extends PublicEmitter implements IUserManager { } $this->emit('\OC\User', 'preCreateUser', [$uid, $password]); + $this->eventDispatcher->dispatchTyped(new CreateUserEvent($uid, $password)); $state = $backend->createUser($uid, $password); if($state === false) { throw new \InvalidArgumentException($l->t('Could not create user')); @@ -356,6 +367,7 @@ class Manager extends PublicEmitter implements IUserManager { $user = $this->getUserObject($uid, $backend); if ($user instanceof IUser) { $this->emit('\OC\User', 'postCreateUser', [$user, $password]); + $this->eventDispatcher->dispatchTyped(new UserCreatedEvent($user, $password)); } return $user; } @@ -460,11 +472,11 @@ class Manager extends PublicEmitter implements IUserManager { ->andWhere($queryBuilder->expr()->eq('configkey', $queryBuilder->createNamedParameter('enabled'))) ->andWhere($queryBuilder->expr()->eq('configvalue', $queryBuilder->createNamedParameter('false'), IQueryBuilder::PARAM_STR)); - + $result = $queryBuilder->execute(); $count = $result->fetchColumn(); $result->closeCursor(); - + if ($count !== false) { $count = (int)$count; } else { @@ -494,7 +506,7 @@ class Manager extends PublicEmitter implements IUserManager { $result = $queryBuilder->execute(); $count = $result->fetchColumn(); $result->closeCursor(); - + if ($count !== false) { $count = (int)$count; } else { diff --git a/lib/private/User/NoUserException.php b/lib/private/User/NoUserException.php index a80bf7dc54b..b72fb6a9e25 100644 --- a/lib/private/User/NoUserException.php +++ b/lib/private/User/NoUserException.php @@ -22,4 +22,4 @@ namespace OC\User; -class NoUserException extends \Exception {}
\ No newline at end of file +class NoUserException extends \Exception {} diff --git a/lib/private/User/Session.php b/lib/private/User/Session.php index 13519d97ef4..3b0231aea03 100644 --- a/lib/private/User/Session.php +++ b/lib/private/User/Session.php @@ -61,6 +61,7 @@ use OCP\IUserSession; use OCP\Lockdown\ILockdownManager; use OCP\Security\ISecureRandom; use OCP\Session\Exceptions\SessionNotAvailableException; +use OCP\User\Events\PostLoginEvent; use OCP\Util; use Symfony\Component\EventDispatcher\GenericEvent; @@ -315,6 +316,29 @@ class Session implements IUserSession, Emitter { } /** + * @return mixed + */ + public function getImpersonatingUserID(): ?string { + + return $this->session->get('oldUserId'); + + } + + public function setImpersonatingUserID(bool $useCurrentUser = true): void { + if ($useCurrentUser === false) { + $this->session->remove('oldUserId'); + return; + } + + $currentUser = $this->getUser(); + + if ($currentUser === null) { + throw new \OC\User\NoUserException(); + } + $this->session->set('oldUserId', $currentUser->getUID()); + + } + /** * set the token id * * @param int|null $token that was used to log in @@ -375,13 +399,11 @@ class Session implements IUserSession, Emitter { $firstTimeLogin = $user->updateLastLoginTimestamp(); } - $postLoginEvent = new OC\User\Events\PostLoginEvent( + $this->dispatcher->dispatchTyped(new PostLoginEvent( $user, $loginDetails['password'], $isToken - ); - $this->dispatcher->dispatch(OC\User\Events\PostLoginEvent::class, $postLoginEvent); - + )); $this->manager->emit('\OC\User', 'postLogin', [ $user, $loginDetails['password'], @@ -838,7 +860,7 @@ class Session implements IUserSession, Emitter { try { $sessionId = $this->session->getId(); - $this->tokenProvider->renewSessionToken($oldSessionId, $sessionId); + $token = $this->tokenProvider->renewSessionToken($oldSessionId, $sessionId); } catch (SessionNotAvailableException $ex) { return false; } catch (InvalidTokenException $ex) { @@ -847,7 +869,6 @@ class Session implements IUserSession, Emitter { } $this->setMagicInCookie($user->getUID(), $newToken); - $token = $this->tokenProvider->getToken($sessionId); //login $this->setUser($user); diff --git a/lib/private/User/User.php b/lib/private/User/User.php index 12af787a5a6..96f0f38b82a 100644 --- a/lib/private/User/User.php +++ b/lib/private/User/User.php @@ -36,12 +36,12 @@ use OC\Files\Cache\Storage; use OC\Hooks\Emitter; use OC_Helper; use OCP\IAvatarManager; +use OCP\IConfig; use OCP\IImage; use OCP\IURLGenerator; use OCP\IUser; -use OCP\IConfig; +use OCP\IUserBackend; use OCP\UserInterface; -use \OCP\IUserBackend; use Symfony\Component\EventDispatcher\EventDispatcherInterface; use Symfony\Component\EventDispatcher\GenericEvent; diff --git a/lib/private/legacy/app.php b/lib/private/legacy/app.php index 58b617aae45..146262c3f95 100644 --- a/lib/private/legacy/app.php +++ b/lib/private/legacy/app.php @@ -55,6 +55,7 @@ use OC\App\Platform; use OC\DB\MigrationService; use OC\Installer; use OC\Repair; +use OC\ServerNotAvailableException; use OCP\App\ManagerEvent; use OCP\ILogger; @@ -153,6 +154,9 @@ class OC_App { try { self::requireAppFile($app); } catch (Throwable $ex) { + if($ex instanceof ServerNotAvailableException) { + throw $ex; + } \OC::$server->getLogger()->logException($ex); if (!\OC::$server->getAppManager()->isShipped($app)) { // Only disable apps which are not shipped @@ -485,6 +489,7 @@ class OC_App { * * @param string $appId * @return string|false + * @deprecated 11.0.0 use \OC::$server->getAppManager()->getAppPath() */ public static function getAppPath(string $appId) { if ($appId === null || trim($appId) === '') { @@ -503,6 +508,7 @@ class OC_App { * * @param string $appId * @return string|false + * @deprecated 18.0.0 use \OC::$server->getAppManager()->getAppWebPath() */ public static function getAppWebPath(string $appId) { if (($dir = self::findAppInDirectories($appId)) != false) { diff --git a/lib/private/legacy/defaults.php b/lib/private/legacy/defaults.php index d313366abe7..8633113ba5a 100644 --- a/lib/private/legacy/defaults.php +++ b/lib/private/legacy/defaults.php @@ -52,7 +52,6 @@ class OC_Defaults { private $defaultTextColorPrimary; public function __construct() { - $l10n = \OC::$server->getL10N('lib'); $config = \OC::$server->getConfig(); $this->defaultEntity = 'Nextcloud'; /* e.g. company name, used for footers and copyright notices */ @@ -65,7 +64,6 @@ class OC_Defaults { $this->defaultAndroidClientUrl = $config->getSystemValue('customclient_android', 'https://play.google.com/store/apps/details?id=com.nextcloud.client'); $this->defaultDocBaseUrl = 'https://docs.nextcloud.com'; $this->defaultDocVersion = \OC_Util::getVersion()[0]; // used to generate doc links - $this->defaultSlogan = $l10n->t('a safe home for all your data'); $this->defaultColorPrimary = '#0082c9'; $this->defaultTextColorPrimary = '#ffffff'; @@ -219,6 +217,10 @@ class OC_Defaults { if ($this->themeExist('getSlogan')) { return $this->theme->getSlogan(); } else { + if ($this->defaultSlogan === null) { + $l10n = \OC::$server->getL10N('lib'); + $this->defaultSlogan = $l10n->t('a safe home for all your data'); + } return $this->defaultSlogan; } } diff --git a/lib/private/legacy/eventsource.php b/lib/private/legacy/eventsource.php index 1e68dd24af0..aa3d3395443 100644 --- a/lib/private/legacy/eventsource.php +++ b/lib/private/legacy/eventsource.php @@ -27,7 +27,7 @@ */ /** - * wrapper for server side events (http://en.wikipedia.org/wiki/Server-sent_events) + * wrapper for server side events (https://en.wikipedia.org/wiki/Server-sent_events) * includes a fallback for older browsers and IE * * use server side events with caution, to many open requests can hang the server diff --git a/lib/private/legacy/files.php b/lib/private/legacy/files.php index 577392f1edc..140c02e77b6 100644 --- a/lib/private/legacy/files.php +++ b/lib/private/legacy/files.php @@ -177,10 +177,21 @@ class OC_Files { foreach ($files as $file) { $file = $dir . '/' . $file; if (\OC\Files\Filesystem::is_file($file)) { - $fileSize = \OC\Files\Filesystem::filesize($file); - $fileTime = \OC\Files\Filesystem::filemtime($file); - $fh = \OC\Files\Filesystem::fopen($file, 'r'); - $streamer->addFileFromStream($fh, basename($file), $fileSize, $fileTime); + $userFolder = \OC::$server->getRootFolder()->get(\OC\Files\Filesystem::getRoot()); + $file = $userFolder->get($file); + if($file instanceof \OC\Files\Node\File) { + $fh = $file->fopen('r'); + $fileSize = $file->getSize(); + $fileTime = $file->getMTime(); + } else { + // File is not a file? … + \OC::$server->getLogger()->debug( + 'File given, but no Node available. Name {file}', + [ 'app' => 'files', 'file' => $file ] + ); + continue; + } + $streamer->addFileFromStream($fh, $file->getName(), $fileSize, $fileTime); fclose($fh); } elseif (\OC\Files\Filesystem::is_dir($file)) { $streamer->addDirRecursive($file); diff --git a/lib/private/legacy/helper.php b/lib/private/legacy/helper.php index a5ed3a3e24b..3c4915ae748 100644 --- a/lib/private/legacy/helper.php +++ b/lib/private/legacy/helper.php @@ -189,6 +189,7 @@ class OC_Helper { } /** + * @deprecated 18.0.0 * @return \OC\Files\Type\TemplateManager */ static public function getFileTemplateManager() { @@ -585,8 +586,13 @@ class OC_Helper { $relative = 0; } - return array('free' => $free, 'used' => $used, 'total' => $total, 'relative' => $relative); - + return [ + 'free' => $free, + 'used' => $used, + 'total' => $total, + 'relative' => $relative, + 'quota' => $quota + ]; } /** diff --git a/lib/private/legacy/image.php b/lib/private/legacy/image.php index d9af45f2226..2efde2ee688 100644 --- a/lib/private/legacy/image.php +++ b/lib/private/legacy/image.php @@ -878,10 +878,8 @@ class OC_Image implements \OCP\IImage { $widthOrig = imagesx($this->resource); $heightOrig = imagesy($this->resource); $process = imagecreatetruecolor($width, $height); - - if ($process == false) { + if ($process === false) { $this->logger->error(__METHOD__ . '(): Error creating true color image', array('app' => 'core')); - imagedestroy($process); return false; } @@ -892,8 +890,8 @@ class OC_Image implements \OCP\IImage { imagesavealpha($process, true); } - imagecopyresampled($process, $this->resource, 0, 0, 0, 0, $width, $height, $widthOrig, $heightOrig); - if ($process == false) { + $res = imagecopyresampled($process, $this->resource, 0, 0, 0, 0, $width, $height, $widthOrig, $heightOrig); + if ($res === false) { $this->logger->error(__METHOD__ . '(): Error re-sampling process image', array('app' => 'core')); imagedestroy($process); return false; diff --git a/lib/private/legacy/template.php b/lib/private/legacy/template.php index 6d9bf7c99f4..26b1b360adc 100644 --- a/lib/private/legacy/template.php +++ b/lib/private/legacy/template.php @@ -266,7 +266,7 @@ class OC_Template extends \OC\Template\Base { * @return bool */ public static function printGuestPage( $application, $name, $parameters = array() ) { - $content = new OC_Template( $application, $name, "guest" ); + $content = new OC_Template($application, $name, $name === 'error' ? $name : 'guest'); foreach( $parameters as $key => $value ) { $content->assign( $key, $value ); } diff --git a/lib/private/legacy/util.php b/lib/private/legacy/util.php index 810f22fb9e5..f73756dd240 100644 --- a/lib/private/legacy/util.php +++ b/lib/private/legacy/util.php @@ -61,11 +61,11 @@ * */ +use OC\AppFramework\Http\Request; use OCP\IConfig; use OCP\IGroupManager; use OCP\ILogger; use OCP\IUser; -use OC\AppFramework\Http\Request; class OC_Util { public static $scripts = array(); @@ -1249,6 +1249,18 @@ class OC_Util { $content = false; } + if (strpos($url, 'https:') === 0) { + $url = 'http:' . substr($url, 6); + } else { + $url = 'https:' . substr($url, 5); + } + + try { + $fallbackContent = \OC::$server->getHTTPClientService()->newClient()->get($url)->getBody(); + } catch (\Exception $e) { + $fallbackContent = false; + } + // cleanup @unlink($testFile); @@ -1256,7 +1268,7 @@ class OC_Util { * If the content is not equal to test content our .htaccess * is working as required */ - return $content !== $testContent; + return $content !== $testContent && $fallbackContent !== $testContent; } /** |