Allow any character in host profile name

Fixes #26
This commit is contained in:
Davide De Rosa 2020-02-28 18:32:04 +01:00
parent c51e578e53
commit 7db0bb0d48
8 changed files with 74 additions and 45 deletions

View File

@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## 1.11.0 Beta 2276 (2020-02-27) ## 1.11.0 Beta 2276 (2020-02-27)
### Changed
- Allow any character in host profile name. [#26](https://github.com/passepartoutvpn/passepartout-ios/issues/26)
### Fixed ### Fixed
- Programming error in some SoftEther negotiation (Grivus). [tunnelkit#143](https://github.com/passepartoutvpn/tunnelkit/pull/143) - Programming error in some SoftEther negotiation (Grivus). [tunnelkit#143](https://github.com/passepartoutvpn/tunnelkit/pull/143)

View File

@ -208,7 +208,7 @@ class ProductManager: NSObject {
log.debug("Checking 'Unlimited hosts'") log.debug("Checking 'Unlimited hosts'")
if !isEligible(forFeature: .unlimitedHosts) { if !isEligible(forFeature: .unlimitedHosts) {
let ids = service.currentHostNames() let ids = service.hostIds()
if ids.count > AppConstants.InApp.limitedNumberOfHosts { if ids.count > AppConstants.InApp.limitedNumberOfHosts {
for id in ids { for id in ids {
service.removeProfile(ProfileKey(.host, id)) service.removeProfile(ProfileKey(.host, id))
@ -219,7 +219,7 @@ class ProductManager: NSObject {
} }
log.debug("Checking providers") log.debug("Checking providers")
for name in service.currentProviderNames() { for name in service.providerNames() {
guard let metadata = InfrastructureFactory.shared.metadata(forName: name) else { guard let metadata = InfrastructureFactory.shared.metadata(forName: name) else {
continue continue
} }
@ -246,7 +246,7 @@ class ProductManager: NSObject {
extension ConnectionService { extension ConnectionService {
var hasReachedMaximumNumberOfHosts: Bool { var hasReachedMaximumNumberOfHosts: Bool {
let numberOfHosts = currentHostNames().count let numberOfHosts = hostIds().count
return numberOfHosts >= AppConstants.InApp.limitedNumberOfHosts return numberOfHosts >= AppConstants.InApp.limitedNumberOfHosts
} }
} }

View File

@ -60,6 +60,7 @@ class ConfigurationViewController: UIViewController, StrongTableHost {
// sections // sections
if isEditable { if isEditable {
model.add(.reset) model.add(.reset)
model.setHeader("", forSection: .reset)
} }
if !isServerPushed { if !isServerPushed {
model.add(.tls) model.add(.tls)

View File

@ -85,8 +85,8 @@ class OrganizerViewController: UITableViewController, StrongTableHost {
// //
providers = service.currentProviderNames().sorted() providers = service.sortedProviderNames()
hosts = service.currentHostNames().sortedCaseInsensitive() hosts = service.sortedHostIds()
var providerRows = [RowType](repeating: .profile, count: providers.count) var providerRows = [RowType](repeating: .profile, count: providers.count)
var hostRows = [RowType](repeating: .profile, count: hosts.count) var hostRows = [RowType](repeating: .profile, count: hosts.count)
@ -467,11 +467,10 @@ extension OrganizerViewController {
if rowProfile.context == .provider { if rowProfile.context == .provider {
let metadata = InfrastructureFactory.shared.metadata(forName: rowProfile.id) let metadata = InfrastructureFactory.shared.metadata(forName: rowProfile.id)
cell.imageView?.image = metadata?.logo cell.imageView?.image = metadata?.logo
cell.leftText = metadata?.description ?? rowProfile.id
} else { } else {
cell.imageView?.image = nil cell.imageView?.image = nil
cell.leftText = rowProfile.id
} }
cell.leftText = service.screenTitle(rowProfile)
cell.rightText = service.isActiveProfile(rowProfile) ? L10n.Core.Organizer.Cells.Profile.Value.current : nil cell.rightText = service.isActiveProfile(rowProfile) ? L10n.Core.Organizer.Cells.Profile.Value.current : nil
return cell return cell
@ -693,7 +692,7 @@ extension OrganizerViewController: ConnectionServiceDelegate {
perform(segue: StoryboardSegue.Organizer.selectProfileSegueIdentifier, sender: profile) perform(segue: StoryboardSegue.Organizer.selectProfileSegueIdentifier, sender: profile)
} }
func connectionService(didRename oldProfile: ConnectionProfile, to newProfile: ConnectionProfile) { func connectionService(didRename profile: ConnectionProfile, to newTitle: String) {
TransientStore.shared.serialize(withProfiles: false) // rename TransientStore.shared.serialize(withProfiles: false) // rename
reloadModel() reloadModel()

View File

@ -34,9 +34,7 @@ private let log = SwiftyBeaver.self
class WizardHostViewController: UITableViewController, StrongTableHost { class WizardHostViewController: UITableViewController, StrongTableHost {
@IBOutlet private weak var itemNext: UIBarButtonItem! @IBOutlet private weak var itemNext: UIBarButtonItem!
private let existingHosts: [String] = { private let existingHostIds = TransientStore.shared.service.sortedHostIds()
return TransientStore.shared.service.currentHostNames().sortedCaseInsensitive()
}()
var parsingResult: OpenVPN.ConfigurationParser.Result? { var parsingResult: OpenVPN.ConfigurationParser.Result? {
didSet { didSet {
@ -47,19 +45,23 @@ class WizardHostViewController: UITableViewController, StrongTableHost {
var removesConfigurationOnCancel = false var removesConfigurationOnCancel = false
private var createdProfile: HostConnectionProfile? private var createdProfile: HostConnectionProfile?
private var createdTitle: String?
private var replacedProfile: ConnectionProfile?
// MARK: StrongTableHost // MARK: StrongTableHost
lazy var model: StrongTableModel<SectionType, RowType> = { lazy var model: StrongTableModel<SectionType, RowType> = {
let model: StrongTableModel<SectionType, RowType> = StrongTableModel() let model: StrongTableModel<SectionType, RowType> = StrongTableModel()
model.add(.meta) model.add(.meta)
model.setFooter(L10n.Core.Global.Host.TitleInput.message, forSection: .meta) // model.setFooter(L10n.Core.Global.Host.TitleInput.message, forSection: .meta)
if !existingHosts.isEmpty { if !existingHostIds.isEmpty {
model.add(.existing) model.add(.existing)
model.setHeader(L10n.App.Wizards.Host.Sections.Existing.header, forSection: .existing) model.setHeader(L10n.App.Wizards.Host.Sections.Existing.header, forSection: .existing)
} }
model.set([.titleInput], forSection: .meta) model.set([.titleInput], forSection: .meta)
model.set(.existingHost, count: existingHosts.count, forSection: .existing) model.set(.existingHost, count: existingHostIds.count, forSection: .existing)
return model return model
}() }()
@ -104,26 +106,28 @@ class WizardHostViewController: UITableViewController, StrongTableHost {
return return
} }
let profile = HostConnectionProfile(title: enteredTitle, hostname: hostname) let profile = HostConnectionProfile(hostname: hostname)
let builder = OpenVPNTunnelProvider.ConfigurationBuilder(sessionConfiguration: result.configuration) let builder = OpenVPNTunnelProvider.ConfigurationBuilder(sessionConfiguration: result.configuration)
profile.parameters = builder.build() profile.parameters = builder.build()
let service = TransientStore.shared.service let service = TransientStore.shared.service
guard !service.containsProfile(profile) else { replacedProfile = nil
let replacedProfile = service.profile(withContext: profile.context, id: profile.id) if let existingHostId = service.existingHostId(withTitle: enteredTitle) {
replacedProfile = service.profile(withContext: profile.context, id: existingHostId)
let alert = UIAlertController.asAlert(title, L10n.Core.Wizards.Host.Alerts.Existing.message) let alert = UIAlertController.asAlert(title, L10n.Core.Wizards.Host.Alerts.Existing.message)
alert.addPreferredAction(L10n.Core.Global.ok) { alert.addPreferredAction(L10n.Core.Global.ok) {
self.next(withProfile: profile, replacedProfile: replacedProfile) self.next(withProfile: profile, title: enteredTitle)
} }
alert.addCancelAction(L10n.Core.Global.cancel) alert.addCancelAction(L10n.Core.Global.cancel)
present(alert, animated: true, completion: nil) present(alert, animated: true, completion: nil)
return return
} }
next(withProfile: profile, replacedProfile: nil) next(withProfile: profile, title: enteredTitle)
} }
private func next(withProfile profile: HostConnectionProfile, replacedProfile: ConnectionProfile?) { private func next(withProfile profile: HostConnectionProfile, title: String) {
createdProfile = profile createdProfile = profile
createdTitle = title
let accountVC = StoryboardScene.Main.accountIdentifier.instantiate() let accountVC = StoryboardScene.Main.accountIdentifier.instantiate()
if let replacedProfile = replacedProfile { if let replacedProfile = replacedProfile {
@ -134,7 +138,7 @@ class WizardHostViewController: UITableViewController, StrongTableHost {
} }
private func finish(withCredentials credentials: Credentials) { private func finish(withCredentials credentials: Credentials) {
guard let profile = createdProfile else { guard let profile = createdProfile, let title = createdTitle else {
fatalError("No profile created?") fatalError("No profile created?")
} }
let service = TransientStore.shared.service let service = TransientStore.shared.service
@ -150,7 +154,10 @@ class WizardHostViewController: UITableViewController, StrongTableHost {
} }
} }
dismiss(animated: true) { dismiss(animated: true) {
service.addOrReplaceProfile(profile, credentials: credentials) if let replacedProfile = self.replacedProfile {
service.removeProfile(ProfileKey(replacedProfile))
}
service.addOrReplaceProfile(profile, credentials: credentials, title: title)
} }
} }
@ -206,15 +213,15 @@ extension WizardHostViewController {
let cell = Cells.field.dequeue(from: tableView, for: indexPath) let cell = Cells.field.dequeue(from: tableView, for: indexPath)
cell.caption = L10n.App.Wizards.Host.Cells.TitleInput.caption cell.caption = L10n.App.Wizards.Host.Cells.TitleInput.caption
cell.captionWidth = 100.0 cell.captionWidth = 100.0
cell.allowedCharset = .filename // cell.allowedCharset = .filename
cell.field.applyProfileId(.current) cell.field.applyProfileId(.current)
cell.delegate = self cell.delegate = self
return cell return cell
case .existingHost: case .existingHost:
let hostTitle = existingHosts[indexPath.row] let existingTitle = hostTitle(at: indexPath.row)
let cell = Cells.setting.dequeue(from: tableView, for: indexPath) let cell = Cells.setting.dequeue(from: tableView, for: indexPath)
cell.leftText = hostTitle cell.leftText = existingTitle
cell.accessoryType = .none cell.accessoryType = .none
cell.isTappable = true cell.isTappable = true
return cell return cell
@ -227,15 +234,19 @@ extension WizardHostViewController {
guard let titleIndexPath = model.indexPath(forRow: .titleInput, ofSection: .meta) else { guard let titleIndexPath = model.indexPath(forRow: .titleInput, ofSection: .meta) else {
fatalError("Could not found title cell?") fatalError("Could not found title cell?")
} }
let hostTitle = existingHosts[indexPath.row] let existingTitle = hostTitle(at: indexPath.row)
let cellTitle = tableView.cellForRow(at: titleIndexPath) as? FieldTableViewCell let cellTitle = tableView.cellForRow(at: titleIndexPath) as? FieldTableViewCell
cellTitle?.field.text = hostTitle cellTitle?.field.text = existingTitle
tableView.deselectRow(at: indexPath, animated: true) tableView.deselectRow(at: indexPath, animated: true)
default: default:
break break
} }
} }
private func hostTitle(at row: Int) -> String {
return TransientStore.shared.service.screenTitle(forHostId: existingHostIds[row])
}
} }
// MARK: - // MARK: -

View File

@ -79,7 +79,11 @@ class ServiceViewController: UIViewController, StrongTableHost {
self.profile = profile self.profile = profile
vpn.profile = profile vpn.profile = profile
title = profile?.screenTitle if let profile = profile {
title = service.screenTitle(ProfileKey(profile))
} else {
title = nil
}
navigationItem.rightBarButtonItem = (profile?.context == .host) ? itemEdit : nil navigationItem.rightBarButtonItem = (profile?.context == .host) ? itemEdit : nil
if reloadingViews { if reloadingViews {
reloadModel() reloadModel()
@ -230,34 +234,36 @@ class ServiceViewController: UIViewController, StrongTableHost {
} }
@IBAction private func renameProfile() { @IBAction private func renameProfile() {
let alert = UIAlertController.asAlert(L10n.Core.Service.Alerts.Rename.title, L10n.Core.Global.Host.TitleInput.message) let alert = UIAlertController.asAlert(L10n.Core.Service.Alerts.Rename.title, nil)
alert.addTextField { (field) in alert.addTextField { (field) in
field.text = self.profile?.id field.text = self.service.screenTitle(ProfileKey(self.uncheckedProfile))
field.applyProfileId(.current) field.applyProfileId(.current)
field.delegate = self field.delegate = self
} }
pendingRenameAction = alert.addPreferredAction(L10n.Core.Global.ok) { pendingRenameAction = alert.addPreferredAction(L10n.Core.Global.ok) {
guard let newId = alert.textFields?.first?.text else { guard let newTitle = alert.textFields?.first?.text else {
return return
} }
self.doRenameCurrentProfile(to: newId) self.doRenameCurrentProfile(to: newTitle)
} }
alert.addCancelAction(L10n.Core.Global.cancel) alert.addCancelAction(L10n.Core.Global.cancel)
pendingRenameAction?.isEnabled = false pendingRenameAction?.isEnabled = false
present(alert, animated: true, completion: nil) present(alert, animated: true, completion: nil)
} }
private func doRenameCurrentProfile(to newId: String) { private func doRenameCurrentProfile(to newTitle: String) {
guard let renamedProfile = service.renameProfile(uncheckedHostProfile, to: newId) else { guard let profile = profile else {
return return
} }
setProfile(renamedProfile, reloadingViews: false) service.renameProfile(profile, to: newTitle)
setProfile(profile, reloadingViews: false)
} }
private func toggleVpnService(cell: ToggleTableViewCell) { private func toggleVpnService(cell: ToggleTableViewCell) {
if cell.isOn { if cell.isOn {
if #available(iOS 12, *) { if #available(iOS 12, *) {
IntentDispatcher.donateConnection(with: uncheckedProfile) let title = service.screenTitle(ProfileKey(uncheckedProfile))
IntentDispatcher.donateConnection(with: uncheckedProfile, title: title)
} }
guard !service.needsCredentials(for: uncheckedProfile) else { guard !service.needsCredentials(for: uncheckedProfile) else {
let alert = UIAlertController.asAlert( let alert = UIAlertController.asAlert(
@ -1424,7 +1430,8 @@ extension ServiceViewController: ProviderPoolViewControllerDelegate {
vpn.reinstallIfEnabled() vpn.reinstallIfEnabled()
if #available(iOS 12, *) { if #available(iOS 12, *) {
IntentDispatcher.donateConnection(with: uncheckedProviderProfile) let title = service.screenTitle(forProviderName: uncheckedProviderProfile.name)
IntentDispatcher.donateConnection(with: uncheckedProviderProfile, title: title)
} }
} }

View File

@ -33,7 +33,7 @@ import Convenience
class ShortcutsConnectToViewController: UITableViewController, ProviderPoolViewControllerDelegate, StrongTableHost { class ShortcutsConnectToViewController: UITableViewController, ProviderPoolViewControllerDelegate, StrongTableHost {
private let service = TransientStore.shared.service private let service = TransientStore.shared.service
private var providers: [Infrastructure.Metadata] = [] private var providers: [Infrastructure.Name] = []
private var hosts: [String] = [] private var hosts: [String] = []
@ -51,8 +51,8 @@ class ShortcutsConnectToViewController: UITableViewController, ProviderPoolViewC
}() }()
func reloadModel() { func reloadModel() {
providers = service.currentProviderMetadata().sorted() providers = service.sortedProviderNames()
hosts = service.currentHostNames().sortedCaseInsensitive() hosts = service.sortedHostIds()
if !providers.isEmpty { if !providers.isEmpty {
model.add(.providers) model.add(.providers)
@ -124,10 +124,16 @@ class ShortcutsConnectToViewController: UITableViewController, ProviderPoolViewC
let cell = Cells.setting.dequeue(from: tableView, for: indexPath) let cell = Cells.setting.dequeue(from: tableView, for: indexPath)
switch model.row(at: indexPath) { switch model.row(at: indexPath) {
case .providerShortcut: case .providerShortcut:
cell.leftText = providers[indexPath.row].description let name = providers[indexPath.row]
if let metadata = InfrastructureFactory.shared.metadata(forName: name) {
cell.leftText = metadata.description
} else {
cell.leftText = name
}
case .hostShortcut: case .hostShortcut:
cell.leftText = hosts[indexPath.row] let id = hosts[indexPath.row]
cell.leftText = service.screenTitle(forHostId: id)
} }
return cell return cell
} }
@ -135,7 +141,7 @@ class ShortcutsConnectToViewController: UITableViewController, ProviderPoolViewC
override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
switch model.row(at: indexPath) { switch model.row(at: indexPath) {
case .providerShortcut: case .providerShortcut:
selectedProfile = service.profile(withContext: .provider, id: providers[indexPath.row].name) selectedProfile = service.profile(withContext: .provider, id: providers[indexPath.row])
pickProviderLocation() pickProviderLocation()
case .hostShortcut: case .hostShortcut:
@ -150,7 +156,8 @@ class ShortcutsConnectToViewController: UITableViewController, ProviderPoolViewC
guard let host = selectedProfile as? HostConnectionProfile else { guard let host = selectedProfile as? HostConnectionProfile else {
fatalError("Not a HostConnectionProfile") fatalError("Not a HostConnectionProfile")
} }
addShortcut(with: IntentDispatcher.intentConnect(profile: host)) let title = service.screenTitle(forHostId: host.id)
addShortcut(with: IntentDispatcher.intentConnect(profile: host, title: title))
} }
private func addMoveToLocation(pool: Pool) { private func addMoveToLocation(pool: Pool) {

@ -1 +1 @@
Subproject commit 872f827c80ab3a94565edb3db3ae8af5ec619d41 Subproject commit d3f2702639049d95a4c52bcf0368ce54b027cff7