Source

e2ee/RoomTracker.ts

  1. import { MatrixClient } from "../MatrixClient";
  2. import { EncryptionEventContent } from "../models/events/EncryptionEvent";
  3. import { ICryptoRoomInformation } from "./ICryptoRoomInformation";
  4. // noinspection ES6RedundantAwait
  5. /**
  6. * Tracks room encryption status for a MatrixClient.
  7. * @category Encryption
  8. */
  9. export class RoomTracker {
  10. public constructor(private client: MatrixClient) {
  11. }
  12. /**
  13. * Handles a room join
  14. * @internal
  15. * @param roomId The room ID.
  16. */
  17. public async onRoomJoin(roomId: string) {
  18. await this.queueRoomCheck(roomId);
  19. }
  20. /**
  21. * Handles a room event.
  22. * @internal
  23. * @param roomId The room ID.
  24. * @param event The event.
  25. */
  26. public async onRoomEvent(roomId: string, event: any) {
  27. if (event['state_key'] !== '') return; // we don't care about anything else
  28. if (event['type'] === 'm.room.encryption' || event['type'] === 'm.room.history_visibility') {
  29. await this.queueRoomCheck(roomId);
  30. }
  31. }
  32. /**
  33. * Prepares the room tracker to track the given rooms.
  34. * @param {string[]} roomIds The room IDs to track. This should be the joined rooms set.
  35. */
  36. public async prepare(roomIds: string[]) {
  37. for (const roomId of roomIds) {
  38. await this.queueRoomCheck(roomId);
  39. }
  40. }
  41. /**
  42. * Queues a room check for the tracker. If the room needs an update to the store, an
  43. * update will be made.
  44. * @param {string} roomId The room ID to check.
  45. */
  46. public async queueRoomCheck(roomId: string) {
  47. const config = await this.client.cryptoStore.getRoom(roomId);
  48. if (config) {
  49. if (config.algorithm !== undefined) {
  50. return; // assume no change to encryption config
  51. }
  52. }
  53. let encEvent: Partial<EncryptionEventContent>;
  54. try {
  55. encEvent = await this.client.getRoomStateEvent(roomId, "m.room.encryption", "");
  56. encEvent.algorithm = encEvent.algorithm ?? 'UNKNOWN';
  57. } catch (e) {
  58. return; // failure == no encryption
  59. }
  60. // Pick out the history visibility setting too
  61. let historyVisibility: string;
  62. try {
  63. const ev = await this.client.getRoomStateEvent(roomId, "m.room.history_visibility", "");
  64. historyVisibility = ev.history_visibility;
  65. } catch (e) {
  66. // ignore - we'll just treat history visibility as normal
  67. }
  68. await this.client.cryptoStore.storeRoom(roomId, {
  69. ...encEvent,
  70. historyVisibility,
  71. });
  72. }
  73. /**
  74. * Gets the room's crypto configuration, as known by the underlying store. If the room is
  75. * not encrypted then this will return an empty object.
  76. * @param {string} roomId The room ID to get the config for.
  77. * @returns {Promise<ICryptoRoomInformation>} Resolves to the encryption config.
  78. */
  79. public async getRoomCryptoConfig(roomId: string): Promise<ICryptoRoomInformation> {
  80. let config = await this.client.cryptoStore.getRoom(roomId);
  81. if (!config) {
  82. await this.queueRoomCheck(roomId);
  83. config = await this.client.cryptoStore.getRoom(roomId);
  84. }
  85. if (!config) {
  86. return {};
  87. }
  88. return config;
  89. }
  90. }