Add system proxy toggle

This commit is contained in:
世界
2023-09-03 22:59:51 +08:00
parent 4e5b251d6f
commit 30bff41517
7 changed files with 120 additions and 41 deletions
@@ -12,6 +12,8 @@ public struct ActiveDashboardView: View {
@State private var selectedProfileID: Int64! @State private var selectedProfileID: Int64!
@State private var alert: Alert? @State private var alert: Alert?
@State private var selection = DashboardPage.overview @State private var selection = DashboardPage.overview
@State private var systemProxyAvailable = false
@State private var systemProxyEnabled = false
public init() {} public init() {}
public var body: some View { public var body: some View {
@@ -37,16 +39,16 @@ public struct ActiveDashboardView: View {
#endif #endif
TabView(selection: $selection) { TabView(selection: $selection) {
ForEach(DashboardPage.allCases) { page in ForEach(DashboardPage.allCases) { page in
page.contentView($profileList, $selectedProfileID) page.contentView($profileList, $selectedProfileID, $systemProxyAvailable, $systemProxyEnabled)
.tag(page) .tag(page)
} }
} }
.tabViewStyle(.page(indexDisplayMode: .always)) .tabViewStyle(.page(indexDisplayMode: .always))
} else { } else {
OverviewView($profileList, $selectedProfileID) OverviewView($profileList, $selectedProfileID, $systemProxyAvailable, $systemProxyEnabled)
} }
#elseif os(macOS) #elseif os(macOS)
OverviewView($profileList, $selectedProfileID) OverviewView($profileList, $selectedProfileID, $systemProxyAvailable, $systemProxyEnabled)
#endif #endif
} }
#if os(iOS) || os(tvOS) #if os(iOS) || os(tvOS)
@@ -65,6 +67,13 @@ public struct ActiveDashboardView: View {
} }
} }
#endif #endif
.onChangeCompat(of: profile.status) { newStatus in
if newStatus == .connected {
Task.detached {
await doReloadSystemProxy()
}
}
}
.alertBinding($alert) .alertBinding($alert)
} }
} }
@@ -100,4 +109,14 @@ public struct ActiveDashboardView: View {
} }
} }
} }
private func doReloadSystemProxy() {
do {
let status = try LibboxNewStandaloneCommandClient()!.getSystemProxyStatus()
systemProxyAvailable = status.available
systemProxyEnabled = status.enabled
} catch {
alert = Alert(error)
}
}
} }
@@ -30,11 +30,11 @@ public extension DashboardPage {
} }
} }
func contentView(_ profileList: Binding<[Profile]>, _ selectedProfileID: Binding<Int64?>) -> some View { func contentView(_ profileList: Binding<[Profile]>, _ selectedProfileID: Binding<Int64?>, _ systemProxyAvailable: Binding<Bool>, _ systemProxyEnabled: Binding<Bool>) -> some View {
viewBuilder { viewBuilder {
switch self { switch self {
case .overview: case .overview:
OverviewView(profileList, selectedProfileID) OverviewView(profileList, selectedProfileID, systemProxyAvailable, systemProxyEnabled)
case .groups: case .groups:
GroupListView() GroupListView()
} }
@@ -10,13 +10,17 @@ public struct OverviewView: View {
@EnvironmentObject private var profile: ExtensionProfile @EnvironmentObject private var profile: ExtensionProfile
@Binding private var profileList: [Profile] @Binding private var profileList: [Profile]
@Binding private var selectedProfileID: Int64! @Binding private var selectedProfileID: Int64!
@Binding private var systemProxyAvailable: Bool
@Binding private var systemProxyEnabled: Bool
@State private var alert: Alert? @State private var alert: Alert?
@State private var reasserting = false @State private var reasserting = false
@State private var observer: Any? @State private var observer: Any?
public init(_ profileList: Binding<[Profile]>, _ selectedProfileID: Binding<Int64?>) { public init(_ profileList: Binding<[Profile]>, _ selectedProfileID: Binding<Int64?>, _ systemProxyAvailable: Binding<Bool>, _ systemProxyEnabled: Binding<Bool>) {
_profileList = profileList _profileList = profileList
_selectedProfileID = selectedProfileID _selectedProfileID = selectedProfileID
_systemProxyAvailable = systemProxyAvailable
_systemProxyEnabled = systemProxyEnabled
} }
public var body: some View { public var body: some View {
@@ -31,6 +35,14 @@ public struct OverviewView: View {
FormView { FormView {
#if os(iOS) || os(tvOS) #if os(iOS) || os(tvOS)
StartStopButton() StartStopButton()
if profile.status.isConnectedStrict, systemProxyAvailable {
Toggle("HTTP Proxy", isOn: $systemProxyEnabled)
.onChangeCompat(of: systemProxyEnabled) { newValue in
Task.detached {
await setSystemProxyEnabled(newValue)
}
}
}
Section("Profile") { Section("Profile") {
Picker(selection: $selectedProfileID) { Picker(selection: $selectedProfileID) {
ForEach(profileList, id: \.id) { profile in ForEach(profileList, id: \.id) { profile in
@@ -40,6 +52,14 @@ public struct OverviewView: View {
.pickerStyle(.inline) .pickerStyle(.inline)
} }
#elseif os(macOS) #elseif os(macOS)
if profile.status.isConnectedStrict, systemProxyAvailable {
Toggle("HTTP Proxy", isOn: $systemProxyEnabled)
.onChangeCompat(of: systemProxyEnabled) { newValue in
Task.detached {
await setSystemProxyEnabled(newValue)
}
}
}
Section("Profile") { Section("Profile") {
ForEach(profileList, id: \.id) { profile in ForEach(profileList, id: \.id) { profile in
Picker(profile.name, selection: $selectedProfileID) { Picker(profile.name, selection: $selectedProfileID) {
@@ -89,4 +109,13 @@ public struct OverviewView: View {
} }
reasserting = false reasserting = false
} }
private func setSystemProxyEnabled(_ isEnabled: Bool) {
do {
try LibboxNewStandaloneCommandClient()!.setSystemProxyEnabled(isEnabled)
SharedPreferences.systemProxyEnabled = isEnabled
} catch {
alert = Alert(error)
}
}
} }
+2
View File
@@ -22,4 +22,6 @@ public enum SharedPreferences {
#if os(iOS) #if os(iOS)
@Preference<Bool>("network_permission_requested", defaultValue: false) public static var networkPermissionRequested @Preference<Bool>("network_permission_requested", defaultValue: false) public static var networkPermissionRequested
#endif #endif
@Preference<Bool>("system_proxy_enabled", defaultValue: true) public static var systemProxyEnabled
} }
@@ -2,14 +2,12 @@ import Foundation
import Libbox import Libbox
import NetworkExtension import NetworkExtension
public class ExtensionPlatformInterface: NSObject, LibboxPlatformInterfaceProtocol { public class ExtensionPlatformInterface: NSObject, LibboxPlatformInterfaceProtocol, LibboxCommandServerHandlerProtocol {
private let tunnel: NEPacketTunnelProvider private let tunnel: ExtensionProvider
private let commandServer: LibboxCommandServer
private var networkSettings: NEPacketTunnelNetworkSettings? private var networkSettings: NEPacketTunnelNetworkSettings?
init(_ tunnel: NEPacketTunnelProvider, _ logServer: LibboxCommandServer) { init(_ tunnel: ExtensionProvider) {
self.tunnel = tunnel self.tunnel = tunnel
commandServer = logServer
} }
public func openTun(_ options: LibboxTunOptionsProtocol?, ret0_: UnsafeMutablePointer<Int32>?) throws { public func openTun(_ options: LibboxTunOptionsProtocol?, ret0_: UnsafeMutablePointer<Int32>?) throws {
@@ -82,10 +80,12 @@ public class ExtensionPlatformInterface: NSObject, LibboxPlatformInterfaceProtoc
if options.isHTTPProxyEnabled() { if options.isHTTPProxyEnabled() {
let proxySettings = NEProxySettings() let proxySettings = NEProxySettings()
let proxyServer = NEProxyServer(address: options.getHTTPProxyServer(), port: Int(options.getHTTPProxyServerPort())) let proxyServer = NEProxyServer(address: options.getHTTPProxyServer(), port: Int(options.getHTTPProxyServerPort()))
proxySettings.httpEnabled = true
proxySettings.httpServer = proxyServer proxySettings.httpServer = proxyServer
proxySettings.httpsEnabled = true
proxySettings.httpsServer = proxyServer proxySettings.httpsServer = proxyServer
if SharedPreferences.systemProxyEnabled {
proxySettings.httpEnabled = true
proxySettings.httpsEnabled = true
}
settings.proxySettings = proxySettings settings.proxySettings = proxySettings
} }
@@ -133,7 +133,7 @@ public class ExtensionPlatformInterface: NSObject, LibboxPlatformInterfaceProtoc
guard let message else { guard let message else {
return return
} }
commandServer.writeMessage(message) tunnel.writeMessage(message)
} }
public func usePlatformDefaultInterfaceMonitor() -> Bool { public func usePlatformDefaultInterfaceMonitor() -> Bool {
@@ -167,4 +167,45 @@ public class ExtensionPlatformInterface: NSObject, LibboxPlatformInterfaceProtoc
} }
tunnel.reasserting = false tunnel.reasserting = false
} }
public func serviceReload() throws {
tunnel.reloadService()
}
public func getSystemProxyStatus() -> LibboxSystemProxyStatus? {
let status = LibboxSystemProxyStatus()
guard let networkSettings else {
return status
}
guard let proxySettings = networkSettings.proxySettings else {
return status
}
if proxySettings.httpServer == nil {
return status
}
status.available = true
status.enabled = proxySettings.httpEnabled
return status
}
public func setSystemProxyEnabled(_ isEnabled: Bool) throws {
guard let networkSettings else {
return
}
guard let proxySettings = networkSettings.proxySettings else {
return
}
if proxySettings.httpServer == nil {
return
}
if proxySettings.httpEnabled == isEnabled {
return
}
proxySettings.httpEnabled = isEnabled
proxySettings.httpsEnabled = isEnabled
networkSettings.proxySettings = proxySettings
try runBlocking {
try await self.tunnel.setTunnelNetworkSettings(networkSettings)
}
}
} }
+11 -23
View File
@@ -8,6 +8,9 @@ open class ExtensionProvider: NEPacketTunnelProvider {
public var username: String? = nil public var username: String? = nil
private var commandServer: LibboxCommandServer! private var commandServer: LibboxCommandServer!
private var boxService: LibboxBoxService! private var boxService: LibboxBoxService!
private var systemProxyAvailable = false
private var systemProxyEnabled = false
private var platformInterface: ExtensionPlatformInterface!
override open func startTunnel(options _: [String: NSObject]?) async throws { override open func startTunnel(options _: [String: NSObject]?) async throws {
NSLog("Here I am") NSLog("Here I am")
@@ -44,7 +47,10 @@ open class ExtensionProvider: NEPacketTunnelProvider {
LibboxSetMemoryLimit(!SharedPreferences.disableMemoryLimit) LibboxSetMemoryLimit(!SharedPreferences.disableMemoryLimit)
commandServer = LibboxNewCommandServer(serverInterface(self), Int32(SharedPreferences.maxLogLines)) if platformInterface == nil {
platformInterface = ExtensionPlatformInterface(self)
}
commandServer = LibboxNewCommandServer(platformInterface, Int32(SharedPreferences.maxLogLines))
do { do {
try commandServer.start() try commandServer.start()
} catch { } catch {
@@ -56,7 +62,7 @@ open class ExtensionProvider: NEPacketTunnelProvider {
startService() startService()
} }
private func writeMessage(_ message: String) { func writeMessage(_ message: String) {
if let commandServer { if let commandServer {
commandServer.writeMessage(message) commandServer.writeMessage(message)
} else { } else {
@@ -64,7 +70,7 @@ open class ExtensionProvider: NEPacketTunnelProvider {
} }
} }
private func writeError(_ message: String) { func writeError(_ message: String) {
writeMessage(message) writeMessage(message)
try? message.write(to: ExtensionProvider.errorFile, atomically: true, encoding: .utf8) try? message.write(to: ExtensionProvider.errorFile, atomically: true, encoding: .utf8)
} }
@@ -97,7 +103,7 @@ open class ExtensionProvider: NEPacketTunnelProvider {
return return
} }
var error: NSError? var error: NSError?
let service = LibboxNewService(configContent, ExtensionPlatformInterface(self, commandServer), &error) let service = LibboxNewService(configContent, platformInterface, &error)
if let error { if let error {
writeError("(packet-tunnel) error: create service: \(error.localizedDescription)") writeError("(packet-tunnel) error: create service: \(error.localizedDescription)")
return return
@@ -132,7 +138,7 @@ open class ExtensionProvider: NEPacketTunnelProvider {
} }
} }
private func reloadService() { func reloadService() {
writeMessage("(packet-tunnel) reloading service") writeMessage("(packet-tunnel) reloading service")
reasserting = true reasserting = true
defer { defer {
@@ -172,22 +178,4 @@ open class ExtensionProvider: NEPacketTunnelProvider {
boxService.wake() boxService.wake()
} }
} }
private class serverInterface: NSObject, LibboxCommandServerHandlerProtocol {
unowned let tunnel: ExtensionProvider
init(_ tunnel: ExtensionProvider) {
self.tunnel = tunnel
super.init()
}
func serviceReload() throws {
tunnel.reloadService()
}
func serviceStop() throws {
tunnel.stopService()
tunnel.writeMessage("(packet-tunnel) debug: service stopped")
}
}
} }
+4 -4
View File
@@ -2489,7 +2489,7 @@
"@executable_path/../../../../Frameworks", "@executable_path/../../../../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 13.0; MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 1.5.0-beta.2; MARKETING_VERSION = 1.5.0-beta.3;
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.system; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.system;
PRODUCT_NAME = "$(inherited)"; PRODUCT_NAME = "$(inherited)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@@ -2525,7 +2525,7 @@
"@executable_path/../../../../Frameworks", "@executable_path/../../../../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 13.0; MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 1.5.0-beta.2; MARKETING_VERSION = 1.5.0-beta.3;
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.system; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.system;
PRODUCT_NAME = "$(inherited)"; PRODUCT_NAME = "$(inherited)";
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@@ -2565,7 +2565,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 13.0; MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 1.5.0-beta.2; MARKETING_VERSION = 1.5.0-beta.3;
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.independent; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.independent;
PRODUCT_NAME = SFM; PRODUCT_NAME = SFM;
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";
@@ -2604,7 +2604,7 @@
"@executable_path/../Frameworks", "@executable_path/../Frameworks",
); );
MACOSX_DEPLOYMENT_TARGET = 13.0; MACOSX_DEPLOYMENT_TARGET = 13.0;
MARKETING_VERSION = 1.5.0-beta.2; MARKETING_VERSION = 1.5.0-beta.3;
PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.independent; PRODUCT_BUNDLE_IDENTIFIER = io.nekohasekai.sfa.independent;
PRODUCT_NAME = SFM; PRODUCT_NAME = SFM;
PROVISIONING_PROFILE_SPECIFIER = ""; PROVISIONING_PROFILE_SPECIFIER = "";