public void StopTransfer(FileTransferIn transfer, bool deleteFile = false) { if (transfer.Status != FileTransferStatus.Finished && transfer.Status != FileTransferStatus.Error) { transfer.Status = FileTransferStatus.Canceled; } if (activeTransfers.Contains(transfer)) { activeTransfers.Remove(transfer); } transfer.Dispose(); if (deleteFile && File.Exists(transfer.FilePath)) { try { File.Delete(transfer.FilePath); } catch (Exception e) { DebugConsole.ThrowError("Failed to delete file \"" + transfer.FilePath + "\" (" + e.Message + ")"); } } }
private bool ValidateReceivedData(FileTransferIn fileTransfer, out string ErrorMessage) { ErrorMessage = ""; switch (fileTransfer.FileType) { case FileTransferType.Submarine: Stream stream = null; try { stream = SaveUtil.DecompressFiletoStream(fileTransfer.FilePath); } catch (Exception e) { ErrorMessage = "Loading received submarine ''" + fileTransfer.FileName + "'' failed! {" + e.Message + "}"; return(false); } if (stream == null) { ErrorMessage = "Decompressing received submarine file''" + fileTransfer.FilePath + "'' failed!"; return(false); } try { stream.Position = 0; XmlReaderSettings settings = new XmlReaderSettings(); settings.DtdProcessing = DtdProcessing.Prohibit; settings.IgnoreProcessingInstructions = true; using (var reader = XmlReader.Create(stream, settings)) { while (reader.Read()) { ; } } } catch { stream.Close(); stream.Dispose(); ErrorMessage = "Parsing file ''" + fileTransfer.FilePath + "'' failed! The file may not be a valid submarine file."; return(false); } stream.Close(); stream.Dispose(); break; case FileTransferType.CampaignSave: //TODO: verify that the received file is a valid save file break; } return(true); }
public void ReadMessage(IReadMessage inc) { System.Diagnostics.Debug.Assert(!activeTransfers.Any(t => t.Status == FileTransferStatus.Error || t.Status == FileTransferStatus.Canceled || t.Status == FileTransferStatus.Finished), "List of active file transfers contains entires that should have been removed"); byte transferMessageType = inc.ReadByte(); switch (transferMessageType) { case (byte)FileTransferMessageType.Initiate: { byte transferId = inc.ReadByte(); var existingTransfer = activeTransfers.Find(t => t.ID == transferId); finishedTransfers.RemoveAll(t => t.First == transferId); byte fileType = inc.ReadByte(); //ushort chunkLen = inc.ReadUInt16(); int fileSize = inc.ReadInt32(); string fileName = inc.ReadString(); if (existingTransfer != null) { if (fileType != (byte)existingTransfer.FileType || fileSize != existingTransfer.FileSize || fileName != existingTransfer.FileName) { GameMain.Client.CancelFileTransfer(transferId); DebugConsole.ThrowError("File transfer error: file transfer initiated with an ID that's already in use"); } else //resend acknowledgement packet { GameMain.Client.UpdateFileTransfer(transferId, 0); } return; } if (!ValidateInitialData(fileType, fileName, fileSize, out string errorMsg)) { GameMain.Client.CancelFileTransfer(transferId); DebugConsole.ThrowError("File transfer failed (" + errorMsg + ")"); return; } if (GameSettings.VerboseLogging) { DebugConsole.Log("Received file transfer initiation message: "); DebugConsole.Log(" File: " + fileName); DebugConsole.Log(" Size: " + fileSize); DebugConsole.Log(" ID: " + transferId); } string downloadFolder = downloadFolders[(FileTransferType)fileType]; if (!Directory.Exists(downloadFolder)) { try { Directory.CreateDirectory(downloadFolder); } catch (Exception e) { DebugConsole.ThrowError("Could not start a file transfer: failed to create the folder \"" + downloadFolder + "\".", e); return; } } FileTransferIn newTransfer = new FileTransferIn(inc.Sender, Path.Combine(downloadFolder, fileName), (FileTransferType)fileType) { ID = transferId, Status = FileTransferStatus.Receiving, FileSize = fileSize }; int maxRetries = 4; for (int i = 0; i <= maxRetries; i++) { try { newTransfer.OpenStream(); } catch (IOException e) { if (i < maxRetries) { DebugConsole.NewMessage("Failed to initiate a file transfer {" + e.Message + "}, retrying in 250 ms...", Color.Red); Thread.Sleep(250); } else { DebugConsole.NewMessage("Failed to initiate a file transfer {" + e.Message + "}", Color.Red); GameMain.Client.CancelFileTransfer(transferId); newTransfer.Status = FileTransferStatus.Error; OnTransferFailed(newTransfer); return; } } } activeTransfers.Add(newTransfer); GameMain.Client.UpdateFileTransfer(transferId, 0); //send acknowledgement packet } break; case (byte)FileTransferMessageType.TransferOnSameMachine: { byte transferId = inc.ReadByte(); byte fileType = inc.ReadByte(); string filePath = inc.ReadString(); if (GameSettings.VerboseLogging) { DebugConsole.Log("Received file transfer message on the same machine: "); DebugConsole.Log(" File: " + filePath); DebugConsole.Log(" ID: " + transferId); } if (!File.Exists(filePath)) { DebugConsole.ThrowError("File transfer on the same machine failed, file \"" + filePath + "\" not found."); GameMain.Client.CancelFileTransfer(transferId); return; } FileTransferIn directTransfer = new FileTransferIn(inc.Sender, filePath, (FileTransferType)fileType) { ID = transferId, Status = FileTransferStatus.Finished, FileSize = 0 }; OnFinished(directTransfer); } break; case (byte)FileTransferMessageType.Data: { byte transferId = inc.ReadByte(); var activeTransfer = activeTransfers.Find(t => t.Connection == inc.Sender && t.ID == transferId); if (activeTransfer == null) { //it's possible for the server to send some extra data //before it acknowledges that the download is finished, //so let's suppress the error message in that case finishedTransfers.RemoveAll(t => t.Second + 5.0 < Timing.TotalTime); if (!finishedTransfers.Any(t => t.First == transferId)) { GameMain.Client.CancelFileTransfer(transferId); DebugConsole.ThrowError("File transfer error: received data without a transfer initiation message"); } return; } int offset = inc.ReadInt32(); if (offset != activeTransfer.Received) { if (offset < activeTransfer.Received) { GameMain.Client.UpdateFileTransfer(activeTransfer.ID, activeTransfer.Received); } return; } int bytesToRead = inc.ReadUInt16(); if (activeTransfer.Received + bytesToRead > activeTransfer.FileSize) { GameMain.Client.CancelFileTransfer(transferId); DebugConsole.ThrowError("File transfer error: Received more data than expected (total received: " + activeTransfer.Received + ", msg received: " + (inc.LengthBytes - inc.BytePosition) + ", msg length: " + inc.LengthBytes + ", msg read: " + inc.BytePosition + ", filesize: " + activeTransfer.FileSize); activeTransfer.Status = FileTransferStatus.Error; StopTransfer(activeTransfer); return; } try { activeTransfer.ReadBytes(inc, bytesToRead); } catch (Exception e) { GameMain.Client.CancelFileTransfer(transferId); DebugConsole.ThrowError("File transfer error: " + e.Message); activeTransfer.Status = FileTransferStatus.Error; StopTransfer(activeTransfer, true); return; } if (activeTransfer.Status == FileTransferStatus.Finished) { GameMain.Client.UpdateFileTransfer(activeTransfer.ID, activeTransfer.Received, true); activeTransfer.Dispose(); if (ValidateReceivedData(activeTransfer, out string errorMessage)) { finishedTransfers.Add(new Pair <int, double>(transferId, Timing.TotalTime)); StopTransfer(activeTransfer); OnFinished(activeTransfer); } else { new GUIMessageBox("File transfer aborted", errorMessage); activeTransfer.Status = FileTransferStatus.Error; StopTransfer(activeTransfer, true); } } } break; case (byte)FileTransferMessageType.Cancel: { byte transferId = inc.ReadByte(); var matchingTransfer = activeTransfers.Find(t => t.Connection == inc.Sender && t.ID == transferId); if (matchingTransfer != null) { new GUIMessageBox("File transfer cancelled", "The server has cancelled the transfer of the file \"" + matchingTransfer.FileName + "\"."); StopTransfer(matchingTransfer); } break; } } }
public void ReadMessage(NetIncomingMessage inc) { System.Diagnostics.Debug.Assert(!activeTransfers.Any(t => t.Status == FileTransferStatus.Error || t.Status == FileTransferStatus.Canceled || t.Status == FileTransferStatus.Finished), "List of active file transfers contains entires that should have been removed"); byte transferMessageType = inc.ReadByte(); switch (transferMessageType) { case (byte)FileTransferMessageType.Initiate: { var existingTransfer = activeTransfers.Find(t => t.SequenceChannel == inc.SequenceChannel); if (existingTransfer != null) { GameMain.Client.CancelFileTransfer(inc.SequenceChannel); DebugConsole.ThrowError("File transfer error: file transfer initiated on a sequence channel that's already in use"); return; } byte fileType = inc.ReadByte(); ushort chunkLen = inc.ReadUInt16(); ulong fileSize = inc.ReadUInt64(); string fileName = inc.ReadString(); string errorMsg; if (!ValidateInitialData(fileType, fileName, fileSize, out errorMsg)) { GameMain.Client.CancelFileTransfer(inc.SequenceChannel); DebugConsole.ThrowError("File transfer failed (" + errorMsg + ")"); return; } if (GameSettings.VerboseLogging) { DebugConsole.Log("Received file transfer initiation message: "); DebugConsole.Log(" File: " + fileName); DebugConsole.Log(" Size: " + fileSize); DebugConsole.Log(" Sequence channel: " + inc.SequenceChannel); } string downloadFolder = downloadFolders[(FileTransferType)fileType]; if (!Directory.Exists(downloadFolder)) { try { Directory.CreateDirectory(downloadFolder); } catch (Exception e) { DebugConsole.ThrowError("Could not start a file transfer: failed to create the folder \"" + downloadFolder + "\".", e); return; } } FileTransferIn newTransfer = new FileTransferIn(inc.SenderConnection, Path.Combine(downloadFolder, fileName), (FileTransferType)fileType) { SequenceChannel = inc.SequenceChannel, Status = FileTransferStatus.Receiving, FileSize = fileSize }; int maxRetries = 4; for (int i = 0; i <= maxRetries; i++) { try { newTransfer.OpenStream(); } catch (IOException e) { if (i < maxRetries) { DebugConsole.NewMessage("Failed to initiate a file transfer {" + e.Message + "}, retrying in 250 ms...", Color.Red); Thread.Sleep(250); } else { DebugConsole.NewMessage("Failed to initiate a file transfer {" + e.Message + "}", Color.Red); GameMain.Client.CancelFileTransfer(inc.SequenceChannel); newTransfer.Status = FileTransferStatus.Error; OnTransferFailed(newTransfer); return; } } } activeTransfers.Add(newTransfer); } break; case (byte)FileTransferMessageType.TransferOnSameMachine: { byte fileType = inc.ReadByte(); string filePath = inc.ReadString(); if (GameSettings.VerboseLogging) { DebugConsole.Log("Received file transfer message on the same machine: "); DebugConsole.Log(" File: " + filePath); DebugConsole.Log(" Sequence channel: " + inc.SequenceChannel); } if (!File.Exists(filePath)) { DebugConsole.ThrowError("File transfer on the same machine failed, file \"" + filePath + "\" not found."); GameMain.Client.CancelFileTransfer(inc.SequenceChannel); return; } FileTransferIn directTransfer = new FileTransferIn(inc.SenderConnection, filePath, (FileTransferType)fileType) { SequenceChannel = inc.SequenceChannel, Status = FileTransferStatus.Finished, FileSize = 0 }; OnFinished(directTransfer); } break; case (byte)FileTransferMessageType.Data: var activeTransfer = activeTransfers.Find(t => t.Connection == inc.SenderConnection && t.SequenceChannel == inc.SequenceChannel); if (activeTransfer == null) { GameMain.Client.CancelFileTransfer(inc.SequenceChannel); DebugConsole.ThrowError("File transfer error: received data without a transfer initiation message"); return; } //allow one extra byte at the end for the 0 that identifies non-compressed messages if (activeTransfer.Received + (ulong)(inc.LengthBytes - inc.PositionInBytes) > activeTransfer.FileSize + 1) { GameMain.Client.CancelFileTransfer(inc.SequenceChannel); DebugConsole.ThrowError("File transfer error: Received more data than expected (total received: " + activeTransfer.Received + ", msg received: " + (inc.LengthBytes - inc.PositionInBytes) + ", msg length: " + inc.LengthBytes + ", msg read: " + inc.PositionInBytes + ", filesize: " + activeTransfer.FileSize); activeTransfer.Status = FileTransferStatus.Error; StopTransfer(activeTransfer); return; } try { activeTransfer.ReadBytes(inc); } catch (Exception e) { GameMain.Client.CancelFileTransfer(inc.SequenceChannel); DebugConsole.ThrowError("File transfer error: " + e.Message); activeTransfer.Status = FileTransferStatus.Error; StopTransfer(activeTransfer, true); return; } if (activeTransfer.Status == FileTransferStatus.Finished) { activeTransfer.Dispose(); if (ValidateReceivedData(activeTransfer, out string errorMessage)) { StopTransfer(activeTransfer); OnFinished(activeTransfer); } else { new GUIMessageBox("File transfer aborted", errorMessage); activeTransfer.Status = FileTransferStatus.Error; StopTransfer(activeTransfer, true); } } break; case (byte)FileTransferMessageType.Cancel: byte sequenceChannel = inc.ReadByte(); var matchingTransfer = activeTransfers.Find(t => t.Connection == inc.SenderConnection && t.SequenceChannel == sequenceChannel); if (matchingTransfer != null) { new GUIMessageBox("File transfer cancelled", "The server has cancelled the transfer of the file \"" + matchingTransfer.FileName + "\"."); StopTransfer(matchingTransfer); } break; } }
public void ReadMessage(NetIncomingMessage inc) { if (GameMain.Server != null) { throw new InvalidOperationException("Receiving files when a server is running is not allowed"); } System.Diagnostics.Debug.Assert(!activeTransfers.Any(t => t.Status == FileTransferStatus.Error || t.Status == FileTransferStatus.Canceled || t.Status == FileTransferStatus.Finished), "List of active file transfers contains entires that should have been removed"); byte transferMessageType = inc.ReadByte(); switch (transferMessageType) { case (byte)FileTransferMessageType.Initiate: var existingTransfer = activeTransfers.Find(t => t.SequenceChannel == inc.SequenceChannel); if (existingTransfer != null) { GameMain.Client.CancelFileTransfer(inc.SequenceChannel); DebugConsole.ThrowError("File transfer error: file transfer initiated on a sequence channel that's already in use"); return; } byte fileType = inc.ReadByte(); ushort chunkLen = inc.ReadUInt16(); ulong fileSize = inc.ReadUInt64(); string fileName = inc.ReadString(); string errorMsg; if (!ValidateInitialData(fileType, fileName, fileSize, out errorMsg)) { GameMain.Client.CancelFileTransfer(inc.SequenceChannel); DebugConsole.ThrowError("File transfer failed (" + errorMsg + ")"); return; } if (GameSettings.VerboseLogging) { DebugConsole.Log("Received file transfer initiation message: "); DebugConsole.Log(" File: " + fileName); DebugConsole.Log(" Size: " + fileSize); DebugConsole.Log(" Sequence channel: " + inc.SequenceChannel); } if (!Directory.Exists(downloadFolder)) { try { Directory.CreateDirectory(downloadFolder); } catch (Exception e) { DebugConsole.ThrowError("Could not start a file transfer: failed to create the folder \"" + downloadFolder + "\".", e); return; } } var newTransfer = new FileTransferIn(inc.SenderConnection, Path.Combine(downloadFolder, fileName), (FileTransferType)fileType); newTransfer.SequenceChannel = inc.SequenceChannel; newTransfer.Status = FileTransferStatus.Receiving; newTransfer.FileSize = fileSize; activeTransfers.Add(newTransfer); break; case (byte)FileTransferMessageType.Data: var activeTransfer = activeTransfers.Find(t => t.Connection == inc.SenderConnection && t.SequenceChannel == inc.SequenceChannel); if (activeTransfer == null) { GameMain.Client.CancelFileTransfer(inc.SequenceChannel); DebugConsole.ThrowError("File transfer error: received data without a transfer initiation message"); return; } if (activeTransfer.Received + (ulong)(inc.LengthBytes - inc.PositionInBytes) > activeTransfer.FileSize) { GameMain.Client.CancelFileTransfer(inc.SequenceChannel); DebugConsole.ThrowError("File transfer error: Received more data than expected"); activeTransfer.Status = FileTransferStatus.Error; StopTransfer(activeTransfer); return; } try { activeTransfer.ReadBytes(inc); } catch (Exception e) { GameMain.Client.CancelFileTransfer(inc.SequenceChannel); DebugConsole.ThrowError("File transfer error: " + e.Message); activeTransfer.Status = FileTransferStatus.Error; StopTransfer(activeTransfer, true); return; } if (activeTransfer.Status == FileTransferStatus.Finished) { activeTransfer.Dispose(); string errorMessage = ""; if (ValidateReceivedData(activeTransfer, out errorMessage)) { OnFinished(activeTransfer); StopTransfer(activeTransfer); } else { new GUIMessageBox("File transfer aborted", errorMessage); activeTransfer.Status = FileTransferStatus.Error; StopTransfer(activeTransfer, true); } } break; case (byte)FileTransferMessageType.Cancel: byte sequenceChannel = inc.ReadByte(); var matchingTransfer = activeTransfers.Find(t => t.Connection == inc.SenderConnection && t.SequenceChannel == sequenceChannel); if (matchingTransfer != null) { new GUIMessageBox("File transfer cancelled", "The server has cancelled the transfer of the file \"" + matchingTransfer.FileName + "\"."); StopTransfer(matchingTransfer); } break; } }
private bool ValidateReceivedData(FileTransferIn fileTransfer, out string ErrorMessage) { ErrorMessage = ""; switch (fileTransfer.FileType) { case FileTransferType.Submarine: System.IO.Stream stream; try { stream = SaveUtil.DecompressFiletoStream(fileTransfer.FilePath); } catch (Exception e) { ErrorMessage = "Loading received submarine \"" + fileTransfer.FileName + "\" failed! {" + e.Message + "}"; return(false); } if (stream == null) { ErrorMessage = "Decompressing received submarine file \"" + fileTransfer.FilePath + "\" failed!"; return(false); } try { stream.Position = 0; XmlReaderSettings settings = new XmlReaderSettings { DtdProcessing = DtdProcessing.Prohibit, IgnoreProcessingInstructions = true }; using (var reader = XmlReader.Create(stream, settings)) { while (reader.Read()) { ; } } } catch { stream?.Close(); ErrorMessage = "Parsing file \"" + fileTransfer.FilePath + "\" failed! The file may not be a valid submarine file."; return(false); } stream?.Close(); break; case FileTransferType.CampaignSave: try { var files = SaveUtil.EnumerateContainedFiles(fileTransfer.FilePath); foreach (var file in files) { string extension = Path.GetExtension(file); if ((!extension.Equals(".sub", StringComparison.OrdinalIgnoreCase) && !file.Equals("gamesession.xml")) || file.CleanUpPathCrossPlatform(correctFilenameCase: false).Contains('/')) { ErrorMessage = $"Found unexpected file in \"{fileTransfer.FileName}\"! ({file})"; return(false); } } } catch (Exception e) { ErrorMessage = $"Loading received campaign save \"{fileTransfer.FileName}\" failed! {{{e.Message}}}"; return(false); } break; } return(true); }