Update FluentFTP to latest version (46.0.2) (#4935)

* Upgrade FluentFTP to v42 in an attempt to resolve TLS session issues:
duplicati/duplicati#4831

Note v40 introduced breaking changes, specifically seperating between sync and async clients.

Using a different approach this time, single async client.

* Bump FluentFTP to latest (42.0.0 --> 46.0.2)
This commit is contained in:
taz-il
2023-09-24 09:28:19 +02:00
committed by gpatel-fr
parent b19f6f3743
commit dbd8cdb86a
3 changed files with 45 additions and 36 deletions
@@ -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<ICommandLineArgument> 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<IFileEntry> 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)
{
@@ -35,8 +35,11 @@
<AssemblyOriginatorKeyFile>Duplicati.snk</AssemblyOriginatorKeyFile>
</PropertyGroup>
<ItemGroup>
<Reference Include="FluentFTP, Version=27.1.1.0, Culture=neutral, PublicKeyToken=f4af092b1d8df44f, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\FluentFTP.27.1.1\lib\net45\FluentFTP.dll</HintPath>
<Reference Include="FluentFTP, Version=46.0.2.0, Culture=neutral, PublicKeyToken=f4af092b1d8df44f, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\FluentFTP.46.0.2\lib\net462\FluentFTP.dll</HintPath>
</Reference>
<Reference Include="Microsoft.Extensions.Logging.Abstractions, Version=2.1.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\Microsoft.Extensions.Logging.Abstractions.2.1.0\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
@@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="FluentFTP" version="27.1.1" targetFramework="net471" />
</packages>
<package id="FluentFTP" version="46.0.2" targetFramework="net471" />
<package id="Microsoft.Extensions.Logging.Abstractions" version="2.1.0" targetFramework="net471" />
</packages>