Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Without the public constructor, the class is effectively internal + @discardableResults + configuring queue #35

Open
wants to merge 30 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
add8270
Without the public constructor, the class is effectively internal
migueldeicaza Apr 28, 2020
0e03231
Make the results of the fluid-like APIs to be discardable, to prevent…
migueldeicaza Apr 28, 2020
8285ab7
Allow for the callback queue to be set
migueldeicaza May 3, 2020
0ec13bd
Allow for logging to be specified
migueldeicaza May 8, 2020
33dc6fb
Upgrade libssh2 to 1.9.0
migueldeicaza May 8, 2020
4c58a54
Backport fix for timeout from github issues
migueldeicaza May 8, 2020
16a72b4
Upgrade libssh2
migueldeicaza May 8, 2020
50fedf4
Add my build script
migueldeicaza May 13, 2020
edd34a9
Update README
migueldeicaza Aug 7, 2020
658bb75
Build an XCFramework
migueldeicaza Oct 28, 2020
03e4fd1
Do not surface LibSSH2 to the swiftmodule, this fixes the 'no such mo…
migueldeicaza Oct 30, 2020
e6acfe2
Use SwiftPM
migueldeicaza May 30, 2021
57bcd02
Add
migueldeicaza May 30, 2021
f7b00a5
Experimentation
migueldeicaza Jun 2, 2021
1b9b874
Add new authentication system: byCallback, to allow me to wire up the…
migueldeicaza Jun 4, 2021
36366ec
Update README.md
migueldeicaza Jun 5, 2021
3f2dd00
Make this compile with older iOS, at the expense of no support for th…
migueldeicaza Jun 6, 2021
f85cd3b
Fix the build for macOS and SwiftPM
migueldeicaza Jun 17, 2021
0ee7ac9
Add support for known hosts
migueldeicaza Jun 18, 2021
2c68d2c
Add Fingerprint SHA256 type
migueldeicaza Jun 23, 2021
5d9d93a
No human should have lost as much time as I did when I introduced thi…
migueldeicaza Jun 23, 2021
231c8dd
Session.fingerprintBytes, new methods that returns an array of bytes,…
migueldeicaza Jun 23, 2021
fccbb74
Disable bitcode to work with the new SSH libraries
migueldeicaza Jun 24, 2021
7e62cfa
Forgo to commit this
migueldeicaza Jun 28, 2021
255fe29
Use my copy
migueldeicaza Jun 29, 2021
9a1c57a
Fix warnings
migueldeicaza Jun 29, 2021
6e8d842
Create swift.yml
migueldeicaza Jun 29, 2021
147db2c
Remove warning
migueldeicaza Jul 15, 2021
4e83a50
Add additional information when available on error returns
migueldeicaza Jul 30, 2021
64ce28f
Allows the session to be reused for more than one task at a time.
migueldeicaza Nov 11, 2021
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions .github/workflows/swift.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: Swift

on:
push:
branches: [ master ]
pull_request:
branches: [ master ]

jobs:
build:

runs-on: macos-latest

steps:
- uses: actions/checkout@v2
- name: Build
run: swift build -v
- name: Run tests
run: swift test -v
39 changes: 39 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
name: "SwiftSH",
platforms: [
.macOS(.v10_15),
.iOS(.v13)
],
products: [
// Products define the executables and libraries a package produces, and make them visible to other packages.
.library(
name: "CSwiftSH",
targets: ["CSwiftSH"]),
.library(
name: "SwiftSH",
targets: ["SwiftSH"]),
],
dependencies: [
// Dependencies declare other packages that this package depends on.
// .package(url: /* package url */, from: "1.0.0"),
.package(name: "CSSH", url: "https://github.com/migueldeicaza/Libssh2Prebuild.git", from: "1.9.0")
],
targets: [
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
// Targets can depend on other targets in this package, and on products in packages this package depends on.
.target(
name: "CSwiftSH",
dependencies: ["CSSH"]),
.target(
name: "SwiftSH",
dependencies: ["CSSH", "CSwiftSH"]),
.testTarget(
name: "SwiftSHTests",
dependencies: ["SwiftSH"]),
]
)
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,19 @@

