Drop v2 migrations (#348)
This commit is contained in:
parent
0f84859354
commit
a3cfde1950
|
@ -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 = "<group>"; };
|
||||
0E34AC8127F892C40042F2AB /* OnDemandView+SSID.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "OnDemandView+SSID.swift"; sourceTree = "<group>"; };
|
||||
0E35C099280E95BB0071FA35 /* ProviderProfileAvailability.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProviderProfileAvailability.swift; sourceTree = "<group>"; };
|
||||
0E3A3C112AAB7C470003A5F6 /* UpgradeManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpgradeManager.swift; sourceTree = "<group>"; };
|
||||
0E3A3C122AAB7C480003A5F6 /* DefaultUpgradeManagerStrategy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultUpgradeManagerStrategy.swift; sourceTree = "<group>"; };
|
||||
0E3A3C152AAB8AB80003A5F6 /* UpgradeManagerStrategy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpgradeManagerStrategy.swift; sourceTree = "<group>"; };
|
||||
0E3A593B2A50975700B3FE40 /* ErrorHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorHandler.swift; sourceTree = "<group>"; };
|
||||
0E3B7FCC27E47B3700C66F13 /* AddHostView+Name.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AddHostView+Name.swift"; sourceTree = "<group>"; };
|
||||
0E3B7FD527E5173A00C66F13 /* ProfileView+VPN.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+VPN.swift"; sourceTree = "<group>"; };
|
||||
|
@ -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 = "<group>";
|
||||
|
@ -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 */,
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
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")
|
||||
}
|
||||
}
|
||||
}
|
|
@ -62,7 +62,7 @@ extension PersistenceManager {
|
|||
}
|
||||
|
||||
private extension PersistenceManager {
|
||||
private enum StoreKey: String, KeyStoreDomainLocation {
|
||||
enum StoreKey: String, KeyStoreDomainLocation {
|
||||
case persistenceAuthor
|
||||
|
||||
var domain: String {
|
||||
|
|
|
@ -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"
|
|
@ -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?)
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -338,7 +338,7 @@ extension VPNManager {
|
|||
}
|
||||
|
||||
private extension VPNManager {
|
||||
private enum StoreKey: String, KeyStoreDomainLocation {
|
||||
enum StoreKey: String, KeyStoreDomainLocation {
|
||||
case tunnelLogPath
|
||||
|
||||
case tunnelLogFormat
|
||||
|
|
|
@ -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 <http://www.gnu.org/licenses/>.
|
||||
//
|
||||
|
||||
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<String> = []
|
||||
|
||||
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<String>) 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
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue