diff --git a/help/Version History (Changelog).md b/help/Version History (Changelog).md
index 7adaf567..b9914586 100644
--- a/help/Version History (Changelog).md
+++ b/help/Version History (Changelog).md
@@ -1,4 +1,4 @@
-# 2024.04.02
+# 2024.04.05
## New Features
- During recovery or version deletion, real progress is shown
@@ -8,6 +8,11 @@
Main idea behind change is to avoid already mounted error. And use it recovery.
- Restoration speed was increased in 2 times for Windows.
+## Bug Fixes
+- FTPS MITM attacks. Be aware that if you had previously setupped FTPS with self-signed certificate, you need to do the following steps:
+a. Open task with FTPS in Edit mode,
+b. Click Save.
+
# 2024.03.28
## Bug Fixes
diff --git a/sources/BUtil.Core/BUtil.Core.csproj b/sources/BUtil.Core/BUtil.Core.csproj
index dfa3814e..b9a97ea3 100644
--- a/sources/BUtil.Core/BUtil.Core.csproj
+++ b/sources/BUtil.Core/BUtil.Core.csproj
@@ -26,7 +26,7 @@
-
+
diff --git a/sources/BUtil.Core/ConfigurationFileModels/V2/FtpsStorageSettingsV2.cs b/sources/BUtil.Core/ConfigurationFileModels/V2/FtpsStorageSettingsV2.cs
index 96a92dcd..8808407b 100644
--- a/sources/BUtil.Core/ConfigurationFileModels/V2/FtpsStorageSettingsV2.cs
+++ b/sources/BUtil.Core/ConfigurationFileModels/V2/FtpsStorageSettingsV2.cs
@@ -10,5 +10,6 @@ public class FtpsStorageSettingsV2 : IStorageSettingsV2
public string? Folder { get; set; }
public string User { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
+ public string TrustedCertificate { get; set; } = null!;
}
diff --git a/sources/BUtil.Core/Localization/Resources.Designer.cs b/sources/BUtil.Core/Localization/Resources.Designer.cs
index 5b1b988e..11b352cd 100644
--- a/sources/BUtil.Core/Localization/Resources.Designer.cs
+++ b/sources/BUtil.Core/Localization/Resources.Designer.cs
@@ -607,6 +607,15 @@ public static string Field_Version {
}
}
+ ///
+ /// Looks up a localized string similar to Deleting "{0}".
+ ///
+ public static string File_Deleting {
+ get {
+ return ResourceManager.GetString("File_Deleting", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Integrity verification script.ps1.
///
@@ -634,6 +643,15 @@ public static string File_List_Saving {
}
}
+ ///
+ /// Looks up a localized string similar to Moving "{0}" to "{1}".
+ ///
+ public static string File_Moving {
+ get {
+ return ResourceManager.GetString("File_Moving", resourceCulture);
+ }
+ }
+
///
/// Looks up a localized string similar to Saving "{0}".
///
diff --git a/sources/BUtil.Core/Localization/Resources.resx b/sources/BUtil.Core/Localization/Resources.resx
index 542c946b..19f6205c 100644
--- a/sources/BUtil.Core/Localization/Resources.resx
+++ b/sources/BUtil.Core/Localization/Resources.resx
@@ -658,4 +658,10 @@ See detailed description in the following link.
User is not specified.
+
+ Deleting "{0}"
+
+
+ Moving "{0}" to "{1}"
+
\ No newline at end of file
diff --git a/sources/BUtil.Core/Localization/Resources.ru.resx b/sources/BUtil.Core/Localization/Resources.ru.resx
index c7db4355..72f8f7c5 100644
--- a/sources/BUtil.Core/Localization/Resources.ru.resx
+++ b/sources/BUtil.Core/Localization/Resources.ru.resx
@@ -659,4 +659,10 @@ bin.
Пользователь не указан.
+
+ Удаление "{0}"
+
+
+ Перемещение "{0}" в "{1}"
+
\ No newline at end of file
diff --git a/sources/BUtil.Core/Storages/FtpsStorage.cs b/sources/BUtil.Core/Storages/FtpsStorage.cs
index e5aaef4a..d63d4ffc 100644
--- a/sources/BUtil.Core/Storages/FtpsStorage.cs
+++ b/sources/BUtil.Core/Storages/FtpsStorage.cs
@@ -5,6 +5,7 @@
using System;
using System.IO;
using System.Linq;
+using System.Net.Security;
using System.Security;
using System.Text.RegularExpressions;
@@ -14,8 +15,9 @@ class FtpsStorage : StorageBase
{
private readonly string? _normalizedFolder;
private readonly FtpClient _client;
+ private readonly bool _autodetectConnectionSettings;
- internal FtpsStorage(ILog log, FtpsStorageSettingsV2 settings)
+ internal FtpsStorage(ILog log, FtpsStorageSettingsV2 settings, bool autodetectConnectionSettings)
: base(log, settings)
{
if (string.IsNullOrWhiteSpace(Settings.Host))
@@ -30,9 +32,11 @@ internal FtpsStorage(ILog log, FtpsStorageSettingsV2 settings)
_normalizedFolder = NormalizeNullablePath(Settings.Folder);
_client = Mount();
+ _autodetectConnectionSettings = autodetectConnectionSettings;
}
private readonly object _uploadLock = new();
+
public override IStorageUploadResult Upload(string sourceFile, string relativeFileName)
{
lock (_uploadLock) // because we're limited by upload speed by Server and Internet
@@ -84,10 +88,34 @@ private FtpClient Mount()
var client = new FtpClient(Settings.Host, Settings.User, Settings.Password, Settings.Port);
client.Config.EncryptionMode = GetFtpEncryptionMode();
client.Config.ValidateAnyCertificate = true;
+ client.ValidateCertificate += OnClientValidateCertificate;
client.Connect();
return client;
}
+ private void OnClientValidateCertificate(FluentFTP.Client.BaseClient.BaseFtpClient control, FtpSslValidationEventArgs e)
+ {
+ if (e.PolicyErrors == SslPolicyErrors.None)
+ {
+ e.Accept = true;
+ return;
+ }
+
+ if (_autodetectConnectionSettings)
+ {
+ Settings.TrustedCertificate = e.Certificate.GetRawCertDataString();
+ }
+
+ if (e.Certificate.GetRawCertDataString() == Settings.TrustedCertificate)
+ {
+ e.Accept = true;
+ return;
+ }
+
+ Log.WriteLine(LoggingEvent.Error, $"Received certificate from FTPS server violates policy {e.PolicyErrors}. Connection will not be accepted. You're facing Man-in-the-middle attack.");
+ Log.WriteLine(LoggingEvent.Error, $"If you trust this certificate, open in BUtil this task in Edit mode and click Save. Application will record this certificate as trusted.");
+ }
+
private FtpEncryptionMode GetFtpEncryptionMode()
{
return Settings.Encryption switch
diff --git a/sources/BUtil.Core/Storages/StorageFactory.cs b/sources/BUtil.Core/Storages/StorageFactory.cs
index 68824bdf..77f74111 100644
--- a/sources/BUtil.Core/Storages/StorageFactory.cs
+++ b/sources/BUtil.Core/Storages/StorageFactory.cs
@@ -6,14 +6,14 @@ namespace BUtil.Core.Storages;
public class StorageFactory
{
- public static IStorage Create(ILog log, IStorageSettingsV2 storageSettings)
+ public static IStorage Create(ILog log, IStorageSettingsV2 storageSettings, bool autodetectConnectionSettings)
{
if (storageSettings is FolderStorageSettingsV2)
return new FailoverStorageWrapper(log, new FolderStorage(log, (FolderStorageSettingsV2)storageSettings));
else if (storageSettings is SambaStorageSettingsV2)
return new FailoverStorageWrapper(log, PlatformSpecificExperience.Instance.GetSmbStorage(log, (SambaStorageSettingsV2)storageSettings));
else if (storageSettings is FtpsStorageSettingsV2)
- return new FailoverStorageWrapper(log, new FtpsStorage(log, (FtpsStorageSettingsV2)storageSettings));
+ return new FailoverStorageWrapper(log, new FtpsStorage(log, (FtpsStorageSettingsV2)storageSettings, autodetectConnectionSettings));
else if (storageSettings is MtpStorageSettings)
{
var mtpStorage = PlatformSpecificExperience.Instance.GetMtpStorage(log, (MtpStorageSettings)storageSettings);
@@ -34,7 +34,7 @@ public static IStorage Create(ILog log, IStorageSettingsV2 storageSettings)
try
{
- using var storage = Create(log, storageSettings);
+ using var storage = Create(log, storageSettings, true);
return storage.Test();
}
catch (Exception ex)
diff --git a/sources/BUtil.Core/TasksTree/ImportMedia/ImportFilesTask.cs b/sources/BUtil.Core/TasksTree/ImportMedia/ImportFilesTask.cs
index 07cbe0f1..ed184e03 100644
--- a/sources/BUtil.Core/TasksTree/ImportMedia/ImportFilesTask.cs
+++ b/sources/BUtil.Core/TasksTree/ImportMedia/ImportFilesTask.cs
@@ -36,8 +36,8 @@ public override void Execute()
var importMediaFileService = new ImportMediaFileService();
var importMediaState = options.SkipAlreadyImportedFiles ? importMediaFileService.Load(_task.Name) ?? new ImportMediaState() : new ImportMediaState();
- var fromStorage = StorageFactory.Create(this.Log, options.From);
- var toStorage = StorageFactory.Create(this.Log, new FolderStorageSettingsV2 { DestinationFolder = options.DestinationFolder });
+ var fromStorage = StorageFactory.Create(this.Log, options.From, false);
+ var toStorage = StorageFactory.Create(this.Log, new FolderStorageSettingsV2 { DestinationFolder = options.DestinationFolder }, false);
var transformFileName = options.TransformFileName;
var fromStorageFiles = fromStorage.GetFiles(null, SearchOption.AllDirectories);
diff --git a/sources/BUtil.Core/TasksTree/IncrementalModel/StorageSpecificServicesIoc.cs b/sources/BUtil.Core/TasksTree/IncrementalModel/StorageSpecificServicesIoc.cs
index db976166..b001e25f 100644
--- a/sources/BUtil.Core/TasksTree/IncrementalModel/StorageSpecificServicesIoc.cs
+++ b/sources/BUtil.Core/TasksTree/IncrementalModel/StorageSpecificServicesIoc.cs
@@ -21,11 +21,11 @@ public class StorageSpecificServicesIoc : IDisposable
private readonly Lazy _incrementalBackupFileService;
public IncrementalBackupFileService IncrementalBackupFileService { get { return _incrementalBackupFileService.Value; } }
- public StorageSpecificServicesIoc(ILog log, IStorageSettingsV2 storageSettings, IHashService hashService)
+ public StorageSpecificServicesIoc(ILog log, IStorageSettingsV2 storageSettings, IHashService hashService, bool autodetectConnectionSettings = false)
{
Log = log;
StorageSettings = storageSettings;
- _storage = new Lazy(() => StorageFactory.Create(log, storageSettings));
+ _storage = new Lazy(() => StorageFactory.Create(log, storageSettings, autodetectConnectionSettings));
_incrementalBackupStateService = new Lazy(() => new IncrementalBackupStateService(this, hashService));
_incrementalBackupFileService = new Lazy(() => new IncrementalBackupFileService(hashService, this));
}
diff --git a/sources/BUtil.Core/TasksTree/Storage/DeleteStorageFileTask.cs b/sources/BUtil.Core/TasksTree/Storage/DeleteStorageFileTask.cs
index ee35ff39..cf8195e9 100644
--- a/sources/BUtil.Core/TasksTree/Storage/DeleteStorageFileTask.cs
+++ b/sources/BUtil.Core/TasksTree/Storage/DeleteStorageFileTask.cs
@@ -1,4 +1,5 @@
using BUtil.Core.Events;
+using BUtil.Core.Localization;
using BUtil.Core.TasksTree.Core;
using BUtil.Core.TasksTree.IncrementalModel;
using System;
@@ -14,7 +15,7 @@ public DeleteStorageFileTask(
StorageSpecificServicesIoc services,
TaskEvents events,
string relativeFileName) :
- base(services.Log, events, $"Delete storage file \"{relativeFileName}\"")
+ base(services.Log, events, string.Format(Resources.File_Deleting, relativeFileName))
{
_services = services;
_relativeFileName = relativeFileName;
diff --git a/sources/BUtil.Core/TasksTree/Storage/MoveStorageFileTask.cs b/sources/BUtil.Core/TasksTree/Storage/MoveStorageFileTask.cs
index 355dc1fa..d72a217d 100644
--- a/sources/BUtil.Core/TasksTree/Storage/MoveStorageFileTask.cs
+++ b/sources/BUtil.Core/TasksTree/Storage/MoveStorageFileTask.cs
@@ -1,4 +1,5 @@
using BUtil.Core.Events;
+using BUtil.Core.Localization;
using BUtil.Core.TasksTree.Core;
using BUtil.Core.TasksTree.IncrementalModel;
using System;
@@ -16,7 +17,7 @@ public MoveStorageFileTask(
TaskEvents events,
string fromRelativeFileName,
string toRelativeFileName) :
- base(services.Log, events, $"Move storage file \"{fromRelativeFileName}\" to \"{toRelativeFileName}\"")
+ base(services.Log, events, string.Format(Resources.File_Moving, fromRelativeFileName, toRelativeFileName))
{
_services = services;
_fromRelativeFileName = fromRelativeFileName;
diff --git a/sources/BUtil.Core/TasksTree/Storage/WriteStorageFilesToSourceFileTask.cs b/sources/BUtil.Core/TasksTree/Storage/WriteStorageFilesToSourceFileTask.cs
index 05bb1fcf..721502c6 100644
--- a/sources/BUtil.Core/TasksTree/Storage/WriteStorageFilesToSourceFileTask.cs
+++ b/sources/BUtil.Core/TasksTree/Storage/WriteStorageFilesToSourceFileTask.cs
@@ -23,7 +23,7 @@ public WriteStorageFilesToSourceFileTask(ILog log, TaskEvents events,
: base(log, events, Resources.Task_Restore)
{
_commonServicesIoc = new CommonServicesIoc();
- _storageSpecificServicesIoc = new StorageSpecificServicesIoc(log, storageSettings, _commonServicesIoc.HashService);
+ _storageSpecificServicesIoc = new StorageSpecificServicesIoc(log, storageSettings, _commonServicesIoc.HashService, true);
Children = storageFiles
.Select(x => new WriteStorageFileToSourceFileTask(_storageSpecificServicesIoc, events, sourceItem, x, destinationFolder))
diff --git a/sources/BUtil.Tests/BUtil.Tests.csproj b/sources/BUtil.Tests/BUtil.Tests.csproj
index 8610af0a..6bcf159d 100644
--- a/sources/BUtil.Tests/BUtil.Tests.csproj
+++ b/sources/BUtil.Tests/BUtil.Tests.csproj
@@ -10,13 +10,13 @@
-
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
-
+
+
diff --git a/sources/butil-ui.Desktop/Properties/launchSettings.json b/sources/butil-ui.Desktop/Properties/launchSettings.json
index 211c21a5..207f3135 100644
--- a/sources/butil-ui.Desktop/Properties/launchSettings.json
+++ b/sources/butil-ui.Desktop/Properties/launchSettings.json
@@ -1,8 +1,7 @@
{
"profiles": {
"butil-ui.Desktop": {
- "commandName": "Project",
- "commandLineArgs": "RestoreTask \"Task=👍👍👍check bug with same content, different last write time\""
+ "commandName": "Project"
}
}
}
\ No newline at end of file
diff --git a/sources/butil-ui/Controls/VersionsList/VersionsListView.axaml b/sources/butil-ui/Controls/VersionsList/VersionsListView.axaml
index d74a0e58..07c1b390 100644
--- a/sources/butil-ui/Controls/VersionsList/VersionsListView.axaml
+++ b/sources/butil-ui/Controls/VersionsList/VersionsListView.axaml
@@ -14,7 +14,7 @@
-
+