diff --git a/Duplicati/Library/Backend/AlternativeFTP/AlternativeFTPBackend.cs b/Duplicati/Library/Backend/AlternativeFTP/AlternativeFTPBackend.cs index 0501760ea..c6f81154a 100644 --- a/Duplicati/Library/Backend/AlternativeFTP/AlternativeFTPBackend.cs +++ b/Duplicati/Library/Backend/AlternativeFTP/AlternativeFTPBackend.cs @@ -21,6 +21,8 @@ using Duplicati.Library.Common.IO; using Duplicati.Library.Interface; using FluentFTP; +using FluentFTP.Client.BaseClient; +using FluentFTP.Exceptions; using System; using System.Collections.Generic; using System.IO; @@ -61,9 +63,7 @@ namespace Duplicati.Library.Backend.AlternativeFTP private readonly string _url; private readonly bool _listVerify = true; - private readonly FtpEncryptionMode _encryptionMode; - private readonly FtpDataConnectionType _dataConnectionType; - private readonly SslProtocols _sslProtocols; + private readonly FtpConfig _ftpConfig; private readonly TimeSpan _uploadWaitTime; private readonly byte[] _copybuffer = new byte[CoreUtility.DEFAULT_BUFFER_SIZE]; @@ -86,7 +86,7 @@ namespace Duplicati.Library.Backend.AlternativeFTP get { return "aftp"; } } - private FtpClient Client + private AsyncFtpClient Client { get; set; } public IList SupportedCommands @@ -162,42 +162,52 @@ namespace Duplicati.Library.Backend.AlternativeFTP // Process the aftp-data-connection-type option string dataConnectionTypeString; + FtpDataConnectionType dataConnectionType; if (!options.TryGetValue(CONFIG_KEY_AFTP_DATA_CONNECTION_TYPE, out dataConnectionTypeString) || string.IsNullOrWhiteSpace(dataConnectionTypeString)) { dataConnectionTypeString = null; } - if (dataConnectionTypeString == null || !Enum.TryParse(dataConnectionTypeString, true, out _dataConnectionType)) + if (dataConnectionTypeString == null || !Enum.TryParse(dataConnectionTypeString, true, out dataConnectionType)) { - _dataConnectionType = DEFAULT_DATA_CONNECTION_TYPE; + dataConnectionType = DEFAULT_DATA_CONNECTION_TYPE; } // Process the aftp-encryption-mode option string encryptionModeString; + FtpEncryptionMode encryptionMode; if (!options.TryGetValue(CONFIG_KEY_AFTP_ENCRYPTION_MODE, out encryptionModeString) || string.IsNullOrWhiteSpace(encryptionModeString)) { encryptionModeString = null; } - if (encryptionModeString == null || !Enum.TryParse(encryptionModeString, true, out _encryptionMode)) + if (encryptionModeString == null || !Enum.TryParse(encryptionModeString, true, out encryptionMode)) { - _encryptionMode = DEFAULT_ENCRYPTION_MODE; + encryptionMode = DEFAULT_ENCRYPTION_MODE; } // Process the aftp-ssl-protocols option string sslProtocolsString; + SslProtocols sslProtocols; if (!options.TryGetValue(CONFIG_KEY_AFTP_SSL_PROTOCOLS, out sslProtocolsString) || string.IsNullOrWhiteSpace(sslProtocolsString)) { sslProtocolsString = null; } - if (sslProtocolsString == null || !Enum.TryParse(sslProtocolsString, true, out _sslProtocols)) + if (sslProtocolsString == null || !Enum.TryParse(sslProtocolsString, true, out sslProtocols)) { - _sslProtocols = DEFAULT_SSL_PROTOCOLS; + sslProtocols = DEFAULT_SSL_PROTOCOLS; } + + _ftpConfig = new FtpConfig + { + DataConnectionType = dataConnectionType, + EncryptionMode = encryptionMode, + SslProtocols = sslProtocols, + }; } public IEnumerable List() @@ -237,11 +247,11 @@ namespace Duplicati.Library.Backend.AlternativeFTP // else: stripping the filename in this case ignoring it } - foreach (FtpListItem item in ftpClient.GetListing(remotePath, FtpListOption.Modify | FtpListOption.Size | FtpListOption.DerefLinks)) + foreach (FtpListItem item in ftpClient.GetListing(remotePath, FtpListOption.Modify | FtpListOption.Size).Result) { switch (item.Type) { - case FtpFileSystemObjectType.Directory: + case FtpObjectType.Directory: { if (item.Name == "." || item.Name == "..") { @@ -255,13 +265,13 @@ namespace Duplicati.Library.Backend.AlternativeFTP break; } - case FtpFileSystemObjectType.File: + case FtpObjectType.File: { list.Add(new FileEntry(item.Name, item.Size, new DateTime(), item.Modified)); break; } - case FtpFileSystemObjectType.Link: + case FtpObjectType.Link: { if (item.Name == "." || item.Name == "..") { @@ -272,7 +282,7 @@ namespace Duplicati.Library.Backend.AlternativeFTP { switch (item.LinkObject.Type) { - case FtpFileSystemObjectType.Directory: + case FtpObjectType.Directory: { if (item.Name == "." || item.Name == "..") { @@ -286,7 +296,7 @@ namespace Duplicati.Library.Backend.AlternativeFTP break; } - case FtpFileSystemObjectType.File: + case FtpObjectType.File: { list.Add(new FileEntry(item.Name, item.Size, new DateTime(), item.Modified)); @@ -337,8 +347,8 @@ namespace Duplicati.Library.Backend.AlternativeFTP remotePath += remotename; } - var success = await ftpClient.UploadAsync(input, remotePath, FtpExists.Overwrite, createRemoteDir: false, token: cancelToken, progress: null).ConfigureAwait(false); - if (!success) + var status = await ftpClient.UploadStream(input, remotePath, FtpRemoteExists.Overwrite, createRemoteDir: false, token: cancelToken, progress: null).ConfigureAwait(false); + if (status != FtpStatus.Success) { throw new UserInformationException(string.Format(Strings.ErrorWriteFile, remotename), "AftpPutFailure"); } @@ -352,7 +362,7 @@ namespace Duplicati.Library.Backend.AlternativeFTP if (_listVerify) { // check remote file size; matching file size indicates completion - var remoteSize = await ftpClient.GetFileSizeAsync(remotePath, cancelToken); + var remoteSize = await ftpClient.GetFileSize(remotePath, -1, cancelToken); if (streamLen != remoteSize) { throw new UserInformationException(Strings.ListVerifySizeFailure(remotename, remoteSize, streamLen), "AftpListVerifySizeFailure"); @@ -391,7 +401,7 @@ namespace Duplicati.Library.Backend.AlternativeFTP remotePath += remotename; } - using (var inputStream = ftpClient.OpenRead(remotePath)) + using (var inputStream = ftpClient.OpenRead(remotePath).Result) { try { @@ -426,7 +436,7 @@ namespace Duplicati.Library.Backend.AlternativeFTP remotePath += remotename; } - ftpClient.DeleteFile(remotePath); + ftpClient.DeleteFile(remotePath).Wait(); } @@ -516,7 +526,7 @@ namespace Duplicati.Library.Backend.AlternativeFTP var remotePath = this.GetUnescapedAbsolutePath(url); // Try to create the directory - client.CreateDirectory(remotePath, true); + client.CreateDirectory(remotePath, true).Wait(); } @@ -529,23 +539,18 @@ namespace Duplicati.Library.Backend.AlternativeFTP _userInfo = null; } - private FtpClient CreateClient() + private AsyncFtpClient CreateClient() { var uri = new Uri(_url); if (this.Client == null) // Create connection if it doesn't exist yet { - var ftpClient = new FtpClient + var ftpClient = new AsyncFtpClient { Host = uri.Host, Port = uri.Port == -1 ? 21 : uri.Port, Credentials = _userInfo, - EncryptionMode = _encryptionMode, - DataConnectionType = _dataConnectionType, - SslProtocols = _sslProtocols, - - // We do not support parallel uploads, and the feature is buggy - EnableThreadSafeDataConnections = false, + Config = _ftpConfig, }; ftpClient.ValidateCertificate += HandleValidateCertificate; @@ -556,7 +561,7 @@ namespace Duplicati.Library.Backend.AlternativeFTP // Change working directory to the remote path // Do this every time to prevent issues when FtpClient silently reconnects after failure. var remotePath = this.GetUnescapedAbsolutePath(uri); - this.Client.SetWorkingDirectory(remotePath); + this.Client.SetWorkingDirectory(remotePath).Wait(); return this.Client; } @@ -567,7 +572,7 @@ namespace Duplicati.Library.Backend.AlternativeFTP return absolutePath.EndsWith("/", StringComparison.Ordinal) ? absolutePath.Substring(0, absolutePath.Length - 1) : absolutePath; } - private void HandleValidateCertificate(FtpClient control, FtpSslValidationEventArgs e) + private void HandleValidateCertificate(BaseFtpClient control, FtpSslValidationEventArgs e) { if (e.PolicyErrors == SslPolicyErrors.None || _accepAllCertificates) { diff --git a/Duplicati/Library/Backend/AlternativeFTP/Duplicati.Library.Backend.AlternativeFTP.csproj b/Duplicati/Library/Backend/AlternativeFTP/Duplicati.Library.Backend.AlternativeFTP.csproj index a2042d478..3088ba7b6 100644 --- a/Duplicati/Library/Backend/AlternativeFTP/Duplicati.Library.Backend.AlternativeFTP.csproj +++ b/Duplicati/Library/Backend/AlternativeFTP/Duplicati.Library.Backend.AlternativeFTP.csproj @@ -35,8 +35,11 @@ Duplicati.snk - - ..\..\..\..\packages\FluentFTP.27.1.1\lib\net45\FluentFTP.dll + + ..\..\..\..\packages\FluentFTP.46.0.2\lib\net462\FluentFTP.dll + + + ..\..\..\..\packages\Microsoft.Extensions.Logging.Abstractions.2.1.0\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll diff --git a/Duplicati/Library/Backend/AlternativeFTP/packages.config b/Duplicati/Library/Backend/AlternativeFTP/packages.config index e18a4b2da..3eaac3f20 100644 --- a/Duplicati/Library/Backend/AlternativeFTP/packages.config +++ b/Duplicati/Library/Backend/AlternativeFTP/packages.config @@ -1,4 +1,5 @@  - - + + + \ No newline at end of file