AsperaDiagnosticsService
class AsperaDiagnosticsService
using Polly;
using Relativity.Transfer.Aspera.Resources;
using Relativity.Transfer.Dto;
using Renci.SshNet;
using Renci.SshNet.Common;
using System;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
namespace Relativity.Transfer.Aspera
{
internal class AsperaDiagnosticsService
{
public const int DefaultMaxTimeoutSeconds = 120;
public const string AsperaLogFileName = "aspera-scp-transfer.log";
private const int SuccessfulExitCode = 0;
private readonly AsperaClientConfiguration configuration;
private readonly DiagnosticsContext diagnosticContext;
private readonly IFileSystemService fileSystemService;
private readonly RelativityConnectionInfo relativityConnectionInfo;
private readonly AsperaCredential asperaCredential;
private readonly ITransferLog transferLog;
public int MaxTimeoutSeconds { get; set; }
public AsperaDiagnosticsService(RelativityConnectionInfo relativityConnectionInfo, AsperaClientConfiguration configuration, DiagnosticsContext context, IFileSystemService fileSystemService, ITransferLog log)
{
if (relativityConnectionInfo == null)
throw new ArgumentNullException("relativityConnectionInfo");
if (configuration == null)
throw new ArgumentNullException("configuration");
if (context == null)
throw new ArgumentNullException("context");
if (fileSystemService == null)
throw new ArgumentNullException("fileSystemService");
if (log == null)
throw new ArgumentNullException("log");
MaxTimeoutSeconds = 120;
this.relativityConnectionInfo = relativityConnectionInfo;
this.configuration = configuration;
diagnosticContext = context;
this.fileSystemService = fileSystemService;
transferLog = log;
asperaCredential = new AsperaCredential(configuration.Credential);
}
public Task<ISupportCheckResult> TestConnectivityAsync(CancellationToken token)
{
return TestConnectivityAsync(configuration.TestConnectionDestinationPath, token);
}
public Task<ISupportCheckResult> TestConnectivityAsync(string targetPath, CancellationToken token)
{
if (string.IsNullOrEmpty(targetPath))
throw new ArgumentNullException("targetPath");
return Task.Run((Func<Task<ISupportCheckResult>>)async delegate {
using (TempDirectory tempDirectory = new TempDirectory()) {
SupportCheckResult result = new SupportCheckResult();
tempDirectory.Create();
string fileName = GetTestUploadFileName();
TransferPath path = new TransferPath {
SourcePath = CreateTestUploadFile(tempDirectory, fileName),
TargetPath = targetPath,
Direction = TransferDirection.Upload
};
await PerformZeroByteTransferAsync(tempDirectory, path, result, false, token).ConfigureAwait(false);
if (string.IsNullOrEmpty(result.ErrorMessage)) {
TransferPath path2 = new TransferPath {
SourcePath = PathHelper.CombineUnix(targetPath, fileName),
TargetPath = tempDirectory.Directory,
Direction = TransferDirection.Download
};
await PerformZeroByteTransferAsync(tempDirectory, path2, result, true, token).ConfigureAwait(false);
if (string.IsNullOrEmpty(result.ErrorMessage)) {
string text = Path.Combine(tempDirectory.Directory, fileName);
if (!File.Exists(text)) {
LogInformation($"""{text}""");
result.IsSupported = false;
result.ErrorMessage = string.Format(CultureInfo.CurrentCulture, AsperaStrings.AsperaTestConnectionFileNotFoundExceptionMessage, GetHostName());
}
} else
TestPorts(result, token);
} else
TestPorts(result, token);
return result;
}
}, token);
}
public Task<ISupportCheckResult> TestPortsAsync(CancellationToken token)
{
SupportCheckResult result = new SupportCheckResult();
return Task.Run((Func<ISupportCheckResult>)delegate {
TestPorts(result, token);
return result;
}, token);
}
public Task<ISupportCheckResult> TestFaspUploadAsync(TempDirectory tempDirectory, string sourcePath, string destinationPath, CancellationToken token)
{
return Task.Run((Func<Task<ISupportCheckResult>>)async delegate {
SupportCheckResult result = new SupportCheckResult();
TransferPath path = new TransferPath {
Direction = TransferDirection.Upload,
SourcePath = sourcePath,
TargetPath = destinationPath
};
await PerformZeroByteTransferAsync(tempDirectory, path, result, false, token).ConfigureAwait(false);
return result;
}, token);
}
public Task<ISupportCheckResult> TestFaspDownloadAsync(TempDirectory tempDirectory, string sourcePath, string destinationPath, CancellationToken token)
{
return Task.Run((Func<Task<ISupportCheckResult>>)async delegate {
SupportCheckResult result = new SupportCheckResult();
TransferPath path = new TransferPath {
Direction = TransferDirection.Download,
SourcePath = sourcePath,
TargetPath = destinationPath
};
await PerformZeroByteTransferAsync(tempDirectory, path, result, true, token).ConfigureAwait(false);
return result;
}, token);
}
private static string GetTestUploadFileName()
{
return $"""{Environment.MachineName.ToUpperInvariant()}""{Environment.UserName.ToUpperInvariant()}""";
}
private static string CreateTestUploadFile(TempDirectory tempDirectory, string fileName)
{
string text = Path.Combine(tempDirectory.Directory, fileName);
using (FileStream fileStream = new FileStream(text, FileMode.CreateNew)) {
fileStream.Seek(8192, SeekOrigin.Begin);
fileStream.WriteByte(13);
fileStream.Flush(true);
return text;
}
}
private string GetRemoteTestUploadFilePath(TransferPath path)
{
if (path.Direction == TransferDirection.Upload) {
string fileName = fileSystemService.GetFileName(path.SourcePath);
return PathHelper.CombineUnix(path.TargetPath, fileName);
}
return path.SourcePath;
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "This is done by design. The result includes the error message.")]
private async Task PerformZeroByteTransferAsync(TempDirectory tempDirectory, TransferPath path, SupportCheckResult result, bool considerDeleteTestfile, CancellationToken token)
{
if (!token.IsCancellationRequested) {
Uri hostName = GetHostName();
string accountUserName = GetAccountUserName();
LogInformation($"""{path.Direction}""{hostName}""{configuration.TcpPort}""{configuration.UdpPortStartRange}");
StringBuilder commandLine = new StringBuilder();
commandLine.AppendFormat(" -P {0}", configuration.TcpPort);
commandLine.AppendFormat(" -O {0}", configuration.UdpPortStartRange);
commandLine.Append(" --policy=fair");
commandLine.Append(" -Q");
commandLine.Append(" -T");
commandLine.Append(" -d");
commandLine.Append(" -p");
commandLine.Append(" -q");
commandLine.Append(" -c aes256");
commandLine.AppendFormat(" -l {0}", configuration.TargetDataRateMbps);
commandLine.AppendFormat(" -L {0}", tempDirectory.Directory);
switch (path.Direction) {
case TransferDirection.Upload:
commandLine.Append(" --no-write");
break;
case TransferDirection.Download:
commandLine.Append(" --no-read");
break;
}
commandLine.AppendFormat(" --mode={0}", (path.Direction == TransferDirection.Upload) ? "send" : "recv");
commandLine.AppendFormat(" --user={0}", accountUserName);
commandLine.AppendFormat(" --host={0}", hostName);
commandLine.AppendFormat(" \"{0}\"", path.SourcePath);
commandLine.AppendFormat(" \"{0}\"", path.TargetPath);
AsperaRuntime runtime = new AsperaRuntime();
runtime.Install(configuration);
int faspExitCode = 0;
try {
faspExitCode = await RetryTResultSyntaxAsync.WaitAndRetryAsync<int>(Policy.HandleResult<int>((Func<int, bool>)((int x) => x != 0)).Or<Exception>(), 2, (Func<int, TimeSpan>)((int retryAttempt) => TimeSpan.FromSeconds(Math.Pow(2, (double)retryAttempt))), (Action<DelegateResult<int>, TimeSpan, Context>)delegate(DelegateResult<int> exception, TimeSpan timespan, Context context) {
LogError($"""{timespan}""", exception.get_Exception());
}).ExecuteAsync((Func<CancellationToken, Task<int>>)((CancellationToken cancellationToken) => Task.FromResult(ExecProcess(path.Direction, commandLine, runtime.Paths, tempDirectory, result))), token).ConfigureAwait(false);
} finally {
if (faspExitCode == 0) {
if (path.Direction == TransferDirection.Upload)
await TryVerifyUploadAsync(path, result, token).ConfigureAwait(false);
if (considerDeleteTestfile)
await TryDeleteTestFile(path, result, token).ConfigureAwait(false);
}
}
}
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "This is done by design. The result includes the error message.")]
private void TestSshPort(SupportCheckResult result, CancellationToken token)
{
if (!token.IsCancellationRequested) {
Uri hostName = GetHostName();
int tcpPort = configuration.TcpPort;
string accountUserName = GetAccountUserName();
string accountPassword = GetAccountPassword();
PasswordConnectionInfo val = new PasswordConnectionInfo(hostName.ToString(), tcpPort, accountUserName, accountPassword);
try {
val.set_Timeout(TimeSpan.FromSeconds((double)MaxTimeoutSeconds));
LogInformation($"""{tcpPort}""{hostName}""");
SshClient val2 = new SshClient(val);
try {
val2.Connect();
LogInformation($"""{tcpPort}""{hostName}""");
result.IsSupported = true;
} catch (Exception ex) {
string message = $"""{tcpPort}""{hostName}""";
result.IsSupported = false;
result.ErrorMessage = string.Format(CultureInfo.CurrentCulture, AsperaStrings.AsperaTestConnectionFailedSshPortMessage, hostName, tcpPort);
object obj = (object)(ex as SshAuthenticationException);
SocketException ex2 = ex as SocketException;
if (obj != null) {
message = $"""{tcpPort}""{hostName}""";
result.IsSupported = false;
result.ErrorMessage = string.Format(CultureInfo.CurrentCulture, AsperaStrings.AsperaTestConnectionAuthenticationMessage, hostName, tcpPort);
} else if (ex2 != null) {
result.IsSupported = false;
switch (ex2.SocketErrorCode) {
case SocketError.ConnectionRefused:
result.ErrorMessage = string.Format(CultureInfo.CurrentCulture, AsperaStrings.AsperaTestConnectionFailedInvalidPortOrBlockedMessage, hostName, tcpPort);
break;
case SocketError.TimedOut:
result.ErrorMessage = string.Format(CultureInfo.CurrentCulture, AsperaStrings.AsperaTestConnectionFailedInvalidPortOrHostOrServerDownMessage, hostName, tcpPort);
break;
default:
result.ErrorMessage = string.Format(CultureInfo.CurrentCulture, AsperaStrings.AsperaTestConnectionFailedSshPortMessage, hostName, tcpPort);
break;
}
}
LogError(message, ex);
} finally {
((IDisposable)val2)?.Dispose();
}
} finally {
((IDisposable)val)?.Dispose();
}
}
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "This is done by design. The result includes the error message.")]
private void TestUdpPort(SupportCheckResult result, CancellationToken token)
{
if (!token.IsCancellationRequested) {
Uri host = asperaCredential.Host;
int udpPortStartRange = configuration.UdpPortStartRange;
LogInformation($"""{udpPortStartRange}""{host}""");
UdpClient udpClient = new UdpClient();
try {
udpClient.Client.ReceiveTimeout = MaxTimeoutSeconds * 1000;
udpClient.Connect(host.ToString(), udpPortStartRange);
LogInformation($"""{udpPortStartRange}""{host}""");
} catch (Exception) {
result.IsSupported = false;
result.ErrorMessage = string.Format(CultureInfo.CurrentCulture, AsperaStrings.AsperaTestConnectionFailedUdpPortMessage, GetHostName(), GetUdpPortRangeString());
} finally {
udpClient.Close();
}
}
}
private void TestPorts(SupportCheckResult result, CancellationToken token)
{
TestSshPort(result, token);
if (result.IsSupported && !token.IsCancellationRequested) {
TestUdpPort(result, token);
if (result.IsSupported && !token.IsCancellationRequested)
result.IsSupported = true;
}
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "This is an expectations for diagnostics.")]
private int ExecProcess(TransferDirection direction, StringBuilder commandLine, AsperaRuntimePaths paths, TempDirectory tempDirectory, SupportCheckResult result)
{
DataReceivedEventHandler value = delegate(object sender, DataReceivedEventArgs args) {
if (!string.IsNullOrEmpty(args.Data))
result.AddStandardOutput(args.Data);
};
DataReceivedEventHandler value2 = delegate(object sender, DataReceivedEventArgs args) {
if (!string.IsNullOrEmpty(args.Data))
result.AddStandardError(args.Data);
};
using (Process process = new Process())
try {
string accountPassword = GetAccountPassword();
ProcessStartInfo processStartInfo = new ProcessStartInfo();
processStartInfo.EnvironmentVariables["ASPERA_SCP_PASS"] = accountPassword;
processStartInfo.EnvironmentVariables["ASPERA_SCP_COOKIE"] = AsperaFaspService.EncodeCookieString(string.Format("{0}-connection-check", "transfer-api"), Guid.NewGuid());
processStartInfo.FileName = paths.AsperaEngineFile;
processStartInfo.Arguments = commandLine.ToString();
processStartInfo.CreateNoWindow = true;
processStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
processStartInfo.RedirectStandardError = true;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.UseShellExecute = false;
processStartInfo.WorkingDirectory = paths.BinDirectory;
process.StartInfo = processStartInfo;
process.EnableRaisingEvents = true;
process.OutputDataReceived += value;
process.ErrorDataReceived += value2;
LogInformation($"""{direction}""");
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
int num = 0;
try {
process.WaitForExit(MaxTimeoutSeconds * 1000);
if (!process.HasExited)
process.Kill();
num = process.ExitCode;
} catch (Exception ex) {
LogError($"""{direction}""{num}", ex);
throw new InvalidOperationException(AsperaStrings.AsperaTestConnectionProcessExceptionMessage, ex);
}
if (num == 0)
LogInformation($"""{direction}""{num}");
else
LogError($"""{direction}""{num}", (Exception)null);
string text = Path.Combine(tempDirectory.Directory, "aspera-scp-transfer.log");
if (!File.Exists(text))
throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, AsperaStrings.AsperaTestConnectionLogFileNotFoundExceptionMessage, text));
LogInformation($"""{text}""");
string[] array = File.ReadAllLines(text);
LogInformation($"""{array.Length}""");
string[] array2 = File.ReadAllLines(text);
foreach (string line in array2) {
result.AddLogFileLine(line);
}
if (num == 0) {
result.ErrorMessage = string.Empty;
result.IsSupported = true;
} else {
result.ErrorMessage = string.Format(CultureInfo.CurrentCulture, AsperaStrings.AsperaTestConnectionFaspFailedMessage, GetHostName(), configuration.TcpPort, GetUdpPortRangeString());
result.IsSupported = false;
}
return num;
} catch (InvalidOperationException exception) {
LogError($"""{direction}""", exception);
result.ErrorMessage = string.Format(CultureInfo.CurrentCulture, AsperaStrings.AsperaTestConnectionEnvironmentExceptionMessage, GetHostName());
result.IsSupported = false;
return -1;
} catch (Exception exception2) {
LogError($"""{direction}""", exception2);
result.ErrorMessage = string.Format(CultureInfo.CurrentCulture, AsperaStrings.AsperaTestConnectionExceptionMessage, GetHostName());
result.IsSupported = false;
return -1;
} finally {
process.OutputDataReceived -= value;
process.ErrorDataReceived -= value2;
}
}
private string GetUdpPortRangeString()
{
return string.Format(CultureInfo.InvariantCulture, "{0}-{1}", configuration.UdpPortStartRange, configuration.UdpPortEndRange);
}
private void LogError(string message, Exception exception = null)
{
transferLog.LogError(exception, "Aspera Test Connection - {Message}", message);
}
private void LogError(string message, AsperaErrorDto error)
{
transferLog.LogError("Aspera Test Connection - {Message} - Aspera error: Code={Code}, Reason={Reason}, UserMessage={UserMessage}", message, error.Code, error.Reason, error.UserMessage);
}
private void LogInformation(string message)
{
transferLog.LogInformation("Aspera Test Connection - {Message}", message);
}
private string GetAccountUserName()
{
string text = asperaCredential.AccountUserName.UnprotectData();
if (!string.IsNullOrEmpty(text))
return text;
throw new InvalidOperationException(AsperaStrings.AsperaTestConnectionUserNameExceptionMessage);
}
private string GetAccountPassword()
{
string text = asperaCredential.AccountPassword.UnprotectData();
if (!string.IsNullOrEmpty(text))
return text;
throw new InvalidOperationException(AsperaStrings.AsperaTestConnectionPasswordExceptionMessage);
}
private Uri GetHostName()
{
Uri host = asperaCredential.Host;
if (host != (Uri)null)
return host;
throw new InvalidOperationException(AsperaStrings.AsperaTestConnectionHostExceptionMessage);
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "This is OK for a try-based method.")]
private async Task TryVerifyUploadAsync(TransferPath testFilePath, SupportCheckResult result, CancellationToken token)
{
if (diagnosticContext.Configuration.UploadValidation)
try {
string unixTargetPath = GetRemoteTestUploadFilePath(testFilePath);
RelativityFileShareBase fileShare = await GetFileShare(result, token).ConfigureAwait(false);
if (string.IsNullOrEmpty(result.ErrorMessage)) {
AsperaNodeService asperaNodeService = CreateAsperaNodeService(fileShare, result);
if (string.IsNullOrEmpty(result.ErrorMessage)) {
string fileName = fileSystemService.GetFileName(testFilePath.SourcePath);
BrowseDirectoryResponseDto browseDirectoryResponseDto = await asperaNodeService.GetFileExistsAsync(testFilePath.TargetPath, fileName, token).ConfigureAwait(false);
if (browseDirectoryResponseDto.Error != null) {
LogError($"""{unixTargetPath}""{fileShare.DocRoot}", browseDirectoryResponseDto.Error);
string message = string.Format(CultureInfo.CurrentCulture, AsperaStrings.AsperaTestConnectionNodeExistsErrorExceptionMessage, unixTargetPath, fileShare.Name);
result.IsSupported = false;
result.ErrorMessage = AsperaErrorHelper.GetDisplayErrorMessage(browseDirectoryResponseDto.Error, message);
} else {
if (browseDirectoryResponseDto.ItemCount > 0)
LogInformation("The Node API file exists check operation is successful.");
else {
LogError($"""{unixTargetPath}""{fileShare.DocRoot}", (Exception)null);
result.IsSupported = false;
result.ErrorMessage = string.Format(CultureInfo.CurrentCulture, AsperaStrings.AsperaTestConnectionNodeFileNotFoundExceptionMessage, unixTargetPath, fileShare.Name);
}
if (diagnosticContext.Configuration.DirectFileExistCheck) {
if (fileSystemService.FileExists(fileSystemService.Combine(testFilePath.TargetPath, fileName)))
LogInformation("The file system service API exists check operation is successful.");
else {
LogError($"""{unixTargetPath}""{fileShare.DocRoot}", (Exception)null);
result.IsSupported = false;
result.ErrorMessage = string.Format(CultureInfo.CurrentCulture, AsperaStrings.AsperaTestConnectionNodeFileNotFoundExceptionMessage, unixTargetPath, fileShare.Name);
}
}
}
}
}
} catch (Exception ex) {
LogError("The Node API file exists check operation experienced an unexpected error.", ex);
result.IsSupported = false;
result.ErrorMessage = string.Format(CultureInfo.CurrentCulture, AsperaStrings.AsperaTestConnectionFileExistsNodeFailedExceptionMessage, asperaCredential.Host, ex.Message);
}
}
[SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "This is OK for a try-based method.")]
private async Task TryDeleteTestFile(TransferPath testFilePath, SupportCheckResult result, CancellationToken token)
{
if (diagnosticContext.Configuration.DeleteUploadTestFiles)
try {
RelativityFileShareBase fileShare = await GetFileShare(result, token).ConfigureAwait(false);
if (string.IsNullOrEmpty(result.ErrorMessage)) {
AsperaNodeService asperaNodeService = CreateAsperaNodeService(fileShare, result);
if (string.IsNullOrEmpty(result.ErrorMessage)) {
string unixTargetPath = GetRemoteTestUploadFilePath(testFilePath);
DeletePathResponseDto deletePathResponseDto = await asperaNodeService.DeleteFileAsync(unixTargetPath, token).ConfigureAwait(false);
if (deletePathResponseDto.Error != null) {
LogError($"""{unixTargetPath}""{fileShare.DocRoot}", deletePathResponseDto.Error);
string message = string.Format(CultureInfo.CurrentCulture, AsperaStrings.AsperaTestConnectionNodeDeleteFileErrorExceptionMessage, unixTargetPath, fileShare.Name);
result.IsSupported = false;
result.ErrorMessage = AsperaErrorHelper.GetDisplayErrorMessage(deletePathResponseDto.Error, message);
} else if (deletePathResponseDto.Paths.Count < 1) {
LogError($"""{unixTargetPath}""{fileShare.DocRoot}", (Exception)null);
result.IsSupported = false;
result.ErrorMessage = string.Format(CultureInfo.CurrentCulture, AsperaStrings.AsperaTestConnectionNodeDeleteFileErrorExceptionMessage, unixTargetPath, fileShare.Name);
} else {
LogInformation("The Node API delete test file operation is successful.");
}
}
}
} catch (Exception ex) {
LogError("The Node API delete test file operation experienced an unexpected error.", ex);
result.IsSupported = false;
result.ErrorMessage = string.Format(CultureInfo.CurrentCulture, AsperaStrings.AsperaTestConnectionDeleteFileNodeFailedExceptionMessage, asperaCredential.Host, ex.Message);
}
}
[SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "DocRoot", Justification = "This is an accepted term.")]
private AsperaNodeService (RelativityFileShareBase fileShare, SupportCheckResult result)
{
if (fileShare.NodeCredential == (AsperaCredential)null) {
LogError($"""{fileShare.Name}""{fileShare.DocRoot}", (Exception)null);
result.IsSupported = false;
result.ErrorMessage = string.Format(CultureInfo.CurrentCulture, AsperaStrings.AsperaTestConnectionNodeCredentialNotFoundExceptionMessage, fileShare.Name);
return null;
}
return new AsperaNodeService(fileShare.NodeCredential.HttpConnectionInfo, transferLog, configuration.MaxHttpRetryAttempts, configuration.HttpTimeoutSeconds);
}
private async Task<RelativityFileShareBase> (SupportCheckResult result, CancellationToken token)
{
FileStorageSearch fileStorageSearch = new FileStorageSearch(relativityConnectionInfo, transferLog, configuration.MaxHttpRetryAttempts, configuration.HttpTimeoutSeconds);
FileStorageSearchContext empty = FileStorageSearchContext.Empty;
if (relativityConnectionInfo.WorkspaceId > 0)
empty.WorkspaceId = relativityConnectionInfo.WorkspaceId;
if (configuration.TargetFileShare != null)
return configuration.TargetFileShare;
FileStorageSearchResults fileStorageSearchResults = await fileStorageSearch.SearchAsync(empty, token).ConfigureAwait(false);
RelativityFileShareBase relativityFileShareBase = (RelativityFileShareBase)(((object)fileStorageSearchResults.GetRelativityFileShare(asperaCredential)) ?? ((object)fileStorageSearchResults.GetRelativityBulkLoadFileShare(asperaCredential)));
if (relativityFileShareBase != null)
return relativityFileShareBase;
if (fileStorageSearchResults.InvalidFileShares.Count > 0 || fileStorageSearchResults.InvalidBulkLoadFileShare.Count > 0) {
string text = string.Join(",", (from x in fileStorageSearchResults.InvalidFileShares
select x.Name).Concat(from x in fileStorageSearchResults.InvalidBulkLoadFileShare
select x.Name));
LogError($"""{asperaCredential.Host}""{asperaCredential.AccountUserName.UnprotectData()}""{text}", (Exception)null);
result.IsSupported = false;
result.ErrorMessage = string.Format(CultureInfo.CurrentCulture, AsperaStrings.AsperaTestConnectionFileShareNotFoundAndInvalidFileSharesExceptionMessage, asperaCredential.Host, text);
return null;
}
LogError($"""{asperaCredential.Host}""{asperaCredential.AccountUserName.UnprotectData()}""", (Exception)null);
result.IsSupported = false;
result.ErrorMessage = string.Format(CultureInfo.CurrentCulture, AsperaStrings.AsperaTestConnectionFileShareNotFoundExceptionMessage, asperaCredential.Host);
return null;
}
}
}