/// <summary>
        /// Sets the hash algorithm on the server to be used with the HASH command asynchronously.
        /// </summary>
        internal async Task SetHashAlgorithmInternalAsync(FtpHashAlgorithm algorithm, CancellationToken token = default(CancellationToken))
        {
            FtpReply reply;

            // skip setting the hash algo if the server is already configured to it
            if (_LastHashAlgo == algorithm)
            {
                return;
            }

            if ((HashAlgorithms & algorithm) != algorithm)
            {
                throw new NotImplementedException("The hash algorithm " + algorithm.ToString() + " was not advertised by the server.");
            }

            string algoName = HashAlgos.PrintToString(algorithm);

            if (!(reply = await ExecuteAsync("OPTS HASH " + algoName, token)).Success)
            {
                throw new FtpCommandException(reply);
            }

            // save the current hash algo so no need to repeat this command
            _LastHashAlgo = algorithm;
        }
        /// <summary>
        /// Sets the hash algorithm on the server to use for the HASH command.
        /// </summary>
        internal void SetHashAlgorithmInternal(FtpHashAlgorithm algorithm)
        {
            FtpReply reply;

            // skip setting the hash algo if the server is already configured to it
            if (_LastHashAlgo == algorithm)
            {
                return;
            }

#if !CORE14
            lock (m_lock) {
#endif
            if ((HashAlgorithms & algorithm) != algorithm)
            {
                throw new NotImplementedException("The hash algorithm " + algorithm.ToString() + " was not advertised by the server.");
            }

            string algoName = HashAlgos.PrintToString(algorithm);

            if (!(reply = Execute("OPTS HASH " + algoName)).Success)
            {
                throw new FtpCommandException(reply);
            }

            // save the current hash algo so no need to repeat this command
            _LastHashAlgo = algorithm;

#if !CORE14
        }
#endif
        }
        /// <summary>
        /// Gets the currently selected hash algorithm for the HASH command.
        /// </summary>
        internal FtpHashAlgorithm GetHashAlgorithmUnused()
        {
            FtpReply reply;
            var      type = FtpHashAlgorithm.NONE;

#if !CORE14
            lock (m_lock) {
#endif
            LogFunc(nameof(GetHashAlgorithmUnused));

            if ((reply = Execute("OPTS HASH")).Success)
            {
                try {
                    type = HashAlgos.FromString(reply.Message);
                }
                catch (InvalidOperationException ex) {
                    // Do nothing
                }
            }

#if !CORE14
        }
#endif

            return(type);
        }
        /// <summary>
        /// Gets the currently selected hash algorithm for the HASH command asynchronously.
        /// </summary>
        internal async Task <FtpHashAlgorithm> GetHashAlgorithmUnusedAsync(CancellationToken token = default(CancellationToken))
        {
            FtpReply reply;
            var      type = FtpHashAlgorithm.NONE;

            LogFunc(nameof(GetHashAlgorithmUnusedAsync));

            if ((reply = await ExecuteAsync("OPTS HASH", token)).Success)
            {
                try {
                    type = HashAlgos.FromString(reply.Message);
                }
                catch (InvalidOperationException ex) {
                    // Do nothing
                }
            }

            return(type);
        }
