From 9ac1d3192edc6add1ca948a313b11f298c9a2117 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Thu, 25 Aug 2022 11:41:40 +0200 Subject: [PATCH 1/2] Fix call left event not sent when leaving conversation while in a call MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When a call is left a message is sent to the external signaling server. However, that message is based on the sessions of the participants in the conversation. When a conversation is left and the participant was in a call in that conversation the call is also left, but the event was emitted after leaving the conversation. Due to that, when the message to be sent to the external signaling server was generated the participant was no longer in the conversation, and therefore it was not included in the message, so the call was not left from the point of view of the external signaling server. To solve that now the call is left and, once that is done, the conversation is left. Signed-off-by: Daniel Calviño Sánchez --- lib/Service/ParticipantService.php | 11 ++---- tests/php/Signaling/BackendNotifierTest.php | 39 +++++++++++++++++++++ 2 files changed, 42 insertions(+), 8 deletions(-) diff --git a/lib/Service/ParticipantService.php b/lib/Service/ParticipantService.php index 7d7582315cc..21f7415d6aa 100644 --- a/lib/Service/ParticipantService.php +++ b/lib/Service/ParticipantService.php @@ -693,17 +693,12 @@ public function leaveRoomAsSession(Room $room, Participant $participant): void { $session = $participant->getSession(); if ($session instanceof Session) { - $dispatchLeaveCallEvents = $session->getInCall() !== Participant::FLAG_DISCONNECTED; - if ($dispatchLeaveCallEvents) { - $event = new ModifyParticipantEvent($room, $participant, 'inCall', Participant::FLAG_DISCONNECTED, $session->getInCall()); - $this->dispatcher->dispatch(Room::EVENT_BEFORE_SESSION_LEAVE_CALL, $event); + $isInCall = $session->getInCall() !== Participant::FLAG_DISCONNECTED; + if ($isInCall) { + $this->changeInCall($room, $participant, Participant::FLAG_DISCONNECTED); } $this->sessionMapper->delete($session); - - if ($dispatchLeaveCallEvents) { - $this->dispatcher->dispatch(Room::EVENT_AFTER_SESSION_LEAVE_CALL, $event); - } } else { $this->sessionMapper->deleteByAttendeeId($participant->getAttendee()->getId()); } diff --git a/tests/php/Signaling/BackendNotifierTest.php b/tests/php/Signaling/BackendNotifierTest.php index 3bb2fc38661..31abad437b7 100644 --- a/tests/php/Signaling/BackendNotifierTest.php +++ b/tests/php/Signaling/BackendNotifierTest.php @@ -697,6 +697,45 @@ public function testRoomInCallChanged() { ]); } + public function testRoomInCallChangedWhenLeavingConversationWhileInCall() { + /** @var IUser|MockObject $testUser */ + $testUser = $this->createMock(IUser::class); + $testUser->expects($this->any()) + ->method('getUID') + ->willReturn($this->userId); + + $room = $this->manager->createRoom(Room::TYPE_PUBLIC); + $this->participantService->addUsers($room, [[ + 'actorType' => 'users', + 'actorId' => $this->userId, + ]]); + $participant = $this->participantService->joinRoom($room, $testUser, ''); + $userSession = $participant->getSession()->getSessionId(); + $this->participantService->changeInCall($room, $participant, Participant::FLAG_IN_CALL | Participant::FLAG_WITH_AUDIO | Participant::FLAG_WITH_VIDEO); + $this->controller->clearRequests(); + $this->participantService->leaveRoomAsSession($room, $participant); + + $this->assertMessageWasSent($room, [ + 'type' => 'incall', + 'incall' => [ + 'incall' => 0, + 'changed' => [ + [ + 'inCall' => 0, + 'lastPing' => 0, + 'sessionId' => $userSession, + 'nextcloudSessionId' => $userSession, + 'participantType' => Participant::USER, + 'participantPermissions' => (Attendee::PERMISSIONS_MAX_DEFAULT ^ Attendee::PERMISSIONS_LOBBY_IGNORE), + 'userId' => $this->userId, + ], + ], + 'users' => [ + ], + ], + ]); + } + public function testRoomPropertiesEvent(): void { $listener = static function (SignalingRoomPropertiesEvent $event) { $room = $event->getRoom(); From 48443886b30f55a598eca1980ccbdb0da088a1d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Daniel=20Calvi=C3=B1o=20S=C3=A1nchez?= Date: Thu, 25 Aug 2022 14:33:12 +0200 Subject: [PATCH 2/2] Fix not leaving previous session when joining the same room again MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When the same room is joined again from the same PHP session the room is left with the previous Nextcloud session before joining with the new one. However, "disinvite" messages were not sent to the external signaling server for regular users, so their UI was not updated to show that the previous Nextcloud session was kicked out from the conversation. Note, however, that as a "disinvite" message is now sent the UI will show a "conversation not found" message rather than a "duplicated session" state. Signed-off-by: Daniel Calviño Sánchez --- lib/Controller/RoomController.php | 2 +- lib/Events/DuplicatedParticipantEvent.php | 34 ++++++++++++++++++ lib/Service/ParticipantService.php | 9 +++-- lib/Signaling/Listener.php | 9 +++-- tests/php/Signaling/BackendNotifierTest.php | 40 +++++++++++++++++++++ 5 files changed, 89 insertions(+), 5 deletions(-) create mode 100644 lib/Events/DuplicatedParticipantEvent.php diff --git a/lib/Controller/RoomController.php b/lib/Controller/RoomController.php index 5a67f99040c..ecaa7aeb038 100644 --- a/lib/Controller/RoomController.php +++ b/lib/Controller/RoomController.php @@ -1379,7 +1379,7 @@ public function joinRoom(string $token, string $password = '', bool $force = tru $this->participantService->changeInCall($room, $previousParticipant, Participant::FLAG_DISCONNECTED); } - $this->participantService->leaveRoomAsSession($room, $previousParticipant); + $this->participantService->leaveRoomAsSession($room, $previousParticipant, true); } $user = $this->userManager->get($this->userId); diff --git a/lib/Events/DuplicatedParticipantEvent.php b/lib/Events/DuplicatedParticipantEvent.php new file mode 100644 index 00000000000..8b7f754b4e0 --- /dev/null +++ b/lib/Events/DuplicatedParticipantEvent.php @@ -0,0 +1,34 @@ + + * + * @license GNU AGPL version 3 or any later version + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + * + */ + +namespace OCA\Talk\Events; + +use OCA\Talk\Participant; +use OCA\Talk\Room; + +class DuplicatedParticipantEvent extends ParticipantEvent { + public function __construct(Room $room, + Participant $participant) { + parent::__construct($room, $participant); + } +} diff --git a/lib/Service/ParticipantService.php b/lib/Service/ParticipantService.php index 21f7415d6aa..cbd692e3557 100644 --- a/lib/Service/ParticipantService.php +++ b/lib/Service/ParticipantService.php @@ -33,6 +33,7 @@ use OCA\Talk\Events\AttendeesAddedEvent; use OCA\Talk\Events\AttendeesRemovedEvent; use OCA\Talk\Events\ChatEvent; +use OCA\Talk\Events\DuplicatedParticipantEvent; use OCA\Talk\Events\EndCallForEveryoneEvent; use OCA\Talk\Events\JoinRoomGuestEvent; use OCA\Talk\Events\JoinRoomUserEvent; @@ -687,8 +688,12 @@ public function ensureOneToOneRoomIsFilled(Room $room): void { } } - public function leaveRoomAsSession(Room $room, Participant $participant): void { - $event = new ParticipantEvent($room, $participant); + public function leaveRoomAsSession(Room $room, Participant $participant, bool $duplicatedParticipant = false): void { + if ($duplicatedParticipant) { + $event = new DuplicatedParticipantEvent($room, $participant); + } else { + $event = new ParticipantEvent($room, $participant); + } $this->dispatcher->dispatch(Room::EVENT_BEFORE_ROOM_DISCONNECT, $event); $session = $participant->getSession(); diff --git a/lib/Signaling/Listener.php b/lib/Signaling/Listener.php index 338eb48c0bb..6155f5fdc85 100644 --- a/lib/Signaling/Listener.php +++ b/lib/Signaling/Listener.php @@ -27,6 +27,7 @@ use OCA\Talk\Config; use OCA\Talk\Events\AddParticipantsEvent; use OCA\Talk\Events\ChatEvent; +use OCA\Talk\Events\DuplicatedParticipantEvent; use OCA\Talk\Events\EndCallForEveryoneEvent; use OCA\Talk\Events\ModifyEveryoneEvent; use OCA\Talk\Events\ModifyParticipantEvent; @@ -252,9 +253,13 @@ protected static function registerExternalSignaling(IEventDispatcher $dispatcher $sessionIds = []; if ($event->getParticipant()->getSession()) { - // Only for guests and self-joined users disconnecting is "leaving" and therefor should trigger a disinvite + // If a previous duplicated session is being removed it must be + // notified to the external signaling server. Otherwise only for + // guests and self-joined users disconnecting is "leaving" and + // therefor should trigger a disinvite. $attendeeParticipantType = $event->getParticipant()->getAttendee()->getParticipantType(); - if ($attendeeParticipantType === Participant::GUEST + if ($event instanceof DuplicatedParticipantEvent + || $attendeeParticipantType === Participant::GUEST || $attendeeParticipantType === Participant::GUEST_MODERATOR) { $sessionIds[] = $event->getParticipant()->getSession()->getSessionId(); $notifier->roomSessionsRemoved($event->getRoom(), $sessionIds); diff --git a/tests/php/Signaling/BackendNotifierTest.php b/tests/php/Signaling/BackendNotifierTest.php index 31abad437b7..fd2be706b1d 100644 --- a/tests/php/Signaling/BackendNotifierTest.php +++ b/tests/php/Signaling/BackendNotifierTest.php @@ -324,6 +324,46 @@ public function testNoRoomDisinviteOnLeaveOfNormalUser() { $this->assertNoMessageOfTypeWasSent($room, 'disinvite'); } + public function testRoomDisinviteOnLeaveOfNormalUserWithDuplicatedSession() { + /** @var IUser|MockObject $testUser */ + $testUser = $this->createMock(IUser::class); + $testUser->expects($this->any()) + ->method('getUID') + ->willReturn($this->userId); + + $room = $this->manager->createRoom(Room::TYPE_PUBLIC); + $this->participantService->addUsers($room, [[ + 'actorType' => 'users', + 'actorId' => $this->userId, + ]]); + $participant = $this->participantService->joinRoom($room, $testUser, ''); + $this->controller->clearRequests(); + $this->participantService->leaveRoomAsSession($room, $participant, true); + + $this->assertMessageWasSent($room, [ + 'type' => 'disinvite', + 'disinvite' => [ + 'sessionids' => [ + $participant->getSession()->getSessionId(), + ], + 'alluserids' => [ + $this->userId, + ], + 'properties' => [ + 'name' => $room->getDisplayName(''), + 'type' => $room->getType(), + 'lobby-state' => Webinary::LOBBY_NONE, + 'lobby-timer' => null, + 'read-only' => Room::READ_WRITE, + 'listable' => Room::LISTABLE_NONE, + 'active-since' => null, + 'sip-enabled' => 0, + 'participant-list' => 'refresh', + ], + ], + ]); + } + public function testRoomDisinviteOnLeaveOfSelfJoinedUser() { /** @var IUser|MockObject $testUser */ $testUser = $this->createMock(IUser::class);