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.PPC.Common.ExternalBridge; using Tango.Transport; using Tango.Transport.Transporters; using Tango.WebRTC; namespace Tango.PPC.Common.FileSystem { [TangoCreateWhenRegistered] public class DefaultFileSystemService : ExtendedObject, IFileSystemService, IExternalBridgeRequestHandler { private enum FileSystemOperationMode { Upload, Download } private class FileSystemOperation { public FileSystemOperationMode Mode { get; set; } public String Id { get; set; } public String Path { get; set; } public bool IsPathTempZip { get; set; } public FileSystemOperation(FileSystemOperationMode mode, String path) { Mode = mode; Id = Guid.NewGuid().ToString(); Path = path; } } 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))] public async void OnInitWebRtcRequest(InitWebRtcRequest request, String token, ExternalBridgeReceiver receiver) { try { if (!EnableWebRTC) { await receiver.SendErrorResponse(new InvalidOperationException("The file system service WebRTC channel is disabled on this machine."), token); return; } if (_webRtcClients.ContainsKey(receiver)) { _webRtcClients[receiver].Dispose(); } var webRtcAdapter = new WebRtcTransportAdapter(receiver, WebRtcTransportAdapterMode.Passive, request.DataChannelName); webRtcAdapter.Ready += (x, e) => { LogManager.Log("File System via WebRTC is ready."); }; BasicTransporter webRtcTransporter = new BasicTransporter(webRtcAdapter); webRtcTransporter = new BasicTransporter(webRtcAdapter); webRtcTransporter.ComponentName = "File System Passive WebRTC Transporter"; webRtcTransporter.UseKeepAlive = false; webRtcTransporter.RegisterRequestHandler(WebRtcChunkDownloadRequestReceived); await webRtcTransporter.Connect(); await receiver.SendGenericResponse(new InitWebRtcResponse(), token); _webRtcClients[receiver] = webRtcTransporter; } catch (Exception ex) { await receiver.SendErrorResponse(ex, token); } } private void WebRtcChunkDownloadRequestReceived(ITransporter transporter, ChunkDownloadRequest request, string token) { OnChunkDownloadRequest(request, token, transporter); } [ExternalBridgeRequestHandlerMethod(typeof(GetFileSystemItemRequest))] public async void OnGetFileSystemItemRequest(GetFileSystemItemRequest request, String token, ExternalBridgeReceiver receiver) { try { FileSystemItemDTO dto = _manager.GetFolder(request); await receiver.SendGenericResponse(new GetFileSystemItemResponse() { FileSystemItem = dto }, token); } catch (Exception ex) { await receiver.SendErrorResponse(ex, token); } } [ExternalBridgeRequestHandlerMethod(typeof(FileUploadRequest))] public async void OnFileUploadRequest(FileUploadRequest request, String token, ExternalBridgeReceiver receiver) { try { using (var stream = new FileStream(request.Path, FileMode.Create)) { } FileSystemOperation operation = new FileSystemOperation(FileSystemOperationMode.Upload, request.Path); _operations.Add(operation.Id, operation); await receiver.SendGenericResponse(new FileUploadResponse() { OperationId = operation.Id }, token); } catch (Exception ex) { await receiver.SendErrorResponse(ex, token); } } [ExternalBridgeRequestHandlerMethod(typeof(FileDownloadRequest))] public async void OnFileDownloadRequest(FileDownloadRequest request, String token, ExternalBridgeReceiver receiver) { try { if (!File.Exists(request.Path)) { await receiver.SendErrorResponse(new FileNotFoundException("Could not find the specified file."), token); return; } 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); } catch (Exception ex) { await receiver.SendErrorResponse(ex, token); } } [ExternalBridgeRequestHandlerMethod(typeof(FolderDownloadRequest))] public async void OnFolderDownloadRequest(FolderDownloadRequest request, String token, ExternalBridgeReceiver receiver) { try { if (!Directory.Exists(request.Path)) { await receiver.SendErrorResponse(new FileNotFoundException("Could not find the specified directory."), token); return; } 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); } catch (Exception ex) { await receiver.SendErrorResponse(ex, token); } } [ExternalBridgeRequestHandlerMethod(typeof(ChunkUploadRequest))] public async void OnChunkUploadRequest(ChunkUploadRequest request, String token, ExternalBridgeReceiver receiver) { try { FileSystemOperation operation; _operations.TryGetValue(request.OperationId, out operation); if (operation == null) { await receiver.SendErrorResponse(new ArgumentException("Invalid operation id."), token); return; } using (var stream = new FileStream(operation.Path, FileMode.Append)) { stream.Write(request.Data, 0, request.Data.Length); } await receiver.SendGenericResponse(new ChunkUploadResponse(), token, new TransportResponseConfig() { Priority = QueuePriority.Low }); } catch (Exception ex) { await receiver.SendErrorResponse(ex, token); } } [ExternalBridgeRequestHandlerMethod(typeof(ChunkDownloadRequest))] public async void OnChunkDownloadRequest(ChunkDownloadRequest request, String token, ITransporter receiver) { FileSystemOperation operation; _operations.TryGetValue(request.OperationId, out operation); if (operation == null) { await receiver.SendErrorResponse(new ArgumentException("Invalid operation id."), token); return; } FileStream stream = null; try { stream = new FileStream(operation.Path, FileMode.Open); stream.Position = request.Position; byte[] data = new byte[Math.Min(request.MaxChunkSize, stream.Length - stream.Position)]; 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(); await receiver.SendErrorResponse(ex, token); } } [ExternalBridgeRequestHandlerMethod(typeof(AbortOperationRequest))] public async void OnAbortOperationRequest(AbortOperationRequest request, String token, ExternalBridgeReceiver receiver) { FileSystemOperation operation; _operations.TryGetValue(request.OperationId, out operation); if (operation == null) { await receiver.SendErrorResponse(new ArgumentException("Invalid operation id."), token); return; } try { if (operation.Mode == FileSystemOperationMode.Upload) { if (File.Exists(operation.Path)) { File.Delete(operation.Path); } else if (Directory.Exists(operation.Path)) { Directory.Delete(operation.Path, true); } } else if (operation.IsPathTempZip) { if (File.Exists(operation.Path)) { File.Delete(operation.Path); } } await receiver.SendGenericResponse(new AbortOperationResponse(), token); } catch (Exception ex) { await receiver.SendErrorResponse(ex, token); } } [ExternalBridgeRequestHandlerMethod(typeof(MoveRequest))] public async void OnMoveRequest(MoveRequest request, String token, ExternalBridgeReceiver receiver) { try { _manager.Move(request); await receiver.SendGenericResponse(new MoveResponse(), token); } catch (Exception ex) { await receiver.SendErrorResponse(ex, token); } } [ExternalBridgeRequestHandlerMethod(typeof(CopyRequest))] public async void OnCopyRequest(CopyRequest request, String token, ExternalBridgeReceiver receiver) { try { _manager.Copy(request); await receiver.SendGenericResponse(new CopyResponse(), token); } catch (Exception ex) { await receiver.SendErrorResponse(ex, token); } } [ExternalBridgeRequestHandlerMethod(typeof(DeleteRequest))] public async void OnDeleteRequest(DeleteRequest request, String token, ExternalBridgeReceiver receiver) { try { _manager.Delete(request.Path); await receiver.SendGenericResponse(new DeleteResponse(), token); } catch (Exception ex) { await receiver.SendErrorResponse(ex, token); } } public void OnReceiverDisconnected(ExternalBridgeReceiver receiver) { if (_webRtcClients.ContainsKey(receiver)) { try { var webRtcTransporter = _webRtcClients[receiver]; _webRtcClients.Remove(receiver); webRtcTransporter.Dispose(); } catch (Exception ex) { LogManager.Log(ex, "Error disposing the WebRTC transporter."); } } } } }