diff --git a/appinfo/info.xml b/appinfo/info.xml index fdc4c96394f..9e298e4fb73 100644 --- a/appinfo/info.xml +++ b/appinfo/info.xml @@ -18,7 +18,7 @@ * 🌉 **Sync with other chat solutions** With [Matterbridge](https://github.com/42wim/matterbridge/) being integrated in Talk, you can easily sync a lot of other chat solutions to Nextcloud Talk and vice-versa. ]]> - 21.0.0-dev.1 + 21.0.0-dev.2 agpl Daniel Calviño Sánchez @@ -64,6 +64,7 @@ OCA\Talk\BackgroundJob\CheckTurnCertificate OCA\Talk\BackgroundJob\ExpireChatMessages OCA\Talk\BackgroundJob\ExpireSignalingMessage + OCA\Talk\BackgroundJob\MaximumCallDuration OCA\Talk\BackgroundJob\Reminder OCA\Talk\BackgroundJob\RemoveEmptyRooms OCA\Talk\BackgroundJob\ResetAssignedSignalingServer diff --git a/docs/settings.md b/docs/settings.md index 35b068585c7..3fd34e61474 100644 --- a/docs/settings.md +++ b/docs/settings.md @@ -82,6 +82,7 @@ Legend: | `bridge_bot_password` | string | | No | | Automatically generated password of the matterbridge bot user profile | | `default_attachment_folder` | string | `/Talk` | No | | Specify default attachment folder location | | `start_calls` | int | `0` | Yes | 🖌️ | Who can start a call, see [constants list](constants.md#start-call) | +| `max_call_duration` | int | `0` | No | ️ | Maximum duration of a call in seconds, 0 for unlimited | | `max-gif-size` | int | `3145728` | No | | Maximum file size for clients to render gifs previews with animation | | `session-ping-limit` | int | `200` | No | | Number of sessions the HPB can ping in a single request | | `token_entropy` | int | `8` | No | | Length of conversation tokens, can be increased to make tokens harder to guess but reduces readability and dial-in comfort | diff --git a/lib/BackgroundJob/MaximumCallDuration.php b/lib/BackgroundJob/MaximumCallDuration.php new file mode 100644 index 00000000000..7444e691230 --- /dev/null +++ b/lib/BackgroundJob/MaximumCallDuration.php @@ -0,0 +1,57 @@ +setInterval(1); + } + + protected function run($argument): void { + $maxCallDuration = $this->appConfig->getAppValueInt('max_call_duration'); + if ($maxCallDuration <= 0) { + return; + } + + $now = $this->time->getDateTime(); + $maxActiveSince = $now->sub(new \DateInterval('PT' . $maxCallDuration . 'S')); + $rooms = $this->manager->getRoomsLongerActiveSince($maxActiveSince); + + foreach ($rooms as $room) { + if ($room->isFederatedConversation()) { + continue; + } + + $result = $this->roomService->resetActiveSinceInDatabaseOnly($room); + if (!$result) { + // Someone else won the race condition, make sure this user disconnects directly and then return + continue; + } + + $this->participantService->endCallForEveryone($room, null); + $this->roomService->resetActiveSinceInModelOnly($room); + } + } +} diff --git a/lib/Manager.php b/lib/Manager.php index 14932633c03..0aea79bb11a 100644 --- a/lib/Manager.php +++ b/lib/Manager.php @@ -294,6 +294,28 @@ public function searchRoomsByToken(string $searchToken = '', ?int $limit = null, return $rooms; } + /** + * @return Room[] + */ + public function getRoomsLongerActiveSince(\DateTime $maxActiveSince): array { + $query = $this->db->getQueryBuilder(); + $helper = new SelectHelper(); + $helper->selectRoomsTable($query); + $query->from('talk_rooms', 'r') + ->where($query->expr()->isNotNull('r.active_since')) + ->andWhere($query->expr()->lte('r.active_since', $query->createNamedParameter($maxActiveSince, IQueryBuilder::PARAM_DATE))) + ->orderBy('r.id', 'ASC'); + $result = $query->executeQuery(); + + $rooms = []; + while ($row = $result->fetch()) { + $rooms[] = $this->createRoomObject($row); + } + $result->closeCursor(); + + return $rooms; + } + /** * @param string $userId * @param array $sessionIds A list of talk sessions to consider for loading (otherwise no session is loaded) diff --git a/lib/Service/ParticipantService.php b/lib/Service/ParticipantService.php index 16e9db52ad4..68a4919c0f9 100644 --- a/lib/Service/ParticipantService.php +++ b/lib/Service/ParticipantService.php @@ -1159,7 +1159,7 @@ public function cleanGuestParticipants(Room $room): void { $this->resetCallStateWhenNeeded($room); } - public function endCallForEveryone(Room $room, Participant $moderator): void { + public function endCallForEveryone(Room $room, ?Participant $moderator): void { $oldActiveSince = $room->getActiveSince(); $event = new BeforeCallEndedForEveryoneEvent($room, $moderator, $oldActiveSince); $this->dispatcher->dispatchTyped($event);