コード例 #1
0
ファイル: SCPClient.cs プロジェクト: poderosaproject/poderosa
        private bool UploadDirectory(string fullPath, SCPChannelStream stream, bool preserveTime,
                        Cancellation cancellation,
                        SCPFileTransferProgressDelegate progressDelegate)
        {
            Debug.Assert(fullPath != null);

            if (cancellation != null && cancellation.IsRequested) {
                return false;   // cancel
            }

            string directoryName = Path.GetFileName(fullPath);
            if (directoryName != null) { // not a root directory
                if (progressDelegate != null)
                    progressDelegate(fullPath, directoryName, SCPFileTransferStatus.CreateDirectory, 0, 0);

                if (preserveTime) {
                    SendModTime(
                        stream,
                        Directory.GetLastWriteTimeUtc(fullPath),
                        Directory.GetLastAccessTimeUtc(fullPath)
                    );
                }

                string line = new StringBuilder()
                    .Append('D')
                    .Append(GetPermissionsText(true))
                    .Append(" 0 ")
                    .Append(directoryName)
                    .Append('\n')
                    .ToString();
                stream.Write(_encoding.GetBytes(line));
                CheckResponse(stream);

                if (progressDelegate != null)
                    progressDelegate(fullPath, directoryName, SCPFileTransferStatus.DirectoryCreated, 0, 0);
            }

            foreach (String fullSubDirPath in Directory.GetDirectories(fullPath)) {
                bool continued = UploadDirectory(fullSubDirPath, stream, preserveTime, cancellation, progressDelegate);
                if (!continued)
                    return false;   // cancel
            }

            foreach (String fullFilePath in Directory.GetFiles(fullPath)) {
                bool continued = UploadFile(fullFilePath, stream, preserveTime, cancellation, progressDelegate);
                if (!continued)
                    return false;   // cancel
            }

            if (directoryName != null) { // not a root directory
                string line = "E\n";
                stream.Write(_encoding.GetBytes(line));
                CheckResponse(stream);
            }

            return true;    // continue
        }
