From 12fb0d36eab427938fb66dd876fcca9dfaf1479c Mon Sep 17 00:00:00 2001 From: Kenneth Skovhede Date: Wed, 5 Jun 2024 11:02:56 +0200 Subject: [PATCH] Removed unused Signer.exe program Updated some licenses that snuck in LGPL text from the Kestrel merge. --- Duplicati.Library.RestAPI/Database/Backup.cs | 52 +- .../Database/Connection.cs | 439 +++++++++-------- Duplicati.Library.RestAPI/Database/Filter.cs | 36 +- .../Database/Notification.cs | 38 +- .../Database/Schedule.cs | 70 +-- .../Database/ServerSettings.cs | 137 +++--- Duplicati.Library.RestAPI/Database/Setting.cs | 36 +- .../Database/TempFile.cs | 36 +- Duplicati.Library.RestAPI/LiveControls.cs | 52 +- Duplicati.Library.RestAPI/LogWriteHandler.cs | 82 ++-- Duplicati.Library.RestAPI/Scheduler.cs | 93 ++-- Duplicati.Library.RestAPI/SpecialFolders.cs | 52 +- Duplicati.Library.RestAPI/UpdatePollThread.cs | 36 +- Duplicati/WebserverCore/RestHandlerCore.cs | 36 +- thirdparty/Signer/README-Setup.txt | 17 - thirdparty/Signer/Signer.exe | Bin 27136 -> 0 bytes thirdparty/Signer/download.txt | 1 - thirdparty/Signer/license.txt | 458 ------------------ thirdparty/Signer/licensedata.json | 7 - 19 files changed, 631 insertions(+), 1047 deletions(-) delete mode 100644 thirdparty/Signer/README-Setup.txt delete mode 100755 thirdparty/Signer/Signer.exe delete mode 100755 thirdparty/Signer/download.txt delete mode 100755 thirdparty/Signer/license.txt delete mode 100644 thirdparty/Signer/licensedata.json diff --git a/Duplicati.Library.RestAPI/Database/Backup.cs b/Duplicati.Library.RestAPI/Database/Backup.cs index 117253942..c71a8bda9 100644 --- a/Duplicati.Library.RestAPI/Database/Backup.cs +++ b/Duplicati.Library.RestAPI/Database/Backup.cs @@ -1,20 +1,24 @@ -// Copyright (C) 2015, The Duplicati Team +// 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. -// http://www.duplicati.com, info@duplicati.com -// -// This library is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as -// published by the Free Software Foundation; either version 2.1 of the -// License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA using System; using Duplicati.Server.Serialization.Interface; using System.Collections.Generic; @@ -46,7 +50,7 @@ namespace Duplicati.Server.Database { this.ID = null; } - + internal void LoadChildren(Connection con) { if (this.IsTemporary) @@ -65,7 +69,7 @@ namespace Duplicati.Server.Database this.Metadata = con.GetMetadata(id); } } - + /// /// The backup ID /// @@ -90,27 +94,27 @@ namespace Duplicati.Server.Database /// The path to the local database /// public string DBPath { get; internal set; } - + /// /// The backup source folders and files /// public string[] Sources { get; set; } - + /// /// The backup settings /// public ISetting[] Settings { get; set; } - + /// /// The filters applied to the source files /// public IFilter[] Filters { get; set; } - + /// /// The backup metadata /// - public IDictionary Metadata { get; set; } - + public IDictionary Metadata { get; set; } + /// /// Gets a value indicating if this instance is not persisted to the database /// diff --git a/Duplicati.Library.RestAPI/Database/Connection.cs b/Duplicati.Library.RestAPI/Database/Connection.cs index fc47340ef..080b345b0 100644 --- a/Duplicati.Library.RestAPI/Database/Connection.cs +++ b/Duplicati.Library.RestAPI/Database/Connection.cs @@ -1,20 +1,24 @@ -// Copyright (C) 2015, The Duplicati Team +// 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. -// http://www.duplicati.com, info@duplicati.com -// -// This library is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as -// published by the Free Software Foundation; either version 2.1 of the -// License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA using System; using System.Collections.Generic; using System.Linq; @@ -38,15 +42,15 @@ namespace Duplicati.Server.Database m_connection = connection; m_errorcmd = m_connection.CreateCommand(); m_errorcmd.CommandText = @"INSERT INTO ""ErrorLog"" (""BackupID"", ""Message"", ""Exception"", ""Timestamp"") VALUES (?,?,?,?)"; - for(var i = 0; i < 4; i++) + for (var i = 0; i < 4; i++) m_errorcmd.Parameters.Add(m_errorcmd.CreateParameter()); - + this.ApplicationSettings = new ServerSettings(this); } - + public void LogError(string backupid, string message, Exception ex) { - lock(m_lock) + lock (m_lock) { if (!long.TryParse(backupid, out long id)) id = -1; @@ -57,49 +61,50 @@ namespace Duplicati.Server.Database m_errorcmd.ExecuteNonQuery(); } } - + internal void ExecuteWithCommand(Action f) { - lock(m_lock) - using(var cmd = m_connection.CreateCommand()) + lock (m_lock) + using (var cmd = m_connection.CreateCommand()) f(cmd); } internal Serializable.ImportExportStructure PrepareBackupForExport(IBackup backup) { var scheduleId = GetScheduleIDsFromTags(new string[] { "ID=" + backup.ID }); - return new Serializable.ImportExportStructure() { - CreatedByVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(), - Backup = (Database.Backup)backup, - Schedule = (Database.Schedule)(scheduleId.Any() ? GetSchedule(scheduleId.First()) : null), - DisplayNames = SpecialFolders.GetSourceNames(backup) - }; + return new Serializable.ImportExportStructure() + { + CreatedByVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString(), + Backup = (Database.Backup)backup, + Schedule = (Database.Schedule)(scheduleId.Any() ? GetSchedule(scheduleId.First()) : null), + DisplayNames = SpecialFolders.GetSourceNames(backup) + }; } - + public string RegisterTemporaryBackup(IBackup backup) { - lock(m_lock) + lock (m_lock) { if (backup == null) throw new ArgumentNullException(nameof(backup)); if (backup.ID != null) throw new ArgumentException("Backup is already active, cannot make temporary"); - + backup.ID = Guid.NewGuid().ToString("D"); m_temporaryBackups.Add(backup.ID, (Backup)backup); return backup.ID; } } - + public void UnregisterTemporaryBackup(IBackup backup) { - lock(m_lock) + lock (m_lock) m_temporaryBackups.Remove(backup.ID); } public void UpdateTemporaryBackup(IBackup backup) { - lock(m_lock) + lock (m_lock) if (m_temporaryBackups.Remove(backup.ID)) m_temporaryBackups.Add(backup.ID, (Backup)backup); } @@ -108,8 +113,8 @@ namespace Duplicati.Server.Database { if (string.IsNullOrEmpty(id)) return null; - - lock(m_lock) + + lock (m_lock) { Backup b; m_temporaryBackups.TryGetValue(id, out b); @@ -118,10 +123,10 @@ namespace Duplicati.Server.Database } public ServerSettings ApplicationSettings { get; private set; } - + internal IDictionary GetMetadata(long id) { - lock(m_lock) + lock (m_lock) return ReadFromDb( (rd) => new KeyValuePair( ConvertToString(rd, 0), @@ -130,11 +135,11 @@ namespace Duplicati.Server.Database @"SELECT ""Name"", ""Value"" FROM ""Metadata"" WHERE ""BackupID"" = ? ", id) .ToDictionary((k) => k.Key, (k) => k.Value); } - + internal void SetMetadata(IDictionary values, long id, System.Data.IDbTransaction transaction) { - lock(m_lock) - using(var tr = transaction == null ? m_connection.BeginTransaction() : null) + lock (m_lock) + using (var tr = transaction == null ? m_connection.BeginTransaction() : null) { OverwriteAndUpdateDb( tr, @@ -143,29 +148,30 @@ namespace Duplicati.Server.Database @"INSERT INTO ""Metadata"" (""BackupID"", ""Name"", ""Value"") VALUES (?, ?, ?)", (f) => new object[] { id, f.Key, f.Value } ); - + if (tr != null) tr.Commit(); } } - + internal IFilter[] GetFilters(long id) { - lock(m_lock) + lock (m_lock) return ReadFromDb( - (rd) => (IFilter)new Filter() { - Order = ConvertToInt64(rd, 0), + (rd) => (IFilter)new Filter() + { + Order = ConvertToInt64(rd, 0), Include = ConvertToBoolean(rd, 1), Expression = ConvertToString(rd, 2) ?? "" }, @"SELECT ""Order"", ""Include"", ""Expression"" FROM ""Filter"" WHERE ""BackupID"" = ? ORDER BY ""Order"" ", id) .ToArray(); } - + internal void SetFilters(IEnumerable values, long id, System.Data.IDbTransaction transaction = null) { - lock(m_lock) - using(var tr = transaction == null ? m_connection.BeginTransaction() : null) + lock (m_lock) + using (var tr = transaction == null ? m_connection.BeginTransaction() : null) { OverwriteAndUpdateDb( tr, @@ -174,7 +180,7 @@ namespace Duplicati.Server.Database @"INSERT INTO ""Filter"" (""BackupID"", ""Order"", ""Include"", ""Expression"") VALUES (?, ?, ?, ?)", (f) => new object[] { id, f.Order, f.Include, f.Expression } ); - + if (tr != null) tr.Commit(); } @@ -182,9 +188,10 @@ namespace Duplicati.Server.Database public ISetting[] GetSettings(long id) { - lock(m_lock) + lock (m_lock) return ReadFromDb( - (rd) => (ISetting)new Setting() { + (rd) => (ISetting)new Setting() + { Filter = ConvertToString(rd, 0) ?? "", Name = ConvertToString(rd, 1) ?? "", Value = ConvertToString(rd, 2) ?? "" @@ -193,42 +200,43 @@ namespace Duplicati.Server.Database @"SELECT ""Filter"", ""Name"", ""Value"" FROM ""Option"" WHERE ""BackupID"" = ?", id) .ToArray(); } - + internal void SetSettings(IEnumerable values, long id, System.Data.IDbTransaction transaction = null) { - lock(m_lock) - using(var tr = transaction == null ? m_connection.BeginTransaction() : null) + lock (m_lock) + using (var tr = transaction == null ? m_connection.BeginTransaction() : null) { OverwriteAndUpdateDb( tr, @"DELETE FROM ""Option"" WHERE ""BackupID"" = ?", new object[] { id }, values, @"INSERT INTO ""Option"" (""BackupID"", ""Filter"", ""Name"", ""Value"") VALUES (?, ?, ?, ?)", - (f) => { + (f) => + { if (Duplicati.Server.WebServer.Server.PASSWORD_PLACEHOLDER.Equals(f.Value)) throw new Exception("Attempted to save a property with the placeholder password"); return new object[] { id, f.Filter ?? "", f.Name, f.Value ?? "" }; } - ); - + ); + if (tr != null) tr.Commit(); } } - + internal string[] GetSources(long id) { - lock(m_lock) + lock (m_lock) return ReadFromDb( (rd) => ConvertToString(rd, 0), @"SELECT ""Path"" FROM ""Source"" WHERE ""BackupID"" = ?", id) .ToArray(); } - + internal void SetSources(IEnumerable values, long id, System.Data.IDbTransaction transaction) { - lock(m_lock) - using(var tr = transaction == null ? m_connection.BeginTransaction() : null) + lock (m_lock) + using (var tr = transaction == null ? m_connection.BeginTransaction() : null) { OverwriteAndUpdateDb( tr, @@ -236,8 +244,8 @@ namespace Duplicati.Server.Database values, @"INSERT INTO ""Source"" (""BackupID"", ""Path"") VALUES (?, ?)", (f) => new object[] { id, f } - ); - + ); + if (tr != null) tr.Commit(); } @@ -247,16 +255,16 @@ namespace Duplicati.Server.Database { if (tags == null || tags.Length == 0) return new long[0]; - + if (tags.Length == 1 && tags[0].StartsWith("ID=", StringComparison.Ordinal)) return new long[] { long.Parse(tags[0].Substring("ID=".Length)) }; - - lock(m_lock) - using(var cmd = m_connection.CreateCommand()) + + lock (m_lock) + using (var cmd = m_connection.CreateCommand()) { var sb = new StringBuilder(); - - foreach(var t in tags) + + foreach (var t in tags) { if (sb.Length != 0) sb.Append(" OR "); @@ -268,7 +276,7 @@ namespace Duplicati.Server.Database } cmd.CommandText = @"SELECT ""ID"" FROM ""Backup"" WHERE " + sb; - + return Read(cmd, (rd) => ConvertToInt64(rd, 0)).ToArray(); } } @@ -283,10 +291,11 @@ namespace Duplicati.Server.Database internal IBackup GetBackup(long id) { - lock(m_lock) + lock (m_lock) { var bk = ReadFromDb( - (rd) => new Backup { + (rd) => new Backup + { ID = ConvertToInt64(rd, 0).ToString(), Name = ConvertToString(rd, 1), Description = ConvertToString(rd, 2), @@ -296,20 +305,21 @@ namespace Duplicati.Server.Database }, @"SELECT ""ID"", ""Name"", ""Description"", ""Tags"", ""TargetURL"", ""DBPath"" FROM ""Backup"" WHERE ID = ?", id) .FirstOrDefault(); - + if (bk != null) bk.LoadChildren(this); - + return bk; } } - + internal ISchedule GetSchedule(long id) { - lock(m_lock) + lock (m_lock) { var bk = ReadFromDb( - (rd) => new Schedule { + (rd) => new Schedule + { ID = ConvertToInt64(rd, 0), Tags = (ConvertToString(rd, 1) ?? "").Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries), Time = ConvertToDateTime(rd, 2), @@ -349,13 +359,13 @@ namespace Duplicati.Server.Database { if (tags == null || tags.Length == 0) return new long[0]; - - lock(m_lock) - using(var cmd = m_connection.CreateCommand()) + + lock (m_lock) + using (var cmd = m_connection.CreateCommand()) { var sb = new StringBuilder(); - - foreach(var t in tags) + + foreach (var t in tags) { if (sb.Length != 0) sb.Append(" OR "); @@ -367,7 +377,7 @@ namespace Duplicati.Server.Database } cmd.CommandText = @"SELECT ""ID"" FROM ""Schedule"" WHERE " + sb; - + return Read(cmd, (rd) => ConvertToInt64(rd, 0)).ToArray(); } } @@ -387,7 +397,7 @@ namespace Duplicati.Server.Database if (item.Sources == null || item.Sources.Any(x => string.IsNullOrWhiteSpace(x)) || item.Sources.Length == 0) return "Invalid source list"; - + var disabled_encryption = false; var passphrase = string.Empty; var gpgAsymmetricEncryption = false; @@ -449,7 +459,8 @@ namespace Duplicati.Server.Database if (!string.IsNullOrWhiteSpace(s.Value) && s.Value.Contains("-")) return "The prefix cannot contain hyphens (-)"; } - else if (string.Equals(s.Name, "--gpg-encryption-command", StringComparison.OrdinalIgnoreCase)) { + else if (string.Equals(s.Name, "--gpg-encryption-command", StringComparison.OrdinalIgnoreCase)) + { gpgAsymmetricEncryption = string.Equals(s.Value, "--encrypt", StringComparison.OrdinalIgnoreCase); } } @@ -485,9 +496,9 @@ namespace Duplicati.Server.Database { cmd.Transaction = tr; cmd.Parameters.Add(cmd.CreateParameter()); - ((System.Data.IDbDataParameter) cmd.Parameters[0]).Value = path; + ((System.Data.IDbDataParameter)cmd.Parameters[0]).Value = path; cmd.Parameters.Add(cmd.CreateParameter()); - ((System.Data.IDbDataParameter) cmd.Parameters[1]).Value = item.ID; + ((System.Data.IDbDataParameter)cmd.Parameters[1]).Value = item.ID; cmd.CommandText = @"UPDATE ""Backup"" SET ""DBPath""=? WHERE ""ID""=?"; cmd.ExecuteNonQuery(); @@ -502,7 +513,7 @@ namespace Duplicati.Server.Database private void AddOrUpdateBackup(IBackup item, bool updateSchedule, ISchedule schedule) { - lock(m_lock) + lock (m_lock) { bool update = item.ID != null; if (!update && item.DBPath == null) @@ -511,7 +522,7 @@ namespace Duplicati.Server.Database if (!System.IO.Directory.Exists(folder)) System.IO.Directory.CreateDirectory(folder); - for(var i = 0; i < 100; i++) + for (var i = 0; i < 100; i++) { var guess = System.IO.Path.Combine(folder, System.IO.Path.ChangeExtension(Duplicati.Library.Main.DatabaseLocator.GenerateRandomName(), ".sqlite")); if (!System.IO.File.Exists(guess)) @@ -520,12 +531,12 @@ namespace Duplicati.Server.Database break; } } - + if (item.DBPath == null) throw new Exception("Unable to generate a unique database file name"); } - using(var tr = m_connection.BeginTransaction()) + using (var tr = m_connection.BeginTransaction()) { OverwriteAndUpdateDb( tr, @@ -535,13 +546,14 @@ namespace Duplicati.Server.Database update ? @"UPDATE ""Backup"" SET ""Name""=?, ""Description""=?, ""Tags""=?, ""TargetURL""=? WHERE ""ID""=?" : @"INSERT INTO ""Backup"" (""Name"", ""Description"", ""Tags"", ""TargetURL"", ""DBPath"") VALUES (?,?,?,?,?)", - (n) => { - + (n) => + { + if (n.TargetURL.IndexOf(Duplicati.Server.WebServer.Server.PASSWORD_PLACEHOLDER, StringComparison.Ordinal) >= 0) throw new Exception("Attempted to save a backup with the password placeholder"); if (update && long.Parse(n.ID) <= 0) throw new Exception("Invalid update, cannot update application settings through update method"); - + return new object[] { n.Name, n.Description ?? "" , // Description is optional but the column is set to NOT NULL, an additional check is welcome @@ -552,13 +564,13 @@ namespace Duplicati.Server.Database }); if (!update) - using(var cmd = m_connection.CreateCommand()) + using (var cmd = m_connection.CreateCommand()) { cmd.Transaction = tr; cmd.CommandText = @"SELECT last_insert_rowid();"; item.ID = ExecuteScalarInt64(cmd).ToString(); } - + var id = long.Parse(item.ID); if (long.Parse(item.ID) <= 0) @@ -568,10 +580,10 @@ namespace Duplicati.Server.Database SetSettings(item.Settings, id, tr); SetFilters(item.Filters, id, tr); SetMetadata(item.Metadata, id, tr); - + if (updateSchedule) { - var tags = new string[] { "ID=" + item.ID }; + var tags = new string[] { "ID=" + item.ID }; var existing = GetScheduleIDsFromTags(tags); if (schedule == null && existing.Any()) DeleteFromDb("Schedule", existing.First(), tr); @@ -584,30 +596,30 @@ namespace Duplicati.Server.Database cur.Repeat = schedule.Repeat; cur.Tags = schedule.Tags; cur.Time = schedule.Time; - + schedule = cur; } else { schedule.ID = -1; } - + schedule.Tags = tags; AddOrUpdateSchedule(schedule, tr); } } - + tr.Commit(); FIXMEGlobal.IncrementLastDataUpdateID(); FIXMEGlobal.StatusEventNotifyer.SignalNewEvent(); } } } - + internal void AddOrUpdateSchedule(ISchedule item) { - lock(m_lock) - using(var tr = m_connection.BeginTransaction()) + lock (m_lock) + using (var tr = m_connection.BeginTransaction()) { AddOrUpdateSchedule(item, tr); tr.Commit(); @@ -615,10 +627,10 @@ namespace Duplicati.Server.Database FIXMEGlobal.StatusEventNotifyer.SignalNewEvent(); } } - + private void AddOrUpdateSchedule(ISchedule item, System.Data.IDbTransaction tr) { - lock(m_lock) + lock (m_lock) { bool update = item.ID >= 0; OverwriteAndUpdateDb( @@ -637,9 +649,9 @@ namespace Duplicati.Server.Database n.Rule ?? "", update ? (object)item.ID : null }); - + if (!update) - using(var cmd = m_connection.CreateCommand()) + using (var cmd = m_connection.CreateCommand()) { cmd.Transaction = tr; cmd.CommandText = @"SELECT last_insert_rowid();"; @@ -652,10 +664,10 @@ namespace Duplicati.Server.Database { if (ID < 0) return; - - lock(m_lock) + + lock (m_lock) { - using(var tr = m_connection.BeginTransaction()) + using (var tr = m_connection.BeginTransaction()) { var existing = GetScheduleIDsFromTags(new string[] { "ID=" + ID.ToString() }); if (existing.Any()) @@ -669,7 +681,7 @@ namespace Duplicati.Server.Database DeleteFromDb("Source", ID, "BackupID", tr); DeleteFromDb("Backup", ID, tr); - + tr.Commit(); } } @@ -677,7 +689,7 @@ namespace Duplicati.Server.Database FIXMEGlobal.IncrementLastDataUpdateID(); FIXMEGlobal.StatusEventNotifyer.SignalNewEvent(); } - + public void DeleteBackup(IBackup backup) { if (backup.IsTemporary) @@ -685,32 +697,33 @@ namespace Duplicati.Server.Database else DeleteBackup(long.Parse(backup.ID)); } - + public void DeleteSchedule(long ID) { if (ID < 0) return; - - lock(m_lock) + + lock (m_lock) DeleteFromDb("Schedule", ID); - + FIXMEGlobal.IncrementLastDataUpdateID(); FIXMEGlobal.StatusEventNotifyer.SignalNewEvent(); } - + public void DeleteSchedule(ISchedule schedule) { DeleteSchedule(schedule.ID); } - + public IBackup[] Backups { get { - lock(m_lock) + lock (m_lock) { var lst = ReadFromDb( - (rd) => (IBackup)new Backup() { + (rd) => (IBackup)new Backup() + { ID = ConvertToInt64(rd, 0).ToString(), Name = ConvertToString(rd, 1), Description = ConvertToString(rd, 2), @@ -720,23 +733,24 @@ namespace Duplicati.Server.Database }, @"SELECT ""ID"", ""Name"", ""Description"", ""Tags"", ""TargetURL"", ""DBPath"" FROM ""Backup"" ") .ToArray(); - - foreach(var n in lst) + + foreach (var n in lst) n.Metadata = GetMetadata(long.Parse(n.ID)); - - + + return lst; } } } - + public ISchedule[] Schedules { get { - lock(m_lock) + lock (m_lock) return ReadFromDb( - (rd) => (ISchedule)new Schedule() { + (rd) => (ISchedule)new Schedule() + { ID = ConvertToInt64(rd, 0), Tags = (ConvertToString(rd, 1) ?? "").Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries), Time = ConvertToDateTime(rd, 2), @@ -746,16 +760,16 @@ namespace Duplicati.Server.Database }, @"SELECT ""ID"", ""Tags"", ""Time"", ""Repeat"", ""LastRun"", ""Rule"" FROM ""Schedule"" ") .ToArray(); - } + } } - - + + public IFilter[] Filters { get { return GetFilters(ANY_BACKUP_ID); } set { SetFilters(value, ANY_BACKUP_ID); } } - + public ISetting[] Settings { get { return GetSettings(ANY_BACKUP_ID); } @@ -764,13 +778,13 @@ namespace Duplicati.Server.Database public INotification[] GetNotifications() { - lock(m_lock) + lock (m_lock) return ReadFromDb(null).Cast().ToArray(); } public bool DismissNotification(long id) { - lock(m_lock) + lock (m_lock) { var notifications = GetNotifications(); var cur = notifications.FirstOrDefault(x => x.ID == id); @@ -790,7 +804,7 @@ namespace Duplicati.Server.Database public void RegisterNotification(Serialization.NotificationType type, string title, string message, Exception ex, string backupid, string action, string logid, string messageid, string logtag, Func conflicthandler) { - lock(m_lock) + lock (m_lock) { var notification = new Notification() { @@ -810,7 +824,7 @@ namespace Duplicati.Server.Database var conflictResult = conflicthandler(notification, GetNotifications()); if (conflictResult == null) return; - + if (conflictResult != notification) DeleteFromDb(typeof(Notification).Name, conflictResult.ID); @@ -829,7 +843,7 @@ namespace Duplicati.Server.Database //Workaround to clean up the database after invalid settings update public void FixInvalidBackupId() { - using(var cmd = m_connection.CreateCommand()) + using (var cmd = m_connection.CreateCommand()) using (var tr = m_connection.BeginTransaction()) { cmd.Transaction = tr; @@ -858,7 +872,7 @@ namespace Duplicati.Server.Database public string[] GetUISettingsSchemes() { - lock(m_lock) + lock (m_lock) return ReadFromDb( (rd) => ConvertToString(rd, 0) ?? "", @"SELECT DISTINCT ""Scheme"" FROM ""UIStorage""") @@ -867,33 +881,34 @@ namespace Duplicati.Server.Database public IDictionary GetUISettings(string scheme) { - lock(m_lock) + lock (m_lock) return ReadFromDb( (rd) => new KeyValuePair( ConvertToString(rd, 0) ?? "", ConvertToString(rd, 1) ?? "" ), - @"SELECT ""Key"", ""Value"" FROM ""UIStorage"" WHERE ""Scheme"" = ?", + @"SELECT ""Key"", ""Value"" FROM ""UIStorage"" WHERE ""Scheme"" = ?", scheme) .GroupBy(x => x.Key) .ToDictionary(x => x.Key, x => x.Last().Value); } - + public void SetUISettings(string scheme, IDictionary values, System.Data.IDbTransaction transaction = null) { - lock(m_lock) - using(var tr = transaction == null ? m_connection.BeginTransaction() : null) + lock (m_lock) + using (var tr = transaction == null ? m_connection.BeginTransaction() : null) { OverwriteAndUpdateDb( tr, @"DELETE FROM ""UIStorage"" WHERE ""Scheme"" = ?", new object[] { scheme }, values, @"INSERT INTO ""UIStorage"" (""Scheme"", ""Key"", ""Value"") VALUES (?, ?, ?)", - (f) => { + (f) => + { return new object[] { scheme, f.Key ?? "", f.Value ?? "" }; } - ); - + ); + if (tr != null) tr.Commit(); } @@ -922,19 +937,20 @@ namespace Duplicati.Server.Database public TempFile[] GetTempFiles() { - lock(m_lock) + lock (m_lock) return ReadFromDb(null).ToArray(); } public void DeleteTempFile(long id) { - lock(m_lock) + lock (m_lock) DeleteFromDb(typeof(TempFile).Name, id); } public long RegisterTempFile(string origin, string path, DateTime expires) { - var tempfile = new TempFile() { + var tempfile = new TempFile() + { Timestamp = DateTime.Now, Origin = origin, Path = path, @@ -950,8 +966,8 @@ namespace Duplicati.Server.Database { var t = Library.Utility.Utility.NormalizeDateTimeToEpochSeconds(purgeDate); - using(var tr = m_connection.BeginTransaction()) - using(var cmd = m_connection.CreateCommand()) + using (var tr = m_connection.BeginTransaction()) + using (var cmd = m_connection.CreateCommand()) { cmd.Transaction = tr; cmd.CommandText = @"DELETE FROM ""ErrorLog"" WHERE ""Timestamp"" < ?"; @@ -962,7 +978,7 @@ namespace Duplicati.Server.Database tr.Commit(); } } - + private static DateTime ConvertToDateTime(System.Data.IDataReader rd, int index) { var unixTime = ConvertToInt64(rd, index); @@ -973,7 +989,7 @@ namespace Duplicati.Server.Database { return ConvertToInt64(rd, index) == 1; } - + private static string ConvertToString(System.Data.IDataReader rd, int index) { var r = rd.GetValue(index); @@ -996,13 +1012,13 @@ namespace Duplicati.Server.Database private static long ExecuteScalarInt64(System.Data.IDbCommand cmd, long defaultValue = -1) { - using(var rd = cmd.ExecuteReader()) + using (var rd = cmd.ExecuteReader()) return rd.Read() ? ConvertToInt64(rd, 0) : defaultValue; } private static string ExecuteScalarString(System.Data.IDbCommand cmd) { - using(var rd = cmd.ExecuteReader()) + using (var rd = cmd.ExecuteReader()) return rd.Read() ? ConvertToString(rd, 0) : null; } @@ -1029,9 +1045,9 @@ namespace Duplicati.Server.Database // New function that allows to delete rows from tables with arbitrary identifier values (e.g. ID or BackupID) private bool DeleteFromDb(string tablename, long id, string identifier, System.Data.IDbTransaction transaction = null) { - if (transaction == null) + if (transaction == null) { - using(var tr = m_connection.BeginTransaction()) + using (var tr = m_connection.BeginTransaction()) { var r = DeleteFromDb(tablename, id, tr); tr.Commit(); @@ -1040,14 +1056,14 @@ namespace Duplicati.Server.Database } else { - using(var cmd = m_connection.CreateCommand()) + using (var cmd = m_connection.CreateCommand()) { cmd.Transaction = transaction; cmd.CommandText = string.Format(@"DELETE FROM ""{0}"" WHERE ""{1}""=?", tablename, identifier); var p = cmd.CreateParameter(); p.Value = id; cmd.Parameters.Add(p); - + var r = cmd.ExecuteNonQuery(); // Roll back the transaction if more than 1 ID was deleted. Multiple "BackupID" rows being deleted isn't a problem. if (identifier == "ID" && r > 1) @@ -1056,24 +1072,24 @@ namespace Duplicati.Server.Database } } } - + private static IEnumerable Read(System.Data.IDbCommand cmd, Func f) { - using(var rd = cmd.ExecuteReader()) - while(rd.Read()) + using (var rd = cmd.ExecuteReader()) + while (rd.Read()) yield return f(rd); } - + private static IEnumerable Read(System.Data.IDataReader rd, Func f) { - while(rd.Read()) + while (rd.Read()) yield return f(); } private System.Reflection.PropertyInfo[] GetORMFields() { - var flags = - System.Reflection.BindingFlags.FlattenHierarchy | + var flags = + System.Reflection.BindingFlags.FlattenHierarchy | System.Reflection.BindingFlags.Instance | System.Reflection.BindingFlags.Public; @@ -1084,10 +1100,10 @@ namespace Duplicati.Server.Database typeof(DateTime) }; - return + return (from n in typeof(T).GetProperties(flags) - where supportedPropertyTypes.Contains(n.PropertyType) || n.PropertyType.IsEnum - select n).ToArray(); + where supportedPropertyTypes.Contains(n.PropertyType) || n.PropertyType.IsEnum + select n).ToArray(); } private IEnumerable ReadFromDb(string whereclause, params object[] args) @@ -1102,26 +1118,27 @@ namespace Duplicati.Server.Database whereclause ?? "" ); - return ReadFromDb((rd) => { - var item = Activator.CreateInstance(); - for(var i = 0; i < properties.Length; i++) - { - var prop = properties[i]; + return ReadFromDb((rd) => + { + var item = Activator.CreateInstance(); + for (var i = 0; i < properties.Length; i++) + { + var prop = properties[i]; - if (prop.PropertyType.IsEnum) - prop.SetValue(item, ConvertToEnum(prop.PropertyType, rd, i, Enum.GetValues(prop.PropertyType).GetValue(0)), null); - else if (prop.PropertyType == typeof(string)) - prop.SetValue(item, ConvertToString(rd, i), null); - else if (prop.PropertyType == typeof(long)) - prop.SetValue(item, ConvertToInt64(rd, i), null); - else if (prop.PropertyType == typeof(bool)) - prop.SetValue(item, ConvertToBoolean(rd, i), null); - else if (prop.PropertyType == typeof(DateTime)) - prop.SetValue(item, ConvertToDateTime(rd, i), null); - } + if (prop.PropertyType.IsEnum) + prop.SetValue(item, ConvertToEnum(prop.PropertyType, rd, i, Enum.GetValues(prop.PropertyType).GetValue(0)), null); + else if (prop.PropertyType == typeof(string)) + prop.SetValue(item, ConvertToString(rd, i), null); + else if (prop.PropertyType == typeof(long)) + prop.SetValue(item, ConvertToInt64(rd, i), null); + else if (prop.PropertyType == typeof(bool)) + prop.SetValue(item, ConvertToBoolean(rd, i), null); + else if (prop.PropertyType == typeof(DateTime)) + prop.SetValue(item, ConvertToDateTime(rd, i), null); + } - return item; - }, sql, args); + return item; + }, sql, args); } private void OverwriteAndUpdateDb(System.Data.IDbTransaction transaction, string deleteSql, object[] deleteArgs, IEnumerable values, bool updateExisting) @@ -1144,7 +1161,7 @@ namespace Duplicati.Server.Database } else { - + sql = string.Format( @"INSERT INTO ""{0}"" (""{1}"") VALUES ({2})", typeof(T).Name, @@ -1164,12 +1181,12 @@ namespace Duplicati.Server.Database else if (x.PropertyType == typeof(DateTime)) val = Library.Utility.Utility.NormalizeDateTimeToEpochSeconds((DateTime)val); - return val; + return val; }).ToArray(); }); if (!updateExisting && values.Count() == 1 && idfield != null) - using(var cmd = m_connection.CreateCommand()) + using (var cmd = m_connection.CreateCommand()) { cmd.Transaction = transaction; cmd.CommandText = @"SELECT last_insert_rowid();"; @@ -1179,64 +1196,64 @@ namespace Duplicati.Server.Database idfield.SetValue(values.First(), ExecuteScalarInt64(cmd), null); } } - + private IEnumerable ReadFromDb(Func f, string sql, params object[] args) { - using(var cmd = m_connection.CreateCommand()) + using (var cmd = m_connection.CreateCommand()) { cmd.CommandText = sql; if (args != null) - foreach(var a in args) + foreach (var a in args) { var p = cmd.CreateParameter(); p.Value = a; cmd.Parameters.Add(p); } - + return Read(cmd, f).ToArray(); } } - + private void OverwriteAndUpdateDb(System.Data.IDbTransaction transaction, string deleteSql, object[] deleteArgs, IEnumerable values, string insertSql, Func f) { - using(var cmd = m_connection.CreateCommand()) + using (var cmd = m_connection.CreateCommand()) { cmd.Transaction = transaction; - + if (!string.IsNullOrEmpty(deleteSql)) { cmd.CommandText = deleteSql; if (deleteArgs != null) - foreach(var a in deleteArgs) + foreach (var a in deleteArgs) { var p = cmd.CreateParameter(); p.Value = a; cmd.Parameters.Add(p); } - + cmd.ExecuteNonQuery(); cmd.Parameters.Clear(); } - + cmd.CommandText = insertSql; - - foreach(var n in values) + + foreach (var n in values) { var r = f(n); if (r == null) continue; - + while (cmd.Parameters.Count < r.Length) cmd.Parameters.Add(cmd.CreateParameter()); - - for(var i = 0; i < r.Length; i++) + + for (var i = 0; i < r.Length; i++) ((System.Data.IDbDataParameter)cmd.Parameters[i]).Value = r[i]; - + cmd.ExecuteNonQuery(); } } } - + #region IDisposable implementation public void Dispose() { @@ -1257,6 +1274,6 @@ namespace Duplicati.Server.Database } #endregion } - + } diff --git a/Duplicati.Library.RestAPI/Database/Filter.cs b/Duplicati.Library.RestAPI/Database/Filter.cs index 7f14e2bda..904b2bf4c 100644 --- a/Duplicati.Library.RestAPI/Database/Filter.cs +++ b/Duplicati.Library.RestAPI/Database/Filter.cs @@ -1,20 +1,24 @@ -// Copyright (C) 2015, The Duplicati Team +// 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. -// http://www.duplicati.com, info@duplicati.com -// -// This library is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as -// published by the Free Software Foundation; either version 2.1 of the -// License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA using System; namespace Duplicati.Server.Database diff --git a/Duplicati.Library.RestAPI/Database/Notification.cs b/Duplicati.Library.RestAPI/Database/Notification.cs index 22b507701..a14d4f6c4 100644 --- a/Duplicati.Library.RestAPI/Database/Notification.cs +++ b/Duplicati.Library.RestAPI/Database/Notification.cs @@ -1,20 +1,24 @@ -// Copyright (C) 2015, The Duplicati Team +// 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. -// http://www.duplicati.com, info@duplicati.com -// -// This library is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as -// published by the Free Software Foundation; either version 2.1 of the -// License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA using System; namespace Duplicati.Server.Database @@ -29,7 +33,7 @@ namespace Duplicati.Server.Database public string Exception { get; set; } public string BackupID { get; set; } public string Action { get; set; } - public DateTime Timestamp { get; set; } + public DateTime Timestamp { get; set; } public string LogEntryID { get; set; } public string MessageID { get; set; } public string MessageLogTag { get; set; } diff --git a/Duplicati.Library.RestAPI/Database/Schedule.cs b/Duplicati.Library.RestAPI/Database/Schedule.cs index 7df817d40..5a9d28202 100644 --- a/Duplicati.Library.RestAPI/Database/Schedule.cs +++ b/Duplicati.Library.RestAPI/Database/Schedule.cs @@ -1,20 +1,24 @@ -// Copyright (C) 2015, The Duplicati Team +// 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. -// http://www.duplicati.com, info@duplicati.com -// -// This library is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as -// published by the Free Software Foundation; either version 2.1 of the -// License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA using System; using System.Linq; using Duplicati.Server.Serialization.Interface; @@ -29,38 +33,38 @@ namespace Duplicati.Server.Database public string Repeat { get; set; } public DateTime LastRun { get; set; } public string Rule { get; set; } - + public DayOfWeek[] AllowedDays - { + { get { if (string.IsNullOrEmpty(this.Rule)) return null; - - var days = (from n in this.Rule.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries) - where n.StartsWith("AllowedWeekDays=", StringComparison.OrdinalIgnoreCase) - select n.Substring("AllowedWeekDays=".Length).Split(new char[] {','}, StringSplitOptions.RemoveEmptyEntries)) + + var days = (from n in this.Rule.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries) + where n.StartsWith("AllowedWeekDays=", StringComparison.OrdinalIgnoreCase) + select n.Substring("AllowedWeekDays=".Length).Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) .FirstOrDefault(); - - + + if (days == null) return null; - return (from n in days + return (from n in days where Enum.TryParse(n, true, out _) select (DayOfWeek)Enum.Parse(typeof(DayOfWeek), n, true)) .ToArray(); } set { - - var parts = - string.IsNullOrEmpty(this.Rule) ? + + var parts = + string.IsNullOrEmpty(this.Rule) ? new string[0] : - (from n in this.Rule.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries) - where !n.StartsWith("AllowedWeekDays=", StringComparison.OrdinalIgnoreCase) - select n); - + (from n in this.Rule.Split(new string[] { ";" }, StringSplitOptions.RemoveEmptyEntries) + where !n.StartsWith("AllowedWeekDays=", StringComparison.OrdinalIgnoreCase) + select n); + if (value != null && value.Length != 0) parts = parts.Union(new string[] { "AllowedWeekDays=" + @@ -70,7 +74,7 @@ namespace Duplicati.Server.Database select Enum.GetName(typeof(DayOfWeek), n)).Distinct() ) }).Distinct(); - + this.Rule = string.Join(";", parts); } } diff --git a/Duplicati.Library.RestAPI/Database/ServerSettings.cs b/Duplicati.Library.RestAPI/Database/ServerSettings.cs index 9e446b731..57a0988dc 100644 --- a/Duplicati.Library.RestAPI/Database/ServerSettings.cs +++ b/Duplicati.Library.RestAPI/Database/ServerSettings.cs @@ -1,20 +1,24 @@ -// Copyright (C) 2015, The Duplicati Team +// 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. -// http://www.duplicati.com, info@duplicati.com -// -// This library is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as -// published by the Free Software Foundation; either version 2.1 of the -// License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA using System; using System.Linq; using System.Collections.Generic; @@ -50,10 +54,10 @@ namespace Duplicati.Server.Database public const string HAS_FIXED_INVALID_BACKUPID = "has-fixed-invalid-backup-id"; public const string UPDATE_CHANNEL = "update-channel"; public const string USAGE_REPORTER_LEVEL = "usage-reporter-level"; - public const string HAS_ASKED_FOR_PASSWORD_PROTECTION = "has-asked-for-password-protection"; + public const string HAS_ASKED_FOR_PASSWORD_PROTECTION = "has-asked-for-password-protection"; public const string DISABLE_TRAY_ICON_LOGIN = "disable-tray-icon-login"; public const string SERVER_ALLOWED_HOSTNAMES = "allowed-hostnames"; - } + } private readonly Dictionary settings; private readonly Connection databaseConnection; @@ -68,12 +72,12 @@ namespace Duplicati.Server.Database public void ReloadSettings() { - lock(databaseConnection.m_lock) + lock (databaseConnection.m_lock) { settings.Clear(); - foreach(var n in typeof(CONST).GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.DeclaredOnly | System.Reflection.BindingFlags.Static).Select(x => (string)x.GetValue(null))) + foreach (var n in typeof(CONST).GetFields(System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.DeclaredOnly | System.Reflection.BindingFlags.Static).Select(x => (string)x.GetValue(null))) settings[n] = null; - foreach(var n in databaseConnection.GetSettings(Connection.SERVER_SETTINGS_ID)) + foreach (var n in databaseConnection.GetSettings(Connection.SERVER_SETTINGS_ID)) settings[n.Name] = n.Value; } } @@ -83,13 +87,13 @@ namespace Duplicati.Server.Database if (newsettings == null) throw new ArgumentNullException(); - lock(databaseConnection.m_lock) + lock (databaseConnection.m_lock) { m_latestUpdate = null; if (clearExisting) settings.Clear(); - foreach(var k in newsettings) + foreach (var k in newsettings) if (!clearExisting && newsettings[k.Key] == null && k.Key.StartsWith("--", StringComparison.Ordinal)) settings.Remove(k.Key); else @@ -98,44 +102,45 @@ namespace Duplicati.Server.Database } SaveSettings(); - + if (newsettings.Keys.Contains(CONST.SERVER_PASSPHRASE)) GenerateWebserverPasswordTrayIcon(); } - + private void SaveSettings() { databaseConnection.SetSettings( from n in settings - select (Duplicati.Server.Serialization.Interface.ISetting)new Setting() { + select (Duplicati.Server.Serialization.Interface.ISetting)new Setting() + { Filter = "", Name = n.Key, Value = n.Value - }, Database.Connection.SERVER_SETTINGS_ID); + }, Database.Connection.SERVER_SETTINGS_ID); FIXMEGlobal.IncrementLastDataUpdateID(); FIXMEGlobal.StatusEventNotifyer.SignalNewEvent(); - // In case the usage reporter is enabled or disabled, refresh now - FIXMEGlobal.StartOrStopUsageReporter(); + // In case the usage reporter is enabled or disabled, refresh now + FIXMEGlobal.StartOrStopUsageReporter(); // If throttle options were changed, update now FIXMEGlobal.UpdateThrottleSpeeds(); } - + public string StartupDelayDuration { - get + get { return settings[CONST.STARTUP_DELAY]; } set { - lock(databaseConnection.m_lock) + lock (databaseConnection.m_lock) settings[CONST.STARTUP_DELAY] = value; SaveSettings(); } } - + public System.Threading.ThreadPriority? ThreadPriorityOverride { get @@ -143,43 +148,43 @@ namespace Duplicati.Server.Database var tp = settings[CONST.THREAD_PRIORITY]; if (string.IsNullOrEmpty(tp)) return null; - - System.Threading.ThreadPriority r; + + System.Threading.ThreadPriority r; if (Enum.TryParse(tp, true, out r)) return r; - + return null; } set { - lock(databaseConnection.m_lock) + lock (databaseConnection.m_lock) settings[CONST.THREAD_PRIORITY] = value.HasValue ? Enum.GetName(typeof(System.Threading.ThreadPriority), value.Value) : null; } } - + public string DownloadSpeedLimit { - get + get { return settings[CONST.DOWNLOAD_SPEED_LIMIT]; } set { - lock(databaseConnection.m_lock) + lock (databaseConnection.m_lock) settings[CONST.DOWNLOAD_SPEED_LIMIT] = value; SaveSettings(); } } - + public string UploadSpeedLimit { - get + get { return settings[CONST.UPLOAD_SPEED_LIMIT]; } set { - lock(databaseConnection.m_lock) + lock (databaseConnection.m_lock) settings[CONST.UPLOAD_SPEED_LIMIT] = value; SaveSettings(); } @@ -193,7 +198,7 @@ namespace Duplicati.Server.Database } set { - lock(databaseConnection.m_lock) + lock (databaseConnection.m_lock) settings[CONST.IS_FIRST_RUN] = value.ToString(); SaveSettings(); } @@ -221,7 +226,7 @@ namespace Duplicati.Server.Database } set { - lock(databaseConnection.m_lock) + lock (databaseConnection.m_lock) settings[CONST.UNACKED_ERROR] = value.ToString(); SaveSettings(); } @@ -235,7 +240,7 @@ namespace Duplicati.Server.Database } set { - lock(databaseConnection.m_lock) + lock (databaseConnection.m_lock) settings[CONST.UNACKED_WARNING] = value.ToString(); SaveSettings(); } @@ -249,7 +254,7 @@ namespace Duplicati.Server.Database } set { - lock(databaseConnection.m_lock) + lock (databaseConnection.m_lock) settings[CONST.SERVER_PORT_CHANGED] = value.ToString(); SaveSettings(); } @@ -282,7 +287,7 @@ namespace Duplicati.Server.Database } set { - lock(databaseConnection.m_lock) + lock (databaseConnection.m_lock) settings[CONST.LAST_WEBSERVER_PORT] = value.ToString(); SaveSettings(); } @@ -290,7 +295,7 @@ namespace Duplicati.Server.Database public string WebserverPassword { - get + get { return settings[CONST.SERVER_PASSPHRASE]; } @@ -298,7 +303,7 @@ namespace Duplicati.Server.Database public string WebserverPasswordSalt { - get + get { return settings[CONST.SERVER_PASSPHRASE_SALT]; } @@ -308,7 +313,7 @@ namespace Duplicati.Server.Database { if (string.IsNullOrWhiteSpace(password)) { - lock(databaseConnection.m_lock) + lock (databaseConnection.m_lock) { settings[CONST.SERVER_PASSPHRASE] = ""; settings[CONST.SERVER_PASSPHRASE_SALT] = ""; @@ -328,7 +333,7 @@ namespace Duplicati.Server.Database sha256.TransformFinalBlock(buf, 0, buf.Length); var pwd = Convert.ToBase64String(sha256.Hash); - lock(databaseConnection.m_lock) + lock (databaseConnection.m_lock) { settings[CONST.SERVER_PASSPHRASE] = pwd; settings[CONST.SERVER_PASSPHRASE_SALT] = salt; @@ -369,7 +374,7 @@ namespace Duplicati.Server.Database sha256.TransformFinalBlock(buf, 0, buf.Length); pwd = Convert.ToBase64String(sha256.Hash); } - + lock (databaseConnection.m_lock) { settings[CONST.SERVER_PASSPHRASETRAYICON] = password; @@ -381,7 +386,7 @@ namespace Duplicati.Server.Database public DateTime LastUpdateCheck { - get + get { long t; if (long.TryParse(settings[CONST.UPDATE_CHECK_LAST], out t)) @@ -391,7 +396,7 @@ namespace Duplicati.Server.Database } set { - lock(databaseConnection.m_lock) + lock (databaseConnection.m_lock) settings[CONST.UPDATE_CHECK_LAST] = value.ToUniversalTime().Ticks.ToString(); SaveSettings(); } @@ -409,7 +414,7 @@ namespace Duplicati.Server.Database } set { - lock(databaseConnection.m_lock) + lock (databaseConnection.m_lock) settings[CONST.UPDATE_CHECK_INTERVAL] = value; SaveSettings(); FIXMEGlobal.UpdatePoller.Reschedule(); @@ -443,7 +448,7 @@ namespace Duplicati.Server.Database if (m_latestUpdate != null) return m_latestUpdate; - using(var tr = new System.IO.StringReader(settings[CONST.UPDATE_CHECK_NEW_VERSION])) + using (var tr = new System.IO.StringReader(settings[CONST.UPDATE_CHECK_NEW_VERSION])) return m_latestUpdate = Server.Serialization.Serializer.Deserialize(tr); } catch @@ -458,14 +463,14 @@ namespace Duplicati.Server.Database if (value != null) { var sb = new System.Text.StringBuilder(); - using(var tw = new System.IO.StringWriter(sb)) + using (var tw = new System.IO.StringWriter(sb)) Server.Serialization.Serializer.SerializeJson(tw, value); result = sb.ToString(); } m_latestUpdate = value; - lock(databaseConnection.m_lock) + lock (databaseConnection.m_lock) settings[CONST.UPDATE_CHECK_NEW_VERSION] = result; SaveSettings(); @@ -474,13 +479,13 @@ namespace Duplicati.Server.Database public string ServerListenInterface { - get + get { return settings[CONST.SERVER_LISTEN_INTERFACE]; } set { - lock(databaseConnection.m_lock) + lock (databaseConnection.m_lock) settings[CONST.SERVER_LISTEN_INTERFACE] = value; SaveSettings(); } @@ -526,7 +531,7 @@ namespace Duplicati.Server.Database } set { - lock(databaseConnection.m_lock) + lock (databaseConnection.m_lock) settings[CONST.HAS_FIXED_INVALID_BACKUPID] = value.ToString(); SaveSettings(); } @@ -534,13 +539,13 @@ namespace Duplicati.Server.Database public string UpdateChannel { - get + get { return settings[CONST.UPDATE_CHANNEL]; } set { - lock(databaseConnection.m_lock) + lock (databaseConnection.m_lock) settings[CONST.UPDATE_CHANNEL] = value; SaveSettings(); } @@ -548,13 +553,13 @@ namespace Duplicati.Server.Database public string UsageReporterLevel { - get + get { return settings[CONST.USAGE_REPORTER_LEVEL]; } set { - lock(databaseConnection.m_lock) + lock (databaseConnection.m_lock) settings[CONST.USAGE_REPORTER_LEVEL] = value; SaveSettings(); } diff --git a/Duplicati.Library.RestAPI/Database/Setting.cs b/Duplicati.Library.RestAPI/Database/Setting.cs index 84150bf1a..3c5cff529 100644 --- a/Duplicati.Library.RestAPI/Database/Setting.cs +++ b/Duplicati.Library.RestAPI/Database/Setting.cs @@ -1,20 +1,24 @@ -// Copyright (C) 2015, The Duplicati Team +// 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. -// http://www.duplicati.com, info@duplicati.com -// -// This library is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as -// published by the Free Software Foundation; either version 2.1 of the -// License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA using System; namespace Duplicati.Server.Database diff --git a/Duplicati.Library.RestAPI/Database/TempFile.cs b/Duplicati.Library.RestAPI/Database/TempFile.cs index b652a2920..284403531 100644 --- a/Duplicati.Library.RestAPI/Database/TempFile.cs +++ b/Duplicati.Library.RestAPI/Database/TempFile.cs @@ -1,20 +1,24 @@ -// Copyright (C) 2015, The Duplicati Team +// 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. -// http://www.duplicati.com, info@duplicati.com -// -// This library is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as -// published by the Free Software Foundation; either version 2.1 of the -// License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA using System; namespace Duplicati.Server.Database diff --git a/Duplicati.Library.RestAPI/LiveControls.cs b/Duplicati.Library.RestAPI/LiveControls.cs index 2171fb754..dd95f1af5 100644 --- a/Duplicati.Library.RestAPI/LiveControls.cs +++ b/Duplicati.Library.RestAPI/LiveControls.cs @@ -1,22 +1,24 @@ -#region Disclaimer / License -// Copyright (C) 2015, The Duplicati Team -// http://www.duplicati.com, info@duplicati.com +// Copyright (C) 2024, The Duplicati Team +// https://duplicati.com, hello@duplicati.com // -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. +// 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: // -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. // -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA -// -#endregion +// 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.Runtime.Versioning; @@ -110,8 +112,8 @@ namespace Duplicati.Server /// /// Gets the current overridden thread priority /// - public System.Threading.ThreadPriority? ThreadPriority - { + public System.Threading.ThreadPriority? ThreadPriority + { get { return m_priority; } set { @@ -127,8 +129,8 @@ namespace Duplicati.Server /// /// Gets the current upload limit in bps /// - public long? UploadLimit - { + public long? UploadLimit + { get { return m_uploadLimit; } set { @@ -144,8 +146,8 @@ namespace Duplicati.Server /// /// Gets the download limit in bps /// - public long? DownloadLimit - { + public long? DownloadLimit + { get { return m_downloadLimit; } set { @@ -180,7 +182,7 @@ namespace Duplicati.Server { long milliseconds = 0; try { milliseconds = (long)Duplicati.Library.Utility.Timeparser.ParseTimeSpan(settings.StartupDelayDuration).TotalMilliseconds; } - catch {} + catch { } if (milliseconds > 0) { @@ -223,7 +225,7 @@ namespace Duplicati.Server /// Event that occurs when the timeout duration is exceeded /// /// The sender of the event - private void m_waitTimer_Tick(object sender) + private void m_waitTimer_Tick(object sender) { lock (m_lock) Resume(); @@ -270,7 +272,7 @@ namespace Duplicati.Server /// public void Pause() { - lock(m_lock) + lock (m_lock) { var fireEvent = m_waitTimeExpiration.Ticks != 0 && m_state == LiveControlState.Paused && StateChanged != null; diff --git a/Duplicati.Library.RestAPI/LogWriteHandler.cs b/Duplicati.Library.RestAPI/LogWriteHandler.cs index 640886f66..1d8259e19 100644 --- a/Duplicati.Library.RestAPI/LogWriteHandler.cs +++ b/Duplicati.Library.RestAPI/LogWriteHandler.cs @@ -1,20 +1,24 @@ -// Copyright (C) 2015, The Duplicati Team +// 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. -// http://www.duplicati.com, info@duplicati.com -// -// This library is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as -// published by the Free Software Foundation; either version 2.1 of the -// License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA using System; using System.Linq; using Duplicati.Library.Logging; @@ -130,7 +134,7 @@ namespace Duplicati.Server this.ExceptionID = exception.HelpID; else this.ExceptionID = entry.Exception.GetType().FullName; - + } } @@ -150,15 +154,15 @@ namespace Duplicati.Server { m_buffer = new T[size]; if (initial != null) - foreach(var t in initial) + foreach (var t in initial) this.Enqueue(t); } - + public int Length { get { return m_length; } } public void Enqueue(T item) { - lock(m_lock) + lock (m_lock) { m_key++; m_buffer[m_head] = item; @@ -174,7 +178,7 @@ namespace Duplicati.Server public IEnumerator GetEnumerator() { var k = m_key; - for(var i = 0; i < m_length; i++) + for (var i = 0; i < m_length; i++) if (m_key != k) throw new InvalidOperationException("Buffer was modified while reading"); else @@ -190,7 +194,7 @@ namespace Duplicati.Server public T[] FlatArray(Func filter = null) { - lock(m_lock) + lock (m_lock) if (filter == null) return this.ToArray(); else @@ -218,7 +222,7 @@ namespace Duplicati.Server public void RenewTimeout(LogMessageType type) { - lock(m_lock) + lock (m_lock) { m_timeouts[(int)type] = DateTime.Now.AddSeconds(30); m_anytimeouts = true; @@ -245,12 +249,12 @@ namespace Duplicati.Server UpdateLogLevel(); offset = offset.ToUniversalTime(); - lock(m_lock) + lock (m_lock) { if (m_buffer == null) return new LogEntry[0]; - - return m_buffer.FlatArray((x) => x.When > offset && x.Type >= level ); + + return m_buffer.FlatArray((x) => x.When > offset && x.Type >= level); } } @@ -259,18 +263,20 @@ namespace Duplicati.Server RenewTimeout(level); UpdateLogLevel(); - lock(m_lock) + lock (m_lock) { if (m_buffer == null) return new LogEntry[0]; - - var buffer = m_buffer.FlatArray((x) => x.ID > id && x.Type >= level ); + + var buffer = m_buffer.FlatArray((x) => x.ID > id && x.Type >= level); // Return the newest entries - if (buffer.Length > pagesize) { + if (buffer.Length > pagesize) + { var index = buffer.Length - pagesize; return buffer.Skip(index).Take(pagesize).ToArray(); } - else { + else + { return buffer; } } @@ -280,13 +286,13 @@ namespace Duplicati.Server { var i = 0; return (from n in m_timeouts - let ix = i++ - where n > DateTime.Now - select ix).ToArray(); + let ix = i++ + where n > DateTime.Now + select ix).ToArray(); } private void UpdateLogLevel() - { + { m_logLevel = (LogMessageType)(GetActiveTimeouts().Union(new int[] { (int)m_serverloglevel }).Min()); } @@ -296,9 +302,9 @@ namespace Duplicati.Server public void WriteMessage(Duplicati.Library.Logging.LogEntry entry) { - if (entry.Level < m_logLevel) + if (entry.Level < m_logLevel) return; - + if (m_serverfile != null && entry.Level >= m_serverloglevel) try { @@ -308,7 +314,7 @@ namespace Duplicati.Server { } - lock(m_lock) + lock (m_lock) { if (m_anytimeouts) { diff --git a/Duplicati.Library.RestAPI/Scheduler.cs b/Duplicati.Library.RestAPI/Scheduler.cs index 81a8cdf41..07e4d9d5c 100644 --- a/Duplicati.Library.RestAPI/Scheduler.cs +++ b/Duplicati.Library.RestAPI/Scheduler.cs @@ -1,25 +1,26 @@ -#region Disclaimer / License -// Copyright (C) 2015, The Duplicati Team -// http://www.duplicati.com, info@duplicati.com +// Copyright (C) 2024, The Duplicati Team +// https://duplicati.com, hello@duplicati.com // -// This library is free software; you can redistribute it and/or -// modify it under the terms of the GNU Lesser General Public -// License as published by the Free Software Foundation; either -// version 2.1 of the License, or (at your option) any later version. +// 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: // -// This library is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +// 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 Duplicati.Server.Serialization.Interface; - -#endregion using System; using System.Collections.Generic; using System.Text; @@ -67,7 +68,7 @@ namespace Duplicati.Server /// The currently scheduled items /// private KeyValuePair[] m_schedule; - + /// /// List of update tasks, used to set the timestamp on the schedule once completed /// @@ -104,13 +105,13 @@ namespace Duplicati.Server /// /// A snapshot copy of the current schedule list /// - public List> Schedule - { - get + public List> Schedule + { + get { lock (m_lock) return m_schedule.ToList(); - } + } } /// @@ -195,26 +196,26 @@ namespace Duplicati.Server throw new Exception(Strings.Scheduler.InvalidTimeSetupError(basetime, repetition, sb.ToString())); } - + return res; } - + private void OnCompleted(WorkerThread worker, Runner.IRunnerData task) { Tuple t = null; - lock(m_lock) + lock (m_lock) { if (task != null && m_updateTasks.TryGetValue(task, out t)) m_updateTasks.Remove(task); } - + if (t != null) { t.Item1.Time = t.Item2; t.Item1.LastRun = t.Item3; FIXMEGlobal.DataConnection.AddOrUpdateSchedule(t.Item1); } - + } private void OnStartingWork(WorkerThread worker, Runner.IRunnerData task) @@ -223,8 +224,8 @@ namespace Duplicati.Server { return; } - - lock(m_lock) + + lock (m_lock) { if (m_updateTasks.TryGetValue(task, out Tuple scheduleInfo)) { @@ -245,10 +246,10 @@ namespace Duplicati.Server { //TODO: As this is executed repeatedly we should cache it // to avoid frequent db lookups - + //Determine schedule list var lst = FIXMEGlobal.DataConnection.Schedules; - foreach(var sc in lst) + foreach (var sc in lst) { if (!string.IsNullOrEmpty(sc.Repeat)) { @@ -267,7 +268,7 @@ namespace Duplicati.Server { start = startkey.Value; } - + try { // Recover from timedrift issues by overriding the dates if the last run date is in the future. @@ -288,15 +289,15 @@ namespace Duplicati.Server { var jobsToRun = new List(); //TODO: Cache this to avoid frequent lookups - foreach(var id in FIXMEGlobal.DataConnection.GetBackupIDsForTags(sc.Tags).Distinct().Select(x => x.ToString())) + foreach (var id in FIXMEGlobal.DataConnection.GetBackupIDsForTags(sc.Tags).Distinct().Select(x => x.ToString())) { //See if it is already queued var tmplst = from n in m_worker.CurrentTasks - where n.Operation == Duplicati.Server.Serialization.DuplicatiOperation.Backup - select n.Backup; + where n.Operation == Duplicati.Server.Serialization.DuplicatiOperation.Backup + select n.Backup; var tastTemp = m_worker.CurrentTask; if (tastTemp != null && tastTemp.Operation == Duplicati.Server.Serialization.DuplicatiOperation.Backup) - tmplst = tmplst.Union(new [] { tastTemp.Backup }); + tmplst = tmplst.Union(new[] { tastTemp.Backup }); //If it is not already in queue, put it there if (!tmplst.Any(x => x.ID == id)) @@ -323,12 +324,12 @@ namespace Duplicati.Server { start = GetNextValidTime(start, new DateTime(Math.Max(DateTime.UtcNow.AddSeconds(1).Ticks, start.AddSeconds(1).Ticks), DateTimeKind.Utc), sc.Repeat, sc.AllowedDays); } - catch(Exception ex) + catch (Exception ex) { FIXMEGlobal.DataConnection.LogError(sc.ID.ToString(), "Scheduler failed to find next date", ex); continue; } - + Server.Runner.IRunnerData lastJob = jobsToRun.LastOrDefault(); if (lastJob != null) { @@ -341,7 +342,7 @@ namespace Duplicati.Server foreach (var job in jobsToRun) m_worker.AddTask(job); - + if (start < DateTime.UtcNow) { //TODO: Report this somehow @@ -349,20 +350,20 @@ namespace Duplicati.Server } } - scheduled[sc.ID] = new KeyValuePair(scticks, start); + scheduled[sc.ID] = new KeyValuePair(scticks, start); } } var existing = lst.ToDictionary(x => x.ID); //Sort them, lock as we assign the m_schedule variable - lock(m_lock) + lock (m_lock) m_schedule = (from n in scheduled - where existing.ContainsKey(n.Key) - orderby n.Value.Value - select new KeyValuePair(n.Value.Value, existing[n.Key])).ToArray(); + where existing.ContainsKey(n.Key) + orderby n.Value.Value + select new KeyValuePair(n.Value.Value, existing[n.Key])).ToArray(); // Remove unused entries - foreach(var c in (from n in scheduled where !existing.ContainsKey(n.Key) select n.Key).ToArray()) + foreach (var c in (from n in scheduled where !existing.ContainsKey(n.Key) select n.Key).ToArray()) scheduled.Remove(c); //Raise event if needed @@ -406,7 +407,7 @@ namespace Duplicati.Server if (allowedDays == null || allowedDays.Length == 0) return true; else - return Array.IndexOf(allowedDays, localTime.DayOfWeek) >= 0; + return Array.IndexOf(allowedDays, localTime.DayOfWeek) >= 0; } } diff --git a/Duplicati.Library.RestAPI/SpecialFolders.cs b/Duplicati.Library.RestAPI/SpecialFolders.cs index 6a83004f1..3bef4eac2 100644 --- a/Duplicati.Library.RestAPI/SpecialFolders.cs +++ b/Duplicati.Library.RestAPI/SpecialFolders.cs @@ -1,20 +1,24 @@ -// Copyright (C) 2015, The Duplicati Team +// 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. -// http://www.duplicati.com, info@duplicati.com -// -// This library is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as -// published by the Free Software Foundation; either version 2.1 of the -// License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA using System; using System.Linq; using System.Collections.Generic; @@ -31,7 +35,7 @@ namespace Duplicati.Server public static string ExpandEnvironmentVariables(string path) { - foreach(var n in Nodes) + foreach (var n in Nodes) if (path.StartsWith(n.id, StringComparison.Ordinal)) path = path.Replace(n.id, n.resolvedpath); return Environment.ExpandEnvironmentVariables(path); @@ -54,21 +58,21 @@ namespace Duplicati.Server }); } - public static string TranslateToPath(string str) + public static string TranslateToPath(string str) { string res; if (PathMap.TryGetValue(str, out res)) return res; - + return null; } - public static string TranslateToDisplayString(string str) + public static string TranslateToDisplayString(string str) { string res; if (DisplayMap.TryGetValue(str, out res)) return res; - + return null; } @@ -109,11 +113,11 @@ namespace Duplicati.Server { } } - + static SpecialFolders() { var lst = new List(); - + if (OperatingSystem.IsWindows()) { TryAdd(lst, Environment.SpecialFolder.MyDocuments, "%MY_DOCUMENTS%", "My Documents"); @@ -181,7 +185,7 @@ namespace Duplicati.Server // Handle duplicates var result = new Dictionary(); - foreach(var x in sources) + foreach (var x in sources) result[x.Key] = x.Value; return result; diff --git a/Duplicati.Library.RestAPI/UpdatePollThread.cs b/Duplicati.Library.RestAPI/UpdatePollThread.cs index fb3890b67..4584223d2 100644 --- a/Duplicati.Library.RestAPI/UpdatePollThread.cs +++ b/Duplicati.Library.RestAPI/UpdatePollThread.cs @@ -1,20 +1,24 @@ -// Copyright (C) 2015, The Duplicati Team +// 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. -// http://www.duplicati.com, info@duplicati.com -// -// This library is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as -// published by the Free Software Foundation; either version 2.1 of the -// License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA using System; using System.Linq; using System.Threading; diff --git a/Duplicati/WebserverCore/RestHandlerCore.cs b/Duplicati/WebserverCore/RestHandlerCore.cs index 9d1c41a4e..2543f4363 100644 --- a/Duplicati/WebserverCore/RestHandlerCore.cs +++ b/Duplicati/WebserverCore/RestHandlerCore.cs @@ -1,19 +1,23 @@ -// Copyright (C) 2023, The Duplicati Team -// http://www.duplicati.com, info@duplicati.com -// -// This library is free software; you can redistribute it and/or modify -// it under the terms of the GNU Lesser General Public License as -// published by the Free Software Foundation; either version 2.1 of the -// License, or (at your option) any later version. -// -// This library is distributed in the hope that it will be useful, but -// WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU -// Lesser General Public License for more details. -// -// You should have received a copy of the GNU Lesser General Public -// License along with this library; if not, write to the Free Software -// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +// 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 Amazon.S3; using Duplicati.Library.RestAPI; diff --git a/thirdparty/Signer/README-Setup.txt b/thirdparty/Signer/README-Setup.txt deleted file mode 100644 index d7884d268..000000000 --- a/thirdparty/Signer/README-Setup.txt +++ /dev/null @@ -1,17 +0,0 @@ -To set up Signer on a clean Windows box, install .Net 4.0 Framework: -Microsoft .NET Framework 4 (Web Installer) -http://www.microsoft.com/en-us/download/details.aspx?id=17851 - -Then install 4.0 SDK: -Microsoft Windows SDK for Windows 7 and .NET Framework 4 -http://www.microsoft.com/en-us/download/details.aspx?id=8279 - -Then install VC redist: -Microsoft Visual C++ 2008 Service Pack 1 Redistributable Package MFC Security Update -http://www.microsoft.com/en-us/download/details.aspx?id=26368 - -Then set paths: - -set PATH=%PATH%;C:\Windows\Microsoft.NET\Framework\v4.0.30319;C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin - - diff --git a/thirdparty/Signer/Signer.exe b/thirdparty/Signer/Signer.exe deleted file mode 100755 index eae02bc3992da284a10fdb50ff688575096caf5e..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 27136 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