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

 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: registered

 Class for registered users

*********************************************************}
unit registered;

interface

{$I defines.pas}

uses
 Windows, Classes2, SysUtils, constants, stypes, blcksock, synsock, winsock, share,
 servers, users;

type
 TRegisteredUser = record
  nick            : String;
  password        : String;
  level           : TNapUserLevel;
  downloads       : Integer;
  uploads         : Integer;
  last_ip         : Integer;
  last_seen       : time_t;
  state           : TUserState;
  created         : time_t;
  createdby       : String;
  lastsetby       : String;
 end;
 PRegisteredUser = ^TRegisteredUser;
 TRegUserList = class(TMyList)
  function  Add(Value: TRegisteredUser):Integer;
  procedure Insert(Index:Integer; Value: TRegisteredUser);
  procedure Clear; override;
  procedure Delete(Index: Integer);
  function  FindUser(nick: String): Integer;
  function  FindUserRec(nick: String): PRegisteredUser;
  function  isConsole(index: Integer): Boolean;
  function  User(index: Integer): TRegisteredUser;
  function  PasswordOk(nick, password: String): Boolean;
  constructor Create;
  destructor Destroy; override;
 end;
 TRegUserDataBase = class(TObject)
  list             : Array[0..USERS_NAME_ARRAY-1] of TRegUserList;
  constructor Create;
  destructor Destroy; override;
  procedure SaveToFile(filename: String);
  procedure LoadFromFile(filename: String);
  function  FindUser(nick: String): PRegisteredUser;
  function  PasswordOk(nick, password: String): Boolean;
  procedure Add(reg: TRegisteredUser);
  procedure Delete(nick: String);
  function  CountUsers: Integer;
 end;

procedure ResetRegistered(var reg: TRegisteredUser);

implementation

uses
 vars, thread, lang, handler, memory_manager;

procedure ResetRegistered(var reg: TRegisteredUser);
begin
 reg.nick:='';
 reg.password:=null_pass;
 reg.level:=napUserUser;
 reg.downloads:=0;
 reg.uploads:=0;
 reg.last_ip:=0;
 reg.last_seen:=0;
 reg.state:=[];
 reg.created:=0;
 reg.createdby:='';
 reg.lastsetby:='';
end;

function  TRegUserList.Add(Value: TRegisteredUser):Integer;
var
 data:PRegisteredUser;
begin
 data:=AllocMem(sizeof(TRegisteredUser));
 with data^ do
 begin
  Pointer(nick):=nil;
  nick:=Value.nick;
  Pointer(password):=nil;
  password:=Value.password;
  level:=Value.level;
  downloads:=Value.downloads;
  uploads:=Value.uploads;
  last_ip:=Value.last_ip;
  last_seen:=Value.last_seen;
  state:=Value.state;
  created:=Value.created;
  createdby:=Value.createdby;
  pointer(lastsetby):=nil;
  lastsetby:=Value.lastsetby;
 end;
 Result:=inherited Add(data);
end;

procedure TRegUserList.Insert(Index:Integer; Value: TRegisteredUser);
var
 data:PRegisteredUser;
begin
 data:=AllocMem(sizeof(TRegisteredUser));
 with data^ do
 begin
  Pointer(nick):=nil;
  nick:=Value.nick;
  Pointer(password):=nil;
  password:=Value.password;
  level:=Value.level;
  downloads:=Value.downloads;
  uploads:=Value.uploads;
  last_ip:=Value.last_ip;
  last_seen:=Value.last_seen;
  state:=Value.state;
  created:=Value.created;
  createdby:=Value.createdby;
  Pointer(lastsetby):=nil;
  lastsetby:=Value.lastsetby;
 end;
 inherited Insert(index,data);
end;

procedure TRegUserList.Clear;
begin
 while count>0 do
  Delete(count-1);
 inherited Clear;
end;

procedure TRegUserList.Delete(Index: Integer);
begin
 if (Index<0) or (Index>=Count) then exit;
 if Items[Index]<>nil then
 begin
  Finalize(PRegisteredUser(Items[Index])^);
  FreeMem(PRegisteredUser(Items[Index]),sizeof(TRegisteredUser));
 end;
 inherited Delete(index);
end;

function  TRegUserList.FindUser(nick: String): Integer;
var
 i: Integer;
