using Logging.Net; using Renci.SshNet; using ConnectionInfo = Renci.SshNet.ConnectionInfo; namespace Moonlight.App.Helpers.Files; public class SftpFileAccess : FileAccess { private readonly string SftpHost; private readonly string SftpUser; private readonly string SftpPassword; private readonly int SftpPort; private readonly bool ForceUserDir; private readonly string AdditionalPath; private readonly SftpClient Client; private string InternalPath { get { if (ForceUserDir) return $"/home/{SftpUser}{AdditionalPath}{CurrentPath}"; return InternalPath; } } public SftpFileAccess(string sftpHost, string sftpUser, string sftpPassword, int sftpPort, bool forceUserDir = false, string additionalPath = "") { SftpHost = sftpHost; SftpUser = sftpUser; SftpPassword = sftpPassword; SftpPort = sftpPort; ForceUserDir = forceUserDir; AdditionalPath = additionalPath; Client = new( new ConnectionInfo( SftpHost, SftpPort, SftpUser, new PasswordAuthenticationMethod( SftpUser, SftpPassword ) ) ); } private void EnsureConnect() { if (!Client.IsConnected) Client.Connect(); } public override Task Ls() { EnsureConnect(); var x = new List(); foreach (var file in Client.ListDirectory(InternalPath)) { if (file.Name != "." && file.Name != "..") { x.Add(new() { Name = file.Name, Size = file.Attributes.Size, IsFile = !file.IsDirectory }); } } return Task.FromResult(x.ToArray()); } public override Task Cd(string dir) { var x = Path.Combine(CurrentPath, dir).Replace("\\", "/") + "/"; x = x.Replace("//", "/"); CurrentPath = x; return Task.CompletedTask; } public override Task Up() { CurrentPath = Path.GetFullPath(Path.Combine(CurrentPath, "..")).Replace("\\", "/").Replace("C:", ""); return Task.CompletedTask; } public override Task SetDir(string dir) { CurrentPath = dir; return Task.CompletedTask; } public override Task Read(FileData fileData) { EnsureConnect(); var textStream = Client.Open(InternalPath.TrimEnd('/') + "/" + fileData.Name, FileMode.Open); if (textStream == null) return Task.FromResult(""); var streamReader = new StreamReader(textStream); var text = streamReader.ReadToEnd(); streamReader.Close(); textStream.Close(); return Task.FromResult(text); } public override Task Write(FileData fileData, string content) { EnsureConnect(); var textStream = Client.Open(InternalPath.TrimEnd('/') + "/" + fileData.Name, FileMode.Create); var streamWriter = new StreamWriter(textStream); streamWriter.Write(content); streamWriter.Flush(); textStream.Flush(); streamWriter.Close(); textStream.Close(); return Task.CompletedTask; } public override async Task Upload(string name, Stream stream, Action? progressUpdated = null) { var dataStream = new SyncStreamAdapter(stream); await Task.Factory.FromAsync((x, _) => Client.BeginUploadFile(dataStream, InternalPath + name, x, null, u => { progressUpdated?.Invoke((int)((long)u / stream.Length)); }), Client.EndUploadFile, null); } public override Task MkDir(string name) { Client.CreateDirectory(InternalPath + name); return Task.CompletedTask; } public override Task Pwd() { return Task.FromResult(CurrentPath); } public override Task DownloadUrl(FileData fileData) { throw new NotImplementedException(); } public override Task DownloadStream(FileData fileData) { var stream = new MemoryStream(100 * 1024 * 1024); Client.DownloadFile(InternalPath + fileData.Name, stream); return Task.FromResult(stream); } public override Task Delete(FileData fileData) { Client.Delete(InternalPath + fileData.Name); return Task.CompletedTask; } public override Task Move(FileData fileData, string newPath) { Client.RenameFile(InternalPath + fileData.Name, InternalPath + newPath); return Task.CompletedTask; } public override Task Compress(params FileData[] files) { throw new NotImplementedException(); } public override Task Decompress(FileData fileData) { throw new NotImplementedException(); } public override Task GetLaunchUrl() { return Task.FromResult($"sftp://{SftpUser}@{SftpHost}:{SftpPort}"); } public override object Clone() { return new SftpFileAccess(SftpHost, SftpUser, SftpPassword, SftpPort, ForceUserDir); } }