예제 #1
0
        /// <summary>
        /// Parses the recieved hash value into the FtpHash object
        /// </summary>
        private Match ParseHashValue(FtpReply reply, FtpHash hash)
        {
            Match m;

            // Current draft says the server should return this:
            // SHA-256 0-49 169cd22282da7f147cb491e559e9dd filename.ext
            if (!(m = Regex.Match(reply.Message,
                                  @"(?<algorithm>.+)\s" +
                                  @"(?<bytestart>\d+)-(?<byteend>\d+)\s" +
                                  @"(?<hash>.+)\s" +
                                  @"(?<filename>.+)")).Success)
            {
                // Current version of FileZilla returns this:
                // SHA-1 21c2ca15cf570582949eb59fb78038b9c27ffcaf
                m = Regex.Match(reply.Message, @"(?<algorithm>.+)\s(?<hash>.+)\s");
            }

            if (m != null && m.Success)
            {
                hash.Algorithm = FtpHashAlgorithms.FromString(m.Groups["algorithm"].Value);
                hash.Value     = m.Groups["hash"].Value;
            }
            else
            {
                LogStatus(FtpTraceLevel.Warn, "Failed to parse hash from: " + reply.Message);
            }

            return(m);
        }
        /// <summary>
        /// Tests if the specified directory exists on the server asynchronously. This
        /// method works by trying to change the working directory to
        /// the path specified. If it succeeds, the directory is changed
        /// back to the old working directory and true is returned. False
        /// is returned otherwise and since the CWD failed it is assumed
        /// the working directory is still the same.
        /// </summary>
        /// <param name='path'>The full or relative path of the directory to check for</param>
        /// <param name="token">The token that can be used to cancel the entire process</param>
        /// <returns>True if the directory exists. False otherwise.</returns>
        public async Task <bool> DirectoryExistsAsync(string path, CancellationToken token = default(CancellationToken))
        {
            string pwd;

            // don't verify args as blank/null path is OK
            //if (path.IsBlank())
            //	throw new ArgumentException("Required parameter is null or blank.", "path");

            LogFunc(nameof(DirectoryExistsAsync), new object[] { path });

            // quickly check if root path, then it always exists!
            var ftppath = path.GetFtpPath();

            if (ftppath == "." || ftppath == "./" || ftppath == "/")
            {
                return(true);
            }

            // check if a folder exists by changing the working dir to it
            pwd = await GetWorkingDirectoryAsync(token);

            if ((await ExecuteAsync("CWD " + ftppath, token)).Success)
            {
                FtpReply reply = await ExecuteAsync("CWD " + pwd.GetFtpPath(), token);

                if (!reply.Success)
                {
                    throw new FtpException("DirectoryExists(): Failed to restore the working directory.");
                }

                return(true);
            }

            return(false);
        }
예제 #3
0
        /// <summary>
        /// Performs a login on the server. This method is overridable so
        /// that the login procedure can be changed to support, for example,
        /// a FTP proxy.
        /// </summary>
        /// <exception cref="FtpAuthenticationException">On authentication failures</exception>
        /// <remarks>
        /// To handle authentication failures without retries, catch FtpAuthenticationException.
        /// </remarks>
        protected virtual async Task AuthenticateAsync(string userName, string password, string account, CancellationToken token)
        {
            // send the USER command along with the FTP username
            FtpReply reply = await ExecuteAsync("USER " + userName, token);

            // check the reply to the USER command
            if (!reply.Success)
            {
                throw new FtpAuthenticationException(reply);
            }

            // if it was accepted
            else if (reply.Type == FtpResponseType.PositiveIntermediate)
            {
                // send the PASS command along with the FTP password
                reply = await ExecuteAsync("PASS " + password, token);

                // fix for #620: some servers send multiple responses that must be read and decoded,
                // otherwise the connection is aborted and remade and it goes into an infinite loop
                var staleData = await ReadStaleDataAsync(false, true, true, token);

                if (staleData != null)
                {
                    var staleReply = new FtpReply();
                    if (DecodeStringToReply(staleData, ref staleReply) && !staleReply.Success)
                    {
                        throw new FtpAuthenticationException(staleReply);
                    }
                }

                // check the first reply to the PASS command
                if (!reply.Success)
                {
                    throw new FtpAuthenticationException(reply);
                }

                // only possible 3** here is `332 Need account for login`
                if (reply.Type == FtpResponseType.PositiveIntermediate)
                {
                    reply = await ExecuteAsync("ACCT " + account, token);

                    if (!reply.Success)
                    {
                        throw new FtpAuthenticationException(reply);
                    }
                    else
                    {
                        m_IsAuthenticated = true;
                    }
                }
                else if (reply.Type == FtpResponseType.PositiveCompletion)
                {
                    m_IsAuthenticated = true;
                }
            }
        }
