Merge branch 'warn-about-compression-settings'
This commit is contained in:
commit
45634b58d1
|
@ -95,12 +95,27 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
|
||||||
try? fm.removeItem(at: url)
|
try? fm.removeItem(at: url)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
if let warning = parsedFile.warning {
|
||||||
|
ParsedFile.alertImportWarning(url: url, in: root, withWarning: warning) {
|
||||||
|
if $0 {
|
||||||
|
self.handleParsedFile(parsedFile, in: root)
|
||||||
|
} else {
|
||||||
|
try? fm.removeItem(at: url)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
handleParsedFile(parsedFile, in: root)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
private func handleParsedFile(_ parsedFile: ParsedFile, in root: UIViewController) {
|
||||||
|
|
||||||
// already presented: update parsed configuration
|
// already presented: update parsed configuration
|
||||||
if let nav = root.presentedViewController as? UINavigationController, let wizard = nav.topViewController as? WizardHostViewController {
|
if let nav = root.presentedViewController as? UINavigationController, let wizard = nav.topViewController as? WizardHostViewController {
|
||||||
wizard.parsedFile = parsedFile
|
wizard.parsedFile = parsedFile
|
||||||
wizard.removesConfigurationOnCancel = true
|
wizard.removesConfigurationOnCancel = true
|
||||||
return true
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// present now
|
// present now
|
||||||
|
@ -121,7 +136,6 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
|
||||||
}
|
}
|
||||||
nav.modalPresentationStyle = .formSheet
|
nav.modalPresentationStyle = .formSheet
|
||||||
root.present(nav, animated: true, completion: nil)
|
root.present(nav, animated: true, completion: nil)
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,26 +32,19 @@ private let log = SwiftyBeaver.self
|
||||||
|
|
||||||
extension ParsedFile {
|
extension ParsedFile {
|
||||||
static func from(_ url: URL, withErrorAlertIn viewController: UIViewController) -> ParsedFile? {
|
static func from(_ url: URL, withErrorAlertIn viewController: UIViewController) -> ParsedFile? {
|
||||||
|
let file: ParsedFile
|
||||||
log.debug("Parsing configuration URL: \(url)")
|
log.debug("Parsing configuration URL: \(url)")
|
||||||
do {
|
do {
|
||||||
return try TunnelKitProvider.Configuration.parsed(from: url)
|
file = try TunnelKitProvider.Configuration.parsed(from: url)
|
||||||
} catch ApplicationError.missingConfiguration(let option) {
|
|
||||||
log.error("Could not parse configuration URL: missing configuration, \(option)")
|
|
||||||
let message = L10n.ParsedFile.Alerts.Missing.message(option)
|
|
||||||
alertConfigurationImportError(url: url, in: viewController, withMessage: message)
|
|
||||||
} catch ApplicationError.unsupportedConfiguration(let option) {
|
|
||||||
log.error("Could not parse configuration URL: unsupported configuration, \(option)")
|
|
||||||
let message = L10n.ParsedFile.Alerts.Unsupported.message(option)
|
|
||||||
alertConfigurationImportError(url: url, in: viewController, withMessage: message)
|
|
||||||
} catch let e {
|
} catch let e {
|
||||||
log.error("Could not parse configuration URL: \(e)")
|
let message = localizedMessage(forError: e)
|
||||||
let message = L10n.ParsedFile.Alerts.Parsing.message(e.localizedDescription)
|
alertImportError(url: url, in: viewController, withMessage: message)
|
||||||
alertConfigurationImportError(url: url, in: viewController, withMessage: message)
|
return nil
|
||||||
}
|
}
|
||||||
return nil
|
return file
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func alertConfigurationImportError(url: URL, in vc: UIViewController, withMessage message: String) {
|
private static func alertImportError(url: URL, in vc: UIViewController, withMessage message: String) {
|
||||||
let alert = Macros.alert(url.normalizedFilename, message)
|
let alert = Macros.alert(url.normalizedFilename, message)
|
||||||
// alert.addDefaultAction(L10n.ParsedFile.Alerts.Buttons.report) {
|
// alert.addDefaultAction(L10n.ParsedFile.Alerts.Buttons.report) {
|
||||||
// var attach = IssueReporter.Attachments(debugLog: false, configurationURL: url)
|
// var attach = IssueReporter.Attachments(debugLog: false, configurationURL: url)
|
||||||
|
@ -61,4 +54,48 @@ extension ParsedFile {
|
||||||
alert.addCancelAction(L10n.Global.ok)
|
alert.addCancelAction(L10n.Global.ok)
|
||||||
vc.present(alert, animated: true, completion: nil)
|
vc.present(alert, animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func alertImportWarning(url: URL, in vc: UIViewController, withWarning warning: ApplicationError, completionHandler: @escaping (Bool) -> Void) {
|
||||||
|
let message = details(forWarning: warning)
|
||||||
|
let alert = Macros.alert(url.normalizedFilename, L10n.ParsedFile.Alerts.PotentiallyUnsupported.message(message))
|
||||||
|
alert.addDefaultAction(L10n.Global.ok) {
|
||||||
|
completionHandler(true)
|
||||||
|
}
|
||||||
|
alert.addCancelAction(L10n.Global.cancel) {
|
||||||
|
completionHandler(false)
|
||||||
|
}
|
||||||
|
vc.present(alert, animated: true, completion: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func localizedMessage(forError error: Error) -> String {
|
||||||
|
if let appError = error as? ApplicationError {
|
||||||
|
switch appError {
|
||||||
|
case .missingConfiguration(let option):
|
||||||
|
log.error("Could not parse configuration URL: missing configuration, \(option)")
|
||||||
|
return L10n.ParsedFile.Alerts.Missing.message(option)
|
||||||
|
|
||||||
|
case .unsupportedConfiguration(let option):
|
||||||
|
log.error("Could not parse configuration URL: unsupported configuration, \(option)")
|
||||||
|
return L10n.ParsedFile.Alerts.Unsupported.message(option)
|
||||||
|
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
log.error("Could not parse configuration URL: \(error)")
|
||||||
|
return L10n.ParsedFile.Alerts.Parsing.message(error.localizedDescription)
|
||||||
|
}
|
||||||
|
|
||||||
|
private static func details(forWarning warning: ApplicationError) -> String {
|
||||||
|
switch warning {
|
||||||
|
case .missingConfiguration(let option):
|
||||||
|
return option
|
||||||
|
|
||||||
|
case .unsupportedConfiguration(let option):
|
||||||
|
return option
|
||||||
|
|
||||||
|
default:
|
||||||
|
fatalError("Only use .missingConfiguration or .unsupportedConfiguration for warnings")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -92,6 +92,7 @@ internal enum StoryboardSegue {
|
||||||
case disclaimerSegueIdentifier = "DisclaimerSegueIdentifier"
|
case disclaimerSegueIdentifier = "DisclaimerSegueIdentifier"
|
||||||
case importHostSegueIdentifier = "ImportHostSegueIdentifier"
|
case importHostSegueIdentifier = "ImportHostSegueIdentifier"
|
||||||
case selectProfileSegueIdentifier = "SelectProfileSegueIdentifier"
|
case selectProfileSegueIdentifier = "SelectProfileSegueIdentifier"
|
||||||
|
case showImportedHostsSegueIdentifier = "ShowImportedHostsSegueIdentifier"
|
||||||
case versionSegueIdentifier = "VersionSegueIdentifier"
|
case versionSegueIdentifier = "VersionSegueIdentifier"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -41,6 +41,12 @@ class ImportedHostsViewController: UITableViewController {
|
||||||
|
|
||||||
title = L10n.ImportedHosts.title
|
title = L10n.ImportedHosts.title
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func viewWillAppear(_ animated: Bool) {
|
||||||
|
super.viewWillAppear(animated)
|
||||||
|
|
||||||
|
parsedFile = nil
|
||||||
|
}
|
||||||
|
|
||||||
override func viewDidAppear(_ animated: Bool) {
|
override func viewDidAppear(_ animated: Bool) {
|
||||||
super.viewDidAppear(animated)
|
super.viewDidAppear(animated)
|
||||||
|
@ -56,22 +62,40 @@ class ImportedHostsViewController: UITableViewController {
|
||||||
present(alert, animated: true, completion: nil)
|
present(alert, animated: true, completion: nil)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
if let selectedIP = tableView.indexPathForSelectedRow {
|
||||||
|
tableView.deselectRow(at: selectedIP, animated: true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Actions
|
// MARK: Actions
|
||||||
|
|
||||||
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
|
override func shouldPerformSegue(withIdentifier identifier: String, sender: Any?) -> Bool {
|
||||||
guard let cell = sender as? UITableViewCell, let indexPath = tableView.indexPath(for: cell) else {
|
|
||||||
return false
|
// segue parses configuration file if not yet
|
||||||
}
|
if parsedFile == nil {
|
||||||
let url = pendingConfigurationURLs[indexPath.row]
|
guard let cell = sender as? UITableViewCell, let indexPath = tableView.indexPath(for: cell) else {
|
||||||
guard let parsedFile = ParsedFile.from(url, withErrorAlertIn: self) else {
|
return false
|
||||||
if let selectedIP = tableView.indexPathForSelectedRow {
|
}
|
||||||
tableView.deselectRow(at: selectedIP, animated: true)
|
let url = pendingConfigurationURLs[indexPath.row]
|
||||||
|
guard let parsedFile = ParsedFile.from(url, withErrorAlertIn: self) else {
|
||||||
|
deselectSelectedRow()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
self.parsedFile = parsedFile
|
||||||
|
|
||||||
|
// postpone segue until alert dismissal
|
||||||
|
if let warning = parsedFile.warning {
|
||||||
|
ParsedFile.alertImportWarning(url: url, in: self, withWarning: warning) {
|
||||||
|
self.deselectSelectedRow()
|
||||||
|
if $0 {
|
||||||
|
self.perform(segue: StoryboardSegue.Organizer.importHostSegueIdentifier)
|
||||||
|
} else {
|
||||||
|
self.parsedFile = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
self.parsedFile = parsedFile
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -81,11 +105,20 @@ class ImportedHostsViewController: UITableViewController {
|
||||||
}
|
}
|
||||||
wizard.parsedFile = parsedFile
|
wizard.parsedFile = parsedFile
|
||||||
wizard.delegate = wizardDelegate
|
wizard.delegate = wizardDelegate
|
||||||
|
|
||||||
|
// retain back button
|
||||||
|
wizard.navigationItem.leftBarButtonItem = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@IBAction private func close() {
|
@IBAction private func close() {
|
||||||
dismiss(animated: true, completion: nil)
|
dismiss(animated: true, completion: nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func deselectSelectedRow() {
|
||||||
|
if let selectedIP = tableView.indexPathForSelectedRow {
|
||||||
|
tableView.deselectRow(at: selectedIP, animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension ImportedHostsViewController {
|
extension ImportedHostsViewController {
|
||||||
|
|
|
@ -176,7 +176,7 @@ class OrganizerViewController: UITableViewController, TableModelHost {
|
||||||
}
|
}
|
||||||
|
|
||||||
private func addNewHost() {
|
private func addNewHost() {
|
||||||
perform(segue: StoryboardSegue.Organizer.importHostSegueIdentifier)
|
perform(segue: StoryboardSegue.Organizer.showImportedHostsSegueIdentifier)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func removeProfile(at indexPath: IndexPath) {
|
private func removeProfile(at indexPath: IndexPath) {
|
||||||
|
|
|
@ -194,7 +194,7 @@
|
||||||
</subviews>
|
</subviews>
|
||||||
</tableViewCellContentView>
|
</tableViewCellContentView>
|
||||||
<connections>
|
<connections>
|
||||||
<segue destination="oga-go-FqD" kind="show" id="kwN-PZ-Zg5"/>
|
<segue destination="oga-go-FqD" kind="show" identifier="ImportHostSegueIdentifier" id="kwN-PZ-Zg5"/>
|
||||||
</connections>
|
</connections>
|
||||||
</tableViewCell>
|
</tableViewCell>
|
||||||
</prototypes>
|
</prototypes>
|
||||||
|
@ -293,7 +293,7 @@
|
||||||
<connections>
|
<connections>
|
||||||
<segue destination="NVA-bQ-iIE" kind="presentation" identifier="AddProviderSegueIdentifier" modalPresentationStyle="formSheet" id="Win-5U-mIc"/>
|
<segue destination="NVA-bQ-iIE" kind="presentation" identifier="AddProviderSegueIdentifier" modalPresentationStyle="formSheet" id="Win-5U-mIc"/>
|
||||||
<segue destination="a3d-vD-Pr7" kind="presentation" identifier="AboutSegueIdentifier" id="fd4-we-46n"/>
|
<segue destination="a3d-vD-Pr7" kind="presentation" identifier="AboutSegueIdentifier" id="fd4-we-46n"/>
|
||||||
<segue destination="z6E-m6-Op0" kind="presentation" identifier="ImportHostSegueIdentifier" modalPresentationStyle="formSheet" id="TZv-OK-8vU"/>
|
<segue destination="z6E-m6-Op0" kind="presentation" identifier="ShowImportedHostsSegueIdentifier" modalPresentationStyle="formSheet" id="TZv-OK-8vU"/>
|
||||||
</connections>
|
</connections>
|
||||||
</tableViewController>
|
</tableViewController>
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="bGp-H5-24W" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
<placeholder placeholderIdentifier="IBFirstResponder" id="bGp-H5-24W" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||||
|
@ -567,6 +567,6 @@
|
||||||
</resources>
|
</resources>
|
||||||
<inferredMetricsTieBreakers>
|
<inferredMetricsTieBreakers>
|
||||||
<segue reference="HW6-RJ-VFY"/>
|
<segue reference="HW6-RJ-VFY"/>
|
||||||
<segue reference="qQl-B5-moM"/>
|
<segue reference="kwN-PZ-Zg5"/>
|
||||||
</inferredMetricsTieBreakers>
|
</inferredMetricsTieBreakers>
|
||||||
</document>
|
</document>
|
||||||
|
|
|
@ -55,6 +55,7 @@
|
||||||
|
|
||||||
"parsed_file.alerts.missing.message" = "The configuration file lacks a required option (%@).";
|
"parsed_file.alerts.missing.message" = "The configuration file lacks a required option (%@).";
|
||||||
"parsed_file.alerts.unsupported.message" = "The configuration file contains an unsupported option (%@).";
|
"parsed_file.alerts.unsupported.message" = "The configuration file contains an unsupported option (%@).";
|
||||||
|
"parsed_file.alerts.potentially_unsupported.message" = "The configuration file is correct but contains a potentially unsupported option (%@).\n\nConnectivity may break depending on server settings.";
|
||||||
"parsed_file.alerts.parsing.message" = "Unable to parse the provided configuration file (%@).";
|
"parsed_file.alerts.parsing.message" = "Unable to parse the provided configuration file (%@).";
|
||||||
"parsed_file.alerts.buttons.report" = "Report an issue";
|
"parsed_file.alerts.buttons.report" = "Report an issue";
|
||||||
|
|
||||||
|
|
|
@ -432,6 +432,13 @@ internal enum L10n {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal enum PotentiallyUnsupported {
|
||||||
|
/// The configuration file is correct but contains a potentially unsupported option (%@).\n\nConnectivity may break depending on server settings.
|
||||||
|
internal static func message(_ p1: String) -> String {
|
||||||
|
return L10n.tr("Localizable", "parsed_file.alerts.potentially_unsupported.message", p1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
internal enum Unsupported {
|
internal enum Unsupported {
|
||||||
/// The configuration file contains an unsupported option (%@).
|
/// The configuration file contains an unsupported option (%@).
|
||||||
internal static func message(_ p1: String) -> String {
|
internal static func message(_ p1: String) -> String {
|
||||||
|
|
|
@ -37,6 +37,8 @@ struct ParsedFile {
|
||||||
let configuration: TunnelKitProvider.Configuration
|
let configuration: TunnelKitProvider.Configuration
|
||||||
|
|
||||||
let strippedLines: [String]?
|
let strippedLines: [String]?
|
||||||
|
|
||||||
|
let warning: ApplicationError?
|
||||||
}
|
}
|
||||||
|
|
||||||
extension TunnelKitProvider.Configuration {
|
extension TunnelKitProvider.Configuration {
|
||||||
|
@ -78,6 +80,7 @@ extension TunnelKitProvider.Configuration {
|
||||||
static func parsed(from url: URL, returnsStripped: Bool = false) throws -> ParsedFile {
|
static func parsed(from url: URL, returnsStripped: Bool = false) throws -> ParsedFile {
|
||||||
let lines = try String(contentsOf: url).trimmedLines()
|
let lines = try String(contentsOf: url).trimmedLines()
|
||||||
var strippedLines: [String]? = returnsStripped ? [] : nil
|
var strippedLines: [String]? = returnsStripped ? [] : nil
|
||||||
|
var warning: ApplicationError? = nil
|
||||||
|
|
||||||
var defaultProto: TunnelKitProvider.SocketType?
|
var defaultProto: TunnelKitProvider.SocketType?
|
||||||
var defaultPort: UInt16?
|
var defaultPort: UInt16?
|
||||||
|
@ -221,13 +224,23 @@ extension TunnelKitProvider.Configuration {
|
||||||
unsupportedError = ApplicationError.unsupportedConfiguration(option: "auth \(rawValue)")
|
unsupportedError = ApplicationError.unsupportedConfiguration(option: "auth \(rawValue)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Regex.compLZO.enumerateComponents(in: line) { _ in
|
Regex.compLZO.enumerateArguments(in: line) {
|
||||||
isHandled = true
|
isHandled = true
|
||||||
compressionFraming = .compLZO
|
compressionFraming = .compLZO
|
||||||
|
|
||||||
|
guard let arg = $0.first, arg == "no" else {
|
||||||
|
warning = .unsupportedConfiguration(option: "compression")
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Regex.compress.enumerateComponents(in: line) { _ in
|
Regex.compress.enumerateArguments(in: line) {
|
||||||
isHandled = true
|
isHandled = true
|
||||||
compressionFraming = .compress
|
compressionFraming = .compress
|
||||||
|
|
||||||
|
guard $0.isEmpty else {
|
||||||
|
warning = .unsupportedConfiguration(option: "compression")
|
||||||
|
return
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Regex.keyDirection.enumerateArguments(in: line) {
|
Regex.keyDirection.enumerateArguments(in: line) {
|
||||||
isHandled = true
|
isHandled = true
|
||||||
|
@ -325,7 +338,13 @@ extension TunnelKitProvider.Configuration {
|
||||||
var builder = TunnelKitProvider.ConfigurationBuilder(sessionConfiguration: sessionBuilder.build())
|
var builder = TunnelKitProvider.ConfigurationBuilder(sessionConfiguration: sessionBuilder.build())
|
||||||
builder.endpointProtocols = endpointProtocols
|
builder.endpointProtocols = endpointProtocols
|
||||||
|
|
||||||
return ParsedFile(url: url, hostname: hostname, configuration: builder.build(), strippedLines: strippedLines)
|
return ParsedFile(
|
||||||
|
url: url,
|
||||||
|
hostname: hostname,
|
||||||
|
configuration: builder.build(),
|
||||||
|
strippedLines: strippedLines,
|
||||||
|
warning: warning
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue