{*********************************************************

 SlavaNap source code.

 Copyright 2001,2002 by SlavaNap development team
 Released under GNU General Public License

 Latest version is available at
 http://www.slavanap.org

**********************************************************

 Unit: Users

 class for local and remote users

*********************************************************}
unit Users;

interface

uses
  Windows, Classes2, SysUtils, Constants, STypes, BlckSock, SynSock, WinSock,
    Share, Servers;

type
  TOnlineUser = record
    UserName: string;
    Password: string;
    Software: string;
    Level: TNapUserLevel;
    Ip: Cardinal;
    DataPort: Word;
    NameCrc: Word;
    Total_Up, Total_Down: Word;
    Uploads, Downloads, Max_Up, Queue: SmallInt;
    Speed: TNapSpeed;
    Shared: Word;
    Local: Pointer; // Pointer to local user or nil
    Server: TServer;
    State: TUserState;
    Last_Seen_T: Time_T;
    ULRequests: SmallInt;
    DLRequests: SmallInt;
    SearchReqs: Integer;
    BlockFiles: Integer;
  end;
  POnlineUser = ^TOnlineUser;
  TOnlineUsers = class(TObject)
    Names: array[0..USERS_NAME_ARRAY - 1] of TMyList;
    Ips: array[0..USERS_IP_ARRAY - 1] of TMyList;
    constructor Create;
    destructor Destroy; override;
    procedure Clear;
    function FindUser(Nick: string): POnlineUser;
    function AddUser(Value: TOnlineUser): POnlineUser;
    procedure DeleteUser(Nick: string);
    procedure DeleteRecord(User: POnlineUser);
    function CountClones(Ip: Cardinal; Include_Mods: Boolean): Integer;
    function GetClones(Ip: Cardinal; List: TMyList): Integer;
    function CountItems: Integer;
  end;

procedure ResetOnlineRec(var Rec: TOnlineUser);
function GetCharIndex(Nick: string): Integer;

implementation

uses
  Vars, LocalUsers;

function CreateUser: POnlineUser;
begin
  Result := AllocMem(SizeOf(TOnlineUser));
  with Result^ do
  begin
    Pointer(UserName) := nil;
    Pointer(Password) := nil;
    Pointer(Software) := nil;
  end;
end;

procedure FreeUser(User: POnlineUser);
begin
  if User = nil then Exit;
  User^.UserName := '';
  User^.Password := '';
  User^.Software := '';
  Finalize(User^);
  FreeMem(User, SizeOf(TOnlineUser));
end;

procedure ResetOnlineRec(var Rec: TOnlineUser);
begin
  with Rec do
  begin
    UserName := '';
    NameCrc := 0;
    Password := '';
    Software := '';
    Level := napUserUser;
    Ip := 0;
    DataPort := 0;
    Uploads := 0;
    Downloads := 0;
    Total_Up := 0;
    Total_Down := 0;
    Max_Up := -1;
    Queue := -1;
    Speed := napSpeedUnknown;
    Shared := 0;
    Server := nil;
    Local := nil;
    State := [];
    Last_Seen_T := Current_Time_T;
    ULRequests := 0;
    DLRequests := 0;
    SearchReqs := 0;
    BlockFiles := 0;
  end;
end;

function GetCharIndex2(C: Char): Integer;
begin
  case C of
    'a'..'z': Result := Ord(C) - Ord('a'); // 0..25
    'A'..'Z': Result := Ord(C) - Ord('A'); // 0..25
    '0'..'9': Result := Ord(C) - Ord('0') + 26; // 26..35
    '_': Result := 36;
    '[': Result := 37;
    ']': Result := 38;
    '{': Result := 39;
    '}': Result := 40;
    '-': Result := 41;
    '@': Result := 42;
    '^': Result := 43;
    '!': Result := 44;
    '$': Result := 45;
  else
    Result := 46; // -1;
  end;
end;

function GetCharIndex(Nick: string): Integer;
var // Returns array index for user names
  I, J: Integer;
begin
  Result := -1;
  if Length(Nick) < 2 then Exit;
  I := GetCharIndex2(Nick[1]);
  if I = -1 then Exit;
  J := GetCharIndex2(Nick[2]);
  if J = -1 then Exit;
  Result := USERS_INDEX_INC * I + J;
end;

constructor TOnlineUsers.Create;
var
  I: Integer;
begin
  inherited Create;
  for I := 0 to USERS_NAME_ARRAY - 1 do
    Names[I] := TMyList.Create;
  for I := 0 to USERS_IP_ARRAY - 1 do
    Ips[I] := TMyList.Create;
end;

destructor TOnlineUsers.Destroy;
var
  I: Integer;
begin
  Clear;
  for I := 0 to USERS_NAME_ARRAY - 1 do
    Names[I].Free;
  for I := 0 to USERS_IP_ARRAY - 1 do
    Ips[I].Free;
  inherited Destroy;
