Skip to content

Commit 29ce285

Browse files
committed
Merge branch 'meta-dev' into meta
2 parents 1028220 + 865f14f commit 29ce285

12 files changed

Lines changed: 497 additions & 159 deletions

File tree

ClashX.xcodeproj/project.pbxproj

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1225,6 +1225,7 @@
12251225
CURRENT_PROJECT_VERSION = 0;
12261226
DEAD_CODE_STRIPPING = YES;
12271227
DEVELOPMENT_TEAM = "";
1228+
DRIVERKIT_DEPLOYMENT_TARGET = "$(RECOMMENDED_DRIVERKIT_DEPLOYMENT_TARGET)";
12281229
ENABLE_HARDENED_RUNTIME = YES;
12291230
FRAMEWORK_SEARCH_PATHS = (
12301231
"$(inherited)",
@@ -1272,6 +1273,7 @@
12721273
DEAD_CODE_STRIPPING = YES;
12731274
DEPLOYMENT_POSTPROCESSING = YES;
12741275
DEVELOPMENT_TEAM = "";
1276+
DRIVERKIT_DEPLOYMENT_TARGET = "$(RECOMMENDED_DRIVERKIT_DEPLOYMENT_TARGET)";
12751277
ENABLE_HARDENED_RUNTIME = YES;
12761278
FRAMEWORK_SEARCH_PATHS = (
12771279
"$(inherited)",
@@ -1443,6 +1445,7 @@
14431445
CURRENT_PROJECT_VERSION = 3;
14441446
DEAD_CODE_STRIPPING = YES;
14451447
DEVELOPMENT_TEAM = "";
1448+
DRIVERKIT_DEPLOYMENT_TARGET = "$(RECOMMENDED_DRIVERKIT_DEPLOYMENT_TARGET)";
14461449
ENABLE_HARDENED_RUNTIME = YES;
14471450
INFOPLIST_FILE = "$(SRCROOT)/ProxyConfigHelper/Helper-Info.plist";
14481451
LD_RUNPATH_SEARCH_PATHS = (
@@ -1451,7 +1454,7 @@
14511454
"@loader_path/../Frameworks",
14521455
);
14531456
LIBRARY_SEARCH_PATHS = "$(inherited)";
1454-
MACOSX_DEPLOYMENT_TARGET = 11.0;
1457+
MACOSX_DEPLOYMENT_TARGET = 12.4;
14551458
MARKETING_VERSION = 1.2;
14561459
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
14571460
MTL_FAST_MATH = YES;
@@ -1487,6 +1490,7 @@
14871490
DEAD_CODE_STRIPPING = YES;
14881491
DEPLOYMENT_POSTPROCESSING = YES;
14891492
DEVELOPMENT_TEAM = "";
1493+
DRIVERKIT_DEPLOYMENT_TARGET = "$(RECOMMENDED_DRIVERKIT_DEPLOYMENT_TARGET)";
14901494
ENABLE_HARDENED_RUNTIME = YES;
14911495
INFOPLIST_FILE = "$(SRCROOT)/ProxyConfigHelper/Helper-Info.plist";
14921496
LD_RUNPATH_SEARCH_PATHS = (
@@ -1495,7 +1499,7 @@
14951499
"@loader_path/../Frameworks",
14961500
);
14971501
LIBRARY_SEARCH_PATHS = "$(inherited)";
1498-
MACOSX_DEPLOYMENT_TARGET = 11.0;
1502+
MACOSX_DEPLOYMENT_TARGET = 12.4;
14991503
MARKETING_VERSION = 1.2;
15001504
MTL_FAST_MATH = YES;
15011505
OTHER_LDFLAGS = (

ClashX/AppDelegate.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,7 +89,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
8989
// setup menu item first
9090
statusItem = NSStatusBar.system.statusItem(withLength: statusItemLengthWithSpeed)
9191
statusItemView = await StatusItemView.create(statusItem: statusItem)
92-
statusItemView.updateSize(width: statusItemLengthWithSpeed)
92+
statusItemView.updateSize(statusItem, width: statusItemLengthWithSpeed)
9393
statusMenu.delegate = self
9494
setupStatusMenuItemData()
9595

@@ -206,7 +206,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
206206
self.showNetSpeedIndicatorMenuItem.state = (show ?? true) ? .on : .off
207207
let statusItemLength: CGFloat = (show ?? true) ? statusItemLengthWithSpeed : 25
208208
self.statusItem.length = statusItemLength
209-
self.statusItemView.updateSize(width: statusItemLength)
209+
self.statusItemView.updateSize(self.statusItem, width: statusItemLength)
210210
self.statusItemView.showSpeedContainer(show: show ?? true)
211211
}.disposed(by: disposeBag)
212212

ClashX/Base.lproj/Main.storyboard

Lines changed: 35 additions & 35 deletions
Large diffs are not rendered by default.

ClashX/General/ClashProcess.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,4 +484,37 @@ actor ClashProcess {
484484
return "\(error)"
485485
}
486486
}
487+
488+
// MARK: age decrypt
489+
490+
static func ageDecrypt(key: String, inputPath: String, outputPath: String) -> String? {
491+
do {
492+
let proc = Process()
493+
proc.executableURL = Paths.defaultCorePath()
494+
proc.arguments = [
495+
"age",
496+
"decrypt",
497+
key,
498+
inputPath,
499+
outputPath
500+
]
501+
502+
let pipe = Pipe()
503+
proc.standardOutput = pipe
504+
proc.standardError = pipe
505+
506+
try proc.run()
507+
proc.waitUntilExit()
508+
509+
guard proc.terminationStatus == 0 else {
510+
let data = pipe.fileHandleForReading.readDataToEndOfFile()
511+
let output = String(data: data, encoding: .utf8) ?? ""
512+
return "Decrypt failed, status \(proc.terminationStatus): \(output)"
513+
}
514+
515+
return nil
516+
} catch {
517+
return "\(error)"
518+
}
519+
}
487520
}

