From 5b9e294dec31b2ae620aa2eee4e2a8a491713dca Mon Sep 17 00:00:00 2001 From: Kenneth Skovhede Date: Mon, 29 Apr 2024 17:58:00 +0200 Subject: [PATCH] Revisiting the machine-id concept. Removed the text from the update folder as it is no longer being used. Added a new `machineid.txt` file that controls the machine-id. De-coupled the machine-id from the installationid as the latter is used for usage reporting, and should not be connected to the machine-id --- .../AutoUpdater/AutoUpdateFolderReadme.txt | 15 ----- .../AutoUpdateMachineIdTemplate.txt | 4 ++ .../Library/AutoUpdater/AutoUpdateSettings.cs | 31 ++++----- .../Duplicati.Library.AutoUpdater.csproj | 2 +- .../Library/AutoUpdater/UpdaterManager.cs | 64 +++++++++++++++---- .../Library/Modules/Builtin/ReportHelper.cs | 4 +- 6 files changed, 72 insertions(+), 48 deletions(-) delete mode 100644 Duplicati/Library/AutoUpdater/AutoUpdateFolderReadme.txt create mode 100644 Duplicati/Library/AutoUpdater/AutoUpdateMachineIdTemplate.txt diff --git a/Duplicati/Library/AutoUpdater/AutoUpdateFolderReadme.txt b/Duplicati/Library/AutoUpdater/AutoUpdateFolderReadme.txt deleted file mode 100644 index fbab8836c..000000000 --- a/Duplicati/Library/AutoUpdater/AutoUpdateFolderReadme.txt +++ /dev/null @@ -1,15 +0,0 @@ -This folder is used to store updates. - -Each folder is checked before launch to ensure the integrity of the application. - -DO NOT MODIFY THE FOLDERS! - -This means: -DO NOT CHANGE ANY FILES! -DO NOT DELETE ANY FILES! -DO NOT ADD ANY FILES! - -If anything is changed, the updates will be rejected, and a previous version is used. - -If you need to modify something, use the folder where the application is installed. - diff --git a/Duplicati/Library/AutoUpdater/AutoUpdateMachineIdTemplate.txt b/Duplicati/Library/AutoUpdater/AutoUpdateMachineIdTemplate.txt new file mode 100644 index 000000000..ba37b38dd --- /dev/null +++ b/Duplicati/Library/AutoUpdater/AutoUpdateMachineIdTemplate.txt @@ -0,0 +1,4 @@ +{0} + +This file is used to assign a unique ID to the machine, and used to identify the machine in reports. +The first line in the file is used as an ID, but can be changed with --machine-id for a single or multiple runs. \ No newline at end of file diff --git a/Duplicati/Library/AutoUpdater/AutoUpdateSettings.cs b/Duplicati/Library/AutoUpdater/AutoUpdateSettings.cs index 9bb922551..c17e47be6 100644 --- a/Duplicati/Library/AutoUpdater/AutoUpdateSettings.cs +++ b/Duplicati/Library/AutoUpdater/AutoUpdateSettings.cs @@ -33,13 +33,12 @@ namespace Duplicati.Library.AutoUpdater private const string UPDATE_URL = "AutoUpdateURL.txt"; private const string UPDATE_KEY = "AutoUpdateSignKey.txt"; private const string UPDATE_CHANNEL = "AutoUpdateBuildChannel.txt"; - private const string UPDATE_README = "AutoUpdateFolderReadme.txt"; private const string UPDATE_INSTALL_FILE = "AutoUpdateInstallIDTemplate.txt"; + private const string UPDATE_MACHINE_FILE = "AutoUpdateMachineIDTemplate.txt"; private const string OEM_APP_NAME = "oem-app-name.txt"; private const string OEM_UPDATE_URL = "oem-update-url.txt"; private const string OEM_UPDATE_KEY = "oem-update-key.txt"; - private const string OEM_UPDATE_README = "oem-update-readme.txt"; private const string OEM_UPDATE_INSTALL_FILE = "oem-update-installid.txt"; public const string UPDATEURL_ENVNAME_TEMPLATE = "AUTOUPDATER_{0}_URLS"; @@ -49,13 +48,13 @@ namespace Duplicati.Library.AutoUpdater internal const string MATCH_UPDATE_URL_CHANNEL_GROUP = "channel"; internal const string MATCH_UPDATE_URL_FILENAME_GROUP = "filename"; - internal static readonly Regex MATCH_AUTOUPDATE_URL = + internal static readonly Regex MATCH_AUTOUPDATE_URL = new Regex(string.Format( - "(?<{0}>.+)(?<{1}>{3})(?<{2}>/([^/]+).manifest)", + "(?<{0}>.+)(?<{1}>{3})(?<{2}>/([^/]+).manifest)", MATCH_UPDATE_URL_PREFIX_GROUP, MATCH_UPDATE_URL_CHANNEL_GROUP, MATCH_UPDATE_URL_FILENAME_GROUP, - string.Join("|", Enum.GetNames(typeof(ReleaseType)).Union(new [] { "preview", "rene" }) )), RegexOptions.Compiled | RegexOptions.IgnoreCase); + string.Join("|", Enum.GetNames(typeof(ReleaseType)).Union(new[] { "preview" }))), RegexOptions.Compiled | RegexOptions.IgnoreCase); static AutoUpdateSettings() @@ -63,8 +62,8 @@ namespace Duplicati.Library.AutoUpdater ReadResourceText(APP_NAME, OEM_APP_NAME); ReadResourceText(UPDATE_URL, OEM_UPDATE_URL); ReadResourceText(UPDATE_KEY, OEM_UPDATE_KEY); - ReadResourceText(UPDATE_README, OEM_UPDATE_README); ReadResourceText(UPDATE_INSTALL_FILE, OEM_UPDATE_INSTALL_FILE); + ReadResourceText(UPDATE_MACHINE_FILE, null); ReadResourceText(UPDATE_CHANNEL, null); } @@ -104,8 +103,8 @@ namespace Duplicati.Library.AutoUpdater public static string[] URLs { - get - { + get + { if (UsesAlternateURLs) return Environment.GetEnvironmentVariable(string.Format(UPDATEURL_ENVNAME_TEMPLATE, AppName)).Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries); else @@ -115,7 +114,7 @@ namespace Duplicati.Library.AutoUpdater public static bool UsesAlternateURLs { - get + get { return !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable(string.Format(UPDATEURL_ENVNAME_TEMPLATE, AppName))); } @@ -129,14 +128,14 @@ namespace Duplicati.Library.AutoUpdater if (UsesAlternateURLs && string.IsNullOrWhiteSpace(channelstring)) { - foreach(var url in URLs) + foreach (var url in URLs) { var match = AutoUpdateSettings.MATCH_AUTOUPDATE_URL.Match(url); if (match.Success) { channelstring = match.Groups[AutoUpdateSettings.MATCH_UPDATE_URL_CHANNEL_GROUP].Value; break; - } + } } } @@ -148,7 +147,7 @@ namespace Duplicati.Library.AutoUpdater channelstring = ReleaseType.Experimental.ToString(); if (string.Equals(channelstring, "rene", StringComparison.OrdinalIgnoreCase)) channelstring = ReleaseType.Canary.ToString(); - + ReleaseType rt; if (!Enum.TryParse(channelstring, true, out rt)) rt = ReleaseType.Stable; @@ -167,16 +166,14 @@ namespace Duplicati.Library.AutoUpdater get { return ReadResourceText(UPDATE_CHANNEL, null); } } - public static string UpdateFolderReadme - { - get { return ReadResourceText(UPDATE_README, OEM_UPDATE_README); } - } - public static string UpdateInstallFileText { get { return string.Format(ReadResourceText(UPDATE_INSTALL_FILE, OEM_UPDATE_INSTALL_FILE), Guid.NewGuid().ToString("N")); } } + public static string UpdateMachineFileText(string machineid) + => string.Format(ReadResourceText(UPDATE_MACHINE_FILE, "{0}"), string.IsNullOrWhiteSpace(machineid) ? Guid.NewGuid().ToString("N") : machineid); + public static System.Security.Cryptography.RSACryptoServiceProvider[] SignKeys { get diff --git a/Duplicati/Library/AutoUpdater/Duplicati.Library.AutoUpdater.csproj b/Duplicati/Library/AutoUpdater/Duplicati.Library.AutoUpdater.csproj index 3c7e2234b..e6d1b0110 100644 --- a/Duplicati/Library/AutoUpdater/Duplicati.Library.AutoUpdater.csproj +++ b/Duplicati/Library/AutoUpdater/Duplicati.Library.AutoUpdater.csproj @@ -19,8 +19,8 @@ - + diff --git a/Duplicati/Library/AutoUpdater/UpdaterManager.cs b/Duplicati/Library/AutoUpdater/UpdaterManager.cs index e277fc1e4..0fabe3832 100644 --- a/Duplicati/Library/AutoUpdater/UpdaterManager.cs +++ b/Duplicati/Library/AutoUpdater/UpdaterManager.cs @@ -91,14 +91,15 @@ namespace Duplicati.Library.AutoUpdater /// private const string PACKAGE_TYPE_FILE = "package_type_id.txt"; /// - /// The README file stored in the folder, explaining what the folder is for - /// - private const string README_FILE = "README.txt"; - /// /// The installation ID filename stored in /// private const string INSTALL_FILE = "installation.txt"; + /// + /// The machine ID filename stored in + /// + private const string MACHINE_FILE = "machineid.txt"; + /// /// Gets the last version found from an update /// @@ -119,19 +120,31 @@ namespace Duplicati.Library.AutoUpdater var candidates = new List(); if (Platform.IsClientWindows) { - candidates.Add(System.IO.Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), APPNAME, "updates")); - candidates.Add(System.IO.Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), APPNAME, "updates")); + candidates.Add(System.IO.Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), APPNAME)); + candidates.Add(System.IO.Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), APPNAME)); } else { - if (Platform.IsClientOSX) - candidates.Add(System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "Library", "Application Support", APPNAME, "updates")); - - candidates.Add(System.IO.Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), APPNAME, "updates")); + candidates.Add(System.IO.Path.Combine(System.Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), APPNAME)); } + if (Platform.IsClientOSX) + candidates.Add(System.IO.Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.Personal), "Library", "Application Support", APPNAME)); + // Find the first writeable directory in the list UPDATEDIR = candidates.FirstOrDefault(p => !string.IsNullOrWhiteSpace(p) && System.IO.Directory.Exists(p) && TestDirectoryIsWriteable(p)); + + // Try to create a writeable folder, if none is found + if (string.IsNullOrWhiteSpace(UPDATEDIR)) + UPDATEDIR = candidates.Where(p => !string.IsNullOrWhiteSpace(p) && !System.IO.Directory.Exists(p)) + .Select(p => + { + try { System.IO.Directory.CreateDirectory(p); } + catch { } + return p; + }) + .Where(p => System.IO.Directory.Exists(p) && TestDirectoryIsWriteable(p)) + .FirstOrDefault(); } else { @@ -141,10 +154,18 @@ namespace Duplicati.Library.AutoUpdater if (!string.IsNullOrWhiteSpace(UPDATEDIR)) { - if (!System.IO.File.Exists(System.IO.Path.Combine(UPDATEDIR, README_FILE))) - System.IO.File.WriteAllText(System.IO.Path.Combine(UPDATEDIR, README_FILE), AutoUpdateSettings.UpdateFolderReadme); if (!System.IO.File.Exists(System.IO.Path.Combine(UPDATEDIR, INSTALL_FILE))) - System.IO.File.WriteAllText(System.IO.Path.Combine(UPDATEDIR, INSTALL_FILE), AutoUpdateSettings.UpdateInstallFileText); + { + // In case there was already a machine id file, copy it to the new location + if (System.IO.File.Exists(System.IO.Path.Combine(UPDATEDIR, "updates", INSTALL_FILE))) + System.IO.File.Copy(System.IO.Path.Combine(UPDATEDIR, "updates", INSTALL_FILE), System.IO.Path.Combine(UPDATEDIR, INSTALL_FILE), true); + else + System.IO.File.WriteAllText(System.IO.Path.Combine(UPDATEDIR, INSTALL_FILE), AutoUpdateSettings.UpdateInstallFileText); + } + + if (!System.IO.File.Exists(System.IO.Path.Combine(UPDATEDIR, MACHINE_FILE))) + System.IO.File.WriteAllText(System.IO.Path.Combine(UPDATEDIR, MACHINE_FILE), AutoUpdateSettings.UpdateMachineFileText(InstallID)); + } // Attempt to read the installed manifest file @@ -226,6 +247,23 @@ namespace Duplicati.Library.AutoUpdater } } + /// + /// The unique machine ID + /// + public static string MachineID + { + get + { + string machinedId = null; + try { machinedId = System.IO.File.ReadAllLines(System.IO.Path.Combine(UPDATEDIR, MACHINE_FILE)).FirstOrDefault(x => !string.IsNullOrWhiteSpace(x))?.Trim() ?? ""; } + catch { } + + return string.IsNullOrWhiteSpace(machinedId) + ? InstallID + : machinedId; + } + } + /// /// The package type ID /// diff --git a/Duplicati/Library/Modules/Builtin/ReportHelper.cs b/Duplicati/Library/Modules/Builtin/ReportHelper.cs index aed7d3299..afe25d178 100644 --- a/Duplicati/Library/Modules/Builtin/ReportHelper.cs +++ b/Duplicati/Library/Modules/Builtin/ReportHelper.cs @@ -346,7 +346,7 @@ namespace Duplicati.Library.Modules.Builtin if (input.IndexOf("%machine-id%", StringComparison.OrdinalIgnoreCase) >= 0) { if (!m_options.ContainsKey("machine-id")) - extra["machine-id"] = Library.AutoUpdater.UpdaterManager.InstallID; + extra["machine-id"] = Library.AutoUpdater.UpdaterManager.MachineID; } if (input.IndexOf("%backup-id%", StringComparison.OrdinalIgnoreCase) >= 0) @@ -394,7 +394,7 @@ namespace Duplicati.Library.Modules.Builtin input = Regex.Replace(input, "\\%backup-id\\%", Library.Utility.Utility.ByteArrayAsHexString(Library.Utility.Utility.RepeatedHashWithSalt(m_remoteurl, SALT)), RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); if (!m_options.ContainsKey("machine-id")) - input = Regex.Replace(input, "\\%machine-id\\%", Library.AutoUpdater.UpdaterManager.InstallID, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); + input = Regex.Replace(input, "\\%machine-id\\%", Library.AutoUpdater.UpdaterManager.MachineID, RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); input = Regex.Replace(input, "\\%[^\\%]+\\%", ""); return input;