Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ public interface IPasswordProtectedItem

Func<IPasswordProtectedItem, Task<StorageCredential>> PasswordRequestedCallback { get; set; }

// Called after an operation succeeds with credentials obtained from PasswordRequestedCallback
void OnCredentialsVerified() { }

async Task<TOut> RetryWithCredentialsAsync<TOut>(Func<Task<TOut>> func, Exception exception)
{
var handled = exception is SevenZipOpenFailedException szofex && szofex.Result is OperationResult.WrongPassword ||
Expand All @@ -23,7 +26,9 @@ exception is ExtractionFailedException efex && efex.Result is OperationResult.Wr

Credentials = await PasswordRequestedCallback(this);

return await func();
var result = await func();
OnCredentialsVerified();
return result;
}

async Task RetryWithCredentialsAsync(Func<Task> func, Exception exception)
Expand All @@ -38,6 +43,7 @@ exception is ExtractionFailedException efex && efex.Result is OperationResult.Wr
Credentials = await PasswordRequestedCallback(this);

await func();
OnCredentialsVerified();
}

void CopyFrom(IPasswordProtectedItem parent)
Expand Down
17 changes: 14 additions & 3 deletions src/Files.App/Utils/Storage/StorageItems/ZipStorageFile.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,17 @@ public override string DisplayType

public Func<IPasswordProtectedItem, Task<StorageCredential>> PasswordRequestedCallback { get; set; }

void IPasswordProtectedItem.OnCredentialsVerified()
{
using var password = Credentials.SecurePassword;
ZipStorageFolder.CachedCredentials[containerPath] = new StorageCredential(Credentials.UserName, password);
}

private string ArchivePassword
=> string.IsNullOrEmpty(Credentials.Password) && ZipStorageFolder.CachedCredentials.TryGetValue(containerPath, out var cached)
? cached.Password
: Credentials.Password;

public ZipStorageFile(string path, string containerPath)
{
Name = IO.Path.GetFileName(path.TrimEnd('\\', '/'));
Expand Down Expand Up @@ -329,7 +340,7 @@ public override IAsyncAction RenameAsync(string desiredName, NameCollisionOption
compressor.CustomParameters.Add("cu", "on");
compressor.SetFormatFromExistingArchive(archiveStream);
var fileName = IO.Path.GetRelativePath(containerPath, IO.Path.Combine(IO.Path.GetDirectoryName(Path), desiredName));
await compressor.ModifyArchiveAsync(archiveStream, new Dictionary<int, string>() { { index, fileName } }, Credentials.Password, ms);
await compressor.ModifyArchiveAsync(archiveStream, new Dictionary<int, string>() { { index, fileName } }, ArchivePassword, ms);
}

await using (var archiveStream = await OpenZipFileAsync(FileAccessMode.ReadWrite))
Expand Down Expand Up @@ -378,7 +389,7 @@ public override IAsyncAction DeleteAsync(StorageDeleteOption option)
SevenZipCompressor compressor = new SevenZipCompressor() { CompressionMode = CompressionMode.Append };
compressor.CustomParameters.Add("cu", "on");
compressor.SetFormatFromExistingArchive(archiveStream);
await compressor.ModifyArchiveAsync(archiveStream, new Dictionary<int, string>() { { index, null } }, Credentials.Password, ms);
await compressor.ModifyArchiveAsync(archiveStream, new Dictionary<int, string>() { { index, null } }, ArchivePassword, ms);
}
await using (var archiveStream = await OpenZipFileAsync(FileAccessMode.ReadWrite))
{
Expand Down Expand Up @@ -463,7 +474,7 @@ private IAsyncOperation<SevenZipExtractor> OpenZipFileAsync()
return AsyncInfo.Run<SevenZipExtractor>(async (cancellationToken) =>
{
var zipFile = await OpenZipFileAsync(FileAccessMode.Read);
return zipFile is not null ? new SevenZipExtractor(zipFile, Credentials.Password) : null;
return zipFile is not null ? new SevenZipExtractor(zipFile, ArchivePassword) : null;
});
}

Expand Down
24 changes: 19 additions & 5 deletions src/Files.App/Utils/Storage/StorageItems/ZipStorageFolder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,20 @@ public sealed partial class ZipStorageFolder : BaseStorageFolder, ICreateFileWit

public Func<IPasswordProtectedItem, Task<StorageCredential>> PasswordRequestedCallback { get; set; }

