diff --git a/Sources/WireGuardApp/Tunnel/TunnelsManager.swift b/Sources/WireGuardApp/Tunnel/TunnelsManager.swift index c277f6d68..35cd8713e 100644 --- a/Sources/WireGuardApp/Tunnel/TunnelsManager.swift +++ b/Sources/WireGuardApp/Tunnel/TunnelsManager.swift @@ -206,6 +206,7 @@ class TunnelsManager { } } + @available(*, renamed: "modify(tunnel:tunnelConfiguration:onDemandOption:shouldEnsureOnDemandEnabled:)") func modify(tunnel: TunnelContainer, tunnelConfiguration: TunnelConfiguration, onDemandOption: ActivateOnDemandOption, shouldEnsureOnDemandEnabled: Bool = false, @@ -299,6 +300,22 @@ class TunnelsManager { } } + @available(iOS 13.0, macOS 10.15.0, *) + func modify(tunnel: TunnelContainer, tunnelConfiguration: TunnelConfiguration, + onDemandOption: ActivateOnDemandOption, + shouldEnsureOnDemandEnabled: Bool = false) async throws { + return try await withCheckedThrowingContinuation { continuation in + modify(tunnel: tunnel, tunnelConfiguration: tunnelConfiguration, onDemandOption: onDemandOption, shouldEnsureOnDemandEnabled: shouldEnsureOnDemandEnabled) { error in + if let error = error { + continuation.resume(throwing: error) + return + } + + continuation.resume(returning: ()) + } + } + } + func remove(tunnel: TunnelContainer, completionHandler: @escaping (TunnelsManagerError?) -> Void) { let tunnelProviderManager = tunnel.tunnelProvider #if os(macOS) diff --git a/Sources/WireGuardApp/UI/iOS/AppDelegate.swift b/Sources/WireGuardApp/UI/iOS/AppDelegate.swift index fbb09c7ee..7623b7eee 100644 --- a/Sources/WireGuardApp/UI/iOS/AppDelegate.swift +++ b/Sources/WireGuardApp/UI/iOS/AppDelegate.swift @@ -3,6 +3,7 @@ import UIKit import os.log +import AppIntents @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate { @@ -11,6 +12,10 @@ class AppDelegate: UIResponder, UIApplicationDelegate { var mainVC: MainViewController? var isLaunchedForSpecificAction = false + var tunnelsManager: TunnelsManager? + + static let tunnelsManagerReadyNotificationName: Notification.Name = Notification.Name(rawValue: "com.wireguard.ios.tunnelsManagerReadyNotification") + func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { Logger.configureGlobal(tagged: "APP", withFilePath: FileManager.logFileURL?.path) @@ -29,6 +34,29 @@ class AppDelegate: UIResponder, UIApplicationDelegate { self.mainVC = mainVC + // Create the tunnels manager, and when it's ready, inform tunnelsListVC + TunnelsManager.create { [weak self] result in + guard let self = self else { return } + + switch result { + case .failure(let error): + ErrorPresenter.showErrorAlert(error: error, from: self.mainVC) + case .success(let tunnelsManager): + self.tunnelsManager = tunnelsManager + self.mainVC?.tunnelsListVC?.setTunnelsManager(tunnelsManager: tunnelsManager) + + tunnelsManager.activationDelegate = self.mainVC + + if #available(iOS 16.0, *) { + AppDependencyManager.shared.add(dependency: tunnelsManager) + } + + NotificationCenter.default.post(name: AppDelegate.tunnelsManagerReadyNotificationName, + object: self, + userInfo: nil) + } + } + return true } diff --git a/Sources/WireGuardApp/UI/iOS/Info.plist b/Sources/WireGuardApp/UI/iOS/Info.plist index 7d910772d..bfe758dbc 100644 --- a/Sources/WireGuardApp/UI/iOS/Info.plist +++ b/Sources/WireGuardApp/UI/iOS/Info.plist @@ -2,10 +2,10 @@ - ITSAppUsesNonExemptEncryption - CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) + CFBundleDisplayName + $(PRODUCT_NAME) CFBundleDocumentTypes @@ -64,8 +64,6 @@ $(PRODUCT_BUNDLE_IDENTIFIER) CFBundleInfoDictionaryVersion 6.0 - CFBundleDisplayName - $(PRODUCT_NAME) CFBundleName $(PRODUCT_NAME) CFBundlePackageType @@ -74,17 +72,22 @@ $(VERSION_NAME) CFBundleVersion $(VERSION_ID) + ITSAppUsesNonExemptEncryption + LSRequiresIPhoneOS LSSupportsOpeningDocumentsInPlace NSCameraUsageDescription Localized + NSFaceIDUsageDescription + Localized + NSUserActivityTypes + UILaunchStoryboardName LaunchScreen UIRequiredDeviceCapabilities - - + UISupportedInterfaceOrientations UIInterfaceOrientationPortrait @@ -123,8 +126,6 @@ - NSFaceIDUsageDescription - Localized com.wireguard.ios.app_group_id group.$(APP_ID_IOS) diff --git a/Sources/WireGuardApp/UI/iOS/ViewController/MainViewController.swift b/Sources/WireGuardApp/UI/iOS/ViewController/MainViewController.swift index 854229604..39780f17a 100644 --- a/Sources/WireGuardApp/UI/iOS/ViewController/MainViewController.swift +++ b/Sources/WireGuardApp/UI/iOS/ViewController/MainViewController.swift @@ -5,7 +5,9 @@ import UIKit class MainViewController: UISplitViewController { - var tunnelsManager: TunnelsManager? + var tunnelsManager: TunnelsManager? { + return (UIApplication.shared.delegate as? AppDelegate)?.tunnelsManager + } var onTunnelsManagerReady: ((TunnelsManager) -> Void)? var tunnelsListVC: TunnelsListTableViewController? @@ -38,29 +40,24 @@ class MainViewController: UISplitViewController { // On iPad, always show both masterVC and detailVC, even in portrait mode, like the Settings app preferredDisplayMode = .allVisible - // Create the tunnels manager, and when it's ready, inform tunnelsListVC - TunnelsManager.create { [weak self] result in - guard let self = self else { return } - - switch result { - case .failure(let error): - ErrorPresenter.showErrorAlert(error: error, from: self) - case .success(let tunnelsManager): - self.tunnelsManager = tunnelsManager - self.tunnelsListVC?.setTunnelsManager(tunnelsManager: tunnelsManager) - - tunnelsManager.activationDelegate = self - - self.onTunnelsManagerReady?(tunnelsManager) - self.onTunnelsManagerReady = nil - } - } + NotificationCenter.default.addObserver(self, selector: #selector(handleTunnelsManagerReady(_:)), + name: AppDelegate.tunnelsManagerReadyNotificationName, object: nil) } func allTunnelNames() -> [String]? { guard let tunnelsManager = self.tunnelsManager else { return nil } return tunnelsManager.mapTunnels { $0.name } } + + @objc + func handleTunnelsManagerReady(_ notification: Notification) { + guard let tunnelsManager = self.tunnelsManager else { return } + + self.onTunnelsManagerReady?(tunnelsManager) + self.onTunnelsManagerReady = nil + + NotificationCenter.default.removeObserver(self, name: AppDelegate.tunnelsManagerReadyNotificationName, object: nil) + } } extension MainViewController: TunnelsManagerActivationDelegate { diff --git a/Sources/WireGuardApp/UI/macOS/AppDelegate.swift b/Sources/WireGuardApp/UI/macOS/AppDelegate.swift index 1dda3e61a..bd9654483 100644 --- a/Sources/WireGuardApp/UI/macOS/AppDelegate.swift +++ b/Sources/WireGuardApp/UI/macOS/AppDelegate.swift @@ -3,6 +3,7 @@ import Cocoa import ServiceManagement +import AppIntents @NSApplicationMain class AppDelegate: NSObject, NSApplicationDelegate { @@ -58,6 +59,10 @@ class AppDelegate: NSObject, NSApplicationDelegate { self.tunnelsTracker = tunnelsTracker self.statusItemController = statusItemController + if #available(macOS 13.0, *) { + AppDependencyManager.shared.add(dependency: tunnelsManager) + } + if !isLaunchedAtLogin { self.showManageTunnelsWindow(completion: nil) } diff --git a/Sources/WireguardAppIntents/AppIntents.strings b/Sources/WireguardAppIntents/AppIntents.strings new file mode 100644 index 000000000..e799409d1 --- /dev/null +++ b/Sources/WireguardAppIntents/AppIntents.strings @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: MIT +// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved. + +// App Intents Common +"wireguardAppIntentsWrongTunnelError %@" = "Cannot find a tunnel with the name \"%1$@\""; +"wireguardAppIntentsMissingConfigurationError" = "The selected tunnel has no configuration."; + +// Get peers Intent +"getPeersIntentName" = "Get Peers"; +"getPeersIntentDescription" = "Get list of public keys of peers in the selected configuration"; +"getPeersIntentTunnelParameterTitle" = "Tunnel"; +"getPeersIntentSummary ${tunnelName}" = "Get peers of ${tunnelName}"; + +// Tunnel Configuration Update +"updateTunnelConfigurationIntentName" = "Update Tunnel Configuration"; +"updateTunnelConfigurationDescription" = "Update peers configuration of the selected tunnel."; +"updateTunnelConfigurationIntentTunnelParameterTitle" = "Tunnel"; +"updateTunnelConfigurationIntentPeersParameterTitle" = "Peers"; +"updateTunnelConfigurationIntentMergeParameterTitle" = "Merge configuration"; +"updateTunnelConfigurationIntentSummary ${tunnelName}" = "Update ${tunnelName} configuration"; + +"updateTunnelConfigurationIntentPeerOptionsUnavailableError" = "Use the output of \"Build Peer Configuration\" action to update tunnel configuration. To update multiple peers at once, you can use the \"Add to Variable\" action and pass the resulting Variable in this field."; +"updateTunnelConfigurationIntentMalformedPublicKeyError %@" = "The key \"%1$@\" is not a valid Public Key encoded in Base64 format."; + +// Build Peer Configuration +"buildPeerConfigurationUpdateIntentName" = "Build Peer Configuration"; +"buildPeerConfigurationUpdateIntentDescription" = "Build an item that contains the informations needed to update an peer's configuration."; +"buildPeerConfigurationUpdateIntentSummary ${publicKey}" = "Build configuration for peer ${publicKey}"; +"buildPeerConfigurationUpdateIntentPublicKeyParameterTitle" = "Peer Public Key"; +"buildPeerConfigurationUpdateIntentEndpointParameterTitle" = "Endpoint"; + +// Peer Configuration Update Entity +"peerConfigurationUpdateEntityName" = "Configuration Update - Peer"; +"peerConfigurationUpdateEntityPropertyPublicKeyTitle" = "Public Key"; +"peerConfigurationUpdateEntityPropertyEndpointTitle" = "Endpoint"; diff --git a/Sources/WireguardAppIntents/BuildPeerConfigurationUpdate.swift b/Sources/WireguardAppIntents/BuildPeerConfigurationUpdate.swift new file mode 100644 index 000000000..d5f8cbfdd --- /dev/null +++ b/Sources/WireguardAppIntents/BuildPeerConfigurationUpdate.swift @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: MIT +// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved. + +import AppIntents + +@available(iOS 16.0, macOS 13.0, watchOS 9.0, tvOS 16.0, *) +struct BuildPeerConfigurationUpdate: AppIntent { + + static var title = LocalizedStringResource("buildPeerConfigurationUpdateIntentName", table: "AppIntents") + static var description = IntentDescription( + LocalizedStringResource("buildPeerConfigurationUpdateIntentDescription", table: "AppIntents") + ) + + @Parameter( + title: LocalizedStringResource("buildPeerConfigurationUpdateIntentPublicKeyParameterTitle", table: "AppIntents") + ) + var publicKey: String + + @Parameter( + title: LocalizedStringResource("buildPeerConfigurationUpdateIntentEndpointParameterTitle", table: "AppIntents") + ) + var endpoint: String + + func perform() async throws -> some IntentResult & ReturnsValue { + let peerConfigurationUpdate = AppIntentsPeer() + peerConfigurationUpdate.publicKey = publicKey + peerConfigurationUpdate.endpoint = endpoint + + return .result(value: peerConfigurationUpdate) + } + + static var parameterSummary: some ParameterSummary { + Summary("buildPeerConfigurationUpdateIntentSummary \(\.$publicKey)", table: "AppIntents") { + \.$endpoint + } + } +} + +@available(iOS 16.0, macOS 13.0, watchOS 9.0, tvOS 16.0, *) +struct AppIntentsPeer: TransientAppEntity { + + static let kEndpointConfigUpdateDictionaryKey = "Endpoint" + + static var typeDisplayRepresentation = TypeDisplayRepresentation( + name: LocalizedStringResource("peerConfigurationUpdateEntityName", table: "AppIntents") + ) + + @Property( + title: LocalizedStringResource("peerConfigurationUpdateEntityPropertyPublicKeyTitle", table: "AppIntents") + ) + var publicKey: String + + @Property( + title: LocalizedStringResource("peerConfigurationUpdateEntityPropertyEndpointTitle", table: "AppIntents") + ) + var endpoint: String? + + var displayRepresentation: DisplayRepresentation { + var dictionary: [String: [String: String]] = [:] + dictionary[publicKey] = [:] + + if let endpoint { + dictionary[publicKey]?.updateValue(endpoint, forKey: Self.kEndpointConfigUpdateDictionaryKey) + } + + let jsonData: Data + do { + jsonData = try JSONSerialization.data(withJSONObject: dictionary) + } catch { + return DisplayRepresentation(stringLiteral: error.localizedDescription) + } + + let jsonString = String(data: jsonData, encoding: .utf8)! + + return DisplayRepresentation(stringLiteral: jsonString) + } +} diff --git a/Sources/WireguardAppIntents/GetPeers.swift b/Sources/WireguardAppIntents/GetPeers.swift new file mode 100644 index 000000000..00e539910 --- /dev/null +++ b/Sources/WireguardAppIntents/GetPeers.swift @@ -0,0 +1,55 @@ +// SPDX-License-Identifier: MIT +// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved. + +import Foundation +import AppIntents + +@available(iOS 16.0, macOS 13.0, watchOS 9.0, tvOS 16.0, *) +struct GetPeers: AppIntent { + + static var title = LocalizedStringResource("getPeersIntentName", table: "AppIntents") + static var description = IntentDescription( + LocalizedStringResource("getPeersIntentDescription", table: "AppIntents") + ) + + @Parameter( + title: LocalizedStringResource("getPeersIntentTunnelParameterTitle", table: "AppIntents"), + optionsProvider: TunnelsOptionsProvider() + ) + var tunnelName: String + + @Dependency + var tunnelsManager: TunnelsManager + + func perform() async throws -> some IntentResult & ReturnsValue<[String]> { + guard let tunnelContainer = tunnelsManager.tunnel(named: tunnelName) else { + throw GetPeersIntentError.wrongTunnel(name: tunnelName) + } + + guard let tunnelConfiguration = tunnelContainer.tunnelConfiguration else { + throw GetPeersIntentError.missingConfiguration + } + + let publicKeys = tunnelConfiguration.peers.map { $0.publicKey.base64Key } + return .result(value: publicKeys) + } + + static var parameterSummary: some ParameterSummary { + Summary("getPeersIntentSummary \(\.$tunnelName)", table: "AppIntents") + } +} + +@available(iOS 16.0, macOS 13.0, watchOS 9.0, tvOS 16.0, *) +enum GetPeersIntentError: Swift.Error, CustomLocalizedStringResourceConvertible { + case wrongTunnel(name: String) + case missingConfiguration + + var localizedStringResource: LocalizedStringResource { + switch self { + case .wrongTunnel(let name): + return LocalizedStringResource("wireguardAppIntentsWrongTunnelError \(name)", table: "AppIntents") + case .missingConfiguration: + return LocalizedStringResource("wireguardAppIntentsMissingConfigurationError", table: "AppIntents") + } + } +} diff --git a/Sources/WireguardAppIntents/TunnelsOptionsProvider.swift b/Sources/WireguardAppIntents/TunnelsOptionsProvider.swift new file mode 100644 index 000000000..a21109c5d --- /dev/null +++ b/Sources/WireguardAppIntents/TunnelsOptionsProvider.swift @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: MIT +// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved. + +import AppIntents + +@available(iOS 16.0, macOS 13.0, watchOS 9.0, tvOS 16.0, *) +struct TunnelsOptionsProvider: DynamicOptionsProvider { + @Dependency + var tunnelsManager: TunnelsManager + + func results() async throws -> [String] { + let tunnelsNames = tunnelsManager.mapTunnels { $0.name } + return tunnelsNames + } +} diff --git a/Sources/WireguardAppIntents/UpdateTunnelConfiguration.swift b/Sources/WireguardAppIntents/UpdateTunnelConfiguration.swift new file mode 100644 index 000000000..68e88fae2 --- /dev/null +++ b/Sources/WireguardAppIntents/UpdateTunnelConfiguration.swift @@ -0,0 +1,131 @@ +// SPDX-License-Identifier: MIT +// Copyright © 2018-2021 WireGuard LLC. All Rights Reserved. + +import AppIntents + +@available(iOS 16.0, macOS 13.0, watchOS 9.0, tvOS 16.0, *) +struct UpdateTunnelConfiguration: AppIntent { + + static var title = LocalizedStringResource("updateTunnelConfigurationIntentName", table: "AppIntents") + static var description = IntentDescription( + LocalizedStringResource("updateTunnelConfigurationDescription", table: "AppIntents") + ) + + @Parameter( + title: LocalizedStringResource("updateTunnelConfigurationIntentTunnelParameterTitle", table: "AppIntents"), + optionsProvider: TunnelsOptionsProvider() + ) + var tunnelName: String + + @Parameter( + title: LocalizedStringResource("updateTunnelConfigurationIntentPeersParameterTitle", table: "AppIntents"), + optionsProvider: AppIntentsPeerOptionsProvider() + ) + var peers: [AppIntentsPeer]? + + @Parameter( + title: LocalizedStringResource("updateTunnelConfigurationIntentMergeParameterTitle", table: "AppIntents"), + default: true + ) + var mergeConfiguration: Bool + + @Dependency + var tunnelsManager: TunnelsManager + + func perform() async throws -> some IntentResult { + let peers = peers ?? [] + + guard let tunnelContainer = tunnelsManager.tunnel(named: tunnelName) else { + throw AppIntentConfigurationUpdateError.wrongTunnel(name: tunnelName) + } + + guard let tunnelConfiguration = tunnelContainer.tunnelConfiguration else { + throw AppIntentConfigurationUpdateError.missingConfiguration + } + + let newConfiguration = try buildNewConfiguration(from: tunnelConfiguration, peersUpdates: peers, mergeChanges: mergeConfiguration) + + do { + try await tunnelsManager.modify(tunnel: tunnelContainer, tunnelConfiguration: newConfiguration, onDemandOption: tunnelContainer.onDemandOption) + } catch { + wg_log(.error, message: error.localizedDescription) + throw error + } + + wg_log(.debug, message: "Updated configuration of tunnel \(tunnelName)") + + return .result() + } + + static var parameterSummary: some ParameterSummary { + Summary("updateTunnelConfigurationIntentSummary \(\.$tunnelName)", table: "AppIntents") { + \.$peers + \.$mergeConfiguration + } + } + + private func buildNewConfiguration(from oldConfiguration: TunnelConfiguration, peersUpdates: [AppIntentsPeer], mergeChanges: Bool) throws -> TunnelConfiguration { + var peers = oldConfiguration.peers + + for peerUpdate in peersUpdates { + let peerIndex: Array.Index + if let foundIndex = peers.firstIndex(where: { $0.publicKey.base64Key == peerUpdate.publicKey }) { + peerIndex = foundIndex + if mergeChanges == false { + peers[peerIndex] = PeerConfiguration(publicKey: peers[peerIndex].publicKey) + } + } else { + wg_log(.debug, message: "Failed to find peer \(peerUpdate.publicKey) in tunnel with name \(tunnelName). Adding it.") + + guard let pubKeyEncoded = PublicKey(base64Key: peerUpdate.publicKey) else { + throw AppIntentConfigurationUpdateError.malformedPublicKey(key: peerUpdate.publicKey) + } + let newPeerConfig = PeerConfiguration(publicKey: pubKeyEncoded) + peerIndex = peers.endIndex + peers.append(newPeerConfig) + } + + if let endpointString = peerUpdate.endpoint { + if let newEntpoint = Endpoint(from: endpointString) { + peers[peerIndex].endpoint = newEntpoint + } else { + wg_log(.debug, message: "Failed to convert \(endpointString) to Endpoint") + } + } + } + + let newConfiguration = TunnelConfiguration(name: oldConfiguration.name, interface: oldConfiguration.interface, peers: peers) + return newConfiguration + } +} + +@available(iOS 16.0, macOS 13.0, watchOS 9.0, tvOS 16.0, *) +struct AppIntentsPeerOptionsProvider: DynamicOptionsProvider { + + func results() async throws -> ItemCollection { + // The error thrown here is not displayed correctly to the user. A Feedback + // has been opened (FB12098463). + throw AppIntentConfigurationUpdateError.peerOptionsUnavailable + } +} + +@available(iOS 16.0, macOS 13.0, watchOS 9.0, tvOS 16.0, *) +enum AppIntentConfigurationUpdateError: Swift.Error, CustomLocalizedStringResourceConvertible { + case wrongTunnel(name: String) + case missingConfiguration + case peerOptionsUnavailable + case malformedPublicKey(key: String) + + var localizedStringResource: LocalizedStringResource { + switch self { + case .wrongTunnel(let name): + return LocalizedStringResource("wireguardAppIntentsWrongTunnelError \(name)", table: "AppIntents") + case .missingConfiguration: + return LocalizedStringResource("wireguardAppIntentsMissingConfigurationError", table: "AppIntents") + case .peerOptionsUnavailable: + return LocalizedStringResource("updateTunnelConfigurationIntentPeerOptionsUnavailableError", table: "AppIntents") + case .malformedPublicKey(let malformedKey): + return LocalizedStringResource("updateTunnelConfigurationIntentMalformedPublicKeyError \(malformedKey)", table: "AppIntents") + } + } +} diff --git a/WireGuard.xcodeproj/project.pbxproj b/WireGuard.xcodeproj/project.pbxproj index bc7c35760..8418dfcdd 100644 --- a/WireGuard.xcodeproj/project.pbxproj +++ b/WireGuard.xcodeproj/project.pbxproj @@ -205,6 +205,16 @@ 6FFA5DA021958ECC0001E2F7 /* ErrorNotifier.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFA5D9F21958ECC0001E2F7 /* ErrorNotifier.swift */; }; 6FFA5DA42197085D0001E2F7 /* ActivateOnDemandOption.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFA5DA32197085D0001E2F7 /* ActivateOnDemandOption.swift */; }; 6FFACD2021E4D8D500E9A2A5 /* ParseError+WireGuardAppError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6FFACD1E21E4D89600E9A2A5 /* ParseError+WireGuardAppError.swift */; }; + A25DF37029E60E870094E89B /* BuildPeerConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6E361FB29D9AEEA00FFF234 /* BuildPeerConfigurationUpdate.swift */; }; + A25DF37129E60E8C0094E89B /* UpdateTunnelConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6E361F929D9821100FFF234 /* UpdateTunnelConfiguration.swift */; }; + A25DF37229E60E8F0094E89B /* GetPeers.swift in Sources */ = {isa = PBXBuildFile; fileRef = A625F05029C4C627005EF23D /* GetPeers.swift */; }; + A25DF37329E60E930094E89B /* AppIntents.strings in Resources */ = {isa = PBXBuildFile; fileRef = A6E361F729D8758500FFF234 /* AppIntents.strings */; }; + A25DF37429E60E960094E89B /* TunnelsOptionsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6E361FD29D9B18C00FFF234 /* TunnelsOptionsProvider.swift */; }; + A625F05529C4C627005EF23D /* GetPeers.swift in Sources */ = {isa = PBXBuildFile; fileRef = A625F05029C4C627005EF23D /* GetPeers.swift */; }; + A6E361F829D8758500FFF234 /* AppIntents.strings in Resources */ = {isa = PBXBuildFile; fileRef = A6E361F729D8758500FFF234 /* AppIntents.strings */; }; + A6E361FA29D9821200FFF234 /* UpdateTunnelConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6E361F929D9821100FFF234 /* UpdateTunnelConfiguration.swift */; }; + A6E361FC29D9AEEA00FFF234 /* BuildPeerConfigurationUpdate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6E361FB29D9AEEA00FFF234 /* BuildPeerConfigurationUpdate.swift */; }; + A6E361FE29D9B18C00FFF234 /* TunnelsOptionsProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = A6E361FD29D9B18C00FFF234 /* TunnelsOptionsProvider.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -347,8 +357,6 @@ 6F6483E6229293300075BA15 /* LaunchedAtLoginDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchedAtLoginDetector.swift; sourceTree = ""; }; 6F689999218043390012E523 /* WireGuard-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "WireGuard-Bridging-Header.h"; sourceTree = ""; }; 6F70E20D221058DF008BDFB4 /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Sources/WireGuardApp/Base.lproj/InfoPlist.strings; sourceTree = ""; }; - 6F70E20D221058DF008BDFBA /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "Sources/WireGuardApp/zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; - 6F70E20D221058DF008BDFC6 /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "Sources/WireGuardApp/zh-Hant.lproj/Localizable.strings"; sourceTree = ""; }; 6F70E22922106A2D008BDFB4 /* WireGuardLoginItemHelper.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = WireGuardLoginItemHelper.app; sourceTree = BUILT_PRODUCTS_DIR; }; 6F70E23222106A31008BDFB4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 6F70E23922109BEF008BDFB4 /* LoginItemHelper.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = LoginItemHelper.entitlements; sourceTree = ""; }; @@ -404,24 +412,24 @@ 6FDEF801218646B900D8FBF6 /* ZipArchive.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ZipArchive.swift; sourceTree = ""; }; 6FDEF805218725D200D8FBF6 /* SettingsTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsTableViewController.swift; sourceTree = ""; }; 6FE1765521C90BBE002690EA /* Base */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = Base; path = Sources/WireGuardApp/Base.lproj/Localizable.strings; sourceTree = ""; }; - 6FE1765521C90BBE002690FC /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh-Hant; path = Sources/WireGuardApp/zh-Hant.lproj/Localizable.strings; sourceTree = ""; }; - 6FE1765521C90BBE002690F0 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = zh-Hans; path = Sources/WireGuardApp/zh-Hans.lproj/Localizable.strings; sourceTree = ""; }; - 6FE1765521C90BBE002690FB /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = Sources/WireGuardApp/id.lproj/Localizable.strings; sourceTree = ""; }; - 6FE1765521C90BBE002690F2 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = Sources/WireGuardApp/it.lproj/Localizable.strings; sourceTree = ""; }; - 6FE1765521C90BBE002690F9 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = Sources/WireGuardApp/de.lproj/Localizable.strings; sourceTree = ""; }; 6FE1765521C90BBE002690EB /* fr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fr; path = Sources/WireGuardApp/fr.lproj/Localizable.strings; sourceTree = ""; }; - 6FE1765521C90BBE002690F5 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = Sources/WireGuardApp/fi.lproj/Localizable.strings; sourceTree = ""; }; - 6FE1765521C90BBE002690F4 /* fa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fa; path = Sources/WireGuardApp/fa.lproj/Localizable.strings; sourceTree = ""; }; - 6FE1765521C90BBE002690FA /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = Sources/WireGuardApp/sl.lproj/Localizable.strings; sourceTree = ""; }; 6FE1765521C90BBE002690EC /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = Sources/WireGuardApp/pl.lproj/Localizable.strings; sourceTree = ""; }; - 6FE1765521C90BBE002690EF /* pa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pa; path = Sources/WireGuardApp/pa.lproj/Localizable.strings; sourceTree = ""; }; - 6FE1765521C90BBE002690F7 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = Sources/WireGuardApp/ko.lproj/Localizable.strings; sourceTree = ""; }; + 6FE1765521C90BBE002690ED /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = Sources/WireGuardApp/es.lproj/Localizable.strings; sourceTree = ""; }; 6FE1765521C90BBE002690EE /* ca */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ca; path = Sources/WireGuardApp/ca.lproj/Localizable.strings; sourceTree = ""; }; - 6FE1765521C90BBE002690F6 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = Sources/WireGuardApp/ru.lproj/Localizable.strings; sourceTree = ""; }; + 6FE1765521C90BBE002690EF /* pa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pa; path = Sources/WireGuardApp/pa.lproj/Localizable.strings; sourceTree = ""; }; + 6FE1765521C90BBE002690F0 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "Sources/WireGuardApp/zh-Hans.lproj/Localizable.strings"; sourceTree = ""; }; + 6FE1765521C90BBE002690F1 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = Sources/WireGuardApp/ja.lproj/Localizable.strings; sourceTree = ""; }; + 6FE1765521C90BBE002690F2 /* it */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = it; path = Sources/WireGuardApp/it.lproj/Localizable.strings; sourceTree = ""; }; 6FE1765521C90BBE002690F3 /* ro */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ro; path = Sources/WireGuardApp/ro.lproj/Localizable.strings; sourceTree = ""; }; + 6FE1765521C90BBE002690F4 /* fa */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fa; path = Sources/WireGuardApp/fa.lproj/Localizable.strings; sourceTree = ""; }; + 6FE1765521C90BBE002690F5 /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = Sources/WireGuardApp/fi.lproj/Localizable.strings; sourceTree = ""; }; + 6FE1765521C90BBE002690F6 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = Sources/WireGuardApp/ru.lproj/Localizable.strings; sourceTree = ""; }; + 6FE1765521C90BBE002690F7 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = Sources/WireGuardApp/ko.lproj/Localizable.strings; sourceTree = ""; }; 6FE1765521C90BBE002690F8 /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = Sources/WireGuardApp/tr.lproj/Localizable.strings; sourceTree = ""; }; - 6FE1765521C90BBE002690F1 /* ja */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ja; path = Sources/WireGuardApp/ja.lproj/Localizable.strings; sourceTree = ""; }; - 6FE1765521C90BBE002690ED /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = Sources/WireGuardApp/es.lproj/Localizable.strings; sourceTree = ""; }; + 6FE1765521C90BBE002690F9 /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = Sources/WireGuardApp/de.lproj/Localizable.strings; sourceTree = ""; }; + 6FE1765521C90BBE002690FA /* sl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sl; path = Sources/WireGuardApp/sl.lproj/Localizable.strings; sourceTree = ""; }; + 6FE1765521C90BBE002690FB /* id */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = id; path = Sources/WireGuardApp/id.lproj/Localizable.strings; sourceTree = ""; }; + 6FE1765521C90BBE002690FC /* zh-Hant */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hant"; path = "Sources/WireGuardApp/zh-Hant.lproj/Localizable.strings"; sourceTree = ""; }; 6FE1765921C90E87002690EA /* LocalizationHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalizationHelper.swift; sourceTree = ""; }; 6FE254FA219C10800028284D /* ZipImporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZipImporter.swift; sourceTree = ""; }; 6FE254FE219C60290028284D /* ZipExporter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ZipExporter.swift; sourceTree = ""; }; @@ -440,6 +448,11 @@ 6FFA5D9F21958ECC0001E2F7 /* ErrorNotifier.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorNotifier.swift; sourceTree = ""; }; 6FFA5DA32197085D0001E2F7 /* ActivateOnDemandOption.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivateOnDemandOption.swift; sourceTree = ""; }; 6FFACD1E21E4D89600E9A2A5 /* ParseError+WireGuardAppError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ParseError+WireGuardAppError.swift"; sourceTree = ""; }; + A625F05029C4C627005EF23D /* GetPeers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GetPeers.swift; sourceTree = ""; }; + A6E361F729D8758500FFF234 /* AppIntents.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = AppIntents.strings; sourceTree = ""; }; + A6E361F929D9821100FFF234 /* UpdateTunnelConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateTunnelConfiguration.swift; sourceTree = ""; }; + A6E361FB29D9AEEA00FFF234 /* BuildPeerConfigurationUpdate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BuildPeerConfigurationUpdate.swift; sourceTree = ""; }; + A6E361FD29D9B18C00FFF234 /* TunnelsOptionsProvider.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelsOptionsProvider.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -762,6 +775,7 @@ 6FF4AC0B211EC46F002C96EB = { isa = PBXGroup; children = ( + A625F04C29C4C627005EF23D /* WireguardAppIntents */, 6F70E20C221058DF008BDFB4 /* InfoPlist.strings */, 6FE1765421C90BBE002690EA /* Localizable.strings */, 6F5D0C432183B4A4000F85AD /* Shared */, @@ -813,6 +827,19 @@ name = Frameworks; sourceTree = ""; }; + A625F04C29C4C627005EF23D /* WireguardAppIntents */ = { + isa = PBXGroup; + children = ( + A6E361F929D9821100FFF234 /* UpdateTunnelConfiguration.swift */, + A6E361FB29D9AEEA00FFF234 /* BuildPeerConfigurationUpdate.swift */, + A625F05029C4C627005EF23D /* GetPeers.swift */, + A6E361F729D8758500FFF234 /* AppIntents.strings */, + A6E361FD29D9B18C00FFF234 /* TunnelsOptionsProvider.swift */, + ); + name = WireguardAppIntents; + path = Sources/WireguardAppIntents; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXLegacyTarget section */ @@ -960,7 +987,7 @@ 6FF4AC0C211EC46F002C96EB /* Project object */ = { isa = PBXProject; attributes = { - LastSwiftUpdateCheck = 1010; + LastSwiftUpdateCheck = 1320; LastUpgradeCheck = 1010; ORGANIZATIONNAME = "WireGuard LLC"; TargetAttributes = { @@ -1078,6 +1105,7 @@ files = ( 6FB1BD6221D2607E00A991BF /* Assets.xcassets in Resources */, 6F70E20F221058E1008BDFB4 /* InfoPlist.strings in Resources */, + A25DF37329E60E930094E89B /* AppIntents.strings in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1097,6 +1125,7 @@ 6FE1765621C90BBE002690EA /* Localizable.strings in Resources */, 6FF4AC22211EC472002C96EB /* LaunchScreen.storyboard in Resources */, 6F919EDA218C65C50023B400 /* wireguard_doc_logo_44x58.png in Resources */, + A6E361F829D8758500FFF234 /* AppIntents.strings in Resources */, 6FF4AC1F211EC472002C96EB /* Assets.xcassets in Resources */, 6F919EDB218C65C50023B400 /* wireguard_doc_logo_64x64.png in Resources */, 6F919EDC218C65C50023B400 /* wireguard_doc_logo_320x320.png in Resources */, @@ -1305,6 +1334,7 @@ 6FB1BDD121D50F5300A991BF /* ZipImporter.swift in Sources */, 6FB1BDD221D50F5300A991BF /* ZipExporter.swift in Sources */, 585B10642577E294004F691E /* DNSServer.swift in Sources */, + A25DF37129E60E8C0094E89B /* UpdateTunnelConfiguration.swift in Sources */, 585B108C2577E294004F691E /* Endpoint.swift in Sources */, 6FBA104621D7EBFA0051C35F /* TunnelsListTableViewController.swift in Sources */, 6FB1BDD321D50F5300A991BF /* ZipArchive.swift in Sources */, @@ -1342,11 +1372,13 @@ 6FE3661D21F64F6B00F78C7D /* ConfTextColorTheme.swift in Sources */, 6F3E02E9228000F6001FE7E3 /* MainMenu.swift in Sources */, 5F52D0BF21E3788900283CEA /* NSColor+Hex.swift in Sources */, + A25DF37229E60E8F0094E89B /* GetPeers.swift in Sources */, 6FB1BDBE21D50F0200A991BF /* Logger.swift in Sources */, 6F6483E7229293300075BA15 /* LaunchedAtLoginDetector.swift in Sources */, 6FB1BDBF21D50F0200A991BF /* TunnelConfiguration+WgQuickConfig.swift in Sources */, 6FADE96C2254B8C300B838A4 /* UnusableTunnelDetailViewController.swift in Sources */, 6FFACD2021E4D8D500E9A2A5 /* ParseError+WireGuardAppError.swift in Sources */, + A25DF37029E60E870094E89B /* BuildPeerConfigurationUpdate.swift in Sources */, 6FB1BDC021D50F0200A991BF /* NETunnelProviderProtocol+Extension.swift in Sources */, 6F1075642258AE9800D78929 /* DeleteTunnelsConfirmationAlert.swift in Sources */, 6FBA101821D656000051C35F /* StatusMenu.swift in Sources */, @@ -1357,6 +1389,7 @@ 6FBA104021D6B7040051C35F /* ErrorPresenterProtocol.swift in Sources */, 6FCD99AA21E0E14700BA4C82 /* ButtonedDetailViewController.swift in Sources */, 6FBA104321D6BC250051C35F /* ErrorPresenter.swift in Sources */, + A25DF37429E60E960094E89B /* TunnelsOptionsProvider.swift in Sources */, 6F2449E8226587B90047B9E9 /* MacAppStoreUpdateDetector.swift in Sources */, 585B105C2577E293004F691E /* InterfaceConfiguration.swift in Sources */, 6F907C9D224663A2003CED21 /* LogViewHelper.swift in Sources */, @@ -1407,6 +1440,7 @@ 6FF3527221C2616C0008484E /* ringlogger.c in Sources */, 6F0F44CB222D55FD00B0FF04 /* EditableTextCell.swift in Sources */, 585B105E2577E293004F691E /* PeerConfiguration.swift in Sources */, + A6E361FA29D9821200FFF234 /* UpdateTunnelConfiguration.swift in Sources */, 6FF3527321C2616C0008484E /* Logger.swift in Sources */, 6F7774E421718281006A79B3 /* TunnelsListTableViewController.swift in Sources */, 585B108E2577E294004F691E /* x25519.c in Sources */, @@ -1421,8 +1455,10 @@ 6FDB6D18224CC05A00EE4BC3 /* LogViewController.swift in Sources */, 6FFA5D952194454A0001E2F7 /* NETunnelProviderProtocol+Extension.swift in Sources */, 5F4541A921C451D100994C13 /* TunnelStatus.swift in Sources */, + A625F05529C4C627005EF23D /* GetPeers.swift in Sources */, 6F8F0D7422267AD2000E8335 /* ChevronCell.swift in Sources */, 6F61F1E921B932F700483816 /* WireGuardAppError.swift in Sources */, + A6E361FE29D9B18C00FFF234 /* TunnelsOptionsProvider.swift in Sources */, 6F7774E2217181B1006A79B3 /* AppDelegate.swift in Sources */, 6FDEF80021863C0100D8FBF6 /* ioapi.c in Sources */, 6F7F7E5F21C7D74B00527607 /* TunnelErrors.swift in Sources */, @@ -1437,6 +1473,7 @@ 6F919EC3218A2AE90023B400 /* ErrorPresenter.swift in Sources */, 6B62E45F220A6FA900EF34A6 /* PrivateDataConfirmation.swift in Sources */, 6F5A2B4821AFF49A0081EDD8 /* FileManager+Extension.swift in Sources */, + A6E361FC29D9AEEA00FFF234 /* BuildPeerConfigurationUpdate.swift in Sources */, 5F45418C21C2D48200994C13 /* TunnelEditKeyValueCell.swift in Sources */, 6FE254FB219C10800028284D /* ZipImporter.swift in Sources */, 585B107E2577E294004F691E /* PrivateKey.swift in Sources */,