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