serverthread.pas

来自「著名的SecureBlackBox控件完整源码」· PAS 代码 · 共 1,275 行 · 第 1/3 页

PAS
1,275
字号
unit ServerThread;

interface

uses SysUtils,
  Winsock, ScktComp, SBSSHServer, SBSSHKeyStorage, SBSftpHandler, Classes,
  Windows, SBSSHCommon, SBUtils, SBSftpServer, SBSftpCommon;

type
  TSSHLogEvent = procedure(Sender: TObject; const S: string) of object;
  TSSHServerState = (ssConnecting, ssSecuring, ssAuthenticating, ssActive,
    ssDisconnected);

  TSSHServerThread = class(TServerClientThread)
  private
    FSSHServer: TElSSHServer;
    FHostKeys: TElSSHMemoryKeyStorage;
    FOnLog: TSSHLogEvent;
    FOnAuthAttempt: TSSHAuthAttemptEvent;
    FOnAuthPassword: TSSHAuthPasswordEvent;
    FOnAuthPublicKey: TSSHAuthPublicKeyEvent;
    FOnFurtherAuthNeeded: TSSHFurtherAuthNeededEvent;
    // note that the approach to keyboard-interactive user authentication used in the demo
    // is chosen only as an example. You can ask as many questions as needed to authorize the user.
    FOnAuthKeyboard : TSSHAuthPasswordEvent;
    FOnConnClosed: TNotifyEvent;
    FOnRefreshInfo: TNotifyEvent;
    FCurrLogLine: string;
    FCurrUserName: string;
    FCurrAuthType: integer;
    FCurrPassword: string;
    FCurrBool: boolean;
    FCurrKey : TElSSHKey;
    FUsername: string;
    FSubsystemThreads: TList;
    FServerForwardings: TList;
    FState: TSSHServerState;
    FCurrentDir: widestring;
    SftpVersion: integer;
    procedure SetupServer;
    procedure SetupHostKeys;
    procedure OnSrvAuthAttempt(Sender: TObject; const Username: string;
      AuthType: integer; var Accept: boolean);
    procedure OnSrvAuthFailed(Sender: TObject; AuthenticationType: integer);
    procedure OnSrvAuthPublicKey(Sender: TObject; const Username: string;
      Key: TElSSHKey; var Accept: boolean);
    procedure OnSrvAuthPassword(Sender: TObject; const Username: string;
      const Password: string; var Accept: boolean; var ForceChangePassword: boolean);
    procedure OnSrvAuthKeyboard(Sender: TObject; const Username: string;
      Submethods: TStringList; var Name: string; var Instruction: string;
      Requests: TStringList; Echoes: TBits);
    procedure OnSrvAuthKeyboardResponse(Sender: TObject; Requests: TStringList;
      Responses: TStringList; var Name: string; var Instruction: string;
      NewRequests: TStringList; Echoes: TBits; var Accept: boolean);
    procedure OnSrvSend(Sender: TObject; Buffer: pointer; Size: longint);
    procedure OnSrvReceive(Sender: TObject; Buffer: pointer; MaxSize: longint; out Written: longint);
    procedure OnSrvOpen(Sender: TObject);
    procedure OnSrvClose(Sender: TObject);
    procedure OnSrvError(Sender: TObject; ErrorCode: integer);
    procedure OnSrvOpenSubsystem(Sender: TObject; Conn: TElSSHTunnelConnection; const Subsystem: string);
    procedure DoLog;
    procedure DoAuthAttempt;
    procedure DoAuthPassword;
    procedure DoAuthPublicKey;
    procedure DoAuthKeyboard;
    procedure DoConnClosed;
    procedure DoRefreshInfo;
    procedure DoFurtherAuthNeeded;
    procedure Log(const S: string);
    procedure ChangeState(NewState: TSSHServerState);
    function GetTunnelCount: integer;
    function FileInfoFromRec(r: WIN32_FIND_DATA): TElSftpFileInfo; virtual;
    function StatFile(name: string): TElSftpFileInfo; virtual;
    function StatOpenFile(Data : pointer): TElSftpFileInfo; virtual;
    procedure OnSftpOpen(Sender: TObject);
    procedure OnSftpCloseHandle(Sender: TObject; Data : pointer;
      var ErrorCode: integer; var Comment: string);
    procedure OnSftpCreateDirectory(Sender: TObject; const Path: string;
      Attributes: TElSftpFileAttributes; var ErrorCode: integer; var Comment: string);
    procedure OnSftpFindClose(Sender: TObject; Data : pointer;
      var ErrorCode: integer; var Comment: string);
    procedure OnSftpFindFirst(Sender: TObject; const Path: string;
      var Data: pointer; Info: TElSftpFileInfo; var ErrorCode: integer;
      var Comment: string);
    procedure OnSftpFindNext(Sender: TObject; Data: pointer; Info: TElSftpFileInfo;
      var ErrorCode: integer; var Comment: string);
    procedure OnSftpOpenFile(Sender: TObject; const Path: string;
      Modes: TSBSftpFileOpenModes; Access: TSBSftpFileOpenAccess;
      DesiredAccess : Cardinal;
      Attributes: TElSftpFileAttributes; var Data: pointer; var ErrorCode: integer;
      var Comment: string);
    procedure OnSftpReadFile(Sender: TObject; Data: pointer; Offset: Int64;
      Buffer: Pointer; Count: integer; var Read: integer; var ErrorCode: integer;
      var Comment: string);
    procedure OnSftpRemove(Sender: TObject; const Path: string;
      var ErrorCode: integer; var Comment: string);
    procedure OnSftpRenameFile(Sender: TObject; const OldPath, NewPath: string;
      Flags : TSBSftpRenameFlags; var ErrorCode: integer; var Comment: string);
    procedure OnSftpRequestAbsolutePath(Sender: TObject; const Path: string;
      var AbsolutePath: string; Control : TSBSftpRealpathControl;
      ComposePath: TStringList; var ErrorCode: integer; var Comment: string);
    procedure OnSftpRequestAttributes(Sender: TObject; const Path: string;
      FollowSymLinks: boolean; Attributes: TElSftpFileAttributes;
      var ErrorCode: integer; var Comment: string);
    procedure OnSftpRequestAttributes2(Sender: TObject;
      Data : pointer; Attributes: TElSftpFileAttributes; var ErrorCode: integer;
      var Comment: string);
    procedure OnSftpWriteFile(Sender: TObject; Data : pointer; Offset: Int64;
      Buffer: Pointer; Count: integer; var ErrorCode: integer; var Comment: string);
    procedure OnSrvFurtherAuthNeeded(Sender: TObject; const Username: string; var
        Needed: boolean);
  protected
    FAuthAllTypes: integer;
    FAuthAllUsername: string;
    procedure ClientExecute; override;
    procedure Execute; override;
  public
    constructor Create(ASocket: TServerClientWinSocket);
    destructor Destroy; override;
    property AuthAllTypes: integer read FAuthAllTypes write FAuthAllTypes;
    property AuthAllUsername: string read FAuthAllUsername write FAuthAllUsername;
    property OnLog: TSSHLogEvent read FOnLog write FOnLog;
    property OnAuthAttempt: TSSHAuthAttemptEvent read FOnAuthAttempt write FOnAuthAttempt;
    property OnAuthPassword: TSSHAuthPasswordEvent read FOnAuthPassword write FOnAuthPassword;
    property OnAuthKeyboard : TSSHAuthPasswordEvent read FOnAuthKeyboard write FOnAuthKeyboard;
    property OnConnClosed: TNotifyEvent read FOnConnClosed write FOnConnClosed;
    property OnAuthPublicKey: TSSHAuthPublicKeyEvent read FOnAuthPublicKey write
        FOnAuthPublicKey;
    property OnFurtherAuthNeeded: TSSHFurtherAuthNeededEvent read
        FOnFurtherAuthNeeded write FOnFurtherAuthNeeded;
    property OnRefreshInfo: TNotifyEvent read FOnRefreshInfo write FOnRefreshInfo;
    property Username: string read FUsername;
    property State: TSSHServerState read FState;
    property TunnelCount: integer read GetTunnelCount;
  end;

