Skip to content

Commit 2ee948e

Browse files
authored
Merge pull request #30368 from nextcloud/dav-allow-object-properties
Allow DAV Object properties
2 parents 804ee11 + bd8b213 commit 2ee948e

File tree

6 files changed

+187
-13
lines changed

6 files changed

+187
-13
lines changed

apps/dav/appinfo/info.xml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<name>WebDAV</name>
66
<summary>WebDAV endpoint</summary>
77
<description>WebDAV endpoint</description>
8-
<version>1.23.0</version>
8+
<version>1.24.0</version>
99
<licence>agpl</licence>
1010
<author>owncloud.org</author>
1111
<namespace>DAV</namespace>
@@ -39,6 +39,7 @@
3939
<step>OCA\DAV\Migration\RemoveOrphanEventsAndContacts</step>
4040
<step>OCA\DAV\Migration\RemoveClassifiedEventActivity</step>
4141
<step>OCA\DAV\Migration\RemoveDeletedUsersCalendarSubscriptions</step>
42+
<step>OCA\DAV\Migration\RemoveObjectProperties</step>
4243
</post-migration>
4344
<live-migration>
4445
<step>OCA\DAV\Migration\ChunkCleanup</step>

apps/dav/composer/composer/autoload_classmap.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,6 +257,7 @@
257257
'OCA\\DAV\\Migration\\RegisterBuildReminderIndexBackgroundJob' => $baseDir . '/../lib/Migration/RegisterBuildReminderIndexBackgroundJob.php',
258258
'OCA\\DAV\\Migration\\RemoveClassifiedEventActivity' => $baseDir . '/../lib/Migration/RemoveClassifiedEventActivity.php',
259259
'OCA\\DAV\\Migration\\RemoveDeletedUsersCalendarSubscriptions' => $baseDir . '/../lib/Migration/RemoveDeletedUsersCalendarSubscriptions.php',
260+
'OCA\\DAV\\Migration\\RemoveObjectProperties' => $baseDir . '/../lib/Migration/RemoveObjectProperties.php',
260261
'OCA\\DAV\\Migration\\RemoveOrphanEventsAndContacts' => $baseDir . '/../lib/Migration/RemoveOrphanEventsAndContacts.php',
261262
'OCA\\DAV\\Migration\\Version1004Date20170825134824' => $baseDir . '/../lib/Migration/Version1004Date20170825134824.php',
262263
'OCA\\DAV\\Migration\\Version1004Date20170919104507' => $baseDir . '/../lib/Migration/Version1004Date20170919104507.php',
@@ -278,6 +279,7 @@
278279
'OCA\\DAV\\Migration\\Version1016Date20201109085907' => $baseDir . '/../lib/Migration/Version1016Date20201109085907.php',
279280
'OCA\\DAV\\Migration\\Version1017Date20210216083742' => $baseDir . '/../lib/Migration/Version1017Date20210216083742.php',
280281
'OCA\\DAV\\Migration\\Version1018Date20210312100735' => $baseDir . '/../lib/Migration/Version1018Date20210312100735.php',
282+
'OCA\\DAV\\Migration\\Version1024Date20211221144219' => $baseDir . '/../lib/Migration/Version1024Date20211221144219.php',
281283
'OCA\\DAV\\Profiler\\ProfilerPlugin' => $baseDir . '/../lib/Profiler/ProfilerPlugin.php',
282284
'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningNode' => $baseDir . '/../lib/Provisioning/Apple/AppleProvisioningNode.php',
283285
'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningPlugin' => $baseDir . '/../lib/Provisioning/Apple/AppleProvisioningPlugin.php',

