Merge branch 'meta-proc' into meta-dev

This commit is contained in:
mrFq1 2022-07-27 13:24:12 +08:00
commit 384de6e3c9
21 changed files with 313 additions and 170 deletions

View File

@ -7,6 +7,8 @@
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
015F1E91288E42A50052B20A /* ClashMetaConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 015F1E90288E42A50052B20A /* ClashMetaConfig.swift */; };
015F1E92288E60D30052B20A /* MetaTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0162E74E2864B819007218A6 /* MetaTask.swift */; };
0162E74F2864B819007218A6 /* MetaTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0162E74E2864B819007218A6 /* MetaTask.swift */; }; 0162E74F2864B819007218A6 /* MetaTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0162E74E2864B819007218A6 /* MetaTask.swift */; };
018F88F9286DD0CB004DD0F7 /* DualTitleMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 018F88F8286DD0CB004DD0F7 /* DualTitleMenuItem.swift */; }; 018F88F9286DD0CB004DD0F7 /* DualTitleMenuItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 018F88F8286DD0CB004DD0F7 /* DualTitleMenuItem.swift */; };
01943259287D19BC008CC51A /* ClashRuleProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01943258287D19BC008CC51A /* ClashRuleProvider.swift */; }; 01943259287D19BC008CC51A /* ClashRuleProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = 01943258287D19BC008CC51A /* ClashRuleProvider.swift */; };
@ -131,6 +133,7 @@
/* End PBXCopyFilesBuildPhase section */ /* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */ /* Begin PBXFileReference section */
015F1E90288E42A50052B20A /* ClashMetaConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClashMetaConfig.swift; sourceTree = "<group>"; };
0162E74D2864B818007218A6 /* com.metacubex.ClashX.ProxyConfigHelper-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "com.metacubex.ClashX.ProxyConfigHelper-Bridging-Header.h"; sourceTree = "<group>"; }; 0162E74D2864B818007218A6 /* com.metacubex.ClashX.ProxyConfigHelper-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "com.metacubex.ClashX.ProxyConfigHelper-Bridging-Header.h"; sourceTree = "<group>"; };
0162E74E2864B819007218A6 /* MetaTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetaTask.swift; sourceTree = "<group>"; }; 0162E74E2864B819007218A6 /* MetaTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MetaTask.swift; sourceTree = "<group>"; };
018F88F8286DD0CB004DD0F7 /* DualTitleMenuItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DualTitleMenuItem.swift; sourceTree = "<group>"; }; 018F88F8286DD0CB004DD0F7 /* DualTitleMenuItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DualTitleMenuItem.swift; sourceTree = "<group>"; };
@ -325,6 +328,7 @@
491C250121BD561200AB5D44 /* Managers */, 491C250121BD561200AB5D44 /* Managers */,
491C250021BD55B900AB5D44 /* Utils */, 491C250021BD55B900AB5D44 /* Utils */,
492C4868210EE6B9004554A0 /* ApiRequest.swift */, 492C4868210EE6B9004554A0 /* ApiRequest.swift */,
015F1E90288E42A50052B20A /* ClashMetaConfig.swift */,
); );
path = General; path = General;
sourceTree = "<group>"; sourceTree = "<group>";
@ -709,6 +713,7 @@
49722FEF211F338B00650A41 /* FileEvent.swift in Sources */, 49722FEF211F338B00650A41 /* FileEvent.swift in Sources */,
49D176A72355FE680093DD7B /* NetworkChangeNotifier.swift in Sources */, 49D176A72355FE680093DD7B /* NetworkChangeNotifier.swift in Sources */,
4913C82321157D0200F6B87C /* Notification.swift in Sources */, 4913C82321157D0200F6B87C /* Notification.swift in Sources */,
015F1E91288E42A50052B20A /* ClashMetaConfig.swift in Sources */,
8ACD21BD27A04ED500BC4632 /* ProxyModeChangeCommand.swift in Sources */, 8ACD21BD27A04ED500BC4632 /* ProxyModeChangeCommand.swift in Sources */,
49228457270AADE20027A4B6 /* RemoteConfigUpdateIntervalSettingView.swift in Sources */, 49228457270AADE20027A4B6 /* RemoteConfigUpdateIntervalSettingView.swift in Sources */,
F9203A26236342820020D57D /* AppDelegate+..swift in Sources */, F9203A26236342820020D57D /* AppDelegate+..swift in Sources */,
@ -729,6 +734,7 @@
F92D0B2A236C759100575E15 /* NSTextField+Vibrancy.swift in Sources */, F92D0B2A236C759100575E15 /* NSTextField+Vibrancy.swift in Sources */,
F910AA24240134AF00116E95 /* ProxyGroupMenu.swift in Sources */, F910AA24240134AF00116E95 /* ProxyGroupMenu.swift in Sources */,
4952C3BF2115C7CA004A4FA8 /* MenuItemFactory.swift in Sources */, 4952C3BF2115C7CA004A4FA8 /* MenuItemFactory.swift in Sources */,
015F1E92288E60D30052B20A /* MetaTask.swift in Sources */,
F977FAAC2366790500C17F1F /* AutoUpgardeManager.swift in Sources */, F977FAAC2366790500C17F1F /* AutoUpgardeManager.swift in Sources */,
499A485822ED715200F6C675 /* RemoteConfigModel.swift in Sources */, 499A485822ED715200F6C675 /* RemoteConfigModel.swift in Sources */,
49D176AB23575BB20093DD7B /* ProxyGroupMenuItemView.swift in Sources */, 49D176AB23575BB20093DD7B /* ProxyGroupMenuItemView.swift in Sources */,

View File

