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 }; Md5Hash.RemoveFromCache(directTransfer.FilePath); 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); Md5Hash.RemoveFromCache(activeTransfer.FilePath); 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; } } }