diff options
Diffstat (limited to 'lib')
68 files changed, 558 insertions, 450 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..856afbe5677 100644 --- a/lib/composer/composer/autoload_classmap.php +++ b/lib/composer/composer/autoload_classmap.php @@ -1539,6 +1539,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 +1550,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..fed9723421e 100644 --- a/lib/composer/composer/autoload_static.php +++ b/lib/composer/composer/autoload_static.php @@ -1580,6 +1580,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 +1591,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/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/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/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/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..aeaac030413 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; @@ -225,11 +226,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/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..2a60287254b 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,6 +163,7 @@ class Router implements IRouter { $this->root->addCollection($collection); } } + $this->eventLogger->end('route:load:files'); if (!isset($this->loadedApps['core'])) { $this->loadedApps['core'] = true; @@ -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/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_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/Files.php b/lib/public/Files.php index 62c41c4ada1..fb03a4192fc 100644 --- a/lib/public/Files.php +++ b/lib/public/Files.php @@ -18,12 +18,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); } /** 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/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..d7cfd65ba56 100644 --- a/lib/public/Util.php +++ b/lib/public/Util.php @@ -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)); } /** |