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
{
///
/// Wrapper Managed Class around putty sftp.
///
///
/// SFTPClient client = new SFTPClient();
/// if(client.Connect("sftp.somedomain.com","user","password","22"))
/// bool bFetchedFile = client.CmdExec_Any("get somefile.txt");
///
///
///
public class SFTPClient : IDisposable
{
#region Private Statics
///
/// Used in the all to LoadLibrary * Names here
/// * if you change it here * also change DllImports (below)
///
private const string PSFTP_FILENAME = "psftp.dll";
///
/// We only want to load the Psftp dll once per process
///
private static bool s_PSFTPDLL_IsLoaded = false;
///
/// Responsible for creating an active connection
///
[DllImport("psftp.dll")]
private static extern int dllmain(int argc, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr)] String[] argv);
///
/// Responsible for running a command on an active connection
///
[DllImport("psftp.dll")]
private static extern int do_sftp2([MarshalAs(UnmanagedType.LPStr)] string cmdLine);
///
/// Responsible for Putty Cleanup
///
[DllImport("psftp.dll")]
private static extern void dllmain_cleanup();
///
/// Loads the Putty Dll
///
[DllImport("kernel32.dll")]
private static extern IntPtr LoadLibrary(String dllname);
#endregion
#region Construction / Destruction
///
/// Create a SFTClient Object
///
public SFTPClient()
{
bool bForceFileWrite = true;
string regValue = RegKey.GetKey(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"));
}
///
/// Finalizer
///
~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
///
/// Start a Connection to the specified Host with the specified credentials
///
/// url to sftp host
/// user credential
/// password credential
/// port to connect to (default 22)
/// true if connection occured successfully, false otherwise
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;
}
///
/// End the active Connection
///
public bool Disconnect()
{
if (_IsConnected)
{
dllmain_cleanup();
_IsConnected = false;
}
return !_IsConnected;
}
///
/// Performs the specified Command on the Active Connection
///
/// a Command including optional parameters like "dir *.*" that can be understood by psftp
/// true, if command run successfully, falser otherwise
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)
///
/// Set the Working Directory Locally
///
/// path to a local directory
/// true to create the local directory, if it doesn't exist, false otherwise
/// true, if successful, false otherwise
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;
}
///
/// Set the Working Directory on the Remote sftp connection
///
/// path to a remote directory
/// true, if successful, false otherwise
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;
}
///
/// Checks for existence of the Directory on the Server
///
/// path to a remote directory
/// true, if remote directory exists, false otherwise
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;
}
///
/// Download a Directory at once including all it's files. if bRecursive is set,
/// Download all child directories and files
///
/// If bRecursive is true, recursively fetch files and directories
/// path to a remote directory
/// true, if successful, false otherwise
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;
}
///
/// Download multiple files at once
///
/// "*.c" "filename1.txt" "*.sln"
///
///
/// space seperated list of multiple filenames or wildcards
/// true, if successful, false otherwise
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;
}
///
/// Upload a Directory at once including all it's files. if bRecursive is set,
/// Upload all child directories and files
///
/// If bRecursive is true, recursively put files and directories
/// path to a local directory
/// true, if successful, false otherwise
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;
}
///
/// Upload multiple files at once
///
/// "*.c" "filename1.txt" "*.sln"
///
///
/// space seperated list of multiple filenames or wildcards
/// true, if successful, false otherwise
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;
}
///
/// Delete multiple files at once from the server
///
/// "*.c" "filename1.txt" "*.sln"
///
///
/// space seperated list of multiple filenames or wildcards
/// true, if successful, false otherwise
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;
}
///
/// Removes a Directory from the server. Directory must be empty in order
/// to be removed.
///
/// path to a remote directory
/// true, if successful, false otherwise
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
}
}