summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGary Kim <gary@garykim.dev>2021-06-10 23:35:54 -0400
committerGary Kim <gary@garykim.dev>2021-07-15 13:54:35 -0400
commita35b98f8c15e19bde6098081b41e544e95e4b532 (patch)
treea9af9784cd16ae1c4321c8c29e1ab8996db57196
parentddda9be34bd14f8ccf14521a6e100870493cd4b3 (diff)
Implement CloudFederationProvider for Talk
Signed-off-by: Gary Kim <gary@garykim.dev>
-rw-r--r--appinfo/info.xml2
-rw-r--r--appinfo/routes.php21
-rw-r--r--lib/BackgroundJob/RemoveEmptyRooms.php6
-rw-r--r--lib/Controller/FederationController.php87
-rw-r--r--lib/Federation/CloudFederationProviderTalk.php262
-rw-r--r--lib/Federation/FederationManager.php156
-rw-r--r--lib/Manager.php44
-rw-r--r--lib/Migration/Version13000Date20210625232111.php88
-rw-r--r--lib/Model/Attendee.php10
-rw-r--r--lib/Model/AttendeeMapper.php24
-rw-r--r--lib/Model/Invitation.php66
-rw-r--r--lib/Model/InvitationMapper.php105
-rw-r--r--lib/Model/SelectHelper.php2
-rw-r--r--lib/Notification/Notifier.php50
-rw-r--r--lib/Room.php12
-rw-r--r--lib/Service/ParticipantService.php3
-rw-r--r--tests/php/Notification/NotifierTest.php7
-rw-r--r--tests/php/RoomTest.php1
18 files changed, 937 insertions, 9 deletions
diff --git a/appinfo/info.xml b/appinfo/info.xml
index fcd28db7a..52561d744 100644
--- a/appinfo/info.xml
+++ b/appinfo/info.xml
@@ -16,7 +16,7 @@ And in the works for the [coming versions](https://github.com/nextcloud/spreed/m
]]></description>
- <version>13.0.0-dev</version>
+ <version>13.0.0-dev.1</version>
<licence>agpl</licence>
<author>Daniel Calviño Sánchez</author>
diff --git a/appinfo/routes.php b/appinfo/routes.php
index 26ec3ccbd..d06380a6c 100644
--- a/appinfo/routes.php
+++ b/appinfo/routes.php
@@ -524,6 +524,27 @@ return [
],
/**
+ * Federation
+ */
+
+ [
+ 'name' => 'Federation#acceptShare',
+ 'url' => 'api/{apiVersion}/federation/pending/{id}',
+ 'verb' => 'POST',
+ 'requirements' => [
+ 'apiVersion' => 'v1',
+ ],
+ ],
+ [
+ 'name' => 'Federation#rejectShare',
+ 'url' => 'api/{apiVersion}/federation/pending/{id}',
+ 'verb' => 'DELETE',
+ 'requirements' => [
+ 'apiVersion' => 'v1',
+ ],
+ ],
+
+ /**
* PublicShareAuth
*/
[
diff --git a/lib/BackgroundJob/RemoveEmptyRooms.php b/lib/BackgroundJob/RemoveEmptyRooms.php
index af9738809..3c620c171 100644
--- a/lib/BackgroundJob/RemoveEmptyRooms.php
+++ b/lib/BackgroundJob/RemoveEmptyRooms.php
@@ -23,6 +23,7 @@ declare(strict_types=1);
namespace OCA\Talk\BackgroundJob;
+use OCA\Talk\Federation\FederationManager;
use OCA\Talk\Service\ParticipantService;
use OCP\AppFramework\Utility\ITimeFactory;
use OCP\BackgroundJob\TimedJob;
@@ -46,6 +47,9 @@ class RemoveEmptyRooms extends TimedJob {
/** @var LoggerInterface */
protected $logger;
+ /** @var FederationManager */
+ protected $federationManager;
+
protected $numDeletedRooms = 0;
public function __construct(ITimeFactory $timeFactory,
@@ -77,7 +81,7 @@ class RemoveEmptyRooms extends TimedJob {
return;
}
- if ($this->participantService->getNumberOfActors($room) === 0 && $room->getObjectType() !== 'file') {
+ if ($this->participantService->getNumberOfActors($room) === 0 && $room->getObjectType() !== 'file' && $this->federationManager->getNumberOfInvitations($room) === 0) {
$room->deleteRoom();
$this->numDeletedRooms++;
}
diff --git a/lib/Controller/FederationController.php b/lib/Controller/FederationController.php
new file mode 100644
index 000000000..ea951d593
--- /dev/null
+++ b/lib/Controller/FederationController.php
@@ -0,0 +1,87 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2021, Gary Kim <gary@garykim.dev>
+ *
+ * @author Gary Kim <gary@garykim.dev>
+ *
+ * @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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Talk\Controller;
+
+use OCA\Talk\AppInfo\Application;
+use OCA\Talk\Exceptions\UnauthorizedException;
+use OCA\Talk\Federation\FederationManager;
+use OCP\AppFramework\Db\MultipleObjectsReturnedException;
+use OCP\AppFramework\Http\DataResponse;
+use OCP\AppFramework\OCSController;
+use OCP\DB\Exception as DBException;
+use OCP\IRequest;
+use OCP\IUser;
+use OCP\IUserSession;
+
+class FederationController extends OCSController {
+ /** @var FederationManager */
+ private $federationManager;
+
+ /** @var IUserSession */
+ private $userSession;
+
+ public function __construct(IRequest $request, FederationManager $federationManager, IUserSession $userSession) {
+ parent::__construct(Application::APP_ID, $request);
+ $this->federationManager = $federationManager;
+ $this->userSession = $userSession;
+ }
+
+ /**
+ * @NoAdminRequired
+ *
+ * @param int $id
+ * @return DataResponse
+ * @throws UnauthorizedException
+ * @throws DBException
+ * @throws MultipleObjectsReturnedException
+ */
+ public function acceptShare(int $id): DataResponse {
+ $user = $this->userSession->getUser();
+ if (!$user instanceof IUser) {
+ throw new UnauthorizedException();
+ }
+ $this->federationManager->acceptRemoteRoomShare($user, $id);
+ return new DataResponse();
+ }
+
+ /**
+ * @NoAdminRequired
+ *
+ * @param int $id
+ * @return DataResponse
+ * @throws UnauthorizedException
+ * @throws DBException
+ * @throws MultipleObjectsReturnedException
+ */
+ public function rejectShare(int $id): DataResponse {
+ $user = $this->userSession->getUser();
+ if (!$user instanceof IUser) {
+ throw new UnauthorizedException();
+ }
+ $this->federationManager->rejectRemoteRoomShare($user, $id);
+ return new DataResponse();
+ }
+}
diff --git a/lib/Federation/CloudFederationProviderTalk.php b/lib/Federation/CloudFederationProviderTalk.php
new file mode 100644
index 000000000..9cf401afd
--- /dev/null
+++ b/lib/Federation/CloudFederationProviderTalk.php
@@ -0,0 +1,262 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2021 Gary Kim <gary@garykim.dev>
+ *
+ * @author Gary Kim <gary@garykim.dev>
+ *
+ * @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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Talk\Federation;
+
+use Exception;
+use OC\AppFramework\Http;
+use OC\HintException;
+use OCA\FederatedFileSharing\AddressHandler;
+use OCA\Talk\AppInfo\Application;
+use OCA\Talk\Manager;
+use OCA\Talk\Model\Attendee;
+use OCA\Talk\Model\AttendeeMapper;
+use OCA\Talk\Participant;
+use OCA\Talk\Service\ParticipantService;
+use OCP\DB\Exception as DBException;
+use OCP\Federation\Exceptions\ActionNotSupportedException;
+use OCP\Federation\Exceptions\AuthenticationFailedException;
+use OCP\Federation\Exceptions\BadRequestException;
+use OCP\Federation\Exceptions\ProviderCouldNotAddShareException;
+use OCP\Federation\ICloudFederationProvider;
+use OCP\Federation\ICloudFederationShare;
+use OCP\IURLGenerator;
+use OCP\IUser;
+use OCP\IUserManager;
+use OCP\Notification\IManager as INotificationManager;
+use OCP\Share\Exceptions\ShareNotFound;
+
+class CloudFederationProviderTalk implements ICloudFederationProvider {
+
+ /** @var IUserManager */
+ private $userManager;
+
+ /** @var AddressHandler */
+ private $addressHandler;
+
+ /** @var FederationManager */
+ private $federationManager;
+
+ /** @var INotificationManager */
+ private $notificationManager;
+
+ /** @var IURLGenerator */
+ private $urlGenerator;
+
+ /** @var ParticipantService */
+ private $participantService;
+
+ /** @var AttendeeMapper */
+ private $attendeeMapper;
+
+ /** @var Manager */
+ private $manager;
+
+ public function __construct(
+ IUserManager $userManager,
+ AddressHandler $addressHandler,
+ FederationManager $federationManager,
+ INotificationManager $notificationManager,
+ IURLGenerator $urlGenerator,
+ ParticipantService $participantService,
+ AttendeeMapper $attendeeMapper,
+ Manager $manager
+ ) {
+ $this->userManager = $userManager;
+ $this->addressHandler = $addressHandler;
+ $this->federationManager = $federationManager;
+ $this->notificationManager = $notificationManager;
+ $this->urlGenerator = $urlGenerator;
+ $this->participantService = $participantService;
+ $this->attendeeMapper = $attendeeMapper;
+ $this->manager = $manager;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getShareType(): string {
+ return 'talk-room';
+ }
+
+ /**
+ * @inheritDoc
+ * @throws HintException
+ * @throws DBException
+ */
+ public function shareReceived(ICloudFederationShare $share): string {
+ if (!$this->federationManager->isEnabled()) {
+ throw new ProviderCouldNotAddShareException('Server does not support talk federation', '', Http::STATUS_SERVICE_UNAVAILABLE);
+ }
+ if ($share->getShareType() !== 'user') {
+ throw new ProviderCouldNotAddShareException('support for sharing with non-users not implemented yet', '', Http::STATUS_NOT_IMPLEMENTED);
+ // TODO: Implement group shares
+ }
+
+ if (!is_numeric($share->getShareType())) {
+ throw new ProviderCouldNotAddShareException('RoomType is not a number', '', Http::STATUS_BAD_REQUEST);
+ }
+
+ $shareSecret = $share->getShareSecret();
+ $shareWith = $share->getShareWith();
+ $roomToken = $share->getProviderId();
+ $roomName = $share->getResourceName();
+ $roomType = (int) $share->getShareType();
+ $sharedBy = $share->getSharedByDisplayName();
+ $sharedByFederatedId = $share->getSharedBy();
+ $owner = $share->getOwnerDisplayName();
+ $ownerFederatedId = $share->getOwner();
+ [, $remote] = $this->addressHandler->splitUserRemote($ownerFederatedId);
+
+ // if no explicit information about the person who created the share was send
+ // we assume that the share comes from the owner
+ if ($sharedByFederatedId === null) {
+ $sharedBy = $owner;
+ $sharedByFederatedId = $ownerFederatedId;
+ }
+
+ if ($remote && $shareSecret && $shareWith && $roomToken && $roomName && $owner) {
+ $shareWith = $this->userManager->get($shareWith);
+ if ($shareWith === null) {
+ throw new ProviderCouldNotAddShareException('User does not exist', '',Http::STATUS_BAD_REQUEST);
+ }
+
+ $shareId = (string) $this->federationManager->addRemoteRoom($shareWith, $roomType, $roomName, $roomToken, $remote, $shareSecret);
+
+ $this->notifyAboutNewShare($shareWith, $shareId, $sharedByFederatedId, $sharedBy, $roomName, $roomToken, $remote);
+ return $shareId;
+ }
+ throw new ProviderCouldNotAddShareException('required request data not found', '', Http::STATUS_BAD_REQUEST);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function notificationReceived($notificationType, $providerId, array $notification): array {
+ if (!is_numeric($providerId)) {
+ throw new BadRequestException(['providerId']);
+ }
+ switch ($notificationType) {
+ case 'SHARE_ACCEPTED':
+ return $this->shareAccepted((int) $providerId, $notification);
+ case 'SHARE_DECLINED':
+ return $this->shareDeclined((int) $providerId, $notification);
+ case 'SHARE_UNSHARED':
+ return []; // TODO: Implement
+ case 'REQUEST_RESHARE':
+ return []; // TODO: Implement
+ case 'RESHARE_UNDO':
+ return []; // TODO: Implement
+ case 'RESHARE_CHANGE_PERMISSION':
+ return []; // TODO: Implement
+ }
+ // TODO: Implement notificationReceived() method.
+ }
+
+ /**
+ * @throws ActionNotSupportedException
+ * @throws ShareNotFound
+ * @throws AuthenticationFailedException
+ */
+ private function shareAccepted(int $id, array $notification): array {
+ $attendee = $this->getAttendeeAndValidate($id, $notification['sharedSecret']);
+
+ // TODO: Add activity for share accepted
+
+ return [];
+ }
+
+ /**
+ * @throws ActionNotSupportedException
+ * @throws ShareNotFound
+ * @throws AuthenticationFailedException
+ */
+ private function shareDeclined(int $id, array $notification): array {
+ $attendee = $this->getAttendeeAndValidate($id, $notification['sharedSecret']);
+
+ $room = $this->manager->getRoomById($attendee->getRoomId());
+ $participant = new Participant($room, $attendee, null);
+ $this->participantService->removeAttendee($room, $participant, 'Left Room');
+ return [];
+ }
+
+ /**
+ * @throws AuthenticationFailedException
+ * @throws ActionNotSupportedException
+ * @throws ShareNotFound
+ */
+ private function getAttendeeAndValidate(int $id, string $sharedSecret): Attendee {
+ if (!$this->federationManager->isEnabled()) {
+ throw new ActionNotSupportedException('Server does not support Talk federation');
+ }
+
+ try {
+ $attendee = $this->attendeeMapper->getById($id);
+ } catch (Exception $ex) {
+ throw new ShareNotFound();
+ }
+ if ($attendee->getActorType() !== Attendee::ACTOR_FEDERATED_REMOTE_USER) {
+ throw new ShareNotFound();
+ }
+ if ($attendee->getAccessToken() !== $sharedSecret) {
+ throw new AuthenticationFailedException();
+ }
+ return $attendee;
+ }
+
+ private function notifyAboutNewShare(IUser $shareWith, string $shareId, string $sharedByFederatedId, string $sharedByName, string $roomName, string $roomToken, string $serverUrl) {
+ $notification = $this->notificationManager->createNotification();
+ $notification->setApp(Application::APP_ID)
+ ->setUser($shareWith->getUID())
+ ->setDateTime(new \DateTime())
+ ->setObject('remote_talk_share', $shareId)
+ ->setSubject('remote_talk_share', [
+ 'sharedByDisplayName' => $sharedByName,
+ 'sharedByFederatedId' => $sharedByFederatedId,
+ 'roomName' => $roomName,
+ 'serverUrl' => $serverUrl,
+ 'roomToken' => $roomToken,
+ ]);
+
+ $declineAction = $notification->createAction();
+ $declineAction->setLabel('decline')
+ ->setLink($this->urlGenerator->linkToOCSRouteAbsolute('spreed.Federation.rejectShare', ['id' => $shareId]), 'DELETE');
+ $notification->addAction($declineAction);
+
+ $acceptAction = $notification->createAction();
+ $acceptAction->setLabel('accept')
+ ->setLink($this->urlGenerator->linkToOCSRouteAbsolute('spreed.Federation.acceptShare', ['id' => $shareId]), 'POST');
+ $notification->addAction($acceptAction);
+
+ $this->notificationManager->notify($notification);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getSupportedShareTypes() {
+ return ['user'];
+ }
+}
diff --git a/lib/Federation/FederationManager.php b/lib/Federation/FederationManager.php
new file mode 100644
index 000000000..58336478c
--- /dev/null
+++ b/lib/Federation/FederationManager.php
@@ -0,0 +1,156 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2021 Gary Kim <gary@garykim.dev>
+ *
+ * @author Gary Kim <gary@garykim.dev>
+ *
+ * @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 <http://www.gnu.org/licenses/>.
+ *
+ */
+
+namespace OCA\Talk\Federation;
+
+use OCA\Talk\Exceptions\RoomNotFoundException;
+use OCA\Talk\Exceptions\UnauthorizedException;
+use OCA\Talk\Manager;
+use OCA\Talk\Model\Attendee;
+use OCA\Talk\Model\Invitation;
+use OCA\Talk\Model\InvitationMapper;
+use OCA\Talk\Room;
+use OCA\Talk\Service\ParticipantService;
+use OCP\AppFramework\Db\MultipleObjectsReturnedException;
+use OCP\DB\Exception as DBException;
+use OCP\IConfig;
+use OCP\IUser;
+
+/**
+ * Class FederationManager
+ *
+ * @package OCA\Talk\Federation
+ *
+ * FederationManager handles incoming federated rooms
+ */
+class FederationManager {
+ /** @var IConfig */
+ private $config;
+
+ /** @var Manager */
+ private $manager;
+
+ /** @var ParticipantService */
+ private $participantService;
+
+ /** @var InvitationMapper */
+ private $invitationMapper;
+
+ public function __construct(
+ IConfig $config,
+ Manager $manager,
+ ParticipantService $participantService,
+ InvitationMapper $invitationMapper
+ ) {
+ $this->config = $config;
+ $this->manager = $manager;
+ $this->participantService = $participantService;
+ $this->invitationMapper = $invitationMapper;
+ }
+
+ /**
+ * Determine if Talk federation is enabled on this instance
+ * @return bool
+ */
+ public function isEnabled(): bool {
+ // TODO: Set to default true once implementation is complete
+ return $this->config->getSystemValueBool('talk_federation_enabled', false);
+ }
+
+ /**
+ * @param IUser $user
+ * @param int $roomType
+ * @param string $roomName
+ * @param string $roomToken
+ * @param string $remoteUrl
+ * @param string $sharedSecret
+ * @return int share id for this specific remote room share
+ * @throws DBException
+ */
+ public function addRemoteRoom(IUser $user, int $roomType, string $roomName, string $roomToken, string $remoteUrl, string $sharedSecret): int {
+ try {
+ $room = $this->manager->getRoomByToken($roomToken, null, $remoteUrl);
+ } catch (RoomNotFoundException $ex) {
+ $room = $this->manager->createRemoteRoom($roomType, $roomName, $roomToken, $remoteUrl);
+ }
+ $invitation = new Invitation();
+ $invitation->setUserId($user->getUID());
+ $invitation->setRoomId($room->getId());
+ $invitation->setAccessToken($sharedSecret);
+ $invitation = $this->invitationMapper->insert($invitation);
+
+ return $invitation->getId();
+ }
+
+ /**
+ * @throws DBException
+ * @throws UnauthorizedException
+ * @throws MultipleObjectsReturnedException
+ */
+ public function acceptRemoteRoomShare(IUser $user, int $shareId) {
+ $invitation = $this->invitationMapper->getInvitationById($shareId);
+ if ($invitation->getUserId() !== $user->getUID()) {
+ throw new UnauthorizedException('invitation is for a different user');
+ }
+
+ // Add user to the room
+ $room = $this->manager->getRoomById($invitation->getRoomId());
+ $participant = [
+ [
+ 'actorType' => Attendee::ACTOR_USERS,
+ 'actorId' => $user->getUID(),
+ 'displayName' => $user->getDisplayName(),
+ 'accessToken' => $invitation->getAccessToken(),
+ ]
+ ];
+ $this->participantService->addUsers($room, $participant);
+
+ $this->invitationMapper->delete($invitation);
+
+ // TODO: Send SHARE_ACCEPTED notification
+ }
+
+ /**
+ * @throws DBException
+ * @throws UnauthorizedException
+ * @throws MultipleObjectsReturnedException
+ */
+ public function rejectRemoteRoomShare(IUser $user, int $shareId) {
+ $invitation = $this->invitationMapper->getInvitationById($shareId);
+ if ($invitation->getUserId() !== $user->getUID()) {
+ throw new UnauthorizedException('invitation is for a different user');
+ }
+ $this->invitationMapper->delete($invitation);
+
+ // TODO: Send SHARE_DECLINED notification
+ }
+
+ /**
+ * @throws DBException
+ */
+ public function getNumberOfInvitations(Room $room): int {
+ return $this->invitationMapper->countInvitationsForRoom($room);
+ }
+}
diff --git a/lib/Manager.php b/lib/Manager.php
index aeb52b200..044c6d3e3 100644
--- a/lib/Manager.php
+++ b/lib/Manager.php
@@ -37,6 +37,7 @@ use OCP\AppFramework\Utility\ITimeFactory;
use OCP\Comments\IComment;
use OCP\Comments\ICommentsManager;
use OCP\Comments\NotFoundException;
+use OCP\DB\Exception as DBException;
use OCP\DB\QueryBuilder\IQueryBuilder;
use OCP\EventDispatcher\IEventDispatcher;
use OCP\ICache;
@@ -185,6 +186,7 @@ class Manager {
(string) $row['name'],
(string) $row['description'],
(string) $row['password'],
+ (string) $row['server_url'],
(int) $row['active_guests'],
(int) $row['call_flag'],
$activeSince,
@@ -628,7 +630,7 @@ class Manager {
* @return Room
* @throws RoomNotFoundException
*/
- public function getRoomByActor(string $token, string $actorType, string $actorId, ?string $sessionId = null): Room {
+ public function getRoomByActor(string $token, string $actorType, string $actorId, ?string $sessionId = null, ?string $server_url = null): Room {
$query = $this->db->getQueryBuilder();
$helper = new SelectHelper();
$helper->selectRoomsTable($query);
@@ -641,6 +643,12 @@ class Manager {
))
->where($query->expr()->eq('r.token', $query->createNamedParameter($token)));
+ if ($server_url === null) {
+ $query->andWhere($query->expr()->isNull('r.server_url'));
+ } else {
+ $query->andWhere($query->expr()->eq('r.server_url', $query->createNamedParameter($server_url)));
+ }
+
if ($sessionId !== null) {
$helper->selectSessionsTable($query);
$query->leftJoin('a', 'talk_sessions', 's', $query->expr()->andX(
@@ -676,10 +684,10 @@ class Manager {
* @return Room
* @throws RoomNotFoundException
*/
- public function getRoomByToken(string $token, ?string $preloadUserId = null): Room {
+ public function getRoomByToken(string $token, ?string $preloadUserId = null, ?string $serverUrl = null): Room {
$preloadUserId = $preloadUserId === '' ? null : $preloadUserId;
if ($preloadUserId !== null) {
- return $this->getRoomByActor($token, Attendee::ACTOR_USERS, $preloadUserId);
+ return $this->getRoomByActor($token, Attendee::ACTOR_USERS, $preloadUserId, null, $serverUrl);
}
$query = $this->db->getQueryBuilder();
@@ -688,6 +696,13 @@ class Manager {
$query->from('talk_rooms', 'r')
->where($query->expr()->eq('r.token', $query->createNamedParameter($token)));
+ if ($serverUrl === null) {
+ $query->andWhere($query->expr()->isNull('r.server_url'));
+ } else {
+ $query->andWhere($query->expr()->eq('r.server_url', $query->createNamedParameter($serverUrl)));
+ }
+
+
$result = $query->execute();
$row = $result->fetch();
$result->closeCursor();
@@ -908,6 +923,29 @@ class Manager {
return $room;
}
+ /**
+ * @param int $type
+ * @param string $name
+ * @return Room
+ * @throws DBException
+ */
+ public function createRemoteRoom(int $type, string $name, string $token, string $serverUrl): Room {
+ $qb = $this->db->getQueryBuilder();
+
+ $qb->insert('talk_rooms')
+ ->values([
+ 'name' => $qb->createNamedParameter($name),
+ 'type' => $qb->createNamedParameter($type, IQueryBuilder::PARAM_INT),
+ 'token' => $qb->createNamedParameter($token),
+ 'server_url' => $qb->createNamedParameter($serverUrl),
+ ]);
+
+ $qb->executeStatement();
+ $roomId = $qb->getLastInsertId();
+
+ return $this->getRoomById($roomId);
+ }
+
public function resolveRoomDisplayName(Room $room, string $userId): string {
if ($room->getObjectType() === 'share:password') {
return $this->l->t('Password request: %s', [$room->getName()]);
diff --git a/lib/Migration/Version13000Date20210625232111.php b/lib/Migration/Version13000Date20210625232111.php
new file mode 100644
index 000000000..437a785d7
--- /dev/null
+++ b/lib/Migration/Version13000Date20210625232111.php
@@ -0,0 +1,88 @@
+<?php
+
+declare(strict_types=1);
+/**
+ * @copyright Copyright (c) 2021 Gary Kim <gary@garykim.dev>
+ *
+ * @author Gary Kim <gary@garykim.dev>
+ *
+ * @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