Files
sing-box-for-apple/Library/Network/ExtensionProvider.swift
T
世界 e2c378d42b Add QR scanner support for macOS with camera selection
- Refactor QR scanner into separate Scanner module for iOS and macOS
- Add camera selection menu for devices with multiple cameras
- Use Vision framework for QR detection on macOS
- Remove tvOS QR scanner support (continuity camera)
- Add camera entitlement and usage description for macOS
- Fix minor issues in Intents and ExtensionProvider
2026-01-01 18:18:03 +08:00

179 lines
6.2 KiB
Swift

import Foundation
import Libbox
import NetworkExtension
#if os(iOS)
import WidgetKit
#endif
#if os(macOS)
import CoreLocation
#endif
open class ExtensionProvider: NEPacketTunnelProvider {
public var username: String?
private var commandServer: LibboxCommandServer!
private var platformInterface: ExtensionPlatformInterface!
override open func startTunnel(options _: [String: NSObject]?) async throws {
let options = LibboxSetupOptions()
options.basePath = FilePath.sharedDirectory.relativePath
options.workingPath = FilePath.workingDirectory.relativePath
options.tempPath = FilePath.cacheDirectory.relativePath
options.logMaxLines = 3000
#if os(tvOS)
options.commandServerListenPort = await SharedPreferences.commandServerPort.get()
options.commandServerSecret = await SharedPreferences.commandServerSecret.get()
#endif
var setupError: NSError?
LibboxSetup(options, &setupError)
if let setupError {
throw ExtensionStartupError("(packet-tunnel) error: setup service: \(setupError.localizedDescription)")
}
var stderrError: NSError?
LibboxRedirectStderr(FilePath.cacheDirectory.appendingPathComponent("stderr.log").relativePath, &stderrError)
if let stderrError {
throw ExtensionStartupError("(packet-tunnel) redirect stderr error: \(stderrError.localizedDescription)")
}
await LibboxSetMemoryLimit(!SharedPreferences.ignoreMemoryLimit.get())
if platformInterface == nil {
platformInterface = ExtensionPlatformInterface(self)
}
var error: NSError?
commandServer = LibboxNewCommandServer(platformInterface, platformInterface, &error)
if let error {
throw ExtensionStartupError("(packet-tunnel): create command server error: \(error.localizedDescription)")
}
do {
try commandServer.start()
} catch {
throw ExtensionStartupError("(packet-tunnel): start command server error: \(error.localizedDescription)")
}
writeMessage("(packet-tunnel): Here I stand")
try await startService()
#if os(iOS)
if #available(iOS 18.0, *) {
ControlCenter.shared.reloadControls(ofKind: ExtensionProfile.controlKind)
}
#endif
}
func writeMessage(_ message: String) {
if let commandServer {
commandServer.writeMessage(2, message: message)
}
}
private func startService() async throws {
let profileID = await SharedPreferences.selectedProfileID.get()
let profile: Profile?
do {
profile = try await ProfileManager.get(profileID)
} catch {
throw ExtensionStartupError("(packet-tunnel) error: read selected profile: \(error.localizedDescription)")
}
guard let profile else {
throw ExtensionStartupError("(packet-tunnel) error: missing selected profile")
}
let configContent: String
do {
configContent = try profile.read()
} catch {
throw ExtensionStartupError("(packet-tunnel) error: read config file \(profile.path): \(error.localizedDescription)")
}
let options = LibboxOverrideOptions()
do {
try commandServer.startOrReloadService(configContent, options: options)
} catch {
throw ExtensionStartupError("(packet-tunnel) error: start service: \(error.localizedDescription)")
}
#if os(macOS)
await SharedPreferences.startedByUser.set(true)
if commandServer.needWIFIState() {
if !Variant.useSystemExtension {
locationManager = CLLocationManager()
locationDelegate = stubLocationDelegate()
locationManager?.delegate = locationDelegate
locationManager?.requestLocation()
} else {
writeMessage("(packet-tunnel) WIFI SSID and BSSID information is not currently available in the standalone version of SFM. We are working on resolving this issue.")
}
}
#endif
}
#if os(macOS)
private var locationManager: CLLocationManager?
private var locationDelegate: stubLocationDelegate?
class stubLocationDelegate: NSObject, CLLocationManagerDelegate {
func locationManagerDidChangeAuthorization(_: CLLocationManager) {}
func locationManager(_: CLLocationManager, didUpdateLocations _: [CLLocation]) {}
func locationManager(_: CLLocationManager, didFailWithError _: Error) {}
}
#endif
func stopService() {
do {
try commandServer.closeService()
} catch {
writeMessage("(packet-tunnel) error: stop service: \(error.localizedDescription)")
}
if let platformInterface {
platformInterface.reset()
}
}
func reloadService() async throws {
writeMessage("(packet-tunnel) reloading service")
reasserting = true
defer {
reasserting = false
}
try await startService()
}
override open func stopTunnel(with reason: NEProviderStopReason) async {
writeMessage("(packet-tunnel) stopping, reason: \(reason)")
stopService()
if let server = commandServer {
try? await Task.sleep(nanoseconds: 100 * NSEC_PER_MSEC)
server.close()
commandServer = nil
}
#if os(macOS)
if reason == .userInitiated {
await SharedPreferences.startedByUser.set(reason == .userInitiated)
}
#endif
#if os(iOS)
if #available(iOS 18.0, *) {
ControlCenter.shared.reloadControls(ofKind: ExtensionProfile.controlKind)
}
#endif
}
override open func handleAppMessage(_ messageData: Data) async -> Data? {
messageData
}
override open func sleep() async {
if let commandServer {
commandServer.pause()
}
}
override open func wake() {
if let commandServer {
commandServer.wake()
}
}
}