// Verified credentials per archive file path, shared with ZipStorageFile; SecureString-backed like FtpManager.Credentials
internal static readonly ConcurrentDictionary<string, StorageCredential> CachedCredentials = new(StringComparer.OrdinalIgnoreCase);

void IPasswordProtectedItem.OnCredentialsVerified()
{
using var password = Credentials.SecurePassword;
CachedCredentials[containerPath] = new StorageCredential(Credentials.UserName, password);
}

private string ArchivePassword
=> string.IsNullOrEmpty(Credentials.Password) && CachedCredentials.TryGetValue(containerPath, out var cached)
? cached.Password
: Credentials.Password;

public ZipStorageFolder(string path, string containerPath)
{
Name = IO.Path.GetFileName(path.TrimEnd('\\', '/'));
Expand Down Expand Up @@ -316,7 +330,7 @@ public override IAsyncOperation<BaseStorageFolder> CreateFolderAsync(string desi
compressor.CustomParameters.Add("cu", "on");
compressor.SetFormatFromExistingArchive(archiveStream);
var fileName = IO.Path.GetRelativePath(containerPath, zipDesiredName);
await compressor.CompressStreamDictionaryAsync(archiveStream, new Dictionary<string, Stream>() { { fileName, null } }, Credentials.Password, ms);
await compressor.CompressStreamDictionaryAsync(archiveStream, new Dictionary<string, Stream>() { { fileName, null } }, ArchivePassword, ms);
}
await using (var archiveStream = await OpenZipFileAsync(FileAccessMode.ReadWrite))
{
Expand Down Expand Up @@ -371,7 +385,7 @@ public override IAsyncAction RenameAsync(string desiredName, NameCollisionOption
var folderDes = IO.Path.Combine(IO.Path.GetDirectoryName(folderKey), desiredName);
var entriesMap = new Dictionary<int, string>(index.Select(x => new KeyValuePair<int, string>(x.Index,
IO.Path.Combine(folderDes, IO.Path.GetRelativePath(folderKey, x.Key)))));
await compressor.ModifyArchiveAsync(archiveStream, entriesMap, Credentials.Password, ms);
await compressor.ModifyArchiveAsync(archiveStream, entriesMap, ArchivePassword, ms);
}
await using (var archiveStream = await OpenZipFileAsync(FileAccessMode.ReadWrite))
{
Expand Down Expand Up @@ -420,7 +434,7 @@ public override IAsyncAction DeleteAsync(StorageDeleteOption option)
compressor.CustomParameters.Add("cu", "on");
compressor.SetFormatFromExistingArchive(archiveStream);
var entriesMap = new Dictionary<int, string>(index.Select(x => new KeyValuePair<int, string>(x.Index, null)));
await compressor.ModifyArchiveAsync(archiveStream, entriesMap, Credentials.Password, ms);
await compressor.ModifyArchiveAsync(archiveStream, entriesMap, ArchivePassword, ms);
}
await using (var archiveStream = await OpenZipFileAsync(FileAccessMode.ReadWrite))
{
Expand Down Expand Up @@ -581,7 +595,7 @@ private IAsyncOperation<SevenZipExtractor> OpenZipFileAsync()
return AsyncInfo.Run<SevenZipExtractor>(async (cancellationToken) =>
{
var zipFile = await OpenZipFileAsync(FileAccessMode.Read);
return zipFile is not null ? new SevenZipExtractor(zipFile, Credentials.Password) : null;
return zipFile is not null ? new SevenZipExtractor(zipFile, ArchivePassword) : null;
});
}

Expand Down Expand Up @@ -645,7 +659,7 @@ public IAsyncOperation<BaseStorageFile> CreateFileAsync(Stream contents, string
compressor.CustomParameters.Add("cu", "on");
compressor.SetFormatFromExistingArchive(archiveStream);
var fileName = IO.Path.GetRelativePath(containerPath, zipDesiredName);
await compressor.CompressStreamDictionaryAsync(archiveStream, new Dictionary<string, Stream>() { { fileName, contents } }, Credentials.Password, ms);
await compressor.CompressStreamDictionaryAsync(archiveStream, new Dictionary<string, Stream>() { { fileName, contents } }, ArchivePassword, ms);
}
await using (var archiveStream = await OpenZipFileAsync(FileAccessMode.ReadWrite))
{
Expand Down
Loading