Skip to content

Fix: Fixed issue where it didn't work to open password protected archives #12708

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 41 commits into from
Jul 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
1e9c87f
[WIP]
gave92 Jun 11, 2023
c3149bb
[WIP]
gave92 Jun 11, 2023
64562cc
Remember zip pw
gave92 Jun 11, 2023
b0939e2
Show password box only
gave92 Jun 13, 2023
7461df3
Store ftp creds
gave92 Jun 13, 2023
aee56ff
[WIP]
gave92 Jun 13, 2023
204d616
Ask for pw when extracting files
gave92 Jun 15, 2023
3f2b360
Merge branch 'main' of https://github.com/files-community/Files into …
gave92 Jun 15, 2023
59e7b5a
Fix formatting
gave92 Jun 15, 2023
5487c86
[WIP] Revert and switch to PasswordRequested event
gave92 Jun 15, 2023
7a6f0a6
[WIP]
gave92 Jun 20, 2023
1869cc3
[WIP]
gave92 Jun 20, 2023
6716807
[WIP]
gave92 Jun 21, 2023
c63d4b7
Fix layout mode rename crash
gave92 Jun 21, 2023
05d6d4c
Tasks start immediately, use Func<Task>
gave92 Jun 21, 2023
de88337
Merge branch 'main' of https://github.com/files-community/Files into …
gave92 Jun 21, 2023
a64646a
Fix: missed unsubscribe event
gave92 Jun 21, 2023
5b26ef8
Pass password event from parent to items
gave92 Jun 21, 2023
63fa664
Delete file if wrong pw
gave92 Jun 22, 2023
6b4f7ea
Revert "Fix layout mode rename crash"
gave92 Jun 22, 2023
dfcb2fc
Merge branch 'main' of https://github.com/files-community/Files into …
gave92 Jun 22, 2023
729485c
Do not req pw when navigating up
gave92 Jun 24, 2023
98bca3a
Fix opening zip files
gave92 Jun 24, 2023
ba76e45
Fix browsing FTP
gave92 Jun 24, 2023
cbae163
Merge branch 'main' into issue_12105
gave92 Jun 24, 2023
2eac5b8
Skip win32 for ftp
gave92 Jun 24, 2023
af36369
Merge branch 'issue_12105' of https://github.com/gave92/files-uwp int…
gave92 Jun 24, 2023
b405fd5
Update
gave92 Jun 24, 2023
b8719f5
Merge branch 'main' into issue_12105
yaira2 Jun 26, 2023
bc1c60b
Merge branch 'main' of https://github.com/files-community/Files into …
gave92 Jul 3, 2023
459090d
Merge branch 'issue_12105' of https://github.com/gave92/files-uwp int…
gave92 Jul 3, 2023
d10f5e0
Address review
gave92 Jul 3, 2023
65732d8
Reduce code duplication
gave92 Jul 4, 2023
eaa33f6
Switch to callback and default interface impl
gave92 Jul 4, 2023
32736bf
Merge branch 'main' of https://github.com/files-community/Files into …
gave92 Jul 4, 2023
69f0603
Merge branch 'main' of https://github.com/files-community/Files into …
gave92 Jul 5, 2023
1b009c4
Merge branch 'main' into issue_12105
yaira2 Jul 6, 2023
b860a8f
Merge branch 'main' of https://github.com/files-community/Files into …
gave92 Jul 7, 2023
361d6db
Merge branch 'issue_12105' of https://github.com/gave92/files-uwp int…
gave92 Jul 7, 2023
888d41d
Merge branch 'main' into issue_12105
gave92 Jul 7, 2023
6445cba
Merge branch 'main' of https://github.com/files-community/Files into …
gave92 Jul 7, 2023
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
209 changes: 52 additions & 157 deletions src/Files.App/Data/Models/ItemViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,19 @@
using Files.App.Utils.StorageItems;
using Files.App.Helpers.StorageCache;
using Files.App.Utils.Shell;
using Files.App.Storage.FtpStorage;
using Files.App.ViewModels.Previews;
using Files.Core.Services.SizeProvider;
using Files.Shared.Cloud;
using Files.Shared.EventArguments;
using Files.Shared.Services;
using FluentFTP;
using Microsoft.Extensions.Logging;
using Microsoft.UI.Xaml.Data;
using Microsoft.UI.Xaml.Media;
using Microsoft.UI.Xaml.Media.Imaging;
using System.Collections.Concurrent;
using System.IO;
using System.Net;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.Json;
using Vanara.Windows.Shell;
using Windows.Foundation;
Expand Down Expand Up @@ -144,20 +140,14 @@ public async Task SetWorkingDirectoryAsync(string? value)

