using System; using System.IO; using System.Net.Sockets; using System.Text; using System.Text.RegularExpressions; using System.Reflection; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Security.Cryptography.X509Certificates; using System.Globalization; using System.Security.Authentication; using System.Net; using FluentFTP.Proxy; #if !CORE using System.Web; #endif #if (CORE || NETFX) using System.Threading; #endif #if ASYNC using System.Threading.Tasks; #endif namespace FluentFTP { /// /// FTP Control Connection. Speaks the FTP protocol with the server and /// provides facilities for performing transactions. /// /// Debugging problems with FTP transactions is much easier to do when /// you can see exactly what is sent to the server and the reply /// FluentFTP gets in return. Please review the Debug example /// below for information on how to add s for capturing /// the conversation between FluentFTP and the server. /// /// The following example illustrates how to assist in debugging /// FluentFTP by getting a transaction log from the server. /// /// /// The following example demonstrates adding a custom file /// listing parser in the event that you encounter a list format /// not already supported. /// /// /// The following example demonstrates how to validate /// a SSL certificate when using SSL/TLS. /// /// /// The following example demonstrates how to download a file. /// /// /// The following example demonstrates how to download a file /// using a URI object. /// /// /// The following example demonstrates how to upload a file. /// /// /// The following example demonstrates how to upload a file /// using a URI object. /// /// /// The following example demonstrates how to append to a file. /// /// /// The following example demonstrates how to append to a file /// using a URI object. /// /// /// The following example demonstrates how to get a file /// listing from the server. /// /// public partial class FtpClient : IDisposable { #region File Hashing - HASH /// /// Gets the currently selected hash algorithm for the HASH command. /// /// /// This feature is experimental. See this link for details: /// http://tools.ietf.org/html/draft-bryan-ftpext-hash-02 /// /// The flag or if there was a problem. /// public FtpHashAlgorithm GetHashAlgorithm() { FtpReply reply; FtpHashAlgorithm type = FtpHashAlgorithm.NONE; #if !CORE14 lock (m_lock) { #endif if ((reply = Execute("OPTS HASH")).Success) { switch (reply.Message) { case "SHA-1": type = FtpHashAlgorithm.SHA1; break; case "SHA-256": type = FtpHashAlgorithm.SHA256; break; case "SHA-512": type = FtpHashAlgorithm.SHA512; break; case "MD5": type = FtpHashAlgorithm.MD5; break; } } #if !CORE14 } #endif return type; } delegate FtpHashAlgorithm AsyncGetHashAlgorithm(); /// /// Begins an asynchronous operation to get the currently selected hash algorithm for the HASH command. /// /// /// This feature is experimental. See this link for details: /// http://tools.ietf.org/html/draft-bryan-ftpext-hash-02 /// /// Async callback /// State object /// IAsyncResult public IAsyncResult BeginGetHashAlgorithm(AsyncCallback callback, object state) { AsyncGetHashAlgorithm func; IAsyncResult ar; ar = (func = new AsyncGetHashAlgorithm(GetHashAlgorithm)).BeginInvoke(callback, state); lock (m_asyncmethods) { m_asyncmethods.Add(ar, func); } return ar; } /// /// Ends a call to /// /// IAsyncResult returned from /// The flag or if there was a problem. public FtpHashAlgorithm EndGetHashAlgorithm(IAsyncResult ar) { return GetAsyncDelegate(ar).EndInvoke(ar); } #if ASYNC /// /// Gets the currently selected hash algorithm for the HASH command asynchronously. /// /// /// This feature is experimental. See this link for details: /// http://tools.ietf.org/html/draft-bryan-ftpext-hash-02 /// /// The flag or if there was a problem. public async Task GetHashAlgorithmAsync() { //TODO: Rewrite as true async method with cancellation support return await Task.Factory.FromAsync( (ac, s) => BeginGetHashAlgorithm(ac, s), ar => EndGetHashAlgorithm(ar), null); } #endif /// /// Sets the hash algorithm on the server to use for the HASH command. /// /// /// If you specify an algorithm not listed in /// a will be thrown /// so be sure to query that list of Flags before /// selecting a hash algorithm. Support for the /// HASH command is experimental. Please see /// the following link for more details: /// http://tools.ietf.org/html/draft-bryan-ftpext-hash-02 /// /// Hash Algorithm /// Thrown if the selected algorithm is not available on the server /// public void SetHashAlgorithm(FtpHashAlgorithm type) { FtpReply reply; string algorithm; #if !CORE14 lock (m_lock) { #endif if ((HashAlgorithms & type) != type) throw new NotImplementedException(("The hash algorithm " + type.ToString() + " was not advertised by the server.")); switch (type) { case FtpHashAlgorithm.SHA1: algorithm = "SHA-1"; break; case FtpHashAlgorithm.SHA256: algorithm = "SHA-256"; break; case FtpHashAlgorithm.SHA512: algorithm = "SHA-512"; break; case FtpHashAlgorithm.MD5: algorithm = "MD5"; break; default: algorithm = type.ToString(); break; } if (!(reply = Execute("OPTS HASH " + algorithm)).Success) throw new FtpCommandException(reply); #if !CORE14 } #endif } delegate void AsyncSetHashAlgorithm(FtpHashAlgorithm type); /// /// Begins an asynchronous operation to set the hash algorithm on the server to use for the HASH command. /// /// /// If you specify an algorithm not listed in /// a will be thrown /// so be sure to query that list of Flags before /// selecting a hash algorithm. Support for the /// HASH command is experimental. Please see /// the following link for more details: /// http://tools.ietf.org/html/draft-bryan-ftpext-hash-02 /// /// Hash algorithm to use /// Async Callback /// State object /// IAsyncResult public IAsyncResult BeginSetHashAlgorithm(FtpHashAlgorithm type, AsyncCallback callback, object state) { AsyncSetHashAlgorithm func; IAsyncResult ar; ar = (func = new AsyncSetHashAlgorithm(SetHashAlgorithm)).BeginInvoke(type, callback, state); lock (m_asyncmethods) { m_asyncmethods.Add(ar, func); } return ar; } /// /// Ends an asynchronous call to /// /// IAsyncResult returned from public void EndSetHashAlgorithm(IAsyncResult ar) { GetAsyncDelegate(ar).EndInvoke(ar); } #if ASYNC /// /// Sets the hash algorithm on the server to be used with the HASH command asynchronously. /// /// Hash algorithm to use /// Thrown if the selected algorithm is not available on the server public async Task SetHashAlgorithmAsync(FtpHashAlgorithm type) { //TODO: Rewrite as true async method with cancellation support await Task.Factory.FromAsync( (t, ac, s) => BeginSetHashAlgorithm(t, ac, s), ar => EndSetHashAlgorithm(ar), type, null); } #endif /// /// Gets the hash of an object on the server using the currently selected hash algorithm. /// /// /// Supported algorithms, if any, are available in the /// property. You should confirm that it's not equal /// to before calling this method /// otherwise the server trigger a /// due to a lack of support for the HASH command. You can /// set the algorithm using the method and /// you can query the server for the current hash algorithm /// using the method. /// /// This feature is experimental and based on the following draft: /// http://tools.ietf.org/html/draft-bryan-ftpext-hash-02 /// /// Full or relative path of the object to compute the hash for. /// The hash of the file. /// /// Thrown if the property is , /// the remote path does not exist, or the command cannot be executed. /// /// Path argument is null /// Thrown when an unknown hash algorithm type is returned by the server /// public FtpHash GetHash(string path) { FtpReply reply; FtpHash hash = new FtpHash(); Match m; if (path == null) throw new ArgumentException("GetHash(path) argument can't be null"); #if !CORE14 lock (m_lock) { #endif if (!(reply = Execute("HASH " + path.GetFtpPath())).Success) throw new FtpCommandException(reply); #if !CORE14 } #endif // Current draft says the server should return this: // SHA-256 0-49 169cd22282da7f147cb491e559e9dd filename.ext if (!(m = Regex.Match(reply.Message, @"(?.+)\s" + @"(?\d+)-(?\d+)\s" + @"(?.+)\s" + @"(?.+)")).Success) { // Current version of FileZilla returns this: // SHA-1 21c2ca15cf570582949eb59fb78038b9c27ffcaf m = Regex.Match(reply.Message, @"(?.+)\s(?.+)\s"); } if (m != null && m.Success) { switch (m.Groups["algorithm"].Value) { case "SHA-1": hash.Algorithm = FtpHashAlgorithm.SHA1; break; case "SHA-256": hash.Algorithm = FtpHashAlgorithm.SHA256; break; case "SHA-512": hash.Algorithm = FtpHashAlgorithm.SHA512; break; case "MD5": hash.Algorithm = FtpHashAlgorithm.MD5; break; default: throw new NotImplementedException("Unknown hash algorithm: " + m.Groups["algorithm"].Value); } hash.Value = m.Groups["hash"].Value; } else { FtpTrace.WriteStatus(FtpTraceLevel.Warn, "Failed to parse hash from: " + reply.Message); } return hash; } #if !CORE delegate FtpHash AsyncGetHash(string path); /// /// Begins an asynchronous operation to get the hash of an object on the server using the currently selected hash algorithm. /// /// /// Supported algorithms, if any, are available in the /// property. You should confirm that it's not equal /// to before calling this method /// otherwise the server trigger a /// due to a lack of support for the HASH command. You can /// set the algorithm using the method and /// you can query the server for the current hash algorithm /// using the method. /// /// This feature is experimental and based on the following draft: /// http://tools.ietf.org/html/draft-bryan-ftpext-hash-02 /// /// The file you want the server to compute the hash for /// AsyncCallback /// State object /// IAsyncResult public IAsyncResult BeginGetHash(string path, AsyncCallback callback, object state) { AsyncGetHash func; IAsyncResult ar; ar = (func = new AsyncGetHash(GetHash)).BeginInvoke(path, callback, state); lock (m_asyncmethods) { m_asyncmethods.Add(ar, func); } return ar; } /// /// Ends an asynchronous call to /// /// IAsyncResult returned from public FtpHash EndGetHash(IAsyncResult ar) { return GetAsyncDelegate(ar).EndInvoke(ar); } #endif #if ASYNC /// /// Gets the hash of an object on the server using the currently selected hash algorithm asynchronously. /// /// /// Supported algorithms, if any, are available in the /// property. You should confirm that it's not equal /// to before calling this method /// otherwise the server trigger a /// due to a lack of support for the HASH command. You can /// set the algorithm using the method and /// you can query the server for the current hash algorithm /// using the method. /// /// This feature is experimental and based on the following draft: /// http://tools.ietf.org/html/draft-bryan-ftpext-hash-02 /// /// The file you want the server to compute the hash for /// /// Thrown if the property is , /// the remote path does not exist, or the command cannot be executed. /// /// Path argument is null /// Thrown when an unknown hash algorithm type is returned by the server /// The hash of the file. public async Task GetHashAsync(string path) { //TODO: Add cancellation support FtpReply reply; FtpHash hash = new FtpHash(); Match m; if (path == null) throw new ArgumentException("GetHash(path) argument can't be null"); if (!(reply = await ExecuteAsync("HASH " + path.GetFtpPath())).Success) throw new FtpCommandException(reply); // Current draft says the server should return this: // SHA-256 0-49 169cd22282da7f147cb491e559e9dd filename.ext if (!(m = Regex.Match(reply.Message, @"(?.+)\s" + @"(?\d+)-(?\d+)\s" + @"(?.+)\s" + @"(?.+)")).Success) { // Current version of FileZilla returns this: // SHA-1 21c2ca15cf570582949eb59fb78038b9c27ffcaf m = Regex.Match(reply.Message, @"(?.+)\s(?.+)\s"); } if (m != null && m.Success) { switch (m.Groups["algorithm"].Value) { case "SHA-1": hash.Algorithm = FtpHashAlgorithm.SHA1; break; case "SHA-256": hash.Algorithm = FtpHashAlgorithm.SHA256; break; case "SHA-512": hash.Algorithm = FtpHashAlgorithm.SHA512; break; case "MD5": hash.Algorithm = FtpHashAlgorithm.MD5; break; default: throw new NotImplementedException("Unknown hash algorithm: " + m.Groups["algorithm"].Value); } hash.Value = m.Groups["hash"].Value; } else { FtpTrace.WriteStatus(FtpTraceLevel.Warn, "Failed to parse hash from: " + reply.Message); } return hash; } #endif #endregion #region File Checksum /// /// Retrieves a checksum of the given file using a checksum method that the server supports, if any. /// /// /// The algorithm used goes in this order: /// 1. HASH command; server preferred algorithm. See /// 2. MD5 / XMD5 commands /// 3. XSHA1 command /// 4. XSHA256 command /// 5. XSHA512 command /// 6. XCRC command /// /// Full or relative path of the file to checksum /// object containing the value and algorithm. Use the property to /// determine if this command was successful. s can be thrown from /// the underlying calls. /// /// The command fails public FtpHash GetChecksum(string path) { if (HasFeature(FtpCapability.HASH)) { return GetHash(path); } else { FtpHash res = new FtpHash(); if (HasFeature(FtpCapability.MD5)) { res.Value = GetMD5(path); res.Algorithm = FtpHashAlgorithm.MD5; } else if (HasFeature(FtpCapability.XMD5)) { res.Value = GetXMD5(path); res.Algorithm = FtpHashAlgorithm.MD5; } else if (HasFeature(FtpCapability.XSHA1)) { res.Value = GetXSHA1(path); res.Algorithm = FtpHashAlgorithm.SHA1; } else if (HasFeature(FtpCapability.XSHA256)) { res.Value = GetXSHA256(path); res.Algorithm = FtpHashAlgorithm.SHA256; } else if (HasFeature(FtpCapability.XSHA512)) { res.Value = GetXSHA512(path); res.Algorithm = FtpHashAlgorithm.SHA512; } else if (HasFeature(FtpCapability.XCRC)) { res.Value = GetXCRC(path); res.Algorithm = FtpHashAlgorithm.CRC; } return res; } } #if !CORE delegate FtpHash AsyncGetChecksum(string path); /// /// Begins an asynchronous operation to retrieve a checksum of the given file using a checksum method that the server supports, if any. /// /// /// The algorithm used goes in this order: /// 1. HASH command; server preferred algorithm. See /// 2. MD5 / XMD5 commands /// 3. XSHA1 command /// 4. XSHA256 command /// 5. XSHA512 command /// 6. XCRC command /// /// Full or relative path to remote file /// AsyncCallback /// State Object /// IAsyncResult public IAsyncResult BeginGetChecksum(string path, AsyncCallback callback, object state) { AsyncGetChecksum func = new AsyncGetChecksum(GetChecksum); IAsyncResult ar = func.BeginInvoke(path, callback, state); ; lock (m_asyncmethods) { m_asyncmethods.Add(ar, func); } return ar; } /// /// Ends an asynchronous call to /// /// IAsyncResult returned from /// object containing the value and algorithm. Use the property to /// determine if this command was successful. s can be thrown from /// the underlying calls. public FtpHash EndGetChecksum(IAsyncResult ar) { AsyncGetChecksum func = null; lock (m_asyncmethods) { if (!m_asyncmethods.ContainsKey(ar)) throw new InvalidOperationException("The specified IAsyncResult was not found in the collection."); func = (AsyncGetChecksum)m_asyncmethods[ar]; m_asyncmethods.Remove(ar); } return func.EndInvoke(ar); } #endif #if ASYNC /// /// Retrieves a checksum of the given file using a checksum method that the server supports, if any. /// /// /// The algorithm used goes in this order: /// 1. HASH command; server preferred algorithm. See /// 2. MD5 / XMD5 commands /// 3. XSHA1 command /// 4. XSHA256 command /// 5. XSHA512 command /// 6. XCRC command /// /// Full or relative path of the file to checksum /// object containing the value and algorithm. Use the property to /// determine if this command was successful. s can be thrown from /// the underlying calls. /// /// The command fails public async Task GetChecksumAsync(string path) { //TODO: Add cancellation support if (HasFeature(FtpCapability.HASH)) { return await GetHashAsync(path); } else { FtpHash res = new FtpHash(); if (HasFeature(FtpCapability.MD5)) { res.Value = await GetMD5Async(path); res.Algorithm = FtpHashAlgorithm.MD5; } else if (HasFeature(FtpCapability.XMD5)) { res.Value = await GetXMD5Async(path); res.Algorithm = FtpHashAlgorithm.MD5; } else if (HasFeature(FtpCapability.XSHA1)) { res.Value = await GetXSHA1Async(path); res.Algorithm = FtpHashAlgorithm.SHA1; } else if (HasFeature(FtpCapability.XSHA256)) { res.Value = await GetXSHA256Async(path); res.Algorithm = FtpHashAlgorithm.SHA256; } else if (HasFeature(FtpCapability.XSHA512)) { res.Value = await GetXSHA512Async(path); res.Algorithm = FtpHashAlgorithm.SHA512; } else if (HasFeature(FtpCapability.XCRC)) { res.Value = await GetXCRCAsync(path); res.Algorithm = FtpHashAlgorithm.CRC; } return res; } } #endif #endregion #region MD5 /// /// Gets the MD5 hash of the specified file using MD5. This is a non-standard extension /// to the protocol and may or may not work. A FtpCommandException will be /// thrown if the command fails. /// /// Full or relative path to remote file /// Server response, presumably the MD5 hash. /// The command fails public string GetMD5(string path) { // http://tools.ietf.org/html/draft-twine-ftpmd5-00#section-3.1 FtpReply reply; string response; if (!(reply = Execute("MD5 " + path)).Success) throw new FtpCommandException(reply); response = reply.Message; if (response.StartsWith(path)) { response = response.Remove(0, path.Length).Trim(); } return response; } #if !CORE delegate string AsyncGetMD5(string path); /// /// Begins an asynchronous operation to retrieve a MD5 hash. The MD5 command is non-standard /// and not guaranteed to work. /// /// Full or relative path to remote file /// AsyncCallback /// State Object /// IAsyncResult public IAsyncResult BeginGetMD5(string path, AsyncCallback callback, object state) { AsyncGetMD5 func = new AsyncGetMD5(GetMD5); IAsyncResult ar = func.BeginInvoke(path, callback, state); ; lock (m_asyncmethods) { m_asyncmethods.Add(ar, func); } return ar; } /// /// Ends an asynchronous call to /// /// IAsyncResult returned from /// The MD5 hash of the specified file. public string EndGetMD5(IAsyncResult ar) { AsyncGetMD5 func = null; lock (m_asyncmethods) { if (!m_asyncmethods.ContainsKey(ar)) throw new InvalidOperationException("The specified IAsyncResult was not found in the collection."); func = (AsyncGetMD5)m_asyncmethods[ar]; m_asyncmethods.Remove(ar); } return func.EndInvoke(ar); } #endif #if ASYNC /// /// Gets the MD5 hash of the specified file using MD5 asynchronously. This is a non-standard extension /// to the protocol and may or may not work. A FtpCommandException will be /// thrown if the command fails. /// /// Full or relative path to remote file /// Server response, presumably the MD5 hash. /// The command fails public async Task GetMD5Async(string path) { FtpReply reply; string response; if (!(reply = await ExecuteAsync("MD5 " + path)).Success) throw new FtpCommandException(reply); response = reply.Message; if (response.StartsWith(path)) { response = response.Remove(0, path.Length).Trim(); } return response; } #endif #endregion #region XCRC /// /// Get the CRC value of the specified file. This is a non-standard extension of the protocol /// and may throw a FtpCommandException if the server does not support it. /// /// The path of the file you'd like the server to compute the CRC value for. /// The response from the server, typically the XCRC value. FtpCommandException thrown on error /// The command fails public string GetXCRC(string path) { FtpReply reply; if (!(reply = Execute("XCRC " + path)).Success) throw new FtpCommandException(reply); return reply.Message; } #if !CORE delegate string AsyncGetXCRC(string path); /// /// Begins an asynchronous operation to retrieve a CRC hash. The XCRC command is non-standard /// and not guaranteed to work. /// /// Full or relative path to remote file /// AsyncCallback /// State Object /// IAsyncResult public IAsyncResult BeginGetXCRC(string path, AsyncCallback callback, object state) { AsyncGetXCRC func = new AsyncGetXCRC(GetXCRC); IAsyncResult ar = func.BeginInvoke(path, callback, state); ; lock (m_asyncmethods) { m_asyncmethods.Add(ar, func); } return ar; } /// /// Ends an asynchronous call to /// /// IAsyncResult returned from /// The CRC hash of the specified file. public string EndGetXCRC(IAsyncResult ar) { AsyncGetXCRC func = null; lock (m_asyncmethods) { if (!m_asyncmethods.ContainsKey(ar)) throw new InvalidOperationException("The specified IAsyncResult was not found in the collection."); func = (AsyncGetXCRC)m_asyncmethods[ar]; m_asyncmethods.Remove(ar); } return func.EndInvoke(ar); } #endif #if ASYNC /// /// Gets the CRC hash of the specified file using XCRC asynchronously. This is a non-standard extension /// to the protocol and may or may not work. A FtpCommandException will be /// thrown if the command fails. /// /// Full or relative path to remote file /// Server response, presumably the CRC hash. /// The command fails public async Task GetXCRCAsync(string path) { FtpReply reply; string response; if (!(reply = await ExecuteAsync("MD5 " + path)).Success) throw new FtpCommandException(reply); response = reply.Message; if (response.StartsWith(path)) { response = response.Remove(0, path.Length).Trim(); } return response; } #endif #endregion #region XMD5 /// /// Gets the MD5 hash of the specified file using XMD5. This is a non-standard extension /// to the protocol and may or may not work. A FtpCommandException will be /// thrown if the command fails. /// /// Full or relative path to remote file /// Server response, presumably the MD5 hash. /// The command fails public string GetXMD5(string path) { FtpReply reply; if (!(reply = Execute("XMD5 " + path)).Success) throw new FtpCommandException(reply); return reply.Message; } #if !CORE delegate string AsyncGetXMD5(string path); /// /// Begins an asynchronous operation to retrieve a XMD5 hash. The XMD5 command is non-standard /// and not guaranteed to work. /// /// Full or relative path to remote file /// AsyncCallback /// State Object /// IAsyncResult public IAsyncResult BeginGetXMD5(string path, AsyncCallback callback, object state) { AsyncGetXMD5 func = new AsyncGetXMD5(GetXMD5); IAsyncResult ar = func.BeginInvoke(path, callback, state); ; lock (m_asyncmethods) { m_asyncmethods.Add(ar, func); } return ar; } /// /// Ends an asynchronous call to /// /// IAsyncResult returned from /// The MD5 hash of the specified file. public string EndGetXMD5(IAsyncResult ar) { AsyncGetXMD5 func = null; lock (m_asyncmethods) { if (!m_asyncmethods.ContainsKey(ar)) throw new InvalidOperationException("The specified IAsyncResult was not found in the collection."); func = (AsyncGetXMD5)m_asyncmethods[ar]; m_asyncmethods.Remove(ar); } return func.EndInvoke(ar); } #endif #if ASYNC /// /// Gets the MD5 hash of the specified file using XMD5 asynchronously. This is a non-standard extension /// to the protocol and may or may not work. A FtpCommandException will be /// thrown if the command fails. /// /// Full or relative path to remote file /// Server response, presumably the MD5 hash. /// The command fails public async Task GetXMD5Async(string path) { FtpReply reply; if (!(reply = await ExecuteAsync("XMD5 " + path)).Success) throw new FtpCommandException(reply); return reply.Message; } #endif #endregion #region XSHA1 /// /// Gets the SHA-1 hash of the specified file using XSHA1. This is a non-standard extension /// to the protocol and may or may not work. A FtpCommandException will be /// thrown if the command fails. /// /// Full or relative path to remote file /// Server response, presumably the SHA-1 hash. /// The command fails public string GetXSHA1(string path) { FtpReply reply; if (!(reply = Execute("XSHA1 " + path)).Success) throw new FtpCommandException(reply); return reply.Message; } #if !CORE delegate string AsyncGetXSHA1(string path); /// /// Begins an asynchronous operation to retrieve a SHA1 hash. The XSHA1 command is non-standard /// and not guaranteed to work. /// /// Full or relative path to remote file /// AsyncCallback /// State Object /// IAsyncResult public IAsyncResult BeginGetXSHA1(string path, AsyncCallback callback, object state) { AsyncGetXSHA1 func = new AsyncGetXSHA1(GetXSHA1); IAsyncResult ar = func.BeginInvoke(path, callback, state); ; lock (m_asyncmethods) { m_asyncmethods.Add(ar, func); } return ar; } /// /// Ends an asynchronous call to /// /// IAsyncResult returned from /// The SHA-1 hash of the specified file. public string EndGetXSHA1(IAsyncResult ar) { AsyncGetXSHA1 func = null; lock (m_asyncmethods) { if (!m_asyncmethods.ContainsKey(ar)) throw new InvalidOperationException("The specified IAsyncResult was not found in the collection."); func = (AsyncGetXSHA1)m_asyncmethods[ar]; m_asyncmethods.Remove(ar); } return func.EndInvoke(ar); } #endif #if ASYNC /// /// Gets the SHA-1 hash of the specified file using XSHA1 asynchronously. This is a non-standard extension /// to the protocol and may or may not work. A FtpCommandException will be /// thrown if the command fails. /// /// Full or relative path to remote file /// Server response, presumably the SHA-1 hash. /// The command fails public async Task GetXSHA1Async(string path) { FtpReply reply; if (!(reply = await ExecuteAsync("XSHA1 " + path)).Success) throw new FtpCommandException(reply); return reply.Message; } #endif #endregion #region XSHA256 /// /// Gets the SHA-256 hash of the specified file using XSHA256. This is a non-standard extension /// to the protocol and may or may not work. A FtpCommandException will be /// thrown if the command fails. /// /// Full or relative path to remote file /// Server response, presumably the SHA-256 hash. /// The command fails public string GetXSHA256(string path) { FtpReply reply; if (!(reply = Execute("XSHA256 " + path)).Success) throw new FtpCommandException(reply); return reply.Message; } #if !CORE delegate string AsyncGetXSHA256(string path); /// /// Begins an asynchronous operation to retrieve a SHA256 hash. The XSHA256 command is non-standard /// and not guaranteed to work. /// /// Full or relative path to remote file /// AsyncCallback /// State Object /// IAsyncResult public IAsyncResult BeginGetXSHA256(string path, AsyncCallback callback, object state) { AsyncGetXSHA256 func = new AsyncGetXSHA256(GetXSHA256); IAsyncResult ar = func.BeginInvoke(path, callback, state); ; lock (m_asyncmethods) { m_asyncmethods.Add(ar, func); } return ar; } /// /// Ends an asynchronous call to /// /// IAsyncResult returned from /// The SHA-256 hash of the specified file. public string EndGetXSHA256(IAsyncResult ar) { AsyncGetXSHA256 func = null; lock (m_asyncmethods) { if (!m_asyncmethods.ContainsKey(ar)) throw new InvalidOperationException("The specified IAsyncResult was not found in the collection."); func = (AsyncGetXSHA256)m_asyncmethods[ar]; m_asyncmethods.Remove(ar); } return func.EndInvoke(ar); } #endif #if ASYNC /// /// Gets the SHA-256 hash of the specified file using XSHA256 asynchronously. This is a non-standard extension /// to the protocol and may or may not work. A FtpCommandException will be /// thrown if the command fails. /// /// Full or relative path to remote file /// Server response, presumably the SHA-256 hash. /// The command fails public async Task GetXSHA256Async(string path) { FtpReply reply; if (!(reply = await ExecuteAsync("XSHA256 " + path)).Success) throw new FtpCommandException(reply); return reply.Message; } #endif #endregion #region XSHA512 /// /// Gets the SHA-512 hash of the specified file using XSHA512. This is a non-standard extension /// to the protocol and may or may not work. A FtpCommandException will be /// thrown if the command fails. /// /// Full or relative path to remote file /// Server response, presumably the SHA-512 hash. /// The command fails public string GetXSHA512(string path) { FtpReply reply; if (!(reply = Execute("XSHA512 " + path)).Success) throw new FtpCommandException(reply); return reply.Message; } #if !CORE delegate string AsyncGetXSHA512(string path); /// /// Begins an asynchronous operation to retrieve a SHA512 hash. The XSHA512 command is non-standard /// and not guaranteed to work. /// /// Full or relative path to remote file /// AsyncCallback /// State Object /// IAsyncResult public IAsyncResult BeginGetXSHA512(string path, AsyncCallback callback, object state) { AsyncGetXSHA512 func = new AsyncGetXSHA512(GetXSHA512); IAsyncResult ar = func.BeginInvoke(path, callback, state); ; lock (m_asyncmethods) { m_asyncmethods.Add(ar, func); } return ar; } /// /// Ends an asynchronous call to /// /// IAsyncResult returned from /// The SHA-512 hash of the specified file. public string EndGetXSHA512(IAsyncResult ar) { AsyncGetXSHA512 func = null; lock (m_asyncmethods) { if (!m_asyncmethods.ContainsKey(ar)) throw new InvalidOperationException("The specified IAsyncResult was not found in the collection."); func = (AsyncGetXSHA512)m_asyncmethods[ar]; m_asyncmethods.Remove(ar); } return func.EndInvoke(ar); } #endif #if ASYNC /// /// Gets the SHA-512 hash of the specified file using XSHA512 asynchronously. This is a non-standard extension /// to the protocol and may or may not work. A FtpCommandException will be /// thrown if the command fails. /// /// Full or relative path to remote file /// Server response, presumably the SHA-512 hash. /// The command fails public async Task GetXSHA512Async(string path) { FtpReply reply; if (!(reply = await ExecuteAsync("XSHA512 " + path)).Success) throw new FtpCommandException(reply); return reply.Message; } #endif #endregion } }