From 863bf67f82fdb72f8e1e5a9df09b756fb966c8dd 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 6dc318ccc8a..760079a7ff2 100644 --- a/lib/Service/ParticipantService.php +++ b/lib/Service/ParticipantService.php @@ -683,17 +683,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 b757fa1b3a5..4cb6d4c2cde 100644 --- a/tests/php/Signaling/BackendNotifierTest.php +++ b/tests/php/Signaling/BackendNotifierTest.php @@ -707,6 +707,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 b822f8c8c63b0b0b01ffc2a12a085f1b6a48807d 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 8538aac97e2..581d980fa8a 100644 --- a/lib/Controller/RoomController.php +++ b/lib/Controller/RoomController.php @@ -1401,7 +1401,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 760079a7ff2..bf45ae742c1 100644 --- a/lib/Service/ParticipantService.php +++ b/lib/Service/ParticipantService.php @@ -30,6 +30,7 @@ use OCA\Talk\Events\AddParticipantsEvent; use OCA\Talk\Events\AttendeesAddedEvent; use OCA\Talk\Events\AttendeesRemovedEvent; +use OCA\Talk\Events\DuplicatedParticipantEvent; use OCA\Talk\Events\EndCallForEveryoneEvent; use OCA\Talk\Events\JoinRoomGuestEvent; use OCA\Talk\Events\JoinRoomUserEvent; @@ -677,8 +678,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 dc47a382937..d0396c922e9 100644 --- a/lib/Signaling/Listener.php +++ b/lib/Signaling/Listener.php @@ -28,6 +28,7 @@ use OCA\Talk\Events\AddParticipantsEvent; use OCA\Talk\Events\ChatEvent; use OCA\Talk\Events\ChatParticipantEvent; +use OCA\Talk\Events\DuplicatedParticipantEvent; use OCA\Talk\Events\EndCallForEveryoneEvent; use OCA\Talk\Events\ModifyEveryoneEvent; use OCA\Talk\Events\ModifyParticipantEvent; @@ -254,9 +255,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 4cb6d4c2cde..c575c114525 100644 --- a/tests/php/Signaling/BackendNotifierTest.php +++ b/tests/php/Signaling/BackendNotifierTest.php @@ -334,6 +334,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);