Example #5
0
        /// <summary>
        /// Parses the recieved FTP hash response into a new FtpHash object.
        /// </summary>
        public static FtpHash Parse(string reply)
        {
            // Current draft says the server should return this:
            //		SHA-256 0-49 169cd22282da7f147cb491e559e9dd filename.ext

            // Current version of FileZilla returns this:
            //		SHA-1 21c2ca15cf570582949eb59fb78038b9c27ffcaf

            // Real reply that was failing:
            //		213 MD5 0-170500096 3197bf4ec5fa2d441c0f50264ca52f11


            var hash = new FtpHash();

            // FIX #722 - remove the FTP status code causing a wrong hash to be returned
            if (reply.StartsWith("2") && reply.Length > 10)
            {
                reply = reply.Substring(4);
            }

            Match m;

            if (!(m = Regex.Match(reply,
                                  @"(?<algorithm>.+)\s" +
                                  @"(?<bytestart>\d+)-(?<byteend>\d+)\s" +
                                  @"(?<hash>.+)\s" +
                                  @"(?<filename>.+)")).Success)
            {
                m = Regex.Match(reply, @"(?<algorithm>.+)\s(?<hash>.+)\s");
            }

            if (m != null && m.Success)
            {
                hash.Algorithm = HashAlgorithms.FromString(m.Groups["algorithm"].Value);
                hash.Value     = m.Groups["hash"].Value;
            }
            else
            {
                // failed to parse
            }

            return(hash);
        }
        /// <summary>
        /// Retrieves a checksum of the given file using the specified checksum algorithm, or using the first available algorithm that the server supports.
        /// </summary>
        /// <remarks>
        /// The algorithm used goes in this order:
        /// 1. HASH command using the first supported algorithm.
        /// 2. MD5 / XMD5 / MMD5 commands
        /// 3. XSHA1 command
        /// 4. XSHA256 command
        /// 5. XSHA512 command
        /// 6. XCRC command
        /// </remarks>
        /// <param name="path">Full or relative path of the file to checksum</param>
        /// <param name="algorithm">Specify an algorithm that you prefer, or NONE to use the first available algorithm. If the preferred algorithm is not supported, a blank hash is returned.</param>
        /// <returns><see cref="FtpHash"/> object containing the value and algorithm. Use the <see cref="FtpHash.IsValid"/> property to
        /// determine if this command was successful. <see cref="FtpCommandException"/>s can be thrown from
        /// the underlying calls.</returns>
        /// <exception cref="FtpCommandException">The command fails</exception>
        public FtpHash GetChecksum(string path, FtpHashAlgorithm algorithm = FtpHashAlgorithm.NONE)
        {
            if (path == null)
            {
                throw new ArgumentException("Required argument is null", "path");
            }

            ValidateHashAlgorithm(algorithm);

            path = path.GetFtpPath();

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

            var useFirst = (algorithm == FtpHashAlgorithm.NONE);

            // if HASH is supported and the caller prefers an algorithm and that algorithm is supported
            if (HasFeature(FtpCapability.HASH) && !useFirst && HashAlgorithms.HasFlag(algorithm))
            {
                // switch to that algorithm
                SetHashAlgorithmInternal(algorithm);

                // get the hash of the file using HASH Command
                return(HashCommandInternal(path));
            }

            // if HASH is supported and the caller does not prefer any specific algorithm
            else if (HasFeature(FtpCapability.HASH) && useFirst)
            {
                // switch to the first preferred algorithm
                SetHashAlgorithmInternal(HashAlgos.FirstSupported(HashAlgorithms));

                // get the hash of the file using HASH Command
                return(HashCommandInternal(path));
            }
            else
            {
                var result = new FtpHash();

                // execute the first available algorithm, or the preferred algorithm if specified

                if (HasFeature(FtpCapability.MD5) && (useFirst || algorithm == FtpHashAlgorithm.MD5))
                {
                    result.Value     = GetHashInternal(path, "MD5");
                    result.Algorithm = FtpHashAlgorithm.MD5;
                }
                else if (HasFeature(FtpCapability.XMD5) && (useFirst || algorithm == FtpHashAlgorithm.MD5))
                {
                    result.Value     = GetHashInternal(path, "XMD5");
                    result.Algorithm = FtpHashAlgorithm.MD5;
                }
                else if (HasFeature(FtpCapability.MMD5) && (useFirst || algorithm == FtpHashAlgorithm.MD5))
                {
                    result.Value     = GetHashInternal(path, "MMD5");
                    result.Algorithm = FtpHashAlgorithm.MD5;
                }
                else if (HasFeature(FtpCapability.XSHA1) && (useFirst || algorithm == FtpHashAlgorithm.SHA1))
                {
                    result.Value     = GetHashInternal(path, "XSHA1");
                    result.Algorithm = FtpHashAlgorithm.SHA1;
                }
                else if (HasFeature(FtpCapability.XSHA256) && (useFirst || algorithm == FtpHashAlgorithm.SHA256))
                {
                    result.Value     = GetHashInternal(path, "XSHA256");
                    result.Algorithm = FtpHashAlgorithm.SHA256;
                }
                else if (HasFeature(FtpCapability.XSHA512) && (useFirst || algorithm == FtpHashAlgorithm.SHA512))
                {
                    result.Value     = GetHashInternal(path, "XSHA512");
                    result.Algorithm = FtpHashAlgorithm.SHA512;
                }
                else if (HasFeature(FtpCapability.XCRC) && (useFirst || algorithm == FtpHashAlgorithm.CRC))
                {
                    result.Value     = GetHashInternal(path, "XCRC");
                    result.Algorithm = FtpHashAlgorithm.CRC;
                }

                return(result);
            }
        }