Пример #1
0
 /// <summary>
 /// Requests a remote file with its header. All further information is specfied in the <see cref="FTEventArgs"/>. After a <see cref="FileMeta"/> was received you can start the actual download with <see cref="ContinueAsync(FTEventArgs)"/>.
 /// </summary>
 /// <param name="e">Specifies an identifier for the remote file, a local path and many more information of the file to download.</param>
 public Task <bool> StartDownloadAsync(FTEventArgs e)
 {
     e.Assign(parent, this);
     e.Mode      = StreamMode.GetFile;
     currentItem = e;
     return(parent.Manager.SendPacketAsync(new P07OpenFileTransfer(e.Identifier, e.Mode)));
 }
Пример #2
0
        /// <summary>
        /// Accepts a DownloadHeader or DownloadFile request with custom metadata.
        /// </summary>
        /// <param name="e">The related request with many important information.</param>
        /// <param name="path">Local path where the file currently exists or will be stored.</param>
        /// <param name="meta">Only for file sending! The metadata to send especially for E2E server applications.</param>
        public async Task <bool> AcceptAsync(FTEventArgs e, string path, FileMeta meta)
        {
            e.Path = path;
            e.Assign(parent, this);
            e.FileMeta  = meta;
            currentItem = e;

            if (!await parent.Manager.SendPacketAsync(new P06Accepted(true, 7, ProblemCategory.None))) // accept the transfer
            {
                return(false);
            }
            if (currentItem.Mode == StreamMode.PushHeader || currentItem.Mode == StreamMode.PushFile) // start by sending the FileMeta
            {
                if (currentItem.FileMeta == null)
                {
                    currentItem.FileMeta = await FileMeta.FromFileAsync(path, ContentAlgorithm.None);
                }
                if (!await parent.Manager.SendPacketAsync(new P08FileHeader(currentItem.FileMeta.GetBinaryData(parent.ConnectionVersion.Value))))
                {
                    return(false);
                }
            }
            else if (meta != null)
            {
                throw new ArgumentException("You must not supply a FileMeta for a receive operation.", nameof(meta));
            }
            return(true);
        }
Пример #3
0
        private async void SendFileAsync(object state)
        {
            try
            {
                byte[] buffer   = new byte[parent.Settings.FTBlockSize];
                ulong  position = 0;
                int    count    = 0;
                do
                {
                    position = (ulong)currentItem.Stream.Position;
                    count    = await currentItem.Stream.ReadAsync(buffer, 0, buffer.Length);

                    var packet = new P09FileDataBlock(position, new ArraySegment <byte>(buffer, 0, count));
                    if (!await parent.Manager.SendPacketAsync(packet, background: true))
                    {
                        return;
                    }
                    currentItem.OnProgress();
                } while (count == buffer.Length);
                currentItem.Finish(success: true);
                currentItem = null;
            }
            catch (Exception ex)
            {
                parent.ExceptionHandler.CloseConnection(ex);
            }
        }