# SwiftSH
[![Build Status](https://travis-ci.org/Frugghi/SwiftSH.svg?branch=master)](https://travis-ci.org/Frugghi/SwiftSH)

This is a fork of the original SwiftSH module with a handful of bug
fixes and changes that I wanted for my SwiftTerm emulator. The upstream
package does not seem active.

While Carthage, Pods and other platforms were originally supported, I
have not attempted to update those, I have now moved to SwiftPM for it,
which solved various issues for me.

Additionally, this add a callback-based authentication system, that you
can now use for authenticating using the secure enclave.

# Original Readme

[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![Pods](https://img.shields.io/cocoapods/v/SwiftSH.svg)](https://cocoapods.org/pods/SwiftSH)
[![Pod platforms](https://img.shields.io/cocoapods/p/SwiftSH.svg)](https://cocoapods.org/pods/SwiftSH)
Expand Down
10 changes: 5 additions & 5 deletions SwiftSH/Libssh2.m → Sources/CSwiftSH/Libssh2.m
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,14 @@
// SOFTWARE.
//

#import "Libssh2.h"
#import "libssh2.h"

@import Darwin;
@import Libssh2;
@import CSSH;
@import Foundation;

void trace_callback(LIBSSH2_SESSION *session, void *context, const char *data, size_t length) {
NSLog(@"Trace Callback");
NSLog(@"Trace Callback %s", data);
}

ssize_t send_callback(libssh2_socket_t socket, const void *buffer, size_t length, int flags, void **abstract) {
Expand Down Expand Up @@ -58,8 +58,8 @@ void disconnect_callback(LIBSSH2_SESSION *session, int reason, const char *messa
NSLog(@"Libssh2 disconnect");
}

void libssh2_setup_session_callbacks(void *session) {
libssh2_trace(session, 1);
void libssh2_setup_session_callbacks(void *session, int flags) {
libssh2_trace(session, flags);
libssh2_trace_sethandler(session, nil, &trace_callback);

libssh2_session_callback_set(session, LIBSSH2_CALLBACK_SEND, &send_callback);
Expand Down
2 changes: 1 addition & 1 deletion SwiftSH/Libssh2.h → Sources/CSwiftSH/include/Libssh2.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,4 +22,4 @@
// SOFTWARE.
//

void libssh2_setup_session_callbacks(void *session);
void libssh2_setup_session_callbacks(void *session, int flags);
54 changes: 33 additions & 21 deletions SwiftSH/Channel.swift → Sources/SwiftSH/Channel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,17 @@
// SOFTWARE.
//

open class SSHChannel: SSHSession {
open class SSHChannel {

// MARK: - Public variables

public fileprivate(set) var terminal: Terminal?
public fileprivate(set) var channel: SSHLibraryChannel!

public private(set) var session: SSHSession

// MARK: - Internal variables

internal var channel: SSHLibraryChannel!
internal let environment: [Environment]

// MARK: - Initialization
Expand All @@ -39,44 +41,53 @@ open class SSHChannel: SSHSession {
self.environment = environment
self.terminal = terminal

try super.init(sshLibrary: sshLibrary, host: host, port: port)
self.session = try SSHSession(sshLibrary: sshLibrary, host: host, port: port)

self.channel = session.session.makeChannel()
}

internal init(sshLibrary: SSHLibrary.Type = Libssh2.self, session: SSHSession, environment: [Environment] = [], terminal: Terminal? = nil) throws {
self.environment = environment
self.terminal = terminal

self.session = session

self.channel = self.session.makeChannel()
self.channel = session.session.makeChannel()
}

// MARK: - Open/Close

internal func open() throws {
assert(self.queue.current)
assert(session.queue.current)

// Check if we are authenticated
guard self.authenticated else {
throw SSHError.authenticationFailed
guard session.authenticated else {
throw SSHError.authenticationFailed(detail: "Open attempted, but we are not authenticated")
}

// Check if the channel is already open
guard !self.channel.opened else {
throw SSHError.Channel.alreadyOpen
}

self.log.debug("Opening the channel...")
session.log.debug("Opening the channel...")

// Set blocking mode
self.session.blocking = true
session.session.blocking = true

// Opening the channel
try self.channel.openChannel()

do {
// Set the environment's variables
self.log.debug("Environment: \(self.environment)")
session.log.debug("Environment: \(self.environment)")
for variable in self.environment {
try self.channel.setEnvironment(variable)
}

// Request the pseudo terminal
if let terminal = self.terminal {
self.log.debug("\(terminal) pseudo terminal requested")
session.log.debug("\(terminal) pseudo terminal requested")
try self.channel.requestPseudoTerminal(terminal)
}
} catch {
Expand All @@ -86,41 +97,42 @@ open class SSHChannel: SSHSession {
}

internal func close() {
assert(self.queue.current)
assert(session.queue.current)

self.log.debug("Closing the channel...")
session.log.debug("Closing the channel...")

// Set blocking mode
self.session.blocking = true
session.session.blocking = true

// Close the channel
do {
try self.channel.closeChannel()
} catch {
self.log.error("\(error)")
session.log.error("\(error)")
}
}

public override func disconnect(_ completion: (() -> Void)?) {
self.queue.async {
public func disconnect(_ completion: (() -> Void)?) {
session.queue.async {
self.close()

super.disconnect(completion)
self.session.disconnect(completion)
}
}

// MARK: - Terminal

@discardableResult
public func setTerminalSize(width: UInt, height: UInt) -> Self {
self.setTerminalSize(width: width, height: height, completion: nil)

return self
}

public func setTerminalSize(width: UInt, height: UInt, completion: SSHCompletionBlock?) {
self.queue.async(completion: completion) {
session.queue.async(completion: completion) {
guard let terminal = self.terminal else {
throw SSHError.badUse
throw SSHError.badUse(detail:"Terminal set size attempted, but the terminal has not been created yet"
)
}

// Check if the new size is different from the old one
Expand Down
44 changes: 29 additions & 15 deletions SwiftSH/Command.swift → Sources/SwiftSH/Command.swift
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
import Foundation

public class SSHCommand: SSHChannel {

Expand All @@ -35,14 +36,18 @@ public class SSHCommand: SSHChannel {
try super.init(sshLibrary: sshLibrary, host: host, port: port, environment: environment, terminal: terminal)
}

public override init(sshLibrary: SSHLibrary.Type = Libssh2.self, session: SSHSession, environment: [Environment] = [], terminal: Terminal? = nil) throws {
try super.init(sshLibrary: sshLibrary, session: session, environment: environment, terminal: terminal)
}

deinit {
self.cancelSources()
}

public override func close() {
self.cancelSources()

self.queue.async {
session.queue.async {
super.close()
}
}
Expand All @@ -63,7 +68,7 @@ public class SSHCommand: SSHChannel {
private var error: Data?

public func execute(_ command: String, completion: ((String, Data?, Error?) -> Void)?) {
self.queue.async(completion: { (error: Error?) in
session.queue.async(completion: { (error: Error?) in
if let error = error {
self.close()

Expand All @@ -79,9 +84,9 @@ public class SSHCommand: SSHChannel {
try self.open()

// Read the received data
self.socketSource = DispatchSource.makeReadSource(fileDescriptor: CFSocketGetNative(self.socket), queue: self.queue.queue)
self.socketSource = DispatchSource.makeReadSource(fileDescriptor: CFSocketGetNative(self.session.socket), queue: self.session.queue.queue)
guard let socketSource = self.socketSource else {
throw SSHError.allocation
throw SSHError.allocation(detail: "")
}

socketSource.setEventHandler { [weak self] in
Expand All @@ -96,7 +101,7 @@ public class SSHCommand: SSHChannel {
}

// Set non-blocking mode
self.session.blocking = false
self.session.session.blocking = false

// Read the result
var socketClosed = true
Expand All @@ -110,7 +115,7 @@ public class SSHCommand: SSHChannel {

socketClosed = false
} catch let error {
self.log.error("[STD] \(error)")
self.session.log.error("[STD] \(error)")
}

// Read the error
Expand All @@ -126,7 +131,7 @@ public class SSHCommand: SSHChannel {

socketClosed = false
} catch let error {
self.log.error("[ERR] \(error)")
self.session.log.error("[ERR] \(error)")
}

// Check if we can return the response
Expand All @@ -142,7 +147,7 @@ public class SSHCommand: SSHChannel {
error = SSHError.Command.execError(String(data: message, encoding: .utf8), message)
}

self.queue.callbackQueue.async {
self.session.queue.callbackQueue.async {
completion(command, result, error)
}
}
Expand All @@ -153,9 +158,9 @@ public class SSHCommand: SSHChannel {
}

// Create the timeout handler
self.timeoutSource = DispatchSource.makeTimerSource(queue: self.queue.queue)
self.timeoutSource = DispatchSource.makeTimerSource(queue: self.session.queue.queue)
guard let timeoutSource = self.timeoutSource else {
throw SSHError.allocation
throw SSHError.allocation(detail:"")
}

timeoutSource.setEventHandler { [weak self] in
Expand All @@ -168,21 +173,21 @@ public class SSHCommand: SSHChannel {
if let completion = completion {
let result = self.response

self.queue.callbackQueue.async {
completion(command, result, SSHError.timeout)
self.session.queue.callbackQueue.async {
completion(command, result, SSHError.timeout(detail:"timeout DispatchSource"))
}
}
}
timeoutSource.schedule(deadline: .now() + self.timeout, repeating: self.timeout, leeway: .seconds(10))
timeoutSource.schedule(deadline: .now() + self.session.timeout, repeating: self.session.timeout, leeway: .seconds(10))

// Set blocking mode
self.session.blocking = true
self.session.session.blocking = true

// Execute the command
try self.channel.exec(command)

// Set non-blocking mode
self.session.blocking = false
self.session.session.blocking = false

// Start listening for new data
timeoutSource.resume()
Expand All @@ -205,4 +210,13 @@ public class SSHCommand: SSHChannel {
}
}

public func connect () -> Self {
session.connect()
return self
}

public func authenticate (_ challenge: AuthenticationChallenge?) -> Self {
session.authenticate(challenge)
return self
}
}
File renamed without changes.
Loading