diff options
Diffstat (limited to 'lib/private/legacy')
-rw-r--r-- | lib/private/legacy/OC_API.php | 185 | ||||
-rw-r--r-- | lib/private/legacy/OC_App.php | 610 | ||||
-rw-r--r-- | lib/private/legacy/OC_Defaults.php | 78 | ||||
-rw-r--r-- | lib/private/legacy/OC_EventSource.php | 125 | ||||
-rw-r--r-- | lib/private/legacy/OC_FileChunking.php | 184 | ||||
-rw-r--r-- | lib/private/legacy/OC_Files.php | 459 | ||||
-rw-r--r-- | lib/private/legacy/OC_Helper.php | 412 | ||||
-rw-r--r-- | lib/private/legacy/OC_Hook.php | 42 | ||||
-rw-r--r-- | lib/private/legacy/OC_Image.php | 1152 | ||||
-rw-r--r-- | lib/private/legacy/OC_JSON.php | 70 | ||||
-rw-r--r-- | lib/private/legacy/OC_Response.php | 50 | ||||
-rw-r--r-- | lib/private/legacy/OC_Template.php | 342 | ||||
-rw-r--r-- | lib/private/legacy/OC_User.php | 152 | ||||
-rw-r--r-- | lib/private/legacy/OC_Util.php | 563 | ||||
-rw-r--r-- | lib/private/legacy/template/functions.php | 339 |
15 files changed, 452 insertions, 4311 deletions
diff --git a/lib/private/legacy/OC_API.php b/lib/private/legacy/OC_API.php deleted file mode 100644 index 275e02986c4..00000000000 --- a/lib/private/legacy/OC_API.php +++ /dev/null @@ -1,185 +0,0 @@ -<?php -/** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Michael Gapczynski <GapczynskiM@gmail.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Tom Needham <tom@owncloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ -use OCP\API; -use OCP\AppFramework\Http; - -class OC_API { - /** - * api actions - */ - protected static $actions = []; - - /** - * respond to a call - * @param \OC\OCS\Result $result - * @param string $format the format xml|json - * @psalm-taint-escape html - */ - public static function respond($result, $format = 'xml') { - $request = \OC::$server->getRequest(); - - // Send 401 headers if unauthorised - if ($result->getStatusCode() === \OCP\AppFramework\OCSController::RESPOND_UNAUTHORISED) { - // If request comes from JS return dummy auth request - if ($request->getHeader('X-Requested-With') === 'XMLHttpRequest') { - header('WWW-Authenticate: DummyBasic realm="Authorisation Required"'); - } else { - header('WWW-Authenticate: Basic realm="Authorisation Required"'); - } - http_response_code(401); - } - - foreach ($result->getHeaders() as $name => $value) { - header($name . ': ' . $value); - } - - $meta = $result->getMeta(); - $data = $result->getData(); - if (self::isV2($request)) { - $statusCode = self::mapStatusCodes($result->getStatusCode()); - if (!is_null($statusCode)) { - $meta['statuscode'] = $statusCode; - http_response_code($statusCode); - } - } - - self::setContentType($format); - $body = self::renderResult($format, $meta, $data); - echo $body; - } - - /** - * @param XMLWriter $writer - */ - private static function toXML($array, $writer) { - foreach ($array as $k => $v) { - if ($k[0] === '@') { - $writer->writeAttribute(substr($k, 1), $v); - continue; - } elseif (is_numeric($k)) { - $k = 'element'; - } - if (is_array($v)) { - $writer->startElement($k); - self::toXML($v, $writer); - $writer->endElement(); - } else { - $writer->writeElement($k, $v); - } - } - } - - public static function requestedFormat(): string { - $formats = ['json', 'xml']; - - $format = (isset($_GET['format']) && is_string($_GET['format']) && in_array($_GET['format'], $formats)) ? $_GET['format'] : 'xml'; - return $format; - } - - /** - * Based on the requested format the response content type is set - * @param string $format - */ - public static function setContentType($format = null) { - $format = is_null($format) ? self::requestedFormat() : $format; - if ($format === 'xml') { - header('Content-type: text/xml; charset=UTF-8'); - return; - } - - if ($format === 'json') { - header('Content-Type: application/json; charset=utf-8'); - return; - } - - header('Content-Type: application/octet-stream; charset=utf-8'); - } - - /** - * @param \OCP\IRequest $request - * @return bool - */ - protected static function isV2(\OCP\IRequest $request) { - $script = $request->getScriptName(); - - return substr($script, -11) === '/ocs/v2.php'; - } - - /** - * @param integer $sc - * @return int - */ - public static function mapStatusCodes($sc) { - switch ($sc) { - case \OCP\AppFramework\OCSController::RESPOND_NOT_FOUND: - return Http::STATUS_NOT_FOUND; - case \OCP\AppFramework\OCSController::RESPOND_SERVER_ERROR: - return Http::STATUS_INTERNAL_SERVER_ERROR; - case \OCP\AppFramework\OCSController::RESPOND_UNKNOWN_ERROR: - return Http::STATUS_INTERNAL_SERVER_ERROR; - case \OCP\AppFramework\OCSController::RESPOND_UNAUTHORISED: - // already handled for v1 - return null; - case 100: - return Http::STATUS_OK; - } - // any 2xx, 4xx and 5xx will be used as is - if ($sc >= 200 && $sc < 600) { - return $sc; - } - - return Http::STATUS_BAD_REQUEST; - } - - /** - * @param string $format - * @return string - */ - public static function renderResult($format, $meta, $data) { - $response = [ - 'ocs' => [ - 'meta' => $meta, - 'data' => $data, - ], - ]; - if ($format == 'json') { - return OC_JSON::encode($response); - } - - $writer = new XMLWriter(); - $writer->openMemory(); - $writer->setIndent(true); - $writer->startDocument(); - self::toXML($response, $writer); - $writer->endDocument(); - return $writer->outputMemory(true); - } -} diff --git a/lib/private/legacy/OC_App.php b/lib/private/legacy/OC_App.php index c4c2f089767..10b78e2a7ef 100644 --- a/lib/private/legacy/OC_App.php +++ b/lib/private/legacy/OC_App.php @@ -1,80 +1,36 @@ <?php declare(strict_types=1); - /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * @copyright Copyright (c) 2016, Lukas Reschke <lukas@statuscode.ch> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Borjan Tchakaloff <borjan@tchakaloff.fr> - * @author Brice Maron <brice@bmaron.net> - * @author Christopher Schäpers <kondou@ts.unde.re> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Rudolf <github.com@daniel-rudolf.de> - * @author Frank Karlitschek <frank@karlitschek.de> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Jakob Sack <mail@jakobsack.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Julius Haertl <jus@bitgrid.net> - * @author Julius Härtl <jus@bitgrid.net> - * @author Kamil Domanski <kdomanski@kdemail.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Markus Goetz <markus@woboq.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author RealRancor <Fisch.666@gmx.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Sam Tuke <mail@samtuke.com> - * @author Sebastian Wessalowski <sebastian@wessalowski.org> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Thomas Tanghus <thomas@tanghus.net> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ -use OCP\AppFramework\QueryException; -use OCP\App\ManagerEvent; -use OCP\Authentication\IAlternativeLogin; -use OCP\ILogger; -use OCP\Settings\IManager as ISettingsManager; -use OC\AppFramework\Bootstrap\Coordinator; use OC\App\DependencyAnalyzer; use OC\App\Platform; +use OC\AppFramework\Bootstrap\Coordinator; +use OC\Config\ConfigManager; use OC\DB\MigrationService; use OC\Installer; use OC\Repair; use OC\Repair\Events\RepairErrorEvent; -use OC\ServerNotAvailableException; +use OCP\App\Events\AppUpdateEvent; +use OCP\App\IAppManager; +use OCP\App\ManagerEvent; +use OCP\Authentication\IAlternativeLogin; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\IAppConfig; +use OCP\Server; +use Psr\Container\ContainerExceptionInterface; use Psr\Log\LoggerInterface; +use function OCP\Log\logger; /** * This class manages the apps. It allows them to register and integrate in the - * ownCloud ecosystem. Furthermore, this class is responsible for installing, + * Nextcloud ecosystem. Furthermore, this class is responsible for installing, * upgrading and removing apps. */ class OC_App { - private static $adminForms = []; - private static $personalForms = []; - private static $appTypes = []; - private static $loadedApps = []; private static $altLogin = []; private static $alreadyRegistered = []; public const supportedApp = 300; @@ -85,12 +41,13 @@ class OC_App { * * @psalm-taint-escape file * @psalm-taint-escape include + * @psalm-taint-escape html + * @psalm-taint-escape has_quotes * - * @param string $app AppId that needs to be cleaned - * @return string + * @deprecated 31.0.0 use IAppManager::cleanAppId */ public static function cleanAppId(string $app): string { - return str_replace(['\0', '/', '\\', '..'], '', $app); + return str_replace(['<', '>', '"', "'", '\0', '/', '\\', '..'], '', $app); } /** @@ -98,9 +55,10 @@ class OC_App { * * @param string $app * @return bool + * @deprecated 27.0.0 use IAppManager::isAppLoaded */ public static function isAppLoaded(string $app): bool { - return isset(self::$loadedApps[$app]); + return \OC::$server->get(IAppManager::class)->isAppLoaded($app); } /** @@ -109,47 +67,20 @@ class OC_App { * @param string[] $types * @return bool * - * This function walks through the ownCloud directory and loads all apps + * This function walks through the Nextcloud directory and loads all apps * it can find. A directory contains an app if the file /appinfo/info.xml * exists. * * if $types is set to non-empty array, only apps of those types will be loaded + * + * @deprecated 29.0.0 use IAppManager::loadApps instead */ public static function loadApps(array $types = []): bool { - if ((bool) \OC::$server->getSystemConfig()->getValue('maintenance', false)) { + if (!\OC::$server->getSystemConfig()->getValue('installed', false)) { + // This should be done before calling this method so that appmanager can be used return false; } - // Load the enabled apps here - $apps = self::getEnabledApps(); - - // Add each apps' folder as allowed class path - foreach ($apps as $app) { - // If the app is already loaded then autoloading it makes no sense - if (!isset(self::$loadedApps[$app])) { - $path = self::getAppPath($app); - if ($path !== false) { - self::registerAutoloading($app, $path); - } - } - } - - // prevent app.php from printing output - ob_start(); - foreach ($apps as $app) { - if (!isset(self::$loadedApps[$app]) && ($types === [] || self::isType($app, $types))) { - try { - self::loadApp($app); - } catch (\Throwable $e) { - \OC::$server->get(LoggerInterface::class)->emergency('Error during app loading: ' . $e->getMessage(), [ - 'exception' => $e, - 'app' => $app, - ]); - } - } - } - ob_end_clean(); - - return true; + return \OC::$server->get(IAppManager::class)->loadApps($types); } /** @@ -157,110 +88,10 @@ class OC_App { * * @param string $app * @throws Exception + * @deprecated 27.0.0 use IAppManager::loadApp */ - public static function loadApp(string $app) { - self::$loadedApps[$app] = true; - $appPath = self::getAppPath($app); - if ($appPath === false) { - return; - } - - // in case someone calls loadApp() directly - self::registerAutoloading($app, $appPath); - - /** @var Coordinator $coordinator */ - $coordinator = \OC::$server->query(Coordinator::class); - $isBootable = $coordinator->isBootable($app); - - $hasAppPhpFile = is_file($appPath . '/appinfo/app.php'); - - \OC::$server->getEventLogger()->start('bootstrap:load_app_' . $app, 'Load app: ' . $app); - if ($isBootable && $hasAppPhpFile) { - \OC::$server->getLogger()->error('/appinfo/app.php is not loaded when \OCP\AppFramework\Bootstrap\IBootstrap on the application class is used. Migrate everything from app.php to the Application class.', [ - 'app' => $app, - ]); - } elseif ($hasAppPhpFile) { - \OC::$server->getLogger()->debug('/appinfo/app.php is deprecated, use \OCP\AppFramework\Bootstrap\IBootstrap on the application class instead.', [ - 'app' => $app, - ]); - try { - self::requireAppFile($appPath); - } catch (Throwable $ex) { - if ($ex instanceof ServerNotAvailableException) { - throw $ex; - } - if (!\OC::$server->getAppManager()->isShipped($app) && !self::isType($app, ['authentication'])) { - \OC::$server->getLogger()->logException($ex, [ - 'message' => "App $app threw an error during app.php load and will be disabled: " . $ex->getMessage(), - ]); - - // Only disable apps which are not shipped and that are not authentication apps - \OC::$server->getAppManager()->disableApp($app, true); - } else { - \OC::$server->getLogger()->logException($ex, [ - 'message' => "App $app threw an error during app.php load: " . $ex->getMessage(), - ]); - } - } - } - \OC::$server->getEventLogger()->end('bootstrap:load_app_' . $app); - - $coordinator->bootApp($app); - - $info = self::getAppInfo($app); - if (!empty($info['activity']['filters'])) { - foreach ($info['activity']['filters'] as $filter) { - \OC::$server->getActivityManager()->registerFilter($filter); - } - } - if (!empty($info['activity']['settings'])) { - foreach ($info['activity']['settings'] as $setting) { - \OC::$server->getActivityManager()->registerSetting($setting); - } - } - if (!empty($info['activity']['providers'])) { - foreach ($info['activity']['providers'] as $provider) { - \OC::$server->getActivityManager()->registerProvider($provider); - } - } - - if (!empty($info['settings']['admin'])) { - foreach ($info['settings']['admin'] as $setting) { - \OC::$server->get(ISettingsManager::class)->registerSetting('admin', $setting); - } - } - if (!empty($info['settings']['admin-section'])) { - foreach ($info['settings']['admin-section'] as $section) { - \OC::$server->get(ISettingsManager::class)->registerSection('admin', $section); - } - } - if (!empty($info['settings']['personal'])) { - foreach ($info['settings']['personal'] as $setting) { - \OC::$server->get(ISettingsManager::class)->registerSetting('personal', $setting); - } - } - if (!empty($info['settings']['personal-section'])) { - foreach ($info['settings']['personal-section'] as $section) { - \OC::$server->get(ISettingsManager::class)->registerSection('personal', $section); - } - } - - if (!empty($info['collaboration']['plugins'])) { - // deal with one or many plugin entries - $plugins = isset($info['collaboration']['plugins']['plugin']['@value']) ? - [$info['collaboration']['plugins']['plugin']] : $info['collaboration']['plugins']['plugin']; - foreach ($plugins as $plugin) { - if ($plugin['@attributes']['type'] === 'collaborator-search') { - $pluginInfo = [ - 'shareType' => $plugin['@attributes']['share-type'], - 'class' => $plugin['@value'], - ]; - \OC::$server->getCollaboratorSearch()->registerPlugin($pluginInfo); - } elseif ($plugin['@attributes']['type'] === 'autocomplete-sort') { - \OC::$server->getAutoCompleteManager()->registerSorter($plugin['@value']); - } - } - } + public static function loadApp(string $app): void { + \OC::$server->get(IAppManager::class)->loadApp($app); } /** @@ -285,8 +116,6 @@ class OC_App { require_once $path . '/composer/autoload.php'; } else { \OC::$composerAutoloader->addPsr4($appNamespace . '\\', $path . '/lib/', true); - // Register on legacy autoloader - \OC::$loader->addValidRoot($path); } // Register Test namespace only when testing @@ -296,50 +125,15 @@ class OC_App { } /** - * Load app.php from the given app - * - * @param string $app app name - * @throws Error - */ - private static function requireAppFile(string $app) { - // encapsulated here to avoid variable scope conflicts - require_once $app . '/appinfo/app.php'; - } - - /** * check if an app is of a specific type * * @param string $app * @param array $types * @return bool + * @deprecated 27.0.0 use IAppManager::isType */ public static function isType(string $app, array $types): bool { - $appTypes = self::getAppTypes($app); - foreach ($types as $type) { - if (array_search($type, $appTypes) !== false) { - return true; - } - } - return false; - } - - /** - * get the types of an app - * - * @param string $app - * @return array - */ - private static function getAppTypes(string $app): array { - //load the cache - if (count(self::$appTypes) == 0) { - self::$appTypes = \OC::$server->getAppConfig()->getValues(false, 'types'); - } - - if (isset(self::$appTypes[$app])) { - return explode(',', self::$appTypes[$app]); - } - - return []; + return \OC::$server->get(IAppManager::class)->isType($app, $types); } /** @@ -375,8 +169,8 @@ class OC_App { * * @param bool $forceRefresh whether to refresh the cache * @param bool $all whether to return apps for all users, not only the - * currently logged in one - * @return string[] + * currently logged in one + * @return list<string> */ public static function getEnabledApps(bool $forceRefresh = false, bool $all = false): array { if (!\OC::$server->getSystemConfig()->getValue('installed', false)) { @@ -392,7 +186,7 @@ class OC_App { } if (is_null($user)) { - $apps = $appManager->getInstalledApps(); + $apps = $appManager->getEnabledApps(); } else { $apps = $appManager->getEnabledAppsForUser($user); } @@ -405,19 +199,6 @@ class OC_App { } /** - * checks whether or not an app is enabled - * - * @param string $app app - * @return bool - * @deprecated 13.0.0 use \OC::$server->getAppManager()->isEnabledForUser($appId) - * - * This function checks whether or not an app is enabled. - */ - public static function isEnabled(string $app): bool { - return \OC::$server->getAppManager()->isEnabledForUser($app); - } - - /** * enables an app * * @param string $appId @@ -428,10 +209,10 @@ class OC_App { * This function set an app as enabled in appconfig. */ public function enable(string $appId, - array $groups = []) { + array $groups = []) { // Check if app is already downloaded /** @var Installer $installer */ - $installer = \OC::$server->query(Installer::class); + $installer = Server::get(Installer::class); $isDownloaded = $installer->isDownloaded($appId); if (!$isDownloaded) { @@ -457,28 +238,13 @@ class OC_App { } /** - * Get the path where to install apps + * Find the apps root for an app id. * - * @return string|false - */ - public static function getInstallPath() { - foreach (OC::$APPSROOTS as $dir) { - if (isset($dir['writable']) && $dir['writable'] === true) { - return $dir['path']; - } - } - - \OCP\Util::writeLog('core', 'No application directories are marked as writable.', ILogger::ERROR); - return null; - } - - - /** - * search for an app in all app-directories + * If multiple copies are found, the apps root the latest version is returned. * * @param string $appId * @param bool $ignoreCache ignore cache and rebuild it - * @return false|string + * @return false|array{path: string, url: string} the apps root shape */ public static function findAppInDirectories(string $appId, bool $ignoreCache = false) { $sanitizedAppId = self::cleanAppId($appId); @@ -530,11 +296,14 @@ class OC_App { * @param string $appId * @param bool $refreshAppPath should be set to true only during install/upgrade * @return string|false - * @deprecated 11.0.0 use \OC::$server->getAppManager()->getAppPath() + * @deprecated 11.0.0 use Server::get(IAppManager)->getAppPath() */ public static function getAppPath(string $appId, bool $refreshAppPath = false) { - if ($appId === null || trim($appId) === '') { + $appId = self::cleanAppId($appId); + if ($appId === '') { return false; + } elseif ($appId === 'core') { + return __DIR__ . '/../../../core'; } if (($dir = self::findAppInDirectories($appId, $refreshAppPath)) != false) { @@ -559,18 +328,6 @@ class OC_App { } /** - * get the last version of the app from appinfo/info.xml - * - * @param string $appId - * @param bool $useCache - * @return string - * @deprecated 14.0.0 use \OC::$server->getAppManager()->getAppVersion() - */ - public static function getAppVersion(string $appId, bool $useCache = true): string { - return \OC::$server->getAppManager()->getAppVersion($appId, $useCache); - } - - /** * get app's version based on it's path * * @param string $path @@ -578,51 +335,8 @@ class OC_App { */ public static function getAppVersionByPath(string $path): string { $infoFile = $path . '/appinfo/info.xml'; - $appData = \OC::$server->getAppManager()->getAppInfo($infoFile, true); - return isset($appData['version']) ? $appData['version'] : ''; - } - - - /** - * Read all app metadata from the info.xml file - * - * @param string $appId id of the app or the path of the info.xml file - * @param bool $path - * @param string $lang - * @return array|null - * @note all data is read from info.xml, not just pre-defined fields - * @deprecated 14.0.0 use \OC::$server->getAppManager()->getAppInfo() - */ - public static function getAppInfo(string $appId, bool $path = false, string $lang = null) { - return \OC::$server->getAppManager()->getAppInfo($appId, $path, $lang); - } - - /** - * Returns the navigation - * - * @return array - * @deprecated 14.0.0 use \OC::$server->getNavigationManager()->getAll() - * - * This function returns an array containing all entries added. The - * entries are sorted by the key 'order' ascending. Additional to the keys - * given for each app the following keys exist: - * - active: boolean, signals if the user is on this navigation entry - */ - public static function getNavigation(): array { - return OC::$server->getNavigationManager()->getAll(); - } - - /** - * Returns the Settings Navigation - * - * @return string[] - * @deprecated 14.0.0 use \OC::$server->getNavigationManager()->getAll('settings') - * - * This function returns an array containing all settings pages added. The - * entries are sorted by the key 'order' ascending. - */ - public static function getSettingsNavigation(): array { - return OC::$server->getNavigationManager()->getAll('settings'); + $appData = Server::get(IAppManager::class)->getAppInfoByPath($infoFile); + return $appData['version'] ?? ''; } /** @@ -659,33 +373,11 @@ class OC_App { } /** - * @param string $type - * @return array - */ - public static function getForms(string $type): array { - $forms = []; - switch ($type) { - case 'admin': - $source = self::$adminForms; - break; - case 'personal': - $source = self::$personalForms; - break; - default: - return []; - } - foreach ($source as $form) { - $forms[] = include $form; - } - return $forms; - } - - /** * @param array $entry * @deprecated 20.0.0 Please register your alternative login option using the registerAlternativeLogin() on the RegistrationContext in your Application class implementing the OCP\Authentication\IAlternativeLogin interface */ public static function registerLogIn(array $entry) { - \OC::$server->getLogger()->debug('OC_App::registerLogIn() is deprecated, please register your alternative login option using the registerAlternativeLogin() on the RegistrationContext in your Application class implementing the OCP\Authentication\IAlternativeLogin interface'); + Server::get(LoggerInterface::class)->debug('OC_App::registerLogIn() is deprecated, please register your alternative login option using the registerAlternativeLogin() on the RegistrationContext in your Application class implementing the OCP\Authentication\IAlternativeLogin interface'); self::$altLogin[] = $entry; } @@ -694,11 +386,11 @@ class OC_App { */ public static function getAlternativeLogIns(): array { /** @var Coordinator $bootstrapCoordinator */ - $bootstrapCoordinator = \OC::$server->query(Coordinator::class); + $bootstrapCoordinator = Server::get(Coordinator::class); foreach ($bootstrapCoordinator->getRegistrationContext()->getAlternativeLogins() as $registration) { if (!in_array(IAlternativeLogin::class, class_implements($registration->getService()), true)) { - \OC::$server->getLogger()->error('Alternative login option {option} does not implement {interface} and is therefore ignored.', [ + Server::get(LoggerInterface::class)->error('Alternative login option {option} does not implement {interface} and is therefore ignored.', [ 'option' => $registration->getService(), 'interface' => IAlternativeLogin::class, 'app' => $registration->getAppId(), @@ -708,13 +400,14 @@ class OC_App { try { /** @var IAlternativeLogin $provider */ - $provider = \OC::$server->query($registration->getService()); - } catch (QueryException $e) { - \OC::$server->getLogger()->logException($e, [ - 'message' => 'Alternative login option {option} can not be initialised.', - 'option' => $registration->getService(), - 'app' => $registration->getAppId(), - ]); + $provider = Server::get($registration->getService()); + } catch (ContainerExceptionInterface $e) { + Server::get(LoggerInterface::class)->error('Alternative login option {option} can not be initialized.', + [ + 'exception' => $e, + 'option' => $registration->getService(), + 'app' => $registration->getAppId(), + ]); } try { @@ -726,11 +419,12 @@ class OC_App { 'class' => $provider->getClass(), ]; } catch (Throwable $e) { - \OC::$server->getLogger()->logException($e, [ - 'message' => 'Alternative login option {option} had an error while loading.', - 'option' => $registration->getService(), - 'app' => $registration->getAppId(), - ]); + Server::get(LoggerInterface::class)->error('Alternative login option {option} had an error while loading.', + [ + 'exception' => $e, + 'option' => $registration->getService(), + 'app' => $registration->getAppId(), + ]); } } @@ -741,40 +435,19 @@ class OC_App { * get a list of all apps in the apps folder * * @return string[] an array of app names (string IDs) - * @todo: change the name of this method to getInstalledApps, which is more accurate + * @deprecated 31.0.0 Use IAppManager::getAllAppsInAppsFolders instead */ public static function getAllApps(): array { - $apps = []; - - foreach (OC::$APPSROOTS as $apps_dir) { - if (!is_readable($apps_dir['path'])) { - \OCP\Util::writeLog('core', 'unable to read app folder : ' . $apps_dir['path'], ILogger::WARN); - continue; - } - $dh = opendir($apps_dir['path']); - - if (is_resource($dh)) { - while (($file = readdir($dh)) !== false) { - if ($file[0] != '.' and is_dir($apps_dir['path'] . '/' . $file) and is_file($apps_dir['path'] . '/' . $file . '/appinfo/info.xml')) { - $apps[] = $file; - } - } - } - } - - $apps = array_unique($apps); - - return $apps; + return Server::get(IAppManager::class)->getAllAppsInAppsFolders(); } /** * List all supported apps * - * @return array + * @deprecated 32.0.0 Use \OCP\Support\Subscription\IRegistry::delegateGetSupportedApps instead */ public function getSupportedApps(): array { - /** @var \OCP\Support\Subscription\IRegistry $subscriptionRegistry */ - $subscriptionRegistry = \OC::$server->query(\OCP\Support\Subscription\IRegistry::class); + $subscriptionRegistry = Server::get(\OCP\Support\Subscription\IRegistry::class); $supportedApps = $subscriptionRegistry->delegateGetSupportedApps(); return $supportedApps; } @@ -785,9 +458,9 @@ class OC_App { * @return array */ public function listAllApps(): array { - $installedApps = OC_App::getAllApps(); - $appManager = \OC::$server->getAppManager(); + + $installedApps = $appManager->getAllAppsInAppsFolders(); //we don't want to show configuration for these $blacklist = $appManager->getAlwaysEnabledApps(); $appList = []; @@ -796,15 +469,15 @@ class OC_App { $supportedApps = $this->getSupportedApps(); foreach ($installedApps as $app) { - if (array_search($app, $blacklist) === false) { - $info = OC_App::getAppInfo($app, false, $langCode); + if (!in_array($app, $blacklist)) { + $info = $appManager->getAppInfo($app, false, $langCode); if (!is_array($info)) { - \OCP\Util::writeLog('core', 'Could not read app info file for app "' . $app . '"', ILogger::ERROR); + Server::get(LoggerInterface::class)->error('Could not read app info file for app "' . $app . '"', ['app' => 'core']); continue; } if (!isset($info['name'])) { - \OCP\Util::writeLog('core', 'App id "' . $app . '" has no name in appinfo', ILogger::ERROR); + Server::get(LoggerInterface::class)->error('App id "' . $app . '" has no name in appinfo', ['app' => 'core']); continue; } @@ -861,7 +534,7 @@ class OC_App { } } - $info['version'] = OC_App::getAppVersion($app); + $info['version'] = $appManager->getAppVersion($app); $appList[] = $info; } } @@ -871,7 +544,7 @@ class OC_App { public static function shouldUpgrade(string $app): bool { $versions = self::getAppVersions(); - $currentVersion = OC_App::getAppVersion($app); + $currentVersion = Server::get(\OCP\App\IAppManager::class)->getAppVersion($app); if ($currentVersion && isset($versions[$app])) { $installedVersion = $versions[$app]; if (!version_compare($currentVersion, $installedVersion, '=')) { @@ -904,7 +577,7 @@ class OC_App { } /** - * Check whether the current ownCloud version matches the given + * Check whether the current Nextcloud version matches the given * application's version requirements. * * The comparison is made based on the number of parts that the @@ -914,7 +587,7 @@ class OC_App { * This means that it's possible to specify "requiremin" => 6 * and "requiremax" => 6 and it will still match ownCloud 6.0.3. * - * @param string $ocVersion ownCloud version to check against + * @param string $ocVersion Nextcloud version to check against * @param array $appInfo app info (from xml) * * @return boolean true if compatible, otherwise false @@ -957,15 +630,10 @@ class OC_App { /** * get the installed version of all apps + * @deprecated 32.0.0 Use IAppManager::getAppInstalledVersions or IAppConfig::getAppInstalledVersions instead */ - public static function getAppVersions() { - static $versions; - - if (!$versions) { - $appConfig = \OC::$server->getAppConfig(); - $versions = $appConfig->getValues(false, 'installed_version'); - } - return $versions; + public static function getAppVersions(): array { + return Server::get(IAppConfig::class)->getAppInstalledVersions(); } /** @@ -983,13 +651,13 @@ class OC_App { } if (is_file($appPath . '/appinfo/database.xml')) { - \OC::$server->getLogger()->error('The appinfo/database.xml file is not longer supported. Used in ' . $appId); + Server::get(LoggerInterface::class)->error('The appinfo/database.xml file is not longer supported. Used in ' . $appId); return false; } \OC::$server->getAppManager()->clearAppsCache(); $l = \OC::$server->getL10N('core'); - $appData = self::getAppInfo($appId, false, $l->getLanguageCode()); + $appData = Server::get(\OCP\App\IAppManager::class)->getAppInfo($appId, false, $l->getLanguageCode()); $ignoreMaxApps = \OC::$server->getConfig()->getSystemValue('app_install_overwrite', []); $ignoreMax = in_array($appId, $ignoreMaxApps, true); @@ -1029,10 +697,15 @@ class OC_App { self::setAppTypes($appId); - $version = \OC_App::getAppVersion($appId); + $version = Server::get(\OCP\App\IAppManager::class)->getAppVersion($appId); \OC::$server->getConfig()->setAppValue($appId, 'installed_version', $version); - \OC::$server->getEventDispatcher()->dispatch(ManagerEvent::EVENT_APP_UPDATE, new ManagerEvent( + // migrate eventual new config keys in the process + /** @psalm-suppress InternalMethod */ + Server::get(ConfigManager::class)->migrateConfigLexiconKeys($appId); + + \OC::$server->get(IEventDispatcher::class)->dispatchTyped(new AppUpdateEvent($appId)); + \OC::$server->get(IEventDispatcher::class)->dispatch(ManagerEvent::EVENT_APP_UPDATE, new ManagerEvent( ManagerEvent::EVENT_APP_UPDATE, $appId )); @@ -1051,16 +724,16 @@ class OC_App { // load the app self::loadApp($appId); - $dispatcher = \OC::$server->get(\OCP\EventDispatcher\IEventDispatcher::class); + $dispatcher = Server::get(IEventDispatcher::class); // load the steps - $r = new Repair([], $dispatcher, \OC::$server->get(LoggerInterface::class)); + $r = Server::get(Repair::class); foreach ($steps as $step) { try { $r->addStep($step); } catch (Exception $ex) { $dispatcher->dispatchTyped(new RepairErrorEvent($ex->getMessage())); - \OC::$server->getLogger()->logException($ex); + logger('core')->error('Failed to add app migration step ' . $step, ['exception' => $ex]); } } // run the steps @@ -1088,103 +761,6 @@ class OC_App { } /** - * @param string $appId - * @return \OC\Files\View|false - */ - public static function getStorage(string $appId) { - if (\OC::$server->getAppManager()->isEnabledForUser($appId)) { //sanity check - if (\OC::$server->getUserSession()->isLoggedIn()) { - $view = new \OC\Files\View('/' . OC_User::getUser()); - if (!$view->file_exists($appId)) { - $view->mkdir($appId); - } - return new \OC\Files\View('/' . OC_User::getUser() . '/' . $appId); - } else { - \OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ', user not logged in', ILogger::ERROR); - return false; - } - } else { - \OCP\Util::writeLog('core', 'Can\'t get app storage, app ' . $appId . ' not enabled', ILogger::ERROR); - return false; - } - } - - protected static function findBestL10NOption(array $options, string $lang): string { - // only a single option - if (isset($options['@value'])) { - return $options['@value']; - } - - $fallback = $similarLangFallback = $englishFallback = false; - - $lang = strtolower($lang); - $similarLang = $lang; - if (strpos($similarLang, '_')) { - // For "de_DE" we want to find "de" and the other way around - $similarLang = substr($lang, 0, strpos($lang, '_')); - } - - foreach ($options as $option) { - if (is_array($option)) { - if ($fallback === false) { - $fallback = $option['@value']; - } - - if (!isset($option['@attributes']['lang'])) { - continue; - } - - $attributeLang = strtolower($option['@attributes']['lang']); - if ($attributeLang === $lang) { - return $option['@value']; - } - - if ($attributeLang === $similarLang) { - $similarLangFallback = $option['@value']; - } elseif (strpos($attributeLang, $similarLang . '_') === 0) { - if ($similarLangFallback === false) { - $similarLangFallback = $option['@value']; - } - } - } else { - $englishFallback = $option; - } - } - - if ($similarLangFallback !== false) { - return $similarLangFallback; - } elseif ($englishFallback !== false) { - return $englishFallback; - } - return (string) $fallback; - } - - /** - * parses the app data array and enhanced the 'description' value - * - * @param array $data the app data - * @param string $lang - * @return array improved app data - */ - public static function parseAppInfo(array $data, $lang = null): array { - if ($lang && isset($data['name']) && is_array($data['name'])) { - $data['name'] = self::findBestL10NOption($data['name'], $lang); - } - if ($lang && isset($data['summary']) && is_array($data['summary'])) { - $data['summary'] = self::findBestL10NOption($data['summary'], $lang); - } - if ($lang && isset($data['description']) && is_array($data['description'])) { - $data['description'] = trim(self::findBestL10NOption($data['description'], $lang)); - } elseif (isset($data['description']) && is_string($data['description'])) { - $data['description'] = trim($data['description']); - } else { - $data['description'] = ''; - } - - return $data; - } - - /** * @param \OCP\IConfig $config * @param \OCP\IL10N $l * @param array $info diff --git a/lib/private/legacy/OC_Defaults.php b/lib/private/legacy/OC_Defaults.php index 707df7279bb..0d460ff966d 100644 --- a/lib/private/legacy/OC_Defaults.php +++ b/lib/private/legacy/OC_Defaults.php @@ -1,42 +1,15 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Jan-Christoph Borchardt <hey@jancborchardt.net> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Julius Haertl <jus@bitgrid.net> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Markus Staab <markus.staab@redaxo.de> - * @author Michael Weimann <mail@michael-weimann.eu> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Pascal de Bruijn <pmjdebruijn@pcode.nl> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author scolebrook <scolebrook@mac.com> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Volkan Gezer <volkangezer@gmail.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ +use OCP\IConfig; +use OCP\Server; +use OCP\ServerVersion; + class OC_Defaults { private $theme; @@ -52,12 +25,14 @@ class OC_Defaults { private $defaultDocBaseUrl; private $defaultDocVersion; private $defaultSlogan; + private $defaultColorBackground; private $defaultColorPrimary; private $defaultTextColorPrimary; private $defaultProductName; public function __construct() { - $config = \OC::$server->getConfig(); + $config = Server::get(IConfig::class); + $serverVersion = Server::get(ServerVersion::class); $this->defaultEntity = 'Nextcloud'; /* e.g. company name, used for footers and copyright notices */ $this->defaultName = 'Nextcloud'; /* short name, used when referring to the software */ @@ -69,8 +44,9 @@ class OC_Defaults { $this->defaultAndroidClientUrl = $config->getSystemValue('customclient_android', 'https://play.google.com/store/apps/details?id=com.nextcloud.client'); $this->defaultFDroidClientUrl = $config->getSystemValue('customclient_fdroid', 'https://f-droid.org/packages/com.nextcloud.client/'); $this->defaultDocBaseUrl = 'https://docs.nextcloud.com'; - $this->defaultDocVersion = \OC_Util::getVersion()[0]; // used to generate doc links - $this->defaultColorPrimary = '#0082c9'; + $this->defaultDocVersion = $serverVersion->getMajorVersion(); // used to generate doc links + $this->defaultColorBackground = '#00679e'; + $this->defaultColorPrimary = '#00679e'; $this->defaultTextColorPrimary = '#ffffff'; $this->defaultProductName = 'Nextcloud'; @@ -245,15 +221,6 @@ class OC_Defaults { } /** - * Returns logo claim - * @return string logo claim - * @deprecated 13.0.0 - */ - public function getLogoClaim() { - return ''; - } - - /** * Returns short version of the footer * @return string short footer */ @@ -261,9 +228,9 @@ class OC_Defaults { if ($this->themeExist('getShortFooter')) { $footer = $this->theme->getShortFooter(); } else { - $footer = '<a href="'. $this->getBaseUrl() . '" target="_blank"' . - ' rel="noreferrer noopener">' .$this->getEntity() . '</a>'. - ' – ' . $this->getSlogan(); + $footer = '<a href="' . $this->getBaseUrl() . '" target="_blank"' + . ' rel="noreferrer noopener">' . $this->getEntity() . '</a>' + . ' – ' . $this->getSlogan(); } return $footer; @@ -309,6 +276,17 @@ class OC_Defaults { } /** + * Returns primary color + * @return string + */ + public function getColorBackground() { + if ($this->themeExist('getColorBackground')) { + return $this->theme->getColorBackground(); + } + return $this->defaultColorBackground; + } + + /** * @return array scss variables to overwrite */ public function getScssVariables() { diff --git a/lib/private/legacy/OC_EventSource.php b/lib/private/legacy/OC_EventSource.php deleted file mode 100644 index c733316050f..00000000000 --- a/lib/private/legacy/OC_EventSource.php +++ /dev/null @@ -1,125 +0,0 @@ -<?php -/** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christian Oliff <christianoliff@yahoo.com> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Felix Moeller <mail@felixmoeller.de> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ -class OC_EventSource implements \OCP\IEventSource { - /** - * @var bool - */ - private $fallback; - - /** - * @var int - */ - private $fallBackId = 0; - - /** - * @var bool - */ - private $started = false; - - protected function init() { - if ($this->started) { - return; - } - $this->started = true; - - // prevent php output buffering, caching and nginx buffering - OC_Util::obEnd(); - header('Cache-Control: no-cache'); - header('X-Accel-Buffering: no'); - $this->fallback = isset($_GET['fallback']) and $_GET['fallback'] == 'true'; - if ($this->fallback) { - $this->fallBackId = (int)$_GET['fallback_id']; - /** - * FIXME: The default content-security-policy of ownCloud forbids inline - * JavaScript for security reasons. IE starting on Windows 10 will - * however also obey the CSP which will break the event source fallback. - * - * As a workaround thus we set a custom policy which allows the execution - * of inline JavaScript. - * - * @link https://github.com/owncloud/core/issues/14286 - */ - header("Content-Security-Policy: default-src 'none'; script-src 'unsafe-inline'"); - header("Content-Type: text/html"); - echo str_repeat('<span></span>' . PHP_EOL, 10); //dummy data to keep IE happy - } else { - header("Content-Type: text/event-stream"); - } - if (!\OC::$server->getRequest()->passesStrictCookieCheck()) { - header('Location: '.\OC::$WEBROOT); - exit(); - } - if (!\OC::$server->getRequest()->passesCSRFCheck()) { - $this->send('error', 'Possible CSRF attack. Connection will be closed.'); - $this->close(); - exit(); - } - flush(); - } - - /** - * send a message to the client - * - * @param string $type - * @param mixed $data - * - * @throws \BadMethodCallException - * if only one parameter is given, a typeless message will be send with that parameter as data - * @suppress PhanDeprecatedFunction - */ - public function send($type, $data = null) { - if ($data and !preg_match('/^[A-Za-z0-9_]+$/', $type)) { - throw new BadMethodCallException('Type needs to be alphanumeric ('. $type .')'); - } - $this->init(); - if (is_null($data)) { - $data = $type; - $type = null; - } - if ($this->fallback) { - $response = '<script type="text/javascript">window.parent.OC.EventSource.fallBackCallBack(' - . $this->fallBackId . ',"' . $type . '",' . OC_JSON::encode($data) . ')</script>' . PHP_EOL; - echo $response; - } else { - if ($type) { - echo 'event: ' . $type . PHP_EOL; - } - echo 'data: ' . OC_JSON::encode($data) . PHP_EOL; - } - echo PHP_EOL; - flush(); - } - - /** - * close the connection of the event source - */ - public function close() { - $this->send('__internal__', 'close'); //server side closing can be an issue, let the client do it - } -} diff --git a/lib/private/legacy/OC_FileChunking.php b/lib/private/legacy/OC_FileChunking.php deleted file mode 100644 index e3782cabb4a..00000000000 --- a/lib/private/legacy/OC_FileChunking.php +++ /dev/null @@ -1,184 +0,0 @@ -<?php -/** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Felix Moeller <mail@felixmoeller.de> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Thomas Tanghus <thomas@tanghus.net> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ -class OC_FileChunking { - protected $info; - protected $cache; - - /** - * TTL of chunks - * - * @var int - */ - protected $ttl; - - public static function decodeName($name) { - preg_match('/(?P<name>.*)-chunking-(?P<transferid>\d+)-(?P<chunkcount>\d+)-(?P<index>\d+)/', $name, $matches); - return $matches; - } - - /** - * @param string[] $info - */ - public function __construct($info) { - $this->info = $info; - $this->ttl = \OC::$server->getConfig()->getSystemValue('cache_chunk_gc_ttl', 86400); - } - - public function getPrefix() { - $name = $this->info['name']; - $transferid = $this->info['transferid']; - - return $name.'-chunking-'.$transferid.'-'; - } - - protected function getCache() { - if (!isset($this->cache)) { - $this->cache = new \OC\Cache\File(); - } - return $this->cache; - } - - /** - * Stores the given $data under the given $key - the number of stored bytes is returned - * - * @param string $index - * @param resource $data - * @return int - */ - public function store($index, $data) { - $cache = $this->getCache(); - $name = $this->getPrefix().$index; - $cache->set($name, $data, $this->ttl); - - return $cache->size($name); - } - - public function isComplete() { - $prefix = $this->getPrefix(); - $cache = $this->getCache(); - $chunkcount = (int)$this->info['chunkcount']; - - for ($i = ($chunkcount - 1); $i >= 0; $i--) { - if (!$cache->hasKey($prefix.$i)) { - return false; - } - } - - return true; - } - - /** - * Assembles the chunks into the file specified by the path. - * Chunks are deleted afterwards. - * - * @param resource $f target path - * - * @return integer assembled file size - * - * @throws \OC\InsufficientStorageException when file could not be fully - * assembled due to lack of free space - */ - public function assemble($f) { - $cache = $this->getCache(); - $prefix = $this->getPrefix(); - $count = 0; - for ($i = 0; $i < $this->info['chunkcount']; $i++) { - $chunk = $cache->get($prefix.$i); - // remove after reading to directly save space - $cache->remove($prefix.$i); - $count += fwrite($f, $chunk); - // let php release the memory to work around memory exhausted error with php 5.6 - $chunk = null; - } - - return $count; - } - - /** - * Returns the size of the chunks already present - * @return integer size in bytes - */ - public function getCurrentSize() { - $cache = $this->getCache(); - $prefix = $this->getPrefix(); - $total = 0; - for ($i = 0; $i < $this->info['chunkcount']; $i++) { - $total += $cache->size($prefix.$i); - } - return $total; - } - - /** - * Removes all chunks which belong to this transmission - */ - public function cleanup() { - $cache = $this->getCache(); - $prefix = $this->getPrefix(); - for ($i = 0; $i < $this->info['chunkcount']; $i++) { - $cache->remove($prefix.$i); - } - } - - /** - * Removes one specific chunk - * @param string $index - */ - public function remove($index) { - $cache = $this->getCache(); - $prefix = $this->getPrefix(); - $cache->remove($prefix.$index); - } - - /** - * Assembles the chunks into the file specified by the path. - * Also triggers the relevant hooks and proxies. - * - * @param \OC\Files\Storage\Storage $storage storage - * @param string $path target path relative to the storage - * @return bool true on success or false if file could not be created - * - * @throws \OC\ServerNotAvailableException - */ - public function file_assemble($storage, $path) { - // use file_put_contents as method because that best matches what this function does - if (\OC\Files\Filesystem::isValidPath($path)) { - $target = $storage->fopen($path, 'w'); - if ($target) { - $count = $this->assemble($target); - fclose($target); - return $count > 0; - } else { - return false; - } - } - return false; - } -} diff --git a/lib/private/legacy/OC_Files.php b/lib/private/legacy/OC_Files.php deleted file mode 100644 index 6a3a44d6cc0..00000000000 --- a/lib/private/legacy/OC_Files.php +++ /dev/null @@ -1,459 +0,0 @@ -<?php -/** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Calviño Sánchez <danxuliu@gmail.com> - * @author Frank Karlitschek <frank@karlitschek.de> - * @author Jakob Sack <mail@jakobsack.de> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Julius Härtl <jus@bitgrid.net> - * @author Ko- <k.stoffelen@cs.ru.nl> - * @author Michael Gapczynski <GapczynskiM@gmail.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Nicolai Ehemann <en@enlightened.de> - * @author Piotr Filiciak <piotr@filiciak.pl> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thibaut GRIDEL <tgridel@free.fr> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Valdnet <47037905+Valdnet@users.noreply.github.com> - * @author Victor Dubiniuk <dubiniuk@owncloud.com> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ -use bantu\IniGetWrapper\IniGetWrapper; -use OC\Files\View; -use OC\Streamer; -use OCP\Lock\ILockingProvider; -use OCP\Files\Events\BeforeZipCreatedEvent; -use OCP\Files\Events\BeforeDirectFileDownloadEvent; -use OCP\EventDispatcher\IEventDispatcher; - -/** - * Class for file server access - */ -class OC_Files { - public const FILE = 1; - public const ZIP_FILES = 2; - public const ZIP_DIR = 3; - - public const UPLOAD_MIN_LIMIT_BYTES = 1048576; // 1 MiB - - - private static $multipartBoundary = ''; - - /** - * @return string - */ - private static function getBoundary() { - if (empty(self::$multipartBoundary)) { - self::$multipartBoundary = md5(mt_rand()); - } - return self::$multipartBoundary; - } - - /** - * @param string $filename - * @param string $name - * @param array $rangeArray ('from'=>int,'to'=>int), ... - */ - private static function sendHeaders($filename, $name, array $rangeArray) { - OC_Response::setContentDispositionHeader($name, 'attachment'); - header('Content-Transfer-Encoding: binary', true); - header('Pragma: public');// enable caching in IE - header('Expires: 0'); - header("Cache-Control: must-revalidate, post-check=0, pre-check=0"); - $fileSize = \OC\Files\Filesystem::filesize($filename); - $type = \OC::$server->getMimeTypeDetector()->getSecureMimeType(\OC\Files\Filesystem::getMimeType($filename)); - if ($fileSize > -1) { - if (!empty($rangeArray)) { - http_response_code(206); - header('Accept-Ranges: bytes', true); - if (count($rangeArray) > 1) { - $type = 'multipart/byteranges; boundary='.self::getBoundary(); - // no Content-Length header here - } else { - header(sprintf('Content-Range: bytes %d-%d/%d', $rangeArray[0]['from'], $rangeArray[0]['to'], $fileSize), true); - OC_Response::setContentLengthHeader($rangeArray[0]['to'] - $rangeArray[0]['from'] + 1); - } - } else { - OC_Response::setContentLengthHeader($fileSize); - } - } - header('Content-Type: '.$type, true); - header('X-Accel-Buffering: no'); - } - - /** - * return the content of a file or return a zip file containing multiple files - * - * @param string $dir - * @param string $files ; separated list of files to download - * @param array $params ; 'head' boolean to only send header of the request ; 'range' http range header - */ - public static function get($dir, $files, $params = null) { - OC_Util::setupFS(); - $view = \OC\Files\Filesystem::getView(); - $getType = self::FILE; - $filename = $dir; - try { - if (is_array($files) && count($files) === 1) { - $files = $files[0]; - } - - if (!is_array($files)) { - $filename = $dir . '/' . $files; - if (!$view->is_dir($filename)) { - self::getSingleFile($view, $dir, $files, is_null($params) ? [] : $params); - return; - } - } - - $name = 'download'; - if (is_array($files)) { - $getType = self::ZIP_FILES; - $basename = basename($dir); - if ($basename) { - $name = $basename; - } - - $filename = $dir . '/' . $name; - } else { - $filename = $dir . '/' . $files; - $getType = self::ZIP_DIR; - // downloading root ? - if ($files !== '') { - $name = $files; - } - } - - self::lockFiles($view, $dir, $files); - $numberOfFiles = 0; - $fileSize = 0; - - /* Calculate filesize and number of files */ - if ($getType === self::ZIP_FILES) { - $fileInfos = []; - foreach ($files as $file) { - $fileInfo = \OC\Files\Filesystem::getFileInfo($dir . '/' . $file); - if ($fileInfo) { - $fileSize += $fileInfo->getSize(); - $fileInfos[] = $fileInfo; - } - } - $numberOfFiles = self::getNumberOfFiles($fileInfos); - } elseif ($getType === self::ZIP_DIR) { - $fileInfo = \OC\Files\Filesystem::getFileInfo($dir . '/' . $files); - if ($fileInfo) { - $fileSize = $fileInfo->getSize(); - $numberOfFiles = self::getNumberOfFiles([$fileInfo]); - } - } - - //Dispatch an event to see if any apps have problem with download - $event = new BeforeZipCreatedEvent($dir, is_array($files) ? $files : [$files]); - $dispatcher = \OCP\Server::get(IEventDispatcher::class); - $dispatcher->dispatchTyped($event); - if ((!$event->isSuccessful()) || $event->getErrorMessage() !== null) { - throw new \OC\ForbiddenException($event->getErrorMessage()); - } - - $streamer = new Streamer(\OC::$server->getRequest(), $fileSize, $numberOfFiles); - OC_Util::obEnd(); - - $streamer->sendHeaders($name); - $executionTime = (int)OC::$server->get(IniGetWrapper::class)->getNumeric('max_execution_time'); - if (strpos(@ini_get('disable_functions'), 'set_time_limit') === false) { - @set_time_limit(0); - } - ignore_user_abort(true); - - if ($getType === self::ZIP_FILES) { - foreach ($files as $file) { - $file = $dir . '/' . $file; - if (\OC\Files\Filesystem::is_file($file)) { - $userFolder = \OC::$server->getRootFolder()->get(\OC\Files\Filesystem::getRoot()); - $file = $userFolder->get($file); - if ($file instanceof \OC\Files\Node\File) { - try { - $fh = $file->fopen('r'); - } catch (\OCP\Files\NotPermittedException $e) { - continue; - } - $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); - } - } - } elseif ($getType === self::ZIP_DIR) { - $file = $dir . '/' . $files; - $streamer->addDirRecursive($file); - } - $streamer->finalize(); - set_time_limit($executionTime); - self::unlockAllTheFiles($dir, $files, $getType, $view, $filename); - } catch (\OCP\Lock\LockedException $ex) { - self::unlockAllTheFiles($dir, $files, $getType, $view, $filename); - OC::$server->getLogger()->logException($ex); - $l = \OC::$server->getL10N('lib'); - $hint = method_exists($ex, 'getHint') ? $ex->getHint() : ''; - \OC_Template::printErrorPage($l->t('File is currently busy, please try again later'), $hint, 200); - } catch (\OCP\Files\ForbiddenException $ex) { - self::unlockAllTheFiles($dir, $files, $getType, $view, $filename); - OC::$server->getLogger()->logException($ex); - $l = \OC::$server->getL10N('lib'); - \OC_Template::printErrorPage($l->t('Cannot download file'), $ex->getMessage(), 200); - } catch (\Exception $ex) { - self::unlockAllTheFiles($dir, $files, $getType, $view, $filename); - OC::$server->getLogger()->logException($ex); - $l = \OC::$server->getL10N('lib'); - $hint = method_exists($ex, 'getHint') ? $ex->getHint() : ''; - if ($event && $event->getErrorMessage() !== null) { - $hint .= ' ' . $event->getErrorMessage(); - } - \OC_Template::printErrorPage($l->t('Cannot download file'), $hint, 200); - } - } - - /** - * @param string $rangeHeaderPos - * @param int $fileSize - * @return array $rangeArray ('from'=>int,'to'=>int), ... - */ - private static function parseHttpRangeHeader($rangeHeaderPos, $fileSize) { - $rArray = explode(',', $rangeHeaderPos); - $minOffset = 0; - $ind = 0; - - $rangeArray = []; - - foreach ($rArray as $value) { - $ranges = explode('-', $value); - if (is_numeric($ranges[0])) { - if ($ranges[0] < $minOffset) { // case: bytes=500-700,601-999 - $ranges[0] = $minOffset; - } - if ($ind > 0 && $rangeArray[$ind - 1]['to'] + 1 == $ranges[0]) { // case: bytes=500-600,601-999 - $ind--; - $ranges[0] = $rangeArray[$ind]['from']; - } - } - - if (is_numeric($ranges[0]) && is_numeric($ranges[1]) && $ranges[0] < $fileSize && $ranges[0] <= $ranges[1]) { - // case: x-x - if ($ranges[1] >= $fileSize) { - $ranges[1] = $fileSize - 1; - } - $rangeArray[$ind++] = [ 'from' => $ranges[0], 'to' => $ranges[1], 'size' => $fileSize ]; - $minOffset = $ranges[1] + 1; - if ($minOffset >= $fileSize) { - break; - } - } elseif (is_numeric($ranges[0]) && $ranges[0] < $fileSize) { - // case: x- - $rangeArray[$ind++] = [ 'from' => $ranges[0], 'to' => $fileSize - 1, 'size' => $fileSize ]; - break; - } elseif (is_numeric($ranges[1])) { - // case: -x - if ($ranges[1] > $fileSize) { - $ranges[1] = $fileSize; - } - $rangeArray[$ind++] = [ 'from' => $fileSize - $ranges[1], 'to' => $fileSize - 1, 'size' => $fileSize ]; - break; - } - } - return $rangeArray; - } - - /** - * @param View $view - * @param string $name - * @param string $dir - * @param array $params ; 'head' boolean to only send header of the request ; 'range' http range header - * @throws \OC\ForbiddenException - */ - private static function getSingleFile($view, $dir, $name, $params) { - $filename = $dir . '/' . $name; - $file = null; - - try { - $userFolder = \OC::$server->getRootFolder()->get(\OC\Files\Filesystem::getRoot()); - $file = $userFolder->get($filename); - if (!$file instanceof \OC\Files\Node\File || !$file->isReadable()) { - http_response_code(403); - die('403 Forbidden'); - } - $fileSize = $file->getSize(); - } catch (\OCP\Files\NotPermittedException $e) { - http_response_code(403); - die('403 Forbidden'); - } catch (\OCP\Files\InvalidPathException $e) { - http_response_code(403); - die('403 Forbidden'); - } catch (\OCP\Files\NotFoundException $e) { - http_response_code(404); - $tmpl = new OC_Template('', '404', 'guest'); - $tmpl->printPage(); - exit(); - } - - OC_Util::obEnd(); - $view->lockFile($filename, ILockingProvider::LOCK_SHARED); - - $rangeArray = []; - - if (isset($params['range']) && substr($params['range'], 0, 6) === 'bytes=') { - $rangeArray = self::parseHttpRangeHeader(substr($params['range'], 6), $fileSize); - } - - $dispatcher = \OC::$server->query(IEventDispatcher::class); - $event = new BeforeDirectFileDownloadEvent($filename); - $dispatcher->dispatchTyped($event); - - if (!\OC\Files\Filesystem::isReadable($filename) || $event->getErrorMessage()) { - if ($event->getErrorMessage()) { - $msg = $event->getErrorMessage(); - } else { - $msg = 'Access denied'; - } - throw new \OC\ForbiddenException($msg); - } - - self::sendHeaders($filename, $name, $rangeArray); - - if (isset($params['head']) && $params['head']) { - return; - } - - if (!empty($rangeArray)) { - try { - if (count($rangeArray) == 1) { - $view->readfilePart($filename, $rangeArray[0]['from'], $rangeArray[0]['to']); - } else { - // check if file is seekable (if not throw UnseekableException) - // we have to check it before body contents - $view->readfilePart($filename, $rangeArray[0]['size'], $rangeArray[0]['size']); - - $type = \OC::$server->getMimeTypeDetector()->getSecureMimeType(\OC\Files\Filesystem::getMimeType($filename)); - - foreach ($rangeArray as $range) { - echo "\r\n--".self::getBoundary()."\r\n". - "Content-type: ".$type."\r\n". - "Content-range: bytes ".$range['from']."-".$range['to']."/".$range['size']."\r\n\r\n"; - $view->readfilePart($filename, $range['from'], $range['to']); - } - echo "\r\n--".self::getBoundary()."--\r\n"; - } - } catch (\OCP\Files\UnseekableException $ex) { - // file is unseekable - header_remove('Accept-Ranges'); - header_remove('Content-Range'); - http_response_code(200); - self::sendHeaders($filename, $name, []); - $view->readfile($filename); - } - } else { - $view->readfile($filename); - } - } - - /** - * Returns the total (recursive) number of files and folders in the given - * FileInfos. - * - * @param \OCP\Files\FileInfo[] $fileInfos the FileInfos to count - * @return int the total number of files and folders - */ - private static function getNumberOfFiles($fileInfos) { - $numberOfFiles = 0; - - $view = new View(); - - while ($fileInfo = array_pop($fileInfos)) { - $numberOfFiles++; - - if ($fileInfo->getType() === \OCP\Files\FileInfo::TYPE_FOLDER) { - $fileInfos = array_merge($fileInfos, $view->getDirectoryContent($fileInfo->getPath())); - } - } - - return $numberOfFiles; - } - - /** - * @param View $view - * @param string $dir - * @param string[]|string $files - */ - public static function lockFiles($view, $dir, $files) { - if (!is_array($files)) { - $file = $dir . '/' . $files; - $files = [$file]; - } - foreach ($files as $file) { - $file = $dir . '/' . $file; - $view->lockFile($file, ILockingProvider::LOCK_SHARED); - if ($view->is_dir($file)) { - $contents = $view->getDirectoryContent($file); - $contents = array_map(function ($fileInfo) use ($file) { - /** @var \OCP\Files\FileInfo $fileInfo */ - return $file . '/' . $fileInfo->getName(); - }, $contents); - self::lockFiles($view, $dir, $contents); - } - } - } - - /** - * @param string $dir - * @param $files - * @param integer $getType - * @param View $view - * @param string $filename - */ - private static function unlockAllTheFiles($dir, $files, $getType, $view, $filename) { - if ($getType === self::FILE) { - $view->unlockFile($filename, ILockingProvider::LOCK_SHARED); - } - if ($getType === self::ZIP_FILES) { - foreach ($files as $file) { - $file = $dir . '/' . $file; - $view->unlockFile($file, ILockingProvider::LOCK_SHARED); - } - } - if ($getType === self::ZIP_DIR) { - $file = $dir . '/' . $files; - $view->unlockFile($file, ILockingProvider::LOCK_SHARED); - } - } -} diff --git a/lib/private/legacy/OC_Helper.php b/lib/private/legacy/OC_Helper.php index 9ecd05b0a73..4388f775623 100644 --- a/lib/private/legacy/OC_Helper.php +++ b/lib/private/legacy/OC_Helper.php @@ -1,197 +1,69 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Ardinis <Ardinis@users.noreply.github.com> - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Felix Moeller <mail@felixmoeller.de> - * @author J0WI <J0WI@users.noreply.github.com> - * @author Jakob Sack <mail@jakobsack.de> - * @author Jan-Christoph Borchardt <hey@jancborchardt.net> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Olivier Paroz <github@oparoz.com> - * @author Pellaeon Lin <nfsmwlin@gmail.com> - * @author RealRancor <fisch.666@gmx.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Simon Könnecke <simonkoennecke@gmail.com> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Thomas Tanghus <thomas@tanghus.net> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ use bantu\IniGetWrapper\IniGetWrapper; +use OC\Files\FilenameValidator; use OC\Files\Filesystem; use OCP\Files\Mount\IMountPoint; -use OCP\ICacheFactory; use OCP\IBinaryFinder; +use OCP\ICacheFactory; use OCP\IUser; +use OCP\Server; +use OCP\Util; use Psr\Log\LoggerInterface; /** * Collection of useful functions + * + * @psalm-type StorageInfo = array{ + * free: float|int, + * mountPoint: string, + * mountType: string, + * owner: string, + * ownerDisplayName: string, + * quota: float|int, + * relative: float|int, + * total: float|int, + * used: float|int, + * } */ class OC_Helper { private static $templateManager; - - /** - * Make a human file size - * @param int $bytes file size in bytes - * @return string a human readable file size - * - * Makes 2048 to 2 kB. - */ - public static function humanFileSize($bytes) { - if ($bytes < 0) { - return "?"; - } - if ($bytes < 1024) { - return "$bytes B"; - } - $bytes = round($bytes / 1024, 0); - if ($bytes < 1024) { - return "$bytes KB"; - } - $bytes = round($bytes / 1024, 1); - if ($bytes < 1024) { - return "$bytes MB"; - } - $bytes = round($bytes / 1024, 1); - if ($bytes < 1024) { - return "$bytes GB"; - } - $bytes = round($bytes / 1024, 1); - if ($bytes < 1024) { - return "$bytes TB"; - } - - $bytes = round($bytes / 1024, 1); - return "$bytes PB"; - } - - /** - * Make a computer file size - * @param string $str file size in human readable format - * @return int|false a file size in bytes - * - * Makes 2kB to 2048. - * - * Inspired by: https://www.php.net/manual/en/function.filesize.php#92418 - */ - public static function computerFileSize($str) { - $str = strtolower($str); - if (is_numeric($str)) { - return (int)$str; - } - - $bytes_array = [ - 'b' => 1, - 'k' => 1024, - 'kb' => 1024, - 'mb' => 1024 * 1024, - 'm' => 1024 * 1024, - 'gb' => 1024 * 1024 * 1024, - 'g' => 1024 * 1024 * 1024, - 'tb' => 1024 * 1024 * 1024 * 1024, - 't' => 1024 * 1024 * 1024 * 1024, - 'pb' => 1024 * 1024 * 1024 * 1024 * 1024, - 'p' => 1024 * 1024 * 1024 * 1024 * 1024, - ]; - - $bytes = (float)$str; - - if (preg_match('#([kmgtp]?b?)$#si', $str, $matches) && !empty($bytes_array[$matches[1]])) { - $bytes *= $bytes_array[$matches[1]]; - } else { - return false; - } - - $bytes = round($bytes); - - return (int)$bytes; - } + private static ?ICacheFactory $cacheFactory = null; + private static ?bool $quotaIncludeExternalStorage = null; /** * Recursive copying of folders * @param string $src source folder * @param string $dest target folder - * + * @return void + * @deprecated 32.0.0 - use \OCP\Files\Folder::copy */ public static function copyr($src, $dest) { + if (!file_exists($src)) { + return; + } + if (is_dir($src)) { if (!is_dir($dest)) { mkdir($dest); } $files = scandir($src); foreach ($files as $file) { - if ($file != "." && $file != "..") { + if ($file != '.' && $file != '..') { self::copyr("$src/$file", "$dest/$file"); } } - } elseif (file_exists($src) && !\OC\Files\Filesystem::isFileBlacklisted($src)) { - copy($src, $dest); - } - } - - /** - * Recursive deletion of folders - * @param string $dir path to the folder - * @param bool $deleteSelf if set to false only the content of the folder will be deleted - * @return bool - */ - public static function rmdirr($dir, $deleteSelf = true) { - 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()); - } elseif ($fileInfo->isDir()) { - rmdir($fileInfo->getRealPath()); - } else { - unlink($fileInfo->getRealPath()); - } - } - if ($deleteSelf) { - rmdir($dir); - } - } elseif (file_exists($dir)) { - if ($deleteSelf) { - unlink($dir); + } else { + $validator = \OCP\Server::get(FilenameValidator::class); + if (!$validator->isForbidden($src)) { + copy($src, $dest); } } - if (!$deleteSelf) { - return true; - } - - return !file_exists($dir); } /** @@ -212,21 +84,22 @@ class OC_Helper { * @param bool $path * @internal param string $program name * @internal param string $optional search path, defaults to $PATH - * @return bool true if executable program found in path + * @return bool true if executable program found in path + * @deprecated 32.0.0 use the \OCP\IBinaryFinder */ public static function canExecute($name, $path = false) { // path defaults to PATH from environment if not set if ($path === false) { - $path = getenv("PATH"); + $path = getenv('PATH'); } // we look for an executable file of that name - $exts = [""]; - $check_fn = "is_executable"; + $exts = ['']; + $check_fn = 'is_executable'; // Default check will be done with $path directories : - $dirs = explode(PATH_SEPARATOR, $path); + $dirs = explode(PATH_SEPARATOR, (string)$path); // WARNING : We have to check if open_basedir is enabled : $obd = OC::$server->get(IniGetWrapper::class)->getString('open_basedir'); - if ($obd != "none") { + if ($obd != 'none') { $obd_values = explode(PATH_SEPARATOR, $obd); if (count($obd_values) > 0 and $obd_values[0]) { // open_basedir is in effect ! @@ -250,31 +123,10 @@ class OC_Helper { * @param resource $source * @param resource $target * @return array the number of bytes copied and result + * @deprecated 5.0.0 - Use \OCP\Files::streamCopy */ public static function streamCopy($source, $target) { - if (!$source or !$target) { - return [0, false]; - } - $bufSize = 8192; - $result = true; - $count = 0; - while (!feof($source)) { - $buf = fread($source, $bufSize); - $bytesWritten = fwrite($target, $buf); - if ($bytesWritten !== false) { - $count += $bytesWritten; - } - // note: strlen is expensive so only use it when necessary, - // on the last block - if ($bytesWritten === false - || ($bytesWritten < $bufSize && $bytesWritten < strlen($buf)) - ) { - // write error, could be disk full ? - $result = false; - break; - } - } - return [$count, $result]; + return \OCP\Files::streamCopy($source, $target, true); } /** @@ -337,115 +189,20 @@ class OC_Helper { } /** - * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is. - * - * @param array $input The array to work on - * @param int $case Either MB_CASE_UPPER or MB_CASE_LOWER (default) - * @param string $encoding The encoding parameter is the character encoding. Defaults to UTF-8 - * @return array - * - * Returns an array with all keys from input lowercased or uppercased. Numbered indices are left as is. - * based on https://www.php.net/manual/en/function.array-change-key-case.php#107715 - * - */ - public static function mb_array_change_key_case($input, $case = MB_CASE_LOWER, $encoding = 'UTF-8') { - $case = ($case != MB_CASE_UPPER) ? MB_CASE_LOWER : MB_CASE_UPPER; - $ret = []; - foreach ($input as $k => $v) { - $ret[mb_convert_case($k, $case, $encoding)] = $v; - } - return $ret; - } - - /** - * performs a search in a nested array - * @param array $haystack the array to be searched - * @param string $needle the search string - * @param mixed $index optional, only search this key name - * @return mixed the key of the matching field, otherwise false - * - * performs a search in a nested array - * - * taken from https://www.php.net/manual/en/function.array-search.php#97645 - */ - public static function recursiveArraySearch($haystack, $needle, $index = null) { - $aIt = new RecursiveArrayIterator($haystack); - $it = new RecursiveIteratorIterator($aIt); - - while ($it->valid()) { - if (((isset($index) and ($it->key() == $index)) or !isset($index)) and ($it->current() == $needle)) { - return $aIt->key(); - } - - $it->next(); - } - - return false; - } - - /** - * calculates the maximum upload size respecting system settings, free space and user quota - * - * @param string $dir the current folder where the user currently operates - * @param int $freeSpace the number of bytes free on the storage holding $dir, if not set this will be received from the storage directly - * @return int number of bytes representing - */ - public static function maxUploadFilesize($dir, $freeSpace = null) { - if (is_null($freeSpace) || $freeSpace < 0) { - $freeSpace = self::freeSpace($dir); - } - return min($freeSpace, self::uploadLimit()); - } - - /** - * Calculate free space left within user quota - * - * @param string $dir the current folder where the user currently operates - * @return int number of bytes representing - */ - public static function freeSpace($dir) { - $freeSpace = \OC\Files\Filesystem::free_space($dir); - if ($freeSpace < \OCP\Files\FileInfo::SPACE_UNLIMITED) { - $freeSpace = max($freeSpace, 0); - return $freeSpace; - } else { - return (INF > 0)? INF: PHP_INT_MAX; // work around https://bugs.php.net/bug.php?id=69188 - } - } - - /** - * Calculate PHP upload limit - * - * @return int PHP upload file size limit - */ - public static function uploadLimit() { - $ini = \OC::$server->get(IniGetWrapper::class); - $upload_max_filesize = (int)OCP\Util::computerFileSize($ini->get('upload_max_filesize')); - $post_max_size = (int)OCP\Util::computerFileSize($ini->get('post_max_size')); - if ($upload_max_filesize === 0 && $post_max_size === 0) { - return INF; - } elseif ($upload_max_filesize === 0 || $post_max_size === 0) { - return max($upload_max_filesize, $post_max_size); //only the non 0 value counts - } else { - return min($upload_max_filesize, $post_max_size); - } - } - - /** * Checks if a function is available * - * @deprecated Since 25.0.0 use \OCP\Util::isFunctionEnabled instead + * @deprecated 25.0.0 use \OCP\Util::isFunctionEnabled instead */ public static function is_function_enabled(string $function_name): bool { - return \OCP\Util::isFunctionEnabled($function_name); + return Util::isFunctionEnabled($function_name); } /** * Try to find a program - * @deprecated Since 25.0.0 Use \OC\BinaryFinder directly + * @deprecated 25.0.0 Use \OCP\IBinaryFinder directly */ public static function findBinaryPath(string $program): ?string { - $result = \OCP\Server::get(IBinaryFinder::class)->findBinaryPath($program); + $result = Server::get(IBinaryFinder::class)->findBinaryPath($program); return $result !== false ? $result : null; } @@ -459,24 +216,28 @@ class OC_Helper { * @param \OCP\Files\FileInfo $rootInfo (optional) * @param bool $includeMountPoints whether to include mount points in the size calculation * @param bool $useCache whether to use the cached quota values - * @return array + * @psalm-suppress LessSpecificReturnStatement Legacy code outputs weird types - manually validated that they are correct + * @return StorageInfo * @throws \OCP\Files\NotFoundException */ public static function getStorageInfo($path, $rootInfo = null, $includeMountPoints = true, $useCache = true) { - /** @var ICacheFactory $cacheFactory */ - $cacheFactory = \OC::$server->get(ICacheFactory::class); - $memcache = $cacheFactory->createLocal('storage_info'); + if (!self::$cacheFactory) { + self::$cacheFactory = Server::get(ICacheFactory::class); + } + $memcache = self::$cacheFactory->createLocal('storage_info'); // return storage info without adding mount points - $includeExtStorage = \OC::$server->getSystemConfig()->getValue('quota_include_external_storage', false); + if (self::$quotaIncludeExternalStorage === null) { + self::$quotaIncludeExternalStorage = \OC::$server->getSystemConfig()->getValue('quota_include_external_storage', false); + } $view = Filesystem::getView(); if (!$view) { throw new \OCP\Files\NotFoundException(); } - $fullPath = $view->getAbsolutePath($path); + $fullPath = Filesystem::normalizePath($view->getAbsolutePath($path)); - $cacheKey = $fullPath. '::' . ($includeMountPoints ? 'include' : 'exclude'); + $cacheKey = $fullPath . '::' . ($includeMountPoints ? 'include' : 'exclude'); if ($useCache) { $cached = $memcache->get($cacheKey); if ($cached) { @@ -485,23 +246,24 @@ class OC_Helper { } if (!$rootInfo) { - $rootInfo = \OC\Files\Filesystem::getFileInfo($path, $includeExtStorage ? 'ext' : false); + $rootInfo = \OC\Files\Filesystem::getFileInfo($path, self::$quotaIncludeExternalStorage ? 'ext' : false); } if (!$rootInfo instanceof \OCP\Files\FileInfo) { - throw new \OCP\Files\NotFoundException(); + throw new \OCP\Files\NotFoundException('The root directory of the user\'s files is missing'); } $used = $rootInfo->getSize($includeMountPoints); if ($used < 0) { - $used = 0; + $used = 0.0; } + /** @var int|float $quota */ $quota = \OCP\Files\FileInfo::SPACE_UNLIMITED; $mount = $rootInfo->getMountPoint(); $storage = $mount->getStorage(); $sourceStorage = $storage; if ($storage->instanceOfStorage('\OCA\Files_Sharing\SharedStorage')) { - $includeExtStorage = false; + self::$quotaIncludeExternalStorage = false; } - if ($includeExtStorage) { + if (self::$quotaIncludeExternalStorage) { if ($storage->instanceOfStorage('\OC\Files\Storage\Home') || $storage->instanceOfStorage('\OC\Files\ObjectStore\HomeObjectStoreStorage') ) { @@ -510,7 +272,7 @@ class OC_Helper { } else { $user = \OC::$server->getUserSession()->getUser(); } - $quota = OC_Util::getUserQuota($user); + $quota = $user?->getQuotaBytes() ?? \OCP\Files\FileInfo::SPACE_UNKNOWN; if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) { // always get free space / total space from root + mount points return self::getGlobalStorageInfo($quota, $user, $mount); @@ -524,14 +286,17 @@ class OC_Helper { } try { $free = $sourceStorage->free_space($rootInfo->getInternalPath()); + if (is_bool($free)) { + $free = 0.0; + } } catch (\Exception $e) { - if ($path === "") { + if ($path === '') { throw $e; } /** @var LoggerInterface $logger */ $logger = \OC::$server->get(LoggerInterface::class); - $logger->warning("Error while getting quota info, using root quota", ['exception' => $e]); - $rootInfo = self::getStorageInfo(""); + $logger->warning('Error while getting quota info, using root quota', ['exception' => $e]); + $rootInfo = self::getStorageInfo(''); $memcache->set($cacheKey, $rootInfo, 5 * 60); return $rootInfo; } @@ -550,9 +315,16 @@ class OC_Helper { $relative = 0; } + /* + * \OCA\Files_Sharing\External\Storage returns the cloud ID as the owner for the storage. + * It is unnecessary to query the user manager for the display name, as it won't have this information. + */ + $isRemoteShare = $storage->instanceOfStorage(\OCA\Files_Sharing\External\Storage::class); + $ownerId = $storage->getOwner($path); $ownerDisplayName = ''; - if ($ownerId) { + + if ($isRemoteShare === false && $ownerId !== false) { $ownerDisplayName = \OC::$server->getUserManager()->getDisplayName($ownerId) ?? ''; } @@ -574,6 +346,11 @@ class OC_Helper { 'mountPoint' => trim($mountPoint, '/'), ]; + if ($isRemoteShare === false && $ownerId !== false && $path === '/') { + // If path is root, store this as last known quota usage for this user + \OCP\Server::get(\OCP\IConfig::class)->setUserValue($ownerId, 'files', 'lastSeenQuotaUsage', (string)$relative); + } + $memcache->set($cacheKey, $info, 5 * 60); return $info; @@ -581,15 +358,20 @@ class OC_Helper { /** * Get storage info including all mount points and quota + * + * @psalm-suppress LessSpecificReturnStatement Legacy code outputs weird types - manually validated that they are correct + * @return StorageInfo */ - private static function getGlobalStorageInfo(int $quota, IUser $user, IMountPoint $mount): array { + private static function getGlobalStorageInfo(int|float $quota, IUser $user, IMountPoint $mount): array { $rootInfo = \OC\Files\Filesystem::getFileInfo('', 'ext'); + /** @var int|float $used */ $used = $rootInfo['size']; if ($used < 0) { - $used = 0; + $used = 0.0; } $total = $quota; + /** @var int|float $free */ $free = $quota - $used; if ($total > 0) { @@ -599,7 +381,7 @@ class OC_Helper { // prevent division by zero or error codes (negative values) $relative = round(($used / $total) * 10000) / 100; } else { - $relative = 0; + $relative = 0.0; } if (substr_count($mount->getMountPoint(), '/') < 3) { @@ -621,11 +403,21 @@ class OC_Helper { ]; } + public static function clearStorageInfo(string $absolutePath): void { + /** @var ICacheFactory $cacheFactory */ + $cacheFactory = \OC::$server->get(ICacheFactory::class); + $memcache = $cacheFactory->createLocal('storage_info'); + $cacheKeyPrefix = Filesystem::normalizePath($absolutePath) . '::'; + $memcache->remove($cacheKeyPrefix . 'include'); + $memcache->remove($cacheKeyPrefix . 'exclude'); + } + /** * Returns whether the config file is set manually to read-only * @return bool + * @deprecated 32.0.0 use the `config_is_read_only` system config directly */ public static function isReadOnlyConfigEnabled() { - return \OC::$server->getConfig()->getSystemValue('config_is_read_only', false); + return \OC::$server->getConfig()->getSystemValueBool('config_is_read_only', false); } } diff --git a/lib/private/legacy/OC_Hook.php b/lib/private/legacy/OC_Hook.php index a7a0f755673..e472b105498 100644 --- a/lib/private/legacy/OC_Hook.php +++ b/lib/private/legacy/OC_Hook.php @@ -1,33 +1,13 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Jakob Sack <mail@jakobsack.de> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Sam Tuke <mail@samtuke.com> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ + +use Psr\Log\LoggerInterface; + class OC_Hook { public static $thrownExceptions = []; @@ -66,8 +46,8 @@ class OC_Hook { } // Connect the hook handler to the requested emitter self::$registered[$signalClass][$signalName][] = [ - "class" => $slotClass, - "name" => $slotName + 'class' => $slotClass, + 'name' => $slotName ]; // No chance for failure ;-) @@ -102,10 +82,10 @@ class OC_Hook { // Call all slots foreach (self::$registered[$signalClass][$signalName] as $i) { try { - call_user_func([ $i["class"], $i["name"] ], $params); + call_user_func([ $i['class'], $i['name'] ], $params); } catch (Exception $e) { self::$thrownExceptions[] = $e; - \OC::$server->getLogger()->logException($e); + \OCP\Server::get(LoggerInterface::class)->error($e->getMessage(), ['exception' => $e]); if ($e instanceof \OCP\HintException) { throw $e; } diff --git a/lib/private/legacy/OC_Image.php b/lib/private/legacy/OC_Image.php deleted file mode 100644 index f2fa2058faa..00000000000 --- a/lib/private/legacy/OC_Image.php +++ /dev/null @@ -1,1152 +0,0 @@ -<?php - -declare(strict_types=1); - -/** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bartek Przybylski <bart.p.pl@gmail.com> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Byron Marohn <combustible@live.com> - * @author Côme Chilliet <come.chilliet@nextcloud.com> - * @author Christopher Schäpers <kondou@ts.unde.re> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author J0WI <J0WI@users.noreply.github.com> - * @author j-ed <juergen@eisfair.org> - * @author Joas Schilling <coding@schilljs.com> - * @author Johannes Willnecker <johannes@willnecker.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Olivier Paroz <github@oparoz.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Samuel CHEMLA <chemla.samuel@gmail.com> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Thomas Tanghus <thomas@tanghus.net> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ -use OCP\IImage; - -/** - * Class for basic image manipulation - */ -class OC_Image implements \OCP\IImage { - // Default memory limit for images to load (256 MBytes). - protected const DEFAULT_MEMORY_LIMIT = 256; - - // Default quality for jpeg images - protected const DEFAULT_JPEG_QUALITY = 80; - - /** @var false|resource|\GdImage */ - protected $resource = false; // tmp resource. - /** @var int */ - protected $imageType = IMAGETYPE_PNG; // Default to png if file type isn't evident. - /** @var null|string */ - protected $mimeType = 'image/png'; // Default to png - /** @var null|string */ - protected $filePath = null; - /** @var finfo */ - private $fileInfo; - /** @var \OCP\ILogger */ - private $logger; - /** @var \OCP\IConfig */ - private $config; - /** @var array */ - private $exif; - - /** - * Constructor. - * - * @param resource|string|\GdImage $imageRef The path to a local file, a base64 encoded string or a resource created by - * an imagecreate* function. - * @param \OCP\ILogger $logger - * @param \OCP\IConfig $config - * @throws \InvalidArgumentException in case the $imageRef parameter is not null - */ - public function __construct($imageRef = null, \OCP\ILogger $logger = null, \OCP\IConfig $config = null) { - $this->logger = $logger; - if ($logger === null) { - $this->logger = \OC::$server->getLogger(); - } - $this->config = $config; - if ($config === null) { - $this->config = \OC::$server->getConfig(); - } - - if (\OC_Util::fileInfoLoaded()) { - $this->fileInfo = new finfo(FILEINFO_MIME_TYPE); - } - - if ($imageRef !== null) { - throw new \InvalidArgumentException('The first parameter in the constructor is not supported anymore. Please use any of the load* methods of the image object to load an image.'); - } - } - - /** - * Determine whether the object contains an image resource. - * - * @return bool - */ - public function valid(): bool { - if ((is_resource($this->resource) && get_resource_type($this->resource) === 'gd') || - (is_object($this->resource) && get_class($this->resource) === \GdImage::class)) { - return true; - } - - return false; - } - - /** - * Returns the MIME type of the image or null if no image is loaded. - * - * @return string - */ - public function mimeType(): ?string { - return $this->valid() ? $this->mimeType : null; - } - - /** - * Returns the width of the image or -1 if no image is loaded. - * - * @return int - */ - public function width(): int { - if ($this->valid()) { - $width = imagesx($this->resource); - if ($width !== false) { - return $width; - } - } - return -1; - } - - /** - * Returns the height of the image or -1 if no image is loaded. - * - * @return int - */ - public function height(): int { - if ($this->valid()) { - $height = imagesy($this->resource); - if ($height !== false) { - return $height; - } - } - return -1; - } - - /** - * Returns the width when the image orientation is top-left. - * - * @return int - */ - public function widthTopLeft(): int { - $o = $this->getOrientation(); - $this->logger->debug('OC_Image->widthTopLeft() Orientation: ' . $o, ['app' => 'core']); - switch ($o) { - case -1: - case 1: - case 2: // Not tested - case 3: - case 4: // Not tested - return $this->width(); - case 5: // Not tested - case 6: - case 7: // Not tested - case 8: - return $this->height(); - } - return $this->width(); - } - - /** - * Returns the height when the image orientation is top-left. - * - * @return int - */ - public function heightTopLeft(): int { - $o = $this->getOrientation(); - $this->logger->debug('OC_Image->heightTopLeft() Orientation: ' . $o, ['app' => 'core']); - switch ($o) { - case -1: - case 1: - case 2: // Not tested - case 3: - case 4: // Not tested - return $this->height(); - case 5: // Not tested - case 6: - case 7: // Not tested - case 8: - return $this->width(); - } - return $this->height(); - } - - /** - * Outputs the image. - * - * @param string $mimeType - * @return bool - */ - public function show(string $mimeType = null): bool { - if ($mimeType === null) { - $mimeType = $this->mimeType(); - } - header('Content-Type: ' . $mimeType); - return $this->_output(null, $mimeType); - } - - /** - * Saves the image. - * - * @param string $filePath - * @param string $mimeType - * @return bool - */ - - public function save(?string $filePath = null, ?string $mimeType = null): bool { - if ($mimeType === null) { - $mimeType = $this->mimeType(); - } - if ($filePath === null) { - if ($this->filePath === null) { - $this->logger->error(__METHOD__ . '(): called with no path.', ['app' => 'core']); - return false; - } else { - $filePath = $this->filePath; - } - } - return $this->_output($filePath, $mimeType); - } - - /** - * Outputs/saves the image. - * - * @param string $filePath - * @param string $mimeType - * @return bool - * @throws Exception - */ - private function _output(?string $filePath = null, ?string $mimeType = null): bool { - if ($filePath) { - if (!file_exists(dirname($filePath))) { - mkdir(dirname($filePath), 0777, true); - } - $isWritable = is_writable(dirname($filePath)); - if (!$isWritable) { - $this->logger->error(__METHOD__ . '(): Directory \'' . dirname($filePath) . '\' is not writable.', ['app' => 'core']); - return false; - } elseif (file_exists($filePath) && !is_writable($filePath)) { - $this->logger->error(__METHOD__ . '(): File \'' . $filePath . '\' is not writable.', ['app' => 'core']); - return false; - } - } - if (!$this->valid()) { - return false; - } - - $imageType = $this->imageType; - if ($mimeType !== null) { - switch ($mimeType) { - case 'image/gif': - $imageType = IMAGETYPE_GIF; - break; - case 'image/jpeg': - $imageType = IMAGETYPE_JPEG; - break; - case 'image/png': - $imageType = IMAGETYPE_PNG; - break; - case 'image/x-xbitmap': - $imageType = IMAGETYPE_XBM; - break; - case 'image/bmp': - case 'image/x-ms-bmp': - $imageType = IMAGETYPE_BMP; - break; - default: - throw new Exception('\OC_Image::_output(): "' . $mimeType . '" is not supported when forcing a specific output format'); - } - } - - switch ($imageType) { - case IMAGETYPE_GIF: - $retVal = imagegif($this->resource, $filePath); - break; - case IMAGETYPE_JPEG: - /** @psalm-suppress InvalidScalarArgument */ - imageinterlace($this->resource, (PHP_VERSION_ID >= 80000 ? true : 1)); - $retVal = imagejpeg($this->resource, $filePath, $this->getJpegQuality()); - break; - case IMAGETYPE_PNG: - $retVal = imagepng($this->resource, $filePath); - break; - case IMAGETYPE_XBM: - if (function_exists('imagexbm')) { - $retVal = imagexbm($this->resource, $filePath); - } else { - throw new Exception('\OC_Image::_output(): imagexbm() is not supported.'); - } - - break; - case IMAGETYPE_WBMP: - $retVal = imagewbmp($this->resource, $filePath); - break; - case IMAGETYPE_BMP: - $retVal = imagebmp($this->resource, $filePath); - break; - default: - $retVal = imagepng($this->resource, $filePath); - } - return $retVal; - } - - /** - * Prints the image when called as $image(). - */ - public function __invoke() { - return $this->show(); - } - - /** - * @param resource|\GdImage $resource - * @throws \InvalidArgumentException in case the supplied resource does not have the type "gd" - */ - public function setResource($resource) { - // For PHP<8 - if (is_resource($resource) && get_resource_type($resource) === 'gd') { - $this->resource = $resource; - return; - } - // PHP 8 has real objects for GD stuff - if (is_object($resource) && get_class($resource) === \GdImage::class) { - $this->resource = $resource; - return; - } - throw new \InvalidArgumentException('Supplied resource is not of type "gd".'); - } - - /** - * @return false|resource|\GdImage Returns the image resource if any - */ - public function resource() { - return $this->resource; - } - - /** - * @return string Returns the mimetype of the data. Returns null if the data is not valid. - */ - public function dataMimeType(): ?string { - if (!$this->valid()) { - return null; - } - - switch ($this->mimeType) { - case 'image/png': - case 'image/jpeg': - case 'image/gif': - return $this->mimeType; - default: - return 'image/png'; - } - } - - /** - * @return null|string Returns the raw image data. - */ - public function data(): ?string { - if (!$this->valid()) { - return null; - } - ob_start(); - switch ($this->mimeType) { - case "image/png": - $res = imagepng($this->resource); - break; - case "image/jpeg": - /** @psalm-suppress InvalidScalarArgument */ - imageinterlace($this->resource, (PHP_VERSION_ID >= 80000 ? true : 1)); - $quality = $this->getJpegQuality(); - $res = imagejpeg($this->resource, null, $quality); - break; - case "image/gif": - $res = imagegif($this->resource); - break; - default: - $res = imagepng($this->resource); - $this->logger->info('OC_Image->data. Could not guess mime-type, defaulting to png', ['app' => 'core']); - break; - } - if (!$res) { - $this->logger->error('OC_Image->data. Error getting image data.', ['app' => 'core']); - } - return ob_get_clean(); - } - - /** - * @return string - base64 encoded, which is suitable for embedding in a VCard. - */ - public function __toString() { - return base64_encode($this->data()); - } - - /** - * @return int - */ - protected function getJpegQuality(): int { - $quality = $this->config->getAppValue('preview', 'jpeg_quality', (string) self::DEFAULT_JPEG_QUALITY); - // TODO: remove when getAppValue is type safe - if ($quality === null) { - $quality = self::DEFAULT_JPEG_QUALITY; - } - return min(100, max(10, (int) $quality)); - } - - /** - * (I'm open for suggestions on better method name ;) - * Get the orientation based on EXIF data. - * - * @return int The orientation or -1 if no EXIF data is available. - */ - public function getOrientation(): int { - if ($this->exif !== null) { - return $this->exif['Orientation']; - } - - if ($this->imageType !== IMAGETYPE_JPEG) { - $this->logger->debug('OC_Image->fixOrientation() Image is not a JPEG.', ['app' => 'core']); - return -1; - } - if (!is_callable('exif_read_data')) { - $this->logger->debug('OC_Image->fixOrientation() Exif module not enabled.', ['app' => 'core']); - return -1; - } - if (!$this->valid()) { - $this->logger->debug('OC_Image->fixOrientation() No image loaded.', ['app' => 'core']); - return -1; - } - if (is_null($this->filePath) || !is_readable($this->filePath)) { - $this->logger->debug('OC_Image->fixOrientation() No readable file path set.', ['app' => 'core']); - return -1; - } - $exif = @exif_read_data($this->filePath, 'IFD0'); - if (!$exif) { - return -1; - } - if (!isset($exif['Orientation'])) { - return -1; - } - $this->exif = $exif; - return $exif['Orientation']; - } - - public function readExif($data): void { - if (!is_callable('exif_read_data')) { - $this->logger->debug('OC_Image->fixOrientation() Exif module not enabled.', ['app' => 'core']); - return; - } - if (!$this->valid()) { - $this->logger->debug('OC_Image->fixOrientation() No image loaded.', ['app' => 'core']); - return; - } - - $exif = @exif_read_data('data://image/jpeg;base64,' . base64_encode($data)); - if (!$exif) { - return; - } - if (!isset($exif['Orientation'])) { - return; - } - $this->exif = $exif; - } - - /** - * (I'm open for suggestions on better method name ;) - * Fixes orientation based on EXIF data. - * - * @return bool - */ - public function fixOrientation(): bool { - if (!$this->valid()) { - $this->logger->debug(__METHOD__ . '(): No image loaded', ['app' => 'core']); - return false; - } - $o = $this->getOrientation(); - $this->logger->debug('OC_Image->fixOrientation() Orientation: ' . $o, ['app' => 'core']); - $rotate = 0; - $flip = false; - switch ($o) { - case -1: - return false; //Nothing to fix - case 1: - $rotate = 0; - break; - case 2: - $rotate = 0; - $flip = true; - break; - case 3: - $rotate = 180; - break; - case 4: - $rotate = 180; - $flip = true; - break; - case 5: - $rotate = 90; - $flip = true; - break; - case 6: - $rotate = 270; - break; - case 7: - $rotate = 270; - $flip = true; - break; - case 8: - $rotate = 90; - break; - } - if ($flip && function_exists('imageflip')) { - imageflip($this->resource, IMG_FLIP_HORIZONTAL); - } - if ($rotate) { - $res = imagerotate($this->resource, $rotate, 0); - if ($res) { - if (imagealphablending($res, true)) { - if (imagesavealpha($res, true)) { - imagedestroy($this->resource); - $this->resource = $res; - return true; - } else { - $this->logger->debug('OC_Image->fixOrientation() Error during alpha-saving', ['app' => 'core']); - return false; - } - } else { - $this->logger->debug('OC_Image->fixOrientation() Error during alpha-blending', ['app' => 'core']); - return false; - } - } else { - $this->logger->debug('OC_Image->fixOrientation() Error during orientation fixing', ['app' => 'core']); - return false; - } - } - return false; - } - - /** - * Loads an image from an open file handle. - * It is the responsibility of the caller to position the pointer at the correct place and to close the handle again. - * - * @param resource $handle - * @return resource|\GdImage|false An image resource or false on error - */ - public function loadFromFileHandle($handle) { - $contents = stream_get_contents($handle); - if ($this->loadFromData($contents)) { - return $this->resource; - } - return false; - } - - /** - * Check if allocating an image with the given size is allowed. - * - * @param int $width The image width. - * @param int $height The image height. - * @return bool true if allocating is allowed, false otherwise - */ - private function checkImageMemory($width, $height) { - $memory_limit = $this->config->getSystemValueInt('preview_max_memory', self::DEFAULT_MEMORY_LIMIT); - if ($memory_limit < 0) { - // Not limited. - return true; - } - - // Assume 32 bits per pixel. - if ($width * $height * 4 > $memory_limit * 1024 * 1024) { - $this->logger->info('Image size of ' . $width . 'x' . $height . ' would exceed allowed memory limit of ' . $memory_limit . '. You may increase the preview_max_memory in your config.php if you need previews of this image.'); - return false; - } - - return true; - } - - /** - * Check if loading an image file from the given path is allowed. - * - * @param string $path The path to a local file. - * @return bool true if allocating is allowed, false otherwise - */ - private function checkImageSize($path) { - $size = getimagesize($path); - if (!$size) { - return true; - } - - $width = $size[0]; - $height = $size[1]; - if (!$this->checkImageMemory($width, $height)) { - return false; - } - - return true; - } - - /** - * Check if loading an image from the given data is allowed. - * - * @param string $data A string of image data as read from a file. - * @return bool true if allocating is allowed, false otherwise - */ - private function checkImageDataSize($data) { - $size = getimagesizefromstring($data); - if (!$size) { - return true; - } - - $width = $size[0]; - $height = $size[1]; - if (!$this->checkImageMemory($width, $height)) { - return false; - } - - return true; - } - - /** - * Loads an image from a local file. - * - * @param bool|string $imagePath The path to a local file. - * @return bool|resource|\GdImage An image resource or false on error - */ - public function loadFromFile($imagePath = false) { - // exif_imagetype throws "read error!" if file is less than 12 byte - if (is_bool($imagePath) || !@is_file($imagePath) || !file_exists($imagePath) || filesize($imagePath) < 12 || !is_readable($imagePath)) { - return false; - } - $iType = exif_imagetype($imagePath); - switch ($iType) { - case IMAGETYPE_GIF: - if (imagetypes() & IMG_GIF) { - if (!$this->checkImageSize($imagePath)) { - return false; - } - $this->resource = imagecreatefromgif($imagePath); - if ($this->resource) { - // Preserve transparency - imagealphablending($this->resource, true); - imagesavealpha($this->resource, true); - } else { - $this->logger->debug('OC_Image->loadFromFile, GIF image not valid: ' . $imagePath, ['app' => 'core']); - } - } else { - $this->logger->debug('OC_Image->loadFromFile, GIF images not supported: ' . $imagePath, ['app' => 'core']); - } - break; - case IMAGETYPE_JPEG: - if (imagetypes() & IMG_JPG) { - if (!$this->checkImageSize($imagePath)) { - return false; - } - if (getimagesize($imagePath) !== false) { - $this->resource = @imagecreatefromjpeg($imagePath); - } else { - $this->logger->debug('OC_Image->loadFromFile, JPG image not valid: ' . $imagePath, ['app' => 'core']); - } - } else { - $this->logger->debug('OC_Image->loadFromFile, JPG images not supported: ' . $imagePath, ['app' => 'core']); - } - break; - case IMAGETYPE_PNG: - if (imagetypes() & IMG_PNG) { - if (!$this->checkImageSize($imagePath)) { - return false; - } - $this->resource = @imagecreatefrompng($imagePath); - if ($this->resource) { - // Preserve transparency - imagealphablending($this->resource, true); - imagesavealpha($this->resource, true); - } else { - $this->logger->debug('OC_Image->loadFromFile, PNG image not valid: ' . $imagePath, ['app' => 'core']); - } - } else { - $this->logger->debug('OC_Image->loadFromFile, PNG images not supported: ' . $imagePath, ['app' => 'core']); - } - break; - case IMAGETYPE_XBM: - if (imagetypes() & IMG_XPM) { - if (!$this->checkImageSize($imagePath)) { - return false; - } - $this->resource = @imagecreatefromxbm($imagePath); - } else { - $this->logger->debug('OC_Image->loadFromFile, XBM/XPM images not supported: ' . $imagePath, ['app' => 'core']); - } - break; - case IMAGETYPE_WBMP: - if (imagetypes() & IMG_WBMP) { - if (!$this->checkImageSize($imagePath)) { - return false; - } - $this->resource = @imagecreatefromwbmp($imagePath); - } else { - $this->logger->debug('OC_Image->loadFromFile, WBMP images not supported: ' . $imagePath, ['app' => 'core']); - } - break; - case IMAGETYPE_BMP: - $this->resource = imagecreatefrombmp($imagePath); - break; - case IMAGETYPE_WEBP: - if (imagetypes() & IMG_WEBP) { - if (!$this->checkImageSize($imagePath)) { - return false; - } - $this->resource = @imagecreatefromwebp($imagePath); - } else { - $this->logger->debug('OC_Image->loadFromFile, webp images not supported: ' . $imagePath, ['app' => 'core']); - } - break; - /* - case IMAGETYPE_TIFF_II: // (intel byte order) - break; - case IMAGETYPE_TIFF_MM: // (motorola byte order) - break; - case IMAGETYPE_JPC: - break; - case IMAGETYPE_JP2: - break; - case IMAGETYPE_JPX: - break; - case IMAGETYPE_JB2: - break; - case IMAGETYPE_SWC: - break; - case IMAGETYPE_IFF: - break; - case IMAGETYPE_ICO: - break; - case IMAGETYPE_SWF: - break; - case IMAGETYPE_PSD: - break; - */ - default: - - // this is mostly file created from encrypted file - $data = file_get_contents($imagePath); - if (!$this->checkImageDataSize($data)) { - return false; - } - $this->resource = imagecreatefromstring($data); - $iType = IMAGETYPE_PNG; - $this->logger->debug('OC_Image->loadFromFile, Default', ['app' => 'core']); - break; - } - if ($this->valid()) { - $this->imageType = $iType; - $this->mimeType = image_type_to_mime_type($iType); - $this->filePath = $imagePath; - } - return $this->resource; - } - - /** - * Loads an image from a string of data. - * - * @param string $str A string of image data as read from a file. - * @return bool|resource|\GdImage An image resource or false on error - */ - public function loadFromData(string $str) { - if (!$this->checkImageDataSize($str)) { - return false; - } - $this->resource = @imagecreatefromstring($str); - if ($this->fileInfo) { - $this->mimeType = $this->fileInfo->buffer($str); - } - if ($this->valid()) { - imagealphablending($this->resource, false); - imagesavealpha($this->resource, true); - } - - if (!$this->resource) { - $this->logger->debug('OC_Image->loadFromFile, could not load', ['app' => 'core']); - return false; - } - return $this->resource; - } - - /** - * Loads an image from a base64 encoded string. - * - * @param string $str A string base64 encoded string of image data. - * @return bool|resource|\GdImage An image resource or false on error - */ - public function loadFromBase64(string $str) { - $data = base64_decode($str); - if ($data) { // try to load from string data - if (!$this->checkImageDataSize($data)) { - return false; - } - $this->resource = @imagecreatefromstring($data); - if ($this->fileInfo) { - $this->mimeType = $this->fileInfo->buffer($data); - } - if (!$this->resource) { - $this->logger->debug('OC_Image->loadFromBase64, could not load', ['app' => 'core']); - return false; - } - return $this->resource; - } else { - return false; - } - } - - /** - * Resizes the image preserving ratio. - * - * @param int $maxSize The maximum size of either the width or height. - * @return bool - */ - public function resize(int $maxSize): bool { - if (!$this->valid()) { - $this->logger->debug(__METHOD__ . '(): No image loaded', ['app' => 'core']); - return false; - } - $result = $this->resizeNew($maxSize); - imagedestroy($this->resource); - $this->resource = $result; - return $this->valid(); - } - - /** - * @param $maxSize - * @return resource|bool|\GdImage - */ - private function resizeNew(int $maxSize) { - if (!$this->valid()) { - $this->logger->debug(__METHOD__ . '(): No image loaded', ['app' => 'core']); - return false; - } - $widthOrig = imagesx($this->resource); - $heightOrig = imagesy($this->resource); - $ratioOrig = $widthOrig / $heightOrig; - - if ($ratioOrig > 1) { - $newHeight = round($maxSize / $ratioOrig); - $newWidth = $maxSize; - } else { - $newWidth = round($maxSize * $ratioOrig); - $newHeight = $maxSize; - } - - return $this->preciseResizeNew((int)round($newWidth), (int)round($newHeight)); - } - - /** - * @param int $width - * @param int $height - * @return bool - */ - public function preciseResize(int $width, int $height): bool { - if (!$this->valid()) { - $this->logger->debug(__METHOD__ . '(): No image loaded', ['app' => 'core']); - return false; - } - $result = $this->preciseResizeNew($width, $height); - imagedestroy($this->resource); - $this->resource = $result; - return $this->valid(); - } - - /** - * @param int $width - * @param int $height - * @return resource|bool|\GdImage - */ - public function preciseResizeNew(int $width, int $height) { - if (!($width > 0) || !($height > 0)) { - $this->logger->info(__METHOD__ . '(): Requested image size not bigger than 0', ['app' => 'core']); - return false; - } - if (!$this->valid()) { - $this->logger->debug(__METHOD__ . '(): No image loaded', ['app' => 'core']); - return false; - } - $widthOrig = imagesx($this->resource); - $heightOrig = imagesy($this->resource); - $process = imagecreatetruecolor($width, $height); - if ($process === false) { - $this->logger->debug(__METHOD__ . '(): Error creating true color image', ['app' => 'core']); - return false; - } - - // preserve transparency - if ($this->imageType == IMAGETYPE_GIF or $this->imageType == IMAGETYPE_PNG) { - imagecolortransparent($process, imagecolorallocatealpha($process, 0, 0, 0, 127)); - imagealphablending($process, false); - imagesavealpha($process, true); - } - - $res = imagecopyresampled($process, $this->resource, 0, 0, 0, 0, $width, $height, $widthOrig, $heightOrig); - if ($res === false) { - $this->logger->debug(__METHOD__ . '(): Error re-sampling process image', ['app' => 'core']); - imagedestroy($process); - return false; - } - return $process; - } - - /** - * Crops the image to the middle square. If the image is already square it just returns. - * - * @param int $size maximum size for the result (optional) - * @return bool for success or failure - */ - public function centerCrop(int $size = 0): bool { - if (!$this->valid()) { - $this->logger->debug('OC_Image->centerCrop, No image loaded', ['app' => 'core']); - return false; - } - $widthOrig = imagesx($this->resource); - $heightOrig = imagesy($this->resource); - if ($widthOrig === $heightOrig and $size == 0) { - return true; - } - $ratioOrig = $widthOrig / $heightOrig; - $width = $height = min($widthOrig, $heightOrig); - - if ($ratioOrig > 1) { - $x = (int) (($widthOrig / 2) - ($width / 2)); - $y = 0; - } else { - $y = (int) (($heightOrig / 2) - ($height / 2)); - $x = 0; - } - if ($size > 0) { - $targetWidth = $size; - $targetHeight = $size; - } else { - $targetWidth = $width; - $targetHeight = $height; - } - $process = imagecreatetruecolor($targetWidth, $targetHeight); - if ($process === false) { - $this->logger->debug('OC_Image->centerCrop, Error creating true color image', ['app' => 'core']); - return false; - } - - // preserve transparency - if ($this->imageType == IMAGETYPE_GIF or $this->imageType == IMAGETYPE_PNG) { - imagecolortransparent($process, imagecolorallocatealpha($process, 0, 0, 0, 127)); - imagealphablending($process, false); - imagesavealpha($process, true); - } - - imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $targetWidth, $targetHeight, $width, $height); - if ($process === false) { - $this->logger->debug('OC_Image->centerCrop, Error re-sampling process image ' . $width . 'x' . $height, ['app' => 'core']); - return false; - } - imagedestroy($this->resource); - $this->resource = $process; - return true; - } - - /** - * Crops the image from point $x$y with dimension $wx$h. - * - * @param int $x Horizontal position - * @param int $y Vertical position - * @param int $w Width - * @param int $h Height - * @return bool for success or failure - */ - public function crop(int $x, int $y, int $w, int $h): bool { - if (!$this->valid()) { - $this->logger->debug(__METHOD__ . '(): No image loaded', ['app' => 'core']); - return false; - } - $result = $this->cropNew($x, $y, $w, $h); - imagedestroy($this->resource); - $this->resource = $result; - return $this->valid(); - } - - /** - * Crops the image from point $x$y with dimension $wx$h. - * - * @param int $x Horizontal position - * @param int $y Vertical position - * @param int $w Width - * @param int $h Height - * @return resource|\GdImage|false - */ - public function cropNew(int $x, int $y, int $w, int $h) { - if (!$this->valid()) { - $this->logger->debug(__METHOD__ . '(): No image loaded', ['app' => 'core']); - return false; - } - $process = imagecreatetruecolor($w, $h); - if ($process === false) { - $this->logger->debug(__METHOD__ . '(): Error creating true color image', ['app' => 'core']); - return false; - } - - // preserve transparency - if ($this->imageType == IMAGETYPE_GIF or $this->imageType == IMAGETYPE_PNG) { - imagecolortransparent($process, imagecolorallocatealpha($process, 0, 0, 0, 127)); - imagealphablending($process, false); - imagesavealpha($process, true); - } - - imagecopyresampled($process, $this->resource, 0, 0, $x, $y, $w, $h, $w, $h); - if ($process === false) { - $this->logger->debug(__METHOD__ . '(): Error re-sampling process image ' . $w . 'x' . $h, ['app' => 'core']); - return false; - } - return $process; - } - - /** - * Resizes the image to fit within a boundary while preserving ratio. - * - * Warning: Images smaller than $maxWidth x $maxHeight will end up being scaled up - * - * @param int $maxWidth - * @param int $maxHeight - * @return bool - */ - public function fitIn(int $maxWidth, int $maxHeight): bool { - if (!$this->valid()) { - $this->logger->debug(__METHOD__ . '(): No image loaded', ['app' => 'core']); - return false; - } - $widthOrig = imagesx($this->resource); - $heightOrig = imagesy($this->resource); - $ratio = $widthOrig / $heightOrig; - - $newWidth = min($maxWidth, $ratio * $maxHeight); - $newHeight = min($maxHeight, $maxWidth / $ratio); - - $this->preciseResize((int)round($newWidth), (int)round($newHeight)); - return true; - } - - /** - * Shrinks larger images to fit within specified boundaries while preserving ratio. - * - * @param int $maxWidth - * @param int $maxHeight - * @return bool - */ - public function scaleDownToFit(int $maxWidth, int $maxHeight): bool { - if (!$this->valid()) { - $this->logger->debug(__METHOD__ . '(): No image loaded', ['app' => 'core']); - return false; - } - $widthOrig = imagesx($this->resource); - $heightOrig = imagesy($this->resource); - - if ($widthOrig > $maxWidth || $heightOrig > $maxHeight) { - return $this->fitIn($maxWidth, $maxHeight); - } - - return false; - } - - public function copy(): IImage { - $image = new OC_Image(null, $this->logger, $this->config); - $image->resource = imagecreatetruecolor($this->width(), $this->height()); - imagecopy( - $image->resource(), - $this->resource(), - 0, - 0, - 0, - 0, - $this->width(), - $this->height() - ); - - return $image; - } - - public function cropCopy(int $x, int $y, int $w, int $h): IImage { - $image = new OC_Image(null, $this->logger, $this->config); - $image->imageType = $this->imageType; - $image->mimeType = $this->mimeType; - $image->resource = $this->cropNew($x, $y, $w, $h); - - return $image; - } - - public function preciseResizeCopy(int $width, int $height): IImage { - $image = new OC_Image(null, $this->logger, $this->config); - $image->imageType = $this->imageType; - $image->mimeType = $this->mimeType; - $image->resource = $this->preciseResizeNew($width, $height); - - return $image; - } - - public function resizeCopy(int $maxSize): IImage { - $image = new OC_Image(null, $this->logger, $this->config); - $image->imageType = $this->imageType; - $image->mimeType = $this->mimeType; - $image->resource = $this->resizeNew($maxSize); - - return $image; - } - - /** - * Destroys the current image and resets the object - */ - public function destroy(): void { - if ($this->valid()) { - imagedestroy($this->resource); - } - $this->resource = false; - } - - public function __destruct() { - $this->destroy(); - } -} - -if (!function_exists('exif_imagetype')) { - /** - * Workaround if exif_imagetype does not exist - * - * @link https://www.php.net/manual/en/function.exif-imagetype.php#80383 - * @param string $fileName - * @return string|boolean - */ - function exif_imagetype(string $fileName) { - if (($info = getimagesize($fileName)) !== false) { - return $info[2]; - } - return false; - } -} diff --git a/lib/private/legacy/OC_JSON.php b/lib/private/legacy/OC_JSON.php index b9cfb8210e0..6daef18dd61 100644 --- a/lib/private/legacy/OC_JSON.php +++ b/lib/private/legacy/OC_JSON.php @@ -1,37 +1,18 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Thomas Tanghus <thomas@tanghus.net> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ + +use OC\Authentication\TwoFactorAuth\Manager as TwoFactorAuthManager; + class OC_JSON { /** * Check if the app is enabled, send json error msg if not * @param string $app - * @deprecated Use the AppFramework instead. It will automatically check if the app is enabled. + * @deprecated 12.0.0 Use the AppFramework instead. It will automatically check if the app is enabled. * @suppress PhanDeprecatedFunction */ public static function checkAppEnabled($app) { @@ -44,11 +25,11 @@ class OC_JSON { /** * Check if the user is logged in, send json error msg if not - * @deprecated Use annotation based ACLs from the AppFramework instead + * @deprecated 12.0.0 Use annotation based ACLs from the AppFramework instead * @suppress PhanDeprecatedFunction */ public static function checkLoggedIn() { - $twoFactorAuthManger = \OC::$server->getTwoFactorAuthManager(); + $twoFactorAuthManger = \OC::$server->get(TwoFactorAuthManager::class); if (!\OC::$server->getUserSession()->isLoggedIn() || $twoFactorAuthManger->needsSecondFactor(\OC::$server->getUserSession()->getUser())) { $l = \OC::$server->getL10N('lib'); @@ -60,12 +41,12 @@ class OC_JSON { /** * Check an ajax get/post call if the request token is valid, send json error msg if not. - * @deprecated Use annotation based CSRF checks from the AppFramework instead + * @deprecated 12.0.0 Use annotation based CSRF checks from the AppFramework instead * @suppress PhanDeprecatedFunction */ public static function callCheck() { if (!\OC::$server->getRequest()->passesStrictCookieCheck()) { - header('Location: '.\OC::$WEBROOT); + header('Location: ' . \OC::$WEBROOT); exit(); } @@ -78,7 +59,7 @@ class OC_JSON { /** * Check if the user is a admin, send json error msg if not. - * @deprecated Use annotation based ACLs from the AppFramework instead + * @deprecated 12.0.0 Use annotation based ACLs from the AppFramework instead * @suppress PhanDeprecatedFunction */ public static function checkAdminUser() { @@ -91,9 +72,8 @@ class OC_JSON { /** * Send json error msg - * @deprecated Use a AppFramework JSONResponse instead + * @deprecated 12.0.0 Use a AppFramework JSONResponse instead * @suppress PhanDeprecatedFunction - * @psalm-taint-escape html */ public static function error($data = []) { $data['status'] = 'error'; @@ -103,9 +83,8 @@ class OC_JSON { /** * Send json success msg - * @deprecated Use a AppFramework JSONResponse instead + * @deprecated 12.0.0 Use a AppFramework JSONResponse instead * @suppress PhanDeprecatedFunction - * @psalm-taint-escape html */ public static function success($data = []) { $data['status'] = 'success'; @@ -114,22 +93,13 @@ class OC_JSON { } /** - * Convert OC_L10N_String to string, for use in json encodings - */ - protected static function to_string(&$value) { - if ($value instanceof \OC\L10N\L10NString) { - $value = (string)$value; - } - } - - /** * Encode JSON - * @deprecated Use a AppFramework JSONResponse instead + * @deprecated 12.0.0 Use a AppFramework JSONResponse instead + * + * @psalm-taint-escape has_quotes + * @psalm-taint-escape html */ - public static function encode($data) { - if (is_array($data)) { - array_walk_recursive($data, ['OC_JSON', 'to_string']); - } + private static function encode($data) { return json_encode($data, JSON_HEX_TAG); } } diff --git a/lib/private/legacy/OC_Response.php b/lib/private/legacy/OC_Response.php index b849710e90b..c45852b4b1d 100644 --- a/lib/private/legacy/OC_Response.php +++ b/lib/private/legacy/OC_Response.php @@ -1,31 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Andreas Fischer <bantu@owncloud.com> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author J0WI <J0WI@users.noreply.github.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ class OC_Response { /** @@ -52,7 +30,20 @@ class OC_Response { * @param string|int|float $length Length to be sent */ public static function setContentLengthHeader($length) { - header('Content-Length: '.$length); + if (PHP_INT_SIZE === 4) { + if ($length > PHP_INT_MAX && stripos(PHP_SAPI, 'apache') === 0) { + // Apache PHP SAPI casts Content-Length headers to PHP integers. + // This enforces a limit of PHP_INT_MAX (2147483647 on 32-bit + // platforms). So, if the length is greater than PHP_INT_MAX, + // we just do not send a Content-Length header to prevent + // bodies from being received incompletely. + return; + } + // Convert signed integer or float to unsigned base-10 string. + $lfh = new \OC\LargeFileHelper; + $length = $lfh->formatUnsignedInteger($length); + } + header('Content-Length: ' . $length); } /** @@ -68,7 +59,7 @@ class OC_Response { * @see \OCP\AppFramework\Http\Response::getHeaders */ $policy = 'default-src \'self\'; ' - . 'script-src \'self\' \'nonce-'.\OC::$server->getContentSecurityPolicyNonceManager()->getNonce().'\'; ' + . 'script-src \'self\' \'nonce-' . \OC::$server->getContentSecurityPolicyNonceManager()->getNonce() . '\'; ' . 'style-src \'self\' \'unsafe-inline\'; ' . 'frame-src *; ' . 'img-src * data: blob:; ' @@ -86,8 +77,7 @@ class OC_Response { header('X-Content-Type-Options: nosniff'); // Disable sniffing the content type for IE header('X-Frame-Options: SAMEORIGIN'); // Disallow iFraming from other domains header('X-Permitted-Cross-Domain-Policies: none'); // https://www.adobe.com/devnet/adobe-media-server/articles/cross-domain-xml-for-streaming.html - header('X-Robots-Tag: none'); // https://developers.google.com/webmasters/control-crawl-index/docs/robots_meta_tag - header('X-XSS-Protection: 1; mode=block'); // Enforce browser based XSS filters + header('X-Robots-Tag: noindex, nofollow'); // https://developers.google.com/webmasters/control-crawl-index/docs/robots_meta_tag } } } diff --git a/lib/private/legacy/OC_Template.php b/lib/private/legacy/OC_Template.php index 0c9fa1ccc0c..bccf99af65e 100644 --- a/lib/private/legacy/OC_Template.php +++ b/lib/private/legacy/OC_Template.php @@ -1,275 +1,29 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Brice Maron <brice@bmaron.net> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Frank Karlitschek <frank@karlitschek.de> - * @author Individual IT Services <info@individual-it.net> - * @author Jakob Sack <mail@jakobsack.de> - * @author Jan-Christoph Borchardt <hey@jancborchardt.net> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Marin Treselj <marin@pixelipo.com> - * @author Michael Letzgus <www@chronos.michael-letzgus.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ -use OC\TemplateLayout; -use OCP\AppFramework\Http\TemplateResponse; -use OCP\Util; - -require_once __DIR__.'/template/functions.php'; +use OCP\Server; +use OCP\Template\ITemplateManager; /** * This class provides the templates for ownCloud. + * @deprecated 32.0.0 Use \OCP\Template\ITemplateManager instead */ -class OC_Template extends \OC\Template\Base { - /** @var string */ - private $renderAs; // Create a full page? - - /** @var string */ - private $path; // The path to the template - - /** @var array */ - private $headers = []; //custom headers - - /** @var string */ - protected $app; // app id - - protected static $initTemplateEngineFirstRun = true; - - /** - * Constructor - * - * @param string $app app providing the template - * @param string $name of the template file (without suffix) - * @param string $renderAs If $renderAs is set, OC_Template will try to - * produce a full page in the according layout. For - * now, $renderAs can be set to "guest", "user" or - * "admin". - * @param bool $registerCall = true - */ - public function __construct($app, $name, $renderAs = TemplateResponse::RENDER_AS_BLANK, $registerCall = true) { - // Read the selected theme from the config file - self::initTemplateEngine($renderAs); - - $theme = OC_Util::getTheme(); - - $requestToken = (OC::$server->getSession() && $registerCall) ? \OCP\Util::callRegister() : ''; - - $parts = explode('/', $app); // fix translation when app is something like core/lostpassword - $l10n = \OC::$server->getL10N($parts[0]); - /** @var \OCP\Defaults $themeDefaults */ - $themeDefaults = \OC::$server->query(\OCP\Defaults::class); - - [$path, $template] = $this->findTemplate($theme, $app, $name); - - // Set the private data - $this->renderAs = $renderAs; - $this->path = $path; - $this->app = $app; - - parent::__construct($template, $requestToken, $l10n, $themeDefaults); - } - - /** - * @param string $renderAs - */ - public static function initTemplateEngine($renderAs) { - if (self::$initTemplateEngineFirstRun) { - // apps that started before the template initialization can load their own scripts/styles - // so to make sure this scripts/styles here are loaded first we put all core scripts first - // check lib/public/Util.php - OC_Util::addStyle('server', null, true); - - // include common nextcloud webpack bundle - Util::addScript('core', 'common'); - Util::addScript('core', 'main'); - Util::addTranslations('core'); - - if (\OC::$server->getSystemConfig()->getValue('installed', false) && !\OCP\Util::needUpgrade()) { - Util::addScript('core', 'files_fileinfo'); - Util::addScript('core', 'files_client'); - Util::addScript('core', 'merged-template-prepend'); - } - - // If installed and background job is set to ajax, add dedicated script - if (\OC::$server->getSystemConfig()->getValue('installed', false) - && $renderAs !== TemplateResponse::RENDER_AS_ERROR - && !\OCP\Util::needUpgrade()) { - if (\OC::$server->getConfig()->getAppValue('core', 'backgroundjobs_mode', 'ajax') == 'ajax') { - Util::addScript('core', 'backgroundjobs'); - } - } - - self::$initTemplateEngineFirstRun = false; - } - } - - - /** - * find the template with the given name - * @param string $name of the template file (without suffix) - * - * Will select the template file for the selected theme. - * Checking all the possible locations. - * @param string $theme - * @param string $app - * @return string[] - */ - protected function findTemplate($theme, $app, $name) { - // Check if it is a app template or not. - if ($app !== '') { - $dirs = $this->getAppTemplateDirs($theme, $app, OC::$SERVERROOT, OC_App::getAppPath($app)); - } else { - $dirs = $this->getCoreTemplateDirs($theme, OC::$SERVERROOT); - } - $locator = new \OC\Template\TemplateFileLocator($dirs); - $template = $locator->find($name); - $path = $locator->getPath(); - return [$path, $template]; - } - - /** - * Add a custom element to the header - * @param string $tag tag name of the element - * @param array $attributes array of attributes for the element - * @param string $text the text content for the element. If $text is null then the - * element will be written as empty element. So use "" to get a closing tag. - */ - public function addHeader($tag, $attributes, $text = null) { - $this->headers[] = [ - 'tag' => $tag, - 'attributes' => $attributes, - 'text' => $text - ]; - } - - /** - * Process the template - * @return string - * - * This function process the template. If $this->renderAs is set, it - * will produce a full page. - */ - public function fetchPage($additionalParams = null) { - $data = parent::fetchPage($additionalParams); - - if ($this->renderAs) { - $page = new TemplateLayout($this->renderAs, $this->app); - - if (is_array($additionalParams)) { - foreach ($additionalParams as $key => $value) { - $page->assign($key, $value); - } - } - - // Add custom headers - $headers = ''; - foreach (OC_Util::$headers as $header) { - $headers .= '<'.\OCP\Util::sanitizeHTML($header['tag']); - if (strcasecmp($header['tag'], 'script') === 0 && in_array('src', array_map('strtolower', array_keys($header['attributes'])))) { - $headers .= ' defer'; - } - foreach ($header['attributes'] as $name => $value) { - $headers .= ' '.\OCP\Util::sanitizeHTML($name).'="'.\OCP\Util::sanitizeHTML($value).'"'; - } - if ($header['text'] !== null) { - $headers .= '>'.\OCP\Util::sanitizeHTML($header['text']).'</'.\OCP\Util::sanitizeHTML($header['tag']).'>'; - } else { - $headers .= '/>'; - } - } - - $page->assign('headers', $headers); - - $page->assign('content', $data); - return $page->fetchPage($additionalParams); - } - - return $data; - } - - /** - * Include template - * - * @param string $file - * @param array|null $additionalParams - * @return string returns content of included template - * - * Includes another template. use <?php echo $this->inc('template'); ?> to - * do this. - */ - public function inc($file, $additionalParams = null) { - return $this->load($this->path.$file.'.php', $additionalParams); - } - - /** - * Shortcut to print a simple page for users - * @param string $application The application we render the template for - * @param string $name Name of the template - * @param array $parameters Parameters for the template - * @return boolean|null - */ - public static function printUserPage($application, $name, $parameters = []) { - $content = new OC_Template($application, $name, "user"); - foreach ($parameters as $key => $value) { - $content->assign($key, $value); - } - print $content->printPage(); - } - - /** - * Shortcut to print a simple page for admins - * @param string $application The application we render the template for - * @param string $name Name of the template - * @param array $parameters Parameters for the template - * @return bool - */ - public static function printAdminPage($application, $name, $parameters = []) { - $content = new OC_Template($application, $name, "admin"); - foreach ($parameters as $key => $value) { - $content->assign($key, $value); - } - return $content->printPage(); - } - +class OC_Template extends \OC\Template\Template { /** * Shortcut to print a simple page for guests * @param string $application The application we render the template for * @param string $name Name of the template - * @param array|string $parameters Parameters for the template + * @param array $parameters Parameters for the template * @return bool + * @deprecated 32.0.0 Use \OCP\Template\ITemplateManager instead */ public static function printGuestPage($application, $name, $parameters = []) { - $content = new OC_Template($application, $name, $name === 'error' ? $name : 'guest'); - foreach ($parameters as $key => $value) { - $content->assign($key, $value); - } - return $content->printPage(); + Server::get(ITemplateManager::class)->printGuestPage($application, $name, $parameters); + return true; } /** @@ -277,81 +31,21 @@ class OC_Template extends \OC\Template\Base { * @param string $error_msg The error message to show * @param string $hint An optional hint message - needs to be properly escape * @param int $statusCode - * @suppress PhanAccessMethodInternal + * @return never + * @deprecated 32.0.0 Use \OCP\Template\ITemplateManager instead */ public static function printErrorPage($error_msg, $hint = '', $statusCode = 500) { - if (\OC::$server->getAppManager()->isEnabledForUser('theming') && !\OC_App::isAppLoaded('theming')) { - \OC_App::loadApp('theming'); - } - - - if ($error_msg === $hint) { - // If the hint is the same as the message there is no need to display it twice. - $hint = ''; - } - - http_response_code($statusCode); - try { - $content = new \OC_Template('', 'error', 'error', false); - $errors = [['error' => $error_msg, 'hint' => $hint]]; - $content->assign('errors', $errors); - $content->printPage(); - } catch (\Exception $e) { - $logger = \OC::$server->getLogger(); - $logger->error("$error_msg $hint", ['app' => 'core']); - $logger->logException($e, ['app' => 'core']); - - header('Content-Type: text/plain; charset=utf-8'); - print("$error_msg $hint"); - } - die(); + Server::get(ITemplateManager::class)->printErrorPage($error_msg, $hint, $statusCode); } /** * print error page using Exception details * @param Exception|Throwable $exception * @param int $statusCode - * @return bool|string - * @suppress PhanAccessMethodInternal + * @return never + * @deprecated 32.0.0 Use \OCP\Template\ITemplateManager instead */ public static function printExceptionErrorPage($exception, $statusCode = 503) { - http_response_code($statusCode); - try { - $request = \OC::$server->getRequest(); - $content = new \OC_Template('', 'exception', 'error', false); - $content->assign('errorClass', get_class($exception)); - $content->assign('errorMsg', $exception->getMessage()); - $content->assign('errorCode', $exception->getCode()); - $content->assign('file', $exception->getFile()); - $content->assign('line', $exception->getLine()); - $content->assign('exception', $exception); - $content->assign('debugMode', \OC::$server->getSystemConfig()->getValue('debug', false)); - $content->assign('remoteAddr', $request->getRemoteAddress()); - $content->assign('requestID', $request->getId()); - $content->printPage(); - } catch (\Exception $e) { - try { - $logger = \OC::$server->getLogger(); - $logger->logException($exception, ['app' => 'core']); - $logger->logException($e, ['app' => 'core']); - } catch (Throwable $e) { - // no way to log it properly - but to avoid a white page of death we send some output - header('Content-Type: text/plain; charset=utf-8'); - print("Internal Server Error\n\n"); - print("The server encountered an internal error and was unable to complete your request.\n"); - print("Please contact the server administrator if this error reappears multiple times, please include the technical details below in your report.\n"); - print("More details can be found in the server log.\n"); - - // and then throw it again to log it at least to the web server error log - throw $e; - } - - header('Content-Type: text/plain; charset=utf-8'); - print("Internal Server Error\n\n"); - print("The server encountered an internal error and was unable to complete your request.\n"); - print("Please contact the server administrator if this error reappears multiple times, please include the technical details below in your report.\n"); - print("More details can be found in the server log.\n"); - } - die(); + Server::get(ITemplateManager::class)->printExceptionErrorPage($exception, $statusCode); } } diff --git a/lib/private/legacy/OC_User.php b/lib/private/legacy/OC_User.php index 8aaa9072ba4..e5343864c45 100644 --- a/lib/private/legacy/OC_User.php +++ b/lib/private/legacy/OC_User.php @@ -1,46 +1,25 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Aldo "xoen" Giambelluca <xoen@xoen.org> - * @author Andreas Fischer <bantu@owncloud.com> - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bartek Przybylski <bart.p.pl@gmail.com> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author Jakob Sack <mail@jakobsack.de> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author shkdee <louis.traynard@m4x.org> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - -use OC\User\LoginException; +use OC\Authentication\Token\IProvider; +use OC\User\DisabledUserException; +use OCP\Authentication\Exceptions\InvalidTokenException; +use OCP\Authentication\Exceptions\WipeTokenException; +use OCP\Authentication\Token\IToken; use OCP\EventDispatcher\IEventDispatcher; -use OCP\ILogger; +use OCP\IGroupManager; +use OCP\ISession; +use OCP\IUser; use OCP\IUserManager; +use OCP\Server; +use OCP\Session\Exceptions\SessionNotAvailableException; +use OCP\User\Events\BeforeUserLoggedInEvent; use OCP\User\Events\UserLoggedInEvent; +use Psr\Log\LoggerInterface; /** * This class provides wrapper methods for user management. Multiple backends are @@ -61,8 +40,6 @@ use OCP\User\Events\UserLoggedInEvent; * logout() */ class OC_User { - private static $_usedBackends = []; - private static $_setupedBackends = []; // bool, stores if a user want to access a resource anonymously, e.g if they open a public link @@ -73,17 +50,16 @@ class OC_User { * * @param string|\OCP\UserInterface $backend default: database The backend to use for user management * @return bool + * @deprecated 32.0.0 Use IUserManager::registerBackend instead * * Set the User Authentication Module - * @suppress PhanDeprecatedFunction */ public static function useBackend($backend = 'database') { if ($backend instanceof \OCP\UserInterface) { - self::$_usedBackends[get_class($backend)] = $backend; - \OC::$server->getUserManager()->registerBackend($backend); + Server::get(IUserManager::class)->registerBackend($backend); } else { // You'll never know what happens - if (null === $backend or !is_string($backend)) { + if ($backend === null or !is_string($backend)) { $backend = 'database'; } @@ -92,19 +68,16 @@ class OC_User { case 'database': case 'mysql': case 'sqlite': - \OCP\Util::writeLog('core', 'Adding user backend ' . $backend . '.', ILogger::DEBUG); - self::$_usedBackends[$backend] = new \OC\User\Database(); - \OC::$server->getUserManager()->registerBackend(self::$_usedBackends[$backend]); + Server::get(LoggerInterface::class)->debug('Adding user backend ' . $backend . '.', ['app' => 'core']); + Server::get(IUserManager::class)->registerBackend(new \OC\User\Database()); break; case 'dummy': - self::$_usedBackends[$backend] = new \Test\Util\User\Dummy(); - \OC::$server->getUserManager()->registerBackend(self::$_usedBackends[$backend]); + Server::get(IUserManager::class)->registerBackend(new \Test\Util\User\Dummy()); break; default: - \OCP\Util::writeLog('core', 'Adding default user backend ' . $backend . '.', ILogger::DEBUG); + Server::get(LoggerInterface::class)->debug('Adding default user backend ' . $backend . '.', ['app' => 'core']); $className = 'OC_USER_' . strtoupper($backend); - self::$_usedBackends[$backend] = new $className(); - \OC::$server->getUserManager()->registerBackend(self::$_usedBackends[$backend]); + Server::get(IUserManager::class)->registerBackend(new $className()); break; } } @@ -113,10 +86,10 @@ class OC_User { /** * remove all used backends + * @deprecated 32.0.0 Use IUserManager::clearBackends instead */ public static function clearBackends() { - self::$_usedBackends = []; - \OC::$server->getUserManager()->clearBackends(); + Server::get(IUserManager::class)->clearBackends(); } /** @@ -137,7 +110,7 @@ class OC_User { $class = $config['class']; $arguments = $config['arguments']; if (class_exists($class)) { - if (array_search($i, self::$_setupedBackends) === false) { + if (!in_array($i, self::$_setupedBackends)) { // make a reflection object $reflectionObj = new ReflectionClass($class); @@ -146,10 +119,10 @@ class OC_User { self::useBackend($backend); self::$_setupedBackends[] = $i; } else { - \OCP\Util::writeLog('core', 'User backend ' . $class . ' already initialized.', ILogger::DEBUG); + Server::get(LoggerInterface::class)->debug('User backend ' . $class . ' already initialized.', ['app' => 'core']); } } else { - \OCP\Util::writeLog('core', 'User backend ' . $class . ' not found.', ILogger::ERROR); + Server::get(LoggerInterface::class)->error('User backend ' . $class . ' not found.', ['app' => 'core']); } } } @@ -166,15 +139,19 @@ class OC_User { public static function loginWithApache(\OCP\Authentication\IApacheBackend $backend) { $uid = $backend->getCurrentUserId(); $run = true; - OC_Hook::emit("OC_User", "pre_login", ["run" => &$run, "uid" => $uid, 'backend' => $backend]); + OC_Hook::emit('OC_User', 'pre_login', ['run' => &$run, 'uid' => $uid, 'backend' => $backend]); if ($uid) { if (self::getUser() !== $uid) { self::setUserId($uid); $userSession = \OC::$server->getUserSession(); + + /** @var IEventDispatcher $dispatcher */ + $dispatcher = \OC::$server->get(IEventDispatcher::class); + if ($userSession->getUser() && !$userSession->getUser()->isEnabled()) { - $message = \OC::$server->getL10N('lib')->t('User disabled'); - throw new LoginException($message); + $message = \OC::$server->getL10N('lib')->t('Account disabled'); + throw new DisabledUserException($message); } $userSession->setLoginName($uid); $request = OC::$server->getRequest(); @@ -182,8 +159,28 @@ class OC_User { if ($backend instanceof \OCP\Authentication\IProvideUserSecretBackend) { $password = $backend->getCurrentUserSecret(); } + + /** @var IEventDispatcher $dispatcher */ + $dispatcher->dispatchTyped(new BeforeUserLoggedInEvent($uid, $password, $backend)); + $userSession->createSessionToken($request, $uid, $uid, $password); $userSession->createRememberMeToken($userSession->getUser()); + + if (empty($password)) { + $tokenProvider = \OC::$server->get(IProvider::class); + try { + $token = $tokenProvider->getToken($userSession->getSession()->getId()); + $token->setScope([ + IToken::SCOPE_SKIP_PASSWORD_VALIDATION => true, + IToken::SCOPE_FILESYSTEM => true, + ]); + $tokenProvider->updateToken($token); + } catch (InvalidTokenException|WipeTokenException|SessionNotAvailableException) { + // swallow the exceptions as we do not deal with them here + // simply skip updating the token when is it missing + } + } + // setup the filesystem OC_Util::setupFS($uid); // first call the post_login hooks, the login-process needs to be @@ -199,8 +196,6 @@ class OC_User { 'isTokenLogin' => false, ] ); - /** @var IEventDispatcher $dispatcher */ - $dispatcher = \OC::$server->get(IEventDispatcher::class); $dispatcher->dispatchTyped(new UserLoggedInEvent( \OC::$server->get(IUserManager::class)->get($uid), $uid, @@ -220,9 +215,9 @@ class OC_User { * Verify with Apache whether user is authenticated. * * @return boolean|null - * true: authenticated - * false: not authenticated - * null: not handled / no backend available + * true: authenticated + * false: not authenticated + * null: not handled / no backend available */ public static function handleApacheAuth() { $backend = self::findFirstActiveUsedBackend(); @@ -247,7 +242,7 @@ class OC_User { */ public static function setUserId($uid) { $userSession = \OC::$server->getUserSession(); - $userManager = \OC::$server->getUserManager(); + $userManager = Server::get(IUserManager::class); if ($user = $userManager->get($uid)) { $userSession->setUser($user); } else { @@ -258,7 +253,7 @@ class OC_User { /** * Check if the user is logged in, considers also the HTTP basic credentials * - * @deprecated use \OC::$server->getUserSession()->isLoggedIn() + * @deprecated 12.0.0 use \OC::$server->getUserSession()->isLoggedIn() * @return bool */ public static function isLoggedIn() { @@ -296,7 +291,7 @@ class OC_User { } $user = \OC::$server->getUserSession()->getUser(); - if ($user instanceof \OCP\IUser) { + if ($user instanceof IUser) { $backend = $user->getBackend(); if ($backend instanceof \OCP\User\Backend\ICustomLogout) { return $backend->getLogoutUrl(); @@ -316,12 +311,9 @@ class OC_User { * @return bool */ public static function isAdminUser($uid) { - $group = \OC::$server->getGroupManager()->get('admin'); - $user = \OC::$server->getUserManager()->get($uid); - if ($group && $user && $group->inGroup($user) && self::$incognitoMode === false) { - return true; - } - return false; + $user = Server::get(IUserManager::class)->get($uid); + $isAdmin = $user && Server::get(IGroupManager::class)->isAdmin($user->getUID()); + return $isAdmin && self::$incognitoMode === false; } @@ -331,7 +323,7 @@ class OC_User { * @return string|false uid or false */ public static function getUser() { - $uid = \OC::$server->getSession() ? \OC::$server->getSession()->get('user_id') : null; + $uid = Server::get(ISession::class)?->get('user_id'); if (!is_null($uid) && self::$incognitoMode === false) { return $uid; } else { @@ -350,7 +342,7 @@ class OC_User { * Change the password of a user */ public static function setPassword($uid, $password, $recoveryPassword = null) { - $user = \OC::$server->getUserManager()->get($uid); + $user = Server::get(IUserManager::class)->get($uid); if ($user) { return $user->setPassword($password, $recoveryPassword); } else { @@ -363,10 +355,10 @@ class OC_User { * @return string * * returns the path to the users home directory - * @deprecated Use \OC::$server->getUserManager->getHome() + * @deprecated 12.0.0 Use \OC::$server->getUserManager->getHome() */ public static function getHome($uid) { - $user = \OC::$server->getUserManager()->get($uid); + $user = Server::get(IUserManager::class)->get($uid); if ($user) { return $user->getHome(); } else { @@ -383,11 +375,11 @@ class OC_User { * @return array associative array with all display names (value) and corresponding uids (key) * * Get a list of all display names and user ids. - * @deprecated Use \OC::$server->getUserManager->searchDisplayName($search, $limit, $offset) instead. + * @deprecated 12.0.0 Use \OC::$server->getUserManager->searchDisplayName($search, $limit, $offset) instead. */ public static function getDisplayNames($search = '', $limit = null, $offset = null) { $displayNames = []; - $users = \OC::$server->getUserManager()->searchDisplayName($search, $limit, $offset); + $users = Server::get(IUserManager::class)->searchDisplayName($search, $limit, $offset); foreach ($users as $user) { $displayNames[$user->getUID()] = $user->getDisplayName(); } @@ -400,7 +392,7 @@ class OC_User { * @return OCP\Authentication\IApacheBackend|null if no backend active, otherwise OCP\Authentication\IApacheBackend */ private static function findFirstActiveUsedBackend() { - foreach (self::$_usedBackends as $backend) { + foreach (Server::get(IUserManager::class)->getBackends() as $backend) { if ($backend instanceof OCP\Authentication\IApacheBackend) { if ($backend->isSessionActive()) { return $backend; diff --git a/lib/private/legacy/OC_Util.php b/lib/private/legacy/OC_Util.php index 6dc08341b18..ebca1105838 100644 --- a/lib/private/legacy/OC_Util.php +++ b/lib/private/legacy/OC_Util.php @@ -1,93 +1,30 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Bart Visscher <bartv@thisnet.nl> - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Birk Borkason <daniel.niccoli@gmail.com> - * @author Bjoern Schiessle <bjoern@schiessle.org> - * @author Björn Schießle <bjoern@schiessle.org> - * @author Brice Maron <brice@bmaron.net> - * @author Christopher Schäpers <kondou@ts.unde.re> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Clark Tomlinson <fallen013@gmail.com> - * @author cmeh <cmeh@users.noreply.github.com> - * @author Eric Masseran <rico.masseran@gmail.com> - * @author Felix Epp <work@felixepp.de> - * @author Florin Peter <github@florin-peter.de> - * @author Frank Karlitschek <frank@karlitschek.de> - * @author Georg Ehrke <oc.list@georgehrke.com> - * @author helix84 <helix84@centrum.sk> - * @author Ilja Neumann <ineumann@owncloud.com> - * @author Individual IT Services <info@individual-it.net> - * @author Jakob Sack <mail@jakobsack.de> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ <skjnldsv@protonmail.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Julius Härtl <jus@bitgrid.net> - * @author Kawohl <john@owncloud.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Markus Goetz <markus@woboq.com> - * @author Martin Mattel <martin.mattel@diemattels.at> - * @author Marvin Thomas Rabe <mrabe@marvinrabe.de> - * @author Michael Gapczynski <GapczynskiM@gmail.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author rakekniven <mark.ziegler@rakekniven.de> - * @author Robert Dailey <rcdailey@gmail.com> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Sebastian Wessalowski <sebastian@wessalowski.org> - * @author Stefan Rado <owncloud@sradonia.net> - * @author Stefan Weil <sw@weilnetz.de> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Thomas Tanghus <thomas@tanghus.net> - * @author Valdnet <47037905+Valdnet@users.noreply.github.com> - * @author Victor Dubiniuk <dubiniuk@owncloud.com> - * @author Vincent Petry <vincent@nextcloud.com> - * @author Volkan Gezer <volkangezer@gmail.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - use bantu\IniGetWrapper\IniGetWrapper; -use OC\AppFramework\Http\Request; +use OC\Authentication\TwoFactorAuth\Manager as TwoFactorAuthManager; use OC\Files\SetupManager; use OCP\Files\Template\ITemplateManager; use OCP\IConfig; -use OCP\IDBConnection; use OCP\IGroupManager; use OCP\IURLGenerator; use OCP\IUser; +use OCP\L10N\IFactory; +use OCP\Security\ISecureRandom; use OCP\Share\IManager; use Psr\Log\LoggerInterface; +/** + * @deprecated 32.0.0 Use \OCP\Util or any appropriate official API instead + */ class OC_Util { - public static $scripts = []; public static $styles = []; public static $headers = []; - /** @var array Local cache of version.php */ - private static $versionCache = null; - - protected static function getAppManager() { - return \OC::$server->getAppManager(); - } - /** * Setup the file system * @@ -120,8 +57,8 @@ class OC_Util { * Check if a password is required for each public link * * @param bool $checkGroupMembership Check group membership exclusion - * @return boolean - * @suppress PhanDeprecatedFunction + * @return bool + * @deprecated 32.0.0 use OCP\Share\IManager's shareApiLinkEnforcePassword directly */ public static function isPublicLinkPasswordRequired(bool $checkGroupMembership = true) { /** @var IManager $shareManager */ @@ -135,6 +72,7 @@ class OC_Util { * @param IGroupManager $groupManager * @param IUser|null $user * @return bool + * @deprecated 32.0.0 use OCP\Share\IManager's sharingDisabledForUser directly */ public static function isSharingDisabledForUser(IConfig $config, IGroupManager $groupManager, $user) { /** @var IManager $shareManager */ @@ -146,8 +84,8 @@ class OC_Util { /** * check if share API enforces a default expire date * - * @return boolean - * @suppress PhanDeprecatedFunction + * @return bool + * @deprecated 32.0.0 use OCP\Share\IManager's shareApiLinkDefaultExpireDateEnforced directly */ public static function isDefaultExpireDateEnforced() { /** @var IManager $shareManager */ @@ -159,7 +97,8 @@ class OC_Util { * Get the quota of a user * * @param IUser|null $user - * @return int|\OCP\Files\FileInfo::SPACE_UNLIMITED|false Quota bytes + * @return int|\OCP\Files\FileInfo::SPACE_UNLIMITED|false|float Quota bytes + * @deprecated 9.0.0 - Use \OCP\IUser::getQuota or \OCP\IUser::getQuotaBytes */ public static function getUserQuota(?IUser $user) { if (is_null($user)) { @@ -169,7 +108,7 @@ class OC_Util { if ($userQuota === 'none') { return \OCP\Files\FileInfo::SPACE_UNLIMITED; } - return OC_Helper::computerFileSize($userQuota); + return \OCP\Util::computerFileSize($userQuota); } /** @@ -185,8 +124,8 @@ class OC_Util { /** @var LoggerInterface $logger */ $logger = \OC::$server->get(LoggerInterface::class); - $plainSkeletonDirectory = \OC::$server->getConfig()->getSystemValue('skeletondirectory', \OC::$SERVERROOT . '/core/skeleton'); - $userLang = \OC::$server->getL10NFactory()->findLanguage(); + $plainSkeletonDirectory = \OC::$server->getConfig()->getSystemValueString('skeletondirectory', \OC::$SERVERROOT . '/core/skeleton'); + $userLang = \OC::$server->get(IFactory::class)->findLanguage(); $skeletonDirectory = str_replace('{lang}', $userLang, $plainSkeletonDirectory); if (!file_exists($skeletonDirectory)) { @@ -213,7 +152,7 @@ class OC_Util { } if (!empty($skeletonDirectory)) { - $logger->debug('copying skeleton for '.$userId.' from '.$skeletonDirectory.' to '.$userDirectory->getFullPath('/'), ['app' => 'files_skeleton']); + $logger->debug('copying skeleton for ' . $userId . ' from ' . $skeletonDirectory . ' to ' . $userDirectory->getFullPath('/'), ['app' => 'files_skeleton']); self::copyr($skeletonDirectory, $userDirectory); // update the file cache $userDirectory->getStorage()->getScanner()->scan('', \OC\Files\Cache\Scanner::SCAN_RECURSIVE); @@ -232,7 +171,7 @@ class OC_Util { * @return void */ public static function copyr($source, \OCP\Files\Folder $target) { - $logger = \OC::$server->getLogger(); + $logger = \OCP\Server::get(LoggerInterface::class); // Verify if folder exists $dir = opendir($source); @@ -248,14 +187,13 @@ class OC_Util { $child = $target->newFolder($file); self::copyr($source . '/' . $file, $child); } else { - $child = $target->newFile($file); $sourceStream = fopen($source . '/' . $file, 'r'); if ($sourceStream === false) { $logger->error(sprintf('Could not fopen "%s"', $source . '/' . $file), ['app' => 'core']); closedir($dir); return; } - $child->putContent($sourceStream); + $target->newFile($file, $sourceStream); } } } @@ -263,97 +201,24 @@ class OC_Util { } /** - * @return void - * @suppress PhanUndeclaredMethod + * @deprecated 32.0.0 Call tearDown directly on SetupManager */ - public static function tearDownFS() { - /** @var SetupManager $setupManager */ - $setupManager = \OC::$server->get(SetupManager::class); + public static function tearDownFS(): void { + $setupManager = \OCP\Server::get(SetupManager::class); $setupManager->tearDown(); } /** - * get the current installed version of ownCloud - * - * @return array - */ - public static function getVersion() { - OC_Util::loadVersion(); - return self::$versionCache['OC_Version']; - } - - /** - * get the current installed version string of ownCloud - * - * @return string - */ - public static function getVersionString() { - OC_Util::loadVersion(); - return self::$versionCache['OC_VersionString']; - } - - /** - * @deprecated the value is of no use anymore - * @return string - */ - public static function getEditionString() { - return ''; - } - - /** - * @description get the update channel of the current installed of ownCloud. - * @return string - */ - public static function getChannel() { - OC_Util::loadVersion(); - return \OC::$server->getConfig()->getSystemValue('updater.release.channel', self::$versionCache['OC_Channel']); - } - - /** - * @description get the build number of the current installed of ownCloud. - * @return string - */ - public static function getBuild() { - OC_Util::loadVersion(); - return self::$versionCache['OC_Build']; - } - - /** - * @description load the version.php into the session as cache - * @suppress PhanUndeclaredVariable - */ - private static function loadVersion() { - if (self::$versionCache !== null) { - return; - } - - $timestamp = filemtime(OC::$SERVERROOT . '/version.php'); - require OC::$SERVERROOT . '/version.php'; - /** @var int $timestamp */ - self::$versionCache['OC_Version_Timestamp'] = $timestamp; - /** @var string $OC_Version */ - self::$versionCache['OC_Version'] = $OC_Version; - /** @var string $OC_VersionString */ - self::$versionCache['OC_VersionString'] = $OC_VersionString; - /** @var string $OC_Build */ - self::$versionCache['OC_Build'] = $OC_Build; - - /** @var string $OC_Channel */ - self::$versionCache['OC_Channel'] = $OC_Channel; - } - - /** * generates a path for JS/CSS files. If no application is provided it will create the path for core. * * @param string $application application to get the files from * @param string $directory directory within this application (css, js, vendor, etc) - * @param string $file the file inside of the above folder - * @return string the path + * @param ?string $file the file inside of the above folder */ - private static function generatePath($application, $directory, $file) { + private static function generatePath($application, $directory, $file): string { if (is_null($file)) { $file = $application; - $application = ""; + $application = ''; } if (!empty($application)) { return "$application/$directory/$file"; @@ -363,70 +228,16 @@ class OC_Util { } /** - * add a javascript file - * - * @deprecated 24.0.0 - Use \OCP\Util::addScript - * - * @param string $application application id - * @param string|null $file filename - * @param bool $prepend prepend the Script to the beginning of the list - * @return void - */ - public static function addScript($application, $file = null, $prepend = false) { - $path = OC_Util::generatePath($application, 'js', $file); - - // core js files need separate handling - if ($application !== 'core' && $file !== null) { - self::addTranslations($application); - } - self::addExternalResource($application, $prepend, $path, "script"); - } - - /** - * add a javascript file from the vendor sub folder - * - * @param string $application application id - * @param string|null $file filename - * @param bool $prepend prepend the Script to the beginning of the list - * @return void - */ - public static function addVendorScript($application, $file = null, $prepend = false) { - $path = OC_Util::generatePath($application, 'vendor', $file); - self::addExternalResource($application, $prepend, $path, "script"); - } - - /** - * add a translation JS file - * - * @deprecated 24.0.0 - * - * @param string $application application id - * @param string|null $languageCode language code, defaults to the current language - * @param bool|null $prepend prepend the Script to the beginning of the list - */ - public static function addTranslations($application, $languageCode = null, $prepend = false) { - if (is_null($languageCode)) { - $languageCode = \OC::$server->getL10NFactory()->findLanguage($application); - } - if (!empty($application)) { - $path = "$application/l10n/$languageCode"; - } else { - $path = "l10n/$languageCode"; - } - self::addExternalResource($application, $prepend, $path, "script"); - } - - /** * add a css file * * @param string $application application id * @param string|null $file filename * @param bool $prepend prepend the Style to the beginning of the list - * @return void + * @deprecated 32.0.0 Use \OCP\Util::addStyle */ - public static function addStyle($application, $file = null, $prepend = false) { + public static function addStyle($application, $file = null, $prepend = false): void { $path = OC_Util::generatePath($application, 'css', $file); - self::addExternalResource($application, $prepend, $path, "style"); + self::addExternalResource($application, $prepend, $path, 'style'); } /** @@ -435,11 +246,11 @@ class OC_Util { * @param string $application application id * @param string|null $file filename * @param bool $prepend prepend the Style to the beginning of the list - * @return void + * @deprecated 32.0.0 */ - public static function addVendorStyle($application, $file = null, $prepend = false) { + public static function addVendorStyle($application, $file = null, $prepend = false): void { $path = OC_Util::generatePath($application, 'vendor', $file); - self::addExternalResource($application, $prepend, $path, "style"); + self::addExternalResource($application, $prepend, $path, 'style'); } /** @@ -449,10 +260,9 @@ class OC_Util { * @param bool $prepend prepend the file to the beginning of the list * @param string $path * @param string $type (script or style) - * @return void */ - private static function addExternalResource($application, $prepend, $path, $type = "script") { - if ($type === "style") { + private static function addExternalResource($application, $prepend, $path, $type = 'script'): void { + if ($type === 'style') { if (!in_array($path, self::$styles)) { if ($prepend === true) { array_unshift(self::$styles, $path); @@ -460,14 +270,6 @@ class OC_Util { self::$styles[] = $path; } } - } elseif ($type === "script") { - if (!in_array($path, self::$scripts)) { - if ($prepend === true) { - array_unshift(self::$scripts, $path); - } else { - self::$scripts [] = $path; - } - } } } @@ -479,8 +281,9 @@ class OC_Util { * @param array $attributes array of attributes for the element * @param string $text the text content for the element * @param bool $prepend prepend the header to the beginning of the list + * @deprecated 32.0.0 Use \OCP\Util::addHeader instead */ - public static function addHeader($tag, $attributes, $text = null, $prepend = false) { + public static function addHeader($tag, $attributes, $text = null, $prepend = false): void { $header = [ 'tag' => $tag, 'attributes' => $attributes, @@ -496,7 +299,6 @@ class OC_Util { /** * check if the current server configuration is suitable for ownCloud * - * @param \OC\SystemConfig $config * @return array arrays with error messages and hints */ public static function checkServer(\OC\SystemConfig $config) { @@ -515,15 +317,7 @@ class OC_Util { } $webServerRestart = false; - $setup = new \OC\Setup( - $config, - \OC::$server->get(IniGetWrapper::class), - \OC::$server->getL10N('lib'), - \OC::$server->get(\OCP\Defaults::class), - \OC::$server->get(LoggerInterface::class), - \OC::$server->getSecureRandom(), - \OC::$server->get(\OC\Installer::class) - ); + $setup = \OCP\Server::get(\OC\Setup::class); $urlGenerator = \OC::$server->getURLGenerator(); @@ -537,7 +331,7 @@ class OC_Util { } // Check if config folder is writable. - if (!OC_Helper::isReadOnlyConfigEnabled()) { + if (!(bool)$config->getValue('config_is_read_only', false)) { if (!is_writable(OC::$configDir) or !is_readable(OC::$configDir)) { $errors[] = [ 'error' => $l->t('Cannot write into "config" directory.'), @@ -549,19 +343,6 @@ class OC_Util { } } - // Check if there is a writable install folder. - if ($config->getValue('appstoreenabled', true)) { - if (OC_App::getInstallPath() === null - || !is_writable(OC_App::getInstallPath()) - || !is_readable(OC_App::getInstallPath()) - ) { - $errors[] = [ - 'error' => $l->t('Cannot write into "apps" directory.'), - 'hint' => $l->t('This can usually be fixed by giving the web server write access to the apps directory' - . ' or disabling the App Store in the config file.') - ]; - } - } // Create root dir. if ($config->getValue('installed', false)) { if (!is_dir($CONFIG_DATADIRECTORY)) { @@ -610,8 +391,7 @@ class OC_Util { // defined = defined // ini = ini_get // If the dependency is not found the missing module name is shown to the EndUser - // When adding new checks always verify that they pass on Travis as well - // for ini settings, see https://github.com/owncloud/administration/blob/master/travis-ci/custom.ini + // When adding new checks always verify that they pass on CI as well $dependencies = [ 'classes' => [ 'ZipArchive' => 'zip', @@ -686,28 +466,14 @@ class OC_Util { * TODO: Should probably be implemented in the above generic dependency * check somehow in the long-term. */ - if ($iniWrapper->getBool('mbstring.func_overload') !== null && - $iniWrapper->getBool('mbstring.func_overload') === true) { + if ($iniWrapper->getBool('mbstring.func_overload') !== null + && $iniWrapper->getBool('mbstring.func_overload') === true) { $errors[] = [ 'error' => $l->t('<code>mbstring.func_overload</code> is set to <code>%s</code> instead of the expected value <code>0</code>.', [$iniWrapper->getString('mbstring.func_overload')]), 'hint' => $l->t('To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini.') ]; } - if (function_exists('xml_parser_create') && - LIBXML_LOADED_VERSION < 20700) { - $version = LIBXML_LOADED_VERSION; - $major = floor($version / 10000); - $version -= ($major * 10000); - $minor = floor($version / 100); - $version -= ($minor * 100); - $patch = $version; - $errors[] = [ - 'error' => $l->t('libxml2 2.7.0 is at least required. Currently %s is installed.', [$major . '.' . $minor . '.' . $patch]), - 'hint' => $l->t('To fix this issue update your libxml2 version and restart your web server.') - ]; - } - if (!self::isAnnotationsWorking()) { $errors[] = [ 'error' => $l->t('PHP is apparently set up to strip inline doc blocks. This will make several core apps inaccessible.'), @@ -731,8 +497,6 @@ class OC_Util { } } - $errors = array_merge($errors, self::checkDatabaseVersion()); - // Cache the result of this function \OC::$server->getSession()->set('checkServer_succeeded', count($errors) == 0); @@ -740,50 +504,14 @@ class OC_Util { } /** - * Check the database version - * - * @return array errors array - */ - public static function checkDatabaseVersion() { - $l = \OC::$server->getL10N('lib'); - $errors = []; - $dbType = \OC::$server->getSystemConfig()->getValue('dbtype', 'sqlite'); - if ($dbType === 'pgsql') { - // check PostgreSQL version - // TODO latest postgresql 8 released was 8 years ago, maybe remove the - // check completely? - try { - /** @var IDBConnection $connection */ - $connection = \OC::$server->get(IDBConnection::class); - $result = $connection->executeQuery('SHOW SERVER_VERSION'); - $data = $result->fetch(); - $result->closeCursor(); - if (isset($data['server_version'])) { - $version = $data['server_version']; - if (version_compare($version, '9.0.0', '<')) { - $errors[] = [ - 'error' => $l->t('PostgreSQL >= 9 required.'), - 'hint' => $l->t('Please upgrade your database version.') - ]; - } - } - } catch (\Doctrine\DBAL\Exception $e) { - $logger = \OC::$server->getLogger(); - $logger->warning('Error occurred while checking PostgreSQL version, assuming >= 9'); - $logger->logException($e); - } - } - return $errors; - } - - /** * Check for correct file permissions of data directory * * @param string $dataDirectory * @return array arrays with error messages and hints + * @internal */ public static function checkDataDirectoryPermissions($dataDirectory) { - if (\OC::$server->getConfig()->getSystemValue('check_data_directory_permissions', true) === false) { + if (!\OC::$server->getConfig()->getSystemValueBool('check_data_directory_permissions', true)) { return []; } @@ -795,8 +523,8 @@ class OC_Util { if ($perms[2] !== '0') { $l = \OC::$server->getL10N('lib'); return [[ - 'error' => $l->t('Your data directory is readable by other users.'), - 'hint' => $l->t('Please change the permissions to 0770 so that the directory cannot be listed by other users.'), + 'error' => $l->t('Your data directory is readable by other people.'), + 'hint' => $l->t('Please change the permissions to 0770 so that the directory cannot be listed by other people.'), ]]; } } @@ -805,10 +533,11 @@ class OC_Util { /** * Check that the data directory exists and is valid by - * checking the existence of the ".ocdata" file. + * checking the existence of the ".ncdata" file. * * @param string $dataDirectory data directory path * @return array errors found + * @internal */ public static function checkDataDirectoryValidity($dataDirectory) { $l = \OC::$server->getL10N('lib'); @@ -819,11 +548,11 @@ class OC_Util { 'hint' => $l->t('Check the value of "datadirectory" in your configuration.') ]; } - if (!file_exists($dataDirectory . '/.ocdata')) { + + if (!file_exists($dataDirectory . '/.ncdata')) { $errors[] = [ 'error' => $l->t('Your data directory is invalid.'), - 'hint' => $l->t('Ensure there is a file called ".ocdata"' . - ' in the root of the data directory.') + 'hint' => $l->t('Ensure there is a file called "%1$s" in the root of the data directory. It should have the content: "%2$s"', ['.ncdata', '# Nextcloud data directory']), ]; } return $errors; @@ -833,9 +562,9 @@ class OC_Util { * Check if the user is logged in, redirects to home if not. With * redirect URL parameter to the request URI. * - * @return void + * @deprecated 32.0.0 */ - public static function checkLoggedIn() { + public static function checkLoggedIn(): void { // Check if we are a user if (!\OC::$server->getUserSession()->isLoggedIn()) { header('Location: ' . \OC::$server->getURLGenerator()->linkToRoute( @@ -848,7 +577,7 @@ class OC_Util { exit(); } // Redirect to 2FA challenge selection if 2FA challenge was not solved yet - if (\OC::$server->getTwoFactorAuthManager()->needsSecondFactor(\OC::$server->getUserSession()->getUser())) { + if (\OC::$server->get(TwoFactorAuthManager::class)->needsSecondFactor(\OC::$server->getUserSession()->getUser())) { header('Location: ' . \OC::$server->getURLGenerator()->linkToRoute('core.TwoFactorChallenge.selectChallenge')); exit(); } @@ -857,10 +586,10 @@ class OC_Util { /** * Check if the user is a admin, redirects to home if not * - * @return void + * @deprecated 32.0.0 */ - public static function checkAdminUser() { - OC_Util::checkLoggedIn(); + public static function checkAdminUser(): void { + self::checkLoggedIn(); if (!OC_User::isAdminUser(OC_User::getUser())) { header('Location: ' . \OCP\Util::linkToAbsolute('', 'index.php')); exit(); @@ -873,7 +602,7 @@ class OC_Util { * the apps visible for the current user * * @return string URL - * @suppress PhanDeprecatedFunction + * @deprecated 32.0.0 use IURLGenerator's linkToDefaultPageUrl directly */ public static function getDefaultPageUrl() { /** @var IURLGenerator $urlGenerator */ @@ -884,9 +613,9 @@ class OC_Util { /** * Redirect to the user default page * - * @return void + * @deprecated 32.0.0 */ - public static function redirectToDefaultPage() { + public static function redirectToDefaultPage(): void { $location = self::getDefaultPageUrl(); header('Location: ' . $location); exit(); @@ -897,11 +626,11 @@ class OC_Util { * * @return string */ - public static function getInstanceId() { + public static function getInstanceId(): string { $id = \OC::$server->getSystemConfig()->getValue('instanceid', null); if (is_null($id)) { // We need to guarantee at least one letter in instanceid so it can be used as the session_name - $id = 'oc' . \OC::$server->getSecureRandom()->generate(10, \OCP\Security\ISecureRandom::CHAR_LOWER.\OCP\Security\ISecureRandom::CHAR_DIGITS); + $id = 'oc' . \OC::$server->get(ISecureRandom::class)->generate(10, \OCP\Security\ISecureRandom::CHAR_LOWER . \OCP\Security\ISecureRandom::CHAR_DIGITS); \OC::$server->getSystemConfig()->setValue('instanceid', $id); } return $id; @@ -914,11 +643,11 @@ class OC_Util { * string or array of strings before displaying it on a web page. * * @param string|string[] $value - * @return string|string[] an array of sanitized strings or a single sanitized string, depends on the input parameter. + * @return ($value is array ? string[] : string) + * @deprecated 32.0.0 use \OCP\Util::sanitizeHTML instead */ public static function sanitizeHTML($value) { if (is_array($value)) { - /** @var string[] $value */ $value = array_map(function ($value) { return self::sanitizeHTML($value); }, $value); @@ -938,6 +667,7 @@ class OC_Util { * * @param string $component part of URI to encode * @return string + * @deprecated 32.0.0 use \OCP\Util::encodePath instead */ public static function encodePath($component) { $encoded = rawurlencode($component); @@ -945,86 +675,6 @@ class OC_Util { return $encoded; } - - public function createHtaccessTestFile(\OCP\IConfig $config) { - // php dev server does not support htaccess - if (php_sapi_name() === 'cli-server') { - return false; - } - - // testdata - $fileName = '/htaccesstest.txt'; - $testContent = 'This is used for testing whether htaccess is properly enabled to disallow access from the outside. This file can be safely removed.'; - - // creating a test file - $testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $fileName; - - if (file_exists($testFile)) {// already running this test, possible recursive call - return false; - } - - $fp = @fopen($testFile, 'w'); - if (!$fp) { - throw new \OCP\HintException('Can\'t create test file to check for working .htaccess file.', - 'Make sure it is possible for the web server to write to ' . $testFile); - } - fwrite($fp, $testContent); - fclose($fp); - - return $testContent; - } - - /** - * Check if the .htaccess file is working - * - * @param \OCP\IConfig $config - * @return bool - * @throws Exception - * @throws \OCP\HintException If the test file can't get written. - */ - public function isHtaccessWorking(\OCP\IConfig $config) { - if (\OC::$CLI || !$config->getSystemValue('check_for_working_htaccess', true)) { - return true; - } - - $testContent = $this->createHtaccessTestFile($config); - if ($testContent === false) { - return false; - } - - $fileName = '/htaccesstest.txt'; - $testFile = $config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data') . '/' . $fileName; - - // accessing the file via http - $url = \OC::$server->getURLGenerator()->getAbsoluteURL(OC::$WEBROOT . '/data' . $fileName); - try { - $content = \OC::$server->getHTTPClientService()->newClient()->get($url)->getBody(); - } catch (\Exception $e) { - $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); - - /* - * If the content is not equal to test content our .htaccess - * is working as required - */ - return $content !== $testContent && $fallbackContent !== $testContent; - } - /** * Check if current locale is non-UTF8 * @@ -1032,11 +682,11 @@ class OC_Util { */ private static function isNonUTF8Locale() { if (function_exists('escapeshellcmd')) { - return '' === escapeshellcmd('§'); + return escapeshellcmd('§') === ''; } elseif (function_exists('escapeshellarg')) { - return '\'\'' === escapeshellarg('§'); + return escapeshellarg('§') === '\'\''; } else { - return 0 === preg_match('/utf-?8/i', setlocale(LC_CTYPE, 0)); + return preg_match('/utf-?8/i', setlocale(LC_CTYPE, 0)) === 0; } } @@ -1044,9 +694,9 @@ class OC_Util { * Check if the setlocale call does not work. This can happen if the right * local packages are not available on the server. * - * @return bool + * @internal */ - public static function isSetLocaleWorking() { + public static function isSetLocaleWorking(): bool { if (self::isNonUTF8Locale()) { // Borrowed from \Patchwork\Utf8\Bootup::initLocale setlocale(LC_ALL, 'C.UTF-8', 'C'); @@ -1064,10 +714,15 @@ class OC_Util { /** * Check if it's possible to get the inline annotations * - * @return bool + * @internal */ - public static function isAnnotationsWorking() { - $reflection = new \ReflectionMethod(__METHOD__); + public static function isAnnotationsWorking(): bool { + if (PHP_VERSION_ID >= 80300) { + /** @psalm-suppress UndefinedMethod */ + $reflection = \ReflectionMethod::createFromMethodName(__METHOD__); + } else { + $reflection = new \ReflectionMethod(__METHOD__); + } $docs = $reflection->getDocComment(); return (is_string($docs) && strlen($docs) > 50); @@ -1076,9 +731,9 @@ class OC_Util { /** * Check if the PHP module fileinfo is loaded. * - * @return bool + * @internal */ - public static function fileInfoLoaded() { + public static function fileInfoLoaded(): bool { return function_exists('finfo_open'); } @@ -1109,7 +764,7 @@ class OC_Util { * @return string the theme */ public static function getTheme() { - $theme = \OC::$server->getSystemConfig()->getValue("theme", ''); + $theme = \OC::$server->getSystemConfig()->getValue('theme', ''); if ($theme === '') { if (is_dir(OC::$SERVERROOT . '/themes/default')) { @@ -1124,16 +779,16 @@ class OC_Util { * Normalize a unicode string * * @param string $value a not normalized string - * @return bool|string + * @return string The normalized string or the input if the normalization failed */ - public static function normalizeUnicode($value) { + public static function normalizeUnicode(string $value): string { if (Normalizer::isNormalized($value)) { return $value; } $normalizedValue = Normalizer::normalize($value); - if ($normalizedValue === null || $normalizedValue === false) { - \OC::$server->getLogger()->warning('normalizing failed for "' . $value . '"', ['app' => 'core']); + if ($normalizedValue === false) { + \OCP\Server::get(LoggerInterface::class)->warning('normalizing failed for "' . $value . '"', ['app' => 'core']); return $value; } @@ -1141,49 +796,6 @@ class OC_Util { } /** - * A human readable string is generated based on version and build number - * - * @return string - */ - public static function getHumanVersion() { - $version = OC_Util::getVersionString(); - $build = OC_Util::getBuild(); - if (!empty($build) and OC_Util::getChannel() === 'daily') { - $version .= ' Build:' . $build; - } - return $version; - } - - /** - * Returns whether the given file name is valid - * - * @param string $file file name to check - * @return bool true if the file name is valid, false otherwise - * @deprecated use \OC\Files\View::verifyPath() - */ - public static function isValidFileName($file) { - $trimmed = trim($file); - if ($trimmed === '') { - return false; - } - if (\OC\Files\Filesystem::isIgnoredDir($trimmed)) { - return false; - } - - // detect part files - if (preg_match('/' . \OCP\Files\FileInfo::BLACKLIST_FILES_REGEX . '/', $trimmed) !== 0) { - return false; - } - - foreach (str_split($trimmed) as $char) { - if (strpos(\OCP\Constants::FILENAME_INVALID_CHARS, $char) !== false) { - return false; - } - } - return true; - } - - /** * Check whether the instance needs to perform an upgrade, * either when the core version is higher or any app requires * an upgrade. @@ -1191,6 +803,7 @@ class OC_Util { * @param \OC\SystemConfig $config * @return bool whether the core or any app needs an upgrade * @throws \OCP\HintException When the upgrade from the given version is not allowed + * @deprecated 32.0.0 Use \OCP\Util::needUpgrade instead */ public static function needUpgrade(\OC\SystemConfig $config) { if ($config->getValue('installed', false)) { diff --git a/lib/private/legacy/template/functions.php b/lib/private/legacy/template/functions.php deleted file mode 100644 index 56c488d5abe..00000000000 --- a/lib/private/legacy/template/functions.php +++ /dev/null @@ -1,339 +0,0 @@ -<?php - -use OCP\Util; - -/** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Bart Visscher <bartv@thisnet.nl> - * @author Bernhard Posselt <dev@bernhard-posselt.com> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Jörn Friedrich Dreyer <jfd@butonic.de> - * @author Julius Härtl <jus@bitgrid.net> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Michael Letzgus <www@chronos.michael-letzgus.de> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin McCorkell <robin@mccorkell.me.uk> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ -function p($string) { - print(\OCP\Util::sanitizeHTML($string)); -} - - -/** - * Prints a <link> tag for loading css - * @param string $href the source URL, ignored when empty - * @param string $opts, additional optional options - */ -function emit_css_tag($href, $opts = '') { - $s = '<link rel="stylesheet"'; - if (!empty($href)) { - $s .= ' href="' . $href .'"'; - } - if (!empty($opts)) { - $s .= ' '.$opts; - } - print_unescaped($s.">\n"); -} - -/** - * Prints all tags for CSS loading - * @param array $obj all the script information from template - */ -function emit_css_loading_tags($obj) { - foreach ($obj['cssfiles'] as $css) { - emit_css_tag($css); - } - foreach ($obj['printcssfiles'] as $css) { - emit_css_tag($css, 'media="print"'); - } -} - -/** - * Prints a <script> tag with nonce and defer depending on config - * @param string $src the source URL, ignored when empty - * @param string $script_content the inline script content, ignored when empty - */ -function emit_script_tag($src, $script_content = '') { - $defer_str = ' defer'; - $s = '<script nonce="' . \OC::$server->getContentSecurityPolicyNonceManager()->getNonce() . '"'; - if (!empty($src)) { - // emit script tag for deferred loading from $src - $s .= $defer_str.' src="' . $src .'">'; - } elseif (!empty($script_content)) { - // emit script tag for inline script from $script_content without defer (see MDN) - $s .= ">\n".$script_content."\n"; - } else { - // no $src nor $src_content, really useless empty tag - $s .= '>'; - } - $s .= '</script>'; - print_unescaped($s."\n"); -} - -/** - * Print all <script> tags for loading JS - * @param array $obj all the script information from template - */ -function emit_script_loading_tags($obj) { - foreach ($obj['jsfiles'] as $jsfile) { - emit_script_tag($jsfile, ''); - } - if (!empty($obj['inline_ocjs'])) { - emit_script_tag('', $obj['inline_ocjs']); - } -} - -/** - * Prints an unsanitized string - usage of this function may result into XSS. - * Consider using p() instead. - * @param string|array $string the string which will be printed as it is - */ -function print_unescaped($string) { - print($string); -} - -/** - * Shortcut for adding scripts to a page - * All scripts are forced to be loaded after core since - * they are coming from a template registration. - * Please consider moving them into the relevant controller - * - * @deprecated 24.0.0 - Use \OCP\Util::addScript - * - * @param string $app the appname - * @param string|string[] $file the filename, - * if an array is given it will add all scripts - */ -function script($app, $file = null) { - if (is_array($file)) { - foreach ($file as $script) { - Util::addScript($app, $script, 'core'); - } - } else { - Util::addScript($app, $file, 'core'); - } -} - -/** - * Shortcut for adding vendor scripts to a page - * @param string $app the appname - * @param string|string[] $file the filename, - * if an array is given it will add all scripts - */ -function vendor_script($app, $file = null) { - if (is_array($file)) { - foreach ($file as $f) { - OC_Util::addVendorScript($app, $f); - } - } else { - OC_Util::addVendorScript($app, $file); - } -} - -/** - * Shortcut for adding styles to a page - * @param string $app the appname - * @param string|string[] $file the filename, - * if an array is given it will add all styles - */ -function style($app, $file = null) { - if (is_array($file)) { - foreach ($file as $f) { - OC_Util::addStyle($app, $f); - } - } else { - OC_Util::addStyle($app, $file); - } -} - -/** - * Shortcut for adding vendor styles to a page - * @param string $app the appname - * @param string|string[] $file the filename, - * if an array is given it will add all styles - */ -function vendor_style($app, $file = null) { - if (is_array($file)) { - foreach ($file as $f) { - OC_Util::addVendorStyle($app, $f); - } - } else { - OC_Util::addVendorStyle($app, $file); - } -} - -/** - * Shortcut for adding translations to a page - * @param string $app the appname - * if an array is given it will add all styles - */ -function translation($app) { - OC_Util::addTranslations($app); -} - -/** - * Shortcut for HTML imports - * @param string $app the appname - * @param string|string[] $file the path relative to the app's component folder, - * if an array is given it will add all components - */ -function component($app, $file) { - if (is_array($file)) { - foreach ($file as $f) { - $url = link_to($app, 'component/' . $f . '.html'); - OC_Util::addHeader('link', ['rel' => 'import', 'href' => $url]); - } - } else { - $url = link_to($app, 'component/' . $file . '.html'); - OC_Util::addHeader('link', ['rel' => 'import', 'href' => $url]); - } -} - -/** - * make \OCP\IURLGenerator::linkTo available as a simple function - * @param string $app app - * @param string $file file - * @param array $args array with param=>value, will be appended to the returned url - * @return string link to the file - * - * For further information have a look at \OCP\IURLGenerator::linkTo - */ -function link_to($app, $file, $args = []) { - return \OC::$server->getURLGenerator()->linkTo($app, $file, $args); -} - -/** - * @param $key - * @return string url to the online documentation - */ -function link_to_docs($key) { - return \OC::$server->getURLGenerator()->linkToDocs($key); -} - -/** - * make \OCP\IURLGenerator::imagePath available as a simple function - * @param string $app app - * @param string $image image - * @return string link to the image - * - * For further information have a look at \OCP\IURLGenerator::imagePath - */ -function image_path($app, $image) { - return \OC::$server->getURLGenerator()->imagePath($app, $image); -} - -/** - * make OC_Helper::mimetypeIcon available as a simple function - * @param string $mimetype mimetype - * @return string link to the image - */ -function mimetype_icon($mimetype) { - return \OC::$server->getMimeTypeDetector()->mimeTypeIcon($mimetype); -} - -/** - * make preview_icon available as a simple function - * Returns the path to the preview of the image. - * @param string $path path of file - * @return string link to the preview - */ -function preview_icon($path) { - return \OC::$server->getURLGenerator()->linkToRoute('core.Preview.getPreview', ['x' => 32, 'y' => 32, 'file' => $path]); -} - -/** - * @param string $path - * @param string $token - * @return string - */ -function publicPreview_icon($path, $token) { - return \OC::$server->getURLGenerator()->linkToRoute('files_sharing.PublicPreview.getPreview', ['x' => 32, 'y' => 32, 'file' => $path, 'token' => $token]); -} - -/** - * make OC_Helper::humanFileSize available as a simple function - * @param int $bytes size in bytes - * @return string size as string - * - * For further information have a look at OC_Helper::humanFileSize - */ -function human_file_size($bytes) { - return OC_Helper::humanFileSize($bytes); -} - -/** - * Strips the timestamp of its time value - * @param int $timestamp UNIX timestamp to strip - * @return int timestamp without time value - */ -function strip_time($timestamp) { - $date = new \DateTime("@{$timestamp}"); - $date->setTime(0, 0, 0); - return (int)$date->format('U'); -} - -/** - * Formats timestamp relatively to the current time using - * a human-friendly format like "x minutes ago" or "yesterday" - * @param int $timestamp timestamp to format - * @param int|null $fromTime timestamp to compare from, defaults to current time - * @param bool|null $dateOnly whether to strip time information - * @return string timestamp - */ -function relative_modified_date($timestamp, $fromTime = null, $dateOnly = false) { - /** @var \OC\DateTimeFormatter $formatter */ - $formatter = \OC::$server->query('DateTimeFormatter'); - - if ($dateOnly) { - return $formatter->formatDateSpan($timestamp, $fromTime); - } - return $formatter->formatTimeSpan($timestamp, $fromTime); -} - -function html_select_options($options, $selected, $params = []) { - if (!is_array($selected)) { - $selected = [$selected]; - } - if (isset($params['combine']) && $params['combine']) { - $options = array_combine($options, $options); - } - $value_name = $label_name = false; - if (isset($params['value'])) { - $value_name = $params['value']; - } - if (isset($params['label'])) { - $label_name = $params['label']; - } - $html = ''; - foreach ($options as $value => $label) { - if ($value_name && is_array($label)) { - $value = $label[$value_name]; - } - if ($label_name && is_array($label)) { - $label = $label[$label_name]; - } - $select = in_array($value, $selected) ? ' selected="selected"' : ''; - $html .= '<option value="' . \OCP\Util::sanitizeHTML($value) . '"' . $select . '>' . \OCP\Util::sanitizeHTML($label) . '</option>'."\n"; - } - return $html; -} |