Пример #4
0
        internal async Task <bool> OnPacketReceivedAsync(P09FileDataBlock packet)
        {
            const string name = nameof(OnPacketReceivedAsync) + "(" + nameof(P09FileDataBlock) + ")";

            if (currentItem == null) // It may be more efficient not to close the connection but only to cancel the file transfer.
            {
                parent.ExceptionHandler.CloseConnection("InvalidPacket",
                                                        "Cannot resume file transfer for the received file data block.",
                                                        nameof(FTSocket), name);
                return(false);
            }
            if (currentItem.Mode != StreamMode.GetFile)
            {
                parent.ExceptionHandler.CloseConnection("InvalidPacket",
                                                        $"The running file transfer with mode {currentItem.Mode} is not supposed to receive a file data block.",
                                                        nameof(FTSocket), name);
                return(false);
            }
            if (currentItem.Stream == null)
            {
                parent.ExceptionHandler.CloseConnection("InvalidPacket",
                                                        "The request for the first FileDataBlock has not been sent yet.",
                                                        nameof(FTSocket), name);
                return(false);
            }
            try
            {
                await currentItem.Stream.WriteAsync(packet.DataBlock.Array, 0, packet.DataBlock.Count, currentItem.CancellationToken);
            }
            catch (Exception ex)
            {
                parent.ExceptionHandler.CloseConnection(ex);
                return(false);
            }
            currentItem.OnProgress();
            if (currentItem.Stream.Position == currentItem.FileMeta.Length)
            {
                if (!currentItem.Finish(success: true))
                {
                    return(false);
                }
                currentItem = null;
            }
            else if (currentItem.Stream.Position > currentItem.FileMeta.Length)
            {
                parent.ExceptionHandler.CloseConnection("EndOfFileExpected",
                                                        $"The file meta indicates a size of {currentItem.FileMeta.Length} bytes for this file " +
                                                        $"but the stream position is already at {currentItem.Stream.Position} bytes",
                                                        nameof(FTSocket), name);
            }

            if (parent.ConnectionVersion <= 2) // Previous versions to 1.3 need to request the counterpart to continue
            {
                return(await parent.Manager.SendPacketAsync(new P06Accepted(true, 9, ProblemCategory.None)));
            }

            return(true);
        }
Пример #5
0
        /// <summary>
        /// Cancels a pending request or a running file transfer.
        /// </summary>
        /// <param name="e"></param>
        public Task <bool> CancelAsync(FTEventArgs e)
        {
            Task <bool> t = parent.Manager.SendPacketAsync(new P06Accepted(false, 7, ProblemCategory.None)); // This packet cancels any request or running transfer.

            e.Assign(parent, this);                                                                          // This assignment is necessary for FTEventArgs to print exceptions and raise events.
            e.Finish(success: false);
            currentItem = null;
            return(t);
        }
Пример #6
0
        /// <summary>
        /// Requests the permission to upload a file with its header. All further information is specfied in the <see cref="FTEventArgs"/>.
        /// </summary>
        /// <param name="e">Specifies an remote identifier for the file, a local path and many more information of the file to upload.</param>
        public Task <bool> StartUploadAsync(FTEventArgs e)
        {
            e.Assign(parent, this);
            e.Mode      = StreamMode.PushFile;
            currentItem = e;
            var packet = new P07OpenFileTransfer(e.Identifier, e.Mode);

            packet.PrepareSend(parent.ConnectionVersion.Value);
            return(parent.Manager.SendPacketAsync(packet));
        }
Пример #7
0
 /// <summary>
 /// Continues a file receive operation after a <see cref="FileMeta"/> was received. Cryptographic keys stored in this <see cref="FileMeta"/> will be used if available.
 /// </summary>
 /// <param name="e">The associated file transfer operation to continue.</param>
 /// <returns>Returns true when the continuation succeeded.</returns>
 /// <exception cref="InvalidOperationException">When attempting to continue operations other than <see cref="StreamMode.GetFile"/>.</exception>
 public async Task <bool> ContinueAsync(FTEventArgs e)
 {
     if (e.Mode != StreamMode.GetFile)
     {
         throw new InvalidOperationException($"You cannot continue a file transfer operation with {e.Mode}. " +
                                             "Only file transfer with StreamMode.GetFile can be continued.");
     }
     else
     {
         if (!currentItem.OpenStream())
         {
             return(false);
         }
         return(await parent.Manager.SendPacketAsync(new P06Accepted(true, 8, ProblemCategory.None)));
     }
 }
