diff --git a/Duplicati/Library/Backend/Filen/FilenBackend.cs b/Duplicati/Library/Backend/Filen/FilenBackend.cs
index 7a7915c34..3f953a754 100644
--- a/Duplicati/Library/Backend/Filen/FilenBackend.cs
+++ b/Duplicati/Library/Backend/Filen/FilenBackend.cs
@@ -282,6 +282,6 @@ public class FilenBackend : IStreamingBackend, IRenameEnabledBackend
if (fileEntry == null)
throw new FileMissingException($"File '{oldname}' not found.");
- await Utility.Utility.WithTimeout(_timeout.ShortTimeout, cancellationToken, ct => client.RenameFileAsync(fileEntry.Uuid, newname, ct)).ConfigureAwait(false);
+ await Utility.Utility.WithTimeout(_timeout.ShortTimeout, cancellationToken, ct => client.RenameFileAsync(fileEntry, newname, ct)).ConfigureAwait(false);
}
}
diff --git a/Duplicati/Library/Backend/Filen/FilenClient.cs b/Duplicati/Library/Backend/Filen/FilenClient.cs
index 28de2c4db..21b21e636 100644
--- a/Duplicati/Library/Backend/Filen/FilenClient.cs
+++ b/Duplicati/Library/Backend/Filen/FilenClient.cs
@@ -22,7 +22,6 @@
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Runtime.CompilerServices;
-using System.Text;
using System.Text.Json;
using Duplicati.Library.Interface;
@@ -415,12 +414,14 @@ public class FilenClient : IDisposable
Name = decryptedName.Name,
IsFolder = true,
LastModified = DateTimeOffset.FromUnixTimeMilliseconds(folder.LastModified).UtcDateTime,
+ CreatedTimestamp = new DateTime(0),
Size = 0,
Region = string.Empty,
Bucket = string.Empty,
Chunks = 0,
FileKey = string.Empty,
- Version = 0
+ Version = 0,
+ MimeType = string.Empty,
};
}
@@ -447,11 +448,13 @@ public class FilenClient : IDisposable
IsFolder = false,
Size = file.Size,
LastModified = DateTimeOffset.FromUnixTimeMilliseconds(fileInfo.LastModified).UtcDateTime,
+ CreatedTimestamp = DateTimeOffset.FromUnixTimeMilliseconds(fileInfo.Create).UtcDateTime,
Region = file.Region,
Bucket = file.Bucket,
Chunks = file.Chunks,
FileKey = fileInfo.Key,
- Version = file.Version
+ Version = file.Version,
+ MimeType = fileInfo.Mime,
};
}
}
@@ -736,27 +739,58 @@ public class FilenClient : IDisposable
_httpClient.Dispose();
}
+ ///
+ /// Helper for mapping DateTime to epoch milliseconds
+ ///
+ /// The date time to map
+ /// The epoch milliseconds
+ private static long MapDateTimeToEpochMs(DateTime dateTime)
+ {
+ if (dateTime.Ticks == 0)
+ return 0;
+ return (long)(dateTime - DateTime.UnixEpoch).TotalMilliseconds;
+ }
+
///
/// Renames a file in the Filen API
///
- /// The UUID of the file to rename
+ /// The file entry to rename
/// The new name of the file
/// The cancellation token to use for the operation
/// A task that completes when the file is renamed
- public async Task RenameFileAsync(string fileUuid, string newName, CancellationToken cancellationToken)
+ public async Task RenameFileAsync(FilenFileEntry fileEntry, string newName, CancellationToken cancellationToken)
{
- var encryptedName = _authResult.EncryptMetadata(JsonSerializer.Serialize(new NameEntry() { Name = newName }));
+ var fileKey = DerivedKey.Create(fileEntry.FileKey);
+ var encryptedName = fileKey.EncryptMetadata(newName);
+ var nameHashed = FilenCrypto.HashFn(newName);
+
+ var metadata = _authResult.EncryptMetadata(JsonSerializer.Serialize(new FileInfoMetadata
+ {
+ Name = newName,
+ Size = fileEntry.Size,
+ Mime = fileEntry.MimeType,
+ Key = fileEntry.FileKey,
+ LastModified = MapDateTimeToEpochMs(fileEntry.LastModified),
+ Create = MapDateTimeToEpochMs(fileEntry.CreatedTimestamp),
+ }));
+
var url = $"{_baseUrl}/v3/file/rename";
using var request = new HttpRequestMessage(HttpMethod.Post, url);
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _authResult.ApiKey);
- request.Content = JsonContent.Create(new { uuid = fileUuid, name = encryptedName });
+ request.Content = JsonContent.Create(new
+ {
+ uuid = fileEntry.Uuid,
+ name = encryptedName,
+ nameHashed,
+ metadata
+ });
var response = await _httpClient.SendAsync(request, cancellationToken).ConfigureAwait(false);
await ExtractDataFromResponse(response, cancellationToken).ConfigureAwait(false);
// Update cache
- var key = _cachedFiles.FirstOrDefault(kv => kv.Value.Uuid == fileUuid).Key;
+ var key = _cachedFiles.FirstOrDefault(kv => kv.Value.Uuid == fileEntry.Uuid).Key;
if (!string.IsNullOrWhiteSpace(key))
_cachedFiles.Remove(key);
}
diff --git a/Duplicati/Library/Backend/Filen/FilenFileEntry.cs b/Duplicati/Library/Backend/Filen/FilenFileEntry.cs
index 32dd2aff3..b1ab2dbd1 100644
--- a/Duplicati/Library/Backend/Filen/FilenFileEntry.cs
+++ b/Duplicati/Library/Backend/Filen/FilenFileEntry.cs
@@ -47,6 +47,10 @@ public sealed record FilenFileEntry
///
public required DateTime LastModified { get; set; }
///
+ /// The creation date of the file
+ ///
+ public required DateTime CreatedTimestamp { get; set; }
+ ///
/// The region of the file
///
public required string Region { get; set; }
@@ -66,5 +70,9 @@ public sealed record FilenFileEntry
/// The encryption version
///
public required int Version;
+ ///
+ /// The MIME type of the file
+ ///
+ public required string MimeType { get; set; }
}