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
}
}