Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
3 changes: 3 additions & 0 deletions .fvm/fvm_config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"flutterSdkVersion": "2.0.4"
}
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@ pubspec.lock

build/

.idea/
.idea/
.fvm/flutter_sdk
3 changes: 3 additions & 0 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"dart.flutterSdkPath": "F:\\Documents\\DartProjects\\flutter_ssh\\.fvm\\flutter_sdk"
}
13 changes: 13 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
[![style: lint](https://img.shields.io/badge/style-lint-4BC0F5.svg)](https://pub.dev/packages/lint)

# ssh

SSH and SFTP client for Flutter. Wraps iOS library [NMSSH](https://github.com/NMSSH/NMSSH) and Android library [JSch](http://www.jcraft.com/jsch/).
Expand All @@ -6,6 +8,14 @@ SSH and SFTP client for Flutter. Wraps iOS library [NMSSH](https://github.com/NM

Add `ssh` as a [dependency in your pubspec.yaml file](https://flutter.io/using-packages/).

## Contribute

### Generate DTO

```
fvm flutter pub run build_runner build
```

## Known issue

- Platform exception in release mode for Android:
Expand Down Expand Up @@ -109,6 +119,9 @@ await client.closeShell();
### SFTP

#### Connect SFTP:

Make sure to call connect method before connectSFTP

```dart
await client.connectSFTP();
```
Expand Down
16 changes: 16 additions & 0 deletions analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
include: package:lint/analysis_options.yaml

analyzer:
strong-mode:
implicit-casts: false
implicit-dynamic: false
exclude:
- "**/*.g.dart"
- lib/localizations.g.dart

linter:
rules:
camel_case_types: true
avoid_print: true
sort_constructors_first: true
avoid_classes_with_only_static_members: false
112 changes: 57 additions & 55 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,53 @@ import 'dart:async';
import 'dart:io';
import 'package:flutter/services.dart';
import 'package:flutter/material.dart';
import 'package:ssh/ssh.dart';
import 'package:path_provider/path_provider.dart';
import 'package:ssh/ssh.dart';

void main() => runApp(new MyApp());
void main() => runApp(MyApp());

class MyApp extends StatefulWidget {
@override
_MyAppState createState() => new _MyAppState();
_MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
String _result = '';
List _array;
List<dynamic>? _array;

Future<void> onClickCmd() async {
var client = new SSHClient(
final client = SSHClient(
host: "my.sshtest",
port: 22,
username: "sha",
passwordOrKey: "Password01.",
);

String result;
String? result;
try {
result = await client.connect();
if (result == "session_connected") result = await client.execute("ps");
if (result == "session_connected") {
result = await client.execute("ps");
}
client.disconnect();
} on PlatformException catch (e) {
print('Error: ${e.code}\nError Message: ${e.message}');
}

setState(() {
_result = result;
_result = result ?? '';
_array = null;
});
}

Future<void> onClickShell() async {
var client = new SSHClient(
final client = SSHClient(
host: "my.sshtest",
port: 22,
username: "sha",
passwordOrKey: {
"privateKey": """-----BEGIN RSA PRIVATE KEY-----
"privateKey": """
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA2DdFSeWG8wOHddRpOhf4FRqksJITr59iXdNrXq+n79QFN1g4
bvRG9zCDmyLb8EF+gah78dpJsGZVIltmfYWpsk7ok9GT/foCB1d2E6DbEU6mBIPe
OLxYOqyiea8mi7iGt9BvAB4Mj+v2LnhK4O2BB6PTU4KLjSgMdqtV/EGctLdK+JEU
Expand All @@ -71,7 +74,8 @@ VYm6XcNwPF/t5SM01ZuxH9NE2HZJ1cHcUGYQcUUJuqSkzsVK9j32E/akW9Cg3LVD
D/fESTECgYBwWv9yveto6pP6/xbR9k/Jdgr+vXQ3BJVU3BOsD38SeSrZfMSNGqgx
eiukCOIsRHYY7Qqi2vCJ62mwbHJ3RhSKKxcGpgzGX7KoGZS+bb5wb7RGNYK/mVaI
pFkz72+8eA2cnbWUqHt9WqMUgUBYZTMESzQrTf7+q+0gWf49AZJ/QQ==
-----END RSA PRIVATE KEY-----""",
-----END RSA PRIVATE KEY-----
""",
},
);

Expand All @@ -81,22 +85,23 @@ pFkz72+8eA2cnbWUqHt9WqMUgUBYZTMESzQrTf7+q+0gWf49AZJ/QQ==
});

try {
String result = await client.connect();
String? result = await client.connect();
if (result == "session_connected") {
result = await client.startShell(
ptyType: "xterm",
callback: (dynamic res) {
setState(() {
_result += res;
_result += res as String;
});
});

if (result == "shell_started") {
print(await client.writeToShell("echo hello > world\n"));
print(await client.writeToShell("cat world\n"));
new Future.delayed(

Future<dynamic>.delayed(
const Duration(seconds: 5),
() async => await client.closeShell(),
client.closeShell,
);
}
}
Expand All @@ -106,21 +111,21 @@ pFkz72+8eA2cnbWUqHt9WqMUgUBYZTMESzQrTf7+q+0gWf49AZJ/QQ==
}

Future<void> onClickSFTP() async {
var client = new SSHClient(
final client = SSHClient(
host: "my.sshtest",
port: 22,
username: "sha",
passwordOrKey: "Password01.",
);

try {
String result = await client.connect();
String? result = await client.connect();
if (result == "session_connected") {
result = await client.connectSFTP();
if (result == "sftp_connected") {
var array = await client.sftpLs();
final array = await client.sftpLs();
setState(() {
_result = result;
_result = result!;
_array = array;
});

Expand All @@ -131,27 +136,29 @@ pFkz72+8eA2cnbWUqHt9WqMUgUBYZTMESzQrTf7+q+0gWf49AZJ/QQ==
));
print(await client.sftpRmdir("testsftprename"));

Directory tempDir = await getTemporaryDirectory();
String tempPath = tempDir.path;
var filePath = await client.sftpDownload(
final Directory tempDir = await getTemporaryDirectory();
final String tempPath = tempDir.path;
final filePath = await client.sftpDownload(
path: "testupload",
toPath: tempPath,
callback: (progress) async {
callback: (dynamic progress) async {
print(progress);
// if (progress == 20) await client.sftpCancelDownload();
},
);

print(await client.sftpRm("testupload"));

print(await client.sftpUpload(
path: filePath,
toPath: ".",
callback: (progress) async {
print(progress);
// if (progress == 30) await client.sftpCancelUpload();
},
));
if (filePath != null) {
print(await client.sftpUpload(
path: filePath,
toPath: ".",
callback: (dynamic progress) async {
print(progress);
// if (progress == 30) await client.sftpCancelUpload();
},
));
}

print(await client.disconnectSFTP());

Expand All @@ -167,32 +174,29 @@ pFkz72+8eA2cnbWUqHt9WqMUgUBYZTMESzQrTf7+q+0gWf49AZJ/QQ==
Widget build(BuildContext context) {
Widget renderButtons() {
return ButtonTheme(
padding: EdgeInsets.all(5.0),
padding: const EdgeInsets.all(5.0),
child: ButtonBar(
children: <Widget>[
FlatButton(
child: Text(
TextButton(
onPressed: onClickCmd,
child: const Text(
'Test command',
style: TextStyle(color: Colors.white),
),
onPressed: onClickCmd,
color: Colors.blue,
),
FlatButton(
child: Text(
TextButton(
onPressed: onClickShell,
child: const Text(
'Test shell',
style: TextStyle(color: Colors.white),
),
onPressed: onClickShell,
color: Colors.blue,
),
FlatButton(
child: Text(
TextButton(
onPressed: onClickSFTP,
child: const Text(
'Test SFTP',
style: TextStyle(color: Colors.white),
),
onPressed: onClickSFTP,
color: Colors.blue,
),
],
),
Expand All @@ -206,20 +210,18 @@ pFkz72+8eA2cnbWUqHt9WqMUgUBYZTMESzQrTf7+q+0gWf49AZJ/QQ==
),
body: ListView(
shrinkWrap: true,
padding: EdgeInsets.all(15.0),
padding: const EdgeInsets.all(15.0),
children: <Widget>[
Text(
"Please edit the connection setting in the source code before clicking the test buttons"),
const Text("Please edit the connection setting in the source code before clicking the test buttons"),
renderButtons(),
Text(_result),
_array != null && _array.length > 0
? Column(
children: _array.map((f) {
return Text(
"${f["filename"]} ${f["isDirectory"]} ${f["modificationDate"]} ${f["lastAccess"]} ${f["fileSize"]} ${f["ownerUserID"]} ${f["ownerGroupID"]} ${f["permissions"]} ${f["flags"]}");
}).toList(),
)
: Container(),
if (_array != null && _array!.isNotEmpty)
Column(
children: _array!.map((dynamic f) {
return Text(
"${f["filename"]} ${f["isDirectory"]} ${f["modificationDate"]} ${f["lastAccess"]} ${f["fileSize"]} ${f["ownerUserID"]} ${f["ownerGroupID"]} ${f["permissions"]} ${f["flags"]}");
}).toList(),
),
],
),
),
Expand Down
54 changes: 3 additions & 51 deletions example/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,72 +1,24 @@
name: ssh_example
description: Demonstrates how to use the ssh plugin.

# The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +.
# Both the version and the builder number may be overridden in flutter
# build by specifying --build-name and --build-number, respectively.
# Read more about versioning at semver.org.
version: 1.0.0+1

environment:
sdk: ">=2.0.0-dev.68.0 <3.0.0"
sdk: ">=2.12.0"

dependencies:
cupertino_icons: ^1.0.1
flutter:
sdk: flutter

# The following adds the Cupertino Icons font to your application.
# Use with the CupertinoIcons class for iOS style icons.
cupertino_icons: ^0.1.2

dev_dependencies:
flutter_test:
sdk: flutter

path_provider: ^2.0.1
ssh:
path: ../

path_provider: ^0.4.1

# For information on the generic Dart part of this file, see the
# following page: https://www.dartlang.org/tools/pub/pubspec

# The following section is specific to Flutter.
flutter:

# The following line ensures that the Material Icons font is
# included with your application, so that you can use the icons in
# the material Icons class.
uses-material-design: true

# To add assets to your application, add an assets section, like this:
# assets:
# - images/a_dot_burr.jpeg
# - images/a_dot_ham.jpeg

# An image asset can refer to one or more resolution-specific "variants", see
# https://flutter.io/assets-and-images/#resolution-aware.

# For details regarding adding assets from package dependencies, see
# https://flutter.io/assets-and-images/#from-packages

# To add custom fonts to your application, add a fonts section here,
# in this "flutter" section. Each entry in this list should have a
# "family" key with the font family name, and a "fonts" key with a
# list giving the asset and other descriptors for the font. For
# example:
# fonts:
# - family: Schyler
# fonts:
# - asset: fonts/Schyler-Regular.ttf
# - asset: fonts/Schyler-Italic.ttf
# style: italic
# - family: Trajan Pro
# fonts:
# - asset: fonts/TrajanPro.ttf
# - asset: fonts/TrajanPro_Bold.ttf
# weight: 700
#
# For details regarding fonts from package dependencies,
# see https://flutter.io/custom-fonts/#from-packages
Loading