aboutsummaryrefslogtreecommitdiffstats
path: root/lib/private
diff options
context:
space:
mode:
Diffstat (limited to 'lib/private')
-rw-r--r--lib/private/Accounts/Hooks.php1
-rw-r--r--lib/private/Activity/Event.php15
-rw-r--r--lib/private/Activity/EventMerger.php1
-rw-r--r--lib/private/Activity/Manager.php1
-rw-r--r--lib/private/AllConfig.php1
-rw-r--r--lib/private/App/AppManager.php49
-rw-r--r--lib/private/App/AppStore/AppNotFoundException.php13
-rw-r--r--lib/private/App/AppStore/Bundles/Bundle.php1
-rw-r--r--lib/private/App/AppStore/Bundles/BundleFetcher.php1
-rw-r--r--lib/private/App/AppStore/Bundles/EducationBundle.php1
-rw-r--r--lib/private/App/AppStore/Bundles/EnterpriseBundle.php1
-rw-r--r--lib/private/App/AppStore/Bundles/GroupwareBundle.php1
-rw-r--r--lib/private/App/AppStore/Bundles/PublicSectorBundle.php1
-rw-r--r--lib/private/App/AppStore/Bundles/SocialSharingBundle.php1
-rw-r--r--lib/private/App/AppStore/Fetcher/AppDiscoverFetcher.php1
-rw-r--r--lib/private/App/AppStore/Fetcher/AppFetcher.php5
-rw-r--r--lib/private/App/AppStore/Fetcher/CategoryFetcher.php1
-rw-r--r--lib/private/App/AppStore/Fetcher/Fetcher.php1
-rw-r--r--lib/private/App/AppStore/Version/Version.php1
-rw-r--r--lib/private/App/AppStore/Version/VersionParser.php1
-rw-r--r--lib/private/App/DependencyAnalyzer.php1
-rw-r--r--lib/private/App/InfoParser.php1
-rw-r--r--lib/private/App/Platform.php1
-rw-r--r--lib/private/App/PlatformRepository.php1
-rw-r--r--lib/private/AppConfig.php93
-rw-r--r--lib/private/AppFramework/Bootstrap/Coordinator.php16
-rw-r--r--lib/private/AppFramework/Bootstrap/RegistrationContext.php2
-rw-r--r--lib/private/AppFramework/DependencyInjection/DIContainer.php5
-rw-r--r--lib/private/AppFramework/Http.php4
-rw-r--r--lib/private/AppFramework/Middleware/FlowV2EphemeralSessionsMiddleware.php4
-rw-r--r--lib/private/AppFramework/Middleware/NotModifiedMiddleware.php2
-rw-r--r--lib/private/AppFramework/Middleware/OCSMiddleware.php1
-rw-r--r--lib/private/AppFramework/Middleware/PublicShare/Exceptions/NeedAuthenticationException.php1
-rw-r--r--lib/private/AppFramework/Middleware/PublicShare/PublicShareMiddleware.php1
-rw-r--r--lib/private/AppFramework/Middleware/Security/CORSMiddleware.php12
-rw-r--r--lib/private/AppFramework/Middleware/Security/Exceptions/AppNotEnabledException.php1
-rw-r--r--lib/private/AppFramework/Middleware/Security/Exceptions/LaxSameSiteCookieFailedException.php1
-rw-r--r--lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php5
-rw-r--r--lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php4
-rw-r--r--lib/private/AppFramework/Middleware/Security/SameSiteCookieMiddleware.php1
-rw-r--r--lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php4
-rw-r--r--lib/private/AppFramework/OCS/BaseResponse.php7
-rw-r--r--lib/private/AppFramework/OCS/V1Response.php1
-rw-r--r--lib/private/AppFramework/OCS/V2Response.php1
-rw-r--r--lib/private/AppFramework/Services/AppConfig.php4
-rw-r--r--lib/private/AppFramework/Utility/SimpleContainer.php39
-rw-r--r--lib/private/AppScriptDependency.php1
-rw-r--r--lib/private/AppScriptSort.php1
-rw-r--r--lib/private/Authentication/LoginCredentials/Credentials.php1
-rw-r--r--lib/private/Authentication/Token/TokenCleanupJob.php1
-rw-r--r--lib/private/Authentication/TwoFactorAuth/Manager.php4
-rw-r--r--lib/private/Avatar/UserAvatar.php4
-rw-r--r--lib/private/Blurhash/Listener/GenerateBlurhashMetadata.php5
-rw-r--r--lib/private/Calendar/Manager.php2
-rw-r--r--lib/private/Collaboration/AutoComplete/Manager.php1
-rw-r--r--lib/private/Collaboration/Collaborators/GroupPlugin.php1
-rw-r--r--lib/private/Collaboration/Collaborators/LookupPlugin.php1
-rw-r--r--lib/private/Collaboration/Collaborators/MailPlugin.php5
-rw-r--r--lib/private/Collaboration/Collaborators/RemoteGroupPlugin.php1
-rw-r--r--lib/private/Collaboration/Collaborators/RemotePlugin.php1
-rw-r--r--lib/private/Collaboration/Collaborators/Search.php1
-rw-r--r--lib/private/Collaboration/Collaborators/SearchResult.php1
-rw-r--r--lib/private/Collaboration/Collaborators/UserPlugin.php15
-rw-r--r--lib/private/Collaboration/Reference/File/FileReferenceEventListener.php5
-rw-r--r--lib/private/Collaboration/Resources/Collection.php4
-rw-r--r--lib/private/Color.php1
-rw-r--r--lib/private/Command/CronBus.php1
-rw-r--r--lib/private/Config/ConfigManager.php250
-rw-r--r--lib/private/Config/UserConfig.php95
-rw-r--r--lib/private/Console/Application.php4
-rw-r--r--lib/private/Contacts/ContactsMenu/ActionFactory.php1
-rw-r--r--lib/private/Contacts/ContactsMenu/Actions/LinkAction.php1
-rw-r--r--lib/private/Contacts/ContactsMenu/Manager.php1
-rw-r--r--lib/private/Contacts/ContactsMenu/Providers/EMailProvider.php1
-rw-r--r--lib/private/DB/Adapter.php20
-rw-r--r--lib/private/DB/AdapterOCI8.php2
-rw-r--r--lib/private/DB/AdapterPgSql.php2
-rw-r--r--lib/private/DB/Connection.php33
-rw-r--r--lib/private/DB/MigrationService.php1
-rw-r--r--lib/private/DB/MySqlTools.php5
-rw-r--r--lib/private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php1
-rw-r--r--lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php1
-rw-r--r--lib/private/DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php1
-rw-r--r--lib/private/DB/QueryBuilder/FunctionBuilder/PgSqlFunctionBuilder.php1
-rw-r--r--lib/private/DB/QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php1
-rw-r--r--lib/private/DB/QueryBuilder/Partitioned/PartitionedQueryBuilder.php23
-rw-r--r--lib/private/DB/SchemaWrapper.php1
-rw-r--r--lib/private/Diagnostics/Event.php1
-rw-r--r--lib/private/Diagnostics/EventLogger.php1
-rw-r--r--lib/private/Diagnostics/Query.php2
-rw-r--r--lib/private/DirectEditing/Manager.php1
-rw-r--r--lib/private/DirectEditing/Token.php1
-rw-r--r--lib/private/EmojiHelper.php26
-rw-r--r--lib/private/Encryption/EncryptionEventListener.php17
-rw-r--r--lib/private/Encryption/Keys/Storage.php1
-rw-r--r--lib/private/Encryption/Util.php4
-rw-r--r--lib/private/Federation/CloudFederationFactory.php1
-rw-r--r--lib/private/Federation/CloudFederationNotification.php1
-rw-r--r--lib/private/Federation/CloudFederationProviderManager.php4
-rw-r--r--lib/private/Federation/CloudFederationShare.php1
-rw-r--r--lib/private/Files/Cache/Cache.php4
-rw-r--r--lib/private/Files/Cache/QuerySearchHelper.php1
-rw-r--r--lib/private/Files/Cache/SearchBuilder.php1
-rw-r--r--lib/private/Files/Cache/Storage.php1
-rw-r--r--lib/private/Files/Cache/Wrapper/JailPropagator.php1
-rw-r--r--lib/private/Files/Config/CachedMountFileInfo.php1
-rw-r--r--lib/private/Files/Config/MountProviderCollection.php116
-rw-r--r--lib/private/Files/Config/UserMountCache.php6
-rw-r--r--lib/private/Files/Conversion/ConversionManager.php4
-rw-r--r--lib/private/Files/FilenameValidator.php2
-rw-r--r--lib/private/Files/Mount/ObjectHomeMountProvider.php110
-rw-r--r--lib/private/Files/Mount/RootMountProvider.php62
-rw-r--r--lib/private/Files/Node/Folder.php9
-rw-r--r--lib/private/Files/Node/LazyFolder.php4
-rw-r--r--lib/private/Files/Notify/Change.php1
-rw-r--r--lib/private/Files/Notify/RenameChange.php1
-rw-r--r--lib/private/Files/ObjectStore/Azure.php1
-rw-r--r--lib/private/Files/ObjectStore/ObjectStoreStorage.php20
-rw-r--r--lib/private/Files/ObjectStore/PrimaryObjectStoreConfig.php140
-rw-r--r--lib/private/Files/ObjectStore/S3.php1
-rw-r--r--lib/private/Files/ObjectStore/S3ConfigTrait.php4
-rw-r--r--lib/private/Files/ObjectStore/S3ConnectionTrait.php5
-rw-r--r--lib/private/Files/ObjectStore/S3ObjectTrait.php60
-rw-r--r--lib/private/Files/ObjectStore/S3Signature.php1
-rw-r--r--lib/private/Files/ObjectStore/StorageObjectStore.php1
-rw-r--r--lib/private/Files/Search/QueryOptimizer/FlattenNestedBool.php4
-rw-r--r--lib/private/Files/Search/QueryOptimizer/FlattenSingleArgumentBinaryOperation.php10
-rw-r--r--lib/private/Files/Search/QueryOptimizer/OrEqualsToIn.php4
-rw-r--r--lib/private/Files/Search/QueryOptimizer/PathPrefixOptimizer.php6
-rw-r--r--lib/private/Files/Search/QueryOptimizer/SplitLargeIn.php6
-rw-r--r--lib/private/Files/Search/SearchBinaryOperator.php1
-rw-r--r--lib/private/Files/Search/SearchOrder.php1
-rw-r--r--lib/private/Files/Search/SearchQuery.php1
-rw-r--r--lib/private/Files/SetupManager.php18
-rw-r--r--lib/private/Files/SimpleFS/SimpleFile.php1
-rw-r--r--lib/private/Files/SimpleFS/SimpleFolder.php1
-rw-r--r--lib/private/Files/Storage/Common.php4
-rw-r--r--lib/private/Files/Storage/DAV.php10
-rw-r--r--lib/private/Files/Storage/Wrapper/Encryption.php28
-rw-r--r--lib/private/Files/Stream/Encryption.php8
-rw-r--r--lib/private/Files/Stream/SeekableHttpStream.php1
-rw-r--r--lib/private/Files/View.php4
-rw-r--r--lib/private/FullTextSearch/FullTextSearchManager.php6
-rw-r--r--lib/private/FullTextSearch/Model/IndexDocument.php4
-rw-r--r--lib/private/GlobalScale/Config.php1
-rw-r--r--lib/private/Group/Group.php2
-rw-r--r--lib/private/Hooks/BasicEmitter.php1
-rw-r--r--lib/private/Hooks/Emitter.php1
-rw-r--r--lib/private/Hooks/EmitterTrait.php1
-rw-r--r--lib/private/Hooks/PublicEmitter.php1
-rw-r--r--lib/private/Http/Client/Client.php4
-rw-r--r--lib/private/Http/Client/Response.php39
-rw-r--r--lib/private/Image.php10
-rw-r--r--lib/private/Installer.php9
-rw-r--r--lib/private/L10N/Factory.php12
-rw-r--r--lib/private/L10N/LanguageNotFoundException.php1
-rwxr-xr-xlib/private/LargeFileHelper.php1
-rw-r--r--lib/private/Lockdown/Filesystem/NullCache.php32
-rw-r--r--lib/private/Lockdown/Filesystem/NullStorage.php1
-rw-r--r--lib/private/Lockdown/LockdownManager.php1
-rw-r--r--lib/private/Log.php6
-rw-r--r--lib/private/Log/ErrorHandler.php4
-rw-r--r--lib/private/Log/ExceptionSerializer.php1
-rw-r--r--lib/private/Log/LogDetails.php1
-rw-r--r--lib/private/Log/LogFactory.php1
-rw-r--r--lib/private/Log/Rotate.php10
-rw-r--r--lib/private/Log/Systemdlog.php1
-rw-r--r--lib/private/Mail/Message.php4
-rw-r--r--lib/private/Mail/Provider/Manager.php18
-rw-r--r--lib/private/Memcache/APCu.php1
-rw-r--r--lib/private/Memcache/ArrayCache.php1
-rw-r--r--lib/private/Memcache/CADTrait.php1
-rw-r--r--lib/private/Memcache/CASTrait.php1
-rw-r--r--lib/private/Memcache/Cache.php1
-rw-r--r--lib/private/Memcache/Factory.php5
-rw-r--r--lib/private/Memcache/Memcached.php5
-rw-r--r--lib/private/Memcache/NullCache.php1
-rw-r--r--lib/private/Memcache/Redis.php1
-rw-r--r--lib/private/Migration/ConsoleOutput.php1
-rw-r--r--lib/private/Migration/NullOutput.php1
-rw-r--r--lib/private/Migration/SimpleOutput.php1
-rw-r--r--lib/private/Notification/Manager.php4
-rw-r--r--lib/private/Notification/Notification.php18
-rw-r--r--lib/private/OCM/Model/OCMProvider.php94
-rw-r--r--lib/private/OCM/OCMDiscoveryService.php8
-rw-r--r--lib/private/Preview/Bundled.php1
-rw-r--r--lib/private/Preview/GeneratorHelper.php1
-rw-r--r--lib/private/Preview/Imaginary.php1
-rw-r--r--lib/private/Preview/Krita.php1
-rw-r--r--lib/private/Preview/MimeIconProvider.php5
-rw-r--r--lib/private/Preview/Movie.php2
-rw-r--r--lib/private/Preview/SGI.php1
-rw-r--r--lib/private/Preview/TGA.php1
-rw-r--r--lib/private/PreviewManager.php16
-rw-r--r--lib/private/PreviewNotAvailableException.php1
-rw-r--r--lib/private/Profile/ProfileManager.php4
-rw-r--r--lib/private/RedisFactory.php8
-rw-r--r--lib/private/Remote/Api/ApiBase.php1
-rw-r--r--lib/private/Remote/Api/ApiCollection.php1
-rw-r--r--lib/private/Remote/Api/ApiFactory.php1
-rw-r--r--lib/private/Remote/Api/NotFoundException.php1
-rw-r--r--lib/private/Remote/Api/OCS.php1
-rw-r--r--lib/private/Remote/Credentials.php1
-rw-r--r--lib/private/Remote/Instance.php9
-rw-r--r--lib/private/Remote/InstanceFactory.php1
-rw-r--r--lib/private/Remote/User.php1
-rw-r--r--lib/private/Repair/AddCleanupUpdaterBackupsJob.php1
-rw-r--r--lib/private/Repair/AddMetadataGenerationJob.php1
-rw-r--r--lib/private/Repair/ClearFrontendCaches.php1
-rw-r--r--lib/private/Repair/ClearGeneratedAvatarCache.php1
-rw-r--r--lib/private/Repair/ClearGeneratedAvatarCacheJob.php1
-rw-r--r--lib/private/Repair/Collation.php20
-rw-r--r--lib/private/Repair/ConfigKeyMigration.php29
-rw-r--r--lib/private/Repair/MoveUpdaterStepFile.php1
-rw-r--r--lib/private/Repair/NC13/AddLogRotateJob.php1
-rw-r--r--lib/private/Repair/NC21/AddCheckForUserCertificatesJob.php1
-rw-r--r--lib/private/Repair/Owncloud/CleanPreviews.php1
-rw-r--r--lib/private/Repair/Owncloud/DropAccountTermsTable.php1
-rw-r--r--lib/private/Repair/Owncloud/MoveAvatars.php1
-rw-r--r--lib/private/Repair/Owncloud/SaveAccountsTableData.php1
-rw-r--r--lib/private/Repair/Owncloud/UpdateLanguageCodes.php1
-rw-r--r--lib/private/Repair/RepairMimeTypes.php2
-rw-r--r--lib/private/Route/CachingRouter.php100
-rw-r--r--lib/private/Route/Route.php10
-rw-r--r--lib/private/Route/Router.php60
-rw-r--r--lib/private/Security/Bruteforce/Throttler.php26
-rw-r--r--lib/private/Security/Hasher.php8
-rw-r--r--lib/private/Security/Signature/Model/SignedRequest.php4
-rw-r--r--lib/private/Security/VerificationToken/VerificationToken.php6
-rw-r--r--lib/private/Server.php34
-rw-r--r--lib/private/Session/CryptoWrapper.php2
-rw-r--r--lib/private/Settings/DeclarativeManager.php71
-rw-r--r--lib/private/Settings/Manager.php1
-rw-r--r--lib/private/Settings/Section.php1
-rw-r--r--lib/private/Setup.php89
-rw-r--r--lib/private/Setup/AbstractDatabase.php5
-rw-r--r--lib/private/Setup/MySQL.php4
-rw-r--r--lib/private/Setup/OCI.php2
-rw-r--r--lib/private/Setup/PostgreSQL.php5
-rw-r--r--lib/private/Setup/Sqlite.php2
-rw-r--r--lib/private/Share/Share.php4
-rw-r--r--lib/private/Share20/Exception/BackendError.php1
-rw-r--r--lib/private/Share20/Exception/InvalidShare.php1
-rw-r--r--lib/private/Share20/LegacyHooks.php7
-rw-r--r--lib/private/Share20/Manager.php68
-rw-r--r--lib/private/Share20/ProviderFactory.php6
-rw-r--r--lib/private/Share20/Share.php25
-rw-r--r--lib/private/Share20/ShareAttributes.php4
-rw-r--r--lib/private/Share20/ShareHelper.php1
-rw-r--r--lib/private/Support/Subscription/Registry.php4
-rw-r--r--lib/private/TaskProcessing/SynchronousBackgroundJob.php4
-rw-r--r--lib/private/Teams/TeamManager.php1
-rw-r--r--lib/private/Template/JSCombiner.php1
-rw-r--r--lib/private/TemplateLayout.php2
-rw-r--r--lib/private/URLGenerator.php6
-rw-r--r--lib/private/Updater.php12
-rw-r--r--lib/private/Updater/VersionCheck.php17
-rw-r--r--lib/private/User/LazyUser.php4
-rw-r--r--lib/private/User/Listeners/UserChangedListener.php2
-rw-r--r--lib/private/User/Manager.php9
-rw-r--r--lib/private/User/Session.php21
-rw-r--r--lib/private/User/User.php13
-rw-r--r--lib/private/legacy/OC_App.php45
-rw-r--r--lib/private/legacy/OC_Defaults.php6
-rw-r--r--lib/private/legacy/OC_Helper.php2
-rw-r--r--lib/private/legacy/OC_Response.php1
-rw-r--r--lib/private/legacy/OC_Util.php9
267 files changed, 1827 insertions, 858 deletions
diff --git a/lib/private/Accounts/Hooks.php b/lib/private/Accounts/Hooks.php
index 0235879e8e7..12f2b4777f8 100644
--- a/lib/private/Accounts/Hooks.php
+++ b/lib/private/Accounts/Hooks.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Activity/Event.php b/lib/private/Activity/Event.php
index 54274acea6b..39cdc12b3fb 100644
--- a/lib/private/Activity/Event.php
+++ b/lib/private/Activity/Event.php
@@ -415,8 +415,7 @@ class Event implements IEvent {
public function isValid(): bool {
return
$this->isValidCommon()
- &&
- $this->getSubject() !== ''
+ && $this->getSubject() !== ''
;
}
@@ -443,20 +442,16 @@ class Event implements IEvent {
return
$this->isValidCommon()
- &&
- $this->getParsedSubject() !== ''
+ && $this->getParsedSubject() !== ''
;
}
protected function isValidCommon(): bool {
return
$this->getApp() !== ''
- &&
- $this->getType() !== ''
- &&
- $this->getAffectedUser() !== ''
- &&
- $this->getTimestamp() !== 0
+ && $this->getType() !== ''
+ && $this->getAffectedUser() !== ''
+ && $this->getTimestamp() !== 0
/**
* Disabled for BC with old activities
* &&
diff --git a/lib/private/Activity/EventMerger.php b/lib/private/Activity/EventMerger.php
index 504f9088f24..0e7d318103a 100644
--- a/lib/private/Activity/EventMerger.php
+++ b/lib/private/Activity/EventMerger.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Activity/Manager.php b/lib/private/Activity/Manager.php
index 5c306fe6399..4e10f8a0c1a 100644
--- a/lib/private/Activity/Manager.php
+++ b/lib/private/Activity/Manager.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/lib/private/AllConfig.php b/lib/private/AllConfig.php
index 72af6c960a5..8a6bb5a4723 100644
--- a/lib/private/AllConfig.php
+++ b/lib/private/AllConfig.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/lib/private/App/AppManager.php b/lib/private/App/AppManager.php
index f6494fa946d..1911bce12bf 100644
--- a/lib/private/App/AppManager.php
+++ b/lib/private/App/AppManager.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@@ -8,6 +9,7 @@ namespace OC\App;
use OC\AppConfig;
use OC\AppFramework\Bootstrap\Coordinator;
+use OC\Config\ConfigManager;
use OCP\Activity\IManager as IActivityManager;
use OCP\App\AppPathNotFoundException;
use OCP\App\Events\AppDisableEvent;
@@ -18,6 +20,7 @@ use OCP\Collaboration\AutoComplete\IManager as IAutoCompleteManager;
use OCP\Collaboration\Collaborators\ISearch as ICollaboratorSearch;
use OCP\Diagnostics\IEventLogger;
use OCP\EventDispatcher\IEventDispatcher;
+use OCP\IAppConfig;
use OCP\ICacheFactory;
use OCP\IConfig;
use OCP\IGroup;
@@ -26,6 +29,7 @@ use OCP\INavigationManager;
use OCP\IURLGenerator;
use OCP\IUser;
use OCP\IUserSession;
+use OCP\Server;
use OCP\ServerVersion;
use OCP\Settings\IManager as ISettingsManager;
use Psr\Log\LoggerInterface;
@@ -81,12 +85,13 @@ class AppManager implements IAppManager {
private IEventDispatcher $dispatcher,
private LoggerInterface $logger,
private ServerVersion $serverVersion,
+ private ConfigManager $configManager,
) {
}
private function getNavigationManager(): INavigationManager {
if ($this->navigationManager === null) {
- $this->navigationManager = \OCP\Server::get(INavigationManager::class);
+ $this->navigationManager = Server::get(INavigationManager::class);
}
return $this->navigationManager;
}
@@ -112,7 +117,7 @@ class AppManager implements IAppManager {
if (!$this->config->getSystemValueBool('installed', false)) {
throw new \Exception('Nextcloud is not installed yet, AppConfig is not available');
}
- $this->appConfig = \OCP\Server::get(AppConfig::class);
+ $this->appConfig = Server::get(AppConfig::class);
return $this->appConfig;
}
@@ -123,7 +128,7 @@ class AppManager implements IAppManager {
if (!$this->config->getSystemValueBool('installed', false)) {
throw new \Exception('Nextcloud is not installed yet, AppConfig is not available');
}
- $this->urlGenerator = \OCP\Server::get(IURLGenerator::class);
+ $this->urlGenerator = Server::get(IURLGenerator::class);
return $this->urlGenerator;
}
@@ -134,7 +139,8 @@ class AppManager implements IAppManager {
*/
private function getEnabledAppsValues(): array {
if (!$this->enabledAppsCache) {
- $values = $this->getAppConfig()->getValues(false, 'enabled');
+ /** @var array<string,string> */
+ $values = $this->getAppConfig()->searchValues('enabled', false, IAppConfig::VALUE_STRING);
$alwaysEnabledApps = $this->getAlwaysEnabledApps();
foreach ($alwaysEnabledApps as $appId) {
@@ -185,9 +191,9 @@ class AppManager implements IAppManager {
if (is_resource($dh)) {
while (($file = readdir($dh)) !== false) {
if (
- $file[0] != '.' &&
- is_dir($apps_dir['path'] . '/' . $file) &&
- is_file($apps_dir['path'] . '/' . $file . '/appinfo/info.xml')
+ $file[0] != '.'
+ && is_dir($apps_dir['path'] . '/' . $file)
+ && is_file($apps_dir['path'] . '/' . $file . '/appinfo/info.xml')
) {
$apps[] = $file;
}
@@ -202,7 +208,7 @@ class AppManager implements IAppManager {
* List all apps enabled for a user
*
* @param \OCP\IUser $user
- * @return string[]
+ * @return list<string>
*/
public function getEnabledAppsForUser(IUser $user) {
$apps = $this->getEnabledAppsValues();
@@ -457,7 +463,7 @@ class AppManager implements IAppManager {
]);
}
- $coordinator = \OCP\Server::get(Coordinator::class);
+ $coordinator = Server::get(Coordinator::class);
$coordinator->bootApp($app);
$eventLogger->start("bootstrap:load_app:$app:info", "Load info.xml for $app and register any services defined in it");
@@ -507,8 +513,8 @@ class AppManager implements IAppManager {
if (!empty($info['collaboration']['plugins'])) {
// deal with one or many plugin entries
- $plugins = isset($info['collaboration']['plugins']['plugin']['@value']) ?
- [$info['collaboration']['plugins']['plugin']] : $info['collaboration']['plugins']['plugin'];
+ $plugins = isset($info['collaboration']['plugins']['plugin']['@value'])
+ ? [$info['collaboration']['plugins']['plugin']] : $info['collaboration']['plugins']['plugin'];
$collaboratorSearch = null;
$autoCompleteManager = null;
foreach ($plugins as $plugin) {
@@ -545,11 +551,16 @@ class AppManager implements IAppManager {
* @param string $appId
* @param bool $forceEnable
* @throws AppPathNotFoundException
+ * @throws \InvalidArgumentException if the application is not installed yet
*/
public function enableApp(string $appId, bool $forceEnable = false): void {
// Check if app exists
$this->getAppPath($appId);
+ if ($this->config->getAppValue($appId, 'installed_version', '') === '') {
+ throw new \InvalidArgumentException("$appId is not installed, cannot be enabled.");
+ }
+
if ($forceEnable) {
$this->overwriteNextcloudRequirement($appId);
}
@@ -561,6 +572,8 @@ class AppManager implements IAppManager {
ManagerEvent::EVENT_APP_ENABLE, $appId
));
$this->clearAppsCache();
+
+ $this->configManager->migrateConfigLexiconKeys($appId);
}
/**
@@ -596,6 +609,10 @@ class AppManager implements IAppManager {
throw new \InvalidArgumentException("$appId can't be enabled for groups.");
}
+ if ($this->config->getAppValue($appId, 'installed_version', '') === '') {
+ throw new \InvalidArgumentException("$appId is not installed, cannot be enabled.");
+ }
+
if ($forceEnable) {
$this->overwriteNextcloudRequirement($appId);
}
@@ -615,6 +632,8 @@ class AppManager implements IAppManager {
ManagerEvent::EVENT_APP_ENABLE_FOR_GROUPS, $appId, $groups
));
$this->clearAppsCache();
+
+ $this->configManager->migrateConfigLexiconKeys($appId);
}
/**
@@ -775,8 +794,8 @@ class AppManager implements IAppManager {
*
* @return array<string, string>
*/
- public function getAppInstalledVersions(): array {
- return $this->getAppConfig()->getAppInstalledVersions();
+ public function getAppInstalledVersions(bool $onlyEnabled = false): array {
+ return $this->getAppConfig()->getAppInstalledVersions($onlyEnabled);
}
/**
@@ -812,6 +831,10 @@ class AppManager implements IAppManager {
}
private function isAlwaysEnabled(string $appId): bool {
+ if ($appId === 'core') {
+ return true;
+ }
+
$alwaysEnabled = $this->getAlwaysEnabledApps();
return in_array($appId, $alwaysEnabled, true);
}
diff --git a/lib/private/App/AppStore/AppNotFoundException.php b/lib/private/App/AppStore/AppNotFoundException.php
new file mode 100644
index 00000000000..79ceebb4423
--- /dev/null
+++ b/lib/private/App/AppStore/AppNotFoundException.php
@@ -0,0 +1,13 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OC\App\AppStore;
+
+class AppNotFoundException extends \Exception {
+}
diff --git a/lib/private/App/AppStore/Bundles/Bundle.php b/lib/private/App/AppStore/Bundles/Bundle.php
index 62f09b82f79..1443be81e92 100644
--- a/lib/private/App/AppStore/Bundles/Bundle.php
+++ b/lib/private/App/AppStore/Bundles/Bundle.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/App/AppStore/Bundles/BundleFetcher.php b/lib/private/App/AppStore/Bundles/BundleFetcher.php
index 01325699e2c..4ff53b0c70b 100644
--- a/lib/private/App/AppStore/Bundles/BundleFetcher.php
+++ b/lib/private/App/AppStore/Bundles/BundleFetcher.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/App/AppStore/Bundles/EducationBundle.php b/lib/private/App/AppStore/Bundles/EducationBundle.php
index 6770d4a7091..23681ec7416 100644
--- a/lib/private/App/AppStore/Bundles/EducationBundle.php
+++ b/lib/private/App/AppStore/Bundles/EducationBundle.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/App/AppStore/Bundles/EnterpriseBundle.php b/lib/private/App/AppStore/Bundles/EnterpriseBundle.php
index 88a9ec60a00..fc2d43e0388 100644
--- a/lib/private/App/AppStore/Bundles/EnterpriseBundle.php
+++ b/lib/private/App/AppStore/Bundles/EnterpriseBundle.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/App/AppStore/Bundles/GroupwareBundle.php b/lib/private/App/AppStore/Bundles/GroupwareBundle.php
index 7c7b74ff53d..93fa70268cd 100644
--- a/lib/private/App/AppStore/Bundles/GroupwareBundle.php
+++ b/lib/private/App/AppStore/Bundles/GroupwareBundle.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/App/AppStore/Bundles/PublicSectorBundle.php b/lib/private/App/AppStore/Bundles/PublicSectorBundle.php
index 158d525bdcc..106a6353029 100644
--- a/lib/private/App/AppStore/Bundles/PublicSectorBundle.php
+++ b/lib/private/App/AppStore/Bundles/PublicSectorBundle.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/App/AppStore/Bundles/SocialSharingBundle.php b/lib/private/App/AppStore/Bundles/SocialSharingBundle.php
index 7f925688ca3..40f0fb15977 100644
--- a/lib/private/App/AppStore/Bundles/SocialSharingBundle.php
+++ b/lib/private/App/AppStore/Bundles/SocialSharingBundle.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/App/AppStore/Fetcher/AppDiscoverFetcher.php b/lib/private/App/AppStore/Fetcher/AppDiscoverFetcher.php
index 2537d1dcec9..8389e525750 100644
--- a/lib/private/App/AppStore/Fetcher/AppDiscoverFetcher.php
+++ b/lib/private/App/AppStore/Fetcher/AppDiscoverFetcher.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/App/AppStore/Fetcher/AppFetcher.php b/lib/private/App/AppStore/Fetcher/AppFetcher.php
index 352646c3f5e..bbf4b00245b 100644
--- a/lib/private/App/AppStore/Fetcher/AppFetcher.php
+++ b/lib/private/App/AppStore/Fetcher/AppFetcher.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -78,8 +79,8 @@ class AppFetcher extends Fetcher {
$minServerVersion = $serverVersion->getMinimumVersion();
$maxServerVersion = $serverVersion->getMaximumVersion();
$minFulfilled = $this->compareVersion->isCompatible($ncVersion, $minServerVersion, '>=');
- $maxFulfilled = $maxServerVersion !== '' &&
- $this->compareVersion->isCompatible($ncVersion, $maxServerVersion, '<=');
+ $maxFulfilled = $maxServerVersion !== ''
+ && $this->compareVersion->isCompatible($ncVersion, $maxServerVersion, '<=');
$isPhpCompatible = true;
if (($release['rawPhpVersionSpec'] ?? '*') !== '*') {
$phpVersion = $versionParser->getVersion($release['rawPhpVersionSpec']);
diff --git a/lib/private/App/AppStore/Fetcher/CategoryFetcher.php b/lib/private/App/AppStore/Fetcher/CategoryFetcher.php
index d72f8fa7e24..d7857d41bee 100644
--- a/lib/private/App/AppStore/Fetcher/CategoryFetcher.php
+++ b/lib/private/App/AppStore/Fetcher/CategoryFetcher.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/App/AppStore/Fetcher/Fetcher.php b/lib/private/App/AppStore/Fetcher/Fetcher.php
index f998c9e2023..2e949fedb51 100644
--- a/lib/private/App/AppStore/Fetcher/Fetcher.php
+++ b/lib/private/App/AppStore/Fetcher/Fetcher.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/App/AppStore/Version/Version.php b/lib/private/App/AppStore/Version/Version.php
index b7e679d250a..2d169a291f1 100644
--- a/lib/private/App/AppStore/Version/Version.php
+++ b/lib/private/App/AppStore/Version/Version.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/App/AppStore/Version/VersionParser.php b/lib/private/App/AppStore/Version/VersionParser.php
index 59715c8d385..8976f28837f 100644
--- a/lib/private/App/AppStore/Version/VersionParser.php
+++ b/lib/private/App/AppStore/Version/VersionParser.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/App/DependencyAnalyzer.php b/lib/private/App/DependencyAnalyzer.php
index 72b38ca12c7..1e56612132b 100644
--- a/lib/private/App/DependencyAnalyzer.php
+++ b/lib/private/App/DependencyAnalyzer.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/lib/private/App/InfoParser.php b/lib/private/App/InfoParser.php
index 6610121f446..e7a75afdf0b 100644
--- a/lib/private/App/InfoParser.php
+++ b/lib/private/App/InfoParser.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/lib/private/App/Platform.php b/lib/private/App/Platform.php
index c2c059220c8..80e4cefed64 100644
--- a/lib/private/App/Platform.php
+++ b/lib/private/App/Platform.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/lib/private/App/PlatformRepository.php b/lib/private/App/PlatformRepository.php
index 6d9a9cd011a..faed8b07feb 100644
--- a/lib/private/App/PlatformRepository.php
+++ b/lib/private/App/PlatformRepository.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/lib/private/AppConfig.php b/lib/private/AppConfig.php
index a8a6f689ffa..476adbb93e0 100644
--- a/lib/private/AppConfig.php
+++ b/lib/private/AppConfig.php
@@ -15,6 +15,7 @@ use NCU\Config\Lexicon\ConfigLexiconEntry;
use NCU\Config\Lexicon\ConfigLexiconStrictness;
use NCU\Config\Lexicon\IConfigLexicon;
use OC\AppFramework\Bootstrap\Coordinator;
+use OC\Config\ConfigManager;
use OCP\DB\Exception as DBException;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Exceptions\AppConfigIncorrectTypeException;
@@ -24,6 +25,7 @@ use OCP\IAppConfig;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\Security\ICrypto;
+use OCP\Server;
use Psr\Log\LoggerInterface;
/**
@@ -59,8 +61,9 @@ class AppConfig implements IAppConfig {
private array $valueTypes = []; // type for all config values
private bool $fastLoaded = false;
private bool $lazyLoaded = false;
- /** @var array<array-key, array{entries: array<array-key, ConfigLexiconEntry>, strictness: ConfigLexiconStrictness}> ['app_id' => ['strictness' => ConfigLexiconStrictness, 'entries' => ['config_key' => ConfigLexiconEntry[]]] */
+ /** @var array<string, array{entries: array<string, ConfigLexiconEntry>, aliases: array<string, string>, strictness: ConfigLexiconStrictness}> ['app_id' => ['strictness' => ConfigLexiconStrictness, 'entries' => ['config_key' => ConfigLexiconEntry[]]] */
private array $configLexiconDetails = [];
+ private bool $ignoreLexiconAliases = false;
/** @var ?array<string, string> */
private ?array $appVersionsCache = null;
@@ -117,6 +120,7 @@ class AppConfig implements IAppConfig {
public function hasKey(string $app, string $key, ?bool $lazy = false): bool {
$this->assertParams($app, $key);
$this->loadConfig($app, $lazy);
+ $this->matchAndApplyLexiconDefinition($app, $key);
if ($lazy === null) {
$appCache = $this->getAllValues($app);
@@ -142,6 +146,7 @@ class AppConfig implements IAppConfig {
public function isSensitive(string $app, string $key, ?bool $lazy = false): bool {
$this->assertParams($app, $key);
$this->loadConfig(null, $lazy);
+ $this->matchAndApplyLexiconDefinition($app, $key);
if (!isset($this->valueTypes[$app][$key])) {
throw new AppConfigUnknownKeyException('unknown config key');
@@ -162,6 +167,9 @@ class AppConfig implements IAppConfig {
* @since 29.0.0
*/
public function isLazy(string $app, string $key): bool {
+ $this->assertParams($app, $key);
+ $this->matchAndApplyLexiconDefinition($app, $key);
+
// there is a huge probability the non-lazy config are already loaded
if ($this->hasKey($app, $key, false)) {
return false;
@@ -284,7 +292,7 @@ class AppConfig implements IAppConfig {
): string {
try {
$lazy = ($lazy === null) ? $this->isLazy($app, $key) : $lazy;
- } catch (AppConfigUnknownKeyException $e) {
+ } catch (AppConfigUnknownKeyException) {
return $default;
}
@@ -429,6 +437,7 @@ class AppConfig implements IAppConfig {
int $type,
): string {
$this->assertParams($app, $key, valueType: $type);
+ $origKey = $key;
if (!$this->matchAndApplyLexiconDefinition($app, $key, $lazy, $type, $default)) {
return $default; // returns default if strictness of lexicon is set to WARNING (block and report)
}
@@ -469,6 +478,14 @@ class AppConfig implements IAppConfig {
$value = $this->crypto->decrypt(substr($value, self::ENCRYPTION_PREFIX_LENGTH));
}
+ // in case the key was modified while running matchAndApplyLexiconDefinition() we are
+ // interested to check options in case a modification of the value is needed
+ // ie inverting value from previous key when using lexicon option RENAME_INVERT_BOOLEAN
+ if ($origKey !== $key && $type === self::VALUE_BOOL) {
+ $configManager = Server::get(ConfigManager::class);
+ $value = ($configManager->convertToBool($value, $this->getLexiconEntry($app, $key))) ? '1' : '0';
+ }
+
return $value;
}
@@ -798,8 +815,8 @@ class AppConfig implements IAppConfig {
* we only accept a different type from the one stored in database
* if the one stored in database is not-defined (VALUE_MIXED)
*/
- if (!$this->isTyped(self::VALUE_MIXED, $currType) &&
- ($type | self::VALUE_SENSITIVE) !== ($currType | self::VALUE_SENSITIVE)) {
+ if (!$this->isTyped(self::VALUE_MIXED, $currType)
+ && ($type | self::VALUE_SENSITIVE) !== ($currType | self::VALUE_SENSITIVE)) {
try {
$currType = $this->convertTypeToString($currType);
$type = $this->convertTypeToString($type);
@@ -863,7 +880,8 @@ class AppConfig implements IAppConfig {
public function updateType(string $app, string $key, int $type = self::VALUE_MIXED): bool {
$this->assertParams($app, $key);
$this->loadConfigAll();
- $lazy = $this->isLazy($app, $key);
+ $this->matchAndApplyLexiconDefinition($app, $key);
+ $this->isLazy($app, $key); // confirm key exists
// type can only be one type
if (!in_array($type, [self::VALUE_MIXED, self::VALUE_STRING, self::VALUE_INT, self::VALUE_FLOAT, self::VALUE_BOOL, self::VALUE_ARRAY])) {
@@ -905,6 +923,7 @@ class AppConfig implements IAppConfig {
public function updateSensitive(string $app, string $key, bool $sensitive): bool {
$this->assertParams($app, $key);
$this->loadConfigAll();
+ $this->matchAndApplyLexiconDefinition($app, $key);
try {
if ($sensitive === $this->isSensitive($app, $key, null)) {
@@ -964,6 +983,7 @@ class AppConfig implements IAppConfig {
public function updateLazy(string $app, string $key, bool $lazy): bool {
$this->assertParams($app, $key);
$this->loadConfigAll();
+ $this->matchAndApplyLexiconDefinition($app, $key);
try {
if ($lazy === $this->isLazy($app, $key)) {
@@ -999,6 +1019,7 @@ class AppConfig implements IAppConfig {
public function getDetails(string $app, string $key): array {
$this->assertParams($app, $key);
$this->loadConfigAll();
+ $this->matchAndApplyLexiconDefinition($app, $key);
$lazy = $this->isLazy($app, $key);
if ($lazy) {
@@ -1086,6 +1107,8 @@ class AppConfig implements IAppConfig {
*/
public function deleteKey(string $app, string $key): void {
$this->assertParams($app, $key);
+ $this->matchAndApplyLexiconDefinition($app, $key);
+
$qb = $this->connection->getQueryBuilder();
$qb->delete('appconfig')
->where($qb->expr()->eq('appid', $qb->createNamedParameter($app)))
@@ -1293,6 +1316,7 @@ class AppConfig implements IAppConfig {
*/
public function getValue($app, $key, $default = null) {
$this->loadConfig($app);
+ $this->matchAndApplyLexiconDefinition($app, $key);
return $this->fastCache[$app][$key] ?? $default;
}
@@ -1372,7 +1396,7 @@ class AppConfig implements IAppConfig {
foreach ($values as $key => $value) {
try {
$type = $this->getValueType($app, $key, $lazy);
- } catch (AppConfigUnknownKeyException $e) {
+ } catch (AppConfigUnknownKeyException) {
continue;
}
@@ -1556,7 +1580,8 @@ class AppConfig implements IAppConfig {
}
/**
- * match and apply current use of config values with defined lexicon
+ * Match and apply current use of config values with defined lexicon.
+ * Set $lazy to NULL only if only interested into checking that $key is alias.
*
* @throws AppConfigUnknownKeyException
* @throws AppConfigTypeConflictException
@@ -1564,9 +1589,9 @@ class AppConfig implements IAppConfig {
*/
private function matchAndApplyLexiconDefinition(
string $app,
- string $key,
- bool &$lazy,
- int &$type,
+ string &$key,
+ ?bool &$lazy = null,
+ int &$type = self::VALUE_MIXED,
string &$default = '',
): bool {
if (in_array($key,
@@ -1578,11 +1603,18 @@ class AppConfig implements IAppConfig {
return true; // we don't break stuff for this list of config keys.
}
$configDetails = $this->getConfigDetailsFromLexicon($app);
+ if (array_key_exists($key, $configDetails['aliases']) && !$this->ignoreLexiconAliases) {
+ // in case '$rename' is set in ConfigLexiconEntry, we use the new config key
+ $key = $configDetails['aliases'][$key];
+ }
+
if (!array_key_exists($key, $configDetails['entries'])) {
- return $this->applyLexiconStrictness(
- $configDetails['strictness'],
- 'The app config key ' . $app . '/' . $key . ' is not defined in the config lexicon'
- );
+ return $this->applyLexiconStrictness($configDetails['strictness'], 'The app config key ' . $app . '/' . $key . ' is not defined in the config lexicon');
+ }
+
+ // if lazy is NULL, we ignore all check on the type/lazyness/default from Lexicon
+ if ($lazy === null) {
+ return true;
}
/** @var ConfigLexiconEntry $configValue */
@@ -1644,20 +1676,25 @@ class AppConfig implements IAppConfig {
* extract details from registered $appId's config lexicon
*
* @param string $appId
+ * @internal
*
- * @return array{entries: array<array-key, ConfigLexiconEntry>, strictness: ConfigLexiconStrictness}
+ * @return array{entries: array<string, ConfigLexiconEntry>, aliases: array<string, string>, strictness: ConfigLexiconStrictness}
*/
- private function getConfigDetailsFromLexicon(string $appId): array {
+ public function getConfigDetailsFromLexicon(string $appId): array {
if (!array_key_exists($appId, $this->configLexiconDetails)) {
- $entries = [];
+ $entries = $aliases = [];
$bootstrapCoordinator = \OCP\Server::get(Coordinator::class);
$configLexicon = $bootstrapCoordinator->getRegistrationContext()?->getConfigLexicon($appId);
foreach ($configLexicon?->getAppConfigs() ?? [] as $configEntry) {
$entries[$configEntry->getKey()] = $configEntry;
+ if ($configEntry->getRename() !== null) {
+ $aliases[$configEntry->getRename()] = $configEntry->getKey();
+ }
}
$this->configLexiconDetails[$appId] = [
'entries' => $entries,
+ 'aliases' => $aliases,
'strictness' => $configLexicon?->getStrictness() ?? ConfigLexiconStrictness::IGNORE
];
}
@@ -1665,16 +1702,36 @@ class AppConfig implements IAppConfig {
return $this->configLexiconDetails[$appId];
}
+ private function getLexiconEntry(string $appId, string $key): ?ConfigLexiconEntry {
+ return $this->getConfigDetailsFromLexicon($appId)['entries'][$key] ?? null;
+ }
+
+ /**
+ * if set to TRUE, ignore aliases defined in Config Lexicon during the use of the methods of this class
+ *
+ * @internal
+ */
+ public function ignoreLexiconAliases(bool $ignore): void {
+ $this->ignoreLexiconAliases = $ignore;
+ }
+
/**
* Returns the installed versions of all apps
*
* @return array<string, string>
*/
- public function getAppInstalledVersions(): array {
+ public function getAppInstalledVersions(bool $onlyEnabled = false): array {
if ($this->appVersionsCache === null) {
/** @var array<string, string> */
$this->appVersionsCache = $this->searchValues('installed_version', false, IAppConfig::VALUE_STRING);
}
+ if ($onlyEnabled) {
+ return array_filter(
+ $this->appVersionsCache,
+ fn (string $app): bool => $this->getValueString($app, 'enabled', 'no') !== 'no',
+ ARRAY_FILTER_USE_KEY
+ );
+ }
return $this->appVersionsCache;
}
}
diff --git a/lib/private/AppFramework/Bootstrap/Coordinator.php b/lib/private/AppFramework/Bootstrap/Coordinator.php
index 4e613703dec..64e3dbfd928 100644
--- a/lib/private/AppFramework/Bootstrap/Coordinator.php
+++ b/lib/private/AppFramework/Bootstrap/Coordinator.php
@@ -20,6 +20,7 @@ use OCP\Dashboard\IManager;
use OCP\Diagnostics\IEventLogger;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\IServerContainer;
+use Psr\Container\ContainerExceptionInterface;
use Psr\Log\LoggerInterface;
use Throwable;
use function class_exists;
@@ -69,19 +70,24 @@ class Coordinator {
*/
try {
$path = $this->appManager->getAppPath($appId);
+ OC_App::registerAutoloading($appId, $path);
} catch (AppPathNotFoundException) {
// Ignore
continue;
}
- OC_App::registerAutoloading($appId, $path);
$this->eventLogger->end("bootstrap:register_app:$appId:autoloader");
/*
* Next we check if there is an application class, and it implements
* the \OCP\AppFramework\Bootstrap\IBootstrap interface
*/
- $appNameSpace = App::buildAppNamespace($appId);
+ if ($appId === 'core') {
+ $appNameSpace = 'OC\\Core';
+ } else {
+ $appNameSpace = App::buildAppNamespace($appId);
+ }
$applicationClassName = $appNameSpace . '\\AppInfo\\Application';
+
try {
if (class_exists($applicationClassName) && is_a($applicationClassName, IBootstrap::class, true)) {
$this->eventLogger->start("bootstrap:register_app:$appId:application", "Load `Application` instance for $appId");
@@ -89,7 +95,7 @@ class Coordinator {
/** @var IBootstrap&App $application */
$application = $this->serverContainer->query($applicationClassName);
$apps[$appId] = $application;
- } catch (QueryException $e) {
+ } catch (ContainerExceptionInterface $e) {
// Weird, but ok
$this->eventLogger->end("bootstrap:register_app:$appId");
continue;
@@ -172,7 +178,7 @@ class Coordinator {
public function isBootable(string $appId) {
$appNameSpace = App::buildAppNamespace($appId);
$applicationClassName = $appNameSpace . '\\AppInfo\\Application';
- return class_exists($applicationClassName) &&
- in_array(IBootstrap::class, class_implements($applicationClassName), true);
+ return class_exists($applicationClassName)
+ && in_array(IBootstrap::class, class_implements($applicationClassName), true);
}
}
diff --git a/lib/private/AppFramework/Bootstrap/RegistrationContext.php b/lib/private/AppFramework/Bootstrap/RegistrationContext.php
index c3b829825c2..95ad129c466 100644
--- a/lib/private/AppFramework/Bootstrap/RegistrationContext.php
+++ b/lib/private/AppFramework/Bootstrap/RegistrationContext.php
@@ -157,7 +157,7 @@ class RegistrationContext {
/** @var ServiceRegistration<\OCP\Files\Conversion\IConversionProvider>[] */
private array $fileConversionProviders = [];
-
+
/** @var ServiceRegistration<IMailProvider>[] */
private $mailProviders = [];
diff --git a/lib/private/AppFramework/DependencyInjection/DIContainer.php b/lib/private/AppFramework/DependencyInjection/DIContainer.php
index b6e2df4ce7b..87361a9d1ea 100644
--- a/lib/private/AppFramework/DependencyInjection/DIContainer.php
+++ b/lib/private/AppFramework/DependencyInjection/DIContainer.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@@ -440,7 +441,7 @@ class DIContainer extends SimpleContainer implements IAppContainer {
return parent::query($name);
}
- throw new QueryException('Could not resolve ' . $name . '!' .
- ' Class can not be instantiated', 1);
+ throw new QueryException('Could not resolve ' . $name . '!'
+ . ' Class can not be instantiated', 1);
}
}
diff --git a/lib/private/AppFramework/Http.php b/lib/private/AppFramework/Http.php
index 3c3e19692bf..08d6259c2a2 100644
--- a/lib/private/AppFramework/Http.php
+++ b/lib/private/AppFramework/Http.php
@@ -102,7 +102,7 @@ class Http extends BaseHttp {
$status = self::STATUS_FOUND;
}
- return $this->protocolVersion . ' ' . $status . ' ' .
- $this->headers[$status];
+ return $this->protocolVersion . ' ' . $status . ' '
+ . $this->headers[$status];
}
}
diff --git a/lib/private/AppFramework/Middleware/FlowV2EphemeralSessionsMiddleware.php b/lib/private/AppFramework/Middleware/FlowV2EphemeralSessionsMiddleware.php
index c30855a0e98..e4571dfc50e 100644
--- a/lib/private/AppFramework/Middleware/FlowV2EphemeralSessionsMiddleware.php
+++ b/lib/private/AppFramework/Middleware/FlowV2EphemeralSessionsMiddleware.php
@@ -33,8 +33,8 @@ class FlowV2EphemeralSessionsMiddleware extends Middleware {
}
if (
- $controller instanceof ClientFlowLoginV2Controller &&
- ($methodName === 'grantPage' || $methodName === 'generateAppPassword')
+ $controller instanceof ClientFlowLoginV2Controller
+ && ($methodName === 'grantPage' || $methodName === 'generateAppPassword')
) {
return;
}
diff --git a/lib/private/AppFramework/Middleware/NotModifiedMiddleware.php b/lib/private/AppFramework/Middleware/NotModifiedMiddleware.php
index 17b423164f6..08b30092155 100644
--- a/lib/private/AppFramework/Middleware/NotModifiedMiddleware.php
+++ b/lib/private/AppFramework/Middleware/NotModifiedMiddleware.php
@@ -29,7 +29,7 @@ class NotModifiedMiddleware extends Middleware {
}
$modifiedSinceHeader = $this->request->getHeader('IF_MODIFIED_SINCE');
- if ($modifiedSinceHeader !== '' && $response->getLastModified() !== null && trim($modifiedSinceHeader) === $response->getLastModified()->format(\DateTimeInterface::RFC2822)) {
+ if ($modifiedSinceHeader !== '' && $response->getLastModified() !== null && trim($modifiedSinceHeader) === $response->getLastModified()->format(\DateTimeInterface::RFC7231)) {
$response->setStatus(Http::STATUS_NOT_MODIFIED);
return $response;
}
diff --git a/lib/private/AppFramework/Middleware/OCSMiddleware.php b/lib/private/AppFramework/Middleware/OCSMiddleware.php
index 46612bf0d29..64f4b0054de 100644
--- a/lib/private/AppFramework/Middleware/OCSMiddleware.php
+++ b/lib/private/AppFramework/Middleware/OCSMiddleware.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/AppFramework/Middleware/PublicShare/Exceptions/NeedAuthenticationException.php b/lib/private/AppFramework/Middleware/PublicShare/Exceptions/NeedAuthenticationException.php
index c80d06c90ae..5df4009b094 100644
--- a/lib/private/AppFramework/Middleware/PublicShare/Exceptions/NeedAuthenticationException.php
+++ b/lib/private/AppFramework/Middleware/PublicShare/Exceptions/NeedAuthenticationException.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/AppFramework/Middleware/PublicShare/PublicShareMiddleware.php b/lib/private/AppFramework/Middleware/PublicShare/PublicShareMiddleware.php
index b3040673d0f..83e799e3d3b 100644
--- a/lib/private/AppFramework/Middleware/PublicShare/PublicShareMiddleware.php
+++ b/lib/private/AppFramework/Middleware/PublicShare/PublicShareMiddleware.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/AppFramework/Middleware/Security/CORSMiddleware.php b/lib/private/AppFramework/Middleware/Security/CORSMiddleware.php
index 40af67739d6..4453f5a7d4b 100644
--- a/lib/private/AppFramework/Middleware/Security/CORSMiddleware.php
+++ b/lib/private/AppFramework/Middleware/Security/CORSMiddleware.php
@@ -68,8 +68,8 @@ class CORSMiddleware extends Middleware {
// ensure that @CORS annotated API routes are not used in conjunction
// with session authentication since this enables CSRF attack vectors
- if ($this->hasAnnotationOrAttribute($reflectionMethod, 'CORS', CORS::class) &&
- (!$this->hasAnnotationOrAttribute($reflectionMethod, 'PublicPage', PublicPage::class) || $this->session->isLoggedIn())) {
+ if ($this->hasAnnotationOrAttribute($reflectionMethod, 'CORS', CORS::class)
+ && (!$this->hasAnnotationOrAttribute($reflectionMethod, 'PublicPage', PublicPage::class) || $this->session->isLoggedIn())) {
$user = array_key_exists('PHP_AUTH_USER', $this->request->server) ? $this->request->server['PHP_AUTH_USER'] : null;
$pass = array_key_exists('PHP_AUTH_PW', $this->request->server) ? $this->request->server['PHP_AUTH_PW'] : null;
@@ -134,10 +134,10 @@ class CORSMiddleware extends Middleware {
// allow credentials headers must not be true or CSRF is possible
// otherwise
foreach ($response->getHeaders() as $header => $value) {
- if (strtolower($header) === 'access-control-allow-credentials' &&
- strtolower(trim($value)) === 'true') {
- $msg = 'Access-Control-Allow-Credentials must not be ' .
- 'set to true in order to prevent CSRF';
+ if (strtolower($header) === 'access-control-allow-credentials'
+ && strtolower(trim($value)) === 'true') {
+ $msg = 'Access-Control-Allow-Credentials must not be '
+ . 'set to true in order to prevent CSRF';
throw new SecurityException($msg);
}
}
diff --git a/lib/private/AppFramework/Middleware/Security/Exceptions/AppNotEnabledException.php b/lib/private/AppFramework/Middleware/Security/Exceptions/AppNotEnabledException.php
index 646a5240bfc..53fbaaf5ed2 100644
--- a/lib/private/AppFramework/Middleware/Security/Exceptions/AppNotEnabledException.php
+++ b/lib/private/AppFramework/Middleware/Security/Exceptions/AppNotEnabledException.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/lib/private/AppFramework/Middleware/Security/Exceptions/LaxSameSiteCookieFailedException.php b/lib/private/AppFramework/Middleware/Security/Exceptions/LaxSameSiteCookieFailedException.php
index 91f1dba874d..0380c6781aa 100644
--- a/lib/private/AppFramework/Middleware/Security/Exceptions/LaxSameSiteCookieFailedException.php
+++ b/lib/private/AppFramework/Middleware/Security/Exceptions/LaxSameSiteCookieFailedException.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php b/lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php
index 7e950f2c976..ca30f736fbc 100644
--- a/lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php
+++ b/lib/private/AppFramework/Middleware/Security/Exceptions/NotConfirmedException.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -14,7 +15,7 @@ use OCP\AppFramework\Http;
* @package OC\AppFramework\Middleware\Security\Exceptions
*/
class NotConfirmedException extends SecurityException {
- public function __construct() {
- parent::__construct('Password confirmation is required', Http::STATUS_FORBIDDEN);
+ public function __construct(string $message = 'Password confirmation is required') {
+ parent::__construct($message, Http::STATUS_FORBIDDEN);
}
}
diff --git a/lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php b/lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php
index d00840084a3..0facbffe504 100644
--- a/lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php
+++ b/lib/private/AppFramework/Middleware/Security/PasswordConfirmationMiddleware.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -79,6 +80,9 @@ class PasswordConfirmationMiddleware extends Middleware {
if ($this->isPasswordConfirmationStrict($reflectionMethod)) {
$authHeader = $this->request->getHeader('Authorization');
+ if (!str_starts_with(strtolower($authHeader), 'basic ')) {
+ throw new NotConfirmedException('Required authorization header missing');
+ }
[, $password] = explode(':', base64_decode(substr($authHeader, 6)), 2);
$loginName = $this->session->get('loginname');
$loginResult = $this->userManager->checkPassword($loginName, $password);
diff --git a/lib/private/AppFramework/Middleware/Security/SameSiteCookieMiddleware.php b/lib/private/AppFramework/Middleware/Security/SameSiteCookieMiddleware.php
index efe56e0b124..ed3bb232023 100644
--- a/lib/private/AppFramework/Middleware/Security/SameSiteCookieMiddleware.php
+++ b/lib/private/AppFramework/Middleware/Security/SameSiteCookieMiddleware.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php b/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php
index 6b054b03f44..e3a293e0fd9 100644
--- a/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php
+++ b/lib/private/AppFramework/Middleware/Security/SecurityMiddleware.php
@@ -184,8 +184,8 @@ class SecurityMiddleware extends Middleware {
}
// Check for strict cookie requirement
- if ($this->hasAnnotationOrAttribute($reflectionMethod, 'StrictCookieRequired', StrictCookiesRequired::class) ||
- !$this->hasAnnotationOrAttribute($reflectionMethod, 'NoCSRFRequired', NoCSRFRequired::class)) {
+ if ($this->hasAnnotationOrAttribute($reflectionMethod, 'StrictCookieRequired', StrictCookiesRequired::class)
+ || !$this->hasAnnotationOrAttribute($reflectionMethod, 'NoCSRFRequired', NoCSRFRequired::class)) {
if (!$this->request->passesStrictCookieCheck()) {
throw new StrictCookieMissingException();
}
diff --git a/lib/private/AppFramework/OCS/BaseResponse.php b/lib/private/AppFramework/OCS/BaseResponse.php
index 5929a3993ec..05ce133db24 100644
--- a/lib/private/AppFramework/OCS/BaseResponse.php
+++ b/lib/private/AppFramework/OCS/BaseResponse.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -83,9 +84,9 @@ abstract class BaseResponse extends Response {
*/
protected function renderResult(array $meta): string {
$status = $this->getStatus();
- if ($status === Http::STATUS_NO_CONTENT ||
- $status === Http::STATUS_NOT_MODIFIED ||
- ($status >= 100 && $status <= 199)) {
+ if ($status === Http::STATUS_NO_CONTENT
+ || $status === Http::STATUS_NOT_MODIFIED
+ || ($status >= 100 && $status <= 199)) {
// Those status codes are not supposed to have a body:
// https://stackoverflow.com/q/8628725
return '';
diff --git a/lib/private/AppFramework/OCS/V1Response.php b/lib/private/AppFramework/OCS/V1Response.php
index 131ca22ff24..1c2c25f5cb0 100644
--- a/lib/private/AppFramework/OCS/V1Response.php
+++ b/lib/private/AppFramework/OCS/V1Response.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/AppFramework/OCS/V2Response.php b/lib/private/AppFramework/OCS/V2Response.php
index 47cf0f60200..efc9348eb37 100644
--- a/lib/private/AppFramework/OCS/V2Response.php
+++ b/lib/private/AppFramework/OCS/V2Response.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/AppFramework/Services/AppConfig.php b/lib/private/AppFramework/Services/AppConfig.php
index 77c5ea4de0c..04d97738483 100644
--- a/lib/private/AppFramework/Services/AppConfig.php
+++ b/lib/private/AppFramework/Services/AppConfig.php
@@ -343,7 +343,7 @@ class AppConfig implements IAppConfig {
*
* @return array<string, string>
*/
- public function getAppInstalledVersions(): array {
- return $this->appConfig->getAppInstalledVersions();
+ public function getAppInstalledVersions(bool $onlyEnabled = false): array {
+ return $this->appConfig->getAppInstalledVersions($onlyEnabled);
}
}
diff --git a/lib/private/AppFramework/Utility/SimpleContainer.php b/lib/private/AppFramework/Utility/SimpleContainer.php
index 9af65a37ab8..1d77c277b02 100644
--- a/lib/private/AppFramework/Utility/SimpleContainer.php
+++ b/lib/private/AppFramework/Utility/SimpleContainer.php
@@ -12,6 +12,7 @@ use Closure;
use OCP\AppFramework\QueryException;
use OCP\IContainer;
use Pimple\Container;
+use Psr\Container\ContainerExceptionInterface;
use Psr\Container\ContainerInterface;
use ReflectionClass;
use ReflectionException;
@@ -23,8 +24,9 @@ use function class_exists;
* SimpleContainer is a simple implementation of a container on basis of Pimple
*/
class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
- /** @var Container */
- private $container;
+ public static bool $useLazyObjects = false;
+
+ private Container $container;
public function __construct() {
$this->container = new Container();
@@ -49,16 +51,29 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
/**
* @param ReflectionClass $class the class to instantiate
- * @return \stdClass the created class
+ * @return object the created class
* @suppress PhanUndeclaredClassInstanceof
*/
- private function buildClass(ReflectionClass $class) {
+ private function buildClass(ReflectionClass $class): object {
$constructor = $class->getConstructor();
if ($constructor === null) {
+ /* No constructor, return a instance directly */
return $class->newInstance();
}
+ if (PHP_VERSION_ID >= 80400 && self::$useLazyObjects) {
+ /* For PHP>=8.4, use a lazy ghost to delay constructor and dependency resolving */
+ /** @psalm-suppress UndefinedMethod */
+ return $class->newLazyGhost(function (object $object) use ($constructor): void {
+ /** @psalm-suppress DirectConstructorCall For lazy ghosts we have to call the constructor directly */
+ $object->__construct(...$this->buildClassConstructorParameters($constructor));
+ });
+ } else {
+ return $class->newInstanceArgs($this->buildClassConstructorParameters($constructor));
+ }
+ }
- return $class->newInstanceArgs(array_map(function (ReflectionParameter $parameter) {
+ private function buildClassConstructorParameters(\ReflectionMethod $constructor): array {
+ return array_map(function (ReflectionParameter $parameter) {
$parameterType = $parameter->getType();
$resolveName = $parameter->getName();
@@ -69,10 +84,10 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
}
try {
- $builtIn = $parameter->hasType() && ($parameter->getType() instanceof ReflectionNamedType)
- && $parameter->getType()->isBuiltin();
+ $builtIn = $parameterType !== null && ($parameterType instanceof ReflectionNamedType)
+ && $parameterType->isBuiltin();
return $this->query($resolveName, !$builtIn);
- } catch (QueryException $e) {
+ } catch (ContainerExceptionInterface $e) {
// Service not found, use the default value when available
if ($parameter->isDefaultValueAvailable()) {
return $parameter->getDefaultValue();
@@ -82,7 +97,7 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
$resolveName = $parameter->getName();
try {
return $this->query($resolveName);
- } catch (QueryException $e2) {
+ } catch (ContainerExceptionInterface $e2) {
// Pass null if typed and nullable
if ($parameter->allowsNull() && ($parameterType instanceof ReflectionNamedType)) {
return null;
@@ -95,7 +110,7 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
throw $e;
}
- }, $constructor->getParameters()));
+ }, $constructor->getParameters());
}
public function resolve($name) {
@@ -105,8 +120,8 @@ class SimpleContainer implements ArrayAccess, ContainerInterface, IContainer {
if ($class->isInstantiable()) {
return $this->buildClass($class);
} else {
- throw new QueryException($baseMsg .
- ' Class can not be instantiated');
+ throw new QueryException($baseMsg
+ . ' Class can not be instantiated');
}
} catch (ReflectionException $e) {
// Class does not exist
diff --git a/lib/private/AppScriptDependency.php b/lib/private/AppScriptDependency.php
index 380816f21ac..cc5ded0d011 100644
--- a/lib/private/AppScriptDependency.php
+++ b/lib/private/AppScriptDependency.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/AppScriptSort.php b/lib/private/AppScriptSort.php
index eda41e97445..134dad100dc 100644
--- a/lib/private/AppScriptSort.php
+++ b/lib/private/AppScriptSort.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Authentication/LoginCredentials/Credentials.php b/lib/private/Authentication/LoginCredentials/Credentials.php
index 2d7ed3adfd0..3414034b33c 100644
--- a/lib/private/Authentication/LoginCredentials/Credentials.php
+++ b/lib/private/Authentication/LoginCredentials/Credentials.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Authentication/Token/TokenCleanupJob.php b/lib/private/Authentication/Token/TokenCleanupJob.php
index 041d2e8a5e2..e6d1e69e9b4 100644
--- a/lib/private/Authentication/Token/TokenCleanupJob.php
+++ b/lib/private/Authentication/Token/TokenCleanupJob.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-only
diff --git a/lib/private/Authentication/TwoFactorAuth/Manager.php b/lib/private/Authentication/TwoFactorAuth/Manager.php
index 1b22300e317..07aa98610ed 100644
--- a/lib/private/Authentication/TwoFactorAuth/Manager.php
+++ b/lib/private/Authentication/TwoFactorAuth/Manager.php
@@ -308,8 +308,8 @@ class Manager {
// First check if the session tells us we should do 2FA (99% case)
if (!$this->session->exists(self::SESSION_UID_KEY)) {
// Check if the session tells us it is 2FA authenticated already
- if ($this->session->exists(self::SESSION_UID_DONE) &&
- $this->session->get(self::SESSION_UID_DONE) === $user->getUID()) {
+ if ($this->session->exists(self::SESSION_UID_DONE)
+ && $this->session->get(self::SESSION_UID_DONE) === $user->getUID()) {
return false;
}
diff --git a/lib/private/Avatar/UserAvatar.php b/lib/private/Avatar/UserAvatar.php
index 51c44b23a55..bef0a20e7b8 100644
--- a/lib/private/Avatar/UserAvatar.php
+++ b/lib/private/Avatar/UserAvatar.php
@@ -82,8 +82,8 @@ class UserAvatar extends Avatar {
$img = new \OCP\Image();
if (
- (is_resource($data) && get_resource_type($data) === 'gd') ||
- (is_object($data) && get_class($data) === \GdImage::class)
+ (is_resource($data) && get_resource_type($data) === 'gd')
+ || (is_object($data) && get_class($data) === \GdImage::class)
) {
$img->setResource($data);
} elseif (is_resource($data)) {
diff --git a/lib/private/Blurhash/Listener/GenerateBlurhashMetadata.php b/lib/private/Blurhash/Listener/GenerateBlurhashMetadata.php
index 982693bcfe8..8faf4627251 100644
--- a/lib/private/Blurhash/Listener/GenerateBlurhashMetadata.php
+++ b/lib/private/Blurhash/Listener/GenerateBlurhashMetadata.php
@@ -68,6 +68,11 @@ class GenerateBlurhashMetadata implements IEventListener {
return;
}
+ // Preview are disabled, so we skip generating the blurhash.
+ if (!$this->preview->isAvailable($file)) {
+ return;
+ }
+
$preview = $this->preview->getPreview($file, 64, 64, cacheResult: false);
$image = @imagecreatefromstring($preview->getContent());
diff --git a/lib/private/Calendar/Manager.php b/lib/private/Calendar/Manager.php
index 21370e74d54..0e2a3f5f679 100644
--- a/lib/private/Calendar/Manager.php
+++ b/lib/private/Calendar/Manager.php
@@ -236,7 +236,7 @@ class Manager implements IManager {
$this->logger->warning('iMip message could not be processed because user has no calendars');
return false;
}
-
+
try {
/** @var VCalendar $vObject|null */
$calendarObject = Reader::read($calendarData);
diff --git a/lib/private/Collaboration/AutoComplete/Manager.php b/lib/private/Collaboration/AutoComplete/Manager.php
index c4dd4093e69..cc5df78beea 100644
--- a/lib/private/Collaboration/AutoComplete/Manager.php
+++ b/lib/private/Collaboration/AutoComplete/Manager.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Collaboration/Collaborators/GroupPlugin.php b/lib/private/Collaboration/Collaborators/GroupPlugin.php
index a7b84b72199..a59d5981825 100644
--- a/lib/private/Collaboration/Collaborators/GroupPlugin.php
+++ b/lib/private/Collaboration/Collaborators/GroupPlugin.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Collaboration/Collaborators/LookupPlugin.php b/lib/private/Collaboration/Collaborators/LookupPlugin.php
index 2c8dc845458..fb6b9f2e0e8 100644
--- a/lib/private/Collaboration/Collaborators/LookupPlugin.php
+++ b/lib/private/Collaboration/Collaborators/LookupPlugin.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Collaboration/Collaborators/MailPlugin.php b/lib/private/Collaboration/Collaborators/MailPlugin.php
index 278ba19aa5a..55e3945ace2 100644
--- a/lib/private/Collaboration/Collaborators/MailPlugin.php
+++ b/lib/private/Collaboration/Collaborators/MailPlugin.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -228,8 +229,8 @@ class MailPlugin implements ISearchPlugin {
$reachedEnd = true;
if ($this->shareeEnumeration) {
- $reachedEnd = (count($result['wide']) < $offset + $limit) &&
- (count($userResults['wide']) < $offset + $limit);
+ $reachedEnd = (count($result['wide']) < $offset + $limit)
+ && (count($userResults['wide']) < $offset + $limit);
$result['wide'] = array_slice($result['wide'], $offset, $limit);
$userResults['wide'] = array_slice($userResults['wide'], $offset, $limit);
diff --git a/lib/private/Collaboration/Collaborators/RemoteGroupPlugin.php b/lib/private/Collaboration/Collaborators/RemoteGroupPlugin.php
index 89d5c4e4f79..f4c1793ea0a 100644
--- a/lib/private/Collaboration/Collaborators/RemoteGroupPlugin.php
+++ b/lib/private/Collaboration/Collaborators/RemoteGroupPlugin.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Collaboration/Collaborators/RemotePlugin.php b/lib/private/Collaboration/Collaborators/RemotePlugin.php
index 788ece70cb9..037c6f6cbea 100644
--- a/lib/private/Collaboration/Collaborators/RemotePlugin.php
+++ b/lib/private/Collaboration/Collaborators/RemotePlugin.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Collaboration/Collaborators/Search.php b/lib/private/Collaboration/Collaborators/Search.php
index 78b57b52400..32c70549a7b 100644
--- a/lib/private/Collaboration/Collaborators/Search.php
+++ b/lib/private/Collaboration/Collaborators/Search.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Collaboration/Collaborators/SearchResult.php b/lib/private/Collaboration/Collaborators/SearchResult.php
index 73c0fed41e0..c9c2f032f36 100644
--- a/lib/private/Collaboration/Collaborators/SearchResult.php
+++ b/lib/private/Collaboration/Collaborators/SearchResult.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Collaboration/Collaborators/UserPlugin.php b/lib/private/Collaboration/Collaborators/UserPlugin.php
index d196abae042..671181aea35 100644
--- a/lib/private/Collaboration/Collaborators/UserPlugin.php
+++ b/lib/private/Collaboration/Collaborators/UserPlugin.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -147,11 +148,11 @@ class UserPlugin implements ISearchPlugin {
if (
- $this->shareeEnumerationFullMatch &&
- $lowerSearch !== '' && (strtolower($uid) === $lowerSearch ||
- strtolower($userDisplayName) === $lowerSearch ||
- ($this->shareeEnumerationFullMatchIgnoreSecondDisplayName && trim(strtolower(preg_replace('/ \(.*\)$/', '', $userDisplayName))) === $lowerSearch) ||
- ($this->shareeEnumerationFullMatchEmail && strtolower($userEmail ?? '') === $lowerSearch))
+ $this->shareeEnumerationFullMatch
+ && $lowerSearch !== '' && (strtolower($uid) === $lowerSearch
+ || strtolower($userDisplayName) === $lowerSearch
+ || ($this->shareeEnumerationFullMatchIgnoreSecondDisplayName && trim(strtolower(preg_replace('/ \(.*\)$/', '', $userDisplayName))) === $lowerSearch)
+ || ($this->shareeEnumerationFullMatchEmail && strtolower($userEmail ?? '') === $lowerSearch))
) {
if (strtolower($uid) === $lowerSearch) {
$foundUserById = true;
@@ -169,8 +170,8 @@ class UserPlugin implements ISearchPlugin {
];
} else {
$addToWideResults = false;
- if ($this->shareeEnumeration &&
- !($this->shareeEnumerationInGroupOnly || $this->shareeEnumerationPhone)) {
+ if ($this->shareeEnumeration
+ && !($this->shareeEnumerationInGroupOnly || $this->shareeEnumerationPhone)) {
$addToWideResults = true;
}
diff --git a/lib/private/Collaboration/Reference/File/FileReferenceEventListener.php b/lib/private/Collaboration/Reference/File/FileReferenceEventListener.php
index e468ad4eb4c..9c18531c8e7 100644
--- a/lib/private/Collaboration/Reference/File/FileReferenceEventListener.php
+++ b/lib/private/Collaboration/Reference/File/FileReferenceEventListener.php
@@ -15,6 +15,7 @@ use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\EventDispatcher\IEventListener;
use OCP\Files\Events\Node\NodeDeletedEvent;
+use OCP\Files\Events\Node\NodeRenamedEvent;
use OCP\Share\Events\ShareCreatedEvent;
use OCP\Share\Events\ShareDeletedEvent;
@@ -27,6 +28,7 @@ class FileReferenceEventListener implements IEventListener {
public static function register(IEventDispatcher $eventDispatcher): void {
$eventDispatcher->addServiceListener(NodeDeletedEvent::class, FileReferenceEventListener::class);
+ $eventDispatcher->addServiceListener(NodeRenamedEvent::class, FileReferenceEventListener::class);
$eventDispatcher->addServiceListener(ShareDeletedEvent::class, FileReferenceEventListener::class);
$eventDispatcher->addServiceListener(ShareCreatedEvent::class, FileReferenceEventListener::class);
}
@@ -42,6 +44,9 @@ class FileReferenceEventListener implements IEventListener {
$this->manager->invalidateCache((string)$event->getNode()->getId());
}
+ if ($event instanceof NodeRenamedEvent) {
+ $this->manager->invalidateCache((string)$event->getTarget()->getId());
+ }
if ($event instanceof ShareDeletedEvent) {
$this->manager->invalidateCache((string)$event->getShare()->getNodeId());
}
diff --git a/lib/private/Collaboration/Resources/Collection.php b/lib/private/Collaboration/Resources/Collection.php
index cf5f7740ced..2481a3e9a09 100644
--- a/lib/private/Collaboration/Resources/Collection.php
+++ b/lib/private/Collaboration/Resources/Collection.php
@@ -164,8 +164,8 @@ class Collection implements ICollection {
}
protected function isSameResource(IResource $resource1, IResource $resource2): bool {
- return $resource1->getType() === $resource2->getType() &&
- $resource1->getId() === $resource2->getId();
+ return $resource1->getType() === $resource2->getType()
+ && $resource1->getId() === $resource2->getId();
}
protected function removeCollection(): void {
diff --git a/lib/private/Color.php b/lib/private/Color.php
index e96d6fd23bd..d97c519e552 100644
--- a/lib/private/Color.php
+++ b/lib/private/Color.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Command/CronBus.php b/lib/private/Command/CronBus.php
index 1ff9bb7099a..a12520469a9 100644
--- a/lib/private/Command/CronBus.php
+++ b/lib/private/Command/CronBus.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Config/ConfigManager.php b/lib/private/Config/ConfigManager.php
new file mode 100644
index 00000000000..1980269e2ca
--- /dev/null
+++ b/lib/private/Config/ConfigManager.php
@@ -0,0 +1,250 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OC\Config;
+
+use JsonException;
+use NCU\Config\Exceptions\TypeConflictException;
+use NCU\Config\IUserConfig;
+use NCU\Config\Lexicon\ConfigLexiconEntry;
+use NCU\Config\ValueType;
+use OC\AppConfig;
+use OCP\App\IAppManager;
+use OCP\IAppConfig;
+use OCP\Server;
+use Psr\Log\LoggerInterface;
+
+/**
+ * tools to maintains configurations
+ *
+ * @since 32.0.0
+ */
+class ConfigManager {
+ /** @var AppConfig|null $appConfig */
+ private ?IAppConfig $appConfig = null;
+ /** @var UserConfig|null $userConfig */
+ private ?IUserConfig $userConfig = null;
+
+ public function __construct(
+ private readonly LoggerInterface $logger,
+ ) {
+ }
+
+ /**
+ * Use the rename values from the list of ConfigLexiconEntry defined in each app ConfigLexicon
+ * to migrate config value to a new config key.
+ * Migration will only occur if new config key has no value in database.
+ * The previous value from the key set in rename will be deleted from the database when migration
+ * is over.
+ *
+ * This method should be mainly called during a new upgrade or when a new app is enabled.
+ *
+ * @see ConfigLexiconEntry
+ * @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
+ */
+ public function migrateConfigLexiconKeys(?string $appId = null): void {
+ if ($appId === null) {
+ $this->migrateConfigLexiconKeys('core');
+ $appManager = Server::get(IAppManager::class);
+ foreach ($appManager->getEnabledApps() as $app) {
+ $this->migrateConfigLexiconKeys($app);
+ }
+
+ return;
+ }
+
+ $this->loadConfigServices();
+
+ // it is required to ignore aliases when moving config values
+ $this->appConfig->ignoreLexiconAliases(true);
+ $this->userConfig->ignoreLexiconAliases(true);
+
+ $this->migrateAppConfigKeys($appId);
+ $this->migrateUserConfigKeys($appId);
+
+ // switch back to normal behavior
+ $this->appConfig->ignoreLexiconAliases(false);
+ $this->userConfig->ignoreLexiconAliases(false);
+ }
+
+ /**
+ * config services cannot be load at __construct() or install will fail
+ */
+ private function loadConfigServices(): void {
+ if ($this->appConfig === null) {
+ $this->appConfig = Server::get(IAppConfig::class);
+ }
+ if ($this->userConfig === null) {
+ $this->userConfig = Server::get(IUserConfig::class);
+ }
+ }
+
+ /**
+ * Get details from lexicon related to AppConfig and search for entries with rename to initiate
+ * a migration to new config key
+ */
+ private function migrateAppConfigKeys(string $appId): void {
+ $lexicon = $this->appConfig->getConfigDetailsFromLexicon($appId);
+ foreach ($lexicon['entries'] as $entry) {
+ // only interested in entries with rename set
+ if ($entry->getRename() === null) {
+ continue;
+ }
+
+ // only migrate if rename config key has a value and the new config key hasn't
+ if ($this->appConfig->hasKey($appId, $entry->getRename())
+ && !$this->appConfig->hasKey($appId, $entry->getKey())) {
+ try {
+ $this->migrateAppConfigValue($appId, $entry);
+ } catch (TypeConflictException $e) {
+ $this->logger->error('could not migrate AppConfig value', ['appId' => $appId, 'entry' => $entry, 'exception' => $e]);
+ continue;
+ }
+ }
+
+ // we only delete previous config value if migration went fine.
+ $this->appConfig->deleteKey($appId, $entry->getRename());
+ }
+ }
+
+ /**
+ * Get details from lexicon related to UserConfig and search for entries with rename to initiate
+ * a migration to new config key
+ */
+ private function migrateUserConfigKeys(string $appId): void {
+ $lexicon = $this->userConfig->getConfigDetailsFromLexicon($appId);
+ foreach ($lexicon['entries'] as $entry) {
+ // only interested in keys with rename set
+ if ($entry->getRename() === null) {
+ continue;
+ }
+
+ foreach ($this->userConfig->getValuesByUsers($appId, $entry->getRename()) as $userId => $value) {
+ if ($this->userConfig->hasKey($userId, $appId, $entry->getKey())) {
+ continue;
+ }
+
+ try {
+ $this->migrateUserConfigValue($userId, $appId, $entry);
+ } catch (TypeConflictException $e) {
+ $this->logger->error('could not migrate UserConfig value', ['userId' => $userId, 'appId' => $appId, 'entry' => $entry, 'exception' => $e]);
+ continue;
+ }
+
+ $this->userConfig->deleteUserConfig($userId, $appId, $entry->getRename());
+ }
+ }
+ }
+
+
+ /**
+ * converting value from rename to the new key
+ *
+ * @throws TypeConflictException if previous value does not fit the expected type
+ */
+ private function migrateAppConfigValue(string $appId, ConfigLexiconEntry $entry): void {
+ $value = $this->appConfig->getValueMixed($appId, $entry->getRename(), lazy: null);
+ switch ($entry->getValueType()) {
+ case ValueType::STRING:
+ $this->appConfig->setValueString($appId, $entry->getKey(), $value);
+ return;
+
+ case ValueType::INT:
+ $this->appConfig->setValueInt($appId, $entry->getKey(), $this->convertToInt($value));
+ return;
+
+ case ValueType::FLOAT:
+ $this->appConfig->setValueFloat($appId, $entry->getKey(), $this->convertToFloat($value));
+ return;
+
+ case ValueType::BOOL:
+ $this->appConfig->setValueBool($appId, $entry->getKey(), $this->convertToBool($value, $entry));
+ return;
+
+ case ValueType::ARRAY:
+ $this->appConfig->setValueArray($appId, $entry->getKey(), $this->convertToArray($value));
+ return;
+ }
+ }
+
+ /**
+ * converting value from rename to the new key
+ *
+ * @throws TypeConflictException if previous value does not fit the expected type
+ */
+ private function migrateUserConfigValue(string $userId, string $appId, ConfigLexiconEntry $entry): void {
+ $value = $this->userConfig->getValueMixed($userId, $appId, $entry->getRename(), lazy: null);
+ switch ($entry->getValueType()) {
+ case ValueType::STRING:
+ $this->userConfig->setValueString($userId, $appId, $entry->getKey(), $value);
+ return;
+
+ case ValueType::INT:
+ $this->userConfig->setValueInt($userId, $appId, $entry->getKey(), $this->convertToInt($value));
+ return;
+
+ case ValueType::FLOAT:
+ $this->userConfig->setValueFloat($userId, $appId, $entry->getKey(), $this->convertToFloat($value));
+ return;
+
+ case ValueType::BOOL:
+ $this->userConfig->setValueBool($userId, $appId, $entry->getKey(), $this->convertToBool($value, $entry));
+ return;
+
+ case ValueType::ARRAY:
+ $this->userConfig->setValueArray($userId, $appId, $entry->getKey(), $this->convertToArray($value));
+ return;
+ }
+ }
+
+ public function convertToInt(string $value): int {
+ if (!is_numeric($value) || (float)$value <> (int)$value) {
+ throw new TypeConflictException('Value is not an integer');
+ }
+
+ return (int)$value;
+ }
+
+ public function convertToFloat(string $value): float {
+ if (!is_numeric($value)) {
+ throw new TypeConflictException('Value is not a float');
+ }
+
+ return (float)$value;
+ }
+
+ public function convertToBool(string $value, ?ConfigLexiconEntry $entry = null): bool {
+ if (in_array(strtolower($value), ['true', '1', 'on', 'yes'])) {
+ $valueBool = true;
+ } elseif (in_array(strtolower($value), ['false', '0', 'off', 'no'])) {
+ $valueBool = false;
+ } else {
+ throw new TypeConflictException('Value cannot be converted to boolean');
+ }
+ if ($entry?->hasOption(ConfigLexiconEntry::RENAME_INVERT_BOOLEAN) === true) {
+ $valueBool = !$valueBool;
+ }
+
+ return $valueBool;
+ }
+
+ public function convertToArray(string $value): array {
+ try {
+ $valueArray = json_decode($value, true, flags: JSON_THROW_ON_ERROR);
+ } catch (JsonException) {
+ throw new TypeConflictException('Value is not a valid json');
+ }
+ if (!is_array($valueArray)) {
+ throw new TypeConflictException('Value is not an array');
+ }
+
+ return $valueArray;
+ }
+}
diff --git a/lib/private/Config/UserConfig.php b/lib/private/Config/UserConfig.php
index 77a86a5e1c7..7848f1728e3 100644
--- a/lib/private/Config/UserConfig.php
+++ b/lib/private/Config/UserConfig.php
@@ -25,6 +25,7 @@ use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\Security\ICrypto;
+use OCP\Server;
use Psr\Log\LoggerInterface;
/**
@@ -62,8 +63,9 @@ class UserConfig implements IUserConfig {
private array $fastLoaded = [];
/** @var array<string, boolean> ['user_id' => bool] */
private array $lazyLoaded = [];
- /** @var array<array-key, array{entries: array<array-key, ConfigLexiconEntry>, strictness: ConfigLexiconStrictness}> ['app_id' => ['strictness' => ConfigLexiconStrictness, 'entries' => ['config_key' => ConfigLexiconEntry[]]] */
+ /** @var array<string, array{entries: array<string, ConfigLexiconEntry>, aliases: array<string, string>, strictness: ConfigLexiconStrictness}> ['app_id' => ['strictness' => ConfigLexiconStrictness, 'entries' => ['config_key' => ConfigLexiconEntry[]]] */
private array $configLexiconDetails = [];
+ private bool $ignoreLexiconAliases = false;
public function __construct(
protected IDBConnection $connection,
@@ -150,6 +152,7 @@ class UserConfig implements IUserConfig {
public function hasKey(string $userId, string $app, string $key, ?bool $lazy = false): bool {
$this->assertParams($userId, $app, $key);
$this->loadConfig($userId, $lazy);
+ $this->matchAndApplyLexiconDefinition($userId, $app, $key);
if ($lazy === null) {
$appCache = $this->getValues($userId, $app);
@@ -178,6 +181,7 @@ class UserConfig implements IUserConfig {
public function isSensitive(string $userId, string $app, string $key, ?bool $lazy = false): bool {
$this->assertParams($userId, $app, $key);
$this->loadConfig($userId, $lazy);
+ $this->matchAndApplyLexiconDefinition($userId, $app, $key);
if (!isset($this->valueDetails[$userId][$app][$key])) {
throw new UnknownKeyException('unknown config key');
@@ -201,6 +205,7 @@ class UserConfig implements IUserConfig {
public function isIndexed(string $userId, string $app, string $key, ?bool $lazy = false): bool {
$this->assertParams($userId, $app, $key);
$this->loadConfig($userId, $lazy);
+ $this->matchAndApplyLexiconDefinition($userId, $app, $key);
if (!isset($this->valueDetails[$userId][$app][$key])) {
throw new UnknownKeyException('unknown config key');
@@ -222,6 +227,8 @@ class UserConfig implements IUserConfig {
* @since 31.0.0
*/
public function isLazy(string $userId, string $app, string $key): bool {
+ $this->matchAndApplyLexiconDefinition($userId, $app, $key);
+
// there is a huge probability the non-lazy config are already loaded
// meaning that we can start by only checking if a current non-lazy key exists
if ($this->hasKey($userId, $app, $key, false)) {
@@ -349,6 +356,7 @@ class UserConfig implements IUserConfig {
?array $userIds = null,
): array {
$this->assertParams('', $app, $key, allowEmptyUser: true);
+ $this->matchAndApplyLexiconDefinition('', $app, $key);
$qb = $this->connection->getQueryBuilder();
$qb->select('userid', 'configvalue', 'type')
@@ -464,6 +472,7 @@ class UserConfig implements IUserConfig {
*/
private function searchUsersByTypedValue(string $app, string $key, string|array $value, bool $caseInsensitive = false): Generator {
$this->assertParams('', $app, $key, allowEmptyUser: true);
+ $this->matchAndApplyLexiconDefinition('', $app, $key);
$qb = $this->connection->getQueryBuilder();
$qb->from('preferences');
@@ -541,6 +550,7 @@ class UserConfig implements IUserConfig {
string $default = '',
?bool $lazy = false,
): string {
+ $this->matchAndApplyLexiconDefinition($userId, $app, $key);
try {
$lazy ??= $this->isLazy($userId, $app, $key);
} catch (UnknownKeyException) {
@@ -710,6 +720,7 @@ class UserConfig implements IUserConfig {
ValueType $type,
): string {
$this->assertParams($userId, $app, $key);
+ $origKey = $key;
if (!$this->matchAndApplyLexiconDefinition($userId, $app, $key, $lazy, $type, default: $default)) {
// returns default if strictness of lexicon is set to WARNING (block and report)
return $default;
@@ -746,6 +757,15 @@ class UserConfig implements IUserConfig {
}
$this->decryptSensitiveValue($userId, $app, $key, $value);
+
+ // in case the key was modified while running matchAndApplyLexiconDefinition() we are
+ // interested to check options in case a modification of the value is needed
+ // ie inverting value from previous key when using lexicon option RENAME_INVERT_BOOLEAN
+ if ($origKey !== $key && $type === ValueType::BOOL) {
+ $configManager = Server::get(ConfigManager::class);
+ $value = ($configManager->convertToBool($value, $this->getLexiconEntry($app, $key))) ? '1' : '0';
+ }
+
return $value;
}
@@ -764,6 +784,7 @@ class UserConfig implements IUserConfig {
public function getValueType(string $userId, string $app, string $key, ?bool $lazy = null): ValueType {
$this->assertParams($userId, $app, $key);
$this->loadConfig($userId, $lazy);
+ $this->matchAndApplyLexiconDefinition($userId, $app, $key);
if (!isset($this->valueDetails[$userId][$app][$key]['type'])) {
throw new UnknownKeyException('unknown config key');
@@ -788,6 +809,7 @@ class UserConfig implements IUserConfig {
public function getValueFlags(string $userId, string $app, string $key, bool $lazy = false): int {
$this->assertParams($userId, $app, $key);
$this->loadConfig($userId, $lazy);
+ $this->matchAndApplyLexiconDefinition($userId, $app, $key);
if (!isset($this->valueDetails[$userId][$app][$key])) {
throw new UnknownKeyException('unknown config key');
@@ -1045,6 +1067,11 @@ class UserConfig implements IUserConfig {
int $flags,
ValueType $type,
): bool {
+ // Primary email addresses are always(!) expected to be lowercase
+ if ($app === 'settings' && $key === 'email') {
+ $value = strtolower($value);
+ }
+
$this->assertParams($userId, $app, $key);
if (!$this->matchAndApplyLexiconDefinition($userId, $app, $key, $lazy, $type, $flags)) {
// returns false as database is not updated
@@ -1129,8 +1156,8 @@ class UserConfig implements IUserConfig {
* we only accept a different type from the one stored in database
* if the one stored in database is not-defined (VALUE_MIXED)
*/
- if ($currType !== ValueType::MIXED &&
- $currType !== $type) {
+ if ($currType !== ValueType::MIXED
+ && $currType !== $type) {
try {
$currTypeDef = $currType->getDefinition();
$typeDef = $type->getDefinition();
@@ -1197,8 +1224,8 @@ class UserConfig implements IUserConfig {
public function updateType(string $userId, string $app, string $key, ValueType $type = ValueType::MIXED): bool {
$this->assertParams($userId, $app, $key);
$this->loadConfigAll($userId);
- // confirm key exists
- $this->isLazy($userId, $app, $key);
+ $this->matchAndApplyLexiconDefinition($userId, $app, $key);
+ $this->isLazy($userId, $app, $key); // confirm key exists
$update = $this->connection->getQueryBuilder();
$update->update('preferences')
@@ -1227,6 +1254,7 @@ class UserConfig implements IUserConfig {
public function updateSensitive(string $userId, string $app, string $key, bool $sensitive): bool {
$this->assertParams($userId, $app, $key);
$this->loadConfigAll($userId);
+ $this->matchAndApplyLexiconDefinition($userId, $app, $key);
try {
if ($sensitive === $this->isSensitive($userId, $app, $key, null)) {
@@ -1282,6 +1310,8 @@ class UserConfig implements IUserConfig {
*/
public function updateGlobalSensitive(string $app, string $key, bool $sensitive): void {
$this->assertParams('', $app, $key, allowEmptyUser: true);
+ $this->matchAndApplyLexiconDefinition('', $app, $key);
+
foreach (array_keys($this->getValuesByUsers($app, $key)) as $userId) {
try {
$this->updateSensitive($userId, $app, $key, $sensitive);
@@ -1311,6 +1341,7 @@ class UserConfig implements IUserConfig {
public function updateIndexed(string $userId, string $app, string $key, bool $indexed): bool {
$this->assertParams($userId, $app, $key);
$this->loadConfigAll($userId);
+ $this->matchAndApplyLexiconDefinition($userId, $app, $key);
try {
if ($indexed === $this->isIndexed($userId, $app, $key, null)) {
@@ -1366,6 +1397,8 @@ class UserConfig implements IUserConfig {
*/
public function updateGlobalIndexed(string $app, string $key, bool $indexed): void {
$this->assertParams('', $app, $key, allowEmptyUser: true);
+ $this->matchAndApplyLexiconDefinition('', $app, $key);
+
foreach (array_keys($this->getValuesByUsers($app, $key)) as $userId) {
try {
$this->updateIndexed($userId, $app, $key, $indexed);
@@ -1392,6 +1425,7 @@ class UserConfig implements IUserConfig {
public function updateLazy(string $userId, string $app, string $key, bool $lazy): bool {
$this->assertParams($userId, $app, $key);
$this->loadConfigAll($userId);
+ $this->matchAndApplyLexiconDefinition($userId, $app, $key);
try {
if ($lazy === $this->isLazy($userId, $app, $key)) {
@@ -1426,6 +1460,7 @@ class UserConfig implements IUserConfig {
*/
public function updateGlobalLazy(string $app, string $key, bool $lazy): void {
$this->assertParams('', $app, $key, allowEmptyUser: true);
+ $this->matchAndApplyLexiconDefinition('', $app, $key);
$update = $this->connection->getQueryBuilder();
$update->update('preferences')
@@ -1451,6 +1486,8 @@ class UserConfig implements IUserConfig {
public function getDetails(string $userId, string $app, string $key): array {
$this->assertParams($userId, $app, $key);
$this->loadConfigAll($userId);
+ $this->matchAndApplyLexiconDefinition($userId, $app, $key);
+
$lazy = $this->isLazy($userId, $app, $key);
if ($lazy) {
@@ -1498,6 +1535,8 @@ class UserConfig implements IUserConfig {
*/
public function deleteUserConfig(string $userId, string $app, string $key): void {
$this->assertParams($userId, $app, $key);
+ $this->matchAndApplyLexiconDefinition($userId, $app, $key);
+
$qb = $this->connection->getQueryBuilder();
$qb->delete('preferences')
->where($qb->expr()->eq('userid', $qb->createNamedParameter($userId)))
@@ -1520,6 +1559,8 @@ class UserConfig implements IUserConfig {
*/
public function deleteKey(string $app, string $key): void {
$this->assertParams('', $app, $key, allowEmptyUser: true);
+ $this->matchAndApplyLexiconDefinition('', $app, $key);
+
$qb = $this->connection->getQueryBuilder();
$qb->delete('preferences')
->where($qb->expr()->eq('appid', $qb->createNamedParameter($app)))
@@ -1538,6 +1579,7 @@ class UserConfig implements IUserConfig {
*/
public function deleteApp(string $app): void {
$this->assertParams('', $app, allowEmptyUser: true);
+
$qb = $this->connection->getQueryBuilder();
$qb->delete('preferences')
->where($qb->expr()->eq('appid', $qb->createNamedParameter($app)));
@@ -1830,7 +1872,8 @@ class UserConfig implements IUserConfig {
}
/**
- * match and apply current use of config values with defined lexicon
+ * Match and apply current use of config values with defined lexicon.
+ * Set $lazy to NULL only if only interested into checking that $key is alias.
*
* @throws UnknownKeyException
* @throws TypeConflictException
@@ -1839,17 +1882,27 @@ class UserConfig implements IUserConfig {
private function matchAndApplyLexiconDefinition(
string $userId,
string $app,
- string $key,
- bool &$lazy,
- ValueType &$type,
+ string &$key,
+ ?bool &$lazy = null,
+ ValueType &$type = ValueType::MIXED,
int &$flags = 0,
string &$default = '',
): bool {
$configDetails = $this->getConfigDetailsFromLexicon($app);
+ if (array_key_exists($key, $configDetails['aliases']) && !$this->ignoreLexiconAliases) {
+ // in case '$rename' is set in ConfigLexiconEntry, we use the new config key
+ $key = $configDetails['aliases'][$key];
+ }
+
if (!array_key_exists($key, $configDetails['entries'])) {
return $this->applyLexiconStrictness($configDetails['strictness'], 'The user config key ' . $app . '/' . $key . ' is not defined in the config lexicon');
}
+ // if lazy is NULL, we ignore all check on the type/lazyness/default from Lexicon
+ if ($lazy === null) {
+ return true;
+ }
+
/** @var ConfigLexiconEntry $configValue */
$configValue = $configDetails['entries'][$key];
if ($type === ValueType::MIXED) {
@@ -1934,24 +1987,42 @@ class UserConfig implements IUserConfig {
* extract details from registered $appId's config lexicon
*
* @param string $appId
+ * @internal
*
- * @return array{entries: array<array-key, ConfigLexiconEntry>, strictness: ConfigLexiconStrictness}
+ * @return array{entries: array<string, ConfigLexiconEntry>, aliases: array<string, string>, strictness: ConfigLexiconStrictness}
*/
- private function getConfigDetailsFromLexicon(string $appId): array {
+ public function getConfigDetailsFromLexicon(string $appId): array {
if (!array_key_exists($appId, $this->configLexiconDetails)) {
- $entries = [];
+ $entries = $aliases = [];
$bootstrapCoordinator = \OCP\Server::get(Coordinator::class);
$configLexicon = $bootstrapCoordinator->getRegistrationContext()?->getConfigLexicon($appId);
foreach ($configLexicon?->getUserConfigs() ?? [] as $configEntry) {
$entries[$configEntry->getKey()] = $configEntry;
+ if ($configEntry->getRename() !== null) {
+ $aliases[$configEntry->getRename()] = $configEntry->getKey();
+ }
}
$this->configLexiconDetails[$appId] = [
'entries' => $entries,
+ 'aliases' => $aliases,
'strictness' => $configLexicon?->getStrictness() ?? ConfigLexiconStrictness::IGNORE
];
}
return $this->configLexiconDetails[$appId];
}
+
+ private function getLexiconEntry(string $appId, string $key): ?ConfigLexiconEntry {
+ return $this->getConfigDetailsFromLexicon($appId)['entries'][$key] ?? null;
+ }
+
+ /**
+ * if set to TRUE, ignore aliases defined in Config Lexicon during the use of the methods of this class
+ *
+ * @internal
+ */
+ public function ignoreLexiconAliases(bool $ignore): void {
+ $this->ignoreLexiconAliases = $ignore;
+ }
}
diff --git a/lib/private/Console/Application.php b/lib/private/Console/Application.php
index f896c0abebe..4cf1e0da8ca 100644
--- a/lib/private/Console/Application.php
+++ b/lib/private/Console/Application.php
@@ -74,8 +74,8 @@ class Application {
if ($this->memoryInfo->isMemoryLimitSufficient() === false) {
$output->getErrorOutput()->writeln(
- '<comment>The current PHP memory limit ' .
- 'is below the recommended value of 512MB.</comment>'
+ '<comment>The current PHP memory limit '
+ . 'is below the recommended value of 512MB.</comment>'
);
}
diff --git a/lib/private/Contacts/ContactsMenu/ActionFactory.php b/lib/private/Contacts/ContactsMenu/ActionFactory.php
index 71ebe575fdd..40037598d49 100644
--- a/lib/private/Contacts/ContactsMenu/ActionFactory.php
+++ b/lib/private/Contacts/ContactsMenu/ActionFactory.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Contacts/ContactsMenu/Actions/LinkAction.php b/lib/private/Contacts/ContactsMenu/Actions/LinkAction.php
index 0d4cc9b9b01..cdaf9308bfc 100644
--- a/lib/private/Contacts/ContactsMenu/Actions/LinkAction.php
+++ b/lib/private/Contacts/ContactsMenu/Actions/LinkAction.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Contacts/ContactsMenu/Manager.php b/lib/private/Contacts/ContactsMenu/Manager.php
index 65a2c4469ea..f8def45421b 100644
--- a/lib/private/Contacts/ContactsMenu/Manager.php
+++ b/lib/private/Contacts/ContactsMenu/Manager.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Contacts/ContactsMenu/Providers/EMailProvider.php b/lib/private/Contacts/ContactsMenu/Providers/EMailProvider.php
index c852fc90b9e..266125f5ed5 100644
--- a/lib/private/Contacts/ContactsMenu/Providers/EMailProvider.php
+++ b/lib/private/Contacts/ContactsMenu/Providers/EMailProvider.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/DB/Adapter.php b/lib/private/DB/Adapter.php
index edd8c1bf023..8f1b8e6d75f 100644
--- a/lib/private/DB/Adapter.php
+++ b/lib/private/DB/Adapter.php
@@ -28,25 +28,11 @@ class Adapter {
/**
* @param string $table name
*
- * @return int id of last insert statement, 0 in case there was no INSERT before or it failed to get the ID
+ * @return int id of last insert statement
* @throws Exception
*/
- 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;
+ public function lastInsertId($table) {
+ return (int)$this->conn->realLastInsertId($table);
}
/**
diff --git a/lib/private/DB/AdapterOCI8.php b/lib/private/DB/AdapterOCI8.php
index f5ad9f7c934..0a509090bca 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, bool $allowRetry = true): int {
+ public function lastInsertId($table) {
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 b321fcf4715..db48c81c2c5 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, bool $allowRetry = true): int {
+ public function lastInsertId($table) {
$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 4ba2d2a341d..88bdc377e2b 100644
--- a/lib/private/DB/Connection.php
+++ b/lib/private/DB/Connection.php
@@ -92,8 +92,6 @@ 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' => [
@@ -512,9 +510,9 @@ class Connection extends PrimaryReadReplicaConnection {
* because the underlying database may not even support the notion of AUTO_INCREMENT/IDENTITY
* columns or sequences.
*
- * @param ?string $name Name of the sequence object from which the ID should be returned.
+ * @param string $seqName Name of the sequence object from which the ID should be returned.
*
- * @return int the last inserted ID, 0 in case there was no INSERT before or it failed to get the ID
+ * @return int the last inserted ID.
* @throws Exception
*/
public function lastInsertId($name = null): int {
@@ -528,13 +526,8 @@ class Connection extends PrimaryReadReplicaConnection {
* @internal
* @throws Exception
*/
- public function realLastInsertId($seqName = null): int {
- if ($this->lastInsertId !== 0) {
- $lastInsertId = $this->lastInsertId;
- $this->lastInsertId = 0;
- return $lastInsertId;
- }
- return (int)parent::lastInsertId($seqName);
+ public function realLastInsertId($seqName = null) {
+ return parent::lastInsertId($seqName);
}
/**
@@ -901,25 +894,13 @@ class Connection extends PrimaryReadReplicaConnection {
private function reconnectIfNeeded(): void {
if (
- !isset($this->lastConnectionCheck[$this->getConnectionName()]) ||
- time() <= $this->lastConnectionCheck[$this->getConnectionName()] + 30 ||
- $this->isTransactionActive() ||
- $this->disableReconnect
+ !isset($this->lastConnectionCheck[$this->getConnectionName()])
+ || time() <= $this->lastConnectionCheck[$this->getConnectionName()] + 30
+ || $this->isTransactionActive()
) {
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/DB/MigrationService.php b/lib/private/DB/MigrationService.php
index 2f4c8131d9f..40579c7a898 100644
--- a/lib/private/DB/MigrationService.php
+++ b/lib/private/DB/MigrationService.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2017 ownCloud GmbH
diff --git a/lib/private/DB/MySqlTools.php b/lib/private/DB/MySqlTools.php
index cd6b812be61..3413be43417 100644
--- a/lib/private/DB/MySqlTools.php
+++ b/lib/private/DB/MySqlTools.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 ownCloud GmbH
* SPDX-License-Identifier: AGPL-3.0-only
@@ -45,7 +46,7 @@ class MySqlTools {
return false;
}
- return str_contains($row, 'maria') && version_compare($row, '10.3', '>=') ||
- !str_contains($row, 'maria') && version_compare($row, '8.0', '>=');
+ return str_contains($row, 'maria') && version_compare($row, '10.3', '>=')
+ || !str_contains($row, 'maria') && version_compare($row, '8.0', '>=');
}
}
diff --git a/lib/private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php b/lib/private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php
index 559c29df208..52f82db2232 100644
--- a/lib/private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php
+++ b/lib/private/DB/QueryBuilder/ExpressionBuilder/SqliteExpressionBuilder.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php b/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php
index 2466493c1fa..48dc1da6330 100644
--- a/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php
+++ b/lib/private/DB/QueryBuilder/FunctionBuilder/FunctionBuilder.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php b/lib/private/DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php
index 6791430b1b0..8fae6275916 100644
--- a/lib/private/DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php
+++ b/lib/private/DB/QueryBuilder/FunctionBuilder/OCIFunctionBuilder.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/DB/QueryBuilder/FunctionBuilder/PgSqlFunctionBuilder.php b/lib/private/DB/QueryBuilder/FunctionBuilder/PgSqlFunctionBuilder.php
index ee430a6bd71..354a2b126d7 100644
--- a/lib/private/DB/QueryBuilder/FunctionBuilder/PgSqlFunctionBuilder.php
+++ b/lib/private/DB/QueryBuilder/FunctionBuilder/PgSqlFunctionBuilder.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/DB/QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php b/lib/private/DB/QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php
index 956b2123f2c..53aa530054b 100644
--- a/lib/private/DB/QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php
+++ b/lib/private/DB/QueryBuilder/FunctionBuilder/SqliteFunctionBuilder.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/DB/QueryBuilder/Partitioned/PartitionedQueryBuilder.php b/lib/private/DB/QueryBuilder/Partitioned/PartitionedQueryBuilder.php
index 2942eeccdf7..d748c791321 100644
--- a/lib/private/DB/QueryBuilder/Partitioned/PartitionedQueryBuilder.php
+++ b/lib/private/DB/QueryBuilder/Partitioned/PartitionedQueryBuilder.php
@@ -126,8 +126,8 @@ class PartitionedQueryBuilder extends ShardedQueryBuilder {
$selectPartition = null;
}
if (
- ($select === $checkColumn || $select === '*') &&
- $selectPartition === $partition
+ ($select === $checkColumn || $select === '*')
+ && $selectPartition === $partition
) {
return;
}
@@ -151,8 +151,8 @@ class PartitionedQueryBuilder extends ShardedQueryBuilder {
foreach ($this->selects as $select) {
foreach ($this->partitions as $partition) {
if (is_string($select['select']) && (
- $select['select'] === '*' ||
- $partition->isColumnInPartition($select['select']))
+ $select['select'] === '*'
+ || $partition->isColumnInPartition($select['select']))
) {
if (isset($this->splitQueries[$partition->name])) {
if ($select['alias']) {
@@ -444,4 +444,19 @@ class PartitionedQueryBuilder extends ShardedQueryBuilder {
public function getPartitionCount(): int {
return count($this->splitQueries) + 1;
}
+
+ public function hintShardKey(string $column, mixed $value, bool $overwrite = false): self {
+ if (str_contains($column, '.')) {
+ [$alias, $column] = explode('.', $column);
+ $partition = $this->getPartition($alias);
+ if ($partition) {
+ $this->splitQueries[$partition->name]->query->hintShardKey($column, $value, $overwrite);
+ } else {
+ parent::hintShardKey($column, $value, $overwrite);
+ }
+ } else {
+ parent::hintShardKey($column, $value, $overwrite);
+ }
+ return $this;
+ }
}
diff --git a/lib/private/DB/SchemaWrapper.php b/lib/private/DB/SchemaWrapper.php
index 473c0009237..0d5b2040513 100644
--- a/lib/private/DB/SchemaWrapper.php
+++ b/lib/private/DB/SchemaWrapper.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Diagnostics/Event.php b/lib/private/Diagnostics/Event.php
index cf36bf9f82a..11d6505a888 100644
--- a/lib/private/Diagnostics/Event.php
+++ b/lib/private/Diagnostics/Event.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/lib/private/Diagnostics/EventLogger.php b/lib/private/Diagnostics/EventLogger.php
index 40cbd3e9e5d..3ec35a5e9ce 100644
--- a/lib/private/Diagnostics/EventLogger.php
+++ b/lib/private/Diagnostics/EventLogger.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/lib/private/Diagnostics/Query.php b/lib/private/Diagnostics/Query.php
index 81610074709..020fc4bb512 100644
--- a/lib/private/Diagnostics/Query.php
+++ b/lib/private/Diagnostics/Query.php
@@ -56,7 +56,7 @@ class Query implements IQuery {
public function getStart() {
return $this->start;
}
-
+
/**
* @return float
*/
diff --git a/lib/private/DirectEditing/Manager.php b/lib/private/DirectEditing/Manager.php
index 4823b6b4456..154002ef340 100644
--- a/lib/private/DirectEditing/Manager.php
+++ b/lib/private/DirectEditing/Manager.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/DirectEditing/Token.php b/lib/private/DirectEditing/Token.php
index 12ad9411216..ca01265f9df 100644
--- a/lib/private/DirectEditing/Token.php
+++ b/lib/private/DirectEditing/Token.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/EmojiHelper.php b/lib/private/EmojiHelper.php
index 514973b2959..52ab441e73a 100644
--- a/lib/private/EmojiHelper.php
+++ b/lib/private/EmojiHelper.php
@@ -19,8 +19,8 @@ class EmojiHelper implements IEmojiHelper {
}
public function doesPlatformSupportEmoji(): bool {
- return $this->db->supports4ByteText() &&
- \class_exists(\IntlBreakIterator::class);
+ return $this->db->supports4ByteText()
+ && \class_exists(\IntlBreakIterator::class);
}
public function isValidSingleEmoji(string $emoji): bool {
@@ -48,17 +48,17 @@ class EmojiHelper implements IEmojiHelper {
if (strlen($emoji) >= 2) {
// If the current code-point is an emoji or a modifier (like a skin-tone)
// just continue and check the next character
- if ($codePointType === \IntlChar::CHAR_CATEGORY_MODIFIER_SYMBOL ||
- $codePointType === \IntlChar::CHAR_CATEGORY_MODIFIER_LETTER ||
- $codePointType === \IntlChar::CHAR_CATEGORY_OTHER_SYMBOL ||
- $codePointType === \IntlChar::CHAR_CATEGORY_FORMAT_CHAR || // i.e. 🏴󠁧󠁢󠁥󠁮󠁧󠁿 🏴󠁧󠁢󠁳󠁣󠁴󠁿
- $codePointType === \IntlChar::CHAR_CATEGORY_OTHER_PUNCTUATION || // i.e. ‼️ ⁉️ #⃣
- $codePointType === \IntlChar::CHAR_CATEGORY_LOWERCASE_LETTER || // i.e. ℹ️
- $codePointType === \IntlChar::CHAR_CATEGORY_MATH_SYMBOL || // i.e. ↔️ ◻️ ⤴️ ⤵️
- $codePointType === \IntlChar::CHAR_CATEGORY_ENCLOSING_MARK || // i.e. 0⃣..9⃣
- $codePointType === \IntlChar::CHAR_CATEGORY_DECIMAL_DIGIT_NUMBER || // i.e. 0⃣..9⃣
- $codePointType === \IntlChar::CHAR_CATEGORY_DASH_PUNCTUATION || // i.e. 〰️
- $codePointType === \IntlChar::CHAR_CATEGORY_GENERAL_OTHER_TYPES
+ if ($codePointType === \IntlChar::CHAR_CATEGORY_MODIFIER_SYMBOL
+ || $codePointType === \IntlChar::CHAR_CATEGORY_MODIFIER_LETTER
+ || $codePointType === \IntlChar::CHAR_CATEGORY_OTHER_SYMBOL
+ || $codePointType === \IntlChar::CHAR_CATEGORY_FORMAT_CHAR // i.e. 🏴󠁧󠁢󠁥󠁮󠁧󠁿 🏴󠁧󠁢󠁳󠁣󠁴󠁿
+ || $codePointType === \IntlChar::CHAR_CATEGORY_OTHER_PUNCTUATION // i.e. ‼️ ⁉️ #⃣
+ || $codePointType === \IntlChar::CHAR_CATEGORY_LOWERCASE_LETTER // i.e. ℹ️
+ || $codePointType === \IntlChar::CHAR_CATEGORY_MATH_SYMBOL // i.e. ↔️ ◻️ ⤴️ ⤵️
+ || $codePointType === \IntlChar::CHAR_CATEGORY_ENCLOSING_MARK // i.e. 0⃣..9⃣
+ || $codePointType === \IntlChar::CHAR_CATEGORY_DECIMAL_DIGIT_NUMBER // i.e. 0⃣..9⃣
+ || $codePointType === \IntlChar::CHAR_CATEGORY_DASH_PUNCTUATION // i.e. 〰️
+ || $codePointType === \IntlChar::CHAR_CATEGORY_GENERAL_OTHER_TYPES
) {
continue;
}
diff --git a/lib/private/Encryption/EncryptionEventListener.php b/lib/private/Encryption/EncryptionEventListener.php
index 59ac0dea932..d51b4b0d531 100644
--- a/lib/private/Encryption/EncryptionEventListener.php
+++ b/lib/private/Encryption/EncryptionEventListener.php
@@ -17,7 +17,9 @@ use OCP\EventDispatcher\Event;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\EventDispatcher\IEventListener;
use OCP\Files\Events\Node\NodeRenamedEvent;
+use OCP\Files\NotFoundException;
use OCP\IUser;
+use OCP\IUserManager;
use OCP\IUserSession;
use OCP\Share\Events\ShareCreatedEvent;
use OCP\Share\Events\ShareDeletedEvent;
@@ -31,6 +33,7 @@ class EncryptionEventListener implements IEventListener {
private IUserSession $userSession,
private SetupManager $setupManager,
private Manager $encryptionManager,
+ private IUserManager $userManager,
) {
}
@@ -50,10 +53,14 @@ class EncryptionEventListener implements IEventListener {
} 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());
+ try {
+ // 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 = $this->userManager->get($event->getShare()->getShareOwner());
+ $this->getUpdate($owner)->postUnshared($event->getShare()->getNode());
+ } catch (NotFoundException $e) {
+ /* The node was deleted already, nothing to update */
+ }
} elseif ($event instanceof NodeRestoredEvent) {
$this->getUpdate()->postRestore($event->getTarget());
}
@@ -78,7 +85,7 @@ class EncryptionEventListener implements IEventListener {
$this->updater = new Update(
new Util(
new View(),
- \OC::$server->getUserManager(),
+ $this->userManager,
\OC::$server->getGroupManager(),
\OC::$server->getConfig()),
\OC::$server->getEncryptionManager(),
diff --git a/lib/private/Encryption/Keys/Storage.php b/lib/private/Encryption/Keys/Storage.php
index 2c0ce9e5ef3..cce22b9138a 100644
--- a/lib/private/Encryption/Keys/Storage.php
+++ b/lib/private/Encryption/Keys/Storage.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/lib/private/Encryption/Util.php b/lib/private/Encryption/Util.php
index 0566ab9a760..2d7bc28129b 100644
--- a/lib/private/Encryption/Util.php
+++ b/lib/private/Encryption/Util.php
@@ -290,8 +290,8 @@ class Util {
if (count($root) > 1) {
// detect alternative key storage root
$rootDir = $this->getKeyStorageRoot();
- if ($rootDir !== '' &&
- str_starts_with(Filesystem::normalizePath($path), Filesystem::normalizePath($rootDir))
+ if ($rootDir !== ''
+ && str_starts_with(Filesystem::normalizePath($path), Filesystem::normalizePath($rootDir))
) {
return true;
}
diff --git a/lib/private/Federation/CloudFederationFactory.php b/lib/private/Federation/CloudFederationFactory.php
index f5f25d14ea1..d06de0f2f58 100644
--- a/lib/private/Federation/CloudFederationFactory.php
+++ b/lib/private/Federation/CloudFederationFactory.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Federation/CloudFederationNotification.php b/lib/private/Federation/CloudFederationNotification.php
index 855580843ba..6ae805df1d9 100644
--- a/lib/private/Federation/CloudFederationNotification.php
+++ b/lib/private/Federation/CloudFederationNotification.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Federation/CloudFederationProviderManager.php b/lib/private/Federation/CloudFederationProviderManager.php
index e9354294351..81b5d717a56 100644
--- a/lib/private/Federation/CloudFederationProviderManager.php
+++ b/lib/private/Federation/CloudFederationProviderManager.php
@@ -227,8 +227,8 @@ class CloudFederationProviderManager implements ICloudFederationProviderManager
private function prepareOcmPayload(string $uri, string $payload): array {
$payload = array_merge($this->getDefaultRequestOptions(), ['body' => $payload]);
- if ($this->appConfig->getValueBool('core', OCMSignatoryManager::APPCONFIG_SIGN_ENFORCED, lazy: true) &&
- $this->signatoryManager->getRemoteSignatory($this->signatureManager->extractIdentityFromUri($uri)) === null) {
+ if ($this->appConfig->getValueBool('core', OCMSignatoryManager::APPCONFIG_SIGN_ENFORCED, lazy: true)
+ && $this->signatoryManager->getRemoteSignatory($this->signatureManager->extractIdentityFromUri($uri)) === null) {
return $payload;
}
diff --git a/lib/private/Federation/CloudFederationShare.php b/lib/private/Federation/CloudFederationShare.php
index 3ec53d89ed3..2eb06b3acea 100644
--- a/lib/private/Federation/CloudFederationShare.php
+++ b/lib/private/Federation/CloudFederationShare.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Files/Cache/Cache.php b/lib/private/Files/Cache/Cache.php
index cb160115851..329466e682d 100644
--- a/lib/private/Files/Cache/Cache.php
+++ b/lib/private/Files/Cache/Cache.php
@@ -668,8 +668,8 @@ class Cache implements ICache {
$shardDefinition = $this->connection->getShardDefinition('filecache');
if (
- $shardDefinition &&
- $shardDefinition->getShardForKey($sourceCache->getNumericStorageId()) !== $shardDefinition->getShardForKey($this->getNumericStorageId())
+ $shardDefinition
+ && $shardDefinition->getShardForKey($sourceCache->getNumericStorageId()) !== $shardDefinition->getShardForKey($this->getNumericStorageId())
) {
$this->moveFromStorageSharded($shardDefinition, $sourceCache, $sourceData, $targetPath);
return;
diff --git a/lib/private/Files/Cache/QuerySearchHelper.php b/lib/private/Files/Cache/QuerySearchHelper.php
index ff2d6766893..3ddcf1ca4e6 100644
--- a/lib/private/Files/Cache/QuerySearchHelper.php
+++ b/lib/private/Files/Cache/QuerySearchHelper.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Files/Cache/SearchBuilder.php b/lib/private/Files/Cache/SearchBuilder.php
index 6a0cba7f1f2..e1d3c42a8a2 100644
--- a/lib/private/Files/Cache/SearchBuilder.php
+++ b/lib/private/Files/Cache/SearchBuilder.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Files/Cache/Storage.php b/lib/private/Files/Cache/Storage.php
index 2b49e65f0b4..1a3bda58e6a 100644
--- a/lib/private/Files/Cache/Storage.php
+++ b/lib/private/Files/Cache/Storage.php
@@ -213,6 +213,7 @@ class Storage {
$query = $db->getQueryBuilder();
$query->delete('filecache')
->where($query->expr()->in('storage', $query->createNamedParameter($storageIds, IQueryBuilder::PARAM_INT_ARRAY)));
+ $query->runAcrossAllShards();
$query->executeStatement();
$query = $db->getQueryBuilder();
diff --git a/lib/private/Files/Cache/Wrapper/JailPropagator.php b/lib/private/Files/Cache/Wrapper/JailPropagator.php
index 19ca4a13ece..d6409b7875e 100644
--- a/lib/private/Files/Cache/Wrapper/JailPropagator.php
+++ b/lib/private/Files/Cache/Wrapper/JailPropagator.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Files/Config/CachedMountFileInfo.php b/lib/private/Files/Config/CachedMountFileInfo.php
index 90a6b47f9d8..69bd4e9301e 100644
--- a/lib/private/Files/Config/CachedMountFileInfo.php
+++ b/lib/private/Files/Config/CachedMountFileInfo.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Files/Config/MountProviderCollection.php b/lib/private/Files/Config/MountProviderCollection.php
index 6a5407934c8..9d63184e05f 100644
--- a/lib/private/Files/Config/MountProviderCollection.php
+++ b/lib/private/Files/Config/MountProviderCollection.php
@@ -24,60 +24,43 @@ class MountProviderCollection implements IMountProviderCollection, Emitter {
use EmitterTrait;
/**
- * @var \OCP\Files\Config\IHomeMountProvider[]
+ * @var list<IHomeMountProvider>
*/
- private $homeProviders = [];
+ private array $homeProviders = [];
/**
- * @var \OCP\Files\Config\IMountProvider[]
+ * @var list<IMountProvider>
*/
- private $providers = [];
+ private array $providers = [];
- /** @var \OCP\Files\Config\IRootMountProvider[] */
- private $rootProviders = [];
+ /** @var list<IRootMountProvider> */
+ private array $rootProviders = [];
- /**
- * @var \OCP\Files\Storage\IStorageFactory
- */
- private $loader;
-
- /**
- * @var \OCP\Files\Config\IUserMountCache
- */
- private $mountCache;
-
- /** @var callable[] */
- private $mountFilters = [];
+ /** @var list<callable> */
+ private array $mountFilters = [];
- private IEventLogger $eventLogger;
-
- /**
- * @param \OCP\Files\Storage\IStorageFactory $loader
- * @param IUserMountCache $mountCache
- */
public function __construct(
- IStorageFactory $loader,
- IUserMountCache $mountCache,
- IEventLogger $eventLogger,
+ private IStorageFactory $loader,
+ private IUserMountCache $mountCache,
+ private IEventLogger $eventLogger,
) {
- $this->loader = $loader;
- $this->mountCache = $mountCache;
- $this->eventLogger = $eventLogger;
}
+ /**
+ * @return list<IMountPoint>
+ */
private function getMountsFromProvider(IMountProvider $provider, IUser $user, IStorageFactory $loader): array {
$class = str_replace('\\', '_', get_class($provider));
$uid = $user->getUID();
$this->eventLogger->start('fs:setup:provider:' . $class, "Getting mounts from $class for $uid");
$mounts = $provider->getMountsForUser($user, $loader) ?? [];
$this->eventLogger->end('fs:setup:provider:' . $class);
- return $mounts;
+ return array_values($mounts);
}
/**
- * @param IUser $user
- * @param IMountProvider[] $providers
- * @return IMountPoint[]
+ * @param list<IMountProvider> $providers
+ * @return list<IMountPoint>
*/
private function getUserMountsForProviders(IUser $user, array $providers): array {
$loader = $this->loader;
@@ -90,10 +73,16 @@ class MountProviderCollection implements IMountProviderCollection, Emitter {
return $this->filterMounts($user, $mounts);
}
+ /**
+ * @return list<IMountPoint>
+ */
public function getMountsForUser(IUser $user): array {
return $this->getUserMountsForProviders($user, $this->providers);
}
+ /**
+ * @return list<IMountPoint>
+ */
public function getUserMountsForProviderClasses(IUser $user, array $mountProviderClasses): array {
$providers = array_filter(
$this->providers,
@@ -102,7 +91,10 @@ class MountProviderCollection implements IMountProviderCollection, Emitter {
return $this->getUserMountsForProviders($user, $providers);
}
- public function addMountForUser(IUser $user, IMountManager $mountManager, ?callable $providerFilter = null) {
+ /**
+ * @return list<IMountPoint>
+ */
+ public function addMountForUser(IUser $user, IMountManager $mountManager, ?callable $providerFilter = null): array {
// shared mount provider gets to go last since it needs to know existing files
// to check for name collisions
$firstMounts = [];
@@ -135,18 +127,15 @@ class MountProviderCollection implements IMountProviderCollection, Emitter {
array_walk($lateMounts, [$mountManager, 'addMount']);
$this->eventLogger->end('fs:setup:add-mounts');
- return array_merge($lateMounts, $firstMounts);
+ return array_values(array_merge($lateMounts, $firstMounts));
}
/**
* Get the configured home mount for this user
*
- * @param \OCP\IUser $user
- * @return \OCP\Files\Mount\IMountPoint
* @since 9.1.0
*/
- public function getHomeMountForUser(IUser $user) {
- /** @var \OCP\Files\Config\IHomeMountProvider[] $providers */
+ public function getHomeMountForUser(IUser $user): IMountPoint {
$providers = array_reverse($this->homeProviders); // call the latest registered provider first to give apps an opportunity to overwrite builtin
foreach ($providers as $homeProvider) {
if ($mount = $homeProvider->getHomeMountForUser($user, $this->loader)) {
@@ -159,34 +148,36 @@ class MountProviderCollection implements IMountProviderCollection, Emitter {
/**
* Add a provider for mount points
- *
- * @param \OCP\Files\Config\IMountProvider $provider
*/
- public function registerProvider(IMountProvider $provider) {
+ public function registerProvider(IMountProvider $provider): void {
$this->providers[] = $provider;
$this->emit('\OC\Files\Config', 'registerMountProvider', [$provider]);
}
- public function registerMountFilter(callable $filter) {
+ public function registerMountFilter(callable $filter): void {
$this->mountFilters[] = $filter;
}
- private function filterMounts(IUser $user, array $mountPoints) {
- return array_filter($mountPoints, function (IMountPoint $mountPoint) use ($user) {
+ /**
+ * @param list<IMountPoint> $mountPoints
+ * @return list<IMountPoint>
+ */
+ private function filterMounts(IUser $user, array $mountPoints): array {
+ return array_values(array_filter($mountPoints, function (IMountPoint $mountPoint) use ($user) {
foreach ($this->mountFilters as $filter) {
if ($filter($mountPoint, $user) === false) {
return false;
}
}
return true;
- });
+ }));
}
/**
* Add a provider for home mount points
*
- * @param \OCP\Files\Config\IHomeMountProvider $provider
+ * @param IHomeMountProvider $provider
* @since 9.1.0
*/
public function registerHomeProvider(IHomeMountProvider $provider) {
@@ -196,21 +187,19 @@ class MountProviderCollection implements IMountProviderCollection, Emitter {
/**
* Get the mount cache which can be used to search for mounts without setting up the filesystem
- *
- * @return IUserMountCache
*/
- public function getMountCache() {
+ public function getMountCache(): IUserMountCache {
return $this->mountCache;
}
- public function registerRootProvider(IRootMountProvider $provider) {
+ public function registerRootProvider(IRootMountProvider $provider): void {
$this->rootProviders[] = $provider;
}
/**
* Get all root mountpoints
*
- * @return \OCP\Files\Mount\IMountPoint[]
+ * @return list<IMountPoint>
* @since 20.0.0
*/
public function getRootMounts(): array {
@@ -226,16 +215,33 @@ class MountProviderCollection implements IMountProviderCollection, Emitter {
throw new \Exception('No root mounts provided by any provider');
}
- return $mounts;
+ return array_values($mounts);
}
- public function clearProviders() {
+ public function clearProviders(): void {
$this->providers = [];
$this->homeProviders = [];
$this->rootProviders = [];
}
+ /**
+ * @return list<IMountProvider>
+ */
public function getProviders(): array {
return $this->providers;
}
+
+ /**
+ * @return list<IHomeMountProvider>
+ */
+ public function getHomeProviders(): array {
+ return $this->homeProviders;
+ }
+
+ /**
+ * @return list<IRootMountProvider>
+ */
+ public function getRootProviders(): array {
+ return $this->rootProviders;
+ }
}
diff --git a/lib/private/Files/Config/UserMountCache.php b/lib/private/Files/Config/UserMountCache.php
index 09ac6d1416a..3e53a67a044 100644
--- a/lib/private/Files/Config/UserMountCache.php
+++ b/lib/private/Files/Config/UserMountCache.php
@@ -151,9 +151,9 @@ class UserMountCache implements IUserMountCache {
if (isset($newMounts[$key])) {
$newMount = $newMounts[$key];
if (
- $newMount->getStorageId() !== $cachedMount->getStorageId() ||
- $newMount->getMountId() !== $cachedMount->getMountId() ||
- $newMount->getMountProvider() !== $cachedMount->getMountProvider()
+ $newMount->getStorageId() !== $cachedMount->getStorageId()
+ || $newMount->getMountId() !== $cachedMount->getMountId()
+ || $newMount->getMountProvider() !== $cachedMount->getMountProvider()
) {
$changed[] = [$cachedMount, $newMount];
}
diff --git a/lib/private/Files/Conversion/ConversionManager.php b/lib/private/Files/Conversion/ConversionManager.php
index cf1085f66f0..2c98a4c6404 100644
--- a/lib/private/Files/Conversion/ConversionManager.php
+++ b/lib/private/Files/Conversion/ConversionManager.php
@@ -129,7 +129,7 @@ class ConversionManager implements IConversionManager {
$this->preferredProviders[$class] = $this->serverContainer->get($class);
continue;
}
-
+
$this->providers[$class] = $this->serverContainer->get($class);
} catch (NotFoundExceptionInterface|ContainerExceptionInterface|Throwable $e) {
$this->logger->error('Failed to load file conversion provider ' . $class, [
@@ -175,7 +175,7 @@ class ConversionManager implements IConversionManager {
}
}
}
-
+
return null;
}
}
diff --git a/lib/private/Files/FilenameValidator.php b/lib/private/Files/FilenameValidator.php
index 57a62b0b219..a78c6d3cc3c 100644
--- a/lib/private/Files/FilenameValidator.php
+++ b/lib/private/Files/FilenameValidator.php
@@ -232,7 +232,7 @@ class FilenameValidator implements IFilenameValidator {
$forbiddenCharacters = $this->getForbiddenCharacters();
if ($charReplacement === null) {
- $charReplacement = array_diff([' ', '_', '-'], $forbiddenCharacters);
+ $charReplacement = array_diff(['_', '-', ' '], $forbiddenCharacters);
$charReplacement = reset($charReplacement) ?: '';
}
if (mb_strlen($charReplacement) !== 1) {
diff --git a/lib/private/Files/Mount/ObjectHomeMountProvider.php b/lib/private/Files/Mount/ObjectHomeMountProvider.php
index 99c52108fa8..4b088f2c808 100644
--- a/lib/private/Files/Mount/ObjectHomeMountProvider.php
+++ b/lib/private/Files/Mount/ObjectHomeMountProvider.php
@@ -7,117 +7,39 @@
*/
namespace OC\Files\Mount;
+use OC\Files\ObjectStore\HomeObjectStoreStorage;
+use OC\Files\ObjectStore\PrimaryObjectStoreConfig;
use OCP\Files\Config\IHomeMountProvider;
+use OCP\Files\Mount\IMountPoint;
use OCP\Files\Storage\IStorageFactory;
-use OCP\IConfig;
use OCP\IUser;
-use Psr\Log\LoggerInterface;
/**
* Mount provider for object store home storages
*/
class ObjectHomeMountProvider implements IHomeMountProvider {
- /**
- * @var IConfig
- */
- private $config;
-
- /**
- * ObjectStoreHomeMountProvider constructor.
- *
- * @param IConfig $config
- */
- public function __construct(IConfig $config) {
- $this->config = $config;
+ public function __construct(
+ private PrimaryObjectStoreConfig $objectStoreConfig,
+ ) {
}
/**
- * Get the cache mount for a user
+ * Get the home mount for a user
*
* @param IUser $user
* @param IStorageFactory $loader
- * @return \OCP\Files\Mount\IMountPoint
+ * @return ?IMountPoint
*/
- public function getHomeMountForUser(IUser $user, IStorageFactory $loader) {
- $config = $this->getMultiBucketObjectStoreConfig($user);
- if ($config === null) {
- $config = $this->getSingleBucketObjectStoreConfig($user);
- }
-
- if ($config === null) {
+ public function getHomeMountForUser(IUser $user, IStorageFactory $loader): ?IMountPoint {
+ $objectStoreConfig = $this->objectStoreConfig->getObjectStoreConfigForUser($user);
+ if ($objectStoreConfig === null) {
return null;
}
+ $arguments = array_merge($objectStoreConfig['arguments'], [
+ 'objectstore' => $this->objectStoreConfig->buildObjectStore($objectStoreConfig),
+ 'user' => $user,
+ ]);
- return new HomeMountPoint($user, '\OC\Files\ObjectStore\HomeObjectStoreStorage', '/' . $user->getUID(), $config['arguments'], $loader, null, null, self::class);
- }
-
- /**
- * @param IUser $user
- * @return array|null
- */
- private function getSingleBucketObjectStoreConfig(IUser $user) {
- $config = $this->config->getSystemValue('objectstore');
- if (!is_array($config)) {
- return null;
- }
-
- // sanity checks
- if (empty($config['class'])) {
- \OC::$server->get(LoggerInterface::class)->error('No class given for objectstore', ['app' => 'files']);
- }
- if (!isset($config['arguments'])) {
- $config['arguments'] = [];
- }
- // instantiate object store implementation
- $config['arguments']['objectstore'] = new $config['class']($config['arguments']);
-
- $config['arguments']['user'] = $user;
-
- return $config;
- }
-
- /**
- * @param IUser $user
- * @return array|null
- */
- private function getMultiBucketObjectStoreConfig(IUser $user) {
- $config = $this->config->getSystemValue('objectstore_multibucket');
- if (!is_array($config)) {
- return null;
- }
-
- // sanity checks
- if (empty($config['class'])) {
- \OC::$server->get(LoggerInterface::class)->error('No class given for objectstore', ['app' => 'files']);
- }
- if (!isset($config['arguments'])) {
- $config['arguments'] = [];
- }
-
- $bucket = $this->config->getUserValue($user->getUID(), 'homeobjectstore', 'bucket', null);
-
- if ($bucket === null) {
- /*
- * Use any provided bucket argument as prefix
- * and add the mapping from username => bucket
- */
- if (!isset($config['arguments']['bucket'])) {
- $config['arguments']['bucket'] = '';
- }
- $mapper = new \OC\Files\ObjectStore\Mapper($user, $this->config);
- $numBuckets = $config['arguments']['num_buckets'] ?? 64;
- $config['arguments']['bucket'] .= $mapper->getBucket($numBuckets);
-
- $this->config->setUserValue($user->getUID(), 'homeobjectstore', 'bucket', $config['arguments']['bucket']);
- } else {
- $config['arguments']['bucket'] = $bucket;
- }
-
- // instantiate object store implementation
- $config['arguments']['objectstore'] = new $config['class']($config['arguments']);
-
- $config['arguments']['user'] = $user;
-
- return $config;
+ return new HomeMountPoint($user, HomeObjectStoreStorage::class, '/' . $user->getUID(), $arguments, $loader, null, null, self::class);
}
}
diff --git a/lib/private/Files/Mount/RootMountProvider.php b/lib/private/Files/Mount/RootMountProvider.php
index 86f8188978f..5e0c924ad38 100644
--- a/lib/private/Files/Mount/RootMountProvider.php
+++ b/lib/private/Files/Mount/RootMountProvider.php
@@ -10,79 +10,41 @@ namespace OC\Files\Mount;
use OC;
use OC\Files\ObjectStore\ObjectStoreStorage;
+use OC\Files\ObjectStore\PrimaryObjectStoreConfig;
use OC\Files\Storage\LocalRootStorage;
-use OC_App;
use OCP\Files\Config\IRootMountProvider;
use OCP\Files\Storage\IStorageFactory;
use OCP\IConfig;
-use Psr\Log\LoggerInterface;
class RootMountProvider implements IRootMountProvider {
+ private PrimaryObjectStoreConfig $objectStoreConfig;
private IConfig $config;
- private LoggerInterface $logger;
- public function __construct(IConfig $config, LoggerInterface $logger) {
+ public function __construct(PrimaryObjectStoreConfig $objectStoreConfig, IConfig $config) {
+ $this->objectStoreConfig = $objectStoreConfig;
$this->config = $config;
- $this->logger = $logger;
}
public function getRootMounts(IStorageFactory $loader): array {
- $objectStore = $this->config->getSystemValue('objectstore', null);
- $objectStoreMultiBucket = $this->config->getSystemValue('objectstore_multibucket', null);
+ $objectStoreConfig = $this->objectStoreConfig->getObjectStoreConfigForRoot();
- if ($objectStoreMultiBucket) {
- return [$this->getMultiBucketStoreRootMount($loader, $objectStoreMultiBucket)];
- } elseif ($objectStore) {
- return [$this->getObjectStoreRootMount($loader, $objectStore)];
+ if ($objectStoreConfig) {
+ return [$this->getObjectStoreRootMount($loader, $objectStoreConfig)];
} else {
return [$this->getLocalRootMount($loader)];
}
}
- private function validateObjectStoreConfig(array &$config) {
- if (empty($config['class'])) {
- $this->logger->error('No class given for objectstore', ['app' => 'files']);
- }
- if (!isset($config['arguments'])) {
- $config['arguments'] = [];
- }
-
- // instantiate object store implementation
- $name = $config['class'];
- if (str_starts_with($name, 'OCA\\') && substr_count($name, '\\') >= 2) {
- $segments = explode('\\', $name);
- OC_App::loadApp(strtolower($segments[1]));
- }
- }
-
private function getLocalRootMount(IStorageFactory $loader): MountPoint {
$configDataDirectory = $this->config->getSystemValue('datadirectory', OC::$SERVERROOT . '/data');
return new MountPoint(LocalRootStorage::class, '/', ['datadir' => $configDataDirectory], $loader, null, null, self::class);
}
- private function getObjectStoreRootMount(IStorageFactory $loader, array $config): MountPoint {
- $this->validateObjectStoreConfig($config);
-
- $config['arguments']['objectstore'] = new $config['class']($config['arguments']);
- // mount with plain / root object store implementation
- $config['class'] = ObjectStoreStorage::class;
-
- return new MountPoint($config['class'], '/', $config['arguments'], $loader, null, null, self::class);
- }
-
- private function getMultiBucketStoreRootMount(IStorageFactory $loader, array $config): MountPoint {
- $this->validateObjectStoreConfig($config);
-
- if (!isset($config['arguments']['bucket'])) {
- $config['arguments']['bucket'] = '';
- }
- // put the root FS always in first bucket for multibucket configuration
- $config['arguments']['bucket'] .= '0';
-
- $config['arguments']['objectstore'] = new $config['class']($config['arguments']);
- // mount with plain / root object store implementation
- $config['class'] = ObjectStoreStorage::class;
+ private function getObjectStoreRootMount(IStorageFactory $loader, array $objectStoreConfig): MountPoint {
+ $arguments = array_merge($objectStoreConfig['arguments'], [
+ 'objectstore' => $this->objectStoreConfig->buildObjectStore($objectStoreConfig),
+ ]);
- return new MountPoint($config['class'], '/', $config['arguments'], $loader, null, null, self::class);
+ return new MountPoint(ObjectStoreStorage::class, '/', $arguments, $loader, null, null, self::class);
}
}
diff --git a/lib/private/Files/Node/Folder.php b/lib/private/Files/Node/Folder.php
index 16365948031..7453b553119 100644
--- a/lib/private/Files/Node/Folder.php
+++ b/lib/private/Files/Node/Folder.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@@ -460,4 +461,12 @@ class Folder extends Node implements \OCP\Files\Folder {
return $this->search($query);
}
+
+ public function verifyPath($fileName, $readonly = false): void {
+ $this->view->verifyPath(
+ $this->getPath(),
+ $fileName,
+ $readonly,
+ );
+ }
}
diff --git a/lib/private/Files/Node/LazyFolder.php b/lib/private/Files/Node/LazyFolder.php
index 5879748d951..37b1efa0fad 100644
--- a/lib/private/Files/Node/LazyFolder.php
+++ b/lib/private/Files/Node/LazyFolder.php
@@ -561,4 +561,8 @@ class LazyFolder implements Folder {
public function getMetadata(): array {
return $this->data['metadata'] ?? $this->__call(__FUNCTION__, func_get_args());
}
+
+ public function verifyPath($fileName, $readonly = false): void {
+ $this->__call(__FUNCTION__, func_get_args());
+ }
}
diff --git a/lib/private/Files/Notify/Change.php b/lib/private/Files/Notify/Change.php
index d2448e5deec..c8eccd11ae2 100644
--- a/lib/private/Files/Notify/Change.php
+++ b/lib/private/Files/Notify/Change.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Files/Notify/RenameChange.php b/lib/private/Files/Notify/RenameChange.php
index 98fd7099a58..28554ceaa26 100644
--- a/lib/private/Files/Notify/RenameChange.php
+++ b/lib/private/Files/Notify/RenameChange.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Files/ObjectStore/Azure.php b/lib/private/Files/ObjectStore/Azure.php
index 575cc336ba8..2729bb3c037 100644
--- a/lib/private/Files/ObjectStore/Azure.php
+++ b/lib/private/Files/ObjectStore/Azure.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Files/ObjectStore/ObjectStoreStorage.php b/lib/private/Files/ObjectStore/ObjectStoreStorage.php
index ebe87399ab4..10ee6aec167 100644
--- a/lib/private/Files/ObjectStore/ObjectStoreStorage.php
+++ b/lib/private/Files/ObjectStore/ObjectStoreStorage.php
@@ -67,7 +67,7 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFil
$this->logger = \OCP\Server::get(LoggerInterface::class);
}
- public function mkdir(string $path, bool $force = false): bool {
+ public function mkdir(string $path, bool $force = false, array $metadata = []): bool {
$path = $this->normalizePath($path);
if (!$force && $this->file_exists($path)) {
$this->logger->warning("Tried to create an object store folder that already exists: $path");
@@ -77,7 +77,7 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFil
$mTime = time();
$data = [
'mimetype' => 'httpd/unix-directory',
- 'size' => 0,
+ 'size' => $metadata['size'] ?? 0,
'mtime' => $mTime,
'storage_mtime' => $mTime,
'permissions' => \OCP\Constants::PERMISSION_ALL,
@@ -413,16 +413,6 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFil
//create a empty file, need to have at least on char to make it
// work with all object storage implementations
$this->file_put_contents($path, ' ');
- $mimeType = \OC::$server->getMimeTypeDetector()->detectPath($path);
- $stat = [
- 'etag' => $this->getETag($path),
- 'mimetype' => $mimeType,
- 'size' => 0,
- 'mtime' => $mtime,
- 'storage_mtime' => $mtime,
- 'permissions' => \OCP\Constants::PERMISSION_ALL - \OCP\Constants::PERMISSION_CREATE,
- ];
- $this->getCache()->put($path, $stat);
} catch (\Exception $ex) {
$this->logger->error(
'Could not create object for ' . $path,
@@ -607,8 +597,8 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFil
public function moveFromStorage(IStorage $sourceStorage, string $sourceInternalPath, string $targetInternalPath, ?ICacheEntry $sourceCacheEntry = null): bool {
$sourceCache = $sourceStorage->getCache();
if (
- $sourceStorage->instanceOfStorage(ObjectStoreStorage::class) &&
- $sourceStorage->getObjectStore()->getStorageId() === $this->getObjectStore()->getStorageId()
+ $sourceStorage->instanceOfStorage(ObjectStoreStorage::class)
+ && $sourceStorage->getObjectStore()->getStorageId() === $this->getObjectStore()->getStorageId()
) {
if ($this->getCache()->get($targetInternalPath)) {
$this->unlink($targetInternalPath);
@@ -709,7 +699,7 @@ class ObjectStoreStorage extends \OC\Files\Storage\Common implements IChunkedFil
if ($cache->inCache($to)) {
$cache->remove($to);
}
- $this->mkdir($to);
+ $this->mkdir($to, false, ['size' => $sourceEntry->getSize()]);
foreach ($sourceCache->getFolderContentsById($sourceEntry->getId()) as $child) {
$this->copyInner($sourceCache, $child, $to . '/' . $child->getName());
diff --git a/lib/private/Files/ObjectStore/PrimaryObjectStoreConfig.php b/lib/private/Files/ObjectStore/PrimaryObjectStoreConfig.php
new file mode 100644
index 00000000000..fdfe989addc
--- /dev/null
+++ b/lib/private/Files/ObjectStore/PrimaryObjectStoreConfig.php
@@ -0,0 +1,140 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+namespace OC\Files\ObjectStore;
+
+use OCP\App\IAppManager;
+use OCP\Files\ObjectStore\IObjectStore;
+use OCP\IConfig;
+use OCP\IUser;
+
+/**
+ * @psalm-type ObjectStoreConfig array{class: class-string<IObjectStore>, arguments: array{multibucket: bool, ...}}
+ */
+class PrimaryObjectStoreConfig {
+ public function __construct(
+ private readonly IConfig $config,
+ private readonly IAppManager $appManager,
+ ) {
+ }
+
+ /**
+ * @param ObjectStoreConfig $config
+ */
+ public function buildObjectStore(array $config): IObjectStore {
+ return new $config['class']($config['arguments']);
+ }
+
+ /**
+ * @return ?ObjectStoreConfig
+ */
+ public function getObjectStoreConfigForRoot(): ?array {
+ $config = $this->getObjectStoreConfig();
+
+ if ($config && $config['arguments']['multibucket']) {
+ if (!isset($config['arguments']['bucket'])) {
+ $config['arguments']['bucket'] = '';
+ }
+
+ // put the root FS always in first bucket for multibucket configuration
+ $config['arguments']['bucket'] .= '0';
+ }
+ return $config;
+ }
+
+ /**
+ * @return ?ObjectStoreConfig
+ */
+ public function getObjectStoreConfigForUser(IUser $user): ?array {
+ $config = $this->getObjectStoreConfig();
+
+ if ($config && $config['arguments']['multibucket']) {
+ $config['arguments']['bucket'] = $this->getBucketForUser($user, $config);
+ }
+ return $config;
+ }
+
+ /**
+ * @return ?ObjectStoreConfig
+ */
+ private function getObjectStoreConfig(): ?array {
+ $objectStore = $this->config->getSystemValue('objectstore', null);
+ $objectStoreMultiBucket = $this->config->getSystemValue('objectstore_multibucket', null);
+
+ // new-style multibucket config uses the same 'objectstore' key but sets `'multibucket' => true`, transparently upgrade older style config
+ if ($objectStoreMultiBucket) {
+ $objectStoreMultiBucket['arguments']['multibucket'] = true;
+ return $this->validateObjectStoreConfig($objectStoreMultiBucket);
+ } elseif ($objectStore) {
+ return $this->validateObjectStoreConfig($objectStore);
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * @return ObjectStoreConfig
+ */
+ private function validateObjectStoreConfig(array $config) {
+ if (!isset($config['class'])) {
+ throw new \Exception('No class configured for object store');
+ }
+ if (!isset($config['arguments'])) {
+ $config['arguments'] = [];
+ }
+ $class = $config['class'];
+ $arguments = $config['arguments'];
+ if (!is_array($arguments)) {
+ throw new \Exception('Configured object store arguments are not an array');
+ }
+ if (!isset($arguments['multibucket'])) {
+ $arguments['multibucket'] = false;
+ }
+ if (!is_bool($arguments['multibucket'])) {
+ throw new \Exception('arguments.multibucket must be a boolean in object store configuration');
+ }
+
+ if (!is_string($class)) {
+ throw new \Exception('Configured class for object store is not a string');
+ }
+
+ if (str_starts_with($class, 'OCA\\') && substr_count($class, '\\') >= 2) {
+ [$appId] = explode('\\', $class);
+ $this->appManager->loadApp(strtolower($appId));
+ }
+
+ if (!is_a($class, IObjectStore::class, true)) {
+ throw new \Exception('Configured class for object store is not an object store');
+ }
+ return [
+ 'class' => $class,
+ 'arguments' => $arguments,
+ ];
+ }
+
+ private function getBucketForUser(IUser $user, array $config): string {
+ $bucket = $this->config->getUserValue($user->getUID(), 'homeobjectstore', 'bucket', null);
+
+ if ($bucket === null) {
+ /*
+ * Use any provided bucket argument as prefix
+ * and add the mapping from username => bucket
+ */
+ if (!isset($config['arguments']['bucket'])) {
+ $config['arguments']['bucket'] = '';
+ }
+ $mapper = new Mapper($user, $this->config);
+ $numBuckets = isset($config['arguments']['num_buckets']) ? $config['arguments']['num_buckets'] : 64;
+ $bucket = $config['arguments']['bucket'] . $mapper->getBucket($numBuckets);
+
+ $this->config->setUserValue($user->getUID(), 'homeobjectstore', 'bucket', $bucket);
+ }
+
+ return $bucket;
+ }
+}
diff --git a/lib/private/Files/ObjectStore/S3.php b/lib/private/Files/ObjectStore/S3.php
index 23c061db174..72e1751e23d 100644
--- a/lib/private/Files/ObjectStore/S3.php
+++ b/lib/private/Files/ObjectStore/S3.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Files/ObjectStore/S3ConfigTrait.php b/lib/private/Files/ObjectStore/S3ConfigTrait.php
index 63f14ac2d00..5b086db8f77 100644
--- a/lib/private/Files/ObjectStore/S3ConfigTrait.php
+++ b/lib/private/Files/ObjectStore/S3ConfigTrait.php
@@ -18,6 +18,10 @@ trait S3ConfigTrait {
/** Maximum number of concurrent multipart uploads */
protected int $concurrency;
+ /** Timeout, in seconds, for the connection to S3 server, not for the
+ * request. */
+ protected float $connectTimeout;
+
protected int $timeout;
protected string|false $proxy;
diff --git a/lib/private/Files/ObjectStore/S3ConnectionTrait.php b/lib/private/Files/ObjectStore/S3ConnectionTrait.php
index b7017583dc2..67b82a44ab7 100644
--- a/lib/private/Files/ObjectStore/S3ConnectionTrait.php
+++ b/lib/private/Files/ObjectStore/S3ConnectionTrait.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -39,6 +40,7 @@ trait S3ConnectionTrait {
// Default to 5 like the S3 SDK does
$this->concurrency = $params['concurrency'] ?? 5;
$this->proxy = $params['proxy'] ?? false;
+ $this->connectTimeout = $params['connect_timeout'] ?? 5;
$this->timeout = $params['timeout'] ?? 15;
$this->storageClass = !empty($params['storageClass']) ? $params['storageClass'] : 'STANDARD';
$this->uploadPartSize = $params['uploadPartSize'] ?? 524288000;
@@ -102,8 +104,7 @@ trait S3ConnectionTrait {
'use_arn_region' => false,
'http' => [
'verify' => $this->getCertificateBundlePath(),
- // Timeout for the connection to S3 server, not for the request.
- 'connect_timeout' => 5
+ 'connect_timeout' => $this->connectTimeout,
],
'use_aws_shared_config_files' => false,
'retries' => [
diff --git a/lib/private/Files/ObjectStore/S3ObjectTrait.php b/lib/private/Files/ObjectStore/S3ObjectTrait.php
index 61e8158b863..5e6dcf88a42 100644
--- a/lib/private/Files/ObjectStore/S3ObjectTrait.php
+++ b/lib/private/Files/ObjectStore/S3ObjectTrait.php
@@ -119,28 +119,54 @@ trait S3ObjectTrait {
protected function writeMultiPart(string $urn, StreamInterface $stream, array $metaData): void {
$mimetype = $metaData['mimetype'] ?? null;
unset($metaData['mimetype']);
- $uploader = new MultipartUploader($this->getConnection(), $stream, [
- 'bucket' => $this->bucket,
- 'concurrency' => $this->concurrency,
- 'key' => $urn,
- 'part_size' => $this->uploadPartSize,
- 'params' => [
- 'ContentType' => $mimetype,
- 'Metadata' => $this->buildS3Metadata($metaData),
- 'StorageClass' => $this->storageClass,
- ] + $this->getSSECParameters(),
- ]);
- try {
- $uploader->upload();
- } catch (S3MultipartUploadException $e) {
+ $attempts = 0;
+ $uploaded = false;
+ $concurrency = $this->concurrency;
+ $exception = null;
+ $state = null;
+
+ // retry multipart upload once with concurrency at half on failure
+ while (!$uploaded && $attempts <= 1) {
+ $uploader = new MultipartUploader($this->getConnection(), $stream, [
+ 'bucket' => $this->bucket,
+ 'concurrency' => $concurrency,
+ 'key' => $urn,
+ 'part_size' => $this->uploadPartSize,
+ 'state' => $state,
+ 'params' => [
+ 'ContentType' => $mimetype,
+ 'Metadata' => $this->buildS3Metadata($metaData),
+ 'StorageClass' => $this->storageClass,
+ ] + $this->getSSECParameters(),
+ ]);
+
+ try {
+ $uploader->upload();
+ $uploaded = true;
+ } catch (S3MultipartUploadException $e) {
+ $exception = $e;
+ $attempts++;
+
+ if ($concurrency > 1) {
+ $concurrency = round($concurrency / 2);
+ }
+
+ if ($stream->isSeekable()) {
+ $stream->rewind();
+ }
+ }
+ }
+
+ if (!$uploaded) {
// if anything goes wrong with multipart, make sure that you don´t poison and
// slow down s3 bucket with orphaned fragments
- $uploadInfo = $e->getState()->getId();
- if ($e->getState()->isInitiated() && (array_key_exists('UploadId', $uploadInfo))) {
+ $uploadInfo = $exception->getState()->getId();
+ if ($exception->getState()->isInitiated() && (array_key_exists('UploadId', $uploadInfo))) {
$this->getConnection()->abortMultipartUpload($uploadInfo);
}
- throw new \OCA\DAV\Connector\Sabre\Exception\BadGateway('Error while uploading to S3 bucket', 0, $e);
+
+ throw new \OCA\DAV\Connector\Sabre\Exception\BadGateway('Error while uploading to S3 bucket', 0, $exception);
}
}
diff --git a/lib/private/Files/ObjectStore/S3Signature.php b/lib/private/Files/ObjectStore/S3Signature.php
index 994d65cc219..b80382ff67d 100644
--- a/lib/private/Files/ObjectStore/S3Signature.php
+++ b/lib/private/Files/ObjectStore/S3Signature.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Files/ObjectStore/StorageObjectStore.php b/lib/private/Files/ObjectStore/StorageObjectStore.php
index 4361795ec45..888602a62e4 100644
--- a/lib/private/Files/ObjectStore/StorageObjectStore.php
+++ b/lib/private/Files/ObjectStore/StorageObjectStore.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Files/Search/QueryOptimizer/FlattenNestedBool.php b/lib/private/Files/Search/QueryOptimizer/FlattenNestedBool.php
index 2bde6f0bbdb..bb7bef2ed63 100644
--- a/lib/private/Files/Search/QueryOptimizer/FlattenNestedBool.php
+++ b/lib/private/Files/Search/QueryOptimizer/FlattenNestedBool.php
@@ -14,8 +14,8 @@ class FlattenNestedBool extends QueryOptimizerStep {
public function processOperator(ISearchOperator &$operator) {
if (
$operator instanceof SearchBinaryOperator && (
- $operator->getType() === ISearchBinaryOperator::OPERATOR_OR ||
- $operator->getType() === ISearchBinaryOperator::OPERATOR_AND
+ $operator->getType() === ISearchBinaryOperator::OPERATOR_OR
+ || $operator->getType() === ISearchBinaryOperator::OPERATOR_AND
)
) {
$newArguments = [];
diff --git a/lib/private/Files/Search/QueryOptimizer/FlattenSingleArgumentBinaryOperation.php b/lib/private/Files/Search/QueryOptimizer/FlattenSingleArgumentBinaryOperation.php
index cb04351534a..7e99c04f197 100644
--- a/lib/private/Files/Search/QueryOptimizer/FlattenSingleArgumentBinaryOperation.php
+++ b/lib/private/Files/Search/QueryOptimizer/FlattenSingleArgumentBinaryOperation.php
@@ -16,11 +16,11 @@ class FlattenSingleArgumentBinaryOperation extends ReplacingOptimizerStep {
public function processOperator(ISearchOperator &$operator): bool {
parent::processOperator($operator);
if (
- $operator instanceof ISearchBinaryOperator &&
- count($operator->getArguments()) === 1 &&
- (
- $operator->getType() === ISearchBinaryOperator::OPERATOR_OR ||
- $operator->getType() === ISearchBinaryOperator::OPERATOR_AND
+ $operator instanceof ISearchBinaryOperator
+ && count($operator->getArguments()) === 1
+ && (
+ $operator->getType() === ISearchBinaryOperator::OPERATOR_OR
+ || $operator->getType() === ISearchBinaryOperator::OPERATOR_AND
)
) {
$operator = $operator->getArguments()[0];
diff --git a/lib/private/Files/Search/QueryOptimizer/OrEqualsToIn.php b/lib/private/Files/Search/QueryOptimizer/OrEqualsToIn.php
index 42d960cc22a..6df35c9c9a2 100644
--- a/lib/private/Files/Search/QueryOptimizer/OrEqualsToIn.php
+++ b/lib/private/Files/Search/QueryOptimizer/OrEqualsToIn.php
@@ -18,8 +18,8 @@ use OCP\Files\Search\ISearchOperator;
class OrEqualsToIn extends ReplacingOptimizerStep {
public function processOperator(ISearchOperator &$operator): bool {
if (
- $operator instanceof ISearchBinaryOperator &&
- $operator->getType() === ISearchBinaryOperator::OPERATOR_OR
+ $operator instanceof ISearchBinaryOperator
+ && $operator->getType() === ISearchBinaryOperator::OPERATOR_OR
) {
$groups = $this->groupEqualsComparisonsByField($operator->getArguments());
$newParts = array_map(function (array $group) {
diff --git a/lib/private/Files/Search/QueryOptimizer/PathPrefixOptimizer.php b/lib/private/Files/Search/QueryOptimizer/PathPrefixOptimizer.php
index 9d50746d5d1..2994a9365a7 100644
--- a/lib/private/Files/Search/QueryOptimizer/PathPrefixOptimizer.php
+++ b/lib/private/Files/Search/QueryOptimizer/PathPrefixOptimizer.php
@@ -52,9 +52,9 @@ class PathPrefixOptimizer extends QueryOptimizerStep {
private function operatorPairIsPathPrefix(ISearchOperator $like, ISearchOperator $equal): bool {
return (
- $like instanceof ISearchComparison && $equal instanceof ISearchComparison &&
- !$like->getExtra() && !$equal->getExtra() && $like->getField() === 'path' && $equal->getField() === 'path' &&
- $like->getType() === ISearchComparison::COMPARE_LIKE_CASE_SENSITIVE && $equal->getType() === ISearchComparison::COMPARE_EQUAL
+ $like instanceof ISearchComparison && $equal instanceof ISearchComparison
+ && !$like->getExtra() && !$equal->getExtra() && $like->getField() === 'path' && $equal->getField() === 'path'
+ && $like->getType() === ISearchComparison::COMPARE_LIKE_CASE_SENSITIVE && $equal->getType() === ISearchComparison::COMPARE_EQUAL
&& $like->getValue() === SearchComparison::escapeLikeParameter($equal->getValue()) . '/%'
);
}
diff --git a/lib/private/Files/Search/QueryOptimizer/SplitLargeIn.php b/lib/private/Files/Search/QueryOptimizer/SplitLargeIn.php
index 3e5258a715f..8aee1975708 100644
--- a/lib/private/Files/Search/QueryOptimizer/SplitLargeIn.php
+++ b/lib/private/Files/Search/QueryOptimizer/SplitLargeIn.php
@@ -18,9 +18,9 @@ use OCP\Files\Search\ISearchOperator;
class SplitLargeIn extends ReplacingOptimizerStep {
public function processOperator(ISearchOperator &$operator): bool {
if (
- $operator instanceof ISearchComparison &&
- $operator->getType() === ISearchComparison::COMPARE_IN &&
- count($operator->getValue()) > 1000
+ $operator instanceof ISearchComparison
+ && $operator->getType() === ISearchComparison::COMPARE_IN
+ && count($operator->getValue()) > 1000
) {
$chunks = array_chunk($operator->getValue(), 1000);
$chunkComparisons = array_map(function (array $values) use ($operator) {
diff --git a/lib/private/Files/Search/SearchBinaryOperator.php b/lib/private/Files/Search/SearchBinaryOperator.php
index 59a1ba2dfaf..49f599933f4 100644
--- a/lib/private/Files/Search/SearchBinaryOperator.php
+++ b/lib/private/Files/Search/SearchBinaryOperator.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Files/Search/SearchOrder.php b/lib/private/Files/Search/SearchOrder.php
index 3dcbc02bc1a..5a036653f4e 100644
--- a/lib/private/Files/Search/SearchOrder.php
+++ b/lib/private/Files/Search/SearchOrder.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Files/Search/SearchQuery.php b/lib/private/Files/Search/SearchQuery.php
index 1793e42d349..592749cf4a0 100644
--- a/lib/private/Files/Search/SearchQuery.php
+++ b/lib/private/Files/Search/SearchQuery.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Files/SetupManager.php b/lib/private/Files/SetupManager.php
index 2b8121a529d..d6260c38fb0 100644
--- a/lib/private/Files/SetupManager.php
+++ b/lib/private/Files/SetupManager.php
@@ -23,7 +23,6 @@ use OC\Share\Share;
use OC\Share20\ShareDisableChecker;
use OC_App;
use OC_Hook;
-use OC_Util;
use OCA\Files_External\Config\ExternalMountPoint;
use OCA\Files_Sharing\External\Mount;
use OCA\Files_Sharing\ISharedMountPoint;
@@ -34,6 +33,7 @@ use OCP\EventDispatcher\IEventDispatcher;
use OCP\Files\Config\ICachedMountInfo;
use OCP\Files\Config\IHomeMountProvider;
use OCP\Files\Config\IMountProvider;
+use OCP\Files\Config\IRootMountProvider;
use OCP\Files\Config\IUserMountCache;
use OCP\Files\Events\BeforeFileSystemSetupEvent;
use OCP\Files\Events\InvalidateMountCacheEvent;
@@ -156,7 +156,7 @@ class SetupManager {
if ($mount instanceof HomeMountPoint) {
$user = $mount->getUser();
return new Quota(['storage' => $storage, 'quotaCallback' => function () use ($user) {
- return OC_Util::getUserQuota($user);
+ return $user->getQuotaBytes();
}, 'root' => 'files', 'include_external_storage' => $quotaIncludeExternal]);
}
@@ -171,9 +171,9 @@ class SetupManager {
return new PermissionsMask([
'storage' => $storage,
'mask' => Constants::PERMISSION_ALL & ~(
- Constants::PERMISSION_UPDATE |
- Constants::PERMISSION_CREATE |
- Constants::PERMISSION_DELETE
+ Constants::PERMISSION_UPDATE
+ | Constants::PERMISSION_CREATE
+ | Constants::PERMISSION_DELETE
),
]);
}
@@ -280,9 +280,13 @@ class SetupManager {
$mounts = array_filter($mounts, function (IMountPoint $mount) use ($userRoot) {
return str_starts_with($mount->getMountPoint(), $userRoot);
});
- $allProviders = array_map(function (IMountProvider $provider) {
+ $allProviders = array_map(function (IMountProvider|IHomeMountProvider|IRootMountProvider $provider) {
return get_class($provider);
- }, $this->mountProviderCollection->getProviders());
+ }, array_merge(
+ $this->mountProviderCollection->getProviders(),
+ $this->mountProviderCollection->getHomeProviders(),
+ $this->mountProviderCollection->getRootProviders(),
+ ));
$newProviders = array_diff($allProviders, $previouslySetupProviders);
$mounts = array_filter($mounts, function (IMountPoint $mount) use ($previouslySetupProviders) {
return !in_array($mount->getMountProvider(), $previouslySetupProviders);
diff --git a/lib/private/Files/SimpleFS/SimpleFile.php b/lib/private/Files/SimpleFS/SimpleFile.php
index 0bfaea21788..d9c1b47d2f1 100644
--- a/lib/private/Files/SimpleFS/SimpleFile.php
+++ b/lib/private/Files/SimpleFS/SimpleFile.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Files/SimpleFS/SimpleFolder.php b/lib/private/Files/SimpleFS/SimpleFolder.php
index 0da552e402b..62f3db25e9b 100644
--- a/lib/private/Files/SimpleFS/SimpleFolder.php
+++ b/lib/private/Files/SimpleFS/SimpleFolder.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Files/Storage/Common.php b/lib/private/Files/Storage/Common.php
index b1e02cf2e6c..2dc359169d7 100644
--- a/lib/private/Files/Storage/Common.php
+++ b/lib/private/Files/Storage/Common.php
@@ -550,8 +550,8 @@ abstract class Common implements Storage, ILockingStorage, IWriteStreamStorage,
public function moveFromStorage(IStorage $sourceStorage, string $sourceInternalPath, string $targetInternalPath): bool {
if (
- !$sourceStorage->instanceOfStorage(Encryption::class) &&
- $this->isSameStorage($sourceStorage)
+ !$sourceStorage->instanceOfStorage(Encryption::class)
+ && $this->isSameStorage($sourceStorage)
) {
// resolve any jailed paths
while ($sourceStorage->instanceOfStorage(Jail::class)) {
diff --git a/lib/private/Files/Storage/DAV.php b/lib/private/Files/Storage/DAV.php
index 10670d6331a..afd8f87e2de 100644
--- a/lib/private/Files/Storage/DAV.php
+++ b/lib/private/Files/Storage/DAV.php
@@ -350,7 +350,13 @@ class DAV extends Common {
}
}
- return $response->getBody();
+ $content = $response->getBody();
+
+ if ($content === null || is_string($content)) {
+ return false;
+ }
+
+ return $content;
case 'w':
case 'wb':
case 'a':
@@ -390,6 +396,8 @@ class DAV extends Common {
$this->writeBack($tmpFile, $path);
});
}
+
+ return false;
}
public function writeBack(string $tmpFile, string $path): void {
diff --git a/lib/private/Files/Storage/Wrapper/Encryption.php b/lib/private/Files/Storage/Wrapper/Encryption.php
index 4d38d2d37aa..51a5f99908d 100644
--- a/lib/private/Files/Storage/Wrapper/Encryption.php
+++ b/lib/private/Files/Storage/Wrapper/Encryption.php
@@ -185,11 +185,11 @@ class Encryption extends Wrapper {
public function rename(string $source, string $target): bool {
$result = $this->storage->rename($source, $target);
- if ($result &&
+ if ($result
// versions always use the keys from the original file, so we can skip
// this step for versions
- $this->isVersion($target) === false &&
- $this->encryptionManager->isEnabled()) {
+ && $this->isVersion($target) === false
+ && $this->encryptionManager->isEnabled()) {
$sourcePath = $this->getFullPath($source);
if (!$this->util->isExcluded($sourcePath)) {
$targetPath = $this->getFullPath($target);
@@ -210,9 +210,9 @@ class Encryption extends Wrapper {
public function rmdir(string $path): bool {
$result = $this->storage->rmdir($path);
$fullPath = $this->getFullPath($path);
- if ($result &&
- $this->util->isExcluded($fullPath) === false &&
- $this->encryptionManager->isEnabled()
+ if ($result
+ && $this->util->isExcluded($fullPath) === false
+ && $this->encryptionManager->isEnabled()
) {
$this->keyStorage->deleteAllFileKeys($fullPath);
}
@@ -225,9 +225,9 @@ class Encryption extends Wrapper {
$metaData = $this->getMetaData($path);
if (
- !$this->is_dir($path) &&
- isset($metaData['encrypted']) &&
- $metaData['encrypted'] === true
+ !$this->is_dir($path)
+ && isset($metaData['encrypted'])
+ && $metaData['encrypted'] === true
) {
$fullPath = $this->getFullPath($path);
$module = $this->getEncryptionModule($path);
@@ -384,9 +384,9 @@ class Encryption extends Wrapper {
$size = $this->storage->filesize($path);
$result = $unencryptedSize;
- if ($unencryptedSize < 0 ||
- ($size > 0 && $unencryptedSize === $size) ||
- $unencryptedSize > $size
+ if ($unencryptedSize < 0
+ || ($size > 0 && $unencryptedSize === $size)
+ || $unencryptedSize > $size
) {
// check if we already calculate the unencrypted size for the
// given path to avoid recursions
@@ -634,8 +634,8 @@ class Encryption extends Wrapper {
): bool {
// for versions we have nothing to do, because versions should always use the
// key from the original file. Just create a 1:1 copy and done
- if ($this->isVersion($targetInternalPath) ||
- $this->isVersion($sourceInternalPath)) {
+ if ($this->isVersion($targetInternalPath)
+ || $this->isVersion($sourceInternalPath)) {
// remember that we try to create a version so that we can detect it during
// fopen($sourceInternalPath) and by-pass the encryption in order to
// create a 1:1 copy of the file
diff --git a/lib/private/Files/Stream/Encryption.php b/lib/private/Files/Stream/Encryption.php
index 0d55385820c..ef147ec421f 100644
--- a/lib/private/Files/Stream/Encryption.php
+++ b/lib/private/Files/Stream/Encryption.php
@@ -312,8 +312,8 @@ class Encryption extends Wrapper {
// for seekable streams the pointer is moved back to the beginning of the encrypted block
// flush will start writing there when the position moves to another block
- $positionInFile = (int)floor($this->position / $this->unencryptedBlockSize) *
- $this->util->getBlockSize() + $this->headerSize;
+ $positionInFile = (int)floor($this->position / $this->unencryptedBlockSize)
+ * $this->util->getBlockSize() + $this->headerSize;
$resultFseek = $this->parentStreamSeek($positionInFile);
// only allow writes on seekable streams, or at the end of the encrypted stream
@@ -336,8 +336,8 @@ class Encryption extends Wrapper {
// if $data doesn't fit the current block, the fill the current block and reiterate
// after the block is filled, it is flushed and $data is updatedxxx
} else {
- $this->cache = substr($this->cache, 0, $blockPosition) .
- substr($data, 0, $this->unencryptedBlockSize - $blockPosition);
+ $this->cache = substr($this->cache, 0, $blockPosition)
+ . substr($data, 0, $this->unencryptedBlockSize - $blockPosition);
$this->flush();
$this->position += ($this->unencryptedBlockSize - $blockPosition);
$length += ($this->unencryptedBlockSize - $blockPosition);
diff --git a/lib/private/Files/Stream/SeekableHttpStream.php b/lib/private/Files/Stream/SeekableHttpStream.php
index 5ed04ed9066..6ce0a880e8d 100644
--- a/lib/private/Files/Stream/SeekableHttpStream.php
+++ b/lib/private/Files/Stream/SeekableHttpStream.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Files/View.php b/lib/private/Files/View.php
index 6832b4e1551..63eecf5e1d6 100644
--- a/lib/private/Files/View.php
+++ b/lib/private/Files/View.php
@@ -938,7 +938,7 @@ class View {
try {
$exists = $this->file_exists($target);
- if ($this->shouldEmitHooks()) {
+ if ($this->shouldEmitHooks($target)) {
\OC_Hook::emit(
Filesystem::CLASSNAME,
Filesystem::signal_copy,
@@ -978,7 +978,7 @@ class View {
$this->changeLock($target, ILockingProvider::LOCK_SHARED);
$lockTypePath2 = ILockingProvider::LOCK_SHARED;
- if ($this->shouldEmitHooks() && $result !== false) {
+ if ($this->shouldEmitHooks($target) && $result !== false) {
\OC_Hook::emit(
Filesystem::CLASSNAME,
Filesystem::signal_post_copy,
diff --git a/lib/private/FullTextSearch/FullTextSearchManager.php b/lib/private/FullTextSearch/FullTextSearchManager.php
index 3ef8547ad3f..989da8d6bae 100644
--- a/lib/private/FullTextSearch/FullTextSearchManager.php
+++ b/lib/private/FullTextSearch/FullTextSearchManager.php
@@ -53,9 +53,9 @@ class FullTextSearchManager implements IFullTextSearchManager {
* @since 16.0.0
*/
public function isAvailable(): bool {
- if ($this->indexService === null ||
- $this->providerService === null ||
- $this->searchService === null) {
+ if ($this->indexService === null
+ || $this->providerService === null
+ || $this->searchService === null) {
return false;
}
diff --git a/lib/private/FullTextSearch/Model/IndexDocument.php b/lib/private/FullTextSearch/Model/IndexDocument.php
index 8bd20bad1e0..a51447393ed 100644
--- a/lib/private/FullTextSearch/Model/IndexDocument.php
+++ b/lib/private/FullTextSearch/Model/IndexDocument.php
@@ -512,8 +512,8 @@ class IndexDocument implements IIndexDocument, JsonSerializable {
* @since 16.0.0
*/
final public function addExcerpt(string $source, string $excerpt): IIndexDocument {
- $this->excerpts[] =
- [
+ $this->excerpts[]
+ = [
'source' => $source,
'excerpt' => $this->cleanExcerpt($excerpt)
];
diff --git a/lib/private/GlobalScale/Config.php b/lib/private/GlobalScale/Config.php
index 02acc1ab50a..2f729c2702e 100644
--- a/lib/private/GlobalScale/Config.php
+++ b/lib/private/GlobalScale/Config.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Group/Group.php b/lib/private/Group/Group.php
index 147c5baf543..6e42fad8b9f 100644
--- a/lib/private/Group/Group.php
+++ b/lib/private/Group/Group.php
@@ -377,7 +377,7 @@ class Group implements IGroup {
*/
public function hideFromCollaboration(): bool {
return array_reduce($this->backends, function (bool $hide, GroupInterface $backend) {
- return $hide | ($backend instanceof IHideFromCollaborationBackend && $backend->hideGroup($this->gid));
+ return $hide || ($backend instanceof IHideFromCollaborationBackend && $backend->hideGroup($this->gid));
}, false);
}
}
diff --git a/lib/private/Hooks/BasicEmitter.php b/lib/private/Hooks/BasicEmitter.php
index c9444b40473..091334b71c8 100644
--- a/lib/private/Hooks/BasicEmitter.php
+++ b/lib/private/Hooks/BasicEmitter.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/lib/private/Hooks/Emitter.php b/lib/private/Hooks/Emitter.php
index 8a63ac9ed3a..86eb410860c 100644
--- a/lib/private/Hooks/Emitter.php
+++ b/lib/private/Hooks/Emitter.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/lib/private/Hooks/EmitterTrait.php b/lib/private/Hooks/EmitterTrait.php
index 8bffb6f7c3b..7b2ec4ad7fe 100644
--- a/lib/private/Hooks/EmitterTrait.php
+++ b/lib/private/Hooks/EmitterTrait.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/lib/private/Hooks/PublicEmitter.php b/lib/private/Hooks/PublicEmitter.php
index 042b616e264..77cb2bf30dd 100644
--- a/lib/private/Hooks/PublicEmitter.php
+++ b/lib/private/Hooks/PublicEmitter.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/lib/private/Http/Client/Client.php b/lib/private/Http/Client/Client.php
index c3f8f589827..553a8921a80 100644
--- a/lib/private/Http/Client/Client.php
+++ b/lib/private/Http/Client/Client.php
@@ -149,8 +149,8 @@ class Client implements IClient {
}
private function isLocalAddressAllowed(array $options) : bool {
- if (($options['nextcloud']['allow_local_address'] ?? false) ||
- $this->config->getSystemValueBool('allow_local_remote_servers', false)) {
+ if (($options['nextcloud']['allow_local_address'] ?? false)
+ || $this->config->getSystemValueBool('allow_local_remote_servers', false)) {
return true;
}
diff --git a/lib/private/Http/Client/Response.php b/lib/private/Http/Client/Response.php
index adf83306d07..1e4cb3b8fa2 100644
--- a/lib/private/Http/Client/Response.php
+++ b/lib/private/Http/Client/Response.php
@@ -11,49 +11,25 @@ namespace OC\Http\Client;
use OCP\Http\Client\IResponse;
use Psr\Http\Message\ResponseInterface;
-/**
- * Class Response
- *
- * @package OC\Http
- */
class Response implements IResponse {
- /** @var ResponseInterface */
- private $response;
-
- /**
- * @var bool
- */
- private $stream;
+ private ResponseInterface $response;
+ private bool $stream;
- /**
- * @param ResponseInterface $response
- * @param bool $stream
- */
- public function __construct(ResponseInterface $response, $stream = false) {
+ public function __construct(ResponseInterface $response, bool $stream = false) {
$this->response = $response;
$this->stream = $stream;
}
- /**
- * @return string|resource
- */
public function getBody() {
- return $this->stream ?
- $this->response->getBody()->detach():
- $this->response->getBody()->getContents();
+ return $this->stream
+ ? $this->response->getBody()->detach()
+ :$this->response->getBody()->getContents();
}
- /**
- * @return int
- */
public function getStatusCode(): int {
return $this->response->getStatusCode();
}
- /**
- * @param string $key
- * @return string
- */
public function getHeader(string $key): string {
$headers = $this->response->getHeader($key);
@@ -64,9 +40,6 @@ class Response implements IResponse {
return $headers[0];
}
- /**
- * @return array
- */
public function getHeaders(): array {
return $this->response->getHeaders();
}
diff --git a/lib/private/Image.php b/lib/private/Image.php
index 96699a0046b..3b8cb79c853 100644
--- a/lib/private/Image.php
+++ b/lib/private/Image.php
@@ -699,11 +699,11 @@ class Image implements IImage {
fclose($fp);
unset($fp);
- $headerFormat = 'A4Riff/' . // get n string
- 'I1Filesize/' . // get integer (file size but not actual size)
- 'A4Webp/' . // get n string
- 'A4Vp/' . // get n string
- 'A74Chunk';
+ $headerFormat = 'A4Riff/' // get n string
+ . 'I1Filesize/' // get integer (file size but not actual size)
+ . 'A4Webp/' // get n string
+ . 'A4Vp/' // get n string
+ . 'A74Chunk';
$header = unpack($headerFormat, $data);
unset($data, $headerFormat);
diff --git a/lib/private/Installer.php b/lib/private/Installer.php
index f32b0e5919a..3bbef3252f4 100644
--- a/lib/private/Installer.php
+++ b/lib/private/Installer.php
@@ -10,6 +10,7 @@ declare(strict_types=1);
namespace OC;
use Doctrine\DBAL\Exception\TableExistsException;
+use OC\App\AppStore\AppNotFoundException;
use OC\App\AppStore\Bundles\Bundle;
use OC\App\AppStore\Fetcher\AppFetcher;
use OC\AppFramework\Bootstrap\Coordinator;
@@ -174,6 +175,7 @@ class Installer {
* @param string $appId
* @param bool [$allowUnstable]
*
+ * @throws AppNotFoundException If the app is not found on the appstore
* @throws \Exception If the installation was not successful
*/
public function downloadApp(string $appId, bool $allowUnstable = false): void {
@@ -341,6 +343,9 @@ class Installer {
// otherwise we just copy the outer directory
$this->copyRecursive($extractDir, $baseDir);
Files::rmdirr($extractDir);
+ if (function_exists('opcache_reset')) {
+ opcache_reset();
+ }
return;
}
// Signature does not match
@@ -353,9 +358,9 @@ class Installer {
}
}
- throw new \Exception(
+ throw new AppNotFoundException(
sprintf(
- 'Could not download app %s',
+ 'Could not download app %s, it was not found on the appstore',
$appId
)
);
diff --git a/lib/private/L10N/Factory.php b/lib/private/L10N/Factory.php
index 5645693f8d9..6a747744829 100644
--- a/lib/private/L10N/Factory.php
+++ b/lib/private/L10N/Factory.php
@@ -437,8 +437,8 @@ class Factory implements IFactory {
}
// Use language from request
- if ($this->userSession->getUser() instanceof IUser &&
- $user->getUID() === $this->userSession->getUser()->getUID()) {
+ if ($this->userSession->getUser() instanceof IUser
+ && $user->getUID() === $this->userSession->getUser()->getUID()) {
try {
return $this->getLanguageFromRequest();
} catch (LanguageNotFoundException $e) {
@@ -517,10 +517,10 @@ class Factory implements IFactory {
// use formal version of german ("Sie" instead of "Du") if the default
// language is set to 'de_DE' if possible
if (
- is_string($defaultLanguage) &&
- strtolower($lang) === 'de' &&
- strtolower($defaultLanguage) === 'de_de' &&
- $this->languageExists($app, 'de_DE')
+ is_string($defaultLanguage)
+ && strtolower($lang) === 'de'
+ && strtolower($defaultLanguage) === 'de_de'
+ && $this->languageExists($app, 'de_DE')
) {
$result = 'de_DE';
}
diff --git a/lib/private/L10N/LanguageNotFoundException.php b/lib/private/L10N/LanguageNotFoundException.php
index 31b08b2ec14..087a384e00e 100644
--- a/lib/private/L10N/LanguageNotFoundException.php
+++ b/lib/private/L10N/LanguageNotFoundException.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/LargeFileHelper.php b/lib/private/LargeFileHelper.php
index 238fb0790b8..fa4c72da756 100755
--- a/lib/private/LargeFileHelper.php
+++ b/lib/private/LargeFileHelper.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/lib/private/Lockdown/Filesystem/NullCache.php b/lib/private/Lockdown/Filesystem/NullCache.php
index e84ff40e00c..5a27c5d5c6e 100644
--- a/lib/private/Lockdown/Filesystem/NullCache.php
+++ b/lib/private/Lockdown/Filesystem/NullCache.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -21,20 +22,23 @@ class NullCache implements ICache {
}
public function get($file) {
- return $file !== '' ? null :
- new CacheEntry([
- 'fileid' => -1,
- 'parent' => -1,
- 'name' => '',
- 'path' => '',
- 'size' => '0',
- 'mtime' => time(),
- 'storage_mtime' => time(),
- 'etag' => '',
- 'mimetype' => FileInfo::MIMETYPE_FOLDER,
- 'mimepart' => 'httpd',
- 'permissions' => Constants::PERMISSION_READ
- ]);
+ if ($file !== '') {
+ return false;
+ }
+
+ return new CacheEntry([
+ 'fileid' => -1,
+ 'parent' => -1,
+ 'name' => '',
+ 'path' => '',
+ 'size' => '0',
+ 'mtime' => time(),
+ 'storage_mtime' => time(),
+ 'etag' => '',
+ 'mimetype' => FileInfo::MIMETYPE_FOLDER,
+ 'mimepart' => 'httpd',
+ 'permissions' => Constants::PERMISSION_READ
+ ]);
}
public function getFolderContents($folder) {
diff --git a/lib/private/Lockdown/Filesystem/NullStorage.php b/lib/private/Lockdown/Filesystem/NullStorage.php
index 967f8b5108e..71a40d8da1e 100644
--- a/lib/private/Lockdown/Filesystem/NullStorage.php
+++ b/lib/private/Lockdown/Filesystem/NullStorage.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Lockdown/LockdownManager.php b/lib/private/Lockdown/LockdownManager.php
index 3b45709d5c9..4f351812bad 100644
--- a/lib/private/Lockdown/LockdownManager.php
+++ b/lib/private/Lockdown/LockdownManager.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Log.php b/lib/private/Log.php
index 746e4d75b91..301a25d12c1 100644
--- a/lib/private/Log.php
+++ b/lib/private/Log.php
@@ -306,9 +306,9 @@ class Log implements ILogger, IDataLogger {
protected function checkLogSecret(string $conditionSecret): bool {
$request = \OCP\Server::get(IRequest::class);
- if ($request->getMethod() === 'PUT' &&
- !str_contains($request->getHeader('Content-Type'), 'application/x-www-form-urlencoded') &&
- !str_contains($request->getHeader('Content-Type'), 'application/json')) {
+ if ($request->getMethod() === 'PUT'
+ && !str_contains($request->getHeader('Content-Type'), 'application/x-www-form-urlencoded')
+ && !str_contains($request->getHeader('Content-Type'), 'application/json')) {
return hash_equals($conditionSecret, '');
}
diff --git a/lib/private/Log/ErrorHandler.php b/lib/private/Log/ErrorHandler.php
index e1faf336118..6597274a868 100644
--- a/lib/private/Log/ErrorHandler.php
+++ b/lib/private/Log/ErrorHandler.php
@@ -72,9 +72,9 @@ class ErrorHandler {
private static function errnoToLogLevel(int $errno): int {
return match ($errno) {
- E_USER_WARNING => ILogger::WARN,
+ E_WARNING, E_USER_WARNING => ILogger::WARN,
E_DEPRECATED, E_USER_DEPRECATED => ILogger::DEBUG,
- E_USER_NOTICE => ILogger::INFO,
+ E_NOTICE, E_USER_NOTICE => ILogger::INFO,
default => ILogger::ERROR,
};
}
diff --git a/lib/private/Log/ExceptionSerializer.php b/lib/private/Log/ExceptionSerializer.php
index 6d94bf51f45..af7c9e48435 100644
--- a/lib/private/Log/ExceptionSerializer.php
+++ b/lib/private/Log/ExceptionSerializer.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Log/LogDetails.php b/lib/private/Log/LogDetails.php
index 8c1efaea20d..6063b25cef9 100644
--- a/lib/private/Log/LogDetails.php
+++ b/lib/private/Log/LogDetails.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Log/LogFactory.php b/lib/private/Log/LogFactory.php
index bbe77de198c..ee6054b81f8 100644
--- a/lib/private/Log/LogFactory.php
+++ b/lib/private/Log/LogFactory.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Log/Rotate.php b/lib/private/Log/Rotate.php
index 839c40726b7..ee1593b87ac 100644
--- a/lib/private/Log/Rotate.php
+++ b/lib/private/Log/Rotate.php
@@ -7,6 +7,8 @@
*/
namespace OC\Log;
+use OCP\AppFramework\Utility\ITimeFactory;
+use OCP\BackgroundJob\TimedJob;
use OCP\IConfig;
use OCP\Log\RotationTrait;
use Psr\Log\LoggerInterface;
@@ -17,9 +19,15 @@ use Psr\Log\LoggerInterface;
* For more professional log management set the 'logfile' config to a different
* location and manage that with your own tools.
*/
-class Rotate extends \OCP\BackgroundJob\Job {
+class Rotate extends TimedJob {
use RotationTrait;
+ public function __construct(ITimeFactory $time) {
+ parent::__construct($time);
+
+ $this->setInterval(3600);
+ }
+
public function run($argument): void {
$config = \OCP\Server::get(IConfig::class);
$this->filePath = $config->getSystemValueString('logfile', $config->getSystemValueString('datadirectory', \OC::$SERVERROOT . '/data') . '/nextcloud.log');
diff --git a/lib/private/Log/Systemdlog.php b/lib/private/Log/Systemdlog.php
index 46de0f7db97..ffea0511732 100644
--- a/lib/private/Log/Systemdlog.php
+++ b/lib/private/Log/Systemdlog.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Mail/Message.php b/lib/private/Mail/Message.php
index faeba469e20..523a4836760 100644
--- a/lib/private/Mail/Message.php
+++ b/lib/private/Mail/Message.php
@@ -316,7 +316,7 @@ class Message implements IMessage {
public function getAutoSubmitted(): string {
$headers = $this->symfonyEmail->getHeaders();
- return $headers->has(AutoSubmitted::HEADER) ?
- $headers->get(AutoSubmitted::HEADER)->getBodyAsString() : AutoSubmitted::VALUE_NO;
+ return $headers->has(AutoSubmitted::HEADER)
+ ? $headers->get(AutoSubmitted::HEADER)->getBodyAsString() : AutoSubmitted::VALUE_NO;
}
}
diff --git a/lib/private/Mail/Provider/Manager.php b/lib/private/Mail/Provider/Manager.php
index 61791620198..f162d30b834 100644
--- a/lib/private/Mail/Provider/Manager.php
+++ b/lib/private/Mail/Provider/Manager.php
@@ -17,7 +17,7 @@ use Psr\Log\LoggerInterface;
use Throwable;
class Manager implements IManager {
-
+
protected ?array $providersCollection = null;
public function __construct(
@@ -63,7 +63,7 @@ class Manager implements IManager {
* @return array<string,string> collection of provider id and label ['jmap' => 'JMap Connector']
*/
public function types(): array {
-
+
// construct types collection
$types = [];
// extract id and name from providers collection
@@ -72,7 +72,7 @@ class Manager implements IManager {
}
// return types collection
return $types;
-
+
}
/**
@@ -131,7 +131,7 @@ class Manager implements IManager {
if (!is_array($this->providersCollection)) {
$this->providers();
}
-
+
if (isset($this->providersCollection[$providerId])) {
return $this->providersCollection[$providerId];
}
@@ -150,7 +150,7 @@ class Manager implements IManager {
* @return array<string,array<string,IService>> collection of provider id, service id and object ['jmap' => ['Service1' => IServiceObject]]
*/
public function services(string $userId): array {
-
+
// initilize collection
$services = [];
// retrieve and iterate through mail providers
@@ -164,7 +164,7 @@ class Manager implements IManager {
}
// return collection
return $services;
-
+
}
/**
@@ -179,7 +179,7 @@ class Manager implements IManager {
* @return IService|null returns service object or null if none found
*/
public function findServiceById(string $userId, string $serviceId, ?string $providerId = null): ?IService {
-
+
// evaluate if provider id was specified
if ($providerId !== null) {
// find provider
@@ -204,7 +204,7 @@ class Manager implements IManager {
}
}
}
-
+
// return null if no match was found
return null;
@@ -223,7 +223,7 @@ class Manager implements IManager {
* @return IService|null returns service object or null if none found
*/
public function findServiceByAddress(string $userId, string $address, ?string $providerId = null): ?IService {
-
+
// evaluate if provider id was specified
if ($providerId !== null) {
// find provider
diff --git a/lib/private/Memcache/APCu.php b/lib/private/Memcache/APCu.php
index 024462d227b..937f8a863ab 100644
--- a/lib/private/Memcache/APCu.php
+++ b/lib/private/Memcache/APCu.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/lib/private/Memcache/ArrayCache.php b/lib/private/Memcache/ArrayCache.php
index 4cac60c272c..9b3540b771f 100644
--- a/lib/private/Memcache/ArrayCache.php
+++ b/lib/private/Memcache/ArrayCache.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/lib/private/Memcache/CADTrait.php b/lib/private/Memcache/CADTrait.php
index 3bf94246338..d0f6611c4f3 100644
--- a/lib/private/Memcache/CADTrait.php
+++ b/lib/private/Memcache/CADTrait.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/lib/private/Memcache/CASTrait.php b/lib/private/Memcache/CASTrait.php
index 945f1539f99..8c2d2a46b19 100644
--- a/lib/private/Memcache/CASTrait.php
+++ b/lib/private/Memcache/CASTrait.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/lib/private/Memcache/Cache.php b/lib/private/Memcache/Cache.php
index 2a2a6e2a23f..774769b25fe 100644
--- a/lib/private/Memcache/Cache.php
+++ b/lib/private/Memcache/Cache.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/lib/private/Memcache/Factory.php b/lib/private/Memcache/Factory.php
index a44f0127651..b54189937fc 100644
--- a/lib/private/Memcache/Factory.php
+++ b/lib/private/Memcache/Factory.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@@ -134,8 +135,8 @@ class Factory implements ICacheFactory {
$this->profiler->add($cache);
}
- if ($this->lockingCacheClass === Redis::class &&
- $this->logFile !== '' && is_writable(dirname($this->logFile)) && (!file_exists($this->logFile) || is_writable($this->logFile))) {
+ if ($this->lockingCacheClass === Redis::class
+ && $this->logFile !== '' && is_writable(dirname($this->logFile)) && (!file_exists($this->logFile) || is_writable($this->logFile))) {
$cache = new LoggerWrapperCache($cache, $this->logFile);
}
return $cache;
diff --git a/lib/private/Memcache/Memcached.php b/lib/private/Memcache/Memcached.php
index 620013feda6..d8b624a978a 100644
--- a/lib/private/Memcache/Memcached.php
+++ b/lib/private/Memcache/Memcached.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@@ -48,8 +49,8 @@ class Memcached extends Cache implements IMemcache {
* @psalm-suppress TypeDoesNotContainType
*/
if (\Memcached::HAVE_IGBINARY) {
- $defaultOptions[\Memcached::OPT_SERIALIZER] =
- \Memcached::SERIALIZER_IGBINARY;
+ $defaultOptions[\Memcached::OPT_SERIALIZER]
+ = \Memcached::SERIALIZER_IGBINARY;
}
$options = \OC::$server->getConfig()->getSystemValue('memcached_options', []);
if (is_array($options)) {
diff --git a/lib/private/Memcache/NullCache.php b/lib/private/Memcache/NullCache.php
index b667869bf0d..eac1e6ddadc 100644
--- a/lib/private/Memcache/NullCache.php
+++ b/lib/private/Memcache/NullCache.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/lib/private/Memcache/Redis.php b/lib/private/Memcache/Redis.php
index 711531e0ac2..f8c51570c4f 100644
--- a/lib/private/Memcache/Redis.php
+++ b/lib/private/Memcache/Redis.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/lib/private/Migration/ConsoleOutput.php b/lib/private/Migration/ConsoleOutput.php
index 7ccc4e7825a..31412bf4ff0 100644
--- a/lib/private/Migration/ConsoleOutput.php
+++ b/lib/private/Migration/ConsoleOutput.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2015 ownCloud GmbH
diff --git a/lib/private/Migration/NullOutput.php b/lib/private/Migration/NullOutput.php
index 3f4cc38dba8..8db7b950af8 100644
--- a/lib/private/Migration/NullOutput.php
+++ b/lib/private/Migration/NullOutput.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-only
diff --git a/lib/private/Migration/SimpleOutput.php b/lib/private/Migration/SimpleOutput.php
index 31420d49932..b7a07cc6ff2 100644
--- a/lib/private/Migration/SimpleOutput.php
+++ b/lib/private/Migration/SimpleOutput.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2015 ownCloud GmbH
diff --git a/lib/private/Notification/Manager.php b/lib/private/Notification/Manager.php
index b75e52deacb..8c457db8beb 100644
--- a/lib/private/Notification/Manager.php
+++ b/lib/private/Notification/Manager.php
@@ -217,7 +217,9 @@ class Manager implements IManager {
* @since 8.2.0
*/
public function hasNotifiers(): bool {
- return !empty($this->notifiers) || !empty($this->notifierClasses);
+ return !empty($this->notifiers)
+ || !empty($this->notifierClasses)
+ || (!$this->parsedRegistrationContext && !empty($this->coordinator->getRegistrationContext()->getNotifierServices()));
}
/**
diff --git a/lib/private/Notification/Notification.php b/lib/private/Notification/Notification.php
index f8f1e247854..fcce7fd0020 100644
--- a/lib/private/Notification/Notification.php
+++ b/lib/private/Notification/Notification.php
@@ -429,8 +429,7 @@ class Notification implements INotification {
public function isValid(): bool {
return
$this->isValidCommon()
- &&
- $this->getSubject() !== ''
+ && $this->getSubject() !== ''
;
}
@@ -456,8 +455,7 @@ class Notification implements INotification {
return
$this->isValidCommon()
- &&
- $this->getParsedSubject() !== ''
+ && $this->getParsedSubject() !== ''
;
}
@@ -468,14 +466,10 @@ class Notification implements INotification {
return
$this->getApp() !== ''
- &&
- $this->getUser() !== ''
- &&
- $this->getDateTime()->getTimestamp() !== 0
- &&
- $this->getObjectType() !== ''
- &&
- $this->getObjectId() !== ''
+ && $this->getUser() !== ''
+ && $this->getDateTime()->getTimestamp() !== 0
+ && $this->getObjectType() !== ''
+ && $this->getObjectId() !== ''
;
}
}
diff --git a/lib/private/OCM/Model/OCMProvider.php b/lib/private/OCM/Model/OCMProvider.php
index f4b0ac584de..be13d65a40f 100644
--- a/lib/private/OCM/Model/OCMProvider.php
+++ b/lib/private/OCM/Model/OCMProvider.php
@@ -11,18 +11,22 @@ namespace OC\OCM\Model;
use NCU\Security\Signature\Model\Signatory;
use OCP\EventDispatcher\IEventDispatcher;
+use OCP\IConfig;
use OCP\OCM\Events\ResourceTypeRegisterEvent;
use OCP\OCM\Exceptions\OCMArgumentException;
use OCP\OCM\Exceptions\OCMProviderException;
-use OCP\OCM\IOCMProvider;
+use OCP\OCM\ICapabilityAwareOCMProvider;
use OCP\OCM\IOCMResource;
/**
* @since 28.0.0
*/
-class OCMProvider implements IOCMProvider {
+class OCMProvider implements ICapabilityAwareOCMProvider {
+ private string $provider;
private bool $enabled = false;
private string $apiVersion = '';
+ private string $inviteAcceptDialog = '';
+ private array $capabilities = [];
private string $endPoint = '';
/** @var IOCMResource[] */
private array $resourceTypes = [];
@@ -31,7 +35,9 @@ class OCMProvider implements IOCMProvider {
public function __construct(
protected IEventDispatcher $dispatcher,
+ protected IConfig $config,
) {
+ $this->provider = 'Nextcloud ' . $config->getSystemValue('version');
}
/**
@@ -71,6 +77,30 @@ class OCMProvider implements IOCMProvider {
}
/**
+ * returns the invite accept dialog
+ *
+ * @return string
+ * @since 32.0.0
+ */
+ public function getInviteAcceptDialog(): string {
+ return $this->inviteAcceptDialog;
+ }
+
+ /**
+ * set the invite accept dialog
+ *
+ * @param string $inviteAcceptDialog
+ *
+ * @return $this
+ * @since 32.0.0
+ */
+ public function setInviteAcceptDialog(string $inviteAcceptDialog): static {
+ $this->inviteAcceptDialog = $inviteAcceptDialog;
+
+ return $this;
+ }
+
+ /**
* @param string $endPoint
*
* @return $this
@@ -89,6 +119,34 @@ class OCMProvider implements IOCMProvider {
}
/**
+ * @return string
+ */
+ public function getProvider(): string {
+ return $this->provider;
+ }
+
+ /**
+ * @param array $capabilities
+ *
+ * @return $this
+ */
+ public function setCapabilities(array $capabilities): static {
+ foreach ($capabilities as $value) {
+ if (!in_array($value, $this->capabilities)) {
+ array_push($this->capabilities, $value);
+ }
+ }
+
+ return $this;
+ }
+
+ /**
+ * @return array
+ */
+ public function getCapabilities(): array {
+ return $this->capabilities;
+ }
+ /**
* create a new resource to later add it with {@see IOCMProvider::addResourceType()}
* @return IOCMResource
*/
@@ -166,9 +224,8 @@ class OCMProvider implements IOCMProvider {
*
* @param array $data
*
- * @return $this
+ * @return OCMProvider&static
* @throws OCMProviderException in case a descent provider cannot be generated from data
- * @see self::jsonSerialize()
*/
public function import(array $data): static {
$this->setEnabled(is_bool($data['enabled'] ?? '') ? $data['enabled'] : false)
@@ -209,21 +266,7 @@ class OCMProvider implements IOCMProvider {
}
/**
- * @return array{
- * enabled: bool,
- * apiVersion: '1.0-proposal1',
- * endPoint: string,
- * publicKey?: array{
- * keyId: string,
- * publicKeyPem: string
- * },
- * resourceTypes: list<array{
- * name: string,
- * shareTypes: list<string>,
- * protocols: array<string, string>
- * }>,
- * version: string
- * }
+ * @since 28.0.0
*/
public function jsonSerialize(): array {
$resourceTypes = [];
@@ -231,7 +274,7 @@ class OCMProvider implements IOCMProvider {
$resourceTypes[] = $res->jsonSerialize();
}
- return [
+ $response = [
'enabled' => $this->isEnabled(),
'apiVersion' => '1.0-proposal1', // deprecated, but keep it to stay compatible with old version
'version' => $this->getApiVersion(), // informative but real version
@@ -239,5 +282,16 @@ class OCMProvider implements IOCMProvider {
'publicKey' => $this->getSignatory()?->jsonSerialize(),
'resourceTypes' => $resourceTypes
];
+
+ $capabilities = $this->getCapabilities();
+ $inviteAcceptDialog = $this->getInviteAcceptDialog();
+ if ($capabilities) {
+ $response['capabilities'] = $capabilities;
+ }
+ if ($inviteAcceptDialog) {
+ $response['inviteAcceptDialog'] = $inviteAcceptDialog;
+ }
+ return $response;
+
}
}
diff --git a/lib/private/OCM/OCMDiscoveryService.php b/lib/private/OCM/OCMDiscoveryService.php
index af612416372..a151bbc753c 100644
--- a/lib/private/OCM/OCMDiscoveryService.php
+++ b/lib/private/OCM/OCMDiscoveryService.php
@@ -17,8 +17,8 @@ use OCP\ICache;
use OCP\ICacheFactory;
use OCP\IConfig;
use OCP\OCM\Exceptions\OCMProviderException;
+use OCP\OCM\ICapabilityAwareOCMProvider;
use OCP\OCM\IOCMDiscoveryService;
-use OCP\OCM\IOCMProvider;
use Psr\Log\LoggerInterface;
/**
@@ -31,7 +31,7 @@ class OCMDiscoveryService implements IOCMDiscoveryService {
ICacheFactory $cacheFactory,
private IClientService $clientService,
private IConfig $config,
- private IOCMProvider $provider,
+ private ICapabilityAwareOCMProvider $provider,
private LoggerInterface $logger,
) {
$this->cache = $cacheFactory->createDistributed('ocm-discovery');
@@ -42,10 +42,10 @@ class OCMDiscoveryService implements IOCMDiscoveryService {
* @param string $remote
* @param bool $skipCache
*
- * @return IOCMProvider
+ * @return ICapabilityAwareOCMProvider
* @throws OCMProviderException
*/
- public function discover(string $remote, bool $skipCache = false): IOCMProvider {
+ public function discover(string $remote, bool $skipCache = false): ICapabilityAwareOCMProvider {
$remote = rtrim($remote, '/');
if (!str_starts_with($remote, 'http://') && !str_starts_with($remote, 'https://')) {
// if scheme not specified, we test both;
diff --git a/lib/private/Preview/Bundled.php b/lib/private/Preview/Bundled.php
index 836dc4bd357..6100e8262a4 100644
--- a/lib/private/Preview/Bundled.php
+++ b/lib/private/Preview/Bundled.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Preview/GeneratorHelper.php b/lib/private/Preview/GeneratorHelper.php
index 5f43c94b624..e914dcc2002 100644
--- a/lib/private/Preview/GeneratorHelper.php
+++ b/lib/private/Preview/GeneratorHelper.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Preview/Imaginary.php b/lib/private/Preview/Imaginary.php
index baa883f4bd9..d421da74ac8 100644
--- a/lib/private/Preview/Imaginary.php
+++ b/lib/private/Preview/Imaginary.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Preview/Krita.php b/lib/private/Preview/Krita.php
index 2e77c7befd2..e96fac993aa 100644
--- a/lib/private/Preview/Krita.php
+++ b/lib/private/Preview/Krita.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Preview/MimeIconProvider.php b/lib/private/Preview/MimeIconProvider.php
index 80545bd4063..d1963fe882b 100644
--- a/lib/private/Preview/MimeIconProvider.php
+++ b/lib/private/Preview/MimeIconProvider.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -54,7 +55,7 @@ class MimeIconProvider implements IMimeIconProvider {
return null;
}
-
+
private function searchfileName(string $fileName): ?string {
// If the file exists in the current enabled legacy
// custom theme, let's return it
@@ -65,7 +66,7 @@ class MimeIconProvider implements IMimeIconProvider {
return $this->urlGenerator->getAbsoluteURL($path);
}
}
-
+
// Previously, we used to pass this through Theming
// But it was only used to colour icons containing
// 0082c9. Since with vue we moved to inline svg icons,
diff --git a/lib/private/Preview/Movie.php b/lib/private/Preview/Movie.php
index 7de543198f4..47895f999d8 100644
--- a/lib/private/Preview/Movie.php
+++ b/lib/private/Preview/Movie.php
@@ -166,8 +166,8 @@ class Movie extends ProviderV2 {
$returnCode = -1;
$output = '';
if (is_resource($proc)) {
- $stdout = trim(stream_get_contents($pipes[1]));
$stderr = trim(stream_get_contents($pipes[2]));
+ $stdout = trim(stream_get_contents($pipes[1]));
$returnCode = proc_close($proc);
$output = $stdout . $stderr;
}
diff --git a/lib/private/Preview/SGI.php b/lib/private/Preview/SGI.php
index 06ea9c0c69a..78b1ea5828a 100644
--- a/lib/private/Preview/SGI.php
+++ b/lib/private/Preview/SGI.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Preview/TGA.php b/lib/private/Preview/TGA.php
index 62e5aadc2af..675907b4e49 100644
--- a/lib/private/Preview/TGA.php
+++ b/lib/private/Preview/TGA.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/PreviewManager.php b/lib/private/PreviewManager.php
index 1f618dab22d..0bb0280406c 100644
--- a/lib/private/PreviewManager.php
+++ b/lib/private/PreviewManager.php
@@ -154,7 +154,7 @@ class PreviewManager implements IPreview {
$mimeType = null,
bool $cacheResult = true,
): ISimpleFile {
- $this->throwIfPreviewsDisabled();
+ $this->throwIfPreviewsDisabled($file, $mimeType);
$previewConcurrency = $this->getGenerator()->getNumConcurrentPreviews('preview_concurrency_all');
$sem = Generator::guardWithSemaphore(Generator::SEMAPHORE_ID_ALL, $previewConcurrency);
try {
@@ -178,7 +178,7 @@ class PreviewManager implements IPreview {
* @since 19.0.0
*/
public function generatePreviews(File $file, array $specifications, $mimeType = null) {
- $this->throwIfPreviewsDisabled();
+ $this->throwIfPreviewsDisabled($file, $mimeType);
return $this->getGenerator()->generatePreviews($file, $specifications, $mimeType);
}
@@ -213,13 +213,15 @@ class PreviewManager implements IPreview {
/**
* Check if a preview can be generated for a file
*/
- public function isAvailable(\OCP\Files\FileInfo $file): bool {
+ public function isAvailable(\OCP\Files\FileInfo $file, ?string $mimeType = null): bool {
if (!$this->enablePreviews) {
return false;
}
+ $fileMimeType = $mimeType ?? $file->getMimeType();
+
$this->registerCoreProviders();
- if (!$this->isMimeSupported($file->getMimetype())) {
+ if (!$this->isMimeSupported($fileMimeType)) {
return false;
}
@@ -229,7 +231,7 @@ class PreviewManager implements IPreview {
}
foreach ($this->providers as $supportedMimeType => $providers) {
- if (preg_match($supportedMimeType, $file->getMimetype())) {
+ if (preg_match($supportedMimeType, $fileMimeType)) {
foreach ($providers as $providerClosure) {
$provider = $this->helper->getProvider($providerClosure);
if (!($provider instanceof IProviderV2)) {
@@ -455,8 +457,8 @@ class PreviewManager implements IPreview {
/**
* @throws NotFoundException if preview generation is disabled
*/
- private function throwIfPreviewsDisabled(): void {
- if (!$this->enablePreviews) {
+ private function throwIfPreviewsDisabled(File $file, ?string $mimeType = null): void {
+ if (!$this->isAvailable($file, $mimeType)) {
throw new NotFoundException('Previews disabled');
}
}
diff --git a/lib/private/PreviewNotAvailableException.php b/lib/private/PreviewNotAvailableException.php
index b1fec912769..582f337acd1 100644
--- a/lib/private/PreviewNotAvailableException.php
+++ b/lib/private/PreviewNotAvailableException.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Profile/ProfileManager.php b/lib/private/Profile/ProfileManager.php
index 13860286356..84c7ea48373 100644
--- a/lib/private/Profile/ProfileManager.php
+++ b/lib/private/Profile/ProfileManager.php
@@ -247,8 +247,8 @@ class ProfileManager implements IProfileManager {
case IAccountManager::PROPERTY_ORGANISATION:
case IAccountManager::PROPERTY_ROLE:
case IAccountManager::PROPERTY_PRONOUNS:
- $profileParameters[$property] =
- $this->isProfileFieldVisible($property, $targetUser, $visitingUser)
+ $profileParameters[$property]
+ = $this->isProfileFieldVisible($property, $targetUser, $visitingUser)
// Explicitly set to null when value is empty string
? ($account->getProperty($property)->getValue() ?: null)
: null;
diff --git a/lib/private/RedisFactory.php b/lib/private/RedisFactory.php
index dcb56cee9ef..f13f9299c1c 100644
--- a/lib/private/RedisFactory.php
+++ b/lib/private/RedisFactory.php
@@ -152,8 +152,8 @@ class RedisFactory {
}
public function isAvailable(): bool {
- return \extension_loaded('redis') &&
- \version_compare(\phpversion('redis'), self::REDIS_MINIMAL_VERSION, '>=');
+ return \extension_loaded('redis')
+ && \version_compare(\phpversion('redis'), self::REDIS_MINIMAL_VERSION, '>=');
}
/**
@@ -163,7 +163,7 @@ class RedisFactory {
* @return boolean
*/
private function isConnectionParametersSupported(): bool {
- return \extension_loaded('redis') &&
- \version_compare(\phpversion('redis'), self::REDIS_EXTRA_PARAMETERS_MINIMAL_VERSION, '>=');
+ return \extension_loaded('redis')
+ && \version_compare(\phpversion('redis'), self::REDIS_EXTRA_PARAMETERS_MINIMAL_VERSION, '>=');
}
}
diff --git a/lib/private/Remote/Api/ApiBase.php b/lib/private/Remote/Api/ApiBase.php
index dff3edb51b9..b2f96fb3c24 100644
--- a/lib/private/Remote/Api/ApiBase.php
+++ b/lib/private/Remote/Api/ApiBase.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Remote/Api/ApiCollection.php b/lib/private/Remote/Api/ApiCollection.php
index 65039f4b5aa..f154cd21926 100644
--- a/lib/private/Remote/Api/ApiCollection.php
+++ b/lib/private/Remote/Api/ApiCollection.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Remote/Api/ApiFactory.php b/lib/private/Remote/Api/ApiFactory.php
index 7daddd16011..795584e566a 100644
--- a/lib/private/Remote/Api/ApiFactory.php
+++ b/lib/private/Remote/Api/ApiFactory.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Remote/Api/NotFoundException.php b/lib/private/Remote/Api/NotFoundException.php
index 5251313f5f0..361d03d24ed 100644
--- a/lib/private/Remote/Api/NotFoundException.php
+++ b/lib/private/Remote/Api/NotFoundException.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Remote/Api/OCS.php b/lib/private/Remote/Api/OCS.php
index de07eb8bc56..36bc22751a5 100644
--- a/lib/private/Remote/Api/OCS.php
+++ b/lib/private/Remote/Api/OCS.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Remote/Credentials.php b/lib/private/Remote/Credentials.php
index fb0f03ae148..7f1ffaa727c 100644
--- a/lib/private/Remote/Credentials.php
+++ b/lib/private/Remote/Credentials.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Remote/Instance.php b/lib/private/Remote/Instance.php
index ac3233b93c9..10403af4ec7 100644
--- a/lib/private/Remote/Instance.php
+++ b/lib/private/Remote/Instance.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -123,7 +124,13 @@ class Instance implements IInstance {
private function downloadStatus($url) {
try {
$request = $this->clientService->newClient()->get($url);
- return $request->getBody();
+ $content = $request->getBody();
+
+ // IResponse.getBody responds with null|resource if returning a stream response was requested.
+ // As that's not the case here, we can just ignore the psalm warning by adding an assertion.
+ assert(is_string($content));
+
+ return $content;
} catch (\Exception $e) {
return false;
}
diff --git a/lib/private/Remote/InstanceFactory.php b/lib/private/Remote/InstanceFactory.php
index f3047b851b2..f1b7a1de4ba 100644
--- a/lib/private/Remote/InstanceFactory.php
+++ b/lib/private/Remote/InstanceFactory.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Remote/User.php b/lib/private/Remote/User.php
index 5c8e9d3ca4e..ae1032cdc1c 100644
--- a/lib/private/Remote/User.php
+++ b/lib/private/Remote/User.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Repair/AddCleanupUpdaterBackupsJob.php b/lib/private/Repair/AddCleanupUpdaterBackupsJob.php
index 8bd938b7e3a..e631a3303f1 100644
--- a/lib/private/Repair/AddCleanupUpdaterBackupsJob.php
+++ b/lib/private/Repair/AddCleanupUpdaterBackupsJob.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Repair/AddMetadataGenerationJob.php b/lib/private/Repair/AddMetadataGenerationJob.php
index 4535fb0c9e0..76c60f303a7 100644
--- a/lib/private/Repair/AddMetadataGenerationJob.php
+++ b/lib/private/Repair/AddMetadataGenerationJob.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Repair/ClearFrontendCaches.php b/lib/private/Repair/ClearFrontendCaches.php
index 77a3df5598a..5c57a63379d 100644
--- a/lib/private/Repair/ClearFrontendCaches.php
+++ b/lib/private/Repair/ClearFrontendCaches.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Repair/ClearGeneratedAvatarCache.php b/lib/private/Repair/ClearGeneratedAvatarCache.php
index 2dea4bd2d61..0f743afbb4c 100644
--- a/lib/private/Repair/ClearGeneratedAvatarCache.php
+++ b/lib/private/Repair/ClearGeneratedAvatarCache.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Repair/ClearGeneratedAvatarCacheJob.php b/lib/private/Repair/ClearGeneratedAvatarCacheJob.php
index 38cf03b731a..524a470e62a 100644
--- a/lib/private/Repair/ClearGeneratedAvatarCacheJob.php
+++ b/lib/private/Repair/ClearGeneratedAvatarCacheJob.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Repair/Collation.php b/lib/private/Repair/Collation.php
index 5a309892bf0..43229792217 100644
--- a/lib/private/Repair/Collation.php
+++ b/lib/private/Repair/Collation.php
@@ -97,11 +97,11 @@ class Collation implements IRepairStep {
// fetch tables by columns
$statement = $connection->executeQuery(
- 'SELECT DISTINCT(TABLE_NAME) AS `table`' .
- ' FROM INFORMATION_SCHEMA . COLUMNS' .
- ' WHERE TABLE_SCHEMA = ?' .
- " AND (COLLATION_NAME <> '" . $characterSet . "_bin' OR CHARACTER_SET_NAME <> '" . $characterSet . "')" .
- " AND TABLE_NAME LIKE '*PREFIX*%'",
+ 'SELECT DISTINCT(TABLE_NAME) AS `table`'
+ . ' FROM INFORMATION_SCHEMA . COLUMNS'
+ . ' WHERE TABLE_SCHEMA = ?'
+ . " AND (COLLATION_NAME <> '" . $characterSet . "_bin' OR CHARACTER_SET_NAME <> '" . $characterSet . "')"
+ . " AND TABLE_NAME LIKE '*PREFIX*%'",
[$dbName]
);
$rows = $statement->fetchAll();
@@ -112,11 +112,11 @@ class Collation implements IRepairStep {
// fetch tables by collation
$statement = $connection->executeQuery(
- 'SELECT DISTINCT(TABLE_NAME) AS `table`' .
- ' FROM INFORMATION_SCHEMA . TABLES' .
- ' WHERE TABLE_SCHEMA = ?' .
- " AND TABLE_COLLATION <> '" . $characterSet . "_bin'" .
- " AND TABLE_NAME LIKE '*PREFIX*%'",
+ 'SELECT DISTINCT(TABLE_NAME) AS `table`'
+ . ' FROM INFORMATION_SCHEMA . TABLES'
+ . ' WHERE TABLE_SCHEMA = ?'
+ . " AND TABLE_COLLATION <> '" . $characterSet . "_bin'"
+ . " AND TABLE_NAME LIKE '*PREFIX*%'",
[$dbName]
);
$rows = $statement->fetchAll();
diff --git a/lib/private/Repair/ConfigKeyMigration.php b/lib/private/Repair/ConfigKeyMigration.php
new file mode 100644
index 00000000000..da4aa153dc5
--- /dev/null
+++ b/lib/private/Repair/ConfigKeyMigration.php
@@ -0,0 +1,29 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OC\Repair;
+
+use OC\Config\ConfigManager;
+use OCP\Migration\IOutput;
+use OCP\Migration\IRepairStep;
+
+class ConfigKeyMigration implements IRepairStep {
+ public function __construct(
+ private ConfigManager $configManager,
+ ) {
+ }
+
+ public function getName(): string {
+ return 'Migrate config keys';
+ }
+
+ public function run(IOutput $output) {
+ $this->configManager->migrateConfigLexiconKeys();
+ }
+}
diff --git a/lib/private/Repair/MoveUpdaterStepFile.php b/lib/private/Repair/MoveUpdaterStepFile.php
index eb9f78b0a39..bb8f9d3acfc 100644
--- a/lib/private/Repair/MoveUpdaterStepFile.php
+++ b/lib/private/Repair/MoveUpdaterStepFile.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Repair/NC13/AddLogRotateJob.php b/lib/private/Repair/NC13/AddLogRotateJob.php
index 8fe68a42819..bd6c510785f 100644
--- a/lib/private/Repair/NC13/AddLogRotateJob.php
+++ b/lib/private/Repair/NC13/AddLogRotateJob.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Repair/NC21/AddCheckForUserCertificatesJob.php b/lib/private/Repair/NC21/AddCheckForUserCertificatesJob.php
index 4f80b3809e8..5cee33b381c 100644
--- a/lib/private/Repair/NC21/AddCheckForUserCertificatesJob.php
+++ b/lib/private/Repair/NC21/AddCheckForUserCertificatesJob.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Repair/Owncloud/CleanPreviews.php b/lib/private/Repair/Owncloud/CleanPreviews.php
index 86e173cf402..50ee965e087 100644
--- a/lib/private/Repair/Owncloud/CleanPreviews.php
+++ b/lib/private/Repair/Owncloud/CleanPreviews.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Repair/Owncloud/DropAccountTermsTable.php b/lib/private/Repair/Owncloud/DropAccountTermsTable.php
index 18f169c9b49..534825c146a 100644
--- a/lib/private/Repair/Owncloud/DropAccountTermsTable.php
+++ b/lib/private/Repair/Owncloud/DropAccountTermsTable.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Repair/Owncloud/MoveAvatars.php b/lib/private/Repair/Owncloud/MoveAvatars.php
index 7fdabae7a66..9e3f4b89b13 100644
--- a/lib/private/Repair/Owncloud/MoveAvatars.php
+++ b/lib/private/Repair/Owncloud/MoveAvatars.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Repair/Owncloud/SaveAccountsTableData.php b/lib/private/Repair/Owncloud/SaveAccountsTableData.php
index 08665687b29..ab1560ddb8d 100644
--- a/lib/private/Repair/Owncloud/SaveAccountsTableData.php
+++ b/lib/private/Repair/Owncloud/SaveAccountsTableData.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Repair/Owncloud/UpdateLanguageCodes.php b/lib/private/Repair/Owncloud/UpdateLanguageCodes.php
index e27ab06b2f3..8d9046ad49f 100644
--- a/lib/private/Repair/Owncloud/UpdateLanguageCodes.php
+++ b/lib/private/Repair/Owncloud/UpdateLanguageCodes.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Repair/RepairMimeTypes.php b/lib/private/Repair/RepairMimeTypes.php
index 28b22ec3f20..3c9720b9e91 100644
--- a/lib/private/Repair/RepairMimeTypes.php
+++ b/lib/private/Repair/RepairMimeTypes.php
@@ -350,7 +350,7 @@ class RepairMimeTypes implements IRepairStep {
return $this->updateMimetypes($updatedMimetypes);
}
-
+
/**
* Check if there are any migrations available
diff --git a/lib/private/Route/CachingRouter.php b/lib/private/Route/CachingRouter.php
index 7dd26827d3c..becdb807f73 100644
--- a/lib/private/Route/CachingRouter.php
+++ b/lib/private/Route/CachingRouter.php
@@ -15,10 +15,16 @@ use OCP\IConfig;
use OCP\IRequest;
use Psr\Container\ContainerInterface;
use Psr\Log\LoggerInterface;
+use Symfony\Component\Routing\Exception\ResourceNotFoundException;
+use Symfony\Component\Routing\Matcher\CompiledUrlMatcher;
+use Symfony\Component\Routing\Matcher\Dumper\CompiledUrlMatcherDumper;
+use Symfony\Component\Routing\RouteCollection;
class CachingRouter extends Router {
protected ICache $cache;
+ protected array $legacyCreatedRoutes = [];
+
public function __construct(
ICacheFactory $cacheFactory,
LoggerInterface $logger,
@@ -54,4 +60,98 @@ class CachingRouter extends Router {
return $url;
}
}
+
+ private function serializeRouteCollection(RouteCollection $collection): array {
+ $dumper = new CompiledUrlMatcherDumper($collection);
+ return $dumper->getCompiledRoutes();
+ }
+
+ /**
+ * Find the route matching $url
+ *
+ * @param string $url The url to find
+ * @throws \Exception
+ * @return array
+ */
+ public function findMatchingRoute(string $url): array {
+ $this->eventLogger->start('cacheroute:match', 'Match route');
+ $key = $this->context->getHost() . '#' . $this->context->getBaseUrl() . '#rootCollection';
+ $cachedRoutes = $this->cache->get($key);
+ if (!$cachedRoutes) {
+ parent::loadRoutes();
+ $cachedRoutes = $this->serializeRouteCollection($this->root);
+ $this->cache->set($key, $cachedRoutes, ($this->config->getSystemValueBool('debug') ? 3 : 3600));
+ }
+ $matcher = new CompiledUrlMatcher($cachedRoutes, $this->context);
+ $this->eventLogger->start('cacheroute:url:match', 'Symfony URL match call');
+ try {
+ $parameters = $matcher->match($url);
+ } catch (ResourceNotFoundException $e) {
+ if (!str_ends_with($url, '/')) {
+ // We allow links to apps/files? for backwards compatibility reasons
+ // However, since Symfony does not allow empty route names, the route
+ // we need to match is '/', so we need to append the '/' here.
+ try {
+ $parameters = $matcher->match($url . '/');
+ } catch (ResourceNotFoundException $newException) {
+ // If we still didn't match a route, we throw the original exception
+ throw $e;
+ }
+ } else {
+ throw $e;
+ }
+ }
+ $this->eventLogger->end('cacheroute:url:match');
+
+ $this->eventLogger->end('cacheroute:match');
+ return $parameters;
+ }
+
+ /**
+ * @param array{action:mixed, ...} $parameters
+ */
+ protected function callLegacyActionRoute(array $parameters): void {
+ /*
+ * Closures cannot be serialized to cache, so for legacy routes calling an action we have to include the routes.php file again
+ */
+ $app = $parameters['app'];
+ $this->useCollection($app);
+ parent::requireRouteFile($parameters['route-file'], $app);
+ $collection = $this->getCollection($app);
+ $parameters['action'] = $collection->get($parameters['_route'])?->getDefault('action');
+ parent::callLegacyActionRoute($parameters);
+ }
+
+ /**
+ * Create a \OC\Route\Route.
+ * Deprecated
+ *
+ * @param string $name Name of the route to create.
+ * @param string $pattern The pattern to match
+ * @param array $defaults An array of default parameter values
+ * @param array $requirements An array of requirements for parameters (regexes)
+ */
+ public function create($name, $pattern, array $defaults = [], array $requirements = []): Route {
+ $this->legacyCreatedRoutes[] = $name;
+ return parent::create($name, $pattern, $defaults, $requirements);
+ }
+
+ /**
+ * Require a routes.php file
+ */
+ protected function requireRouteFile(string $file, string $appName): void {
+ $this->legacyCreatedRoutes = [];
+ parent::requireRouteFile($file, $appName);
+ foreach ($this->legacyCreatedRoutes as $routeName) {
+ $route = $this->collection?->get($routeName);
+ if ($route === null) {
+ /* Should never happen */
+ throw new \Exception("Could not find route $routeName");
+ }
+ if ($route->hasDefault('action')) {
+ $route->setDefault('route-file', $file);
+ $route->setDefault('app', $appName);
+ }
+ }
+ }
}
diff --git a/lib/private/Route/Route.php b/lib/private/Route/Route.php
index ab5a1f6b59a..08231649e76 100644
--- a/lib/private/Route/Route.php
+++ b/lib/private/Route/Route.php
@@ -124,15 +124,9 @@ class Route extends SymfonyRoute implements IRoute {
* The action to execute when this route matches, includes a file like
* it is called directly
* @param string $file
- * @return void
*/
public function actionInclude($file) {
- $function = function ($param) use ($file) {
- unset($param['_route']);
- $_GET = array_merge($_GET, $param);
- unset($param);
- require_once "$file";
- } ;
- $this->action($function);
+ $this->setDefault('file', $file);
+ return $this;
}
}
diff --git a/lib/private/Route/Router.php b/lib/private/Route/Router.php
index 376852a1b6e..90225212e9a 100644
--- a/lib/private/Route/Router.php
+++ b/lib/private/Route/Router.php
@@ -53,7 +53,7 @@ class Router implements IRouter {
public function __construct(
protected LoggerInterface $logger,
IRequest $request,
- private IConfig $config,
+ protected IConfig $config,
protected IEventLogger $eventLogger,
private ContainerInterface $container,
protected IAppManager $appManager,
@@ -74,6 +74,14 @@ class Router implements IRouter {
$this->root = $this->getCollection('root');
}
+ public function setContext(RequestContext $context): void {
+ $this->context = $context;
+ }
+
+ public function getRouteCollection() {
+ return $this->root;
+ }
+
/**
* Get the files to load the routes from
*
@@ -82,7 +90,7 @@ class Router implements IRouter {
public function getRoutingFiles() {
if ($this->routingFiles === null) {
$this->routingFiles = [];
- foreach (\OC_APP::getEnabledApps() as $app) {
+ foreach ($this->appManager->getEnabledApps() as $app) {
try {
$appPath = $this->appManager->getAppPath($app);
$file = $appPath . '/appinfo/routes.php';
@@ -102,7 +110,7 @@ class Router implements IRouter {
*
* @param null|string $app
*/
- public function loadRoutes($app = null) {
+ public function loadRoutes(?string $app = null, bool $skipLoadingCore = false): void {
if (is_string($app)) {
$app = $this->appManager->cleanAppId($app);
}
@@ -117,7 +125,7 @@ class Router implements IRouter {
$routingFiles = $this->getRoutingFiles();
$this->eventLogger->start('route:load:attributes', 'Loading Routes from attributes');
- foreach (\OC_App::getEnabledApps() as $enabledApp) {
+ foreach ($this->appManager->getEnabledApps() as $enabledApp) {
$this->loadAttributeRoutes($enabledApp);
}
$this->eventLogger->end('route:load:attributes');
@@ -165,7 +173,7 @@ class Router implements IRouter {
}
$this->eventLogger->end('route:load:files');
- if (!isset($this->loadedApps['core'])) {
+ if (!$skipLoadingCore && !isset($this->loadedApps['core'])) {
$this->loadedApps['core'] = true;
$this->useCollection('root');
$this->setupRoutes($this->getAttributeRoutes('core'), 'core');
@@ -312,17 +320,11 @@ class Router implements IRouter {
$application = $this->getApplicationClass($caller[0]);
\OC\AppFramework\App::main($caller[1], $caller[2], $application->getContainer(), $parameters);
} elseif (isset($parameters['action'])) {
- $action = $parameters['action'];
- if (!is_callable($action)) {
- throw new \Exception('not a callable action');
- }
- unset($parameters['action']);
- unset($parameters['caller']);
- $this->eventLogger->start('route:run:call', 'Run callable route');
- call_user_func($action, $parameters);
- $this->eventLogger->end('route:run:call');
+ $this->logger->warning('Deprecated action route used', ['parameters' => $parameters]);
+ $this->callLegacyActionRoute($parameters);
} elseif (isset($parameters['file'])) {
- include $parameters['file'];
+ $this->logger->debug('Deprecated file route used', ['parameters' => $parameters]);
+ $this->includeLegacyFileRoute($parameters);
} else {
throw new \Exception('no action available');
}
@@ -330,6 +332,32 @@ class Router implements IRouter {
}
/**
+ * @param array{file:mixed, ...} $parameters
+ */
+ protected function includeLegacyFileRoute(array $parameters): void {
+ $param = $parameters;
+ unset($param['_route']);
+ $_GET = array_merge($_GET, $param);
+ unset($param);
+ require_once $parameters['file'];
+ }
+
+ /**
+ * @param array{action:mixed, ...} $parameters
+ */
+ protected function callLegacyActionRoute(array $parameters): void {
+ $action = $parameters['action'];
+ if (!is_callable($action)) {
+ throw new \Exception('not a callable action');
+ }
+ unset($parameters['action']);
+ unset($parameters['caller']);
+ $this->eventLogger->start('route:run:call', 'Run callable route');
+ call_user_func($action, $parameters);
+ $this->eventLogger->end('route:run:call');
+ }
+
+ /**
* Get the url generator
*
* @return \Symfony\Component\Routing\Generator\UrlGenerator
@@ -492,7 +520,7 @@ class Router implements IRouter {
* @param string $file the route file location to include
* @param string $appName
*/
- private function requireRouteFile($file, $appName) {
+ protected function requireRouteFile(string $file, string $appName): void {
$this->setupRoutes(include $file, $appName);
}
diff --git a/lib/private/Security/Bruteforce/Throttler.php b/lib/private/Security/Bruteforce/Throttler.php
index 065f720ba72..574f6c80c3f 100644
--- a/lib/private/Security/Bruteforce/Throttler.php
+++ b/lib/private/Security/Bruteforce/Throttler.php
@@ -206,25 +206,27 @@ class Throttler implements IThrottler {
* {@inheritDoc}
*/
public function sleepDelayOrThrowOnMax(string $ip, string $action = ''): int {
- $attempts = $this->getAttempts($ip, $action, 0.5);
- if ($attempts > $this->config->getSystemValueInt('auth.bruteforce.max-attempts', self::MAX_ATTEMPTS)) {
- $this->logger->info('IP address blocked because it reached the maximum failed attempts in the last 30 minutes [action: {action}, attempts: {attempts}, ip: {ip}]', [
- 'action' => $action,
- 'ip' => $ip,
- 'attempts' => $attempts,
- ]);
- // If the ip made too many attempts within the last 30 mins we don't execute anymore
- throw new MaxDelayReached('Reached maximum delay');
- }
-
+ $maxAttempts = $this->config->getSystemValueInt('auth.bruteforce.max-attempts', self::MAX_ATTEMPTS);
$attempts = $this->getAttempts($ip, $action);
- if ($attempts > 10) {
+ if ($attempts > $maxAttempts) {
+ $attempts30mins = $this->getAttempts($ip, $action, 0.5);
+ if ($attempts30mins > $maxAttempts) {
+ $this->logger->info('IP address blocked because it reached the maximum failed attempts in the last 30 minutes [action: {action}, attempts: {attempts}, ip: {ip}]', [
+ 'action' => $action,
+ 'ip' => $ip,
+ 'attempts' => $attempts30mins,
+ ]);
+ // If the ip made too many attempts within the last 30 mins we don't execute anymore
+ throw new MaxDelayReached('Reached maximum delay');
+ }
+
$this->logger->info('IP address throttled because it reached the attempts limit in the last 12 hours [action: {action}, attempts: {attempts}, ip: {ip}]', [
'action' => $action,
'ip' => $ip,
'attempts' => $attempts,
]);
}
+
if ($attempts > 0) {
return $this->calculateDelay($attempts);
}
diff --git a/lib/private/Security/Hasher.php b/lib/private/Security/Hasher.php
index ba661f5a356..722fdab902f 100644
--- a/lib/private/Security/Hasher.php
+++ b/lib/private/Security/Hasher.php
@@ -106,8 +106,8 @@ class Hasher implements IHasher {
// Verify whether it matches a legacy PHPass or SHA1 string
$hashLength = \strlen($hash);
- if (($hashLength === 60 && password_verify($message . $this->legacySalt, $hash)) ||
- ($hashLength === 40 && hash_equals($hash, sha1($message)))) {
+ if (($hashLength === 60 && password_verify($message . $this->legacySalt, $hash))
+ || ($hashLength === 40 && hash_equals($hash, sha1($message)))) {
$newHash = $this->hash($message);
return true;
}
@@ -115,8 +115,8 @@ class Hasher implements IHasher {
// Verify whether it matches a legacy PHPass or SHA1 string
// Retry with empty passwordsalt for cases where it was not set
$hashLength = \strlen($hash);
- if (($hashLength === 60 && password_verify($message, $hash)) ||
- ($hashLength === 40 && hash_equals($hash, sha1($message)))) {
+ if (($hashLength === 60 && password_verify($message, $hash))
+ || ($hashLength === 40 && hash_equals($hash, sha1($message)))) {
$newHash = $this->hash($message);
return true;
}
diff --git a/lib/private/Security/Signature/Model/SignedRequest.php b/lib/private/Security/Signature/Model/SignedRequest.php
index f30935e83b1..12a43f32bcc 100644
--- a/lib/private/Security/Signature/Model/SignedRequest.php
+++ b/lib/private/Security/Signature/Model/SignedRequest.php
@@ -74,8 +74,8 @@ class SignedRequest implements ISignedRequest, JsonSerializable {
*/
public function getDigest(): string {
if ($this->digest === '') {
- $this->digest = $this->digestAlgorithm->value . '=' .
- base64_encode(hash($this->digestAlgorithm->getHashingAlgorithm(), $this->body, true));
+ $this->digest = $this->digestAlgorithm->value . '='
+ . base64_encode(hash($this->digestAlgorithm->getHashingAlgorithm(), $this->body, true));
}
return $this->digest;
}
diff --git a/lib/private/Security/VerificationToken/VerificationToken.php b/lib/private/Security/VerificationToken/VerificationToken.php
index 1995b482597..89f45180359 100644
--- a/lib/private/Security/VerificationToken/VerificationToken.php
+++ b/lib/private/Security/VerificationToken/VerificationToken.php
@@ -85,9 +85,9 @@ class VerificationToken implements IVerificationToken {
): string {
$token = $this->secureRandom->generate(
21,
- ISecureRandom::CHAR_DIGITS .
- ISecureRandom::CHAR_LOWER .
- ISecureRandom::CHAR_UPPER
+ ISecureRandom::CHAR_DIGITS
+ . ISecureRandom::CHAR_LOWER
+ . ISecureRandom::CHAR_UPPER
);
$tokenValue = $this->timeFactory->getTime() . ':' . $token;
$encryptedValue = $this->crypto->encrypt($tokenValue, $passwordPrefix . $this->config->getSystemValueString('secret'));
diff --git a/lib/private/Server.php b/lib/private/Server.php
index ea8c1ce3797..c78decd90cb 100644
--- a/lib/private/Server.php
+++ b/lib/private/Server.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@@ -55,6 +56,7 @@ use OC\Files\Mount\RootMountProvider;
use OC\Files\Node\HookConnector;
use OC\Files\Node\LazyRoot;
use OC\Files\Node\Root;
+use OC\Files\ObjectStore\PrimaryObjectStoreConfig;
use OC\Files\SetupManager;
use OC\Files\Storage\StorageFactory;
use OC\Files\Template\TemplateManager;
@@ -195,6 +197,7 @@ use OCP\Lock\ILockingProvider;
use OCP\Lockdown\ILockdownManager;
use OCP\Log\ILogFactory;
use OCP\Mail\IMailer;
+use OCP\OCM\ICapabilityAwareOCMProvider;
use OCP\OCM\IOCMDiscoveryService;
use OCP\OCM\IOCMProvider;
use OCP\Preview\IMimeIconProvider;
@@ -319,7 +322,7 @@ class Server extends ServerContainer implements IServerContainer {
return new Profiler($c->get(SystemConfig::class));
});
- $this->registerService(\OCP\Encryption\IManager::class, function (Server $c): Encryption\Manager {
+ $this->registerService(Encryption\Manager::class, function (Server $c): Encryption\Manager {
$view = new View();
$util = new Encryption\Util(
$view,
@@ -336,6 +339,7 @@ class Server extends ServerContainer implements IServerContainer {
new ArrayCache()
);
});
+ $this->registerAlias(\OCP\Encryption\IManager::class, Encryption\Manager::class);
$this->registerService(IFile::class, function (ContainerInterface $c) {
$util = new Encryption\Util(
@@ -568,6 +572,7 @@ class Server extends ServerContainer implements IServerContainer {
$this->registerAlias(IAppConfig::class, \OC\AppConfig::class);
$this->registerAlias(IUserConfig::class, \OC\Config\UserConfig::class);
+ $this->registerAlias(IAppManager::class, AppManager::class);
$this->registerService(IFactory::class, function (Server $c) {
return new \OC\L10N\Factory(
@@ -604,7 +609,7 @@ class Server extends ServerContainer implements IServerContainer {
$prefixClosure = function () use ($logQuery, $serverVersion): ?string {
if (!$logQuery) {
try {
- $v = \OCP\Server::get(IAppConfig::class)->getAppInstalledVersions();
+ $v = \OCP\Server::get(IAppConfig::class)->getAppInstalledVersions(true);
} catch (\Doctrine\DBAL\Exception $e) {
// Database service probably unavailable
// Probably related to https://github.com/nextcloud/server/issues/37424
@@ -619,7 +624,7 @@ class Server extends ServerContainer implements IServerContainer {
];
}
$v['core'] = implode(',', $serverVersion->getVersion());
- $version = implode(',', $v);
+ $version = implode(',', array_keys($v)) . implode(',', $v);
$instanceId = \OC_Util::getInstanceId();
$path = \OC::$SERVERROOT;
return md5($instanceId . '-' . $version . '-' . $path);
@@ -776,21 +781,6 @@ class Server extends ServerContainer implements IServerContainer {
});
$this->registerAlias(ITempManager::class, TempManager::class);
-
- $this->registerService(AppManager::class, function (ContainerInterface $c) {
- // TODO: use auto-wiring
- return new \OC\App\AppManager(
- $c->get(IUserSession::class),
- $c->get(\OCP\IConfig::class),
- $c->get(IGroupManager::class),
- $c->get(ICacheFactory::class),
- $c->get(IEventDispatcher::class),
- $c->get(LoggerInterface::class),
- $c->get(ServerVersion::class),
- );
- });
- $this->registerAlias(IAppManager::class, AppManager::class);
-
$this->registerAlias(IDateTimeZone::class, DateTimeZone::class);
$this->registerService(IDateTimeFormatter::class, function (Server $c) {
@@ -819,10 +809,11 @@ class Server extends ServerContainer implements IServerContainer {
$config = $c->get(\OCP\IConfig::class);
$logger = $c->get(LoggerInterface::class);
+ $objectStoreConfig = $c->get(PrimaryObjectStoreConfig::class);
$manager->registerProvider(new CacheMountProvider($config));
$manager->registerHomeProvider(new LocalHomeMountProvider());
- $manager->registerHomeProvider(new ObjectHomeMountProvider($config));
- $manager->registerRootProvider(new RootMountProvider($config, $c->get(LoggerInterface::class)));
+ $manager->registerHomeProvider(new ObjectHomeMountProvider($objectStoreConfig));
+ $manager->registerRootProvider(new RootMountProvider($objectStoreConfig, $config));
$manager->registerRootProvider(new ObjectStorePreviewCacheMountProvider($logger, $config));
return $manager;
@@ -1269,7 +1260,8 @@ class Server extends ServerContainer implements IServerContainer {
$this->registerAlias(IPhoneNumberUtil::class, PhoneNumberUtil::class);
- $this->registerAlias(IOCMProvider::class, OCMProvider::class);
+ $this->registerAlias(ICapabilityAwareOCMProvider::class, OCMProvider::class);
+ $this->registerDeprecatedAlias(IOCMProvider::class, OCMProvider::class);
$this->registerAlias(ISetupCheckManager::class, SetupCheckManager::class);
diff --git a/lib/private/Session/CryptoWrapper.php b/lib/private/Session/CryptoWrapper.php
index 380c699d32d..40c2ba6adf3 100644
--- a/lib/private/Session/CryptoWrapper.php
+++ b/lib/private/Session/CryptoWrapper.php
@@ -59,7 +59,7 @@ class CryptoWrapper {
[
'expires' => 0,
'path' => $webRoot,
- 'domain' => '',
+ 'domain' => \OCP\Server::get(\OCP\IConfig::class)->getSystemValueString('cookie_domain'),
'secure' => $secureCookie,
'httponly' => true,
'samesite' => 'Lax',
diff --git a/lib/private/Settings/DeclarativeManager.php b/lib/private/Settings/DeclarativeManager.php
index dea0c678f20..534b4b19984 100644
--- a/lib/private/Settings/DeclarativeManager.php
+++ b/lib/private/Settings/DeclarativeManager.php
@@ -15,6 +15,7 @@ use OCP\IAppConfig;
use OCP\IConfig;
use OCP\IGroupManager;
use OCP\IUser;
+use OCP\Security\ICrypto;
use OCP\Server;
use OCP\Settings\DeclarativeSettingsTypes;
use OCP\Settings\Events\DeclarativeSettingsGetValueEvent;
@@ -49,6 +50,7 @@ class DeclarativeManager implements IDeclarativeManager {
private IConfig $config,
private IAppConfig $appConfig,
private LoggerInterface $logger,
+ private ICrypto $crypto,
) {
}
@@ -266,7 +268,7 @@ class DeclarativeManager implements IDeclarativeManager {
$this->eventDispatcher->dispatchTyped(new DeclarativeSettingsSetValueEvent($user, $app, $formId, $fieldId, $value));
break;
case DeclarativeSettingsTypes::STORAGE_TYPE_INTERNAL:
- $this->saveInternalValue($user, $app, $fieldId, $value);
+ $this->saveInternalValue($user, $app, $formId, $fieldId, $value);
break;
default:
throw new Exception('Unknown storage type "' . $storageType . '"');
@@ -290,18 +292,52 @@ class DeclarativeManager implements IDeclarativeManager {
private function getInternalValue(IUser $user, string $app, string $formId, string $fieldId): mixed {
$sectionType = $this->getSectionType($app, $fieldId);
$defaultValue = $this->getDefaultValue($app, $formId, $fieldId);
+
+ $field = $this->getSchemaField($app, $formId, $fieldId);
+ $isSensitive = $field !== null && isset($field['sensitive']) && $field['sensitive'] === true;
+
switch ($sectionType) {
case DeclarativeSettingsTypes::SECTION_TYPE_ADMIN:
- return $this->config->getAppValue($app, $fieldId, $defaultValue);
+ $value = $this->config->getAppValue($app, $fieldId, $defaultValue);
+ break;
case DeclarativeSettingsTypes::SECTION_TYPE_PERSONAL:
- return $this->config->getUserValue($user->getUID(), $app, $fieldId, $defaultValue);
+ $value = $this->config->getUserValue($user->getUID(), $app, $fieldId, $defaultValue);
+ break;
default:
throw new Exception('Unknown section type "' . $sectionType . '"');
}
+ if ($isSensitive && $value !== '') {
+ try {
+ $value = $this->crypto->decrypt($value);
+ } catch (Exception $e) {
+ $this->logger->warning('Failed to decrypt sensitive value for field {field} in app {app}: {message}', [
+ 'field' => $fieldId,
+ 'app' => $app,
+ 'message' => $e->getMessage(),
+ ]);
+ $value = $defaultValue;
+ }
+ }
+ return $value;
}
- private function saveInternalValue(IUser $user, string $app, string $fieldId, mixed $value): void {
+ private function saveInternalValue(IUser $user, string $app, string $formId, string $fieldId, mixed $value): void {
$sectionType = $this->getSectionType($app, $fieldId);
+
+ $field = $this->getSchemaField($app, $formId, $fieldId);
+ if ($field !== null && isset($field['sensitive']) && $field['sensitive'] === true && $value !== '' && $value !== 'dummySecret') {
+ try {
+ $value = $this->crypto->encrypt($value);
+ } catch (Exception $e) {
+ $this->logger->warning('Failed to decrypt sensitive value for field {field} in app {app}: {message}', [
+ 'field' => $fieldId,
+ 'app' => $app,
+ 'message' => $e->getMessage()]
+ );
+ throw new Exception('Failed to encrypt sensitive value');
+ }
+ }
+
switch ($sectionType) {
case DeclarativeSettingsTypes::SECTION_TYPE_ADMIN:
$this->appConfig->setValueString($app, $fieldId, $value);
@@ -314,6 +350,27 @@ class DeclarativeManager implements IDeclarativeManager {
}
}
+ private function getSchemaField(string $app, string $formId, string $fieldId): ?array {
+ $form = $this->getForm($app, $formId);
+ if ($form !== null) {
+ foreach ($form->getSchema()['fields'] as $field) {
+ if ($field['id'] === $fieldId) {
+ return $field;
+ }
+ }
+ }
+ foreach ($this->appSchemas[$app] ?? [] as $schema) {
+ if ($schema['id'] === $formId) {
+ foreach ($schema['fields'] as $field) {
+ if ($field['id'] === $fieldId) {
+ return $field;
+ }
+ }
+ }
+ }
+ return null;
+ }
+
private function getDefaultValue(string $app, string $formId, string $fieldId): mixed {
foreach ($this->appSchemas[$app] as $schema) {
if ($schema['id'] === $formId) {
@@ -391,6 +448,12 @@ class DeclarativeManager implements IDeclarativeManager {
]);
return false;
}
+ if (isset($field['sensitive']) && $field['sensitive'] === true && !in_array($field['type'], [DeclarativeSettingsTypes::TEXT, DeclarativeSettingsTypes::PASSWORD])) {
+ $this->logger->warning('Declarative settings: sensitive field type is supported only for TEXT and PASSWORD types ({app}, {form_id}, {field_id})', [
+ 'app' => $appId, 'form_id' => $formId, 'field_id' => $fieldId,
+ ]);
+ return false;
+ }
if (!$this->validateField($appId, $formId, $field)) {
return false;
}
diff --git a/lib/private/Settings/Manager.php b/lib/private/Settings/Manager.php
index c96c04f34ff..78dc64c3c3f 100644
--- a/lib/private/Settings/Manager.php
+++ b/lib/private/Settings/Manager.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Settings/Section.php b/lib/private/Settings/Section.php
index 9cc6523b9ae..6cd8885d2df 100644
--- a/lib/private/Settings/Section.php
+++ b/lib/private/Settings/Section.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Setup.php b/lib/private/Setup.php
index 959797fb962..4082cd5df50 100644
--- a/lib/private/Setup.php
+++ b/lib/private/Setup.php
@@ -187,8 +187,8 @@ class Setup {
if (\OC_Util::runningOnMac()) {
$errors[] = [
'error' => $this->l10n->t(
- 'Mac OS X is not supported and %s will not work properly on this platform. ' .
- 'Use it at your own risk!',
+ 'Mac OS X is not supported and %s will not work properly on this platform. '
+ . 'Use it at your own risk!',
[$this->defaults->getProductName()]
),
'hint' => $this->l10n->t('For the best results, please consider using a GNU/Linux server instead.'),
@@ -198,8 +198,8 @@ class Setup {
if ($this->iniWrapper->getString('open_basedir') !== '' && PHP_INT_SIZE === 4) {
$errors[] = [
'error' => $this->l10n->t(
- 'It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. ' .
- 'This will lead to problems with files over 4 GB and is highly discouraged.',
+ 'It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. '
+ . 'This will lead to problems with files over 4 GB and is highly discouraged.',
[$this->defaults->getProductName()]
),
'hint' => $this->l10n->t('Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP.'),
@@ -304,11 +304,15 @@ class Setup {
$error = [];
$dbType = $options['dbtype'];
- if (empty($options['adminlogin'])) {
- $error[] = $l->t('Set an admin Login.');
- }
- if (empty($options['adminpass'])) {
- $error[] = $l->t('Set an admin password.');
+ $disableAdminUser = (bool)($options['admindisable'] ?? false);
+
+ if (!$disableAdminUser) {
+ if (empty($options['adminlogin'])) {
+ $error[] = $l->t('Set an admin Login.');
+ }
+ if (empty($options['adminpass'])) {
+ $error[] = $l->t('Set an admin password.');
+ }
}
if (empty($options['directory'])) {
$options['directory'] = \OC::$SERVERROOT . '/data';
@@ -318,8 +322,6 @@ class Setup {
$dbType = 'sqlite';
}
- $username = htmlspecialchars_decode($options['adminlogin']);
- $password = htmlspecialchars_decode($options['adminpass']);
$dataDir = htmlspecialchars_decode($options['directory']);
$class = self::$dbSetupClasses[$dbType];
@@ -375,7 +377,7 @@ class Setup {
$this->outputDebug($output, 'Configuring database');
$dbSetup->initialize($options);
try {
- $dbSetup->setupDatabase($username);
+ $dbSetup->setupDatabase();
} catch (\OC\DatabaseSetupException $e) {
$error[] = [
'error' => $e->getMessage(),
@@ -405,19 +407,22 @@ class Setup {
return $error;
}
- $this->outputDebug($output, 'Create admin account');
-
- // create the admin account and group
$user = null;
- try {
- $user = Server::get(IUserManager::class)->createUser($username, $password);
- if (!$user) {
- $error[] = "Account <$username> could not be created.";
+ if (!$disableAdminUser) {
+ $username = htmlspecialchars_decode($options['adminlogin']);
+ $password = htmlspecialchars_decode($options['adminpass']);
+ $this->outputDebug($output, 'Create admin account');
+
+ try {
+ $user = Server::get(IUserManager::class)->createUser($username, $password);
+ if (!$user) {
+ $error[] = "Account <$username> could not be created.";
+ return $error;
+ }
+ } catch (Exception $exception) {
+ $error[] = $exception->getMessage();
return $error;
}
- } catch (Exception $exception) {
- $error[] = $exception->getMessage();
- return $error;
}
$config = Server::get(IConfig::class);
@@ -432,7 +437,7 @@ class Setup {
}
$group = Server::get(IGroupManager::class)->createGroup('admin');
- if ($group instanceof IGroup) {
+ if ($user !== null && $group instanceof IGroup) {
$group->addUser($user);
}
@@ -464,26 +469,28 @@ class Setup {
$bootstrapCoordinator = Server::get(\OC\AppFramework\Bootstrap\Coordinator::class);
$bootstrapCoordinator->runInitialRegistration();
- // Create a session token for the newly created user
- // The token provider requires a working db, so it's not injected on setup
- /** @var \OC\User\Session $userSession */
- $userSession = Server::get(IUserSession::class);
- $provider = Server::get(PublicKeyTokenProvider::class);
- $userSession->setTokenProvider($provider);
- $userSession->login($username, $password);
- $user = $userSession->getUser();
- if (!$user) {
- $error[] = 'No account found in session.';
- return $error;
- }
- $userSession->createSessionToken($request, $user->getUID(), $username, $password);
+ if (!$disableAdminUser) {
+ // Create a session token for the newly created user
+ // The token provider requires a working db, so it's not injected on setup
+ /** @var \OC\User\Session $userSession */
+ $userSession = Server::get(IUserSession::class);
+ $provider = Server::get(PublicKeyTokenProvider::class);
+ $userSession->setTokenProvider($provider);
+ $userSession->login($username, $password);
+ $user = $userSession->getUser();
+ if (!$user) {
+ $error[] = 'No account found in session.';
+ return $error;
+ }
+ $userSession->createSessionToken($request, $user->getUID(), $username, $password);
- $session = $userSession->getSession();
- $session->set('last-password-confirm', Server::get(ITimeFactory::class)->getTime());
+ $session = $userSession->getSession();
+ $session->set('last-password-confirm', Server::get(ITimeFactory::class)->getTime());
- // Set email for admin
- if (!empty($options['adminemail'])) {
- $user->setSystemEMailAddress($options['adminemail']);
+ // Set email for admin
+ if (!empty($options['adminemail'])) {
+ $user->setSystemEMailAddress($options['adminemail']);
+ }
}
return $error;
diff --git a/lib/private/Setup/AbstractDatabase.php b/lib/private/Setup/AbstractDatabase.php
index dbbb587206b..ec4ce040090 100644
--- a/lib/private/Setup/AbstractDatabase.php
+++ b/lib/private/Setup/AbstractDatabase.php
@@ -127,10 +127,7 @@ abstract class AbstractDatabase {
return $connection;
}
- /**
- * @param string $username
- */
- abstract public function setupDatabase($username);
+ abstract public function setupDatabase();
public function runMigrations(?IOutput $output = null) {
if (!is_dir(\OC::$SERVERROOT . '/core/Migrations')) {
diff --git a/lib/private/Setup/MySQL.php b/lib/private/Setup/MySQL.php
index 6dd9855d851..1e2dda4c609 100644
--- a/lib/private/Setup/MySQL.php
+++ b/lib/private/Setup/MySQL.php
@@ -16,7 +16,7 @@ use OCP\Security\ISecureRandom;
class MySQL extends AbstractDatabase {
public $dbprettyname = 'MySQL/MariaDB';
- public function setupDatabase($username) {
+ public function setupDatabase() {
//check if the database user has admin right
$connection = $this->connect(['dbname' => null]);
@@ -28,7 +28,7 @@ class MySQL extends AbstractDatabase {
}
if ($this->tryCreateDbUser) {
- $this->createSpecificUser($username, new ConnectionAdapter($connection));
+ $this->createSpecificUser('oc_admin', new ConnectionAdapter($connection));
}
$this->config->setValues([
diff --git a/lib/private/Setup/OCI.php b/lib/private/Setup/OCI.php
index 47e5e5436a5..61c7f968787 100644
--- a/lib/private/Setup/OCI.php
+++ b/lib/private/Setup/OCI.php
@@ -40,7 +40,7 @@ class OCI extends AbstractDatabase {
return $errors;
}
- public function setupDatabase($username) {
+ public function setupDatabase() {
try {
$this->connect();
} catch (\Exception $e) {
diff --git a/lib/private/Setup/PostgreSQL.php b/lib/private/Setup/PostgreSQL.php
index b1cf031e876..9a686db2e54 100644
--- a/lib/private/Setup/PostgreSQL.php
+++ b/lib/private/Setup/PostgreSQL.php
@@ -16,10 +16,9 @@ class PostgreSQL extends AbstractDatabase {
public $dbprettyname = 'PostgreSQL';
/**
- * @param string $username
* @throws \OC\DatabaseSetupException
*/
- public function setupDatabase($username) {
+ public function setupDatabase() {
try {
$connection = $this->connect([
'dbname' => 'postgres'
@@ -46,7 +45,7 @@ class PostgreSQL extends AbstractDatabase {
//use the admin login data for the new database user
//add prefix to the postgresql user name to prevent collisions
- $this->dbUser = 'oc_' . strtolower($username);
+ $this->dbUser = 'oc_admin';
//create a new password so we don't need to store the admin config in the config file
$this->dbPassword = \OC::$server->get(ISecureRandom::class)->generate(30, ISecureRandom::CHAR_ALPHANUMERIC);
diff --git a/lib/private/Setup/Sqlite.php b/lib/private/Setup/Sqlite.php
index 1b90ebd5a5e..b34b1e32ede 100644
--- a/lib/private/Setup/Sqlite.php
+++ b/lib/private/Setup/Sqlite.php
@@ -45,7 +45,7 @@ class Sqlite extends AbstractDatabase {
}
}
- public function setupDatabase($username) {
+ public function setupDatabase() {
$datadir = $this->config->getValue(
'datadirectory',
\OC::$SERVERROOT . '/data'
diff --git a/lib/private/Share/Share.php b/lib/private/Share/Share.php
index 56a4c6410c5..1121d71e45f 100644
--- a/lib/private/Share/Share.php
+++ b/lib/private/Share/Share.php
@@ -133,8 +133,8 @@ class Share extends Constants {
// for file/folder shares we need to compare file_source, otherwise we compare item_source
// only group shares if they already point to the same target, otherwise the file where shared
// before grouping of shares was added. In this case we don't group them to avoid confusions
- if (($fileSharing && $item['file_source'] === $r['file_source'] && $item['file_target'] === $r['file_target']) ||
- (!$fileSharing && $item['item_source'] === $r['item_source'] && $item['item_target'] === $r['item_target'])) {
+ if (($fileSharing && $item['file_source'] === $r['file_source'] && $item['file_target'] === $r['file_target'])
+ || (!$fileSharing && $item['item_source'] === $r['item_source'] && $item['item_target'] === $r['item_target'])) {
// add the first item to the list of grouped shares
if (!isset($result[$key]['grouped'])) {
$result[$key]['grouped'][] = $result[$key];
diff --git a/lib/private/Share20/Exception/BackendError.php b/lib/private/Share20/Exception/BackendError.php
index 60f7dcc1a17..b2585367727 100644
--- a/lib/private/Share20/Exception/BackendError.php
+++ b/lib/private/Share20/Exception/BackendError.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/lib/private/Share20/Exception/InvalidShare.php b/lib/private/Share20/Exception/InvalidShare.php
index 755efdfd2cc..8756455f9d2 100644
--- a/lib/private/Share20/Exception/InvalidShare.php
+++ b/lib/private/Share20/Exception/InvalidShare.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
diff --git a/lib/private/Share20/LegacyHooks.php b/lib/private/Share20/LegacyHooks.php
index 99c2b0a9a87..3bce0b9560a 100644
--- a/lib/private/Share20/LegacyHooks.php
+++ b/lib/private/Share20/LegacyHooks.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
@@ -69,9 +70,9 @@ class LegacyHooks {
// Prepare hook
$shareType = $share->getShareType();
$sharedWith = '';
- if ($shareType === IShare::TYPE_USER ||
- $shareType === IShare::TYPE_GROUP ||
- $shareType === IShare::TYPE_REMOTE) {
+ if ($shareType === IShare::TYPE_USER
+ || $shareType === IShare::TYPE_GROUP
+ || $shareType === IShare::TYPE_REMOTE) {
$sharedWith = $share->getSharedWith();
}
diff --git a/lib/private/Share20/Manager.php b/lib/private/Share20/Manager.php
index 2104c07593a..01664c6a0a3 100644
--- a/lib/private/Share20/Manager.php
+++ b/lib/private/Share20/Manager.php
@@ -182,8 +182,8 @@ class Manager implements IManager {
}
// Cannot share with yourself
- if ($share->getShareType() === IShare::TYPE_USER &&
- $share->getSharedWith() === $share->getSharedBy()) {
+ if ($share->getShareType() === IShare::TYPE_USER
+ && $share->getSharedWith() === $share->getSharedBy()) {
throw new \InvalidArgumentException($this->l->t('Cannot share with yourself'));
}
@@ -193,8 +193,8 @@ class Manager implements IManager {
}
// And it should be a file or a folder
- if (!($share->getNode() instanceof \OCP\Files\File) &&
- !($share->getNode() instanceof \OCP\Files\Folder)) {
+ if (!($share->getNode() instanceof \OCP\Files\File)
+ && !($share->getNode() instanceof \OCP\Files\Folder)) {
throw new \InvalidArgumentException($this->l->t('Shared path must be either a file or a folder'));
}
@@ -251,8 +251,8 @@ class Manager implements IManager {
// Link shares are allowed to have no read permissions to allow upload to hidden folders
$noReadPermissionRequired = $share->getShareType() === IShare::TYPE_LINK
|| $share->getShareType() === IShare::TYPE_EMAIL;
- if (!$noReadPermissionRequired &&
- ($share->getPermissions() & \OCP\Constants::PERMISSION_READ) === 0) {
+ if (!$noReadPermissionRequired
+ && ($share->getPermissions() & \OCP\Constants::PERMISSION_READ) === 0) {
throw new \InvalidArgumentException($this->l->t('Shares need at least read permissions'));
}
@@ -561,8 +561,8 @@ class Manager implements IManager {
}
// Check if public upload is allowed
- if ($share->getNodeType() === 'folder' && !$this->shareApiLinkAllowPublicUpload() &&
- ($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE))) {
+ if ($share->getNodeType() === 'folder' && !$this->shareApiLinkAllowPublicUpload()
+ && ($share->getPermissions() & (\OCP\Constants::PERMISSION_CREATE | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_DELETE))) {
throw new \InvalidArgumentException($this->l->t('Public upload is not allowed'));
}
}
@@ -698,8 +698,8 @@ class Manager implements IManager {
}
// Cannot share with the owner
- if ($share->getShareType() === IShare::TYPE_USER &&
- $share->getSharedWith() === $share->getShareOwner()) {
+ if ($share->getShareType() === IShare::TYPE_USER
+ && $share->getSharedWith() === $share->getShareOwner()) {
throw new \InvalidArgumentException($this->l->t('Cannot share with the share owner'));
}
@@ -791,14 +791,14 @@ class Manager implements IManager {
}
// We can only change the recipient on user shares
- if ($share->getSharedWith() !== $originalShare->getSharedWith() &&
- $share->getShareType() !== IShare::TYPE_USER) {
+ if ($share->getSharedWith() !== $originalShare->getSharedWith()
+ && $share->getShareType() !== IShare::TYPE_USER) {
throw new \InvalidArgumentException($this->l->t('Can only update recipient on user shares'));
}
// Cannot share with the owner
- if ($share->getShareType() === IShare::TYPE_USER &&
- $share->getSharedWith() === $share->getShareOwner()) {
+ if ($share->getShareType() === IShare::TYPE_USER
+ && $share->getSharedWith() === $share->getShareOwner()) {
throw new \InvalidArgumentException($this->l->t('Cannot share with the share owner'));
}
@@ -949,11 +949,11 @@ class Manager implements IManager {
* @return boolean whether the password was updated or not.
*/
private function updateSharePasswordIfNeeded(IShare $share, IShare $originalShare) {
- $passwordsAreDifferent = ($share->getPassword() !== $originalShare->getPassword()) &&
- (($share->getPassword() !== null && $originalShare->getPassword() === null) ||
- ($share->getPassword() === null && $originalShare->getPassword() !== null) ||
- ($share->getPassword() !== null && $originalShare->getPassword() !== null &&
- !$this->hasher->verify($share->getPassword(), $originalShare->getPassword())));
+ $passwordsAreDifferent = ($share->getPassword() !== $originalShare->getPassword())
+ && (($share->getPassword() !== null && $originalShare->getPassword() === null)
+ || ($share->getPassword() === null && $originalShare->getPassword() !== null)
+ || ($share->getPassword() !== null && $originalShare->getPassword() !== null
+ && !$this->hasher->verify($share->getPassword(), $originalShare->getPassword())));
// Password updated.
if ($passwordsAreDifferent) {
@@ -1237,9 +1237,9 @@ class Manager implements IManager {
* @inheritdoc
*/
public function getSharesBy($userId, $shareType, $path = null, $reshares = false, $limit = 50, $offset = 0, bool $onlyValid = true) {
- if ($path !== null &&
- !($path instanceof \OCP\Files\File) &&
- !($path instanceof \OCP\Files\Folder)) {
+ if ($path !== null
+ && !($path instanceof \OCP\Files\File)
+ && !($path instanceof \OCP\Files\Folder)) {
throw new \InvalidArgumentException($this->l->t('Invalid path'));
}
@@ -1798,8 +1798,8 @@ class Manager implements IManager {
* @return bool
*/
public function shareApiLinkDefaultExpireDateEnforced() {
- return $this->shareApiLinkDefaultExpireDate() &&
- $this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes';
+ return $this->shareApiLinkDefaultExpireDate()
+ && $this->config->getAppValue('core', 'shareapi_enforce_expire_date', 'no') === 'yes';
}
@@ -1836,8 +1836,8 @@ class Manager implements IManager {
* @return bool
*/
public function shareApiInternalDefaultExpireDateEnforced(): bool {
- return $this->shareApiInternalDefaultExpireDate() &&
- $this->config->getAppValue('core', 'shareapi_enforce_internal_expire_date', 'no') === 'yes';
+ return $this->shareApiInternalDefaultExpireDate()
+ && $this->config->getAppValue('core', 'shareapi_enforce_internal_expire_date', 'no') === 'yes';
}
/**
@@ -1846,8 +1846,8 @@ class Manager implements IManager {
* @return bool
*/
public function shareApiRemoteDefaultExpireDateEnforced(): bool {
- return $this->shareApiRemoteDefaultExpireDate() &&
- $this->config->getAppValue('core', 'shareapi_enforce_remote_expire_date', 'no') === 'yes';
+ return $this->shareApiRemoteDefaultExpireDate()
+ && $this->config->getAppValue('core', 'shareapi_enforce_remote_expire_date', 'no') === 'yes';
}
/**
@@ -1915,13 +1915,13 @@ class Manager implements IManager {
}
public function limitEnumerationToGroups(): bool {
- return $this->allowEnumeration() &&
- $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
+ return $this->allowEnumeration()
+ && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_group', 'no') === 'yes';
}
public function limitEnumerationToPhone(): bool {
- return $this->allowEnumeration() &&
- $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
+ return $this->allowEnumeration()
+ && $this->config->getAppValue('core', 'shareapi_restrict_user_enumeration_to_phone', 'no') === 'yes';
}
public function allowEnumerationFullMatch(): bool {
@@ -1940,6 +1940,10 @@ class Manager implements IManager {
return $this->appConfig->getValueBool('core', 'shareapi_allow_custom_tokens', false);
}
+ public function allowViewWithoutDownload(): bool {
+ return $this->appConfig->getValueBool('core', 'shareapi_allow_view_without_download', true);
+ }
+
public function currentUserCanEnumerateTargetUser(?IUser $currentUser, IUser $targetUser): bool {
if ($this->allowEnumerationFullMatch()) {
return true;
diff --git a/lib/private/Share20/ProviderFactory.php b/lib/private/Share20/ProviderFactory.php
index eba3f4f26f1..d920edfd90e 100644
--- a/lib/private/Share20/ProviderFactory.php
+++ b/lib/private/Share20/ProviderFactory.php
@@ -174,9 +174,9 @@ class ProviderFactory implements IProviderFactory {
public function getProviderForType($shareType) {
$provider = null;
- if ($shareType === IShare::TYPE_USER ||
- $shareType === IShare::TYPE_GROUP ||
- $shareType === IShare::TYPE_LINK
+ if ($shareType === IShare::TYPE_USER
+ || $shareType === IShare::TYPE_GROUP
+ || $shareType === IShare::TYPE_LINK
) {
$provider = $this->defaultShareProvider();
} elseif ($shareType === IShare::TYPE_REMOTE || $shareType === IShare::TYPE_REMOTE_GROUP) {
diff --git a/lib/private/Share20/Share.php b/lib/private/Share20/Share.php
index 466817efc9a..8caabb0898a 100644
--- a/lib/private/Share20/Share.php
+++ b/lib/private/Share20/Share.php
@@ -14,8 +14,10 @@ use OCP\Files\IRootFolder;
use OCP\Files\Node;
use OCP\Files\NotFoundException;
use OCP\IUserManager;
+use OCP\Server;
use OCP\Share\Exceptions\IllegalIDChangeException;
use OCP\Share\IAttributes;
+use OCP\Share\IManager;
use OCP\Share\IShare;
class Share implements IShare {
@@ -418,8 +420,8 @@ class Share implements IShare {
* @inheritdoc
*/
public function isExpired() {
- return $this->getExpirationDate() !== null &&
- $this->getExpirationDate() <= new \DateTime();
+ return $this->getExpirationDate() !== null
+ && $this->getExpirationDate() <= new \DateTime();
}
/**
@@ -622,4 +624,23 @@ class Share implements IShare {
public function getReminderSent(): bool {
return $this->reminderSent;
}
+
+ public function canSeeContent(): bool {
+ $shareManager = Server::get(IManager::class);
+
+ $allowViewWithoutDownload = $shareManager->allowViewWithoutDownload();
+ // If the share manager allows viewing without download, we can always see the content.
+ if ($allowViewWithoutDownload) {
+ return true;
+ }
+
+ // No "allow preview" header set, so we must check if
+ // the share has not explicitly disabled download permissions
+ $attributes = $this->getAttributes();
+ if ($attributes?->getAttribute('permissions', 'download') === false) {
+ return false;
+ }
+
+ return true;
+ }
}
diff --git a/lib/private/Share20/ShareAttributes.php b/lib/private/Share20/ShareAttributes.php
index 96da1e336e3..f90fbd9c6cd 100644
--- a/lib/private/Share20/ShareAttributes.php
+++ b/lib/private/Share20/ShareAttributes.php
@@ -32,8 +32,8 @@ class ShareAttributes implements IAttributes {
* @inheritdoc
*/
public function getAttribute(string $scope, string $key): mixed {
- if (\array_key_exists($scope, $this->attributes) &&
- \array_key_exists($key, $this->attributes[$scope])) {
+ if (\array_key_exists($scope, $this->attributes)
+ && \array_key_exists($key, $this->attributes[$scope])) {
return $this->attributes[$scope][$key];
}
return null;
diff --git a/lib/private/Share20/ShareHelper.php b/lib/private/Share20/ShareHelper.php
index d4a54f1d687..3f6bab98a7f 100644
--- a/lib/private/Share20/ShareHelper.php
+++ b/lib/private/Share20/ShareHelper.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Support/Subscription/Registry.php b/lib/private/Support/Subscription/Registry.php
index 5eed6885f78..34f24d1d026 100644
--- a/lib/private/Support/Subscription/Registry.php
+++ b/lib/private/Support/Subscription/Registry.php
@@ -138,8 +138,8 @@ class Registry implements IRegistry {
*/
public function delegateIsHardUserLimitReached(?IManager $notificationManager = null): bool {
$subscription = $this->getSubscription();
- if ($subscription instanceof ISubscription &&
- $subscription->hasValidSubscription()) {
+ if ($subscription instanceof ISubscription
+ && $subscription->hasValidSubscription()) {
$userLimitReached = $subscription->isHardUserLimitReached();
if ($userLimitReached && $notificationManager instanceof IManager) {
$this->notifyAboutReachedUserLimit($notificationManager);
diff --git a/lib/private/TaskProcessing/SynchronousBackgroundJob.php b/lib/private/TaskProcessing/SynchronousBackgroundJob.php
index de3b424176c..19c53d59932 100644
--- a/lib/private/TaskProcessing/SynchronousBackgroundJob.php
+++ b/lib/private/TaskProcessing/SynchronousBackgroundJob.php
@@ -59,8 +59,8 @@ class SynchronousBackgroundJob extends QueuedJob {
// check if this job needs to be scheduled again:
// if there is at least one preferred synchronous provider that has a scheduled task
- $synchronousProviders = array_filter($providers, fn ($provider) =>
- $provider instanceof ISynchronousProvider);
+ $synchronousProviders = array_filter($providers, fn ($provider)
+ => $provider instanceof ISynchronousProvider);
$synchronousPreferredProviders = array_filter($synchronousProviders, function ($provider) {
$taskTypeId = $provider->getTaskTypeId();
$preferredProvider = $this->taskProcessingManager->getPreferredProvider($taskTypeId);
diff --git a/lib/private/Teams/TeamManager.php b/lib/private/Teams/TeamManager.php
index d75b0209c71..13d6cc459a9 100644
--- a/lib/private/Teams/TeamManager.php
+++ b/lib/private/Teams/TeamManager.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/Template/JSCombiner.php b/lib/private/Template/JSCombiner.php
index 5fce3effb3f..a94f822a448 100644
--- a/lib/private/Template/JSCombiner.php
+++ b/lib/private/Template/JSCombiner.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-License-Identifier: AGPL-3.0-or-later
diff --git a/lib/private/TemplateLayout.php b/lib/private/TemplateLayout.php
index caffbfceefa..cfc387d2164 100644
--- a/lib/private/TemplateLayout.php
+++ b/lib/private/TemplateLayout.php
@@ -201,7 +201,7 @@ class TemplateLayout {
if ($this->config->getSystemValueBool('installed', false)) {
if (empty(self::$versionHash)) {
- $v = $this->appManager->getAppInstalledVersions();
+ $v = $this->appManager->getAppInstalledVersions(true);
$v['core'] = implode('.', $this->serverVersion->getVersion());
self::$versionHash = substr(md5(implode(',', $v)), 0, 8);
}
diff --git a/lib/private/URLGenerator.php b/lib/private/URLGenerator.php
index c78ecac0903..1a2978b84d7 100644
--- a/lib/private/URLGenerator.php
+++ b/lib/private/URLGenerator.php
@@ -189,14 +189,14 @@ class URLGenerator implements IURLGenerator {
$basename = substr(basename($file), 0, -4);
try {
- $appPath = $this->getAppManager()->getAppPath($appName);
- } catch (AppPathNotFoundException $e) {
if ($appName === 'core' || $appName === '') {
$appName = 'core';
$appPath = false;
} else {
- throw new RuntimeException('image not found: image: ' . $file . ' webroot: ' . \OC::$WEBROOT . ' serverroot: ' . \OC::$SERVERROOT);
+ $appPath = $this->getAppManager()->getAppPath($appName);
}
+ } catch (AppPathNotFoundException $e) {
+ throw new RuntimeException('image not found: image: ' . $file . ' webroot: ' . \OC::$WEBROOT . ' serverroot: ' . \OC::$SERVERROOT);
}
// Check if the app is in the app folder
diff --git a/lib/private/Updater.php b/lib/private/Updater.php
index 7707a310d99..6495bad2da2 100644
--- a/lib/private/Updater.php
+++ b/lib/private/Updater.php
@@ -167,8 +167,8 @@ class Updater extends BasicEmitter {
// Vendor was not set correctly on install, so we have to white-list known versions
if ($currentVendor === '' && (
- isset($allowedPreviousVersions['owncloud'][$oldVersion]) ||
- isset($allowedPreviousVersions['owncloud'][$majorMinor])
+ isset($allowedPreviousVersions['owncloud'][$oldVersion])
+ || isset($allowedPreviousVersions['owncloud'][$majorMinor])
)) {
$currentVendor = 'owncloud';
$this->config->setAppValue('core', 'vendor', $currentVendor);
@@ -176,13 +176,13 @@ class Updater extends BasicEmitter {
if ($currentVendor === 'nextcloud') {
return isset($allowedPreviousVersions[$currentVendor][$majorMinor])
- && (version_compare($oldVersion, $newVersion, '<=') ||
- $this->config->getSystemValueBool('debug', false));
+ && (version_compare($oldVersion, $newVersion, '<=')
+ || $this->config->getSystemValueBool('debug', false));
}
// Check if the instance can be migrated
- return isset($allowedPreviousVersions[$currentVendor][$majorMinor]) ||
- isset($allowedPreviousVersions[$currentVendor][$oldVersion]);
+ return isset($allowedPreviousVersions[$currentVendor][$majorMinor])
+ || isset($allowedPreviousVersions[$currentVendor][$oldVersion]);
}
/**
diff --git a/lib/private/Updater/VersionCheck.php b/lib/private/Updater/VersionCheck.php
index 53bfc0d5d5f..be410b06c3e 100644
--- a/lib/private/Updater/VersionCheck.php
+++ b/lib/private/Updater/VersionCheck.php
@@ -105,17 +105,20 @@ class VersionCheck {
}
/**
- * @codeCoverageIgnore
- * @param string $url
- * @return resource|string
* @throws \Exception
*/
- protected function getUrlContent($url) {
- $client = $this->clientService->newClient();
- $response = $client->get($url, [
+ protected function getUrlContent(string $url): string {
+ $response = $this->clientService->newClient()->get($url, [
'timeout' => 5,
]);
- return $response->getBody();
+
+ $content = $response->getBody();
+
+ // IResponse.getBody responds with null|resource if returning a stream response was requested.
+ // As that's not the case here, we can just ignore the psalm warning by adding an assertion.
+ assert(is_string($content));
+
+ return $content;
}
private function computeCategory(): int {
diff --git a/lib/private/User/LazyUser.php b/lib/private/User/LazyUser.php
index 715265f6a39..501169019d4 100644
--- a/lib/private/User/LazyUser.php
+++ b/lib/private/User/LazyUser.php
@@ -160,6 +160,10 @@ class LazyUser implements IUser {
return $this->getUser()->getQuota();
}
+ public function getQuotaBytes(): int|float {
+ return $this->getUser()->getQuotaBytes();
+ }
+
public function setQuota($quota) {
$this->getUser()->setQuota($quota);
}
diff --git a/lib/private/User/Listeners/UserChangedListener.php b/lib/private/User/Listeners/UserChangedListener.php
index 983a4e81233..8f618950255 100644
--- a/lib/private/User/Listeners/UserChangedListener.php
+++ b/lib/private/User/Listeners/UserChangedListener.php
@@ -28,7 +28,7 @@ class UserChangedListener implements IEventListener {
if (!($event instanceof UserChangedEvent)) {
return;
}
-
+
$user = $event->getUser();
$feature = $event->getFeature();
$oldValue = $event->getOldValue();
diff --git a/lib/private/User/Manager.php b/lib/private/User/Manager.php
index ca5d90f8c00..097fd9a0dc8 100644
--- a/lib/private/User/Manager.php
+++ b/lib/private/User/Manager.php
@@ -320,9 +320,9 @@ class Manager extends PublicEmitter implements IUserManager {
$users,
function (IUser $user) use ($search): bool {
try {
- return mb_stripos($user->getUID(), $search) !== false ||
- mb_stripos($user->getDisplayName(), $search) !== false ||
- mb_stripos($user->getEMailAddress() ?? '', $search) !== false;
+ return mb_stripos($user->getUID(), $search) !== false
+ || mb_stripos($user->getDisplayName(), $search) !== false
+ || mb_stripos($user->getEMailAddress() ?? '', $search) !== false;
} catch (NoUserException $ex) {
$this->logger->error('Error while filtering disabled users', ['exception' => $ex, 'userUID' => $user->getUID()]);
return false;
@@ -724,7 +724,8 @@ class Manager extends PublicEmitter implements IUserManager {
// User ID is too long
if (strlen($uid) > IUser::MAX_USERID_LENGTH) {
- throw new \InvalidArgumentException($l->t('Login is too long'));
+ // TRANSLATORS User ID is too long
+ throw new \InvalidArgumentException($l->t('Username is too long'));
}
if (!$this->verifyUid($uid, $checkDataDirectory)) {
diff --git a/lib/private/User/Session.php b/lib/private/User/Session.php
index 27570822ef2..95e4b6e4a87 100644
--- a/lib/private/User/Session.php
+++ b/lib/private/User/Session.php
@@ -1,4 +1,5 @@
<?php
+
/**
* SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors
* SPDX-FileCopyrightText: 2016 ownCloud, Inc.
@@ -967,6 +968,7 @@ class Session implements IUserSession, Emitter {
if ($webRoot === '') {
$webRoot = '/';
}
+ $domain = $this->config->getSystemValueString('cookie_domain');
$maxAge = $this->config->getSystemValueInt('remember_login_cookie_lifetime', 60 * 60 * 24 * 15);
\OC\Http\CookieHelper::setCookie(
@@ -974,7 +976,7 @@ class Session implements IUserSession, Emitter {
$username,
$maxAge,
$webRoot,
- '',
+ $domain,
$secureCookie,
true,
\OC\Http\CookieHelper::SAMESITE_LAX
@@ -984,7 +986,7 @@ class Session implements IUserSession, Emitter {
$token,
$maxAge,
$webRoot,
- '',
+ $domain,
$secureCookie,
true,
\OC\Http\CookieHelper::SAMESITE_LAX
@@ -995,7 +997,7 @@ class Session implements IUserSession, Emitter {
$this->session->getId(),
$maxAge,
$webRoot,
- '',
+ $domain,
$secureCookie,
true,
\OC\Http\CookieHelper::SAMESITE_LAX
@@ -1011,18 +1013,19 @@ class Session implements IUserSession, Emitter {
public function unsetMagicInCookie() {
//TODO: DI for cookies and IRequest
$secureCookie = OC::$server->getRequest()->getServerProtocol() === 'https';
+ $domain = $this->config->getSystemValueString('cookie_domain');
unset($_COOKIE['nc_username']); //TODO: DI
unset($_COOKIE['nc_token']);
unset($_COOKIE['nc_session_id']);
- setcookie('nc_username', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT, '', $secureCookie, true);
- setcookie('nc_token', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT, '', $secureCookie, true);
- setcookie('nc_session_id', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT, '', $secureCookie, true);
+ setcookie('nc_username', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT, $domain, $secureCookie, true);
+ setcookie('nc_token', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT, $domain, $secureCookie, true);
+ setcookie('nc_session_id', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT, $domain, $secureCookie, true);
// old cookies might be stored under /webroot/ instead of /webroot
// and Firefox doesn't like it!
- setcookie('nc_username', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true);
- setcookie('nc_token', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true);
- setcookie('nc_session_id', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT . '/', '', $secureCookie, true);
+ setcookie('nc_username', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT . '/', $domain, $secureCookie, true);
+ setcookie('nc_token', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT . '/', $domain, $secureCookie, true);
+ setcookie('nc_session_id', '', $this->timeFactory->getTime() - 3600, OC::$WEBROOT . '/', $domain, $secureCookie, true);
}
/**
diff --git a/lib/private/User/User.php b/lib/private/User/User.php
index 8e01a15695c..88ed0d44387 100644
--- a/lib/private/User/User.php
+++ b/lib/private/User/User.php
@@ -558,6 +558,19 @@ class User implements IUser {
return $quota;
}
+ public function getQuotaBytes(): int|float {
+ $quota = $this->getQuota();
+ if ($quota === 'none') {
+ return \OCP\Files\FileInfo::SPACE_UNLIMITED;
+ }
+
+ $bytes = \OCP\Util::computerFileSize($quota);
+ if ($bytes === false) {
+ return \OCP\Files\FileInfo::SPACE_UNKNOWN;
+ }
+ return $bytes;
+ }
+
/**
* set the users' quota
*
diff --git a/lib/private/legacy/OC_App.php b/lib/private/legacy/OC_App.php
index abac0d2635e..24982ab9e80 100644
--- a/lib/private/legacy/OC_App.php
+++ b/lib/private/legacy/OC_App.php
@@ -9,6 +9,7 @@ declare(strict_types=1);
use OC\App\DependencyAnalyzer;
use OC\App\Platform;
use OC\AppFramework\Bootstrap\Coordinator;
+use OC\Config\ConfigManager;
use OC\DB\MigrationService;
use OC\Installer;
use OC\Repair;
@@ -211,7 +212,7 @@ class OC_App {
array $groups = []) {
// Check if app is already downloaded
/** @var Installer $installer */
- $installer = \OCP\Server::get(Installer::class);
+ $installer = Server::get(Installer::class);
$isDownloaded = $installer->isDownloaded($appId);
if (!$isDownloaded) {
@@ -246,7 +247,7 @@ class OC_App {
}
}
- \OCP\Server::get(LoggerInterface::class)->error('No application directories are marked as writable.', ['app' => 'core']);
+ Server::get(LoggerInterface::class)->error('No application directories are marked as writable.', ['app' => 'core']);
return null;
}
@@ -310,12 +311,14 @@ class OC_App {
* @param string $appId
* @param bool $refreshAppPath should be set to true only during install/upgrade
* @return string|false
- * @deprecated 11.0.0 use \OCP\Server::get(IAppManager)->getAppPath()
+ * @deprecated 11.0.0 use Server::get(IAppManager)->getAppPath()
*/
public static function getAppPath(string $appId, bool $refreshAppPath = false) {
$appId = self::cleanAppId($appId);
if ($appId === '') {
return false;
+ } elseif ($appId === 'core') {
+ return __DIR__ . '/../../../core';
}
if (($dir = self::findAppInDirectories($appId, $refreshAppPath)) != false) {
@@ -347,7 +350,7 @@ class OC_App {
*/
public static function getAppVersionByPath(string $path): string {
$infoFile = $path . '/appinfo/info.xml';
- $appData = \OCP\Server::get(IAppManager::class)->getAppInfoByPath($infoFile);
+ $appData = Server::get(IAppManager::class)->getAppInfoByPath($infoFile);
return $appData['version'] ?? '';
}
@@ -389,7 +392,7 @@ class OC_App {
* @deprecated 20.0.0 Please register your alternative login option using the registerAlternativeLogin() on the RegistrationContext in your Application class implementing the OCP\Authentication\IAlternativeLogin interface
*/
public static function registerLogIn(array $entry) {
- \OCP\Server::get(LoggerInterface::class)->debug('OC_App::registerLogIn() is deprecated, please register your alternative login option using the registerAlternativeLogin() on the RegistrationContext in your Application class implementing the OCP\Authentication\IAlternativeLogin interface');
+ Server::get(LoggerInterface::class)->debug('OC_App::registerLogIn() is deprecated, please register your alternative login option using the registerAlternativeLogin() on the RegistrationContext in your Application class implementing the OCP\Authentication\IAlternativeLogin interface');
self::$altLogin[] = $entry;
}
@@ -398,11 +401,11 @@ class OC_App {
*/
public static function getAlternativeLogIns(): array {
/** @var Coordinator $bootstrapCoordinator */
- $bootstrapCoordinator = \OCP\Server::get(Coordinator::class);
+ $bootstrapCoordinator = Server::get(Coordinator::class);
foreach ($bootstrapCoordinator->getRegistrationContext()->getAlternativeLogins() as $registration) {
if (!in_array(IAlternativeLogin::class, class_implements($registration->getService()), true)) {
- \OCP\Server::get(LoggerInterface::class)->error('Alternative login option {option} does not implement {interface} and is therefore ignored.', [
+ Server::get(LoggerInterface::class)->error('Alternative login option {option} does not implement {interface} and is therefore ignored.', [
'option' => $registration->getService(),
'interface' => IAlternativeLogin::class,
'app' => $registration->getAppId(),
@@ -412,9 +415,9 @@ class OC_App {
try {
/** @var IAlternativeLogin $provider */
- $provider = \OCP\Server::get($registration->getService());
+ $provider = Server::get($registration->getService());
} catch (ContainerExceptionInterface $e) {
- \OCP\Server::get(LoggerInterface::class)->error('Alternative login option {option} can not be initialized.',
+ Server::get(LoggerInterface::class)->error('Alternative login option {option} can not be initialized.',
[
'exception' => $e,
'option' => $registration->getService(),
@@ -431,7 +434,7 @@ class OC_App {
'class' => $provider->getClass(),
];
} catch (Throwable $e) {
- \OCP\Server::get(LoggerInterface::class)->error('Alternative login option {option} had an error while loading.',
+ Server::get(LoggerInterface::class)->error('Alternative login option {option} had an error while loading.',
[
'exception' => $e,
'option' => $registration->getService(),
@@ -450,7 +453,7 @@ class OC_App {
* @deprecated 31.0.0 Use IAppManager::getAllAppsInAppsFolders instead
*/
public static function getAllApps(): array {
- return \OCP\Server::get(IAppManager::class)->getAllAppsInAppsFolders();
+ return Server::get(IAppManager::class)->getAllAppsInAppsFolders();
}
/**
@@ -459,7 +462,7 @@ class OC_App {
* @deprecated 32.0.0 Use \OCP\Support\Subscription\IRegistry::delegateGetSupportedApps instead
*/
public function getSupportedApps(): array {
- $subscriptionRegistry = \OCP\Server::get(\OCP\Support\Subscription\IRegistry::class);
+ $subscriptionRegistry = Server::get(\OCP\Support\Subscription\IRegistry::class);
$supportedApps = $subscriptionRegistry->delegateGetSupportedApps();
return $supportedApps;
}
@@ -484,12 +487,12 @@ class OC_App {
if (!in_array($app, $blacklist)) {
$info = $appManager->getAppInfo($app, false, $langCode);
if (!is_array($info)) {
- \OCP\Server::get(LoggerInterface::class)->error('Could not read app info file for app "' . $app . '"', ['app' => 'core']);
+ Server::get(LoggerInterface::class)->error('Could not read app info file for app "' . $app . '"', ['app' => 'core']);
continue;
}
if (!isset($info['name'])) {
- \OCP\Server::get(LoggerInterface::class)->error('App id "' . $app . '" has no name in appinfo', ['app' => 'core']);
+ Server::get(LoggerInterface::class)->error('App id "' . $app . '" has no name in appinfo', ['app' => 'core']);
continue;
}
@@ -556,7 +559,7 @@ class OC_App {
public static function shouldUpgrade(string $app): bool {
$versions = self::getAppVersions();
- $currentVersion = \OCP\Server::get(\OCP\App\IAppManager::class)->getAppVersion($app);
+ $currentVersion = Server::get(\OCP\App\IAppManager::class)->getAppVersion($app);
if ($currentVersion && isset($versions[$app])) {
$installedVersion = $versions[$app];
if (!version_compare($currentVersion, $installedVersion, '=')) {
@@ -645,7 +648,7 @@ class OC_App {
* @deprecated 32.0.0 Use IAppManager::getAppInstalledVersions or IAppConfig::getAppInstalledVersions instead
*/
public static function getAppVersions(): array {
- return \OCP\Server::get(IAppConfig::class)->getAppInstalledVersions();
+ return Server::get(IAppConfig::class)->getAppInstalledVersions();
}
/**
@@ -663,13 +666,13 @@ class OC_App {
}
if (is_file($appPath . '/appinfo/database.xml')) {
- \OCP\Server::get(LoggerInterface::class)->error('The appinfo/database.xml file is not longer supported. Used in ' . $appId);
+ Server::get(LoggerInterface::class)->error('The appinfo/database.xml file is not longer supported. Used in ' . $appId);
return false;
}
\OC::$server->getAppManager()->clearAppsCache();
$l = \OC::$server->getL10N('core');
- $appData = \OCP\Server::get(\OCP\App\IAppManager::class)->getAppInfo($appId, false, $l->getLanguageCode());
+ $appData = Server::get(\OCP\App\IAppManager::class)->getAppInfo($appId, false, $l->getLanguageCode());
$ignoreMaxApps = \OC::$server->getConfig()->getSystemValue('app_install_overwrite', []);
$ignoreMax = in_array($appId, $ignoreMaxApps, true);
@@ -709,9 +712,13 @@ class OC_App {
self::setAppTypes($appId);
- $version = \OCP\Server::get(\OCP\App\IAppManager::class)->getAppVersion($appId);
+ $version = Server::get(\OCP\App\IAppManager::class)->getAppVersion($appId);
\OC::$server->getConfig()->setAppValue($appId, 'installed_version', $version);
+ // migrate eventual new config keys in the process
+ /** @psalm-suppress InternalMethod */
+ Server::get(ConfigManager::class)->migrateConfigLexiconKeys($appId);
+
\OC::$server->get(IEventDispatcher::class)->dispatchTyped(new AppUpdateEvent($appId));
\OC::$server->get(IEventDispatcher::class)->dispatch(ManagerEvent::EVENT_APP_UPDATE, new ManagerEvent(
ManagerEvent::EVENT_APP_UPDATE, $appId
diff --git a/lib/private/legacy/OC_Defaults.php b/lib/private/legacy/OC_Defaults.php
index f7015a1863a..0d460ff966d 100644
--- a/lib/private/legacy/OC_Defaults.php
+++ b/lib/private/legacy/OC_Defaults.php
@@ -228,9 +228,9 @@ class OC_Defaults {
if ($this->themeExist('getShortFooter')) {
$footer = $this->theme->getShortFooter();
} else {
- $footer = '<a href="' . $this->getBaseUrl() . '" target="_blank"' .
- ' rel="noreferrer noopener">' . $this->getEntity() . '</a>' .
- ' – ' . $this->getSlogan();
+ $footer = '<a href="' . $this->getBaseUrl() . '" target="_blank"'
+ . ' rel="noreferrer noopener">' . $this->getEntity() . '</a>'
+ . ' – ' . $this->getSlogan();
}
return $footer;
diff --git a/lib/private/legacy/OC_Helper.php b/lib/private/legacy/OC_Helper.php
index 87c820dcd53..4388f775623 100644
--- a/lib/private/legacy/OC_Helper.php
+++ b/lib/private/legacy/OC_Helper.php
@@ -272,7 +272,7 @@ class OC_Helper {
} else {
$user = \OC::$server->getUserSession()->getUser();
}
- $quota = OC_Util::getUserQuota($user);
+ $quota = $user?->getQuotaBytes() ?? \OCP\Files\FileInfo::SPACE_UNKNOWN;
if ($quota !== \OCP\Files\FileInfo::SPACE_UNLIMITED) {
// always get free space / total space from root + mount points
return self::getGlobalStorageInfo($quota, $user, $mount);
diff --git a/lib/private/legacy/OC_Response.php b/lib/private/legacy/OC_Response.php
index 86274f5fcb7..c45852b4b1d 100644
--- a/lib/private/legacy/OC_Response.php
+++ b/lib/private/legacy/OC_Response.php
@@ -78,7 +78,6 @@ class OC_Response {
header('X-Frame-Options: SAMEORIGIN'); // Disallow iFraming from other domains
header('X-Permitted-Cross-Domain-Policies: none'); // https://www.adobe.com/devnet/adobe-media-server/articles/cross-domain-xml-for-streaming.html
header('X-Robots-Tag: noindex, nofollow'); // https://developers.google.com/webmasters/control-crawl-index/docs/robots_meta_tag
- header('X-XSS-Protection: 1; mode=block'); // Enforce browser based XSS filters
}
}
}
diff --git a/lib/private/legacy/OC_Util.php b/lib/private/legacy/OC_Util.php
index 9444da4f36d..948dfcf7926 100644
--- a/lib/private/legacy/OC_Util.php
+++ b/lib/private/legacy/OC_Util.php
@@ -98,7 +98,7 @@ class OC_Util {
*
* @param IUser|null $user
* @return int|\OCP\Files\FileInfo::SPACE_UNLIMITED|false|float Quota bytes
- * @deprecated 9.0.0 - Use \OCP\IUser::getQuota
+ * @deprecated 9.0.0 - Use \OCP\IUser::getQuota or \OCP\IUser::getQuotaBytes
*/
public static function getUserQuota(?IUser $user) {
if (is_null($user)) {
@@ -187,14 +187,13 @@ class OC_Util {
$child = $target->newFolder($file);
self::copyr($source . '/' . $file, $child);
} else {
- $child = $target->newFile($file);
$sourceStream = fopen($source . '/' . $file, 'r');
if ($sourceStream === false) {
$logger->error(sprintf('Could not fopen "%s"', $source . '/' . $file), ['app' => 'core']);
closedir($dir);
return;
}
- $child->putContent($sourceStream);
+ $target->newFile($file, $sourceStream);
}
}
}
@@ -480,8 +479,8 @@ class OC_Util {
* TODO: Should probably be implemented in the above generic dependency
* check somehow in the long-term.
*/
- if ($iniWrapper->getBool('mbstring.func_overload') !== null &&
- $iniWrapper->getBool('mbstring.func_overload') === true) {
+ if ($iniWrapper->getBool('mbstring.func_overload') !== null
+ && $iniWrapper->getBool('mbstring.func_overload') === true) {
$errors[] = [
'error' => $l->t('<code>mbstring.func_overload</code> is set to <code>%s</code> instead of the expected value <code>0</code>.', [$iniWrapper->getString('mbstring.func_overload')]),
'hint' => $l->t('To fix this issue set <code>mbstring.func_overload</code> to <code>0</code> in your php.ini.')