コード例 #2
0
ファイル: SCPClient.cs プロジェクト: poderosaproject/poderosa
        private bool UploadFile(string fullPath, SCPChannelStream stream, bool preserveTime,
                        Cancellation cancellation,
                        SCPFileTransferProgressDelegate progressDelegate)
        {
            Debug.Assert(fullPath != null);

            string fileName = Path.GetFileName(fullPath);
            FileInfo fileInfo = new FileInfo(fullPath);
            long fileSize = fileInfo.Length;
            ulong transmitted = 0;

            if (progressDelegate != null)
                progressDelegate(fullPath, fileName, SCPFileTransferStatus.Open, (ulong)fileSize, transmitted);

            if (preserveTime) {
                SendModTime(
                    stream,
                    File.GetLastWriteTimeUtc(fullPath),
                    File.GetLastAccessTimeUtc(fullPath)
                );
            }

            using (FileStream fileStream = new FileStream(fullPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) {
                string line = new StringBuilder()
                    .Append('C')
                    .Append(GetPermissionsText(false))
                    .Append(' ')
                    .Append(fileSize.ToString(NumberFormatInfo.InvariantInfo))
                    .Append(' ')
                    .Append(fileName)
                    .Append('\n')
                    .ToString();
                stream.Write(_encoding.GetBytes(line));
                CheckResponse(stream);

                byte[] buff = new byte[stream.GetPreferredDatagramSize()];

                long remain = fileSize;
                while (remain > 0) {
                    if (cancellation != null && cancellation.IsRequested) {
                        if (progressDelegate != null)
                            progressDelegate(fullPath, fileName, SCPFileTransferStatus.CompletedAbort, (ulong)fileSize, transmitted);
                        return false;   // cancel
                    }

                    int readLength = fileStream.Read(buff, 0, buff.Length);
                    if (readLength <= 0)
                        break;
                    if (readLength > remain)
                        readLength = (int)remain;
                    stream.Write(buff, readLength);
                    remain -= readLength;

                    transmitted += (ulong)readLength;
                    if (progressDelegate != null)
                        progressDelegate(fullPath, fileName, SCPFileTransferStatus.Transmitting, (ulong)fileSize, transmitted);
                }

                stream.Write(ZERO);
                CheckResponse(stream);
            }

            if (progressDelegate != null)
                progressDelegate(fullPath, fileName, SCPFileTransferStatus.CompletedSuccess, (ulong)fileSize, transmitted);

            return true;
        }
コード例 #3
0
ファイル: SCPClient.cs プロジェクト: poderosaproject/poderosa
        private bool CreateDirectory(SCPChannelStream stream, string directoryPath, SCPModTime modTime,
                            Cancellation cancellation,
                            SCPFileTransferProgressDelegate progressDelegate)
        {
            if (cancellation != null && cancellation.IsRequested) {
                return false;   // cancel
            }

            string directoryName = Path.GetFileName(directoryPath);

            if (progressDelegate != null)
                progressDelegate(directoryPath, directoryName, SCPFileTransferStatus.CreateDirectory, 0, 0);

            if (!Directory.Exists(directoryPath)) { // skip if already exists
                try {
                    Directory.CreateDirectory(directoryPath);
                }
                catch (Exception e) {
                    SendError(stream, "failed to create a directory");
                    throw new SCPClientException("Failed to create a directory: " + directoryPath, e);
                }
            }

            if (modTime != null) {
                try {
                    Directory.SetLastWriteTimeUtc(directoryPath, modTime.MTime);
                    Directory.SetLastAccessTimeUtc(directoryPath, modTime.ATime);
                }
                catch (Exception e) {
                    SendError(stream, "failed to modify time of a directory");
                    throw new SCPClientException("Failed to modify time of a directory: " + directoryPath, e);
                }
            }

            stream.Write(ZERO);

            if (progressDelegate != null)
                progressDelegate(directoryPath, directoryName, SCPFileTransferStatus.DirectoryCreated, 0, 0);

            return true;
        }
コード例 #4
0
ファイル: SCPClient.cs プロジェクト: poderosaproject/poderosa
        private bool CreateFile(SCPChannelStream stream, string filePath, SCPEntry entry, SCPModTime modTime,
                    Cancellation cancellation,
                    SCPFileTransferProgressDelegate progressDelegate)
        {
            string fileName = Path.GetFileName(filePath);
            ulong transmitted = 0;

            if (progressDelegate != null)
                progressDelegate(filePath, fileName, SCPFileTransferStatus.Open, (ulong)entry.FileSize, transmitted);

            FileStream fileStream;
            try {
                fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write, FileShare.Read);
            }
            catch (Exception e) {
                SendError(stream, "failed to create a file");
                throw new SCPClientException("Failed to create a file: " + filePath, e);
            }
            stream.Write(ZERO);

            using (fileStream) {
                byte[] buff = new byte[FILE_TRANSFER_BLOCK_SIZE];
                long remain = entry.FileSize;
                try {
                    while (remain > 0) {
                        if (cancellation != null && cancellation.IsRequested) {
                            if (progressDelegate != null)
                                progressDelegate(filePath, fileName, SCPFileTransferStatus.CompletedAbort, (ulong)entry.FileSize, transmitted);
                            return false;   // cancel
                        }

                        int maxLength = (int)Math.Min((long)buff.Length, remain);
                        int readLength = stream.Read(buff, maxLength, _protocolTimeout);
                        fileStream.Write(buff, 0, readLength);
                        remain -= readLength;

                        transmitted += (ulong)readLength;
                        if (progressDelegate != null)
                            progressDelegate(filePath, fileName, SCPFileTransferStatus.Transmitting, (ulong)entry.FileSize, transmitted);
                    }
                }
                catch (Exception e) {
                    SendError(stream, "failed to write to a file");
                    throw new SCPClientException("Failed to write to a file: " + filePath, e);
                }
            }

            if (modTime != null) {
                try {
                    File.SetLastWriteTimeUtc(filePath, modTime.MTime);
                    File.SetLastAccessTimeUtc(filePath, modTime.ATime);
                }
                catch (Exception e) {
                    SendError(stream, "failed to modify time of a file");
                    throw new SCPClientException("Failed to modify time of a file: " + filePath, e);
                }
            }

            CheckResponse(stream);
            stream.Write(ZERO);

            if (progressDelegate != null)
                progressDelegate(filePath, fileName, SCPFileTransferStatus.CompletedSuccess, (ulong)entry.FileSize, transmitted);

            return true;
        }
