Feature: Use apple prefer way to run privileged task
This commit is contained in:
parent
a6e09a625e
commit
b42ba7123f
|
@ -5,3 +5,4 @@ ClashX.h
|
||||||
ClashX/Resources/dashboard
|
ClashX/Resources/dashboard
|
||||||
ClashX.app
|
ClashX.app
|
||||||
*.dmg
|
*.dmg
|
||||||
|
**/xcuserdata/
|
||||||
|
|
|
@ -44,20 +44,23 @@
|
||||||
499A485C22ED793C00F6C675 /* NSView+Nib.swift in Sources */ = {isa = PBXBuildFile; fileRef = 499A485B22ED793C00F6C675 /* NSView+Nib.swift */; };
|
499A485C22ED793C00F6C675 /* NSView+Nib.swift in Sources */ = {isa = PBXBuildFile; fileRef = 499A485B22ED793C00F6C675 /* NSView+Nib.swift */; };
|
||||||
499A485E22ED9B7C00F6C675 /* NSTableView+Reload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 499A485D22ED9B7C00F6C675 /* NSTableView+Reload.swift */; };
|
499A485E22ED9B7C00F6C675 /* NSTableView+Reload.swift in Sources */ = {isa = PBXBuildFile; fileRef = 499A485D22ED9B7C00F6C675 /* NSTableView+Reload.swift */; };
|
||||||
499A486522EEA3FD00F6C675 /* Array+Safe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 499A486422EEA3FC00F6C675 /* Array+Safe.swift */; };
|
499A486522EEA3FD00F6C675 /* Array+Safe.swift in Sources */ = {isa = PBXBuildFile; fileRef = 499A486422EEA3FC00F6C675 /* Array+Safe.swift */; };
|
||||||
49AF9104225B2B03004CC077 /* ProxyConfig in CopyFiles */ = {isa = PBXBuildFile; fileRef = 49AF9103225B2A96004CC077 /* ProxyConfig */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
|
||||||
49B1086A216A356D0064FFCE /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49B10869216A356D0064FFCE /* String+Extension.swift */; };
|
49B1086A216A356D0064FFCE /* String+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49B10869216A356D0064FFCE /* String+Extension.swift */; };
|
||||||
49BC061C212931F4005A0FE7 /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49BC061B212931F4005A0FE7 /* AboutViewController.swift */; };
|
49BC061C212931F4005A0FE7 /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49BC061B212931F4005A0FE7 /* AboutViewController.swift */; };
|
||||||
49C9EF64223E78F5005D8B6A /* ClashProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49C9EF63223E78F5005D8B6A /* ClashProxy.swift */; };
|
49C9EF64223E78F5005D8B6A /* ClashProxy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49C9EF63223E78F5005D8B6A /* ClashProxy.swift */; };
|
||||||
49CF3B2120CD7463001EBF94 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49CF3B2020CD7463001EBF94 /* AppDelegate.swift */; };
|
49CF3B2120CD7463001EBF94 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49CF3B2020CD7463001EBF94 /* AppDelegate.swift */; };
|
||||||
49CF3B2520CD7465001EBF94 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 49CF3B2420CD7465001EBF94 /* Assets.xcassets */; };
|
49CF3B2520CD7465001EBF94 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 49CF3B2420CD7465001EBF94 /* Assets.xcassets */; };
|
||||||
49CF3B2820CD7465001EBF94 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 49CF3B2620CD7465001EBF94 /* Main.storyboard */; };
|
49CF3B2820CD7465001EBF94 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 49CF3B2620CD7465001EBF94 /* Main.storyboard */; };
|
||||||
49CF3B5C20CE8068001EBF94 /* ProxyConfigHelperManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49CF3B5B20CE8068001EBF94 /* ProxyConfigHelperManager.swift */; };
|
49CF3B5C20CE8068001EBF94 /* ClashResourceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49CF3B5B20CE8068001EBF94 /* ClashResourceManager.swift */; };
|
||||||
49CF3B5F20CE80D2001EBF94 /* install_proxy_helper.sh in Resources */ = {isa = PBXBuildFile; fileRef = 49CF3B5E20CE80D2001EBF94 /* install_proxy_helper.sh */; };
|
|
||||||
49CF3B6320CED9CF001EBF94 /* check_proxy_helper.sh in Resources */ = {isa = PBXBuildFile; fileRef = 49CF3B6220CED934001EBF94 /* check_proxy_helper.sh */; };
|
|
||||||
49CF3B6520CEE06C001EBF94 /* ConfigManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49CF3B6420CEE06C001EBF94 /* ConfigManager.swift */; };
|
49CF3B6520CEE06C001EBF94 /* ConfigManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49CF3B6420CEE06C001EBF94 /* ConfigManager.swift */; };
|
||||||
49E07A8C20D501A000A088A3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 49E07A8920D501A000A088A3 /* Main.storyboard */; };
|
49E07A8C20D501A000A088A3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 49E07A8920D501A000A088A3 /* Main.storyboard */; };
|
||||||
49EC8D1922F13F330079B8F4 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 49EC8D0F22F13DEE0079B8F4 /* Sparkle.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
49EC8D1922F13F330079B8F4 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 49EC8D0F22F13DEE0079B8F4 /* Sparkle.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||||
49EC8D1A22F13FFC0079B8F4 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49EC8D0F22F13DEE0079B8F4 /* Sparkle.framework */; };
|
49EC8D1A22F13FFC0079B8F4 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 49EC8D0F22F13DEE0079B8F4 /* Sparkle.framework */; };
|
||||||
|
F935B2F02307C52E009E4D33 /* com.west2online.ClashX.ProxyConfigHelper in Copy Files */ = {isa = PBXBuildFile; fileRef = F9A7C0692306E874007163C7 /* com.west2online.ClashX.ProxyConfigHelper */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
|
||||||
|
F935B2F42307CD32009E4D33 /* ProxyConfigHelper.m in Sources */ = {isa = PBXBuildFile; fileRef = F935B2F32307CD32009E4D33 /* ProxyConfigHelper.m */; };
|
||||||
|
F935B2FA23083EE6009E4D33 /* ProxySettingTool.m in Sources */ = {isa = PBXBuildFile; fileRef = F935B2F923083EE6009E4D33 /* ProxySettingTool.m */; };
|
||||||
|
F935B2FC23085515009E4D33 /* SystemProxyManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = F935B2FB23085515009E4D33 /* SystemProxyManager.swift */; };
|
||||||
|
F94245092306DABA0005B196 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 49E07A8A20D501A000A088A3 /* Main.storyboard */; };
|
||||||
|
F9A7C06C2306E874007163C7 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = F9A7C06B2306E874007163C7 /* main.m */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
|
@ -68,12 +71,12 @@
|
||||||
remoteGlobalIDString = 495A44BB20D2660A00888A0A;
|
remoteGlobalIDString = 495A44BB20D2660A00888A0A;
|
||||||
remoteInfo = ClashXLaunchHelper;
|
remoteInfo = ClashXLaunchHelper;
|
||||||
};
|
};
|
||||||
49AF9102225B2A96004CC077 /* PBXContainerItemProxy */ = {
|
F935B2EB2307B7CD009E4D33 /* PBXContainerItemProxy */ = {
|
||||||
isa = PBXContainerItemProxy;
|
isa = PBXContainerItemProxy;
|
||||||
containerPortal = 49AF90FE225B2A95004CC077 /* ProxyConfig.xcodeproj */;
|
containerPortal = 49CF3B1520CD7463001EBF94 /* Project object */;
|
||||||
proxyType = 2;
|
proxyType = 1;
|
||||||
remoteGlobalIDString = 49AF90F3225B17D2004CC077;
|
remoteGlobalIDString = F9A7C0682306E874007163C7;
|
||||||
remoteInfo = ProxyConfig;
|
remoteInfo = ProxyConfigHelper;
|
||||||
};
|
};
|
||||||
/* End PBXContainerItemProxy section */
|
/* End PBXContainerItemProxy section */
|
||||||
|
|
||||||
|
@ -101,13 +104,24 @@
|
||||||
663E4677213FCDC4006F11BB /* Copy Files */ = {
|
663E4677213FCDC4006F11BB /* Copy Files */ = {
|
||||||
isa = PBXCopyFilesBuildPhase;
|
isa = PBXCopyFilesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
dstPath = "";
|
dstPath = Contents/Library/LaunchServices;
|
||||||
dstSubfolderSpec = 7;
|
dstSubfolderSpec = 1;
|
||||||
files = (
|
files = (
|
||||||
49AF9104225B2B03004CC077 /* ProxyConfig in CopyFiles */,
|
F935B2F02307C52E009E4D33 /* com.west2online.ClashX.ProxyConfigHelper in Copy Files */,
|
||||||
);
|
);
|
||||||
|
name = "Copy Files";
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
F9A7C0672306E874007163C7 /* Copy Files */ = {
|
||||||
|
isa = PBXCopyFilesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
dstPath = /usr/share/man/man1/;
|
||||||
|
dstSubfolderSpec = 0;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
name = "Copy Files";
|
||||||
|
runOnlyForDeploymentPostprocessing = 1;
|
||||||
|
};
|
||||||
/* End PBXCopyFilesBuildPhase section */
|
/* End PBXCopyFilesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
|
@ -157,7 +171,6 @@
|
||||||
499A485B22ED793C00F6C675 /* NSView+Nib.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSView+Nib.swift"; sourceTree = "<group>"; };
|
499A485B22ED793C00F6C675 /* NSView+Nib.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSView+Nib.swift"; sourceTree = "<group>"; };
|
||||||
499A485D22ED9B7C00F6C675 /* NSTableView+Reload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTableView+Reload.swift"; sourceTree = "<group>"; };
|
499A485D22ED9B7C00F6C675 /* NSTableView+Reload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSTableView+Reload.swift"; sourceTree = "<group>"; };
|
||||||
499A486422EEA3FC00F6C675 /* Array+Safe.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+Safe.swift"; sourceTree = "<group>"; };
|
499A486422EEA3FC00F6C675 /* Array+Safe.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Array+Safe.swift"; sourceTree = "<group>"; };
|
||||||
49AF90FE225B2A95004CC077 /* ProxyConfig.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; name = ProxyConfig.xcodeproj; path = ProxyConfig/ProxyConfig.xcodeproj; sourceTree = "<group>"; };
|
|
||||||
49B10869216A356D0064FFCE /* String+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extension.swift"; sourceTree = "<group>"; };
|
49B10869216A356D0064FFCE /* String+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "String+Extension.swift"; sourceTree = "<group>"; };
|
||||||
49BC061B212931F4005A0FE7 /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = "<group>"; };
|
49BC061B212931F4005A0FE7 /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = "<group>"; };
|
||||||
49C9EF63223E78F5005D8B6A /* ClashProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClashProxy.swift; sourceTree = "<group>"; };
|
49C9EF63223E78F5005D8B6A /* ClashProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClashProxy.swift; sourceTree = "<group>"; };
|
||||||
|
@ -168,14 +181,22 @@
|
||||||
49CF3B2920CD7465001EBF94 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
49CF3B2920CD7465001EBF94 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
49CF3B2A20CD7465001EBF94 /* ClashX.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ClashX.entitlements; sourceTree = "<group>"; };
|
49CF3B2A20CD7465001EBF94 /* ClashX.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ClashX.entitlements; sourceTree = "<group>"; };
|
||||||
49CF3B3520CD75DF001EBF94 /* ClashX-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ClashX-Bridging-Header.h"; sourceTree = "<group>"; };
|
49CF3B3520CD75DF001EBF94 /* ClashX-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "ClashX-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||||
49CF3B5B20CE8068001EBF94 /* ProxyConfigHelperManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProxyConfigHelperManager.swift; sourceTree = "<group>"; };
|
49CF3B5B20CE8068001EBF94 /* ClashResourceManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClashResourceManager.swift; sourceTree = "<group>"; };
|
||||||
49CF3B5E20CE80D2001EBF94 /* install_proxy_helper.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = install_proxy_helper.sh; sourceTree = "<group>"; };
|
|
||||||
49CF3B6220CED934001EBF94 /* check_proxy_helper.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = check_proxy_helper.sh; sourceTree = "<group>"; };
|
|
||||||
49CF3B6420CEE06C001EBF94 /* ConfigManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigManager.swift; sourceTree = "<group>"; };
|
49CF3B6420CEE06C001EBF94 /* ConfigManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConfigManager.swift; sourceTree = "<group>"; };
|
||||||
49E07A8A20D501A000A088A3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Main.storyboard; sourceTree = "<group>"; };
|
49E07A8A20D501A000A088A3 /* Main.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = "<group>"; };
|
||||||
49EC8D0F22F13DEE0079B8F4 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Sparkle.framework; sourceTree = "<group>"; };
|
49EC8D0F22F13DEE0079B8F4 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = Sparkle.framework; sourceTree = "<group>"; };
|
||||||
5217C006C5A22A1CEA24BFC1 /* Pods-ClashX.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClashX.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ClashX/Pods-ClashX.debug.xcconfig"; sourceTree = "<group>"; };
|
5217C006C5A22A1CEA24BFC1 /* Pods-ClashX.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClashX.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ClashX/Pods-ClashX.debug.xcconfig"; sourceTree = "<group>"; };
|
||||||
A1485BCE642059532D01B8BA /* Pods-ClashX.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClashX.release.xcconfig"; path = "Pods/Target Support Files/Pods-ClashX/Pods-ClashX.release.xcconfig"; sourceTree = "<group>"; };
|
A1485BCE642059532D01B8BA /* Pods-ClashX.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClashX.release.xcconfig"; path = "Pods/Target Support Files/Pods-ClashX/Pods-ClashX.release.xcconfig"; sourceTree = "<group>"; };
|
||||||
|
F935B2EA2307B6BA009E4D33 /* Helper-Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Helper-Info.plist"; sourceTree = "<group>"; };
|
||||||
|
F935B2F12307C802009E4D33 /* Helper-Launchd.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Helper-Launchd.plist"; sourceTree = "<group>"; };
|
||||||
|
F935B2F22307CD32009E4D33 /* ProxyConfigHelper.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ProxyConfigHelper.h; sourceTree = "<group>"; };
|
||||||
|
F935B2F32307CD32009E4D33 /* ProxyConfigHelper.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ProxyConfigHelper.m; sourceTree = "<group>"; };
|
||||||
|
F935B2F52307D00D009E4D33 /* ProxyConfigRemoteProcessProtocol.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ProxyConfigRemoteProcessProtocol.h; sourceTree = "<group>"; };
|
||||||
|
F935B2F823083EE6009E4D33 /* ProxySettingTool.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = ProxySettingTool.h; sourceTree = "<group>"; };
|
||||||
|
F935B2F923083EE6009E4D33 /* ProxySettingTool.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = ProxySettingTool.m; sourceTree = "<group>"; };
|
||||||
|
F935B2FB23085515009E4D33 /* SystemProxyManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SystemProxyManager.swift; sourceTree = "<group>"; };
|
||||||
|
F9A7C0692306E874007163C7 /* com.west2online.ClashX.ProxyConfigHelper */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = com.west2online.ClashX.ProxyConfigHelper; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
|
F9A7C06B2306E874007163C7 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
|
@ -196,6 +217,13 @@
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
F9A7C0662306E874007163C7 /* Frameworks */ = {
|
||||||
|
isa = PBXFrameworksBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXFrameworksBuildPhase section */
|
/* End PBXFrameworksBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
/* Begin PBXGroup section */
|
||||||
|
@ -232,10 +260,11 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
4952C3CF2117027C004A4FA8 /* ConfigFileManager.swift */,
|
4952C3CF2117027C004A4FA8 /* ConfigFileManager.swift */,
|
||||||
49CF3B5B20CE8068001EBF94 /* ProxyConfigHelperManager.swift */,
|
49CF3B5B20CE8068001EBF94 /* ClashResourceManager.swift */,
|
||||||
49CF3B6420CEE06C001EBF94 /* ConfigManager.swift */,
|
49CF3B6420CEE06C001EBF94 /* ConfigManager.swift */,
|
||||||
495BFB8721919B9800C8779D /* RemoteConfigManager.swift */,
|
495BFB8721919B9800C8779D /* RemoteConfigManager.swift */,
|
||||||
4952C3BE2115C7CA004A4FA8 /* MenuItemFactory.swift */,
|
4952C3BE2115C7CA004A4FA8 /* MenuItemFactory.swift */,
|
||||||
|
F935B2FB23085515009E4D33 /* SystemProxyManager.swift */,
|
||||||
);
|
);
|
||||||
path = Managers;
|
path = Managers;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -275,6 +304,7 @@
|
||||||
495A44BD20D2660A00888A0A /* ClashXLaunchHelper */ = {
|
495A44BD20D2660A00888A0A /* ClashXLaunchHelper */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
49E07A8A20D501A000A088A3 /* Main.storyboard */,
|
||||||
49E07A8920D501A000A088A3 /* Main.storyboard */,
|
49E07A8920D501A000A088A3 /* Main.storyboard */,
|
||||||
495A44BE20D2660A00888A0A /* AppDelegate.swift */,
|
495A44BE20D2660A00888A0A /* AppDelegate.swift */,
|
||||||
495A44C720D2660B00888A0A /* Info.plist */,
|
495A44C720D2660B00888A0A /* Info.plist */,
|
||||||
|
@ -306,7 +336,6 @@
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
49761DA621C9497000AE13EF /* dashboard */,
|
49761DA621C9497000AE13EF /* dashboard */,
|
||||||
49CF3B5D20CE80D2001EBF94 /* script */,
|
|
||||||
);
|
);
|
||||||
path = Resources;
|
path = Resources;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -334,19 +363,12 @@
|
||||||
path = Basic;
|
path = Basic;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
49AF90FF225B2A95004CC077 /* Products */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
49AF9103225B2A96004CC077 /* ProxyConfig */,
|
|
||||||
);
|
|
||||||
name = Products;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
49CF3B1420CD7463001EBF94 = {
|
49CF3B1420CD7463001EBF94 = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
49CF3B1F20CD7463001EBF94 /* ClashX */,
|
49CF3B1F20CD7463001EBF94 /* ClashX */,
|
||||||
495A44BD20D2660A00888A0A /* ClashXLaunchHelper */,
|
495A44BD20D2660A00888A0A /* ClashXLaunchHelper */,
|
||||||
|
F9A7C06A2306E874007163C7 /* ProxyConfigHelper */,
|
||||||
49CF3B1E20CD7463001EBF94 /* Products */,
|
49CF3B1E20CD7463001EBF94 /* Products */,
|
||||||
76229F122B00E935D126742A /* Pods */,
|
76229F122B00E935D126742A /* Pods */,
|
||||||
CF1AC9FACC36FCE7663C5583 /* Frameworks */,
|
CF1AC9FACC36FCE7663C5583 /* Frameworks */,
|
||||||
|
@ -359,6 +381,7 @@
|
||||||
children = (
|
children = (
|
||||||
49CF3B1D20CD7463001EBF94 /* ClashX.app */,
|
49CF3B1D20CD7463001EBF94 /* ClashX.app */,
|
||||||
495A44BC20D2660A00888A0A /* ClashXLaunchHelper.app */,
|
495A44BC20D2660A00888A0A /* ClashXLaunchHelper.app */,
|
||||||
|
F9A7C0692306E874007163C7 /* com.west2online.ClashX.ProxyConfigHelper */,
|
||||||
);
|
);
|
||||||
name = Products;
|
name = Products;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
|
@ -376,7 +399,6 @@
|
||||||
4989F98520D0AA300001E564 /* ViewControllers */,
|
4989F98520D0AA300001E564 /* ViewControllers */,
|
||||||
49761DA521C9490400AE13EF /* Resources */,
|
49761DA521C9490400AE13EF /* Resources */,
|
||||||
49CF3B3A20CD783A001EBF94 /* Support Files */,
|
49CF3B3A20CD783A001EBF94 /* Support Files */,
|
||||||
49AF90FE225B2A95004CC077 /* ProxyConfig.xcodeproj */,
|
|
||||||
49CF3B2020CD7463001EBF94 /* AppDelegate.swift */,
|
49CF3B2020CD7463001EBF94 /* AppDelegate.swift */,
|
||||||
49CF3B2420CD7465001EBF94 /* Assets.xcassets */,
|
49CF3B2420CD7465001EBF94 /* Assets.xcassets */,
|
||||||
497F0DF220DE2FE50077AD41 /* Icon.icns */,
|
497F0DF220DE2FE50077AD41 /* Icon.icns */,
|
||||||
|
@ -401,15 +423,6 @@
|
||||||
path = "Support Files";
|
path = "Support Files";
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
49CF3B5D20CE80D2001EBF94 /* script */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
49CF3B5E20CE80D2001EBF94 /* install_proxy_helper.sh */,
|
|
||||||
49CF3B6220CED934001EBF94 /* check_proxy_helper.sh */,
|
|
||||||
);
|
|
||||||
path = script;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
76229F122B00E935D126742A /* Pods */ = {
|
76229F122B00E935D126742A /* Pods */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
@ -427,6 +440,21 @@
|
||||||
name = Frameworks;
|
name = Frameworks;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
};
|
};
|
||||||
|
F9A7C06A2306E874007163C7 /* ProxyConfigHelper */ = {
|
||||||
|
isa = PBXGroup;
|
||||||
|
children = (
|
||||||
|
F9A7C06B2306E874007163C7 /* main.m */,
|
||||||
|
F935B2F12307C802009E4D33 /* Helper-Launchd.plist */,
|
||||||
|
F935B2EA2307B6BA009E4D33 /* Helper-Info.plist */,
|
||||||
|
F935B2F22307CD32009E4D33 /* ProxyConfigHelper.h */,
|
||||||
|
F935B2F32307CD32009E4D33 /* ProxyConfigHelper.m */,
|
||||||
|
F935B2F52307D00D009E4D33 /* ProxyConfigRemoteProcessProtocol.h */,
|
||||||
|
F935B2F823083EE6009E4D33 /* ProxySettingTool.h */,
|
||||||
|
F935B2F923083EE6009E4D33 /* ProxySettingTool.m */,
|
||||||
|
);
|
||||||
|
path = ProxyConfigHelper;
|
||||||
|
sourceTree = "<group>";
|
||||||
|
};
|
||||||
/* End PBXGroup section */
|
/* End PBXGroup section */
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
/* Begin PBXNativeTarget section */
|
||||||
|
@ -459,11 +487,12 @@
|
||||||
A741C26F5755233F0D7CEC6F /* [CP] Embed Pods Frameworks */,
|
A741C26F5755233F0D7CEC6F /* [CP] Embed Pods Frameworks */,
|
||||||
663E4677213FCDC4006F11BB /* Copy Files */,
|
663E4677213FCDC4006F11BB /* Copy Files */,
|
||||||
49EC8D1822F13F100079B8F4 /* CopyFiles */,
|
49EC8D1822F13F100079B8F4 /* CopyFiles */,
|
||||||
4929F1E621BC170D0059D8B4 /* ShellScript */,
|
4929F1E621BC170D0059D8B4 /* Run Script - Fabric */,
|
||||||
);
|
);
|
||||||
buildRules = (
|
buildRules = (
|
||||||
);
|
);
|
||||||
dependencies = (
|
dependencies = (
|
||||||
|
F935B2EC2307B7CD009E4D33 /* PBXTargetDependency */,
|
||||||
495A44CF20D2671F00888A0A /* PBXTargetDependency */,
|
495A44CF20D2671F00888A0A /* PBXTargetDependency */,
|
||||||
);
|
);
|
||||||
name = ClashX;
|
name = ClashX;
|
||||||
|
@ -471,13 +500,30 @@
|
||||||
productReference = 49CF3B1D20CD7463001EBF94 /* ClashX.app */;
|
productReference = 49CF3B1D20CD7463001EBF94 /* ClashX.app */;
|
||||||
productType = "com.apple.product-type.application";
|
productType = "com.apple.product-type.application";
|
||||||
};
|
};
|
||||||
|
F9A7C0682306E874007163C7 /* com.west2online.ClashX.ProxyConfigHelper */ = {
|
||||||
|
isa = PBXNativeTarget;
|
||||||
|
buildConfigurationList = F9A7C06D2306E874007163C7 /* Build configuration list for PBXNativeTarget "com.west2online.ClashX.ProxyConfigHelper" */;
|
||||||
|
buildPhases = (
|
||||||
|
F9A7C0652306E874007163C7 /* Sources */,
|
||||||
|
F9A7C0662306E874007163C7 /* Frameworks */,
|
||||||
|
F9A7C0672306E874007163C7 /* Copy Files */,
|
||||||
|
);
|
||||||
|
buildRules = (
|
||||||
|
);
|
||||||
|
dependencies = (
|
||||||
|
);
|
||||||
|
name = com.west2online.ClashX.ProxyConfigHelper;
|
||||||
|
productName = ProxyConfigHelper;
|
||||||
|
productReference = F9A7C0692306E874007163C7 /* com.west2online.ClashX.ProxyConfigHelper */;
|
||||||
|
productType = "com.apple.product-type.tool";
|
||||||
|
};
|
||||||
/* End PBXNativeTarget section */
|
/* End PBXNativeTarget section */
|
||||||
|
|
||||||
/* Begin PBXProject section */
|
/* Begin PBXProject section */
|
||||||
49CF3B1520CD7463001EBF94 /* Project object */ = {
|
49CF3B1520CD7463001EBF94 /* Project object */ = {
|
||||||
isa = PBXProject;
|
isa = PBXProject;
|
||||||
attributes = {
|
attributes = {
|
||||||
LastSwiftUpdateCheck = 0940;
|
LastSwiftUpdateCheck = 1030;
|
||||||
LastUpgradeCheck = 1020;
|
LastUpgradeCheck = 1020;
|
||||||
ORGANIZATIONNAME = west2online;
|
ORGANIZATIONNAME = west2online;
|
||||||
TargetAttributes = {
|
TargetAttributes = {
|
||||||
|
@ -502,6 +548,9 @@
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
F9A7C0682306E874007163C7 = {
|
||||||
|
CreatedOnToolsVersion = 10.3;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
buildConfigurationList = 49CF3B1820CD7463001EBF94 /* Build configuration list for PBXProject "ClashX" */;
|
buildConfigurationList = 49CF3B1820CD7463001EBF94 /* Build configuration list for PBXProject "ClashX" */;
|
||||||
|
@ -512,40 +561,25 @@
|
||||||
en,
|
en,
|
||||||
Base,
|
Base,
|
||||||
"zh-Hans",
|
"zh-Hans",
|
||||||
global,
|
|
||||||
);
|
);
|
||||||
mainGroup = 49CF3B1420CD7463001EBF94;
|
mainGroup = 49CF3B1420CD7463001EBF94;
|
||||||
productRefGroup = 49CF3B1E20CD7463001EBF94 /* Products */;
|
productRefGroup = 49CF3B1E20CD7463001EBF94 /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
projectReferences = (
|
|
||||||
{
|
|
||||||
ProductGroup = 49AF90FF225B2A95004CC077 /* Products */;
|
|
||||||
ProjectRef = 49AF90FE225B2A95004CC077 /* ProxyConfig.xcodeproj */;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
projectRoot = "";
|
projectRoot = "";
|
||||||
targets = (
|
targets = (
|
||||||
49CF3B1C20CD7463001EBF94 /* ClashX */,
|
49CF3B1C20CD7463001EBF94 /* ClashX */,
|
||||||
495A44BB20D2660A00888A0A /* ClashXLaunchHelper */,
|
495A44BB20D2660A00888A0A /* ClashXLaunchHelper */,
|
||||||
|
F9A7C0682306E874007163C7 /* com.west2online.ClashX.ProxyConfigHelper */,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
/* End PBXProject section */
|
/* End PBXProject section */
|
||||||
|
|
||||||
/* Begin PBXReferenceProxy section */
|
|
||||||
49AF9103225B2A96004CC077 /* ProxyConfig */ = {
|
|
||||||
isa = PBXReferenceProxy;
|
|
||||||
fileType = "compiled.mach-o.executable";
|
|
||||||
path = ProxyConfig;
|
|
||||||
remoteRef = 49AF9102225B2A96004CC077 /* PBXContainerItemProxy */;
|
|
||||||
sourceTree = BUILT_PRODUCTS_DIR;
|
|
||||||
};
|
|
||||||
/* End PBXReferenceProxy section */
|
|
||||||
|
|
||||||
/* Begin PBXResourcesBuildPhase section */
|
/* Begin PBXResourcesBuildPhase section */
|
||||||
495A44BA20D2660A00888A0A /* Resources */ = {
|
495A44BA20D2660A00888A0A /* Resources */ = {
|
||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
F94245092306DABA0005B196 /* Main.storyboard in Resources */,
|
||||||
49E07A8C20D501A000A088A3 /* Main.storyboard in Resources */,
|
49E07A8C20D501A000A088A3 /* Main.storyboard in Resources */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
@ -559,12 +593,10 @@
|
||||||
4981C88B216BAE4A008CC14A /* Localizable.strings in Resources */,
|
4981C88B216BAE4A008CC14A /* Localizable.strings in Resources */,
|
||||||
495340B020DE5F7200B0D3FF /* StatusItemView.xib in Resources */,
|
495340B020DE5F7200B0D3FF /* StatusItemView.xib in Resources */,
|
||||||
49CF3B2820CD7465001EBF94 /* Main.storyboard in Resources */,
|
49CF3B2820CD7465001EBF94 /* Main.storyboard in Resources */,
|
||||||
49CF3B5F20CE80D2001EBF94 /* install_proxy_helper.sh in Resources */,
|
|
||||||
499A485A22ED781100F6C675 /* RemoteConfigAddView.xib in Resources */,
|
499A485A22ED781100F6C675 /* RemoteConfigAddView.xib in Resources */,
|
||||||
4989F98420D02D200001E564 /* Country.mmdb in Resources */,
|
4989F98420D02D200001E564 /* Country.mmdb in Resources */,
|
||||||
497F0DF320DE2FE50077AD41 /* Icon.icns in Resources */,
|
497F0DF320DE2FE50077AD41 /* Icon.icns in Resources */,
|
||||||
4989F98E20D0AE990001E564 /* sampleConfig.yaml in Resources */,
|
4989F98E20D0AE990001E564 /* sampleConfig.yaml in Resources */,
|
||||||
49CF3B6320CED9CF001EBF94 /* check_proxy_helper.sh in Resources */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
@ -589,7 +621,7 @@
|
||||||
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
|
||||||
showEnvVarsInLog = 0;
|
showEnvVarsInLog = 0;
|
||||||
};
|
};
|
||||||
4929F1E621BC170D0059D8B4 /* ShellScript */ = {
|
4929F1E621BC170D0059D8B4 /* Run Script - Fabric */ = {
|
||||||
isa = PBXShellScriptBuildPhase;
|
isa = PBXShellScriptBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
@ -597,6 +629,7 @@
|
||||||
inputPaths = (
|
inputPaths = (
|
||||||
"$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)",
|
"$(BUILT_PRODUCTS_DIR)/$(INFOPLIST_PATH)",
|
||||||
);
|
);
|
||||||
|
name = "Run Script - Fabric";
|
||||||
outputPaths = (
|
outputPaths = (
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
@ -636,12 +669,13 @@
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
499A485522ED707300F6C675 /* RemoteConfigViewController.swift in Sources */,
|
499A485522ED707300F6C675 /* RemoteConfigViewController.swift in Sources */,
|
||||||
49CF3B5C20CE8068001EBF94 /* ProxyConfigHelperManager.swift in Sources */,
|
49CF3B5C20CE8068001EBF94 /* ClashResourceManager.swift in Sources */,
|
||||||
4952C3D02117027C004A4FA8 /* ConfigFileManager.swift in Sources */,
|
4952C3D02117027C004A4FA8 /* ConfigFileManager.swift in Sources */,
|
||||||
4997732520D251A60009B136 /* SWBApplication.m in Sources */,
|
4997732520D251A60009B136 /* SWBApplication.m in Sources */,
|
||||||
49BC061C212931F4005A0FE7 /* AboutViewController.swift in Sources */,
|
49BC061C212931F4005A0FE7 /* AboutViewController.swift in Sources */,
|
||||||
4949D154213242F600EF85E6 /* Paths.swift in Sources */,
|
4949D154213242F600EF85E6 /* Paths.swift in Sources */,
|
||||||
495340B320DE68C300B0D3FF /* StatusItemView.swift in Sources */,
|
495340B320DE68C300B0D3FF /* StatusItemView.swift in Sources */,
|
||||||
|
F935B2FC23085515009E4D33 /* SystemProxyManager.swift in Sources */,
|
||||||
495A44D320D267D000888A0A /* LaunchAtLogin.swift in Sources */,
|
495A44D320D267D000888A0A /* LaunchAtLogin.swift in Sources */,
|
||||||
493AEAE3221AE3420016FE98 /* AppVersionUtil.swift in Sources */,
|
493AEAE3221AE3420016FE98 /* AppVersionUtil.swift in Sources */,
|
||||||
49CF3B2120CD7463001EBF94 /* AppDelegate.swift in Sources */,
|
49CF3B2120CD7463001EBF94 /* AppDelegate.swift in Sources */,
|
||||||
|
@ -670,6 +704,16 @@
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
|
F9A7C0652306E874007163C7 /* Sources */ = {
|
||||||
|
isa = PBXSourcesBuildPhase;
|
||||||
|
buildActionMask = 2147483647;
|
||||||
|
files = (
|
||||||
|
F935B2F42307CD32009E4D33 /* ProxyConfigHelper.m in Sources */,
|
||||||
|
F9A7C06C2306E874007163C7 /* main.m in Sources */,
|
||||||
|
F935B2FA23083EE6009E4D33 /* ProxySettingTool.m in Sources */,
|
||||||
|
);
|
||||||
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
|
};
|
||||||
/* End PBXSourcesBuildPhase section */
|
/* End PBXSourcesBuildPhase section */
|
||||||
|
|
||||||
/* Begin PBXTargetDependency section */
|
/* Begin PBXTargetDependency section */
|
||||||
|
@ -678,6 +722,11 @@
|
||||||
target = 495A44BB20D2660A00888A0A /* ClashXLaunchHelper */;
|
target = 495A44BB20D2660A00888A0A /* ClashXLaunchHelper */;
|
||||||
targetProxy = 495A44CE20D2671F00888A0A /* PBXContainerItemProxy */;
|
targetProxy = 495A44CE20D2671F00888A0A /* PBXContainerItemProxy */;
|
||||||
};
|
};
|
||||||
|
F935B2EC2307B7CD009E4D33 /* PBXTargetDependency */ = {
|
||||||
|
isa = PBXTargetDependency;
|
||||||
|
target = F9A7C0682306E874007163C7 /* com.west2online.ClashX.ProxyConfigHelper */;
|
||||||
|
targetProxy = F935B2EB2307B7CD009E4D33 /* PBXContainerItemProxy */;
|
||||||
|
};
|
||||||
/* End PBXTargetDependency section */
|
/* End PBXTargetDependency section */
|
||||||
|
|
||||||
/* Begin PBXVariantGroup section */
|
/* Begin PBXVariantGroup section */
|
||||||
|
@ -702,7 +751,6 @@
|
||||||
49E07A8920D501A000A088A3 /* Main.storyboard */ = {
|
49E07A8920D501A000A088A3 /* Main.storyboard */ = {
|
||||||
isa = PBXVariantGroup;
|
isa = PBXVariantGroup;
|
||||||
children = (
|
children = (
|
||||||
49E07A8A20D501A000A088A3 /* Base */,
|
|
||||||
4981C888216BAB8A008CC14A /* zh-Hans */,
|
4981C888216BAB8A008CC14A /* zh-Hans */,
|
||||||
);
|
);
|
||||||
name = Main.storyboard;
|
name = Main.storyboard;
|
||||||
|
@ -716,6 +764,7 @@
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = ClashXLaunchHelper/ClashXLaunchHelper.entitlements;
|
CODE_SIGN_ENTITLEMENTS = ClashXLaunchHelper/ClashXLaunchHelper.entitlements;
|
||||||
|
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
DEVELOPMENT_TEAM = MEWHFZ92DY;
|
DEVELOPMENT_TEAM = MEWHFZ92DY;
|
||||||
|
@ -879,7 +928,7 @@
|
||||||
baseConfigurationReference = 5217C006C5A22A1CEA24BFC1 /* Pods-ClashX.debug.xcconfig */;
|
baseConfigurationReference = 5217C006C5A22A1CEA24BFC1 /* Pods-ClashX.debug.xcconfig */;
|
||||||
buildSettings = {
|
buildSettings = {
|
||||||
CLANG_ENABLE_MODULES = YES;
|
CLANG_ENABLE_MODULES = YES;
|
||||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||||
CODE_SIGN_STYLE = Manual;
|
CODE_SIGN_STYLE = Manual;
|
||||||
COMBINE_HIDPI_IMAGES = YES;
|
COMBINE_HIDPI_IMAGES = YES;
|
||||||
DEVELOPMENT_TEAM = MEWHFZ92DY;
|
DEVELOPMENT_TEAM = MEWHFZ92DY;
|
||||||
|
@ -950,6 +999,57 @@
|
||||||
};
|
};
|
||||||
name = Release;
|
name = Release;
|
||||||
};
|
};
|
||||||
|
F9A7C06E2306E874007163C7 /* Debug */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||||
|
CODE_SIGN_STYLE = Manual;
|
||||||
|
DEVELOPMENT_TEAM = MEWHFZ92DY;
|
||||||
|
INFOPLIST_FILE = "$(SRCROOT)/ProxyConfigHelper/Helper-Info.plist";
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||||
|
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
OTHER_LDFLAGS = (
|
||||||
|
"-sectcreate",
|
||||||
|
__TEXT,
|
||||||
|
__info_plist,
|
||||||
|
"\"$(SRCROOT)/ProxyConfigHelper/Helper-Info.plist\"",
|
||||||
|
"-sectcreate",
|
||||||
|
__TEXT,
|
||||||
|
__launchd_plist,
|
||||||
|
"\"$(SRCROOT)/ProxyConfigHelper/Helper-Launchd.plist\"",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.west2online.ClashX.ProxyConfigHelper;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
};
|
||||||
|
name = Debug;
|
||||||
|
};
|
||||||
|
F9A7C06F2306E874007163C7 /* Release */ = {
|
||||||
|
isa = XCBuildConfiguration;
|
||||||
|
buildSettings = {
|
||||||
|
CODE_SIGN_IDENTITY = "Developer ID Application";
|
||||||
|
CODE_SIGN_STYLE = Manual;
|
||||||
|
DEVELOPMENT_TEAM = MEWHFZ92DY;
|
||||||
|
INFOPLIST_FILE = "$(SRCROOT)/ProxyConfigHelper/Helper-Info.plist";
|
||||||
|
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
||||||
|
MTL_FAST_MATH = YES;
|
||||||
|
OTHER_LDFLAGS = (
|
||||||
|
"-sectcreate",
|
||||||
|
__TEXT,
|
||||||
|
__info_plist,
|
||||||
|
"\"$(SRCROOT)/ProxyConfigHelper/Helper-Info.plist\"",
|
||||||
|
"-sectcreate",
|
||||||
|
__TEXT,
|
||||||
|
__launchd_plist,
|
||||||
|
"\"$(SRCROOT)/ProxyConfigHelper/Helper-Launchd.plist\"",
|
||||||
|
);
|
||||||
|
PRODUCT_BUNDLE_IDENTIFIER = com.west2online.ClashX.ProxyConfigHelper;
|
||||||
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
|
};
|
||||||
|
name = Release;
|
||||||
|
};
|
||||||
/* End XCBuildConfiguration section */
|
/* End XCBuildConfiguration section */
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
/* Begin XCConfigurationList section */
|
||||||
|
@ -980,6 +1080,15 @@
|
||||||
defaultConfigurationIsVisible = 0;
|
defaultConfigurationIsVisible = 0;
|
||||||
defaultConfigurationName = Release;
|
defaultConfigurationName = Release;
|
||||||
};
|
};
|
||||||
|
F9A7C06D2306E874007163C7 /* Build configuration list for PBXNativeTarget "com.west2online.ClashX.ProxyConfigHelper" */ = {
|
||||||
|
isa = XCConfigurationList;
|
||||||
|
buildConfigurations = (
|
||||||
|
F9A7C06E2306E874007163C7 /* Debug */,
|
||||||
|
F9A7C06F2306E874007163C7 /* Release */,
|
||||||
|
);
|
||||||
|
defaultConfigurationIsVisible = 0;
|
||||||
|
defaultConfigurationName = Release;
|
||||||
|
};
|
||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
};
|
};
|
||||||
rootObject = 49CF3B1520CD7463001EBF94 /* Project object */;
|
rootObject = 49CF3B1520CD7463001EBF94 /* Project object */;
|
||||||
|
|
|
@ -53,6 +53,8 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
func applicationDidFinishLaunching(_ notification: Notification) {
|
func applicationDidFinishLaunching(_ notification: Notification) {
|
||||||
signal(SIGPIPE, SIG_IGN)
|
signal(SIGPIPE, SIG_IGN)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// setup menu item first
|
// setup menu item first
|
||||||
statusItem = NSStatusBar.system.statusItem(withLength:statusItemLengthWithSpeed)
|
statusItem = NSStatusBar.system.statusItem(withLength:statusItemLengthWithSpeed)
|
||||||
statusItem.menu = statusMenu
|
statusItem.menu = statusMenu
|
||||||
|
@ -60,13 +62,13 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
statusItemView = StatusItemView.create(statusItem: statusItem)
|
statusItemView = StatusItemView.create(statusItem: statusItem)
|
||||||
statusItemView.frame = CGRect(x: 0, y: 0, width: statusItemLengthWithSpeed, height: 22)
|
statusItemView.frame = CGRect(x: 0, y: 0, width: statusItemLengthWithSpeed, height: 22)
|
||||||
statusMenu.delegate = self
|
statusMenu.delegate = self
|
||||||
|
|
||||||
// crash recorder
|
// crash recorder
|
||||||
failLaunchProtect()
|
failLaunchProtect()
|
||||||
registCrashLogger()
|
registCrashLogger()
|
||||||
|
|
||||||
// install proxy helper
|
// install proxy helper
|
||||||
_ = ProxyConfigHelperManager.install()
|
_ = ClashResourceManager.check()
|
||||||
|
SystemProxyManager.shared.checkInstall()
|
||||||
ConfigFileManager.copySampleConfigIfNeed()
|
ConfigFileManager.copySampleConfigIfNeed()
|
||||||
ConfigManager.shared.refreshApiInfo()
|
ConfigManager.shared.refreshApiInfo()
|
||||||
|
|
||||||
|
@ -92,7 +94,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
|
|
||||||
func applicationWillTerminate(_ aNotification: Notification) {
|
func applicationWillTerminate(_ aNotification: Notification) {
|
||||||
if ConfigManager.shared.proxyPortAutoSet {
|
if ConfigManager.shared.proxyPortAutoSet {
|
||||||
_ = ProxyConfigHelperManager.setUpSystemProxy(port: nil,socksPort: nil)
|
SystemProxyManager.shared.disableProxy()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,7 +183,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
self.updateProxyList()
|
self.updateProxyList()
|
||||||
|
|
||||||
if (old?.port != config.port && ConfigManager.shared.proxyPortAutoSet) {
|
if (old?.port != config.port && ConfigManager.shared.proxyPortAutoSet) {
|
||||||
_ = ProxyConfigHelperManager.setUpSystemProxy(port: config.port,socksPort: config.socketPort)
|
SystemProxyManager.shared.enableProxy(port: config.port, socksPort: config.socketPort)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.httpPortMenuItem.title = "Http Port:\(config.port)"
|
self.httpPortMenuItem.title = "Http Port:\(config.port)"
|
||||||
|
@ -361,9 +363,9 @@ extension AppDelegate {
|
||||||
if ConfigManager.shared.proxyPortAutoSet {
|
if ConfigManager.shared.proxyPortAutoSet {
|
||||||
let port = ConfigManager.shared.currentConfig?.port ?? 0
|
let port = ConfigManager.shared.currentConfig?.port ?? 0
|
||||||
let socketPort = ConfigManager.shared.currentConfig?.socketPort ?? 0
|
let socketPort = ConfigManager.shared.currentConfig?.socketPort ?? 0
|
||||||
_ = ProxyConfigHelperManager.setUpSystemProxy(port: port,socksPort:socketPort)
|
SystemProxyManager.shared.enableProxy(port: port, socksPort: socketPort)
|
||||||
} else {
|
} else {
|
||||||
_ = ProxyConfigHelperManager.setUpSystemProxy(port: nil,socksPort: nil)
|
SystemProxyManager.shared.disableProxy()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,3 +2,4 @@
|
||||||
// Use this file to import your target's public headers that you would like to expose to Swift.
|
// Use this file to import your target's public headers that you would like to expose to Swift.
|
||||||
//
|
//
|
||||||
#import "ClashX.h"
|
#import "ClashX.h"
|
||||||
|
#import "ProxyConfigRemoteProcessProtocol.h"
|
||||||
|
|
|
@ -0,0 +1,95 @@
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import AppKit
|
||||||
|
|
||||||
|
class ClashResourceManager {
|
||||||
|
static let kProxyConfigFolder = (NSHomeDirectory() as NSString).appendingPathComponent("/.config/clash")
|
||||||
|
|
||||||
|
static func check() -> Bool {
|
||||||
|
checkConfigDir()
|
||||||
|
checkMMDB()
|
||||||
|
upgardeYmlExtensionName()
|
||||||
|
checkAndRemoveOldErrorConfig()
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
static func checkConfigDir() {
|
||||||
|
var isDir : ObjCBool = true
|
||||||
|
|
||||||
|
if !FileManager.default.fileExists(atPath: kProxyConfigFolder, isDirectory:&isDir) {
|
||||||
|
do {
|
||||||
|
try FileManager.default.createDirectory(atPath: kProxyConfigFolder, withIntermediateDirectories: true, attributes: nil)
|
||||||
|
} catch {
|
||||||
|
showCreateConfigDirFailAlert()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func checkMMDB() {
|
||||||
|
let fileManage = FileManager.default
|
||||||
|
let destMMDBPath = "\(kProxyConfigFolder)/Country.mmdb"
|
||||||
|
|
||||||
|
// Remove old mmdb file after version update.
|
||||||
|
if fileManage.fileExists(atPath: destMMDBPath) {
|
||||||
|
if AppVersionUtil.hasVersionChanged || AppVersionUtil.isFirstLaunch {
|
||||||
|
try? fileManage.removeItem(atPath: destMMDBPath)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if !fileManage.fileExists(atPath: destMMDBPath) {
|
||||||
|
if let mmdbPath = Bundle.main.path(forResource: "Country", ofType: "mmdb") {
|
||||||
|
try? fileManage.copyItem(at: URL(fileURLWithPath: mmdbPath), to: URL(fileURLWithPath: destMMDBPath))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func checkAndRemoveOldErrorConfig() {
|
||||||
|
if FileManager.default.fileExists(atPath: kDefaultConfigFilePath) {
|
||||||
|
do {
|
||||||
|
let defaultConfigData = try Data(contentsOf: URL(fileURLWithPath: kDefaultConfigFilePath))
|
||||||
|
var checkSum: UInt8 = 0
|
||||||
|
for byte in defaultConfigData {
|
||||||
|
checkSum &+= byte
|
||||||
|
}
|
||||||
|
|
||||||
|
if checkSum == 101 {
|
||||||
|
// old error config
|
||||||
|
Logger.log(msg: "removing old config.yaml")
|
||||||
|
try FileManager.default.removeItem(atPath: kDefaultConfigFilePath)
|
||||||
|
}
|
||||||
|
} catch let err {
|
||||||
|
Logger.log(msg: "removing old config.yaml fail: \(err.localizedDescription)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static func upgardeYmlExtensionName() {
|
||||||
|
do {
|
||||||
|
let fileURLs = try FileManager.default.contentsOfDirectory(at: URL(fileURLWithPath: kConfigFolderPath, isDirectory: true), includingPropertiesForKeys: nil, options: [.skipsSubdirectoryDescendants])
|
||||||
|
|
||||||
|
for upgradeUrl in fileURLs.filter({$0.pathExtension == "yml" }) {
|
||||||
|
let dest = upgradeUrl.deletingPathExtension().appendingPathExtension("yaml")
|
||||||
|
try FileManager.default.moveItem(at: upgradeUrl, to: dest)
|
||||||
|
}
|
||||||
|
|
||||||
|
} catch let err {
|
||||||
|
Logger.log(msg: err.localizedDescription)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static func showCreateConfigDirFailAlert() {
|
||||||
|
let alert = NSAlert()
|
||||||
|
alert.messageText = NSLocalizedString("ClashX fail to create ~/.config/clash folder. Please check privileges or manually create folder and restart ClashX.", comment: "")
|
||||||
|
alert.alertStyle = .warning
|
||||||
|
alert.addButton(withTitle: NSLocalizedString("Quit", comment: ""))
|
||||||
|
alert.runModal()
|
||||||
|
NSApplication.shared.terminate(nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
|
@ -1,175 +0,0 @@
|
||||||
|
|
||||||
import Foundation
|
|
||||||
import AppKit
|
|
||||||
|
|
||||||
class ProxyConfigHelperManager {
|
|
||||||
static let kProxyConfigFolder = (NSHomeDirectory() as NSString).appendingPathComponent("/.config/clash")
|
|
||||||
static let kVersion = "0.1.3"
|
|
||||||
|
|
||||||
|
|
||||||
static func vaildHelper() -> Bool {
|
|
||||||
let scriptPath = "\(Bundle.main.resourcePath!)/check_proxy_helper.sh"
|
|
||||||
let appleScriptStr = "do shell script \"bash \\\"\(scriptPath)\\\" \(kProxyConfigFolder) \(kVersion) \" "
|
|
||||||
let appleScript = NSAppleScript(source: appleScriptStr)
|
|
||||||
var dict: NSDictionary?
|
|
||||||
if let res = appleScript?.executeAndReturnError(&dict) {
|
|
||||||
if (res.stringValue?.contains("success")) ?? false {
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
Logger.log(msg: "\(String(describing: dict))",level: .error)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static func install() -> Bool {
|
|
||||||
checkConfigDir()
|
|
||||||
checkMMDB()
|
|
||||||
upgardeYmlExtensionName()
|
|
||||||
checkAndRemoveOldErrorConfig()
|
|
||||||
|
|
||||||
let proxyHelperPath = Bundle.main.path(forResource: "ProxyConfig", ofType: nil)
|
|
||||||
let targetPath = "\(kProxyConfigFolder)/ProxyConfig"
|
|
||||||
|
|
||||||
|
|
||||||
if !vaildHelper() {
|
|
||||||
if (!showInstallHelperAlert()) {
|
|
||||||
NSApplication.shared.terminate(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
if (FileManager.default.fileExists(atPath: targetPath)) {
|
|
||||||
try? FileManager.default.removeItem(atPath: targetPath)
|
|
||||||
}
|
|
||||||
try? FileManager.default.copyItem(at: URL(fileURLWithPath: proxyHelperPath!), to: URL(fileURLWithPath: targetPath))
|
|
||||||
|
|
||||||
let scriptPath = "\(Bundle.main.resourcePath!)/install_proxy_helper.sh"
|
|
||||||
let appleScriptStr = "do shell script \"bash \(scriptPath) \(kProxyConfigFolder) \" with administrator privileges"
|
|
||||||
let appleScript = NSAppleScript(source: appleScriptStr)
|
|
||||||
|
|
||||||
var dict: NSDictionary?
|
|
||||||
if let _ = appleScript?.executeAndReturnError(&dict) {
|
|
||||||
return true
|
|
||||||
} else {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
static func checkConfigDir() {
|
|
||||||
var isDir : ObjCBool = true
|
|
||||||
|
|
||||||
if !FileManager.default.fileExists(atPath: kProxyConfigFolder, isDirectory:&isDir) {
|
|
||||||
do {
|
|
||||||
try FileManager.default.createDirectory(atPath: kProxyConfigFolder, withIntermediateDirectories: true, attributes: nil)
|
|
||||||
} catch {
|
|
||||||
showCreateConfigDirFailAlert()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static func checkMMDB() {
|
|
||||||
let fileManage = FileManager.default
|
|
||||||
let destMMDBPath = "\(kProxyConfigFolder)/Country.mmdb"
|
|
||||||
|
|
||||||
// Remove old mmdb file after version update.
|
|
||||||
if fileManage.fileExists(atPath: destMMDBPath) {
|
|
||||||
if AppVersionUtil.hasVersionChanged || AppVersionUtil.isFirstLaunch {
|
|
||||||
try? fileManage.removeItem(atPath: destMMDBPath)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if !fileManage.fileExists(atPath: destMMDBPath) {
|
|
||||||
if let mmdbPath = Bundle.main.path(forResource: "Country", ofType: "mmdb") {
|
|
||||||
try? fileManage.copyItem(at: URL(fileURLWithPath: mmdbPath), to: URL(fileURLWithPath: destMMDBPath))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static func checkAndRemoveOldErrorConfig() {
|
|
||||||
if FileManager.default.fileExists(atPath: kDefaultConfigFilePath) {
|
|
||||||
do {
|
|
||||||
let defaultConfigData = try Data(contentsOf: URL(fileURLWithPath: kDefaultConfigFilePath))
|
|
||||||
var checkSum: UInt8 = 0
|
|
||||||
for byte in defaultConfigData {
|
|
||||||
checkSum &+= byte
|
|
||||||
}
|
|
||||||
|
|
||||||
if checkSum == 101 {
|
|
||||||
// old error config
|
|
||||||
Logger.log(msg: "removing old config.yaml")
|
|
||||||
try FileManager.default.removeItem(atPath: kDefaultConfigFilePath)
|
|
||||||
}
|
|
||||||
} catch let err {
|
|
||||||
Logger.log(msg: "removing old config.yaml fail: \(err.localizedDescription)")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static func upgardeYmlExtensionName() {
|
|
||||||
do {
|
|
||||||
let fileURLs = try FileManager.default.contentsOfDirectory(at: URL(fileURLWithPath: kConfigFolderPath, isDirectory: true), includingPropertiesForKeys: nil, options: [.skipsSubdirectoryDescendants])
|
|
||||||
|
|
||||||
for upgradeUrl in fileURLs.filter({$0.pathExtension == "yml" }) {
|
|
||||||
let dest = upgradeUrl.deletingPathExtension().appendingPathExtension("yaml")
|
|
||||||
try FileManager.default.moveItem(at: upgradeUrl, to: dest)
|
|
||||||
}
|
|
||||||
|
|
||||||
} catch let err {
|
|
||||||
Logger.log(msg: err.localizedDescription)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
static func setUpSystemProxy(port: Int?,socksPort: Int?) -> Bool {
|
|
||||||
let task = Process()
|
|
||||||
task.launchPath = "\(kProxyConfigFolder)/ProxyConfig"
|
|
||||||
let hookTask:String?
|
|
||||||
if let port = port,let socksPort = socksPort {
|
|
||||||
hookTask = UserDefaults.standard.string(forKey: "kProxyEnableHook")
|
|
||||||
task.arguments = [String(port),String(socksPort), "enable"]
|
|
||||||
} else {
|
|
||||||
hookTask = UserDefaults.standard.string(forKey: "kProxyDisableHook")
|
|
||||||
task.arguments = ["0", "0", "disable"]
|
|
||||||
}
|
|
||||||
|
|
||||||
task.launch()
|
|
||||||
|
|
||||||
task.waitUntilExit()
|
|
||||||
|
|
||||||
if task.terminationStatus != 0 {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
DispatchQueue.global().async {
|
|
||||||
if let command = hookTask {
|
|
||||||
let appleScriptStr = "do shell script \"\(command)\""
|
|
||||||
let appleScript = NSAppleScript(source: appleScriptStr)
|
|
||||||
_ = appleScript?.executeAndReturnError(nil)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
static func showInstallHelperAlert() -> Bool{
|
|
||||||
let alert = NSAlert()
|
|
||||||
alert.messageText = NSLocalizedString("ClashX needs to install a small tool to ~/.config/clash with administrator privileges to set system proxy quickly.\n\nOtherwise you need to type in the administrator password every time you change system proxy through ClashX.", comment: "")
|
|
||||||
alert.alertStyle = .warning
|
|
||||||
alert.addButton(withTitle: NSLocalizedString("Install", comment: ""))
|
|
||||||
alert.addButton(withTitle: NSLocalizedString("Quit", comment: ""))
|
|
||||||
return alert.runModal() == .alertFirstButtonReturn
|
|
||||||
}
|
|
||||||
|
|
||||||
static func showCreateConfigDirFailAlert() {
|
|
||||||
let alert = NSAlert()
|
|
||||||
alert.messageText = NSLocalizedString("ClashX fail to create ~/.config/clash folder. Please check privileges or manually create folder and restart ClashX.", comment: "")
|
|
||||||
alert.alertStyle = .warning
|
|
||||||
alert.addButton(withTitle: NSLocalizedString("Quit", comment: ""))
|
|
||||||
alert.runModal()
|
|
||||||
NSApplication.shared.terminate(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
|
@ -0,0 +1,228 @@
|
||||||
|
//
|
||||||
|
// SystemProxyManager.swift
|
||||||
|
// ClashX
|
||||||
|
//
|
||||||
|
// Created by yichengchen on 2019/8/17.
|
||||||
|
// Copyright © 2019 west2online. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import ServiceManagement
|
||||||
|
import AppKit
|
||||||
|
|
||||||
|
class SystemProxyManager: NSObject {
|
||||||
|
|
||||||
|
static let shared = SystemProxyManager()
|
||||||
|
|
||||||
|
private static let machServiceName = "com.west2online.ClashX.ProxyConfigHelper"
|
||||||
|
private var authRef: AuthorizationRef?
|
||||||
|
private var connection: NSXPCConnection?
|
||||||
|
private var _helper: ProxyConfigRemoteProcessProtocol?
|
||||||
|
|
||||||
|
// MARK: - LifeCycle
|
||||||
|
|
||||||
|
override init() {
|
||||||
|
super.init()
|
||||||
|
initAuthorizationRef()
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Public
|
||||||
|
|
||||||
|
func checkInstall() {
|
||||||
|
helperStatus { [weak self] installed in
|
||||||
|
if installed {return}
|
||||||
|
if Thread.isMainThread {
|
||||||
|
self?.notifyInstall()
|
||||||
|
} else {
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
self?.notifyInstall()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func enableProxy(port: Int,socksPort: Int) {
|
||||||
|
Logger.log(msg: "enableProxy", level: .debug)
|
||||||
|
helper()?.enableProxy(withPort: Int32(port), socksPort: Int32(socksPort), authData: authData(), error: { error in
|
||||||
|
if let error = error{
|
||||||
|
Logger.log(msg: "enableProxy \(error)", level: .error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func disableProxy() {
|
||||||
|
Logger.log(msg: "disableProxy", level: .debug)
|
||||||
|
helper()?.disableProxy(withAuthData: authData(), error: { error in
|
||||||
|
if let error = error{
|
||||||
|
Logger.log(msg: "disableProxy \(error)", level: .error)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private
|
||||||
|
|
||||||
|
private func initAuthorizationRef() {
|
||||||
|
// Create an empty AuthorizationRef
|
||||||
|
let status = AuthorizationCreate(nil, nil, AuthorizationFlags(), &authRef)
|
||||||
|
if (status != OSStatus(errAuthorizationSuccess)) {
|
||||||
|
Logger.log(msg:"initAuthorizationRef AuthorizationCreate failed",level: .error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// Install new helper daemon
|
||||||
|
private func installHelperDaemon() {
|
||||||
|
|
||||||
|
// Create authorization reference for the user
|
||||||
|
var authRef: AuthorizationRef?
|
||||||
|
var authStatus = AuthorizationCreate(nil, nil, [], &authRef)
|
||||||
|
|
||||||
|
// Check if the reference is valid
|
||||||
|
guard authStatus == errAuthorizationSuccess else {
|
||||||
|
Logger.log(msg: "Authorization failed: \(authStatus)", level: .error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Ask user for the admin privileges to install the
|
||||||
|
var authItem = AuthorizationItem(name: kSMRightBlessPrivilegedHelper, valueLength: 0, value: nil, flags: 0)
|
||||||
|
var authRights = AuthorizationRights(count: 1, items: &authItem)
|
||||||
|
let flags: AuthorizationFlags = [[], .interactionAllowed, .extendRights, .preAuthorize]
|
||||||
|
authStatus = AuthorizationCreate(&authRights, nil, flags, &authRef)
|
||||||
|
defer {
|
||||||
|
if let ref = authRef {
|
||||||
|
AuthorizationFree(ref, [])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Check if the authorization went succesfully
|
||||||
|
guard authStatus == errAuthorizationSuccess else {
|
||||||
|
Logger.log(msg: "Couldn't obtain admin privileges: \(authStatus)", level: .error)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Launch the privileged helper using SMJobBless tool
|
||||||
|
var error: Unmanaged<CFError>? = nil
|
||||||
|
|
||||||
|
if(SMJobBless(kSMDomainSystemLaunchd, SystemProxyManager.machServiceName as CFString, authRef, &error) == false) {
|
||||||
|
let blessError = error!.takeRetainedValue() as Error
|
||||||
|
Logger.log(msg: "Bless Error: \(blessError)", level: .error)
|
||||||
|
} else {
|
||||||
|
Logger.log(msg: "\(SystemProxyManager.machServiceName) installed successfully", level: .info)
|
||||||
|
}
|
||||||
|
|
||||||
|
connection?.invalidate()
|
||||||
|
connection = nil
|
||||||
|
_helper = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
private func authData() -> Data? {
|
||||||
|
guard let authRef = authRef else {return nil}
|
||||||
|
var authRefExtForm = AuthorizationExternalForm()
|
||||||
|
|
||||||
|
// Make an external form of the AuthorizationRef
|
||||||
|
var status = AuthorizationMakeExternalForm(authRef, &authRefExtForm)
|
||||||
|
if (status != OSStatus(errAuthorizationSuccess)) {
|
||||||
|
Logger.log(msg: "AppviewController: AuthorizationMakeExternalForm failed", level: .error)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add all or update required authorization right definition to the authorization database
|
||||||
|
var currentRight:CFDictionary?
|
||||||
|
|
||||||
|
// Try to get the authorization right definition from the database
|
||||||
|
status = AuthorizationRightGet(AppAuthorizationRights.rightName.utf8String!, ¤tRight)
|
||||||
|
|
||||||
|
if (status == errAuthorizationDenied) {
|
||||||
|
|
||||||
|
let defaultRules = AppAuthorizationRights.rightDefaultRule
|
||||||
|
status = AuthorizationRightSet(authRef,
|
||||||
|
AppAuthorizationRights.rightName.utf8String!,
|
||||||
|
defaultRules as CFDictionary,
|
||||||
|
AppAuthorizationRights.rightDescription,
|
||||||
|
nil, "Common" as CFString)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We need to put the AuthorizationRef to a form that can be passed through inter process call
|
||||||
|
let authData = NSData(bytes: &authRefExtForm, length:kAuthorizationExternalFormLength)
|
||||||
|
return authData as Data
|
||||||
|
}
|
||||||
|
|
||||||
|
private func helperConnection() -> NSXPCConnection? {
|
||||||
|
// Check that the connection is valid before trying to do an inter process call to helper
|
||||||
|
if(connection == nil) {
|
||||||
|
connection = NSXPCConnection(machServiceName: SystemProxyManager.machServiceName, options: NSXPCConnection.Options.privileged)
|
||||||
|
connection?.remoteObjectInterface = NSXPCInterface(with: ProxyConfigRemoteProcessProtocol.self)
|
||||||
|
connection?.invalidationHandler = {
|
||||||
|
self.connection?.invalidationHandler = nil
|
||||||
|
OperationQueue.main.addOperation() {
|
||||||
|
self.connection = nil
|
||||||
|
Logger.log(msg: "XPC Connection Invalidated")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
connection?.resume()
|
||||||
|
}
|
||||||
|
return connection
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private func helper(failture: (() -> Void)? = nil) -> ProxyConfigRemoteProcessProtocol? {
|
||||||
|
if _helper == nil {
|
||||||
|
guard let newHelper = self.helperConnection()?.remoteObjectProxyWithErrorHandler({ error in
|
||||||
|
Logger.log(msg: "Helper connection was closed with error: \(error)")
|
||||||
|
failture?()
|
||||||
|
}) as? ProxyConfigRemoteProcessProtocol else { return nil }
|
||||||
|
_helper = newHelper
|
||||||
|
}
|
||||||
|
return _helper
|
||||||
|
}
|
||||||
|
|
||||||
|
private func helperStatus(completion: @escaping (_ installed: Bool) -> Void) {
|
||||||
|
let helperURL = Bundle.main.bundleURL.appendingPathComponent("Contents/Library/LaunchServices/" + SystemProxyManager.machServiceName)
|
||||||
|
var callback:((Bool)->Void)? = completion
|
||||||
|
|
||||||
|
guard
|
||||||
|
let helperBundleInfo = CFBundleCopyInfoDictionaryForURL(helperURL as CFURL) as? [String: Any],
|
||||||
|
let helperVersion = helperBundleInfo["CFBundleShortVersionString"] as? String,
|
||||||
|
let helper = self.helper() else {
|
||||||
|
callback?(false)
|
||||||
|
callback = nil
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var installed = false
|
||||||
|
let semaphore = DispatchSemaphore(value: 0)
|
||||||
|
helper.getVersion { installedHelperVersion in
|
||||||
|
installed = installedHelperVersion == helperVersion
|
||||||
|
}
|
||||||
|
_ = semaphore.wait(timeout: DispatchTime.now()+1)
|
||||||
|
callback?(installed)
|
||||||
|
callback = nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
extension SystemProxyManager {
|
||||||
|
private func notifyInstall() {
|
||||||
|
guard showInstallHelperAlert() else {exit(0)}
|
||||||
|
self.installHelperDaemon()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func showInstallHelperAlert() -> Bool{
|
||||||
|
let alert = NSAlert()
|
||||||
|
alert.messageText = NSLocalizedString("ClashX needs to install a small tool to ~/.config/clash with administrator privileges to set system proxy quickly.\n\nOtherwise you need to type in the administrator password every time you change system proxy through ClashX.", comment: "")
|
||||||
|
alert.alertStyle = .warning
|
||||||
|
alert.addButton(withTitle: NSLocalizedString("Install", comment: ""))
|
||||||
|
alert.addButton(withTitle: NSLocalizedString("Quit", comment: ""))
|
||||||
|
return alert.runModal() == .alertFirstButtonReturn
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fileprivate struct AppAuthorizationRights {
|
||||||
|
static let rightName: NSString = "com.west2online.ClashX.ProxyConfigHelper.config"
|
||||||
|
static let rightDefaultRule: Dictionary = adminRightsRule
|
||||||
|
static let rightDescription: CFString = "ProxyConfigHelper wants to configure your proxy setting'" as CFString
|
||||||
|
static var adminRightsRule: [String:Any] = ["class" : "user",
|
||||||
|
"group" : "admin",
|
||||||
|
"timeout" : 0,
|
||||||
|
"version" : 1]
|
||||||
|
}
|
|
@ -48,13 +48,12 @@ class JsBridgeUtil {
|
||||||
if let enable = anydata as? Bool {
|
if let enable = anydata as? Bool {
|
||||||
ConfigManager.shared.proxyPortAutoSet = enable
|
ConfigManager.shared.proxyPortAutoSet = enable
|
||||||
if let config = ConfigManager.shared.currentConfig {
|
if let config = ConfigManager.shared.currentConfig {
|
||||||
let success:Bool
|
|
||||||
if enable{
|
if enable{
|
||||||
success = ProxyConfigHelperManager.setUpSystemProxy(port: config.port,socksPort: config.socketPort)
|
SystemProxyManager.shared.enableProxy(port: config.port, socksPort: config.socketPort)
|
||||||
} else {
|
} else {
|
||||||
success = ProxyConfigHelperManager.setUpSystemProxy(port: nil,socksPort: nil)
|
SystemProxyManager.shared.disableProxy()
|
||||||
}
|
}
|
||||||
responseCallback?(success)
|
responseCallback?(true)
|
||||||
} else {
|
} else {
|
||||||
responseCallback?(false)
|
responseCallback?(false)
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,7 +55,8 @@
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
<dict>
|
||||||
<key>KitInfo</key>
|
<key>KitInfo</key>
|
||||||
<dict/>
|
<dict>
|
||||||
|
</dict>
|
||||||
<key>KitName</key>
|
<key>KitName</key>
|
||||||
<string>Crashlytics</string>
|
<string>Crashlytics</string>
|
||||||
</dict>
|
</dict>
|
||||||
|
@ -78,6 +79,15 @@
|
||||||
<string>Main</string>
|
<string>Main</string>
|
||||||
<key>NSPrincipalClass</key>
|
<key>NSPrincipalClass</key>
|
||||||
<string>SWBApplication</string>
|
<string>SWBApplication</string>
|
||||||
|
<key>SMAuthorizedClients</key>
|
||||||
|
<array>
|
||||||
|
<string>identifier "com.west2online.ClashX" and anchor apple generic and certificate leaf[subject.CN] = "Mac Developer: chen yicheng (96U846XGYH)" and certificate 1[field.1.2.840.113635.100.6.2.1] /* exists */</string>
|
||||||
|
</array>
|
||||||
|
<key>SMPrivilegedExecutables</key>
|
||||||
|
<dict>
|
||||||
|
<key>com.west2online.ClashX.ProxyConfigHelper</key>
|
||||||
|
<string>anchor apple generic and identifier "com.west2online.ClashX.ProxyConfigHelper" and (certificate leaf[field.1.2.840.113635.100.6.1.9] /* exists */ or certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = MEWHFZ92DY)</string>
|
||||||
|
</dict>
|
||||||
<key>SUEnableAutomaticChecks</key>
|
<key>SUEnableAutomaticChecks</key>
|
||||||
<true/>
|
<true/>
|
||||||
<key>SUFeedURL</key>
|
<key>SUFeedURL</key>
|
||||||
|
|
|
@ -1,287 +0,0 @@
|
||||||
// !$*UTF8*$!
|
|
||||||
{
|
|
||||||
archiveVersion = 1;
|
|
||||||
classes = {
|
|
||||||
};
|
|
||||||
objectVersion = 50;
|
|
||||||
objects = {
|
|
||||||
|
|
||||||
/* Begin PBXBuildFile section */
|
|
||||||
49AF90F7225B17D2004CC077 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 49AF90F6225B17D2004CC077 /* main.m */; };
|
|
||||||
/* End PBXBuildFile section */
|
|
||||||
|
|
||||||
/* Begin PBXCopyFilesBuildPhase section */
|
|
||||||
49AF90F1225B17D2004CC077 /* CopyFiles */ = {
|
|
||||||
isa = PBXCopyFilesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
dstPath = /usr/share/man/man1/;
|
|
||||||
dstSubfolderSpec = 0;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 1;
|
|
||||||
};
|
|
||||||
/* End PBXCopyFilesBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
|
||||||
49AF90F3225B17D2004CC077 /* ProxyConfig */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ProxyConfig; sourceTree = BUILT_PRODUCTS_DIR; };
|
|
||||||
49AF90F6225B17D2004CC077 /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = "<group>"; };
|
|
||||||
/* End PBXFileReference section */
|
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
|
||||||
49AF90F0225B17D2004CC077 /* Frameworks */ = {
|
|
||||||
isa = PBXFrameworksBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXFrameworksBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin PBXGroup section */
|
|
||||||
49AF90EA225B17D2004CC077 = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
49AF90F5225B17D2004CC077 /* ProxyConfig */,
|
|
||||||
49AF90F4225B17D2004CC077 /* Products */,
|
|
||||||
);
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
49AF90F4225B17D2004CC077 /* Products */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
49AF90F3225B17D2004CC077 /* ProxyConfig */,
|
|
||||||
);
|
|
||||||
name = Products;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
49AF90F5225B17D2004CC077 /* ProxyConfig */ = {
|
|
||||||
isa = PBXGroup;
|
|
||||||
children = (
|
|
||||||
49AF90F6225B17D2004CC077 /* main.m */,
|
|
||||||
);
|
|
||||||
path = ProxyConfig;
|
|
||||||
sourceTree = "<group>";
|
|
||||||
};
|
|
||||||
/* End PBXGroup section */
|
|
||||||
|
|
||||||
/* Begin PBXNativeTarget section */
|
|
||||||
49AF90F2225B17D2004CC077 /* ProxyConfig */ = {
|
|
||||||
isa = PBXNativeTarget;
|
|
||||||
buildConfigurationList = 49AF90FA225B17D2004CC077 /* Build configuration list for PBXNativeTarget "ProxyConfig" */;
|
|
||||||
buildPhases = (
|
|
||||||
49AF90EF225B17D2004CC077 /* Sources */,
|
|
||||||
49AF90F0225B17D2004CC077 /* Frameworks */,
|
|
||||||
49AF90F1225B17D2004CC077 /* CopyFiles */,
|
|
||||||
);
|
|
||||||
buildRules = (
|
|
||||||
);
|
|
||||||
dependencies = (
|
|
||||||
);
|
|
||||||
name = ProxyConfig;
|
|
||||||
productName = ProxyConfig;
|
|
||||||
productReference = 49AF90F3225B17D2004CC077 /* ProxyConfig */;
|
|
||||||
productType = "com.apple.product-type.tool";
|
|
||||||
};
|
|
||||||
/* End PBXNativeTarget section */
|
|
||||||
|
|
||||||
/* Begin PBXProject section */
|
|
||||||
49AF90EB225B17D2004CC077 /* Project object */ = {
|
|
||||||
isa = PBXProject;
|
|
||||||
attributes = {
|
|
||||||
LastUpgradeCheck = 1020;
|
|
||||||
ORGANIZATIONNAME = west2online;
|
|
||||||
TargetAttributes = {
|
|
||||||
49AF90F2225B17D2004CC077 = {
|
|
||||||
CreatedOnToolsVersion = 10.2;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
buildConfigurationList = 49AF90EE225B17D2004CC077 /* Build configuration list for PBXProject "ProxyConfig" */;
|
|
||||||
compatibilityVersion = "Xcode 9.3";
|
|
||||||
developmentRegion = en;
|
|
||||||
hasScannedForEncodings = 0;
|
|
||||||
knownRegions = (
|
|
||||||
en,
|
|
||||||
);
|
|
||||||
mainGroup = 49AF90EA225B17D2004CC077;
|
|
||||||
productRefGroup = 49AF90F4225B17D2004CC077 /* Products */;
|
|
||||||
projectDirPath = "";
|
|
||||||
projectRoot = "";
|
|
||||||
targets = (
|
|
||||||
49AF90F2225B17D2004CC077 /* ProxyConfig */,
|
|
||||||
);
|
|
||||||
};
|
|
||||||
/* End PBXProject section */
|
|
||||||
|
|
||||||
/* Begin PBXSourcesBuildPhase section */
|
|
||||||
49AF90EF225B17D2004CC077 /* Sources */ = {
|
|
||||||
isa = PBXSourcesBuildPhase;
|
|
||||||
buildActionMask = 2147483647;
|
|
||||||
files = (
|
|
||||||
49AF90F7225B17D2004CC077 /* main.m in Sources */,
|
|
||||||
);
|
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
|
||||||
};
|
|
||||||
/* End PBXSourcesBuildPhase section */
|
|
||||||
|
|
||||||
/* Begin XCBuildConfiguration section */
|
|
||||||
49AF90F8225B17D2004CC077 /* Debug */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
|
||||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
|
||||||
CLANG_ENABLE_MODULES = YES;
|
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
|
||||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
|
||||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
|
||||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_COMMA = YES;
|
|
||||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
|
||||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
|
||||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
|
||||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
|
||||||
CLANG_WARN_EMPTY_BODY = YES;
|
|
||||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
|
||||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
|
||||||
CLANG_WARN_INT_CONVERSION = YES;
|
|
||||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
|
||||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
|
||||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
|
||||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
|
||||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
|
||||||
COPY_PHASE_STRIP = NO;
|
|
||||||
DEBUG_INFORMATION_FORMAT = dwarf;
|
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
|
||||||
ENABLE_TESTABILITY = YES;
|
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
|
||||||
GCC_DYNAMIC_NO_PIC = NO;
|
|
||||||
GCC_NO_COMMON_BLOCKS = YES;
|
|
||||||
GCC_OPTIMIZATION_LEVEL = 0;
|
|
||||||
GCC_PREPROCESSOR_DEFINITIONS = (
|
|
||||||
"DEBUG=1",
|
|
||||||
"$(inherited)",
|
|
||||||
);
|
|
||||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
|
||||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
|
||||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
|
||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
|
||||||
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
|
|
||||||
MTL_FAST_MATH = YES;
|
|
||||||
ONLY_ACTIVE_ARCH = YES;
|
|
||||||
SDKROOT = macosx;
|
|
||||||
};
|
|
||||||
name = Debug;
|
|
||||||
};
|
|
||||||
49AF90F9225B17D2004CC077 /* Release */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
ALWAYS_SEARCH_USER_PATHS = NO;
|
|
||||||
CLANG_ANALYZER_NONNULL = YES;
|
|
||||||
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
|
|
||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
|
|
||||||
CLANG_CXX_LIBRARY = "libc++";
|
|
||||||
CLANG_ENABLE_MODULES = YES;
|
|
||||||
CLANG_ENABLE_OBJC_ARC = YES;
|
|
||||||
CLANG_ENABLE_OBJC_WEAK = YES;
|
|
||||||
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
|
|
||||||
CLANG_WARN_BOOL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_COMMA = YES;
|
|
||||||
CLANG_WARN_CONSTANT_CONVERSION = YES;
|
|
||||||
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
|
|
||||||
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
|
|
||||||
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
|
|
||||||
CLANG_WARN_EMPTY_BODY = YES;
|
|
||||||
CLANG_WARN_ENUM_CONVERSION = YES;
|
|
||||||
CLANG_WARN_INFINITE_RECURSION = YES;
|
|
||||||
CLANG_WARN_INT_CONVERSION = YES;
|
|
||||||
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
|
|
||||||
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
|
|
||||||
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
|
|
||||||
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
|
|
||||||
CLANG_WARN_STRICT_PROTOTYPES = YES;
|
|
||||||
CLANG_WARN_SUSPICIOUS_MOVE = YES;
|
|
||||||
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
|
|
||||||
CLANG_WARN_UNREACHABLE_CODE = YES;
|
|
||||||
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
|
|
||||||
CODE_SIGN_IDENTITY = "Mac Developer";
|
|
||||||
COPY_PHASE_STRIP = NO;
|
|
||||||
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
|
|
||||||
ENABLE_NS_ASSERTIONS = NO;
|
|
||||||
ENABLE_STRICT_OBJC_MSGSEND = YES;
|
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu11;
|
|
||||||
GCC_NO_COMMON_BLOCKS = YES;
|
|
||||||
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
|
|
||||||
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
|
|
||||||
GCC_WARN_UNDECLARED_SELECTOR = YES;
|
|
||||||
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
|
|
||||||
GCC_WARN_UNUSED_FUNCTION = YES;
|
|
||||||
GCC_WARN_UNUSED_VARIABLE = YES;
|
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.14;
|
|
||||||
MTL_ENABLE_DEBUG_INFO = NO;
|
|
||||||
MTL_FAST_MATH = YES;
|
|
||||||
SDKROOT = macosx;
|
|
||||||
};
|
|
||||||
name = Release;
|
|
||||||
};
|
|
||||||
49AF90FB225B17D2004CC077 /* Debug */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
CODE_SIGN_IDENTITY = "-";
|
|
||||||
CODE_SIGN_STYLE = Manual;
|
|
||||||
DEVELOPMENT_TEAM = "";
|
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
|
||||||
};
|
|
||||||
name = Debug;
|
|
||||||
};
|
|
||||||
49AF90FC225B17D2004CC077 /* Release */ = {
|
|
||||||
isa = XCBuildConfiguration;
|
|
||||||
buildSettings = {
|
|
||||||
CODE_SIGN_IDENTITY = "-";
|
|
||||||
CODE_SIGN_STYLE = Manual;
|
|
||||||
DEVELOPMENT_TEAM = "";
|
|
||||||
MACOSX_DEPLOYMENT_TARGET = 10.10;
|
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
|
||||||
};
|
|
||||||
name = Release;
|
|
||||||
};
|
|
||||||
/* End XCBuildConfiguration section */
|
|
||||||
|
|
||||||
/* Begin XCConfigurationList section */
|
|
||||||
49AF90EE225B17D2004CC077 /* Build configuration list for PBXProject "ProxyConfig" */ = {
|
|
||||||
isa = XCConfigurationList;
|
|
||||||
buildConfigurations = (
|
|
||||||
49AF90F8225B17D2004CC077 /* Debug */,
|
|
||||||
49AF90F9225B17D2004CC077 /* Release */,
|
|
||||||
);
|
|
||||||
defaultConfigurationIsVisible = 0;
|
|
||||||
defaultConfigurationName = Release;
|
|
||||||
};
|
|
||||||
49AF90FA225B17D2004CC077 /* Build configuration list for PBXNativeTarget "ProxyConfig" */ = {
|
|
||||||
isa = XCConfigurationList;
|
|
||||||
buildConfigurations = (
|
|
||||||
49AF90FB225B17D2004CC077 /* Debug */,
|
|
||||||
49AF90FC225B17D2004CC077 /* Release */,
|
|
||||||
);
|
|
||||||
defaultConfigurationIsVisible = 0;
|
|
||||||
defaultConfigurationName = Release;
|
|
||||||
};
|
|
||||||
/* End XCConfigurationList section */
|
|
||||||
};
|
|
||||||
rootObject = 49AF90EB225B17D2004CC077 /* Project object */;
|
|
||||||
}
|
|
|
@ -1,7 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Workspace
|
|
||||||
version = "1.0">
|
|
||||||
<FileRef
|
|
||||||
location = "self:ProxyConfig.xcodeproj">
|
|
||||||
</FileRef>
|
|
||||||
</Workspace>
|
|
|
@ -1,91 +0,0 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Scheme
|
|
||||||
LastUpgradeVersion = "1020"
|
|
||||||
version = "1.3">
|
|
||||||
<BuildAction
|
|
||||||
parallelizeBuildables = "YES"
|
|
||||||
buildImplicitDependencies = "YES">
|
|
||||||
<BuildActionEntries>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "49AF90F2225B17D2004CC077"
|
|
||||||
BuildableName = "ProxyConfig"
|
|
||||||
BlueprintName = "ProxyConfig"
|
|
||||||
ReferencedContainer = "container:ProxyConfig.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
</BuildActionEntries>
|
|
||||||
</BuildAction>
|
|
||||||
<TestAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
|
||||||
<Testables>
|
|
||||||
</Testables>
|
|
||||||
<MacroExpansion>
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "49AF90F2225B17D2004CC077"
|
|
||||||
BuildableName = "ProxyConfig"
|
|
||||||
BlueprintName = "ProxyConfig"
|
|
||||||
ReferencedContainer = "container:ProxyConfig.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</MacroExpansion>
|
|
||||||
<AdditionalOptions>
|
|
||||||
</AdditionalOptions>
|
|
||||||
</TestAction>
|
|
||||||
<LaunchAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
launchStyle = "0"
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
|
||||||
debugDocumentVersioning = "YES"
|
|
||||||
debugServiceExtension = "internal"
|
|
||||||
allowLocationSimulation = "YES">
|
|
||||||
<BuildableProductRunnable
|
|
||||||
runnableDebuggingMode = "0">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "49AF90F2225B17D2004CC077"
|
|
||||||
BuildableName = "ProxyConfig"
|
|
||||||
BlueprintName = "ProxyConfig"
|
|
||||||
ReferencedContainer = "container:ProxyConfig.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildableProductRunnable>
|
|
||||||
<AdditionalOptions>
|
|
||||||
</AdditionalOptions>
|
|
||||||
</LaunchAction>
|
|
||||||
<ProfileAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
savedToolIdentifier = ""
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
debugDocumentVersioning = "YES">
|
|
||||||
<BuildableProductRunnable
|
|
||||||
runnableDebuggingMode = "0">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "49AF90F2225B17D2004CC077"
|
|
||||||
BuildableName = "ProxyConfig"
|
|
||||||
BlueprintName = "ProxyConfig"
|
|
||||||
ReferencedContainer = "container:ProxyConfig.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildableProductRunnable>
|
|
||||||
</ProfileAction>
|
|
||||||
<AnalyzeAction
|
|
||||||
buildConfiguration = "Debug">
|
|
||||||
</AnalyzeAction>
|
|
||||||
<ArchiveAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
revealArchiveInOrganizer = "YES">
|
|
||||||
</ArchiveAction>
|
|
||||||
</Scheme>
|
|
|
@ -1,13 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
cd "$1";
|
|
||||||
CURRENT=$(./ProxyConfig version);
|
|
||||||
TARGET=$2;
|
|
||||||
if [[ `ls -l ProxyConfig` = *"root"* ]]; then
|
|
||||||
|
|
||||||
if [ "$CURRENT" == "$TARGET" ];then
|
|
||||||
echo "success"
|
|
||||||
exit
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
echo "false"
|
|
|
@ -1,4 +0,0 @@
|
||||||
cd "$1";
|
|
||||||
sudo chown root:admin "ProxyConfig"
|
|
||||||
sudo chmod +s "ProxyConfig"
|
|
||||||
echo done
|
|
|
@ -30,5 +30,10 @@
|
||||||
<string>Main</string>
|
<string>Main</string>
|
||||||
<key>NSPrincipalClass</key>
|
<key>NSPrincipalClass</key>
|
||||||
<string>NSApplication</string>
|
<string>NSApplication</string>
|
||||||
|
<key>SMPrivilegedExecutables</key>
|
||||||
|
<dict>
|
||||||
|
<key>com.github.erikberglund.SwiftPrivilegedHelper</key>
|
||||||
|
<string>identifier "com.github.erikberglund.SwiftPrivilegedHelper" and anchor apple generic and certificate leaf[subject.CN] = "Mac Developer: chen yicheng (96U846XGYH)" and certificate 1[field.1.2.840.113635.100.6.2.1] /* exists */</string>
|
||||||
|
</dict>
|
||||||
</dict>
|
</dict>
|
||||||
</plist>
|
</plist>
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>com.west2online.ClashX.ProxyConfigHelper</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>com.west2online.ClashX.ProxyConfigHelper</string>
|
||||||
|
<key>CFBundleShortVersionString</key>
|
||||||
|
<string>1.1</string>
|
||||||
|
<key>CFBundleVersion</key>
|
||||||
|
<string>1</string>
|
||||||
|
<key>SMAuthorizedClients</key>
|
||||||
|
<array>
|
||||||
|
<string>anchor apple generic and identifier "com.west2online.ClashX" and (certificate leaf[field.1.2.840.113635.100.6.1.9] /* exists */ or certificate 1[field.1.2.840.113635.100.6.2.6] /* exists */ and certificate leaf[field.1.2.840.113635.100.6.1.13] /* exists */ and certificate leaf[subject.OU] = MEWHFZ92DY)</string>
|
||||||
|
</array>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -2,7 +2,12 @@
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>IDEDidComputeMac32BitWarning</key>
|
<key>Label</key>
|
||||||
|
<string>com.west2online.ClashX.ProxyConfigHelper</string>
|
||||||
|
<key>MachServices</key>
|
||||||
|
<dict>
|
||||||
|
<key>com.west2online.ClashX.ProxyConfigHelper</key>
|
||||||
<true/>
|
<true/>
|
||||||
</dict>
|
</dict>
|
||||||
|
</dict>
|
||||||
</plist>
|
</plist>
|
|
@ -0,0 +1,17 @@
|
||||||
|
//
|
||||||
|
// ProxyConfigHelper.h
|
||||||
|
// com.west2online.ClashX.ProxyConfigHelper
|
||||||
|
//
|
||||||
|
// Created by yichengchen on 2019/8/17.
|
||||||
|
// Copyright © 2019 west2online. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface ProxyConfigHelper : NSObject
|
||||||
|
- (void)run;
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,116 @@
|
||||||
|
//
|
||||||
|
// ProxyConfigHelper.m
|
||||||
|
// com.west2online.ClashX.ProxyConfigHelper
|
||||||
|
//
|
||||||
|
// Created by yichengchen on 2019/8/17.
|
||||||
|
// Copyright © 2019 west2online. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "ProxyConfigHelper.h"
|
||||||
|
#import <AppKit/AppKit.h>
|
||||||
|
#import "ProxyConfigRemoteProcessProtocol.h"
|
||||||
|
#import "ProxySettingTool.h"
|
||||||
|
|
||||||
|
@interface ProxyConfigHelper()
|
||||||
|
<
|
||||||
|
NSXPCListenerDelegate,
|
||||||
|
ProxyConfigRemoteProcessProtocol
|
||||||
|
>
|
||||||
|
|
||||||
|
@property (nonatomic, strong) NSXPCListener *listener;
|
||||||
|
@property (nonatomic, strong) NSMutableSet<NSXPCConnection *> *connections;
|
||||||
|
@property (nonatomic, strong) NSTimer *checkTimer;
|
||||||
|
@property (nonatomic, assign) BOOL shouldQuit;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation ProxyConfigHelper
|
||||||
|
- (instancetype)init {
|
||||||
|
|
||||||
|
if (self = [super init]) {
|
||||||
|
self.connections = [NSMutableSet new];
|
||||||
|
self.shouldQuit = NO;
|
||||||
|
self.listener = [[NSXPCListener alloc] initWithMachServiceName:@"com.west2online.ClashX.ProxyConfigHelper"];
|
||||||
|
self.listener.delegate = self;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)run {
|
||||||
|
[self.listener resume];
|
||||||
|
self.checkTimer =
|
||||||
|
[NSTimer timerWithTimeInterval:5.f target:self selector:@selector(connectionCheckOnLaunch) userInfo:nil repeats:NO];
|
||||||
|
[[NSRunLoop currentRunLoop] addTimer:self.checkTimer forMode:NSDefaultRunLoopMode];
|
||||||
|
while (!self.shouldQuit) {
|
||||||
|
[[NSRunLoop currentRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:2.0]];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)connectionCheckOnLaunch {
|
||||||
|
if (self.connections.count == 0) {
|
||||||
|
self.shouldQuit = YES;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (BOOL)connectionIsVaild: (NSXPCConnection *)connection {
|
||||||
|
NSRunningApplication *remoteApp =
|
||||||
|
[NSRunningApplication runningApplicationWithProcessIdentifier:connection.processIdentifier];
|
||||||
|
return remoteApp != nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - NSXPCListenerDelegate
|
||||||
|
|
||||||
|
- (BOOL)listener:(NSXPCListener *)listener shouldAcceptNewConnection:(NSXPCConnection *)newConnection {
|
||||||
|
if (![self connectionIsVaild:newConnection]) {
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
newConnection.exportedInterface = [NSXPCInterface interfaceWithProtocol:@protocol(ProxyConfigRemoteProcessProtocol)];
|
||||||
|
newConnection.exportedObject = self;
|
||||||
|
__weak NSXPCConnection *weakConnection = newConnection;
|
||||||
|
__weak ProxyConfigHelper *weakSelf = self;
|
||||||
|
newConnection.invalidationHandler = ^{
|
||||||
|
[weakSelf.connections removeObject:weakConnection];
|
||||||
|
if (weakSelf.connections.count == 0) {
|
||||||
|
weakSelf.shouldQuit = YES;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
[self.connections addObject:newConnection];
|
||||||
|
[newConnection resume];
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - ProxyConfigRemoteProcessProtocol
|
||||||
|
- (void)getVersion:(stringReplyBlock)reply {
|
||||||
|
NSString *version = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleShortVersionString"];
|
||||||
|
if (version == nil) {
|
||||||
|
version = @"unknown";
|
||||||
|
}
|
||||||
|
reply(version);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)enableProxyWithPort:(int)port
|
||||||
|
socksPort:(int)socksPort
|
||||||
|
authData:(NSData *)authData
|
||||||
|
error:(stringReplyBlock)reply {
|
||||||
|
ProxySettingTool *tool = [ProxySettingTool new];
|
||||||
|
NSString *err = [tool setupAuth:authData];
|
||||||
|
if (err != nil) {
|
||||||
|
reply(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
[tool enableProxyWithport:port socksPort:socksPort];
|
||||||
|
reply(nil);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)disableProxyWithAuthData:(NSData *)authData error:(stringReplyBlock)reply {
|
||||||
|
ProxySettingTool *tool = [ProxySettingTool new];
|
||||||
|
NSString *err = [tool setupAuth:authData];
|
||||||
|
if (err != nil) {
|
||||||
|
reply(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
[tool disableProxy];
|
||||||
|
reply(nil);
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,26 @@
|
||||||
|
//
|
||||||
|
// ProxyConfigRemoteProcessProtocol.h
|
||||||
|
// com.west2online.ClashX.ProxyConfigHelper
|
||||||
|
//
|
||||||
|
// Created by yichengchen on 2019/8/17.
|
||||||
|
// Copyright © 2019 west2online. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
typedef void(^stringReplyBlock)(NSString *);
|
||||||
|
typedef void(^boolReplyBlock)(BOOL);
|
||||||
|
|
||||||
|
@protocol ProxyConfigRemoteProcessProtocol <NSObject>
|
||||||
|
@required
|
||||||
|
|
||||||
|
- (void)getVersion:(stringReplyBlock)reply;
|
||||||
|
|
||||||
|
|
||||||
|
- (void)enableProxyWithPort:(int)port
|
||||||
|
socksPort:(int)socksPort
|
||||||
|
authData:(NSData *)authData
|
||||||
|
error:(stringReplyBlock)reply;
|
||||||
|
|
||||||
|
- (void)disableProxyWithAuthData:(NSData *)authData error:(stringReplyBlock)reply;
|
||||||
|
@end
|
|
@ -0,0 +1,20 @@
|
||||||
|
//
|
||||||
|
// ProxySettingTool.h
|
||||||
|
// com.west2online.ClashX.ProxyConfigHelper
|
||||||
|
//
|
||||||
|
// Created by yichengchen on 2019/8/17.
|
||||||
|
// Copyright © 2019 west2online. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
@interface ProxySettingTool : NSObject
|
||||||
|
|
||||||
|
- (NSString *)setupAuth:(NSData *)authData;
|
||||||
|
- (void)enableProxyWithport:(int)port socksPort:(int)socksPort;
|
||||||
|
- (void)disableProxy;
|
||||||
|
@end
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
|
@ -0,0 +1,199 @@
|
||||||
|
//
|
||||||
|
// ProxySettingTool.m
|
||||||
|
// com.west2online.ClashX.ProxyConfigHelper
|
||||||
|
//
|
||||||
|
// Created by yichengchen on 2019/8/17.
|
||||||
|
// Copyright © 2019 west2online. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "ProxySettingTool.h"
|
||||||
|
#import <SystemConfiguration/SystemConfiguration.h>
|
||||||
|
#import <AppKit/AppKit.h>
|
||||||
|
|
||||||
|
@interface ProxySettingTool()
|
||||||
|
@property (nonatomic, assign) AuthorizationRef authRef;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation ProxySettingTool
|
||||||
|
|
||||||
|
// MARK: - Public
|
||||||
|
|
||||||
|
- (void)enableProxyWithport:(int)port socksPort:(int)socksPort {
|
||||||
|
[self applySCNetworkSettingWithRef:^(SCPreferencesRef ref) {
|
||||||
|
[self getDiviceListWithPrefRef:ref devices:^(NSString *key) {
|
||||||
|
[self enableProxySettings:ref interface:key port:port socksPort:socksPort];
|
||||||
|
}];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)disableProxy {
|
||||||
|
[self applySCNetworkSettingWithRef:^(SCPreferencesRef ref) {
|
||||||
|
[self getDiviceListWithPrefRef:ref devices:^(NSString *key) {
|
||||||
|
[self disableProxySetting:ref interface:key];
|
||||||
|
}];
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Private
|
||||||
|
|
||||||
|
- (void)dealloc {
|
||||||
|
[self freeAuth];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSArray<NSString *> *)getIgnoreList {
|
||||||
|
NSString *configPath = [NSHomeDirectory() stringByAppendingString:@"/.config/clash/proxyIgnoreList.plist"];
|
||||||
|
if ([NSFileManager.defaultManager fileExistsAtPath:configPath]) {
|
||||||
|
NSArray *arr = [[NSArray alloc] initWithContentsOfFile:configPath];
|
||||||
|
if (arr != nil && arr.count > 0 && [arr containsObject:@"127.0.0.1"]) {
|
||||||
|
return arr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
NSArray *ignoreList = @[
|
||||||
|
@"192.168.0.0/16",
|
||||||
|
@"10.0.0.0/8",
|
||||||
|
@"172.16.0.0/12",
|
||||||
|
@"127.0.0.1",
|
||||||
|
@"localhost",
|
||||||
|
@"*.local",
|
||||||
|
@"*.crashlytics.com"
|
||||||
|
];
|
||||||
|
return ignoreList;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSDictionary *)getProxySetting:(BOOL)enable port:(int) port socksPort: (int)socksPort {
|
||||||
|
NSMutableDictionary *proxySettings = [NSMutableDictionary dictionary];
|
||||||
|
|
||||||
|
NSString *ip = enable ? @"127.0.0.1" : @"";
|
||||||
|
NSInteger enableInt = enable ? 1 : 0;
|
||||||
|
|
||||||
|
proxySettings[(NSString *)kCFNetworkProxiesHTTPProxy] = ip;
|
||||||
|
proxySettings[(NSString *)kCFNetworkProxiesHTTPEnable] = @(enableInt);
|
||||||
|
proxySettings[(NSString *)kCFNetworkProxiesHTTPSProxy] = ip;
|
||||||
|
proxySettings[(NSString *)kCFNetworkProxiesHTTPSEnable] = @(enableInt);
|
||||||
|
|
||||||
|
proxySettings[(NSString *)kCFNetworkProxiesSOCKSProxy] = ip;
|
||||||
|
proxySettings[(NSString *)kCFNetworkProxiesSOCKSEnable] = @(enableInt);
|
||||||
|
|
||||||
|
if (enable) {
|
||||||
|
proxySettings[(NSString *)kCFNetworkProxiesHTTPPort] = @(port);
|
||||||
|
proxySettings[(NSString *)kCFNetworkProxiesHTTPSPort] = @(port);
|
||||||
|
proxySettings[(NSString *)kCFNetworkProxiesSOCKSPort] = @(socksPort);
|
||||||
|
} else {
|
||||||
|
proxySettings[(NSString *)kCFNetworkProxiesHTTPPort] = nil;
|
||||||
|
proxySettings[(NSString *)kCFNetworkProxiesHTTPSPort] = nil;
|
||||||
|
proxySettings[(NSString *)kCFNetworkProxiesSOCKSPort] = nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
proxySettings[(NSString *)kCFNetworkProxiesExceptionsList] = [self getIgnoreList];
|
||||||
|
|
||||||
|
return proxySettings;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)proxySettingPathWithInterface:(NSString *)interfaceKey {
|
||||||
|
return [NSString stringWithFormat:@"/%@/%@/%@",
|
||||||
|
(NSString *)kSCPrefNetworkServices,
|
||||||
|
interfaceKey,
|
||||||
|
(NSString *)kSCEntNetProxies];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)enableProxySettings:(SCPreferencesRef)prefs
|
||||||
|
interface:(NSString *)interfaceKey
|
||||||
|
port:(int) port
|
||||||
|
socksPort:(int) socksPort {
|
||||||
|
|
||||||
|
NSDictionary *proxySettings = [self getProxySetting:YES port:port socksPort:socksPort];
|
||||||
|
NSString *path = [self proxySettingPathWithInterface:interfaceKey];
|
||||||
|
SCPreferencesPathSetValue(prefs,
|
||||||
|
(__bridge CFStringRef)path,
|
||||||
|
(__bridge CFDictionaryRef)proxySettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)disableProxySetting:(SCPreferencesRef)prefs
|
||||||
|
interface:(NSString *)interfaceKey {
|
||||||
|
|
||||||
|
NSDictionary *proxySettings = [self getProxySetting:NO port:0 socksPort:0];
|
||||||
|
NSString *path = [self proxySettingPathWithInterface:interfaceKey];
|
||||||
|
SCPreferencesPathSetValue(prefs,
|
||||||
|
(__bridge CFStringRef)path,
|
||||||
|
(__bridge CFDictionaryRef)proxySettings);
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)getDiviceListWithPrefRef:(SCPreferencesRef)ref devices:(void(^)(NSString *))callback {
|
||||||
|
NSDictionary *sets = (__bridge NSDictionary *)SCPreferencesGetValue(ref, kSCPrefNetworkServices);
|
||||||
|
for (NSString *key in [sets allKeys]) {
|
||||||
|
NSMutableDictionary *dict = [sets objectForKey:key];
|
||||||
|
NSString *hardware = [dict valueForKeyPath:@"Interface.Hardware"];
|
||||||
|
if ([hardware isEqualToString:@"AirPort"]
|
||||||
|
|| [hardware isEqualToString:@"Wi-Fi"]
|
||||||
|
|| [hardware isEqualToString:@"Ethernet"]) {
|
||||||
|
callback(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)applySCNetworkSettingWithRef:(void(^)(SCPreferencesRef))callback {
|
||||||
|
SCPreferencesRef ref = SCPreferencesCreateWithAuthorization(nil, CFSTR("com.west2online.ClashX.ProxyConfigHelper.config"), nil, self.authRef);
|
||||||
|
if (!ref) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
callback(ref);
|
||||||
|
|
||||||
|
SCPreferencesCommitChanges(ref);
|
||||||
|
SCPreferencesApplyChanges(ref);
|
||||||
|
SCPreferencesSynchronize(ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- (AuthorizationFlags)authFlags {
|
||||||
|
AuthorizationFlags authFlags = kAuthorizationFlagDefaults
|
||||||
|
| kAuthorizationFlagExtendRights
|
||||||
|
| kAuthorizationFlagInteractionAllowed
|
||||||
|
| kAuthorizationFlagPreAuthorize;
|
||||||
|
return authFlags;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSString *)setupAuth:(NSData *)authData {
|
||||||
|
if (authData.length == 0 || authData.length != kAuthorizationExternalFormLength) {
|
||||||
|
return @"PrivilegedTaskRunnerHelper: Authorization data is malformed";
|
||||||
|
}
|
||||||
|
AuthorizationRef authRef;
|
||||||
|
|
||||||
|
OSStatus status = AuthorizationCreateFromExternalForm([authData bytes],&authRef);
|
||||||
|
if (status != errAuthorizationSuccess) {
|
||||||
|
return @"AuthorizationCreateFromExternalForm fail";
|
||||||
|
}
|
||||||
|
|
||||||
|
NSString *authName = @"com.west2online.ClashX.ProxyConfigHelper.config";
|
||||||
|
AuthorizationItem authItem = {authName.UTF8String, 0, NULL, 0};
|
||||||
|
AuthorizationRights authRight = {1, &authItem};
|
||||||
|
|
||||||
|
AuthorizationFlags authFlags = [self authFlags];
|
||||||
|
|
||||||
|
status = AuthorizationCopyRights(authRef, &authRight, nil, authFlags, nil);
|
||||||
|
if (status != errAuthorizationSuccess) {
|
||||||
|
AuthorizationFree(authRef, authFlags);
|
||||||
|
return @"AuthorizationCopyRights fail";
|
||||||
|
}
|
||||||
|
|
||||||
|
OSStatus authErr = AuthorizationCreate(nil, kAuthorizationEmptyEnvironment, authFlags, &authRef);
|
||||||
|
|
||||||
|
if (authErr != noErr) {
|
||||||
|
AuthorizationFree(authRef, authFlags);
|
||||||
|
return @"AuthorizationCreate fail";
|
||||||
|
}
|
||||||
|
self.authRef = authRef;
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (void)freeAuth {
|
||||||
|
if (self.authRef) {
|
||||||
|
AuthorizationFree(self.authRef, [self authFlags]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@end
|
|
@ -0,0 +1,17 @@
|
||||||
|
//
|
||||||
|
// main.m
|
||||||
|
// ProxyConfigHelper
|
||||||
|
//
|
||||||
|
// Created by yichengchen on 2019/8/16.
|
||||||
|
// Copyright © 2019 west2online. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
#import "ProxyConfigHelper.h"
|
||||||
|
int main(int argc, const char * argv[]) {
|
||||||
|
@autoreleasepool {
|
||||||
|
[[ProxyConfigHelper new] run];
|
||||||
|
NSLog(@"ProxyConfigHelper exit");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -0,0 +1,441 @@
|
||||||
|
#! /usr/bin/python
|
||||||
|
#
|
||||||
|
# File: SMJobBlessUtil.py
|
||||||
|
#
|
||||||
|
# Contains: Tool for checking and correcting apps that use SMJobBless.
|
||||||
|
#
|
||||||
|
# Written by: DTS
|
||||||
|
#
|
||||||
|
# Copyright: Copyright (c) 2012 Apple Inc. All Rights Reserved.
|
||||||
|
#
|
||||||
|
# Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple Inc.
|
||||||
|
# ("Apple") in consideration of your agreement to the following
|
||||||
|
# terms, and your use, installation, modification or
|
||||||
|
# redistribution of this Apple software constitutes acceptance of
|
||||||
|
# these terms. If you do not agree with these terms, please do
|
||||||
|
# not use, install, modify or redistribute this Apple software.
|
||||||
|
#
|
||||||
|
# In consideration of your agreement to abide by the following
|
||||||
|
# terms, and subject to these terms, Apple grants you a personal,
|
||||||
|
# non-exclusive license, under Apple's copyrights in this
|
||||||
|
# original Apple software (the "Apple Software"), to use,
|
||||||
|
# reproduce, modify and redistribute the Apple Software, with or
|
||||||
|
# without modifications, in source and/or binary forms; provided
|
||||||
|
# that if you redistribute the Apple Software in its entirety and
|
||||||
|
# without modifications, you must retain this notice and the
|
||||||
|
# following text and disclaimers in all such redistributions of
|
||||||
|
# the Apple Software. Neither the name, trademarks, service marks
|
||||||
|
# or logos of Apple Inc. may be used to endorse or promote
|
||||||
|
# products derived from the Apple Software without specific prior
|
||||||
|
# written permission from Apple. Except as expressly stated in
|
||||||
|
# this notice, no other rights or licenses, express or implied,
|
||||||
|
# are granted by Apple herein, including but not limited to any
|
||||||
|
# patent rights that may be infringed by your derivative works or
|
||||||
|
# by other works in which the Apple Software may be incorporated.
|
||||||
|
#
|
||||||
|
# The Apple Software is provided by Apple on an "AS IS" basis.
|
||||||
|
# APPLE MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING
|
||||||
|
# WITHOUT LIMITATION THE IMPLIED WARRANTIES OF NON-INFRINGEMENT,
|
||||||
|
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, REGARDING
|
||||||
|
# THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
|
||||||
|
# COMBINATION WITH YOUR PRODUCTS.
|
||||||
|
#
|
||||||
|
# IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT,
|
||||||
|
# INCIDENTAL OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
|
||||||
|
# TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) ARISING IN ANY WAY
|
||||||
|
# OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
|
||||||
|
# OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY
|
||||||
|
# OF CONTRACT, TORT (INCLUDING NEGLIGENCE), STRICT LIABILITY OR
|
||||||
|
# OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE POSSIBILITY OF
|
||||||
|
# SUCH DAMAGE.
|
||||||
|
#
|
||||||
|
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
import getopt
|
||||||
|
import subprocess
|
||||||
|
import plistlib
|
||||||
|
import operator
|
||||||
|
|
||||||
|
class UsageException (Exception):
|
||||||
|
"""
|
||||||
|
Raised when the progam detects a usage issue; the top-level code catches this
|
||||||
|
and prints a usage message.
|
||||||
|
"""
|
||||||
|
pass
|
||||||
|
|
||||||
|
class CheckException (Exception):
|
||||||
|
"""
|
||||||
|
Raised when the "check" subcommand detects a problem; the top-level code catches
|
||||||
|
this and prints a nice error message.
|
||||||
|
"""
|
||||||
|
def __init__(self, message, path=None):
|
||||||
|
self.message = message
|
||||||
|
self.path = path
|
||||||
|
|
||||||
|
def checkCodeSignature(programPath, programType):
|
||||||
|
"""Checks the code signature of the referenced program."""
|
||||||
|
|
||||||
|
# Use the codesign tool to check the signature. The second "-v" is required to enable
|
||||||
|
# verbose mode, which causes codesign to do more checking. By default it does the minimum
|
||||||
|
# amount of checking ("Is the program properly signed?"). If you enabled verbose mode it
|
||||||
|
# does other sanity checks, which we definitely want. The specific thing I'd like to
|
||||||
|
# detect is "Does the code satisfy its own designated requirement?" and I need to enable
|
||||||
|
# verbose mode to get that.
|
||||||
|
|
||||||
|
args = [
|
||||||
|
# "false",
|
||||||
|
"codesign",
|
||||||
|
"-v",
|
||||||
|
"-v",
|
||||||
|
programPath
|
||||||
|
]
|
||||||
|
try:
|
||||||
|
subprocess.check_call(args, stderr=open("/dev/null"))
|
||||||
|
except subprocess.CalledProcessError, e:
|
||||||
|
raise CheckException("%s code signature invalid" % programType, programPath)
|
||||||
|
|
||||||
|
def readDesignatedRequirement(programPath, programType):
|
||||||
|
"""Returns the designated requirement of the program as a string."""
|
||||||
|
args = [
|
||||||
|
# "false",
|
||||||
|
"codesign",
|
||||||
|
"-d",
|
||||||
|
"-r",
|
||||||
|
"-",
|
||||||
|
programPath
|
||||||
|
]
|
||||||
|
try:
|
||||||
|
req = subprocess.check_output(args, stderr=open("/dev/null"))
|
||||||
|
except subprocess.CalledProcessError, e:
|
||||||
|
raise CheckException("%s designated requirement unreadable" % programType, programPath)
|
||||||
|
|
||||||
|
reqLines = req.splitlines()
|
||||||
|
if len(reqLines) != 1 or not req.startswith("designated => "):
|
||||||
|
raise CheckException("%s designated requirement malformed" % programType, programPath)
|
||||||
|
return reqLines[0][len("designated => "):]
|
||||||
|
|
||||||
|
def readInfoPlistFromPath(infoPath):
|
||||||
|
"""Reads an "Info.plist" file from the specified path."""
|
||||||
|
try:
|
||||||
|
info = plistlib.readPlist(infoPath)
|
||||||
|
except:
|
||||||
|
raise CheckException("'Info.plist' not readable", infoPath)
|
||||||
|
if not isinstance(info, dict):
|
||||||
|
raise CheckException("'Info.plist' root must be a dictionary", infoPath)
|
||||||
|
return info
|
||||||
|
|
||||||
|
def readPlistFromToolSection(toolPath, segmentName, sectionName):
|
||||||
|
"""Reads a dictionary property list from the specified section within the specified executable."""
|
||||||
|
|
||||||
|
# Run otool -s to get a hex dump of the section.
|
||||||
|
|
||||||
|
args = [
|
||||||
|
# "false",
|
||||||
|
"otool",
|
||||||
|
"-s",
|
||||||
|
segmentName,
|
||||||
|
sectionName,
|
||||||
|
toolPath
|
||||||
|
]
|
||||||
|
try:
|
||||||
|
plistDump = subprocess.check_output(args)
|
||||||
|
except subprocess.CalledProcessError, e:
|
||||||
|
raise CheckException("tool %s / %s section unreadable" % (segmentName, sectionName), toolPath)
|
||||||
|
|
||||||
|
# Convert that hex dump to an property list.
|
||||||
|
|
||||||
|
plistLines = plistDump.splitlines()
|
||||||
|
if len(plistLines) < 3 or plistLines[1] != ("Contents of (%s,%s) section" % (segmentName, sectionName)):
|
||||||
|
raise CheckException("tool %s / %s section dump malformed (1)" % (segmentName, sectionName), toolPath)
|
||||||
|
del plistLines[0:2]
|
||||||
|
|
||||||
|
try:
|
||||||
|
bytes = []
|
||||||
|
for line in plistLines:
|
||||||
|
# line looks like this:
|
||||||
|
#
|
||||||
|
# '0000000100000b80\t3c 3f 78 6d 6c 20 76 65 72 73 69 6f 6e 3d 22 31 '
|
||||||
|
columns = line.split("\t")
|
||||||
|
assert len(columns) == 2
|
||||||
|
for hexStr in columns[1].split():
|
||||||
|
bytes.append(int(hexStr, 16))
|
||||||
|
plist = plistlib.readPlistFromString(bytearray(bytes))
|
||||||
|
except:
|
||||||
|
raise CheckException("tool %s / %s section dump malformed (2)" % (segmentName, sectionName), toolPath)
|
||||||
|
|
||||||
|
# Check the root of the property list.
|
||||||
|
|
||||||
|
if not isinstance(plist, dict):
|
||||||
|
raise CheckException("tool %s / %s property list root must be a dictionary" % (segmentName, sectionName), toolPath)
|
||||||
|
|
||||||
|
return plist
|
||||||
|
|
||||||
|
def checkStep1(appPath):
|
||||||
|
"""Checks that the app and the tool are both correctly code signed."""
|
||||||
|
|
||||||
|
if not os.path.isdir(appPath):
|
||||||
|
raise CheckException("app not found", appPath)
|
||||||
|
|
||||||
|
# Check the app's code signature.
|
||||||
|
|
||||||
|
checkCodeSignature(appPath, "app")
|
||||||
|
|
||||||
|
# Check the tool directory.
|
||||||
|
|
||||||
|
toolDirPath = os.path.join(appPath, "Contents", "Library", "LaunchServices")
|
||||||
|
if not os.path.isdir(toolDirPath):
|
||||||
|
raise CheckException("tool directory not found", toolDirPath)
|
||||||
|
|
||||||
|
# Check each tool's code signature.
|
||||||
|
|
||||||
|
toolPathList = []
|
||||||
|
for toolName in os.listdir(toolDirPath):
|
||||||
|
if toolName != ".DS_Store":
|
||||||
|
toolPath = os.path.join(toolDirPath, toolName)
|
||||||
|
if not os.path.isfile(toolPath):
|
||||||
|
raise CheckException("tool directory contains a directory", toolPath)
|
||||||
|
checkCodeSignature(toolPath, "tool")
|
||||||
|
toolPathList.append(toolPath)
|
||||||
|
|
||||||
|
# Check that we have at least one tool.
|
||||||
|
|
||||||
|
if len(toolPathList) == 0:
|
||||||
|
raise CheckException("no tools found", toolDirPath)
|
||||||
|
|
||||||
|
return toolPathList
|
||||||
|
|
||||||
|
def checkStep2(appPath, toolPathList):
|
||||||
|
"""Checks the SMPrivilegedExecutables entry in the app's "Info.plist"."""
|
||||||
|
|
||||||
|
# Create a map from the tool name (not path) to its designated requirement.
|
||||||
|
|
||||||
|
toolNameToReqMap = dict()
|
||||||
|
for toolPath in toolPathList:
|
||||||
|
req = readDesignatedRequirement(toolPath, "tool")
|
||||||
|
toolNameToReqMap[os.path.basename(toolPath)] = req
|
||||||
|
|
||||||
|
# Read the Info.plist for the app and extract the SMPrivilegedExecutables value.
|
||||||
|
|
||||||
|
infoPath = os.path.join(appPath, "Contents", "Info.plist")
|
||||||
|
info = readInfoPlistFromPath(infoPath)
|
||||||
|
if not info.has_key("SMPrivilegedExecutables"):
|
||||||
|
raise CheckException("'SMPrivilegedExecutables' not found", infoPath)
|
||||||
|
infoToolDict = info["SMPrivilegedExecutables"]
|
||||||
|
if not isinstance(infoToolDict, dict):
|
||||||
|
raise CheckException("'SMPrivilegedExecutables' must be a dictionary", infoPath)
|
||||||
|
|
||||||
|
# Check that the list of tools matches the list of SMPrivilegedExecutables entries.
|
||||||
|
|
||||||
|
if sorted(infoToolDict.keys()) != sorted(toolNameToReqMap.keys()):
|
||||||
|
raise CheckException("'SMPrivilegedExecutables' and tools in 'Contents/Library/LaunchServices' don't match")
|
||||||
|
|
||||||
|
# Check that all the requirements match.
|
||||||
|
|
||||||
|
# This is an interesting policy choice. Technically the tool just needs to match
|
||||||
|
# the requirement listed in SMPrivilegedExecutables, and we can check that by
|
||||||
|
# putting the requirement into tmp.req and then running
|
||||||
|
#
|
||||||
|
# $ codesign -v -R tmp.req /path/to/tool
|
||||||
|
#
|
||||||
|
# However, for a Developer ID signed tool we really want to have the SMPrivilegedExecutables
|
||||||
|
# entry contain the tool's designated requirement because Xcode has built a
|
||||||
|
# more complex DR that does lots of useful and important checks. So, as a matter
|
||||||
|
# of policy we require that the value in SMPrivilegedExecutables match the tool's DR.
|
||||||
|
|
||||||
|
for toolName in infoToolDict:
|
||||||
|
if infoToolDict[toolName] != toolNameToReqMap[toolName]:
|
||||||
|
raise CheckException("tool designated requirement (%s) doesn't match entry in 'SMPrivilegedExecutables' (%s)" % (toolNameToReqMap[toolName], infoToolDict[toolName]))
|
||||||
|
|
||||||
|
def checkStep3(appPath, toolPathList):
|
||||||
|
"""Checks the "Info.plist" embedded in each helper tool."""
|
||||||
|
|
||||||
|
# First get the app's designated requirement.
|
||||||
|
|
||||||
|
appReq = readDesignatedRequirement(appPath, "app")
|
||||||
|
|
||||||
|
# Then check that the tool's SMAuthorizedClients value matches it.
|
||||||
|
|
||||||
|
for toolPath in toolPathList:
|
||||||
|
info = readPlistFromToolSection(toolPath, "__TEXT", "__info_plist")
|
||||||
|
|
||||||
|
if not info.has_key("CFBundleInfoDictionaryVersion") or info["CFBundleInfoDictionaryVersion"] != "6.0":
|
||||||
|
raise CheckException("'CFBundleInfoDictionaryVersion' in tool __TEXT / __info_plist section must be '6.0'", toolPath)
|
||||||
|
|
||||||
|
if not info.has_key("CFBundleIdentifier") or info["CFBundleIdentifier"] != os.path.basename(toolPath):
|
||||||
|
raise CheckException("'CFBundleIdentifier' in tool __TEXT / __info_plist section must match tool name", toolPath)
|
||||||
|
|
||||||
|
if not info.has_key("SMAuthorizedClients"):
|
||||||
|
raise CheckException("'SMAuthorizedClients' in tool __TEXT / __info_plist section not found", toolPath)
|
||||||
|
infoClientList = info["SMAuthorizedClients"]
|
||||||
|
if not isinstance(infoClientList, list):
|
||||||
|
raise CheckException("'SMAuthorizedClients' in tool __TEXT / __info_plist section must be an array", toolPath)
|
||||||
|
if len(infoClientList) != 1:
|
||||||
|
raise CheckException("'SMAuthorizedClients' in tool __TEXT / __info_plist section must have one entry", toolPath)
|
||||||
|
|
||||||
|
# Again, as a matter of policy we require that the SMAuthorizedClients entry must
|
||||||
|
# match exactly the designated requirement of the app.
|
||||||
|
|
||||||
|
if infoClientList[0] != appReq:
|
||||||
|
raise CheckException("app designated requirement (%s) doesn't match entry in 'SMAuthorizedClients' (%s)" % (appReq, infoClientList[0]), toolPath)
|
||||||
|
|
||||||
|
def checkStep4(appPath, toolPathList):
|
||||||
|
"""Checks the "launchd.plist" embedded in each helper tool."""
|
||||||
|
|
||||||
|
for toolPath in toolPathList:
|
||||||
|
launchd = readPlistFromToolSection(toolPath, "__TEXT", "__launchd_plist")
|
||||||
|
|
||||||
|
if not launchd.has_key("Label") or launchd["Label"] != os.path.basename(toolPath):
|
||||||
|
raise CheckException("'Label' in tool __TEXT / __launchd_plist section must match tool name", toolPath)
|
||||||
|
|
||||||
|
# We don't need to check that the label matches the bundle identifier because
|
||||||
|
# we know it matches the tool name and step 4 checks that the tool name matches
|
||||||
|
# the bundle identifier.
|
||||||
|
|
||||||
|
def checkStep5(appPath):
|
||||||
|
"""There's nothing to do here; we effectively checked for this is steps 1 and 2."""
|
||||||
|
pass
|
||||||
|
|
||||||
|
def check(appPath):
|
||||||
|
"""Checks the SMJobBless setup of the specified app."""
|
||||||
|
|
||||||
|
# Each of the following steps matches a bullet point in the SMJobBless header doc.
|
||||||
|
|
||||||
|
toolPathList = checkStep1(appPath)
|
||||||
|
|
||||||
|
checkStep2(appPath, toolPathList)
|
||||||
|
|
||||||
|
checkStep3(appPath, toolPathList)
|
||||||
|
|
||||||
|
checkStep4(appPath, toolPathList)
|
||||||
|
|
||||||
|
checkStep5(appPath)
|
||||||
|
|
||||||
|
def setreq(appPath, appInfoPlistPath, toolInfoPlistPaths):
|
||||||
|
"""
|
||||||
|
Reads information from the built app and uses it to set the SMJobBless setup
|
||||||
|
in the specified app and tool Info.plist source files.
|
||||||
|
"""
|
||||||
|
|
||||||
|
if not os.path.isdir(appPath):
|
||||||
|
raise CheckException("app not found", appPath)
|
||||||
|
|
||||||
|
if not os.path.isfile(appInfoPlistPath):
|
||||||
|
raise CheckException("app 'Info.plist' not found", appInfoPlistPath)
|
||||||
|
for toolInfoPlistPath in toolInfoPlistPaths:
|
||||||
|
if not os.path.isfile(toolInfoPlistPath):
|
||||||
|
raise CheckException("app 'Info.plist' not found", toolInfoPlistPath)
|
||||||
|
|
||||||
|
# Get the designated requirement for the app and each of the tools.
|
||||||
|
|
||||||
|
appReq = readDesignatedRequirement(appPath, "app")
|
||||||
|
|
||||||
|
toolDirPath = os.path.join(appPath, "Contents", "Library", "LaunchServices")
|
||||||
|
if not os.path.isdir(toolDirPath):
|
||||||
|
raise CheckException("tool directory not found", toolDirPath)
|
||||||
|
|
||||||
|
toolNameToReqMap = {}
|
||||||
|
print os.listdir(toolDirPath)
|
||||||
|
for toolName in os.listdir(toolDirPath):
|
||||||
|
req = readDesignatedRequirement(os.path.join(toolDirPath, toolName), "tool")
|
||||||
|
print '-----'
|
||||||
|
print toolName
|
||||||
|
print '-----'
|
||||||
|
toolNameToReqMap[toolName] = req
|
||||||
|
|
||||||
|
if len(toolNameToReqMap) > len(toolInfoPlistPaths):
|
||||||
|
raise CheckException("tool directory has more tools (%d) than you've supplied tool 'Info.plist' paths (%d)" % (len(toolNameToReqMap), len(toolInfoPlistPaths)), toolDirPath)
|
||||||
|
if len(toolNameToReqMap) < len(toolInfoPlistPaths):
|
||||||
|
raise CheckException("tool directory has fewer tools (%d) than you've supplied tool 'Info.plist' paths (%d)" % (len(toolNameToReqMap), len(toolInfoPlistPaths)), toolDirPath)
|
||||||
|
|
||||||
|
# Build the new value for SMPrivilegedExecutables.
|
||||||
|
|
||||||
|
appToolDict = {}
|
||||||
|
toolInfoPlistPathToToolInfoMap = {}
|
||||||
|
for toolInfoPlistPath in toolInfoPlistPaths:
|
||||||
|
toolInfo = readInfoPlistFromPath(toolInfoPlistPath)
|
||||||
|
toolInfoPlistPathToToolInfoMap[toolInfoPlistPath] = toolInfo
|
||||||
|
if not toolInfo.has_key("CFBundleIdentifier"):
|
||||||
|
raise CheckException("'CFBundleIdentifier' not found", toolInfoPlistPath)
|
||||||
|
bundleID = toolInfo["CFBundleIdentifier"]
|
||||||
|
if not isinstance(bundleID, basestring):
|
||||||
|
raise CheckException("'CFBundleIdentifier' must be a string", toolInfoPlistPath)
|
||||||
|
appToolDict[bundleID] = toolNameToReqMap[bundleID]
|
||||||
|
|
||||||
|
# Set the SMPrivilegedExecutables value in the app "Info.plist".
|
||||||
|
|
||||||
|
appInfo = readInfoPlistFromPath(appInfoPlistPath)
|
||||||
|
needsUpdate = not appInfo.has_key("SMPrivilegedExecutables")
|
||||||
|
if not needsUpdate:
|
||||||
|
oldAppToolDict = appInfo["SMPrivilegedExecutables"]
|
||||||
|
if not isinstance(oldAppToolDict, dict):
|
||||||
|
raise CheckException("'SMPrivilegedExecutables' must be a dictionary", appInfoPlistPath)
|
||||||
|
appToolDictSorted = sorted(appToolDict.iteritems(), key=operator.itemgetter(0))
|
||||||
|
oldAppToolDictSorted = sorted(oldAppToolDict.iteritems(), key=operator.itemgetter(0))
|
||||||
|
needsUpdate = (appToolDictSorted != oldAppToolDictSorted)
|
||||||
|
|
||||||
|
if needsUpdate:
|
||||||
|
appInfo["SMPrivilegedExecutables"] = appToolDict
|
||||||
|
plistlib.writePlist(appInfo, appInfoPlistPath)
|
||||||
|
print >> sys.stdout, "%s: updated" % appInfoPlistPath
|
||||||
|
|
||||||
|
# Set the SMAuthorizedClients value in each tool's "Info.plist".
|
||||||
|
|
||||||
|
toolAppListSorted = [ appReq ] # only one element, so obviously sorted (-:
|
||||||
|
for toolInfoPlistPath in toolInfoPlistPaths:
|
||||||
|
toolInfo = toolInfoPlistPathToToolInfoMap[toolInfoPlistPath]
|
||||||
|
|
||||||
|
needsUpdate = not toolInfo.has_key("SMAuthorizedClients")
|
||||||
|
if not needsUpdate:
|
||||||
|
oldToolAppList = toolInfo["SMAuthorizedClients"]
|
||||||
|
if not isinstance(oldToolAppList, list):
|
||||||
|
raise CheckException("'SMAuthorizedClients' must be an array", toolInfoPlistPath)
|
||||||
|
oldToolAppListSorted = sorted(oldToolAppList)
|
||||||
|
needsUpdate = (toolAppListSorted != oldToolAppListSorted)
|
||||||
|
|
||||||
|
if needsUpdate:
|
||||||
|
toolInfo["SMAuthorizedClients"] = toolAppListSorted
|
||||||
|
plistlib.writePlist(toolInfo, toolInfoPlistPath)
|
||||||
|
print >> sys.stdout, "%s: updated" % toolInfoPlistPath
|
||||||
|
|
||||||
|
def main():
|
||||||
|
options, appArgs = getopt.getopt(sys.argv[1:], "d")
|
||||||
|
|
||||||
|
debug = False
|
||||||
|
for opt, val in options:
|
||||||
|
if opt == "-d":
|
||||||
|
debug = True
|
||||||
|
else:
|
||||||
|
raise UsageException()
|
||||||
|
|
||||||
|
if len(appArgs) == 0:
|
||||||
|
raise UsageException()
|
||||||
|
command = appArgs[0]
|
||||||
|
if command == "check":
|
||||||
|
if len(appArgs) != 2:
|
||||||
|
raise UsageException()
|
||||||
|
check(appArgs[1])
|
||||||
|
elif command == "setreq":
|
||||||
|
if len(appArgs) < 4:
|
||||||
|
raise UsageException()
|
||||||
|
setreq(appArgs[1], appArgs[2], appArgs[3:])
|
||||||
|
else:
|
||||||
|
raise UsageException()
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
try:
|
||||||
|
main()
|
||||||
|
except CheckException, e:
|
||||||
|
if e.path is None:
|
||||||
|
print >> sys.stderr, "%s: %s" % (os.path.basename(sys.argv[0]), e.message)
|
||||||
|
else:
|
||||||
|
path = e.path
|
||||||
|
if path.endswith("/"):
|
||||||
|
path = path[:-1]
|
||||||
|
print >> sys.stderr, "%s: %s" % (path, e.message)
|
||||||
|
sys.exit(1)
|
||||||
|
except UsageException, e:
|
||||||
|
print >> sys.stderr, "usage: %s check /path/to/app" % os.path.basename(sys.argv[0])
|
||||||
|
print >> sys.stderr, " %s setreq /path/to/app /path/to/app/Info.plist /path/to/tool/Info.plist..." % os.path.basename(sys.argv[0])
|
||||||
|
sys.exit(1)
|
Loading…
Reference in New Issue