diff options
Diffstat (limited to 'lib')
93 files changed, 692 insertions, 843 deletions
diff --git a/lib/autoloader.php b/lib/autoloader.php index 41b272a457c..7084eb93c89 100644 --- a/lib/autoloader.php +++ b/lib/autoloader.php @@ -22,10 +22,8 @@ class Autoloader { /** * Optional low-latency memory cache for class to path mapping. - * - * @var \OC\Memcache\Cache */ - protected $memoryCache; + protected ?ICache $memoryCache = null; /** * Autoloader constructor. @@ -127,15 +125,15 @@ class Autoloader { * @throws AutoloadNotAllowedException */ public function load(string $class): bool { + if (class_exists($class, false)) { + return false; + } + $pathsToRequire = null; if ($this->memoryCache) { $pathsToRequire = $this->memoryCache->get($class); } - if (class_exists($class, false)) { - return false; - } - if (!is_array($pathsToRequire)) { // No cache or cache miss $pathsToRequire = []; diff --git a/lib/base.php b/lib/base.php index aa463e206a3..45058db1600 100644 --- a/lib/base.php +++ b/lib/base.php @@ -6,13 +6,14 @@ declare(strict_types=1); * SPDX-FileCopyrightText: 2013-2016 ownCloud, Inc. * SPDX-License-Identifier: AGPL-3.0-only */ -use OC\Encryption\HookManager; + use OC\Profiler\BuiltInProfiler; use OC\Share20\GroupDeletedListener; use OC\Share20\Hooks; use OC\Share20\UserDeletedListener; use OC\Share20\UserRemovedListener; use OCP\EventDispatcher\IEventDispatcher; +use OCP\Files\Events\BeforeFileSystemSetupEvent; use OCP\Group\Events\GroupDeletedEvent; use OCP\Group\Events\UserRemovedEvent; use OCP\IConfig; @@ -22,7 +23,6 @@ use OCP\IURLGenerator; use OCP\IUserSession; use OCP\Security\Bruteforce\IThrottler; use OCP\Server; -use OCP\Share; use OCP\Template\ITemplateManager; use OCP\User\Events\UserChangedEvent; use OCP\User\Events\UserDeletedEvent; @@ -188,8 +188,6 @@ class OC { } public static function checkConfig(): void { - $l = Server::get(\OCP\L10N\IFactory::class)->get('lib'); - // Create config if it does not already exist $configFilePath = self::$configDir . '/config.php'; if (!file_exists($configFilePath)) { @@ -201,6 +199,7 @@ class OC { if (!$configFileWritable && !OC_Helper::isReadOnlyConfigEnabled() || !$configFileWritable && \OCP\Util::needUpgrade()) { $urlGenerator = Server::get(IURLGenerator::class); + $l = Server::get(\OCP\L10N\IFactory::class)->get('lib'); if (self::$CLI) { echo $l->t('Cannot write into "config" directory!') . "\n"; @@ -711,6 +710,7 @@ class OC { self::performSameSiteCookieProtection($config); if (!defined('OC_CONSOLE')) { + $eventLogger->start('check_server', 'Run a few configuration checks'); $errors = OC_Util::checkServer($systemConfig); if (count($errors) > 0) { if (!self::$CLI) { @@ -745,6 +745,7 @@ class OC { } elseif (self::$CLI && $config->getSystemValueBool('installed', false)) { $config->deleteAppValue('core', 'cronErrors'); } + $eventLogger->end('check_server'); } // User and Groups @@ -752,6 +753,7 @@ class OC { self::$server->getSession()->set('user_id', ''); } + $eventLogger->start('setup_backends', 'Setup group and user backends'); Server::get(\OCP\IUserManager::class)->registerBackend(new \OC\User\Database()); Server::get(\OCP\IGroupManager::class)->addBackend(new \OC\Group\Database()); @@ -770,6 +772,7 @@ class OC { // Run upgrades in incognito mode OC_User::setIncognitoMode(true); } + $eventLogger->end('setup_backends'); self::registerCleanupHooks($systemConfig); self::registerShareHooks($systemConfig); @@ -907,15 +910,16 @@ class OC { } private static function registerEncryptionWrapperAndHooks(): void { + /** @var \OC\Encryption\Manager */ $manager = Server::get(\OCP\Encryption\IManager::class); - \OCP\Util::connectHook('OC_Filesystem', 'preSetup', $manager, 'setupStorage'); + Server::get(IEventDispatcher::class)->addListener( + BeforeFileSystemSetupEvent::class, + $manager->setupStorage(...), + ); $enabled = $manager->isEnabled(); if ($enabled) { - \OCP\Util::connectHook(Share::class, 'post_shared', HookManager::class, 'postShared'); - \OCP\Util::connectHook(Share::class, 'post_unshare', HookManager::class, 'postUnshared'); - \OCP\Util::connectHook('OC_Filesystem', 'post_rename', HookManager::class, 'postRename'); - \OCP\Util::connectHook('\OCA\Files_Trashbin\Trashbin', 'post_restore', HookManager::class, 'postRestore'); + \OC\Encryption\EncryptionEventListener::register(Server::get(IEventDispatcher::class)); } } diff --git a/lib/composer/composer/autoload_classmap.php b/lib/composer/composer/autoload_classmap.php index f15d4e2d55f..c0c086e4038 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -1034,7 +1034,6 @@ return array( 'OC\\AppFramework\\OCS\\V1Response' => $baseDir . '/lib/private/AppFramework/OCS/V1Response.php', 'OC\\AppFramework\\OCS\\V2Response' => $baseDir . '/lib/private/AppFramework/OCS/V2Response.php', 'OC\\AppFramework\\Routing\\RouteActionHandler' => $baseDir . '/lib/private/AppFramework/Routing/RouteActionHandler.php', - 'OC\\AppFramework\\Routing\\RouteConfig' => $baseDir . '/lib/private/AppFramework/Routing/RouteConfig.php', 'OC\\AppFramework\\Routing\\RouteParser' => $baseDir . '/lib/private/AppFramework/Routing/RouteParser.php', 'OC\\AppFramework\\ScopedPsrLogger' => $baseDir . '/lib/private/AppFramework/ScopedPsrLogger.php', 'OC\\AppFramework\\Services\\AppConfig' => $baseDir . '/lib/private/AppFramework/Services/AppConfig.php', @@ -1266,6 +1265,8 @@ return array( 'OC\\Core\\Command\\Info\\File' => $baseDir . '/core/Command/Info/File.php', 'OC\\Core\\Command\\Info\\FileUtils' => $baseDir . '/core/Command/Info/FileUtils.php', 'OC\\Core\\Command\\Info\\Space' => $baseDir . '/core/Command/Info/Space.php', + 'OC\\Core\\Command\\Info\\Storage' => $baseDir . '/core/Command/Info/Storage.php', + 'OC\\Core\\Command\\Info\\Storages' => $baseDir . '/core/Command/Info/Storages.php', 'OC\\Core\\Command\\Integrity\\CheckApp' => $baseDir . '/core/Command/Integrity/CheckApp.php', 'OC\\Core\\Command\\Integrity\\CheckCore' => $baseDir . '/core/Command/Integrity/CheckCore.php', 'OC\\Core\\Command\\Integrity\\SignApp' => $baseDir . '/core/Command/Integrity/SignApp.php', @@ -1539,6 +1540,7 @@ return array( 'OC\\DirectEditing\\Token' => $baseDir . '/lib/private/DirectEditing/Token.php', 'OC\\EmojiHelper' => $baseDir . '/lib/private/EmojiHelper.php', 'OC\\Encryption\\DecryptAll' => $baseDir . '/lib/private/Encryption/DecryptAll.php', + 'OC\\Encryption\\EncryptionEventListener' => $baseDir . '/lib/private/Encryption/EncryptionEventListener.php', 'OC\\Encryption\\EncryptionWrapper' => $baseDir . '/lib/private/Encryption/EncryptionWrapper.php', 'OC\\Encryption\\Exceptions\\DecryptionFailedException' => $baseDir . '/lib/private/Encryption/Exceptions/DecryptionFailedException.php', 'OC\\Encryption\\Exceptions\\EmptyEncryptionDataException' => $baseDir . '/lib/private/Encryption/Exceptions/EmptyEncryptionDataException.php', @@ -1549,7 +1551,6 @@ return array( 'OC\\Encryption\\Exceptions\\ModuleDoesNotExistsException' => $baseDir . '/lib/private/Encryption/Exceptions/ModuleDoesNotExistsException.php', 'OC\\Encryption\\Exceptions\\UnknownCipherException' => $baseDir . '/lib/private/Encryption/Exceptions/UnknownCipherException.php', 'OC\\Encryption\\File' => $baseDir . '/lib/private/Encryption/File.php', - 'OC\\Encryption\\HookManager' => $baseDir . '/lib/private/Encryption/HookManager.php', 'OC\\Encryption\\Keys\\Storage' => $baseDir . '/lib/private/Encryption/Keys/Storage.php', 'OC\\Encryption\\Manager' => $baseDir . '/lib/private/Encryption/Manager.php', 'OC\\Encryption\\Update' => $baseDir . '/lib/private/Encryption/Update.php', diff --git a/lib/composer/composer/autoload_static.php b/lib/composer/composer/autoload_static.php index dfb238acbf3..f2f817877d2 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -1075,7 +1075,6 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\AppFramework\\OCS\\V1Response' => __DIR__ . '/../../..' . '/lib/private/AppFramework/OCS/V1Response.php', 'OC\\AppFramework\\OCS\\V2Response' => __DIR__ . '/../../..' . '/lib/private/AppFramework/OCS/V2Response.php', 'OC\\AppFramework\\Routing\\RouteActionHandler' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Routing/RouteActionHandler.php', - 'OC\\AppFramework\\Routing\\RouteConfig' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Routing/RouteConfig.php', 'OC\\AppFramework\\Routing\\RouteParser' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Routing/RouteParser.php', 'OC\\AppFramework\\ScopedPsrLogger' => __DIR__ . '/../../..' . '/lib/private/AppFramework/ScopedPsrLogger.php', 'OC\\AppFramework\\Services\\AppConfig' => __DIR__ . '/../../..' . '/lib/private/AppFramework/Services/AppConfig.php', @@ -1307,6 +1306,8 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Core\\Command\\Info\\File' => __DIR__ . '/../../..' . '/core/Command/Info/File.php', 'OC\\Core\\Command\\Info\\FileUtils' => __DIR__ . '/../../..' . '/core/Command/Info/FileUtils.php', 'OC\\Core\\Command\\Info\\Space' => __DIR__ . '/../../..' . '/core/Command/Info/Space.php', + 'OC\\Core\\Command\\Info\\Storage' => __DIR__ . '/../../..' . '/core/Command/Info/Storage.php', + 'OC\\Core\\Command\\Info\\Storages' => __DIR__ . '/../../..' . '/core/Command/Info/Storages.php', 'OC\\Core\\Command\\Integrity\\CheckApp' => __DIR__ . '/../../..' . '/core/Command/Integrity/CheckApp.php', 'OC\\Core\\Command\\Integrity\\CheckCore' => __DIR__ . '/../../..' . '/core/Command/Integrity/CheckCore.php', 'OC\\Core\\Command\\Integrity\\SignApp' => __DIR__ . '/../../..' . '/core/Command/Integrity/SignApp.php', @@ -1580,6 +1581,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\DirectEditing\\Token' => __DIR__ . '/../../..' . '/lib/private/DirectEditing/Token.php', 'OC\\EmojiHelper' => __DIR__ . '/../../..' . '/lib/private/EmojiHelper.php', 'OC\\Encryption\\DecryptAll' => __DIR__ . '/../../..' . '/lib/private/Encryption/DecryptAll.php', + 'OC\\Encryption\\EncryptionEventListener' => __DIR__ . '/../../..' . '/lib/private/Encryption/EncryptionEventListener.php', 'OC\\Encryption\\EncryptionWrapper' => __DIR__ . '/../../..' . '/lib/private/Encryption/EncryptionWrapper.php', 'OC\\Encryption\\Exceptions\\DecryptionFailedException' => __DIR__ . '/../../..' . '/lib/private/Encryption/Exceptions/DecryptionFailedException.php', 'OC\\Encryption\\Exceptions\\EmptyEncryptionDataException' => __DIR__ . '/../../..' . '/lib/private/Encryption/Exceptions/EmptyEncryptionDataException.php', @@ -1590,7 +1592,6 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2 'OC\\Encryption\\Exceptions\\ModuleDoesNotExistsException' => __DIR__ . '/../../..' . '/lib/private/Encryption/Exceptions/ModuleDoesNotExistsException.php', 'OC\\Encryption\\Exceptions\\UnknownCipherException' => __DIR__ . '/../../..' . '/lib/private/Encryption/Exceptions/UnknownCipherException.php', 'OC\\Encryption\\File' => __DIR__ . '/../../..' . '/lib/private/Encryption/File.php', - 'OC\\Encryption\\HookManager' => __DIR__ . '/../../..' . '/lib/private/Encryption/HookManager.php', 'OC\\Encryption\\Keys\\Storage' => __DIR__ . '/../../..' . '/lib/private/Encryption/Keys/Storage.php', 'OC\\Encryption\\Manager' => __DIR__ . '/../../..' . '/lib/private/Encryption/Manager.php', 'OC\\Encryption\\Update' => __DIR__ . '/../../..' . '/lib/private/Encryption/Update.php', diff --git a/lib/l10n/cs.js b/lib/l10n/cs.js index 2233f31f686..b4304fb14ab 100644 --- a/lib/l10n/cs.js +++ b/lib/l10n/cs.js @@ -93,6 +93,8 @@ OC.L10N.register( "Destination does not exist" : "Cíl neexistuje", "Destination is not creatable" : "Cíl není možné vytvořit", "Dot files are not allowed" : "Názvy souborů, začínající na tečku nejsou dovolené", + "%1$s (renamed)" : "%1$s (přejmenované)", + "renamed file" : "přejmenovaný soubor", "\"%1$s\" is a forbidden file or folder name." : "„%1$s“ je zakázáno v názvu souboru nebo složky.", "\"%1$s\" is a forbidden prefix for file or folder names." : "„%1$s“ je zakázaná předpona pro názvy souborů či složek.", "\"%1$s\" is not allowed inside a file or folder name." : "„%1$s“ není povoleno v rámci názvu souboru či složky.", diff --git a/lib/l10n/cs.json b/lib/l10n/cs.json index 087e2244eeb..f6cb00467cb 100644 --- a/lib/l10n/cs.json +++ b/lib/l10n/cs.json @@ -91,6 +91,8 @@ "Destination does not exist" : "Cíl neexistuje", "Destination is not creatable" : "Cíl není možné vytvořit", "Dot files are not allowed" : "Názvy souborů, začínající na tečku nejsou dovolené", + "%1$s (renamed)" : "%1$s (přejmenované)", + "renamed file" : "přejmenovaný soubor", "\"%1$s\" is a forbidden file or folder name." : "„%1$s“ je zakázáno v názvu souboru nebo složky.", "\"%1$s\" is a forbidden prefix for file or folder names." : "„%1$s“ je zakázaná předpona pro názvy souborů či složek.", "\"%1$s\" is not allowed inside a file or folder name." : "„%1$s“ není povoleno v rámci názvu souboru či složky.", diff --git a/lib/l10n/de.js b/lib/l10n/de.js index 672c6ca4f25..e44f2b89e2b 100644 --- a/lib/l10n/de.js +++ b/lib/l10n/de.js @@ -93,6 +93,8 @@ OC.L10N.register( "Destination does not exist" : "Ziel existiert nicht", "Destination is not creatable" : "Ziel kann nicht erstellt werden", "Dot files are not allowed" : "Dateinamen mit einem Punkt am Anfang sind nicht erlaubt", + "%1$s (renamed)" : "%1$s (umbenannt)", + "renamed file" : "Umbenannte Datei", "\"%1$s\" is a forbidden file or folder name." : "\"%1$s\" ist ein unzulässiger Datei- oder Ordnername.", "\"%1$s\" is a forbidden prefix for file or folder names." : "\"%1$s\" ist ein unzulässiges Präfix für Datei- oder Ordnernamen.", "\"%1$s\" is not allowed inside a file or folder name." : "\"%1$s\" ist in einem Datei- oder Ordnernamen unzulässig.", @@ -120,7 +122,7 @@ OC.L10N.register( "__language_name__" : "Deutsch (Persönlich: Du)", "This is an automatically sent email, please do not reply." : "Dies ist eine automatisch gesendete E-Mail. Bitte antworte nicht auf diese E-Mail.", "Help & privacy" : "Hilfe & Datenschutz", - "Appearance and accessibility" : "Erscheinungsbild und Barrierefreiheit", + "Appearance and accessibility" : "Aussehen und Barrierefreiheit", "Apps" : "Apps", "Personal settings" : "Persönliche Einstellungen", "Administration settings" : "Verwaltungseinstellungen", diff --git a/lib/l10n/de.json b/lib/l10n/de.json index 17ce3b96f49..7a05dfc101f 100644 --- a/lib/l10n/de.json +++ b/lib/l10n/de.json @@ -91,6 +91,8 @@ "Destination does not exist" : "Ziel existiert nicht", "Destination is not creatable" : "Ziel kann nicht erstellt werden", "Dot files are not allowed" : "Dateinamen mit einem Punkt am Anfang sind nicht erlaubt", + "%1$s (renamed)" : "%1$s (umbenannt)", + "renamed file" : "Umbenannte Datei", "\"%1$s\" is a forbidden file or folder name." : "\"%1$s\" ist ein unzulässiger Datei- oder Ordnername.", "\"%1$s\" is a forbidden prefix for file or folder names." : "\"%1$s\" ist ein unzulässiges Präfix für Datei- oder Ordnernamen.", "\"%1$s\" is not allowed inside a file or folder name." : "\"%1$s\" ist in einem Datei- oder Ordnernamen unzulässig.", @@ -118,7 +120,7 @@ "__language_name__" : "Deutsch (Persönlich: Du)", "This is an automatically sent email, please do not reply." : "Dies ist eine automatisch gesendete E-Mail. Bitte antworte nicht auf diese E-Mail.", "Help & privacy" : "Hilfe & Datenschutz", - "Appearance and accessibility" : "Erscheinungsbild und Barrierefreiheit", + "Appearance and accessibility" : "Aussehen und Barrierefreiheit", "Apps" : "Apps", "Personal settings" : "Persönliche Einstellungen", "Administration settings" : "Verwaltungseinstellungen", diff --git a/lib/l10n/de_DE.js b/lib/l10n/de_DE.js index aa926c96d4f..4bfd5faa7f2 100644 --- a/lib/l10n/de_DE.js +++ b/lib/l10n/de_DE.js @@ -93,6 +93,8 @@ OC.L10N.register( "Destination does not exist" : "Ziel existiert nicht", "Destination is not creatable" : "Ziel kann nicht erstellt werden", "Dot files are not allowed" : "Dateinamen mit einem Punkt am Anfang sind nicht erlaubt", + "%1$s (renamed)" : "%1$s (umbenannt)", + "renamed file" : "Umbenannte Datei", "\"%1$s\" is a forbidden file or folder name." : "\"%1$s\" ist ein verbotener Datei- oder Ordnername.", "\"%1$s\" is a forbidden prefix for file or folder names." : "\"%1$s\" ist ein verbotenes Präfix für Datei- oder Ordnernamen.", "\"%1$s\" is not allowed inside a file or folder name." : "\"%1$s\" ist in einem Datei- oder Ordnernamen unzulässig.", diff --git a/lib/l10n/de_DE.json b/lib/l10n/de_DE.json index f23493df570..6faebb2c6c0 100644 --- a/lib/l10n/de_DE.json +++ b/lib/l10n/de_DE.json @@ -91,6 +91,8 @@ "Destination does not exist" : "Ziel existiert nicht", "Destination is not creatable" : "Ziel kann nicht erstellt werden", "Dot files are not allowed" : "Dateinamen mit einem Punkt am Anfang sind nicht erlaubt", + "%1$s (renamed)" : "%1$s (umbenannt)", + "renamed file" : "Umbenannte Datei", "\"%1$s\" is a forbidden file or folder name." : "\"%1$s\" ist ein verbotener Datei- oder Ordnername.", "\"%1$s\" is a forbidden prefix for file or folder names." : "\"%1$s\" ist ein verbotenes Präfix für Datei- oder Ordnernamen.", "\"%1$s\" is not allowed inside a file or folder name." : "\"%1$s\" ist in einem Datei- oder Ordnernamen unzulässig.", diff --git a/lib/l10n/en_GB.js b/lib/l10n/en_GB.js index 2494ab4aca4..d6a8da225ba 100644 --- a/lib/l10n/en_GB.js +++ b/lib/l10n/en_GB.js @@ -93,6 +93,8 @@ OC.L10N.register( "Destination does not exist" : "Destination does not exist", "Destination is not creatable" : "Destination is not creatable", "Dot files are not allowed" : "Dot files are not allowed", + "%1$s (renamed)" : "%1$s (renamed)", + "renamed file" : "renamed file", "\"%1$s\" is a forbidden file or folder name." : "\"%1$s\" is a forbidden file or folder name.", "\"%1$s\" is a forbidden prefix for file or folder names." : "\"%1$s\" is a forbidden prefix for file or folder names.", "\"%1$s\" is not allowed inside a file or folder name." : "\"%1$s\" is not allowed inside a file or folder name.", diff --git a/lib/l10n/en_GB.json b/lib/l10n/en_GB.json index 08e84f39af1..e1bd150aca6 100644 --- a/lib/l10n/en_GB.json +++ b/lib/l10n/en_GB.json @@ -91,6 +91,8 @@ "Destination does not exist" : "Destination does not exist", "Destination is not creatable" : "Destination is not creatable", "Dot files are not allowed" : "Dot files are not allowed", + "%1$s (renamed)" : "%1$s (renamed)", + "renamed file" : "renamed file", "\"%1$s\" is a forbidden file or folder name." : "\"%1$s\" is a forbidden file or folder name.", "\"%1$s\" is a forbidden prefix for file or folder names." : "\"%1$s\" is a forbidden prefix for file or folder names.", "\"%1$s\" is not allowed inside a file or folder name." : "\"%1$s\" is not allowed inside a file or folder name.", diff --git a/lib/l10n/es.js b/lib/l10n/es.js index a3cf61509ec..e4076a5cc2c 100644 --- a/lib/l10n/es.js +++ b/lib/l10n/es.js @@ -93,6 +93,8 @@ OC.L10N.register( "Destination does not exist" : "El destino no existe", "Destination is not creatable" : "El destino no se puede crear", "Dot files are not allowed" : "Los archivos Dot no están permitidos", + "%1$s (renamed)" : "%1$s (renombrado)", + "renamed file" : "archivo renombrado", "\"%1$s\" is a forbidden file or folder name." : "\"%1$s\" Es un nombre de archivo o carpeta no permitido.", "\"%1$s\" is a forbidden prefix for file or folder names." : "%1$s es un prefijo prohibido para nombres de archivo o carpeta.", "\"%1$s\" is not allowed inside a file or folder name." : "«%1$s» no se permite en el nombre de un archivo o carpeta.", diff --git a/lib/l10n/es.json b/lib/l10n/es.json index 34d4db2eec4..ba3043cee36 100644 --- a/lib/l10n/es.json +++ b/lib/l10n/es.json @@ -91,6 +91,8 @@ "Destination does not exist" : "El destino no existe", "Destination is not creatable" : "El destino no se puede crear", "Dot files are not allowed" : "Los archivos Dot no están permitidos", + "%1$s (renamed)" : "%1$s (renombrado)", + "renamed file" : "archivo renombrado", "\"%1$s\" is a forbidden file or folder name." : "\"%1$s\" Es un nombre de archivo o carpeta no permitido.", "\"%1$s\" is a forbidden prefix for file or folder names." : "%1$s es un prefijo prohibido para nombres de archivo o carpeta.", "\"%1$s\" is not allowed inside a file or folder name." : "«%1$s» no se permite en el nombre de un archivo o carpeta.", diff --git a/lib/l10n/et_EE.js b/lib/l10n/et_EE.js index 7932c79256b..ae5999fc496 100644 --- a/lib/l10n/et_EE.js +++ b/lib/l10n/et_EE.js @@ -84,6 +84,8 @@ OC.L10N.register( "Destination does not exist" : "Sihtmeediat pole olemas", "Destination is not creatable" : "Sihtmeedia pole loodav", "Dot files are not allowed" : "Punktiga failid pole lubatud", + "%1$s (renamed)" : "%1$s (nimi on muudetud)", + "renamed file" : "muudetud nimega fail", "\"%1$s\" is a forbidden file or folder name." : "„%1$s“ on keelatud faili- või kaustanimi.", "\"%1$s\" is a forbidden prefix for file or folder names." : "„%1$s“ on keelatud faili- või kaustanime eesliide.", "\"%1$s\" is not allowed inside a file or folder name." : "„%1$s“ pole faili- või kaustanimes lubatud.", diff --git a/lib/l10n/et_EE.json b/lib/l10n/et_EE.json index 99c9583bf87..65621b2643a 100644 --- a/lib/l10n/et_EE.json +++ b/lib/l10n/et_EE.json @@ -82,6 +82,8 @@ "Destination does not exist" : "Sihtmeediat pole olemas", "Destination is not creatable" : "Sihtmeedia pole loodav", "Dot files are not allowed" : "Punktiga failid pole lubatud", + "%1$s (renamed)" : "%1$s (nimi on muudetud)", + "renamed file" : "muudetud nimega fail", "\"%1$s\" is a forbidden file or folder name." : "„%1$s“ on keelatud faili- või kaustanimi.", "\"%1$s\" is a forbidden prefix for file or folder names." : "„%1$s“ on keelatud faili- või kaustanime eesliide.", "\"%1$s\" is not allowed inside a file or folder name." : "„%1$s“ pole faili- või kaustanimes lubatud.", diff --git a/lib/l10n/ga.js b/lib/l10n/ga.js index 9b05143b101..807a668847e 100644 --- a/lib/l10n/ga.js +++ b/lib/l10n/ga.js @@ -93,6 +93,8 @@ OC.L10N.register( "Destination does not exist" : "Níl ceann scríbe ann", "Destination is not creatable" : "Níl ceann scríbe cruthaithe", "Dot files are not allowed" : "Ní cheadaítear comhaid ponc", + "%1$s (renamed)" : "%1$s (athainmnithe)", + "renamed file" : "comhad athainmnithe", "\"%1$s\" is a forbidden file or folder name." : "Is ainm toirmiscthe comhaid nó fillteáin é \"%1$s\".", "\"%1$s\" is a forbidden prefix for file or folder names." : "Réimír toirmiscthe é \"%1$s\" d'ainmneacha comhaid nó fillteán.", "\"%1$s\" is not allowed inside a file or folder name." : "Ní cheadaítear \"%1$s\" taobh istigh d'ainm comhaid nó fillteáin.", diff --git a/lib/l10n/ga.json b/lib/l10n/ga.json index ee093dac05e..657ef31f4e3 100644 --- a/lib/l10n/ga.json +++ b/lib/l10n/ga.json @@ -91,6 +91,8 @@ "Destination does not exist" : "Níl ceann scríbe ann", "Destination is not creatable" : "Níl ceann scríbe cruthaithe", "Dot files are not allowed" : "Ní cheadaítear comhaid ponc", + "%1$s (renamed)" : "%1$s (athainmnithe)", + "renamed file" : "comhad athainmnithe", "\"%1$s\" is a forbidden file or folder name." : "Is ainm toirmiscthe comhaid nó fillteáin é \"%1$s\".", "\"%1$s\" is a forbidden prefix for file or folder names." : "Réimír toirmiscthe é \"%1$s\" d'ainmneacha comhaid nó fillteán.", "\"%1$s\" is not allowed inside a file or folder name." : "Ní cheadaítear \"%1$s\" taobh istigh d'ainm comhaid nó fillteáin.", diff --git a/lib/l10n/it.js b/lib/l10n/it.js index 50750f18726..33a2c8ad6bb 100644 --- a/lib/l10n/it.js +++ b/lib/l10n/it.js @@ -82,6 +82,8 @@ OC.L10N.register( "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Il modulo con ID: %s non esiste. Abilitalo nelle impostazioni delle applicazioni o contatta il tuo amministratore.", "File is too large to convert" : "Il file è troppo grande per essere convertito", "Dot files are not allowed" : "I file con un punto iniziale non sono consentiti", + "%1$s (renamed)" : "%1$s (rinominato)", + "renamed file" : "file rinominato", "File already exists" : "Il file esiste già", "Invalid path" : "Percorso non valido", "Failed to create file from template" : "Impossibile creare un file dal modello", diff --git a/lib/l10n/it.json b/lib/l10n/it.json index c6a820d0f5d..4cd53fbed0a 100644 --- a/lib/l10n/it.json +++ b/lib/l10n/it.json @@ -80,6 +80,8 @@ "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Il modulo con ID: %s non esiste. Abilitalo nelle impostazioni delle applicazioni o contatta il tuo amministratore.", "File is too large to convert" : "Il file è troppo grande per essere convertito", "Dot files are not allowed" : "I file con un punto iniziale non sono consentiti", + "%1$s (renamed)" : "%1$s (rinominato)", + "renamed file" : "file rinominato", "File already exists" : "Il file esiste già", "Invalid path" : "Percorso non valido", "Failed to create file from template" : "Impossibile creare un file dal modello", diff --git a/lib/l10n/pl.js b/lib/l10n/pl.js index 0d9fd0cb98a..953682de0ab 100644 --- a/lib/l10n/pl.js +++ b/lib/l10n/pl.js @@ -82,6 +82,8 @@ OC.L10N.register( "Empty file" : "Pusty plik", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Moduł o ID: %s nie istnieje. Włącz go w ustawieniach aplikacji lub skontaktuj się z administratorem.", "Dot files are not allowed" : "Pliki z kropką są niedozwolone", + "%1$s (renamed)" : "%1$s (zmieniona nazwa)", + "renamed file" : "zmieniona nazwa pliku", "Invalid parent path" : "Nieprawidłowa ścieżka nadrzędna", "File already exists" : "Plik już istnieje", "Invalid path" : "Niewłaściwa ścieżka", diff --git a/lib/l10n/pl.json b/lib/l10n/pl.json index c97634e8eff..4edf90eedba 100644 --- a/lib/l10n/pl.json +++ b/lib/l10n/pl.json @@ -80,6 +80,8 @@ "Empty file" : "Pusty plik", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Moduł o ID: %s nie istnieje. Włącz go w ustawieniach aplikacji lub skontaktuj się z administratorem.", "Dot files are not allowed" : "Pliki z kropką są niedozwolone", + "%1$s (renamed)" : "%1$s (zmieniona nazwa)", + "renamed file" : "zmieniona nazwa pliku", "Invalid parent path" : "Nieprawidłowa ścieżka nadrzędna", "File already exists" : "Plik już istnieje", "Invalid path" : "Niewłaściwa ścieżka", diff --git a/lib/l10n/pt_BR.js b/lib/l10n/pt_BR.js index 975a321f27c..bf7f7728be6 100644 --- a/lib/l10n/pt_BR.js +++ b/lib/l10n/pt_BR.js @@ -93,6 +93,8 @@ OC.L10N.register( "Destination does not exist" : "Destino não existe", "Destination is not creatable" : "Destino não pode ser criado", "Dot files are not allowed" : "Arquivos Dot não são permitidos", + "%1$s (renamed)" : "%1$s (renomeado)", + "renamed file" : "arquivo renomeado", "\"%1$s\" is a forbidden file or folder name." : "\"%1$s\" é um nome de arquivo ou pasta proibido.", "\"%1$s\" is a forbidden prefix for file or folder names." : "\"%1$s\" é um prefixo proibido para nomes de arquivos ou pastas.", "\"%1$s\" is not allowed inside a file or folder name." : "\"%1$s\" não é permitido dentro de um nome de arquivo ou pasta.", @@ -141,7 +143,7 @@ OC.L10N.register( "Profile picture" : "Foto do perfil", "About" : "Sobre", "Display name" : "Nome de exibição", - "Headline" : "Título ", + "Headline" : "Título", "Organisation" : "Organização", "Role" : "Função", "Pronouns" : "Pronomes", @@ -273,7 +275,7 @@ OC.L10N.register( "A valid Login must be provided" : "Um Login válido deve ser fornecido", "Login contains whitespace at the beginning or at the end" : "O login contém espaços em branco no início ou no final", "Login must not consist of dots only" : "O login não deve consistir apenas de pontos", - "Login is too long" : "O login é muito longo", + "Login is too long" : "Login é muito longo", "Login is invalid because files already exist for this user" : "O login é inválido porque já existem arquivos para este usuário", "Account disabled" : "Conta desativada", "Login canceled by app" : "Login cancelado pelo aplicativo", diff --git a/lib/l10n/pt_BR.json b/lib/l10n/pt_BR.json index 56506c4cce0..ad7df10df2b 100644 --- a/lib/l10n/pt_BR.json +++ b/lib/l10n/pt_BR.json @@ -91,6 +91,8 @@ "Destination does not exist" : "Destino não existe", "Destination is not creatable" : "Destino não pode ser criado", "Dot files are not allowed" : "Arquivos Dot não são permitidos", + "%1$s (renamed)" : "%1$s (renomeado)", + "renamed file" : "arquivo renomeado", "\"%1$s\" is a forbidden file or folder name." : "\"%1$s\" é um nome de arquivo ou pasta proibido.", "\"%1$s\" is a forbidden prefix for file or folder names." : "\"%1$s\" é um prefixo proibido para nomes de arquivos ou pastas.", "\"%1$s\" is not allowed inside a file or folder name." : "\"%1$s\" não é permitido dentro de um nome de arquivo ou pasta.", @@ -139,7 +141,7 @@ "Profile picture" : "Foto do perfil", "About" : "Sobre", "Display name" : "Nome de exibição", - "Headline" : "Título ", + "Headline" : "Título", "Organisation" : "Organização", "Role" : "Função", "Pronouns" : "Pronomes", @@ -271,7 +273,7 @@ "A valid Login must be provided" : "Um Login válido deve ser fornecido", "Login contains whitespace at the beginning or at the end" : "O login contém espaços em branco no início ou no final", "Login must not consist of dots only" : "O login não deve consistir apenas de pontos", - "Login is too long" : "O login é muito longo", + "Login is too long" : "Login é muito longo", "Login is invalid because files already exist for this user" : "O login é inválido porque já existem arquivos para este usuário", "Account disabled" : "Conta desativada", "Login canceled by app" : "Login cancelado pelo aplicativo", diff --git a/lib/l10n/sl.js b/lib/l10n/sl.js index 8677c681bcc..b9d564d8c3b 100644 --- a/lib/l10n/sl.js +++ b/lib/l10n/sl.js @@ -75,6 +75,7 @@ OC.L10N.register( "Empty file" : "Prazna datoteka", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modul z ID: %s ne obstaja. Omogočite ga med nastavitvami, ali pa stopite v stik s skrbnikom sistema.", "Dot files are not allowed" : "Skrite datoteke niso dovoljene", + "Filenames must not end with \"%1$s\"." : "Imena datotek se naj ne končajo s \"%1$s\".", "File already exists" : "Datoteka že obstaja", "Invalid path" : "Neveljavna pot", "Failed to create file from template" : "Ustvarjanje datoteke iz predloge je spodletelo", diff --git a/lib/l10n/sl.json b/lib/l10n/sl.json index 39c6044b937..1d526285b66 100644 --- a/lib/l10n/sl.json +++ b/lib/l10n/sl.json @@ -73,6 +73,7 @@ "Empty file" : "Prazna datoteka", "Module with ID: %s does not exist. Please enable it in your apps settings or contact your administrator." : "Modul z ID: %s ne obstaja. Omogočite ga med nastavitvami, ali pa stopite v stik s skrbnikom sistema.", "Dot files are not allowed" : "Skrite datoteke niso dovoljene", + "Filenames must not end with \"%1$s\"." : "Imena datotek se naj ne končajo s \"%1$s\".", "File already exists" : "Datoteka že obstaja", "Invalid path" : "Neveljavna pot", "Failed to create file from template" : "Ustvarjanje datoteke iz predloge je spodletelo", diff --git a/lib/l10n/sr.js b/lib/l10n/sr.js index b62a808994d..6bbd5d62333 100644 --- a/lib/l10n/sr.js +++ b/lib/l10n/sr.js @@ -93,6 +93,8 @@ OC.L10N.register( "Destination does not exist" : "Одредиште не постоји", "Destination is not creatable" : "Одредиште не може да се креира", "Dot files are not allowed" : "Фајлови са почетном тачком нису дозвољени", + "%1$s (renamed)" : "%1$s (преименован)", + "renamed file" : "преименован фајл", "\"%1$s\" is a forbidden file or folder name." : "„%1$s” је забрањено име фајла или фолдера.", "\"%1$s\" is a forbidden prefix for file or folder names." : "„%1$s” је забрањени префикс имена фајла или фолдера.", "\"%1$s\" is not allowed inside a file or folder name." : "„%1$s” није дозвољено у имену фајла или фолдера.", diff --git a/lib/l10n/sr.json b/lib/l10n/sr.json index 7dbc69c583a..8d6b3cc72c3 100644 --- a/lib/l10n/sr.json +++ b/lib/l10n/sr.json @@ -91,6 +91,8 @@ "Destination does not exist" : "Одредиште не постоји", "Destination is not creatable" : "Одредиште не може да се креира", "Dot files are not allowed" : "Фајлови са почетном тачком нису дозвољени", + "%1$s (renamed)" : "%1$s (преименован)", + "renamed file" : "преименован фајл", "\"%1$s\" is a forbidden file or folder name." : "„%1$s” је забрањено име фајла или фолдера.", "\"%1$s\" is a forbidden prefix for file or folder names." : "„%1$s” је забрањени префикс имена фајла или фолдера.", "\"%1$s\" is not allowed inside a file or folder name." : "„%1$s” није дозвољено у имену фајла или фолдера.", diff --git a/lib/l10n/sv.js b/lib/l10n/sv.js index ed4c4af3ef0..f203a0471e7 100644 --- a/lib/l10n/sv.js +++ b/lib/l10n/sv.js @@ -93,6 +93,8 @@ OC.L10N.register( "Destination does not exist" : "Destinationen finns inte", "Destination is not creatable" : "Destinationen går inte att skapa", "Dot files are not allowed" : "Dot-filer är inte tillåtna", + "%1$s (renamed)" : "%1$s (omdöpt)", + "renamed file" : "omdöpt fil", "\"%1$s\" is a forbidden file or folder name." : "\"%1$s\" är ett förbjudet fil- eller mappnamn.", "\"%1$s\" is a forbidden prefix for file or folder names." : "\"%1$s\" är ett förbjudet prefix för fil- eller mappnamn.", "\"%1$s\" is not allowed inside a file or folder name." : "\"%1$s\" är inte tillåtet i ett fil- eller mappnamn.", diff --git a/lib/l10n/sv.json b/lib/l10n/sv.json index 55855426f34..3f3db6805e8 100644 --- a/lib/l10n/sv.json +++ b/lib/l10n/sv.json @@ -91,6 +91,8 @@ "Destination does not exist" : "Destinationen finns inte", "Destination is not creatable" : "Destinationen går inte att skapa", "Dot files are not allowed" : "Dot-filer är inte tillåtna", + "%1$s (renamed)" : "%1$s (omdöpt)", + "renamed file" : "omdöpt fil", "\"%1$s\" is a forbidden file or folder name." : "\"%1$s\" är ett förbjudet fil- eller mappnamn.", "\"%1$s\" is a forbidden prefix for file or folder names." : "\"%1$s\" är ett förbjudet prefix för fil- eller mappnamn.", "\"%1$s\" is not allowed inside a file or folder name." : "\"%1$s\" är inte tillåtet i ett fil- eller mappnamn.", diff --git a/lib/l10n/zh_CN.js b/lib/l10n/zh_CN.js index 1637350f1e9..0cc4fd8d378 100644 --- a/lib/l10n/zh_CN.js +++ b/lib/l10n/zh_CN.js @@ -93,6 +93,8 @@ OC.L10N.register( "Destination does not exist" : "目标不存在", "Destination is not creatable" : "目标不可创建", "Dot files are not allowed" : "以 . 开头的文件不被允许", + "%1$s (renamed)" : "%1$s(已重命名)", + "renamed file" : "已重命名文件", "\"%1$s\" is a forbidden file or folder name." : "\"%1$s\" 是禁止的文件或文件夹名称。", "\"%1$s\" is a forbidden prefix for file or folder names." : "\"%1$s\" 是文件或文件夹名称的禁止前缀。", "\"%1$s\" is not allowed inside a file or folder name." : "文件或文件夹名称中不允许出现 \"%1$s\"。", diff --git a/lib/l10n/zh_CN.json b/lib/l10n/zh_CN.json index dd94f1bb5cf..2ba31a10e87 100644 --- a/lib/l10n/zh_CN.json +++ b/lib/l10n/zh_CN.json @@ -91,6 +91,8 @@ "Destination does not exist" : "目标不存在", "Destination is not creatable" : "目标不可创建", "Dot files are not allowed" : "以 . 开头的文件不被允许", + "%1$s (renamed)" : "%1$s(已重命名)", + "renamed file" : "已重命名文件", "\"%1$s\" is a forbidden file or folder name." : "\"%1$s\" 是禁止的文件或文件夹名称。", "\"%1$s\" is a forbidden prefix for file or folder names." : "\"%1$s\" 是文件或文件夹名称的禁止前缀。", "\"%1$s\" is not allowed inside a file or folder name." : "文件或文件夹名称中不允许出现 \"%1$s\"。", diff --git a/lib/l10n/zh_HK.js b/lib/l10n/zh_HK.js index a85c29e400c..b4bb06b4cdc 100644 --- a/lib/l10n/zh_HK.js +++ b/lib/l10n/zh_HK.js @@ -93,6 +93,8 @@ OC.L10N.register( "Destination does not exist" : "目的地不存在", "Destination is not creatable" : "無法建立目的地", "Dot files are not allowed" : "不允許小數點開頭的檔案", + "%1$s (renamed)" : "%1$s(已重新命名)", + "renamed file" : "已重新命名的檔案", "\"%1$s\" is a forbidden file or folder name." : "「%1$s」是禁止的檔案或資料夾名稱。", "\"%1$s\" is a forbidden prefix for file or folder names." : "「%1$s」是禁止的檔案或資料夾名稱前綴。", "\"%1$s\" is not allowed inside a file or folder name." : "「%1$s」不允許出現在檔案或資料夾名稱中。", diff --git a/lib/l10n/zh_HK.json b/lib/l10n/zh_HK.json index b58db1e510c..6cc1edd0f35 100644 --- a/lib/l10n/zh_HK.json +++ b/lib/l10n/zh_HK.json @@ -91,6 +91,8 @@ "Destination does not exist" : "目的地不存在", "Destination is not creatable" : "無法建立目的地", "Dot files are not allowed" : "不允許小數點開頭的檔案", + "%1$s (renamed)" : "%1$s(已重新命名)", + "renamed file" : "已重新命名的檔案", "\"%1$s\" is a forbidden file or folder name." : "「%1$s」是禁止的檔案或資料夾名稱。", "\"%1$s\" is a forbidden prefix for file or folder names." : "「%1$s」是禁止的檔案或資料夾名稱前綴。", "\"%1$s\" is not allowed inside a file or folder name." : "「%1$s」不允許出現在檔案或資料夾名稱中。", diff --git a/lib/l10n/zh_TW.js b/lib/l10n/zh_TW.js index 4f92a865290..016689524c5 100644 --- a/lib/l10n/zh_TW.js +++ b/lib/l10n/zh_TW.js @@ -93,6 +93,8 @@ OC.L10N.register( "Destination does not exist" : "目的地不存在", "Destination is not creatable" : "無法建立目的地", "Dot files are not allowed" : "不允許小數點開頭的檔案", + "%1$s (renamed)" : "%1$s(已重新命名)", + "renamed file" : "已重新命名檔案", "\"%1$s\" is a forbidden file or folder name." : "「%1$s」是禁止的檔案或資料夾名稱。", "\"%1$s\" is a forbidden prefix for file or folder names." : "「%1$s」是禁止的檔案或資料夾名稱前綴。", "\"%1$s\" is not allowed inside a file or folder name." : "「%1$s」不允許出現在檔案或資料夾名稱中。", diff --git a/lib/l10n/zh_TW.json b/lib/l10n/zh_TW.json index 34b32540bc8..67f95c65e75 100644 --- a/lib/l10n/zh_TW.json +++ b/lib/l10n/zh_TW.json @@ -91,6 +91,8 @@ "Destination does not exist" : "目的地不存在", "Destination is not creatable" : "無法建立目的地", "Dot files are not allowed" : "不允許小數點開頭的檔案", + "%1$s (renamed)" : "%1$s(已重新命名)", + "renamed file" : "已重新命名檔案", "\"%1$s\" is a forbidden file or folder name." : "「%1$s」是禁止的檔案或資料夾名稱。", "\"%1$s\" is a forbidden prefix for file or folder names." : "「%1$s」是禁止的檔案或資料夾名稱前綴。", "\"%1$s\" is not allowed inside a file or folder name." : "「%1$s」不允許出現在檔案或資料夾名稱中。", diff --git a/lib/private/Accounts/AccountManager.php b/lib/private/Accounts/AccountManager.php index d69e72a29de..e8b67311636 100644 --- a/lib/private/Accounts/AccountManager.php +++ b/lib/private/Accounts/AccountManager.php @@ -131,9 +131,7 @@ class AccountManager implements IAccountManager { $property->setScope(self::SCOPE_LOCAL); } } else { - // migrate scope values to the new format - // invalid scopes are mapped to a default value - $property->setScope(AccountProperty::mapScopeToV2($property->getScope())); + $property->setScope($property->getScope()); } } diff --git a/lib/private/Accounts/AccountProperty.php b/lib/private/Accounts/AccountProperty.php index 0c4ad568709..3a89e9bbc7a 100644 --- a/lib/private/Accounts/AccountProperty.php +++ b/lib/private/Accounts/AccountProperty.php @@ -55,16 +55,11 @@ class AccountProperty implements IAccountProperty { * @since 15.0.0 */ public function setScope(string $scope): IAccountProperty { - $newScope = $this->mapScopeToV2($scope); - if (!in_array($newScope, [ - IAccountManager::SCOPE_LOCAL, - IAccountManager::SCOPE_FEDERATED, - IAccountManager::SCOPE_PRIVATE, - IAccountManager::SCOPE_PUBLISHED - ])) { + if (!in_array($scope, IAccountManager::ALLOWED_SCOPES, )) { throw new InvalidArgumentException('Invalid scope'); } - $this->scope = $newScope; + /** @var IAccountManager::SCOPE_* $scope */ + $this->scope = $scope; return $this; } @@ -105,19 +100,6 @@ class AccountProperty implements IAccountProperty { return $this->scope; } - public static function mapScopeToV2(string $scope): string { - if (str_starts_with($scope, 'v2-')) { - return $scope; - } - - return match ($scope) { - IAccountManager::VISIBILITY_PRIVATE, '' => IAccountManager::SCOPE_LOCAL, - IAccountManager::VISIBILITY_CONTACTS_ONLY => IAccountManager::SCOPE_FEDERATED, - IAccountManager::VISIBILITY_PUBLIC => IAccountManager::SCOPE_PUBLISHED, - default => $scope, - }; - } - /** * Get the verification status of a property * diff --git a/lib/private/AppFramework/Bootstrap/Coordinator.php b/lib/private/AppFramework/Bootstrap/Coordinator.php index 2b04d291730..4e613703dec 100644 --- a/lib/private/AppFramework/Bootstrap/Coordinator.php +++ b/lib/private/AppFramework/Bootstrap/Coordinator.php @@ -83,11 +83,12 @@ class Coordinator { $appNameSpace = App::buildAppNamespace($appId); $applicationClassName = $appNameSpace . '\\AppInfo\\Application'; try { - if (class_exists($applicationClassName) && in_array(IBootstrap::class, class_implements($applicationClassName), true)) { + if (class_exists($applicationClassName) && is_a($applicationClassName, IBootstrap::class, true)) { $this->eventLogger->start("bootstrap:register_app:$appId:application", "Load `Application` instance for $appId"); try { - /** @var IBootstrap|App $application */ - $apps[$appId] = $application = $this->serverContainer->query($applicationClassName); + /** @var IBootstrap&App $application */ + $application = $this->serverContainer->query($applicationClassName); + $apps[$appId] = $application; } catch (QueryException $e) { // Weird, but ok $this->eventLogger->end("bootstrap:register_app:$appId"); diff --git a/lib/private/AppFramework/Routing/RouteConfig.php b/lib/private/AppFramework/Routing/RouteConfig.php deleted file mode 100644 index 2b7f21a8ba5..00000000000 --- a/lib/private/AppFramework/Routing/RouteConfig.php +++ /dev/null @@ -1,279 +0,0 @@ -<?php - -declare(strict_types=1); -/** - * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors - * SPDX-FileCopyrightText: 2016 ownCloud, Inc. - * SPDX-License-Identifier: AGPL-3.0-only - */ -namespace OC\AppFramework\Routing; - -use OC\AppFramework\DependencyInjection\DIContainer; -use OC\Route\Router; - -/** - * Class RouteConfig - * @package OC\AppFramework\routing - */ -class RouteConfig { - /** @var DIContainer */ - private $container; - - /** @var Router */ - private $router; - - /** @var array */ - private $routes; - - /** @var string */ - private $appName; - - /** @var string[] */ - private $controllerNameCache = []; - - protected $rootUrlApps = [ - 'cloud_federation_api', - 'core', - 'files_sharing', - 'files', - 'profile', - 'settings', - 'spreed', - ]; - - /** - * @param \OC\AppFramework\DependencyInjection\DIContainer $container - * @param \OC\Route\Router $router - * @param array $routes - * @internal param $appName - */ - public function __construct(DIContainer $container, Router $router, $routes) { - $this->routes = $routes; - $this->container = $container; - $this->router = $router; - $this->appName = $container['AppName']; - } - - /** - * The routes and resource will be registered to the \OCP\Route\IRouter - */ - public function register() { - // parse simple - $this->processIndexRoutes($this->routes); - - // parse resources - $this->processIndexResources($this->routes); - - /* - * OCS routes go into a different collection - */ - $oldCollection = $this->router->getCurrentCollection(); - $this->router->useCollection($oldCollection . '.ocs'); - - // parse ocs simple routes - $this->processOCS($this->routes); - - // parse ocs simple routes - $this->processOCSResources($this->routes); - - $this->router->useCollection($oldCollection); - } - - private function processOCS(array $routes): void { - $ocsRoutes = $routes['ocs'] ?? []; - foreach ($ocsRoutes as $ocsRoute) { - $this->processRoute($ocsRoute, 'ocs.'); - } - } - - /** - * Creates one route base on the give configuration - * @param array $routes - * @throws \UnexpectedValueException - */ - private function processIndexRoutes(array $routes): void { - $simpleRoutes = $routes['routes'] ?? []; - foreach ($simpleRoutes as $simpleRoute) { - $this->processRoute($simpleRoute); - } - } - - protected function processRoute(array $route, string $routeNamePrefix = ''): void { - $name = $route['name']; - $postfix = $route['postfix'] ?? ''; - $root = $this->buildRootPrefix($route, $routeNamePrefix); - - $url = $root . '/' . ltrim($route['url'], '/'); - $verb = strtoupper($route['verb'] ?? 'GET'); - - $split = explode('#', $name, 2); - if (count($split) !== 2) { - throw new \UnexpectedValueException('Invalid route name: use the format foo#bar to reference FooController::bar'); - } - [$controller, $action] = $split; - - $controllerName = $this->buildControllerName($controller); - $actionName = $this->buildActionName($action); - - /* - * The route name has to be lowercase, for symfony to match it correctly. - * This is required because smyfony allows mixed casing for controller names in the routes. - * To avoid breaking all the existing route names, registering and matching will only use the lowercase names. - * This is also safe on the PHP side because class and method names collide regardless of the casing. - */ - $routeName = strtolower($routeNamePrefix . $this->appName . '.' . $controller . '.' . $action . $postfix); - - $router = $this->router->create($routeName, $url) - ->method($verb); - - // optionally register requirements for route. This is used to - // tell the route parser how url parameters should be matched - if (array_key_exists('requirements', $route)) { - $router->requirements($route['requirements']); - } - - // optionally register defaults for route. This is used to - // tell the route parser how url parameters should be default valued - $defaults = []; - if (array_key_exists('defaults', $route)) { - $defaults = $route['defaults']; - } - - $defaults['caller'] = [$this->appName, $controllerName, $actionName]; - $router->defaults($defaults); - } - - /** - * For a given name and url restful OCS routes are created: - * - index - * - show - * - create - * - update - * - destroy - * - * @param array $routes - */ - private function processOCSResources(array $routes): void { - $this->processResources($routes['ocs-resources'] ?? [], 'ocs.'); - } - - /** - * For a given name and url restful routes are created: - * - index - * - show - * - create - * - update - * - destroy - * - * @param array $routes - */ - private function processIndexResources(array $routes): void { - $this->processResources($routes['resources'] ?? []); - } - - /** - * For a given name and url restful routes are created: - * - index - * - show - * - create - * - update - * - destroy - * - * @param array $resources - * @param string $routeNamePrefix - */ - protected function processResources(array $resources, string $routeNamePrefix = ''): void { - // declaration of all restful actions - $actions = [ - ['name' => 'index', 'verb' => 'GET', 'on-collection' => true], - ['name' => 'show', 'verb' => 'GET'], - ['name' => 'create', 'verb' => 'POST', 'on-collection' => true], - ['name' => 'update', 'verb' => 'PUT'], - ['name' => 'destroy', 'verb' => 'DELETE'], - ]; - - foreach ($resources as $resource => $config) { - $root = $this->buildRootPrefix($config, $routeNamePrefix); - - // the url parameter used as id to the resource - foreach ($actions as $action) { - $url = $root . '/' . ltrim($config['url'], '/'); - $method = $action['name']; - - $verb = strtoupper($action['verb'] ?? 'GET'); - $collectionAction = $action['on-collection'] ?? false; - if (!$collectionAction) { - $url .= '/{id}'; - } - if (isset($action['url-postfix'])) { - $url .= '/' . $action['url-postfix']; - } - - $controller = $resource; - - $controllerName = $this->buildControllerName($controller); - $actionName = $this->buildActionName($method); - - $routeName = $routeNamePrefix . $this->appName . '.' . strtolower($resource) . '.' . $method; - - $route = $this->router->create($routeName, $url) - ->method($verb); - - $route->defaults(['caller' => [$this->appName, $controllerName, $actionName]]); - } - } - } - - private function buildRootPrefix(array $route, string $routeNamePrefix): string { - $defaultRoot = $this->appName === 'core' ? '' : '/apps/' . $this->appName; - $root = $route['root'] ?? $defaultRoot; - - if ($routeNamePrefix !== '') { - // In OCS all apps are whitelisted - return $root; - } - - if (!\in_array($this->appName, $this->rootUrlApps, true)) { - // Only allow root URLS for some apps - return $defaultRoot; - } - - return $root; - } - - /** - * Based on a given route name the controller name is generated - * @param string $controller - * @return string - */ - private function buildControllerName(string $controller): string { - if (!isset($this->controllerNameCache[$controller])) { - $this->controllerNameCache[$controller] = $this->underScoreToCamelCase(ucfirst($controller)) . 'Controller'; - } - return $this->controllerNameCache[$controller]; - } - - /** - * Based on the action part of the route name the controller method name is generated - * @param string $action - * @return string - */ - private function buildActionName(string $action): string { - return $this->underScoreToCamelCase($action); - } - - /** - * Underscored strings are converted to camel case strings - * @param string $str - * @return string - */ - private function underScoreToCamelCase(string $str): string { - $pattern = '/_[a-z]?/'; - return preg_replace_callback( - $pattern, - function ($matches) { - return strtoupper(ltrim($matches[0], '_')); - }, - $str); - } -} diff --git a/lib/private/AppFramework/Routing/RouteParser.php b/lib/private/AppFramework/Routing/RouteParser.php index 894a74c727b..55e58234673 100644 --- a/lib/private/AppFramework/Routing/RouteParser.php +++ b/lib/private/AppFramework/Routing/RouteParser.php @@ -76,7 +76,7 @@ class RouteParser { $url = $root . '/' . ltrim($route['url'], '/'); $verb = strtoupper($route['verb'] ?? 'GET'); - $split = explode('#', $name, 2); + $split = explode('#', $name, 3); if (count($split) !== 2) { throw new \UnexpectedValueException('Invalid route name: use the format foo#bar to reference FooController::bar'); } @@ -87,7 +87,7 @@ class RouteParser { /* * The route name has to be lowercase, for symfony to match it correctly. - * This is required because smyfony allows mixed casing for controller names in the routes. + * This is required because symfony allows mixed casing for controller names in the routes. * To avoid breaking all the existing route names, registering and matching will only use the lowercase names. * This is also safe on the PHP side because class and method names collide regardless of the casing. */ diff --git a/lib/private/AppFramework/Utility/SimpleContainer.php b/lib/private/AppFramework/Utility/SimpleContainer.php index 24918992ea3..9af65a37ab8 100644 --- a/lib/private/AppFramework/Utility/SimpleContainer.php +++ b/lib/private/AppFramework/Utility/SimpleContainer.php @@ -153,13 +153,13 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer { return $closure($this); }; $name = $this->sanitizeName($name); - if (isset($this[$name])) { - unset($this[$name]); + if (isset($this->container[$name])) { + unset($this->container[$name]); } if ($shared) { - $this[$name] = $wrapped; + $this->container[$name] = $wrapped; } else { - $this[$name] = $this->container->factory($wrapped); + $this->container[$name] = $this->container->factory($wrapped); } } diff --git a/lib/private/Calendar/Manager.php b/lib/private/Calendar/Manager.php index e86e0e1d410..21370e74d54 100644 --- a/lib/private/Calendar/Manager.php +++ b/lib/private/Calendar/Manager.php @@ -33,6 +33,7 @@ use Sabre\HTTP\Response; use Sabre\VObject\Component\VCalendar; use Sabre\VObject\Component\VEvent; use Sabre\VObject\Component\VFreeBusy; +use Sabre\VObject\ParseException; use Sabre\VObject\Property\VCard\DateTime; use Sabre\VObject\Reader; use Throwable; @@ -235,9 +236,14 @@ class Manager implements IManager { $this->logger->warning('iMip message could not be processed because user has no calendars'); return false; } - - /** @var VCalendar $vObject|null */ - $calendarObject = Reader::read($calendarData); + + try { + /** @var VCalendar $vObject|null */ + $calendarObject = Reader::read($calendarData); + } catch (ParseException $e) { + $this->logger->error('iMip message could not be processed because an error occurred while parsing the iMip message', ['exception' => $e]); + return false; + } if (!isset($calendarObject->METHOD) || $calendarObject->METHOD->getValue() !== 'REQUEST') { $this->logger->warning('iMip message contains an incorrect or invalid method'); @@ -249,6 +255,7 @@ class Manager implements IManager { return false; } + /** @var VEvent|null $vEvent */ $eventObject = $calendarObject->VEVENT; if (!isset($eventObject->UID)) { @@ -256,6 +263,11 @@ class Manager implements IManager { return false; } + if (!isset($eventObject->ORGANIZER)) { + $this->logger->warning('iMip message event dose not contains an organizer'); + return false; + } + if (!isset($eventObject->ATTENDEE)) { $this->logger->warning('iMip message event dose not contains any attendees'); return false; @@ -296,7 +308,7 @@ class Manager implements IManager { } } - $this->logger->warning('iMip message event could not be processed because the no corresponding event was found in any calendar'); + $this->logger->warning('iMip message event could not be processed because no corresponding event was found in any calendar'); return false; } @@ -309,23 +321,51 @@ class Manager implements IManager { string $recipient, string $calendarData, ): bool { - /** @var VCalendar $vObject|null */ - $vObject = Reader::read($calendarData); + + $calendars = $this->getCalendarsForPrincipal($principalUri); + if (empty($calendars)) { + $this->logger->warning('iMip message could not be processed because user has no calendars'); + return false; + } + + try { + /** @var VCalendar $vObject|null */ + $vObject = Reader::read($calendarData); + } catch (ParseException $e) { + $this->logger->error('iMip message could not be processed because an error occurred while parsing the iMip message', ['exception' => $e]); + return false; + } if ($vObject === null) { + $this->logger->warning('iMip message contains an invalid calendar object'); + return false; + } + + if (!isset($vObject->METHOD) || $vObject->METHOD->getValue() !== 'REPLY') { + $this->logger->warning('iMip message contains an incorrect or invalid method'); + return false; + } + + if (!isset($vObject->VEVENT)) { + $this->logger->warning('iMip message contains no event'); return false; } /** @var VEvent|null $vEvent */ - $vEvent = $vObject->{'VEVENT'}; + $vEvent = $vObject->VEVENT; + + if (!isset($vEvent->UID)) { + $this->logger->warning('iMip message event dose not contains a UID'); + return false; + } - if ($vEvent === null) { + if (!isset($vEvent->ORGANIZER)) { + $this->logger->warning('iMip message event dose not contains an organizer'); return false; } - // First, we check if the correct method is passed to us - if (strcasecmp('REPLY', $vObject->{'METHOD'}->getValue()) !== 0) { - $this->logger->warning('Wrong method provided for processing'); + if (!isset($vEvent->ATTENDEE)) { + $this->logger->warning('iMip message event dose not contains any attendees'); return false; } @@ -333,7 +373,7 @@ class Manager implements IManager { $organizer = substr($vEvent->{'ORGANIZER'}->getValue(), 7); if (strcasecmp($recipient, $organizer) !== 0) { - $this->logger->warning('Recipient and ORGANIZER must be identical'); + $this->logger->warning('iMip message event could not be processed because recipient and ORGANIZER must be identical'); return false; } @@ -341,13 +381,7 @@ class Manager implements IManager { /** @var DateTime $eventTime */ $eventTime = $vEvent->{'DTSTART'}; if ($eventTime->getDateTime()->getTimeStamp() < $this->timeFactory->getTime()) { // this might cause issues with recurrences - $this->logger->warning('Only events in the future are processed'); - return false; - } - - $calendars = $this->getCalendarsForPrincipal($principalUri); - if (empty($calendars)) { - $this->logger->warning('Could not find any calendars for principal ' . $principalUri); + $this->logger->warning('iMip message event could not be processed because the event is in the past'); return false; } @@ -369,14 +403,14 @@ class Manager implements IManager { } if (empty($found)) { - $this->logger->info('Event not found in any calendar for principal ' . $principalUri . 'and UID' . $vEvent->{'UID'}->getValue()); + $this->logger->warning('iMip message event could not be processed because no corresponding event was found in any calendar ' . $principalUri . 'and UID' . $vEvent->{'UID'}->getValue()); return false; } try { $found->handleIMipMessage($name, $calendarData); // sabre will handle the scheduling behind the scenes } catch (CalendarException $e) { - $this->logger->error('Could not update calendar for iMIP processing', ['exception' => $e]); + $this->logger->error('An error occurred while processing the iMip message event', ['exception' => $e]); return false; } return true; @@ -393,29 +427,57 @@ class Manager implements IManager { string $recipient, string $calendarData, ): bool { - /** @var VCalendar $vObject|null */ - $vObject = Reader::read($calendarData); + + $calendars = $this->getCalendarsForPrincipal($principalUri); + if (empty($calendars)) { + $this->logger->warning('iMip message could not be processed because user has no calendars'); + return false; + } + + try { + /** @var VCalendar $vObject|null */ + $vObject = Reader::read($calendarData); + } catch (ParseException $e) { + $this->logger->error('iMip message could not be processed because an error occurred while parsing the iMip message', ['exception' => $e]); + return false; + } if ($vObject === null) { + $this->logger->warning('iMip message contains an invalid calendar object'); + return false; + } + + if (!isset($vObject->METHOD) || $vObject->METHOD->getValue() !== 'CANCEL') { + $this->logger->warning('iMip message contains an incorrect or invalid method'); + return false; + } + + if (!isset($vObject->VEVENT)) { + $this->logger->warning('iMip message contains no event'); return false; } /** @var VEvent|null $vEvent */ $vEvent = $vObject->{'VEVENT'}; - if ($vEvent === null) { + if (!isset($vEvent->UID)) { + $this->logger->warning('iMip message event dose not contains a UID'); + return false; + } + + if (!isset($vEvent->ORGANIZER)) { + $this->logger->warning('iMip message event dose not contains an organizer'); return false; } - // First, we check if the correct method is passed to us - if (strcasecmp('CANCEL', $vObject->{'METHOD'}->getValue()) !== 0) { - $this->logger->warning('Wrong method provided for processing'); + if (!isset($vEvent->ATTENDEE)) { + $this->logger->warning('iMip message event dose not contains any attendees'); return false; } $attendee = substr($vEvent->{'ATTENDEE'}->getValue(), 7); if (strcasecmp($recipient, $attendee) !== 0) { - $this->logger->warning('Recipient must be an ATTENDEE of this event'); + $this->logger->warning('iMip message event could not be processed because recipient must be an ATTENDEE of this event'); return false; } @@ -426,7 +488,7 @@ class Manager implements IManager { $organizer = substr($vEvent->{'ORGANIZER'}->getValue(), 7); $isNotOrganizer = ($replyTo !== null) ? (strcasecmp($sender, $organizer) !== 0 && strcasecmp($replyTo, $organizer) !== 0) : (strcasecmp($sender, $organizer) !== 0); if ($isNotOrganizer) { - $this->logger->warning('Sender must be the ORGANIZER of this event'); + $this->logger->warning('iMip message event could not be processed because sender must be the ORGANIZER of this event'); return false; } @@ -434,14 +496,7 @@ class Manager implements IManager { /** @var DateTime $eventTime */ $eventTime = $vEvent->{'DTSTART'}; if ($eventTime->getDateTime()->getTimeStamp() < $this->timeFactory->getTime()) { // this might cause issues with recurrences - $this->logger->warning('Only events in the future are processed'); - return false; - } - - // Check if we have a calendar to work with - $calendars = $this->getCalendarsForPrincipal($principalUri); - if (empty($calendars)) { - $this->logger->warning('Could not find any calendars for principal ' . $principalUri); + $this->logger->warning('iMip message event could not be processed because the event is in the past'); return false; } @@ -463,17 +518,15 @@ class Manager implements IManager { } if (empty($found)) { - $this->logger->info('Event not found in any calendar for principal ' . $principalUri . 'and UID' . $vEvent->{'UID'}->getValue()); - // this is a safe operation - // we can ignore events that have been cancelled but were not in the calendar anyway - return true; + $this->logger->warning('iMip message event could not be processed because no corresponding event was found in any calendar ' . $principalUri . 'and UID' . $vEvent->{'UID'}->getValue()); + return false; } try { $found->handleIMipMessage($name, $calendarData); // sabre will handle the scheduling behind the scenes return true; } catch (CalendarException $e) { - $this->logger->error('Could not update calendar for iMIP processing', ['exception' => $e]); + $this->logger->error('An error occurred while processing the iMip message event', ['exception' => $e]); return false; } } diff --git a/lib/private/DB/Adapter.php b/lib/private/DB/Adapter.php index 8f1b8e6d75f..edd8c1bf023 100644 --- a/lib/private/DB/Adapter.php +++ b/lib/private/DB/Adapter.php @@ -28,11 +28,25 @@ class Adapter { /** * @param string $table name * - * @return int id of last insert statement + * @return int id of last insert statement, 0 in case there was no INSERT before or it failed to get the ID * @throws Exception */ - public function lastInsertId($table) { - return (int)$this->conn->realLastInsertId($table); + public function lastInsertId($table, bool $allowRetry = true): int { + $return = $this->conn->realLastInsertId($table); + if ($return === 0 && $allowRetry) { + /** + * During a reconnect we are losing the connection and when the + * realLastInsertId call is the one triggering the reconnect, it + * does not return the ID. But inside the reconnect, we were able + * to save the last insert id, so calling it a second time is going + * to be successful. + * We can not return the result on the initial call, as we are already + * way deeper in the stack performing the actual database query on + * the doctrine driver. + */ + return $this->lastInsertId($table, false); + } + return $return; } /** diff --git a/lib/private/DB/AdapterOCI8.php b/lib/private/DB/AdapterOCI8.php index 0a509090bca..f5ad9f7c934 100644 --- a/lib/private/DB/AdapterOCI8.php +++ b/lib/private/DB/AdapterOCI8.php @@ -8,7 +8,7 @@ namespace OC\DB; class AdapterOCI8 extends Adapter { - public function lastInsertId($table) { + public function lastInsertId($table, bool $allowRetry = true): int { if (is_null($table)) { throw new \InvalidArgumentException('Oracle requires a table name to be passed into lastInsertId()'); } diff --git a/lib/private/DB/AdapterPgSql.php b/lib/private/DB/AdapterPgSql.php index db48c81c2c5..b321fcf4715 100644 --- a/lib/private/DB/AdapterPgSql.php +++ b/lib/private/DB/AdapterPgSql.php @@ -9,7 +9,7 @@ namespace OC\DB; class AdapterPgSql extends Adapter { - public function lastInsertId($table) { + public function lastInsertId($table, bool $allowRetry = true): int { $result = $this->conn->executeQuery('SELECT lastval()'); $val = $result->fetchOne(); $result->free(); diff --git a/lib/private/DB/Connection.php b/lib/private/DB/Connection.php index 96dd578b2ef..4ba2d2a341d 100644 --- a/lib/private/DB/Connection.php +++ b/lib/private/DB/Connection.php @@ -92,6 +92,8 @@ class Connection extends PrimaryReadReplicaConnection { protected ShardConnectionManager $shardConnectionManager; protected AutoIncrementHandler $autoIncrementHandler; protected bool $isShardingEnabled; + protected bool $disableReconnect = false; + protected int $lastInsertId = 0; public const SHARD_PRESETS = [ 'filecache' => [ @@ -510,9 +512,9 @@ class Connection extends PrimaryReadReplicaConnection { * because the underlying database may not even support the notion of AUTO_INCREMENT/IDENTITY * columns or sequences. * - * @param string $seqName Name of the sequence object from which the ID should be returned. + * @param ?string $name Name of the sequence object from which the ID should be returned. * - * @return int the last inserted ID. + * @return int the last inserted ID, 0 in case there was no INSERT before or it failed to get the ID * @throws Exception */ public function lastInsertId($name = null): int { @@ -526,8 +528,13 @@ class Connection extends PrimaryReadReplicaConnection { * @internal * @throws Exception */ - public function realLastInsertId($seqName = null) { - return parent::lastInsertId($seqName); + public function realLastInsertId($seqName = null): int { + if ($this->lastInsertId !== 0) { + $lastInsertId = $this->lastInsertId; + $this->lastInsertId = 0; + return $lastInsertId; + } + return (int)parent::lastInsertId($seqName); } /** @@ -896,11 +903,23 @@ class Connection extends PrimaryReadReplicaConnection { if ( !isset($this->lastConnectionCheck[$this->getConnectionName()]) || time() <= $this->lastConnectionCheck[$this->getConnectionName()] + 30 || - $this->isTransactionActive() + $this->isTransactionActive() || + $this->disableReconnect ) { return; } + if ($this->getDatabaseProvider() === IDBConnection::PLATFORM_MYSQL) { + /** + * Before reconnecting we save the lastInsertId, so that if the reconnect + * happens between the INSERT executeStatement() and the getLastInsertId call + * we are able to return the correct result after all. + */ + $this->disableReconnect = true; + $this->lastInsertId = (int)parent::lastInsertId(); + $this->disableReconnect = false; + } + try { $this->_conn->query($this->getDriver()->getDatabasePlatform()->getDummySelectSQL()); $this->lastConnectionCheck[$this->getConnectionName()] = time(); diff --git a/lib/private/Encryption/EncryptionEventListener.php b/lib/private/Encryption/EncryptionEventListener.php new file mode 100644 index 00000000000..59ac0dea932 --- /dev/null +++ b/lib/private/Encryption/EncryptionEventListener.php @@ -0,0 +1,93 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +namespace OC\Encryption; + +use OC\Files\SetupManager; +use OC\Files\View; +use OCA\Files_Trashbin\Events\NodeRestoredEvent; +use OCP\Encryption\IFile; +use OCP\EventDispatcher\Event; +use OCP\EventDispatcher\IEventDispatcher; +use OCP\EventDispatcher\IEventListener; +use OCP\Files\Events\Node\NodeRenamedEvent; +use OCP\IUser; +use OCP\IUserSession; +use OCP\Share\Events\ShareCreatedEvent; +use OCP\Share\Events\ShareDeletedEvent; +use Psr\Log\LoggerInterface; + +/** @template-implements IEventListener<NodeRenamedEvent|ShareCreatedEvent|ShareDeletedEvent|NodeRestoredEvent> */ +class EncryptionEventListener implements IEventListener { + private ?Update $updater = null; + + public function __construct( + private IUserSession $userSession, + private SetupManager $setupManager, + private Manager $encryptionManager, + ) { + } + + public static function register(IEventDispatcher $dispatcher): void { + $dispatcher->addServiceListener(NodeRenamedEvent::class, static::class); + $dispatcher->addServiceListener(ShareCreatedEvent::class, static::class); + $dispatcher->addServiceListener(ShareDeletedEvent::class, static::class); + $dispatcher->addServiceListener(NodeRestoredEvent::class, static::class); + } + + public function handle(Event $event): void { + if (!$this->encryptionManager->isEnabled()) { + return; + } + if ($event instanceof NodeRenamedEvent) { + $this->getUpdate()->postRename($event->getSource(), $event->getTarget()); + } elseif ($event instanceof ShareCreatedEvent) { + $this->getUpdate()->postShared($event->getShare()->getNode()); + } elseif ($event instanceof ShareDeletedEvent) { + // In case the unsharing happens in a background job, we don't have + // a session and we load instead the user from the UserManager + $owner = $event->getShare()->getNode()->getOwner(); + $this->getUpdate($owner)->postUnshared($event->getShare()->getNode()); + } elseif ($event instanceof NodeRestoredEvent) { + $this->getUpdate()->postRestore($event->getTarget()); + } + } + + private function getUpdate(?IUser $owner = null): Update { + if (is_null($this->updater)) { + $user = $this->userSession->getUser(); + if (!$user && ($owner !== null)) { + $user = $owner; + } + if (!$user) { + throw new \Exception('Inconsistent data, File unshared, but owner not found. Should not happen'); + } + + $uid = $user->getUID(); + + if (!$this->setupManager->isSetupComplete($user)) { + $this->setupManager->setupForUser($user); + } + + $this->updater = new Update( + new Util( + new View(), + \OC::$server->getUserManager(), + \OC::$server->getGroupManager(), + \OC::$server->getConfig()), + \OC::$server->getEncryptionManager(), + \OC::$server->get(IFile::class), + \OC::$server->get(LoggerInterface::class), + $uid + ); + } + + return $this->updater; + } +} diff --git a/lib/private/Encryption/EncryptionWrapper.php b/lib/private/Encryption/EncryptionWrapper.php index 7f355b603d6..b9db9616538 100644 --- a/lib/private/Encryption/EncryptionWrapper.php +++ b/lib/private/Encryption/EncryptionWrapper.php @@ -75,15 +75,6 @@ class EncryptionWrapper { \OC::$server->getGroupManager(), \OC::$server->getConfig() ); - $update = new Update( - new View(), - $util, - Filesystem::getMountManager(), - $this->manager, - $fileHelper, - $this->logger, - $uid - ); return new Encryption( $parameters, $this->manager, @@ -92,7 +83,6 @@ class EncryptionWrapper { $fileHelper, $uid, $keyStorage, - $update, $mountManager, $this->arrayCache ); diff --git a/lib/private/Encryption/HookManager.php b/lib/private/Encryption/HookManager.php deleted file mode 100644 index 39e7edabb95..00000000000 --- a/lib/private/Encryption/HookManager.php +++ /dev/null @@ -1,75 +0,0 @@ -<?php - -/** - * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors - * SPDX-FileCopyrightText: 2016 ownCloud, Inc. - * SPDX-License-Identifier: AGPL-3.0-only - */ -namespace OC\Encryption; - -use OC\Files\Filesystem; -use OC\Files\SetupManager; -use OC\Files\View; -use OCP\Encryption\IFile; -use Psr\Log\LoggerInterface; - -class HookManager { - private static ?Update $updater = null; - - public static function postShared($params): void { - self::getUpdate()->postShared($params); - } - public static function postUnshared($params): void { - // In case the unsharing happens in a background job, we don't have - // a session and we load instead the user from the UserManager - $path = Filesystem::getPath($params['fileSource']); - $owner = Filesystem::getOwner($path); - self::getUpdate($owner)->postUnshared($params); - } - - public static function postRename($params): void { - self::getUpdate()->postRename($params); - } - - public static function postRestore($params): void { - self::getUpdate()->postRestore($params); - } - - private static function getUpdate(?string $owner = null): Update { - if (is_null(self::$updater)) { - $user = \OC::$server->getUserSession()->getUser(); - if (!$user && $owner) { - $user = \OC::$server->getUserManager()->get($owner); - } - if (!$user) { - throw new \Exception('Inconsistent data, File unshared, but owner not found. Should not happen'); - } - - $uid = ''; - if ($user) { - $uid = $user->getUID(); - } - - $setupManager = \OC::$server->get(SetupManager::class); - if (!$setupManager->isSetupComplete($user)) { - $setupManager->setupForUser($user); - } - - self::$updater = new Update( - new View(), - new Util( - new View(), - \OC::$server->getUserManager(), - \OC::$server->getGroupManager(), - \OC::$server->getConfig()), - Filesystem::getMountManager(), - \OC::$server->getEncryptionManager(), - \OC::$server->get(IFile::class), - \OC::$server->get(LoggerInterface::class), - $uid - ); - } - - return self::$updater; - } -} diff --git a/lib/private/Encryption/Update.php b/lib/private/Encryption/Update.php index 0b27d63c19a..293a1ce653c 100644 --- a/lib/private/Encryption/Update.php +++ b/lib/private/Encryption/Update.php @@ -1,146 +1,85 @@ <?php +declare(strict_types=1); + /** * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors * SPDX-FileCopyrightText: 2016 ownCloud, Inc. * SPDX-License-Identifier: AGPL-3.0-only */ + namespace OC\Encryption; use InvalidArgumentException; -use OC\Files\Filesystem; -use OC\Files\Mount; use OC\Files\View; use OCP\Encryption\Exceptions\GenericEncryptionException; +use OCP\Files\File as OCPFile; +use OCP\Files\Folder; +use OCP\Files\NotFoundException; use Psr\Log\LoggerInterface; /** * update encrypted files, e.g. because a file was shared */ class Update { - /** @var View */ - protected $view; - - /** @var Util */ - protected $util; - - /** @var \OC\Files\Mount\Manager */ - protected $mountManager; - - /** @var Manager */ - protected $encryptionManager; - - /** @var string */ - protected $uid; - - /** @var File */ - protected $file; - - /** @var LoggerInterface */ - protected $logger; - - /** - * @param string $uid - */ public function __construct( - View $view, - Util $util, - Mount\Manager $mountManager, - Manager $encryptionManager, - File $file, - LoggerInterface $logger, - $uid, + protected Util $util, + protected Manager $encryptionManager, + protected File $file, + protected LoggerInterface $logger, + protected string $uid, ) { - $this->view = $view; - $this->util = $util; - $this->mountManager = $mountManager; - $this->encryptionManager = $encryptionManager; - $this->file = $file; - $this->logger = $logger; - $this->uid = $uid; } /** * hook after file was shared - * - * @param array $params */ - public function postShared($params) { - if ($this->encryptionManager->isEnabled()) { - if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') { - $path = Filesystem::getPath($params['fileSource']); - [$owner, $ownerPath] = $this->getOwnerPath($path); - $absPath = '/' . $owner . '/files/' . $ownerPath; - $this->update($absPath); - } - } + public function postShared(OCPFile|Folder $node): void { + $this->update($node); } /** * hook after file was unshared - * - * @param array $params */ - public function postUnshared($params) { - if ($this->encryptionManager->isEnabled()) { - if ($params['itemType'] === 'file' || $params['itemType'] === 'folder') { - $path = Filesystem::getPath($params['fileSource']); - [$owner, $ownerPath] = $this->getOwnerPath($path); - $absPath = '/' . $owner . '/files/' . $ownerPath; - $this->update($absPath); - } - } + public function postUnshared(OCPFile|Folder $node): void { + $this->update($node); } /** * inform encryption module that a file was restored from the trash bin, * e.g. to update the encryption keys - * - * @param array $params */ - public function postRestore($params) { - if ($this->encryptionManager->isEnabled()) { - $path = Filesystem::normalizePath('/' . $this->uid . '/files/' . $params['filePath']); - $this->update($path); - } + public function postRestore(OCPFile|Folder $node): void { + $this->update($node); } /** * inform encryption module that a file was renamed, * e.g. to update the encryption keys - * - * @param array $params */ - public function postRename($params) { - $source = $params['oldpath']; - $target = $params['newpath']; - if ( - $this->encryptionManager->isEnabled() && - dirname($source) !== dirname($target) - ) { - [$owner, $ownerPath] = $this->getOwnerPath($target); - $absPath = '/' . $owner . '/files/' . $ownerPath; - $this->update($absPath); + public function postRename(OCPFile|Folder $source, OCPFile|Folder $target): void { + if (dirname($source->getPath()) !== dirname($target->getPath())) { + $this->update($target); } } /** - * get owner and path relative to data/<owner>/files + * get owner and path relative to data/ * - * @param string $path path to file for current user - * @return array ['owner' => $owner, 'path' => $path] * @throws \InvalidArgumentException */ - protected function getOwnerPath($path) { - $info = Filesystem::getFileInfo($path); - $owner = Filesystem::getOwner($path); + protected function getOwnerPath(OCPFile|Folder $node): string { + $owner = $node->getOwner()?->getUID(); + if ($owner === null) { + throw new InvalidArgumentException('No owner found for ' . $node->getId()); + } $view = new View('/' . $owner . '/files'); - $path = $view->getPath($info->getId()); - if ($path === null) { - throw new InvalidArgumentException('No file found for ' . $info->getId()); + try { + $path = $view->getPath($node->getId()); + } catch (NotFoundException $e) { + throw new InvalidArgumentException('No file found for ' . $node->getId(), previous:$e); } - - return [$owner, $path]; + return '/' . $owner . '/files/' . $path; } /** @@ -149,7 +88,7 @@ class Update { * @param string $path relative to data/ * @throws Exceptions\ModuleDoesNotExistsException */ - public function update($path) { + public function update(OCPFile|Folder $node): void { $encryptionModule = $this->encryptionManager->getEncryptionModule(); // if the encryption module doesn't encrypt the files on a per-user basis @@ -158,15 +97,14 @@ class Update { return; } + $path = $this->getOwnerPath($node); // if a folder was shared, get a list of all (sub-)folders - if ($this->view->is_dir($path)) { + if ($node instanceof Folder) { $allFiles = $this->util->getAllFiles($path); } else { $allFiles = [$path]; } - - foreach ($allFiles as $file) { $usersSharing = $this->file->getAccessList($file); try { diff --git a/lib/private/Encryption/Util.php b/lib/private/Encryption/Util.php index 1fb08b15696..0566ab9a760 100644 --- a/lib/private/Encryption/Util.php +++ b/lib/private/Encryption/Util.php @@ -304,7 +304,7 @@ class Util { // detect user specific folders if ($this->userManager->userExists($root[1]) - && in_array($root[2], $this->excludedPaths)) { + && in_array($root[2] ?? '', $this->excludedPaths)) { return true; } } diff --git a/lib/private/Files/Cache/Cache.php b/lib/private/Files/Cache/Cache.php index 2c190720c23..cb160115851 100644 --- a/lib/private/Files/Cache/Cache.php +++ b/lib/private/Files/Cache/Cache.php @@ -663,7 +663,7 @@ class Cache implements ICache { $sourceData = $sourceCache->get($sourcePath); if (!$sourceData) { - throw new \Exception('Invalid source storage path: ' . $sourcePath); + throw new \Exception('Source path not found in cache: ' . $sourcePath); } $shardDefinition = $this->connection->getShardDefinition('filecache'); diff --git a/lib/private/Files/Cache/Wrapper/CacheJail.php b/lib/private/Files/Cache/Wrapper/CacheJail.php index 5c7bd4334f3..5bc4ee8529d 100644 --- a/lib/private/Files/Cache/Wrapper/CacheJail.php +++ b/lib/private/Files/Cache/Wrapper/CacheJail.php @@ -31,10 +31,13 @@ class CacheJail extends CacheWrapper { ) { parent::__construct($cache, $dependencies); - if ($cache instanceof CacheJail) { - $this->unjailedRoot = $cache->getSourcePath($root); - } else { - $this->unjailedRoot = $root; + $this->unjailedRoot = $root; + $parent = $cache; + while ($parent instanceof CacheWrapper) { + if ($parent instanceof CacheJail) { + $this->unjailedRoot = $parent->getSourcePath($this->unjailedRoot); + } + $parent = $parent->getCache(); } } @@ -50,7 +53,7 @@ class CacheJail extends CacheWrapper { * * @return string */ - protected function getGetUnjailedRoot() { + public function getGetUnjailedRoot() { return $this->unjailedRoot; } diff --git a/lib/private/Files/FilenameValidator.php b/lib/private/Files/FilenameValidator.php index b1979789ec8..57a62b0b219 100644 --- a/lib/private/Files/FilenameValidator.php +++ b/lib/private/Files/FilenameValidator.php @@ -228,6 +228,43 @@ class FilenameValidator implements IFilenameValidator { return false; } + public function sanitizeFilename(string $name, ?string $charReplacement = null): string { + $forbiddenCharacters = $this->getForbiddenCharacters(); + + if ($charReplacement === null) { + $charReplacement = array_diff([' ', '_', '-'], $forbiddenCharacters); + $charReplacement = reset($charReplacement) ?: ''; + } + if (mb_strlen($charReplacement) !== 1) { + throw new \InvalidArgumentException('No or invalid character replacement given'); + } + + $nameLowercase = mb_strtolower($name); + foreach ($this->getForbiddenExtensions() as $extension) { + if (str_ends_with($nameLowercase, $extension)) { + $name = substr($name, 0, strlen($name) - strlen($extension)); + } + } + + $basename = strlen($name) > 1 + ? substr($name, 0, strpos($name, '.', 1) ?: null) + : $name; + if (in_array(mb_strtolower($basename), $this->getForbiddenBasenames())) { + $name = str_replace($basename, $this->l10n->t('%1$s (renamed)', [$basename]), $name); + } + + if ($name === '') { + $name = $this->l10n->t('renamed file'); + } + + if (in_array(mb_strtolower($name), $this->getForbiddenFilenames())) { + $name = $this->l10n->t('%1$s (renamed)', [$name]); + } + + $name = str_replace($forbiddenCharacters, $charReplacement, $name); + return $name; + } + protected function checkForbiddenName(string $filename): void { $filename = mb_strtolower($filename); if ($this->isForbidden($filename)) { diff --git a/lib/private/Files/SimpleFS/SimpleFile.php b/lib/private/Files/SimpleFS/SimpleFile.php index cbb3af4db29..0bfaea21788 100644 --- a/lib/private/Files/SimpleFS/SimpleFile.php +++ b/lib/private/Files/SimpleFS/SimpleFile.php @@ -6,9 +6,11 @@ namespace OC\Files\SimpleFS; use OCP\Files\File; +use OCP\Files\GenericFileException; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; use OCP\Files\SimpleFS\ISimpleFile; +use OCP\Lock\LockedException; class SimpleFile implements ISimpleFile { private File $file; @@ -48,8 +50,10 @@ class SimpleFile implements ISimpleFile { /** * Get the content * - * @throws NotPermittedException + * @throws GenericFileException + * @throws LockedException * @throws NotFoundException + * @throws NotPermittedException */ public function getContent(): string { $result = $this->file->getContent(); @@ -65,8 +69,10 @@ class SimpleFile implements ISimpleFile { * Overwrite the file * * @param string|resource $data - * @throws NotPermittedException + * @throws GenericFileException + * @throws LockedException * @throws NotFoundException + * @throws NotPermittedException */ public function putContent($data): void { try { diff --git a/lib/private/Files/Storage/Temporary.php b/lib/private/Files/Storage/Temporary.php index ff7a816930d..ecf8a1315a9 100644 --- a/lib/private/Files/Storage/Temporary.php +++ b/lib/private/Files/Storage/Temporary.php @@ -7,16 +7,20 @@ */ namespace OC\Files\Storage; +use OCP\Files; +use OCP\ITempManager; +use OCP\Server; + /** * local storage backend in temporary folder for testing purpose */ class Temporary extends Local { public function __construct(array $parameters = []) { - parent::__construct(['datadir' => \OC::$server->getTempManager()->getTemporaryFolder()]); + parent::__construct(['datadir' => Server::get(ITempManager::class)->getTemporaryFolder()]); } public function cleanUp(): void { - \OC_Helper::rmdirr($this->datadir); + Files::rmdirr($this->datadir); } public function __destruct() { diff --git a/lib/private/Files/Storage/Wrapper/Encryption.php b/lib/private/Files/Storage/Wrapper/Encryption.php index bdaba57687a..f4ab4754cff 100644 --- a/lib/private/Files/Storage/Wrapper/Encryption.php +++ b/lib/private/Files/Storage/Wrapper/Encryption.php @@ -8,7 +8,6 @@ namespace OC\Files\Storage\Wrapper; use OC\Encryption\Exceptions\ModuleDoesNotExistsException; -use OC\Encryption\Update; use OC\Encryption\Util; use OC\Files\Cache\CacheEntry; use OC\Files\Filesystem; @@ -50,7 +49,6 @@ class Encryption extends Wrapper { private IFile $fileHelper, private ?string $uid, private IStorage $keyStorage, - private Update $update, private Manager $mountManager, private ArrayCache $arrayCache, ) { @@ -65,7 +63,8 @@ class Encryption extends Wrapper { $info = $this->getCache()->get($path); if ($info === false) { - return false; + /* Pass call to wrapped storage, it may be a special file like a part file */ + return $this->storage->filesize($path); } if (isset($this->unencryptedSize[$fullPath])) { $size = $this->unencryptedSize[$fullPath]; @@ -319,7 +318,7 @@ class Encryption extends Wrapper { if (!empty($encryptionModuleId)) { $encryptionModule = $this->encryptionManager->getEncryptionModule($encryptionModuleId); $shouldEncrypt = true; - } elseif (empty($encryptionModuleId) && $info['encrypted'] === true) { + } elseif ($info !== false && $info['encrypted'] === true) { // we come from a old installation. No header and/or no module defined // but the file is encrypted. In this case we need to use the // OC_DEFAULT_MODULE to read the file @@ -537,10 +536,22 @@ class Encryption extends Wrapper { $result = $this->copyBetweenStorage($sourceStorage, $sourceInternalPath, $targetInternalPath, $preserveMtime, true); if ($result) { - if ($sourceStorage->is_dir($sourceInternalPath)) { - $result = $sourceStorage->rmdir($sourceInternalPath); - } else { - $result = $sourceStorage->unlink($sourceInternalPath); + $setPreserveCacheOnDelete = $sourceStorage->instanceOfStorage(ObjectStoreStorage::class) && !$this->instanceOfStorage(ObjectStoreStorage::class); + if ($setPreserveCacheOnDelete) { + /** @var ObjectStoreStorage $sourceStorage */ + $sourceStorage->setPreserveCacheOnDelete(true); + } + try { + if ($sourceStorage->is_dir($sourceInternalPath)) { + $result = $sourceStorage->rmdir($sourceInternalPath); + } else { + $result = $sourceStorage->unlink($sourceInternalPath); + } + } finally { + if ($setPreserveCacheOnDelete) { + /** @var ObjectStoreStorage $sourceStorage */ + $sourceStorage->setPreserveCacheOnDelete(false); + } } } return $result; @@ -665,7 +676,7 @@ class Encryption extends Wrapper { if (is_resource($dh)) { while ($result && ($file = readdir($dh)) !== false) { if (!Filesystem::isIgnoredDir($file)) { - $result &= $this->copyFromStorage($sourceStorage, $sourceInternalPath . '/' . $file, $targetInternalPath . '/' . $file, false, $isRename); + $result = $this->copyFromStorage($sourceStorage, $sourceInternalPath . '/' . $file, $targetInternalPath . '/' . $file, $preserveMtime, $isRename); } } } diff --git a/lib/private/Files/Type/Detection.php b/lib/private/Files/Type/Detection.php index 42315247dbf..d5810a90868 100644 --- a/lib/private/Files/Type/Detection.php +++ b/lib/private/Files/Type/Detection.php @@ -9,6 +9,7 @@ declare(strict_types=1); namespace OC\Files\Type; use OCP\Files\IMimeTypeDetector; +use OCP\IBinaryFinder; use OCP\ITempManager; use OCP\IURLGenerator; use Psr\Log\LoggerInterface; @@ -23,6 +24,7 @@ use Psr\Log\LoggerInterface; class Detection implements IMimeTypeDetector { private const CUSTOM_MIMETYPEMAPPING = 'mimetypemapping.json'; private const CUSTOM_MIMETYPEALIASES = 'mimetypealiases.json'; + private const CUSTOM_MIMETYPENAMES = 'mimetypenames.json'; /** @var array<list{string, string|null}> */ protected array $mimeTypes = []; @@ -31,6 +33,8 @@ class Detection implements IMimeTypeDetector { protected array $mimeTypeIcons = []; /** @var array<string,string> */ protected array $mimeTypeAlias = []; + /** @var array<string,string> */ + protected array $mimeTypesNames = []; public function __construct( private IURLGenerator $urlGenerator, @@ -148,6 +152,25 @@ class Detection implements IMimeTypeDetector { return $this->mimeTypes; } + private function loadNamings(): void { + if (!empty($this->mimeTypesNames)) { + return; + } + + $mimeTypeMapping = json_decode(file_get_contents($this->defaultConfigDir . '/mimetypenames.dist.json'), true); + $mimeTypeMapping = $this->loadCustomDefinitions(self::CUSTOM_MIMETYPENAMES, $mimeTypeMapping); + + $this->mimeTypesNames = $mimeTypeMapping; + } + + /** + * @return array<string,string> + */ + public function getAllNamings(): array { + $this->loadNamings(); + return $this->mimeTypesNames; + } + /** * detect MIME type only based on filename, content of file is not used * @@ -225,11 +248,13 @@ class Detection implements IMimeTypeDetector { } } - if (\OC_Helper::canExecute('file')) { + $binaryFinder = \OCP\Server::get(IBinaryFinder::class); + $program = $binaryFinder->findBinaryPath('file'); + if ($program !== false) { // it looks like we have a 'file' command, // lets see if it does have mime support $path = escapeshellarg($path); - $fp = popen("test -f $path && file -b --mime-type $path", 'r'); + $fp = popen("test -f $path && $program -b --mime-type $path", 'r'); if ($fp !== false) { $mimeType = fgets($fp); pclose($fp); diff --git a/lib/private/Installer.php b/lib/private/Installer.php index 00fdd84c1bc..a1d27c9255b 100644 --- a/lib/private/Installer.php +++ b/lib/private/Installer.php @@ -19,6 +19,7 @@ use OC\DB\MigrationService; use OC_App; use OC_Helper; use OCP\App\IAppManager; +use OCP\Files; use OCP\HintException; use OCP\Http\Client\IClientService; use OCP\IConfig; @@ -324,14 +325,14 @@ class Installer { $baseDir = OC_App::getInstallPath() . '/' . $appId; // Remove old app with the ID if existent - OC_Helper::rmdirr($baseDir); + Files::rmdirr($baseDir); // Move to app folder if (@mkdir($baseDir)) { $extractDir .= '/' . $folders[0]; OC_Helper::copyr($extractDir, $baseDir); } OC_Helper::copyr($extractDir, $baseDir); - OC_Helper::rmdirr($extractDir); + Files::rmdirr($extractDir); return; } // Signature does not match @@ -450,7 +451,7 @@ class Installer { return false; } $appDir = OC_App::getInstallPath() . '/' . $appId; - OC_Helper::rmdirr($appDir); + Files::rmdirr($appDir); return true; } else { $this->logger->error('can\'t remove app ' . $appId . '. It is not installed.'); diff --git a/lib/private/IntegrityCheck/Checker.php b/lib/private/IntegrityCheck/Checker.php index 361fe8e9b2d..2bd6e426b79 100644 --- a/lib/private/IntegrityCheck/Checker.php +++ b/lib/private/IntegrityCheck/Checker.php @@ -148,10 +148,10 @@ class Checker { } if ($filename === $this->environmentHelper->getServerRoot() . '/core/js/mimetypelist.js') { $oldMimetypeList = new GenerateMimetypeFileBuilder(); - $newFile = $oldMimetypeList->generateFile($this->mimeTypeDetector->getAllAliases()); + $newFile = $oldMimetypeList->generateFile($this->mimeTypeDetector->getAllAliases(), $this->mimeTypeDetector->getAllNamings()); $oldFile = $this->fileAccessHelper->file_get_contents($filename); if ($newFile === $oldFile) { - $hashes[$relativeFileName] = hash('sha512', $oldMimetypeList->generateFile($this->mimeTypeDetector->getOnlyDefaultAliases())); + $hashes[$relativeFileName] = hash('sha512', $oldMimetypeList->generateFile($this->mimeTypeDetector->getOnlyDefaultAliases(), $this->mimeTypeDetector->getAllNamings())); continue; } } diff --git a/lib/private/Log/LogDetails.php b/lib/private/Log/LogDetails.php index b3ae23a3770..8c1efaea20d 100644 --- a/lib/private/Log/LogDetails.php +++ b/lib/private/Log/LogDetails.php @@ -59,6 +59,10 @@ abstract class LogDetails { 'userAgent', 'version' ); + $clientReqId = $request->getHeader('X-Request-Id'); + if ($clientReqId !== '') { + $entry['clientReqId'] = $clientReqId; + } if (is_array($message)) { // Exception messages are extracted and the exception is put into a separate field diff --git a/lib/private/Repair/MoveUpdaterStepFile.php b/lib/private/Repair/MoveUpdaterStepFile.php index c9b51b308c4..eb9f78b0a39 100644 --- a/lib/private/Repair/MoveUpdaterStepFile.php +++ b/lib/private/Repair/MoveUpdaterStepFile.php @@ -5,6 +5,7 @@ */ namespace OC\Repair; +use OCP\Files; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; @@ -40,7 +41,7 @@ class MoveUpdaterStepFile implements IRepairStep { // cleanup if (file_exists($previousStepFile)) { - if (\OC_Helper::rmdirr($previousStepFile)) { + if (Files::rmdirr($previousStepFile)) { $output->info('.step-previous-update removed'); } else { $output->info('.step-previous-update can\'t be removed - abort move of .step file'); diff --git a/lib/private/Route/Router.php b/lib/private/Route/Router.php index d073132516d..376852a1b6e 100644 --- a/lib/private/Route/Router.php +++ b/lib/private/Route/Router.php @@ -54,9 +54,9 @@ class Router implements IRouter { protected LoggerInterface $logger, IRequest $request, private IConfig $config, - private IEventLogger $eventLogger, + protected IEventLogger $eventLogger, private ContainerInterface $container, - private IAppManager $appManager, + protected IAppManager $appManager, ) { $baseUrl = \OC::$WEBROOT; if (!($config->getSystemValue('htaccess.IgnoreFrontController', false) === true || getenv('front_controller_active') === 'true')) { @@ -116,9 +116,11 @@ class Router implements IRouter { $this->loaded = true; $routingFiles = $this->getRoutingFiles(); + $this->eventLogger->start('route:load:attributes', 'Loading Routes from attributes'); foreach (\OC_App::getEnabledApps() as $enabledApp) { $this->loadAttributeRoutes($enabledApp); } + $this->eventLogger->end('route:load:attributes'); } else { if (isset($this->loadedApps[$app])) { return; @@ -140,6 +142,7 @@ class Router implements IRouter { } } + $this->eventLogger->start('route:load:files', 'Loading Routes from files'); foreach ($routingFiles as $app => $file) { if (!isset($this->loadedApps[$app])) { if (!$this->appManager->isAppLoaded($app)) { @@ -160,12 +163,13 @@ class Router implements IRouter { $this->root->addCollection($collection); } } + $this->eventLogger->end('route:load:files'); if (!isset($this->loadedApps['core'])) { $this->loadedApps['core'] = true; $this->useCollection('root'); $this->setupRoutes($this->getAttributeRoutes('core'), 'core'); - require __DIR__ . '/../../../core/routes.php'; + $this->requireRouteFile(__DIR__ . '/../../../core/routes.php', 'core'); // Also add the OCS collection $collection = $this->getCollection('root.ocs'); @@ -265,6 +269,7 @@ class Router implements IRouter { $this->loadRoutes(); } + $this->eventLogger->start('route:url:match', 'Symfony url matcher call'); $matcher = new UrlMatcher($this->root, $this->context); try { $parameters = $matcher->match($url); @@ -283,6 +288,7 @@ class Router implements IRouter { throw $e; } } + $this->eventLogger->end('route:url:match'); $this->eventLogger->end('route:match'); return $parameters; diff --git a/lib/private/Security/RateLimiting/Limiter.php b/lib/private/Security/RateLimiting/Limiter.php index b7ac26d9132..316becfa009 100644 --- a/lib/private/Security/RateLimiting/Limiter.php +++ b/lib/private/Security/RateLimiting/Limiter.php @@ -13,10 +13,12 @@ use OC\Security\RateLimiting\Backend\IBackend; use OC\Security\RateLimiting\Exception\RateLimitExceededException; use OCP\IUser; use OCP\Security\RateLimiting\ILimiter; +use Psr\Log\LoggerInterface; class Limiter implements ILimiter { public function __construct( private IBackend $backend, + private LoggerInterface $logger, ) { } @@ -32,6 +34,11 @@ class Limiter implements ILimiter { ): void { $existingAttempts = $this->backend->getAttempts($methodIdentifier, $userIdentifier); if ($existingAttempts >= $limit) { + $this->logger->info('Request blocked because it exceeds the rate limit [method: {method}, limit: {limit}, period: {period}]', [ + 'method' => $methodIdentifier, + 'limit' => $limit, + 'period' => $period, + ]); throw new RateLimitExceededException(); } diff --git a/lib/private/ServerContainer.php b/lib/private/ServerContainer.php index 9f887b2d48a..b5bcbdaeb6f 100644 --- a/lib/private/ServerContainer.php +++ b/lib/private/ServerContainer.php @@ -128,18 +128,17 @@ class ServerContainer extends SimpleContainer { } catch (QueryException $e) { // Continue with general autoloading then } - } - - // In case the service starts with OCA\ we try to find the service in - // the apps container first. - if (($appContainer = $this->getAppContainerForService($name)) !== null) { - try { - return $appContainer->queryNoFallback($name); - } catch (QueryException $e) { - // Didn't find the service or the respective app container - // In this case the service won't be part of the core container, - // so we can throw directly - throw $e; + // In case the service starts with OCA\ we try to find the service in + // the apps container first. + if (($appContainer = $this->getAppContainerForService($name)) !== null) { + try { + return $appContainer->queryNoFallback($name); + } catch (QueryException $e) { + // Didn't find the service or the respective app container + // In this case the service won't be part of the core container, + // so we can throw directly + throw $e; + } } } elseif (str_starts_with($name, 'OC\\Settings\\') && substr_count($name, '\\') >= 3) { $segments = explode('\\', $name); diff --git a/lib/private/Support/CrashReport/Registry.php b/lib/private/Support/CrashReport/Registry.php index 93969a81265..77dd8163174 100644 --- a/lib/private/Support/CrashReport/Registry.php +++ b/lib/private/Support/CrashReport/Registry.php @@ -110,6 +110,7 @@ class Registry implements IRegistry { \OC::$server->get(LoggerInterface::class)->critical('Could not load lazy crash reporter: ' . $e->getMessage(), [ 'exception' => $e, ]); + return; } /** * Try to register the loaded reporter. Theoretically it could be of a wrong diff --git a/lib/private/TempManager.php b/lib/private/TempManager.php index b7dccad3f95..4c0ffcf43d7 100644 --- a/lib/private/TempManager.php +++ b/lib/private/TempManager.php @@ -8,6 +8,7 @@ namespace OC; use bantu\IniGetWrapper\IniGetWrapper; +use OCP\Files; use OCP\IConfig; use OCP\ITempManager; use OCP\Security\ISecureRandom; @@ -99,7 +100,7 @@ class TempManager implements ITempManager { foreach ($files as $file) { if (file_exists($file)) { try { - \OC_Helper::rmdirr($file); + Files::rmdirr($file); } catch (\UnexpectedValueException $ex) { $this->log->warning( 'Error deleting temporary file/folder: {file} - Reason: {error}', diff --git a/lib/private/User/User.php b/lib/private/User/User.php index f04977314e2..8e01a15695c 100644 --- a/lib/private/User/User.php +++ b/lib/private/User/User.php @@ -11,7 +11,6 @@ use InvalidArgumentException; use OC\Accounts\AccountManager; use OC\Avatar\AvatarManager; use OC\Hooks\Emitter; -use OC_Helper; use OCP\Accounts\IAccountManager; use OCP\Comments\ICommentsManager; use OCP\EventDispatcher\IEventDispatcher; @@ -570,11 +569,11 @@ class User implements IUser { public function setQuota($quota) { $oldQuota = $this->config->getUserValue($this->uid, 'files', 'quota', ''); if ($quota !== 'none' and $quota !== 'default') { - $bytesQuota = OC_Helper::computerFileSize($quota); + $bytesQuota = \OCP\Util::computerFileSize($quota); if ($bytesQuota === false) { throw new InvalidArgumentException('Failed to set quota to invalid value ' . $quota); } - $quota = OC_Helper::humanFileSize($bytesQuota); + $quota = \OCP\Util::humanFileSize($bytesQuota); } if ($quota !== $oldQuota) { $this->config->setUserValue($this->uid, 'files', 'quota', $quota); diff --git a/lib/private/legacy/OC_App.php b/lib/private/legacy/OC_App.php index ecceafa65b3..abac0d2635e 100644 --- a/lib/private/legacy/OC_App.php +++ b/lib/private/legacy/OC_App.php @@ -768,28 +768,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\Server::get(LoggerInterface::class)->error('Can\'t get app storage, app ' . $appId . ', user not logged in', ['app' => 'core']); - return false; - } - } else { - \OCP\Server::get(LoggerInterface::class)->error('Can\'t get app storage, app ' . $appId . ' not enabled', ['app' => 'core']); - return false; - } - } - protected static function findBestL10NOption(array $options, string $lang): string { // only a single option if (isset($options['@value'])) { diff --git a/lib/private/legacy/OC_Helper.php b/lib/private/legacy/OC_Helper.php index a89cbe1bb3a..172c865364c 100644 --- a/lib/private/legacy/OC_Helper.php +++ b/lib/private/legacy/OC_Helper.php @@ -39,75 +39,26 @@ class OC_Helper { * Make a human file size * @param int|float $bytes file size in bytes * @return string a human readable file size + * @deprecated 4.0.0 replaced with \OCP\Util::humanFileSize * * Makes 2048 to 2 kB. */ public static function humanFileSize(int|float $bytes): string { - 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"; + return \OCP\Util::humanFileSize($bytes); } /** * Make a computer file size * @param string $str file size in human readable format * @return false|int|float a file size in bytes + * @deprecated 4.0.0 Use \OCP\Util::computerFileSize * * Makes 2kB to 2048. * * Inspired by: https://www.php.net/manual/en/function.filesize.php#92418 */ public static function computerFileSize(string $str): false|int|float { - $str = strtolower($str); - if (is_numeric($str)) { - return Util::numericToNumber($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) && isset($bytes_array[$matches[1]])) { - $bytes *= $bytes_array[$matches[1]]; - } else { - return false; - } - - return Util::numericToNumber(round($bytes)); + return \OCP\Util::computerFileSize($str); } /** @@ -144,37 +95,10 @@ class OC_Helper { * @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 + * @deprecated 5.0.0 use \OCP\Files::rmdirr instead */ 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); - } - } - if (!$deleteSelf) { - return true; - } - - return !file_exists($dir); + return \OCP\Files::rmdirr($dir, $deleteSelf); } /** @@ -196,6 +120,7 @@ class OC_Helper { * @internal param string $program name * @internal param string $optional search path, defaults to $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 diff --git a/lib/private/legacy/OC_Util.php b/lib/private/legacy/OC_Util.php index 580fec7b5b3..895cfba35c5 100644 --- a/lib/private/legacy/OC_Util.php +++ b/lib/private/legacy/OC_Util.php @@ -107,7 +107,7 @@ class OC_Util { if ($userQuota === 'none') { return \OCP\Files\FileInfo::SPACE_UNLIMITED; } - return OC_Helper::computerFileSize($userQuota); + return \OCP\Util::computerFileSize($userQuota); } /** diff --git a/lib/public/Accounts/IAccountManager.php b/lib/public/Accounts/IAccountManager.php index a15651eb5e6..92fc0002674 100644 --- a/lib/public/Accounts/IAccountManager.php +++ b/lib/public/Accounts/IAccountManager.php @@ -48,30 +48,6 @@ interface IAccountManager { public const SCOPE_PUBLISHED = 'v2-published'; /** - * Contact details only visible locally - * - * @since 15.0.0 - * @deprecated 21.0.1 - */ - public const VISIBILITY_PRIVATE = 'private'; - - /** - * Contact details visible on trusted federated servers. - * - * @since 15.0.0 - * @deprecated 21.0.1 - */ - public const VISIBILITY_CONTACTS_ONLY = 'contacts'; - - /** - * Contact details visible on trusted federated servers and in the public lookup server. - * - * @since 15.0.0 - * @deprecated 21.0.1 - */ - public const VISIBILITY_PUBLIC = 'public'; - - /** * The list of allowed scopes * * @since 25.0.0 @@ -81,9 +57,6 @@ interface IAccountManager { self::SCOPE_LOCAL, self::SCOPE_FEDERATED, self::SCOPE_PUBLISHED, - self::VISIBILITY_PRIVATE, - self::VISIBILITY_CONTACTS_ONLY, - self::VISIBILITY_PUBLIC, ]; /** @@ -98,6 +71,7 @@ interface IAccountManager { /** * @since 27.0.0 + * @deprecated 27.0.0 only added for backwards compatibility with provisioning_api UsersController::getCurrentUser */ public const PROPERTY_DISPLAYNAME_LEGACY = 'display-name'; diff --git a/lib/public/AppFramework/ApiController.php b/lib/public/AppFramework/ApiController.php index dae80456e26..729582c8505 100644 --- a/lib/public/AppFramework/ApiController.php +++ b/lib/public/AppFramework/ApiController.php @@ -58,9 +58,8 @@ abstract class ApiController extends Controller { #[PublicPage] #[NoAdminRequired] public function preflightedCors() { - if (isset($this->request->server['HTTP_ORIGIN'])) { - $origin = $this->request->server['HTTP_ORIGIN']; - } else { + $origin = $this->request->getHeader('origin'); + if ($origin === '') { $origin = '*'; } diff --git a/lib/public/AppFramework/App.php b/lib/public/AppFramework/App.php index 06404baea70..6860de7c324 100644 --- a/lib/public/AppFramework/App.php +++ b/lib/public/AppFramework/App.php @@ -9,10 +9,9 @@ declare(strict_types=1); */ namespace OCP\AppFramework; -use OC\AppFramework\Routing\RouteConfig; -use OC\Route\Router; use OC\ServerContainer; -use OCP\Route\IRouter; +use OCP\IConfig; +use OCP\Server; use Psr\Log\LoggerInterface; /** @@ -47,7 +46,7 @@ class App { * @since 6.0.0 */ public function __construct(string $appName, array $urlParams = []) { - $runIsSetupDirectly = \OC::$server->getConfig()->getSystemValueBool('debug') + $runIsSetupDirectly = Server::get(IConfig::class)->getSystemValueBool('debug') && !ini_get('zend.exception_ignore_args'); if ($runIsSetupDirectly) { @@ -74,7 +73,7 @@ class App { } if (!$setUpViaQuery && $applicationClassName !== \OCP\AppFramework\App::class) { - \OCP\Server::get(LoggerInterface::class)->error($e->getMessage(), [ + Server::get(LoggerInterface::class)->error($e->getMessage(), [ 'app' => $appName, 'exception' => $e, ]); @@ -97,35 +96,6 @@ class App { } /** - * This function is to be called to create single routes and restful routes based on the given $routes array. - * - * Example code in routes.php of tasks app (it will register two restful resources): - * $routes = array( - * 'resources' => array( - * 'lists' => array('url' => '/tasklists'), - * 'tasks' => array('url' => '/tasklists/{listId}/tasks') - * ) - * ); - * - * $a = new TasksApp(); - * $a->registerRoutes($this, $routes); - * - * @param \OCP\Route\IRouter $router - * @param array $routes - * @since 6.0.0 - * @suppress PhanAccessMethodInternal - * @deprecated 20.0.0 Just return an array from your routes.php - */ - public function registerRoutes(IRouter $router, array $routes) { - if (!($router instanceof Router)) { - throw new \RuntimeException('Can only setup routes with real router'); - } - - $routeConfig = new RouteConfig($this->container, $router, $routes); - $routeConfig->register(); - } - - /** * This function is called by the routing component to fire up the frameworks dispatch mechanism. * * Example code in routes.php of the task app: diff --git a/lib/public/AppFramework/Http/RedirectToDefaultAppResponse.php b/lib/public/AppFramework/Http/RedirectToDefaultAppResponse.php index 1681b39ce50..0a0c04f671d 100644 --- a/lib/public/AppFramework/Http/RedirectToDefaultAppResponse.php +++ b/lib/public/AppFramework/Http/RedirectToDefaultAppResponse.php @@ -30,8 +30,7 @@ class RedirectToDefaultAppResponse extends RedirectResponse { * @deprecated 23.0.0 Use RedirectResponse() with IURLGenerator::linkToDefaultPageUrl() instead */ public function __construct(int $status = Http::STATUS_SEE_OTHER, array $headers = []) { - /** @var IURLGenerator $urlGenerator */ - $urlGenerator = \OC::$server->get(IURLGenerator::class); + $urlGenerator = \OCP\Server::get(IURLGenerator::class); parent::__construct($urlGenerator->linkToDefaultPageUrl(), $status, $headers); } } diff --git a/lib/public/AppFramework/Http/Response.php b/lib/public/AppFramework/Http/Response.php index 6fc3d4b98ea..8037243d7a4 100644 --- a/lib/public/AppFramework/Http/Response.php +++ b/lib/public/AppFramework/Http/Response.php @@ -93,7 +93,6 @@ class Response { // Set expires header $expires = new \DateTime(); - /** @var ITimeFactory $time */ $time = \OCP\Server::get(ITimeFactory::class); $expires->setTimestamp($time->getTime()); $expires->add(new \DateInterval('PT' . $cacheSeconds . 'S')); @@ -184,10 +183,10 @@ class Response { if ($this->status === Http::STATUS_NOT_MODIFIED && stripos($name, 'x-') === 0) { /** @var IConfig $config */ - $config = \OC::$server->get(IConfig::class); + $config = \OCP\Server::get(IConfig::class); if ($config->getSystemValueBool('debug', false)) { - \OC::$server->get(LoggerInterface::class)->error('Setting custom header on a 304 is not supported (Header: {header})', [ + \OCP\Server::get(LoggerInterface::class)->error('Setting custom header on a 304 is not supported (Header: {header})', [ 'header' => $name, ]); } @@ -229,7 +228,7 @@ class Response { /** * @psalm-suppress UndefinedClass */ - $request = \OC::$server->get(IRequest::class); + $request = \OCP\Server::get(IRequest::class); $mergeWith = [ 'X-Request-Id' => $request->getId(), 'Cache-Control' => 'no-cache, no-store, must-revalidate', diff --git a/lib/public/Defaults.php b/lib/public/Defaults.php index 9242a230024..6de22caa41e 100644 --- a/lib/public/Defaults.php +++ b/lib/public/Defaults.php @@ -29,7 +29,7 @@ class Defaults { */ public function __construct(?\OC_Defaults $defaults = null) { if ($defaults === null) { - $defaults = \OC::$server->get('ThemingDefaults'); + $defaults = \OCP\Server::get('ThemingDefaults'); } $this->defaults = $defaults; } diff --git a/lib/public/Diagnostics/IQueryLogger.php b/lib/public/Diagnostics/IQueryLogger.php index 1973168803d..07c999023da 100644 --- a/lib/public/Diagnostics/IQueryLogger.php +++ b/lib/public/Diagnostics/IQueryLogger.php @@ -31,7 +31,7 @@ interface IQueryLogger extends SQLLogger { * Mark the end of the current active query. Ending query should store \OCP\Diagnostics\IQuery to * be returned with getQueries() method. * - * @return mixed + * @return void * @since 8.0.0 */ public function stopQuery(); diff --git a/lib/public/EventDispatcher/GenericEvent.php b/lib/public/EventDispatcher/GenericEvent.php index fb0a7677672..7e646c4d6a7 100644 --- a/lib/public/EventDispatcher/GenericEvent.php +++ b/lib/public/EventDispatcher/GenericEvent.php @@ -18,10 +18,12 @@ use function array_key_exists; /** * Class GenericEvent * - * convenience reimplementation of \Symfony\Component\GenericEvent against + * convenience re-implementation of \Symfony\Component\GenericEvent against * \OCP\EventDispatcher\Event * * @since 18.0.0 + * @template-implements ArrayAccess<array-key, mixed> + * @template-implements IteratorAggregate<array-key, mixed> * @deprecated 22.0.0 use \OCP\EventDispatcher\Event */ class GenericEvent extends Event implements ArrayAccess, IteratorAggregate { diff --git a/lib/public/Files.php b/lib/public/Files.php index 62c41c4ada1..b12aa463f1a 100644 --- a/lib/public/Files.php +++ b/lib/public/Files.php @@ -9,6 +9,8 @@ namespace OCP; +use OCP\Files\IMimeTypeDetector; + /** * This class provides access to the internal filesystem abstraction layer. Use * this class exclusively if you want to access files @@ -18,12 +20,44 @@ namespace OCP; class Files { /** * 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 * @since 5.0.0 + * @since 32.0.0 added the $deleteSelf parameter * @deprecated 14.0.0 */ - public static function rmdirr($dir) { - return \OC_Helper::rmdirr($dir); + public static function rmdirr($dir, bool $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); + } + } + if (!$deleteSelf) { + return true; + } + + return !file_exists($dir); } /** @@ -35,7 +69,7 @@ class Files { * @deprecated 14.0.0 */ public static function getMimeType($path) { - return \OC::$server->getMimeTypeDetector()->detect($path); + return Server::get(IMimeTypeDetector::class)->detect($path); } /** @@ -73,16 +107,4 @@ class Files { public static function buildNotExistingFileName($path, $filename) { return \OC_Helper::buildNotExistingFileName($path, $filename); } - - /** - * Gets the Storage for an app - creates the needed folder if they are not - * existent - * @param string $app - * @return \OC\Files\View - * @since 5.0.0 - * @deprecated 14.0.0 use IAppData instead - */ - public static function getStorage($app) { - return \OC_App::getStorage($app); - } } diff --git a/lib/public/Files/IFilenameValidator.php b/lib/public/Files/IFilenameValidator.php index 2bd3bb945dc..d8bd06d179d 100644 --- a/lib/public/Files/IFilenameValidator.php +++ b/lib/public/Files/IFilenameValidator.php @@ -36,4 +36,17 @@ interface IFilenameValidator { * @since 30.0.0 */ public function validateFilename(string $filename): void; + + /** + * Sanitize a give filename to comply with admin setup naming constrains. + * + * If no sanitizing is needed the same name is returned. + * + * @param string $name The filename to sanitize + * @param null|string $charReplacement Character to use for replacing forbidden ones - by default space, dash or underscore is used if allowed. + * @throws \InvalidArgumentException if no character replacement was given (and the default could not be applied) or the replacement is not valid. + * @since 32.0.0 + */ + public function sanitizeFilename(string $name, ?string $charReplacement = null): string; + } diff --git a/lib/public/Files/IMimeTypeDetector.php b/lib/public/Files/IMimeTypeDetector.php index 1bc9c514610..1e87cf932ce 100644 --- a/lib/public/Files/IMimeTypeDetector.php +++ b/lib/public/Files/IMimeTypeDetector.php @@ -87,4 +87,12 @@ interface IMimeTypeDetector { * @since 32.0.0 */ public function getAllMappings(): array; + + /** + * Get all human readable mime names + * + * @return array<string,string> + * @since 32.0.0 + */ + public function getAllNamings(): array; } diff --git a/lib/public/Files/SimpleFS/ISimpleFile.php b/lib/public/Files/SimpleFS/ISimpleFile.php index 2682c22580d..10cdc0a919d 100644 --- a/lib/public/Files/SimpleFS/ISimpleFile.php +++ b/lib/public/Files/SimpleFS/ISimpleFile.php @@ -5,8 +5,10 @@ */ namespace OCP\Files\SimpleFS; +use OCP\Files\GenericFileException; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; +use OCP\Lock\LockedException; /** * This interface allows to manage simple files. @@ -49,8 +51,10 @@ interface ISimpleFile { /** * Get the content * - * @throws NotPermittedException + * @throws GenericFileException + * @throws LockedException * @throws NotFoundException + * @throws NotPermittedException * @since 11.0.0 */ public function getContent(): string; @@ -59,8 +63,10 @@ interface ISimpleFile { * Overwrite the file * * @param string|resource $data - * @throws NotPermittedException + * @throws GenericFileException + * @throws LockedException * @throws NotFoundException + * @throws NotPermittedException * @since 11.0.0 */ public function putContent($data): void; diff --git a/lib/public/Files_FullTextSearch/Model/AFilesDocument.php b/lib/public/Files_FullTextSearch/Model/AFilesDocument.php index ba5f504fccf..297d6bc6ffc 100644 --- a/lib/public/Files_FullTextSearch/Model/AFilesDocument.php +++ b/lib/public/Files_FullTextSearch/Model/AFilesDocument.php @@ -16,7 +16,7 @@ use OCP\FullTextSearch\Model\IIndexDocument; * This is mostly used by 3rd party apps that want to complete the IIndexDocument * with more information about a file before its index: * - * \OC::$server->getEventDispatcher()->addListener( + * \OCP\Server::get(IEventDispatcher::class)->addListener( * '\OCA\Files_FullTextSearch::onFileIndexing', * function(GenericEvent $e) { * //@var \OCP\Files\Node $file diff --git a/lib/public/L10N/ILanguageIterator.php b/lib/public/L10N/ILanguageIterator.php index cba0feefdcf..27f850d4235 100644 --- a/lib/public/L10N/ILanguageIterator.php +++ b/lib/public/L10N/ILanguageIterator.php @@ -21,7 +21,7 @@ namespace OCP\L10N; * if settings are not present or truncating is not applicable, the iterator * skips to the next valid item itself * - * + * @template-extends \Iterator<int, string> * @since 14.0.0 */ interface ILanguageIterator extends \Iterator { @@ -36,22 +36,20 @@ interface ILanguageIterator extends \Iterator { * Move forward to next element * * @since 14.0.0 - * @return void */ - #[\ReturnTypeWillChange] - public function next(); + public function next(): void; /** * Return the key of the current element * * @since 14.0.0 */ - public function key():int; + public function key(): int; /** * Checks if current position is valid * * @since 14.0.0 */ - public function valid():bool; + public function valid(): bool; } diff --git a/lib/public/Mail/IMailer.php b/lib/public/Mail/IMailer.php index 18eaef541c0..277f7863184 100644 --- a/lib/public/Mail/IMailer.php +++ b/lib/public/Mail/IMailer.php @@ -14,7 +14,7 @@ namespace OCP\Mail; * * Example usage: * - * $mailer = \OC::$server->get(\OCP\Mail\IMailer::class); + * $mailer = \OCP\Server::get(\OCP\Mail\IMailer::class); * $message = $mailer->createMessage(); * $message->setSubject('Your Subject'); * $message->setFrom(['cloud@domain.org' => 'Nextcloud Notifier']); diff --git a/lib/public/Profiler/IProfile.php b/lib/public/Profiler/IProfile.php index ddbad4b4388..89eb709d061 100644 --- a/lib/public/Profiler/IProfile.php +++ b/lib/public/Profiler/IProfile.php @@ -17,7 +17,7 @@ use OCP\DataCollector\IDataCollector; * * ```php * <?php - * $profiler = \OC::$server->get(IProfiler::class); + * $profiler = \OCP\Server::get(IProfiler::class); * $profiles = $profiler->find('/settings/users', 10); * ``` * diff --git a/lib/public/Security/ICrypto.php b/lib/public/Security/ICrypto.php index c2ba4cc9c97..78b0fc14d6d 100644 --- a/lib/public/Security/ICrypto.php +++ b/lib/public/Security/ICrypto.php @@ -13,8 +13,8 @@ namespace OCP\Security; * it will use the secret defined in config.php as key. Additionally the message will be HMAC'd. * * Usage: - * $encryptWithDefaultPassword = \OC::$server->getCrypto()->encrypt('EncryptedText'); - * $encryptWithCustomPassword = \OC::$server->getCrypto()->encrypt('EncryptedText', 'password'); + * $encryptWithDefaultPassword = \OCP\Server::get(ICrypto::class)->encrypt('EncryptedText'); + * $encryptWithCustomPassword = \OCP\Server::get(ICrypto::class)->encrypt('EncryptedText', 'password'); * * @since 8.0.0 */ diff --git a/lib/public/Security/IHasher.php b/lib/public/Security/IHasher.php index d985ffe48ab..d0d6e4e9028 100644 --- a/lib/public/Security/IHasher.php +++ b/lib/public/Security/IHasher.php @@ -19,10 +19,10 @@ namespace OCP\Security; * * Usage: * // Hashing a message - * $hash = \OC::$server->get(\OCP\Security\IHasher::class)->hash('MessageToHash'); + * $hash = \OCP\Server::get(\OCP\Security\IHasher::class)->hash('MessageToHash'); * // Verifying a message - $newHash will contain the newly calculated hash * $newHash = null; - * var_dump(\OC::$server->get(\OCP\Security\IHasher::class)->verify('a', '86f7e437faa5a7fce15d1ddcb9eaeaea377667b8', $newHash)); + * var_dump(\OCP\Server::get(\OCP\Security\IHasher::class)->verify('a', '86f7e437faa5a7fce15d1ddcb9eaeaea377667b8', $newHash)); * var_dump($newHash); * * @since 8.0.0 diff --git a/lib/public/Security/ISecureRandom.php b/lib/public/Security/ISecureRandom.php index aa191ca348f..0f4a79e08e0 100644 --- a/lib/public/Security/ISecureRandom.php +++ b/lib/public/Security/ISecureRandom.php @@ -14,7 +14,7 @@ namespace OCP\Security; * use a fallback. * * Usage: - * \OC::$server->get(ISecureRandom::class)->generate(10); + * \OCP\Server::get(ISecureRandom::class)->generate(10); * * @since 8.0.0 */ diff --git a/lib/public/Template.php b/lib/public/Template.php index 3b31ee10a54..715115bc635 100644 --- a/lib/public/Template.php +++ b/lib/public/Template.php @@ -77,7 +77,7 @@ class Template extends \OC_Template implements ITemplate { } /** - * Make OC_Helper::humanFileSize available as a simple function + * Make \OCP\Util::humanFileSize available as a simple function * Example: 2048 to 2 kB. * * @param int $bytes in bytes diff --git a/lib/public/Util.php b/lib/public/Util.php index 14663abd62f..979e7abe609 100644 --- a/lib/public/Util.php +++ b/lib/public/Util.php @@ -51,7 +51,7 @@ class Util { return $subscriptionRegistry->delegateHasExtendedSupport(); } catch (ContainerExceptionInterface $e) { } - return \OC::$server->getConfig()->getSystemValueBool('extendedSupport', false); + return \OCP\Server::get(IConfig::class)->getSystemValueBool('extendedSupport', false); } /** @@ -60,7 +60,7 @@ class Util { * @since 8.1.0 */ public static function setChannel($channel) { - \OC::$server->getConfig()->setSystemValue('updater.release.channel', $channel); + \OCP\Server::get(IConfig::class)->setSystemValue('updater.release.channel', $channel); } /** @@ -182,7 +182,7 @@ class Util { */ public static function getScripts(): array { // Sort scriptDeps into sortedScriptDeps - $scriptSort = \OC::$server->get(AppScriptSort::class); + $scriptSort = \OCP\Server::get(AppScriptSort::class); $sortedScripts = $scriptSort->sort(self::$scripts, self::$scriptDeps); // Flatten array and remove duplicates @@ -209,7 +209,7 @@ class Util { */ public static function addTranslations($application, $languageCode = null, $init = false) { if (is_null($languageCode)) { - $languageCode = \OC::$server->get(IFactory::class)->findLanguage($application); + $languageCode = \OCP\Server::get(IFactory::class)->findLanguage($application); } if (!empty($application)) { $path = "$application/l10n/$languageCode"; @@ -247,7 +247,7 @@ class Util { * @since 4.0.0 - parameter $args was added in 4.5.0 */ public static function linkToAbsolute($app, $file, $args = []) { - $urlGenerator = \OC::$server->getURLGenerator(); + $urlGenerator = \OCP\Server::get(IURLGenerator::class); return $urlGenerator->getAbsoluteURL( $urlGenerator->linkTo($app, $file, $args) ); @@ -260,7 +260,7 @@ class Util { * @since 4.0.0 */ public static function linkToRemote($service) { - $urlGenerator = \OC::$server->getURLGenerator(); + $urlGenerator = \OCP\Server::get(IURLGenerator::class); $remoteBase = $urlGenerator->linkTo('', 'remote.php') . '/' . $service; return $urlGenerator->getAbsoluteURL( $remoteBase . (($service[strlen($service) - 1] != '/') ? '/' : '') @@ -273,7 +273,7 @@ class Util { * @since 5.0.0 */ public static function getServerHostName() { - $host_name = \OC::$server->getRequest()->getServerHost(); + $host_name = \OCP\Server::get(IRequest::class)->getServerHost(); // strip away port number (if existing) $colon_pos = strpos($host_name, ':'); if ($colon_pos != false) { @@ -299,13 +299,13 @@ class Util { * @since 5.0.0 */ public static function getDefaultEmailAddress(string $user_part): string { - $config = \OC::$server->getConfig(); + $config = \OCP\Server::get(IConfig::class); $user_part = $config->getSystemValueString('mail_from_address', $user_part); $host_name = self::getServerHostName(); $host_name = $config->getSystemValueString('mail_domain', $host_name); $defaultEmailAddress = $user_part . '@' . $host_name; - $mailer = \OC::$server->get(IMailer::class); + $mailer = \OCP\Server::get(IMailer::class); if ($mailer->validateMailAddress($defaultEmailAddress)) { return $defaultEmailAddress; } @@ -332,19 +332,70 @@ class Util { * @since 4.0.0 */ public static function humanFileSize(int|float $bytes): string { - return \OC_Helper::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 (2 kB to 2048) + * Inspired by: https://www.php.net/manual/en/function.filesize.php#92418 + * * @param string $str file size in a fancy format * @return false|int|float a file size in bytes - * - * Inspired by: https://www.php.net/manual/en/function.filesize.php#92418 * @since 4.0.0 */ public static function computerFileSize(string $str): false|int|float { - return \OC_Helper::computerFileSize($str); + $str = strtolower($str); + if (is_numeric($str)) { + return Util::numericToNumber($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) && isset($bytes_array[$matches[1]])) { + $bytes *= $bytes_array[$matches[1]]; + } else { + return false; + } + + return Util::numericToNumber(round($bytes)); } /** @@ -396,7 +447,7 @@ class Util { */ public static function callRegister() { if (self::$token === '') { - self::$token = \OC::$server->get(CsrfTokenManager::class)->getToken()->getEncryptedValue(); + self::$token = \OCP\Server::get(CsrfTokenManager::class)->getToken()->getEncryptedValue(); } return self::$token; } @@ -531,7 +582,7 @@ class Util { */ public static function needUpgrade() { if (!isset(self::$needUpgradeCache)) { - self::$needUpgradeCache = \OC_Util::needUpgrade(\OC::$server->getSystemConfig()); + self::$needUpgradeCache = \OC_Util::needUpgrade(\OCP\Server::get(\OC\SystemConfig::class)); } return self::$needUpgradeCache; } |