2021-01-21 19:20:35 +00:00
|
|
|
/*
|
|
|
|
Copyright 2021 The Matrix.org Foundation C.I.C.
|
|
|
|
|
|
|
|
Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
you may not use this file except in compliance with the License.
|
|
|
|
You may obtain a copy of the License at
|
|
|
|
|
|
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
|
|
|
Unless required by applicable law or agreed to in writing, software
|
|
|
|
distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
See the License for the specific language governing permissions and
|
|
|
|
limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2021-10-22 22:23:32 +00:00
|
|
|
import { Room } from "matrix-js-sdk/src/models/room";
|
|
|
|
import { logger } from "matrix-js-sdk/src/logger";
|
2022-02-15 21:05:41 +00:00
|
|
|
import { EventType } from "matrix-js-sdk/src/@types/event";
|
2021-10-22 22:23:32 +00:00
|
|
|
|
2022-03-24 21:32:22 +00:00
|
|
|
import { ensureVirtualRoomExists } from "./createRoom";
|
2021-01-21 19:20:35 +00:00
|
|
|
import { MatrixClientPeg } from "./MatrixClientPeg";
|
|
|
|
import DMRoomMap from "./utils/DMRoomMap";
|
2022-08-30 19:13:39 +00:00
|
|
|
import LegacyCallHandler from "./LegacyCallHandler";
|
2022-03-24 21:26:09 +00:00
|
|
|
import { VIRTUAL_ROOM_EVENT_TYPE } from "./call-types";
|
2022-07-25 08:17:40 +00:00
|
|
|
import { findDMForUser } from "./utils/dm/findDMForUser";
|
2021-09-21 15:48:09 +00:00
|
|
|
|
2021-02-12 20:55:54 +00:00
|
|
|
// Functions for mapping virtual users & rooms. Currently the only lookup
|
|
|
|
// is sip virtual: there could be others in the future.
|
2021-01-21 19:20:35 +00:00
|
|
|
|
2021-02-12 20:55:54 +00:00
|
|
|
export default class VoipUserMapper {
|
2021-06-15 15:27:18 +00:00
|
|
|
// We store mappings of virtual -> native room IDs here until the local echo for the
|
|
|
|
// account data arrives.
|
|
|
|
private virtualToNativeRoomIdCache = new Map<string, string>();
|
2021-02-17 19:00:21 +00:00
|
|
|
|
2021-02-12 20:55:54 +00:00
|
|
|
public static sharedInstance(): VoipUserMapper {
|
|
|
|
if (window.mxVoipUserMapper === undefined) window.mxVoipUserMapper = new VoipUserMapper();
|
|
|
|
return window.mxVoipUserMapper;
|
|
|
|
}
|
2021-01-21 19:20:35 +00:00
|
|
|
|
2023-02-10 18:11:57 +00:00
|
|
|
private async userToVirtualUser(userId: string): Promise<string | null> {
|
2022-08-30 19:13:39 +00:00
|
|
|
const results = await LegacyCallHandler.instance.sipVirtualLookup(userId);
|
2021-06-02 16:39:13 +00:00
|
|
|
if (results.length === 0 || !results[0].fields.lookup_success) return null;
|
2021-02-12 20:55:54 +00:00
|
|
|
return results[0].userid;
|
|
|
|
}
|
2021-01-21 19:20:35 +00:00
|
|
|
|
2022-02-25 15:58:13 +00:00
|
|
|
private async getVirtualUserForRoom(roomId: string): Promise<string | null> {
|
2021-02-12 20:55:54 +00:00
|
|
|
const userId = DMRoomMap.shared().getUserIdForRoomId(roomId);
|
|
|
|
if (!userId) return null;
|
|
|
|
|
|
|
|
const virtualUser = await this.userToVirtualUser(userId);
|
|
|
|
if (!virtualUser) return null;
|
|
|
|
|
2022-02-25 15:58:13 +00:00
|
|
|
return virtualUser;
|
|
|
|
}
|
|
|
|
|
|
|
|
public async getOrCreateVirtualRoomForRoom(roomId: string): Promise<string | null> {
|
|
|
|
const virtualUser = await this.getVirtualUserForRoom(roomId);
|
|
|
|
if (!virtualUser) return null;
|
|
|
|
|
2021-02-17 18:51:21 +00:00
|
|
|
const virtualRoomId = await ensureVirtualRoomExists(MatrixClientPeg.get(), virtualUser, roomId);
|
2023-02-10 18:11:57 +00:00
|
|
|
MatrixClientPeg.get().setRoomAccountData(virtualRoomId!, VIRTUAL_ROOM_EVENT_TYPE, {
|
2021-02-17 18:51:21 +00:00
|
|
|
native_room: roomId,
|
|
|
|
});
|
|
|
|
|
2023-02-10 18:11:57 +00:00
|
|
|
this.virtualToNativeRoomIdCache.set(virtualRoomId!, roomId);
|
2021-06-15 15:27:18 +00:00
|
|
|
|
2021-02-17 18:51:21 +00:00
|
|
|
return virtualRoomId;
|
2021-02-12 20:55:54 +00:00
|
|
|
}
|
2021-01-21 19:20:35 +00:00
|
|
|
|
2022-02-25 15:58:13 +00:00
|
|
|
/**
|
|
|
|
* Gets the ID of the virtual room for a room, or null if the room has no
|
|
|
|
* virtual room
|
|
|
|
*/
|
2023-02-13 17:01:43 +00:00
|
|
|
public async getVirtualRoomForRoom(roomId: string): Promise<Room | undefined> {
|
2022-02-25 15:58:13 +00:00
|
|
|
const virtualUser = await this.getVirtualUserForRoom(roomId);
|
2023-02-13 17:01:43 +00:00
|
|
|
if (!virtualUser) return undefined;
|
2022-02-25 15:58:13 +00:00
|
|
|
|
|
|
|
return findDMForUser(MatrixClientPeg.get(), virtualUser);
|
|
|
|
}
|
|
|
|
|
2022-12-08 21:37:25 +00:00
|
|
|
public nativeRoomForVirtualRoom(roomId: string): string | null {
|
2021-06-15 15:27:18 +00:00
|
|
|
const cachedNativeRoomId = this.virtualToNativeRoomIdCache.get(roomId);
|
|
|
|
if (cachedNativeRoomId) {
|
2021-09-21 15:48:09 +00:00
|
|
|
logger.log(
|
2021-06-15 15:27:18 +00:00
|
|
|
"Returning native room ID " + cachedNativeRoomId + " for virtual room ID " + roomId + " from cache",
|
|
|
|
);
|
|
|
|
return cachedNativeRoomId;
|
|
|
|
}
|
|
|
|
|
2021-02-12 20:55:54 +00:00
|
|
|
const virtualRoom = MatrixClientPeg.get().getRoom(roomId);
|
|
|
|
if (!virtualRoom) return null;
|
|
|
|
const virtualRoomEvent = virtualRoom.getAccountData(VIRTUAL_ROOM_EVENT_TYPE);
|
|
|
|
if (!virtualRoomEvent || !virtualRoomEvent.getContent()) return null;
|
2021-04-19 19:30:51 +00:00
|
|
|
const nativeRoomID = virtualRoomEvent.getContent()["native_room"];
|
|
|
|
const nativeRoom = MatrixClientPeg.get().getRoom(nativeRoomID);
|
|
|
|
if (!nativeRoom || nativeRoom.getMyMembership() !== "join") return null;
|
|
|
|
|
|
|
|
return nativeRoomID;
|
2021-02-12 20:55:54 +00:00
|
|
|
}
|
2021-01-21 19:20:35 +00:00
|
|
|
|
2021-03-09 14:03:58 +00:00
|
|
|
public isVirtualRoom(room: Room): boolean {
|
2021-02-17 18:51:21 +00:00
|
|
|
if (this.nativeRoomForVirtualRoom(room.roomId)) return true;
|
|
|
|
|
2021-06-15 15:27:18 +00:00
|
|
|
if (this.virtualToNativeRoomIdCache.has(room.roomId)) return true;
|
2021-02-17 19:00:21 +00:00
|
|
|
|
2021-02-17 18:51:21 +00:00
|
|
|
// also look in the create event for the claimed native room ID, which is the only
|
|
|
|
// way we can recognise a virtual room we've created when it first arrives down
|
|
|
|
// our stream. We don't trust this in general though, as it could be faked by an
|
|
|
|
// inviter: our main source of truth is the DM state.
|
2022-02-15 21:05:41 +00:00
|
|
|
const roomCreateEvent = room.currentState.getStateEvents(EventType.RoomCreate, "");
|
2021-02-17 18:51:21 +00:00
|
|
|
if (!roomCreateEvent || !roomCreateEvent.getContent()) return false;
|
|
|
|
// we only look at this for rooms we created (so inviters can't just cause rooms
|
|
|
|
// to be invisible)
|
|
|
|
if (roomCreateEvent.getSender() !== MatrixClientPeg.get().getUserId()) return false;
|
|
|
|
const claimedNativeRoomId = roomCreateEvent.getContent()[VIRTUAL_ROOM_EVENT_TYPE];
|
|
|
|
return Boolean(claimedNativeRoomId);
|
2021-01-21 19:20:35 +00:00
|
|
|
}
|
|
|
|
|
2021-06-02 16:39:13 +00:00
|
|
|
public async onNewInvitedRoom(invitedRoom: Room): Promise<void> {
|
2022-08-30 19:13:39 +00:00
|
|
|
if (!LegacyCallHandler.instance.getSupportsVirtualRooms()) return;
|
2021-03-09 14:03:58 +00:00
|
|
|
|
2021-02-12 20:55:54 +00:00
|
|
|
const inviterId = invitedRoom.getDMInviter();
|
2023-02-10 18:11:57 +00:00
|
|
|
if (!inviterId) {
|
|
|
|
logger.error("Could not find DM inviter for room id: " + invitedRoom.roomId);
|
|
|
|
}
|
|
|
|
|
2021-09-21 15:48:09 +00:00
|
|
|
logger.log(`Checking virtual-ness of room ID ${invitedRoom.roomId}, invited by ${inviterId}`);
|
2023-02-10 18:11:57 +00:00
|
|
|
const result = await LegacyCallHandler.instance.sipNativeLookup(inviterId!);
|
2021-02-12 20:55:54 +00:00
|
|
|
if (result.length === 0) {
|
2021-06-02 16:39:13 +00:00
|
|
|
return;
|
2021-02-12 20:55:54 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (result[0].fields.is_virtual) {
|
|
|
|
const nativeUser = result[0].userid;
|
|
|
|
const nativeRoom = findDMForUser(MatrixClientPeg.get(), nativeUser);
|
|
|
|
if (nativeRoom) {
|
|
|
|
// It's a virtual room with a matching native room, so set the room account data. This
|
|
|
|
// will make sure we know where how to map calls and also allow us know not to display
|
|
|
|
// it in the future.
|
|
|
|
MatrixClientPeg.get().setRoomAccountData(invitedRoom.roomId, VIRTUAL_ROOM_EVENT_TYPE, {
|
|
|
|
native_room: nativeRoom.roomId,
|
|
|
|
});
|
|
|
|
// also auto-join the virtual room if we have a matching native room
|
|
|
|
// (possibly we should only join if we've also joined the native room, then we'd also have
|
|
|
|
// to make sure we joined virtual rooms on joining a native one)
|
|
|
|
MatrixClientPeg.get().joinRoom(invitedRoom.roomId);
|
|
|
|
|
2023-02-13 17:01:43 +00:00
|
|
|
// also put this room in the virtual room ID cache so isVirtualRoom return the right answer
|
|
|
|
// in however long it takes for the echo of setAccountData to come down the sync
|
|
|
|
this.virtualToNativeRoomIdCache.set(invitedRoom.roomId, nativeRoom.roomId);
|
|
|
|
}
|
2021-02-12 20:55:54 +00:00
|
|
|
}
|
|
|
|
}
|
2021-01-21 19:20:35 +00:00
|
|
|
}
|