예제 #4
0
        /// <summary>
        /// Checks if a file exists on the server asynchronously.
        /// </summary>
        /// <param name="path">The full or relative path to the file</param>
        /// <param name="token">The token that can be used to cancel the entire process</param>
        /// <returns>True if the file exists, false otherwise</returns>
        public async Task <bool> FileExistsAsync(string path, CancellationToken token = default(CancellationToken))
        {
            // verify args
            if (path.IsBlank())
            {
                throw new ArgumentException("Required parameter is null or blank.", "path");
            }

            path = path.GetFtpPath();

            LogFunc(nameof(FileExistsAsync), new object[] { path });

            // calc the absolute filepath
            path = await GetAbsolutePathAsync(path, token);

            // since FTP does not include a specific command to check if a file exists
            // here we check if file exists by attempting to get its filesize (SIZE)
            if (HasFeature(FtpCapability.SIZE))
            {
                // Fix #328: get filesize in ASCII or Binary mode as required by server
                FtpSizeReply sizeReply = new FtpSizeReply();
                await GetFileSizeInternalAsync(path, -1, token, sizeReply);

                // handle known errors to the SIZE command
                var sizeKnownError = CheckFileExistsBySize(sizeReply);
                if (sizeKnownError.HasValue)
                {
                    return(sizeKnownError.Value);
                }
            }

            // check if file exists by attempting to get its date modified (MDTM)
            if (HasFeature(FtpCapability.MDTM))
            {
                FtpReply reply = await ExecuteAsync("MDTM " + path, token);

                var ch = reply.Code[0];
                if (ch == '2')
                {
                    return(true);
                }

                if (ch == '5' && reply.Message.IsKnownError(FtpServerStrings.fileNotFound))
                {
                    return(false);
                }
            }

            // check if file exists by getting a name listing (NLST)
            string[] fileList = await GetNameListingAsync(path.GetFtpDirectoryName(), token);

            return(FileListings.FileExistsInNameListing(fileList, path));
        }
예제 #5
0
        /// <summary>
        /// Closes the connection and reads the server's reply
        /// </summary>
        public new FtpReply Close()
        {
            base.Close();

            try {
                if (ControlConnection != null)
                {
                    return(ControlConnection.CloseDataStream(this));
                }
            } finally {
                m_commandStatus = new FtpReply();
                m_control       = null;
            }

            return(new FtpReply());
        }
예제 #6
0
        /// <summary>
        /// Called during Connect(). Typically extended by FTP proxies.
        /// </summary>
        protected virtual void Handshake()
        {
            FtpReply reply;

            if (!(reply = GetReply()).Success)
            {
                if (reply.Code == null)
                {
                    throw new IOException("The connection was terminated before a greeting could be read.");
                }
                else
                {
                    throw new FtpCommandException(reply);
                }
            }
            HandshakeReply = reply;
        }
예제 #7
0
        /// <summary>
        /// Called during <see cref="ConnectAsync()"/>. Typically extended by FTP proxies.
        /// </summary>
        protected virtual async Task HandshakeAsync(CancellationToken token = default(CancellationToken))
        {
            FtpReply reply;

            if (!(reply = await GetReplyAsync(token)).Success)
            {
                if (reply.Code == null)
                {
                    throw new IOException("The connection was terminated before a greeting could be read.");
                }
                else
                {
                    throw new FtpCommandException(reply);
                }
            }
            HandshakeReply = reply;
        }
예제 #8
0
        /// <summary>
        /// Disconnects a data stream
        /// </summary>
        /// <param name="stream">The data stream to close</param>
        internal FtpReply CloseDataStream(FtpDataStream stream)
        {
            FtpTrace.WriteFunc("CloseDataStream");

            FtpReply reply = new FtpReply();

            if (stream == null)
            {
                throw new ArgumentException("The data stream parameter was null");
            }

#if !CORE14
            lock (m_lock) {
#endif
            try {
                if (IsConnected)
                {
                    // if the command that required the data connection was
                    // not successful then there will be no reply from
                    // the server, however if the command was successful
                    // the server will send a reply when the data connection
                    // is closed.
                    if (stream.CommandStatus.Type == FtpResponseType.PositivePreliminary)
                    {
                        if (!(reply = GetReply()).Success)
                        {
                            throw new FtpCommandException(reply);
                        }
                    }
                }
            } finally {
                // if this is a clone of the original control
                // connection we should Dispose()
                if (IsClone)
                {
                    Disconnect();
                    Dispose();
                }
            }
#if !CORE14
        }
#endif

            return(reply);
        }