ClashX/General/Managers/RemoteConfigManager.swift

Lines changed: 37 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,16 @@ class RemoteConfigManager {
163163

164164
static func updateConfig(config: RemoteConfigModel) async -> String? {
165165
let (configString, suggestedFilename) = await getRemoteConfigData(config: config)
166-
guard let newConfig = configString else {
166+
guard let configStr = configString else {
167167
return NSLocalizedString("Download fail", comment: "")
168168
}
169-
169+
170+
let (newConfig, error) = decryptConfig(string: configStr, config: config)
171+
172+
guard let newConfig, error == nil else {
173+
return NSLocalizedString("Decrypt config fail", comment: "") + ", " + "\(error ?? "unknown")"
174+
}
175+
170176
let verifyRes = verifyConfig(string: newConfig)
171177
if let error = verifyRes {
172178
return NSLocalizedString("Remote Config Format Error", comment: "") + ": " + error
@@ -228,9 +234,37 @@ class RemoteConfigManager {
228234
guard let confPath = createCacheConfig(string: string) else {
229235
return "Create verify config file failed"
230236
}
231-
237+
232238
return ClashProcess.verify(kConfigFolderPath, confFilePath: confPath)
233239
}
240+
241+
static func decryptConfig(string: String, config: RemoteConfigModel) -> (config: String?, error: String?) {
242+
guard let key = config.ageSecretKey else {
243+
return (string, nil)
244+
}
245+
246+
guard let confPath = createCacheConfig(string: string) else {
247+
return (nil, "Create verify config file failed")
248+
}
249+
250+
let agePath = confPath + ".age"
251+
do {
252+
try FileManager.default.moveItem(atPath: confPath, toPath: agePath)
253+
} catch {
254+
return (nil, NSLocalizedString("Create age config file failed", comment: "") + ", \(error.localizedDescription)")
255+
}
256+
257+
if let error = ClashProcess.ageDecrypt(key: key, inputPath: agePath, outputPath: confPath) {
258+
return (nil, error)
259+
}
260+
261+
if let data = FileManager.default.contents(atPath: confPath),
262+
let newStr = String(data: data, encoding: .utf8) {
263+
return (newStr, nil)
264+
} else {
265+
return (nil, NSLocalizedString("Load decrypted config failed", comment: ""))
266+
}
267+
}
234268

235269
static func showAdd() {
236270
let alertView = NSAlert()

ClashX/Models/RemoteConfigModel.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,17 @@ class RemoteConfigModel: Codable {
1414
var updateTime: Date?
1515
var updating = false
1616
var isPlaceHolderName = false
17+
var ageSecretKey: String?
1718

18-
init(url: String, name: String, updateTime: Date? = nil) {
19+
init(url: String, name: String, updateTime: Date? = nil, ageSecretKey: String? = nil) {
1920
self.url = url
2021
self.name = name
2122
self.updateTime = updateTime
23+
self.ageSecretKey = ageSecretKey
2224
}
2325

2426
private enum CodingKeys: String, CodingKey {
25-
case url, name, updateTime
27+
case url, name, updateTime, ageSecretKey
2628
}
2729

2830
func displayingTimeString() -> String {

ClashX/Support Files/Localizable.xcstrings

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -526,6 +526,28 @@
526526
}
527527
}
528528
},
529+
"Create age config file failed" : {
530+
"localizations" : {
531+
"en" : {
532+
"stringUnit" : {
533+
"state" : "translated",
534+
"value" : "Create age config file failed"
535+
}
536+
},
537+
"zh-Hans" : {
538+
"stringUnit" : {
539+
"state" : "translated",
540+
"value" : "创建 age 配置文件失败"
541+
}
542+
},
543+
"zh-Hant" : {
544+
"stringUnit" : {
545+
"state" : "translated",
546+
"value" : "建立 age 配置檔案失敗"
547+
}
548+
}
549+
}
550+
},
529551
"Current SSID is in the auto-suspend list." : {
530552
"localizations" : {
531553
"zh-Hans" : {
@@ -582,6 +604,28 @@
582604
}
583605
}
584606
},
607+
"Decrypt config fail" : {
608+
"localizations" : {
609+
"en" : {
610+
"stringUnit" : {
611+
"state" : "translated",
612+
"value" : "Decrypt config fail"
613+
}
614+
},
615+
"zh-Hans" : {
616+
"stringUnit" : {
617+
"state" : "translated",
618+
"value" : "解密配置失败"
619+
}
620+
},
621+
"zh-Hant" : {
622+
"stringUnit" : {
623+
"state" : "translated",
624+
"value" : "解密配置失敗"
625+
}
626+
}
627+
}
628+
},
585629
"Destination IP" : {
586630
"extractionState" : "manual",
587631
"localizations" : {
@@ -1325,6 +1369,28 @@
13251369
}
13261370
}
13271371
},
1372+
"Load decrypted config failed" : {
1373+
"localizations" : {
1374+
"en" : {
1375+
"stringUnit" : {
1376+
"state" : "translated",
1377+
"value" : "Load decrypted config failed"
1378+
}
1379+
},
1380+
"zh-Hans" : {
1381+
"stringUnit" : {
1382+
"state" : "translated",
1383+
"value" : "加载解密后的配置失败"
1384+
}
1385+
},
1386+
"zh-Hant" : {
1387+
"stringUnit" : {
1388+
"state" : "translated",
1389+
"value" : "載入解密後的配置失敗"
1390+
}
1391+
}
1392+
}
1393+
},
13281394
"Log" : {
13291395
"extractionState" : "manual",
13301396
"localizations" : {
@@ -2827,4 +2893,4 @@
28272893
}
28282894
},
28292895
"version" : "1.2"
2830-
}
2896+
}

ClashX/ViewControllers/RemoteConfigViewController.swift

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,8 @@ extension RemoteConfigViewController {
8484
func showAdd(defaultUrl: String? = nil,
8585
defaultName: String? = nil,
8686
name: String? = nil,
87-
allowAlt: Bool = false) {
87+
allowAlt: Bool = false,
88+
ageSecretKey: String? = nil) {
8889
let alertView = NSAlert()
8990
alertView.addButton(withTitle: NSLocalizedString("OK", comment: ""))
9091
alertView.addButton(withTitle: NSLocalizedString("Cancel", comment: ""))
@@ -108,19 +109,22 @@ extension RemoteConfigViewController {
108109
let configName = remoteConfigInputView.getConfigName().0
109110
let isPlaceHolderName = remoteConfigInputView.getConfigName().1
110111
let configUrl = remoteConfigInputView.getUrlString()
111-
112+
let ageSecretKey = remoteConfigInputView.getAgeSecretKey()
113+
112114
if let existed = RemoteConfigManager.shared.configs.first(where: { $0.name == configName }) {
113115
guard allowAlt else {
114116
NSAlert.alert(with: NSLocalizedString("The remote config name is duplicated", comment: ""))
115117
return
116118
}
117119
existed.url = configUrl
120+
existed.ageSecretKey = ageSecretKey
118121
latestAddedConfig = existed
119122
requestUpdate(config: existed)
120123
} else {
121124
let remoteConfig = RemoteConfigModel(url: configUrl,
122125
name: configName,
123-
updateTime: nil)
126+
updateTime: nil,
127+
ageSecretKey: ageSecretKey)
124128
remoteConfig.isPlaceHolderName = !isPlaceHolderName
125129
RemoteConfigManager.shared.configs.append(remoteConfig)
126130
requestUpdate(config: remoteConfig)
@@ -172,9 +176,17 @@ extension RemoteConfigViewController: NSTableViewDelegate {
172176
let row = tableView.clickedRow
173177
guard let config = RemoteConfigManager.shared.configs[safe: row] else { return }
174178
if config.isPlaceHolderName {
175-
showAdd(defaultUrl: config.url, defaultName: config.name, name: nil, allowAlt: true)
179+
showAdd(defaultUrl: config.url,
180+
defaultName: config.name,
181+
name: nil,
182+
allowAlt: true,
183+
ageSecretKey: config.ageSecretKey)
176184
} else {
177-
showAdd(defaultUrl: config.url, defaultName: nil, name: config.name, allowAlt: true)
185+
showAdd(defaultUrl: config.url,
186+
defaultName: nil,
187+
name: config.name,
188+
allowAlt: true,
189+
ageSecretKey: config.ageSecretKey)
178190
}
179191
}
180192
}
@@ -215,9 +227,18 @@ extension RemoteConfigViewController: NSTableViewDataSource {
215227
class RemoteConfigAddView: NSView, NibLoadable {
216228
@IBOutlet private var urlTextField: NSTextField!
217229
@IBOutlet private var configNameTextField: NSTextField!
218-
230+
@IBOutlet var ageSecretTextField: NSTextField!
231+
219232
func getUrlString() -> String {
220-
return urlTextField.stringValue
233+
urlTextField.stringValue
234+
}
235+
236+
func getAgeSecretKey() -> String? {
237+
let str = ageSecretTextField.stringValue
238+
if str.isEmpty || !str.uppercased().starts(with: "AGE-SECRET-KEY") {
239+
return nil
240+
}
241+
return str
221242
}
222243

223244
/// Get the config name
@@ -233,7 +254,10 @@ class RemoteConfigAddView: NSView, NibLoadable {
233254
return urlTextField.stringValue.isUrlVaild() && !getConfigName().0.isEmpty
234255
}
235256

236-
func setUrl(string: String, name: String? = nil, defaultName: String?) {
257+
func setUrl(string: String,
258+
name: String? = nil,
259+
defaultName: String?,
260+
ageSecretKey: String? = nil) {
237261
urlTextField.stringValue = string
238262

239263
if let name = name, !name.isEmpty {
@@ -243,6 +267,10 @@ class RemoteConfigAddView: NSView, NibLoadable {
243267
if let defaultName = defaultName, !defaultName.isEmpty {
244268
configNameTextField.placeholderString = defaultName
245269
}
270+
271+
if let key = ageSecretKey, !key.isEmpty {
272+
ageSecretTextField.stringValue = key
273+
}
246274

247275
if name == nil && defaultName == nil {
248276
updateConfigName()

0 commit comments

Comments
 (0)