diff options
Diffstat (limited to 'lib/private')
-rw-r--r-- | lib/private/Encryption/Keys/Storage.php | 146 | ||||
-rw-r--r-- | lib/private/Files/Cache/Cache.php | 40 | ||||
-rw-r--r-- | lib/private/Files/Cache/CacheQueryBuilder.php | 13 | ||||
-rwxr-xr-x | lib/private/LargeFileHelper.php | 4 | ||||
-rw-r--r-- | lib/private/Memcache/APCu.php | 5 | ||||
-rw-r--r-- | lib/private/Repair.php | 2 | ||||
-rw-r--r-- | lib/private/Repair/NC20/EncryptionMigration.php | 62 | ||||
-rw-r--r-- | lib/private/Server.php | 6 | ||||
-rw-r--r-- | lib/private/Setup.php | 2 | ||||
-rw-r--r-- | lib/private/TempManager.php | 16 | ||||
-rw-r--r-- | lib/private/TemplateLayout.php | 5 | ||||
-rw-r--r-- | lib/private/legacy/OC_Files.php | 3 | ||||
-rw-r--r-- | lib/private/legacy/OC_Helper.php | 7 | ||||
-rw-r--r-- | lib/private/legacy/OC_Util.php | 5 |
14 files changed, 263 insertions, 53 deletions
diff --git a/lib/private/Encryption/Keys/Storage.php b/lib/private/Encryption/Keys/Storage.php index cee32691261..43a291b886c 100644 --- a/lib/private/Encryption/Keys/Storage.php +++ b/lib/private/Encryption/Keys/Storage.php @@ -31,8 +31,11 @@ namespace OC\Encryption\Keys; use OC\Encryption\Util; use OC\Files\Filesystem; use OC\Files\View; +use OC\ServerNotAvailableException; use OC\User\NoUserException; use OCP\Encryption\Keys\IStorage; +use OCP\IConfig; +use OCP\Security\ICrypto; class Storage implements IStorage { @@ -62,11 +65,17 @@ class Storage implements IStorage { /** @var array */ private $keyCache = []; + /** @var ICrypto */ + private $crypto; + + /** @var IConfig */ + private $config; + /** * @param View $view * @param Util $util */ - public function __construct(View $view, Util $util) { + public function __construct(View $view, Util $util, ICrypto $crypto, IConfig $config) { $this->view = $view; $this->util = $util; @@ -74,6 +83,8 @@ class Storage implements IStorage { $this->keys_base_dir = $this->encryption_base_dir .'/keys'; $this->backup_base_dir = $this->encryption_base_dir .'/backup'; $this->root_dir = $this->util->getKeyStorageRoot(); + $this->crypto = $crypto; + $this->config = $config; } /** @@ -81,7 +92,7 @@ class Storage implements IStorage { */ public function getUserKey($uid, $keyId, $encryptionModuleId) { $path = $this->constructUserKeyPath($encryptionModuleId, $keyId, $uid); - return $this->getKey($path); + return base64_decode($this->getKeyWithUid($path, $uid)); } /** @@ -90,17 +101,17 @@ class Storage implements IStorage { public function getFileKey($path, $keyId, $encryptionModuleId) { $realFile = $this->util->stripPartialFileExtension($path); $keyDir = $this->getFileKeyDir($encryptionModuleId, $realFile); - $key = $this->getKey($keyDir . $keyId); + $key = $this->getKey($keyDir . $keyId)['key']; if ($key === '' && $realFile !== $path) { // Check if the part file has keys and use them, if no normal keys // exist. This is required to fix copyBetweenStorage() when we // rename a .part file over storage borders. $keyDir = $this->getFileKeyDir($encryptionModuleId, $path); - $key = $this->getKey($keyDir . $keyId); + $key = $this->getKey($keyDir . $keyId)['key']; } - return $key; + return base64_decode($key); } /** @@ -108,7 +119,7 @@ class Storage implements IStorage { */ public function getSystemUserKey($keyId, $encryptionModuleId) { $path = $this->constructUserKeyPath($encryptionModuleId, $keyId, null); - return $this->getKey($path); + return base64_decode($this->getKeyWithUid($path, null)); } /** @@ -116,7 +127,10 @@ class Storage implements IStorage { */ public function setUserKey($uid, $keyId, $key, $encryptionModuleId) { $path = $this->constructUserKeyPath($encryptionModuleId, $keyId, $uid); - return $this->setKey($path, $key); + return $this->setKey($path, [ + 'key' => base64_encode($key), + 'uid' => $uid, + ]); } /** @@ -124,7 +138,9 @@ class Storage implements IStorage { */ public function setFileKey($path, $keyId, $key, $encryptionModuleId) { $keyDir = $this->getFileKeyDir($encryptionModuleId, $path); - return $this->setKey($keyDir . $keyId, $key); + return $this->setKey($keyDir . $keyId, [ + 'key' => base64_encode($key), + ]); } /** @@ -132,7 +148,10 @@ class Storage implements IStorage { */ public function setSystemUserKey($keyId, $key, $encryptionModuleId) { $path = $this->constructUserKeyPath($encryptionModuleId, $keyId, null); - return $this->setKey($path, $key); + return $this->setKey($path, [ + 'key' => base64_encode($key), + 'uid' => null, + ]); } /** @@ -200,19 +219,106 @@ class Storage implements IStorage { } /** + * @param string $path + * @param string|null $uid + * @return string + * @throws ServerNotAvailableException + * + * Small helper function to fetch the key and verify the value for user and system keys + */ + private function getKeyWithUid(string $path, ?string $uid): string { + $data = $this->getKey($path); + + if (!isset($data['key'])) { + throw new ServerNotAvailableException('Key is invalid'); + } + + if ($data['key'] === '') { + return ''; + } + + if (!array_key_exists('uid', $data) || $data['uid'] !== $uid) { + // If the migration is done we error out + $versionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0.0'); + if (version_compare($versionFromBeforeUpdate, '20.0.0.1', '<=')) { + return $data['key']; + } + + if ($this->config->getSystemValueBool('encryption.key_storage_migrated', true)) { + throw new ServerNotAvailableException('Key has been modified'); + } else { + //Otherwise we migrate + $data['uid'] = $uid; + $this->setKey($path, $data); + } + } + + return $data['key']; + } + + /** * read key from hard disk * * @param string $path to key - * @return string + * @return array containing key as base64encoded key, and possible the uid */ - private function getKey($path) { - $key = ''; + private function getKey($path): array { + $key = [ + 'key' => '', + ]; if ($this->view->file_exists($path)) { if (isset($this->keyCache[$path])) { $key = $this->keyCache[$path]; } else { - $key = $this->view->file_get_contents($path); + $data = $this->view->file_get_contents($path); + + // Version <20.0.0.1 doesn't have this + $versionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0.0'); + if (version_compare($versionFromBeforeUpdate, '20.0.0.1', '<=')) { + $key = [ + 'key' => base64_encode($data), + ]; + } else { + if ($this->config->getSystemValueBool('encryption.key_storage_migrated', true)) { + try { + $clearData = $this->crypto->decrypt($data); + } catch (\Exception $e) { + throw new ServerNotAvailableException('Could not decrypt key', 0, $e); + } + + $dataArray = json_decode($clearData, true); + if ($dataArray === null) { + throw new ServerNotAvailableException('Invalid encryption key'); + } + + $key = $dataArray; + } else { + /* + * Even if not all keys are migrated we should still try to decrypt it (in case some have moved). + * However it is only a failure now if it is an array and decryption fails + */ + $fallback = false; + try { + $clearData = $this->crypto->decrypt($data); + } catch (\Exception $e) { + $fallback = true; + } + + if (!$fallback) { + $dataArray = json_decode($clearData, true); + if ($dataArray === null) { + throw new ServerNotAvailableException('Invalid encryption key'); + } + $key = $dataArray; + } else { + $key = [ + 'key' => base64_encode($data), + ]; + } + } + } + $this->keyCache[$path] = $key; } } @@ -225,13 +331,23 @@ class Storage implements IStorage { * * * @param string $path path to key directory - * @param string $key key + * @param array $key key * @return bool */ private function setKey($path, $key) { $this->keySetPreparation(dirname($path)); - $result = $this->view->file_put_contents($path, $key); + $versionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0.0'); + if (version_compare($versionFromBeforeUpdate, '20.0.0.1', '<=')) { + // Only store old format if this happens during the migration. + // TODO: Remove for 21 + $data = base64_decode($key['key']); + } else { + // Wrap the data + $data = $this->crypto->encrypt(json_encode($key)); + } + + $result = $this->view->file_put_contents($path, $data); if (is_int($result) && $result > 0) { $this->keyCache[$path] = $key; diff --git a/lib/private/Files/Cache/Cache.php b/lib/private/Files/Cache/Cache.php index de807421d26..77289e674b3 100644 --- a/lib/private/Files/Cache/Cache.php +++ b/lib/private/Files/Cache/Cache.php @@ -553,25 +553,35 @@ class Cache implements ICache { * @throws \OC\DatabaseException */ private function removeChildren(ICacheEntry $entry) { - $children = $this->getFolderContentsById($entry->getId()); - $childIds = array_map(function (ICacheEntry $cacheEntry) { - return $cacheEntry->getId(); - }, $children); - $childFolders = array_filter($children, function ($child) { - return $child->getMimeType() == FileInfo::MIMETYPE_FOLDER; - }); - foreach ($childFolders as $folder) { - $this->removeChildren($folder); + $parentIds = [$entry->getId()]; + $queue = [$entry->getId()]; + + // we walk depth first trough the file tree, removing all filecache_extended attributes while we walk + // and collecting all folder ids to later use to delete the filecache entries + while ($entryId = array_pop($queue)) { + $children = $this->getFolderContentsById($entryId); + $childIds = array_map(function (ICacheEntry $cacheEntry) { + return $cacheEntry->getId(); + }, $children); + + $query = $this->getQueryBuilder(); + $query->delete('filecache_extended') + ->where($query->expr()->in('fileid', $query->createNamedParameter($childIds, IQueryBuilder::PARAM_INT_ARRAY))); + $query->execute(); + + /** @var ICacheEntry[] $childFolders */ + $childFolders = array_filter($children, function ($child) { + return $child->getMimeType() == FileInfo::MIMETYPE_FOLDER; + }); + foreach ($childFolders as $folder) { + $parentIds[] = $folder->getId(); + $queue[] = $folder->getId(); + } } $query = $this->getQueryBuilder(); $query->delete('filecache') - ->whereParent($entry->getId()); - $query->execute(); - - $query = $this->getQueryBuilder(); - $query->delete('filecache_extended') - ->where($query->expr()->in('fileid', $query->createNamedParameter($childIds, IQueryBuilder::PARAM_INT_ARRAY))); + ->whereParentIn($parentIds); $query->execute(); } diff --git a/lib/private/Files/Cache/CacheQueryBuilder.php b/lib/private/Files/Cache/CacheQueryBuilder.php index 332274eda2a..ac17cfaffb2 100644 --- a/lib/private/Files/Cache/CacheQueryBuilder.php +++ b/lib/private/Files/Cache/CacheQueryBuilder.php @@ -94,4 +94,17 @@ class CacheQueryBuilder extends QueryBuilder { return $this; } + + public function whereParentIn(array $parents) { + $alias = $this->alias; + if ($alias) { + $alias .= '.'; + } else { + $alias = ''; + } + + $this->andWhere($this->expr()->in("{$alias}parent", $this->createNamedParameter($parents, IQueryBuilder::PARAM_INT_ARRAY))); + + return $this; + } } diff --git a/lib/private/LargeFileHelper.php b/lib/private/LargeFileHelper.php index 2a6a6714eb3..c4e76d22c5a 100755 --- a/lib/private/LargeFileHelper.php +++ b/lib/private/LargeFileHelper.php @@ -29,6 +29,8 @@ namespace OC; +use bantu\IniGetWrapper\IniGetWrapper; + /** * Helper class for large files on 32-bit platforms. */ @@ -117,7 +119,7 @@ class LargeFileHelper { * null on failure. */ public function getFileSizeViaCurl($fileName) { - if (\OC::$server->getIniWrapper()->getString('open_basedir') === '') { + if (\OC::$server->get(IniGetWrapper::class)->getString('open_basedir') === '') { $encodedFileName = rawurlencode($fileName); $ch = curl_init("file:///$encodedFileName"); curl_setopt($ch, CURLOPT_NOBODY, true); diff --git a/lib/private/Memcache/APCu.php b/lib/private/Memcache/APCu.php index 87d72ec1968..f7ef6eb2d3e 100644 --- a/lib/private/Memcache/APCu.php +++ b/lib/private/Memcache/APCu.php @@ -28,6 +28,7 @@ namespace OC\Memcache; +use bantu\IniGetWrapper\IniGetWrapper; use OCP\IMemcache; class APCu extends Cache implements IMemcache { @@ -154,9 +155,9 @@ class APCu extends Cache implements IMemcache { public static function isAvailable() { if (!extension_loaded('apcu')) { return false; - } elseif (!\OC::$server->getIniWrapper()->getBool('apc.enabled')) { + } elseif (!\OC::$server->get(IniGetWrapper::class)->getBool('apc.enabled')) { return false; - } elseif (!\OC::$server->getIniWrapper()->getBool('apc.enable_cli') && \OC::$CLI) { + } elseif (!\OC::$server->get(IniGetWrapper::class)->getBool('apc.enable_cli') && \OC::$CLI) { return false; } elseif ( version_compare(phpversion('apc') ?: '0.0.0', '4.0.6') === -1 && diff --git a/lib/private/Repair.php b/lib/private/Repair.php index 6151812d316..4ad21e74238 100644 --- a/lib/private/Repair.php +++ b/lib/private/Repair.php @@ -49,6 +49,7 @@ use OC\Repair\NC16\CleanupCardDAVPhotoCache; use OC\Repair\NC16\ClearCollectionsAccessCache; use OC\Repair\NC18\ResetGeneratedAvatarFlag; use OC\Repair\NC20\EncryptionLegacyCipher; +use OC\Repair\NC20\EncryptionMigration; use OC\Repair\OldGroupMembershipShares; use OC\Repair\Owncloud\DropAccountTermsTable; use OC\Repair\Owncloud\SaveAccountsTableData; @@ -158,6 +159,7 @@ class Repair implements IOutput { new ClearCollectionsAccessCache(\OC::$server->getConfig(), \OC::$server->query(IManager::class)), \OC::$server->query(ResetGeneratedAvatarFlag::class), \OC::$server->query(EncryptionLegacyCipher::class), + \OC::$server->query(EncryptionMigration::class), ]; } diff --git a/lib/private/Repair/NC20/EncryptionMigration.php b/lib/private/Repair/NC20/EncryptionMigration.php new file mode 100644 index 00000000000..6d5c2dc0c58 --- /dev/null +++ b/lib/private/Repair/NC20/EncryptionMigration.php @@ -0,0 +1,62 @@ +<?php + +declare(strict_types=1); +/** + * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl> + * + * @author Roeland Jago Douma <roeland@famdouma.nl> + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +namespace OC\Repair\NC20; + +use OCP\Encryption\IManager; +use OCP\IConfig; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; + +class EncryptionMigration implements IRepairStep { + + /** @var IConfig */ + private $config; + /** @var IManager */ + private $manager; + + public function __construct(IConfig $config, + IManager $manager) { + $this->config = $config; + $this->manager = $manager; + } + + public function getName(): string { + return 'Check encryption key format'; + } + + private function shouldRun(): bool { + $versionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0.0'); + return version_compare($versionFromBeforeUpdate, '20.0.0.1', '<='); + } + + public function run(IOutput $output): void { + if ($this->manager->isEnabled()) { + if ($this->config->getSystemValue('encryption.key_storage_migrated', '') === '') { + $this->config->setSystemValue('encryption.key_storage_migrated', false); + } + } + } +} diff --git a/lib/private/Server.php b/lib/private/Server.php index 9b452f21ce1..e404723089a 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -348,7 +348,7 @@ class Server extends ServerContainer implements IServerContainer { $c->getConfig() ); - return new Encryption\Keys\Storage($view, $util); + return new Encryption\Keys\Storage($view, $util, $c->getCrypto(), $c->getConfig()); }); /** @deprecated 20.0.0 */ $this->registerDeprecatedAlias('TagMapper', TagMapper::class); @@ -1012,7 +1012,7 @@ class Server extends ServerContainer implements IServerContainer { return $factory->getLDAPProvider(); }); $this->registerService(ILockingProvider::class, function (Server $c) { - $ini = $c->getIniWrapper(); + $ini = $c->get(IniGetWrapper::class); $config = $c->getConfig(); $ttl = $config->getSystemValue('filelocking.ttl', max(3600, $ini->getNumeric('max_execution_time'))); if ($config->getSystemValue('filelocking.enabled', true) or (defined('PHPUNIT_RUN') && PHPUNIT_RUN)) { @@ -1953,7 +1953,7 @@ class Server extends ServerContainer implements IServerContainer { * @deprecated */ public function getIniWrapper() { - return $this->query('IniWrapper'); + return $this->query(IniGetWrapper::class); } /** diff --git a/lib/private/Setup.php b/lib/private/Setup.php index 775c2d0a952..839ef9f7bea 100644 --- a/lib/private/Setup.php +++ b/lib/private/Setup.php @@ -503,7 +503,7 @@ class Setup { $setupHelper = new \OC\Setup( $config, - \OC::$server->getIniWrapper(), + \OC::$server->get(IniGetWrapper::class), \OC::$server->getL10N('lib'), \OC::$server->query(Defaults::class), \OC::$server->getLogger(), diff --git a/lib/private/TempManager.php b/lib/private/TempManager.php index 49d4ee94cf6..b4db44f6b1c 100644 --- a/lib/private/TempManager.php +++ b/lib/private/TempManager.php @@ -31,30 +31,30 @@ namespace OC; +use bantu\IniGetWrapper\IniGetWrapper; use OCP\IConfig; -use OCP\ILogger; use OCP\ITempManager; +use Psr\Log\LoggerInterface; class TempManager implements ITempManager { /** @var string[] Current temporary files and folders, used for cleanup */ protected $current = []; /** @var string i.e. /tmp on linux systems */ protected $tmpBaseDir; - /** @var ILogger */ + /** @var LoggerInterface */ protected $log; /** @var IConfig */ protected $config; + /** @var IniGetWrapper */ + protected $iniGetWrapper; /** Prefix */ public const TMP_PREFIX = 'oc_tmp_'; - /** - * @param \OCP\ILogger $logger - * @param \OCP\IConfig $config - */ - public function __construct(ILogger $logger, IConfig $config) { + public function __construct(LoggerInterface $logger, IConfig $config, IniGetWrapper $iniGetWrapper) { $this->log = $logger; $this->config = $config; + $this->iniGetWrapper = $iniGetWrapper; $this->tmpBaseDir = $this->getTempBaseDir(); } @@ -218,7 +218,7 @@ class TempManager implements ITempManager { if ($temp = $this->config->getSystemValue('tempdirectory', null)) { $directories[] = $temp; } - if ($temp = \OC::$server->getIniWrapper()->get('upload_tmp_dir')) { + if ($temp = $this->iniGetWrapper->get('upload_tmp_dir')) { $directories[] = $temp; } if ($temp = getenv('TMP')) { diff --git a/lib/private/TemplateLayout.php b/lib/private/TemplateLayout.php index af3aeb440d2..4a0ec75b517 100644 --- a/lib/private/TemplateLayout.php +++ b/lib/private/TemplateLayout.php @@ -44,6 +44,7 @@ namespace OC; +use bantu\IniGetWrapper\IniGetWrapper; use OC\Search\SearchQuery; use OC\Template\JSCombiner; use OC\Template\JSConfigHelper; @@ -202,7 +203,7 @@ class TemplateLayout extends \OC_Template { \OC::$server->getUserSession()->getUser(), $this->config, \OC::$server->getGroupManager(), - \OC::$server->getIniWrapper(), + \OC::$server->get(IniGetWrapper::class), \OC::$server->getURLGenerator(), \OC::$server->getCapabilitiesManager(), \OC::$server->query(IInitialStateService::class) @@ -259,7 +260,7 @@ class TemplateLayout extends \OC_Template { } } } - + $this->assign('initialStates', $this->initialState->getInitialStates()); } diff --git a/lib/private/legacy/OC_Files.php b/lib/private/legacy/OC_Files.php index ddb824cd6cd..f5f91fc9958 100644 --- a/lib/private/legacy/OC_Files.php +++ b/lib/private/legacy/OC_Files.php @@ -41,6 +41,7 @@ * */ +use bantu\IniGetWrapper\IniGetWrapper; use OC\Files\View; use OC\Streamer; use OCP\Lock\ILockingProvider; @@ -164,7 +165,7 @@ class OC_Files { OC_Util::obEnd(); $streamer->sendHeaders($name); - $executionTime = (int)OC::$server->getIniWrapper()->getNumeric('max_execution_time'); + $executionTime = (int)OC::$server->get(IniGetWrapper::class)->getNumeric('max_execution_time'); if (strpos(@ini_get('disable_functions'), 'set_time_limit') === false) { @set_time_limit(0); } diff --git a/lib/private/legacy/OC_Helper.php b/lib/private/legacy/OC_Helper.php index 8cd492de117..4e9c5cffe98 100644 --- a/lib/private/legacy/OC_Helper.php +++ b/lib/private/legacy/OC_Helper.php @@ -44,6 +44,7 @@ * */ +use bantu\IniGetWrapper\IniGetWrapper; use Symfony\Component\Process\ExecutableFinder; /** @@ -220,7 +221,7 @@ class OC_Helper { // Default check will be done with $path directories : $dirs = explode(PATH_SEPARATOR, $path); // WARNING : We have to check if open_basedir is enabled : - $obd = OC::$server->getIniWrapper()->getString('open_basedir'); + $obd = OC::$server->get(IniGetWrapper::class)->getString('open_basedir'); if ($obd != "none") { $obd_values = explode(PATH_SEPARATOR, $obd); if (count($obd_values) > 0 and $obd_values[0]) { @@ -414,7 +415,7 @@ class OC_Helper { * @return int PHP upload file size limit */ public static function uploadLimit() { - $ini = \OC::$server->getIniWrapper(); + $ini = \OC::$server->get(IniGetWrapper::class); $upload_max_filesize = OCP\Util::computerFileSize($ini->get('upload_max_filesize')); $post_max_size = OCP\Util::computerFileSize($ini->get('post_max_size')); if ((int)$upload_max_filesize === 0 and (int)$post_max_size === 0) { @@ -436,7 +437,7 @@ class OC_Helper { if (!function_exists($function_name)) { return false; } - $ini = \OC::$server->getIniWrapper(); + $ini = \OC::$server->get(IniGetWrapper::class); $disabled = explode(',', $ini->get('disable_functions') ?: ''); $disabled = array_map('trim', $disabled); if (in_array($function_name, $disabled)) { diff --git a/lib/private/legacy/OC_Util.php b/lib/private/legacy/OC_Util.php index f2aa0545afd..caae862ad41 100644 --- a/lib/private/legacy/OC_Util.php +++ b/lib/private/legacy/OC_Util.php @@ -62,6 +62,7 @@ * */ +use bantu\IniGetWrapper\IniGetWrapper; use OC\AppFramework\Http\Request; use OC\Files\Storage\LocalRootStorage; use OCP\IConfig; @@ -738,7 +739,7 @@ class OC_Util { $webServerRestart = false; $setup = new \OC\Setup( $config, - \OC::$server->getIniWrapper(), + \OC::$server->get(IniGetWrapper::class), \OC::$server->getL10N('lib'), \OC::$server->query(\OCP\Defaults::class), \OC::$server->getLogger(), @@ -863,7 +864,7 @@ class OC_Util { $missingDependencies = []; $invalidIniSettings = []; - $iniWrapper = \OC::$server->getIniWrapper(); + $iniWrapper = \OC::$server->get(IniGetWrapper::class); foreach ($dependencies['classes'] as $class => $module) { if (!class_exists($class)) { $missingDependencies[] = $module; |