コード例 #5
0
ファイル: SCPClient.cs プロジェクト: poderosaproject/poderosa
        /// <summary>
        /// Download files or directories.
        /// </summary>
        /// <remarks>
        /// <para>Unfortunately, Granados sends a command line in the ASCII encoding.
        /// So the "remotePath" must be an ASCII text.</para>
        /// </remarks>
        /// <param name="remotePath">Remote path (Unix path)</param>
        /// <param name="localPath">Local path (Windows' path)</param>
        /// <param name="recursive">Specifies recursive mode</param>
        /// <param name="preserveTime">Specifies to preserve time of the directory or file.</param>
        /// <param name="cancellation">An object to request the cancellation. Set null if the cancellation is not needed.</param>
        /// <param name="progressDelegate">Delegate to notify progress. Set null if notification is not needed.</param>
        public void Download(string remotePath, string localPath, bool recursive, bool preserveTime,
                        Cancellation cancellation,
                        SCPFileTransferProgressDelegate progressDelegate)
        {
            if (!IsAscii(remotePath))
                throw new SCPClientException("Remote path must consist of ASCII characters.");

            string absLocalPath = Path.GetFullPath(localPath);

            string command = "scp -f ";
            if (recursive)
                command += "-r ";
            if (preserveTime)
                command += "-p ";
            command += EscapeUnixPath(remotePath);

            string localBasePath = null;    // local directory to store
            SCPModTime modTime = null;

            Stack<string> localBasePathStack = new Stack<string>();

            using (SCPChannelStream stream = new SCPChannelStream()) {
                stream.Open(_connection, command, _protocolTimeout);
                stream.Write(ZERO);

                while (true) {
                    byte[] lineBytes = stream.ReadUntil(LF, _protocolTimeout);
                    if (lineBytes[0] == 1 || lineBytes[0] == 2) {
                        // Warning or Error
                        string message = _encoding.GetString(lineBytes, 1, lineBytes.Length - 2);
                        throw new SCPClientException(message);
                    }

                    if (lineBytes[0] == 0x43 /*'C'*/ || lineBytes[0] == 0x44 /*'D'*/) {
                        SCPEntry entry;
                        try {
                            entry = ParseEntry(lineBytes);
                        }
                        catch (Exception e) {
                            SendError(stream, e.Message);
                            throw;
                        }

                        if (entry.IsDirectory) {
                            string directoryPath = DeterminePathToCreate(localBasePath, absLocalPath, entry);
                            bool continued = CreateDirectory(stream, directoryPath, modTime, cancellation, progressDelegate);
                            if (!continued)
                                break;
                            modTime = null;
                            localBasePathStack.Push(localBasePath);
                            localBasePath = directoryPath;
                        }
                        else {
                            string filePath = DeterminePathToCreate(localBasePath, absLocalPath, entry);
                            bool continued = CreateFile(stream, filePath, entry, modTime, cancellation, progressDelegate);
                            if (!continued)
                                break;
                            modTime = null;
                            if (!recursive)
                                break;
                        }
                    }
                    else if (lineBytes[0] == 0x54 /*'T'*/) {
                        if (preserveTime) {
                            try {
                                modTime = ParseModTime(lineBytes);
                            }
                            catch (Exception e) {
                                SendError(stream, e.Message);
                                throw;
                            }
                        }
                        stream.Write(ZERO);
                    }
                    else if (lineBytes[0] == 0x45 /*'E'*/) {
                        if (localBasePathStack.Count > 0) {
                            localBasePath = localBasePathStack.Pop();
                            if (localBasePath == null)
                                break;
                        }
                        stream.Write(ZERO);
                    }
                    else {
                        SendError(stream, "Invalid control");
                        throw new SCPClientException("Invalid control");
                    }
                }

            }
        }
