summaryrefslogtreecommitdiffstats
path: root/apps/dav
diff options
context:
space:
mode:
authorJohn Molakvoæ <skjnldsv@users.noreply.github.com>2023-08-16 15:43:10 +0200
committerGitHub <noreply@github.com>2023-08-16 15:43:10 +0200
commitde7822477c24e74f24052ea8db36ad4c8b0aaa54 (patch)
tree3fef0514a05e49eb453c62eb4cc4df6c329fbe92 /apps/dav
parent29407c2764e07fe20865ad568eef35b43e1596b3 (diff)
parent526a1404c026a80479ec4c16e06ced8fbe653e62 (diff)
downloadnextcloud-server-de7822477c24e74f24052ea8db36ad4c8b0aaa54.tar.gz
nextcloud-server-de7822477c24e74f24052ea8db36ad4c8b0aaa54.zip
Merge pull request #39858 from nextcloud/backport/39568/stable27
Diffstat (limited to 'apps/dav')
-rw-r--r--apps/dav/lib/CalDAV/CalDavBackend.php39
-rw-r--r--apps/dav/lib/CalDAV/CalendarHome.php5
-rw-r--r--apps/dav/lib/DAV/Sharing/Backend.php48
-rw-r--r--apps/dav/lib/DAV/Sharing/Plugin.php16
4 files changed, 103 insertions, 5 deletions
diff --git a/apps/dav/lib/CalDAV/CalDavBackend.php b/apps/dav/lib/CalDAV/CalDavBackend.php
index f4113de50b9..4c1acfbfbfe 100644
--- a/apps/dav/lib/CalDAV/CalDavBackend.php
+++ b/apps/dav/lib/CalDAV/CalDavBackend.php
@@ -217,6 +217,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
private IConfig $config;
private bool $legacyEndpoint;
private string $dbObjectPropertiesTable = 'calendarobjects_props';
+ private array $cachedObjects = [];
public function __construct(IDBConnection $db,
Principal $principalBackend,
@@ -1112,6 +1113,10 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
* @return array|null
*/
public function getCalendarObject($calendarId, $objectUri, int $calendarType = self::CALENDAR_TYPE_CALENDAR) {
+ $key = $calendarId . '::' . $objectUri . '::' . $calendarType;
+ if (isset($this->cachedObjects[$key])) {
+ return $this->cachedObjects[$key];
+ }
$query = $this->db->getQueryBuilder();
$query->select(['id', 'uri', 'lastmodified', 'etag', 'calendarid', 'size', 'calendardata', 'componenttype', 'classification', 'deleted_at'])
->from('calendarobjects')
@@ -1126,6 +1131,12 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
return null;
}
+ $object = $this->rowToCalendarObject($row);
+ $this->cachedObjects[$key] = $object;
+ return $object;
+ }
+
+ private function rowToCalendarObject(array $row): array {
return [
'id' => $row['id'],
'uri' => $row['uri'],
@@ -1212,6 +1223,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
* @return string
*/
public function createCalendarObject($calendarId, $objectUri, $calendarData, $calendarType = self::CALENDAR_TYPE_CALENDAR) {
+ $this->cachedObjects = [];
$extraData = $this->getDenormalizedData($calendarData);
return $this->atomic(function () use ($calendarId, $objectUri, $calendarData, $extraData, $calendarType) {
@@ -1306,6 +1318,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
* @return string
*/
public function updateCalendarObject($calendarId, $objectUri, $calendarData, $calendarType = self::CALENDAR_TYPE_CALENDAR) {
+ $this->cachedObjects = [];
$extraData = $this->getDenormalizedData($calendarData);
return $this->atomic(function () use ($calendarId, $objectUri, $calendarData, $extraData, $calendarType) {
@@ -1359,6 +1372,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
* @throws Exception
*/
public function moveCalendarObject(int $sourceCalendarId, int $targetCalendarId, int $objectId, string $oldPrincipalUri, string $newPrincipalUri, int $calendarType = self::CALENDAR_TYPE_CALENDAR): bool {
+ $this->cachedObjects = [];
return $this->atomic(function () use ($sourceCalendarId, $targetCalendarId, $objectId, $oldPrincipalUri, $newPrincipalUri, $calendarType) {
$object = $this->getCalendarObjectById($oldPrincipalUri, $objectId);
if (empty($object)) {
@@ -1406,6 +1420,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
* @param int $classification
*/
public function setClassification($calendarObjectId, $classification) {
+ $this->cachedObjects = [];
if (!in_array($classification, [
self::CLASSIFICATION_PUBLIC, self::CLASSIFICATION_PRIVATE, self::CLASSIFICATION_CONFIDENTIAL
])) {
@@ -1430,6 +1445,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
* @return void
*/
public function deleteCalendarObject($calendarId, $objectUri, $calendarType = self::CALENDAR_TYPE_CALENDAR, bool $forceDeletePermanently = false) {
+ $this->cachedObjects = [];
$this->atomic(function () use ($calendarId, $objectUri, $calendarType, $forceDeletePermanently) {
$data = $this->getCalendarObject($calendarId, $objectUri, $calendarType);
@@ -1511,6 +1527,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
* @throws Forbidden
*/
public function restoreCalendarObject(array $objectData): void {
+ $this->cachedObjects = [];
$this->atomic(function () use ($objectData) {
$id = (int) $objectData['id'];
$restoreUri = str_replace("-deleted.ics", ".ics", $objectData['uri']);
@@ -1638,12 +1655,8 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
}
}
}
- $columns = ['uri'];
- if ($requirePostFilter) {
- $columns = ['uri', 'calendardata'];
- }
$query = $this->db->getQueryBuilder();
- $query->select($columns)
+ $query->select(['id', 'uri', 'lastmodified', 'etag', 'calendarid', 'size', 'calendardata', 'componenttype', 'classification', 'deleted_at'])
->from('calendarobjects')
->where($query->expr()->eq('calendarid', $query->createNamedParameter($calendarId)))
->andWhere($query->expr()->eq('calendartype', $query->createNamedParameter($calendarType)))
@@ -1664,6 +1677,11 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
$result = [];
while ($row = $stmt->fetch()) {
+ // if we leave it as a blob we can't read it both from the post filter and the rowToCalendarObject
+ if (isset($row['calendardata'])) {
+ $row['calendardata'] = $this->readBlob($row['calendardata']);
+ }
+
if ($requirePostFilter) {
// validateFilterForObject will parse the calendar data
// catch parsing errors
@@ -1688,6 +1706,8 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
}
}
$result[] = $row['uri'];
+ $key = $calendarId . '::' . $row['uri'] . '::' . $calendarType;
+ $this->cachedObjects[$key] = $this->rowToCalendarObject($row);
}
return $result;
@@ -2648,6 +2668,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
* @return void
*/
public function deleteSchedulingObject($principalUri, $objectUri) {
+ $this->cachedObjects = [];
$query = $this->db->getQueryBuilder();
$query->delete('schedulingobjects')
->where($query->expr()->eq('principaluri', $query->createNamedParameter($principalUri)))
@@ -2664,6 +2685,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
* @return void
*/
public function createSchedulingObject($principalUri, $objectUri, $objectData) {
+ $this->cachedObjects = [];
$query = $this->db->getQueryBuilder();
$query->insert('schedulingobjects')
->values([
@@ -2687,6 +2709,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
* @return void
*/
protected function addChange(int $calendarId, string $objectUri, int $operation, int $calendarType = self::CALENDAR_TYPE_CALENDAR): void {
+ $this->cachedObjects = [];
$table = $calendarType === self::CALENDAR_TYPE_CALENDAR ? 'calendars': 'calendarsubscriptions';
$this->atomic(function () use ($calendarId, $objectUri, $operation, $calendarType, $table) {
@@ -2858,6 +2881,10 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
return $this->calendarSharingBackend->getShares($resourceId);
}
+ public function preloadShares(array $resourceIds): void {
+ $this->calendarSharingBackend->preloadShares($resourceIds);
+ }
+
/**
* @param boolean $value
* @param \OCA\DAV\CalDAV\Calendar $calendar
@@ -2929,6 +2956,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
* @param int $calendarType
*/
public function updateProperties($calendarId, $objectUri, $calendarData, $calendarType = self::CALENDAR_TYPE_CALENDAR) {
+ $this->cachedObjects = [];
$this->atomic(function () use ($calendarId, $objectUri, $calendarData, $calendarType) {
$objectId = $this->getCalendarObjectId($calendarId, $objectUri, $calendarType);
@@ -3093,6 +3121,7 @@ class CalDavBackend extends AbstractBackend implements SyncSupport, Subscription
* @param int $objectId
*/
protected function purgeProperties($calendarId, $objectId) {
+ $this->cachedObjects = [];
$query = $this->db->getQueryBuilder();
$query->delete($this->dbObjectPropertiesTable)
->where($query->expr()->eq('objectid', $query->createNamedParameter($objectId)))
diff --git a/apps/dav/lib/CalDAV/CalendarHome.php b/apps/dav/lib/CalDAV/CalendarHome.php
index a59e76d121f..cbf5cebc9e7 100644
--- a/apps/dav/lib/CalDAV/CalendarHome.php
+++ b/apps/dav/lib/CalDAV/CalendarHome.php
@@ -58,6 +58,7 @@ class CalendarHome extends \Sabre\CalDAV\CalendarHome {
/** @var LoggerInterface */
private $logger;
+ private ?array $cachedChildren = null;
public function __construct(BackendInterface $caldavBackend, $principalInfo, LoggerInterface $logger) {
parent::__construct($caldavBackend, $principalInfo);
@@ -97,6 +98,9 @@ class CalendarHome extends \Sabre\CalDAV\CalendarHome {
* @inheritdoc
*/
public function getChildren() {
+ if ($this->cachedChildren) {
+ return $this->cachedChildren;
+ }
$calendars = $this->caldavBackend->getCalendarsForUser($this->principalInfo['uri']);
$objects = [];
foreach ($calendars as $calendar) {
@@ -136,6 +140,7 @@ class CalendarHome extends \Sabre\CalDAV\CalendarHome {
}
}
+ $this->cachedChildren = $objects;
return $objects;
}
diff --git a/apps/dav/lib/DAV/Sharing/Backend.php b/apps/dav/lib/DAV/Sharing/Backend.php
index 813f99dcbbd..74b34b81b22 100644
--- a/apps/dav/lib/DAV/Sharing/Backend.php
+++ b/apps/dav/lib/DAV/Sharing/Backend.php
@@ -30,6 +30,7 @@ namespace OCA\DAV\DAV\Sharing;
use OCA\DAV\Connector\Sabre\Principal;
use OCP\AppFramework\Db\TTransactional;
+use OCP\Cache\CappedMemoryCache;
use OCP\IDBConnection;
use OCP\IGroupManager;
use OCP\IUserManager;
@@ -48,12 +49,15 @@ class Backend {
public const ACCESS_READ_WRITE = 2;
public const ACCESS_READ = 3;
+ private CappedMemoryCache $shareCache;
+
public function __construct(IDBConnection $db, IUserManager $userManager, IGroupManager $groupManager, Principal $principalBackend, string $resourceType) {
$this->db = $db;
$this->userManager = $userManager;
$this->groupManager = $groupManager;
$this->principalBackend = $principalBackend;
$this->resourceType = $resourceType;
+ $this->shareCache = new CappedMemoryCache();
}
/**
@@ -61,6 +65,7 @@ class Backend {
* @param list<string> $remove
*/
public function updateShares(IShareable $shareable, array $add, array $remove): void {
+ $this->shareCache->clear();
$this->atomic(function () use ($shareable, $add, $remove) {
foreach ($add as $element) {
$principal = $this->principalBackend->findByUri($element['href'], '');
@@ -81,6 +86,7 @@ class Backend {
* @param array{href: string, commonName: string, readOnly: bool} $element
*/
private function shareWith(IShareable $shareable, array $element): void {
+ $this->shareCache->clear();
$user = $element['href'];
$parts = explode(':', $user, 2);
if ($parts[0] !== 'principal') {
@@ -124,6 +130,7 @@ class Backend {
}
public function deleteAllShares(int $resourceId): void {
+ $this->shareCache->clear();
$query = $this->db->getQueryBuilder();
$query->delete('dav_shares')
->where($query->expr()->eq('resourceid', $query->createNamedParameter($resourceId)))
@@ -132,6 +139,7 @@ class Backend {
}
public function deleteAllSharesByUser(string $principaluri): void {
+ $this->shareCache->clear();
$query = $this->db->getQueryBuilder();
$query->delete('dav_shares')
->where($query->expr()->eq('principaluri', $query->createNamedParameter($principaluri)))
@@ -140,6 +148,7 @@ class Backend {
}
private function unshare(IShareable $shareable, string $element): void {
+ $this->shareCache->clear();
$parts = explode(':', $element, 2);
if ($parts[0] !== 'principal') {
return;
@@ -172,6 +181,10 @@ class Backend {
* @return list<array{href: string, commonName: string, status: int, readOnly: bool, '{http://owncloud.org/ns}principal': string, '{http://owncloud.org/ns}group-share': bool}>
*/
public function getShares(int $resourceId): array {
+ $cached = $this->shareCache->get($resourceId);
+ if ($cached) {
+ return $cached;
+ }
$query = $this->db->getQueryBuilder();
$result = $query->select(['principaluri', 'access'])
->from('dav_shares')
@@ -193,9 +206,44 @@ class Backend {
];
}
+ $this->shareCache->set((string)$resourceId, $shares);
return $shares;
}
+ public function preloadShares(array $resourceIds): void {
+ $resourceIds = array_filter($resourceIds, function(int $resourceId) {
+ return !isset($this->shareCache[$resourceId]);
+ });
+ if (count($resourceIds) === 0) {
+ return;
+ }
+ $query = $this->db->getQueryBuilder();
+ $result = $query->select(['resourceid', 'principaluri', 'access'])
+ ->from('dav_shares')
+ ->where($query->expr()->in('resourceid', $query->createNamedParameter($resourceIds, IQueryBuilder::PARAM_INT_ARRAY)))
+ ->andWhere($query->expr()->eq('type', $query->createNamedParameter($this->resourceType)))
+ ->groupBy(['principaluri', 'access', 'resourceid'])
+ ->executeQuery();
+
+ $sharesByResource = array_fill_keys($resourceIds, []);
+ while ($row = $result->fetch()) {
+ $resourceId = (int)$row['resourceid'];
+ $p = $this->principalBackend->getPrincipalByPath($row['principaluri']);
+ $sharesByResource[$resourceId][] = [
+ 'href' => "principal:{$row['principaluri']}",
+ 'commonName' => isset($p['{DAV:}displayname']) ? (string)$p['{DAV:}displayname'] : '',
+ 'status' => 1,
+ 'readOnly' => (int) $row['access'] === self::ACCESS_READ,
+ '{http://owncloud.org/ns}principal' => (string)$row['principaluri'],
+ '{http://owncloud.org/ns}group-share' => isset($p['uri']) ? str_starts_with($p['uri'], 'principals/groups') : false
+ ];
+ }
+
+ foreach ($resourceIds as $resourceId) {
+ $this->shareCache->set($resourceId, $sharesByResource[$resourceId]);
+ }
+ }
+
/**
* For shared resources the sharee is set in the ACL of the resource
*
diff --git a/apps/dav/lib/DAV/Sharing/Plugin.php b/apps/dav/lib/DAV/Sharing/Plugin.php
index a4b2cd3681c..8ddcb664fd5 100644
--- a/apps/dav/lib/DAV/Sharing/Plugin.php
+++ b/apps/dav/lib/DAV/Sharing/Plugin.php
@@ -24,6 +24,8 @@
*/
namespace OCA\DAV\DAV\Sharing;
+use OCA\DAV\CalDAV\CalDavBackend;
+use OCA\DAV\CalDAV\CalendarHome;
use OCA\DAV\Connector\Sabre\Auth;
use OCA\DAV\DAV\Sharing\Xml\Invite;
use OCA\DAV\DAV\Sharing\Xml\ShareRequest;
@@ -201,6 +203,20 @@ class Plugin extends ServerPlugin {
* @return void
*/
public function propFind(PropFind $propFind, INode $node) {
+ if ($node instanceof CalendarHome && $propFind->getDepth() === 1) {
+ $backend = $node->getCalDAVBackend();
+ if ($backend instanceof CalDavBackend) {
+ $calendars = $node->getChildren();
+ $calendars = array_filter($calendars, function (INode $node) {
+ return $node instanceof IShareable;
+ });
+ /** @var int[] $resourceIds */
+ $resourceIds = array_map(function (IShareable $node) {
+ return $node->getResourceId();
+ }, $calendars);
+ $backend->preloadShares($resourceIds);
+ }
+ }
if ($node instanceof IShareable) {
$propFind->handle('{' . Plugin::NS_OWNCLOUD . '}invite', function () use ($node) {
return new Invite(