Title: SSH and SCP Class using Putty
Question: SSH and SCP are widely used on Linux and Unix platforms as replacements for REXEC and FTP, which are normally disabled.
Answer:
Finding a free component for Intel SSH on the web proved fruitless at this moment in time as all workable ones found come at a price. There is however free executables PLINK.EXE and PSCP.EXE available from Putty. These work very well and I decided to write a Delphi class to encapsulate them. You obviously need to distribute them with your application.
The code is for Delphi 2010 and uses my WinMisc and Files class. You can implement your own WinMisc.ExecuteConsolePipe() and Files.CreateUnique(), or
you can E-Mail me and I will send you the source of MW_WinMisc and MW_Files.
The code should not be too difficult to port to lower Delphi versions.
unit MW_PuttySSH;
interface
{$REGION 'Documentation'}
// =================================================================================================
// UltraRAD Components
// Mike Heydon 2010
//
// MW_PuttySSH - Class to implement SSH scp and rexec using Putty PSCP and PLINK
//
// pscp command example
// pscp -l oracle -pw ora123 192.168.0.112:"c:/eoh_logs/Shutdown*.log" "c:/ssh_tools"
//
// =================================================================================================
{$ENDREGION}
uses Windows, Forms, SysUtils, Controls, Classes, MW_WinMisc, MW_Files;
{$REGION 'Types and Classes'}
type
TPuttySSH = class(TComponent)
public type
TProtocolVersion = (pvDefault,pvSSH1,pvSSH2);
private const
C_TEST = '%spscp -l %s -pw %s -ls "%s:/"';
strict private
FExecCommand,
FRemoteFile,FLocalFile,
FSSHToolspath,
FRemoteHost,FUser,FPassword : string;
FProtocolVersion : TProtocolVersion;
FPrivateKeyFile : TFileName;
FPort : integer;
protected
procedure _CheckFile(const AFile : string);
procedure _Put(AResults : TStrings; AIsDir : boolean);
procedure _Get(AResults : TStrings; AIsDir : boolean);
public
constructor Create(AOwner : TComponent); override;
procedure Get(AResults : TStrings; ARecurse : boolean = false);
procedure Put(AResults : TStrings; ARecurse : boolean = false);
procedure Execute(AResults : TStrings);
procedure Directory(const ADirectoryPath : string; AResults : TStrings);
procedure TestConnection;
published
property SSHToolsPath : string read FSSHToolsPath write FSSHToolsPath;
property RemoteHost : string read FRemoteHost write FRemoteHost;
property User : string read FUser write FUser;
property Password : string read FPassword write FPassword;
property RemoteFile : string read FRemoteFile write FRemoteFile;
property LocalFile : string read FLocalFile write FLocalFile;
property ExecCommand : string read FExecCommand write FExecCommand;
property ProtocolVersion : TProtocolVersion read FProtocolVersion
write FProtocolversion;
property PrivateKeyFile : TFileName read FPrivateKeyFile write FPrivateKeyFile;
property Port : integer read FPort write FPort;
end;
{$ENDREGION}
// -------------------------------------------------------------------------------------------------
implementation
{$REGION 'PuttySSH Class'}
constructor TPuttySSH.Create(AOwner : TComponent);
begin
inherited Create(AOwner);
FSSHToolsPath := 'C:\SSH_TOOLS';
FRemoteHost := '';
FUser := '';
FPassword := '';
FProtocolVersion := pvDefault;
FPrivateKeyFile := '';
FRemoteFile := '';
FLocalFile := '';
FExecCommand := '';
FPort := 22;
end;
procedure TPuttySSH._CheckFile(const AFile : string);
var sFile : string;
begin
sFile := IncludeTrailingPathDelimiter(FSSHToolsPath) + AFile;
if not FileExists(sFile) then
raise Exception.Create('Cannot find file ' + sFile);
end;
procedure TPuttySSH._Put(AResults : TStrings; AIsDir : boolean);
var oResults : TStringList;
sCommand : string;
begin
_CheckFile('PSCP.EXE');
sCommand := '"' + IncludeTrailingPathDelimiter(FSSHToolsPath) + 'PSCP"';
if FUser '' then sCommand := sCommand + ' -l ' + Fuser;
if FPassword '' then sCommand := sCommand + ' -pw ' + FPassword;
if FPrivateKeyFile '' then sCommand := sCommand + ' -i "' + FPrivateKeyFile + '"';
if FPort 22 then sCommand := sCommand + ' -P ' + IntTostr(FPort);
if AIsDir then sCommand := sCommand + ' -r';
case FprotocolVersion of
pvSSH1 : sCommand := sCommand + ' -1';
pvSSH2 : sCommand := sCommand + ' -2';
end;
sCommand := sCommand + ' -batch "' + FLocalFile + '" ';
sCommand := sCommand + FRemoteHost + ':"' + FRemoteFile + '"';
Screen.Cursor := crHourGlass;
if Assigned(AResults) then
WinMisc.ExecConsolePipe(sCommand,AResults)
else begin
oResults := tStringList.Create;
WinMisc.ExecConsolePipe(sCommand,oResults);
FreeAndNil(oResults);
end;
Screen.Cursor := crDefault;
end;
procedure TPuttySSH._Get(AResults : TStrings; AIsDir : boolean);
var oResults : TStringList;
sCommand : string;
begin
_CheckFile('PSCP.EXE');
sCommand := '"' + IncludeTrailingPathDelimiter(FSSHToolsPath) + 'PSCP"';
if FUser '' then sCommand := sCommand + ' -l ' + Fuser;
if FPassword '' then sCommand := sCommand + ' -pw ' + FPassword;
if FPrivateKeyFile '' then sCommand := sCommand + ' -i "' + FPrivateKeyFile + '"';
if FPort 22 then sCommand := sCommand + ' -P ' + IntTostr(FPort);
if AIsDir then sCommand := sCommand + ' -r';
case FprotocolVersion of
pvSSH1 : sCommand := sCommand + ' -1';
pvSSH2 : sCommand := sCommand + ' -2';
end;
sCommand := sCommand + ' -batch ' + FRemoteHost + ':"' + FRemoteFile + '"';
sCommand := sCommand + ' "' + LocalFile + '"';
Screen.Cursor := crHourGlass;
if Assigned(AResults) then
WinMisc.ExecConsolePipe(sCommand,AResults)
else begin
oResults := tStringList.Create;
WinMisc.ExecConsolePipe(sCommand,oResults);
FreeAndNil(oResults);
end;
Screen.Cursor := crDefault;
end;
procedure TPuttySSH.Get(AResults : TStrings; ARecurse : boolean = false);
begin
_Get(AResults,ARecurse);
end;
procedure TPuttySSH.Put(AResults : TStrings; ARecurse : boolean = false);
begin
_Put(AResults,ARecurse);
end;
procedure TPuttySSH.Directory(const ADirectoryPath : string; AResults : TStrings);
var sCommand : string;
begin
_CheckFile('PSCP.EXE');
sCommand := '"' + IncludeTrailingPathDelimiter(FSSHToolsPath) + 'PSCP"';
if FUser '' then sCommand := sCommand + ' -l ' + Fuser;
if FPassword '' then sCommand := sCommand + ' -pw ' + FPassword;
if FPrivateKeyFile '' then sCommand := sCommand + ' -i "' + FPrivateKeyFile + '"';
if FPort 22 then sCommand := sCommand + ' -P ' + IntTostr(FPort);
case FprotocolVersion of
pvSSH1 : sCommand := sCommand + ' -1';
pvSSH2 : sCommand := sCommand + ' -2';
end;
sCommand := sCommand + ' -batch -ls ' + FRemoteHost + ':"' + ADirectoryPath + '"';
Screen.Cursor := crHourGlass;
WinMisc.ExecConsolePipe(sCommand,AResults);
Screen.Cursor := crDefault;
end;
procedure TPuttySSH.TestConnection;
var fBatch : TextFile;
sBatch : string;
iExit : longword;
begin
sBatch := Files.CreateUnique();
sBatch := ChangeFileExt(sBatch,'.cmd');
AssignFile(fBatch,sBatch);
Rewrite(fBatch);
WriteLn(fBatch,'echo off');
WriteLn(fBatch,'cls');
WriteLn(fBatch,'echo Testing SSH Connectivity to ' + FRemoteHost + ' ...');
WriteLn(fBatch,Format(C_TEST,[IncludeTrailingPathDelimiter(FSSHToolsPath),FUser,
FPassword,FRemoteHost]));
WriteLn(fBatch,'echo.');
WriteLn(fBatch,'echo If SSH Connectivity is working, you should see a dir listing.');
WriteLn(fBatch,'echo.');
WriteLn(fBatch,'pause');
CloseFile(fBatch);
WinMisc.Execute(sBatch,iExit,'','',true);
sleep(100);
DeleteFile(sBatch);
end;
procedure TPuttySSH.Execute(AResults : TStrings);
var oResults : TStringList;
sCommand : string;
begin
_CheckFile('PLINK.EXE');
sCommand := '"' + IncludeTrailingPathDelimiter(FSSHToolsPath) + 'PLINK"';
if FUser '' then sCommand := sCommand + ' -l ' + Fuser;
if FPassword '' then sCommand := sCommand + ' -pw ' + FPassword;
if FPrivateKeyFile '' then sCommand := sCommand + ' -i "' + FPrivateKeyFile + '"';
if FPort 22 then sCommand := sCommand + ' -P ' + IntTostr(FPort);
case FprotocolVersion of
pvSSH1 : sCommand := sCommand + ' -1';
pvSSH2 : sCommand := sCommand + ' -2';
end;
sCommand := sCommand + ' -ssh -batch ' + FRemoteHost + ' "' + FExecCommand + '"';
Screen.Cursor := crHourGlass;
if Assigned(AResults) then
WinMisc.ExecConsolePipe(sCommand,AResults)
else begin
oResults := tStringList.Create;
WinMisc.ExecConsolePipe(sCommand,oResults);
FreeAndNil(oResults);
end;
Screen.Cursor := crDefault;
end;
{$ENDREGION}
end.