Skip to content

Commit 0b8f626

Browse files
fix: replace null character when serializing
Signed-off-by: SebastianKrupinski <krupinskis05@gmail.com>
1 parent e2a3f56 commit 0b8f626

File tree

6 files changed

+96
-2
lines changed

6 files changed

+96
-2
lines changed

apps/dav/lib/DAV/CustomPropertiesBackend.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -447,7 +447,9 @@ private function encodeValueForDatabase($value): array {
447447
$value = $value->getXml();
448448
} else {
449449
$valueType = self::PROPERTY_TYPE_OBJECT;
450-
$value = serialize($value);
450+
// serialize produces null character
451+
// these can not be properly stored in some databases and need to be replaced
452+
$value = str_replace(chr(0), '\x00', serialize($value));
451453
}
452454
return [$value, $valueType];
453455
}
@@ -460,7 +462,9 @@ private function decodeValueFromDatabase(string $value, int $valueType) {
460462
case self::PROPERTY_TYPE_XML:
461463
return new Complex($value);
462464
case self::PROPERTY_TYPE_OBJECT:
463-
return unserialize($value);
465+
// some databases can not handel null characters, these are custom encoded during serialization
466+
// this custom encoding needs to be first reversed before unserializing
467+
return unserialize(str_replace('\x00', chr(0), $value));
464468
case self::PROPERTY_TYPE_STRING:
465469
default:
466470
return $value;

apps/dav/tests/unit/DAV/CustomPropertiesBackendTest.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
<?php
2+
23
/**
34
* @copyright Copyright (c) 2017, Georg Ehrke <oc.list@georgehrke.com>
45
*
@@ -242,4 +243,21 @@ public function moveProvider() {
242243
[str_repeat('long_path1', 100), str_repeat('long_path2', 100)]
243244
];
244245
}
246+
247+
public function testDecodeValueFromDatabaseObjectCurrent(): void {
248+
$propertyValue = 'O:48:"Sabre\CalDAV\Xml\Property\ScheduleCalendarTransp":1:{s:8:"\x00*\x00value";s:6:"opaque";}';
249+
$propertyType = 3;
250+
$decodeValue = $this->invokePrivate($this->backend, 'decodeValueFromDatabase', [$propertyValue, $propertyType]);
251+
$this->assertInstanceOf(\Sabre\CalDAV\Xml\Property\ScheduleCalendarTransp::class, $decodeValue);
252+
$this->assertEquals('opaque', $decodeValue->getValue());
253+
}
254+
255+
public function testDecodeValueFromDatabaseObjectLegacy(): void {
256+
$propertyValue = 'O:48:"Sabre\CalDAV\Xml\Property\ScheduleCalendarTransp":1:{s:8:"' . chr(0) . '*' . chr(0) . 'value";s:6:"opaque";}';
257+
$propertyType = 3;
258+
$decodeValue = $this->invokePrivate($this->backend, 'decodeValueFromDatabase', [$propertyValue, $propertyType]);
259+
$this->assertInstanceOf(\Sabre\CalDAV\Xml\Property\ScheduleCalendarTransp::class, $decodeValue);
260+
$this->assertEquals('opaque', $decodeValue->getValue());
261+
}
262+
245263
}

lib/composer/composer/autoload_classmap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1655,6 +1655,7 @@
16551655
'OC\\Repair\\Owncloud\\MoveAvatarsBackgroundJob' => $baseDir . '/lib/private/Repair/Owncloud/MoveAvatarsBackgroundJob.php',
16561656
'OC\\Repair\\Owncloud\\SaveAccountsTableData' => $baseDir . '/lib/private/Repair/Owncloud/SaveAccountsTableData.php',
16571657
'OC\\Repair\\Owncloud\\UpdateLanguageCodes' => $baseDir . '/lib/private/Repair/Owncloud/UpdateLanguageCodes.php',
1658+
'OC\\Repair\\RemoveBrokenProperties' => $baseDir . '/lib/private/Repair/RemoveBrokenProperties.php',
16581659
'OC\\Repair\\RemoveLinkShares' => $baseDir . '/lib/private/Repair/RemoveLinkShares.php',
16591660
'OC\\Repair\\RepairDavShares' => $baseDir . '/lib/private/Repair/RepairDavShares.php',
16601661
'OC\\Repair\\RepairInvalidShares' => $baseDir . '/lib/private/Repair/RepairInvalidShares.php',

lib/composer/composer/autoload_static.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1688,6 +1688,7 @@ class ComposerStaticInit749170dad3f5e7f9ca158f5a9f04f6a2
16881688
'OC\\Repair\\Owncloud\\MoveAvatarsBackgroundJob' => __DIR__ . '/../../..' . '/lib/private/Repair/Owncloud/MoveAvatarsBackgroundJob.php',
16891689
'OC\\Repair\\Owncloud\\SaveAccountsTableData' => __DIR__ . '/../../..' . '/lib/private/Repair/Owncloud/SaveAccountsTableData.php',
16901690
'OC\\Repair\\Owncloud\\UpdateLanguageCodes' => __DIR__ . '/../../..' . '/lib/private/Repair/Owncloud/UpdateLanguageCodes.php',
1691+
'OC\\Repair\\RemoveBrokenProperties' => __DIR__ . '/../../..' . '/lib/private/Repair/RemoveBrokenProperties.php',
16911692
'OC\\Repair\\RemoveLinkShares' => __DIR__ . '/../../..' . '/lib/private/Repair/RemoveLinkShares.php',
16921693
'OC\\Repair\\RepairDavShares' => __DIR__ . '/../../..' . '/lib/private/Repair/RepairDavShares.php',
16931694
'OC\\Repair\\RepairInvalidShares' => __DIR__ . '/../../..' . '/lib/private/Repair/RepairInvalidShares.php',

lib/private/Repair.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@
7676
use OC\Repair\Owncloud\MoveAvatars;
7777
use OC\Repair\Owncloud\SaveAccountsTableData;
7878
use OC\Repair\Owncloud\UpdateLanguageCodes;
79+
use OC\Repair\RemoveBrokenProperties;
7980
use OC\Repair\RemoveLinkShares;
8081
use OC\Repair\RepairDavShares;
8182
use OC\Repair\RepairInvalidShares;
@@ -229,6 +230,7 @@ public static function getRepairSteps(): array {
229230
public static function getExpensiveRepairSteps() {
230231
return [
231232
new OldGroupMembershipShares(\OC::$server->getDatabaseConnection(), \OC::$server->getGroupManager()),
233+
new RemoveBrokenProperties(\OC::$server->getDatabaseConnection()),
232234
new RepairMimeTypes(\OC::$server->getConfig(), \OC::$server->getDatabaseConnection()),
233235
\OC::$server->get(ValidatePhoneNumber::class),
234236
\OC::$server->get(DeleteSchedulingObjects::class),
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
namespace OC\Repair;
10+
11+
use OCP\DB\QueryBuilder\IQueryBuilder;
12+
use OCP\IDBConnection;
13+
use OCP\Migration\IOutput;
14+
use OCP\Migration\IRepairStep;
15+
16+
class RemoveBrokenProperties implements IRepairStep {
17+
/**
18+
* RemoveBrokenProperties constructor.
19+
*
20+
* @param IDBConnection $db
21+
*/
22+
public function __construct(
23+
private IDBConnection $db,
24+
) {
25+
}
26+
27+
/**
28+
* @inheritdoc
29+
*/
30+
public function getName() {
31+
return 'Remove broken DAV object properties';
32+
}
33+
34+
/**
35+
* @inheritdoc
36+
*/
37+
public function run(IOutput $output) {
38+
// retrieve all object properties
39+
$qb = $this->db->getQueryBuilder();
40+
$qb->select('id', 'propertyvalue')
41+
->from('properties')
42+
->where($qb->expr()->eq('valuetype', $qb->createNamedParameter('3', IQueryBuilder::PARAM_INT), IQueryBuilder::PARAM_INT));
43+
$result = $qb->executeQuery();
44+
// find broken object properties
45+
$brokenIds = [];
46+
while ($entry = $result->fetch()) {
47+
if (!empty($entry['propertyvalue'])) {
48+
$object = @unserialize(str_replace('\x00', chr(0), $entry['propertyvalue']));
49+
if ($object === false) {
50+
$brokenIds[] = $entry['id'];
51+
}
52+
} else {
53+
$brokenIds[] = $entry['id'];
54+
}
55+
}
56+
$result->closeCursor();
57+
// delete broken object properties
58+
$qb = $this->db->getQueryBuilder();
59+
$qb->delete('properties')
60+
->where($qb->expr()->in('id', $qb->createParameter('ids'), IQueryBuilder::PARAM_STR_ARRAY));
61+
foreach (array_chunk($brokenIds, 1000) as $chunkIds) {
62+
$qb->setParameter('ids', $chunkIds, IQueryBuilder::PARAM_STR_ARRAY);
63+
$qb->executeStatement();
64+
}
65+
$total = count($brokenIds);
66+
$output->info("$total broken object properties removed");
67+
}
68+
}

0 commit comments

Comments
 (0)