From 7ca086229e852780b049391acb261be8797f8621 Mon Sep 17 00:00:00 2001 From: Josh Date: Tue, 13 Oct 2026 09:32:30 -0400 Subject: [PATCH 1/2] fix(dav): handle 4-byte text consistently in calendar indexes CalDAV indexes selected property values and parameters in the same table. However, updateProperties() used opposite supports4ByteText() checks for those two paths. Looks like #36528 may have inadvertently changed this conditional. Additional historical context in #5004. Also remove the redundant pre-truncation in the ordinary-property path. Signed-off-by: Josh --- apps/dav/lib/CalDAV/CalDavBackend.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/apps/dav/lib/CalDAV/CalDavBackend.php b/apps/dav/lib/CalDAV/CalDavBackend.php index 26c27476a4ecc..d783c0532d080 100644 --- a/apps/dav/lib/CalDAV/CalDavBackend.php +++ b/apps/dav/lib/CalDAV/CalDavBackend.php @@ -3746,7 +3746,6 @@ public function updateProperties($calendarId, $objectUri, $calendarData, $calend if (!$this->db->supports4ByteText()) { $value = preg_replace('/[\x{10000}-\x{10FFFF}]/u', "\xEF\xBF\xBD", $value); } - $value = mb_strcut($value, 0, 254); $query->setParameter('name', $property->name); $query->setParameter('parameter', null); @@ -3761,7 +3760,7 @@ public function updateProperties($calendarId, $objectUri, $calendarData, $calend foreach ($parameters as $key => $value) { if (in_array($key, $indexedParametersForProperty, true)) { // is this a shitty db? - if ($this->db->supports4ByteText()) { + if (!$this->db->supports4ByteText()) { $value = preg_replace('/[\x{10000}-\x{10FFFF}]/u', "\xEF\xBF\xBD", $value); } From 11988ed71401c4ec9c45aee6f873974a488a110b Mon Sep 17 00:00:00 2001 From: Josh Date: Tue, 13 Oct 2026 09:39:07 -0400 Subject: [PATCH 2/2] test(dav): cover 4-byte text in indexed calendar parameters Add regression coverage for emoji in indexed ATTENDEE and ORGANIZER CN parameters. Assert that indexed values preserve four-byte characters when supported by the database and replace them when unsupported. Assisted-by: Copilot:gpt-6-luna Signed-off-by: Josh --- .../tests/unit/CalDAV/CalDavBackendTest.php | 44 +++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php b/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php index 4e37a7d47e2b4..a549bcbb5d87a 100644 --- a/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php +++ b/apps/dav/tests/unit/CalDAV/CalDavBackendTest.php @@ -266,6 +266,50 @@ public function testCalendarObjectsOperations(): void { $this->assertCount(0, $calendarObjects); } + public function testUpdatePropertiesHandlesFourByteIndexedParameters(): void { + $this->dispatcher->expects(self::any())->method('dispatchTyped'); + $calendarId = $this->createTestCalendar(); + $uri = static::getUniqueID('calobj'); + $calData = <<<'EOD' +BEGIN:VCALENDAR +VERSION:2.0 +PRODID:Nextcloud Calendar +BEGIN:VEVENT +UID:indexed-parameter-four-byte-test +DTSTAMP:20240101T000000Z +DTSTART:20240101T100000Z +DTEND:20240101T110000Z +ATTENDEE;CN=Attendee 🙈:mailto:attendee@example.com +ORGANIZER;CN=Organizer 🙈:mailto:organizer@example.com +END:VEVENT +END:VCALENDAR +EOD; + $this->backend->createCalendarObject($calendarId, $uri, $calData); + $object = $this->backend->getCalendarObject($calendarId, $uri); + self::assertNotNull($object); + + $supported = $this->db->supports4ByteText(); + $expectedValues = [ + 'ATTENDEE' => 'Attendee ' . ($supported ? '🙈' : "\u{FFFD}"), + 'ORGANIZER' => 'Organizer ' . ($supported ? '🙈' : "\u{FFFD}"), + ]; + + foreach ($expectedValues as $property => $expectedValue) { + $query = $this->db->getQueryBuilder(); + $result = $query->select('value') + ->from('calendarobjects_props') + ->where($query->expr()->eq('objectid', $query->createNamedParameter((int)$object['id']))) + ->andWhere($query->expr()->eq('name', $query->createNamedParameter($property))) + ->andWhere($query->expr()->eq('parameter', $query->createNamedParameter('CN'))) + ->setMaxResults(1) + ->executeQuery(); + $indexedValue = $result->fetchOne(); + $result->closeCursor(); + + self::assertSame($expectedValue, $indexedValue, "$property CN index value"); + } + } + public function testMultipleCalendarObjectsWithSameUID(): void { $this->expectException(UidConflict::class); $this->expectExceptionMessage('Calendar object with uid already exists in this calendar collection.');