diff --git a/src/matrix/verification/SAS/SASVerification.ts b/src/matrix/verification/SAS/SASVerification.ts index e2882fd4..82c5f856 100644 --- a/src/matrix/verification/SAS/SASVerification.ts +++ b/src/matrix/verification/SAS/SASVerification.ts @@ -17,17 +17,28 @@ import {StartVerificationStage} from "./stages/StartVerificationStage"; import type {ILogItem} from "../../../logging/types"; import type {Room} from "../../room/Room.js"; import type {BaseSASVerificationStage, UserData} from "./stages/BaseSASVerificationStage"; +import {WaitForIncomingMessageStage} from "./stages/WaitForIncomingMessageStage"; export class SASVerification { - private stages: BaseSASVerificationStage[] = []; + private startStage: BaseSASVerificationStage; constructor(private room: Room, private ourUser: UserData, otherUserId: string, log: ILogItem) { - this.stages.push(new StartVerificationStage(room, ourUser, otherUserId, log)); + const options = { room, ourUser, otherUserId, log }; + let stage: BaseSASVerificationStage = new StartVerificationStage(options); + this.startStage = stage; + + stage.setNextStage(new WaitForIncomingMessageStage("m.key.verification.ready", options)); + stage = stage.nextStage; + + stage.setNextStage(new WaitForIncomingMessageStage("m.key.verification.start", options)); + stage = stage.nextStage; } async start() { - for (const stage of this.stages) { - await stage.completeStage(); - } + let stage = this.startStage; + do { + await stage.completeStage(); + stage = stage.nextStage; + } while (stage); } } diff --git a/src/matrix/verification/SAS/stages/BaseSASVerificationStage.ts b/src/matrix/verification/SAS/stages/BaseSASVerificationStage.ts index 57a42e72..0fcd249b 100644 --- a/src/matrix/verification/SAS/stages/BaseSASVerificationStage.ts +++ b/src/matrix/verification/SAS/stages/BaseSASVerificationStage.ts @@ -36,13 +36,14 @@ export abstract class BaseSASVerificationStage extends Disposables { protected log: ILogItem; protected requestEventId: string; protected previousResult: undefined | any; + protected _nextStage: BaseSASVerificationStage; constructor(options: Options) { super(); this.room = options.room; this.ourUser = options.ourUser; this.otherUserId = options.otherUserId; - this.log = options.log; + this.log = options.log; } setRequestEventId(id: string) { @@ -55,7 +56,14 @@ export abstract class BaseSASVerificationStage extends Disposables { this.previousResult = result; } + setNextStage(stage: BaseSASVerificationStage) { + this._nextStage = stage; + } + + get nextStage(): BaseSASVerificationStage { + return this._nextStage; + } + abstract get type(): string; - abstract completeStage(): undefined | Record; - abstract get nextStage(): BaseSASVerificationStage; + abstract completeStage(): Promise; } diff --git a/src/matrix/verification/SAS/stages/StartVerificationStage.ts b/src/matrix/verification/SAS/stages/StartVerificationStage.ts index 1ef6fd05..081c6d67 100644 --- a/src/matrix/verification/SAS/stages/StartVerificationStage.ts +++ b/src/matrix/verification/SAS/stages/StartVerificationStage.ts @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ import {BaseSASVerificationStage} from "./BaseSASVerificationStage"; +import {FragmentBoundaryEntry} from "../../../room/timeline/entries/FragmentBoundaryEntry.js"; // From element-web // type KeyAgreement = "curve25519-hkdf-sha256" | "curve25519"; @@ -31,9 +32,6 @@ import {BaseSASVerificationStage} from "./BaseSASVerificationStage"; // const SAS_LIST = Object.keys(sasGenerators); export class StartVerificationStage extends BaseSASVerificationStage { - private readyMessagePromise: Promise; - private startMessagePromise: Promise; - async completeStage() { await this.log.wrap("StartVerificationStage.completeStage", async (log) => { const content = { @@ -43,44 +41,38 @@ export class StartVerificationStage extends BaseSASVerificationStage { "msgtype": "m.key.verification.request", "to": this.otherUserId, }; + const promise = this.trackEventId(); await this.room.sendEvent("m.room.message", content, null, log); - const [readyContent, startContent] = await this.fetchMessageEventsFromTimeline(); - console.log("readyContent", readyContent, "startContent", startContent); + const eventId = await promise; + console.log("eventId", eventId); + this.setRequestEventId(eventId); this.dispose(); }); - return true; } - private fetchMessageEventsFromTimeline() { - let readyResolve, startResolve; - this.readyMessagePromise = new Promise(r => { readyResolve = r; }); - this.startMessagePromise = new Promise(r => { startResolve = r; }); - this.track( - this.room._timeline.entries.subscribe({ - onAdd: (_, entry) => { - if (entry.eventType === "m.key.verification.ready") { - readyResolve(entry.content); - } - else if (entry.eventType === "m.key.verification.start") { - startResolve(entry.content); - } - }, - onRemove: () => { - - }, - onUpdate: () => { - - }, - }) - ); - return Promise.all([this.readyMessagePromise, this.startMessagePromise]); + private trackEventId(): Promise { + return new Promise(resolve => { + this.track( + this.room._timeline.entries.subscribe({ + onAdd: (_, entry) => { + if (entry instanceof FragmentBoundaryEntry) { + return; + } + if (!entry.isPending && + entry.content["msgtype"] === "m.key.verification.request" && + entry.content["from_device"] === this.ourUser.deviceId) { + console.log("found event", entry); + resolve(entry.id); + } + }, + onRemove: () => { /**noop*/ }, + onUpdate: () => { /**noop*/ }, + }) + ); + }); } get type() { return "m.key.verification.request"; } - - get nextStage(): BaseSASVerificationStage { - return this; - } } diff --git a/src/matrix/verification/SAS/stages/WaitForIncomingMessageStage.ts b/src/matrix/verification/SAS/stages/WaitForIncomingMessageStage.ts index 1f8e1fd8..b2d288f1 100644 --- a/src/matrix/verification/SAS/stages/WaitForIncomingMessageStage.ts +++ b/src/matrix/verification/SAS/stages/WaitForIncomingMessageStage.ts @@ -14,6 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ import {BaseSASVerificationStage, Options} from "./BaseSASVerificationStage"; +import {FragmentBoundaryEntry} from "../../../room/timeline/entries/FragmentBoundaryEntry.js"; export class WaitForIncomingMessageStage extends BaseSASVerificationStage { constructor(private messageType: string, options: Options) { @@ -30,7 +31,6 @@ export class WaitForIncomingMessageStage extends BaseSASVerificationStage { }); this.dispose(); }); - return true; } private fetchMessageEventsFromTimeline() { @@ -43,7 +43,7 @@ export class WaitForIncomingMessageStage extends BaseSASVerificationStage { // We only care about incoming / remote message events return; } - if (entry.eventType === this.messageType && + if (entry.type === this.messageType && entry.content["m.relates_to"]["event_id"] === this.requestEventId) { resolve(entry.content); } @@ -55,7 +55,10 @@ export class WaitForIncomingMessageStage extends BaseSASVerificationStage { const remoteEntries = this.room._timeline.remoteEntries; // In case we were slow and the event is already added to the timeline, for (const entry of remoteEntries) { - if (entry.eventType === this.messageType && + if (entry instanceof FragmentBoundaryEntry) { + return; + } + if (entry.type === this.messageType && entry.content["m.relates_to"]["event_id"] === this.requestEventId) { resolve(entry.content); } @@ -66,9 +69,5 @@ export class WaitForIncomingMessageStage extends BaseSASVerificationStage { get type() { return this.messageType; } - - get nextStage(): BaseSASVerificationStage { - return this; - } }