diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 77ce60a93..f140717aa 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,10 +1,6 @@ name: Tests -on: - push: - branches: "**net5**" - pull_request: - branches: "**net5**" +on: [push, pull_request] jobs: unit_tests: @@ -13,30 +9,29 @@ jobs: strategy: fail-fast: false matrix: - os: [ ubuntu-latest, windows-latest, macos-latest ] + os: [ubuntu-latest, windows-latest, macos-latest] steps: - - name: Set up .NET - uses: actions/setup-dotnet@v1.8.0 - with: - dotnet-version: 6.0.x + - name: Set up .NET + uses: actions/setup-dotnet@v1.8.0 + with: + dotnet-version: 8.0.x - - name: Checkout source - uses: actions/checkout@v2.3.4 + - name: Checkout source + uses: actions/checkout@v2.3.4 - - name: Restore NuGet dependencies - run: dotnet restore Duplicati.sln + - name: Restore NuGet dependencies + run: dotnet restore Duplicati.sln - - name: Build Duplicati - run: dotnet build --no-restore Duplicati.sln + - name: Build Duplicati + run: dotnet build --no-restore Duplicati.sln - - name: Run unit tests - run: dotnet test --no-build --verbosity minimal Duplicati.sln + - name: Run unit tests + run: dotnet test --no-build --verbosity minimal Duplicati.sln selenium: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - name: Selenium - run: pipeline/selenium/test.sh - + - uses: actions/checkout@v2 + - name: Selenium + run: pipeline/selenium/test.sh diff --git a/.gitignore b/.gitignore index 359f8d818..20f24ea49 100644 --- a/.gitignore +++ b/.gitignore @@ -27,4 +27,6 @@ binfiles.wxs Installer/debian/*.buildinfo /*.deb -/*.rpm \ No newline at end of file +/*.rpm + +ReleaseBuilder/build-temp/ \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index e0b00a515..aa27c67b8 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -9,7 +9,29 @@ "type": "coreclr", "request": "launch", "preLaunchTask": "build", - "program": "${workspaceFolder}/Executables/net8/Duplicati.GUI.TrayIcon/bin/Debug/net8.0/Duplicati.GUI.TrayIcon.exe", + "program": "${workspaceFolder}/Executables/net8/Duplicati.GUI.TrayIcon/bin/Debug/net8.0/Duplicati.GUI.TrayIcon", + "args": [], + "cwd": "${workspaceFolder}", + "stopAtEntry": false, + "console": "internalConsole" + }, + { + "name": "Launch Server executable", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + "program": "${workspaceFolder}/Executables/net8/Duplicati.Server/bin/Debug/net8.0/Duplicati.Server", + "args": [], + "cwd": "${workspaceFolder}", + "stopAtEntry": false, + "console": "internalConsole" + }, + { + "name": "Launch CommandLine executable", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + "program": "${workspaceFolder}/Executables/net8/Duplicati.Server/bin/Debug/net8.0/Duplicati.CommandLine", "args": [], "cwd": "${workspaceFolder}", "stopAtEntry": false, diff --git a/BuildTools/AutoUpdateBuilder/AutoUpdateBuilder.csproj b/BuildTools/AutoUpdateBuilder/AutoUpdateBuilder.csproj deleted file mode 100644 index 86143c38b..000000000 --- a/BuildTools/AutoUpdateBuilder/AutoUpdateBuilder.csproj +++ /dev/null @@ -1,27 +0,0 @@ - - - - net8.0 - Exe - - - - - - - - - - - - - - - - - all - runtime; build; native; contentfiles; analyzers; buildtransitive - - - - \ No newline at end of file diff --git a/BuildTools/AutoUpdateBuilder/AutoUpdateBuilder.sln b/BuildTools/AutoUpdateBuilder/AutoUpdateBuilder.sln deleted file mode 100644 index 3750b01e7..000000000 --- a/BuildTools/AutoUpdateBuilder/AutoUpdateBuilder.sln +++ /dev/null @@ -1,74 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2012 -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AutoUpdateBuilder", "AutoUpdateBuilder.csproj", "{17E3EB29-87FD-4BE9-A531-8DAD8B295494}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Compression", "..\..\Duplicati\Library\Compression\Duplicati.Library.Compression.csproj", "{19ECCE09-B5EB-406C-8C57-BAC66997D469}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Utility", "..\..\Duplicati\Library\Utility\Duplicati.Library.Utility.csproj", "{DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Interface", "..\..\Duplicati\Library\Interface\Duplicati.Library.Interface.csproj", "{C5899F45-B0FF-483C-9D38-24A9FCAAB237}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Logging", "..\..\Duplicati\Library\Logging\Duplicati.Library.Logging.csproj", "{D10A5FC0-11B4-4E70-86AA-8AEA52BD9798}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Encryption", "..\..\Duplicati\Library\Encryption\Duplicati.Library.Encryption.csproj", "{94484FDB-2EFA-4CF0-9BE6-A561157B4F87}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.AutoUpdater", "..\..\Duplicati\Library\AutoUpdater\Duplicati.Library.AutoUpdater.csproj", "{7E119745-1F62-43F0-936C-F312A1912C0B}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.License", "..\..\Duplicati\License\Duplicati.License.csproj", "{4D012CB1-4B92-47F4-89B7-BF80A73A2E99}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Localization", "..\..\Duplicati\Library\Localization\Duplicati.Library.Localization.csproj", "{B68F2214-951F-4F78-8488-66E1ED3F50BF}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Duplicati.Library.Common", "..\..\Duplicati\Library\Common\Duplicati.Library.Common.csproj", "{D63E53E4-A458-4C2F-914D-92F715F58ACF}" -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {17E3EB29-87FD-4BE9-A531-8DAD8B295494}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {17E3EB29-87FD-4BE9-A531-8DAD8B295494}.Debug|Any CPU.Build.0 = Debug|Any CPU - {17E3EB29-87FD-4BE9-A531-8DAD8B295494}.Release|Any CPU.ActiveCfg = Release|Any CPU - {17E3EB29-87FD-4BE9-A531-8DAD8B295494}.Release|Any CPU.Build.0 = Release|Any CPU - {19ECCE09-B5EB-406C-8C57-BAC66997D469}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {19ECCE09-B5EB-406C-8C57-BAC66997D469}.Debug|Any CPU.Build.0 = Debug|Any CPU - {19ECCE09-B5EB-406C-8C57-BAC66997D469}.Release|Any CPU.ActiveCfg = Release|Any CPU - {19ECCE09-B5EB-406C-8C57-BAC66997D469}.Release|Any CPU.Build.0 = Release|Any CPU - {4D012CB1-4B92-47F4-89B7-BF80A73A2E99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4D012CB1-4B92-47F4-89B7-BF80A73A2E99}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4D012CB1-4B92-47F4-89B7-BF80A73A2E99}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4D012CB1-4B92-47F4-89B7-BF80A73A2E99}.Release|Any CPU.Build.0 = Release|Any CPU - {7E119745-1F62-43F0-936C-F312A1912C0B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {7E119745-1F62-43F0-936C-F312A1912C0B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {7E119745-1F62-43F0-936C-F312A1912C0B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {7E119745-1F62-43F0-936C-F312A1912C0B}.Release|Any CPU.Build.0 = Release|Any CPU - {94484FDB-2EFA-4CF0-9BE6-A561157B4F87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {94484FDB-2EFA-4CF0-9BE6-A561157B4F87}.Debug|Any CPU.Build.0 = Debug|Any CPU - {94484FDB-2EFA-4CF0-9BE6-A561157B4F87}.Release|Any CPU.ActiveCfg = Release|Any CPU - {94484FDB-2EFA-4CF0-9BE6-A561157B4F87}.Release|Any CPU.Build.0 = Release|Any CPU - {B68F2214-951F-4F78-8488-66E1ED3F50BF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B68F2214-951F-4F78-8488-66E1ED3F50BF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B68F2214-951F-4F78-8488-66E1ED3F50BF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B68F2214-951F-4F78-8488-66E1ED3F50BF}.Release|Any CPU.Build.0 = Release|Any CPU - {C5899F45-B0FF-483C-9D38-24A9FCAAB237}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C5899F45-B0FF-483C-9D38-24A9FCAAB237}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C5899F45-B0FF-483C-9D38-24A9FCAAB237}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C5899F45-B0FF-483C-9D38-24A9FCAAB237}.Release|Any CPU.Build.0 = Release|Any CPU - {D10A5FC0-11B4-4E70-86AA-8AEA52BD9798}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D10A5FC0-11B4-4E70-86AA-8AEA52BD9798}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D10A5FC0-11B4-4E70-86AA-8AEA52BD9798}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D10A5FC0-11B4-4E70-86AA-8AEA52BD9798}.Release|Any CPU.Build.0 = Release|Any CPU - {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DE3E5D4C-51AB-4E5E-BEE8-E636CEBFBA65}.Release|Any CPU.Build.0 = Release|Any CPU - {D63E53E4-A458-4C2F-914D-92F715F58ACF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D63E53E4-A458-4C2F-914D-92F715F58ACF}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D63E53E4-A458-4C2F-914D-92F715F58ACF}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D63E53E4-A458-4C2F-914D-92F715F58ACF}.Release|Any CPU.Build.0 = Release|Any CPU - EndGlobalSection - GlobalSection(MonoDevelopProperties) = preSolution - StartupItem = AutoUpdateBuilder.csproj - EndGlobalSection -EndGlobal diff --git a/BuildTools/AutoUpdateBuilder/Program.cs b/BuildTools/AutoUpdateBuilder/Program.cs deleted file mode 100644 index 6d170e20e..000000000 --- a/BuildTools/AutoUpdateBuilder/Program.cs +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright (C) 2024, The Duplicati Team -// https://duplicati.com, hello@duplicati.com -// -// 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. - -using System; -using System.Collections.Generic; -using System.Security.Cryptography; - -namespace AutoUpdateBuilder -{ - public class Program - { - private static RSACryptoServiceProvider privkey; - - private static string keyfile; - private static string keyfilepassword; - - private static void CompareToManifestPublicKey() - { - if (Duplicati.Library.AutoUpdater.AutoUpdateSettings.SignKey == null || privkey.ToXmlString(false) != Duplicati.Library.AutoUpdater.AutoUpdateSettings.SignKey.ToXmlString(false)) - { - Console.WriteLine("The public key in the project is not the same as the public key from the file"); - Console.WriteLine("Try setting the key to: "); - Console.WriteLine(privkey.ToXmlString(false)); - System.Environment.Exit(5); - } - } - - private static void LoadKeyFromFile() - { - using (var enc = new Duplicati.Library.Encryption.AESEncryption(keyfilepassword, new Dictionary())) - using (var ms = new System.IO.MemoryStream()) - using (var fs = System.IO.File.OpenRead(keyfile)) - { - enc.Decrypt(fs, ms); - ms.Position = 0; - - using (var sr = new System.IO.StreamReader(ms)) - privkey.FromXmlString(sr.ReadToEnd()); - } - } - - public static int Main(string[] _args) - { - var args = new List(_args); - var opts = Duplicati.Library.Utility.CommandLineParser.ExtractOptions(args); - - opts.TryGetValue("input", out string inputfolder); - opts.TryGetValue("output", out string outputfolder); - opts.TryGetValue("allow-new-key", out string allowNewKey); - opts.TryGetValue("keyfile", out keyfile); - opts.TryGetValue("manifest", out string manifestfile); - opts.TryGetValue("keyfile-password", out keyfilepassword); - - var usedoptions = new [] { "allow-new-key", "input", "output", "keyfile", "manifest", "keyfile-password" }; - - if (string.IsNullOrWhiteSpace(inputfolder)) - { - Console.WriteLine("Missing input folder"); - return 4; - } - - if (string.IsNullOrWhiteSpace(outputfolder)) - { - Console.WriteLine("Missing output folder"); - return 4; - } - - if (string.IsNullOrWhiteSpace(keyfile)) - { - Console.WriteLine("Missing keyfile"); - return 4; - } - - if (!System.IO.Directory.Exists(inputfolder)) - { - Console.WriteLine("Input folder not found"); - return 4; - } - - if (string.IsNullOrWhiteSpace(keyfilepassword)) - { - Console.WriteLine("Enter keyfile passphrase: "); - keyfilepassword = Console.ReadLine().Trim(); - } - - if (!System.IO.File.Exists(keyfile)) - { - Console.WriteLine("Keyfile not found, creating new"); - var newkey = RSA.Create().ToXmlString(true); - using (var enc = new Duplicati.Library.Encryption.AESEncryption(keyfilepassword, new Dictionary())) - using (var fs = System.IO.File.OpenWrite(keyfile)) - using (var ms = new System.IO.MemoryStream(System.Text.Encoding.UTF8.GetBytes(newkey))) - enc.Encrypt(ms, fs); - } - - if (!System.IO.Directory.Exists(outputfolder)) - System.IO.Directory.CreateDirectory(outputfolder); - - privkey = (RSACryptoServiceProvider) RSA.Create(); - - LoadKeyFromFile(); - - if (!Boolean.TryParse(allowNewKey, out Boolean newKeyAllowed) || !newKeyAllowed) - { - CompareToManifestPublicKey(); - } - - Duplicati.Library.AutoUpdater.UpdateInfo updateInfo; - - using (var fs = System.IO.File.OpenRead(manifestfile)) - using (var sr = new System.IO.StreamReader(fs)) - using (var jr = new Newtonsoft.Json.JsonTextReader(sr)) - updateInfo = new Newtonsoft.Json.JsonSerializer().Deserialize(jr); - - var isopts = new Dictionary(opts, StringComparer.InvariantCultureIgnoreCase); - foreach (var usedopt in usedoptions) - { - isopts.Remove(usedopt); - } - - foreach (var k in updateInfo.GetType().GetFields()) - { - if (!isopts.ContainsKey(k.Name)) - { - continue; - } - try - { - //Console.WriteLine("Setting {0} to {1}", k.Name, isopts[k.Name]); - if (k.FieldType == typeof(string[])) - k.SetValue(updateInfo, isopts[k.Name].Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries)); - else if (k.FieldType == typeof(Version)) - k.SetValue(updateInfo, new Version(isopts[k.Name])); - else if (k.FieldType == typeof(int)) - k.SetValue(updateInfo, int.Parse(isopts[k.Name])); - else if (k.FieldType == typeof(long)) - k.SetValue(updateInfo, long.Parse(isopts[k.Name])); - else - k.SetValue(updateInfo, isopts[k.Name]); - } - catch (Exception ex) - { - Console.WriteLine("Failed setting {0} to {1}: {2}", k.Name, isopts[k.Name], ex.Message); - } - - isopts.Remove(k.Name); - } - - foreach (var opt in isopts) - { - Console.WriteLine("Warning! unused option: {0} = {1}", opt.Key, opt.Value); - } - - using (var tf = new Duplicati.Library.Utility.TempFile()) - { - using (var fs = System.IO.File.OpenWrite(tf)) - using (var tw = new System.IO.StreamWriter(fs)) - new Newtonsoft.Json.JsonSerializer().Serialize(tw, updateInfo); - - Duplicati.Library.AutoUpdater.UpdaterManager.CreateUpdatePackage(privkey, inputfolder, outputfolder, tf); - } - - return 0; - } - } -} \ No newline at end of file diff --git a/Duplicati.Library.RestAPI/Database/Connection.cs b/Duplicati.Library.RestAPI/Database/Connection.cs index fb35401d7..bd1d6f35a 100644 --- a/Duplicati.Library.RestAPI/Database/Connection.cs +++ b/Duplicati.Library.RestAPI/Database/Connection.cs @@ -260,8 +260,8 @@ namespace Duplicati.Server.Database { if (sb.Length != 0) sb.Append(" OR "); - sb.Append(@" ("","" || ""Tags"" || "","" LIKE ""%,"" || ? || "",%"") "); - + sb.Append(@" (',' || ""Tags"" || ',' LIKE '%,' || ? || ',%') "); + var p = cmd.CreateParameter(); p.Value = t; cmd.Parameters.Add(p); @@ -330,7 +330,7 @@ namespace Duplicati.Server.Database { var usesEncryption = ReadFromDb( (rd) => ConvertToBoolean(rd, 0), - @"SELECT VALUE != """" FROM ""Option"" WHERE BackupID = ? AND NAME='encryption-module'", id) + @"SELECT VALUE != '' FROM ""Option"" WHERE BackupID = ? AND NAME='encryption-module'", id) .FirstOrDefault(); if (!usesEncryption) @@ -340,7 +340,7 @@ namespace Duplicati.Server.Database return ReadFromDb( (rd) => ConvertToBoolean(rd, 0), - @"SELECT VALUE != """" FROM ""Option"" WHERE BackupID = ? AND NAME='passphrase'", id) + @"SELECT VALUE != '' FROM ""Option"" WHERE BackupID = ? AND NAME='passphrase'", id) .FirstOrDefault(); } } @@ -359,8 +359,8 @@ namespace Duplicati.Server.Database { if (sb.Length != 0) sb.Append(" OR "); - sb.Append(@" ("","" || ""Tags"" || "","" LIKE ""%,"" || ? || "",%"") "); - + sb.Append(@" (',' || ""Tags"" || ',' LIKE '%,' || ? || ',%') "); + var p = cmd.CreateParameter(); p.Value = t; cmd.Parameters.Add(p); diff --git a/Duplicati.Library.RestAPI/RESTMethods/BackupDefaults.cs b/Duplicati.Library.RestAPI/RESTMethods/BackupDefaults.cs index 9138214b2..4d6675520 100644 --- a/Duplicati.Library.RestAPI/RESTMethods/BackupDefaults.cs +++ b/Duplicati.Library.RestAPI/RESTMethods/BackupDefaults.cs @@ -59,7 +59,7 @@ namespace Duplicati.Server.WebServer.RESTMethods try { // Add install defaults/overrides, if present - var path = SystemIO.IO_OS.PathCombine(Library.AutoUpdater.UpdaterManager.InstalledBaseDir, "newbackup.json"); + var path = SystemIO.IO_OS.PathCombine(Library.AutoUpdater.UpdaterManager.INSTALLATIONDIR, "newbackup.json"); if (System.IO.File.Exists(path)) { Newtonsoft.Json.Linq.JObject n; diff --git a/Duplicati.Library.RestAPI/RESTMethods/Licenses.cs b/Duplicati.Library.RestAPI/RESTMethods/Licenses.cs index f787f5bd5..39bd5d754 100644 --- a/Duplicati.Library.RestAPI/RESTMethods/Licenses.cs +++ b/Duplicati.Library.RestAPI/RESTMethods/Licenses.cs @@ -19,6 +19,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. using System; +using System.IO; namespace Duplicati.Server.WebServer.RESTMethods { @@ -26,7 +27,16 @@ namespace Duplicati.Server.WebServer.RESTMethods { public void GET(string key, RequestInfo info) { - var path = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(Duplicati.Library.Utility.Utility.getEntryAssembly().Location), "licenses"); + var exefolder = System.IO.Path.GetDirectoryName(Duplicati.Library.Utility.Utility.getEntryAssembly().Location); + var path = System.IO.Path.Combine(exefolder, "licenses"); + if (Duplicati.Library.Common.Platform.IsClientOSX && !Directory.Exists(path)) + { + // Go up one, as the licenses cannot be in the binary folder in MacOS Packages + exefolder = Path.GetDirectoryName(exefolder); + var test = Path.Combine(exefolder, "Licenses"); + if (Directory.Exists(test)) + path = test; + } info.OutputOK(Duplicati.License.LicenseReader.ReadLicenses(path)); } } diff --git a/Duplicati.Library.RestAPI/RESTMethods/SystemInfo.cs b/Duplicati.Library.RestAPI/RESTMethods/SystemInfo.cs index 6c7f37b24..3c84ad5c6 100644 --- a/Duplicati.Library.RestAPI/RESTMethods/SystemInfo.cs +++ b/Duplicati.Library.RestAPI/RESTMethods/SystemInfo.cs @@ -57,7 +57,6 @@ namespace Duplicati.Server.WebServer.RESTMethods ServerVersionName = Duplicati.License.VersionNumbers.Version, ServerVersionType = Duplicati.Library.AutoUpdater.UpdaterManager.SelfVersion.ReleaseType, StartedBy = FIXMEGlobal.Origin, - BaseVersionName = Duplicati.Library.AutoUpdater.UpdaterManager.BaseVersion.Displayname, DefaultUpdateChannel = Duplicati.Library.AutoUpdater.AutoUpdateSettings.DefaultUpdateChannel, DefaultUsageReportLevel = Duplicati.Library.UsageReporter.Reporter.DefaultReportLevel, ServerTime = DateTime.Now, @@ -65,8 +64,8 @@ namespace Duplicati.Server.WebServer.RESTMethods DirectorySeparator = System.IO.Path.DirectorySeparatorChar, PathSeparator = System.IO.Path.PathSeparator, CaseSensitiveFilesystem = Duplicati.Library.Utility.Utility.IsFSCaseSensitive, - MonoVersion = Duplicati.Library.Utility.Utility.IsMono ? Duplicati.Library.Utility.Utility.MonoVersion.ToString() : null, MachineName = System.Environment.MachineName, + PackageTypeId = Duplicati.Library.AutoUpdater.UpdaterManager.PackageTypeId, UserName = System.Environment.UserName, NewLine = System.Environment.NewLine, CLRVersion = System.Environment.Version.ToString(), diff --git a/Duplicati.Library.RestAPI/RESTMethods/Updates.cs b/Duplicati.Library.RestAPI/RESTMethods/Updates.cs index 5eec5a43b..94abc4491 100644 --- a/Duplicati.Library.RestAPI/RESTMethods/Updates.cs +++ b/Duplicati.Library.RestAPI/RESTMethods/Updates.cs @@ -33,23 +33,6 @@ namespace Duplicati.Server.WebServer.RESTMethods FIXMEGlobal.UpdatePoller.CheckNow(); info.OutputOK(); return; - - case "install": - FIXMEGlobal.UpdatePoller.InstallUpdate(); - info.OutputOK(); - return; - - case "activate": - if (FIXMEGlobal.WorkThread.CurrentTask != null || FIXMEGlobal.WorkThread.CurrentTasks.Count != 0) - { - info.ReportServerError("Cannot activate update while task is running or scheduled"); - } - else - { - FIXMEGlobal.UpdatePoller.ActivateUpdate(); - info.OutputOK(); - } - return; default: info.ReportClientError("No such action", System.Net.HttpStatusCode.NotFound); diff --git a/Duplicati.Library.RestAPI/Runner.cs b/Duplicati.Library.RestAPI/Runner.cs index 76554701b..5049c4100 100644 --- a/Duplicati.Library.RestAPI/Runner.cs +++ b/Duplicati.Library.RestAPI/Runner.cs @@ -61,7 +61,7 @@ namespace Duplicati.Server { Controller = controller; } - + public void Stop(bool allowCurrentFileToFinish) { var c = Controller; @@ -162,7 +162,8 @@ namespace Duplicati.Server public static IRunnerData CreateTask(Duplicati.Server.Serialization.DuplicatiOperation operation, Duplicati.Server.Serialization.Interface.IBackup backup, IDictionary extraOptions = null, string[] filterStrings = null) { - return new RunnerData() { + return new RunnerData() + { Operation = operation, Backup = backup, ExtraOptions = extraOptions, @@ -189,14 +190,14 @@ namespace Duplicati.Server filters); } - public static IRunnerData CreateRestoreTask(Duplicati.Server.Serialization.Interface.IBackup backup, string[] filters, - DateTime time, string restoreTarget, bool overwrite, bool restore_permissions, + public static IRunnerData CreateRestoreTask(Duplicati.Server.Serialization.Interface.IBackup backup, string[] filters, + DateTime time, string restoreTarget, bool overwrite, bool restore_permissions, bool skip_metadata, string passphrase) { var dict = new Dictionary { ["time"] = Library.Utility.Utility.SerializeDateTime(time.ToUniversalTime()), - ["overwrite"] = overwrite? Boolean.TrueString : Boolean.FalseString, + ["overwrite"] = overwrite ? Boolean.TrueString : Boolean.FalseString, ["restore-permissions"] = restore_permissions ? Boolean.TrueString : Boolean.FalseString, ["skip-metadata"] = skip_metadata ? Boolean.TrueString : Boolean.FalseString, ["allow-passphrase-change"] = Boolean.TrueString @@ -263,7 +264,7 @@ namespace Duplicati.Server public long CurrentFilesize { get { return m_currentFilesize; } } public long CurrentFileoffset { get { return m_currentFileoffset; } } public bool CurrentFilecomplete { get { return m_currentFilecomplete; } } - public string Phase { get { return m_phase.ToString(); } } + public string Phase { get { return m_phase.ToString(); } } public float OverallProgress { get { return m_overallProgress; } } public long ProcessedFileCount { get { return m_processedFileCount; } } public long ProcessedFileSize { get { return m_processedFileSize; } } @@ -285,7 +286,7 @@ namespace Duplicati.Server public Server.Serialization.Interface.IProgressEventData Copy() { - lock(m_lock) + lock (m_lock) { if (m_backendProgress != null) m_backendProgress.Update(out m_state.m_backendAction, out m_state.m_backendPath, out m_state.m_backendFileSize, out m_state.m_backendFileProgress, out m_state.m_backendSpeed, out m_state.m_backendIsBlocking); @@ -302,7 +303,7 @@ namespace Duplicati.Server #region IMessageSink implementation public void BackendEvent(Duplicati.Library.Main.BackendActionType action, Duplicati.Library.Main.BackendEventType type, string path, long size) { - lock(m_lock) + lock (m_lock) { m_state.m_backendAction = action; m_state.m_backendPath = path; @@ -344,7 +345,7 @@ namespace Duplicati.Server var options = ApplyOptions(backup, GetCommonOptions()); if (data.ExtraOptions != null) - foreach(var k in data.ExtraOptions) + foreach (var k in data.ExtraOptions) options[k.Key] = k.Value; var cf = FIXMEGlobal.DataConnection.Filters; @@ -352,37 +353,31 @@ namespace Duplicati.Server var sources = (from n in backup.Sources - let p = SpecialFolders.ExpandEnvironmentVariables(n) - where !string.IsNullOrWhiteSpace(p) - select p).ToArray(); + let p = SpecialFolders.ExpandEnvironmentVariables(n) + where !string.IsNullOrWhiteSpace(p) + select p).ToArray(); - var exe = - System.IO.Path.Combine( - Library.AutoUpdater.UpdaterManager.InstalledBaseDir, - System.IO.Path.GetFileName( - typeof(Duplicati.CommandLine.Commands).Assembly.Location - ) - ); + var exe = System.IO.Path.Combine( + Library.AutoUpdater.UpdaterManager.INSTALLATIONDIR, + Library.AutoUpdater.PackageHelper.GetExecutableName(Library.AutoUpdater.PackageHelper.NamedExecutable.CommandLine) + ); var cmd = new System.Text.StringBuilder(); - if (Library.Utility.Utility.IsMono) - cmd.Append("mono "); - cmd.Append(Library.Utility.Utility.WrapAsCommandLine(new string[] { exe, "backup", backup.TargetURL }, false)); cmd.Append(" "); cmd.Append(Library.Utility.Utility.WrapAsCommandLine(sources, true)); // TODO: We should check each option to see if it is a path, and allow expansion on that - foreach(var opt in options) + foreach (var opt in options) cmd.AppendFormat(" --{0}={1}", opt.Key, Library.Utility.Utility.WrapCommandLineElement(opt.Value, false)); if (cf != null) - foreach(var f in cf) + foreach (var f in cf) cmd.AppendFormat(" --{0}={1}", f.Include ? "include" : "exclude", Library.Utility.Utility.WrapCommandLineElement(f.Expression, true)); if (bf != null) - foreach(var f in bf) + foreach (var f in bf) cmd.AppendFormat(" --{0}={1}", f.Include ? "include" : "exclude", Library.Utility.Utility.WrapCommandLineElement(f.Expression, true)); return cmd.ToString(); @@ -438,7 +433,7 @@ namespace Duplicati.Server task.Run(sink); } - catch(Exception ex) + catch (Exception ex) { FIXMEGlobal.DataConnection.LogError(string.Empty, "Failed while executing custom task", ex); } @@ -465,7 +460,7 @@ namespace Duplicati.Server var options = ApplyOptions(backup, GetCommonOptions()); if (data.ExtraOptions != null) - foreach(var k in data.ExtraOptions) + foreach (var k in data.ExtraOptions) options[k.Key] = k.Value; // Pack in the system or task config for easy restore @@ -475,13 +470,14 @@ namespace Duplicati.Server } // Attach a log scope that tags all messages to relay the TaskID and BackupID - using (Library.Logging.Log.StartScope(log => { + using (Library.Logging.Log.StartScope(log => + { log[LogWriteHandler.LOG_EXTRA_TASKID] = data.TaskID.ToString(); log[LogWriteHandler.LOG_EXTRA_BACKUPID] = data.BackupID; })) - using(tempfolder) - using(var controller = new Duplicati.Library.Main.Controller(backup.TargetURL, options, sink)) + using (tempfolder) + using (var controller = new Duplicati.Library.Main.Controller(backup.TargetURL, options, sink)) { try { @@ -513,9 +509,9 @@ namespace Duplicati.Server var filter = ApplyFilter(backup, GetCommonFilter()); var sources = (from n in backup.Sources - let p = SpecialFolders.ExpandEnvironmentVariables(n) - where !string.IsNullOrWhiteSpace(p) - select p).ToArray(); + let p = SpecialFolders.ExpandEnvironmentVariables(n) + where !string.IsNullOrWhiteSpace(p) + select p).ToArray(); var r = controller.Backup(sources, filter); UpdateMetadata(backup, r); @@ -565,7 +561,7 @@ namespace Duplicati.Server } case DuplicatiOperation.CreateReport: { - using(var tf = new Duplicati.Library.Utility.TempFile()) + using (var tf = new Duplicati.Library.Utility.TempFile()) { var r = controller.CreateLogDatabase(tf); var tempid = FIXMEGlobal.DataConnection.RegisterTempFile("create-bug-report", r.TargetPath, DateTime.Now.AddDays(3)); @@ -706,7 +702,8 @@ namespace Duplicati.Server null, messageid, null, - (n, a) => { + (n, a) => + { return a.FirstOrDefault(x => x.BackupID == backup.ID) ?? n; } ); @@ -909,12 +906,12 @@ namespace Duplicati.Server options["backup-id"] = $"DB-{backup.ID}"; // Apply normal options - foreach(var o in backup.Settings) + foreach (var o in backup.Settings) if (!o.Name.StartsWith("--", StringComparison.Ordinal) && TestIfOptionApplies()) options[o.Name] = o.Value; // Apply override options - foreach(var o in backup.Settings) + foreach (var o in backup.Settings) if (o.Name.StartsWith("--", StringComparison.Ordinal) && TestIfOptionApplies()) options[o.Name.Substring(2)] = o.Value; @@ -931,12 +928,12 @@ namespace Duplicati.Server { var nf = (from n in f2 - let exp = - n.Expression.StartsWith("[", StringComparison.Ordinal) && n.Expression.EndsWith("]", StringComparison.Ordinal) - ? SpecialFolders.ExpandEnvironmentVariablesRegexp(n.Expression) - : SpecialFolders.ExpandEnvironmentVariables(n.Expression) - orderby n.Order - select (Library.Utility.IFilter)(new Library.Utility.FilterExpression(exp, n.Include))) + let exp = + n.Expression.StartsWith("[", StringComparison.Ordinal) && n.Expression.EndsWith("]", StringComparison.Ordinal) + ? SpecialFolders.ExpandEnvironmentVariablesRegexp(n.Expression) + : SpecialFolders.ExpandEnvironmentVariables(n.Expression) + orderby n.Order + select (Library.Utility.IFilter)(new Library.Utility.FilterExpression(exp, n.Include))) .Aggregate((a, b) => Library.Utility.FilterExpression.Combine(a, b)); filter = Library.Utility.FilterExpression.Combine(filter, nf); @@ -961,9 +958,9 @@ namespace Duplicati.Server return (from n in filters - orderby n.Order - let exp = Environment.ExpandEnvironmentVariables(n.Expression) - select (Duplicati.Library.Utility.IFilter)(new Duplicati.Library.Utility.FilterExpression(exp, n.Include))) + orderby n.Order + let exp = Environment.ExpandEnvironmentVariables(n.Expression) + select (Duplicati.Library.Utility.IFilter)(new Duplicati.Library.Utility.FilterExpression(exp, n.Include))) .Aggregate((a, b) => Duplicati.Library.Utility.FilterExpression.Combine(a, b)); } } diff --git a/Duplicati.Library.RestAPI/Serializable/ServerStatus.cs b/Duplicati.Library.RestAPI/Serializable/ServerStatus.cs index 6f0843018..df2a8f6e6 100644 --- a/Duplicati.Library.RestAPI/Serializable/ServerStatus.cs +++ b/Duplicati.Library.RestAPI/Serializable/ServerStatus.cs @@ -55,9 +55,9 @@ namespace Duplicati.Server.Serializable } } - public UpdatePollerStates UpdaterState { get { return FIXMEGlobal.UpdatePoller.ThreadState; } } + public string UpdateDownloadLink => FIXMEGlobal.DataConnection.ApplicationSettings.UpdatedVersion?.GetUpdateUrls()?.FirstOrDefault(); - public bool UpdateReady { get { return Duplicati.Library.AutoUpdater.UpdaterManager.HasUpdateInstalled; } } + public UpdatePollerStates UpdaterState { get { return FIXMEGlobal.UpdatePoller.ThreadState; } } public double UpdateDownloadProgress { get { return FIXMEGlobal.UpdatePoller.DownloadProgess; } } diff --git a/Duplicati.Library.RestAPI/UpdatePollThread.cs b/Duplicati.Library.RestAPI/UpdatePollThread.cs index 869f992d0..b5139db4b 100644 --- a/Duplicati.Library.RestAPI/UpdatePollThread.cs +++ b/Duplicati.Library.RestAPI/UpdatePollThread.cs @@ -30,7 +30,6 @@ namespace Duplicati.Server { private Thread m_thread; private volatile bool m_terminated = false; - private volatile bool m_download = false; private volatile bool m_forceCheck = false; private readonly object m_lock = new object(); private AutoResetEvent m_waitSignal; @@ -41,7 +40,7 @@ namespace Duplicati.Server public UpdatePollerStates ThreadState { get; private set; } public double DownloadProgess { - get { return m_downloadProgress ; } + get { return m_downloadProgress; } private set { @@ -66,35 +65,16 @@ namespace Duplicati.Server public void CheckNow() { - lock(m_lock) + lock (m_lock) { m_forceCheck = true; m_waitSignal.Set(); } } - public void InstallUpdate() - { - lock(m_lock) - { - m_forceCheck = true; - m_download = true; - m_waitSignal.Set(); - } - } - - public void ActivateUpdate() - { - if (Duplicati.Library.AutoUpdater.UpdaterManager.SetRunUpdate()) - { - IsUpdateRequested = true; - FIXMEGlobal.ApplicationExitEvent.Set(); - } - } - public void Terminate() { - lock(m_lock) + lock (m_lock) { m_terminated = true; m_waitSignal.Set(); @@ -130,12 +110,12 @@ namespace Duplicati.Server if (nextCheck < DateTime.UtcNow || m_forceCheck) { - lock(m_lock) + lock (m_lock) m_forceCheck = false; ThreadState = UpdatePollerStates.Checking; FIXMEGlobal.StatusEventNotifyer.SignalNewEvent(); - + DateTime started = DateTime.UtcNow; FIXMEGlobal.DataConnection.ApplicationSettings.LastUpdateCheck = started; nextCheck = FIXMEGlobal.DataConnection.ApplicationSettings.NextUpdateCheck; @@ -148,7 +128,7 @@ namespace Duplicati.Server rt = rt == Duplicati.Library.AutoUpdater.ReleaseType.Unknown ? Duplicati.Library.AutoUpdater.AutoUpdateSettings.DefaultUpdateChannel : rt; try - { + { var update = Duplicati.Library.AutoUpdater.UpdaterManager.CheckForUpdate(rt); if (update != null) FIXMEGlobal.DataConnection.ApplicationSettings.UpdatedVersion = update; @@ -166,52 +146,40 @@ namespace Duplicati.Server var updatertstring = FIXMEGlobal.DataConnection.ApplicationSettings.UpdatedVersion.ReleaseType; if (string.Equals(updatertstring, "preview", StringComparison.OrdinalIgnoreCase)) updatertstring = Library.AutoUpdater.ReleaseType.Experimental.ToString(); - + if (!Enum.TryParse(updatertstring, true, out updatert)) updatert = Duplicati.Library.AutoUpdater.ReleaseType.Nightly; if (updatert == Duplicati.Library.AutoUpdater.ReleaseType.Unknown) updatert = Duplicati.Library.AutoUpdater.ReleaseType.Nightly; - + if (updatert > rt) FIXMEGlobal.DataConnection.ApplicationSettings.UpdatedVersion = null; } - if (FIXMEGlobal.DataConnection.ApplicationSettings.UpdatedVersion != null && Duplicati.Library.AutoUpdater.UpdaterManager.TryParseVersion(FIXMEGlobal.DataConnection.ApplicationSettings.UpdatedVersion.Version) > System.Reflection.Assembly.GetExecutingAssembly().GetName().Version) + var updatedinfo = FIXMEGlobal.DataConnection.ApplicationSettings.UpdatedVersion; + if (updatedinfo != null && Duplicati.Library.AutoUpdater.UpdaterManager.TryParseVersion(updatedinfo.Version) > System.Reflection.Assembly.GetExecutingAssembly().GetName().Version) { + var package = updatedinfo.FindPackage(); + FIXMEGlobal.DataConnection.RegisterNotification( NotificationType.Information, "Found update", - FIXMEGlobal.DataConnection.ApplicationSettings.UpdatedVersion.Displayname, + updatedinfo.Displayname, null, null, "update:new", null, "NewUpdateFound", null, - (self, all) => { + (self, all) => + { return all.FirstOrDefault(x => x.Action == "update:new") ?? self; } ); } } - if (m_download) - { - lock(m_lock) - m_download = false; - - var v = FIXMEGlobal.DataConnection.ApplicationSettings.UpdatedVersion; - if (v != null) - { - ThreadState = UpdatePollerStates.Downloading; - FIXMEGlobal.StatusEventNotifyer.SignalNewEvent(); - - if (Duplicati.Library.AutoUpdater.UpdaterManager.DownloadAndUnpackUpdate(v, (pg) => { DownloadProgess = pg; })) - FIXMEGlobal.StatusEventNotifyer.SignalNewEvent(); - } - } - DownloadProgess = 0; if (ThreadState != UpdatePollerStates.Waiting) @@ -225,14 +193,14 @@ namespace Duplicati.Server // Guard against spin-loop if (waitTime.TotalSeconds < 5) waitTime = TimeSpan.FromSeconds(5); - + // Guard against year-long waits // A re-check does not cause an update check if (waitTime.TotalDays > 1) waitTime = TimeSpan.FromDays(1); - + m_waitSignal.WaitOne(waitTime, true); - } + } } } } diff --git a/Duplicati.Library.RestAPI/WebServer/Server.cs b/Duplicati.Library.RestAPI/WebServer/Server.cs index c8e4408f3..189b69c4b 100644 --- a/Duplicati.Library.RestAPI/WebServer/Server.cs +++ b/Duplicati.Library.RestAPI/WebServer/Server.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; using System.Linq; @@ -224,12 +224,12 @@ namespace Duplicati.Server.WebServer server.Add(new RESTHandler()); - string webroot = System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); - string install_webroot = System.IO.Path.Combine(Library.AutoUpdater.UpdaterManager.InstalledBaseDir, "webroot"); + string webroot = Library.AutoUpdater.UpdaterManager.INSTALLATIONDIR; + string install_webroot = System.IO.Path.Combine(webroot, "webroot"); #if DEBUG // Easy test for extensions while debugging - install_webroot = Library.AutoUpdater.UpdaterManager.InstalledBaseDir; + install_webroot = Library.AutoUpdater.UpdaterManager.INSTALLATIONDIR; if (!System.IO.Directory.Exists(System.IO.Path.Combine(webroot, "webroot"))) { @@ -260,13 +260,7 @@ namespace Duplicati.Server.WebServer // in the same folders as the running application, to avoid users // that inadvertently expose top level folders if (!string.IsNullOrWhiteSpace(userroot) - && - ( - userroot.StartsWith(Util.AppendDirSeparator(System.Reflection.Assembly.GetExecutingAssembly().Location), Library.Utility.Utility.ClientFilenameStringComparison) - || - userroot.StartsWith(Util.AppendDirSeparator(Duplicati.Library.AutoUpdater.UpdaterManager.InstalledBaseDir), Library.Utility.Utility.ClientFilenameStringComparison) - ) - ) + && userroot.StartsWith(Util.AppendDirSeparator(Duplicati.Library.Utility.Utility.getEntryAssembly().Location), Library.Utility.Utility.ClientFilenameStringComparison)) #endif { webroot = userroot; diff --git a/Duplicati.sln b/Duplicati.sln index d73ceee12..62a93a225 100644 --- a/Duplicati.sln +++ b/Duplicati.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.1.32210.238 @@ -101,11 +101,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.CommandLine.AutoU EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Backend.Storj", "Duplicati\Library\Backend\Storj\Duplicati.Library.Backend.Storj.csproj", "{E9AB8491-BD4C-4E4F-84C3-0BD551CC7489}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Backend.Tardigrade", "Duplicati\Library\Backend\Tardigrade\Duplicati.Library.Backend.Tardigrade.csproj", "{11A8D03B-8CAE-4AD6-A5C6-E15D34DA2BDE}" - ProjectSection(ProjectDependencies) = postProject - {E9AB8491-BD4C-4E4F-84C3-0BD551CC7489} = {E9AB8491-BD4C-4E4F-84C3-0BD551CC7489} - EndProjectSection -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Duplicati.Library.Backend.TencentCOS", "Duplicati\Library\Backend\TencentCOS\Duplicati.Library.Backend.TencentCOS.csproj", "{545DD6D4-9476-42D6-B51C-A28E000C489E}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Executables", "Executables", "{FA88A246-EF8E-46E3-90AF-539B8C0A6ADE}" @@ -357,10 +352,6 @@ Global {E9AB8491-BD4C-4E4F-84C3-0BD551CC7489}.Debug|Any CPU.Build.0 = Debug|Any CPU {E9AB8491-BD4C-4E4F-84C3-0BD551CC7489}.Release|Any CPU.ActiveCfg = Release|Any CPU {E9AB8491-BD4C-4E4F-84C3-0BD551CC7489}.Release|Any CPU.Build.0 = Release|Any CPU - {11A8D03B-8CAE-4AD6-A5C6-E15D34DA2BDE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {11A8D03B-8CAE-4AD6-A5C6-E15D34DA2BDE}.Debug|Any CPU.Build.0 = Debug|Any CPU - {11A8D03B-8CAE-4AD6-A5C6-E15D34DA2BDE}.Release|Any CPU.ActiveCfg = Release|Any CPU - {11A8D03B-8CAE-4AD6-A5C6-E15D34DA2BDE}.Release|Any CPU.Build.0 = Release|Any CPU {545DD6D4-9476-42D6-B51C-A28E000C489E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {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 @@ -467,7 +458,6 @@ Global {B93E3BF0-DAA7-49B7-B07D-0559C5816735} = {D19A38DD-68F1-4EF5-BF5F-8966CE0D9A5B} {4D1B198B-F773-44F2-872B-2B283053323C} = {D19A38DD-68F1-4EF5-BF5F-8966CE0D9A5B} {E9AB8491-BD4C-4E4F-84C3-0BD551CC7489} = {E1A9B303-F281-45C5-A4F6-CADD9DE3F3C4} - {11A8D03B-8CAE-4AD6-A5C6-E15D34DA2BDE} = {E1A9B303-F281-45C5-A4F6-CADD9DE3F3C4} {545DD6D4-9476-42D6-B51C-A28E000C489E} = {E1A9B303-F281-45C5-A4F6-CADD9DE3F3C4} {6B46F6B1-1898-49B8-ADA7-5CAF68EB77E3} = {FA88A246-EF8E-46E3-90AF-539B8C0A6ADE} {95B7DD83-2C5A-4F1E-8EA7-39654B2B236A} = {6B46F6B1-1898-49B8-ADA7-5CAF68EB77E3} diff --git a/Duplicati/CommandLine/BackendTester/Duplicati.CommandLine.BackendTester.csproj b/Duplicati/CommandLine/BackendTester/Duplicati.CommandLine.BackendTester.csproj index f15eb653a..72c126e05 100644 --- a/Duplicati/CommandLine/BackendTester/Duplicati.CommandLine.BackendTester.csproj +++ b/Duplicati/CommandLine/BackendTester/Duplicati.CommandLine.BackendTester.csproj @@ -2,7 +2,6 @@ net8.0 - Exe A backend debugging tool for Duplicati Duplicati.CommandLine.BackendTester.Implementation diff --git a/Duplicati/CommandLine/BackendTester/Program.cs b/Duplicati/CommandLine/BackendTester/Program.cs index 5671d7647..7b6973ebd 100644 --- a/Duplicati/CommandLine/BackendTester/Program.cs +++ b/Duplicati/CommandLine/BackendTester/Program.cs @@ -1,23 +1,23 @@ -// Copyright (C) 2024, The Duplicati Team -// https://duplicati.com, hello@duplicati.com -// -// 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. +// Copyright (C) 2024, The Duplicati Team +// https://duplicati.com, hello@duplicati.com +// +// 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. using System; using System.Collections.Generic; @@ -31,7 +31,6 @@ namespace Duplicati.CommandLine.BackendTester { public class Program { - /// /// Used to maintain a reference to initialized system settings. /// @@ -63,13 +62,7 @@ namespace Duplicati.CommandLine.BackendTester /// The main entry point for the application. /// [STAThread] - public static int Main(string[] args) - { - Duplicati.Library.AutoUpdater.UpdaterManager.IgnoreWebrootFolder = true; - return Duplicati.Library.AutoUpdater.UpdaterManager.RunFromMostRecent(typeof(Program).GetMethod("RealMain"), args); - } - - public static void RealMain(string[] _args) + public static int Main(string[] _args) { try { @@ -107,7 +100,7 @@ namespace Duplicati.CommandLine.BackendTester foreach (string s in lines) Console.WriteLine(s); - return; + return 0; } if (options.ContainsKey("tempdir") && !string.IsNullOrEmpty(options["tempdir"])) @@ -129,14 +122,17 @@ namespace Duplicati.CommandLine.BackendTester { Console.WriteLine("Starting run no {0}", i); if (!Run(args, options, i == 0)) - return; + return 1; } Console.WriteLine("Unittest complete!"); + return 0; } catch (Exception ex) { Console.WriteLine("Unittest failed: " + ex); } + + return 1; } static bool Run(List args, Dictionary options, bool first) diff --git a/Duplicati/CommandLine/BackendTool/Duplicati.CommandLine.BackendTool.csproj b/Duplicati/CommandLine/BackendTool/Duplicati.CommandLine.BackendTool.csproj index aa642fd86..60564fccf 100644 --- a/Duplicati/CommandLine/BackendTool/Duplicati.CommandLine.BackendTool.csproj +++ b/Duplicati/CommandLine/BackendTool/Duplicati.CommandLine.BackendTool.csproj @@ -2,7 +2,6 @@ net8.0 - Exe Duplicati.CommandLine.BackendTool.Implementation diff --git a/Duplicati/CommandLine/BackendTool/Program.cs b/Duplicati/CommandLine/BackendTool/Program.cs index b367cddfc..0a8d22f4b 100644 --- a/Duplicati/CommandLine/BackendTool/Program.cs +++ b/Duplicati/CommandLine/BackendTool/Program.cs @@ -34,13 +34,7 @@ namespace Duplicati.CommandLine.BackendTool /// The main entry point for the application. /// [STAThread] - public static int Main(string[] args) - { - Duplicati.Library.AutoUpdater.UpdaterManager.IgnoreWebrootFolder = true; - return Duplicati.Library.AutoUpdater.UpdaterManager.RunFromMostRecent(typeof(Program).GetMethod("RealMain"), args); - } - - public static int RealMain(string[] _args) + public static int Main(string[] _args) { bool debugoutput = false; try diff --git a/Duplicati/CommandLine/CLI/Help.cs b/Duplicati/CommandLine/CLI/Help.cs index 0e19bea98..528788ccf 100644 --- a/Duplicati/CommandLine/CLI/Help.cs +++ b/Duplicati/CommandLine/CLI/Help.cs @@ -24,6 +24,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; +using Duplicati.Library.AutoUpdater; using Duplicati.Library.Common; using FilterGroup = Duplicati.Library.Utility.FilterGroup; @@ -117,8 +118,7 @@ namespace Duplicati.CommandLine tp = tp.Replace("%VERSION%", License.VersionNumbers.Version); tp = tp.Replace("%BACKENDS%", string.Join(", ", Library.DynamicLoader.BackendLoader.Keys)); - tp = tp.Replace("%MONO%", Library.Utility.Utility.IsMono ? "mono " : ""); - tp = tp.Replace("%APP_PATH%", System.IO.Path.GetFileName(System.Reflection.Assembly.GetExecutingAssembly().Location)); + tp = tp.Replace("%APP_PATH%", Path.Combine(UpdaterManager.INSTALLATIONDIR, PackageHelper.GetExecutableName(PackageHelper.NamedExecutable.CommandLine))); tp = tp.Replace("%PATH_SEPARATOR%", System.IO.Path.PathSeparator.ToString()); tp = tp.Replace("%EXAMPLE_SOURCE_PATH%", Platform.IsClientPosix ? "/source" : @"D:\source"); tp = tp.Replace("%EXAMPLE_SOURCE_FILE%", Platform.IsClientPosix ? "/source/myfile.txt" : @"D:\source\file.txt"); diff --git a/Duplicati/CommandLine/CLI/Program.cs b/Duplicati/CommandLine/CLI/Program.cs index 57c82a57a..eeb59d93f 100644 --- a/Duplicati/CommandLine/CLI/Program.cs +++ b/Duplicati/CommandLine/CLI/Program.cs @@ -1,23 +1,23 @@ -// Copyright (C) 2024, The Duplicati Team -// https://duplicati.com, hello@duplicati.com -// -// 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. +// Copyright (C) 2024, The Duplicati Team +// https://duplicati.com, hello@duplicati.com +// +// 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. using System; using System.Collections.Generic; @@ -25,7 +25,7 @@ using System.Linq; using Duplicati.Library.Localization.Short; using System.IO; using Duplicati.Library.Interface; -using Duplicati.Library.Logging; +using Duplicati.Library.AutoUpdater; namespace Duplicati.CommandLine { @@ -39,12 +39,6 @@ namespace Duplicati.CommandLine /// The main entry point for the application. /// public static int Main(string[] args) - { - Duplicati.Library.AutoUpdater.UpdaterManager.IgnoreWebrootFolder = true; - return Duplicati.Library.AutoUpdater.UpdaterManager.RunFromMostRecent(typeof(Program).GetMethod("RealMain"), args); - } - - public static int RealMain(string[] args) { Library.UsageReporter.Reporter.Initialize(); FROM_COMMANDLINE = true; @@ -100,7 +94,7 @@ namespace Duplicati.CommandLine private static int ShowChangeLog(TextWriter outwriter) { - var path = System.IO.Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "changelog.txt"); + var path = System.IO.Path.Combine(UpdaterManager.INSTALLATIONDIR, "changelog.txt"); outwriter.WriteLine(System.IO.File.ReadAllText(path)); return 0; } @@ -113,18 +107,28 @@ namespace Duplicati.CommandLine if (update != null && update.Version != Library.AutoUpdater.UpdaterManager.SelfVersion.Version) { - outwriter.WriteLine("Found update \"{0}\", downloading ...", update.Displayname); - long lastpg = 0; - Library.AutoUpdater.UpdaterManager.DownloadAndUnpackUpdate(update, f => + var package = update.FindPackage(); + if (package == null) { - var npg = (long)(f * 100); - if (Math.Abs(npg - lastpg) >= 5 || (npg == 100 && lastpg != 100)) + outwriter.WriteLine($"Failed to locate a matching package for this machine, please visit this link and select the correct package: {update.GetGenericUpdatePageUrl()}"); + } + else + { + var filename = Path.GetFullPath(package.GetFilename()); + outwriter.WriteLine("Downloading update \"{0}\" to {1} ...", update.Displayname, filename); + + long lastpg = 0; + Library.AutoUpdater.UpdaterManager.DownloadUpdate(update, package, filename, f => { - lastpg = npg; - outwriter.WriteLine("Downloading {0}% ...", npg); - } - }); - outwriter.WriteLine("Update \"{0}\" ({1}) installed, using on next launch", update.Displayname, update.Version); + var npg = (long)(f * 100); + if (Math.Abs(npg - lastpg) >= 5 || (npg == 100 && lastpg != 100)) + { + lastpg = npg; + outwriter.WriteLine("Downloading {0}% ...", npg); + } + }); + outwriter.WriteLine("Update \"{0}\" ({1}) downloaded", update.Displayname, update.Version); + } } } diff --git a/Duplicati/CommandLine/CLI/help.txt b/Duplicati/CommandLine/CLI/help.txt index 3005d305e..d282b71ca 100644 --- a/Duplicati/CommandLine/CLI/help.txt +++ b/Duplicati/CommandLine/CLI/help.txt @@ -19,7 +19,7 @@ See duplicati.commandline.exe help for more information. Formats: date, time, size, encryption, compression Advanced: mail, advanced, returncodes, filter, filter-groups,