implementation

uses
  SBSSHConstants, DemoSettings, SBSSHHandlers,
  SBSSHForwardingHandlers;

type
  PSearchRec = ^TSearchRec;

{ JPM modifications }
//file attrib constants for Win32 - some are here only for completeness
//Others are here because they may not be defined in Windows.pas
const
  FILE_ATTRIBUTE_READONLY             = $00000001;
  FILE_ATTRIBUTE_HIDDEN               = $00000002;
  FILE_ATTRIBUTE_SYSTEM               = $00000004;
  FILE_ATTRIBUTE_DIRECTORY            = $00000010;
  FILE_ATTRIBUTE_ARCHIVE              = $00000020;
  FILE_ATTRIBUTE_DEVICE               = $00000040;
  FILE_ATTRIBUTE_NORMAL               = $00000080;
  FILE_ATTRIBUTE_TEMPORARY            = $00000100;
  FILE_ATTRIBUTE_SPARSE_FILE          = $00000200;
  FILE_ATTRIBUTE_REPARSE_POINT        = $00000400;
  FILE_ATTRIBUTE_COMPRESSED           = $00000800;
  FILE_ATTRIBUTE_OFFLINE              = $00001000;
  FILE_ATTRIBUTE_NOT_CONTENT_INDEXED  = $00002000;
  FILE_ATTRIBUTE_ENCRYPTED            = $00004000;

