Merge branch 'master' into feature/add-office-365-backup

This commit is contained in:
Kenneth Skovhede
2026-01-27 20:44:45 +01:00
committed by GitHub
8 changed files with 49 additions and 13 deletions
@@ -263,7 +263,7 @@ namespace Duplicati.GUI.TrayIcon
{
var settings = await PerformRequestAsync<Dictionary<string, string>>("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);
}
@@ -245,7 +245,12 @@ public static class DataFolderManager
/// </summary>
private static readonly Lazy<string> _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;
@@ -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);
@@ -33,6 +33,7 @@ namespace Duplicati.Library.Main.Volumes
private JsonWriter m_writer = null;
#if DEBUG
private readonly HashSet<string> 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<string, string> 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();
@@ -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 }
@@ -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",
@@ -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<object>(socket, "", null, "Invalid message format", false);
return;
}
@@ -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)