コード例 #6
0
ファイル: SCPClient.cs プロジェクト: poderosaproject/poderosa
        /// <summary>
        /// Upload files or directories.
        /// </summary>
        /// <remarks>
        /// <para>Unfortunately, Granados sends a command line in the ASCII encoding.
        /// So the "remotePath" must be an ASCII text.</para>
        /// </remarks>
        /// <param name="localPath">Local path (Windows' path)</param>
        /// <param name="remotePath">Remote path (Unix path)</param>
        /// <param name="recursive">Specifies recursive mode</param>
        /// <param name="preserveTime">Specifies to preserve time of the directory or file.</param>
        /// <param name="cancellation">An object to request the cancellation. Set null if the cancellation is not needed.</param>
        /// <param name="progressDelegate">Delegate to notify progress. Set null if notification is not needed.</param>
        public void Upload(string localPath, string remotePath, bool recursive, bool preserveTime,
                            Cancellation cancellation,
                            SCPFileTransferProgressDelegate progressDelegate)
        {
            if (!IsAscii(remotePath))
                throw new SCPClientException("Remote path must consist of ASCII characters.");

            bool isDirectory = Directory.Exists(localPath);
            if (!File.Exists(localPath) && !isDirectory)
                throw new SCPClientException("File or directory not found: " + localPath);

            if (isDirectory && !recursive)
                throw new SCPClientException("Cannot copy directory in non-recursive mode");

            string absLocalPath = Path.GetFullPath(localPath);

            string command = "scp -t ";
            if (recursive)
                command += "-r ";
            if (preserveTime)
                command += "-p ";
            command += EscapeUnixPath(remotePath);

            using (SCPChannelStream stream = new SCPChannelStream()) {
                stream.Open(_connection, command, _protocolTimeout);
                CheckResponse(stream);

                if (isDirectory) {  // implies recursive mode
                    UploadDirectory(absLocalPath, stream, preserveTime, cancellation, progressDelegate);
                }
                else {
                    UploadFile(absLocalPath, stream, preserveTime, cancellation, progressDelegate);
                }
            }
        }
コード例 #7
0
ファイル: SFTPForm.cs プロジェクト: FNKGino/poderosa
        private void SFTPDownload(string[] remoteFiles, string localDirectoryPath)
        {
            _fileTransferCancellation = new Cancellation();

            BeginSFTPThread(delegate() {
                ClearProgressBar();

                Log("=== DOWNLOAD ===");

                string localFullPath = Path.GetFullPath(localDirectoryPath);

                bool continued = true;
                try {
                    bool overwite = false;
                    foreach (string remotePath in remoteFiles) {
                        continued = SFTPDownload_DownloadRecursively(remotePath, localFullPath, ref overwite);
                        if (!continued)
                            break;
                    }
                }
                finally {
                    ClearProgressBar();
                }

                _fileTransferCancellation = null;

                Log("DOWNLOAD " + (continued ? "completed." : "canceled."));
            }, true, true);
        }
コード例 #8
0
ファイル: SFTPForm.cs プロジェクト: FNKGino/poderosa
        private void SFTPUpload(string[] localFiles, string remoteDirectoryPath)
        {
            if (!CanExecuteSFTP())
                return;

            _fileTransferCancellation = new Cancellation();

            BeginSFTPThread(delegate() {
                ClearProgressBar();

                Log("=== UPLOAD ===");
                Log("Remote: " + remoteDirectoryPath);

                bool continued = true;
                try {
                    bool overwite = false;
                    foreach (string localFile in localFiles) {
                        string localFullPath = Path.GetFullPath(localFile);
                        continued = SFTPUpload_UploadRecursively(localFullPath, remoteDirectoryPath, ref overwite);
                        if (!continued)
                            break;
                    }
                }
                finally {
                    ClearProgressBar();
                }

                Log("UPLOAD " + (continued ? "completed." : "canceled."));

                SFTPOpenDirectory_Core(remoteDirectoryPath, true);

                _fileTransferCancellation = null;
            }, true, true);
        }
コード例 #9
0
ファイル: SCPForm.cs プロジェクト: FNKGino/poderosa
        private void StartUpload(string[] localFiles)
        {
            Debug.Assert(localFiles != null);

            if (_scp == null || _scpExecuting)
                return;

            Debug.Assert(_scpThread == null || !_scpThread.IsAlive);

            string remotePath;
            if (!TryGetRemotePath(out remotePath))
                return;

            bool recursive = this.checkRecursive.Checked;
            bool preseveTime = this.checkPreserveTime.Checked;

            _fileTransferCancellation = new Cancellation();

            ChangeExecutingState(true);

            _scpThread = new Thread((ThreadStart)
                delegate() {
                    UploadThread(localFiles, remotePath, recursive, preseveTime);

                    ChangeExecutingState(false);
                    _fileTransferCancellation = null;
                });
            _scpThread.Start();
        }
