version bump

remove @mainactors
set env vars
use sshoptionsset to set a username
disconnect sends an eof and handles the channel clsoing, for some reason it crashes on disconnect tho
do catch for pubkeyauth
added asyncReadFromChannel
remove weak self
condense guards
disconnect on a eof reccieve
reduce time sleeping
This commit is contained in:
neon443
2025-06-23 18:14:08 +01:00
parent 31beda2124
commit 88b39363b2
3 changed files with 60 additions and 34 deletions

View File

@@ -5,7 +5,7 @@
// Created by neon443 on 06/06/2025. // Created by neon443 on 06/06/2025.
// //
VERSION = 0.7 VERSION = 1.0
BUILD = 3 BUILD = 3
// Configuration settings file format documentation can be found at: // Configuration settings file format documentation can be found at:

View File

@@ -10,18 +10,17 @@ import LibSSH
import OSLog import OSLog
import SwiftUI import SwiftUI
@MainActor
class SSHHandler: ObservableObject { class SSHHandler: ObservableObject {
private var session: ssh_session? private var session: ssh_session?
private var channel: ssh_channel? var channel: ssh_channel?
@MainActor @Published var connected: Bool = false @Published var connected: Bool = false
@MainActor @Published var authorized: Bool = false @Published var authorized: Bool = false
@MainActor @Published var testSuceeded: Bool? = nil @Published var testSuceeded: Bool? = nil
@MainActor @Published var bell: UUID? @Published var bell: UUID?
@MainActor @Published var host: Host @Published var host: Host
private let userDefaults = NSUbiquitousKeyValueStore.default private let userDefaults = NSUbiquitousKeyValueStore.default
private let logger = Logger(subsystem: "xy", category: "sshHandler") private let logger = Logger(subsystem: "xy", category: "sshHandler")
@@ -67,6 +66,9 @@ class SSHHandler: ObservableObject {
} }
} }
openShell() openShell()
ssh_channel_request_env(channel, "TERM", "xterm-256color")
ssh_channel_request_env(channel, "LANG", "en_US.UTF-8")
ssh_channel_request_env(channel, "LC_ALL", "en_US.UTF-8")
} }
func connect() throws(SSHError) { func connect() throws(SSHError) {
@@ -75,7 +77,8 @@ class SSHHandler: ObservableObject {
self.host.key = getHostkey() self.host.key = getHostkey()
} }
var verbosity: Int = SSH_LOG_FUNCTIONS var verbosity: Int = 0
// var verbosity: Int = SSH_LOG_FUNCTIONS
session = ssh_new() session = ssh_new()
guard session != nil else { guard session != nil else {
@@ -86,6 +89,7 @@ class SSHHandler: ObservableObject {
ssh_options_set(session, SSH_OPTIONS_HOST, host.address) ssh_options_set(session, SSH_OPTIONS_HOST, host.address)
ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity) ssh_options_set(session, SSH_OPTIONS_LOG_VERBOSITY, &verbosity)
ssh_options_set(session, SSH_OPTIONS_PORT, &host.port) ssh_options_set(session, SSH_OPTIONS_PORT, &host.port)
ssh_options_set(session, SSH_OPTIONS_USER, host.username)
let status = ssh_connect(session) let status = ssh_connect(session)
if status != SSH_OK { if status != SSH_OK {
@@ -99,13 +103,18 @@ class SSHHandler: ObservableObject {
} }
func disconnect() { func disconnect() {
guard session != nil else { return }
ssh_disconnect(session)
ssh_free(session)
withAnimation { authorized = false }
withAnimation { connected = false } withAnimation { connected = false }
withAnimation { authorized = false }
withAnimation { testSuceeded = nil } withAnimation { testSuceeded = nil }
session = nil
ssh_channel_send_eof(self.channel)
ssh_channel_close(self.channel)
ssh_channel_free(self.channel)
// self.channel = nil
ssh_disconnect(self.session)
ssh_free(self.session)
// self.session = nil
} }
func testExec() { func testExec() {
@@ -198,12 +207,16 @@ class SSHHandler: ObservableObject {
fileManager.createFile(atPath: tempPubkey.path(), contents: nil) fileManager.createFile(atPath: tempPubkey.path(), contents: nil)
fileManager.createFile(atPath: tempKey.path(), contents: nil) fileManager.createFile(atPath: tempKey.path(), contents: nil)
let attributes: [FileAttributeKey: Any] = [.posixPermissions: 0o600] try? pubInp.write(to: tempPubkey, options: .completeFileProtection)
try? fileManager.setAttributes(attributes, ofItemAtPath: tempPubkey.path()) try? privInp.write(to: tempKey, options: .completeFileProtection)
try? fileManager.setAttributes(attributes, ofItemAtPath: tempKey.path())
try? pubInp.write(to: tempPubkey) let attributes: [FileAttributeKey: Any] = [.posixPermissions: 0o600]
try? privInp.write(to: tempKey) do {
try fileManager.setAttributes(attributes, ofItemAtPath: tempPubkey.path())
try fileManager.setAttributes(attributes, ofItemAtPath: tempKey.path())
} catch {
logCritical("permission settig failed\(error.localizedDescription)")
}
var pubkey: ssh_key? var pubkey: ssh_key?
if ssh_pki_import_pubkey_file(tempPubkey.path(), &pubkey) != 0 { if ssh_pki_import_pubkey_file(tempPubkey.path(), &pubkey) != 0 {
@@ -233,7 +246,6 @@ class SSHHandler: ObservableObject {
try? fileManager.removeItem(at: tempPubkey) try? fileManager.removeItem(at: tempPubkey)
try? fileManager.removeItem(at: tempKey) try? fileManager.removeItem(at: tempKey)
} }
return return
} }
@@ -297,10 +309,6 @@ class SSHHandler: ObservableObject {
} }
interactiveShellSession() interactiveShellSession()
// ssh_channel_close(channel)
// ssh_channel_send_eof(channel)
// ssh_channel_free(channel)
} }
private func interactiveShellSession() { private func interactiveShellSession() {
@@ -316,9 +324,25 @@ class SSHHandler: ObservableObject {
guard status == SSH_OK else { return } guard status == SSH_OK else { return }
} }
func asyncReadFromChannel() async -> String? {
return await withCheckedContinuation { continuation in
DispatchQueue.global(qos: .userInteractive).async {
let result = self.readFromChannel()
continuation.resume(returning: result)
}
}
}
func readFromChannel() -> String? { func readFromChannel() -> String? {
guard ssh_channel_is_open(channel) != 0 else { return nil } print(connected)
guard ssh_channel_is_eof(channel) == 0 else { return nil } if !connected {
return nil
}
guard connected else { return nil }
guard ssh_channel_is_open(channel) != 0 || ssh_channel_is_eof(channel) == 0 else {
disconnect()
return nil
}
var buffer: [CChar] = Array(repeating: 0, count: 512) var buffer: [CChar] = Array(repeating: 0, count: 512)
let nbytes = ssh_channel_read(channel, &buffer, UInt32(buffer.count), 0) let nbytes = ssh_channel_read(channel, &buffer, UInt32(buffer.count), 0)
@@ -342,8 +366,10 @@ class SSHHandler: ObservableObject {
func writeToChannel(_ string: String?) { func writeToChannel(_ string: String?) {
guard let string = string else { return } guard let string = string else { return }
guard ssh_channel_is_open(channel) != 0 else { return } guard ssh_channel_is_open(channel) != 0 || ssh_channel_is_eof(channel) == 0 else {
guard ssh_channel_is_eof(channel) == 0 else { return } disconnect()
return
}
var buffer: [CChar] = [] var buffer: [CChar] = []
for byte in string.utf8 { for byte in string.utf8 {

View File

@@ -23,15 +23,15 @@ class SSHTerminalView: TerminalView, TerminalViewDelegate {
super.init(frame: frame) super.init(frame: frame)
terminalDelegate = self terminalDelegate = self
sshQueue.async { [weak self] in sshQueue.async {
guard let handler = self?.handler else { return } guard let handler = self.handler else { return }
guard handler.channel != nil else { return }
while handler.connected { while handler.connected {
guard let handler = self?.handler else { break }
if let read = handler.readFromChannel() { if let read = handler.readFromChannel() {
DispatchQueue.main.async { self?.feed(text: read) } DispatchQueue.main.async { self.feed(text: read) }
} else { } else {
usleep(1_000_000) usleep(100_000)
} }
// self?.setNeedsDisplay() // self?.setNeedsDisplay()
} }