apps/dav/composer/composer/autoload_static.php

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -272,6 +272,7 @@ class ComposerStaticInitDAV
272272
'OCA\\DAV\\Migration\\RegisterBuildReminderIndexBackgroundJob' => __DIR__ . '/..' . '/../lib/Migration/RegisterBuildReminderIndexBackgroundJob.php',
273273
'OCA\\DAV\\Migration\\RemoveClassifiedEventActivity' => __DIR__ . '/..' . '/../lib/Migration/RemoveClassifiedEventActivity.php',
274274
'OCA\\DAV\\Migration\\RemoveDeletedUsersCalendarSubscriptions' => __DIR__ . '/..' . '/../lib/Migration/RemoveDeletedUsersCalendarSubscriptions.php',
275+
'OCA\\DAV\\Migration\\RemoveObjectProperties' => __DIR__ . '/..' . '/../lib/Migration/RemoveObjectProperties.php',
275276
'OCA\\DAV\\Migration\\RemoveOrphanEventsAndContacts' => __DIR__ . '/..' . '/../lib/Migration/RemoveOrphanEventsAndContacts.php',
276277
'OCA\\DAV\\Migration\\Version1004Date20170825134824' => __DIR__ . '/..' . '/../lib/Migration/Version1004Date20170825134824.php',
277278
'OCA\\DAV\\Migration\\Version1004Date20170919104507' => __DIR__ . '/..' . '/../lib/Migration/Version1004Date20170919104507.php',
@@ -293,6 +294,7 @@ class ComposerStaticInitDAV
293294
'OCA\\DAV\\Migration\\Version1016Date20201109085907' => __DIR__ . '/..' . '/../lib/Migration/Version1016Date20201109085907.php',
294295
'OCA\\DAV\\Migration\\Version1017Date20210216083742' => __DIR__ . '/..' . '/../lib/Migration/Version1017Date20210216083742.php',
295296
'OCA\\DAV\\Migration\\Version1018Date20210312100735' => __DIR__ . '/..' . '/../lib/Migration/Version1018Date20210312100735.php',
297+
'OCA\\DAV\\Migration\\Version1024Date20211221144219' => __DIR__ . '/..' . '/../lib/Migration/Version1024Date20211221144219.php',
296298
'OCA\\DAV\\Profiler\\ProfilerPlugin' => __DIR__ . '/..' . '/../lib/Profiler/ProfilerPlugin.php',
297299
'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningNode' => __DIR__ . '/..' . '/../lib/Provisioning/Apple/AppleProvisioningNode.php',
298300
'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningPlugin' => __DIR__ . '/..' . '/../lib/Provisioning/Apple/AppleProvisioningPlugin.php',

apps/dav/lib/DAV/CustomPropertiesBackend.php

Lines changed: 58 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,29 @@
3131
use Sabre\DAV\PropFind;
3232
use Sabre\DAV\PropPatch;
3333
use Sabre\DAV\Tree;
34+
use Sabre\DAV\Xml\Property\Complex;
3435
use function array_intersect;
3536

