migrate therinaldos.com data
Build & Deploy to DigitalOcean Space / build (push) Failing after 2m38s

This commit is contained in:
2024-05-05 15:50:45 -04:00
commit ef1ff240d4
23182 changed files with 3801898 additions and 0 deletions
@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020, Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Roeland Jago Douma <roeland@famdouma.nl>
*
* @license GNU AGPL version 3 or any later version
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
namespace OC\Repair;
use OC\Security\Bruteforce\CleanupJob;
use OCP\BackgroundJob\IJobList;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
class AddBruteForceCleanupJob implements IRepairStep {
/** @var IJobList */
protected $jobList;
public function __construct(IJobList $jobList) {
$this->jobList = $jobList;
}
public function getName() {
return 'Add job to cleanup the bruteforce entries';
}
public function run(IOutput $output) {
$this->jobList->add(CleanupJob::class);
}
}
@@ -0,0 +1,45 @@
<?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/>.
*
*/
namespace OC\Repair;
use OC\Core\BackgroundJobs\BackgroundCleanupUpdaterBackupsJob;
use OCP\BackgroundJob\IJobList;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
class AddCleanupUpdaterBackupsJob implements IRepairStep {
/** @var IJobList */
protected $jobList;
public function __construct(IJobList $jobList) {
$this->jobList = $jobList;
}
public function getName() {
return 'Queue a one-time job to cleanup old backups of the updater';
}
public function run(IOutput $output) {
$this->jobList->add(BackgroundCleanupUpdaterBackupsJob::class);
}
}
@@ -0,0 +1,43 @@
<?php
/**
* @copyright Copyright (c) 2023 Louis Chmn <louis@chmn.me>
*
* @author Louis Chmn <louis@chmn.me>
*
* @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;
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);
}
}
@@ -0,0 +1,49 @@
<?php
declare(strict_types=1);
/**
* @copyright 2023 Marcel Klehr <mklehr@gmx.net>
*
* @author Marcel Klehr <mklehr@gmx.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;
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 job';
}
public function run(IOutput $output) {
$this->jobList->add(RemoveOldTextProcessingTasksBackgroundJob::class);
$this->jobList->add(RemoveOldTextToImageTasksBackgroundJob::class);
}
}
@@ -0,0 +1,208 @@
<?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/>
*
*/
namespace OC\Repair;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\IUserManager;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
/**
* Class RepairConfig
*
* @package OC\Repair
*/
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;
}
/**
* @return string
*/
public function getName() {
return 'Clean tags and favorites';
}
/**
* Updates the configuration after running an update
*/
public function run(IOutput $output) {
$this->deleteOrphanTags($output);
$this->deleteOrphanFileEntries($output);
$this->deleteOrphanTagEntries($output);
$this->deleteOrphanCategoryEntries($output);
}
/**
* Delete tags for deleted users
*/
protected function deleteOrphanTags(IOutput $output) {
$offset = 0;
while ($this->checkTags($offset)) {
$offset += 50;
}
$output->info(sprintf('%d tags of deleted users have been removed.', $this->deletedTags));
}
protected function checkTags($offset) {
$query = $this->connection->getQueryBuilder();
$query->select('uid')
->from('vcategory')
->groupBy('uid')
->orderBy('uid')
->setMaxResults(50)
->setFirstResult($offset);
$result = $query->execute();
$users = [];
$hadResults = false;
while ($row = $result->fetch()) {
$hadResults = true;
if (!$this->userManager->userExists($row['uid'])) {
$users[] = $row['uid'];
}
}
$result->closeCursor();
if (!$hadResults) {
// No more tags, stop looping
return false;
}
if (!empty($users)) {
$query = $this->connection->getQueryBuilder();
$query->delete('vcategory')
->where($query->expr()->in('uid', $query->createNamedParameter($users, IQueryBuilder::PARAM_STR_ARRAY)));
$this->deletedTags += $query->execute();
}
return true;
}
/**
* Delete tag entries for deleted files
*/
protected function deleteOrphanFileEntries(IOutput $output) {
$this->deleteOrphanEntries(
$output,
'%d tags for delete files have been removed.',
'vcategory_to_object', 'objid',
'filecache', 'fileid', 'path_hash'
);
}
/**
* Delete tag entries for deleted tags
*/
protected function deleteOrphanTagEntries(IOutput $output) {
$this->deleteOrphanEntries(
$output,
'%d tag entries for deleted tags have been removed.',
'vcategory_to_object', 'categoryid',
'vcategory', 'id', 'uid'
);
}
/**
* Delete tags that have no entries
*/
protected function deleteOrphanCategoryEntries(IOutput $output) {
$this->deleteOrphanEntries(
$output,
'%d tags with no entries have been removed.',
'vcategory', 'id',
'vcategory_to_object', 'categoryid', 'type'
);
}
/**
* Deletes all entries from $deleteTable that do not have a matching entry in $sourceTable
*
* A query joins $deleteTable.$deleteId = $sourceTable.$sourceId and checks
* whether $sourceNullColumn is null. If it is null, the entry in $deleteTable
* is being deleted.
*
* @param string $repairInfo
* @param string $deleteTable
* @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
*/
protected function deleteOrphanEntries(IOutput $output, $repairInfo, $deleteTable, $deleteId, $sourceTable, $sourceId, $sourceNullColumn) {
$qb = $this->connection->getQueryBuilder();
$qb->select('d.' . $deleteId)
->from($deleteTable, 'd')
->leftJoin('d', $sourceTable, 's', $qb->expr()->eq('d.' . $deleteId, 's.' . $sourceId))
->where(
$qb->expr()->eq('d.type', $qb->expr()->literal('files'))
)
->andWhere(
$qb->expr()->isNull('s.' . $sourceNullColumn)
);
$result = $qb->execute();
$orphanItems = [];
while ($row = $result->fetch()) {
$orphanItems[] = (int) $row[$deleteId];
}
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();
}
}
if ($repairInfo) {
$output->info(sprintf($repairInfo, count($orphanItems)));
}
}
}
@@ -0,0 +1,53 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2022 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 <https://www.gnu.org/licenses/>.
*
*/
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);
}
}
}
@@ -0,0 +1,61 @@
<?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/>.
*
*/
namespace OC\Repair;
use OC\Template\JSCombiner;
use OCP\ICacheFactory;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
class ClearFrontendCaches implements IRepairStep {
/** @var ICacheFactory */
protected $cacheFactory;
/** @var JSCombiner */
protected $jsCombiner;
public function __construct(ICacheFactory $cacheFactory,
JSCombiner $JSCombiner) {
$this->cacheFactory = $cacheFactory;
$this->jsCombiner = $JSCombiner;
}
public function getName() {
return 'Clear frontend caches';
}
public function run(IOutput $output) {
try {
$c = $this->cacheFactory->createDistributed('imagePath');
$c->clear();
$output->info('Image cache cleared');
$this->jsCombiner->resetCache();
$output->info('JS cache cleared');
} catch (\Exception $e) {
$output->warning('Unable to clear the frontend cache');
}
}
}
@@ -0,0 +1,69 @@
<?php
/**
* @copyright Copyright (c) 2018 John Molakvoæ <skjnldsv@protonmail.com>
*
* @author Christoph Wurst <christoph@winzerhof-wurst.at>
* @author John Molakvoæ <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/>.
*
*/
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;
public function __construct(IConfig $config, AvatarManager $avatarManager, IJobList $jobList) {
$this->config = $config;
$this->avatarManager = $avatarManager;
$this->jobList = $jobList;
}
public function getName(): string {
return 'Clear every generated avatar';
}
/**
* Check if this repair step should run
*/
private function shouldRun(): bool {
$versionFromBeforeUpdate = $this->config->getSystemValueString('version', '0.0.0.0');
// 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): void {
if ($this->shouldRun()) {
try {
$this->jobList->add(ClearGeneratedAvatarCacheJob::class, []);
$output->info('Avatar cache clearing job added');
} catch (\Exception $e) {
$output->warning('Unable to clear the avatar cache');
}
}
}
}
@@ -0,0 +1,38 @@
<?php
/**
* @copyright 2022 Carl Schwan <carl@carlschwan.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/>.
*
*/
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();
}
}
@@ -0,0 +1,149 @@
<?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/>
*
*/
namespace OC\Repair;
use Doctrine\DBAL\Exception\DriverException;
use Doctrine\DBAL\Platforms\MySQLPlatform;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
use Psr\Log\LoggerInterface;
class Collation implements IRepairStep {
/** @var IConfig */
protected $config;
protected LoggerInterface $logger;
/** @var IDBConnection */
protected $connection;
/** @var bool */
protected $ignoreFailures;
/**
* @param bool $ignoreFailures
*/
public function __construct(
IConfig $config,
LoggerInterface $logger,
IDBConnection $connection,
$ignoreFailures
) {
$this->connection = $connection;
$this->config = $config;
$this->logger = $logger;
$this->ignoreFailures = $ignoreFailures;
}
public function getName() {
return 'Repair MySQL collation';
}
/**
* Fix mime types
*/
public function run(IOutput $output) {
if (!$this->connection->getDatabasePlatform() instanceof MySQLPlatform) {
$output->info('Not a mysql database -> nothing to do');
return;
}
$characterSet = $this->config->getSystemValueBool('mysql.utf8mb4', false) ? 'utf8mb4' : 'utf8';
$tables = $this->getAllNonUTF8BinTables($this->connection);
foreach ($tables as $table) {
$output->info("Change row format for $table ...");
$query = $this->connection->prepare('ALTER TABLE `' . $table . '` ROW_FORMAT = DYNAMIC;');
try {
$query->execute();
} catch (DriverException $e) {
// Just log this
$this->logger->error($e->getMessage(), ['exception' => $e]);
if (!$this->ignoreFailures) {
throw $e;
}
}
$output->info("Change collation for $table ...");
$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->error($e->getMessage(), ['exception' => $e]);
if (!$this->ignoreFailures) {
throw $e;
}
}
}
if (empty($tables)) {
$output->info('All tables already have the correct collation -> nothing to do');
}
}
/**
* @param IDBConnection $connection
* @return string[]
*/
protected function getAllNonUTF8BinTables(IDBConnection $connection) {
$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*%'",
[$dbName]
);
$rows = $statement->fetchAll();
$result = [];
foreach ($rows as $row) {
$result[$row['table']] = true;
}
// 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*%'",
[$dbName]
);
$rows = $statement->fetchAll();
foreach ($rows as $row) {
$result[$row['table']] = true;
}
return array_keys($result);
}
}
@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2022 Côme Chilliet <come.chilliet@nextcloud.com>
*
* @author Côme Chilliet <come.chilliet@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\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;
}
}
@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2022 Côme Chilliet <come.chilliet@nextcloud.com>
*
* @author Côme Chilliet <come.chilliet@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\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;
}
}
@@ -0,0 +1,30 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2022 Côme Chilliet <come.chilliet@nextcloud.com>
*
* @author Côme Chilliet <come.chilliet@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\Events;
use OCP\EventDispatcher\Event;
class RepairFinishEvent extends Event {
}
@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2022 Côme Chilliet <come.chilliet@nextcloud.com>
*
* @author Côme Chilliet <come.chilliet@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\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;
}
}
@@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2022 Côme Chilliet <come.chilliet@nextcloud.com>
*
* @author Côme Chilliet <come.chilliet@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\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;
}
}
@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2022 Côme Chilliet <come.chilliet@nextcloud.com>
*
* @author Côme Chilliet <come.chilliet@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\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;
}
}
@@ -0,0 +1,41 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2022 Côme Chilliet <come.chilliet@nextcloud.com>
*
* @author Côme Chilliet <come.chilliet@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\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;
}
}
@@ -0,0 +1,77 @@
<?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/>.
*
*/
namespace OC\Repair;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
class MoveUpdaterStepFile implements IRepairStep {
/** @var \OCP\IConfig */
protected $config;
/**
* @param \OCP\IConfig $config
*/
public function __construct($config) {
$this->config = $config;
}
public function getName() {
return 'Move .step file of updater to backup location';
}
public function run(IOutput $output) {
$updateDir = $this->config->getSystemValue('updatedirectory', null) ?? $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data');
$instanceId = $this->config->getSystemValueString('instanceid');
if (empty($instanceId)) {
return;
}
$updaterFolderPath = $updateDir . '/updater-' . $instanceId;
$stepFile = $updaterFolderPath . '/.step';
if (file_exists($stepFile)) {
$output->info('.step file exists');
$previousStepFile = $updaterFolderPath . '/.step-previous-update';
// cleanup
if (file_exists($previousStepFile)) {
if (\OC_Helper::rmdirr($previousStepFile)) {
$output->info('.step-previous-update removed');
} else {
$output->info('.step-previous-update can\'t be removed - abort move of .step file');
return;
}
}
// move step file
if (rename($stepFile, $previousStepFile)) {
$output->info('.step file moved to .step-previous-update');
} else {
$output->warning('.step file can\'t be moved');
}
}
}
}
@@ -0,0 +1,78 @@
<?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');
}
}
@@ -0,0 +1,45 @@
<?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/>.
*
*/
namespace OC\Repair\NC13;
use OC\Log\Rotate;
use OCP\BackgroundJob\IJobList;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
class AddLogRotateJob implements IRepairStep {
/** @var IJobList */
private $jobList;
public function __construct(IJobList $jobList) {
$this->jobList = $jobList;
}
public function getName() {
return 'Add log rotate job';
}
public function run(IOutput $output) {
$this->jobList->add(Rotate::class);
}
}
@@ -0,0 +1,48 @@
<?php
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/>.
*
*/
namespace OC\Repair\NC14;
use OC\Preview\BackgroundCleanupJob;
use OCP\BackgroundJob\IJobList;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
class AddPreviewBackgroundCleanupJob implements IRepairStep {
/** @var IJobList */
private $jobList;
public function __construct(IJobList $jobList) {
$this->jobList = $jobList;
}
public function getName(): string {
return 'Add preview background cleanup job';
}
public function run(IOutput $output) {
$this->jobList->add(BackgroundCleanupJob::class);
}
}
@@ -0,0 +1,48 @@
<?php
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/>.
*
*/
namespace OC\Repair\NC16;
use OC\Core\BackgroundJobs\CleanupLoginFlowV2;
use OCP\BackgroundJob\IJobList;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
class AddClenupLoginFlowV2BackgroundJob implements IRepairStep {
/** @var IJobList */
private $jobList;
public function __construct(IJobList $jobList) {
$this->jobList = $jobList;
}
public function getName(): string {
return 'Add background job to cleanup login flow v2 tokens';
}
public function run(IOutput $output) {
$this->jobList->add(CleanupLoginFlowV2::class);
}
}
@@ -0,0 +1,110 @@
<?php
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/>.
*
*/
namespace OC\Repair\NC16;
use OCP\Files\IAppData;
use OCP\Files\NotFoundException;
use OCP\Files\SimpleFS\ISimpleFolder;
use OCP\IConfig;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
use Psr\Log\LoggerInterface;
use RuntimeException;
/**
* Class CleanupCardDAVPhotoCache
*
* This repair step removes "photo." files created by photocache
*
* Before https://github.com/nextcloud/server/pull/13843 a "photo." file could be created
* for unsupported image formats by photocache. Because a file is present but not jpg, png or gif no
* 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;
private LoggerInterface $logger;
public function __construct(IConfig $config, IAppData $appData, LoggerInterface $logger) {
$this->config = $config;
$this->appData = $appData;
$this->logger = $logger;
}
public function getName(): string {
return 'Cleanup invalid photocache files for carddav';
}
private function repair(IOutput $output): void {
try {
$folders = $this->appData->getDirectoryListing();
} catch (NotFoundException $e) {
return;
} catch (RuntimeException $e) {
$this->logger->error('Failed to fetch directory listing in CleanupCardDAVPhotoCache', ['exception' => $e]);
return;
}
$folders = array_filter($folders, function (ISimpleFolder $folder) {
return $folder->fileExists('photo.');
});
if (empty($folders)) {
return;
}
$output->info('Delete ' . count($folders) . ' "photo." files');
foreach ($folders as $folder) {
try {
/** @var ISimpleFolder $folder */
$folder->getFile('photo.')->delete();
} catch (\Exception $e) {
$this->logger->error($e->getMessage(), ['exception' => $e]);
$output->warning('Could not delete file "dav-photocache/' . $folder->getName() . '/photo."');
}
}
}
private function shouldRun(): bool {
return version_compare(
$this->config->getSystemValueString('version', '0.0.0.0'),
'16.0.0.0',
'<='
);
}
public function run(IOutput $output): void {
if ($this->shouldRun()) {
$this->repair($output);
}
}
}
@@ -0,0 +1,60 @@
<?php
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/>.
*
*/
namespace OC\Repair\NC16;
use OC\Collaboration\Resources\Manager;
use OCP\Collaboration\Resources\IManager;
use OCP\IConfig;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
class ClearCollectionsAccessCache implements IRepairStep {
/** @var IConfig */
private $config;
/** @var Manager */
private $manager;
public function __construct(IConfig $config, IManager $manager) {
$this->config = $config;
$this->manager = $manager;
}
public function getName(): string {
return 'Clear access cache of projects';
}
private function shouldRun(): bool {
$versionFromBeforeUpdate = $this->config->getSystemValueString('version', '0.0.0.0');
return version_compare($versionFromBeforeUpdate, '17.0.0.3', '<=');
}
public function run(IOutput $output): void {
if ($this->shouldRun()) {
$this->manager->invalidateAccessCacheForAllCollections();
}
}
}
@@ -0,0 +1,62 @@
<?php
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/>.
*
*/
namespace OC\Repair\NC18;
use OCP\IConfig;
use OCP\IDBConnection;
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) {
$this->config = $config;
$this->connection = $connection;
}
public function getName(): string {
return 'Reset generated avatar flag';
}
private function shouldRun(): bool {
$versionFromBeforeUpdate = $this->config->getSystemValueString('version', '0.0.0.0');
return version_compare($versionFromBeforeUpdate, '18.0.0.5', '<=');
}
public function run(IOutput $output): void {
if ($this->shouldRun()) {
$query = $this->connection->getQueryBuilder();
$query->delete('preferences')
->where($query->expr()->eq('appid', $query->createNamedParameter('avatar')))
->andWhere($query->expr()->eq('configkey', $query->createNamedParameter('generated')));
}
}
}
@@ -0,0 +1,67 @@
<?php
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/>.
*
*/
namespace OC\Repair\NC20;
use OCP\Encryption\IManager;
use OCP\IConfig;
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) {
$this->config = $config;
$this->manager = $manager;
}
public function getName(): string {
return 'Keep legacy encryption enabled';
}
private function shouldRun(): bool {
$versionFromBeforeUpdate = $this->config->getSystemValueString('version', '0.0.0.0');
return version_compare($versionFromBeforeUpdate, '20.0.0.0', '<=');
}
public function run(IOutput $output): void {
if (!$this->shouldRun()) {
return;
}
$masterKeyId = $this->config->getAppValue('encryption', 'masterKeyId');
if ($this->manager->isEnabled() || !empty($masterKeyId)) {
if ($this->config->getSystemValue('encryption.legacy_format_support', '') === '') {
$this->config->setSystemValue('encryption.legacy_format_support', true);
}
}
}
}
@@ -0,0 +1,67 @@
<?php
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/>.
*
*/
namespace OC\Repair\NC20;
use OCP\Encryption\IManager;
use OCP\IConfig;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
class EncryptionMigration implements IRepairStep {
/** @var IConfig */
private $config;
/** @var IManager */
private $manager;
public function __construct(IConfig $config,
IManager $manager) {
$this->config = $config;
$this->manager = $manager;
}
public function getName(): string {
return 'Check encryption key format';
}
private function shouldRun(): bool {
$versionFromBeforeUpdate = $this->config->getSystemValueString('version', '0.0.0.0');
return version_compare($versionFromBeforeUpdate, '20.0.0.1', '<=');
}
public function run(IOutput $output): void {
if (!$this->shouldRun()) {
return;
}
$masterKeyId = $this->config->getAppValue('encryption', 'masterKeyId');
if ($this->manager->isEnabled() || !empty($masterKeyId)) {
if ($this->config->getSystemValue('encryption.key_storage_migrated', '') === '') {
$this->config->setSystemValue('encryption.key_storage_migrated', false);
}
}
}
}
@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
/**
* @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/>.
*
*/
namespace OC\Repair\NC20;
use OCP\IConfig;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
class ShippedDashboardEnable implements IRepairStep {
/** @var IConfig */
private $config;
public function __construct(IConfig $config) {
$this->config = $config;
}
public function getName() {
return 'Remove old dashboard app config data';
}
public function run(IOutput $output) {
$version = $this->config->getAppValue('dashboard', 'version', '7.0.0');
if (version_compare($version, '7.0.0', '<')) {
$this->config->deleteAppValues('dashboard');
$output->info('Removed old dashboard app config');
}
}
}
@@ -0,0 +1,59 @@
<?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/>.
*
*/
namespace OC\Repair\NC21;
use OC\Core\BackgroundJobs\CheckForUserCertificates;
use OCP\BackgroundJob\IJobList;
use OCP\IConfig;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
class AddCheckForUserCertificatesJob implements IRepairStep {
/** @var IJobList */
protected $jobList;
/** @var IConfig */
private $config;
public function __construct(IConfig $config, IJobList $jobList) {
$this->jobList = $jobList;
$this->config = $config;
}
public function getName() {
return 'Queue a one-time job to check for user uploaded certificates';
}
private function shouldRun() {
$versionFromBeforeUpdate = $this->config->getSystemValueString('version', '0.0.0.0');
// was added to 21.0.0.2
return version_compare($versionFromBeforeUpdate, '21.0.0.2', '<');
}
public function run(IOutput $output) {
if ($this->shouldRun()) {
$this->config->setAppValue('files_external', 'user_certificate_scan', 'not-run-yet');
$this->jobList->add(CheckForUserCertificates::class);
}
}
}
@@ -0,0 +1,88 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2020 Joas Schilling <coding@schilljs.com>
*
* @author Arthur Schiwon <blizzz@arthur-schiwon.de>
* @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 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 IAccountManager */
private $accountManager;
public function __construct(IUserManager $userManager,
IAccountManager $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';
}
public function run(IOutput $output): void {
if ($this->config->getSystemValueString('default_phone_region', '') === '') {
$output->warning('Can not validate phone numbers without `default_phone_region` being set in the config file');
return;
}
$numUpdated = 0;
$numRemoved = 0;
$this->userManager->callForSeenUsers(function (IUser $user) use (&$numUpdated, &$numRemoved) {
$account = $this->accountManager->getAccount($user);
$property = $account->getProperty(IAccountManager::PROPERTY_PHONE);
if ($property->getValue() !== '') {
$this->accountManager->updateAccount($account);
$updatedAccount = $this->accountManager->getAccount($user);
$updatedProperty = $updatedAccount->getProperty(IAccountManager::PROPERTY_PHONE);
if ($property->getValue() !== $updatedProperty->getValue()) {
if ($updatedProperty->getValue() === '') {
$numRemoved++;
} else {
$numUpdated++;
}
}
}
});
if ($numRemoved > 0 || $numUpdated > 0) {
$output->info('Updated ' . $numUpdated . ' entries and cleaned ' . $numRemoved . ' invalid phone numbers');
}
}
}
@@ -0,0 +1,51 @@
<?php
declare(strict_types=1);
/**
* @copyright Copyright (c) 2021 Roeland Jago Douma <roeland@famdouma.nl>
*
* @author Morris Jobke <hey@morrisjobke.de>
* @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\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);
}
}
@@ -0,0 +1,47 @@
<?php
declare(strict_types=1);
/**
* @copyright 2022 Thomas Citharel <nextcloud@tcit.fr>
*
* @author Thomas Citharel <nextcloud@tcit.fr>
*
* @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\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);
}
}
@@ -0,0 +1,63 @@
<?php
declare(strict_types=1);
/**
* @copyright 2022 Carl Schwan <carl@carlschwan.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/>.
*
*/
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.");
}
}
}
}
@@ -0,0 +1,116 @@
<?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/>
*
*/
namespace OC\Repair;
use OCP\IDBConnection;
use OCP\IGroupManager;
use OCP\Migration\IOutput;
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)]]
*/
protected $memberships;
/**
* @param IDBConnection $connection
* @param IGroupManager $groupManager
*/
public function __construct(IDBConnection $connection, IGroupManager $groupManager) {
$this->connection = $connection;
$this->groupManager = $groupManager;
}
/**
* Returns the step's name
*
* @return string
*/
public function getName() {
return 'Remove shares of old group memberships';
}
/**
* Run repair step.
* Must throw exception on error.
*
* @throws \Exception in case of failure
*/
public function run(IOutput $output) {
$deletedEntries = 0;
$query = $this->connection->getQueryBuilder();
$query->select('s1.id')->selectAlias('s1.share_with', 'user')->selectAlias('s2.share_with', 'group')
->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)))
->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();
while ($row = $result->fetch()) {
if (!$this->isMember($row['group'], $row['user'])) {
$deletedEntries += $deleteQuery->setParameter('share', (int) $row['id'])
->execute();
}
}
$result->closeCursor();
if ($deletedEntries) {
$output->info('Removed ' . $deletedEntries . ' shares where user is not a member of the group anymore');
}
}
/**
* @param string $gid
* @param string $uid
* @return bool
*/
protected function isMember($gid, $uid) {
if (isset($this->memberships[$gid][$uid])) {
return $this->memberships[$gid][$uid];
}
$isMember = $this->groupManager->isInGroup($uid, $gid);
if (!isset($this->memberships[$gid])) {
$this->memberships[$gid] = [];
}
$this->memberships[$gid][$uid] = $isMember;
return $isMember;
}
}
@@ -0,0 +1,72 @@
<?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/>.
*
*/
namespace OC\Repair\Owncloud;
use OCP\BackgroundJob\IJobList;
use OCP\IConfig;
use OCP\IUser;
use OCP\IUserManager;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
class CleanPreviews implements IRepairStep {
/** @var IJobList */
private $jobList;
/** @var IUserManager */
private $userManager;
/** @var IConfig */
private $config;
/**
* MoveAvatars constructor.
*
* @param IJobList $jobList
* @param IUserManager $userManager
* @param IConfig $config
*/
public function __construct(IJobList $jobList,
IUserManager $userManager,
IConfig $config) {
$this->jobList = $jobList;
$this->userManager = $userManager;
$this->config = $config;
}
/**
* @return string
*/
public function getName() {
return 'Add preview cleanup background jobs';
}
public function run(IOutput $output) {
if (!$this->config->getAppValue('core', 'previewsCleanedUp', false)) {
$this->userManager->callForSeenUsers(function (IUser $user) {
$this->jobList->add(CleanPreviewsBackgroundJob::class, ['uid' => $user->getUID()]);
});
$this->config->setAppValue('core', 'previewsCleanedUp', '1');
}
}
}
@@ -0,0 +1,125 @@
<?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/>.
*
*/
namespace OC\Repair\Owncloud;
use OC\BackgroundJob\QueuedJob;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\IJobList;
use OCP\Files\Folder;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\Files\NotPermittedException;
use OCP\IUserManager;
use Psr\Log\LoggerInterface;
class CleanPreviewsBackgroundJob extends QueuedJob {
/** @var IRootFolder */
private $rootFolder;
private LoggerInterface $logger;
/** @var IJobList */
private $jobList;
/** @var ITimeFactory */
private $timeFactory;
/** @var IUserManager */
private $userManager;
/**
* CleanPreviewsBackgroundJob constructor.
*/
public function __construct(IRootFolder $rootFolder,
LoggerInterface $logger,
IJobList $jobList,
ITimeFactory $timeFactory,
IUserManager $userManager) {
$this->rootFolder = $rootFolder;
$this->logger = $logger;
$this->jobList = $jobList;
$this->timeFactory = $timeFactory;
$this->userManager = $userManager;
}
public function run($arguments) {
$uid = $arguments['uid'];
if (!$this->userManager->userExists($uid)) {
$this->logger->info('User no longer exists, skip user ' . $uid);
return;
}
$this->logger->info('Started preview cleanup for ' . $uid);
$empty = $this->cleanupPreviews($uid);
if (!$empty) {
$this->jobList->add(self::class, ['uid' => $uid]);
$this->logger->info('New preview cleanup scheduled for ' . $uid);
} else {
$this->logger->info('Preview cleanup done for ' . $uid);
}
}
/**
* @param $uid
* @return bool
*/
private function cleanupPreviews($uid) {
try {
$userFolder = $this->rootFolder->getUserFolder($uid);
} catch (NotFoundException $e) {
return true;
}
$userRoot = $userFolder->getParent();
try {
/** @var Folder $thumbnailFolder */
$thumbnailFolder = $userRoot->get('thumbnails');
} catch (NotFoundException $e) {
return true;
}
$thumbnails = $thumbnailFolder->getDirectoryListing();
$start = $this->timeFactory->getTime();
foreach ($thumbnails as $thumbnail) {
try {
$thumbnail->delete();
} catch (NotPermittedException $e) {
// Ignore
}
if (($this->timeFactory->getTime() - $start) > 15) {
return false;
}
}
try {
$thumbnailFolder->delete();
} catch (NotPermittedException $e) {
// Ignore
}
return true;
}
}
@@ -0,0 +1,57 @@
<?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/>.
*
*/
namespace OC\Repair\Owncloud;
use OCP\IDBConnection;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
class DropAccountTermsTable implements IRepairStep {
/** @var IDBConnection */
protected $db;
/**
* @param IDBConnection $db
*/
public function __construct(IDBConnection $db) {
$this->db = $db;
}
/**
* @return string
*/
public function getName() {
return 'Drop account terms table when migrating from ownCloud';
}
/**
* @param IOutput $output
*/
public function run(IOutput $output) {
if (!$this->db->tableExists('account_terms')) {
return;
}
$this->db->dropTable('account_terms');
}
}
@@ -0,0 +1,178 @@
<?php
/**
* @copyright 2021 Louis Chemineau <louis@chmn.me>
*
* @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\DB\Connection;
use OC\DB\SchemaWrapper;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
class MigrateOauthTables implements IRepairStep {
/** @var Connection */
protected $db;
/**
* @param Connection $db
*/
public function __construct(Connection $db) {
$this->db = $db;
}
/**
* @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;
}
$output->info("Update the oauth2_access_tokens table schema.");
$schema = new SchemaWrapper($this->db);
$table = $schema->getTable('oauth2_access_tokens');
if (!$table->hasColumn('hashed_code')) {
$table->addColumn('hashed_code', 'string', [
'notnull' => true,
'length' => 128,
]);
}
if (!$table->hasColumn('encrypted_token')) {
$table->addColumn('encrypted_token', 'string', [
'notnull' => true,
'length' => 786,
]);
}
if (!$table->hasIndex('oauth2_access_hash_idx')) {
$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');
}
$output->info("Update the oauth2_clients table schema.");
$schema = new SchemaWrapper($this->db);
$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');
}
$this->db->migrateToSchema($schema->getWrappedSchema());
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.");
$schema = new SchemaWrapper($this->db);
$table = $schema->getTable('oauth2_clients');
$table->dropColumn('identifier');
$this->db->migrateToSchema($schema->getWrappedSchema());
}
$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))
)
->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))
)
->orWhere(
$qbDeleteClients->expr()->iLike('redirect_uri', $qbDeleteClients->createNamedParameter('%*', IQueryBuilder::PARAM_STR))
);
$qbDeleteClients->executeStatement();
}
}
@@ -0,0 +1,71 @@
<?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/>.
*
*/
namespace OC\Repair\Owncloud;
use OCP\BackgroundJob\IJobList;
use OCP\IConfig;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
class MoveAvatars implements IRepairStep {
/** @var IJobList */
private $jobList;
/** @var IConfig */
private $config;
/**
* MoveAvatars constructor.
*
* @param IJobList $jobList
* @param IConfig $config
*/
public function __construct(IJobList $jobList,
IConfig $config) {
$this->jobList = $jobList;
$this->config = $config;
}
/**
* @return string
*/
public function getName() {
return 'Add move avatar background job';
}
public function run(IOutput $output) {
// only run once
if ($this->config->getAppValue('core', 'moveavatarsdone') === 'yes') {
$output->info('Repair step already executed');
return;
}
if (!$this->config->getSystemValueBool('enable_avatars', true)) {
$output->info('Avatars are disabled');
} else {
$output->info('Add background job');
$this->jobList->add(MoveAvatarsBackgroundJob::class);
// if all were done, no need to redo the repair during next upgrade
$this->config->setAppValue('core', 'moveavatarsdone', 'yes');
}
}
}
@@ -0,0 +1,115 @@
<?php
/**
* @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/>.
*
*/
namespace OC\Repair\Owncloud;
use OC\BackgroundJob\QueuedJob;
use OCP\Files\IRootFolder;
use OCP\Files\NotFoundException;
use OCP\Files\Storage;
use OCP\IAvatarManager;
use OCP\IUser;
use OCP\IUserManager;
use Psr\Log\LoggerInterface;
use function is_resource;
class MoveAvatarsBackgroundJob extends QueuedJob {
/** @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;
try {
$this->owncloudAvatarStorage = $rootFolder->get('avatars')->getStorage();
} catch (\Exception $e) {
}
}
public function run($arguments) {
$this->logger->info('Started migrating avatars to AppData folder');
$this->moveAvatars();
$this->logger->info('All avatars migrated to AppData folder');
}
private function moveAvatars(): void {
if (!$this->owncloudAvatarStorage) {
$this->logger->info('No legacy avatars available, skipping migration');
return;
}
$counter = 0;
$this->userManager->callForSeenUsers(function (IUser $user) use ($counter) {
$uid = $user->getUID();
$path = 'avatars/' . $this->buildOwnCloudAvatarPath($uid);
$avatar = $this->avatarManager->getAvatar($uid);
try {
$avatarPath = $path . '/avatar.' . $this->getExtension($path);
$resource = $this->owncloudAvatarStorage->fopen($avatarPath, 'r');
if (is_resource($resource)) {
$avatar->set($resource);
fclose($resource);
} else {
throw new \Exception('Failed to open old avatar file for reading');
}
} catch (NotFoundException $e) {
// In case there is no avatar we can just skip
} catch (\Throwable $e) {
$this->logger->error('Failed to migrate avatar for user ' . $uid, ['exception' => $e]);
}
$counter++;
if ($counter % 100 === 0) {
$this->logger->info('{amount} avatars migrated', ['amount' => $counter]);
}
});
}
/**
* @throws NotFoundException
*/
private function getExtension(string $path): string {
if ($this->owncloudAvatarStorage->file_exists("{$path}/avatar.jpg")) {
return 'jpg';
}
if ($this->owncloudAvatarStorage->file_exists("{$path}/avatar.png")) {
return 'png';
}
throw new NotFoundException("{$path}/avatar.jpg|png");
}
protected function buildOwnCloudAvatarPath(string $userId): string {
return substr_replace(substr_replace(md5($userId), '/', 4, 0), '/', 2, 0);
}
}
@@ -0,0 +1,199 @@
<?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/>.
*
*/
namespace OC\Repair\Owncloud;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
use OCP\PreConditionNotMetException;
/**
* Copies the email address from the accounts table to the preference table,
* before the data structure is changed and the information is gone
*/
class SaveAccountsTableData implements IRepairStep {
public const BATCH_SIZE = 75;
/** @var IDBConnection */
protected $db;
/** @var IConfig */
protected $config;
protected $hasForeignKeyOnPersistentLocks = false;
/**
* @param IDBConnection $db
* @param IConfig $config
*/
public function __construct(IDBConnection $db, IConfig $config) {
$this->db = $db;
$this->config = $config;
}
/**
* @return string
*/
public function getName() {
return 'Copy data from accounts table when migrating from ownCloud';
}
/**
* @param IOutput $output
*/
public function run(IOutput $output) {
if (!$this->shouldRun()) {
return;
}
$offset = 0;
$numUsers = $this->runStep($offset);
while ($numUsers === self::BATCH_SIZE) {
$offset += $numUsers;
$numUsers = $this->runStep($offset);
}
// oc_persistent_locks will be removed later on anyways so we can just drop and ignore any foreign key constraints here
$tableName = $this->config->getSystemValueString('dbtableprefix', 'oc_') . 'persistent_locks';
$schema = $this->db->createSchema();
$table = $schema->getTable($tableName);
foreach ($table->getForeignKeys() as $foreignKey) {
$table->removeForeignKey($foreignKey->getName());
}
$this->db->migrateToSchema($schema);
// Remove the table
if ($this->hasForeignKeyOnPersistentLocks) {
$this->db->dropTable('persistent_locks');
}
$this->db->dropTable('accounts');
}
/**
* @return bool
*/
protected function shouldRun() {
$schema = $this->db->createSchema();
$prefix = $this->config->getSystemValueString('dbtableprefix', 'oc_');
$tableName = $prefix . 'accounts';
if (!$schema->hasTable($tableName)) {
return false;
}
$table = $schema->getTable($tableName);
if (!$table->hasColumn('user_id')) {
return false;
}
if ($schema->hasTable($prefix . 'persistent_locks')) {
$locksTable = $schema->getTable($prefix . 'persistent_locks');
$foreignKeys = $locksTable->getForeignKeys();
foreach ($foreignKeys as $foreignKey) {
if ($tableName === $foreignKey->getForeignTableName()) {
$this->hasForeignKeyOnPersistentLocks = true;
}
}
}
return true;
}
/**
* @param int $offset
* @return int Number of copied users
*/
protected function runStep($offset) {
$query = $this->db->getQueryBuilder();
$query->select('*')
->from('accounts')
->orderBy('id')
->setMaxResults(self::BATCH_SIZE);
if ($offset > 0) {
$query->setFirstResult($offset);
}
$result = $query->execute();
$update = $this->db->getQueryBuilder();
$update->update('users')
->set('displayname', $update->createParameter('displayname'))
->where($update->expr()->eq('uid', $update->createParameter('userid')));
$updatedUsers = 0;
while ($row = $result->fetch()) {
try {
$this->migrateUserInfo($update, $row);
} catch (PreConditionNotMetException $e) {
// Ignore and continue
} catch (\UnexpectedValueException $e) {
// Ignore and continue
}
$updatedUsers++;
}
$result->closeCursor();
return $updatedUsers;
}
/**
* @param IQueryBuilder $update
* @param array $userdata
* @throws PreConditionNotMetException
* @throws \UnexpectedValueException
*/
protected function migrateUserInfo(IQueryBuilder $update, $userdata) {
$state = (int) $userdata['state'];
if ($state === 3) {
// Deleted user, ignore
return;
}
if ($userdata['email'] !== null) {
$this->config->setUserValue($userdata['user_id'], 'settings', 'email', $userdata['email']);
}
if ($userdata['quota'] !== null) {
$this->config->setUserValue($userdata['user_id'], 'files', 'quota', $userdata['quota']);
}
if ($userdata['last_login'] !== null) {
$this->config->setUserValue($userdata['user_id'], 'login', 'lastLogin', $userdata['last_login']);
}
if ($state === 1) {
$this->config->setUserValue($userdata['user_id'], 'core', 'enabled', 'true');
} elseif ($state === 2) {
$this->config->setUserValue($userdata['user_id'], 'core', 'enabled', 'false');
}
if ($userdata['display_name'] !== null) {
$update->setParameter('displayname', $userdata['display_name'])
->setParameter('userid', $userdata['user_id']);
$update->execute();
}
}
}
@@ -0,0 +1,88 @@
<?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/>.
*
*/
namespace OC\Repair\Owncloud;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
class UpdateLanguageCodes implements IRepairStep {
/** @var IDBConnection */
private $connection;
/** @var IConfig */
private $config;
/**
* @param IDBConnection $connection
* @param IConfig $config
*/
public function __construct(IDBConnection $connection,
IConfig $config) {
$this->connection = $connection;
$this->config = $config;
}
/**
* {@inheritdoc}
*/
public function getName() {
return 'Repair language codes';
}
/**
* {@inheritdoc}
*/
public function run(IOutput $output) {
$versionFromBeforeUpdate = $this->config->getSystemValueString('version', '0.0.0');
if (version_compare($versionFromBeforeUpdate, '12.0.0.13', '>')) {
return;
}
$languages = [
'bg_BG' => 'bg',
'cs_CZ' => 'cs',
'fi_FI' => 'fi',
'hu_HU' => 'hu',
'nb_NO' => 'nb',
'sk_SK' => 'sk',
'th_TH' => 'th',
];
foreach ($languages as $oldCode => $newCode) {
$qb = $this->connection->getQueryBuilder();
$affectedRows = $qb->update('preferences')
->set('configvalue', $qb->createNamedParameter($newCode))
->where($qb->expr()->eq('appid', $qb->createNamedParameter('core')))
->andWhere($qb->expr()->eq('configkey', $qb->createNamedParameter('lang')))
->andWhere($qb->expr()->eq('configvalue', $qb->createNamedParameter($oldCode), IQueryBuilder::PARAM_STR))
->execute();
$output->info('Changed ' . $affectedRows . ' setting(s) from "' . $oldCode . '" to "' . $newCode . '" in preferences table.');
}
}
}
@@ -0,0 +1,241 @@
<?php
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@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/>.
*
*/
namespace OC\Repair;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\DB\IResult;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\IGroupManager;
use OCP\Migration\IOutput;
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 getName(): string {
return 'Remove potentially over exposing share links';
}
private function shouldRun(): bool {
$versionFromBeforeUpdate = $this->config->getSystemValueString('version', '0.0.0');
if (version_compare($versionFromBeforeUpdate, '14.0.11', '<')) {
return true;
}
if (version_compare($versionFromBeforeUpdate, '15.0.8', '<')) {
return true;
}
if (version_compare($versionFromBeforeUpdate, '16.0.0', '<=')) {
return true;
}
return false;
}
/**
* Delete the share
*
* @param int $id
*/
private function deleteShare(int $id): void {
$qb = $this->connection->getQueryBuilder();
$qb->delete('share')
->where($qb->expr()->eq('id', $qb->createNamedParameter($id)));
$qb->execute();
}
/**
* Get the total of affected shares
*
* @return int
*/
private function getTotal(): int {
$subSubQuery = $this->connection->getQueryBuilder();
$subSubQuery->select('*')
->from('share')
->where($subSubQuery->expr()->isNotNull('parent'))
->andWhere($subSubQuery->expr()->eq('share_type', $subSubQuery->expr()->literal(3, IQueryBuilder::PARAM_INT)));
$subQuery = $this->connection->getQueryBuilder();
$subQuery->select('s1.id')
->from($subQuery->createFunction('(' . $subSubQuery->getSQL() . ')'), 's1')
->join(
's1', 'share', 's2',
$subQuery->expr()->eq('s1.parent', 's2.id')
)
->where($subQuery->expr()->orX(
$subQuery->expr()->eq('s2.share_type', $subQuery->expr()->literal(1, IQueryBuilder::PARAM_INT)),
$subQuery->expr()->eq('s2.share_type', $subQuery->expr()->literal(2, IQueryBuilder::PARAM_INT))
))
->andWhere($subQuery->expr()->eq('s1.item_source', 's2.item_source'));
$query = $this->connection->getQueryBuilder();
$query->select($query->func()->count('*', 'total'))
->from('share')
->where($query->expr()->in('id', $query->createFunction($subQuery->getSQL())));
$result = $query->execute();
$data = $result->fetch();
$result->closeCursor();
return (int) $data['total'];
}
/**
* Get the cursor to fetch all the shares
*/
private function getShares(): IResult {
$subQuery = $this->connection->getQueryBuilder();
$subQuery->select('*')
->from('share')
->where($subQuery->expr()->isNotNull('parent'))
->andWhere($subQuery->expr()->eq('share_type', $subQuery->expr()->literal(3, IQueryBuilder::PARAM_INT)));
$query = $this->connection->getQueryBuilder();
$query->select('s1.id', 's1.uid_owner', 's1.uid_initiator')
->from($query->createFunction('(' . $subQuery->getSQL() . ')'), 's1')
->join(
's1', 'share', 's2',
$query->expr()->eq('s1.parent', 's2.id')
)
->where($query->expr()->orX(
$query->expr()->eq('s2.share_type', $query->expr()->literal(1, IQueryBuilder::PARAM_INT)),
$query->expr()->eq('s2.share_type', $query->expr()->literal(2, IQueryBuilder::PARAM_INT))
))
->andWhere($query->expr()->eq('s1.item_source', 's2.item_source'));
/** @var IResult $result */
$result = $query->execute();
return $result;
}
/**
* Process a single share
*
* @param array $data
*/
private function processShare(array $data): void {
$id = $data['id'];
$this->addToNotify($data['uid_owner']);
$this->addToNotify($data['uid_initiator']);
$this->deleteShare((int)$id);
}
/**
* Update list of users to notify
*
* @param string $uid
*/
private function addToNotify(string $uid): void {
if (!isset($this->userToNotify[$uid])) {
$this->userToNotify[$uid] = true;
}
}
/**
* Send all notifications
*/
private function sendNotification(): void {
$time = $this->timeFactory->getDateTime();
$notification = $this->notificationManager->createNotification();
$notification->setApp('core')
->setDateTime($time)
->setObject('repair', 'exposing_links')
->setSubject('repair_exposing_links');
$users = array_keys($this->userToNotify);
foreach ($users as $user) {
$notification->setUser((string) $user);
$this->notificationManager->notify($notification);
}
}
private function repair(IOutput $output, int $total): void {
$output->startProgress($total);
$shareResult = $this->getShares();
while ($data = $shareResult->fetch()) {
$this->processShare($data);
$output->advance();
}
$output->finishProgress();
$shareResult->closeCursor();
// Notify all admins
$adminGroup = $this->groupManager->get('admin');
$adminUsers = $adminGroup->getUsers();
foreach ($adminUsers as $user) {
$this->addToNotify($user->getUID());
}
$output->info('Sending notifications to admins and affected users');
$this->sendNotification();
}
public function run(IOutput $output): void {
if ($this->shouldRun() === false || ($total = $this->getTotal()) === 0) {
$output->info('No need to remove link shares.');
return;
}
$output->info('Removing potentially over exposing link shares');
$this->repair($output, $total);
$output->info('Removed potentially over exposing link shares');
}
}
@@ -0,0 +1,138 @@
<?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/>.
*
*/
namespace OC\Repair;
use OCP\DB\Exception;
use OCP\IConfig;
use OCP\IDBConnection;
use OCP\IGroupManager;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
use Psr\Log\LoggerInterface;
use function strlen;
use function substr;
use function urldecode;
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
) {
$this->config = $config;
$this->dbc = $dbc;
$this->groupManager = $groupManager;
$this->logger = $logger;
}
/**
* @inheritDoc
*/
public function getName() {
return 'Repair DAV shares';
}
protected function repairUnencodedGroupShares() {
$qb = $this->dbc->getQueryBuilder();
$qb->select(['id', 'principaluri'])
->from('dav_shares')
->where($qb->expr()->like('principaluri', $qb->createNamedParameter(self::GROUP_PRINCIPAL_PREFIX . '%')));
$updateQuery = $this->dbc->getQueryBuilder();
$updateQuery->update('dav_shares')
->set('principaluri', $updateQuery->createParameter('updatedPrincipalUri'))
->where($updateQuery->expr()->eq('id', $updateQuery->createParameter('shareId')));
$statement = $qb->execute();
while ($share = $statement->fetch()) {
$gid = substr($share['principaluri'], strlen(self::GROUP_PRINCIPAL_PREFIX));
$decodedGid = urldecode($gid);
$encodedGid = urlencode($gid);
if ($gid === $encodedGid
|| !$this->groupManager->groupExists($gid)
|| ($gid !== $decodedGid && $this->groupManager->groupExists($decodedGid))
) {
$this->hintInvalidShares = $this->hintInvalidShares || $gid !== $encodedGid;
continue;
}
// Repair when
// + the group name needs encoding
// + AND it is not encoded yet
// + AND there are no ambivalent groups
try {
$fixedPrincipal = self::GROUP_PRINCIPAL_PREFIX . $encodedGid;
$logParameters = [
'app' => 'core',
'id' => $share['id'],
'old' => $share['principaluri'],
'new' => $fixedPrincipal,
];
$updateQuery
->setParameter('updatedPrincipalUri', $fixedPrincipal)
->setParameter('shareId', $share['id'])
->execute();
$this->logger->info('Repaired principal for dav share {id} from {old} to {new}', $logParameters);
} catch (Exception $e) {
$logParameters['message'] = $e->getMessage();
$logParameters['exception'] = $e;
$this->logger->info('Could not repair principal for dav share {id} from {old} to {new}: {message}', $logParameters);
}
}
return true;
}
/**
* @inheritDoc
*/
public function run(IOutput $output) {
$versionFromBeforeUpdate = $this->config->getSystemValueString('version', '0.0.0');
if (version_compare($versionFromBeforeUpdate, '20.0.8', '<')
&& $this->repairUnencodedGroupShares()
) {
$output->info('Repaired DAV group shares');
if ($this->hintInvalidShares) {
$output->info('Invalid shares might be left in the database, running "occ dav:remove-invalid-shares" can remove them.');
}
}
}
}
@@ -0,0 +1,120 @@
<?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/>
*
*/
namespace OC\Repair;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;
/**
* Repairs shares with invalid data
*/
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 getName() {
return 'Repair invalid shares';
}
/**
* Adjust file share permissions
*/
private function adjustFileSharePermissions(IOutput $out) {
$mask = \OCP\Constants::PERMISSION_READ | \OCP\Constants::PERMISSION_UPDATE | \OCP\Constants::PERMISSION_SHARE;
$builder = $this->connection->getQueryBuilder();
$permsFunc = $builder->expr()->bitwiseAnd('permissions', $mask);
$builder
->update('share')
->set('permissions', $permsFunc)
->where($builder->expr()->eq('item_type', $builder->expr()->literal('file')))
->andWhere($builder->expr()->neq('permissions', $permsFunc));
$updatedEntries = $builder->execute();
if ($updatedEntries > 0) {
$out->info('Fixed file share permissions for ' . $updatedEntries . ' shares');
}
}
/**
* Remove shares where the parent share does not exist anymore
*/
private function removeSharesNonExistingParent(IOutput $out) {
$deletedEntries = 0;
$query = $this->connection->getQueryBuilder();
$query->select('s1.parent')
->from('share', 's1')
->where($query->expr()->isNotNull('s1.parent'))
->andWhere($query->expr()->isNull('s2.id'))
->leftJoin('s1', 'share', 's2', $query->expr()->eq('s1.parent', 's2.id'))
->groupBy('s1.parent')
->setMaxResults(self::CHUNK_SIZE);
$deleteQuery = $this->connection->getQueryBuilder();
$deleteQuery->delete('share')
->where($deleteQuery->expr()->eq('parent', $deleteQuery->createParameter('parent')));
$deletedInLastChunk = self::CHUNK_SIZE;
while ($deletedInLastChunk === self::CHUNK_SIZE) {
$deletedInLastChunk = 0;
$result = $query->execute();
while ($row = $result->fetch()) {
$deletedInLastChunk++;
$deletedEntries += $deleteQuery->setParameter('parent', (int) $row['parent'])
->execute();
}
$result->closeCursor();
}
if ($deletedEntries) {
$out->info('Removed ' . $deletedEntries . ' shares where the parent did not exist');
}
}
public function run(IOutput $out) {
$ocVersionFromBeforeUpdate = $this->config->getSystemValueString('version', '0.0.0');
if (version_compare($ocVersionFromBeforeUpdate, '12.0.0.11', '<')) {
$this->adjustFileSharePermissions($out);
}
$this->removeSharesNonExistingParent($out);
}
}
@@ -0,0 +1,314 @@
<?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/>
*
*/
namespace OC\Repair;
use OCP\DB\QueryBuilder\IQueryBuilder;
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;
/** @var int */
protected $folderMimeTypeId;
public function __construct(IConfig $config,
IDBConnection $connection) {
$this->config = $config;
$this->connection = $connection;
}
public function getName() {
return 'Repair mime types';
}
private function updateMimetypes($updatedMimetypes) {
$query = $this->connection->getQueryBuilder();
$query->select('id')
->from('mimetypes')
->where($query->expr()->eq('mimetype', $query->createParameter('mimetype'), IQueryBuilder::PARAM_INT));
$insert = $this->connection->getQueryBuilder();
$insert->insert('mimetypes')
->setValue('mimetype', $insert->createParameter('mimetype'));
if (empty($this->folderMimeTypeId)) {
$query->setParameter('mimetype', 'httpd/unix-directory');
$result = $query->execute();
$this->folderMimeTypeId = (int)$result->fetchOne();
$result->closeCursor();
}
$update = $this->connection->getQueryBuilder();
$update->update('filecache')
->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))
->andWhere($update->expr()->iLike('name', $update->createParameter('name')))
->setParameter('folder', $this->folderMimeTypeId);
$count = 0;
foreach ($updatedMimetypes as $extension => $mimetype) {
// get target mimetype id
$query->setParameter('mimetype', $mimetype);
$result = $query->execute();
$mimetypeId = (int)$result->fetchOne();
$result->closeCursor();
if (!$mimetypeId) {
// insert mimetype
$insert->setParameter('mimetype', $mimetype);
$insert->execute();
$mimetypeId = $insert->getLastInsertId();
}
// change mimetype for files with x extension
$update->setParameter('mimetype', $mimetypeId)
->setParameter('name', '%' . $this->connection->escapeLikeParameter('.' . $extension));
$count += $update->execute();
}
return $count;
}
private function introduceAsciidocType() {
$updatedMimetypes = [
'adoc' => 'text/asciidoc',
'asciidoc' => 'text/asciidoc',
];
return $this->updateMimetypes($updatedMimetypes);
}
private function introduceImageTypes() {
$updatedMimetypes = [
'jp2' => 'image/jp2',
'webp' => 'image/webp',
];
return $this->updateMimetypes($updatedMimetypes);
}
private function introduceWindowsProgramTypes() {
$updatedMimetypes = [
'htaccess' => 'text/plain',
'bat' => 'application/x-msdos-program',
'cmd' => 'application/cmd',
];
return $this->updateMimetypes($updatedMimetypes);
}
private function introduceLocationTypes() {
$updatedMimetypes = [
'gpx' => 'application/gpx+xml',
'kml' => 'application/vnd.google-earth.kml+xml',
'kmz' => 'application/vnd.google-earth.kmz',
'tcx' => 'application/vnd.garmin.tcx+xml',
];
return $this->updateMimetypes($updatedMimetypes);
}
private function introduceInternetShortcutTypes() {
$updatedMimetypes = [
'url' => 'application/internet-shortcut',
'webloc' => 'application/internet-shortcut'
];
return $this->updateMimetypes($updatedMimetypes);
}
private function introduceStreamingTypes() {
$updatedMimetypes = [
'm3u' => 'audio/mpegurl',
'm3u8' => 'audio/mpegurl',
'pls' => 'audio/x-scpls'
];
return $this->updateMimetypes($updatedMimetypes);
}
private function introduceVisioTypes() {
$updatedMimetypes = [
'vsdm' => 'application/vnd.visio',
'vsdx' => 'application/vnd.visio',
'vssm' => 'application/vnd.visio',
'vssx' => 'application/vnd.visio',
'vstm' => 'application/vnd.visio',
'vstx' => 'application/vnd.visio',
];
return $this->updateMimetypes($updatedMimetypes);
}
private function introduceComicbookTypes() {
$updatedMimetypes = [
'cb7' => 'application/comicbook+7z',
'cba' => 'application/comicbook+ace',
'cbr' => 'application/comicbook+rar',
'cbt' => 'application/comicbook+tar',
'cbtc' => 'application/comicbook+truecrypt',
'cbz' => 'application/comicbook+zip',
];
return $this->updateMimetypes($updatedMimetypes);
}
private function introduceOpenDocumentTemplates() {
$updatedMimetypes = [
'ott' => 'application/vnd.oasis.opendocument.text-template',
'ots' => 'application/vnd.oasis.opendocument.spreadsheet-template',
'otp' => 'application/vnd.oasis.opendocument.presentation-template',
'otg' => 'application/vnd.oasis.opendocument.graphics-template',
];
return $this->updateMimetypes($updatedMimetypes);
}
private function introduceFlatOpenDocumentType() {
$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);
}
private function introduceOrgModeType() {
$updatedMimetypes = [
'org' => 'text/org'
];
return $this->updateMimetypes($updatedMimetypes);
}
private function introduceOnlyofficeFormType() {
$updatedMimetypes = [
"oform" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document.oform",
"docxf" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document.docxf",
];
return $this->updateMimetypes($updatedMimetypes);
}
private function introduceEnhancedMetafileFormatType() {
$updatedMimetypes = [
'emf' => 'image/emf',
];
return $this->updateMimetypes($updatedMimetypes);
}
private function introduceEmlAndMsgFormatType() {
$updatedMimetypes = [
'eml' => 'message/rfc822',
'msg' => 'application/vnd.ms-outlook',
];
return $this->updateMimetypes($updatedMimetypes);
}
/**
* Fix mime types
*/
public function run(IOutput $out) {
$ocVersionFromBeforeUpdate = $this->config->getSystemValueString('version', '0.0.0');
// NOTE TO DEVELOPERS: when adding new mime types, please make sure to
// add a version comparison to avoid doing it every time
if (version_compare($ocVersionFromBeforeUpdate, '12.0.0.14', '<') && $this->introduceImageTypes()) {
$out->info('Fixed image mime types');
}
if (version_compare($ocVersionFromBeforeUpdate, '12.0.0.13', '<') && $this->introduceWindowsProgramTypes()) {
$out->info('Fixed windows program mime types');
}
if (version_compare($ocVersionFromBeforeUpdate, '13.0.0.0', '<') && $this->introduceLocationTypes()) {
$out->info('Fixed geospatial mime types');
}
if (version_compare($ocVersionFromBeforeUpdate, '13.0.0.3', '<') && $this->introduceInternetShortcutTypes()) {
$out->info('Fixed internet-shortcut mime types');
}
if (version_compare($ocVersionFromBeforeUpdate, '13.0.0.6', '<') && $this->introduceStreamingTypes()) {
$out->info('Fixed streaming mime types');
}
if (version_compare($ocVersionFromBeforeUpdate, '14.0.0.8', '<') && $this->introduceVisioTypes()) {
$out->info('Fixed visio mime types');
}
if (version_compare($ocVersionFromBeforeUpdate, '14.0.0.10', '<') && $this->introduceComicbookTypes()) {
$out->info('Fixed comicbook mime types');
}
if (version_compare($ocVersionFromBeforeUpdate, '20.0.0.5', '<') && $this->introduceOpenDocumentTemplates()) {
$out->info('Fixed OpenDocument template mime types');
}
if (version_compare($ocVersionFromBeforeUpdate, '21.0.0.7', '<') && $this->introduceOrgModeType()) {
$out->info('Fixed orgmode mime types');
}
if (version_compare($ocVersionFromBeforeUpdate, '23.0.0.2', '<') && $this->introduceFlatOpenDocumentType()) {
$out->info('Fixed Flat OpenDocument mime types');
}
if (version_compare($ocVersionFromBeforeUpdate, '25.0.0.2', '<') && $this->introduceOnlyofficeFormType()) {
$out->info('Fixed ONLYOFFICE Forms OpenXML mime types');
}
if (version_compare($ocVersionFromBeforeUpdate, '26.0.0.1', '<') && $this->introduceAsciidocType()) {
$out->info('Fixed AsciiDoc mime types');
}
if (version_compare($ocVersionFromBeforeUpdate, '28.0.0.5', '<') && $this->introduceEnhancedMetafileFormatType()) {
$out->info('Fixed Enhanced Metafile Format mime types');
}
if (version_compare($ocVersionFromBeforeUpdate, '28.0.0.8', '<') && $this->introduceEmlAndMsgFormatType()) {
$out->info('Fixed eml and msg mime type');
}
}
}