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 */; };
|
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 */; };
|
0E34AC8227F892C40042F2AB /* OnDemandView+SSID.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E34AC8127F892C40042F2AB /* OnDemandView+SSID.swift */; };
|
||||||
0E35C09A280E95BB0071FA35 /* ProviderProfileAvailability.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E35C099280E95BB0071FA35 /* ProviderProfileAvailability.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 */; };
|
0E3A593C2A50975700B3FE40 /* ErrorHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3A593B2A50975700B3FE40 /* ErrorHandler.swift */; };
|
||||||
0E3B7FCD27E47B3700C66F13 /* AddHostView+Name.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E3B7FCC27E47B3700C66F13 /* AddHostView+Name.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 */; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
0E3B7FD527E5173A00C66F13 /* ProfileView+VPN.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ProfileView+VPN.swift"; sourceTree = "<group>"; };
|
||||||
|
@ -696,9 +702,12 @@
|
||||||
0E3A593F2A54ACC900B3FE40 /* Managers */ = {
|
0E3A593F2A54ACC900B3FE40 /* Managers */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
0E3A3C122AAB7C480003A5F6 /* DefaultUpgradeManagerStrategy.swift */,
|
||||||
0E9C232F27F47032007D5FC7 /* IntentsManager.swift */,
|
0E9C232F27F47032007D5FC7 /* IntentsManager.swift */,
|
||||||
0E7A8C092A1D410400780F4B /* PersistenceManager.swift */,
|
0E7A8C092A1D410400780F4B /* PersistenceManager.swift */,
|
||||||
0E53249627D26B51002565C3 /* ProductManager.swift */,
|
0E53249627D26B51002565C3 /* ProductManager.swift */,
|
||||||
|
0E3A3C112AAB7C470003A5F6 /* UpgradeManager.swift */,
|
||||||
|
0E3A3C152AAB8AB80003A5F6 /* UpgradeManagerStrategy.swift */,
|
||||||
);
|
);
|
||||||
path = Managers;
|
path = Managers;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -1466,6 +1475,7 @@
|
||||||
0ED89C1E27DE3F8D008B36D6 /* IntentAddView.swift in Sources */,
|
0ED89C1E27DE3F8D008B36D6 /* IntentAddView.swift in Sources */,
|
||||||
0E5468002867AC9A00F74D1C /* MacUtils.swift in Sources */,
|
0E5468002867AC9A00F74D1C /* MacUtils.swift in Sources */,
|
||||||
0E0F4C6429C84B5A0022E884 /* LockableView.swift in Sources */,
|
0E0F4C6429C84B5A0022E884 /* LockableView.swift in Sources */,
|
||||||
|
0E3A3C132AAB7C480003A5F6 /* UpgradeManager.swift in Sources */,
|
||||||
0E96D3052872010A005EFBCF /* DefaultLightVPNManager.swift in Sources */,
|
0E96D3052872010A005EFBCF /* DefaultLightVPNManager.swift in Sources */,
|
||||||
0EBE880F281B18DE0090D9E6 /* OrganizerView+ProfileRow.swift in Sources */,
|
0EBE880F281B18DE0090D9E6 /* OrganizerView+ProfileRow.swift in Sources */,
|
||||||
0ED30DCF27EA1EF80057D8A3 /* PaywallView+Beta.swift in Sources */,
|
0ED30DCF27EA1EF80057D8A3 /* PaywallView+Beta.swift in Sources */,
|
||||||
|
@ -1500,6 +1510,7 @@
|
||||||
0E5349BE27C16A4500C71BB3 /* StyledPicker.swift in Sources */,
|
0E5349BE27C16A4500C71BB3 /* StyledPicker.swift in Sources */,
|
||||||
0E2C172B27CB63F9007E8488 /* Reviewer.swift in Sources */,
|
0E2C172B27CB63F9007E8488 /* Reviewer.swift in Sources */,
|
||||||
0E71ACDD27C0295C00F85C4B /* View+Extensions.swift in Sources */,
|
0E71ACDD27C0295C00F85C4B /* View+Extensions.swift in Sources */,
|
||||||
|
0E3A3C162AAB8AB80003A5F6 /* UpgradeManagerStrategy.swift in Sources */,
|
||||||
0E021D9C284E68580077EF5D /* CoreContext.swift in Sources */,
|
0E021D9C284E68580077EF5D /* CoreContext.swift in Sources */,
|
||||||
0E7A8C0A2A1D410500780F4B /* PersistenceManager.swift in Sources */,
|
0E7A8C0A2A1D410500780F4B /* PersistenceManager.swift in Sources */,
|
||||||
A38D607728AFCFD20005C271 /* SettingsView.swift in Sources */,
|
A38D607728AFCFD20005C271 /* SettingsView.swift in Sources */,
|
||||||
|
@ -1511,6 +1522,7 @@
|
||||||
0EBC075527EBC83800208AD9 /* MailComposerView.swift in Sources */,
|
0EBC075527EBC83800208AD9 /* MailComposerView.swift in Sources */,
|
||||||
0EF0FAF727DD159C007EB181 /* IntentDispatcher.swift in Sources */,
|
0EF0FAF727DD159C007EB181 /* IntentDispatcher.swift in Sources */,
|
||||||
0E0838FD2877334300A34EC0 /* DefaultLightProviderManager.swift in Sources */,
|
0E0838FD2877334300A34EC0 /* DefaultLightProviderManager.swift in Sources */,
|
||||||
|
0E3A3C142AAB7C480003A5F6 /* DefaultUpgradeManagerStrategy.swift in Sources */,
|
||||||
0E0F4C6629C84CF60022E884 /* LogoView.swift in Sources */,
|
0E0F4C6629C84CF60022E884 /* LogoView.swift in Sources */,
|
||||||
0E039279281890B100827C10 /* AddHostView.swift in Sources */,
|
0E039279281890B100827C10 /* AddHostView.swift in Sources */,
|
||||||
0E5467F72867A57000F74D1C /* MacBridge.swift in Sources */,
|
0E5467F72867A57000F74D1C /* MacBridge.swift in Sources */,
|
||||||
|
|
|
@ -224,10 +224,10 @@ extension Constants {
|
||||||
enum Delays {
|
enum Delays {
|
||||||
static let scrolling = 100
|
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
|
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
|
static let xxxReloadEditedShortcut = 200
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -577,7 +577,7 @@ extension View {
|
||||||
// MARK: Hacks
|
// MARK: Hacks
|
||||||
|
|
||||||
extension View {
|
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 {
|
func xxxThemeTruncation() -> some View {
|
||||||
minimumScaleFactor(0.5)
|
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 extension PersistenceManager {
|
||||||
private enum StoreKey: String, KeyStoreDomainLocation {
|
enum StoreKey: String, KeyStoreDomainLocation {
|
||||||
case persistenceAuthor
|
case persistenceAuthor
|
||||||
|
|
||||||
var domain: String {
|
var domain: String {
|
||||||
|
|
|
@ -24,7 +24,7 @@
|
||||||
//
|
//
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import PassepartoutCore
|
import PassepartoutLibrary
|
||||||
|
|
||||||
@MainActor
|
@MainActor
|
||||||
public final class UpgradeManager: ObservableObject {
|
public final class UpgradeManager: ObservableObject {
|
||||||
|
@ -48,49 +48,29 @@ public final class UpgradeManager: ObservableObject {
|
||||||
}
|
}
|
||||||
|
|
||||||
public func doMigrations(_ profileManager: ProfileManager) {
|
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
|
isDoingMigrations = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: KeyValueStore
|
// MARK: KeyValueStore
|
||||||
|
|
||||||
extension UpgradeManager {
|
private extension UpgradeManager {
|
||||||
public internal(set) var didMigrateToV2: Bool {
|
var lastVersion: String? {
|
||||||
get {
|
get {
|
||||||
store.value(forLocation: StoreKey.didMigrateToV2) ?? false
|
store.value(forLocation: StoreKey.lastVersion)
|
||||||
}
|
}
|
||||||
set {
|
set {
|
||||||
store.setValue(newValue, forLocation: StoreKey.didMigrateToV2)
|
store.setValue(newValue, forLocation: StoreKey.lastVersion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension UpgradeManager {
|
private extension UpgradeManager {
|
||||||
private enum StoreKey: String, KeyStoreDomainLocation {
|
enum StoreKey: String, KeyStoreDomainLocation {
|
||||||
case didMigrateToV2
|
case lastVersion
|
||||||
|
|
||||||
var domain: String {
|
var domain: String {
|
||||||
"Passepartout.UpgradeManager"
|
"Passepartout.UpgradeManager"
|
|
@ -27,7 +27,5 @@ import Foundation
|
||||||
import PassepartoutCore
|
import PassepartoutCore
|
||||||
|
|
||||||
public protocol UpgradeManagerStrategy {
|
public protocol UpgradeManagerStrategy {
|
||||||
func doMigrateStore(_ store: KeyValueStore, didMigrate: inout Bool)
|
func doMigrateStore(_ store: KeyValueStore, lastVersion: String?)
|
||||||
|
|
||||||
func migratedProfilesToV2() -> [Profile]
|
|
||||||
}
|
}
|
|
@ -213,7 +213,7 @@ extension ProfileManager {
|
||||||
profileRepository.removeProfiles(withIds: ids)
|
profileRepository.removeProfiles(withIds: ids)
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(*, deprecated, message: "only use for testing")
|
@available(*, deprecated, message: "Only use for testing")
|
||||||
public func removeAllProfiles() {
|
public func removeAllProfiles() {
|
||||||
let ids = Array(allProfiles.keys)
|
let ids = Array(allProfiles.keys)
|
||||||
removeProfiles(withIds: ids)
|
removeProfiles(withIds: ids)
|
||||||
|
@ -517,7 +517,7 @@ extension ProfileManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension ProfileManager {
|
private extension ProfileManager {
|
||||||
private enum StoreKey: String, KeyStoreDomainLocation {
|
enum StoreKey: String, KeyStoreDomainLocation {
|
||||||
case activeProfileId
|
case activeProfileId
|
||||||
|
|
||||||
var domain: String {
|
var domain: String {
|
||||||
|
|
|
@ -338,7 +338,7 @@ extension VPNManager {
|
||||||
}
|
}
|
||||||
|
|
||||||
private extension VPNManager {
|
private extension VPNManager {
|
||||||
private enum StoreKey: String, KeyStoreDomainLocation {
|
enum StoreKey: String, KeyStoreDomainLocation {
|
||||||
case tunnelLogPath
|
case tunnelLogPath
|
||||||
|
|
||||||
case tunnelLogFormat
|
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