begin
 Result:=-1;
 nick:=AnsiLowerCase(nick);
 for i:=0 to count-1 do
  if AnsiLowerCase(PRegisteredUser(Items[i])^.nick)=nick then
  begin
    Result:=i;
    exit;
  end;
end;

function  TRegUserList.FindUserRec(nick: String): PRegisteredUser;
var
 i: Integer;
begin
 Result:=nil;
 nick:=AnsiLowerCase(nick);
 for i:=0 to count-1 do
  if AnsiLowerCase(PRegisteredUser(Items[i])^.nick)=nick then
  begin
    Result:=Items[i];
    exit;
  end;
end;

function  TRegUserList.isConsole(index: Integer): Boolean;
var
 str: String;
begin
 str:=PRegisteredUser(Items[index])^.password;
 Result:=(str=CONSOLE_PASS) or (str=CONSOLE_PASS_OFFLINE);
end;

function TRegUserList.User(index: Integer): TRegisteredUser;
var
 user: TRegisteredUser;
 p: PRegisteredUser;
begin
 p:=Items[index];
 user.nick:=p.nick;
 user.password:=p.password;
 user.level:=p.level;
 user.downloads:=p.downloads;
 user.uploads:=p.uploads;
 user.last_ip:=p.last_ip;
 user.last_seen:=p.last_seen;
 user.state:=p.state;
 user.created:=p.created;
 user.createdby:=p.createdby;
 user.lastsetby:=p.lastsetby;
 Result:=user;
end;

function  TRegUserList.PasswordOk(nick, password: String): Boolean;
var
 i: Integer;
 str: String;
begin
 Result:=true;
 nick:=AnsiLowerCase(nick);
 for i:=0 to count-1 do
  if AnsiLowerCase(PRegisteredUser(Items[i])^.nick)=nick then
  begin
    str:=PRegisteredUser(Items[i])^.password;
    Result:=(str=null_password) or (str=password);
    exit;
  end;
end;

constructor TRegUserList.Create;
begin
 inherited Create;
end;

destructor TRegUserList.Destroy;
begin
 Clear;
 inherited Destroy;
end;

constructor TRegUserDataBase.Create;
var
 i: Integer;
begin
 inherited Create;
 for i:=0 to USERS_NAME_ARRAY-1 do
  list[i]:=TRegUserList.Create;
end;

destructor TRegUserDataBase.Destroy;
var
 i: Integer;
begin
 for i:=0 to USERS_NAME_ARRAY-1 do
  list[i].Free;
 inherited Destroy;
end;

procedure TRegUserDataBase.SaveToFile(filename: String);
var
 lst: TMyStringList;
 i,j: Integer;
 user: PRegisteredUser;
 str: String;
begin
 lst:=CreateStringList;
 for j:=0 to USERS_NAME_ARRAY-1 do
 for i:=0 to list[j].count-1 do
 begin
   user:=list[j].Items[i];
   str:=user.nick;
   while Length(str)<22 do str:=str+' ';
   str:=str+user.password+' '+
     IntToStr(Ord(user.level))+' '+
     IntToStr(user.downloads)+' '+
     IntToStr(user.uploads)+' '+
     decode_ip(user.last_ip)+' '+
     IntToStr(user.last_seen)+' '+
     IntToStr(UserState2Int(user.state))+' '+
     IntToStr(user.created)+' '+
     AddStr(user.createdby)+' '+
     AddStr(user.lastsetby);   
   lst.Add(str);
   if (i mod 30)=0 then
   begin
    {$I checksync.pas}
   end;
 end;
 if lst.count<1000 then lst.Sort;
 lst.Insert(0,'# VERSION 3');
 lst.Insert(1,'# ! このファイルは次回サーバーがシャットダウンしたとき上書きされます。');
 lst.Insert(2,'# 編集するときは、サーバーが稼動していないか確認してください。');
 lst.Insert(3,'#');
 lst.Insert(4,'# 形式: name password level downloads uploads ip last-seen [muzzled] created createdby lastsetby');
 lst.Insert(5,'# このファイルにはmods+と発言禁止者だけが記載されます。他のユーザーは削除されます。');
 lst.Insert(6,'#');
 try
  lst.SaveToFile(filename);
  except
 end;
 FreeStringList(lst);
end;

procedure TRegUserDataBase.LoadFromFile(filename: String);
var
 version: Integer;
 i: Integer;
 lst,entry: TMyStringList;
 str: String;
 user: TRegisteredUser;
