using System; using System.Collections.Generic; using System.Globalization; using System.IO; using System.IO.Compression; using System.Linq; using gehGassi.Core.Interfaces; using gehGassi.Permissions; using gehGassi.Web.Auth; using gehGassi.Web.Auth.Attributes; using gehGassi.Web.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.StaticFiles; using Newtonsoft.Json; namespace gehGassi.Web.Controllers { /// /// Controller für die Verwaltung von Stufen /// [Authorize] public class FileManagerController : BaseController { private readonly IFileShareService _fileShareService; /// /// Erstellt eine Instanz /// /// Instanz eines IFileShareService public FileManagerController(IFileShareService fileShareService) { _fileShareService = fileShareService; } #region Admin /// /// Gibt einen View für den Dateimanager zurück /// /// View [Authorize(Policy = Policies.PowerUserOnly)] [HasPermission(Permission.SettingsManage, Permission.SettingsFileManager)] [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult Index() { return View(); } /// /// Handler für file-Browser Anfragen. /// /// FileExplorerParams mit Commands und Parameter /// [Authorize(Policy = Policies.PowerUserOnly)] [HttpPost] [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public object Handler([FromBody] FileManagerDirectoryContent args) { if (args.Action is "delete" or "rename") { if (args.TargetPath == null && args.Path == "") { var response = new FileManagerResponse { Error = new ErrorDetails { Code = "401", Message = "Restricted to modify the root folder." } }; return Json(response); } } if (!string.IsNullOrWhiteSpace(args.Path)) { if (args.Path.StartsWith("/")) args.Path = $"gehGassi{args.Path}"; } if (!string.IsNullOrWhiteSpace(args.TargetPath)) { if (args.TargetPath.StartsWith("/")) args.TargetPath = $"gehGassi{args.TargetPath}"; } var rootName = "gehGassi"; switch (args.Action) { case "read": // reads the file(s) or folder(s) from the given path. return Json(GetFilesAndFolders(args.Path, rootName)); case "delete": // deletes the selected file(s) or folder(s) from the given path. return Json(Delete(args.Path, args.Names.ToList())); case "copy": // copies the selected file(s) or folder(s) from a path and then pastes them into a given target path. return Json(Copy(args.Path, args.TargetPath, args.Names.ToList())); case "move": // cuts the selected file(s) or folder(s) from a path and then pastes them into a given target path. return Json(Move(args.Path, args.TargetPath, args.Names.ToList())); case "details": // gets the details of the selected file(s) or folder(s). return Json(Details(args.Path, args.Names.ToList())); case "create": //creates a new folder in a given path. return Json(CreateFolder(args.Path, args.Name)); case "search": // gets the list of file(s) or folder(s) from a given path based on the searched key string. return Json(GetFilesAndFolders(args.Path, rootName, args.SearchString)); case "rename": // renames a file or folder. return Json(Rename(args.Path, args.Name, args.NewName, rootName)); default: return null; } } /// /// Gibt ein Bild zurück /// /// Parameter /// Bild oder NotFound [Authorize(Policy = Policies.PowerUserOnly)] [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult GetImage(FileManagerDirectoryContent args) { if (args.Path.StartsWith("/")) args.Path = $"gehGassi{args.Path}"; var (share, filename) = GetShareAndFolder(args.Path); var file = _fileShareService.Get(share, filename); if (file != null) { return File(file, GetMimeMapping(Path.GetFileName(filename))); } return NotFound(); } /// /// Hochladen einer oder mehrerer Dateien /// /// Pfad /// Dateien /// Action /// [Authorize(Policy = Policies.PowerUserOnly)] [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult Upload(string path, IList uploadFiles, string action) { if (path.StartsWith("/")) path = $"gehGassi{path}"; var (share, folder) = GetShareAndFolder(path); try { for (var i = 0; i < uploadFiles.Count; i++) { var fileName = Path.GetFileName(uploadFiles[i].FileName); if (!string.IsNullOrWhiteSpace(folder)) fileName = $"{folder}/{fileName}"; using (var ms = new MemoryStream()) { uploadFiles[i].CopyTo(ms); var fileBytes = ms.ToArray(); _fileShareService.Store(share, fileName, fileBytes); } } } catch { } return Content(""); } /// /// Herunterladen einer Datei /// /// PArameter /// Datei [Authorize(Policy = Policies.PowerUserOnly)] [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult Download(string downloadInput) { var args = JsonConvert.DeserializeObject(downloadInput); if (args.Path.StartsWith("/")) args.Path = $"gehGassi{args.Path}"; var names = args.Names; var data = args.Data; var fileCount = 0; for (int i = 0; i < args.Data.Length; i++) { if (args.Data[i].IsFile) fileCount++; } var filesOnly = fileCount == names.Length; if (filesOnly && names.Length == 1) { //Nur eine Datei.... direkter Download var (share, folder) = GetShareAndFolder(args.Path); if (!string.IsNullOrWhiteSpace(folder)) folder += "/"; var file = _fileShareService.Get(share, folder + names[0]); if (file != null) { return File(file, "APPLICATION/octet-stream", names[0]); } } else if (filesOnly && names.Length > 1) { //Mehrere Dateien. Diese nun in ein Zip verpacken und als Zip senden byte[] archiveFile; using (var archiveStream = new MemoryStream()) { using (var archive = new ZipArchive(archiveStream, ZipArchiveMode.Create, true)) { for (int i = 0; i < args.Data.Length; i++) { var (share, folder) = GetShareAndFolder(args.Path); if (!string.IsNullOrWhiteSpace(folder)) folder += "/"; var file = _fileShareService.Get(share, folder + names[i]); if (file != null) { var zipArchiveEntry = archive.CreateEntry(names[i], CompressionLevel.Optimal); using var zipStream = zipArchiveEntry.Open(); zipStream.Write(file, 0, file.Length); } } } archiveFile = archiveStream.ToArray(); } var fileName = $"gehGassi_{DateTime.Now.Year}-{DateTime.Now.Month}-{DateTime.Now.Day}-{DateTime.Now.Hour}-{DateTime.Now.Minute}-{DateTime.Now.Second}.zip"; return File(archiveFile, "APPLICATION/octet-stream", fileName); } return NotFound(); } #endregion #region private /// /// Lesen von Daten /// /// Pfad - beinhaltet bereits Anpassungen an Rollen /// Root-Name der angezeigt werden soll /// Filter wenn gesucht werden soll /// FileManagerResponse private FileManagerResponse GetFilesAndFolders(string path, string rootName, string filter = "") { if (!string.IsNullOrWhiteSpace(filter)) filter = filter.Replace("*", ""); var (share, folder) = GetShareAndFolder(path); var response = new FileManagerResponse { Files = new List() }; try { var directoriesFound = _fileShareService.ListFolders(share, folder); var filesFound = _fileShareService.ListFiles(share, folder); var (dirName, relativePath) = GetFolderNameAndRelativePath(folder, rootName); var folderInfo = _fileShareService.GetFolderInfo(share, folder); var directoryContent = new FileManagerDirectoryContent { Name = dirName, FilterPath = relativePath, Size = 0, Type = "File Folder", IsFile = false, DateModified = folderInfo?.LastModified ?? DateTimeOffset.UtcNow, DateCreated = folderInfo?.Created ?? DateTimeOffset.UtcNow, HasChild = directoriesFound.Count > 0 ? true : false }; response.CWD = directoryContent; foreach (var directory in directoriesFound) { if (directory.Name.ToLowerInvariant().Contains(filter.ToLowerInvariant())) { response.Files.Add(new FileManagerDirectoryContent() { Name = directory.Name, FilterPath = relativePath, Size = 0, Type = "Directory", IsFile = false, DateModified = directory.LastModified ?? DateTimeOffset.UtcNow, DateCreated = directory.Created ?? DateTimeOffset.UtcNow, HasChild = directory.HasSubfolders }); } } foreach (var file in filesFound) { var fileName = file.Name.ToLowerInvariant(); var extension = Path.GetExtension(fileName)?.ToLowerInvariant(); if (fileName.ToLowerInvariant().Contains(filter.ToLowerInvariant())) { response.Files.Add(new FileManagerDirectoryContent() { Name = file.Name, FilterPath = FolderToRelativePath(folder), Size = file.Size, Type = extension, IsFile = true, DateModified = file.LastModified ?? DateTimeOffset.UtcNow, DateCreated = file.Created ?? DateTimeOffset.UtcNow, HasChild = false, }); } } return response; } catch (Exception ex) { var errorDetails = new ErrorDetails { Message = ex.GetType().FullName + ", " + ex.Message, Code = "500" }; response.Error = errorDetails; return response; } } /// /// Anlegen eines Ordners /// /// Pfad - beinhaltet bereits Anpassungen an Rollen /// Ordner in dem ein neuer Ordner angelegt werden soll /// FileManagerResponse private FileManagerResponse CreateFolder(string path, string foldertoCreate) { var response = new FileManagerResponse(); var newFolder = foldertoCreate; var (share, folder) = GetShareAndFolder(path); if (!string.IsNullOrWhiteSpace(folder)) newFolder = $"{folder}/{foldertoCreate}"; try { var success = _fileShareService.CreateFolder(share, newFolder); if (success) { var folderInfo = _fileShareService.GetFolderInfo(share, newFolder); var folderCreatedInfo = new FileManagerDirectoryContent { Name = foldertoCreate, FilterPath = null, IsFile = false, Size = 0, DateModified = folderInfo.LastModified ?? DateTimeOffset.UtcNow, DateCreated = folderInfo.LastModified ?? DateTimeOffset.UtcNow, HasChild = folderInfo.HasSubfolders, Type = "Directory" }; var newData = new List { folderCreatedInfo }; response.Files = newData; } else { var error = new ErrorDetails { Code = "400", Message = $"A file or folder with the name {foldertoCreate} already exists." }; response.Error = error; } return response; } catch (Exception e) { var error = new ErrorDetails { Code = "400", Message = e.GetType().FullName + ", " + e.Message }; response.Error = error; } return response; } /// /// Umbenennen einer Datei oder eines Ordners /// /// Pfad - beinhaltet bereits Anpassungen an Rollen /// Name /// Neuer Name /// Root-Name der angezeigt werden soll /// FileManagerResponse private FileManagerResponse Rename(string path, string name, string newName, string rootName) { var response = new FileManagerResponse(); var (share, folder) = GetShareAndFolder(path); var (dirName, relativePath) = GetFolderNameAndRelativePath(folder, rootName); var isFile = Path.HasExtension(name); if (isFile) { var fileName = name; if (!string.IsNullOrWhiteSpace(folder)) fileName = $"{folder}/{name}"; var newfileName = newName; if (!string.IsNullOrWhiteSpace(folder)) newfileName = $"{folder}/{newName}"; var success = _fileShareService.RenameFile(share, fileName, newfileName, false); if (success) { var fileInfo = _fileShareService.GetFileInfo(share, newfileName); if (fileInfo != null) { var extension = Path.GetExtension(newfileName)?.ToLowerInvariant(); response.Files = new List { new() { Name = fileInfo.Name, FilterPath = FolderToRelativePath(folder), Size = fileInfo.Size, Type = extension, IsFile = true, DateModified = fileInfo.LastModified ?? DateTimeOffset.UtcNow, HasChild = false, } }; } } else { var error = new ErrorDetails { Code = "400", Message = $"Can not rename file" }; response.Error = error; } } else { var folderName = name; if (!string.IsNullOrWhiteSpace(folder)) folderName = $"{folder}/{name}"; var newfolderName = newName; if (!string.IsNullOrWhiteSpace(folder)) newfolderName = $"{folder}/{newName}"; var success = _fileShareService.RenameFolderEx(share, folderName, newfolderName, false); if (success) { var folderInfo = _fileShareService.GetFolderInfo(share, newfolderName); if (folderInfo != null) { response.Files = new List { new() { Name = folderInfo.Name, FilterPath = relativePath, Size = 0, Type = "Directory", IsFile = false, DateModified = folderInfo.LastModified ?? DateTimeOffset.UtcNow, HasChild = folderInfo.HasSubfolders, } }; } } } return response; } /// /// Kopieren von Daten /// /// Pfad - beinhaltet bereits Anpassungen an Rollen /// Zielpfad /// Namen der Daten /// FileManagerResponse private FileManagerResponse Copy(string path, string targetPath, List names) { var response = new FileManagerResponse(); response.Files = new List(); var (share, folder) = GetShareAndFolder(path); var (targetShare, targetFolder) = GetShareAndFolder(targetPath); foreach (var name in names) { var isFile = Path.HasExtension(name); if (isFile) { var fileName = name; if (!string.IsNullOrWhiteSpace(folder)) fileName = $"{folder}/{name}"; var targetFileName = name; if (!string.IsNullOrWhiteSpace(targetFolder)) targetFileName = $"{targetFolder}/{name}"; var success = _fileShareService.RenameFile(share, fileName, targetFileName, true); if (success) { var fileInfo = _fileShareService.GetFileInfo(share, targetFileName); if (fileInfo != null) { var extension = Path.GetExtension(targetFileName)?.ToLowerInvariant(); response.Files.Add(new FileManagerDirectoryContent() { Name = fileInfo.Name, FilterPath = FolderToRelativePath(targetFolder), Size = fileInfo.Size, Type = extension, IsFile = true, DateModified = fileInfo.LastModified ?? DateTimeOffset.UtcNow, HasChild = false, }); } } } else { var folderName = name; if (!string.IsNullOrWhiteSpace(folder)) folderName = $"{folder}/{name}"; var targetFolderName = name; if (!string.IsNullOrWhiteSpace(targetFolder)) targetFolderName = $"{targetFolder}/{name}"; var success = _fileShareService.RenameFolderEx(share, folderName, targetFolderName, true); if (success) { var (dirName, relativePath) = GetFolderNameAndRelativePath(targetFolderName, ""); var folderInfo = _fileShareService.GetFolderInfo(share, targetFolderName); if (folderInfo != null) { response.Files.Add(new FileManagerDirectoryContent() { Name = dirName, FilterPath = relativePath, Size = 0, Type = "Directory", IsFile = false, DateModified = folderInfo.LastModified ?? DateTimeOffset.UtcNow, DateCreated= DateTimeOffset.UtcNow, HasChild = folderInfo.HasSubfolders }); } } } } return response; } /// /// Verschieben von Daten /// /// Pfad - beinhaltet bereits Anpassungen an Rollen /// Zielpfad /// Namen der Daten /// FileManagerResponse private FileManagerResponse Move(string path, string targetPath, List names) { var response = new FileManagerResponse(); response.Files = new List(); var (share, folder) = GetShareAndFolder(path); var (targetShare, targetFolder) = GetShareAndFolder(targetPath); foreach (var name in names) { var isFile = Path.HasExtension(name); if (isFile) { var fileName = name; if (!string.IsNullOrWhiteSpace(folder)) fileName = $"{folder}/{name}"; var targetFileName = name; if (!string.IsNullOrWhiteSpace(targetFolder)) targetFileName = $"{targetFolder}/{name}"; var success = _fileShareService.RenameFile(share, fileName, targetFileName, false); if (success) { var fileInfo = _fileShareService.GetFileInfo(share, targetFileName); if (fileInfo != null) { var extension = Path.GetExtension(targetFileName)?.ToLowerInvariant(); response.Files.Add(new FileManagerDirectoryContent() { Name = fileInfo.Name, FilterPath = FolderToRelativePath(targetFolder), Size = fileInfo.Size, Type = extension, IsFile = true, DateModified = fileInfo.LastModified ?? DateTimeOffset.UtcNow, HasChild = false, }); } } } else { var folderName = name; if (!string.IsNullOrWhiteSpace(folder)) folderName = $"{folder}/{name}"; var targetFolderName = name; if (!string.IsNullOrWhiteSpace(targetFolder)) targetFolderName = $"{targetFolder}/{name}"; var success = _fileShareService.RenameFolderEx(share, folderName, targetFolderName, false); if (success) { var (dirName, relativePath) = GetFolderNameAndRelativePath(targetFolderName, ""); var folderInfo = _fileShareService.GetFolderInfo(share, targetFolderName); if (folderInfo != null) { response.Files.Add(new FileManagerDirectoryContent() { Name = dirName, FilterPath = relativePath, Size = 0, Type = "Directory", IsFile = false, DateModified = folderInfo.LastModified ?? DateTimeOffset.UtcNow, DateCreated = DateTimeOffset.UtcNow, HasChild = folderInfo.HasSubfolders }); } } } } return response; } /// /// Löschen von Daten /// /// Pfad - beinhaltet bereits Anpassungen an Rollen /// Namen der Daten /// FileManagerResponse private FileManagerResponse Delete(string path, List names) { var response = new FileManagerResponse(); response.Files = new List(); var (share, folder) = GetShareAndFolder(path); foreach (var name in names) { var isFile = Path.HasExtension(name); if (isFile) { var fileName = name; if (!string.IsNullOrWhiteSpace(folder)) fileName = $"{folder}/{name}"; var fileInfo = _fileShareService.GetFileInfo(share, fileName); if (fileInfo != null) { var success = _fileShareService.DeleteFile(share, fileName); if (success) { var extension = Path.GetExtension(fileName)?.ToLowerInvariant(); response.Files.Add(new FileManagerDirectoryContent() { Name = fileInfo.Name, Size = fileInfo.Size, Type = extension, IsFile = true, DateModified = fileInfo.LastModified ?? DateTimeOffset.UtcNow, HasChild = false, }); } } } else { var folderName = name; if (!string.IsNullOrWhiteSpace(folder)) folderName = $"{folder}/{name}"; var folderInfo = _fileShareService.GetFolderInfo(share, folderName); if (folderInfo != null) { var success = _fileShareService.DeleteFolder(share, folderName); if (success) { response.Files.Add(new FileManagerDirectoryContent() { Name = folderInfo.Name, Size = 0, Type = "Directory", IsFile = false, DateModified = folderInfo.LastModified ?? DateTimeOffset.UtcNow, HasChild = folderInfo.HasSubfolders, }); } } } } return response; } /// /// Details von Daten anzeigen /// /// Pfad - beinhaltet bereits Anpassungen an Rollen /// Namen der Daten /// FileManagerResponse private FileManagerResponse Details(string path, List names) { var response = new FileManagerResponse(); var details = new FileDetails(); var (share, folder) = GetShareAndFolder(path); var size = 0L; var fileCount = 0; var dirCount = 0; var detailsName = string.Empty; foreach (var name in names) { var isFile = Path.HasExtension(name); if (isFile) { fileCount += 1; var fileName = name; if (!string.IsNullOrWhiteSpace(folder)) fileName = $"{folder}/{name}"; var fileInfo = _fileShareService.GetFileInfo(share, fileName); if (fileInfo != null) { if (string.IsNullOrWhiteSpace(detailsName)) detailsName = fileInfo.Name; else detailsName += $", {fileInfo.Name}"; size += fileInfo.Size; } } else { dirCount += 1; var folderName = name; if (!string.IsNullOrWhiteSpace(folder)) folderName = $"{folder}/{name}"; var folderInfo = _fileShareService.GetFolderInfo(share, folderName); if (folderInfo != null) { if (string.IsNullOrWhiteSpace(detailsName)) detailsName = folderInfo.Name; else detailsName += $", {folderInfo.Name}"; size += _fileShareService.GetFolderSize(share, folderName); } } } details.Name = detailsName; details.Size = GetByteSizeString(size); details.IsFile = fileCount > 0 && dirCount == 0; details.MultipleFiles = fileCount > 0 || dirCount == 0; details.Location = folder != "" ? folder : share; response.Details = details; return response; } #endregion #region Helper /// /// Holt den Share-Namen und den Ordner aus dem Pfad /// /// Pfad /// share,folder private (string, string) GetShareAndFolder(string path) { path = path.ToLowerInvariant(); var pathParts = path.Split('/'); var share = pathParts[0]; var folder = path.Replace(share, ""); folder = folder.Trim('/'); return (share, folder); } /// /// Gibt zu einem Ordner den Namen und den relativen Pfad zurück /// /// Ordner /// rootname /// name,relativePath private (string, string) GetFolderNameAndRelativePath(string folder, string rootName) { folder = folder.ToLowerInvariant(); var dirName = rootName; var relativePath = ""; if (!string.IsNullOrWhiteSpace(folder)) { var folders = folder.Split("/"); dirName = folders.Last(); relativePath = "/"; if (folders.Length > 1) { relativePath += folder.Replace(dirName, ""); } } else { relativePath = "/"; } return (dirName, relativePath); } /// /// Konvertiert einen Ordner-Pfad in einen Relativen-Pfad /// /// Order /// Relativer Pfad private string FolderToRelativePath(string folder) { if (string.IsNullOrWhiteSpace(folder)) return "/"; else return $"/{folder}/"; } /// /// Gibt den Mimetype einer Datei zurück /// /// Dateiname /// Mimetype private string GetMimeMapping(string fileName) { new FileExtensionContentTypeProvider().TryGetContentType(fileName, out var contentType); return contentType ?? "application/octet-stream"; } /// /// Gibt eine Dateigröße in Bytes, KB oder MB usw. zurück /// /// Größe in bytes /// Umgerechnete Größe als String private string GetByteSizeString(long fileSize) { try { string[] index = { "B", "KB", "MB", "GB", "TB", "PB", "EB" }; //Longs run out around EB if (fileSize == 0) { return "0 " + index[0]; } var bytes = Math.Abs(fileSize); var loc = Convert.ToInt32(Math.Floor(Math.Log(bytes, 1024))); var num = Math.Round(bytes / Math.Pow(1024, loc), 1); return (Math.Sign(fileSize) * num).ToString(CultureInfo.InvariantCulture) + " " + index[loc]; } catch { return string.Empty; } } #endregion } #region Helper-Classes public class FileManagerDirectoryContent { public string Path { get; set; } public string Action { get; set; } public string NewName { get; set; } public string[] Names { get; set; } public string Name { get; set; } public long Size { get; set; } public string PreviousName { get; set; } public DateTimeOffset DateModified { get; set; } public DateTimeOffset DateCreated { get; set; } public bool HasChild { get; set; } public bool IsFile { get; set; } public string Type { get; set; } public string Id { get; set; } public string FilterPath { get; set; } public string FilterId { get; set; } public string ParentId { get; set; } public string TargetPath { get; set; } public string[] RenameFiles { get; set; } public IList UploadFiles { get; set; } public bool CaseSensitive { get; set; } public string SearchString { get; set; } public bool ShowHiddenItems { get; set; } public FileManagerDirectoryContent[] Data { get; set; } public FileManagerDirectoryContent TargetData { get; set; } public AccessPermission Permission { get; set; } /// /// Optional - Rootpath der nach den internen Regeln angewendet werden soll /// public string Rp { get; set; } } public class AccessPermission { public bool Copy = true; public bool Download = true; public bool Write = true; public bool WriteContents = true; public bool Read = true; public bool Upload = true; public string Message = String.Empty; } public class FileManagerResponse { public FileManagerDirectoryContent CWD { get; set; } public List Files { get; set; } public ErrorDetails Error { get; set; } public FileDetails Details { get; set; } } public class ErrorDetails { public string Code { get; set; } public string Message { get; set; } public IEnumerable FileExists { get; set; } } public class FileDetails { public string Name { get; set; } public string Location { get; set; } public bool IsFile { get; set; } public string Size { get; set; } public DateTime Created { get; set; } public DateTime Modified { get; set; } public bool MultipleFiles { get; set; } public AccessPermission Permission { get; set; } } #endregion }