using System;
using System.Collections.Generic;
using System.IO;
using System.IO.Compression;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Tango.Core;
using Tango.Core.DI;
using Tango.Core.IO;
using Tango.FileSystem;
using Tango.FileSystem.Network;
using Tango.Integration.ExternalBridge;
using Tango.Integration.Operation;
using Tango.Logging;
using Tango.PPC.Common.ExternalBridge;
using Tango.PPC.Shared.Logs;
using Tango.Transport;
using Tango.Transport.Transporters;
using Tango.WebRTC;
namespace Tango.PPC.Common.FileSystem
{
///
/// Represents the default implementation.
///
///
///
///
[TangoCreateWhenRegistered]
public class DefaultFileSystemService : ExtendedObject, IFileSystemService, IExternalBridgeRequestHandler
{
private FileSystemManager _manager;
private Dictionary _operations;
private Dictionary _webRtcClients;
public bool Enabled { get; set; } = true;
public bool EnableWebRTC { get; set; } = true;
public DefaultFileSystemService(IPPCExternalBridgeService externalBridge)
{
_webRtcClients = new Dictionary();
_manager = new FileSystemManager();
_operations = new Dictionary();
externalBridge.RegisterRequestHandler(this);
}
[ExternalBridgeRequestHandlerMethod(typeof(InitWebRtcRequest), RequestHandlerLoggingMode.LogRequestNameAndContent)]
public async Task OnInitWebRtcRequest(InitWebRtcRequest request, String token, ExternalBridgeReceiver receiver)
{
this.ThrowIfDisabled();
try
{
if (!EnableWebRTC)
{
await receiver.SendErrorResponse(new InvalidOperationException("The file system service WebRTC channel is disabled on this machine."), token);
return;
}
LogManager.Log("Initializing WebRTC channel for file system service.");
if (_webRtcClients.ContainsKey(receiver))
{
_webRtcClients[receiver].Dispose();
}
LogManager.Log("Initializing WebRTC transport adapter on 'Passive' mode.");
var webRtcAdapter = new WebRtcTransportAdapter(receiver, WebRtcTransportAdapterMode.Passive, request.DataChannelName);
webRtcAdapter.Ready += (x, e) =>
{
LogManager.Log("The file system service WebRTC channel is ready.");
};
BasicTransporter webRtcTransporter = new BasicTransporter(webRtcAdapter);
webRtcTransporter = new BasicTransporter(webRtcAdapter);
webRtcTransporter.ComponentName = "File System Passive WebRTC Transporter";
webRtcTransporter.UseKeepAlive = false;
webRtcTransporter.RegisterRequestHandler(WebRtcChunkDownloadRequestReceived);
webRtcTransporter.RegisterRequestHandler(WebRtcChunkUploadRequestReceived);
await webRtcTransporter.Connect();
LogManager.Log("Sending WebRTC initialization response...");
await receiver.SendGenericResponse(new InitWebRtcResponse(), token);
_webRtcClients[receiver] = webRtcTransporter;
}
catch (Exception ex)
{
LogManager.Log(ex, "Error initializing WebRTC channel for file system service.");
await receiver.SendErrorResponse(ex, token);
}
}
private async void WebRtcChunkDownloadRequestReceived(ITransporter transporter, ChunkDownloadRequest request, string token)
{
await OnChunkDownloadRequest(request, token, transporter);
}
private async void WebRtcChunkUploadRequestReceived(ITransporter transporter, ChunkUploadRequest request, string token)
{
await OnChunkUploadRequest(request, token, transporter);
}
[ExternalBridgeRequestHandlerMethod(typeof(GetFileSystemItemRequest), RequestHandlerLoggingMode.LogRequestNameAndContent)]
public async Task OnGetFileSystemItemRequest(GetFileSystemItemRequest request, String token, ExternalBridgeReceiver receiver)
{
this.ThrowIfDisabled();
FileSystemItemDTO dto = _manager.GetFolder(request);
await receiver.SendGenericResponse(new GetFileSystemItemResponse() { FileSystemItem = dto }, token);
}
[ExternalBridgeRequestHandlerMethod(typeof(FileUploadRequest), RequestHandlerLoggingMode.LogRequestNameAndContent)]
public async Task OnFileUploadRequest(FileUploadRequest request, String token, ExternalBridgeReceiver receiver)
{
this.ThrowIfDisabled();
var tempFile = TemporaryManager.CreateFile();
using (var stream = new FileStream(tempFile, FileMode.Create)) { }
FileSystemOperation operation = new FileSystemOperation(FileSystemOperationMode.Upload, tempFile) { UploadPostPath = request.Path };
_operations.Add(operation.Id, operation);
await receiver.SendGenericResponse(new FileUploadResponse() { OperationId = operation.Id }, token);
}
[ExternalBridgeRequestHandlerMethod(typeof(FolderUploadRequest), RequestHandlerLoggingMode.LogRequestNameAndContent)]
public async Task OnFolderUploadRequest(FolderUploadRequest request, String token, ExternalBridgeReceiver receiver)
{
this.ThrowIfDisabled();
var tempFile = TemporaryManager.CreateFile();
using (var stream = new FileStream(tempFile, FileMode.Create)) { }
FileSystemOperation operation = new FileSystemOperation(FileSystemOperationMode.Upload, tempFile) { UploadPostPath = request.Path, IsPathTempZip = true };
_operations.Add(operation.Id, operation);
await receiver.SendGenericResponse(new FolderUploadResponse() { OperationId = operation.Id }, token);
}
[ExternalBridgeRequestHandlerMethod(typeof(FileDownloadRequest), RequestHandlerLoggingMode.LogRequestNameAndContent)]
public async Task OnFileDownloadRequest(FileDownloadRequest request, String token, ExternalBridgeReceiver receiver)
{
this.ThrowIfDisabled();
if (!File.Exists(request.Path))
{
throw new FileNotFoundException("Could not find the specified file.");
}
FileSystemOperation operation = new FileSystemOperation(FileSystemOperationMode.Download, request.Path);
_operations.Add(operation.Id, operation);
await receiver.SendGenericResponse(new FileDownloadResponse()
{
OperationId = operation.Id,
Length = new FileInfo(request.Path).Length
}, token);
}
[ExternalBridgeRequestHandlerMethod(typeof(FolderDownloadRequest), RequestHandlerLoggingMode.LogRequestNameAndContent)]
public async Task OnFolderDownloadRequest(FolderDownloadRequest request, String token, ExternalBridgeReceiver receiver)
{
this.ThrowIfDisabled();
if (!Directory.Exists(request.Path))
{
throw new FileNotFoundException("Could not find the specified directory.");
}
var tempFile = TemporaryManager.CreateImaginaryFile();
ZipFile.CreateFromDirectory(request.Path, tempFile);
FileSystemOperation operation = new FileSystemOperation(FileSystemOperationMode.Download, tempFile);
operation.IsPathTempZip = true;
_operations.Add(operation.Id, operation);
await receiver.SendGenericResponse(new FolderDownloadResponse()
{
OperationId = operation.Id,
Length = new FileInfo(tempFile).Length
}, token);
}
[ExternalBridgeRequestHandlerMethod(typeof(ChunkUploadRequest))]
public async Task OnChunkUploadRequest(ChunkUploadRequest request, String token, ITransporter receiver)
{
this.ThrowIfDisabled();
FileSystemOperation operation;
_operations.TryGetValue(request.OperationId, out operation);
if (operation == null)
{
throw new ArgumentException("Invalid operation id.");
}
using (var stream = new FileStream(operation.Path, FileMode.Append))
{
stream.Write(request.Data, 0, request.Data.Length);
}
if (request.IsCompleted)
{
if (!operation.IsPathTempZip)
{
File.Copy(operation.Path, operation.UploadPostPath, true);
try
{
File.Delete(operation.Path);
}
catch { }
}
else
{
using (Ionic.Zip.ZipFile zip = new Ionic.Zip.ZipFile(operation.Path))
{
zip.ExtractAll(operation.UploadPostPath, Ionic.Zip.ExtractExistingFileAction.OverwriteSilently);
}
try
{
File.Delete(operation.Path);
}
catch { }
}
}
await receiver.SendGenericResponse(new ChunkUploadResponse(), token, new TransportResponseConfig() { Priority = QueuePriority.Low });
}
[ExternalBridgeRequestHandlerMethod(typeof(ChunkDownloadRequest))]
public async Task OnChunkDownloadRequest(ChunkDownloadRequest request, String token, ITransporter receiver)
{
this.ThrowIfDisabled();
FileSystemOperation operation;
_operations.TryGetValue(request.OperationId, out operation);
if (operation == null)
{
throw new ArgumentException("Invalid operation id.");
}
FileStream stream = null;
bool removeTempZipFile = false;
try
{
stream = new FileStream(operation.Path, FileMode.Open);
stream.Position = request.Position;
byte[] data = new byte[Math.Min(request.MaxChunkSize, stream.Length - stream.Position)];
if (stream.Position + data.Length == stream.Length)
{
removeTempZipFile = true;
}
await stream.ReadAsync(data, 0, data.Length);
stream.Dispose();
stream = null;
await receiver.SendGenericResponse(new ChunkDownloadResponse()
{
Data = data
}, token, new TransportResponseConfig() { Priority = QueuePriority.Low });
}
catch (Exception ex)
{
stream?.Dispose();
throw ex;
}
finally
{
if (operation.IsPathTempZip && removeTempZipFile)
{
try
{
if (File.Exists(operation.Path))
{
File.Delete(operation.Path);
}
}
catch { }
}
}
}
[ExternalBridgeRequestHandlerMethod(typeof(AbortOperationRequest), RequestHandlerLoggingMode.LogRequestNameAndContent)]
public async Task OnAbortOperationRequest(AbortOperationRequest request, String token, ExternalBridgeReceiver receiver)
{
this.ThrowIfDisabled();
FileSystemOperation operation;
_operations.TryGetValue(request.OperationId, out operation);
if (operation == null)
{
throw new ArgumentException("Invalid operation id.");
}
if (operation.Mode == FileSystemOperationMode.Upload)
{
if (File.Exists(operation.Path))
{
File.Delete(operation.Path);
}
}
else if (operation.IsPathTempZip)
{
if (File.Exists(operation.Path))
{
File.Delete(operation.Path);
}
}
await receiver.SendGenericResponse(new AbortOperationResponse(), token);
}
[ExternalBridgeRequestHandlerMethod(typeof(MoveRequest), RequestHandlerLoggingMode.LogRequestNameAndContent)]
public async Task OnMoveRequest(MoveRequest request, String token, ExternalBridgeReceiver receiver)
{
this.ThrowIfDisabled();
_manager.Move(request);
await receiver.SendGenericResponse(new MoveResponse(), token);
}
[ExternalBridgeRequestHandlerMethod(typeof(CopyRequest), RequestHandlerLoggingMode.LogRequestNameAndContent)]
public async Task OnCopyRequest(CopyRequest request, String token, ExternalBridgeReceiver receiver)
{
this.ThrowIfDisabled();
_manager.Copy(request);
await receiver.SendGenericResponse(new CopyResponse(), token);
}
[ExternalBridgeRequestHandlerMethod(typeof(DeleteRequest), RequestHandlerLoggingMode.LogRequestNameAndContent)]
public async Task OnDeleteRequest(DeleteRequest request, String token, ExternalBridgeReceiver receiver)
{
this.ThrowIfDisabled();
_manager.Delete(request.Path);
await receiver.SendGenericResponse(new DeleteResponse(), token);
}
[ExternalBridgeRequestHandlerMethod(typeof(CreateFolderRequest), RequestHandlerLoggingMode.LogRequestNameAndContent)]
public async Task OnCreateFolderRequest(CreateFolderRequest request, String token, ExternalBridgeReceiver receiver)
{
this.ThrowIfDisabled();
var dto = _manager.CreateFolder(request.Path, request.FolderName);
await receiver.SendGenericResponse(new CreateFolderResponse() { FolderItem = dto }, token);
}
[ExternalBridgeRequestHandlerMethod(typeof(PerformDiskSpaceOptimizationRequest), RequestHandlerLoggingMode.LogRequestNameAndContent)]
public async Task OnPerformDiskSpaceOptimizationRequest(PerformDiskSpaceOptimizationRequest request, String token, ExternalBridgeReceiver receiver)
{
var deletedBytes = _manager.PerformDiskSpaceOptimization();
await receiver.SendGenericResponse(new PerformDiskSpaceOptimizationResponse() { DeletedBytes = deletedBytes }, token);
}
[ExternalBridgeRequestHandlerMethod(typeof(GetLogFilesRequest), RequestHandlerLoggingMode.LogRequestNameAndContent)]
public async Task OnGetLogFilesRequest(GetLogFilesRequest request, String token, ExternalBridgeReceiver receiver)
{
FolderItem folder = null;
if (request.LogFileType == RemoteLogFileType.Application)
{
var fileLogger = LogManager.RegisteredLoggers.SingleOrDefault(x => x.GetType() == typeof(FileLogger)) as FileLogger;
if (fileLogger == null)
{
throw new InvalidOperationException("Could not locate the application file logger.");
}
folder = await _manager.GetFolder(fileLogger.Folder, false, "*.log") as FolderItem;
}
else
{
if (MachineOperator.EmbeddedLogsFolder == null)
{
throw new InvalidOperationException("The firmware file logger folder could not be read.");
}
folder = await _manager.GetFolder(MachineOperator.EmbeddedLogsFolder, false, "*.log") as FolderItem;
}
GetLogFilesResponse response = new GetLogFilesResponse();
foreach (var file in folder.Items.OfType().OrderByDescending(x => x.DateCreated).DistinctBy(x => x.Name))
{
response.LogFiles.Add(new RemoteLogFile()
{
DateModified = file.DateModified,
DateCreated = file.DateCreated,
Name = file.Name,
Path = file.Path,
Length = new FileInfo(file.Path).Length
});
}
await receiver.SendGenericResponse(response, token);
}
public void OnReceiverDisconnected(ExternalBridgeReceiver receiver)
{
if (_webRtcClients.ContainsKey(receiver))
{
try
{
LogManager.Log("External bridge receiver disconnected. Disposing file system service WebRTC channel...");
var webRtcTransporter = _webRtcClients[receiver];
_webRtcClients.Remove(receiver);
webRtcTransporter.Dispose();
}
catch (Exception ex)
{
LogManager.Log(ex, "Error disposing the WebRTC channel.");
}
}
}
}
}