Skip to content

Commit 5df8127

Browse files
ndo84bwclaude
andcommitted
fix(dav): send reply email to organizer when attendee responds via invitation link
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Nico Donath <ndo84bw@gmx.de>
1 parent e6b8de4 commit 5df8127

2 files changed

Lines changed: 57 additions & 0 deletions

File tree

apps/dav/lib/CalDAV/InvitationResponse/InvitationResponseServer.php

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use OCA\DAV\CalDAV\Auth\PublicPrincipalPlugin;
1313
use OCA\DAV\CalDAV\DefaultCalendarValidator;
1414
use OCA\DAV\CalDAV\Publishing\PublishPlugin;
15+
use OCA\DAV\CalDAV\Schedule\IMipPlugin;
1516
use OCA\DAV\Connector\Sabre\AnonymousOptionsPlugin;
1617
use OCA\DAV\Connector\Sabre\BlockLegacyClientPlugin;
1718
use OCA\DAV\Connector\Sabre\CachingTree;
@@ -113,6 +114,11 @@ public function __construct(bool $public = true) {
113114
* @return void
114115
*/
115116
public function handleITipMessage(Message $iTipMessage) {
117+
// Register the iMIP plugin only for the invitation-link flow, it must stay absent for createFromStringMinimal() and CalendarImpl
118+
if ($this->server->getPlugin('imip') === null) {
119+
$this->server->addPlugin(Server::get(IMipPlugin::class));
120+
}
121+
116122
/** @var \OCA\DAV\CalDAV\Schedule\Plugin $schedulingPlugin */
117123
$schedulingPlugin = $this->server->getPlugin('caldav-schedule');
118124
$schedulingPlugin->scheduleLocalDelivery($iTipMessage);
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
/**
6+
* SPDX-FileCopyrightText: 2026 Nextcloud GmbH and Nextcloud contributors
7+
* SPDX-License-Identifier: AGPL-3.0-or-later
8+
*/
9+
namespace OCA\DAV\Tests\unit\CalDAV\InvitationResponse;
10+
11+
use OCA\DAV\CalDAV\InvitationResponse\InvitationResponseServer;
12+
use OCA\DAV\CalDAV\Schedule\IMipPlugin;
13+
use PHPUnit\Framework\Attributes\Group;
14+
use Sabre\VObject\ITip\Message;
15+
use Sabre\VObject\Reader;
16+
use Test\TestCase;
17+
18+
#[Group(name: 'DB')]
19+
class InvitationResponseServerTest extends TestCase {
20+
public function testIMipPluginIsOnlyRegisteredWhenHandlingITipMessages(): void {
21+
$server = new InvitationResponseServer();
22+
23+
// Not registered by the constructor: other flows (e.g. createFromStringMinimal) must not send emails
24+
$this->assertNull($server->getServer()->getPlugin('imip'));
25+
26+
$message = new Message();
27+
$message->uid = 'fb1bc04c-ac3e-4f5d-8329-7a1e0b07a1e0';
28+
$message->component = 'VEVENT';
29+
$message->method = 'REPLY';
30+
$message->sequence = 0;
31+
$message->sender = 'mailto:attendee@example.com';
32+
$message->recipient = 'mailto:unknown-organizer@example.com';
33+
$message->message = Reader::read(<<<EOF
34+
BEGIN:VCALENDAR
35+
VERSION:2.0
36+
METHOD:REPLY
37+
BEGIN:VEVENT
38+
ATTENDEE;PARTSTAT=ACCEPTED:mailto:attendee@example.com
39+
ORGANIZER:mailto:unknown-organizer@example.com
40+
UID:fb1bc04c-ac3e-4f5d-8329-7a1e0b07a1e0
41+
SEQUENCE:0
42+
DTSTAMP:20260611T120000Z
43+
END:VEVENT
44+
END:VCALENDAR
45+
EOF);
46+
47+
$server->handleITipMessage($message);
48+
49+
$this->assertInstanceOf(IMipPlugin::class, $server->getServer()->getPlugin('imip'));
50+
}
51+
}

0 commit comments

Comments
 (0)