Merge pull request #53036 from nextcloud/backport/53029/stable31

[stable31] fix(caldav): don't send invitations to circles
This commit is contained in:
Andy Scherzinger 2025-05-25 22:51:59 +02:00 committed by GitHub
commit 76b73436ba
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
3 changed files with 129 additions and 3 deletions

View file

@ -156,9 +156,10 @@ class IMipPlugin extends SabreIMipPlugin {
$iTipMessage->scheduleStatus = '5.0; EMail delivery failed';
return;
}
// Don't send emails to things
if ($this->imipService->isRoomOrResource($attendee)) {
$this->logger->debug('No invitation sent as recipient is room or resource', [
// Don't send emails to rooms, resources and circles
if ($this->imipService->isRoomOrResource($attendee)
|| $this->imipService->isCircle($attendee)) {
$this->logger->debug('No invitation sent as recipient is room, resource or circle', [
'attendee' => $recipient,
]);
$iTipMessage->scheduleStatus = '1.0;We got the message, but it\'s not significant enough to warrant an email';

View file

@ -1155,6 +1155,21 @@ class IMipService {
return false;
}
public function isCircle(Property $attendee): bool {
$cuType = $attendee->offsetGet('CUTYPE');
if (!$cuType instanceof Parameter) {
return false;
}
$uri = $attendee->getValue();
if (!$uri) {
return false;
}
$cuTypeValue = $cuType->getValue();
return $cuTypeValue === 'GROUP' && str_starts_with($uri, 'mailto:circle+');
}
public function minimizeInterval(\DateInterval $dateInterval): array {
// evaluate if time interval is in the past
if ($dateInterval->invert == 1) {

View file

@ -218,6 +218,10 @@ class IMipPluginTest extends TestCase {
->method('isRoomOrResource')
->with($atnd)
->willReturn(false);
$this->service->expects(self::once())
->method('isCircle')
->with($atnd)
->willReturn(false);
$this->service->expects(self::once())
->method('buildBodyData')
->with($newVevent, $oldVEvent)
@ -321,6 +325,88 @@ class IMipPluginTest extends TestCase {
->method('isRoomOrResource')
->with($room)
->willReturn(true);
$this->service->expects(self::never())
->method('isCircle');
$this->service->expects(self::never())
->method('buildBodyData');
$this->user->expects(self::any())
->method('getUID')
->willReturn('user1');
$this->user->expects(self::any())
->method('getDisplayName')
->willReturn('Mr. Wizard');
$this->userSession->expects(self::any())
->method('getUser')
->willReturn($this->user);
$this->service->expects(self::never())
->method('getFrom');
$this->service->expects(self::never())
->method('addSubjectAndHeading');
$this->service->expects(self::never())
->method('addBulletList');
$this->service->expects(self::never())
->method('getAttendeeRsvpOrReqForParticipant');
$this->config->expects(self::never())
->method('getValueString');
$this->service->expects(self::never())
->method('createInvitationToken');
$this->service->expects(self::never())
->method('addResponseButtons');
$this->service->expects(self::never())
->method('addMoreOptionsButton');
$this->mailer->expects(self::never())
->method('send');
$this->plugin->schedule($message);
$this->assertEquals('1.0', $message->getScheduleStatus());
}
public function testAttendeeIsCircle(): void {
$message = new Message();
$message->method = 'REQUEST';
$newVCalendar = new VCalendar();
$newVevent = new VEvent($newVCalendar, 'one', array_merge([
'UID' => 'uid-1234',
'SEQUENCE' => 1,
'SUMMARY' => 'Fellowship meeting without (!) Boromir',
'DTSTART' => new \DateTime('2016-01-01 00:00:00')
], []));
$newVevent->add('ORGANIZER', 'mailto:gandalf@wiz.ard');
$newVevent->add('ATTENDEE', 'mailto:' . 'circle+82utEV1Fle8wvxndZLK5TVAPtxj8IIe@middle.earth', ['RSVP' => 'TRUE', 'CN' => 'The Fellowship', 'CUTYPE' => 'GROUP']);
$newVevent->add('ATTENDEE', 'mailto:' . 'boromir@tra.it.or', ['RSVP' => 'TRUE', 'MEMBER' => 'circle+82utEV1Fle8wvxndZLK5TVAPtxj8IIe@middle.earth']);
$message->message = $newVCalendar;
$message->sender = 'mailto:gandalf@wiz.ard';
$message->senderName = 'Mr. Wizard';
$message->recipient = 'mailto:' . 'circle+82utEV1Fle8wvxndZLK5TVAPtxj8IIe@middle.earth';
$attendees = $newVevent->select('ATTENDEE');
$circle = '';
foreach ($attendees as $attendee) {
if (strcasecmp($attendee->getValue(), $message->recipient) === 0) {
$circle = $attendee;
}
}
$this->assertNotEmpty($circle, 'Failed to find attendee belonging to the circle');
$this->service->expects(self::once())
->method('getLastOccurrence')
->willReturn(1496912700);
$this->mailer->expects(self::once())
->method('validateMailAddress')
->with('circle+82utEV1Fle8wvxndZLK5TVAPtxj8IIe@middle.earth')
->willReturn(true);
$this->eventComparisonService->expects(self::once())
->method('findModified')
->willReturn(['new' => [$newVevent], 'old' => null]);
$this->service->expects(self::once())
->method('getCurrentAttendee')
->with($message)
->willReturn($circle);
$this->service->expects(self::once())
->method('isRoomOrResource')
->with($circle)
->willReturn(false);
$this->service->expects(self::once())
->method('isCircle')
->with($circle)
->willReturn(true);
$this->service->expects(self::never())
->method('buildBodyData');
$this->user->expects(self::any())
@ -422,6 +508,10 @@ class IMipPluginTest extends TestCase {
->method('isRoomOrResource')
->with($atnd)
->willReturn(false);
$this->service->expects(self::once())
->method('isCircle')
->with($atnd)
->willReturn(false);
$this->service->expects(self::once())
->method('buildBodyData')
->with($newVevent, null)
@ -553,6 +643,10 @@ class IMipPluginTest extends TestCase {
->method('isRoomOrResource')
->with($atnd)
->willReturn(false);
$this->service->expects(self::once())
->method('isCircle')
->with($atnd)
->willReturn(false);
$this->service->expects(self::once())
->method('buildBodyData')
->with($newVevent, null)
@ -659,6 +753,10 @@ class IMipPluginTest extends TestCase {
->method('isRoomOrResource')
->with($attendee)
->willReturn(false);
$this->service->expects(self::once())
->method('isCircle')
->with($attendee)
->willReturn(false);
$this->service->expects(self::once())
->method('buildBodyData')
->with($event, null)
@ -766,6 +864,10 @@ class IMipPluginTest extends TestCase {
->method('isRoomOrResource')
->with($atnd)
->willReturn(false);
$this->service->expects(self::once())
->method('isCircle')
->with($atnd)
->willReturn(false);
$this->service->expects(self::once())
->method('buildBodyData')
->with($newVevent, $oldVEvent)
@ -861,6 +963,10 @@ class IMipPluginTest extends TestCase {
->method('isRoomOrResource')
->with($atnd)
->willReturn(false);
$this->service->expects(self::once())
->method('isCircle')
->with($atnd)
->willReturn(false);
$this->service->expects(self::once())
->method('buildBodyData')
->with($newVevent, null)
@ -954,6 +1060,10 @@ class IMipPluginTest extends TestCase {
->method('isRoomOrResource')
->with($atnd)
->willReturn(false);
$this->service->expects(self::once())
->method('isCircle')
->with($atnd)
->willReturn(false);
$this->service->expects(self::once())
->method('buildBodyData')
->with($newVevent, null)