Skip to content

Commit

Permalink
feat: ftp client and server
Browse files Browse the repository at this point in the history
  • Loading branch information
alextekartik committed Oct 24, 2024
1 parent 0fef81e commit 3fc9b36
Show file tree
Hide file tree
Showing 32 changed files with 717 additions and 1 deletion.
27 changes: 27 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Dependabot configuration file.
# See https://docs.github.com/en/code-security/dependabot/dependabot-version-updates
version: 2

enable-beta-ecosystems: true

updates:
- package-ecosystem: "pub"
directory: "ftp"
schedule:
interval: "monthly"
- package-ecosystem: "pub"
directory: "ftp_client_io"
schedule:
interval: "monthly"
- package-ecosystem: "pub"
directory: "ftp_io"
schedule:
interval: "monthly"
- package-ecosystem: "pub"
directory: "ftp_server_io"
schedule:
interval: "monthly"
- package-ecosystem: "github-actions"
directory: "/"
schedule:
interval: "monthly"
36 changes: 36 additions & 0 deletions .github/workflows/run_ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: Run CI
on:
push:
workflow_dispatch:
schedule:
- cron: '0 0 * * 0' # every sunday at midnight

jobs:
test:
name: Test on ${{ matrix.os }} / ${{ matrix.dart }}
runs-on: ${{ matrix.os }}
defaults:
run:
working-directory: .
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
dart: stable
- os: ubuntu-latest
dart: beta
- os: ubuntu-latest
dart: dev
- os: windows-latest
dart: stable
- os: macos-latest
dart: stable
steps:
- uses: actions/checkout@v4
- uses: dart-lang/[email protected]
with:
sdk: ${{ matrix.dart }}
- run: dart --version
- run: dart pub global activate dev_build
- run: dart pub global run dev_build:run_ci --recursive
29 changes: 29 additions & 0 deletions .github/workflows/run_ci_downgrade_analyze.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Run CI Downgrade analyze
on:
push:
pull_request:
workflow_dispatch:
schedule:
- cron: '0 0 * * 0' # every sunday at midnight

jobs:
test:
name: Test on ${{ matrix.os }} / dart ${{ matrix.dart }}
runs-on: ${{ matrix.os }}
defaults:
run:
working-directory: .
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-latest
dart: stable
steps:
- uses: actions/checkout@v4
- uses: dart-lang/[email protected]
with:
sdk: ${{ matrix.dart }}
- run: dart --version
- run: dart pub global activate dev_build
- run: dart pub global run dev_build:run_ci --pub-downgrade --analyze --no-override --recursive
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
.local/
pubspec_overrides.yaml
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,5 @@
# ftp.dart
FTP abstraction, client, server

