FluentFTP 추가
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<configuration>
|
<configuration>
|
||||||
<startup>
|
<startup>
|
||||||
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2" />
|
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5.2"/>
|
||||||
</startup>
|
</startup>
|
||||||
</configuration>
|
</configuration>
|
||||||
|
|||||||
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 NewsCrawler
|
||||||
|
{
|
||||||
|
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
@@ -28,6 +28,7 @@
|
|||||||
<UseApplicationTrust>false</UseApplicationTrust>
|
<UseApplicationTrust>false</UseApplicationTrust>
|
||||||
<PublishWizardCompleted>true</PublishWizardCompleted>
|
<PublishWizardCompleted>true</PublishWizardCompleted>
|
||||||
<BootstrapperEnabled>true</BootstrapperEnabled>
|
<BootstrapperEnabled>true</BootstrapperEnabled>
|
||||||
|
<TargetFrameworkProfile />
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
@@ -35,19 +36,21 @@
|
|||||||
<DebugType>full</DebugType>
|
<DebugType>full</DebugType>
|
||||||
<Optimize>false</Optimize>
|
<Optimize>false</Optimize>
|
||||||
<OutputPath>bin\Debug\</OutputPath>
|
<OutputPath>bin\Debug\</OutputPath>
|
||||||
<DefineConstants>DEBUG;TRACE</DefineConstants>
|
<DefineConstants>TRACE;DEBUG;ASYNC;NET45</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
<Prefer32Bit>true</Prefer32Bit>
|
<Prefer32Bit>true</Prefer32Bit>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||||
<PlatformTarget>AnyCPU</PlatformTarget>
|
<PlatformTarget>AnyCPU</PlatformTarget>
|
||||||
<DebugType>pdbonly</DebugType>
|
<DebugType>pdbonly</DebugType>
|
||||||
<Optimize>true</Optimize>
|
<Optimize>true</Optimize>
|
||||||
<OutputPath>bin\Release\</OutputPath>
|
<OutputPath>bin\Release\</OutputPath>
|
||||||
<DefineConstants>TRACE</DefineConstants>
|
<DefineConstants>TRACE;ASYNC;NET45</DefineConstants>
|
||||||
<ErrorReport>prompt</ErrorReport>
|
<ErrorReport>prompt</ErrorReport>
|
||||||
<WarningLevel>4</WarningLevel>
|
<WarningLevel>4</WarningLevel>
|
||||||
|
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ManifestCertificateThumbprint>3313DBEFFBEC4C1DB9EB9C32C7AE16B7932B4A65</ManifestCertificateThumbprint>
|
<ManifestCertificateThumbprint>3313DBEFFBEC4C1DB9EB9C32C7AE16B7932B4A65</ManifestCertificateThumbprint>
|
||||||
@@ -85,6 +88,7 @@
|
|||||||
<Reference Include="System" />
|
<Reference Include="System" />
|
||||||
<Reference Include="System.Configuration" />
|
<Reference Include="System.Configuration" />
|
||||||
<Reference Include="System.Core" />
|
<Reference Include="System.Core" />
|
||||||
|
<Reference Include="System.Web" />
|
||||||
<Reference Include="System.Web.Extensions" />
|
<Reference Include="System.Web.Extensions" />
|
||||||
<Reference Include="System.Xml.Linq" />
|
<Reference Include="System.Xml.Linq" />
|
||||||
<Reference Include="System.Data.DataSetExtensions" />
|
<Reference Include="System.Data.DataSetExtensions" />
|
||||||
@@ -97,6 +101,34 @@
|
|||||||
<Reference Include="System.Xml" />
|
<Reference Include="System.Xml" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<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">
|
<Compile Include="ListViewNF.cs">
|
||||||
<SubType>Component</SubType>
|
<SubType>Component</SubType>
|
||||||
</Compile>
|
</Compile>
|
||||||
@@ -144,6 +176,7 @@
|
|||||||
<Compile Include="Properties\Resources.Designer.cs">
|
<Compile Include="Properties\Resources.Designer.cs">
|
||||||
<AutoGen>True</AutoGen>
|
<AutoGen>True</AutoGen>
|
||||||
<DependentUpon>Resources.resx</DependentUpon>
|
<DependentUpon>Resources.resx</DependentUpon>
|
||||||
|
<DesignTime>True</DesignTime>
|
||||||
</Compile>
|
</Compile>
|
||||||
<None Include="app.manifest" />
|
<None Include="app.manifest" />
|
||||||
<None Include="packages.config" />
|
<None Include="packages.config" />
|
||||||
|
|||||||
12
NewsForm.cs
12
NewsForm.cs
@@ -9,6 +9,7 @@ using System.Globalization;
|
|||||||
using System.IO;
|
using System.IO;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Net;
|
using System.Net;
|
||||||
|
using System.Net.NetworkInformation;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
@@ -882,10 +883,19 @@ namespace NewsCrawler
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if(m_bBuy == true)
|
if (m_bBuy == true)
|
||||||
|
{
|
||||||
|
FileTransfer ft = new FileTransfer();
|
||||||
|
string today = DateTime.Now.ToString("yyyy-MM-dd");
|
||||||
|
string macAddr = NetworkInterface.GetAllNetworkInterfaces()[0].GetPhysicalAddress().ToString();
|
||||||
|
ft.SendDir("/configure", macAddr + "/NewsCrawler/" + today);
|
||||||
|
|
||||||
Util.Log(Util.LOG_TYPE.VERVOSE, "매수 시작");
|
Util.Log(Util.LOG_TYPE.VERVOSE, "매수 시작");
|
||||||
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
Util.Log(Util.LOG_TYPE.VERVOSE, "매수 취소");
|
Util.Log(Util.LOG_TYPE.VERVOSE, "매수 취소");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public string[] GetAccounts()
|
public string[] GetAccounts()
|
||||||
|
|||||||
112
Properties/Resources.Designer.cs
generated
112
Properties/Resources.Designer.cs
generated
@@ -8,64 +8,56 @@
|
|||||||
// </auto-generated>
|
// </auto-generated>
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace NewsCrawler.Properties
|
namespace NewsCrawler.Properties {
|
||||||
{
|
using System;
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// A strongly-typed resource class, for looking up localized strings, etc.
|
/// A strongly-typed resource class, for looking up localized strings, etc.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
// This class was auto-generated by the StronglyTypedResourceBuilder
|
// This class was auto-generated by the StronglyTypedResourceBuilder
|
||||||
// class via a tool like ResGen or Visual Studio.
|
// class via a tool like ResGen or Visual Studio.
|
||||||
// To add or remove a member, edit your .ResX file then rerun ResGen
|
// To add or remove a member, edit your .ResX file then rerun ResGen
|
||||||
// with the /str option, or rebuild your VS project.
|
// with the /str option, or rebuild your VS project.
|
||||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")]
|
||||||
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
|
||||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
internal class Resources
|
internal class Resources {
|
||||||
{
|
|
||||||
|
private static global::System.Resources.ResourceManager resourceMan;
|
||||||
private static global::System.Resources.ResourceManager resourceMan;
|
|
||||||
|
private static global::System.Globalization.CultureInfo resourceCulture;
|
||||||
private static global::System.Globalization.CultureInfo resourceCulture;
|
|
||||||
|
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||||||
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
internal Resources() {
|
||||||
internal Resources()
|
}
|
||||||
{
|
|
||||||
}
|
/// <summary>
|
||||||
|
/// Returns the cached ResourceManager instance used by this class.
|
||||||
/// <summary>
|
/// </summary>
|
||||||
/// Returns the cached ResourceManager instance used by this class.
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
/// </summary>
|
internal static global::System.Resources.ResourceManager ResourceManager {
|
||||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
get {
|
||||||
internal static global::System.Resources.ResourceManager ResourceManager
|
if (object.ReferenceEquals(resourceMan, null)) {
|
||||||
{
|
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NewsCrawler.Properties.Resources", typeof(Resources).Assembly);
|
||||||
get
|
resourceMan = temp;
|
||||||
{
|
}
|
||||||
if((resourceMan==null))
|
return resourceMan;
|
||||||
{
|
}
|
||||||
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("NewsCrawler.Properties.Resources", typeof(Resources).Assembly);
|
}
|
||||||
resourceMan=temp;
|
|
||||||
}
|
/// <summary>
|
||||||
return resourceMan;
|
/// Overrides the current thread's CurrentUICulture property for all
|
||||||
}
|
/// resource lookups using this strongly typed resource class.
|
||||||
}
|
/// </summary>
|
||||||
|
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
||||||
/// <summary>
|
internal static global::System.Globalization.CultureInfo Culture {
|
||||||
/// Overrides the current thread's CurrentUICulture property for all
|
get {
|
||||||
/// resource lookups using this strongly typed resource class.
|
return resourceCulture;
|
||||||
/// </summary>
|
}
|
||||||
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
|
set {
|
||||||
internal static global::System.Globalization.CultureInfo Culture
|
resourceCulture = value;
|
||||||
{
|
}
|
||||||
get
|
}
|
||||||
{
|
}
|
||||||
return resourceCulture;
|
|
||||||
}
|
|
||||||
set
|
|
||||||
{
|
|
||||||
resourceCulture=value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
34
Properties/Settings.Designer.cs
generated
34
Properties/Settings.Designer.cs
generated
@@ -8,23 +8,19 @@
|
|||||||
// </auto-generated>
|
// </auto-generated>
|
||||||
//------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------
|
||||||
|
|
||||||
namespace NewsCrawler.Properties
|
namespace NewsCrawler.Properties {
|
||||||
{
|
|
||||||
|
|
||||||
|
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
||||||
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
|
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "14.0.0.0")]
|
||||||
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
|
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase {
|
||||||
internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase
|
|
||||||
{
|
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
||||||
|
|
||||||
private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings())));
|
public static Settings Default {
|
||||||
|
get {
|
||||||
public static Settings Default
|
return defaultInstance;
|
||||||
{
|
}
|
||||||
get
|
}
|
||||||
{
|
}
|
||||||
return defaultInstance;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -225,6 +225,10 @@ namespace NewsCrawler
|
|||||||
if(result != null)
|
if(result != null)
|
||||||
return new RESULT(TYPE.NEGATIVE, result.ToString());
|
return new RESULT(TYPE.NEGATIVE, result.ToString());
|
||||||
|
|
||||||
|
result = m_PositiveForce.Find(s => s.IsMatch(strText));
|
||||||
|
if (result != null)
|
||||||
|
return new RESULT(TYPE.POSITIVE_FORCE, result.ToString());
|
||||||
|
|
||||||
result = m_Manual.Find(s => s.IsMatch(strText));
|
result = m_Manual.Find(s => s.IsMatch(strText));
|
||||||
if(result != null)
|
if(result != null)
|
||||||
return new RESULT(TYPE.MANUAL, result.ToString());
|
return new RESULT(TYPE.MANUAL, result.ToString());
|
||||||
@@ -233,10 +237,6 @@ namespace NewsCrawler
|
|||||||
if(result != null)
|
if(result != null)
|
||||||
return new RESULT(TYPE.POSITIVE, result.ToString());
|
return new RESULT(TYPE.POSITIVE, result.ToString());
|
||||||
|
|
||||||
result = m_PositiveForce.Find(s => s.IsMatch(strText));
|
|
||||||
if (result != null)
|
|
||||||
return new RESULT(TYPE.POSITIVE_FORCE, result.ToString());
|
|
||||||
|
|
||||||
return new RESULT(TYPE.NOT_MATCHED, "");
|
return new RESULT(TYPE.NOT_MATCHED, "");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user