mirror of
https://github.com/passepartoutvpn/passepartout-apple.git
synced 2025-02-22 15:52:08 +00:00
Merge branch 'use-external-infra-certs'
This commit is contained in:
commit
cbbf136d10
101
Passepartout-iOS/Global/Downloader.swift
Normal file
101
Passepartout-iOS/Global/Downloader.swift
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
//
|
||||||
|
// Downloader.swift
|
||||||
|
// Passepartout-iOS
|
||||||
|
//
|
||||||
|
// Created by Davide De Rosa on 4/10/19.
|
||||||
|
// Copyright (c) 2019 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 MBProgressHUD
|
||||||
|
import SwiftyBeaver
|
||||||
|
import Passepartout_Core
|
||||||
|
|
||||||
|
private let log = SwiftyBeaver.self
|
||||||
|
|
||||||
|
class Downloader: NSObject {
|
||||||
|
static let shared = Downloader(temporaryURL: GroupConstants.App.cachesURL.appendingPathComponent("downloaded.tmp"))
|
||||||
|
|
||||||
|
private let temporaryURL: URL
|
||||||
|
|
||||||
|
private var hud: MBProgressHUD?
|
||||||
|
|
||||||
|
private var completionHandler: ((URL?, Error?) -> Void)?
|
||||||
|
|
||||||
|
init(temporaryURL: URL) {
|
||||||
|
self.temporaryURL = temporaryURL
|
||||||
|
}
|
||||||
|
|
||||||
|
func download(url: URL, in view: UIView, completionHandler: @escaping (URL?, Error?) -> Void) -> Bool {
|
||||||
|
guard hud == nil else {
|
||||||
|
log.info("Download in progress, skipping")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
log.info("Downloading from: \(url)")
|
||||||
|
let session = URLSession(configuration: .default, delegate: self, delegateQueue: .main)
|
||||||
|
let request = URLRequest(url: url, cachePolicy: .reloadIgnoringCacheData, timeoutInterval: AppConstants.Web.timeout)
|
||||||
|
let task = session.downloadTask(with: request)
|
||||||
|
|
||||||
|
hud = MBProgressHUD.showAdded(to: view, animated: true)
|
||||||
|
hud?.mode = .annularDeterminate
|
||||||
|
hud?.progressObject = task.progress
|
||||||
|
|
||||||
|
self.completionHandler = completionHandler
|
||||||
|
task.resume()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Downloader: URLSessionDelegate, URLSessionDownloadDelegate {
|
||||||
|
func urlSession(_ session: URLSession, task: URLSessionTask, didCompleteWithError error: Error?) {
|
||||||
|
if let error = error {
|
||||||
|
log.error("Download failed: \(error)")
|
||||||
|
hud?.hide(animated: true)
|
||||||
|
hud = nil
|
||||||
|
completionHandler?(nil, error)
|
||||||
|
completionHandler = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
completionHandler = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func urlSession(_ session: URLSession, downloadTask: URLSessionDownloadTask, didFinishDownloadingTo location: URL) {
|
||||||
|
log.info("Download complete!")
|
||||||
|
if let url = downloadTask.originalRequest?.url {
|
||||||
|
log.info("\tFrom: \(url)")
|
||||||
|
}
|
||||||
|
log.debug("\tTo: \(location)")
|
||||||
|
|
||||||
|
let fm = FileManager.default
|
||||||
|
do {
|
||||||
|
try? fm.removeItem(at: temporaryURL)
|
||||||
|
try fm.copyItem(at: location, to: temporaryURL)
|
||||||
|
} catch let e {
|
||||||
|
log.error("Failed to copy downloaded file: \(e)")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
hud?.hide(animated: true)
|
||||||
|
hud = nil
|
||||||
|
completionHandler?(temporaryURL, nil)
|
||||||
|
completionHandler = nil
|
||||||
|
}
|
||||||
|
}
|
@ -40,12 +40,13 @@ import MBProgressHUD
|
|||||||
class HUD {
|
class HUD {
|
||||||
private let backend: MBProgressHUD
|
private let backend: MBProgressHUD
|
||||||
|
|
||||||
init() {
|
init(label: String? = nil) {
|
||||||
guard let window = UIApplication.shared.windows.first else {
|
guard let window = UIApplication.shared.windows.first else {
|
||||||
fatalError("Could not locate front window?")
|
fatalError("Could not locate front window?")
|
||||||
}
|
}
|
||||||
|
|
||||||
backend = MBProgressHUD.showAdded(to: window, animated: true)
|
backend = MBProgressHUD.showAdded(to: window, animated: true)
|
||||||
|
backend.label.text = label
|
||||||
backend.backgroundView.backgroundColor = UIColor(white: 0.0, alpha: 0.6)
|
backend.backgroundView.backgroundColor = UIColor(white: 0.0, alpha: 0.6)
|
||||||
backend.mode = .indeterminate
|
backend.mode = .indeterminate
|
||||||
backend.removeFromSuperViewOnHide = true
|
backend.removeFromSuperViewOnHide = true
|
||||||
|
@ -26,6 +26,7 @@
|
|||||||
import UIKit
|
import UIKit
|
||||||
import NetworkExtension
|
import NetworkExtension
|
||||||
import CoreTelephony
|
import CoreTelephony
|
||||||
|
import MBProgressHUD
|
||||||
import TunnelKit
|
import TunnelKit
|
||||||
import Passepartout_Core
|
import Passepartout_Core
|
||||||
|
|
||||||
@ -247,7 +248,14 @@ class ServiceViewController: UIViewController, TableModelHost {
|
|||||||
}
|
}
|
||||||
vpn.reconnect { (error) in
|
vpn.reconnect { (error) in
|
||||||
guard error == nil else {
|
guard error == nil else {
|
||||||
cell.setOn(false, animated: true)
|
|
||||||
|
// XXX: delay to avoid weird toggle state
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(500)) {
|
||||||
|
cell.setOn(false, animated: true)
|
||||||
|
if error as? ApplicationError == .externalResources {
|
||||||
|
self.requireDownload()
|
||||||
|
}
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self.reloadModel()
|
self.reloadModel()
|
||||||
@ -439,6 +447,49 @@ class ServiceViewController: UIViewController, TableModelHost {
|
|||||||
IssueReporter.shared.present(in: self, withAttachments: attach)
|
IssueReporter.shared.present(in: self, withAttachments: attach)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func requireDownload() {
|
||||||
|
guard let providerProfile = profile as? ProviderConnectionProfile else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guard let downloadURL = AppConstants.URLs.externalResources[providerProfile.name] else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let alert = Macros.alert(
|
||||||
|
L10n.Service.Alerts.Download.title,
|
||||||
|
L10n.Service.Alerts.Download.message(providerProfile.name.rawValue)
|
||||||
|
)
|
||||||
|
alert.addCancelAction(L10n.Global.cancel)
|
||||||
|
alert.addDefaultAction(L10n.Global.ok) {
|
||||||
|
self.confirmDownload(URL(string: downloadURL)!)
|
||||||
|
}
|
||||||
|
present(alert, animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func confirmDownload(_ url: URL) {
|
||||||
|
_ = Downloader.shared.download(url: url, in: view) { (url, error) in
|
||||||
|
self.handleDownloadedProviderResources(url: url, error: error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handleDownloadedProviderResources(url: URL?, error: Error?) {
|
||||||
|
guard let url = url else {
|
||||||
|
let alert = Macros.alert(
|
||||||
|
L10n.Service.Alerts.Download.title,
|
||||||
|
L10n.Service.Alerts.Download.failed(error?.localizedDescription ?? "")
|
||||||
|
)
|
||||||
|
alert.addCancelAction(L10n.Global.ok)
|
||||||
|
present(alert, animated: true, completion: nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let hud = HUD(label: L10n.Service.Alerts.Download.Hud.extracting)
|
||||||
|
hud.show()
|
||||||
|
uncheckedProviderProfile.name.importExternalResources(from: url) {
|
||||||
|
hud.hide()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: Notifications
|
// MARK: Notifications
|
||||||
|
|
||||||
@objc private func vpnDidUpdate() {
|
@objc private func vpnDidUpdate() {
|
||||||
|
@ -106,6 +106,7 @@
|
|||||||
0EDE8DC420C86910004C739C /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDE8DC320C86910004C739C /* PacketTunnelProvider.swift */; };
|
0EDE8DC420C86910004C739C /* PacketTunnelProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EDE8DC320C86910004C739C /* PacketTunnelProvider.swift */; };
|
||||||
0EDE8DC820C86910004C739C /* Passepartout-Tunnel.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 0EDE8DBF20C86910004C739C /* Passepartout-Tunnel.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
0EDE8DC820C86910004C739C /* Passepartout-Tunnel.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 0EDE8DBF20C86910004C739C /* Passepartout-Tunnel.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||||
0EE3BBB2215ED3A900F30952 /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EE3BBB1215ED3A900F30952 /* AboutViewController.swift */; };
|
0EE3BBB2215ED3A900F30952 /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EE3BBB1215ED3A900F30952 /* AboutViewController.swift */; };
|
||||||
|
0EEB53B2225D525B00746300 /* Downloader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EEB53B1225D525B00746300 /* Downloader.swift */; };
|
||||||
0EF56BBB2185AC8500B0C8AB /* SwiftGen+Segues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EF56BBA2185AC8500B0C8AB /* SwiftGen+Segues.swift */; };
|
0EF56BBB2185AC8500B0C8AB /* SwiftGen+Segues.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EF56BBA2185AC8500B0C8AB /* SwiftGen+Segues.swift */; };
|
||||||
0EF5CF252141CE58004FF1BD /* HUD.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EF5CF242141CE58004FF1BD /* HUD.swift */; };
|
0EF5CF252141CE58004FF1BD /* HUD.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0EF5CF242141CE58004FF1BD /* HUD.swift */; };
|
||||||
0EF5CF292141F31F004FF1BD /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E4FD7ED20D539A0002221FF /* Utils.swift */; };
|
0EF5CF292141F31F004FF1BD /* Utils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0E4FD7ED20D539A0002221FF /* Utils.swift */; };
|
||||||
@ -276,6 +277,7 @@
|
|||||||
0EDE8DE620C93945004C739C /* Credentials.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Credentials.swift; sourceTree = "<group>"; };
|
0EDE8DE620C93945004C739C /* Credentials.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Credentials.swift; sourceTree = "<group>"; };
|
||||||
0EDE8DED20C93E4C004C739C /* GroupConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupConstants.swift; sourceTree = "<group>"; };
|
0EDE8DED20C93E4C004C739C /* GroupConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupConstants.swift; sourceTree = "<group>"; };
|
||||||
0EE3BBB1215ED3A900F30952 /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = "<group>"; };
|
0EE3BBB1215ED3A900F30952 /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = "<group>"; };
|
||||||
|
0EEB53B1225D525B00746300 /* Downloader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Downloader.swift; sourceTree = "<group>"; };
|
||||||
0EF56BBA2185AC8500B0C8AB /* SwiftGen+Segues.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SwiftGen+Segues.swift"; sourceTree = "<group>"; };
|
0EF56BBA2185AC8500B0C8AB /* SwiftGen+Segues.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "SwiftGen+Segues.swift"; sourceTree = "<group>"; };
|
||||||
0EF5CF242141CE58004FF1BD /* HUD.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HUD.swift; sourceTree = "<group>"; };
|
0EF5CF242141CE58004FF1BD /* HUD.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HUD.swift; sourceTree = "<group>"; };
|
||||||
0EFBFAC021AC464800887A8C /* CreditsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreditsViewController.swift; sourceTree = "<group>"; };
|
0EFBFAC021AC464800887A8C /* CreditsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreditsViewController.swift; sourceTree = "<group>"; };
|
||||||
@ -547,6 +549,7 @@
|
|||||||
0EDE8DEC20C93E3B004C739C /* Global */ = {
|
0EDE8DEC20C93E3B004C739C /* Global */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
0EEB53B1225D525B00746300 /* Downloader.swift */,
|
||||||
0EF5CF242141CE58004FF1BD /* HUD.swift */,
|
0EF5CF242141CE58004FF1BD /* HUD.swift */,
|
||||||
0E24273F225951B00064A1A3 /* InApp.swift */,
|
0E24273F225951B00064A1A3 /* InApp.swift */,
|
||||||
0EFD943D215BE10800529B64 /* IssueReporter.swift */,
|
0EFD943D215BE10800529B64 /* IssueReporter.swift */,
|
||||||
@ -1065,6 +1068,7 @@
|
|||||||
0ECC60DE2256B68A0020BEAC /* SwiftGen+Assets.swift in Sources */,
|
0ECC60DE2256B68A0020BEAC /* SwiftGen+Assets.swift in Sources */,
|
||||||
0E242742225956AC0064A1A3 /* DonationViewController.swift in Sources */,
|
0E242742225956AC0064A1A3 /* DonationViewController.swift in Sources */,
|
||||||
0ED38AEC2141260D0004D387 /* ConfigurationModificationDelegate.swift in Sources */,
|
0ED38AEC2141260D0004D387 /* ConfigurationModificationDelegate.swift in Sources */,
|
||||||
|
0EEB53B2225D525B00746300 /* Downloader.swift in Sources */,
|
||||||
0ECEE45020E1182E00A6BB43 /* Theme+Cells.swift in Sources */,
|
0ECEE45020E1182E00A6BB43 /* Theme+Cells.swift in Sources */,
|
||||||
0E242740225951B00064A1A3 /* InApp.swift in Sources */,
|
0E242740225951B00064A1A3 /* InApp.swift in Sources */,
|
||||||
0E1066C920E0F84A004F98B7 /* Cells.swift in Sources */,
|
0E1066C920E0F84A004F98B7 /* Cells.swift in Sources */,
|
||||||
|
@ -132,6 +132,10 @@
|
|||||||
"service.alerts.data_count.messages.not_available" = "Information not available, are you connected?";
|
"service.alerts.data_count.messages.not_available" = "Information not available, are you connected?";
|
||||||
"service.alerts.masks_private_data.messages.must_reconnect" = "In order to safely reset the current debug log and apply the new masking preference, you must reconnect to the VPN now.";
|
"service.alerts.masks_private_data.messages.must_reconnect" = "In order to safely reset the current debug log and apply the new masking preference, you must reconnect to the VPN now.";
|
||||||
"service.alerts.buttons.reconnect" = "Reconnect";
|
"service.alerts.buttons.reconnect" = "Reconnect";
|
||||||
|
"service.alerts.download.title" = "Download required";
|
||||||
|
"service.alerts.download.message" = "%@ requires the download of additional configuration files.\n\nConfirm to start the download.";
|
||||||
|
"service.alerts.download.failed" = "Failed to download configuration files. %@";
|
||||||
|
"service.alerts.download.hud.extracting" = "Extracting files, please be patient...";
|
||||||
|
|
||||||
"account.sections.credentials.header" = "Credentials";
|
"account.sections.credentials.header" = "Credentials";
|
||||||
"account.sections.guidance.footer.infrastructure.mullvad" = "Use your %@ website account number and password \"m\".";
|
"account.sections.guidance.footer.infrastructure.mullvad" = "Use your %@ website account number and password \"m\".";
|
||||||
|
@ -202,6 +202,8 @@ public class AppConstants {
|
|||||||
.tunnelBear: "https://click.tunnelbear.com/aff_c?offer_id=2&aff_id=7464",
|
.tunnelBear: "https://click.tunnelbear.com/aff_c?offer_id=2&aff_id=7464",
|
||||||
.windscribe: "https://secure.link/kCsD0prd"
|
.windscribe: "https://secure.link/kCsD0prd"
|
||||||
]
|
]
|
||||||
|
|
||||||
|
public static let externalResources: [Infrastructure.Name: String] = [:]
|
||||||
}
|
}
|
||||||
|
|
||||||
public class Repos {
|
public class Repos {
|
||||||
|
@ -33,4 +33,6 @@ public enum ApplicationError: String, Error {
|
|||||||
case migration
|
case migration
|
||||||
|
|
||||||
case inactiveProfile
|
case inactiveProfile
|
||||||
|
|
||||||
|
case externalResources
|
||||||
}
|
}
|
||||||
|
@ -73,6 +73,8 @@ public class GroupConstants {
|
|||||||
try? FileManager.default.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil)
|
try? FileManager.default.createDirectory(at: url, withIntermediateDirectories: true, attributes: nil)
|
||||||
return url
|
return url
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
public static let externalURL = cachesURL.appendingPathComponent("External")
|
||||||
}
|
}
|
||||||
|
|
||||||
public class VPN {
|
public class VPN {
|
||||||
|
@ -121,6 +121,12 @@ public class ProviderConnectionProfile: ConnectionProfile, Codable, Equatable {
|
|||||||
builder.debugLogFormat = configuration.debugLogFormat
|
builder.debugLogFormat = configuration.debugLogFormat
|
||||||
builder.masksPrivateData = configuration.masksPrivateData
|
builder.masksPrivateData = configuration.masksPrivateData
|
||||||
|
|
||||||
|
do {
|
||||||
|
try preset.injectExternalConfiguration(&builder, with: name, pool: pool)
|
||||||
|
} catch {
|
||||||
|
throw ApplicationError.externalResources
|
||||||
|
}
|
||||||
|
|
||||||
if let address = manualAddress {
|
if let address = manualAddress {
|
||||||
builder.prefersResolvedAddresses = true
|
builder.prefersResolvedAddresses = true
|
||||||
builder.resolvedAddresses = [address]
|
builder.resolvedAddresses = [address]
|
||||||
@ -137,7 +143,7 @@ public class ProviderConnectionProfile: ConnectionProfile, Codable, Equatable {
|
|||||||
} else {
|
} else {
|
||||||
|
|
||||||
// restrict "Any" protocol to UDP, unless there are no UDP endpoints
|
// restrict "Any" protocol to UDP, unless there are no UDP endpoints
|
||||||
let allEndpoints = preset.configuration.sessionConfiguration.endpointProtocols
|
let allEndpoints = builder.sessionConfiguration.endpointProtocols
|
||||||
var endpoints = allEndpoints?.filter { $0.socketType == .udp }
|
var endpoints = allEndpoints?.filter { $0.socketType == .udp }
|
||||||
if endpoints?.isEmpty ?? true {
|
if endpoints?.isEmpty ?? true {
|
||||||
endpoints = allEndpoints
|
endpoints = allEndpoints
|
||||||
|
@ -37,14 +37,6 @@ public struct Infrastructure: Codable {
|
|||||||
case tunnelBear = "TunnelBear"
|
case tunnelBear = "TunnelBear"
|
||||||
|
|
||||||
case windscribe = "Windscribe"
|
case windscribe = "Windscribe"
|
||||||
|
|
||||||
public var webName: String {
|
|
||||||
return rawValue.lowercased()
|
|
||||||
}
|
|
||||||
|
|
||||||
public static func <(lhs: Name, rhs: Name) -> Bool {
|
|
||||||
return lhs.webName < rhs.webName
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct Defaults: Codable {
|
public struct Defaults: Codable {
|
||||||
@ -78,3 +70,36 @@ public struct Infrastructure: Codable {
|
|||||||
return presets.first { $0.id == identifier }
|
return presets.first { $0.id == identifier }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension Infrastructure.Name {
|
||||||
|
public var webName: String {
|
||||||
|
return rawValue.lowercased()
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func <(lhs: Infrastructure.Name, rhs: Infrastructure.Name) -> Bool {
|
||||||
|
return lhs.webName < rhs.webName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Infrastructure.Name {
|
||||||
|
public var externalURL: URL {
|
||||||
|
return GroupConstants.App.externalURL.appendingPathComponent(webName)
|
||||||
|
}
|
||||||
|
|
||||||
|
public func importExternalResources(from url: URL, completionHandler: @escaping () -> Void) {
|
||||||
|
switch self {
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func execute(task: @escaping () -> Void, completionHandler: @escaping () -> Void) {
|
||||||
|
let queue: DispatchQueue = .global(qos: .background)
|
||||||
|
queue.async {
|
||||||
|
task()
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
completionHandler()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -30,6 +30,16 @@ import TunnelKit
|
|||||||
// ignores new JSON keys
|
// ignores new JSON keys
|
||||||
|
|
||||||
public struct InfrastructurePreset: Codable {
|
public struct InfrastructurePreset: Codable {
|
||||||
|
public enum ExternalKey: String, Codable {
|
||||||
|
case ca
|
||||||
|
|
||||||
|
case client
|
||||||
|
|
||||||
|
case key
|
||||||
|
|
||||||
|
case wrapKeyData = "wrap.key.data"
|
||||||
|
}
|
||||||
|
|
||||||
public enum PresetKeys: String, CodingKey {
|
public enum PresetKeys: String, CodingKey {
|
||||||
case id
|
case id
|
||||||
|
|
||||||
@ -38,6 +48,8 @@ public struct InfrastructurePreset: Codable {
|
|||||||
case comment
|
case comment
|
||||||
|
|
||||||
case configuration = "cfg"
|
case configuration = "cfg"
|
||||||
|
|
||||||
|
case external
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ConfigurationKeys: String, CodingKey {
|
public enum ConfigurationKeys: String, CodingKey {
|
||||||
@ -78,10 +90,38 @@ public struct InfrastructurePreset: Codable {
|
|||||||
|
|
||||||
public let configuration: TunnelKitProvider.Configuration
|
public let configuration: TunnelKitProvider.Configuration
|
||||||
|
|
||||||
|
public let external: [ExternalKey: String]?
|
||||||
|
|
||||||
public func hasProtocol(_ proto: EndpointProtocol) -> Bool {
|
public func hasProtocol(_ proto: EndpointProtocol) -> Bool {
|
||||||
return configuration.sessionConfiguration.endpointProtocols?.firstIndex(of: proto) != nil
|
return configuration.sessionConfiguration.endpointProtocols?.firstIndex(of: proto) != nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func injectExternalConfiguration(_ configuration: inout TunnelKitProvider.ConfigurationBuilder, with name: Infrastructure.Name, pool: Pool) throws {
|
||||||
|
guard let external = external, !external.isEmpty else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let baseURL = name.externalURL
|
||||||
|
|
||||||
|
var sessionBuilder = configuration.sessionConfiguration.builder()
|
||||||
|
if let pattern = external[.ca] {
|
||||||
|
let filename = pattern.replacingOccurrences(of: "${id}", with: pool.id)
|
||||||
|
let caURL = baseURL.appendingPathComponent(filename)
|
||||||
|
sessionBuilder.ca = CryptoContainer(pem: try String(contentsOf: caURL))
|
||||||
|
}
|
||||||
|
if let pattern = external[.wrapKeyData] {
|
||||||
|
let filename = pattern.replacingOccurrences(of: "${id}", with: pool.id)
|
||||||
|
let tlsKeyURL = baseURL.appendingPathComponent(filename)
|
||||||
|
if let dummyWrap = sessionBuilder.tlsWrap {
|
||||||
|
let file = try String(contentsOf: tlsKeyURL)
|
||||||
|
if let staticKey = StaticKey(file: file, direction: .client) {
|
||||||
|
sessionBuilder.tlsWrap = SessionProxy.TLSWrap(strategy: dummyWrap.strategy, key: staticKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
configuration.sessionConfiguration = sessionBuilder.build()
|
||||||
|
}
|
||||||
|
|
||||||
// MARK: Codable
|
// MARK: Codable
|
||||||
|
|
||||||
public init(from decoder: Decoder) throws {
|
public init(from decoder: Decoder) throws {
|
||||||
@ -89,6 +129,18 @@ public struct InfrastructurePreset: Codable {
|
|||||||
id = try container.decode(String.self, forKey: .id)
|
id = try container.decode(String.self, forKey: .id)
|
||||||
name = try container.decode(String.self, forKey: .name)
|
name = try container.decode(String.self, forKey: .name)
|
||||||
comment = try container.decode(String.self, forKey: .comment)
|
comment = try container.decode(String.self, forKey: .comment)
|
||||||
|
if let rawExternal = try container.decodeIfPresent([String: String].self, forKey: .external) {
|
||||||
|
var remapped: [ExternalKey: String] = [:]
|
||||||
|
for entry in rawExternal {
|
||||||
|
guard let key = ExternalKey(rawValue: entry.key) else {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
remapped[key] = entry.value
|
||||||
|
}
|
||||||
|
external = remapped
|
||||||
|
} else {
|
||||||
|
external = nil
|
||||||
|
}
|
||||||
|
|
||||||
let cfgContainer = try container.nestedContainer(keyedBy: ConfigurationKeys.self, forKey: .configuration)
|
let cfgContainer = try container.nestedContainer(keyedBy: ConfigurationKeys.self, forKey: .configuration)
|
||||||
|
|
||||||
@ -99,7 +151,7 @@ public struct InfrastructurePreset: Codable {
|
|||||||
}
|
}
|
||||||
sessionBuilder.compressionFraming = try cfgContainer.decode(SessionProxy.CompressionFraming.self, forKey: .compressionFraming)
|
sessionBuilder.compressionFraming = try cfgContainer.decode(SessionProxy.CompressionFraming.self, forKey: .compressionFraming)
|
||||||
sessionBuilder.compressionAlgorithm = try cfgContainer.decodeIfPresent(SessionProxy.CompressionAlgorithm.self, forKey: .compressionAlgorithm) ?? .disabled
|
sessionBuilder.compressionAlgorithm = try cfgContainer.decodeIfPresent(SessionProxy.CompressionAlgorithm.self, forKey: .compressionAlgorithm) ?? .disabled
|
||||||
sessionBuilder.ca = try cfgContainer.decode(CryptoContainer.self, forKey: .ca)
|
sessionBuilder.ca = try cfgContainer.decodeIfPresent(CryptoContainer.self, forKey: .ca)
|
||||||
sessionBuilder.clientCertificate = try cfgContainer.decodeIfPresent(CryptoContainer.self, forKey: .clientCertificate)
|
sessionBuilder.clientCertificate = try cfgContainer.decodeIfPresent(CryptoContainer.self, forKey: .clientCertificate)
|
||||||
sessionBuilder.clientKey = try cfgContainer.decodeIfPresent(CryptoContainer.self, forKey: .clientKey)
|
sessionBuilder.clientKey = try cfgContainer.decodeIfPresent(CryptoContainer.self, forKey: .clientKey)
|
||||||
sessionBuilder.tlsWrap = try cfgContainer.decodeIfPresent(SessionProxy.TLSWrap.self, forKey: .tlsWrap)
|
sessionBuilder.tlsWrap = try cfgContainer.decodeIfPresent(SessionProxy.TLSWrap.self, forKey: .tlsWrap)
|
||||||
@ -126,13 +178,14 @@ public struct InfrastructurePreset: Codable {
|
|||||||
try container.encode(id, forKey: .id)
|
try container.encode(id, forKey: .id)
|
||||||
try container.encode(name, forKey: .name)
|
try container.encode(name, forKey: .name)
|
||||||
try container.encode(comment, forKey: .comment)
|
try container.encode(comment, forKey: .comment)
|
||||||
|
try container.encodeIfPresent(external, forKey: .external)
|
||||||
|
|
||||||
var cfgContainer = container.nestedContainer(keyedBy: ConfigurationKeys.self, forKey: .configuration)
|
var cfgContainer = container.nestedContainer(keyedBy: ConfigurationKeys.self, forKey: .configuration)
|
||||||
try cfgContainer.encode(configuration.sessionConfiguration.cipher, forKey: .cipher)
|
try cfgContainer.encode(configuration.sessionConfiguration.cipher, forKey: .cipher)
|
||||||
try cfgContainer.encode(configuration.sessionConfiguration.digest, forKey: .digest)
|
try cfgContainer.encode(configuration.sessionConfiguration.digest, forKey: .digest)
|
||||||
try cfgContainer.encode(configuration.sessionConfiguration.compressionFraming, forKey: .compressionFraming)
|
try cfgContainer.encode(configuration.sessionConfiguration.compressionFraming, forKey: .compressionFraming)
|
||||||
try cfgContainer.encodeIfPresent(configuration.sessionConfiguration.compressionAlgorithm, forKey: .compressionAlgorithm)
|
try cfgContainer.encodeIfPresent(configuration.sessionConfiguration.compressionAlgorithm, forKey: .compressionAlgorithm)
|
||||||
try cfgContainer.encode(ca, forKey: .ca)
|
try cfgContainer.encodeIfPresent(ca, forKey: .ca)
|
||||||
try cfgContainer.encodeIfPresent(configuration.sessionConfiguration.clientCertificate, forKey: .clientCertificate)
|
try cfgContainer.encodeIfPresent(configuration.sessionConfiguration.clientCertificate, forKey: .clientCertificate)
|
||||||
try cfgContainer.encodeIfPresent(configuration.sessionConfiguration.clientKey, forKey: .clientKey)
|
try cfgContainer.encodeIfPresent(configuration.sessionConfiguration.clientKey, forKey: .clientKey)
|
||||||
try cfgContainer.encodeIfPresent(configuration.sessionConfiguration.tlsWrap, forKey: .tlsWrap)
|
try cfgContainer.encodeIfPresent(configuration.sessionConfiguration.tlsWrap, forKey: .tlsWrap)
|
||||||
|
@ -593,6 +593,22 @@ public enum L10n {
|
|||||||
public static let notAvailable = L10n.tr("Localizable", "service.alerts.data_count.messages.not_available")
|
public static let notAvailable = L10n.tr("Localizable", "service.alerts.data_count.messages.not_available")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public enum Download {
|
||||||
|
/// Failed to download configuration files. %@
|
||||||
|
public static func failed(_ p1: String) -> String {
|
||||||
|
return L10n.tr("Localizable", "service.alerts.download.failed", p1)
|
||||||
|
}
|
||||||
|
/// %@ requires the download of additional configuration files.\n\nConfirm to start the download.
|
||||||
|
public static func message(_ p1: String) -> String {
|
||||||
|
return L10n.tr("Localizable", "service.alerts.download.message", p1)
|
||||||
|
}
|
||||||
|
/// Download required
|
||||||
|
public static let title = L10n.tr("Localizable", "service.alerts.download.title")
|
||||||
|
public enum Hud {
|
||||||
|
/// Extracting files, please be patient...
|
||||||
|
public static let extracting = L10n.tr("Localizable", "service.alerts.download.hud.extracting")
|
||||||
|
}
|
||||||
|
}
|
||||||
public enum MasksPrivateData {
|
public enum MasksPrivateData {
|
||||||
public enum Messages {
|
public enum Messages {
|
||||||
/// In order to safely reset the current debug log and apply the new masking preference, you must reconnect to the VPN now.
|
/// In order to safely reset the current debug log and apply the new masking preference, you must reconnect to the VPN now.
|
||||||
|
@ -75,6 +75,10 @@ public class GracefulVPN {
|
|||||||
log.info("Reconnecting...")
|
log.info("Reconnecting...")
|
||||||
try vpn.reconnect(configuration: service.vpnConfiguration(), completionHandler: completionHandler)
|
try vpn.reconnect(configuration: service.vpnConfiguration(), completionHandler: completionHandler)
|
||||||
} catch let e {
|
} catch let e {
|
||||||
|
guard e as? ApplicationError != .externalResources else {
|
||||||
|
completionHandler?(e)
|
||||||
|
return
|
||||||
|
}
|
||||||
log.error("Could not reconnect: \(e)")
|
log.error("Could not reconnect: \(e)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -89,6 +93,10 @@ public class GracefulVPN {
|
|||||||
log.info("Reinstalling...")
|
log.info("Reinstalling...")
|
||||||
try vpn.install(configuration: service.vpnConfiguration(), completionHandler: completionHandler)
|
try vpn.install(configuration: service.vpnConfiguration(), completionHandler: completionHandler)
|
||||||
} catch let e {
|
} catch let e {
|
||||||
|
guard e as? ApplicationError != .externalResources else {
|
||||||
|
completionHandler?(e)
|
||||||
|
return
|
||||||
|
}
|
||||||
log.error("Could not reinstall: \(e)")
|
log.error("Could not reinstall: \(e)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user