Пример #8
0
        internal Task <bool> OnPacketReceivedAsync(P07OpenFileTransfer packet)
        {
            if (currentItem == null)
            {
                FTEventArgs e = new FTEventArgs(packet.Identifier, packet.StreamMode);
                currentItem = e;
                parent.ThreadManager.Post(() => Request?.Invoke(this, e));
#if DEBUG
                parent.Log($"FileTransfer with {e.Mode} and Identifier {e.Identifier} requested");
#endif
                return(Task.FromResult(true));
            }
            else // It may be more efficient not to close the connection but only to cancel the file transfer.
            {
                parent.ExceptionHandler.CloseConnection("InvalidRequest",
                                                        "A new file transfer was requested before the last one was finished or aborted.",
                                                        nameof(FTSocket), "OnPacketReceivedAsync(P07OpenFileTransfer)");
                return(Task.FromResult(false));
            }
        }
Пример #9
0
 /// <summary>
 /// Accepts any type of request and starts the file transfer.
 /// </summary>
 /// <param name="e">The related request with many important information.</param>
 /// <param name="path">Local path where the file currently exists or will be stored.</param>
 public Task <bool> AcceptAsync(FTEventArgs e, string path) => AcceptAsync(e, path, null);
Пример #10
0
        internal async Task <bool> OnPacketReceivedAsync(P06Accepted packet)
        {
            const string name = nameof(OnPacketReceivedAsync) + "(" + nameof(P06Accepted) + ")";

            if (currentItem == null) // It may be more efficient not to close the connection but only to cancel the file transfer.
            {
                parent.ExceptionHandler.CloseConnection("InvalidPacket",
                                                        "Cannot resume file transfer for the received accepted packet.",
                                                        nameof(FTSocket), name);
                return(false);
            }
            if (!packet.Accepted && packet.RelatedPacket == 7) // Cancellation is always made by denying P07OpenFileTransfer
            {
                if (!currentItem.Finish(success: false))
                {
                    return(false);
                }
                currentItem = null;
            }
            else if (packet.Accepted && packet.RelatedPacket == 7)
            {
                if ((currentItem.Mode == StreamMode.PushHeader || currentItem.Mode == StreamMode.PushFile) &&
                    !await parent.Manager.SendPacketAsync(new P08FileHeader(currentItem.FileMeta.GetBinaryData(parent.ConnectionVersion.Value))))
                {
                    return(false);
                }
                // counterpart accepted file transfer -> we have to sent the FileMeta first

                // No exceptions here because every request type get's an accepted packet
            }
            else if (packet.Accepted && packet.RelatedPacket == 8)
            {
                currentItem.OnFileMetaTransfered();
                if (currentItem.Mode == StreamMode.PushFile) // only StreamMode.PushFile wants to receive the file data
                {
                    if (!currentItem.OpenStream())
                    {
                        return(false);
                    }
                    if (parent.ConnectionVersion <= 2)
                    {
                        return(await SendBlockAsync());
                    }
                    else
                    {
                        return(ThreadPool.QueueUserWorkItem(SendFileAsync));
                    }
                }
                else if (currentItem.Mode != StreamMode.PushHeader)
                {
                    parent.ExceptionHandler.CloseConnection("InvalidPacket",
                                                            "The running file transfer is not supposed to receive an accepted packet for a file header.",
                                                            nameof(FTSocket), name);
                    return(false);
                }
            }
            else if (packet.Accepted && packet.RelatedPacket == 9)
            {
                if (parent.ConnectionVersion <= 2)
                {
                    if (currentItem.Mode != StreamMode.PushFile)
                    {
                        parent.ExceptionHandler.CloseConnection("InvalidPacket",
                                                                "The running file transfer is not supposed to receive an accepted packet for a file data block.",
                                                                nameof(FTSocket), name);
                        return(false);
                    }
                    else if (currentItem.Stream != null)
                    {
                        return(await SendBlockAsync());
                    }
                    else // accept for the last file data block
                    {
                        currentItem = null;
                    }
                }
                else
                {
                    parent.ExceptionHandler.CloseConnection("InvalidPacket",
                                                            "VSL 1.3 does not accept data block acknoledges anymore.",
                                                            nameof(FTSocket), name);
                    return(false);
                }
            }
            return(true);
        }