// // Copyright 2025 Element Creations Ltd. // Copyright 2022-2025 New Vector Ltd. // // SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial. // Please see LICENSE files in the repository root for full details. // import Combine import Foundation import MatrixRustSDK private final class WeakSessionVerificationControllerProxy: SessionVerificationControllerDelegate { private weak var proxy: SessionVerificationControllerProxy? init(proxy: SessionVerificationControllerProxy) { self.proxy = proxy } // MARK: - SessionVerificationControllerDelegate func didReceiveVerificationRequest(details: MatrixRustSDK.SessionVerificationRequestDetails) { proxy?.didReceiveVerificationRequest(details: details) } func didReceiveVerificationData(data: MatrixRustSDK.SessionVerificationData) { switch data { // We can handle only emojis for now case .emojis(let emojis, _): proxy?.didReceiveData(emojis) default: break } } func didAcceptVerificationRequest() { proxy?.didAcceptVerificationRequest() } func didStartSasVerification() { proxy?.didStartSasVerification() } func didFail() { proxy?.didFail() } func didCancel() { proxy?.didCancel() } func didFinish() { proxy?.didFinish() } } class SessionVerificationControllerProxy: SessionVerificationControllerProxyProtocol { private let sessionVerificationController: SessionVerificationController init(sessionVerificationController: SessionVerificationController) { self.sessionVerificationController = sessionVerificationController sessionVerificationController.setDelegate(delegate: WeakSessionVerificationControllerProxy(proxy: self)) } deinit { sessionVerificationController.setDelegate(delegate: nil) } let actions = PassthroughSubject() func acknowledgeVerificationRequest(details: SessionVerificationRequestDetails) async -> Result { MXLog.info("Acknowledging verification request") do { try await sessionVerificationController.acknowledgeVerificationRequest(senderId: details.senderProfile.userID, flowId: details.flowID) return .success(()) } catch { MXLog.error("Failed requesting session verification with error: \(error)") return .failure(.failedAcknowledgingVerificationRequest) } } func acceptVerificationRequest() async -> Result { MXLog.info("Accepting verification request") do { try await sessionVerificationController.acceptVerificationRequest() MXLog.info("Accepted verification request") actions.send(.acceptedVerificationRequest) return .success(()) } catch { MXLog.error("Failed requesting session verification with error: \(error)") return .failure(.failedAcceptingVerificationRequest) } } func requestDeviceVerification() async -> Result { MXLog.info("Requesting device verification") do { try await sessionVerificationController.requestDeviceVerification() return .success(()) } catch { MXLog.error("Failed requesting device verification with error: \(error)") return .failure(.failedRequestingVerification) } } func requestUserVerification(_ userID: String) async -> Result { MXLog.info("Requesting user verification") do { try await sessionVerificationController.requestUserVerification(userId: userID) return .success(()) } catch { MXLog.error("Failed requesting verification for user \(userID) with error: \(error)") return .failure(.failedRequestingVerification) } } func startSasVerification() async -> Result { MXLog.info("Starting SAS verification") do { try await sessionVerificationController.startSasVerification() return .success(()) } catch { MXLog.error("Failed starting SAS verification with error: \(error)") return .failure(.failedStartingSasVerification) } } func approveVerification() async -> Result { MXLog.info("Approving verification") do { try await sessionVerificationController.approveVerification() return .success(()) } catch { MXLog.error("Failed approving verification with error: \(error)") return .failure(.failedApprovingVerification) } } func declineVerification() async -> Result { MXLog.info("Declining verification") do { try await sessionVerificationController.declineVerification() return .success(()) } catch { MXLog.error("Failed declining verification with error: \(error)") return .failure(.failedDecliningVerification) } } func cancelVerification() async -> Result { MXLog.info("Cancelling verification") do { try await sessionVerificationController.cancelVerification() return .success(()) } catch { MXLog.error("Failed cancelling verification with error: \(error)") return .failure(.failedCancellingVerification) } } // MARK: - Private fileprivate func didReceiveVerificationRequest(details: MatrixRustSDK.SessionVerificationRequestDetails) { MXLog.info("Received verification request \(details)") let details = SessionVerificationRequestDetails(senderProfile: UserProfileProxy(sdkUserProfile: details.senderProfile), flowID: details.flowId, deviceID: details.deviceId, deviceDisplayName: details.deviceDisplayName, firstSeenDate: Date(timeIntervalSince1970: TimeInterval(details.firstSeenTimestamp / 1000))) actions.send(.receivedVerificationRequest(details: details)) } fileprivate func didAcceptVerificationRequest() { // Noop because the rust side state machine changes states before sending // the actual request, leading to race conditions with the SAS verification // startup. The `acceptedVerificationRequest` is now called from the `startSasVerification` // method above. } fileprivate func didStartSasVerification() { MXLog.info("Started SAS verification") actions.send(.startedSasVerification) } fileprivate func didReceiveData(_ data: [MatrixRustSDK.SessionVerificationEmoji]) { MXLog.info("Received verification data") actions.send(.receivedVerificationData(data.map { emoji in SessionVerificationEmoji(symbol: emoji.symbol(), description: emoji.description()) })) } fileprivate func didFail() { actions.send(.failed) } fileprivate func didFinish() { actions.send(.finished) } fileprivate func didCancel() { actions.send(.cancelled) } }