mirror of
https://github.com/osnr/TabFS.git
synced 2024-05-02 22:03:10 +02:00
126 lines
4.5 KiB
Swift
126 lines
4.5 KiB
Swift
//
|
|
// TabFSService.swift
|
|
// TabFSService
|
|
//
|
|
// Created by Omar Rizwan on 2/7/21.
|
|
//
|
|
|
|
import Foundation
|
|
import Network
|
|
import os.log
|
|
|
|
class TabFSService: NSObject, TabFSServiceProtocol {
|
|
var fs: Process!
|
|
var fsInput: FileHandle!
|
|
var fsOutput: FileHandle!
|
|
func startFs() {
|
|
let fileURL = URL(fileURLWithPath: #filePath)
|
|
let repoURL = fileURL.deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent()
|
|
|
|
fs = Process()
|
|
fs.executableURL = repoURL.appendingPathComponent("fs").appendingPathComponent("tabfs")
|
|
fs.currentDirectoryURL = fs.executableURL?.deletingLastPathComponent()
|
|
|
|
fs.arguments = []
|
|
|
|
let inputPipe = Pipe(), outputPipe = Pipe()
|
|
fs.standardInput = inputPipe
|
|
fs.standardOutput = outputPipe
|
|
|
|
fsInput = inputPipe.fileHandleForWriting
|
|
fsOutput = outputPipe.fileHandleForReading
|
|
|
|
try! fs.run()
|
|
}
|
|
|
|
var ws: NWListener!
|
|
func startWs() {
|
|
// TODO: randomly generate port and report back to caller?
|
|
let port = NWEndpoint.Port(rawValue: 9991)!
|
|
|
|
let parameters = NWParameters(tls: nil)
|
|
parameters.allowLocalEndpointReuse = true
|
|
parameters.includePeerToPeer = true
|
|
// for security ? so people outside your computer can't hijack TabFS at least
|
|
parameters.requiredInterfaceType = .loopback
|
|
|
|
let opts = NWProtocolWebSocket.Options()
|
|
opts.autoReplyPing = true
|
|
parameters.defaultProtocolStack.applicationProtocols.insert(opts, at: 0)
|
|
|
|
ws = try! NWListener(using: parameters, on: port)
|
|
ws.start(queue: .main)
|
|
}
|
|
|
|
override init() {
|
|
super.init()
|
|
|
|
startFs()
|
|
startWs()
|
|
|
|
var handleRequest: ((_ req: Data) -> Void)?
|
|
ws.newConnectionHandler = { conn in
|
|
conn.start(queue: .main)
|
|
|
|
handleRequest = { req in
|
|
let metaData = NWProtocolWebSocket.Metadata(opcode: .text)
|
|
let context = NWConnection.ContentContext(identifier: "context", metadata: [metaData])
|
|
conn.send(content: req, contentContext: context, completion: .contentProcessed({ err in
|
|
if err != nil {
|
|
os_log(.default, "req %{public}@ error: %{public}@", String(data: req, encoding: .utf8)!, err!.debugDescription as CVarArg)
|
|
// FIXME: ERROR
|
|
}
|
|
}))
|
|
}
|
|
|
|
func read() {
|
|
conn.receiveMessage { (resp, context, isComplete, err) in
|
|
guard let resp = resp else {
|
|
// FIXME err
|
|
os_log(.default, "resp error: %{public}@", err!.debugDescription as CVarArg)
|
|
return
|
|
}
|
|
|
|
// Send the response back to tabfs.c.
|
|
self.fsInput.write(withUnsafeBytes(of: UInt32(resp.count)) { Data($0) })
|
|
self.fsInput.write(resp)
|
|
read()
|
|
}
|
|
}
|
|
read()
|
|
}
|
|
|
|
DispatchQueue.global(qos: .default).async {
|
|
while true {
|
|
// Blocking read from the tabfs process.
|
|
let length = self.fsOutput.readData(ofLength: 4).withUnsafeBytes { $0.load(as: UInt32.self) }
|
|
let req = self.fsOutput.readData(ofLength: Int(length))
|
|
|
|
if let handleRequest = handleRequest {
|
|
// Send the request over the WebSocket connection to background.js in browser.
|
|
handleRequest(req)
|
|
} else {
|
|
// FIXME: ERROR
|
|
}
|
|
}
|
|
}
|
|
// FIXME: disable auto termination
|
|
}
|
|
|
|
func start(withReply reply: @escaping () -> Void) {
|
|
// This XPC call is enough to just force the XPC service to be started.
|
|
reply()
|
|
}
|
|
}
|
|
|
|
class TabFSServiceDelegate: NSObject, NSXPCListenerDelegate {
|
|
func listener(_ listener: NSXPCListener, shouldAcceptNewConnection newConnection: NSXPCConnection) -> Bool {
|
|
let exportedObject = TabFSService()
|
|
newConnection.exportedInterface = NSXPCInterface(with: TabFSServiceProtocol.self)
|
|
newConnection.exportedObject = exportedObject
|
|
|
|
newConnection.resume()
|
|
return true
|
|
}
|
|
}
|