diff --git a/Passepartout.xcodeproj/project.pbxproj b/Passepartout.xcodeproj/project.pbxproj index 5bf0914d..5d86baab 100644 --- a/Passepartout.xcodeproj/project.pbxproj +++ b/Passepartout.xcodeproj/project.pbxproj @@ -50,6 +50,9 @@ 0E34AC7827F840890042F2AB /* OrganizerView+Scene.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E34AC7727F840890042F2AB /* OrganizerView+Scene.swift */; }; 0E34AC8227F892C40042F2AB /* OnDemandView+SSID.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E34AC8127F892C40042F2AB /* OnDemandView+SSID.swift */; }; 0E35C09A280E95BB0071FA35 /* ProviderProfileAvailability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E35C099280E95BB0071FA35 /* ProviderProfileAvailability.swift */; }; + 0E3A3C132AAB7C480003A5F6 /* UpgradeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3A3C112AAB7C470003A5F6 /* UpgradeManager.swift */; }; + 0E3A3C142AAB7C480003A5F6 /* DefaultUpgradeManagerStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3A3C122AAB7C480003A5F6 /* DefaultUpgradeManagerStrategy.swift */; }; + 0E3A3C162AAB8AB80003A5F6 /* UpgradeManagerStrategy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3A3C152AAB8AB80003A5F6 /* UpgradeManagerStrategy.swift */; }; 0E3A593C2A50975700B3FE40 /* ErrorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3A593B2A50975700B3FE40 /* ErrorHandler.swift */; }; 0E3B7FCD27E47B3700C66F13 /* AddHostView+Name.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3B7FCC27E47B3700C66F13 /* AddHostView+Name.swift */; }; 0E3B7FD627E5173A00C66F13 /* ProfileView+VPN.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3B7FD527E5173A00C66F13 /* ProfileView+VPN.swift */; }; @@ -340,6 +343,9 @@ 0E34AC7727F840890042F2AB /* OrganizerView+Scene.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OrganizerView+Scene.swift"; sourceTree = ""; }; 0E34AC8127F892C40042F2AB /* OnDemandView+SSID.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OnDemandView+SSID.swift"; sourceTree = ""; }; 0E35C099280E95BB0071FA35 /* ProviderProfileAvailability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProviderProfileAvailability.swift; sourceTree = ""; }; + 0E3A3C112AAB7C470003A5F6 /* UpgradeManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpgradeManager.swift; sourceTree = ""; }; + 0E3A3C122AAB7C480003A5F6 /* DefaultUpgradeManagerStrategy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultUpgradeManagerStrategy.swift; sourceTree = ""; }; + 0E3A3C152AAB8AB80003A5F6 /* UpgradeManagerStrategy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpgradeManagerStrategy.swift; sourceTree = ""; }; 0E3A593B2A50975700B3FE40 /* ErrorHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorHandler.swift; sourceTree = ""; }; 0E3B7FCC27E47B3700C66F13 /* AddHostView+Name.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AddHostView+Name.swift"; sourceTree = ""; }; 0E3B7FD527E5173A00C66F13 /* ProfileView+VPN.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+VPN.swift"; sourceTree = ""; }; @@ -696,9 +702,12 @@ 0E3A593F2A54ACC900B3FE40 /* Managers */ = { isa = PBXGroup; children = ( + 0E3A3C122AAB7C480003A5F6 /* DefaultUpgradeManagerStrategy.swift */, 0E9C232F27F47032007D5FC7 /* IntentsManager.swift */, 0E7A8C092A1D410400780F4B /* PersistenceManager.swift */, 0E53249627D26B51002565C3 /* ProductManager.swift */, + 0E3A3C112AAB7C470003A5F6 /* UpgradeManager.swift */, + 0E3A3C152AAB8AB80003A5F6 /* UpgradeManagerStrategy.swift */, ); path = Managers; sourceTree = ""; @@ -1466,6 +1475,7 @@ 0ED89C1E27DE3F8D008B36D6 /* IntentAddView.swift in Sources */, 0E5468002867AC9A00F74D1C /* MacUtils.swift in Sources */, 0E0F4C6429C84B5A0022E884 /* LockableView.swift in Sources */, + 0E3A3C132AAB7C480003A5F6 /* UpgradeManager.swift in Sources */, 0E96D3052872010A005EFBCF /* DefaultLightVPNManager.swift in Sources */, 0EBE880F281B18DE0090D9E6 /* OrganizerView+ProfileRow.swift in Sources */, 0ED30DCF27EA1EF80057D8A3 /* PaywallView+Beta.swift in Sources */, @@ -1500,6 +1510,7 @@ 0E5349BE27C16A4500C71BB3 /* StyledPicker.swift in Sources */, 0E2C172B27CB63F9007E8488 /* Reviewer.swift in Sources */, 0E71ACDD27C0295C00F85C4B /* View+Extensions.swift in Sources */, + 0E3A3C162AAB8AB80003A5F6 /* UpgradeManagerStrategy.swift in Sources */, 0E021D9C284E68580077EF5D /* CoreContext.swift in Sources */, 0E7A8C0A2A1D410500780F4B /* PersistenceManager.swift in Sources */, A38D607728AFCFD20005C271 /* SettingsView.swift in Sources */, @@ -1511,6 +1522,7 @@ 0EBC075527EBC83800208AD9 /* MailComposerView.swift in Sources */, 0EF0FAF727DD159C007EB181 /* IntentDispatcher.swift in Sources */, 0E0838FD2877334300A34EC0 /* DefaultLightProviderManager.swift in Sources */, + 0E3A3C142AAB7C480003A5F6 /* DefaultUpgradeManagerStrategy.swift in Sources */, 0E0F4C6629C84CF60022E884 /* LogoView.swift in Sources */, 0E039279281890B100827C10 /* AddHostView.swift in Sources */, 0E5467F72867A57000F74D1C /* MacBridge.swift in Sources */, diff --git a/Passepartout/App/Constants/Constants+App.swift b/Passepartout/App/Constants/Constants+App.swift index 666a0f11..dfdef48a 100644 --- a/Passepartout/App/Constants/Constants+App.swift +++ b/Passepartout/App/Constants/Constants+App.swift @@ -224,10 +224,10 @@ extension Constants { enum Delays { static let scrolling = 100 -// @available(*, deprecated, message: "file importer stops showing again after closing with swipe down") +// @available(*, deprecated, message: "File importer stops showing again after closing with swipe down") static let xxxPresentFileImporter = 200 -// @available(*, deprecated, message: "edited shortcut is outdated in delegate") +// @available(*, deprecated, message: "Edited shortcut is outdated in delegate") static let xxxReloadEditedShortcut = 200 } diff --git a/Passepartout/App/Constants/Theme.swift b/Passepartout/App/Constants/Theme.swift index 439cdf1b..e01d45f0 100644 --- a/Passepartout/App/Constants/Theme.swift +++ b/Passepartout/App/Constants/Theme.swift @@ -577,7 +577,7 @@ extension View { // MARK: Hacks extension View { - @available(*, deprecated, message: "mitigates multiline text truncation (1.0 does not work though)") + @available(*, deprecated, message: "Mitigates multiline text truncation (1.0 does not work though)") func xxxThemeTruncation() -> some View { minimumScaleFactor(0.5) } diff --git a/Passepartout/App/Managers/DefaultUpgradeManagerStrategy.swift b/Passepartout/App/Managers/DefaultUpgradeManagerStrategy.swift new file mode 100644 index 00000000..57753f01 --- /dev/null +++ b/Passepartout/App/Managers/DefaultUpgradeManagerStrategy.swift @@ -0,0 +1,40 @@ +// +// DefaultUpgradeManagerStrategy.swift +// Passepartout +// +// Created by Davide De Rosa on 3/20/22. +// Copyright (c) 2023 Davide De Rosa. All rights reserved. +// +// https://github.com/passepartoutvpn +// +// This file is part of Passepartout. +// +// Passepartout is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Passepartout is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Passepartout. If not, see . +// + +import Foundation +import PassepartoutLibrary + +public final class DefaultUpgradeManagerStrategy: UpgradeManagerStrategy { + public init() { + } + + public func doMigrateStore(_ store: KeyValueStore, lastVersion: String?) { + if let lastVersion { + pp_log.debug("Upgrade from \(lastVersion)") + } else { + pp_log.debug("Fresh install") + } + } +} diff --git a/Passepartout/App/Managers/PersistenceManager.swift b/Passepartout/App/Managers/PersistenceManager.swift index 6f03a804..34ac59dd 100644 --- a/Passepartout/App/Managers/PersistenceManager.swift +++ b/Passepartout/App/Managers/PersistenceManager.swift @@ -62,7 +62,7 @@ extension PersistenceManager { } private extension PersistenceManager { - private enum StoreKey: String, KeyStoreDomainLocation { + enum StoreKey: String, KeyStoreDomainLocation { case persistenceAuthor var domain: String { diff --git a/PassepartoutLibrary/Sources/PassepartoutVPN/Managers/UpgradeManager.swift b/Passepartout/App/Managers/UpgradeManager.swift similarity index 57% rename from PassepartoutLibrary/Sources/PassepartoutVPN/Managers/UpgradeManager.swift rename to Passepartout/App/Managers/UpgradeManager.swift index f89b491d..372f3ca7 100644 --- a/PassepartoutLibrary/Sources/PassepartoutVPN/Managers/UpgradeManager.swift +++ b/Passepartout/App/Managers/UpgradeManager.swift @@ -24,7 +24,7 @@ // import Foundation -import PassepartoutCore +import PassepartoutLibrary @MainActor public final class UpgradeManager: ObservableObject { @@ -48,49 +48,29 @@ public final class UpgradeManager: ObservableObject { } public func doMigrations(_ profileManager: ProfileManager) { - strategy.doMigrateStore(store, didMigrate: &didMigrateToV2) + strategy.doMigrateStore(store, lastVersion: lastVersion) + lastVersion = Constants.Global.appVersionNumber -// profileManager.removeAllProfiles() // testing only - guard didMigrateToV2 else { - isDoingMigrations = true - let migrated = strategy.migratedProfilesToV2() - if !migrated.isEmpty { - pp_log.info("Migrating \(migrated.count) profiles") - migrated.forEach { - var profile = $0 - if profileManager.isExistingProfile(withName: profile.header.name) { - profile = profile.renamedUniquely(withLastUpdate: true) - } - profileManager.saveProfile(profile, isActive: nil) - } - } else { - pp_log.info("Nothing to migrate!") - } - isDoingMigrations = false - - didMigrateToV2 = true - return - } isDoingMigrations = false } } // MARK: KeyValueStore -extension UpgradeManager { - public internal(set) var didMigrateToV2: Bool { +private extension UpgradeManager { + var lastVersion: String? { get { - store.value(forLocation: StoreKey.didMigrateToV2) ?? false + store.value(forLocation: StoreKey.lastVersion) } set { - store.setValue(newValue, forLocation: StoreKey.didMigrateToV2) + store.setValue(newValue, forLocation: StoreKey.lastVersion) } } } private extension UpgradeManager { - private enum StoreKey: String, KeyStoreDomainLocation { - case didMigrateToV2 + enum StoreKey: String, KeyStoreDomainLocation { + case lastVersion var domain: String { "Passepartout.UpgradeManager" diff --git a/PassepartoutLibrary/Sources/PassepartoutVPN/Strategies/UpgradeManagerStrategy.swift b/Passepartout/App/Managers/UpgradeManagerStrategy.swift similarity index 89% rename from PassepartoutLibrary/Sources/PassepartoutVPN/Strategies/UpgradeManagerStrategy.swift rename to Passepartout/App/Managers/UpgradeManagerStrategy.swift index 909a17cb..e8a8545e 100644 --- a/PassepartoutLibrary/Sources/PassepartoutVPN/Strategies/UpgradeManagerStrategy.swift +++ b/Passepartout/App/Managers/UpgradeManagerStrategy.swift @@ -27,7 +27,5 @@ import Foundation import PassepartoutCore public protocol UpgradeManagerStrategy { - func doMigrateStore(_ store: KeyValueStore, didMigrate: inout Bool) - - func migratedProfilesToV2() -> [Profile] + func doMigrateStore(_ store: KeyValueStore, lastVersion: String?) } diff --git a/PassepartoutLibrary/Sources/PassepartoutVPN/Managers/ProfileManager.swift b/PassepartoutLibrary/Sources/PassepartoutVPN/Managers/ProfileManager.swift index 50824786..0063c08e 100644 --- a/PassepartoutLibrary/Sources/PassepartoutVPN/Managers/ProfileManager.swift +++ b/PassepartoutLibrary/Sources/PassepartoutVPN/Managers/ProfileManager.swift @@ -213,7 +213,7 @@ extension ProfileManager { profileRepository.removeProfiles(withIds: ids) } - @available(*, deprecated, message: "only use for testing") + @available(*, deprecated, message: "Only use for testing") public func removeAllProfiles() { let ids = Array(allProfiles.keys) removeProfiles(withIds: ids) @@ -517,7 +517,7 @@ extension ProfileManager { } private extension ProfileManager { - private enum StoreKey: String, KeyStoreDomainLocation { + enum StoreKey: String, KeyStoreDomainLocation { case activeProfileId var domain: String { diff --git a/PassepartoutLibrary/Sources/PassepartoutVPN/Managers/VPNManager.swift b/PassepartoutLibrary/Sources/PassepartoutVPN/Managers/VPNManager.swift index a4c38e38..1d9aaf1c 100644 --- a/PassepartoutLibrary/Sources/PassepartoutVPN/Managers/VPNManager.swift +++ b/PassepartoutLibrary/Sources/PassepartoutVPN/Managers/VPNManager.swift @@ -338,7 +338,7 @@ extension VPNManager { } private extension VPNManager { - private enum StoreKey: String, KeyStoreDomainLocation { + enum StoreKey: String, KeyStoreDomainLocation { case tunnelLogPath case tunnelLogFormat diff --git a/PassepartoutLibrary/Sources/PassepartoutVPNImpl/Strategies/DefaultUpgradeManagerStrategy.swift b/PassepartoutLibrary/Sources/PassepartoutVPNImpl/Strategies/DefaultUpgradeManagerStrategy.swift deleted file mode 100644 index a8b3f71e..00000000 --- a/PassepartoutLibrary/Sources/PassepartoutVPNImpl/Strategies/DefaultUpgradeManagerStrategy.swift +++ /dev/null @@ -1,391 +0,0 @@ -// -// DefaultUpgradeManagerStrategy.swift -// Passepartout -// -// Created by Davide De Rosa on 3/20/22. -// Copyright (c) 2023 Davide De Rosa. All rights reserved. -// -// https://github.com/passepartoutvpn -// -// This file is part of Passepartout. -// -// Passepartout is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Passepartout is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Passepartout. If not, see . -// - -import Foundation -import GenericJSON -import PassepartoutCore -import PassepartoutProviders -import PassepartoutVPN -import TunnelKitCore -import TunnelKitManager -import TunnelKitOpenVPNCore - -private typealias Map = [String: Any] - -private enum UpgradeError: Error { - case json - - case missingId - - case missingOpenVPNConfiguration - - case missingHostname - - case missingEndpointProtocols - - case missingProviderName -} - -public final class DefaultUpgradeManagerStrategy: UpgradeManagerStrategy { - public init() { - } -} - -// MARK: Migrate old store - -extension DefaultUpgradeManagerStrategy { - private enum LegacyStoreKey: String, KeyStoreLocation, CaseIterable { - case activeProfileId - - case launchesOnLogin - - case isStatusMenuEnabled - - case isShowingFavorites - - case confirmsQuit - - case logFormat - - case tunnelLogFormat - - case masksPrivateData - - case didHandleSubreddit - - case persistenceAuthor - - case didMigrateToV2 - - case other1 = "MasksPrivateData" - - case other2 = "DidHandleSubreddit" - - case other3 = "Convenience.Reviewer.LastVersion" - - case other4 = "didMigrateKeychainContext" - - var key: String { - rawValue - } - } - - public func doMigrateStore(_ store: KeyValueStore, didMigrate: inout Bool) { - if !didMigrate { - guard let legacyDidMigrateToV2: Bool = store.value(forLocation: LegacyStoreKey.didMigrateToV2) else { - return - } - didMigrate = legacyDidMigrateToV2 - } - - LegacyStoreKey.allCases.forEach { - store.removeValue(forLocation: $0) - } - } -} - -// MARK: Migrate to version 2 - -extension DefaultUpgradeManagerStrategy { - private var appGroup: String { - "group.com.algoritmico.Passepartout" - } - - public func migratedProfilesToV2() -> [Profile] { - var migrated: [Profile] = [] - pp_log.info("Migrating data to v2") - - let fm = FileManager.default - - guard let documents = fm.containerURL(forSecurityApplicationGroupIdentifier: appGroup)? - .appendingPathComponent("Documents") else { - - pp_log.info("No data to migrate") - return [] - } - - let cs = documents.appendingPathComponent("ConnectionService.json") - let hostsFolder = documents.appendingPathComponent("Hosts") - let providersFolder = documents.appendingPathComponent("Providers") - - do { - let csJSON = try cs.asJSON() -// pp_log.error(csJSON) - - do { - var authUserPassUUIDs: Set = [] - - for host in try fm.contentsOfDirectory(at: hostsFolder, includingPropertiesForKeys: nil) { - guard host.isFileURL && host.pathExtension == "ovpn" else { - continue - } - - do { - let uuid = host.deletingPathExtension().lastPathComponent - let content = try String(contentsOf: host) - - if content.contains("auth-user-pass") { - authUserPassUUIDs.insert(uuid) - } - } catch { - pp_log.warning("Unable to read host profile .ovpn: \(host)") - } - } - -// print(">>> authUserPassUUIDs: \(authUserPassUUIDs)") - - for host in try fm.contentsOfDirectory(at: hostsFolder, includingPropertiesForKeys: nil) { - guard host.isFileURL && host.pathExtension == "json" else { - continue - } - do { - let json = try host.asJSON() -// pp_log.error(json) - - let result = try migratedV1Profile(csJSON, hostMap: json, authUserPass: authUserPassUUIDs) -// pp_log.info(result.profile) -// print(">>> Account: \(result.profile.username) -> \(result.password)") - - migrated.append(result) - } catch { - pp_log.warning("Unable to migrate host profile: \(host)") - continue - } - } - } catch { - pp_log.warning(error) - } - - do { - for provider in try fm.contentsOfDirectory(at: providersFolder, includingPropertiesForKeys: nil) { - guard provider.isFileURL && provider.pathExtension == "json" else { - continue - } - do { - let json = try provider.asJSON() -// pp_log.error(json) - - let result = try migratedV1Profile(csJSON, providerMap: json) -// pp_log.info(result.profile) -// print(">>> Account: \(result.profile.username) -> \(result.password)") - - migrated.append(result) - } catch { - pp_log.warning("Unable to migrate provider profile: \(provider)") - continue - } - } - } catch { - pp_log.warning(error) - } - } catch { - pp_log.warning(error) - } - - return migrated - } - - // SHARED - // - // username/password ("username") - // trusted networks ("trustedNetworks") - // network settings ("networkChoices", "manualNetworkSettings") - // - - // HOST - // - // provider configuration ("parameters") -- not crucial - // custom endpoint ("parameters"?) -- not crucial - // ovpn configuration ("parameters" -> "sessionConfiguration") - // - private func migratedV1Profile(_ cs: Map, hostMap: Map, authUserPass: Set) throws -> Profile { - guard let oldUUIDString = hostMap["id"] as? String else { - throw UpgradeError.missingId - } - - let name = (cs["hostTitles"] as? Map)?[oldUUIDString] as? String ?? oldUUIDString - let header = Profile.Header(name: name) // new UUID - - // configuration - guard let params = hostMap["parameters"] as? Map else { - throw UpgradeError.missingOpenVPNConfiguration - } - guard var ovpn = params["sessionConfiguration"] as? Map else { - throw UpgradeError.missingOpenVPNConfiguration - } - guard let hostname = ovpn["hostname"] as? String else { - throw UpgradeError.missingHostname - } - guard let rawEps = ovpn["endpointProtocols"] as? [String] else { - throw UpgradeError.missingEndpointProtocols - } - let eps = rawEps.compactMap(EndpointProtocol.init(rawValue:)) - ovpn["remotes"] = eps.map { - [hostname, $0.description].joined(separator: ":") - } - ovpn["authUserPass"] = authUserPass.contains(oldUUIDString) - let cfg = try JSON(ovpn).decode(OpenVPN.Configuration.self) - - // keychain - let username = hostMap["username"] as? String ?? "" - let password = migratedV1Password(forProfileId: oldUUIDString, profileType: "host", username: username) - - var profile = Profile(header, configuration: cfg) - var account = Profile.Account() - account.username = username - account.password = password - profile.account = account - - // shared - profile.onDemand = migratedV1TrustedNetworks(hostMap) - profile.networkSettings = migratedV1NetworkSettings(hostMap) - - return profile - } - - // HOST - // - // poolId -- not crucial - // presetId - // favoriteGroupIds - // - private func migratedV1Profile(_ cs: Map, providerMap: Map) throws -> Profile { - guard let name = providerMap["name"] as? String else { - throw UpgradeError.missingProviderName - } - - let header = Profile.Header(name: name, providerName: name) - var provider = Profile.Provider(name) - - // keychain - var account = Profile.Account() - account.username = providerMap["username"] as? String ?? "" - account.password = migratedV1Password(forProfileId: name, profileType: "provider", username: account.username) - - // provider configuration - var settings = Profile.Provider.Settings() - if let apiId = providerMap["poolId"] as? String { - settings.serverId = ProviderServer.id(withName: name, vpnProtocol: .openVPN, apiId: apiId) - } - settings.presetId = providerMap["presetId"] as? String - let favoriteGroupIds = providerMap["favoriteGroupIds"] as? [String] ?? [] - settings.favoriteLocationIds = Set(favoriteGroupIds.compactMap { - [ - name, - $0.replacingOccurrences(of: "/", with: ":") - ].joined(separator: ":") - }) - settings.account = account - provider.vpnSettings[.openVPN] = settings - - var profile = Profile(header, provider: provider) - - // shared - profile.onDemand = migratedV1TrustedNetworks(providerMap) - profile.networkSettings = migratedV1NetworkSettings(providerMap) - - return profile - } - - private func migratedV1Password(forProfileId profileId: String, profileType: String, username: String) -> String { - let keychain = Keychain(group: appGroup) - let passwordContext = [Bundle.main.bundleIdentifier!, profileType, profileId].joined(separator: ".") - do { - return try keychain.password(for: username, context: passwordContext) - } catch { - return "" - } - } - - private func migratedV1TrustedNetworks(_ map: Map) -> Profile.OnDemand { - var onDemand = Profile.OnDemand() - onDemand.isEnabled = true - if let trusted = map["trustedNetworks"] as? Map { - onDemand.withMobileNetwork = trusted["includesMobile"] as? Bool ?? false - onDemand.withEthernetNetwork = trusted["includesEthernet"] as? Bool ?? false - onDemand.withSSIDs = trusted["includedWiFis"] as? [String: Bool] ?? [:] - if let rawPolicy = trusted["policy"] as? String, let policy = Profile.OnDemand.Policy(rawValue: rawPolicy) { - onDemand.policy = policy - } - } - return onDemand - } - - private func migratedV1NetworkSettings(_ map: Map) -> Profile.NetworkSettings { - var settings = Profile.NetworkSettings() - - if let choices = map["networkChoices"] as? Map { - settings.gateway.choice = migratedV1Choice(choices, key: "gateway") - settings.dns.choice = migratedV1Choice(choices, key: "dns") - settings.proxy.choice = migratedV1Choice(choices, key: "proxy") - settings.mtu.choice = migratedV1Choice(choices, key: "mtu") - } - - if let manual = map["manualNetworkSettings"] as? Map { - - // gateway - settings.gateway.isDefaultIPv4 = (manual["gatewayPolicies"] as? [String])?.contains("IPv4") ?? false - settings.gateway.isDefaultIPv6 = (manual["gatewayPolicies"] as? [String])?.contains("IPv6") ?? false - - // dns - (manual["dnsProtocol"] as? String).map { - settings.dns.configurationType = .init(rawValue: $0) ?? .plain - } - settings.dns.dnsServers = manual["dnsServers"] as? [String] ?? [] - settings.dns.dnsSearchDomains = manual["dnsSearchDomains"] as? [String] ?? [] - (manual["dnsHTTPSURL"] as? String).map { - settings.dns.dnsHTTPSURL = URL(string: $0) - } - settings.dns.dnsTLSServerName = manual["dnsTLSServerName"] as? String - - // proxy - settings.proxy.proxyAddress = manual["proxyAddress"] as? String - settings.proxy.proxyPort = manual["proxyPort"] as? UInt16 - (manual["proxyAutoConfigurationURL"] as? String).map { - settings.proxy.proxyAutoConfigurationURL = URL(string: $0) - } - settings.proxy.proxyBypassDomains = manual["proxyBypassDomains"] as? [String] ?? [] - - // mtu - settings.mtu.mtuBytes = manual["mtuBytes"] as? Int ?? 0 - } - - return settings - } - - private func migratedV1Choice(_ map: Map, key: String) -> Network.Choice { - (map[key] as? String) == "manual" ? .manual : .automatic - } -} - -private extension URL { - func asJSON() throws -> Map { - let data = try Data(contentsOf: self) - guard let json = try JSONSerialization.jsonObject(with: data) as? Map else { - throw UpgradeError.json - } - return json - } -}