From c0e4733d40c83111c22ec127185b5f69e7d531bc Mon Sep 17 00:00:00 2001 From: mojganii Date: Mon, 18 Dec 2023 16:29:30 +0100 Subject: [PATCH 1/2] removing network operation from settings migration --- ios/MullvadSettings/MigrationManager.swift | 4 --- ios/MullvadSettings/SettingsManager.swift | 1 - ios/MullvadSettings/TunnelSettings.swift | 1 - ios/MullvadSettings/TunnelSettingsV1.swift | 1 - ios/MullvadSettings/TunnelSettingsV2.swift | 1 - ios/MullvadSettings/TunnelSettingsV3.swift | 1 - ios/MullvadVPN/AppDelegate.swift | 2 +- .../MigrationManagerTests.swift | 27 +++++-------------- 8 files changed, 8 insertions(+), 30 deletions(-) diff --git a/ios/MullvadSettings/MigrationManager.swift b/ios/MullvadSettings/MigrationManager.swift index 8667885823c5..38fe546ea187 100644 --- a/ios/MullvadSettings/MigrationManager.swift +++ b/ios/MullvadSettings/MigrationManager.swift @@ -8,7 +8,6 @@ import Foundation import MullvadLogging -import MullvadREST import MullvadTypes public enum SettingsMigrationResult { @@ -37,7 +36,6 @@ public struct MigrationManager { /// - migrationCompleted: Completion handler called with a migration result. public func migrateSettings( store: SettingsStore, - proxyFactory: REST.ProxyFactory, migrationCompleted: @escaping (SettingsMigrationResult) -> Void ) { let resetStoreHandler = { (result: SettingsMigrationResult) in @@ -51,7 +49,6 @@ public struct MigrationManager { do { try upgradeSettingsToLatestVersion( store: store, - proxyFactory: proxyFactory, migrationCompleted: migrationCompleted ) } catch .itemNotFound as KeychainError { @@ -63,7 +60,6 @@ public struct MigrationManager { private func upgradeSettingsToLatestVersion( store: SettingsStore, - proxyFactory: REST.ProxyFactory, migrationCompleted: @escaping (SettingsMigrationResult) -> Void ) throws { let parser = SettingsParser(decoder: JSONDecoder(), encoder: JSONEncoder()) diff --git a/ios/MullvadSettings/SettingsManager.swift b/ios/MullvadSettings/SettingsManager.swift index 9d34fd877d1a..2f8aa546dbbd 100644 --- a/ios/MullvadSettings/SettingsManager.swift +++ b/ios/MullvadSettings/SettingsManager.swift @@ -8,7 +8,6 @@ import Foundation import MullvadLogging -import MullvadREST import MullvadTypes private let keychainServiceName = "Mullvad VPN" diff --git a/ios/MullvadSettings/TunnelSettings.swift b/ios/MullvadSettings/TunnelSettings.swift index 9d27b0a3757e..c72d03c4b796 100644 --- a/ios/MullvadSettings/TunnelSettings.swift +++ b/ios/MullvadSettings/TunnelSettings.swift @@ -7,7 +7,6 @@ // import Foundation -import MullvadREST /// Alias to the latest version of the `TunnelSettings`. public typealias LatestTunnelSettings = TunnelSettingsV3 diff --git a/ios/MullvadSettings/TunnelSettingsV1.swift b/ios/MullvadSettings/TunnelSettingsV1.swift index 21c28164c864..aaa3c278458e 100644 --- a/ios/MullvadSettings/TunnelSettingsV1.swift +++ b/ios/MullvadSettings/TunnelSettingsV1.swift @@ -7,7 +7,6 @@ // import Foundation -import MullvadREST import MullvadTypes import Network import WireGuardKitTypes diff --git a/ios/MullvadSettings/TunnelSettingsV2.swift b/ios/MullvadSettings/TunnelSettingsV2.swift index c5d39ca0e606..c3bb7cd756b4 100644 --- a/ios/MullvadSettings/TunnelSettingsV2.swift +++ b/ios/MullvadSettings/TunnelSettingsV2.swift @@ -7,7 +7,6 @@ // import Foundation -import MullvadREST import MullvadTypes public struct TunnelSettingsV2: Codable, Equatable, TunnelSettings { diff --git a/ios/MullvadSettings/TunnelSettingsV3.swift b/ios/MullvadSettings/TunnelSettingsV3.swift index 98d9f4bc7131..c91fbfe0b93f 100644 --- a/ios/MullvadSettings/TunnelSettingsV3.swift +++ b/ios/MullvadSettings/TunnelSettingsV3.swift @@ -7,7 +7,6 @@ // import Foundation -import MullvadREST import MullvadTypes public struct TunnelSettingsV3: Codable, Equatable, TunnelSettings { diff --git a/ios/MullvadVPN/AppDelegate.swift b/ios/MullvadVPN/AppDelegate.swift index 64f642cd893f..fe254248c221 100644 --- a/ios/MullvadVPN/AppDelegate.swift +++ b/ios/MullvadVPN/AppDelegate.swift @@ -418,7 +418,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD private func getMigrateSettingsOperation(application: UIApplication) -> AsyncBlockOperation { AsyncBlockOperation(dispatchQueue: .main) { [self] finish in migrationManager - .migrateSettings(store: SettingsManager.store, proxyFactory: proxyFactory) { [self] migrationResult in + .migrateSettings(store: SettingsManager.store) { [self] migrationResult in switch migrationResult { case .success: // Tell the tunnel to re-read tunnel configuration after migration. diff --git a/ios/MullvadVPNTests/MigrationManagerTests.swift b/ios/MullvadVPNTests/MigrationManagerTests.swift index 1b0bc35bad4c..c5f693ad01f2 100644 --- a/ios/MullvadVPNTests/MigrationManagerTests.swift +++ b/ios/MullvadVPNTests/MigrationManagerTests.swift @@ -15,7 +15,6 @@ final class MigrationManagerTests: XCTestCase { static let store = InMemorySettingsStore() var manager: MigrationManager! - var proxyFactory: REST.ProxyFactory! override class func setUp() { SettingsManager.unitTestStore = store } @@ -25,18 +24,6 @@ final class MigrationManagerTests: XCTestCase { } override func setUpWithError() throws { - let transportProvider = REST.AnyTransportProvider { nil } - let addressCache = REST.AddressCache(canWriteToCache: false, fileCache: MemoryCache()) - let proxyConfiguration = REST.ProxyConfiguration( - transportProvider: transportProvider, - addressCacheStore: addressCache - ) - let authProxy = REST.AuthProxyConfiguration( - proxyConfiguration: proxyConfiguration, - accessTokenManager: AccessTokenManagerStub() - ) - - proxyFactory = REST.ProxyFactory(configuration: authProxy) manager = MigrationManager() } @@ -46,7 +33,7 @@ final class MigrationManagerTests: XCTestCase { try SettingsManager.writeSettings(settings) let nothingToMigrateExpectation = expectation(description: "No migration") - manager.migrateSettings(store: store, proxyFactory: proxyFactory) { result in + manager.migrateSettings(store: store) { result in if case .nothing = result { nothingToMigrateExpectation.fulfill() } @@ -59,7 +46,7 @@ final class MigrationManagerTests: XCTestCase { SettingsManager.unitTestStore = store let nothingToMigrateExpectation = expectation(description: "No migration") - manager.migrateSettings(store: store, proxyFactory: proxyFactory) { result in + manager.migrateSettings(store: store) { result in if case .nothing = result { nothingToMigrateExpectation.fulfill() } @@ -74,7 +61,7 @@ final class MigrationManagerTests: XCTestCase { func testFailedMigration() throws { let store = Self.store let failedMigrationExpectation = expectation(description: "Failed migration") - manager.migrateSettings(store: store, proxyFactory: proxyFactory) { result in + manager.migrateSettings(store: store) { result in if case .failure = result { failedMigrationExpectation.fulfill() } @@ -89,7 +76,7 @@ final class MigrationManagerTests: XCTestCase { try store.write(data, for: .deviceState) // Failed migration should reset settings and device state keys - manager.migrateSettings(store: store, proxyFactory: proxyFactory) { _ in } + manager.migrateSettings(store: store) { _ in } let assertDeletionFor: (SettingsKey) throws -> Void = { key in try XCTAssertThrowsError(store.read(key: key)) { thrownError in @@ -106,7 +93,7 @@ final class MigrationManagerTests: XCTestCase { let settings = FutureVersionSettings() try write(settings: settings, version: Int.max - 1, in: store) - manager.migrateSettings(store: store, proxyFactory: proxyFactory) { _ in } + manager.migrateSettings(store: store) { _ in } let assertDeletionFor: (SettingsKey) throws -> Void = { key in try XCTAssertThrowsError(store.read(key: key)) { thrownError in @@ -124,7 +111,7 @@ final class MigrationManagerTests: XCTestCase { try write(settings: settings, version: -42, in: store) let failedMigrationExpectation = expectation(description: "Failed migration") - manager.migrateSettings(store: store, proxyFactory: proxyFactory) { result in + manager.migrateSettings(store: store) { result in if case .failure = result { failedMigrationExpectation.fulfill() } @@ -161,7 +148,7 @@ final class MigrationManagerTests: XCTestCase { try write(settings: settings, version: version.rawValue, in: store) let successfulMigrationExpectation = expectation(description: "Successful migration") - manager.migrateSettings(store: store, proxyFactory: proxyFactory) { result in + manager.migrateSettings(store: store) { result in if case .success = result { successfulMigrationExpectation.fulfill() } From 12a6969b45a9c7bdea0e408567ec309922e549d7 Mon Sep 17 00:00:00 2001 From: mojganii Date: Thu, 21 Dec 2023 14:46:03 +0100 Subject: [PATCH 2/2] moving AccessMethodRepository into MullvadSettings --- .../ShadowsocksConfiguration.swift | 11 +- .../Shadowsocks/ShadowsocksTransport.swift | 4 +- .../Socks5/AnyIPEndpoint+Socks5.swift | 10 -- .../Socks5/Socks5Configuration.swift | 19 ++- .../Socks5/URLSessionSocks5Transport.swift | 2 +- .../Transport/TransportProvider.swift | 9 +- ios/MullvadSettings/AccessMethodKind.swift | 57 ++++++++ .../AccessMethodRepository.swift | 118 +++++++++++++++++ .../AccessMethodRepositoryProtocol.swift | 6 +- .../PersistentAccessMethod.swift | 100 ++++++++++++++ .../ShadowsocksCipherOptions.swift | 52 ++++++++ ios/MullvadVPN.xcodeproj/project.pbxproj | 79 +++++------ .../PersistentAccessMethod.swift | 124 ------------------ .../ProxyConfigurationTester.swift | 1 + .../ProxyConfigurationTesterProtocol.swift | 1 + .../ShadowsocksCipher.swift | 48 ------- .../Add/AddAccessMethodCoordinator.swift | 1 + .../Add/AddAccessMethodInteractor.swift | 1 + .../Common/ShadowsocksSectionHandler.swift | 2 +- .../Edit/EditAccessMethodCoordinator.swift | 1 + .../Edit/EditAccessMethodInteractor.swift | 1 + .../List/ListAccessMethodCoordinator.swift | 1 + .../List/ListAccessMethodInteractor.swift | 1 + ...swift => AccessMethodKind+Extension.swift} | 5 +- .../APIAccess/Models/AccessMethodKind.swift | 85 ++++++++++++ .../AccessMethodValidationError+Helpers.swift | 1 + .../AccessMethodViewModel+Persistent.swift | 1 + .../Models/AccessMethodViewModel.swift | 3 +- .../PersistentAccessMethod+ViewModel.swift | 1 + ...rsistentProxyConfiguration+ViewModel.swift | 1 + .../Pickers/AccessMethodProtocolPicker.swift | 1 + .../Pickers/ShadowsocksCipherPicker.swift | 13 +- 32 files changed, 500 insertions(+), 260 deletions(-) create mode 100644 ios/MullvadSettings/AccessMethodKind.swift create mode 100644 ios/MullvadSettings/AccessMethodRepository.swift rename ios/{MullvadVPN/AccessMethodRepository => MullvadSettings}/AccessMethodRepositoryProtocol.swift (84%) create mode 100644 ios/MullvadSettings/PersistentAccessMethod.swift create mode 100644 ios/MullvadSettings/ShadowsocksCipherOptions.swift delete mode 100644 ios/MullvadVPN/AccessMethodRepository/PersistentAccessMethod.swift delete mode 100644 ios/MullvadVPN/AccessMethodRepository/ShadowsocksCipher.swift rename ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/{AccessMethodKind+Extensions.swift => AccessMethodKind+Extension.swift} (91%) create mode 100644 ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/AccessMethodKind.swift diff --git a/ios/MullvadREST/Transport/Shadowsocks/ShadowsocksConfiguration.swift b/ios/MullvadREST/Transport/Shadowsocks/ShadowsocksConfiguration.swift index 0a145f0976f6..710b3ef06b82 100644 --- a/ios/MullvadREST/Transport/Shadowsocks/ShadowsocksConfiguration.swift +++ b/ios/MullvadREST/Transport/Shadowsocks/ShadowsocksConfiguration.swift @@ -7,17 +7,18 @@ // import Foundation +import MullvadTypes import Network public struct ShadowsocksConfiguration: Codable { - public let bridgeAddress: IPv4Address - public let bridgePort: UInt16 + public let address: AnyIPAddress + public let port: UInt16 public let password: String public let cipher: String - public init(bridgeAddress: IPv4Address, bridgePort: UInt16, password: String, cipher: String) { - self.bridgeAddress = bridgeAddress - self.bridgePort = bridgePort + public init(address: AnyIPAddress, port: UInt16, password: String, cipher: String) { + self.address = address + self.port = port self.password = password self.cipher = cipher } diff --git a/ios/MullvadREST/Transport/Shadowsocks/ShadowsocksTransport.swift b/ios/MullvadREST/Transport/Shadowsocks/ShadowsocksTransport.swift index c800a937badc..687bf4ff04b7 100644 --- a/ios/MullvadREST/Transport/Shadowsocks/ShadowsocksTransport.swift +++ b/ios/MullvadREST/Transport/Shadowsocks/ShadowsocksTransport.swift @@ -34,8 +34,8 @@ public final class ShadowsocksTransport: RESTTransport { shadowsocksProxy = ShadowsocksProxy( forwardAddress: apiAddress.ip, forwardPort: apiAddress.port, - bridgeAddress: configuration.bridgeAddress, - bridgePort: configuration.bridgePort, + bridgeAddress: configuration.address, + bridgePort: configuration.port, password: configuration.password, cipher: configuration.cipher ) diff --git a/ios/MullvadREST/Transport/Socks5/AnyIPEndpoint+Socks5.swift b/ios/MullvadREST/Transport/Socks5/AnyIPEndpoint+Socks5.swift index 986f8276fab3..87624656f923 100644 --- a/ios/MullvadREST/Transport/Socks5/AnyIPEndpoint+Socks5.swift +++ b/ios/MullvadREST/Transport/Socks5/AnyIPEndpoint+Socks5.swift @@ -20,14 +20,4 @@ extension AnyIPEndpoint { .ipv6(endpoint) } } - - /// Convert `AnyIPEndpoint` to `NWEndpoint`. - var nwEndpoint: NWEndpoint { - switch self { - case let .ipv4(endpoint): - .hostPort(host: .ipv4(endpoint.ip), port: NWEndpoint.Port(integerLiteral: endpoint.port)) - case let .ipv6(endpoint): - .hostPort(host: .ipv6(endpoint.ip), port: NWEndpoint.Port(integerLiteral: endpoint.port)) - } - } } diff --git a/ios/MullvadREST/Transport/Socks5/Socks5Configuration.swift b/ios/MullvadREST/Transport/Socks5/Socks5Configuration.swift index 39033821d459..2ec3a94b00dd 100644 --- a/ios/MullvadREST/Transport/Socks5/Socks5Configuration.swift +++ b/ios/MullvadREST/Transport/Socks5/Socks5Configuration.swift @@ -8,14 +8,25 @@ import Foundation import MullvadTypes +import Network /// Socks5 configuration. /// - See: ``URLSessionSocks5Transport`` public struct Socks5Configuration { - /// The socks proxy endpoint. - public var proxyEndpoint: AnyIPEndpoint + public let address: AnyIPAddress + public let port: UInt16 - public init(proxyEndpoint: AnyIPEndpoint) { - self.proxyEndpoint = proxyEndpoint + public init(address: AnyIPAddress, port: UInt16) { + self.address = address + self.port = port + } + + var nwEndpoint: NWEndpoint { + switch self.address { + case let .ipv4(endpoint): + .hostPort(host: .ipv4(endpoint), port: NWEndpoint.Port(integerLiteral: port)) + case let .ipv6(endpoint): + .hostPort(host: .ipv6(endpoint), port: NWEndpoint.Port(integerLiteral: port)) + } } } diff --git a/ios/MullvadREST/Transport/Socks5/URLSessionSocks5Transport.swift b/ios/MullvadREST/Transport/Socks5/URLSessionSocks5Transport.swift index f1075692e602..89121db0d0db 100644 --- a/ios/MullvadREST/Transport/Socks5/URLSessionSocks5Transport.swift +++ b/ios/MullvadREST/Transport/Socks5/URLSessionSocks5Transport.swift @@ -45,7 +45,7 @@ public class URLSessionSocks5Transport: RESTTransport { let apiAddress = addressCache.getCurrentEndpoint() socksProxy = Socks5ForwardingProxy( - socksProxyEndpoint: configuration.proxyEndpoint.nwEndpoint, + socksProxyEndpoint: configuration.nwEndpoint, remoteServerEndpoint: apiAddress.socksEndpoint ) diff --git a/ios/MullvadREST/Transport/TransportProvider.swift b/ios/MullvadREST/Transport/TransportProvider.swift index 1bd629d79b16..d0baf7c09265 100644 --- a/ios/MullvadREST/Transport/TransportProvider.swift +++ b/ios/MullvadREST/Transport/TransportProvider.swift @@ -81,10 +81,7 @@ public final class TransportProvider: RESTTransportProvider { private func socks5() -> RESTTransport? { return URLSessionSocks5Transport( urlSession: urlSessionTransport.urlSession, - configuration: Socks5Configuration(proxyEndpoint: AnyIPEndpoint.ipv4(IPv4Endpoint( - ip: .loopback, - port: 8889 - ))), + configuration: Socks5Configuration(address: .ipv4(.loopback), port: 8889), addressCache: addressCache ) } @@ -113,8 +110,8 @@ public final class TransportProvider: RESTTransportProvider { guard let bridgeAddress = closestRelay?.ipv4AddrIn, let bridgeConfiguration else { throw POSIXError(.ENOENT) } let newConfiguration = ShadowsocksConfiguration( - bridgeAddress: bridgeAddress, - bridgePort: bridgeConfiguration.port, + address: .ipv4(bridgeAddress), + port: bridgeConfiguration.port, password: bridgeConfiguration.password, cipher: bridgeConfiguration.cipher ) diff --git a/ios/MullvadSettings/AccessMethodKind.swift b/ios/MullvadSettings/AccessMethodKind.swift new file mode 100644 index 000000000000..7f8ab380dce5 --- /dev/null +++ b/ios/MullvadSettings/AccessMethodKind.swift @@ -0,0 +1,57 @@ +// +// AccessMethodKind.swift +// MullvadVPN +// +// Created by pronebird on 02/11/2023. +// Copyright © 2023 Mullvad VPN AB. All rights reserved. +// + +import Foundation + +/// A kind of API access method. +public enum AccessMethodKind: Equatable, Hashable, CaseIterable { + /// Direct communication. + case direct + + /// Communication over bridges. + case bridges + + /// Communication over shadowsocks. + case shadowsocks + + /// Communication over socks v5 proxy. + case socks5 +} + +public extension AccessMethodKind { + /// Returns `true` if the method is permanent and cannot be deleted. + var isPermanent: Bool { + switch self { + case .direct, .bridges: + true + case .shadowsocks, .socks5: + false + } + } + + /// Returns all access method kinds that can be added by user. + static var allUserDefinedKinds: [AccessMethodKind] { + allCases.filter { !$0.isPermanent } + } +} + +extension PersistentAccessMethod { + /// A kind of access method. + public var kind: AccessMethodKind { + switch proxyConfiguration { + case .direct: + .direct + case .bridges: + .bridges + case .shadowsocks: + .shadowsocks + case .socks5: + .socks5 + } + } +} diff --git a/ios/MullvadSettings/AccessMethodRepository.swift b/ios/MullvadSettings/AccessMethodRepository.swift new file mode 100644 index 000000000000..158501cd7d85 --- /dev/null +++ b/ios/MullvadSettings/AccessMethodRepository.swift @@ -0,0 +1,118 @@ +// +// AccessMethodRepository.swift +// MullvadVPN +// +// Created by Jon Petersson on 12/12/2023. +// Copyright © 2023 Mullvad VPN AB. All rights reserved. +// + +import Combine +import Foundation + +public class AccessMethodRepository: AccessMethodRepositoryProtocol { + let passthroughSubject: CurrentValueSubject<[PersistentAccessMethod], Never> = CurrentValueSubject([ + PersistentAccessMethod( + id: UUID(uuidString: "C9DB7457-2A55-42C3-A926-C07F82131994")!, + name: "", + isEnabled: true, + proxyConfiguration: .direct + ), + PersistentAccessMethod( + id: UUID(uuidString: "8586E75A-CA7B-4432-B70D-EE65F3F95084")!, + name: "", + isEnabled: true, + proxyConfiguration: .bridges + ), + ]) + + public var publisher: AnyPublisher<[PersistentAccessMethod], Never> { + passthroughSubject.eraseToAnyPublisher() + } + + public var accessMethods: [PersistentAccessMethod] { + passthroughSubject.value + } + + public static let shared = AccessMethodRepository() + + private init() { + add(passthroughSubject.value) + } + + public func add(_ method: PersistentAccessMethod) { + add([method]) + } + + public func add(_ methods: [PersistentAccessMethod]) { + var storedMethods = fetchAll() + + methods.forEach { method in + guard !storedMethods.contains(where: { $0.id == method.id }) else { return } + storedMethods.append(method) + } + + do { + try writeApiAccessMethods(storedMethods) + } catch { + print("Could not add access method(s): \(methods) \nError: \(error)") + } + } + + public func update(_ method: PersistentAccessMethod) { + var methods = fetchAll() + + guard let index = methods.firstIndex(where: { $0.id == method.id }) else { return } + methods[index] = method + + do { + try writeApiAccessMethods(methods) + } catch { + print("Could not update access method: \(method) \nError: \(error)") + } + } + + public func delete(id: UUID) { + var methods = fetchAll() + guard let index = methods.firstIndex(where: { $0.id == id }) else { return } + + // Prevent removing methods that have static UUIDs and are always present. + let method = methods[index] + if !method.kind.isPermanent { + methods.remove(at: index) + } + + do { + try writeApiAccessMethods(methods) + } catch { + print("Could not delete access method with id: \(id) \nError: \(error)") + } + } + + public func fetch(by id: UUID) -> PersistentAccessMethod? { + fetchAll().first { $0.id == id } + } + + public func fetchAll() -> [PersistentAccessMethod] { + (try? readApiAccessMethods()) ?? [] + } + + private func readApiAccessMethods() throws -> [PersistentAccessMethod] { + let parser = makeParser() + let data = try SettingsManager.store.read(key: .apiAccessMethods) + + return try parser.parseUnversionedPayload(as: [PersistentAccessMethod].self, from: data) + } + + private func writeApiAccessMethods(_ accessMethods: [PersistentAccessMethod]) throws { + let parser = makeParser() + let data = try parser.produceUnversionedPayload(accessMethods) + + try SettingsManager.store.write(data, for: .apiAccessMethods) + + passthroughSubject.send(accessMethods) + } + + private func makeParser() -> SettingsParser { + SettingsParser(decoder: JSONDecoder(), encoder: JSONEncoder()) + } +} diff --git a/ios/MullvadVPN/AccessMethodRepository/AccessMethodRepositoryProtocol.swift b/ios/MullvadSettings/AccessMethodRepositoryProtocol.swift similarity index 84% rename from ios/MullvadVPN/AccessMethodRepository/AccessMethodRepositoryProtocol.swift rename to ios/MullvadSettings/AccessMethodRepositoryProtocol.swift index 213f524bccef..87fbb00c8928 100644 --- a/ios/MullvadVPN/AccessMethodRepository/AccessMethodRepositoryProtocol.swift +++ b/ios/MullvadSettings/AccessMethodRepositoryProtocol.swift @@ -9,10 +9,12 @@ import Combine import Foundation -protocol AccessMethodRepositoryProtocol { +public protocol AccessMethodRepositoryDataSource { /// Publisher that propagates a snapshot of persistent store upon modifications. - var publisher: PassthroughSubject<[PersistentAccessMethod], Never> { get } + var publisher: AnyPublisher<[PersistentAccessMethod], Never> { get } +} +public protocol AccessMethodRepositoryProtocol: AccessMethodRepositoryDataSource { /// Add new access method. /// - Parameter method: persistent access method model. func add(_ method: PersistentAccessMethod) diff --git a/ios/MullvadSettings/PersistentAccessMethod.swift b/ios/MullvadSettings/PersistentAccessMethod.swift new file mode 100644 index 000000000000..9b4c48a12a0c --- /dev/null +++ b/ios/MullvadSettings/PersistentAccessMethod.swift @@ -0,0 +1,100 @@ +// +// PersistentAccessMethod.swift +// MullvadVPN +// +// Created by pronebird on 15/11/2023. +// Copyright © 2023 Mullvad VPN AB. All rights reserved. +// + +import Foundation +import MullvadTypes +import Network + +/// Persistent access method model. +public struct PersistentAccessMethod: Identifiable, Codable, Equatable { + /// The unique identifier used for referencing the access method entry in a persistent store. + public var id: UUID + + /// The user-defined name for access method. + public var name: String + + /// The flag indicating whether configuration is enabled. + public var isEnabled: Bool + + /// Proxy configuration. + public var proxyConfiguration: PersistentProxyConfiguration + + public init(id: UUID, name: String, isEnabled: Bool, proxyConfiguration: PersistentProxyConfiguration) { + self.id = id + self.name = name + self.isEnabled = isEnabled + self.proxyConfiguration = proxyConfiguration + } + + public static func == (lhs: Self, rhs: Self) -> Bool { + lhs.id == rhs.id + } +} + +/// Persistent proxy configuration. +public enum PersistentProxyConfiguration: Codable { + /// Direct communication without proxy. + case direct + + /// Communication over bridges. + case bridges + + /// Communication over shadowsocks. + case shadowsocks(ShadowsocksConfiguration) + + /// Communication over socks5 proxy. + case socks5(SocksConfiguration) +} + +extension PersistentProxyConfiguration { + /// Socks autentication method. + public enum SocksAuthentication: Codable { + case noAuthentication + case usernamePassword(username: String, password: String) + } + + /// Socks v5 proxy configuration. + public struct SocksConfiguration: Codable { + /// Proxy server address. + public var server: AnyIPAddress + + /// Proxy server port. + public var port: UInt16 + + /// Authentication method. + public var authentication: SocksAuthentication + + public init(server: AnyIPAddress, port: UInt16, authentication: SocksAuthentication) { + self.server = server + self.port = port + self.authentication = authentication + } + } + + /// Shadowsocks configuration. + public struct ShadowsocksConfiguration: Codable { + /// Server address. + public var server: AnyIPAddress + + /// Server port. + public var port: UInt16 + + /// Server password. + public var password: String + + /// Server cipher. + public var cipher: ShadowsocksCipherOptions + + public init(server: AnyIPAddress, port: UInt16, password: String, cipher: ShadowsocksCipherOptions) { + self.server = server + self.port = port + self.password = password + self.cipher = cipher + } + } +} diff --git a/ios/MullvadSettings/ShadowsocksCipherOptions.swift b/ios/MullvadSettings/ShadowsocksCipherOptions.swift new file mode 100644 index 000000000000..6d1bcab7cb43 --- /dev/null +++ b/ios/MullvadSettings/ShadowsocksCipherOptions.swift @@ -0,0 +1,52 @@ +// +// ShadowsocksCipherOptions.swift +// MullvadVPN +// +// Created by pronebird on 13/11/2023. +// Copyright © 2023 Mullvad VPN AB. All rights reserved. +// + +import Foundation + +public struct ShadowsocksCipherOptions: RawRepresentable, Codable, Hashable { + public let rawValue: CipherIdentifiers + + public init(rawValue: CipherIdentifiers) { + self.rawValue = rawValue + } + + /// Default cipher. + public static let `default` = ShadowsocksCipherOptions(rawValue: .CHACHA20) + + /// All supported ciphers. + public static let all = CipherIdentifiers.allCases.map { ShadowsocksCipherOptions(rawValue: $0) } +} + +public enum CipherIdentifiers: String, CaseIterable, CustomStringConvertible, Codable { + // Stream ciphers. + case CFB_AES128 = "aes-128-cfb" + case CFB1_AES128 = "aes-128-cfb1" + case CFB8_AES128 = "aes-128-cfb8" + case CFB128_AES128 = "aes-128-cfb128" + case CFB_AES256 = "aes-256-cfb" + case CFB1_AES256 = "aes-256-cfb1" + case CFB8_AES256 = "aes-256-cfb8" + case CFB128_AES256 = "aes-256-cfb128" + case RC4 = "rc4" + case RC4_MD5 = "rc4-md5" + case CHACHA20 = "chacha20" + case SALSA20 = "salsa20" + case CHACHA20_IETF = "chacha20-ietf" + + // AEAD ciphers. + case GCM_AES128 = "aes-128-gcm" + case GCM_AES256 = "aes-256-gcm" + case CHACHA20_IETF_POLY1305 = "chacha20-ietf-poly1305" + case XCHACHA20_IETF_POLY1305 = "xchacha20-ietf-poly1305" + case PMAC_SIV_AES128 = "aes-128-pmac-siv" + case GPMAC_SIV_AES256 = "aes-256-pmac-siv" + + public var description: String { + rawValue + } +} diff --git a/ios/MullvadVPN.xcodeproj/project.pbxproj b/ios/MullvadVPN.xcodeproj/project.pbxproj index b0a92a69fc1c..2db19e73351c 100644 --- a/ios/MullvadVPN.xcodeproj/project.pbxproj +++ b/ios/MullvadVPN.xcodeproj/project.pbxproj @@ -75,7 +75,6 @@ 5827B09B2B0DEEF800CCBBA1 /* AccessMethodActionSheetPresentationDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5827B09A2B0DEEF800CCBBA1 /* AccessMethodActionSheetPresentationDelegate.swift */; }; 5827B09D2B0DEF1000CCBBA1 /* AccessMethodActionSheetDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5827B09C2B0DEF1000CCBBA1 /* AccessMethodActionSheetDelegate.swift */; }; 5827B09F2B0E05E600CCBBA1 /* AddAccessMethodInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5827B09E2B0E05E600CCBBA1 /* AddAccessMethodInteractor.swift */; }; - 5827B0A12B0E064E00CCBBA1 /* AccessMethodRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5827B0A02B0E064E00CCBBA1 /* AccessMethodRepository.swift */; }; 5827B0A42B0F38FD00CCBBA1 /* EditAccessMethodInteractorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5827B0A32B0F38FD00CCBBA1 /* EditAccessMethodInteractorProtocol.swift */; }; 5827B0A62B0F39E900CCBBA1 /* EditAccessMethodInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5827B0A52B0F39E900CCBBA1 /* EditAccessMethodInteractor.swift */; }; 5827B0A82B0F49EF00CCBBA1 /* ProxyConfigurationInteractorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5827B0A72B0F49EF00CCBBA1 /* ProxyConfigurationInteractorProtocol.swift */; }; @@ -165,7 +164,6 @@ 586C0D912B03D8A400E7CDD7 /* AccessMethodHeaderFooterReuseIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 586C0D902B03D8A400E7CDD7 /* AccessMethodHeaderFooterReuseIdentifier.swift */; }; 586C0D932B03D90700E7CDD7 /* ShadowsocksItemIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 586C0D922B03D90700E7CDD7 /* ShadowsocksItemIdentifier.swift */; }; 586C0D952B03D92100E7CDD7 /* SocksItemIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 586C0D942B03D92100E7CDD7 /* SocksItemIdentifier.swift */; }; - 586C0D972B04E0AC00E7CDD7 /* PersistentAccessMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 586C0D962B04E0AC00E7CDD7 /* PersistentAccessMethod.swift */; }; 586C0D992B04E20200E7CDD7 /* AccessMethodViewModel+Persistent.swift in Sources */ = {isa = PBXBuildFile; fileRef = 586C0D982B04E20200E7CDD7 /* AccessMethodViewModel+Persistent.swift */; }; 586C0D9B2B051E6900E7CDD7 /* AccessMethodActionSheetContainerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 586C0D9A2B051E6900E7CDD7 /* AccessMethodActionSheetContainerView.swift */; }; 586C0D9D2B05F62B00E7CDD7 /* AccessMethodActionSheetContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 586C0D9C2B05F62B00E7CDD7 /* AccessMethodActionSheetContentView.swift */; }; @@ -212,7 +210,6 @@ 5888AD83227B11080051EB06 /* SelectLocationCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5888AD82227B11080051EB06 /* SelectLocationCell.swift */; }; 5888AD87227B17950051EB06 /* SelectLocationViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5888AD86227B17950051EB06 /* SelectLocationViewController.swift */; }; 588D7ED62AF3903F005DF40A /* ListAccessMethodViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 588D7ED52AF3903F005DF40A /* ListAccessMethodViewController.swift */; }; - 588D7ED82AF3A533005DF40A /* AccessMethodKind+Extensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 588D7ED72AF3A533005DF40A /* AccessMethodKind+Extensions.swift */; }; 588D7EDC2AF3A55E005DF40A /* ListAccessMethodInteractorProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 588D7EDB2AF3A55E005DF40A /* ListAccessMethodInteractorProtocol.swift */; }; 588D7EDE2AF3A585005DF40A /* ListAccessMethodItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 588D7EDD2AF3A585005DF40A /* ListAccessMethodItem.swift */; }; 588D7EE02AF3A595005DF40A /* ListAccessMethodInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 588D7EDF2AF3A595005DF40A /* ListAccessMethodInteractor.swift */; }; @@ -402,7 +399,6 @@ 58DFF7D22B0256A300F864E0 /* MarkdownStylingOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58DFF7D12B0256A300F864E0 /* MarkdownStylingOptions.swift */; }; 58DFF7D32B02570000F864E0 /* MarkdownStylingOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58DFF7D12B0256A300F864E0 /* MarkdownStylingOptions.swift */; }; 58DFF7D82B02774C00F864E0 /* ListItemPickerViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58DFF7D72B02774C00F864E0 /* ListItemPickerViewController.swift */; }; - 58DFF7DA2B02862E00F864E0 /* ShadowsocksCipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58DFF7D92B02862E00F864E0 /* ShadowsocksCipher.swift */; }; 58E0729F28814ACC008902F8 /* WireGuardLogLevel+Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58E0729E28814ACC008902F8 /* WireGuardLogLevel+Logging.swift */; }; 58E0A98827C8F46300FE6BDD /* Tunnel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58E0A98727C8F46300FE6BDD /* Tunnel.swift */; }; 58E11188292FA11F009FCA84 /* SettingsMigrationUIHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58E11187292FA11F009FCA84 /* SettingsMigrationUIHandler.swift */; }; @@ -424,7 +420,6 @@ 58EF87532B161D7C00C098B2 /* AccessMethodActionSheetConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58EF87522B161D7C00C098B2 /* AccessMethodActionSheetConfiguration.swift */; }; 58EF87552B16282D00C098B2 /* AccessMethodActionSheetPresentationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58EF87542B16282D00C098B2 /* AccessMethodActionSheetPresentationView.swift */; }; 58EF87572B16330B00C098B2 /* ProxyConfigurationTester.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58EF87562B16330B00C098B2 /* ProxyConfigurationTester.swift */; }; - 58EF875B2B16385400C098B2 /* AccessMethodRepositoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58EF875A2B16385400C098B2 /* AccessMethodRepositoryProtocol.swift */; }; 58EF875D2B1638BF00C098B2 /* ProxyConfigurationTesterProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58EF875C2B1638BF00C098B2 /* ProxyConfigurationTesterProtocol.swift */; }; 58EFC76A2AFAC3B800E9F4CB /* ListAccessMethodHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58EFC7692AFAC3B800E9F4CB /* ListAccessMethodHeaderView.swift */; }; 58EFC76E2AFB3BDA00E9F4CB /* ListAccessMethodCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58EFC76D2AFB3BDA00E9F4CB /* ListAccessMethodCoordinator.swift */; }; @@ -454,7 +449,6 @@ 58FDF2D92A0BA11A00C2B061 /* DeviceCheckOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FDF2D82A0BA11900C2B061 /* DeviceCheckOperation.swift */; }; 58FE25BB2AA72188003D1918 /* MullvadLogging.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58D223F3294C8FF00029F5F8 /* MullvadLogging.framework */; }; 58FE25BF2AA72311003D1918 /* MigrationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A9D96B192A8247C100A5C673 /* MigrationManager.swift */; }; - 58FE25C22AA72729003D1918 /* MullvadREST.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 06799ABC28F98E1D00ACD94E /* MullvadREST.framework */; }; 58FE25C62AA72779003D1918 /* PacketTunnelCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58C7A4362A863F440060C66F /* PacketTunnelCore.framework */; }; 58FE25CE2AA72802003D1918 /* MullvadSettings.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58B2FDD32AA71D2A003EB5C6 /* MullvadSettings.framework */; }; 58FE25D42AA729B5003D1918 /* PacketTunnelActorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58FE25D32AA729B5003D1918 /* PacketTunnelActorTests.swift */; }; @@ -521,10 +515,6 @@ 7A7AD28D29DC677800480EF1 /* FirstTimeLaunch.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A7AD28C29DC677800480EF1 /* FirstTimeLaunch.swift */; }; 7A818F1F29F0305800C7F0F4 /* RootConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A818F1E29F0305800C7F0F4 /* RootConfiguration.swift */; }; 7A83A0C62B29A750008B5CE7 /* APIAccessMethodsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A83A0C52B29A750008B5CE7 /* APIAccessMethodsTests.swift */; }; - 7A83A0C72B29A831008B5CE7 /* AccessMethodRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5827B0A02B0E064E00CCBBA1 /* AccessMethodRepository.swift */; }; - 7A83A0C82B29A851008B5CE7 /* PersistentAccessMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 586C0D962B04E0AC00E7CDD7 /* PersistentAccessMethod.swift */; }; - 7A83A0C92B29AA8C008B5CE7 /* AccessMethodRepositoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58EF875A2B16385400C098B2 /* AccessMethodRepositoryProtocol.swift */; }; - 7A83A0CA2B29AAB5008B5CE7 /* ShadowsocksCipher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58DFF7D92B02862E00F864E0 /* ShadowsocksCipher.swift */; }; 7A83C3FF2A55B72E00DFB83A /* MullvadVPNApp.xctestplan in Resources */ = {isa = PBXBuildFile; fileRef = 7A83C3FE2A55B72E00DFB83A /* MullvadVPNApp.xctestplan */; }; 7A83C4022A57FAA800DFB83A /* SettingsDNSInfoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7A83C4012A57FAA800DFB83A /* SettingsDNSInfoCell.swift */; }; 7A88DCD82A8FABBE00D2FF0E /* Routing.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7A88DCCE2A8FABBE00D2FF0E /* Routing.framework */; }; @@ -746,6 +736,9 @@ F07BF2622A26279100042943 /* RedeemVoucherOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = F07BF2612A26279100042943 /* RedeemVoucherOperation.swift */; }; F07C9D952B220C77006F1C5E /* libshadowsocks_proxy.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 01F1FF1D29F0627D007083C3 /* libshadowsocks_proxy.a */; }; F07CFF2029F2720E008C0343 /* RegisteredDeviceInAppNotificationProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = F07CFF1F29F2720E008C0343 /* RegisteredDeviceInAppNotificationProvider.swift */; }; + F08827872B318C840020A383 /* ShadowsocksCipherOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58DFF7D92B02862E00F864E0 /* ShadowsocksCipherOptions.swift */; }; + F08827882B318F960020A383 /* PersistentAccessMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 586C0D962B04E0AC00E7CDD7 /* PersistentAccessMethod.swift */; }; + F08827892B3192110020A383 /* AccessMethodRepositoryProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58EF875A2B16385400C098B2 /* AccessMethodRepositoryProtocol.swift */; }; F09A297B2A9F8A9B00EA3B6F /* LogoutDialogueView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F09A29782A9F8A9B00EA3B6F /* LogoutDialogueView.swift */; }; F09A297C2A9F8A9B00EA3B6F /* VoucherTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = F09A29792A9F8A9B00EA3B6F /* VoucherTextField.swift */; }; F09A297D2A9F8A9B00EA3B6F /* RedeemVoucherContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F09A297A2A9F8A9B00EA3B6F /* RedeemVoucherContentView.swift */; }; @@ -760,8 +753,12 @@ F09D04C12AF39EA2003D4F89 /* OutgoingConnectionService.swift in Sources */ = {isa = PBXBuildFile; fileRef = F09D04BC2AEBB7C5003D4F89 /* OutgoingConnectionService.swift */; }; F0B0E6972AFE6E7E001DC66B /* XCTest+Async.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0B0E6962AFE6E7E001DC66B /* XCTest+Async.swift */; }; F0C2AEFD2A0BB5CC00986207 /* NotificationProviderIdentifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0C2AEFC2A0BB5CC00986207 /* NotificationProviderIdentifier.swift */; }; + F0C3333C2B31A29C00D1A478 /* MullvadSettings.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 58B2FDD32AA71D2A003EB5C6 /* MullvadSettings.framework */; }; F0C6A8432AB08E54000777A8 /* RedeemVoucherViewConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0C6A8422AB08E54000777A8 /* RedeemVoucherViewConfiguration.swift */; }; F0C6FA852A6A733700F521F0 /* InAppPurchaseInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0C6FA842A6A733700F521F0 /* InAppPurchaseInteractor.swift */; }; + F0D7FF8F2B31DF5900E0FDE5 /* AccessMethodRepository.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5827B0A02B0E064E00CCBBA1 /* AccessMethodRepository.swift */; }; + F0D7FF902B31E00B00E0FDE5 /* AccessMethodKind.swift in Sources */ = {isa = PBXBuildFile; fileRef = 588D7ED72AF3A533005DF40A /* AccessMethodKind.swift */; }; + F0D7FF922B31E05D00E0FDE5 /* AccessMethodKind+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0D7FF912B31E05D00E0FDE5 /* AccessMethodKind+Extension.swift */; }; F0D8825B2B04F53600D3EF9A /* OutgoingConnectionData.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0D8825A2B04F53600D3EF9A /* OutgoingConnectionData.swift */; }; F0D8825C2B04F70E00D3EF9A /* OutgoingConnectionData.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0D8825A2B04F53600D3EF9A /* OutgoingConnectionData.swift */; }; F0DA87472A9CB9A2006044F1 /* AccountExpiryRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = F0DA87462A9CB9A2006044F1 /* AccountExpiryRow.swift */; }; @@ -984,13 +981,6 @@ remoteGlobalIDString = 58D223F2294C8FF00029F5F8; remoteInfo = MullvadLogging; }; - 58FE25C42AA72729003D1918 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 58CE5E58224146200008646E /* Project object */; - proxyType = 1; - remoteGlobalIDString = 06799ABB28F98E1D00ACD94E; - remoteInfo = MullvadREST; - }; 58FE25C82AA72779003D1918 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 58CE5E58224146200008646E /* Project object */; @@ -1426,7 +1416,7 @@ 5888AD82227B11080051EB06 /* SelectLocationCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectLocationCell.swift; sourceTree = ""; }; 5888AD86227B17950051EB06 /* SelectLocationViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectLocationViewController.swift; sourceTree = ""; }; 588D7ED52AF3903F005DF40A /* ListAccessMethodViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListAccessMethodViewController.swift; sourceTree = ""; }; - 588D7ED72AF3A533005DF40A /* AccessMethodKind+Extensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AccessMethodKind+Extensions.swift"; sourceTree = ""; }; + 588D7ED72AF3A533005DF40A /* AccessMethodKind.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccessMethodKind.swift; sourceTree = ""; }; 588D7EDB2AF3A55E005DF40A /* ListAccessMethodInteractorProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListAccessMethodInteractorProtocol.swift; sourceTree = ""; }; 588D7EDD2AF3A585005DF40A /* ListAccessMethodItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListAccessMethodItem.swift; sourceTree = ""; }; 588D7EDF2AF3A595005DF40A /* ListAccessMethodInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListAccessMethodInteractor.swift; sourceTree = ""; }; @@ -1554,7 +1544,7 @@ 58DFF7CF2B02560400F864E0 /* NSAttributedString+Markdown.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSAttributedString+Markdown.swift"; sourceTree = ""; }; 58DFF7D12B0256A300F864E0 /* MarkdownStylingOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MarkdownStylingOptions.swift; sourceTree = ""; }; 58DFF7D72B02774C00F864E0 /* ListItemPickerViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListItemPickerViewController.swift; sourceTree = ""; }; - 58DFF7D92B02862E00F864E0 /* ShadowsocksCipher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShadowsocksCipher.swift; sourceTree = ""; }; + 58DFF7D92B02862E00F864E0 /* ShadowsocksCipherOptions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShadowsocksCipherOptions.swift; sourceTree = ""; }; 58E07298288031D5008902F8 /* WireGuardAdapterError+Localization.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WireGuardAdapterError+Localization.swift"; sourceTree = ""; }; 58E0729E28814ACC008902F8 /* WireGuardLogLevel+Logging.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "WireGuardLogLevel+Logging.swift"; sourceTree = ""; }; 58E0A98727C8F46300FE6BDD /* Tunnel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tunnel.swift; sourceTree = ""; }; @@ -1793,6 +1783,7 @@ F0C2AEFC2A0BB5CC00986207 /* NotificationProviderIdentifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationProviderIdentifier.swift; sourceTree = ""; }; F0C6A8422AB08E54000777A8 /* RedeemVoucherViewConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RedeemVoucherViewConfiguration.swift; sourceTree = ""; }; F0C6FA842A6A733700F521F0 /* InAppPurchaseInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InAppPurchaseInteractor.swift; sourceTree = ""; }; + F0D7FF912B31E05D00E0FDE5 /* AccessMethodKind+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AccessMethodKind+Extension.swift"; sourceTree = ""; }; F0D8825A2B04F53600D3EF9A /* OutgoingConnectionData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = OutgoingConnectionData.swift; sourceTree = ""; }; F0DA87462A9CB9A2006044F1 /* AccountExpiryRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountExpiryRow.swift; sourceTree = ""; }; F0DA87482A9CBA9F006044F1 /* AccountDeviceRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AccountDeviceRow.swift; sourceTree = ""; }; @@ -1822,6 +1813,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + F0C3333C2B31A29C00D1A478 /* MullvadSettings.framework in Frameworks */, 58D223BF294C8AE90029F5F8 /* Operations.framework in Frameworks */, 586A0DD12A20E371006C731C /* WireGuardKitTypes in Frameworks */, 58D2241D294C91D20029F5F8 /* MullvadLogging.framework in Frameworks */, @@ -1870,7 +1862,6 @@ files = ( 58FE25BB2AA72188003D1918 /* MullvadLogging.framework in Frameworks */, 58B2FDEB2AA72049003EB5C6 /* WireGuardKitTypes in Frameworks */, - 58FE25C22AA72729003D1918 /* MullvadREST.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -2113,8 +2104,8 @@ isa = PBXGroup; children = ( 586C0D772B039CC000E7CDD7 /* AccessMethodProtocolPicker.swift */, - 586C0D792B039CE300E7CDD7 /* ShadowsocksCipherPicker.swift */, 58DFF7D72B02774C00F864E0 /* ListItemPickerViewController.swift */, + 586C0D792B039CE300E7CDD7 /* ShadowsocksCipherPicker.swift */, ); path = Pickers; sourceTree = ""; @@ -2147,12 +2138,8 @@ 5827B0A22B0E068800CCBBA1 /* AccessMethodRepository */ = { isa = PBXGroup; children = ( - 5827B0A02B0E064E00CCBBA1 /* AccessMethodRepository.swift */, - 58EF875A2B16385400C098B2 /* AccessMethodRepositoryProtocol.swift */, - 586C0D962B04E0AC00E7CDD7 /* PersistentAccessMethod.swift */, 58EF87562B16330B00C098B2 /* ProxyConfigurationTester.swift */, 58EF875C2B1638BF00C098B2 /* ProxyConfigurationTesterProtocol.swift */, - 58DFF7D92B02862E00F864E0 /* ShadowsocksCipher.swift */, ); path = AccessMethodRepository; sourceTree = ""; @@ -2578,7 +2565,7 @@ 586C0D7D2B03BDE500E7CDD7 /* Models */ = { isa = PBXGroup; children = ( - 588D7ED72AF3A533005DF40A /* AccessMethodKind+Extensions.swift */, + F0D7FF912B31E05D00E0FDE5 /* AccessMethodKind+Extension.swift */, 58FF9FF32B07C61B00E4C97D /* AccessMethodValidationError.swift */, 581DFAED2B178DEA005D6D1C /* AccessMethodValidationError+Helpers.swift */, 586C0D7B2B03BDD100E7CDD7 /* AccessMethodViewModel.swift */, @@ -2757,15 +2744,20 @@ 58B2FDD42AA71D2A003EB5C6 /* MullvadSettings */ = { isa = PBXGroup; children = ( + 588D7ED72AF3A533005DF40A /* AccessMethodKind.swift */, + 5827B0A02B0E064E00CCBBA1 /* AccessMethodRepository.swift */, + 58EF875A2B16385400C098B2 /* AccessMethodRepositoryProtocol.swift */, A92ECC2B2A7803A50052F1B1 /* DeviceState.swift */, 580F8B8528197958002E0998 /* DNSSettings.swift */, 06410DFD292CE18F00AFC18C /* KeychainSettingsStore.swift */, 068CE5732927B7A400A068BB /* Migration.swift */, A9D96B192A8247C100A5C673 /* MigrationManager.swift */, 58B2FDD52AA71D2A003EB5C6 /* MullvadSettings.h */, + 586C0D962B04E0AC00E7CDD7 /* PersistentAccessMethod.swift */, 58FF2C02281BDE02009EF542 /* SettingsManager.swift */, 06410E03292D0F7100AFC18C /* SettingsParser.swift */, 06410E06292D108E00AFC18C /* SettingsStore.swift */, + 58DFF7D92B02862E00F864E0 /* ShadowsocksCipherOptions.swift */, A92ECC232A7802520052F1B1 /* StoredAccountData.swift */, A92ECC272A7802AB0052F1B1 /* StoredDeviceData.swift */, A97D30162AE6B5E90045C0E4 /* StoredWgKeyData.swift */, @@ -2986,16 +2978,16 @@ isa = PBXGroup; children = ( 58EFC7742AFB4CEF00E9F4CB /* AboutViewController.swift */, - 586C0D802B03CA8400E7CDD7 /* CurrentValueSubject+UIActionBindings.swift */, - 5827B0BE2B14B37D00CCBBA1 /* Publisher+PreviousValue.swift */, - 5827B0982B0DC01400CCBBA1 /* Common */, + 58CEB2EB2AFBBCDD00E6E088 /* Add */, 586C0D7E2B03BE2F00E7CDD7 /* Cells */, + 5827B0982B0DC01400CCBBA1 /* Common */, + 586C0D802B03CA8400E7CDD7 /* CurrentValueSubject+UIActionBindings.swift */, + 58FF9FDE2B075AA700E4C97D /* Edit */, + 58CEB2EA2AFBBCBA00E6E088 /* List */, 586C0D7D2B03BDE500E7CDD7 /* Models */, 5827B0972B0DBF3400CCBBA1 /* Pickers */, + 5827B0BE2B14B37D00CCBBA1 /* Publisher+PreviousValue.swift */, 586C0DA02B05FAF200E7CDD7 /* Sheet */, - 58CEB2EB2AFBBCDD00E6E088 /* Add */, - 58FF9FDE2B075AA700E4C97D /* Edit */, - 58CEB2EA2AFBBCBA00E6E088 /* List */, ); path = APIAccess; sourceTree = ""; @@ -3661,7 +3653,6 @@ ); dependencies = ( 58FE25BE2AA72188003D1918 /* PBXTargetDependency */, - 58FE25C52AA72729003D1918 /* PBXTargetDependency */, ); name = MullvadSettings; packageProductDependencies = ( @@ -4238,7 +4229,7 @@ F0ACE1F32B21CB9A0045C1B8 /* Build Shadowsocks */ = { isa = PBXShellScriptBuildPhase; alwaysOutOfDate = 1; - buildActionMask = 2147483647; + buildActionMask = 12; files = ( ); inputFileListPaths = ( @@ -4377,7 +4368,6 @@ A9A5F9E12ACB05160083449F /* AddressCacheTracker.swift in Sources */, A900E9BC2ACC609200C95F67 /* DevicesProxy+Stubs.swift in Sources */, A9A5F9E22ACB05160083449F /* BackgroundTask.swift in Sources */, - 7A83A0C92B29AA8C008B5CE7 /* AccessMethodRepositoryProtocol.swift in Sources */, A9A5F9E32ACB05160083449F /* AccountDataThrottling.swift in Sources */, A9A5F9E42ACB05160083449F /* AppPreferences.swift in Sources */, A9A5F9E52ACB05160083449F /* CustomDateComponentsFormatting.swift in Sources */, @@ -4399,7 +4389,6 @@ A9A5F9F22ACB05160083449F /* NotificationConfiguration.swift in Sources */, A9A5F9F32ACB05160083449F /* AccountExpirySystemNotificationProvider.swift in Sources */, A9A5F9F52ACB05160083449F /* RegisteredDeviceInAppNotificationProvider.swift in Sources */, - 7A83A0CA2B29AAB5008B5CE7 /* ShadowsocksCipher.swift in Sources */, F09D04B72AE941DA003D4F89 /* OutgoingConnectionProxyTests.swift in Sources */, F09D04B92AE95111003D4F89 /* OutgoingConnectionProxy.swift in Sources */, A9A5F9F62ACB05160083449F /* TunnelStatusNotificationProvider.swift in Sources */, @@ -4419,7 +4408,6 @@ A9A5FA022ACB05160083449F /* RelayCacheTracker.swift in Sources */, A9A5FA032ACB05160083449F /* SimulatorTunnelInfo.swift in Sources */, A9A5FA042ACB05160083449F /* SimulatorTunnelProvider.swift in Sources */, - 7A83A0C72B29A831008B5CE7 /* AccessMethodRepository.swift in Sources */, A9A5FA052ACB05160083449F /* SimulatorTunnelProviderHost.swift in Sources */, A900E9C02ACC661900C95F67 /* AccessTokenManager+Stubs.swift in Sources */, A9E0317A2ACB0AE70095D843 /* UIApplication+Stubs.swift in Sources */, @@ -4479,7 +4467,6 @@ A9A5FA302ACB05160083449F /* InputTextFormatterTests.swift in Sources */, F0B0E6972AFE6E7E001DC66B /* XCTest+Async.swift in Sources */, A9A5FA312ACB05160083449F /* MockFileCache.swift in Sources */, - 7A83A0C82B29A851008B5CE7 /* PersistentAccessMethod.swift in Sources */, A9A5FA322ACB05160083449F /* RelayCacheTests.swift in Sources */, A9A5FA332ACB05160083449F /* RelaySelectorTests.swift in Sources */, 58DFF7D32B02570000F864E0 /* MarkdownStylingOptions.swift in Sources */, @@ -4496,6 +4483,7 @@ 58B2FDEE2AA72098003EB5C6 /* ApplicationConfiguration.swift in Sources */, 58B2FDE52AA71D5C003EB5C6 /* TunnelSettingsV2.swift in Sources */, A97D30172AE6B5E90045C0E4 /* StoredWgKeyData.swift in Sources */, + F08827882B318F960020A383 /* PersistentAccessMethod.swift in Sources */, 58B2FDE32AA71D5C003EB5C6 /* StoredDeviceData.swift in Sources */, 58B2FDDF2AA71D5C003EB5C6 /* DNSSettings.swift in Sources */, 58B2FDE02AA71D5C003EB5C6 /* TunnelSettings.swift in Sources */, @@ -4506,10 +4494,14 @@ 58B2FDEF2AA720C4003EB5C6 /* ApplicationTarget.swift in Sources */, A988DF272ADE86ED00D807EF /* WireGuardObfuscationSettings.swift in Sources */, 58B2FDDE2AA71D5C003EB5C6 /* Migration.swift in Sources */, + F0D7FF8F2B31DF5900E0FDE5 /* AccessMethodRepository.swift in Sources */, 58B2FDE12AA71D5C003EB5C6 /* TunnelSettingsV1.swift in Sources */, 58B2FDE72AA71D5C003EB5C6 /* SettingsStore.swift in Sources */, + F08827872B318C840020A383 /* ShadowsocksCipherOptions.swift in Sources */, 58B2FDE92AA71D5C003EB5C6 /* SettingsParser.swift in Sources */, + F08827892B3192110020A383 /* AccessMethodRepositoryProtocol.swift in Sources */, 58B2FDE22AA71D5C003EB5C6 /* StoredAccountData.swift in Sources */, + F0D7FF902B31E00B00E0FDE5 /* AccessMethodKind.swift in Sources */, 58B2FDE82AA71D5C003EB5C6 /* KeychainSettingsStore.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -4693,13 +4685,13 @@ 581DFAEC2B1770C1005D6D1C /* AccessMethodViewModel+NavigationItem.swift in Sources */, 58ACF64D26567A5000ACE4B7 /* CustomSwitch.swift in Sources */, F0DA874B2A9CBACB006044F1 /* AccountNumberRow.swift in Sources */, - 586C0D972B04E0AC00E7CDD7 /* PersistentAccessMethod.swift in Sources */, 58F2E14C276A61C000A79513 /* RotateKeyOperation.swift in Sources */, 5871FB96254ADE4E0051A0A4 /* ConsolidatedApplicationLog.swift in Sources */, 5827B09D2B0DEF1000CCBBA1 /* AccessMethodActionSheetDelegate.swift in Sources */, F0E8E4C52A60499100ED26A3 /* AccountDeletionViewController.swift in Sources */, 7A9CCCC12A96302800DD6A34 /* AccountCoordinator.swift in Sources */, 58FEEB58260B662E00A621A8 /* AutomaticKeyboardResponder.swift in Sources */, + F0D7FF922B31E05D00E0FDE5 /* AccessMethodKind+Extension.swift in Sources */, 58EF87532B161D7C00C098B2 /* AccessMethodActionSheetConfiguration.swift in Sources */, 5846227326E22A160035F7C2 /* StorePaymentObserver.swift in Sources */, F0E3618B2A4ADD2F00AEEF2B /* WelcomeContentView.swift in Sources */, @@ -4835,7 +4827,6 @@ 5878A27329091D6D0096FC88 /* TunnelBlockObserver.swift in Sources */, A9E034642ABB302000E59A5A /* UIEdgeInsets+Extensions.swift in Sources */, 58CEB2E92AFBBA4A00E6E088 /* AddAccessMethodCoordinator.swift in Sources */, - 58DFF7DA2B02862E00F864E0 /* ShadowsocksCipher.swift in Sources */, 58DFF7D02B02560400F864E0 /* NSAttributedString+Markdown.swift in Sources */, 58E0A98827C8F46300FE6BDD /* Tunnel.swift in Sources */, 7A12D0762B062D5C00E9602D /* URLSessionProtocol.swift in Sources */, @@ -4847,7 +4838,6 @@ 58B26E1E2943514300D5980C /* InAppNotificationDescriptor.swift in Sources */, 58421032282E42B000F24E46 /* UpdateDeviceDataOperation.swift in Sources */, 586C0D992B04E20200E7CDD7 /* AccessMethodViewModel+Persistent.swift in Sources */, - 58EF875B2B16385400C098B2 /* AccessMethodRepositoryProtocol.swift in Sources */, 5878A27D2909657C0096FC88 /* RevokedDeviceInteractor.swift in Sources */, 5827B09B2B0DEEF800CCBBA1 /* AccessMethodActionSheetPresentationDelegate.swift in Sources */, F0E8E4C32A602E0D00ED26A3 /* AccountDeletionViewModel.swift in Sources */, @@ -4874,14 +4864,12 @@ F028A56C2A34D8E600C0CAA3 /* AddCreditSucceededViewController.swift in Sources */, 581DFAEE2B178DEA005D6D1C /* AccessMethodValidationError+Helpers.swift in Sources */, 58293FAE2510CA58005D0BB5 /* ProblemReportViewController.swift in Sources */, - 588D7ED82AF3A533005DF40A /* AccessMethodKind+Extensions.swift in Sources */, 58B9EB152489139B00095626 /* RESTError+Display.swift in Sources */, 587B753F2668E5A700DEF7E9 /* NotificationContainerView.swift in Sources */, 58F2E144276A13F300A79513 /* StartTunnelOperation.swift in Sources */, 58CCA01E2242787B004F3011 /* AccountTextField.swift in Sources */, 586E54FB27A2DF6D0029B88B /* SendTunnelProviderMessageOperation.swift in Sources */, 584592612639B4A200EF967F /* TermsOfServiceContentView.swift in Sources */, - 5827B0A12B0E064E00CCBBA1 /* AccessMethodRepository.swift in Sources */, 5875960A26F371FC00BF6711 /* Tunnel+Messaging.swift in Sources */, 586C0D912B03D8A400E7CDD7 /* AccessMethodHeaderFooterReuseIdentifier.swift in Sources */, 7A2960F62A963F7500389B82 /* AlertCoordinator.swift in Sources */, @@ -5228,11 +5216,6 @@ target = 58D223F2294C8FF00029F5F8 /* MullvadLogging */; targetProxy = 58FE25BD2AA72188003D1918 /* PBXContainerItemProxy */; }; - 58FE25C52AA72729003D1918 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 06799ABB28F98E1D00ACD94E /* MullvadREST */; - targetProxy = 58FE25C42AA72729003D1918 /* PBXContainerItemProxy */; - }; 58FE25C92AA72779003D1918 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 58C7A4352A863F440060C66F /* PacketTunnelCore */; diff --git a/ios/MullvadVPN/AccessMethodRepository/PersistentAccessMethod.swift b/ios/MullvadVPN/AccessMethodRepository/PersistentAccessMethod.swift deleted file mode 100644 index b5d5ef2947ea..000000000000 --- a/ios/MullvadVPN/AccessMethodRepository/PersistentAccessMethod.swift +++ /dev/null @@ -1,124 +0,0 @@ -// -// PersistentAccessMethod.swift -// MullvadVPN -// -// Created by pronebird on 15/11/2023. -// Copyright © 2023 Mullvad VPN AB. All rights reserved. -// - -import Foundation -import MullvadTypes -import Network - -/// Persistent access method model. -struct PersistentAccessMethod: Identifiable, Codable { - /// The unique identifier used for referencing the access method entry in a persistent store. - var id: UUID - - /// The user-defined name for access method. - var name: String - - /// The flag indicating whether configuration is enabled. - var isEnabled: Bool - - /// Proxy configuration. - var proxyConfiguration: PersistentProxyConfiguration -} - -/// Persistent proxy configuration. -enum PersistentProxyConfiguration: Codable { - /// Direct communication without proxy. - case direct - - /// Communication over bridges. - case bridges - - /// Communication over shadowsocks. - case shadowsocks(ShadowsocksConfiguration) - - /// Communication over socks5 proxy. - case socks5(SocksConfiguration) -} - -extension PersistentProxyConfiguration { - /// Socks autentication method. - enum SocksAuthentication: Codable { - case noAuthentication - case usernamePassword(username: String, password: String) - } - - /// Socks v5 proxy configuration. - struct SocksConfiguration: Codable { - /// Proxy server address. - var server: AnyIPAddress - - /// Proxy server port. - var port: UInt16 - - /// Authentication method. - var authentication: SocksAuthentication - } - - /// Shadowsocks configuration. - struct ShadowsocksConfiguration: Codable { - /// Server address. - var server: AnyIPAddress - - /// Server port. - var port: UInt16 - - /// Server password. - var password: String - - /// Server cipher. - var cipher: ShadowsocksCipher - } -} - -extension PersistentAccessMethod { - /// A kind of access method. - var kind: AccessMethodKind { - switch proxyConfiguration { - case .direct: - .direct - case .bridges: - .bridges - case .shadowsocks: - .shadowsocks - case .socks5: - .socks5 - } - } -} - -/// A kind of API access method. -enum AccessMethodKind: Equatable, Hashable, CaseIterable { - /// Direct communication. - case direct - - /// Communication over bridges. - case bridges - - /// Communication over shadowsocks. - case shadowsocks - - /// Communication over socks v5 proxy. - case socks5 -} - -extension AccessMethodKind { - /// Returns `true` if the method is permanent and cannot be deleted. - var isPermanent: Bool { - switch self { - case .direct, .bridges: - true - case .shadowsocks, .socks5: - false - } - } - - /// Returns all access method kinds that can be added by user. - static var allUserDefinedKinds: [AccessMethodKind] { - allCases.filter { !$0.isPermanent } - } -} diff --git a/ios/MullvadVPN/AccessMethodRepository/ProxyConfigurationTester.swift b/ios/MullvadVPN/AccessMethodRepository/ProxyConfigurationTester.swift index bf9ad5f03a9a..95f6b302f719 100644 --- a/ios/MullvadVPN/AccessMethodRepository/ProxyConfigurationTester.swift +++ b/ios/MullvadVPN/AccessMethodRepository/ProxyConfigurationTester.swift @@ -8,6 +8,7 @@ import Combine import Foundation +import MullvadSettings /// A concrete implementation of an access method proxy configuration. class ProxyConfigurationTester: ProxyConfigurationTesterProtocol { diff --git a/ios/MullvadVPN/AccessMethodRepository/ProxyConfigurationTesterProtocol.swift b/ios/MullvadVPN/AccessMethodRepository/ProxyConfigurationTesterProtocol.swift index 3ee795cbf9a6..b01d817d61b6 100644 --- a/ios/MullvadVPN/AccessMethodRepository/ProxyConfigurationTesterProtocol.swift +++ b/ios/MullvadVPN/AccessMethodRepository/ProxyConfigurationTesterProtocol.swift @@ -7,6 +7,7 @@ // import Foundation +import MullvadSettings /// Type implementing access method proxy configuration testing. protocol ProxyConfigurationTesterProtocol { diff --git a/ios/MullvadVPN/AccessMethodRepository/ShadowsocksCipher.swift b/ios/MullvadVPN/AccessMethodRepository/ShadowsocksCipher.swift deleted file mode 100644 index 8610bf33c1a4..000000000000 --- a/ios/MullvadVPN/AccessMethodRepository/ShadowsocksCipher.swift +++ /dev/null @@ -1,48 +0,0 @@ -// -// ShadowsocksCipher.swift -// MullvadVPN -// -// Created by pronebird on 13/11/2023. -// Copyright © 2023 Mullvad VPN AB. All rights reserved. -// - -import Foundation - -/// Type representing a shadowsocks cipher. -struct ShadowsocksCipher: RawRepresentable, CustomStringConvertible, Equatable, Hashable, Codable { - let rawValue: String - - var description: String { - rawValue - } - - /// Default cipher. - static let `default` = ShadowsocksCipher(rawValue: "chacha20") - - /// All supported ciphers. - static let supportedCiphers = supportedCipherIdentifiers.map { ShadowsocksCipher(rawValue: $0) } -} - -private let supportedCipherIdentifiers = [ - // Stream ciphers. - "aes-128-cfb", - "aes-128-cfb1", - "aes-128-cfb8", - "aes-128-cfb128", - "aes-256-cfb", - "aes-256-cfb1", - "aes-256-cfb8", - "aes-256-cfb128", - "rc4", - "rc4-md5", - "chacha20", - "salsa20", - "chacha20-ietf", - // AEAD ciphers. - "aes-128-gcm", - "aes-256-gcm", - "chacha20-ietf-poly1305", - "xchacha20-ietf-poly1305", - "aes-128-pmac-siv", - "aes-256-pmac-siv", -] diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Add/AddAccessMethodCoordinator.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Add/AddAccessMethodCoordinator.swift index 1ccf3ec4b141..116c1fbb8f96 100644 --- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Add/AddAccessMethodCoordinator.swift +++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Add/AddAccessMethodCoordinator.swift @@ -7,6 +7,7 @@ // import Combine +import MullvadSettings import Routing import UIKit diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Add/AddAccessMethodInteractor.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Add/AddAccessMethodInteractor.swift index 88143db43b89..919bc72de775 100644 --- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Add/AddAccessMethodInteractor.swift +++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Add/AddAccessMethodInteractor.swift @@ -8,6 +8,7 @@ import Combine import Foundation +import MullvadSettings struct AddAccessMethodInteractor: AddAccessMethodInteractorProtocol { let subject: CurrentValueSubject diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Common/ShadowsocksSectionHandler.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Common/ShadowsocksSectionHandler.swift index e9afdd51e524..fc1a19ed5f21 100644 --- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Common/ShadowsocksSectionHandler.swift +++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Common/ShadowsocksSectionHandler.swift @@ -66,7 +66,7 @@ struct ShadowsocksSectionHandler { func configureCipher(_ cell: UITableViewCell, itemIdentifier: ShadowsocksItemIdentifier) { var contentConfiguration = UIListContentConfiguration.mullvadValueCell(tableStyle: tableStyle) contentConfiguration.text = itemIdentifier.text - contentConfiguration.secondaryText = "\(subject.value.shadowsocks.cipher)" + contentConfiguration.secondaryText = subject.value.shadowsocks.cipher.rawValue.description cell.contentConfiguration = contentConfiguration if let cell = cell as? CustomCellDisclosureHandling { diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodCoordinator.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodCoordinator.swift index 92d3a7074b23..237b3f1ea2a1 100644 --- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodCoordinator.swift +++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodCoordinator.swift @@ -7,6 +7,7 @@ // import Combine +import MullvadSettings import Routing import UIKit diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodInteractor.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodInteractor.swift index ebecc582c013..5af1f9bf7937 100644 --- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodInteractor.swift +++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Edit/EditAccessMethodInteractor.swift @@ -8,6 +8,7 @@ import Combine import Foundation +import MullvadSettings struct EditAccessMethodInteractor: EditAccessMethodInteractorProtocol { let subject: CurrentValueSubject diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/List/ListAccessMethodCoordinator.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/List/ListAccessMethodCoordinator.swift index 7b58a4ae52ee..afd687847fb3 100644 --- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/List/ListAccessMethodCoordinator.swift +++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/List/ListAccessMethodCoordinator.swift @@ -6,6 +6,7 @@ // Copyright © 2023 Mullvad VPN AB. All rights reserved. // +import MullvadSettings import Routing import UIKit diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/List/ListAccessMethodInteractor.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/List/ListAccessMethodInteractor.swift index 025e35e0389f..ee38216dd5a8 100644 --- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/List/ListAccessMethodInteractor.swift +++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/List/ListAccessMethodInteractor.swift @@ -8,6 +8,7 @@ import Combine import Foundation +import MullvadSettings /// A concrete implementation of an API access list interactor. struct ListAccessMethodInteractor: ListAccessMethodInteractorProtocol { diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/AccessMethodKind+Extensions.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/AccessMethodKind+Extension.swift similarity index 91% rename from ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/AccessMethodKind+Extensions.swift rename to ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/AccessMethodKind+Extension.swift index 9d60ea9c5c8d..944e9e61380f 100644 --- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/AccessMethodKind+Extensions.swift +++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/AccessMethodKind+Extension.swift @@ -1,12 +1,13 @@ // -// AccessMethodKind.swift +// AccessMethodKind+Extension.swift // MullvadVPN // -// Created by pronebird on 02/11/2023. +// Created by Mojgan on 2023-12-19. // Copyright © 2023 Mullvad VPN AB. All rights reserved. // import Foundation +import MullvadSettings extension AccessMethodKind { /// Returns localized description describing the access method. diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/AccessMethodKind.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/AccessMethodKind.swift new file mode 100644 index 000000000000..88137dd14832 --- /dev/null +++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/AccessMethodKind.swift @@ -0,0 +1,85 @@ +// +// AccessMethodKind.swift +// MullvadVPN +// +// Created by pronebird on 02/11/2023. +// Copyright © 2023 Mullvad VPN AB. All rights reserved. +// + +import Foundation +import MullvadSettings + +/// A kind of API access method. +enum AccessMethodKind: Equatable, Hashable, CaseIterable { + /// Direct communication. + case direct + + /// Communication over bridges. + case bridges + + /// Communication over shadowsocks. + case shadowsocks + + /// Communication over socks v5 proxy. + case socks5 +} + +extension AccessMethodKind { + /// Returns `true` if the method is permanent and cannot be deleted. + var isPermanent: Bool { + switch self { + case .direct, .bridges: + true + case .shadowsocks, .socks5: + false + } + } + + /// Returns all access method kinds that can be added by user. + static var allUserDefinedKinds: [AccessMethodKind] { + allCases.filter { !$0.isPermanent } + } +} + +extension PersistentAccessMethod { + /// A kind of access method. + var kind: AccessMethodKind { + switch proxyConfiguration { + case .direct: + .direct + case .bridges: + .bridges + case .shadowsocks: + .shadowsocks + case .socks5: + .socks5 + } + } +} + +extension AccessMethodKind { + /// Returns localized description describing the access method. + var localizedDescription: String { + switch self { + case .direct: + NSLocalizedString("DIRECT", tableName: "APIAccess", value: "Direct", comment: "") + case .bridges: + NSLocalizedString("BRIDGES", tableName: "APIAccess", value: "Bridges", comment: "") + case .shadowsocks: + NSLocalizedString("SHADOWSOCKS", tableName: "APIAccess", value: "Shadowsocks", comment: "") + case .socks5: + NSLocalizedString("SOCKS_V5", tableName: "APIAccess", value: "Socks5", comment: "") + } + } + + /// Returns `true` if access method is configurable. + /// Methods that aren't configurable do not offer any additional configuration. + var hasProxyConfiguration: Bool { + switch self { + case .direct, .bridges: + false + case .shadowsocks, .socks5: + true + } + } +} diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/AccessMethodValidationError+Helpers.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/AccessMethodValidationError+Helpers.swift index 9f346f9c92a4..d1df95a84774 100644 --- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/AccessMethodValidationError+Helpers.swift +++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/AccessMethodValidationError+Helpers.swift @@ -7,6 +7,7 @@ // import Foundation +import MullvadSettings extension AccessMethodValidationError { /// Checks if any of the fields associated with the given access method have validation errors. diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/AccessMethodViewModel+Persistent.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/AccessMethodViewModel+Persistent.swift index 7a9c2798c39a..848986af1824 100644 --- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/AccessMethodViewModel+Persistent.swift +++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/AccessMethodViewModel+Persistent.swift @@ -7,6 +7,7 @@ // import Foundation +import MullvadSettings import MullvadTypes import Network diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/AccessMethodViewModel.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/AccessMethodViewModel.swift index 2dc6262dcb14..4e6ba6197492 100644 --- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/AccessMethodViewModel.swift +++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/AccessMethodViewModel.swift @@ -7,6 +7,7 @@ // import Foundation +import MullvadSettings import MullvadTypes /// The view model used by view controllers editing access method data. @@ -34,7 +35,7 @@ struct AccessMethodViewModel: Identifiable { /// Server password. var password = "" /// Shadowsocks cipher. - var cipher = ShadowsocksCipher.default + var cipher = ShadowsocksCipherOptions.default } /// Access method testing status view model. diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/PersistentAccessMethod+ViewModel.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/PersistentAccessMethod+ViewModel.swift index 3e3a040b2880..3b2f8512feea 100644 --- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/PersistentAccessMethod+ViewModel.swift +++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/PersistentAccessMethod+ViewModel.swift @@ -7,6 +7,7 @@ // import Foundation +import MullvadSettings extension PersistentAccessMethod { /// Convert persistent model into view model. diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/PersistentProxyConfiguration+ViewModel.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/PersistentProxyConfiguration+ViewModel.swift index 3e3d76cb29df..2bb7e763ba84 100644 --- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/PersistentProxyConfiguration+ViewModel.swift +++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Models/PersistentProxyConfiguration+ViewModel.swift @@ -7,6 +7,7 @@ // import Foundation +import MullvadSettings extension PersistentProxyConfiguration { /// View model for socks configuration. diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Pickers/AccessMethodProtocolPicker.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Pickers/AccessMethodProtocolPicker.swift index 0fb922406a8f..3d7cecbe60c6 100644 --- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Pickers/AccessMethodProtocolPicker.swift +++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Pickers/AccessMethodProtocolPicker.swift @@ -6,6 +6,7 @@ // Copyright © 2023 Mullvad VPN AB. All rights reserved. // +import MullvadSettings import UIKit /// Type implementing the access method protocol picker. diff --git a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Pickers/ShadowsocksCipherPicker.swift b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Pickers/ShadowsocksCipherPicker.swift index 2b3ffbd19d7a..c858aa472625 100644 --- a/ios/MullvadVPN/Coordinators/Settings/APIAccess/Pickers/ShadowsocksCipherPicker.swift +++ b/ios/MullvadVPN/Coordinators/Settings/APIAccess/Pickers/ShadowsocksCipherPicker.swift @@ -6,6 +6,7 @@ // Copyright © 2023 Mullvad VPN AB. All rights reserved. // +import MullvadSettings import UIKit /// Type implementing the shadowsocks cipher picker. @@ -17,7 +18,7 @@ struct ShadowsocksCipherPicker { /// - Parameters: /// - currentValue: current selection. /// - completion: a completion handler. - func present(currentValue: ShadowsocksCipher, completion: @escaping (ShadowsocksCipher) -> Void) { + func present(currentValue: ShadowsocksCipherOptions, completion: @escaping (ShadowsocksCipherOptions) -> Void) { let navigationController = navigationController let dataSource = ShadowsocksCipherPickerDataSource() @@ -42,13 +43,13 @@ struct ShadowsocksCipherPicker { /// Type implementing the data source for the shadowsocks cipher picker. struct ShadowsocksCipherPickerDataSource: ListItemDataSourceProtocol { struct Item: ListItemDataSourceItem { - let cipher: ShadowsocksCipher + let cipher: ShadowsocksCipherOptions - var id: ShadowsocksCipher { cipher } - var text: String { "\(cipher)" } + var id: ShadowsocksCipherOptions { cipher } + var text: String { "\(cipher.rawValue.description)" } } - let items = ShadowsocksCipher.supportedCiphers.map { Item(cipher: $0) } + let items = ShadowsocksCipherOptions.all.map { Item(cipher: $0) } var itemCount: Int { items.count @@ -58,7 +59,7 @@ struct ShadowsocksCipherPickerDataSource: ListItemDataSourceProtocol { items[indexPath.row] } - func indexPath(for itemID: ShadowsocksCipher) -> IndexPath? { + func indexPath(for itemID: ShadowsocksCipherOptions) -> IndexPath? { guard let index = items.firstIndex(where: { $0.id == itemID }) else { return nil } return IndexPath(row: index, section: 0)