コード例 #10
0
ファイル: SFTPClient.cs プロジェクト: yiyi99/poderosa
        /// <summary>
        /// Upload a file.
        /// </summary>
        /// 
        /// <param name="localPath">Local file path to upload.</param>
        /// <param name="remotePath">Remote file path to write.</param>
        /// <param name="cancellation">An object to request the cancellation. Set null if the cancellation is not needed.</param>
        /// <param name="progressDelegate">Delegate to notify progress. Set null if notification is not needed.</param>
        /// 
        /// <exception cref="SFTPClientErrorException">Operation failed.</exception>
        /// <exception cref="SFTPClientTimeoutException">Timeout has occured.</exception>
        /// <exception cref="SFTPClientInvalidStatusException">Invalid status.</exception>
        /// <exception cref="SFTPClientException">Error.</exception>
        /// <exception cref="Exception">Error.</exception>
        public void UploadFile(string localPath, string remotePath, Cancellation cancellation, SFTPFileTransferProgressDelegate progressDelegate)
        {
            CheckStatus();

            uint requestId = ++_requestId;

            ulong transmitted = 0;

            Exception pendingException = null;

            try {
                bool isTransferring = false;
                bool hasError = false;
                bool isCanceled = false;
                byte[] handle = null;
                try {
                    using (FileStream fileStream = new FileStream(localPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) {

                        if (progressDelegate != null)
                            progressDelegate(SFTPFileTransferStatus.Open, transmitted);

                        isTransferring = true;
                        handle = OpenFile(requestId, remotePath, SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC);
                        isTransferring = false;

                        byte[] buff = new byte[FILE_TRANSFER_BLOCK_SIZE];

                        while (true) {
                            if (cancellation != null && cancellation.IsRequested) {
                                isCanceled = true;
                                break;
                            }

                            if (progressDelegate != null)
                                progressDelegate(SFTPFileTransferStatus.Transmitting, transmitted);

                            int length = fileStream.Read(buff, 0, buff.Length);

                            if (length > 0) {
                                isTransferring = true;
                                WriteFile(requestId, handle, transmitted, buff, length);
                                isTransferring = false;

                                transmitted += (ulong)length;
                            }

                            if (length == 0 || length < buff.Length)
                                break; // EOF
                        }
                    }
                }
                catch (Exception e) {
                    if (isTransferring)    // exception was raised in OpenFile() or WriteFile() ?
                        throw;

                    pendingException = e;
                    hasError = true;
                }

                if (handle != null) {
                    if (progressDelegate != null)
                        progressDelegate(SFTPFileTransferStatus.Close, transmitted);

                    CloseHandle(requestId, handle);
                }

                if (progressDelegate != null) {
                    SFTPFileTransferStatus status =
                        hasError ? SFTPFileTransferStatus.CompletedError :
                        isCanceled ? SFTPFileTransferStatus.CompletedAbort : SFTPFileTransferStatus.CompletedSuccess;
                    progressDelegate(status, transmitted);
                }
            }
            catch (Exception) {
                if (progressDelegate != null)
                    progressDelegate(SFTPFileTransferStatus.CompletedError, transmitted);
                throw;
            }

            if (pendingException != null)
                throw new SFTPClientException(pendingException.Message, pendingException);
        }
コード例 #11
0
ファイル: SFTPClient.cs プロジェクト: yiyi99/poderosa
        /// <summary>
        /// Download a file.
        /// </summary>
        /// <remarks>
        /// Even if download failed, local file is not deleted.
        /// </remarks>
        /// 
        /// <param name="remotePath">Remote file path to download.</param>
        /// <param name="localPath">Local file path to save.</param>
        /// <param name="cancellation">An object to request the cancellation. Set null if the cancellation is not needed.</param>
        /// <param name="progressDelegate">Delegate to notify progress. Set null if notification is not needed.</param>
        /// 
        /// <exception cref="SFTPClientErrorException">Operation failed.</exception>
        /// <exception cref="SFTPClientTimeoutException">Timeout has occured.</exception>
        /// <exception cref="SFTPClientInvalidStatusException">Invalid status.</exception>
        /// <exception cref="SFTPClientException">Error.</exception>
        /// <exception cref="Exception">Error.</exception>
        public void DownloadFile(string remotePath, string localPath, Cancellation cancellation, SFTPFileTransferProgressDelegate progressDelegate)
        {
            CheckStatus();

            uint requestId = ++_requestId;

            ulong transmitted = 0;

            Exception pendingException = null;

            try {
                if (progressDelegate != null)
                    progressDelegate(SFTPFileTransferStatus.Open, transmitted);

                byte[] handle = OpenFile(requestId, remotePath, SSH_FXF_READ);

                bool isTransferring = false;
                bool hasError = false;
                bool isCanceled = false;
                try {
                    using (FileStream fileStream = new FileStream(localPath, FileMode.Create, FileAccess.Write, FileShare.Read)) {
                        while (true) {
                            if (cancellation != null && cancellation.IsRequested) {
                                isCanceled = true;
                                break;
                            }

                            if (progressDelegate != null)
                                progressDelegate(SFTPFileTransferStatus.Transmitting, transmitted);

                            isTransferring = true;
                            byte[] data = ReadFile(requestId, handle, transmitted, FILE_TRANSFER_BLOCK_SIZE);
                            isTransferring = false;

                            if (data == null)
                                break; // EOF

                            if (data.Length > 0) {
                                fileStream.Write(data, 0, data.Length);
                                transmitted += (ulong)data.Length;
                            }
                        }
                    }
                }
                catch (Exception e) {
                    if (isTransferring)    // exception was raised in ReadFile() ?
                        throw;

                    pendingException = e;
                    hasError = true;
                }

                if (progressDelegate != null)
                    progressDelegate(SFTPFileTransferStatus.Close, transmitted);

                CloseHandle(requestId, handle);

                if (progressDelegate != null) {
                    SFTPFileTransferStatus status =
                        hasError ? SFTPFileTransferStatus.CompletedError :
                        isCanceled ? SFTPFileTransferStatus.CompletedAbort : SFTPFileTransferStatus.CompletedSuccess;
                    progressDelegate(status, transmitted);
                }
            }
            catch (Exception) {
                if (progressDelegate != null)
                    progressDelegate(SFTPFileTransferStatus.CompletedError, transmitted);
                throw;
            }

            if (pendingException != null)
                throw new SFTPClientException(pendingException.Message, pendingException);
        }
コード例 #12
0
        /// <summary>
        /// Upload a file.
        /// </summary>
        /// 
        /// <param name="localPath">Local file path to upload.</param>
        /// <param name="remotePath">Remote file path to write.</param>
        /// <param name="cancellation">An object to request the cancellation. Set null if the cancellation is not needed.</param>
        /// <param name="progressDelegate">Delegate to notify progress. Set null if notification is not needed.</param>
        /// 
        /// <exception cref="SFTPClientErrorException">Operation failed.</exception>
        /// <exception cref="SFTPClientTimeoutException">Timeout has occured.</exception>
        /// <exception cref="SFTPClientInvalidStatusException">Invalid status.</exception>
        /// <exception cref="SFTPClientException">Error.</exception>
        /// <exception cref="Exception">Error.</exception>
        public void UploadFile(string localPath, string remotePath, Cancellation cancellation, SFTPFileTransferProgressDelegate progressDelegate)
        {
            CheckStatus();

            uint requestId = ++_requestId;

            ulong transmitted = 0;

            Exception pendingException = null;

            bool hasError = false;
            bool dataFinished = false;
            byte[] handle = null;
            try {
                using (FileStream fileStream = new FileStream(localPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) {

                    if (progressDelegate != null) {
                        progressDelegate(SFTPFileTransferStatus.Open, transmitted);
                    }

                    handle = OpenFile(requestId, remotePath, SSH_FXF_WRITE | SSH_FXF_CREAT | SSH_FXF_TRUNC);

                    var dataToSend = new AtomicBox<DataFragment>();
                    var cancelTask = new CancellationTokenSource();
                    var cancelToken = cancelTask.Token;

                    Task readFileTask = Task.Run(() => {
                        // SSH_FXP_WRITE header part
                        //   4 bytes : packet length
                        //   1 byte  : message type (SSH_FXP_WRITE)
                        //   4 bytes : request id
                        //   4 bytes : handle length
                        //   n bytes : handle
                        //   8 bytes : offset
                        //   4 bytes : length of the datagram
                        int buffSize = _channel.MaxChannelDatagramSize - 25 - handle.Length;
                        // use multiple buffers cyclically.
                        // at least 3 buffers are required.
                        DataFragment[] dataFrags =
                            {
                                new DataFragment(new byte[buffSize], 0, buffSize),
                                new DataFragment(new byte[buffSize], 0, buffSize),
                                new DataFragment(new byte[buffSize], 0, buffSize),
                            };
                        int buffIndex = 0;

                        while (true) {
                            if (cancelToken.IsCancellationRequested) {
                                return;
                            }

                            DataFragment df = dataFrags[buffIndex];
                            buffIndex = (buffIndex + 1) % 3;

                            int length = fileStream.Read(df.Data, 0, df.Data.Length);
                            if (length == 0) {
                                df = null;  // end of file
                            }
                            else {
                                df.SetLength(0, length);
                            }

                            // pass to the sending loop
                            while (true) {
                                if (dataToSend.TrySet(df, 500)) {
                                    break;
                                }
                                if (cancelToken.IsCancellationRequested) {
                                    return;
                                }
                            }

                            if (length == 0) {
                                return; // end of file
                            }
                        }
                    }, cancelToken);

                    try {
                        while (true) {
                            if (cancellation != null && cancellation.IsRequested) {
                                break;
                            }

                            DataFragment dataFrag = null;
                            if (!dataToSend.TryGet(ref dataFrag, 1000)) {
                                throw new Exception("read error");
                            }

                            if (dataFrag == null) {
                                dataFinished = true;
                                break;
                            }

                            WriteFile(requestId, handle, transmitted, dataFrag.Data, dataFrag.Length);

                            transmitted += (ulong)dataFrag.Length;

                            if (progressDelegate != null) {
                                progressDelegate(SFTPFileTransferStatus.Transmitting, transmitted);
                            }
                        }
                    }
                    finally {
                        if (!readFileTask.IsCompleted) {
                            cancelTask.Cancel();
                        }
                        readFileTask.Wait();
                    }
                }   // using
            }
            catch (Exception e) {
                if (e is AggregateException) {
                    pendingException = ((AggregateException)e).InnerExceptions[0];
                }
                else {
                    pendingException = e;
                }

                hasError = true;
            }

            try {
                if (handle != null) {
                    if (progressDelegate != null)
                        progressDelegate(SFTPFileTransferStatus.Close, transmitted);

                    CloseHandle(requestId, handle);
                }

                if (progressDelegate != null) {
                    SFTPFileTransferStatus status =
                        hasError ? SFTPFileTransferStatus.CompletedError :
                        dataFinished ? SFTPFileTransferStatus.CompletedSuccess : SFTPFileTransferStatus.CompletedAbort;
                    progressDelegate(status, transmitted);
                }
            }
            catch (Exception) {
                if (progressDelegate != null) {
                    progressDelegate(SFTPFileTransferStatus.CompletedError, transmitted);
                }
                throw;
            }

            if (pendingException != null) {
                throw new SFTPClientException(pendingException.Message, pendingException);
            }
        }
コード例 #13
0
        /// <summary>
        /// Download a file.
        /// </summary>
        /// <remarks>
        /// Even if download failed, local file is not deleted.
        /// </remarks>
        /// 
        /// <param name="remotePath">Remote file path to download.</param>
        /// <param name="localPath">Local file path to save.</param>
        /// <param name="cancellation">An object to request the cancellation. Set null if the cancellation is not needed.</param>
        /// <param name="progressDelegate">Delegate to notify progress. Set null if notification is not needed.</param>
        /// 
        /// <exception cref="SFTPClientErrorException">Operation failed.</exception>
        /// <exception cref="SFTPClientTimeoutException">Timeout has occured.</exception>
        /// <exception cref="SFTPClientInvalidStatusException">Invalid status.</exception>
        /// <exception cref="SFTPClientException">Error.</exception>
        /// <exception cref="Exception">Error.</exception>
        public void DownloadFile(string remotePath, string localPath, Cancellation cancellation, SFTPFileTransferProgressDelegate progressDelegate)
        {
            CheckStatus();

            uint requestId = ++_requestId;

            ulong transmitted = 0;

            Exception pendingException = null;

            if (progressDelegate != null) {
                progressDelegate(SFTPFileTransferStatus.Open, transmitted);
            }

            byte[] handle;
            try {
                handle = OpenFile(requestId, remotePath, SSH_FXF_READ);
            }
            catch (Exception) {
                if (progressDelegate != null) {
                    progressDelegate(SFTPFileTransferStatus.CompletedError, transmitted);
                }
                throw;
            }

            bool hasError = false;
            bool dataFinished = false;
            try {
                using (FileStream fileStream = new FileStream(localPath, FileMode.Create, FileAccess.Write, FileShare.Read)) {

                    var dataToSave = new AtomicBox<DataFragment>();
                    var cancelTask = new CancellationTokenSource();
                    var cancelToken = cancelTask.Token;

                    Task writeFileTask = Task.Run(() => {
                        while (true) {
                            DataFragment df = null;
                            while (true) {
                                if (dataToSave.TryGet(ref df, 500)) {
                                    break;
                                }
                                if (cancelToken.IsCancellationRequested) {
                                    return;
                                }
                            }

                            if (df == null) {
                                dataFinished = true;
                                return; // end of file
                            }

                            fileStream.Write(df.Data, df.Offset, df.Length);
                        }
                    }, cancelToken);

                    try {
                        // fixed buffer size is used.
                        // it is very difficult to decide optimal buffer size
                        // because the server may change the packet size
                        // depending on the available window size.
                        const int buffSize = 0x10000;
                        // use multiple buffers cyclically.
                        // at least 3 buffers are required.
                        DataFragment[] dataFrags =
                            {
                                new DataFragment(new byte[buffSize], 0, buffSize),
                                new DataFragment(new byte[buffSize], 0, buffSize),
                                new DataFragment(new byte[buffSize], 0, buffSize),
                            };
                        int buffIndex = 0;

                        while (true) {
                            if (cancellation != null && cancellation.IsRequested) {
                                break;
                            }

                            DataFragment df = dataFrags[buffIndex];
                            buffIndex = (buffIndex + 1) % 3;

                            int length = ReadFile(requestId, handle, transmitted, buffSize, df.Data);
                            if (length == 0) {
                                df = null;  // end of file
                            } else {
                                df.SetLength(0, length);
                            }

                            // pass to the writing task
                            if (!dataToSave.TrySet(df, 1000)) {
                                throw new Exception("write error");
                            }

                            transmitted += (ulong)length;

                            if (progressDelegate != null) {
                                progressDelegate(SFTPFileTransferStatus.Transmitting, transmitted);
                            }

                            if (length == 0) {
                                writeFileTask.Wait(1000);
                                break; // EOF
                            }
                        }
                    }
                    finally {
                        if (!writeFileTask.IsCompleted) {
                            cancelTask.Cancel();
                        }
                        writeFileTask.Wait();
                    }
                }
            }
            catch (Exception e) {
                if (e is AggregateException) {
                    pendingException = ((AggregateException)e).InnerExceptions[0];
                }
                else {
                    pendingException = e;
                }

                hasError = true;
            }

            try {
                if (progressDelegate != null) {
                    progressDelegate(SFTPFileTransferStatus.Close, transmitted);
                }

                CloseHandle(requestId, handle);

                if (progressDelegate != null) {
                    SFTPFileTransferStatus status =
                        hasError ? SFTPFileTransferStatus.CompletedError :
                        dataFinished ? SFTPFileTransferStatus.CompletedSuccess : SFTPFileTransferStatus.CompletedAbort;
                    progressDelegate(status, transmitted);
                }
            }
            catch (Exception) {
                if (progressDelegate != null) {
                    progressDelegate(SFTPFileTransferStatus.CompletedError, transmitted);
                }
                throw;
            }

            if (pendingException != null) {
                throw new SFTPClientException(pendingException.Message, pendingException);
            }
        }