407 lines
15 KiB
C#
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, "Software\\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
|
|
}
|
|
}
|