WorkingDirectory = value;

string? pathRoot;
if (FtpHelpers.IsFtpPath(WorkingDirectory))
{
var rootIndex = FtpHelpers.GetRootIndex(WorkingDirectory);
pathRoot = rootIndex is -1
? WorkingDirectory
: WorkingDirectory.Substring(0, rootIndex);
}
else
string? pathRoot = null;
if (!FtpHelpers.IsFtpPath(WorkingDirectory))
{
pathRoot = Path.GetPathRoot(WorkingDirectory);
}

GitDirectory = pathRoot is null ? null : GitHelpers.GetGitRepositoryPath(WorkingDirectory, pathRoot);

OnPropertyChanged(nameof(WorkingDirectory));
}

Expand Down Expand Up @@ -1380,55 +1370,46 @@ private async Task RapidAddItemsToCollection(string? path, LibraryItem? library
var stopwatch = new Stopwatch();
stopwatch.Start();

if (FtpHelpers.IsFtpPath(path))
{
// Recycle bin and network are enumerated by the fulltrust process
PageTypeUpdated?.Invoke(this, new PageTypeUpdatedEventArgs() { IsTypeCloudDrive = false });
await EnumerateItemsFromSpecialFolderAsync(path);
}
else
var isRecycleBin = path.StartsWith(Constants.UserEnvironmentPaths.RecycleBinPath, StringComparison.Ordinal);
var enumerated = await EnumerateItemsFromStandardFolderAsync(path, addFilesCTS.Token, library);

// Hide progressbar after enumeration
IsLoadingItems = false;

switch (enumerated)
{
var isRecycleBin = path.StartsWith(Constants.UserEnvironmentPaths.RecycleBinPath, StringComparison.Ordinal);
var enumerated = await EnumerateItemsFromStandardFolderAsync(path, addFilesCTS.Token, library);
// Enumerated with FindFirstFileExFromApp
// Is folder synced to cloud storage?
case 0:
currentStorageFolder ??= await FilesystemTasks.Wrap(() => StorageFileExtensions.DangerousGetFolderWithPathFromPathAsync(path));
var syncStatus = await CheckCloudDriveSyncStatusAsync(currentStorageFolder?.Item);
PageTypeUpdated?.Invoke(this, new PageTypeUpdatedEventArgs()
{
IsTypeCloudDrive = syncStatus != CloudDriveSyncStatus.NotSynced && syncStatus != CloudDriveSyncStatus.Unknown,
IsTypeGitRepository = GitDirectory is not null
});
WatchForDirectoryChanges(path, syncStatus);
if (GitDirectory is not null)
WatchForGitChanges();
break;

// Hide progressbar after enumeration
IsLoadingItems = false;
// Enumerated with StorageFolder
case 1:
PageTypeUpdated?.Invoke(this, new PageTypeUpdatedEventArgs() { IsTypeCloudDrive = false, IsTypeRecycleBin = isRecycleBin });
currentStorageFolder ??= await FilesystemTasks.Wrap(() => StorageFileExtensions.DangerousGetFolderWithPathFromPathAsync(path));
WatchForStorageFolderChanges(currentStorageFolder?.Item);
break;

switch (enumerated)
{
// Enumerated with FindFirstFileExFromApp
// Is folder synced to cloud storage?
case 0:
currentStorageFolder ??= await FilesystemTasks.Wrap(() => StorageFileExtensions.DangerousGetFolderWithPathFromPathAsync(path));
var syncStatus = await CheckCloudDriveSyncStatusAsync(currentStorageFolder?.Item);
PageTypeUpdated?.Invoke(this, new PageTypeUpdatedEventArgs()
{
IsTypeCloudDrive = syncStatus != CloudDriveSyncStatus.NotSynced && syncStatus != CloudDriveSyncStatus.Unknown,
IsTypeGitRepository = GitDirectory is not null
});
WatchForDirectoryChanges(path, syncStatus);
if (GitDirectory is not null)
WatchForGitChanges();
break;

// Enumerated with StorageFolder
case 1:
PageTypeUpdated?.Invoke(this, new PageTypeUpdatedEventArgs() { IsTypeCloudDrive = false, IsTypeRecycleBin = isRecycleBin });
currentStorageFolder ??= await FilesystemTasks.Wrap(() => StorageFileExtensions.DangerousGetFolderWithPathFromPathAsync(path));
WatchForStorageFolderChanges(currentStorageFolder?.Item);
break;

// Watch for changes using FTP in Box Drive folder (#7428) and network drives (#5869)
case 2:
PageTypeUpdated?.Invoke(this, new PageTypeUpdatedEventArgs() { IsTypeCloudDrive = false });
WatchForWin32FolderChanges(path);
break;

// Enumeration failed
case -1:
default:
break;
}
// Watch for changes using Win32 in Box Drive folder (#7428) and network drives (#5869)
case 2:
PageTypeUpdated?.Invoke(this, new PageTypeUpdatedEventArgs() { IsTypeCloudDrive = false });
WatchForWin32FolderChanges(path);
break;

// Enumeration failed
case -1:
default:
break;
}

await GetDefaultItemIcons(folderSettings.GetIconSize());
Expand All @@ -1455,99 +1436,6 @@ public void CloseWatcher()
watcherCTS = new CancellationTokenSource();
}

