mirror of
https://github.com/passepartoutvpn/wireguard-apple.git
synced 2025-02-08 00:42:03 +00:00
macOS: Support for on-demand activation
This commit is contained in:
parent
59f8cc792b
commit
1e848c5ef8
@ -59,6 +59,7 @@
|
|||||||
6F919EDA218C65C50023B400 /* wireguard_doc_logo_44x58.png in Resources */ = {isa = PBXBuildFile; fileRef = 6F919ED6218C65C50023B400 /* wireguard_doc_logo_44x58.png */; };
|
6F919EDA218C65C50023B400 /* wireguard_doc_logo_44x58.png in Resources */ = {isa = PBXBuildFile; fileRef = 6F919ED6218C65C50023B400 /* wireguard_doc_logo_44x58.png */; };
|
||||||
6F919EDB218C65C50023B400 /* wireguard_doc_logo_64x64.png in Resources */ = {isa = PBXBuildFile; fileRef = 6F919ED7218C65C50023B400 /* wireguard_doc_logo_64x64.png */; };
|
6F919EDB218C65C50023B400 /* wireguard_doc_logo_64x64.png in Resources */ = {isa = PBXBuildFile; fileRef = 6F919ED7218C65C50023B400 /* wireguard_doc_logo_64x64.png */; };
|
||||||
6F919EDC218C65C50023B400 /* wireguard_doc_logo_320x320.png in Resources */ = {isa = PBXBuildFile; fileRef = 6F919ED8218C65C50023B400 /* wireguard_doc_logo_320x320.png */; };
|
6F919EDC218C65C50023B400 /* wireguard_doc_logo_320x320.png in Resources */ = {isa = PBXBuildFile; fileRef = 6F919ED8218C65C50023B400 /* wireguard_doc_logo_320x320.png */; };
|
||||||
|
6F9B582921E8D6D100544D02 /* PopupRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F9B582721E8CD4300544D02 /* PopupRow.swift */; };
|
||||||
6FB1017921C57DE600766195 /* MockTunnels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB1017821C57DE600766195 /* MockTunnels.swift */; };
|
6FB1017921C57DE600766195 /* MockTunnels.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB1017821C57DE600766195 /* MockTunnels.swift */; };
|
||||||
6FB1BD6021D2607A00A991BF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB1BD5F21D2607A00A991BF /* AppDelegate.swift */; };
|
6FB1BD6021D2607A00A991BF /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FB1BD5F21D2607A00A991BF /* AppDelegate.swift */; };
|
||||||
6FB1BD6221D2607E00A991BF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6FB1BD6121D2607E00A991BF /* Assets.xcassets */; };
|
6FB1BD6221D2607E00A991BF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6FB1BD6121D2607E00A991BF /* Assets.xcassets */; };
|
||||||
@ -267,6 +268,7 @@
|
|||||||
6F919ED6218C65C50023B400 /* wireguard_doc_logo_44x58.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = wireguard_doc_logo_44x58.png; sourceTree = "<group>"; };
|
6F919ED6218C65C50023B400 /* wireguard_doc_logo_44x58.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = wireguard_doc_logo_44x58.png; sourceTree = "<group>"; };
|
||||||
6F919ED7218C65C50023B400 /* wireguard_doc_logo_64x64.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = wireguard_doc_logo_64x64.png; sourceTree = "<group>"; };
|
6F919ED7218C65C50023B400 /* wireguard_doc_logo_64x64.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = wireguard_doc_logo_64x64.png; sourceTree = "<group>"; };
|
||||||
6F919ED8218C65C50023B400 /* wireguard_doc_logo_320x320.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = wireguard_doc_logo_320x320.png; sourceTree = "<group>"; };
|
6F919ED8218C65C50023B400 /* wireguard_doc_logo_320x320.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = wireguard_doc_logo_320x320.png; sourceTree = "<group>"; };
|
||||||
|
6F9B582721E8CD4300544D02 /* PopupRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopupRow.swift; sourceTree = "<group>"; };
|
||||||
6FB1017821C57DE600766195 /* MockTunnels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockTunnels.swift; sourceTree = "<group>"; };
|
6FB1017821C57DE600766195 /* MockTunnels.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockTunnels.swift; sourceTree = "<group>"; };
|
||||||
6FB1BD5D21D2607A00A991BF /* WireGuard.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WireGuard.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
6FB1BD5D21D2607A00A991BF /* WireGuard.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WireGuard.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
6FB1BD5F21D2607A00A991BF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
6FB1BD5F21D2607A00A991BF /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||||
@ -387,6 +389,7 @@
|
|||||||
6F613D9A21DE33B8004B217A /* KeyValueRow.swift */,
|
6F613D9A21DE33B8004B217A /* KeyValueRow.swift */,
|
||||||
5F52D0BA21E3781B00283CEA /* ConfTextView.swift */,
|
5F52D0BA21E3781B00283CEA /* ConfTextView.swift */,
|
||||||
5F52D0BC21E3785C00283CEA /* ConfTextStorage.swift */,
|
5F52D0BC21E3785C00283CEA /* ConfTextStorage.swift */,
|
||||||
|
6F9B582721E8CD4300544D02 /* PopupRow.swift */,
|
||||||
);
|
);
|
||||||
path = View;
|
path = View;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@ -1103,6 +1106,7 @@
|
|||||||
6FCD99B121E0EDA900BA4C82 /* TunnelEditViewController.swift in Sources */,
|
6FCD99B121E0EDA900BA4C82 /* TunnelEditViewController.swift in Sources */,
|
||||||
6FB1BDCA21D50F1700A991BF /* x25519.c in Sources */,
|
6FB1BDCA21D50F1700A991BF /* x25519.c in Sources */,
|
||||||
6FB1BDCB21D50F1700A991BF /* Curve25519.swift in Sources */,
|
6FB1BDCB21D50F1700A991BF /* Curve25519.swift in Sources */,
|
||||||
|
6F9B582921E8D6D100544D02 /* PopupRow.swift in Sources */,
|
||||||
6FB1BDBB21D50F0200A991BF /* Localizable.strings in Sources */,
|
6FB1BDBB21D50F0200A991BF /* Localizable.strings in Sources */,
|
||||||
6FB1BDBC21D50F0200A991BF /* ringlogger.c in Sources */,
|
6FB1BDBC21D50F0200A991BF /* ringlogger.c in Sources */,
|
||||||
6FB1BDBD21D50F0200A991BF /* ringlogger.h in Sources */,
|
6FB1BDBD21D50F0200A991BF /* ringlogger.h in Sources */,
|
||||||
|
@ -255,6 +255,7 @@
|
|||||||
// Mac detail/edit view fields
|
// Mac detail/edit view fields
|
||||||
|
|
||||||
"macFieldKey (%@)" = "%@:";
|
"macFieldKey (%@)" = "%@:";
|
||||||
|
"macFieldOnDemand" = "On-Demand:";
|
||||||
|
|
||||||
// Mac status display
|
// Mac status display
|
||||||
|
|
||||||
|
75
WireGuard/WireGuard/UI/macOS/View/PopupRow.swift
Normal file
75
WireGuard/WireGuard/UI/macOS/View/PopupRow.swift
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright © 2018 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class PopupRow: NSView {
|
||||||
|
let keyLabel: NSTextField = {
|
||||||
|
let keyLabel = NSTextField()
|
||||||
|
keyLabel.isEditable = false
|
||||||
|
keyLabel.isSelectable = false
|
||||||
|
keyLabel.isBordered = false
|
||||||
|
keyLabel.alignment = .right
|
||||||
|
keyLabel.maximumNumberOfLines = 1
|
||||||
|
keyLabel.lineBreakMode = .byTruncatingTail
|
||||||
|
keyLabel.backgroundColor = .clear
|
||||||
|
return keyLabel
|
||||||
|
}()
|
||||||
|
|
||||||
|
let valuePopup = NSPopUpButton()
|
||||||
|
|
||||||
|
var key: String {
|
||||||
|
get { return keyLabel.stringValue }
|
||||||
|
set(value) { keyLabel.stringValue = value }
|
||||||
|
}
|
||||||
|
|
||||||
|
var valueOptions: [String] {
|
||||||
|
get { return valuePopup.itemTitles }
|
||||||
|
set(value) {
|
||||||
|
valuePopup.removeAllItems()
|
||||||
|
valuePopup.addItems(withTitles: value)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var selectedOptionIndex: Int {
|
||||||
|
get { return valuePopup.indexOfSelectedItem }
|
||||||
|
set(value) { valuePopup.selectItem(at: value) }
|
||||||
|
}
|
||||||
|
|
||||||
|
override var intrinsicContentSize: NSSize {
|
||||||
|
let height = max(keyLabel.intrinsicContentSize.height, valuePopup.intrinsicContentSize.height)
|
||||||
|
return NSSize(width: NSView.noIntrinsicMetric, height: height)
|
||||||
|
}
|
||||||
|
|
||||||
|
init() {
|
||||||
|
super.init(frame: CGRect.zero)
|
||||||
|
|
||||||
|
addSubview(keyLabel)
|
||||||
|
addSubview(valuePopup)
|
||||||
|
keyLabel.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
valuePopup.translatesAutoresizingMaskIntoConstraints = false
|
||||||
|
|
||||||
|
NSLayoutConstraint.activate([
|
||||||
|
keyLabel.centerYAnchor.constraint(equalTo: self.centerYAnchor),
|
||||||
|
keyLabel.firstBaselineAnchor.constraint(equalTo: valuePopup.firstBaselineAnchor),
|
||||||
|
self.leadingAnchor.constraint(equalTo: keyLabel.leadingAnchor),
|
||||||
|
keyLabel.trailingAnchor.constraint(equalTo: valuePopup.leadingAnchor, constant: -5)
|
||||||
|
])
|
||||||
|
|
||||||
|
keyLabel.setContentCompressionResistancePriority(.defaultHigh + 2, for: .horizontal)
|
||||||
|
keyLabel.setContentHuggingPriority(.defaultHigh, for: .horizontal)
|
||||||
|
|
||||||
|
let widthConstraint = keyLabel.widthAnchor.constraint(equalToConstant: 150)
|
||||||
|
widthConstraint.priority = .defaultHigh + 1
|
||||||
|
widthConstraint.isActive = true
|
||||||
|
}
|
||||||
|
|
||||||
|
required init?(coder decoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
override func prepareForReuse() {
|
||||||
|
key = ""
|
||||||
|
valueOptions = []
|
||||||
|
}
|
||||||
|
}
|
@ -8,12 +8,14 @@ class TunnelDetailTableViewController: NSViewController {
|
|||||||
private enum TableViewModelRow {
|
private enum TableViewModelRow {
|
||||||
case interfaceFieldRow(TunnelViewModel.InterfaceField)
|
case interfaceFieldRow(TunnelViewModel.InterfaceField)
|
||||||
case peerFieldRow(peer: TunnelViewModel.PeerData, field: TunnelViewModel.PeerField)
|
case peerFieldRow(peer: TunnelViewModel.PeerData, field: TunnelViewModel.PeerField)
|
||||||
|
case onDemandRow
|
||||||
case spacerRow
|
case spacerRow
|
||||||
|
|
||||||
func localizedSectionKeyString() -> String {
|
func localizedSectionKeyString() -> String {
|
||||||
switch self {
|
switch self {
|
||||||
case .interfaceFieldRow: return tr("tunnelSectionTitleInterface")
|
case .interfaceFieldRow: return tr("tunnelSectionTitleInterface")
|
||||||
case .peerFieldRow: return tr("tunnelSectionTitlePeer")
|
case .peerFieldRow: return tr("tunnelSectionTitlePeer")
|
||||||
|
case .onDemandRow: return ""
|
||||||
case .spacerRow: return ""
|
case .spacerRow: return ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -22,6 +24,7 @@ class TunnelDetailTableViewController: NSViewController {
|
|||||||
switch self {
|
switch self {
|
||||||
case .interfaceFieldRow(let field): return field == .name
|
case .interfaceFieldRow(let field): return field == .name
|
||||||
case .peerFieldRow(_, let field): return field == .publicKey
|
case .peerFieldRow(_, let field): return field == .publicKey
|
||||||
|
case .onDemandRow: return true
|
||||||
case .spacerRow: return false
|
case .spacerRow: return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -163,6 +166,8 @@ class TunnelDetailTableViewController: NSViewController {
|
|||||||
tableViewModelRows.append(.peerFieldRow(peer: peerData, field: field))
|
tableViewModelRows.append(.peerFieldRow(peer: peerData, field: field))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
tableViewModelRows.append(.spacerRow)
|
||||||
|
tableViewModelRows.append(.onDemandRow)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateStatus() {
|
func updateStatus() {
|
||||||
@ -232,6 +237,12 @@ extension TunnelDetailTableViewController: NSTableViewDelegate {
|
|||||||
return cell
|
return cell
|
||||||
case .spacerRow:
|
case .spacerRow:
|
||||||
return NSView()
|
return NSView()
|
||||||
|
case .onDemandRow:
|
||||||
|
let cell: KeyValueRow = tableView.dequeueReusableCell()
|
||||||
|
cell.key = tr("macFieldOnDemand")
|
||||||
|
cell.value = TunnelViewModel.activateOnDemandDetailText(for: tunnel.activateOnDemandSetting)
|
||||||
|
cell.isKeyInBold = true
|
||||||
|
return cell
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -41,6 +41,12 @@ class TunnelEditViewController: NSViewController {
|
|||||||
return textView
|
return textView
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
let onDemandRow: PopupRow = {
|
||||||
|
let popupRow = PopupRow()
|
||||||
|
popupRow.key = tr("macFieldOnDemand")
|
||||||
|
return popupRow
|
||||||
|
}()
|
||||||
|
|
||||||
let scrollView: NSScrollView = {
|
let scrollView: NSScrollView = {
|
||||||
let scrollView = NSScrollView()
|
let scrollView = NSScrollView()
|
||||||
scrollView.hasVerticalScroller = true
|
scrollView.hasVerticalScroller = true
|
||||||
@ -64,6 +70,13 @@ class TunnelEditViewController: NSViewController {
|
|||||||
return button
|
return button
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
let activateOnDemandOptions: [ActivateOnDemandOption] = [
|
||||||
|
.none,
|
||||||
|
.useOnDemandOverWiFiOrEthernet,
|
||||||
|
.useOnDemandOverWiFiOnly,
|
||||||
|
.useOnDemandOverEthernetOnly
|
||||||
|
]
|
||||||
|
|
||||||
let tunnelsManager: TunnelsManager
|
let tunnelsManager: TunnelsManager
|
||||||
let tunnel: TunnelContainer?
|
let tunnel: TunnelContainer?
|
||||||
|
|
||||||
@ -82,6 +95,7 @@ class TunnelEditViewController: NSViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func populateTextFields() {
|
func populateTextFields() {
|
||||||
|
let selectedActivateOnDemandOption: ActivateOnDemandOption
|
||||||
if let tunnel = tunnel {
|
if let tunnel = tunnel {
|
||||||
// Editing an existing tunnel
|
// Editing an existing tunnel
|
||||||
let tunnelConfiguration = tunnel.tunnelConfiguration!
|
let tunnelConfiguration = tunnel.tunnelConfiguration!
|
||||||
@ -99,6 +113,11 @@ class TunnelEditViewController: NSViewController {
|
|||||||
publicKeyRow?.value = ""
|
publicKeyRow?.value = ""
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if tunnel.activateOnDemandSetting.isActivateOnDemandEnabled {
|
||||||
|
selectedActivateOnDemandOption = tunnel.activateOnDemandSetting.activateOnDemandOption
|
||||||
|
} else {
|
||||||
|
selectedActivateOnDemandOption = .none
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// Creating a new tunnel
|
// Creating a new tunnel
|
||||||
let privateKey = Curve25519.generatePrivateKey()
|
let privateKey = Curve25519.generatePrivateKey()
|
||||||
@ -109,7 +128,13 @@ class TunnelEditViewController: NSViewController {
|
|||||||
"""
|
"""
|
||||||
publicKeyRow.value = publicKey.base64EncodedString()
|
publicKeyRow.value = publicKey.base64EncodedString()
|
||||||
textView.string = bootstrappingText
|
textView.string = bootstrappingText
|
||||||
|
selectedActivateOnDemandOption = .none
|
||||||
}
|
}
|
||||||
|
|
||||||
|
onDemandRow.valueOptions = activateOnDemandOptions.map {
|
||||||
|
return TunnelViewModel.activateOnDemandOptionText(for: $0)
|
||||||
|
}
|
||||||
|
onDemandRow.selectedOptionIndex = activateOnDemandOptions.firstIndex(of: selectedActivateOnDemandOption)!
|
||||||
}
|
}
|
||||||
|
|
||||||
override func loadView() {
|
override func loadView() {
|
||||||
@ -126,7 +151,7 @@ class TunnelEditViewController: NSViewController {
|
|||||||
let margin: CGFloat = 20
|
let margin: CGFloat = 20
|
||||||
let internalSpacing: CGFloat = 10
|
let internalSpacing: CGFloat = 10
|
||||||
|
|
||||||
let editorStackView = NSStackView(views: [nameRow, publicKeyRow, scrollView])
|
let editorStackView = NSStackView(views: [nameRow, publicKeyRow, onDemandRow, scrollView])
|
||||||
editorStackView.orientation = .vertical
|
editorStackView.orientation = .vertical
|
||||||
editorStackView.setHuggingPriority(.defaultHigh, for: .horizontal)
|
editorStackView.setHuggingPriority(.defaultHigh, for: .horizontal)
|
||||||
editorStackView.spacing = internalSpacing
|
editorStackView.spacing = internalSpacing
|
||||||
@ -157,6 +182,13 @@ class TunnelEditViewController: NSViewController {
|
|||||||
ErrorPresenter.showErrorAlert(title: tr("macAlertNameIsEmpty"), message: "", from: self)
|
ErrorPresenter.showErrorAlert(title: tr("macAlertNameIsEmpty"), message: "", from: self)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
let onDemandSetting: ActivateOnDemandSetting
|
||||||
|
let onDemandOption = activateOnDemandOptions[onDemandRow.selectedOptionIndex]
|
||||||
|
if onDemandOption == .none {
|
||||||
|
onDemandSetting = ActivateOnDemandSetting.defaultSetting
|
||||||
|
} else {
|
||||||
|
onDemandSetting = ActivateOnDemandSetting(isActivateOnDemandEnabled: true, activateOnDemandOption: onDemandOption)
|
||||||
|
}
|
||||||
if let tunnel = tunnel {
|
if let tunnel = tunnel {
|
||||||
// We're modifying an existing tunnel
|
// We're modifying an existing tunnel
|
||||||
if name != tunnel.name && tunnelsManager.tunnel(named: name) != nil {
|
if name != tunnel.name && tunnelsManager.tunnel(named: name) != nil {
|
||||||
@ -165,7 +197,6 @@ class TunnelEditViewController: NSViewController {
|
|||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
let tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: textView.string, called: nameRow.value)
|
let tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: textView.string, called: nameRow.value)
|
||||||
let onDemandSetting = ActivateOnDemandSetting.defaultSetting
|
|
||||||
tunnelsManager.modify(tunnel: tunnel, tunnelConfiguration: tunnelConfiguration, activateOnDemandSetting: onDemandSetting) { [weak self] error in
|
tunnelsManager.modify(tunnel: tunnel, tunnelConfiguration: tunnelConfiguration, activateOnDemandSetting: onDemandSetting) { [weak self] error in
|
||||||
if let error = error {
|
if let error = error {
|
||||||
ErrorPresenter.showErrorAlert(error: error, from: self)
|
ErrorPresenter.showErrorAlert(error: error, from: self)
|
||||||
@ -187,7 +218,6 @@ class TunnelEditViewController: NSViewController {
|
|||||||
}
|
}
|
||||||
do {
|
do {
|
||||||
let tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: textView.string, called: nameRow.value)
|
let tunnelConfiguration = try TunnelConfiguration(fromWgQuickConfig: textView.string, called: nameRow.value)
|
||||||
let onDemandSetting = ActivateOnDemandSetting.defaultSetting
|
|
||||||
tunnelsManager.add(tunnelConfiguration: tunnelConfiguration, activateOnDemandSetting: onDemandSetting) { [weak self] result in
|
tunnelsManager.add(tunnelConfiguration: tunnelConfiguration, activateOnDemandSetting: onDemandSetting) { [weak self] result in
|
||||||
if let error = result.error {
|
if let error = result.error {
|
||||||
ErrorPresenter.showErrorAlert(error: error, from: self)
|
ErrorPresenter.showErrorAlert(error: error, from: self)
|
||||||
|
Loading…
Reference in New Issue
Block a user