mirror of
https://github.com/neon443/ShhShell.git
synced 2026-03-11 13:26:16 +00:00
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:
@@ -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:
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
@@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user