Compare commits
61 Commits
1.0.12-22
...
am/develop
| Author | SHA1 | Date | |
|---|---|---|---|
| b7f344968d | |||
| 399013415d | |||
| 7dea71898d | |||
| 23618f994f | |||
| ba644415c7 | |||
| 10da5cfdef | |||
| 75b6925deb | |||
| 03a59ff38e | |||
| abf506c1fe | |||
| dfb685f258 | |||
| f3798d0e11 | |||
| 86afd1a46a | |||
| 7171df84fa | |||
| d882a486a9 | |||
| adcbd17ebe | |||
| 3d8de22b96 | |||
| ba4d1e7b21 | |||
| f5a14b8434 | |||
| b74eb7239a | |||
| a8226b35d2 | |||
| 73c708d902 | |||
| 3668f3af9f | |||
| 54697a3240 | |||
| 3428bfbc9e | |||
| cfd1b16801 | |||
| ca70fe9ddc | |||
| 55c587b443 | |||
| b6831c1aca | |||
| 2ac17da7cb | |||
| 274c4cd092 | |||
| 95e1409bfb | |||
| 2c2c53b1f8 | |||
| 9cbfec99df | |||
| 1bd6dcb7e7 | |||
| c1fe8b0162 | |||
| 64c2fb337d | |||
| 147ac02f0d | |||
| 03ef79c0fd | |||
| a261d84fc6 | |||
| abaf1f1454 | |||
| 1e9e21bacf | |||
| ac9f7b9f5e | |||
| a115dd3bd9 | |||
| df9934a4b8 | |||
| 40f18de4d2 | |||
| 13b720442d | |||
| c1f509d65b | |||
| 87f0526f09 | |||
| 060c027325 | |||
| 23bf3cfccb | |||
| 7f5ad3e503 | |||
| 820fa55380 | |||
| eb528c766b | |||
| 53235eb38f | |||
| b9ff5c2e94 | |||
| b7f69d20b6 | |||
| 6c4f4109eb | |||
| 7b5b564a6e | |||
| 695f868b1f | |||
| e724c043d9 | |||
| 491301f58b |
@@ -1,4 +1,4 @@
|
|||||||
Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||||
this software and associated documentation files (the "Software"), to deal in
|
this software and associated documentation files (the "Software"), to deal in
|
||||||
|
|||||||
+1
-1
@@ -30,7 +30,7 @@ let package = Package(
|
|||||||
"goruntime-boottime-over-monotonic.diff",
|
"goruntime-boottime-over-monotonic.diff",
|
||||||
"go.mod",
|
"go.mod",
|
||||||
"go.sum",
|
"go.sum",
|
||||||
"api-ios.go",
|
"api-apple.go",
|
||||||
"Makefile"
|
"Makefile"
|
||||||
],
|
],
|
||||||
publicHeadersPath: ".",
|
publicHeadersPath: ".",
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ $ open WireGuard.xcodeproj
|
|||||||
the "External Build Tool Configuration":
|
the "External Build Tool Configuration":
|
||||||
|
|
||||||
```
|
```
|
||||||
$BUILD_DIR/../../SourcePackages/checkouts/wireguard-apple/Sources/WireGuardKitGo
|
${BUILD_DIR%Build/*}SourcePackages/checkouts/wireguard-apple/Sources/WireGuardKitGo
|
||||||
```
|
```
|
||||||
|
|
||||||
- Switch to "Build Settings" and find `SDKROOT`.
|
- Switch to "Build Settings" and find `SDKROOT`.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import os.log
|
import os.log
|
||||||
@@ -35,6 +35,10 @@ extension FileManager {
|
|||||||
return sharedFolderURL?.appendingPathComponent("last-error.txt")
|
return sharedFolderURL?.appendingPathComponent("last-error.txt")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static var loginHelperTimestampURL: URL? {
|
||||||
|
return sharedFolderURL?.appendingPathComponent("login-helper-timestamp.bin")
|
||||||
|
}
|
||||||
|
|
||||||
static func deleteFile(at url: URL) -> Bool {
|
static func deleteFile(at url: URL) -> Bool {
|
||||||
do {
|
do {
|
||||||
try FileManager.default.removeItem(at: url)
|
try FileManager.default.removeItem(at: url)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import Security
|
import Security
|
||||||
@@ -7,8 +7,7 @@ import Security
|
|||||||
class Keychain {
|
class Keychain {
|
||||||
static func openReference(called ref: Data) -> String? {
|
static func openReference(called ref: Data) -> String? {
|
||||||
var result: CFTypeRef?
|
var result: CFTypeRef?
|
||||||
let ret = SecItemCopyMatching([kSecClass: kSecClassGenericPassword,
|
let ret = SecItemCopyMatching([kSecValuePersistentRef: ref,
|
||||||
kSecValuePersistentRef: ref,
|
|
||||||
kSecReturnData: true] as CFDictionary,
|
kSecReturnData: true] as CFDictionary,
|
||||||
&result)
|
&result)
|
||||||
if ret != errSecSuccess || result == nil {
|
if ret != errSecSuccess || result == nil {
|
||||||
@@ -109,8 +108,7 @@ class Keychain {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static func verifyReference(called ref: Data) -> Bool {
|
static func verifyReference(called ref: Data) -> Bool {
|
||||||
return SecItemCopyMatching([kSecClass: kSecClassGenericPassword,
|
return SecItemCopyMatching([kSecValuePersistentRef: ref] as CFDictionary,
|
||||||
kSecValuePersistentRef: ref] as CFDictionary,
|
|
||||||
nil) != errSecItemNotFound
|
nil) != errSecItemNotFound
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import os.log
|
import os.log
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
* Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
/* SPDX-License-Identifier: MIT
|
/* SPDX-License-Identifier: MIT
|
||||||
*
|
*
|
||||||
* Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
* Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifndef RINGLOGGER_H
|
#ifndef RINGLOGGER_H
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import NetworkExtension
|
import NetworkExtension
|
||||||
|
|
||||||
@@ -72,7 +72,7 @@ extension NETunnelProviderProtocol {
|
|||||||
#error("Unimplemented")
|
#error("Unimplemented")
|
||||||
#endif
|
#endif
|
||||||
guard passwordReference == nil else { return true }
|
guard passwordReference == nil else { return true }
|
||||||
wg_log(.debug, message: "Migrating tunnel configuration '\(name)'")
|
wg_log(.info, message: "Migrating tunnel configuration '\(name)'")
|
||||||
passwordReference = Keychain.makeReference(containing: oldConfig, called: name)
|
passwordReference = Keychain.makeReference(containing: oldConfig, called: name)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -81,6 +81,27 @@ extension NETunnelProviderProtocol {
|
|||||||
providerConfiguration = ["UID": getuid()]
|
providerConfiguration = ["UID": getuid()]
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
#elseif os(iOS)
|
||||||
|
if #available(iOS 15, *) {
|
||||||
|
/* Update the stored reference from the old iOS 14 one to the canonical iOS 15 one.
|
||||||
|
* The iOS 14 ones are 96 bits, while the iOS 15 ones are 160 bits. We do this so
|
||||||
|
* that we can have fast set exclusion in deleteReferences safely. */
|
||||||
|
if passwordReference != nil && passwordReference!.count == 12 {
|
||||||
|
var result: CFTypeRef?
|
||||||
|
let ret = SecItemCopyMatching([kSecValuePersistentRef: passwordReference!,
|
||||||
|
kSecReturnPersistentRef: true] as CFDictionary,
|
||||||
|
&result)
|
||||||
|
if ret != errSecSuccess || result == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
guard let newReference = result as? Data else { return false }
|
||||||
|
if !newReference.elementsEqual(passwordReference!) {
|
||||||
|
wg_log(.info, message: "Migrating iOS 14-style keychain reference to iOS 15-style keychain reference for '\(name)'")
|
||||||
|
passwordReference = newReference
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
// iOS permission prompts
|
// iOS permission prompts
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
// Generic alert action names
|
// Generic alert action names
|
||||||
|
|
||||||
@@ -17,6 +17,7 @@
|
|||||||
"tunnelsListSelectAllButtonTitle" = "Select All";
|
"tunnelsListSelectAllButtonTitle" = "Select All";
|
||||||
"tunnelsListDeleteButtonTitle" = "Delete";
|
"tunnelsListDeleteButtonTitle" = "Delete";
|
||||||
"tunnelsListSelectedTitle (%d)" = "%d selected";
|
"tunnelsListSelectedTitle (%d)" = "%d selected";
|
||||||
|
"tunnelListCaptionOnDemand" = "On-Demand";
|
||||||
|
|
||||||
// Tunnels list menu
|
// Tunnels list menu
|
||||||
|
|
||||||
@@ -55,6 +56,11 @@
|
|||||||
"tunnelStatusRestarting" = "Restarting";
|
"tunnelStatusRestarting" = "Restarting";
|
||||||
"tunnelStatusWaiting" = "Waiting";
|
"tunnelStatusWaiting" = "Waiting";
|
||||||
|
|
||||||
|
"tunnelStatusAddendumOnDemand" = " (On-Demand)";
|
||||||
|
"tunnelStatusOnDemandDisabled" = "On-Demand Disabled";
|
||||||
|
"tunnelStatusAddendumOnDemandEnabled" = ", On-Demand Enabled";
|
||||||
|
"tunnelStatusAddendumOnDemandDisabled" = ", On-Demand Disabled";
|
||||||
|
|
||||||
"macToggleStatusButtonActivate" = "Activate";
|
"macToggleStatusButtonActivate" = "Activate";
|
||||||
"macToggleStatusButtonActivating" = "Activating…";
|
"macToggleStatusButtonActivating" = "Activating…";
|
||||||
"macToggleStatusButtonDeactivate" = "Deactivate";
|
"macToggleStatusButtonDeactivate" = "Deactivate";
|
||||||
@@ -62,6 +68,9 @@
|
|||||||
"macToggleStatusButtonReasserting" = "Reactivating…";
|
"macToggleStatusButtonReasserting" = "Reactivating…";
|
||||||
"macToggleStatusButtonRestarting" = "Restarting…";
|
"macToggleStatusButtonRestarting" = "Restarting…";
|
||||||
"macToggleStatusButtonWaiting" = "Waiting…";
|
"macToggleStatusButtonWaiting" = "Waiting…";
|
||||||
|
"macToggleStatusButtonEnableOnDemand" = "Enable On-Demand";
|
||||||
|
"macToggleStatusButtonDisableOnDemand" = "Disable On-Demand";
|
||||||
|
"macToggleStatusButtonDisableOnDemandDeactivate" = "Disable On-Demand and Deactivate";
|
||||||
|
|
||||||
"tunnelSectionTitleInterface" = "Interface";
|
"tunnelSectionTitleInterface" = "Interface";
|
||||||
|
|
||||||
@@ -109,8 +118,9 @@
|
|||||||
"tunnelOnDemandSectionTitleAddSSIDs" = "Add SSIDs";
|
"tunnelOnDemandSectionTitleAddSSIDs" = "Add SSIDs";
|
||||||
"tunnelOnDemandAddMessageAddConnectedSSID (%@)" = "Add connected: %@";
|
"tunnelOnDemandAddMessageAddConnectedSSID (%@)" = "Add connected: %@";
|
||||||
"tunnelOnDemandAddMessageAddNewSSID" = "Add new";
|
"tunnelOnDemandAddMessageAddNewSSID" = "Add new";
|
||||||
|
"tunnelOnDemandSSIDTextFieldPlaceholder" = "SSID";
|
||||||
|
|
||||||
"tunnelOnDemandKey" = "On demand";
|
"tunnelOnDemandKey" = "On-demand";
|
||||||
"tunnelOnDemandOptionOff" = "Off";
|
"tunnelOnDemandOptionOff" = "Off";
|
||||||
"tunnelOnDemandOptionWiFiOnly" = "Wi-Fi only";
|
"tunnelOnDemandOptionWiFiOnly" = "Wi-Fi only";
|
||||||
"tunnelOnDemandOptionWiFiOrCellular" = "Wi-Fi or cellular";
|
"tunnelOnDemandOptionWiFiOrCellular" = "Wi-Fi or cellular";
|
||||||
@@ -255,8 +265,6 @@
|
|||||||
"alertTunnelActivationFileDescriptorFailureMessage" = "Unable to determine TUN device file descriptor.";
|
"alertTunnelActivationFileDescriptorFailureMessage" = "Unable to determine TUN device file descriptor.";
|
||||||
"alertTunnelActivationSetNetworkSettingsMessage" = "Unable to apply network settings to tunnel object.";
|
"alertTunnelActivationSetNetworkSettingsMessage" = "Unable to apply network settings to tunnel object.";
|
||||||
|
|
||||||
"alertTunnelActivationFailureOnDemandAddendum" = " This tunnel has Activate On Demand enabled, so this tunnel might be re-activated automatically by the OS. You may turn off Activate On Demand in this app by editing the tunnel configuration.";
|
|
||||||
|
|
||||||
"alertTunnelDNSFailureTitle" = "DNS resolution failure";
|
"alertTunnelDNSFailureTitle" = "DNS resolution failure";
|
||||||
"alertTunnelDNSFailureMessage" = "One or more endpoint domains could not be resolved.";
|
"alertTunnelDNSFailureMessage" = "One or more endpoint domains could not be resolved.";
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
VERSION_NAME = 1.0.12
|
VERSION_NAME = 1.0.15
|
||||||
VERSION_ID = 22
|
VERSION_ID = 26
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import NetworkExtension
|
import NetworkExtension
|
||||||
|
|
||||||
@@ -42,11 +42,11 @@ extension ActivateOnDemandOption {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
tunnelProviderManager.onDemandRules = rules
|
tunnelProviderManager.onDemandRules = rules
|
||||||
tunnelProviderManager.isOnDemandEnabled = self != .off
|
tunnelProviderManager.isOnDemandEnabled = (rules != nil) && tunnelProviderManager.isOnDemandEnabled
|
||||||
}
|
}
|
||||||
|
|
||||||
init(from tunnelProviderManager: NETunnelProviderManager) {
|
init(from tunnelProviderManager: NETunnelProviderManager) {
|
||||||
if tunnelProviderManager.isOnDemandEnabled, let onDemandRules = tunnelProviderManager.onDemandRules {
|
if let onDemandRules = tunnelProviderManager.onDemandRules {
|
||||||
self = ActivateOnDemandOption.create(from: onDemandRules)
|
self = ActivateOnDemandOption.create(from: onDemandRules)
|
||||||
} else {
|
} else {
|
||||||
self = .off
|
self = .off
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import NetworkExtension
|
import NetworkExtension
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import NetworkExtension
|
import NetworkExtension
|
||||||
|
|
||||||
@@ -56,10 +56,10 @@ enum TunnelsManagerActivationError: WireGuardAppError {
|
|||||||
|
|
||||||
var alertText: AlertText {
|
var alertText: AlertText {
|
||||||
switch self {
|
switch self {
|
||||||
case .activationFailed(let wasOnDemandEnabled):
|
case .activationFailed:
|
||||||
return (tr("alertTunnelActivationFailureTitle"), tr("alertTunnelActivationFailureMessage") + (wasOnDemandEnabled ? tr("alertTunnelActivationFailureOnDemandAddendum") : ""))
|
return (tr("alertTunnelActivationFailureTitle"), tr("alertTunnelActivationFailureMessage"))
|
||||||
case .activationFailedWithExtensionError(let title, let message, let wasOnDemandEnabled):
|
case .activationFailedWithExtensionError(let title, let message, _):
|
||||||
return (title, message + (wasOnDemandEnabled ? tr("alertTunnelActivationFailureOnDemandAddendum") : ""))
|
return (title, message)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import NetworkExtension
|
import NetworkExtension
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import NetworkExtension
|
import NetworkExtension
|
||||||
import os.log
|
import os.log
|
||||||
|
|
||||||
protocol TunnelsManagerListDelegate: class {
|
protocol TunnelsManagerListDelegate: AnyObject {
|
||||||
func tunnelAdded(at index: Int)
|
func tunnelAdded(at index: Int)
|
||||||
func tunnelModified(at index: Int)
|
func tunnelModified(at index: Int)
|
||||||
func tunnelMoved(from oldIndex: Int, to newIndex: Int)
|
func tunnelMoved(from oldIndex: Int, to newIndex: Int)
|
||||||
func tunnelRemoved(at index: Int, tunnel: TunnelContainer)
|
func tunnelRemoved(at index: Int, tunnel: TunnelContainer)
|
||||||
}
|
}
|
||||||
|
|
||||||
protocol TunnelsManagerActivationDelegate: class {
|
protocol TunnelsManagerActivationDelegate: AnyObject {
|
||||||
func tunnelActivationAttemptFailed(tunnel: TunnelContainer, error: TunnelsManagerActivationAttemptError) // startTunnel wasn't called or failed
|
func tunnelActivationAttemptFailed(tunnel: TunnelContainer, error: TunnelsManagerActivationAttemptError) // startTunnel wasn't called or failed
|
||||||
func tunnelActivationAttemptSucceeded(tunnel: TunnelContainer) // startTunnel succeeded
|
func tunnelActivationAttemptSucceeded(tunnel: TunnelContainer) // startTunnel succeeded
|
||||||
func tunnelActivationFailed(tunnel: TunnelContainer, error: TunnelsManagerActivationError) // status didn't change to connected
|
func tunnelActivationFailed(tunnel: TunnelContainer, error: TunnelsManagerActivationError) // status didn't change to connected
|
||||||
@@ -206,7 +206,10 @@ class TunnelsManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func modify(tunnel: TunnelContainer, tunnelConfiguration: TunnelConfiguration, onDemandOption: ActivateOnDemandOption, completionHandler: @escaping (TunnelsManagerError?) -> Void) {
|
func modify(tunnel: TunnelContainer, tunnelConfiguration: TunnelConfiguration,
|
||||||
|
onDemandOption: ActivateOnDemandOption,
|
||||||
|
shouldEnsureOnDemandEnabled: Bool = false,
|
||||||
|
completionHandler: @escaping (TunnelsManagerError?) -> Void) {
|
||||||
let tunnelName = tunnelConfiguration.name ?? ""
|
let tunnelName = tunnelConfiguration.name ?? ""
|
||||||
if tunnelName.isEmpty {
|
if tunnelName.isEmpty {
|
||||||
completionHandler(TunnelsManagerError.tunnelNameEmpty)
|
completionHandler(TunnelsManagerError.tunnelNameEmpty)
|
||||||
@@ -214,6 +217,20 @@ class TunnelsManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let tunnelProviderManager = tunnel.tunnelProvider
|
let tunnelProviderManager = tunnel.tunnelProvider
|
||||||
|
|
||||||
|
let isIntroducingOnDemandRules = (tunnelProviderManager.onDemandRules ?? []).isEmpty && onDemandOption != .off
|
||||||
|
if isIntroducingOnDemandRules && tunnel.status != .inactive && tunnel.status != .deactivating {
|
||||||
|
tunnel.onDeactivated = { [weak self] in
|
||||||
|
self?.modify(tunnel: tunnel, tunnelConfiguration: tunnelConfiguration,
|
||||||
|
onDemandOption: onDemandOption, shouldEnsureOnDemandEnabled: true,
|
||||||
|
completionHandler: completionHandler)
|
||||||
|
}
|
||||||
|
self.startDeactivation(of: tunnel)
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
tunnel.onDeactivated = nil
|
||||||
|
}
|
||||||
|
|
||||||
let oldName = tunnelProviderManager.localizedDescription ?? ""
|
let oldName = tunnelProviderManager.localizedDescription ?? ""
|
||||||
let isNameChanged = tunnelName != oldName
|
let isNameChanged = tunnelName != oldName
|
||||||
if isNameChanged {
|
if isNameChanged {
|
||||||
@@ -231,12 +248,15 @@ class TunnelsManager {
|
|||||||
}
|
}
|
||||||
tunnelProviderManager.isEnabled = true
|
tunnelProviderManager.isEnabled = true
|
||||||
|
|
||||||
let isActivatingOnDemand = !tunnelProviderManager.isOnDemandEnabled && onDemandOption != .off
|
let isActivatingOnDemand = !tunnelProviderManager.isOnDemandEnabled && shouldEnsureOnDemandEnabled
|
||||||
onDemandOption.apply(on: tunnelProviderManager)
|
onDemandOption.apply(on: tunnelProviderManager)
|
||||||
|
if shouldEnsureOnDemandEnabled {
|
||||||
|
tunnelProviderManager.isOnDemandEnabled = true
|
||||||
|
}
|
||||||
|
|
||||||
tunnelProviderManager.saveToPreferences { [weak self] error in
|
tunnelProviderManager.saveToPreferences { [weak self] error in
|
||||||
if let error = error {
|
if let error = error {
|
||||||
//TODO: the passwordReference for the old one has already been removed at this point and we can't easily roll back!
|
// TODO: the passwordReference for the old one has already been removed at this point and we can't easily roll back!
|
||||||
wg_log(.error, message: "Modify: Saving configuration failed: \(error)")
|
wg_log(.error, message: "Modify: Saving configuration failed: \(error)")
|
||||||
completionHandler(TunnelsManagerError.systemErrorOnModifyTunnel(systemError: error))
|
completionHandler(TunnelsManagerError.systemErrorOnModifyTunnel(systemError: error))
|
||||||
return
|
return
|
||||||
@@ -342,6 +362,41 @@ class TunnelsManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func setOnDemandEnabled(_ isOnDemandEnabled: Bool, on tunnel: TunnelContainer, completionHandler: @escaping (TunnelsManagerError?) -> Void) {
|
||||||
|
let tunnelProviderManager = tunnel.tunnelProvider
|
||||||
|
let isCurrentlyEnabled = (tunnelProviderManager.isOnDemandEnabled && tunnelProviderManager.isEnabled)
|
||||||
|
guard isCurrentlyEnabled != isOnDemandEnabled else {
|
||||||
|
completionHandler(nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let isActivatingOnDemand = !tunnelProviderManager.isOnDemandEnabled && isOnDemandEnabled
|
||||||
|
tunnelProviderManager.isOnDemandEnabled = isOnDemandEnabled
|
||||||
|
tunnelProviderManager.isEnabled = true
|
||||||
|
tunnelProviderManager.saveToPreferences { error in
|
||||||
|
if let error = error {
|
||||||
|
wg_log(.error, message: "Modify On-Demand: Saving configuration failed: \(error)")
|
||||||
|
completionHandler(TunnelsManagerError.systemErrorOnModifyTunnel(systemError: error))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if isActivatingOnDemand {
|
||||||
|
// If we're enabling on-demand, we want to make sure the tunnel is enabled.
|
||||||
|
// If not enabled, the OS will not turn the tunnel on/off based on our rules.
|
||||||
|
tunnelProviderManager.loadFromPreferences { error in
|
||||||
|
// isActivateOnDemandEnabled will get changed in reload(), but no harm in setting it here too
|
||||||
|
tunnel.isActivateOnDemandEnabled = tunnelProviderManager.isOnDemandEnabled
|
||||||
|
if let error = error {
|
||||||
|
wg_log(.error, message: "Modify On-Demand: Re-loading after saving configuration failed: \(error)")
|
||||||
|
completionHandler(TunnelsManagerError.systemErrorOnModifyTunnel(systemError: error))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
completionHandler(nil)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
completionHandler(nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func numberOfTunnels() -> Int {
|
func numberOfTunnels() -> Int {
|
||||||
return tunnels.count
|
return tunnels.count
|
||||||
}
|
}
|
||||||
@@ -389,7 +444,17 @@ class TunnelsManager {
|
|||||||
tunnel.status = .waiting
|
tunnel.status = .waiting
|
||||||
activateWaitingTunnelOnDeactivation(of: tunnelInOperation)
|
activateWaitingTunnelOnDeactivation(of: tunnelInOperation)
|
||||||
if tunnelInOperation.status != .deactivating {
|
if tunnelInOperation.status != .deactivating {
|
||||||
startDeactivation(of: tunnelInOperation)
|
if tunnelInOperation.isActivateOnDemandEnabled {
|
||||||
|
setOnDemandEnabled(false, on: tunnelInOperation) { [weak self] error in
|
||||||
|
guard error == nil else {
|
||||||
|
wg_log(.error, message: "Unable to activate tunnel '\(tunnel.name)' because on-demand could not be disabled on active tunnel '\(tunnel.name)'")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self?.startDeactivation(of: tunnelInOperation)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
startDeactivation(of: tunnelInOperation)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -454,6 +519,11 @@ class TunnelsManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if session.status == .disconnected {
|
||||||
|
tunnel.onDeactivated?()
|
||||||
|
tunnel.onDeactivated = nil
|
||||||
|
}
|
||||||
|
|
||||||
if tunnel.status == .restarting && session.status == .disconnected {
|
if tunnel.status == .restarting && session.status == .disconnected {
|
||||||
tunnel.startActivation(activationDelegate: self.activationDelegate)
|
tunnel.startActivation(activationDelegate: self.activationDelegate)
|
||||||
return
|
return
|
||||||
@@ -498,6 +568,7 @@ class TunnelContainer: NSObject {
|
|||||||
@objc dynamic var status: TunnelStatus
|
@objc dynamic var status: TunnelStatus
|
||||||
|
|
||||||
@objc dynamic var isActivateOnDemandEnabled: Bool
|
@objc dynamic var isActivateOnDemandEnabled: Bool
|
||||||
|
@objc dynamic var hasOnDemandRules: Bool
|
||||||
|
|
||||||
var isAttemptingActivation = false {
|
var isAttemptingActivation = false {
|
||||||
didSet {
|
didSet {
|
||||||
@@ -523,8 +594,14 @@ class TunnelContainer: NSObject {
|
|||||||
var activationAttemptId: String?
|
var activationAttemptId: String?
|
||||||
var activationTimer: Timer?
|
var activationTimer: Timer?
|
||||||
var deactivationTimer: Timer?
|
var deactivationTimer: Timer?
|
||||||
|
var onDeactivated: (() -> Void)?
|
||||||
|
|
||||||
fileprivate var tunnelProvider: NETunnelProviderManager
|
fileprivate var tunnelProvider: NETunnelProviderManager {
|
||||||
|
didSet {
|
||||||
|
isActivateOnDemandEnabled = tunnelProvider.isOnDemandEnabled && tunnelProvider.isEnabled
|
||||||
|
hasOnDemandRules = !(tunnelProvider.onDemandRules ?? []).isEmpty
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var tunnelConfiguration: TunnelConfiguration? {
|
var tunnelConfiguration: TunnelConfiguration? {
|
||||||
return tunnelProvider.tunnelConfiguration
|
return tunnelProvider.tunnelConfiguration
|
||||||
@@ -544,7 +621,8 @@ class TunnelContainer: NSObject {
|
|||||||
name = tunnel.localizedDescription ?? "Unnamed"
|
name = tunnel.localizedDescription ?? "Unnamed"
|
||||||
let status = TunnelStatus(from: tunnel.connection.status)
|
let status = TunnelStatus(from: tunnel.connection.status)
|
||||||
self.status = status
|
self.status = status
|
||||||
isActivateOnDemandEnabled = tunnel.isOnDemandEnabled
|
isActivateOnDemandEnabled = tunnel.isOnDemandEnabled && tunnel.isEnabled
|
||||||
|
hasOnDemandRules = !(tunnel.onDemandRules ?? []).isEmpty
|
||||||
tunnelProvider = tunnel
|
tunnelProvider = tunnel
|
||||||
super.init()
|
super.init()
|
||||||
}
|
}
|
||||||
@@ -567,11 +645,10 @@ class TunnelContainer: NSObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func refreshStatus() {
|
func refreshStatus() {
|
||||||
if status == .restarting {
|
if (status == .restarting) || (status == .waiting && tunnelProvider.connection.status == .disconnected) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
status = TunnelStatus(from: tunnelProvider.connection.status)
|
status = TunnelStatus(from: tunnelProvider.connection.status)
|
||||||
isActivateOnDemandEnabled = tunnelProvider.isOnDemandEnabled
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fileprivate func startActivation(recursionCount: UInt = 0, lastError: Error? = nil, activationDelegate: TunnelsManagerActivationDelegate?) {
|
fileprivate func startActivation(recursionCount: UInt = 0, lastError: Error? = nil, activationDelegate: TunnelsManagerActivationDelegate?) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
@@ -51,20 +51,18 @@ class ActivateOnDemandViewModel {
|
|||||||
extension ActivateOnDemandViewModel {
|
extension ActivateOnDemandViewModel {
|
||||||
convenience init(tunnel: TunnelContainer) {
|
convenience init(tunnel: TunnelContainer) {
|
||||||
self.init()
|
self.init()
|
||||||
if tunnel.isActivateOnDemandEnabled {
|
switch tunnel.onDemandOption {
|
||||||
switch tunnel.onDemandOption {
|
case .off:
|
||||||
case .off:
|
break
|
||||||
break
|
case .wiFiInterfaceOnly(let onDemandSSIDOption):
|
||||||
case .wiFiInterfaceOnly(let onDemandSSIDOption):
|
isWiFiInterfaceEnabled = true
|
||||||
isWiFiInterfaceEnabled = true
|
(ssidOption, selectedSSIDs) = ssidViewModel(from: onDemandSSIDOption)
|
||||||
(ssidOption, selectedSSIDs) = ssidViewModel(from: onDemandSSIDOption)
|
case .nonWiFiInterfaceOnly:
|
||||||
case .nonWiFiInterfaceOnly:
|
isNonWiFiInterfaceEnabled = true
|
||||||
isNonWiFiInterfaceEnabled = true
|
case .anyInterface(let onDemandSSIDOption):
|
||||||
case .anyInterface(let onDemandSSIDOption):
|
isWiFiInterfaceEnabled = true
|
||||||
isWiFiInterfaceEnabled = true
|
isNonWiFiInterfaceEnabled = true
|
||||||
isNonWiFiInterfaceEnabled = true
|
(ssidOption, selectedSSIDs) = ssidViewModel(from: onDemandSSIDOption)
|
||||||
(ssidOption, selectedSSIDs) = ssidViewModel(from: onDemandSSIDOption)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
protocol ErrorPresenterProtocol {
|
protocol ErrorPresenterProtocol {
|
||||||
static func showErrorAlert(title: String, message: String, from sourceVC: AnyObject?, onPresented: (() -> Void)?, onDismissal: (() -> Void)?)
|
static func showErrorAlert(title: String, message: String, from sourceVC: AnyObject?, onPresented: (() -> Void)?, onDismissal: (() -> Void)?)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
import LocalAuthentication
|
import LocalAuthentication
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import os.log
|
import os.log
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import os.log
|
import os.log
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
@@ -15,7 +15,7 @@ extension UITableView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func dequeueReusableCell<T: UITableViewCell>(for indexPath: IndexPath) -> T {
|
func dequeueReusableCell<T: UITableViewCell>(for indexPath: IndexPath) -> T {
|
||||||
//swiftlint:disable:next force_cast
|
// swiftlint:disable:next force_cast
|
||||||
return dequeueReusableCell(withIdentifier: T.reuseIdentifier, for: indexPath) as! T
|
return dequeueReusableCell(withIdentifier: T.reuseIdentifier, for: indexPath) as! T
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
@@ -9,6 +9,11 @@ class EditableTextCell: UITableViewCell {
|
|||||||
set(value) { valueTextField.text = value }
|
set(value) { valueTextField.text = value }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var placeholder: String? {
|
||||||
|
get { return valueTextField.placeholder }
|
||||||
|
set(value) { valueTextField.placeholder = value }
|
||||||
|
}
|
||||||
|
|
||||||
let valueTextField: UITextField = {
|
let valueTextField: UITextField = {
|
||||||
let valueTextField = UITextField()
|
let valueTextField = UITextField()
|
||||||
valueTextField.textAlignment = .left
|
valueTextField.textAlignment = .left
|
||||||
@@ -29,12 +34,13 @@ class EditableTextCell: UITableViewCell {
|
|||||||
valueTextField.delegate = self
|
valueTextField.delegate = self
|
||||||
contentView.addSubview(valueTextField)
|
contentView.addSubview(valueTextField)
|
||||||
valueTextField.translatesAutoresizingMaskIntoConstraints = false
|
valueTextField.translatesAutoresizingMaskIntoConstraints = false
|
||||||
let bottomAnchorConstraint = contentView.layoutMarginsGuide.bottomAnchor.constraint(equalToSystemSpacingBelow: valueTextField.bottomAnchor, multiplier: 1)
|
// Reduce the bottom margin by 0.5pt to maintain the default cell height (44pt)
|
||||||
|
let bottomAnchorConstraint = contentView.layoutMarginsGuide.bottomAnchor.constraint(equalTo: valueTextField.bottomAnchor, constant: -0.5)
|
||||||
bottomAnchorConstraint.priority = .defaultLow
|
bottomAnchorConstraint.priority = .defaultLow
|
||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
valueTextField.leadingAnchor.constraint(equalToSystemSpacingAfter: contentView.layoutMarginsGuide.leadingAnchor, multiplier: 1),
|
valueTextField.leadingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.leadingAnchor),
|
||||||
contentView.layoutMarginsGuide.trailingAnchor.constraint(equalToSystemSpacingAfter: valueTextField.trailingAnchor, multiplier: 1),
|
contentView.layoutMarginsGuide.trailingAnchor.constraint(equalTo: valueTextField.trailingAnchor),
|
||||||
valueTextField.topAnchor.constraint(equalToSystemSpacingBelow: contentView.layoutMarginsGuide.topAnchor, multiplier: 1),
|
contentView.layoutMarginsGuide.topAnchor.constraint(equalTo: valueTextField.topAnchor),
|
||||||
bottomAnchorConstraint
|
bottomAnchorConstraint
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
@@ -50,6 +56,7 @@ class EditableTextCell: UITableViewCell {
|
|||||||
override func prepareForReuse() {
|
override func prepareForReuse() {
|
||||||
super.prepareForReuse()
|
super.prepareForReuse()
|
||||||
message = ""
|
message = ""
|
||||||
|
placeholder = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
@@ -27,7 +27,7 @@ class KeyValueCell: UITableViewCell {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
let valueTextField: UITextField = {
|
let valueTextField: UITextField = {
|
||||||
let valueTextField = UITextField()
|
let valueTextField = KeyValueCellTextField()
|
||||||
valueTextField.textAlignment = .right
|
valueTextField.textAlignment = .right
|
||||||
valueTextField.isEnabled = false
|
valueTextField.isEnabled = false
|
||||||
valueTextField.font = UIFont.preferredFont(forTextStyle: .body)
|
valueTextField.font = UIFont.preferredFont(forTextStyle: .body)
|
||||||
@@ -115,8 +115,6 @@ class KeyValueCell: UITableViewCell {
|
|||||||
expandToFitValueLabelConstraint.priority = .defaultLow + 1
|
expandToFitValueLabelConstraint.priority = .defaultLow + 1
|
||||||
expandToFitValueLabelConstraint.isActive = true
|
expandToFitValueLabelConstraint.isActive = true
|
||||||
|
|
||||||
contentView.addSubview(valueLabelScrollView)
|
|
||||||
|
|
||||||
contentView.addSubview(valueLabelScrollView)
|
contentView.addSubview(valueLabelScrollView)
|
||||||
valueLabelScrollView.translatesAutoresizingMaskIntoConstraints = false
|
valueLabelScrollView.translatesAutoresizingMaskIntoConstraints = false
|
||||||
NSLayoutConstraint.activate([
|
NSLayoutConstraint.activate([
|
||||||
@@ -234,3 +232,10 @@ extension KeyValueCell: UITextFieldDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class KeyValueCellTextField: UITextField {
|
||||||
|
override func placeholderRect(forBounds bounds: CGRect) -> CGRect {
|
||||||
|
// UIKit renders the placeholder label 0.5pt higher
|
||||||
|
return super.placeholderRect(forBounds: bounds).integral.offsetBy(dx: 0, dy: -0.5)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
@@ -26,7 +26,9 @@ class SwitchCell: UITableViewCell {
|
|||||||
|
|
||||||
var onSwitchToggled: ((Bool) -> Void)?
|
var onSwitchToggled: ((Bool) -> Void)?
|
||||||
|
|
||||||
var observationToken: AnyObject?
|
var statusObservationToken: AnyObject?
|
||||||
|
var isOnDemandEnabledObservationToken: AnyObject?
|
||||||
|
var hasOnDemandRulesObservationToken: AnyObject?
|
||||||
|
|
||||||
let switchView = UISwitch()
|
let switchView = UISwitch()
|
||||||
|
|
||||||
@@ -51,6 +53,8 @@ class SwitchCell: UITableViewCell {
|
|||||||
isEnabled = true
|
isEnabled = true
|
||||||
message = ""
|
message = ""
|
||||||
isOn = false
|
isOn = false
|
||||||
observationToken = nil
|
statusObservationToken = nil
|
||||||
|
isOnDemandEnabledObservationToken = nil
|
||||||
|
hasOnDemandRulesObservationToken = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
@@ -12,9 +12,16 @@ class TunnelListCell: UITableViewCell {
|
|||||||
self?.nameLabel.text = tunnel.name
|
self?.nameLabel.text = tunnel.name
|
||||||
}
|
}
|
||||||
// Bind to the tunnel's status
|
// Bind to the tunnel's status
|
||||||
update(from: tunnel?.status, animated: false)
|
update(from: tunnel, animated: false)
|
||||||
statusObservationToken = tunnel?.observe(\.status) { [weak self] tunnel, _ in
|
statusObservationToken = tunnel?.observe(\.status) { [weak self] tunnel, _ in
|
||||||
self?.update(from: tunnel.status, animated: true)
|
self?.update(from: tunnel, animated: true)
|
||||||
|
}
|
||||||
|
// Bind to tunnel's on-demand settings
|
||||||
|
isOnDemandEnabledObservationToken = tunnel?.observe(\.isActivateOnDemandEnabled) { [weak self] tunnel, _ in
|
||||||
|
self?.update(from: tunnel, animated: true)
|
||||||
|
}
|
||||||
|
hasOnDemandRulesObservationToken = tunnel?.observe(\.hasOnDemandRules) { [weak self] tunnel, _ in
|
||||||
|
self?.update(from: tunnel, animated: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -28,6 +35,20 @@ class TunnelListCell: UITableViewCell {
|
|||||||
return nameLabel
|
return nameLabel
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
let onDemandLabel: UILabel = {
|
||||||
|
let label = UILabel()
|
||||||
|
label.text = ""
|
||||||
|
label.font = UIFont.preferredFont(forTextStyle: .caption2)
|
||||||
|
label.adjustsFontForContentSizeCategory = true
|
||||||
|
label.numberOfLines = 1
|
||||||
|
if #available(iOS 13.0, *) {
|
||||||
|
label.textColor = .secondaryLabel
|
||||||
|
} else {
|
||||||
|
label.textColor = .gray
|
||||||
|
}
|
||||||
|
return label
|
||||||
|
}()
|
||||||
|
|
||||||
let busyIndicator: UIActivityIndicatorView = {
|
let busyIndicator: UIActivityIndicatorView = {
|
||||||
let busyIndicator: UIActivityIndicatorView
|
let busyIndicator: UIActivityIndicatorView
|
||||||
if #available(iOS 13.0, *) {
|
if #available(iOS 13.0, *) {
|
||||||
@@ -41,20 +62,26 @@ class TunnelListCell: UITableViewCell {
|
|||||||
|
|
||||||
let statusSwitch = UISwitch()
|
let statusSwitch = UISwitch()
|
||||||
|
|
||||||
private var statusObservationToken: NSKeyValueObservation?
|
|
||||||
private var nameObservationToken: NSKeyValueObservation?
|
private var nameObservationToken: NSKeyValueObservation?
|
||||||
|
private var statusObservationToken: NSKeyValueObservation?
|
||||||
|
private var isOnDemandEnabledObservationToken: NSKeyValueObservation?
|
||||||
|
private var hasOnDemandRulesObservationToken: NSKeyValueObservation?
|
||||||
|
|
||||||
|
private var subTitleLabelBottomConstraint: NSLayoutConstraint?
|
||||||
|
private var nameLabelBottomConstraint: NSLayoutConstraint?
|
||||||
|
|
||||||
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
|
||||||
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
super.init(style: style, reuseIdentifier: reuseIdentifier)
|
||||||
|
|
||||||
accessoryType = .disclosureIndicator
|
accessoryType = .disclosureIndicator
|
||||||
|
|
||||||
for subview in [statusSwitch, busyIndicator, nameLabel] {
|
for subview in [statusSwitch, busyIndicator, onDemandLabel, nameLabel] {
|
||||||
subview.translatesAutoresizingMaskIntoConstraints = false
|
subview.translatesAutoresizingMaskIntoConstraints = false
|
||||||
contentView.addSubview(subview)
|
contentView.addSubview(subview)
|
||||||
}
|
}
|
||||||
|
|
||||||
nameLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
nameLabel.setContentCompressionResistancePriority(.defaultLow, for: .horizontal)
|
||||||
|
onDemandLabel.setContentCompressionResistancePriority(.defaultHigh, for: .horizontal)
|
||||||
|
|
||||||
let nameLabelBottomConstraint =
|
let nameLabelBottomConstraint =
|
||||||
contentView.layoutMarginsGuide.bottomAnchor.constraint(equalToSystemSpacingBelow: nameLabel.bottomAnchor, multiplier: 1)
|
contentView.layoutMarginsGuide.bottomAnchor.constraint(equalToSystemSpacingBelow: nameLabel.bottomAnchor, multiplier: 1)
|
||||||
@@ -64,13 +91,18 @@ class TunnelListCell: UITableViewCell {
|
|||||||
statusSwitch.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
|
statusSwitch.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
|
||||||
statusSwitch.trailingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.trailingAnchor),
|
statusSwitch.trailingAnchor.constraint(equalTo: contentView.layoutMarginsGuide.trailingAnchor),
|
||||||
statusSwitch.leadingAnchor.constraint(equalToSystemSpacingAfter: busyIndicator.trailingAnchor, multiplier: 1),
|
statusSwitch.leadingAnchor.constraint(equalToSystemSpacingAfter: busyIndicator.trailingAnchor, multiplier: 1),
|
||||||
|
statusSwitch.leadingAnchor.constraint(equalToSystemSpacingAfter: onDemandLabel.trailingAnchor, multiplier: 1),
|
||||||
|
|
||||||
nameLabel.topAnchor.constraint(equalToSystemSpacingBelow: contentView.layoutMarginsGuide.topAnchor, multiplier: 1),
|
nameLabel.topAnchor.constraint(equalToSystemSpacingBelow: contentView.layoutMarginsGuide.topAnchor, multiplier: 1),
|
||||||
nameLabelBottomConstraint,
|
|
||||||
nameLabel.leadingAnchor.constraint(equalToSystemSpacingAfter: contentView.layoutMarginsGuide.leadingAnchor, multiplier: 1),
|
nameLabel.leadingAnchor.constraint(equalToSystemSpacingAfter: contentView.layoutMarginsGuide.leadingAnchor, multiplier: 1),
|
||||||
|
nameLabel.trailingAnchor.constraint(lessThanOrEqualTo: statusSwitch.leadingAnchor),
|
||||||
|
nameLabelBottomConstraint,
|
||||||
|
|
||||||
|
onDemandLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
|
||||||
|
onDemandLabel.leadingAnchor.constraint(equalToSystemSpacingAfter: nameLabel.trailingAnchor, multiplier: 1),
|
||||||
|
|
||||||
busyIndicator.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
|
busyIndicator.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
|
||||||
busyIndicator.leadingAnchor.constraint(equalToSystemSpacingAfter: nameLabel.trailingAnchor, multiplier: 1)
|
busyIndicator.leadingAnchor.constraint(greaterThanOrEqualToSystemSpacingAfter: nameLabel.trailingAnchor, multiplier: 1)
|
||||||
])
|
])
|
||||||
|
|
||||||
statusSwitch.addTarget(self, action: #selector(switchToggled), for: .valueChanged)
|
statusSwitch.addTarget(self, action: #selector(switchToggled), for: .valueChanged)
|
||||||
@@ -94,21 +126,43 @@ class TunnelListCell: UITableViewCell {
|
|||||||
onSwitchToggled?(statusSwitch.isOn)
|
onSwitchToggled?(statusSwitch.isOn)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func update(from status: TunnelStatus?, animated: Bool) {
|
private func update(from tunnel: TunnelContainer?, animated: Bool) {
|
||||||
guard let status = status else {
|
guard let tunnel = tunnel else {
|
||||||
reset(animated: animated)
|
reset(animated: animated)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
statusSwitch.setOn(!(status == .deactivating || status == .inactive), animated: animated)
|
let status = tunnel.status
|
||||||
statusSwitch.isUserInteractionEnabled = (status == .inactive || status == .active)
|
let isOnDemandEngaged = tunnel.isActivateOnDemandEnabled
|
||||||
if status == .inactive || status == .active {
|
|
||||||
busyIndicator.stopAnimating()
|
let shouldSwitchBeOn = ((status != .deactivating && status != .inactive) || isOnDemandEngaged)
|
||||||
|
statusSwitch.setOn(shouldSwitchBeOn, animated: true)
|
||||||
|
|
||||||
|
if isOnDemandEngaged && !(status == .activating || status == .active) {
|
||||||
|
statusSwitch.onTintColor = UIColor.systemYellow
|
||||||
} else {
|
} else {
|
||||||
busyIndicator.startAnimating()
|
statusSwitch.onTintColor = UIColor.systemGreen
|
||||||
}
|
}
|
||||||
|
|
||||||
|
statusSwitch.isUserInteractionEnabled = (status == .inactive || status == .active)
|
||||||
|
|
||||||
|
if tunnel.hasOnDemandRules {
|
||||||
|
onDemandLabel.text = isOnDemandEngaged ? tr("tunnelListCaptionOnDemand") : ""
|
||||||
|
busyIndicator.stopAnimating()
|
||||||
|
statusSwitch.isUserInteractionEnabled = true
|
||||||
|
} else {
|
||||||
|
onDemandLabel.text = ""
|
||||||
|
if status == .inactive || status == .active {
|
||||||
|
busyIndicator.stopAnimating()
|
||||||
|
} else {
|
||||||
|
busyIndicator.startAnimating()
|
||||||
|
}
|
||||||
|
statusSwitch.isUserInteractionEnabled = (status == .inactive || status == .active)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func reset(animated: Bool) {
|
private func reset(animated: Bool) {
|
||||||
|
statusSwitch.thumbTintColor = nil
|
||||||
statusSwitch.setOn(false, animated: animated)
|
statusSwitch.setOn(false, animated: animated)
|
||||||
statusSwitch.isUserInteractionEnabled = false
|
statusSwitch.isUserInteractionEnabled = false
|
||||||
busyIndicator.stopAnimating()
|
busyIndicator.stopAnimating()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import AVFoundation
|
import AVFoundation
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
protocol QRScanViewControllerDelegate: class {
|
protocol QRScanViewControllerDelegate: AnyObject {
|
||||||
func addScannedQRCode(tunnelConfiguration: TunnelConfiguration, qrScanViewController: QRScanViewController, completionHandler: (() -> Void)?)
|
func addScannedQRCode(tunnelConfiguration: TunnelConfiguration, qrScanViewController: QRScanViewController, completionHandler: (() -> Void)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
|
|||||||
+48
-25
@@ -1,10 +1,11 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import SystemConfiguration.CaptiveNetwork
|
import SystemConfiguration.CaptiveNetwork
|
||||||
|
import NetworkExtension
|
||||||
|
|
||||||
protocol SSIDOptionEditTableViewControllerDelegate: class {
|
protocol SSIDOptionEditTableViewControllerDelegate: AnyObject {
|
||||||
func ssidOptionSaved(option: ActivateOnDemandViewModel.OnDemandSSIDOption, ssids: [String])
|
func ssidOptionSaved(option: ActivateOnDemandViewModel.OnDemandSSIDOption, ssids: [String])
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -39,9 +40,16 @@ class SSIDOptionEditTableViewController: UITableViewController {
|
|||||||
selectedOption = option
|
selectedOption = option
|
||||||
selectedSSIDs = ssids
|
selectedSSIDs = ssids
|
||||||
super.init(style: .grouped)
|
super.init(style: .grouped)
|
||||||
connectedSSID = getConnectedSSID()
|
|
||||||
loadSections()
|
loadSections()
|
||||||
loadAddSSIDRows()
|
addSSIDRows.removeAll()
|
||||||
|
addSSIDRows.append(.addNewSSID)
|
||||||
|
|
||||||
|
getConnectedSSID { [weak self] ssid in
|
||||||
|
guard let self = self else { return }
|
||||||
|
self.connectedSSID = ssid
|
||||||
|
self.updateCurrentSSIDEntry()
|
||||||
|
self.updateTableViewAddSSIDRows()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
required init?(coder aDecoder: NSCoder) {
|
required init?(coder aDecoder: NSCoder) {
|
||||||
@@ -60,6 +68,7 @@ class SSIDOptionEditTableViewController: UITableViewController {
|
|||||||
tableView.register(TextCell.self)
|
tableView.register(TextCell.self)
|
||||||
tableView.isEditing = true
|
tableView.isEditing = true
|
||||||
tableView.allowsSelectionDuringEditing = true
|
tableView.allowsSelectionDuringEditing = true
|
||||||
|
tableView.keyboardDismissMode = .onDrag
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadSections() {
|
func loadSections() {
|
||||||
@@ -71,14 +80,14 @@ class SSIDOptionEditTableViewController: UITableViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func loadAddSSIDRows() {
|
func updateCurrentSSIDEntry() {
|
||||||
addSSIDRows.removeAll()
|
if let connectedSSID = connectedSSID, !selectedSSIDs.contains(connectedSSID) {
|
||||||
if let connectedSSID = connectedSSID {
|
if let first = addSSIDRows.first, case .addNewSSID = first {
|
||||||
if !selectedSSIDs.contains(connectedSSID) {
|
addSSIDRows.insert(.addConnectedSSID(connectedSSID: connectedSSID), at: 0)
|
||||||
addSSIDRows.append(.addConnectedSSID(connectedSSID: connectedSSID))
|
|
||||||
}
|
}
|
||||||
|
} else if let first = addSSIDRows.first, case .addConnectedSSID = first {
|
||||||
|
addSSIDRows.removeFirst()
|
||||||
}
|
}
|
||||||
addSSIDRows.append(.addNewSSID)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateTableViewAddSSIDRows() {
|
func updateTableViewAddSSIDRows() {
|
||||||
@@ -188,12 +197,13 @@ extension SSIDOptionEditTableViewController {
|
|||||||
private func selectedSSIDCell(for tableView: UITableView, at indexPath: IndexPath) -> UITableViewCell {
|
private func selectedSSIDCell(for tableView: UITableView, at indexPath: IndexPath) -> UITableViewCell {
|
||||||
let cell: EditableTextCell = tableView.dequeueReusableCell(for: indexPath)
|
let cell: EditableTextCell = tableView.dequeueReusableCell(for: indexPath)
|
||||||
cell.message = selectedSSIDs[indexPath.row]
|
cell.message = selectedSSIDs[indexPath.row]
|
||||||
|
cell.placeholder = tr("tunnelOnDemandSSIDTextFieldPlaceholder")
|
||||||
cell.isEditing = true
|
cell.isEditing = true
|
||||||
cell.onValueBeingEdited = { [weak self, weak cell] text in
|
cell.onValueBeingEdited = { [weak self, weak cell] text in
|
||||||
guard let self = self, let cell = cell else { return }
|
guard let self = self, let cell = cell else { return }
|
||||||
if let row = self.tableView.indexPath(for: cell)?.row {
|
if let row = self.tableView.indexPath(for: cell)?.row {
|
||||||
self.selectedSSIDs[row] = text
|
self.selectedSSIDs[row] = text
|
||||||
self.loadAddSSIDRows()
|
self.updateCurrentSSIDEntry()
|
||||||
self.updateTableViewAddSSIDRows()
|
self.updateTableViewAddSSIDRows()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -224,7 +234,7 @@ extension SSIDOptionEditTableViewController {
|
|||||||
} else {
|
} else {
|
||||||
tableView.reloadRows(at: [indexPath], with: .automatic)
|
tableView.reloadRows(at: [indexPath], with: .automatic)
|
||||||
}
|
}
|
||||||
loadAddSSIDRows()
|
updateCurrentSSIDEntry()
|
||||||
updateTableViewAddSSIDRows()
|
updateTableViewAddSSIDRows()
|
||||||
case .addSSIDs:
|
case .addSSIDs:
|
||||||
assert(editingStyle == .insert)
|
assert(editingStyle == .insert)
|
||||||
@@ -244,7 +254,7 @@ extension SSIDOptionEditTableViewController {
|
|||||||
} else {
|
} else {
|
||||||
tableView.insertRows(at: [indexPath], with: .automatic)
|
tableView.insertRows(at: [indexPath], with: .automatic)
|
||||||
}
|
}
|
||||||
loadAddSSIDRows()
|
updateCurrentSSIDEntry()
|
||||||
updateTableViewAddSSIDRows()
|
updateTableViewAddSSIDRows()
|
||||||
if newSSID.isEmpty {
|
if newSSID.isEmpty {
|
||||||
if let selectedSSIDCell = tableView.cellForRow(at: indexPath) as? EditableTextCell {
|
if let selectedSSIDCell = tableView.cellForRow(at: indexPath) as? EditableTextCell {
|
||||||
@@ -253,6 +263,31 @@ extension SSIDOptionEditTableViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func getConnectedSSID(completionHandler: @escaping (String?) -> Void) {
|
||||||
|
#if targetEnvironment(simulator)
|
||||||
|
completionHandler("Simulator Wi-Fi")
|
||||||
|
#else
|
||||||
|
if #available(iOS 14, *) {
|
||||||
|
NEHotspotNetwork.fetchCurrent { hotspotNetwork in
|
||||||
|
completionHandler(hotspotNetwork?.ssid)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if let supportedInterfaces = CNCopySupportedInterfaces() as? [CFString] {
|
||||||
|
for interface in supportedInterfaces {
|
||||||
|
if let networkInfo = CNCopyCurrentNetworkInfo(interface) {
|
||||||
|
if let ssid = (networkInfo as NSDictionary)[kCNNetworkInfoKeySSID as String] as? String {
|
||||||
|
completionHandler(!ssid.isEmpty ? ssid : nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
completionHandler(nil)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension SSIDOptionEditTableViewController {
|
extension SSIDOptionEditTableViewController {
|
||||||
@@ -289,15 +324,3 @@ extension SSIDOptionEditTableViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getConnectedSSID() -> String? {
|
|
||||||
guard let supportedInterfaces = CNCopySupportedInterfaces() as? [CFString] else { return nil }
|
|
||||||
for interface in supportedInterfaces {
|
|
||||||
if let networkInfo = CNCopyCurrentNetworkInfo(interface) {
|
|
||||||
if let ssid = (networkInfo as NSDictionary)[kCNNetworkInfoKeySSID as String] as? String {
|
|
||||||
return !ssid.isEmpty ? ssid : nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import os.log
|
import os.log
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
@@ -324,8 +324,22 @@ extension TunnelDetailTableViewController {
|
|||||||
private func statusCell(for tableView: UITableView, at indexPath: IndexPath) -> UITableViewCell {
|
private func statusCell(for tableView: UITableView, at indexPath: IndexPath) -> UITableViewCell {
|
||||||
let cell: SwitchCell = tableView.dequeueReusableCell(for: indexPath)
|
let cell: SwitchCell = tableView.dequeueReusableCell(for: indexPath)
|
||||||
|
|
||||||
let statusUpdate: (SwitchCell, TunnelStatus) -> Void = { cell, status in
|
func update(cell: SwitchCell?, with tunnel: TunnelContainer) {
|
||||||
let text: String
|
guard let cell = cell else { return }
|
||||||
|
|
||||||
|
let status = tunnel.status
|
||||||
|
let isOnDemandEngaged = tunnel.isActivateOnDemandEnabled
|
||||||
|
|
||||||
|
let isSwitchOn = (status == .activating || status == .active || isOnDemandEngaged)
|
||||||
|
cell.switchView.setOn(isSwitchOn, animated: true)
|
||||||
|
|
||||||
|
if isOnDemandEngaged && !(status == .activating || status == .active) {
|
||||||
|
cell.switchView.onTintColor = UIColor.systemYellow
|
||||||
|
} else {
|
||||||
|
cell.switchView.onTintColor = UIColor.systemGreen
|
||||||
|
}
|
||||||
|
|
||||||
|
var text: String
|
||||||
switch status {
|
switch status {
|
||||||
case .inactive:
|
case .inactive:
|
||||||
text = tr("tunnelStatusInactive")
|
text = tr("tunnelStatusInactive")
|
||||||
@@ -342,24 +356,49 @@ extension TunnelDetailTableViewController {
|
|||||||
case .waiting:
|
case .waiting:
|
||||||
text = tr("tunnelStatusWaiting")
|
text = tr("tunnelStatusWaiting")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if tunnel.hasOnDemandRules {
|
||||||
|
text += isOnDemandEngaged ? tr("tunnelStatusAddendumOnDemand") : ""
|
||||||
|
cell.switchView.isUserInteractionEnabled = true
|
||||||
|
cell.isEnabled = true
|
||||||
|
} else {
|
||||||
|
cell.switchView.isUserInteractionEnabled = (status == .inactive || status == .active)
|
||||||
|
cell.isEnabled = (status == .inactive || status == .active)
|
||||||
|
}
|
||||||
|
|
||||||
|
if tunnel.hasOnDemandRules && !isOnDemandEngaged && status == .inactive {
|
||||||
|
text = tr("tunnelStatusOnDemandDisabled")
|
||||||
|
}
|
||||||
|
|
||||||
cell.textLabel?.text = text
|
cell.textLabel?.text = text
|
||||||
cell.switchView.isOn = !(status == .deactivating || status == .inactive)
|
|
||||||
cell.switchView.isUserInteractionEnabled = (status == .inactive || status == .active)
|
|
||||||
cell.isEnabled = status == .active || status == .inactive
|
|
||||||
}
|
}
|
||||||
|
|
||||||
statusUpdate(cell, tunnel.status)
|
update(cell: cell, with: tunnel)
|
||||||
cell.observationToken = tunnel.observe(\.status) { [weak cell] tunnel, _ in
|
cell.statusObservationToken = tunnel.observe(\.status) { [weak cell] tunnel, _ in
|
||||||
guard let cell = cell else { return }
|
update(cell: cell, with: tunnel)
|
||||||
statusUpdate(cell, tunnel.status)
|
}
|
||||||
|
cell.isOnDemandEnabledObservationToken = tunnel.observe(\.isActivateOnDemandEnabled) { [weak cell] tunnel, _ in
|
||||||
|
update(cell: cell, with: tunnel)
|
||||||
|
}
|
||||||
|
cell.hasOnDemandRulesObservationToken = tunnel.observe(\.hasOnDemandRules) { [weak cell] tunnel, _ in
|
||||||
|
update(cell: cell, with: tunnel)
|
||||||
}
|
}
|
||||||
|
|
||||||
cell.onSwitchToggled = { [weak self] isOn in
|
cell.onSwitchToggled = { [weak self] isOn in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
if isOn {
|
|
||||||
self.tunnelsManager.startActivation(of: self.tunnel)
|
if self.tunnel.hasOnDemandRules {
|
||||||
|
self.tunnelsManager.setOnDemandEnabled(isOn, on: self.tunnel) { error in
|
||||||
|
if error == nil && !isOn {
|
||||||
|
self.tunnelsManager.startDeactivation(of: self.tunnel)
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.tunnelsManager.startDeactivation(of: self.tunnel)
|
if isOn {
|
||||||
|
self.tunnelsManager.startActivation(of: self.tunnel)
|
||||||
|
} else {
|
||||||
|
self.tunnelsManager.startDeactivation(of: self.tunnel)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return cell
|
return cell
|
||||||
@@ -395,6 +434,7 @@ extension TunnelDetailTableViewController {
|
|||||||
let cell: KeyValueCell = tableView.dequeueReusableCell(for: indexPath)
|
let cell: KeyValueCell = tableView.dequeueReusableCell(for: indexPath)
|
||||||
cell.key = field.localizedUIString
|
cell.key = field.localizedUIString
|
||||||
cell.value = onDemandViewModel.localizedInterfaceDescription
|
cell.value = onDemandViewModel.localizedInterfaceDescription
|
||||||
|
cell.copyableGesture = false
|
||||||
return cell
|
return cell
|
||||||
} else {
|
} else {
|
||||||
assert(field == .ssid)
|
assert(field == .ssid)
|
||||||
@@ -402,6 +442,7 @@ extension TunnelDetailTableViewController {
|
|||||||
let cell: KeyValueCell = tableView.dequeueReusableCell(for: indexPath)
|
let cell: KeyValueCell = tableView.dequeueReusableCell(for: indexPath)
|
||||||
cell.key = field.localizedUIString
|
cell.key = field.localizedUIString
|
||||||
cell.value = onDemandViewModel.ssidOption.localizedUIString
|
cell.value = onDemandViewModel.ssidOption.localizedUIString
|
||||||
|
cell.copyableGesture = false
|
||||||
return cell
|
return cell
|
||||||
} else {
|
} else {
|
||||||
let cell: ChevronCell = tableView.dequeueReusableCell(for: indexPath)
|
let cell: ChevronCell = tableView.dequeueReusableCell(for: indexPath)
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
|
|
||||||
protocol TunnelEditTableViewControllerDelegate: class {
|
protocol TunnelEditTableViewControllerDelegate: AnyObject {
|
||||||
func tunnelSaved(tunnel: TunnelContainer)
|
func tunnelSaved(tunnel: TunnelContainer)
|
||||||
func tunnelEditingCancelled()
|
func tunnelEditingCancelled()
|
||||||
}
|
}
|
||||||
@@ -318,7 +318,7 @@ extension TunnelEditTableViewController {
|
|||||||
let removedSectionIndices = self.deletePeer(peer: peerData)
|
let removedSectionIndices = self.deletePeer(peer: peerData)
|
||||||
let shouldShowExcludePrivateIPs = (self.tunnelViewModel.peersData.count == 1 && self.tunnelViewModel.peersData[0].shouldAllowExcludePrivateIPsControl)
|
let shouldShowExcludePrivateIPs = (self.tunnelViewModel.peersData.count == 1 && self.tunnelViewModel.peersData[0].shouldAllowExcludePrivateIPsControl)
|
||||||
|
|
||||||
//swiftlint:disable:next trailing_closure
|
// swiftlint:disable:next trailing_closure
|
||||||
tableView.performBatchUpdates({
|
tableView.performBatchUpdates({
|
||||||
self.tableView.deleteSections(removedSectionIndices, with: .fade)
|
self.tableView.deleteSections(removedSectionIndices, with: .fade)
|
||||||
if shouldShowExcludePrivateIPs {
|
if shouldShowExcludePrivateIPs {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import UIKit
|
import UIKit
|
||||||
import MobileCoreServices
|
import MobileCoreServices
|
||||||
@@ -317,10 +317,18 @@ extension TunnelsListTableViewController: UITableViewDataSource {
|
|||||||
cell.tunnel = tunnel
|
cell.tunnel = tunnel
|
||||||
cell.onSwitchToggled = { [weak self] isOn in
|
cell.onSwitchToggled = { [weak self] isOn in
|
||||||
guard let self = self, let tunnelsManager = self.tunnelsManager else { return }
|
guard let self = self, let tunnelsManager = self.tunnelsManager else { return }
|
||||||
if isOn {
|
if tunnel.hasOnDemandRules {
|
||||||
tunnelsManager.startActivation(of: tunnel)
|
tunnelsManager.setOnDemandEnabled(isOn, on: tunnel) { error in
|
||||||
|
if error == nil && !isOn {
|
||||||
|
tunnelsManager.startDeactivation(of: tunnel)
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
tunnelsManager.startDeactivation(of: tunnel)
|
if isOn {
|
||||||
|
tunnelsManager.startActivation(of: tunnel)
|
||||||
|
} else {
|
||||||
|
tunnelsManager.startDeactivation(of: tunnel)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
import ServiceManagement
|
import ServiceManagement
|
||||||
@@ -65,19 +65,6 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows: Bool) -> Bool {
|
|
||||||
if let appleEvent = NSAppleEventManager.shared().currentAppleEvent {
|
|
||||||
if LaunchedAtLoginDetector.isReopenedByLoginItemHelper(reopenAppleEvent: appleEvent) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if hasVisibleWindows {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
showManageTunnelsWindow(completion: nil)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func confirmAndQuit() {
|
@objc func confirmAndQuit() {
|
||||||
let alert = NSAlert()
|
let alert = NSAlert()
|
||||||
alert.messageText = tr("macConfirmAndQuitAlertMessage")
|
alert.messageText = tr("macConfirmAndQuitAlertMessage")
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
class Application: NSApplication {
|
class Application: NSApplication {
|
||||||
|
|
||||||
private var appDelegate: AppDelegate? //swiftlint:disable:this weak_delegate
|
private var appDelegate: AppDelegate? // swiftlint:disable:this weak_delegate
|
||||||
|
|
||||||
override init() {
|
override init() {
|
||||||
super.init()
|
super.init()
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"info" : {
|
"info" : {
|
||||||
"version" : 1,
|
"author" : "xcode",
|
||||||
"author" : "xcode"
|
"version" : 1
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "StatusCircleYellow@1x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "StatusCircleYellow@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
BIN
Binary file not shown.
|
After Width: | Height: | Size: 364 B |
Vendored
BIN
Binary file not shown.
|
After Width: | Height: | Size: 602 B |
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
<key>LSUIElement</key>
|
<key>LSUIElement</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>NSHumanReadableCopyright</key>
|
<key>NSHumanReadableCopyright</key>
|
||||||
<string>Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.</string>
|
<string>Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.</string>
|
||||||
<key>NSPrincipalClass</key>
|
<key>NSPrincipalClass</key>
|
||||||
<string>WireGuard.Application</string>
|
<string>WireGuard.Application</string>
|
||||||
<key>LSApplicationCategoryType</key>
|
<key>LSApplicationCategoryType</key>
|
||||||
|
|||||||
@@ -1,28 +1,19 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
class LaunchedAtLoginDetector {
|
class LaunchedAtLoginDetector {
|
||||||
static let launchCode = "LaunchedByWireGuardLoginItemHelper"
|
|
||||||
|
|
||||||
static func isLaunchedAtLogin(openAppleEvent: NSAppleEventDescriptor) -> Bool {
|
static func isLaunchedAtLogin(openAppleEvent: NSAppleEventDescriptor) -> Bool {
|
||||||
guard isOpenEvent(openAppleEvent) else { return false }
|
let now = clock_gettime_nsec_np(CLOCK_UPTIME_RAW)
|
||||||
guard let propData = openAppleEvent.paramDescriptor(forKeyword: keyAEPropData) else { return false }
|
guard openAppleEvent.eventClass == kCoreEventClass && openAppleEvent.eventID == kAEOpenApplication else { return false }
|
||||||
return propData.stringValue == launchCode
|
guard let url = FileManager.loginHelperTimestampURL else { return false }
|
||||||
}
|
guard let data = try? Data(contentsOf: url) else { return false }
|
||||||
|
_ = FileManager.deleteFile(at: url)
|
||||||
static func isReopenedByLoginItemHelper(reopenAppleEvent: NSAppleEventDescriptor) -> Bool {
|
guard data.count == 8 else { return false }
|
||||||
guard isReopenEvent(reopenAppleEvent) else { return false }
|
let then = data.withUnsafeBytes { ptr in
|
||||||
guard let propData = reopenAppleEvent.paramDescriptor(forKeyword: keyAEPropData) else { return false }
|
ptr.load(as: UInt64.self)
|
||||||
return propData.stringValue == launchCode
|
}
|
||||||
|
return now - then <= 20000000000
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func isOpenEvent(_ event: NSAppleEventDescriptor) -> Bool {
|
|
||||||
return event.eventClass == kCoreEventClass && event.eventID == kAEOpenApplication
|
|
||||||
}
|
|
||||||
|
|
||||||
private func isReopenEvent(_ event: NSAppleEventDescriptor) -> Bool {
|
|
||||||
return event.eventClass == kCoreEventClass && event.eventID == kAEReopenApplication
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -25,12 +25,14 @@
|
|||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||||
<key>NSHumanReadableCopyright</key>
|
<key>NSHumanReadableCopyright</key>
|
||||||
<string>Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.</string>
|
<string>Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.</string>
|
||||||
<key>NSPrincipalClass</key>
|
<key>NSPrincipalClass</key>
|
||||||
<string>NSApplication</string>
|
<string>NSApplication</string>
|
||||||
<key>LSBackgroundOnly</key>
|
<key>LSBackgroundOnly</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>com.wireguard.macos.app_id</key>
|
<key>com.wireguard.macos.app_id</key>
|
||||||
<string>$(APP_ID_MACOS)</string>
|
<string>$(APP_ID_MACOS)</string>
|
||||||
|
<key>com.wireguard.macos.app_group_id</key>
|
||||||
|
<string>$(DEVELOPMENT_TEAM).group.$(APP_ID_MACOS)</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -4,5 +4,9 @@
|
|||||||
<dict>
|
<dict>
|
||||||
<key>com.apple.security.app-sandbox</key>
|
<key>com.apple.security.app-sandbox</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>com.apple.security.application-groups</key>
|
||||||
|
<array>
|
||||||
|
<string>$(DEVELOPMENT_TEAM).group.$(APP_ID_MACOS)</string>
|
||||||
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -1,17 +1,36 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
#import <Cocoa/Cocoa.h>
|
#import <Cocoa/Cocoa.h>
|
||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
NSString *appIdInfoDictionaryKey = @"com.wireguard.macos.app_id";
|
NSString *appId = [NSBundle.mainBundle objectForInfoDictionaryKey:@"com.wireguard.macos.app_id"];
|
||||||
NSString *appId = [NSBundle.mainBundle objectForInfoDictionaryKey:appIdInfoDictionaryKey];
|
NSString *appGroupId = [NSBundle.mainBundle objectForInfoDictionaryKey:@"com.wireguard.macos.app_group_id"];
|
||||||
|
if (!appId || !appGroupId)
|
||||||
NSString *launchCode = @"LaunchedByWireGuardLoginItemHelper";
|
return 1;
|
||||||
NSAppleEventDescriptor *paramDescriptor = [NSAppleEventDescriptor descriptorWithString:launchCode];
|
NSURL *containerUrl = [[NSFileManager defaultManager] containerURLForSecurityApplicationGroupIdentifier:appGroupId];
|
||||||
|
if (!containerUrl)
|
||||||
[NSWorkspace.sharedWorkspace launchAppWithBundleIdentifier:appId options:NSWorkspaceLaunchWithoutActivation
|
return 2;
|
||||||
additionalEventParamDescriptor:paramDescriptor launchIdentifier:NULL];
|
uint64_t now = clock_gettime_nsec_np(CLOCK_UPTIME_RAW);
|
||||||
|
if (![[NSData dataWithBytes:&now length:sizeof(now)] writeToURL:[containerUrl URLByAppendingPathComponent:@"login-helper-timestamp.bin"] atomically:YES])
|
||||||
|
return 3;
|
||||||
|
if (@available(macOS 10.15, *)) {
|
||||||
|
NSCondition *condition = [[NSCondition alloc] init];
|
||||||
|
NSURL *appURL = [NSWorkspace.sharedWorkspace URLForApplicationWithBundleIdentifier:appId];
|
||||||
|
if (!appURL)
|
||||||
|
return 4;
|
||||||
|
NSWorkspaceOpenConfiguration *openConfiguration = [NSWorkspaceOpenConfiguration configuration];
|
||||||
|
openConfiguration.activates = NO;
|
||||||
|
openConfiguration.addsToRecentItems = NO;
|
||||||
|
openConfiguration.hides = YES;
|
||||||
|
[NSWorkspace.sharedWorkspace openApplicationAtURL:appURL configuration:openConfiguration completionHandler:^(NSRunningApplication * _Nullable app, NSError * _Nullable error) {
|
||||||
|
[condition signal];
|
||||||
|
}];
|
||||||
|
[condition wait];
|
||||||
|
} else {
|
||||||
|
[NSWorkspace.sharedWorkspace launchAppWithBundleIdentifier:appId options:NSWorkspaceLaunchWithoutActivation
|
||||||
|
additionalEventParamDescriptor:NULL launchIdentifier:NULL];
|
||||||
|
}
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
@@ -8,11 +8,12 @@ class MacAppStoreUpdateDetector {
|
|||||||
guard isQuitEvent(quitAppleEvent) else { return false }
|
guard isQuitEvent(quitAppleEvent) else { return false }
|
||||||
guard let senderPIDDescriptor = quitAppleEvent.attributeDescriptor(forKeyword: keySenderPIDAttr) else { return false }
|
guard let senderPIDDescriptor = quitAppleEvent.attributeDescriptor(forKeyword: keySenderPIDAttr) else { return false }
|
||||||
let pid = senderPIDDescriptor.int32Value
|
let pid = senderPIDDescriptor.int32Value
|
||||||
|
wg_log(.debug, message: "aevt/quit Apple event received from pid: \(pid)")
|
||||||
guard let executablePath = getExecutablePath(from: pid) else { return false }
|
guard let executablePath = getExecutablePath(from: pid) else { return false }
|
||||||
wg_log(.debug, message: "aevt/quit Apple event received from: \(executablePath)")
|
wg_log(.debug, message: "aevt/quit Apple event received from executable: \(executablePath)")
|
||||||
if executablePath.hasPrefix("/System/Library/") {
|
if executablePath.hasPrefix("/System/Library/") {
|
||||||
let executableName = URL(fileURLWithPath: executablePath, isDirectory: false).lastPathComponent
|
let executableName = URL(fileURLWithPath: executablePath, isDirectory: false).lastPathComponent
|
||||||
return executableName == "com.apple.CommerceKit.StoreAEService"
|
return executableName.hasPrefix("com.apple.") && executableName.hasSuffix(".StoreAEService")
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import AppKit
|
import AppKit
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
@@ -7,7 +7,7 @@ extension NSTableView {
|
|||||||
func dequeueReusableCell<T: NSView>() -> T {
|
func dequeueReusableCell<T: NSView>() -> T {
|
||||||
let identifier = NSUserInterfaceItemIdentifier(NSStringFromClass(T.self))
|
let identifier = NSUserInterfaceItemIdentifier(NSStringFromClass(T.self))
|
||||||
if let cellView = makeView(withIdentifier: identifier, owner: self) {
|
if let cellView = makeView(withIdentifier: identifier, owner: self) {
|
||||||
//swiftlint:disable:next force_cast
|
// swiftlint:disable:next force_cast
|
||||||
return cellView as! T
|
return cellView as! T
|
||||||
}
|
}
|
||||||
let cellView = T()
|
let cellView = T()
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
protocol StatusMenuWindowDelegate: class {
|
protocol StatusMenuWindowDelegate: AnyObject {
|
||||||
func showManageTunnelsWindow(completion: ((NSWindow?) -> Void)?)
|
func showManageTunnelsWindow(completion: ((NSWindow?) -> Void)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -144,10 +144,20 @@ class StatusMenu: NSMenu {
|
|||||||
|
|
||||||
@objc func tunnelClicked(sender: AnyObject) {
|
@objc func tunnelClicked(sender: AnyObject) {
|
||||||
guard let tunnelMenuItem = sender as? TunnelMenuItem else { return }
|
guard let tunnelMenuItem = sender as? TunnelMenuItem else { return }
|
||||||
if tunnelMenuItem.state == .off {
|
let tunnel = tunnelMenuItem.tunnel
|
||||||
tunnelsManager.startActivation(of: tunnelMenuItem.tunnel)
|
if tunnel.hasOnDemandRules {
|
||||||
|
let turnOn = !tunnel.isActivateOnDemandEnabled
|
||||||
|
tunnelsManager.setOnDemandEnabled(turnOn, on: tunnel) { error in
|
||||||
|
if error == nil && !turnOn {
|
||||||
|
self.tunnelsManager.startDeactivation(of: tunnel)
|
||||||
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
tunnelsManager.startDeactivation(of: tunnelMenuItem.tunnel)
|
if tunnel.status == .inactive {
|
||||||
|
tunnelsManager.startActivation(of: tunnel)
|
||||||
|
} else if tunnel.status == .active {
|
||||||
|
tunnelsManager.startDeactivation(of: tunnel)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -291,6 +301,7 @@ class TunnelMenuItem: NSMenuItem {
|
|||||||
|
|
||||||
private var statusObservationToken: AnyObject?
|
private var statusObservationToken: AnyObject?
|
||||||
private var nameObservationToken: AnyObject?
|
private var nameObservationToken: AnyObject?
|
||||||
|
private var isOnDemandEnabledObservationToken: AnyObject?
|
||||||
|
|
||||||
init(tunnel: TunnelContainer, action selector: Selector?) {
|
init(tunnel: TunnelContainer, action selector: Selector?) {
|
||||||
self.tunnel = tunnel
|
self.tunnel = tunnel
|
||||||
@@ -303,7 +314,12 @@ class TunnelMenuItem: NSMenuItem {
|
|||||||
let nameObservationToken = tunnel.observe(\TunnelContainer.name) { [weak self] _, _ in
|
let nameObservationToken = tunnel.observe(\TunnelContainer.name) { [weak self] _, _ in
|
||||||
self?.updateTitle()
|
self?.updateTitle()
|
||||||
}
|
}
|
||||||
|
let isOnDemandEnabledObservationToken = tunnel.observe(\.isActivateOnDemandEnabled) { [weak self] _, _ in
|
||||||
|
self?.updateTitle()
|
||||||
|
self?.updateStatus()
|
||||||
|
}
|
||||||
self.statusObservationToken = statusObservationToken
|
self.statusObservationToken = statusObservationToken
|
||||||
|
self.isOnDemandEnabledObservationToken = isOnDemandEnabledObservationToken
|
||||||
self.nameObservationToken = nameObservationToken
|
self.nameObservationToken = nameObservationToken
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -312,12 +328,19 @@ class TunnelMenuItem: NSMenuItem {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func updateTitle() {
|
func updateTitle() {
|
||||||
title = tunnel.name
|
if tunnel.isActivateOnDemandEnabled {
|
||||||
|
title = tunnel.name + " (On-Demand)"
|
||||||
|
} else {
|
||||||
|
title = tunnel.name
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateStatus() {
|
func updateStatus() {
|
||||||
let shouldShowCheckmark = (tunnel.status != .inactive && tunnel.status != .deactivating)
|
if tunnel.isActivateOnDemandEnabled {
|
||||||
state = shouldShowCheckmark ? .on : .off
|
state = (tunnel.status == .inactive || tunnel.status == .deactivating) ? .mixed : .on
|
||||||
|
} else {
|
||||||
|
state = (tunnel.status == .inactive || tunnel.status == .deactivating) ? .off : .on
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
@@ -28,7 +28,9 @@ class ButtonRow: NSView {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var onButtonClicked: (() -> Void)?
|
var onButtonClicked: (() -> Void)?
|
||||||
var observationToken: AnyObject?
|
var statusObservationToken: AnyObject?
|
||||||
|
var isOnDemandEnabledObservationToken: AnyObject?
|
||||||
|
var hasOnDemandRulesObservationToken: AnyObject?
|
||||||
|
|
||||||
override var intrinsicContentSize: NSSize {
|
override var intrinsicContentSize: NSSize {
|
||||||
return NSSize(width: NSView.noIntrinsicMetric, height: button.intrinsicContentSize.height)
|
return NSSize(width: NSView.noIntrinsicMetric, height: button.intrinsicContentSize.height)
|
||||||
@@ -62,6 +64,8 @@ class ButtonRow: NSView {
|
|||||||
buttonTitle = ""
|
buttonTitle = ""
|
||||||
buttonToolTip = ""
|
buttonToolTip = ""
|
||||||
onButtonClicked = nil
|
onButtonClicked = nil
|
||||||
observationToken = nil
|
statusObservationToken = nil
|
||||||
|
isOnDemandEnabledObservationToken = nil
|
||||||
|
hasOnDemandRulesObservationToken = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
@@ -16,7 +16,7 @@ class ConfTextStorage: NSTextStorage {
|
|||||||
private(set) var hasError = false
|
private(set) var hasError = false
|
||||||
private(set) var privateKeyString: String?
|
private(set) var privateKeyString: String?
|
||||||
|
|
||||||
private(set) var hasOnePeer: Bool = false
|
private(set) var hasOnePeer = false
|
||||||
private(set) var lastOnePeerAllowedIPs = [String]()
|
private(set) var lastOnePeerAllowedIPs = [String]()
|
||||||
private(set) var lastOnePeerDNSServers = [String]()
|
private(set) var lastOnePeerDNSServers = [String]()
|
||||||
private(set) var lastOnePeerHasPublicKey = false
|
private(set) var lastOnePeerHasPublicKey = false
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
@@ -7,7 +7,7 @@ class ConfTextView: NSTextView {
|
|||||||
|
|
||||||
private let confTextStorage = ConfTextStorage()
|
private let confTextStorage = ConfTextStorage()
|
||||||
|
|
||||||
@objc dynamic var hasError: Bool = false
|
@objc dynamic var hasError = false
|
||||||
@objc dynamic var privateKeyString: String?
|
@objc dynamic var privateKeyString: String?
|
||||||
@objc dynamic var singlePeerAllowedIPs: [String]?
|
@objc dynamic var singlePeerAllowedIPs: [String]?
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
@@ -49,7 +49,9 @@ class EditableKeyValueRow: NSView {
|
|||||||
set(value) { valueImageView?.image = value }
|
set(value) { valueImageView?.image = value }
|
||||||
}
|
}
|
||||||
|
|
||||||
var observationToken: AnyObject?
|
var statusObservationToken: AnyObject?
|
||||||
|
var isOnDemandEnabledObservationToken: AnyObject?
|
||||||
|
var hasOnDemandRulesObservationToken: AnyObject?
|
||||||
|
|
||||||
override var intrinsicContentSize: NSSize {
|
override var intrinsicContentSize: NSSize {
|
||||||
let height = max(keyLabel.intrinsicContentSize.height, valueLabel.intrinsicContentSize.height)
|
let height = max(keyLabel.intrinsicContentSize.height, valueLabel.intrinsicContentSize.height)
|
||||||
@@ -108,7 +110,9 @@ class EditableKeyValueRow: NSView {
|
|||||||
key = ""
|
key = ""
|
||||||
value = ""
|
value = ""
|
||||||
isKeyInBold = false
|
isKeyInBold = false
|
||||||
observationToken = nil
|
statusObservationToken = nil
|
||||||
|
isOnDemandEnabledObservationToken = nil
|
||||||
|
hasOnDemandRulesObservationToken = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
import CoreWLAN
|
import CoreWLAN
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
@@ -12,9 +12,12 @@ class TunnelListRow: NSView {
|
|||||||
self?.nameLabel.stringValue = tunnel.name
|
self?.nameLabel.stringValue = tunnel.name
|
||||||
}
|
}
|
||||||
// Bind to the tunnel's status
|
// Bind to the tunnel's status
|
||||||
statusImageView.image = TunnelListRow.image(for: tunnel?.status)
|
statusImageView.image = TunnelListRow.image(for: tunnel)
|
||||||
statusObservationToken = tunnel?.observe(\TunnelContainer.status) { [weak self] tunnel, _ in
|
statusObservationToken = tunnel?.observe(\TunnelContainer.status) { [weak self] tunnel, _ in
|
||||||
self?.statusImageView.image = TunnelListRow.image(for: tunnel.status)
|
self?.statusImageView.image = TunnelListRow.image(for: tunnel)
|
||||||
|
}
|
||||||
|
isOnDemandEnabledObservationToken = tunnel?.observe(\TunnelContainer.isActivateOnDemandEnabled) { [weak self] tunnel, _ in
|
||||||
|
self?.statusImageView.image = TunnelListRow.image(for: tunnel)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -33,6 +36,7 @@ class TunnelListRow: NSView {
|
|||||||
|
|
||||||
private var statusObservationToken: AnyObject?
|
private var statusObservationToken: AnyObject?
|
||||||
private var nameObservationToken: AnyObject?
|
private var nameObservationToken: AnyObject?
|
||||||
|
private var isOnDemandEnabledObservationToken: AnyObject?
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
super.init(frame: CGRect.zero)
|
super.init(frame: CGRect.zero)
|
||||||
@@ -56,15 +60,19 @@ class TunnelListRow: NSView {
|
|||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
static func image(for status: TunnelStatus?) -> NSImage? {
|
static func image(for tunnel: TunnelContainer?) -> NSImage? {
|
||||||
guard let status = status else { return nil }
|
guard let tunnel = tunnel else { return nil }
|
||||||
switch status {
|
switch tunnel.status {
|
||||||
case .active, .restarting, .reasserting:
|
case .active, .restarting, .reasserting:
|
||||||
return NSImage(named: NSImage.statusAvailableName)
|
return NSImage(named: NSImage.statusAvailableName)
|
||||||
case .activating, .waiting, .deactivating:
|
case .activating, .waiting, .deactivating:
|
||||||
return NSImage(named: NSImage.statusPartiallyAvailableName)
|
return NSImage(named: NSImage.statusPartiallyAvailableName)
|
||||||
case .inactive:
|
case .inactive:
|
||||||
return NSImage(named: NSImage.statusNoneName)
|
if tunnel.isActivateOnDemandEnabled {
|
||||||
|
return NSImage(named: NSImage.Name.statusOnDemandEnabled)
|
||||||
|
} else {
|
||||||
|
return NSImage(named: NSImage.statusNoneName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,3 +81,7 @@ class TunnelListRow: NSView {
|
|||||||
statusImageView.image = nil
|
statusImageView.image = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension NSImage.Name {
|
||||||
|
static let statusOnDemandEnabled = NSImage.Name("StatusCircleYellow")
|
||||||
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
|
|||||||
+86
-43
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
@@ -216,10 +216,19 @@ class TunnelDetailTableViewController: NSViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc func handleToggleActiveStatusAction() {
|
@objc func handleToggleActiveStatusAction() {
|
||||||
if tunnel.status == .inactive {
|
if tunnel.hasOnDemandRules {
|
||||||
tunnelsManager.startActivation(of: tunnel)
|
let turnOn = !tunnel.isActivateOnDemandEnabled
|
||||||
} else if tunnel.status == .active {
|
tunnelsManager.setOnDemandEnabled(turnOn, on: tunnel) { error in
|
||||||
tunnelsManager.startDeactivation(of: tunnel)
|
if error == nil && !turnOn {
|
||||||
|
self.tunnelsManager.startDeactivation(of: self.tunnel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if tunnel.status == .inactive {
|
||||||
|
tunnelsManager.startActivation(of: tunnel)
|
||||||
|
} else if tunnel.status == .active {
|
||||||
|
tunnelsManager.startDeactivation(of: tunnel)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -422,79 +431,113 @@ extension TunnelDetailTableViewController: NSTableViewDelegate {
|
|||||||
func statusCell() -> NSView {
|
func statusCell() -> NSView {
|
||||||
let cell: KeyValueImageRow = tableView.dequeueReusableCell()
|
let cell: KeyValueImageRow = tableView.dequeueReusableCell()
|
||||||
cell.key = tr(format: "macFieldKey (%@)", tr("tunnelInterfaceStatus"))
|
cell.key = tr(format: "macFieldKey (%@)", tr("tunnelInterfaceStatus"))
|
||||||
cell.value = TunnelDetailTableViewController.localizedStatusDescription(forStatus: tunnel.status)
|
cell.value = TunnelDetailTableViewController.localizedStatusDescription(for: tunnel)
|
||||||
cell.valueImage = TunnelDetailTableViewController.image(forStatus: tunnel.status)
|
cell.valueImage = TunnelDetailTableViewController.image(for: tunnel)
|
||||||
cell.observationToken = tunnel.observe(\.status) { [weak cell] tunnel, _ in
|
let changeHandler: (TunnelContainer, Any) -> Void = { [weak cell] tunnel, _ in
|
||||||
guard let cell = cell else { return }
|
guard let cell = cell else { return }
|
||||||
cell.value = TunnelDetailTableViewController.localizedStatusDescription(forStatus: tunnel.status)
|
cell.value = TunnelDetailTableViewController.localizedStatusDescription(for: tunnel)
|
||||||
cell.valueImage = TunnelDetailTableViewController.image(forStatus: tunnel.status)
|
cell.valueImage = TunnelDetailTableViewController.image(for: tunnel)
|
||||||
}
|
}
|
||||||
|
cell.statusObservationToken = tunnel.observe(\.status, changeHandler: changeHandler)
|
||||||
|
cell.isOnDemandEnabledObservationToken = tunnel.observe(\.isActivateOnDemandEnabled, changeHandler: changeHandler)
|
||||||
|
cell.hasOnDemandRulesObservationToken = tunnel.observe(\.hasOnDemandRules, changeHandler: changeHandler)
|
||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
|
|
||||||
func toggleStatusCell() -> NSView {
|
func toggleStatusCell() -> NSView {
|
||||||
let cell: ButtonRow = tableView.dequeueReusableCell()
|
let cell: ButtonRow = tableView.dequeueReusableCell()
|
||||||
cell.buttonTitle = TunnelDetailTableViewController.localizedToggleStatusActionText(forStatus: tunnel.status)
|
cell.buttonTitle = TunnelDetailTableViewController.localizedToggleStatusActionText(for: tunnel)
|
||||||
cell.isButtonEnabled = (tunnel.status == .active || tunnel.status == .inactive)
|
cell.isButtonEnabled = (tunnel.hasOnDemandRules || tunnel.status == .active || tunnel.status == .inactive)
|
||||||
cell.buttonToolTip = tr("macToolTipToggleStatus")
|
cell.buttonToolTip = tr("macToolTipToggleStatus")
|
||||||
cell.onButtonClicked = { [weak self] in
|
cell.onButtonClicked = { [weak self] in
|
||||||
self?.handleToggleActiveStatusAction()
|
self?.handleToggleActiveStatusAction()
|
||||||
}
|
}
|
||||||
cell.observationToken = tunnel.observe(\.status) { [weak cell] tunnel, _ in
|
let changeHandler: (TunnelContainer, Any) -> Void = { [weak cell] tunnel, _ in
|
||||||
guard let cell = cell else { return }
|
guard let cell = cell else { return }
|
||||||
cell.buttonTitle = TunnelDetailTableViewController.localizedToggleStatusActionText(forStatus: tunnel.status)
|
cell.buttonTitle = TunnelDetailTableViewController.localizedToggleStatusActionText(for: tunnel)
|
||||||
cell.isButtonEnabled = (tunnel.status == .active || tunnel.status == .inactive)
|
cell.isButtonEnabled = (tunnel.hasOnDemandRules || tunnel.status == .active || tunnel.status == .inactive)
|
||||||
}
|
}
|
||||||
|
cell.statusObservationToken = tunnel.observe(\.status, changeHandler: changeHandler)
|
||||||
|
cell.isOnDemandEnabledObservationToken = tunnel.observe(\.isActivateOnDemandEnabled, changeHandler: changeHandler)
|
||||||
|
cell.hasOnDemandRulesObservationToken = tunnel.observe(\.hasOnDemandRules, changeHandler: changeHandler)
|
||||||
return cell
|
return cell
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func localizedStatusDescription(forStatus status: TunnelStatus) -> String {
|
private static func localizedStatusDescription(for tunnel: TunnelContainer) -> String {
|
||||||
|
let status = tunnel.status
|
||||||
|
let isOnDemandEngaged = tunnel.isActivateOnDemandEnabled
|
||||||
|
|
||||||
|
var text: String
|
||||||
switch status {
|
switch status {
|
||||||
case .inactive:
|
case .inactive:
|
||||||
return tr("tunnelStatusInactive")
|
text = tr("tunnelStatusInactive")
|
||||||
case .activating:
|
case .activating:
|
||||||
return tr("tunnelStatusActivating")
|
text = tr("tunnelStatusActivating")
|
||||||
case .active:
|
case .active:
|
||||||
return tr("tunnelStatusActive")
|
text = tr("tunnelStatusActive")
|
||||||
case .deactivating:
|
case .deactivating:
|
||||||
return tr("tunnelStatusDeactivating")
|
text = tr("tunnelStatusDeactivating")
|
||||||
case .reasserting:
|
case .reasserting:
|
||||||
return tr("tunnelStatusReasserting")
|
text = tr("tunnelStatusReasserting")
|
||||||
case .restarting:
|
case .restarting:
|
||||||
return tr("tunnelStatusRestarting")
|
text = tr("tunnelStatusRestarting")
|
||||||
case .waiting:
|
case .waiting:
|
||||||
return tr("tunnelStatusWaiting")
|
text = tr("tunnelStatusWaiting")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if tunnel.hasOnDemandRules {
|
||||||
|
text += isOnDemandEngaged ?
|
||||||
|
tr("tunnelStatusAddendumOnDemandEnabled") : tr("tunnelStatusAddendumOnDemandDisabled")
|
||||||
|
}
|
||||||
|
|
||||||
|
return text
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func image(forStatus status: TunnelStatus?) -> NSImage? {
|
private static func image(for tunnel: TunnelContainer?) -> NSImage? {
|
||||||
guard let status = status else { return nil }
|
guard let tunnel = tunnel else { return nil }
|
||||||
switch status {
|
switch tunnel.status {
|
||||||
case .active, .restarting, .reasserting:
|
case .active, .restarting, .reasserting:
|
||||||
return NSImage(named: NSImage.statusAvailableName)
|
return NSImage(named: NSImage.statusAvailableName)
|
||||||
case .activating, .waiting, .deactivating:
|
case .activating, .waiting, .deactivating:
|
||||||
return NSImage(named: NSImage.statusPartiallyAvailableName)
|
return NSImage(named: NSImage.statusPartiallyAvailableName)
|
||||||
case .inactive:
|
case .inactive:
|
||||||
return NSImage(named: NSImage.statusNoneName)
|
if tunnel.isActivateOnDemandEnabled {
|
||||||
|
return NSImage(named: NSImage.Name.statusOnDemandEnabled)
|
||||||
|
} else {
|
||||||
|
return NSImage(named: NSImage.statusNoneName)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static func localizedToggleStatusActionText(forStatus status: TunnelStatus) -> String {
|
private static func localizedToggleStatusActionText(for tunnel: TunnelContainer) -> String {
|
||||||
switch status {
|
if tunnel.hasOnDemandRules {
|
||||||
case .waiting:
|
let turnOn = !tunnel.isActivateOnDemandEnabled
|
||||||
return tr("macToggleStatusButtonWaiting")
|
if turnOn {
|
||||||
case .inactive:
|
return tr("macToggleStatusButtonEnableOnDemand")
|
||||||
return tr("macToggleStatusButtonActivate")
|
} else {
|
||||||
case .activating:
|
if tunnel.status == .active {
|
||||||
return tr("macToggleStatusButtonActivating")
|
return tr("macToggleStatusButtonDisableOnDemandDeactivate")
|
||||||
case .active:
|
} else {
|
||||||
return tr("macToggleStatusButtonDeactivate")
|
return tr("macToggleStatusButtonDisableOnDemand")
|
||||||
case .deactivating:
|
}
|
||||||
return tr("macToggleStatusButtonDeactivating")
|
}
|
||||||
case .reasserting:
|
} else {
|
||||||
return tr("macToggleStatusButtonReasserting")
|
switch tunnel.status {
|
||||||
case .restarting:
|
case .waiting:
|
||||||
return tr("macToggleStatusButtonRestarting")
|
return tr("macToggleStatusButtonWaiting")
|
||||||
|
case .inactive:
|
||||||
|
return tr("macToggleStatusButtonActivate")
|
||||||
|
case .activating:
|
||||||
|
return tr("macToggleStatusButtonActivating")
|
||||||
|
case .active:
|
||||||
|
return tr("macToggleStatusButtonDeactivate")
|
||||||
|
case .deactivating:
|
||||||
|
return tr("macToggleStatusButtonDeactivating")
|
||||||
|
case .reasserting:
|
||||||
|
return tr("macToggleStatusButtonReasserting")
|
||||||
|
case .restarting:
|
||||||
|
return tr("macToggleStatusButtonRestarting")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
protocol TunnelEditViewControllerDelegate: class {
|
protocol TunnelEditViewControllerDelegate: AnyObject {
|
||||||
func tunnelSaved(tunnel: TunnelContainer)
|
func tunnelSaved(tunnel: TunnelContainer)
|
||||||
func tunnelEditingCancelled()
|
func tunnelEditingCancelled()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
protocol TunnelsListTableViewControllerDelegate: class {
|
protocol TunnelsListTableViewControllerDelegate: AnyObject {
|
||||||
func tunnelsSelected(tunnelIndices: [Int])
|
func tunnelsSelected(tunnelIndices: [Int])
|
||||||
func tunnelsListEmpty()
|
func tunnelsListEmpty()
|
||||||
}
|
}
|
||||||
@@ -232,10 +232,19 @@ class TunnelsListTableViewController: NSViewController {
|
|||||||
let tunnelIndex = tableView.clickedRow
|
let tunnelIndex = tableView.clickedRow
|
||||||
guard tunnelIndex >= 0 && tunnelIndex < tunnelsManager.numberOfTunnels() else { return }
|
guard tunnelIndex >= 0 && tunnelIndex < tunnelsManager.numberOfTunnels() else { return }
|
||||||
let tunnel = tunnelsManager.tunnel(at: tunnelIndex)
|
let tunnel = tunnelsManager.tunnel(at: tunnelIndex)
|
||||||
if tunnel.status == .inactive {
|
if tunnel.hasOnDemandRules {
|
||||||
tunnelsManager.startActivation(of: tunnel)
|
let turnOn = !tunnel.isActivateOnDemandEnabled
|
||||||
} else if tunnel.status == .active {
|
tunnelsManager.setOnDemandEnabled(turnOn, on: tunnel) { error in
|
||||||
tunnelsManager.startDeactivation(of: tunnel)
|
if error == nil && !turnOn {
|
||||||
|
self.tunnelsManager.startDeactivation(of: tunnel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if tunnel.status == .inactive {
|
||||||
|
tunnelsManager.startActivation(of: tunnel)
|
||||||
|
} else if tunnel.status == .active {
|
||||||
|
tunnelsManager.startDeactivation(of: tunnel)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
protocol WireGuardAppError: Error {
|
protocol WireGuardAppError: Error {
|
||||||
typealias AlertText = (title: String, message: String)
|
typealias AlertText = (title: String, message: String)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
enum WireGuardResult<T> {
|
enum WireGuardResult<T> {
|
||||||
case success(_ value: T)
|
case success(_ value: T)
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
|||||||
@@ -148,7 +148,6 @@
|
|||||||
"alertInvalidInterfaceTitle" = "Invalid interface";
|
"alertInvalidInterfaceTitle" = "Invalid interface";
|
||||||
"tunnelSectionTitleStatus" = "Status";
|
"tunnelSectionTitleStatus" = "Status";
|
||||||
"macDeleteTunnelConfirmationAlertButtonTitleDelete" = "Delete";
|
"macDeleteTunnelConfirmationAlertButtonTitleDelete" = "Delete";
|
||||||
"alertTunnelActivationFailureOnDemandAddendum" = " This tunnel has Activate On Demand enabled, so this tunnel might be re-activated automatically by the OS. You may turn off Activate On Demand in this app by editing the tunnel configuration.";
|
|
||||||
"alertTunnelActivationFailureTitle" = "Activation failure";
|
"alertTunnelActivationFailureTitle" = "Activation failure";
|
||||||
"macLogButtonTitleClose" = "Close";
|
"macLogButtonTitleClose" = "Close";
|
||||||
"tunnelOnDemandSSIDViewTitle" = "SSIDs";
|
"tunnelOnDemandSSIDViewTitle" = "SSIDs";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
// Generic alert action names
|
// Generic alert action names
|
||||||
|
|
||||||
|
|
||||||
@@ -255,8 +255,6 @@
|
|||||||
"alertTunnelActivationFileDescriptorFailureMessage" = "TUN Dateideskriptor kann nicht ermittelt werden.";
|
"alertTunnelActivationFileDescriptorFailureMessage" = "TUN Dateideskriptor kann nicht ermittelt werden.";
|
||||||
"alertTunnelActivationSetNetworkSettingsMessage" = "Netzwerkeinstellungen können nicht auf Tunnelobjekt angewendet werden.";
|
"alertTunnelActivationSetNetworkSettingsMessage" = "Netzwerkeinstellungen können nicht auf Tunnelobjekt angewendet werden.";
|
||||||
|
|
||||||
"alertTunnelActivationFailureOnDemandAddendum" = " Dieser Tunnel hat 'Activate on Demand' aktiviert, so dass dieser Tunnel vom Betriebssystem automatisch aktiviert werden kann. Du kannst 'Activate on Demand' in dieser App deaktivieren, indem du die Tunnelkonfiguration änderst.";
|
|
||||||
|
|
||||||
"alertTunnelDNSFailureTitle" = "DNS-Auflösungsfehler";
|
"alertTunnelDNSFailureTitle" = "DNS-Auflösungsfehler";
|
||||||
"alertTunnelDNSFailureMessage" = "Eine oder mehrere Endpunkt-Domains konnten nicht aufgelöst werden.";
|
"alertTunnelDNSFailureMessage" = "Eine oder mehrere Endpunkt-Domains konnten nicht aufgelöst werden.";
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
// Generic alert action names
|
// Generic alert action names
|
||||||
|
|
||||||
|
|
||||||
@@ -368,7 +368,6 @@
|
|||||||
"alertTunnelNameEmptyMessage" = "Cannot create tunnel with an empty name";
|
"alertTunnelNameEmptyMessage" = "Cannot create tunnel with an empty name";
|
||||||
"alertTunnelNameEmptyTitle" = "No name provided";
|
"alertTunnelNameEmptyTitle" = "No name provided";
|
||||||
"macMenuAddEmptyTunnel" = "Add Empty Tunnel…";
|
"macMenuAddEmptyTunnel" = "Add Empty Tunnel…";
|
||||||
"alertTunnelActivationFailureOnDemandAddendum" = " This tunnel has Activate On Demand enabled, so this tunnel might be re-activated automatically by the OS. You may turn off Activate On Demand in this app by editing the tunnel configuration.";
|
|
||||||
"macViewPrivateData" = "view tunnel private keys";
|
"macViewPrivateData" = "view tunnel private keys";
|
||||||
"alertTunnelActivationErrorTunnelIsNotInactiveTitle" = "Activation in progress";
|
"alertTunnelActivationErrorTunnelIsNotInactiveTitle" = "Activation in progress";
|
||||||
"macAppExitingWithActiveTunnelInfo" = "The tunnel will remain active after exiting. You may disable it by reopening this application or through the Network panel in System Preferences.";
|
"macAppExitingWithActiveTunnelInfo" = "The tunnel will remain active after exiting. You may disable it by reopening this application or through the Network panel in System Preferences.";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
// Generic alert action names
|
// Generic alert action names
|
||||||
|
|
||||||
|
|
||||||
@@ -363,7 +363,6 @@
|
|||||||
"alertTunnelNameEmptyMessage" = "Cannot create tunnel with an empty name";
|
"alertTunnelNameEmptyMessage" = "Cannot create tunnel with an empty name";
|
||||||
"alertInvalidInterfaceMessageMTUInvalid" = "Interface’s MTU must be between 576 and 65535, or unspecified";
|
"alertInvalidInterfaceMessageMTUInvalid" = "Interface’s MTU must be between 576 and 65535, or unspecified";
|
||||||
"alertUnableToWriteLogMessage" = "Unable to write logs to file";
|
"alertUnableToWriteLogMessage" = "Unable to write logs to file";
|
||||||
"alertTunnelActivationFailureOnDemandAddendum" = " This tunnel has Activate On Demand enabled, so this tunnel might be re-activated automatically by the OS. You may turn off Activate On Demand in this app by editing the tunnel configuration.";
|
|
||||||
"alertInvalidPeerMessageEndpointInvalid" = "Peer’s endpoint must be of the form ‘host:port’ or ‘[host]:port’";
|
"alertInvalidPeerMessageEndpointInvalid" = "Peer’s endpoint must be of the form ‘host:port’ or ‘[host]:port’";
|
||||||
"alertInvalidPeerMessagePublicKeyDuplicated" = "Two or more peers cannot have the same public key";
|
"alertInvalidPeerMessagePublicKeyDuplicated" = "Two or more peers cannot have the same public key";
|
||||||
"alertInvalidPeerMessagePreSharedKeyInvalid" = "Peer’s preshared key must be a 32-byte key in base64 encoding";
|
"alertInvalidPeerMessagePreSharedKeyInvalid" = "Peer’s preshared key must be a 32-byte key in base64 encoding";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
// Generic alert action names
|
// Generic alert action names
|
||||||
|
|
||||||
|
|
||||||
@@ -368,7 +368,6 @@
|
|||||||
"macAlertInfoUnrecognizedInterfaceKey" = "Valid keys are: ‘PrivateKey’, ‘ListenPort’, ‘Address’, ‘DNS’ and ‘MTU’.";
|
"macAlertInfoUnrecognizedInterfaceKey" = "Valid keys are: ‘PrivateKey’, ‘ListenPort’, ‘Address’, ‘DNS’ and ‘MTU’.";
|
||||||
"alertTunnelNameEmptyMessage" = "Cannot create tunnel with an empty name";
|
"alertTunnelNameEmptyMessage" = "Cannot create tunnel with an empty name";
|
||||||
"alertUnableToWriteLogMessage" = "Unable to write logs to file";
|
"alertUnableToWriteLogMessage" = "Unable to write logs to file";
|
||||||
"alertTunnelActivationFailureOnDemandAddendum" = " This tunnel has Activate On Demand enabled, so this tunnel might be re-activated automatically by the OS. You may turn off Activate On Demand in this app by editing the tunnel configuration.";
|
|
||||||
"settingsExportZipButtonTitle" = "Export zip archive";
|
"settingsExportZipButtonTitle" = "Export zip archive";
|
||||||
"macViewPrivateData" = "view tunnel private keys";
|
"macViewPrivateData" = "view tunnel private keys";
|
||||||
"macAppExitingWithActiveTunnelInfo" = "The tunnel will remain active after exiting. You may disable it by reopening this application or through the Network panel in System Preferences.";
|
"macAppExitingWithActiveTunnelInfo" = "The tunnel will remain active after exiting. You may disable it by reopening this application or through the Network panel in System Preferences.";
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
// SPDX-License-Identifier: MIT
|
// SPDX-License-Identifier: MIT
|
||||||
// Copyright © 2018-2020 WireGuard LLC. All Rights Reserved.
|
// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved.
|
||||||
// Generic alert action names
|
// Generic alert action names
|
||||||
|
|
||||||
|
|
||||||
@@ -255,8 +255,6 @@
|
|||||||
"alertTunnelActivationFileDescriptorFailureMessage" = "Impossible de déterminer le descripteur de fichier TUN.";
|
"alertTunnelActivationFileDescriptorFailureMessage" = "Impossible de déterminer le descripteur de fichier TUN.";
|
||||||
"alertTunnelActivationSetNetworkSettingsMessage" = "Impossible d'appliquer les paramètres réseau à l'objet tunnel.";
|
"alertTunnelActivationSetNetworkSettingsMessage" = "Impossible d'appliquer les paramètres réseau à l'objet tunnel.";
|
||||||
|
|
||||||
"alertTunnelActivationFailureOnDemandAddendum" = " Ce tunnel a été activé à la demande, donc ce tunnel pourrait être réactivé automatiquement par l'OS. Vous pouvez désactiver l'activation à la demande dans cette application en modifiant la configuration du tunnel.";
|
|
||||||
|
|
||||||
"alertTunnelDNSFailureTitle" = "Échec de la résolution DNS";
|
"alertTunnelDNSFailureTitle" = "Échec de la résolution DNS";
|
||||||
"alertTunnelDNSFailureMessage" = "Un ou plusieurs domaines de terminaison n'ont pas pu être résolus.";
|
"alertTunnelDNSFailureMessage" = "Un ou plusieurs domaines de terminaison n'ont pas pu être résolus.";
|
||||||
|
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user