end;

procedure TOnlineUsers.Clear;
var
  I, J: Integer;
  Usr: POnlineUser;
begin
  for I := 0 to USERS_NAME_ARRAY - 1 do
  try
    for J := 0 to Names[I].Count - 1 do
    begin
      Usr := Names[I].Items[J];
      if (Usr.Local <> nil) and Running then
        TLocalUser(Usr.Local).Data := nil;
      FreeUser(Usr);
    end;
    Names[I].Clear;
  except
  end;
  for I := 0 to USERS_IP_ARRAY - 1 do
    Ips[I].Clear;
end;

function TOnlineUsers.AddUser(Value: TOnlineUser): POnlineUser;
var
  Index: Integer;
begin
  Result := CreateUser;
  with Result^ do
  begin
    UserName := Value.UserName;
    NameCrc := Value.NameCrc;
    Password := Value.Password;
    Software := Value.Software;
    Level := Value.Level;
    Ip := Value.Ip;
    DataPort := Value.DataPort;
    Uploads := Value.Uploads;
    Downloads := Value.Downloads;
    Total_Up := Value.Total_Up;
    Total_Down := Value.Total_Down;
    Max_Up := Value.Max_Up;
    Queue := Value.Queue;
    Speed := Value.Speed;
    Shared := Value.Shared;
    Server := Value.Server;
    Local := Value.Local;
    State := Value.State;
    Last_Seen_T := Value.Last_Seen_T;
    ULRequests := Value.ULRequests;
    DLRequests := Value.DLRequests;
    SearchReqs := Value.SearchReqs;
    BlockFiles := Value.BlockFiles; // ブロックワードによるブロックのみカウント
  end;
  Index := GetCharIndex(Result^.UserName);
  Names[Index].Add(Result);
  Index := Index_Ip(Result^.Ip);
  Ips[Index].Add(Result);
end;

function TOnlineUsers.FindUser(Nick: string): POnlineUser;
var
  I, Crc, Index: Integer;
  Usr: POnlineUser;
begin
  Result := nil;
  Crc := StringCRC(Nick, True);
  Index := GetCharIndex(Nick);
  if Index = -1 then Exit;
  Nick := AnsiLowerCase(Nick);
  for I := 0 to Names[Index].Count - 1 do
    if POnlineUser(Names[Index].Items[I])^.NameCrc = Crc then
      if AnsiLowerCase(POnlineUser(Names[Index].Items[I])^.UserName) = Nick then
      begin
        Result := Names[Index].Items[I];
        Exit;
      end;
end;

procedure TOnlineUsers.DeleteUser(Nick: string);
begin
  DeleteRecord(FindUser(Nick));
end;

procedure TOnlineUsers.DeleteRecord(User: POnlineUser);
var
  I, Index: Integer;
  Found: Boolean;
begin
  if User = nil then Exit;
  Index := Index_Ip(User^.Ip);
  Found := False;
  for I := Ips[Index].Count - 1 downto 0 do
    if not Found then
      if Ips[Index].Items[I] = User then
      begin
        Ips[Index].Delete(I);
        Found := True;
      end;
  Index := GetCharIndex(User^.UserName);
  Found := False;
  for I := Names[Index].Count - 1 downto 0 do
    if not Found then
      if Names[Index].Items[I] = User then
      begin
        Names[Index].Delete(I);
        Found := True;
      end;
  if User^.Local <> nil then
    TLocalUser(User^.Local).Data := nil;
  User^.UserName := '';
  User^.Password := '';
  User^.Software := '';
  FreeMem(User, SizeOf(TOnlineUser));
end;

function TOnlineUsers.CountClones(Ip: Cardinal; Include_Mods: Boolean): Integer;
var
  I, Index: Integer;
begin
  Index := Index_Ip(Ip);
  Result := 0;
  for I := 0 to Ips[Index].Count - 1 do
    if POnlineUser(Ips[Index].Items[I])^.Ip = Ip then
      if Include_Mods or (POnlineUser(Ips[Index].Items[I])^.Level <
        napUserModerator) then
        Inc(Result);
end;

function TOnlineUsers.GetClones(Ip: Cardinal; List: TMyList): Integer;
var
  I, Index: Integer;
begin
  List.Clear;
  Index := Index_Ip(Ip);
  Result := 0;
  for I := 0 to Ips[Index].Count - 1 do
    if POnlineUser(Ips[Index].Items[I])^.Ip = Ip then
    begin
      Inc(Result);
      List.Add(Ips[Index].Items[I]);
    end;
end;

function TOnlineUsers.CountItems: Integer;
var
  I: Integer;
begin
  Result := 0;
  for I := 0 to USERS_NAME_ARRAY - 1 do
    Inc(Result, Names[I].Count);
end;

end.
