diff options
Diffstat (limited to 'lib/private')
22 files changed, 240 insertions, 244 deletions
diff --git a/lib/private/AllConfig.php b/lib/private/AllConfig.php index 8a6bb5a4723..c80ee52eb0d 100644 --- a/lib/private/AllConfig.php +++ b/lib/private/AllConfig.php @@ -7,11 +7,11 @@ */ namespace OC; -use NCU\Config\Exceptions\TypeConflictException; -use NCU\Config\IUserConfig; -use NCU\Config\ValueType; use OC\Config\UserConfig; use OCP\Cache\CappedMemoryCache; +use OCP\Config\Exceptions\TypeConflictException; +use OCP\Config\IUserConfig; +use OCP\Config\ValueType; use OCP\IConfig; use OCP\IDBConnection; use OCP\PreConditionNotMetException; diff --git a/lib/private/App/AppStore/Fetcher/Fetcher.php b/lib/private/App/AppStore/Fetcher/Fetcher.php index 2e949fedb51..24876675d60 100644 --- a/lib/private/App/AppStore/Fetcher/Fetcher.php +++ b/lib/private/App/AppStore/Fetcher/Fetcher.php @@ -56,7 +56,7 @@ abstract class Fetcher { * * @return array */ - protected function fetch($ETag, $content) { + protected function fetch($ETag, $content, $allowUnstable = false) { $appstoreenabled = $this->config->getSystemValueBool('appstoreenabled', true); if ((int)$this->config->getAppValue('settings', 'appstore-fetcher-lastFailure', '0') > time() - self::RETRY_AFTER_FAILURE_SECONDS) { return []; diff --git a/lib/private/AppConfig.php b/lib/private/AppConfig.php index f050deba1ca..2280ac1a79f 100644 --- a/lib/private/AppConfig.php +++ b/lib/private/AppConfig.php @@ -11,12 +11,13 @@ namespace OC; use InvalidArgumentException; use JsonException; -use NCU\Config\Lexicon\ConfigLexiconEntry; -use NCU\Config\Lexicon\ConfigLexiconStrictness; -use NCU\Config\Lexicon\IConfigLexicon; -use NCU\Config\Lexicon\Preset; use OC\AppFramework\Bootstrap\Coordinator; use OC\Config\ConfigManager; +use OCP\Config\Lexicon\Entry; +use OCP\Config\Lexicon\ILexicon; +use OCP\Config\Lexicon\Preset; +use OCP\Config\Lexicon\Strictness; +use OCP\Config\ValueType; use OCP\DB\Exception as DBException; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Exceptions\AppConfigIncorrectTypeException; @@ -62,7 +63,7 @@ class AppConfig implements IAppConfig { private array $valueTypes = []; // type for all config values private bool $fastLoaded = false; private bool $lazyLoaded = false; - /** @var array<string, array{entries: array<string, ConfigLexiconEntry>, aliases: array<string, string>, strictness: ConfigLexiconStrictness}> ['app_id' => ['strictness' => ConfigLexiconStrictness, 'entries' => ['config_key' => ConfigLexiconEntry[]]] */ + /** @var array<string, array{entries: array<string, Entry>, aliases: array<string, string>, strictness: Strictness}> ['app_id' => ['strictness' => ConfigLexiconStrictness, 'entries' => ['config_key' => ConfigLexiconEntry[]]] */ private array $configLexiconDetails = []; private bool $ignoreLexiconAliases = false; private ?Preset $configLexiconPreset = null; @@ -1095,6 +1096,49 @@ class AppConfig implements IAppConfig { } /** + * @inheritDoc + * + * @param string $app id of the app + * @param string $key config key + * + * @return array{app: string, key: string, lazy?: bool, valueType?: ValueType, valueTypeName?: string, sensitive?: bool, default?: string, definition?: string, note?: string} + * @since 32.0.0 + */ + public function getKeyDetails(string $app, string $key): array { + $this->assertParams($app, $key); + try { + $details = $this->getDetails($app, $key); + } catch (AppConfigUnknownKeyException $e) { + $details = [ + 'app' => $app, + 'key' => $key + ]; + } + + /** @var Entry $lexiconEntry */ + try { + $lazy = false; + $this->matchAndApplyLexiconDefinition($app, $key, $lazy, lexiconEntry: $lexiconEntry); + } catch (AppConfigTypeConflictException|AppConfigUnknownKeyException) { + // can be ignored + } + + if ($lexiconEntry !== null) { + $details = array_merge($details, [ + 'lazy' => $lexiconEntry->isLazy(), + 'valueType' => $lexiconEntry->getValueType(), + 'valueTypeName' => $lexiconEntry->getValueType()->name, + 'sensitive' => $lexiconEntry->isFlagged(self::FLAG_SENSITIVE), + 'default' => $lexiconEntry->getDefault($this->getLexiconPreset()), + 'definition' => $lexiconEntry->getDefinition(), + 'note' => $lexiconEntry->getNote(), + ]); + } + + return array_filter($details); + } + + /** * @param string $type * * @return int @@ -1631,6 +1675,7 @@ class AppConfig implements IAppConfig { ?bool &$lazy = null, int &$type = self::VALUE_MIXED, ?string &$default = null, + ?Entry &$lexiconEntry = null, ): bool { if (in_array($key, [ @@ -1655,27 +1700,27 @@ class AppConfig implements IAppConfig { return true; } - /** @var ConfigLexiconEntry $configValue */ - $configValue = $configDetails['entries'][$key]; + /** @var Entry $lexiconEntry */ + $lexiconEntry = $configDetails['entries'][$key]; $type &= ~self::VALUE_SENSITIVE; - $appConfigValueType = $configValue->getValueType()->toAppConfigFlag(); + $appConfigValueType = $lexiconEntry->getValueType()->toAppConfigFlag(); if ($type === self::VALUE_MIXED) { $type = $appConfigValueType; // we overwrite if value was requested as mixed } elseif ($appConfigValueType !== $type) { throw new AppConfigTypeConflictException('The app config key ' . $app . '/' . $key . ' is typed incorrectly in relation to the config lexicon'); } - $lazy = $configValue->isLazy(); + $lazy = $lexiconEntry->isLazy(); // only look for default if needed, default from Lexicon got priority if ($default !== null) { - $default = $configValue->getDefault($this->getLexiconPreset()) ?? $default; + $default = $lexiconEntry->getDefault($this->getLexiconPreset()) ?? $default; } - if ($configValue->isFlagged(self::FLAG_SENSITIVE)) { + if ($lexiconEntry->isFlagged(self::FLAG_SENSITIVE)) { $type |= self::VALUE_SENSITIVE; } - if ($configValue->isDeprecated()) { + if ($lexiconEntry->isDeprecated()) { $this->logger->notice('App config key ' . $app . '/' . $key . ' is set as deprecated.'); } @@ -1685,15 +1730,15 @@ class AppConfig implements IAppConfig { /** * manage ConfigLexicon behavior based on strictness set in IConfigLexicon * - * @param ConfigLexiconStrictness|null $strictness + * @param Strictness|null $strictness * @param string $line * * @return bool TRUE if conflict can be fully ignored, FALSE if action should be not performed * @throws AppConfigUnknownKeyException if strictness implies exception - * @see IConfigLexicon::getStrictness() + * @see ILexicon::getStrictness() */ private function applyLexiconStrictness( - ?ConfigLexiconStrictness $strictness, + ?Strictness $strictness, string $line = '', ): bool { if ($strictness === null) { @@ -1701,12 +1746,12 @@ class AppConfig implements IAppConfig { } switch ($strictness) { - case ConfigLexiconStrictness::IGNORE: + case Strictness::IGNORE: return true; - case ConfigLexiconStrictness::NOTICE: + case Strictness::NOTICE: $this->logger->notice($line); return true; - case ConfigLexiconStrictness::WARNING: + case Strictness::WARNING: $this->logger->warning($line); return false; } @@ -1720,7 +1765,7 @@ class AppConfig implements IAppConfig { * @param string $appId * @internal * - * @return array{entries: array<string, ConfigLexiconEntry>, aliases: array<string, string>, strictness: ConfigLexiconStrictness} + * @return array{entries: array<string, Entry>, aliases: array<string, string>, strictness: Strictness} */ public function getConfigDetailsFromLexicon(string $appId): array { if (!array_key_exists($appId, $this->configLexiconDetails)) { @@ -1737,14 +1782,14 @@ class AppConfig implements IAppConfig { $this->configLexiconDetails[$appId] = [ 'entries' => $entries, 'aliases' => $aliases, - 'strictness' => $configLexicon?->getStrictness() ?? ConfigLexiconStrictness::IGNORE + 'strictness' => $configLexicon?->getStrictness() ?? Strictness::IGNORE ]; } return $this->configLexiconDetails[$appId]; } - private function getLexiconEntry(string $appId, string $key): ?ConfigLexiconEntry { + private function getLexiconEntry(string $appId, string $key): ?Entry { return $this->getConfigDetailsFromLexicon($appId)['entries'][$key] ?? null; } diff --git a/lib/private/AppFramework/Bootstrap/RegistrationContext.php b/lib/private/AppFramework/Bootstrap/RegistrationContext.php index 95ad129c466..8bd1ff35610 100644 --- a/lib/private/AppFramework/Bootstrap/RegistrationContext.php +++ b/lib/private/AppFramework/Bootstrap/RegistrationContext.php @@ -10,8 +10,6 @@ declare(strict_types=1); namespace OC\AppFramework\Bootstrap; use Closure; -use NCU\Config\Lexicon\IConfigLexicon; -use OC\Config\Lexicon\CoreConfigLexicon; use OC\Support\CrashReport\Registry; use OCP\AppFramework\App; use OCP\AppFramework\Bootstrap\IRegistrationContext; @@ -23,6 +21,7 @@ use OCP\Calendar\Resource\IBackend as IResourceBackend; use OCP\Calendar\Room\IBackend as IRoomBackend; use OCP\Capabilities\ICapability; use OCP\Collaboration\Reference\IReferenceProvider; +use OCP\Config\Lexicon\ILexicon; use OCP\Dashboard\IManager; use OCP\Dashboard\IWidget; use OCP\EventDispatcher\IEventDispatcher; @@ -144,7 +143,7 @@ class RegistrationContext { private array $declarativeSettings = []; /** @var array<array-key, string> */ - private array $configLexiconClasses = ['core' => CoreConfigLexicon::class]; + private array $configLexiconClasses = []; /** @var ServiceRegistration<ITeamResourceProvider>[] */ private array $teamResourceProviders = []; @@ -652,7 +651,7 @@ class RegistrationContext { } /** - * @psalm-param class-string<IConfigLexicon> $configLexiconClass + * @psalm-param class-string<ILexicon> $configLexiconClass */ public function registerConfigLexicon(string $appId, string $configLexiconClass): void { $this->configLexiconClasses[$appId] = $configLexiconClass; @@ -1023,9 +1022,9 @@ class RegistrationContext { * * @param string $appId * - * @return IConfigLexicon|null + * @return ILexicon|null */ - public function getConfigLexicon(string $appId): ?IConfigLexicon { + public function getConfigLexicon(string $appId): ?ILexicon { if (!array_key_exists($appId, $this->configLexiconClasses)) { return null; } diff --git a/lib/private/AppFramework/Middleware/FlowV2EphemeralSessionsMiddleware.php b/lib/private/AppFramework/Middleware/FlowV2EphemeralSessionsMiddleware.php index e4571dfc50e..b69b129f798 100644 --- a/lib/private/AppFramework/Middleware/FlowV2EphemeralSessionsMiddleware.php +++ b/lib/private/AppFramework/Middleware/FlowV2EphemeralSessionsMiddleware.php @@ -15,6 +15,7 @@ use OCP\AppFramework\Http\Attribute\PublicPage; use OCP\AppFramework\Middleware; use OCP\ISession; use OCP\IUserSession; +use Psr\Log\LoggerInterface; use ReflectionMethod; // Will close the session if the user session is ephemeral. @@ -24,6 +25,7 @@ class FlowV2EphemeralSessionsMiddleware extends Middleware { private ISession $session, private IUserSession $userSession, private ControllerMethodReflector $reflector, + private LoggerInterface $logger, ) { } @@ -52,6 +54,10 @@ class FlowV2EphemeralSessionsMiddleware extends Middleware { return; } + $this->logger->info('Closing user and PHP session for ephemeral session', [ + 'controller' => $controller::class, + 'method' => $methodName, + ]); $this->userSession->logout(); $this->session->close(); } diff --git a/lib/private/BackgroundJob/JobList.php b/lib/private/BackgroundJob/JobList.php index 0d88200cff7..c00a51e3851 100644 --- a/lib/private/BackgroundJob/JobList.php +++ b/lib/private/BackgroundJob/JobList.php @@ -24,6 +24,9 @@ use function min; use function strlen; class JobList implements IJobList { + /** @var array<string, int> */ + protected array $alreadyVisitedParallelBlocked = []; + public function __construct( protected IDBConnection $connection, protected IConfig $config, @@ -198,6 +201,12 @@ class JobList implements IJobList { $job = $this->buildJob($row); if ($job instanceof IParallelAwareJob && !$job->getAllowParallelRuns() && $this->hasReservedJob(get_class($job))) { + if (!isset($this->alreadyVisitedParallelBlocked[get_class($job)])) { + $this->alreadyVisitedParallelBlocked[get_class($job)] = $job->getId(); + } elseif ($this->alreadyVisitedParallelBlocked[get_class($job)] === $job->getId()) { + $this->logger->info('Skipped through all jobs and revisited a IParallelAwareJob blocked job again, giving up.', ['app' => 'cron']); + return null; + } $this->logger->info('Skipping ' . get_class($job) . ' job with ID ' . $job->getId() . ' because another job with the same class is already running', ['app' => 'cron']); $update = $this->connection->getQueryBuilder(); @@ -210,6 +219,10 @@ class JobList implements IJobList { return $this->getNext($onlyTimeSensitive, $jobClasses); } + if ($job !== null && isset($this->alreadyVisitedParallelBlocked[get_class($job)])) { + unset($this->alreadyVisitedParallelBlocked[get_class($job)]); + } + if ($job instanceof \OCP\BackgroundJob\TimedJob) { $now = $this->timeFactory->getTime(); $nextPossibleRun = $job->getLastRun() + $job->getInterval(); diff --git a/lib/private/Calendar/Manager.php b/lib/private/Calendar/Manager.php index 0e2a3f5f679..7da1379809d 100644 --- a/lib/private/Calendar/Manager.php +++ b/lib/private/Calendar/Manager.php @@ -403,7 +403,10 @@ class Manager implements IManager { } if (empty($found)) { - $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()); + $this->logger->warning('iMip message event could not be processed because no corresponding event was found in any calendar', [ + 'principalUri' => $principalUri, + 'eventUid' => $vEvent->{'UID'}->getValue(), + ]); return false; } @@ -518,7 +521,10 @@ class Manager implements IManager { } if (empty($found)) { - $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()); + $this->logger->warning('iMip message event could not be processed because no corresponding event was found in any calendar', [ + 'principalUri' => $principalUri, + 'eventUid' => $vEvent->{'UID'}->getValue(), + ]); return false; } diff --git a/lib/private/Collaboration/Collaborators/Search.php b/lib/private/Collaboration/Collaborators/Search.php index 32c70549a7b..ea39f885fc6 100644 --- a/lib/private/Collaboration/Collaborators/Search.php +++ b/lib/private/Collaboration/Collaborators/Search.php @@ -11,7 +11,7 @@ use OCP\Collaboration\Collaborators\ISearchPlugin; use OCP\Collaboration\Collaborators\ISearchResult; use OCP\Collaboration\Collaborators\SearchResultType; use OCP\IContainer; -use OCP\Share; +use OCP\Share\IShare; class Search implements ISearch { protected array $pluginList = []; @@ -81,7 +81,7 @@ class Search implements ISearch { } public function registerPlugin(array $pluginInfo): void { - $shareType = constant(Share::class . '::' . $pluginInfo['shareType']); + $shareType = constant(IShare::class . '::' . substr($pluginInfo['shareType'], strlen('SHARE_'))); if ($shareType === null) { throw new \InvalidArgumentException('Provided ShareType is invalid'); } diff --git a/lib/private/Config/ConfigManager.php b/lib/private/Config/ConfigManager.php index 67466617941..ed516abdcbf 100644 --- a/lib/private/Config/ConfigManager.php +++ b/lib/private/Config/ConfigManager.php @@ -9,13 +9,13 @@ declare(strict_types=1); namespace OC\Config; use JsonException; -use NCU\Config\Exceptions\TypeConflictException; -use NCU\Config\IUserConfig; -use NCU\Config\Lexicon\ConfigLexiconEntry; -use NCU\Config\Lexicon\Preset; -use NCU\Config\ValueType; use OC\AppConfig; use OCP\App\IAppManager; +use OCP\Config\Exceptions\TypeConflictException; +use OCP\Config\IUserConfig; +use OCP\Config\Lexicon\Entry; +use OCP\Config\Lexicon\Preset; +use OCP\Config\ValueType; use OCP\IAppConfig; use OCP\IConfig; use OCP\Server; @@ -50,10 +50,11 @@ class ConfigManager { * * This method should be mainly called during a new upgrade or when a new app is enabled. * - * @see ConfigLexiconEntry + * @param string|null $appId when set to NULL the method will be executed for all enabled apps of the instance + * * @internal * @since 32.0.0 - * @param string|null $appId when set to NULL the method will be executed for all enabled apps of the instance + * @see Entry */ public function migrateConfigLexiconKeys(?string $appId = null): void { if ($appId === null) { @@ -166,7 +167,7 @@ class ConfigManager { * * @throws TypeConflictException if previous value does not fit the expected type */ - private function migrateAppConfigValue(string $appId, ConfigLexiconEntry $entry): void { + private function migrateAppConfigValue(string $appId, Entry $entry): void { $value = $this->appConfig->getValueMixed($appId, $entry->getRename(), lazy: null); switch ($entry->getValueType()) { case ValueType::STRING: @@ -196,7 +197,7 @@ class ConfigManager { * * @throws TypeConflictException if previous value does not fit the expected type */ - private function migrateUserConfigValue(string $userId, string $appId, ConfigLexiconEntry $entry): void { + private function migrateUserConfigValue(string $userId, string $appId, Entry $entry): void { $value = $this->userConfig->getValueMixed($userId, $appId, $entry->getRename(), lazy: null); switch ($entry->getValueType()) { case ValueType::STRING: @@ -237,7 +238,7 @@ class ConfigManager { return (float)$value; } - public function convertToBool(string $value, ?ConfigLexiconEntry $entry = null): bool { + public function convertToBool(string $value, ?Entry $entry = null): bool { if (in_array(strtolower($value), ['true', '1', 'on', 'yes'])) { $valueBool = true; } elseif (in_array(strtolower($value), ['false', '0', 'off', 'no'])) { @@ -245,7 +246,7 @@ class ConfigManager { } else { throw new TypeConflictException('Value cannot be converted to boolean'); } - if ($entry?->hasOption(ConfigLexiconEntry::RENAME_INVERT_BOOLEAN) === true) { + if ($entry?->hasOption(Entry::RENAME_INVERT_BOOLEAN) === true) { $valueBool = !$valueBool; } diff --git a/lib/private/Config/Lexicon/CoreConfigLexicon.php b/lib/private/Config/Lexicon/CoreConfigLexicon.php deleted file mode 100644 index 34a0b883c54..00000000000 --- a/lib/private/Config/Lexicon/CoreConfigLexicon.php +++ /dev/null @@ -1,43 +0,0 @@ -<?php - -declare(strict_types=1); -/** - * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors - * SPDX-License-Identifier: AGPL-3.0-or-later - */ - -namespace OC\Config\Lexicon; - -use NCU\Config\Lexicon\ConfigLexiconEntry; -use NCU\Config\Lexicon\ConfigLexiconStrictness; -use NCU\Config\Lexicon\IConfigLexicon; -use NCU\Config\ValueType; - -/** - * ConfigLexicon for 'core' app/user configs - */ -class CoreConfigLexicon implements IConfigLexicon { - public function getStrictness(): ConfigLexiconStrictness { - return ConfigLexiconStrictness::IGNORE; - } - - /** - * @inheritDoc - * @return ConfigLexiconEntry[] - */ - public function getAppConfigs(): array { - return [ - new ConfigLexiconEntry('lastcron', ValueType::INT, 0, 'timestamp of last cron execution'), - ]; - } - - /** - * @inheritDoc - * @return ConfigLexiconEntry[] - */ - public function getUserConfigs(): array { - return [ - new ConfigLexiconEntry('lang', ValueType::STRING, null, 'language'), - ]; - } -} diff --git a/lib/private/Config/UserConfig.php b/lib/private/Config/UserConfig.php index fb0bf954f57..04ba0e29db0 100644 --- a/lib/private/Config/UserConfig.php +++ b/lib/private/Config/UserConfig.php @@ -11,15 +11,16 @@ namespace OC\Config; use Generator; use InvalidArgumentException; use JsonException; -use NCU\Config\Exceptions\IncorrectTypeException; -use NCU\Config\Exceptions\TypeConflictException; -use NCU\Config\Exceptions\UnknownKeyException; -use NCU\Config\IUserConfig; -use NCU\Config\Lexicon\ConfigLexiconEntry; -use NCU\Config\Lexicon\ConfigLexiconStrictness; -use NCU\Config\Lexicon\Preset; -use NCU\Config\ValueType; use OC\AppFramework\Bootstrap\Coordinator; +use OCP\Config\Exceptions\IncorrectTypeException; +use OCP\Config\Exceptions\TypeConflictException; +use OCP\Config\Exceptions\UnknownKeyException; +use OCP\Config\IUserConfig; +use OCP\Config\Lexicon\Entry; +use OCP\Config\Lexicon\ILexicon; +use OCP\Config\Lexicon\Preset; +use OCP\Config\Lexicon\Strictness; +use OCP\Config\ValueType; use OCP\DB\Exception as DBException; use OCP\DB\IResult; use OCP\DB\QueryBuilder\IQueryBuilder; @@ -64,7 +65,7 @@ class UserConfig implements IUserConfig { private array $fastLoaded = []; /** @var array<string, boolean> ['user_id' => bool] */ private array $lazyLoaded = []; - /** @var array<string, array{entries: array<string, ConfigLexiconEntry>, aliases: array<string, string>, strictness: ConfigLexiconStrictness}> ['app_id' => ['strictness' => ConfigLexiconStrictness, 'entries' => ['config_key' => ConfigLexiconEntry[]]] */ + /** @var array<string, array{entries: array<string, Entry>, aliases: array<string, string>, strictness: Strictness}> ['app_id' => ['strictness' => ConfigLexiconStrictness, 'entries' => ['config_key' => ConfigLexiconEntry[]]] */ private array $configLexiconDetails = []; private bool $ignoreLexiconAliases = false; private ?Preset $configLexiconPreset = null; @@ -1913,7 +1914,7 @@ class UserConfig implements IUserConfig { return true; } - /** @var ConfigLexiconEntry $configValue */ + /** @var Entry $configValue */ $configValue = $configDetails['entries'][$key]; if ($type === ValueType::MIXED) { // we overwrite if value was requested as mixed @@ -1954,7 +1955,7 @@ class UserConfig implements IUserConfig { * * The entry is converted to string to fit the expected type when managing default value */ - private function getSystemDefault(string $appId, ConfigLexiconEntry $configValue): ?string { + private function getSystemDefault(string $appId, Entry $configValue): ?string { $default = $this->config->getSystemValue('lexicon.default.userconfig', [])[$appId][$configValue->getKey()] ?? null; if ($default === null) { // no system default, using default default. @@ -1967,28 +1968,28 @@ class UserConfig implements IUserConfig { /** * manage ConfigLexicon behavior based on strictness set in IConfigLexicon * - * @see IConfigLexicon::getStrictness() - * @param ConfigLexiconStrictness|null $strictness + * @param Strictness|null $strictness * @param string $line * * @return bool TRUE if conflict can be fully ignored * @throws UnknownKeyException + *@see ILexicon::getStrictness() */ - private function applyLexiconStrictness(?ConfigLexiconStrictness $strictness, string $line = ''): bool { + private function applyLexiconStrictness(?Strictness $strictness, string $line = ''): bool { if ($strictness === null) { return true; } switch ($strictness) { - case ConfigLexiconStrictness::IGNORE: + case Strictness::IGNORE: return true; - case ConfigLexiconStrictness::NOTICE: + case Strictness::NOTICE: $this->logger->notice($line); return true; - case ConfigLexiconStrictness::WARNING: + case Strictness::WARNING: $this->logger->warning($line); return false; - case ConfigLexiconStrictness::EXCEPTION: + case Strictness::EXCEPTION: throw new UnknownKeyException($line); } @@ -1999,9 +2000,10 @@ class UserConfig implements IUserConfig { * extract details from registered $appId's config lexicon * * @param string $appId - * @internal * - * @return array{entries: array<string, ConfigLexiconEntry>, aliases: array<string, string>, strictness: ConfigLexiconStrictness} + * @return array{entries: array<string, Entry>, aliases: array<string, string>, strictness: Strictness} + *@internal + * */ public function getConfigDetailsFromLexicon(string $appId): array { if (!array_key_exists($appId, $this->configLexiconDetails)) { @@ -2018,14 +2020,14 @@ class UserConfig implements IUserConfig { $this->configLexiconDetails[$appId] = [ 'entries' => $entries, 'aliases' => $aliases, - 'strictness' => $configLexicon?->getStrictness() ?? ConfigLexiconStrictness::IGNORE + 'strictness' => $configLexicon?->getStrictness() ?? Strictness::IGNORE ]; } return $this->configLexiconDetails[$appId]; } - private function getLexiconEntry(string $appId, string $key): ?ConfigLexiconEntry { + private function getLexiconEntry(string $appId, string $key): ?Entry { return $this->getConfigDetailsFromLexicon($appId)['entries'][$key] ?? null; } diff --git a/lib/private/DB/ConnectionFactory.php b/lib/private/DB/ConnectionFactory.php index 4d286cb3068..d9b80b81992 100644 --- a/lib/private/DB/ConnectionFactory.php +++ b/lib/private/DB/ConnectionFactory.php @@ -121,21 +121,9 @@ class ConnectionFactory { case 'oci': $eventManager->addEventSubscriber(new OracleSessionInit); - // the driverOptions are unused in dbal and need to be mapped to the parameters - if (isset($connectionParams['driverOptions'])) { - $connectionParams = array_merge($connectionParams, $connectionParams['driverOptions']); - } - $host = $connectionParams['host']; - $port = $connectionParams['port'] ?? null; - $dbName = $connectionParams['dbname']; - - // we set the connect string as dbname and unset the host to coerce doctrine into using it as connect string - if ($host === '') { - $connectionParams['dbname'] = $dbName; // use dbname as easy connect name - } else { - $connectionParams['dbname'] = '//' . $host . (!empty($port) ? ":{$port}" : '') . '/' . $dbName; - } - unset($connectionParams['host']); + $connectionParams = $this->forceConnectionStringOracle($connectionParams); + $connectionParams['primary'] = $this->forceConnectionStringOracle($connectionParams['primary']); + $connectionParams['replica'] = array_map([$this, 'forceConnectionStringOracle'], $connectionParams['replica']); break; case 'sqlite3': @@ -265,4 +253,24 @@ class ConnectionFactory { return $params; } + + protected function forceConnectionStringOracle(array $connectionParams): array { + // the driverOptions are unused in dbal and need to be mapped to the parameters + if (isset($connectionParams['driverOptions'])) { + $connectionParams = array_merge($connectionParams, $connectionParams['driverOptions']); + } + $host = $connectionParams['host']; + $port = $connectionParams['port'] ?? null; + $dbName = $connectionParams['dbname']; + + // we set the connect string as dbname and unset the host to coerce doctrine into using it as connect string + if ($host === '') { + $connectionParams['dbname'] = $dbName; // use dbname as easy connect name + } else { + $connectionParams['dbname'] = '//' . $host . (!empty($port) ? ":{$port}" : '') . '/' . $dbName; + } + unset($connectionParams['host']); + + return $connectionParams; + } } diff --git a/lib/private/Federation/CloudIdManager.php b/lib/private/Federation/CloudIdManager.php index 7e7adda3d39..c599d9046a6 100644 --- a/lib/private/Federation/CloudIdManager.php +++ b/lib/private/Federation/CloudIdManager.php @@ -14,6 +14,7 @@ use OCP\EventDispatcher\Event; use OCP\EventDispatcher\IEventDispatcher; use OCP\Federation\ICloudId; use OCP\Federation\ICloudIdManager; +use OCP\Federation\ICloudIdResolver; use OCP\ICache; use OCP\ICacheFactory; use OCP\IURLGenerator; @@ -21,27 +22,19 @@ use OCP\IUserManager; use OCP\User\Events\UserChangedEvent; class CloudIdManager implements ICloudIdManager { - /** @var IManager */ - private $contactsManager; - /** @var IURLGenerator */ - private $urlGenerator; - /** @var IUserManager */ - private $userManager; private ICache $memCache; private ICache $displayNameCache; - /** @var array[] */ private array $cache = []; + /** @var ICloudIdResolver[] */ + private array $cloudIdResolvers = []; public function __construct( - IManager $contactsManager, - IURLGenerator $urlGenerator, - IUserManager $userManager, ICacheFactory $cacheFactory, IEventDispatcher $eventDispatcher, + private IManager $contactsManager, + private IURLGenerator $urlGenerator, + private IUserManager $userManager, ) { - $this->contactsManager = $contactsManager; - $this->urlGenerator = $urlGenerator; - $this->userManager = $userManager; $this->memCache = $cacheFactory->createDistributed('cloud_id_'); $this->displayNameCache = $cacheFactory->createDistributed('cloudid_name_'); $eventDispatcher->addListener(UserChangedEvent::class, [$this, 'handleUserEvent']); @@ -81,6 +74,12 @@ class CloudIdManager implements ICloudIdManager { public function resolveCloudId(string $cloudId): ICloudId { // TODO magic here to get the url and user instead of just splitting on @ + foreach ($this->cloudIdResolvers as $resolver) { + if ($resolver->isValidCloudId($cloudId)) { + return $resolver->resolveCloudId($cloudId); + } + } + if (!$this->isValidCloudId($cloudId)) { throw new \InvalidArgumentException('Invalid cloud id'); } @@ -251,6 +250,26 @@ class CloudIdManager implements ICloudIdManager { * @return bool */ public function isValidCloudId(string $cloudId): bool { - return str_contains($cloudId, '@'); + foreach ($this->cloudIdResolvers as $resolver) { + if ($resolver->isValidCloudId($cloudId)) { + return true; + } + } + + return strpos($cloudId, '@') !== false; + } + + public function createCloudId(string $id, string $user, string $remote, ?string $displayName = null): ICloudId { + return new CloudId($id, $user, $remote, $displayName); + } + + public function registerCloudIdResolver(ICloudIdResolver $resolver): void { + array_unshift($this->cloudIdResolvers, $resolver); + } + + public function unregisterCloudIdResolver(ICloudIdResolver $resolver): void { + if (($key = array_search($resolver, $this->cloudIdResolvers)) !== false) { + array_splice($this->cloudIdResolvers, $key, 1); + } } } diff --git a/lib/private/Files/Filesystem.php b/lib/private/Files/Filesystem.php index 48c069de0b9..8fe56cf060c 100644 --- a/lib/private/Files/Filesystem.php +++ b/lib/private/Files/Filesystem.php @@ -8,6 +8,7 @@ namespace OC\Files; use OC\Files\Mount\MountPoint; +use OC\Files\Storage\StorageFactory; use OC\User\NoUserException; use OCP\Cache\CappedMemoryCache; use OCP\EventDispatcher\IEventDispatcher; @@ -178,7 +179,9 @@ class Filesystem { } $mounts = self::getMountManager()->getAll(); - if (!self::getLoader()->addStorageWrapper($wrapperName, $wrapper, $priority, $mounts)) { + /** @var StorageFactory $loader */ + $loader = self::getLoader(); + if (!$loader->addStorageWrapper($wrapperName, $wrapper, $priority, $mounts)) { // do not re-wrap if storage with this name already existed return; } diff --git a/lib/private/Lockdown/Filesystem/NullStorage.php b/lib/private/Lockdown/Filesystem/NullStorage.php index 71a40d8da1e..fd952fae637 100644 --- a/lib/private/Lockdown/Filesystem/NullStorage.php +++ b/lib/private/Lockdown/Filesystem/NullStorage.php @@ -30,7 +30,7 @@ class NullStorage extends Common { } public function opendir(string $path): IteratorDirectory { - return new IteratorDirectory([]); + return new IteratorDirectory(); } public function is_dir(string $path): bool { diff --git a/lib/private/Server.php b/lib/private/Server.php index 1e1ba2a1ad3..171fee2afa1 100644 --- a/lib/private/Server.php +++ b/lib/private/Server.php @@ -8,7 +8,6 @@ namespace OC; use bantu\IniGetWrapper\IniGetWrapper; -use NCU\Config\IUserConfig; use NCU\Security\Signature\ISignatureManager; use OC\Accounts\AccountManager; use OC\App\AppManager; @@ -139,6 +138,7 @@ use OCP\BackgroundJob\IJobList; use OCP\Collaboration\Reference\IReferenceManager; use OCP\Command\IBus; use OCP\Comments\ICommentsManager; +use OCP\Config\IUserConfig; use OCP\Contacts\ContactsMenu\IActionFactory; use OCP\Contacts\ContactsMenu\IContactsStore; use OCP\Defaults; @@ -163,7 +163,6 @@ use OCP\Files\Storage\IStorageFactory; use OCP\Files\Template\ITemplateManager; use OCP\FilesMetadata\IFilesMetadataManager; use OCP\FullTextSearch\IFullTextSearchManager; -use OCP\GlobalScale\IConfig; use OCP\Group\ISubAdmin; use OCP\Http\Client\IClientService; use OCP\IAppConfig; @@ -1161,11 +1160,11 @@ class Server extends ServerContainer implements IServerContainer { $this->registerService(ICloudIdManager::class, function (ContainerInterface $c) { return new CloudIdManager( + $c->get(ICacheFactory::class), + $c->get(IEventDispatcher::class), $c->get(\OCP\Contacts\IManager::class), $c->get(IURLGenerator::class), $c->get(IUserManager::class), - $c->get(ICacheFactory::class), - $c->get(IEventDispatcher::class), ); }); diff --git a/lib/private/Share/Constants.php b/lib/private/Share/Constants.php index baff04fbc4a..c55caee6f0a 100644 --- a/lib/private/Share/Constants.php +++ b/lib/private/Share/Constants.php @@ -7,56 +7,7 @@ */ namespace OC\Share; -use OCP\Share\IShare; - class Constants { - /** - * @deprecated 17.0.0 - use IShare::TYPE_USER instead - */ - public const SHARE_TYPE_USER = 0; - /** - * @deprecated 17.0.0 - use IShare::TYPE_GROUP instead - */ - public const SHARE_TYPE_GROUP = 1; - // const SHARE_TYPE_USERGROUP = 2; // Internal type used by DefaultShareProvider - /** - * @deprecated 17.0.0 - use IShare::TYPE_LINK instead - */ - public const SHARE_TYPE_LINK = 3; - /** - * @deprecated 17.0.0 - use IShare::TYPE_EMAIL instead - */ - public const SHARE_TYPE_EMAIL = 4; - public const SHARE_TYPE_CONTACT = 5; // ToDo Check if it is still in use otherwise remove it - /** - * @deprecated 17.0.0 - use IShare::TYPE_REMOTE instead - */ - public const SHARE_TYPE_REMOTE = 6; - /** - * @deprecated 17.0.0 - use IShare::TYPE_CIRCLE instead - */ - public const SHARE_TYPE_CIRCLE = 7; - /** - * @deprecated 17.0.0 - use IShare::TYPE_GUEST instead - */ - public const SHARE_TYPE_GUEST = 8; - /** - * @deprecated 17.0.0 - use IShare::REMOTE_GROUP instead - */ - public const SHARE_TYPE_REMOTE_GROUP = 9; - /** - * @deprecated 17.0.0 - use IShare::TYPE_ROOM instead - */ - public const SHARE_TYPE_ROOM = 10; - // const SHARE_TYPE_USERROOM = 11; // Internal type used by RoomShareProvider - /** - * @deprecated 21.0.0 - use IShare::TYPE_DECK instead - */ - public const SHARE_TYPE_DECK = 12; - // const SHARE_TYPE_DECK_USER = 13; // Internal type used by DeckShareProvider - - // Note to developers: Do not add new share types here - public const FORMAT_NONE = -1; public const FORMAT_STATUSES = -2; public const FORMAT_SOURCES = -3; // ToDo Check if it is still in use otherwise remove it diff --git a/lib/private/Share20/DefaultShareProvider.php b/lib/private/Share20/DefaultShareProvider.php index e1eebe1e450..5300e6e1baa 100644 --- a/lib/private/Share20/DefaultShareProvider.php +++ b/lib/private/Share20/DefaultShareProvider.php @@ -13,12 +13,14 @@ use OC\Share20\Exception\BackendError; use OC\Share20\Exception\InvalidShare; use OC\Share20\Exception\ProviderException; use OC\User\LazyUser; +use OCA\Files_Sharing\AppInfo\Application; use OCP\AppFramework\Utility\ITimeFactory; use OCP\DB\QueryBuilder\IQueryBuilder; use OCP\Defaults; use OCP\Files\Folder; use OCP\Files\IRootFolder; use OCP\Files\Node; +use OCP\IConfig; use OCP\IDBConnection; use OCP\IGroupManager; use OCP\IL10N; @@ -43,9 +45,6 @@ use function str_starts_with; * @package OC\Share20 */ class DefaultShareProvider implements IShareProviderWithNotification, IShareProviderSupportsAccept, IShareProviderSupportsAllSharesInFolder { - // Special share type for user modified group shares - public const SHARE_TYPE_USERGROUP = 2; - public function __construct( private IDBConnection $dbConn, private IUserManager $userManager, @@ -58,6 +57,7 @@ class DefaultShareProvider implements IShareProviderWithNotification, IShareProv private ITimeFactory $timeFactory, private LoggerInterface $logger, private IManager $shareManager, + private IConfig $config, ) { } @@ -127,9 +127,7 @@ class DefaultShareProvider implements IShareProviderWithNotification, IShareProv $qb->setValue('expiration', $qb->createNamedParameter($expirationDate, 'datetime')); } - if (method_exists($share, 'getParent')) { - $qb->setValue('parent', $qb->createNamedParameter($share->getParent())); - } + $qb->setValue('parent', $qb->createNamedParameter($share->getParent())); $qb->setValue('hide_download', $qb->createNamedParameter($share->getHideDownload() ? 1 : 0, IQueryBuilder::PARAM_INT)); } else { @@ -284,7 +282,7 @@ class DefaultShareProvider implements IShareProviderWithNotification, IShareProv ->set('expiration', $qb->createNamedParameter($expirationDate, IQueryBuilder::PARAM_DATETIME_MUTABLE)) ->set('note', $qb->createNamedParameter($share->getNote())) ->set('label', $qb->createNamedParameter($share->getLabel())) - ->set('hide_download', $qb->createNamedParameter($share->getHideDownload() ? 1 : 0), IQueryBuilder::PARAM_INT) + ->set('hide_download', $qb->createNamedParameter($share->getHideDownload() ? 1 : 0, IQueryBuilder::PARAM_INT)) ->executeStatement(); } @@ -358,14 +356,7 @@ class DefaultShareProvider implements IShareProviderWithNotification, IShareProv return $share; } - /** - * Get all children of this share - * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in - * - * @param \OCP\Share\IShare $parent - * @return \OCP\Share\IShare[] - */ - public function getChildren(\OCP\Share\IShare $parent) { + public function getChildren(IShare $parent): array { $children = []; $qb = $this->dbConn->getQueryBuilder(); @@ -485,6 +476,15 @@ class DefaultShareProvider implements IShareProviderWithNotification, IShareProv protected function createUserSpecificGroupShare(IShare $share, string $recipient): int { $type = $share->getNodeType(); + $shareFolder = $this->config->getSystemValue('share_folder', '/'); + $allowCustomShareFolder = $this->config->getSystemValueBool('sharing.allow_custom_share_folder', true); + if ($allowCustomShareFolder) { + $shareFolder = $this->config->getUserValue($recipient, Application::APP_ID, 'share_folder', $shareFolder); + } + + $target = $shareFolder . '/' . $share->getNode()->getName(); + $target = \OC\Files\Filesystem::normalizePath($target); + $qb = $this->dbConn->getQueryBuilder(); $qb->insert('share') ->values([ @@ -496,7 +496,7 @@ class DefaultShareProvider implements IShareProviderWithNotification, IShareProv 'item_type' => $qb->createNamedParameter($type), 'item_source' => $qb->createNamedParameter($share->getNodeId()), 'file_source' => $qb->createNamedParameter($share->getNodeId()), - 'file_target' => $qb->createNamedParameter($share->getTarget()), + 'file_target' => $qb->createNamedParameter($target), 'permissions' => $qb->createNamedParameter($share->getPermissions()), 'stime' => $qb->createNamedParameter($share->getShareTime()->getTimestamp()), ])->executeStatement(); diff --git a/lib/private/Share20/LegacyHooks.php b/lib/private/Share20/LegacyHooks.php index 3bce0b9560a..d54c8e3203d 100644 --- a/lib/private/Share20/LegacyHooks.php +++ b/lib/private/Share20/LegacyHooks.php @@ -82,7 +82,7 @@ class LegacyHooks { 'itemSource' => $share->getNodeId(), 'shareType' => $shareType, 'shareWith' => $sharedWith, - 'itemparent' => method_exists($share, 'getParent') ? $share->getParent() : '', + 'itemparent' => $share->getParent(), 'uidOwner' => $share->getSharedBy(), 'fileSource' => $share->getNodeId(), 'fileTarget' => $share->getTarget() diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php index 01664c6a0a3..855bb173d56 100644 --- a/lib/private/Share20/Manager.php +++ b/lib/private/Share20/Manager.php @@ -7,11 +7,13 @@ */ namespace OC\Share20; +use OC\Core\AppInfo\ConfigLexicon; use OC\Files\Mount\MoveableMount; use OC\KnownUser\KnownUserService; use OC\Share20\Exception\ProviderException; use OCA\Files_Sharing\AppInfo\Application; use OCA\Files_Sharing\SharedStorage; +use OCA\ShareByMail\ShareByMailProvider; use OCP\EventDispatcher\IEventDispatcher; use OCP\Files\File; use OCP\Files\Folder; @@ -579,13 +581,10 @@ class Manager implements IManager { * @param IShare $share */ protected function setLinkParent(IShare $share) { - // No sense in checking if the method is not there. - if (method_exists($share, 'setParent')) { - $storage = $share->getNode()->getStorage(); - if ($storage->instanceOfStorage(SharedStorage::class)) { - /** @var \OCA\Files_Sharing\SharedStorage $storage */ - $share->setParent($storage->getShareId()); - } + $storage = $share->getNode()->getStorage(); + if ($storage->instanceOfStorage(SharedStorage::class)) { + /** @var \OCA\Files_Sharing\SharedStorage $storage */ + $share->setParent((int)$storage->getShareId()); } } @@ -704,12 +703,12 @@ class Manager implements IManager { } // Generate the target - $defaultShareFolder = $this->config->getSystemValue('share_folder', '/'); - $allowCustomShareFolder = $this->config->getSystemValueBool('sharing.allow_custom_share_folder', true); - if ($allowCustomShareFolder) { - $shareFolder = $this->config->getUserValue($share->getSharedWith(), Application::APP_ID, 'share_folder', $defaultShareFolder); - } else { - $shareFolder = $defaultShareFolder; + $shareFolder = $this->config->getSystemValue('share_folder', '/'); + if ($share->getShareType() === IShare::TYPE_USER) { + $allowCustomShareFolder = $this->config->getSystemValueBool('sharing.allow_custom_share_folder', true); + if ($allowCustomShareFolder) { + $shareFolder = $this->config->getUserValue($share->getSharedWith(), Application::APP_ID, 'share_folder', $shareFolder); + } } $target = $shareFolder . '/' . $share->getNode()->getName(); @@ -869,6 +868,7 @@ class Manager implements IManager { // Now update the share! $provider = $this->factory->getProviderForType($share->getShareType()); if ($share->getShareType() === IShare::TYPE_EMAIL) { + /** @var ShareByMailProvider $provider */ $share = $provider->update($share, $plainTextPassword); } else { $share = $provider->update($share); @@ -1006,7 +1006,6 @@ class Manager implements IManager { /** * Delete all the children of this share - * FIXME: remove once https://github.com/owncloud/core/pull/21660 is in * * @param IShare $share * @return IShare[] List of deleted shares @@ -1937,7 +1936,7 @@ class Manager implements IManager { } public function allowCustomTokens(): bool { - return $this->appConfig->getValueBool('core', 'shareapi_allow_custom_tokens', false); + return $this->appConfig->getValueBool('core', ConfigLexicon::SHARE_CUSTOM_TOKEN); } public function allowViewWithoutDownload(): bool { diff --git a/lib/private/Share20/Share.php b/lib/private/Share20/Share.php index 8caabb0898a..571efc8c4be 100644 --- a/lib/private/Share20/Share.php +++ b/lib/private/Share20/Share.php @@ -60,8 +60,7 @@ class Share implements IShare { private $sendPasswordByTalk = false; /** @var string */ private $token; - /** @var int */ - private $parent; + private ?int $parent = null; /** @var string */ private $target; /** @var \DateTime */ @@ -526,25 +525,12 @@ class Share implements IShare { return $this->token; } - /** - * Set the parent of this share - * - * @param int $parent - * @return IShare - * @deprecated 12.0.0 The new shares do not have parents. This is just here for legacy reasons. - */ - public function setParent($parent) { + public function setParent(int $parent): self { $this->parent = $parent; return $this; } - /** - * Get the parent of this share. - * - * @return int - * @deprecated 12.0.0 The new shares do not have parents. This is just here for legacy reasons. - */ - public function getParent() { + public function getParent(): ?int { return $this->parent; } diff --git a/lib/private/Template/JSConfigHelper.php b/lib/private/Template/JSConfigHelper.php index de9df04ae4b..07e557d0706 100644 --- a/lib/private/Template/JSConfigHelper.php +++ b/lib/private/Template/JSConfigHelper.php @@ -70,6 +70,8 @@ class JSConfigHelper { $userBackendAllowsPasswordConfirmation = $backend->canConfirmPassword($uid) && $this->canUserValidatePassword(); } elseif (isset($this->excludedUserBackEnds[$this->currentUser->getBackendClassName()])) { $userBackendAllowsPasswordConfirmation = false; + } else { + $userBackendAllowsPasswordConfirmation = $this->canUserValidatePassword(); } } else { $uid = null; |