Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d20daa345a | ||
|
|
168ba2da8a | ||
|
|
714d6a41bd | ||
|
|
9b92a8f933 | ||
|
|
5e9780ef8f | ||
|
|
9faf814e8b | ||
|
|
30da10a0e9 | ||
|
|
a18614d6b3 | ||
|
|
5100e597aa | ||
|
|
0340641c4c | ||
|
|
813dea6902 | ||
|
|
c30d491edc | ||
|
|
88c80d6694 | ||
|
|
393718dfaf | ||
|
|
f852b6f919 | ||
|
|
8926434682 | ||
|
|
493c7b102e | ||
|
|
717bc8a26f | ||
|
|
e582155a10 | ||
|
|
70d19691a7 | ||
|
|
40b1f0bac8 | ||
|
|
52ac9b82c2 | ||
|
|
fc1fdbbcdb | ||
|
|
300268daa0 | ||
|
|
9bf304a9ac | ||
|
|
586a592b68 | ||
|
|
c0526d2efb | ||
|
|
fdbd4f875e | ||
|
|
4a037cc706 | ||
|
|
5190fc2249 | ||
|
|
3f25d54dcc | ||
|
|
f9880907a2 | ||
|
|
404fa741e8 | ||
|
|
6e1f03e41c | ||
|
|
6d8965e97d | ||
|
|
5e5481b69b | ||
|
|
69b33c0fad | ||
|
|
167e4f0bf2 | ||
|
|
6e3b28852a | ||
|
|
5914e868ab | ||
|
|
83ea9d6fa7 |
@@ -53,6 +53,9 @@
|
|||||||
6F1075642258AE9800D78929 /* DeleteTunnelsConfirmationAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1075632258AE9800D78929 /* DeleteTunnelsConfirmationAlert.swift */; };
|
6F1075642258AE9800D78929 /* DeleteTunnelsConfirmationAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F1075632258AE9800D78929 /* DeleteTunnelsConfirmationAlert.swift */; };
|
||||||
6F19D30422402B8700A126F2 /* ConfirmationAlertPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F19D30322402B8700A126F2 /* ConfirmationAlertPresenter.swift */; };
|
6F19D30422402B8700A126F2 /* ConfirmationAlertPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F19D30322402B8700A126F2 /* ConfirmationAlertPresenter.swift */; };
|
||||||
6F2449E8226587B90047B9E9 /* MacAppStoreUpdateDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2449E7226587B80047B9E9 /* MacAppStoreUpdateDetector.swift */; };
|
6F2449E8226587B90047B9E9 /* MacAppStoreUpdateDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F2449E7226587B80047B9E9 /* MacAppStoreUpdateDetector.swift */; };
|
||||||
|
6F3E02E9228000F6001FE7E3 /* MainMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F3E02E8228000F6001FE7E3 /* MainMenu.swift */; };
|
||||||
|
6F29A9432278518D00DC6A6B /* RecentTunnelsTracker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F29A9422278518D00DC6A6B /* RecentTunnelsTracker.swift */; };
|
||||||
|
6F29A94722787B1600DC6A6B /* QuickActionItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F29A94622787B1600DC6A6B /* QuickActionItem.swift */; };
|
||||||
6F4DD16B21DA558800690EAE /* TunnelListRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4DD16A21DA558800690EAE /* TunnelListRow.swift */; };
|
6F4DD16B21DA558800690EAE /* TunnelListRow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4DD16A21DA558800690EAE /* TunnelListRow.swift */; };
|
||||||
6F4DD16C21DA558F00690EAE /* NSTableView+Reuse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4DD16721DA552B00690EAE /* NSTableView+Reuse.swift */; };
|
6F4DD16C21DA558F00690EAE /* NSTableView+Reuse.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4DD16721DA552B00690EAE /* NSTableView+Reuse.swift */; };
|
||||||
6F4DD16E21DBEA0700690EAE /* ManageTunnelsRootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4DD16D21DBEA0700690EAE /* ManageTunnelsRootViewController.swift */; };
|
6F4DD16E21DBEA0700690EAE /* ManageTunnelsRootViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F4DD16D21DBEA0700690EAE /* ManageTunnelsRootViewController.swift */; };
|
||||||
@@ -67,6 +70,7 @@
|
|||||||
6F628C3D217F09E9003482A3 /* TunnelViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F628C3C217F09E9003482A3 /* TunnelViewModel.swift */; };
|
6F628C3D217F09E9003482A3 /* TunnelViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F628C3C217F09E9003482A3 /* TunnelViewModel.swift */; };
|
||||||
6F628C3F217F3413003482A3 /* DNSServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F628C3E217F3413003482A3 /* DNSServer.swift */; };
|
6F628C3F217F3413003482A3 /* DNSServer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F628C3E217F3413003482A3 /* DNSServer.swift */; };
|
||||||
6F628C41217F47DB003482A3 /* TunnelDetailTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F628C40217F47DB003482A3 /* TunnelDetailTableViewController.swift */; };
|
6F628C41217F47DB003482A3 /* TunnelDetailTableViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F628C40217F47DB003482A3 /* TunnelDetailTableViewController.swift */; };
|
||||||
|
6F6483E7229293300075BA15 /* LaunchedAtLoginDetector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F6483E6229293300075BA15 /* LaunchedAtLoginDetector.swift */; };
|
||||||
6F6899A62180447E0012E523 /* x25519.c in Sources */ = {isa = PBXBuildFile; fileRef = 6F6899A52180447E0012E523 /* x25519.c */; };
|
6F6899A62180447E0012E523 /* x25519.c in Sources */ = {isa = PBXBuildFile; fileRef = 6F6899A52180447E0012E523 /* x25519.c */; };
|
||||||
6F6899A8218044FC0012E523 /* Curve25519.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F6899A7218044FC0012E523 /* Curve25519.swift */; };
|
6F6899A8218044FC0012E523 /* Curve25519.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F6899A7218044FC0012E523 /* Curve25519.swift */; };
|
||||||
6F693A562179E556008551C1 /* Endpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F693A552179E556008551C1 /* Endpoint.swift */; };
|
6F693A562179E556008551C1 /* Endpoint.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6F693A552179E556008551C1 /* Endpoint.swift */; };
|
||||||
@@ -300,6 +304,9 @@
|
|||||||
6F1075632258AE9800D78929 /* DeleteTunnelsConfirmationAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteTunnelsConfirmationAlert.swift; sourceTree = "<group>"; };
|
6F1075632258AE9800D78929 /* DeleteTunnelsConfirmationAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeleteTunnelsConfirmationAlert.swift; sourceTree = "<group>"; };
|
||||||
6F19D30322402B8700A126F2 /* ConfirmationAlertPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmationAlertPresenter.swift; sourceTree = "<group>"; };
|
6F19D30322402B8700A126F2 /* ConfirmationAlertPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfirmationAlertPresenter.swift; sourceTree = "<group>"; };
|
||||||
6F2449E7226587B80047B9E9 /* MacAppStoreUpdateDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacAppStoreUpdateDetector.swift; sourceTree = "<group>"; };
|
6F2449E7226587B80047B9E9 /* MacAppStoreUpdateDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MacAppStoreUpdateDetector.swift; sourceTree = "<group>"; };
|
||||||
|
6F3E02E8228000F6001FE7E3 /* MainMenu.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MainMenu.swift; sourceTree = "<group>"; };
|
||||||
|
6F29A9422278518D00DC6A6B /* RecentTunnelsTracker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RecentTunnelsTracker.swift; sourceTree = "<group>"; };
|
||||||
|
6F29A94622787B1600DC6A6B /* QuickActionItem.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QuickActionItem.swift; sourceTree = "<group>"; };
|
||||||
6F4DD16721DA552B00690EAE /* NSTableView+Reuse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTableView+Reuse.swift"; sourceTree = "<group>"; };
|
6F4DD16721DA552B00690EAE /* NSTableView+Reuse.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTableView+Reuse.swift"; sourceTree = "<group>"; };
|
||||||
6F4DD16A21DA558800690EAE /* TunnelListRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelListRow.swift; sourceTree = "<group>"; };
|
6F4DD16A21DA558800690EAE /* TunnelListRow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelListRow.swift; sourceTree = "<group>"; };
|
||||||
6F4DD16D21DBEA0700690EAE /* ManageTunnelsRootViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManageTunnelsRootViewController.swift; sourceTree = "<group>"; };
|
6F4DD16D21DBEA0700690EAE /* ManageTunnelsRootViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ManageTunnelsRootViewController.swift; sourceTree = "<group>"; };
|
||||||
@@ -318,6 +325,7 @@
|
|||||||
6F628C3C217F09E9003482A3 /* TunnelViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelViewModel.swift; sourceTree = "<group>"; };
|
6F628C3C217F09E9003482A3 /* TunnelViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TunnelViewModel.swift; sourceTree = "<group>"; };
|
||||||
6F628C3E217F3413003482A3 /* DNSServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DNSServer.swift; sourceTree = "<group>"; };
|
6F628C3E217F3413003482A3 /* DNSServer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DNSServer.swift; sourceTree = "<group>"; };
|
||||||
6F628C40217F47DB003482A3 /* TunnelDetailTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TunnelDetailTableViewController.swift; sourceTree = "<group>"; };
|
6F628C40217F47DB003482A3 /* TunnelDetailTableViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TunnelDetailTableViewController.swift; sourceTree = "<group>"; };
|
||||||
|
6F6483E6229293300075BA15 /* LaunchedAtLoginDetector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LaunchedAtLoginDetector.swift; sourceTree = "<group>"; };
|
||||||
6F689999218043390012E523 /* WireGuard-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "WireGuard-Bridging-Header.h"; sourceTree = "<group>"; };
|
6F689999218043390012E523 /* WireGuard-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "WireGuard-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
6F6899A42180447E0012E523 /* x25519.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = x25519.h; sourceTree = "<group>"; };
|
6F6899A42180447E0012E523 /* x25519.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = x25519.h; sourceTree = "<group>"; };
|
||||||
6F6899A52180447E0012E523 /* x25519.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = x25519.c; sourceTree = "<group>"; };
|
6F6899A52180447E0012E523 /* x25519.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = x25519.c; sourceTree = "<group>"; };
|
||||||
@@ -564,6 +572,8 @@
|
|||||||
6F919EC2218A2AE90023B400 /* ErrorPresenter.swift */,
|
6F919EC2218A2AE90023B400 /* ErrorPresenter.swift */,
|
||||||
6F19D30322402B8700A126F2 /* ConfirmationAlertPresenter.swift */,
|
6F19D30322402B8700A126F2 /* ConfirmationAlertPresenter.swift */,
|
||||||
5F45417C21C1B23600994C13 /* UITableViewCell+Reuse.swift */,
|
5F45417C21C1B23600994C13 /* UITableViewCell+Reuse.swift */,
|
||||||
|
6F29A9422278518D00DC6A6B /* RecentTunnelsTracker.swift */,
|
||||||
|
6F29A94622787B1600DC6A6B /* QuickActionItem.swift */,
|
||||||
6FF4AC23211EC472002C96EB /* Info.plist */,
|
6FF4AC23211EC472002C96EB /* Info.plist */,
|
||||||
6FF4AC482120B9E0002C96EB /* WireGuard.entitlements */,
|
6FF4AC482120B9E0002C96EB /* WireGuard.entitlements */,
|
||||||
);
|
);
|
||||||
@@ -631,10 +641,12 @@
|
|||||||
6FB1BD5F21D2607A00A991BF /* AppDelegate.swift */,
|
6FB1BD5F21D2607A00A991BF /* AppDelegate.swift */,
|
||||||
6F89E17921EDEB0E00C97BB9 /* StatusItemController.swift */,
|
6F89E17921EDEB0E00C97BB9 /* StatusItemController.swift */,
|
||||||
6FBA101621D655340051C35F /* StatusMenu.swift */,
|
6FBA101621D655340051C35F /* StatusMenu.swift */,
|
||||||
|
6F3E02E8228000F6001FE7E3 /* MainMenu.swift */,
|
||||||
6F89E17B21F090CC00C97BB9 /* TunnelsTracker.swift */,
|
6F89E17B21F090CC00C97BB9 /* TunnelsTracker.swift */,
|
||||||
6FBA104121D6BC210051C35F /* ErrorPresenter.swift */,
|
6FBA104121D6BC210051C35F /* ErrorPresenter.swift */,
|
||||||
6FCD99AE21E0EA1700BA4C82 /* ImportPanelPresenter.swift */,
|
6FCD99AE21E0EA1700BA4C82 /* ImportPanelPresenter.swift */,
|
||||||
6F2449E7226587B80047B9E9 /* MacAppStoreUpdateDetector.swift */,
|
6F2449E7226587B80047B9E9 /* MacAppStoreUpdateDetector.swift */,
|
||||||
|
6F6483E6229293300075BA15 /* LaunchedAtLoginDetector.swift */,
|
||||||
6FB1BD6121D2607E00A991BF /* Assets.xcassets */,
|
6FB1BD6121D2607E00A991BF /* Assets.xcassets */,
|
||||||
6FB1BD6621D2607E00A991BF /* Info.plist */,
|
6FB1BD6621D2607E00A991BF /* Info.plist */,
|
||||||
6FB1BD6721D2607E00A991BF /* WireGuard.entitlements */,
|
6FB1BD6721D2607E00A991BF /* WireGuard.entitlements */,
|
||||||
@@ -1289,8 +1301,10 @@
|
|||||||
6F4DD16B21DA558800690EAE /* TunnelListRow.swift in Sources */,
|
6F4DD16B21DA558800690EAE /* TunnelListRow.swift in Sources */,
|
||||||
6FDB6D15224CB2CE00EE4BC3 /* LogViewCell.swift in Sources */,
|
6FDB6D15224CB2CE00EE4BC3 /* LogViewCell.swift in Sources */,
|
||||||
6FE3661D21F64F6B00F78C7D /* ConfTextColorTheme.swift in Sources */,
|
6FE3661D21F64F6B00F78C7D /* ConfTextColorTheme.swift in Sources */,
|
||||||
|
6F3E02E9228000F6001FE7E3 /* MainMenu.swift in Sources */,
|
||||||
5F52D0BF21E3788900283CEA /* NSColor+Hex.swift in Sources */,
|
5F52D0BF21E3788900283CEA /* NSColor+Hex.swift in Sources */,
|
||||||
6FB1BDBE21D50F0200A991BF /* Logger.swift in Sources */,
|
6FB1BDBE21D50F0200A991BF /* Logger.swift in Sources */,
|
||||||
|
6F6483E7229293300075BA15 /* LaunchedAtLoginDetector.swift in Sources */,
|
||||||
6FB1BDBF21D50F0200A991BF /* TunnelConfiguration+WgQuickConfig.swift in Sources */,
|
6FB1BDBF21D50F0200A991BF /* TunnelConfiguration+WgQuickConfig.swift in Sources */,
|
||||||
6FADE96C2254B8C300B838A4 /* UnusableTunnelDetailViewController.swift in Sources */,
|
6FADE96C2254B8C300B838A4 /* UnusableTunnelDetailViewController.swift in Sources */,
|
||||||
6FFACD2021E4D8D500E9A2A5 /* ParseError+WireGuardAppError.swift in Sources */,
|
6FFACD2021E4D8D500E9A2A5 /* ParseError+WireGuardAppError.swift in Sources */,
|
||||||
@@ -1347,6 +1361,7 @@
|
|||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
6FE1765A21C90E87002690EA /* LocalizationHelper.swift in Sources */,
|
6FE1765A21C90E87002690EA /* LocalizationHelper.swift in Sources */,
|
||||||
|
6F29A94722787B1600DC6A6B /* QuickActionItem.swift in Sources */,
|
||||||
6FF3527221C2616C0008484E /* ringlogger.c in Sources */,
|
6FF3527221C2616C0008484E /* ringlogger.c in Sources */,
|
||||||
6F0F44CB222D55FD00B0FF04 /* EditableTextCell.swift in Sources */,
|
6F0F44CB222D55FD00B0FF04 /* EditableTextCell.swift in Sources */,
|
||||||
6FF3527321C2616C0008484E /* Logger.swift in Sources */,
|
6FF3527321C2616C0008484E /* Logger.swift in Sources */,
|
||||||
@@ -1387,6 +1402,7 @@
|
|||||||
6F7774EA217229DB006A79B3 /* IPAddressRange.swift in Sources */,
|
6F7774EA217229DB006A79B3 /* IPAddressRange.swift in Sources */,
|
||||||
6F7774E82172020C006A79B3 /* TunnelConfiguration.swift in Sources */,
|
6F7774E82172020C006A79B3 /* TunnelConfiguration.swift in Sources */,
|
||||||
6FDEF7FB21863B6100D8FBF6 /* unzip.c in Sources */,
|
6FDEF7FB21863B6100D8FBF6 /* unzip.c in Sources */,
|
||||||
|
6F29A9432278518D00DC6A6B /* RecentTunnelsTracker.swift in Sources */,
|
||||||
6F6899A8218044FC0012E523 /* Curve25519.swift in Sources */,
|
6F6899A8218044FC0012E523 /* Curve25519.swift in Sources */,
|
||||||
6F0F44C9222D55BB00B0FF04 /* TextCell.swift in Sources */,
|
6F0F44C9222D55BB00B0FF04 /* TextCell.swift in Sources */,
|
||||||
5F4541A021C2D6B700994C13 /* TunnelListCell.swift in Sources */,
|
5F4541A021C2D6B700994C13 /* TunnelListCell.swift in Sources */,
|
||||||
@@ -1517,7 +1533,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_ENTITLEMENTS = WireGuard/UI/macOS/LoginItemHelper/LoginItemHelper.entitlements;
|
CODE_SIGN_ENTITLEMENTS = WireGuard/UI/macOS/LoginItemHelper/LoginItemHelper.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||||
INFOPLIST_FILE = WireGuard/UI/macOS/Info.plist;
|
INFOPLIST_FILE = WireGuard/UI/macOS/LoginItemHelper/Info.plist;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "$(APP_ID_MACOS).login-item-helper";
|
PRODUCT_BUNDLE_IDENTIFIER = "$(APP_ID_MACOS).login-item-helper";
|
||||||
PRODUCT_NAME = WireGuardLoginItemHelper;
|
PRODUCT_NAME = WireGuardLoginItemHelper;
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
@@ -1530,7 +1546,7 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
CODE_SIGN_ENTITLEMENTS = WireGuard/UI/macOS/LoginItemHelper/LoginItemHelper.entitlements;
|
CODE_SIGN_ENTITLEMENTS = WireGuard/UI/macOS/LoginItemHelper/LoginItemHelper.entitlements;
|
||||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
CODE_SIGN_IDENTITY = "Mac Developer";
|
||||||
INFOPLIST_FILE = WireGuard/UI/macOS/Info.plist;
|
INFOPLIST_FILE = WireGuard/UI/macOS/LoginItemHelper/Info.plist;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "$(APP_ID_MACOS).login-item-helper";
|
PRODUCT_BUNDLE_IDENTIFIER = "$(APP_ID_MACOS).login-item-helper";
|
||||||
PRODUCT_NAME = WireGuardLoginItemHelper;
|
PRODUCT_NAME = WireGuardLoginItemHelper;
|
||||||
SDKROOT = macosx;
|
SDKROOT = macosx;
|
||||||
|
|||||||
@@ -291,19 +291,42 @@
|
|||||||
"alertSystemErrorMessageTunnelConfigurationReadWriteFailed" = "Reading or writing the configuration failed.";
|
"alertSystemErrorMessageTunnelConfigurationReadWriteFailed" = "Reading or writing the configuration failed.";
|
||||||
"alertSystemErrorMessageTunnelConfigurationUnknown" = "Unknown system error.";
|
"alertSystemErrorMessageTunnelConfigurationUnknown" = "Unknown system error.";
|
||||||
|
|
||||||
// Mac status bar menu / pulldown menu
|
// Mac status bar menu / pulldown menu / main menu
|
||||||
|
|
||||||
"macMenuNetworks (%@)" = "Networks: %@";
|
"macMenuNetworks (%@)" = "Networks: %@";
|
||||||
"macMenuNetworksNone" = "Networks: None";
|
"macMenuNetworksNone" = "Networks: None";
|
||||||
|
|
||||||
"macMenuTitle" = "WireGuard";
|
"macMenuTitle" = "WireGuard";
|
||||||
"macMenuManageTunnels" = "Manage tunnels";
|
"macMenuManageTunnels" = "Manage Tunnels";
|
||||||
"macMenuImportTunnels" = "Import tunnel(s) from file…";
|
"macMenuImportTunnels" = "Import Tunnel(s) from File…";
|
||||||
"macMenuAddEmptyTunnel" = "Add empty tunnel…";
|
"macMenuAddEmptyTunnel" = "Add Empty Tunnel…";
|
||||||
"macMenuViewLog" = "View log";
|
"macMenuViewLog" = "View Log";
|
||||||
"macMenuExportTunnels" = "Export tunnels to zip…";
|
"macMenuExportTunnels" = "Export Tunnels to Zip…";
|
||||||
"macMenuAbout" = "About WireGuard";
|
"macMenuAbout" = "About WireGuard";
|
||||||
"macMenuQuit" = "Quit";
|
"macMenuQuit" = "Quit WireGuard";
|
||||||
|
|
||||||
|
"macMenuHideApp" = "Hide WireGuard";
|
||||||
|
"macMenuHideOtherApps" = "Hide Others";
|
||||||
|
"macMenuShowAllApps" = "Show All";
|
||||||
|
"macMenuQuitManagingTunnels" = "Quit Tunnel Manager";
|
||||||
|
|
||||||
|
"macMenuFile" = "File";
|
||||||
|
"macMenuCloseWindow" = "Close Window";
|
||||||
|
|
||||||
|
"macMenuEdit" = "Edit";
|
||||||
|
"macMenuCut" = "Cut";
|
||||||
|
"macMenuCopy" = "Copy";
|
||||||
|
"macMenuPaste" = "Paste";
|
||||||
|
"macMenuSelectAll" = "Select All";
|
||||||
|
|
||||||
|
"macMenuTunnel" = "Tunnel";
|
||||||
|
"macMenuToggleStatus" = "Toggle Status";
|
||||||
|
"macMenuEditTunnel" = "Edit…";
|
||||||
|
"macMenuDeleteSelected" = "Delete Selected";
|
||||||
|
|
||||||
|
"macMenuWindow" = "Window";
|
||||||
|
"macMenuMinimize" = "Minimize";
|
||||||
|
"macMenuZoom" = "Zoom";
|
||||||
|
|
||||||
// Mac manage tunnels window
|
// Mac manage tunnels window
|
||||||
|
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
VERSION_NAME = 0.0.20190423
|
VERSION_NAME = 0.0.20190531
|
||||||
VERSION_ID = 8
|
VERSION_ID = 9
|
||||||
|
|||||||
@@ -46,7 +46,11 @@ class TunnelsManager {
|
|||||||
|
|
||||||
var tunnelManagers = managers ?? []
|
var tunnelManagers = managers ?? []
|
||||||
var refs: Set<Data> = []
|
var refs: Set<Data> = []
|
||||||
|
var tunnelNames: Set<String> = []
|
||||||
for (index, tunnelManager) in tunnelManagers.enumerated().reversed() {
|
for (index, tunnelManager) in tunnelManagers.enumerated().reversed() {
|
||||||
|
if let tunnelName = tunnelManager.localizedDescription {
|
||||||
|
tunnelNames.insert(tunnelName)
|
||||||
|
}
|
||||||
guard let proto = tunnelManager.protocolConfiguration as? NETunnelProviderProtocol else { continue }
|
guard let proto = tunnelManager.protocolConfiguration as? NETunnelProviderProtocol else { continue }
|
||||||
if proto.migrateConfigurationIfNeeded(called: tunnelManager.localizedDescription ?? "unknown") {
|
if proto.migrateConfigurationIfNeeded(called: tunnelManager.localizedDescription ?? "unknown") {
|
||||||
tunnelManager.saveToPreferences { _ in }
|
tunnelManager.saveToPreferences { _ in }
|
||||||
@@ -66,6 +70,9 @@ class TunnelsManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Keychain.deleteReferences(except: refs)
|
Keychain.deleteReferences(except: refs)
|
||||||
|
#if os(iOS)
|
||||||
|
RecentTunnelsTracker.cleanupTunnels(except: tunnelNames)
|
||||||
|
#endif
|
||||||
completionHandler(.success(TunnelsManager(tunnelProviders: tunnelManagers)))
|
completionHandler(.success(TunnelsManager(tunnelProviders: tunnelManagers)))
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -188,7 +195,8 @@ class TunnelsManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let tunnelProviderManager = tunnel.tunnelProvider
|
let tunnelProviderManager = tunnel.tunnelProvider
|
||||||
let isNameChanged = tunnelName != tunnelProviderManager.localizedDescription
|
let oldName = tunnelProviderManager.localizedDescription ?? ""
|
||||||
|
let isNameChanged = tunnelName != oldName
|
||||||
if isNameChanged {
|
if isNameChanged {
|
||||||
guard !tunnels.contains(where: { $0.name == tunnelName }) else {
|
guard !tunnels.contains(where: { $0.name == tunnelName }) else {
|
||||||
completionHandler(TunnelsManagerError.tunnelAlreadyExistsWithThatName)
|
completionHandler(TunnelsManagerError.tunnelAlreadyExistsWithThatName)
|
||||||
@@ -220,6 +228,9 @@ class TunnelsManager {
|
|||||||
self.tunnels.sort { TunnelsManager.tunnelNameIsLessThan($0.name, $1.name) }
|
self.tunnels.sort { TunnelsManager.tunnelNameIsLessThan($0.name, $1.name) }
|
||||||
let newIndex = self.tunnels.firstIndex(of: tunnel)!
|
let newIndex = self.tunnels.firstIndex(of: tunnel)!
|
||||||
self.tunnelsListDelegate?.tunnelMoved(from: oldIndex, to: newIndex)
|
self.tunnelsListDelegate?.tunnelMoved(from: oldIndex, to: newIndex)
|
||||||
|
#if os(iOS)
|
||||||
|
RecentTunnelsTracker.handleTunnelRenamed(oldName: oldName, newName: tunnelName)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
self.tunnelsListDelegate?.tunnelModified(at: self.tunnels.firstIndex(of: tunnel)!)
|
self.tunnelsListDelegate?.tunnelModified(at: self.tunnels.firstIndex(of: tunnel)!)
|
||||||
|
|
||||||
@@ -266,6 +277,10 @@ class TunnelsManager {
|
|||||||
self.tunnelsListDelegate?.tunnelRemoved(at: index, tunnel: tunnel)
|
self.tunnelsListDelegate?.tunnelRemoved(at: index, tunnel: tunnel)
|
||||||
}
|
}
|
||||||
completionHandler(nil)
|
completionHandler(nil)
|
||||||
|
|
||||||
|
#if os(iOS)
|
||||||
|
RecentTunnelsTracker.handleTunnelRemoved(tunnelName: tunnel.name)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -298,6 +313,10 @@ class TunnelsManager {
|
|||||||
return tunnels[index]
|
return tunnels[index]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func mapTunnels<T>(transform: (TunnelContainer) throws -> T) rethrows -> [T] {
|
||||||
|
return try tunnels.map(transform)
|
||||||
|
}
|
||||||
|
|
||||||
func index(of tunnel: TunnelContainer) -> Int? {
|
func index(of tunnel: TunnelContainer) -> Int? {
|
||||||
return tunnels.firstIndex(of: tunnel)
|
return tunnels.firstIndex(of: tunnel)
|
||||||
}
|
}
|
||||||
@@ -343,6 +362,10 @@ class TunnelsManager {
|
|||||||
#else
|
#else
|
||||||
tunnel.startActivation(activationDelegate: activationDelegate)
|
tunnel.startActivation(activationDelegate: activationDelegate)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if os(iOS)
|
||||||
|
RecentTunnelsTracker.handleTunnelActivated(tunnelName: tunnel.name)
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
func startDeactivation(of tunnel: TunnelContainer) {
|
func startDeactivation(of tunnel: TunnelContainer) {
|
||||||
|
|||||||
@@ -9,10 +9,17 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||||||
|
|
||||||
var window: UIWindow?
|
var window: UIWindow?
|
||||||
var mainVC: MainViewController?
|
var mainVC: MainViewController?
|
||||||
|
var isLaunchedForSpecificAction = false
|
||||||
|
|
||||||
func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
func application(_ application: UIApplication, willFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||||
Logger.configureGlobal(tagged: "APP", withFilePath: FileManager.logFileURL?.path)
|
Logger.configureGlobal(tagged: "APP", withFilePath: FileManager.logFileURL?.path)
|
||||||
|
|
||||||
|
if let launchOptions = launchOptions {
|
||||||
|
if launchOptions[.url] != nil || launchOptions[.shortcutItem] != nil {
|
||||||
|
isLaunchedForSpecificAction = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let window = UIWindow(frame: UIScreen.main.bounds)
|
let window = UIWindow(frame: UIScreen.main.bounds)
|
||||||
window.backgroundColor = .white
|
window.backgroundColor = .white
|
||||||
self.window = window
|
self.window = window
|
||||||
@@ -37,6 +44,21 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
|||||||
func applicationDidBecomeActive(_ application: UIApplication) {
|
func applicationDidBecomeActive(_ application: UIApplication) {
|
||||||
mainVC?.refreshTunnelConnectionStatuses()
|
mainVC?.refreshTunnelConnectionStatuses()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func applicationWillResignActive(_ application: UIApplication) {
|
||||||
|
guard let allTunnelNames = mainVC?.allTunnelNames() else { return }
|
||||||
|
application.shortcutItems = QuickActionItem.createItems(allTunnelNames: allTunnelNames)
|
||||||
|
}
|
||||||
|
|
||||||
|
func application(_ application: UIApplication, performActionFor shortcutItem: UIApplicationShortcutItem, completionHandler: @escaping (Bool) -> Void) {
|
||||||
|
guard shortcutItem.type == QuickActionItem.type else {
|
||||||
|
completionHandler(false)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
let tunnelName = shortcutItem.localizedTitle
|
||||||
|
mainVC?.showTunnelDetailForTunnel(named: tunnelName, animated: false, shouldToggleStatus: true)
|
||||||
|
completionHandler(true)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension AppDelegate {
|
extension AppDelegate {
|
||||||
@@ -45,7 +67,7 @@ extension AppDelegate {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func application(_ application: UIApplication, shouldRestoreApplicationState coder: NSCoder) -> Bool {
|
func application(_ application: UIApplication, shouldRestoreApplicationState coder: NSCoder) -> Bool {
|
||||||
return true
|
return !self.isLaunchedForSpecificAction
|
||||||
}
|
}
|
||||||
|
|
||||||
func application(_ application: UIApplication, viewControllerWithRestorationIdentifierPath identifierComponents: [String], coder: NSCoder) -> UIViewController? {
|
func application(_ application: UIApplication, viewControllerWithRestorationIdentifierPath identifierComponents: [String], coder: NSCoder) -> UIViewController? {
|
||||||
@@ -58,7 +80,7 @@ extension AppDelegate {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Show it when tunnelsManager is available
|
// Show it when tunnelsManager is available
|
||||||
mainVC?.showTunnelDetailForTunnel(named: tunnelName, animated: false)
|
mainVC?.showTunnelDetailForTunnel(named: tunnelName, animated: false, shouldToggleStatus: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
|
import UIKit
|
||||||
|
|
||||||
|
class QuickActionItem: UIApplicationShortcutItem {
|
||||||
|
static let type = "WireGuardTunnelActivateAndShow"
|
||||||
|
|
||||||
|
init(tunnelName: String) {
|
||||||
|
super.init(type: QuickActionItem.type, localizedTitle: tunnelName, localizedSubtitle: nil, icon: nil, userInfo: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func createItems(allTunnelNames: [String]) -> [QuickActionItem] {
|
||||||
|
let numberOfItems = 10
|
||||||
|
// Currently, only 4 items shown by iOS, but that can increase in the future.
|
||||||
|
// iOS will discard additional items we give it.
|
||||||
|
var tunnelNames = RecentTunnelsTracker.recentlyActivatedTunnelNames(limit: numberOfItems)
|
||||||
|
let numberOfSlotsRemaining = numberOfItems - tunnelNames.count
|
||||||
|
if numberOfSlotsRemaining > 0 {
|
||||||
|
let moreTunnels = allTunnelNames.filter { !tunnelNames.contains($0) }.prefix(numberOfSlotsRemaining)
|
||||||
|
tunnelNames.append(contentsOf: moreTunnels)
|
||||||
|
}
|
||||||
|
return tunnelNames.map { QuickActionItem(tunnelName: $0) }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
class RecentTunnelsTracker {
|
||||||
|
|
||||||
|
private static let keyRecentlyActivatedTunnelNames = "recentlyActivatedTunnelNames"
|
||||||
|
private static let maxNumberOfTunnels = 10
|
||||||
|
|
||||||
|
private static var userDefaults: UserDefaults? {
|
||||||
|
guard let appGroupId = FileManager.appGroupId else {
|
||||||
|
wg_log(.error, staticMessage: "Cannot obtain app group ID from bundle for tracking recently used tunnels")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
guard let userDefaults = UserDefaults(suiteName: appGroupId) else {
|
||||||
|
wg_log(.error, staticMessage: "Cannot obtain shared user defaults for tracking recently used tunnels")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return userDefaults
|
||||||
|
}
|
||||||
|
|
||||||
|
static func handleTunnelActivated(tunnelName: String) {
|
||||||
|
guard let userDefaults = RecentTunnelsTracker.userDefaults else { return }
|
||||||
|
var recentTunnels = userDefaults.stringArray(forKey: keyRecentlyActivatedTunnelNames) ?? []
|
||||||
|
if let existingIndex = recentTunnels.firstIndex(of: tunnelName) {
|
||||||
|
recentTunnels.remove(at: existingIndex)
|
||||||
|
}
|
||||||
|
recentTunnels.insert(tunnelName, at: 0)
|
||||||
|
if recentTunnels.count > maxNumberOfTunnels {
|
||||||
|
recentTunnels.removeLast(recentTunnels.count - maxNumberOfTunnels)
|
||||||
|
}
|
||||||
|
userDefaults.set(recentTunnels, forKey: keyRecentlyActivatedTunnelNames)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func handleTunnelRemoved(tunnelName: String) {
|
||||||
|
guard let userDefaults = RecentTunnelsTracker.userDefaults else { return }
|
||||||
|
var recentTunnels = userDefaults.stringArray(forKey: keyRecentlyActivatedTunnelNames) ?? []
|
||||||
|
if let existingIndex = recentTunnels.firstIndex(of: tunnelName) {
|
||||||
|
recentTunnels.remove(at: existingIndex)
|
||||||
|
userDefaults.set(recentTunnels, forKey: keyRecentlyActivatedTunnelNames)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func handleTunnelRenamed(oldName: String, newName: String) {
|
||||||
|
guard let userDefaults = RecentTunnelsTracker.userDefaults else { return }
|
||||||
|
var recentTunnels = userDefaults.stringArray(forKey: keyRecentlyActivatedTunnelNames) ?? []
|
||||||
|
if let existingIndex = recentTunnels.firstIndex(of: oldName) {
|
||||||
|
recentTunnels[existingIndex] = newName
|
||||||
|
userDefaults.set(recentTunnels, forKey: keyRecentlyActivatedTunnelNames)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func cleanupTunnels(except tunnelNamesToKeep: Set<String>) {
|
||||||
|
guard let userDefaults = RecentTunnelsTracker.userDefaults else { return }
|
||||||
|
var recentTunnels = userDefaults.stringArray(forKey: keyRecentlyActivatedTunnelNames) ?? []
|
||||||
|
let oldCount = recentTunnels.count
|
||||||
|
recentTunnels.removeAll { !tunnelNamesToKeep.contains($0) }
|
||||||
|
if oldCount != recentTunnels.count {
|
||||||
|
userDefaults.set(recentTunnels, forKey: keyRecentlyActivatedTunnelNames)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func recentlyActivatedTunnelNames(limit: Int) -> [String] {
|
||||||
|
guard let userDefaults = RecentTunnelsTracker.userDefaults else { return [] }
|
||||||
|
var recentTunnels = userDefaults.stringArray(forKey: keyRecentlyActivatedTunnelNames) ?? []
|
||||||
|
if limit < recentTunnels.count {
|
||||||
|
recentTunnels.removeLast(recentTunnels.count - limit)
|
||||||
|
}
|
||||||
|
return recentTunnels
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,6 +20,15 @@ class LogViewController: UIViewController {
|
|||||||
return busyIndicator
|
return busyIndicator
|
||||||
}()
|
}()
|
||||||
|
|
||||||
|
let paragraphStyle: NSParagraphStyle = {
|
||||||
|
let paragraphStyle = NSMutableParagraphStyle()
|
||||||
|
paragraphStyle.setParagraphStyle(NSParagraphStyle.default)
|
||||||
|
paragraphStyle.lineHeightMultiple = 1.2
|
||||||
|
return paragraphStyle
|
||||||
|
}()
|
||||||
|
|
||||||
|
var isNextLineHighlighted = false
|
||||||
|
|
||||||
var logViewHelper: LogViewHelper?
|
var logViewHelper: LogViewHelper?
|
||||||
var isFetchingLogEntries = false
|
var isFetchingLogEntries = false
|
||||||
private var updateLogEntriesTimer: Timer?
|
private var updateLogEntriesTimer: Timer?
|
||||||
@@ -68,9 +77,20 @@ class LogViewController: UIViewController {
|
|||||||
}
|
}
|
||||||
guard !fetchedLogEntries.isEmpty else { return }
|
guard !fetchedLogEntries.isEmpty else { return }
|
||||||
let isScrolledToEnd = self.textView.contentSize.height - self.textView.bounds.height - self.textView.contentOffset.y < 1
|
let isScrolledToEnd = self.textView.contentSize.height - self.textView.bounds.height - self.textView.contentOffset.y < 1
|
||||||
let text = fetchedLogEntries.reduce("") { $0 + $1.text() + "\n" }
|
|
||||||
let font = UIFont.preferredFont(forTextStyle: UIFont.TextStyle.body)
|
let richText = NSMutableAttributedString()
|
||||||
let richText = NSAttributedString(string: text, attributes: [.font: font])
|
let bodyFont = UIFont.preferredFont(forTextStyle: UIFont.TextStyle.body)
|
||||||
|
let captionFont = UIFont.preferredFont(forTextStyle: UIFont.TextStyle.caption1)
|
||||||
|
let lightGrayColor = UIColor(white: 0.88, alpha: 1.0)
|
||||||
|
|
||||||
|
for logEntry in fetchedLogEntries {
|
||||||
|
let bgColor = self.isNextLineHighlighted ? lightGrayColor : UIColor.white
|
||||||
|
let timestampText = NSAttributedString(string: logEntry.timestamp + "\n", attributes: [.font: captionFont, .backgroundColor: bgColor, .paragraphStyle: self.paragraphStyle])
|
||||||
|
let messageText = NSAttributedString(string: logEntry.message + "\n", attributes: [.font: bodyFont, .backgroundColor: bgColor, .paragraphStyle: self.paragraphStyle])
|
||||||
|
richText.append(timestampText)
|
||||||
|
richText.append(messageText)
|
||||||
|
self.isNextLineHighlighted.toggle()
|
||||||
|
}
|
||||||
self.textView.textStorage.append(richText)
|
self.textView.textStorage.append(richText)
|
||||||
if isScrolledToEnd {
|
if isScrolledToEnd {
|
||||||
let endOfCurrentText = NSRange(location: (self.textView.text as NSString).length, length: 0)
|
let endOfCurrentText = NSRange(location: (self.textView.text as NSString).length, length: 0)
|
||||||
|
|||||||
@@ -57,6 +57,10 @@ class MainViewController: UISplitViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func allTunnelNames() -> [String]? {
|
||||||
|
guard let tunnelsManager = self.tunnelsManager else { return nil }
|
||||||
|
return tunnelsManager.mapTunnels { $0.name }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension MainViewController: TunnelsManagerActivationDelegate {
|
extension MainViewController: TunnelsManagerActivationDelegate {
|
||||||
@@ -84,19 +88,17 @@ extension MainViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func showTunnelDetailForTunnel(named tunnelName: String, animated: Bool) {
|
func showTunnelDetailForTunnel(named tunnelName: String, animated: Bool, shouldToggleStatus: Bool) {
|
||||||
let showTunnelDetailBlock: (TunnelsManager) -> Void = { [weak self] tunnelsManager in
|
let showTunnelDetailBlock: (TunnelsManager) -> Void = { [weak self] tunnelsManager in
|
||||||
|
guard let self = self else { return }
|
||||||
|
guard let tunnelsListVC = self.tunnelsListVC else { return }
|
||||||
if let tunnel = tunnelsManager.tunnel(named: tunnelName) {
|
if let tunnel = tunnelsManager.tunnel(named: tunnelName) {
|
||||||
let tunnelDetailVC = TunnelDetailTableViewController(tunnelsManager: tunnelsManager, tunnel: tunnel)
|
tunnelsListVC.showTunnelDetail(for: tunnel, animated: false)
|
||||||
let tunnelDetailNC = UINavigationController(rootViewController: tunnelDetailVC)
|
if shouldToggleStatus {
|
||||||
tunnelDetailNC.restorationIdentifier = "DetailNC"
|
if tunnel.status == .inactive {
|
||||||
if let self = self {
|
tunnelsManager.startActivation(of: tunnel)
|
||||||
if animated {
|
} else if tunnel.status == .active {
|
||||||
self.showDetailViewController(tunnelDetailNC, sender: self)
|
tunnelsManager.startDeactivation(of: tunnel)
|
||||||
} else {
|
|
||||||
UIView.performWithoutAnimation {
|
|
||||||
self.showDetailViewController(tunnelDetailNC, sender: self)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -251,6 +251,24 @@ class TunnelsListTableViewController: UIViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func showTunnelDetail(for tunnel: TunnelContainer, animated: Bool) {
|
||||||
|
guard let tunnelsManager = tunnelsManager else { return }
|
||||||
|
guard let splitViewController = splitViewController else { return }
|
||||||
|
guard let navController = navigationController else { return }
|
||||||
|
|
||||||
|
let tunnelDetailVC = TunnelDetailTableViewController(tunnelsManager: tunnelsManager,
|
||||||
|
tunnel: tunnel)
|
||||||
|
let tunnelDetailNC = UINavigationController(rootViewController: tunnelDetailVC)
|
||||||
|
tunnelDetailNC.restorationIdentifier = "DetailNC"
|
||||||
|
if splitViewController.isCollapsed && navController.viewControllers.count > 1 {
|
||||||
|
navController.setViewControllers([self, tunnelDetailNC], animated: animated)
|
||||||
|
} else {
|
||||||
|
splitViewController.showDetailViewController(tunnelDetailNC, sender: self, animated: animated)
|
||||||
|
}
|
||||||
|
detailDisplayedTunnel = tunnel
|
||||||
|
self.presentedViewController?.dismiss(animated: false, completion: nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension TunnelsListTableViewController: UIDocumentPickerDelegate {
|
extension TunnelsListTableViewController: UIDocumentPickerDelegate {
|
||||||
@@ -309,12 +327,7 @@ extension TunnelsListTableViewController: UITableViewDelegate {
|
|||||||
}
|
}
|
||||||
guard let tunnelsManager = tunnelsManager else { return }
|
guard let tunnelsManager = tunnelsManager else { return }
|
||||||
let tunnel = tunnelsManager.tunnel(at: indexPath.row)
|
let tunnel = tunnelsManager.tunnel(at: indexPath.row)
|
||||||
let tunnelDetailVC = TunnelDetailTableViewController(tunnelsManager: tunnelsManager,
|
showTunnelDetail(for: tunnel, animated: true)
|
||||||
tunnel: tunnel)
|
|
||||||
let tunnelDetailNC = UINavigationController(rootViewController: tunnelDetailVC)
|
|
||||||
tunnelDetailNC.restorationIdentifier = "DetailNC"
|
|
||||||
showDetailViewController(tunnelDetailNC, sender: self) // Shall get propagated up to the split-vc
|
|
||||||
detailDisplayedTunnel = tunnel
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
|
func tableView(_ tableView: UITableView, didDeselectRowAt indexPath: IndexPath) {
|
||||||
@@ -387,3 +400,15 @@ extension TunnelsListTableViewController: TunnelsManagerListDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension UISplitViewController {
|
||||||
|
func showDetailViewController(_ vc: UIViewController, sender: Any?, animated: Bool) {
|
||||||
|
if animated {
|
||||||
|
showDetailViewController(vc, sender: sender)
|
||||||
|
} else {
|
||||||
|
UIView.performWithoutAnimation {
|
||||||
|
showDetailViewController(vc, sender: sender)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -13,11 +13,30 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
|
|
||||||
var manageTunnelsRootVC: ManageTunnelsRootViewController?
|
var manageTunnelsRootVC: ManageTunnelsRootViewController?
|
||||||
var manageTunnelsWindowObject: NSWindow?
|
var manageTunnelsWindowObject: NSWindow?
|
||||||
|
var onAppDeactivation: (() -> Void)?
|
||||||
|
|
||||||
|
func applicationWillFinishLaunching(_ notification: Notification) {
|
||||||
|
// To workaround a possible AppKit bug that causes the main menu to become unresponsive sometimes
|
||||||
|
// (especially when launched through Xcode) if we call setActivationPolicy(.regular) in
|
||||||
|
// in applicationDidFinishLaunching, we set it to .prohibited here.
|
||||||
|
// Setting it to .regular would fix that problem too, but at this point, we don't know
|
||||||
|
// whether the app was launched at login or not, so we're not sure whether we should
|
||||||
|
// show the app icon in the dock or not.
|
||||||
|
NSApp.setActivationPolicy(.prohibited)
|
||||||
|
}
|
||||||
|
|
||||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||||
Logger.configureGlobal(tagged: "APP", withFilePath: FileManager.logFileURL?.path)
|
Logger.configureGlobal(tagged: "APP", withFilePath: FileManager.logFileURL?.path)
|
||||||
registerLoginItem(shouldLaunchAtLogin: true)
|
registerLoginItem(shouldLaunchAtLogin: true)
|
||||||
|
|
||||||
|
var isLaunchedAtLogin = false
|
||||||
|
if let appleEvent = NSAppleEventManager.shared().currentAppleEvent {
|
||||||
|
isLaunchedAtLogin = LaunchedAtLoginDetector.isLaunchedAtLogin(openAppleEvent: appleEvent)
|
||||||
|
}
|
||||||
|
|
||||||
|
NSApp.mainMenu = MainMenu()
|
||||||
|
setDockIconAndMainMenuVisibility(isVisible: !isLaunchedAtLogin)
|
||||||
|
|
||||||
TunnelsManager.create { [weak self] result in
|
TunnelsManager.create { [weak self] result in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
|
|
||||||
@@ -38,10 +57,27 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
self.tunnelsManager = tunnelsManager
|
self.tunnelsManager = tunnelsManager
|
||||||
self.tunnelsTracker = tunnelsTracker
|
self.tunnelsTracker = tunnelsTracker
|
||||||
self.statusItemController = statusItemController
|
self.statusItemController = statusItemController
|
||||||
|
|
||||||
|
if !isLaunchedAtLogin {
|
||||||
|
self.showManageTunnelsWindow(completion: nil)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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 quit() {
|
@objc func quit() {
|
||||||
if let manageWindow = manageTunnelsWindowObject, manageWindow.attachedSheet != nil {
|
if let manageWindow = manageTunnelsWindowObject, manageWindow.attachedSheet != nil {
|
||||||
NSApp.activate(ignoringOtherApps: true)
|
NSApp.activate(ignoringOtherApps: true)
|
||||||
@@ -97,12 +133,76 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
}
|
}
|
||||||
return .terminateCancel
|
return .terminateCancel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func applicationShouldTerminateAfterLastWindowClosed(_ application: NSApplication) -> Bool {
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(200)) { [weak self] in
|
||||||
|
self?.setDockIconAndMainMenuVisibility(isVisible: false)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
private func setDockIconAndMainMenuVisibility(isVisible: Bool, completion: (() -> Void)? = nil) {
|
||||||
|
let currentActivationPolicy = NSApp.activationPolicy()
|
||||||
|
let newActivationPolicy: NSApplication.ActivationPolicy = isVisible ? .regular : .accessory
|
||||||
|
guard currentActivationPolicy != newActivationPolicy else {
|
||||||
|
if newActivationPolicy == .regular {
|
||||||
|
NSApp.activate(ignoringOtherApps: true)
|
||||||
|
}
|
||||||
|
completion?()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if newActivationPolicy == .regular && NSApp.isActive {
|
||||||
|
// To workaround a possible AppKit bug that causes the main menu to become unresponsive,
|
||||||
|
// we should deactivate the app first and then set the activation policy.
|
||||||
|
// NSApp.deactivate() doesn't always deactivate the app, so we instead use
|
||||||
|
// setActivationPolicy(.prohibited).
|
||||||
|
onAppDeactivation = {
|
||||||
|
NSApp.setActivationPolicy(.regular)
|
||||||
|
NSApp.activate(ignoringOtherApps: true)
|
||||||
|
completion?()
|
||||||
|
}
|
||||||
|
NSApp.setActivationPolicy(.prohibited)
|
||||||
|
} else {
|
||||||
|
NSApp.setActivationPolicy(newActivationPolicy)
|
||||||
|
if newActivationPolicy == .regular {
|
||||||
|
NSApp.activate(ignoringOtherApps: true)
|
||||||
|
}
|
||||||
|
completion?()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func applicationDidResignActive(_ notification: Notification) {
|
||||||
|
onAppDeactivation?()
|
||||||
|
onAppDeactivation = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension AppDelegate {
|
||||||
|
@objc func aboutClicked() {
|
||||||
|
var appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown"
|
||||||
|
if let appBuild = Bundle.main.infoDictionary?["CFBundleVersion"] as? String {
|
||||||
|
appVersion += " (\(appBuild))"
|
||||||
|
}
|
||||||
|
let appVersionString = [
|
||||||
|
tr(format: "macAppVersion (%@)", appVersion),
|
||||||
|
tr(format: "macGoBackendVersion (%@)", WIREGUARD_GO_VERSION)
|
||||||
|
].joined(separator: "\n")
|
||||||
|
NSApp.activate(ignoringOtherApps: true)
|
||||||
|
NSApp.orderFrontStandardAboutPanel(options: [
|
||||||
|
.applicationVersion: appVersionString,
|
||||||
|
.version: ""
|
||||||
|
])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
extension AppDelegate: StatusMenuWindowDelegate {
|
extension AppDelegate: StatusMenuWindowDelegate {
|
||||||
func manageTunnelsWindow() -> NSWindow {
|
func showManageTunnelsWindow(completion: ((NSWindow?) -> Void)?) {
|
||||||
|
guard let tunnelsManager = tunnelsManager else {
|
||||||
|
completion?(nil)
|
||||||
|
return
|
||||||
|
}
|
||||||
if manageTunnelsWindowObject == nil {
|
if manageTunnelsWindowObject == nil {
|
||||||
manageTunnelsRootVC = ManageTunnelsRootViewController(tunnelsManager: tunnelsManager!)
|
manageTunnelsRootVC = ManageTunnelsRootViewController(tunnelsManager: tunnelsManager)
|
||||||
let window = NSWindow(contentViewController: manageTunnelsRootVC!)
|
let window = NSWindow(contentViewController: manageTunnelsRootVC!)
|
||||||
window.title = tr("macWindowTitleManageTunnels")
|
window.title = tr("macWindowTitleManageTunnels")
|
||||||
window.setContentSize(NSSize(width: 800, height: 480))
|
window.setContentSize(NSSize(width: 800, height: 480))
|
||||||
@@ -110,7 +210,10 @@ extension AppDelegate: StatusMenuWindowDelegate {
|
|||||||
manageTunnelsWindowObject = window
|
manageTunnelsWindowObject = window
|
||||||
tunnelsTracker?.manageTunnelsRootVC = manageTunnelsRootVC
|
tunnelsTracker?.manageTunnelsRootVC = manageTunnelsRootVC
|
||||||
}
|
}
|
||||||
return manageTunnelsWindowObject!
|
setDockIconAndMainMenuVisibility(isVisible: true) { [weak manageTunnelsWindowObject] in
|
||||||
|
manageTunnelsWindowObject?.makeKeyAndOrderFront(self)
|
||||||
|
completion?(manageTunnelsWindowObject)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -5,18 +5,6 @@ import Cocoa
|
|||||||
|
|
||||||
class Application: NSApplication {
|
class Application: NSApplication {
|
||||||
|
|
||||||
private let characterKeyCommands = [
|
|
||||||
"x": #selector(NSText.cut(_:)),
|
|
||||||
"c": #selector(NSText.copy(_:)),
|
|
||||||
"v": #selector(NSText.paste(_:)),
|
|
||||||
"z": #selector(UndoActionRespondable.undo(_:)),
|
|
||||||
"a": #selector(NSResponder.selectAll(_:)),
|
|
||||||
"Z": #selector(UndoActionRespondable.redo(_:)),
|
|
||||||
"w": #selector(NSWindow.performClose(_:)),
|
|
||||||
"m": #selector(NSWindow.performMiniaturize(_:)),
|
|
||||||
"q": #selector(AppDelegate.quit)
|
|
||||||
]
|
|
||||||
|
|
||||||
private var appDelegate: AppDelegate? //swiftlint:disable:this weak_delegate
|
private var appDelegate: AppDelegate? //swiftlint:disable:this weak_delegate
|
||||||
|
|
||||||
override init() {
|
override init() {
|
||||||
@@ -28,21 +16,4 @@ class Application: NSApplication {
|
|||||||
required init?(coder: NSCoder) {
|
required init?(coder: NSCoder) {
|
||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
override func sendEvent(_ event: NSEvent) {
|
|
||||||
let modifierFlags = event.modifierFlags.rawValue & NSEvent.ModifierFlags.deviceIndependentFlagsMask.rawValue
|
|
||||||
|
|
||||||
if event.type == .keyDown,
|
|
||||||
(modifierFlags == NSEvent.ModifierFlags.command.rawValue || modifierFlags == NSEvent.ModifierFlags.command.rawValue | NSEvent.ModifierFlags.shift.rawValue),
|
|
||||||
let selector = characterKeyCommands[event.charactersIgnoringModifiers ?? ""] {
|
|
||||||
sendAction(selector, to: nil, from: self)
|
|
||||||
} else {
|
|
||||||
super.sendEvent(event)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc protocol UndoActionRespondable {
|
|
||||||
func undo(_ sender: AnyObject)
|
|
||||||
func redo(_ sender: AnyObject)
|
|
||||||
}
|
}
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 104 KiB After Width: | Height: | Size: 88 KiB |
|
Before Width: | Height: | Size: 9.6 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 995 B After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 2.1 KiB After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 47 KiB After Width: | Height: | Size: 49 KiB |
|
Before Width: | Height: | Size: 4.5 KiB After Width: | Height: | Size: 4.9 KiB |
@@ -0,0 +1,28 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright © 2018-2019 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
class LaunchedAtLoginDetector {
|
||||||
|
static let launchCode = "LaunchedByWireGuardLoginItemHelper"
|
||||||
|
|
||||||
|
static func isLaunchedAtLogin(openAppleEvent: NSAppleEventDescriptor) -> Bool {
|
||||||
|
guard isOpenEvent(openAppleEvent) else { return false }
|
||||||
|
guard let propData = openAppleEvent.paramDescriptor(forKeyword: keyAEPropData) else { return false }
|
||||||
|
return propData.stringValue == launchCode
|
||||||
|
}
|
||||||
|
|
||||||
|
static func isReopenedByLoginItemHelper(reopenAppleEvent: NSAppleEventDescriptor) -> Bool {
|
||||||
|
guard isReopenEvent(reopenAppleEvent) else { return false }
|
||||||
|
guard let propData = reopenAppleEvent.paramDescriptor(forKeyword: keyAEPropData) else { return false }
|
||||||
|
return propData.stringValue == launchCode
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
||||||
@@ -19,9 +19,9 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>1.0</string>
|
<string>$(VERSION_NAME)</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1</string>
|
<string>$(VERSION_ID)</string>
|
||||||
<key>LSMinimumSystemVersion</key>
|
<key>LSMinimumSystemVersion</key>
|
||||||
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
|
||||||
<key>NSHumanReadableCopyright</key>
|
<key>NSHumanReadableCopyright</key>
|
||||||
@@ -30,5 +30,7 @@
|
|||||||
<string>NSApplication</string>
|
<string>NSApplication</string>
|
||||||
<key>LSBackgroundOnly</key>
|
<key>LSBackgroundOnly</key>
|
||||||
<true/>
|
<true/>
|
||||||
|
<key>com.wireguard.macos.app_id</key>
|
||||||
|
<string>$(APP_ID_MACOS)</string>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|||||||
@@ -5,12 +5,13 @@
|
|||||||
|
|
||||||
int main(int argc, char *argv[])
|
int main(int argc, char *argv[])
|
||||||
{
|
{
|
||||||
NSURL *bundleURL = [NSBundle.mainBundle bundleURL];
|
NSString *appIdInfoDictionaryKey = @"com.wireguard.macos.app_id";
|
||||||
|
NSString *appId = [NSBundle.mainBundle objectForInfoDictionaryKey:appIdInfoDictionaryKey];
|
||||||
|
|
||||||
// From <path>/WireGuard.app/Contents/Library/LoginItems/WireGuardLoginItemHelper.app, derive <path>/WireGuard.app
|
NSString *launchCode = @"LaunchedByWireGuardLoginItemHelper";
|
||||||
for (int i = 0; i < 4; ++i)
|
NSAppleEventDescriptor *paramDescriptor = [NSAppleEventDescriptor descriptorWithString:launchCode];
|
||||||
bundleURL = [bundleURL URLByDeletingLastPathComponent];
|
|
||||||
|
|
||||||
[NSWorkspace.sharedWorkspace launchApplication:[bundleURL path]];
|
[NSWorkspace.sharedWorkspace launchAppWithBundleIdentifier:appId options:NSWorkspaceLaunchWithoutActivation
|
||||||
|
additionalEventParamDescriptor:paramDescriptor launchIdentifier:NULL];
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,11 +19,7 @@ class MacAppStoreUpdateDetector {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func isQuitEvent(_ event: NSAppleEventDescriptor) -> Bool {
|
private func isQuitEvent(_ event: NSAppleEventDescriptor) -> Bool {
|
||||||
if let eventClassDescriptor = event.attributeDescriptor(forKeyword: keyEventClassAttr),
|
return event.eventClass == kCoreEventClass && event.eventID == kAEQuitApplication
|
||||||
let eventIdDescriptor = event.attributeDescriptor(forKeyword: keyEventIDAttr) {
|
|
||||||
return eventClassDescriptor.typeCodeValue == kCoreEventClass && eventIdDescriptor.typeCodeValue == kAEQuitApplication
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getExecutablePath(from pid: pid_t) -> String? {
|
private func getExecutablePath(from pid: pid_t) -> String? {
|
||||||
|
|||||||
@@ -0,0 +1,130 @@
|
|||||||
|
// SPDX-License-Identifier: MIT
|
||||||
|
// Copyright © 2018 WireGuard LLC. All Rights Reserved.
|
||||||
|
|
||||||
|
import Cocoa
|
||||||
|
|
||||||
|
// swiftlint:disable colon
|
||||||
|
|
||||||
|
class MainMenu: NSMenu {
|
||||||
|
init() {
|
||||||
|
super.init(title: "")
|
||||||
|
addSubmenu(createApplicationMenu())
|
||||||
|
addSubmenu(createFileMenu())
|
||||||
|
addSubmenu(createEditMenu())
|
||||||
|
addSubmenu(createTunnelMenu())
|
||||||
|
addSubmenu(createWindowMenu())
|
||||||
|
}
|
||||||
|
|
||||||
|
required init(coder decoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
private func addSubmenu(_ menu: NSMenu) {
|
||||||
|
let menuItem = self.addItem(withTitle: "", action: nil, keyEquivalent: "")
|
||||||
|
self.setSubmenu(menu, for: menuItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func createApplicationMenu() -> NSMenu {
|
||||||
|
let menu = NSMenu()
|
||||||
|
|
||||||
|
let aboutMenuItem = menu.addItem(withTitle: tr("macMenuAbout"),
|
||||||
|
action: #selector(AppDelegate.aboutClicked), keyEquivalent: "")
|
||||||
|
aboutMenuItem.target = NSApp.delegate
|
||||||
|
|
||||||
|
menu.addItem(NSMenuItem.separator())
|
||||||
|
|
||||||
|
menu.addItem(withTitle: tr("macMenuViewLog"),
|
||||||
|
action: #selector(TunnelsListTableViewController.handleViewLogAction), keyEquivalent: "")
|
||||||
|
|
||||||
|
menu.addItem(NSMenuItem.separator())
|
||||||
|
|
||||||
|
let hideMenuItem = menu.addItem(withTitle: tr("macMenuHideApp"),
|
||||||
|
action: #selector(NSApplication.hide), keyEquivalent: "h")
|
||||||
|
hideMenuItem.target = NSApp
|
||||||
|
let hideOthersMenuItem = menu.addItem(withTitle: tr("macMenuHideOtherApps"),
|
||||||
|
action: #selector(NSApplication.hideOtherApplications), keyEquivalent: "h")
|
||||||
|
hideOthersMenuItem.keyEquivalentModifierMask = [.command, .option]
|
||||||
|
hideOthersMenuItem.target = NSApp
|
||||||
|
let showAllMenuItem = menu.addItem(withTitle: tr("macMenuShowAllApps"),
|
||||||
|
action: #selector(NSApplication.unhideAllApplications), keyEquivalent: "")
|
||||||
|
showAllMenuItem.target = NSApp
|
||||||
|
|
||||||
|
menu.addItem(NSMenuItem.separator())
|
||||||
|
|
||||||
|
menu.addItem(withTitle: tr("macMenuQuitManagingTunnels"),
|
||||||
|
action: #selector(NSWindow.performClose(_:)), keyEquivalent: "q")
|
||||||
|
|
||||||
|
return menu
|
||||||
|
}
|
||||||
|
|
||||||
|
private func createFileMenu() -> NSMenu {
|
||||||
|
let menu = NSMenu(title: tr("macMenuFile"))
|
||||||
|
|
||||||
|
menu.addItem(withTitle: tr("macMenuAddEmptyTunnel"),
|
||||||
|
action: #selector(TunnelsListTableViewController.handleAddEmptyTunnelAction), keyEquivalent: "n")
|
||||||
|
menu.addItem(withTitle: tr("macMenuImportTunnels"),
|
||||||
|
action: #selector(TunnelsListTableViewController.handleImportTunnelAction), keyEquivalent: "o")
|
||||||
|
|
||||||
|
menu.addItem(NSMenuItem.separator())
|
||||||
|
|
||||||
|
menu.addItem(withTitle: tr("macMenuExportTunnels"),
|
||||||
|
action: #selector(TunnelsListTableViewController.handleExportTunnelsAction), keyEquivalent: "")
|
||||||
|
|
||||||
|
menu.addItem(NSMenuItem.separator())
|
||||||
|
|
||||||
|
menu.addItem(withTitle: tr("macMenuCloseWindow"), action: #selector(NSWindow.performClose(_:)), keyEquivalent:"w")
|
||||||
|
|
||||||
|
return menu
|
||||||
|
}
|
||||||
|
|
||||||
|
private func createEditMenu() -> NSMenu {
|
||||||
|
let menu = NSMenu(title: tr("macMenuEdit"))
|
||||||
|
|
||||||
|
menu.addItem(withTitle: "", action: #selector(UndoActionRespondable.undo(_:)), keyEquivalent:"z")
|
||||||
|
menu.addItem(withTitle: "", action: #selector(UndoActionRespondable.redo(_:)), keyEquivalent:"Z")
|
||||||
|
|
||||||
|
menu.addItem(NSMenuItem.separator())
|
||||||
|
|
||||||
|
menu.addItem(withTitle: tr("macMenuCut"), action: #selector(NSText.cut(_:)), keyEquivalent:"x")
|
||||||
|
menu.addItem(withTitle: tr("macMenuCopy"), action: #selector(NSText.copy(_:)), keyEquivalent:"c")
|
||||||
|
menu.addItem(withTitle: tr("macMenuPaste"), action: #selector(NSText.paste(_:)), keyEquivalent:"v")
|
||||||
|
|
||||||
|
menu.addItem(NSMenuItem.separator())
|
||||||
|
|
||||||
|
menu.addItem(withTitle: tr("macMenuSelectAll"), action: #selector(NSText.selectAll(_:)), keyEquivalent:"a")
|
||||||
|
|
||||||
|
return menu
|
||||||
|
}
|
||||||
|
|
||||||
|
private func createTunnelMenu() -> NSMenu {
|
||||||
|
let menu = NSMenu(title: tr("macMenuTunnel"))
|
||||||
|
|
||||||
|
menu.addItem(withTitle: tr("macMenuToggleStatus"), action: #selector(TunnelDetailTableViewController.handleToggleActiveStatusAction), keyEquivalent:"t")
|
||||||
|
|
||||||
|
menu.addItem(NSMenuItem.separator())
|
||||||
|
|
||||||
|
menu.addItem(withTitle: tr("macMenuEditTunnel"), action: #selector(TunnelDetailTableViewController.handleEditTunnelAction), keyEquivalent:"e")
|
||||||
|
menu.addItem(withTitle: tr("macMenuDeleteSelected"), action: #selector(TunnelsListTableViewController.handleRemoveTunnelAction), keyEquivalent: "")
|
||||||
|
|
||||||
|
return menu
|
||||||
|
}
|
||||||
|
|
||||||
|
private func createWindowMenu() -> NSMenu {
|
||||||
|
let menu = NSMenu(title: tr("macMenuWindow"))
|
||||||
|
|
||||||
|
menu.addItem(withTitle: tr("macMenuMinimize"), action: #selector(NSWindow.performMiniaturize(_:)), keyEquivalent:"m")
|
||||||
|
menu.addItem(withTitle: tr("macMenuZoom"), action: #selector(NSWindow.performZoom(_:)), keyEquivalent:"")
|
||||||
|
|
||||||
|
menu.addItem(NSMenuItem.separator())
|
||||||
|
|
||||||
|
let fullScreenMenuItem = menu.addItem(withTitle: "", action: #selector(NSWindow.toggleFullScreen(_:)), keyEquivalent:"f")
|
||||||
|
fullScreenMenuItem.keyEquivalentModifierMask = [.command, .control]
|
||||||
|
|
||||||
|
return menu
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc protocol UndoActionRespondable {
|
||||||
|
func undo(_ sender: AnyObject)
|
||||||
|
func redo(_ sender: AnyObject)
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
import Cocoa
|
import Cocoa
|
||||||
|
|
||||||
protocol StatusMenuWindowDelegate: class {
|
protocol StatusMenuWindowDelegate: class {
|
||||||
func manageTunnelsWindow() -> NSWindow
|
func showManageTunnelsWindow(completion: ((NSWindow?) -> Void)?)
|
||||||
}
|
}
|
||||||
|
|
||||||
class StatusMenu: NSMenu {
|
class StatusMenu: NSMenu {
|
||||||
@@ -127,8 +127,8 @@ class StatusMenu: NSMenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func addApplicationItems() {
|
func addApplicationItems() {
|
||||||
let aboutItem = NSMenuItem(title: tr("macMenuAbout"), action: #selector(aboutClicked), keyEquivalent: "")
|
let aboutItem = NSMenuItem(title: tr("macMenuAbout"), action: #selector(AppDelegate.aboutClicked), keyEquivalent: "")
|
||||||
aboutItem.target = self
|
aboutItem.target = NSApp.delegate
|
||||||
addItem(aboutItem)
|
addItem(aboutItem)
|
||||||
let quitItem = NSMenuItem(title: tr("macMenuQuit"), action: #selector(AppDelegate.quit), keyEquivalent: "")
|
let quitItem = NSMenuItem(title: tr("macMenuQuit"), action: #selector(AppDelegate.quit), keyEquivalent: "")
|
||||||
quitItem.target = NSApp.delegate
|
quitItem.target = NSApp.delegate
|
||||||
@@ -151,32 +151,16 @@ class StatusMenu: NSMenu {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc func manageTunnelsClicked() {
|
@objc func manageTunnelsClicked() {
|
||||||
NSApp.activate(ignoringOtherApps: true)
|
windowDelegate?.showManageTunnelsWindow(completion: nil)
|
||||||
guard let manageTunnelsWindow = windowDelegate?.manageTunnelsWindow() else { return }
|
|
||||||
manageTunnelsWindow.makeKeyAndOrderFront(self)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func importTunnelsClicked() {
|
@objc func importTunnelsClicked() {
|
||||||
NSApp.activate(ignoringOtherApps: true)
|
windowDelegate?.showManageTunnelsWindow { [weak self] manageTunnelsWindow in
|
||||||
guard let manageTunnelsWindow = windowDelegate?.manageTunnelsWindow() else { return }
|
guard let self = self else { return }
|
||||||
manageTunnelsWindow.makeKeyAndOrderFront(self)
|
guard let manageTunnelsWindow = manageTunnelsWindow else { return }
|
||||||
ImportPanelPresenter.presentImportPanel(tunnelsManager: tunnelsManager, sourceVC: manageTunnelsWindow.contentViewController)
|
ImportPanelPresenter.presentImportPanel(tunnelsManager: self.tunnelsManager,
|
||||||
}
|
sourceVC: manageTunnelsWindow.contentViewController)
|
||||||
|
|
||||||
@objc func aboutClicked() {
|
|
||||||
var appVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "Unknown"
|
|
||||||
if let appBuild = Bundle.main.infoDictionary?["CFBundleVersion"] as? String {
|
|
||||||
appVersion += " (\(appBuild))"
|
|
||||||
}
|
}
|
||||||
let appVersionString = [
|
|
||||||
tr(format: "macAppVersion (%@)", appVersion),
|
|
||||||
tr(format: "macGoBackendVersion (%@)", WIREGUARD_GO_VERSION)
|
|
||||||
].joined(separator: "\n")
|
|
||||||
NSApp.activate(ignoringOtherApps: true)
|
|
||||||
NSApp.orderFrontStandardAboutPanel(options: [
|
|
||||||
.applicationVersion: appVersionString,
|
|
||||||
.version: ""
|
|
||||||
])
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -220,7 +220,8 @@ class LogViewController: NSViewController {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
DispatchQueue.main.async { [weak self] in
|
DispatchQueue.main.async { [weak self] in
|
||||||
self?.dismiss(self)
|
guard let self = self else { return }
|
||||||
|
self.presentingViewController?.dismiss(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -228,7 +229,7 @@ class LogViewController: NSViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc func closeClicked() {
|
@objc func closeClicked() {
|
||||||
dismiss(self)
|
presentingViewController?.dismiss(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func copy(_ sender: Any?) {
|
@objc func copy(_ sender: Any?) {
|
||||||
@@ -261,3 +262,9 @@ extension LogViewController: NSTableViewDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension LogViewController {
|
||||||
|
override func cancelOperation(_ sender: Any?) {
|
||||||
|
closeClicked()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -117,25 +117,19 @@ extension ManageTunnelsRootViewController: TunnelsListTableViewControllerDelegat
|
|||||||
}
|
}
|
||||||
|
|
||||||
extension ManageTunnelsRootViewController {
|
extension ManageTunnelsRootViewController {
|
||||||
override func keyDown(with event: NSEvent) {
|
override func supplementalTarget(forAction action: Selector, sender: Any?) -> Any? {
|
||||||
let modifierFlags = event.modifierFlags.rawValue & NSEvent.ModifierFlags.deviceIndependentFlagsMask.rawValue
|
switch action {
|
||||||
let isCmdOrCmdShiftDown = (modifierFlags == NSEvent.ModifierFlags.command.rawValue || modifierFlags == NSEvent.ModifierFlags.command.rawValue | NSEvent.ModifierFlags.shift.rawValue)
|
case #selector(TunnelsListTableViewController.handleViewLogAction),
|
||||||
|
#selector(TunnelsListTableViewController.handleAddEmptyTunnelAction),
|
||||||
if event.specialKey == .delete {
|
#selector(TunnelsListTableViewController.handleImportTunnelAction),
|
||||||
tunnelsListVC?.handleRemoveTunnelAction()
|
#selector(TunnelsListTableViewController.handleExportTunnelsAction),
|
||||||
} else if isCmdOrCmdShiftDown {
|
#selector(TunnelsListTableViewController.handleRemoveTunnelAction):
|
||||||
switch event.charactersIgnoringModifiers {
|
return tunnelsListVC
|
||||||
case "n":
|
case #selector(TunnelDetailTableViewController.handleToggleActiveStatusAction),
|
||||||
tunnelsListVC?.handleAddEmptyTunnelAction()
|
#selector(TunnelDetailTableViewController.handleEditTunnelAction):
|
||||||
case "o":
|
return tunnelDetailVC
|
||||||
tunnelsListVC?.handleImportTunnelAction()
|
default:
|
||||||
case "t":
|
return super.supplementalTarget(forAction: action, sender: sender)
|
||||||
tunnelDetailVC?.handleToggleActiveStatusAction()
|
|
||||||
case "e":
|
|
||||||
tunnelDetailVC?.handleEditTunnelAction()
|
|
||||||
default:
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -101,9 +101,14 @@ class TunnelDetailTableViewController: NSViewController {
|
|||||||
super.init(nibName: nil, bundle: nil)
|
super.init(nibName: nil, bundle: nil)
|
||||||
updateTableViewModelRowsBySection()
|
updateTableViewModelRowsBySection()
|
||||||
updateTableViewModelRows()
|
updateTableViewModelRows()
|
||||||
updateStatus()
|
|
||||||
statusObservationToken = tunnel.observe(\TunnelContainer.status) { [weak self] _, _ in
|
statusObservationToken = tunnel.observe(\TunnelContainer.status) { [weak self] _, _ in
|
||||||
self?.updateStatus()
|
guard let self = self else { return }
|
||||||
|
if tunnel.status == .active {
|
||||||
|
self.startUpdatingRuntimeConfiguration()
|
||||||
|
} else if tunnel.status == .inactive {
|
||||||
|
self.reloadRuntimeConfiguration()
|
||||||
|
self.stopUpdatingRuntimeConfiguration()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,15 +205,6 @@ class TunnelDetailTableViewController: NSViewController {
|
|||||||
tableViewModelRows = tableViewModelRowsBySection.flatMap { $0.filter { $0.isVisible }.map { $0.modelRow } }
|
tableViewModelRows = tableViewModelRowsBySection.flatMap { $0.filter { $0.isVisible }.map { $0.modelRow } }
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateStatus() {
|
|
||||||
if tunnel.status == .active {
|
|
||||||
startUpdatingRuntimeConfiguration()
|
|
||||||
} else if tunnel.status == .inactive {
|
|
||||||
reloadRuntimeConfiguration()
|
|
||||||
stopUpdatingRuntimeConfiguration()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@objc func handleEditTunnelAction() {
|
@objc func handleEditTunnelAction() {
|
||||||
PrivateDataConfirmation.confirmAccess(to: tr("macViewPrivateData")) { [weak self] in
|
PrivateDataConfirmation.confirmAccess(to: tr("macViewPrivateData")) { [weak self] in
|
||||||
guard let self = self else { return }
|
guard let self = self else { return }
|
||||||
@@ -227,6 +223,12 @@ class TunnelDetailTableViewController: NSViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func viewWillAppear() {
|
||||||
|
if tunnel.status == .active {
|
||||||
|
startUpdatingRuntimeConfiguration()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override func viewWillDisappear() {
|
override func viewWillDisappear() {
|
||||||
super.viewWillDisappear()
|
super.viewWillDisappear()
|
||||||
if let tunnelEditVC = tunnelEditVC {
|
if let tunnelEditVC = tunnelEditVC {
|
||||||
@@ -503,7 +505,6 @@ extension TunnelDetailTableViewController: TunnelEditViewControllerDelegate {
|
|||||||
onDemandViewModel = ActivateOnDemandViewModel(tunnel: tunnel)
|
onDemandViewModel = ActivateOnDemandViewModel(tunnel: tunnel)
|
||||||
updateTableViewModelRowsBySection()
|
updateTableViewModelRowsBySection()
|
||||||
updateTableViewModelRows()
|
updateTableViewModelRows()
|
||||||
updateStatus()
|
|
||||||
tableView.reloadData()
|
tableView.reloadData()
|
||||||
self.tunnelEditVC = nil
|
self.tunnelEditVC = nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,6 +73,8 @@ class TunnelEditViewController: NSViewController {
|
|||||||
button.title = tr("macEditSave")
|
button.title = tr("macEditSave")
|
||||||
button.setButtonType(.momentaryPushIn)
|
button.setButtonType(.momentaryPushIn)
|
||||||
button.bezelStyle = .rounded
|
button.bezelStyle = .rounded
|
||||||
|
button.keyEquivalent = "s"
|
||||||
|
button.keyEquivalentModifierMask = [.command]
|
||||||
return button
|
return button
|
||||||
}()
|
}()
|
||||||
|
|
||||||
@@ -235,24 +237,26 @@ class TunnelEditViewController: NSViewController {
|
|||||||
if let tunnel = tunnel {
|
if let tunnel = tunnel {
|
||||||
// We're modifying an existing tunnel
|
// We're modifying an existing tunnel
|
||||||
tunnelsManager.modify(tunnel: tunnel, tunnelConfiguration: tunnelConfiguration, onDemandOption: onDemandOption) { [weak self] error in
|
tunnelsManager.modify(tunnel: tunnel, tunnelConfiguration: tunnelConfiguration, onDemandOption: onDemandOption) { [weak self] error in
|
||||||
self?.setUserInteractionEnabled(true)
|
guard let self = self else { return }
|
||||||
|
self.setUserInteractionEnabled(true)
|
||||||
if let error = error {
|
if let error = error {
|
||||||
ErrorPresenter.showErrorAlert(error: error, from: self)
|
ErrorPresenter.showErrorAlert(error: error, from: self)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
self?.dismiss(self)
|
self.delegate?.tunnelSaved(tunnel: tunnel)
|
||||||
self?.delegate?.tunnelSaved(tunnel: tunnel)
|
self.presentingViewController?.dismiss(self)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// We're creating a new tunnel
|
// We're creating a new tunnel
|
||||||
self.tunnelsManager.add(tunnelConfiguration: tunnelConfiguration, onDemandOption: onDemandOption) { [weak self] result in
|
self.tunnelsManager.add(tunnelConfiguration: tunnelConfiguration, onDemandOption: onDemandOption) { [weak self] result in
|
||||||
self?.setUserInteractionEnabled(true)
|
guard let self = self else { return }
|
||||||
|
self.setUserInteractionEnabled(true)
|
||||||
switch result {
|
switch result {
|
||||||
case .failure(let error):
|
case .failure(let error):
|
||||||
ErrorPresenter.showErrorAlert(error: error, from: self)
|
ErrorPresenter.showErrorAlert(error: error, from: self)
|
||||||
case .success(let tunnel):
|
case .success(let tunnel):
|
||||||
self?.dismiss(self)
|
self.delegate?.tunnelSaved(tunnel: tunnel)
|
||||||
self?.delegate?.tunnelSaved(tunnel: tunnel)
|
self.presentingViewController?.dismiss(self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -260,7 +264,7 @@ class TunnelEditViewController: NSViewController {
|
|||||||
|
|
||||||
@objc func handleDiscardAction() {
|
@objc func handleDiscardAction() {
|
||||||
delegate?.tunnelEditingCancelled()
|
delegate?.tunnelEditingCancelled()
|
||||||
dismiss(self)
|
presentingViewController?.dismiss(self)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateExcludePrivateIPsVisibility(singlePeerAllowedIPs: [String]?) {
|
func updateExcludePrivateIPsVisibility(singlePeerAllowedIPs: [String]?) {
|
||||||
@@ -287,3 +291,9 @@ class TunnelEditViewController: NSViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension TunnelEditViewController {
|
||||||
|
override func cancelOperation(_ sender: Any?) {
|
||||||
|
handleDiscardAction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -309,6 +309,23 @@ extension TunnelsListTableViewController: NSTableViewDelegate {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension TunnelsListTableViewController {
|
||||||
|
override func keyDown(with event: NSEvent) {
|
||||||
|
if event.specialKey == .delete {
|
||||||
|
handleRemoveTunnelAction()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension TunnelsListTableViewController: NSMenuItemValidation {
|
||||||
|
func validateMenuItem(_ menuItem: NSMenuItem) -> Bool {
|
||||||
|
if menuItem.action == #selector(TunnelsListTableViewController.handleRemoveTunnelAction) {
|
||||||
|
return !tableView.selectedRowIndexes.isEmpty
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class FillerButton: NSButton {
|
class FillerButton: NSButton {
|
||||||
override var intrinsicContentSize: NSSize {
|
override var intrinsicContentSize: NSSize {
|
||||||
return NSSize(width: NSView.noIntrinsicMetric, height: NSView.noIntrinsicMetric)
|
return NSSize(width: NSView.noIntrinsicMetric, height: NSView.noIntrinsicMetric)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||||||
private var handle: Int32?
|
private var handle: Int32?
|
||||||
private var networkMonitor: NWPathMonitor?
|
private var networkMonitor: NWPathMonitor?
|
||||||
private var ifname: String?
|
private var ifname: String?
|
||||||
|
private var lastSeenInterfaces: [String] = []
|
||||||
private var packetTunnelSettingsGenerator: PacketTunnelSettingsGenerator?
|
private var packetTunnelSettingsGenerator: PacketTunnelSettingsGenerator?
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
@@ -142,18 +143,19 @@ class PacketTunnelProvider: NEPacketTunnelProvider {
|
|||||||
|
|
||||||
private func pathUpdate(path: Network.NWPath) {
|
private func pathUpdate(path: Network.NWPath) {
|
||||||
guard let handle = handle else { return }
|
guard let handle = handle else { return }
|
||||||
|
guard let ifname = ifname else { return }
|
||||||
wg_log(.debug, message: "Network change detected with \(path.status) route and interface order \(path.availableInterfaces)")
|
wg_log(.debug, message: "Network change detected with \(path.status) route and interface order \(path.availableInterfaces)")
|
||||||
|
guard path.status == .satisfied else { return }
|
||||||
|
|
||||||
#if os(iOS)
|
#if os(iOS)
|
||||||
if let packetTunnelSettingsGenerator = packetTunnelSettingsGenerator {
|
if let packetTunnelSettingsGenerator = packetTunnelSettingsGenerator {
|
||||||
_ = packetTunnelSettingsGenerator.endpointUapiConfiguration().withGoString { return wgSetConfig(handle, $0) }
|
_ = packetTunnelSettingsGenerator.endpointUapiConfiguration().withGoString { return wgSetConfig(handle, $0) }
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
var interfaces = path.availableInterfaces
|
let interfaces = path.availableInterfaces.filter { $0.name != ifname }.compactMap { $0.name }
|
||||||
if let ifname = ifname {
|
if !interfaces.elementsEqual(lastSeenInterfaces) {
|
||||||
interfaces = interfaces.filter { $0.name != ifname }
|
lastSeenInterfaces = interfaces
|
||||||
}
|
wgBumpSockets(handle)
|
||||||
if let ifscope = interfaces.first?.index {
|
|
||||||
wgBindInterfaceScope(handle, Int32(ifscope))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -165,22 +165,14 @@ func wgGetConfig(tunnelHandle int32) *C.char {
|
|||||||
writer.Flush()
|
writer.Flush()
|
||||||
return C.CString(settings.String())
|
return C.CString(settings.String())
|
||||||
}
|
}
|
||||||
|
//export wgBumpSockets
|
||||||
//export wgBindInterfaceScope
|
func wgBumpSockets(tunnelHandle int32) {
|
||||||
func wgBindInterfaceScope(tunnelHandle int32, ifscope int32) {
|
|
||||||
device, ok := tunnelHandles[tunnelHandle]
|
device, ok := tunnelHandles[tunnelHandle]
|
||||||
if !ok {
|
if !ok {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
device.Info.Printf("Binding sockets to interface %d\n", ifscope)
|
device.BindUpdate()
|
||||||
err := device.BindSocketToInterface4(uint32(ifscope))
|
device.SendKeepalivesToPeersWithCurrentKeypair()
|
||||||
if err != nil {
|
|
||||||
device.Error.Printf("Unable to bind v4 socket to interface:", err)
|
|
||||||
}
|
|
||||||
err = device.BindSocketToInterface6(uint32(ifscope))
|
|
||||||
if err != nil {
|
|
||||||
device.Error.Printf("Unable to bind v6 socket to interface:", err)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
//export wgVersion
|
//export wgVersion
|
||||||
|
|||||||
@@ -3,6 +3,6 @@ module golang.zx2c4.com/wireguard/ios
|
|||||||
go 1.12
|
go 1.12
|
||||||
|
|
||||||
require (
|
require (
|
||||||
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67
|
golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20190409083948-18fa27047265
|
golang.zx2c4.com/wireguard v0.0.20190518-0.20190530131616-d9f995209c3c
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -2,12 +2,22 @@ github.com/Microsoft/go-winio v0.4.12/go.mod h1:VhR8bwka0BXejwEJY73c50VrPtXAaKcy
|
|||||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576 h1:aUX/1G2gFSs4AsJJg2cL3HuoRhCSCz733FE5GUSuaT4=
|
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576 h1:aUX/1G2gFSs4AsJJg2cL3HuoRhCSCz733FE5GUSuaT4=
|
||||||
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
golang.org/x/crypto v0.0.0-20190320223903-b7391e95e576/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||||
|
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f h1:R423Cnkcp5JABoeemiGEPlt9tHXFfw5kvc0yqlxRPWo=
|
||||||
|
golang.org/x/crypto v0.0.0-20190513172903-22d7a77e9e5f/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||||
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53 h1:kcXqo9vE6fsZY5X5Rd7R1l7fTgnWaDCVmln65REefiE=
|
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53 h1:kcXqo9vE6fsZY5X5Rd7R1l7fTgnWaDCVmln65REefiE=
|
||||||
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
golang.org/x/net v0.0.0-20190320064053-1272bf9dcd53/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||||
|
golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
|
||||||
|
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||||
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190321052220-f7bb7a8bee54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67 h1:1Fzlr8kkDLQwqMP8GxrhptBLqZG/EDpiATneiZHY998=
|
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67 h1:1Fzlr8kkDLQwqMP8GxrhptBLqZG/EDpiATneiZHY998=
|
||||||
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
|
golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5 h1:f005F/Jl5JLP036x7QIvUVhNTqxvSYwFIiyOh2q12iU=
|
||||||
|
golang.org/x/sys v0.0.0-20190522044717-8097e1b27ff5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20190409083948-18fa27047265 h1:ujM5BaP4MD/2MJZ1n7pmw6IIsyyS7SyPl0fDefnx/2o=
|
golang.zx2c4.com/wireguard v0.0.0-20190409083948-18fa27047265 h1:ujM5BaP4MD/2MJZ1n7pmw6IIsyyS7SyPl0fDefnx/2o=
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20190409083948-18fa27047265/go.mod h1:u0Cl3X+pyWdXaax3S583DQrnGDuTASO0QdlKFrs8r/8=
|
golang.zx2c4.com/wireguard v0.0.0-20190409083948-18fa27047265/go.mod h1:u0Cl3X+pyWdXaax3S583DQrnGDuTASO0QdlKFrs8r/8=
|
||||||
|
golang.zx2c4.com/wireguard v0.0.20190518-0.20190530131616-d9f995209c3c h1:nUKiRKxUjK8Zghs+JBZZYXo0g51BlXVqkr6mDfm22ow=
|
||||||
|
golang.zx2c4.com/wireguard v0.0.20190518-0.20190530131616-d9f995209c3c/go.mod h1:8X7vp4RrsvM83bde6vQ94DsL4ZpjUViVUym8aa8zGhs=
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ extern int wgTurnOn(gostring_t settings, int32_t tun_fd);
|
|||||||
extern void wgTurnOff(int handle);
|
extern void wgTurnOff(int handle);
|
||||||
extern int64_t wgSetConfig(int handle, gostring_t settings);
|
extern int64_t wgSetConfig(int handle, gostring_t settings);
|
||||||
extern char *wgGetConfig(int handle);
|
extern char *wgGetConfig(int handle);
|
||||||
extern void wgBindInterfaceScope(int handle, int32_t ifscope);
|
extern void wgBumpSockets(int handle);
|
||||||
extern const char *wgVersion();
|
extern const char *wgVersion();
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|||||||