mirror of
https://github.com/maxgoedjen/secretive.git
synced 2025-08-31 01:20:57 +00:00
Working
This commit is contained in:
parent
a8662c9a2f
commit
f9d8818879
@ -2,24 +2,63 @@ import Foundation
|
|||||||
import OSLog
|
import OSLog
|
||||||
import SecretKit
|
import SecretKit
|
||||||
|
|
||||||
|
public struct Session: Sendable {
|
||||||
|
|
||||||
|
public let messages: AsyncStream<Data>
|
||||||
|
public let provenance: SigningRequestProvenance
|
||||||
|
|
||||||
|
private let fileHandle: FileHandle
|
||||||
|
private let continuation: AsyncStream<Data>.Continuation
|
||||||
|
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "Session")
|
||||||
|
|
||||||
|
init(fileHandle: FileHandle) {
|
||||||
|
self.fileHandle = fileHandle
|
||||||
|
provenance = SigningRequestTracer().provenance(from: fileHandle)
|
||||||
|
(messages, continuation) = AsyncStream.makeStream()
|
||||||
|
Task { [continuation, logger] in
|
||||||
|
await fileHandle.waitForDataInBackgroundAndNotifyOnMainActor()
|
||||||
|
for await _ in NotificationCenter.default.notifications(named: .NSFileHandleDataAvailable, object: fileHandle) {
|
||||||
|
let data = fileHandle.availableData
|
||||||
|
guard !data.isEmpty else {
|
||||||
|
logger.debug("Socket controller received empty data, ending continuation.")
|
||||||
|
continuation.finish()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
continuation.yield(data)
|
||||||
|
logger.debug("Socket controller yielded data.")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func write(_ data: Data) async throws {
|
||||||
|
try fileHandle.write(contentsOf: data)
|
||||||
|
await fileHandle.waitForDataInBackgroundAndNotifyOnMainActor()
|
||||||
|
}
|
||||||
|
|
||||||
|
public func close() throws {
|
||||||
|
logger.debug("Session closed.")
|
||||||
|
try fileHandle.close()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
/// A controller that manages socket configuration and request dispatching.
|
/// A controller that manages socket configuration and request dispatching.
|
||||||
public final class SocketController {
|
public struct SocketController {
|
||||||
|
|
||||||
/// The active SocketPort.
|
/// The active SocketPort.
|
||||||
private var port: SocketPort?
|
private let port: SocketPort
|
||||||
/// A handler that will be notified when a new read/write handle is available.
|
/// The FileHandle for the main socket.
|
||||||
/// False if no data could be read
|
private let fileHandle: FileHandle
|
||||||
public var handler: OSAllocatedUnfairLock<(@Sendable (Data, SigningRequestProvenance) async throws -> Data)?> = .init(initialState: nil)
|
|
||||||
/// Logger.
|
|
||||||
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "SocketController")
|
private let logger = Logger(subsystem: "com.maxgoedjen.secretive.secretagent", category: "SocketController")
|
||||||
|
|
||||||
private let requestTracer = SigningRequestTracer()
|
private let requestTracer = SigningRequestTracer()
|
||||||
|
|
||||||
// Async sequence of message?
|
public let sessions: AsyncStream<Session>
|
||||||
|
public let continuation: AsyncStream<Session>.Continuation
|
||||||
|
|
||||||
/// Initializes a socket controller with a specified path.
|
/// Initializes a socket controller with a specified path.
|
||||||
/// - Parameter path: The path to use as a socket.
|
/// - Parameter path: The path to use as a socket.
|
||||||
public init(path: String) {
|
public init(path: String) {
|
||||||
|
(sessions, continuation) = AsyncStream<Session>.makeStream()
|
||||||
logger.debug("Socket controller setting up at \(path)")
|
logger.debug("Socket controller setting up at \(path)")
|
||||||
if let _ = try? FileManager.default.removeItem(atPath: path) {
|
if let _ = try? FileManager.default.removeItem(atPath: path) {
|
||||||
logger.debug("Socket controller removed existing socket")
|
logger.debug("Socket controller removed existing socket")
|
||||||
@ -27,76 +66,19 @@ public final class SocketController {
|
|||||||
let exists = FileManager.default.fileExists(atPath: path)
|
let exists = FileManager.default.fileExists(atPath: path)
|
||||||
assert(!exists)
|
assert(!exists)
|
||||||
logger.debug("Socket controller path is clear")
|
logger.debug("Socket controller path is clear")
|
||||||
port = socketPort(at: path)
|
port = SocketPort(path: path)
|
||||||
configureSocket(at: path)
|
fileHandle = FileHandle(fileDescriptor: port.socket, closeOnDealloc: true)
|
||||||
logger.debug("Socket listening at \(path)")
|
Task { [fileHandle, continuation, logger] in
|
||||||
}
|
|
||||||
|
|
||||||
/// Configures the socket and a corresponding FileHandle.
|
|
||||||
/// - Parameter path: The path to use as a socket.
|
|
||||||
func configureSocket(at path: String) {
|
|
||||||
guard let port = port else { return }
|
|
||||||
let fileHandle = FileHandle(fileDescriptor: port.socket, closeOnDealloc: true)
|
|
||||||
fileHandle.acceptConnectionInBackgroundAndNotify(forModes: [RunLoop.Mode.common])
|
|
||||||
Task { [handler, logger] in
|
|
||||||
for await notification in NotificationCenter.default.notifications(named: .NSFileHandleConnectionAccepted) {
|
for await notification in NotificationCenter.default.notifications(named: .NSFileHandleConnectionAccepted) {
|
||||||
logger.debug("Socket controller accepted connection")
|
logger.debug("Socket controller accepted connection")
|
||||||
guard let new = notification.userInfo?[NSFileHandleNotificationFileHandleItem] as? FileHandle else { continue }
|
guard let new = notification.userInfo?[NSFileHandleNotificationFileHandleItem] as? FileHandle else { continue }
|
||||||
let provenance = SigningRequestTracer().provenance(from: new)
|
let session = Session(fileHandle: new)
|
||||||
guard let handler = handler.withLock({ $0 }) else {
|
continuation.yield(session)
|
||||||
// FIXME: THIS
|
|
||||||
fatalError()
|
|
||||||
}
|
|
||||||
let response = try await handler(Data(fileHandle.availableData), provenance)
|
|
||||||
try fileHandle.write(contentsOf: response)
|
|
||||||
await new.waitForDataInBackgroundAndNotifyOnMainActor()
|
|
||||||
await fileHandle.acceptConnectionInBackgroundAndNotifyOnMainActor()
|
await fileHandle.acceptConnectionInBackgroundAndNotifyOnMainActor()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Task { [logger, handler] in
|
fileHandle.acceptConnectionInBackgroundAndNotify(forModes: [RunLoop.Mode.common])
|
||||||
for await notification in NotificationCenter.default.notifications(named: .NSFileHandleDataAvailable) {
|
logger.debug("Socket listening at \(path)")
|
||||||
logger.debug("Socket controller has new data available")
|
|
||||||
guard let new = notification.object as? FileHandle else { return }
|
|
||||||
logger.debug("Socket controller received new file handle")
|
|
||||||
guard let handler = handler.withLock({ $0 }) else {
|
|
||||||
// FIXME: THIS
|
|
||||||
fatalError()
|
|
||||||
}
|
|
||||||
do {
|
|
||||||
let provenance = SigningRequestTracer().provenance(from: new)
|
|
||||||
let response = try await handler(Data(fileHandle.availableData), provenance)
|
|
||||||
try fileHandle.write(contentsOf: response)
|
|
||||||
logger.debug("Socket controller handled data, wait for more data")
|
|
||||||
await new.waitForDataInBackgroundAndNotifyOnMainActor()
|
|
||||||
} catch {
|
|
||||||
logger.debug("Socket controller called with empty data, socked closed")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Creates a SocketPort for a path.
|
|
||||||
/// - Parameter path: The path to use as a socket.
|
|
||||||
/// - Returns: A configured SocketPort.
|
|
||||||
func socketPort(at path: String) -> SocketPort {
|
|
||||||
var addr = sockaddr_un()
|
|
||||||
addr.sun_family = sa_family_t(AF_UNIX)
|
|
||||||
|
|
||||||
var len: Int = 0
|
|
||||||
withUnsafeMutablePointer(to: &addr.sun_path.0) { pointer in
|
|
||||||
path.withCString { cstring in
|
|
||||||
len = strlen(cstring)
|
|
||||||
strncpy(pointer, cstring, len)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
addr.sun_len = UInt8(len+2)
|
|
||||||
|
|
||||||
var data: Data!
|
|
||||||
withUnsafePointer(to: &addr) { pointer in
|
|
||||||
data = Data(bytes: pointer, count: MemoryLayout<sockaddr_un>.size)
|
|
||||||
}
|
|
||||||
|
|
||||||
return SocketPort(protocolFamily: AF_UNIX, socketType: SOCK_STREAM, protocol: 0, address: data)!
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -116,3 +98,28 @@ extension FileHandle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extension SocketPort {
|
||||||
|
|
||||||
|
convenience init(path: String) {
|
||||||
|
var addr = sockaddr_un()
|
||||||
|
addr.sun_family = sa_family_t(AF_UNIX)
|
||||||
|
|
||||||
|
var len: Int = 0
|
||||||
|
withUnsafeMutablePointer(to: &addr.sun_path.0) { pointer in
|
||||||
|
path.withCString { cstring in
|
||||||
|
len = strlen(cstring)
|
||||||
|
strncpy(pointer, cstring, len)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
addr.sun_len = UInt8(len+2)
|
||||||
|
|
||||||
|
var data: Data!
|
||||||
|
withUnsafePointer(to: &addr) { pointer in
|
||||||
|
data = Data(bytes: pointer, count: MemoryLayout<sockaddr_un>.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
self.init(protocolFamily: AF_UNIX, socketType: SOCK_STREAM, protocol: 0, address: data)!
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
@ -33,12 +33,20 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
|
|
||||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||||
logger.debug("SecretAgent finished launching")
|
logger.debug("SecretAgent finished launching")
|
||||||
socketController.handler.withLock { [agent] in $0 = agent.handle }
|
Task {
|
||||||
// Task {
|
for await session in socketController.sessions {
|
||||||
// for await (message, response) in socketController.messages {
|
Task {
|
||||||
// let handled =
|
do {
|
||||||
// }
|
for await message in session.messages {
|
||||||
// }
|
let agentResponse = try await agent.handle(data: message, provenance: session.provenance)
|
||||||
|
try await session.write(agentResponse)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
try session.close()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Task {
|
Task {
|
||||||
for await _ in NotificationCenter.default.notifications(named: .secretStoreReloaded) {
|
for await _ in NotificationCenter.default.notifications(named: .secretStoreReloaded) {
|
||||||
try? publicKeyFileStoreController.generatePublicKeys(for: storeList.allSecrets, clear: true)
|
try? publicKeyFileStoreController.generatePublicKeys(for: storeList.allSecrets, clear: true)
|
||||||
|
Loading…
Reference in New Issue
Block a user