├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── android
├── .classpath
├── .gitignore
├── .project
├── .settings
│ └── org.eclipse.buildship.core.prefs
├── build.gradle
├── gradle.properties
├── settings.gradle
└── src
│ └── main
│ ├── AndroidManifest.xml
│ └── java
│ └── sq
│ └── flutter
│ └── ssh
│ └── SshPlugin.java
├── example
├── .gitignore
├── .metadata
├── README.md
├── android
│ ├── .gitignore
│ ├── .project
│ ├── .settings
│ │ └── org.eclipse.buildship.core.prefs
│ ├── app
│ │ ├── .classpath
│ │ ├── .project
│ │ ├── .settings
│ │ │ └── org.eclipse.buildship.core.prefs
│ │ ├── build.gradle
│ │ └── src
│ │ │ └── main
│ │ │ ├── AndroidManifest.xml
│ │ │ ├── java
│ │ │ └── sq
│ │ │ │ └── flutter
│ │ │ │ └── sshexample
│ │ │ │ └── MainActivity.java
│ │ │ └── res
│ │ │ ├── drawable
│ │ │ └── launch_background.xml
│ │ │ ├── mipmap-hdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-mdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxhdpi
│ │ │ └── ic_launcher.png
│ │ │ ├── mipmap-xxxhdpi
│ │ │ └── ic_launcher.png
│ │ │ └── values
│ │ │ └── styles.xml
│ ├── build.gradle
│ ├── gradle.properties
│ ├── gradle
│ │ └── wrapper
│ │ │ ├── gradle-wrapper.jar
│ │ │ └── gradle-wrapper.properties
│ ├── gradlew
│ ├── gradlew.bat
│ └── settings.gradle
├── ios
│ ├── .gitignore
│ ├── Flutter
│ │ ├── AppFrameworkInfo.plist
│ │ ├── Debug.xcconfig
│ │ └── Release.xcconfig
│ ├── Podfile
│ ├── Podfile.lock
│ ├── Runner.xcodeproj
│ │ ├── project.pbxproj
│ │ ├── project.xcworkspace
│ │ │ └── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ └── xcschemes
│ │ │ └── Runner.xcscheme
│ ├── Runner.xcworkspace
│ │ ├── contents.xcworkspacedata
│ │ └── xcshareddata
│ │ │ ├── IDEWorkspaceChecks.plist
│ │ │ └── WorkspaceSettings.xcsettings
│ └── Runner
│ │ ├── AppDelegate.h
│ │ ├── AppDelegate.m
│ │ ├── Assets.xcassets
│ │ ├── AppIcon.appiconset
│ │ │ ├── Contents.json
│ │ │ ├── Icon-App-1024x1024@1x.png
│ │ │ ├── Icon-App-20x20@1x.png
│ │ │ ├── Icon-App-20x20@2x.png
│ │ │ ├── Icon-App-20x20@3x.png
│ │ │ ├── Icon-App-29x29@1x.png
│ │ │ ├── Icon-App-29x29@2x.png
│ │ │ ├── Icon-App-29x29@3x.png
│ │ │ ├── Icon-App-40x40@1x.png
│ │ │ ├── Icon-App-40x40@2x.png
│ │ │ ├── Icon-App-40x40@3x.png
│ │ │ ├── Icon-App-60x60@2x.png
│ │ │ ├── Icon-App-60x60@3x.png
│ │ │ ├── Icon-App-76x76@1x.png
│ │ │ ├── Icon-App-76x76@2x.png
│ │ │ └── Icon-App-83.5x83.5@2x.png
│ │ └── LaunchImage.imageset
│ │ │ ├── Contents.json
│ │ │ ├── LaunchImage.png
│ │ │ ├── LaunchImage@2x.png
│ │ │ ├── LaunchImage@3x.png
│ │ │ └── README.md
│ │ ├── Base.lproj
│ │ ├── LaunchScreen.storyboard
│ │ └── Main.storyboard
│ │ ├── Info.plist
│ │ └── main.m
├── lib
│ └── main.dart
├── pubspec.yaml
├── ssh_example.iml
├── ssh_example_android.iml
└── test
│ └── widget_test.dart
├── ios
├── .gitignore
├── Assets
│ └── .gitkeep
├── Classes
│ ├── SSHClient.h
│ ├── SSHClient.m
│ ├── SshPlugin.h
│ └── SshPlugin.m
└── ssh.podspec
├── lib
└── ssh.dart
├── pubspec.yaml
├── ssh.iml
└── ssh_android.iml
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 | .dart_tool/
3 |
4 | .packages
5 | .pub/
6 | pubspec.lock
7 |
8 | build/
9 |
10 | .idea/
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## 0.0.7
2 |
3 | * Moved getInputStream before channel connect on Android.
4 | * Set minimum SDK version to 2.1.0.
5 |
6 | ## 0.0.6
7 |
8 | * Set compileSdkVersion to 28, fixing build error "Execution failed for task ':ssh:verifyReleaseResources'"
9 | * Set uuid plugin version to ^2.0.0.
10 |
11 | ## 0.0.5
12 |
13 | * Fixed download exception on Android due to [this issue](https://github.com/flutter/flutter/issues/34993).
14 |
15 | ## 0.0.4
16 |
17 | * Changed disconnect() method to sync.
18 | * Fixed Android crash issue "Methods marked with @UiThread must be executed on the main thread.".
19 |
20 | ## 0.0.3
21 |
22 | * Fixed invokeMethod missing indirection.
23 | * Add note about OpenSSH keys, add passphrase to key example.
24 |
25 | ## 0.0.2
26 |
27 | * Check if client is null before getting session in Android.
28 |
29 | ## 0.0.1
30 |
31 | * Initial release.
32 |
33 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) 2018 Qian Sha
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is
8 | furnished to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice shall be included in all
11 | copies or substantial portions of the Software.
12 |
13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
14 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
15 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
16 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
17 | DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
18 | OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
19 | OR OTHER DEALINGS IN THE SOFTWARE.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ssh
2 |
3 | SSH and SFTP client for Flutter. Wraps iOS library [NMSSH](https://github.com/NMSSH/NMSSH) and Android library [JSch](http://www.jcraft.com/jsch/).
4 |
5 | ## Installation
6 |
7 | Add `ssh` as a [dependency in your pubspec.yaml file](https://flutter.io/using-packages/).
8 |
9 | ## Known issue
10 |
11 | - Platform exception in release mode for Android:
12 |
13 | ```
14 | PlatformException(connection_failure, java.lang.ClassNotFoundException: com.jcraft.jsch.jce.Random, null)
15 | ```
16 |
17 | There are 2 workarounds:
18 |
19 | - Disable shrink:
20 |
21 | `flutter build apk --no-shrink`
22 |
23 | - Configure proguard-rules. Refer to [this comment](https://github.com/shaqian/flutter_ssh/issues/27#issuecomment-599180850) for details.
24 |
25 | ## Usage
26 |
27 | ### Create a client using password authentication
28 | ```dart
29 | import 'package:ssh/ssh.dart';
30 |
31 | var client = new SSHClient(
32 | host: "my.sshtest",
33 | port: 22,
34 | username: "sha",
35 | passwordOrKey: "Password01.",
36 | );
37 | ```
38 |
39 | ### Create a client using public key authentication
40 | ```dart
41 | import 'package:ssh/ssh.dart';
42 |
43 | var client = new SSHClient(
44 | host: "my.sshtest",
45 | port: 22,
46 | username: "sha",
47 | passwordOrKey: {
48 | "privateKey": """-----BEGIN RSA PRIVATE KEY-----
49 | ......
50 | -----END RSA PRIVATE KEY-----""",
51 | "passphrase": "passphrase-for-key",
52 | },
53 | );
54 | ```
55 |
56 | #### OpenSSH keys
57 |
58 | Recent versions of OpenSSH introduce a proprietary key format that is not supported by most other software, including this one, you must convert it to a PEM-format RSA key using the `puttygen` tool. On Windows this is a graphical tool. On the Mac, install it per [these instructions](https://www.ssh.com/ssh/putty/mac/). On Linux install your distribution's `putty` or `puttygen` packages.
59 |
60 | * Temporarily remove the passphrase from the original key (enter blank password as the new password)
61 | `ssh-keygen -p -f id_rsa`
62 | * convert to RSA PEM format
63 | `puttygen id_rsa -O private-openssh -o id_rsa_unencrypted`
64 | * re-apply the passphrase to the original key
65 | `ssh-keygen -p -f id_rsa`
66 | * apply a passphrase to the converted key:
67 | `puttygen id_rsa_unencrypted -P -O private-openssh -o id_rsa_flutter`
68 | * remove the unencrypted version:
69 | `rm id_rsa_unencrypted`
70 |
71 | ### Connect client
72 | ```dart
73 | await client.connect();
74 | ```
75 |
76 | ### Close client
77 | ```dart
78 | await client.disconnect();
79 | ```
80 |
81 | ### Execute SSH command
82 | ```dart
83 | var result = await client.execute("ps");
84 | ```
85 |
86 | ### Shell
87 |
88 | #### Start shell:
89 | - Supported ptyType: vanilla, vt100, vt102, vt220, ansi, xterm
90 | ```dart
91 | var result = await client.startShell(
92 | ptyType: "xterm", // defaults to vanilla
93 | callback: (dynamic res) {
94 | print(res); // read from shell
95 | }
96 | );
97 | ```
98 |
99 | #### Write to shell:
100 | ```dart
101 | await client.writeToShell("ls\n");
102 | ```
103 |
104 | #### Close shell:
105 | ```dart
106 | await client.closeShell();
107 | ```
108 |
109 | ### SFTP
110 |
111 | #### Connect SFTP:
112 | ```dart
113 | await client.connectSFTP();
114 | ```
115 |
116 | #### List directory:
117 | ```dart
118 | var array = await client.sftpLs("/home"); // defaults to .
119 | ```
120 |
121 | #### Create directory:
122 | ```dart
123 | await client.sftpMkdir("testdir");
124 | ```
125 |
126 | #### Rename file or directory:
127 | ```dart
128 | await client.sftpRename(
129 | oldPath: "testfile",
130 | newPath: "newtestfile",
131 | );
132 | ```
133 |
134 | #### Remove directory:
135 | ```dart
136 | await client.sftpRmdir("testdir");
137 | ```
138 |
139 | #### Remove file:
140 | ```dart
141 | await client.sftpRm("testfile");
142 | ```
143 |
144 | #### Download file:
145 | ```dart
146 | var filePath = await client.sftpDownload(
147 | path: "testfile",
148 | toPath: tempPath,
149 | callback: (progress) {
150 | print(progress); // read download progress
151 | },
152 | );
153 |
154 | // Cancel download:
155 | await client.sftpCancelDownload();
156 | ```
157 |
158 | #### Upload file:
159 | ```dart
160 | await client.sftpUpload(
161 | path: filePath,
162 | toPath: ".",
163 | callback: (progress) {
164 | print(progress); // read upload progress
165 | },
166 | );
167 |
168 | // Cancel upload:
169 | await client.sftpCancelUpload();
170 | ```
171 |
172 | #### Close SFTP:
173 | ```dart
174 | await client.disconnectSFTP();
175 | ```
176 |
177 | ## Demo
178 |
179 | Refer to the [example](https://github.com/shaqian/flutter_ssh/tree/master/example).
180 |
--------------------------------------------------------------------------------
/android/.classpath:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/android/.gitignore:
--------------------------------------------------------------------------------
1 | *.iml
2 | .gradle
3 | /local.properties
4 | /.idea/workspace.xml
5 | /.idea/libraries
6 | .DS_Store
7 | /build
8 | /captures
9 |
--------------------------------------------------------------------------------
/android/.project:
--------------------------------------------------------------------------------
1 |
2 |
3 | ssh
4 | Project ssh created by Buildship.
5 |
6 |
7 |
8 |
9 | org.eclipse.jdt.core.javabuilder
10 |
11 |
12 |
13 |
14 | org.eclipse.buildship.core.gradleprojectbuilder
15 |
16 |
17 |
18 |
19 |
20 | org.eclipse.jdt.core.javanature
21 | org.eclipse.buildship.core.gradleprojectnature
22 |
23 |
24 |
--------------------------------------------------------------------------------
/android/.settings/org.eclipse.buildship.core.prefs:
--------------------------------------------------------------------------------
1 | connection.project.dir=../example/android
2 | eclipse.preferences.version=1
3 |
--------------------------------------------------------------------------------
/android/build.gradle:
--------------------------------------------------------------------------------
1 | group 'sq.flutter.ssh'
2 | version '1.0-SNAPSHOT'
3 |
4 | buildscript {
5 | repositories {
6 | google()
7 | jcenter()
8 | }
9 |
10 | dependencies {
11 | classpath 'com.android.tools.build:gradle:3.1.2'
12 | }
13 | }
14 |
15 | rootProject.allprojects {
16 | repositories {
17 | google()
18 | jcenter()
19 | }
20 | }
21 |
22 | apply plugin: 'com.android.library'
23 |
24 | android {
25 | compileSdkVersion 28
26 |
27 | defaultConfig {
28 | minSdkVersion 16
29 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
30 | }
31 | lintOptions {
32 | disable 'InvalidPackage'
33 | }
34 |
35 | dependencies {
36 | implementation 'com.jcraft:jsch:0.1.54'
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/android/gradle.properties:
--------------------------------------------------------------------------------
1 | org.gradle.jvmargs=-Xmx1536M
2 |
--------------------------------------------------------------------------------
/android/settings.gradle:
--------------------------------------------------------------------------------
1 | rootProject.name = 'ssh'
2 |
--------------------------------------------------------------------------------
/android/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
--------------------------------------------------------------------------------
/android/src/main/java/sq/flutter/ssh/SshPlugin.java:
--------------------------------------------------------------------------------
1 | package sq.flutter.ssh;
2 |
3 | import android.util.Log;
4 | import android.os.Handler;
5 | import android.os.Looper;
6 |
7 | import com.jcraft.jsch.Channel;
8 | import com.jcraft.jsch.ChannelExec;
9 | import com.jcraft.jsch.ChannelSftp;
10 | import com.jcraft.jsch.ChannelSftp.LsEntry;
11 | import com.jcraft.jsch.ChannelShell;
12 | import com.jcraft.jsch.JSch;
13 | import com.jcraft.jsch.JSchException;
14 | import com.jcraft.jsch.Session;
15 | import com.jcraft.jsch.SftpException;
16 | import com.jcraft.jsch.SftpProgressMonitor;
17 |
18 | import java.io.BufferedReader;
19 | import java.io.DataOutputStream;
20 | import java.io.File;
21 | import java.io.IOException;
22 | import java.io.InputStream;
23 | import java.io.InputStreamReader;
24 | import java.util.ArrayList;
25 | import java.util.HashMap;
26 | import java.util.List;
27 | import java.util.Map;
28 | import java.util.Properties;
29 | import java.util.Vector;
30 | import java.text.SimpleDateFormat;
31 | import java.util.Date;
32 |
33 | import io.flutter.plugin.common.EventChannel;
34 | import io.flutter.plugin.common.EventChannel.EventSink;
35 | import io.flutter.plugin.common.EventChannel.StreamHandler;
36 | import io.flutter.plugin.common.MethodCall;
37 | import io.flutter.plugin.common.MethodChannel;
38 | import io.flutter.plugin.common.MethodChannel.MethodCallHandler;
39 | import io.flutter.plugin.common.MethodChannel.Result;
40 | import io.flutter.plugin.common.PluginRegistry.Registrar;
41 |
42 | /** SshPlugin */
43 | public class SshPlugin implements MethodCallHandler, StreamHandler {
44 | /** Plugin registration. */
45 | public static void registerWith(Registrar registrar) {
46 | final MethodChannel channel = new MethodChannel(registrar.messenger(), "ssh");
47 | final EventChannel eventChannel = new EventChannel(registrar.messenger(), "shell_sftp");
48 | final SshPlugin instance = new SshPlugin();
49 | channel.setMethodCallHandler(instance);
50 | eventChannel.setStreamHandler(instance);
51 | }
52 |
53 | // MethodChannel.Result wrapper that responds on the platform thread.
54 | private static class MethodResultWrapper implements Result {
55 | private Result methodResult;
56 | private Handler handler;
57 |
58 | MethodResultWrapper(Result result) {
59 | methodResult = result;
60 | handler = new Handler(Looper.getMainLooper());
61 | }
62 |
63 | @Override
64 | public void success(final Object result) {
65 | handler.post(
66 | new Runnable() {
67 | @Override
68 | public void run() {
69 | methodResult.success(result);
70 | }
71 | });
72 | }
73 |
74 | @Override
75 | public void error(
76 | final String errorCode, final String errorMessage, final Object errorDetails) {
77 | handler.post(
78 | new Runnable() {
79 | @Override
80 | public void run() {
81 | methodResult.error(errorCode, errorMessage, errorDetails);
82 | }
83 | });
84 | }
85 |
86 | @Override
87 | public void notImplemented() {
88 | handler.post(
89 | new Runnable() {
90 | @Override
91 | public void run() {
92 | methodResult.notImplemented();
93 | }
94 | });
95 | }
96 | }
97 |
98 | private static class MainThreadEventSink implements EventChannel.EventSink {
99 | private EventChannel.EventSink eventSink;
100 | private Handler handler;
101 |
102 | MainThreadEventSink(EventChannel.EventSink eventSink) {
103 | this.eventSink = eventSink;
104 | handler = new Handler(Looper.getMainLooper());
105 | }
106 |
107 | @Override
108 | public void success(final Object o) {
109 | handler.post(new Runnable() {
110 | @Override
111 | public void run() {
112 | eventSink.success(o);
113 | }
114 | });
115 | }
116 |
117 | @Override
118 | public void error(final String s, final String s1, final Object o) {
119 | handler.post(new Runnable() {
120 | @Override
121 | public void run() {
122 | eventSink.error(s, s1, o);
123 | }
124 | });
125 | }
126 |
127 | @Override
128 | public void endOfStream() {
129 | handler.post(new Runnable() {
130 | @Override
131 | public void run() {
132 | eventSink.endOfStream();
133 | }
134 | });
135 | }
136 | }
137 |
138 | @Override
139 | public void onMethodCall(MethodCall call, Result rawResult) {
140 | Result result = new MethodResultWrapper(rawResult);
141 | if (call.method.equals("connectToHost")) {
142 | connectToHost((HashMap) call.arguments, result);
143 | } else if (call.method.equals("execute")) {
144 | execute((HashMap) call.arguments, result);
145 | } else if (call.method.equals("portForwardL")) {
146 | portForwardL((HashMap) call.arguments, result);
147 | } else if (call.method.equals("startShell")) {
148 | startShell((HashMap) call.arguments, result);
149 | } else if (call.method.equals("writeToShell")) {
150 | writeToShell((HashMap) call.arguments, result);
151 | } else if (call.method.equals("closeShell")) {
152 | closeShell((HashMap) call.arguments);
153 | } else if (call.method.equals("connectSFTP")) {
154 | connectSFTP((HashMap) call.arguments, result);
155 | } else if (call.method.equals("sftpLs")) {
156 | sftpLs((HashMap) call.arguments, result);
157 | } else if (call.method.equals("sftpRename")) {
158 | sftpRename((HashMap) call.arguments, result);
159 | } else if (call.method.equals("sftpMkdir")) {
160 | sftpMkdir((HashMap) call.arguments, result);
161 | } else if (call.method.equals("sftpRm")) {
162 | sftpRm((HashMap) call.arguments, result);
163 | } else if (call.method.equals("sftpRmdir")) {
164 | sftpRmdir((HashMap) call.arguments, result);
165 | } else if (call.method.equals("sftpDownload")) {
166 | sftpDownload((HashMap) call.arguments, result);
167 | } else if (call.method.equals("sftpUpload")) {
168 | sftpUpload((HashMap) call.arguments, result);
169 | } else if (call.method.equals("sftpCancelDownload")) {
170 | sftpCancelDownload((HashMap) call.arguments);
171 | } else if (call.method.equals("sftpCancelUpload")) {
172 | sftpCancelUpload((HashMap) call.arguments);
173 | } else if (call.method.equals("disconnectSFTP")) {
174 | disconnectSFTP((HashMap) call.arguments);
175 | } else if (call.method.equals("disconnect")) {
176 | disconnect((HashMap) call.arguments);
177 | } else if (call.method.equals("isConnected")) {
178 | isConnected((HashMap) call.arguments, result);
179 | } else {
180 | result.notImplemented();
181 | }
182 | }
183 |
184 | private class SSHClient {
185 | Session _session;
186 | String _key;
187 | BufferedReader _bufferedReader;
188 | DataOutputStream _dataOutputStream;
189 | Channel _channel = null;
190 | ChannelSftp _sftpSession = null;
191 | Boolean _downloadContinue = false;
192 | Boolean _uploadContinue = false;
193 | }
194 |
195 | private static final String LOGTAG = "SshPlugin";
196 |
197 | Map clientPool = new HashMap<>();
198 | private EventSink eventSink;
199 |
200 | private SSHClient getClient(final String key, final Result result) {
201 | SSHClient client = clientPool.get(key);
202 | if (client == null)
203 | result.error("unknown_client", "Unknown client", null);
204 | return client;
205 | }
206 |
207 | @Override
208 | public void onListen(Object arguments, EventSink events) {
209 | this.eventSink = new MainThreadEventSink(events);
210 | }
211 |
212 | @Override
213 | public void onCancel(Object arguments) {
214 | this.eventSink = null;
215 | }
216 |
217 | private void connectToHost(final HashMap args, final Result result) {
218 | new Thread(new Runnable() {
219 | public void run() {
220 | try {
221 | String key = args.get("id").toString();
222 | String host = args.get("host").toString();
223 | int port = (int)args.get("port");
224 | String username = args.get("username").toString();
225 |
226 | JSch jsch = new JSch();
227 |
228 | String password = "";
229 | if (args.get("passwordOrKey").getClass() == args.getClass()) {
230 | HashMap keyPairs = (HashMap) args.get("passwordOrKey");
231 | byte[] privateKey = keyPairs.containsKey("privateKey") ? keyPairs.get("privateKey").toString().getBytes(): null;
232 | byte[] publicKey = keyPairs.containsKey("publicKey") ? keyPairs.get("publicKey").toString().getBytes(): null;
233 | byte[] passphrase = keyPairs.containsKey("passphrase") ? keyPairs.get("passphrase").toString().getBytes(): null;
234 | jsch.addIdentity("default", privateKey, publicKey, passphrase);
235 |
236 | } else {
237 | password = args.get("passwordOrKey").toString();
238 | }
239 |
240 | Session session = jsch.getSession(username, host, port);
241 |
242 | if (password.length() > 0)
243 | session.setPassword(password);
244 |
245 | Properties properties = new Properties();
246 | properties.setProperty("StrictHostKeyChecking", "no");
247 | session.setConfig(properties);
248 | session.connect();
249 |
250 | if (session.isConnected()) {
251 | SSHClient client = new SSHClient();
252 | client._session = session;
253 | client._key = key;
254 | clientPool.put(key, client);
255 |
256 | Log.d(LOGTAG, "Session connected");
257 | result.success("session_connected");
258 | }
259 | } catch (Exception error) {
260 | Log.e(LOGTAG, "Connection failed: " + error.getMessage());
261 | result.error("connection_failure", error.getMessage(), null);
262 | }
263 | }
264 | }).start();
265 | }
266 |
267 | private void execute(final HashMap args, final Result result) {
268 | new Thread(new Runnable() {
269 | public void run() {
270 | try {
271 | SSHClient client = getClient(args.get("id").toString(), result);
272 | if (client == null)
273 | return;
274 |
275 | Session session = client._session;
276 | ChannelExec channel = (ChannelExec) session.openChannel("exec");
277 |
278 | InputStream in = channel.getInputStream();
279 |
280 | channel.setCommand(args.get("cmd").toString());
281 | channel.connect();
282 |
283 | String line, response = "";
284 | BufferedReader reader = new BufferedReader(new InputStreamReader(in));
285 | while ((line = reader.readLine()) != null) {
286 | response += line + "\r\n";
287 | }
288 |
289 | result.success(response);
290 | } catch (Exception error) {
291 | Log.e(LOGTAG, "Error executing command: " + error.getMessage());
292 | result.error("execute_failure", error.getMessage(), null);
293 | }
294 | }
295 | }).start();
296 | }
297 |
298 | private void portForwardL(final HashMap args, final Result result) {
299 | new Thread(new Runnable() {
300 | public void run() {
301 | try {
302 | SSHClient client = getClient(args.get("id").toString(), result);
303 | if (client == null)
304 | return;
305 |
306 | Session session = client._session;
307 | int rport = Integer.parseInt(args.get("rport").toString());
308 | int lport = Integer.parseInt(args.get("lport").toString());
309 | String rhost = args.get("rhost").toString();
310 | int assinged_port=session.setPortForwardingL(lport, rhost, rport);
311 |
312 | result.success(Integer.toString(assinged_port));
313 | } catch (JSchException error) {
314 | Log.e(LOGTAG, "Error connecting portforwardL:" + error.getMessage());
315 | result.error("portforwardL_failure", error.getMessage(), null);
316 | }
317 | }
318 | }).start();
319 | }
320 |
321 | private void startShell(final HashMap args, final Result result) {
322 | new Thread(new Runnable() {
323 | public void run() {
324 | try {
325 | String key = args.get("id").toString();
326 | SSHClient client = getClient(args.get("id").toString(), result);
327 | if (client == null)
328 | return;
329 |
330 | Session session = client._session;
331 | Channel channel = session.openChannel("shell");
332 |
333 | InputStream in = channel.getInputStream();
334 |
335 | ((ChannelShell)channel).setPtyType(args.get("ptyType").toString());
336 | channel.connect();
337 |
338 | client._channel = channel;
339 | client._bufferedReader = new BufferedReader(new InputStreamReader(in));
340 | client._dataOutputStream = new DataOutputStream(channel.getOutputStream());
341 |
342 | result.success("shell_started");
343 |
344 | String line;
345 | while (client._bufferedReader != null && (line = client._bufferedReader.readLine()) != null) {
346 | Map map = new HashMap<>();
347 | map.put("name", "Shell");
348 | map.put("key", key);
349 | map.put("value", line + '\n');
350 | sendEvent(map);
351 | }
352 |
353 | } catch (Exception error) {
354 | Log.e(LOGTAG, "Error starting shell: " + error.getMessage());
355 | result.error("shell_failure", error.getMessage(), null);
356 | }
357 | }
358 | }).start();
359 | }
360 |
361 | private void writeToShell(final HashMap args, final Result result) {
362 | new Thread(new Runnable() {
363 | public void run() {
364 | try {
365 | SSHClient client = getClient(args.get("id").toString(), result);
366 | if (client == null)
367 | return;
368 |
369 | client._dataOutputStream.writeBytes(args.get("cmd").toString());
370 | client._dataOutputStream.flush();
371 | result.success("write_success");
372 | } catch (IOException error) {
373 | Log.e(LOGTAG, "Error writing to shell:" + error.getMessage());
374 | result.error("write_failure", error.getMessage(), null);
375 | }
376 | }
377 | }).start();
378 | }
379 |
380 | private void closeShell(final HashMap args) {
381 | new Thread(new Runnable() {
382 | public void run() {
383 | try {
384 | SSHClient client = clientPool.get(args.get("id"));
385 | if (client == null)
386 | return;
387 |
388 | if (client._channel != null) {
389 | client._channel.disconnect();
390 | }
391 |
392 | if (client._dataOutputStream != null) {
393 | client._dataOutputStream.flush();
394 | client._dataOutputStream.close();
395 | }
396 |
397 | if (client._bufferedReader != null) {
398 | client._bufferedReader.close();
399 | client._bufferedReader = null;
400 | }
401 | } catch (IOException error) {
402 | Log.e(LOGTAG, "Error closing shell:" + error.getMessage());
403 | }
404 | }
405 | }).start();
406 | }
407 |
408 | private void connectSFTP(final HashMap args, final Result result) {
409 | new Thread(new Runnable() {
410 | public void run() {
411 | try {
412 | SSHClient client = getClient(args.get("id").toString(), result);
413 | if (client == null)
414 | return;
415 |
416 | ChannelSftp channelSftp = (ChannelSftp) client._session.openChannel("sftp");
417 | channelSftp.connect();
418 | client._sftpSession = channelSftp;
419 | result.success("sftp_connected");
420 | } catch (JSchException error) {
421 | Log.e(LOGTAG, "Error connecting SFTP:" + error.getMessage());
422 | result.error("sftp_failure", error.getMessage(), null);
423 | }
424 | }
425 | }).start();
426 | }
427 |
428 | private void sftpLs(final HashMap args, final Result result) {
429 | new Thread(new Runnable() {
430 | public void run() {
431 | try {
432 | SSHClient client = clientPool.get(args.get("id"));
433 | ChannelSftp channelSftp = client._sftpSession;
434 |
435 | Vector files = channelSftp.ls(args.get("path").toString());
436 | List