Add system proxy toggle
This commit is contained in:
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 = "";
|
||||||
|
|||||||
Reference in New Issue
Block a user