@ -12,6 +12,8 @@ import LetsMove
import RxCocoa import RxCocoa
import RxSwift import RxSwift
import SwiftyJSON import SwiftyJSON
import Yams
import PromiseKit
private let statusItemLengthWithSpeed: CGFloat = 72 private let statusItemLengthWithSpeed: CGFloat = 72
@ -45,7 +47,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
@IBOutlet var copyExportCommandExternalMenuItem: NSMenuItem! @IBOutlet var copyExportCommandExternalMenuItem: NSMenuItem!
@IBOutlet var experimentalMenu: NSMenu! @IBOutlet var experimentalMenu: NSMenu!
@IBOutlet var externalControlSeparator: NSMenuItem! @IBOutlet var externalControlSeparator: NSMenuItem!
@IBOutlet var hideUnselecableMenuItem: NSMenuItem! @IBOutlet var hideUnselecableMenuItem: NSMenuItem!
@IBOutlet var proxyProvidersMenu: NSMenu! @IBOutlet var proxyProvidersMenu: NSMenu!
@IBOutlet var ruleProvidersMenu: NSMenu! @IBOutlet var ruleProvidersMenu: NSMenu!
@ -53,7 +55,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
@IBOutlet var ruleProvidersMenuItem: NSMenuItem! @IBOutlet var ruleProvidersMenuItem: NSMenuItem!
@IBOutlet var snifferMenuItem: NSMenuItem! @IBOutlet var snifferMenuItem: NSMenuItem!
@IBOutlet var flushFakeipCacheMenuItem: NSMenuItem! @IBOutlet var flushFakeipCacheMenuItem: NSMenuItem!
@IBOutlet var useAlphaMetaMenuItem: NSMenuItem! @IBOutlet var useAlphaMetaMenuItem: NSMenuItem!
@IBOutlet var alphaMetaVersionMenuItem: NSMenuItem! @IBOutlet var alphaMetaVersionMenuItem: NSMenuItem!
@IBOutlet var updateAlphaMetaMenuItem: NSMenuItem! @IBOutlet var updateAlphaMetaMenuItem: NSMenuItem!
@ -65,7 +67,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
var runAfterConfigReload: (() -> Void)? var runAfterConfigReload: (() -> Void)?
var dashboardWindowController: ClashWebViewWindowController? var dashboardWindowController: ClashWebViewWindowController?
func applicationWillFinishLaunching(_ notification: Notification) { func applicationWillFinishLaunching(_ notification: Notification) {
signal(SIGPIPE, SIG_IGN) signal(SIGPIPE, SIG_IGN)
// crash recorder // crash recorder
@ -114,7 +116,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
runAfterConfigReload = { [weak self] in runAfterConfigReload = { [weak self] in
self?.selectAllowLanWithMenory() self?.selectAllowLanWithMenory()
} }
updateLoggingLevel() updateLoggingLevel()
// start watch config file change // start watch config file change
@ -128,7 +130,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply { func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {
let group = DispatchGroup() let group = DispatchGroup()
var shouldWait = false var shouldWait = false
PrivilegedHelperManager.shared.helper()?.stopMeta() PrivilegedHelperManager.shared.helper()?.stopMeta()
if ConfigManager.shared.proxyPortAutoSet && !ConfigManager.shared.isProxySetByOtherVariable.value || NetworkChangeNotifier.isCurrentSystemSetToClash(looser: true) || if ConfigManager.shared.proxyPortAutoSet && !ConfigManager.shared.isProxySetByOtherVariable.value || NetworkChangeNotifier.isCurrentSystemSetToClash(looser: true) ||
@ -211,7 +213,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
}).disposed(by: disposeBag) }).disposed(by: disposeBag)
remoteConfigAutoupdateMenuItem.state = RemoteConfigManager.autoUpdateEnable ? .on : .off remoteConfigAutoupdateMenuItem.state = RemoteConfigManager.autoUpdateEnable ? .on : .off
hideUnselecableMenuItem.state = .init(rawValue: MenuItemFactory.hideUnselectable) hideUnselecableMenuItem.state = .init(rawValue: MenuItemFactory.hideUnselectable)
useAlphaMetaMenuItem.state = MenuItemFactory.useAlphaCore ? .on : .off useAlphaMetaMenuItem.state = MenuItemFactory.useAlphaCore ? .on : .off
} }
@ -289,7 +291,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
self.proxySettingMenuItem.target = self self.proxySettingMenuItem.target = self
}.disposed(by: disposeBag) }.disposed(by: disposeBag)
} }
// start proxy // start proxy
if !PrivilegedHelperManager.shared.isHelperCheckFinished.value { if !PrivilegedHelperManager.shared.isHelperCheckFinished.value {
PrivilegedHelperManager.shared.isHelperCheckFinished PrivilegedHelperManager.shared.isHelperCheckFinished
@ -298,14 +300,14 @@ class AppDelegate: NSObject, NSApplicationDelegate {
.observe(on: MainScheduler.instance) .observe(on: MainScheduler.instance)
.bind(onNext: { _ in .bind(onNext: { _ in
self.initMetaCore() self.initMetaCore()
self.updateConfig(showNotification: false) self.startProxy()
}).disposed(by: disposeBag) }).disposed(by: disposeBag)
} else { } else {
initMetaCore() initMetaCore()
updateConfig(showNotification: false) startProxy()
} }
} }
func setupSystemData() { func setupSystemData() {
if !PrivilegedHelperManager.shared.isHelperCheckFinished.value && if !PrivilegedHelperManager.shared.isHelperCheckFinished.value &&
ConfigManager.shared.proxyPortAutoSet { ConfigManager.shared.proxyPortAutoSet {
@ -419,10 +421,10 @@ class AppDelegate: NSObject, NSApplicationDelegate {
} }
NotificationCenter.default.post(name: .reloadDashboard, object: nil) NotificationCenter.default.post(name: .reloadDashboard, object: nil)
} }
func initMetaCore() { func initMetaCore() {
Logger.log("initClashCore") Logger.log("initClashCore")
let corePath: String = { let corePath: String = {
if let path = Paths.alphaCorePath()?.path, if let path = Paths.alphaCorePath()?.path,
let v = testMetaCore(path) { let v = testMetaCore(path) {
@ -433,7 +435,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
} else { } else {
updateAlphaVersion(nil) updateAlphaVersion(nil)
} }
if let path = Paths.defaultCorePath(), if let path = Paths.defaultCorePath(),
testMetaCore(path) != nil { testMetaCore(path) != nil {
return path return path
@ -443,16 +445,17 @@ class AppDelegate: NSObject, NSApplicationDelegate {
} }
}() }()
RemoteConfigManager.shared.verifyConfigTask.setLaunchPath(corePath)
PrivilegedHelperManager.shared.helper()?.initMetaCore(withPath: corePath) PrivilegedHelperManager.shared.helper()?.initMetaCore(withPath: corePath)
Logger.log("initClashCore finish") Logger.log("initClashCore finish")
} }
func testMetaCore(_ path: String) -> (version: String, date: Date?)? { func testMetaCore(_ path: String) -> (version: String, date: Date?)? {
guard FileManager.default.fileExists(atPath: path), guard FileManager.default.fileExists(atPath: path),
chmodX(path) else { chmodX(path) else {
return nil return nil
} }
let proc = Process() let proc = Process()
proc.executableURL = .init(fileURLWithPath: path) proc.executableURL = .init(fileURLWithPath: path)
proc.arguments = ["-v"] proc.arguments = ["-v"]
@ -466,23 +469,23 @@ class AppDelegate: NSObject, NSApplicationDelegate {
} }
proc.waitUntilExit() proc.waitUntilExit()
let data = pipe.fileHandleForReading.readDataToEndOfFile() let data = pipe.fileHandleForReading.readDataToEndOfFile()
guard proc.terminationStatus == 0, guard proc.terminationStatus == 0,
let out = String(data: data, encoding: .utf8) else { let out = String(data: data, encoding: .utf8) else {
return nil return nil
} }
let outs = out.replacingOccurrences(of: "\n", with: "").split(separator: " ").map(String.init) let outs = out.replacingOccurrences(of: "\n", with: "").split(separator: " ").map(String.init)
guard outs.count == 13, guard outs.count == 13,
outs[0] == "Clash", outs[0] == "Clash",
outs[1] == "Meta", outs[1] == "Meta",
outs[3] == "darwin" else { outs[3] == "darwin" else {
return nil return nil
} }
let version = outs[2] let version = outs[2]
let dateString = [outs[7], outs[8], outs[9], outs[10], outs[12]].joined(separator: "-") let dateString = [outs[7], outs[8], outs[9], outs[10], outs[12]].joined(separator: "-")
let f = DateFormatter() let f = DateFormatter()
f.dateFormat = "E-MMM-d-HH:mm:ss-yyyy" f.dateFormat = "E-MMM-d-HH:mm:ss-yyyy"
@ -491,7 +494,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
return (version: version, date: date) return (version: version, date: date)
} }
func chmodX(_ path: String) -> Bool { func chmodX(_ path: String) -> Bool {
let proc = Process() let proc = Process()
proc.executableURL = .init(fileURLWithPath: "/bin/chmod") proc.executableURL = .init(fileURLWithPath: "/bin/chmod")
@ -514,30 +517,16 @@ class AppDelegate: NSObject, NSApplicationDelegate {
let secret: String let secret: String
let log: String? let log: String?
} }
// setup ui config first
if let htmlPath = Bundle.main.path(forResource: "index", ofType: "html", inDirectory: "dashboard") {
let uiPath = URL(fileURLWithPath: htmlPath).deletingLastPathComponent().path
PrivilegedHelperManager.shared.helper()?.metaSetUIPath(uiPath)
}
Logger.log("Trying start proxy") let config = ClashMetaConfig.generateInitConfig()
var string = ""
let queue = DispatchGroup() Logger.log("Trying start meta core")
queue.enter() startMeta(config).map { string -> String in
guard let jsonData = string.data(using: .utf8),
PrivilegedHelperManager.shared.helper { let res = try? JSONDecoder().decode(StartProxyResp.self, from: jsonData) else {
string = "Can't connect to helper." return string == "" ? "unknown error" : string
queue.leave() }
}?.startMeta(withConfPath: kConfigFolderPath,
confFilePath: "") {
string = $0 ?? ""
queue.leave()
}
queue.wait()
let jsonData = string.data(using: .utf8) ?? Data()
if let res = try? JSONDecoder().decode(StartProxyResp.self, from: jsonData) {
if let log = res.log { if let log = res.log {
Logger.log(""" Logger.log("""
\n######## Clash Meta Start Log ######### \n######## Clash Meta Start Log #########
@ -545,22 +534,64 @@ class AppDelegate: NSObject, NSApplicationDelegate {
######## END ######### ######## END #########
""", level: .info) """, level: .info)
} }
let port = res.externalController.components(separatedBy: ":").last ?? "9090" let port = res.externalController.components(separatedBy: ":").last ?? "9090"
ConfigManager.shared.apiPort = port ConfigManager.shared.apiPort = port
ConfigManager.shared.apiSecret = res.secret ConfigManager.shared.apiSecret = res.secret
ConfigManager.shared.isRunning = true ConfigManager.shared.isRunning = true
proxyModeMenuItem.isEnabled = true self.proxyModeMenuItem.isEnabled = true
dashboardMenuItem.isEnabled = true self.dashboardMenuItem.isEnabled = true
return ""
setupSystemData() }.then {
} else { self.pushInitConfig($0)
ConfigManager.shared.isRunning = false }.done { s in
proxyModeMenuItem.isEnabled = false if s != "" {
Logger.log(string, level: .error) ConfigManager.shared.isRunning = false
NSUserNotificationCenter.default.postConfigErrorNotice(msg: string) self.proxyModeMenuItem.isEnabled = false
Logger.log(s, level: .error)
NSUserNotificationCenter.default.postConfigErrorNotice(msg: s)
} else {
Logger.log("Init config file success.")
}
}.catch { _ in }
}
func startMeta(_ config: ClashMetaConfig.Config) -> Promise<String> {
.init { resolver in
PrivilegedHelperManager.shared.helper {
resolver.fulfill("Can't connect to helper.")
}?.startMeta(withConfPath: kConfigFolderPath,
confFilePath: config.path) {
resolver.fulfill($0 ?? "")
}
}
}
func pushInitConfig(_ s: String) -> Promise<String> {
.init { resolver in
guard s == "" else {
resolver.fulfill(s)
return
}
ClashProxy.cleanCache()
let configName = ConfigManager.selectConfigName
Logger.log("Push init config file: \(configName)")
ApiRequest.requestConfigUpdate(configName: configName) { err in
if let error = err {
resolver.fulfill(error)
} else {
self.syncConfig()
self.resetStreamApi()
self.runAfterConfigReload?()
self.runAfterConfigReload = nil
self.selectProxyGroupWithMemory()
self.selectOutBoundModeWithMenory()
MenuItemFactory.recreateProxyMenuItems()
NotificationCenter.default.post(name: .reloadDashboard, object: nil)
resolver.fulfill("")
}
}
} }
Logger.log("Start proxy done")
} }
func syncConfig(completeHandler: (() -> Void)? = nil) { func syncConfig(completeHandler: (() -> Void)? = nil) {
@ -868,7 +899,6 @@ extension AppDelegate {
@IBAction func actionUpdateProxyGroupMenu(_ sender: Any) { @IBAction func actionUpdateProxyGroupMenu(_ sender: Any) {
ConfigManager.shared.disableShowCurrentProxyInMenu = !ConfigManager.shared.disableShowCurrentProxyInMenu ConfigManager.shared.disableShowCurrentProxyInMenu = !ConfigManager.shared.disableShowCurrentProxyInMenu
updateExperimentalFeatureStatus() updateExperimentalFeatureStatus()
print("211")
MenuItemFactory.recreateProxyMenuItems() MenuItemFactory.recreateProxyMenuItems()
} }
@ -912,7 +942,7 @@ extension AppDelegate {
sender.state = newState sender.state = newState
MenuItemFactory.hideUnselectable = newState.rawValue MenuItemFactory.hideUnselectable = newState.rawValue
} }
@IBAction func checkForUpdate(_ sender: NSMenuItem) { @IBAction func checkForUpdate(_ sender: NSMenuItem) {
let unc = NSUserNotificationCenter.default let unc = NSUserNotificationCenter.default
AF.request("https://api.github.com/repos/MetaCubeX/ClashX.Meta/releases/latest").responseString { AF.request("https://api.github.com/repos/MetaCubeX/ClashX.Meta/releases/latest").responseString {
@ -922,7 +952,7 @@ extension AppDelegate {
unc.postUpdateNotice(msg: "Some thing failed.") unc.postUpdateNotice(msg: "Some thing failed.")
return return
} }
if tagName != AppVersionUtil.currentVersion { if tagName != AppVersionUtil.currentVersion {
let alert = NSAlert() let alert = NSAlert()
alert.messageText = "Open github release page to download \(tagName)" alert.messageText = "Open github release page to download \(tagName)"
@ -936,38 +966,38 @@ extension AppDelegate {
} }
} }
} }
@IBAction func updateGEO(_ sender: NSMenuItem) { @IBAction func updateGEO(_ sender: NSMenuItem) {
ApiRequest.updateGEO() { _ in ApiRequest.updateGEO { _ in
NSUserNotificationCenter.default.post(title: "Updating GEO Databases...", info: "Good luck to you 🙃") NSUserNotificationCenter.default.post(title: "Updating GEO Databases...", info: "Good luck to you 🙃")
} }
} }
@IBAction func flushFakeipCache(_ sender: NSMenuItem) { @IBAction func flushFakeipCache(_ sender: NSMenuItem) {
ApiRequest.flushFakeipCache() { ApiRequest.flushFakeipCache {
NSUserNotificationCenter.default.post(title: "Flush fake-ip cache", info: $0 ? "Success" : "Failed") NSUserNotificationCenter.default.post(title: "Flush fake-ip cache", info: $0 ? "Success" : "Failed")
} }
} }
@IBAction func updateSniffing(_ sender: NSMenuItem) { @IBAction func updateSniffing(_ sender: NSMenuItem) {
let enable = sender.state != .on let enable = sender.state != .on
ApiRequest.updateSniffing(enable: enable) { ApiRequest.updateSniffing(enable: enable) {
sender.state = enable ? .on : .off sender.state = enable ? .on : .off
} }
} }
@IBAction func useAlphaMeta(_ sender: NSMenuItem) { @IBAction func useAlphaMeta(_ sender: NSMenuItem) {
let use = sender.state != .on let use = sender.state != .on
MenuItemFactory.useAlphaCore = use MenuItemFactory.useAlphaCore = use
sender.state = use ? .on : .off sender.state = use ? .on : .off
} }
@IBAction func showAlphaInFinder(_ sender: NSMenuItem) { @IBAction func showAlphaInFinder(_ sender: NSMenuItem) {
guard let u = Paths.alphaCorePath(), guard let u = Paths.alphaCorePath(),
FileManager.default.fileExists(atPath: u.path) else { return } FileManager.default.fileExists(atPath: u.path) else { return }
NSWorkspace.shared.activateFileViewerSelecting([u]) NSWorkspace.shared.activateFileViewerSelecting([u])
} }
@IBAction func updateAlphaMeta(_ sender: NSMenuItem) { @IBAction func updateAlphaMeta(_ sender: NSMenuItem) {
guard let helperURL = Paths.alphaCorePath() else { guard let helperURL = Paths.alphaCorePath() else {
return return
@ -989,7 +1019,7 @@ extension AppDelegate {
} }
} }
} }
func GetMachineHardwareName() -> String? { func GetMachineHardwareName() -> String? {
var sysInfo = utsname() var sysInfo = utsname()
let retVal = uname(&sysInfo) let retVal = uname(&sysInfo)
@ -998,7 +1028,7 @@ extension AppDelegate {
return String(cString: &sysInfo.machine.0, encoding: .utf8) return String(cString: &sysInfo.machine.0, encoding: .utf8)
} }
let assetName: String? = { let assetName: String? = {
switch GetMachineHardwareName() { switch GetMachineHardwareName() {
case "x86_64": case "x86_64":
@ -1010,12 +1040,12 @@ extension AppDelegate {
} }
}() }()
let fm = FileManager.default let fm = FileManager.default
func dlResult(_ info: String) { func dlResult(_ info: String) {
sender.isEnabled = true sender.isEnabled = true
NSUserNotificationCenter.default.post(title: "Clash Meta Core", info: info) NSUserNotificationCenter.default.post(title: "Clash Meta Core", info: info)
} }
AF.request("https://api.github.com/repos/MetaCubeX/Clash.Meta/releases/tags/Prerelease-Alpha").responseDecodable(of: ReleasesResp.self) { AF.request("https://api.github.com/repos/MetaCubeX/Clash.Meta/releases/tags/Prerelease-Alpha").responseDecodable(of: ReleasesResp.self) {
guard let assets = $0.value?.assets, guard let assets = $0.value?.assets,
let assetName = assetName, let assetName = assetName,
@ -1027,16 +1057,16 @@ extension AppDelegate {
dlResult("Decode alpha release info failed") dlResult("Decode alpha release info failed")
return return
} }
if let v = self.testMetaCore(helperURL.path), if let v = self.testMetaCore(helperURL.path),
asset.name.contains(v.version) { asset.name.contains(v.version) {
dlResult("Not found update") dlResult("Not found update")
return return
} }
self.updateAlphaVersion(nil) self.updateAlphaVersion(nil)
try? fm.removeItem(at: helperURL) try? fm.removeItem(at: helperURL)
AF.download(asset.downloadUrl).response { AF.download(asset.downloadUrl).response {
guard let gzPath = $0.fileURL?.path, guard let gzPath = $0.fileURL?.path,
let contentData = fm.contents(atPath: gzPath) let contentData = fm.contents(atPath: gzPath)
@ -1059,7 +1089,7 @@ extension AppDelegate {
} }
} }
} }
func updateAlphaVersion(_ version: String?) { func updateAlphaVersion(_ version: String?) {
let enable = version != nil let enable = version != nil
useAlphaMetaMenuItem.isEnabled = enable useAlphaMetaMenuItem.isEnabled = enable

View File

@ -15,7 +15,7 @@ extension DateFormatter {
dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SZ" dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SZ"
return dateFormatter return dateFormatter
} }
static var provider: DateFormatter { static var provider: DateFormatter {
let f = DateFormatter() let f = DateFormatter()
f.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSZZ" f.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSSSSZZ"

View File

@ -136,7 +136,7 @@ extension NSUserNotificationCenter {
post(title: NSLocalizedString("System Proxy Changed", comment: ""), post(title: NSLocalizedString("System Proxy Changed", comment: ""),
info: NSLocalizedString("Proxy settings are changed by another process. ClashX is no longer the default system proxy.", comment: ""), notiOnly: true) info: NSLocalizedString("Proxy settings are changed by another process. ClashX is no longer the default system proxy.", comment: ""), notiOnly: true)
} }
func postUpdateNotice(msg: String) { func postUpdateNotice(msg: String) {
postNotificationAlert(title: "Update ClashX Meta", info: msg) postNotificationAlert(title: "Update ClashX Meta", info: msg)
} }

View File

@ -26,14 +26,14 @@ class ApiRequest {
private lazy var logQueue = DispatchQueue(label: "com.ClashX.core.log") private lazy var logQueue = DispatchQueue(label: "com.ClashX.core.log")
static let clashRequestQueue = DispatchQueue(label: "com.clashx.clashRequestQueue") static let clashRequestQueue = DispatchQueue(label: "com.clashx.clashRequestQueue")
@objc enum ProviderType: Int { @objc enum ProviderType: Int {
case rule, proxy case rule, proxy
func apiString() -> String { func apiString() -> String {
self == .proxy ? "proxies" : "rules" self == .proxy ? "proxies" : "rules"
} }
func logString() -> String { func logString() -> String {
self == .proxy ? "Proxy" : "Rule" self == .proxy ? "Proxy" : "Rule"
} }
@ -254,9 +254,9 @@ class ApiRequest {
static func getRules(completeHandler: @escaping ([ClashRule]) -> Void) { static func getRules(completeHandler: @escaping ([ClashRule]) -> Void) {
req("/rules").responseData { res in req("/rules").responseData { res in
guard let data = try? res.result.get() else { return } guard let data = try? res.result.get() else { return }
ClashRuleProviderResp.init() ClashRuleProviderResp.init()
let rule = ClashRuleResponse.fromData(data) let rule = ClashRuleResponse.fromData(data)
completeHandler(rule.rules ?? []) completeHandler(rule.rules ?? [])
} }
@ -304,10 +304,10 @@ extension ApiRequest {
extension ApiRequest { extension ApiRequest {
static func updateAllProviders(for type: ProviderType, completeHandler: ((Int) -> Void)? = nil) { static func updateAllProviders(for type: ProviderType, completeHandler: ((Int) -> Void)? = nil) {
var failuresCount = 0 var failuresCount = 0
let group = DispatchGroup() let group = DispatchGroup()
group.enter() group.enter()
if type == .proxy { if type == .proxy {
requestProxyProviderList { resp in requestProxyProviderList { resp in
resp.allProviders.filter { resp.allProviders.filter {
@ -337,15 +337,15 @@ extension ApiRequest {
group.leave() group.leave()
} }
} }
group.notify(queue: .main) { group.notify(queue: .main) {
completeHandler?(failuresCount) completeHandler?(failuresCount)
} }
} }
static func updateProvider(for type: ProviderType, name: String, completeHandler: ((Bool) -> Void)? = nil) { static func updateProvider(for type: ProviderType, name: String, completeHandler: ((Bool) -> Void)? = nil) {
let s = "Update \(type.logString()) Provider" let s = "Update \(type.logString()) Provider"
Logger.log("\(s) \(name)") Logger.log("\(s) \(name)")
req("/providers/\(type.apiString())/\(name)", method: .put).response { req("/providers/\(type.apiString())/\(name)", method: .put).response {
let re = $0.response?.statusCode == 204 let re = $0.response?.statusCode == 204
@ -353,7 +353,7 @@ extension ApiRequest {
completeHandler?(re) completeHandler?(re)
} }
} }
static func requestRuleProviderList(completeHandler: @escaping (ClashRuleProviderResp) -> Void) { static func requestRuleProviderList(completeHandler: @escaping (ClashRuleProviderResp) -> Void) {
req("/providers/rules") req("/providers/rules")
.responseDecodable(of: ClashRuleProviderResp.self, decoder: ClashProviderResp.decoder) { resp in .responseDecodable(of: ClashRuleProviderResp.self, decoder: ClashProviderResp.decoder) { resp in
@ -366,18 +366,18 @@ extension ApiRequest {
} }
} }
} }
static func updateGEO(completeHandler: ((Bool) -> Void)? = nil) { static func updateGEO(completeHandler: ((Bool) -> Void)? = nil) {
Logger.log("UpdateGEO") Logger.log("UpdateGEO")
req("/configs/geo", method: .post).response { req("/configs/geo", method: .post).response {
let re = $0.response?.statusCode == 204 let re = $0.response?.statusCode == 204
completeHandler?(re) completeHandler?(re)
// Logger.log("UpdateGEO \(re ? "success" : "failed")") // Logger.log("UpdateGEO \(re ? "success" : "failed")")
Logger.log("Updating GEO Databases...") Logger.log("Updating GEO Databases...")
} }
} }
static func updateSniffing(enable: Bool, completeHandler: (() -> Void)? = nil) { static func updateSniffing(enable: Bool, completeHandler: (() -> Void)? = nil) {
Logger.log("update sniffing:\(enable)", level: .debug) Logger.log("update sniffing:\(enable)", level: .debug)
req("/configs", req("/configs",
@ -388,7 +388,7 @@ extension ApiRequest {
completeHandler?() completeHandler?()
} }
} }
static func flushFakeipCache(completeHandler: ((Bool) -> Void)? = nil) { static func flushFakeipCache(completeHandler: ((Bool) -> Void)? = nil) {
Logger.log("FlushFakeipCache") Logger.log("FlushFakeipCache")
req("/cache/fakeip/flush", req("/cache/fakeip/flush",

View File

@ -0,0 +1,89 @@
//
// ClashMetaConfig.swift
// ClashX Meta
import Foundation
import Cocoa
import Yams
class ClashMetaConfig: NSObject {
struct Config: Codable {
var externalUI: String? = {
guard let htmlPath = Bundle.main.path(forResource: "index", ofType: "html", inDirectory: "dashboard") else {
return nil
}
return URL(fileURLWithPath: htmlPath).deletingLastPathComponent().path
}()
var externalController = "127.0.0.1:9090"
var secret: String?
var port: Int?
var socksPort: Int?
var mixedPort: Int?
var logLevel = ConfigManager.selectLoggingApiLevel.rawValue
var path: String {
get {
guard let s = try? YAMLEncoder().encode(self),
let path = RemoteConfigManager.createCacheConfig(string: s) else {
assertionFailure("Create init config file failed.")
return ""
}
return path
}
}
enum CodingKeys: String, CodingKey {
case externalController = "external-controller",
externalUI = "external-ui",
mixedPort = "mixed-port",
port,
socksPort = "socks-port",
logLevel = "log-level",
secret
}
mutating func loadDefaultConfigFile() {
let fm = FileManager.default
guard let data = fm.contents(atPath: kDefaultConfigFilePath),
let string = String(data: data, encoding: .utf8),
let yaml = try? Yams.load(yaml: string) as? [String: Any] else {
return
}
let keys = Config.CodingKeys.self
if let ec = yaml[keys.externalController.rawValue] as? String {
externalController = ec
}
if let s = yaml[keys.secret.rawValue] as? String {
secret = s
}
if let port = yaml[keys.mixedPort.rawValue] as? Int {
mixedPort = port
} else {
if let p = yaml[keys.port.rawValue] as? Int {
port = p
}
if let sp = yaml[keys.socksPort.rawValue] as? Int {
socksPort = sp
}
}
if port == nil && mixedPort == nil {
mixedPort = 7890
}
}
}
static func generateInitConfig() -> Config {
var config = Config()
config.loadDefaultConfigFile()
return config
}
}

View File

@ -7,7 +7,7 @@
// //
import Cocoa import Cocoa
//import Sparkle // import Sparkle
class AutoUpgardeManager: NSObject { class AutoUpgardeManager: NSObject {
static let shared = AutoUpgardeManager() static let shared = AutoUpgardeManager()
@ -72,7 +72,7 @@ extension AutoUpgardeManager {
} }
} }
//extension AutoUpgardeManager: SUUpdaterDelegate { // extension AutoUpgardeManager: SUUpdaterDelegate {
// func feedURLString(for updater: SUUpdater) -> String? { // func feedURLString(for updater: SUUpdater) -> String? {
// return current.urlString // return current.urlString
// } // }
@ -80,7 +80,7 @@ extension AutoUpgardeManager {
// func updaterWillRelaunchApplication(_ updater: SUUpdater) { // func updaterWillRelaunchApplication(_ updater: SUUpdater) {
// SystemProxyManager.shared.disableProxy(port: 0, socksPort: 0, forceDisable: true) // SystemProxyManager.shared.disableProxy(port: 0, socksPort: 0, forceDisable: true)
// } // }
//} // }
// MARK: - Channel Enum // MARK: - Channel Enum

View File

@ -9,7 +9,7 @@ class ClashResourceManager {
case geosite = "geosite.dat" case geosite = "geosite.dat"
case geoip = "geoip.dat" case geoip = "geoip.dat"
} }
static func check() -> Bool { static func check() -> Bool {
checkConfigDir() checkConfigDir()
checkMMDB() checkMMDB()
@ -34,7 +34,7 @@ class ClashResourceManager {
checkRule(.geoip) checkRule(.geoip)
checkRule(.geosite) checkRule(.geosite)
} }
static func checkRule(_ file: RuleFiles) { static func checkRule(_ file: RuleFiles) {
let fileManage = FileManager.default let fileManage = FileManager.default
let destPath = "\(kConfigFolderPath)/\(file.rawValue)" let destPath = "\(kConfigFolderPath)/\(file.rawValue)"

View File

@ -18,20 +18,20 @@ class MenuItemFactory {
UserDefaults.standard.set(useViewToRenderProxy, forKey: "useViewToRenderProxy") UserDefaults.standard.set(useViewToRenderProxy, forKey: "useViewToRenderProxy")
} }
} }
static var hideUnselectable: Int = UserDefaults.standard.object(forKey: "hideUnselectable") as? Int ?? NSControl.StateValue.off.rawValue { static var hideUnselectable: Int = UserDefaults.standard.object(forKey: "hideUnselectable") as? Int ?? NSControl.StateValue.off.rawValue {
didSet { didSet {
UserDefaults.standard.set(hideUnselectable, forKey: "hideUnselectable") UserDefaults.standard.set(hideUnselectable, forKey: "hideUnselectable")
recreateProxyMenuItems() recreateProxyMenuItems()
} }
} }
static var useAlphaCore: Bool = UserDefaults.standard.object(forKey: "useAlphaCore") as? Bool ?? false { static var useAlphaCore: Bool = UserDefaults.standard.object(forKey: "useAlphaCore") as? Bool ?? false {
didSet { didSet {
UserDefaults.standard.set(useAlphaCore, forKey: "useAlphaCore") UserDefaults.standard.set(useAlphaCore, forKey: "useAlphaCore")
} }
} }
static let updateAllProvidersTitle = "Update All Providers" static let updateAllProvidersTitle = "Update All Providers"
// MARK: - Public // MARK: - Public
@ -58,7 +58,7 @@ class MenuItemFactory {
refreshMenuItems(mergedData: proxyInfo) refreshMenuItems(mergedData: proxyInfo)
} }
} }
static func recreateRuleProvidersMenuItems() { static func recreateRuleProvidersMenuItems() {
ApiRequest.requestRuleProviderList { ApiRequest.requestRuleProviderList {
refreshRuleProviderMenuItems($0.allProviders.map({ $0.value })) refreshRuleProviderMenuItems($0.allProviders.map({ $0.value }))
@ -68,9 +68,9 @@ class MenuItemFactory {
static func refreshMenuItems(mergedData proxyInfo: ClashProxyResp?) { static func refreshMenuItems(mergedData proxyInfo: ClashProxyResp?) {
let leftPadding = AppDelegate.shared.hasMenuSelected() let leftPadding = AppDelegate.shared.hasMenuSelected()
guard let proxyInfo = proxyInfo else { return } guard let proxyInfo = proxyInfo else { return }
let hideState = NSControl.StateValue(rawValue: hideUnselectable) let hideState = NSControl.StateValue(rawValue: hideUnselectable)
var menuItems = [NSMenuItem]() var menuItems = [NSMenuItem]()
var collapsedItems = [NSMenuItem]() var collapsedItems = [NSMenuItem]()
@ -89,7 +89,7 @@ class MenuItemFactory {
guard let menu = menu else { guard let menu = menu else {
continue continue
} }
switch hideState { switch hideState {
case .mixed where [.urltest, .fallback, .loadBalance, .relay].contains(proxy.type): case .mixed where [.urltest, .fallback, .loadBalance, .relay].contains(proxy.type):
collapsedItems.append(menu) collapsedItems.append(menu)
@ -101,19 +101,19 @@ class MenuItemFactory {
menu.isEnabled = true menu.isEnabled = true
} }
} }
if hideState == .mixed { if hideState == .mixed {
let collapsedItem = NSMenuItem(title: "Collapsed", action: nil, keyEquivalent: "") let collapsedItem = NSMenuItem(title: "Collapsed", action: nil, keyEquivalent: "")
collapsedItem.isEnabled = true collapsedItem.isEnabled = true
collapsedItem.submenu = .init(title: "") collapsedItem.submenu = .init(title: "")
collapsedItem.submenu?.items = collapsedItems collapsedItem.submenu?.items = collapsedItems
menuItems.append(collapsedItem) menuItems.append(collapsedItem)
} }
let items = Array(menuItems.reversed()) let items = Array(menuItems.reversed())
updateProxyList(withMenus: items) updateProxyList(withMenus: items)
refreshProxyProviderMenuItems(mergedData: proxyInfo) refreshProxyProviderMenuItems(mergedData: proxyInfo)
recreateRuleProvidersMenuItems() recreateRuleProvidersMenuItems()
} }
@ -301,11 +301,10 @@ extension MenuItemFactory {
} }
} }
// MARK: - Meta // MARK: - Meta
extension MenuItemFactory { extension MenuItemFactory {
static func refreshProxyProviderMenuItems(mergedData proxyInfo: ClashProxyResp?) { static func refreshProxyProviderMenuItems(mergedData proxyInfo: ClashProxyResp?) {
let app = AppDelegate.shared let app = AppDelegate.shared
guard let proxyInfo = proxyInfo, guard let proxyInfo = proxyInfo,
@ -316,7 +315,7 @@ extension MenuItemFactory {
let proxyProviders = providers.allProviders.filter { let proxyProviders = providers.allProviders.filter {
$0.value.vehicleType == .HTTP $0.value.vehicleType == .HTTP
}.values.sorted(by: { $0.name < $1.name }) }.values.sorted(by: { $0.name < $1.name })
let isEmpty = proxyProviders.count == 0 let isEmpty = proxyProviders.count == 0
app.proxyProvidersMenuItem.isEnabled = !isEmpty app.proxyProvidersMenuItem.isEnabled = !isEmpty
guard !isEmpty else { return } guard !isEmpty else { return }
@ -334,16 +333,16 @@ extension MenuItemFactory {
menu.addItem(item) menu.addItem(item)
} }
} }
static func refreshRuleProviderMenuItems(_ ruleProviders: [ClashRuleProvider]) { static func refreshRuleProviderMenuItems(_ ruleProviders: [ClashRuleProvider]) {
let app = AppDelegate.shared let app = AppDelegate.shared
let isEmpty = ruleProviders.count == 0 let isEmpty = ruleProviders.count == 0
app.ruleProvidersMenuItem.isEnabled = !isEmpty app.ruleProvidersMenuItem.isEnabled = !isEmpty
guard !isEmpty, guard !isEmpty,
let menu = app.ruleProvidersMenu let menu = app.ruleProvidersMenu
else { return } else { return }
initUpdateAllProvidersMenuItem(for: menu, type: .rule) initUpdateAllProvidersMenuItem(for: menu, type: .rule)
let maxNameLength = maxProvidersLength(for: ruleProviders.map({ $0.name })) let maxNameLength = maxProvidersLength(for: ruleProviders.map({ $0.name }))
ruleProviders.sorted(by: { $0.name < $1.name }) ruleProviders.sorted(by: { $0.name < $1.name })
@ -358,7 +357,7 @@ extension MenuItemFactory {
menu.addItem(item) menu.addItem(item)
} }
} }
static func initUpdateAllProvidersMenuItem(for menu: NSMenu, type: ApiRequest.ProviderType) { static func initUpdateAllProvidersMenuItem(for menu: NSMenu, type: ApiRequest.ProviderType) {
if menu.items.count > 1 { if menu.items.count > 1 {
menu.items.enumerated().filter { menu.items.enumerated().filter {
@ -374,7 +373,7 @@ extension MenuItemFactory {
menu.addItem(.separator()) menu.addItem(.separator())
} }
} }
static func maxProvidersLength(for names: [String]) -> CGFloat { static func maxProvidersLength(for names: [String]) -> CGFloat {
func getLength(_ string: String) -> CGFloat { func getLength(_ string: String) -> CGFloat {
let rect = CGSize(width: CGFloat.greatestFiniteMagnitude, height: 20) let rect = CGSize(width: CGFloat.greatestFiniteMagnitude, height: 20)
@ -385,30 +384,30 @@ extension MenuItemFactory {
attributes: attr).width attributes: attr).width
return length return length
} }
var lengths = names.map { var lengths = names.map {
getLength($0) + 65 getLength($0) + 65
} }
lengths.append(getLength(updateAllProvidersTitle)) lengths.append(getLength(updateAllProvidersTitle))
return lengths.max() ?? 0 return lengths.max() ?? 0
} }
static func providerUpdateTitle(_ updatedAt: String?) -> String? { static func providerUpdateTitle(_ updatedAt: String?) -> String? {
let dateCF = DateComponentsFormatter() let dateCF = DateComponentsFormatter()
dateCF.allowedUnits = [.day, .hour, .minute] dateCF.allowedUnits = [.day, .hour, .minute]
dateCF.maximumUnitCount = 1 dateCF.maximumUnitCount = 1
dateCF.unitsStyle = .abbreviated dateCF.unitsStyle = .abbreviated
dateCF.zeroFormattingBehavior = .dropAll dateCF.zeroFormattingBehavior = .dropAll
guard let dateStr = updatedAt, guard let dateStr = updatedAt,
let date = DateFormatter.provider.date(from: dateStr), let date = DateFormatter.provider.date(from: dateStr),
!date.timeIntervalSinceNow.isNaN, !date.timeIntervalSinceNow.isNaN,
!date.timeIntervalSinceNow.isInfinite, !date.timeIntervalSinceNow.isInfinite,
let re = dateCF.string(from: abs(date.timeIntervalSinceNow)) else { return nil } let re = dateCF.string(from: abs(date.timeIntervalSinceNow)) else { return nil }
return "\(re) ago" return "\(re) ago"
} }
@objc static func actionUpdateAllProviders(sender: NSMenuItem) { @objc static func actionUpdateAllProviders(sender: NSMenuItem) {
let type = ApiRequest.ProviderType(rawValue: sender.tag)! let type = ApiRequest.ProviderType(rawValue: sender.tag)!
let s = "Update All \(type.logString()) Providers" let s = "Update All \(type.logString()) Providers"
@ -420,11 +419,11 @@ extension MenuItemFactory {
recreateProxyMenuItems() recreateProxyMenuItems()
} }
} }
@objc static func actionUpdateSelectProvider(sender: DualTitleMenuItem) { @objc static func actionUpdateSelectProvider(sender: DualTitleMenuItem) {
let name = sender.originTitle let name = sender.originTitle
let type = ApiRequest.ProviderType(rawValue: sender.tag)! let type = ApiRequest.ProviderType(rawValue: sender.tag)!
let log = "Update \(type.logString()) Provider \(name)" let log = "Update \(type.logString()) Provider \(name)"
Logger.log(log) Logger.log(log)
ApiRequest.updateProvider(for: type, name: name) { ApiRequest.updateProvider(for: type, name: name) {

View File

@ -14,7 +14,7 @@ import RxCocoa
class PrivilegedHelperManager { class PrivilegedHelperManager {
let isHelperCheckFinished = BehaviorRelay<Bool>(value: false) let isHelperCheckFinished = BehaviorRelay<Bool>(value: false)
private var cancelInstallCheck = false private var cancelInstallCheck = false
private let useLegacyInstall = true private let useLegacyInstall = true
private var authRef: AuthorizationRef? private var authRef: AuthorizationRef?

View File

@ -13,6 +13,8 @@ class RemoteConfigManager {
var configs: [RemoteConfigModel] = [] var configs: [RemoteConfigModel] = []
var refreshActivity: NSBackgroundActivityScheduler? var refreshActivity: NSBackgroundActivityScheduler?
let verifyConfigTask = MetaTask()
static let shared = RemoteConfigManager() static let shared = RemoteConfigManager()
private init() { private init() {
@ -205,29 +207,28 @@ class RemoteConfigManager {
} }
} }
static func verifyConfig(string: String) -> ErrorString? { static func createCacheConfig(string: String) -> String? {
var res: String?
let path = NSTemporaryDirectory().appending("com.MetaCubeX.ClashX.meta") let path = NSTemporaryDirectory().appending("com.MetaCubeX.ClashX.meta")
let confPath = path + "/\(UUID().uuidString).yaml" let confPath = path + "/\(UUID().uuidString).yaml"
let fm = FileManager.default let fm = FileManager.default
try? fm.createDirectory(atPath: path, withIntermediateDirectories: true) try? fm.createDirectory(atPath: path, withIntermediateDirectories: true)
if fm.fileExists(atPath: confPath) {
try? fm.removeItem(atPath: confPath)
}
guard fm.createFile(atPath: confPath, contents: string.data(using: .utf8)) else { guard fm.createFile(atPath: confPath, contents: string.data(using: .utf8)) else {
return nil
}
return confPath
}
static func verifyConfig(string: String) -> ErrorString? {
guard let confPath = createCacheConfig(string: string) else {
return "Create verify config file failed" return "Create verify config file failed"
} }
return RemoteConfigManager.shared.verifyConfigTask.test(kConfigFolderPath, confFilePath: confPath)
let queue = DispatchGroup()
queue.enter()
PrivilegedHelperManager.shared.helper {
res = "unknown error"
queue.leave()
}?.verifyMeta(withConfPath: kConfigFolderPath, confFilePath: "", result: {
res = $0
queue.leave()
})
queue.wait()
return res
} }
static func showAdd() { static func showAdd() {

View File

@ -19,11 +19,11 @@ struct Paths {
static func configFileName(for name: String) -> String { static func configFileName(for name: String) -> String {
return "\(name).yaml" return "\(name).yaml"
} }
static func defaultCorePath() -> String? { static func defaultCorePath() -> String? {
Bundle.main.path(forResource: "com.metacubex.ClashX.ProxyConfigHelper.meta", ofType: nil) Bundle.main.path(forResource: "com.metacubex.ClashX.ProxyConfigHelper.meta", ofType: nil)
} }
static func alphaCorePath() -> URL? { static func alphaCorePath() -> URL? {
FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask) FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)
.first? .first?

View File

@ -39,7 +39,7 @@ class ClashConfig: Codable {
var mixedPort: Int var mixedPort: Int
var mode: ClashProxyMode var mode: ClashProxyMode
var logLevel: ClashLogLevel var logLevel: ClashLogLevel
var sniffing: Bool var sniffing: Bool
var usedHttpPort: Int { var usedHttpPort: Int {

View File

@ -2,9 +2,11 @@
## 配置文件需要放置在 $HOME/.config/clash/*.yaml ## 配置文件需要放置在 $HOME/.config/clash/*.yaml
## 这份文件是clashX的基础配置文件请尽量新建配置文件进行修改。 ## 这份文件是clashX的基础配置文件请尽量新建配置文件进行修改。
## 只有这份文件的端口设置会随ClashX启动生效
## 如果您不知道如何操作,请参阅 官方Github文档 https://github.com/Dreamacro/clash/blob/dev/README.md ## 如果您不知道如何操作,请参阅
## Clash Meta wiki https://docs.metacubex.one
## Clash Meta Github文档 https://github.com/MetaCubeX/Clash.Meta/blob/Alpha/README.md
#---------------------------------------------------# #---------------------------------------------------#
# (HTTP and SOCKS5 in one port) # (HTTP and SOCKS5 in one port)
@ -14,11 +16,18 @@ external-controller: 127.0.0.1:9090
allow-lan: false allow-lan: false
mode: rule mode: rule
log-level: warning log-level: warning
ipv6: false
proxies: proxies:
proxy-groups: proxy-groups:
proxy-providers:
rule-providers:
rules: rules:
- DOMAIN-SUFFIX,google.com,DIRECT - DOMAIN-SUFFIX,google.com,DIRECT
- DOMAIN-KEYWORD,google,DIRECT - DOMAIN-KEYWORD,google,DIRECT

View File

@ -2,11 +2,10 @@
// DualTitleMenuItem.swift // DualTitleMenuItem.swift
// ClashX // ClashX
import Cocoa import Cocoa
class DualTitleMenuItem: NSMenuItem { class DualTitleMenuItem: NSMenuItem {
var originTitle: String = "" var originTitle: String = ""
convenience init(_ title: String, convenience init(_ title: String,
@ -17,7 +16,7 @@ class DualTitleMenuItem: NSMenuItem {
originTitle = title originTitle = title
setAttributedTitle(name: title, secondLabel: subTitle, maxLength: maxLength) setAttributedTitle(name: title, secondLabel: subTitle, maxLength: maxLength)
} }
func setAttributedTitle(name: String, secondLabel: String?, maxLength: CGFloat = 0) { func setAttributedTitle(name: String, secondLabel: String?, maxLength: CGFloat = 0) {
let paragraph = NSMutableParagraphStyle() let paragraph = NSMutableParagraphStyle()
paragraph.tabStops = [ paragraph.tabStops = [

View File

@ -28,5 +28,7 @@ target 'ClashX Meta' do
pod 'Starscream','3.1.1' pod 'Starscream','3.1.1'
pod "FlexibleDiff" pod "FlexibleDiff"
pod 'GzipSwift' pod 'GzipSwift'
pod 'Yams'
pod "PromiseKit", "~> 6.8"
end end

View File

@ -6,6 +6,13 @@ PODS:
- FlexibleDiff (0.0.9) - FlexibleDiff (0.0.9)
- GzipSwift (5.1.1) - GzipSwift (5.1.1)
- LetsMove (1.25) - LetsMove (1.25)
- PromiseKit (6.18.0):
- PromiseKit/CorePromise (= 6.18.0)
- PromiseKit/Foundation (= 6.18.0)
- PromiseKit/UIKit (= 6.18.0)
- PromiseKit/CorePromise (6.18.0)
- PromiseKit/Foundation (6.18.0):
- PromiseKit/CorePromise
- RxCocoa (6.5.0): - RxCocoa (6.5.0):
- RxRelay (= 6.5.0) - RxRelay (= 6.5.0)
- RxSwift (= 6.5.0) - RxSwift (= 6.5.0)
@ -15,6 +22,7 @@ PODS:
- Starscream (3.1.1) - Starscream (3.1.1)
- SwiftyJSON (5.0.1) - SwiftyJSON (5.0.1)
- WebViewJavascriptBridge (6.0.3) - WebViewJavascriptBridge (6.0.3)
- Yams (5.0.0)
DEPENDENCIES: DEPENDENCIES:
- Alamofire (~> 5.0) - Alamofire (~> 5.0)
@ -22,11 +30,13 @@ DEPENDENCIES:
- FlexibleDiff - FlexibleDiff
- GzipSwift - GzipSwift
- LetsMove - LetsMove
- PromiseKit (~> 6.8)
- RxCocoa - RxCocoa
- RxSwift - RxSwift
- Starscream (= 3.1.1) - Starscream (= 3.1.1)
- SwiftyJSON - SwiftyJSON
- WebViewJavascriptBridge - WebViewJavascriptBridge
- Yams
SPEC REPOS: SPEC REPOS:
trunk: trunk:
@ -35,12 +45,14 @@ SPEC REPOS:
- FlexibleDiff - FlexibleDiff
- GzipSwift - GzipSwift
- LetsMove - LetsMove
- PromiseKit
- RxCocoa - RxCocoa
- RxRelay - RxRelay
- RxSwift - RxSwift
- Starscream - Starscream
- SwiftyJSON - SwiftyJSON
- WebViewJavascriptBridge - WebViewJavascriptBridge
- Yams
SPEC CHECKSUMS: SPEC CHECKSUMS:
Alamofire: 87bd8c952f9a4454320fce00d9cc3de57bcadaf5 Alamofire: 87bd8c952f9a4454320fce00d9cc3de57bcadaf5
@ -48,13 +60,15 @@ SPEC CHECKSUMS:
FlexibleDiff: b9ee9b8305b42c784f5dd40589203c97c55bbaa0 FlexibleDiff: b9ee9b8305b42c784f5dd40589203c97c55bbaa0
GzipSwift: 893f3e48e597a1a4f62fafcb6514220fcf8287fa GzipSwift: 893f3e48e597a1a4f62fafcb6514220fcf8287fa
LetsMove: 7b9fe44737707d984fbd3f47af46609a9a07b461 LetsMove: 7b9fe44737707d984fbd3f47af46609a9a07b461
PromiseKit: 879fa89b55fcb8f11ade9aad0c9721850bb71b9f
RxCocoa: 94f817b71c07517321eb4f9ad299112ca8af743b RxCocoa: 94f817b71c07517321eb4f9ad299112ca8af743b
RxRelay: 1de1523e604c72b6c68feadedd1af3b1b4d0ecbd RxRelay: 1de1523e604c72b6c68feadedd1af3b1b4d0ecbd
RxSwift: 5710a9e6b17f3c3d6e40d6e559b9fa1e813b2ef8 RxSwift: 5710a9e6b17f3c3d6e40d6e559b9fa1e813b2ef8
Starscream: 4bb2f9942274833f7b4d296a55504dcfc7edb7b0 Starscream: 4bb2f9942274833f7b4d296a55504dcfc7edb7b0
SwiftyJSON: 2f33a42c6fbc52764d96f13368585094bfd8aa5e SwiftyJSON: 2f33a42c6fbc52764d96f13368585094bfd8aa5e
WebViewJavascriptBridge: 7f5bc4d3581e672e8f32bd0f812d54bc69bb8e29 WebViewJavascriptBridge: 7f5bc4d3581e672e8f32bd0f812d54bc69bb8e29
Yams: b5c3e188ceeb8321a9830b1d0e791ccea9af3201
PODFILE CHECKSUM: f4571fd419858d0946fa886653353d5b9e10a822 PODFILE CHECKSUM: 57cda85d5fd198c1d1f83f6a71b69a86b1884bb5
COCOAPODS: 1.11.3 COCOAPODS: 1.11.3

View File

@ -9,9 +9,9 @@
<key>CFBundleName</key> <key>CFBundleName</key>
<string>com.metacubex.ClashX.ProxyConfigHelper</string> <string>com.metacubex.ClashX.ProxyConfigHelper</string>
<key>CFBundleShortVersionString</key> <key>CFBundleShortVersionString</key>
<string>1.3</string> <string>1.4</string>
<key>CFBundleVersion</key> <key>CFBundleVersion</key>
<string>4</string> <string>5</string>
<key>SMAuthorizedClients</key> <key>SMAuthorizedClients</key>
<array> <array>
<string>anchor apple generic and identifier &quot;com.metacubex.ClashX.ProxyConfigHelper&quot; 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> <string>anchor apple generic and identifier &quot;com.metacubex.ClashX.ProxyConfigHelper&quot; 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>

View File

@ -8,7 +8,7 @@ import Cocoa
class MetaTask: NSObject { class MetaTask: NSObject {
struct MetaServer: Encodable { struct MetaServer: Encodable {
let externalController: String var externalController: String
let secret: String let secret: String
var log: String = "" var log: String = ""
@ -29,7 +29,6 @@ class MetaTask: NSObject {
} }
let proc = Process() let proc = Process()
var uiPath: String?
let procQueue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".MetaProcess") let procQueue = DispatchQueue(label: Bundle.main.bundleIdentifier! + ".MetaProcess")
var timer: DispatchSourceTimer? var timer: DispatchSourceTimer?
@ -39,10 +38,6 @@ class MetaTask: NSObject {
proc.executableURL = .init(fileURLWithPath: path) proc.executableURL = .init(fileURLWithPath: path)
} }
@objc func setUIPath(_ path: String) {
uiPath = path
}
@objc func start(_ confPath: String, @objc func start(_ confPath: String,
confFilePath: String, confFilePath: String,
result: @escaping stringReplyBlock) { result: @escaping stringReplyBlock) {
@ -71,13 +66,6 @@ class MetaTask: NSObject {
]) ])
} }
if let uiPath = uiPath {
args.append(contentsOf: [
"-ext-ui",
uiPath
])
}
killOldProc() killOldProc()
procQueue.async { procQueue.async {
@ -94,6 +82,17 @@ class MetaTask: NSObject {
return return
} }
let port = serverResult.externalController.components(separatedBy: ":").last ?? "9090"
if let p = Int(port) {
let newPort = self.updateExternalControllerPort(p)
let ec = "127.0.0.1:\(newPort)"
args.append(contentsOf: [
"-ext-ctl",
ec
])
serverResult.externalController = ec
}
self.proc.arguments = args self.proc.arguments = args
let pipe = Pipe() let pipe = Pipe()
var logs = [String]() var logs = [String]()
@ -276,7 +275,7 @@ class MetaTask: NSObject {
return (Int32(pid) ?? 0, addr) return (Int32(pid) ?? 0, addr)
} }
func findExternalControllerPort(_ port: Int) -> Int { func updateExternalControllerPort(_ port: Int) -> Int {
let proc = Process() let proc = Process()
let pipe = Pipe() let pipe = Pipe()
proc.standardOutput = pipe proc.standardOutput = pipe

View File

@ -79,6 +79,7 @@ ProxyConfigRemoteProcessProtocol
[weakSelf.connections removeObject:weakConnection]; [weakSelf.connections removeObject:weakConnection];
if (weakSelf.connections.count == 0) { if (weakSelf.connections.count == 0) {
weakSelf.shouldQuit = YES; weakSelf.shouldQuit = YES;
[weakSelf.metaTask stop];
} }
}; };
[self.connections addObject:newConnection]; [self.connections addObject:newConnection];
@ -140,10 +141,6 @@ ProxyConfigRemoteProcessProtocol
} }
- (void)metaSetUIPath:(NSString *)path {
[self.metaTask setUIPath:path];
}
- (void)startMetaWithConfPath:(NSString *)confPath ConfFilePath:(NSString *)confFilePath result:(stringReplyBlock)reply { - (void)startMetaWithConfPath:(NSString *)confPath ConfFilePath:(NSString *)confFilePath result:(stringReplyBlock)reply {
[self.metaTask start:confPath confFilePath:confFilePath result:reply]; [self.metaTask start:confPath confFilePath:confFilePath result:reply];
} }

View File

@ -17,8 +17,6 @@ typedef void(^dictReplyBlock)(NSDictionary *);
- (void)initMetaCoreWithPath:(NSString *)path; - (void)initMetaCoreWithPath:(NSString *)path;
- (void)metaSetUIPath:(NSString *)path;
- (void)startMetaWithConfPath:(NSString *)confPath - (void)startMetaWithConfPath:(NSString *)confPath
ConfFilePath:(NSString *)confFilePath ConfFilePath:(NSString *)confFilePath
result:(stringReplyBlock)reply; result:(stringReplyBlock)reply;