{ $HDR$}
{**********************************************************************}
{ Unit archived using Team Coherence                                   }
{ Team Coherence is Copyright 2002 by Quality Software Components      }
{                                                                      }
{ For further information / comments, visit our WEB site at            }
{ http://www.TeamCoherence.com                                         }
{**********************************************************************}
{}
{ $Log:  11986: IdStack.pas
{
{   Rev 1.28    10/21/2003 9:24:32 PM  BGooijen
{ Started on SendTo, ReceiveFrom
}
{
{   Rev 1.27    10/19/2003 5:21:28 PM  BGooijen
{ SetSocketOption
}
{
    Rev 1.26    10/15/2003 7:21:02 PM  DSiders
  Added resource strings in TIdStack.Make.
}
{
{   Rev 1.25    2003.10.11 5:51:02 PM  czhower
{ -VCL fixes for servers
{ -Chain suport for servers (Super core)
{ -Scheduler upgrades
{ -Full yarn support
}
{
{   Rev 1.24    10/5/2003 9:55:30 PM  BGooijen
{ TIdTCPServer works on D7 and DotNet now
}
{
{   Rev 1.23    04/10/2003 22:31:56  HHariri
{ moving of WSNXXX method to IdStack and renaming of the DotNet ones
}
{
{   Rev 1.22    10/2/2003 7:31:18 PM  BGooijen
{ .net
}
{
{   Rev 1.21    10/2/2003 6:05:16 PM  GGrieve
{ DontNet
}
{
{   Rev 1.20    2003.10.02 10:16:30 AM  czhower
{ .Net
}
{
{   Rev 1.19    2003.10.01 9:11:20 PM  czhower
{ .Net
}
{
{   Rev 1.18    2003.10.01 5:05:16 PM  czhower
{ .Net
}
{
{   Rev 1.17    2003.10.01 2:30:40 PM  czhower
{ .Net
}
{
{   Rev 1.16    2003.10.01 12:30:08 PM  czhower
{ .Net
}
{
{   Rev 1.14    2003.10.01 1:37:36 AM  czhower
{ .Net
}
{
{   Rev 1.12    9/30/2003 7:15:46 PM  BGooijen
{ IdCompilerDefines.inc is included now
}
{
{   Rev 1.11    2003.09.30 1:23:04 PM  czhower
{ Stack split for DotNet
}
unit IdStack;

{$I IdCompilerDefines.inc}

interface

uses
  Classes,
  IdException, IdStackConsts, IdCoreGlobal,
  SyncObjs;

type
  TIdSocketListClass = class of TIdSocketList;

  // Descend from only TObject. This objects is created a lot and should be fast
  // and small
  TIdSocketList = class(TObject)
  protected
    FLock: TCriticalSection;
    //
    function GetItem(AIndex: Integer): TIdStackSocketHandle; virtual; abstract;
  public
    constructor Create; virtual;
    destructor Destroy; override;
    procedure Add(AHandle: TIdStackSocketHandle); virtual; abstract;
    function Clone: TIdSocketList; virtual; abstract;
    function Count: Integer; virtual; abstract;
    class function CreateSocketList: TIdSocketList;
    property Items[AIndex: Integer]: TIdStackSocketHandle read GetItem; default;
    procedure Remove(AHandle: TIdStackSocketHandle); virtual; abstract;
    procedure Clear; virtual; abstract;
    function Contains(AHandle: TIdStackSocketHandle): boolean; virtual; abstract;
    procedure Lock;
    class function Select(AReadList: TIdSocketList; AWriteList: TIdSocketList;
     AExceptList: TIdSocketList; const ATimeout: Integer = IdTimeoutInfinite): Boolean; virtual;
    function SelectRead(const ATimeout: Integer = IdTimeoutInfinite): Boolean; virtual; abstract;
    function  SelectReadList(var VSocketList: TIdSocketList; const ATimeout: Integer = IdTimeoutInfinite): Boolean; virtual; abstract;
    procedure Unlock;
  end;

  TIdStack = class(TObject)
  protected
    FHostName: string;
    //
    function HostByName(const AHostName: string): string; virtual; abstract;
    function ReadHostName: string; virtual; abstract;
  public
    function Accept(ASocket: TIdStackSocketHandle; var VIP: string;
             var VPort: Integer): TIdStackSocketHandle; virtual; abstract;
    procedure Bind(ASocket: TIdStackSocketHandle; const AIP: string;
              const APort: Integer); virtual; abstract;
    procedure Connect(const ASocket: TIdStackSocketHandle; const AIP: string;
              const APort: TIdPort); virtual; abstract;
    constructor Create; virtual;
    procedure Disconnect(ASocket: TIdStackSocketHandle); virtual; abstract;
    class procedure Make;
    procedure GetPeerName(ASocket: TIdStackSocketHandle; var VIP: string;
              var VPort: Integer); virtual; abstract;
    procedure GetSocketName(ASocket: TIdStackSocketHandle; var VIP: string;
              var VPort: TIdPort); virtual; abstract;
    function HostToNetwork(AValue: Word): Word; overload; virtual; abstract;
    function HostToNetwork(AValue: LongWord): LongWord; overload; virtual;
             abstract;
    function IsIP(AIP: string): Boolean;
    procedure Listen(ASocket: TIdStackSocketHandle; ABackLog: Integer); virtual;
              abstract;
    function NewSocketHandle(const ASocketType:TIdSocketType;
      const AProtocol: TIdSocketProtocol; const AOverlapped: Boolean = False)
      : TIdStackSocketHandle; virtual; abstract;
    function NetworkToHost(AValue: Word): Word; overload; virtual; abstract;
    function NetworkToHost(AValue: LongWord): LongWord; overload; virtual;
             abstract;
             
    procedure SetSocketOption(ASocket: TIdStackSocketHandle; ALevel:TIdSocketOptionLevel; 
      AOptName: TIdSocketOption; AOptVal: Integer); overload;virtual;abstract;
    function ResolveHost(const AHost: string): string;

    // Result:
    // > 0: Number of bytes received
    //   0: Connection closed gracefully
    // Will raise exceptions in other cases
    function Receive(ASocket: TIdStackSocketHandle; const ABuffer: TIdBytes)
             : Integer; virtual; abstract;
    function Send(ASocket: TIdStackSocketHandle; const ABuffer: TIdBytes;
             const AOffset: Integer): Integer; virtual; abstract;

    function ReceiveFrom(ASocket: TIdStackSocketHandle; const ABuffer: TIdBytes;
             var VIP: string; var VPort: Integer): Integer; virtual; abstract;
    function SendTo(ASocket: TIdStackSocketHandle; const ABuffer: TIdBytes;
             const AOffset: Integer; const AIP: string; const APort: integer
             ): Integer; virtual; abstract;

    //
    // Properties
    //
    property HostName: string read FHostname;
  end;

  TIdStackClass = class of TIdStack;

var
  GStack: TIdStack = nil;
  GSocketListClass: TIdSocketListClass;

// Procedures
  function IdStackFactory : TIdStack;

implementation

uses
  {$IFDEF LINUX}     IdStackLinux, {$ENDIF}
  {$IFDEF MSWINDOWS} IdStackWindows, {$ENDIF}
  {$IFDEF DOTNET}    IdStackDotNet, {$ENDIF}
  IdCoreResourceStrings,
  SysUtils;

var
  GStackClass: TIdStackClass = nil;

function IdStackFactory: TIdStack;
begin
  Result := GStackClass.Create;
  // GStackClass used to be public, but this factory has
  // replaced it so that the following line (which once
  // live in AfterConstruction, but this doesn't exist
  // in DotNet) will be run
  Result.FHostName := Result.ReadHostName;
end;

{ TIdSocketList }

constructor TIdSocketList.Create;
begin
  inherited Create;
  FLock := TCriticalSection.Create;
end;

class function TIdSocketList.CreateSocketList: TIdSocketList;
Begin
  Result := GSocketListClass.Create;
End;

destructor TIdSocketList.Destroy;
begin
  FreeAndNil(FLock);
  inherited;
end;

procedure TIdSocketList.Lock;
begin
  FLock.Acquire;
end;

class function TIdSocketList.Select(AReadList, AWriteList,
  AExceptList: TIdSocketList; const ATimeout: Integer): Boolean;
begin
  // C++ Builder cannot have abstract class functions thus we need this base
  Result := False;
end;

procedure TIdSocketList.Unlock;
begin
  FLock.Release;
end;

{ TIdStack }

constructor TIdStack.Create;
begin
  // Here for .net
  inherited;
end;

function TIdStack.IsIP(AIP: string): Boolean;
var
  i: Integer;
begin
  i := StrToIntDef(Fetch(AIP, '.'), 0);    {Do not Localize}
  Result := (i > 0) and (i < 256);
  i := StrToIntDef(Fetch(AIP, '.'), 0);    {Do not Localize}
  Result := Result and ((i > 0) and (i < 256));
  i := StrToIntDef(Fetch(AIP, '.'), 0);    {Do not Localize}
  Result := Result and ((i > 0) and (i < 256));
  i := StrToIntDef(Fetch(AIP, '.'), 0);    {Do not Localize}
  Result := Result and ((i > 0) and (i < 256)) and (AIP = '');
end;

class procedure TIdStack.Make;
begin
  EIdException.IfTrue(GStackClass = nil, RSStackClassUndefined);
  EIdException.IfTrue(GStack <> nil, RSStackAlreadyCreated);
  GStack := IdStackFactory;
end;

function TIdStack.ResolveHost(const AHost: string): string;
begin
  // Sometimes 95 forgets who localhost is
  if AnsiSameText(AHost, 'LOCALHOST') then begin    {Do not Localize}
    Result := '127.0.0.1';    {Do not Localize}
  end else if IsIP(AHost) then begin
    Result := AHost;
  end else begin
    Result := HostByName(AHost);
  end;
end;

initialization
  GStackClass :=
   {$IFDEF LINUX}     TIdStackLinux;   {$ENDIF}
   {$IFDEF MSWINDOWS} TIdStackWindows; {$ENDIF}
   {$IFDEF DOTNET}    TIdStackDotNet;  {$ENDIF}
end.