begin
 for i:=0 to USERS_NAME_ARRAY-1 do
  list[i].Clear;
 lst:=CreateStringList;
 try
  lst.LoadFromFile(filename);
  except
   FreeStringList(lst);
   exit;
 end;
 version:=0;
 entry:=CreateStringList;
 for i:=0 to lst.count-1 do
 if Length(lst.Strings[i])>1 then
 begin
   if lst.Strings[i][1]='#' then
   begin
     str:=lowercase(lst.Strings[i]);
     if str='# version 2' then version:=2;
     if str='# version 3' then version:=3;
   end
   else
   begin
     SplitStringOld(lst.Strings[i],entry);
     if entry.count>6 then
     if FindUser(entry.Strings[0])=nil then
     begin
       if (i mod 30)=0 then
       begin
        {$I checksync.pas}
       end;
       if version=2 then
       begin
         ResetRegistered(user);
         user.nick:=entry.Strings[0];
         user.password:=entry.Strings[1];
         user.level:=Str2Level(entry.Strings[3]);
         user.downloads:=StrToIntDef(entry.Strings[4],0);
         user.uploads:=StrToIntDef(entry.Strings[5],0);
         if entry.count>7 then user.last_ip:=StrToInt64Def(encode_ip_str(entry.Strings[7]),0)
         else user.last_ip:=0;
         if entry.count>8 then user.last_seen:=StrToIntDef(entry.Strings[8],0)
         else user.last_seen:=0;
         if entry.count>9 then user.state:=Int2UserState(StrToIntDef(entry.Strings[9],0),false)
         else user.state:=[];
         if entry.count>10 then user.created:=StrToIntDef(entry.Strings[10],0)
         else user.created:=GetTickCountT;
         if entry.count>11 then user.createdby:=entry.Strings[11]
         else user.createdby:='';
         if entry.count>12 then user.lastsetby:=entry.Strings[12]
         else user.lastsetby:='';
         if user.level<>napUserConsole then
          Add(user);
       end;
       if version=3 then
       begin
         ResetRegistered(user);
         user.nick:=entry.Strings[0];
         user.password:=entry.Strings[1];
         user.level:=Str2Level(entry.Strings[2]);
         user.downloads:=StrToIntDef(entry.Strings[3],0);
         user.uploads:=StrToIntDef(entry.Strings[4],0);
         user.last_ip:=StrToInt64Def(encode_ip_str(entry.Strings[5]),0);
         user.last_seen:=StrToIntDef(entry.Strings[6],0);
         if entry.count>7 then user.state:=Int2UserState(StrToIntDef(entry.Strings[7],0),false)
         else user.state:=[];
         if entry.count>8 then user.created:=StrToIntDef(entry.Strings[8],0)
         else user.created:=946702800; // shows created date as 1/1/00
         if entry.count>9 then user.createdby:=entry.Strings[9]
         else user.createdby:='';
         if entry.count>10 then user.lastsetby:=entry.Strings[10]
         else user.lastsetby:='';
         if user.level<>napUserConsole then
          Add(user);
       end;
     end;
   end;
 end;
 FreeStringList(entry);
 FreeStringList(lst);
end;

function  TRegUserDataBase.FindUser(nick: String): PRegisteredUser;
var
 i: Integer;
begin
 Result:=nil;
 i:=GetCharIndex(nick);
 if i=-1 then exit;
 Result:=list[i].FindUserRec(nick);
end;

function  TRegUserDataBase.PasswordOk(nick, password: String): Boolean;
var
 i: Integer;
begin
 Result:=false;
 i:=GetCharIndex(nick);
 if i=-1 then exit;
 Result:=list[i].PasswordOk(nick, password);
end;

procedure TRegUserDataBase.Delete(nick: String);
var
 i,j: Integer;
begin
 i:=GetCharIndex(nick);
 if i=-1 then exit;
 j:=list[i].FindUser(nick);
 if j<>-1 then
  list[i].Delete(j);
end;

procedure TRegUserDataBase.Add(reg: TRegisteredUser);
var
 i: Integer;
begin
 if reg.level=napUserConsole then exit;
 i:=GetCharIndex(reg.nick);
 if i=-1 then exit;
 list[i].Add(reg);
end;

function  TRegUserDataBase.CountUsers: Integer;
var
 i,j: Integer;
begin
 j:=0;
 for i:=0 to USERS_NAME_ARRAY-1 do
  inc(j,list[i].count);
 Result:=j;
end;

end.

