Merge remote-tracking branch 'origin/master' into meta-dev

# Conflicts:
#	.github/workflows/main.yml
#	ClashX.xcodeproj/project.pbxproj
#	ClashX/goClash/go.mod
#	ClashX/goClash/go.sum
This commit is contained in:
mrFq1 2023-04-28 18:14:43 +08:00
commit fe814bf803
8 changed files with 162 additions and 138 deletions

View File

@ -22,6 +22,7 @@
01E33AB229B5BF4200FD1006 /* NSColor+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01E33AB129B5BF4200FD1006 /* NSColor+Extension.swift */; };
01E33AB529B5C5E400FD1006 /* menu_icon@2x.png in Resources */ = {isa = PBXBuildFile; fileRef = 01E33AB429B5C5E300FD1006 /* menu_icon@2x.png */; };
185CBAEDFE986E6E1B836359 /* libPods-ClashX Meta.a in Frameworks */ = {isa = PBXBuildFile; fileRef = AA63125BBF03DC1A291D3351 /* libPods-ClashX Meta.a */; };
4908087B29F8F405007A4944 /* libresolv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 4908087A29F8F3FF007A4944 /* libresolv.tbd */; };
4913C82321157D0200F6B87C /* Notification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4913C82221157D0200F6B87C /* Notification.swift */; };
491E6203258A424D00313AEF /* CommonUtils.m in Sources */ = {isa = PBXBuildFile; fileRef = 491E61FD258A424500313AEF /* CommonUtils.m */; };
49228457270AADE20027A4B6 /* RemoteConfigUpdateIntervalSettingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 49228456270AADE20027A4B6 /* RemoteConfigUpdateIntervalSettingView.swift */; };
@ -156,6 +157,7 @@
01E33AB129B5BF4200FD1006 /* NSColor+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSColor+Extension.swift"; sourceTree = "<group>"; };
01E33AB429B5C5E300FD1006 /* menu_icon@2x.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "menu_icon@2x.png"; sourceTree = "<group>"; };
3F86DA2DA3CC14731BE1ABF7 /* Pods-ClashX Meta.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ClashX Meta.release.xcconfig"; path = "Pods/Target Support Files/Pods-ClashX Meta/Pods-ClashX Meta.release.xcconfig"; sourceTree = "<group>"; };
4908087A29F8F3FF007A4944 /* libresolv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libresolv.tbd; path = usr/lib/libresolv.tbd; sourceTree = SDKROOT; };
4913C82221157D0200F6B87C /* Notification.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Notification.swift; sourceTree = "<group>"; };
491E61FC258A424500313AEF /* CommonUtils.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = CommonUtils.h; sourceTree = "<group>"; };
491E61FD258A424500313AEF /* CommonUtils.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = CommonUtils.m; sourceTree = "<group>"; };
@ -266,6 +268,7 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
4908087B29F8F405007A4944 /* libresolv.tbd in Frameworks */,
185CBAEDFE986E6E1B836359 /* libPods-ClashX Meta.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -544,6 +547,7 @@
CF1AC9FACC36FCE7663C5583 /* Frameworks */ = {
isa = PBXGroup;
children = (
4908087A29F8F3FF007A4944 /* libresolv.tbd */,
AA63125BBF03DC1A291D3351 /* libPods-ClashX Meta.a */,
);
name = Frameworks;

View File

@ -154,7 +154,7 @@ class PrivilegedHelperManager {
case needUpdate
}
private func getHelperStatus(callback:@escaping ((HelperStatus) -> Void)) {
private func getHelperStatus(callback: @escaping ((HelperStatus) -> Void)) {
var called = false
let reply: ((HelperStatus) -> Void) = {
status in
@ -278,7 +278,6 @@ private enum DaemonInstallResult {
case kSMErrorToolNotValid: return "blessError: kSMErrorToolNotValid"
case kSMErrorJobNotFound: return "blessError: kSMErrorJobNotFound"
case kSMErrorServiceUnavailable: return "blessError: kSMErrorServiceUnavailable"
case kSMErrorJobNotFound: return "blessError: kSMErrorJobNotFound"
case kSMErrorJobMustBeEnabled: return "ClashX Helper is disabled by other process. Please run \"sudo launchctl enable system/\(PrivilegedHelperManager.machServiceName)\" in your terminal. The command has been copied to your pasteboard"
case kSMErrorInvalidPlist: return "blessError: kSMErrorInvalidPlist"
default:

View File

@ -9,16 +9,16 @@
import Foundation
enum Settings {
@UserDefault("mmdbDownloadUrl", defaultValue: "")
static var mmdbDownloadUrl:String
static var mmdbDownloadUrl: String
@UserDefault("filterInterface", defaultValue: true)
static var filterInterface:Bool
static var filterInterface: Bool
@UserDefault("disableNoti", defaultValue: false)
static var disableNoti:Bool
static var disableNoti: Bool
@UserDefault("usePacMode", defaultValue: false)
static var usePacMode:Bool
static var usePacMode: Bool
@UserDefault("configAutoUpdateInterval", defaultValue: 48*60*60)
static var configAutoUpdateInterval: TimeInterval

View File

@ -78,7 +78,7 @@ class NetworkChangeNotifier {
return (httpProxy, httpsProxy, socksProxy)
}
static func isCurrentSystemSetToClash(looser:Bool = false) -> Bool {
static func isCurrentSystemSetToClash(looser: Bool = false) -> Bool {
let (http, https, socks) = NetworkChangeNotifier.currentSystemProxySetting()
let currentPort = ConfigManager.shared.currentConfig?.usedHttpPort ?? 0
let currentSocks = ConfigManager.shared.currentConfig?.usedSocksPort ?? 0
@ -99,7 +99,7 @@ class NetworkChangeNotifier {
let sets = SCPreferencesGetValue(prefRef, kSCPrefNetworkServices) {
for key in sets.allKeys {
let dict = sets[key] as? NSDictionary
let proxySettings = dict?["Proxies"] as? [String:Any]
let proxySettings = dict?["Proxies"] as? [String: Any]
if currentPort != nil {
if proxySettings?[kCFNetworkProxiesHTTPPort as String] as? Int == currentPort ||
proxySettings?[kCFNetworkProxiesHTTPSPort as String] as? Int == currentPort {

View File

@ -216,7 +216,7 @@ class CustomWKWebView: WKWebView {
var dragableAreaHeight: CGFloat = 30
let alwaysDragableLeftAreaWidth: CGFloat = 150
private func isInDargArea(with event:NSEvent?) -> Bool {
private func isInDargArea(with event: NSEvent?) -> Bool {
guard let event = event else { return false }
let x = event.locationInWindow.x
let y = (window?.frame.size.height ?? 0) - event.locationInWindow.y

View File

@ -9,7 +9,7 @@
import AppKit
import SwiftUI
@available(macOS 10.15, *)
class NewStatusMenuView:NSHostingView<SwiftUIView>, StatusItemViewProtocol {
class NewStatusMenuView: NSHostingView<SwiftUIView>, StatusItemViewProtocol {
private var viewModel: StatusMenuViewModel!
static func create(on button: NSView) -> NewStatusMenuView {
@ -45,7 +45,7 @@ class NewStatusMenuView:NSHostingView<SwiftUIView>, StatusItemViewProtocol {
}
@available(macOS 10.15, *)
class StatusMenuViewModel:ObservableObject {
class StatusMenuViewModel: ObservableObject {
@Published var image = StatusItemTool.getMenuImage(enableProxy: false)
@Published var upSpeed = "0KB/s"
@Published var downSpeed = "0KB/s"
@ -57,7 +57,9 @@ struct SwiftUIView: View {
@ObservedObject var viewModel: StatusMenuViewModel
var body: some View {
HStack(alignment:.center) {
Image(nsImage: $viewModel.image.wrappedValue).renderingMode(.template)
Image(nsImage: $viewModel.image.wrappedValue).renderingMode(.template)
.resizable().aspectRatio(contentMode: .fit).frame(width: 16,height: 16)
if $viewModel.showSpeed.wrappedValue {
Spacer(minLength: 0)
VStack(alignment: .trailing) {

View File

@ -1,4 +1,4 @@
#! /usr/bin/python
#! /usr/bin/python3
#
# File: SMJobBlessUtil.py
#
@ -57,6 +57,7 @@ import getopt
import subprocess
import plistlib
import operator
import platform
class UsageException (Exception):
"""
@ -93,7 +94,7 @@ def checkCodeSignature(programPath, programType):
]
try:
subprocess.check_call(args, stderr=open("/dev/null"))
except subprocess.CalledProcessError, e:
except subprocess.CalledProcessError as e:
raise CheckException("%s code signature invalid" % programType, programPath)
def readDesignatedRequirement(programPath, programType):
@ -107,8 +108,8 @@ def readDesignatedRequirement(programPath, programType):
programPath
]
try:
req = subprocess.check_output(args, stderr=open("/dev/null"))
except subprocess.CalledProcessError, e:
req = subprocess.check_output(args, stderr=open("/dev/null"), encoding="utf-8")
except subprocess.CalledProcessError as e:
raise CheckException("%s designated requirement unreadable" % programType, programPath)
reqLines = req.splitlines()
@ -119,7 +120,8 @@ def readDesignatedRequirement(programPath, programType):
def readInfoPlistFromPath(infoPath):
"""Reads an "Info.plist" file from the specified path."""
try:
info = plistlib.readPlist(infoPath)
with open(infoPath, 'rb') as fp:
info = plistlib.load(fp)
except:
raise CheckException("'Info.plist' not readable", infoPath)
if not isinstance(info, dict):
@ -134,36 +136,55 @@ def readPlistFromToolSection(toolPath, segmentName, sectionName):
args = [
# "false",
"otool",
"-V",
"-arch",
platform.machine(),
"-s",
segmentName,
sectionName,
toolPath
]
try:
plistDump = subprocess.check_output(args)
except subprocess.CalledProcessError, e:
plistDump = subprocess.check_output(args, encoding="utf-8")
except subprocess.CalledProcessError as e:
raise CheckException("tool %s / %s section unreadable" % (segmentName, sectionName), toolPath)
# Convert that hex dump to an property list.
# Convert that dump to an property list.
plistLines = plistDump.splitlines()
if len(plistLines) < 3 or plistLines[1] != ("Contents of (%s,%s) section" % (segmentName, sectionName)):
plistLines = plistDump.strip().splitlines(keepends=True)
if len(plistLines) < 3:
raise CheckException("tool %s / %s section dump malformed (1)" % (segmentName, sectionName), toolPath)
header = plistLines[1].strip()
if not header.endswith("(%s,%s) section" % (segmentName, sectionName)):
raise CheckException("tool %s / %s section dump malformed (2)" % (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))
if header.startswith('Contents of'):
data = []
for line in plistLines:
# line looks like this:
#
# '100000000 3c 3f 78 6d 6c 20 76 65 72 73 69 6f 6e 3d 22 31 |<?xml version="1|'
parts = line.split('|')
assert len(parts) == 3
columns = parts[0].split()
assert len(columns) >= 2
del columns[0]
for hexStr in columns:
data.append(int(hexStr, 16))
data = bytes(data)
else:
data = bytes("".join(plistLines), encoding="utf-8")
plist = plistlib.loads(data)
except:
raise CheckException("tool %s / %s section dump malformed (2)" % (segmentName, sectionName), toolPath)
raise CheckException("tool %s / %s section dump malformed (3)" % (segmentName, sectionName), toolPath)
# Check the root of the property list.
@ -220,7 +241,7 @@ def checkStep2(appPath, toolPathList):
infoPath = os.path.join(appPath, "Contents", "Info.plist")
info = readInfoPlistFromPath(infoPath)
if not info.has_key("SMPrivilegedExecutables"):
if "SMPrivilegedExecutables" not in info:
raise CheckException("'SMPrivilegedExecutables' not found", infoPath)
infoToolDict = info["SMPrivilegedExecutables"]
if not isinstance(infoToolDict, dict):
@ -260,13 +281,13 @@ def checkStep3(appPath, toolPathList):
for toolPath in toolPathList:
info = readPlistFromToolSection(toolPath, "__TEXT", "__info_plist")
if not info.has_key("CFBundleInfoDictionaryVersion") or info["CFBundleInfoDictionaryVersion"] != "6.0":
if "CFBundleInfoDictionaryVersion" not in info 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):
if "CFBundleIdentifier" not in info 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"):
if "SMAuthorizedClients" not in info:
raise CheckException("'SMAuthorizedClients' in tool __TEXT / __info_plist section not found", toolPath)
infoClientList = info["SMAuthorizedClients"]
if not isinstance(infoClientList, list):
@ -286,7 +307,7 @@ def checkStep4(appPath, toolPathList):
for toolPath in toolPathList:
launchd = readPlistFromToolSection(toolPath, "__TEXT", "__launchd_plist")
if not launchd.has_key("Label") or launchd["Label"] != os.path.basename(toolPath):
if "Label" not in launchd 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
@ -336,12 +357,8 @@ def setreq(appPath, appInfoPlistPath, toolInfoPlistPaths):
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):
@ -356,29 +373,30 @@ def setreq(appPath, appInfoPlistPath, toolInfoPlistPaths):
for toolInfoPlistPath in toolInfoPlistPaths:
toolInfo = readInfoPlistFromPath(toolInfoPlistPath)
toolInfoPlistPathToToolInfoMap[toolInfoPlistPath] = toolInfo
if not toolInfo.has_key("CFBundleIdentifier"):
if "CFBundleIdentifier" not in toolInfo:
raise CheckException("'CFBundleIdentifier' not found", toolInfoPlistPath)
bundleID = toolInfo["CFBundleIdentifier"]
if not isinstance(bundleID, basestring):
if not isinstance(bundleID, str):
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")
needsUpdate = "SMPrivilegedExecutables" not in appInfo
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))
appToolDictSorted = sorted(appToolDict.items(), key=operator.itemgetter(0))
oldAppToolDictSorted = sorted(oldAppToolDict.items(), key=operator.itemgetter(0))
needsUpdate = (appToolDictSorted != oldAppToolDictSorted)
if needsUpdate:
appInfo["SMPrivilegedExecutables"] = appToolDict
plistlib.writePlist(appInfo, appInfoPlistPath)
print >> sys.stdout, "%s: updated" % appInfoPlistPath
with open(appInfoPlistPath, 'wb') as fp:
plistlib.dump(appInfo, fp)
print ("%s: updated" % appInfoPlistPath, file = sys.stdout)
# Set the SMAuthorizedClients value in each tool's "Info.plist".
@ -386,7 +404,7 @@ def setreq(appPath, appInfoPlistPath, toolInfoPlistPaths):
for toolInfoPlistPath in toolInfoPlistPaths:
toolInfo = toolInfoPlistPathToToolInfoMap[toolInfoPlistPath]
needsUpdate = not toolInfo.has_key("SMAuthorizedClients")
needsUpdate = "SMAuthorizedClients" not in toolInfo
if not needsUpdate:
oldToolAppList = toolInfo["SMAuthorizedClients"]
if not isinstance(oldToolAppList, list):
@ -396,8 +414,9 @@ def setreq(appPath, appInfoPlistPath, toolInfoPlistPaths):
if needsUpdate:
toolInfo["SMAuthorizedClients"] = toolAppListSorted
plistlib.writePlist(toolInfo, toolInfoPlistPath)
print >> sys.stdout, "%s: updated" % toolInfoPlistPath
with open(toolInfoPlistPath, 'wb') as f:
plistlib.dump(toolInfo, f)
print("%s: updated" % toolInfoPlistPath, file = sys.stdout)
def main():
options, appArgs = getopt.getopt(sys.argv[1:], "d")
@ -426,16 +445,16 @@ def main():
if __name__ == "__main__":
try:
main()
except CheckException, e:
except CheckException as e:
if e.path is None:
print >> sys.stderr, "%s: %s" % (os.path.basename(sys.argv[0]), e.message)
print("%s: %s" % (os.path.basename(sys.argv[0]), e.message), file = sys.stderr)
else:
path = e.path
if path.endswith("/"):
path = path[:-1]
print >> sys.stderr, "%s: %s" % (path, e.message)
print("%s: %s" % (path, e.message), file = sys.stderr)
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])
except UsageException as e:
print("usage: %s check /path/to/app" % os.path.basename(sys.argv[0]), file = sys.stderr)
print(" %s setreq /path/to/app /path/to/app/Info.plist /path/to/tool/Info.plist..." % os.path.basename(sys.argv[0]), file = sys.stderr)
sys.exit(1)