diff options
author | Morris Jobke <hey@morrisjobke.de> | 2016-01-07 09:14:35 +0100 |
---|---|---|
committer | Morris Jobke <hey@morrisjobke.de> | 2016-01-07 09:14:35 +0100 |
commit | 604897945b01a368893661c0455072c0b390f7e8 (patch) | |
tree | d7adcb3122ad799ad60fb8999841b4d5b024dfa4 /lib/private/repair | |
parent | 470bf234c545ae5d0ad9c73834f8a596593791d6 (diff) | |
download | nextcloud-server-604897945b01a368893661c0455072c0b390f7e8.tar.gz nextcloud-server-604897945b01a368893661c0455072c0b390f7e8.zip |
Move lib/repair to lib/private/repair
Diffstat (limited to 'lib/private/repair')
-rw-r--r-- | lib/private/repair/assetcache.php | 45 | ||||
-rw-r--r-- | lib/private/repair/cleantags.php | 146 | ||||
-rw-r--r-- | lib/private/repair/collation.php | 90 | ||||
-rw-r--r-- | lib/private/repair/dropoldjobs.php | 78 | ||||
-rw-r--r-- | lib/private/repair/dropoldtables.php | 96 | ||||
-rw-r--r-- | lib/private/repair/filletags.php | 55 | ||||
-rw-r--r-- | lib/private/repair/innodb.php | 69 | ||||
-rw-r--r-- | lib/private/repair/oldgroupmembershipshares.php | 117 | ||||
-rw-r--r-- | lib/private/repair/preview.php | 45 | ||||
-rw-r--r-- | lib/private/repair/removegetetagentries.php | 59 | ||||
-rw-r--r-- | lib/private/repair/repairinvalidshares.php | 112 | ||||
-rw-r--r-- | lib/private/repair/repairlegacystorages.php | 263 | ||||
-rw-r--r-- | lib/private/repair/repairmimetypes.php | 360 | ||||
-rw-r--r-- | lib/private/repair/searchlucenetables.php | 77 | ||||
-rw-r--r-- | lib/private/repair/sqliteautoincrement.php | 98 | ||||
-rw-r--r-- | lib/private/repair/updatecertificatestore.php | 88 | ||||
-rw-r--r-- | lib/private/repair/updateoutdatedocsids.php | 108 |
17 files changed, 1906 insertions, 0 deletions
diff --git a/lib/private/repair/assetcache.php b/lib/private/repair/assetcache.php new file mode 100644 index 00000000000..c46aa63a3e4 --- /dev/null +++ b/lib/private/repair/assetcache.php @@ -0,0 +1,45 @@ +<?php +/** + * @author Adam Williamson <awilliam@redhat.com> + * @author Morris Jobke <hey@morrisjobke.de> + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OC\Repair; + +use Doctrine\DBAL\Platforms\MySqlPlatform; +use OC\Hooks\BasicEmitter; + +class AssetCache extends BasicEmitter implements \OC\RepairStep { + + public function getName() { + return 'Clear asset cache after upgrade'; + } + + public function run() { + if (!\OC_Template::isAssetPipelineEnabled()) { + $this->emit('\OC\Repair', 'info', array('Asset pipeline disabled -> nothing to do')); + return; + } + $assetDir = \OC::$server->getConfig()->getSystemValue('assetdirectory', \OC::$SERVERROOT) . '/assets'; + \OC_Helper::rmdirr($assetDir, false); + $this->emit('\OC\Repair', 'info', array('Asset cache cleared.')); + } +} + diff --git a/lib/private/repair/cleantags.php b/lib/private/repair/cleantags.php new file mode 100644 index 00000000000..d16a49fbca7 --- /dev/null +++ b/lib/private/repair/cleantags.php @@ -0,0 +1,146 @@ +<?php +/** + * @author Joas Schilling <nickvergessen@owncloud.com> + * @author Morris Jobke <hey@morrisjobke.de> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @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 OC\Hooks\BasicEmitter; +use OC\RepairStep; +use OCP\IDBConnection; + +/** + * Class RepairConfig + * + * @package OC\Repair + */ +class CleanTags extends BasicEmitter implements RepairStep { + + /** @var IDBConnection */ + protected $connection; + + /** + * @param IDBConnection $connection + */ + public function __construct(IDBConnection $connection) { + $this->connection = $connection; + } + + /** + * @return string + */ + public function getName() { + return 'Clean tags and favorites'; + } + + /** + * Updates the configuration after running an update + */ + public function run() { + $this->deleteOrphanFileEntries(); + $this->deleteOrphanTagEntries(); + $this->deleteOrphanCategoryEntries(); + } + + /** + * Delete tag entries for deleted files + */ + protected function deleteOrphanFileEntries() { + $this->deleteOrphanEntries( + '%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() { + $this->deleteOrphanEntries( + '%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() { + $this->deleteOrphanEntries( + '%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($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 = array(); + 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, \Doctrine\DBAL\Connection::PARAM_INT_ARRAY); + $qb->execute(); + } + } + + if ($repairInfo) { + $this->emit('\OC\Repair', 'info', array(sprintf($repairInfo, sizeof($orphanItems)))); + } + } +} diff --git a/lib/private/repair/collation.php b/lib/private/repair/collation.php new file mode 100644 index 00000000000..7eb14f0ded2 --- /dev/null +++ b/lib/private/repair/collation.php @@ -0,0 +1,90 @@ +<?php +/** + * @author Morris Jobke <hey@morrisjobke.de> + * @author Robin Appelman <icewind@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OC\Repair; + +use Doctrine\DBAL\Platforms\MySqlPlatform; +use OC\Hooks\BasicEmitter; + +class Collation extends BasicEmitter implements \OC\RepairStep { + /** + * @var \OCP\IConfig + */ + protected $config; + + /** + * @var \OC\DB\Connection + */ + protected $connection; + + /** + * @param \OCP\IConfig $config + * @param \OC\DB\Connection $connection + */ + public function __construct($config, $connection) { + $this->connection = $connection; + $this->config = $config; + } + + public function getName() { + return 'Repair MySQL collation'; + } + + /** + * Fix mime types + */ + public function run() { + if (!$this->connection->getDatabasePlatform() instanceof MySqlPlatform) { + $this->emit('\OC\Repair', 'info', array('Not a mysql database -> nothing to no')); + return; + } + + $tables = $this->getAllNonUTF8BinTables($this->connection); + foreach ($tables as $table) { + $this->emit('\OC\Repair', 'info', array("Change collation for $table ...")); + $query = $this->connection->prepare('ALTER TABLE `' . $table . '` CONVERT TO CHARACTER SET utf8 COLLATE utf8_bin;'); + $query->execute(); + } + } + + /** + * @param \Doctrine\DBAL\Connection $connection + * @return string[] + */ + protected function getAllNonUTF8BinTables($connection) { + $dbName = $this->config->getSystemValue("dbname"); + $rows = $connection->fetchAll( + "SELECT DISTINCT(TABLE_NAME) AS `table`" . + " FROM INFORMATION_SCHEMA . COLUMNS" . + " WHERE TABLE_SCHEMA = ?" . + " AND (COLLATION_NAME <> 'utf8_bin' OR CHARACTER_SET_NAME <> 'utf8')" . + " AND TABLE_NAME LIKE \"*PREFIX*%\"", + array($dbName) + ); + $result = array(); + foreach ($rows as $row) { + $result[] = $row['table']; + } + return $result; + } +} + diff --git a/lib/private/repair/dropoldjobs.php b/lib/private/repair/dropoldjobs.php new file mode 100644 index 00000000000..89d7f96a144 --- /dev/null +++ b/lib/private/repair/dropoldjobs.php @@ -0,0 +1,78 @@ +<?php +/** + * @author Arthur Schiwon <blizzz@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @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 OC\Hooks\BasicEmitter; +use OC\RepairStep; +use OCP\BackgroundJob\IJobList; + +class DropOldJobs extends BasicEmitter implements RepairStep { + + /** @var IJobList */ + protected $jobList; + + /** + * @param IJobList $jobList + */ + public function __construct(IJobList $jobList) { + $this->jobList = $jobList; + } + + /** + * Returns the step's name + * + * @return string + */ + public function getName() { + return 'Drop old background jobs'; + } + + /** + * Run repair step. + * Must throw exception on error. + * + * @throws \Exception in case of failure + */ + public function run() { + $oldJobs = $this->oldJobs(); + foreach($oldJobs as $job) { + if($this->jobList->has($job['class'], $job['arguments'])) { + $this->jobList->remove($job['class'], $job['arguments']); + } + } + } + + /** + * returns a list of old jobs as an associative array with keys 'class' and + * 'arguments'. + * + * @return array + */ + public function oldJobs() { + return [ + ['class' => 'OC_Cache_FileGlobalGC', 'arguments' => null], + ['class' => 'OC\Cache\FileGlobalGC', 'arguments' => null], + ]; + } + + +} diff --git a/lib/private/repair/dropoldtables.php b/lib/private/repair/dropoldtables.php new file mode 100644 index 00000000000..2d7fc8376b3 --- /dev/null +++ b/lib/private/repair/dropoldtables.php @@ -0,0 +1,96 @@ +<?php +/** + * @author Joas Schilling <nickvergessen@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @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 OC\Hooks\BasicEmitter; +use OC\RepairStep; +use OCP\IDBConnection; + +class DropOldTables extends BasicEmitter implements RepairStep { + + /** @var IDBConnection */ + protected $connection; + + /** + * @param IDBConnection $connection + */ + public function __construct(IDBConnection $connection) { + $this->connection = $connection; + } + + /** + * Returns the step's name + * + * @return string + */ + public function getName() { + return 'Drop old database tables'; + } + + /** + * Run repair step. + * Must throw exception on error. + * + * @throws \Exception in case of failure + */ + public function run() { + foreach ($this->oldDatabaseTables() as $tableName) { + if ($this->connection->tableExists($tableName)){ + $this->emit('\OC\Repair', 'info', [ + sprintf('Table %s has been deleted', $tableName) + ]); + $this->connection->dropTable($tableName); + } + } + } + + /** + * Returns a list of outdated tables which are not used anymore + * @return array + */ + protected function oldDatabaseTables() { + return [ + 'calendar_calendars', + 'calendar_objects', + 'calendar_share_calendar', + 'calendar_share_event', + 'file_map', + 'foldersize', + 'fscache', + 'gallery_sharing', + 'locks', + 'log', + 'media_albums', + 'media_artists', + 'media_sessions', + 'media_songs', + 'media_users', + 'permissions', + 'pictures_images_cache', + 'principalgroups', + 'principals', + 'queuedtasks', + 'sharing', + ]; + } +} diff --git a/lib/private/repair/filletags.php b/lib/private/repair/filletags.php new file mode 100644 index 00000000000..8cfc4a7c258 --- /dev/null +++ b/lib/private/repair/filletags.php @@ -0,0 +1,55 @@ +<?php +/** + * @author Joas Schilling <nickvergessen@owncloud.com> + * @author Morris Jobke <hey@morrisjobke.de> + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @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 OC\Hooks\BasicEmitter; + +class FillETags extends BasicEmitter implements \OC\RepairStep { + + /** @var \OCP\IDBConnection */ + protected $connection; + + /** + * @param \OCP\IDBConnection $connection + */ + public function __construct($connection) { + $this->connection = $connection; + } + + public function getName() { + return 'Generate ETags for file where no ETag is present.'; + } + + public function run() { + $qb = $this->connection->getQueryBuilder(); + $qb->update('filecache') + ->set('etag', $qb->expr()->literal('xxx')) + ->where($qb->expr()->eq('etag', $qb->expr()->literal(''))) + ->orWhere($qb->expr()->isNull('etag')); + + $result = $qb->execute(); + $this->emit('\OC\Repair', 'info', array("ETags have been fixed for $result files/folders.")); + } +} + diff --git a/lib/private/repair/innodb.php b/lib/private/repair/innodb.php new file mode 100644 index 00000000000..ab94c79468d --- /dev/null +++ b/lib/private/repair/innodb.php @@ -0,0 +1,69 @@ +<?php +/** + * @author Jörn Friedrich Dreyer <jfd@butonic.de> + * @author Morris Jobke <hey@morrisjobke.de> + * @author Robin Appelman <icewind@owncloud.com> + * @author Thomas Müller <thomas.mueller@tmit.eu> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OC\Repair; + +use Doctrine\DBAL\Platforms\MySqlPlatform; +use OC\Hooks\BasicEmitter; + +class InnoDB extends BasicEmitter implements \OC\RepairStep { + + public function getName() { + return 'Repair MySQL database engine'; + } + + /** + * Fix mime types + */ + public function run() { + $connection = \OC_DB::getConnection(); + if (!$connection->getDatabasePlatform() instanceof MySqlPlatform) { + $this->emit('\OC\Repair', 'info', array('Not a mysql database -> nothing to do')); + return; + } + + $tables = $this->getAllMyIsamTables($connection); + if (is_array($tables)) { + foreach ($tables as $table) { + $connection->exec("ALTER TABLE $table ENGINE=InnoDB;"); + $this->emit('\OC\Repair', 'info', array("Fixed $table")); + } + } + } + + /** + * @param \Doctrine\DBAL\Connection $connection + * @return string[] + */ + private function getAllMyIsamTables($connection) { + $dbName = \OC::$server->getConfig()->getSystemValue("dbname"); + $result = $connection->fetchArray( + "SELECT table_name FROM information_schema.tables WHERE table_schema = ? AND engine = 'MyISAM' AND TABLE_NAME LIKE \"*PREFIX*%\"", + array($dbName) + ); + + return $result; + } +} + diff --git a/lib/private/repair/oldgroupmembershipshares.php b/lib/private/repair/oldgroupmembershipshares.php new file mode 100644 index 00000000000..2d701ac9fb7 --- /dev/null +++ b/lib/private/repair/oldgroupmembershipshares.php @@ -0,0 +1,117 @@ +<?php +/** + * @author Joas Schilling <nickvergessen@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @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 OC\Hooks\BasicEmitter; +use OC\RepairStep; +use OCP\IDBConnection; +use OCP\IGroupManager; +use OCP\Share; + +class OldGroupMembershipShares extends BasicEmitter implements RepairStep { + + /** @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() { + $deletedEntries = 0; + + $query = $this->connection->getQueryBuilder(); + $query->select(['s1.id', $query->createFunction('s1.`share_with` AS `user`'), $query->createFunction('s2.`share_with` AS `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(Share::SHARE_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) { + $this->emit('\OC\Repair', 'info', array('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; + } +} diff --git a/lib/private/repair/preview.php b/lib/private/repair/preview.php new file mode 100644 index 00000000000..2284da93734 --- /dev/null +++ b/lib/private/repair/preview.php @@ -0,0 +1,45 @@ +<?php +/** + * @author Georg Ehrke <georg@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @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 OC\Files\View; +use OC\Hooks\BasicEmitter; + +class Preview extends BasicEmitter implements \OC\RepairStep { + + public function getName() { + return 'Cleaning-up broken previews'; + } + + public function run() { + $view = new View('/'); + $children = $view->getDirectoryContent('/'); + + foreach ($children as $child) { + if ($view->is_dir($child->getPath())) { + $thumbnailsFolder = $child->getPath() . '/thumbnails'; + if ($view->is_dir($thumbnailsFolder)) { + $view->rmdir($thumbnailsFolder); + } + } + } + } +}
\ No newline at end of file diff --git a/lib/private/repair/removegetetagentries.php b/lib/private/repair/removegetetagentries.php new file mode 100644 index 00000000000..40040763654 --- /dev/null +++ b/lib/private/repair/removegetetagentries.php @@ -0,0 +1,59 @@ +<?php +/** + * @author Morris Jobke <hey@morrisjobke.de> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @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 OC\Hooks\BasicEmitter; +use OCP\IDBConnection; + +class RemoveGetETagEntries extends BasicEmitter { + + /** + * @var IDBConnection + */ + protected $connection; + + /** + * @param IDBConnection $connection + */ + public function __construct(IDBConnection $connection) { + $this->connection = $connection; + } + + public function getName() { + return 'Remove getetag entries in properties table'; + } + + /** + * Removes all entries with the key "{DAV:}getetag" from the table properties + */ + public function run() { + $sql = 'DELETE FROM `*PREFIX*properties`' + . ' WHERE `propertyname` = ?'; + $deletedRows = $this->connection->executeUpdate($sql, ['{DAV:}getetag']); + + $this->emit( + '\OC\Repair', + 'info', + ['Removed ' . $deletedRows . ' unneeded "{DAV:}getetag" entries from properties table.'] + ); + } +} diff --git a/lib/private/repair/repairinvalidshares.php b/lib/private/repair/repairinvalidshares.php new file mode 100644 index 00000000000..5a4cb445ce9 --- /dev/null +++ b/lib/private/repair/repairinvalidshares.php @@ -0,0 +1,112 @@ +<?php +/** + * @author Morris Jobke <hey@morrisjobke.de> + * @author Vincent Petry <pvince81@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @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 OC\Hooks\BasicEmitter; + +/** + * Repairs shares with invalid data + */ +class RepairInvalidShares extends BasicEmitter implements \OC\RepairStep { + + /** + * @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'; + } + + /** + * Past bugs would make it possible to set an expiration date on user shares even + * though it is not supported. This functions removes the expiration date from such entries. + */ + private function removeExpirationDateFromNonLinkShares() { + $builder = $this->connection->getQueryBuilder(); + $builder + ->update('share') + ->set('expiration', 'null') + ->where($builder->expr()->isNotNull('expiration')) + ->andWhere($builder->expr()->neq('share_type', $builder->expr()->literal(\OC\Share\Constants::SHARE_TYPE_LINK))); + + $updatedEntries = $builder->execute(); + if ($updatedEntries > 0) { + $this->emit('\OC\Repair', 'info', array('Removed invalid expiration date from ' . $updatedEntries . ' shares')); + } + } + + /** + * Remove shares where the parent share does not exist anymore + */ + private function removeSharesNonExistingParent() { + $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'); + + $deleteQuery = $this->connection->getQueryBuilder(); + $deleteQuery->delete('share') + ->where($query->expr()->eq('parent', $deleteQuery->createParameter('parent'))); + + $result = $query->execute(); + while ($row = $result->fetch()) { + $deletedEntries += $deleteQuery->setParameter('parent', (int) $row['parent']) + ->execute(); + } + $result->closeCursor(); + + if ($deletedEntries) { + $this->emit('\OC\Repair', 'info', array('Removed ' . $deletedEntries . ' shares where the parent did not exist')); + } + } + + public function run() { + $ocVersionFromBeforeUpdate = $this->config->getSystemValue('version', '0.0.0'); + if (version_compare($ocVersionFromBeforeUpdate, '8.2.0.7', '<')) { + // this situation was only possible before 8.2 + $this->removeExpirationDateFromNonLinkShares(); + } + + $this->removeSharesNonExistingParent(); + } +} diff --git a/lib/private/repair/repairlegacystorages.php b/lib/private/repair/repairlegacystorages.php new file mode 100644 index 00000000000..5ba452cbbc6 --- /dev/null +++ b/lib/private/repair/repairlegacystorages.php @@ -0,0 +1,263 @@ +<?php +/** + * @author Joas Schilling <nickvergessen@owncloud.com> + * @author Morris Jobke <hey@morrisjobke.de> + * @author Vincent Petry <pvince81@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @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 OC\Files\Cache\Storage; +use OC\Hooks\BasicEmitter; +use OC\RepairException; + +class RepairLegacyStorages extends BasicEmitter { + /** + * @var \OCP\IConfig + */ + protected $config; + + /** + * @var \OCP\IDBConnection + */ + protected $connection; + + protected $findStorageInCacheStatement; + protected $renameStorageStatement; + + /** + * @param \OCP\IConfig $config + * @param \OCP\IDBConnection $connection + */ + public function __construct($config, $connection) { + $this->connection = $connection; + $this->config = $config; + + $this->findStorageInCacheStatement = $this->connection->prepare( + 'SELECT DISTINCT `storage` FROM `*PREFIX*filecache`' + . ' WHERE `storage` in (?, ?)' + ); + $this->renameStorageStatement = $this->connection->prepare( + 'UPDATE `*PREFIX*storages`' + . ' SET `id` = ?' + . ' WHERE `id` = ?' + ); + } + + public function getName() { + return 'Repair legacy storages'; + } + + /** + * Extracts the user id from a legacy storage id + * + * @param string $storageId legacy storage id in the + * format "local::/path/to/datadir/userid" + * @return string user id extracted from the storage id + */ + private function extractUserId($storageId) { + $storageId = rtrim($storageId, '/'); + $pos = strrpos($storageId, '/'); + return substr($storageId, $pos + 1); + } + + /** + * Fix the given legacy storage by renaming the old id + * to the new id. If the new id already exists, whichever + * storage that has data in the file cache will be used. + * If both have data, nothing will be done and false is + * returned. + * + * @param string $oldId old storage id + * @param int $oldNumericId old storage numeric id + * @param string $userId + * @return bool true if fixed, false otherwise + * @throws RepairException + */ + private function fixLegacyStorage($oldId, $oldNumericId, $userId = null) { + // check whether the new storage already exists + if (is_null($userId)) { + $userId = $this->extractUserId($oldId); + } + $newId = 'home::' . $userId; + + // check if target id already exists + $newNumericId = Storage::getNumericStorageId($newId); + if (!is_null($newNumericId)) { + $newNumericId = (int)$newNumericId; + // try and resolve the conflict + // check which one of "local::" or "home::" needs to be kept + $this->findStorageInCacheStatement->execute(array($oldNumericId, $newNumericId)); + $row1 = $this->findStorageInCacheStatement->fetch(); + $row2 = $this->findStorageInCacheStatement->fetch(); + $this->findStorageInCacheStatement->closeCursor(); + if ($row2 !== false) { + // two results means both storages have data, not auto-fixable + throw new RepairException( + 'Could not automatically fix legacy storage ' + . '"' . $oldId . '" => "' . $newId . '"' + . ' because they both have data.' + ); + } + if ($row1 === false || (int)$row1['storage'] === $oldNumericId) { + // old storage has data, then delete the empty new id + $toDelete = $newId; + } else if ((int)$row1['storage'] === $newNumericId) { + // new storage has data, then delete the empty old id + $toDelete = $oldId; + } else { + // unknown case, do not continue + return false; + } + + // delete storage including file cache + Storage::remove($toDelete); + + // if we deleted the old id, the new id will be used + // automatically + if ($toDelete === $oldId) { + // nothing more to do + return true; + } + } + + // rename old id to new id + $newId = Storage::adjustStorageId($newId); + $oldId = Storage::adjustStorageId($oldId); + $rowCount = $this->renameStorageStatement->execute(array($newId, $oldId)); + $this->renameStorageStatement->closeCursor(); + return ($rowCount === 1); + } + + /** + * Converts legacy home storage ids in the format + * "local::/data/dir/path/userid/" to the new format "home::userid" + */ + public function run() { + // only run once + if ($this->config->getAppValue('core', 'repairlegacystoragesdone') === 'yes') { + return; + } + + $dataDir = $this->config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data/'); + $dataDir = rtrim($dataDir, '/') . '/'; + $dataDirId = 'local::' . $dataDir; + + $count = 0; + $hasWarnings = false; + + $this->connection->beginTransaction(); + + // note: not doing a direct UPDATE with the REPLACE function + // because regexp search/extract is needed and it is not guaranteed + // to work on all database types + $sql = 'SELECT `id`, `numeric_id` FROM `*PREFIX*storages`' + . ' WHERE `id` LIKE ?' + . ' ORDER BY `id`'; + $result = $this->connection->executeQuery($sql, array($dataDirId . '%')); + + while ($row = $result->fetch()) { + $currentId = $row['id']; + // one entry is the datadir itself + if ($currentId === $dataDirId) { + continue; + } + + try { + if ($this->fixLegacyStorage($currentId, (int)$row['numeric_id'])) { + $count++; + } + } + catch (RepairException $e) { + $hasWarnings = true; + $this->emit( + '\OC\Repair', + 'warning', + array('Could not repair legacy storage ' . $currentId . ' automatically.') + ); + } + } + + // check for md5 ids, not in the format "prefix::" + $sql = 'SELECT COUNT(*) AS "c" FROM `*PREFIX*storages`' + . ' WHERE `id` NOT LIKE \'%::%\''; + $result = $this->connection->executeQuery($sql); + $row = $result->fetch(); + + // find at least one to make sure it's worth + // querying the user list + if ((int)$row['c'] > 0) { + $userManager = \OC::$server->getUserManager(); + + // use chunks to avoid caching too many users in memory + $limit = 30; + $offset = 0; + + do { + // query the next page of users + $results = $userManager->search('', $limit, $offset); + $storageIds = array(); + foreach ($results as $uid => $userObject) { + $storageId = $dataDirId . $uid . '/'; + if (strlen($storageId) <= 64) { + // skip short storage ids as they were handled in the previous section + continue; + } + $storageIds[$uid] = $storageId; + } + + if (count($storageIds) > 0) { + // update the storages of these users + foreach ($storageIds as $uid => $storageId) { + $numericId = Storage::getNumericStorageId($storageId); + try { + if (!is_null($numericId) && $this->fixLegacyStorage($storageId, (int)$numericId)) { + $count++; + } + } + catch (RepairException $e) { + $hasWarnings = true; + $this->emit( + '\OC\Repair', + 'warning', + array('Could not repair legacy storage ' . $storageId . ' automatically.') + ); + } + } + } + $offset += $limit; + } while (count($results) >= $limit); + } + + $this->emit('\OC\Repair', 'info', array('Updated ' . $count . ' legacy home storage ids')); + + $this->connection->commit(); + + if ($hasWarnings) { + $this->emit( + '\OC\Repair', + 'warning', + array('Some legacy storages could not be repaired. Please manually fix them then re-run ./occ maintenance:repair') + ); + } else { + // if all were done, no need to redo the repair during next upgrade + $this->config->setAppValue('core', 'repairlegacystoragesdone', 'yes'); + } + } +} diff --git a/lib/private/repair/repairmimetypes.php b/lib/private/repair/repairmimetypes.php new file mode 100644 index 00000000000..e687dbde688 --- /dev/null +++ b/lib/private/repair/repairmimetypes.php @@ -0,0 +1,360 @@ +<?php +/** + * @author Joas Schilling <nickvergessen@owncloud.com> + * @author Morris Jobke <hey@morrisjobke.de> + * @author Normal Ra <normalraw@gmail.com> + * @author Olivier Paroz <github@oparoz.com> + * @author Roeland Jago Douma <rullzer@owncloud.com> + * @author Victor Dubiniuk <dubiniuk@owncloud.com> + * @author Vincent Petry <pvince81@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @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 OC\Hooks\BasicEmitter; + +class RepairMimeTypes extends BasicEmitter implements \OC\RepairStep { + /** + * @var \OCP\IConfig + */ + protected $config; + + /** + * @var int + */ + protected $folderMimeTypeId; + + /** + * @param \OCP\IConfig $config + */ + public function __construct($config) { + $this->config = $config; + } + + public function getName() { + return 'Repair mime types'; + } + + private static function existsStmt() { + return \OC_DB::prepare(' + SELECT count(`mimetype`) + FROM `*PREFIX*mimetypes` + WHERE `mimetype` = ? + '); + } + + private static function getIdStmt() { + return \OC_DB::prepare(' + SELECT `id` + FROM `*PREFIX*mimetypes` + WHERE `mimetype` = ? + '); + } + + private static function insertStmt() { + return \OC_DB::prepare(' + INSERT INTO `*PREFIX*mimetypes` ( `mimetype` ) + VALUES ( ? ) + '); + } + + private static function updateWrongStmt() { + return \OC_DB::prepare(' + UPDATE `*PREFIX*filecache` + SET `mimetype` = ( + SELECT `id` + FROM `*PREFIX*mimetypes` + WHERE `mimetype` = ? + ) WHERE `mimetype` = ? + '); + } + + private static function deleteStmt() { + return \OC_DB::prepare(' + DELETE FROM `*PREFIX*mimetypes` + WHERE `id` = ? + '); + } + + private static function updateByNameStmt() { + return \OC_DB::prepare(' + UPDATE `*PREFIX*filecache` + SET `mimetype` = ? + WHERE `mimetype` <> ? AND `mimetype` <> ? AND `name` ILIKE ? + '); + } + + private function repairMimetypes($wrongMimetypes) { + foreach ($wrongMimetypes as $wrong => $correct) { + // do we need to remove a wrong mimetype? + $result = \OC_DB::executeAudited(self::getIdStmt(), array($wrong)); + $wrongId = $result->fetchOne(); + + if ($wrongId !== false) { + // do we need to insert the correct mimetype? + $result = \OC_DB::executeAudited(self::existsStmt(), array($correct)); + $exists = $result->fetchOne(); + + if (!is_null($correct)) { + if (!$exists) { + // insert mimetype + \OC_DB::executeAudited(self::insertStmt(), array($correct)); + } + + // change wrong mimetype to correct mimetype in filecache + \OC_DB::executeAudited(self::updateWrongStmt(), array($correct, $wrongId)); + } + + // delete wrong mimetype + \OC_DB::executeAudited(self::deleteStmt(), array($wrongId)); + + } + } + } + + private function updateMimetypes($updatedMimetypes) { + if (empty($this->folderMimeTypeId)) { + $result = \OC_DB::executeAudited(self::getIdStmt(), array('httpd/unix-directory')); + $this->folderMimeTypeId = (int)$result->fetchOne(); + } + + foreach ($updatedMimetypes as $extension => $mimetype) { + $result = \OC_DB::executeAudited(self::existsStmt(), array($mimetype)); + $exists = $result->fetchOne(); + + if (!$exists) { + // insert mimetype + \OC_DB::executeAudited(self::insertStmt(), array($mimetype)); + } + + // get target mimetype id + $result = \OC_DB::executeAudited(self::getIdStmt(), array($mimetype)); + $mimetypeId = $result->fetchOne(); + + // change mimetype for files with x extension + \OC_DB::executeAudited(self::updateByNameStmt(), array($mimetypeId, $this->folderMimeTypeId, $mimetypeId, '%.' . $extension)); + } + } + + private function fixOfficeMimeTypes() { + // update wrong mimetypes + $wrongMimetypes = array( + 'application/mspowerpoint' => 'application/vnd.ms-powerpoint', + 'application/msexcel' => 'application/vnd.ms-excel', + ); + + self::repairMimetypes($wrongMimetypes); + + $updatedMimetypes = array( + 'docx' => 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', + 'xlsx' => 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', + 'pptx' => 'application/vnd.openxmlformats-officedocument.presentationml.presentation', + ); + + + // separate doc from docx etc + self::updateMimetypes($updatedMimetypes); + + } + + private function fixApkMimeType() { + $updatedMimetypes = array( + 'apk' => 'application/vnd.android.package-archive', + ); + + self::updateMimetypes($updatedMimetypes); + } + + private function fixFontsMimeTypes() { + // update wrong mimetypes + $wrongMimetypes = array( + 'font' => null, + 'font/opentype' => 'application/font-sfnt', + 'application/x-font-ttf' => 'application/font-sfnt', + ); + + self::repairMimetypes($wrongMimetypes); + + $updatedMimetypes = array( + 'ttf' => 'application/font-sfnt', + 'otf' => 'application/font-sfnt', + 'pfb' => 'application/x-font', + ); + + self::updateMimetypes($updatedMimetypes); + } + + private function fixPostscriptMimeType() { + $updatedMimetypes = array( + 'eps' => 'application/postscript', + 'ps' => 'application/postscript', + ); + + self::updateMimetypes($updatedMimetypes); + } + + private function introduceRawMimeType() { + $updatedMimetypes = array( + 'arw' => 'image/x-dcraw', + 'cr2' => 'image/x-dcraw', + 'dcr' => 'image/x-dcraw', + 'dng' => 'image/x-dcraw', + 'erf' => 'image/x-dcraw', + 'iiq' => 'image/x-dcraw', + 'k25' => 'image/x-dcraw', + 'kdc' => 'image/x-dcraw', + 'mef' => 'image/x-dcraw', + 'nef' => 'image/x-dcraw', + 'orf' => 'image/x-dcraw', + 'pef' => 'image/x-dcraw', + 'raf' => 'image/x-dcraw', + 'rw2' => 'image/x-dcraw', + 'srf' => 'image/x-dcraw', + 'sr2' => 'image/x-dcraw', + 'xrf' => 'image/x-dcraw', + ); + + self::updateMimetypes($updatedMimetypes); + } + + private function introduce3dImagesMimeType() { + $updatedMimetypes = array( + 'jps' => 'image/jpeg', + 'mpo' => 'image/jpeg', + ); + + self::updateMimetypes($updatedMimetypes); + } + + private function introduceConfMimeType() { + $updatedMimetypes = array( + 'conf' => 'text/plain', + 'cnf' => 'text/plain', + ); + + self::updateMimetypes($updatedMimetypes); + } + + private function introduceYamlMimeType() { + $updatedMimetypes = array( + 'yaml' => 'application/yaml', + 'yml' => 'application/yaml', + ); + + self::updateMimetypes($updatedMimetypes); + } + + private function introduceJavaMimeType() { + $updatedMimetypes = array( + 'class' => 'application/java', + 'java' => 'text/x-java-source', + ); + + self::updateMimetypes($updatedMimetypes); + } + + private function introduceHppMimeType() { + $updatedMimetypes = array( + 'hpp' => 'text/x-h', + ); + + self::updateMimetypes($updatedMimetypes); + } + + private function introduceRssMimeType() { + $updatedMimetypes = array( + 'rss' => 'application/rss+xml', + ); + + self::updateMimetypes($updatedMimetypes); + } + + private function introduceRtfMimeType() { + $updatedMimetypes = array( + 'rtf' => 'text/rtf', + ); + + self::updateMimetypes($updatedMimetypes); + } + + /** + * Fix mime types + */ + public function run() { + + $ocVersionFromBeforeUpdate = $this->config->getSystemValue('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 + + // only update mime types if necessary as it can be expensive + if (version_compare($ocVersionFromBeforeUpdate, '8.2.0', '<')) { + if ($this->fixOfficeMimeTypes()) { + $this->emit('\OC\Repair', 'info', array('Fixed office mime types')); + } + + if ($this->fixApkMimeType()) { + $this->emit('\OC\Repair', 'info', array('Fixed APK mime type')); + } + + if ($this->fixFontsMimeTypes()) { + $this->emit('\OC\Repair', 'info', array('Fixed fonts mime types')); + } + + if ($this->fixPostscriptMimeType()) { + $this->emit('\OC\Repair', 'info', array('Fixed Postscript mime types')); + } + + if ($this->introduceRawMimeType()) { + $this->emit('\OC\Repair', 'info', array('Fixed Raw mime types')); + } + + if ($this->introduce3dImagesMimeType()) { + $this->emit('\OC\Repair', 'info', array('Fixed 3D images mime types')); + } + + if ($this->introduceConfMimeType()) { + $this->emit('\OC\Repair', 'info', array('Fixed Conf/cnf mime types')); + } + + if ($this->introduceYamlMimeType()) { + $this->emit('\OC\Repair', 'info', array('Fixed Yaml/Yml mime types')); + } + } + + // Mimetype updates from #19272 + if (version_compare($ocVersionFromBeforeUpdate, '8.2.0.8', '<')) { + if ($this->introduceJavaMimeType()) { + $this->emit('\OC\Repair', 'info', array('Fixed java/class mime types')); + } + + if ($this->introduceHppMimeType()) { + $this->emit('\OC\Repair', 'info', array('Fixed hpp mime type')); + } + + if ($this->introduceRssMimeType()) { + $this->emit('\OC\Repair', 'info', array('Fixed rss mime type')); + } + + if ($this->introduceRtfMimeType()) { + $this->emit('\OC\Repair', 'info', array('Fixed rtf mime type')); + } + } + } +} diff --git a/lib/private/repair/searchlucenetables.php b/lib/private/repair/searchlucenetables.php new file mode 100644 index 00000000000..5ae8a300246 --- /dev/null +++ b/lib/private/repair/searchlucenetables.php @@ -0,0 +1,77 @@ +<?php +/** + * @author Jörn Friedrich Dreyer <jfd@butonic.de> + * @author Morris Jobke <hey@morrisjobke.de> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @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 OC\Hooks\BasicEmitter; + +class SearchLuceneTables extends BasicEmitter implements \OC\RepairStep { + + public function getName() { + return 'Repair duplicate entries in oc_lucene_status'; + } + + /** + * Fix duplicate entries in oc_lucene_status + * + * search_lucene prior to v0.5.0 did not have a primary key on the lucene_status table. Newer versions do, which + * causes the migration check to fail because it tries to insert duplicate rows into the new schema. + * + * FIXME Currently, apps don't have a way of repairing anything before the migration check: + * @link https://github.com/owncloud/core/issues/10980 + * + * As a result this repair step needs to live in the core repo, although it belongs into search_lucene: + * @link https://github.com/owncloud/core/issues/10205#issuecomment-54957557 + * + * It will completely remove any rows that make a file id have more than one status: + * fileid | status fileid | status + * --------+-------- will become --------+-------- + * 2 | E 3 | E + * 2 | I + * 3 | E + * + * search_lucene will then reindex the fileids without a status when the next indexing job is executed + */ + public function run() { + if (\OC_DB::tableExists('lucene_status')) { + $this->emit('\OC\Repair', 'info', array('removing duplicate entries from lucene_status')); + + $connection = \OC_DB::getConnection(); + $query = $connection->prepare(' + DELETE FROM `*PREFIX*lucene_status` + WHERE `fileid` IN ( + SELECT `fileid` + FROM ( + SELECT `fileid` + FROM `*PREFIX*lucene_status` + GROUP BY `fileid` + HAVING count(`fileid`) > 1 + ) AS `mysqlerr1093hack` + )'); + $query->execute(); + } else { + $this->emit('\OC\Repair', 'info', array('lucene_status table does not exist -> nothing to do')); + } + } + +} + diff --git a/lib/private/repair/sqliteautoincrement.php b/lib/private/repair/sqliteautoincrement.php new file mode 100644 index 00000000000..70d0adae5d7 --- /dev/null +++ b/lib/private/repair/sqliteautoincrement.php @@ -0,0 +1,98 @@ +<?php +/** + * @author Vincent Petry <pvince81@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @license AGPL-3.0 + * + * This code is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License, version 3, + * as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License, version 3, + * along with this program. If not, see <http://www.gnu.org/licenses/> + * + */ + +namespace OC\Repair; + +use Doctrine\DBAL\Platforms\SqlitePlatform; +use Doctrine\DBAL\Schema\SchemaException; +use Doctrine\DBAL\Schema\SchemaDiff; +use Doctrine\DBAL\Schema\TableDiff; +use Doctrine\DBAL\Schema\ColumnDiff; +use OC\Hooks\BasicEmitter; + +/** + * Fixes Sqlite autoincrement by forcing the SQLite table schemas to be + * altered in order to retrigger SQL schema generation through OCSqlitePlatform. + */ +class SqliteAutoincrement extends BasicEmitter implements \OC\RepairStep { + /** + * @var \OC\DB\Connection + */ + protected $connection; + + /** + * @param \OC\DB\Connection $connection + */ + public function __construct($connection) { + $this->connection = $connection; + } + + public function getName() { + return 'Repair SQLite autoincrement'; + } + + /** + * Fix mime types + */ + public function run() { + if (!$this->connection->getDatabasePlatform() instanceof SqlitePlatform) { + return; + } + + $sourceSchema = $this->connection->getSchemaManager()->createSchema(); + + $schemaDiff = new SchemaDiff(); + + foreach ($sourceSchema->getTables() as $tableSchema) { + $primaryKey = $tableSchema->getPrimaryKey(); + if (!$primaryKey) { + continue; + } + + $columnNames = $primaryKey->getColumns(); + + // add a column diff for every primary key column, + // but do not actually change anything, this will + // force the generation of SQL statements to alter + // those tables, which will then trigger the + // specific SQL code from OCSqlitePlatform + try { + $tableDiff = new TableDiff($tableSchema->getName()); + $tableDiff->fromTable = $tableSchema; + foreach ($columnNames as $columnName) { + $columnSchema = $tableSchema->getColumn($columnName); + $columnDiff = new ColumnDiff($columnSchema->getName(), $columnSchema); + $tableDiff->changedColumns[] = $columnDiff; + $schemaDiff->changedTables[] = $tableDiff; + } + } catch (SchemaException $e) { + // ignore + } + } + + $this->connection->beginTransaction(); + foreach ($schemaDiff->toSql($this->connection->getDatabasePlatform()) as $sql) { + $this->connection->query($sql); + } + $this->connection->commit(); + } +} + diff --git a/lib/private/repair/updatecertificatestore.php b/lib/private/repair/updatecertificatestore.php new file mode 100644 index 00000000000..5fad309a959 --- /dev/null +++ b/lib/private/repair/updatecertificatestore.php @@ -0,0 +1,88 @@ +<?php +/** + * @author Lukas Reschke <lukas@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @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 OC\Files\View; +use OC\Hooks\BasicEmitter; +use OC\RepairStep; +use OC\Server; +use OCP\IConfig; + +/** + * Class UpdateCertificateStore rewrites the user specific certificate store after + * an update has been performed. This is done because a new root certificate file + * might have been added. + * + * @package OC\Repair + */ +class UpdateCertificateStore extends BasicEmitter implements RepairStep { + /** + * FIXME: The certificate manager does only allow specifying the user + * within the constructor. This makes DI impossible. + * @var Server + */ + protected $server; + /** @var IConfig */ + protected $config; + + /** + * @param Server $server + * @param IConfig $config + */ + public function __construct(Server $server, + IConfig $config) { + $this->server = $server; + $this->config = $config; + } + + /** {@inheritDoc} */ + public function getName() { + return 'Update user certificate stores with new root certificates'; + } + + /** {@inheritDoc} */ + public function run() { + $rootView = new View(); + $dataDirectory = $this->config->getSystemValue('datadirectory', null); + if(is_null($dataDirectory)) { + throw new \Exception('No data directory specified'); + } + + $pathToRootCerts = '/files_external/rootcerts.crt'; + + foreach($rootView->getDirectoryContent('', 'httpd/unix-directory') as $fileInfo) { + $uid = trim($fileInfo->getPath(), '/'); + if($rootView->file_exists($uid . $pathToRootCerts)) { + // Delete the existing root certificate + $rootView->unlink($uid . $pathToRootCerts); + + /** + * FIXME: The certificate manager does only allow specifying the user + * within the constructor. This makes DI impossible. + */ + // Regenerate the certificates + $certificateManager = $this->server->getCertificateManager($uid); + $certificateManager->createCertificateBundle(); + } + } + } +} diff --git a/lib/private/repair/updateoutdatedocsids.php b/lib/private/repair/updateoutdatedocsids.php new file mode 100644 index 00000000000..5f6ee029536 --- /dev/null +++ b/lib/private/repair/updateoutdatedocsids.php @@ -0,0 +1,108 @@ +<?php +/** + * @author Lukas Reschke <lukas@owncloud.com> + * + * @copyright Copyright (c) 2015, ownCloud, Inc. + * @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 OC\Hooks\BasicEmitter; +use OC\RepairStep; +use OCP\IConfig; + +/** + * Class UpdateOutdatedOcsIds is used to update invalid outdated OCS IDs, this is + * for example the case when an application has had another OCS ID in the past such + * as for contacts and calendar when apps.owncloud.com migrated to a unified identifier + * for multiple versions. + * + * @package OC\Repair + */ +class UpdateOutdatedOcsIds extends BasicEmitter implements RepairStep { + /** @var IConfig */ + private $config; + + /** + * @param IConfig $config + */ + public function __construct(IConfig $config) { + $this->config = $config; + } + + /** + * {@inheritdoc} + */ + public function getName() { + return 'Repair outdated OCS IDs'; + } + + /** + * @param string $appName + * @param string $oldId + * @param string $newId + * @return bool True if updated, false otherwise + */ + public function fixOcsId($appName, $oldId, $newId) { + $existingId = $this->config->getAppValue($appName, 'ocsid'); + + if($existingId === $oldId) { + $this->config->setAppValue($appName, 'ocsid', $newId); + return true; + } + + return false; + } + + /** + * {@inheritdoc} + */ + public function run() { + $appsToUpdate = [ + 'contacts' => [ + 'old' => '166044', + 'new' => '168708', + ], + 'calendar' => [ + 'old' => '166043', + 'new' => '168707', + ], + 'bookmarks' => [ + 'old' => '166042', + 'new' => '168710', + ], + 'search_lucene' => [ + 'old' => '166057', + 'new' => '168709', + ], + 'documents' => [ + 'old' => '166045', + 'new' => '168711', + ] + ]; + + foreach($appsToUpdate as $appName => $ids) { + if ($this->fixOcsId($appName, $ids['old'], $ids['new'])) { + $this->emit( + '\OC\Repair', + 'info', + [sprintf('Fixed invalid %s OCS id', $appName)] + ); + } + } + } +} |