Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/duplicati/duplicati.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.github/workflows/tests.yml6
-rw-r--r--Duplicati.sln8
-rw-r--r--Duplicati/CommandLine/BackendTester/Duplicati.CommandLine.BackendTester.csproj6
-rw-r--r--Duplicati/CommandLine/BackendTool/Duplicati.CommandLine.BackendTool.csproj6
-rw-r--r--Duplicati/CommandLine/Duplicati.CommandLine.csproj10
-rw-r--r--Duplicati/CommandLine/RecoveryTool/Duplicati.CommandLine.RecoveryTool.csproj8
-rw-r--r--Duplicati/GUI/Duplicati.GUI.TrayIcon/Duplicati.GUI.TrayIcon.csproj6
-rw-r--r--Duplicati/Library/Backend/AlternativeFTP/AlternativeFTPBackend.cs8
-rw-r--r--Duplicati/Library/Backend/AzureBlob/AzureBlobBackend.cs4
-rw-r--r--Duplicati/Library/Backend/Backblaze/B2.cs20
-rw-r--r--Duplicati/Library/Backend/Box/BoxBackend.cs4
-rw-r--r--Duplicati/Library/Backend/CloudFiles/CloudFiles.cs4
-rw-r--r--Duplicati/Library/Backend/Dropbox/Dropbox.cs4
-rw-r--r--Duplicati/Library/Backend/FTP/FTPBackend.cs4
-rw-r--r--Duplicati/Library/Backend/GoogleServices/GoogleCloudStorage.cs4
-rw-r--r--Duplicati/Library/Backend/GoogleServices/GoogleDrive.cs4
-rw-r--r--Duplicati/Library/Backend/Jottacloud/Jottacloud.cs4
-rw-r--r--Duplicati/Library/Backend/Mega/Duplicati.Library.Backend.Mega.csproj7
-rw-r--r--Duplicati/Library/Backend/Mega/MegaBackend.cs17
-rw-r--r--Duplicati/Library/Backend/Mega/Strings.cs4
-rw-r--r--Duplicati/Library/Backend/Mega/packages.config5
-rw-r--r--Duplicati/Library/Backend/OpenStack/OpenStackStorage.cs4
-rw-r--r--Duplicati/Library/Backend/S3/S3Backend.cs4
-rw-r--r--Duplicati/Library/Backend/SSHv2/SSHv2Backend.cs4
-rw-r--r--Duplicati/Library/Backend/SharePoint/SharePointBackend.cs4
-rw-r--r--Duplicati/Library/Backend/Storj/Duplicati.Library.Backend.Storj.csproj128
-rw-r--r--Duplicati/Library/Backend/Storj/Properties/AssemblyInfo.cs37
-rw-r--r--Duplicati/Library/Backend/Storj/StorjBackend.cs368
-rw-r--r--Duplicati/Library/Backend/Storj/StorjConfig.cs (renamed from Duplicati/Library/Backend/Tardigrade/TardigradeConfig.cs)18
-rw-r--r--Duplicati/Library/Backend/Storj/StorjFile.cs (renamed from Duplicati/Library/Backend/Tardigrade/TardigradeFile.cs)18
-rw-r--r--Duplicati/Library/Backend/Storj/Strings.cs30
-rw-r--r--Duplicati/Library/Backend/Storj/libstorj_uplink.dylib (renamed from Duplicati/Library/Backend/Tardigrade/libstorj_uplink.dylib)bin16654056 -> 15742632 bytes
-rw-r--r--Duplicati/Library/Backend/Storj/libstorj_uplink.so (renamed from Duplicati/Library/Backend/Tardigrade/libstorj_uplink.so)bin17074528 -> 15696192 bytes
-rw-r--r--Duplicati/Library/Backend/Storj/packages.config13
-rw-r--r--Duplicati/Library/Backend/Storj/win-x64/storj_uplink.dll (renamed from Duplicati/Library/Backend/Tardigrade/win-x64/storj_uplink.dll)bin10642432 -> 9741824 bytes
-rw-r--r--Duplicati/Library/Backend/Storj/win-x86/storj_uplink.dll (renamed from Duplicati/Library/Backend/Tardigrade/win-x86/storj_uplink.dll)bin9273344 -> 8320000 bytes
-rw-r--r--Duplicati/Library/Backend/TahoeLAFS/TahoeBackend.cs4
-rw-r--r--Duplicati/Library/Backend/Tardigrade/Duplicati.Library.Backend.Tardigrade.csproj59
-rw-r--r--Duplicati/Library/Backend/Tardigrade/Properties/AssemblyInfo.cs2
-rw-r--r--Duplicati/Library/Backend/Tardigrade/Strings.cs19
-rw-r--r--Duplicati/Library/Backend/Tardigrade/TardigradeBackend.cs327
-rw-r--r--Duplicati/Library/Backend/Tardigrade/packages.config11
-rw-r--r--Duplicati/Library/Backend/TencentCOS/COSBackend.cs4
-rw-r--r--Duplicati/Library/Backend/WEBDAV/WEBDAV.cs4
-rw-r--r--Duplicati/Library/Interface/CustomExceptions.cs16
-rw-r--r--Duplicati/Library/Main/Controller.cs8
-rw-r--r--Duplicati/Library/Main/Operation/BackupHandler.cs2
-rw-r--r--Duplicati/Library/Main/Operation/FilelistProcessor.cs8
-rw-r--r--Duplicati/Library/Modules/Builtin/Duplicati.Library.Modules.Builtin.csproj3
-rw-r--r--Duplicati/Library/Modules/Builtin/packages.config1
-rw-r--r--Duplicati/Library/Utility/FilterExpression.cs5
-rw-r--r--Duplicati/License/Duplicati.License.csproj26
-rw-r--r--Duplicati/Server/Database/ServerSettings.cs39
-rw-r--r--Duplicati/Server/Duplicati.Server.csproj9
-rw-r--r--Duplicati/Server/packages.config1
-rw-r--r--Duplicati/Server/webroot/ngax/scripts/services/EditUriBuiltins.js165
-rw-r--r--Duplicati/Server/webroot/ngax/scripts/services/SystemInfo.js1
-rw-r--r--Duplicati/Server/webroot/ngax/templates/backends/storj.html43
-rw-r--r--Duplicati/Server/webroot/ngax/templates/backends/tardigrade.html4
-rw-r--r--Duplicati/UnitTest/ProblematicPathTests.cs33
-rw-r--r--README.md4
-rw-r--r--thirdparty/BoncyCastle/Homepage.txt1
-rw-r--r--thirdparty/BoncyCastle/license.txt8
-rw-r--r--thirdparty/BoncyCastle/licensedata.json7
-rw-r--r--thirdparty/Otp.NET/Homepage.txt1
-rwxr-xr-xthirdparty/Otp.NET/license.txt21
-rw-r--r--thirdparty/Otp.NET/licensedata.json7
67 files changed, 1077 insertions, 537 deletions
diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml
index 37fca57bc..046fa2aa1 100644
--- a/.github/workflows/tests.yml
+++ b/.github/workflows/tests.yml
@@ -1,10 +1,6 @@
name: Tests
-on:
- push:
- branches: "*"
- pull_request:
- branches: "*"
+on: [push, pull_request]
env:
build_directory: "Duplicati/GUI/Duplicati.GUI.TrayIcon/bin/Release"
diff --git a/Duplicati.sln b/Duplicati.sln
index d162cb249..5f90df87f 100644
--- a/Duplicati.sln
+++ b/Duplicati.sln
@@ -101,10 +101,12 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.CommandLine.Confi
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Encryption", "Duplicati\Library\Encryption\Duplicati.Library.Encryption.csproj", "{2CF2D90E-C25B-47AD-91E0-98451BAB8058}"
EndProject
-Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.Tardigrade", "Duplicati\Library\Backend\Tardigrade\Duplicati.Library.Backend.Tardigrade.csproj", "{AE035E01-C917-4F13-A35E-78F21C1A2F17}"
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.Storj", "Duplicati\Library\Backend\Storj\Duplicati.Library.Backend.Storj.csproj", "{AE035E01-C917-4F13-A35E-78F21C1A2F17}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.TencentCOS", "Duplicati\Library\Backend\TencentCOS\Duplicati.Library.Backend.TencentCOS.csproj", "{545DD6D4-9476-42D6-B51C-A28E000C489E}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Backend.Tardigrade", "Duplicati\Library\Backend\Tardigrade\Duplicati.Library.Backend.Tardigrade.csproj", "{9A04CB37-DA72-4008-9703-3AC5191974E9}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -315,6 +317,10 @@ Global
{545DD6D4-9476-42D6-B51C-A28E000C489E}.Debug|Any CPU.Build.0 = Debug|Any CPU
{545DD6D4-9476-42D6-B51C-A28E000C489E}.Release|Any CPU.ActiveCfg = Release|Any CPU
{545DD6D4-9476-42D6-B51C-A28E000C489E}.Release|Any CPU.Build.0 = Release|Any CPU
+ {9A04CB37-DA72-4008-9703-3AC5191974E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {9A04CB37-DA72-4008-9703-3AC5191974E9}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {9A04CB37-DA72-4008-9703-3AC5191974E9}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {9A04CB37-DA72-4008-9703-3AC5191974E9}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/Duplicati/CommandLine/BackendTester/Duplicati.CommandLine.BackendTester.csproj b/Duplicati/CommandLine/BackendTester/Duplicati.CommandLine.BackendTester.csproj
index 721163ebb..69d035db8 100644
--- a/Duplicati/CommandLine/BackendTester/Duplicati.CommandLine.BackendTester.csproj
+++ b/Duplicati/CommandLine/BackendTester/Duplicati.CommandLine.BackendTester.csproj
@@ -86,8 +86,12 @@
<Project>{32a74526-3e5f-413a-8cb4-1efdad4c8b91}</Project>
<Name>Duplicati.Library.Backend.Sia</Name>
</ProjectReference>
- <ProjectReference Include="..\..\Library\Backend\Tardigrade\Duplicati.Library.Backend.Tardigrade.csproj">
+ <ProjectReference Include="..\..\Library\Backend\Storj\Duplicati.Library.Backend.Storj.csproj">
<Project>{ae035e01-c917-4f13-a35e-78f21c1a2f17}</Project>
+ <Name>Duplicati.Library.Backend.Storj</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\..\Library\Backend\Tardigrade\Duplicati.Library.Backend.Tardigrade.csproj">
+ <Project>{9a04cb37-da72-4008-9703-3ac5191974e9}</Project>
<Name>Duplicati.Library.Backend.Tardigrade</Name>
</ProjectReference>
<ProjectReference Include="..\..\Library\Backend\TencentCOS\Duplicati.Library.Backend.TencentCOS.csproj">
diff --git a/Duplicati/CommandLine/BackendTool/Duplicati.CommandLine.BackendTool.csproj b/Duplicati/CommandLine/BackendTool/Duplicati.CommandLine.BackendTool.csproj
index 694439e22..f344da03f 100644
--- a/Duplicati/CommandLine/BackendTool/Duplicati.CommandLine.BackendTool.csproj
+++ b/Duplicati/CommandLine/BackendTool/Duplicati.CommandLine.BackendTool.csproj
@@ -92,8 +92,12 @@
<Project>{C0270709-2A40-43B5-8CF1-69581B9FA2A1}</Project>
<Name>Duplicati.Library.Backend.TahoeLAFS</Name>
</ProjectReference>
- <ProjectReference Include="..\..\Library\Backend\Tardigrade\Duplicati.Library.Backend.Tardigrade.csproj">
+ <ProjectReference Include="..\..\Library\Backend\Storj\Duplicati.Library.Backend.Storj.csproj">
<Project>{ae035e01-c917-4f13-a35e-78f21c1a2f17}</Project>
+ <Name>Duplicati.Library.Backend.Storj</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\..\Library\Backend\Tardigrade\Duplicati.Library.Backend.Tardigrade.csproj">
+ <Project>{9a04cb37-da72-4008-9703-3ac5191974e9}</Project>
<Name>Duplicati.Library.Backend.Tardigrade</Name>
</ProjectReference>
<ProjectReference Include="..\..\Library\Backend\TencentCOS\Duplicati.Library.Backend.TencentCOS.csproj">
diff --git a/Duplicati/CommandLine/Duplicati.CommandLine.csproj b/Duplicati/CommandLine/Duplicati.CommandLine.csproj
index 83b3926dc..e6fc646ef 100644
--- a/Duplicati/CommandLine/Duplicati.CommandLine.csproj
+++ b/Duplicati/CommandLine/Duplicati.CommandLine.csproj
@@ -111,6 +111,10 @@
<Project>{32a74526-3e5f-413a-8cb4-1efdad4c8b91}</Project>
<Name>Duplicati.Library.Backend.Sia</Name>
</ProjectReference>
+ <ProjectReference Include="..\Library\Backend\Tardigrade\Duplicati.Library.Backend.Tardigrade.csproj">
+ <Project>{9a04cb37-da72-4008-9703-3ac5191974e9}</Project>
+ <Name>Duplicati.Library.Backend.Tardigrade</Name>
+ </ProjectReference>
<ProjectReference Include="..\Library\Compression\Duplicati.Library.Compression.csproj">
<Project>{19ECCE09-B5EB-406C-8C57-BAC66997D469}</Project>
<Name>Duplicati.Library.Compression</Name>
@@ -231,9 +235,9 @@
<Project>{D63E53E4-A458-4C2F-914D-92F715F58ACF}</Project>
<Name>Duplicati.Library.Common</Name>
</ProjectReference>
- <ProjectReference Include="..\Library\Backend\Tardigrade\Duplicati.Library.Backend.Tardigrade.csproj">
+ <ProjectReference Include="..\Library\Backend\Storj\Duplicati.Library.Backend.Storj.csproj">
<Project>{AE035E01-C917-4F13-A35E-78F21C1A2F17}</Project>
- <Name>Duplicati.Library.Backend.Tardigrade</Name>
+ <Name>Duplicati.Library.Backend.Storj</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
@@ -256,4 +260,4 @@
<Target Name="AfterBuild">
</Target>
-->
-</Project>
+</Project> \ No newline at end of file
diff --git a/Duplicati/CommandLine/RecoveryTool/Duplicati.CommandLine.RecoveryTool.csproj b/Duplicati/CommandLine/RecoveryTool/Duplicati.CommandLine.RecoveryTool.csproj
index 819aff870..a4d631b04 100644
--- a/Duplicati/CommandLine/RecoveryTool/Duplicati.CommandLine.RecoveryTool.csproj
+++ b/Duplicati/CommandLine/RecoveryTool/Duplicati.CommandLine.RecoveryTool.csproj
@@ -134,6 +134,10 @@
<Project>{C0270709-2A40-43B5-8CF1-69581B9FA2A1}</Project>
<Name>Duplicati.Library.Backend.TahoeLAFS</Name>
</ProjectReference>
+ <ProjectReference Include="..\..\Library\Backend\Tardigrade\Duplicati.Library.Backend.Tardigrade.csproj">
+ <Project>{9a04cb37-da72-4008-9703-3ac5191974e9}</Project>
+ <Name>Duplicati.Library.Backend.Tardigrade</Name>
+ </ProjectReference>
<ProjectReference Include="..\..\Library\Backend\WEBDAV\Duplicati.Library.Backend.WEBDAV.csproj">
<Project>{BAE27510-8B5D-44B2-B33E-372A98908041}</Project>
<Name>Duplicati.Library.Backend.WEBDAV</Name>
@@ -182,9 +186,9 @@
<Project>{D63E53E4-A458-4C2F-914D-92F715F58ACF}</Project>
<Name>Duplicati.Library.Common</Name>
</ProjectReference>
- <ProjectReference Include="..\..\Library\Backend\Tardigrade\Duplicati.Library.Backend.Tardigrade.csproj">
+ <ProjectReference Include="..\..\Library\Backend\Storj\Duplicati.Library.Backend.Storj.csproj">
<Project>{AE035E01-C917-4F13-A35E-78F21C1A2F17}</Project>
- <Name>Duplicati.Library.Backend.Tardigrade</Name>
+ <Name>Duplicati.Library.Backend.Storj</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup>
diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Duplicati.GUI.TrayIcon.csproj b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Duplicati.GUI.TrayIcon.csproj
index e5b3f7b72..df66bc6bc 100644
--- a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Duplicati.GUI.TrayIcon.csproj
+++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Duplicati.GUI.TrayIcon.csproj
@@ -217,8 +217,12 @@
<Project>{C0270709-2A40-43B5-8CF1-69581B9FA2A1}</Project>
<Name>Duplicati.Library.Backend.TahoeLAFS</Name>
</ProjectReference>
- <ProjectReference Include="..\..\Library\Backend\Tardigrade\Duplicati.Library.Backend.Tardigrade.csproj">
+ <ProjectReference Include="..\..\Library\Backend\Storj\Duplicati.Library.Backend.Storj.csproj">
<Project>{ae035e01-c917-4f13-a35e-78f21c1a2f17}</Project>
+ <Name>Duplicati.Library.Backend.Storj</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\..\Library\Backend\Tardigrade\Duplicati.Library.Backend.Tardigrade.csproj">
+ <Project>{9a04cb37-da72-4008-9703-3ac5191974e9}</Project>
<Name>Duplicati.Library.Backend.Tardigrade</Name>
</ProjectReference>
<ProjectReference Include="..\..\Library\Backend\TencentCOS\Duplicati.Library.Backend.TencentCOS.csproj">
diff --git a/Duplicati/Library/Backend/AlternativeFTP/AlternativeFTPBackend.cs b/Duplicati/Library/Backend/AlternativeFTP/AlternativeFTPBackend.cs
index 23d3a3f6c..3c99e48ac 100644
--- a/Duplicati/Library/Backend/AlternativeFTP/AlternativeFTPBackend.cs
+++ b/Duplicati/Library/Backend/AlternativeFTP/AlternativeFTPBackend.cs
@@ -30,8 +30,8 @@ using System.Security.Authentication;
using System.Threading;
using System.Threading.Tasks;
using CoreUtility = Duplicati.Library.Utility.Utility;
-using Uri = System.Uri;
-
+using Uri = System.Uri;
+
namespace Duplicati.Library.Backend.AlternativeFTP
{
// ReSharper disable once RedundantExtendsListEntry
@@ -370,11 +370,11 @@ namespace Duplicati.Library.Backend.AlternativeFTP
}
}
- public Task PutAsync(string remotename, string localname, CancellationToken cancelToken)
+ public async Task PutAsync(string remotename, string localname, CancellationToken cancelToken)
{
using (FileStream fs = File.Open(localname, FileMode.Open, FileAccess.Read, FileShare.Read))
{
- return PutAsync(remotename, fs, cancelToken);
+ await PutAsync(remotename, fs, cancelToken);
}
}
diff --git a/Duplicati/Library/Backend/AzureBlob/AzureBlobBackend.cs b/Duplicati/Library/Backend/AzureBlob/AzureBlobBackend.cs
index 18ab0cc92..becf985c9 100644
--- a/Duplicati/Library/Backend/AzureBlob/AzureBlobBackend.cs
+++ b/Duplicati/Library/Backend/AzureBlob/AzureBlobBackend.cs
@@ -101,12 +101,12 @@ namespace Duplicati.Library.Backend.AzureBlob
return _azureBlob.ListContainerEntries();
}
- public Task PutAsync(string remotename, string localname, CancellationToken cancelToken)
+ public async Task PutAsync(string remotename, string localname, CancellationToken cancelToken)
{
using (var fs = File.Open(localname,
FileMode.Open, FileAccess.Read, FileShare.Read))
{
- return PutAsync(remotename, fs, cancelToken);
+ await PutAsync(remotename, fs, cancelToken);
}
}
diff --git a/Duplicati/Library/Backend/Backblaze/B2.cs b/Duplicati/Library/Backend/Backblaze/B2.cs
index d1e811d92..241cd7821 100644
--- a/Duplicati/Library/Backend/Backblaze/B2.cs
+++ b/Duplicati/Library/Backend/Backblaze/B2.cs
@@ -30,10 +30,10 @@ namespace Duplicati.Library.Backend.Backblaze
public class B2 : IBackend, IStreamingBackend
{
private const string B2_ID_OPTION = "b2-accountid";
- private const string B2_KEY_OPTION = "b2-applicationkey";
- private const string B2_PAGESIZE_OPTION = "b2-page-size";
+ private const string B2_KEY_OPTION = "b2-applicationkey";
+ private const string B2_PAGESIZE_OPTION = "b2-page-size";
private const string B2_DOWNLOAD_URL_OPTION = "b2-download-url";
-
+
private const string B2_CREATE_BUCKET_TYPE_OPTION = "b2-create-bucket-type";
private const string DEFAULT_BUCKET_TYPE = "allPrivate";
@@ -95,9 +95,9 @@ namespace Duplicati.Library.Backend.Backblaze
if (string.IsNullOrEmpty(accountKey))
throw new UserInformationException(Strings.B2.NoB2KeyError, "B2MissingKey");
- m_helper = new B2AuthHelper(accountId, accountKey);
-
- m_pagesize = DEFAULT_PAGE_SIZE;
+ m_helper = new B2AuthHelper(accountId, accountKey);
+
+ m_pagesize = DEFAULT_PAGE_SIZE;
if (options.ContainsKey(B2_PAGESIZE_OPTION))
{
int.TryParse(options[B2_PAGESIZE_OPTION], out m_pagesize);
@@ -182,8 +182,8 @@ namespace Duplicati.Library.Backend.Backblaze
new CommandLineArgument(B2_KEY_OPTION, CommandLineArgument.ArgumentType.Password, Strings.B2.B2applicationkeyDescriptionShort, Strings.B2.B2applicationkeyDescriptionLong, null, new string[] {"auth-username"}, null),
new CommandLineArgument("auth-password", CommandLineArgument.ArgumentType.Password, Strings.B2.AuthPasswordDescriptionShort, Strings.B2.AuthPasswordDescriptionLong),
new CommandLineArgument("auth-username", CommandLineArgument.ArgumentType.String, Strings.B2.AuthUsernameDescriptionShort, Strings.B2.AuthUsernameDescriptionLong),
- new CommandLineArgument(B2_CREATE_BUCKET_TYPE_OPTION, CommandLineArgument.ArgumentType.String, Strings.B2.B2createbuckettypeDescriptionShort, Strings.B2.B2createbuckettypeDescriptionLong, DEFAULT_BUCKET_TYPE),
- new CommandLineArgument(B2_PAGESIZE_OPTION, CommandLineArgument.ArgumentType.Integer, Strings.B2.B2pagesizeDescriptionShort, Strings.B2.B2pagesizeDescriptionLong, DEFAULT_PAGE_SIZE.ToString()),
+ new CommandLineArgument(B2_CREATE_BUCKET_TYPE_OPTION, CommandLineArgument.ArgumentType.String, Strings.B2.B2createbuckettypeDescriptionShort, Strings.B2.B2createbuckettypeDescriptionLong, DEFAULT_BUCKET_TYPE),
+ new CommandLineArgument(B2_PAGESIZE_OPTION, CommandLineArgument.ArgumentType.Integer, Strings.B2.B2pagesizeDescriptionShort, Strings.B2.B2pagesizeDescriptionLong, DEFAULT_PAGE_SIZE.ToString()),
new CommandLineArgument(B2_DOWNLOAD_URL_OPTION, CommandLineArgument.ArgumentType.String, Strings.B2.B2downloadurlDescriptionShort, Strings.B2.B2downloadurlDescriptionLong),
});
@@ -362,10 +362,10 @@ namespace Duplicati.Library.Backend.Backblaze
).ToList();
}
- public Task PutAsync(string remotename, string filename, CancellationToken cancelToken)
+ public async Task PutAsync(string remotename, string filename, CancellationToken cancelToken)
{
using (System.IO.FileStream fs = System.IO.File.OpenRead(filename))
- return PutAsync(remotename, fs, cancelToken);
+ await PutAsync(remotename, fs, cancelToken);
}
public void Get(string remotename, string filename)
diff --git a/Duplicati/Library/Backend/Box/BoxBackend.cs b/Duplicati/Library/Backend/Box/BoxBackend.cs
index 03d6f6bb0..acc725d20 100644
--- a/Duplicati/Library/Backend/Box/BoxBackend.cs
+++ b/Duplicati/Library/Backend/Box/BoxBackend.cs
@@ -260,10 +260,10 @@ namespace Duplicati.Library.Backend.Box
select (IFileEntry)new FileEntry(n.Name, n.Size, n.ModifiedAt, n.ModifiedAt) { IsFolder = n.Type == "folder" };
}
- public Task PutAsync(string remotename, string filename, CancellationToken cancelToken)
+ public async Task PutAsync(string remotename, string filename, CancellationToken cancelToken)
{
using (System.IO.FileStream fs = System.IO.File.OpenRead(filename))
- return PutAsync(remotename, fs, cancelToken);
+ await PutAsync(remotename, fs, cancelToken);
}
public void Get(string remotename, string filename)
diff --git a/Duplicati/Library/Backend/CloudFiles/CloudFiles.cs b/Duplicati/Library/Backend/CloudFiles/CloudFiles.cs
index ece4510e7..f53c40e78 100644
--- a/Duplicati/Library/Backend/CloudFiles/CloudFiles.cs
+++ b/Duplicati/Library/Backend/CloudFiles/CloudFiles.cs
@@ -194,10 +194,10 @@ namespace Duplicati.Library.Backend
} while (repeat);
}
- public Task PutAsync(string remotename, string filename, CancellationToken cancelToken)
+ public async Task PutAsync(string remotename, string filename, CancellationToken cancelToken)
{
using (System.IO.FileStream fs = System.IO.File.OpenRead(filename))
- return PutAsync(remotename, fs, cancelToken);
+ await PutAsync(remotename, fs, cancelToken);
}
public void Get(string remotename, string filename)
diff --git a/Duplicati/Library/Backend/Dropbox/Dropbox.cs b/Duplicati/Library/Backend/Dropbox/Dropbox.cs
index 835deb38b..89ea8b863 100644
--- a/Duplicati/Library/Backend/Dropbox/Dropbox.cs
+++ b/Duplicati/Library/Backend/Dropbox/Dropbox.cs
@@ -107,10 +107,10 @@ namespace Duplicati.Library.Backend
}
}
- public Task PutAsync(string remotename, string filename, CancellationToken cancelToken)
+ public async Task PutAsync(string remotename, string filename, CancellationToken cancelToken)
{
using(FileStream fs = File.OpenRead(filename))
- return PutAsync(remotename, fs, cancelToken);
+ await PutAsync(remotename, fs, cancelToken);
}
public void Get(string remotename, string filename)
diff --git a/Duplicati/Library/Backend/FTP/FTPBackend.cs b/Duplicati/Library/Backend/FTP/FTPBackend.cs
index e5c2bbc2e..cabffc9dc 100644
--- a/Duplicati/Library/Backend/FTP/FTPBackend.cs
+++ b/Duplicati/Library/Backend/FTP/FTPBackend.cs
@@ -296,10 +296,10 @@ namespace Duplicati.Library.Backend
}
}
- public Task PutAsync(string remotename, string localname, CancellationToken cancelToken)
+ public async Task PutAsync(string remotename, string localname, CancellationToken cancelToken)
{
using (System.IO.FileStream fs = System.IO.File.Open(localname, System.IO.FileMode.Open, System.IO.FileAccess.Read, System.IO.FileShare.Read))
- return PutAsync(remotename, fs, cancelToken);
+ await PutAsync(remotename, fs, cancelToken);
}
public void Get(string remotename, System.IO.Stream output)
diff --git a/Duplicati/Library/Backend/GoogleServices/GoogleCloudStorage.cs b/Duplicati/Library/Backend/GoogleServices/GoogleCloudStorage.cs
index 1c63f0e06..65f744969 100644
--- a/Duplicati/Library/Backend/GoogleServices/GoogleCloudStorage.cs
+++ b/Duplicati/Library/Backend/GoogleServices/GoogleCloudStorage.cs
@@ -139,10 +139,10 @@ namespace Duplicati.Library.Backend.GoogleCloudStorage
}
}
- public Task PutAsync(string remotename, string filename, CancellationToken cancelToken)
+ public async Task PutAsync(string remotename, string filename, CancellationToken cancelToken)
{
using (System.IO.FileStream fs = System.IO.File.OpenRead(filename))
- return PutAsync(remotename, fs, cancelToken);
+ await PutAsync(remotename, fs, cancelToken);
}
public void Get(string remotename, string filename)
diff --git a/Duplicati/Library/Backend/GoogleServices/GoogleDrive.cs b/Duplicati/Library/Backend/GoogleServices/GoogleDrive.cs
index 29e179e50..bee4df367 100644
--- a/Duplicati/Library/Backend/GoogleServices/GoogleDrive.cs
+++ b/Duplicati/Library/Backend/GoogleServices/GoogleDrive.cs
@@ -253,10 +253,10 @@ namespace Duplicati.Library.Backend.GoogleDrive
}
}
- public Task PutAsync(string remotename, string filename, CancellationToken cancelToken)
+ public async Task PutAsync(string remotename, string filename, CancellationToken cancelToken)
{
using (System.IO.FileStream fs = System.IO.File.OpenRead(filename))
- return PutAsync(remotename, fs, cancelToken);
+ await PutAsync(remotename, fs, cancelToken);
}
public void Get(string remotename, string filename)
diff --git a/Duplicati/Library/Backend/Jottacloud/Jottacloud.cs b/Duplicati/Library/Backend/Jottacloud/Jottacloud.cs
index 95dc2762f..9f297abf2 100644
--- a/Duplicati/Library/Backend/Jottacloud/Jottacloud.cs
+++ b/Duplicati/Library/Backend/Jottacloud/Jottacloud.cs
@@ -304,10 +304,10 @@ namespace Duplicati.Library.Backend
return ToFileEntry(xFile);
}
- public Task PutAsync(string remotename, string filename, CancellationToken cancelToken)
+ public async Task PutAsync(string remotename, string filename, CancellationToken cancelToken)
{
using (System.IO.FileStream fs = System.IO.File.OpenRead(filename))
- return PutAsync(remotename, fs, cancelToken);
+ await PutAsync(remotename, fs, cancelToken);
}
public void Get(string remotename, string filename)
diff --git a/Duplicati/Library/Backend/Mega/Duplicati.Library.Backend.Mega.csproj b/Duplicati/Library/Backend/Mega/Duplicati.Library.Backend.Mega.csproj
index c8871b0e0..27a9e9cd4 100644
--- a/Duplicati/Library/Backend/Mega/Duplicati.Library.Backend.Mega.csproj
+++ b/Duplicati/Library/Backend/Mega/Duplicati.Library.Backend.Mega.csproj
@@ -31,12 +31,15 @@
<ConsolePause>false</ConsolePause>
</PropertyGroup>
<ItemGroup>
- <Reference Include="MegaApiClient, Version=1.7.1.495, Culture=neutral, PublicKeyToken=0480d311efbeb4e2, processorArchitecture=MSIL">
- <HintPath>..\..\..\..\packages\MegaApiClient.1.7.1\lib\net46\MegaApiClient.dll</HintPath>
+ <Reference Include="MegaApiClient, Version=1.9.0.0, Culture=neutral, PublicKeyToken=0480d311efbeb4e2">
+ <HintPath>..\..\..\..\packages\MegaApiClient.1.9.0\lib\net46\MegaApiClient.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\Newtonsoft.Json.12.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
+ <Reference Include="Otp.NET, Version=1.2.2.0, Culture=neutral, PublicKeyToken=38a48df817e173a6">
+ <HintPath>..\..\..\..\packages\Otp.NET.1.2.2\lib\net45\Otp.NET.dll</HintPath>
+ </Reference>
<Reference Include="System" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Web" />
diff --git a/Duplicati/Library/Backend/Mega/MegaBackend.cs b/Duplicati/Library/Backend/Mega/MegaBackend.cs
index 184a63769..8f73cf2a4 100644
--- a/Duplicati/Library/Backend/Mega/MegaBackend.cs
+++ b/Duplicati/Library/Backend/Mega/MegaBackend.cs
@@ -22,6 +22,7 @@ using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
+using OtpNet;
namespace Duplicati.Library.Backend.Mega
{
@@ -31,6 +32,7 @@ namespace Duplicati.Library.Backend.Mega
{
private readonly string m_username = null;
private readonly string m_password = null;
+ private readonly string m_twoFactorKey = null;
private Dictionary<string, List<INode>> m_filecache;
private INode m_currentFolder = null;
private readonly string m_prefix = null;
@@ -48,7 +50,13 @@ namespace Duplicati.Library.Backend.Mega
if (m_client == null)
{
var cl = new MegaApiClient();
- cl.Login(m_username, m_password);
+ if (m_twoFactorKey == null)
+ cl.Login(m_username, m_password);
+ else
+ {
+ var totp = new Totp(Base32Encoding.ToBytes(m_twoFactorKey)).ComputeTotp();
+ cl.Login(m_username, m_password, totp);
+ }
m_client = cl;
}
@@ -64,6 +72,8 @@ namespace Duplicati.Library.Backend.Mega
m_username = options["auth-username"];
if (options.ContainsKey("auth-password"))
m_password = options["auth-password"];
+ if (options.ContainsKey("auth-two-factor-key"))
+ m_twoFactorKey = options["auth-two-factor-key"];
if (!string.IsNullOrEmpty(uri.Username))
m_username = uri.Username;
@@ -186,10 +196,10 @@ namespace Duplicati.Library.Backend.Mega
select new FileEntry(item.Name, item.Size, item.ModificationDate ?? new DateTime(0), item.ModificationDate ?? new DateTime(0));
}
- public Task PutAsync(string remotename, string filename, CancellationToken cancelToken)
+ public async Task PutAsync(string remotename, string filename, CancellationToken cancelToken)
{
using (System.IO.FileStream fs = System.IO.File.OpenRead(filename))
- return PutAsync(remotename, fs, cancelToken);
+ await PutAsync(remotename, fs, cancelToken);
}
public void Get(string remotename, string filename)
@@ -253,6 +263,7 @@ namespace Duplicati.Library.Backend.Mega
return new List<ICommandLineArgument>(new ICommandLineArgument[] {
new CommandLineArgument("auth-password", CommandLineArgument.ArgumentType.Password, Strings.MegaBackend.AuthPasswordDescriptionShort, Strings.MegaBackend.AuthPasswordDescriptionLong),
new CommandLineArgument("auth-username", CommandLineArgument.ArgumentType.String, Strings.MegaBackend.AuthUsernameDescriptionShort, Strings.MegaBackend.AuthUsernameDescriptionLong),
+ new CommandLineArgument("auth-two-factor-key", CommandLineArgument.ArgumentType.Password, Strings.MegaBackend.AuthTwoFactorKeyDescriptionShort, Strings.MegaBackend.AuthTwoFactorKeyDescriptionLong),
});
}
}
diff --git a/Duplicati/Library/Backend/Mega/Strings.cs b/Duplicati/Library/Backend/Mega/Strings.cs
index d52089191..3009798e1 100644
--- a/Duplicati/Library/Backend/Mega/Strings.cs
+++ b/Duplicati/Library/Backend/Mega/Strings.cs
@@ -6,8 +6,10 @@ namespace Duplicati.Library.Backend.Strings {
public static string AuthPasswordDescriptionShort { get { return LC.L(@"Supplies the password used to connect to the server"); } }
public static string AuthUsernameDescriptionLong { get { return LC.L(@"The username used to connect to the server. This may also be supplied as the environment variable ""AUTH_USERNAME""."); } }
public static string AuthUsernameDescriptionShort { get { return LC.L(@"Supplies the username used to connect to the server"); } }
+ public static string AuthTwoFactorKeyDescriptionShort { get { return LC.L(@"The shared secret used to generate two-factor TOTP codes."); } }
+ public static string AuthTwoFactorKeyDescriptionLong { get { return LC.L(@"For accounts with two-factor authentication enabled, this is the shared secret used to generate the two-factor TOTP codes."); } }
public static string NoPasswordError { get { return LC.L(@"No password given"); } }
public static string NoUsernameError { get { return LC.L(@"No username given"); } }
public static string Description { get { return LC.L(@"This backend can read and write data to Mega.co.nz. Allowed formats are: ""mega://folder/subfolder"""); } }
}
-}
+} \ No newline at end of file
diff --git a/Duplicati/Library/Backend/Mega/packages.config b/Duplicati/Library/Backend/Mega/packages.config
index 06ff32662..c90fbddd1 100644
--- a/Duplicati/Library/Backend/Mega/packages.config
+++ b/Duplicati/Library/Backend/Mega/packages.config
@@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="MegaApiClient" version="1.7.1" targetFramework="net471" />
+ <package id="MegaApiClient" version="1.9.0" targetFramework="net471" />
<package id="Newtonsoft.Json" version="12.0.2" targetFramework="net471" />
-</packages>
+ <package id="Otp.NET" version="1.2.2" targetFramework="net471" />
+</packages> \ No newline at end of file
diff --git a/Duplicati/Library/Backend/OpenStack/OpenStackStorage.cs b/Duplicati/Library/Backend/OpenStack/OpenStackStorage.cs
index 306aa9087..935645c3b 100644
--- a/Duplicati/Library/Backend/OpenStack/OpenStackStorage.cs
+++ b/Duplicati/Library/Backend/OpenStack/OpenStackStorage.cs
@@ -565,10 +565,10 @@ namespace Duplicati.Library.Backend.OpenStack
}
}
- public Task PutAsync(string remotename, string filename, CancellationToken cancelToken)
+ public async Task PutAsync(string remotename, string filename, CancellationToken cancelToken)
{
using (FileStream fs = File.OpenRead(filename))
- return PutAsync(remotename, fs, cancelToken);
+ await PutAsync(remotename, fs, cancelToken);
}
public void Get(string remotename, string filename)
diff --git a/Duplicati/Library/Backend/S3/S3Backend.cs b/Duplicati/Library/Backend/S3/S3Backend.cs
index 6b14eabbf..cde4dbf23 100644
--- a/Duplicati/Library/Backend/S3/S3Backend.cs
+++ b/Duplicati/Library/Backend/S3/S3Backend.cs
@@ -341,10 +341,10 @@ namespace Duplicati.Library.Backend
}
}
- public Task PutAsync(string remotename, string localname, CancellationToken cancelToken)
+ public async Task PutAsync(string remotename, string localname, CancellationToken cancelToken)
{
using (FileStream fs = File.Open(localname, FileMode.Open, FileAccess.Read, FileShare.Read))
- return PutAsync(remotename, fs, cancelToken);
+ await PutAsync(remotename, fs, cancelToken);
}
public async Task PutAsync(string remotename, Stream input, CancellationToken cancelToken)
diff --git a/Duplicati/Library/Backend/SSHv2/SSHv2Backend.cs b/Duplicati/Library/Backend/SSHv2/SSHv2Backend.cs
index 14bb5264a..5e79a6db9 100644
--- a/Duplicati/Library/Backend/SSHv2/SSHv2Backend.cs
+++ b/Duplicati/Library/Backend/SSHv2/SSHv2Backend.cs
@@ -167,11 +167,11 @@ namespace Duplicati.Library.Backend
public string ProtocolKey => "ssh";
- public Task PutAsync(string remotename, string filename, CancellationToken cancelToken)
+ public async Task PutAsync(string remotename, string filename, CancellationToken cancelToken)
{
using (System.IO.FileStream fs = System.IO.File.Open(filename, System.IO.FileMode.Open,
System.IO.FileAccess.Read, System.IO.FileShare.Read))
- return PutAsync(remotename, fs, cancelToken);
+ await PutAsync(remotename, fs, cancelToken);
}
public void Get(string remotename, string filename)
diff --git a/Duplicati/Library/Backend/SharePoint/SharePointBackend.cs b/Duplicati/Library/Backend/SharePoint/SharePointBackend.cs
index c0af18060..646471669 100644
--- a/Duplicati/Library/Backend/SharePoint/SharePointBackend.cs
+++ b/Duplicati/Library/Backend/SharePoint/SharePointBackend.cs
@@ -518,10 +518,10 @@ namespace Duplicati.Library.Backend
Utility.Utility.CopyStream(s, stream, true, copybuffer);
}
- public Task PutAsync(string remotename, string filename, CancellationToken cancelToken)
+ public async Task PutAsync(string remotename, string filename, CancellationToken cancelToken)
{
using (FileStream fs = System.IO.File.OpenRead(filename))
- return PutAsync(remotename, fs, cancelToken);
+ await PutAsync(remotename, fs, cancelToken);
}
public Task PutAsync(string remotename, Stream stream, CancellationToken cancelToken) { return doPut(remotename, stream, false, cancelToken); }
diff --git a/Duplicati/Library/Backend/Storj/Duplicati.Library.Backend.Storj.csproj b/Duplicati/Library/Backend/Storj/Duplicati.Library.Backend.Storj.csproj
new file mode 100644
index 000000000..4f6179435
--- /dev/null
+++ b/Duplicati/Library/Backend/Storj/Duplicati.Library.Backend.Storj.csproj
@@ -0,0 +1,128 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+ <Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
+ <PropertyGroup>
+ <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
+ <Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
+ <ProjectGuid>{AE035E01-C917-4F13-A35E-78F21C1A2F17}</ProjectGuid>
+ <OutputType>Library</OutputType>
+ <AppDesignerFolder>Properties</AppDesignerFolder>
+ <RootNamespace>Duplicati.Library.Backend.Storj</RootNamespace>
+ <AssemblyName>Duplicati.Library.Backend.Storj</AssemblyName>
+ <TargetFrameworkVersion>v4.7.1</TargetFrameworkVersion>
+ <FileAlignment>512</FileAlignment>
+ <Deterministic>true</Deterministic>
+ <NuGetPackageImportStamp>
+ </NuGetPackageImportStamp>
+ <TargetFrameworkProfile />
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
+ <DebugSymbols>true</DebugSymbols>
+ <DebugType>full</DebugType>
+ <Optimize>false</Optimize>
+ <OutputPath>bin\Debug\</OutputPath>
+ <DefineConstants>DEBUG;TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ <PlatformTarget>x64</PlatformTarget>
+ </PropertyGroup>
+ <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
+ <DebugType>pdbonly</DebugType>
+ <Optimize>true</Optimize>
+ <OutputPath>bin\Release\</OutputPath>
+ <DefineConstants>TRACE</DefineConstants>
+ <ErrorReport>prompt</ErrorReport>
+ <WarningLevel>4</WarningLevel>
+ </PropertyGroup>
+ <ItemGroup>
+ <Reference Include="SQLite-net, Version=1.7.335.0, Culture=neutral, processorArchitecture=MSIL">
+ <HintPath>..\..\..\..\packages\sqlite-net-pcl.1.7.335\lib\netstandard2.0\SQLite-net.dll</HintPath>
+ </Reference>
+ <Reference Include="SQLitePCLRaw.batteries_v2, Version=2.0.3.851, Culture=neutral, PublicKeyToken=8226ea5df37bcae9, processorArchitecture=MSIL">
+ <HintPath>..\..\..\..\packages\SQLitePCLRaw.bundle_green.2.0.3\lib\net461\SQLitePCLRaw.batteries_v2.dll</HintPath>
+ </Reference>
+ <Reference Include="SQLitePCLRaw.core, Version=2.0.3.851, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
+ <HintPath>..\..\..\..\packages\SQLitePCLRaw.core.2.0.3\lib\netstandard2.0\SQLitePCLRaw.core.dll</HintPath>
+ </Reference>
+ <Reference Include="SQLitePCLRaw.nativelibrary, Version=2.0.3.851, Culture=neutral, PublicKeyToken=502ed628492ab262, processorArchitecture=MSIL">
+ <HintPath>..\..\..\..\packages\SQLitePCLRaw.bundle_green.2.0.3\lib\net461\SQLitePCLRaw.nativelibrary.dll</HintPath>
+ </Reference>
+ <Reference Include="SQLitePCLRaw.provider.dynamic_cdecl, Version=2.0.3.851, Culture=neutral, PublicKeyToken=b68184102cba0b3b, processorArchitecture=MSIL">
+ <HintPath>..\..\..\..\packages\SQLitePCLRaw.provider.dynamic_cdecl.2.0.3\lib\netstandard2.0\SQLitePCLRaw.provider.dynamic_cdecl.dll</HintPath>
+ </Reference>
+ <Reference Include="System" />
+ <Reference Include="System.Buffers, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
+ <HintPath>..\..\..\..\packages\System.Buffers.4.4.0\lib\netstandard2.0\System.Buffers.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Core" />
+ <Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
+ <HintPath>..\..\..\..\packages\System.Memory.4.5.3\lib\netstandard2.0\System.Memory.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Numerics" />
+ <Reference Include="System.Numerics.Vectors, Version=4.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\..\..\..\packages\System.Numerics.Vectors.4.4.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\..\..\..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Xml.Linq" />
+ <Reference Include="System.Data.DataSetExtensions" />
+ <Reference Include="Microsoft.CSharp" />
+ <Reference Include="System.Data" />
+ <Reference Include="System.Net.Http" />
+ <Reference Include="System.Xml" />
+ <Reference Include="uplink.NET, Version=2.7.0.0, Culture=neutral, processorArchitecture=MSIL">
+ <HintPath>..\..\..\..\packages\uplink.NET.2.7.1604\lib\netstandard2.0\uplink.NET.dll</HintPath>
+ </Reference>
+ </ItemGroup>
+ <ItemGroup>
+ <Compile Include="StorjFile.cs" />
+ <Compile Include="Strings.cs" />
+ <Compile Include="StorjBackend.cs" />
+ <Compile Include="Properties\AssemblyInfo.cs" />
+ <Compile Include="StorjConfig.cs" />
+ </ItemGroup>
+ <ItemGroup>
+ <ProjectReference Include="..\..\Common\Duplicati.Library.Common.csproj">
+ <Project>{d63e53e4-a458-4c2f-914d-92f715f58acf}</Project>
+ <Name>Duplicati.Library.Common</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\..\Interface\Duplicati.Library.Interface.csproj">
+ <Project>{c5899f45-b0ff-483c-9d38-24a9fcaab237}</Project>
+ <Name>Duplicati.Library.Interface</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\..\Localization\Duplicati.Library.Localization.csproj">
+ <Project>{b68f2214-951f-4f78-8488-66e1ed3f50bf}</Project>
+ <Name>Duplicati.Library.Localization</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\..\Utility\Duplicati.Library.Utility.csproj">
+ <Project>{de3e5d4c-51ab-4e5e-bee8-e636cebfba65}</Project>
+ <Name>Duplicati.Library.Utility</Name>
+ </ProjectReference>
+ </ItemGroup>
+ <ItemGroup>
+ <None Include="libstorj_uplink.dylib">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </None>
+ <None Include="libstorj_uplink.so">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </None>
+ <None Include="packages.config" />
+ </ItemGroup>
+ <ItemGroup>
+ <Content Include="win-x64\storj_uplink.dll">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="win-x86\storj_uplink.dll">
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ </ItemGroup>
+ <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
+ <Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
+ <PropertyGroup>
+ <ErrorText>Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}".</ErrorText>
+ </PropertyGroup>
+ <Error Condition="!Exists('..\..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.2.0.3\build\net461\SQLitePCLRaw.lib.e_sqlite3.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.2.0.3\build\net461\SQLitePCLRaw.lib.e_sqlite3.targets'))" />
+ </Target>
+ <Import Project="..\..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.2.0.3\build\net461\SQLitePCLRaw.lib.e_sqlite3.targets" Condition="Exists('..\..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.2.0.3\build\net461\SQLitePCLRaw.lib.e_sqlite3.targets')" />
+</Project> \ No newline at end of file
diff --git a/Duplicati/Library/Backend/Storj/Properties/AssemblyInfo.cs b/Duplicati/Library/Backend/Storj/Properties/AssemblyInfo.cs
new file mode 100644
index 000000000..27b1092b7
--- /dev/null
+++ b/Duplicati/Library/Backend/Storj/Properties/AssemblyInfo.cs
@@ -0,0 +1,37 @@
+using System.Reflection;
+using System.Runtime.CompilerServices;
+using System.Runtime.InteropServices;
+
+// Allgemeine Informationen über eine Assembly werden über die folgenden
+// Attribute gesteuert. Ändern Sie diese Attributwerte, um die Informationen zu ändern,
+// die einer Assembly zugeordnet sind.
+[assembly: AssemblyTitle("Duplicati.Library.Backend.Tardigrade")]
+[assembly: AssemblyDescription("")]
+[assembly: AssemblyConfiguration("")]
+[assembly: AssemblyCompany("")]
+[assembly: AssemblyProduct("Duplicati.Library.Backend.Tardigrade")]
+[assembly: AssemblyCopyright("Copyright © 2020")]
+[assembly: AssemblyTrademark("")]
+[assembly: AssemblyCulture("")]
+
+// Durch Festlegen von ComVisible auf FALSE werden die Typen in dieser Assembly
+// für COM-Komponenten unsichtbar. Wenn Sie auf einen Typ in dieser Assembly von
+// COM aus zugreifen müssen, sollten Sie das ComVisible-Attribut für diesen Typ auf "True" festlegen.
+[assembly: ComVisible(false)]
+
+// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
+[assembly: Guid("ae035e01-c917-4f13-a35e-78f21c1a2f17")]
+
+// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
+//
+// Hauptversion
+// Nebenversion
+// Buildnummer
+// Revision
+//
+// Sie können alle Werte angeben oder Standardwerte für die Build- und Revisionsnummern verwenden,
+// indem Sie "*" wie unten gezeigt eingeben:
+// [assembly: AssemblyVersion("1.0.*")]
+[assembly: AssemblyVersion("1.0.0.0")]
+[assembly: AssemblyFileVersion("1.0.0.0")]
+[assembly:InternalsVisibleTo("Duplicati.Library.Backend.Tardigrade")]
diff --git a/Duplicati/Library/Backend/Storj/StorjBackend.cs b/Duplicati/Library/Backend/Storj/StorjBackend.cs
new file mode 100644
index 000000000..43b4f42a9
--- /dev/null
+++ b/Duplicati/Library/Backend/Storj/StorjBackend.cs
@@ -0,0 +1,368 @@
+using Duplicati.Library.Interface;
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Linq.Expressions;
+using System.Runtime.InteropServices;
+using System.Text;
+using System.Threading;
+using System.Threading.Tasks;
+using uplink.NET.Interfaces;
+using uplink.NET.Models;
+using uplink.NET.Services;
+
+namespace Duplicati.Library.Backend.Storj
+{
+ public class Storj : IStreamingBackend
+ {
+ private const string STORJ_AUTH_METHOD = "storj-auth-method";
+ private const string STORJ_SATELLITE = "storj-satellite";
+ private const string STORJ_API_KEY = "storj-api-key";
+ private const string STORJ_SECRET = "storj-secret";
+ private const string STORJ_SHARED_ACCESS = "storj-shared-access";
+ private const string STORJ_BUCKET = "storj-bucket";
+ private const string STORJ_FOLDER = "storj-folder";
+
+ private const string PROTOCOL_KEY = "storj";
+ private const string STORJ_PARTNER_ID = "duplicati";
+
+ private readonly string _satellite;
+ private readonly string _api_key;
+ private readonly string _secret;
+ private readonly string _bucket;
+ private readonly string _folder;
+ private Access _access;
+ private IBucketService _bucketService;
+ private IObjectService _objectService;
+
+ public static readonly Dictionary<string, string> KNOWN_STORJ_SATELLITES = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase){
+ { "US Central", "us1.storj.io:7777" },
+ { "Asia East", "ap1.storj.io:7777" },
+ { "Europe", "eu1.storj.io:7777" },
+ };
+
+ public static readonly Dictionary<string, string> KNOWN_AUTHENTICATION_METHODS = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase){
+ { "API key", "API key" },
+ { "Access grant", "Access grant" },
+ };
+
+ [DllImport("kernel32.dll")]
+ protected static extern IntPtr LoadLibrary(string filename);
+
+ private static bool _libraryLoaded = false;
+ private static void InitStorjLibrary()
+ {
+ if (_libraryLoaded)
+ return;
+
+ if (Duplicati.Library.Common.Platform.IsClientWindows) //We need to init only on Windows to distinguish between x64 and x86
+ {
+ if (System.Environment.Is64BitProcess)
+ {
+ var res = LoadLibrary("win-x64/storj_uplink.dll");
+ }
+ else
+ {
+ var res = LoadLibrary("win-x86/storj_uplink.dll");
+ }
+ }
+ Access.SetTempDirectory(Library.Utility.TempFolder.SystemTempPath);
+ _libraryLoaded = true;
+ }
+
+ // ReSharper disable once UnusedMember.Global
+ // This constructor is needed by the BackendLoader.
+ public Storj()
+ {
+ }
+
+ // ReSharper disable once UnusedMember.Global
+ // This constructor is needed by the BackendLoader.
+ public Storj(string url, Dictionary<string, string> options)
+ {
+ InitStorjLibrary();
+
+ foreach(var option in options.ToList())
+ {
+ if(option.Key.ToLower().Contains("tardigrade"))
+ {
+ options.Add(option.Key.ToLower().Replace("tardigrade", "storj"), option.Value);
+ }
+ }
+
+ var auth_method = options[STORJ_AUTH_METHOD];
+ if (auth_method == "Access grant")
+ {
+ //Create an access from the access grant
+ var shared_access = options[STORJ_SHARED_ACCESS];
+ _access = new Access(shared_access, new Config() { UserAgent = STORJ_PARTNER_ID });
+ }
+ else
+ {
+ //Create an access for a satellite, API key and encryption passphrase
+ _satellite = options[STORJ_SATELLITE];
+
+ if (options.ContainsKey(STORJ_API_KEY))
+ {
+ _api_key = options[STORJ_API_KEY];
+ }
+ if (options.ContainsKey(STORJ_SECRET))
+ {
+ _secret = options[STORJ_SECRET];
+ }
+
+ _access = new Access(_satellite, _api_key, _secret, new Config() { UserAgent = STORJ_PARTNER_ID });
+ }
+
+ _bucketService = new BucketService(_access);
+ _objectService = new ObjectService(_access);
+
+ //If no bucket was provided use the default "duplicati"-bucket
+ if (options.ContainsKey(STORJ_BUCKET))
+ {
+ _bucket = options[STORJ_BUCKET];
+ }
+ else
+ {
+ _bucket = "duplicati";
+ }
+
+ if (options.ContainsKey(STORJ_FOLDER))
+ {
+ _folder = options[STORJ_FOLDER];
+ }
+ }
+
+ public string DisplayName
+ {
+ get { return Strings.Storj.DisplayName; }
+ }
+
+ public string ProtocolKey => PROTOCOL_KEY;
+
+ public IList<ICommandLineArgument> SupportedCommands
+ {
+ get
+ {
+ return new List<ICommandLineArgument>(new ICommandLineArgument[] {
+ new CommandLineArgument(STORJ_AUTH_METHOD, CommandLineArgument.ArgumentType.String, Strings.Storj.StorjAuthMethodDescriptionShort, Strings.Storj.StorjAuthMethodDescriptionLong, "API key"),
+ new CommandLineArgument(STORJ_SATELLITE, CommandLineArgument.ArgumentType.String, Strings.Storj.StorjSatelliteDescriptionShort, Strings.Storj.StorjSatelliteDescriptionLong, "us1.storj.io:7777"),
+ new CommandLineArgument(STORJ_API_KEY, CommandLineArgument.ArgumentType.String, Strings.Storj.StorjAPIKeyDescriptionShort, Strings.Storj.StorjAPIKeyDescriptionLong),
+ new CommandLineArgument(STORJ_SECRET, CommandLineArgument.ArgumentType.Password, Strings.Storj.StorjSecretDescriptionShort, Strings.Storj.StorjSecretDescriptionLong),
+ new CommandLineArgument(STORJ_SHARED_ACCESS, CommandLineArgument.ArgumentType.String, Strings.Storj.StorjSharedAccessDescriptionShort, Strings.Storj.StorjSharedAccessDescriptionLong),
+ new CommandLineArgument(STORJ_BUCKET, CommandLineArgument.ArgumentType.String, Strings.Storj.StorjBucketDescriptionShort, Strings.Storj.StorjBucketDescriptionLong),
+ new CommandLineArgument(STORJ_FOLDER, CommandLineArgument.ArgumentType.String, Strings.Storj.StorjFolderDescriptionShort, Strings.Storj.StorjFolderDescriptionLong),
+ });
+ }
+ }
+
+ public string Description
+ {
+ get
+ {
+ return Strings.Storj.Description;
+ }
+ }
+
+ public string[] DNSName
+ {
+ get
+ {
+ return new string[0];
+ }
+ }
+
+ public void CreateFolder()
+ {
+ //Storj DCS has no folders
+ }
+
+ public void Delete(string remotename)
+ {
+ var deleteTask = DeleteAsync(remotename);
+ deleteTask.Wait();
+ }
+
+ public async Task DeleteAsync(string remotename)
+ {
+ try
+ {
+ var bucket = await _bucketService.EnsureBucketAsync(_bucket);
+ await _objectService.DeleteObjectAsync(bucket, GetBasePath() + remotename);
+ }
+ catch (Exception root)
+ {
+ throw new FileMissingException(root);
+ }
+ }
+
+ public void Dispose()
+ {
+ if (_objectService != null)
+ {
+ _objectService = null;
+ }
+ if (_bucketService != null)
+ {
+ _bucketService = null;
+ }
+ if (_access != null)
+ {
+ _access.Dispose();
+ _access = null;
+ }
+ }
+
+ public void Get(string remotename, string filename)
+ {
+ var getTask = GetAsync(remotename, filename);
+ getTask.Wait();
+ }
+
+ public async Task GetAsync(string remotename, string filename)
+ {
+ var bucket = await _bucketService.EnsureBucketAsync(_bucket);
+ var download = await _objectService.DownloadObjectAsync(bucket, GetBasePath() + remotename, new DownloadOptions(), false);
+ await download.StartDownloadAsync();
+
+ if (download.Completed)
+ {
+ using (FileStream file = new FileStream(filename, FileMode.Create))
+ {
+ await file.WriteAsync(download.DownloadedBytes, 0, (int)download.BytesReceived);
+ await file.FlushAsync().ConfigureAwait(false);
+ }
+ }
+ }
+
+ public void Get(string remotename, Stream stream)
+ {
+ var getTask = GetAsync(remotename, stream);
+ getTask.Wait();
+ }
+
+ public async Task GetAsync(string remotename, Stream stream)
+ {
+ int index = 0;
+ var bucket = await _bucketService.EnsureBucketAsync(_bucket);
+ var download = await _objectService.DownloadObjectAsync(bucket, GetBasePath() + remotename, new DownloadOptions(), false);
+ download.DownloadOperationProgressChanged += (op) =>
+ {
+ int newPartLength = (int)op.BytesReceived - index;
+ byte[] newPart = new byte[newPartLength];
+ Array.Copy(op.DownloadedBytes, index, newPart, 0, newPartLength);
+ stream.Write(newPart, 0, newPartLength);
+ index = index + newPartLength;
+ };
+ await download.StartDownloadAsync();
+ }
+
+ public IEnumerable<IFileEntry> List()
+ {
+ var listTask = ListAsync();
+ listTask.Wait();
+ return listTask.Result;
+ }
+
+ private async Task<IEnumerable<IFileEntry>> ListAsync()
+ {
+ List<StorjFile> files = new List<StorjFile>();
+ var bucket = await _bucketService.EnsureBucketAsync(_bucket);
+ var prefix = GetBasePath();
+ var objects = await _objectService.ListObjectsAsync(bucket, new ListObjectsOptions { Recursive = true, System = true, Custom = true, Prefix = prefix });
+
+ foreach (var obj in objects.Items)
+ {
+ StorjFile file = new StorjFile(obj);
+ if (prefix != "")
+ {
+ file.Name = file.Name.Replace(prefix, "");
+ }
+ files.Add(file);
+ }
+
+ return files;
+ }
+
+ public async Task PutAsync(string remotename, string filename, CancellationToken cancelToken)
+ {
+ using (FileStream fs = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read))
+ await PutAsync(remotename, fs, cancelToken);
+ }
+
+ public async Task PutAsync(string remotename, Stream stream, CancellationToken cancelToken)
+ {
+ var bucket = await _bucketService.EnsureBucketAsync(_bucket);
+ CustomMetadata custom = new CustomMetadata();
+ custom.Entries.Add(new CustomMetadataEntry { Key = StorjFile.STORJ_LAST_ACCESS, Value = DateTime.Now.ToUniversalTime().ToString("O") });
+ custom.Entries.Add(new CustomMetadataEntry { Key = StorjFile.STORJ_LAST_MODIFICATION, Value = DateTime.Now.ToUniversalTime().ToString("O") });
+ var upload = await _objectService.UploadObjectAsync(bucket, GetBasePath() + remotename, new UploadOptions(), stream, custom, false);
+ await upload.StartUploadAsync();
+ }
+
+ public void Test()
+ {
+ var testTask = TestAsync();
+ testTask.Wait(10000);
+ if (!testTask.Result)
+ {
+ throw new Exception(Strings.Storj.TestConnectionFailed);
+ }
+ }
+
+ /// <summary>
+ /// Test the connection by:
+ /// - creating the bucket (if it not already exists)
+ /// - uploading 256 random bytes to a test-file
+ /// - downloading the file back and expecting 256 bytes
+ /// </summary>
+ /// <returns>true, if the test was successfull or and exception</returns>
+ private async Task<bool> TestAsync()
+ {
+ string testFileName = GetBasePath() + "duplicati_test.dat";
+
+ var bucket = await _bucketService.EnsureBucketAsync(_bucket);
+ var upload = await _objectService.UploadObjectAsync(bucket, testFileName, new UploadOptions(), GetRandomBytes(256), false);
+ await upload.StartUploadAsync();
+
+ var download = await _objectService.DownloadObjectAsync(bucket, testFileName, new DownloadOptions(), false);
+ await download.StartDownloadAsync();
+
+ await _objectService.DeleteObjectAsync(bucket, testFileName);
+
+ if (download.Failed || download.BytesReceived != 256)
+ {
+ throw new Exception(download.ErrorMessage);
+ }
+
+ return true;
+ }
+
+ /// <summary>
+ /// Gets the base path - depending on there is a folder set or not
+ /// </summary>
+ /// <returns>The base path within a bucket where the backup shall be placed</returns>
+ private string GetBasePath()
+ {
+ if (!string.IsNullOrEmpty(_folder))
+ return _folder + "/";
+ else
+ return "";
+ }
+
+ /// <summary>
+ /// Creates some random bytes with the given length - just for testing the connection
+ /// </summary>
+ /// <param name="length">The length of the bytes to create</param>
+ /// <returns>A byte-array with the given length</returns>
+ private static byte[] GetRandomBytes(long length)
+ {
+ byte[] bytes = new byte[length];
+ Random rand = new Random();
+ rand.NextBytes(bytes);
+
+ return bytes;
+ }
+ }
+}
diff --git a/Duplicati/Library/Backend/Tardigrade/TardigradeConfig.cs b/Duplicati/Library/Backend/Storj/StorjConfig.cs
index 1464fe17f..7b449645f 100644
--- a/Duplicati/Library/Backend/Tardigrade/TardigradeConfig.cs
+++ b/Duplicati/Library/Backend/Storj/StorjConfig.cs
@@ -5,12 +5,12 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
-namespace Duplicati.Library.Backend.Tardigrade
+namespace Duplicati.Library.Backend.Storj
{
- public class TardigradeConfig : IWebModule
+ public class StorjConfig : IWebModule
{
private const ConfigType DEFAULT_CONFIG_TYPE = ConfigType.Satellites;
- private const string KEY_CONFIGTYPE = "tardigrade-config";
+ private const string KEY_CONFIGTYPE = "storj-config";
private static readonly string DEFAULT_CONFIG_TYPE_STR = Enum.GetName(typeof(ConfigType), DEFAULT_CONFIG_TYPE);
public enum ConfigType
@@ -21,11 +21,11 @@ namespace Duplicati.Library.Backend.Tardigrade
#region IWebModule implementation
- public string Key { get { return "tardigrade-getconfig"; } }
+ public string Key { get { return "storj-getconfig"; } }
- public string DisplayName { get { return "Tardigrade configuration module"; } }
+ public string DisplayName { get { return "Storj DCS configuration module"; } }
- public string Description { get { return "Exposes Tardigrade configuration as a web module"; } }
+ public string Description { get { return "Exposes Storj DCS configuration as a web module"; } }
public IList<ICommandLineArgument> SupportedCommands
{
@@ -56,11 +56,11 @@ namespace Duplicati.Library.Backend.Tardigrade
switch (ct)
{
case ConfigType.Satellites:
- return Tardigrade.KNOWN_TARDIGRADE_SATELLITES;
+ return Storj.KNOWN_STORJ_SATELLITES;
case ConfigType.AuthenticationMethods:
- return Tardigrade.KNOWN_AUTHENTICATION_METHODS;
+ return Storj.KNOWN_AUTHENTICATION_METHODS;
default:
- return Tardigrade.KNOWN_TARDIGRADE_SATELLITES;
+ return Storj.KNOWN_STORJ_SATELLITES;
}
}
#endregion
diff --git a/Duplicati/Library/Backend/Tardigrade/TardigradeFile.cs b/Duplicati/Library/Backend/Storj/StorjFile.cs
index 794e427e0..fa83c3907 100644
--- a/Duplicati/Library/Backend/Tardigrade/TardigradeFile.cs
+++ b/Duplicati/Library/Backend/Storj/StorjFile.cs
@@ -5,12 +5,12 @@ using System.Linq;
using System.Text;
using System.Threading.Tasks;
-namespace Duplicati.Library.Backend.Tardigrade
+namespace Duplicati.Library.Backend.Storj
{
- public class TardigradeFile : IFileEntry
+ public class StorjFile : IFileEntry
{
- public static readonly string TARDIGRADE_LAST_ACCESS = "DUPLICATI:LAST-ACCESS";
- public static readonly string TARDIGRADE_LAST_MODIFICATION = "DUPLICATI:LAST-MODIFICATION";
+ public static readonly string STORJ_LAST_ACCESS = "DUPLICATI:LAST-ACCESS";
+ public static readonly string STORJ_LAST_MODIFICATION = "DUPLICATI:LAST-MODIFICATION";
public bool IsFolder { get; set; }
public DateTime LastAccess { get; set; }
@@ -21,15 +21,15 @@ namespace Duplicati.Library.Backend.Tardigrade
public long Size { get; set; }
- public TardigradeFile()
+ public StorjFile()
{
}
- public TardigradeFile(uplink.NET.Models.Object tardigradeObject)
+ public StorjFile(uplink.NET.Models.Object tardigradeObject)
{
IsFolder = tardigradeObject.IsPrefix;
- var lastAccess = tardigradeObject.CustomMetaData.Entries.Where(e => e.Key == TARDIGRADE_LAST_ACCESS).FirstOrDefault();
+ var lastAccess = tardigradeObject.CustomMetadata.Entries.Where(e => e.Key == STORJ_LAST_ACCESS).FirstOrDefault();
if (lastAccess != null && !string.IsNullOrEmpty(lastAccess.Value))
{
LastAccess = DateTime.Parse(lastAccess.Value);
@@ -39,7 +39,7 @@ namespace Duplicati.Library.Backend.Tardigrade
LastAccess = DateTime.MinValue;
}
- var lastMod = tardigradeObject.CustomMetaData.Entries.Where(e => e.Key == TARDIGRADE_LAST_MODIFICATION).FirstOrDefault();
+ var lastMod = tardigradeObject.CustomMetadata.Entries.Where(e => e.Key == STORJ_LAST_MODIFICATION).FirstOrDefault();
if (lastMod != null && !string.IsNullOrEmpty(lastMod.Value))
{
LastModification = DateTime.Parse(lastMod.Value);
@@ -50,7 +50,7 @@ namespace Duplicati.Library.Backend.Tardigrade
}
Name = tardigradeObject.Key;
- Size = tardigradeObject.SystemMetaData.ContentLength;
+ Size = tardigradeObject.SystemMetadata.ContentLength;
}
}
}
diff --git a/Duplicati/Library/Backend/Storj/Strings.cs b/Duplicati/Library/Backend/Storj/Strings.cs
new file mode 100644
index 000000000..c1de9425f
--- /dev/null
+++ b/Duplicati/Library/Backend/Storj/Strings.cs
@@ -0,0 +1,30 @@
+using Duplicati.Library.Localization.Short;
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Duplicati.Library.Backend.Strings
+{
+ internal static class Storj
+ {
+ public static string DisplayName { get { return LC.L(@"Storj DCS (Decentralized Cloud Storage)"); } }
+ public static string Description { get { return LC.L(@"This backend can read and write data to the Storj DCS."); } }
+ public static string TestConnectionFailed { get { return LC.L(@"The connection-test failed."); } }
+ public static string StorjAuthMethodDescriptionShort { get { return LC.L(@"The authentication method"); } }
+ public static string StorjAuthMethodDescriptionLong { get { return LC.L(@"The authentication method describes which way to use to connect to the network - either via API key or via an access grant."); } }
+ public static string StorjSatelliteDescriptionShort { get { return LC.L(@"The satellite"); } }
+ public static string StorjSatelliteDescriptionLong { get { return LC.L(@"The satellite that keeps track of all metadata. Use a Storj DCS server for high-performance SLA-backed connectivity or use a community server. Or even host your own."); } }
+ public static string StorjAPIKeyDescriptionShort { get { return LC.L(@"The API key"); } }
+ public static string StorjAPIKeyDescriptionLong { get { return LC.L(@"The API key grants access to a specific project on your chosen satellite. Head over to the dashboard of your satellite to create one if you do not already have an API key."); } }
+ public static string StorjSecretDescriptionShort { get { return LC.L(@"The encryption passphrase"); } }
+ public static string StorjSecretDescriptionLong { get { return LC.L(@"The encryption passphrase is used to encrypt your data before sending it to the Storj network. This passphrase can be the only secret to provide - for Storj you do not necessary need any additional encryption (from Duplicati) in place."); } }
+ public static string StorjSharedAccessDescriptionShort { get { return LC.L(@"The access grant"); } }
+ public static string StorjSharedAccessDescriptionLong { get { return LC.L(@"An access grant contains all information in one encrypted string. You may use it instead of a satellite, API key and secret."); } }
+ public static string StorjBucketDescriptionShort { get { return LC.L(@"The bucket"); } }
+ public static string StorjBucketDescriptionLong { get { return LC.L(@"The bucket where the backup will reside in."); } }
+ public static string StorjFolderDescriptionShort { get { return LC.L(@"The folder"); } }
+ public static string StorjFolderDescriptionLong { get { return LC.L(@"The folder within the bucket where the backup will reside in."); } }
+ }
+}
diff --git a/Duplicati/Library/Backend/Tardigrade/libstorj_uplink.dylib b/Duplicati/Library/Backend/Storj/libstorj_uplink.dylib
index 4f0190dea..4f8ea2938 100644
--- a/Duplicati/Library/Backend/Tardigrade/libstorj_uplink.dylib
+++ b/Duplicati/Library/Backend/Storj/libstorj_uplink.dylib
Binary files differ
diff --git a/Duplicati/Library/Backend/Tardigrade/libstorj_uplink.so b/Duplicati/Library/Backend/Storj/libstorj_uplink.so
index 7c307a134..f4f495271 100644
--- a/Duplicati/Library/Backend/Tardigrade/libstorj_uplink.so
+++ b/Duplicati/Library/Backend/Storj/libstorj_uplink.so
Binary files differ
diff --git a/Duplicati/Library/Backend/Storj/packages.config b/Duplicati/Library/Backend/Storj/packages.config
new file mode 100644
index 000000000..983b0e7cc
--- /dev/null
+++ b/Duplicati/Library/Backend/Storj/packages.config
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<packages>
+ <package id="sqlite-net-pcl" version="1.7.335" targetFramework="net471" />
+ <package id="SQLitePCLRaw.bundle_green" version="2.0.3" targetFramework="net471" />
+ <package id="SQLitePCLRaw.core" version="2.0.3" targetFramework="net471" />
+ <package id="SQLitePCLRaw.lib.e_sqlite3" version="2.0.3" targetFramework="net471" />
+ <package id="SQLitePCLRaw.provider.dynamic_cdecl" version="2.0.3" targetFramework="net471" />
+ <package id="System.Buffers" version="4.4.0" targetFramework="net471" />
+ <package id="System.Memory" version="4.5.3" targetFramework="net471" />
+ <package id="System.Numerics.Vectors" version="4.4.0" targetFramework="net471" />
+ <package id="System.Runtime.CompilerServices.Unsafe" version="4.5.2" targetFramework="net471" />
+ <package id="uplink.NET" version="2.7.1604" targetFramework="net471" />
+</packages> \ No newline at end of file
diff --git a/Duplicati/Library/Backend/Tardigrade/win-x64/storj_uplink.dll b/Duplicati/Library/Backend/Storj/win-x64/storj_uplink.dll
index 29863308c..dbbca788d 100644
--- a/Duplicati/Library/Backend/Tardigrade/win-x64/storj_uplink.dll
+++ b/Duplicati/Library/Backend/Storj/win-x64/storj_uplink.dll
Binary files differ
diff --git a/Duplicati/Library/Backend/Tardigrade/win-x86/storj_uplink.dll b/Duplicati/Library/Backend/Storj/win-x86/storj_uplink.dll
index 6f1cc9e95..bbc697695 100644
--- a/Duplicati/Library/Backend/Tardigrade/win-x86/storj_uplink.dll
+++ b/Duplicati/Library/Backend/Storj/win-x86/storj_uplink.dll
Binary files differ
diff --git a/Duplicati/Library/Backend/TahoeLAFS/TahoeBackend.cs b/Duplicati/Library/Backend/TahoeLAFS/TahoeBackend.cs
index e36e03ce9..b35ad4585 100644
--- a/Duplicati/Library/Backend/TahoeLAFS/TahoeBackend.cs
+++ b/Duplicati/Library/Backend/TahoeLAFS/TahoeBackend.cs
@@ -209,10 +209,10 @@ namespace Duplicati.Library.Backend
}
}
- public Task PutAsync(string remotename, string filename, CancellationToken cancelToken)
+ public async Task PutAsync(string remotename, string filename, CancellationToken cancelToken)
{
using (System.IO.FileStream fs = System.IO.File.OpenRead(filename))
- return PutAsync(remotename, fs, cancelToken);
+ await PutAsync(remotename, fs, cancelToken);
}
public void Get(string remotename, string filename)
diff --git a/Duplicati/Library/Backend/Tardigrade/Duplicati.Library.Backend.Tardigrade.csproj b/Duplicati/Library/Backend/Tardigrade/Duplicati.Library.Backend.Tardigrade.csproj
index 0383b960b..5f7ba3ef7 100644
--- a/Duplicati/Library/Backend/Tardigrade/Duplicati.Library.Backend.Tardigrade.csproj
+++ b/Duplicati/Library/Backend/Tardigrade/Duplicati.Library.Backend.Tardigrade.csproj
@@ -4,7 +4,7 @@
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
- <ProjectGuid>{AE035E01-C917-4F13-A35E-78F21C1A2F17}</ProjectGuid>
+ <ProjectGuid>{9A04CB37-DA72-4008-9703-3AC5191974E9}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Duplicati.Library.Backend.Tardigrade</RootNamespace>
@@ -35,24 +35,50 @@
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
+ <Reference Include="SQLite-net, Version=1.7.335.0, Culture=neutral, processorArchitecture=MSIL">
+ <HintPath>..\..\..\..\packages\sqlite-net-pcl.1.7.335\lib\netstandard2.0\SQLite-net.dll</HintPath>
+ </Reference>
+ <Reference Include="SQLitePCLRaw.batteries_v2, Version=2.0.3.851, Culture=neutral, PublicKeyToken=8226ea5df37bcae9, processorArchitecture=MSIL">
+ <HintPath>..\..\..\..\packages\SQLitePCLRaw.bundle_green.2.0.3\lib\net461\SQLitePCLRaw.batteries_v2.dll</HintPath>
+ </Reference>
+ <Reference Include="SQLitePCLRaw.core, Version=2.0.3.851, Culture=neutral, PublicKeyToken=1488e028ca7ab535, processorArchitecture=MSIL">
+ <HintPath>..\..\..\..\packages\SQLitePCLRaw.core.2.0.3\lib\netstandard2.0\SQLitePCLRaw.core.dll</HintPath>
+ </Reference>
+ <Reference Include="SQLitePCLRaw.nativelibrary, Version=2.0.3.851, Culture=neutral, PublicKeyToken=502ed628492ab262, processorArchitecture=MSIL">
+ <HintPath>..\..\..\..\packages\SQLitePCLRaw.bundle_green.2.0.3\lib\net461\SQLitePCLRaw.nativelibrary.dll</HintPath>
+ </Reference>
+ <Reference Include="SQLitePCLRaw.provider.dynamic_cdecl, Version=2.0.3.851, Culture=neutral, PublicKeyToken=b68184102cba0b3b, processorArchitecture=MSIL">
+ <HintPath>..\..\..\..\packages\SQLitePCLRaw.provider.dynamic_cdecl.2.0.3\lib\netstandard2.0\SQLitePCLRaw.provider.dynamic_cdecl.dll</HintPath>
+ </Reference>
<Reference Include="System" />
+ <Reference Include="System.Buffers, Version=4.0.2.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
+ <HintPath>..\..\..\..\packages\System.Buffers.4.4.0\lib\netstandard2.0\System.Buffers.dll</HintPath>
+ </Reference>
<Reference Include="System.Core" />
+ <Reference Include="System.Memory, Version=4.0.1.1, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51, processorArchitecture=MSIL">
+ <HintPath>..\..\..\..\packages\System.Memory.4.5.3\lib\netstandard2.0\System.Memory.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Numerics" />
+ <Reference Include="System.Numerics.Vectors, Version=4.1.3.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\..\..\..\packages\System.Numerics.Vectors.4.4.0\lib\net46\System.Numerics.Vectors.dll</HintPath>
+ </Reference>
+ <Reference Include="System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a, processorArchitecture=MSIL">
+ <HintPath>..\..\..\..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll</HintPath>
+ </Reference>
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
- <Reference Include="uplink.NET, Version=2.3.4.0, Culture=neutral, processorArchitecture=MSIL">
- <HintPath>..\..\..\..\packages\uplink.NET.2.3.4\lib\netstandard2.0\uplink.NET.dll</HintPath>
+ <Reference Include="uplink.NET, Version=2.7.0.0, Culture=neutral, processorArchitecture=MSIL">
+ <HintPath>..\..\..\..\packages\uplink.NET.2.7.1604\lib\netstandard2.0\uplink.NET.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup>
- <Compile Include="TardigradeFile.cs" />
<Compile Include="Strings.cs" />
<Compile Include="TardigradeBackend.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
- <Compile Include="TardigradeConfig.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Common\Duplicati.Library.Common.csproj">
@@ -71,29 +97,20 @@
<Project>{de3e5d4c-51ab-4e5e-bee8-e636cebfba65}</Project>
<Name>Duplicati.Library.Utility</Name>
</ProjectReference>
+ <ProjectReference Include="..\Storj\Duplicati.Library.Backend.Storj.csproj">
+ <Project>{ae035e01-c917-4f13-a35e-78f21c1a2f17}</Project>
+ <Name>Duplicati.Library.Backend.Storj</Name>
+ </ProjectReference>
</ItemGroup>
<ItemGroup>
- <None Include="libstorj_uplink.dylib">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </None>
- <None Include="libstorj_uplink.so">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </None>
<None Include="packages.config" />
</ItemGroup>
- <ItemGroup>
- <Content Include="win-x64\storj_uplink.dll">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="win-x86\storj_uplink.dll">
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- </ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Target Name="EnsureNuGetPackageBuildImports" BeforeTargets="PrepareForBuild">
<PropertyGroup>
<ErrorText>Dieses Projekt verweist auf mindestens ein NuGet-Paket, das auf diesem Computer fehlt. Verwenden Sie die Wiederherstellung von NuGet-Paketen, um die fehlenden Dateien herunterzuladen. Weitere Informationen finden Sie unter "http://go.microsoft.com/fwlink/?LinkID=322105". Die fehlende Datei ist "{0}".</ErrorText>
</PropertyGroup>
- <Error Condition="!Exists('..\..\..\..\packages\uplink.NET.2.3.4\build\net40\uplink.NET.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\uplink.NET.2.3.4\build\net40\uplink.NET.targets'))" />
+ <Error Condition="!Exists('..\..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.2.0.3\build\net461\SQLitePCLRaw.lib.e_sqlite3.targets')" Text="$([System.String]::Format('$(ErrorText)', '..\..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.2.0.3\build\net461\SQLitePCLRaw.lib.e_sqlite3.targets'))" />
</Target>
-</Project>
+ <Import Project="..\..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.2.0.3\build\net461\SQLitePCLRaw.lib.e_sqlite3.targets" Condition="Exists('..\..\..\..\packages\SQLitePCLRaw.lib.e_sqlite3.2.0.3\build\net461\SQLitePCLRaw.lib.e_sqlite3.targets')" />
+</Project> \ No newline at end of file
diff --git a/Duplicati/Library/Backend/Tardigrade/Properties/AssemblyInfo.cs b/Duplicati/Library/Backend/Tardigrade/Properties/AssemblyInfo.cs
index 5f3cc907e..762e1e0c9 100644
--- a/Duplicati/Library/Backend/Tardigrade/Properties/AssemblyInfo.cs
+++ b/Duplicati/Library/Backend/Tardigrade/Properties/AssemblyInfo.cs
@@ -20,7 +20,7 @@ using System.Runtime.InteropServices;
[assembly: ComVisible(false)]
// Die folgende GUID bestimmt die ID der Typbibliothek, wenn dieses Projekt für COM verfügbar gemacht wird
-[assembly: Guid("ae035e01-c917-4f13-a35e-78f21c1a2f17")]
+[assembly: Guid("9A04CB37-DA72-4008-9703-3AC5191974E9")]
// Versionsinformationen für eine Assembly bestehen aus den folgenden vier Werten:
//
diff --git a/Duplicati/Library/Backend/Tardigrade/Strings.cs b/Duplicati/Library/Backend/Tardigrade/Strings.cs
index 7540c2859..3703872f2 100644
--- a/Duplicati/Library/Backend/Tardigrade/Strings.cs
+++ b/Duplicati/Library/Backend/Tardigrade/Strings.cs
@@ -9,22 +9,7 @@ namespace Duplicati.Library.Backend.Strings
{
internal static class Tardigrade
{
- public static string DisplayName { get { return LC.L(@"Tardigrade Decentralized Cloud Storage"); } }
- public static string Description { get { return LC.L(@"This backend can read and write data to the Tardigrade Decentralized Cloud Storage."); } }
- public static string TestConnectionFailed { get { return LC.L(@"The connection-test failed."); } }
- public static string TardigradeAuthMethodDescriptionShort { get { return LC.L(@"The authentication method"); } }
- public static string TardigradeAuthMethodDescriptionLong { get { return LC.L(@"The authentication method describes which way to use to connect to the network - either via API key or via an access grant."); } }
- public static string TardigradeSatelliteDescriptionShort { get { return LC.L(@"The satellite"); } }
- public static string TardigradeSatelliteDescriptionLong { get { return LC.L(@"The satellite that keeps track of all metadata. Use a Tardigrade-grade server for high-performance SLA-backed connectivity or use a community server. Or even host your own."); } }
- public static string TardigradeAPIKeyDescriptionShort { get { return LC.L(@"The API key"); } }
- public static string TardigradeAPIKeyDescriptionLong { get { return LC.L(@"The API key grants access to a specific project on your chosen satellite. Head over to the dashboard of your satellite to create one if you do not already have an API key."); } }
- public static string TardigradeSecretDescriptionShort { get { return LC.L(@"The encryption passphrase"); } }
- public static string TardigradeSecretDescriptionLong { get { return LC.L(@"The encryption passphrase is used to encrypt your data before sending it to the tardigrade network. This passphrase can be the only secret to provide - for Tardigrade you do not necessary need any additional encryption (from Duplicati) in place."); } }
- public static string TardigradeSharedAccessDescriptionShort { get { return LC.L(@"The access grant"); } }
- public static string TardigradeSharedAccessDescriptionLong { get { return LC.L(@"An access grant contains all information in one encrypted string. You may use it instead of a satellite, API key and secret."); } }
- public static string TardigradeBucketDescriptionShort { get { return LC.L(@"The bucket"); } }
- public static string TardigradeBucketDescriptionLong { get { return LC.L(@"The bucket where the backup will reside in."); } }
- public static string TardigradeFolderDescriptionShort { get { return LC.L(@"The folder"); } }
- public static string TardigradeFolderDescriptionLong { get { return LC.L(@"The folder within the bucket where the backup will reside in."); } }
+ public static string DisplayName { get { return LC.L(@"Tardigrade Decentralised Cloud Storage (Deprecated)"); } }
+ public static string Description { get { return LC.L(@"This backend can read and write data to the Tardigrade Decentralized Cloud Storage. It is deprecated - please move over to the new Storj DCS. (Deprecated)"); } }
}
}
diff --git a/Duplicati/Library/Backend/Tardigrade/TardigradeBackend.cs b/Duplicati/Library/Backend/Tardigrade/TardigradeBackend.cs
index 0bf808105..255830500 100644
--- a/Duplicati/Library/Backend/Tardigrade/TardigradeBackend.cs
+++ b/Duplicati/Library/Backend/Tardigrade/TardigradeBackend.cs
@@ -14,7 +14,15 @@ using uplink.NET.Services;
namespace Duplicati.Library.Backend.Tardigrade
{
- public class Tardigrade : IStreamingBackend
+
+ /// <summary>
+ /// This backend is deprecated! It will be removed in the future.
+ /// Tardigrade renamed to Storj DCS in Spring 2021 - but existing Tardigrade-Configurations could not be easily renamed.
+ /// So we decided to "copy" Tardigrade over to the new name Storj DCS. In order to reduce duplicate code, the old
+ /// Tardigrade-Backend hands it's logic over to Storj DCS. Only the UI-specific part, the config-parameters and
+ /// the protocol-key stay here named for Tardigrade.
+ /// </summary>
+ public class Tardigrade : Duplicati.Library.Backend.Storj.Storj, IStreamingBackend
{
private const string TARDIGRADE_AUTH_METHOD = "tardigrade-auth-method";
private const string TARDIGRADE_SATELLITE = "tardigrade-satellite";
@@ -23,339 +31,50 @@ namespace Duplicati.Library.Backend.Tardigrade
private const string TARDIGRADE_SHARED_ACCESS = "tardigrade-shared-access";
private const string TARDIGRADE_BUCKET = "tardigrade-bucket";
private const string TARDIGRADE_FOLDER = "tardigrade-folder";
- private const string PROTOCOL_KEY = "tardigrade";
- private const string TARDIGRADE_PARTNER_ID = "duplicati";
-
- private readonly string _satellite;
- private readonly string _api_key;
- private readonly string _secret;
- private readonly string _bucket;
- private readonly string _folder;
- private Access _access;
- private IBucketService _bucketService;
- private IObjectService _objectService;
-
- public static readonly Dictionary<string, string> KNOWN_TARDIGRADE_SATELLITES = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase){
- { "US Central 1", "us-central-1.tardigrade.io:7777" },
- { "Asia East 1", "asia-east-1.tardigrade.io:7777" },
- { "Saltlake", "saltlake.tardigrade.io:7777" },
- { "Europe West 1", "europe-west-1.tardigrade.io:7777" },
- { "Europe North 1", "europe-north-1.tardigrade.io:7777" },
- };
-
- public static readonly Dictionary<string, string> KNOWN_AUTHENTICATION_METHODS = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase){
- { "API key", "API key" },
- { "Access grant", "Access grant" },
- };
-
- [DllImport("kernel32.dll")]
- protected static extern IntPtr LoadLibrary(string filename);
-
- private static bool _libraryLoaded = false;
- private static void InitStorjLibrary()
- {
- if (_libraryLoaded)
- return;
- if (Duplicati.Library.Common.Platform.IsClientWindows) //We need to init only on Windows to distinguish between x64 and x86
- {
- if (System.Environment.Is64BitProcess)
- {
- var res = LoadLibrary("win-x64/storj_uplink.dll");
- }
- else
- {
- var res = LoadLibrary("win-x86/storj_uplink.dll");
- }
- }
- Access.SetTempDirectory(Library.Utility.TempFolder.SystemTempPath);
- _libraryLoaded = true;
- }
+ private const string PROTOCOL_KEY = "tardigrade";
// ReSharper disable once UnusedMember.Global
// This constructor is needed by the BackendLoader.
- public Tardigrade()
+ public Tardigrade():base()
{
}
// ReSharper disable once UnusedMember.Global
// This constructor is needed by the BackendLoader.
- public Tardigrade(string url, Dictionary<string, string> options)
+ public Tardigrade(string url, Dictionary<string, string> options) :base(url, options)
{
- InitStorjLibrary();
-
- var auth_method = options[TARDIGRADE_AUTH_METHOD];
- if (auth_method == "Access grant")
- {
- //Create an access from the access grant
- var shared_access = options[TARDIGRADE_SHARED_ACCESS];
- _access = new Access(shared_access, new Config() { UserAgent = TARDIGRADE_PARTNER_ID });
- }
- else
- {
- //Create an access for a satellite, API key and encryption passphrase
- _satellite = options[TARDIGRADE_SATELLITE];
-
- if (options.ContainsKey(TARDIGRADE_API_KEY))
- {
- _api_key = options[TARDIGRADE_API_KEY];
- }
- if (options.ContainsKey(TARDIGRADE_SECRET))
- {
- _secret = options[TARDIGRADE_SECRET];
- }
-
- _access = new Access(_satellite, _api_key, _secret, new Config() { UserAgent = TARDIGRADE_PARTNER_ID });
- }
-
- _bucketService = new BucketService(_access);
- _objectService = new ObjectService(_access);
-
- //If no bucket was provided use the default "duplicati"-bucket
- if (options.ContainsKey(TARDIGRADE_BUCKET))
- {
- _bucket = options[TARDIGRADE_BUCKET];
- }
- else
- {
- _bucket = "duplicati";
- }
-
- if (options.ContainsKey(TARDIGRADE_FOLDER))
- {
- _folder = options[TARDIGRADE_FOLDER];
- }
}
- public string DisplayName
+ public new string DisplayName
{
get { return Strings.Tardigrade.DisplayName; }
}
- public string ProtocolKey => PROTOCOL_KEY;
+ public new string ProtocolKey => PROTOCOL_KEY;
- public IList<ICommandLineArgument> SupportedCommands
+ public new IList<ICommandLineArgument> SupportedCommands
{
get
{
return new List<ICommandLineArgument>(new ICommandLineArgument[] {
- new CommandLineArgument(TARDIGRADE_AUTH_METHOD, CommandLineArgument.ArgumentType.String, Strings.Tardigrade.TardigradeAuthMethodDescriptionShort, Strings.Tardigrade.TardigradeAuthMethodDescriptionLong, "API key"),
- new CommandLineArgument(TARDIGRADE_SATELLITE, CommandLineArgument.ArgumentType.String, Strings.Tardigrade.TardigradeSatelliteDescriptionShort, Strings.Tardigrade.TardigradeSatelliteDescriptionLong, "us-central-1.tardigrade.io:7777"),
- new CommandLineArgument(TARDIGRADE_API_KEY, CommandLineArgument.ArgumentType.String, Strings.Tardigrade.TardigradeAPIKeyDescriptionShort, Strings.Tardigrade.TardigradeAPIKeyDescriptionLong),
- new CommandLineArgument(TARDIGRADE_SECRET, CommandLineArgument.ArgumentType.Password, Strings.Tardigrade.TardigradeSecretDescriptionShort, Strings.Tardigrade.TardigradeSecretDescriptionLong),
- new CommandLineArgument(TARDIGRADE_SHARED_ACCESS, CommandLineArgument.ArgumentType.String, Strings.Tardigrade.TardigradeSharedAccessDescriptionShort, Strings.Tardigrade.TardigradeSharedAccessDescriptionLong),
- new CommandLineArgument(TARDIGRADE_BUCKET, CommandLineArgument.ArgumentType.String, Strings.Tardigrade.TardigradeBucketDescriptionShort, Strings.Tardigrade.TardigradeBucketDescriptionLong),
- new CommandLineArgument(TARDIGRADE_FOLDER, CommandLineArgument.ArgumentType.String, Strings.Tardigrade.TardigradeFolderDescriptionShort, Strings.Tardigrade.TardigradeFolderDescriptionLong),
+ new CommandLineArgument(TARDIGRADE_AUTH_METHOD, CommandLineArgument.ArgumentType.String, Strings.Storj.StorjAuthMethodDescriptionShort, Strings.Storj.StorjAuthMethodDescriptionLong, "API key", null, null),
+ new CommandLineArgument(TARDIGRADE_SATELLITE, CommandLineArgument.ArgumentType.String, Strings.Storj.StorjSatelliteDescriptionShort, Strings.Storj.StorjSatelliteDescriptionLong, "us1.storj.io:7777", null, null),
+ new CommandLineArgument(TARDIGRADE_API_KEY, CommandLineArgument.ArgumentType.String, Strings.Storj.StorjAPIKeyDescriptionShort, Strings.Storj.StorjAPIKeyDescriptionLong, null, null, null),
+ new CommandLineArgument(TARDIGRADE_SECRET, CommandLineArgument.ArgumentType.Password, Strings.Storj.StorjSecretDescriptionShort, Strings.Storj.StorjSecretDescriptionLong, null, null, null),
+ new CommandLineArgument(TARDIGRADE_SHARED_ACCESS, CommandLineArgument.ArgumentType.String, Strings.Storj.StorjSharedAccessDescriptionShort, Strings.Storj.StorjSharedAccessDescriptionLong, null, null, null),
+ new CommandLineArgument(TARDIGRADE_BUCKET, CommandLineArgument.ArgumentType.String, Strings.Storj.StorjBucketDescriptionShort, Strings.Storj.StorjBucketDescriptionLong, null, null, null),
+ new CommandLineArgument(TARDIGRADE_FOLDER, CommandLineArgument.ArgumentType.String, Strings.Storj.StorjFolderDescriptionShort, Strings.Storj.StorjFolderDescriptionLong, null, null, null),
});
}
}
- public string Description
+ public new string Description
{
get
{
return Strings.Tardigrade.Description;
}
}
-
- public string[] DNSName
- {
- get
- {
- return new string[0];
- }
- }
-
- public void CreateFolder()
- {
- //Tardigrade has no folders
- }
-
- public void Delete(string remotename)
- {
- var deleteTask = DeleteAsync(remotename);
- deleteTask.Wait();
- }
-
- public async Task DeleteAsync(string remotename)
- {
- try
- {
- var bucket = await _bucketService.EnsureBucketAsync(_bucket);
- await _objectService.DeleteObjectAsync(bucket, GetBasePath() + remotename);
- }
- catch (Exception root)
- {
- throw new FileMissingException(root);
- }
- }
-
- public void Dispose()
- {
- if (_objectService != null)
- {
- _objectService = null;
- }
- if (_bucketService != null)
- {
- _bucketService = null;
- }
- if (_access != null)
- {
- _access.Dispose();
- _access = null;
- }
- }
-
- public void Get(string remotename, string filename)
- {
- var getTask = GetAsync(remotename, filename);
- getTask.Wait();
- }
-
- public async Task GetAsync(string remotename, string filename)
- {
- var bucket = await _bucketService.EnsureBucketAsync(_bucket);
- var download = await _objectService.DownloadObjectAsync(bucket, GetBasePath() + remotename, new DownloadOptions(), false);
- await download.StartDownloadAsync();
-
- if (download.Completed)
- {
- using (FileStream file = new FileStream(filename, FileMode.Create))
- {
- await file.WriteAsync(download.DownloadedBytes, 0, (int)download.BytesReceived);
- await file.FlushAsync().ConfigureAwait(false);
- }
- }
- }
-
- public void Get(string remotename, Stream stream)
- {
- var getTask = GetAsync(remotename, stream);
- getTask.Wait();
- }
-
- public async Task GetAsync(string remotename, Stream stream)
- {
- int index = 0;
- var bucket = await _bucketService.EnsureBucketAsync(_bucket);
- var download = await _objectService.DownloadObjectAsync(bucket, GetBasePath() + remotename, new DownloadOptions(), false);
- download.DownloadOperationProgressChanged += (op) =>
- {
- int newPartLength = (int)op.BytesReceived - index;
- byte[] newPart = new byte[newPartLength];
- Array.Copy(op.DownloadedBytes, index, newPart, 0, newPartLength);
- stream.Write(newPart, 0, newPartLength);
- index = index + newPartLength;
- };
- await download.StartDownloadAsync();
- }
-
- public IEnumerable<IFileEntry> List()
- {
- var listTask = ListAsync();
- listTask.Wait();
- return listTask.Result;
- }
-
- private async Task<IEnumerable<IFileEntry>> ListAsync()
- {
- List<TardigradeFile> files = new List<TardigradeFile>();
- var bucket = await _bucketService.EnsureBucketAsync(_bucket);
- var prefix = GetBasePath();
- var objects = await _objectService.ListObjectsAsync(bucket, new ListObjectsOptions { Recursive = true, System = true, Custom = true, Prefix = prefix });
-
- foreach (var obj in objects.Items)
- {
- TardigradeFile file = new TardigradeFile(obj);
- if (prefix != "")
- {
- file.Name = file.Name.Replace(prefix, "");
- }
- files.Add(file);
- }
-
- return files;
- }
-
- public async Task PutAsync(string remotename, string filename, CancellationToken cancelToken)
- {
- using (FileStream fs = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read))
- await PutAsync(remotename, fs, cancelToken);
- }
-
- public async Task PutAsync(string remotename, Stream stream, CancellationToken cancelToken)
- {
- var bucket = await _bucketService.EnsureBucketAsync(_bucket);
- CustomMetadata custom = new CustomMetadata();
- custom.Entries.Add(new CustomMetadataEntry { Key = TardigradeFile.TARDIGRADE_LAST_ACCESS, Value = DateTime.Now.ToUniversalTime().ToString("O") });
- custom.Entries.Add(new CustomMetadataEntry { Key = TardigradeFile.TARDIGRADE_LAST_MODIFICATION, Value = DateTime.Now.ToUniversalTime().ToString("O") });
- var upload = await _objectService.UploadObjectAsync(bucket, GetBasePath() + remotename, new UploadOptions(), stream, custom, false);
- await upload.StartUploadAsync();
- }
-
- public void Test()
- {
- var testTask = TestAsync();
- testTask.Wait(10000);
- if (!testTask.Result)
- {
- throw new Exception(Strings.Tardigrade.TestConnectionFailed);
- }
- }
-
- /// <summary>
- /// Test the connection by:
- /// - creating the bucket (if it not already exists)
- /// - uploading 256 random bytes to a test-file
- /// - downloading the file back and expecting 256 bytes
- /// </summary>
- /// <returns>true, if the test was successfull or and exception</returns>
- private async Task<bool> TestAsync()
- {
- string testFileName = GetBasePath() + "duplicati_test.dat";
-
- var bucket = await _bucketService.EnsureBucketAsync(_bucket);
- var upload = await _objectService.UploadObjectAsync(bucket, testFileName, new UploadOptions(), GetRandomBytes(256), false);
- await upload.StartUploadAsync();
-
- var download = await _objectService.DownloadObjectAsync(bucket, testFileName, new DownloadOptions(), false);
- await download.StartDownloadAsync();
-
- await _objectService.DeleteObjectAsync(bucket, testFileName);
-
- if (download.Failed || download.BytesReceived != 256)
- {
- throw new Exception(download.ErrorMessage);
- }
-
- return true;
- }
-
- /// <summary>
- /// Gets the base path - depending on there is a folder set or not
- /// </summary>
- /// <returns>The base path within a bucket where the backup shall be placed</returns>
- private string GetBasePath()
- {
- if (!string.IsNullOrEmpty(_folder))
- return _folder + "/";
- else
- return "";
- }
-
- /// <summary>
- /// Creates some random bytes with the given length - just for testing the connection
- /// </summary>
- /// <param name="length">The length of the bytes to create</param>
- /// <returns>A byte-array with the given length</returns>
- private static byte[] GetRandomBytes(long length)
- {
- byte[] bytes = new byte[length];
- Random rand = new Random();
- rand.NextBytes(bytes);
-
- return bytes;
- }
}
}
diff --git a/Duplicati/Library/Backend/Tardigrade/packages.config b/Duplicati/Library/Backend/Tardigrade/packages.config
index 51d5eec31..983b0e7cc 100644
--- a/Duplicati/Library/Backend/Tardigrade/packages.config
+++ b/Duplicati/Library/Backend/Tardigrade/packages.config
@@ -1,4 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="uplink.NET" version="2.3.4" targetFramework="net471" />
+ <package id="sqlite-net-pcl" version="1.7.335" targetFramework="net471" />
+ <package id="SQLitePCLRaw.bundle_green" version="2.0.3" targetFramework="net471" />
+ <package id="SQLitePCLRaw.core" version="2.0.3" targetFramework="net471" />
+ <package id="SQLitePCLRaw.lib.e_sqlite3" version="2.0.3" targetFramework="net471" />
+ <package id="SQLitePCLRaw.provider.dynamic_cdecl" version="2.0.3" targetFramework="net471" />
+ <package id="System.Buffers" version="4.4.0" targetFramework="net471" />
+ <package id="System.Memory" version="4.5.3" targetFramework="net471" />
+ <package id="System.Numerics.Vectors" version="4.4.0" targetFramework="net471" />
+ <package id="System.Runtime.CompilerServices.Unsafe" version="4.5.2" targetFramework="net471" />
+ <package id="uplink.NET" version="2.7.1604" targetFramework="net471" />
</packages> \ No newline at end of file
diff --git a/Duplicati/Library/Backend/TencentCOS/COSBackend.cs b/Duplicati/Library/Backend/TencentCOS/COSBackend.cs
index 59d5a719b..5951c50b3 100644
--- a/Duplicati/Library/Backend/TencentCOS/COSBackend.cs
+++ b/Duplicati/Library/Backend/TencentCOS/COSBackend.cs
@@ -196,10 +196,10 @@ namespace Duplicati.Library.Backend.TencentCOS
}
}
- public Task PutAsync(string remotename, string filename, CancellationToken cancelToken)
+ public async Task PutAsync(string remotename, string filename, CancellationToken cancelToken)
{
using (FileStream fs = File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read))
- return PutAsync(remotename, fs, cancelToken);
+ await PutAsync(remotename, fs, cancelToken);
}
public void Get(string remotename, string filename)
diff --git a/Duplicati/Library/Backend/WEBDAV/WEBDAV.cs b/Duplicati/Library/Backend/WEBDAV/WEBDAV.cs
index 5e1f0b205..1f40b8f00 100644
--- a/Duplicati/Library/Backend/WEBDAV/WEBDAV.cs
+++ b/Duplicati/Library/Backend/WEBDAV/WEBDAV.cs
@@ -256,10 +256,10 @@ namespace Duplicati.Library.Backend
return files;
}
- public Task PutAsync(string remotename, string filename, CancellationToken cancelToken)
+ public async Task PutAsync(string remotename, string filename, CancellationToken cancelToken)
{
using (System.IO.FileStream fs = System.IO.File.OpenRead(filename))
- return PutAsync(remotename, fs, cancelToken);
+ await PutAsync(remotename, fs, cancelToken);
}
public void Get(string remotename, string filename)
diff --git a/Duplicati/Library/Interface/CustomExceptions.cs b/Duplicati/Library/Interface/CustomExceptions.cs
index 2e0c90262..1dbc0ff63 100644
--- a/Duplicati/Library/Interface/CustomExceptions.cs
+++ b/Duplicati/Library/Interface/CustomExceptions.cs
@@ -183,4 +183,20 @@ namespace Duplicati.Library.Interface
AbortReason = reason;
}
}
+
+ /// <summary>
+ /// An exception indicating that verification of uploaded volumes has failed
+ /// due to extra, missing, or duplicate files.
+ /// </summary>
+ [Serializable]
+ public class RemoteListVerificationException : UserInformationException
+ {
+ public RemoteListVerificationException(string message, string helpId)
+ : base(message, helpId)
+ {}
+
+ public RemoteListVerificationException(string message, string helpId, Exception innerException)
+ : base(message, helpId, innerException)
+ {}
+ }
}
diff --git a/Duplicati/Library/Main/Controller.cs b/Duplicati/Library/Main/Controller.cs
index 18669a994..5b319fab3 100644
--- a/Duplicati/Library/Main/Controller.cs
+++ b/Duplicati/Library/Main/Controller.cs
@@ -816,8 +816,12 @@ namespace Duplicati.Library.Main
if (string.Equals(m_options.CompressionModule, "7z", StringComparison.OrdinalIgnoreCase))
Logging.Log.WriteWarningMessage(LOGTAG, "7zModuleHasIssues", null, "The 7z compression module has known issues and should only be used for experimental purposes");
- //TODO: Based on the action, see if all options are relevant
- }
+ //Inform the user about the deprecated Tardigrade-Backend. They should switch to Storj DCS instead.
+ if (string.Equals(new Library.Utility.Uri(m_backend).Scheme, "tardigrade", StringComparison.OrdinalIgnoreCase))
+ Logging.Log.WriteWarningMessage(LOGTAG, "TardigradeRename", null, "The Tardigrade-backend got renamed to Storj DCS - please migrate your backups to the new configuration by changing the destination storage type to Storj DCS.");
+
+ //TODO: Based on the action, see if all options are relevant
+ }
/// <summary>
/// Helper method that expands the users chosen source input paths,
diff --git a/Duplicati/Library/Main/Operation/BackupHandler.cs b/Duplicati/Library/Main/Operation/BackupHandler.cs
index f78246cdd..ef12b94e8 100644
--- a/Duplicati/Library/Main/Operation/BackupHandler.cs
+++ b/Duplicati/Library/Main/Operation/BackupHandler.cs
@@ -156,7 +156,7 @@ namespace Duplicati.Library.Main.Operation
else
FilelistProcessor.VerifyRemoteList(backend, m_options, m_database, m_result.BackendWriter, new string[] { protectedfile });
}
- catch (Exception ex)
+ catch (RemoteListVerificationException ex)
{
if (m_options.AutoCleanup)
{
diff --git a/Duplicati/Library/Main/Operation/FilelistProcessor.cs b/Duplicati/Library/Main/Operation/FilelistProcessor.cs
index 643c0110f..0652020d8 100644
--- a/Duplicati/Library/Main/Operation/FilelistProcessor.cs
+++ b/Duplicati/Library/Main/Operation/FilelistProcessor.cs
@@ -113,7 +113,7 @@ namespace Duplicati.Library.Main.Operation
{
var s = string.Format("Found {0} remote files that are not recorded in local storage, please run repair", extraCount);
Logging.Log.WriteErrorMessage(LOGTAG, "ExtraRemoteFiles", null, s);
- throw new Duplicati.Library.Interface.UserInformationException(s, "ExtraRemoteFiles");
+ throw new RemoteListVerificationException(s, "ExtraRemoteFiles");
}
ISet<string> doubles;
@@ -123,7 +123,7 @@ namespace Duplicati.Library.Main.Operation
{
var s = string.Format("Found remote files reported as duplicates, either the backend module is broken or you need to manually remove the extra copies.\nThe following files were found multiple times: {0}", string.Join(", ", doubles));
Logging.Log.WriteErrorMessage(LOGTAG, "DuplicateRemoteFiles", null, s);
- throw new Duplicati.Library.Interface.UserInformationException(s, "DuplicateRemoteFiles");
+ throw new RemoteListVerificationException(s, "DuplicateRemoteFiles");
}
if (missingCount > 0)
@@ -135,7 +135,7 @@ namespace Duplicati.Library.Main.Operation
s = string.Format("Found {0} files that are missing from the remote storage, please run repair", missingCount);
Logging.Log.WriteErrorMessage(LOGTAG, "MissingRemoteFiles", null, s);
- throw new Duplicati.Library.Interface.UserInformationException(s, "MissingRemoteFiles");
+ throw new RemoteListVerificationException(s, "MissingRemoteFiles");
}
}
@@ -279,7 +279,7 @@ namespace Duplicati.Library.Main.Operation
if (e.Value == RemoteVolumeState.Uploading || e.Value == RemoteVolumeState.Temporary)
database.UnlinkRemoteVolume(e.Key, e.Value);
else
- throw new Exception(string.Format("The remote volume {0} appears in the database with state {1} and a deleted state, cannot continue", e.Key, e.Value.ToString()));
+ throw new RemoteListVerificationException(string.Format("The remote volume {0} appears in the database with state {1} and a deleted state, cannot continue", e.Key, e.Value.ToString()), "AmbiguousStateRemoteFiles");
}
var locallist = database.GetRemoteVolumes();
diff --git a/Duplicati/Library/Modules/Builtin/Duplicati.Library.Modules.Builtin.csproj b/Duplicati/Library/Modules/Builtin/Duplicati.Library.Modules.Builtin.csproj
index e6c962ce9..559c21c80 100644
--- a/Duplicati/Library/Modules/Builtin/Duplicati.Library.Modules.Builtin.csproj
+++ b/Duplicati/Library/Modules/Builtin/Duplicati.Library.Modules.Builtin.csproj
@@ -38,9 +38,6 @@
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<ItemGroup>
- <Reference Include="BouncyCastle.Crypto, Version=1.8.5.0, Culture=neutral, PublicKeyToken=0e99375e54769942">
- <HintPath>..\..\..\..\packages\BouncyCastle.1.8.5\lib\BouncyCastle.Crypto.dll</HintPath>
- </Reference>
<Reference Include="MailKit, Version=2.3.0.0, Culture=neutral, PublicKeyToken=4e064fe7c44a8f1b, processorArchitecture=MSIL">
<HintPath>..\..\..\..\packages\MailKit.2.3.1.6\lib\net46\MailKit.dll</HintPath>
</Reference>
diff --git a/Duplicati/Library/Modules/Builtin/packages.config b/Duplicati/Library/Modules/Builtin/packages.config
index 5733784ee..743c5b29d 100644
--- a/Duplicati/Library/Modules/Builtin/packages.config
+++ b/Duplicati/Library/Modules/Builtin/packages.config
@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="BouncyCastle" version="1.8.5" targetFramework="net471" />
<package id="MailKit" version="2.3.1.6" targetFramework="net471" />
<package id="MimeKit" version="2.3.1" targetFramework="net471" />
<package id="Newtonsoft.Json" version="12.0.2" targetFramework="net471" />
diff --git a/Duplicati/Library/Utility/FilterExpression.cs b/Duplicati/Library/Utility/FilterExpression.cs
index fc4eb1745..60ca22588 100644
--- a/Duplicati/Library/Utility/FilterExpression.cs
+++ b/Duplicati/Library/Utility/FilterExpression.cs
@@ -81,14 +81,15 @@ namespace Duplicati.Library.Utility
/// The multiple wildcard character (DOS style)
/// </summary>
private const char MULTIPLE_WILDCARD = '*';
-
+
/// <summary>
/// The regular expression flags
/// </summary>
private static readonly RegexOptions REGEXP_OPTIONS =
RegexOptions.Compiled |
RegexOptions.ExplicitCapture |
- (Utility.IsFSCaseSensitive ? RegexOptions.None : RegexOptions.IgnoreCase);
+ (Utility.IsFSCaseSensitive ? RegexOptions.None : RegexOptions.IgnoreCase) |
+ RegexOptions.Singleline;
// Since we might need to get the regex for a particular filter group multiple times
// (e.g., when combining multiple FilterExpressions together, which discards the existing FilterEntries and recreates them from the Filter representations),
diff --git a/Duplicati/License/Duplicati.License.csproj b/Duplicati/License/Duplicati.License.csproj
index c579b5450..6ff901c70 100644
--- a/Duplicati/License/Duplicati.License.csproj
+++ b/Duplicati/License/Duplicati.License.csproj
@@ -70,6 +70,18 @@
<Link>licenses\gpg\License.txt</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
+ <Content Include="..\..\thirdparty\Otp.NET\Homepage.txt">
+ <Link>licenses\Otp.NET\Homepage.txt</Link>
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="..\..\thirdparty\Otp.NET\license.txt">
+ <Link>licenses\Otp.NET\license.txt</Link>
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
+ <Content Include="..\..\thirdparty\Otp.NET\licensedata.json">
+ <Link>licenses\Otp.NET\licensedata.json</Link>
+ <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
+ </Content>
<Content Include="..\..\thirdparty\SharpCompress\download.txt">
<Link>licenses\SharpCompress\download.txt</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@@ -344,18 +356,6 @@
<Link>licenses\MimeKit\licensedata.json</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
- <Content Include="..\..\thirdparty\BoncyCastle\Homepage.txt">
- <Link>licenses\BouncyCastle\Homepage.txt</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\..\thirdparty\BoncyCastle\license.txt">
- <Link>licenses\BouncyCastle\license.txt</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
- <Content Include="..\..\thirdparty\BoncyCastle\licensedata.json">
- <Link>licenses\BouncyCastle\licensedata.json</Link>
- <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
- </Content>
<Content Include="..\..\thirdparty\angular-gettext\Homepage.txt">
<Link>licenses\AngularGettext\Homepage.txt</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
@@ -448,4 +448,4 @@
<Name>Duplicati.Library.Utility</Name>
</ProjectReference>
</ItemGroup>
-</Project>
+</Project> \ No newline at end of file
diff --git a/Duplicati/Server/Database/ServerSettings.cs b/Duplicati/Server/Database/ServerSettings.cs
index 7755d51e5..f39379867 100644
--- a/Duplicati/Server/Database/ServerSettings.cs
+++ b/Duplicati/Server/Database/ServerSettings.cs
@@ -20,9 +20,6 @@ using System.Linq;
using System.Collections.Generic;
using System.Security.Cryptography;
using System.Security.Cryptography.X509Certificates;
-using Org.BouncyCastle.Crypto.Parameters;
-using Org.BouncyCastle.Pkcs;
-using Org.BouncyCastle.Security;
using Duplicati.Library.Common;
namespace Duplicati.Server.Database
@@ -498,25 +495,7 @@ namespace Duplicati.Server.Database
if (Platform.IsClientWindows)
return new X509Certificate2(Convert.FromBase64String(settings[CONST.SERVER_SSL_CERTIFICATE]));
else
- {
- var store = new Pkcs12Store();
-
- using (var stream = new System.IO.MemoryStream(Convert.FromBase64String(settings[CONST.SERVER_SSL_CERTIFICATE])))
- store.Load(stream, null);
-
- if (store.Count != 1)
- return null;
-
- var certAlias = store.Aliases.Cast<string>().FirstOrDefault(n => store.IsKeyEntry(n));
- var cert = new X509Certificate2(DotNetUtilities.ToX509Certificate(store.GetCertificate(certAlias).Certificate).GetRawCertData());
- var rsaPriv = DotNetUtilities.ToRSA(store.GetKey(certAlias).Key as RsaPrivateCrtKeyParameters);
- var rsaPrivate = new RSACryptoServiceProvider(new CspParameters { KeyContainerName = "KeyContainer" });
-
- rsaPrivate.ImportParameters(rsaPriv.ExportParameters(true));
- cert.PrivateKey = rsaPrivate;
-
- return cert;
- }
+ return new X509Certificate2(Convert.FromBase64String(settings[CONST.SERVER_SSL_CERTIFICATE]), "");
}
set
{
@@ -531,20 +510,8 @@ namespace Duplicati.Server.Database
lock (databaseConnection.m_lock)
settings[CONST.SERVER_SSL_CERTIFICATE] = Convert.ToBase64String(value.Export(X509ContentType.Pkcs12));
else
- {
- var store = new Pkcs12Store();
-
- store.SetKeyEntry(value.FriendlyName,
- new AsymmetricKeyEntry(DotNetUtilities.GetKeyPair(value.PrivateKey).Private),
- new[] { new X509CertificateEntry(DotNetUtilities.FromX509Certificate(value)) });
-
- using (var stream = new System.IO.MemoryStream())
- {
- store.Save(stream, null, new SecureRandom());
- lock (databaseConnection.m_lock)
- settings[CONST.SERVER_SSL_CERTIFICATE] = Convert.ToBase64String(stream.ToArray());
- }
- }
+ lock (databaseConnection.m_lock)
+ settings[CONST.SERVER_SSL_CERTIFICATE] = Convert.ToBase64String(value.Export(X509ContentType.Pkcs12, ""));
}
SaveSettings();
}
diff --git a/Duplicati/Server/Duplicati.Server.csproj b/Duplicati/Server/Duplicati.Server.csproj
index 4a81dbdad..c0697798d 100644
--- a/Duplicati/Server/Duplicati.Server.csproj
+++ b/Duplicati/Server/Duplicati.Server.csproj
@@ -48,9 +48,6 @@
<ApplicationManifest>app.manifest</ApplicationManifest>
</PropertyGroup>
<ItemGroup>
- <Reference Include="BouncyCastle.Crypto, Version=1.8.5.0, Culture=neutral, PublicKeyToken=0e99375e54769942">
- <HintPath>..\..\packages\BouncyCastle.1.8.5\lib\BouncyCastle.Crypto.dll</HintPath>
- </Reference>
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\..\packages\Newtonsoft.Json.12.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
@@ -203,8 +200,12 @@
<Project>{C0270709-2A40-43B5-8CF1-69581B9FA2A1}</Project>
<Name>Duplicati.Library.Backend.TahoeLAFS</Name>
</ProjectReference>
- <ProjectReference Include="..\Library\Backend\Tardigrade\Duplicati.Library.Backend.Tardigrade.csproj">
+ <ProjectReference Include="..\Library\Backend\Storj\Duplicati.Library.Backend.Storj.csproj">
<Project>{ae035e01-c917-4f13-a35e-78f21c1a2f17}</Project>
+ <Name>Duplicati.Library.Backend.Storj</Name>
+ </ProjectReference>
+ <ProjectReference Include="..\Library\Backend\Tardigrade\Duplicati.Library.Backend.Tardigrade.csproj">
+ <Project>{9a04cb37-da72-4008-9703-3ac5191974e9}</Project>
<Name>Duplicati.Library.Backend.Tardigrade</Name>
</ProjectReference>
<ProjectReference Include="..\Library\Backend\TencentCOS\Duplicati.Library.Backend.TencentCOS.csproj">
diff --git a/Duplicati/Server/packages.config b/Duplicati/Server/packages.config
index 81a2c4f6c..5e9c8c41c 100644
--- a/Duplicati/Server/packages.config
+++ b/Duplicati/Server/packages.config
@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
- <package id="BouncyCastle" version="1.8.5" targetFramework="net471" />
<package id="Newtonsoft.Json" version="12.0.2" targetFramework="net471" />
</packages>
diff --git a/Duplicati/Server/webroot/ngax/scripts/services/EditUriBuiltins.js b/Duplicati/Server/webroot/ngax/scripts/services/EditUriBuiltins.js
index 5ed824925..63a94b64d 100644
--- a/Duplicati/Server/webroot/ngax/scripts/services/EditUriBuiltins.js
+++ b/Duplicati/Server/webroot/ngax/scripts/services/EditUriBuiltins.js
@@ -29,8 +29,9 @@ backupApp.service('EditUriBuiltins', function (AppService, AppUtils, SystemInfo,
EditUriBackendConfig.templates['box'] = 'templates/backends/oauth.html';
EditUriBackendConfig.templates['dropbox'] = 'templates/backends/oauth.html';
EditUriBackendConfig.templates['sia'] = 'templates/backends/sia.html';
+ EditUriBackendConfig.templates['storj'] = 'templates/backends/storj.html';
EditUriBackendConfig.templates['tardigrade'] = 'templates/backends/tardigrade.html';
- EditUriBackendConfig.templates['rclone'] = 'templates/backends/rclone.html';
+ EditUriBackendConfig.templates['rclone'] = 'templates/backends/rclone.html';
EditUriBackendConfig.templates['cos'] = 'templates/backends/cos.html';
EditUriBackendConfig.testers['s3'] = function(scope, callback) {
@@ -157,21 +158,47 @@ backupApp.service('EditUriBuiltins', function (AppService, AppUtils, SystemInfo,
scope.s3_client_options = s3_client_options;
};
+ EditUriBackendConfig.loaders['storj'] = function (scope) {
+ if (scope.storj_satellites == null) {
+ AppService.post('/webmodule/storj-getconfig', {'storj-config': 'Satellites'}).then(function (data) {
+ scope.storj_satellites = data.data.Result;
+ if (scope.storj_satellite == undefined && scope.storj_satellite_custom == undefined)
+ scope.storj_satellite = 'us1.storj.io:7777';
+
+ }, AppUtils.connectionError);
+ } else {
+ if (scope.storj_satellite == undefined && scope.storj_satellite_custom == undefined)
+ scope.storj_satellite = 'us1.storj.io:7777';
+ }
+
+ if (scope.storj_auth_methods == null) {
+ AppService.post('/webmodule/storj-getconfig', {'storj-config': 'AuthenticationMethods'}).then(function (data) {
+ scope.storj_auth_methods = data.data.Result;
+ if (scope.storj_auth_method == undefined)
+ scope.storj_auth_method = 'API key';
+
+ }, AppUtils.connectionError);
+ } else {
+ if (scope.storj_auth_method == undefined)
+ scope.storj_auth_method = 'API key';
+ }
+ };
+
EditUriBackendConfig.loaders['tardigrade'] = function (scope) {
if (scope.tardigrade_satellites == null) {
- AppService.post('/webmodule/tardigrade-getconfig', {'tardigrade-config': 'Satellites'}).then(function (data) {
+ AppService.post('/webmodule/storj-getconfig', {'storj-config': 'Satellites'}).then(function (data) {
scope.tardigrade_satellites = data.data.Result;
if (scope.tardigrade_satellite == undefined && scope.tardigrade_satellite_custom == undefined)
- scope.tardigrade_satellite = 'us-central-1.tardigrade.io:7777';
+ scope.tardigrade_satellite = 'us1.storj.io:7777';
}, AppUtils.connectionError);
} else {
if (scope.tardigrade_satellite == undefined && scope.tardigrade_satellite_custom == undefined)
- scope.tardigrade_satellite = 'us-central-1.tardigrade.io:7777';
+ scope.tardigrade_satellite = 'us1.storj.io:7777';
}
if (scope.tardigrade_auth_methods == null) {
- AppService.post('/webmodule/tardigrade-getconfig', {'tardigrade-config': 'AuthenticationMethods'}).then(function (data) {
+ AppService.post('/webmodule/storj-getconfig', {'storj-config': 'AuthenticationMethods'}).then(function (data) {
scope.tardigrade_auth_methods = data.data.Result;
if (scope.tardigrade_auth_method == undefined)
scope.tardigrade_auth_method = 'API key';
@@ -486,7 +513,30 @@ backupApp.service('EditUriBuiltins', function (AppService, AppUtils, SystemInfo,
delete options[nukeopts[x]];
}
- EditUriBackendConfig.parsers['tardigrade'] = function (scope, module, server, port, path, options) {
+ EditUriBackendConfig.parsers['storj'] = function (scope, module, server, port, path, options) {
+ if (options['--storj-auth-method'])
+ scope.storj_auth_method = options['--storj-auth-method'];
+ if (options['--storj-satellite'])
+ scope.storj_satellite = options['--storj-satellite'];
+ if (options['--storj-api-key'])
+ scope.storj_api_key = options['--storj-api-key'];
+ if (options['--storj-secret'])
+ scope.storj_secret = options['--storj-secret'];
+ if (options['--storj-secret-verify'])
+ scope.storj_secret_verify = options['--storj-secret-verify'];
+ if (options['--storj-shared-access'])
+ scope.storj_shared_access = options['--storj-shared-access'];
+ if (options['--storj-bucket'])
+ scope.storj_bucket = options['--storj-bucket'];
+ if (options['--storj-folder'])
+ scope.storj_folder = options['--storj-folder'];
+
+ var nukeopts = ['--storj-auth-method','--storj-satellite', '--storj-api-key', '--storj-secret', '--storj-secret-verify', '--storj-shared-access', '--storj-bucket', '--storj-folder'];
+ for (var x in nukeopts)
+ delete options[nukeopts[x]];
+ };
+
+ EditUriBackendConfig.parsers['tardigrade'] = function (scope, module, server, port, path, options) {
if (options['--tardigrade-auth-method'])
scope.tardigrade_auth_method = options['--tardigrade-auth-method'];
if (options['--tardigrade-satellite'])
@@ -495,14 +545,16 @@ backupApp.service('EditUriBuiltins', function (AppService, AppUtils, SystemInfo,
scope.tardigrade_api_key = options['--tardigrade-api-key'];
if (options['--tardigrade-secret'])
scope.tardigrade_secret = options['--tardigrade-secret'];
- if (options['--tardigrade-shared-access'])
+ if (options['--tardigrade-secret-verify'])
+ scope.tardigrade_secret_verify = options['--tardigrade-secret-verify'];
+ if (options['--tardigrade-shared-access'])
scope.tardigrade_shared_access = options['--tardigrade-shared-access'];
- if (options['--tardigrade-bucket'])
+ if (options['--tardigrade-bucket'])
scope.tardigrade_bucket = options['--tardigrade-bucket'];
- if (options['--tardigrade-folder'])
+ if (options['--tardigrade-folder'])
scope.tardigrade_folder = options['--tardigrade-folder'];
-
- var nukeopts = ['--tardigrade-auth-method','--tardigrade-satellite', '--tardigrade-api-key', '--tardigrade-secret', '--tardigrade-shared-access', '--tardigrade-bucket', '--tardigrade-folder'];
+
+ var nukeopts = ['--tardigrade-auth-method','--tardigrade-satellite', '--tardigrade-api-key', '--tardigrade-secret', '--tardigrade-secret-verify', '--tardigrade-shared-access', '--tardigrade-bucket', '--tardigrade-folder'];
for (var x in nukeopts)
delete options[nukeopts[x]];
};
@@ -743,15 +795,36 @@ backupApp.service('EditUriBuiltins', function (AppService, AppUtils, SystemInfo,
return url;
}
+ EditUriBackendConfig.builders['storj'] = function (scope) {
+ var opts = {
+ 'storj-auth-method': scope.storj_auth_method,
+ 'storj-satellite': scope.storj_satellite,
+ 'storj-api-key': scope.storj_api_key,
+ 'storj-secret': scope.storj_secret,
+ 'storj-shared-access': scope.storj_shared_access,
+ 'storj-bucket': scope.storj_bucket,
+ 'storj-folder': scope.storj_folder
+ };
+
+ EditUriBackendConfig.merge_in_advanced_options(scope, opts);
+
+ var url = AppUtils.format('{0}://storj.io/config{1}',
+ scope.Backend.Key,
+ AppUtils.encodeDictAsUrl(opts)
+ );
+
+ return url;
+ };
+
EditUriBackendConfig.builders['tardigrade'] = function (scope) {
var opts = {
- 'tardigrade-auth-method': scope.tardigrade_auth_method,
+ 'tardigrade-auth-method': scope.tardigrade_auth_method,
'tardigrade-satellite': scope.tardigrade_satellite,
'tardigrade-api-key': scope.tardigrade_api_key,
'tardigrade-secret': scope.tardigrade_secret,
'tardigrade-shared-access': scope.tardigrade_shared_access,
- 'tardigrade-bucket': scope.tardigrade_bucket,
- 'tardigrade-folder': scope.tardigrade_folder
+ 'tardigrade-bucket': scope.tardigrade_bucket,
+ 'tardigrade-folder': scope.tardigrade_folder
};
EditUriBackendConfig.merge_in_advanced_options(scope, opts);
@@ -1110,7 +1183,71 @@ backupApp.service('EditUriBuiltins', function (AppService, AppUtils, SystemInfo,
continuation();
};
+ EditUriBackendConfig.validaters['storj'] = function (scope, continuation) {
+ var res = true;
+
+ if(res && !scope['storj_auth_method']){
+ res = EditUriBackendConfig.require_field(scope, 'storj_auth_method', gettextCatalog.getString('Authentication method'));
+ }
+
+ if(res && scope['storj_auth_method'] == 'Access grant'){
+ res = EditUriBackendConfig.require_field(scope, 'storj_shared_access', gettextCatalog.getString('storj_shared_access')) &&
+ EditUriBackendConfig.require_field(scope, 'storj_bucket', gettextCatalog.getString('Bucket'));
+ }
+
+ if(res && scope['storj_auth_method'] == 'API key'){
+ res = EditUriBackendConfig.require_field(scope, 'storj_api_key', gettextCatalog.getString('API key')) &&
+ EditUriBackendConfig.require_field(scope, 'storj_secret', gettextCatalog.getString('Encryption passphrase')) &&
+ EditUriBackendConfig.require_field(scope, 'storj_bucket', gettextCatalog.getString('Bucket'));
+ }
+
+ if(res && scope['storj_auth_method'] == 'API key' && !scope['storj_satellite']){
+ res = EditUriBackendConfig.require_field(scope, 'storj_satellite_custom', gettextCatalog.getString('Custom Satellite'));
+ }
+
+ if(res && scope['storj_auth_method'] == 'API key' && scope['storj_secret'] != scope['storj_secret_verify'])
+ res = EditUriBackendConfig.show_error_dialog(gettextCatalog.getString('The encryption passphrases do not match'));
+
+ var re = new RegExp('^([a-z0-9]+([a-z0-9\-][a-z0-9])*)+(.[a-z0-9]+([a-z0-9\-][a-z0-9])*)*$');
+ if(res && scope['storj_bucket'] && (!re.test(scope['storj_bucket']) || !(scope['storj_bucket'].length > 2 && scope['storj_bucket'].length < 64))){
+ res = EditUriBackendConfig.show_error_dialog(gettextCatalog.getString('Bucket name can only be between 3 and 63 characters long and contain only lower-case characters, numbers, periods and dashes'));
+ }
+
+ if (res)
+ continuation();
+ };
+
EditUriBackendConfig.validaters['tardigrade'] = function (scope, continuation) {
+ var res = true;
+
+ if(res && !scope['tardigrade_auth_method']){
+ res = EditUriBackendConfig.require_field(scope, 'tardigrade_auth_method', gettextCatalog.getString('Authentication method'));
+ }
+
+ if(res && scope['tardigrade_auth_method'] == 'Access grant'){
+ res = EditUriBackendConfig.require_field(scope, 'tardigrade_shared_access', gettextCatalog.getString('tardigrade_shared_access')) &&
+ EditUriBackendConfig.require_field(scope, 'tardigrade_bucket', gettextCatalog.getString('Bucket'));
+ }
+
+ if(res && scope['tardigrade_auth_method'] == 'API key'){
+ res = EditUriBackendConfig.require_field(scope, 'tardigrade_api_key', gettextCatalog.getString('API key')) &&
+ EditUriBackendConfig.require_field(scope, 'tardigrade_secret', gettextCatalog.getString('Encryption passphrase')) &&
+ EditUriBackendConfig.require_field(scope, 'tardigrade_bucket', gettextCatalog.getString('Bucket'));
+ }
+
+ if(res && scope['tardigrade_auth_method'] == 'API key' && !scope['tardigrade_satellite']){
+ res = EditUriBackendConfig.require_field(scope, 'tardigrade_satellite_custom', gettextCatalog.getString('Custom Satellite'));
+ }
+
+ if(res && scope['tardigrade_auth_method'] == 'API key' && scope['tardigrade_secret'] != scope['tardigrade_secret_verify'])
+ res = EditUriBackendConfig.show_error_dialog(gettextCatalog.getString('The encryption passphrases do not match'));
+
+ var re = new RegExp('^([a-z0-9]+([a-z0-9\-][a-z0-9])*)+(.[a-z0-9]+([a-z0-9\-][a-z0-9])*)*$');
+ if(res && scope['tardigrade_bucket'] && (!re.test(scope['tardigrade_bucket']) || !(scope['tardigrade_bucket'].length > 2 && scope['tardigrade_bucket'].length < 64))){
+ res = EditUriBackendConfig.show_error_dialog(gettextCatalog.getString('Bucket name can only be between 3 and 63 characters long and contain only lower-case characters, numbers, periods and dashes'));
+ }
+
+ if (res)
continuation();
};
diff --git a/Duplicati/Server/webroot/ngax/scripts/services/SystemInfo.js b/Duplicati/Server/webroot/ngax/scripts/services/SystemInfo.js
index c422020e5..b6b4e4e52 100644
--- a/Duplicati/Server/webroot/ngax/scripts/services/SystemInfo.js
+++ b/Duplicati/Server/webroot/ngax/scripts/services/SystemInfo.js
@@ -71,6 +71,7 @@ backupApp.service('SystemInfo', function($rootScope, $timeout, $cookies, AppServ
'mssp': null,
'dropbox': null,
'sia': null,
+ 'storj': null,
'tardigrade': null,
'jottacloud': null,
'rclone': null,
diff --git a/Duplicati/Server/webroot/ngax/templates/backends/storj.html b/Duplicati/Server/webroot/ngax/templates/backends/storj.html
new file mode 100644
index 000000000..bb25db55a
--- /dev/null
+++ b/Duplicati/Server/webroot/ngax/templates/backends/storj.html
@@ -0,0 +1,43 @@
+<div class="input select">
+ <label for="storj_auth_method" translate>Authentication method</label>
+ <select name="storj_auth_method" id="storj_auth_method" ng-model="$parent.storj_auth_method"
+ ng-options="v as k + ' (' + v + ')' for (k, v) in storj_auth_methods | orderBy: k">
+ <option value="" translate >Authentication method ({{auth_method}})
+ </option>
+ </select>
+</div>
+<div class="input select" ng-show="$parent.storj_auth_method == 'API key'">
+ <label for="storj_satellite" translate>Satellite</label>
+ <select name="storj_satellite" id="storj_satellite" ng-model="$parent.storj_satellite"
+ ng-options="v as k + ' (' + v + ')' for (k, v) in storj_satellites | orderBy: k">
+ <option value="" translate translate-params-satellite="storj_satellite_custom || ''">Custom Satellite ({{satellite}})
+ </option>
+ </select>
+
+ <input ng-hide="contains_value(storj_satellites, storj_satellite)" type="text" id="storj_satellite_custom"
+ ng-model="$parent.storj_satellite_custom" placeholder="{{'Custom Satellite' | translate}}"/>
+</div>
+<div class="input text" ng-show="$parent.storj_auth_method == 'API key'">
+ <label for="storj_api_key" translate>API key</label>
+ <input type="text" name="storj_api_key" id="storj_api_key" ng-model="$parent.storj_api_key" placeholder="{{'The API key' | translate}}" />
+</div>
+<div class="input password" ng-show="$parent.storj_auth_method == 'API key'">
+ <label for="storj_secret" translate>Encryption passphrase</label>
+ <input autocomplete="new-password" type="password" name="storj_secret" id="storj_secret" ng-model="$parent.storj_secret" placeholder="{{'The encryption passphrase' | translate}}" />
+</div>
+<div class="input password" ng-show="$parent.storj_auth_method == 'API key'">
+ <label for="storj_secret_verify" translate>Verify encryption passphrase</label>
+ <input autocomplete="new-password" type="password" name="storj_secret_verify" id="storj_secret_verify" ng-model="$parent.storj_secret_verify" placeholder="{{'The encryption passphrase (for verification)' | translate}}" />
+</div>
+<div class="input text" ng-show="$parent.storj_auth_method == 'Access grant'">
+ <label for="storj_shared_access" translate>Access grant</label>
+ <input type="text" name="storj_shared_access" id="storj_shared_access" ng-model="$parent.storj_shared_access" placeholder="{{'The access grant instead of the info above'}}" />
+</div>
+<div class="input text">
+ <label for="storj_bucket" translate>Bucket</label>
+ <input type="text" name="storj_bucket" id="storj_bucket" ng-model="$parent.storj_bucket" placeholder="{{'The bucket for storing the backup'}}" />
+</div>
+<div class="input text">
+ <label for="storj_folder" translate>Folder path</label>
+ <input type="text" name="storj_folder" id="storj_folder" ng-model="$parent.storj_folder" placeholder="{{'The folder within the bucket for storing the backup'}}" />
+</div>
diff --git a/Duplicati/Server/webroot/ngax/templates/backends/tardigrade.html b/Duplicati/Server/webroot/ngax/templates/backends/tardigrade.html
index eaa094c69..0131cab56 100644
--- a/Duplicati/Server/webroot/ngax/templates/backends/tardigrade.html
+++ b/Duplicati/Server/webroot/ngax/templates/backends/tardigrade.html
@@ -25,6 +25,10 @@
<label for="tardigrade_secret" translate>Encryption passphrase</label>
<input autocomplete="new-password" type="password" name="tardigrade_secret" id="tardigrade_secret" ng-model="$parent.tardigrade_secret" placeholder="{{'The encryption passphrase' | translate}}" />
</div>
+<div class="input password" ng-show="$parent.tardigrade_auth_method == 'API key'">
+ <label for="tardigrade_secret_verify" translate>Verify encryption passphrase</label>
+ <input autocomplete="new-password" type="password" name="tardigrade_secret_verify" id="tardigrade_secret_verify" ng-model="$parent.tardigrade_secret_verify" placeholder="{{'The encryption passphrase (for verification)' | translate}}" />
+</div>
<div class="input text" ng-show="$parent.tardigrade_auth_method == 'Access grant'">
<label for="tardigrade_shared_access" translate>Access grant</label>
<input type="text" name="tardigrade_shared_access" id="tardigrade_shared_access" ng-model="$parent.tardigrade_shared_access" placeholder="{{'The access grant instead of the info above'}}" />
diff --git a/Duplicati/UnitTest/ProblematicPathTests.cs b/Duplicati/UnitTest/ProblematicPathTests.cs
index e470f67c3..d0cd45537 100644
--- a/Duplicati/UnitTest/ProblematicPathTests.cs
+++ b/Duplicati/UnitTest/ProblematicPathTests.cs
@@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
+using System.Text.RegularExpressions;
using Duplicati.Library.Common;
using Duplicati.Library.Common.IO;
using Duplicati.Library.Interface;
@@ -252,12 +253,18 @@ namespace Duplicati.UnitTest
[Test]
[Category("ProblematicPath")]
- [TestCase("ends_with_dot.")]
- [TestCase("ends_with_dots..")]
- [TestCase("ends_with_space ")]
- [TestCase("ends_with_spaces ")]
- public void ProblematicSuffixes(string pathComponent)
+ [TestCase("ends_with_dot.", false)]
+ [TestCase("ends_with_dots..", false)]
+ [TestCase("ends_with_space ", false)]
+ [TestCase("ends_with_spaces ", false)]
+ [TestCase("ends_with_newline\n", true)]
+ public void ProblematicSuffixes(string pathComponent, bool skipOnWindows)
{
+ if (Platform.IsClientWindows && skipOnWindows)
+ {
+ return;
+ }
+
string folderPath = SystemIO.IO_OS.PathCombine(this.DATAFOLDER, pathComponent);
SystemIO.IO_OS.DirectoryCreate(folderPath);
@@ -274,6 +281,8 @@ namespace Duplicati.UnitTest
}
Dictionary<string, string> restoreOptions = new Dictionary<string, string>(this.TestOptions) {["restore-path"] = this.RESTOREFOLDER};
+
+ // Restore just the file.
using (Controller c = new Controller("file://" + this.TARGETFOLDER, restoreOptions, null))
{
IRestoreResults restoreResults = c.Restore(new[] {filePath});
@@ -282,15 +291,19 @@ namespace Duplicati.UnitTest
}
string restoreFilePath = SystemIO.IO_OS.PathCombine(this.RESTOREFOLDER, pathComponent);
- Assert.IsTrue(SystemIO.IO_OS.FileExists(restoreFilePath));
+ TestUtils.AssertFilesAreEqual(filePath, restoreFilePath, true, pathComponent);
+ SystemIO.IO_OS.FileDelete(restoreFilePath);
- MemoryStream restoredStream = new MemoryStream();
- using (FileStream fileStream = SystemIO.IO_OS.FileOpenRead(restoreFilePath))
+ // Restore the entire directory.
+ string pathSpec = $"[{Regex.Escape(Util.AppendDirSeparator(this.DATAFOLDER))}.*]";
+ using (Controller c = new Controller("file://" + this.TARGETFOLDER, restoreOptions, null))
{
- Utility.CopyStream(fileStream, restoredStream);
+ IRestoreResults restoreResults = c.Restore(new[] {pathSpec});
+ Assert.AreEqual(0, restoreResults.Errors.Count());
+ Assert.AreEqual(0, restoreResults.Warnings.Count());
}
- Assert.AreEqual(fileBytes, restoredStream.ToArray());
+ TestUtils.AssertDirectoryTreesAreEquivalent(this.DATAFOLDER, this.RESTOREFOLDER, true, pathComponent);
}
}
} \ No newline at end of file
diff --git a/README.md b/README.md
index e89660ad2..4200e63b8 100644
--- a/README.md
+++ b/README.md
@@ -21,7 +21,7 @@ Removed Gitter
Duplicati is a free, open source, backup client that securely stores encrypted, incremental, compressed backups on cloud storage services and remote file servers. It works with:
-&nbsp;&nbsp; *Amazon S3, [Backblaze (B2)](https://www.backblaze.com/blog/duplicati-backups-cloud-storage/ "Duplicati with Backblaze B2 Cloud Storage"), Box, Dropbox, FTP, Google Cloud and Drive, HubiC, MEGA, Microsoft Azure and OneDrive, Rackspace Cloud Files, OpenStack Storage (Swift), Sia, Tardigrade, SSH (SFTP), WebDAV, Tencent Cloud Object Storage (COS), [and more!](https://duplicati.readthedocs.io/en/latest/01-introduction/#supported-backends)*
+&nbsp;&nbsp; *Amazon S3, [Backblaze (B2)](https://www.backblaze.com/blog/duplicati-backups-cloud-storage/ "Duplicati with Backblaze B2 Cloud Storage"), Box, Dropbox, FTP, Google Cloud and Drive, HubiC, MEGA, Microsoft Azure and OneDrive, Rackspace Cloud Files, OpenStack Storage (Swift), Sia, Storj DCS, SSH (SFTP), WebDAV, Tencent Cloud Object Storage (COS), [and more!](https://duplicati.readthedocs.io/en/latest/01-introduction/#supported-backends)*
Duplicati is licensed under LGPL and available for Windows, OSX and Linux (.NET 4.7.1+ or Mono 5.10.0+ required).
@@ -66,7 +66,7 @@ But Duplicati does!
Keep your data safe! Bad guys on the Internet seem to look for interesting data everywhere. But people do not want to see any of their private data revealed anywhere. Duplicati provides strong encryption to make sure that your data looks like garbage to others. With a well chosen password your backup files will be more safe on a public webserver than your unencrypted files at home.
-Store your backup far away! The best backup is useless when it is destroyed together with it's original data. Just assume that a fire destroys your office - would your backup survive? Duplicati stores backups on various remote file servers and it supports incremental backups so that only changed parts need to be transferred. This makes it easy to use a destination far away from the original data.
+Store your backup far away! The best backup is useless when it is destroyed together with its original data. Just assume that a fire destroys your office - would your backup survive? Duplicati stores backups on various remote file servers and it supports incremental backups so that only changed parts need to be transferred. This makes it easy to use a destination far away from the original data.
Backup regularly! The worst case is that your backup is outdated simply because someone forgot to make a backup at the right time. Duplicati has a built-in scheduler, so that it's easy to have a regular, up-to-date backup. Furthermore, Duplicati uses file compression and is able to store incremental backups to save storage space and bandwidth.
diff --git a/thirdparty/BoncyCastle/Homepage.txt b/thirdparty/BoncyCastle/Homepage.txt
deleted file mode 100644
index 3e4d61d83..000000000
--- a/thirdparty/BoncyCastle/Homepage.txt
+++ /dev/null
@@ -1 +0,0 @@
-http://www.bouncycastle.org/csharp/ \ No newline at end of file
diff --git a/thirdparty/BoncyCastle/license.txt b/thirdparty/BoncyCastle/license.txt
deleted file mode 100644
index 3786ce3d9..000000000
--- a/thirdparty/BoncyCastle/license.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-LICENSE
-Copyright (c) 2000 - 2015 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org)
-
-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:
-
-The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
-
-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. \ No newline at end of file
diff --git a/thirdparty/BoncyCastle/licensedata.json b/thirdparty/BoncyCastle/licensedata.json
deleted file mode 100644
index 220a9b791..000000000
--- a/thirdparty/BoncyCastle/licensedata.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "name": "Bouncy Castle",
- "description": "A lightweight cryptography API for Java and C#",
- "link": "http://www.bouncycastle.org/csharp",
- "license": "MIT",
- "notes": ""
-} \ No newline at end of file
diff --git a/thirdparty/Otp.NET/Homepage.txt b/thirdparty/Otp.NET/Homepage.txt
new file mode 100644
index 000000000..1f46ecb7e
--- /dev/null
+++ b/thirdparty/Otp.NET/Homepage.txt
@@ -0,0 +1 @@
+https://github.com/kspearrin/Otp.NET \ No newline at end of file
diff --git a/thirdparty/Otp.NET/license.txt b/thirdparty/Otp.NET/license.txt
new file mode 100755
index 000000000..0b41517cb
--- /dev/null
+++ b/thirdparty/Otp.NET/license.txt
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2017 Kyle Spearrin
+
+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:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+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. \ No newline at end of file
diff --git a/thirdparty/Otp.NET/licensedata.json b/thirdparty/Otp.NET/licensedata.json
new file mode 100644
index 000000000..3c098b67f
--- /dev/null
+++ b/thirdparty/Otp.NET/licensedata.json
@@ -0,0 +1,7 @@
+{
+ "name": "Otp.NET",
+ "description": "An implementation TOTP RFC 6238 and HOTP RFC 4226 in C#.",
+ "link": "https://github.com/kspearrin/Otp.NET",
+ "license": "MIT",
+ "notes": ""
+} \ No newline at end of file