Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  DeviceLinking.swift   Sprache: JAVA

 

import CocoaLumberjackSwift
import Foundation
import PromiseKit
import ThreemaFramework

@available(
    *,
    deprecated,
    message: "Only use for old linking wizard. If not available move functionality to `MultiDeviceManager`"
)
final class DeviceLinking: NSObject {
    private let businessInjector: BusinessInjectorProtocol

    private let safeStore: SafeStore
    private let safeManager: SafeManager

    private var stateDisconnectedContinuation: CheckedContinuation<Void, Never>?
    private var stateLoggedInContinuation: CheckedContinuation<Void, Error>?

    enum DeviceLinkingError: Error {
        case noDeviceGroupKey, couldNotConnect, timeout
    }

    required init(businessInjector: BusinessInjectorProtocol) {
        self.businessInjector = businessInjector

        let safeConfigManager = SafeConfigManager()
        self.safeStore = SafeStore(
            safeConfigManager: safeConfigManager,
            serverApiConnector: ServerAPIConnector(),
            groupManager: businessInjector.groupManager,
            myIdentityStore: businessInjector.myIdentityStore,
            phoneNumberNormalizer: PhoneNumberNormalizer()
        )
        self.safeManager = SafeManager(
            safeConfigManager: safeConfigManager,
            safeStore: safeStore,
            safeAPIService: SafeApiService()
        )
    }

    deinit {
        self.businessInjector.serverConnector.unregisterConnectionStateDelegate(delegate: self)
    }

    var threemaSafeServer: String {
        safeStore.getSafeServerToDisplay()
    }

    private(set) var threemaSafePassword: String?

    func generateDeviceGroupKey() throws {
        guard DeviceGroupKeyManager().create() != nil else {
            throw DeviceLinkingError.noDeviceGroupKey
        }
    }

    /// Upload Threema Safe backup just for Device Linking.
    /// The server will be shown on `self.threemaSafeSever` and the password for backup will be generated.
    /// If backup successfully uploaded the password will shown on `self.threemaSafePassword`.
    func uploadThreemaSafeBackup() async throws {
        let password = SwiftUtils.pseudoRandomStringUpperCaseOnly(length: 16, exclude: ["I", "O"])
        try await safeManager.startBackupForDeviceLinking(password: password)
        threemaSafePassword = password
    }

    func deleteThreemaSafeBackup() {
        guard let password = threemaSafePassword else {
            return
        }
        safeManager.deleteBackupForDeviceLinking(password: password)
    }

    /// Block communications to the services: Threema Calls, MDM, Web, Safe,
    /// Contact-Sync and Chat Server. Disconnect Threema Web and Chat Server connection.
    func blockCommunicationAndDisconnect() async {
        // Block communication setting was removed

        if let webClientSessions = businessInjector.entityManager.entityFetcher.activeWebClientSessionEntities() {
            for session in webClientSessions {
                WCSessionManager.shared.stopSession(session)
            }
        }

        await disconnect()
    }

    /// Unblock communications to the services: Threema Calls, MDM, Web, Safe,
    /// Contact-Sync and Chat Server.
    func unblockCommunication() {
        // no-op: Block communication setting was removed
    }

    func currentLinkedDevicesCount() async throws -> Int {
        let mm = MultiDeviceManager()

        return try await withCheckedThrowingContinuation { continuation in
            mm.otherDevices()
                .done { otherDevices in
                    continuation.resume(returning: otherDevices.count)
                }
                .catch { error in
                    continuation.resume(throwing: error)
                }
        }
    }

    /// Wait (timeout 900s) until desktop app is linked.
    /// Connection to Mediator server must be established without receiving any messages.
    /// - Parameter currentLinkedDevicesCount: Is null waiting for desktop app otherwise waiting for next linked device
    ///             desktop or iOS (`currentLinkedDevicesCount + 1`)
    func waitForLinkedDevice(currentLinkedDevicesCount: Int? = nil) async throws {
        let allowedPlatforms: [Platform] = currentLinkedDevicesCount != nil ? [.desktop, .ios] : [.desktop]
        let endTime = Date().addingTimeInterval(900)
        let mm = MultiDeviceManager()

        return try await withCheckedThrowingContinuation { continuation in
            DispatchQueue.main.async {
                self.polling(until: endTime) {
                    mm.otherDevices()
                        .then { otherDevices -> Promise<Void> in
                            if let count = currentLinkedDevicesCount, otherDevices.count <= count {
                                throw MessageReceiverError.responseTimeout
                            }
                            else {
                                if !otherDevices.contains(where: { deviceInfo in
                                    allowedPlatforms.contains(deviceInfo.platform)
                                }) {
                                    throw MessageReceiverError.responseTimeout
                                }
                            }
                            return Promise()
                        }
                }
                .done {
                    continuation.resume()
                }
                .catch { error in
                    continuation.resume(throwing: error)
                }
            }
        }
    }

