diff --git a/Duplicati/GUI/Duplicati.GUI.TrayIcon/HttpServerConnection.cs b/Duplicati/GUI/Duplicati.GUI.TrayIcon/HttpServerConnection.cs index 6c79f9cd4..507723d36 100644 --- a/Duplicati/GUI/Duplicati.GUI.TrayIcon/HttpServerConnection.cs +++ b/Duplicati/GUI/Duplicati.GUI.TrayIcon/HttpServerConnection.cs @@ -263,7 +263,7 @@ namespace Duplicati.GUI.TrayIcon { var settings = await PerformRequestAsync>("GET", "/serversettings", null, null) .ConfigureAwait(false); - if (settings != null && settings.TryGetValue("disable-tray-icon-login", out var str)) + if (settings != null && settings.TryGetValue(Server.Database.ServerSettings.CONST.DISABLE_TRAY_ICON_LOGIN, out var str)) m_disableTrayIconLogin = Library.Utility.Utility.ParseBool(str, false); } diff --git a/Duplicati/Library/AutoUpdater/DataFolderManager.cs b/Duplicati/Library/AutoUpdater/DataFolderManager.cs index 1b4f1c024..b27223f20 100644 --- a/Duplicati/Library/AutoUpdater/DataFolderManager.cs +++ b/Duplicati/Library/AutoUpdater/DataFolderManager.cs @@ -245,7 +245,12 @@ public static class DataFolderManager /// private static readonly Lazy _installID = new(() => { - try { return File.ReadAllLines(Path.Combine(GetDataFolder(DataFolderManager.AccessMode.ProbeOnly), INSTALL_FILE)).FirstOrDefault(x => !string.IsNullOrWhiteSpace(x))?.Trim() ?? ""; } + try + { + var path = Path.Combine(GetDataFolder(DataFolderManager.AccessMode.ProbeOnly), INSTALL_FILE); + if (File.Exists(path)) + return File.ReadAllLines(path).FirstOrDefault(x => !string.IsNullOrWhiteSpace(x))?.Trim() ?? ""; + } catch { } return string.Empty; diff --git a/Duplicati/Library/Backend/Storj/StorjBackend.cs b/Duplicati/Library/Backend/Storj/StorjBackend.cs index 0f8539c40..e75d35982 100644 --- a/Duplicati/Library/Backend/Storj/StorjBackend.cs +++ b/Duplicati/Library/Backend/Storj/StorjBackend.cs @@ -22,6 +22,7 @@ using Duplicati.Library.Common.IO; using Duplicati.Library.Interface; using Duplicati.Library.Utility; using Duplicati.Library.Utility.Options; +using System.Globalization; using System.Runtime.CompilerServices; using uplink.NET.Interfaces; using uplink.NET.Models; @@ -252,8 +253,8 @@ namespace Duplicati.Library.Backend.Storj { var bucket = await GetBucketAsync(cancelToken).ConfigureAwait(false); var custom = new CustomMetadata(); - custom.Entries.Add(new CustomMetadataEntry { Key = StorjFile.STORJ_LAST_ACCESS, Value = DateTime.Now.ToUniversalTime().ToString("O") }); - custom.Entries.Add(new CustomMetadataEntry { Key = StorjFile.STORJ_LAST_MODIFICATION, Value = DateTime.Now.ToUniversalTime().ToString("O") }); + custom.Entries.Add(new CustomMetadataEntry { Key = StorjFile.STORJ_LAST_ACCESS, Value = DateTime.Now.ToUniversalTime().ToString("O", CultureInfo.InvariantCulture) }); + custom.Entries.Add(new CustomMetadataEntry { Key = StorjFile.STORJ_LAST_MODIFICATION, Value = DateTime.Now.ToUniversalTime().ToString("O", CultureInfo.InvariantCulture) }); using var ts = stream.ObserveReadTimeout(_timeouts.ReadWriteTimeout, false); var upload = await Utility.Utility.WithTimeout(_timeouts.ShortTimeout, cancelToken, _ => _objectService.UploadObjectAsync(bucket, GetBasePath() + remotename, new UploadOptions(), ts, custom, false)).ConfigureAwait(false); await upload.StartUploadAsync().ConfigureAwait(false); diff --git a/Duplicati/Library/Main/Volumes/IndexVolumeWriter.cs b/Duplicati/Library/Main/Volumes/IndexVolumeWriter.cs index 47fd82175..ec94f0e9c 100644 --- a/Duplicati/Library/Main/Volumes/IndexVolumeWriter.cs +++ b/Duplicati/Library/Main/Volumes/IndexVolumeWriter.cs @@ -33,6 +33,7 @@ namespace Duplicati.Library.Main.Volumes private JsonWriter m_writer = null; #if DEBUG private readonly HashSet m_knownBlocklisthashes = new(); + private bool m_disposed = false; #endif private long m_volumes = 0; @@ -52,6 +53,10 @@ namespace Duplicati.Library.Main.Volumes public void StartVolume(string filename) { +#if DEBUG + if (m_disposed) + throw new ObjectDisposedException("IndexVolumeWriter"); +#endif if (m_writer != null || m_streamwriter != null) throw new InvalidOperationException("Previous volume not finished, call FinishVolume before starting a new volume"); @@ -76,11 +81,24 @@ namespace Duplicati.Library.Main.Volumes public void FinishVolume(string volumehash, long volumesize) { +#if DEBUG + if (m_disposed) + throw new ObjectDisposedException("IndexVolumeWriter"); +#endif + + // If StartVolume was never called, m_writer will be null + // This can happen if the volume was created but never used + if (m_writer == null || m_streamwriter == null) + return; + m_writer.WriteEndArray(); - m_writer.WritePropertyName("volumehash"); - m_writer.WriteValue(volumehash); - m_writer.WritePropertyName("volumesize"); - m_writer.WriteValue(volumesize); + if (!string.IsNullOrWhiteSpace(volumehash)) + { + m_writer.WritePropertyName("volumehash"); + m_writer.WriteValue(volumehash); + m_writer.WritePropertyName("volumesize"); + m_writer.WriteValue(volumesize); + } m_writer.WriteEndObject(); try { m_writer.Close(); } @@ -92,6 +110,8 @@ namespace Duplicati.Library.Main.Volumes public void WriteBlocklist(string hash, byte[] data, int offset, int size) { #if DEBUG + if (m_disposed) + throw new ObjectDisposedException("IndexVolumeWriter"); // Nothing breaks with duplicates, but it should not be there if (m_knownBlocklisthashes.Contains(hash)) throw new InvalidOperationException($"Attempted to write a blocklist with hash {hash} more than once"); @@ -114,6 +134,8 @@ namespace Duplicati.Library.Main.Volumes public void WriteBlocklist(string hash, Stream source) { #if DEBUG + if (m_disposed) + throw new ObjectDisposedException("IndexVolumeWriter"); // Nothing breaks with duplicates, but it should not be there if (m_knownBlocklisthashes.Contains(hash)) throw new InvalidOperationException($"Attempted to write a blocklist with hash {hash} more than once"); @@ -132,6 +154,10 @@ namespace Duplicati.Library.Main.Volumes public void CopyFrom(IndexVolumeReader rd, Func filename_mapping) { +#if DEBUG + if (m_disposed) + throw new ObjectDisposedException("IndexVolumeWriter"); +#endif foreach (var n in rd.Volumes) { this.StartVolume(filename_mapping(n.Filename)); @@ -147,6 +173,9 @@ namespace Duplicati.Library.Main.Volumes public override void Dispose() { +#if DEBUG + m_disposed = true; +#endif if (m_writer != null || m_streamwriter != null) throw new InvalidOperationException("Attempted to dispose an index volume that was being written"); base.Dispose(); diff --git a/Duplicati/Library/RemoteControl/RegisterForRemote.cs b/Duplicati/Library/RemoteControl/RegisterForRemote.cs index 8abaee2a7..4e2004449 100644 --- a/Duplicati/Library/RemoteControl/RegisterForRemote.cs +++ b/Duplicati/Library/RemoteControl/RegisterForRemote.cs @@ -19,6 +19,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER // DEALINGS IN THE SOFTWARE. +using System.Globalization; using System.Net.Http.Json; using System.Text.Json; using Duplicati.Library.Logging; @@ -237,7 +238,7 @@ public class RegisterForRemote : IDisposable { "machineId", AutoUpdater.DataFolderManager.MachineID }, { "machineName", AutoUpdater.DataFolderManager.MachineName }, { "installId", AutoUpdater.DataFolderManager.InstallID }, - { "localTime", DateTimeOffset.Now.ToString("o") }, + { "localTime", DateTimeOffset.Now.ToString("o", CultureInfo.InvariantCulture) }, { "version", AutoUpdater.UpdaterManager.SelfVersion?.Version }, { "packageTypeId", AutoUpdater.UpdaterManager.PackageTypeId }, { "operatingSystem", AutoUpdater.UpdaterManager.OperatingSystemName } diff --git a/Duplicati/WebserverCore/Endpoints/V1/FilesystemPlugins/MSSQL.cs b/Duplicati/WebserverCore/Endpoints/V1/FilesystemPlugins/MSSQL.cs index 9cc7f899f..e10a07781 100644 --- a/Duplicati/WebserverCore/Endpoints/V1/FilesystemPlugins/MSSQL.cs +++ b/Duplicati/WebserverCore/Endpoints/V1/FilesystemPlugins/MSSQL.cs @@ -76,7 +76,7 @@ public class MSSQL : IFilesystemPlugin var servers = serverNames.Select(x => new Dto.TreeNodeDto { - id = string.Join(Path.DirectorySeparatorChar, RootName, x) + Path.DirectorySeparatorChar, + id = Util.AppendDirSeparator(string.Join(Path.DirectorySeparatorChar, RootName, x)), text = x, cls = "folder", iconCls = "x-tree-icon-mssql", diff --git a/Duplicati/WebserverCore/Notifications/WebsocketAccessor.cs b/Duplicati/WebserverCore/Notifications/WebsocketAccessor.cs index 2c003dad9..93c1e1eae 100644 --- a/Duplicati/WebserverCore/Notifications/WebsocketAccessor.cs +++ b/Duplicati/WebserverCore/Notifications/WebsocketAccessor.cs @@ -262,7 +262,7 @@ public class WebsocketAccessor : IWebsocketAccessor } catch (Exception ex) { - Log.WriteErrorMessage(LOGTAG, "WebsockSendFailure", ex, $"Failed to send websocket message: {ex.Message}"); + Log.WriteErrorMessage(LOGTAG, "WebsockSendFailure", ex, $"Failed to send websocket message"); } } @@ -277,7 +277,7 @@ public class WebsocketAccessor : IWebsocketAccessor } catch (Exception ex) { - Log.WriteErrorMessage(LOGTAG, "WebsocketDeserializationError", ex, $"Failed to deserialize websocket message: {ex.Message}"); + Log.WriteErrorMessage(LOGTAG, "WebsocketDeserializationError", ex, $"Failed to deserialize websocket message"); await SendRequestReply(socket, "", null, "Invalid message format", false); return; } diff --git a/Duplicati/WebserverCore/Notifications/WebsocketAuthenticator.cs b/Duplicati/WebserverCore/Notifications/WebsocketAuthenticator.cs index 03bc58fcc..8c46414de 100644 --- a/Duplicati/WebserverCore/Notifications/WebsocketAuthenticator.cs +++ b/Duplicati/WebserverCore/Notifications/WebsocketAuthenticator.cs @@ -123,7 +123,7 @@ public class WebsocketAuthenticator( } catch (Exception ex) { - Log.WriteVerboseMessage(LOGTAG, "WebsocketDeserializationError", ex, $"Failed to deserialize websocket message: {ex.Message}"); + Log.WriteVerboseMessage(LOGTAG, "WebsocketDeserializationError", ex, $"Failed to deserialize websocket message"); } if (message == null)