Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion apps/dav/appinfo/info.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<name>WebDAV</name>
<summary>WebDAV endpoint</summary>
<description>WebDAV endpoint</description>
<version>1.23.0</version>
<version>1.24.0</version>
<licence>agpl</licence>
<author>owncloud.org</author>
<namespace>DAV</namespace>
Expand Down Expand Up @@ -39,6 +39,7 @@
<step>OCA\DAV\Migration\RemoveOrphanEventsAndContacts</step>
<step>OCA\DAV\Migration\RemoveClassifiedEventActivity</step>
<step>OCA\DAV\Migration\RemoveDeletedUsersCalendarSubscriptions</step>
<step>OCA\DAV\Migration\RemoveObjectProperties</step>
</post-migration>
<live-migration>
<step>OCA\DAV\Migration\ChunkCleanup</step>
Expand Down
2 changes: 2 additions & 0 deletions apps/dav/composer/composer/autoload_classmap.php
Original file line number Diff line number Diff line change
Expand Up @@ -252,6 +252,7 @@
'OCA\\DAV\\Migration\\RegisterBuildReminderIndexBackgroundJob' => $baseDir . '/../lib/Migration/RegisterBuildReminderIndexBackgroundJob.php',
'OCA\\DAV\\Migration\\RemoveClassifiedEventActivity' => $baseDir . '/../lib/Migration/RemoveClassifiedEventActivity.php',
'OCA\\DAV\\Migration\\RemoveDeletedUsersCalendarSubscriptions' => $baseDir . '/../lib/Migration/RemoveDeletedUsersCalendarSubscriptions.php',
'OCA\\DAV\\Migration\\RemoveObjectProperties' => $baseDir . '/../lib/Migration/RemoveObjectProperties.php',
'OCA\\DAV\\Migration\\RemoveOrphanEventsAndContacts' => $baseDir . '/../lib/Migration/RemoveOrphanEventsAndContacts.php',
'OCA\\DAV\\Migration\\Version1004Date20170825134824' => $baseDir . '/../lib/Migration/Version1004Date20170825134824.php',
'OCA\\DAV\\Migration\\Version1004Date20170919104507' => $baseDir . '/../lib/Migration/Version1004Date20170919104507.php',
Expand All @@ -273,6 +274,7 @@
'OCA\\DAV\\Migration\\Version1016Date20201109085907' => $baseDir . '/../lib/Migration/Version1016Date20201109085907.php',
'OCA\\DAV\\Migration\\Version1017Date20210216083742' => $baseDir . '/../lib/Migration/Version1017Date20210216083742.php',
'OCA\\DAV\\Migration\\Version1018Date20210312100735' => $baseDir . '/../lib/Migration/Version1018Date20210312100735.php',
'OCA\\DAV\\Migration\\Version1024Date20211221144219' => $baseDir . '/../lib/Migration/Version1024Date20211221144219.php',
'OCA\\DAV\\Profiler\\ProfilerPlugin' => $baseDir . '/../lib/Profiler/ProfilerPlugin.php',
'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningNode' => $baseDir . '/../lib/Provisioning/Apple/AppleProvisioningNode.php',
'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningPlugin' => $baseDir . '/../lib/Provisioning/Apple/AppleProvisioningPlugin.php',
Expand Down
2 changes: 2 additions & 0 deletions apps/dav/composer/composer/autoload_static.php
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,7 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Migration\\RegisterBuildReminderIndexBackgroundJob' => __DIR__ . '/..' . '/../lib/Migration/RegisterBuildReminderIndexBackgroundJob.php',
'OCA\\DAV\\Migration\\RemoveClassifiedEventActivity' => __DIR__ . '/..' . '/../lib/Migration/RemoveClassifiedEventActivity.php',
'OCA\\DAV\\Migration\\RemoveDeletedUsersCalendarSubscriptions' => __DIR__ . '/..' . '/../lib/Migration/RemoveDeletedUsersCalendarSubscriptions.php',
'OCA\\DAV\\Migration\\RemoveObjectProperties' => __DIR__ . '/..' . '/../lib/Migration/RemoveObjectProperties.php',
'OCA\\DAV\\Migration\\RemoveOrphanEventsAndContacts' => __DIR__ . '/..' . '/../lib/Migration/RemoveOrphanEventsAndContacts.php',
'OCA\\DAV\\Migration\\Version1004Date20170825134824' => __DIR__ . '/..' . '/../lib/Migration/Version1004Date20170825134824.php',
'OCA\\DAV\\Migration\\Version1004Date20170919104507' => __DIR__ . '/..' . '/../lib/Migration/Version1004Date20170919104507.php',
Expand All @@ -288,6 +289,7 @@ class ComposerStaticInitDAV
'OCA\\DAV\\Migration\\Version1016Date20201109085907' => __DIR__ . '/..' . '/../lib/Migration/Version1016Date20201109085907.php',
'OCA\\DAV\\Migration\\Version1017Date20210216083742' => __DIR__ . '/..' . '/../lib/Migration/Version1017Date20210216083742.php',
'OCA\\DAV\\Migration\\Version1018Date20210312100735' => __DIR__ . '/..' . '/../lib/Migration/Version1018Date20210312100735.php',
'OCA\\DAV\\Migration\\Version1024Date20211221144219' => __DIR__ . '/..' . '/../lib/Migration/Version1024Date20211221144219.php',
'OCA\\DAV\\Profiler\\ProfilerPlugin' => __DIR__ . '/..' . '/../lib/Profiler/ProfilerPlugin.php',
'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningNode' => __DIR__ . '/..' . '/../lib/Provisioning/Apple/AppleProvisioningNode.php',
'OCA\\DAV\\Provisioning\\Apple\\AppleProvisioningPlugin' => __DIR__ . '/..' . '/../lib/Provisioning/Apple/AppleProvisioningPlugin.php',
Expand Down
70 changes: 58 additions & 12 deletions apps/dav/lib/DAV/CustomPropertiesBackend.php
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,29 @@
use Sabre\DAV\PropFind;
use Sabre\DAV\PropPatch;
use Sabre\DAV\Tree;
use Sabre\DAV\Xml\Property\Complex;
use function array_intersect;

