FluentFTP 추가
This commit is contained in:
@@ -6,6 +6,7 @@ using System.ComponentModel;
|
||||
using System.Data;
|
||||
using System.Drawing;
|
||||
using System.Linq;
|
||||
using System.Net.NetworkInformation;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Forms;
|
||||
@@ -313,6 +314,16 @@ namespace AutoSellerNS
|
||||
{
|
||||
btSell.Primary = !btSell.Primary;
|
||||
m_bSell = (btSell.Primary == false);
|
||||
|
||||
if(m_bSell == true)
|
||||
{
|
||||
FileTransfer ft = new FileTransfer();
|
||||
string today = DateTime.Now.ToString("yyyy-MM-dd");
|
||||
string macAddr = NetworkInterface.GetAllNetworkInterfaces()[0].GetPhysicalAddress().ToString();
|
||||
ft.SendDir("/configure", macAddr + "/AutoSeller/" + today);
|
||||
//ft.SendDir("/log", macAddr + "/AutoSeller");
|
||||
//ft.SendDir("/simulation", macAddr + "/AutoSeller");
|
||||
}
|
||||
}
|
||||
|
||||
public bool IsSelling()
|
||||
|
||||
@@ -34,18 +34,20 @@
|
||||
<DebugType>full</DebugType>
|
||||
<Optimize>false</Optimize>
|
||||
<OutputPath>bin\Debug\</OutputPath>
|
||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
||||
<DefineConstants>TRACE;DEBUG;ASYNC;NET45</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||
<DebugType>pdbonly</DebugType>
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release\</OutputPath>
|
||||
<DefineConstants>TRACE</DefineConstants>
|
||||
<DefineConstants>TRACE;ASYNC;NET45</DefineConstants>
|
||||
<ErrorReport>prompt</ErrorReport>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup>
|
||||
<SignManifests>false</SignManifests>
|
||||
@@ -76,6 +78,7 @@
|
||||
</Reference>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Web" />
|
||||
<Reference Include="System.Xml.Linq" />
|
||||
<Reference Include="System.Data.DataSetExtensions" />
|
||||
<Reference Include="Microsoft.CSharp" />
|
||||
@@ -96,6 +99,34 @@
|
||||
<DependentUpon>AutoSeller.cs</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="ExcelHandler.cs" />
|
||||
<Compile Include="FileTransfer.cs" />
|
||||
<Compile Include="FluentFTP\Client\FtpClient_Connection.cs" />
|
||||
<Compile Include="FluentFTP\Client\FtpClient_Hash.cs" />
|
||||
<Compile Include="FluentFTP\Client\FtpClient_HighLevel.cs" />
|
||||
<Compile Include="FluentFTP\Client\FtpClient_Listing.cs" />
|
||||
<Compile Include="FluentFTP\Client\FtpClient_LowLevel.cs" />
|
||||
<Compile Include="FluentFTP\Client\FtpClient_Management.cs" />
|
||||
<Compile Include="FluentFTP\Client\IFtpClient.cs" />
|
||||
<Compile Include="FluentFTP\Helpers\FtpEnums.cs" />
|
||||
<Compile Include="FluentFTP\Helpers\FtpEvents.cs" />
|
||||
<Compile Include="FluentFTP\Helpers\FtpExceptions.cs" />
|
||||
<Compile Include="FluentFTP\Helpers\FtpHash.cs" />
|
||||
<Compile Include="FluentFTP\Helpers\FtpListItem.cs" />
|
||||
<Compile Include="FluentFTP\Helpers\FtpListParser.cs" />
|
||||
<Compile Include="FluentFTP\Helpers\FtpReply.cs" />
|
||||
<Compile Include="FluentFTP\Helpers\FtpTrace.cs" />
|
||||
<Compile Include="FluentFTP\Helpers\IntRef.cs" />
|
||||
<Compile Include="FluentFTP\Proxy\FtpClientHttp11Proxy.cs" />
|
||||
<Compile Include="FluentFTP\Proxy\FtpClientProxy.cs" />
|
||||
<Compile Include="FluentFTP\Proxy\FtpClientUserAtHostProxy.cs" />
|
||||
<Compile Include="FluentFTP\Proxy\FtpClientUserAtHostProxyBlueCoat.cs" />
|
||||
<Compile Include="FluentFTP\Proxy\ProxyInfo.cs" />
|
||||
<Compile Include="FluentFTP\Stream\FtpDataStream.cs" />
|
||||
<Compile Include="FluentFTP\Stream\FtpSocketStream.cs" />
|
||||
<Compile Include="FluentFTP\Stream\FtpSslStream.cs" />
|
||||
<Compile Include="FluentFTP\Utils\FtpExtensions.cs" />
|
||||
<Compile Include="FluentFTP\Utils\FtpReflection.cs" />
|
||||
<Compile Include="FluentFTP\Utils\NET2Compatibility.cs" />
|
||||
<Compile Include="ListViewNF.cs">
|
||||
<SubType>Component</SubType>
|
||||
</Compile>
|
||||
|
||||
41
FileTransfer.cs
Normal file
41
FileTransfer.cs
Normal file
@@ -0,0 +1,41 @@
|
||||
using FluentFTP;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace AutoSellerNS
|
||||
{
|
||||
class FileTransfer
|
||||
{
|
||||
const string HOST = "mjjo53.us.to";
|
||||
const string USER = "trader";
|
||||
const string PASSWORD = "sbtmaoao";
|
||||
const string REMOTE_BASE_PATH = "/";
|
||||
|
||||
public async void SendDir(string localDir, string remoteDir)
|
||||
{
|
||||
try
|
||||
{
|
||||
using (FtpClient client = new FtpClient(HOST, USER, PASSWORD))
|
||||
{
|
||||
client.ConnectTimeout = 3000;
|
||||
client.RetryAttempts = 3;
|
||||
|
||||
string project_path = Path.GetDirectoryName(Path.GetDirectoryName(System.IO.Directory.GetCurrentDirectory()));
|
||||
List<string> files = Directory.GetFiles(project_path + localDir).ToList();
|
||||
string remotePath = REMOTE_BASE_PATH + remoteDir;
|
||||
await client.ConnectAsync();
|
||||
await client.UploadFilesAsync(files, remotePath, FtpExists.Overwrite);
|
||||
//await client.ChmodAsync(remotePath, 777);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
2216
FluentFTP/Client/FtpClient_Connection.cs
Normal file
2216
FluentFTP/Client/FtpClient_Connection.cs
Normal file
File diff suppressed because it is too large
Load Diff
1162
FluentFTP/Client/FtpClient_Hash.cs
Normal file
1162
FluentFTP/Client/FtpClient_Hash.cs
Normal file
File diff suppressed because it is too large
Load Diff
2162
FluentFTP/Client/FtpClient_HighLevel.cs
Normal file
2162
FluentFTP/Client/FtpClient_HighLevel.cs
Normal file
File diff suppressed because it is too large
Load Diff
1072
FluentFTP/Client/FtpClient_Listing.cs
Normal file
1072
FluentFTP/Client/FtpClient_Listing.cs
Normal file
File diff suppressed because it is too large
Load Diff
1473
FluentFTP/Client/FtpClient_LowLevel.cs
Normal file
1473
FluentFTP/Client/FtpClient_LowLevel.cs
Normal file
File diff suppressed because it is too large
Load Diff
2249
FluentFTP/Client/FtpClient_Management.cs
Normal file
2249
FluentFTP/Client/FtpClient_Management.cs
Normal file
File diff suppressed because it is too large
Load Diff
152
FluentFTP/Client/IFtpClient.cs
Normal file
152
FluentFTP/Client/IFtpClient.cs
Normal file
@@ -0,0 +1,152 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Text;
|
||||
|
||||
namespace FluentFTP {
|
||||
|
||||
/// <summary>
|
||||
/// Interface for the FtpClient class. For detailed documentation of the methods, please see the FtpClient class.
|
||||
/// </summary>
|
||||
public interface IFtpClient : IDisposable {
|
||||
|
||||
|
||||
// CONNECTION
|
||||
|
||||
bool IsDisposed { get; }
|
||||
FtpIpVersion InternetProtocolVersions { get; set; }
|
||||
int SocketPollInterval { get; set; }
|
||||
bool StaleDataCheck { get; set; }
|
||||
bool IsConnected { get; }
|
||||
bool EnableThreadSafeDataConnections { get; set; }
|
||||
Encoding Encoding { get; set; }
|
||||
string Host { get; set; }
|
||||
int Port { get; set; }
|
||||
NetworkCredential Credentials { get; set; }
|
||||
int MaximumDereferenceCount { get; set; }
|
||||
X509CertificateCollection ClientCertificates { get; }
|
||||
Func<string> AddressResolver { get; set; }
|
||||
IEnumerable<int> ActivePorts { get; set; }
|
||||
FtpDataConnectionType DataConnectionType { get; set; }
|
||||
bool UngracefullDisconnection { get; set; }
|
||||
int ConnectTimeout { get; set; }
|
||||
int ReadTimeout { get; set; }
|
||||
int DataConnectionConnectTimeout { get; set; }
|
||||
int DataConnectionReadTimeout { get; set; }
|
||||
bool SocketKeepAlive { get; set; }
|
||||
FtpCapability Capabilities { get; }
|
||||
FtpHashAlgorithm HashAlgorithms { get; }
|
||||
FtpEncryptionMode EncryptionMode { get; set; }
|
||||
bool DataConnectionEncryption { get; set; }
|
||||
SslProtocols SslProtocols { get; set; }
|
||||
string SystemType { get; }
|
||||
string ConnectionType { get; }
|
||||
FtpParser ListingParser { get; set; }
|
||||
CultureInfo ListingCulture { get; set; }
|
||||
double TimeOffset { get; set; }
|
||||
bool RecursiveList { get; set; }
|
||||
bool BulkListing { get; set; }
|
||||
int BulkListingLength { get; set; }
|
||||
int TransferChunkSize { get; set; }
|
||||
int RetryAttempts { get; set; }
|
||||
uint UploadRateLimit { get; set; }
|
||||
uint DownloadRateLimit { get; set; }
|
||||
FtpDataType UploadDataType { get; set; }
|
||||
FtpDataType DownloadDataType { get; set; }
|
||||
event FtpSslValidation ValidateCertificate;
|
||||
FtpReply Execute(string command);
|
||||
FtpReply GetReply();
|
||||
void Connect();
|
||||
void Disconnect();
|
||||
bool HasFeature(FtpCapability cap);
|
||||
void DisableUTF8();
|
||||
|
||||
|
||||
// MANAGEMENT
|
||||
|
||||
void DeleteFile(string path);
|
||||
void DeleteDirectory(string path);
|
||||
void DeleteDirectory(string path, FtpListOption options);
|
||||
bool DirectoryExists(string path);
|
||||
bool FileExists(string path);
|
||||
void CreateDirectory(string path);
|
||||
void CreateDirectory(string path, bool force);
|
||||
void Rename(string path, string dest);
|
||||
bool MoveFile(string path, string dest, FtpExists existsMode = FtpExists.Overwrite);
|
||||
bool MoveDirectory(string path, string dest, FtpExists existsMode = FtpExists.Overwrite);
|
||||
void SetFilePermissions(string path, int permissions);
|
||||
void Chmod(string path, int permissions);
|
||||
void SetFilePermissions(string path, FtpPermission owner, FtpPermission group, FtpPermission other);
|
||||
void Chmod(string path, FtpPermission owner, FtpPermission group, FtpPermission other);
|
||||
FtpListItem GetFilePermissions(string path);
|
||||
int GetChmod(string path);
|
||||
FtpListItem DereferenceLink(FtpListItem item);
|
||||
FtpListItem DereferenceLink(FtpListItem item, int recMax);
|
||||
void SetWorkingDirectory(string path);
|
||||
string GetWorkingDirectory();
|
||||
long GetFileSize(string path);
|
||||
DateTime GetModifiedTime(string path, FtpDate type = FtpDate.Original);
|
||||
void SetModifiedTime(string path, DateTime date, FtpDate type = FtpDate.Original);
|
||||
|
||||
|
||||
// LISTING
|
||||
|
||||
FtpListItem GetObjectInfo(string path, bool dateModified = false);
|
||||
FtpListItem[] GetListing();
|
||||
FtpListItem[] GetListing(string path);
|
||||
FtpListItem[] GetListing(string path, FtpListOption options);
|
||||
string[] GetNameListing();
|
||||
string[] GetNameListing(string path);
|
||||
|
||||
|
||||
// LOW LEVEL
|
||||
|
||||
Stream OpenRead(string path);
|
||||
Stream OpenRead(string path, FtpDataType type);
|
||||
Stream OpenRead(string path, FtpDataType type, bool checkIfFileExists);
|
||||
Stream OpenRead(string path, FtpDataType type, long restart);
|
||||
Stream OpenRead(string path, long restart);
|
||||
Stream OpenRead(string path, long restart, bool checkIfFileExists);
|
||||
Stream OpenRead(string path, FtpDataType type, long restart, bool checkIfFileExists);
|
||||
Stream OpenWrite(string path);
|
||||
Stream OpenWrite(string path, FtpDataType type);
|
||||
Stream OpenWrite(string path, FtpDataType type, bool checkIfFileExists);
|
||||
Stream OpenAppend(string path);
|
||||
Stream OpenAppend(string path, FtpDataType type);
|
||||
Stream OpenAppend(string path, FtpDataType type, bool checkIfFileExists);
|
||||
|
||||
|
||||
// HIGH LEVEL
|
||||
|
||||
int UploadFiles(IEnumerable<string> localPaths, string remoteDir, FtpExists existsMode = FtpExists.Overwrite, bool createRemoteDir = true, FtpVerify verifyOptions = FtpVerify.None, FtpError errorHandling = FtpError.None);
|
||||
int UploadFiles(IEnumerable<FileInfo> localFiles, string remoteDir, FtpExists existsMode = FtpExists.Overwrite, bool createRemoteDir = true, FtpVerify verifyOptions = FtpVerify.None, FtpError errorHandling = FtpError.None);
|
||||
int DownloadFiles(string localDir, IEnumerable<string> remotePaths, bool overwrite = true, FtpVerify verifyOptions = FtpVerify.None, FtpError errorHandling = FtpError.None);
|
||||
|
||||
bool UploadFile(string localPath, string remotePath, FtpExists existsMode = FtpExists.Overwrite, bool createRemoteDir = false, FtpVerify verifyOptions = FtpVerify.None, IProgress<double> progress = null);
|
||||
bool Upload(Stream fileStream, string remotePath, FtpExists existsMode = FtpExists.Overwrite, bool createRemoteDir = false, IProgress<double> progress = null);
|
||||
bool Upload(byte[] fileData, string remotePath, FtpExists existsMode = FtpExists.Overwrite, bool createRemoteDir = false, IProgress<double> progress = null);
|
||||
|
||||
bool DownloadFile(string localPath, string remotePath, bool overwrite = true, FtpVerify verifyOptions = FtpVerify.None, IProgress<double> progress = null);
|
||||
bool Download(Stream outStream, string remotePath, IProgress<double> progress = null);
|
||||
bool Download(out byte[] outBytes, string remotePath, IProgress<double> progress = null);
|
||||
|
||||
|
||||
// HASH
|
||||
|
||||
FtpHashAlgorithm GetHashAlgorithm();
|
||||
void SetHashAlgorithm(FtpHashAlgorithm type);
|
||||
FtpHash GetHash(string path);
|
||||
FtpHash GetChecksum(string path);
|
||||
string GetMD5(string path);
|
||||
string GetXCRC(string path);
|
||||
string GetXMD5(string path);
|
||||
string GetXSHA1(string path);
|
||||
string GetXSHA256(string path);
|
||||
string GetXSHA512(string path);
|
||||
|
||||
}
|
||||
}
|
||||
606
FluentFTP/Helpers/FtpEnums.cs
Normal file
606
FluentFTP/Helpers/FtpEnums.cs
Normal file
@@ -0,0 +1,606 @@
|
||||
using System;
|
||||
|
||||
namespace FluentFTP {
|
||||
|
||||
/// <summary>
|
||||
/// Defines the type of encryption to use
|
||||
/// </summary>
|
||||
public enum FtpEncryptionMode {
|
||||
/// <summary>
|
||||
/// Plain text.
|
||||
/// </summary>
|
||||
None,
|
||||
/// <summary>
|
||||
/// FTPS encryption is used from the start of the connection, port 990.
|
||||
/// </summary>
|
||||
Implicit,
|
||||
/// <summary>
|
||||
/// Connection starts in plain text and FTPS encryption is enabled
|
||||
/// with the AUTH command immediately after the server greeting.
|
||||
/// </summary>
|
||||
Explicit
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The type of response the server responded with
|
||||
/// </summary>
|
||||
public enum FtpResponseType : int {
|
||||
/// <summary>
|
||||
/// No response
|
||||
/// </summary>
|
||||
None = 0,
|
||||
/// <summary>
|
||||
/// Success
|
||||
/// </summary>
|
||||
PositivePreliminary = 1,
|
||||
/// <summary>
|
||||
/// Success
|
||||
/// </summary>
|
||||
PositiveCompletion = 2,
|
||||
/// <summary>
|
||||
/// Success
|
||||
/// </summary>
|
||||
PositiveIntermediate = 3,
|
||||
/// <summary>
|
||||
/// Temporary failure
|
||||
/// </summary>
|
||||
TransientNegativeCompletion = 4,
|
||||
/// <summary>
|
||||
/// Permanent failure
|
||||
/// </summary>
|
||||
PermanentNegativeCompletion = 5
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Server features
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum FtpCapability : int {
|
||||
/// <summary>
|
||||
/// This server said it doesn't support anything!
|
||||
/// </summary>
|
||||
NONE = 0,
|
||||
/// <summary>
|
||||
/// Supports the MLST command
|
||||
/// </summary>
|
||||
MLSD = 1,
|
||||
/// <summary>
|
||||
/// Supports the SIZE command
|
||||
/// </summary>
|
||||
SIZE = 2,
|
||||
/// <summary>
|
||||
/// Supports the MDTM command
|
||||
/// </summary>
|
||||
MDTM = 4,
|
||||
/// <summary>
|
||||
/// Supports download/upload stream resumes
|
||||
/// </summary>
|
||||
REST = 8,
|
||||
/// <summary>
|
||||
/// Supports UTF8
|
||||
/// </summary>
|
||||
UTF8 = 16,
|
||||
/// <summary>
|
||||
/// PRET Command used in distributed ftp server software DrFTPD
|
||||
/// </summary>
|
||||
PRET = 32,
|
||||
/// <summary>
|
||||
/// Server supports the MFMT command for setting the
|
||||
/// modified date of an object on the server
|
||||
/// </summary>
|
||||
MFMT = 64,
|
||||
/// <summary>
|
||||
/// Server supports the MFCT command for setting the
|
||||
/// created date of an object on the server
|
||||
/// </summary>
|
||||
MFCT = 128,
|
||||
/// <summary>
|
||||
/// Server supports the MFF command for setting certain facts
|
||||
/// about file system objects. If you need this command, it would
|
||||
/// probably be handy to query FEAT your self and have a look at
|
||||
/// the FtpReply.InfoMessages property to see which facts the server
|
||||
/// allows you to modify.
|
||||
/// </summary>
|
||||
MFF = 256,
|
||||
/// <summary>
|
||||
/// Server supports the STAT command
|
||||
/// </summary>
|
||||
STAT = 512,
|
||||
/// <summary>
|
||||
/// Support for the HASH command
|
||||
/// </summary>
|
||||
HASH = 1024,
|
||||
/// <summary>
|
||||
/// Support for the non-standard MD5 command
|
||||
/// </summary>
|
||||
MD5 = 2048,
|
||||
/// <summary>
|
||||
/// Support for the non-standard XMD5 command
|
||||
/// </summary>
|
||||
XMD5 = 4096,
|
||||
/// <summary>
|
||||
/// Support for the non-standard XCRC command
|
||||
/// </summary>
|
||||
XCRC = 8192,
|
||||
/// <summary>
|
||||
/// Support for the non-standard XSHA1 command
|
||||
/// </summary>
|
||||
XSHA1 = 16384,
|
||||
/// <summary>
|
||||
/// Support for the non-standard XSHA256 command
|
||||
/// </summary>
|
||||
XSHA256 = 32768,
|
||||
/// <summary>
|
||||
/// Support for the non-standard XSHA512 command
|
||||
/// </summary>
|
||||
XSHA512 = 65536
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Different types of hashing algorithms for computing checksums.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum FtpHashAlgorithm : int {
|
||||
/// <summary>
|
||||
/// HASH command is not supported
|
||||
/// </summary>
|
||||
NONE = 0,
|
||||
/// <summary>
|
||||
/// SHA-1
|
||||
/// </summary>
|
||||
SHA1 = 1,
|
||||
/// <summary>
|
||||
/// SHA-256
|
||||
/// </summary>
|
||||
SHA256 = 2,
|
||||
/// <summary>
|
||||
/// SHA-512
|
||||
/// </summary>
|
||||
SHA512 = 4,
|
||||
/// <summary>
|
||||
/// MD5
|
||||
/// </summary>
|
||||
MD5 = 8,
|
||||
/// <summary>
|
||||
/// CRC
|
||||
/// </summary>
|
||||
CRC = 16
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// IP Versions to allow when connecting
|
||||
/// to a server.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum FtpIpVersion : int {
|
||||
/// <summary>
|
||||
/// Internet Protocol Version 4
|
||||
/// </summary>
|
||||
IPv4 = 1,
|
||||
/// <summary>
|
||||
/// Internet Protocol Version 6
|
||||
/// </summary>
|
||||
IPv6 = 2,
|
||||
/// <summary>
|
||||
/// Allow any supported version
|
||||
/// </summary>
|
||||
ANY = IPv4 | IPv6
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Data connection type
|
||||
/// </summary>
|
||||
public enum FtpDataConnectionType {
|
||||
/// <summary>
|
||||
/// This type of data connection attempts to use the EPSV command
|
||||
/// and if the server does not support EPSV it falls back to the
|
||||
/// PASV command before giving up unless you are connected via IPv6
|
||||
/// in which case the PASV command is not supported.
|
||||
/// </summary>
|
||||
AutoPassive,
|
||||
/// <summary>
|
||||
/// Passive data connection. EPSV is a better
|
||||
/// option if it's supported. Passive connections
|
||||
/// connect to the IP address dictated by the server
|
||||
/// which may or may not be accessible by the client
|
||||
/// for example a server behind a NAT device may
|
||||
/// give an IP address on its local network that
|
||||
/// is inaccessible to the client. Please note that IPv6
|
||||
/// does not support this type data connection. If you
|
||||
/// ask for PASV and are connected via IPv6 EPSV will
|
||||
/// automatically be used in its place.
|
||||
/// </summary>
|
||||
PASV,
|
||||
/// <summary>
|
||||
/// Same as PASV except the host supplied by the server is ignored
|
||||
/// and the data connection is made to the same address that the control
|
||||
/// connection is connected to. This is useful in scenarios where the
|
||||
/// server supplies a private/non-routable network address in the
|
||||
/// PASV response. It's functionally identical to EPSV except some
|
||||
/// servers may not implement the EPSV command. Please note that IPv6
|
||||
/// does not support this type data connection. If you
|
||||
/// ask for PASV and are connected via IPv6 EPSV will
|
||||
/// automatically be used in its place.
|
||||
/// </summary>
|
||||
PASVEX,
|
||||
/// <summary>
|
||||
/// Extended passive data connection, recommended. Works
|
||||
/// the same as a PASV connection except the server
|
||||
/// does not dictate an IP address to connect to, instead
|
||||
/// the passive connection goes to the same address used
|
||||
/// in the control connection. This type of data connection
|
||||
/// supports IPv4 and IPv6.
|
||||
/// </summary>
|
||||
EPSV,
|
||||
/// <summary>
|
||||
/// This type of data connection attempts to use the EPRT command
|
||||
/// and if the server does not support EPRT it falls back to the
|
||||
/// PORT command before giving up unless you are connected via IPv6
|
||||
/// in which case the PORT command is not supported.
|
||||
/// </summary>
|
||||
AutoActive,
|
||||
/// <summary>
|
||||
/// Active data connection, not recommended unless
|
||||
/// you have a specific reason for using this type.
|
||||
/// Creates a listening socket on the client which
|
||||
/// requires firewall exceptions on the client system
|
||||
/// as well as client network when connecting to a
|
||||
/// server outside of the client's network. In addition
|
||||
/// the IP address of the interface used to connect to the
|
||||
/// server is the address the server is told to connect to
|
||||
/// which, if behind a NAT device, may be inaccessible to
|
||||
/// the server. This type of data connection is not supported
|
||||
/// by IPv6. If you specify PORT and are connected via IPv6
|
||||
/// EPRT will automatically be used instead.
|
||||
/// </summary>
|
||||
PORT,
|
||||
/// <summary>
|
||||
/// Extended active data connection, not recommended
|
||||
/// unless you have a specific reason for using this
|
||||
/// type. Creates a listening socket on the client
|
||||
/// which requires firewall exceptions on the client
|
||||
/// as well as client network when connecting to a
|
||||
/// server outside of the client's network. The server
|
||||
/// connects to the IP address it sees the client coming
|
||||
/// from. This type of data connection supports IPv4 and IPv6.
|
||||
/// </summary>
|
||||
EPRT
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Type of data transfer to do
|
||||
/// </summary>
|
||||
public enum FtpDataType {
|
||||
/// <summary>
|
||||
/// ASCII transfer
|
||||
/// </summary>
|
||||
ASCII,
|
||||
/// <summary>
|
||||
/// Binary transfer
|
||||
/// </summary>
|
||||
Binary
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Type of file system of object
|
||||
/// </summary>
|
||||
public enum FtpFileSystemObjectType {
|
||||
/// <summary>
|
||||
/// A file
|
||||
/// </summary>
|
||||
File,
|
||||
/// <summary>
|
||||
/// A directory
|
||||
/// </summary>
|
||||
Directory,
|
||||
/// <summary>
|
||||
/// A symbolic link
|
||||
/// </summary>
|
||||
Link
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Types of file permissions
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum FtpPermission : uint {
|
||||
/// <summary>
|
||||
/// No access
|
||||
/// </summary>
|
||||
None = 0,
|
||||
/// <summary>
|
||||
/// Executable
|
||||
/// </summary>
|
||||
Execute = 1,
|
||||
/// <summary>
|
||||
/// Writable
|
||||
/// </summary>
|
||||
Write = 2,
|
||||
/// <summary>
|
||||
/// Readable
|
||||
/// </summary>
|
||||
Read = 4
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Types of special UNIX permissions
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum FtpSpecialPermissions : int {
|
||||
/// <summary>
|
||||
/// No special permissions are set
|
||||
/// </summary>
|
||||
None = 0,
|
||||
/// <summary>
|
||||
/// Sticky bit is set
|
||||
/// </summary>
|
||||
Sticky = 1,
|
||||
/// <summary>
|
||||
/// SGID bit is set
|
||||
/// </summary>
|
||||
SetGroupID = 2,
|
||||
/// <summary>
|
||||
/// SUID bit is set
|
||||
/// </summary>
|
||||
SetUserID = 4
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The type of response the server responded with
|
||||
/// </summary>
|
||||
public enum FtpParser : int {
|
||||
/// <summary>
|
||||
/// Use the legacy parser (for older projects that depend on the pre-2017 parser routines).
|
||||
/// </summary>
|
||||
Legacy = -1,
|
||||
/// <summary>
|
||||
/// Automatically detect the file listing parser to use based on the FTP server (SYST command).
|
||||
/// </summary>
|
||||
Auto = 0,
|
||||
/// <summary>
|
||||
/// Machine listing parser, works on any FTP server supporting the MLST/MLSD commands.
|
||||
/// </summary>
|
||||
Machine = 1,
|
||||
/// <summary>
|
||||
/// File listing parser for Windows/IIS.
|
||||
/// </summary>
|
||||
Windows = 2,
|
||||
/// <summary>
|
||||
/// File listing parser for Unix.
|
||||
/// </summary>
|
||||
Unix = 3,
|
||||
/// <summary>
|
||||
/// Alternate parser for Unix. Use this if the default one does not work.
|
||||
/// </summary>
|
||||
UnixAlt = 4,
|
||||
/// <summary>
|
||||
/// File listing parser for Vax/VMS/OpenVMS.
|
||||
/// </summary>
|
||||
VMS = 5,
|
||||
/// <summary>
|
||||
/// File listing parser for IBM OS400.
|
||||
/// </summary>
|
||||
IBM = 6,
|
||||
/// <summary>
|
||||
/// File listing parser for Tandem/Nonstop Guardian OS.
|
||||
/// </summary>
|
||||
NonStop = 7
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Flags that can dictate how a file listing is performed
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum FtpListOption {
|
||||
/// <summary>
|
||||
/// Tries machine listings (MDTM command) if supported,
|
||||
/// and if not then falls back to OS-specific listings (LIST command)
|
||||
/// </summary>
|
||||
Auto = 0,
|
||||
/// <summary>
|
||||
/// Load the modify date using MDTM when it could not
|
||||
/// be parsed from the server listing. This only pertains
|
||||
/// to servers that do not implement the MLSD command.
|
||||
/// </summary>
|
||||
Modify = 1,
|
||||
/// <summary>
|
||||
/// Load the file size using the SIZE command when it
|
||||
/// could not be parsed from the server listing. This
|
||||
/// only pertains to servers that do not support the
|
||||
/// MLSD command.
|
||||
/// </summary>
|
||||
Size = 2,
|
||||
/// <summary>
|
||||
/// Combines the Modify and Size flags
|
||||
/// </summary>
|
||||
SizeModify = Modify | Size,
|
||||
/// <summary>
|
||||
/// Show hidden/dot files. This only pertains to servers
|
||||
/// that do not support the MLSD command. This option
|
||||
/// makes use the non standard -a parameter to LIST to
|
||||
/// tell the server to show hidden files. Since it's a
|
||||
/// non-standard option it may not always work. MLSD listings
|
||||
/// have no such option and whether or not a hidden file is
|
||||
/// shown is at the discretion of the server.
|
||||
/// </summary>
|
||||
AllFiles = 4,
|
||||
/// <summary>
|
||||
/// Force the use of OS-specific listings (LIST command) even if
|
||||
/// machine listings (MLSD command) are supported by the server
|
||||
/// </summary>
|
||||
ForceList = 8,
|
||||
/// <summary>
|
||||
/// Use the NLST command instead of LIST for a reliable file listing
|
||||
/// </summary>
|
||||
NameList = 16,
|
||||
/// <summary>
|
||||
/// Force the use of the NLST command (the slowest mode) even if machine listings
|
||||
/// and OS-specific listings are supported by the server
|
||||
/// </summary>
|
||||
ForceNameList = ForceList | NameList,
|
||||
/// <summary>
|
||||
/// Try to dereference symbolic links, and stored the linked file/directory in FtpListItem.LinkObject
|
||||
/// </summary>
|
||||
DerefLinks = 32,
|
||||
/// <summary>
|
||||
/// Sets the ForceList flag and uses `LS' instead of `LIST' as the
|
||||
/// command for getting a directory listing. This option overrides
|
||||
/// ForceNameList and ignores the AllFiles flag.
|
||||
/// </summary>
|
||||
UseLS = 64 | ForceList,
|
||||
/// <summary>
|
||||
/// Gets files within subdirectories as well. Adds the -r option to the LIST command.
|
||||
/// Some servers may not support this feature.
|
||||
/// </summary>
|
||||
Recursive = 128,
|
||||
/// <summary>
|
||||
/// Do not retrieve path when no path is supplied to GetListing(),
|
||||
/// instead just execute LIST with no path argument.
|
||||
/// </summary>
|
||||
NoPath = 256,
|
||||
/// <summary>
|
||||
/// Include two extra items into the listing, for the current directory (".")
|
||||
/// and the parent directory (".."). Meaningless unless you want these two
|
||||
/// items for some reason.
|
||||
/// </summary>
|
||||
IncludeSelfAndParent = 512
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines the behavior for uploading/downloading files that already exist
|
||||
/// </summary>
|
||||
public enum FtpExists {
|
||||
/// <summary>
|
||||
/// Do not check if the file exists. A bit faster than the other options. Only use this if you are SURE that the file does not exist on the server.
|
||||
/// Otherwise it can cause the UploadFile method to hang due to filesize mismatch.
|
||||
/// </summary>
|
||||
NoCheck,
|
||||
/// <summary>
|
||||
/// Skip the file if it exists, without any more checks.
|
||||
/// </summary>
|
||||
Skip,
|
||||
/// <summary>
|
||||
/// Overwrite the file if it exists.
|
||||
/// </summary>
|
||||
Overwrite,
|
||||
/// <summary>
|
||||
/// Append to the file if it exists, by checking the length and adding the missing data.
|
||||
/// </summary>
|
||||
Append
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines the level of the tracing message. Depending on the framework version this is translated
|
||||
/// to an equivalent logging level in System.Diagnostices (if available)
|
||||
/// </summary>
|
||||
public enum FtpTraceLevel {
|
||||
/// <summary>
|
||||
/// Used for logging Debug or Verbose level messages
|
||||
/// </summary>
|
||||
Verbose,
|
||||
/// <summary>
|
||||
/// Used for logging Informational messages
|
||||
/// </summary>
|
||||
Info,
|
||||
/// <summary>
|
||||
/// Used for logging non-fatal or ignorable error messages
|
||||
/// </summary>
|
||||
Warn,
|
||||
/// <summary>
|
||||
/// Used for logging Error messages that may need investigation
|
||||
/// </summary>
|
||||
Error
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines how multi-file processes should handle a processing error.
|
||||
/// </summary>
|
||||
/// <remarks><see cref="FtpError.Stop"/> & <see cref="FtpError.Throw"/> Cannot Be Combined</remarks>
|
||||
[Flags]
|
||||
public enum FtpError {
|
||||
/// <summary>
|
||||
/// No action is taken upon errors. The method absorbs the error and continues.
|
||||
/// </summary>
|
||||
None = 0,
|
||||
/// <summary>
|
||||
/// If any files have completed successfully (or failed after a partial download/upload) then should be deleted.
|
||||
/// This will simulate an all-or-nothing transaction downloading or uploading multiple files. If this option is not
|
||||
/// combined with <see cref="FtpError.Stop"/> or <see cref="FtpError.Throw"/> then the method will
|
||||
/// continue to process all items whether if they are successful or not and then delete everything if a failure was
|
||||
/// encountered at any point.
|
||||
/// </summary>
|
||||
DeleteProcessed = 1,
|
||||
/// <summary>
|
||||
/// The method should stop processing any additional files and immediately return upon encountering an error.
|
||||
/// Cannot be combined with <see cref="FtpError.Throw"/>
|
||||
/// </summary>
|
||||
Stop = 2,
|
||||
/// <summary>
|
||||
/// The method should stop processing any additional files and immediately throw the current error.
|
||||
/// Cannot be combined with <see cref="FtpError.Stop"/>
|
||||
/// </summary>
|
||||
Throw = 4,
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines if additional verification and actions upon failure that
|
||||
/// should be performed when uploading/downloading files using the high-level APIs. Ignored if the
|
||||
/// FTP server does not support any hashing algorithms.
|
||||
/// </summary>
|
||||
[Flags]
|
||||
public enum FtpVerify {
|
||||
/// <summary>
|
||||
/// No verification of the file is performed
|
||||
/// </summary>
|
||||
None = 0,
|
||||
/// <summary>
|
||||
/// The checksum of the file is verified, if supported by the server.
|
||||
/// If the checksum comparison fails then we retry the download/upload
|
||||
/// a specified amount of times before giving up. (See <see cref="FtpClient.RetryAttempts"/>)
|
||||
/// </summary>
|
||||
Retry = 1,
|
||||
/// <summary>
|
||||
/// The checksum of the file is verified, if supported by the server.
|
||||
/// If the checksum comparison fails then the failed file will be deleted.
|
||||
/// If combined with <see cref="FtpVerify.Retry"/>, then
|
||||
/// the deletion will occur if it fails upon the final retry.
|
||||
/// </summary>
|
||||
Delete = 2,
|
||||
/// <summary>
|
||||
/// The checksum of the file is verified, if supported by the server.
|
||||
/// If the checksum comparison fails then an exception will be thrown.
|
||||
/// If combined with <see cref="FtpVerify.Retry"/>, then the throw will
|
||||
/// occur upon the failure of the final retry, and/or if combined with <see cref="FtpVerify.Delete"/>
|
||||
/// the method will throw after the deletion is processed.
|
||||
/// </summary>
|
||||
Throw = 4,
|
||||
/// <summary>
|
||||
/// The checksum of the file is verified, if supported by the server.
|
||||
/// If the checksum comparison fails then the method returns false and no other action is taken.
|
||||
/// </summary>
|
||||
OnlyChecksum = 8,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Defines if additional verification and actions upon failure that
|
||||
/// should be performed when uploading/downloading files using the high-level APIs. Ignored if the
|
||||
/// FTP server does not support any hashing algorithms.
|
||||
/// </summary>
|
||||
public enum FtpDate {
|
||||
/// <summary>
|
||||
/// The date is whatever the server returns, with no conversion performed.
|
||||
/// </summary>
|
||||
Original = 0,
|
||||
#if !CORE
|
||||
/// <summary>
|
||||
/// The date is converted to the local timezone, based on the TimeOffset property in FtpClient.
|
||||
/// </summary>
|
||||
Local = 1,
|
||||
#endif
|
||||
/// <summary>
|
||||
/// The date is converted to UTC, based on the TimeOffset property in FtpClient.
|
||||
/// </summary>
|
||||
UTC = 2,
|
||||
}
|
||||
}
|
||||
92
FluentFTP/Helpers/FtpEvents.cs
Normal file
92
FluentFTP/Helpers/FtpEvents.cs
Normal file
@@ -0,0 +1,92 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Net.Sockets;
|
||||
using System.Net.Security;
|
||||
using System.Security.Authentication;
|
||||
using System.Security.Cryptography.X509Certificates;
|
||||
using System.Threading;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Net;
|
||||
|
||||
#if NET45
|
||||
using System.Threading.Tasks;
|
||||
#endif
|
||||
|
||||
namespace FluentFTP {
|
||||
|
||||
/// <summary>
|
||||
/// Event is fired when a SSL certificate needs to be validated
|
||||
/// </summary>
|
||||
/// <param name="control">The control connection that triggered the event</param>
|
||||
/// <param name="e">Event args</param>
|
||||
public delegate void FtpSslValidation(FtpClient control, FtpSslValidationEventArgs e);
|
||||
|
||||
/// <summary>
|
||||
/// Event fired if a bad SSL certificate is encountered. This even is used internally; if you
|
||||
/// don't have a specific reason for using it you are probably looking for FtpSslValidation.
|
||||
/// </summary>
|
||||
/// <param name="stream"></param>
|
||||
/// <param name="e"></param>
|
||||
public delegate void FtpSocketStreamSslValidation(FtpSocketStream stream, FtpSslValidationEventArgs e);
|
||||
|
||||
/// <summary>
|
||||
/// Event args for the FtpSslValidationError delegate
|
||||
/// </summary>
|
||||
public class FtpSslValidationEventArgs : EventArgs {
|
||||
X509Certificate m_certificate = null;
|
||||
/// <summary>
|
||||
/// The certificate to be validated
|
||||
/// </summary>
|
||||
public X509Certificate Certificate {
|
||||
get {
|
||||
return m_certificate;
|
||||
}
|
||||
set {
|
||||
m_certificate = value;
|
||||
}
|
||||
}
|
||||
|
||||
X509Chain m_chain = null;
|
||||
/// <summary>
|
||||
/// The certificate chain
|
||||
/// </summary>
|
||||
public X509Chain Chain {
|
||||
get {
|
||||
return m_chain;
|
||||
}
|
||||
set {
|
||||
m_chain = value;
|
||||
}
|
||||
}
|
||||
|
||||
SslPolicyErrors m_policyErrors = SslPolicyErrors.None;
|
||||
/// <summary>
|
||||
/// Validation errors, if any.
|
||||
/// </summary>
|
||||
public SslPolicyErrors PolicyErrors {
|
||||
get {
|
||||
return m_policyErrors;
|
||||
}
|
||||
set {
|
||||
m_policyErrors = value;
|
||||
}
|
||||
}
|
||||
|
||||
bool m_accept = false;
|
||||
/// <summary>
|
||||
/// Gets or sets a value indicating if this certificate should be accepted. The default
|
||||
/// value is false. If the certificate is not accepted, an AuthenticationException will
|
||||
/// be thrown.
|
||||
/// </summary>
|
||||
public bool Accept {
|
||||
get {
|
||||
return m_accept;
|
||||
}
|
||||
set {
|
||||
m_accept = value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
127
FluentFTP/Helpers/FtpExceptions.cs
Normal file
127
FluentFTP/Helpers/FtpExceptions.cs
Normal file
@@ -0,0 +1,127 @@
|
||||
using System;
|
||||
#if !CORE
|
||||
using System.Runtime.Serialization;
|
||||
#endif
|
||||
|
||||
namespace FluentFTP {
|
||||
/// <summary>
|
||||
/// FTP related error
|
||||
/// </summary>
|
||||
#if !CORE
|
||||
[Serializable]
|
||||
#endif
|
||||
public class FtpException : Exception {
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FtpException"/> class.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message</param>
|
||||
public FtpException(string message) : base(message) { }
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="FtpException"/> class with an inner exception.
|
||||
/// </summary>
|
||||
/// <param name="message">The error message that explains the reason for the exception.</param>
|
||||
/// <param name="innerException">The exception that is the cause of the current exception, or a null reference (Nothing in Visual Basic) if no inner exception is specified.</param>
|
||||
public FtpException(string message, Exception innerException) : base(message, innerException) { }
|
||||
|
||||
#if !CORE
|
||||
/// <summary>
|
||||
/// Must be implemented so every Serializer can Deserialize the Exception
|
||||
/// </summary>
|
||||
protected FtpException(SerializationInfo info, StreamingContext context) : base(info, context) { }
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exception triggered on command failures
|
||||
/// </summary>
|
||||
#if !CORE
|
||||
[Serializable]
|
||||
#endif
|
||||
public class FtpCommandException : FtpException {
|
||||
string _code = null;
|
||||
/// <summary>
|
||||
/// Gets the completion code associated with the response
|
||||
/// </summary>
|
||||
public string CompletionCode {
|
||||
get { return _code; }
|
||||
private set { _code = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The type of response received from the last command executed
|
||||
/// </summary>
|
||||
public FtpResponseType ResponseType {
|
||||
get {
|
||||
if (_code != null) {
|
||||
// we only care about error types, if an exception
|
||||
// is being thrown for a successful response there
|
||||
// is a problem.
|
||||
switch (_code[0]) {
|
||||
case '4':
|
||||
return FtpResponseType.TransientNegativeCompletion;
|
||||
case '5':
|
||||
return FtpResponseType.PermanentNegativeCompletion;
|
||||
}
|
||||
}
|
||||
|
||||
return FtpResponseType.None;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of a FtpResponseException
|
||||
/// </summary>
|
||||
/// <param name="code">Status code</param>
|
||||
/// <param name="message">Associated message</param>
|
||||
public FtpCommandException(string code, string message)
|
||||
: base(message) {
|
||||
CompletionCode = code;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes 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) {
|
||||
}
|
||||
|
||||
#if !CORE
|
||||
/// <summary>
|
||||
/// Must be implemented so every Serializer can Deserialize the Exception
|
||||
/// </summary>
|
||||
protected FtpCommandException(SerializationInfo info, StreamingContext context) : base(info, context) { }
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Exception is thrown when encryption could not be negotiated by the server
|
||||
/// </summary>
|
||||
#if !CORE
|
||||
[Serializable]
|
||||
#endif
|
||||
public class FtpSecurityNotAvailableException : FtpException {
|
||||
/// <summary>
|
||||
/// Default constructor
|
||||
/// </summary>
|
||||
public FtpSecurityNotAvailableException()
|
||||
: base("Security is not available on the server.") {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Custom error message
|
||||
/// </summary>
|
||||
/// <param name="message">Error message</param>
|
||||
public FtpSecurityNotAvailableException(string message)
|
||||
: base(message) {
|
||||
}
|
||||
|
||||
#if !CORE
|
||||
/// <summary>
|
||||
/// Must be implemented so every Serializer can Deserialize the Exception
|
||||
/// </summary>
|
||||
protected FtpSecurityNotAvailableException(SerializationInfo info, StreamingContext context) : base(info, context) { }
|
||||
#endif
|
||||
}
|
||||
}
|
||||
137
FluentFTP/Helpers/FtpHash.cs
Normal file
137
FluentFTP/Helpers/FtpHash.cs
Normal file
@@ -0,0 +1,137 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Security.Cryptography;
|
||||
|
||||
namespace FluentFTP {
|
||||
/// <summary>
|
||||
/// Represents a computed hash of an object
|
||||
/// on the FTP server. See the following link
|
||||
/// for more information:
|
||||
/// http://tools.ietf.org/html/draft-bryan-ftpext-hash-02
|
||||
/// </summary>
|
||||
public class FtpHash {
|
||||
FtpHashAlgorithm m_algorithm = FtpHashAlgorithm.NONE;
|
||||
/// <summary>
|
||||
/// Gets the algorithm that was used to compute the hash
|
||||
/// </summary>
|
||||
public FtpHashAlgorithm Algorithm {
|
||||
get { return m_algorithm; }
|
||||
internal set { m_algorithm = value; }
|
||||
}
|
||||
|
||||
string m_value = null;
|
||||
/// <summary>
|
||||
/// Gets the computed hash returned by the server
|
||||
/// </summary>
|
||||
public string Value {
|
||||
get { return m_value; }
|
||||
internal set { m_value = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a value indicating if this object represents a
|
||||
/// valid hash response from the server.
|
||||
/// </summary>
|
||||
public bool IsValid {
|
||||
get { return m_algorithm != FtpHashAlgorithm.NONE && !string.IsNullOrEmpty(m_value); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the hash for the specified file and compares
|
||||
/// it to the value in this object. CRC hashes are not supported
|
||||
/// because there is no built-in support in the .net framework and
|
||||
/// a CRC implementation exceeds the scope of this project. If you
|
||||
/// attempt to call this on a CRC hash a <see cref="NotImplementedException"/> will
|
||||
/// be thrown.
|
||||
/// </summary>
|
||||
/// <param name="file">The file to compute the hash for</param>
|
||||
/// <returns>True if the computed hash matches what's stored in this object.</returns>
|
||||
/// <exception cref="NotImplementedException">Thrown if called on a CRC Hash</exception>
|
||||
public bool Verify(string file) {
|
||||
using (FileStream istream = new FileStream(file, FileMode.Open, FileAccess.Read)) {
|
||||
return Verify(istream);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Computes the hash for the specified stream and compares
|
||||
/// it to the value in this object. CRC hashes are not supported
|
||||
/// because there is no built-in support in the .net framework and
|
||||
/// a CRC implementation exceeds the scope of this project. If you
|
||||
/// attempt to call this on a CRC hash a <see cref="NotImplementedException"/> will
|
||||
/// be thrown.
|
||||
/// </summary>
|
||||
/// <param name="istream">The stream to compute the hash for</param>
|
||||
/// <returns>True if the computed hash matches what's stored in this object.</returns>
|
||||
/// <exception cref="NotImplementedException">Thrown if called on a CRC Hash</exception>
|
||||
public bool Verify(Stream istream) {
|
||||
if (IsValid) {
|
||||
HashAlgorithm hashAlg = null;
|
||||
|
||||
switch (m_algorithm) {
|
||||
case FtpHashAlgorithm.SHA1:
|
||||
#if CORE
|
||||
hashAlg = SHA1.Create();
|
||||
#else
|
||||
hashAlg = new SHA1CryptoServiceProvider();
|
||||
#endif
|
||||
break;
|
||||
#if !NET20
|
||||
case FtpHashAlgorithm.SHA256:
|
||||
#if CORE
|
||||
hashAlg = SHA256.Create();
|
||||
#else
|
||||
hashAlg = new SHA256CryptoServiceProvider();
|
||||
#endif
|
||||
break;
|
||||
case FtpHashAlgorithm.SHA512:
|
||||
#if CORE
|
||||
hashAlg = SHA512.Create();
|
||||
#else
|
||||
hashAlg = new SHA512CryptoServiceProvider();
|
||||
#endif
|
||||
break;
|
||||
#endif
|
||||
case FtpHashAlgorithm.MD5:
|
||||
#if CORE
|
||||
hashAlg = MD5.Create();
|
||||
#else
|
||||
hashAlg = new MD5CryptoServiceProvider();
|
||||
#endif
|
||||
break;
|
||||
case FtpHashAlgorithm.CRC:
|
||||
throw new NotImplementedException("There is no built in support for computing CRC hashes.");
|
||||
default:
|
||||
throw new NotImplementedException("Unknown hash algorithm: " + m_algorithm.ToString());
|
||||
}
|
||||
|
||||
try {
|
||||
byte[] data = null;
|
||||
string hash = "";
|
||||
|
||||
data = hashAlg.ComputeHash(istream);
|
||||
if (data != null) {
|
||||
foreach (byte b in data) {
|
||||
hash += b.ToString("x2");
|
||||
}
|
||||
|
||||
return (hash.ToUpper() == m_value.ToUpper());
|
||||
}
|
||||
} finally {
|
||||
#if !NET20 && !NET35 // .NET 2.0 doesn't provide access to Dispose() for HashAlgorithm
|
||||
if (hashAlg != null)
|
||||
hashAlg.Dispose();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates an empty instance.
|
||||
/// </summary>
|
||||
internal FtpHash() {
|
||||
}
|
||||
}
|
||||
}
|
||||
297
FluentFTP/Helpers/FtpListItem.cs
Normal file
297
FluentFTP/Helpers/FtpListItem.cs
Normal file
@@ -0,0 +1,297 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Globalization;
|
||||
using System.Threading;
|
||||
|
||||
namespace FluentFTP {
|
||||
/// <summary>
|
||||
/// Represents a file system object on the server
|
||||
/// </summary>
|
||||
/// <example><code source="..\Examples\CustomParser.cs" lang="cs" /></example>
|
||||
public class FtpListItem {
|
||||
|
||||
/// <summary>
|
||||
/// Blank constructor; Fill args manually.
|
||||
///
|
||||
/// NOTE TO USER : You should not need to construct this class manually except in advanced cases. Typically constructed by GetListing().
|
||||
/// </summary>
|
||||
public FtpListItem() {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Constructor with mandatory args filled.
|
||||
///
|
||||
/// NOTE TO USER : You should not need to construct this class manually except in advanced cases. Typically constructed by GetListing().
|
||||
/// </summary>
|
||||
public FtpListItem(string raw, string name, long size, bool isDir, ref DateTime lastModifiedTime) {
|
||||
m_input = raw;
|
||||
m_name = name;
|
||||
m_size = size;
|
||||
m_type = isDir ? FtpFileSystemObjectType.Directory : FtpFileSystemObjectType.File;
|
||||
m_modified = lastModifiedTime;
|
||||
}
|
||||
|
||||
FtpFileSystemObjectType m_type = 0;
|
||||
/// <summary>
|
||||
/// Gets the type of file system object.
|
||||
/// </summary>
|
||||
public FtpFileSystemObjectType Type {
|
||||
get {
|
||||
return m_type;
|
||||
}
|
||||
set {
|
||||
m_type = value;
|
||||
}
|
||||
}
|
||||
|
||||
string m_path = null;
|
||||
/// <summary>
|
||||
/// Gets the full path name to the object.
|
||||
/// </summary>
|
||||
public string FullName {
|
||||
get {
|
||||
return m_path;
|
||||
}
|
||||
set {
|
||||
m_path = value;
|
||||
}
|
||||
}
|
||||
|
||||
string m_name = null;
|
||||
/// <summary>
|
||||
/// Gets the name of the object.
|
||||
/// </summary>
|
||||
public string Name {
|
||||
get {
|
||||
if (m_name == null && m_path != null)
|
||||
return m_path.GetFtpFileName();
|
||||
return m_name;
|
||||
}
|
||||
set {
|
||||
m_name = value;
|
||||
}
|
||||
}
|
||||
|
||||
string m_linkTarget = null;
|
||||
/// <summary>
|
||||
/// Gets the target a symbolic link points to.
|
||||
/// </summary>
|
||||
public string LinkTarget {
|
||||
get {
|
||||
return m_linkTarget;
|
||||
}
|
||||
set {
|
||||
m_linkTarget = value;
|
||||
}
|
||||
}
|
||||
|
||||
int m_linkCount = 0;
|
||||
/// <summary>
|
||||
/// Gets the number of links pointing to this file. Only supplied by Unix servers.
|
||||
/// </summary>
|
||||
public int LinkCount {
|
||||
get {
|
||||
return m_linkCount;
|
||||
}
|
||||
set {
|
||||
m_linkCount = value;
|
||||
}
|
||||
}
|
||||
|
||||
FtpListItem m_linkObject = null;
|
||||
/// <summary>
|
||||
/// Gets the object that the LinkTarget points to. This property is null unless you pass the
|
||||
/// <see cref="FtpListOption.DerefLinks"/> flag in which case GetListing() will try to resolve
|
||||
/// the target itself.
|
||||
/// </summary>
|
||||
public FtpListItem LinkObject {
|
||||
get {
|
||||
return m_linkObject;
|
||||
}
|
||||
set {
|
||||
m_linkObject = value;
|
||||
}
|
||||
}
|
||||
|
||||
DateTime m_modified = DateTime.MinValue;
|
||||
/// <summary>
|
||||
/// Gets the last write time of the object.
|
||||
/// </summary>
|
||||
public DateTime Modified {
|
||||
get {
|
||||
return m_modified;
|
||||
}
|
||||
set {
|
||||
m_modified = value;
|
||||
}
|
||||
}
|
||||
|
||||
DateTime m_created = DateTime.MinValue;
|
||||
/// <summary>
|
||||
/// Gets the created date of the object.
|
||||
/// </summary>
|
||||
public DateTime Created {
|
||||
get {
|
||||
return m_created;
|
||||
}
|
||||
set {
|
||||
m_created = value;
|
||||
}
|
||||
}
|
||||
|
||||
long m_size = -1;
|
||||
/// <summary>
|
||||
/// Gets the size of the object.
|
||||
/// </summary>
|
||||
public long Size {
|
||||
get {
|
||||
return m_size;
|
||||
}
|
||||
set {
|
||||
m_size = value;
|
||||
}
|
||||
}
|
||||
|
||||
FtpSpecialPermissions m_specialPermissions = FtpSpecialPermissions.None;
|
||||
/// <summary>
|
||||
/// Gets special UNIX permissions such as Sticky, SUID and SGID.
|
||||
/// </summary>
|
||||
public FtpSpecialPermissions SpecialPermissions {
|
||||
get {
|
||||
return m_specialPermissions;
|
||||
}
|
||||
set {
|
||||
m_specialPermissions = value;
|
||||
}
|
||||
}
|
||||
|
||||
FtpPermission m_ownerPermissions = FtpPermission.None;
|
||||
/// <summary>
|
||||
/// Gets the owner permissions.
|
||||
/// </summary>
|
||||
public FtpPermission OwnerPermissions {
|
||||
get {
|
||||
return m_ownerPermissions;
|
||||
}
|
||||
set {
|
||||
m_ownerPermissions = value;
|
||||
}
|
||||
}
|
||||
|
||||
FtpPermission m_groupPermissions = FtpPermission.None;
|
||||
/// <summary>
|
||||
/// Gets the group permissions.
|
||||
/// </summary>
|
||||
public FtpPermission GroupPermissions {
|
||||
get {
|
||||
return m_groupPermissions;
|
||||
}
|
||||
set {
|
||||
m_groupPermissions = value;
|
||||
}
|
||||
}
|
||||
|
||||
FtpPermission m_otherPermissions = FtpPermission.None;
|
||||
/// <summary>
|
||||
/// Gets the others permissions.
|
||||
/// </summary>
|
||||
public FtpPermission OthersPermissions {
|
||||
get {
|
||||
return m_otherPermissions;
|
||||
}
|
||||
set {
|
||||
m_otherPermissions = value;
|
||||
}
|
||||
}
|
||||
|
||||
string m_rawPermissions = null;
|
||||
/// <summary>
|
||||
/// Gets the raw string received for the file permissions.
|
||||
/// Use this if the other properties are blank/invalid.
|
||||
/// </summary>
|
||||
public string RawPermissions {
|
||||
get {
|
||||
return m_rawPermissions;
|
||||
}
|
||||
set {
|
||||
m_rawPermissions = value;
|
||||
}
|
||||
}
|
||||
|
||||
int m_chmod = 0;
|
||||
/// <summary>
|
||||
/// Gets the file permissions in the CHMOD format.
|
||||
/// </summary>
|
||||
public int Chmod {
|
||||
get {
|
||||
return m_chmod;
|
||||
}
|
||||
set {
|
||||
m_chmod = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the raw string received for the file's GROUP permissions.
|
||||
/// Use this if the other properties are blank/invalid.
|
||||
/// </summary>
|
||||
public string RawGroup = null;
|
||||
/// <summary>
|
||||
/// Gets the raw string received for the file's OWNER permissions.
|
||||
/// Use this if the other properties are blank/invalid.
|
||||
/// </summary>
|
||||
public string RawOwner = null;
|
||||
|
||||
|
||||
string m_input = null;
|
||||
/// <summary>
|
||||
/// Gets the input string that was parsed to generate the
|
||||
/// values in this object.
|
||||
/// </summary>
|
||||
public string Input {
|
||||
get {
|
||||
return m_input;
|
||||
}
|
||||
set {
|
||||
m_input = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a string representation of this object and its properties
|
||||
/// </summary>
|
||||
/// <returns>A string representing this object</returns>
|
||||
public override string ToString() {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
if (Type == FtpFileSystemObjectType.File) {
|
||||
sb.Append("FILE");
|
||||
} else if (Type == FtpFileSystemObjectType.Directory) {
|
||||
sb.Append("DIR ");
|
||||
} else if (Type == FtpFileSystemObjectType.Link) {
|
||||
sb.Append("LINK");
|
||||
}
|
||||
sb.Append(" ");
|
||||
sb.Append(Name);
|
||||
if (Type == FtpFileSystemObjectType.File) {
|
||||
sb.Append(" ");
|
||||
sb.Append("(");
|
||||
sb.Append(Size.FileSizeToString());
|
||||
sb.Append(")");
|
||||
}
|
||||
if (Created != DateTime.MinValue) {
|
||||
sb.Append(" ");
|
||||
sb.Append("Created : ");
|
||||
sb.Append(Created.ToString());
|
||||
}
|
||||
if (Modified != DateTime.MinValue) {
|
||||
sb.Append(" ");
|
||||
sb.Append("Modified : ");
|
||||
sb.Append(Modified.ToString());
|
||||
}
|
||||
return sb.ToString();
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
1738
FluentFTP/Helpers/FtpListParser.cs
Normal file
1738
FluentFTP/Helpers/FtpListParser.cs
Normal file
File diff suppressed because it is too large
Load Diff
113
FluentFTP/Helpers/FtpReply.cs
Normal file
113
FluentFTP/Helpers/FtpReply.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace FluentFTP {
|
||||
/// <summary>
|
||||
/// Represents a reply to an event on the server
|
||||
/// </summary>
|
||||
public struct FtpReply {
|
||||
/// <summary>
|
||||
/// The type of response received from the last command executed
|
||||
/// </summary>
|
||||
public FtpResponseType Type {
|
||||
get {
|
||||
int code;
|
||||
|
||||
if (Code != null && Code.Length > 0 &&
|
||||
int.TryParse(Code[0].ToString(), out code)) {
|
||||
return (FtpResponseType)code;
|
||||
}
|
||||
|
||||
return FtpResponseType.None;
|
||||
}
|
||||
}
|
||||
|
||||
string m_respCode;
|
||||
/// <summary>
|
||||
/// The status code of the response
|
||||
/// </summary>
|
||||
public string Code {
|
||||
get {
|
||||
return m_respCode;
|
||||
}
|
||||
set {
|
||||
m_respCode = value;
|
||||
}
|
||||
}
|
||||
|
||||
string m_respMessage;
|
||||
/// <summary>
|
||||
/// The message, if any, that the server sent with the response
|
||||
/// </summary>
|
||||
public string Message {
|
||||
get {
|
||||
return m_respMessage;
|
||||
}
|
||||
set {
|
||||
m_respMessage = value;
|
||||
}
|
||||
}
|
||||
|
||||
string m_infoMessages;
|
||||
/// <summary>
|
||||
/// Informational messages sent from the server
|
||||
/// </summary>
|
||||
public string InfoMessages {
|
||||
get {
|
||||
return m_infoMessages;
|
||||
}
|
||||
set {
|
||||
m_infoMessages = value;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// General success or failure of the last command executed
|
||||
/// </summary>
|
||||
public bool Success {
|
||||
get {
|
||||
if (Code != null && Code.Length > 0) {
|
||||
int i;
|
||||
|
||||
// 1xx, 2xx, 3xx indicate success
|
||||
// 4xx, 5xx are failures
|
||||
if (int.TryParse(Code[0].ToString(), out i) && i >= 1 && i <= 3) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the error message including any informational output
|
||||
/// that was sent by the server. Sometimes the final response
|
||||
/// line doesn't contain anything informative as to what was going
|
||||
/// on with the server. Instead it may send information messages so
|
||||
/// in an effort to give as meaningful as a response as possible
|
||||
/// the informational messages will be included in the error.
|
||||
/// </summary>
|
||||
public string ErrorMessage {
|
||||
get {
|
||||
string message = "";
|
||||
|
||||
if (Success) {
|
||||
return message;
|
||||
}
|
||||
|
||||
if (InfoMessages != null && InfoMessages.Length > 0) {
|
||||
foreach (string s in InfoMessages.Split('\n')) {
|
||||
string m = Regex.Replace(s, "^[0-9]{3}-", "");
|
||||
message += (m.Trim() + "; ");
|
||||
}
|
||||
}
|
||||
|
||||
message += Message;
|
||||
|
||||
return message;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
275
FluentFTP/Helpers/FtpTrace.cs
Normal file
275
FluentFTP/Helpers/FtpTrace.cs
Normal file
@@ -0,0 +1,275 @@
|
||||
#define TRACE
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
|
||||
namespace FluentFTP {
|
||||
/// <summary>
|
||||
/// Used for transaction logging and debug information.
|
||||
/// </summary>
|
||||
public static class FtpTrace {
|
||||
|
||||
#if !CORE
|
||||
private static volatile TraceSource m_traceSource = new TraceSource("FluentFTP") {
|
||||
Switch = new SourceSwitch("sourceSwitch", "Verbose") { Level = SourceLevels.All }
|
||||
};
|
||||
|
||||
static bool m_flushOnWrite = true;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Should the trace listeners be flushed immediately after writing to them?
|
||||
/// </summary>
|
||||
public static bool FlushOnWrite {
|
||||
get { return m_flushOnWrite; }
|
||||
set { m_flushOnWrite = value; }
|
||||
}
|
||||
|
||||
static bool m_prefix = false;
|
||||
|
||||
/// <summary>
|
||||
/// Should the log entries be written with a prefix of "FluentFTP"?
|
||||
/// Useful if you have a single TraceListener shared across multiple libraries.
|
||||
/// </summary>
|
||||
public static bool LogPrefix {
|
||||
get { return m_prefix; }
|
||||
set { m_prefix = value; }
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Add a TraceListner to the collection. You can use one of the predefined
|
||||
/// TraceListeners in the System.Diagnostics namespace, such as ConsoleTraceListener
|
||||
/// for logging to the console, or you can write your own deriving from
|
||||
/// System.Diagnostics.TraceListener.
|
||||
/// </summary>
|
||||
/// <param name="listener">The TraceListener to add to the collection</param>
|
||||
public static void AddListener(TraceListener listener) {
|
||||
lock (m_traceSource) {
|
||||
m_traceSource.Listeners.Add(listener);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Remove the specified TraceListener from the collection
|
||||
/// </summary>
|
||||
/// <param name="listener">The TraceListener to remove from the collection.</param>
|
||||
public static void RemoveListener(TraceListener listener) {
|
||||
lock (m_traceSource) {
|
||||
m_traceSource.Listeners.Remove(listener);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#if CORE
|
||||
|
||||
static bool m_LogToConsole = false;
|
||||
|
||||
/// <summary>
|
||||
/// Should FTP communication be be logged to console?
|
||||
/// </summary>
|
||||
public static bool LogToConsole {
|
||||
get { return m_LogToConsole; }
|
||||
set { m_LogToConsole = value; }
|
||||
}
|
||||
|
||||
static string m_LogToFile = null;
|
||||
|
||||
/// <summary>
|
||||
/// Set this to a file path to append all FTP communication to it.
|
||||
/// </summary>
|
||||
public static string LogToFile {
|
||||
get { return m_LogToFile; }
|
||||
set { m_LogToFile = value; }
|
||||
}
|
||||
|
||||
#endif
|
||||
static bool m_functions = true;
|
||||
|
||||
/// <summary>
|
||||
/// Should the function calls be logged in Verbose mode?
|
||||
/// </summary>
|
||||
public static bool LogFunctions {
|
||||
get { return m_functions; }
|
||||
set { m_functions = value; }
|
||||
}
|
||||
|
||||
static bool m_IP = true;
|
||||
|
||||
/// <summary>
|
||||
/// Should the FTP server IP addresses be included in the logs?
|
||||
/// </summary>
|
||||
public static bool LogIP {
|
||||
get { return m_IP; }
|
||||
set { m_IP = value; }
|
||||
}
|
||||
|
||||
static bool m_username = true;
|
||||
|
||||
/// <summary>
|
||||
/// Should the FTP usernames be included in the logs?
|
||||
/// </summary>
|
||||
public static bool LogUserName {
|
||||
get { return m_username; }
|
||||
set { m_username = value; }
|
||||
}
|
||||
|
||||
static bool m_password = false;
|
||||
|
||||
/// <summary>
|
||||
/// Should the FTP passwords be included in the logs?
|
||||
/// </summary>
|
||||
public static bool LogPassword {
|
||||
get { return m_password; }
|
||||
set { m_password = value; }
|
||||
}
|
||||
|
||||
static bool m_tracing = true;
|
||||
|
||||
/// <summary>
|
||||
/// Should we trace at all?
|
||||
/// </summary>
|
||||
public static bool EnableTracing
|
||||
{
|
||||
get { return m_tracing; }
|
||||
set { m_tracing = value; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write to the TraceListeners
|
||||
/// </summary>
|
||||
/// <param name="message">The message to write</param>
|
||||
//[Obsolete("Use overloads with FtpTraceLevel")]
|
||||
public static void Write(string message) {
|
||||
Write(FtpTraceLevel.Verbose, message);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write to the TraceListeners
|
||||
/// </summary>
|
||||
/// <param name="message">The message to write</param>
|
||||
//[Obsolete("Use overloads with FtpTraceLevel")]
|
||||
public static void WriteLine(object message) {
|
||||
Write(FtpTraceLevel.Verbose, message.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write to the TraceListeners
|
||||
/// </summary>
|
||||
/// <param name="eventType">The type of tracing event</param>
|
||||
/// <param name="message">The message to write</param>
|
||||
public static void WriteLine(FtpTraceLevel eventType, object message) {
|
||||
Write(eventType, message.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write to the TraceListeners, adding an automatic prefix to the message based on the `eventType`
|
||||
/// </summary>
|
||||
/// <param name="eventType">The type of tracing event</param>
|
||||
/// <param name="message">The message to write</param>
|
||||
public static void WriteStatus(FtpTraceLevel eventType, object message) {
|
||||
Write(eventType, TraceLevelPrefix(eventType) + message.ToString());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write to the TraceListeners, for the purpose of logging a API function call
|
||||
/// </summary>
|
||||
/// <param name="function">The name of the API function</param>
|
||||
/// <param name="args">The args passed to the function</param>
|
||||
public static void WriteFunc(string function, object[] args = null) {
|
||||
if (m_functions) {
|
||||
Write(FtpTraceLevel.Verbose, "");
|
||||
Write(FtpTraceLevel.Verbose, "# " + function + "(" + args.ItemsToString().Join(", ") + ")");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Write to the TraceListeners
|
||||
/// </summary>
|
||||
/// <param name="eventType">The type of tracing event</param>
|
||||
/// <param name="message">A formattable string to write</param>
|
||||
public static void Write(FtpTraceLevel eventType, string message) {
|
||||
if(!EnableTracing)
|
||||
return;
|
||||
#if CORE
|
||||
#if DEBUG
|
||||
Debug.WriteLine(message);
|
||||
#else
|
||||
if (m_LogToConsole) {
|
||||
Console.WriteLine(message);
|
||||
}
|
||||
if (m_LogToFile != null) {
|
||||
File.AppendAllText(m_LogToFile, message + "\n");
|
||||
}
|
||||
#endif
|
||||
#elif !CORE
|
||||
|
||||
if (m_prefix) {
|
||||
|
||||
// if prefix is wanted then use TraceEvent()
|
||||
m_traceSource.TraceEvent(TraceLevelTranslation(eventType), 0, message);
|
||||
|
||||
} else {
|
||||
|
||||
// if prefix is NOT wanted then write manually
|
||||
EmitEvent(m_traceSource, TraceLevelTranslation(eventType), message);
|
||||
|
||||
}
|
||||
if (m_flushOnWrite) {
|
||||
m_traceSource.Flush();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
private static string TraceLevelPrefix(FtpTraceLevel level) {
|
||||
switch (level) {
|
||||
case FtpTraceLevel.Verbose:
|
||||
return "Status: ";
|
||||
case FtpTraceLevel.Info:
|
||||
return "Status: ";
|
||||
case FtpTraceLevel.Warn:
|
||||
return "Warning: ";
|
||||
case FtpTraceLevel.Error:
|
||||
return "Error: ";
|
||||
}
|
||||
return "Status: ";
|
||||
}
|
||||
|
||||
#if !CORE
|
||||
|
||||
private static TraceEventType TraceLevelTranslation(FtpTraceLevel level) {
|
||||
switch (level) {
|
||||
case FtpTraceLevel.Verbose:
|
||||
return TraceEventType.Verbose;
|
||||
case FtpTraceLevel.Info:
|
||||
return TraceEventType.Information;
|
||||
case FtpTraceLevel.Warn:
|
||||
return TraceEventType.Warning;
|
||||
case FtpTraceLevel.Error:
|
||||
return TraceEventType.Error;
|
||||
default:
|
||||
return TraceEventType.Verbose;
|
||||
}
|
||||
}
|
||||
|
||||
static object traceSync = new object();
|
||||
private static void EmitEvent(TraceSource traceSource, TraceEventType eventType, string message) {
|
||||
try {
|
||||
lock (traceSync) {
|
||||
if (traceSource.Switch.ShouldTrace(eventType)) {
|
||||
foreach (TraceListener listener in traceSource.Listeners) {
|
||||
try {
|
||||
listener.WriteLine(message);
|
||||
listener.Flush();
|
||||
} catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
11
FluentFTP/Helpers/IntRef.cs
Normal file
11
FluentFTP/Helpers/IntRef.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace FluentFTP
|
||||
{
|
||||
internal class IntRef
|
||||
{
|
||||
public int Value;
|
||||
}
|
||||
}
|
||||
114
FluentFTP/Proxy/FtpClientHttp11Proxy.cs
Normal file
114
FluentFTP/Proxy/FtpClientHttp11Proxy.cs
Normal file
@@ -0,0 +1,114 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace FluentFTP.Proxy {
|
||||
/// <summary> A FTP client with a HTTP 1.1 proxy implementation. </summary>
|
||||
public class FtpClientHttp11Proxy : FtpClientProxy {
|
||||
/// <summary> A FTP client with a HTTP 1.1 proxy implementation </summary>
|
||||
/// <param name="proxy">Proxy information</param>
|
||||
public FtpClientHttp11Proxy(ProxyInfo proxy)
|
||||
: base(proxy) {
|
||||
ConnectionType = "HTTP 1.1 Proxy";
|
||||
}
|
||||
|
||||
/// <summary> Redefine the first dialog: HTTP Frame for the HTTP 1.1 Proxy </summary>
|
||||
protected override void Handshake() {
|
||||
var proxyConnectionReply = GetReply();
|
||||
if (!proxyConnectionReply.Success)
|
||||
throw new FtpException("Can't connect " + Host + " via proxy " + Proxy.Host + ".\nMessage : " +
|
||||
proxyConnectionReply.ErrorMessage);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of this class. Useful in FTP proxy classes.
|
||||
/// </summary>
|
||||
protected override FtpClient Create() {
|
||||
return new FtpClientHttp11Proxy(Proxy);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connects to the server using an existing <see cref="FtpSocketStream"/>
|
||||
/// </summary>
|
||||
/// <param name="stream">The existing socket stream</param>
|
||||
protected override void Connect(FtpSocketStream stream) {
|
||||
Connect(stream, Host, Port, FtpIpVersion.ANY);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Connects to the server using an existing <see cref="FtpSocketStream"/>
|
||||
/// </summary>
|
||||
/// <param name="stream">The existing socket stream</param>
|
||||
/// <param name="host">Host name</param>
|
||||
/// <param name="port">Port number</param>
|
||||
/// <param name="ipVersions">IP version to use</param>
|
||||
protected override void Connect(FtpSocketStream stream, string host, int port, FtpIpVersion ipVersions) {
|
||||
base.Connect(stream);
|
||||
|
||||
var writer = new StreamWriter(stream);
|
||||
writer.WriteLine("CONNECT {0}:{1} HTTP/1.1", host, port);
|
||||
writer.WriteLine("Host: {0}:{1}", host, port);
|
||||
if (Proxy.Credentials != null) {
|
||||
var credentialsHash = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(Proxy.Credentials.UserName + ":"+ Proxy.Credentials.Password));
|
||||
writer.WriteLine("Proxy-Authorization: Basic "+ credentialsHash);
|
||||
}
|
||||
writer.WriteLine("User-Agent: custom-ftp-client");
|
||||
writer.WriteLine();
|
||||
writer.Flush();
|
||||
|
||||
ProxyHandshake(stream);
|
||||
}
|
||||
|
||||
private void ProxyHandshake(FtpSocketStream stream) {
|
||||
var proxyConnectionReply = GetProxyReply(stream);
|
||||
if (!proxyConnectionReply.Success)
|
||||
throw new FtpException("Can't connect " + Host + " via proxy " + Proxy.Host + ".\nMessage : " + proxyConnectionReply.ErrorMessage);
|
||||
}
|
||||
|
||||
private FtpReply GetProxyReply( FtpSocketStream stream ) {
|
||||
|
||||
FtpReply reply = new FtpReply();
|
||||
string buf;
|
||||
|
||||
#if !CORE14
|
||||
lock ( Lock ) {
|
||||
#endif
|
||||
if( !IsConnected )
|
||||
throw new InvalidOperationException( "No connection to the server has been established." );
|
||||
|
||||
stream.ReadTimeout = ReadTimeout;
|
||||
while( ( buf = stream.ReadLine( Encoding ) ) != null ) {
|
||||
Match m;
|
||||
|
||||
FtpTrace.WriteLine(FtpTraceLevel.Info, buf);
|
||||
|
||||
if( ( m = Regex.Match( buf, @"^HTTP/.*\s(?<code>[0-9]{3}) (?<message>.*)$" ) ).Success ) {
|
||||
reply.Code = m.Groups[ "code" ].Value;
|
||||
reply.Message = m.Groups[ "message" ].Value;
|
||||
break;
|
||||
}
|
||||
|
||||
reply.InfoMessages += ( buf+"\n" );
|
||||
}
|
||||
|
||||
// fixes #84 (missing bytes when downloading/uploading files through proxy)
|
||||
while( ( buf = stream.ReadLine( Encoding ) ) != null ) {
|
||||
|
||||
FtpTrace.WriteLine(FtpTraceLevel.Info, buf);
|
||||
|
||||
if (FtpExtensions.IsNullOrWhiteSpace(buf)) {
|
||||
break;
|
||||
}
|
||||
|
||||
reply.InfoMessages += ( buf+"\n" );
|
||||
}
|
||||
|
||||
#if !CORE14
|
||||
}
|
||||
#endif
|
||||
|
||||
return reply;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
22
FluentFTP/Proxy/FtpClientProxy.cs
Normal file
22
FluentFTP/Proxy/FtpClientProxy.cs
Normal file
@@ -0,0 +1,22 @@
|
||||
namespace FluentFTP.Proxy {
|
||||
/// <summary>
|
||||
/// Abstraction of an FtpClient with a proxy
|
||||
/// </summary>
|
||||
public abstract class FtpClientProxy : FtpClient {
|
||||
private ProxyInfo _proxy;
|
||||
/// <summary> The proxy connection info. </summary>
|
||||
protected ProxyInfo Proxy { get { return _proxy; } }
|
||||
|
||||
/// <summary> A FTP client with a HTTP 1.1 proxy implementation </summary>
|
||||
/// <param name="proxy">Proxy information</param>
|
||||
protected FtpClientProxy(ProxyInfo proxy) {
|
||||
_proxy = proxy;
|
||||
}
|
||||
|
||||
/// <summary> Redefine connect for FtpClient : authentication on the Proxy </summary>
|
||||
/// <param name="stream">The socket stream.</param>
|
||||
protected override void Connect(FtpSocketStream stream) {
|
||||
stream.Connect(Proxy.Host, Proxy.Port, InternetProtocolVersions);
|
||||
}
|
||||
}
|
||||
}
|
||||
28
FluentFTP/Proxy/FtpClientUserAtHostProxy.cs
Normal file
28
FluentFTP/Proxy/FtpClientUserAtHostProxy.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
namespace FluentFTP.Proxy {
|
||||
/// <summary> A FTP client with a user@host proxy identification. </summary>
|
||||
public class FtpClientUserAtHostProxy : FtpClientProxy {
|
||||
/// <summary> A FTP client with a user@host proxy identification. </summary>
|
||||
/// <param name="proxy">Proxy information</param>
|
||||
public FtpClientUserAtHostProxy(ProxyInfo proxy)
|
||||
: base(proxy) {
|
||||
ConnectionType = "User@Host";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of this class. Useful in FTP proxy classes.
|
||||
/// </summary>
|
||||
protected override FtpClient Create() {
|
||||
return new FtpClientUserAtHostProxy(Proxy);
|
||||
}
|
||||
|
||||
/// <summary> Redefine the first dialog: auth with proxy information </summary>
|
||||
protected override void Handshake() {
|
||||
// Proxy authentication eventually needed.
|
||||
if (Proxy.Credentials != null)
|
||||
Authenticate(Proxy.Credentials.UserName, Proxy.Credentials.Password);
|
||||
|
||||
// Connection USER@Host means to change user name to add host.
|
||||
Credentials.UserName = Credentials.UserName + "@" + Host;
|
||||
}
|
||||
}
|
||||
}
|
||||
42
FluentFTP/Proxy/FtpClientUserAtHostProxyBlueCoat.cs
Normal file
42
FluentFTP/Proxy/FtpClientUserAtHostProxyBlueCoat.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
namespace FluentFTP.Proxy {
|
||||
/// <summary>
|
||||
/// A FTP client with a user@host proxy identification, that works with Blue Coat FTP Service servers.
|
||||
///
|
||||
/// The 'blue coat variant' forces the client to wait for a 220 FTP response code in
|
||||
/// the handshake phase.
|
||||
/// </summary>
|
||||
public class FtpClientUserAtHostProxyBlueCoat : FtpClientProxy
|
||||
{
|
||||
/// <summary> A FTP client with a user@host proxy identification. </summary>
|
||||
/// <param name="proxy">Proxy information</param>
|
||||
public FtpClientUserAtHostProxyBlueCoat(ProxyInfo proxy)
|
||||
: base(proxy)
|
||||
{
|
||||
ConnectionType = "User@Host";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of this class. Useful in FTP proxy classes.
|
||||
/// </summary>
|
||||
protected override FtpClient Create()
|
||||
{
|
||||
return new FtpClientUserAtHostProxyBlueCoat(Proxy);
|
||||
}
|
||||
|
||||
/// <summary> Redefine the first dialog: auth with proxy information </summary>
|
||||
protected override void Handshake()
|
||||
{
|
||||
// Proxy authentication eventually needed.
|
||||
if (Proxy.Credentials != null)
|
||||
Authenticate(Proxy.Credentials.UserName, Proxy.Credentials.Password);
|
||||
|
||||
// Connection USER@Host means to change user name to add host.
|
||||
Credentials.UserName = Credentials.UserName + "@" + Host;
|
||||
|
||||
FtpReply reply = GetReply();
|
||||
if (reply.Code == "220")
|
||||
FtpTrace.WriteLine(FtpTraceLevel.Info, "Status: Server is ready for the new client");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
15
FluentFTP/Proxy/ProxyInfo.cs
Normal file
15
FluentFTP/Proxy/ProxyInfo.cs
Normal file
@@ -0,0 +1,15 @@
|
||||
using System.Net;
|
||||
|
||||
namespace FluentFTP.Proxy {
|
||||
/// <summary> POCO holding proxy information</summary>
|
||||
public class ProxyInfo {
|
||||
/// <summary> Proxy host name </summary>
|
||||
public string Host { get; set; }
|
||||
|
||||
/// <summary> Proxy port </summary>
|
||||
public int Port { get; set; }
|
||||
|
||||
/// <summary> Proxy login credentials </summary>
|
||||
public NetworkCredential Credentials { get; set; }
|
||||
}
|
||||
}
|
||||
189
FluentFTP/Stream/FtpDataStream.cs
Normal file
189
FluentFTP/Stream/FtpDataStream.cs
Normal file
@@ -0,0 +1,189 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Diagnostics;
|
||||
using System.Threading;
|
||||
|
||||
#if ASYNC
|
||||
using System.Threading.Tasks;
|
||||
#endif
|
||||
|
||||
namespace FluentFTP {
|
||||
|
||||
/// <summary>
|
||||
/// Base class for data stream connections
|
||||
/// </summary>
|
||||
public class FtpDataStream : FtpSocketStream {
|
||||
FtpReply m_commandStatus;
|
||||
/// <summary>
|
||||
/// Gets the status of the command that was used to open
|
||||
/// this data channel
|
||||
/// </summary>
|
||||
public FtpReply CommandStatus {
|
||||
get {
|
||||
return m_commandStatus;
|
||||
}
|
||||
set {
|
||||
m_commandStatus = value;
|
||||
}
|
||||
}
|
||||
|
||||
FtpClient m_control = null;
|
||||
/// <summary>
|
||||
/// Gets or sets the control connection for this data stream. Setting
|
||||
/// the control connection causes the object to be cloned and a new
|
||||
/// connection is made to the server to carry out the task. This ensures
|
||||
/// that multiple streams can be opened simultaneously.
|
||||
/// </summary>
|
||||
public FtpClient ControlConnection {
|
||||
get {
|
||||
return m_control;
|
||||
}
|
||||
set {
|
||||
m_control = value;
|
||||
}
|
||||
}
|
||||
|
||||
long m_length = 0;
|
||||
/// <summary>
|
||||
/// Gets or sets the length of the stream. Only valid for file transfers
|
||||
/// and only valid on servers that support the Size command.
|
||||
/// </summary>
|
||||
public override long Length {
|
||||
get {
|
||||
return m_length;
|
||||
}
|
||||
}
|
||||
|
||||
long m_position = 0;
|
||||
/// <summary>
|
||||
/// Gets or sets the position of the stream
|
||||
/// </summary>
|
||||
public override long Position {
|
||||
get {
|
||||
return m_position;
|
||||
}
|
||||
set {
|
||||
throw new InvalidOperationException("You cannot modify the position of a FtpDataStream. This property is updated as data is read or written to the stream.");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Reads data off the stream
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer to read into</param>
|
||||
/// <param name="offset">Where to start in the buffer</param>
|
||||
/// <param name="count">Number of bytes to read</param>
|
||||
/// <returns>The number of bytes read</returns>
|
||||
public override int Read(byte[] buffer, int offset, int count) {
|
||||
int read = base.Read(buffer, offset, count);
|
||||
m_position += read;
|
||||
return read;
|
||||
}
|
||||
|
||||
#if ASYNC
|
||||
/// <summary>
|
||||
/// Reads data off the stream asynchronously
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer to read into</param>
|
||||
/// <param name="offset">Where to start in the buffer</param>
|
||||
/// <param name="count">Number of bytes to read</param>
|
||||
/// <param name="token">The cancellation token for this task</param>
|
||||
/// <returns>The number of bytes read</returns>
|
||||
public override async Task<int> ReadAsync(byte[] buffer, int offset, int count, CancellationToken token) {
|
||||
int read = await base.ReadAsync(buffer, offset, count, token);
|
||||
m_position += read;
|
||||
return read;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Writes data to the stream
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer to write to the stream</param>
|
||||
/// <param name="offset">Where to start in the buffer</param>
|
||||
/// <param name="count">The number of bytes to write to the buffer</param>
|
||||
public override void Write(byte[] buffer, int offset, int count) {
|
||||
base.Write(buffer, offset, count);
|
||||
m_position += count;
|
||||
}
|
||||
|
||||
#if ASYNC
|
||||
/// <summary>
|
||||
/// Writes data to the stream asynchronously
|
||||
/// </summary>
|
||||
/// <param name="buffer">The buffer to write to the stream</param>
|
||||
/// <param name="offset">Where to start in the buffer</param>
|
||||
/// <param name="count">The number of bytes to write to the buffer</param>
|
||||
/// <param name="token">The <see cref="CancellationToken"/> for this task</param>
|
||||
public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken token) {
|
||||
await base.WriteAsync(buffer, offset, count, token);
|
||||
m_position += count;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Sets the length of this stream
|
||||
/// </summary>
|
||||
/// <param name="value">Value to apply to the Length property</param>
|
||||
public override void SetLength(long value) {
|
||||
m_length = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Sets the position of the stream. Intended to be used
|
||||
/// internally by FtpControlConnection.
|
||||
/// </summary>
|
||||
/// <param name="pos">The position</param>
|
||||
public void SetPosition(long pos) {
|
||||
m_position = pos;
|
||||
}
|
||||
|
||||
/// <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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new data stream object
|
||||
/// </summary>
|
||||
/// <param name="conn">The control connection to be used for carrying out this operation</param>
|
||||
public FtpDataStream(FtpClient conn) : base(conn.SslProtocols) {
|
||||
if (conn == null)
|
||||
throw new ArgumentException("The control connection cannot be null.");
|
||||
|
||||
ControlConnection = conn;
|
||||
// always accept certificate no matter what because if code execution ever
|
||||
// gets here it means the certificate on the control connection object being
|
||||
// cloned was already accepted.
|
||||
ValidateCertificate += new FtpSocketStreamSslValidation(delegate (FtpSocketStream obj, FtpSslValidationEventArgs e) {
|
||||
e.Accept = true;
|
||||
});
|
||||
|
||||
m_position = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finalizer
|
||||
/// </summary>
|
||||
~FtpDataStream() {
|
||||
try {
|
||||
Dispose(false);
|
||||
} catch (Exception ex) {
|
||||
FtpTrace.WriteLine(FtpTraceLevel.Warn, "[Finalizer] Caught and discarded an exception while disposing the FtpDataStream: " + ex.ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1158
FluentFTP/Stream/FtpSocketStream.cs
Normal file
1158
FluentFTP/Stream/FtpSocketStream.cs
Normal file
File diff suppressed because it is too large
Load Diff
284
FluentFTP/Stream/FtpSslStream.cs
Normal file
284
FluentFTP/Stream/FtpSslStream.cs
Normal file
@@ -0,0 +1,284 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
#if !CORE
|
||||
using System.Linq;
|
||||
using System.Net.Security;
|
||||
using System.Runtime.ConstrainedExecution;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
#endif
|
||||
|
||||
namespace FluentFTP {
|
||||
|
||||
#if !CORE
|
||||
/// <summary>
|
||||
/// .NET SslStream doesn't close TLS connection properly.
|
||||
/// It does not send the close_notify alert before closing the connection.
|
||||
/// FtpSslStream uses unsafe code to do that.
|
||||
/// This is required when we want to downgrade the connection to plaintext using CCC command.
|
||||
/// Thanks to Neco @ https://stackoverflow.com/questions/237807/net-sslstream-doesnt-close-tls-connection-properly/22626756#22626756
|
||||
/// </summary>
|
||||
internal class FtpSslStream : SslStream {
|
||||
|
||||
private bool sentCloseNotify = false;
|
||||
|
||||
public FtpSslStream(Stream innerStream)
|
||||
: base(innerStream) {
|
||||
}
|
||||
public FtpSslStream(Stream innerStream, bool leaveInnerStreamOpen)
|
||||
: base(innerStream, leaveInnerStreamOpen) {
|
||||
}
|
||||
public FtpSslStream(Stream innerStream, bool leaveInnerStreamOpen, RemoteCertificateValidationCallback userCertificateValidationCallback)
|
||||
: base(innerStream, leaveInnerStreamOpen, userCertificateValidationCallback) {
|
||||
}
|
||||
public FtpSslStream(Stream innerStream, bool leaveInnerStreamOpen, RemoteCertificateValidationCallback userCertificateValidationCallback, LocalCertificateSelectionCallback userCertificateSelectionCallback)
|
||||
: base(innerStream, leaveInnerStreamOpen, userCertificateValidationCallback, userCertificateSelectionCallback) {
|
||||
}
|
||||
#if !NET20 && !NET35
|
||||
public FtpSslStream(Stream innerStream, bool leaveInnerStreamOpen, RemoteCertificateValidationCallback userCertificateValidationCallback, LocalCertificateSelectionCallback userCertificateSelectionCallback, EncryptionPolicy encryptionPolicy)
|
||||
: base(innerStream, leaveInnerStreamOpen, userCertificateValidationCallback, userCertificateSelectionCallback, encryptionPolicy) {
|
||||
}
|
||||
#endif
|
||||
public override void Close() {
|
||||
try {
|
||||
if (!sentCloseNotify) {
|
||||
SslDirectCall.CloseNotify(this);
|
||||
sentCloseNotify = true;
|
||||
}
|
||||
} finally {
|
||||
base.Close();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal unsafe static class SslDirectCall {
|
||||
/// <summary>
|
||||
/// Send an SSL close_notify alert.
|
||||
/// </summary>
|
||||
/// <param name="sslStream"></param>
|
||||
public static void CloseNotify(SslStream sslStream) {
|
||||
if (sslStream.IsAuthenticated && sslStream.CanWrite) {
|
||||
bool isServer = sslStream.IsServer;
|
||||
|
||||
byte[] result;
|
||||
int resultSz;
|
||||
var asmbSystem = typeof(System.Net.Authorization).Assembly;
|
||||
|
||||
int SCHANNEL_SHUTDOWN = 1;
|
||||
var workArray = BitConverter.GetBytes(SCHANNEL_SHUTDOWN);
|
||||
|
||||
var sslstate = FtpReflection.GetField(sslStream, "_SslState");
|
||||
var context = FtpReflection.GetProperty(sslstate, "Context");
|
||||
|
||||
var securityContext = FtpReflection.GetField(context, "m_SecurityContext");
|
||||
var securityContextHandleOriginal = FtpReflection.GetField(securityContext, "_handle");
|
||||
SslNativeApi.SSPIHandle securityContextHandle = default(SslNativeApi.SSPIHandle);
|
||||
securityContextHandle.HandleHi = (IntPtr)FtpReflection.GetField(securityContextHandleOriginal, "HandleHi");
|
||||
securityContextHandle.HandleLo = (IntPtr)FtpReflection.GetField(securityContextHandleOriginal, "HandleLo");
|
||||
|
||||
var credentialsHandle = FtpReflection.GetField(context, "m_CredentialsHandle");
|
||||
var credentialsHandleHandleOriginal = FtpReflection.GetField(credentialsHandle, "_handle");
|
||||
SslNativeApi.SSPIHandle credentialsHandleHandle = default(SslNativeApi.SSPIHandle);
|
||||
credentialsHandleHandle.HandleHi = (IntPtr)FtpReflection.GetField(credentialsHandleHandleOriginal, "HandleHi");
|
||||
credentialsHandleHandle.HandleLo = (IntPtr)FtpReflection.GetField(credentialsHandleHandleOriginal, "HandleLo");
|
||||
|
||||
int bufferSize = 1;
|
||||
SslNativeApi.SecurityBufferDescriptor securityBufferDescriptor = new SslNativeApi.SecurityBufferDescriptor(bufferSize);
|
||||
SslNativeApi.SecurityBufferStruct[] unmanagedBuffer = new SslNativeApi.SecurityBufferStruct[bufferSize];
|
||||
|
||||
fixed (SslNativeApi.SecurityBufferStruct* ptr = unmanagedBuffer)
|
||||
fixed (void* workArrayPtr = workArray) {
|
||||
securityBufferDescriptor.UnmanagedPointer = (void*)ptr;
|
||||
|
||||
unmanagedBuffer[0].token = (IntPtr)workArrayPtr;
|
||||
unmanagedBuffer[0].count = workArray.Length;
|
||||
unmanagedBuffer[0].type = SslNativeApi.BufferType.Token;
|
||||
|
||||
SslNativeApi.SecurityStatus status;
|
||||
status = (SslNativeApi.SecurityStatus)SslNativeApi.ApplyControlToken(ref securityContextHandle, securityBufferDescriptor);
|
||||
if (status == SslNativeApi.SecurityStatus.OK) {
|
||||
unmanagedBuffer[0].token = IntPtr.Zero;
|
||||
unmanagedBuffer[0].count = 0;
|
||||
unmanagedBuffer[0].type = SslNativeApi.BufferType.Token;
|
||||
|
||||
SslNativeApi.SSPIHandle contextHandleOut = default(SslNativeApi.SSPIHandle);
|
||||
SslNativeApi.ContextFlags outflags = SslNativeApi.ContextFlags.Zero;
|
||||
long ts = 0;
|
||||
|
||||
var inflags = SslNativeApi.ContextFlags.SequenceDetect |
|
||||
SslNativeApi.ContextFlags.ReplayDetect |
|
||||
SslNativeApi.ContextFlags.Confidentiality |
|
||||
SslNativeApi.ContextFlags.AcceptExtendedError |
|
||||
SslNativeApi.ContextFlags.AllocateMemory |
|
||||
SslNativeApi.ContextFlags.InitStream;
|
||||
|
||||
if (isServer) {
|
||||
status = (SslNativeApi.SecurityStatus)SslNativeApi.AcceptSecurityContext(ref credentialsHandleHandle, ref securityContextHandle, null,
|
||||
inflags, SslNativeApi.Endianness.Native, ref contextHandleOut, securityBufferDescriptor, ref outflags, out ts);
|
||||
} else {
|
||||
status = (SslNativeApi.SecurityStatus)SslNativeApi.InitializeSecurityContextW(ref credentialsHandleHandle, ref securityContextHandle, null,
|
||||
inflags, 0, SslNativeApi.Endianness.Native, null, 0, ref contextHandleOut, securityBufferDescriptor, ref outflags, out ts);
|
||||
}
|
||||
if (status == SslNativeApi.SecurityStatus.OK) {
|
||||
byte[] resultArr = new byte[unmanagedBuffer[0].count];
|
||||
Marshal.Copy(unmanagedBuffer[0].token, resultArr, 0, resultArr.Length);
|
||||
Marshal.FreeCoTaskMem(unmanagedBuffer[0].token);
|
||||
result = resultArr;
|
||||
resultSz = resultArr.Length;
|
||||
} else {
|
||||
throw new InvalidOperationException(string.Format("AcceptSecurityContext/InitializeSecurityContextW returned [{0}] during CloseNotify.", status));
|
||||
}
|
||||
} else {
|
||||
throw new InvalidOperationException(string.Format("ApplyControlToken returned [{0}] during CloseNotify.", status));
|
||||
}
|
||||
}
|
||||
|
||||
var innerStream = (Stream)FtpReflection.GetProperty(sslstate, "InnerStream");
|
||||
innerStream.Write(result, 0, resultSz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal unsafe static class SslNativeApi {
|
||||
internal enum BufferType {
|
||||
Empty,
|
||||
Data,
|
||||
Token,
|
||||
Parameters,
|
||||
Missing,
|
||||
Extra,
|
||||
Trailer,
|
||||
Header,
|
||||
Padding = 9,
|
||||
Stream,
|
||||
ChannelBindings = 14,
|
||||
TargetHost = 16,
|
||||
ReadOnlyFlag = -2147483648,
|
||||
ReadOnlyWithChecksum = 268435456
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential, Pack = 1)]
|
||||
internal struct SSPIHandle {
|
||||
public IntPtr HandleHi;
|
||||
public IntPtr HandleLo;
|
||||
public bool IsZero {
|
||||
get {
|
||||
return this.HandleHi == IntPtr.Zero && this.HandleLo == IntPtr.Zero;
|
||||
}
|
||||
}
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
|
||||
internal void SetToInvalid() {
|
||||
this.HandleHi = IntPtr.Zero;
|
||||
this.HandleLo = IntPtr.Zero;
|
||||
}
|
||||
public override string ToString() {
|
||||
return this.HandleHi.ToString("x") + ":" + this.HandleLo.ToString("x");
|
||||
}
|
||||
}
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal class SecurityBufferDescriptor {
|
||||
public readonly int Version;
|
||||
public readonly int Count;
|
||||
public unsafe void* UnmanagedPointer;
|
||||
public SecurityBufferDescriptor(int count) {
|
||||
this.Version = 0;
|
||||
this.Count = count;
|
||||
this.UnmanagedPointer = null;
|
||||
}
|
||||
}
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
internal struct SecurityBufferStruct {
|
||||
public int count;
|
||||
public BufferType type;
|
||||
public IntPtr token;
|
||||
public static readonly int Size = sizeof(SecurityBufferStruct);
|
||||
}
|
||||
|
||||
internal enum SecurityStatus {
|
||||
OK,
|
||||
ContinueNeeded = 590610,
|
||||
CompleteNeeded,
|
||||
CompAndContinue,
|
||||
ContextExpired = 590615,
|
||||
CredentialsNeeded = 590624,
|
||||
Renegotiate,
|
||||
OutOfMemory = -2146893056,
|
||||
InvalidHandle,
|
||||
Unsupported,
|
||||
TargetUnknown,
|
||||
InternalError,
|
||||
PackageNotFound,
|
||||
NotOwner,
|
||||
CannotInstall,
|
||||
InvalidToken,
|
||||
CannotPack,
|
||||
QopNotSupported,
|
||||
NoImpersonation,
|
||||
LogonDenied,
|
||||
UnknownCredentials,
|
||||
NoCredentials,
|
||||
MessageAltered,
|
||||
OutOfSequence,
|
||||
NoAuthenticatingAuthority,
|
||||
IncompleteMessage = -2146893032,
|
||||
IncompleteCredentials = -2146893024,
|
||||
BufferNotEnough,
|
||||
WrongPrincipal,
|
||||
TimeSkew = -2146893020,
|
||||
UntrustedRoot,
|
||||
IllegalMessage,
|
||||
CertUnknown,
|
||||
CertExpired,
|
||||
AlgorithmMismatch = -2146893007,
|
||||
SecurityQosFailed,
|
||||
SmartcardLogonRequired = -2146892994,
|
||||
UnsupportedPreauth = -2146892989,
|
||||
BadBinding = -2146892986
|
||||
}
|
||||
[Flags]
|
||||
internal enum ContextFlags {
|
||||
Zero = 0,
|
||||
Delegate = 1,
|
||||
MutualAuth = 2,
|
||||
ReplayDetect = 4,
|
||||
SequenceDetect = 8,
|
||||
Confidentiality = 16,
|
||||
UseSessionKey = 32,
|
||||
AllocateMemory = 256,
|
||||
Connection = 2048,
|
||||
InitExtendedError = 16384,
|
||||
AcceptExtendedError = 32768,
|
||||
InitStream = 32768,
|
||||
AcceptStream = 65536,
|
||||
InitIntegrity = 65536,
|
||||
AcceptIntegrity = 131072,
|
||||
InitManualCredValidation = 524288,
|
||||
InitUseSuppliedCreds = 128,
|
||||
InitIdentify = 131072,
|
||||
AcceptIdentify = 524288,
|
||||
ProxyBindings = 67108864,
|
||||
AllowMissingBindings = 268435456,
|
||||
UnverifiedTargetName = 536870912
|
||||
}
|
||||
internal enum Endianness {
|
||||
Network,
|
||||
Native = 16
|
||||
}
|
||||
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
[DllImport("secur32.dll", ExactSpelling = true, SetLastError = true)]
|
||||
internal static extern int ApplyControlToken(ref SSPIHandle contextHandle, [In] [Out] SecurityBufferDescriptor outputBuffer);
|
||||
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
[DllImport("secur32.dll", ExactSpelling = true, SetLastError = true)]
|
||||
internal unsafe static extern int AcceptSecurityContext(ref SSPIHandle credentialHandle, ref SSPIHandle contextHandle, [In] SecurityBufferDescriptor inputBuffer, [In] ContextFlags inFlags, [In] Endianness endianness, ref SSPIHandle outContextPtr, [In] [Out] SecurityBufferDescriptor outputBuffer, [In] [Out] ref ContextFlags attributes, out long timeStamp);
|
||||
|
||||
[ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)]
|
||||
[DllImport("secur32.dll", ExactSpelling = true, SetLastError = true)]
|
||||
internal unsafe static extern int InitializeSecurityContextW(ref SSPIHandle credentialHandle, ref SSPIHandle contextHandle, [In] byte* targetName, [In] ContextFlags inFlags, [In] int reservedI, [In] Endianness endianness, [In] SecurityBufferDescriptor inputBuffer, [In] int reservedII, ref SSPIHandle outContextPtr, [In] [Out] SecurityBufferDescriptor outputBuffer, [In] [Out] ref ContextFlags attributes, out long timeStamp);
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
363
FluentFTP/Utils/FtpExtensions.cs
Normal file
363
FluentFTP/Utils/FtpExtensions.cs
Normal file
@@ -0,0 +1,363 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Globalization;
|
||||
#if (CORE || NETFX)
|
||||
using System.Diagnostics;
|
||||
#endif
|
||||
#if NET45
|
||||
using System.Threading.Tasks;
|
||||
using System.Collections;
|
||||
#endif
|
||||
|
||||
namespace FluentFTP {
|
||||
/// <summary>
|
||||
/// Extension methods related to FTP tasks
|
||||
/// </summary>
|
||||
public static class FtpExtensions {
|
||||
|
||||
/// <summary>
|
||||
/// Converts the specified path into a valid FTP file system path
|
||||
/// </summary>
|
||||
/// <param name="path">The file system path</param>
|
||||
/// <returns>A path formatted for FTP</returns>
|
||||
public static string GetFtpPath(this string path) {
|
||||
if (String.IsNullOrEmpty(path))
|
||||
return "./";
|
||||
|
||||
path = path.Replace('\\', '/');
|
||||
path = Regex.Replace(path, "[/]+", "/");
|
||||
path = path.TrimEnd('/');
|
||||
|
||||
if (path.Length == 0)
|
||||
path = "/";
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a valid FTP path by appending the specified segments to this string
|
||||
/// </summary>
|
||||
/// <param name="path">This string</param>
|
||||
/// <param name="segments">The path segments to append</param>
|
||||
/// <returns>A valid FTP path</returns>
|
||||
public static string GetFtpPath(this string path, params string[] segments) {
|
||||
if (String.IsNullOrEmpty(path))
|
||||
path = "./";
|
||||
|
||||
foreach (string part in segments) {
|
||||
if (part != null) {
|
||||
if (path.Length > 0 && !path.EndsWith("/"))
|
||||
path += "/";
|
||||
path += Regex.Replace(part.Replace('\\', '/'), "[/]+", "/").TrimEnd('/');
|
||||
}
|
||||
}
|
||||
|
||||
path = Regex.Replace(path.Replace('\\', '/'), "[/]+", "/").TrimEnd('/');
|
||||
if (path.Length == 0)
|
||||
path = "/";
|
||||
|
||||
/*if (!path.StartsWith("/") || !path.StartsWith("./"))
|
||||
path = "./" + path;*/
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent directory path (formatted for a FTP server)
|
||||
/// </summary>
|
||||
/// <param name="path">The path</param>
|
||||
/// <returns>The parent directory path</returns>
|
||||
public static string GetFtpDirectoryName(this string path) {
|
||||
string tpath = (path == null ? "" : path.GetFtpPath());
|
||||
|
||||
if (tpath.Length == 0 || tpath == "/")
|
||||
return "/";
|
||||
|
||||
int lastslash = tpath.LastIndexOf('/');
|
||||
if (lastslash < 0)
|
||||
return ".";
|
||||
if (lastslash == 0)
|
||||
return "/";
|
||||
|
||||
return tpath.Substring(0, lastslash);
|
||||
}
|
||||
|
||||
/*public static string GetFtpDirectoryName(this string path) {
|
||||
if (path == null || path.Length == 0 || path.GetFtpPath() == "/")
|
||||
return "/";
|
||||
|
||||
return System.IO.Path.GetDirectoryName(path).GetFtpPath();
|
||||
}*/
|
||||
|
||||
/// <summary>
|
||||
/// Gets the file name and extension from the path
|
||||
/// </summary>
|
||||
/// <param name="path">The full path to the file</param>
|
||||
/// <returns>The file name</returns>
|
||||
public static string GetFtpFileName(this string path) {
|
||||
string tpath = (path == null ? null : path);
|
||||
int lastslash = -1;
|
||||
|
||||
if (tpath == null)
|
||||
return null;
|
||||
|
||||
lastslash = tpath.LastIndexOf('/');
|
||||
if (lastslash < 0)
|
||||
return tpath;
|
||||
|
||||
lastslash += 1;
|
||||
if (lastslash >= tpath.Length)
|
||||
return tpath;
|
||||
|
||||
return tpath.Substring(lastslash, tpath.Length - lastslash);
|
||||
}
|
||||
|
||||
/*public static string GetFtpFileName(this string path) {
|
||||
return System.IO.Path.GetFileName(path).GetFtpPath();
|
||||
}*/
|
||||
|
||||
private static string[] FtpDateFormats = { "yyyyMMddHHmmss", "yyyyMMddHHmmss'.'f", "yyyyMMddHHmmss'.'ff", "yyyyMMddHHmmss'.'fff", "MMM dd yyyy","MMM d yyyy","MMM dd HH:mm","MMM d HH:mm" };
|
||||
|
||||
/// <summary>
|
||||
/// Tries to convert the string FTP date representation into a <see cref="DateTime"/> object
|
||||
/// </summary>
|
||||
/// <param name="date">The date</param>
|
||||
/// <param name="style">UTC/Local Time</param>
|
||||
/// <returns>A <see cref="DateTime"/> object representing the date, or <see cref="DateTime.MinValue"/> if there was a problem</returns>
|
||||
public static DateTime GetFtpDate(this string date, DateTimeStyles style) {
|
||||
DateTime parsed;
|
||||
|
||||
if (DateTime.TryParseExact(date, FtpDateFormats, CultureInfo.InvariantCulture, style, out parsed)) {
|
||||
return parsed;
|
||||
}
|
||||
|
||||
return DateTime.MinValue;
|
||||
}
|
||||
|
||||
private static string[] sizePostfix = { "bytes", "KB", "MB", "GB", "TB" };
|
||||
|
||||
/// <summary>
|
||||
/// Converts a file size in bytes to a string representation (eg. 12345 becomes 12.3 KB)
|
||||
/// </summary>
|
||||
public static string FileSizeToString(this int bytes) {
|
||||
return ((long)bytes).FileSizeToString();
|
||||
}
|
||||
/// <summary>
|
||||
/// Converts a file size in bytes to a string representation (eg. 12345 becomes 12.3 KB)
|
||||
/// </summary>
|
||||
public static string FileSizeToString(this uint bytes) {
|
||||
return ((long)bytes).FileSizeToString();
|
||||
}
|
||||
/// <summary>
|
||||
/// Converts a file size in bytes to a string representation (eg. 12345 becomes 12.3 KB)
|
||||
/// </summary>
|
||||
public static string FileSizeToString(this ulong bytes) {
|
||||
return ((long)bytes).FileSizeToString();
|
||||
}
|
||||
/// <summary>
|
||||
/// Converts a file size in bytes to a string representation (eg. 12345 becomes 12.3 KB)
|
||||
/// </summary>
|
||||
public static string FileSizeToString(this long bytes) {
|
||||
int order = 0;
|
||||
double len = bytes;
|
||||
while (len >= 1024 && order < sizePostfix.Length - 1) {
|
||||
order++;
|
||||
len = len / 1024;
|
||||
}
|
||||
return String.Format("{0:0.#} {1}", len, sizePostfix[order]);
|
||||
}
|
||||
|
||||
#if NET45
|
||||
/// <summary>
|
||||
/// This creates a <see cref="System.Threading.Tasks.Task{TResult}"/> that represents a pair of begin and end methods
|
||||
/// that conform to the Asynchronous Programming Model pattern. This extends the maximum amount of arguments from
|
||||
/// <see cref="o:System.Threading.TaskFactory.FromAsync"/> to 4 from a 3.
|
||||
/// </summary>
|
||||
/// <typeparam name="TArg1">The type of the first argument passed to the <paramref name="beginMethod"/> delegate</typeparam>
|
||||
/// <typeparam name="TArg2">The type of the second argument passed to the <paramref name="beginMethod"/> delegate</typeparam>
|
||||
/// <typeparam name="TArg3">The type of the third argument passed to the <paramref name="beginMethod"/> delegate</typeparam>
|
||||
/// <typeparam name="TArg4">The type of the forth argument passed to the <paramref name="beginMethod"/> delegate</typeparam>
|
||||
/// <typeparam name="TResult">The type of the result.</typeparam>
|
||||
/// <param name="factory">The <see cref="TaskFactory"/> used</param>
|
||||
/// <param name="beginMethod">The delegate that begins the asynchronous operation</param>
|
||||
/// <param name="endMethod">The delegate that ends the asynchronous operation</param>
|
||||
/// <param name="arg1">The first argument passed to the <paramref name="beginMethod"/> delegate</param>
|
||||
/// <param name="arg2">The second argument passed to the <paramref name="beginMethod"/> delegate</param>
|
||||
/// <param name="arg3">The third argument passed to the <paramref name="beginMethod"/> delegate</param>
|
||||
/// <param name="arg4">The forth argument passed to the <paramref name="beginMethod"/> delegate</param>
|
||||
/// <param name="state">An object containing data to be used by the <paramref name="beginMethod"/> delegate</param>
|
||||
/// <returns>The created <see cref="System.Threading.Tasks.Task{TResult}"/> that represents the asynchronous operation</returns>
|
||||
/// <exception cref="System.ArgumentNullException">
|
||||
/// beginMethod is null
|
||||
/// or
|
||||
/// endMethod is null
|
||||
/// </exception>
|
||||
public static Task<TResult> FromAsync<TArg1, TArg2, TArg3, TArg4, TResult>(this TaskFactory factory,
|
||||
Func<TArg1, TArg2, TArg3, TArg4, AsyncCallback, object, IAsyncResult> beginMethod,
|
||||
Func<IAsyncResult, TResult> endMethod,
|
||||
TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4, object state) {
|
||||
if (beginMethod == null)
|
||||
throw new ArgumentNullException("beginMethod");
|
||||
|
||||
if (endMethod == null)
|
||||
throw new ArgumentNullException("endMethod");
|
||||
|
||||
TaskCompletionSource<TResult> tcs = new TaskCompletionSource<TResult>(state, factory.CreationOptions);
|
||||
try {
|
||||
AsyncCallback callback = delegate(IAsyncResult asyncResult) {
|
||||
tcs.TrySetResult(endMethod(asyncResult));
|
||||
};
|
||||
|
||||
beginMethod(arg1, arg2, arg3, arg4, callback, state);
|
||||
}
|
||||
catch {
|
||||
tcs.TrySetResult(default(TResult));
|
||||
throw;
|
||||
}
|
||||
|
||||
return tcs.Task;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Validates that the FtpError flags set are not in an invalid combination.
|
||||
/// </summary>
|
||||
/// <param name="options">The error handling options set</param>
|
||||
/// <returns>True if a valid combination, otherwise false</returns>
|
||||
public static bool IsValidCombination(this FtpError options) {
|
||||
return options != (FtpError.Stop | FtpError.Throw) &&
|
||||
options != (FtpError.Throw | FtpError.Stop | FtpError.DeleteProcessed);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if every character in the string is whitespace, or the string is null.
|
||||
/// </summary>
|
||||
public static bool IsNullOrWhiteSpace(string value) {
|
||||
if (value == null) return true;
|
||||
|
||||
for (int i = 0; i < value.Length; i++) {
|
||||
if (!Char.IsWhiteSpace(value[i])) return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the string is null or 0 length.
|
||||
/// </summary>
|
||||
public static bool IsBlank(this string value) {
|
||||
return value == null || value.Length == 0;
|
||||
}
|
||||
|
||||
#if NET45
|
||||
/// <summary>
|
||||
/// Checks if the array is null or 0 length.
|
||||
/// </summary>
|
||||
public static bool IsBlank(this IList value) {
|
||||
return value == null || value.Count == 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if the array is null or 0 length.
|
||||
/// </summary>
|
||||
public static bool IsBlank(this IEnumerable value) {
|
||||
if (value == null){
|
||||
return true;
|
||||
}
|
||||
if (value is IList){
|
||||
return ((IList)value).Count == 0;
|
||||
}
|
||||
if (value is byte[]){
|
||||
return ((byte[])value).Length == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// Join the given strings by a delimiter.
|
||||
/// </summary>
|
||||
public static string Join(this string[] values, string delimiter) {
|
||||
return string.Join(delimiter, values);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Join the given strings by a delimiter.
|
||||
/// </summary>
|
||||
public static string Join(this List<string> values, string delimiter) {
|
||||
#if NET20 || NET35
|
||||
return string.Join(delimiter, values.ToArray());
|
||||
#else
|
||||
return string.Join(delimiter, values);
|
||||
#endif
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a prefix to the given strings, returns a new array.
|
||||
/// </summary>
|
||||
public static string[] AddPrefix(this string[] values, string prefix, bool trim = false) {
|
||||
List<string> results = new List<string>();
|
||||
foreach (string v in values) {
|
||||
string txt = prefix + (trim ? v.Trim() : v);
|
||||
results.Add(txt);
|
||||
}
|
||||
return results.ToArray();
|
||||
}
|
||||
/// <summary>
|
||||
/// Adds a prefix to the given strings, returns a new array.
|
||||
/// </summary>
|
||||
public static List<string> AddPrefix(this List<string> values, string prefix, bool trim = false) {
|
||||
List<string> results = new List<string>();
|
||||
foreach (string v in values) {
|
||||
string txt = prefix + (trim ? v.Trim() : v);
|
||||
results.Add(txt);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a prefix to the given strings, returns a new array.
|
||||
/// </summary>
|
||||
public static List<string> ItemsToString(this object[] args) {
|
||||
List<string> results = new List<string>();
|
||||
if (args == null) {
|
||||
return results;
|
||||
}
|
||||
foreach (object v in args) {
|
||||
string txt;
|
||||
if (v == null){
|
||||
txt = "null";
|
||||
} else if (v is string) {
|
||||
txt = ("\"" + v as string + "\"");
|
||||
} else {
|
||||
txt = v.ToString();
|
||||
}
|
||||
results.Add(txt);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
#if NET20 || NET35
|
||||
public static bool HasFlag(this FtpHashAlgorithm flags, FtpHashAlgorithm flag) {
|
||||
return (flags & flag) == flag;
|
||||
}
|
||||
public static bool HasFlag(this FtpCapability flags, FtpCapability flag) {
|
||||
return (flags & flag) == flag;
|
||||
}
|
||||
public static bool HasFlag(this FtpVerify flags, FtpVerify flag) {
|
||||
return (flags & flag) == flag;
|
||||
}
|
||||
public static bool HasFlag(this FtpError flags, FtpError flag) {
|
||||
return (flags & flag) == flag;
|
||||
}
|
||||
public static void Restart(this Stopwatch watch) {
|
||||
watch.Stop();
|
||||
watch.Start();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
82
FluentFTP/Utils/FtpReflection.cs
Normal file
82
FluentFTP/Utils/FtpReflection.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
#if !CORE
|
||||
using System.Reflection;
|
||||
using System.Linq;
|
||||
#endif
|
||||
|
||||
namespace FluentFTP {
|
||||
internal static class FtpReflection {
|
||||
|
||||
#if !CORE
|
||||
public static object GetField(this object obj, string fieldName) {
|
||||
var tp = obj.GetType();
|
||||
var info = GetAllFields(tp).Where(f => f.Name == fieldName).Single();
|
||||
return info.GetValue(obj);
|
||||
}
|
||||
public static void SetField(this object obj, string fieldName, object value) {
|
||||
var tp = obj.GetType();
|
||||
var info = GetAllFields(tp).Where(f => f.Name == fieldName).Single();
|
||||
info.SetValue(obj, value);
|
||||
}
|
||||
public static object GetStaticField(this Assembly assembly, string typeName, string fieldName) {
|
||||
var tp = assembly.GetType(typeName);
|
||||
var info = GetAllFields(tp).Where(f => f.IsStatic).Where(f => f.Name == fieldName).Single();
|
||||
return info.GetValue(null);
|
||||
}
|
||||
|
||||
public static object GetProperty(this object obj, string propertyName) {
|
||||
var tp = obj.GetType();
|
||||
var info = GetAllProperties(tp).Where(f => f.Name == propertyName).Single();
|
||||
return info.GetValue(obj, null);
|
||||
}
|
||||
public static object CallMethod(this object obj, string methodName, params object[] prm) {
|
||||
var tp = obj.GetType();
|
||||
var info = GetAllMethods(tp).Where(f => f.Name == methodName && f.GetParameters().Length == prm.Length).Single();
|
||||
object rez = info.Invoke(obj, prm);
|
||||
return rez;
|
||||
}
|
||||
public static object NewInstance(this Assembly assembly, string typeName, params object[] prm) {
|
||||
var tp = assembly.GetType(typeName);
|
||||
var info = tp.GetConstructors().Where(f => f.GetParameters().Length == prm.Length).Single();
|
||||
object rez = info.Invoke(prm);
|
||||
return rez;
|
||||
}
|
||||
public static object InvokeStaticMethod(this Assembly assembly, string typeName, string methodName, params object[] prm) {
|
||||
var tp = assembly.GetType(typeName);
|
||||
var info = GetAllMethods(tp).Where(f => f.IsStatic).Where(f => f.Name == methodName && f.GetParameters().Length == prm.Length).Single();
|
||||
object rez = info.Invoke(null, prm);
|
||||
return rez;
|
||||
}
|
||||
public static object GetEnumValue(this Assembly assembly, string typeName, int value) {
|
||||
var tp = assembly.GetType(typeName);
|
||||
object rez = Enum.ToObject(tp, value);
|
||||
return rez;
|
||||
}
|
||||
|
||||
private static IEnumerable<FieldInfo> GetAllFields(Type t) {
|
||||
if (t == null)
|
||||
return Enumerable.Empty<FieldInfo>();
|
||||
|
||||
BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly;
|
||||
return t.GetFields(flags).Concat(GetAllFields(t.BaseType));
|
||||
}
|
||||
private static IEnumerable<PropertyInfo> GetAllProperties(Type t) {
|
||||
if (t == null)
|
||||
return Enumerable.Empty<PropertyInfo>();
|
||||
|
||||
BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly;
|
||||
return t.GetProperties(flags).Concat(GetAllProperties(t.BaseType));
|
||||
}
|
||||
private static IEnumerable<MethodInfo> GetAllMethods(Type t) {
|
||||
if (t == null)
|
||||
return Enumerable.Empty<MethodInfo>();
|
||||
|
||||
BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static | BindingFlags.Instance | BindingFlags.DeclaredOnly;
|
||||
return t.GetMethods(flags).Concat(GetAllMethods(t.BaseType));
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
}
|
||||
2850
FluentFTP/Utils/NET2Compatibility.cs
Normal file
2850
FluentFTP/Utils/NET2Compatibility.cs
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user