diff --git a/README.md b/README.md index 88b0891..16185a3 100644 --- a/README.md +++ b/README.md @@ -6,57 +6,13 @@ SSH and SFTP client library for React Native. ``` npm install react-native-ssh-sftp --save -react-native link react-native-ssh-sftp ``` -### iOS (only) - -NMSSH is required for iOS. - -1. Initialize Pod: - ``` - cd ios - pod init - ``` -2. Open Podfile and add: - ``` - target '[your project's name]' do - pod 'NMSSH', '2.2.8' - end - ``` -3. Install Pod: - ``` - pod install - ``` - -### Manual Link - -#### iOS - -1. In XCode, in the project navigator, right click `Libraries` ➜ `Add Files to [your project's name]` -2. Go to `node_modules` ➜ `react-native-ssh-sftp` and add `RNSSHClient.xcodeproj` -3. In XCode, in the project navigator, select your project. Add `libRNSSHClient.a` to your project's `Build Phases` ➜ `Link Binary With Libraries` - -#### Android - -1. Open up `android/app/src/main/java/[...]/MainActivity.java` - - Add `import com.reactlibrary.RNSshClientPackage;` to the imports at the top of the file - - Add `new RNSshClientPackage()` to the list returned by the `getPackages()` method -2. Append the following lines to `android/settings.gradle`: - ``` - include ':react-native-ssh-sftp' - project(':react-native-ssh-sftp').projectDir = new File(rootProject.projectDir, '../node_modules/react-native-ssh-sftp/android') - ``` -3. Insert the following lines inside the dependencies block in `android/app/build.gradle`: - ``` - compile project(':react-native-ssh-sftp') - ``` - ## Demo ![example](https://raw.githubusercontent.com/shaqian/react-native-ssh-sftp/master/example.gif) -- This library is also used in iOS app PiHelper. +- This library is also used in iOS app PiHelper.
@@ -66,6 +22,7 @@ NMSSH is required for iOS. ## Run demo ### iOS + ``` cd example cd ios @@ -76,6 +33,7 @@ react-native run-ios ``` ### Android + ``` cd example npm install @@ -85,26 +43,33 @@ react-native run-android ## Usage ### Create a client using password authentication + ```javascript -import SSHClient from 'react-native-ssh-sftp'; +import SSHClient from "react-native-ssh-sftp"; -let client = new SSHClient('10.0.0.10', 22, 'user', 'password', (error) => { - if (error) - console.warn(error); +let client = new SSHClient("10.0.0.10", 22, "user", "password", (error) => { + if (error) console.warn(error); }); ``` ### Create a client using public key authentication + ```javascript -import SSHClient from 'react-native-ssh-sftp'; +import SSHClient from "react-native-ssh-sftp"; -let client = new SSHClient('10.0.0.10', 22, 'user', {privateKey: '-----BEGIN RSA......'}, (error) => { - if (error) - console.warn(error); -}); +let client = new SSHClient( + "10.0.0.10", + 22, + "user", + { privateKey: "-----BEGIN RSA......" }, + (error) => { + if (error) console.warn(error); + } +); ``` - Public key authentication also supports: + ``` {privateKey: '-----BEGIN RSA......'} {privateKey: '-----BEGIN RSA......', publicKey: 'ssh-rsa AAAAB3NzaC1yc2EA......'} @@ -112,51 +77,53 @@ let client = new SSHClient('10.0.0.10', 22, 'user', {privateKey: '-----BEGIN RSA ``` ### Close client + ```javascript client.disconnect(); ``` ### Execute SSH command + ```javascript -var command = 'ls -l'; +var command = "ls -l"; client.execute(command, (error, output) => { - if (error) - console.warn(error); - if (output) - console.warn(output); + if (error) console.warn(error); + if (output) console.warn(output); }); ``` ### Shell -#### Start shell: +#### Start shell: + - Supported ptyType: vanilla, vt100, vt102, vt220, ansi, xterm + ```javascript -var ptyType = 'vanilla'; +var ptyType = "vanilla"; client.startShell(ptyType, (error) => { - if (error) - console.warn(error); + if (error) console.warn(error); }); ``` #### Read from shell: + ```javascript -client.on('Shell', (event) => { - if (event) - console.warn(event); +client.on("Shell", (event) => { + if (event) console.warn(event); }); ``` -#### Write to shell: +#### Write to shell: + ```javascript -var str = 'ls -l\n'; +var str = "ls -l\n"; client.writeToShell(str, (error) => { - if (error) - console.warn(error); + if (error) console.warn(error); }); ``` -#### Close shell: +#### Close shell: + ```javascript client.closeShell(); ``` @@ -164,67 +131,69 @@ client.closeShell(); ### SFTP #### Connect SFTP + ```javascript client.connectSFTP((error) => { - if (error) - console.warn(error); + if (error) console.warn(error); }); ``` -#### List directory: +#### List directory: + ```javascript -var path = '.'; +var path = "."; client.sftpLs(path, (error, response) => { - if (error) - console.warn(error); - if (response) - console.warn(response); + if (error) console.warn(error); + if (response) console.warn(response); }); ``` -#### Create directory: +#### Create directory: + ```javascript -client.sftpMkdir('dirName', (error) => { - if (error) - console.warn(error); +client.sftpMkdir("dirName", (error) => { + if (error) console.warn(error); }); ``` -#### Rename file or directory: +#### Rename file or directory: + ```javascript -client.sftpRename('oldName', 'newName', (error) => { - if (error) - console.warn(error); +client.sftpRename("oldName", "newName", (error) => { + if (error) console.warn(error); }); ``` -#### Remove directory: +#### Remove directory: + ```javascript -client.sftpRmdir('dirName', (error) => { - if (error) - console.warn(error); +client.sftpRmdir("dirName", (error) => { + if (error) console.warn(error); }); ``` -#### Remove file: +#### Remove file: + ```javascript -client.sftpRm('fileName', (error) => { - if (error) - console.warn(error); +client.sftpRm("fileName", (error) => { + if (error) console.warn(error); }); ``` -#### Download file: +#### Download file: + ```javascript -client.sftpDownload('[path-to-remote-file]', '[path-to-local-direcotry]', (error, downloadedFilePath) => { - if (error) - console.warn(error); - if (downloadedFilePath) - console.warn(downloadedFilePath); -}); +client.sftpDownload( + "[path-to-remote-file]", + "[path-to-local-direcotry]", + (error, downloadedFilePath) => { + if (error) console.warn(error); + if (downloadedFilePath) console.warn(downloadedFilePath); + } +); // Downlowd progress -client.on('DownloadProgress', (event) => { +client.on("DownloadProgress", (event) => { console.warn(event); }); @@ -232,15 +201,15 @@ client.on('DownloadProgress', (event) => { client.sftpCancelDownload(); ``` -#### Upload file: +#### Upload file: + ```javascript -client.sftpUpload('[path-to-local-file]', '[path-to-remote-directory]', (error) => { - if (error) - console.warn(error); +client.sftpUpload("[path-to-local-file]", "[path-to-remote-directory]", (error) => { + if (error) console.warn(error); }); // Upload progress -client.on('UploadProgress', (event) => { +client.on("UploadProgress", (event) => { console.warn(event); }); @@ -248,12 +217,13 @@ client.on('UploadProgress', (event) => { client.sftpCancelUpload(); ``` -#### Close SFTP: +#### Close SFTP (Android only): + ```javascript client.disconnectSFTP(); ``` ## Credits -* iOS SSH library: [NMSSH](https://github.com/NMSSH/NMSSH) -* Android SSH library: [JSch](http://www.jcraft.com/jsch/) +- iOS SSH library: [NMSSH](https://github.com/NMSSH/NMSSH) +- Android SSH library: [JSch](http://www.jcraft.com/jsch/) diff --git a/RNSSHClient.podspec b/RNSSHClient.podspec index a2962e1..35a6a15 100644 --- a/RNSSHClient.podspec +++ b/RNSSHClient.podspec @@ -12,8 +12,8 @@ Pod::Spec.new do |s| s.source = { :git => 'https://github.com/shaqian/react-native-ssh-sftp.git', :tag => s.version } s.source_files = 'ios/**/*.{h,m}' s.requires_arc = true - s.platforms = { :ios => "8.0", :tvos => "9.2" } + s.platforms = { :ios => "11.0", :tvos => "9.2" } s.dependency 'React' - s.dependency 'NMSSH' + s.dependency 'NMSSH', '2.3.1' end diff --git a/android/build.gradle b/android/build.gradle index cd4d6cd..7aa5d78 100644 --- a/android/build.gradle +++ b/android/build.gradle @@ -2,22 +2,27 @@ buildscript { repositories { jcenter() + google() } dependencies { - classpath 'com.android.tools.build:gradle:1.3.1' + classpath 'com.android.tools.build:gradle:3.2.1' } } apply plugin: 'com.android.library' +def DEFAULT_COMPILE_SDK_VERSION = 23 +def DEFAULT_BUILD_TOOLS_VERSION = "23.0.1" +def DEFAULT_TARGET_SDK_VERSION = 22 + android { - compileSdkVersion 23 - buildToolsVersion "23.0.1" + compileSdkVersion rootProject.hasProperty('compileSdkVersion') ? rootProject.compileSdkVersion : DEFAULT_COMPILE_SDK_VERSION + buildToolsVersion rootProject.hasProperty('buildToolsVersion') ? rootProject.buildToolsVersion : DEFAULT_BUILD_TOOLS_VERSION defaultConfig { minSdkVersion 16 - targetSdkVersion 22 + targetSdkVersion rootProject.hasProperty('targetSdkVersion') ? rootProject.targetSdkVersion : DEFAULT_TARGET_SDK_VERSION versionCode 1 versionName "1.0" } @@ -28,12 +33,13 @@ android { repositories { mavenCentral() + jcenter() } dependencies { - compile 'com.facebook.react:react-native:+' - compile 'com.jcraft:jsch:0.1.54' - compile 'com.fasterxml.jackson.core:jackson-core:2.9.0' - compile 'com.fasterxml.jackson.core:jackson-databind:2.9.0' + api 'com.facebook.react:react-native:+' + api 'com.jcraft:jsch:0.1.54' + api 'com.fasterxml.jackson.core:jackson-core:2.9.0' + api 'com.fasterxml.jackson.core:jackson-databind:2.9.0' } \ No newline at end of file diff --git a/android/src/main/java/com/reactlibrary/RNSshClientModule.java b/android/src/main/java/com/reactlibrary/RNSshClientModule.java index a8d97ac..3fae2b1 100644 --- a/android/src/main/java/com/reactlibrary/RNSshClientModule.java +++ b/android/src/main/java/com/reactlibrary/RNSshClientModule.java @@ -2,7 +2,7 @@ import android.os.Environment; import android.util.Log; -import android.support.annotation.Nullable; +import androidx.annotation.Nullable; import com.facebook.react.bridge.Arguments; import com.facebook.react.bridge.Callback; diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 0000000..3ac405d --- /dev/null +++ b/index.d.ts @@ -0,0 +1,35 @@ +declare module "react-native-ssh-sftp" { + type Callback = (error: string | any, output?: string) => void; + export type Handler = (output: string) => void; + export type PtyType = "vanilla" | "vt100" | "vt102" | "vt220" | "ansi" | "xterm"; + export type Event = "Shell"; + class SSHClient { + constructor( + address: string, + port: number, + username: string, + credential: string | { privateKey?: string; publicKey?: string; passphrase?: string }, + callback?: Callback + ); + startShell: (ptyType: PtyType, callback: Callback) => void; + writeToShell: (command: string, callback: Callback) => void; + on: (event: Event, handler: Handler) => void; + closeShell: () => void; + connect: (callback: Callback) => void; + connectSFTP: (callback: Callback) => void; + sftpLs: (path: string, callback: Callback) => void; + sftpRename: (oldPath: string, newPath: string, callback: Callback) => void; + sftpMkdir: (path: string, callback: Callback) => void; + sftpRm: (path: string, callback: Callback) => void; + sftpRmdir: (path: string, callback: Callback) => void; + sftpUpload: (filePath: string, path: string, callback: Callback) => void; + sftpCancelUpload: () => void; + sftpDownload: (path: string, toPath: string, callback: Callback) => void; + sftpCancelDownload: () => void; + disconnectSFTP: () => void; + disconnect: () => void; + execute: (command: string, callback: Callback) => void; + } + + export default SSHClient; +} diff --git a/index.js b/index.js index 73dc2c8..e4ce11f 100644 --- a/index.js +++ b/index.js @@ -1,9 +1,4 @@ -import { - Platform, - NativeModules, - NativeEventEmitter, - DeviceEventEmitter -} from 'react-native'; +import { Platform, NativeModules, NativeEventEmitter, DeviceEventEmitter } from "react-native"; const { RNSSHClient } = NativeModules; @@ -11,17 +6,19 @@ const RNSSHClientEmitter = new NativeEventEmitter(RNSSHClient); class SSHClient { // passwordOrKey: password or {privateKey: value, [publicKey: value, passphrase: value]} - constructor(host, port, username, passwordOrKey, callback) { - this._key = Math.floor((1 + Math.random()) * 0x10000).toString(16).substring(1); + constructor(host, port, username, passwordOrKey, callback) { + this._key = Math.floor((1 + Math.random()) * 0x10000) + .toString(16) + .substring(1); this.handlers = {}; this.host = host; this.port = port; this.username = username; this.passwordOrKey = passwordOrKey; this.connect(callback); - } + } - _handleEvent (event) { + _handleEvent(event) { if (this.handlers.hasOwnProperty(event.name) && this._key === event.key) { this.handlers[event.name](event.value); } @@ -34,20 +31,38 @@ class SSHClient { connect(callback) { if (Platform.OS === "android") { if (typeof this.passwordOrKey === "string") - RNSSHClient.connectToHostByPassword(this.host, this.port, this.username, this.passwordOrKey, this._key, + RNSSHClient.connectToHostByPassword( + this.host, + this.port, + this.username, + this.passwordOrKey, + this._key, (error) => { callback && callback(error); - }); + } + ); else - RNSSHClient.connectToHostByKey(this.host, this.port, this.username, this.passwordOrKey, this._key, + RNSSHClient.connectToHostByKey( + this.host, + this.port, + this.username, + this.passwordOrKey, + this._key, (error) => { callback && callback(error); - }); + } + ); } else { - RNSSHClient.connectToHost(this.host, this.port, this.username, this.passwordOrKey, this._key, + RNSSHClient.connectToHost( + this.host, + this.port, + this.username, + this.passwordOrKey, + this._key, (error) => { callback && callback(error); - }); + } + ); } } @@ -58,11 +73,11 @@ class SSHClient { } // ptyType: vanilla, vt100, vt102, vt220, ansi, xterm - startShell(ptyType, callback) { - if (Platform.OS === 'ios') { - this.shellListener = RNSSHClientEmitter.addListener('Shell', this._handleEvent.bind(this)); + startShell(ptyType, callback) { + if (Platform.OS === "ios") { + this.shellListener = RNSSHClientEmitter.addListener("Shell", this._handleEvent.bind(this)); } else { - this.shellListener = DeviceEventEmitter.addListener('Shell', this._handleEvent.bind(this)); + this.shellListener = DeviceEventEmitter.addListener("Shell", this._handleEvent.bind(this)); } RNSSHClient.startShell(this._key, ptyType, (error, response) => { callback && callback(error, response); @@ -86,12 +101,24 @@ class SSHClient { connectSFTP(callback) { RNSSHClient.connectSFTP(this._key, (error) => { callback && callback(error); - if (Platform.OS === 'ios') { - this.downloadProgressListener = RNSSHClientEmitter.addListener('DownloadProgress', this._handleEvent.bind(this)); - this.uploadProgressListener = RNSSHClientEmitter.addListener('UploadProgress', this._handleEvent.bind(this)); + if (Platform.OS === "ios") { + this.downloadProgressListener = RNSSHClientEmitter.addListener( + "DownloadProgress", + this._handleEvent.bind(this) + ); + this.uploadProgressListener = RNSSHClientEmitter.addListener( + "UploadProgress", + this._handleEvent.bind(this) + ); } else { - this.downloadProgressListener = DeviceEventEmitter.addListener('DownloadProgress', this._handleEvent.bind(this)); - this.uploadProgressListener = DeviceEventEmitter.addListener('UploadProgress', this._handleEvent.bind(this)); + this.downloadProgressListener = DeviceEventEmitter.addListener( + "DownloadProgress", + this._handleEvent.bind(this) + ); + this.uploadProgressListener = DeviceEventEmitter.addListener( + "UploadProgress", + this._handleEvent.bind(this) + ); } }); } @@ -147,24 +174,23 @@ class SSHClient { } disconnectSFTP() { - if (this.downloadProgressListener) { - this.downloadProgressListener.remove(); - this.downloadProgressListener = null; - } - if (this.uploadProgressListener) { - this.uploadProgressListener.remove(); - this.uploadProgressListener = null; + if (Platform.OS !== "ios") { + if (this.downloadProgressListener) { + this.downloadProgressListener.remove(); + this.downloadProgressListener = null; + } + if (this.uploadProgressListener) { + this.uploadProgressListener.remove(); + this.uploadProgressListener = null; + } + RNSSHClient.disconnectSFTP(this._key); } - RNSSHClient.disconnectSFTP(this._key); } disconnect() { - if (this.shellListener) - this.shellListener.remove(); - if (this.downloadProgressListener) - this.downloadProgressListener.remove(); - if (this.uploadProgressListener) - this.uploadProgressListener.remove(); + if (this.shellListener) this.shellListener.remove(); + if (this.downloadProgressListener) this.downloadProgressListener.remove(); + if (this.uploadProgressListener) this.uploadProgressListener.remove(); RNSSHClient.disconnect(this._key); } } diff --git a/ios/SSHClient.m b/ios/SSHClient.m index 1921b82..5fdd719 100644 --- a/ios/SSHClient.m +++ b/ios/SSHClient.m @@ -15,7 +15,7 @@ @implementation SSHClient - (instancetype)init { if ((self = [super init])) { - _session = [NMSSHSession new]; + _session = nil; _sftpSession = [[NMSFTP alloc] init]; _key = [[NSString alloc] init]; _downloadContinue = false; diff --git a/package.json b/package.json index 6dccaa2..9e56c4c 100644 --- a/package.json +++ b/package.json @@ -18,11 +18,11 @@ "author": "Qian Sha", "license": "MIT", "peerDependencies": { - "react-native": "^0.54.0" + "react-native": "*" }, - "repository" : - { - "type" : "git", - "url" : "https://github.com/shaqian/react-native-ssh-sftp.git" + "types": "./index.d.ts", + "repository": { + "type": "git", + "url": "https://github.com/shaqian/react-native-ssh-sftp.git" } }