예제 #9
0
        private string ParseWorkingDirectory(FtpReply reply)
        {
            Match m;

            if ((m = Regex.Match(reply.Message, "\"(?<pwd>.*)\"")).Success)
            {
                return(m.Groups["pwd"].Value.GetFtpPath());
            }

            // check for MODCOMP ftp path mentioned in forums: https://netftp.codeplex.com/discussions/444461
            if ((m = Regex.Match(reply.Message, "PWD = (?<pwd>.*)")).Success)
            {
                return(m.Groups["pwd"].Value.GetFtpPath());
            }

            LogStatus(FtpTraceLevel.Warn, "Failed to parse working directory from: " + reply.Message);

            return("/");
        }
예제 #10
0
        /// <summary>
        /// Tests if the specified directory exists on the server. This
        /// method works by trying to change the working directory to
        /// the path specified. If it succeeds, the directory is changed
        /// back to the old working directory and true is returned. False
        /// is returned otherwise and since the CWD failed it is assumed
        /// the working directory is still the same.
        /// </summary>
        /// <param name="path">The path of the directory</param>
        /// <returns>True if it exists, false otherwise.</returns>
        /// <example><code source="..\Examples\DirectoryExists.cs" lang="cs" /></example>
        public bool DirectoryExists(string path)
        {
            string pwd;

            // dont verify args as blank/null path is OK
            //if (path.IsBlank())
            //	throw new ArgumentException("Required parameter is null or blank.", "path");

            this.LogFunc("DirectoryExists", new object[] { path });

            // quickly check if root path, then it always exists!
            string ftppath = path.GetFtpPath();

            if (ftppath == "." || ftppath == "./" || ftppath == "/")
            {
                return(true);
            }

            // check if a folder exists by changing the working dir to it
#if !CORE14
            lock (m_lock) {
#endif
            pwd = GetWorkingDirectory();

            if (Execute("CWD " + ftppath).Success)
            {
                FtpReply reply = Execute("CWD " + pwd.GetFtpPath());

                if (!reply.Success)
                {
                    throw new FtpException("DirectoryExists(): Failed to restore the working directory.");
                }

                return(true);
            }
#if !CORE14
        }
#endif

            return(false);
        }
