├── FUNDING.yml ├── FluentFTP ├── restore.bat ├── sn.snk ├── Client │ ├── Interfaces │ │ └── IFtpLogger.cs │ ├── BaseClient │ │ ├── CanUploadFile.cs │ │ ├── IsProxy.cs │ │ ├── LoadProfile.cs │ │ ├── Disconnect.cs │ │ ├── ConvertDate.cs │ │ ├── Connect.cs │ │ ├── ValidateAutoDetect.cs │ │ ├── ValidateCertficate.cs │ │ ├── CheckFileExistsBySize.cs │ │ ├── StartListeningOnPort.cs │ │ └── Dispose.cs │ ├── SyncClient │ │ ├── GetWorkingDirectory.cs │ │ ├── GetReply.cs │ │ ├── Execute.cs │ │ ├── DisableUTF8.cs │ │ ├── Handshake.cs │ │ ├── IsStillConnected.cs │ │ ├── GetChmod.cs │ │ ├── GetAbsoluteFilePath.cs │ │ ├── GetAbsoluteDir.cs │ │ ├── DeleteFile.cs │ │ ├── SetWorkingDirectory.cs │ │ ├── Disconnect.cs │ │ ├── IsRoot.cs │ │ ├── Noop.cs │ │ ├── GetFilePermissions.cs │ │ ├── GetModifiedTime.cs │ │ ├── AutoConnect.cs │ │ ├── SetDataType.cs │ │ └── EmptyDirectory.cs │ └── AsyncClient │ │ ├── DisableUTF8.cs │ │ ├── Handshake.cs │ │ ├── GetAbsoluteFilePath.cs │ │ ├── GetAbsoluteDir.cs │ │ ├── GetReply.cs │ │ ├── GetChmod.cs │ │ ├── IsRoot.cs │ │ ├── DeleteFile.cs │ │ ├── SetWorkingDirectory.cs │ │ ├── Disconnect.cs │ │ ├── GetFilePermissions.cs │ │ └── GetModifiedTime.cs ├── Proxy │ ├── Enums │ │ ├── SocksVersion.cs │ │ ├── SocksRequestCommand.cs │ │ ├── SocksRequestAddressType.cs │ │ ├── SocksAuthType.cs │ │ └── SocksReply.cs │ ├── SyncProxy │ │ ├── FtpClientProxy.cs │ │ └── FtpClientUserAtHostProxy.cs │ └── AsyncProxy │ │ └── AsyncFtpClientUserAtHostProxy.cs ├── Streams │ └── Interfaces │ │ ├── IFtpStreamConfig.cs │ │ └── IFtpStream.cs ├── Enums │ ├── FtpObjectType.cs │ ├── FtpDataType.cs │ ├── FtpPermission.cs │ ├── FtpSpecialPermissions.cs │ ├── FtpIpVersion.cs │ ├── FtpStatus.cs │ ├── FtpFolderSyncMode.cs │ ├── FtpsBuffering.cs │ ├── FtpEncryptionMode.cs │ ├── FtpTraceLevel.cs │ ├── FtpResponseType.cs │ ├── FtpHashAlgorithm.cs │ ├── FtpObjectSubType.cs │ ├── FtpConnectionState.cs │ ├── FtpCompareResult.cs │ ├── FtpZOSListRealm.cs │ ├── FtpDate.cs │ ├── FtpOperatingSystem.cs │ ├── FtpVerifyMethod.cs │ ├── FtpLocalExists.cs │ ├── FtpOperator.cs │ └── FtpCompareOption.cs ├── Events │ └── FtpSocketStreamSslValidation.cs ├── Model │ ├── FtpLogEntry.cs │ ├── FtpSizeReply.cs │ └── FtpProxyProfile.cs ├── Exceptions │ ├── FtpProtocolUnsupportedException.cs │ ├── FtpProxyException.cs │ ├── FtpListParseException.cs │ ├── FtpMissingSocketException.cs │ ├── IOExceptions.cs │ ├── FtpSecurityNotAvailableException.cs │ ├── FtpAuthenticationException.cs │ ├── FtpException.cs │ └── FtpInvalidCertificateException.cs ├── Helpers │ ├── Uris.cs │ ├── TimeSpans.cs │ ├── Logging │ │ └── LoggerExtensions.cs │ ├── LocalPorts.cs │ └── Enums.cs ├── Servers │ ├── Handlers │ │ ├── DLinkServer.cs │ │ ├── TPLinkServer.cs │ │ ├── BFtpdServer.cs │ │ ├── WSFTPServer.cs │ │ ├── PyFtpdLibServer.cs │ │ ├── CrushFtpServer.cs │ │ ├── IDALFtpServer.cs │ │ ├── TitanFtpServer.cs │ │ ├── XLightServer.cs │ │ ├── CerberusServer.cs │ │ ├── HomegateFtpServer.cs │ │ ├── Ftp2S3GatewayServer.cs │ │ ├── GlobalScapeEftServer.cs │ │ ├── RumpusServer.cs │ │ ├── FritzBoxServer.cs │ │ ├── GlFtpdServer.cs │ │ ├── ApacheFtpServer.cs │ │ ├── SolarisFtpServer.cs │ │ ├── MicroTikServer.cs │ │ ├── HuaweiServer.cs │ │ ├── WindowsIISServer.cs │ │ ├── WindowsCEServer.cs │ │ ├── FileZillaServer.cs │ │ ├── PureFtpdServer.cs │ │ ├── VsFtpdServer.cs │ │ └── NonStopTandemServer.cs │ └── FtpHandlerIndex.cs └── Rules │ └── FtpRule.cs ├── FluentFTP.Dockers ├── pyftpdlib │ ├── requirements.txt │ ├── Dockerfile │ └── LICENSE ├── filezilla │ ├── run-filezilla.sh │ ├── get-filezilla.sh │ ├── docker-compose.yml │ └── users.xml ├── glftpd │ ├── xinetd_glftpd.conf │ ├── docker-compose.yml │ ├── xinetd.conf │ └── run-glftpd.sh ├── bftpd │ ├── docker-compose.yml │ ├── run-bftpd.sh │ └── Dockerfile ├── vsftpd │ ├── docker-compose.yml │ └── run-vsftpd.sh ├── apache │ ├── docker-compose.yml │ ├── run-apache.sh │ └── users.properties ├── common-debian-slim │ └── Dockerfile ├── proftpd │ ├── docker-compose.yml │ └── run-proftpd.sh ├── pureftpd │ ├── docker-compose.yml │ ├── run-pureftpd.sh │ └── Dockerfile ├── common-mirror │ └── Dockerfile └── common-debian │ └── Dockerfile ├── FluentFTP.Logging ├── restore.bat ├── sn.snk ├── RELEASES.md ├── README.md └── FtpLogAdapter.cs ├── .github ├── gnutls.png ├── logging.png ├── gnutls-2.png ├── logo-new.png ├── features-5.png ├── ftp-monitor.png ├── logo-nuget.png ├── yourkit-logo.png ├── auto-reconnect.png ├── balsamiq-logo.png ├── contributors-3.png ├── jetbrains-logo.png └── workflows_WIP │ └── pull_request.yml ├── FluentFTP.Tests ├── config.yaml.template ├── Unit │ ├── ExceptionTests.cs │ ├── HelperTests.cs │ └── IsProxyTests.cs └── Integration │ └── System │ └── IntegrationTestRunner.cs ├── FluentFTP.Xunit ├── Attributes │ ├── SkippableState.cs │ ├── SkipTestException.cs │ ├── SkippableFactAttribute.cs │ ├── SkippableTheoryAttribute.cs │ └── Internal │ │ ├── SkippableFactDiscoverer.cs │ │ └── SkippableFactMessageBus.cs ├── Docker │ ├── DockerFtpConfig.cs │ ├── DockerFtpContainerIndex.cs │ └── Containers │ │ ├── BFtpdContainer.cs │ │ ├── ApacheContainer.cs │ │ ├── GlFtpdContainer.cs │ │ ├── FileZillaContainer.cs │ │ ├── ProFtpdContainer.cs │ │ ├── PyFtpdLibContainer.cs │ │ └── VsFtpdContainer.cs └── FluentFTP.Xunit.csproj ├── .gitignore ├── FluentFTP.VBExamples ├── VBExamples.vbproj ├── DeleteFile.vb ├── SetWorkingDirectory.vb ├── CreateDirectory.vb ├── GetWorkingDirectory.vb ├── GetFileSize.vb ├── ConnectFTPS.vb ├── FileExists.vb ├── GetModifiedTime.vb ├── DirectoryExists.vb ├── DeleteDirectory.vb ├── Rename.vb ├── GetNameListing.vb ├── Connect.vb ├── FolderMonitor.vb ├── ConnectFTPSCertificate.vb ├── UploadManyFiles.vb ├── DownloadManyFiles.vb ├── DownloadDirectory.vb ├── UploadDirectory.vb └── DownloadFile.vb ├── appveyor.yml ├── FluentFTP.CSharpExamples ├── Extensions.cs ├── DeleteFile.cs ├── Log_NLog.cs ├── SetWorkingDirectory.cs ├── CreateDirectory.cs ├── GetWorkingDirectory.cs ├── DirectoryExists.cs ├── GetFileSize.cs ├── ConnectFTPS.cs ├── DeleteDirectory.cs ├── GetModifiedTime.cs ├── Log_Serilog.cs ├── CSharpExamples.csproj ├── ExecuteFTPCommand.cs ├── FileExists.cs ├── Rename.cs ├── Connect.cs ├── GetNameListing.cs ├── ConnectFTPSCertificate.cs ├── UploadManyFiles.cs ├── DownloadManyFiles.cs └── DownloadDirectory.cs ├── ISSUE_TEMPLATE.md └── LICENSE.TXT /FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: robinrodricks 2 | -------------------------------------------------------------------------------- /FluentFTP/restore.bat: -------------------------------------------------------------------------------- 1 | dotnet restore FluentFTP.csproj 2 | pause -------------------------------------------------------------------------------- /FluentFTP.Dockers/pyftpdlib/requirements.txt: -------------------------------------------------------------------------------- 1 | pyftpdlib==1.5.6 2 | -------------------------------------------------------------------------------- /FluentFTP.Logging/restore.bat: -------------------------------------------------------------------------------- 1 | dotnet restore FluentFTP.Logging.csproj 2 | pause -------------------------------------------------------------------------------- /FluentFTP/sn.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhamedHabib/FluentFTP/master/FluentFTP/sn.snk -------------------------------------------------------------------------------- /.github/gnutls.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhamedHabib/FluentFTP/master/.github/gnutls.png -------------------------------------------------------------------------------- /.github/logging.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhamedHabib/FluentFTP/master/.github/logging.png -------------------------------------------------------------------------------- /.github/gnutls-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhamedHabib/FluentFTP/master/.github/gnutls-2.png -------------------------------------------------------------------------------- /.github/logo-new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhamedHabib/FluentFTP/master/.github/logo-new.png -------------------------------------------------------------------------------- /.github/features-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhamedHabib/FluentFTP/master/.github/features-5.png -------------------------------------------------------------------------------- /.github/ftp-monitor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhamedHabib/FluentFTP/master/.github/ftp-monitor.png -------------------------------------------------------------------------------- /.github/logo-nuget.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhamedHabib/FluentFTP/master/.github/logo-nuget.png -------------------------------------------------------------------------------- /.github/yourkit-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhamedHabib/FluentFTP/master/.github/yourkit-logo.png -------------------------------------------------------------------------------- /FluentFTP.Logging/sn.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhamedHabib/FluentFTP/master/FluentFTP.Logging/sn.snk -------------------------------------------------------------------------------- /.github/auto-reconnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhamedHabib/FluentFTP/master/.github/auto-reconnect.png -------------------------------------------------------------------------------- /.github/balsamiq-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhamedHabib/FluentFTP/master/.github/balsamiq-logo.png -------------------------------------------------------------------------------- /.github/contributors-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhamedHabib/FluentFTP/master/.github/contributors-3.png -------------------------------------------------------------------------------- /.github/jetbrains-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhamedHabib/FluentFTP/master/.github/jetbrains-logo.png -------------------------------------------------------------------------------- /FluentFTP.Logging/RELEASES.md: -------------------------------------------------------------------------------- 1 | ### 1.0.0 2 | 3 | - Introduced `FtpLogAdapter` to integrate FluentFTP with MELA. 4 | -------------------------------------------------------------------------------- /FluentFTP.Tests/config.yaml.template: -------------------------------------------------------------------------------- 1 | server: 2 | host: 1.2.3.4 3 | port: 21 4 | user: ftpuser 5 | pass: ftppass -------------------------------------------------------------------------------- /FluentFTP.Dockers/filezilla/run-filezilla.sh: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MuhamedHabib/FluentFTP/master/FluentFTP.Dockers/filezilla/run-filezilla.sh -------------------------------------------------------------------------------- /FluentFTP/Client/Interfaces/IFtpLogger.cs: -------------------------------------------------------------------------------- 1 | namespace FluentFTP { 2 | public interface IFtpLogger { 3 | void Log(FtpLogEntry entry); 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /FluentFTP/Client/BaseClient/CanUploadFile.cs: -------------------------------------------------------------------------------- 1 | using FluentFTP.Helpers; 2 | 3 | namespace FluentFTP.Client.BaseClient { 4 | public partial class BaseFtpClient { 5 | 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /FluentFTP/Proxy/Enums/SocksVersion.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace FluentFTP.Proxy.Enums { 7 | internal enum SocksVersion { 8 | V4 = 0x04, 9 | V5 = 0x05 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /FluentFTP/Streams/Interfaces/IFtpStreamConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace FluentFTP.Streams { 8 | public interface IFtpStreamConfig { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /FluentFTP.Logging/README.md: -------------------------------------------------------------------------------- 1 | # FluentFTP.Logging 2 | 3 | Logging adapter to help FluentFTP integrate with MELA-compatible Loggers (NLog, Serilog, Log4Net, PLogger, etc). 4 | 5 | Read the [Logging](https://github.com/robinrodricks/FluentFTP/wiki/Logging) page to learn how to use this library. -------------------------------------------------------------------------------- /FluentFTP/Proxy/Enums/SocksRequestCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace FluentFTP.Proxy.Enums { 7 | internal enum SocksRequestCommand : byte { 8 | Connect = 0x01, 9 | Bind = 0x02, 10 | UdpAssociate = 0x03 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /FluentFTP.Xunit/Attributes/SkippableState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace FluentFTP.Xunit.Attributes { 8 | internal static class SkippableState { 9 | internal static bool ShouldSkip { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /FluentFTP/Proxy/Enums/SocksRequestAddressType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace FluentFTP.Proxy.Enums { 7 | internal enum SocksRequestAddressType { 8 | Unknown = 0x00, 9 | IPv4 = 0x01, 10 | FQDN = 0x03, 11 | IPv6 = 0x04 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /FluentFTP.Dockers/filezilla/get-filezilla.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | wget -O tempfile $1 4 | 5 | STEP2=$(cat tempfile | grep linux-gnu.deb) 6 | echo " " 7 | echo $STEP2 8 | 9 | rm tempfile 10 | 11 | STEP3=$(echo $STEP2 | grep -P -o '(?<=a href=").*?(?=")') 12 | echo " " 13 | echo $STEP3 14 | 15 | wget -O FileZilla-Server.deb $STEP3 16 | -------------------------------------------------------------------------------- /FluentFTP.Dockers/glftpd/xinetd_glftpd.conf: -------------------------------------------------------------------------------- 1 | service glftpd 2 | { 3 | disable = no 4 | flags = REUSE IPv6 5 | socket_type = stream 6 | protocol = tcp 7 | wait = no 8 | user = root 9 | server = /glftpd/bin/glftpd 10 | server_args = -l -i -o -e 11 | } 12 | -------------------------------------------------------------------------------- /FluentFTP.Dockers/pyftpdlib/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM python:3.10-slim 2 | 3 | MAINTAINER Andriy Kogut "kogut.andriy@gmail.com" 4 | 5 | COPY . /app 6 | WORKDIR /app 7 | RUN pip install -r requirements.txt 8 | 9 | RUN mkdir /ftp_root 10 | RUN mkdir /ftp_root/nobody 11 | RUN mkdir /ftp_root/user 12 | 13 | EXPOSE 20 21 14 | 15 | CMD python ftpd.py 16 | -------------------------------------------------------------------------------- /FluentFTP/Proxy/Enums/SocksAuthType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace FluentFTP.Proxy.Enums { 7 | internal enum SocksAuthType { 8 | NoAuthRequired = 0x00, 9 | GSSAPI = 0x01, 10 | UsernamePassword = 0x02, 11 | NoAcceptableMethods = 0xFF 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /FluentFTP.Dockers/glftpd/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | glftpd: 3 | build: 4 | context: . 5 | network: host 6 | restart: unless-stopped 7 | ports: 8 | - 0.0.0.0:20:20 9 | - 0.0.0.0:21:21 10 | - 21100-21110:21100-21199 11 | volumes: 12 | - ./home:/home/glftpd 13 | - ./logs:/var/log/glftpd 14 | -------------------------------------------------------------------------------- /FluentFTP.Xunit/Attributes/SkipTestException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace FluentFTP.Xunit.Attributes { 8 | public class SkipTestException : Exception { 9 | public SkipTestException(string reason) 10 | : base(reason) { } 11 | } 12 | } -------------------------------------------------------------------------------- /FluentFTP.Dockers/bftpd/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | bftpd: 3 | build: 4 | context: . 5 | network: host 6 | restart: unless-stopped 7 | restart: always 8 | ports: 9 | - 0.0.0.0:20:20 10 | - 0.0.0.0:21:21 11 | - 21100-21199:21100-21199 12 | volumes: 13 | - ./home:/home/bftpd 14 | - ./logs:/var/log/bftpd 15 | -------------------------------------------------------------------------------- /FluentFTP.Dockers/vsftpd/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | vsftpd: 3 | build: 4 | context: . 5 | network: host 6 | restart: unless-stopped 7 | restart: always 8 | ports: 9 | - 0.0.0.0:20:20 10 | - 0.0.0.0:21:21 11 | - 21100-21199:21100-21199 12 | volumes: 13 | - ./home:/home/vsftpd 14 | - ./logs:/var/log/vsftpd 15 | -------------------------------------------------------------------------------- /FluentFTP.Dockers/apache/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | apache: 3 | build: 4 | context: . 5 | network: host 6 | restart: unless-stopped 7 | restart: always 8 | network_mode: "host" 9 | ports: 10 | - 0.0.0.0:20:20 11 | - 0.0.0.0:21:21 12 | - 21100-21199:21100-21199 13 | volumes: 14 | - ./home:/home/apache 15 | - ./logs:/var/log/apache 16 | -------------------------------------------------------------------------------- /FluentFTP.Dockers/common-debian-slim/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # FluentFTP Integration Test: Debian Run Image 3 | # 4 | 5 | FROM debian:bullseye-slim AS build 6 | 7 | SHELL ["/bin/bash", "-c"] 8 | 9 | ARG DEBIAN_FRONTEND=noninteractive 10 | ARG APT_CMD='apt install -y --no-install-recommends' 11 | 12 | COPY sources.list /etc/apt/sources.list 13 | 14 | RUN apt update && apt upgrade -y 15 | -------------------------------------------------------------------------------- /FluentFTP.Dockers/proftpd/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | proftpd: 3 | build: 4 | context: . 5 | network: host 6 | restart: unless-stopped 7 | restart: always 8 | network_mode: "host" 9 | ports: 10 | - 0.0.0.0:20:20 11 | - 0.0.0.0:21:21 12 | - 21100-21199:21100-21199 13 | volumes: 14 | - ./home:/home/proftpd 15 | - ./logs:/var/log/proftpd 16 | -------------------------------------------------------------------------------- /FluentFTP.Dockers/pureftpd/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | pureftpd: 3 | build: 4 | context: . 5 | network: host 6 | restart: unless-stopped 7 | restart: always 8 | network_mode: "host" 9 | ports: 10 | - 0.0.0.0:20:20 11 | - 0.0.0.0:21:21 12 | - 21100-21199:21100-21199 13 | volumes: 14 | - ./home:/home/pureftpd 15 | - ./logs:/var/log/pureftpd 16 | -------------------------------------------------------------------------------- /FluentFTP.Dockers/filezilla/docker-compose.yml: -------------------------------------------------------------------------------- 1 | services: 2 | filezilla: 3 | build: 4 | context: . 5 | network: host 6 | restart: unless-stopped 7 | ports: 8 | - 0.0.0.0:20:20 9 | - 0.0.0.0:21:21 10 | - 0.0.0.0:14149:14149 11 | - 21100-21110:21100-21199 12 | volumes: 13 | - ./home:/home/filezilla 14 | - ./logs:/var/log/filezilla 15 | -------------------------------------------------------------------------------- /FluentFTP.Dockers/common-mirror/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # FluentFTP Integration Test: Debian Mirror Selection 3 | # 4 | 5 | FROM python:3.10-slim AS prebuild 6 | 7 | SHELL ["/bin/bash", "-c"] 8 | 9 | WORKDIR /usr/src/app 10 | RUN pip install --user apt-smart 11 | 12 | COPY run.sh /usr/bin/ 13 | 14 | RUN sed -i -e "s/\r//" /usr/bin/run.sh && \ 15 | chmod +x /usr/bin/run.sh 16 | 17 | CMD ["/usr/bin/run.sh"] -------------------------------------------------------------------------------- /FluentFTP.Xunit/Attributes/SkippableFactAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Xunit; 7 | using Xunit.Sdk; 8 | 9 | namespace FluentFTP.Xunit.Attributes { 10 | [XunitTestCaseDiscoverer("FluentFTP.Xunit.Attributes.Internal.SkippableFactDiscoverer", "FluentFTP.Xunit")] 11 | public class SkippableFactAttribute : FactAttribute { } 12 | } -------------------------------------------------------------------------------- /FluentFTP/Enums/FtpObjectType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentFTP { 4 | /// 5 | /// Type of file system of object 6 | /// 7 | public enum FtpObjectType { 8 | /// 9 | /// A file 10 | /// 11 | File, 12 | 13 | /// 14 | /// A directory 15 | /// 16 | Directory, 17 | 18 | /// 19 | /// A symbolic link 20 | /// 21 | Link 22 | } 23 | } -------------------------------------------------------------------------------- /FluentFTP.Xunit/Attributes/SkippableTheoryAttribute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Xunit; 7 | using Xunit.Sdk; 8 | 9 | namespace FluentFTP.Xunit.Attributes { 10 | [XunitTestCaseDiscoverer("FluentFTP.Xunit.Attributes.Internal.SkippableTheoryDiscoverer", "FluentFTP.Xunit")] 11 | public class SkippableTheoryAttribute : TheoryAttribute { } 12 | } -------------------------------------------------------------------------------- /FluentFTP/Enums/FtpDataType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentFTP { 4 | /// 5 | /// Type of data transfer to do 6 | /// 7 | public enum FtpDataType { 8 | /// 9 | /// ASCII transfer 10 | /// 11 | ASCII, 12 | 13 | /// 14 | /// Binary transfer 15 | /// 16 | Binary, 17 | 18 | /// 19 | /// Not known yet 20 | /// 21 | Unknown 22 | 23 | } 24 | } -------------------------------------------------------------------------------- /FluentFTP/Client/BaseClient/IsProxy.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using FluentFTP.Proxy.AsyncProxy; 3 | using FluentFTP.Proxy.SyncProxy; 4 | 5 | namespace FluentFTP.Client.BaseClient { 6 | public partial class BaseFtpClient { 7 | 8 | /// 9 | /// Checks if this FTP/FTPS connection is made through a proxy. 10 | /// 11 | public bool IsProxy() { 12 | return this is FtpClientProxy or AsyncFtpClientProxy; 13 | } 14 | 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | */bin 2 | */obj 3 | */nuget 4 | 5 | API Reference.shfbproj_jp 6 | Help 7 | 8 | *.sublime-workspace 9 | *.user 10 | *.suo 11 | *.nupkg 12 | .gitignore 13 | NuGetPackages 14 | Releases 15 | project.lock.json 16 | project.multitarget.json 17 | 18 | API_Reference_HTML.shfbproj_jp 19 | CHM 20 | HTML 21 | API_Reference_CHM.shfbproj_jp 22 | .DS_Store 23 | .vs 24 | 25 | FluentFTP.Tests/testdata 26 | *.yaml 27 | /Powershell/FluentFTP.dll 28 | -------------------------------------------------------------------------------- /FluentFTP.VBExamples/VBExamples.vbproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net5.0 5 | Library 6 | latest 7 | 8 | 9 | AnyCPU;x64 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /FluentFTP.Dockers/common-debian/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # FluentFTP Integration Test: Debian Build Image 3 | # 4 | 5 | FROM debian:bullseye-slim AS build 6 | 7 | SHELL ["/bin/bash", "-c"] 8 | 9 | ARG DEBIAN_FRONTEND=noninteractive 10 | ARG APT_CMD='apt install -y --no-install-recommends' 11 | 12 | COPY sources.list /etc/apt/sources.list 13 | 14 | RUN apt update && apt upgrade -y && apt install -y apt-utils && \ 15 | \ 16 | $APT_CMD \ 17 | ca-certificates \ 18 | wget \ 19 | build-essential 20 | -------------------------------------------------------------------------------- /FluentFTP/Events/FtpSocketStreamSslValidation.cs: -------------------------------------------------------------------------------- 1 | namespace FluentFTP { 2 | 3 | /// 4 | /// Event fired if a bad SSL certificate is encountered. This even is used internally; if you 5 | /// don't have a specific reason for using it you are probably looking for FtpSslValidation. 6 | /// 7 | /// 8 | /// 9 | public delegate void FtpSocketStreamSslValidation(FtpSocketStream stream, FtpSslValidationEventArgs e); 10 | 11 | } -------------------------------------------------------------------------------- /FluentFTP/Client/BaseClient/LoadProfile.cs: -------------------------------------------------------------------------------- 1 | using FluentFTP.Client.Modules; 2 | 3 | namespace FluentFTP.Client.BaseClient { 4 | public partial class BaseFtpClient { 5 | 6 | /// 7 | /// Load the given connection profile and configure the FTP client instance accordingly. 8 | /// 9 | /// Connection profile. Not modified. 10 | public void LoadProfile(FtpProfile profile) { 11 | ConnectModule.LoadProfile(this, profile); 12 | } 13 | 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | version: 1.0.0.{build} 2 | 3 | image: Visual Studio 2017 4 | 5 | configuration: Release 6 | 7 | before_build: 8 | - cmd: msbuild FluentFTP.sln /t:Restore 9 | 10 | build: 11 | verbosity: minimal 12 | 13 | after_build: 14 | - cmd: msbuild FluentFTP.sln /t:FluentFTP:Pack /p:Configuration=Release 15 | 16 | test_script: 17 | - cmd: >- 18 | dotnet test fluentftp.tests -f net452 19 | 20 | dotnet test fluentftp.tests -f netcoreapp1.0 21 | 22 | artifacts: 23 | - path: '**\*.nupkg' 24 | -------------------------------------------------------------------------------- /FluentFTP/Model/FtpLogEntry.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentFTP { 4 | /// 5 | /// Metadata of a single log message. 6 | /// 7 | public readonly struct FtpLogEntry { 8 | public FtpTraceLevel Severity { get; } 9 | public string Message { get; } 10 | public Exception Exception { get; } 11 | 12 | public FtpLogEntry(FtpTraceLevel severity, string msg, Exception ex = null) { 13 | Severity = severity; 14 | Message = msg; 15 | Exception = ex; 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /FluentFTP/Model/FtpSizeReply.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace FluentFTP { 6 | 7 | /// 8 | /// Reply from a SIZE command 9 | /// 10 | public class FtpSizeReply { 11 | 12 | /// 13 | /// The returned file size 14 | /// 15 | public long FileSize { get; set; } 16 | 17 | /// 18 | /// The reply we got 19 | /// 20 | public FtpReply Reply { get; set; } 21 | 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /FluentFTP/Proxy/Enums/SocksReply.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace FluentFTP.Proxy.Enums { 7 | internal enum SocksReply { 8 | Succeeded = 0x00, 9 | GeneralSOCKSServerFailure = 0x01, 10 | NotAllowedByRuleset = 0x02, 11 | NetworkUnreachable = 0x03, 12 | HostUnreachable = 0x04, 13 | ConnectionRefused = 0x05, 14 | TTLExpired = 0x06, 15 | CommandNotSupported = 0x07, 16 | AddressTypeNotSupported = 0x08 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /FluentFTP.Dockers/glftpd/xinetd.conf: -------------------------------------------------------------------------------- 1 | # Simple configuration file for xinetd 2 | # 3 | # Some defaults, and include /etc/xinetd.d/ 4 | 5 | defaults 6 | { 7 | 8 | # Please note that you need a log_type line to be able to use log_on_success 9 | # and log_on_failure. The default is the following : 10 | # log_type = SYSLOG daemon info 11 | 12 | log_type = FILE /var/log/xinetdlog 13 | log_on_success = HOST PID 14 | log_on_failure = HOST 15 | } 16 | 17 | includedir /etc/xinetd.d 18 | -------------------------------------------------------------------------------- /FluentFTP/Enums/FtpPermission.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentFTP { 4 | /// 5 | /// Types of file permissions 6 | /// 7 | [Flags] 8 | public enum FtpPermission : uint { 9 | /// 10 | /// No access 11 | /// 12 | None = 0, 13 | 14 | /// 15 | /// Executable 16 | /// 17 | Execute = 1, 18 | 19 | /// 20 | /// Writable 21 | /// 22 | Write = 2, 23 | 24 | /// 25 | /// Readable 26 | /// 27 | Read = 4 28 | } 29 | } -------------------------------------------------------------------------------- /FluentFTP.Dockers/bftpd/run-bftpd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # stdout server info: 4 | cat << EOB 5 | ************************************************* 6 | * * 7 | * Docker image: fluentftp bftpd * 8 | * * 9 | ************************************************* 10 | 11 | SERVER SETTINGS 12 | --------------- 13 | · FTP User: fluentuser 14 | · FTP Password: fluentpass 15 | EOB 16 | 17 | # Run bftpd: 18 | &>/dev/null /usr/sbin/bftpd -c /usr/etc/bftpd.conf -D 19 | -------------------------------------------------------------------------------- /FluentFTP.Dockers/apache/run-apache.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # stdout server info: 4 | cat << EOB 5 | ************************************************* 6 | * * 7 | * Docker image: fluentftp apache * 8 | * * 9 | ************************************************* 10 | 11 | SERVER SETTINGS 12 | --------------- 13 | · FTP User: fluentuser 14 | · FTP Password: fluentpass 15 | EOB 16 | 17 | # Run apache: 18 | cd /root/apache 19 | bin/ftpd.sh res/conf/config.xml 20 | -------------------------------------------------------------------------------- /FluentFTP/Client/SyncClient/GetWorkingDirectory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using FluentFTP.Helpers; 4 | using FluentFTP.Client.Modules; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace FluentFTP { 9 | public partial class FtpClient { 10 | 11 | /// 12 | /// Gets the current working directory 13 | /// 14 | /// The current working directory, ./ if the response couldn't be parsed. 15 | public string GetWorkingDirectory() { 16 | return ((IInternalFtpClient)this).GetWorkingDirectoryInternal(); 17 | } 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /FluentFTP/Enums/FtpSpecialPermissions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentFTP { 4 | /// 5 | /// Types of special UNIX permissions 6 | /// 7 | [Flags] 8 | public enum FtpSpecialPermissions : int { 9 | /// 10 | /// No special permissions are set 11 | /// 12 | None = 0, 13 | 14 | /// 15 | /// Sticky bit is set 16 | /// 17 | Sticky = 1, 18 | 19 | /// 20 | /// SGID bit is set 21 | /// 22 | SetGroupID = 2, 23 | 24 | /// 25 | /// SUID bit is set 26 | /// 27 | SetUserID = 4 28 | } 29 | } -------------------------------------------------------------------------------- /FluentFTP.Xunit/Docker/DockerFtpConfig.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace FluentFTP.Xunit.Docker { 8 | public static class DockerFtpConfig { 9 | 10 | /// 11 | /// Detect if running inside GitHub Actions CI pipeline. 12 | /// 13 | public static bool IsCI = string.Equals(Environment.GetEnvironmentVariable("CI"), "true", StringComparison.OrdinalIgnoreCase); 14 | 15 | public static string FtpUser = "fluentuser"; 16 | 17 | public static string FtpPass = "fluentpass"; 18 | 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /FluentFTP/Exceptions/FtpProtocolUnsupportedException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | #if NETFRAMEWORK 3 | using System.Runtime.Serialization; 4 | #endif 5 | 6 | namespace FluentFTP.Exceptions { 7 | 8 | /// 9 | /// FtpProtocolUnsupportedException 10 | /// 11 | #if NETFRAMEWORK 12 | [Serializable] 13 | #endif 14 | public class FtpProtocolUnsupportedException : FtpException { 15 | 16 | /// 17 | /// FtpProtocolUnsupportedException 18 | /// 19 | /// Error message 20 | public FtpProtocolUnsupportedException(string message) 21 | : base(message) { 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /FluentFTP/Client/BaseClient/Disconnect.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace FluentFTP.Client.BaseClient { 5 | 6 | public partial class BaseFtpClient { 7 | 8 | /// 9 | /// Disconnects from the server 10 | /// 11 | void IInternalFtpClient.DisconnectInternal() { 12 | ((IFtpClient)this).Disconnect(); 13 | } 14 | 15 | /// 16 | /// Disconnects from the server asynchronously 17 | /// 18 | async Task IInternalFtpClient.DisconnectInternal(CancellationToken token) { 19 | await ((IAsyncFtpClient)this).Disconnect(token); 20 | } 21 | 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /FluentFTP/Client/SyncClient/GetReply.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using System.Threading; 3 | 4 | namespace FluentFTP { 5 | public partial class FtpClient { 6 | 7 | /// 8 | /// Retrieves a reply from the server. 9 | /// Support "normal" mode waiting for a command reply, subject to timeout exception 10 | /// and "exhaustNoop" mode, which waits for 10 seconds to collect out of band NOOP responses 11 | /// 12 | /// FtpReply representing the response from the server 13 | public FtpReply GetReply() { 14 | return ((IInternalFtpClient)this).GetReplyInternal(null, false, 0); 15 | } 16 | } 17 | } -------------------------------------------------------------------------------- /FluentFTP/Enums/FtpIpVersion.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentFTP { 4 | /// 5 | /// IP Versions to allow when connecting 6 | /// to a server. 7 | /// 8 | [Flags] 9 | public enum FtpIpVersion : int { 10 | 11 | /// 12 | /// Unknown protocol. 13 | /// 14 | Unknown = 0, 15 | 16 | /// 17 | /// Internet Protocol Version 4 18 | /// 19 | IPv4 = 1, 20 | 21 | /// 22 | /// Internet Protocol Version 6 23 | /// 24 | IPv6 = 2, 25 | 26 | /// 27 | /// Allow any supported version 28 | /// 29 | ANY = IPv4 | IPv6 30 | } 31 | } -------------------------------------------------------------------------------- /FluentFTP.CSharpExamples/Extensions.cs: -------------------------------------------------------------------------------- 1 | namespace Examples { 2 | internal static class Extensions { 3 | public static string FormatBytes(this long val) { 4 | return ((double)val).FormatBytes(); 5 | } 6 | 7 | public static string FormatBytes(this int val) { 8 | return ((double)val).FormatBytes(); 9 | } 10 | 11 | public static string FormatBytes(this double val) { 12 | var units = new[] { "B", "KB", "MB", "GB", "TB" }; 13 | var count = 0; 14 | 15 | while (count + 1 < units.Length && val >= 1024) { 16 | count += 1; 17 | val /= 1024; 18 | } 19 | 20 | return string.Format("{0:0.00} {1}", val, units[count]); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /FluentFTP.Tests/Unit/ExceptionTests.cs: -------------------------------------------------------------------------------- 1 | using FluentFTP.Exceptions; 2 | using FluentFTP.Helpers; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using Xunit; 7 | 8 | namespace FluentFTP.Tests.Unit { 9 | public class ExceptionTests { 10 | 11 | [Fact] 12 | public void FtpCommandException_includes_CompletionCode_in_message() { 13 | 14 | Action act = () => throw new FtpCommandException("501", "MyErrorMessage"); 15 | 16 | var exception = Assert.Throws(act); 17 | Assert.Contains("501", exception.ToString()); 18 | Assert.Contains("MyErrorMessage", exception.ToString()); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /FluentFTP.Xunit/Docker/DockerFtpContainerIndex.cs: -------------------------------------------------------------------------------- 1 | using FluentFTP.Xunit.Docker.Containers; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace FluentFTP.Xunit.Docker { 9 | internal static class DockerFtpContainerIndex { 10 | 11 | public static List Index = new List { 12 | new ApacheContainer(), 13 | new BFtpdContainer(), 14 | new FileZillaContainer(), 15 | new GlFtpdContainer(), 16 | new ProFtpdContainer(), 17 | new PureFtpdContainer(), 18 | new PyFtpdLibContainer(), 19 | new VsFtpdContainer(), 20 | }; 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /FluentFTP/Client/SyncClient/Execute.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net.Sockets; 4 | using System.Text.RegularExpressions; 5 | using System.Linq; 6 | using System.Net; 7 | using FluentFTP.Helpers; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | 11 | namespace FluentFTP { 12 | public partial class FtpClient { 13 | 14 | /// 15 | /// Executes a command 16 | /// 17 | /// The command to execute 18 | /// The servers reply to the command 19 | public FtpReply Execute(string command) { 20 | return ((IInternalFtpClient)this).ExecuteInternal(command); 21 | } 22 | 23 | } 24 | } -------------------------------------------------------------------------------- /FluentFTP.Dockers/proftpd/run-proftpd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # stdout server info: 4 | cat << EOB 5 | ************************************************* 6 | * * 7 | * Docker image: fluentftp proftpd * 8 | * * 9 | ************************************************* 10 | 11 | SERVER SETTINGS 12 | --------------- 13 | · FTP User: fluentuser 14 | · FTP Password: fluentpass 15 | · SSL: $USE_SSL 16 | EOB 17 | 18 | if [[ -n "${USE_SSL}" ]]; then 19 | sed -i "s/^\(# \)\?TLSEngine.*$/TLSEngine on/" /etc/proftpd/tls.conf 20 | fi 21 | 22 | # Run proftpd: 23 | &>/dev/null /usr/sbin/proftpd -n 24 | -------------------------------------------------------------------------------- /FluentFTP.Dockers/pureftpd/run-pureftpd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # stdout server info: 4 | cat << EOB 5 | ************************************************* 6 | * * 7 | * Docker image: fluentftp pureftpd * 8 | * * 9 | ************************************************* 10 | 11 | SERVER SETTINGS 12 | --------------- 13 | · FTP User: fluentuser 14 | · FTP Password: fluentpass 15 | · SSL: $USE_SSL 16 | EOB 17 | 18 | if [[ -n "${USE_SSL}" ]]; then 19 | TLS=1 20 | else 21 | TLS=0 22 | fi 23 | 24 | # Run pureftpd: 25 | &>/dev/null /usr/sbin/pure-ftpd -A -E -j -R -l unix -p 21100:21199 --tls $TLS 26 | -------------------------------------------------------------------------------- /FluentFTP.Dockers/vsftpd/run-vsftpd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # stdout server info: 4 | cat << EOB 5 | ************************************************* 6 | * * 7 | * Docker image: fluentftp vsftpd * 8 | * * 9 | ************************************************* 10 | 11 | SERVER SETTINGS 12 | --------------- 13 | · FTP User: fluentuser 14 | · FTP Password: fluentpass 15 | · SSL: $USE_SSL 16 | EOB 17 | 18 | if [[ -n "${USE_SSL}" ]]; then 19 | sed -i "s/^\(# \)\?ssl_enable=.*$/ssl_enable=YES/" /etc/vsftpd.conf 20 | fi 21 | 22 | # Run vsftpd: 23 | &>/dev/null /usr/sbin/vsftpd /etc/vsftpd.conf 24 | -------------------------------------------------------------------------------- /FluentFTP/Client/SyncClient/DisableUTF8.cs: -------------------------------------------------------------------------------- 1 | using FluentFTP.Exceptions; 2 | 3 | 4 | 5 | using System.Text; 6 | 7 | namespace FluentFTP { 8 | public partial class FtpClient { 9 | 10 | /// 11 | /// Disables UTF8 support and changes the Encoding property 12 | /// back to ASCII. If the server returns an error when trying 13 | /// to turn UTF8 off a FtpCommandException will be thrown. 14 | /// 15 | public void DisableUTF8() { 16 | FtpReply reply; 17 | 18 | reply = Execute("OPTS UTF8 OFF"); 19 | 20 | if (!reply.Success) { 21 | throw new FtpCommandException(reply); 22 | } 23 | 24 | m_textEncoding = Encoding.ASCII; 25 | m_textEncodingAutoUTF = false; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /FluentFTP/Enums/FtpStatus.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace FluentFTP { 6 | /// 7 | /// The result of an upload or download operation 8 | /// 9 | public enum FtpStatus { 10 | 11 | /// 12 | /// The upload or download failed with an error transferring, or the source file did not exist 13 | /// 14 | Failed = 0, 15 | 16 | /// 17 | /// The upload or download completed successfully 18 | /// 19 | Success = 1, 20 | 21 | /// 22 | /// The upload or download was skipped because the file already existed on the target 23 | /// 24 | Skipped = 2 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /FluentFTP/Exceptions/FtpProxyException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentFTP.Exceptions { 4 | 5 | /// 6 | /// FtpProxyException 7 | /// 8 | public class FtpProxyException : FtpException { 9 | 10 | /// 11 | /// FtpProxyException 12 | /// 13 | public FtpProxyException() : base("Exception with a FTP proxy server.") { 14 | } 15 | 16 | /// 17 | /// FtpProxyException 18 | /// 19 | public FtpProxyException(string message) 20 | : base(message) { 21 | } 22 | 23 | /// 24 | /// FtpProxyException 25 | /// 26 | public FtpProxyException(string message, Exception inner) 27 | : base(message, inner) { 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /FluentFTP.Tests/Unit/HelperTests.cs: -------------------------------------------------------------------------------- 1 | using FluentFTP.Helpers; 2 | using FluentFTP.Model.Functions; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Diagnostics; 6 | using System.Net; 7 | using System.Net.Sockets; 8 | using System.Threading.Tasks; 9 | using Xunit; 10 | using Xunit.Sdk; 11 | 12 | namespace FluentFTP.Tests.Unit { 13 | public class HelperTests { 14 | 15 | [Fact] 16 | public void ValuePrinterTest() { 17 | 18 | var obj = new FtpAutoDetectConfig(); 19 | var txt = ValuePrinter.ObjectToString(obj); 20 | 21 | Assert.Equal("CloneConnection = True, FirstOnly = True, IncludeImplicit = True, AbortOnTimeout = True, RequireEncryption = False, ProtocolPriority = [Tls12]", txt); 22 | } 23 | 24 | } 25 | } -------------------------------------------------------------------------------- /FluentFTP/Streams/Interfaces/IFtpStream.cs: -------------------------------------------------------------------------------- 1 | using FluentFTP.Client.BaseClient; 2 | using System.IO; 3 | using System.Net.Sockets; 4 | using System.Security.Authentication; 5 | 6 | namespace FluentFTP.Streams { 7 | public interface IFtpStream { 8 | 9 | void Init( 10 | BaseFtpClient client, 11 | string targetHost, 12 | Socket socket, 13 | CustomRemoteCertificateValidationCallback customRemoteCertificateValidation, 14 | bool isControl, 15 | IFtpStream controlConnStream, 16 | IFtpStreamConfig config); 17 | 18 | Stream GetBaseStream(); 19 | 20 | bool CanRead(); 21 | bool CanWrite(); 22 | 23 | SslProtocols GetSslProtocol(); 24 | string GetCipherSuite(); 25 | 26 | void Dispose(); 27 | 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /FluentFTP/Helpers/Uris.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentFTP.Helpers { 4 | /// 5 | /// Extension methods related to FTP tasks 6 | /// 7 | internal static class Uris { 8 | /// 9 | /// Ensures that the URI points to a server, and not a directory or invalid path. 10 | /// 11 | /// 12 | public static void ValidateFtpServer(this Uri uri) { 13 | if (string.IsNullOrEmpty(uri.PathAndQuery)) { 14 | throw new UriFormatException("The supplied URI does not contain a valid path."); 15 | } 16 | 17 | if (uri.PathAndQuery.EndsWith("/")) { 18 | throw new UriFormatException("The supplied URI points at a directory."); 19 | } 20 | } 21 | 22 | 23 | } 24 | } -------------------------------------------------------------------------------- /FluentFTP/Client/SyncClient/Handshake.cs: -------------------------------------------------------------------------------- 1 | using FluentFTP.Exceptions; 2 | using System; 3 | using System.IO; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace FluentFTP { 8 | public partial class FtpClient { 9 | 10 | /// 11 | /// Called during Connect(). Typically extended by FTP proxies. 12 | /// 13 | protected virtual void Handshake() { 14 | FtpReply reply; 15 | if (!(reply = GetReply()).Success) { 16 | if (reply.Code == null) { 17 | throw new IOException("The connection was terminated before a greeting could be read."); 18 | } 19 | else { 20 | throw new FtpCommandException(reply); 21 | } 22 | } 23 | 24 | HandshakeReply = reply; 25 | } 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /FluentFTP/Enums/FtpFolderSyncMode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace FluentFTP { 6 | /// 7 | /// Determines how we handle downloading and uploading folders 8 | /// 9 | public enum FtpFolderSyncMode { 10 | 11 | /// 12 | /// Dangerous but useful method! 13 | /// Uploads/downloads all the missing files to update the server/local filesystem. 14 | /// Deletes the extra files to ensure that the target is an exact mirror of the source. 15 | /// 16 | Mirror, 17 | 18 | /// 19 | /// Safe method! 20 | /// Uploads/downloads all the missing files to update the server/local filesystem. 21 | /// 22 | Update, 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /FluentFTP/Client/BaseClient/ConvertDate.cs: -------------------------------------------------------------------------------- 1 | using FluentFTP.Client.Modules; 2 | using System; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | 6 | namespace FluentFTP.Client.BaseClient { 7 | 8 | public partial class BaseFtpClient { 9 | 10 | /// 11 | /// If `reverse` is false, converts the date provided by the FTP server into the timezone required locally. 12 | /// If `reverse` is true, converts the local timezone date into the date required by the FTP server. 13 | /// 14 | /// Affected by properties: TimeConversion, ServerTimeZone, ClientTimeZone. 15 | /// 16 | public DateTime ConvertDate(DateTime date, bool reverse = false) { 17 | return TimezoneModule.ConvertDate(date, Config, reverse); 18 | } 19 | 20 | } 21 | } -------------------------------------------------------------------------------- /FluentFTP/Client/SyncClient/IsStillConnected.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using FluentFTP.Helpers; 5 | 6 | namespace FluentFTP { 7 | public partial class FtpClient { 8 | 9 | /// 10 | /// Performs a series of tests to check if we are still connected to the FTP server. 11 | /// More thourough than IsConnected. 12 | /// 13 | /// How to wait for connection confirmation 14 | /// bool connection status 15 | public bool IsStillConnected(int timeout = 10000) { 16 | LogFunction(nameof(IsStillConnected), new object[] { timeout }); 17 | 18 | return ((IInternalFtpClient)this).IsStillConnectedInternal(timeout); 19 | } 20 | 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /FluentFTP/Enums/FtpsBuffering.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentFTP { 4 | /// 5 | /// Determines how SSL Buffering is handled 6 | /// 7 | public enum FtpsBuffering { 8 | /// 9 | /// Enables SSL Buffering to massively speed up FTPS operations except when: 10 | /// Under .NET 5.0 and later due to platform issues (see issue 682 in FluentFTP issue tracker). 11 | /// On the control connection 12 | /// For proxy connections 13 | /// If NOOPs are configured to be used 14 | /// 15 | Auto, 16 | 17 | /// 18 | /// Always disables SSL Buffering to reduce FTPS connectivity issues. 19 | /// 20 | Off, 21 | 22 | /// 23 | /// Same as "Auto" 24 | /// 25 | On 26 | } 27 | } -------------------------------------------------------------------------------- /FluentFTP.VBExamples/DeleteFile.vb: -------------------------------------------------------------------------------- 1 | Imports System 2 | Imports System.Net 3 | Imports System.Threading 4 | Imports System.Threading.Tasks 5 | Imports FluentFTP 6 | 7 | Namespace Examples 8 | Friend Module DeleteFileExample 9 | Sub DeleteFile() 10 | Using conn = New FtpClient("127.0.0.1", "ftptest", "ftptest") 11 | conn.Connect() 12 | conn.DeleteFile("/full/or/relative/path/to/file") 13 | End Using 14 | End Sub 15 | 16 | Async Function DeleteDirectoryAsync() As Task 17 | Dim token = New CancellationToken() 18 | 19 | Using conn = New AsyncFtpClient("127.0.0.1", "ftptest", "ftptest") 20 | Await conn.Connect(token) 21 | Await conn.DeleteFile("/full/or/relative/path/to/file") 22 | End Using 23 | End Function 24 | End Module 25 | End Namespace 26 | -------------------------------------------------------------------------------- /FluentFTP.CSharpExamples/DeleteFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using FluentFTP; 6 | 7 | namespace Examples { 8 | internal static class DeleteFileExample { 9 | 10 | public static void DeleteFile() { 11 | using (var conn = new FtpClient("127.0.0.1", "ftptest", "ftptest")) { 12 | conn.Connect(); 13 | 14 | conn.DeleteFile("/full/or/relative/path/to/file"); 15 | } 16 | } 17 | 18 | public static async Task DeleteDirectoryAsync() { 19 | var token = new CancellationToken(); 20 | using (var conn = new AsyncFtpClient("127.0.0.1", "ftptest", "ftptest")) { 21 | await conn.Connect(token); 22 | 23 | await conn.DeleteFile("/full/or/relative/path/to/file", token); 24 | } 25 | } 26 | 27 | } 28 | } -------------------------------------------------------------------------------- /FluentFTP/Client/SyncClient/GetChmod.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using FluentFTP.Helpers; 5 | 6 | namespace FluentFTP { 7 | public partial class FtpClient { 8 | 9 | /// 10 | /// Retrieve the permissions of the given file/folder as an integer in the CHMOD format. 11 | /// Throws FtpCommandException if there is an issue. 12 | /// Returns 0 if the server did not specify a permission value. 13 | /// Use `GetFilePermissions` if you required the permissions in the FtpPermission format. 14 | /// 15 | /// The full or relative path to the item 16 | public int GetChmod(string path) { 17 | var item = GetFilePermissions(path); 18 | return item != null ? item.Chmod : 0; 19 | } 20 | 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /FluentFTP.CSharpExamples/Log_NLog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using FluentFTP; 4 | using FluentFTP.Logging; 5 | using NLog.Extensions.Logging; 6 | 7 | namespace Examples { 8 | internal static class NLogExample { 9 | 10 | public static void Configure() { 11 | 12 | using (var conn = new FtpClient()) { 13 | conn.Host = "localhost"; 14 | conn.Credentials = new NetworkCredential("ftptest", "ftptest"); 15 | 16 | 17 | // create NLog logger 18 | var nlogLogger = new NLogLoggerProvider(); 19 | 20 | // wrap with MELA ILogger 21 | var microsoftLogger = nlogLogger.CreateLogger(typeof(Log4NetExample).FullName); 22 | 23 | // wrap with FtpLogAdapter 24 | conn.Logger = new FtpLogAdapter(microsoftLogger); 25 | 26 | 27 | conn.Connect(); 28 | } 29 | } 30 | 31 | } 32 | } -------------------------------------------------------------------------------- /FluentFTP/Client/SyncClient/GetAbsoluteFilePath.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using FluentFTP.Helpers; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace FluentFTP { 8 | public partial class FtpClient { 9 | 10 | /// 11 | /// Concat a path and a filename 12 | /// 13 | protected string GetAbsoluteFilePath(string path, string fileName) { 14 | string filePath = null; 15 | if (ServerHandler != null && ServerHandler.IsCustomGetAbsoluteFilePath()) { 16 | filePath = ServerHandler.GetAbsoluteFilePath(this, path, fileName); 17 | } 18 | 19 | if (filePath != null) { 20 | return filePath; 21 | } 22 | 23 | path = !path.EndsWith("/") ? path + "/" + fileName : path + fileName; 24 | 25 | return path; 26 | } 27 | 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /FluentFTP/Enums/FtpEncryptionMode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentFTP { 4 | /// 5 | /// Defines the type of encryption to use 6 | /// 7 | public enum FtpEncryptionMode { 8 | /// 9 | /// Plain text. 10 | /// 11 | None, 12 | 13 | /// 14 | /// FTPS encryption is used from the start of the connection, port 990. 15 | /// 16 | Implicit, 17 | 18 | /// 19 | /// Connection starts in plain text and FTPS encryption is enabled 20 | /// with the AUTH command immediately after the server greeting. 21 | /// 22 | Explicit, 23 | 24 | /// 25 | /// FTPS encryption is used if supported by the server, otherwise it falls back to plaintext FTP communication. 26 | /// 27 | Auto 28 | } 29 | } -------------------------------------------------------------------------------- /FluentFTP.VBExamples/SetWorkingDirectory.vb: -------------------------------------------------------------------------------- 1 | Imports System 2 | Imports System.Net 3 | Imports System.Threading 4 | Imports System.Threading.Tasks 5 | Imports FluentFTP 6 | 7 | Namespace Examples 8 | Friend Module SetWorkingDirectoryExample 9 | Sub SetWorkingDirectory() 10 | Using conn = New FtpClient("127.0.0.1", "ftptest", "ftptest") 11 | conn.Connect() 12 | conn.SetWorkingDirectory("/full/or/relative/path") 13 | End Using 14 | End Sub 15 | 16 | Async Function SetWorkingDirectoryAsync() As Task 17 | Dim token = New CancellationToken() 18 | 19 | Using conn = New AsyncFtpClient("127.0.0.1", "ftptest", "ftptest") 20 | Await conn.Connect(token) 21 | Await conn.SetWorkingDirectory("/full/or/relative/path", token) 22 | End Using 23 | End Function 24 | End Module 25 | End Namespace 26 | -------------------------------------------------------------------------------- /FluentFTP/Client/SyncClient/GetAbsoluteDir.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using FluentFTP.Helpers; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace FluentFTP { 8 | public partial class FtpClient { 9 | 10 | /// 11 | /// Ensure a relative dir is absolute by prepending the working dir 12 | /// 13 | protected string GetAbsoluteDir(string path) { 14 | string dirPath = null; 15 | if (ServerHandler != null && ServerHandler.IsCustomGetAbsoluteDir()) { 16 | dirPath = ServerHandler.GetAbsoluteDir(this, path); 17 | } 18 | 19 | if (dirPath != null) { 20 | return dirPath; 21 | } 22 | 23 | path = GetAbsolutePath(path); 24 | 25 | path = !path.EndsWith("/") ? path + "/" : path; 26 | 27 | return path; 28 | } 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /FluentFTP/Enums/FtpTraceLevel.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentFTP { 4 | /// 5 | /// Defines the level of the tracing message. Depending on the framework version this is translated 6 | /// to an equivalent logging level in System.Diagnostices (if available) 7 | /// 8 | public enum FtpTraceLevel { 9 | /// 10 | /// Used for logging Debug or Verbose level messages 11 | /// 12 | Verbose, 13 | 14 | /// 15 | /// Used for logging Informational messages 16 | /// 17 | Info, 18 | 19 | /// 20 | /// Used for logging non-fatal or ignorable error messages 21 | /// 22 | Warn, 23 | 24 | /// 25 | /// Used for logging Error messages that may need investigation 26 | /// 27 | Error 28 | } 29 | } -------------------------------------------------------------------------------- /FluentFTP.CSharpExamples/SetWorkingDirectory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using FluentFTP; 6 | 7 | namespace Examples { 8 | internal static class SetWorkingDirectoryExample { 9 | 10 | public static void SetWorkingDirectory() { 11 | using (var conn = new FtpClient("127.0.0.1", "ftptest", "ftptest")) { 12 | 13 | conn.Connect(); 14 | conn.SetWorkingDirectory("/full/or/relative/path"); 15 | } 16 | } 17 | 18 | public static async Task SetWorkingDirectoryAsync() { 19 | var token = new CancellationToken(); 20 | using (var conn = new AsyncFtpClient("127.0.0.1", "ftptest", "ftptest")) { 21 | 22 | await conn.Connect(token); 23 | await conn.SetWorkingDirectory("/full/or/relative/path", token); 24 | } 25 | } 26 | 27 | } 28 | } -------------------------------------------------------------------------------- /FluentFTP.VBExamples/CreateDirectory.vb: -------------------------------------------------------------------------------- 1 | Imports System 2 | Imports System.Net 3 | Imports System.Threading 4 | Imports System.Threading.Tasks 5 | Imports FluentFTP 6 | 7 | Namespace Examples 8 | Friend Module CreateDirectoryExample 9 | Sub CreateDirectory() 10 | Using conn = New FtpClient("127.0.0.1", "ftptest", "ftptest") 11 | conn.Connect() 12 | conn.CreateDirectory("/test/path/that/should/be/created", True) 13 | End Using 14 | End Sub 15 | 16 | Async Function CreateDirectoryAsync() As Task 17 | Dim token = New CancellationToken() 18 | 19 | Using conn = New AsyncFtpClient("127.0.0.1", "ftptest", "ftptest") 20 | Await conn.Connect(token) 21 | Await conn.CreateDirectory("/test/path/that/should/be/created", True, token) 22 | End Using 23 | End Function 24 | End Module 25 | End Namespace 26 | -------------------------------------------------------------------------------- /FluentFTP/Enums/FtpResponseType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentFTP { 4 | /// 5 | /// The type of response the server responded with 6 | /// 7 | public enum FtpResponseType : int { 8 | /// 9 | /// No response 10 | /// 11 | None = 0, 12 | 13 | /// 14 | /// Success 15 | /// 16 | PositivePreliminary = 1, 17 | 18 | /// 19 | /// Success 20 | /// 21 | PositiveCompletion = 2, 22 | 23 | /// 24 | /// Success 25 | /// 26 | PositiveIntermediate = 3, 27 | 28 | /// 29 | /// Temporary failure 30 | /// 31 | TransientNegativeCompletion = 4, 32 | 33 | /// 34 | /// Permanent failure 35 | /// 36 | PermanentNegativeCompletion = 5 37 | } 38 | } -------------------------------------------------------------------------------- /FluentFTP.CSharpExamples/CreateDirectory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using FluentFTP; 6 | 7 | namespace Examples { 8 | internal static class CreateDirectoryExample { 9 | 10 | public static void CreateDirectory() { 11 | using (var conn = new FtpClient("127.0.0.1", "ftptest", "ftptest")) { 12 | conn.Connect(); 13 | 14 | conn.CreateDirectory("/test/path/that/should/be/created", true); 15 | } 16 | } 17 | 18 | public static async Task CreateDirectoryAsync() { 19 | var token = new CancellationToken(); 20 | using (var conn = new AsyncFtpClient("127.0.0.1", "ftptest", "ftptest")) { 21 | await conn.Connect(token); 22 | 23 | await conn.CreateDirectory("/test/path/that/should/be/created", true, token); 24 | } 25 | } 26 | } 27 | } -------------------------------------------------------------------------------- /FluentFTP/Enums/FtpHashAlgorithm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentFTP { 4 | /// 5 | /// Different types of hashing algorithms for computing checksums. 6 | /// 7 | [Flags] 8 | public enum FtpHashAlgorithm : int { 9 | 10 | /// 11 | /// Automatic algorithm, or hashing not supported. 12 | /// 13 | NONE = 0, 14 | 15 | /// 16 | /// SHA-1 algorithm 17 | /// 18 | SHA1 = 1, 19 | 20 | /// 21 | /// SHA-256 algorithm 22 | /// 23 | SHA256 = 2, 24 | 25 | /// 26 | /// SHA-512 algorithm 27 | /// 28 | SHA512 = 4, 29 | 30 | /// 31 | /// MD5 algorithm 32 | /// 33 | MD5 = 8, 34 | 35 | /// 36 | /// CRC algorithm 37 | /// 38 | CRC = 16 39 | } 40 | } -------------------------------------------------------------------------------- /FluentFTP/Enums/FtpObjectSubType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentFTP { 4 | /// 5 | /// Type of file system of object 6 | /// 7 | public enum FtpObjectSubType { 8 | 9 | /// 10 | /// The default subtype. 11 | /// 12 | Unknown, 13 | 14 | /// 15 | /// A sub directory within the listed directory. 16 | /// (Only set when machine listing is available and type is 'dir') 17 | /// 18 | SubDirectory, 19 | 20 | /// 21 | /// The self directory. 22 | /// (Only set when machine listing is available and type is 'cdir') 23 | /// 24 | SelfDirectory, 25 | 26 | /// 27 | /// The parent directory. 28 | /// (Only set when machine listing is available and type is 'pdir') 29 | /// 30 | ParentDirectory, 31 | 32 | } 33 | } -------------------------------------------------------------------------------- /ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | **FTP Server OS:** Unix / Windows / Embedded 2 | 3 | **FTP Server Type:** Vsftpd / glFTPd / BFTPd / ProFTPD / Pure-FTPd / IBM CS FTP (z/OS, OS/400) / Windows CE / Windows Server IIS / Vax / VMS / OpenVMS / Tandem / HP NonStop 4 | 5 | **Client Computer OS:** Windows / Ubuntu / Debian / Linux Mint / Arch Linux 6 | 7 | **FluentFTP Version:** ? 8 | 9 | **Framework:** .NET 2 / 3.5 / 4 / 5 / 6 / 7 / 8 / UWP / Xamarin / Mono 10 | 11 | 12 | 13 | **Logs :** 14 | 15 | 21 | 22 | ``` 23 | 24 | 25 | 26 | ``` 27 | -------------------------------------------------------------------------------- /FluentFTP/Client/AsyncClient/DisableUTF8.cs: -------------------------------------------------------------------------------- 1 | using FluentFTP.Exceptions; 2 | 3 | using System.Text; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace FluentFTP { 8 | public partial class AsyncFtpClient { 9 | 10 | /// 11 | /// Disables UTF8 support and changes the Encoding property 12 | /// back to ASCII. If the server returns an error when trying 13 | /// to turn UTF8 off a FtpCommandException will be thrown. 14 | /// 15 | public async Task DisableUTF8(CancellationToken token = default(CancellationToken)) { 16 | FtpReply reply; 17 | 18 | reply = await Execute("OPTS UTF8 OFF", token); 19 | 20 | if (!reply.Success) { 21 | throw new FtpCommandException(reply); 22 | } 23 | 24 | m_textEncoding = Encoding.ASCII; 25 | m_textEncodingAutoUTF = false; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /FluentFTP/Enums/FtpConnectionState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentFTP { 4 | 5 | /// 6 | /// Actual connection state from the FTP client to the FTP server, as determined by the NOOP Deamon. 7 | /// 8 | public enum FtpConnectionState { 9 | 10 | /// 11 | /// Unknown state. NOOP Deamon will determine the state in a short while. 12 | /// 13 | Unknown, 14 | 15 | /// 16 | /// Not a good state and it will be brought down, closed and disposed soon. 17 | /// 18 | PendingDisconnect, 19 | 20 | /// 21 | /// Closed and disposed. 22 | /// 23 | Disconnected, 24 | 25 | /// 26 | /// Connected to the FTP server, at least the last time the NOOP daemon checked the connection. 27 | /// 28 | Connected, 29 | 30 | }; 31 | 32 | } -------------------------------------------------------------------------------- /FluentFTP/Client/AsyncClient/Handshake.cs: -------------------------------------------------------------------------------- 1 | using FluentFTP.Exceptions; 2 | using System; 3 | using System.IO; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace FluentFTP { 8 | public partial class AsyncFtpClient { 9 | 10 | /// 11 | /// Called during . Typically extended by FTP proxies. 12 | /// 13 | protected virtual async Task HandshakeAsync(CancellationToken token = default(CancellationToken)) { 14 | FtpReply reply; 15 | if (!(reply = await GetReply(token)).Success) { 16 | if (reply.Code == null) { 17 | throw new IOException("The connection was terminated before a greeting could be read."); 18 | } 19 | else { 20 | throw new FtpCommandException(reply); 21 | } 22 | } 23 | 24 | HandshakeReply = reply; 25 | } 26 | 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /FluentFTP.VBExamples/GetWorkingDirectory.vb: -------------------------------------------------------------------------------- 1 | Imports System 2 | Imports System.Net 3 | Imports System.Threading 4 | Imports System.Threading.Tasks 5 | Imports FluentFTP 6 | 7 | Namespace Examples 8 | Friend Module GetWorkingDirectoryExample 9 | Sub GetWorkingDirectory() 10 | Using conn = New FtpClient("127.0.0.1", "ftptest", "ftptest") 11 | conn.Connect() 12 | Console.WriteLine("The working directory is: " & conn.GetWorkingDirectory()) 13 | End Using 14 | End Sub 15 | 16 | Async Function GetWorkingDirectoryAsync() As Task 17 | Dim token = New CancellationToken() 18 | 19 | Using conn = New AsyncFtpClient("127.0.0.1", "ftptest", "ftptest") 20 | Await conn.Connect(token) 21 | Console.WriteLine("The working directory is: " & Await conn.GetWorkingDirectory(token)) 22 | End Using 23 | End Function 24 | End Module 25 | End Namespace 26 | -------------------------------------------------------------------------------- /FluentFTP/Enums/FtpCompareResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace FluentFTP { 6 | 7 | /// 8 | /// The result of a file comparison operation. 9 | /// 10 | public enum FtpCompareResult { 11 | 12 | /// 13 | /// Success. Local and remote files are exactly equal. 14 | /// 15 | Equal = 1, 16 | 17 | /// 18 | /// Failure. Local and remote files do not match. 19 | /// 20 | NotEqual = 2, 21 | 22 | /// 23 | /// Failure. Either the local or remote file does not exist. 24 | /// 25 | FileNotExisting = 3, 26 | 27 | /// 28 | /// Failure. Checksum verification is enabled and your server does not support any hash algorithm. 29 | /// 30 | ChecksumNotSupported = 4, 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /FluentFTP.VBExamples/GetFileSize.vb: -------------------------------------------------------------------------------- 1 | Imports System 2 | Imports System.Net 3 | Imports System.Threading 4 | Imports System.Threading.Tasks 5 | Imports FluentFTP 6 | 7 | Namespace Examples 8 | Friend Module GetFileSizeExample 9 | Sub GetFileSize() 10 | Using conn = New FtpClient("127.0.0.1", "ftptest", "ftptest") 11 | conn.Connect() 12 | Console.WriteLine("The file size is: " & conn.GetFileSize("/full/or/relative/path/to/file")) 13 | End Using 14 | End Sub 15 | 16 | Async Function GetFileSizeAsync() As Task 17 | Dim token = New CancellationToken() 18 | 19 | Using conn = New AsyncFtpClient("127.0.0.1", "ftptest", "ftptest") 20 | Await conn.Connect(token) 21 | Console.WriteLine("The file size is: " & Await conn.GetFileSize("/full/or/relative/path/to/file", -1, token)) 22 | End Using 23 | End Function 24 | End Module 25 | End Namespace 26 | -------------------------------------------------------------------------------- /FluentFTP.CSharpExamples/GetWorkingDirectory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using FluentFTP; 6 | 7 | namespace Examples { 8 | internal static class GetWorkingDirectoryExample { 9 | 10 | public static void GetWorkingDirectory() { 11 | using (var conn = new FtpClient("127.0.0.1", "ftptest", "ftptest")) { 12 | conn.Connect(); 13 | 14 | Console.WriteLine("The working directory is: " + conn.GetWorkingDirectory()); 15 | } 16 | } 17 | 18 | public static async Task GetWorkingDirectoryAsync() { 19 | var token = new CancellationToken(); 20 | using (var conn = new AsyncFtpClient("127.0.0.1", "ftptest", "ftptest")) { 21 | await conn.Connect(token); 22 | 23 | Console.WriteLine("The working directory is: " + await conn.GetWorkingDirectory(token)); 24 | } 25 | } 26 | 27 | } 28 | } -------------------------------------------------------------------------------- /FluentFTP/Client/AsyncClient/GetAbsoluteFilePath.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using FluentFTP.Helpers; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace FluentFTP { 8 | public partial class AsyncFtpClient { 9 | 10 | /// 11 | /// Concat a path and a filename 12 | /// 13 | protected async Task GetAbsoluteFilePathAsync(string path, string fileName, CancellationToken token) { 14 | string filePath = null; 15 | if (ServerHandler != null && ServerHandler.IsCustomGetAbsoluteFilePath()) { 16 | filePath = await ServerHandler.GetAbsoluteFilePathAsync(this, path, fileName, token); 17 | } 18 | 19 | if (filePath != null) { 20 | return filePath; 21 | } 22 | 23 | path = !path.EndsWith("/") ? path + "/" + fileName : path + fileName; 24 | 25 | return path; 26 | } 27 | 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /FluentFTP.CSharpExamples/DirectoryExists.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using FluentFTP; 6 | 7 | namespace Examples { 8 | internal static class DirectoryExistsExample { 9 | 10 | public static void DirectoryExists() { 11 | using (var conn = new FtpClient("127.0.0.1", "ftptest", "ftptest")) { 12 | conn.Connect(); 13 | 14 | if (conn.DirectoryExists("/full/or/relative/path")) { 15 | // do something 16 | } 17 | } 18 | } 19 | 20 | public static async Task DirectoryExistsAsync() { 21 | var token = new CancellationToken(); 22 | using (var conn = new AsyncFtpClient("127.0.0.1", "ftptest", "ftptest")) { 23 | await conn.Connect(token); 24 | 25 | if (await conn.DirectoryExists("/full/or/relative/path", token)) { 26 | // do something 27 | } 28 | } 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /FluentFTP.VBExamples/ConnectFTPS.vb: -------------------------------------------------------------------------------- 1 | Imports System 2 | Imports System.Net 3 | Imports System.Threading 4 | Imports System.Threading.Tasks 5 | Imports FluentFTP 6 | 7 | Namespace Examples 8 | Friend Module ConnectFTPSExample 9 | Sub ConnectFTPS() 10 | Using conn = New FtpClient("127.0.0.1", "ftptest", "ftptest") 11 | conn.Config.EncryptionMode = FtpEncryptionMode.Explicit 12 | conn.Config.ValidateAnyCertificate = True 13 | conn.Connect() 14 | End Using 15 | End Sub 16 | 17 | Async Function ConnectFTPSAsync() As Task 18 | Dim token = New CancellationToken() 19 | 20 | Using conn = New AsyncFtpClient("127.0.0.1", "ftptest", "ftptest") 21 | conn.Config.EncryptionMode = FtpEncryptionMode.Explicit 22 | conn.Config.ValidateAnyCertificate = True 23 | Await conn.Connect(token) 24 | End Using 25 | End Function 26 | End Module 27 | End Namespace 28 | -------------------------------------------------------------------------------- /FluentFTP.VBExamples/FileExists.vb: -------------------------------------------------------------------------------- 1 | Imports System 2 | Imports System.Net 3 | Imports System.Threading 4 | Imports System.Threading.Tasks 5 | Imports FluentFTP 6 | 7 | Namespace Examples 8 | Friend Module FileExistsExample 9 | Sub FileExists() 10 | Using conn = New FtpClient("127.0.0.1", "ftptest", "ftptest") 11 | conn.Connect() 12 | 13 | If conn.FileExists("/full/or/relative/path") Then 14 | ' do something 15 | End If 16 | 17 | End Using 18 | End Sub 19 | 20 | Async Function FileExistsAsync() As Task 21 | Dim token = New CancellationToken() 22 | 23 | Using conn = New AsyncFtpClient("127.0.0.1", "ftptest", "ftptest") 24 | Await conn.Connect(token) 25 | 26 | If Await conn.FileExists("/full/or/relative/path", token) Then 27 | ' do something 28 | End If 29 | 30 | End Using 31 | End Function 32 | End Module 33 | End Namespace 34 | -------------------------------------------------------------------------------- /FluentFTP/Exceptions/FtpListParseException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | #if NETFRAMEWORK 3 | using System.Runtime.Serialization; 4 | #endif 5 | 6 | namespace FluentFTP.Exceptions { 7 | /// 8 | /// Exception thrown by FtpListParser when parsing of FTP directory listing fails. 9 | /// 10 | #if NETFRAMEWORK 11 | [Serializable] 12 | #endif 13 | public class FtpListParseException : FtpException { 14 | /// 15 | /// Creates a new FtpListParseException. 16 | /// 17 | public FtpListParseException() 18 | : base("Cannot parse file listing!") { 19 | } 20 | 21 | #if NETFRAMEWORK 22 | /// 23 | /// Must be implemented so every Serializer can Deserialize the Exception 24 | /// 25 | protected FtpListParseException(SerializationInfo info, StreamingContext context) : base(info, context) { 26 | } 27 | 28 | #endif 29 | } 30 | } -------------------------------------------------------------------------------- /FluentFTP.CSharpExamples/GetFileSize.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using FluentFTP; 6 | 7 | namespace Examples { 8 | internal static class GetFileSizeExample { 9 | 10 | public static void GetFileSize() { 11 | using (var conn = new FtpClient("127.0.0.1", "ftptest", "ftptest")) { 12 | conn.Connect(); 13 | 14 | Console.WriteLine("The file size is: " + conn.GetFileSize("/full/or/relative/path/to/file")); 15 | } 16 | } 17 | 18 | public static async Task GetFileSizeAsync() { 19 | var token = new CancellationToken(); 20 | using (var conn = new AsyncFtpClient("127.0.0.1", "ftptest", "ftptest")) { 21 | await conn.Connect(token); 22 | 23 | Console.WriteLine("The file size is: " + await conn.GetFileSize("/full/or/relative/path/to/file", -1, token)); 24 | } 25 | } 26 | 27 | 28 | } 29 | } -------------------------------------------------------------------------------- /FluentFTP/Enums/FtpZOSListRealm.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentFTP { 4 | /// 5 | /// Flags that can control how a file listing is performed. If you are unsure what to use, set it to Auto. 6 | /// 7 | [Flags] 8 | public enum FtpZOSListRealm { 9 | /// 10 | /// Not z/OS Server 11 | /// 12 | Invalid = -1, 13 | 14 | /// 15 | /// HFS / USS 16 | /// 17 | Unix = 0, 18 | 19 | /// 20 | /// z/OS classic dataset 21 | /// 22 | Dataset = 1, 23 | 24 | /// 25 | /// Partitioned dataset member, RECFM != U 26 | /// 27 | Member = 2, 28 | 29 | /// 30 | /// Partitioned dataset member, RECFM = U 31 | /// 32 | MemberU = 3, 33 | 34 | /// 35 | /// SITE FILETYPE=JES LIST 36 | /// 37 | Jes2 = 4 38 | } 39 | } -------------------------------------------------------------------------------- /FluentFTP.CSharpExamples/ConnectFTPS.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using FluentFTP; 6 | 7 | namespace Examples { 8 | internal static class ConnectFTPSExample { 9 | 10 | public static void ConnectFTPS() { 11 | using (var conn = new FtpClient("127.0.0.1", "ftptest", "ftptest")) { 12 | conn.Config.EncryptionMode = FtpEncryptionMode.Explicit; 13 | conn.Config.ValidateAnyCertificate = true; 14 | conn.Connect(); 15 | } 16 | } 17 | 18 | public static async Task ConnectFTPSAsync() { 19 | var token = new CancellationToken(); 20 | using (var conn = new AsyncFtpClient("127.0.0.1", "ftptest", "ftptest")) { 21 | 22 | conn.Config.EncryptionMode = FtpEncryptionMode.Explicit; 23 | conn.Config.ValidateAnyCertificate = true; 24 | await conn.Connect(token); 25 | } 26 | } 27 | 28 | 29 | } 30 | } -------------------------------------------------------------------------------- /FluentFTP.VBExamples/GetModifiedTime.vb: -------------------------------------------------------------------------------- 1 | Imports System 2 | Imports System.Net 3 | Imports System.Threading 4 | Imports System.Threading.Tasks 5 | Imports FluentFTP 6 | 7 | Namespace Examples 8 | Friend Module GetModifiedTimeExample 9 | Sub GetModifiedTime() 10 | Using conn = New FtpClient("127.0.0.1", "ftptest", "ftptest") 11 | conn.Connect() 12 | Console.WriteLine("The modified type is: " & conn.GetModifiedTime("/full/or/relative/path/to/file")) 13 | End Using 14 | End Sub 15 | 16 | Async Function GetModifiedTimeAsync() As Task 17 | Dim token = New CancellationToken() 18 | 19 | Using conn = New AsyncFtpClient("127.0.0.1", "ftptest", "ftptest") 20 | Await conn.Connect(token) 21 | Console.WriteLine("The modified type is: " & Await conn.GetModifiedTime("/full/or/relative/path/to/file", token)) 22 | End Using 23 | End Function 24 | End Module 25 | End Namespace 26 | -------------------------------------------------------------------------------- /FluentFTP/Client/AsyncClient/GetAbsoluteDir.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using FluentFTP.Helpers; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace FluentFTP { 8 | public partial class AsyncFtpClient { 9 | 10 | /// 11 | /// Ensure a relative dir is absolute by prepending the working dir 12 | /// 13 | protected async Task GetAbsoluteDirAsync(string path, CancellationToken token) { 14 | string dirPath = null; 15 | if (ServerHandler != null && ServerHandler.IsCustomGetAbsoluteDir()) { 16 | dirPath = await ServerHandler.GetAbsoluteDirAsync(this, path, token); 17 | } 18 | 19 | if (dirPath != null) { 20 | return dirPath; 21 | } 22 | 23 | path = await GetAbsolutePathAsync(path, token); 24 | 25 | path = !path.EndsWith("/") ? path + "/" : path; 26 | 27 | return path; 28 | } 29 | 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /FluentFTP.VBExamples/DirectoryExists.vb: -------------------------------------------------------------------------------- 1 | Imports System 2 | Imports System.Net 3 | Imports System.Threading 4 | Imports System.Threading.Tasks 5 | Imports FluentFTP 6 | 7 | Namespace Examples 8 | Friend Module DirectoryExistsExample 9 | Sub DirectoryExists() 10 | Using conn = New FtpClient("127.0.0.1", "ftptest", "ftptest") 11 | conn.Connect() 12 | 13 | If conn.DirectoryExists("/full/or/relative/path") Then 14 | ' do something 15 | End If 16 | 17 | End Using 18 | End Sub 19 | 20 | Async Function DirectoryExistsAsync() As Task 21 | Dim token = New CancellationToken() 22 | 23 | Using conn = New AsyncFtpClient("127.0.0.1", "ftptest", "ftptest") 24 | Await conn.Connect(token) 25 | 26 | If Await conn.DirectoryExists("/full/or/relative/path") Then 27 | ' do something 28 | End If 29 | 30 | End Using 31 | End Function 32 | End Module 33 | End Namespace 34 | -------------------------------------------------------------------------------- /FluentFTP/Client/AsyncClient/GetReply.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using System.Diagnostics; 5 | using FluentFTP.Client.Modules; 6 | 7 | namespace FluentFTP { 8 | public partial class AsyncFtpClient { 9 | 10 | /// 11 | /// Retrieves a reply from the server. 12 | /// Support "normal" mode waiting for a command reply, subject to timeout exception 13 | /// and "exhaustNoop" mode, which waits for 10 seconds to collect out of band NOOP responses 14 | /// 15 | /// The token that can be used to cancel the entire process. 16 | /// FtpReply representing the response from the server 17 | public async Task GetReply(CancellationToken token = default(CancellationToken)) { 18 | return await ((IInternalFtpClient)this).GetReplyInternal(token, null, false, 0); 19 | } 20 | } 21 | } -------------------------------------------------------------------------------- /FluentFTP/Enums/FtpDate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentFTP { 4 | /// 5 | /// Controls how timestamps returned by the server are converted. 6 | /// 7 | public enum FtpDate { 8 | 9 | /// 10 | /// Returns the server timestamps in Server Time. No timezone conversion is performed. 11 | /// 12 | ServerTime = 0, 13 | 14 | /// 15 | /// Returns the server timestamps in Local Time. 16 | /// Ensure that the `ServerTimeZone` property is set to the server's timezone. 17 | /// Ensure that the `ClientTimeZone` property is set to the client's timezone. 18 | /// 19 | LocalTime = 1, 20 | 21 | /// 22 | /// Returns the server timestamps in UTC (Coordinated Universal Time). 23 | /// Ensure that the `ServerTimeZone` property is correctly set to the server's timezone. 24 | /// 25 | UTC = 2, 26 | 27 | } 28 | } -------------------------------------------------------------------------------- /FluentFTP/Client/SyncClient/DeleteFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentFTP.Helpers; 3 | using System.Threading; 4 | using FluentFTP.Client.Modules; 5 | using System.Threading.Tasks; 6 | using FluentFTP.Exceptions; 7 | 8 | namespace FluentFTP { 9 | public partial class FtpClient { 10 | 11 | /// 12 | /// Deletes a file on the server 13 | /// 14 | /// The full or relative path to the file 15 | public void DeleteFile(string path) { 16 | FtpReply reply; 17 | 18 | // verify args 19 | if (path.IsBlank()) { 20 | throw new ArgumentException("Required parameter is null or blank.", nameof(path)); 21 | } 22 | 23 | path = path.GetFtpPath(); 24 | 25 | LogFunction(nameof(DeleteFile), new object[] { path }); 26 | 27 | if (!(reply = Execute("DELE " + path)).Success) { 28 | throw new FtpCommandException(reply); 29 | } 30 | } 31 | 32 | } 33 | } -------------------------------------------------------------------------------- /FluentFTP.CSharpExamples/DeleteDirectory.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using FluentFTP; 5 | 6 | namespace Examples { 7 | internal static class DeleteDirectoryExample { 8 | 9 | public static void DeleteDirectory() { 10 | using (var conn = new FtpClient("127.0.0.1", "ftptest", "ftptest")) { 11 | conn.Connect(); 12 | 13 | // Remove the directory and all files and subdirectories inside it 14 | conn.DeleteDirectory("/path/to/directory"); 15 | } 16 | } 17 | 18 | public static async Task DeleteDirectoryAsync() { 19 | var token = new CancellationToken(); 20 | using (var conn = new AsyncFtpClient("127.0.0.1", "ftptest", "ftptest")) { 21 | await conn.Connect(token); 22 | 23 | // Remove the directory and all files and subdirectories inside it 24 | await conn.DeleteDirectory("/path/to/directory", token); 25 | } 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /FluentFTP.CSharpExamples/GetModifiedTime.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using FluentFTP; 6 | 7 | namespace Examples { 8 | internal static class GetModifiedTimeExample { 9 | 10 | public static void GetModifiedTime() { 11 | using (var conn = new FtpClient("127.0.0.1", "ftptest", "ftptest")) { 12 | conn.Connect(); 13 | 14 | Console.WriteLine("The modified type is: " + 15 | conn.GetModifiedTime("/full/or/relative/path/to/file")); 16 | } 17 | } 18 | 19 | public static async Task GetModifiedTimeAsync() { 20 | var token = new CancellationToken(); 21 | using (var conn = new AsyncFtpClient("127.0.0.1", "ftptest", "ftptest")) { 22 | await conn.Connect(token); 23 | 24 | Console.WriteLine("The modified type is: " + 25 | await conn.GetModifiedTime("/full/or/relative/path/to/file", token)); 26 | } 27 | } 28 | 29 | } 30 | } -------------------------------------------------------------------------------- /FluentFTP.VBExamples/DeleteDirectory.vb: -------------------------------------------------------------------------------- 1 | Imports System.Net 2 | Imports System.Threading 3 | Imports System.Threading.Tasks 4 | Imports FluentFTP 5 | 6 | Namespace Examples 7 | Friend Module DeleteDirectoryExample 8 | Sub DeleteDirectory() 9 | Using conn = New FtpClient("127.0.0.1", "ftptest", "ftptest") 10 | conn.Connect() 11 | 12 | ' Remove the directory And all files And subdirectories inside it 13 | conn.DeleteDirectory("/path/to/directory") 14 | 15 | End Using 16 | End Sub 17 | 18 | Async Function DeleteDirectoryAsync() As Task 19 | Dim token = New CancellationToken() 20 | 21 | Using conn = New AsyncFtpClient("127.0.0.1", "ftptest", "ftptest") 22 | Await conn.Connect(token) 23 | 24 | ' Remove the directory And all files And subdirectories inside it 25 | Await conn.DeleteDirectory("/path/to/directory", token) 26 | 27 | End Using 28 | End Function 29 | End Module 30 | End Namespace 31 | -------------------------------------------------------------------------------- /FluentFTP/Client/BaseClient/Connect.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace FluentFTP.Client.BaseClient { 5 | 6 | public partial class BaseFtpClient { 7 | 8 | /// 9 | /// Connect to the server 10 | /// 11 | /// true indicates that we want a reconnect to take place. 12 | void IInternalFtpClient.ConnectInternal(bool reConnect) { 13 | ((IFtpClient)this).Connect(reConnect); 14 | } 15 | 16 | /// 17 | /// Connect to the server 18 | /// 19 | /// true indicates that we want a reconnect to take place. 20 | /// The token that can be used to cancel the entire process 21 | async Task IInternalFtpClient.ConnectInternal(bool reConnect, CancellationToken token) { 22 | await ((IAsyncFtpClient)this).Connect(reConnect, token); 23 | } 24 | 25 | } 26 | } -------------------------------------------------------------------------------- /FluentFTP/Enums/FtpOperatingSystem.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentFTP { 4 | /// 5 | /// Defines the operating system of the FTP server. 6 | /// 7 | public enum FtpOperatingSystem { 8 | /// 9 | /// Unknown operating system 10 | /// 11 | Unknown, 12 | 13 | /// 14 | /// Definitely Windows or Windows Server 15 | /// 16 | Windows, 17 | 18 | /// 19 | /// Definitely Unix or AIX-based server 20 | /// 21 | Unix, 22 | 23 | /// 24 | /// Definitely VMS or OpenVMS server 25 | /// 26 | VMS, 27 | 28 | /// 29 | /// Definitely IBM OS/400 server 30 | /// 31 | IBMOS400, 32 | 33 | /// 34 | /// Definitely IBM z/OS server 35 | /// 36 | IBMzOS, 37 | 38 | /// 39 | /// Definitely SUN OS/Solaris server 40 | /// 41 | SunOS, 42 | } 43 | } -------------------------------------------------------------------------------- /FluentFTP.Xunit/Attributes/Internal/SkippableFactDiscoverer.cs: -------------------------------------------------------------------------------- 1 | using FluentFTP.Xunit.Internal; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Xunit.Abstractions; 8 | using Xunit.Sdk; 9 | 10 | namespace FluentFTP.Xunit.Attributes.Internal { 11 | internal class SkippableFactDiscoverer : IXunitTestCaseDiscoverer { 12 | readonly IMessageSink diagnosticMessageSink; 13 | 14 | public SkippableFactDiscoverer(IMessageSink diagnosticMessageSink) { 15 | this.diagnosticMessageSink = diagnosticMessageSink; 16 | } 17 | 18 | public IEnumerable Discover(ITestFrameworkDiscoveryOptions discoveryOptions, ITestMethod testMethod, IAttributeInfo factAttribute) { 19 | yield return new SkippableFactTestCase(diagnosticMessageSink, discoveryOptions.MethodDisplayOrDefault(), discoveryOptions.MethodDisplayOptionsOrDefault(), testMethod); 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /FluentFTP/Servers/Handlers/DLinkServer.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | namespace FluentFTP.Servers.Handlers { 4 | 5 | /// 6 | /// Server-specific handling for D-Link FTP servers 7 | /// 8 | internal class DLinkServer : FtpBaseServer { 9 | 10 | /// 11 | /// Return the FtpServer enum value corresponding to your server, or Unknown if its a custom implementation. 12 | /// 13 | public override FtpServer ToEnum() { 14 | return FtpServer.DLink; 15 | } 16 | 17 | /// 18 | /// Return true if your server is detected by the given FTP server welcome message. 19 | /// 20 | public override bool DetectByWelcome(string message) { 21 | 22 | // Detect D-Link server 23 | // Welcome message: "220 D-Link FTP version 1.0 ready" 24 | if (message.Contains("D-Link FTP")) { 25 | return true; 26 | } 27 | 28 | return false; 29 | } 30 | 31 | } 32 | } -------------------------------------------------------------------------------- /FluentFTP/Client/SyncClient/SetWorkingDirectory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using FluentFTP.Helpers; 4 | using FluentFTP.Client.Modules; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using FluentFTP.Exceptions; 8 | 9 | namespace FluentFTP { 10 | public partial class FtpClient { 11 | 12 | /// 13 | /// Sets the work directory on the server 14 | /// 15 | /// The path of the directory to change to 16 | public void SetWorkingDirectory(string path) { 17 | 18 | path = path.GetFtpPath(); 19 | 20 | LogFunction(nameof(SetWorkingDirectory), new object[] { path }); 21 | 22 | FtpReply reply; 23 | 24 | // exit if invalid path 25 | if (path is "." or "./") { 26 | return; 27 | } 28 | 29 | // modify working dir 30 | if (!(reply = Execute("CWD " + path)).Success) { 31 | throw new FtpCommandException(reply); 32 | } 33 | 34 | } 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /FluentFTP/Servers/Handlers/TPLinkServer.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace FluentFTP.Servers.Handlers { 5 | 6 | /// 7 | /// Server-specific handling for TP-LINK FTP servers 8 | /// 9 | internal class TPLinkServer : FtpBaseServer { 10 | 11 | /// 12 | /// Return the FtpServer enum value corresponding to your server, or Unknown if its a custom implementation. 13 | /// 14 | public override FtpServer ToEnum() { 15 | return FtpServer.TPLink; 16 | } 17 | 18 | /// 19 | /// Return true if your server is detected by the given FTP server welcome message. 20 | /// 21 | public override bool DetectByWelcome(string message) { 22 | 23 | // Detect TP-LINK server 24 | // Welcome message: "TP-LINK FTP version 1.0 ready" 25 | if (message.Contains("TP-LINK FTP")) { 26 | return true; 27 | } 28 | 29 | return false; 30 | } 31 | 32 | } 33 | } -------------------------------------------------------------------------------- /FluentFTP/Enums/FtpVerifyMethod.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentFTP { 4 | /// 5 | /// Defines which verification types should be performed when 6 | /// uploading/downloading files using the high-level APIs. 7 | /// Multiple verification types can be combined. 8 | /// 9 | [Flags] 10 | public enum FtpVerifyMethod { 11 | /// 12 | /// Compares the file size. 13 | /// Both file sizes should exactly match for the file to be considered equal. 14 | /// 15 | Size = 1, 16 | 17 | /// 18 | /// Compares the date modified of the file. 19 | /// Both dates should exactly match for the file to be considered equal. 20 | /// 21 | Date = 2, 22 | 23 | /// 24 | /// Compares the checksum or hash of the file using the first supported hash algorithm. 25 | /// Both checksums should exactly match for the file to be considered equal. 26 | /// 27 | Checksum = 4, 28 | } 29 | } -------------------------------------------------------------------------------- /FluentFTP/Servers/Handlers/BFtpdServer.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace FluentFTP.Servers.Handlers { 5 | 6 | /// 7 | /// Server-specific handling for BFTPd FTP servers 8 | /// 9 | internal class BFtpdServer : FtpBaseServer { 10 | 11 | /// 12 | /// Return the FtpServer enum value corresponding to your server, or Unknown if its a custom implementation. 13 | /// 14 | public override FtpServer ToEnum() { 15 | return FtpServer.BFTPd; 16 | } 17 | 18 | /// 19 | /// Return true if your server is detected by the given FTP server welcome message. 20 | /// 21 | public override bool DetectByWelcome(string message) { 22 | 23 | // Detect BFTPd server 24 | // Welcome message: "220 bftpd 2.2.1 at 192.168.1.1 ready" 25 | if (message.Contains("bftpd ")) { 26 | return true; 27 | } 28 | 29 | return false; 30 | } 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /FluentFTP/Servers/Handlers/WSFTPServer.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace FluentFTP.Servers.Handlers { 5 | 6 | /// 7 | /// Server-specific handling for WS_FTP servers 8 | /// 9 | internal class WSFTPServer : FtpBaseServer { 10 | 11 | /// 12 | /// Return the FtpServer enum value corresponding to your server, or Unknown if its a custom implementation. 13 | /// 14 | public override FtpServer ToEnum() { 15 | return FtpServer.WSFTP; 16 | } 17 | 18 | /// 19 | /// Return true if your server is detected by the given FTP server welcome message. 20 | /// 21 | public override bool DetectByWelcome(string message) { 22 | 23 | // Detect FTP2S3 server 24 | // Welcome message: "220 ***.com X2 WS_FTP Server 8.5.0(24135676)" 25 | if (message.Contains("WS_FTP Server")) { 26 | return true; 27 | } 28 | return false; 29 | } 30 | 31 | } 32 | } -------------------------------------------------------------------------------- /FluentFTP/Servers/Handlers/PyFtpdLibServer.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace FluentFTP.Servers.Handlers { 5 | 6 | /// 7 | /// Server-specific handling for PyFtpdLib FTP servers 8 | /// 9 | internal class PyFtpdLibServer : FtpBaseServer { 10 | 11 | /// 12 | /// Return the FtpServer enum value corresponding to your server, or Unknown if its a custom implementation. 13 | /// 14 | public override FtpServer ToEnum() { 15 | return FtpServer.PyFtpdLib; 16 | } 17 | 18 | /// 19 | /// Return true if your server is detected by the given FTP server welcome message. 20 | /// 21 | public override bool DetectByWelcome(string message) { 22 | 23 | // Detect PyFtpdLib server 24 | // Welcome message: "220 pyftpdlib 1.5.6 ready" 25 | if (message.Contains("pyftpdlib ")) { 26 | return true; 27 | } 28 | 29 | return false; 30 | } 31 | 32 | } 33 | } -------------------------------------------------------------------------------- /FluentFTP/Servers/Handlers/CrushFtpServer.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace FluentFTP.Servers.Handlers { 5 | 6 | /// 7 | /// Server-specific handling for CrushFTP servers 8 | /// 9 | internal class CrushFtpServer : FtpBaseServer { 10 | 11 | /// 12 | /// Return the FtpServer enum value corresponding to your server, or Unknown if its a custom implementation. 13 | /// 14 | public override FtpServer ToEnum() { 15 | return FtpServer.CrushFTP; 16 | } 17 | 18 | /// 19 | /// Return true if your server is detected by the given FTP server welcome message. 20 | /// 21 | public override bool DetectByWelcome(string message) { 22 | 23 | // Detect CrushFTP server 24 | // Welcome message: "220 CrushFTP Server Ready!" 25 | if (message.Contains("CrushFTP Server")) { 26 | return true; 27 | } 28 | 29 | return false; 30 | } 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /FluentFTP/Servers/Handlers/IDALFtpServer.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace FluentFTP.Servers.Handlers { 5 | 6 | /// 7 | /// Server-specific handling for ABB IDAL FTP servers 8 | /// 9 | internal class IDALFtpServer : FtpBaseServer { 10 | 11 | /// 12 | /// Return the FtpServer enum value corresponding to your server, or Unknown if its a custom implementation. 13 | /// 14 | public override FtpServer ToEnum() { 15 | return FtpServer.IDALFTP; 16 | } 17 | 18 | /// 19 | /// Return true if your server is detected by the given FTP server welcome message. 20 | /// 21 | public override bool DetectByWelcome(string message) { 22 | 23 | // Detect IDAL server 24 | // Welcome message: "220 Welcome to IDAL FTP server. READY." 25 | if (message.Contains("IDAL FTP server")) { 26 | return true; 27 | } 28 | 29 | return false; 30 | } 31 | 32 | } 33 | } -------------------------------------------------------------------------------- /FluentFTP/Client/SyncClient/Disconnect.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentFTP { 4 | public partial class FtpClient { 5 | 6 | /// 7 | /// Disconnects from the server 8 | /// 9 | public void Disconnect() { 10 | LogFunction(nameof(Disconnect), null); 11 | 12 | if (IsConnected) { 13 | try { 14 | if (Config.DisconnectWithQuit) { 15 | Execute("QUIT"); 16 | } 17 | } 18 | catch (Exception ex) { 19 | LogWithPrefix(FtpTraceLevel.Verbose, "FtpClient.Disconnect().Execute(\"QUIT\"): " + ex.Message); 20 | } 21 | finally { 22 | // When debugging, the stream might have already been taken down 23 | // from the remote side, thus causing an exception here, so check for null 24 | if (m_stream != null) { 25 | m_stream.Close(); 26 | } 27 | } 28 | } 29 | else { 30 | LogWithPrefix(FtpTraceLevel.Verbose, "Connection already closed, nothing to do."); 31 | } 32 | } 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /FluentFTP/Servers/Handlers/TitanFtpServer.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace FluentFTP.Servers.Handlers { 5 | 6 | /// 7 | /// Server-specific handling for Titan FTP servers 8 | /// 9 | internal class TitanFtpServer : FtpBaseServer { 10 | 11 | /// 12 | /// Return the FtpServer enum value corresponding to your server, or Unknown if its a custom implementation. 13 | /// 14 | public override FtpServer ToEnum() { 15 | return FtpServer.TitanFTP; 16 | } 17 | 18 | /// 19 | /// Return true if your server is detected by the given FTP server welcome message. 20 | /// 21 | public override bool DetectByWelcome(string message) { 22 | 23 | // Detect Pure-FTPd server 24 | // Welcome message: "220 Titan FTP Server 10.01.1740 Ready" 25 | if (message.Contains("Titan FTP")) { 26 | return true; 27 | } 28 | 29 | return false; 30 | } 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /FluentFTP/Servers/Handlers/XLightServer.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace FluentFTP.Servers.Handlers { 5 | 6 | /// 7 | /// Server-specific handling for XLight FTP servers 8 | /// 9 | internal class XLightServer : FtpBaseServer { 10 | 11 | /// 12 | /// Return the FtpServer enum value corresponding to your server, or Unknown if its a custom implementation. 13 | /// 14 | public override FtpServer ToEnum() { 15 | return FtpServer.XLight; 16 | } 17 | 18 | /// 19 | /// Return true if your server is detected by the given FTP server welcome message. 20 | /// 21 | public override bool DetectByWelcome(string message) { 22 | 23 | // Detect XLight server 24 | // Welcome message: "220 Xlight FTP Server 3.9 ready" 25 | if (message.Contains("Xlight FTP Server")) { 26 | return true; 27 | } 28 | 29 | return false; 30 | } 31 | 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /FluentFTP/Client/AsyncClient/GetChmod.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using FluentFTP.Helpers; 5 | 6 | namespace FluentFTP { 7 | public partial class AsyncFtpClient { 8 | 9 | /// 10 | /// Retrieve the permissions of the given file/folder as an integer in the CHMOD format. 11 | /// Throws FtpCommandException if there is an issue. 12 | /// Returns 0 if the server did not specify a permission value. 13 | /// Use `GetFilePermissions` if you required the permissions in the FtpPermission format. 14 | /// 15 | /// The full or relative path to the item 16 | /// The token that can be used to cancel the entire process 17 | public async Task GetChmod(string path, CancellationToken token = default(CancellationToken)) { 18 | FtpListItem item = await GetFilePermissions(path, token); 19 | return item != null ? item.Chmod : 0; 20 | } 21 | 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /FluentFTP/Client/BaseClient/ValidateAutoDetect.cs: -------------------------------------------------------------------------------- 1 | using FluentFTP.Exceptions; 2 | using System; 3 | 4 | namespace FluentFTP.Client.BaseClient { 5 | public partial class BaseFtpClient { 6 | 7 | /// 8 | /// Validate the client before the auto detect process 9 | /// 10 | /// 11 | /// 12 | protected void ValidateAutoDetect() { 13 | if (IsDisposed) { 14 | throw new ObjectDisposedException("This FtpClient object has been disposed. It is no longer accessible."); 15 | } 16 | 17 | if (Host == null) { 18 | throw new FtpException("No host has been specified. Please set the 'Host' property before trying to auto connect."); 19 | } 20 | 21 | if (Credentials == null) { 22 | throw new FtpException("No username and password has been specified. Please set the 'Credentials' property before trying to auto connect."); 23 | } 24 | } 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /FluentFTP/Client/SyncClient/IsRoot.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using FluentFTP.Helpers; 4 | using FluentFTP.Client.Modules; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace FluentFTP { 9 | public partial class FtpClient { 10 | 11 | /// 12 | /// Is the current working directory the root? 13 | /// 14 | /// true if root. 15 | public bool IsRoot() { 16 | 17 | // this case occurs immediately after connection and after the working dir has changed 18 | if (Status.LastWorkingDir == null) { 19 | ReadCurrentWorkingDirectory(); 20 | } 21 | 22 | if (Status.LastWorkingDir.IsFtpRootDirectory()) { 23 | return true; 24 | } 25 | 26 | // execute server-specific check if the current working dir is a root directory 27 | if (ServerHandler != null && ServerHandler.IsRoot(this, Status.LastWorkingDir)) { 28 | return true; 29 | } 30 | 31 | return false; 32 | } 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /FluentFTP/Servers/Handlers/CerberusServer.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace FluentFTP.Servers.Handlers { 5 | 6 | /// 7 | /// Server-specific handling for Cerberus FTP servers 8 | /// 9 | internal class CerberusServer : FtpBaseServer { 10 | 11 | /// 12 | /// Return the FtpServer enum value corresponding to your server, or Unknown if its a custom implementation. 13 | /// 14 | public override FtpServer ToEnum() { 15 | return FtpServer.Cerberus; 16 | } 17 | 18 | /// 19 | /// Return true if your server is detected by the given FTP server welcome message. 20 | /// 21 | public override bool DetectByWelcome(string message) { 22 | 23 | // Detect Cerberus server 24 | // Welcome message: "220-Cerberus FTP Server Personal Edition" 25 | if (message.Contains("Cerberus FTP")) { 26 | return true; 27 | } 28 | 29 | return false; 30 | } 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /FluentFTP/Servers/Handlers/HomegateFtpServer.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace FluentFTP.Servers.Handlers { 5 | 6 | /// 7 | /// Server-specific handling for HomegateFTP servers 8 | /// 9 | internal class HomegateFtpServer : FtpBaseServer { 10 | 11 | /// 12 | /// Return the FtpServer enum value corresponding to your server, or Unknown if its a custom implementation. 13 | /// 14 | public override FtpServer ToEnum() { 15 | return FtpServer.HomegateFTP; 16 | } 17 | 18 | /// 19 | /// Return true if your server is detected by the given FTP server welcome message. 20 | /// 21 | public override bool DetectByWelcome(string message) { 22 | 23 | // Detect Homegate FTP server 24 | // Welcome message: "220 Homegate FTP Server ready" 25 | if (message.Contains("Homegate FTP Server")) { 26 | return true; 27 | } 28 | 29 | return false; 30 | } 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /FluentFTP.CSharpExamples/Log_Serilog.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using FluentFTP; 4 | using FluentFTP.Logging; 5 | using Serilog; 6 | using Serilog.Extensions.Logging; 7 | 8 | namespace Examples { 9 | internal static class SerilogExample { 10 | 11 | public static void Configure() { 12 | 13 | using (var conn = new FtpClient()) { 14 | conn.Host = "localhost"; 15 | conn.Credentials = new NetworkCredential("ftptest", "ftptest"); 16 | 17 | 18 | // create Serilog logger 19 | var serilogLogger = new LoggerConfiguration() 20 | .MinimumLevel.Debug() 21 | .WriteTo.File("logs/FluentFTPLogs.txt", rollingInterval: RollingInterval.Day) 22 | .CreateLogger(); 23 | 24 | // wrap with MELA ILogger 25 | var microsoftLogger = new SerilogLoggerFactory(serilogLogger) 26 | .CreateLogger("FTP"); 27 | 28 | // wrap with FtpLogAdapter 29 | conn.Logger = new FtpLogAdapter(microsoftLogger); 30 | 31 | 32 | conn.Connect(); 33 | } 34 | } 35 | 36 | } 37 | } -------------------------------------------------------------------------------- /FluentFTP/Servers/Handlers/Ftp2S3GatewayServer.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace FluentFTP.Servers.Handlers { 5 | 6 | /// 7 | /// Server-specific handling for FTP2S3Gateway FTP servers 8 | /// 9 | internal class Ftp2S3GatewayServer : FtpBaseServer { 10 | 11 | /// 12 | /// Return the FtpServer enum value corresponding to your server, or Unknown if its a custom implementation. 13 | /// 14 | public override FtpServer ToEnum() { 15 | return FtpServer.FTP2S3Gateway; 16 | } 17 | 18 | /// 19 | /// Return true if your server is detected by the given FTP server welcome message. 20 | /// 21 | public override bool DetectByWelcome(string message) { 22 | 23 | // Detect FTP2S3 server 24 | // Welcome message: "220 Aruba FTP2S3 gateway 1.0.1 ready" 25 | if (message.Contains("FTP2S3 gateway")) { 26 | return true; 27 | } 28 | return false; 29 | } 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /FluentFTP.CSharpExamples/CSharpExamples.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | Library 6 | latest 7 | 8 | 9 | AnyCPU;x64 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /FluentFTP/Servers/Handlers/GlobalScapeEftServer.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace FluentFTP.Servers.Handlers { 5 | 6 | /// 7 | /// Server-specific handling for GlobalScapeEFT FTP servers 8 | /// 9 | internal class GlobalScapeEftServer : FtpBaseServer { 10 | 11 | /// 12 | /// Return the FtpServer enum value corresponding to your server, or Unknown if its a custom implementation. 13 | /// 14 | public override FtpServer ToEnum() { 15 | return FtpServer.GlobalScapeEFT; 16 | } 17 | 18 | /// 19 | /// Return true if your server is detected by the given FTP server welcome message. 20 | /// 21 | public override bool DetectByWelcome(string message) { 22 | 23 | // Detect GlobalScape EFT server 24 | // Welcome message: "EFT Server Enterprise 7.4.5.6" 25 | if (message.Contains("EFT Server")) { 26 | return true; 27 | } 28 | 29 | return false; 30 | } 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /FluentFTP.Logging/FtpLogAdapter.cs: -------------------------------------------------------------------------------- 1 | using FluentFTP; 2 | using Microsoft.Extensions.Logging; 3 | 4 | namespace FluentFTP.Logging { 5 | /// 6 | /// Logging adapter to help FluentFTP integrate with MELA-compatible Loggers (NLog, Serilog, Log4Net, PLogger, etc). 7 | /// Read the Logging page: https://github.com/robinrodricks/FluentFTP/wiki/Logging 8 | /// 9 | public sealed class FtpLogAdapter : IFtpLogger { 10 | private readonly ILogger adaptee; 11 | 12 | public FtpLogAdapter(ILogger adaptee) => 13 | this.adaptee = adaptee; 14 | 15 | public void Log(FtpLogEntry entry) => 16 | adaptee.Log(ToLevel(entry.Severity), 0, entry.Message, entry.Exception, (s, _) => s); 17 | 18 | private static LogLevel ToLevel(FtpTraceLevel s) => s switch { 19 | FtpTraceLevel.Verbose => LogLevel.Debug, 20 | FtpTraceLevel.Info => LogLevel.Information, 21 | FtpTraceLevel.Warn => LogLevel.Warning, 22 | FtpTraceLevel.Error => LogLevel.Error, 23 | _ => LogLevel.Information 24 | }; 25 | } 26 | } -------------------------------------------------------------------------------- /FluentFTP.CSharpExamples/ExecuteFTPCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using FluentFTP; 6 | using FluentFTP.Exceptions; 7 | 8 | namespace Examples { 9 | internal static class ExecuteFTPCommandExample { 10 | 11 | public static void Execute() { 12 | using (var conn = new FtpClient("127.0.0.1", "ftptest", "ftptest")) { 13 | conn.Connect(); 14 | 15 | FtpReply reply; 16 | if (!(reply = conn.Execute("SITE CHMOD 640 FOO.TXT")).Success) { 17 | throw new FtpCommandException(reply); 18 | } 19 | } 20 | } 21 | 22 | public static async Task ExecuteAsync() { 23 | var token = new CancellationToken(); 24 | using (var conn = new AsyncFtpClient("127.0.0.1", "ftptest", "ftptest")) { 25 | await conn.Connect(token); 26 | 27 | FtpReply reply; 28 | if (!(reply = await conn.Execute("SITE CHMOD 640 FOO.TXT", token)).Success) { 29 | throw new FtpCommandException(reply); 30 | } 31 | } 32 | } 33 | 34 | 35 | } 36 | } -------------------------------------------------------------------------------- /FluentFTP/Helpers/TimeSpans.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace FluentFTP.Helpers { 8 | /// 9 | /// Extension methods related to FTP time span values 10 | /// 11 | public static class TimeSpans { 12 | 13 | public static string ToShortString(this TimeSpan span, string format = "0.###", string zeroString = "<1ms") { 14 | if (span.TotalDays > 1) { 15 | return span.TotalDays.ToString(format) + "d"; 16 | } 17 | if (span.TotalHours > 1) { 18 | return span.TotalHours.ToString(format) + "h"; 19 | } 20 | if (span.TotalMinutes > 1) { 21 | return span.TotalMinutes.ToString(format) + "m"; 22 | } 23 | if (span.TotalSeconds > 1) { 24 | return span.TotalSeconds.ToString(format) + "s"; 25 | } 26 | if (span.TotalMilliseconds > 1) { 27 | return ((int)span.TotalMilliseconds).ToString() + "ms"; 28 | } 29 | return zeroString; 30 | } 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /FluentFTP/Servers/Handlers/RumpusServer.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace FluentFTP.Servers.Handlers { 5 | 6 | /// 7 | /// Server-specific handling for Rumpus FTP servers 8 | /// 9 | internal class RumpusServer : FtpBaseServer { 10 | 11 | /// 12 | /// Return the FtpServer enum value corresponding to your server, or Unknown if its a custom implementation. 13 | /// 14 | public override FtpServer ToEnum() { 15 | return FtpServer.Rumpus; 16 | } 17 | 18 | /// 19 | /// Return true if your server is detected by the given FTP server welcome message. 20 | /// 21 | public override bool DetectByWelcome(string message) { 22 | 23 | // Detect Rumpus server 24 | // Welcome message: "Response: 220-Welcome To Rumpus!" 25 | // "Response: 220 Service ready for new user" 26 | if (message.Contains("Welcome To Rumpus")) { 27 | return true; 28 | } 29 | 30 | return false; 31 | } 32 | 33 | } 34 | } -------------------------------------------------------------------------------- /FluentFTP/Helpers/Logging/LoggerExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentFTP.Helpers.Logging; 3 | 4 | namespace FluentFTP.Helpers.Logging { 5 | internal static class LoggerExtensions { 6 | 7 | /// 8 | /// Log a message to the given IFluentLogger class. 9 | /// 10 | public static void Log(this IFtpLogger logger, FtpTraceLevel eventType, string message, Exception ex = null) { 11 | logger.Log(new FtpLogEntry(eventType, message, ex)); 12 | } 13 | 14 | /// 15 | /// Get the log prefix for the given trace level type. 16 | /// 17 | public static string GetLogPrefix(this FtpTraceLevel eventType) { 18 | switch (eventType) { 19 | case FtpTraceLevel.Verbose: 20 | return "Status: "; 21 | 22 | case FtpTraceLevel.Info: 23 | return "Status: "; 24 | 25 | case FtpTraceLevel.Warn: 26 | return "Warning: "; 27 | 28 | case FtpTraceLevel.Error: 29 | return "Error: "; 30 | } 31 | 32 | return "Status: "; 33 | } 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /FluentFTP/Client/SyncClient/Noop.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Net.Sockets; 4 | using System.Text.RegularExpressions; 5 | using System.Linq; 6 | using System.Net; 7 | using FluentFTP.Helpers; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | 11 | namespace FluentFTP { 12 | public partial class FtpClient { 13 | 14 | /// 15 | /// Sends the NOOP command according to (effectively a no-op if 0). 16 | /// Please call as needed to read the "OK" command sent by the server and prevent stale data on the socket. 17 | /// Note that response is not guaranteed by all FTP servers when sent during file transfers. 18 | /// Send the command regardless of NoopInterval 19 | /// 20 | /// true if NOOP command was sent 21 | public bool Noop(bool ignoreNoopInterval = false) { 22 | return ((IInternalFtpClient)this).NoopInternal(ignoreNoopInterval); 23 | } 24 | 25 | } 26 | } -------------------------------------------------------------------------------- /FluentFTP/Servers/Handlers/FritzBoxServer.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace FluentFTP.Servers.Handlers { 5 | 6 | /// 7 | /// Server-specific handling for FritzBox FTP servers 8 | /// 9 | internal class FritzBoxServer : FtpBaseServer { 10 | 11 | /// 12 | /// Return the FtpServer enum value corresponding to your server, or Unknown if its a custom implementation. 13 | /// 14 | public override FtpServer ToEnum() { 15 | return FtpServer.FritzBox; 16 | } 17 | 18 | /// 19 | /// Return true if your server is detected by the given FTP server welcome message. 20 | /// 21 | public override bool DetectByWelcome(string message) { 22 | 23 | // Detect FTP2S3 server 24 | // Welcome message: "220 FRITZ!Box7490 FTP server ready" 25 | // Welcome message: "220 FRITZ!BoxFonWLAN7390 FTP server ready" 26 | if (message.Contains("FRITZ!Box")) { 27 | return true; 28 | } 29 | return false; 30 | } 31 | 32 | } 33 | } -------------------------------------------------------------------------------- /FluentFTP/Exceptions/FtpMissingSocketException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | #if NETFRAMEWORK 3 | using System.Runtime.Serialization; 4 | #endif 5 | 6 | namespace FluentFTP.Exceptions { 7 | /// 8 | /// Exception is thrown by FtpSocketStream when there is no FTP server socket to connect to. 9 | /// 10 | #if NETFRAMEWORK 11 | [Serializable] 12 | #endif 13 | public class FtpMissingSocketException : FtpException { 14 | /// 15 | /// Creates a new FtpMissingSocketException. 16 | /// 17 | /// The original exception. 18 | public FtpMissingSocketException(Exception innerException) 19 | : base("Socket is missing", innerException) { 20 | } 21 | 22 | #if NETFRAMEWORK 23 | /// 24 | /// Must be implemented so every Serializer can Deserialize the Exception 25 | /// 26 | protected FtpMissingSocketException(SerializationInfo info, StreamingContext context) : base(info, context) { 27 | } 28 | 29 | #endif 30 | } 31 | } -------------------------------------------------------------------------------- /FluentFTP/Servers/Handlers/GlFtpdServer.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace FluentFTP.Servers.Handlers { 5 | 6 | /// 7 | /// Server-specific handling for glFTPd FTP servers 8 | /// 9 | internal class GlFtpdServer : FtpBaseServer { 10 | 11 | /// 12 | /// Return the FtpServer enum value corresponding to your server, or Unknown if its a custom implementation. 13 | /// 14 | public override FtpServer ToEnum() { 15 | return FtpServer.glFTPd; 16 | } 17 | 18 | /// 19 | /// Return true if your server is detected by the given FTP server welcome message. 20 | /// 21 | public override bool DetectByWelcome(string message) { 22 | 23 | // Detect glFTPd server 24 | // Welcome message: "220 W 00 T (glFTPd 2.01 Linux+TLS) ready." 25 | // Welcome message: "220 (glFTPd 2.01 Linux+TLS) ready." 26 | if (message.Contains("glFTPd ")) { 27 | return true; 28 | } 29 | 30 | return false; 31 | } 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /FluentFTP/Rules/FtpRule.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace FluentFTP.Rules { 6 | 7 | /// 8 | /// Base class used for all FTP Rules. Extend this class to create custom rules. 9 | /// You only need to provide an implementation for IsAllowed, and add any custom arguments that you require. 10 | /// 11 | public class FtpRule { 12 | 13 | /// 14 | /// Rule object 15 | /// 16 | public FtpRule() { 17 | } 18 | 19 | /// 20 | /// Returns true if the object has passed this rules. 21 | /// 22 | public virtual bool IsAllowed(FtpListItem result) { 23 | return true; 24 | } 25 | 26 | /// 27 | /// Returns true if the object has passed all the rules. 28 | /// 29 | public static bool IsAllAllowed(List rules, FtpListItem result) { 30 | foreach (var rule in rules) { 31 | if (!rule.IsAllowed(result)) { 32 | return false; 33 | } 34 | } 35 | return true; 36 | } 37 | 38 | } 39 | } -------------------------------------------------------------------------------- /FluentFTP/Servers/Handlers/ApacheFtpServer.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace FluentFTP.Servers.Handlers { 5 | 6 | /// 7 | /// Server-specific handling for Apache (MINA) FTP servers 8 | /// 9 | internal class ApacheFtpServer : FtpBaseServer { 10 | 11 | /// 12 | /// Return the FtpServer enum value corresponding to your server, or Unknown if its a custom implementation. 13 | /// 14 | public override FtpServer ToEnum() { 15 | return FtpServer.Apache; 16 | } 17 | 18 | /// 19 | /// Return true if your server is detected by the given SYST response message. 20 | /// Its a fallback method if the server did not send an identifying welcome message. 21 | /// 22 | public override bool DetectBySyst(string message) { 23 | 24 | // Detect Apache server 25 | // SYST type: "UNIX Type: Apache FtpServer" 26 | if (message.Contains("Apache FtpServer")) { 27 | return true; 28 | } 29 | 30 | return false; 31 | } 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /FluentFTP/Servers/Handlers/SolarisFtpServer.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace FluentFTP.Servers.Handlers { 5 | 6 | /// 7 | /// Server-specific handling for SolarisFTP servers 8 | /// 9 | internal class SolarisFtpServer : FtpBaseServer { 10 | 11 | /// 12 | /// Return the FtpServer enum value corresponding to your server, or Unknown if its a custom implementation. 13 | /// 14 | public override FtpServer ToEnum() { 15 | return FtpServer.SolarisFTP; 16 | } 17 | 18 | /// 19 | /// Return true if your server is detected by the given SYST response message. 20 | /// Its a fallback method if the server did not send an identifying welcome message. 21 | /// 22 | public override bool DetectBySyst(string message) { 23 | 24 | // Detect SolarisFTP server 25 | // SYST response: "215 UNIX Type: L8 Version: SUNOS" 26 | if (message.Contains("SUNOS")) { 27 | return true; 28 | } 29 | 30 | return false; 31 | } 32 | 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /FluentFTP/Client/AsyncClient/IsRoot.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using FluentFTP.Helpers; 4 | using FluentFTP.Client.Modules; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace FluentFTP { 9 | public partial class AsyncFtpClient { 10 | 11 | /// 12 | /// Is the current working directory the root? 13 | /// 14 | /// true if root. 15 | public async Task IsRoot(CancellationToken token = default(CancellationToken)) { 16 | 17 | // this case occurs immediately after connection and after the working dir has changed 18 | if (Status.LastWorkingDir == null) { 19 | await ReadCurrentWorkingDirectory(token); 20 | } 21 | 22 | if (Status.LastWorkingDir.IsFtpRootDirectory()) { 23 | return true; 24 | } 25 | 26 | // execute server-specific check if the current working dir is a root directory 27 | if (ServerHandler != null && ServerHandler.IsRoot(this, Status.LastWorkingDir)) { 28 | return true; 29 | } 30 | 31 | return false; 32 | } 33 | 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /FluentFTP/Servers/Handlers/MicroTikServer.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace FluentFTP.Servers.Handlers { 5 | 6 | /// 7 | /// Server-specific handling for MikroTik RouterOS FTP servers 8 | /// 9 | internal class MicroTikServer : FtpBaseServer { 10 | 11 | /// 12 | /// Return the FtpServer enum value corresponding to your server, or Unknown if its a custom implementation. 13 | /// 14 | public override FtpServer ToEnum() { 15 | return FtpServer.MikroTik; 16 | } 17 | 18 | /// 19 | /// Return true if your server is detected by the given FTP server welcome message. 20 | /// 21 | public override bool DetectByWelcome(string message) { 22 | 23 | // Detect MikroTik server 24 | // Welcome message: "MikroTik FTP server (MikroTik 2.9.27) ready" 25 | // Welcome message: "MikroTik FTP server (MikroTik 6.0rc2) ready" 26 | if (message.Contains("MikroTik FTP")) { 27 | return true; 28 | } 29 | 30 | return false; 31 | } 32 | 33 | } 34 | } -------------------------------------------------------------------------------- /FluentFTP.Tests/Unit/IsProxyTests.cs: -------------------------------------------------------------------------------- 1 | using FluentFTP.Client.BaseClient; 2 | using FluentFTP.Proxy.AsyncProxy; 3 | using FluentFTP.Proxy.SyncProxy; 4 | using Xunit; 5 | 6 | namespace FluentFTP.Tests.Unit { 7 | public class IsProxyTests { 8 | [MemberData(nameof(ProxyFtpClients))] 9 | [Theory] 10 | public void Proxy_clients_are_proxies(BaseFtpClient ftpClient) { 11 | // Act 12 | var isProxy = ftpClient.IsProxy(); 13 | 14 | // Assert 15 | Assert.True(isProxy); 16 | } 17 | 18 | public static TheoryData ProxyFtpClients => new() { 19 | new AsyncFtpClientSocks4aProxy(new FtpProxyProfile()), 20 | new FtpClientSocks4aProxy(new FtpProxyProfile()) 21 | }; 22 | 23 | [MemberData(nameof(FtpClients))] 24 | [Theory] 25 | public void Clients_are_not_proxies(BaseFtpClient ftpClient) { 26 | // Act 27 | var isProxy = ftpClient.IsProxy(); 28 | 29 | // Assert 30 | Assert.False(isProxy); 31 | } 32 | 33 | public static TheoryData FtpClients => new() { 34 | new AsyncFtpClient(), 35 | new FtpClient() 36 | }; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /FluentFTP/Client/SyncClient/GetFilePermissions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using FluentFTP.Helpers; 5 | 6 | namespace FluentFTP { 7 | public partial class FtpClient { 8 | 9 | /// 10 | /// Retrieve the permissions of the given file/folder as an FtpListItem object with all "Permission" properties set. 11 | /// Throws FtpCommandException if there is an issue. 12 | /// Returns null if the server did not specify a permission value. 13 | /// Use `GetChmod` if you required the integer value instead. 14 | /// 15 | /// The full or relative path to the item 16 | public FtpListItem GetFilePermissions(string path) { 17 | // verify args 18 | if (path.IsBlank()) { 19 | throw new ArgumentException("Required parameter is null or blank.", nameof(path)); 20 | } 21 | 22 | path = path.GetFtpPath(); 23 | 24 | LogFunction(nameof(GetFilePermissions), new object[] { path }); 25 | 26 | var result = GetObjectInfo(path); 27 | 28 | return result; 29 | } 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /LICENSE.TXT: -------------------------------------------------------------------------------- 1 | Copyright (c) 2015 Robin Rodricks and FluentFTP Contributors 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /FluentFTP/Client/AsyncClient/DeleteFile.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentFTP.Helpers; 3 | using System.Threading; 4 | using FluentFTP.Client.Modules; 5 | using System.Threading.Tasks; 6 | using FluentFTP.Exceptions; 7 | 8 | namespace FluentFTP { 9 | public partial class AsyncFtpClient { 10 | 11 | /// 12 | /// Deletes a file from the server asynchronously 13 | /// 14 | /// The full or relative path to the file 15 | /// The token that can be used to cancel the entire process 16 | public async Task DeleteFile(string path, CancellationToken token = default(CancellationToken)) { 17 | FtpReply reply; 18 | 19 | // verify args 20 | if (path.IsBlank()) { 21 | throw new ArgumentException("Required parameter is null or blank.", nameof(path)); 22 | } 23 | 24 | path = path.GetFtpPath(); 25 | 26 | LogFunction(nameof(DeleteFile), new object[] { path }); 27 | 28 | if (!(reply = await Execute("DELE " + path, token)).Success) { 29 | throw new FtpCommandException(reply); 30 | } 31 | } 32 | 33 | } 34 | } -------------------------------------------------------------------------------- /FluentFTP.VBExamples/Rename.vb: -------------------------------------------------------------------------------- 1 | Imports System 2 | Imports System.Net 3 | Imports System.Threading 4 | Imports System.Threading.Tasks 5 | Imports FluentFTP 6 | 7 | Namespace Examples 8 | Friend Module RenameExample 9 | Sub Rename() 10 | Using conn = New FtpClient("127.0.0.1", "ftptest", "ftptest") 11 | conn.Connect() 12 | 13 | ' renaming a directory Is dependent on the server! if you attempt it 14 | ' And it fails it's not because FluentFTP has a bug! 15 | conn.Rename("/full/or/relative/path/to/src", "/full/or/relative/path/to/dest") 16 | End Using 17 | End Sub 18 | 19 | Async Function RenameAsync() As Task 20 | Dim token = New CancellationToken() 21 | 22 | Using conn = New AsyncFtpClient("127.0.0.1", "ftptest", "ftptest") 23 | Await conn.Connect(token) 24 | 25 | ' renaming a directory Is dependent on the server! if you attempt it 26 | ' And it fails it's not because FluentFTP has a bug! 27 | Await conn.Rename("/full/or/relative/path/to/src", "/full/or/relative/path/to/dest", token) 28 | End Using 29 | End Function 30 | End Module 31 | End Namespace 32 | -------------------------------------------------------------------------------- /FluentFTP.CSharpExamples/FileExists.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using FluentFTP; 6 | 7 | namespace Examples { 8 | internal static class FileExistsExample { 9 | 10 | public static void FileExists() { 11 | using (var conn = new FtpClient("127.0.0.1", "ftptest", "ftptest")) { 12 | conn.Connect(); 13 | 14 | // The last parameter forces FluentFTP to use LIST -a 15 | // for getting a list of objects in the parent directory. 16 | if (conn.FileExists("/full/or/relative/path")) { 17 | // dome something 18 | } 19 | } 20 | } 21 | 22 | public static async Task FileExistsAsync() { 23 | var token = new CancellationToken(); 24 | using (var conn = new AsyncFtpClient("127.0.0.1", "ftptest", "ftptest")) { 25 | await conn.Connect(token); 26 | 27 | // The last parameter forces FluentFTP to use LIST -a 28 | // for getting a list of objects in the parent directory. 29 | if (await conn.FileExists("/full/or/relative/path", token)) { 30 | // dome something 31 | } 32 | } 33 | } 34 | 35 | } 36 | } -------------------------------------------------------------------------------- /FluentFTP.CSharpExamples/Rename.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using FluentFTP; 6 | 7 | namespace Examples { 8 | 9 | internal static class RenameExample { 10 | 11 | public static void Rename() { 12 | using (var conn = new FtpClient("127.0.0.1", "ftptest", "ftptest")) { 13 | conn.Connect(); 14 | 15 | // renaming a directory is dependent on the server! if you attempt it 16 | // and it fails it's not because FluentFTP has a bug! 17 | conn.Rename("/full/or/relative/path/to/src", "/full/or/relative/path/to/dest"); 18 | } 19 | } 20 | 21 | public static async Task RenameAsync() { 22 | var token = new CancellationToken(); 23 | using (var conn = new AsyncFtpClient("127.0.0.1", "ftptest", "ftptest")) { 24 | await conn.Connect(token); 25 | 26 | // renaming a directory is dependent on the server! if you attempt it 27 | // and it fails it's not because FluentFTP has a bug! 28 | await conn.Rename("/full/or/relative/path/to/src", "/full/or/relative/path/to/dest", token); 29 | } 30 | } 31 | 32 | } 33 | } -------------------------------------------------------------------------------- /FluentFTP/Enums/FtpLocalExists.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace FluentFTP { 4 | /// 5 | /// Determines how we handle partially downloaded files 6 | /// 7 | public enum FtpLocalExists { 8 | /// 9 | /// Restart the download of a file if it is partially downloaded. 10 | /// Overwrites the file if it exists on disk. 11 | /// 12 | Overwrite, 13 | 14 | /// 15 | /// Resume the download of a file if it is partially downloaded. 16 | /// Appends to the file if it exists, by checking the length and adding the missing data. 17 | /// If the file doesn't exist on disk, a new file is created. 18 | /// 19 | Resume, 20 | 21 | /// 22 | /// Blindly skip downloading the file if it exists on disk, without any more checks. 23 | /// This is only included to be compatible with legacy behaviour. 24 | /// 25 | Skip, 26 | 27 | /// 28 | /// Append is now renamed to Resume. 29 | /// 30 | [ObsoleteAttribute("Append is now renamed to Resume to better reflect its behaviour.", true)] 31 | Append, 32 | } 33 | } -------------------------------------------------------------------------------- /FluentFTP/Servers/FtpHandlerIndex.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using FluentFTP.Servers.Handlers; 3 | 4 | namespace FluentFTP.Servers { 5 | internal static class FtpHandlerIndex { 6 | 7 | public static List AllServers = new List { 8 | new ApacheFtpServer(), 9 | new BFtpdServer(), 10 | new CerberusServer(), 11 | new CrushFtpServer(), 12 | new FileZillaServer(), 13 | new FritzBoxServer(), 14 | new Ftp2S3GatewayServer(), 15 | new GlFtpdServer(), 16 | new GlobalScapeEftServer(), 17 | new HomegateFtpServer(), 18 | new IBMOS400FtpServer(), 19 | new IBMzOSFtpServer(), 20 | new IDALFtpServer(), 21 | new NonStopTandemServer(), 22 | new OpenVmsServer(), 23 | new ProFtpdServer(), 24 | new PureFtpdServer(), 25 | new PyFtpdLibServer(), 26 | new RumpusServer(), 27 | new ServUServer(), 28 | new SolarisFtpServer(), 29 | new TitanFtpServer(), 30 | new VsFtpdServer(), 31 | new WindowsCEServer(), 32 | new WindowsIISServer(), 33 | new WSFTPServer(), 34 | new WuFtpdServer(), 35 | new XLightServer(), 36 | }; 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /FluentFTP/Enums/FtpOperator.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace FluentFTP { 6 | 7 | /// 8 | /// For setting up rules 9 | /// 10 | public enum FtpOperator { 11 | 12 | /// 13 | /// If the value is exactly equal to X 14 | /// 15 | Equals, 16 | /// 17 | /// If the value is anything except for X 18 | /// 19 | NotEquals, 20 | /// 21 | /// If the value is less than X 22 | /// 23 | LessThan, 24 | /// 25 | /// If the value is less than or equal to X 26 | /// 27 | LessThanOrEquals, 28 | /// 29 | /// If the value is more than X 30 | /// 31 | MoreThan, 32 | /// 33 | /// If the value is more than or equal to X 34 | /// 35 | MoreThanOrEquals, 36 | /// 37 | /// If the value is between the range of X and Y 38 | /// 39 | BetweenRange, 40 | /// 41 | /// If the value is outside the range of X and Y 42 | /// 43 | OutsideRange, 44 | 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /.github/workflows_WIP/pull_request.yml: -------------------------------------------------------------------------------- 1 | name: pull_request 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | Tests: 11 | env: 12 | framework: 'net6.0' 13 | strategy: 14 | matrix: 15 | include: 16 | - ftp: pureftpd 17 | os: ubuntu-latest 18 | - ftp: vsftpd 19 | os: ubuntu-latest 20 | - ftp: pyftpdlib 21 | os: ubuntu-latest 22 | 23 | runs-on: ${{ matrix.os }} 24 | 25 | steps: 26 | - uses: actions/checkout@v3 27 | - name: Setup .NET 28 | uses: actions/setup-dotnet@v2 29 | with: 30 | dotnet-version: 6.0.x 31 | - name: Restore dependencies 32 | run: dotnet restore 33 | - name: Build 34 | run: dotnet build FluentFTP.Tests/FluentFTP.Tests.csproj --no-restore --framework ${{ env.framework }} 35 | - name: Test 36 | env: 37 | FluentFTP__Tests__Integration__FtpServerKey: ${{ matrix.ftp }} 38 | run: dotnet test FluentFTP.Tests/FluentFTP.Tests.csproj --no-build --verbosity normal --framework ${{ env.framework }} 39 | -------------------------------------------------------------------------------- /FluentFTP/Client/AsyncClient/SetWorkingDirectory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using FluentFTP.Helpers; 4 | using FluentFTP.Client.Modules; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | using FluentFTP.Exceptions; 8 | 9 | namespace FluentFTP { 10 | public partial class AsyncFtpClient { 11 | 12 | /// 13 | /// Sets the working directory on the server asynchronously 14 | /// 15 | /// The directory to change to 16 | /// The token that can be used to cancel the entire process 17 | public async Task SetWorkingDirectory(string path, CancellationToken token = default(CancellationToken)) { 18 | 19 | path = path.GetFtpPath(); 20 | 21 | LogFunction(nameof(SetWorkingDirectory), new object[] { path }); 22 | 23 | FtpReply reply; 24 | 25 | // exit if invalid path 26 | if (path is "." or "./") { 27 | return; 28 | } 29 | 30 | // modify working dir 31 | if (!(reply = await Execute("CWD " + path, token)).Success) { 32 | throw new FtpCommandException(reply); 33 | } 34 | 35 | } 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /FluentFTP/Helpers/LocalPorts.cs: -------------------------------------------------------------------------------- 1 | namespace FluentFTP.Helpers { 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Net; 6 | using System.Net.NetworkInformation; 7 | 8 | /// 9 | /// The local ports. 10 | /// 11 | internal static class LocalPorts { 12 | 13 | internal static readonly Random randomGen = new Random(); 14 | 15 | /// 16 | /// Get random local port for the given local IP address 17 | /// 18 | public static int GetRandomAvailable(IPAddress localIpAddress) { 19 | lock (randomGen) { 20 | var ipGlobalProperties = IPGlobalProperties.GetIPGlobalProperties(); 21 | var tcpConnInfoArray = ipGlobalProperties.GetActiveTcpListeners(); 22 | var inUsePorts = new HashSet( 23 | tcpConnInfoArray.Where(ipEndPoint => localIpAddress.Equals(ipEndPoint.Address)) 24 | .Select(ipEndPoint => ipEndPoint.Port)); 25 | int localPort; 26 | do { 27 | localPort = 1025 + randomGen.Next(32000); 28 | } 29 | while (inUsePorts.Contains(localPort)); 30 | 31 | return localPort; 32 | } 33 | } 34 | 35 | } 36 | } -------------------------------------------------------------------------------- /FluentFTP/Client/BaseClient/ValidateCertficate.cs: -------------------------------------------------------------------------------- 1 | namespace FluentFTP.Client.BaseClient { 2 | 3 | public partial class BaseFtpClient { 4 | 5 | /// 6 | /// Catches the socket stream ssl validation event and fires the event handlers 7 | /// attached to this object for validating SSL certificates 8 | /// 9 | /// The stream that fired the event 10 | /// The event args used to validate the certificate 11 | protected void FireValidateCertficate(FtpSocketStream stream, FtpSslValidationEventArgs e) { 12 | OnValidateCertficate(e); 13 | } 14 | 15 | /// 16 | /// Fires the SSL validation event 17 | /// 18 | /// Event Args 19 | protected void OnValidateCertficate(FtpSslValidationEventArgs e) { 20 | 21 | // automatically validate if ValidateAnyCertificate is set 22 | if (Config.ValidateAnyCertificate) { 23 | e.Accept = true; 24 | return; 25 | } 26 | 27 | // fallback to manual validation using the ValidateCertificate event 28 | m_ValidateCertificate?.Invoke(this, e); 29 | 30 | } 31 | 32 | } 33 | } -------------------------------------------------------------------------------- /FluentFTP.Dockers/glftpd/run-glftpd.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # stdout server info: 4 | cat << EOB 5 | ************************************************* 6 | * * 7 | * Docker image: fluentftp glftpd * 8 | * * 9 | ************************************************* 10 | 11 | SERVER SETTINGS 12 | --------------- 13 | · FTP User: fluentuser 14 | · FTP Password: fluentpass 15 | EOB 16 | 17 | # start xinetd, because glftpd cannot be run standalone. 18 | 19 | /usr/sbin/xinetd -pidfile /run/xinetd.pid -stayalive -inetd_compat -inetd_ipv6 20 | 21 | # The -n AND the quote user, quote password are NEEDED to make this hack work - 22 | # add the fluentuser to the initial glftpd user database. Can only do this be 23 | # logging on the glftpd and using site commands. 24 | 25 | ftp -n localhost </dev/null /opt/ 36 | tail -f /dev/null -------------------------------------------------------------------------------- /FluentFTP/Client/AsyncClient/Disconnect.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace FluentFTP { 6 | public partial class AsyncFtpClient { 7 | 8 | /// 9 | /// Disconnects from the server asynchronously 10 | /// 11 | public async Task Disconnect(CancellationToken token = default(CancellationToken)) { 12 | LogFunction(nameof(Disconnect), null); 13 | 14 | if (IsConnected) { 15 | try { 16 | if (Config.DisconnectWithQuit) { 17 | await Execute("QUIT", token); 18 | } 19 | } 20 | catch (Exception ex) { 21 | LogWithPrefix(FtpTraceLevel.Verbose, "AsyncFtpClient.Disconnect().Execute(\"QUIT\"): " + ex.Message); 22 | } 23 | finally { 24 | // When debugging, the stream might have already been taken down 25 | // from the remote side, thus causing an exception here, so check for null 26 | if (m_stream != null) { 27 | await m_stream.CloseAsync(token); 28 | } 29 | } 30 | } 31 | else { 32 | LogWithPrefix(FtpTraceLevel.Verbose, "Connection already closed, nothing to do."); 33 | } 34 | } 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /FluentFTP/Client/BaseClient/CheckFileExistsBySize.cs: -------------------------------------------------------------------------------- 1 | using FluentFTP.Helpers; 2 | using FluentFTP.Client.Modules; 3 | 4 | namespace FluentFTP.Client.BaseClient { 5 | public partial class BaseFtpClient { 6 | 7 | /// 8 | /// Try using the SIZE command to check if file exists 9 | /// 10 | /// 11 | /// 12 | protected bool? CheckFileExistsBySize(FtpSizeReply sizeReply) { 13 | 14 | // file surely exists 15 | if (sizeReply.Reply.Code[0] == '2') { 16 | return true; 17 | } 18 | 19 | // file surely does not exist 20 | if (sizeReply.Reply.Code[0] == '5' && sizeReply.Reply.Message.ContainsAnyCI(ServerStringModule.fileNotFound)) { 21 | return false; 22 | } 23 | 24 | // Fix #518: This check is too broad and must be disabled, need to fallback to MDTM or NLST instead. 25 | // Fix #179: Add a generic check to since server returns 550 if file not found or no access to file. 26 | /*if (sizeReply.Reply.Code.Substring(0, 3) == "550") { 27 | return false; 28 | }*/ 29 | 30 | // fallback to MDTM or NLST 31 | return null; 32 | } 33 | 34 | } 35 | } -------------------------------------------------------------------------------- /FluentFTP/Exceptions/IOExceptions.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Net.Sockets; 3 | using FluentFTP.Client.Modules; 4 | using FluentFTP.Helpers; 5 | 6 | namespace FluentFTP.Exceptions { 7 | /// 8 | /// Extension methods related to FTP tasks 9 | /// 10 | public static class IOExceptions { 11 | 12 | /// 13 | /// Check if operation can resume after . 14 | /// 15 | /// Received exception. 16 | /// Result of checking. 17 | public static bool IsResumeAllowed(this IOException exception) { 18 | // resume if server disconnects midway (fixes #39 and #410) 19 | if (exception.InnerException != null || exception.Message.ContainsAnyCI(ServerStringModule.unexpectedEOF)) { 20 | if (exception.InnerException is SocketException socketException) { 21 | #if NETSTANDARD || NET5_0_OR_GREATER 22 | return (int)socketException.SocketErrorCode == 10054; 23 | #else 24 | return socketException.ErrorCode == 10054; 25 | #endif 26 | } 27 | 28 | return true; 29 | } 30 | 31 | return false; 32 | } 33 | 34 | 35 | } 36 | } -------------------------------------------------------------------------------- /FluentFTP.VBExamples/GetNameListing.vb: -------------------------------------------------------------------------------- 1 | Imports System 2 | Imports System.Net 3 | Imports System.Threading 4 | Imports System.Threading.Tasks 5 | Imports FluentFTP 6 | 7 | Namespace Examples 8 | Friend Module GetNameListingExample 9 | Sub GetNameListing() 10 | Using conn = New FtpClient("127.0.0.1", "ftptest", "ftptest") 11 | conn.Connect() 12 | 13 | For Each s In conn.GetNameListing() 14 | Dim isDirectory = conn.DirectoryExists(s) 15 | Dim modify = conn.GetModifiedTime(s) 16 | Dim size = If(isDirectory, 0, conn.GetFileSize(s)) 17 | Next 18 | End Using 19 | End Sub 20 | 21 | Async Function GetNameListingAsync() As Task 22 | Dim token = New CancellationToken() 23 | 24 | Using conn = New AsyncFtpClient("127.0.0.1", "ftptest", "ftptest") 25 | Await conn.Connect(token) 26 | 27 | For Each s In Await conn.GetNameListing(token) 28 | Dim isDirectory = Await conn.DirectoryExists(s, token) 29 | Dim modify = Await conn.GetModifiedTime(s, token) 30 | Dim size = If(isDirectory, 0, Await conn.GetFileSize(s, -1, token)) 31 | Next 32 | End Using 33 | End Function 34 | End Module 35 | End Namespace 36 | -------------------------------------------------------------------------------- /FluentFTP/Client/SyncClient/GetModifiedTime.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentFTP.Helpers; 3 | using FluentFTP.Client.Modules; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace FluentFTP { 8 | public partial class FtpClient { 9 | 10 | /// 11 | /// Gets the modified time of a remote file. 12 | /// 13 | /// The full path to the file 14 | /// The modified time, or if there was a problem 15 | public virtual DateTime GetModifiedTime(string path) { 16 | // verify args 17 | if (path.IsBlank()) { 18 | throw new ArgumentException("Required parameter is null or blank.", nameof(path)); 19 | } 20 | 21 | path = path.GetFtpPath(); 22 | 23 | LogFunction(nameof(GetModifiedTime), new object[] { path }); 24 | 25 | var date = DateTime.MinValue; 26 | FtpReply reply; 27 | 28 | // get modified date of a file 29 | if ((reply = Execute("MDTM " + path)).Success) { 30 | date = reply.Message.ParseFtpDate(this); 31 | date = ConvertDate(date); 32 | } 33 | 34 | return date; 35 | } 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /FluentFTP/Client/BaseClient/StartListeningOnPort.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net.Sockets; 3 | using FluentFTP.Helpers; 4 | 5 | namespace FluentFTP.Client.BaseClient { 6 | 7 | public partial class BaseFtpClient { 8 | 9 | /// 10 | /// Open a local port on the given ActivePort or a random port. 11 | /// 12 | /// 13 | protected void StartListeningOnPort(FtpDataStream stream) { 14 | if (Config.ActivePorts.IsBlank()) { 15 | // Use random port 16 | stream.Listen(m_stream.LocalEndPoint.Address, 0); 17 | } 18 | else { 19 | var success = false; 20 | 21 | // Use one of the specified ports 22 | foreach (var port in Config.ActivePorts) { 23 | try { 24 | stream.Listen(m_stream.LocalEndPoint.Address, port); 25 | success = true; 26 | break; 27 | } 28 | catch (SocketException se) when (se.SocketErrorCode == SocketError.AddressAlreadyInUse) { 29 | } 30 | } 31 | 32 | // No usable port found 33 | if (!success) { 34 | throw new Exception("No valid active data port available!"); 35 | } 36 | } 37 | } 38 | 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /FluentFTP.Xunit/Docker/Containers/BFtpdContainer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using DotNet.Testcontainers.Builders; 7 | using DotNet.Testcontainers.Containers; 8 | 9 | namespace FluentFTP.Xunit.Docker.Containers { 10 | internal class BFtpdContainer : DockerFtpContainer { 11 | 12 | public BFtpdContainer() { 13 | ServerType = FtpServer.BFTPd; 14 | ServerName = "bftpd"; 15 | DockerImage = "bftpd:fluentftp"; 16 | //without SSL: 17 | // RunCommand = "docker run --rm -it -p 21:21 -p 21100-21199:21100-21199 bftpd:fluentftp"; 18 | //with SSL: 19 | // not possible 20 | } 21 | 22 | /// 23 | /// For help creating this section see https://github.com/testcontainers/testcontainers-dotnet#supported-commands 24 | /// 25 | public override ITestcontainersBuilder Configure(ITestcontainersBuilder builder) { 26 | 27 | builder = builder.WithPortBinding(20); 28 | 29 | builder = ExposePortRange(builder, 21100, 21199); 30 | 31 | return builder; 32 | } 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /FluentFTP.VBExamples/Connect.vb: -------------------------------------------------------------------------------- 1 | Imports System 2 | Imports System.Net 3 | Imports System.Threading 4 | Imports System.Threading.Tasks 5 | Imports FluentFTP 6 | 7 | Namespace Examples 8 | Friend Module ConnectExample 9 | Sub Connect() 10 | Using conn = New FtpClient() 11 | conn.Host = "localhost" 12 | conn.Credentials = New NetworkCredential("ftptest", "ftptest") 13 | conn.Connect() 14 | End Using 15 | End Sub 16 | 17 | Sub ConnectAlt() 18 | Using conn = New FtpClient("127.0.0.1", "ftptest", "ftptest") 19 | conn.Connect() 20 | End Using 21 | End Sub 22 | 23 | Async Function ConnectAsync() As Task 24 | Dim token = New CancellationToken() 25 | 26 | Using conn = New AsyncFtpClient() 27 | conn.Host = "localhost" 28 | conn.Credentials = New NetworkCredential("ftptest", "ftptest") 29 | Await conn.Connect(token) 30 | End Using 31 | End Function 32 | 33 | Async Function ConnectAsyncAlt() As Task 34 | Dim token = New CancellationToken() 35 | 36 | Using conn = New AsyncFtpClient("127.0.0.1", "ftptest", "ftptest") 37 | Await conn.Connect(token) 38 | End Using 39 | End Function 40 | End Module 41 | End Namespace 42 | -------------------------------------------------------------------------------- /FluentFTP.Dockers/pyftpdlib/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Andriy Kohut 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /FluentFTP.Xunit/Docker/Containers/ApacheContainer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using DotNet.Testcontainers.Builders; 7 | using DotNet.Testcontainers.Containers; 8 | 9 | namespace FluentFTP.Xunit.Docker.Containers { 10 | internal class ApacheContainer : DockerFtpContainer { 11 | 12 | public ApacheContainer() { 13 | ServerType = FtpServer.Apache; 14 | ServerName = "apache"; 15 | DockerImage = "apache:fluentftp"; 16 | //without SSL: 17 | // RunCommand = "docker run --rm -it -p 21:21 -p 21100-21199:21100-21199 apache:fluentftp"; 18 | //with SSL: 19 | // not possible 20 | } 21 | 22 | /// 23 | /// For help creating this section see https://github.com/testcontainers/testcontainers-dotnet#supported-commands 24 | /// 25 | public override ITestcontainersBuilder Configure(ITestcontainersBuilder builder) { 26 | 27 | builder = builder.WithPortBinding(20); 28 | 29 | builder = ExposePortRange(builder, 21100, 21199); 30 | 31 | return builder; 32 | } 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /FluentFTP.Xunit/Docker/Containers/GlFtpdContainer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using DotNet.Testcontainers.Builders; 7 | using DotNet.Testcontainers.Containers; 8 | 9 | namespace FluentFTP.Xunit.Docker.Containers { 10 | internal class GlFtpdContainer : DockerFtpContainer { 11 | 12 | public GlFtpdContainer() { 13 | ServerType = FtpServer.glFTPd; 14 | ServerName = "glftpd"; 15 | DockerImage = "glftpd:fluentftp"; 16 | //without SSL: 17 | // RunCommand = "docker run --rm -it -p 21:21 -p 21100-21199:21100-21199 glftpd:fluentftp"; 18 | //with SSL: 19 | // not possible 20 | } 21 | 22 | /// 23 | /// For help creating this section see https://github.com/testcontainers/testcontainers-dotnet#supported-commands 24 | /// 25 | public override ITestcontainersBuilder Configure(ITestcontainersBuilder builder) { 26 | 27 | builder = builder.WithPortBinding(20); 28 | 29 | builder = ExposePortRange(builder, 21100, 21199); 30 | 31 | return builder; 32 | } 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /FluentFTP.Xunit/FluentFTP.Xunit.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net7.0 5 | enable 6 | enable 7 | false 8 | AnyCPU;x64 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | runtime; build; native; contentfiles; analyzers; buildtransitive 17 | all 18 | 19 | 20 | runtime; build; native; contentfiles; analyzers; buildtransitive 21 | all 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /FluentFTP.Xunit/Docker/Containers/FileZillaContainer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using DotNet.Testcontainers.Builders; 7 | using DotNet.Testcontainers.Containers; 8 | 9 | namespace FluentFTP.Xunit.Docker.Containers { 10 | internal class FileZillaContainer : DockerFtpContainer { 11 | 12 | public FileZillaContainer() { 13 | ServerType = FtpServer.FileZilla; 14 | ServerName = "filezilla"; 15 | DockerImage = "filezilla:fluentftp"; 16 | //without SSL: 17 | // not possible 18 | //with SSL: 19 | // RunCommand = "docker run --rm -it -p 21:21 -p 21100-21199:21100-21199 filezilla:fluentftp"; 20 | } 21 | 22 | /// 23 | /// For help creating this section see https://github.com/testcontainers/testcontainers-dotnet#supported-commands 24 | /// 25 | public override ITestcontainersBuilder Configure(ITestcontainersBuilder builder) { 26 | 27 | builder = builder.WithPortBinding(20); 28 | 29 | builder = ExposePortRange(builder, 21100, 21199); 30 | 31 | return builder; 32 | } 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /FluentFTP.Xunit/Docker/Containers/ProFtpdContainer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using DotNet.Testcontainers.Builders; 7 | using DotNet.Testcontainers.Containers; 8 | 9 | namespace FluentFTP.Xunit.Docker.Containers { 10 | internal class ProFtpdContainer : DockerFtpContainer { 11 | 12 | public ProFtpdContainer() { 13 | ServerType = FtpServer.ProFTPD; 14 | ServerName = "proftpd"; 15 | DockerImage = "proftpd:fluentftp"; 16 | //without SSL: 17 | // RunCommand = "docker run -d --net host proftpd:fluentftp"; 18 | //with SSL: 19 | // RunCommand = "docker run -d --net host -e USE_SSL=YES proftpd:fluentftp"; 20 | } 21 | 22 | /// 23 | /// For help creating this section see https://github.com/testcontainers/testcontainers-dotnet#supported-commands 24 | /// 25 | public override ITestcontainersBuilder Configure(ITestcontainersBuilder builder) { 26 | 27 | builder = builder.WithPortBinding(20); 28 | 29 | builder = ExposePortRange(builder, 21100, 21199); 30 | 31 | return builder; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /FluentFTP.CSharpExamples/Connect.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using FluentFTP; 6 | 7 | namespace Examples { 8 | internal static class ConnectExample { 9 | 10 | public static void Connect() { 11 | 12 | using (var conn = new FtpClient()) { 13 | conn.Host = "localhost"; 14 | conn.Credentials = new NetworkCredential("ftptest", "ftptest"); 15 | 16 | conn.Connect(); 17 | } 18 | } 19 | 20 | public static void ConnectAlt() { 21 | 22 | using (var conn = new FtpClient("127.0.0.1", "ftptest", "ftptest")) { 23 | 24 | conn.Connect(); 25 | } 26 | } 27 | 28 | public static async Task ConnectAsync() { 29 | var token = new CancellationToken(); 30 | 31 | using (var conn = new AsyncFtpClient()) { 32 | conn.Host = "localhost"; 33 | conn.Credentials = new NetworkCredential("ftptest", "ftptest"); 34 | 35 | await conn.Connect(token); 36 | } 37 | } 38 | 39 | public static async Task ConnectAsyncAlt() { 40 | var token = new CancellationToken(); 41 | 42 | using (var conn = new AsyncFtpClient("127.0.0.1", "ftptest", "ftptest")) { 43 | 44 | await conn.Connect(token); 45 | } 46 | } 47 | 48 | } 49 | } -------------------------------------------------------------------------------- /FluentFTP.Xunit/Attributes/Internal/SkippableFactMessageBus.cs: -------------------------------------------------------------------------------- 1 | using FluentFTP.Xunit.Attributes; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Xunit.Abstractions; 8 | using Xunit.Sdk; 9 | 10 | namespace FluentFTP.Xunit.Attributes.Internal { 11 | internal class SkippableFactMessageBus : IMessageBus { 12 | readonly IMessageBus innerBus; 13 | 14 | public SkippableFactMessageBus(IMessageBus innerBus) { 15 | this.innerBus = innerBus; 16 | } 17 | 18 | public int DynamicallySkippedTestCount { get; private set; } 19 | 20 | public void Dispose() { } 21 | 22 | public bool QueueMessage(IMessageSinkMessage message) { 23 | var testFailed = message as ITestFailed; 24 | if (testFailed != null) { 25 | var exceptionType = testFailed.ExceptionTypes.FirstOrDefault(); 26 | if (exceptionType == typeof(SkipTestException).FullName) { 27 | DynamicallySkippedTestCount++; 28 | return innerBus.QueueMessage(new TestSkipped(testFailed.Test, testFailed.Messages.FirstOrDefault())); 29 | } 30 | } 31 | 32 | // Nothing we care about, send it on its way 33 | return innerBus.QueueMessage(message); 34 | } 35 | } 36 | } -------------------------------------------------------------------------------- /FluentFTP.Xunit/Docker/Containers/PyFtpdLibContainer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using DotNet.Testcontainers.Builders; 7 | using DotNet.Testcontainers.Containers; 8 | 9 | namespace FluentFTP.Xunit.Docker.Containers { 10 | internal class PyFtpdLibContainer : DockerFtpContainer { 11 | public PyFtpdLibContainer() { 12 | ServerType = FtpServer.PyFtpdLib; 13 | ServerName = "pyftpdlib"; 14 | DockerImage = "pyftpdlib:fluentftp"; 15 | DockerImageOriginal = "akogut/docker-pyftpdlib"; 16 | DockerGithub = "https://github.com/andriykohut/docker-pyftpdlib"; 17 | //RunCommand = "docker run -it --rm -p 21:21 pyftpdlib:fluentftp"; 18 | FixedUsername = "user"; 19 | FixedPassword = "password"; 20 | } 21 | 22 | /// 23 | /// For help creating this section see https://github.com/testcontainers/testcontainers-dotnet#supported-commands 24 | /// 25 | public override ITestcontainersBuilder Configure(ITestcontainersBuilder builder) { 26 | 27 | builder = base.ExposePortRange(builder, 3000, 3010); 28 | return builder; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /FluentFTP/Exceptions/FtpSecurityNotAvailableException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | #if NETFRAMEWORK 3 | using System.Runtime.Serialization; 4 | #endif 5 | 6 | namespace FluentFTP.Exceptions { 7 | 8 | /// 9 | /// Exception is thrown when TLS/SSL encryption could not be negotiated by the FTP server. 10 | /// 11 | #if NETFRAMEWORK 12 | [Serializable] 13 | #endif 14 | public class FtpSecurityNotAvailableException : FtpException { 15 | /// 16 | /// Default constructor 17 | /// 18 | public FtpSecurityNotAvailableException() 19 | : base("FTPS security is not available on the server. To disable FTPS, set the EncryptionMode property to None.") { 20 | 21 | } 22 | 23 | /// 24 | /// Custom error message 25 | /// 26 | /// Error message 27 | public FtpSecurityNotAvailableException(string message) 28 | : base(message) { 29 | } 30 | 31 | #if NETFRAMEWORK 32 | /// 33 | /// Must be implemented so every Serializer can Deserialize the Exception 34 | /// 35 | protected FtpSecurityNotAvailableException(SerializationInfo info, StreamingContext context) : base(info, context) { 36 | } 37 | 38 | #endif 39 | } 40 | } -------------------------------------------------------------------------------- /FluentFTP/Servers/Handlers/HuaweiServer.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace FluentFTP.Servers.Handlers { 5 | 6 | /// 7 | /// Server-specific handling for Huawei FTP servers 8 | /// 9 | internal class HuaweiServer : FtpBaseServer { 10 | 11 | /// 12 | /// Return the FtpServer enum value corresponding to your server, or Unknown if its a custom implementation. 13 | /// 14 | public override FtpServer ToEnum() { 15 | return FtpServer.Huawei; 16 | } 17 | 18 | /// 19 | /// Return true if your server is detected by the given FTP server welcome message. 20 | /// 21 | public override bool DetectByWelcome(string message) { 22 | 23 | // Detect Huawei server 24 | // Welcome message: "220 HG510a FTP version 1.0 ready" 25 | // Welcome message: "220 HG520b FTP version 1.0 ready" 26 | // Welcome message: "220 HG530 FTP version 1.0 ready" 27 | if (message.Contains("FTP version 1.0")) { 28 | if (message.Contains("HG51") || message.Contains("HG52") 29 | || message.Contains("HG53") || message.Contains("HG54")) { 30 | return true; 31 | } 32 | } 33 | 34 | return false; 35 | } 36 | 37 | } 38 | } -------------------------------------------------------------------------------- /FluentFTP/Servers/Handlers/WindowsIISServer.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace FluentFTP.Servers.Handlers { 5 | 6 | /// 7 | /// Server-specific handling for WindowsServer/IIS FTP servers 8 | /// 9 | internal class WindowsIISServer : FtpBaseServer { 10 | 11 | /// 12 | /// Return the FtpServer enum value corresponding to your server, or Unknown if its a custom implementation. 13 | /// 14 | public override FtpServer ToEnum() { 15 | return FtpServer.WindowsServerIIS; 16 | } 17 | 18 | /// 19 | /// Return true if your server is detected by the given FTP server welcome message. 20 | /// 21 | public override bool DetectByWelcome(string message) { 22 | 23 | // Detect Windows Server/IIS FTP server 24 | // Welcome message: "220-Microsoft FTP Service." 25 | if (message.Contains("Microsoft FTP Service")) { 26 | return true; 27 | } 28 | 29 | return false; 30 | } 31 | 32 | /// 33 | /// Return the default file listing parser to be used with your FTP server. 34 | /// 35 | public override FtpParser GetParser() { 36 | return FtpParser.Windows; 37 | } 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /FluentFTP/Client/BaseClient/Dispose.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace FluentFTP.Client.BaseClient { 5 | 6 | public partial class BaseFtpClient { 7 | 8 | // Some BaseFtpClient methods refer to a "Dispose". It is necessary to route these 9 | // to the appropriate sync or async dispose, depending on FtpClient or AsyncFtpClient 10 | // having inherited BaseFtpClient 11 | 12 | /// 13 | /// Disconnects from the server 14 | /// 15 | void IInternalFtpClient.DisposeInternal() { 16 | // sync: This dispose percolates down to the BaseFtpClient.Dispose 17 | ((IFtpClient)this).Dispose(); 18 | } 19 | 20 | /// 21 | /// Disconnects from the server asynchronously 22 | /// 23 | #if NETSTANDARD2_1_OR_GREATER || NET5_0_OR_GREATER 24 | async ValueTask IInternalFtpClient.DisposeInternal(CancellationToken token) { 25 | // async: This dispose handled in the AsyncFtpClient 26 | await ((IAsyncFtpClient)this).DisposeAsync(); 27 | } 28 | #else 29 | async Task IInternalFtpClient.DisposeInternal(CancellationToken token) { 30 | // async: This dispose handled in the BaseFtpClient 31 | await Task.Run(() => Dispose()); 32 | } 33 | #endif 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /FluentFTP/Enums/FtpCompareOption.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace FluentFTP { 6 | /// 7 | /// Flags that control how file comparison is performed. If you are unsure what to use, set it to Auto. 8 | /// 9 | [Flags] 10 | public enum FtpCompareOption { 11 | 12 | /// 13 | /// Compares the file size and the checksum of the file (using the first supported hash algorithm). 14 | /// The local and remote file sizes and checksums should exactly match for the file to be considered equal. 15 | /// 16 | Auto = 0, 17 | 18 | /// 19 | /// Compares the file size. 20 | /// Both file sizes should exactly match for the file to be considered equal. 21 | /// 22 | Size = 1, 23 | 24 | /// 25 | /// Compares the date modified of the file. 26 | /// Both dates should exactly match for the file to be considered equal. 27 | /// 28 | DateModified = 2, 29 | 30 | /// 31 | /// Compares the checksum or hash of the file using the first supported hash algorithm. 32 | /// Both checksums should exactly match for the file to be considered equal. 33 | /// 34 | Checksum = 4, 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /FluentFTP/Proxy/SyncProxy/FtpClientProxy.cs: -------------------------------------------------------------------------------- 1 | using FluentFTP.Client.BaseClient; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace FluentFTP.Proxy.SyncProxy { 6 | /// 7 | /// Abstraction of an FtpClient with a proxy 8 | /// 9 | public abstract class FtpClientProxy : FtpClient { 10 | private FtpProxyProfile _proxy; 11 | 12 | /// The proxy connection info. 13 | protected FtpProxyProfile Proxy => _proxy; 14 | 15 | /// A FTP client with a HTTP 1.1 proxy implementation 16 | /// Proxy information 17 | protected FtpClientProxy(FtpProxyProfile proxy) { 18 | _proxy = proxy; 19 | 20 | // set the FTP server details into the client if provided 21 | if (_proxy.FtpHost != null) { 22 | Host = _proxy.FtpHost; 23 | Port = _proxy.FtpPort; 24 | Credentials = _proxy.FtpCredentials; 25 | } 26 | } 27 | 28 | /// Redefine connect for FtpClient : authentication on the Proxy 29 | /// The socket stream. 30 | protected override void Connect(FtpSocketStream stream) { 31 | stream.Connect(Proxy.ProxyHost, Proxy.ProxyPort, Config.InternetProtocolVersions); 32 | } 33 | 34 | } 35 | } -------------------------------------------------------------------------------- /FluentFTP.Xunit/Docker/Containers/VsFtpdContainer.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using DotNet.Testcontainers.Builders; 7 | using DotNet.Testcontainers.Containers; 8 | 9 | namespace FluentFTP.Xunit.Docker.Containers { 10 | internal class VsFtpdContainer : DockerFtpContainer { 11 | 12 | public VsFtpdContainer() { 13 | ServerType = FtpServer.VsFTPd; 14 | ServerName = "vsftpd"; 15 | DockerImage = "vsftpd:fluentftp"; 16 | //without SSL: 17 | // RunCommand = "docker run --rm -it -p 21:21 -p 21100-21199:21100-21199 vsftpd:fluentftp"; 18 | //with SSL: 19 | // RunCommand = "docker run --rm -it -p 21:21 -p 21100-21199:21100-21199 -e USE_SSL=YES vsftpd:fluentftp"; 20 | } 21 | 22 | /// 23 | /// For help creating this section see https://github.com/testcontainers/testcontainers-dotnet#supported-commands 24 | /// 25 | public override ITestcontainersBuilder Configure(ITestcontainersBuilder builder) { 26 | 27 | builder = builder.WithPortBinding(20); 28 | 29 | builder = ExposePortRange(builder, 21100, 21199); 30 | 31 | return builder; 32 | } 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /FluentFTP/Servers/Handlers/WindowsCEServer.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace FluentFTP.Servers.Handlers { 5 | 6 | /// 7 | /// Server-specific handling for WindowsCE FTP servers 8 | /// 9 | internal class WindowsCEServer : FtpBaseServer { 10 | 11 | /// 12 | /// Return the FtpServer enum value corresponding to your server, or Unknown if its a custom implementation. 13 | /// 14 | public override FtpServer ToEnum() { 15 | return FtpServer.WindowsCE; 16 | } 17 | 18 | /// 19 | /// Return true if your server is detected by the given SYST response message. 20 | /// Its a fallback method if the server did not send an identifying welcome message. 21 | /// 22 | public override bool DetectBySyst(string message) { 23 | 24 | // Detect WindowsCE server 25 | // SYST type: "Windows_CE version 7.0" 26 | if (message.Contains("Windows_CE")) { 27 | return true; 28 | } 29 | 30 | return false; 31 | } 32 | 33 | /// 34 | /// Return the default file listing parser to be used with your FTP server. 35 | /// 36 | public override FtpParser GetParser() { 37 | return FtpParser.Windows; 38 | } 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /FluentFTP/Client/AsyncClient/GetFilePermissions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | using FluentFTP.Helpers; 5 | 6 | namespace FluentFTP { 7 | public partial class AsyncFtpClient { 8 | 9 | /// 10 | /// Retrieve the permissions of the given file/folder as an FtpListItem object with all "Permission" properties set. 11 | /// Throws FtpCommandException if there is an issue. 12 | /// Returns null if the server did not specify a permission value. 13 | /// Use `GetChmod` if you required the integer value instead. 14 | /// 15 | /// The full or relative path to the item 16 | /// The token that can be used to cancel the entire process 17 | public async Task GetFilePermissions(string path, CancellationToken token = default(CancellationToken)) { 18 | // verify args 19 | if (path.IsBlank()) { 20 | throw new ArgumentException("Required parameter is null or blank.", nameof(path)); 21 | } 22 | 23 | path = path.GetFtpPath(); 24 | 25 | LogFunction(nameof(GetFilePermissions), new object[] { path }); 26 | 27 | var result = await GetObjectInfo(path, false, token); 28 | 29 | return result; 30 | } 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /FluentFTP.Dockers/bftpd/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # FluentFTP Integration Test Server: bftpd 3 | # 4 | 5 | # 6 | # Stage 1: build 7 | # 8 | 9 | FROM common-debian:fluentftp AS build 10 | 11 | SHELL ["/bin/bash", "-c"] 12 | 13 | WORKDIR /tmp/bftpd 14 | RUN wget --no-check-certificate -O bftpd.tar.gz https://downloads.sourceforge.net/project/bftpd/bftpd/bftpd-6.1/bftpd-6.1.tar.gz && \ 15 | tar -xzf bftpd.tar.gz && \ 16 | cd bftpd && \ 17 | ./configure && \ 18 | make -j$(nprocs) && \ 19 | make install 20 | 21 | # 22 | # Stage 2: production 23 | # 24 | 25 | FROM common-debian-slim:fluentftp AS production 26 | 27 | LABEL Description="FluentFTP bftpd docker image based on Debian Bullseye." 28 | 29 | SHELL ["/bin/bash", "-c"] 30 | 31 | COPY run-bftpd.sh /usr/sbin/ 32 | 33 | COPY --from=build /usr/sbin/bftpd /usr/sbin 34 | 35 | COPY bftpd.conf /usr/etc/ 36 | 37 | WORKDIR / 38 | RUN sed -i -e "s/\r//" /usr/etc/bftpd.conf && \ 39 | sed -i -e "s/\r//" /usr/sbin/run-bftpd.sh && \ 40 | chmod +x /usr/sbin/run-bftpd.sh && \ 41 | \ 42 | useradd -m -p savatlcb.1m26 fluentuser && \ 43 | \ 44 | mkdir -p /home/fluentuser/ && \ 45 | chown -R fluentuser:users /home/fluentuser 46 | 47 | VOLUME ["/home/fluentuser", "/var/log/bftpd"] 48 | 49 | EXPOSE 20 21 50 | 51 | CMD ["/usr/sbin/run-bftpd.sh"] 52 | -------------------------------------------------------------------------------- /FluentFTP/Proxy/SyncProxy/FtpClientUserAtHostProxy.cs: -------------------------------------------------------------------------------- 1 | using FluentFTP.Client.BaseClient; 2 | 3 | namespace FluentFTP.Proxy.SyncProxy { 4 | /// A FTP client with a user@host proxy identification. 5 | public class FtpClientUserAtHostProxy : FtpClientProxy { 6 | /// A FTP client with a user@host proxy identification. 7 | /// Proxy information 8 | public FtpClientUserAtHostProxy(FtpProxyProfile proxy) 9 | : base(proxy) { 10 | ConnectionType = "User@Host"; 11 | } 12 | 13 | /// 14 | /// Creates a new instance of this class. Useful in FTP proxy classes. 15 | /// 16 | protected override BaseFtpClient Create() { 17 | return new FtpClientUserAtHostProxy(Proxy); 18 | } 19 | 20 | /// Redefine the first dialog: auth with proxy information 21 | protected override void Handshake() { 22 | // Proxy authentication eventually needed. 23 | if (Proxy.ProxyCredentials != null) { 24 | Authenticate(Proxy.ProxyCredentials.UserName, Proxy.ProxyCredentials.Password, Proxy.ProxyCredentials.Domain); 25 | } 26 | 27 | // Connection USER@Host means to change user name to add host. 28 | Credentials.UserName = Credentials.UserName + "@" + Host + ":" + Port; 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /FluentFTP/Exceptions/FtpAuthenticationException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | #if NETFRAMEWORK 3 | using System.Runtime.Serialization; 4 | #endif 5 | 6 | namespace FluentFTP.Exceptions { 7 | 8 | /// 9 | /// Exception triggered on FTP authentication failures 10 | /// 11 | #if NETFRAMEWORK 12 | [Serializable] 13 | #endif 14 | public class FtpAuthenticationException : FtpCommandException { 15 | /// 16 | /// Initializes a new instance of a FtpAuthenticationException 17 | /// 18 | /// Status code 19 | /// Associated message 20 | public FtpAuthenticationException(string code, string message) : base(code, message) { 21 | } 22 | 23 | /// 24 | /// Initializes a new instance of a FtpAuthenticationException 25 | /// 26 | /// The FtpReply to build the exception from 27 | public FtpAuthenticationException(FtpReply reply) : base(reply) { 28 | } 29 | 30 | #if NETFRAMEWORK 31 | /// 32 | /// Must be implemented so every Serializer can Deserialize the Exception 33 | /// 34 | protected FtpAuthenticationException(SerializationInfo info, StreamingContext context) : base(info, context) { 35 | } 36 | 37 | #endif 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /FluentFTP.VBExamples/FolderMonitor.vb: -------------------------------------------------------------------------------- 1 | Imports System 2 | Imports System.Collections.Generic 3 | Imports System.IO 4 | Imports System.Linq 5 | Imports System.Threading 6 | Imports System.Threading.Tasks 7 | Imports FluentFTP 8 | Imports FluentFTP.Monitors 9 | 10 | Namespace Examples 11 | Friend Module FolderMonitorExample 12 | Async Function DownloadStablePdfFilesAsync(ByVal token As CancellationToken) As Task 13 | Dim conn = New AsyncFtpClient("127.0.0.1", "ftptest", "ftptest") 14 | Await conn.Connect(token) 15 | 16 | Using monitor = New BlockingAsyncFtpMonitor(conn, New List(Of String) From { 17 | "path/to/folder" 18 | }) 19 | monitor.PollInterval = TimeSpan.FromMinutes(5) 20 | monitor.WaitForUpload = True 21 | 22 | monitor.ChangeDetected = 23 | Async Function(e) 24 | If True Then 25 | For Each file In e.Added.Where(Function(x) Path.GetExtension(x) = ".pdf") 26 | Dim localFilePath = Path.Combine("C:\LocalFolder", Path.GetFileName(file)) 27 | Await e.AsyncFtpClient.DownloadFile(localFilePath, file, token:=e.CancellationToken) 28 | Await e.AsyncFtpClient.DeleteFile(file) 29 | Next 30 | End If 31 | End Function 32 | 33 | Await monitor.Start(token) 34 | End Using 35 | End Function 36 | 37 | End Module 38 | End Namespace 39 | 40 | -------------------------------------------------------------------------------- /FluentFTP/Model/FtpProxyProfile.cs: -------------------------------------------------------------------------------- 1 | using System.Net; 2 | 3 | namespace FluentFTP { 4 | 5 | /// 6 | /// Connection profile for a proxy connection. 7 | /// 8 | public class FtpProxyProfile { 9 | 10 | /// 11 | /// Proxy server host name. Mandatory. 12 | /// 13 | public string ProxyHost { get; set; } 14 | 15 | /// 16 | /// Proxy server port. Mandatory. 17 | /// 18 | public int ProxyPort { get; set; } 19 | 20 | /// 21 | /// Proxy server login credentials. Mandatory if your proxy needs authentication, leave it blank otherwise. 22 | /// 23 | public NetworkCredential ProxyCredentials { get; set; } 24 | 25 | /// 26 | /// FTP server host name. Optional. You can either set it here or set `ftpClient.Host` later on. 27 | /// 28 | public string FtpHost { get; set; } 29 | 30 | /// 31 | /// FTP server port. Optional. You can either set it here or set `ftpClient.Port` later on. 32 | /// 33 | public int FtpPort { get; set; } 34 | 35 | /// 36 | /// FTP server login credentials. Optional. You can either set it here or set `ftpClient.Credentials` later on. 37 | /// 38 | public NetworkCredential FtpCredentials { get; set; } 39 | 40 | 41 | } 42 | } -------------------------------------------------------------------------------- /FluentFTP/Client/AsyncClient/GetModifiedTime.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using FluentFTP.Helpers; 3 | using FluentFTP.Client.Modules; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace FluentFTP { 8 | public partial class AsyncFtpClient { 9 | 10 | /// 11 | /// Gets the modified time of a remote file asynchronously 12 | /// 13 | /// The full path to the file 14 | /// The token that can be used to cancel the entire process 15 | /// The modified time, or if there was a problem 16 | public async Task GetModifiedTime(string path, CancellationToken token = default(CancellationToken)) { 17 | // verify args 18 | if (path.IsBlank()) { 19 | throw new ArgumentException("Required parameter is null or blank.", nameof(path)); 20 | } 21 | 22 | path = path.GetFtpPath(); 23 | 24 | LogFunction(nameof(GetModifiedTime), new object[] { path }); 25 | 26 | var date = DateTime.MinValue; 27 | FtpReply reply; 28 | 29 | // get modified date of a file 30 | if ((reply = await Execute("MDTM " + path, token)).Success) { 31 | date = reply.Message.ParseFtpDate(this); 32 | date = ConvertDate(date); 33 | } 34 | 35 | return date; 36 | } 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /FluentFTP.VBExamples/ConnectFTPSCertificate.vb: -------------------------------------------------------------------------------- 1 | Imports System 2 | Imports System.Net 3 | Imports System.Threading 4 | Imports System.Threading.Tasks 5 | Imports FluentFTP 6 | 7 | Namespace Examples 8 | Friend Module ConnectFTPSCertificateExample 9 | Sub ConnectFTPSCertificate() 10 | Using conn = New FtpClient("127.0.0.1", "ftptest", "ftptest") 11 | conn.Config.EncryptionMode = FtpEncryptionMode.Explicit 12 | AddHandler conn.ValidateCertificate, New FtpSslValidation(AddressOf OnValidateCertificate) 13 | conn.Connect() 14 | End Using 15 | End Sub 16 | 17 | Async Function ConnectFTPSCertificateAsync() As Task 18 | Dim token = New CancellationToken() 19 | 20 | Using conn = New AsyncFtpClient("127.0.0.1", "ftptest", "ftptest") 21 | conn.Config.EncryptionMode = FtpEncryptionMode.Explicit 22 | AddHandler conn.ValidateCertificate, New FtpSslValidation(AddressOf OnValidateCertificate) 23 | Await conn.Connect(token) 24 | End Using 25 | End Function 26 | 27 | Private Sub OnValidateCertificate(ByVal control As FtpClient, ByVal e As FtpSslValidationEventArgs) 28 | If e.PolicyErrors <> System.Net.Security.SslPolicyErrors.None Then 29 | ' invalid cert, do you want to accept it? 30 | ' e.Accept = True 31 | Else 32 | e.Accept = True 33 | End If 34 | End Sub 35 | End Module 36 | End Namespace 37 | -------------------------------------------------------------------------------- /FluentFTP.CSharpExamples/GetNameListing.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using FluentFTP; 6 | 7 | namespace Examples { 8 | internal static class GetNameListingExample { 9 | 10 | public static void GetNameListing() { 11 | using (var conn = new FtpClient("127.0.0.1", "ftptest", "ftptest")) { 12 | conn.Connect(); 13 | 14 | foreach (var s in conn.GetNameListing()) { 15 | // load some information about the object 16 | // returned from the listing... 17 | var isDirectory = conn.DirectoryExists(s); 18 | var modify = conn.GetModifiedTime(s); 19 | var size = isDirectory ? 0 : conn.GetFileSize(s); 20 | } 21 | } 22 | } 23 | 24 | public static async Task GetNameListingAsync() { 25 | var token = new CancellationToken(); 26 | using (var conn = new AsyncFtpClient("127.0.0.1", "ftptest", "ftptest")) { 27 | await conn.Connect(token); 28 | 29 | foreach (var s in await conn.GetNameListing(token)) { 30 | // load some information about the object 31 | // returned from the listing... 32 | var isDirectory = await conn.DirectoryExists(s, token); 33 | var modify = await conn.GetModifiedTime(s, token); 34 | var size = isDirectory ? 0 : await conn.GetFileSize(s, -1, token); 35 | } 36 | } 37 | } 38 | 39 | } 40 | } -------------------------------------------------------------------------------- /FluentFTP/Servers/Handlers/FileZillaServer.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace FluentFTP.Servers.Handlers { 5 | 6 | /// 7 | /// Server-specific handling for FileZilla FTP servers 8 | /// 9 | internal class FileZillaServer : FtpBaseServer { 10 | 11 | /// 12 | /// Return the FtpServer enum value corresponding to your server, or Unknown if its a custom implementation. 13 | /// 14 | public override FtpServer ToEnum() { 15 | return FtpServer.FileZilla; 16 | } 17 | 18 | /// 19 | /// Return true if your server is detected by the given FTP server welcome message. 20 | /// 21 | public override bool DetectByWelcome(string message) { 22 | 23 | // Detect FileZilla server 24 | // Welcome message: "FileZilla Server 0.9.60 beta" 25 | if (message.Contains("FileZilla Server")) { 26 | return true; 27 | } 28 | 29 | return false; 30 | } 31 | 32 | /// 33 | /// Detect if your FTP server supports the recursive LIST command (LIST -R). 34 | /// If you know for sure that this is supported, return true here. 35 | /// 36 | public override bool RecursiveList() { 37 | 38 | // No support, per: https://trac.filezilla-project.org/ticket/1848 39 | return false; 40 | 41 | } 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /FluentFTP.CSharpExamples/ConnectFTPSCertificate.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using FluentFTP; 6 | using FluentFTP.Client.BaseClient; 7 | 8 | namespace Examples { 9 | internal static class ConnectFTPSCertificateExample { 10 | 11 | public static void ConnectFTPSCertificate() { 12 | using (var conn = new FtpClient("127.0.0.1", "ftptest", "ftptest")) { 13 | conn.Config.EncryptionMode = FtpEncryptionMode.Explicit; 14 | conn.ValidateCertificate += new FtpSslValidation(OnValidateCertificate); 15 | conn.Connect(); 16 | } 17 | } 18 | 19 | public static async Task ConnectFTPSCertificateAsync() { 20 | var token = new CancellationToken(); 21 | using (var conn = new AsyncFtpClient("127.0.0.1", "ftptest", "ftptest")) { 22 | 23 | conn.Config.EncryptionMode = FtpEncryptionMode.Explicit; 24 | conn.ValidateCertificate += new FtpSslValidation(OnValidateCertificate); 25 | await conn.Connect(token); 26 | } 27 | } 28 | 29 | private static void OnValidateCertificate(BaseFtpClient control, FtpSslValidationEventArgs e) { 30 | if (e.PolicyErrors != System.Net.Security.SslPolicyErrors.None) { 31 | // invalid cert, do you want to accept it? 32 | // e.Accept = true; 33 | } 34 | else { 35 | e.Accept = true; 36 | } 37 | } 38 | 39 | } 40 | } -------------------------------------------------------------------------------- /FluentFTP.VBExamples/UploadManyFiles.vb: -------------------------------------------------------------------------------- 1 | Imports System 2 | Imports System.Net 3 | Imports System.Threading 4 | Imports System.Threading.Tasks 5 | Imports FluentFTP 6 | 7 | Namespace Examples 8 | Friend Module UploadFilesExample 9 | Sub UploadFiles() 10 | Using ftp = New FtpClient("127.0.0.1", "ftptest", "ftptest") 11 | ftp.Connect() 12 | 13 | ' upload many files, skip if they already exist on server 14 | ftp.UploadFiles({ 15 | "D:\Drivers\test\file0.exe", 16 | "D:\Drivers\test\file1.exe", 17 | "D:\Drivers\test\file2.exe", 18 | "D:\Drivers\test\file3.exe", 19 | "D:\Drivers\test\file4.exe" 20 | }, "/public_html/temp/", FtpRemoteExists.Skip) 21 | 22 | End Using 23 | End Sub 24 | 25 | Async Function UploadFilesAsync() As Task 26 | Dim token = New CancellationToken() 27 | 28 | Using ftp = New AsyncFtpClient("127.0.0.1", "ftptest", "ftptest") 29 | Await ftp.Connect(token) 30 | 31 | ' upload many files, skip if they already exist on server 32 | Await ftp.UploadFiles({ 33 | "D:\Drivers\test\file0.exe", 34 | "D:\Drivers\test\file1.exe", 35 | "D:\Drivers\test\file2.exe", 36 | "D:\Drivers\test\file3.exe", 37 | "D:\Drivers\test\file4.exe" 38 | }, "/public_html/temp/", FtpRemoteExists.Skip) 39 | 40 | End Using 41 | End Function 42 | End Module 43 | End Namespace 44 | -------------------------------------------------------------------------------- /FluentFTP/Client/SyncClient/AutoConnect.cs: -------------------------------------------------------------------------------- 1 | using FluentFTP.Client.Modules; 2 | using FluentFTP.Model.Functions; 3 | 4 | namespace FluentFTP { 5 | public partial class FtpClient { 6 | 7 | /// 8 | /// Automatic FTP and FTPS connection negotiation. 9 | /// This method tries every possible combination of the FTP connection properties, and connects to the first successful profile. 10 | /// Returns the FtpProfile if the connection succeeded, or null if it failed. 11 | /// It will throw exceptions for permanent failures like invalid host or invalid credentials. 12 | /// 13 | public FtpProfile AutoConnect() { 14 | LogFunction(nameof(AutoConnect)); 15 | 16 | // connect to the first available connection profile 17 | var results = AutoDetect(new FtpAutoDetectConfig() { 18 | FirstOnly = true, 19 | CloneConnection = false, 20 | }); 21 | if (results.Count > 0) { 22 | var profile = results[0]; 23 | 24 | // load the profile so final property selections are 25 | // loaded into the current connection 26 | LoadProfile(profile); 27 | 28 | // if we are using SSL, set a basic server acceptance function 29 | ConnectModule.SetDefaultCertificateValidation(this, profile); 30 | 31 | // return the working profile 32 | return profile; 33 | } 34 | 35 | return null; 36 | } 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /FluentFTP/Servers/Handlers/PureFtpdServer.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace FluentFTP.Servers.Handlers { 5 | 6 | /// 7 | /// Server-specific handling for PureFTPd FTP servers 8 | /// 9 | internal class PureFtpdServer : FtpBaseServer { 10 | 11 | /// 12 | /// Return the FtpServer enum value corresponding to your server, or Unknown if its a custom implementation. 13 | /// 14 | public override FtpServer ToEnum() { 15 | return FtpServer.PureFTPd; 16 | } 17 | 18 | /// 19 | /// Return true if your server is detected by the given FTP server welcome message. 20 | /// 21 | public override bool DetectByWelcome(string message) { 22 | 23 | // Detect Pure-FTPd server 24 | // Welcome message: "---------- Welcome to Pure-FTPd [privsep] [TLS] ----------" 25 | if (message.Contains("Pure-FTPd")) { 26 | return true; 27 | } 28 | 29 | return false; 30 | } 31 | 32 | /// 33 | /// Detect if your FTP server supports the recursive LIST command (LIST -R). 34 | /// If you know for sure that this is supported, return true here. 35 | /// 36 | public override bool RecursiveList() { 37 | 38 | // Has support, per https://download.pureftpd.org/pub/pure-ftpd/doc/README 39 | return true; 40 | } 41 | 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /FluentFTP/Helpers/Enums.cs: -------------------------------------------------------------------------------- 1 | namespace FluentFTP.Helpers { 2 | /// 3 | /// Extension methods related to FTP tasks 4 | /// 5 | public static class Enums { 6 | 7 | /// 8 | /// Validates that the FtpError flags set are not in an invalid combination. 9 | /// 10 | /// The error handling options set 11 | /// True if a valid combination, otherwise false 12 | public static bool IsValidCombination(this FtpError options) { 13 | return options != (FtpError.Stop | FtpError.Throw) && 14 | options != (FtpError.Throw | FtpError.Stop | FtpError.DeleteProcessed); 15 | } 16 | 17 | /// 18 | /// Checks if the operation was successful or skipped (indicating success). 19 | /// 20 | public static bool IsSuccess(this FtpStatus status) { 21 | return status is FtpStatus.Success or FtpStatus.Skipped; 22 | } 23 | 24 | /// 25 | /// Checks if the operation was skipped (specifically). 26 | /// 27 | public static bool IsSkipped(this FtpStatus status) { 28 | return status is FtpStatus.Skipped; 29 | } 30 | 31 | /// 32 | /// Checks if the operation has failed. 33 | /// 34 | public static bool IsFailure(this FtpStatus status) { 35 | return status == FtpStatus.Failed; 36 | } 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /FluentFTP/Servers/Handlers/VsFtpdServer.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace FluentFTP.Servers.Handlers { 5 | 6 | /// 7 | /// Server-specific handling for VsFTPd FTP servers 8 | /// 9 | internal class VsFtpdServer : FtpBaseServer { 10 | 11 | /// 12 | /// Return the FtpServer enum value corresponding to your server, or Unknown if its a custom implementation. 13 | /// 14 | public override FtpServer ToEnum() { 15 | return FtpServer.VsFTPd; 16 | } 17 | 18 | /// 19 | /// Return true if your server is detected by the given FTP server welcome message. 20 | /// 21 | public override bool DetectByWelcome(string message) { 22 | 23 | // Detect vsFTPd server 24 | // Welcome message: "(vsFTPd 3.0.3)" 25 | if (message.Contains("(vsFTPd")) { 26 | return true; 27 | } 28 | 29 | return false; 30 | } 31 | 32 | /// 33 | /// Detect if your FTP server supports the recursive LIST command (LIST -R). 34 | /// If you know for sure that this is supported, return true here. 35 | /// 36 | public override bool RecursiveList() { 37 | 38 | // Has support, but OFF by default, per: https://linux.die.net/man/5/vsftpd.conf 39 | return false; // impossible to detect on a server-by-server basis 40 | } 41 | 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /FluentFTP.Dockers/apache/users.properties: -------------------------------------------------------------------------------- 1 | # Licensed to the Apache Software Foundation (ASF) under one 2 | # or more contributor license agreements. See the NOTICE file 3 | # distributed with this work for additional information 4 | # regarding copyright ownership. The ASF licenses this file 5 | # to you under the Apache License, Version 2.0 (the 6 | # "License"); you may not use this file except in compliance 7 | # with the License. You may obtain a copy of the License at 8 | # 9 | # http://www.apache.org/licenses/LICENSE-2.0 10 | # 11 | # Unless required by applicable law or agreed to in writing, 12 | # software distributed under the License is distributed on an 13 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 14 | # KIND, either express or implied. See the License for the 15 | # specific language governing permissions and limitations 16 | # under the License. 17 | 18 | # user password is "fluentpass" code in md5 19 | ftpserver.user.fluentuser.userpassword=9AB870DD41C07E40C6C96307DB575E56 20 | ftpserver.user.fluentuser.homedirectory=./res/home 21 | ftpserver.user.fluentuser.enableflag=true 22 | ftpserver.user.fluentuser.writepermission=true 23 | ftpserver.user.fluentuser.maxloginnumber=0 24 | ftpserver.user.fluentuser.maxloginperip=0 25 | ftpserver.user.fluentuser.idletime=0 26 | ftpserver.user.fluentuser.uploadrate=0 27 | ftpserver.user.fluentuser.downloadrate=0 28 | -------------------------------------------------------------------------------- /FluentFTP.VBExamples/DownloadManyFiles.vb: -------------------------------------------------------------------------------- 1 | Imports System 2 | Imports System.Net 3 | Imports System.Threading 4 | Imports System.Threading.Tasks 5 | Imports FluentFTP 6 | 7 | Namespace Examples 8 | Friend Module DownloadFilesExample 9 | Sub DownloadFiles() 10 | Using ftp = New FtpClient("127.0.0.1", "ftptest", "ftptest") 11 | ftp.Connect() 12 | 13 | ' download many files, skip if they already exist on disk 14 | ftp.DownloadFiles("D:\Drivers\test\", { 15 | "/public_html/temp/file0.exe", 16 | "/public_html/temp/file1.exe", 17 | "/public_html/temp/file2.exe", 18 | "/public_html/temp/file3.exe", 19 | "/public_html/temp/file4.exe" 20 | }, FtpLocalExists.Skip) 21 | 22 | End Using 23 | End Sub 24 | 25 | Async Function DownloadFilesAsync() As Task 26 | Dim token = New CancellationToken() 27 | 28 | Using ftp = New AsyncFtpClient("127.0.0.1", "ftptest", "ftptest") 29 | Await ftp.Connect(token) 30 | 31 | ' download many files, skip if they already exist on disk 32 | Await ftp.DownloadFiles("D:\Drivers\test\", { 33 | "/public_html/temp/file0.exe", 34 | "/public_html/temp/file1.exe", 35 | "/public_html/temp/file2.exe", 36 | "/public_html/temp/file3.exe", 37 | "/public_html/temp/file4.exe" 38 | }, FtpLocalExists.Skip) 39 | 40 | End Using 41 | End Function 42 | End Module 43 | End Namespace 44 | -------------------------------------------------------------------------------- /FluentFTP/Servers/Handlers/NonStopTandemServer.cs: -------------------------------------------------------------------------------- 1 | using System.Threading; 2 | using System.Threading.Tasks; 3 | 4 | namespace FluentFTP.Servers.Handlers { 5 | 6 | /// 7 | /// Server-specific handling for NonStop/Tandem FTP servers 8 | /// 9 | internal class NonStopTandemServer : FtpBaseServer { 10 | 11 | /// 12 | /// Return the FtpServer enum value corresponding to your server, or Unknown if its a custom implementation. 13 | /// 14 | public override FtpServer ToEnum() { 15 | return FtpServer.NonStopTandem; 16 | } 17 | 18 | /// 19 | /// Return true if your server is detected by the given FTP server welcome message. 20 | /// 21 | public override bool DetectByWelcome(string message) { 22 | 23 | // Detect Tandem/NonStop server 24 | // Welcome message: "220 mysite.com FTP SERVER T9552H02 (Version H02 TANDEM 11SEP2008) ready." 25 | // Welcome message: "220 FTP SERVER T9552G08 (Version G08 TANDEM 15JAN2008) ready." 26 | if (message.Contains("FTP SERVER ") && message.Contains(" TANDEM ")) { 27 | return true; 28 | } 29 | 30 | return false; 31 | } 32 | 33 | /// 34 | /// Return the default file listing parser to be used with your FTP server. 35 | /// 36 | public override FtpParser GetParser() { 37 | return FtpParser.NonStop; 38 | } 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /FluentFTP/Exceptions/FtpException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | #if NETFRAMEWORK 3 | using System.Runtime.Serialization; 4 | #endif 5 | 6 | namespace FluentFTP.Exceptions { 7 | /// 8 | /// FTP related error 9 | /// 10 | #if NETFRAMEWORK 11 | [Serializable] 12 | #endif 13 | public class FtpException : Exception { 14 | /// 15 | /// Initializes a new instance of the class. 16 | /// 17 | /// The error message 18 | public FtpException(string message) : base(message) { 19 | } 20 | 21 | /// 22 | /// Initializes a new instance of the class with an inner exception. 23 | /// 24 | /// The error message that explains the reason for the exception. 25 | /// The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified. 26 | public FtpException(string message, Exception innerException) : base(message, innerException) { 27 | } 28 | 29 | #if NETFRAMEWORK 30 | /// 31 | /// Must be implemented so every Serializer can Deserialize the Exception 32 | /// 33 | protected FtpException(SerializationInfo info, StreamingContext context) : base(info, context) { 34 | } 35 | 36 | #endif 37 | } 38 | } -------------------------------------------------------------------------------- /FluentFTP/Client/SyncClient/SetDataType.cs: -------------------------------------------------------------------------------- 1 | using FluentFTP.Exceptions; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace FluentFTP { 6 | public partial class FtpClient { 7 | 8 | /// Sets the data type of information sent over the data stream 9 | /// Thrown when a FTP Command error condition occurs. 10 | /// Thrown when a FTP error condition occurs. 11 | /// ASCII/Binary. 12 | /// This method doesn't do any locking to prevent recursive lock scenarios. Callers must do their own locking. 13 | protected void SetDataType(FtpDataType type) { 14 | // FIX : #291 only change the data type if different 15 | if (Status.CurrentDataType != type) { 16 | FtpReply reply; 17 | switch (type) { 18 | case FtpDataType.ASCII: 19 | if (!(reply = Execute("TYPE A")).Success) { 20 | throw new FtpCommandException(reply); 21 | } 22 | 23 | break; 24 | 25 | case FtpDataType.Binary: 26 | if (!(reply = Execute("TYPE I")).Success) { 27 | throw new FtpCommandException(reply); 28 | } 29 | 30 | break; 31 | 32 | default: 33 | throw new FtpException("Unsupported data type: " + type.ToString()); 34 | } 35 | 36 | Status.CurrentDataType = type; 37 | } 38 | } 39 | 40 | } 41 | } -------------------------------------------------------------------------------- /FluentFTP.Dockers/filezilla/users.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | This user can impersonate any system user. 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | l9jcg8P68htZEZF7iIy6GhrbS8HLFGSikYzaUAnHo5E 23 | oFoJsx+xI6ZIEg9dc7fJUnLqjHWklAi2QcuHe5QVhTE 24 | 100000 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /FluentFTP/Client/SyncClient/EmptyDirectory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using FluentFTP.Helpers; 4 | using FluentFTP.Client.Modules; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace FluentFTP { 9 | public partial class FtpClient { 10 | 11 | /// 12 | /// Deletes the contents of the specified directory, without deleting the directory itself. 13 | /// 14 | /// The full or relative path of the directorys contents to delete 15 | public void EmptyDirectory(string path) { 16 | EmptyDirectory(path, FtpListOption.Recursive); 17 | } 18 | 19 | /// 20 | /// Deletes the contents of the specified directory, without deleting the directory itself. 21 | /// 22 | /// The full or relative path of the directorys contents to delete 23 | /// Useful to delete hidden files or dot-files. 24 | public void EmptyDirectory(string path, FtpListOption options = FtpListOption.Recursive) { 25 | 26 | // verify args 27 | if (path.IsBlank()) { 28 | throw new ArgumentException("Required parameter is null or blank.", nameof(path)); 29 | } 30 | 31 | path = path.GetFtpPath(); 32 | 33 | LogFunction(nameof(EmptyDirectory), new object[] { path, options }); 34 | DeleteDirInternal(path, true, options, false, true); 35 | } 36 | 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /FluentFTP.Dockers/pureftpd/Dockerfile: -------------------------------------------------------------------------------- 1 | # 2 | # FluentFTP Integration Test Server: pureftpd 3 | # 4 | 5 | # 6 | # Stage 1: build & production 7 | # 8 | 9 | FROM common-debian-slim:fluentftp AS build 10 | 11 | LABEL Description="FluentFTP pureftpd docker image based on Debian Bullseye." 12 | 13 | SHELL ["/bin/bash", "-c"] 14 | 15 | ARG DEBIAN_FRONTEND=noninteractive 16 | ARG APT_CMD='apt install -y --no-install-recommends' 17 | 18 | COPY sources.list /etc/apt/sources.list 19 | 20 | RUN apt update && \ 21 | \ 22 | $APT_CMD \ 23 | openssl \ 24 | pure-ftpd 25 | 26 | COPY run-pureftpd.sh /usr/sbin/ 27 | 28 | RUN sed -i -e "s/\r//" /usr/sbin/run-pureftpd.sh && \ 29 | chmod +x /usr/sbin/run-pureftpd.sh && \ 30 | \ 31 | useradd -m -p savatlcb.1m26 fluentuser && \ 32 | \ 33 | mkdir -p /home/fluentuser/ && \ 34 | chown -R fluentuser:users /home/fluentuser && \ 35 | \ 36 | openssl req -x509 -newkey rsa:4096 \ 37 | -keyout /etc/ssl/private/pure-ftpd.key -out /etc/ssl/certs/pure-ftpd.crt \ 38 | -subj "/C=US/ST=State/L=/O=Dev/CN=fluentftp" \ 39 | -nodes -days 3650 && \ 40 | \ 41 | chmod 0600 /etc/ssl/private/pure-ftpd.key && \ 42 | chmod 0640 /etc/ssl/private/pure-ftpd.key && \ 43 | \ 44 | cat /etc/ssl/certs/pure-ftpd.crt /etc/ssl/private/pure-ftpd.key > /etc/ssl/private/pure-ftpd.pem 45 | 46 | VOLUME ["/home/fluentuser", "/var/log/pureftpd"] 47 | 48 | EXPOSE 20 21 49 | 50 | CMD ["/usr/sbin/run-pureftpd.sh"] 51 | -------------------------------------------------------------------------------- /FluentFTP.VBExamples/DownloadDirectory.vb: -------------------------------------------------------------------------------- 1 | Imports System 2 | Imports System.Collections.Generic 3 | Imports System.Net 4 | Imports System.Threading 5 | Imports System.Threading.Tasks 6 | Imports FluentFTP 7 | Imports FluentFTP.Rules 8 | 9 | Namespace Examples 10 | Friend Module DownloadDirectoryExample 11 | Sub DownloadDirectory() 12 | Using ftp = New FtpClient("127.0.0.1", "ftptest", "ftptest") 13 | ftp.Connect() 14 | 15 | ' download a folder And all its files 16 | ftp.DownloadDirectory("C:\website\logs\", "/public_html/logs", FtpFolderSyncMode.Update) 17 | 18 | ' download a folder And all its files, And delete extra files on disk 19 | ftp.DownloadDirectory("C:\website\dailybackup\", "/public_html/", FtpFolderSyncMode.Mirror) 20 | 21 | End Using 22 | End Sub 23 | 24 | Async Function DownloadDirectoryAsync() As Task 25 | Dim token = New CancellationToken() 26 | 27 | Using ftp = New AsyncFtpClient("127.0.0.1", "ftptest", "ftptest") 28 | Await ftp.Connect(token) 29 | 30 | ' download a folder And all its files 31 | Await ftp.DownloadDirectory("C:\website\logs\", "/public_html/logs", FtpFolderSyncMode.Update) 32 | 33 | ' download a folder And all its files, And delete extra files on disk 34 | Await ftp.DownloadDirectory("C:\website\dailybackup\", "/public_html/", FtpFolderSyncMode.Mirror) 35 | 36 | End Using 37 | End Function 38 | End Module 39 | End Namespace 40 | -------------------------------------------------------------------------------- /FluentFTP.VBExamples/UploadDirectory.vb: -------------------------------------------------------------------------------- 1 | Imports System 2 | Imports System.Collections.Generic 3 | Imports System.Net 4 | Imports System.Threading 5 | Imports System.Threading.Tasks 6 | Imports FluentFTP 7 | Imports FluentFTP.Rules 8 | 9 | Namespace Examples 10 | Friend Module UploadDirectoryExample 11 | Sub UploadDirectory() 12 | Using ftp = New FtpClient("127.0.0.1", "ftptest", "ftptest") 13 | ftp.Connect() 14 | 15 | ' upload a folder and all its files 16 | ftp.UploadDirectory("C:\website\videos\", "/public_html/videos", FtpFolderSyncMode.Update) 17 | 18 | ' upload a folder and all its files, and delete extra files on the server 19 | ftp.UploadDirectory("C:\website\assets\", "/public_html/assets", FtpFolderSyncMode.Mirror) 20 | 21 | End Using 22 | End Sub 23 | 24 | Async Function UploadDirectoryAsync() As Task 25 | Dim token = New CancellationToken() 26 | 27 | Using ftp = New AsyncFtpClient("127.0.0.1", "ftptest", "ftptest") 28 | Await ftp.Connect(token) 29 | 30 | ' upload a folder and all its files 31 | Await ftp.UploadDirectory("C:\website\videos\", "/public_html/videos", FtpFolderSyncMode.Update) 32 | 33 | ' upload a folder and all its files, and delete extra files on the server 34 | Await ftp.UploadDirectory("C:\website\assets\", "/public_html/assets", FtpFolderSyncMode.Mirror) 35 | 36 | End Using 37 | End Function 38 | End Module 39 | End Namespace 40 | -------------------------------------------------------------------------------- /FluentFTP.Tests/Integration/System/IntegrationTestRunner.cs: -------------------------------------------------------------------------------- 1 | using FluentFTP.Tests.Integration.Tests; 2 | using FluentFTP.Xunit.Docker; 3 | using System; 4 | using System.Threading.Tasks; 5 | using Xunit; 6 | 7 | namespace FluentFTP.Tests.Integration.System { 8 | internal static class IntegrationTestRunner { 9 | 10 | public static async Task Run(FtpServer serverType, UseStream useStream, bool useSsl = false) { 11 | 12 | // If we are in CI pipeline 13 | if (DockerFtpConfig.IsCI) { 14 | 15 | // just let the test pass 16 | return; 17 | } 18 | 19 | // spin up a new docker 20 | using var server = new DockerFtpServer(serverType, useStream.ToString(), useSsl); 21 | 22 | try { 23 | // TODO: create a better system instead of calling each test suite manually 24 | 25 | // run all types of sync tests 26 | new ConnectTests(server, useStream).RunAllTests(); 27 | new FileTransferTests(server, useStream).RunAllTests(); 28 | new ListingTests(server, useStream).RunAllTests(); 29 | 30 | // run all types of async tests 31 | await new ConnectTests(server, useStream).RunAllTestsAsync(); 32 | await new FileTransferTests(server, useStream).RunAllTestsAsync(); 33 | await new ListingTests(server, useStream).RunAllTestsAsync(); 34 | 35 | } 36 | catch (Exception ex) { 37 | Assert.True(false, $"Integration test failed : " + ex.ToString()); 38 | } 39 | 40 | } 41 | 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /FluentFTP.CSharpExamples/UploadManyFiles.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using FluentFTP; 6 | 7 | namespace Examples { 8 | 9 | internal static class UploadFilesExample { 10 | 11 | public static void UploadFiles() { 12 | using (var ftp = new FtpClient("127.0.0.1", "ftptest", "ftptest")) { 13 | ftp.Connect(); 14 | 15 | // upload many files, skip if they already exist on server 16 | ftp.UploadFiles( 17 | new[] { 18 | @"D:\Drivers\test\file0.exe", 19 | @"D:\Drivers\test\file1.exe", 20 | @"D:\Drivers\test\file2.exe", 21 | @"D:\Drivers\test\file3.exe", 22 | @"D:\Drivers\test\file4.exe" 23 | }, 24 | "/public_html/temp/", FtpRemoteExists.Skip); 25 | 26 | } 27 | } 28 | 29 | public static async Task UploadFilesAsync() { 30 | var token = new CancellationToken(); 31 | using (var ftp = new AsyncFtpClient("127.0.0.1", "ftptest", "ftptest")) { 32 | await ftp.Connect(token); 33 | 34 | // upload many files, skip if they already exist on server 35 | await ftp.UploadFiles( 36 | new[] { 37 | @"D:\Drivers\test\file0.exe", 38 | @"D:\Drivers\test\file1.exe", 39 | @"D:\Drivers\test\file2.exe", 40 | @"D:\Drivers\test\file3.exe", 41 | @"D:\Drivers\test\file4.exe" 42 | }, 43 | "/public_html/temp/", FtpRemoteExists.Skip, token: token); 44 | 45 | } 46 | } 47 | 48 | } 49 | } -------------------------------------------------------------------------------- /FluentFTP/Exceptions/FtpInvalidCertificateException.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Security.Authentication; 3 | #if NETFRAMEWORK 4 | using System.Runtime.Serialization; 5 | #endif 6 | 7 | namespace FluentFTP.Exceptions { 8 | 9 | /// 10 | /// Exception is thrown when TLS/SSL encryption could not be negotiated by the FTP server. 11 | /// 12 | #if NETFRAMEWORK 13 | [Serializable] 14 | #endif 15 | public class FtpInvalidCertificateException : FtpException { 16 | 17 | /// 18 | /// AuthenticationException that caused this. 19 | /// 20 | public new Exception InnerException { get; private set; } 21 | 22 | /// 23 | /// Default constructor 24 | /// 25 | public FtpInvalidCertificateException(Exception innerException) 26 | : base("FTPS security could not be established on the server. The certificate was not accepted.", innerException) { 27 | } 28 | 29 | /// 30 | /// Custom error message 31 | /// 32 | /// Error message 33 | public FtpInvalidCertificateException(string message) 34 | : base(message) { 35 | } 36 | 37 | #if NETFRAMEWORK 38 | /// 39 | /// Must be implemented so every Serializer can Deserialize the Exception 40 | /// 41 | protected FtpInvalidCertificateException(SerializationInfo info, StreamingContext context) : base(info, context) { 42 | } 43 | 44 | #endif 45 | } 46 | } -------------------------------------------------------------------------------- /FluentFTP/Proxy/AsyncProxy/AsyncFtpClientUserAtHostProxy.cs: -------------------------------------------------------------------------------- 1 | using FluentFTP.Client.BaseClient; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace FluentFTP.Proxy.AsyncProxy { 6 | /// A FTP client with a user@host proxy identification. 7 | public class AsyncFtpClientUserAtHostProxy : AsyncFtpClientProxy { 8 | /// A FTP client with a user@host proxy identification. 9 | /// Proxy information 10 | public AsyncFtpClientUserAtHostProxy(FtpProxyProfile proxy) 11 | : base(proxy) { 12 | ConnectionType = "User@Host"; 13 | } 14 | 15 | /// 16 | /// Creates a new instance of this class. Useful in FTP proxy classes. 17 | /// 18 | protected override BaseFtpClient Create() { 19 | return new AsyncFtpClientUserAtHostProxy(Proxy); 20 | } 21 | 22 | /// Redefine the first dialog: auth with proxy information 23 | protected override async Task HandshakeAsync(CancellationToken token = default) { 24 | 25 | // Proxy authentication eventually needed. 26 | if (Proxy.ProxyCredentials != null) { 27 | await Authenticate(Proxy.ProxyCredentials.UserName, Proxy.ProxyCredentials.Password, Proxy.ProxyCredentials.Domain, token); 28 | } 29 | 30 | // Connection USER@Host means to change user name to add host. 31 | Credentials.UserName = Credentials.UserName + "@" + Host + ":" + Port; 32 | } 33 | } 34 | } -------------------------------------------------------------------------------- /FluentFTP.CSharpExamples/DownloadManyFiles.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Net; 3 | using System.Threading; 4 | using System.Threading.Tasks; 5 | using FluentFTP; 6 | 7 | namespace Examples { 8 | 9 | internal static class DownloadFilesExample { 10 | 11 | public static void DownloadFiles() { 12 | using (var ftp = new FtpClient("127.0.0.1", "ftptest", "ftptest")) { 13 | ftp.Connect(); 14 | 15 | // download many files, skip if they already exist on disk 16 | ftp.DownloadFiles(@"D:\Drivers\test\", 17 | new[] { 18 | @"/public_html/temp/file0.exe", 19 | @"/public_html/temp/file1.exe", 20 | @"/public_html/temp/file2.exe", 21 | @"/public_html/temp/file3.exe", 22 | @"/public_html/temp/file4.exe" 23 | }, FtpLocalExists.Skip); 24 | 25 | } 26 | } 27 | 28 | public static async Task DownloadFilesAsync() { 29 | var token = new CancellationToken(); 30 | using (var ftp = new AsyncFtpClient("127.0.0.1", "ftptest", "ftptest")) { 31 | await ftp.Connect(token); 32 | 33 | // download many files, skip if they already exist on disk 34 | await ftp.DownloadFiles(@"D:\Drivers\test\", 35 | new[] { 36 | @"/public_html/temp/file0.exe", 37 | @"/public_html/temp/file1.exe", 38 | @"/public_html/temp/file2.exe", 39 | @"/public_html/temp/file3.exe", 40 | @"/public_html/temp/file4.exe" 41 | }, FtpLocalExists.Skip, token: token); 42 | 43 | } 44 | } 45 | 46 | } 47 | } -------------------------------------------------------------------------------- /FluentFTP.VBExamples/DownloadFile.vb: -------------------------------------------------------------------------------- 1 | Imports System 2 | Imports System.Net 3 | Imports System.Threading 4 | Imports System.Threading.Tasks 5 | Imports FluentFTP 6 | 7 | Namespace Examples 8 | Friend Module DownloadFileExample 9 | Sub DownloadFile() 10 | Using ftp = New FtpClient("127.0.0.1", "ftptest", "ftptest") 11 | ftp.Connect() 12 | 13 | ' download a file and ensure the local directory is created 14 | ftp.DownloadFile("D:\Github\FluentFTP\README.md", "/public_html/temp/README.md") 15 | 16 | ' download a file and ensure the local directory is created, verify the file after download 17 | ftp.DownloadFile("D:\Github\FluentFTP\README.md", "/public_html/temp/README.md", FtpLocalExists.Overwrite, FtpVerify.Retry) 18 | 19 | End Using 20 | End Sub 21 | 22 | Async Function DownloadFileAsync() As Task 23 | Dim token = New CancellationToken() 24 | 25 | Using ftp = New AsyncFtpClient("127.0.0.1", "ftptest", "ftptest") 26 | Await ftp.Connect(token) 27 | 28 | ' download a file and ensure the local directory is created 29 | Await ftp.DownloadFile("D:\Github\FluentFTP\README.md", "/public_html/temp/README.md") 30 | 31 | ' download a file and ensure the local directory is created, verify the file after download 32 | Await ftp.DownloadFile("D:\Github\FluentFTP\README.md", "/public_html/temp/README.md", FtpLocalExists.Overwrite, FtpVerify.Retry) 33 | 34 | End Using 35 | End Function 36 | End Module 37 | End Namespace 38 | -------------------------------------------------------------------------------- /FluentFTP.CSharpExamples/DownloadDirectory.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | using FluentFTP; 7 | using FluentFTP.Rules; 8 | 9 | namespace Examples { 10 | 11 | internal static class DownloadDirectoryExample { 12 | 13 | public static void DownloadDirectory() { 14 | using (var ftp = new FtpClient("127.0.0.1", "ftptest", "ftptest")) { 15 | ftp.Connect(); 16 | 17 | 18 | // download a folder and all its files 19 | ftp.DownloadDirectory(@"C:\website\logs\", @"/public_html/logs", FtpFolderSyncMode.Update); 20 | 21 | // download a folder and all its files, and delete extra files on disk 22 | ftp.DownloadDirectory(@"C:\website\dailybackup\", @"/public_html/", FtpFolderSyncMode.Mirror); 23 | 24 | } 25 | } 26 | 27 | public static async Task DownloadDirectoryAsync() { 28 | var token = new CancellationToken(); 29 | using (var ftp = new AsyncFtpClient("127.0.0.1", "ftptest", "ftptest")) { 30 | await ftp.Connect(token); 31 | 32 | 33 | // download a folder and all its files 34 | await ftp.DownloadDirectory(@"C:\website\logs\", @"/public_html/logs", FtpFolderSyncMode.Update, token: token); 35 | 36 | // download a folder and all its files, and delete extra files on disk 37 | await ftp.DownloadDirectory(@"C:\website\dailybackup\", @"/public_html/", FtpFolderSyncMode.Mirror, token: token); 38 | 39 | } 40 | } 41 | 42 | } 43 | } --------------------------------------------------------------------------------