class CustomPropertiesBackend implements BackendInterface {

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

/**
* Value is stored as string.
*/
public const PROPERTY_TYPE_STRING = 1;

/**
* Value is stored as XML fragment.
*/
public const PROPERTY_TYPE_XML = 2;

/**
* Value is stored as a property object.
*/
public const PROPERTY_TYPE_OBJECT = 3;

/**
* Ignored properties
*
Expand Down Expand Up @@ -239,7 +255,7 @@ private function getPublishedProperties(string $path, array $requestedProperties
$result = $qb->executeQuery();
$props = [];
while ($row = $result->fetch()) {
$props[$row['propertyname']] = $row['propertyvalue'];
$props[$row['propertyname']] = $this->decodeValueFromDatabase($row['propertyvalue'], $row['valuetype']);
}
$result->closeCursor();
return $props;
Expand Down Expand Up @@ -282,7 +298,7 @@ private function getUserProperties(string $path, array $requestedProperties) {

$props = [];
while ($row = $result->fetch()) {
$props[$row['propertyname']] = $row['propertyvalue'];
$props[$row['propertyname']] = $this->decodeValueFromDatabase($row['propertyvalue'], $row['valuetype']);
}

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

$insertStatement = 'INSERT INTO `*PREFIX*properties`' .
' (`userid`,`propertypath`,`propertyname`,`propertyvalue`) VALUES(?,?,?,?)';
' (`userid`,`propertypath`,`propertyname`,`propertyvalue`, `valuetype`) VALUES(?,?,?,?,?)';

$updateStatement = 'UPDATE `*PREFIX*properties` SET `propertyvalue` = ?' .
$updateStatement = 'UPDATE `*PREFIX*properties` SET `propertyvalue` = ?, `valuetype` = ?' .
' WHERE `userid` = ? AND `propertypath` = ? AND `propertyname` = ?';

// TODO: use "insert or update" strategy ?
Expand All @@ -325,24 +341,22 @@ private function updateProperties(string $path, array $properties) {
);
}
} else {
if ($propertyValue instanceOf \Sabre\DAV\Xml\Property\Complex) {
$propertyValue = $propertyValue->getXml();
} elseif (!is_string($propertyValue)) {
$propertyValue = (string)$propertyValue;
}
[$value, $valueType] = $this->encodeValueForDatabase($propertyValue);
if (!array_key_exists($propertyName, $existing)) {
$this->connection->executeUpdate($insertStatement,
[
$this->user->getUID(),
$this->formatPath($path),
$propertyName,
$propertyValue,
$value,
$valueType
]
);
} else {
$this->connection->executeUpdate($updateStatement,
[
$propertyValue,
$value,
$valueType,
$this->user->getUID(),
$this->formatPath($path),
$propertyName,
Expand All @@ -367,8 +381,40 @@ private function updateProperties(string $path, array $properties) {
private function formatPath(string $path): string {
if (strlen($path) > 250) {
return sha1($path);
}

return $path;
}

/**
* @param mixed $value
* @return array
*/
private function encodeValueForDatabase($value): array {
if (is_scalar($value)) {
$valueType = self::PROPERTY_TYPE_STRING;
} elseif ($value instanceof Complex) {
$valueType = self::PROPERTY_TYPE_XML;
$value = $value->getXml();
} else {
return $path;
$valueType = self::PROPERTY_TYPE_OBJECT;
$value = serialize($value);
}
return [$value, $valueType];
}

/**
* @return mixed|Complex|string
*/
private function decodeValueFromDatabase(string $value, int $valueType) {
switch ($valueType) {
case self::PROPERTY_TYPE_XML:
return new Complex($value);
case self::PROPERTY_TYPE_OBJECT:
return unserialize($value);
case self::PROPERTY_TYPE_STRING:
default:
return $value;
}
}
}
65 changes: 65 additions & 0 deletions apps/dav/lib/Migration/RemoveObjectProperties.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
<?php
/**
* @copyright Copyright (c) 2021, Thomas Citharel <[email protected]>.
*
* @author Thomas Citharel <[email protected]>
*
* @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 OCA\DAV\Migration;

use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\IDBConnection;
use OCP\Migration\IOutput;
use OCP\Migration\IRepairStep;

class RemoveObjectProperties implements IRepairStep {
private const RESOURCE_TYPE_PROPERTY = '{DAV:}resourcetype';
private const ME_CARD_PROPERTY = '{http://calendarserver.org/ns/}me-card';
private const CALENDAR_TRANSP_PROPERTY = '{urn:ietf:params:xml:ns:caldav}schedule-calendar-transp';

/** @var IDBConnection */
private $connection;