예제 #11
0
        /// <summary>
        /// Performs a login on the server. This method is overridable so
        /// that the login procedure can be changed to support, for example,
        /// a FTP proxy.
        /// </summary>
        /// <exception cref="FtpAuthenticationException">On authentication failures</exception>
        /// <remarks>
        /// To handle authentication failures without retries, catch FtpAuthenticationException.
        /// </remarks>
        protected virtual void Authenticate(string userName, string password)
        {
            // send the USER command along with the FTP username
            FtpReply reply = Execute("USER " + userName);

            // check the reply to the USER command
            if (!reply.Success)
            {
                throw new FtpAuthenticationException(reply);
            }

            // if it was accepted
            else if (reply.Type == FtpResponseType.PositiveIntermediate)
            {
                // send the PASS command along with the FTP password
                reply = Execute("PASS " + password);

                // fix for #620: some servers send multiple responses that must be read and decoded,
                // otherwise the connection is aborted and remade and it goes into an infinite loop
                var staleData = ReadStaleData(false, true, true);
                if (staleData != null)
                {
                    var staleReply = new FtpReply();
                    if (DecodeStringToReply(staleData, ref staleReply) && !staleReply.Success)
                    {
                        throw new FtpAuthenticationException(staleReply);
                    }
                }

                // check the first reply to the PASS command
                if (!reply.Success)
                {
                    throw new FtpAuthenticationException(reply);
                }
            }
        }
        /// <summary>
        /// Download a file from the server and write the data into the given stream asynchronously.
        /// Reads data in chunks. Retries if server disconnects midway.
        /// </summary>
        private async Task <bool> DownloadFileInternalAsync(string remotePath, Stream outStream, long restartPosition, IProgress <FtpProgress> progress, CancellationToken token = default(CancellationToken))
        {
            Stream downStream = null;

            try {
                // get file size if downloading in binary mode (in ASCII mode we read until EOF)
                long fileLen = 0;

                if (DownloadDataType == FtpDataType.Binary && progress != null)
                {
                    fileLen = await GetFileSizeAsync(remotePath, token);
                }

                // open the file for reading
                downStream = await OpenReadAsync(remotePath, DownloadDataType, restartPosition, fileLen > 0, token);

                // if the server has not provided a length for this file
                // we read until EOF instead of reading a specific number of bytes
                var readToEnd = fileLen <= 0;

                const int rateControlResolution = 100;
                var       rateLimitBytes        = DownloadRateLimit != 0 ? (long)DownloadRateLimit * 1024 : 0;
                var       chunkSize             = TransferChunkSize;
                if (m_transferChunkSize == null && rateLimitBytes > 0)
                {
                    // reduce chunk size to optimize rate control
                    const int chunkSizeMin = 64;
                    while (chunkSize > chunkSizeMin)
                    {
                        var chunkLenInMs = 1000L * chunkSize / rateLimitBytes;
                        if (chunkLenInMs <= rateControlResolution)
                        {
                            break;
                        }
                        chunkSize = Math.Max(chunkSize >> 1, chunkSizeMin);
                    }
                }

                // loop till entire file downloaded
                var buffer = new byte[chunkSize];
                var offset = restartPosition;

                var transferStarted = DateTime.Now;
                var sw = new Stopwatch();

                while (offset < fileLen || readToEnd)
                {
                    try {
                        // read a chunk of bytes from the FTP stream
                        var  readBytes       = 1;
                        long limitCheckBytes = 0;
                        long bytesProcessed  = 0;

                        sw.Start();
                        while ((readBytes = await downStream.ReadAsync(buffer, 0, buffer.Length, token)) > 0)
                        {
                            // write chunk to output stream
                            await outStream.WriteAsync(buffer, 0, readBytes, token);

                            offset          += readBytes;
                            bytesProcessed  += readBytes;
                            limitCheckBytes += readBytes;

                            // send progress reports
                            if (progress != null)
                            {
                                ReportProgress(progress, fileLen, offset, bytesProcessed, DateTime.Now - transferStarted);
                            }

                            // honor the rate limit
                            var swTime = sw.ElapsedMilliseconds;
                            if (rateLimitBytes > 0)
                            {
                                var timeShouldTake = limitCheckBytes * 1000 / rateLimitBytes;
                                if (timeShouldTake > swTime)
                                {
                                    await Task.Delay((int)(timeShouldTake - swTime), token);

                                    token.ThrowIfCancellationRequested();
                                }
                                else if (swTime > timeShouldTake + rateControlResolution)
                                {
                                    limitCheckBytes = 0;
                                    sw.Restart();
                                }
                            }
                        }

                        // if we reach here means EOF encountered
                        // stop if we are in "read until EOF" mode
                        if (readToEnd || offset == fileLen)
                        {
                            break;
                        }

                        // zero return value (with no Exception) indicates EOS; so we should fail here and attempt to resume
                        throw new IOException($"Unexpected EOF for remote file {remotePath} [{offset}/{fileLen} bytes read]");
                    }
                    catch (IOException ex) {
                        // resume if server disconnected midway, or throw if there is an exception doing that as well
                        var resumeResult = await ResumeDownloadAsync(remotePath, downStream, offset, ex);

                        if (resumeResult.Item1)
                        {
                            downStream = resumeResult.Item2;
                        }
                        else
                        {
                            sw.Stop();
                            throw;
                        }
                    }
                }

                sw.Stop();

                // disconnect FTP stream before exiting
                await outStream.FlushAsync(token);

                downStream.Dispose();

                // FIX : if this is not added, there appears to be "stale data" on the socket
                // listen for a success/failure reply
                if (!m_threadSafeDataChannels)
                {
                    FtpReply status = await GetReplyAsync(token);
                }

                return(true);
            }
            catch (Exception ex1) {
                // close stream before throwing error
                try {
                    downStream.Dispose();
                }
                catch (Exception) {
                }

                if (ex1 is OperationCanceledException)
                {
                    LogStatus(FtpTraceLevel.Info, "Upload cancellation requested");
                    throw;
                }

                // absorb "file does not exist" exceptions and simply return false
                if (ex1.Message.Contains("No such file") || ex1.Message.Contains("not exist") || ex1.Message.Contains("missing file") || ex1.Message.Contains("unknown file"))
                {
                    LogStatus(FtpTraceLevel.Error, "File does not exist: " + ex1);
                    return(false);
                }

                // catch errors during upload
                throw new FtpException("Error while downloading the file from the server. See InnerException for more info.", ex1);
            }
        }
