Retry import after passphrase input

Isolate .ovpn parsing routines.

- AppDelegate (external URL)
- ImportedHosts

Only delete URL if error is not encryption passphrase.
This commit is contained in:
Davide De Rosa 2019-03-25 20:33:07 +01:00
parent 50aebe2310
commit dc81356b31
5 changed files with 80 additions and 24 deletions

View File

@ -94,23 +94,27 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDele
}
let topmost = root.presentedViewController ?? root
let fm = FileManager.default
guard let parsingResult = ConfigurationParser.ParsingResult.from(url, withErrorAlertIn: topmost) else {
try? fm.removeItem(at: url)
return tryParseURL(url, passphrase: nil, target: topmost)
}
private func tryParseURL(_ url: URL, passphrase: String?, target: UIViewController) -> Bool {
let passphraseBlock = { (passphrase) in
_ = self.tryParseURL(url, passphrase: passphrase, target: target)
}
guard let parsingResult = ConfigurationParser.ParsingResult.from(url, withErrorAlertIn: target, passphraseBlock: passphraseBlock) else {
return true
}
if let warning = parsingResult.warning {
ConfigurationParser.ParsingResult.alertImportWarning(url: url, in: topmost, withWarning: warning) {
ConfigurationParser.ParsingResult.alertImportWarning(url: url, in: target, withWarning: warning) {
if $0 {
self.handleParsingResult(parsingResult, in: topmost)
self.handleParsingResult(parsingResult, in: target)
} else {
try? fm.removeItem(at: url)
try? FileManager.default.removeItem(at: url)
}
}
return true
}
handleParsingResult(parsingResult, in: topmost)
handleParsingResult(parsingResult, in: target)
return true
}

View File

@ -32,14 +32,39 @@ import Passepartout_Core
private let log = SwiftyBeaver.self
extension ConfigurationParser.ParsingResult {
static func from(_ url: URL, withErrorAlertIn viewController: UIViewController) -> ConfigurationParser.ParsingResult? {
static func from(_ url: URL, withErrorAlertIn viewController: UIViewController, passphraseBlock: @escaping (String) -> Void) -> ConfigurationParser.ParsingResult? {
let result: ConfigurationParser.ParsingResult
let fm = FileManager.default
log.debug("Parsing configuration URL: \(url)")
do {
result = try ConfigurationParser.parsed(fromURL: url)
} catch let e as ConfigurationParser.ParsingError {
switch e {
case .encryptionPassphrase, .unableToDecrypt(_):
let alert = Macros.alert(url.normalizedFilename, L10n.ParsedFile.Alerts.EncryptionPassphrase.message)
alert.addTextField(configurationHandler: nil)
alert.addDefaultAction(L10n.Global.ok) {
guard let passphrase = alert.textFields?.first?.text else {
return
}
passphraseBlock(passphrase)
}
alert.addCancelAction(L10n.Global.cancel) {
try? fm.removeItem(at: url)
}
viewController.present(alert, animated: true, completion: nil)
default:
let message = localizedMessage(forError: e)
alertImportError(url: url, in: viewController, withMessage: message)
try? fm.removeItem(at: url)
}
return nil
} catch let e {
let message = localizedMessage(forError: e)
alertImportError(url: url, in: viewController, withMessage: message)
try? fm.removeItem(at: url)
return nil
}
return result
@ -78,6 +103,9 @@ extension ConfigurationParser.ParsingResult {
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)")
@ -91,6 +119,9 @@ extension ConfigurationParser.ParsingResult {
case .unsupportedConfiguration(let option):
return option
default:
return "" // XXX: should never get here
}
}
}

View File

@ -76,24 +76,35 @@ class ImportedHostsViewController: UITableViewController {
return false
}
let url = pendingConfigurationURLs[indexPath.row]
guard let parsingResult = ConfigurationParser.ParsingResult.from(url, withErrorAlertIn: self) else {
deselectSelectedRow()
return false
return tryParseURL(url, passphrase: nil, cell: cell)
}
return true
}
private func tryParseURL(_ url: URL, passphrase: String?, cell: UITableViewCell) -> Bool {
let passphraseBlock: (String) -> Void = { (passphrase) in
guard self.tryParseURL(url, passphrase: passphrase, cell: cell) else {
return
}
self.parsingResult = parsingResult
// postpone segue until alert dismissal
if let warning = parsingResult.warning {
ConfigurationParser.ParsingResult.alertImportWarning(url: url, in: self, withWarning: warning) {
self.deselectSelectedRow()
if $0 {
self.perform(segue: StoryboardSegue.Organizer.importHostSegueIdentifier)
} else {
self.parsingResult = nil
}
self.perform(segue: StoryboardSegue.Organizer.importHostSegueIdentifier, sender: cell)
}
guard let parsingResult = ConfigurationParser.ParsingResult.from(url, withErrorAlertIn: self, passphraseBlock: passphraseBlock) else {
deselectSelectedRow()
return false
}
self.parsingResult = parsingResult
// postpone segue until alert dismissal
if let warning = parsingResult.warning {
ConfigurationParser.ParsingResult.alertImportWarning(url: url, in: self, withWarning: warning) {
self.deselectSelectedRow()
if $0 {
self.perform(segue: StoryboardSegue.Organizer.importHostSegueIdentifier)
} else {
self.parsingResult = nil
}
return false
}
return false
}
return true
}

View File

@ -59,6 +59,8 @@
"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.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.encryption_passphrase.message" = "Please enter the encryption passphrase.";
"parsed_file.alerts.decryption.message" = "The configuration contains an encrypted private key and it could not be decrypted. Double check your entered passphrase.";
"parsed_file.alerts.parsing.message" = "Unable to parse the provided configuration file (%@).";
"parsed_file.alerts.buttons.report" = "Report an issue";

View File

@ -403,6 +403,14 @@ public enum L10n {
/// Report an issue
public static let report = L10n.tr("Localizable", "parsed_file.alerts.buttons.report")
}
public enum Decryption {
/// The configuration contains an encrypted private key and it could not be decrypted. Double check your entered passphrase.
public static let message = L10n.tr("Localizable", "parsed_file.alerts.decryption.message")
}
public enum EncryptionPassphrase {
/// Please enter the encryption passphrase.
public static let message = L10n.tr("Localizable", "parsed_file.alerts.encryption_passphrase.message")
}
public enum Missing {
/// The configuration file lacks a required option (%@).
public static func message(_ p1: String) -> String {