private void OnDownloadProgressAsync(DownloadProgressEventArgs e)
 {
     var handler = this.DownloadProgress;
     if (handler != null)
     {
         handler.BeginInvoke(this,
                             e,
                             handler.EndInvoke,
                             null);
     }
 }
        public async Task<bool> DownloadAsync(FtpFile ftpFile,
                                              Stream stream,
                                              CancellationToken cancellationToken)
        {
            using (await this._mutex.LockAsync(cancellationToken))
            {
                var controlComplexSocket = await this.EnsureConnectionAndAuthenticationAsync(cancellationToken);
                if (controlComplexSocket == null)
                {
                    return false;
                }

                {
                    var success = await this.GotoParentDirectoryAsync(controlComplexSocket,
                                                                      ftpFile,
                                                                      cancellationToken);
                    if (!success)
                    {
                        return false;
                    }
                }

                long bytesTotal;

                // sending SIZE
                // reading SIZE
                {
                    var success = await this.SendAndLogAsync(controlComplexSocket,
                                                             cancellationToken,
                                                             "SIZE {0}",
                                                             ftpFile.FileName);
                    if (!success)
                    {
                        return false;
                    }

                    var ftpReply = await this.ReceiveAndLogAsync(controlComplexSocket,
                                                                 cancellationToken);
                    if (!ftpReply.Success)
                    {
                        return false;
                    }

                    if (!long.TryParse(ftpReply.ResponseMessage,
                                       out bytesTotal))
                    {
                        return false;
                    }
                }

                // sending PASV
                // reading PASV
                var transferComplexSocket = await this.GetPassiveComplexSocketAsync(controlComplexSocket,
                                                                                    cancellationToken);
                if (transferComplexSocket == null)
                {
                    return false;
                }

                {
                    FtpReply ftpReply;

                    using (transferComplexSocket)
                    {
                        // sending RETR
                        // open transfer socket
                        // reading RETR (150 Opening BINARY mode data connection...)
                        var success = await this.SendAndLogAsync(controlComplexSocket,
                                                                 cancellationToken,
                                                                 "RETR {0}",
                                                                 ftpFile.FileName);
                        if (!success)
                        {
                            return false;
                        }

                        success = await transferComplexSocket.ConnectAsync(cancellationToken);
                        if (!success)
                        {
                            return false;
                        }

                        ftpReply = await this.ReceiveAndLogAsync(controlComplexSocket,
                                                                 cancellationToken);
                        if (!ftpReply.Success)
                        {
                            return false;
                        }

                        {
                            this.WaitBeforeReceive();

                            // reading transfer socket
                            var rawFtpResponse = await transferComplexSocket.Socket.ReceiveAsync(this.ChunkReceiveBufferSize,
                                                                                                 transferComplexSocket.GetSocketAsyncEventArgs,
                                                                                                 cancellationToken,
                                                                                                 bytesTotal,
                                                                                                 bytesReceived =>
                                                                                                 {
                                                                                                     var downloadProgressEventArgs = new DownloadProgressEventArgs(bytesReceived,
                                                                                                                                                                   bytesTotal);
                                                                                                     this.OnDownloadProgressAsync(downloadProgressEventArgs);
                                                                                                 });
                            if (!rawFtpResponse.Success)
                            {
                                return false;
                            }

                            stream.Write(rawFtpResponse.Buffer,
                                         0,
                                         rawFtpResponse.Buffer.Length);
                        }
                    }

                    if (!ftpReply.Completed)
                    {
                        // reading RETR (226 Transfer complete)
                        ftpReply = await this.ReceiveAndLogAsync(controlComplexSocket,
                                                                 cancellationToken);
                        if (!ftpReply.Success)
                        {
                            return false;
                        }
                    }
                }
            }

            return true;
        }