diff options
Diffstat (limited to 'lib/private/Repair')
56 files changed, 1597 insertions, 1279 deletions
diff --git a/lib/private/Repair/AddAppConfigLazyMigration.php b/lib/private/Repair/AddAppConfigLazyMigration.php new file mode 100644 index 00000000000..7ae83e87669 --- /dev/null +++ b/lib/private/Repair/AddAppConfigLazyMigration.php @@ -0,0 +1,45 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Repair; + +use OCP\IAppConfig; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; +use Psr\Log\LoggerInterface; + +class AddAppConfigLazyMigration implements IRepairStep { + /** + * Just add config values that needs to be migrated to lazy loading + */ + private static array $lazyAppConfig = [ + 'core' => [ + 'oc.integritycheck.checker', + ], + ]; + + public function __construct( + private IAppConfig $appConfig, + private LoggerInterface $logger, + ) { + } + + public function getName() { + return 'migrate lazy config values'; + } + + public function run(IOutput $output) { + $c = 0; + foreach (self::$lazyAppConfig as $appId => $configKeys) { + foreach ($configKeys as $configKey) { + $c += (int)$this->appConfig->updateLazy($appId, $configKey, true); + } + } + + $this->logger->notice('core/BackgroundJobs/AppConfigLazyMigration: ' . $c . ' config values updated'); + } +} diff --git a/lib/private/Repair/AddBruteForceCleanupJob.php b/lib/private/Repair/AddBruteForceCleanupJob.php index b287b4baa2f..dd08e36a597 100644 --- a/lib/private/Repair/AddBruteForceCleanupJob.php +++ b/lib/private/Repair/AddBruteForceCleanupJob.php @@ -3,27 +3,9 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\Repair; use OC\Security\Bruteforce\CleanupJob; @@ -32,7 +14,6 @@ use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; class AddBruteForceCleanupJob implements IRepairStep { - /** @var IJobList */ protected $jobList; diff --git a/lib/private/Repair/AddCleanupDeletedUsersBackgroundJob.php b/lib/private/Repair/AddCleanupDeletedUsersBackgroundJob.php new file mode 100644 index 00000000000..9713d8595e7 --- /dev/null +++ b/lib/private/Repair/AddCleanupDeletedUsersBackgroundJob.php @@ -0,0 +1,30 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Repair; + +use OC\User\BackgroundJobs\CleanupDeletedUsers; +use OCP\BackgroundJob\IJobList; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; + +class AddCleanupDeletedUsersBackgroundJob implements IRepairStep { + private IJobList $jobList; + + public function __construct(IJobList $jobList) { + $this->jobList = $jobList; + } + + public function getName(): string { + return 'Add cleanup-deleted-users background job'; + } + + public function run(IOutput $output) { + $this->jobList->add(CleanupDeletedUsers::class); + } +} diff --git a/lib/private/Repair/AddCleanupUpdaterBackupsJob.php b/lib/private/Repair/AddCleanupUpdaterBackupsJob.php index 44c1942ec5d..e631a3303f1 100644 --- a/lib/private/Repair/AddCleanupUpdaterBackupsJob.php +++ b/lib/private/Repair/AddCleanupUpdaterBackupsJob.php @@ -1,26 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2018 Morris Jobke <hey@morrisjobke.de> - * - * @author Morris Jobke <hey@morrisjobke.de> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\Repair; use OC\Core\BackgroundJobs\BackgroundCleanupUpdaterBackupsJob; @@ -29,7 +12,6 @@ use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; class AddCleanupUpdaterBackupsJob implements IRepairStep { - /** @var IJobList */ protected $jobList; diff --git a/lib/private/Repair/AddMetadataGenerationJob.php b/lib/private/Repair/AddMetadataGenerationJob.php new file mode 100644 index 00000000000..76c60f303a7 --- /dev/null +++ b/lib/private/Repair/AddMetadataGenerationJob.php @@ -0,0 +1,27 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Repair; + +use OC\Core\BackgroundJobs\GenerateMetadataJob; +use OCP\BackgroundJob\IJobList; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; + +class AddMetadataGenerationJob implements IRepairStep { + public function __construct( + private IJobList $jobList, + ) { + } + + public function getName() { + return 'Queue a job to generate metadata'; + } + + public function run(IOutput $output) { + $this->jobList->add(GenerateMetadataJob::class); + } +} diff --git a/lib/private/Repair/AddRemoveOldTasksBackgroundJob.php b/lib/private/Repair/AddRemoveOldTasksBackgroundJob.php new file mode 100644 index 00000000000..4ad320a0311 --- /dev/null +++ b/lib/private/Repair/AddRemoveOldTasksBackgroundJob.php @@ -0,0 +1,34 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2023 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Repair; + +use OC\TaskProcessing\RemoveOldTasksBackgroundJob; +use OC\TextProcessing\RemoveOldTasksBackgroundJob as RemoveOldTextProcessingTasksBackgroundJob; +use OC\TextToImage\RemoveOldTasksBackgroundJob as RemoveOldTextToImageTasksBackgroundJob; +use OCP\BackgroundJob\IJobList; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; + +class AddRemoveOldTasksBackgroundJob implements IRepairStep { + private IJobList $jobList; + + public function __construct(IJobList $jobList) { + $this->jobList = $jobList; + } + + public function getName(): string { + return 'Add AI tasks cleanup jobs'; + } + + public function run(IOutput $output) { + $this->jobList->add(RemoveOldTextProcessingTasksBackgroundJob::class); + $this->jobList->add(RemoveOldTextToImageTasksBackgroundJob::class); + $this->jobList->add(RemoveOldTasksBackgroundJob::class); + } +} diff --git a/lib/private/Repair/CleanTags.php b/lib/private/Repair/CleanTags.php index 5b8e44f37b6..ad8fa6235e6 100644 --- a/lib/private/Repair/CleanTags.php +++ b/lib/private/Repair/CleanTags.php @@ -1,30 +1,10 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OC\Repair; use OCP\DB\QueryBuilder\IQueryBuilder; @@ -40,21 +20,16 @@ use OCP\Migration\IRepairStep; */ class CleanTags implements IRepairStep { - /** @var IDBConnection */ - protected $connection; - - /** @var IUserManager */ - protected $userManager; - protected $deletedTags = 0; /** * @param IDBConnection $connection * @param IUserManager $userManager */ - public function __construct(IDBConnection $connection, IUserManager $userManager) { - $this->connection = $connection; - $this->userManager = $userManager; + public function __construct( + protected IDBConnection $connection, + protected IUserManager $userManager, + ) { } /** @@ -94,7 +69,7 @@ class CleanTags implements IRepairStep { ->orderBy('uid') ->setMaxResults(50) ->setFirstResult($offset); - $result = $query->execute(); + $result = $query->executeQuery(); $users = []; $hadResults = false; @@ -115,7 +90,7 @@ class CleanTags implements IRepairStep { $query = $this->connection->getQueryBuilder(); $query->delete('vcategory') ->where($query->expr()->in('uid', $query->createNamedParameter($users, IQueryBuilder::PARAM_STR_ARRAY))); - $this->deletedTags += $query->execute(); + $this->deletedTags += $query->executeStatement(); } return true; } @@ -128,7 +103,7 @@ class CleanTags implements IRepairStep { $output, '%d tags for delete files have been removed.', 'vcategory_to_object', 'objid', - 'filecache', 'fileid', 'path_hash' + 'filecache', 'fileid', 'fileid' ); } @@ -168,8 +143,8 @@ class CleanTags implements IRepairStep { * @param string $deleteId * @param string $sourceTable * @param string $sourceId - * @param string $sourceNullColumn If this column is null in the source table, - * the entry is deleted in the $deleteTable + * @param string $sourceNullColumn If this column is null in the source table, + * the entry is deleted in the $deleteTable */ protected function deleteOrphanEntries(IOutput $output, $repairInfo, $deleteTable, $deleteId, $sourceTable, $sourceId, $sourceNullColumn) { $qb = $this->connection->getQueryBuilder(); @@ -183,23 +158,24 @@ class CleanTags implements IRepairStep { ->andWhere( $qb->expr()->isNull('s.' . $sourceNullColumn) ); - $result = $qb->execute(); + $result = $qb->executeQuery(); $orphanItems = []; while ($row = $result->fetch()) { - $orphanItems[] = (int) $row[$deleteId]; + $orphanItems[] = (int)$row[$deleteId]; } + $deleteQuery = $this->connection->getQueryBuilder(); + $deleteQuery->delete($deleteTable) + ->where( + $deleteQuery->expr()->eq('type', $deleteQuery->expr()->literal('files')) + ) + ->andWhere($deleteQuery->expr()->in($deleteId, $deleteQuery->createParameter('ids'))); if (!empty($orphanItems)) { $orphanItemsBatch = array_chunk($orphanItems, 200); foreach ($orphanItemsBatch as $items) { - $qb->delete($deleteTable) - ->where( - $qb->expr()->eq('type', $qb->expr()->literal('files')) - ) - ->andWhere($qb->expr()->in($deleteId, $qb->createParameter('ids'))); - $qb->setParameter('ids', $items, IQueryBuilder::PARAM_INT_ARRAY); - $qb->execute(); + $deleteQuery->setParameter('ids', $items, IQueryBuilder::PARAM_INT_ARRAY); + $deleteQuery->executeStatement(); } } diff --git a/lib/private/Repair/CleanUpAbandonedApps.php b/lib/private/Repair/CleanUpAbandonedApps.php new file mode 100644 index 00000000000..718f625be86 --- /dev/null +++ b/lib/private/Repair/CleanUpAbandonedApps.php @@ -0,0 +1,34 @@ +<?php + +declare(strict_types=1); +/** + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Repair; + +use OCP\IConfig; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; + +class CleanUpAbandonedApps implements IRepairStep { + protected const ABANDONED_APPS = ['accessibility', 'files_videoplayer']; + private IConfig $config; + + public function __construct(IConfig $config) { + $this->config = $config; + } + + public function getName(): string { + return 'Clean up abandoned apps'; + } + + public function run(IOutput $output): void { + foreach (self::ABANDONED_APPS as $app) { + // only remove global app values + // user prefs of accessibility are dealt with in Theming migration + // videoplayer did not have user prefs + $this->config->deleteAppValues($app); + } + } +} diff --git a/lib/private/Repair/ClearFrontendCaches.php b/lib/private/Repair/ClearFrontendCaches.php index 523a1fc6be5..5c57a63379d 100644 --- a/lib/private/Repair/ClearFrontendCaches.php +++ b/lib/private/Repair/ClearFrontendCaches.php @@ -1,52 +1,26 @@ <?php + /** - * @copyright Copyright (c) 2018 Julius Härtl <jus@bitgrid.net> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Julius Härtl <jus@bitgrid.net> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\Repair; use OC\Template\JSCombiner; -use OC\Template\SCSSCacher; use OCP\ICacheFactory; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; class ClearFrontendCaches implements IRepairStep { - /** @var ICacheFactory */ protected $cacheFactory; - /** @var SCSSCacher */ - protected $scssCacher; - /** @var JSCombiner */ protected $jsCombiner; public function __construct(ICacheFactory $cacheFactory, - SCSSCacher $SCSSCacher, - JSCombiner $JSCombiner) { + JSCombiner $JSCombiner) { $this->cacheFactory = $cacheFactory; - $this->scssCacher = $SCSSCacher; $this->jsCombiner = $JSCombiner; } @@ -60,9 +34,6 @@ class ClearFrontendCaches implements IRepairStep { $c->clear(); $output->info('Image cache cleared'); - $this->scssCacher->resetCache(); - $output->info('SCSS cache cleared'); - $this->jsCombiner->resetCache(); $output->info('JS cache cleared'); } catch (\Exception $e) { diff --git a/lib/private/Repair/ClearGeneratedAvatarCache.php b/lib/private/Repair/ClearGeneratedAvatarCache.php index 26d436451bf..0f743afbb4c 100644 --- a/lib/private/Repair/ClearGeneratedAvatarCache.php +++ b/lib/private/Repair/ClearGeneratedAvatarCache.php @@ -1,69 +1,48 @@ <?php + /** - * @copyright Copyright (c) 2018 John Molakvoæ <skjnldsv@protonmail.com> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com> - * @author Michael Weimann <mail@michael-weimann.eu> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\Repair; use OC\Avatar\AvatarManager; +use OCP\BackgroundJob\IJobList; use OCP\IConfig; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; class ClearGeneratedAvatarCache implements IRepairStep { + protected AvatarManager $avatarManager; + private IConfig $config; + private IJobList $jobList; - /** @var AvatarManager */ - protected $avatarManager; - - /** @var IConfig */ - private $config; - - public function __construct(IConfig $config, AvatarManager $avatarManager) { + public function __construct(IConfig $config, AvatarManager $avatarManager, IJobList $jobList) { $this->config = $config; $this->avatarManager = $avatarManager; + $this->jobList = $jobList; } - public function getName() { - return 'Clear every generated avatar on major updates'; + public function getName(): string { + return 'Clear every generated avatar'; } /** * Check if this repair step should run - * - * @return boolean */ - private function shouldRun() { - $versionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0.0'); + private function shouldRun(): bool { + $versionFromBeforeUpdate = $this->config->getSystemValueString('version', '0.0.0.0'); - // was added to 15.0.0.4 - return version_compare($versionFromBeforeUpdate, '15.0.0.4', '<='); + // This job only runs if the server was on a version lower than or equal to 27.0.0 before the upgrade. + // To clear the avatar cache again, bump the version to the currently released version (and change the operator to <= if it's not the master branch) and wait for the next release. + return version_compare($versionFromBeforeUpdate, '27.0.0', '<'); } - public function run(IOutput $output) { + public function run(IOutput $output): void { if ($this->shouldRun()) { try { - $this->avatarManager->clearCachedAvatars(); - $output->info('Avatar cache cleared'); + $this->jobList->add(ClearGeneratedAvatarCacheJob::class, []); + $output->info('Avatar cache clearing job added'); } catch (\Exception $e) { $output->warning('Unable to clear the avatar cache'); } diff --git a/lib/private/Repair/ClearGeneratedAvatarCacheJob.php b/lib/private/Repair/ClearGeneratedAvatarCacheJob.php new file mode 100644 index 00000000000..524a470e62a --- /dev/null +++ b/lib/private/Repair/ClearGeneratedAvatarCacheJob.php @@ -0,0 +1,24 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Repair; + +use OC\Avatar\AvatarManager; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\BackgroundJob\QueuedJob; + +class ClearGeneratedAvatarCacheJob extends QueuedJob { + protected AvatarManager $avatarManager; + + public function __construct(ITimeFactory $timeFactory, AvatarManager $avatarManager) { + parent::__construct($timeFactory); + $this->avatarManager = $avatarManager; + } + + public function run($argument) { + $this->avatarManager->clearCachedAvatars(); + } +} diff --git a/lib/private/Repair/Collation.php b/lib/private/Repair/Collation.php index d2974c1680a..43229792217 100644 --- a/lib/private/Repair/Collation.php +++ b/lib/private/Repair/Collation.php @@ -1,46 +1,24 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Morris Jobke <hey@morrisjobke.de> - * @author Robin Appelman <robin@icewind.nl> - * @author Robin Müller <coder-hugo@users.noreply.github.com> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OC\Repair; use Doctrine\DBAL\Exception\DriverException; -use Doctrine\DBAL\Platforms\MySQLPlatform; use OCP\IConfig; use OCP\IDBConnection; -use OCP\ILogger; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; +use Psr\Log\LoggerInterface; class Collation implements IRepairStep { - /** @var IConfig */ + /** @var IConfig */ protected $config; - /** @var ILogger */ - protected $logger; + protected LoggerInterface $logger; /** @var IDBConnection */ protected $connection; @@ -49,12 +27,14 @@ class Collation implements IRepairStep { protected $ignoreFailures; /** - * @param IConfig $config - * @param ILogger $logger - * @param IDBConnection $connection * @param bool $ignoreFailures */ - public function __construct(IConfig $config, ILogger $logger, IDBConnection $connection, $ignoreFailures) { + public function __construct( + IConfig $config, + LoggerInterface $logger, + IDBConnection $connection, + $ignoreFailures, + ) { $this->connection = $connection; $this->config = $config; $this->logger = $logger; @@ -69,12 +49,12 @@ class Collation implements IRepairStep { * Fix mime types */ public function run(IOutput $output) { - if (!$this->connection->getDatabasePlatform() instanceof MySQLPlatform) { + if ($this->connection->getDatabaseProvider() !== IDBConnection::PLATFORM_MYSQL) { $output->info('Not a mysql database -> nothing to do'); return; } - $characterSet = $this->config->getSystemValue('mysql.utf8mb4', false) ? 'utf8mb4' : 'utf8'; + $characterSet = $this->config->getSystemValueBool('mysql.utf8mb4', false) ? 'utf8mb4' : 'utf8'; $tables = $this->getAllNonUTF8BinTables($this->connection); foreach ($tables as $table) { @@ -84,24 +64,19 @@ class Collation implements IRepairStep { $query->execute(); } catch (DriverException $e) { // Just log this - $this->logger->logException($e); + $this->logger->error($e->getMessage(), ['exception' => $e]); if (!$this->ignoreFailures) { throw $e; } } $output->info("Change collation for $table ..."); - if ($characterSet === 'utf8mb4') { - // need to set row compression first - $query = $this->connection->prepare('ALTER TABLE `' . $table . '` ROW_FORMAT=COMPRESSED;'); - $query->execute(); - } $query = $this->connection->prepare('ALTER TABLE `' . $table . '` CONVERT TO CHARACTER SET ' . $characterSet . ' COLLATE ' . $characterSet . '_bin;'); try { $query->execute(); } catch (DriverException $e) { // Just log this - $this->logger->logException($e); + $this->logger->error($e->getMessage(), ['exception' => $e]); if (!$this->ignoreFailures) { throw $e; } @@ -117,16 +92,16 @@ class Collation implements IRepairStep { * @return string[] */ protected function getAllNonUTF8BinTables(IDBConnection $connection) { - $dbName = $this->config->getSystemValue("dbname"); - $characterSet = $this->config->getSystemValue('mysql.utf8mb4', false) ? 'utf8mb4' : 'utf8'; + $dbName = $this->config->getSystemValueString('dbname'); + $characterSet = $this->config->getSystemValueBool('mysql.utf8mb4', false) ? 'utf8mb4' : 'utf8'; // 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(); @@ -137,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/Events/RepairAdvanceEvent.php b/lib/private/Repair/Events/RepairAdvanceEvent.php new file mode 100644 index 00000000000..476db9e4702 --- /dev/null +++ b/lib/private/Repair/Events/RepairAdvanceEvent.php @@ -0,0 +1,32 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ +namespace OC\Repair\Events; + +use OCP\EventDispatcher\Event; + +class RepairAdvanceEvent extends Event { + private int $increment; + private string $description; + + public function __construct( + int $increment, + string $description, + ) { + $this->increment = $increment; + $this->description = $description; + } + + public function getIncrement(): int { + return $this->increment; + } + + public function getDescription(): string { + return $this->description; + } +} diff --git a/lib/private/Repair/Events/RepairErrorEvent.php b/lib/private/Repair/Events/RepairErrorEvent.php new file mode 100644 index 00000000000..e5be8a5a031 --- /dev/null +++ b/lib/private/Repair/Events/RepairErrorEvent.php @@ -0,0 +1,25 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ +namespace OC\Repair\Events; + +use OCP\EventDispatcher\Event; + +class RepairErrorEvent extends Event { + private string $message; + + public function __construct( + string $message, + ) { + $this->message = $message; + } + + public function getMessage(): string { + return $this->message; + } +} diff --git a/lib/private/Repair/Events/RepairFinishEvent.php b/lib/private/Repair/Events/RepairFinishEvent.php new file mode 100644 index 00000000000..767a7506e6f --- /dev/null +++ b/lib/private/Repair/Events/RepairFinishEvent.php @@ -0,0 +1,14 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ +namespace OC\Repair\Events; + +use OCP\EventDispatcher\Event; + +class RepairFinishEvent extends Event { +} diff --git a/lib/private/Repair/Events/RepairInfoEvent.php b/lib/private/Repair/Events/RepairInfoEvent.php new file mode 100644 index 00000000000..ce8eb2f99e6 --- /dev/null +++ b/lib/private/Repair/Events/RepairInfoEvent.php @@ -0,0 +1,25 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ +namespace OC\Repair\Events; + +use OCP\EventDispatcher\Event; + +class RepairInfoEvent extends Event { + private string $message; + + public function __construct( + string $message, + ) { + $this->message = $message; + } + + public function getMessage(): string { + return $this->message; + } +} diff --git a/lib/private/Repair/Events/RepairStartEvent.php b/lib/private/Repair/Events/RepairStartEvent.php new file mode 100644 index 00000000000..47e713d57d9 --- /dev/null +++ b/lib/private/Repair/Events/RepairStartEvent.php @@ -0,0 +1,32 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ +namespace OC\Repair\Events; + +use OCP\EventDispatcher\Event; + +class RepairStartEvent extends Event { + private int $max; + private string $current; + + public function __construct( + int $max, + string $current, + ) { + $this->max = $max; + $this->current = $current; + } + + public function getMaxStep(): int { + return $this->max; + } + + public function getCurrentStepName(): string { + return $this->current; + } +} diff --git a/lib/private/Repair/Events/RepairStepEvent.php b/lib/private/Repair/Events/RepairStepEvent.php new file mode 100644 index 00000000000..27e1efbdb08 --- /dev/null +++ b/lib/private/Repair/Events/RepairStepEvent.php @@ -0,0 +1,25 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ +namespace OC\Repair\Events; + +use OCP\EventDispatcher\Event; + +class RepairStepEvent extends Event { + private string $stepName; + + public function __construct( + string $stepName, + ) { + $this->stepName = $stepName; + } + + public function getStepName(): string { + return $this->stepName; + } +} diff --git a/lib/private/Repair/Events/RepairWarningEvent.php b/lib/private/Repair/Events/RepairWarningEvent.php new file mode 100644 index 00000000000..6893a7212ec --- /dev/null +++ b/lib/private/Repair/Events/RepairWarningEvent.php @@ -0,0 +1,25 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ +namespace OC\Repair\Events; + +use OCP\EventDispatcher\Event; + +class RepairWarningEvent extends Event { + private string $message; + + public function __construct( + string $message, + ) { + $this->message = $message; + } + + public function getMessage(): string { + return $this->message; + } +} diff --git a/lib/private/Repair/MoveUpdaterStepFile.php b/lib/private/Repair/MoveUpdaterStepFile.php index e912838f50a..bb8f9d3acfc 100644 --- a/lib/private/Repair/MoveUpdaterStepFile.php +++ b/lib/private/Repair/MoveUpdaterStepFile.php @@ -1,34 +1,16 @@ <?php + /** - * @copyright Copyright (c) 2016 Morris Jobke <hey@morrisjobke.de> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Morris Jobke <hey@morrisjobke.de> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\Repair; +use OCP\Files; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; class MoveUpdaterStepFile implements IRepairStep { - /** @var \OCP\IConfig */ protected $config; @@ -44,14 +26,14 @@ class MoveUpdaterStepFile implements IRepairStep { } public function run(IOutput $output) { - $dataDir = $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data'); - $instanceId = $this->config->getSystemValue('instanceid', null); + $updateDir = $this->config->getSystemValue('updatedirectory', null) ?? $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data'); + $instanceId = $this->config->getSystemValueString('instanceid'); - if (!is_string($instanceId) || empty($instanceId)) { + if (empty($instanceId)) { return; } - $updaterFolderPath = $dataDir . '/updater-' . $instanceId; + $updaterFolderPath = $updateDir . '/updater-' . $instanceId; $stepFile = $updaterFolderPath . '/.step'; if (file_exists($stepFile)) { $output->info('.step file exists'); @@ -60,7 +42,7 @@ class MoveUpdaterStepFile implements IRepairStep { // cleanup if (file_exists($previousStepFile)) { - if (\OC_Helper::rmdirr($previousStepFile)) { + if (Files::rmdirr($previousStepFile)) { $output->info('.step-previous-update removed'); } else { $output->info('.step-previous-update can\'t be removed - abort move of .step file'); diff --git a/lib/private/Repair/NC11/FixMountStorages.php b/lib/private/Repair/NC11/FixMountStorages.php deleted file mode 100644 index 199b504fa35..00000000000 --- a/lib/private/Repair/NC11/FixMountStorages.php +++ /dev/null @@ -1,80 +0,0 @@ -<?php -/** - * @copyright 2016 Joas Schilling <coding@schilljs.com> - * - * @author Joas Schilling <coding@schilljs.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -namespace OC\Repair\NC11; - -use OCP\DB\QueryBuilder\IQueryBuilder; -use OCP\IDBConnection; -use OCP\Migration\IOutput; -use OCP\Migration\IRepairStep; - -class FixMountStorages implements IRepairStep { - - /** @var IDBConnection */ - private $db; - - /** - * @param IDBConnection $db - */ - public function __construct(IDBConnection $db) { - $this->db = $db; - } - - /** - * @return string - */ - public function getName() { - return 'Fix potential broken mount points'; - } - - public function run(IOutput $output) { - $query = $this->db->getQueryBuilder(); - $query->select('m.id', 'f.storage') - ->from('mounts', 'm') - ->leftJoin('m', 'filecache', 'f', $query->expr()->eq('m.root_id', 'f.fileid')) - ->where($query->expr()->neq('m.storage_id', 'f.storage')); - - $update = $this->db->getQueryBuilder(); - $update->update('mounts') - ->set('storage_id', $update->createParameter('storage')) - ->where($query->expr()->eq('id', $update->createParameter('mount'))); - - $result = $query->execute(); - $entriesUpdated = 0; - while ($row = $result->fetch()) { - $update->setParameter('storage', $row['storage'], IQueryBuilder::PARAM_INT) - ->setParameter('mount', $row['id'], IQueryBuilder::PARAM_INT); - $update->execute(); - $entriesUpdated++; - } - $result->closeCursor(); - - if ($entriesUpdated > 0) { - $output->info($entriesUpdated . ' mounts updated'); - return; - } - - $output->info('No mounts updated'); - } -} diff --git a/lib/private/Repair/NC13/AddLogRotateJob.php b/lib/private/Repair/NC13/AddLogRotateJob.php index 7bd290894a4..bd6c510785f 100644 --- a/lib/private/Repair/NC13/AddLogRotateJob.php +++ b/lib/private/Repair/NC13/AddLogRotateJob.php @@ -1,26 +1,9 @@ <?php + /** - * @copyright 2017, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\Repair\NC13; use OC\Log\Rotate; @@ -29,7 +12,6 @@ use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; class AddLogRotateJob implements IRepairStep { - /** @var IJobList */ private $jobList; diff --git a/lib/private/Repair/NC14/AddPreviewBackgroundCleanupJob.php b/lib/private/Repair/NC14/AddPreviewBackgroundCleanupJob.php index f2958de5b96..417bc5e6adc 100644 --- a/lib/private/Repair/NC14/AddPreviewBackgroundCleanupJob.php +++ b/lib/private/Repair/NC14/AddPreviewBackgroundCleanupJob.php @@ -3,27 +3,9 @@ declare(strict_types=1); /** - * @copyright 2018, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2018 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\Repair\NC14; use OC\Preview\BackgroundCleanupJob; @@ -32,7 +14,6 @@ use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; class AddPreviewBackgroundCleanupJob implements IRepairStep { - /** @var IJobList */ private $jobList; diff --git a/lib/private/Repair/NC16/AddClenupLoginFlowV2BackgroundJob.php b/lib/private/Repair/NC16/AddClenupLoginFlowV2BackgroundJob.php index 34afd5dea60..ab5f93415fc 100644 --- a/lib/private/Repair/NC16/AddClenupLoginFlowV2BackgroundJob.php +++ b/lib/private/Repair/NC16/AddClenupLoginFlowV2BackgroundJob.php @@ -3,27 +3,9 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\Repair\NC16; use OC\Core\BackgroundJobs\CleanupLoginFlowV2; @@ -32,7 +14,6 @@ use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; class AddClenupLoginFlowV2BackgroundJob implements IRepairStep { - /** @var IJobList */ private $jobList; diff --git a/lib/private/Repair/NC16/CleanupCardDAVPhotoCache.php b/lib/private/Repair/NC16/CleanupCardDAVPhotoCache.php index fb09097f6ef..646dd2c5e83 100644 --- a/lib/private/Repair/NC16/CleanupCardDAVPhotoCache.php +++ b/lib/private/Repair/NC16/CleanupCardDAVPhotoCache.php @@ -3,37 +3,19 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019, Daniel Kesselberg (mail@danielkesselberg.de) - * - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Morris Jobke <hey@morrisjobke.de> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair\NC16; -use OCP\Files\IAppData; +use OCP\Files\AppData\IAppDataFactory; use OCP\Files\NotFoundException; use OCP\Files\SimpleFS\ISimpleFolder; use OCP\IConfig; -use OCP\ILogger; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; +use Psr\Log\LoggerInterface; use RuntimeException; /** @@ -46,20 +28,11 @@ use RuntimeException; * photo could be returned for this vcard. These invalid files are removed by this migration step. */ class CleanupCardDAVPhotoCache implements IRepairStep { - - /** @var IConfig */ - private $config; - - /** @var IAppData */ - private $appData; - - /** @var ILogger */ - private $logger; - - public function __construct(IConfig $config, IAppData $appData, ILogger $logger) { - $this->config = $config; - $this->appData = $appData; - $this->logger = $logger; + public function __construct( + private IConfig $config, + private IAppDataFactory $appDataFactory, + private LoggerInterface $logger, + ) { } public function getName(): string { @@ -67,12 +40,14 @@ class CleanupCardDAVPhotoCache implements IRepairStep { } private function repair(IOutput $output): void { + $photoCacheAppData = $this->appDataFactory->get('dav-photocache'); + try { - $folders = $this->appData->getDirectoryListing(); + $folders = $photoCacheAppData->getDirectoryListing(); } catch (NotFoundException $e) { return; } catch (RuntimeException $e) { - $this->logger->logException($e, ['message' => 'Failed to fetch directory listing in CleanupCardDAVPhotoCache']); + $this->logger->error('Failed to fetch directory listing in CleanupCardDAVPhotoCache', ['exception' => $e]); return; } @@ -91,7 +66,7 @@ class CleanupCardDAVPhotoCache implements IRepairStep { /** @var ISimpleFolder $folder */ $folder->getFile('photo.')->delete(); } catch (\Exception $e) { - $this->logger->logException($e); + $this->logger->error($e->getMessage(), ['exception' => $e]); $output->warning('Could not delete file "dav-photocache/' . $folder->getName() . '/photo."'); } } @@ -99,7 +74,7 @@ class CleanupCardDAVPhotoCache implements IRepairStep { private function shouldRun(): bool { return version_compare( - $this->config->getSystemValue('version', '0.0.0.0'), + $this->config->getSystemValueString('version', '0.0.0.0'), '16.0.0.0', '<=' ); diff --git a/lib/private/Repair/NC16/ClearCollectionsAccessCache.php b/lib/private/Repair/NC16/ClearCollectionsAccessCache.php index 6d13a8f13a0..1627ed40b98 100644 --- a/lib/private/Repair/NC16/ClearCollectionsAccessCache.php +++ b/lib/private/Repair/NC16/ClearCollectionsAccessCache.php @@ -3,27 +3,9 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019 Joas Schilling <coding@schilljs.com> - * - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\Repair\NC16; use OC\Collaboration\Resources\Manager; @@ -33,11 +15,10 @@ use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; class ClearCollectionsAccessCache implements IRepairStep { - /** @var IConfig */ private $config; - /** @var IManager|Manager */ + /** @var Manager */ private $manager; public function __construct(IConfig $config, IManager $manager) { @@ -50,7 +31,7 @@ class ClearCollectionsAccessCache implements IRepairStep { } private function shouldRun(): bool { - $versionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0.0'); + $versionFromBeforeUpdate = $this->config->getSystemValueString('version', '0.0.0.0'); return version_compare($versionFromBeforeUpdate, '17.0.0.3', '<='); } diff --git a/lib/private/Repair/NC18/ResetGeneratedAvatarFlag.php b/lib/private/Repair/NC18/ResetGeneratedAvatarFlag.php index 1a86b8706ae..b0dfec295e7 100644 --- a/lib/private/Repair/NC18/ResetGeneratedAvatarFlag.php +++ b/lib/private/Repair/NC18/ResetGeneratedAvatarFlag.php @@ -3,27 +3,9 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019, Joas Schilling <coding@schilljs.com> - * - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\Repair\NC18; use OCP\IConfig; @@ -32,14 +14,13 @@ use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; class ResetGeneratedAvatarFlag implements IRepairStep { - /** @var IConfig */ private $config; /** @var IDBConnection */ private $connection; public function __construct(IConfig $config, - IDBConnection $connection) { + IDBConnection $connection) { $this->config = $config; $this->connection = $connection; } @@ -49,7 +30,7 @@ class ResetGeneratedAvatarFlag implements IRepairStep { } private function shouldRun(): bool { - $versionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0.0'); + $versionFromBeforeUpdate = $this->config->getSystemValueString('version', '0.0.0.0'); return version_compare($versionFromBeforeUpdate, '18.0.0.5', '<='); } diff --git a/lib/private/Repair/NC20/EncryptionLegacyCipher.php b/lib/private/Repair/NC20/EncryptionLegacyCipher.php index e60a2435349..89473ffd5e8 100644 --- a/lib/private/Repair/NC20/EncryptionLegacyCipher.php +++ b/lib/private/Repair/NC20/EncryptionLegacyCipher.php @@ -3,28 +3,9 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Julius Härtl <jus@bitgrid.net> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\Repair\NC20; use OCP\Encryption\IManager; @@ -33,14 +14,13 @@ use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; class EncryptionLegacyCipher implements IRepairStep { - /** @var IConfig */ private $config; /** @var IManager */ private $manager; public function __construct(IConfig $config, - IManager $manager) { + IManager $manager) { $this->config = $config; $this->manager = $manager; } @@ -50,7 +30,7 @@ class EncryptionLegacyCipher implements IRepairStep { } private function shouldRun(): bool { - $versionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0.0'); + $versionFromBeforeUpdate = $this->config->getSystemValueString('version', '0.0.0.0'); return version_compare($versionFromBeforeUpdate, '20.0.0.0', '<='); } diff --git a/lib/private/Repair/NC20/EncryptionMigration.php b/lib/private/Repair/NC20/EncryptionMigration.php index 45789d2317b..6de143747b3 100644 --- a/lib/private/Repair/NC20/EncryptionMigration.php +++ b/lib/private/Repair/NC20/EncryptionMigration.php @@ -3,28 +3,9 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Julius Härtl <jus@bitgrid.net> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\Repair\NC20; use OCP\Encryption\IManager; @@ -33,14 +14,13 @@ use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; class EncryptionMigration implements IRepairStep { - /** @var IConfig */ private $config; /** @var IManager */ private $manager; public function __construct(IConfig $config, - IManager $manager) { + IManager $manager) { $this->config = $config; $this->manager = $manager; } @@ -50,7 +30,7 @@ class EncryptionMigration implements IRepairStep { } private function shouldRun(): bool { - $versionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0.0'); + $versionFromBeforeUpdate = $this->config->getSystemValueString('version', '0.0.0.0'); return version_compare($versionFromBeforeUpdate, '20.0.0.1', '<='); } diff --git a/lib/private/Repair/NC20/ShippedDashboardEnable.php b/lib/private/Repair/NC20/ShippedDashboardEnable.php index b8900373b2a..955011a8c84 100644 --- a/lib/private/Repair/NC20/ShippedDashboardEnable.php +++ b/lib/private/Repair/NC20/ShippedDashboardEnable.php @@ -1,29 +1,11 @@ <?php -/** - * @copyright Copyright (c) 2020 Julius Härtl <jus@bitgrid.net> - * - * @author Julius Härtl <jus@bitgrid.net> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ declare(strict_types=1); - +/** + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ namespace OC\Repair\NC20; use OCP\IConfig; @@ -31,7 +13,6 @@ use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; class ShippedDashboardEnable implements IRepairStep { - /** @var IConfig */ private $config; diff --git a/lib/private/Repair/NC21/AddCheckForUserCertificatesJob.php b/lib/private/Repair/NC21/AddCheckForUserCertificatesJob.php index df6637e3948..5cee33b381c 100644 --- a/lib/private/Repair/NC21/AddCheckForUserCertificatesJob.php +++ b/lib/private/Repair/NC21/AddCheckForUserCertificatesJob.php @@ -1,26 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2020 Morris Jobke <hey@morrisjobke.de> - * - * @author Morris Jobke <hey@morrisjobke.de> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\Repair\NC21; use OC\Core\BackgroundJobs\CheckForUserCertificates; @@ -30,7 +13,6 @@ use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; class AddCheckForUserCertificatesJob implements IRepairStep { - /** @var IJobList */ protected $jobList; /** @var IConfig */ @@ -46,7 +28,7 @@ class AddCheckForUserCertificatesJob implements IRepairStep { } private function shouldRun() { - $versionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0.0'); + $versionFromBeforeUpdate = $this->config->getSystemValueString('version', '0.0.0.0'); // was added to 21.0.0.2 return version_compare($versionFromBeforeUpdate, '21.0.0.2', '<'); diff --git a/lib/private/Repair/NC21/ValidatePhoneNumber.php b/lib/private/Repair/NC21/ValidatePhoneNumber.php deleted file mode 100644 index 995c38602f3..00000000000 --- a/lib/private/Repair/NC21/ValidatePhoneNumber.php +++ /dev/null @@ -1,90 +0,0 @@ -<?php - -declare(strict_types=1); - -/** - * @copyright Copyright (c) 2020 Joas Schilling <coding@schilljs.com> - * - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -namespace OC\Repair\NC21; - -use OC\Accounts\AccountManager; -use OCP\Accounts\IAccountManager; -use OCP\IConfig; -use OCP\IUser; -use OCP\IUserManager; -use OCP\Migration\IOutput; -use OCP\Migration\IRepairStep; - -class ValidatePhoneNumber implements IRepairStep { - - /** @var IConfig */ - protected $config; - /** @var IUserManager */ - protected $userManager; - /** @var AccountManager */ - private $accountManager; - - public function __construct(IUserManager $userManager, - AccountManager $accountManager, - IConfig $config) { - $this->config = $config; - $this->userManager = $userManager; - $this->accountManager = $accountManager; - } - - public function getName(): string { - return 'Validate the phone number and store it in a known format for search'; - } - - private function shouldRun(): bool { - return true; - } - - public function run(IOutput $output): void { - if ($this->config->getSystemValueString('default_phone_region', '') === '') { - throw new \Exception('Can not validate phone numbers without `default_phone_region` being set in the config file'); - } - - $numUpdated = 0; - $numRemoved = 0; - - $this->userManager->callForSeenUsers(function (IUser $user) use (&$numUpdated, &$numRemoved) { - $account = $this->accountManager->getUser($user); - - if ($account[IAccountManager::PROPERTY_PHONE]['value'] !== '') { - $updated = $this->accountManager->updateUser($user, $account); - - if ($account[IAccountManager::PROPERTY_PHONE]['value'] !== $updated[IAccountManager::PROPERTY_PHONE]['value']) { - if ($updated[IAccountManager::PROPERTY_PHONE]['value'] === '') { - $numRemoved++; - } else { - $numUpdated++; - } - } - } - }); - - if ($numRemoved > 0 || $numUpdated > 0) { - $output->info('Updated ' . $numUpdated . ' entries and cleaned ' . $numRemoved . ' invalid phone numbers'); - } - } -} diff --git a/lib/private/Repair/NC22/LookupServerSendCheck.php b/lib/private/Repair/NC22/LookupServerSendCheck.php new file mode 100644 index 00000000000..540dc2a730d --- /dev/null +++ b/lib/private/Repair/NC22/LookupServerSendCheck.php @@ -0,0 +1,33 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Repair\NC22; + +use OC\Core\BackgroundJobs\LookupServerSendCheckBackgroundJob; +use OCP\BackgroundJob\IJobList; +use OCP\IConfig; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; + +class LookupServerSendCheck implements IRepairStep { + private IJobList $jobList; + private IConfig $config; + + public function __construct(IJobList $jobList, IConfig $config) { + $this->jobList = $jobList; + $this->config = $config; + } + + public function getName(): string { + return 'Add background job to set the lookup server share state for users'; + } + + public function run(IOutput $output): void { + $this->jobList->add(LookupServerSendCheckBackgroundJob::class); + } +} diff --git a/lib/private/Repair/NC24/AddTokenCleanupJob.php b/lib/private/Repair/NC24/AddTokenCleanupJob.php new file mode 100644 index 00000000000..f1dac2d4e12 --- /dev/null +++ b/lib/private/Repair/NC24/AddTokenCleanupJob.php @@ -0,0 +1,30 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Repair\NC24; + +use OC\Authentication\Token\TokenCleanupJob; +use OCP\BackgroundJob\IJobList; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; + +class AddTokenCleanupJob implements IRepairStep { + private IJobList $jobList; + + public function __construct(IJobList $jobList) { + $this->jobList = $jobList; + } + + public function getName(): string { + return 'Add token cleanup job'; + } + + public function run(IOutput $output) { + $this->jobList->add(TokenCleanupJob::class); + } +} diff --git a/lib/private/Repair/NC25/AddMissingSecretJob.php b/lib/private/Repair/NC25/AddMissingSecretJob.php new file mode 100644 index 00000000000..46b89d5f6f7 --- /dev/null +++ b/lib/private/Repair/NC25/AddMissingSecretJob.php @@ -0,0 +1,49 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2022 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Repair\NC25; + +use OCP\HintException; +use OCP\IConfig; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; +use OCP\Security\ISecureRandom; + +class AddMissingSecretJob implements IRepairStep { + private IConfig $config; + private ISecureRandom $random; + + public function __construct(IConfig $config, ISecureRandom $random) { + $this->config = $config; + $this->random = $random; + } + + public function getName(): string { + return 'Add possibly missing system config'; + } + + public function run(IOutput $output): void { + $passwordSalt = $this->config->getSystemValueString('passwordsalt', ''); + if ($passwordSalt === '') { + try { + $this->config->setSystemValue('passwordsalt', $this->random->generate(30)); + } catch (HintException $e) { + $output->warning('passwordsalt is missing from your config.php and your config.php is read only. Please fix it manually.'); + } + } + + $secret = $this->config->getSystemValueString('secret', ''); + if ($secret === '') { + try { + $this->config->setSystemValue('secret', $this->random->generate(48)); + } catch (HintException $e) { + $output->warning('secret is missing from your config.php and your config.php is read only. Please fix it manually.'); + } + } + } +} diff --git a/lib/private/Repair/NC29/SanitizeAccountProperties.php b/lib/private/Repair/NC29/SanitizeAccountProperties.php new file mode 100644 index 00000000000..412570ba71d --- /dev/null +++ b/lib/private/Repair/NC29/SanitizeAccountProperties.php @@ -0,0 +1,30 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Repair\NC29; + +use OCP\BackgroundJob\IJobList; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; + +class SanitizeAccountProperties implements IRepairStep { + + public function __construct( + private IJobList $jobList, + ) { + } + + public function getName(): string { + return 'Validate account properties and store phone numbers in a known format for search'; + } + + public function run(IOutput $output): void { + $this->jobList->add(SanitizeAccountPropertiesJob::class, null); + $output->info('Queued background to validate account properties.'); + } +} diff --git a/lib/private/Repair/NC29/SanitizeAccountPropertiesJob.php b/lib/private/Repair/NC29/SanitizeAccountPropertiesJob.php new file mode 100644 index 00000000000..55ec445e9da --- /dev/null +++ b/lib/private/Repair/NC29/SanitizeAccountPropertiesJob.php @@ -0,0 +1,75 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2025 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Repair\NC29; + +use InvalidArgumentException; +use OCP\Accounts\IAccountManager; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\BackgroundJob\QueuedJob; +use OCP\IUser; +use OCP\IUserManager; +use Psr\Log\LoggerInterface; + +class SanitizeAccountPropertiesJob extends QueuedJob { + + private const PROPERTIES_TO_CHECK = [ + IAccountManager::PROPERTY_PHONE, + IAccountManager::PROPERTY_WEBSITE, + IAccountManager::PROPERTY_TWITTER, + IAccountManager::PROPERTY_FEDIVERSE, + ]; + + public function __construct( + ITimeFactory $timeFactory, + private IUserManager $userManager, + private IAccountManager $accountManager, + private LoggerInterface $logger, + ) { + parent::__construct($timeFactory); + $this->setAllowParallelRuns(false); + } + + protected function run(mixed $argument): void { + $numRemoved = 0; + + $this->userManager->callForSeenUsers(function (IUser $user) use (&$numRemoved) { + $account = $this->accountManager->getAccount($user); + $properties = array_keys($account->jsonSerialize()); + + // Check if there are some properties we can sanitize - reduces number of db queries + if (empty(array_intersect($properties, self::PROPERTIES_TO_CHECK))) { + return; + } + + // Limit the loop to the properties we check to ensure there are no infinite loops + // we add one additional loop (+ 1) as we need 1 loop for checking + 1 for update. + $iteration = count(self::PROPERTIES_TO_CHECK) + 1; + while ($iteration-- > 0) { + try { + $this->accountManager->updateAccount($account); + return; + } catch (InvalidArgumentException $e) { + if (in_array($e->getMessage(), IAccountManager::ALLOWED_PROPERTIES)) { + $numRemoved++; + $property = $account->getProperty($e->getMessage()); + $account->setProperty($property->getName(), '', $property->getScope(), IAccountManager::NOT_VERIFIED); + } else { + $this->logger->error('Error while sanitizing account property', ['exception' => $e, 'user' => $user->getUID()]); + return; + } + } + } + $this->logger->error('Iteration limit exceeded while cleaning account properties', ['user' => $user->getUID()]); + }); + + if ($numRemoved > 0) { + $this->logger->info('Cleaned ' . $numRemoved . ' invalid account property entries'); + } + } +} diff --git a/lib/private/Repair/NC30/RemoveLegacyDatadirFile.php b/lib/private/Repair/NC30/RemoveLegacyDatadirFile.php new file mode 100644 index 00000000000..623163927bd --- /dev/null +++ b/lib/private/Repair/NC30/RemoveLegacyDatadirFile.php @@ -0,0 +1,32 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Repair\NC30; + +use OCP\IConfig; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; + +class RemoveLegacyDatadirFile implements IRepairStep { + + public function __construct( + private IConfig $config, + ) { + } + + public function getName(): string { + return 'Remove legacy ".ocdata" file'; + } + + public function run(IOutput $output): void { + $ocdata = $this->config->getSystemValueString('datadirectory', \OC::$SERVERROOT . '/data') . '/.ocdata'; + if (file_exists($ocdata)) { + unlink($ocdata); + } + } +} diff --git a/lib/private/Repair/OldGroupMembershipShares.php b/lib/private/Repair/OldGroupMembershipShares.php index d991ef82ac9..003c15cfb88 100644 --- a/lib/private/Repair/OldGroupMembershipShares.php +++ b/lib/private/Repair/OldGroupMembershipShares.php @@ -1,26 +1,10 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Joas Schilling <coding@schilljs.com> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OC\Repair; use OCP\IDBConnection; @@ -30,13 +14,6 @@ use OCP\Migration\IRepairStep; use OCP\Share\IShare; class OldGroupMembershipShares implements IRepairStep { - - /** @var \OCP\IDBConnection */ - protected $connection; - - /** @var \OCP\IGroupManager */ - protected $groupManager; - /** * @var array [gid => [uid => (bool)]] */ @@ -46,9 +23,10 @@ class OldGroupMembershipShares implements IRepairStep { * @param IDBConnection $connection * @param IGroupManager $groupManager */ - public function __construct(IDBConnection $connection, IGroupManager $groupManager) { - $this->connection = $connection; - $this->groupManager = $groupManager; + public function __construct( + protected IDBConnection $connection, + protected IGroupManager $groupManager, + ) { } /** @@ -74,20 +52,20 @@ class OldGroupMembershipShares implements IRepairStep { ->from('share', 's1') ->where($query->expr()->isNotNull('s1.parent')) // \OC\Share\Constant::$shareTypeGroupUserUnique === 2 - ->andWhere($query->expr()->eq('s1.share_type', $query->expr()->literal(2))) - ->andWhere($query->expr()->isNotNull('s2.id')) - ->andWhere($query->expr()->eq('s2.share_type', $query->expr()->literal(IShare::TYPE_GROUP))) + ->andWhere($query->expr()->eq('s1.share_type', $query->expr()->literal(2))) + ->andWhere($query->expr()->isNotNull('s2.id')) + ->andWhere($query->expr()->eq('s2.share_type', $query->expr()->literal(IShare::TYPE_GROUP))) ->leftJoin('s1', 'share', 's2', $query->expr()->eq('s1.parent', 's2.id')); $deleteQuery = $this->connection->getQueryBuilder(); $deleteQuery->delete('share') ->where($query->expr()->eq('id', $deleteQuery->createParameter('share'))); - $result = $query->execute(); + $result = $query->executeQuery(); while ($row = $result->fetch()) { if (!$this->isMember($row['group'], $row['user'])) { - $deletedEntries += $deleteQuery->setParameter('share', (int) $row['id']) - ->execute(); + $deletedEntries += $deleteQuery->setParameter('share', (int)$row['id']) + ->executeStatement(); } } $result->closeCursor(); diff --git a/lib/private/Repair/Owncloud/CleanPreviews.php b/lib/private/Repair/Owncloud/CleanPreviews.php index 267a1be7985..50ee965e087 100644 --- a/lib/private/Repair/Owncloud/CleanPreviews.php +++ b/lib/private/Repair/Owncloud/CleanPreviews.php @@ -1,24 +1,8 @@ <?php + /** - * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Julius Härtl <jus@bitgrid.net> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair\Owncloud; @@ -30,7 +14,6 @@ use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; class CleanPreviews implements IRepairStep { - /** @var IJobList */ private $jobList; @@ -48,8 +31,8 @@ class CleanPreviews implements IRepairStep { * @param IConfig $config */ public function __construct(IJobList $jobList, - IUserManager $userManager, - IConfig $config) { + IUserManager $userManager, + IConfig $config) { $this->jobList = $jobList; $this->userManager = $userManager; $this->config = $config; diff --git a/lib/private/Repair/Owncloud/CleanPreviewsBackgroundJob.php b/lib/private/Repair/Owncloud/CleanPreviewsBackgroundJob.php index 90aa59b2950..6c606453bb9 100644 --- a/lib/private/Repair/Owncloud/CleanPreviewsBackgroundJob.php +++ b/lib/private/Repair/Owncloud/CleanPreviewsBackgroundJob.php @@ -1,72 +1,32 @@ <?php + +declare(strict_types=1); + /** - * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Julius Härtl <jus@bitgrid.net> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair\Owncloud; -use OC\BackgroundJob\QueuedJob; use OCP\AppFramework\Utility\ITimeFactory; use OCP\BackgroundJob\IJobList; +use OCP\BackgroundJob\QueuedJob; use OCP\Files\Folder; use OCP\Files\IRootFolder; use OCP\Files\NotFoundException; use OCP\Files\NotPermittedException; -use OCP\ILogger; use OCP\IUserManager; +use Psr\Log\LoggerInterface; class CleanPreviewsBackgroundJob extends QueuedJob { - /** @var IRootFolder */ - private $rootFolder; - - /** @var ILogger */ - private $logger; - - /** @var IJobList */ - private $jobList; - - /** @var ITimeFactory */ - private $timeFactory; - - /** @var IUserManager */ - private $userManager; - - /** - * CleanPreviewsBackgroundJob constructor. - * - * @param IRootFolder $rootFolder - * @param ILogger $logger - * @param IJobList $jobList - * @param ITimeFactory $timeFactory - * @param IUserManager $userManager - */ - public function __construct(IRootFolder $rootFolder, - ILogger $logger, - IJobList $jobList, - ITimeFactory $timeFactory, - IUserManager $userManager) { - $this->rootFolder = $rootFolder; - $this->logger = $logger; - $this->jobList = $jobList; - $this->timeFactory = $timeFactory; - $this->userManager = $userManager; + public function __construct( + private IRootFolder $rootFolder, + private LoggerInterface $logger, + private IJobList $jobList, + ITimeFactory $timeFactory, + private IUserManager $userManager, + ) { + parent::__construct($timeFactory); } public function run($arguments) { @@ -87,10 +47,9 @@ class CleanPreviewsBackgroundJob extends QueuedJob { } /** - * @param $uid - * @return bool + * @param string $uid */ - private function cleanupPreviews($uid) { + private function cleanupPreviews($uid): bool { try { $userFolder = $this->rootFolder->getUserFolder($uid); } catch (NotFoundException $e) { @@ -108,7 +67,7 @@ class CleanPreviewsBackgroundJob extends QueuedJob { $thumbnails = $thumbnailFolder->getDirectoryListing(); - $start = $this->timeFactory->getTime(); + $start = $this->time->getTime(); foreach ($thumbnails as $thumbnail) { try { $thumbnail->delete(); @@ -116,7 +75,7 @@ class CleanPreviewsBackgroundJob extends QueuedJob { // Ignore } - if (($this->timeFactory->getTime() - $start) > 15) { + if (($this->time->getTime() - $start) > 15) { return false; } } diff --git a/lib/private/Repair/Owncloud/DropAccountTermsTable.php b/lib/private/Repair/Owncloud/DropAccountTermsTable.php index c6e8a9d4645..534825c146a 100644 --- a/lib/private/Repair/Owncloud/DropAccountTermsTable.php +++ b/lib/private/Repair/Owncloud/DropAccountTermsTable.php @@ -1,26 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com> - * - * @author Joas Schilling <coding@schilljs.com> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\Repair\Owncloud; use OCP\IDBConnection; @@ -28,7 +11,6 @@ use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; class DropAccountTermsTable implements IRepairStep { - /** @var IDBConnection */ protected $db; diff --git a/lib/private/Repair/Owncloud/InstallCoreBundle.php b/lib/private/Repair/Owncloud/InstallCoreBundle.php deleted file mode 100644 index 10d5357e5d8..00000000000 --- a/lib/private/Repair/Owncloud/InstallCoreBundle.php +++ /dev/null @@ -1,80 +0,0 @@ -<?php -/** - * @copyright Copyright (c) 2017 Lukas Reschke <lukas@statuscode.ch> - * - * @author Julius Härtl <jus@bitgrid.net> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * - */ - -namespace OC\Repair\Owncloud; - -use OC\App\AppStore\Bundles\BundleFetcher; -use OC\Installer; -use OCP\IConfig; -use OCP\Migration\IOutput; -use OCP\Migration\IRepairStep; - -class InstallCoreBundle implements IRepairStep { - /** @var BundleFetcher */ - private $bundleFetcher; - /** @var IConfig */ - private $config; - /** @var Installer */ - private $installer; - - /** - * @param BundleFetcher $bundleFetcher - * @param IConfig $config - * @param Installer $installer - */ - public function __construct(BundleFetcher $bundleFetcher, - IConfig $config, - Installer $installer) { - $this->bundleFetcher = $bundleFetcher; - $this->config = $config; - $this->installer = $installer; - } - - /** - * {@inheritdoc} - */ - public function getName() { - return 'Install new core bundle components'; - } - - /** - * {@inheritdoc} - */ - public function run(IOutput $output) { - $versionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0'); - - if (version_compare($versionFromBeforeUpdate, '12.0.0.14', '>')) { - return; - } - - $defaultBundle = $this->bundleFetcher->getDefaultInstallationBundle(); - foreach ($defaultBundle as $bundle) { - try { - $this->installer->installAppBundle($bundle); - $output->info('Successfully installed core app bundle.'); - } catch (\Exception $e) { - $output->warning('Could not install core app bundle: ' . $e->getMessage()); - } - } - } -} diff --git a/lib/private/Repair/Owncloud/MigrateOauthTables.php b/lib/private/Repair/Owncloud/MigrateOauthTables.php new file mode 100644 index 00000000000..de26a907e02 --- /dev/null +++ b/lib/private/Repair/Owncloud/MigrateOauthTables.php @@ -0,0 +1,259 @@ +<?php + +/** + * SPDX-FileCopyrightText: 2021 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ + +namespace OC\Repair\Owncloud; + +use OC\Authentication\Token\IProvider as ITokenProvider; +use OC\DB\Connection; +use OC\DB\SchemaWrapper; +use OCA\OAuth2\Db\AccessToken; +use OCA\OAuth2\Db\AccessTokenMapper; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\Authentication\Token\IToken; +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IConfig; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; +use OCP\Security\ICrypto; +use OCP\Security\ISecureRandom; + +class MigrateOauthTables implements IRepairStep { + + public function __construct( + protected Connection $db, + private AccessTokenMapper $accessTokenMapper, + private ITokenProvider $tokenProvider, + private ISecureRandom $random, + private ITimeFactory $timeFactory, + private ICrypto $crypto, + private IConfig $config, + ) { + } + + /** + * @return string + */ + public function getName() { + return 'Migrate oauth2_clients table to nextcloud schema'; + } + + public function run(IOutput $output) { + $schema = new SchemaWrapper($this->db); + if (!$schema->hasTable('oauth2_clients')) { + $output->info('oauth2_clients table does not exist.'); + return; + } + + // Create column and then migrate before handling unique index. + // So that we can distinguish between legacy (from oc) and new rows (from nc). + $table = $schema->getTable('oauth2_access_tokens'); + if (!$table->hasColumn('hashed_code')) { + $output->info('Prepare the oauth2_access_tokens table schema.'); + $table->addColumn('hashed_code', 'string', [ + 'notnull' => true, + 'length' => 128, + ]); + + // Regenerate schema after migrating to it + $this->db->migrateToSchema($schema->getWrappedSchema()); + $schema = new SchemaWrapper($this->db); + } + + $output->info('Update the oauth2_access_tokens table schema.'); + $table = $schema->getTable('oauth2_access_tokens'); + if (!$table->hasColumn('encrypted_token')) { + $table->addColumn('encrypted_token', 'string', [ + 'notnull' => true, + 'length' => 786, + ]); + } + if (!$table->hasIndex('oauth2_access_hash_idx')) { + // Drop legacy access codes first to prevent integrity constraint violations + $qb = $this->db->getQueryBuilder(); + $qb->delete('oauth2_access_tokens') + ->where($qb->expr()->eq('hashed_code', $qb->createNamedParameter(''))); + $qb->executeStatement(); + + $table->addUniqueIndex(['hashed_code'], 'oauth2_access_hash_idx'); + } + if (!$table->hasIndex('oauth2_access_client_id_idx')) { + $table->addIndex(['client_id'], 'oauth2_access_client_id_idx'); + } + if (!$table->hasColumn('token_id')) { + $table->addColumn('token_id', 'integer', [ + 'notnull' => true, + ]); + } + if ($table->hasColumn('expires')) { + $table->dropColumn('expires'); + } + if ($table->hasColumn('user_id')) { + $table->dropColumn('user_id'); + } + if ($table->hasColumn('token')) { + $table->dropColumn('token'); + } + + $output->info('Update the oauth2_clients table schema.'); + $table = $schema->getTable('oauth2_clients'); + if ($table->getColumn('name')->getLength() !== 64) { + // shorten existing values before resizing the column + $qb = $this->db->getQueryBuilder(); + $qb->update('oauth2_clients') + ->set('name', $qb->createParameter('shortenedName')) + ->where($qb->expr()->eq('id', $qb->createParameter('theId'))); + + $qbSelect = $this->db->getQueryBuilder(); + $qbSelect->select('id', 'name') + ->from('oauth2_clients'); + + $result = $qbSelect->executeQuery(); + while ($row = $result->fetch()) { + $id = $row['id']; + $shortenedName = mb_substr($row['name'], 0, 64); + $qb->setParameter('theId', $id, IQueryBuilder::PARAM_INT); + $qb->setParameter('shortenedName', $shortenedName, IQueryBuilder::PARAM_STR); + $qb->executeStatement(); + } + $result->closeCursor(); + + // safely set the new column length + $table->getColumn('name')->setLength(64); + } + if ($table->hasColumn('allow_subdomains')) { + $table->dropColumn('allow_subdomains'); + } + if ($table->hasColumn('trusted')) { + $table->dropColumn('trusted'); + } + + if (!$schema->getTable('oauth2_clients')->hasColumn('client_identifier')) { + $table->addColumn('client_identifier', 'string', [ + 'notnull' => true, + 'length' => 64, + 'default' => '' + ]); + $table->addIndex(['client_identifier'], 'oauth2_client_id_idx'); + } + + // Regenerate schema after migrating to it + $this->db->migrateToSchema($schema->getWrappedSchema()); + $schema = new SchemaWrapper($this->db); + + if ($schema->getTable('oauth2_clients')->hasColumn('identifier')) { + $output->info("Move identifier column's data to the new client_identifier column."); + // 1. Fetch all [id, identifier] couple. + $selectQuery = $this->db->getQueryBuilder(); + $selectQuery->select('id', 'identifier')->from('oauth2_clients'); + $result = $selectQuery->executeQuery(); + $identifiers = $result->fetchAll(); + $result->closeCursor(); + + // 2. Insert them into the client_identifier column. + foreach ($identifiers as ['id' => $id, 'identifier' => $clientIdentifier]) { + $insertQuery = $this->db->getQueryBuilder(); + $insertQuery->update('oauth2_clients') + ->set('client_identifier', $insertQuery->createNamedParameter($clientIdentifier, IQueryBuilder::PARAM_STR)) + ->where($insertQuery->expr()->eq('id', $insertQuery->createNamedParameter($id, IQueryBuilder::PARAM_INT))) + ->executeStatement(); + } + + $output->info('Drop the identifier column.'); + $table = $schema->getTable('oauth2_clients'); + $table->dropColumn('identifier'); + + // Regenerate schema after migrating to it + $this->db->migrateToSchema($schema->getWrappedSchema()); + $schema = new SchemaWrapper($this->db); + } + + $enableOcClients = $this->config->getSystemValueBool('oauth2.enable_oc_clients', false); + if ($enableOcClients) { + $output->info('Delete clients (and their related access tokens) with the redirect_uri starting with oc://'); + } else { + $output->info('Delete clients (and their related access tokens) with the redirect_uri starting with oc:// or ending with *'); + } + // delete the access tokens + $qbDeleteAccessTokens = $this->db->getQueryBuilder(); + + $qbSelectClientId = $this->db->getQueryBuilder(); + $qbSelectClientId->select('id') + ->from('oauth2_clients') + ->where( + $qbSelectClientId->expr()->iLike('redirect_uri', $qbDeleteAccessTokens->createNamedParameter('oc://%', IQueryBuilder::PARAM_STR)) + ); + if (!$enableOcClients) { + $qbSelectClientId->orWhere( + $qbSelectClientId->expr()->iLike('redirect_uri', $qbDeleteAccessTokens->createNamedParameter('%*', IQueryBuilder::PARAM_STR)) + ); + } + + $qbDeleteAccessTokens->delete('oauth2_access_tokens') + ->where( + $qbSelectClientId->expr()->in('client_id', $qbDeleteAccessTokens->createFunction($qbSelectClientId->getSQL()), IQueryBuilder::PARAM_STR_ARRAY) + ); + $qbDeleteAccessTokens->executeStatement(); + + // delete the clients + $qbDeleteClients = $this->db->getQueryBuilder(); + $qbDeleteClients->delete('oauth2_clients') + ->where( + $qbDeleteClients->expr()->iLike('redirect_uri', $qbDeleteClients->createNamedParameter('oc://%', IQueryBuilder::PARAM_STR)) + ); + if (!$enableOcClients) { + $qbDeleteClients->orWhere( + $qbDeleteClients->expr()->iLike('redirect_uri', $qbDeleteClients->createNamedParameter('%*', IQueryBuilder::PARAM_STR)) + ); + } + $qbDeleteClients->executeStatement(); + + // Migrate legacy refresh tokens from oc + if ($schema->hasTable('oauth2_refresh_tokens')) { + $output->info('Migrate legacy oauth2 refresh tokens.'); + + $qbSelect = $this->db->getQueryBuilder(); + $qbSelect->select('*') + ->from('oauth2_refresh_tokens'); + $result = $qbSelect->executeQuery(); + $now = $this->timeFactory->now()->getTimestamp(); + $index = 0; + while ($row = $result->fetch()) { + $clientId = $row['client_id']; + $refreshToken = $row['token']; + + // Insert expired token so that it can be rotated on the next refresh + $accessToken = $this->random->generate(72, ISecureRandom::CHAR_UPPER . ISecureRandom::CHAR_LOWER . ISecureRandom::CHAR_DIGITS); + $authToken = $this->tokenProvider->generateToken( + $accessToken, + $row['user_id'], + $row['user_id'], + null, + "oc_migrated_client{$clientId}_t{$now}_i$index", + IToken::PERMANENT_TOKEN, + IToken::DO_NOT_REMEMBER, + ); + $authToken->setExpires($now - 3600); + $this->tokenProvider->updateToken($authToken); + + $accessTokenEntity = new AccessToken(); + $accessTokenEntity->setTokenId($authToken->getId()); + $accessTokenEntity->setClientId($clientId); + $accessTokenEntity->setHashedCode(hash('sha512', $refreshToken)); + $accessTokenEntity->setEncryptedToken($this->crypto->encrypt($accessToken, $refreshToken)); + $accessTokenEntity->setCodeCreatedAt($now); + $accessTokenEntity->setTokenCount(1); + $this->accessTokenMapper->insert($accessTokenEntity); + + $index++; + } + $result->closeCursor(); + + $schema->dropTable('oauth2_refresh_tokens'); + $schema->performDropTableCalls(); + } + } +} diff --git a/lib/private/Repair/Owncloud/MoveAvatars.php b/lib/private/Repair/Owncloud/MoveAvatars.php index 37c79de22ab..9e3f4b89b13 100644 --- a/lib/private/Repair/Owncloud/MoveAvatars.php +++ b/lib/private/Repair/Owncloud/MoveAvatars.php @@ -1,24 +1,8 @@ <?php + /** - * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Julius Härtl <jus@bitgrid.net> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair\Owncloud; @@ -28,7 +12,6 @@ use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; class MoveAvatars implements IRepairStep { - /** @var IJobList */ private $jobList; @@ -42,7 +25,7 @@ class MoveAvatars implements IRepairStep { * @param IConfig $config */ public function __construct(IJobList $jobList, - IConfig $config) { + IConfig $config) { $this->jobList = $jobList; $this->config = $config; } @@ -60,7 +43,7 @@ class MoveAvatars implements IRepairStep { $output->info('Repair step already executed'); return; } - if ($this->config->getSystemValue('enable_avatars', true) === false) { + if (!$this->config->getSystemValueBool('enable_avatars', true)) { $output->info('Avatars are disabled'); } else { $output->info('Add background job'); diff --git a/lib/private/Repair/Owncloud/MoveAvatarsBackgroundJob.php b/lib/private/Repair/Owncloud/MoveAvatarsBackgroundJob.php index 6cda9424696..e145fb71863 100644 --- a/lib/private/Repair/Owncloud/MoveAvatarsBackgroundJob.php +++ b/lib/private/Repair/Owncloud/MoveAvatarsBackgroundJob.php @@ -1,32 +1,18 @@ <?php + +declare(strict_types=1); + /** - * @copyright 2016 Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Julius Härtl <jus@bitgrid.net> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ namespace OC\Repair\Owncloud; -use OC\BackgroundJob\QueuedJob; +use OCP\AppFramework\Utility\ITimeFactory; +use OCP\BackgroundJob\QueuedJob; use OCP\Files\IRootFolder; use OCP\Files\NotFoundException; -use OCP\Files\Storage; +use OCP\Files\Storage\IStorage; use OCP\IAvatarManager; use OCP\IUser; use OCP\IUserManager; @@ -34,23 +20,16 @@ use Psr\Log\LoggerInterface; use function is_resource; class MoveAvatarsBackgroundJob extends QueuedJob { + private ?IStorage $owncloudAvatarStorage = null; - /** @var IUserManager */ - private $userManager; - - /** @var LoggerInterface */ - private $logger; - - /** @var IAvatarManager */ - private $avatarManager; - - /** @var Storage */ - private $owncloudAvatarStorage; - - public function __construct(IUserManager $userManager, LoggerInterface $logger, IAvatarManager $avatarManager, IRootFolder $rootFolder) { - $this->userManager = $userManager; - $this->logger = $logger; - $this->avatarManager = $avatarManager; + public function __construct( + private IUserManager $userManager, + private LoggerInterface $logger, + private IAvatarManager $avatarManager, + private IRootFolder $rootFolder, + ITimeFactory $time, + ) { + parent::__construct($time); try { $this->owncloudAvatarStorage = $rootFolder->get('avatars')->getStorage(); } catch (\Exception $e) { @@ -70,7 +49,7 @@ class MoveAvatarsBackgroundJob extends QueuedJob { } $counter = 0; - $this->userManager->callForSeenUsers(function (IUser $user) use ($counter) { + $this->userManager->callForSeenUsers(function (IUser $user) use (&$counter) { $uid = $user->getUID(); $path = 'avatars/' . $this->buildOwnCloudAvatarPath($uid); diff --git a/lib/private/Repair/Owncloud/SaveAccountsTableData.php b/lib/private/Repair/Owncloud/SaveAccountsTableData.php index f11397d7ae5..ab1560ddb8d 100644 --- a/lib/private/Repair/Owncloud/SaveAccountsTableData.php +++ b/lib/private/Repair/Owncloud/SaveAccountsTableData.php @@ -1,28 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2017 Joas Schilling <coding@schilljs.com> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2017 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\Repair\Owncloud; use OCP\DB\QueryBuilder\IQueryBuilder; @@ -80,7 +61,7 @@ class SaveAccountsTableData implements IRepairStep { } // oc_persistent_locks will be removed later on anyways so we can just drop and ignore any foreign key constraints here - $tableName = $this->config->getSystemValue('dbtableprefix', 'oc_') . 'persistent_locks'; + $tableName = $this->config->getSystemValueString('dbtableprefix', 'oc_') . 'persistent_locks'; $schema = $this->db->createSchema(); $table = $schema->getTable($tableName); foreach ($table->getForeignKeys() as $foreignKey) { @@ -100,7 +81,7 @@ class SaveAccountsTableData implements IRepairStep { */ protected function shouldRun() { $schema = $this->db->createSchema(); - $prefix = $this->config->getSystemValue('dbtableprefix', 'oc_'); + $prefix = $this->config->getSystemValueString('dbtableprefix', 'oc_'); $tableName = $prefix . 'accounts'; if (!$schema->hasTable($tableName)) { @@ -170,7 +151,7 @@ class SaveAccountsTableData implements IRepairStep { * @throws \UnexpectedValueException */ protected function migrateUserInfo(IQueryBuilder $update, $userdata) { - $state = (int) $userdata['state']; + $state = (int)$userdata['state']; if ($state === 3) { // Deleted user, ignore return; diff --git a/lib/private/Repair/Owncloud/UpdateLanguageCodes.php b/lib/private/Repair/Owncloud/UpdateLanguageCodes.php index cd923b4e802..8d9046ad49f 100644 --- a/lib/private/Repair/Owncloud/UpdateLanguageCodes.php +++ b/lib/private/Repair/Owncloud/UpdateLanguageCodes.php @@ -1,26 +1,9 @@ <?php + /** - * @copyright Copyright (c) 2016 Morris Jobke <hey@morrisjobke.de> - * - * @author Julius Härtl <jus@bitgrid.net> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2016 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\Repair\Owncloud; use OCP\DB\QueryBuilder\IQueryBuilder; @@ -41,7 +24,7 @@ class UpdateLanguageCodes implements IRepairStep { * @param IConfig $config */ public function __construct(IDBConnection $connection, - IConfig $config) { + IConfig $config) { $this->connection = $connection; $this->config = $config; } @@ -57,7 +40,7 @@ class UpdateLanguageCodes implements IRepairStep { * {@inheritdoc} */ public function run(IOutput $output) { - $versionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0'); + $versionFromBeforeUpdate = $this->config->getSystemValueString('version', '0.0.0'); if (version_compare($versionFromBeforeUpdate, '12.0.0.13', '>')) { return; diff --git a/lib/private/Repair/RemoveBrokenProperties.php b/lib/private/Repair/RemoveBrokenProperties.php new file mode 100644 index 00000000000..85939b39e5e --- /dev/null +++ b/lib/private/Repair/RemoveBrokenProperties.php @@ -0,0 +1,68 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Repair; + +use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IDBConnection; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; + +class RemoveBrokenProperties implements IRepairStep { + /** + * RemoveBrokenProperties constructor. + * + * @param IDBConnection $db + */ + public function __construct( + private IDBConnection $db, + ) { + } + + /** + * @inheritdoc + */ + public function getName() { + return 'Remove broken DAV object properties'; + } + + /** + * @inheritdoc + */ + public function run(IOutput $output) { + // retrieve all object properties + $qb = $this->db->getQueryBuilder(); + $qb->select('id', 'propertyvalue') + ->from('properties') + ->where($qb->expr()->eq('valuetype', $qb->createNamedParameter('3', IQueryBuilder::PARAM_INT), IQueryBuilder::PARAM_INT)); + $result = $qb->executeQuery(); + // find broken object properties + $brokenIds = []; + while ($entry = $result->fetch()) { + if (!empty($entry['propertyvalue'])) { + $object = @unserialize(str_replace('\x00', chr(0), $entry['propertyvalue'])); + if ($object === false) { + $brokenIds[] = $entry['id']; + } + } else { + $brokenIds[] = $entry['id']; + } + } + $result->closeCursor(); + // delete broken object properties + $qb = $this->db->getQueryBuilder(); + $qb->delete('properties') + ->where($qb->expr()->in('id', $qb->createParameter('ids'), IQueryBuilder::PARAM_STR_ARRAY)); + foreach (array_chunk($brokenIds, 1000) as $chunkIds) { + $qb->setParameter('ids', $chunkIds, IQueryBuilder::PARAM_STR_ARRAY); + $qb->executeStatement(); + } + $total = count($brokenIds); + $output->info("$total broken object properties removed"); + } +} diff --git a/lib/private/Repair/RemoveLinkShares.php b/lib/private/Repair/RemoveLinkShares.php index 32cbd1f73f8..a07ebdb72c3 100644 --- a/lib/private/Repair/RemoveLinkShares.php +++ b/lib/private/Repair/RemoveLinkShares.php @@ -3,31 +3,9 @@ declare(strict_types=1); /** - * @copyright Copyright (c) 2019, Roeland Jago Douma <roeland@famdouma.nl> - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Daniel Kesselberg <mail@danielkesselberg.de> - * @author Joas Schilling <coding@schilljs.com> - * @author John Molakvoæ (skjnldsv) <skjnldsv@protonmail.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2019 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\Repair; use OCP\AppFramework\Utility\ITimeFactory; @@ -41,38 +19,24 @@ use OCP\Migration\IRepairStep; use OCP\Notification\IManager; class RemoveLinkShares implements IRepairStep { - /** @var IDBConnection */ - private $connection; - /** @var IConfig */ - private $config; /** @var string[] */ private $userToNotify = []; - /** @var IGroupManager */ - private $groupManager; - /** @var IManager */ - private $notificationManager; - /** @var ITimeFactory */ - private $timeFactory; - - public function __construct(IDBConnection $connection, - IConfig $config, - IGroupManager $groupManager, - IManager $notificationManager, - ITimeFactory $timeFactory) { - $this->connection = $connection; - $this->config = $config; - $this->groupManager = $groupManager; - $this->notificationManager = $notificationManager; - $this->timeFactory = $timeFactory; - } + public function __construct( + private IDBConnection $connection, + private IConfig $config, + private IGroupManager $groupManager, + private IManager $notificationManager, + private ITimeFactory $timeFactory, + ) { + } public function getName(): string { return 'Remove potentially over exposing share links'; } private function shouldRun(): bool { - $versionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0'); + $versionFromBeforeUpdate = $this->config->getSystemValueString('version', '0.0.0'); if (version_compare($versionFromBeforeUpdate, '14.0.11', '<')) { return true; @@ -96,7 +60,7 @@ class RemoveLinkShares implements IRepairStep { $qb = $this->connection->getQueryBuilder(); $qb->delete('share') ->where($qb->expr()->eq('id', $qb->createNamedParameter($id))); - $qb->execute(); + $qb->executeStatement(); } /** @@ -127,13 +91,13 @@ class RemoveLinkShares implements IRepairStep { $query = $this->connection->getQueryBuilder(); $query->select($query->func()->count('*', 'total')) ->from('share') - ->where($query->expr()->in('id', $query->createFunction('(' . $subQuery->getSQL() . ')'))); + ->where($query->expr()->in('id', $query->createFunction($subQuery->getSQL()))); - $result = $query->execute(); + $result = $query->executeQuery(); $data = $result->fetch(); $result->closeCursor(); - return (int) $data['total']; + return (int)$data['total']; } /** @@ -159,7 +123,7 @@ class RemoveLinkShares implements IRepairStep { )) ->andWhere($query->expr()->eq('s1.item_source', 's2.item_source')); /** @var IResult $result */ - $result = $query->execute(); + $result = $query->executeQuery(); return $result; } @@ -202,7 +166,7 @@ class RemoveLinkShares implements IRepairStep { $users = array_keys($this->userToNotify); foreach ($users as $user) { - $notification->setUser((string) $user); + $notification->setUser((string)$user); $this->notificationManager->notify($notification); } } @@ -218,7 +182,7 @@ class RemoveLinkShares implements IRepairStep { $output->finishProgress(); $shareResult->closeCursor(); - // Notifiy all admins + // Notify all admins $adminGroup = $this->groupManager->get('admin'); $adminUsers = $adminGroup->getUsers(); foreach ($adminUsers as $user) { diff --git a/lib/private/Repair/RepairDavShares.php b/lib/private/Repair/RepairDavShares.php index 64104c1e28a..557e2c080ca 100644 --- a/lib/private/Repair/RepairDavShares.php +++ b/lib/private/Repair/RepairDavShares.php @@ -1,28 +1,11 @@ <?php declare(strict_types=1); + /** - * @copyright Copyright (c) 2020 Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * - * @license GNU AGPL version 3 or any later version - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of the - * License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License - * along with this program. If not, see <http://www.gnu.org/licenses/>. - * + * SPDX-FileCopyrightText: 2020 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later */ - namespace OC\Repair; use OCP\DB\Exception; @@ -40,27 +23,15 @@ use function urlencode; class RepairDavShares implements IRepairStep { protected const GROUP_PRINCIPAL_PREFIX = 'principals/groups/'; - /** @var IConfig */ - private $config; - /** @var IDBConnection */ - private $dbc; - /** @var IGroupManager */ - private $groupManager; - /** @var LoggerInterface */ - private $logger; /** @var bool */ private $hintInvalidShares = false; public function __construct( - IConfig $config, - IDBConnection $dbc, - IGroupManager $groupManager, - LoggerInterface $logger + private IConfig $config, + private IDBConnection $dbc, + private IGroupManager $groupManager, + private LoggerInterface $logger, ) { - $this->config = $config; - $this->dbc = $dbc; - $this->groupManager = $groupManager; - $this->logger = $logger; } /** @@ -125,8 +96,8 @@ class RepairDavShares implements IRepairStep { * @inheritDoc */ public function run(IOutput $output) { - $versionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0'); - if (version_compare($versionFromBeforeUpdate, '20.0.7', '<') + $versionFromBeforeUpdate = $this->config->getSystemValueString('version', '0.0.0'); + if (version_compare($versionFromBeforeUpdate, '20.0.8', '<') && $this->repairUnencodedGroupShares() ) { $output->info('Repaired DAV group shares'); diff --git a/lib/private/Repair/RepairInvalidShares.php b/lib/private/Repair/RepairInvalidShares.php index bc03374ff98..9553f25ee70 100644 --- a/lib/private/Repair/RepairInvalidShares.php +++ b/lib/private/Repair/RepairInvalidShares.php @@ -1,31 +1,14 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Lukas Reschke <lukas@statuscode.ch> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2016-2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-FileCopyrightText: 2016 ownCloud, Inc. + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OC\Repair; +use OCP\IConfig; +use OCP\IDBConnection; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; @@ -35,19 +18,10 @@ use OCP\Migration\IRepairStep; class RepairInvalidShares implements IRepairStep { public const CHUNK_SIZE = 200; - /** @var \OCP\IConfig */ - protected $config; - - /** @var \OCP\IDBConnection */ - protected $connection; - - /** - * @param \OCP\IConfig $config - * @param \OCP\IDBConnection $connection - */ - public function __construct($config, $connection) { - $this->connection = $connection; - $this->config = $config; + public function __construct( + protected IConfig $config, + protected IDBConnection $connection, + ) { } public function getName() { @@ -68,7 +42,7 @@ class RepairInvalidShares implements IRepairStep { ->where($builder->expr()->eq('item_type', $builder->expr()->literal('file'))) ->andWhere($builder->expr()->neq('permissions', $permsFunc)); - $updatedEntries = $builder->execute(); + $updatedEntries = $builder->executeStatement(); if ($updatedEntries > 0) { $out->info('Fixed file share permissions for ' . $updatedEntries . ' shares'); } @@ -84,7 +58,7 @@ class RepairInvalidShares implements IRepairStep { $query->select('s1.parent') ->from('share', 's1') ->where($query->expr()->isNotNull('s1.parent')) - ->andWhere($query->expr()->isNull('s2.id')) + ->andWhere($query->expr()->isNull('s2.id')) ->leftJoin('s1', 'share', 's2', $query->expr()->eq('s1.parent', 's2.id')) ->groupBy('s1.parent') ->setMaxResults(self::CHUNK_SIZE); @@ -96,11 +70,11 @@ class RepairInvalidShares implements IRepairStep { $deletedInLastChunk = self::CHUNK_SIZE; while ($deletedInLastChunk === self::CHUNK_SIZE) { $deletedInLastChunk = 0; - $result = $query->execute(); + $result = $query->executeQuery(); while ($row = $result->fetch()) { $deletedInLastChunk++; - $deletedEntries += $deleteQuery->setParameter('parent', (int) $row['parent']) - ->execute(); + $deletedEntries += $deleteQuery->setParameter('parent', (int)$row['parent']) + ->executeStatement(); } $result->closeCursor(); } @@ -111,7 +85,7 @@ class RepairInvalidShares implements IRepairStep { } public function run(IOutput $out) { - $ocVersionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0'); + $ocVersionFromBeforeUpdate = $this->config->getSystemValueString('version', '0.0.0'); if (version_compare($ocVersionFromBeforeUpdate, '12.0.0.11', '<')) { $this->adjustFileSharePermissions($out); } diff --git a/lib/private/Repair/RepairLogoDimension.php b/lib/private/Repair/RepairLogoDimension.php new file mode 100644 index 00000000000..854aeb3ab07 --- /dev/null +++ b/lib/private/Repair/RepairLogoDimension.php @@ -0,0 +1,82 @@ +<?php + +declare(strict_types=1); + +/** + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-or-later + */ +namespace OC\Repair; + +use OCA\Theming\ImageManager; +use OCP\Files\NotFoundException; +use OCP\Files\NotPermittedException; +use OCP\IConfig; +use OCP\Migration\IOutput; +use OCP\Migration\IRepairStep; +use OCP\Server; + +class RepairLogoDimension implements IRepairStep { + public function __construct( + protected IConfig $config, + ) { + } + + public function getName(): string { + return 'Cache logo dimension to fix size in emails on Outlook'; + } + + public function run(IOutput $output): void { + $logoDimensions = $this->config->getAppValue('theming', 'logoDimensions'); + if (preg_match('/^\d+x\d+$/', $logoDimensions)) { + $output->info('Logo dimensions are already known'); + return; + } + + try { + /** @var ImageManager $imageManager */ + $imageManager = Server::get(ImageManager::class); + } catch (\Throwable) { + $output->info('Theming is disabled'); + return; + } + + if (!$imageManager->hasImage('logo')) { + $output->info('Theming is not used to provide a logo'); + return; + } + + try { + try { + $simpleFile = $imageManager->getImage('logo', false); + $image = @imagecreatefromstring($simpleFile->getContent()); + } catch (NotFoundException|NotPermittedException) { + $simpleFile = $imageManager->getImage('logo'); + $image = false; + } + } catch (NotFoundException|NotPermittedException) { + $output->info('Theming is not used to provide a logo'); + return; + } + + $dimensions = ''; + if ($image !== false) { + $dimensions = imagesx($image) . 'x' . imagesy($image); + } elseif (str_starts_with($simpleFile->getMimeType(), 'image/svg')) { + $matched = preg_match('/viewbox=["\']\d* \d* (\d*\.?\d*) (\d*\.?\d*)["\']/i', $simpleFile->getContent(), $matches); + if ($matched) { + $dimensions = $matches[1] . 'x' . $matches[2]; + } + } + + if (!$dimensions) { + $output->warning('Failed to read dimensions from logo'); + $this->config->deleteAppValue('theming', 'logoDimensions'); + return; + } + + $dimensions = imagesx($image) . 'x' . imagesy($image); + $this->config->setAppValue('theming', 'logoDimensions', $dimensions); + $output->info('Updated logo dimensions: ' . $dimensions); + } +} diff --git a/lib/private/Repair/RepairMimeTypes.php b/lib/private/Repair/RepairMimeTypes.php index d0e3bbff707..3c9720b9e91 100644 --- a/lib/private/Repair/RepairMimeTypes.php +++ b/lib/private/Repair/RepairMimeTypes.php @@ -1,65 +1,48 @@ <?php + /** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Arthur Schiwon <blizzz@arthur-schiwon.de> - * @author Christoph Wurst <christoph@winzerhof-wurst.at> - * @author Joas Schilling <coding@schilljs.com> - * @author Julius Härtl <jus@bitgrid.net> - * @author Morris Jobke <hey@morrisjobke.de> - * @author nik gaffney <nik@fo.am> - * @author Olivier Paroz <github@oparoz.com> - * @author Rello <Rello@users.noreply.github.com> - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Stefan Weil <sw@weilnetz.de> - * @author Thomas Ebert <thomas.ebert@usability.de> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * + * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors + * SPDX-License-Identifier: AGPL-3.0-only */ - namespace OC\Repair; +use OC\Migration\NullOutput; +use OCP\DB\Exception; +use OCP\DB\IResult; use OCP\DB\QueryBuilder\IQueryBuilder; +use OCP\IAppConfig; use OCP\IConfig; use OCP\IDBConnection; use OCP\Migration\IOutput; use OCP\Migration\IRepairStep; class RepairMimeTypes implements IRepairStep { - /** @var IConfig */ - protected $config; - /** @var IDBConnection */ - protected $connection; + private bool $dryRun = false; + private int $changeCount = 0; /** @var int */ - protected $folderMimeTypeId; + protected int $folderMimeTypeId; - public function __construct(IConfig $config, - IDBConnection $connection) { - $this->config = $config; - $this->connection = $connection; + public function __construct( + protected IConfig $config, + protected IAppConfig $appConfig, + protected IDBConnection $connection, + ) { } - public function getName() { + public function getName(): string { return 'Repair mime types'; } - private function updateMimetypes($updatedMimetypes) { + /** + * @throws Exception + */ + private function updateMimetypes($updatedMimetypes): IResult|int|null { + if ($this->dryRun) { + $this->changeCount += count($updatedMimetypes); + return null; + } + $query = $this->connection->getQueryBuilder(); $query->select('id') ->from('mimetypes') @@ -77,6 +60,7 @@ class RepairMimeTypes implements IRepairStep { $update = $this->connection->getQueryBuilder(); $update->update('filecache') + ->runAcrossAllShards() ->set('mimetype', $update->createParameter('mimetype')) ->where($update->expr()->neq('mimetype', $update->createParameter('mimetype'), IQueryBuilder::PARAM_INT)) ->andWhere($update->expr()->neq('mimetype', $update->createParameter('folder'), IQueryBuilder::PARAM_INT)) @@ -107,7 +91,11 @@ class RepairMimeTypes implements IRepairStep { return $count; } - private function introduceImageTypes() { + /** + * @throws Exception + * @since 12.0.0.14 + */ + private function introduceImageTypes(): IResult|int|null { $updatedMimetypes = [ 'jp2' => 'image/jp2', 'webp' => 'image/webp', @@ -116,7 +104,11 @@ class RepairMimeTypes implements IRepairStep { return $this->updateMimetypes($updatedMimetypes); } - private function introduceWindowsProgramTypes() { + /** + * @throws Exception + * @since 12.0.0.13 + */ + private function introduceWindowsProgramTypes(): IResult|int|null { $updatedMimetypes = [ 'htaccess' => 'text/plain', 'bat' => 'application/x-msdos-program', @@ -126,7 +118,11 @@ class RepairMimeTypes implements IRepairStep { return $this->updateMimetypes($updatedMimetypes); } - private function introduceLocationTypes() { + /** + * @throws Exception + * @since 13.0.0.0 + */ + private function introduceLocationTypes(): IResult|int|null { $updatedMimetypes = [ 'gpx' => 'application/gpx+xml', 'kml' => 'application/vnd.google-earth.kml+xml', @@ -137,7 +133,11 @@ class RepairMimeTypes implements IRepairStep { return $this->updateMimetypes($updatedMimetypes); } - private function introduceInternetShortcutTypes() { + /** + * @throws Exception + * @since 13.0.0.3 + */ + private function introduceInternetShortcutTypes(): IResult|int|null { $updatedMimetypes = [ 'url' => 'application/internet-shortcut', 'webloc' => 'application/internet-shortcut' @@ -146,7 +146,11 @@ class RepairMimeTypes implements IRepairStep { return $this->updateMimetypes($updatedMimetypes); } - private function introduceStreamingTypes() { + /** + * @throws Exception + * @since 13.0.0.6 + */ + private function introduceStreamingTypes(): IResult|int|null { $updatedMimetypes = [ 'm3u' => 'audio/mpegurl', 'm3u8' => 'audio/mpegurl', @@ -156,7 +160,11 @@ class RepairMimeTypes implements IRepairStep { return $this->updateMimetypes($updatedMimetypes); } - private function introduceVisioTypes() { + /** + * @throws Exception + * @since 14.0.0.8 + */ + private function introduceVisioTypes(): IResult|int|null { $updatedMimetypes = [ 'vsdm' => 'application/vnd.visio', 'vsdx' => 'application/vnd.visio', @@ -169,7 +177,11 @@ class RepairMimeTypes implements IRepairStep { return $this->updateMimetypes($updatedMimetypes); } - private function introduceComicbookTypes() { + /** + * @throws Exception + * @since 14.0.0.10 + */ + private function introduceComicbookTypes(): IResult|int|null { $updatedMimetypes = [ 'cb7' => 'application/comicbook+7z', 'cba' => 'application/comicbook+ace', @@ -182,7 +194,11 @@ class RepairMimeTypes implements IRepairStep { return $this->updateMimetypes($updatedMimetypes); } - private function introduceOpenDocumentTemplates() { + /** + * @throws Exception + * @since 20.0.0.5 + */ + private function introduceOpenDocumentTemplates(): IResult|int|null { $updatedMimetypes = [ 'ott' => 'application/vnd.oasis.opendocument.text-template', 'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template', @@ -193,7 +209,11 @@ class RepairMimeTypes implements IRepairStep { return $this->updateMimetypes($updatedMimetypes); } - private function introduceOrgModeType() { + /** + * @throws Exception + * @since 21.0.0.7 + */ + private function introduceOrgModeType(): IResult|int|null { $updatedMimetypes = [ 'org' => 'text/org' ]; @@ -201,50 +221,253 @@ class RepairMimeTypes implements IRepairStep { return $this->updateMimetypes($updatedMimetypes); } + /** + * @throws Exception + * @since 23.0.0.2 + */ + private function introduceFlatOpenDocumentType(): IResult|int|null { + $updatedMimetypes = [ + 'fodt' => 'application/vnd.oasis.opendocument.text-flat-xml', + 'fods' => 'application/vnd.oasis.opendocument.spreadsheet-flat-xml', + 'fodg' => 'application/vnd.oasis.opendocument.graphics-flat-xml', + 'fodp' => 'application/vnd.oasis.opendocument.presentation-flat-xml', + ]; + + return $this->updateMimetypes($updatedMimetypes); + } + + /** + * @throws Exception + * @since 25.0.0.2 + */ + private function introduceOnlyofficeFormType(): IResult|int|null { + $updatedMimetypes = [ + 'oform' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document.oform', + 'docxf' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document.docxf', + ]; + + return $this->updateMimetypes($updatedMimetypes); + } + + /** + * @throws Exception + * @since 26.0.0.1 + */ + private function introduceAsciidocType(): IResult|int|null { + $updatedMimetypes = [ + 'adoc' => 'text/asciidoc', + 'asciidoc' => 'text/asciidoc', + ]; + + return $this->updateMimetypes($updatedMimetypes); + } + + /** + * @throws Exception + * @since 28.0.0.5 + */ + private function introduceEnhancedMetafileFormatType(): IResult|int|null { + $updatedMimetypes = [ + 'emf' => 'image/emf', + ]; + + return $this->updateMimetypes($updatedMimetypes); + } + + /** + * @throws Exception + * @since 29.0.0.2 + */ + private function introduceEmlAndMsgFormatType(): IResult|int|null { + $updatedMimetypes = [ + 'eml' => 'message/rfc822', + 'msg' => 'application/vnd.ms-outlook', + ]; + + return $this->updateMimetypes($updatedMimetypes); + } + + /** + * @throws Exception + * @since 29.0.0.6 + */ + private function introduceAacAudioType(): IResult|int|null { + $updatedMimetypes = [ + 'aac' => 'audio/aac', + ]; + + return $this->updateMimetypes($updatedMimetypes); + } + + /** + * @throws Exception + * @since 29.0.10 + */ + private function introduceReStructuredTextFormatType(): IResult|int|null { + $updatedMimetypes = [ + 'rst' => 'text/x-rst', + ]; + + return $this->updateMimetypes($updatedMimetypes); + } + + /** + * @throws Exception + * @since 30.0.0 + */ + private function introduceExcalidrawType(): IResult|int|null { + $updatedMimetypes = [ + 'excalidraw' => 'application/vnd.excalidraw+json', + ]; + + return $this->updateMimetypes($updatedMimetypes); + } + + + /** + * @throws Exception + * @since 31.0.0 + */ + private function introduceZstType(): IResult|int|null { + $updatedMimetypes = [ + 'zst' => 'application/zstd', + 'nfo' => 'text/x-nfo', + ]; + + return $this->updateMimetypes($updatedMimetypes); + } + + /** + * @throws Exception + * @since 32.0.0 + */ + private function introduceMusicxmlType(): IResult|int|null { + $updatedMimetypes = [ + 'mxl' => 'application/vnd.recordare.musicxml', + 'musicxml' => 'application/vnd.recordare.musicxml+xml', + ]; + + return $this->updateMimetypes($updatedMimetypes); + } + + + + /** + * Check if there are any migrations available + * + * @throws Exception + */ + public function migrationsAvailable(): bool { + $this->dryRun = true; + $this->run(new NullOutput()); + $this->dryRun = false; + return $this->changeCount > 0; + } + + /** + * Get the current mimetype version + */ + private function getMimeTypeVersion(): string { + $serverVersion = $this->config->getSystemValueString('version', '0.0.0'); + // 29.0.0.10 is the last version with a mimetype migration before it was moved to a separate version number + if (version_compare($serverVersion, '29.0.0.10', '>')) { + return $this->appConfig->getValueString('files', 'mimetype_version', '29.0.0.10'); + } + + return $serverVersion; + } /** * Fix mime types + * + * @throws Exception */ - public function run(IOutput $out) { - $ocVersionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0'); + public function run(IOutput $out): void { + $serverVersion = $this->config->getSystemValueString('version', '0.0.0'); + $mimeTypeVersion = $this->getMimeTypeVersion(); // NOTE TO DEVELOPERS: when adding new mime types, please make sure to // add a version comparison to avoid doing it every time + // PLEASE ALSO KEEP THE LIST SORTED BY VERSION NUMBER - if (version_compare($ocVersionFromBeforeUpdate, '12.0.0.14', '<') && $this->introduceImageTypes()) { + if (version_compare($mimeTypeVersion, '12.0.0.14', '<') && $this->introduceImageTypes()) { $out->info('Fixed image mime types'); } - if (version_compare($ocVersionFromBeforeUpdate, '12.0.0.13', '<') && $this->introduceWindowsProgramTypes()) { + if (version_compare($mimeTypeVersion, '12.0.0.13', '<') && $this->introduceWindowsProgramTypes()) { $out->info('Fixed windows program mime types'); } - if (version_compare($ocVersionFromBeforeUpdate, '13.0.0.0', '<') && $this->introduceLocationTypes()) { + if (version_compare($mimeTypeVersion, '13.0.0.0', '<') && $this->introduceLocationTypes()) { $out->info('Fixed geospatial mime types'); } - if (version_compare($ocVersionFromBeforeUpdate, '13.0.0.3', '<') && $this->introduceInternetShortcutTypes()) { + if (version_compare($mimeTypeVersion, '13.0.0.3', '<') && $this->introduceInternetShortcutTypes()) { $out->info('Fixed internet-shortcut mime types'); } - if (version_compare($ocVersionFromBeforeUpdate, '13.0.0.6', '<') && $this->introduceStreamingTypes()) { + if (version_compare($mimeTypeVersion, '13.0.0.6', '<') && $this->introduceStreamingTypes()) { $out->info('Fixed streaming mime types'); } - if (version_compare($ocVersionFromBeforeUpdate, '14.0.0.8', '<') && $this->introduceVisioTypes()) { + if (version_compare($mimeTypeVersion, '14.0.0.8', '<') && $this->introduceVisioTypes()) { $out->info('Fixed visio mime types'); } - if (version_compare($ocVersionFromBeforeUpdate, '14.0.0.10', '<') && $this->introduceComicbookTypes()) { + if (version_compare($mimeTypeVersion, '14.0.0.10', '<') && $this->introduceComicbookTypes()) { $out->info('Fixed comicbook mime types'); } - if (version_compare($ocVersionFromBeforeUpdate, '20.0.0.5', '<') && $this->introduceOpenDocumentTemplates()) { + if (version_compare($mimeTypeVersion, '20.0.0.5', '<') && $this->introduceOpenDocumentTemplates()) { $out->info('Fixed OpenDocument template mime types'); } - if (version_compare($ocVersionFromBeforeUpdate, '21.0.0.7', '<') && $this->introduceOrgModeType()) { + if (version_compare($mimeTypeVersion, '21.0.0.7', '<') && $this->introduceOrgModeType()) { $out->info('Fixed orgmode mime types'); } + + if (version_compare($mimeTypeVersion, '23.0.0.2', '<') && $this->introduceFlatOpenDocumentType()) { + $out->info('Fixed Flat OpenDocument mime types'); + } + + if (version_compare($mimeTypeVersion, '25.0.0.2', '<') && $this->introduceOnlyofficeFormType()) { + $out->info('Fixed ONLYOFFICE Forms OpenXML mime types'); + } + + if (version_compare($mimeTypeVersion, '26.0.0.1', '<') && $this->introduceAsciidocType()) { + $out->info('Fixed AsciiDoc mime types'); + } + + if (version_compare($mimeTypeVersion, '28.0.0.5', '<') && $this->introduceEnhancedMetafileFormatType()) { + $out->info('Fixed Enhanced Metafile Format mime types'); + } + + if (version_compare($mimeTypeVersion, '29.0.0.2', '<') && $this->introduceEmlAndMsgFormatType()) { + $out->info('Fixed eml and msg mime type'); + } + + if (version_compare($mimeTypeVersion, '29.0.0.6', '<') && $this->introduceAacAudioType()) { + $out->info('Fixed aac mime type'); + } + + if (version_compare($mimeTypeVersion, '29.0.0.10', '<') && $this->introduceReStructuredTextFormatType()) { + $out->info('Fixed ReStructured Text mime type'); + } + + if (version_compare($mimeTypeVersion, '30.0.0.0', '<') && $this->introduceExcalidrawType()) { + $out->info('Fixed Excalidraw mime type'); + } + + if (version_compare($mimeTypeVersion, '31.0.0.0', '<') && $this->introduceZstType()) { + $out->info('Fixed zst mime type'); + } + + if (version_compare($mimeTypeVersion, '32.0.0.0', '<') && $this->introduceMusicxmlType()) { + $out->info('Fixed musicxml mime type'); + } + + if (!$this->dryRun) { + $this->appConfig->setValueString('files', 'mimetype_version', $serverVersion); + } } } diff --git a/lib/private/Repair/SqliteAutoincrement.php b/lib/private/Repair/SqliteAutoincrement.php deleted file mode 100644 index 651a3c144f3..00000000000 --- a/lib/private/Repair/SqliteAutoincrement.php +++ /dev/null @@ -1,101 +0,0 @@ -<?php -/** - * @copyright Copyright (c) 2016, ownCloud, Inc. - * - * @author Roeland Jago Douma <roeland@famdouma.nl> - * @author Thomas Müller <thomas.mueller@tmit.eu> - * @author Vincent Petry <vincent@nextcloud.com> - * - * @license AGPL-3.0 - * - * This code is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License, version 3, - * as published by the Free Software Foundation. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * You should have received a copy of the GNU Affero General Public License, version 3, - * along with this program. If not, see <http://www.gnu.org/licenses/> - * - */ - -namespace OC\Repair; - -use Doctrine\DBAL\Platforms\SqlitePlatform; -use Doctrine\DBAL\Schema\ColumnDiff; -use Doctrine\DBAL\Schema\SchemaDiff; -use Doctrine\DBAL\Schema\SchemaException; -use Doctrine\DBAL\Schema\TableDiff; -use OCP\Migration\IOutput; -use OCP\Migration\IRepairStep; - -/** - * Fixes Sqlite autoincrement by forcing the SQLite table schemas to be - * altered in order to retrigger SQL schema generation through OCSqlitePlatform. - */ -class SqliteAutoincrement implements IRepairStep { - /** - * @var \OC\DB\Connection - */ - protected $connection; - - /** - * @param \OC\DB\Connection $connection - */ - public function __construct($connection) { - $this->connection = $connection; - } - - public function getName() { - return 'Repair SQLite autoincrement'; - } - - /** - * Fix mime types - */ - public function run(IOutput $out) { - if (!$this->connection->getDatabasePlatform() instanceof SqlitePlatform) { - return; - } - - $sourceSchema = $this->connection->getSchemaManager()->createSchema(); - - $schemaDiff = new SchemaDiff(); - - foreach ($sourceSchema->getTables() as $tableSchema) { - $primaryKey = $tableSchema->getPrimaryKey(); - if (!$primaryKey) { - continue; - } - - $columnNames = $primaryKey->getColumns(); - - // add a column diff for every primary key column, - // but do not actually change anything, this will - // force the generation of SQL statements to alter - // those tables, which will then trigger the - // specific SQL code from OCSqlitePlatform - try { - $tableDiff = new TableDiff($tableSchema->getName()); - $tableDiff->fromTable = $tableSchema; - foreach ($columnNames as $columnName) { - $columnSchema = $tableSchema->getColumn($columnName); - $columnDiff = new ColumnDiff($columnSchema->getName(), $columnSchema); - $tableDiff->changedColumns[] = $columnDiff; - $schemaDiff->changedTables[] = $tableDiff; - } - } catch (SchemaException $e) { - // ignore - } - } - - $this->connection->beginTransaction(); - foreach ($schemaDiff->toSql($this->connection->getDatabasePlatform()) as $sql) { - $this->connection->query($sql); - } - $this->connection->commit(); - } -} |