예제 #13
0
        /// <summary>
        /// Download a file from the server and write the data into the given stream.
        /// Reads data in chunks. Retries if server disconnects midway.
        /// </summary>
        private bool DownloadFileInternal(string remotePath, Stream outStream, long restartPosition, IProgress <FtpProgress> progress)
        {
            Stream downStream = null;

            try {
                // get file size if downloading in binary mode (in ASCII mode we read until EOF)
                long fileLen = 0;
                if (DownloadDataType == FtpDataType.Binary && progress != null)
                {
                    fileLen = GetFileSize(remotePath);
                }

                // open the file for reading
                downStream = OpenRead(remotePath, DownloadDataType, restartPosition, fileLen > 0);

                // if the server has not provided a length for this file
                // we read until EOF instead of reading a specific number of bytes
                bool readToEnd = (fileLen <= 0);

                // loop till entire file downloaded
                byte[] buffer = new byte[TransferChunkSize];
                long   offset = restartPosition;

                DateTime  transferStarted = DateTime.Now;
                Stopwatch sw             = new Stopwatch();
                long      rateLimitBytes = DownloadRateLimit != 0 ? DownloadRateLimit * 1024 : 0;
                while (offset < fileLen || readToEnd)
                {
                    try {
                        // read a chunk of bytes from the FTP stream
                        int    readBytes       = 1;
                        double limitCheckBytes = 0;
                        long   bytesProcessed  = 0;

                        sw.Start();
                        while ((readBytes = downStream.Read(buffer, 0, buffer.Length)) > 0)
                        {
                            // write chunk to output stream
                            outStream.Write(buffer, 0, readBytes);
                            offset          += readBytes;
                            bytesProcessed  += readBytes;
                            limitCheckBytes += readBytes;

                            // send progress reports
                            if (progress != null)
                            {
                                ReportProgress(progress, fileLen, offset, bytesProcessed, DateTime.Now - transferStarted);
                            }

                            // honor the rate limit
                            int swTime = (int)sw.ElapsedMilliseconds;
                            if (rateLimitBytes > 0 && swTime >= 1000)
                            {
                                double timeShouldTake = limitCheckBytes / rateLimitBytes * 1000;
                                if (timeShouldTake > swTime)
                                {
#if CORE14
                                    Task.Delay((int)(timeShouldTake - swTime)).Wait();
#else
                                    Thread.Sleep((int)(timeShouldTake - swTime));
#endif
                                }
                                limitCheckBytes = 0;
                                sw.Restart();
                            }
                        }

                        // if we reach here means EOF encountered
                        // stop if we are in "read until EOF" mode
                        if (readToEnd || offset == fileLen)
                        {
                            break;
                        }

                        // zero return value (with no Exception) indicates EOS; so we should fail here and attempt to resume
                        throw new IOException($"Unexpected EOF for remote file {remotePath} [{offset}/{fileLen} bytes read]");
                    } catch (IOException ex) {
                        // resume if server disconnected midway, or throw if there is an exception doing that as well
                        if (!ResumeDownload(remotePath, ref downStream, offset, ex))
                        {
                            sw.Stop();
                            throw;
                        }
                    }
                }

                sw.Stop();


                // disconnect FTP stream before exiting
                outStream.Flush();
                downStream.Dispose();

                // FIX : if this is not added, there appears to be "stale data" on the socket
                // listen for a success/failure reply
                if (!m_threadSafeDataChannels)
                {
                    FtpReply status = GetReply();
                }
                return(true);
            } catch (Exception ex1) {
                // close stream before throwing error
                try {
                    downStream.Dispose();
                } catch (Exception) { }

                // absorb "file does not exist" exceptions and simply return false
                if (ex1.Message.Contains("No such file") || ex1.Message.Contains("not exist") || ex1.Message.Contains("missing file") || ex1.Message.Contains("unknown file"))
                {
                    this.LogStatus(FtpTraceLevel.Error, "File does not exist: " + ex1);
                    return(false);
                }

                // catch errors during upload
                throw new FtpException("Error while downloading the file from the server. See InnerException for more info.", ex1);
            }
        }
        /// <summary>
        /// Checks if a file exists on the server asynchronously.
        /// </summary>
        /// <param name="path">The full or relative path to the file</param>
        /// <param name="token">The token that can be used to cancel the entire process</param>
        /// <returns>True if the file exists, false otherwise</returns>
        public async Task <bool> FileExistsAsync(string path, CancellationToken token = default(CancellationToken))
        {
            // verify args
            if (path.IsBlank())
            {
                throw new ArgumentException("Required parameter is null or blank.", "path");
            }

            path = path.GetFtpPath();

            LogFunc(nameof(FileExistsAsync), new object[] { path });

            // A check for path.StartsWith("/") tells us, even if it is z/OS, we can use the normal unix logic

            // Do not need GetAbsolutePath(path) if z/OS
            if (ServerType != FtpServer.IBMzOSFTP || path.StartsWith("/"))
            {
                // calc the absolute filepath
                path = await GetAbsolutePathAsync(path, token);
            }

            // since FTP does not include a specific command to check if a file exists
            // here we check if file exists by attempting to get its filesize (SIZE)
            // If z/OS: Do not do SIZE, unless we have a leading slash
            if (HasFeature(FtpCapability.SIZE) && (ServerType != FtpServer.IBMzOSFTP || path.StartsWith("/")))
            {
                // Fix #328: get filesize in ASCII or Binary mode as required by server
                FtpSizeReply sizeReply = new FtpSizeReply();
                await GetFileSizeInternalAsync(path, -1, token, sizeReply);

                // handle known errors to the SIZE command
                var sizeKnownError = CheckFileExistsBySize(sizeReply);
                if (sizeKnownError.HasValue)
                {
                    return(sizeKnownError.Value);
                }
            }

            // check if file exists by attempting to get its date modified (MDTM)
            // If z/OS: Do not do MDTM, unless we have a leading slash
            if (HasFeature(FtpCapability.MDTM) && (ServerType != FtpServer.IBMzOSFTP || path.StartsWith("/")))
            {
                FtpReply reply = await ExecuteAsync("MDTM " + path, token);

                var ch = reply.Code[0];
                if (ch == '2')
                {
                    return(true);
                }

                if (ch == '5' && reply.Message.IsKnownError(FtpServerStrings.fileNotFound))
                {
                    return(false);
                }
            }

            // If z/OS: different handling, unless we have a leading slash
            if (ServerType == FtpServer.IBMzOSFTP && !path.StartsWith("/"))
            {
                var fileList = await GetNameListingAsync(path, token);

                return(fileList.Count() > 0);
            }
            else
            // check if file exists by getting a name listing (NLST)
            {
                var fileList = await GetNameListingAsync(path.GetFtpDirectoryName(), token);

                return(FileListings.FileExistsInNameListing(fileList, path));
            }
        }