// Unfortunately, Win32 attributes do not map cleanly to SFTP v. 5 so
// we have to make some modifications
function Win32AttrToSFTPAttr(const AWinAttr : Cardinal) : Cardinal;
begin
  Result := 0;
  if AWinAttr and FILE_ATTRIBUTE_READONLY > 0 then
  begin
    Result := Result or SSH_FILEXFER_ATTR_FLAGS_READONLY;
  end;
  if AWinAttr and FILE_ATTRIBUTE_SYSTEM > 0 then
  begin
    Result := Result or  SSH_FILEXFER_ATTR_FLAGS_SYSTEM;
  end;
  if AWinAttr and FILE_ATTRIBUTE_HIDDEN > 0 then
  begin
    Result := Result or  SSH_FILEXFER_ATTR_FLAGS_HIDDEN;
  end;
  //I think that Windows NT often is case-insensitive, unlike Linux
  Result := Result or SSH_FILEXFER_ATTR_FLAGS_CASE_INSENSITIVE;
  if AWinAttr and FILE_ATTRIBUTE_ARCHIVE > 0 then
  begin
    Result := Result or SSH_FILEXFER_ATTR_FLAGS_ARCHIVE;
  end;
  if AWinAttr and FILE_ATTRIBUTE_ENCRYPTED > 0 then
  begin
    Result := Result or SSH_FILEXFER_ATTR_FLAGS_ENCRYPTED ;
  end;
  if AWinAttr and FILE_ATTRIBUTE_COMPRESSED > 0 then
  begin
    Result := Result or SSH_FILEXFER_ATTR_FLAGS_COMPRESSED;
  end;
  if AWinAttr and FILE_ATTRIBUTE_SPARSE_FILE > 0 then
  begin
    Result := Result or SSH_FILEXFER_ATTR_FLAGS_SPARSE;
  end;
end;

function FileTimeToTDateTime(const AFileTime: TFileTime): TDateTime;
var
  LDosTime : LongInt;
begin
  if Windows.FileTimeToDosDateTime(AFileTime, LongRec(LDosTime).Hi,
      LongRec(LDosTime).Lo) then
    Result := SysUtils.FileDateToDateTime(LDosTime)
  else
  begin
    Result := 0;
  end;
end;

{ JPM: Addition, it turns out that LongName is being parsed by some programs
such as FTP Voyager.  In this case, it's best to give standard Unix formated
file listings in the longname. }
function IsIn6MonthWindow(const AMDate : TDateTime):Boolean;
//based on http://www.opengroup.org/onlinepubs/007908799/xbd/utilconv.html#usg
//For dates, we display the time only if the date is within 6 monthes of the current
//date.  Otherwise, we send the year.
var
  LCurMonth, LCurDay, LCurYear : Word;  //Now
  LPMonth,  LPYear : Word;
  LMMonth, LMDay, LMYear : Word;
begin
  DecodeDate(Now,LCurYear,LCurMonth,LCurDay);
  DecodeDate(AMDate,LMYear,LMMonth,LMDay);
  if (LCurMonth - 6) < 1 then
  begin
    LPMonth :=  12 + (LCurMonth - 6);
    LPYear := LCurYear - 1;
  end
  else
  begin
    LPMonth := LCurMonth - 6;
    LPYear := LCurYear;
  end;
  if LMYear < LPYear then
  begin
    Result := False;
    Exit;
  end;
  if LMYear = LPYear then
  begin
    Result := (LMMonth >= LPMonth);
    if Result and (LMMonth = LPMonth) then
    begin
      Result := (LMDay >= LCurDay);
      Exit;
    end;
  end
  else
  begin
    Result := True;
  end;
end;

function TwoDigits(const AValue : Word) : String;
begin
  if AValue >9 then
  begin
    Result := IntToStr(AValue);
  end
  else
  begin
    Result := '0'+IntToStr(AValue);
  end;
