/// <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); }
/// <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); } }