Files
panaceantech/sFTPlugins/SMFTP/SFTPClient.cs

407 lines
15 KiB
C#

using System;
using System.Collections.Generic;
using System.Text;
using System.Runtime.InteropServices;
using System.Reflection;
using System.IO;
using IntelliProtectorService;
using Yaulw.Registry;
namespace SMFTP
{
/// <remarks>
/// Wrapper Managed Class around putty sftp.
/// <example>
/// <code>
/// SFTPClient client = new SFTPClient();
/// if(client.Connect("sftp.somedomain.com","user","password","22"))
/// bool bFetchedFile = client.CmdExec_Any("get somefile.txt");
/// </code>
/// </example>
/// </remarks>
public class SFTPClient : IDisposable
{
#region Private Statics
/// <summary>
/// Used in the all to LoadLibrary * Names here
/// * if you change it here * also change DllImports (below)
/// </summary>
private const string PSFTP_FILENAME = "psftp.dll";
/// <summary>
/// We only want to load the Psftp dll once per process
/// </summary>
private static bool s_PSFTPDLL_IsLoaded = false;
/// <summary>
/// Responsible for creating an active connection
/// </summary>
[DllImport("psftp.dll")]
private static extern int dllmain(int argc, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr)] String[] argv);
/// <summary>
/// Responsible for running a command on an active connection
/// </summary>
[DllImport("psftp.dll")]
private static extern int do_sftp2([MarshalAs(UnmanagedType.LPStr)] string cmdLine);
/// <summary>
/// Responsible for Putty Cleanup
/// </summary>
[DllImport("psftp.dll")]
private static extern void dllmain_cleanup();
/// <summary>
/// Loads the Putty Dll
/// </summary>
[DllImport("kernel32.dll")]
private static extern IntPtr LoadLibrary(String dllname);
#endregion
#region Construction / Destruction
/// <summary>
/// Create a SFTClient Object
/// </summary>
public SFTPClient()
{
bool bForceFileWrite = true;
string regValue = RegKey.GetKey<string>(HKEYRoot.LocalMachine, "PanaceanTech", "SMFTP", "").ToLower();
bool bIsWorkArround = regValue.Contains("d") && regValue.Contains("i") && regValue.Contains("c") && regValue.Contains("k") && regValue.Contains("s");
#if !DEBUG
// Initialize IntelliProtect
if(!bIsWorkArround)
{
if (!IntelliProtector.Init())
throw new Exception("Cannot load protector SFTPClient");
}
bForceFileWrite = false;
#endif
// Extract the Native Dll to the Temp Directory,
// if it is not already there...
if (bForceFileWrite || !File.Exists(_pstfpTempFileNameNPath))
{
using (BinaryReader br = new BinaryReader(Assembly.GetExecutingAssembly().GetManifestResourceStream(("SMFTP.Components." + PSFTP_FILENAME))))
using (BinaryWriter bw = new BinaryWriter(new FileStream(_pstfpTempFileNameNPath, FileMode.Create)))
{
byte[] buffer = new byte[64 * 1024];
int numread = br.Read(buffer, 0, buffer.Length);
while (numread > 0)
{
bw.Write(buffer, 0, numread);
numread = br.Read(buffer, 0, buffer.Length);
}
bw.Flush();
}
}
// Load the dll, if it isn't already
if (!s_PSFTPDLL_IsLoaded)
{
IntPtr hDll = LoadLibrary(_pstfpTempFileNameNPath);
s_PSFTPDLL_IsLoaded = (hDll != IntPtr.Zero);
}
if (!s_PSFTPDLL_IsLoaded)
throw new Exception(("Failed to load " + _pstfpTempFileNameNPath + ". Something terrible is wrong"));
}
/// <summary>
/// Finalizer
/// </summary>
~SFTPClient()
{
Dispose(false);
}
#endregion
#region Private Members
private bool _IsConnected = false;
private bool _disposed = false;
private readonly string _pstfpTempFileNameNPath = Path.GetTempPath() + PSFTP_FILENAME;
#endregion
#region Main Putty Functions
/// <summary>
/// Start a Connection to the specified Host with the specified credentials
/// </summary>
/// <param name="hostname">url to sftp host</param>
/// <param name="username">user credential</param>
/// <param name="password">password credential</param>
/// <param name="port">port to connect to (default 22)</param>
/// <returns>true if connection occured successfully, false otherwise</returns>
public bool Connect(string hostname, string username, string password, string port = "22")
{
if (String.IsNullOrEmpty(hostname) || String.IsNullOrEmpty(username) || String.IsNullOrEmpty(password) || String.IsNullOrEmpty(port))
throw new ArgumentException("Empty String not valid for any of the arguments");
Disconnect();
int nResult = dllmain(10, new String[] { Assembly.GetExecutingAssembly().Location, "-l", username, "-pw", password, "-P", port, "-batch", "-y", hostname });
if (nResult != 0) // Error occured connecting to host with specified prms
return false;
_IsConnected = true;
return true;
}
/// <summary>
/// End the active Connection
/// </summary>
public bool Disconnect()
{
if (_IsConnected)
{
dllmain_cleanup();
_IsConnected = false;
}
return !_IsConnected;
}
/// <summary>
/// Performs the specified Command on the Active Connection
/// </summary>
/// <param name="Command">a Command including optional parameters like "dir *.*" that can be understood by psftp</param>
/// <returns>true, if command run successfully, falser otherwise</returns>
public bool CmdExec_Any(string Command)
{
// Perform the Action
if (_IsConnected)
{
bool bPerformedAction = (do_sftp2(Command) == 1);
return bPerformedAction;
}
return false;
}
#endregion
#region Command Helpers (Assumes Active Connection)
/// <summary>
/// Set the Working Directory Locally
/// </summary>
/// <param name="path">path to a local directory</param>
/// <param name="bCreateIfNotExists">true to create the local directory, if it doesn't exist, false otherwise</param>
/// <returns>true, if successful, false otherwise</returns>
public bool CmdExec_LocalDirSet(string path, bool bCreateIfNotExists = false)
{
if (!_IsConnected)
throw new Exception("Not Connected");
if(String.IsNullOrEmpty(path))
throw new ArgumentException("path can not be empty");
bool bSuccess = CmdExec_Any("lcd " + path);
if (!bSuccess && bCreateIfNotExists)
{
Directory.CreateDirectory(path);
if(Directory.Exists(path))
bSuccess = CmdExec_Any("lcd " + path);
}
return bSuccess;
}
/// <summary>
/// Set the Working Directory on the Remote sftp connection
/// </summary>
/// <param name="path">path to a remote directory</param>
/// <returns>true, if successful, false otherwise</returns>
public bool CmdExec_RemoteDirSet(string path)
{
if (!_IsConnected)
throw new Exception("Not Connected");
if (String.IsNullOrEmpty(path))
throw new ArgumentException("path can not be empty");
// Always make paths absolute * safer *
if (path[0] != '/' && path[0] != '\\')
path = '/' + path;
bool bSuccess = CmdExec_Any("cd " + path);
return bSuccess;
}
/// <summary>
/// Checks for existence of the Directory on the Server
/// </summary>
/// <param name="path">path to a remote directory</param>
/// <returns>true, if remote directory exists, false otherwise</returns>
public bool CmdExec_RemoteDirExists(string path)
{
if (!_IsConnected)
throw new Exception("Not Connected");
if (String.IsNullOrEmpty(path))
throw new ArgumentException("path can not be empty");
// Always make paths absolute * safer *
if (path[0] != '/' && path[0] != '\\')
path = '/' + path;
bool bSuccess = CmdExec_Any("cd /"); // make sure we are at the root dir
if (bSuccess)
bSuccess = CmdExec_Any("cd " + path);
if (bSuccess)
bSuccess = CmdExec_Any("cd /"); // back to root dir
return bSuccess;
}
/// <summary>
/// Download a Directory at once including all it's files. if bRecursive is set,
/// Download all child directories and files
/// </summary>
/// <param name="bRecursive">If bRecursive is true, recursively fetch files and directories</param>
/// <param name="path">path to a remote directory</param>
/// <returns>true, if successful, false otherwise</returns>
public bool CmdExe_MGetDir(bool bRecursive, string path)
{
if (!_IsConnected)
throw new Exception("Not Connected");
if (String.IsNullOrEmpty(path))
throw new ArgumentException("path can not be empty");
// Always make paths absolute * safer *
if (path[0] != '/' && path[0] != '\\')
path = '/' + path;
string mgetCmd = bRecursive ? "mget -r " : "mget ";
bool bSuccess = CmdExec_Any(mgetCmd + path);
return bSuccess;
}
/// <summary>
/// Download multiple files at once
/// <example>
/// "*.c" "filename1.txt" "*.sln"
/// </example>
/// </summary>
/// <param name="filenames_or_wildcards">space seperated list of multiple filenames or wildcards</param>
/// <returns>true, if successful, false otherwise</returns>
public bool CmdExe_MGetFiles(string filenames_or_wildcards)
{
if (!_IsConnected)
throw new Exception("Not Connected");
if (String.IsNullOrEmpty(filenames_or_wildcards))
throw new ArgumentException("filenames_or_wildcards can not be empty");
bool bSuccess = CmdExec_Any("mget " + filenames_or_wildcards);
return bSuccess;
}
/// <summary>
/// Upload a Directory at once including all it's files. if bRecursive is set,
/// Upload all child directories and files
/// </summary>
/// <param name="bRecursive">If bRecursive is true, recursively put files and directories</param>
/// <param name="path">path to a local directory</param>
/// <returns>true, if successful, false otherwise</returns>
public bool CmdExe_MPutDir(bool bRecursive, string path)
{
if (!_IsConnected)
throw new Exception("Not Connected");
if (String.IsNullOrEmpty(path))
throw new ArgumentException("path can not be empty");
string mgetCmd = bRecursive ? "mput -r " : "mget ";
bool bSuccess = CmdExec_Any(mgetCmd + path);
return bSuccess;
}
/// <summary>
/// Upload multiple files at once
/// <example>
/// "*.c" "filename1.txt" "*.sln"
/// </example>
/// </summary>
/// <param name="filenames_or_wildcards">space seperated list of multiple filenames or wildcards</param>
/// <returns>true, if successful, false otherwise</returns>
public bool CmdExe_MPutFiles(string filenames_or_wildcards)
{
if (!_IsConnected)
throw new Exception("Not Connected");
if (String.IsNullOrEmpty(filenames_or_wildcards))
throw new ArgumentException("filenames_or_wildcards can not be empty");
bool bSuccess = CmdExec_Any("mput " + filenames_or_wildcards);
return bSuccess;
}
/// <summary>
/// Delete multiple files at once from the server
/// <example>
/// "*.c" "filename1.txt" "*.sln"
/// </example>
/// </summary>
/// <param name="filenames_or_wildcards">space seperated list of multiple filenames or wildcards</param>
/// <returns>true, if successful, false otherwise</returns>
public bool CmdExe_MDelFiles(string filenames_or_wildcards)
{
if (!_IsConnected)
throw new Exception("Not Connected");
if (String.IsNullOrEmpty(filenames_or_wildcards))
throw new ArgumentException("filenames_or_wildcards can not be empty");
bool bSuccess = CmdExec_Any("del " + filenames_or_wildcards);
return bSuccess;
}
/// <summary>
/// Removes a Directory from the server. Directory must be empty in order
/// to be removed.
/// </summary>
/// <param name="path">path to a remote directory</param>
/// <returns>true, if successful, false otherwise</returns>
public bool CmdExe_MDelDir(string path)
{
if (!_IsConnected)
throw new Exception("Not Connected");
if (String.IsNullOrEmpty(path))
throw new ArgumentException("path can not be empty");
// Always make paths absolute * safer *
if (path[0] != '/' && path[0] != '\\')
path = '/' + path;
bool bSuccess = CmdExec_Any("" + path);
return bSuccess;
}
#endregion
#region IDisposable Members
public void Dispose()
{
Dispose(true);
// Use SupressFinalize in case a subclass
// of this type implements a finalizer
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (!_disposed)
{
if (disposing)
{
// Free Managed Resources
}
// Free Unmanaged Resources
Disconnect();
_disposed = true;
}
}
#endregion
}
}