//******************************************************************************************************
// FtpClient.cs - Gbtc
//
// Copyright © 2020, Grid Protection Alliance. All Rights Reserved.
//
// Licensed to the Grid Protection Alliance (GPA) under one or more contributor license agreements. See
// the NOTICE file distributed with this work for additional information regarding copyright ownership.
// The GPA licenses this file to you under the MIT License (MIT), the "License"; you may not use this
// file except in compliance with the License. You may obtain a copy of the License at:
//
// http://opensource.org/licenses/MIT
//
// Unless agreed to in writing, the subject software distributed under the License is distributed on an
// "AS-IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. Refer to the
// License for the specific language governing permissions and limitations.
//
// Code Modification History:
// ----------------------------------------------------------------------------------------------------
// 05/12/2020 - J. Ritchie Carroll
// Generated original version of source code.
//
//******************************************************************************************************
using System;
// ReSharper disable InconsistentNaming
namespace GSF.Net.VirtualFtpClient
{
#region [ Enumerations ]
///
/// Defines support FTP types for virtual FTP client.
///
public enum FtpType
{
///
/// Standard File Transport Protocol (FTP)
///
Ftp,
///
/// Trivial File Transport Protocol (TFTP)
///
TFtp
// Future: FTPS
// Future: SFTP/SSH
}
///
/// FTP file transfer direction enumeration.
///
public enum TransferDirection
{
///
/// FTP transfer direction set to upload.
///
Upload,
///
/// FTP transfer direction set to download.
///
Download
}
#endregion
///
/// Represents an virtual FTP session for the specified target .
///
public class FtpClient : IDisposable
{
#region [ Members ]
// Events
///
/// Raised when file transfer begins.
///
///
/// is local filename,
/// is remote filename,
/// is file transfer direction.
///
public event EventHandler> BeginFileTransfer;
///
/// Raised when file transfer completes.
///
///
/// is local filename,
/// is remote filename,
/// is file transfer direction.
///
public event EventHandler> EndFileTransfer;
///
/// Raised as file transfer is progressing.
///
///
/// is current file transfer progress,
/// is file transfer direction.
///
public event EventHandler, TransferDirection>> FileTransferProgress;
///
/// Raised when file transfer process has completed (success or failure).
///
///
/// is result of FTP transfer process.
///
public event EventHandler> FileTransferNotification;
///
/// Raised when FTP response has been received.
///
///
/// is received FTP response.
///
public event EventHandler> ResponseReceived;
///
/// Raised when FTP command has been sent.
///
///
/// is sent FTP command.
///
public event EventHandler> CommandSent;
///
/// Raised when class is disposed;
///
public event EventHandler Disposed;
// Fields
private Ftp.FtpClient m_ftpClient;
private TFtp.TFtpClient m_tftpClient;
private bool m_disposed;
#endregion
#region [ Constructors ]
///
/// Creates a new .
///
/// Target for this
/// Set to true to be case sensitive with FTP file and directory names.
public FtpClient(FtpType ftpType, bool caseSensitive = false)
{
FtpType = ftpType;
CaseSensitive = caseSensitive;
}
#endregion
#region [ Properties ]
///
/// Gets for this .
///
public FtpType FtpType { get; }
///
/// Gets or sets FTP server name (DNS name or IP).
///
///
/// FTP server name should not be prefixed with FTP://.
///
public string Server { get; set; }
///
/// Gets or sets FTP server port to use, defaults to 21.
///
///
/// This only needs to be changed if the FTP server is established on a non-standard port number.
///
public int? Port { get; set; }
///
/// Gets or sets the timeout, in milliseconds, for
/// read and write operations, defaults to 30 seconds.
///
public int Timeout { get; set; }
///
/// Gets or sets FTP case sensitivity of file and directory names.
///
///
/// Set to true to be case sensitive with FTP file and directory names.
///
public bool CaseSensitive { get; }
///
/// Gets or sets the passive/active mode of the server.
///
public bool Passive { get; set; }
///
/// Gets or sets the IP address to send with the PORT command.
///
public string ActiveAddress { get; set; }
///
/// Gets or sets the minimum value in the range of ports
/// used when listening for connections in active mode.
///
public int? MinActivePort { get; set; }
///
/// Gets or sets the maximum value in the range of ports
/// used when listening for connections in active mode.
///
public int? MaxActivePort { get; set; }
///
/// Returns true if FTP session is currently connected.
///
public bool IsConnected => FtpClientConnection?.IsConnected ?? TFtpClientConnection != null;
internal Ftp.FtpClient FtpClientConnection
{
get => m_ftpClient;
set
{
if (m_ftpClient != null)
{
// Detach from events on existing reference
m_ftpClient.BeginFileTransfer -= FtpClient_BeginFileTransfer;
m_ftpClient.EndFileTransfer -= FtpClient_EndFileTransfer;
m_ftpClient.FileTransferProgress -= FtpClient_FileTransferProgress;
m_ftpClient.FileTransferNotification -= FtpClient_FileTransferNotification;
m_ftpClient.ResponseReceived -= FtpClient_ResponseReceived;
m_ftpClient.CommandSent -= FtpClient_CommandSent;
if (m_ftpClient != value)
m_ftpClient?.Dispose();
}
// Assign new reference
m_ftpClient = value;
if (m_ftpClient == null)
return;
// Attach to events on new reference
m_ftpClient.BeginFileTransfer += FtpClient_BeginFileTransfer;
m_ftpClient.EndFileTransfer += FtpClient_EndFileTransfer;
m_ftpClient.FileTransferProgress += FtpClient_FileTransferProgress;
m_ftpClient.FileTransferNotification += FtpClient_FileTransferNotification;
m_ftpClient.ResponseReceived += FtpClient_ResponseReceived;
m_ftpClient.CommandSent += FtpClient_CommandSent;
}
}
internal TFtp.TFtpClient TFtpClientConnection
{
get => m_tftpClient;
set
{
if (m_tftpClient != null && m_tftpClient != value)
m_ftpClient?.Dispose();
// Assign new reference
m_tftpClient = value;
}
}
#endregion
#region [ Methods ]
///
/// Releases all the resources used by the object.
///
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
///
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
///
/// true to release both managed and unmanaged resources; false to release only unmanaged resources.
protected virtual void Dispose(bool disposing)
{
if (m_disposed)
return;
try
{
if (disposing)
{
m_ftpClient?.Dispose();
}
}
finally
{
Disposed?.Invoke(this, EventArgs.Empty);
m_disposed = true; // Prevent duplicate dispose.
}
}
///
/// Connects to FTP server using specified credentials.
///
/// User name used to authenticate to FTP server.
/// Password used to authenticate to FTP server.
public void Connect(string userName, string password)
{
switch (FtpType)
{
case FtpType.Ftp:
{
FtpClientConnection = new Ftp.FtpClient(!CaseSensitive)
{
Server = Server,
Port = Port ?? 21,
Timeout = Timeout,
Passive = Passive,
ActiveAddress = ActiveAddress,
MinActivePort = MinActivePort ?? 0,
MaxActivePort = MaxActivePort ?? 0
};
FtpClientConnection.Connect(userName, password);
break;
}
case FtpType.TFtp:
{
TFtpClientConnection = new TFtp.TFtpClient(Server, Port ?? TFtp.TFtpServer.DefaultServerPort);
break;
}
default:
throw new ArgumentOutOfRangeException();
}
}
///
/// Changes the current FTP session directory to the specified path.
///
/// New directory.
public void SetCurrentDirectory(string directoryPath)
{
if (!IsConnected)
throw new InvalidOperationException("You must be connected to the FTP server before you can set the current directory.");
if (directoryPath.Length <= 0)
return;
//State.CurrentDirectory = new FtpDirectory((FtpSessionConnected)State, CaseInsensitive, directoryPath);
//State.CurrentDirectory.Refresh();
}
private void OnBeginFileTransfer(string localFileName, string remoteFileName, TransferDirection transferDirection)
{
BeginFileTransfer?.Invoke(this, new EventArgs(localFileName, remoteFileName, transferDirection));
}
private void OnEndFileTransfer(string localFileName, string remoteFileName, TransferDirection transferDirection)
{
EndFileTransfer?.Invoke(this, new EventArgs(localFileName, remoteFileName, transferDirection));
}
private void OnFileTransferProgress(ProcessProgress fileTransferProgress, TransferDirection transferDirection)
{
FileTransferProgress?.Invoke(this, new EventArgs, TransferDirection>(fileTransferProgress, transferDirection));
}
private void OnFileTransferNotification(FtpTransferResult transferResult)
{
FileTransferNotification?.Invoke(this, new EventArgs(transferResult));
}
private void OnResponseReceived(string response)
{
ResponseReceived?.Invoke(this, new EventArgs(response));
}
private void OnCommandSent(string command)
{
CommandSent?.Invoke(this, new EventArgs(command));
}
private void FtpClient_BeginFileTransfer(object sender, EventArgs e)
{
OnBeginFileTransfer(e.Argument1, e.Argument2, e.Argument3 == Ftp.TransferDirection.Upload ? TransferDirection.Upload : TransferDirection.Download);
}
private void FtpClient_EndFileTransfer(object sender, EventArgs e)
{
OnEndFileTransfer(e.Argument1, e.Argument2, e.Argument3 == Ftp.TransferDirection.Upload ? TransferDirection.Upload : TransferDirection.Download);
}
private void FtpClient_FileTransferProgress(object sender, EventArgs, Ftp.TransferDirection> e)
{
OnFileTransferProgress(e.Argument1, e.Argument2 == Ftp.TransferDirection.Upload ? TransferDirection.Upload : TransferDirection.Download);
}
private void FtpClient_FileTransferNotification(object sender, EventArgs e)
{
OnFileTransferNotification(new FtpTransferResult(e.Argument.Message, e.Argument.ResponseCode));
}
private void FtpClient_ResponseReceived(object sender, EventArgs e)
{
OnResponseReceived(e.Argument);
}
private void FtpClient_CommandSent(object sender, EventArgs e)
{
OnCommandSent(e.Argument);
}
#endregion
}
}