Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions LocalPackages/PIALibrary/Sources/PIALibrary/ClientError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ public enum ClientError: Error, Equatable {
/// The specified VPN profile protocol is unavailable.
case vpnProfileUnavailable

/// The specified VPN type is not supported or has no profile configuration.
case unsupportedVPNType

/// Error while checking the dip token renewal.
case dipTokenRenewalError

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ public final class MockVPNProvider: VPNProvider, ConfigurationAccess, DatabaseAc
callback?(nil)
}

/// :nodoc:
public func install(force forceInstall: Bool) async throws {
Macros.postNotification(.PIAVPNDidInstall)
}

/// :nodoc:
public func uninstall(_ callback: SuccessLibraryCallback?) {
callback?(nil)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,70 +131,89 @@ open class DefaultVPNProvider: VPNProvider, ConfigurationAccess, DatabaseAccess,
if self.accessedProviders.accountProvider.isLoggedIn {
self.install(force: force, nil)
}


}

@available(*, deprecated, renamed: "install(force:)")
public func install(force forceInstall: Bool, _ callback: SuccessLibraryCallback?) {
guard accessedProviders.accountProvider.isLoggedIn else {
callback?(ClientError.unauthorized)
return
}

let newVPNType = accessedPreferences.vpnType
guard let profile = accessedConfiguration.profile(forVPNType: newVPNType) else {
callback?(ClientError.vpnProfileUnavailable)
return
Task {
do {
try await install(force: forceInstall)
callback?(nil)
} catch {
callback?(error)
}
}
}

var previousProfile: VPNProfile?
if (newVPNType != activeProfile?.vpnType) {
previousProfile = activeProfile
}
public func install(force forceInstall: Bool) async throws {
try await withCheckedThrowingContinuation { (continuation: CheckedContinuation<Void, Error>) in
guard accessedProviders.accountProvider.isLoggedIn else {
log.error("VPN install failed: User is not logged in (unauthorized)")
continuation.resume(throwing: ClientError.unauthorized)
return
}

let forcedStatuses = DefaultVPNProvider.forcedStatuses.contains(accessedDatabase.transient.vpnStatus)
let installBlock: SuccessLibraryCallback = { (error) in
guard let configuration = self.vpnClientConfiguration(for: profile) else {
callback?(ClientError.vpnProfileUnavailable)
let newVPNType = accessedPreferences.vpnType
guard let profile = accessedConfiguration.profile(forVPNType: newVPNType) else {
log.error("VPN install failed: No profile configuration found for VPN type: \(newVPNType)")
continuation.resume(throwing: ClientError.unsupportedVPNType)
return
}
profile.save(withConfiguration: configuration, force: forcedStatuses) { (error) in
if let error = error {
callback?(error)

var previousProfile: VPNProfile?
if newVPNType != activeProfile?.vpnType {
previousProfile = activeProfile
}

let forcedStatuses = DefaultVPNProvider.forcedStatuses.contains(accessedDatabase.transient.vpnStatus)
let installBlock: SuccessLibraryCallback = { _ in
guard let configuration = self.vpnClientConfiguration(for: profile) else {
continuation.resume(throwing: ClientError.vpnProfileUnavailable)
return
}
self.activeProfile = profile
profile.save(withConfiguration: configuration, force: forcedStatuses) { error in
if let error {
log.error("VPN install failed: Profile save failed with error: \(error.localizedDescription)")
continuation.resume(throwing: error)
return
}
self.activeProfile = profile

if let previousProfile = previousProfile,
!((profile.vpnType == IPSecProfile.vpnType || profile.vpnType == IKEv2Profile.vpnType) &&
(previousProfile.vpnType == IPSecProfile.vpnType || previousProfile.vpnType == IKEv2Profile.vpnType)) {
//only remove the profile if is not Ipsec or IKEv2, if are one of them, override instead
previousProfile.remove({ _ in
if let previousProfile,
!((profile.vpnType == IPSecProfile.vpnType || profile.vpnType == IKEv2Profile.vpnType) &&
(previousProfile.vpnType == IPSecProfile.vpnType || previousProfile.vpnType == IKEv2Profile.vpnType)) {
//only remove the profile if is not Ipsec or IKEv2, if are one of them, override instead
previousProfile.remove { _ in
Macros.postNotification(.PIAVPNDidInstall)
continuation.resume()
}
} else {
if previousProfile != nil { // dont connect after install
self.connect(nil)
}
Macros.postNotification(.PIAVPNDidInstall)
callback?(nil)
})
} else {
if previousProfile != nil { // dont connect after install
self.connect(nil)
continuation.resume()
}
Macros.postNotification(.PIAVPNDidInstall)
callback?(nil)
}
}
}

if let previousProfile = previousProfile {
previousProfile.disconnect(installBlock)
} else {
if newVPNType != activeProfile?.vpnType || !forcedStatuses || forceInstall {
//only install if new and connected
if Client.providers.vpnProvider.vpnStatus == .connected || forceInstall {
installBlock(nil)
if let previousProfile = previousProfile {
previousProfile.disconnect(installBlock)
} else {
if newVPNType != activeProfile?.vpnType || !forcedStatuses || forceInstall {
//only install if new and connected
if Client.providers.vpnProvider.vpnStatus == .connected || forceInstall {
installBlock(nil)
} else {
continuation.resume()
}
} else {
continuation.resume()
}
}
}
}

public func disable(_ callback: SuccessLibraryCallback?) {
guard let activeProfile = activeProfile else {
callback?(ClientError.vpnProfileUnavailable)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,14 @@ public protocol VPNProvider: AnyObject {
*/
func install(force forceInstall: Bool, _ callback: SuccessLibraryCallback?)

/**
Installs the profile as per `currentVPNType`.

- Parameter forceInstall: Force the install of the profile.
- Throws: Error if installation fails.
*/
func install(force forceInstall: Bool) async throws

/**
Disables the current profile.

Expand Down Expand Up @@ -129,7 +137,7 @@ public protocol VPNProvider: AnyObject {
}

public extension VPNProvider {
public func reconnect(after delay: Int?, forceDisconnect: Bool = false, _ callback: SuccessLibraryCallback?) {
func reconnect(after delay: Int?, forceDisconnect: Bool = false, _ callback: SuccessLibraryCallback?) {
return reconnect(after: delay, forceDisconnect: forceDisconnect, callback)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,18 +18,12 @@ class InstallVpnConfigurationProvider: InstallVPNConfigurationUseCaseType {
self.vpnConfigurationAvailability = vpnConfigurationAvailability
}


func callAsFunction() async throws {
return try await withCheckedThrowingContinuation { continuation in
vpnProvider.install(force: true) { [self] error in
if error != nil {
continuation.resume(throwing: InstallVPNConfigurationError.userCanceled)
return
}

vpnConfigurationAvailability.set(value: true)
continuation.resume()
}
do {
try await vpnProvider.install(force: true)
vpnConfigurationAvailability.set(value: true)
} catch {
throw InstallVPNConfigurationError.userCanceled
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import Foundation
import PIALibrary

protocol VpnConfigurationProviderType {
func install(force forceInstall: Bool, _ callback: SuccessLibraryCallback?)

func install(force forceInstall: Bool) async throws
func uninstall(_ callback: SuccessLibraryCallback?)
}

Expand All @@ -21,11 +20,11 @@ class VpnConfigurationProvider: VpnConfigurationProviderType {
init(vpnProvider: VPNProvider) {
self.vpnProvider = vpnProvider
}
func install(force forceInstall: Bool, _ callback: PIALibrary.SuccessLibraryCallback?) {
vpnProvider.install(force: forceInstall, callback)

func install(force forceInstall: Bool) async throws {
try await vpnProvider.install(force: forceInstall)
}

func uninstall(_ callback: SuccessLibraryCallback?) {
self.vpnProvider.uninstall(callback)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,13 @@ class VpnConfigurationProviderTypeMock: VpnConfigurationProviderType {
func install(force forceInstall: Bool, _ callback: SuccessLibraryCallback?) {
callback?(error)
}


func install(force forceInstall: Bool) async throws {
if let error = error {
throw error
}
}

func uninstall(_ callback: PIALibrary.SuccessLibraryCallback?) {
callback?(error)
}
Expand Down
20 changes: 11 additions & 9 deletions PIA VPN/UI/Dashboard/VPNPermissionViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,10 @@
import UIKit
import PIALibrary
import MessageUI
import PIADesignSystem
import PIAUIKit
import PIADesignSystem

private let log = PIALogger.logger(for: VPNPermissionViewController.self)

final class VPNPermissionViewController: AutolayoutViewController {
@IBOutlet private weak var contentCardView: UIView!
Expand Down Expand Up @@ -68,16 +70,16 @@ final class VPNPermissionViewController: AutolayoutViewController {
}

@IBAction private func submit() {
let vpn = Client.providers.vpnProvider
vpn.install(force: true, { (error) in
guard (error == nil) else {
Task { @MainActor in
do {
let vpn = Client.providers.vpnProvider
try await vpn.install(force: true)
self.dismissingViewController?.dismiss(animated: true)
} catch {
log.error("VPN install failed with error: \(error.localizedDescription)")
self.alertRequiredPermission()
return
}
self.dismissingViewController?.dismiss(animated: true) {
// vpn.connect(nil)
}
})
}
}

private func alertRequiredPermission() {
Expand Down