    /// Polling (every 2 seconds) till `pollingRequest` is fulfilled or until date is reached.
    /// - Parameters:
    ///   - until: Max date/time until polling stops
    ///   - pollingRequest: Is function fulfilled than stops polling otherwise throw
    ///                     MessageReceiverError.responseTimeout
    private func polling<T>(until: Date, _ pollingRequest: @escaping () -> Promise<T>) -> Promise<T> {
        func polling() -> Promise<T> {
            pollingRequest().recover { error -> Promise<T> in
                guard case MessageReceiverError.responseTimeout = error else {
                    throw error
                }
                guard until > Date() else {
                    throw DeviceLinkingError.timeout
                }
                return after(.seconds(2)).then(polling)
            }
        }
        return polling()
    }

    /// Drop all devices including this device and disable Multi Device
    func disableMultiDevice() -> Promise<Void> {
        businessInjector.multiDeviceManager.otherDevices()
            .then { dropDevices -> Promise<Void> in
                self.drop(items: dropDevices)
            }
            .then { () -> Promise<Bool> in
                Promise { seal in
                    self.drop(items: [self.businessInjector.multiDeviceManager.thisDevice])
                        .done {
                            self.businessInjector.serverConnector.deactivateMultiDevice()

                            // Update feature mask to activate forward secrecy
                            FeatureMask.updateLocal()

                            // In this case explicit disconnect is not necessary, because Mediator server makes a
                            // disconnect after dropping this device
                            seal.fulfill(false)
                        }
                        .catch { error in
                            seal.reject(error)
                        }
                }
            }
            .then { doDisconnect -> Promise<Void> in
                Promise { seal in
                    Task {
                        // Reconnect to Chat server
                        if doDisconnect {
                            await self.disconnect()
                        }
                        self.unblockCommunication()
                        try? await self.connectWait()

                        seal.fulfill_()
                    }
                }
            }
    }

    func drop(items: [DeviceInfo]) -> Promise<Void> {
        let drops: [Promise<Void>] = items.map { deviceInfo in
            self.businessInjector.multiDeviceManager.drop(device: deviceInfo)
        }

        return when(fulfilled: drops)
    }

    /// Connect to Chat (Mediator) server (waits until connection state has changed to logged in) without receiving any
    /// messages.
    func connectWaitDoNotUnblockIncomingMessages() async throws {
        // swiftformat:disable:next all
        return try await withCheckedThrowingContinuation { continuation in
            guard businessInjector.serverConnector.connectionState != .loggedIn else {
                return continuation.resume()
            }
            stateLoggedInContinuation = continuation
            businessInjector.serverConnector.registerConnectionStateDelegate(delegate: self)
            self.businessInjector.serverConnector.connectWaitDoNotUnblockIncomingMessages(initiator: .app)
        }
    }

    /// Connect to Chat (Mediator) server (waits until connection state has changed to logged in).
    func connectWait() async throws {
        // swiftformat:disable:next all
        return try await withCheckedThrowingContinuation { continuation in
            guard businessInjector.serverConnector.connectionState != .loggedIn else {
                return continuation.resume()
            }
            stateLoggedInContinuation = continuation
            businessInjector.serverConnector.registerConnectionStateDelegate(delegate: self)
            self.businessInjector.serverConnector.connectWait(initiator: .app)
        }
    }

    // Disconnect to server (waits until connection state has changed to disconnected).
    func disconnect() async {
        // swiftformat:disable:next all
        return await withCheckedContinuation { continuation in
            guard businessInjector.serverConnector.connectionState != .disconnected else {
                return continuation.resume()
            }
            stateDisconnectedContinuation = continuation
            businessInjector.serverConnector.registerConnectionStateDelegate(delegate: self)
            self.businessInjector.serverConnector.disconnect(initiator: .app)
        }
    }
}

// MARK: - ConnectionStateDelegate

extension DeviceLinking: ConnectionStateDelegate {
    func changed(connectionState state: ConnectionState) {
        if state == .loggedIn {
            stateLoggedInContinuation?.resume()
            stateLoggedInContinuation = nil
        }
        else if state == .disconnected {
            stateLoggedInContinuation?.resume(throwing: DeviceLinkingError.couldNotConnect)
            stateLoggedInContinuation = nil

            stateDisconnectedContinuation?.resume()
            stateDisconnectedContinuation = nil
        }
    }
}

Messung
CHZG
85969993

¤ Dauer der Verarbeitung: 0.0 Sekunden  (vorverarbeitet am  2026-11-04) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover


Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=1104850
#Domains=416975