Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Threema/ThreemaFramework/OnPrem/   (Social Medium ©)  Datei vom 26.9.2026 mit Größe 6 kB image not shown  

Quelle  OnPremConfigFetcher.swift   Sprache: JAVA

 

import CocoaLumberjackSwift
import FileUtility
import Foundation
import Keychain

protocol OnPremConfigFetcherDelegate: AnyObject {
    func oppfFileUpdated()
}

final class OnPremConfigFetcher: OnPremConfigFetcherProtocol {
    private let configDownloader: OnPremConfigDownloaderProtocol
    private let configVerifier: OnPremConfigVerifierProtocol
    private let cacheURL: URL
    private weak var delegate: OnPremConfigFetcherDelegate?

    private static var currentTask: Task<OnPremConfig, any Error>? = nil
    private var cachedConfig: OnPremConfig?

    init(
        configDownloader: OnPremConfigDownloaderProtocol,
        configVerifier: OnPremConfigVerifierProtocol,
        cacheURL: URL,
        delegate: OnPremConfigFetcherDelegate
    ) {
        self.configDownloader = configDownloader
        self.configVerifier = configVerifier
        self.cacheURL = cacheURL
        self.delegate = delegate
    }
    
    func fetch(completionHandler: @escaping (Swift.Result<OnPremConfig, Error>) -> Void) {
        DDLogVerbose("[Fetch OPPF] New fetch")

        // If a task already runs we wait for the result. If no task exists we create a new one and wait on it
        //
        // Note: This is intentionally not fully race safe if the fetch task should only be run once. We landed on this
        // implementation to keep the code more readable and because multiple request to fetch the OPPF are not too
        // resource intensive. This could be fixed by protecting the access of `currentTask` either by making this class
        // an actor or using some sort of mutex or queue.
        Task {
            // The cache is never reset. Thus we can take this shortcut if the config is already cached
            if await !self.configDownloader.isRecoveryModeEnabled, let cachedConfig {
                DDLogVerbose("[Fetch OPPF] Getting from cache")
                completionHandler(.success(cachedConfig))
                return
            }

            let task: Task<OnPremConfig, any Error>
            if let currentTask = OnPremConfigFetcher.currentTask {
                DDLogVerbose("[Fetch OPPF] Load existing fetch task to wait on it")
                task = currentTask
            }
            else {
                DDLogVerbose("[Fetch OPPF] Create new fetch task")
                let newTask = Task {
                    var doFetching = true
                    while doFetching {
                        do {
                            let config = try await self.fetch()
                            doFetching = false
                            return config
                        }
                        catch OnPremConfigError.fetchRequestFailed {
                            if await configDownloader.isRecoveryModeEnabled {
                                DDLogVerbose(
                                    "[Fetch OPPF] Fetch failed while in recovery, wait 10s to avoid too many requests in a short time"
                                )
                                try await Task.sleep(seconds: 10)
                            }
                            throw OnPremConfigError.fetchRequestFailed
                        }
                        catch {
                            doFetching = false
                            DDLogError("[Fetch OPPF] Fetch failed: \(error)")
                            throw error
                        }
                    }
                }
                OnPremConfigFetcher.currentTask = newTask

                task = newTask
            }

            defer {
                OnPremConfigFetcher.currentTask = nil
            }

            do {
                let config = try await task.value
                completionHandler(.success(config))
            }
            catch {
                completionHandler(.failure(error))
            }
        }
    }
    
    private func fetch() async throws -> OnPremConfig {
        let isRecoveryModeEnabled = await configDownloader.isRecoveryModeEnabled
        if !isRecoveryModeEnabled, let cachedConfig {
            DDLogVerbose("[Fetch OPPF] Getting from cache 2")
            return cachedConfig
        }

        DDLogVerbose("[Fetch OPPF] Fetch from server")
        let result = try await configDownloader.downloadData()

        guard let httpResponse = result.response as? HTTPURLResponse else {
            throw OnPremConfigError.fetchRequestFailed
        }

        switch httpResponse.statusCode {
        case 400:
            DDLogError("[Fetch OPPF] HTTP status code: \(httpResponse.statusCode)")
            if isRecoveryModeEnabled {
                DDLogError("[Fetch OPPF] HTTP Authorization contains user or password")
            }
            throw OnPremConfigError.fetchRequestFailed
        case 401:
            DDLogError("[Fetch OPPF] HTTP status code: \(httpResponse.statusCode)")
            if let oppf = try JSONSerialization.jsonObject(with: result.oppfData, options: .allowFragments) as? [
                String: String
            ],
                let errorString = oppf["error"] {
                DDLogError("[Fetch OPPF] HTTP error message: \(errorString)")
            }
            if isRecoveryModeEnabled {
                DDLogError("[Fetch OPPF] HTTP Authorization contains user or password")
            }
            throw OnPremConfigError.unauthorized
        case 404:
            DDLogError("[Fetch OPPF] HTTP status code: \(httpResponse.statusCode)")
            if isRecoveryModeEnabled {
                DDLogError(
                    "[Fetch OPPF] The temporary fallback has not been activated via cockpit"
                )
            }
            throw OnPremConfigError.fetchRequestFailed
        case 429:
            DDLogError("[Fetch OPPF] HTTP status code: \(httpResponse.statusCode)")
            if isRecoveryModeEnabled {
                DDLogError("[Fetch OPPF] License check exceeds quota per IP per minute")
            }
            throw OnPremConfigError.fetchRequestFailed
        case 200:
            DDLogVerbose("[Fetch OPPF] Fetch was successful")
        default:
            DDLogVerbose("[Fetch OPPF] Untreated HTTP code: \(httpResponse.statusCode)")
            throw OnPremConfigError.fetchRequestFailed
        }

        guard let oppfString = String(data: result.oppfData, encoding: .utf8) else {
            throw OnPremConfigError.badInputOppfData
        }
        
        if oppfString == "Unauthorized" {
            throw OnPremConfigError.unauthorized
        }
        let config = try configVerifier.verify(oppfData: oppfString)
        cachedConfig = config

        // Caches the new OPPF file and reset cached pins
        FileUtility.shared.write(contents: result.oppfData, to: cacheURL)
        await configDownloader.enableRecoveryMode(false)

        delegate?.oppfFileUpdated()

        NotificationCenter.default.post(name: .resetSSLCAHelperCache, object: nil)

        return config
    }
}

Messung
CHZG
879710094

¤ Dauer der Verarbeitung: 0.4 Sekunden  ¤

*© 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.