unit clientconnection; {< Contains objects for mediating between clients connect to the proxy and the proxy itself.} { This file is part of AMIProxyPal. AMIProxyPal is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Foobar is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with Foobar. If not, see . } {$mode objfpc}{$H+} interface uses Classes, SysUtils, tiObject, SyncObjs, ami_client_interfaces, blcksock, synsock, client_decorators ; type // ----------------------------------------------------------------- // Object classes // ----------------------------------------------------------------- // forward declarations {: Queue for requests to AMI server (from proxy clients) and responses/events to proxy clients (from ami server that meet filter restructions). } TAstMessageQueue = class(TtiObject) private FIncoming: TStringList; FOutgoing: TStringList; FCritical: TCriticalSection; public // ---> methods procedure Lock; virtual; procedure Unlock; virtual; // ---> properties property Incoming: TStringList read FIncoming; property Outgoing: TStringList read FOutgoing; // ---> construction ahead constructor Create; destructor Destroy; override; end; // forward declaration TClientConnectionList = class; {: Represents a base client connection } TClientConnection = class(TtiObject, IAMIClient) private FClientID: string; FRestrictByID: boolean; FRestrictChannel: string; {: Connect object to client. } FSock: TBlockSocket; {: Queue to hold incoming and outgoing messages. } FQueue: TAstMessageQueue; {: Critical section to provide locking of the object. } FCritical: TCriticalSection; {: Filter List } FFilter: TStringList; {: Permission list. } FPermissions: TStringList; {: TProxyDecorator to use. } FDecorator: TProxyDecoratorAbs; FTerminated: boolean; procedure SetClientID( const AValue: string) ; procedure SetRestrictByID( const AValue: boolean) ; procedure SetRestrictChannel( const AValue: string) ; procedure SetTerminated( const AValue: boolean) ; protected {: Determines if any passed in permissions match a list of permisions. } function CheckPermissions(APermsToCheck: string): boolean; public // ---> Implements IAMIClient procedure AddOutgoing(APacket: string); function GetIncoming: string; procedure Lock; virtual; procedure Unlock; virtual; function AcceptEvent(const APacket, AEvent, APerms: string): boolean; function GetOutgoing:string; procedure AddIncoming(APacket: string); // ---> properties {: Public access to Queue } property Queue: TAstMessageQueue read FQueue; {: Indicates if Connection should terminate. } property Terminated: boolean read FTerminated write SetTerminated; {: Indicates if Client is only interested in events that have its ID contained in the packet. } property RestrictByID: boolean read FRestrictByID write SetRestrictByID; {: If not empty, the channel to restrict events by. } property RestrictChannel: string read FRestrictChannel write SetRestrictChannel; // ---> construction ahead constructor Create(ASock: TSocket; ADecorator: TProxyDecoratorAbs; const APerms: string); destructor Destroy; override; published {: ClientID is equal to the thread's ID. } property ClientID: string read FClientID write SetClientID; end; {: ObjectList of @link(TClientConnection) objects. } TClientConnectionList = class(TPerObjThreadList) protected function GetItems(i: integer): TClientConnection; reintroduce; procedure SetItems(i: integer; const AValue: TClientConnection); reintroduce; public property Items[i:integer] : TClientConnection read GetItems write SetItems; procedure Add(AObject : TClientConnection); reintroduce; end; // ----------------------------------------------------------------- // Singleton access to global client list // ----------------------------------------------------------------- implementation uses astevent_utils ; { TAstMessageQueue } procedure TAstMessageQueue.Lock; begin FCritical.Enter; end; procedure TAstMessageQueue.Unlock; begin FCritical.Leave; end; constructor TAstMessageQueue.Create; begin inherited Create; FOutgoing := TStringList.create; FIncoming := TStringList.create; FCritical := TCriticalSection.Create; end; destructor TAstMessageQueue.Destroy; begin FreeAndNil(FOutgoing); FreeAndNil(FIncoming); FreeAndNIl(FCritical); inherited Destroy; end; { TClientConnection } procedure TClientConnection.SetClientID( const AValue: string) ; begin if FClientID= AValue then exit; FClientID:= AValue; end; procedure TClientConnection.SetRestrictByID( const AValue: boolean) ; begin if FRestrictByID= AValue then exit; FRestrictByID:= AValue; end; procedure TClientConnection.SetRestrictChannel( const AValue: string) ; begin if FRestrictChannel= AValue then exit; FRestrictChannel:= AValue; end; procedure TClientConnection.SetTerminated( const AValue: boolean) ; begin if FTerminated= AValue then exit; FTerminated:= AValue; end; function TClientConnection.CheckPermissions( APermsToCheck: string) : boolean; var SLCheck: TStringList; iCheck: integer; sPerm: string; sTemp: string; begin result := false; SLCheck := TStringList.create; try // setup APermsToCheck := LowerCase(APermsToCheck); SLCheck.Delimiter := ','; SLCheck.DelimitedText := APermsToCheck; sTemp := FPermissions.Text; // loop through and see if any single item matches for iCheck := 0 to SLCheck.Count - 1 do begin sPerm := slCheck[iCheck]; if FPermissions.IndexOf(sPerm) >= 0 then begin result := true; exit; end; end; finally SLCheck.free; end; end; procedure TClientConnection.AddOutgoing( APacket: string) ; begin FQueue.Outgoing.Add(FDecorator.Decorate(APacket)); end; function TClientConnection.GetIncoming: string; begin result := ''; if FQueue.Incoming.Count > 0 then begin result := FQueue.Incoming[0]; FQueue.Incoming.Delete(0); end; end; procedure TClientConnection.Lock; begin FCritical.Enter; end; procedure TClientConnection.Unlock; begin FCritical.Leave; end; function TClientConnection.AcceptEvent( const APacket, AEvent, APerms: string) : boolean; begin //result := (FFilter.IndexOf(AEvent) > 0) and //(CheckPermissions(APerms)); if (FRestrictByID) and (FRestrictChannel<> '') then result := (POS(ClientID, APacket) > 0) OR (POS(FRestrictChannel, APacket) > 0) else if FRestrictByID then result := (POS(ClientID, APacket) > 0) else if FRestrictChannel <> '' then result := POS(FRestrictChannel, APacket) > 0 else result := (CheckPermissions(APerms)) or (APerms = ''); end; function TClientConnection.GetOutgoing: string; begin result := ''; if FQueue.Outgoing.Count > 0 then begin result := FQueue.Outgoing[0]; FQueue.Outgoing.Delete(0); end; end; procedure TClientConnection.AddIncoming( APacket: string) ; begin TAstPacketUtil.SetActionID(APacket, FClientID); FQueue.Incoming.Add(FDecorator.UnDecorate(Trim(APacket))); end; constructor TClientConnection.Create(ASock: TSocket; ADecorator: TProxyDecoratorAbs; const APerms: string) ; begin FTerminated := false; FQueue := TAstMessageQueue.Create; FCritical := TCriticalSection.Create; FFilter := TStringList.Create; FDecorator := ADecorator; FPermissions := TStringList.create; FPermissions.Delimiter := ','; FPermissions.DelimitedText := APerms; FSock := TBlockSocket.Create; FSock.Socket := ASock; FSock.GetSins; // setup client ID FClientID := FSock.GetRemoteSinIP + IntToStr(FSock.GetRemoteSinPort); end; destructor TClientConnection.Destroy; begin FreeAndNil(FQueue); FreeAndNil(FCritical); FreeAndNil(FFilter); FreeAndNil(FPermissions); FreeAndNil(FSock); FreeAndNil(FDecorator); inherited Destroy; end; { TClientConnectionList } function TClientConnectionList.GetItems( i: integer) : TClientConnection; begin result := TClientConnection(inherited GetItems(i)); end; procedure TClientConnectionList.SetItems( i: integer; const AValue: TClientConnection) ; begin inherited SetItems(i, AValue); end; procedure TClientConnectionList.Add( AObject: TClientConnection) ; begin inherited Add(AObject); end; end.