SMB, GUI imput handler, ThreadSafe SMB
This commit is contained in:
@@ -54,7 +54,10 @@ namespace FileTime.Core.Components
|
|||||||
IItem? itemToSelect = null;
|
IItem? itemToSelect = null;
|
||||||
if (value != null)
|
if (value != null)
|
||||||
{
|
{
|
||||||
itemToSelect = (await _currentLocation.GetItems())?.FirstOrDefault(i => i.FullName == value?.FullName);
|
itemToSelect = (await _currentLocation.GetItems())?.FirstOrDefault(i =>
|
||||||
|
i.FullName == null && value?.FullName == null
|
||||||
|
? i.Name == value?.Name
|
||||||
|
: i.FullName == value?.FullName);
|
||||||
if (itemToSelect == null) throw new IndexOutOfRangeException("Provided item does not exists in the current container.");
|
if (itemToSelect == null) throw new IndexOutOfRangeException("Provided item does not exists in the current container.");
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -123,7 +126,7 @@ namespace FileTime.Core.Components
|
|||||||
|
|
||||||
private async Task HandleCurrentLocationRefresh(object? sender, AsyncEventArgs e)
|
private async Task HandleCurrentLocationRefresh(object? sender, AsyncEventArgs e)
|
||||||
{
|
{
|
||||||
var currentSelectedName = (await GetCurrentSelectedItem())?.FullName ?? (await GetItemByLastPath()).FullName;
|
var currentSelectedName = (await GetCurrentSelectedItem())?.FullName ?? (await GetItemByLastPath())?.FullName;
|
||||||
var currentLocationItems = (await (await GetCurrentLocation()).GetItems())!;
|
var currentLocationItems = (await (await GetCurrentLocation()).GetItems())!;
|
||||||
if (currentSelectedName != null)
|
if (currentSelectedName != null)
|
||||||
{
|
{
|
||||||
|
|||||||
9
src/Core/FileTime.Core/Interactions/BasicInputHandler.cs
Normal file
9
src/Core/FileTime.Core/Interactions/BasicInputHandler.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace FileTime.Core.Interactions
|
||||||
|
{
|
||||||
|
public class BasicInputHandler : IInputInterface
|
||||||
|
{
|
||||||
|
public Func<IEnumerable<InputElement>, Task<string?[]>>? InputHandler { get; set; }
|
||||||
|
public async Task<string?[]> ReadInputs(IEnumerable<InputElement> fields) =>
|
||||||
|
InputHandler != null ? await InputHandler.Invoke(fields) : throw new NotImplementedException(nameof(InputHandler) + " is not set");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ using FileTime.Avalonia.Application;
|
|||||||
using FileTime.Avalonia.Services;
|
using FileTime.Avalonia.Services;
|
||||||
using FileTime.Avalonia.ViewModels;
|
using FileTime.Avalonia.ViewModels;
|
||||||
using FileTime.Core.Command;
|
using FileTime.Core.Command;
|
||||||
|
using FileTime.Core.Interactions;
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
|
||||||
namespace FileTime.Avalonia
|
namespace FileTime.Avalonia
|
||||||
@@ -13,7 +14,8 @@ namespace FileTime.Avalonia
|
|||||||
{
|
{
|
||||||
return serviceCollection
|
return serviceCollection
|
||||||
.AddSingleton<AppState>()
|
.AddSingleton<AppState>()
|
||||||
.AddTransient<MainPageViewModel>();
|
.AddTransient<MainPageViewModel>()
|
||||||
|
.AddSingleton<IInputInterface, BasicInputHandler>();
|
||||||
}
|
}
|
||||||
internal static IServiceCollection AddServices(this IServiceCollection serviceCollection)
|
internal static IServiceCollection AddServices(this IServiceCollection serviceCollection)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -15,8 +15,9 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
{
|
{
|
||||||
[ViewModel]
|
[ViewModel]
|
||||||
[Inject(typeof(ItemNameConverterService))]
|
[Inject(typeof(ItemNameConverterService))]
|
||||||
public partial class ContainerViewModel : IItemViewModel
|
public partial class ContainerViewModel : IItemViewModel, IDisposable
|
||||||
{
|
{
|
||||||
|
private bool _disposed;
|
||||||
private bool _isRefreshing;
|
private bool _isRefreshing;
|
||||||
private bool _isInitialized;
|
private bool _isInitialized;
|
||||||
private INewItemProcessor _newItemProcessor;
|
private INewItemProcessor _newItemProcessor;
|
||||||
@@ -128,13 +129,20 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
{
|
{
|
||||||
_isRefreshing = true;
|
_isRefreshing = true;
|
||||||
|
|
||||||
var containers = (await _container.GetContainers()).Select(c => AdoptOrCreateItem(c, (c2) => new ContainerViewModel(_newItemProcessor, this, c2, ItemNameConverterService))).ToList();
|
var containers = (await _container.GetContainers())!.Select(c => AdoptOrReuseOrCreateItem(c, (c2) => new ContainerViewModel(_newItemProcessor, this, c2, ItemNameConverterService))).ToList();
|
||||||
var elements = (await _container.GetElements()).Select(e => AdoptOrCreateItem(e, (e2) => new ElementViewModel(e2, this, ItemNameConverterService))).ToList();
|
var elements = (await _container.GetElements())!.Select(e => AdoptOrReuseOrCreateItem(e, (e2) => new ElementViewModel(e2, this, ItemNameConverterService))).ToList();
|
||||||
|
|
||||||
|
var containersToRemove = _containers.Except(containers);
|
||||||
|
|
||||||
_containers.Clear();
|
_containers.Clear();
|
||||||
_elements.Clear();
|
_elements.Clear();
|
||||||
_items.Clear();
|
_items.Clear();
|
||||||
|
|
||||||
|
foreach (var containerToRemove in containersToRemove)
|
||||||
|
{
|
||||||
|
containerToRemove?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
foreach (var container in containers)
|
foreach (var container in containers)
|
||||||
{
|
{
|
||||||
if (initializeChildren) await container.Init(false);
|
if (initializeChildren) await container.Init(false);
|
||||||
@@ -161,11 +169,14 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
_isRefreshing = false;
|
_isRefreshing = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
private TResult AdoptOrCreateItem<T, TResult>(T item, Func<T, TResult> generator) where T : IItem
|
private TResult AdoptOrReuseOrCreateItem<T, TResult>(T item, Func<T, TResult> generator) where T : class, IItem
|
||||||
{
|
{
|
||||||
var itemToAdopt = ChildrenToAdopt.Find(i => i.Item.Name == item.Name);
|
var itemToAdopt = ChildrenToAdopt.Find(i => i.Item == item);
|
||||||
if (itemToAdopt is TResult itemViewModel) return itemViewModel;
|
if (itemToAdopt is TResult itemViewModel) return itemViewModel;
|
||||||
|
|
||||||
|
var existingViewModel = _items?.FirstOrDefault(i => i.Item == item);
|
||||||
|
if (existingViewModel is TResult itemViewModelToReuse) return itemViewModelToReuse;
|
||||||
|
|
||||||
return generator(item);
|
return generator(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,6 +188,7 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
foreach (var container in _containers)
|
foreach (var container in _containers)
|
||||||
{
|
{
|
||||||
container.Unload(true);
|
container.Unload(true);
|
||||||
|
container.Dispose();
|
||||||
container.ChildrenToAdopt.Clear();
|
container.ChildrenToAdopt.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -203,5 +215,25 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
if (!_isInitialized) await Task.Run(Refresh);
|
if (!_isInitialized) await Task.Run(Refresh);
|
||||||
return _items;
|
return _items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
~ContainerViewModel()
|
||||||
|
{
|
||||||
|
Dispose(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Dispose(true);
|
||||||
|
GC.SuppressFinalize(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
if (!_disposed && disposing)
|
||||||
|
{
|
||||||
|
Container.Refreshed.Remove(Container_Refreshed);
|
||||||
|
}
|
||||||
|
_disposed = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ using FileTime.App.Core.Clipboard;
|
|||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using FileTime.Core.Command;
|
using FileTime.Core.Command;
|
||||||
using FileTime.Core.Timeline;
|
using FileTime.Core.Timeline;
|
||||||
|
using FileTime.Core.Providers;
|
||||||
|
|
||||||
namespace FileTime.Avalonia.ViewModels
|
namespace FileTime.Avalonia.ViewModels
|
||||||
{
|
{
|
||||||
@@ -40,7 +41,7 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
|
|
||||||
private IClipboard _clipboard;
|
private IClipboard _clipboard;
|
||||||
private TimeRunner _timeRunner;
|
private TimeRunner _timeRunner;
|
||||||
|
private IEnumerable<IContentProvider>? _contentProviders;
|
||||||
private Action? _inputHandler;
|
private Action? _inputHandler;
|
||||||
|
|
||||||
[Property]
|
[Property]
|
||||||
@@ -67,6 +68,11 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
{
|
{
|
||||||
_clipboard = App.ServiceProvider.GetService<IClipboard>()!;
|
_clipboard = App.ServiceProvider.GetService<IClipboard>()!;
|
||||||
_timeRunner = App.ServiceProvider.GetService<TimeRunner>()!;
|
_timeRunner = App.ServiceProvider.GetService<TimeRunner>()!;
|
||||||
|
_contentProviders = App.ServiceProvider.GetService<IEnumerable<IContentProvider>>();
|
||||||
|
var inputInterface = (BasicInputHandler)App.ServiceProvider.GetService<IInputInterface>()!;
|
||||||
|
inputInterface.InputHandler = ReadInputs2;
|
||||||
|
App.ServiceProvider.GetService<TopContainer>();
|
||||||
|
|
||||||
_timeRunner.CommandsChanged += (o, e) => OnPropertyChanged(nameof(TimelineCommands));
|
_timeRunner.CommandsChanged += (o, e) => OnPropertyChanged(nameof(TimelineCommands));
|
||||||
InitCommandBindings();
|
InitCommandBindings();
|
||||||
|
|
||||||
@@ -496,6 +502,19 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
await _timeRunner.Refresh();
|
await _timeRunner.Refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task GoToContainer()
|
||||||
|
{
|
||||||
|
var handler = () =>
|
||||||
|
{
|
||||||
|
if (Inputs != null)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ReadInputs(new List<Core.Interactions.InputElement>() { new Core.Interactions.InputElement("Path", InputType.Text) }, handler);
|
||||||
|
}
|
||||||
|
|
||||||
[Command]
|
[Command]
|
||||||
public void ProcessInputs()
|
public void ProcessInputs()
|
||||||
{
|
{
|
||||||
@@ -669,6 +688,24 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
_inputHandler = inputHandler;
|
_inputHandler = inputHandler;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<string?[]> ReadInputs2(IEnumerable<Core.Interactions.InputElement> fields)
|
||||||
|
{
|
||||||
|
var waiting = true;
|
||||||
|
var result = new string[0];
|
||||||
|
ReadInputs(fields.ToList(), () =>
|
||||||
|
{
|
||||||
|
if(Inputs != null)
|
||||||
|
{
|
||||||
|
result = Inputs.Select(i => i.Value).ToArray();
|
||||||
|
}
|
||||||
|
waiting = false;
|
||||||
|
});
|
||||||
|
|
||||||
|
while (waiting) await Task.Delay(100);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private void ShowMessageBox(string text, Action inputHandler)
|
private void ShowMessageBox(string text, Action inputHandler)
|
||||||
{
|
{
|
||||||
MessageBoxText = text;
|
MessageBoxText = text;
|
||||||
@@ -847,6 +884,11 @@ namespace FileTime.Avalonia.ViewModels
|
|||||||
FileTime.App.Core.Command.Commands.Refresh,
|
FileTime.App.Core.Command.Commands.Refresh,
|
||||||
new KeyWithModifiers[]{new KeyWithModifiers(Key.R)},
|
new KeyWithModifiers[]{new KeyWithModifiers(Key.R)},
|
||||||
RefreshCurrentLocation),
|
RefreshCurrentLocation),
|
||||||
|
new CommandBinding(
|
||||||
|
"go to",
|
||||||
|
FileTime.App.Core.Command.Commands.Refresh,
|
||||||
|
new KeyWithModifiers[]{new KeyWithModifiers(Key.L, ctrl: true)},
|
||||||
|
GoToContainer),
|
||||||
};
|
};
|
||||||
var universalCommandBindings = new List<CommandBinding>()
|
var universalCommandBindings = new List<CommandBinding>()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -49,8 +49,8 @@ namespace FileTime.Providers.Local
|
|||||||
public async Task<IItem?> GetByPath(string path)
|
public async Task<IItem?> GetByPath(string path)
|
||||||
{
|
{
|
||||||
var pathParts = (IsCaseInsensitive ? path.ToLower() : path).TrimStart(Constants.SeparatorChar).Split(Constants.SeparatorChar);
|
var pathParts = (IsCaseInsensitive ? path.ToLower() : path).TrimStart(Constants.SeparatorChar).Split(Constants.SeparatorChar);
|
||||||
|
|
||||||
if(RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && pathParts.Length == 1 && pathParts[0] == "") return this;
|
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) && pathParts.Length == 1 && pathParts[0] == "") return this;
|
||||||
|
|
||||||
var rootContainer = _rootContainers.FirstOrDefault(c => NormalizePath(c.Name) == NormalizePath(pathParts[0]));
|
var rootContainer = _rootContainers.FirstOrDefault(c => NormalizePath(c.Name) == NormalizePath(pathParts[0]));
|
||||||
|
|
||||||
|
|||||||
62
src/Providers/FileTime.Providers.Smb/SmbClientContext.cs
Normal file
62
src/Providers/FileTime.Providers.Smb/SmbClientContext.cs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
using SMBLibrary.Client;
|
||||||
|
|
||||||
|
namespace FileTime.Providers.Smb
|
||||||
|
{
|
||||||
|
public class SmbClientContext
|
||||||
|
{
|
||||||
|
private readonly Func<Task<ISMBClient>> _getSmbClient;
|
||||||
|
private readonly Action _disposeClient;
|
||||||
|
private bool _isRunning;
|
||||||
|
private readonly object _lock = new object();
|
||||||
|
|
||||||
|
public SmbClientContext(Func<Task<ISMBClient>> getSmbClient, Action disposeClient)
|
||||||
|
{
|
||||||
|
_getSmbClient = getSmbClient;
|
||||||
|
_disposeClient = disposeClient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<T> RunWithSmbClientAsync<T>(Func<ISMBClient, T> func)
|
||||||
|
{
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
if(!_isRunning)
|
||||||
|
{
|
||||||
|
_isRunning = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await Task.Delay(1);
|
||||||
|
}
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ISMBClient client;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
client = await _getSmbClient();
|
||||||
|
return func(client);
|
||||||
|
}
|
||||||
|
catch (Exception e) when (e.Source == "SMBLibrary")
|
||||||
|
{
|
||||||
|
_disposeClient();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
throw new Exception("Exception was thrown while executing method with SmbClient.", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
lock (_lock)
|
||||||
|
{
|
||||||
|
_isRunning = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -63,9 +63,21 @@ namespace FileTime.Providers.Smb
|
|||||||
throw new NotSupportedException();
|
throw new NotSupportedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IItem?> GetByPath(string path)
|
public async Task<IItem?> GetByPath(string path)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
if (path == null) return this;
|
||||||
|
|
||||||
|
var pathParts = path.TrimStart(Constants.SeparatorChar).Split(Constants.SeparatorChar);
|
||||||
|
|
||||||
|
var rootContainer = _rootContainers.Find(c => c.Name == pathParts[0]);
|
||||||
|
|
||||||
|
if (rootContainer == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var remainingPath = string.Join(Constants.SeparatorChar, pathParts.Skip(1));
|
||||||
|
return remainingPath.Length == 0 ? rootContainer : await rootContainer.GetByPath(remainingPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
public IContainer? GetParent() => _parent;
|
public IContainer? GetParent() => _parent;
|
||||||
|
|||||||
@@ -17,7 +17,10 @@ namespace FileTime.Providers.Smb
|
|||||||
private IReadOnlyList<IItem>? _items;
|
private IReadOnlyList<IItem>? _items;
|
||||||
private readonly IReadOnlyList<IElement>? _elements = new List<IElement>().AsReadOnly();
|
private readonly IReadOnlyList<IElement>? _elements = new List<IElement>().AsReadOnly();
|
||||||
private ISMBClient? _client;
|
private ISMBClient? _client;
|
||||||
|
private readonly object _clientGuard = new object();
|
||||||
|
private bool _refreshingClient;
|
||||||
private readonly IInputInterface _inputInterface;
|
private readonly IInputInterface _inputInterface;
|
||||||
|
private readonly SmbClientContext _smbClientContext;
|
||||||
|
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
|
|
||||||
@@ -37,6 +40,7 @@ namespace FileTime.Providers.Smb
|
|||||||
public SmbServer(string path, SmbContentProvider contentProvider, IInputInterface inputInterface)
|
public SmbServer(string path, SmbContentProvider contentProvider, IInputInterface inputInterface)
|
||||||
{
|
{
|
||||||
_inputInterface = inputInterface;
|
_inputInterface = inputInterface;
|
||||||
|
_smbClientContext = new SmbClientContext(GetSmbClient, DisposeSmbClient);
|
||||||
|
|
||||||
Provider = contentProvider;
|
Provider = contentProvider;
|
||||||
FullName = Name = path;
|
FullName = Name = path;
|
||||||
@@ -86,26 +90,60 @@ namespace FileTime.Providers.Smb
|
|||||||
|
|
||||||
public async Task Refresh()
|
public async Task Refresh()
|
||||||
{
|
{
|
||||||
ISMBClient client = await GetSmbClient();
|
List<string> shares = await _smbClientContext.RunWithSmbClientAsync((client) => client.ListShares(out var status));
|
||||||
|
|
||||||
List<string> shares = client.ListShares(out var status);
|
_shares = shares.ConvertAll(s => new SmbShare(s, Provider, this, _smbClientContext)).AsReadOnly();
|
||||||
|
|
||||||
_shares = shares.ConvertAll(s => new SmbShare(s, Provider, this, GetSmbClient)).AsReadOnly();
|
|
||||||
_items = _shares.Cast<IItem>().ToList().AsReadOnly();
|
_items = _shares.Cast<IItem>().ToList().AsReadOnly();
|
||||||
await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty);
|
await Refreshed.InvokeAsync(this, AsyncEventArgs.Empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<IContainer> Clone() => Task.FromResult((IContainer)this);
|
public Task<IContainer> Clone() => Task.FromResult((IContainer)this);
|
||||||
|
|
||||||
|
private void DisposeSmbClient()
|
||||||
|
{
|
||||||
|
lock (_clientGuard)
|
||||||
|
{
|
||||||
|
_client = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<ISMBClient> GetSmbClient()
|
private async Task<ISMBClient> GetSmbClient()
|
||||||
{
|
{
|
||||||
if (_client == null)
|
bool isClientNull;
|
||||||
|
lock (_clientGuard)
|
||||||
|
{
|
||||||
|
isClientNull = _client == null;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (isClientNull)
|
||||||
|
{
|
||||||
|
if (!await RefreshSmbClient())
|
||||||
|
{
|
||||||
|
await Task.Delay(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
lock (_clientGuard)
|
||||||
|
{
|
||||||
|
isClientNull = _client == null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return _client!;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<bool> RefreshSmbClient()
|
||||||
|
{
|
||||||
|
lock (_clientGuard)
|
||||||
|
{
|
||||||
|
if (_refreshingClient) return false;
|
||||||
|
_refreshingClient = true;
|
||||||
|
}
|
||||||
|
try
|
||||||
{
|
{
|
||||||
var couldParse = IPAddress.TryParse(Name[2..], out var ipAddress);
|
var couldParse = IPAddress.TryParse(Name[2..], out var ipAddress);
|
||||||
_client = new SMB2Client();
|
var client = new SMB2Client();
|
||||||
var connected = couldParse
|
var connected = couldParse
|
||||||
? _client.Connect(ipAddress, SMBTransportType.DirectTCPTransport)
|
? client.Connect(ipAddress, SMBTransportType.DirectTCPTransport)
|
||||||
: _client.Connect(Name[2..], SMBTransportType.DirectTCPTransport);
|
: client.Connect(Name[2..], SMBTransportType.DirectTCPTransport);
|
||||||
|
|
||||||
if (connected)
|
if (connected)
|
||||||
{
|
{
|
||||||
@@ -122,14 +160,29 @@ namespace FileTime.Providers.Smb
|
|||||||
_password = inputs[1];
|
_password = inputs[1];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (_client.Login(string.Empty, _username, _password) != NTStatus.STATUS_SUCCESS)
|
if (client.Login(string.Empty, _username, _password) != NTStatus.STATUS_SUCCESS)
|
||||||
{
|
{
|
||||||
_username = null;
|
_username = null;
|
||||||
_password = null;
|
_password = null;
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lock (_clientGuard)
|
||||||
|
{
|
||||||
|
_client = client;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return _client;
|
finally
|
||||||
|
{
|
||||||
|
lock (_clientGuard)
|
||||||
|
{
|
||||||
|
_refreshingClient = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task Rename(string newName) => throw new NotSupportedException();
|
public Task Rename(string newName) => throw new NotSupportedException();
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ namespace FileTime.Providers.Smb
|
|||||||
private IReadOnlyList<IItem>? _items;
|
private IReadOnlyList<IItem>? _items;
|
||||||
private IReadOnlyList<IContainer>? _containers;
|
private IReadOnlyList<IContainer>? _containers;
|
||||||
private IReadOnlyList<IElement>? _elements;
|
private IReadOnlyList<IElement>? _elements;
|
||||||
private Func<Task<ISMBClient>> _getSmbClient;
|
private SmbClientContext _smbClientContext;
|
||||||
private readonly IContainer? _parent;
|
private readonly IContainer? _parent;
|
||||||
|
|
||||||
public string Name { get; }
|
public string Name { get; }
|
||||||
@@ -28,10 +28,10 @@ namespace FileTime.Providers.Smb
|
|||||||
|
|
||||||
public AsyncEventHandler Refreshed { get; } = new();
|
public AsyncEventHandler Refreshed { get; } = new();
|
||||||
|
|
||||||
public SmbShare(string name, SmbContentProvider contentProvider, IContainer parent, Func<Task<ISMBClient>> getSmbClient)
|
public SmbShare(string name, SmbContentProvider contentProvider, IContainer parent, SmbClientContext smbClientContext)
|
||||||
{
|
{
|
||||||
_parent = parent;
|
_parent = parent;
|
||||||
_getSmbClient = getSmbClient;
|
_smbClientContext = smbClientContext;
|
||||||
|
|
||||||
Name = name;
|
Name = name;
|
||||||
FullName = parent?.FullName == null ? Name : parent.FullName + Constants.SeparatorChar + Name;
|
FullName = parent?.FullName == null ? Name : parent.FullName + Constants.SeparatorChar + Name;
|
||||||
@@ -117,40 +117,42 @@ namespace FileTime.Providers.Smb
|
|||||||
|
|
||||||
public async Task<(List<IContainer> containers, List<IElement> elements)> ListFolder(IContainer parent, string shareName, string folderName)
|
public async Task<(List<IContainer> containers, List<IElement> elements)> ListFolder(IContainer parent, string shareName, string folderName)
|
||||||
{
|
{
|
||||||
var containers = new List<IContainer>();
|
return await _smbClientContext.RunWithSmbClientAsync(client =>
|
||||||
var elements = new List<IElement>();
|
|
||||||
|
|
||||||
var client = await _getSmbClient();
|
|
||||||
ISMBFileStore fileStore = client.TreeConnect(shareName, out var status);
|
|
||||||
if (status == NTStatus.STATUS_SUCCESS)
|
|
||||||
{
|
{
|
||||||
status = fileStore.CreateFile(out object directoryHandle, out FileStatus fileStatus, folderName, AccessMask.GENERIC_READ, SMBLibrary.FileAttributes.Directory, ShareAccess.Read | ShareAccess.Write, CreateDisposition.FILE_OPEN, CreateOptions.FILE_DIRECTORY_FILE, null);
|
var containers = new List<IContainer>();
|
||||||
|
var elements = new List<IElement>();
|
||||||
|
NTStatus status = NTStatus.STATUS_DATA_ERROR;
|
||||||
|
ISMBFileStore fileStore = client.TreeConnect(shareName, out status);
|
||||||
if (status == NTStatus.STATUS_SUCCESS)
|
if (status == NTStatus.STATUS_SUCCESS)
|
||||||
{
|
{
|
||||||
status = fileStore.QueryDirectory(out List<QueryDirectoryFileInformation> fileList, directoryHandle, "*", FileInformationClass.FileDirectoryInformation);
|
status = fileStore.CreateFile(out object directoryHandle, out FileStatus fileStatus, folderName, AccessMask.GENERIC_READ, SMBLibrary.FileAttributes.Directory, ShareAccess.Read | ShareAccess.Write, CreateDisposition.FILE_OPEN, CreateOptions.FILE_DIRECTORY_FILE, null);
|
||||||
status = fileStore.CloseFile(directoryHandle);
|
if (status == NTStatus.STATUS_SUCCESS)
|
||||||
|
|
||||||
foreach (var item in fileList)
|
|
||||||
{
|
{
|
||||||
if (item is FileDirectoryInformation fileDirectoryInformation && fileDirectoryInformation.FileName != "." && fileDirectoryInformation.FileName != "..")
|
status = fileStore.QueryDirectory(out List<QueryDirectoryFileInformation> fileList, directoryHandle, "*", FileInformationClass.FileDirectoryInformation);
|
||||||
|
status = fileStore.CloseFile(directoryHandle);
|
||||||
|
|
||||||
|
foreach (var item in fileList)
|
||||||
{
|
{
|
||||||
if ((fileDirectoryInformation.FileAttributes & SMBLibrary.FileAttributes.Directory) == SMBLibrary.FileAttributes.Directory)
|
if (item is FileDirectoryInformation fileDirectoryInformation && fileDirectoryInformation.FileName != "." && fileDirectoryInformation.FileName != "..")
|
||||||
{
|
{
|
||||||
containers.Add(new SmbFolder(fileDirectoryInformation.FileName, Provider, this, parent));
|
if ((fileDirectoryInformation.FileAttributes & SMBLibrary.FileAttributes.Directory) == SMBLibrary.FileAttributes.Directory)
|
||||||
}
|
{
|
||||||
else
|
containers.Add(new SmbFolder(fileDirectoryInformation.FileName, Provider, this, parent));
|
||||||
{
|
}
|
||||||
elements.Add(new SmbFile(fileDirectoryInformation.FileName, Provider, parent));
|
else
|
||||||
|
{
|
||||||
|
elements.Add(new SmbFile(fileDirectoryInformation.FileName, Provider, parent));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
containers = containers.OrderBy(c => c.Name).ToList();
|
containers = containers.OrderBy(c => c.Name).ToList();
|
||||||
elements = elements.OrderBy(e => e.Name).ToList();
|
elements = elements.OrderBy(e => e.Name).ToList();
|
||||||
|
|
||||||
return (containers, elements);
|
return (containers, elements);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task Rename(string newName) => throw new NotSupportedException();
|
public Task Rename(string newName) => throw new NotSupportedException();
|
||||||
|
|||||||
Reference in New Issue
Block a user