diff --git a/CHANGELOG.md b/CHANGELOG.md index d8c36f6c..1ab7eb16 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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) +### Changed + +- Allow any character in host profile name. [#26](https://github.com/passepartoutvpn/passepartout-ios/issues/26) + ### Fixed - Programming error in some SoftEther negotiation (Grivus). [tunnelkit#143](https://github.com/passepartoutvpn/tunnelkit/pull/143) diff --git a/Passepartout-iOS/Global/ProductManager.swift b/Passepartout-iOS/Global/ProductManager.swift index e96342b7..b4e5cf32 100644 --- a/Passepartout-iOS/Global/ProductManager.swift +++ b/Passepartout-iOS/Global/ProductManager.swift @@ -208,7 +208,7 @@ class ProductManager: NSObject { log.debug("Checking 'Unlimited hosts'") if !isEligible(forFeature: .unlimitedHosts) { - let ids = service.currentHostNames() + let ids = service.hostIds() if ids.count > AppConstants.InApp.limitedNumberOfHosts { for id in ids { service.removeProfile(ProfileKey(.host, id)) @@ -219,7 +219,7 @@ class ProductManager: NSObject { } log.debug("Checking providers") - for name in service.currentProviderNames() { + for name in service.providerNames() { guard let metadata = InfrastructureFactory.shared.metadata(forName: name) else { continue } @@ -246,7 +246,7 @@ class ProductManager: NSObject { extension ConnectionService { var hasReachedMaximumNumberOfHosts: Bool { - let numberOfHosts = currentHostNames().count + let numberOfHosts = hostIds().count return numberOfHosts >= AppConstants.InApp.limitedNumberOfHosts } } diff --git a/Passepartout-iOS/Scenes/ConfigurationViewController.swift b/Passepartout-iOS/Scenes/ConfigurationViewController.swift index 3d91af53..11f8acd0 100644 --- a/Passepartout-iOS/Scenes/ConfigurationViewController.swift +++ b/Passepartout-iOS/Scenes/ConfigurationViewController.swift @@ -60,6 +60,7 @@ class ConfigurationViewController: UIViewController, StrongTableHost { // sections if isEditable { model.add(.reset) + model.setHeader("", forSection: .reset) } if !isServerPushed { model.add(.tls) diff --git a/Passepartout-iOS/Scenes/Organizer/OrganizerViewController.swift b/Passepartout-iOS/Scenes/Organizer/OrganizerViewController.swift index 78634b6a..ea25d7ef 100644 --- a/Passepartout-iOS/Scenes/Organizer/OrganizerViewController.swift +++ b/Passepartout-iOS/Scenes/Organizer/OrganizerViewController.swift @@ -85,8 +85,8 @@ class OrganizerViewController: UITableViewController, StrongTableHost { // - providers = service.currentProviderNames().sorted() - hosts = service.currentHostNames().sortedCaseInsensitive() + providers = service.sortedProviderNames() + hosts = service.sortedHostIds() var providerRows = [RowType](repeating: .profile, count: providers.count) var hostRows = [RowType](repeating: .profile, count: hosts.count) @@ -467,11 +467,10 @@ extension OrganizerViewController { if rowProfile.context == .provider { let metadata = InfrastructureFactory.shared.metadata(forName: rowProfile.id) cell.imageView?.image = metadata?.logo - cell.leftText = metadata?.description ?? rowProfile.id } else { 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 return cell @@ -693,7 +692,7 @@ extension OrganizerViewController: ConnectionServiceDelegate { 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 reloadModel() diff --git a/Passepartout-iOS/Scenes/Organizer/WizardHostViewController.swift b/Passepartout-iOS/Scenes/Organizer/WizardHostViewController.swift index 3fea01ae..f0712a5a 100644 --- a/Passepartout-iOS/Scenes/Organizer/WizardHostViewController.swift +++ b/Passepartout-iOS/Scenes/Organizer/WizardHostViewController.swift @@ -34,9 +34,7 @@ private let log = SwiftyBeaver.self class WizardHostViewController: UITableViewController, StrongTableHost { @IBOutlet private weak var itemNext: UIBarButtonItem! - private let existingHosts: [String] = { - return TransientStore.shared.service.currentHostNames().sortedCaseInsensitive() - }() + private let existingHostIds = TransientStore.shared.service.sortedHostIds() var parsingResult: OpenVPN.ConfigurationParser.Result? { didSet { @@ -47,19 +45,23 @@ class WizardHostViewController: UITableViewController, StrongTableHost { var removesConfigurationOnCancel = false private var createdProfile: HostConnectionProfile? + + private var createdTitle: String? + + private var replacedProfile: ConnectionProfile? // MARK: StrongTableHost lazy var model: StrongTableModel = { let model: StrongTableModel = StrongTableModel() model.add(.meta) - model.setFooter(L10n.Core.Global.Host.TitleInput.message, forSection: .meta) - if !existingHosts.isEmpty { +// model.setFooter(L10n.Core.Global.Host.TitleInput.message, forSection: .meta) + if !existingHostIds.isEmpty { model.add(.existing) model.setHeader(L10n.App.Wizards.Host.Sections.Existing.header, forSection: .existing) } model.set([.titleInput], forSection: .meta) - model.set(.existingHost, count: existingHosts.count, forSection: .existing) + model.set(.existingHost, count: existingHostIds.count, forSection: .existing) return model }() @@ -104,26 +106,28 @@ class WizardHostViewController: UITableViewController, StrongTableHost { return } - let profile = HostConnectionProfile(title: enteredTitle, hostname: hostname) + let profile = HostConnectionProfile(hostname: hostname) let builder = OpenVPNTunnelProvider.ConfigurationBuilder(sessionConfiguration: result.configuration) profile.parameters = builder.build() let service = TransientStore.shared.service - guard !service.containsProfile(profile) else { - let replacedProfile = service.profile(withContext: profile.context, id: profile.id) + replacedProfile = nil + 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) alert.addPreferredAction(L10n.Core.Global.ok) { - self.next(withProfile: profile, replacedProfile: replacedProfile) + self.next(withProfile: profile, title: enteredTitle) } alert.addCancelAction(L10n.Core.Global.cancel) present(alert, animated: true, completion: nil) 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 + createdTitle = title let accountVC = StoryboardScene.Main.accountIdentifier.instantiate() if let replacedProfile = replacedProfile { @@ -134,7 +138,7 @@ class WizardHostViewController: UITableViewController, StrongTableHost { } private func finish(withCredentials credentials: Credentials) { - guard let profile = createdProfile else { + guard let profile = createdProfile, let title = createdTitle else { fatalError("No profile created?") } let service = TransientStore.shared.service @@ -150,7 +154,10 @@ class WizardHostViewController: UITableViewController, StrongTableHost { } } 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) cell.caption = L10n.App.Wizards.Host.Cells.TitleInput.caption cell.captionWidth = 100.0 - cell.allowedCharset = .filename +// cell.allowedCharset = .filename cell.field.applyProfileId(.current) cell.delegate = self return cell case .existingHost: - let hostTitle = existingHosts[indexPath.row] + let existingTitle = hostTitle(at: indexPath.row) let cell = Cells.setting.dequeue(from: tableView, for: indexPath) - cell.leftText = hostTitle + cell.leftText = existingTitle cell.accessoryType = .none cell.isTappable = true return cell @@ -227,15 +234,19 @@ extension WizardHostViewController { guard let titleIndexPath = model.indexPath(forRow: .titleInput, ofSection: .meta) else { 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 - cellTitle?.field.text = hostTitle + cellTitle?.field.text = existingTitle tableView.deselectRow(at: indexPath, animated: true) default: break } } + + private func hostTitle(at row: Int) -> String { + return TransientStore.shared.service.screenTitle(forHostId: existingHostIds[row]) + } } // MARK: - diff --git a/Passepartout-iOS/Scenes/ServiceViewController.swift b/Passepartout-iOS/Scenes/ServiceViewController.swift index 7f06d6a9..1c70ec69 100644 --- a/Passepartout-iOS/Scenes/ServiceViewController.swift +++ b/Passepartout-iOS/Scenes/ServiceViewController.swift @@ -79,7 +79,11 @@ class ServiceViewController: UIViewController, StrongTableHost { self.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 if reloadingViews { reloadModel() @@ -230,34 +234,36 @@ class ServiceViewController: UIViewController, StrongTableHost { } @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 - field.text = self.profile?.id + field.text = self.service.screenTitle(ProfileKey(self.uncheckedProfile)) field.applyProfileId(.current) field.delegate = self } pendingRenameAction = alert.addPreferredAction(L10n.Core.Global.ok) { - guard let newId = alert.textFields?.first?.text else { + guard let newTitle = alert.textFields?.first?.text else { return } - self.doRenameCurrentProfile(to: newId) + self.doRenameCurrentProfile(to: newTitle) } alert.addCancelAction(L10n.Core.Global.cancel) pendingRenameAction?.isEnabled = false present(alert, animated: true, completion: nil) } - private func doRenameCurrentProfile(to newId: String) { - guard let renamedProfile = service.renameProfile(uncheckedHostProfile, to: newId) else { + private func doRenameCurrentProfile(to newTitle: String) { + guard let profile = profile else { return } - setProfile(renamedProfile, reloadingViews: false) + service.renameProfile(profile, to: newTitle) + setProfile(profile, reloadingViews: false) } private func toggleVpnService(cell: ToggleTableViewCell) { if cell.isOn { 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 { let alert = UIAlertController.asAlert( @@ -1424,7 +1430,8 @@ extension ServiceViewController: ProviderPoolViewControllerDelegate { vpn.reinstallIfEnabled() if #available(iOS 12, *) { - IntentDispatcher.donateConnection(with: uncheckedProviderProfile) + let title = service.screenTitle(forProviderName: uncheckedProviderProfile.name) + IntentDispatcher.donateConnection(with: uncheckedProviderProfile, title: title) } } diff --git a/Passepartout-iOS/Scenes/Shortcuts/ShortcutsConnectToViewController.swift b/Passepartout-iOS/Scenes/Shortcuts/ShortcutsConnectToViewController.swift index feb4b952..202a713d 100644 --- a/Passepartout-iOS/Scenes/Shortcuts/ShortcutsConnectToViewController.swift +++ b/Passepartout-iOS/Scenes/Shortcuts/ShortcutsConnectToViewController.swift @@ -33,7 +33,7 @@ import Convenience class ShortcutsConnectToViewController: UITableViewController, ProviderPoolViewControllerDelegate, StrongTableHost { private let service = TransientStore.shared.service - private var providers: [Infrastructure.Metadata] = [] + private var providers: [Infrastructure.Name] = [] private var hosts: [String] = [] @@ -51,8 +51,8 @@ class ShortcutsConnectToViewController: UITableViewController, ProviderPoolViewC }() func reloadModel() { - providers = service.currentProviderMetadata().sorted() - hosts = service.currentHostNames().sortedCaseInsensitive() + providers = service.sortedProviderNames() + hosts = service.sortedHostIds() if !providers.isEmpty { model.add(.providers) @@ -124,10 +124,16 @@ class ShortcutsConnectToViewController: UITableViewController, ProviderPoolViewC let cell = Cells.setting.dequeue(from: tableView, for: indexPath) switch model.row(at: indexPath) { 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: - cell.leftText = hosts[indexPath.row] + let id = hosts[indexPath.row] + cell.leftText = service.screenTitle(forHostId: id) } return cell } @@ -135,7 +141,7 @@ class ShortcutsConnectToViewController: UITableViewController, ProviderPoolViewC override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { switch model.row(at: indexPath) { case .providerShortcut: - selectedProfile = service.profile(withContext: .provider, id: providers[indexPath.row].name) + selectedProfile = service.profile(withContext: .provider, id: providers[indexPath.row]) pickProviderLocation() case .hostShortcut: @@ -150,7 +156,8 @@ class ShortcutsConnectToViewController: UITableViewController, ProviderPoolViewC guard let host = selectedProfile as? HostConnectionProfile else { 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) { diff --git a/Submodules/Core b/Submodules/Core index 872f827c..d3f27026 160000 --- a/Submodules/Core +++ b/Submodules/Core @@ -1 +1 @@ -Subproject commit 872f827c80ab3a94565edb3db3ae8af5ec619d41 +Subproject commit d3f2702639049d95a4c52bcf0368ce54b027cff7