예제 #15
0
 /// <summary>
 /// Initializes a new instance of a FtpAuthenticationException
 /// </summary>
 /// <param name="reply">The FtpReply to build the exception from</param>
 public FtpAuthenticationException(FtpReply reply) : base(reply)
 {
 }
예제 #16
0
 /// <summary>
 /// Populates the capabilities flags based on capabilities
 /// supported by this server. This method is overridable
 /// so that new features can be supported
 /// </summary>
 /// <param name="reply">The reply object from the FEAT command. The InfoMessages property will
 /// contain a list of the features the server supported delimited by a new line '\n' character.</param>
 protected virtual void GetFeatures(FtpReply reply)
 {
     FtpServerSpecificHandler.GetFeatures(this, m_capabilities, ref m_hashAlgorithms, reply.InfoMessages.Split('\n'));
 }
예제 #17
0
 /// <summary>
 /// Initalizes a new instance of a FtpResponseException
 /// </summary>
 /// <param name="reply">The FtpReply to build the exception from</param>
 public FtpCommandException(FtpReply reply)
     : this(reply.Code, reply.ErrorMessage)
 {
 }
예제 #18
0
 /// <summary>
 /// Populates the capabilities flags based on capabilities
 /// supported by this server. This method is overridable
 /// so that new features can be supported
 /// </summary>
 /// <param name="reply">The reply object from the FEAT command. The InfoMessages property will
 /// contain a list of the features the server supported delimited by a new line '\n' character.</param>
 protected virtual void GetFeatures(FtpReply reply)
 {
     GetFeatures(reply.InfoMessages.Split('\n'));
 }