mirror of
https://github.com/shadowsocks/ShadowsocksX-NG.git
synced 2026-11-15 18:59:52 +00:00
Implements issue #71 with ADR-0020 (supersedes ADR-0019's endpoint-signature cleanup, startup cleanup, and health-gate cleanup). - New ShadowsocksX-NG2SystemProxyHelper target: app-bundled root LaunchDaemon (SMAppService.daemon, on-demand via MachServices) that mechanically applies a closed typed configuration or executes an unconditional clear. Clients are accepted by code-signing bundle identifier only; requests run serially on a shared queue (last-writer-wins). - SystemProxyConfiguration is now the closed typed apply payload (Codable over JSON XPC): explicit SOCKS/HTTP/HTTPS enable + endpoints, PAC and auto-discovery disabled with URL/JS removed, explicit simple-hostname exclusion, complete ExceptionsList. Unmodeled Proxies keys are preserved and equivalent values skip the commit. - Cleanup is a side effect of a persisted system-proxy intent on-to-off transition only: the switch itself, or the agent-off cascade (persist both intents off, clear, then stop listeners; failed cascade persistence records no transition and clears nothing). Launch with the switch off, health-gate closure, invalid targets, and passive events never clear. Failed clears are surfaced without deferred retry. - Endpoint-signature store, ownership migration, and direct Authorization Services writes are removed; old signature files stay inert. Helper unavailability or pending approval keeps an enabled intent pending and surfaces the Login Items approval path (HomeView button + menu label).
77 lines
3.1 KiB
Swift
77 lines
3.1 KiB
Swift
import Foundation
|
|
|
|
/// Pure projection of one SystemConfiguration Proxies dictionary. The key mapping is a
|
|
/// mechanical translation of the closed typed configuration (issue #71): every field the
|
|
/// GUI supplies is written, disabled protocols lose their endpoint keys, PAC/auto-
|
|
/// discovery/simple-hostname exclusion follow their explicit flags, and the complete
|
|
/// exceptions list is always written. Keys outside the typed field set are preserved.
|
|
enum SystemProxyPropertyList {
|
|
static let httpEnabled = "HTTPEnable"
|
|
static let httpsEnabled = "HTTPSEnable"
|
|
static let socksEnabled = "SOCKSEnable"
|
|
static let httpPort = "HTTPPort"
|
|
static let httpProxy = "HTTPProxy"
|
|
static let httpsPort = "HTTPSPort"
|
|
static let httpsProxy = "HTTPSProxy"
|
|
static let socksPort = "SOCKSPort"
|
|
static let socksProxy = "SOCKSProxy"
|
|
static let pacEnabled = "ProxyAutoConfigEnable"
|
|
static let pacURL = "ProxyAutoConfigURLString"
|
|
static let pacJavaScript = "ProxyAutoConfigJavaScript"
|
|
static let autoDiscoveryEnabled = "ProxyAutoDiscoveryEnable"
|
|
static let exceptionsList = "ExceptionsList"
|
|
static let excludeSimpleHostnames = "ExcludeSimpleHostnames"
|
|
|
|
/// 一个协议族的 Proxies 键组。
|
|
private struct ProtocolKeys {
|
|
let enable: String
|
|
let proxy: String
|
|
let port: String
|
|
}
|
|
|
|
static func applying(
|
|
_ configuration: SystemProxyConfiguration, to original: [String: Any]
|
|
) -> [String: Any] {
|
|
var dictionary = original
|
|
applyProtocol(
|
|
enabled: configuration.socksEnabled, endpoint: configuration.socks,
|
|
keys: ProtocolKeys(enable: socksEnabled, proxy: socksProxy, port: socksPort),
|
|
into: &dictionary)
|
|
applyProtocol(
|
|
enabled: configuration.httpEnabled, endpoint: configuration.http,
|
|
keys: ProtocolKeys(enable: httpEnabled, proxy: httpProxy, port: httpPort),
|
|
into: &dictionary)
|
|
applyProtocol(
|
|
enabled: configuration.httpsEnabled, endpoint: configuration.https,
|
|
keys: ProtocolKeys(enable: httpsEnabled, proxy: httpsProxy, port: httpsPort),
|
|
into: &dictionary)
|
|
|
|
dictionary[pacEnabled] = configuration.pacEnabled ? 1 : 0
|
|
if !configuration.pacEnabled {
|
|
// 关闭的 PAC 不留残余:URL 与 JavaScript 值一并移除(issue #71 AC27)。
|
|
dictionary.removeValue(forKey: pacJavaScript)
|
|
dictionary.removeValue(forKey: pacURL)
|
|
}
|
|
dictionary[autoDiscoveryEnabled] = configuration.autoDiscoveryEnabled ? 1 : 0
|
|
dictionary[excludeSimpleHostnames] = configuration.excludeSimpleHostnames ? 1 : 0
|
|
dictionary[exceptionsList] = configuration.exceptions
|
|
return dictionary
|
|
}
|
|
|
|
/// 启用的协议族写入显式端点;关闭的协议族移除端点键,保持字典与配置一致。
|
|
private static func applyProtocol(
|
|
enabled: Bool, endpoint: SystemProxyConfiguration.Endpoint,
|
|
keys: ProtocolKeys,
|
|
into dictionary: inout [String: Any]
|
|
) {
|
|
dictionary[keys.enable] = enabled ? 1 : 0
|
|
if enabled {
|
|
dictionary[keys.proxy] = endpoint.host
|
|
dictionary[keys.port] = endpoint.port
|
|
} else {
|
|
dictionary.removeValue(forKey: keys.proxy)
|
|
dictionary.removeValue(forKey: keys.port)
|
|
}
|
|
}
|
|
}
|