FTP abstraction for client and server
- IO server implementation using [ftp_server](https://pub.dev/packages/ftp_server)
- IO client implementation using [ftpconnect](https://pub.dev/packages/ftpconnect)
7 changes: 7 additions & 0 deletions ftp/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# https://dart.dev/guides/libraries/private-files
# Created by `dart pub`
.dart_tool/

# Avoid committing pubspec.lock for library packages; see
# https://dart.dev/guides/libraries/private-files#pubspeclock.
pubspec.lock
15 changes: 15 additions & 0 deletions ftp/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# ftp

FTP abstract client

## Setup

In `pubspec.yaml`:

```yaml
tekartik_ftp:
git:
url: https://github.com/tekartik/ftp.dart
ref: dart3a
path: ftp
```
1 change: 1 addition & 0 deletions ftp/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include: package:tekartik_lints/package.yaml
1 change: 1 addition & 0 deletions ftp/lib/ftp_client.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export 'src/client/ftp_client.dart' show FtpClient, FtpEntry, FtpEntryType;
1 change: 1 addition & 0 deletions ftp/lib/ftp_server.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export 'src/server/ftp_server.dart' show FtpServer;
55 changes: 55 additions & 0 deletions ftp/lib/src/client/ftp_client.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import 'dart:io';

/// FTP client interface
abstract class FtpClient {
/// Connect
Future<bool> connect();

/// Disconnect
Future<bool> disconnect();

/// Change dir
Future<bool> cd(String path);

/// List entries
Future<List<FtpEntry>> list();

/// Download file
Future<bool> downloadFile(String remoteName, File localFile);

/// Update file
Future<bool> uploadFile(
File localFile,
String remoteName,
);
}

/// FTP entry
abstract class FtpEntry {
/// name of entry
String get name;

/// type of entry
FtpEntryType get type;

/// modified time, if available
DateTime? get modified;

/// -1 if unknown, dir size should not be consided (sometimes reported as 4096)
int get size;
}

/// FTP entry type
enum FtpEntryType {
/// File
file,

/// Directory
dir,

/// Link
link,

/// Unknown
unknown
}
16 changes: 16 additions & 0 deletions ftp/lib/src/server/ftp_server.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import 'dart:io';

/// FTP server
abstract class FtpServer {
/// port
int get port;

/// Root directory
Directory get root;

/// Start the server
Future<void> start();

/// Stop the server
Future<void> stop();
}
19 changes: 19 additions & 0 deletions ftp/pubspec.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
name: tekartik_ftp
description: Abstract server and client ftp library
version: 1.0.0

environment:
sdk: ^3.5.0

# Add regular dependencies here.
dependencies:
# path: ^1.8.0

dev_dependencies:
lints: ">=5.0.0"
tekartik_lints:
git:
url: https://github.com/tekartik/common.dart
ref: dart3a
path: packages/lints
test: ">=1.24.0"
7 changes: 7 additions & 0 deletions ftp_client_io/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# https://dart.dev/guides/libraries/private-files
# Created by `dart pub`
.dart_tool/

# Avoid committing pubspec.lock for library packages; see
# https://dart.dev/guides/libraries/private-files#pubspeclock.
pubspec.lock
15 changes: 15 additions & 0 deletions ftp_client_io/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
# ftp_client_io

FTP client io implementation

## Setup

In `pubspec.yaml`:

```yaml
tekartik_ftp_client_io:
git:
url: https://github.com/tekartik/ftp.dart
ref: dart3a
path: ftp_io
```
1 change: 1 addition & 0 deletions ftp_client_io/analysis_options.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
include: package:tekartik_lints/package.yaml
2 changes: 2 additions & 0 deletions ftp_client_io/lib/ftp_client_io.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export 'package:tekartik_ftp/ftp_client.dart';
export 'src/ftp_client_ftpconnect.dart' show FtpClientIo;
127 changes: 127 additions & 0 deletions ftp_client_io/lib/src/ftp_client_ftpconnect.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import 'dart:io';

import 'package:ftpconnect/ftpconnect.dart' as fc;
import 'package:tekartik_common_utils/common_utils_import.dart';
import 'package:tekartik_ftp/ftp_client.dart';

/// Allow debugging from external client
final debugFtpClientFtpConnect = false;
// final debugFtpClientFtpConnect = devWarning(true);

bool get _debug => debugFtpClientFtpConnect;
void _log(Object? message) {
if (_debug) {
// ignore: avoid_print
print(message);
}
}

/// Ftp client using io
abstract class FtpClientIo implements FtpClient {
/// Constructor
factory FtpClientIo(
{required String host,
required String user,
required String password,
int? port}) =>
_FtpClientFtpConnect(
host: host, user: user, password: password, port: port);
}

/// Ftp client using ftpconnect
class _FtpClientFtpConnect implements FtpClientIo {
late fc.FTPConnect _delegate;

@override
Future<bool> connect() async {
return await _delegate.connect();
}

@override
Future<bool> disconnect() async {
return await _delegate.disconnect();
}

/// Constructor
_FtpClientFtpConnect(
{required String host,
required String user,
required String password,
int? port}) {
_delegate = fc.FTPConnect(host,
port: port,
user: user,
pass: password,
securityType: fc.SecurityType.FTP);
}

@override
Future<List<FtpEntry>> list() async {
if (_debug) {
_log('list');
}
var entries = await _delegate.listDirectoryContent();
return entries.map((e) => _FtpEntry(e)).toList();
}

@override
Future<bool> downloadFile(String remoteName, File localFile) async {
if (_debug) {
_log('download $remoteName to $localFile');
}
return await _delegate.downloadFile(remoteName, localFile);
}

@override
Future<bool> cd(String path) async {
if (_debug) {
_log('cd $path');
}
return await _delegate.changeDirectory(path);
}

@override
Future<bool> uploadFile(File localFile, String remoteName) async {
if (_debug) {
_log('upload $localFile to $remoteName');
}
return await _delegate.uploadFile(localFile, sRemoteName: remoteName);
}
}

class _FtpEntry implements FtpEntry {
final fc.FTPEntry _delegate;

_FtpEntry(this._delegate);
@override
String get name => _delegate.name;

@override
FtpEntryType get type => _delegate.type.toFtpType();

@override
int get size => _delegate.size ?? -1;

@override
DateTime? get modified => _delegate.modifyTime;

@override
String toString() =>
'FtpEntry(name: $name, type: $type${size > 0 ? ', size: $size' : ''}'
'${modified != null ? ', ${modified?.toIso8601String()}' : ''}';
}

extension on fc.FTPEntryType {
FtpEntryType toFtpType() {
switch (this) {
case fc.FTPEntryType.FILE:
return FtpEntryType.file;
case fc.FTPEntryType.DIR:
return FtpEntryType.dir;
case fc.FTPEntryType.LINK:
return FtpEntryType.link;
default:
return FtpEntryType.unknown;
}
}
}
Loading

0 comments on commit 3fc9b36

Please sign in to comment.