mirror of
https://github.com/neon443/ShhShell.git
synced 2026-03-11 13:26:16 +00:00
new icon
UI update: has an x if failed testexec, withanimation cleanup new hostkey variable getHostKey returns Data
This commit is contained in:
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"images" : [
|
"images" : [
|
||||||
{
|
{
|
||||||
|
"filename" : "ShhShell.png",
|
||||||
"idiom" : "universal",
|
"idiom" : "universal",
|
||||||
"platform" : "ios",
|
"platform" : "ios",
|
||||||
"size" : "1024x1024"
|
"size" : "1024x1024"
|
||||||
|
|||||||
BIN
Resources/Assets.xcassets/AppIcon.appiconset/ShhShell.png
Normal file
BIN
Resources/Assets.xcassets/AppIcon.appiconset/ShhShell.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 108 KiB |
BIN
ShhShell.pxd
Normal file
BIN
ShhShell.pxd
Normal file
Binary file not shown.
@@ -10,13 +10,15 @@ import LibSSH
|
|||||||
import OSLog
|
import OSLog
|
||||||
|
|
||||||
class SSHHandler: ObservableObject {
|
class SSHHandler: ObservableObject {
|
||||||
var session: ssh_session?
|
private var session: ssh_session?
|
||||||
|
|
||||||
@Published var authorized: Bool = false
|
@Published var authorized: Bool = false
|
||||||
@Published var username: String
|
@Published var username: String
|
||||||
@Published var password: String
|
@Published var password: String
|
||||||
@Published var address: String
|
@Published var address: String
|
||||||
@Published var port: Int
|
@Published var port: Int
|
||||||
|
|
||||||
|
@Published var hostkey: Data?
|
||||||
|
|
||||||
private let logger = Logger(subsystem: "xy", category: "sshHandler")
|
private let logger = Logger(subsystem: "xy", category: "sshHandler")
|
||||||
|
|
||||||
@@ -38,22 +40,23 @@ class SSHHandler: ObservableObject {
|
|||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
func getHostkey() {
|
func getHostkey() -> Data? {
|
||||||
var hostkey: ssh_key?
|
var hostkey: ssh_key?
|
||||||
ssh_get_server_publickey(session, &hostkey)
|
ssh_get_server_publickey(session, &hostkey)
|
||||||
|
|
||||||
var hostkeyB64: UnsafeMutablePointer<CChar>? = nil
|
var hostkeyB64: UnsafeMutablePointer<CChar>? = nil
|
||||||
if ssh_pki_export_pubkey_base64(hostkey, &hostkeyB64) == SSH_OK {
|
|
||||||
if let hostkeyB64 = hostkeyB64 {
|
let status = ssh_pki_export_pubkey_base64(hostkey, &hostkeyB64)
|
||||||
print(String(cString: hostkeyB64))
|
guard status == SSH_OK else { return nil }
|
||||||
}
|
guard let data = hostkeyB64 else { return nil }
|
||||||
}
|
|
||||||
|
return Data(base64Encoded: String(cString: data))
|
||||||
}
|
}
|
||||||
|
|
||||||
func connect() -> Bool {
|
func connect() -> Bool {
|
||||||
defer {
|
defer {
|
||||||
getAuthMethods()
|
getAuthMethods()
|
||||||
getHostkey()
|
self.hostkey = getHostkey()
|
||||||
}
|
}
|
||||||
|
|
||||||
var verbosity: Int = 0
|
var verbosity: Int = 0
|
||||||
@@ -85,6 +88,7 @@ class SSHHandler: ObservableObject {
|
|||||||
ssh_free(session)
|
ssh_free(session)
|
||||||
session = nil
|
session = nil
|
||||||
authorized = false
|
authorized = false
|
||||||
|
hostkey = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func testExec() -> Bool {
|
func testExec() -> Bool {
|
||||||
@@ -262,7 +266,7 @@ class SSHHandler: ObservableObject {
|
|||||||
ssh_channel_free(channel)
|
ssh_channel_free(channel)
|
||||||
}
|
}
|
||||||
|
|
||||||
func interactiveShellSession(channel: ssh_channel) {
|
private func interactiveShellSession(channel: ssh_channel) {
|
||||||
var status: CInt
|
var status: CInt
|
||||||
|
|
||||||
status = ssh_channel_request_pty(channel)
|
status = ssh_channel_request_pty(channel)
|
||||||
|
|||||||
@@ -10,19 +10,28 @@ import SwiftUI
|
|||||||
struct ContentView: View {
|
struct ContentView: View {
|
||||||
@ObservedObject var handler: SSHHandler
|
@ObservedObject var handler: SSHHandler
|
||||||
@State var connected: Bool = false
|
@State var connected: Bool = false
|
||||||
@State var testSucceded: Bool = false
|
@State var testSucceded: Bool?
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack {
|
VStack {
|
||||||
Text(connected ? "connected" : "not connected")
|
Text(connected ? "connected" : "not connected")
|
||||||
.foregroundStyle(connected ? .green : .red)
|
.foregroundStyle(connected ? .green : .red)
|
||||||
|
|
||||||
Text(handler.authorized ? "authorized" : "unauthorized")
|
Text(handler.authorized ? "authorized" : "unauthorized")
|
||||||
.foregroundStyle(handler.authorized ? .green : .red)
|
.foregroundStyle(handler.authorized ? .green : .red)
|
||||||
if testSucceded {
|
|
||||||
Image(systemName: "checkmark.circle")
|
if let testSucceded = testSucceded {
|
||||||
|
Image(systemName: testSucceded ? "checkmark.circle" : "xmark.circle")
|
||||||
|
.foregroundStyle(testSucceded ? .green : .red)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if handler.hostkey != nil {
|
||||||
|
Text("Hostkey: \(handler.hostkey!.base64EncodedString())")
|
||||||
|
}
|
||||||
|
|
||||||
TextField("address", text: $handler.address)
|
TextField("address", text: $handler.address)
|
||||||
.textFieldStyle(.roundedBorder)
|
.textFieldStyle(.roundedBorder)
|
||||||
|
|
||||||
TextField(
|
TextField(
|
||||||
"port",
|
"port",
|
||||||
text: Binding(
|
text: Binding(
|
||||||
@@ -31,8 +40,10 @@ struct ContentView: View {
|
|||||||
)
|
)
|
||||||
.keyboardType(.numberPad)
|
.keyboardType(.numberPad)
|
||||||
.textFieldStyle(.roundedBorder)
|
.textFieldStyle(.roundedBorder)
|
||||||
|
|
||||||
TextField("username", text: $handler.username)
|
TextField("username", text: $handler.username)
|
||||||
.textFieldStyle(.roundedBorder)
|
.textFieldStyle(.roundedBorder)
|
||||||
|
|
||||||
TextField("password", text: $handler.password)
|
TextField("password", text: $handler.password)
|
||||||
.textFieldStyle(.roundedBorder)
|
.textFieldStyle(.roundedBorder)
|
||||||
|
|
||||||
@@ -42,20 +53,28 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
handler.authWithPw()
|
handler.authWithPw()
|
||||||
}
|
}
|
||||||
|
.disabled(connected)
|
||||||
|
|
||||||
Button("disconnect") {
|
Button("disconnect") {
|
||||||
handler.disconnect()
|
handler.disconnect()
|
||||||
withAnimation { testSucceded = false }
|
withAnimation { testSucceded = false }
|
||||||
withAnimation { connected = false}
|
withAnimation { connected = false }
|
||||||
|
withAnimation { testSucceded = nil }
|
||||||
}
|
}
|
||||||
|
.disabled(!connected)
|
||||||
|
|
||||||
Button("run a test command") {
|
Button("run a test command") {
|
||||||
withAnimation {
|
if handler.testExec() {
|
||||||
if handler.testExec() {
|
withAnimation { testSucceded = true }
|
||||||
testSucceded = true
|
} else {
|
||||||
} else {
|
withAnimation { testSucceded = false }
|
||||||
testSucceded = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.disabled(!(connected && handler.authorized))
|
||||||
|
|
||||||
|
Button("request a shell") {
|
||||||
|
handler.openShell()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user