Merge branch 'refs/heads/feature/kestrel-avalonia-upgrade' into feature/http-server-modernization

# Conflicts:
#	Duplicati.Library.RestAPI/UpdatePollThread.cs
#	Duplicati/Server/Program.cs
#	Duplicati/Server/webroot/ngax/scripts/services/ServerStatus.js
#	Duplicati/WebserverCore/DuplicatiWebserver.cs
This commit is contained in:
natan
2024-04-22 09:35:53 +02:00
235 changed files with 5945 additions and 5567 deletions
@@ -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);
@@ -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;
@@ -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));
}
}
@@ -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(),
@@ -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);
+43 -46
View File
@@ -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<string, string> 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<string, string>
{
["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));
}
}
@@ -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; } }
+18 -50
View File
@@ -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<Library.AutoUpdater.ReleaseType>(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);
}
}
}
}
}
+5 -11
View File
@@ -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;