/**
* RemoveObjectProperties constructor.
*
* @param IDBConnection $connection
*/
public function __construct(IDBConnection $connection) {
$this->connection = $connection;
}

/**
* @inheritdoc
*/
public function getName() {
return 'Remove invalid object properties';
}

/**
* @inheritdoc
*/
public function run(IOutput $output) {
$query = $this->connection->getQueryBuilder();
$updated = $query->delete('properties')
->where($query->expr()->in('propertyname', $query->createNamedParameter([self::RESOURCE_TYPE_PROPERTY, self::ME_CARD_PROPERTY, self::CALENDAR_TRANSP_PROPERTY], IQueryBuilder::PARAM_STR_ARRAY)))
->andWhere($query->expr()->eq('propertyvalue', $query->createNamedParameter('Object')))
->executeStatement();

$output->info("$updated invalid object properties removed.");
}
}
58 changes: 58 additions & 0 deletions apps/dav/lib/Migration/Version1024Date20211221144219.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?php

declare(strict_types=1);

namespace OCA\DAV\Migration;

use Closure;
use Doctrine\DBAL\Schema\SchemaException;
use OCA\DAV\DAV\CustomPropertiesBackend;
use OCP\DB\ISchemaWrapper;
use OCP\DB\Types;
use OCP\Migration\IOutput;
use OCP\Migration\SimpleMigrationStep;

/**
* Auto-generated migration step: Please modify to your needs!
*/
class Version1024Date20211221144219 extends SimpleMigrationStep {

/**
* @param IOutput $output
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
*/
public function preSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
}

/**
* @param IOutput $output
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
* @return null|ISchemaWrapper
* @throws SchemaException
*/
public function changeSchema(IOutput $output, Closure $schemaClosure, array $options): ?ISchemaWrapper {
/** @var ISchemaWrapper $schema */
$schema = $schemaClosure();
$propertiesTable = $schema->getTable('properties');

if ($propertiesTable->hasColumn('valuetype')) {
return null;
}
$propertiesTable->addColumn('valuetype', Types::SMALLINT, [
'notnull' => false,
'default' => CustomPropertiesBackend::PROPERTY_TYPE_STRING
]);

return $schema;
}

/**
* @param IOutput $output
* @param Closure $schemaClosure The `\Closure` returns a `ISchemaWrapper`
* @param array $options
*/
public function postSchemaChange(IOutput $output, Closure $schemaClosure, array $options): void {
}
}