aboutsummaryrefslogtreecommitdiffstats
path: root/apps/dav/lib/CalDAV
diff options
context:
space:
mode:
authorSebastianKrupinski <krupinskis05@gmail.com>2024-10-06 18:39:36 -0400
committerSebastianKrupinski <krupinskis05@gmail.com>2024-10-21 14:21:28 -0400
commit3f4210e7371351b68ee3055f940d9f2727f6b61e (patch)
treeb019a265d41cdd853668a6164c26aa2b25bcacd6 /apps/dav/lib/CalDAV
parent432ea6e714362ef89e38887c8889bf653516cad4 (diff)
downloadnextcloud-server-3f4210e7371351b68ee3055f940d9f2727f6b61e.tar.gz
nextcloud-server-3f4210e7371351b68ee3055f940d9f2727f6b61e.zip
fix: override iTip Broker to fix several issuesfix/overide-itip-broker
Signed-off-by: SebastianKrupinski <krupinskis05@gmail.com>
Diffstat (limited to 'apps/dav/lib/CalDAV')
-rw-r--r--apps/dav/lib/CalDAV/Schedule/Plugin.php10
-rw-r--r--apps/dav/lib/CalDAV/TipBroker.php187
2 files changed, 196 insertions, 1 deletions
diff --git a/apps/dav/lib/CalDAV/Schedule/Plugin.php b/apps/dav/lib/CalDAV/Schedule/Plugin.php
index 11f0dfbe251..010f6408731 100644
--- a/apps/dav/lib/CalDAV/Schedule/Plugin.php
+++ b/apps/dav/lib/CalDAV/Schedule/Plugin.php
@@ -11,6 +11,7 @@ use OCA\DAV\CalDAV\Calendar;
use OCA\DAV\CalDAV\CalendarHome;
use OCA\DAV\CalDAV\CalendarObject;
use OCA\DAV\CalDAV\DefaultCalendarValidator;
+use OCA\DAV\CalDAV\TipBroker;
use OCP\IConfig;
use Psr\Log\LoggerInterface;
use Sabre\CalDAV\ICalendar;
@@ -86,6 +87,13 @@ class Plugin extends \Sabre\CalDAV\Schedule\Plugin {
}
/**
+ * Returns an instance of the iTip\Broker.
+ */
+ protected function createITipBroker(): TipBroker {
+ return new TipBroker();
+ }
+
+ /**
* Allow manual setting of the object change URL
* to support public write
*
@@ -168,7 +176,7 @@ class Plugin extends \Sabre\CalDAV\Schedule\Plugin {
$calendarNode = $this->server->tree->getNodeForPath($calendarPath);
// extract addresses for owner
$addresses = $this->getAddressesForPrincipal($calendarNode->getOwner());
- // determain if request is from a sharee
+ // determine if request is from a sharee
if ($calendarNode->isShared()) {
// extract addresses for sharee and add to address collection
$addresses = array_merge(
diff --git a/apps/dav/lib/CalDAV/TipBroker.php b/apps/dav/lib/CalDAV/TipBroker.php
new file mode 100644
index 00000000000..43eff124f0b
--- /dev/null
+++ b/apps/dav/lib/CalDAV/TipBroker.php
@@ -0,0 +1,187 @@
+<?php
+
+declare(strict_types=1);
+
+/**
+ * SPDX-FileCopyrightText: 2024 Nextcloud GmbH and Nextcloud contributors
+ * SPDX-License-Identifier: AGPL-3.0-or-later
+ */
+
+namespace OCA\DAV\CalDAV;
+
+use Sabre\VObject\Component\VCalendar;
+use Sabre\VObject\ITip\Broker;
+use Sabre\VObject\ITip\Message;
+
+class TipBroker extends Broker {
+
+ public $significantChangeProperties = [
+ 'DTSTART',
+ 'DTEND',
+ 'DURATION',
+ 'DUE',
+ 'RRULE',
+ 'RDATE',
+ 'EXDATE',
+ 'STATUS',
+ 'SUMMARY',
+ 'DESCRIPTION',
+ 'LOCATION',
+
+ ];
+
+ /**
+ * This method is used in cases where an event got updated, and we
+ * potentially need to send emails to attendees to let them know of updates
+ * in the events.
+ *
+ * We will detect which attendees got added, which got removed and create
+ * specific messages for these situations.
+ *
+ * @return array
+ */
+ protected function parseEventForOrganizer(VCalendar $calendar, array $eventInfo, array $oldEventInfo) {
+ // Merging attendee lists.
+ $attendees = [];
+ foreach ($oldEventInfo['attendees'] as $attendee) {
+ $attendees[$attendee['href']] = [
+ 'href' => $attendee['href'],
+ 'oldInstances' => $attendee['instances'],
+ 'newInstances' => [],
+ 'name' => $attendee['name'],
+ 'forceSend' => null,
+ ];
+ }
+ foreach ($eventInfo['attendees'] as $attendee) {
+ if (isset($attendees[$attendee['href']])) {
+ $attendees[$attendee['href']]['name'] = $attendee['name'];
+ $attendees[$attendee['href']]['newInstances'] = $attendee['instances'];
+ $attendees[$attendee['href']]['forceSend'] = $attendee['forceSend'];
+ } else {
+ $attendees[$attendee['href']] = [
+ 'href' => $attendee['href'],
+ 'oldInstances' => [],
+ 'newInstances' => $attendee['instances'],
+ 'name' => $attendee['name'],
+ 'forceSend' => $attendee['forceSend'],
+ ];
+ }
+ }
+
+ $messages = [];
+
+ foreach ($attendees as $attendee) {
+ // An organizer can also be an attendee. We should not generate any
+ // messages for those.
+ if ($attendee['href'] === $eventInfo['organizer']) {
+ continue;
+ }
+
+ $message = new Message();
+ $message->uid = $eventInfo['uid'];
+ $message->component = 'VEVENT';
+ $message->sequence = $eventInfo['sequence'];
+ $message->sender = $eventInfo['organizer'];
+ $message->senderName = $eventInfo['organizerName'];
+ $message->recipient = $attendee['href'];
+ $message->recipientName = $attendee['name'];
+
+ // Creating the new iCalendar body.
+ $icalMsg = new VCalendar();
+
+ foreach ($calendar->select('VTIMEZONE') as $timezone) {
+ $icalMsg->add(clone $timezone);
+ }
+ // If there are no instances the attendee is a part of, it means
+ // the attendee was removed and we need to send them a CANCEL message.
+ // Also If the meeting STATUS property was changed to CANCELLED
+ // we need to send the attendee a CANCEL message.
+ if (!$attendee['newInstances'] || $eventInfo['status'] === 'CANCELLED') {
+
+ $message->method = $icalMsg->METHOD = 'CANCEL';
+ $message->significantChange = true;
+ // clone base event
+ $event = clone $eventInfo['instances']['master'];
+ // alter some properties
+ unset($event->ATTENDEE);
+ $event->add('ATTENDEE', $attendee['href'], ['CN' => $attendee['name'],]);
+ $event->DTSTAMP = gmdate('Ymd\\THis\\Z');
+ $event->SEQUENCE = $message->sequence;
+ $icalMsg->add($event);
+
+ } else {
+ // The attendee gets the updated event body
+ $message->method = $icalMsg->METHOD = 'REQUEST';
+
+ // We need to find out that this change is significant. If it's
+ // not, systems may opt to not send messages.
+ //
+ // We do this based on the 'significantChangeHash' which is
+ // some value that changes if there's a certain set of
+ // properties changed in the event, or simply if there's a
+ // difference in instances that the attendee is invited to.
+
+ $oldAttendeeInstances = array_keys($attendee['oldInstances']);
+ $newAttendeeInstances = array_keys($attendee['newInstances']);
+
+ $message->significantChange =
+ $attendee['forceSend'] === 'REQUEST' ||
+ count($oldAttendeeInstances) !== count($newAttendeeInstances) ||
+ count(array_diff($oldAttendeeInstances, $newAttendeeInstances)) > 0 ||
+ $oldEventInfo['significantChangeHash'] !== $eventInfo['significantChangeHash'];
+
+ foreach ($attendee['newInstances'] as $instanceId => $instanceInfo) {
+ $currentEvent = clone $eventInfo['instances'][$instanceId];
+ if ($instanceId === 'master') {
+ // We need to find a list of events that the attendee
+ // is not a part of to add to the list of exceptions.
+ $exceptions = [];
+ foreach ($eventInfo['instances'] as $instanceId => $vevent) {
+ if (!isset($attendee['newInstances'][$instanceId])) {
+ $exceptions[] = $instanceId;
+ }
+ }
+
+ // If there were exceptions, we need to add it to an
+ // existing EXDATE property, if it exists.
+ if ($exceptions) {
+ if (isset($currentEvent->EXDATE)) {
+ $currentEvent->EXDATE->setParts(array_merge(
+ $currentEvent->EXDATE->getParts(),
+ $exceptions
+ ));
+ } else {
+ $currentEvent->EXDATE = $exceptions;
+ }
+ }
+
+ // Cleaning up any scheduling information that
+ // shouldn't be sent along.
+ unset($currentEvent->ORGANIZER['SCHEDULE-FORCE-SEND']);
+ unset($currentEvent->ORGANIZER['SCHEDULE-STATUS']);
+
+ foreach ($currentEvent->ATTENDEE as $attendee) {
+ unset($attendee['SCHEDULE-FORCE-SEND']);
+ unset($attendee['SCHEDULE-STATUS']);
+
+ // We're adding PARTSTAT=NEEDS-ACTION to ensure that
+ // iOS shows an "Inbox Item"
+ if (!isset($attendee['PARTSTAT'])) {
+ $attendee['PARTSTAT'] = 'NEEDS-ACTION';
+ }
+ }
+ }
+
+ $currentEvent->DTSTAMP = gmdate('Ymd\\THis\\Z');
+ $icalMsg->add($currentEvent);
+ }
+ }
+
+ $message->message = $icalMsg;
+ $messages[] = $message;
+ }
+
+ return $messages;
+ }
+
+}