2018-10-24 01:37:28 +00:00
|
|
|
// SPDX-License-Identifier: MIT
|
2021-06-17 14:56:46 +00:00
|
|
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
2018-08-11 07:49:21 +00:00
|
|
|
|
|
|
|
import UIKit
|
|
|
|
|
2018-10-13 01:38:30 +00:00
|
|
|
class MainViewController: UISplitViewController {
|
2018-12-03 13:21:51 +00:00
|
|
|
|
|
|
|
var tunnelsManager: TunnelsManager?
|
|
|
|
var onTunnelsManagerReady: ((TunnelsManager) -> Void)?
|
2018-10-25 05:40:18 +00:00
|
|
|
var tunnelsListVC: TunnelsListTableViewController?
|
|
|
|
|
2018-11-07 10:24:30 +00:00
|
|
|
init() {
|
2018-10-13 01:38:30 +00:00
|
|
|
let detailVC = UIViewController()
|
2019-10-25 08:59:16 +00:00
|
|
|
if #available(iOS 13.0, *) {
|
|
|
|
detailVC.view.backgroundColor = .systemBackground
|
|
|
|
} else {
|
|
|
|
detailVC.view.backgroundColor = .white
|
|
|
|
}
|
2018-10-13 01:38:30 +00:00
|
|
|
let detailNC = UINavigationController(rootViewController: detailVC)
|
|
|
|
|
|
|
|
let masterVC = TunnelsListTableViewController()
|
|
|
|
let masterNC = UINavigationController(rootViewController: masterVC)
|
|
|
|
|
2018-12-14 23:12:59 +00:00
|
|
|
tunnelsListVC = masterVC
|
2018-11-07 10:24:30 +00:00
|
|
|
|
|
|
|
super.init(nibName: nil, bundle: nil)
|
2018-10-13 01:38:30 +00:00
|
|
|
|
2018-12-14 23:12:59 +00:00
|
|
|
viewControllers = [ masterNC, detailNC ]
|
2018-12-07 13:35:04 +00:00
|
|
|
|
2018-12-14 23:12:59 +00:00
|
|
|
restorationIdentifier = "MainVC"
|
2018-12-07 13:35:04 +00:00
|
|
|
masterNC.restorationIdentifier = "MasterNC"
|
|
|
|
detailNC.restorationIdentifier = "DetailNC"
|
2018-11-07 10:24:30 +00:00
|
|
|
}
|
2018-10-25 05:40:18 +00:00
|
|
|
|
2018-11-07 10:24:30 +00:00
|
|
|
required init?(coder aDecoder: NSCoder) {
|
|
|
|
fatalError("init(coder:) has not been implemented")
|
2018-08-11 07:49:21 +00:00
|
|
|
}
|
2018-10-13 10:32:42 +00:00
|
|
|
|
|
|
|
override func viewDidLoad() {
|
2018-12-14 23:12:59 +00:00
|
|
|
delegate = self
|
2018-10-13 10:32:42 +00:00
|
|
|
|
|
|
|
// On iPad, always show both masterVC and detailVC, even in portrait mode, like the Settings app
|
2018-12-14 23:12:59 +00:00
|
|
|
preferredDisplayMode = .allVisible
|
2018-12-03 13:21:51 +00:00
|
|
|
|
|
|
|
// Create the tunnels manager, and when it's ready, inform tunnelsListVC
|
2018-12-06 10:28:27 +00:00
|
|
|
TunnelsManager.create { [weak self] result in
|
2018-12-12 17:40:57 +00:00
|
|
|
guard let self = self else { return }
|
|
|
|
|
2019-04-08 07:52:06 +00:00
|
|
|
switch result {
|
|
|
|
case .failure(let error):
|
2018-12-06 10:28:27 +00:00
|
|
|
ErrorPresenter.showErrorAlert(error: error, from: self)
|
2019-04-08 07:52:06 +00:00
|
|
|
case .success(let tunnelsManager):
|
|
|
|
self.tunnelsManager = tunnelsManager
|
|
|
|
self.tunnelsListVC?.setTunnelsManager(tunnelsManager: tunnelsManager)
|
2018-12-03 13:21:51 +00:00
|
|
|
|
2019-04-08 07:52:06 +00:00
|
|
|
tunnelsManager.activationDelegate = self
|
2018-12-03 13:21:51 +00:00
|
|
|
|
2019-04-08 07:52:06 +00:00
|
|
|
self.onTunnelsManagerReady?(tunnelsManager)
|
|
|
|
self.onTunnelsManagerReady = nil
|
|
|
|
}
|
2018-12-03 13:21:51 +00:00
|
|
|
}
|
|
|
|
}
|
2018-12-22 03:59:43 +00:00
|
|
|
|
2019-04-30 12:58:06 +00:00
|
|
|
func allTunnelNames() -> [String]? {
|
|
|
|
guard let tunnelsManager = self.tunnelsManager else { return nil }
|
|
|
|
return tunnelsManager.mapTunnels { $0.name }
|
|
|
|
}
|
2018-12-03 13:21:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
extension MainViewController: TunnelsManagerActivationDelegate {
|
2018-12-13 13:25:20 +00:00
|
|
|
func tunnelActivationAttemptFailed(tunnel: TunnelContainer, error: TunnelsManagerActivationAttemptError) {
|
2018-12-03 13:21:51 +00:00
|
|
|
ErrorPresenter.showErrorAlert(error: error, from: self)
|
|
|
|
}
|
2018-12-13 13:25:20 +00:00
|
|
|
|
|
|
|
func tunnelActivationAttemptSucceeded(tunnel: TunnelContainer) {
|
|
|
|
// Nothing to do
|
|
|
|
}
|
|
|
|
|
|
|
|
func tunnelActivationFailed(tunnel: TunnelContainer, error: TunnelsManagerActivationError) {
|
|
|
|
ErrorPresenter.showErrorAlert(error: error, from: self)
|
|
|
|
}
|
|
|
|
|
|
|
|
func tunnelActivationSucceeded(tunnel: TunnelContainer) {
|
|
|
|
// Nothing to do
|
|
|
|
}
|
2018-12-03 13:21:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
extension MainViewController {
|
|
|
|
func refreshTunnelConnectionStatuses() {
|
|
|
|
if let tunnelsManager = tunnelsManager {
|
|
|
|
tunnelsManager.refreshStatuses()
|
|
|
|
}
|
2018-10-13 10:32:42 +00:00
|
|
|
}
|
2018-12-07 13:35:04 +00:00
|
|
|
|
2019-04-30 12:58:06 +00:00
|
|
|
func showTunnelDetailForTunnel(named tunnelName: String, animated: Bool, shouldToggleStatus: Bool) {
|
2018-12-12 21:33:14 +00:00
|
|
|
let showTunnelDetailBlock: (TunnelsManager) -> Void = { [weak self] tunnelsManager in
|
2019-05-18 21:34:23 +00:00
|
|
|
guard let self = self else { return }
|
|
|
|
guard let tunnelsListVC = self.tunnelsListVC else { return }
|
2018-12-07 13:35:04 +00:00
|
|
|
if let tunnel = tunnelsManager.tunnel(named: tunnelName) {
|
2019-05-18 21:34:23 +00:00
|
|
|
tunnelsListVC.showTunnelDetail(for: tunnel, animated: false)
|
2019-04-30 12:58:06 +00:00
|
|
|
if shouldToggleStatus {
|
|
|
|
if tunnel.status == .inactive {
|
|
|
|
tunnelsManager.startActivation(of: tunnel)
|
|
|
|
} else if tunnel.status == .active {
|
|
|
|
tunnelsManager.startDeactivation(of: tunnel)
|
|
|
|
}
|
|
|
|
}
|
2018-12-07 13:35:04 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if let tunnelsManager = tunnelsManager {
|
|
|
|
showTunnelDetailBlock(tunnelsManager)
|
|
|
|
} else {
|
|
|
|
onTunnelsManagerReady = showTunnelDetailBlock
|
|
|
|
}
|
|
|
|
}
|
2019-06-13 17:31:47 +00:00
|
|
|
|
|
|
|
func importFromDisposableFile(url: URL) {
|
|
|
|
let importFromFileBlock: (TunnelsManager) -> Void = { [weak self] tunnelsManager in
|
|
|
|
TunnelImporter.importFromFile(urls: [url], into: tunnelsManager, sourceVC: self, errorPresenterType: ErrorPresenter.self) {
|
|
|
|
_ = FileManager.deleteFile(at: url)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if let tunnelsManager = tunnelsManager {
|
|
|
|
importFromFileBlock(tunnelsManager)
|
|
|
|
} else {
|
|
|
|
onTunnelsManagerReady = importFromFileBlock
|
|
|
|
}
|
|
|
|
}
|
2018-10-13 10:32:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
extension MainViewController: UISplitViewControllerDelegate {
|
|
|
|
func splitViewController(_ splitViewController: UISplitViewController,
|
|
|
|
collapseSecondary secondaryViewController: UIViewController,
|
|
|
|
onto primaryViewController: UIViewController) -> Bool {
|
|
|
|
// On iPhone, if the secondaryVC (detailVC) is just a UIViewController, it indicates that it's empty,
|
|
|
|
// so just show the primaryVC (masterVC).
|
|
|
|
let detailVC = (secondaryViewController as? UINavigationController)?.viewControllers.first
|
|
|
|
let isDetailVCEmpty: Bool
|
|
|
|
if let detailVC = detailVC {
|
|
|
|
isDetailVCEmpty = (type(of: detailVC) == UIViewController.self)
|
|
|
|
} else {
|
|
|
|
isDetailVCEmpty = true
|
|
|
|
}
|
|
|
|
return isDetailVCEmpty
|
|
|
|
}
|
2018-08-11 07:49:21 +00:00
|
|
|
}
|