public async Task EnumerateItemsFromSpecialFolderAsync(string path)
{
var isFtp = FtpHelpers.IsFtpPath(path);

CurrentFolder = new ListedItem(null!)
{
PrimaryItemAttribute = StorageItemTypes.Folder,
ItemPropertiesInitialized = true,
ItemNameRaw =
path.StartsWith(Constants.UserEnvironmentPaths.RecycleBinPath, StringComparison.OrdinalIgnoreCase) ? "RecycleBin".GetLocalizedResource() :
path.StartsWith(Constants.UserEnvironmentPaths.NetworkFolderPath, StringComparison.OrdinalIgnoreCase) ? "Network".GetLocalizedResource() :
path.StartsWith(Constants.UserEnvironmentPaths.MyComputerPath, StringComparison.OrdinalIgnoreCase) ? "ThisPC".GetLocalizedResource() :
isFtp ? "FTP" : "Unknown",
ItemDateModifiedReal = DateTimeOffset.Now, // Fake for now
ItemDateCreatedReal = DateTimeOffset.Now, // Fake for now
ItemType = "Folder".GetLocalizedResource(),
FileImage = null,
LoadFileIcon = false,
ItemPath = path,
FileSize = null,
FileSizeBytes = 0
};

if (!isFtp || !FtpHelpers.VerifyFtpPath(path))
return;

// TODO: Show invalid path dialog

using var client = new AsyncFtpClient();
client.Host = FtpHelpers.GetFtpHost(path);
client.Port = FtpHelpers.GetFtpPort(path);
client.Credentials = FtpManager.Credentials.Get(client.Host, FtpManager.Anonymous);

static async Task<FtpProfile?> WrappedAutoConnectFtpAsync(AsyncFtpClient client)
{
try
{
return await client.AutoConnect();
}
catch (FtpAuthenticationException)
{
return null;
}

throw new InvalidOperationException();
}

await Task.Run(async () =>
{
try
{
if (!client.IsConnected && await WrappedAutoConnectFtpAsync(client) is null)
{
await dispatcherQueue.EnqueueOrInvokeAsync(async () =>
{
var credentialDialogViewModel = new CredentialDialogViewModel();

if (await dialogService.ShowDialogAsync(credentialDialogViewModel) != DialogResult.Primary)
return;

// Can't do more than that to mitigate immutability of strings. Perhaps convert DisposableArray to SecureString immediately?
if (!credentialDialogViewModel.IsAnonymous)
client.Credentials = new NetworkCredential(credentialDialogViewModel.UserName, Encoding.UTF8.GetString(credentialDialogViewModel.Password));
});
}

if (!client.IsConnected && await WrappedAutoConnectFtpAsync(client) is null)
throw new InvalidOperationException();

FtpManager.Credentials[client.Host] = client.Credentials;

var sampler = new IntervalSampler(500);
var list = await client.GetListing(FtpHelpers.GetFtpPath(path));

for (var i = 0; i < list.Length; i++)
{
filesAndFolders.Add(new FtpItem(list[i], path));

if (i == list.Length - 1 || sampler.CheckNow())
{
await OrderFilesAndFoldersAsync();
await ApplyFilesAndFoldersChangesAsync();
}
}
}
catch
{
// Network issue
FtpManager.Credentials.Remove(client.Host);
}
});
}