end;

function UnixListing(ARec : WIN32_FIND_DATA) : String;
var
   LHour, LMin, LSec, LMSec : Word;
      LMonth, LDay, LYear : Word;
   LFileTime : TDateTime;
const
  LMONTHS : array [1..12] of string =
    ('Jan','Feb','Mar',
     'Apr','May','Jun',
     'Jul','Aug','Sep',
     'Oct','Nov','Dec');

begin
  Result := '';
  if (ARec.dwFileAttributes and FILE_ATTRIBUTE_DIRECTORY) <> 0 then
  begin
    //Note that Windows has a slightly different concept of user and ownership.
    //
    //There is a file owner but you can't report it properly in the unix format
    //because Win32 permits spaces in account names while Unix does not.
    //Reporting a username or group name with a space could throw off a Unix list
    //parser.
    if (ARec.dwFileAttributes and FILE_ATTRIBUTE_READONLY) = 0 then
    begin
      Result := 'dr-xr-xr-x   1 user     group    ';
    end
    else
    begin
      Result := 'drwxrwxrwx   1 user     group    ';
   end;
  end
  else
  begin
    Result := '-';
    if (ARec.dwFileAttributes and FILE_ATTRIBUTE_READONLY) = 0 then
    begin
      Result := '-r--r--r--   1 user     group    ';
    end
    else
    begin
      Result := '-rw-rw-rw-   1 user     group    ';
    end;
  end;
  Result := Result + Format('%8d ',
    [Int64(ARec.nFileSizeHigh shl 32) + ARec.nFileSizeLow ]);
  LFileTime := FileTimeToTDateTime(ARec.ftLastWriteTime);
  DecodeDate (LFileTime,LYear,LMonth,LDay);
  Result := Result + LMONTHS[LMonth] + Format(' %2d ',[LDay]);
  if IsIn6MonthWindow(LFileTime) then
  begin
    DecodeTime( LFileTime, LHour,LMin,LSec,LMSec);
    Result := Result + Format('%2s:%2s ',[TwoDigits(LHour),TwoDigits(LMin)]);
  end
  else
    Result := Result + ' '+IntToStr(LYear)+ ' ';
  Result := Result + string(ARec.cFileName);
end;
{ end JPM modifications }

function AuthTypeToStr(AuthType: integer): string;
begin
  case AuthType of
    SSH_AUTH_TYPE_RHOSTS: Result := 'Rhosts';
    SSH_AUTH_TYPE_PUBLICKEY: Result := 'PublicKey';
    SSH_AUTH_TYPE_PASSWORD: Result := 'Password';
    SSH_AUTH_TYPE_HOSTBASED: Result := 'Hostbased';
    SSH_AUTH_TYPE_KEYBOARD: Result := 'Keyboard-interactive';
  else
    Result := 'unknown'
  end;
end;

function SizeOfFile(const n: string): int64;
var
  f: TFileStream;
begin
  try
    f := TFileStream.Create(n, fmOpenRead);
    try
      Result := f.Size;
    finally
      f.Free;
    end;
  except
    result := -1;
  end;
end;

function replace(var Str: string; const SourceString, DestString: string): boolean;
var
  i: integer;
begin
  i := pos(SourceString, Str);
  if i = 0 then
  begin
    Result := false;
    exit;
  end;
  Delete(Str, i, Length(SourceString));
  if Length(DestString)>0 then
    Insert(DestString, Str, i);
  Result := true;
end;

function rpos(c: widechar; const s: widestring): integer;
var
  i: integer;
begin
  result := -1;
  i := Length(s);
  while i > 0 do
    if s[i] = c then
    begin
      result := i;
      break;
    end
    else
      dec(i);
end;

function UpDir(const p: widestring): widestring;
var
  i: integer;
begin
  i := rpos('/', p);
  if i < Length(p) then
    result := copy(p, 1, i - 1)
  else
    result := p;
end;

function ResolveDots(p: widestring): widestring;
var
  d, us, hu: widestring;
  i: integer;
begin
  d := '';
  if (Length(p) > 1) and (p[2] = ':') then
  begin
    d := copy(p, 1, 2);
    delete(p, 1, 2);
  end;
  i := pos('/', p);
  us := '';
  while true do
  begin
    while i = 1 do
    begin
      delete(p, 1, 1);
      i := pos('/', p);
    end;
    if p = '' then
      break;
    if i > 0 then
    begin

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?