1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
|
//
// LoadTunnelConfigurationOperation.swift
// MullvadVPN
//
// Created by pronebird on 16/12/2021.
// Copyright © 2025 Mullvad VPN AB. All rights reserved.
//
import Foundation
import MullvadLogging
import MullvadSettings
import MullvadTypes
import Operations
class LoadTunnelConfigurationOperation: ResultOperation<Void>, @unchecked Sendable {
private let logger = Logger(label: "LoadTunnelConfigurationOperation")
private let interactor: TunnelInteractor
init(dispatchQueue: DispatchQueue, interactor: TunnelInteractor) {
self.interactor = interactor
super.init(dispatchQueue: dispatchQueue)
}
override func main() {
let settingsResult = readSettings()
let deviceStateResult = readDeviceState()
let persistentTunnels = interactor.getPersistentTunnels()
let tunnel = persistentTunnels.first
let settings = settingsResult.flattenValue()
let deviceState = deviceStateResult.flattenValue()
interactor.setSettings(settings ?? LatestTunnelSettings(), persist: false)
interactor.setDeviceState(deviceState ?? .loggedOut, persist: false)
if let tunnel, deviceState == nil {
logger.debug("Remove orphaned VPN configuration.")
tunnel.removeFromPreferences { error in
if let error {
self.logger.error(
error: error,
message: "Failed to remove VPN configuration."
)
}
self.finishOperation(tunnel: nil)
}
} else {
finishOperation(tunnel: tunnel)
}
}
private func finishOperation(tunnel: (any TunnelProtocol)?) {
interactor.setTunnel(tunnel, shouldRefreshTunnelState: true)
interactor.setConfigurationLoaded()
finish(result: .success(()))
}
private func readSettings() -> Result<LatestTunnelSettings?, Error> {
Result { try SettingsManager.readSettings() }
.flatMapError { error in
if let error = error as? ReadSettingsVersionError,
let keychainError = error.underlyingError as? KeychainError, keychainError == .itemNotFound
{
logger.debug("Settings not found in keychain.")
return .success(nil)
} else {
logger.error(
error: error,
message: "Cannot read settings."
)
return .failure(error)
}
}
}
private func readDeviceState() -> Result<DeviceState?, Error> {
Result { try SettingsManager.readDeviceState() }
.flatMapError { error in
if let error = error as? KeychainError, error == .itemNotFound {
logger.debug("Device state not found in keychain.")
return .success(nil)
} else {
logger.error(
error: error,
message: "Cannot read device state."
)
return .failure(error)
}
}
}
}
|