public async Task<int> EnumerateItemsFromStandardFolderAsync(string path, CancellationToken cancellationToken, LibraryItem? library = null)
{
// Flag to use FindFirstFileExFromApp or StorageFolder enumeration - Use storage folder for Box Drive (#4629)
Expand All @@ -1557,7 +1445,8 @@ public async Task<int> EnumerateItemsFromStandardFolderAsync(string path, Cancel
!path.StartsWith(@"\\?\", StringComparison.Ordinal) &&
!path.StartsWith(@"\\SHELL\", StringComparison.Ordinal) &&
!isWslDistro;
bool enumFromStorageFolder = isBoxFolder;
bool isFtp = FtpHelpers.IsFtpPath(path);
bool enumFromStorageFolder = isBoxFolder || isFtp;

BaseStorageFolder? rootFolder = null;

Expand Down Expand Up @@ -1585,7 +1474,7 @@ public async Task<int> EnumerateItemsFromStandardFolderAsync(string path, Cancel
}
else
{
var res = await FilesystemTasks.Wrap(() => StorageFileExtensions.DangerousGetFolderWithPathFromPathAsync(path));
var res = await FilesystemTasks.Wrap(() => StorageFileExtensions.DangerousGetFolderWithPathFromPathAsync(path, workingRoot, currentStorageFolder));
if (res)
{
currentStorageFolder = res.Result;
Expand Down Expand Up @@ -1756,12 +1645,15 @@ await Task.Run(async () =>
}
}

private Task EnumFromStorageFolderAsync(string path, BaseStorageFolder? rootFolder, StorageFolderWithPath currentStorageFolder, CancellationToken cancellationToken)
private async Task EnumFromStorageFolderAsync(string path, BaseStorageFolder? rootFolder, StorageFolderWithPath currentStorageFolder, CancellationToken cancellationToken)
{
if (rootFolder is null)
return Task.CompletedTask;
return;

return Task.Run(async () =>
if (rootFolder is IPasswordProtectedItem ppis)
ppis.PasswordRequestedCallback = UIFilesystemHelpers.RequestPassword;

await Task.Run(async () =>
{
List<ListedItem> finalList = await UniversalStorageEnumerator.ListEntries(
rootFolder,
Expand All @@ -1782,6 +1674,9 @@ private Task EnumFromStorageFolderAsync(string path, BaseStorageFolder? rootFold
await OrderFilesAndFoldersAsync();
await ApplyFilesAndFoldersChangesAsync();
}, cancellationToken);

if (rootFolder is IPasswordProtectedItem ppiu)
ppiu.PasswordRequestedCallback = null;
}

private void CheckForSolutionFile()
Expand Down
2 changes: 2 additions & 0 deletions src/Files.App/Dialogs/CredentialDialog.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@
<TextBox
x:Name="UserName"
Margin="4"
x:Load="{x:Bind ViewModel.PasswordOnly, Converter={StaticResource BoolNegationConverter}, Mode=OneWay}"
IsEnabled="{x:Bind ViewModel.IsAnonymous, Mode=OneWay, Converter={StaticResource BoolNegationConverter}}"
PlaceholderText="{helpers:ResourceString Name=CredentialDialogUserName/PlaceholderText}"
Text="{x:Bind ViewModel.UserName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" />
Expand All @@ -47,6 +48,7 @@
<CheckBox
x:Name="Anonymous"
Margin="4"
x:Load="{x:Bind ViewModel.CanBeAnonymous, Mode=OneWay}"
Content="{helpers:ResourceString Name=CredentialDialogAnonymous/Content}" />
</StackPanel>
</Grid>
Expand Down
4 changes: 2 additions & 2 deletions src/Files.App/Helpers/Storage/StorageHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -144,9 +144,9 @@ await FilesystemTasks.Wrap(() => StorageFileExtensions.DangerousGetFileFromPathA
await FilesystemTasks.Wrap(() => StorageFileExtensions.DangerousGetFolderFromPathAsync(item.Path, rootItem)));
}
if (returnedItem.Result is null && item.Item is not null)
{
returnedItem = new FilesystemResult<IStorageItem>(item.Item, FileSystemStatusCode.Success);
}
if (returnedItem.Result is IPasswordProtectedItem ppid && item.Item is IPasswordProtectedItem ppis)
ppid.Credentials = ppis.Credentials;
return returnedItem;
}

Expand Down
29 changes: 29 additions & 0 deletions src/Files.App/Helpers/UI/UIFilesystemHelpers.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@

using Files.App.Dialogs;
using Files.App.Utils.StorageItems;
using Files.App.Storage.FtpStorage;
using Files.App.ViewModels.Dialogs;
using Microsoft.Extensions.Logging;
using System.Collections.Concurrent;
using System.IO;
using System.Net;
using System.Text;
using Windows.ApplicationModel.DataTransfer;
using Windows.Storage;
using Windows.System;
Expand Down Expand Up @@ -414,5 +417,31 @@ public static void UpdateShortcutItemProperties(ShortcutItem item, string target
item.WorkingDirectory = workingDir;
item.RunAsAdmin = runAsAdmin;
}

public async static Task<StorageCredential> RequestPassword(IPasswordProtectedItem sender)
{
var path = ((IStorageItem)sender).Path;
var isFtp = FtpHelpers.IsFtpPath(path);

var credentialDialogViewModel = new CredentialDialogViewModel() { CanBeAnonymous = isFtp, PasswordOnly = !isFtp };
IDialogService dialogService = Ioc.Default.GetRequiredService<IDialogService>();
var dialogResult = await App.Window.DispatcherQueue.EnqueueOrInvokeAsync(() =>
dialogService.ShowDialogAsync(credentialDialogViewModel));

if (dialogResult != DialogResult.Primary || credentialDialogViewModel.IsAnonymous)
return new();

// Can't do more than that to mitigate immutability of strings. Perhaps convert DisposableArray to SecureString immediately?
var credentials = new StorageCredential(credentialDialogViewModel.UserName, Encoding.UTF8.GetString(credentialDialogViewModel.Password));
credentialDialogViewModel.Password?.Dispose();

if (isFtp)
{
var host = FtpHelpers.GetFtpHost(path);
FtpManager.Credentials[host] = new NetworkCredential(credentials.UserName, credentials.SecurePassword);
}

return credentials;
}
}
}
Loading