3637
class CustomPropertiesBackend implements BackendInterface {
3738

3839
/** @var string */
3940
private const TABLE_NAME = 'properties';
4041

42+
/**
43+
* Value is stored as string.
44+
*/
45+
public const PROPERTY_TYPE_STRING = 1;
46+
47+
/**
48+
* Value is stored as XML fragment.
49+
*/
50+
public const PROPERTY_TYPE_XML = 2;
51+
52+
/**
53+
* Value is stored as a property object.
54+
*/
55+
public const PROPERTY_TYPE_OBJECT = 3;
56+
4157
/**
4258
* Ignored properties
4359
*
@@ -239,7 +255,7 @@ private function getPublishedProperties(string $path, array $requestedProperties
239255
$result = $qb->executeQuery();
240256
$props = [];
241257
while ($row = $result->fetch()) {
242-
$props[$row['propertyname']] = $row['propertyvalue'];
258+
$props[$row['propertyname']] = $this->decodeValueFromDatabase($row['propertyvalue'], $row['valuetype']);
243259
}
244260
$result->closeCursor();
245261
return $props;
@@ -282,7 +298,7 @@ private function getUserProperties(string $path, array $requestedProperties) {
282298

283299
$props = [];
284300
while ($row = $result->fetch()) {
285-
$props[$row['propertyname']] = $row['propertyvalue'];
301+
$props[$row['propertyname']] = $this->decodeValueFromDatabase($row['propertyvalue'], $row['valuetype']);
286302
}
287303

288304
$result->closeCursor();
@@ -304,9 +320,9 @@ private function updateProperties(string $path, array $properties) {
304320
' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?';
305321

306322
$insertStatement = 'INSERT INTO `*PREFIX*properties`' .
307-
' (`userid`,`propertypath`,`propertyname`,`propertyvalue`) VALUES(?,?,?,?)';
323+
' (`userid`,`propertypath`,`propertyname`,`propertyvalue`, `valuetype`) VALUES(?,?,?,?,?)';
308324

309-
$updateStatement = 'UPDATE `*PREFIX*properties` SET `propertyvalue` = ?' .
325+
$updateStatement = 'UPDATE `*PREFIX*properties` SET `propertyvalue` = ?, `valuetype` = ?' .
310326
' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?';
311327

312328
// TODO: use "insert or update" strategy ?
@@ -325,24 +341,22 @@ private function updateProperties(string $path, array $properties) {
325341
);
326342
}
327343
} else {
328-
if ($propertyValue instanceOf \Sabre\DAV\Xml\Property\Complex) {
329-
$propertyValue = $propertyValue->getXml();
330-
} elseif (!is_string($propertyValue)) {
331-
$propertyValue = (string)$propertyValue;
332-
}
344+
[$value, $valueType] = $this->encodeValueForDatabase($propertyValue);
333345
if (!array_key_exists($propertyName, $existing)) {
334346
$this->connection->executeUpdate($insertStatement,
335347
[
336348
$this->user->getUID(),
337349
$this->formatPath($path),
338350
$propertyName,
339-
$propertyValue,
351+
$value,
352+
$valueType
340353
]
341354
);
342355
} else {
343356
$this->connection->executeUpdate($updateStatement,
344357
[
345-
$propertyValue,
358+
$value,
359+
$valueType,
346360
$this->user->getUID(),
347361
$this->formatPath($path),
348362
$propertyName,
@@ -367,8 +381,40 @@ private function updateProperties(string $path, array $properties) {
367381
private function formatPath(string $path): string {
368382
if (strlen($path) > 250) {
369383
return sha1($path);
384+
}
385+
386+
return $path;
387+
}
388+
389+
/**
390+
* @param mixed $value
391+
* @return array
392+
*/
393+
private function encodeValueForDatabase($value): array {
394+
if (is_scalar($value)) {
395+
$valueType = self::PROPERTY_TYPE_STRING;
396+
} elseif ($value instanceof Complex) {
397+
$valueType = self::PROPERTY_TYPE_XML;
398+
$value = $value->getXml();
370399
} else {
371-
return $path;
400+
$valueType = self::PROPERTY_TYPE_OBJECT;
401+
$value = serialize($value);
402+
}
403+
return [$value, $valueType];
404+
}
405+
406+
/**
407+
* @return mixed|Complex|string
408+
*/
409+
private function decodeValueFromDatabase(string $value, int $valueType) {
410+
switch ($valueType) {
411+
case self::PROPERTY_TYPE_XML:
412+
return new Complex($value);
413+
case self::PROPERTY_TYPE_OBJECT:
414+
return unserialize($value);
415+
case self::PROPERTY_TYPE_STRING:
416+
default:
417+
return $value;
372418
}
373419
}
374420
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
<?php
2+
/**
3+
* @copyright Copyright (c) 2021, Thomas Citharel <[email protected]>.
4+
*
5+
* @author Thomas Citharel <[email protected]>
6+
*
7+
* @license AGPL-3.0
8+
*
9+
* This code is free software: you can redistribute it and/or modify
10+
* it under the terms of the GNU Affero General Public License, version 3,
11+
* as published by the Free Software Foundation.
12+
*
13+
* This program is distributed in the hope that it will be useful,
14+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
15+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16+
* GNU Affero General Public License for more details.
17+
*
18+
* You should have received a copy of the GNU Affero General Public License, version 3,
19+
* along with this program. If not, see <http://www.gnu.org/licenses/>
20+
*
21+
*/
22+
namespace OCA\DAV\Migration;
23+
24+
use OCP\DB\QueryBuilder\IQueryBuilder;
25+
use OCP\IDBConnection;
26+
use OCP\Migration\IOutput;
27+
use OCP\Migration\IRepairStep;
28+
29+
class RemoveObjectProperties implements IRepairStep {
30+
private const RESOURCE_TYPE_PROPERTY = '{DAV:}resourcetype';
31+
private const ME_CARD_PROPERTY = '{http://calendarserver.org/ns/}me-card';
32+
private const CALENDAR_TRANSP_PROPERTY = '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp';
33+
34+
/** @var IDBConnection */
35+
private $connection;
36+
37+
/**
38+
* RemoveObjectProperties constructor.
39+
*
40+
* @param IDBConnection $connection
41+
*/
42+
public function __construct(IDBConnection $connection) {
43+
$this->connection = $connection;
44+
}
45+
46+
/**
47+
* @inheritdoc
48+
*/
49+
public function getName() {
50+
return 'Remove invalid object properties';
51+
}
52+
53+
/**
54+
* @inheritdoc
55+
*/
56+
public function run(IOutput $output) {
57+
$query = $this->connection->getQueryBuilder();
58+
$updated = $query->delete('properties')
59+
->where($query->expr()->in('propertyname', $query->createNamedParameter([self::RESOURCE_TYPE_PROPERTY, self::ME_CARD_PROPERTY, self::CALENDAR_TRANSP_PROPERTY], IQueryBuilder::PARAM_STR_ARRAY)))
60+
->andWhere($query->expr()->eq('propertyvalue', $query->createNamedParameter('Object')))
61+
->executeStatement();
62+
63+
$output->info("$updated invalid object properties removed.");
64+
}
65+
}
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace OCA\DAV\Migration;
6+
7+
use Closure;
8+
use Doctrine\DBAL\Schema\SchemaException;
9+
use OCA\DAV\DAV\CustomPropertiesBackend;
10+
use OCP\DB\ISchemaWrapper;
11+
use OCP\DB\Types;
12+
use OCP\Migration\IOutput;
13+
use OCP\Migration\SimpleMigrationStep;
14+
15+
/**
16+
* Auto-generated migration step: Please modify to your needs!
17+
*/
18+
class Version1024Date20211221144219 extends SimpleMigrationStep {
19+
20+
/**
21+
* @param IOutput $output
22+
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
23+
* @param array $options
24+
*/
25+
public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
26+
}
27+
28+
/**
29+
* @param IOutput $output
30+
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
31+
* @param array $options
32+
* @return null|ISchemaWrapper
33+
* @throws SchemaException
34+
*/
35+
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
36+
/** @var ISchemaWrapper $schema */
37+
$schema = $schemaClosure();
38+
$propertiesTable = $schema->getTable('properties');
39+
40+
if ($propertiesTable->hasColumn('valuetype')) {
41+
return null;
42+
}
43+
$propertiesTable->addColumn('valuetype', Types::SMALLINT, [
44+
'notnull' => false,
45+
'default' => CustomPropertiesBackend::PROPERTY_TYPE_STRING
46+
]);
47+
48+
return $schema;
49+
}
50+
51+
/**
52+
* @param IOutput $output
53+
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
54+
* @param array $options
55+
*/
56+
public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
57+
}
58+
}

0 commit comments

Comments
 (0)