Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(caldav): Add default reminder to attendees of scheduling messages #48226

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Changes from 2 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
191 changes: 190 additions & 1 deletion apps/dav/lib/CalDAV/Schedule/Plugin.php
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
use Sabre\DAV\PropFind;
use Sabre\DAV\Server;
use Sabre\DAV\Xml\Property\LocalHref;
use Sabre\DAVACL;
use Sabre\DAVACL\IACL;
use Sabre\DAVACL\IPrincipal;
use Sabre\HTTP\RequestInterface;
Expand Down Expand Up @@ -229,7 +230,7 @@
$vevent->remove('VALARM');
}

parent::scheduleLocalDelivery($iTipMessage);
$this->scheduleLocalDeliveryHandler($iTipMessage);
// We only care when the message was successfully delivered locally
// Log all possible codes returned from the parent method that mean something went wrong
// 3.7, 3.8, 5.0, 5.2
Expand Down Expand Up @@ -444,6 +445,159 @@
}
}

/**
* Event handler for the 'schedule' event.
*
* This handler attempts to look at local accounts to deliver the
* scheduling object.
*/
public function scheduleLocalDeliveryHandler(ITip\Message $iTipMessage)
{
$aclPlugin = $this->server->getPlugin('acl');

// Local delivery is not available if the ACL plugin is not loaded.
if (!$aclPlugin) {

Check notice

Code scanning / Psalm

DocblockTypeContradiction Note

Operand of type false is always falsy

Check notice

Code scanning / Psalm

DocblockTypeContradiction Note

Docblock-defined type Sabre\DAV\ServerPlugin for $aclPlugin is never falsy
return;
}

$caldavNS = '{'.self::NS_CALDAV.'}';

$principalUri = $aclPlugin->getPrincipalByUri($iTipMessage->recipient);

Check failure on line 465 in apps/dav/lib/CalDAV/Schedule/Plugin.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

UndefinedMethod

apps/dav/lib/CalDAV/Schedule/Plugin.php:465:31: UndefinedMethod: Method Sabre\DAV\ServerPlugin::getPrincipalByUri does not exist (see https://psalm.dev/022)

Check failure

Code scanning / Psalm

UndefinedMethod Error

Method Sabre\DAV\ServerPlugin::getPrincipalByUri does not exist
if (!$principalUri) {
$iTipMessage->scheduleStatus = '3.7;Could not find principal.';

return;
}

// We found a principal URL, now we need to find its inbox.
// Unfortunately we may not have sufficient privileges to find this, so
// we are temporarily turning off ACL to let this come through.
//
// Once we support PHP 5.5, this should be wrapped in a try..finally
// block so we can ensure that this privilege gets added again after.
$this->server->removeListener('propFind', [$aclPlugin, 'propFind']);

Check notice

Code scanning / Psalm

InvalidArgument Note

Argument 2 of Sabre\DAV\Server::removeListener expects callable, but list{Sabre\DAV\ServerPlugin, 'propFind'} provided

$result = $this->server->getProperties(
$principalUri,
[
'{DAV:}principal-URL',
$caldavNS.'calendar-home-set',
$caldavNS.'schedule-inbox-URL',
$caldavNS.'schedule-default-calendar-URL',
'{http://sabredav.org/ns}email-address',
]
);

// Re-registering the ACL event
$this->server->on('propFind', [$aclPlugin, 'propFind'], 20);

Check notice

Code scanning / Psalm

InvalidArgument Note

Argument 2 of Sabre\DAV\Server::on expects callable, but list{Sabre\DAV\ServerPlugin, 'propFind'} provided

if (!isset($result[$caldavNS.'schedule-inbox-URL'])) {
$iTipMessage->scheduleStatus = '5.2;Could not find local inbox';

return;
}
if (!isset($result[$caldavNS.'calendar-home-set'])) {
$iTipMessage->scheduleStatus = '5.2;Could not locate a calendar-home-set';

return;
}
if (!isset($result[$caldavNS.'schedule-default-calendar-URL'])) {
$iTipMessage->scheduleStatus = '5.2;Could not find a schedule-default-calendar-URL property';

return;
}

$calendarPath = $result[$caldavNS.'schedule-default-calendar-URL']->getHref();
$homePath = $result[$caldavNS.'calendar-home-set']->getHref();
$inboxPath = $result[$caldavNS.'schedule-inbox-URL']->getHref();

if ('REPLY' === $iTipMessage->method) {
$privilege = 'schedule-deliver-reply';
} else {
$privilege = 'schedule-deliver-invite';
}

if (!$aclPlugin->checkPrivileges($inboxPath, $caldavNS.$privilege, DAVACL\Plugin::R_PARENT, false)) {

Check failure on line 520 in apps/dav/lib/CalDAV/Schedule/Plugin.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

UndefinedMethod

apps/dav/lib/CalDAV/Schedule/Plugin.php:520:20: UndefinedMethod: Method Sabre\DAV\ServerPlugin::checkPrivileges does not exist (see https://psalm.dev/022)

Check failure

Code scanning / Psalm

UndefinedMethod Error

Method Sabre\DAV\ServerPlugin::checkPrivileges does not exist
$iTipMessage->scheduleStatus = '3.8;insufficient privileges: '.$privilege.' is required on the recipient schedule inbox.';

return;
}

// Next, we're going to find out if the item already exits in one of
// the users' calendars.
$uid = $iTipMessage->uid;

$newFileName = 'sabredav-'.\Sabre\DAV\UUIDUtil::getUUID().'.ics';

$home = $this->server->tree->getNodeForPath($homePath);
$inbox = $this->server->tree->getNodeForPath($inboxPath);

$currentObject = null;
$objectNode = null;
$oldICalendarData = null;
$isNewNode = false;

$userDefaultReminder = $this->config->getUserValue($this->stripOffMailTo($iTipMessage->recipient), 'calendar', 'defaultReminder', 'none');
// If the user hasn't changed the default reminder, it will use the global one
if ($userDefaultReminder === 'none') {
$userDefaultReminder = $this->config->getAppValue('calendar', 'defaultReminder', 'none');
}
if ($userDefaultReminder !== 'none') {
$userDefaultReminder = intval($userDefaultReminder);
$this->createAlarm($iTipMessage, $userDefaultReminder);
}

$result = $home->getCalendarObjectByUID($uid);

Check failure on line 550 in apps/dav/lib/CalDAV/Schedule/Plugin.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

UndefinedInterfaceMethod

apps/dav/lib/CalDAV/Schedule/Plugin.php:550:20: UndefinedInterfaceMethod: Method Sabre\DAV\INode::getCalendarObjectByUID does not exist (see https://psalm.dev/181)

Check failure

Code scanning / Psalm

UndefinedInterfaceMethod Error

Method Sabre\DAV\INode::getCalendarObjectByUID does not exist
if ($result) {
// There was an existing object, we need to update probably.
$objectPath = $homePath.'/'.$result;
$objectNode = $this->server->tree->getNodeForPath($objectPath);
$oldICalendarData = $objectNode->get();

Check failure on line 555 in apps/dav/lib/CalDAV/Schedule/Plugin.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

UndefinedInterfaceMethod

apps/dav/lib/CalDAV/Schedule/Plugin.php:555:37: UndefinedInterfaceMethod: Method Sabre\DAV\INode::get does not exist (see https://psalm.dev/181)

Check notice

Code scanning / Psalm

UndefinedInterfaceMethod Note

Method Sabre\DAV\INode::get does not exist
$currentObject = Reader::read($oldICalendarData);
} else {
$isNewNode = true;
}

$broker = new ITip\Broker();
$newObject = $broker->processMessage($iTipMessage, $currentObject);

Check notice

Code scanning / Psalm

ArgumentTypeCoercion Note

Argument 2 of Sabre\VObject\ITip\Broker::processMessage expects Sabre\VObject\Component\VCalendar|null, but parent type Sabre\VObject\Document|null provided

$inbox->createFile($newFileName, $iTipMessage->message->serialize());

Check failure on line 564 in apps/dav/lib/CalDAV/Schedule/Plugin.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

UndefinedInterfaceMethod

apps/dav/lib/CalDAV/Schedule/Plugin.php:564:11: UndefinedInterfaceMethod: Method Sabre\DAV\INode::createFile does not exist (see https://psalm.dev/181)

Check failure

Code scanning / Psalm

UndefinedInterfaceMethod Error

Method Sabre\DAV\INode::createFile does not exist

if (!$newObject) {
// We received an iTip message referring to a UID that we don't
// have in any calendars yet, and processMessage did not give us a
// calendarobject back.
//
// The implication is that processMessage did not understand the
// iTip message.
$iTipMessage->scheduleStatus = '5.0;iTip message was not processed by the server, likely because we didn\'t understand it.';

return;
}

// Note that we are bypassing ACL on purpose by calling this directly.
// We may need to look a bit deeper into this later. Supporting ACL
// here would be nice.
if ($isNewNode) {
$calendar = $this->server->tree->getNodeForPath($calendarPath);
$calendar->createFile($newFileName, $newObject->serialize());

Check failure on line 583 in apps/dav/lib/CalDAV/Schedule/Plugin.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

UndefinedInterfaceMethod

apps/dav/lib/CalDAV/Schedule/Plugin.php:583:15: UndefinedInterfaceMethod: Method Sabre\DAV\INode::createFile does not exist (see https://psalm.dev/181)

Check failure

Code scanning / Psalm

UndefinedInterfaceMethod Error

Method Sabre\DAV\INode::createFile does not exist
} else {
// If the message was a reply, we may have to inform other
// attendees of this attendees status. Therefore we're shooting off
// another itipMessage.
if ('REPLY' === $iTipMessage->method) {
$this->processICalendarChange(
$oldICalendarData,
$newObject,
[$iTipMessage->recipient],
[$iTipMessage->sender]
);
}
$objectNode->put($newObject->serialize());

Check failure on line 596 in apps/dav/lib/CalDAV/Schedule/Plugin.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

UndefinedInterfaceMethod

apps/dav/lib/CalDAV/Schedule/Plugin.php:596:17: UndefinedInterfaceMethod: Method Sabre\DAV\INode::put does not exist (see https://psalm.dev/181)

Check failure

Code scanning / Psalm

UndefinedInterfaceMethod Error

Method Sabre\DAV\INode::put does not exist

Check notice

Code scanning / Psalm

PossiblyNullReference Note

Cannot call method put on possibly null value
}
$iTipMessage->scheduleStatus = '1.2;Message delivered locally';
}

/**
* Returns a list of addresses that are associated with a principal.
*
Expand Down Expand Up @@ -541,7 +695,7 @@
// and Sabre\CalDAV\Schedule\Plugin::getFreeBusyForEmail

$aclPlugin = $this->server->getPlugin('acl');
$this->server->removeListener('propFind', [$aclPlugin, 'propFind']);

Check failure on line 698 in apps/dav/lib/CalDAV/Schedule/Plugin.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

InvalidArgument

apps/dav/lib/CalDAV/Schedule/Plugin.php:698:45: InvalidArgument: Argument 2 of Sabre\DAV\Server::removeListener expects callable, but list{Sabre\DAV\ServerPlugin, 'propFind'} provided (see https://psalm.dev/004)

$result = $aclPlugin->principalSearch(
['{http://sabredav.org/ns}email-address' => $this->stripOffMailTo($email)],
Expand All @@ -553,7 +707,7 @@

]
);
$this->server->on('propFind', [$aclPlugin, 'propFind'], 20);

Check failure on line 710 in apps/dav/lib/CalDAV/Schedule/Plugin.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

InvalidArgument

apps/dav/lib/CalDAV/Schedule/Plugin.php:710:33: InvalidArgument: Argument 2 of Sabre\DAV\Server::on expects callable, but list{Sabre\DAV\ServerPlugin, 'propFind'} provided (see https://psalm.dev/004)


// Grabbing the calendar list
Expand Down Expand Up @@ -734,4 +888,39 @@
}
}
}

/**
* Creates a VALARM inside an iTipMessage
*
* @param ITip\Message $iTipMessage
* @param int $userDefaultReminder
*/
private function createAlarm(ITip\Message $iTipMessage, int $userDefaultReminder) {

Check notice

Code scanning / Psalm

MissingReturnType Note

Method OCA\DAV\CalDAV\Schedule\Plugin::createAlarm does not have a return type, expecting void
$alarm = $iTipMessage->message->createComponent('VALARM');
$alarm->add($iTipMessage->message->createProperty('TRIGGER', '-' . $this->secondsToIso8601Duration(abs($userDefaultReminder)), ['RELATED' => 'START']));
$alarm->add($iTipMessage->message->createProperty('ACTION', 'DISPLAY'));
$iTipMessage->message->VEVENT->add($alarm);

Check failure on line 902 in apps/dav/lib/CalDAV/Schedule/Plugin.php

View workflow job for this annotation

GitHub Actions / static-code-analysis

InvalidArgument

apps/dav/lib/CalDAV/Schedule/Plugin.php:902:38: InvalidArgument: Argument 1 of Sabre\VObject\Property::add expects string, but Sabre\VObject\Component provided (see https://psalm.dev/004)

Check notice

Code scanning / Psalm

PossiblyNullReference Note

Cannot call method add on possibly null value

Check failure

Code scanning / Psalm

InvalidArgument Error

Argument 1 of Sabre\VObject\Property::add expects string, but Sabre\VObject\Component provided
}

/**
* Converts seconds to an ISO 8601 duration string
*
* @param int $secs
* @return string
*/
private function secondsToIso8601Duration(int $secs): string {
$day = 24 * 60 * 60;
$hour = 60 * 60;
$minute = 60;
if ($secs % $day === 0) {
return 'P' . $secs / $day . 'D';
}
if ($secs % $hour === 0) {
return 'PT' . $secs / $hour . 'H';
}
if ($secs % $minute === 0) {
return 'PT' . $secs / $minute . 'M';
}
return 'PT' . $secs . 'S';
}
}
Loading