From 2b2709b51521c4314bc19c05638b66661b559851 Mon Sep 17 00:00:00 2001 From: kp-laura-sempere <46663952+kp-laura-sempere@users.noreply.github.com> Date: Mon, 8 Jan 2024 12:07:48 +0100 Subject: [PATCH] PIA-887: Basic IKEv2 connection PoC (#55) * PIA-886: Selected region dashboard section (UI) * PIA-886: Quick connect dashboard section (UI) * PIA-887: IKev2 connection PoC --- .../UseCases/VpnConnectionUseCase.swift | 14 +- PIA VPN-tvOS/PIA VPN-tvOS.entitlements | 11 +- .../Presentation/RootContainerViewModel.swift | 38 +++- .../RootContainer/UI/RootContainerView.swift | 48 ++-- PIA VPN.xcodeproj/project.pbxproj | 208 +++++++++++++++++- PIA VPN/AppConfiguration.swift | 7 +- PIA VPN/AppConstants.swift | 3 +- PIA VPN/AppPreferences.swift | 30 ++- PIA VPN/Bootstrapper.swift | 46 +++- PIA-VPN-tvOS-NetworkExtension/Info.plist | 13 ++ ...PIA_VPN_tvOS_NetworkExtension.entitlements | 10 + .../PacketTunnelProvider.swift | 37 ++++ 12 files changed, 422 insertions(+), 43 deletions(-) create mode 100644 PIA-VPN-tvOS-NetworkExtension/Info.plist create mode 100644 PIA-VPN-tvOS-NetworkExtension/PIA_VPN_tvOS_NetworkExtension.entitlements create mode 100644 PIA-VPN-tvOS-NetworkExtension/PacketTunnelProvider.swift diff --git a/PIA VPN-tvOS/Dashboard/UseCases/VpnConnectionUseCase.swift b/PIA VPN-tvOS/Dashboard/UseCases/VpnConnectionUseCase.swift index 07d75a12f..21bc03c39 100644 --- a/PIA VPN-tvOS/Dashboard/UseCases/VpnConnectionUseCase.swift +++ b/PIA VPN-tvOS/Dashboard/UseCases/VpnConnectionUseCase.swift @@ -1,5 +1,6 @@ import Foundation +import PIALibrary protocol VpnConnectionUseCaseType { func connect() @@ -16,11 +17,20 @@ class VpnConnectionUseCase: VpnConnectionUseCaseType { } func connect() { - // TODO: Implement + // TODO: Inject VPNProvider object + let vpnProvider = Client.providers.vpnProvider + vpnProvider.connect { error in + NSLog("Connection error: \(error)") + } } func disconnect() { - // TODO: Implement + // TODO: Inject VPNProvider object + let vpnProvider = Client.providers.vpnProvider + vpnProvider.disconnect { error in + NSLog("Disconnect error: \(error)") + } + } func connect(to server: ServerType) { diff --git a/PIA VPN-tvOS/PIA VPN-tvOS.entitlements b/PIA VPN-tvOS/PIA VPN-tvOS.entitlements index 0c67376eb..25512ea17 100644 --- a/PIA VPN-tvOS/PIA VPN-tvOS.entitlements +++ b/PIA VPN-tvOS/PIA VPN-tvOS.entitlements @@ -1,5 +1,14 @@ - + + com.apple.developer.networking.networkextension + + packet-tunnel-provider + + com.apple.security.application-groups + + group.com.privateinternetaccess + + diff --git a/PIA VPN-tvOS/RootContainer/Presentation/RootContainerViewModel.swift b/PIA VPN-tvOS/RootContainer/Presentation/RootContainerViewModel.swift index 050768a01..b2c22e414 100644 --- a/PIA VPN-tvOS/RootContainer/Presentation/RootContainerViewModel.swift +++ b/PIA VPN-tvOS/RootContainer/Presentation/RootContainerViewModel.swift @@ -1,6 +1,7 @@ import Foundation import SwiftUI +import PIALibrary class RootContainerViewModel: ObservableObject { enum State { @@ -11,9 +12,10 @@ class RootContainerViewModel: ObservableObject { } @Published var state: State = .splash + @Published private var isBootstrapped: Bool = false // TODO: Update this value from the Vpn OnBoarding installation profile screen - @AppStorage(.kOnboardingVpnProfileInstalled) var onBoardingVpnProfileInstalled = true + @AppStorage(.kOnboardingVpnProfileInstalled) var onBoardingVpnProfileInstalled = false let accountProvider: AccountProviderType let notificationCenter: NotificationCenterType @@ -30,7 +32,17 @@ class RootContainerViewModel: ObservableObject { notificationCenter.removeObserver(self) } + func phaseDidBecomeActive() { + // Bootstrap PIALibrary preferences and settings + Bootstrapper.shared.bootstrap() + isBootstrapped = true + updateState() + } + private func updateState() { + guard isBootstrapped else { + return + } switch (accountProvider.isLoggedIn, onBoardingVpnProfileInstalled) { // logged in, vpn profile installed case (true, true): @@ -43,7 +55,12 @@ class RootContainerViewModel: ObservableObject { state = .notActivated } } - + +} + +// NotificationCenter subscriptions + +extension RootContainerViewModel { private func subscribeToAccountUpdates() { notificationCenter.addObserver(self, selector: #selector(handleAccountDidLogin), name: .PIAAccountDidLogin, object: nil) @@ -57,6 +74,19 @@ class RootContainerViewModel: ObservableObject { @objc func handleAccountDidLogout() { updateState() } - - +} + +extension RootContainerViewModel { + // FIXME: this method should be in the VpnProfile installation VM object + // Implemented in PIA-888 + func installVpnProfile() { + let vpnProvider = Client.providers.vpnProvider + vpnProvider.install(force: true) { error in + NSLog("Install VPN profile error: \(String(describing: error))") + if error == nil { + self.onBoardingVpnProfileInstalled = true + } + } + + } } diff --git a/PIA VPN-tvOS/RootContainer/UI/RootContainerView.swift b/PIA VPN-tvOS/RootContainer/UI/RootContainerView.swift index d3aa159a8..4a06089c6 100644 --- a/PIA VPN-tvOS/RootContainer/UI/RootContainerView.swift +++ b/PIA VPN-tvOS/RootContainer/UI/RootContainerView.swift @@ -4,25 +4,45 @@ import SwiftUI struct RootContainerView: View { @ObservedObject var viewModel: RootContainerViewModel + @Environment(\.scenePhase) var scenePhase var body: some View { - switch viewModel.state { - case .splash: - VStack { - // TODO: Add Splash screen here + VStack { + switch viewModel.state { + case .splash: + VStack { + // TODO: Add Splash screen here + } + case .notActivated: + LoginFactory.makeLoginView() + case .activatedNotOnboarded: + // TODO: Replace this view with the Onboarding Vpn Profile installation view + VStack { + Text("Show Onboarding vpn installation view") + Button { + viewModel.installVpnProfile() + } label: { + Text("Install Vpn profile") + } + + } + case .activated: + DashboardFactory.makeDashboardView() + } - case .notActivated: - LoginFactory.makeLoginView() - case .activatedNotOnboarded: - // TODO: Replace this view with the Onboarding Vpn Profile installation view - VStack { - Text("Show Onboarding vpn installation view") + + + }.onChange(of: scenePhase) { newPhase in + + if newPhase == .active { + NSLog(">>> Active") + viewModel.phaseDidBecomeActive() + } else if newPhase == .inactive { + NSLog(">>> Inactive") + } else if newPhase == .background { + NSLog(">>> Background") } - case .activated: - DashboardFactory.makeDashboardView() } } - - } diff --git a/PIA VPN.xcodeproj/project.pbxproj b/PIA VPN.xcodeproj/project.pbxproj index ed8e83ffb..9b3d4d811 100644 --- a/PIA VPN.xcodeproj/project.pbxproj +++ b/PIA VPN.xcodeproj/project.pbxproj @@ -176,6 +176,16 @@ 692483202AB05F18002A0407 /* PIAConnectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6924831F2AB05F18002A0407 /* PIAConnectionView.swift */; }; 692483222AB05F37002A0407 /* PIACircleIcon.swift in Sources */ = {isa = PBXBuildFile; fileRef = 692483212AB05F37002A0407 /* PIACircleIcon.swift */; }; 692483262AB05F85002A0407 /* PIAConnectionActivityWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 692483252AB05F85002A0407 /* PIAConnectionActivityWidget.swift */; }; + 693CA5AA2B3ED5F600D38378 /* Bootstrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E98BB6D1FD5BC6200B41D6B /* Bootstrapper.swift */; }; + 693CA5AB2B3ED67A00D38378 /* AppPreferences.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFDC1DF1FE4A450007C0B9B /* AppPreferences.swift */; }; + 693CA5AC2B3ED69B00D38378 /* AppConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFDC1EB1FE4B9DC007C0B9B /* AppConstants.swift */; }; + 693CA5AD2B3ED74100D38378 /* AppConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EFDC1EE1FE4B9E6007C0B9B /* AppConfiguration.swift */; }; + 693CA5AE2B3ED75500D38378 /* Flags.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E492C661FE60907007F23DF /* Flags.swift */; }; + 693CA5B02B3EDB4900D38378 /* RegionFilter.swift in Sources */ = {isa = PBXBuildFile; fileRef = DDD271EF21D6718F00B6D20F /* RegionFilter.swift */; }; + 693CA5B12B3EDBEF00D38378 /* ThemeCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0ECF5C072017EBAD0047596C /* ThemeCode.swift */; }; + 693CA5B72B3F268400D38378 /* NetworkExtension.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DD606AB921C7A17900E0781D /* NetworkExtension.framework */; }; + 693CA5BA2B3F268400D38378 /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 693CA5B92B3F268400D38378 /* PacketTunnelProvider.swift */; }; + 693CA5BF2B3F268400D38378 /* PIA-VPN-tvOS-NetworkExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 693CA5B62B3F268300D38378 /* PIA-VPN-tvOS-NetworkExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 694AC74E2B17AB9C007E7B56 /* DashboardView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 694AC74D2B17AB9C007E7B56 /* DashboardView.swift */; }; 694AC74F2B17ADF9007E7B56 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 291C6397183EBC210039EC03 /* Images.xcassets */; }; 695BF81D2AC30EFB00D1139C /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 0E7EC043209326E30029811E /* Localizable.strings */; }; @@ -657,6 +667,13 @@ remoteGlobalIDString = 0EFB606F203D7A2C0095398C; remoteInfo = "PIA VPN AdBlocker"; }; + 693CA5BD2B3F268400D38378 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 291C6374183EBC210039EC03 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 693CA5B52B3F268300D38378; + remoteInfo = "PIA-VPN-tvOS-NetworkExtension"; + }; 69CB24A22B051CED00D83A52 /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = 291C6374183EBC210039EC03 /* Project object */; @@ -747,6 +764,17 @@ name = "Embed App Extensions"; runOnlyForDeploymentPostprocessing = 0; }; + 693CA5C32B3F268400D38378 /* Embed Foundation Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + 693CA5BF2B3F268400D38378 /* PIA-VPN-tvOS-NetworkExtension.appex in Embed Foundation Extensions */, + ); + name = "Embed Foundation Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ @@ -899,6 +927,11 @@ 6924831F2AB05F18002A0407 /* PIAConnectionView.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = PIAConnectionView.swift; sourceTree = ""; tabWidth = 4; }; 692483212AB05F37002A0407 /* PIACircleIcon.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = PIACircleIcon.swift; sourceTree = ""; tabWidth = 4; }; 692483252AB05F85002A0407 /* PIAConnectionActivityWidget.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = PIAConnectionActivityWidget.swift; sourceTree = ""; tabWidth = 4; }; + 693CA5AF2B3ED9E100D38378 /* PIA VPN-tvOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "PIA VPN-tvOS.entitlements"; sourceTree = ""; }; + 693CA5B62B3F268300D38378 /* PIA-VPN-tvOS-NetworkExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "PIA-VPN-tvOS-NetworkExtension.appex"; sourceTree = BUILT_PRODUCTS_DIR; }; + 693CA5B92B3F268400D38378 /* PacketTunnelProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PacketTunnelProvider.swift; sourceTree = ""; }; + 693CA5BB2B3F268400D38378 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 693CA5BC2B3F268400D38378 /* PIA_VPN_tvOS_NetworkExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = PIA_VPN_tvOS_NetworkExtension.entitlements; sourceTree = ""; }; 6947AADB2ACDC8AE001BCC66 /* PIA-VPN-e2e-simulator.xctestplan */ = {isa = PBXFileReference; lastKnownFileType = text; path = "PIA-VPN-e2e-simulator.xctestplan"; sourceTree = ""; }; 694AC74D2B17AB9C007E7B56 /* DashboardView.swift */ = {isa = PBXFileReference; indentWidth = 4; lastKnownFileType = sourcecode.swift; path = DashboardView.swift; sourceTree = ""; tabWidth = 4; }; 696E8F0C2B31A8760080BB31 /* NotificationCenterMock.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationCenterMock.swift; sourceTree = ""; }; @@ -1286,6 +1319,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 693CA5B32B3F268300D38378 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 693CA5B72B3F268400D38378 /* NetworkExtension.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 69B70AAD2ACBF51C0072A09D /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -1554,6 +1595,7 @@ E5C507782B0E144E00200A6A /* PIA VPN-tvOS */, E5C507892B0E145100200A6A /* PIA VPN-tvOSTests */, E5C507932B0E145100200A6A /* PIA VPN-tvOSUITests */, + 693CA5B82B3F268400D38378 /* PIA-VPN-tvOS-NetworkExtension */, 291C637E183EBC210039EC03 /* Frameworks */, 291C637D183EBC210039EC03 /* Products */, ); @@ -1573,6 +1615,7 @@ E5C507772B0E144D00200A6A /* PIA VPN-tvOS.app */, E5C507862B0E145100200A6A /* PIA VPN-tvOSTests.xctest */, E5C507902B0E145100200A6A /* PIA VPN-tvOSUITests.xctest */, + 693CA5B62B3F268300D38378 /* PIA-VPN-tvOS-NetworkExtension.appex */, ); name = Products; sourceTree = ""; @@ -1749,6 +1792,16 @@ path = Core; sourceTree = ""; }; + 693CA5B82B3F268400D38378 /* PIA-VPN-tvOS-NetworkExtension */ = { + isa = PBXGroup; + children = ( + 693CA5B92B3F268400D38378 /* PacketTunnelProvider.swift */, + 693CA5BB2B3F268400D38378 /* Info.plist */, + 693CA5BC2B3F268400D38378 /* PIA_VPN_tvOS_NetworkExtension.entitlements */, + ); + path = "PIA-VPN-tvOS-NetworkExtension"; + sourceTree = ""; + }; 694AC74B2B17AB73007E7B56 /* Dashboard */ = { isa = PBXGroup; children = ( @@ -2284,6 +2337,7 @@ E5C507782B0E144E00200A6A /* PIA VPN-tvOS */ = { isa = PBXGroup; children = ( + 693CA5AF2B3ED9E100D38378 /* PIA VPN-tvOS.entitlements */, 69A226B92B307C8A0065EDDB /* RootContainer */, 69A226AF2B3074110065EDDB /* Navigation */, E5C507A32B153A1800200A6A /* Login */, @@ -2576,6 +2630,23 @@ productReference = 291C637C183EBC210039EC03 /* PIA VPN.app */; productType = "com.apple.product-type.application"; }; + 693CA5B52B3F268300D38378 /* PIA-VPN-tvOS-NetworkExtension */ = { + isa = PBXNativeTarget; + buildConfigurationList = 693CA5C02B3F268400D38378 /* Build configuration list for PBXNativeTarget "PIA-VPN-tvOS-NetworkExtension" */; + buildPhases = ( + 693CA5B22B3F268300D38378 /* Sources */, + 693CA5B32B3F268300D38378 /* Frameworks */, + 693CA5B42B3F268300D38378 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "PIA-VPN-tvOS-NetworkExtension"; + productName = "PIA-VPN-tvOS-NetworkExtension"; + productReference = 693CA5B62B3F268300D38378 /* PIA-VPN-tvOS-NetworkExtension.appex */; + productType = "com.apple.product-type.tv-app-extension"; + }; 69B70AAF2ACBF51C0072A09D /* PIA-VPN_E2E_Tests */ = { isa = PBXNativeTarget; buildConfigurationList = 69B70ABA2ACBF51C0072A09D /* Build configuration list for PBXNativeTarget "PIA-VPN_E2E_Tests" */; @@ -2643,10 +2714,12 @@ E5C507732B0E144D00200A6A /* Sources */, E5C507742B0E144D00200A6A /* Frameworks */, E5C507752B0E144D00200A6A /* Resources */, + 693CA5C32B3F268400D38378 /* Embed Foundation Extensions */, ); buildRules = ( ); dependencies = ( + 693CA5BE2B3F268400D38378 /* PBXTargetDependency */, ); name = "PIA VPN-tvOS"; packageProductDependencies = ( @@ -2699,7 +2772,7 @@ isa = PBXProject; attributes = { CLASSPREFIX = PIA; - LastSwiftUpdateCheck = 1500; + LastSwiftUpdateCheck = 1510; LastUpgradeCheck = 0930; ORGANIZATIONNAME = "Private Internet Access Inc."; TargetAttributes = { @@ -2769,6 +2842,9 @@ }; }; }; + 693CA5B52B3F268300D38378 = { + CreatedOnToolsVersion = 15.1; + }; 69B70AAF2ACBF51C0072A09D = { CreatedOnToolsVersion = 14.3.1; TestTargetID = 291C637B183EBC210039EC03; @@ -2848,6 +2924,7 @@ E5C507762B0E144D00200A6A /* PIA VPN-tvOS */, E5C507852B0E145100200A6A /* PIA VPN-tvOSTests */, E5C5078F2B0E145100200A6A /* PIA VPN-tvOSUITests */, + 693CA5B52B3F268300D38378 /* PIA-VPN-tvOS-NetworkExtension */, ); }; /* End PBXProject section */ @@ -2984,6 +3061,13 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 693CA5B42B3F268300D38378 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; 69B70AAE2ACBF51C0072A09D /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -3449,6 +3533,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 693CA5B22B3F268300D38378 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 693CA5BA2B3F268400D38378 /* PacketTunnelProvider.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; 69B70AAC2ACBF51C0072A09D /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -3531,6 +3623,7 @@ E5AB288C2B2A487A00744E5F /* LoginViewModelErrorHandler.swift in Sources */, E5AB28732B279B7000744E5F /* LoginProvider.swift in Sources */, E5C507A52B153A2F00200A6A /* LoginViewModel.swift in Sources */, + 693CA5AE2B3ED75500D38378 /* Flags.swift in Sources */, E5C507B72B17E75B00200A6A /* CheckLoginAvailability.swift in Sources */, E5AB288E2B2A489500744E5F /* LoginStatus.swift in Sources */, 69FF0B132B3AF2050074AA04 /* QuickConnectButtonViewModel.swift in Sources */, @@ -3540,8 +3633,10 @@ 69A226B82B3079EA0065EDDB /* PIA+String.swift in Sources */, 69F1C29C2B2B299300E924AE /* VpnConnectionUseCase.swift in Sources */, 69A226B32B3074D30065EDDB /* AppRouter.swift in Sources */, + 693CA5AB2B3ED67A00D38378 /* AppPreferences.swift in Sources */, E5AB288A2B29BDED00744E5F /* Foundation+PIA.swift in Sources */, E5C507A22B0FE40000200A6A /* LoginView.swift in Sources */, + 693CA5AD2B3ED74100D38378 /* AppConfiguration.swift in Sources */, E5AB287D2B28E4E700744E5F /* UserAccountMapper.swift in Sources */, 69FF0B0A2B3AD3F60074AA04 /* QuickConnectViewModel.swift in Sources */, 699311A02B31894D00D316C8 /* Foundation+Protocols.swift in Sources */, @@ -3553,9 +3648,11 @@ 69FF0B0D2B3AD57C0074AA04 /* SelectedServerUseCase.swift in Sources */, 69FF0B112B3AF1F00074AA04 /* QuickConnectButton.swift in Sources */, E5AB28712B279B4000744E5F /* LoginProviderType.swift in Sources */, + 693CA5B02B3EDB4900D38378 /* RegionFilter.swift in Sources */, E5AB287B2B28B4C400744E5F /* Credentials.swift in Sources */, E5C5077C2B0E144E00200A6A /* ContentView.swift in Sources */, 69A226BE2B307D5F0065EDDB /* RootContainerView.swift in Sources */, + 693CA5AA2B3ED5F600D38378 /* Bootstrapper.swift in Sources */, 694AC74E2B17AB9C007E7B56 /* DashboardView.swift in Sources */, 69F1C2932B2B10D400E924AE /* DashboardFactory.swift in Sources */, E5C5077A2B0E144E00200A6A /* PIA_VPN_tvOSApp.swift in Sources */, @@ -3563,8 +3660,10 @@ 69A226B52B3075AB0065EDDB /* RootContainerViewModel.swift in Sources */, E5C507C72B1FABFF00200A6A /* LoginDomainErrorMapperType.swift in Sources */, 69F1C2972B2B239000E924AE /* PIAConnectionButtonViewModel.swift in Sources */, + 693CA5B12B3EDBEF00D38378 /* ThemeCode.swift in Sources */, E5C507B32B17E5AD00200A6A /* LoginError.swift in Sources */, E5C507CA2B1FAC3600200A6A /* LoginDomainErrorMapper.swift in Sources */, + 693CA5AC2B3ED69B00D38378 /* AppConstants.swift in Sources */, 69FF0B0F2B3AE8F60074AA04 /* PIAImages+SwiftUI.swift in Sources */, E5AB28772B28B49A00744E5F /* UserAccount.swift in Sources */, 69A226C02B307DBF0065EDDB /* RootContainerFactory.swift in Sources */, @@ -3642,6 +3741,11 @@ target = 0EFB606F203D7A2C0095398C /* PIA VPN AdBlocker */; targetProxy = 0EFB607E203D893E0095398C /* PBXContainerItemProxy */; }; + 693CA5BE2B3F268400D38378 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 693CA5B52B3F268300D38378 /* PIA-VPN-tvOS-NetworkExtension */; + targetProxy = 693CA5BD2B3F268400D38378 /* PBXContainerItemProxy */; + }; 69CB24A32B051CED00D83A52 /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = 291C637B183EBC210039EC03 /* PIA VPN */; @@ -4307,6 +4411,95 @@ }; name = Release; }; + 693CA5C12B3F268400D38378 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = "PIA-VPN-tvOS-NetworkExtension/PIA_VPN_tvOS_NetworkExtension.entitlements"; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = 5357M5NW9W; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "PIA-VPN-tvOS-NetworkExtension/Info.plist"; + INFOPLIST_KEY_CFBundleDisplayName = "PIA-VPN-tvOS-NetworkExtension"; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Private Internet Access Inc. All rights reserved."; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN-tvOS.PacketTunnel"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 17.2; + }; + name = Debug; + }; + 693CA5C22B3F268400D38378 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = "PIA-VPN-tvOS-NetworkExtension/PIA_VPN_tvOS_NetworkExtension.entitlements"; + CODE_SIGN_STYLE = Automatic; + COPY_PHASE_STRIP = NO; + CURRENT_PROJECT_VERSION = 1; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = 5357M5NW9W; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "PIA-VPN-tvOS-NetworkExtension/Info.plist"; + INFOPLIST_KEY_CFBundleDisplayName = "PIA-VPN-tvOS-NetworkExtension"; + INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2023 Private Internet Access Inc. All rights reserved."; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MARKETING_VERSION = 1.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_BUNDLE_IDENTIFIER = "com.privateinternetaccess.ios.PIA-VPN-tvOS.PacketTunnel"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = appletvos; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = 3; + TVOS_DEPLOYMENT_TARGET = 17.2; + }; + name = Release; + }; 69B70AB82ACBF51C0072A09D /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -4535,6 +4728,7 @@ E5C507982B0E145100200A6A /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -4545,6 +4739,7 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = "PIA VPN-tvOS/PIA VPN-tvOS.entitlements"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEBUG_INFORMATION_FORMAT = dwarf; @@ -4580,6 +4775,7 @@ E5C507992B0E145100200A6A /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = "App Icon & Top Shelf Image"; ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; @@ -4590,6 +4786,7 @@ CLANG_WARN_DOCUMENTATION_COMMENTS = YES; CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CODE_SIGN_ENTITLEMENTS = "PIA VPN-tvOS/PIA VPN-tvOS.entitlements"; CODE_SIGN_STYLE = Automatic; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; @@ -4826,6 +5023,15 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 693CA5C02B3F268400D38378 /* Build configuration list for PBXNativeTarget "PIA-VPN-tvOS-NetworkExtension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 693CA5C12B3F268400D38378 /* Debug */, + 693CA5C22B3F268400D38378 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; 69B70ABA2ACBF51C0072A09D /* Build configuration list for PBXNativeTarget "PIA-VPN_E2E_Tests" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/PIA VPN/AppConfiguration.swift b/PIA VPN/AppConfiguration.swift index ce0f3cc10..508340833 100644 --- a/PIA VPN/AppConfiguration.swift +++ b/PIA VPN/AppConfiguration.swift @@ -22,8 +22,10 @@ import Foundation import PIALibrary +#if canImport(TunnelKitCore) import TunnelKitCore import TunnelKitOpenVPN +#endif import UIKit struct AppConfiguration { @@ -66,7 +68,8 @@ struct AppConfiguration { } static let profileName = "Private Internet Access" - + +#if os(iOS) static let piaDefaultConfigurationBuilder: OpenVPNProvider.ConfigurationBuilder = { var sessionBuilder = OpenVPN.ConfigurationBuilder() sessionBuilder.renegotiatesAfter = piaRenegotiationInterval @@ -88,11 +91,13 @@ struct AppConfiguration { return builder }() + static let piaAutomaticProtocols: [EndpointProtocol] = [ // let vpnPorts = Client.providers.serverProvider.currentServersConfiguration.vpnPorts EndpointProtocol(.udp, 8080), EndpointProtocol(.tcp, 443) ] +#endif private static let piaCustomRenegotiation: Renegotiation = .qa diff --git a/PIA VPN/AppConstants.swift b/PIA VPN/AppConstants.swift index bd3dd4da1..aef05c744 100644 --- a/PIA VPN/AppConstants.swift +++ b/PIA VPN/AppConstants.swift @@ -144,7 +144,7 @@ struct AppConstants { return servers }() } - +#if os(iOS) struct Fonts { static let typeface: Theme.Typeface = { let typeface = Theme.Typeface() @@ -153,6 +153,7 @@ struct AppConstants { return typeface }() } +#endif struct VPNWidget { static let vpnStatus = "vpn.status" diff --git a/PIA VPN/AppPreferences.swift b/PIA VPN/AppPreferences.swift index bae1dd396..48236e3ec 100644 --- a/PIA VPN/AppPreferences.swift +++ b/PIA VPN/AppPreferences.swift @@ -22,8 +22,10 @@ import Foundation import PIALibrary +#if canImport(TunnelKitCore) import TunnelKitCore import TunnelKitOpenVPN +#endif import SwiftyBeaver import Intents import UIKit @@ -141,7 +143,7 @@ class AppPreferences { defaults.set(newValue, forKey: Entries.didAskToEnableNotifications) } } - +#if os(iOS) var currentThemeCode: ThemeCode { get { let rawCode = defaults.integer(forKey: Entries.themeCode) @@ -151,7 +153,7 @@ class AppPreferences { defaults.set(newValue.rawValue, forKey: Entries.themeCode) } } - +#endif var lastVPNConnectionStatus: PIALibrary.VPNStatus { get { guard let rawValue = defaults.string(forKey: Entries.lastVPNConnectionStatus) else { @@ -163,7 +165,7 @@ class AppPreferences { defaults.set(newValue.rawValue, forKey: Entries.lastVPNConnectionStatus) } } - +#if os(iOS) // nil = automatic var piaSocketType: SocketType? { get { @@ -180,7 +182,7 @@ class AppPreferences { } } } - + var piaHandshake: OpenVPN.Configuration.Handshake { get { guard let rawValue = defaults.string(forKey: Entries.piaHandshake) else { @@ -193,7 +195,7 @@ class AppPreferences { defaults.set(newValue.rawValue, forKey: Entries.piaHandshake) } } - +#endif var favoriteServerIdentifiersGen4: [String] { get { let keychain = PIALibrary.Keychain(team: AppConstants.teamId, group: AppConstants.appGroup) @@ -343,7 +345,7 @@ class AppPreferences { } } } - +#if os(iOS) @available(iOS 12.0, *) var connectShortcut: INVoiceShortcut? { get { @@ -376,7 +378,7 @@ class AppPreferences { defaults.set(encodedObject, forKey: Entries.disconnectShortcut) } } } - +#endif var quickSettingThemeVisible: Bool{ get { return defaults.bool(forKey: Entries.quickSettingThemeVisible) @@ -664,7 +666,7 @@ class AppPreferences { try? keychain.set(favorites: favorites) } } - + #if os(iOS) func migrateOVPN() { guard let currentOpenVPNConfiguration = Client.preferences.vpnCustomConfiguration(for: PIATunnelProfile.vpnType) as? OpenVPNProvider.Configuration ?? @@ -704,6 +706,7 @@ class AppPreferences { } } + #endif func migrateNMT() { @@ -829,6 +832,7 @@ class AppPreferences { } func reset() { + #if os(iOS) piaHandshake = .rsa4096 piaSocketType = nil favoriteServerIdentifiersGen4 = [] @@ -842,6 +846,7 @@ class AppPreferences { transitionTheme(to: .light) return } + #endif quickSettingThemeVisible = true quickSettingKillswitchVisible = true quickSettingNetworkToolVisible = true @@ -862,13 +867,16 @@ class AppPreferences { showServiceMessages = false dedicatedTokenIPReleation = [:] appEnvironmentIsProduction = Client.environment == .production ? true : false + #if os(iOS) MessagesManager.shared.reset() + #endif userInteractedWithSurvey = false successConnectionsUntilSurvey = nil Client.preferences.lastKnownException = nil } func clean() { + #if os(iOS) piaHandshake = .rsa4096 piaSocketType = nil favoriteServerIdentifiersGen4 = [] @@ -882,6 +890,7 @@ class AppPreferences { todayWidgetVpnPort = "500" todayWidgetVpnSocket = "UDP" todayWidgetTrustedNetwork = false + #endif quickSettingThemeVisible = true quickSettingKillswitchVisible = true quickSettingNetworkToolVisible = true @@ -900,7 +909,9 @@ class AppPreferences { showServiceMessages = false dismissedMessages = [] dedicatedTokenIPReleation = [:] + #if os(iOS) MessagesManager.shared.reset() + #endif appEnvironmentIsProduction = Client.environment == .production ? true : false userInteractedWithSurvey = false successConnectionsUntilSurvey = nil @@ -908,7 +919,7 @@ class AppPreferences { } // + (void)eraseForTesting; - +#if os(iOS) func transitionTheme(to code: ThemeCode, withDuration duration: Double = AppConfiguration.Animations.duration) { guard !isTransitioningTheme else { return @@ -947,6 +958,7 @@ class AppPreferences { } } } +#endif // MARK: Connections func incrementSuccessConnections() { diff --git a/PIA VPN/Bootstrapper.swift b/PIA VPN/Bootstrapper.swift index 3d974406d..bb518bf13 100644 --- a/PIA VPN/Bootstrapper.swift +++ b/PIA VPN/Bootstrapper.swift @@ -22,10 +22,13 @@ import Foundation import PIALibrary +#if canImport(TunnelKitCore) import TunnelKitCore import TunnelKitOpenVPN -import SwiftyBeaver import PIAWireguard +#endif +import SwiftyBeaver + class Bootstrapper { @@ -87,7 +90,7 @@ class Bootstrapper { AppPreferences.shared.migrateNMT() // PIALibrary - + #if os(iOS) guard let bundledRegionsURL = AppConstants.RegionsGEN4.bundleURL else { fatalError("Could not find bundled regions file") } @@ -97,6 +100,7 @@ class Bootstrapper { } catch let e { fatalError("Could not parse bundled regions file: \(e)") } + #endif Client.configuration.rsa4096Certificate = rsa4096Certificate() @@ -127,20 +131,25 @@ class Bootstrapper { Client.configuration.enablesConnectivityUpdates = true Client.configuration.enablesServerUpdates = true Client.configuration.enablesServerPings = true + #if os(iOS) Client.configuration.bundledServersJSON = bundledServersJSON + #endif Client.configuration.webTimeout = AppConfiguration.ClientConfiguration.webTimeout Client.configuration.vpnProfileName = AppConfiguration.VPN.profileName + #if os(iOS) Client.configuration.addVPNProfile(PIATunnelProfile(bundleIdentifier: AppConstants.Extensions.tunnelBundleIdentifier)) Client.configuration.addVPNProfile(PIAWGTunnelProfile(bundleIdentifier: AppConstants.Extensions.tunnelWireguardBundleIdentifier)) - + #endif let defaults = Client.preferences.defaults defaults.isPersistentConnection = true defaults.mace = false defaults.vpnType = IKEv2Profile.vpnType + #if os(iOS) defaults.vpnCustomConfigurations = [ PIATunnelProfile.vpnType: AppConfiguration.VPN.piaDefaultConfigurationBuilder.build(), PIAWGTunnelProfile.vpnType: PIAWireguardConfiguration(customDNSServers: [], packetSize: AppConstants.WireGuardPacketSize.defaultPacketSize) ] + #endif if Client.preferences.shareServiceQualityData { ServiceQualityManager.shared.start() @@ -162,6 +171,7 @@ class Bootstrapper { }) //FORCE THE MIGRATION TO GEN4 + #if os(iOS) if Client.providers.vpnProvider.needsMigrationToGEN4() { Client.preferences.displayedServer = Server.automatic @@ -171,7 +181,7 @@ class Bootstrapper { Client.providers.vpnProvider.reconnect(after: 200, forceDisconnect: true, { _ in }) } - + #endif Client.providers.accountProvider.subscriptionInformation { [weak self] (info, error) in if let _ = error { @@ -217,25 +227,28 @@ class Bootstrapper { } pref.commit() - + #if os(iOS) AppPreferences.shared.migrateOVPN() - + // Business objects AccountObserver.shared.start() // DataCounter.shared.startCounting() - + #endif // Notifications let nc = NotificationCenter.default + #if os(iOS) nc.addObserver(self, selector: #selector(reloadTheme), name: .PIAThemeDidChange, object: nil) + #endif nc.addObserver(self, selector: #selector(vpnStatusDidChange(notification:)), name: .PIADaemonsDidUpdateVPNStatus, object: nil) nc.addObserver(self, selector: #selector(internetUnreachable(notification:)), name: .ConnectivityDaemonDidGetUnreachable, object: nil) nc.addObserver(self, selector: #selector(internetReachable(notification:)), name: .ConnectivityDaemonDidGetReachable, object: nil) // PIALibrary (Theme) - + #if os(iOS) AppPreferences.shared.currentThemeCode.apply(theme: Theme.current, reload: true) + #endif // show walkthrough on upgrade except for logged in users if Client.providers.accountProvider.isLoggedIn { @@ -267,36 +280,49 @@ class Bootstrapper { // MARK: Certificate func rsa4096Certificate() -> String? { + #if os(iOS) return AppPreferences.shared.piaHandshake.pemString() + #else + // FIXME: Implement for tvOS + return nil + #endif } // MARK: Notifications - +#if os(iOS) @objc private func reloadTheme() { Theme.current.applySideMenu() Theme.current.applyAppearance() } - +#endif @objc private func vpnStatusDidChange(notification: Notification) { let vpnStatus = Client.providers.vpnProvider.vpnStatus switch vpnStatus { case .connected: AppPreferences.shared.incrementSuccessConnections() + #if os(iOS) UserSurveyManager.shared.handleConnectionSuccess() + #endif case .disconnected: AppPreferences.shared.incrementSuccessDisconnections() default: break } + #if os(iOS) RatingManager.shared.handleConnectionStatusChanged() + #endif } @objc private func internetReachable(notification: Notification) { + #if os(iOS) Macros.removeStickyNote() + #endif } @objc private func internetUnreachable(notification: Notification) { + #if os(iOS) Macros.displayStickyNote(withMessage: L10n.Localizable.Global.unreachable, andImage: Asset.Images.iconWarning.image) + #endif } } diff --git a/PIA-VPN-tvOS-NetworkExtension/Info.plist b/PIA-VPN-tvOS-NetworkExtension/Info.plist new file mode 100644 index 000000000..3059459e1 --- /dev/null +++ b/PIA-VPN-tvOS-NetworkExtension/Info.plist @@ -0,0 +1,13 @@ + + + + + NSExtension + + NSExtensionPointIdentifier + com.apple.networkextension.packet-tunnel + NSExtensionPrincipalClass + $(PRODUCT_MODULE_NAME).PacketTunnelProvider + + + diff --git a/PIA-VPN-tvOS-NetworkExtension/PIA_VPN_tvOS_NetworkExtension.entitlements b/PIA-VPN-tvOS-NetworkExtension/PIA_VPN_tvOS_NetworkExtension.entitlements new file mode 100644 index 000000000..448ac3e95 --- /dev/null +++ b/PIA-VPN-tvOS-NetworkExtension/PIA_VPN_tvOS_NetworkExtension.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.com.privateinternetaccess + + + diff --git a/PIA-VPN-tvOS-NetworkExtension/PacketTunnelProvider.swift b/PIA-VPN-tvOS-NetworkExtension/PacketTunnelProvider.swift new file mode 100644 index 000000000..a9753e481 --- /dev/null +++ b/PIA-VPN-tvOS-NetworkExtension/PacketTunnelProvider.swift @@ -0,0 +1,37 @@ +// +// PacketTunnelProvider.swift +// PIA-VPN-tvOS-NetworkExtension +// +// Created by Laura S on 12/29/23. +// Copyright © 2023 Private Internet Access Inc. All rights reserved. +// + +import NetworkExtension + +class PacketTunnelProvider: NEPacketTunnelProvider { + + override func startTunnel(options: [String : NSObject]?, completionHandler: @escaping (Error?) -> Void) { + // Add code here to start the process of connecting the tunnel. + } + + override func stopTunnel(with reason: NEProviderStopReason, completionHandler: @escaping () -> Void) { + // Add code here to start the process of stopping the tunnel. + completionHandler() + } + + override func handleAppMessage(_ messageData: Data, completionHandler: ((Data?) -> Void)?) { + // Add code here to handle the message. + if let handler = completionHandler { + handler(messageData) + } + } + + override func sleep(completionHandler: @escaping () -> Void) { + // Add code here to get ready to sleep. + completionHandler() + } + + override func wake() { + // Add code here to wake up. + } +}