Beispiel #1
0
        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 + ")");
                }
            }
        }
Beispiel #2
0
        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);
        }
Beispiel #3
0
        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;
            }
            }
        }
Beispiel #4
0
        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;
            }
        }
Beispiel #5
0
        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;
            }
        }
Beispiel #6
0
        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);
        }