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

 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.

