From 4444ee01b3bf43ea3b1747b01e2561e0b5461e05 Mon Sep 17 00:00:00 2001 From: verhoek <30193551+verhoek@users.noreply.github.com> Date: Mon, 4 Feb 2019 23:32:53 +0100 Subject: [PATCH 01/17] Null propagation. --- Duplicati/Server/WebServer/RESTMethods/Backup.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Duplicati/Server/WebServer/RESTMethods/Backup.cs b/Duplicati/Server/WebServer/RESTMethods/Backup.cs index 2a0ce491e..f72fc048e 100644 --- a/Duplicati/Server/WebServer/RESTMethods/Backup.cs +++ b/Duplicati/Server/WebServer/RESTMethods/Backup.cs @@ -311,7 +311,7 @@ namespace Duplicati.Server.WebServer.RESTMethods // Already running } else if (Program.WorkThread.CurrentTasks.Any(x => { - var bn = x == null ? null : x.Backup; + var bn = x?.Backup; return bn == null || bn.ID == backup.ID; })) { From 1523b0a142175c20767f71d2541f3deb09ebc48e Mon Sep 17 00:00:00 2001 From: verhoek <30193551+verhoek@users.noreply.github.com> Date: Wed, 6 Feb 2019 21:03:06 +0100 Subject: [PATCH 02/17] Minor changes in server settings class. --- Duplicati/Server/Database/ServerSettings.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Duplicati/Server/Database/ServerSettings.cs b/Duplicati/Server/Database/ServerSettings.cs index c808eac18..96d70c2ee 100644 --- a/Duplicati/Server/Database/ServerSettings.cs +++ b/Duplicati/Server/Database/ServerSettings.cs @@ -29,7 +29,7 @@ namespace Duplicati.Server.Database { public class ServerSettings { - private class CONST + private static class CONST { public const string STARTUP_DELAY = "startup-delay"; public const string DOWNLOAD_SPEED_LIMIT = "max-download-speed"; @@ -58,7 +58,7 @@ namespace Duplicati.Server.Database } private readonly Dictionary m_values; - private readonly Database.Connection m_connection; + private readonly Connection m_connection; private Library.AutoUpdater.UpdateInfo m_latestUpdate; internal ServerSettings(Connection con) @@ -318,7 +318,7 @@ namespace Duplicati.Server.Database } else { - var prng = System.Security.Cryptography.RNGCryptoServiceProvider.Create(); + var prng = RandomNumberGenerator.Create(); var buf = new byte[32]; prng.GetBytes(buf); var salt = Convert.ToBase64String(buf); From 289f14a5ffa66535d88f0481ceed0767dadb3db8 Mon Sep 17 00:00:00 2001 From: verhoek <30193551+verhoek@users.noreply.github.com> Date: Wed, 6 Feb 2019 21:34:41 +0100 Subject: [PATCH 03/17] renaming of instance members. --- Duplicati/Server/Database/ServerSettings.cs | 194 ++++++++++---------- 1 file changed, 97 insertions(+), 97 deletions(-) diff --git a/Duplicati/Server/Database/ServerSettings.cs b/Duplicati/Server/Database/ServerSettings.cs index 96d70c2ee..342a86199 100644 --- a/Duplicati/Server/Database/ServerSettings.cs +++ b/Duplicati/Server/Database/ServerSettings.cs @@ -57,26 +57,26 @@ namespace Duplicati.Server.Database public const string SERVER_ALLOWED_HOSTNAMES = "allowed-hostnames"; } - private readonly Dictionary m_values; - private readonly Connection m_connection; + private readonly Dictionary settings; + private readonly Connection databaseConnection; private Library.AutoUpdater.UpdateInfo m_latestUpdate; internal ServerSettings(Connection con) { - m_values = new Dictionary(); - m_connection = con; + settings = new Dictionary(); + databaseConnection = con; ReloadSettings(); } public void ReloadSettings() { - lock(m_connection.m_lock) + lock(databaseConnection.m_lock) { - m_values.Clear(); + 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))) - m_values[n] = null; - foreach(var n in m_connection.GetSettings(Connection.SERVER_SETTINGS_ID)) - m_values[n.Name] = n.Value; + settings[n] = null; + foreach(var n in databaseConnection.GetSettings(Connection.SERVER_SETTINGS_ID)) + settings[n.Name] = n.Value; } } @@ -85,17 +85,17 @@ namespace Duplicati.Server.Database if (newsettings == null) throw new ArgumentNullException(); - lock(m_connection.m_lock) + lock(databaseConnection.m_lock) { m_latestUpdate = null; if (clearExisting) - m_values.Clear(); + settings.Clear(); foreach(var k in newsettings) if (!clearExisting && newsettings[k.Key] == null && k.Key.StartsWith("--", StringComparison.Ordinal)) - m_values.Remove(k.Key); + settings.Remove(k.Key); else - m_values[k.Key] = newsettings[k.Key]; + settings[k.Key] = newsettings[k.Key]; } @@ -107,8 +107,8 @@ namespace Duplicati.Server.Database private void SaveSettings() { - m_connection.SetSettings( - from n in m_values + databaseConnection.SetSettings( + from n in settings select (Duplicati.Server.Serialization.Interface.ISetting)new Setting() { Filter = "", Name = n.Key, @@ -128,12 +128,12 @@ namespace Duplicati.Server.Database { get { - return m_values[CONST.STARTUP_DELAY]; + return settings[CONST.STARTUP_DELAY]; } set { - lock(m_connection.m_lock) - m_values[CONST.STARTUP_DELAY] = value; + lock(databaseConnection.m_lock) + settings[CONST.STARTUP_DELAY] = value; SaveSettings(); } } @@ -142,7 +142,7 @@ namespace Duplicati.Server.Database { get { - var tp = m_values[CONST.THREAD_PRIORITY]; + var tp = settings[CONST.THREAD_PRIORITY]; if (string.IsNullOrEmpty(tp)) return null; @@ -154,8 +154,8 @@ namespace Duplicati.Server.Database } set { - lock(m_connection.m_lock) - m_values[CONST.THREAD_PRIORITY] = value.HasValue ? Enum.GetName(typeof(System.Threading.ThreadPriority), value.Value) : null; + lock(databaseConnection.m_lock) + settings[CONST.THREAD_PRIORITY] = value.HasValue ? Enum.GetName(typeof(System.Threading.ThreadPriority), value.Value) : null; } } @@ -163,12 +163,12 @@ namespace Duplicati.Server.Database { get { - return m_values[CONST.DOWNLOAD_SPEED_LIMIT]; + return settings[CONST.DOWNLOAD_SPEED_LIMIT]; } set { - lock(m_connection.m_lock) - m_values[CONST.DOWNLOAD_SPEED_LIMIT] = value; + lock(databaseConnection.m_lock) + settings[CONST.DOWNLOAD_SPEED_LIMIT] = value; SaveSettings(); } } @@ -177,12 +177,12 @@ namespace Duplicati.Server.Database { get { - return m_values[CONST.UPLOAD_SPEED_LIMIT]; + return settings[CONST.UPLOAD_SPEED_LIMIT]; } set { - lock(m_connection.m_lock) - m_values[CONST.UPLOAD_SPEED_LIMIT] = value; + lock(databaseConnection.m_lock) + settings[CONST.UPLOAD_SPEED_LIMIT] = value; SaveSettings(); } } @@ -191,12 +191,12 @@ namespace Duplicati.Server.Database { get { - return Duplicati.Library.Utility.Utility.ParseBoolOption(m_values, CONST.IS_FIRST_RUN); + return Duplicati.Library.Utility.Utility.ParseBoolOption(settings, CONST.IS_FIRST_RUN); } set { - lock(m_connection.m_lock) - m_values[CONST.IS_FIRST_RUN] = value.ToString(); + lock(databaseConnection.m_lock) + settings[CONST.IS_FIRST_RUN] = value.ToString(); SaveSettings(); } } @@ -205,12 +205,12 @@ namespace Duplicati.Server.Database { get { - return Duplicati.Library.Utility.Utility.ParseBoolOption(m_values, CONST.HAS_ASKED_FOR_PASSWORD_PROTECTION); + return Duplicati.Library.Utility.Utility.ParseBoolOption(settings, CONST.HAS_ASKED_FOR_PASSWORD_PROTECTION); } set { - lock (m_connection.m_lock) - m_values[CONST.HAS_ASKED_FOR_PASSWORD_PROTECTION] = value.ToString(); + lock (databaseConnection.m_lock) + settings[CONST.HAS_ASKED_FOR_PASSWORD_PROTECTION] = value.ToString(); SaveSettings(); } } @@ -219,12 +219,12 @@ namespace Duplicati.Server.Database { get { - return Duplicati.Library.Utility.Utility.ParseBool(m_values[CONST.UNACKED_ERROR], false); + return Duplicati.Library.Utility.Utility.ParseBool(settings[CONST.UNACKED_ERROR], false); } set { - lock(m_connection.m_lock) - m_values[CONST.UNACKED_ERROR] = value.ToString(); + lock(databaseConnection.m_lock) + settings[CONST.UNACKED_ERROR] = value.ToString(); SaveSettings(); } } @@ -233,12 +233,12 @@ namespace Duplicati.Server.Database { get { - return Duplicati.Library.Utility.Utility.ParseBool(m_values[CONST.UNACKED_WARNING], false); + return Duplicati.Library.Utility.Utility.ParseBool(settings[CONST.UNACKED_WARNING], false); } set { - lock(m_connection.m_lock) - m_values[CONST.UNACKED_WARNING] = value.ToString(); + lock(databaseConnection.m_lock) + settings[CONST.UNACKED_WARNING] = value.ToString(); SaveSettings(); } } @@ -247,12 +247,12 @@ namespace Duplicati.Server.Database { get { - return Duplicati.Library.Utility.Utility.ParseBool(m_values[CONST.SERVER_PORT_CHANGED], false); + return Duplicati.Library.Utility.Utility.ParseBool(settings[CONST.SERVER_PORT_CHANGED], false); } set { - lock(m_connection.m_lock) - m_values[CONST.SERVER_PORT_CHANGED] = value.ToString(); + lock(databaseConnection.m_lock) + settings[CONST.SERVER_PORT_CHANGED] = value.ToString(); SaveSettings(); } } @@ -261,12 +261,12 @@ namespace Duplicati.Server.Database { get { - return Duplicati.Library.Utility.Utility.ParseBool(m_values[CONST.DISABLE_TRAY_ICON_LOGIN], false); + return Duplicati.Library.Utility.Utility.ParseBool(settings[CONST.DISABLE_TRAY_ICON_LOGIN], false); } set { - lock (m_connection.m_lock) - m_values[CONST.DISABLE_TRAY_ICON_LOGIN] = value.ToString(); + lock (databaseConnection.m_lock) + settings[CONST.DISABLE_TRAY_ICON_LOGIN] = value.ToString(); SaveSettings(); } } @@ -275,7 +275,7 @@ namespace Duplicati.Server.Database { get { - var tp = m_values[CONST.LAST_WEBSERVER_PORT]; + var tp = settings[CONST.LAST_WEBSERVER_PORT]; int p; if (string.IsNullOrEmpty(tp) || !int.TryParse(tp, out p)) return -1; @@ -284,8 +284,8 @@ namespace Duplicati.Server.Database } set { - lock(m_connection.m_lock) - m_values[CONST.LAST_WEBSERVER_PORT] = value.ToString(); + lock(databaseConnection.m_lock) + settings[CONST.LAST_WEBSERVER_PORT] = value.ToString(); SaveSettings(); } } @@ -294,7 +294,7 @@ namespace Duplicati.Server.Database { get { - return m_values[CONST.SERVER_PASSPHRASE]; + return settings[CONST.SERVER_PASSPHRASE]; } } @@ -302,7 +302,7 @@ namespace Duplicati.Server.Database { get { - return m_values[CONST.SERVER_PASSPHRASE_SALT]; + return settings[CONST.SERVER_PASSPHRASE_SALT]; } } @@ -310,10 +310,10 @@ namespace Duplicati.Server.Database { if (string.IsNullOrWhiteSpace(password)) { - lock(m_connection.m_lock) + lock(databaseConnection.m_lock) { - m_values[CONST.SERVER_PASSPHRASE] = ""; - m_values[CONST.SERVER_PASSPHRASE_SALT] = ""; + settings[CONST.SERVER_PASSPHRASE] = ""; + settings[CONST.SERVER_PASSPHRASE_SALT] = ""; } } else @@ -330,10 +330,10 @@ namespace Duplicati.Server.Database sha256.TransformFinalBlock(buf, 0, buf.Length); var pwd = Convert.ToBase64String(sha256.Hash); - lock(m_connection.m_lock) + lock(databaseConnection.m_lock) { - m_values[CONST.SERVER_PASSPHRASE] = pwd; - m_values[CONST.SERVER_PASSPHRASE_SALT] = salt; + settings[CONST.SERVER_PASSPHRASE] = pwd; + settings[CONST.SERVER_PASSPHRASE_SALT] = salt; } } @@ -343,27 +343,27 @@ namespace Duplicati.Server.Database public void SetAllowedHostnames(string allowedHostnames) { - lock (m_connection.m_lock) - m_values[CONST.SERVER_ALLOWED_HOSTNAMES] = allowedHostnames; + lock (databaseConnection.m_lock) + settings[CONST.SERVER_ALLOWED_HOSTNAMES] = allowedHostnames; SaveSettings(); } - public string WebserverPasswordTrayIcon => m_values[CONST.SERVER_PASSPHRASETRAYICON]; + public string WebserverPasswordTrayIcon => settings[CONST.SERVER_PASSPHRASETRAYICON]; - public string WebserverPasswordTrayIconHash => m_values[CONST.SERVER_PASSPHRASETRAYICONHASH]; + public string WebserverPasswordTrayIconHash => settings[CONST.SERVER_PASSPHRASETRAYICONHASH]; - public string AllowedHostnames => m_values[CONST.SERVER_ALLOWED_HOSTNAMES]; + public string AllowedHostnames => settings[CONST.SERVER_ALLOWED_HOSTNAMES]; public void GenerateWebserverPasswordTrayIcon() { var password = ""; var pwd = ""; - if (!string.IsNullOrEmpty(m_values[CONST.SERVER_PASSPHRASE])) + if (!string.IsNullOrEmpty(settings[CONST.SERVER_PASSPHRASE])) { password = Guid.NewGuid().ToString(); - var buf = Convert.FromBase64String(m_values[CONST.SERVER_PASSPHRASE_SALT]); + var buf = Convert.FromBase64String(settings[CONST.SERVER_PASSPHRASE_SALT]); var sha256 = System.Security.Cryptography.SHA256.Create(); var str = System.Text.Encoding.UTF8.GetBytes(password); @@ -373,10 +373,10 @@ namespace Duplicati.Server.Database pwd = Convert.ToBase64String(sha256.Hash); } - lock (m_connection.m_lock) + lock (databaseConnection.m_lock) { - m_values[CONST.SERVER_PASSPHRASETRAYICON] = password; - m_values[CONST.SERVER_PASSPHRASETRAYICONHASH] = pwd; + settings[CONST.SERVER_PASSPHRASETRAYICON] = password; + settings[CONST.SERVER_PASSPHRASETRAYICONHASH] = pwd; } SaveSettings(); @@ -387,15 +387,15 @@ namespace Duplicati.Server.Database get { long t; - if (long.TryParse(m_values[CONST.UPDATE_CHECK_LAST], out t)) + if (long.TryParse(settings[CONST.UPDATE_CHECK_LAST], out t)) return new DateTime(t, DateTimeKind.Utc); else return new DateTime(0, DateTimeKind.Utc); } set { - lock(m_connection.m_lock) - m_values[CONST.UPDATE_CHECK_LAST] = value.ToUniversalTime().Ticks.ToString(); + lock(databaseConnection.m_lock) + settings[CONST.UPDATE_CHECK_LAST] = value.ToUniversalTime().Ticks.ToString(); SaveSettings(); } } @@ -404,7 +404,7 @@ namespace Duplicati.Server.Database { get { - var tp = m_values[CONST.UPDATE_CHECK_INTERVAL]; + var tp = settings[CONST.UPDATE_CHECK_INTERVAL]; if (string.IsNullOrWhiteSpace(tp)) tp = "1W"; @@ -412,8 +412,8 @@ namespace Duplicati.Server.Database } set { - lock(m_connection.m_lock) - m_values[CONST.UPDATE_CHECK_INTERVAL] = value; + lock(databaseConnection.m_lock) + settings[CONST.UPDATE_CHECK_INTERVAL] = value; SaveSettings(); Program.UpdatePoller.Reschedule(); } @@ -438,7 +438,7 @@ namespace Duplicati.Server.Database { get { - if (string.IsNullOrWhiteSpace(m_values[CONST.UPDATE_CHECK_NEW_VERSION])) + if (string.IsNullOrWhiteSpace(settings[CONST.UPDATE_CHECK_NEW_VERSION])) return null; try @@ -446,7 +446,7 @@ namespace Duplicati.Server.Database if (m_latestUpdate != null) return m_latestUpdate; - using(var tr = new System.IO.StringReader(m_values[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 @@ -468,8 +468,8 @@ namespace Duplicati.Server.Database } m_latestUpdate = value; - lock(m_connection.m_lock) - m_values[CONST.UPDATE_CHECK_NEW_VERSION] = result; + lock(databaseConnection.m_lock) + settings[CONST.UPDATE_CHECK_NEW_VERSION] = result; SaveSettings(); } @@ -479,12 +479,12 @@ namespace Duplicati.Server.Database { get { - return m_values[CONST.SERVER_LISTEN_INTERFACE]; + return settings[CONST.SERVER_LISTEN_INTERFACE]; } set { - lock(m_connection.m_lock) - m_values[CONST.SERVER_LISTEN_INTERFACE] = value; + lock(databaseConnection.m_lock) + settings[CONST.SERVER_LISTEN_INTERFACE] = value; SaveSettings(); } } @@ -493,16 +493,16 @@ namespace Duplicati.Server.Database { get { - if (String.IsNullOrEmpty(m_values[CONST.SERVER_SSL_CERTIFICATE])) + if (String.IsNullOrEmpty(settings[CONST.SERVER_SSL_CERTIFICATE])) return null; if (Platform.IsClientWindows) - return new X509Certificate2(Convert.FromBase64String(m_values[CONST.SERVER_SSL_CERTIFICATE])); + return new X509Certificate2(Convert.FromBase64String(settings[CONST.SERVER_SSL_CERTIFICATE])); else { var store = new Pkcs12Store(); - using (var stream = new System.IO.MemoryStream(Convert.FromBase64String(m_values[CONST.SERVER_SSL_CERTIFICATE]))) + using (var stream = new System.IO.MemoryStream(Convert.FromBase64String(settings[CONST.SERVER_SSL_CERTIFICATE]))) store.Load(stream, null); if (store.Count != 1) @@ -523,14 +523,14 @@ namespace Duplicati.Server.Database { if (value == null) { - lock (m_connection.m_lock) - m_values[CONST.SERVER_SSL_CERTIFICATE] = String.Empty; + lock (databaseConnection.m_lock) + settings[CONST.SERVER_SSL_CERTIFICATE] = String.Empty; } else { if (Platform.IsClientWindows) - lock (m_connection.m_lock) - m_values[CONST.SERVER_SSL_CERTIFICATE] = Convert.ToBase64String(value.Export(X509ContentType.Pkcs12)); + lock (databaseConnection.m_lock) + settings[CONST.SERVER_SSL_CERTIFICATE] = Convert.ToBase64String(value.Export(X509ContentType.Pkcs12)); else { var store = new Pkcs12Store(); @@ -542,8 +542,8 @@ namespace Duplicati.Server.Database using (var stream = new System.IO.MemoryStream()) { store.Save(stream, null, new SecureRandom()); - lock (m_connection.m_lock) - m_values[CONST.SERVER_SSL_CERTIFICATE] = Convert.ToBase64String(stream.ToArray()); + lock (databaseConnection.m_lock) + settings[CONST.SERVER_SSL_CERTIFICATE] = Convert.ToBase64String(stream.ToArray()); } } } @@ -555,12 +555,12 @@ namespace Duplicati.Server.Database { get { - return Duplicati.Library.Utility.Utility.ParseBool(m_values[CONST.HAS_FIXED_INVALID_BACKUPID], false); + return Duplicati.Library.Utility.Utility.ParseBool(settings[CONST.HAS_FIXED_INVALID_BACKUPID], false); } set { - lock(m_connection.m_lock) - m_values[CONST.HAS_FIXED_INVALID_BACKUPID] = value.ToString(); + lock(databaseConnection.m_lock) + settings[CONST.HAS_FIXED_INVALID_BACKUPID] = value.ToString(); SaveSettings(); } } @@ -569,12 +569,12 @@ namespace Duplicati.Server.Database { get { - return m_values[CONST.UPDATE_CHANNEL]; + return settings[CONST.UPDATE_CHANNEL]; } set { - lock(m_connection.m_lock) - m_values[CONST.UPDATE_CHANNEL] = value; + lock(databaseConnection.m_lock) + settings[CONST.UPDATE_CHANNEL] = value; SaveSettings(); } } @@ -583,12 +583,12 @@ namespace Duplicati.Server.Database { get { - return m_values[CONST.USAGE_REPORTER_LEVEL]; + return settings[CONST.USAGE_REPORTER_LEVEL]; } set { - lock(m_connection.m_lock) - m_values[CONST.USAGE_REPORTER_LEVEL] = value; + lock(databaseConnection.m_lock) + settings[CONST.USAGE_REPORTER_LEVEL] = value; SaveSettings(); } } From 6332a24984f3fea566e346490345d4d64584d144 Mon Sep 17 00:00:00 2001 From: verhoek <30193551+verhoek@users.noreply.github.com> Date: Thu, 7 Feb 2019 20:37:46 +0100 Subject: [PATCH 04/17] Remove unused imports. --- Duplicati/CommandLine/Program.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/Duplicati/CommandLine/Program.cs b/Duplicati/CommandLine/Program.cs index 893cb8c03..5eef3343e 100644 --- a/Duplicati/CommandLine/Program.cs +++ b/Duplicati/CommandLine/Program.cs @@ -19,10 +19,8 @@ #endregion using System; using System.Collections.Generic; -using System.Text; using System.Linq; using Duplicati.Library.Localization.Short; -using Duplicati.Library.Utility; using System.IO; using Duplicati.Library.Common; From eb36915045a072caf2ff9ae4e67d798a02578a70 Mon Sep 17 00:00:00 2001 From: verhoek <30193551+verhoek@users.noreply.github.com> Date: Thu, 7 Feb 2019 21:36:24 +0100 Subject: [PATCH 05/17] Use more readable local function. --- .../Server/KeyManager/IPasswordService.cs | 24 ++++++++++ .../Server/KeyManager/PasswordFromDatabase.cs | 44 +++++++++++++++++++ Duplicati/Server/Program.cs | 10 +++-- 3 files changed, 74 insertions(+), 4 deletions(-) create mode 100644 Duplicati/Server/KeyManager/IPasswordService.cs create mode 100644 Duplicati/Server/KeyManager/PasswordFromDatabase.cs diff --git a/Duplicati/Server/KeyManager/IPasswordService.cs b/Duplicati/Server/KeyManager/IPasswordService.cs new file mode 100644 index 000000000..0fa8fce4e --- /dev/null +++ b/Duplicati/Server/KeyManager/IPasswordService.cs @@ -0,0 +1,24 @@ +// Copyright (C) 2019, 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 +using System; +namespace Duplicati.Security.KeyManager +{ + public interface IPasswordService + { + string GetPassphrase(long backupId); + } +} diff --git a/Duplicati/Server/KeyManager/PasswordFromDatabase.cs b/Duplicati/Server/KeyManager/PasswordFromDatabase.cs new file mode 100644 index 000000000..e0e3b3c24 --- /dev/null +++ b/Duplicati/Server/KeyManager/PasswordFromDatabase.cs @@ -0,0 +1,44 @@ +// Copyright (C) 2019, 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 +using System; +using Duplicati.Server.Database; +using Duplicati.Server.Serialization.Interface; +using System.Linq; + + +namespace Duplicati.Security.KeyManager +{ + public class PasswordFromDatabase : IPasswordService + { + + private readonly Connection con; + + public PasswordFromDatabase(Connection con) + { + this.con = con; + } + + public string GetPassphrase(long backupId) + { + ISetting[] settings = con.GetSettings(backupId); + return settings.Where(s => s.Name == "passphrase") + .Select(s => s.Value).FirstOrDefault(); + } + + public string PasswordSalt => throw new NotImplementedException(); + } +} diff --git a/Duplicati/Server/Program.cs b/Duplicati/Server/Program.cs index 22d0e7ab7..e6044b53f 100644 --- a/Duplicati/Server/Program.cs +++ b/Duplicati/Server/Program.cs @@ -402,8 +402,10 @@ namespace Duplicati.Server } }; - Action registerTaskResult = (id, ex) => { - lock(Program.MainLock) { + void registerTaskResult(long id, Exception ex) + { + lock (Program.MainLock) + { // If the new results says it crashed, we store that instead of success if (Program.TaskResultCache.Count > 0 && Program.TaskResultCache.Last().Key == id) @@ -415,10 +417,10 @@ namespace Duplicati.Server } Program.TaskResultCache.Add(new KeyValuePair(id, ex)); - while(Program.TaskResultCache.Count > MAX_TASK_RESULT_CACHE_SIZE) + while (Program.TaskResultCache.Count > MAX_TASK_RESULT_CACHE_SIZE) Program.TaskResultCache.RemoveAt(0); } - }; + } Program.WorkThread.CompletedWork += (worker, task) => { registerTaskResult(task.TaskID, null); }; Program.WorkThread.OnError += (worker, task, exception) => { registerTaskResult(task.TaskID, exception); }; From e57475d972048b993e0415bea134dff0ff243276 Mon Sep 17 00:00:00 2001 From: verhoek <30193551+verhoek@users.noreply.github.com> Date: Thu, 7 Feb 2019 21:41:58 +0100 Subject: [PATCH 06/17] Simplify show help. --- Duplicati/Server/Program.cs | 58 +++++++++++++++++++------------------ 1 file changed, 30 insertions(+), 28 deletions(-) diff --git a/Duplicati/Server/Program.cs b/Duplicati/Server/Program.cs index e6044b53f..d5277ba4e 100644 --- a/Duplicati/Server/Program.cs +++ b/Duplicati/Server/Program.cs @@ -209,39 +209,26 @@ namespace Duplicati.Server Library.Utility.SystemContextSettings.StartSession(); - // Check if a parameters-file was provided. Skip if help was already specified - if (!commandlineOptions.ContainsKey("help")) - { - // try and parse all parameter file aliases - foreach (string parameterOption in new[] { "parameters-file", "parameters-file", "parameterfile" }) - { - if (commandlineOptions.ContainsKey(parameterOption) && !string.IsNullOrEmpty(commandlineOptions[parameterOption])) - { - string filename = commandlineOptions[parameterOption]; - commandlineOptions.Remove(parameterOption); - if (!ReadOptionsFromFile(filename, ref filter, args, commandlineOptions)) - return 100; - break; - } - } - } - //If the commandline issues --help, just stop here if (commandlineOptions.ContainsKey("help")) { - if (writeConsole) - { - Console.WriteLine(Strings.Program.HelpDisplayDialog); - - foreach(Library.Interface.ICommandLineArgument arg in SupportedCommands) - Console.WriteLine(Strings.Program.HelpDisplayFormat(arg.Name, arg.LongDescription)); - - return 0; - } - - throw new Exception("Server invoked with --help"); + return ShowHelp(writeConsole); } + // try and parse all parameter file aliases + foreach (string parameterOption in new[] { "parameters-file", "parameters-file", "parameterfile" }) + { + if (commandlineOptions.ContainsKey(parameterOption) && !string.IsNullOrEmpty(commandlineOptions[parameterOption])) + { + string filename = commandlineOptions[parameterOption]; + commandlineOptions.Remove(parameterOption); + if (!ReadOptionsFromFile(filename, ref filter, args, commandlineOptions)) + return 100; + break; + } + } + + #if DEBUG //Log various information in the logfile if (!commandlineOptions.ContainsKey("log-file")) @@ -494,6 +481,21 @@ namespace Duplicati.Server return 0; } + private static int ShowHelp(bool writeConsole) + { + if (writeConsole) + { + Console.WriteLine(Strings.Program.HelpDisplayDialog); + + foreach (Library.Interface.ICommandLineArgument arg in SupportedCommands) + Console.WriteLine(Strings.Program.HelpDisplayFormat(arg.Name, arg.LongDescription)); + + return 0; + } + + throw new Exception("Server invoked with --help"); + } + public static Database.Connection GetDatabaseConnection(Dictionary commandlineOptions) { var dbPassword = Environment.GetEnvironmentVariable(DB_KEY_ENV_NAME); From 1967eabd6f37918da294209cbf269e49939b729d Mon Sep 17 00:00:00 2001 From: verhoek <30193551+verhoek@users.noreply.github.com> Date: Thu, 7 Feb 2019 21:57:36 +0100 Subject: [PATCH 07/17] Make alternative help commands more compact. --- Duplicati/Server/Program.cs | 22 +++++++--------------- 1 file changed, 7 insertions(+), 15 deletions(-) diff --git a/Duplicati/Server/Program.cs b/Duplicati/Server/Program.cs index d5277ba4e..dada843db 100644 --- a/Duplicati/Server/Program.cs +++ b/Duplicati/Server/Program.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; -using System.Text; using Duplicati.Library.Common; using Duplicati.Library.Common.IO; @@ -10,6 +9,9 @@ namespace Duplicati.Server { public class Program { + + private static readonly List alternativeHelpStrings = new List { "help", "/help", "usage", "/usage", "--help" }; + /// /// The log tag for messages from this class /// @@ -195,26 +197,16 @@ namespace Duplicati.Server var commandlineOptions = argtuple.Item1; var filter = argtuple.Item2; - foreach(var s in _args) - if ( - s.Equals("help", StringComparison.OrdinalIgnoreCase) || - s.Equals("/help", StringComparison.OrdinalIgnoreCase) || - s.Equals("usage", StringComparison.OrdinalIgnoreCase) || - s.Equals("/usage", StringComparison.OrdinalIgnoreCase)) - commandlineOptions["help"] = ""; - + if (_args.Select(s => s.ToLower()).Intersect(alternativeHelpStrings).Any()) + { + return ShowHelp(writeConsole); + } if (commandlineOptions.ContainsKey("tempdir") && !string.IsNullOrEmpty(commandlineOptions["tempdir"])) Library.Utility.SystemContextSettings.DefaultTempPath = commandlineOptions["tempdir"]; Library.Utility.SystemContextSettings.StartSession(); - //If the commandline issues --help, just stop here - if (commandlineOptions.ContainsKey("help")) - { - return ShowHelp(writeConsole); - } - // try and parse all parameter file aliases foreach (string parameterOption in new[] { "parameters-file", "parameters-file", "parameterfile" }) { From bcf7df2de37a609f0cfd492838ab1b298c7b3407 Mon Sep 17 00:00:00 2001 From: verhoek <30193551+verhoek@users.noreply.github.com> Date: Sat, 9 Feb 2019 16:06:40 +0100 Subject: [PATCH 08/17] Refactored parameterfile option. --- Duplicati/Server/Program.cs | 23 +++++++++++------------ 1 file changed, 11 insertions(+), 12 deletions(-) diff --git a/Duplicati/Server/Program.cs b/Duplicati/Server/Program.cs index dada843db..52fe0a830 100644 --- a/Duplicati/Server/Program.cs +++ b/Duplicati/Server/Program.cs @@ -12,6 +12,8 @@ namespace Duplicati.Server private static readonly List alternativeHelpStrings = new List { "help", "/help", "usage", "/usage", "--help" }; + private static readonly List parameterFileOptionStrings = new List { "parameters-file", "parameters-file", "parameterfile" }; + /// /// The log tag for messages from this class /// @@ -203,23 +205,20 @@ namespace Duplicati.Server } if (commandlineOptions.ContainsKey("tempdir") && !string.IsNullOrEmpty(commandlineOptions["tempdir"])) + { Library.Utility.SystemContextSettings.DefaultTempPath = commandlineOptions["tempdir"]; + } Library.Utility.SystemContextSettings.StartSession(); - // try and parse all parameter file aliases - foreach (string parameterOption in new[] { "parameters-file", "parameters-file", "parameterfile" }) - { - if (commandlineOptions.ContainsKey(parameterOption) && !string.IsNullOrEmpty(commandlineOptions[parameterOption])) - { - string filename = commandlineOptions[parameterOption]; - commandlineOptions.Remove(parameterOption); - if (!ReadOptionsFromFile(filename, ref filter, args, commandlineOptions)) - return 100; - break; - } - } + var parameterFileOption = commandlineOptions.Keys.Select(s => s.ToLower()).Intersect(parameterFileOptionStrings).FirstOrDefault(); + if (parameterFileOption != null && !string.IsNullOrEmpty(commandlineOptions[parameterFileOption])) + { + commandlineOptions.Remove(parameterFileOption); + if (!ReadOptionsFromFile(commandlineOptions[parameterFileOption], ref filter, args, commandlineOptions)) + return 100; + } #if DEBUG //Log various information in the logfile From 571e8ef989cbef3775f9574016ad35dc462846b6 Mon Sep 17 00:00:00 2001 From: verhoek <30193551+verhoek@users.noreply.github.com> Date: Sat, 9 Feb 2019 16:39:40 +0100 Subject: [PATCH 09/17] Conver static consts to lowercase. Extract logging config to private function. --- Duplicati/Server/Program.cs | 72 ++++++++++++++++++++----------------- 1 file changed, 40 insertions(+), 32 deletions(-) diff --git a/Duplicati/Server/Program.cs b/Duplicati/Server/Program.cs index 52fe0a830..50af6be31 100644 --- a/Duplicati/Server/Program.cs +++ b/Duplicati/Server/Program.cs @@ -199,7 +199,7 @@ namespace Duplicati.Server var commandlineOptions = argtuple.Item1; var filter = argtuple.Item2; - if (_args.Select(s => s.ToLower()).Intersect(alternativeHelpStrings).Any()) + if (_args.Select(s => s.ToLower()).Intersect(alternativeHelpStrings.ConvertAll(x => x.ToLower())).Any()) { return ShowHelp(writeConsole); } @@ -208,10 +208,11 @@ namespace Duplicati.Server { Library.Utility.SystemContextSettings.DefaultTempPath = commandlineOptions["tempdir"]; } - + Library.Utility.SystemContextSettings.StartSession(); - var parameterFileOption = commandlineOptions.Keys.Select(s => s.ToLower()).Intersect(parameterFileOptionStrings).FirstOrDefault(); + var parameterFileOption = commandlineOptions.Keys.Select(s => s.ToLower()) + .Intersect(parameterFileOptionStrings.ConvertAll(x => x.ToLower())).FirstOrDefault(); if (parameterFileOption != null && !string.IsNullOrEmpty(commandlineOptions[parameterFileOption])) { @@ -220,35 +221,10 @@ namespace Duplicati.Server return 100; } -#if DEBUG - //Log various information in the logfile - if (!commandlineOptions.ContainsKey("log-file")) - { - commandlineOptions["log-file"] = System.IO.Path.Combine(StartupPath, "Duplicati.debug.log"); - commandlineOptions["log-level"] = Duplicati.Library.Logging.LogMessageType.Profiling.ToString(); - } -#endif + ConfigureLogging(commandlineOptions); try { - // Setup the log redirect - Library.Logging.Log.StartScope(Program.LogHandler, null); - - if (commandlineOptions.ContainsKey("log-file")) - { -#if DEBUG - // Only delete the --log-file when in DEBUG mode. Otherwise, don't delete and just append logs. - if (System.IO.File.Exists(commandlineOptions["log-file"])) - System.IO.File.Delete(commandlineOptions["log-file"]); -#endif - - var loglevel = Duplicati.Library.Logging.LogMessageType.Error; - - if (commandlineOptions.ContainsKey("log-level")) - Enum.TryParse(commandlineOptions["log-level"], true, out loglevel); - - Program.LogHandler.SetServerFile(commandlineOptions["log-file"], loglevel); - } DataConnection = GetDatabaseConnection(commandlineOptions); @@ -303,7 +279,8 @@ namespace Duplicati.Server UpdatePoller = new UpdatePollThread(); DateTime lastPurge = new DateTime(0); - System.Threading.TimerCallback purgeTempFilesCallback = (x) => { + System.Threading.TimerCallback purgeTempFilesCallback = (x) => + { try { if (Math.Abs((DateTime.Now - lastPurge).TotalHours) < 23) @@ -311,7 +288,7 @@ namespace Duplicati.Server lastPurge = DateTime.Now; - foreach(var e in Program.DataConnection.GetTempFiles().Where((f) => f.Expires < DateTime.Now)) + foreach (var e in Program.DataConnection.GetTempFiles().Where((f) => f.Expires < DateTime.Now)) { try { @@ -327,7 +304,8 @@ namespace Duplicati.Server } - Duplicati.Library.Utility.TempFile.RemoveOldApplicationTempFiles((path, ex) => { + Duplicati.Library.Utility.TempFile.RemoveOldApplicationTempFiles((path, ex) => + { Program.DataConnection.LogError(null, string.Format("Failed to delete temp file: {0}", path), ex); }); @@ -472,6 +450,36 @@ namespace Duplicati.Server return 0; } + private static void ConfigureLogging(Dictionary commandlineOptions) + { + +#if DEBUG + //Log various information in the logfile + if (!commandlineOptions.ContainsKey("log-file")) + { + commandlineOptions["log-file"] = System.IO.Path.Combine(StartupPath, "Duplicati.debug.log"); + commandlineOptions["log-level"] = Duplicati.Library.Logging.LogMessageType.Profiling.ToString(); + if (System.IO.File.Exists(commandlineOptions["log-file"])) + { + System.IO.File.Delete(commandlineOptions["log-file"]); + } + } +#endif + + // Setup the log redirect + Library.Logging.Log.StartScope(LogHandler, null); + + if (commandlineOptions.ContainsKey("log-file")) + { + var loglevel = Library.Logging.LogMessageType.Error; + + if (commandlineOptions.ContainsKey("log-level")) + Enum.TryParse(commandlineOptions["log-level"], true, out loglevel); + + LogHandler.SetServerFile(commandlineOptions["log-file"], loglevel); + } + } + private static int ShowHelp(bool writeConsole) { if (writeConsole) From c5f1198752ec2599ff83bf9971d196750a14b950 Mon Sep 17 00:00:00 2001 From: verhoek <30193551+verhoek@users.noreply.github.com> Date: Sun, 10 Feb 2019 19:58:56 +0100 Subject: [PATCH 10/17] Renamed instance. --- Duplicati/Server/Program.cs | 31 ++++++++++++++++--------------- 1 file changed, 16 insertions(+), 15 deletions(-) diff --git a/Duplicati/Server/Program.cs b/Duplicati/Server/Program.cs index 50af6be31..74c4be355 100644 --- a/Duplicati/Server/Program.cs +++ b/Duplicati/Server/Program.cs @@ -41,7 +41,7 @@ namespace Duplicati.Server /// /// The single instance /// - public static SingleInstance Instance = null; + public static SingleInstance ApplicationInstance = null; /// /// This is the only access to the database @@ -234,7 +234,7 @@ namespace Duplicati.Server try { //This will also create DATAFOLDER if it does not exist - Instance = new SingleInstance(DataFolder); + ApplicationInstance = new SingleInstance(DataFolder); } catch (Exception ex) { @@ -247,7 +247,7 @@ namespace Duplicati.Server throw new Exception(Strings.Program.StartupFailure(ex)); } - if (!Instance.IsFirstInstance) + if (!ApplicationInstance.IsFirstInstance) { if (writeConsole) { @@ -273,7 +273,7 @@ namespace Duplicati.Server Duplicati.Library.AutoUpdater.UpdaterManager.OnError += (Exception obj) => { - Program.DataConnection.LogError(null, "Error in updater", obj); + DataConnection.LogError(null, "Error in updater", obj); }; UpdatePoller = new UpdatePollThread(); @@ -288,7 +288,7 @@ namespace Duplicati.Server lastPurge = DateTime.Now; - foreach (var e in Program.DataConnection.GetTempFiles().Where((f) => f.Expires < DateTime.Now)) + foreach (var e in DataConnection.GetTempFiles().Where((f) => f.Expires < DateTime.Now)) { try { @@ -297,27 +297,28 @@ namespace Duplicati.Server } catch (Exception ex) { - Program.DataConnection.LogError(null, string.Format("Failed to delete temp file: {0}", e.Path), ex); + DataConnection.LogError(null, string.Format("Failed to delete temp file: {0}", e.Path), ex); } - Program.DataConnection.DeleteTempFile(e.ID); + DataConnection.DeleteTempFile(e.ID); } - Duplicati.Library.Utility.TempFile.RemoveOldApplicationTempFiles((path, ex) => + Library.Utility.TempFile.RemoveOldApplicationTempFiles((path, ex) => { - Program.DataConnection.LogError(null, string.Format("Failed to delete temp file: {0}", path), ex); + DataConnection.LogError(null, string.Format("Failed to delete temp file: {0}", path), ex); }); - string pts; - if (!commandlineOptions.TryGetValue("log-retention", out pts)) + if (!commandlineOptions.TryGetValue("log-retention", out string pts)) + { pts = DEFAULT_LOG_RETENTION; + } - Program.DataConnection.PurgeLogData(Library.Utility.Timeparser.ParseTimeInterval(pts, DateTime.Now, true)); + DataConnection.PurgeLogData(Library.Utility.Timeparser.ParseTimeInterval(pts, DateTime.Now, true)); } catch (Exception ex) { - Program.DataConnection.LogError(null, "Failed during temp file cleanup", ex); + DataConnection.LogError(null, "Failed during temp file cleanup", ex); } }; @@ -429,8 +430,8 @@ namespace Duplicati.Server Scheduler.Terminate(true); if (WorkThread != null) WorkThread.Terminate(true); - if (Instance != null) - Instance.Dispose(); + if (ApplicationInstance != null) + ApplicationInstance.Dispose(); if (PurgeTempFilesTimer != null) PurgeTempFilesTimer.Dispose(); From 72e07eb3c1a12b76319cee17ba86ae31562bea08 Mon Sep 17 00:00:00 2001 From: verhoek <30193551+verhoek@users.noreply.github.com> Date: Mon, 11 Feb 2019 21:35:59 +0100 Subject: [PATCH 11/17] Consolidate logic of settings from database or not. --- .../GUI/Duplicati.GUI.TrayIcon/Program.cs | 34 +++++++++---------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs index c0dafd935..044729f51 100644 --- a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs @@ -2,7 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Net; -using Duplicati.Library.Common; +using Duplicati.Library.Common; using Duplicati.Library.Interface; namespace Duplicati.GUI.TrayIcon @@ -140,6 +140,7 @@ namespace Duplicati.GUI.TrayIcon string password = null; var saltedpassword = false; var serverURL = new Uri(DEFAULT_HOSTURL); + var disableTrayIconLogin = false; if (!Library.Utility.Utility.ParseBoolOption(options, NOHOSTEDSERVER_OPTION)) { @@ -172,29 +173,28 @@ namespace Duplicati.GUI.TrayIcon Scheme = scheme }).Uri; } - - if (Library.Utility.Utility.ParseBoolOption(options, NOHOSTEDSERVER_OPTION) && Library.Utility.Utility.ParseBoolOption(options, READCONFIGFROMDB_OPTION)) + else if (Library.Utility.Utility.ParseBoolOption(options, READCONFIGFROMDB_OPTION)) + { databaseConnection = Server.Program.GetDatabaseConnection(options); - var disableTrayIconLogin = false; + if (databaseConnection != null) + { + disableTrayIconLogin = databaseConnection.ApplicationSettings.DisableTrayIconLogin; + password = databaseConnection.ApplicationSettings.WebserverPasswordTrayIcon; + saltedpassword = false; - if (databaseConnection != null) - { - disableTrayIconLogin = databaseConnection.ApplicationSettings.DisableTrayIconLogin; - password = databaseConnection.ApplicationSettings.WebserverPasswordTrayIcon; - saltedpassword = false; + var cert = databaseConnection.ApplicationSettings.ServerSSLCertificate; + var scheme = "http"; - var cert = databaseConnection.ApplicationSettings.ServerSSLCertificate; - var scheme = "http"; + if (cert != null && cert.HasPrivateKey) + scheme = "https"; - if (cert != null && cert.HasPrivateKey) - scheme = "https"; - - serverURL = (new UriBuilder(serverURL) + serverURL = (new UriBuilder(serverURL) { Port = databaseConnection.ApplicationSettings.LastWebserverPort == -1 ? serverURL.Port : databaseConnection.ApplicationSettings.LastWebserverPort, Scheme = scheme }).Uri; + } } string pwd; @@ -224,8 +224,8 @@ namespace Duplicati.GUI.TrayIcon { using (var tk = RunTrayIcon(toolkit)) { - if (hosted != null && Server.Program.Instance != null) - Server.Program.Instance.SecondInstanceDetected += + if (hosted != null && Server.Program.ApplicationInstance != null) + Server.Program.ApplicationInstance.SecondInstanceDetected += new Server.SingleInstance.SecondInstanceDelegate( x => { tk.ShowUrlInWindow(serverURL.ToString()); }); From db13255dc8d037b1638d0cb67f21bb1abc4e1807 Mon Sep 17 00:00:00 2001 From: verhoek <30193551+verhoek@users.noreply.github.com> Date: Tue, 12 Feb 2019 21:47:17 +0100 Subject: [PATCH 12/17] Extracted function. --- Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs index 044729f51..802176a56 100644 --- a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs @@ -76,7 +76,7 @@ namespace Duplicati.GUI.TrayIcon if (Platform.IsClientWindows && (Duplicati.Library.AutoUpdater.UpdaterManager.IsRunningInUpdateEnvironment || !Duplicati.Library.Utility.Utility.ParseBoolOption(options, DETACHED_PROCESS))) Duplicati.Library.Utility.Win32.AttachConsole(Duplicati.Library.Utility.Win32.ATTACH_PARENT_PROCESS); - + foreach (string s in args) if ( s.Equals("help", StringComparison.OrdinalIgnoreCase) || @@ -94,7 +94,7 @@ namespace Duplicati.GUI.TrayIcon { Console.WriteLine("--{0}: {1}", arg.Name, arg.LongDescription); if (arg.Name == TOOLKIT_OPTION) - Console.WriteLine(" Supported toolkits: {0}{1}", string.Join(", ", arg.ValidValues), Environment.NewLine); + Console.WriteLine(" Supported toolkits: {0}{1}", string.Join(", ", arg.ValidValues), Environment.NewLine); } Console.WriteLine("Additionally, these server options are also supported:"); @@ -107,7 +107,7 @@ namespace Duplicati.GUI.TrayIcon } options.TryGetValue(BROWSER_COMMAND_OPTION, out _browser_command); - + string toolkit; if (!options.TryGetValue(TOOLKIT_OPTION, out toolkit)) { @@ -117,7 +117,7 @@ namespace Duplicati.GUI.TrayIcon #endif toolkit = GetDefaultToolKit(); } - else + else { if (TOOLKIT_WINDOWS_FORMS.Equals(toolkit, StringComparison.OrdinalIgnoreCase)) toolkit = TOOLKIT_WINDOWS_FORMS; @@ -209,7 +209,12 @@ namespace Duplicati.GUI.TrayIcon if (options.TryGetValue(HOSTURL_OPTION, out url)) serverURL = new Uri(url); - + + StartTray(_args, options, toolkit, hosted, openui, password, saltedpassword, serverURL, disableTrayIconLogin); + } + + private static void StartTray(string[] _args, Dictionary options, string toolkit, HostedInstanceKeeper hosted, bool openui, string password, bool saltedpassword, Uri serverURL, bool disableTrayIconLogin) + { using (hosted) { var reSpawn = 0; From d644de3668e996d9e61cc79e609f4c1e52de3974 Mon Sep 17 00:00:00 2001 From: verhoek <30193551+verhoek@users.noreply.github.com> Date: Tue, 12 Feb 2019 21:50:33 +0100 Subject: [PATCH 13/17] Promote vars to member instances. --- Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs index 802176a56..411d668f4 100644 --- a/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/Program.cs @@ -34,6 +34,11 @@ namespace Duplicati.GUI.TrayIcon private const string DEFAULT_HOSTURL = "http://localhost:8200"; private static string _browser_command = null; + private static bool disableTrayIconLogin = false; + private static bool openui = false; + private static Uri serverURL = new Uri(DEFAULT_HOSTURL); + + public static string BrowserCommand { get { return _browser_command; } } public static Server.Database.Connection databaseConnection = null; @@ -136,11 +141,9 @@ namespace Duplicati.GUI.TrayIcon } HostedInstanceKeeper hosted = null; - var openui = false; + string password = null; var saltedpassword = false; - var serverURL = new Uri(DEFAULT_HOSTURL); - var disableTrayIconLogin = false; if (!Library.Utility.Utility.ParseBoolOption(options, NOHOSTEDSERVER_OPTION)) { @@ -210,10 +213,10 @@ namespace Duplicati.GUI.TrayIcon if (options.TryGetValue(HOSTURL_OPTION, out url)) serverURL = new Uri(url); - StartTray(_args, options, toolkit, hosted, openui, password, saltedpassword, serverURL, disableTrayIconLogin); + StartTray(_args, options, toolkit, hosted, password, saltedpassword); } - private static void StartTray(string[] _args, Dictionary options, string toolkit, HostedInstanceKeeper hosted, bool openui, string password, bool saltedpassword, Uri serverURL, bool disableTrayIconLogin) + private static void StartTray(string[] _args, Dictionary options, string toolkit, HostedInstanceKeeper hosted, string password, bool saltedpassword) { using (hosted) { From f89e60033b2dc68f2232b95ebe416dcc01902c41 Mon Sep 17 00:00:00 2001 From: verhoek <30193551+verhoek@users.noreply.github.com> Date: Tue, 12 Feb 2019 21:59:43 +0100 Subject: [PATCH 14/17] Extracted method. --- Duplicati/Server/Program.cs | 66 ++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 30 deletions(-) diff --git a/Duplicati/Server/Program.cs b/Duplicati/Server/Program.cs index 74c4be355..c547f9cdd 100644 --- a/Duplicati/Server/Program.cs +++ b/Duplicati/Server/Program.cs @@ -191,7 +191,7 @@ namespace Duplicati.Server } //If this executable is invoked directly, write to console, otherwise throw exceptions - var writeConsole = System.Reflection.Assembly.GetEntryAssembly() == System.Reflection.Assembly.GetExecutingAssembly(); + var writeToConsole = System.Reflection.Assembly.GetEntryAssembly() == System.Reflection.Assembly.GetExecutingAssembly(); //Find commandline options here for handling special startup cases var args = new List(_args); @@ -201,7 +201,7 @@ namespace Duplicati.Server if (_args.Select(s => s.ToLower()).Intersect(alternativeHelpStrings.ConvertAll(x => x.ToLower())).Any()) { - return ShowHelp(writeConsole); + return ShowHelp(writeToConsole); } if (commandlineOptions.ContainsKey("tempdir") && !string.IsNullOrEmpty(commandlineOptions["tempdir"])) @@ -231,32 +231,7 @@ namespace Duplicati.Server if (!DataConnection.ApplicationSettings.FixedInvalidBackupId) DataConnection.FixInvalidBackupId(); - try - { - //This will also create DATAFOLDER if it does not exist - ApplicationInstance = new SingleInstance(DataFolder); - } - catch (Exception ex) - { - if (writeConsole) - { - Console.WriteLine(Strings.Program.StartupFailure(ex)); - return 200; - } - - throw new Exception(Strings.Program.StartupFailure(ex)); - } - - if (!ApplicationInstance.IsFirstInstance) - { - if (writeConsole) - { - Console.WriteLine(Strings.Program.AnotherInstanceDetected); - return 200; - } - - throw new SingleInstance.MultipleInstanceException(Strings.Program.AnotherInstanceDetected); - } + CreateApplicationInstance(writeToConsole); StartOrStopUsageReporter(); @@ -401,7 +376,7 @@ namespace Duplicati.Server catch (SingleInstance.MultipleInstanceException mex) { System.Diagnostics.Trace.WriteLine(Strings.Program.SeriousError(mex.ToString())); - if (writeConsole) + if (writeToConsole) { Console.WriteLine(Strings.Program.SeriousError(mex.ToString())); return 100; @@ -412,7 +387,7 @@ namespace Duplicati.Server catch (Exception ex) { System.Diagnostics.Trace.WriteLine(Strings.Program.SeriousError(ex.ToString())); - if (writeConsole) + if (writeToConsole) { Console.WriteLine(Strings.Program.SeriousError(ex.ToString())); return 100; @@ -451,6 +426,37 @@ namespace Duplicati.Server return 0; } + + private static void CreateApplicationInstance(bool writeConsole) + { + try + { + //This will also create DATAFOLDER if it does not exist + ApplicationInstance = new SingleInstance(DataFolder); + } + catch (Exception ex) + { + if (writeConsole) + { + Console.WriteLine(Strings.Program.StartupFailure(ex)); + Environment.Exit(200); + } + + throw new Exception(Strings.Program.StartupFailure(ex)); + } + + if (!ApplicationInstance.IsFirstInstance) + { + if (writeConsole) + { + Console.WriteLine(Strings.Program.AnotherInstanceDetected); + Environment.Exit(200); + } + + throw new SingleInstance.MultipleInstanceException(Strings.Program.AnotherInstanceDetected); + } + } + private static void ConfigureLogging(Dictionary commandlineOptions) { From 5a7fd7183d5657d198d038af732e2a30eb0e5977 Mon Sep 17 00:00:00 2001 From: verhoek <30193551+verhoek@users.noreply.github.com> Date: Wed, 13 Feb 2019 22:06:21 +0100 Subject: [PATCH 15/17] Call GenerateWebserverPasswordTrayIcon from one place only. --- Duplicati/Server/Database/ServerSettings.cs | 1 - Duplicati/Server/Program.cs | 11 +++++++---- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Duplicati/Server/Database/ServerSettings.cs b/Duplicati/Server/Database/ServerSettings.cs index 342a86199..7755d51e5 100644 --- a/Duplicati/Server/Database/ServerSettings.cs +++ b/Duplicati/Server/Database/ServerSettings.cs @@ -338,7 +338,6 @@ namespace Duplicati.Server.Database } SaveSettings(); - GenerateWebserverPasswordTrayIcon(); } public void SetAllowedHostnames(string allowedHostnames) diff --git a/Duplicati/Server/Program.cs b/Duplicati/Server/Program.cs index c547f9cdd..e22dcaba1 100644 --- a/Duplicati/Server/Program.cs +++ b/Duplicati/Server/Program.cs @@ -236,13 +236,16 @@ namespace Duplicati.Server StartOrStopUsageReporter(); if (commandlineOptions.ContainsKey("webservice-password")) - Program.DataConnection.ApplicationSettings.SetWebserverPassword(commandlineOptions["webservice-password"]); + { + DataConnection.ApplicationSettings.SetWebserverPassword(commandlineOptions["webservice-password"]); + } - Program.DataConnection.ApplicationSettings.GenerateWebserverPasswordTrayIcon(); + DataConnection.ApplicationSettings.GenerateWebserverPasswordTrayIcon(); if (commandlineOptions.ContainsKey("webservice-allowed-hostnames")) - Program.DataConnection.ApplicationSettings.SetAllowedHostnames(commandlineOptions["webservice-allowed-hostnames"]); - + { + DataConnection.ApplicationSettings.SetAllowedHostnames(commandlineOptions["webservice-allowed-hostnames"]); + } ApplicationExitEvent = new System.Threading.ManualResetEvent(false); From 13fec7b25350ac545ec99945588b41af52e9532a Mon Sep 17 00:00:00 2001 From: verhoek <30193551+verhoek@users.noreply.github.com> Date: Wed, 13 Feb 2019 22:10:00 +0100 Subject: [PATCH 16/17] Extract method. --- Duplicati/Server/Program.cs | 28 ++++++++++++++++------------ 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/Duplicati/Server/Program.cs b/Duplicati/Server/Program.cs index e22dcaba1..5c49f5f5e 100644 --- a/Duplicati/Server/Program.cs +++ b/Duplicati/Server/Program.cs @@ -235,21 +235,11 @@ namespace Duplicati.Server StartOrStopUsageReporter(); - if (commandlineOptions.ContainsKey("webservice-password")) - { - DataConnection.ApplicationSettings.SetWebserverPassword(commandlineOptions["webservice-password"]); - } - - DataConnection.ApplicationSettings.GenerateWebserverPasswordTrayIcon(); - - if (commandlineOptions.ContainsKey("webservice-allowed-hostnames")) - { - DataConnection.ApplicationSettings.SetAllowedHostnames(commandlineOptions["webservice-allowed-hostnames"]); - } + AdjustApplicationSettings(commandlineOptions); ApplicationExitEvent = new System.Threading.ManualResetEvent(false); - Duplicati.Library.AutoUpdater.UpdaterManager.OnError += (Exception obj) => + Library.AutoUpdater.UpdaterManager.OnError += (Exception obj) => { DataConnection.LogError(null, "Error in updater", obj); }; @@ -429,6 +419,20 @@ namespace Duplicati.Server return 0; } + private static void AdjustApplicationSettings(Dictionary commandlineOptions) + { + if (commandlineOptions.ContainsKey("webservice-password")) + { + DataConnection.ApplicationSettings.SetWebserverPassword(commandlineOptions["webservice-password"]); + } + + DataConnection.ApplicationSettings.GenerateWebserverPasswordTrayIcon(); + + if (commandlineOptions.ContainsKey("webservice-allowed-hostnames")) + { + DataConnection.ApplicationSettings.SetAllowedHostnames(commandlineOptions["webservice-allowed-hostnames"]); + } + } private static void CreateApplicationInstance(bool writeConsole) { From 5969cb07d2803ccd22a636fd19e25c3fb7f4b7d2 Mon Sep 17 00:00:00 2001 From: verhoek <30193551+verhoek@users.noreply.github.com> Date: Thu, 14 Feb 2019 21:50:28 +0100 Subject: [PATCH 17/17] Extract purge temp files timer logic to method. --- Duplicati/Server/Program.cs | 150 ++++++++++++++++++------------------ 1 file changed, 75 insertions(+), 75 deletions(-) diff --git a/Duplicati/Server/Program.cs b/Duplicati/Server/Program.cs index 5c49f5f5e..3e79bef14 100644 --- a/Duplicati/Server/Program.cs +++ b/Duplicati/Server/Program.cs @@ -2,9 +2,9 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; -using Duplicati.Library.Common; -using Duplicati.Library.Common.IO; - +using Duplicati.Library.Common; +using Duplicati.Library.Common.IO; + namespace Duplicati.Server { public class Program @@ -245,60 +245,8 @@ namespace Duplicati.Server }; UpdatePoller = new UpdatePollThread(); - DateTime lastPurge = new DateTime(0); - - System.Threading.TimerCallback purgeTempFilesCallback = (x) => - { - try - { - if (Math.Abs((DateTime.Now - lastPurge).TotalHours) < 23) - return; - - lastPurge = DateTime.Now; - - foreach (var e in DataConnection.GetTempFiles().Where((f) => f.Expires < DateTime.Now)) - { - try - { - if (System.IO.File.Exists(e.Path)) - System.IO.File.Delete(e.Path); - } - catch (Exception ex) - { - DataConnection.LogError(null, string.Format("Failed to delete temp file: {0}", e.Path), ex); - } - - DataConnection.DeleteTempFile(e.ID); - } - - - Library.Utility.TempFile.RemoveOldApplicationTempFiles((path, ex) => - { - DataConnection.LogError(null, string.Format("Failed to delete temp file: {0}", path), ex); - }); - - if (!commandlineOptions.TryGetValue("log-retention", out string pts)) - { - pts = DEFAULT_LOG_RETENTION; - } - - DataConnection.PurgeLogData(Library.Utility.Timeparser.ParseTimeInterval(pts, DateTime.Now, true)); - } - catch (Exception ex) - { - DataConnection.LogError(null, "Failed during temp file cleanup", ex); - } - }; - - try - { - PurgeTempFilesTimer = new System.Threading.Timer(purgeTempFilesCallback, null, TimeSpan.FromHours(1), TimeSpan.FromDays(1)); - } - catch (ArgumentOutOfRangeException) - { - //Bugfix for older Mono, slightly more resources used to avoid large values in the period field - PurgeTempFilesTimer = new System.Threading.Timer(purgeTempFilesCallback, null, TimeSpan.FromHours(1), TimeSpan.FromHours(1)); - } + + SetPurgeTempFilesTimer(commandlineOptions); LiveControl = new LiveControls(DataConnection.ApplicationSettings); LiveControl.StateChanged += new EventHandler(LiveControl_StateChanged); @@ -317,14 +265,12 @@ namespace Duplicati.Server Program.Scheduler.NewSchedule += new EventHandler(SignalNewEvent); Program.WorkThread.OnError += (worker, task, exception) => { Program.DataConnection.LogError(task?.BackupID, "Error in worker", exception); }; - var lastscheduleid = LastDataUpdateID; + var lastScheduleId = LastDataUpdateID; Program.StatusEventNotifyer.NewEvent += (sender, e) => { - if (lastscheduleid != LastDataUpdateID) - { - lastscheduleid = LastDataUpdateID; - Program.Scheduler.Reschedule(); - } + if (lastScheduleId == LastDataUpdateID) return; + lastScheduleId = LastDataUpdateID; + Program.Scheduler.Reschedule(); }; void registerTaskResult(long id, Exception ex) @@ -392,16 +338,11 @@ namespace Duplicati.Server { StatusEventNotifyer.SignalNewEvent(); - if (UpdatePoller != null) - UpdatePoller.Terminate(); - if (Scheduler != null) - Scheduler.Terminate(true); - if (WorkThread != null) - WorkThread.Terminate(true); - if (ApplicationInstance != null) - ApplicationInstance.Dispose(); - if (PurgeTempFilesTimer != null) - PurgeTempFilesTimer.Dispose(); + UpdatePoller?.Terminate(); + Scheduler?.Terminate(true); + WorkThread?.Terminate(true); + ApplicationInstance?.Dispose(); + PurgeTempFilesTimer?.Dispose(); Library.UsageReporter.Reporter.ShutDown(); @@ -409,8 +350,7 @@ namespace Duplicati.Server try { PingPongThread.Abort(); } catch { } - if (LogHandler != null) - LogHandler.Dispose(); + LogHandler?.Dispose(); } if (UpdatePoller != null && UpdatePoller.IsUpdateRequested) @@ -419,6 +359,66 @@ namespace Duplicati.Server return 0; } + private static void SetPurgeTempFilesTimer(Dictionary commandlineOptions) + { + var lastPurge = new DateTime(0); + + System.Threading.TimerCallback purgeTempFilesCallback = (x) => + { + try + { + if (Math.Abs((DateTime.Now - lastPurge).TotalHours) < 23) + return; + + lastPurge = DateTime.Now; + + foreach (var e in DataConnection.GetTempFiles().Where((f) => f.Expires < DateTime.Now)) + { + try + { + if (System.IO.File.Exists(e.Path)) + System.IO.File.Delete(e.Path); + } + catch (Exception ex) + { + DataConnection.LogError(null, $"Failed to delete temp file: {e.Path}", ex); + } + + DataConnection.DeleteTempFile(e.ID); + } + + + Library.Utility.TempFile.RemoveOldApplicationTempFiles((path, ex) => + { + DataConnection.LogError(null, $"Failed to delete temp file: {path}", ex); + }); + + if (!commandlineOptions.TryGetValue("log-retention", out string pts)) + { + pts = DEFAULT_LOG_RETENTION; + } + + DataConnection.PurgeLogData(Library.Utility.Timeparser.ParseTimeInterval(pts, DateTime.Now, true)); + } + catch (Exception ex) + { + DataConnection.LogError(null, "Failed during temp file cleanup", ex); + } + }; + + try + { + PurgeTempFilesTimer = + new System.Threading.Timer(purgeTempFilesCallback, null, TimeSpan.FromHours(1), TimeSpan.FromDays(1)); + } + catch (ArgumentOutOfRangeException) + { + //Bugfix for older Mono, slightly more resources used to avoid large values in the period field + PurgeTempFilesTimer = + new System.Threading.Timer(purgeTempFilesCallback, null, TimeSpan.FromHours(1), TimeSpan.FromHours(1)); + } + } + private static void AdjustApplicationSettings(Dictionary commandlineOptions) { if (commandlineOptions.ContainsKey("webservice-password"))