QwkSync.FTP
1.0.0
dotnet add package QwkSync.FTP --version 1.0.0
NuGet\Install-Package QwkSync.FTP -Version 1.0.0
<PackageReference Include="QwkSync.FTP" Version="1.0.0" />
<PackageVersion Include="QwkSync.FTP" Version="1.0.0" />
<PackageReference Include="QwkSync.FTP" />
paket add QwkSync.FTP --version 1.0.0
#r "nuget: QwkSync.FTP, 1.0.0"
#:package QwkSync.FTP@1.0.0
#addin nuget:?package=QwkSync.FTP&version=1.0.0
#tool nuget:?package=QwkSync.FTP&version=1.0.0
QWKSync.FTP
FTP/FTPS transport extension for QWKSync.NET.
Overview
QWKSync.FTP provides lightweight FTP and FTPS (explicit and implicit TLS) file transfer capabilities for QWKSync.NET. It uses the MIT-licenced FluentFTP library for all FTP operations.
Installation
dotnet add package QwkSync.Ftp
Quick Start
using QwkSync;
using QwkSync.Ftp;
// Create a profile with credentials in settings
QwkSyncProfile profile = new QwkSyncProfile
{
Endpoint = new Uri("ftp://bbs.example.com"),
TransportId = "ftp",
Settings = new Dictionary<string, string>
{
{ "ftp.username", "myuser" },
{ "ftp.password", "mypassword" },
{ "ftp.tls", "true" }
}
};
// Create a plan
QwkSyncPlan plan = new QwkSyncPlan
{
LocalInboxDirectory = "/path/to/local/inbox",
LocalOutboxDirectory = "/path/to/local/outbox"
};
// Create client and register the FTP transport
QwkSyncClient client = new QwkSyncClient();
client.RegisterTransportFactory(new FtpTransportFactory());
// Run synchronisation
QwkSyncResult result = await client.SyncAsync(profile, plan, CancellationToken.None);
Configuration Settings
All settings are optional and have sensible defaults.
| Setting | Description | Default |
|---|---|---|
ftp.username |
FTP username | (anonymous) |
ftp.password |
FTP password | (qwksync@localhost) |
ftp.port |
FTP port number | 21 |
ftp.tls |
Enable FTPS. Values: true/false |
false |
ftp.tls.mode |
FTPS mode. Values: explicit, implicit |
explicit |
ftp.tls.certValidation |
Certificate validation. Values: strict, acceptAny |
strict |
ftp.tls.protocols |
TLS protocols (comma-separated). Values: tls10, tls11, tls12, tls13 |
tls12,tls13 |
ftp.tls.dataEncryption |
Data connection encryption. Values: both, controlOnly, none |
both |
ftp.tls.certThumbprints |
SHA256 certificate thumbprints for pinning (comma-separated) | (none) |
ftp.passive |
Use passive mode. Values: true/false |
false (active mode) |
ftp.connectTimeoutMs |
Connect timeout in milliseconds | 15000 |
ftp.readWriteTimeoutMs |
Read/write timeout in milliseconds | 60000 |
ftp.keepAlive |
Keep control connection alive. Values: true/false |
true |
ftp.userAgent |
Client name string | QWKSync.NET |
ftp.remoteRoot |
Remote root directory | (server login directory) |
ftp.pathSeparator |
Path separator. Values: / only |
/ |
ftp.verbose |
Enable verbose FTP protocol logging. Values: true/false |
false |
ftp.proxy.type |
Proxy type. Values: none, http, socks4, socks5 |
none |
ftp.proxy.host |
Proxy server hostname or IP address | (none) |
ftp.proxy.port |
Proxy server port number | (varies by type) |
ftp.proxy.username |
Proxy authentication username | (none) |
ftp.proxy.password |
Proxy authentication password | (none) |
Path Resolution Examples
Example A — Simple FTP, everything in one directory
Endpoint: ftp://bbs.example.com
ftp.remoteRoot: (unset)
RemoteInboxPath: (unset)
RemoteOutboxPath: (unset)
Result:
- RemoteRoot: (FTP login directory)
- Inbox: (FTP login directory)
- Outbox: (FTP login directory)
Example B — FTP with QWK subfolder
Endpoint: ftp://bbs.example.com/qwk
ftp.remoteRoot: (unset)
RemoteInboxPath: (unset)
RemoteOutboxPath: (unset)
Result:
- RemoteRoot:
/qwk - Inbox:
/qwk - Outbox:
/qwk
Example C — FTP with explicit layout
Endpoint: ftp://bbs.example.com
ftp.remoteRoot: /bbsdata
RemoteInboxPath: in
RemoteOutboxPath: out
Result:
- RemoteRoot:
/bbsdata - Inbox:
/bbsdata/in - Outbox:
/bbsdata/out
Credentials
The FTP transport uses Credentials.UsernamePassword(user, passwordProvider) from the profile.
For anonymous FTP, either:
- Omit credentials (uses
anonymouswith a placeholder email) - Set username to
anonymousand provide an email-style password
Connection Modes
Active vs Passive Mode
- Active mode (default): The FTP server initiates the data connection back to the client. This is the traditional FTP mode and works well with many BBS systems.
- Passive mode: The client initiates both control and data connections to the server. Better for clients behind firewalls or NAT.
To use passive mode, set ftp.passive=true in settings or use --passive flag in the demo tool.
Automatic Mode Fallback
The transport automatically handles connection mode failures by cycling through available modes:
- When starting in passive mode: PASV → EPSV → PORT (active)
- When starting in active mode: PORT → PASV
This ensures maximum compatibility with servers that have blocked passive ports or network configurations that prevent active mode connections. The working mode is remembered for the session to avoid repeated fallback attempts.
Directory Listing Fallback
The transport also handles servers that advertise MLSD (machine-readable listing) support but have incomplete implementations:
- Attempts MLSD first (modern, structured format)
- Falls back to LIST if MLSD returns empty or fails (legacy format)
This fallback is automatic and invisible to callers.
BBS Server Compatibility
Synchronet BBS
Synchronet BBS servers may require special handling due to their FTP implementation:
- Passive mode ports may be blocked: The server's passive data ports (returned in PASV/EPSV responses) may not be accessible. The transport will automatically fall back to active mode.
- MLSD may return incomplete results: Despite advertising MLSD support, some Synchronet installations return empty listings. The transport falls back to LIST automatically.
- NAT configuration: Synchronet servers behind NAT may return internal IP addresses in PASV responses. FluentFTP handles this, but if passive ports are blocked, active mode is required.
For Synchronet servers, try connecting with --passive first (which will auto-fallback to active if needed), or use active mode directly by omitting the --passive flag.
Example for Synchronet BBS:
dotnet run -- --host bbs.example.net --username myuser --password mypass \
--passive --verbose --list-only
If you see "Connection refused" errors for passive mode, the transport will automatically switch to active mode.
Security Notes
TLS/FTPS
When ftp.tls=true:
- Supports both explicit TLS (AUTH TLS after connection) and implicit TLS (port 990)
- Set
ftp.tls.modetoexplicit(default) orimplicit - Supports TLS 1.2 and TLS 1.3
- By default, validates server certificates strictly
Certificate Validation
strict(default): Validates certificate chain and hostname. Recommended for production.acceptAny: Accepts any certificate including self-signed. Use only in controlled environments.
Advanced TLS Configuration
TLS Protocol Versions
Control which TLS protocol versions are enabled:
- Default: TLS 1.2 and 1.3 (recommended)
- Legacy support: Can enable TLS 1.0 or 1.1 for older BBS systems
- Setting:
ftp.tls.protocols=tls12,tls13
Note: TLS 1.0 and 1.1 are deprecated due to known security vulnerabilities and should only be enabled when connecting to legacy systems that don't support modern TLS versions. Always use TLS 1.2 or 1.3 when possible.
Data Connection Encryption
Choose whether to encrypt data connections:
both(default): Encrypts both control and data connections (most secure)controlOnly: Only encrypts credentials and commands; data transfers in clear text (better performance)- Setting:
ftp.tls.dataEncryption=both
Certificate Pinning
Pin specific certificates by SHA256 thumbprint for enhanced security:
- Provides protection against compromised Certificate Authorities
- Only certificates matching the specified thumbprints will be accepted
- Multiple thumbprints can be specified (comma-separated)
- Setting:
ftp.tls.certThumbprints=ABC123...,DEF456...
Example with advanced TLS:
# Legacy BBS with TLS 1.0, control-only encryption
dotnet run -- --host oldserver.bbs.com --tls \
--tls-protocols tls10,tls11 \
--tls-data-encryption controlOnly \
--list-only
# Modern server with certificate pinning
dotnet run -- --host secure.example.com --tls \
--tls-cert-thumbprints 1A2B3C4D... \
--list-only
Proxy Support
FTP connections can be routed through proxy servers. Supported proxy types:
- HTTP/HTTPS: Standard HTTP proxies using CONNECT method
- SOCKS4/4a: Basic SOCKS proxy protocol
- SOCKS5: Modern SOCKS with authentication support
Configure proxy settings using ftp.proxy.* settings or command-line options:
# Connect through HTTP proxy
dotnet run -- --host bbs.example.com --username user \
--proxy-type http --proxy-host proxy.corp.com --proxy-port 8080 \
--list-only
# Connect through SOCKS5 proxy with authentication
dotnet run -- --host bbs.example.com \
--proxy-type socks5 --proxy-host 127.0.0.1 --proxy-port 1080 \
--proxy-username proxyuser --proxy-password proxypass \
--list-only
Demo Tool
The QWKSync.FTP.Demo tool demonstrates the FTP transport:
# List files on anonymous FTP server
dotnet run -- --host ftp.example.com --list-only
# Sync with authenticated FTP server
dotnet run -- --host ftp.example.com --username user --password pass \
--local-inbox ./inbox --local-outbox ./outbox
# Sync with FTPS server (explicit TLS)
dotnet run -- --host secure.example.com --tls --username user \
--local-inbox ./inbox --local-outbox ./outbox
# Sync with implicit FTPS (port 990) with verbose logging
dotnet run -- --host secure.example.com --implicit-tls --username user \
--verbose --local-inbox ./inbox --local-outbox ./outbox
Limitations
- Only forward-slash path separator is supported.
- Active mode requires incoming connections to be allowed through your firewall/router.
Forward-slash Path Separator
FTP Protocol Standard (RFC 959) uses / as the path separator, regardless of the server's operating system. Even Windows FTP servers use forward slashes in FTP commands.
Active Mode Network Requirements
When using active mode (PORT), the FTP server connects back to your machine on a dynamically allocated port. This requires:
- Your firewall to allow incoming connections on high ports (typically 1024+)
- Your router to support FTP connection tracking, or manual port forwarding
If active mode fails, the transport will attempt passive modes. If all modes fail, check your network configuration or contact the server administrator.
Dependencies
- FluentFTP (MIT Licence) - FTP client library
Licence
MIT Licence. See LICENSE for details.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.0.0 | 532 | 1/27/2026 |
Initial release.