{*********************************************************
 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: Handler

 Handlers for all client-server and server-server messages

*********************************************************}
unit Handler;

interface

uses
  SysUtils, Classes2, Graphics, ZLibEx, WinSock, Windows, Constants, Users,
  Servers, STypes, BlckSock, SynSock, LocalUsers, Registered, SlavaStrings,
  Class_Cmdlist, Class_Cmd2List, Class_CmdExList, Mmsystem, Ips, Keywords,
  Keywords2, Share2, Blocks, StringResources;

function ProcessCommand(Usr: TLocalUser; Q: TQuery = queryNormal): Boolean;
procedure ProcessServerCommand(Srv: TServer);
procedure DisconnectUser(Usr: TLocalUser; Reason, Text, Sender: string;
  Close_Socket: Boolean);
procedure KickUser(User: POnlineUser; Msg: string);
procedure DisconnectServer(Srv: TServer; Inform_Servers, Do_Wallop: Boolean;
  Sender: string);
procedure Exec(Usr: POnlineUser; Id: Integer; Cmd: string);
procedure WriteAllServersEx(Srv: TServer; Id: Integer; Sender, Cmd: string);
function WriteAllServers(Id: Integer; Sender, Cmd: string; Ignored: TServer =
  nil): Integer;
function FormatString(User: POnlineUser; Str: string;
  Strip_Quotes: Boolean): string;
procedure Wallop(Id: Integer; Wid: TWallopType; Cmd: string;
  Local_Only: Boolean);
procedure Error(Str: string; Check_Permission: Boolean = False);
procedure PermissionDenied(Func: string; Check_Permission: Boolean = False);
procedure CountStats;
procedure CompleteSyncUser(Srv: TServer; User: POnlineUser);
procedure Handler_JoinChannel;
procedure UpdateUser(User: POnlineUser; Ignored: TServer = nil);
procedure Handler_ServerConnect(Srv: TServer; Timer: Boolean);
procedure BanUser(Ban, Server: string; Time: Integer; Reason: string;
  Write_All: Boolean);
procedure AddReconnector(Ip: string);
procedure DoCloseSocket(S: TSocket);
// function FindLocalUser(Data: POnlineUser): TLocalUser;
function FindLocalUser(Nick: string): TLocalUser;
function CheckWinMX(Usr: TLocalUser): Boolean;
function IsLogged: Boolean;
function CheckLevel(Func: string; Lev: TNapUserLevel): Boolean;
function CheckParams(Count: Integer; Show_Error: Boolean = True): Boolean;
procedure Handler_MsgServ;

var
  GCmd: TNapCmd;
  Query: TQuery;
  User: POnlineUser;
  Server: TServer;

implementation

{$I Defines.pas}

uses
  Lang, Vars, Share, Thread, Channels, Bans, Config, Md5, Memory_Manager,
    BrowseForm;

var
  HList, HLst: TMyStringList;
  Local: TLocalUser;
  Query_Channel: string;
  Search_Data: TSearchStruct;
  Search_Data_Old: TSearchStruct;
  Compressed: Boolean;

function FindLocalUser(Nick: string): TLocalUser;
var
  I, Len: Integer;
begin
  Result := nil;
  Nick := AnsiLowerCase(Nick);
  Len := Length(Nick);
  try
    if (Local <> nil) and (AnsiLowerCase(Local.Nick) = Nick) then
      Result := Local
    else
      for I := 0 to DB_Local.Count - 1 do
        if Length(TLocalUser(DB_Local.Items[I]).Nick) = Len then
          if AnsiLowerCase(TLocalUser(DB_Local.Items[I]).Nick) = Nick then
          begin
            Result := DB_Local.Items[I];
            Exit;
          end;
  except
  end
end;

procedure Exec(Usr: POnlineUser; Id: Integer; Cmd: string);
begin
  if Usr = nil then Exit;
  try
    if Usr^.Server = nil then
      TLocalUser(Usr.Local).Exec(Id, Cmd)
    else
      Usr^.Server.Exec(MSG_CLIENT_RELAY, IntToStr(Id) + ' ' + Usr^.UserName + ' '
        + Cmd);
  except
  end;
end;

function CanSendError(Usr: POnlineUser; Q: TQuery): Boolean;
begin
  case Q of
    queryServer: Result := False;
    queryOperServ, queryChanServ, queryNickServ, queryMsgServ, queryChannel:
      Result := True;
  else if Usr = nil then
    Result := True
  else
    Result := not (userHideErrors in Usr^.State);
  end;
end;

procedure Error(Str: string; Check_Permission: Boolean = False);
begin
  if User = nil then Exit;
  if Check_Permission then
    if not CanSendError(User, Query) then Exit;
  case Query of
    queryOperServ: Exec(User, MSG_SERVER_PRIVMSG, 'OperServ ' + Str);
    queryNickServ: Exec(User, MSG_SERVER_PRIVMSG, 'NickServ ' + Str);
    queryChanServ: Exec(User, MSG_SERVER_PRIVMSG, 'ChanServ ' + Str);
    queryMsgServ: Exec(User, MSG_SERVER_PRIVMSG, 'MsgServ ' + Str);
    queryChannel: Exec(User, MSG_SERVER_PUBLIC, Query_Channel + ' Server ' +
      Str);
    queryRemoteUser,
      queryServer:
      begin
      end;
  else
    Exec(User, MSG_SERVER_NOSUCH, Str);
  end;
end;

procedure PermissionDenied(Func: string; Check_Permission: Boolean = False);
begin
  if Func = '' then
    Error(GetLangT(LNG_ACCESS), Check_Permission)
  else
    Error(Func + ': ' + GetLangT(LNG_ACCESS), Check_Permission);
end;

procedure UserIsOffline(Str: string; Check_Permission: Boolean = False);
begin
  Error(GetLangT(LNG_OFFLINE2, Str), Check_Permission);
end;

procedure NoSuchUser(Check_Permission: Boolean = False);
begin
  Error(GetLangT(LNG_NOUSER), Check_Permission);
end;

function CheckRange(Num: string; Min, Max: Integer): Boolean;
var
  N: Integer;
begin
  N := StrToIntDef(Num, Min - 1);
  Result := (N >= Min) and (N <= Max);
end;

function CheckRangeEx(Num: string; Min, Max: Integer): Boolean;
var
  N: Integer;
begin
  N := StrToIntDef(Num, Min - 1);
  Result := (N >= Min) and (N <= Max);
  if Result = False then
    Error(GetLangT(LNG_INVALIDARGS), True);
end;

function CheckParams(Count: Integer; Show_Error: Boolean = True): Boolean;
begin
  SplitString(GCmd.Cmd, HList);
  if HList.Count < Count then
  begin
    Result := False;
    if Query <> queryServer then
      if Show_Error then
        Error(GetLangT(LNG_INVALIDARGS), True);
  end
  else
    Result := True;
end;

function CheckLevel(Func: string; Lev: TNapUserLevel): Boolean;
begin
  if User = nil then
  begin
    Result := False;
    Exit;
  end;
  if User^.Level < Lev then
  begin
    Result := False;
    PermissionDenied(Func, True);
  end
  else
    Result := True;
end;

function IsLogged: Boolean;
begin
  Result := User <> nil;
end;

function IsLocal: Boolean;
begin
  Result := Local <> nil;
end;

procedure AddSoftware(Str: string);
var
  I, Crc: Integer;
  Str1: string;
begin
  Str := Trim(Str);
  Str1 := LowerCase(Str);
  Crc := StringCRC(Str1, True);
  if DB_Software = nil then Exit;
  for I := 0 to DB_Software.Count - 1 do
    if PNapCmd2(DB_Software.Items[I])^.Id2 = Crc then
      if LowerCase(PNapCmd2(DB_Software.Items[I])^.Cmd) = Str1 then
      begin
        Inc(PNapCmd2(DB_Software.Items[I])^.Id1);
        Exit;
      end;
  if DB_Software.Count > MAX_SOFTWARE_INDEX then
  begin
    I := 0;
    while I < DB_Software.Count do
    begin
      if PNapCmd2(DB_Software.Items[I])^.Id1 < 2 then
        DB_Software.Delete(I)
      else
        Inc(I);
    end;
    Exit;
  end;
  if DB_Software.Count > MAX_SOFTWARE_INDEX then Exit;
  DB_Software.AddCmd(1, Crc, Str);
end;

procedure BlockCQEXChat(var Str: string);
begin
  if Pos('<', Str) < 1 then Exit;
  if Pos('>', Str) < 1 then Exit;
  ReplaceString(Str, '<b>', '');
  ReplaceString(Str, '</b>', '');
  ReplaceString(Str, '<c>', '');
  ReplaceString(Str, '</c>', '');
  ReplaceString(Str, '<u>', '');
  ReplaceString(Str, '</u>', '');
  ReplaceString(Str, '<inverse>', '');
  ReplaceString(Str, '</inverse>', '');
  ReplaceString(Str, '<underline>', '');
  ReplaceString(Str, '</underline>', '');
  ReplaceString(Str, '<bold>', '');
  ReplaceString(Str, '</bold>', '');
  ReplaceString(Str, '<white>', '');
  ReplaceString(Str, '<black>', '');
  ReplaceString(Str, '<navy>', '');
  ReplaceString(Str, '<green>', '');
  ReplaceString(Str, '<red>', '');
  ReplaceString(Str, '<maroon>', '');
  ReplaceString(Str, '<purple>', '');
  ReplaceString(Str, '<orange>', '');
  ReplaceString(Str, '<yellow>', '');
  ReplaceString(Str, '<lime>', '');
  ReplaceString(Str, '<teal>', '');
  ReplaceString(Str, '<aqua>', '');
  ReplaceString(Str, '<blue>', '');
  ReplaceString(Str, '<fuchsia>', '');
  ReplaceString(Str, '<gray>', '');
  ReplaceString(Str, '<silver>', '');
end;

function CheckWinMX(Usr: TLocalUser): Boolean;
// Detects winmx. Returns False if handler Should be Interrupted
begin
  Result := True;
  if Usr = nil then Exit;
  if Usr.Data = nil then Exit; // not Logged in
  case Usr.SoftwareID of
    softWinMXNormal, softWinMXJap, softWinMXHidden, softNapchan: Exit;
  end;
  if locPingable in Usr.LocalState then Exit;
  // Potential WinMX with changed Signature detected
  Usr.SoftwareID := softWinMXHidden;
  Inc(Mx_Users);
  if Blocked_Clients[softWinMXHidden] then
    if Usr.Data^.Level < napUserModerator then
      if not StrHash_FindString(DB_Friends, Usr.Data^.UserName, True) then
      begin
        Usr.Exec(MSG_SERVER_NOSUCH, GetLangT(LNG_BLOCKEDWINMX));
        if Ban_FakeMX then
          BanUser(Decode_Ip(Usr.Ip), ServerName_T, Mx_Ban,
            GetLangT(LNG_REASON_WINMXBAN, ServerAlias), True);
        DisconnectUser(Usr, '', GetLangT(LNG_DISCONNECT_WINMX, Usr.Nick,
          Usr.Software), 'CheckWinMX', False);
        Result := False;
        Exit;
      end;
end;

function WriteAllServers(Id: Integer; Sender, Cmd: string; Ignored: TServer =
  nil): Integer;
var
  I, Num: Integer;
  Srv: TServer;
  Str: string;
begin
  Tmp_Pos := 100;
  if Sender = '' then
    Str := Cmd
  else if Cmd <> '' then
    Str := Sender + ' ' + Cmd
  else
    Str := Sender;
  Str := IntToStr(Id) + ' ' + IntToStr(MyServerHandle) + ' ' + Str;
  Num := 0;
  Ignored := GetServerLink(Ignored);
  Tmp_Pos := 101;
  for I := 0 to DB_Servers.Count - 1 do
  begin
    Srv := DB_Servers.Items[I];
    if Srv <> Ignored then
      if Srv.Logged then
        if Srv.Hub = nil then
        begin
          Srv.Exec(MSG_SRV_FORWARDALL, Str);
          Inc(Num);
        end;
  end;
  Result := Num;
  Tmp_Pos := 102;
end;

procedure WriteAllServersEx(Srv: TServer; Id: Integer; Sender, Cmd: string);
var
  Str: string;
begin
  Tmp_Pos := 110;
  if Sender = '' then
    Str := Cmd
  else if Cmd <> '' then
    Str := Sender + ' ' + Cmd
  else
    Str := Sender;
  Str := IntToStr(Id) + ' ' + IntToStr(MyServerHandle) + ' ' + Str;
  Tmp_Pos := 111;
  Srv := GetServerLink(Srv);
  Tmp_Pos := 112;
  if Srv.Logged then
    if Srv.Hub = nil then
      Srv.Exec(MSG_SRV_FORWARDALL, Str);
  Tmp_Pos := 113;
end;

procedure Handler_Stats;
begin
  if User <> nil then
    Exec(User, MSG_SERVER_STATS, IntToStr(Total_Users) + ' ' +
      IntToStr(Total_Files) + ' ' + IntToStr(Total_Bytes div 1073741824));
      // + ' GB ' + IntToStr(User^.Shared)
end;

procedure Wallop(Id: Integer; Wid: TWallopType; Cmd: string;
  Local_Only: Boolean);
var
  I: Integer;
  User2: TLocalUser;
  B: Boolean;
begin
  Tmp_Pos := 120;
  for I := 0 to DB_Local.Count - 1 do
  begin
    User2 := DB_Local.Items[I];
    if User2.Logged then
      if User2.Data^.Level > napUserUser then
      begin
        B := False;
        case Wid of
          wallopWallop: B := userHideWallop in User2.Data^.State;
          wallopMBan: B := userHideMBans in User2.Data^.State;
          wallopSBan: B := userHideSBans in User2.Data^.State;
          wallopMBanConn: B := userHideMBanConn in User2.Data^.State;
          wallopSBanConn: B := userHideSBanConn in User2.Data^.State;
          wallopChange: B := userHideChange in User2.Data^.State;
          wallopKill: B := userHideKill in User2.Data^.State;
          wallopLevel: B := userHideLevel in User2.Data^.State;
          wallopServer: B := userHideServer in User2.Data^.State;
          wallopMuzzle: B := userHideMuzzle in User2.Data^.State;
          wallopPort: B := userHidePort in User2.Data^.State;
          wallopCloak: B := userHideCloak in User2.Data^.State;
          wallopFlood: B := userHideFlood in User2.Data^.State;
          wallopPing: B := userHidePing in User2.Data^.State;
          wallopWhois: B := userHideWhois in User2.Data^.State;
          wallopFriends: B := userHideFriends in User2.Data^.State;
          wallopAnnouncement: B := userHideAnnouncements in User2.Data^.State;
          wallopChannel: B := userHideChannel in User2.Data^.State;
          wallopRegister: B := userHideRegister in User2.Data^.State;
          wallopVar: B := userHideVar in User2.Data^.State;
          wallopMotd: B := userHideMotd in User2.Data^.State;
          wallopLeech: B := userHideLeech in User2.Data^.State;
        end;
        if not B then
          User2.Exec(Id, Cmd);
      end;
  end;
  Tmp_Pos := 121;
  if not Local_Only then
    WriteAllServers(MSG_SRV_WALLOP, IntToStr(Id) + ' ' + IntToStr(Ord(Wid)),
      Cmd);
end;

procedure RegisterUser(User: POnlineUser; Setby: string);
var
  Reg: TRegisteredUser;
  Preg: PRegisteredUser;
begin
  Reg.Nick := User^.UserName;
  Reg.Password := User^.Password;
  Reg.Level := User^.Level;
  Reg.Downloads := User^.Total_Down;
  Reg.Uploads := User^.Total_Up;
  Reg.Last_Ip := User^.Ip;
  Reg.Last_Seen := Current_Time_T;
  Reg.State := User^.State - [userChatting];
  Preg := DB_Registered.FindUser(User^.UserName);
  if Preg <> nil then
  begin
    Reg.Created := Preg.Created;
    Reg.CreatedBy := Preg.CreatedBy;
    if SetBy <> 'server' then
      Reg.LastSetBy := Setby;
    DB_Registered.Delete(User^.UserName);
  end
  else
  begin
    Reg.Created := Current_Time_T;
    Reg.CreatedBy := Setby;
  end;
  DB_Registered.Add(Reg);
end;

procedure UserDisconnected(User: POnlineUser);
var
  Str: string;
  I: Integer;
  Cmd: TNapCmdEx;
begin
  Tmp_Pos := 1253;
  Str := AnsiLowerCase(User^.UserName);
  I := DB_Whowas.FindByCmd(Str);
  if I <> -1 then
    DB_Whowas.Delete(I);
  Tmp_Pos := 1254;
  Cmd.Cmd := Str;
  Cmd.Data := AddStr(User^.UserName) + ' ' + AddStr(Level2Str(User^.Level)) + ' '
    + IntToStr(Current_Time_T) + ' ' +
    IntToStr(User^.Ip) + ' ' + AddStr(GetServerName(User^.Server)) + ' ' +
      AddStr(User^.Software);
  Tmp_Pos := 1255;
  Cmd.Id := User^.Ip;
  Tmp_Pos := 1256;
  DB_Whowas.Add(Cmd);
end;

procedure UserOnline(Usr: TLocalUser; Mail: string = '');
var
  I: Integer;
  User2: TLocalUser;
  L: TNapUserLevel;
  Str: string;
begin
  Tmp_Pos := 130;
  if Usr.Data = nil then Exit;
  Usr.Last_Seen := Current_Time;
  Usr.Data^.Last_Seen_T := Current_Time_T;
  CompleteSyncUser(nil, Usr.Data);
{$I CheckSync.pas}
  Tmp_Pos := 131;
  if Running and Log_Login then
  begin
    if Mail = '' then
    begin
      Log(slOnline, GetLangT(LNG_USERONLINE, Usr.Nick, Usr.Software,
        Decode_Ip(Usr.Ip)));
      if StrHash_FindString(DB_Friends, Usr.Nick, True) then
      begin
        Wallop(MSG_SERVER_NOSUCH, wallopFriends, GetLangT(LNG_FRIENDLOGIN,
          User^.UserName, Usr.Software, Decode_Ip(Usr.Ip), Str), True);
        if Friend_Sound then
          PlaySound(PChar(ApplicationDir + 'friendlogin.wav'), 0, SND_FILENAME or
            SND_ASYNC);
      end;
    end
    else
      Log(slOnline, GetLangT(LNG_USERREGISTER, Usr.Nick, Usr.Software,
        Decode_Ip(Usr.Ip), Mail));
  end;
  Tmp_Pos := 132;
  Inc(Users_Per_Minute);
  if Usr.Level > napUserUser then
  begin
    Usr.Exec(MSG_SERVER_NOSUCH, GetLangT(LNG_LEVEL, ServerName_T,
      Level2Str(Usr.Level), Ord(Usr.Level)));
    L := Usr.Level;
    Usr.Data^.Level := napUserUser; // to avoid Sending this Message to Usr
    Wallop(MSG_SERVER_NOSUCH, wallopLevel, GetLangT(LNG_LEVEL1, ServerName_T,
      Usr.Nick, Level2Str(L), IntToStr(Ord(L))), False);
    Usr.Data^.Level := L;
  end;
  if Usr.Level = napUserLeech then
    Wallop(MSG_SERVER_NOSUCH, wallopLeech, GetLangT(LNG_LEVEL1, ServerName_T,
      Usr.Nick, Level2Str(Usr.Level), IntToStr(Ord(Usr.Level))), False);
  Usr.Exec(MSG_SERVER_PING, 'server');
  if (Usr.SoftwareID = softWinMXNormal) or (Usr.SoftwareID = softWinMXJap) or
    (Usr.SoftwareID = softWinMXHidden) then
    Inc(Mx_Users);
  Tmp_Pos := 133;
  Inc(Local_Users);
  Local_Users_Max := Max(Local_Users, Local_Users_Max);
  Inc(Total_Connections);
  Inc(Total_Users);
  if Total_Users > Total_Users_Max then
    Total_Users_Max := Total_Users;
  Tmp_Pos := 134;
  for I := 0 to DB_Local.Count - 1 do
  begin
    User2 := DB_Local.Items[I];
    if User2 <> Usr then
    begin
      Str := StrHash_FindStringEx(User2.Hotlist, Usr.Nick, True);
      if Str <> '' then
        User2.Exec(MSG_SERVER_USER_SIGNON, Str + ' ' +
          IntToStr(Ord(Usr.Data^.Speed)));
    end;
  end;
  Tmp_Pos := 135;
  Usr.LocalState := Usr.LocalState - [locNeedsUpdate];
  Handler_Stats;
  if userCloaked in Usr.Data^.State then
  begin
    Local.Exec(MSG_SERVER_NOSUCH, RS_Handler_Cloak);
    Wallop(MSG_SERVER_NOSUCH, wallopCloak, GetLangT(LNG_CLOAKED, Local.Nick),
      False);
    if User^.Server = nil then
      WriteAllServers(MSG_CLIENT_CLOAK, Local.Nick, '1');
  end;
end;

procedure UserOffline(Usr: TLocalUser; Reason: string; Off_Message: string;
  Sender: string);
var
  Preg: PRegisteredUser;
  I: Integer;
  B, Cloaked: Boolean;
  User2: TLocalUser;
  Ch: TChannel;
  Str: string;
begin
  try
    Tmp_Pos := 140;
    if Usr = Cons then
      if Running then Exit;
    Tmp_Pos := 141;
    if (Usr.Data <> nil) and (Usr.Nick <> '') then
    begin
      Dec(Total_Users);
      Dec(Local_Users);
      if (Usr.SoftwareID = softWinMXNormal) or (Usr.SoftwareID = softWinMXJap)
        or (Usr.SoftwareID = softWinMXHidden) then
        Dec(Mx_Users);
      Tmp_Pos := 1250;
      UserDisconnected(Usr.Data);
      Tmp_Pos := 1251;
      WriteAllServers(MSG_SRV_USEROFFLINE, Usr.Nick, '');
      Tmp_Pos := 142;
      if DB_Channels <> nil then
        for I := DB_Channels.Count - 1 downto 0 do
        begin
          Ch := DB_Channels.Items[I];
          if Ch.FindUser(Usr.Data) <> -1 then
          begin
            Ch.Part(Usr.Data);
            if Ch.Users.Count = 0 then
              if not (chRegistered in Ch.State) then
              begin
                DB_Channels.Delete(I);
                Ch.Free;
              end;
          end;
        end;
      Cloaked := userCloaked in Usr.Data^.State;
      Tmp_Pos := 143;
      if Usr.Shared <> nil then
      begin
        Tmp_Pos := 1204;
        Dec(Local_Files, Usr.Data^.Shared);
        if Nocount_Text then
          Dec(Local_Files, Usr.Shared_Text);
        Dec(Local_Bytes, Usr.Shared_Size);
        Dec(Total_Files, Usr.Data^.Shared);
        if Nocount_Text then
          Dec(Total_Files, Usr.Shared_Text);
        Dec(Total_Bytes, Usr.Shared_Size);
        Tmp_Pos := 1205;
        Usr.Shared_Size := 0;
        Usr.Shared_Mp3 := 0;
        Usr.Shared_Audio := 0;
        Usr.Shared_Video := 0;
        Usr.Shared_Images := 0;
        Usr.Shared_Apps := 0;
        Usr.Shared_Text := 0;
        Usr.Shared_Cd := 0;
        Usr.Shared_Blocked := 0;
        Tmp_Pos := 1206;
        Usr.Shared.Clear;
        Tmp_Pos := 1207;
        Usr.Shared.Free;
        Tmp_Pos := 1208;
        Usr.Shared := nil;
      end;
      Tmp_Pos := 144;
      B := Registered_Only;
      if B = False then
      begin
        if userMuzzled in Usr.Data^.State then
          B := True;
        if Usr.Level <> napUserUser then
          B := True;
      end;
      Preg := DB_Registered.FindUser(Usr.Nick);
      if Preg <> nil then
        B := True;
      if Usr.Data^.Level = napUserConsole then
      begin
        if Preg <> nil then
          DB_Registered.Delete(Usr.Data^.UserName);
        B := False;
      end;
      Tmp_Pos := 145;
      if B then
        RegisterUser(Usr.Data, 'server');
      Tmp_Pos := 146;
      if Running and Log_Login then
      begin
        if Off_Message <> '' then
          Log(slOffline, Off_Message)
        else
        begin
          if Reason = '' then
            Log(slOffline, GetLangT(LNG_USEROFFLINE2, Usr.Nick, Usr.Software))
          else
            Log(slOffline, GetLangT(LNG_USEROFFLINE, Usr.Nick, Usr.Software,
              Reason, Sender));
        end;
      end;
      Tmp_Pos := 147;
      for I := 0 to DB_Local.Count - 1 do
      begin
        User2 := DB_Local.Items[I];
        if User2.Logged then
          if User2 <> Usr then
            if (not cloaked) or (User2.Level > napUserUser) then
            begin
              Str := StrHash_FindStringEx(User2.Hotlist, Usr.Nick, True);
              if Str <> '' then
                User2.Exec(MSG_SERVER_USER_SIGNOFF, Str);
            end;
      end;
      Tmp_Pos := 148;
      StrHash_Clear(Usr.Hotlist);
      StrHash_Clear(Usr.Ignored);
    end;
    Tmp_Pos := 149;
    if Usr.Data <> nil then
      DB_Online.DeleteRecord(Usr.Data);
  except
    on E: Exception do
      DebugLog('Exception in UserOffline (Pos=' + IntToStr(Tmp_Pos) + ') : ' +
        E.Message);
  end;
end;

procedure DisconnectUser(Usr: TLocalUser; Reason, Text, Sender: string;
  Close_Socket: Boolean);
begin
  Tmp_Pos := 150;
  if Usr = Cons then
  begin
    DebugLog('Warning: Someone tried to kick Console User. (DisconnectUser Arguments: Reason="'
      + Reason + '" Text="' + Text + '" Sender="' + Sender + '" Cmd.Id=' +
      IntToStr(GCmd.Id) + ' Cmd.Cmd="' + GCmd.Cmd + '" Query=' +
      IntToStr(Ord(Query)) + ')');
    Exit;
  end;
  Tmp_Pos := 151;
  UserOffline(Usr, Reason, Text, 'DisconnectUser-' + Sender);
  Tmp_Pos := 152;
  if Usr.Socket <> INVALID_SOCKET then
  begin
    if Close_Socket then
    begin
      SynSock.Shutdown(Usr.Socket, SD_BOTH);
      SynSock.CloseSocket(Usr.Socket);
      Dec(Sockets_Count);
    end
    else
    begin
      Usr.Flush(False);
      DoCloseSocket(Usr.Socket);
    end;
    Usr.Socket := INVALID_SOCKET;
  end;
  Usr.Last_Seen := 0;
  Tmp_Pos := 153;
end;

procedure KickUser(User: POnlineUser; Msg: string);
var
  Preg: PRegisteredUser;
  I: Integer;
  B: Boolean;
  User2: TLocalUser;
  Ch: TChannel;
  Str: string;
begin
  Tmp_Pos := 160;
  if User = nil then Exit;
  if User^.Server = nil then
  begin
    User2 := User^.Local;
    if User2 = nil then Exit;
    if Msg <> '' then
      User2.Exec(MSG_SERVER_NOSUCH, Msg);
    DisconnectUser(User2, Msg, '', 'KickUser', False);
    Exit;
  end;
  Tmp_Pos := 161;
  UserDisconnected(User);
  if DB_Channels <> nil then
    for I := DB_Channels.Count - 1 downto 0 do
    begin
      Ch := DB_Channels.Items[I];
      if Ch.FindUser(User) <> -1 then
      begin
        Ch.Part(User);
        if Ch.Users.Count = 0 then
          if not (chRegistered in Ch.State) then
          begin
            DB_Channels.Delete(I);
            Ch.Free;
          end;
      end;
    end;
  Tmp_Pos := 162;
  B := Registered_Only;
  if B = False then
  begin
    if userMuzzled in User^.State then
      B := True;
    if User^.Level <> napUserUser then
      B := True;
  end;
  Tmp_Pos := 163;
  Preg := DB_Registered.FindUser(User^.UserName);
  if Preg <> nil then
    B := True;
  if User^.Level = napUserConsole then
  begin
    B := False;
    DB_Registered.Delete(User^.UserName);
  end;
  if B then
    RegisterUser(User, 'server');
  Tmp_Pos := 164;
  for I := 0 to DB_Local.Count - 1 do
  begin
    User2 := DB_Local.Items[I];
    Str := StrHash_FindStringEx(User2.Hotlist, User^.UserName, True);
    if Str <> '' then
      User2.Exec(MSG_SERVER_USER_SIGNOFF, Str);
  end;
  Tmp_Pos := 165;
  DB_Online.DeleteRecord(User);
  Tmp_Pos := 166;
end;

function FormatString(User: POnlineUser; Str: string;
  Strip_Quotes: Boolean): string;
begin
 // variables:
 //
 // $user$       - user's name
 // $server$     - server's name (server user is connected to)
 // $level$      - user's level
 // $levelnum$   - user's level as digit (0..4)
 // $client$     - user's software
 // $speed$      - user's connection speed
 // $minshare$   - minimum shared files
 // $maxshare$   - maximum shared files
 // $minsharesize$ - minimum MB to share
 // $maxusers$   - user's limit
 // $maxusers_total$ - total user's limit (on all linked servers)
 // $version$    - server version
 // $build$      - server build
 // $console$    - console user's name
 // $users$      - number of users online
 // $users_local$ - number of local users
 // $files$      - total number of shared files (by all users)
 // $files_local$ - number of shared files only on this server
 // $files_ex$   - total number of shared files (by all users), but
   // thousands are separated by dots (example: 12.345.678 instead of 12345678)
 // $files_local_ex$
 // $gb$         - size of shared files in Gb. (by all users)
 // $gb_ex$      - size of shared files in Gb using dots
 // $gb_local$   - size of shared files on this server
 // $gb_local_ex$ -
 // $servers$    - number of linked servers
 // $connections$ - number of connections
 // $memory$     - number of bytes used by application
 // $memory_ex$  - same as memory, but thousands are separated by dot.
 // $avgfiles$   - avg # of shared files/user (by all users)
 // $avggig$     - avg # of shared gb/user (by all users)
 // $avgfiles_local$ - avg # of shared files/user (by local users)
 // $avggig_local$ - avg # of shared gb/user (by local users)
 // $transfers$
 // $searches$

  Tmp_Pos := 170;
  if Strip_Quotes then
    ReplaceString(Str, '"', '`');
  // $user$
  if Pos('$user$', Str) > 0 then
    if User <> nil then
      ReplaceString(Str, '$user$', User^.UserName)
    else
      ReplaceString(Str, '$user$', '<unknown>');
  // $server$
  if Pos('$server$', Str) > 0 then
    if User = nil then
      ReplaceString(Str, '$server$', ServerAlias)
    else
      ReplaceString(Str, '$server$', GetServerAlias(User^.Server));
  // $level$
  if Pos('$level$', Str) > 0 then
    if User <> nil then
      ReplaceString(Str, '$level$', Level2Str(User^.Level))
    else
      ReplaceString(Str, '$level$', '<unknown>');
  // $levelnum$
  if Pos('$levelnum$', Str) > 0 then
    if User <> nil then
      ReplaceString(Str, '$levelnum$', IntToStr(Ord(User^.Level)))
    else
      ReplaceString(Str, '$levelnum$', '<unknown>');
  Tmp_Pos := 171;
  // $client$
  if Pos('$client$', Str) > 0 then
    if User <> nil then
      ReplaceString(Str, '$client$', User^.Software)
    else
      ReplaceString(Str, '$client$', '<unknown>');
  // $speed$
  if Pos('$speed$', Str) > 0 then
    if User <> nil then
      ReplaceString(Str, '$speed$', Speed2Str(User^.Speed))
    else
      ReplaceString(Str, '$speed$', '<unknown>');
  // $minshare$
  if Pos('$minshare$', Str) > 0 then
    ReplaceString(Str, '$minshare$', IntToStr(MinShare));
  // $minsharesize$
  if Pos('$minsharesize$', Str) > 0 then
    ReplaceString(Str, '$minsharesize$', IntToStr(MinShare_Size div MegaByte) +
      ' MB');
  // $maxshare$
  if Pos('$maxshare$', Str) > 0 then
    ReplaceString(Str, '$maxshare$', IntToStr(MaxShare));
  // $maxusers$
  if Pos('$maxusers$', Str) > 0 then
    ReplaceString(Str, '$maxusers$', IntToStr(Max_Users));
  // $maxusers_total$
  if Pos('$maxusers_total$', Str) > 0 then
    ReplaceString(Str, '$maxusers_total$', IntToStr(Total_Users_Limit));
  Tmp_Pos := 172;
  // $version$
  if Pos('$version$', Str) > 0 then
    ReplaceString(Str, '$version$', SLAVANAP_VERSION_SHORT);
  // $build$
  if Pos('$build$', Str) > 0 then
    ReplaceString(Str, '$build$', SLAVANAP_BUILD);
  // $console$
  if Pos('$console$', Str) > 0 then
    ReplaceString(Str, '$console$', Cons.Data^.UserName);
  // $users$
  if Pos('$users$', Str) > 0 then
    ReplaceString(Str, '$users$', IntToStr(Total_Users));
  // $users_local$
  if Pos('$users_local$', Str) > 0 then
    ReplaceString(Str, '$users_local$', IntToStr(Local_Users));
  // $files$
  if Pos('$files$', Str) > 0 then
    ReplaceString(Str, '$files$', IntToStr(Total_Files));
  // $files_local$
  if Pos('$files_local$', Str) > 0 then
    ReplaceString(Str, '$files_local$', IntToStr(Local_Files));
  // $gb$
  if Pos('$gb$', Str) > 0 then
    ReplaceString(Str, '$gb$', IntToStr(Total_Bytes div 1073741824));
  // $gb_local$
  if Pos('$gb_local$', Str) > 0 then
    ReplaceString(Str, '$gb_local$', IntToStr(Local_Bytes div 1073741824));
  // $files_ex$
  if Pos('$files_ex$', Str) > 0 then
    ReplaceString(Str, '$files_ex$', IntToStrDot(Total_Files));
  // $files_local_ex$
  if Pos('$files_local_ex$', Str) > 0 then
    ReplaceString(Str, '$files_local_ex$', IntToStrDot(Local_Files));
  // $gb_ex$
  if Pos('$gb_ex$', Str) > 0 then
    ReplaceString(Str, '$gb_ex$', IntToStrDot(Total_Bytes div 1073741824));
  // $gb_local_ex$
  if Pos('$gb_local_ex$', Str) > 0 then
    ReplaceString(Str, '$gb_local_ex$', IntToStrDot(Local_Bytes div
      1073741824));
  Tmp_Pos := 173;
  // $servers$
  if Pos('$servers$', Str) > 0 then
    ReplaceString(Str, '$servers$', IntToStr(Num_Servers));
  // $connections$
  if Pos('$connections$', Str) > 0 then
    ReplaceString(Str, '$connections$', IntToStr(Total_Connections));
  // $memory$
  if Pos('$memory$', Str) > 0 then
    ReplaceString(Str, '$memory$', IntToStr(AllocMemSize));
  // $memory_ex$
  if Pos('$memory_ex$', Str) > 0 then
    ReplaceString(Str, '$memory_ex$', IntToStrDot(AllocMemSize));
  // $avgfiles$
  if Pos('$avgfiles_local$', Str) > 0 then
    ReplaceString(Str, '$avgfiles_local$', IntToStrDot(Local_Files div
      Local_Users));
  // $avggig$
  if Pos('$avggig_local$', Str) > 0 then
    ReplaceString(Str, '$avggig_local$', IntToStrDot((Local_Bytes div GigaByte)
      div Local_Users));
  // $avgfiles$
  if Pos('$avgfiles$', Str) > 0 then
    ReplaceString(Str, '$avgfiles$', IntToStrDot(Total_Files div Total_Users));
  // $avggig$
  if Pos('$avggig$', Str) > 0 then
    ReplaceString(Str, '$avggig$', IntToStrDot((Total_Bytes div GigaByte) div
      Total_Users));
  if Pos('$transfers$', Str) > 0 then
    ReplaceString(Str, '$transfers$', IntToStrDot(Total_Transfers +
      Num_Transfers));
  if Pos('$searches$', Str) > 0 then
    ReplaceString(Str, '$searches$', IntToStrDot(Total_Searches +
      Num_Searches));
  Result := Str;
  Tmp_Pos := 174;
end;

{* * * * * Handlers * * * * *}

procedure Handler_Motd;
var
  Str: string;
  T: PStringHashItem;
begin
  Tmp_Pos := 180;
  if Local = nil then Exit;
  if Query <> queryNormal then Exit;
  if User = nil then Exit;
  if userHideMotd in User^.State then Exit;
  //
  // !!!!!  DO NOT Translate or Modify the Following
  // Strings - Some clients Use it to detect Server version
  //
  Local.Exec(MSG_SERVER_MOTD, 'VERSION ' + SLAVANAP_VERSION_MOTD);
  Local.Exec(MSG_SERVER_MOTD, 'SERVER ' + ServerAlias);
  // {$IFDEF }
 // Local.Exec(MSG_SERVER_MOTD, Format(RS_Handler_TotalConnections, [Total_Connections]);
 // {$ENDIF}
  Tmp_Pos := 181;
  T := DB_Motd.First;
  while T <> nil do
  try
    Str := FormatString(User, T^.Data, False);
    if (Length(Str) > 0) and (Str[1] = ';') then
      // do nothing
    else
      Local.Exec(MSG_SERVER_MOTD, Str);
    T := T^.Next;
  except
    on E: Exception do
    begin
      DebugLog('Exception in Handler_Motd : ' + E.Message);
      Exit;
    end;
  end;
  Tmp_Pos := 182;
end;

procedure AddReconnector(Ip: string);
begin
  if not Reconnect_Delay then Exit;
  if GetIndex(DB_Reconnect, Ip, False) = -1 then
    DB_Reconnect.Add(Ip);
end;

procedure DoCloseSocket(S: TSocket);
begin
  if S = INVALID_SOCKET then Exit;
  if not Running then
  begin // If slavanap isn't running - close socket regardless of waiting data
    SynSock.ShutDown(S, SD_BOTH);
    SynSock.CloseSocket(S);
    Dec(Sockets_Count);
    Exit;
  end;
  DB_Closed.AddCmd(Current_Time, S, '');
end;

procedure LoginError(Str: string);
begin
  if Local = nil then Exit;
  AddReconnector(Decode_Ip(Local.Ip));
  Local.Exec(MSG_SERVER_ERROR, Str);
  DisconnectUser(Local, '', '', 'LoginError', False);
end;

procedure RedirectUser(Def: string);
var
  Srv: TServer;
  I, J, K: Integer;
begin
  if Local = nil then Exit;
  if Num_Servers < 1 then
  begin
    LoginError(Def);
    Exit;
  end;
  AddReconnector(Decode_Ip(Local.Ip));
  K := 0;
  for I := 0 to DB_Servers.Count - 1 do
  begin
    Srv := DB_Servers.Items[I];
    if Srv.Logged then
      if Srv.Num_Users < (Srv.Max_Users - 10) then
        Inc(K, Srv.Max_Users - Srv.Num_Users);
  end;
  if K = 0 then
  begin
    LoginError(Def);
    Exit;
  end;
  K := Random(K); // Selecting random Server
  J := 0;
  for I := 0 to DB_Servers.Count - 1 do
  begin
    Srv := DB_Servers.Items[I];
    if Srv.Logged then
      if Srv.Num_Users < (Srv.Max_Users - 10) then
      begin
        Inc(J, Srv.Max_Users - Srv.Num_Users);
        if J >= K then
        begin
          Local.Exec(MSG_CLIENT_REDIRECT, Srv.Host + ' ' + IntToStr(Srv.Port));
          LoginError(Def);
          Exit;
        end;
      end;
  end;
  LoginError(Def);
end;

procedure Handler_Login;
var
  Rec: TOnlineUser;
  User2: POnlineUser;
  Reg: PRegisteredUser;
  Str, Bandata, Tempnick, Loginims: string;
  I: Integer;
  // Sin: TSockAddrIn;
  Ban: PBan;
  B: Boolean;
begin
  Tmp_Pos := 190;
  if Local = nil then Exit;
  if (User <> nil) or (Local.Socket = INVALID_SOCKET) then
  begin
    LoginError(GetLangT(LNG_LOGGED));
    Exit;
  end;
  if Query <> queryNormal then
  begin
    LoginError(GetLangT(LNG_ACCESS));
    Exit;
  end;
  SplitString(GCmd.Cmd, HList);
  if HList.Count < 5 then
  begin
    LoginError(GetLangT(LNG_INVALIDARGS));
    Exit;
  end;
  Tmp_Pos := 191;
  if not CheckRange(HList.Strings[4], 0, 10) then
  begin
    LoginError(GetLangT(LNG_INVSPEED));
    Exit;
  end;
  TempNick := HList.Strings[0];
  if not Check_Name(Tempnick) then
  begin
    LoginError(GetLangT(LNG_INVALIDNICK2, Tempnick));
    Exit;
  end;
  if IsDigit(Tempnick) then
  begin
    LoginError(GetLangT(LNG_INVALIDNICK));
    Exit;
  end;
  if not Check_Software(HList.Strings[3]) then
  begin
    LoginError(GetLangT(LNG_INVALIDSOFTWARE));
    Exit;
  end;
  if Check_Loginpass and (HList.Strings[1] <> Loginpass) then
    if DB_Registered.FindUser(Tempnick) = nil then
    begin
      LoginError(RS_Handler_Invalid_LoginPass);
      Exit;
    end;
  if not Check_Port(StrToIntDef(HList.Strings[2], -1)) then
  begin
    LoginError(Format(RS_Handler_Invalid_TransferPort, [HList.Strings[2]]));
    if (Log_Login) then
      Log(slDebugData, Format(RS_Handler_TransferPort_Blocked,
        [HList.Strings[2], HList.Strings[0]]));
    Exit;
  end;
  Tmp_Pos := 12262;
  for I := 0 to MAX_LISTEN_TRAPSOCKET do
  begin
    if Trap_Port[I] = 0 then Continue;
    if TCPSocket_GetLocalSin(Local.Socket).Sin_Port
      <> TCPSocket_GetLocalSin(Trap_Socket[I]).Sin_Port then
      Continue;
    LoginError(RS_Handler_Invalid_ServerPort);
    BanUser(Tempnick, ServerName_T, 7 * 86400, Format(RS_Handler_Ban_ServerPort,
      [Tempnick]), True);
    Exit;
  end;
  Tmp_Pos := 192;
  Local.SoftwareId := GetSoftware(HList.Strings[3]);
  ResetOnlineRec(Rec);
  Tmp_Pos := 193;
  Rec.UserName := Tempnick;
  Rec.NameCrc := StringCRC(Rec.UserName, True);
  Rec.Ip := CheckIP(Local.Socket);
  Rec.DataPort := StrToIntDef(HList.Strings[2], 0);
  Rec.Software := Copy(Trim(HList.Strings[3]), 1, Max_Software);
  Rec.Local := Local;
  Tmp_Pos := 194;
  Bandata := JoinBan(Rec.UserName, Decode_Ip(Rec.Ip));
  User2 := DB_Online.FindUser(Rec.UserName);
  if User2 <> nil then
  begin
    if User2.Level < napUserModerator then
      Exec(User2, MSG_SERVER_GHOST, '')
    else if not (userHideSameNic in User2^.State) then
      Exec(User2, MSG_SERVER_NOSUCH, GetLangT(LNG_SAMENIC, Rec.Software,
        Decode_Ip(Rec.Ip)));
    LoginError(GetLangT(LNG_GHOST));
    Exit;
  end;
  Tmp_Pos := 195;
  Reg := DB_Registered.FindUser(Rec.UserName);
  if (Reg = nil) or ((Reg <> nil) and (Reg^.Level < napUserModerator)) then
    if not StrHash_FindString(DB_Friends, Rec.UserName, True) then
      // if not StrHash_FindString(DB_Friends, Decode_Ip(Rec.Ip), False) then
    begin
      Tmp_Pos := 196;
      if Reconnect_Delay then
        if GetIndex(DB_Reconnect, Decode_Ip(Rec.Ip), False) <> -1 then
        begin // Reconnector
          RedirectUser(GetLangT(LNG_RECONNECT));
          Local.LocalState := Local.LocalState + [locWriteOnly];
          Exit;
        end;
      if Network_Hub then
      begin
        // if Redirect_Cqex and (Local.SoftwareID=softCQEX) then RedirectUser(GetLangT(LNG_ACCESS))
        LoginError(GetLangT(LNG_ACCESS));
        Exit;
      end;
      if Linking then
      begin
        if Redirect_Cqex and (Local.SoftwareID = softCQEX) then
          RedirectUser(GetLangT(LNG_LINKING))
        else
          LoginError(GetLangT(LNG_LINKING));
        Exit;
      end;
      if Bandwidth_Limited then
      begin
        if Redirect_Cqex and (Local.SoftwareID = softCQEX) then
          RedirectUser(GetLangT(LNG_BANDWIDTHLIMIT))
        else
          LoginError(GetLangT(LNG_BANDWIDTHLIMIT));
        Inc(Num_Rejects);
        Exit;
      end;
      if Local_Users >= Max_Users then
      begin
        if Redirect_Cqex and (Local.SoftwareID = softCQEX) then
          RedirectUser(GetLangT(LNG_USERSLIMIT, Local_Users))
        else
          LoginError(GetLangT(LNG_USERSLIMIT, Local_Users));
        Inc(Num_Rejects);
        Exit;
      end;
      if (Memory_Limit > 0) and Win98 then
        if AllocMemSize > Memory_Limit then
        begin
          if Redirect_Cqex and (Local.SoftwareID = softCQEX) then
            RedirectUser(GetLangT(LNG_MEMORYLIMIT))
          else
            LoginError(GetLangT(LNG_MEMORYLIMIT));
          Inc(Num_Rejects);
          Exit;
        end;
      if Max_Users_Per_Minute > 0 then
        if Users_Per_Minute >= Max_Users_Per_Minute then
        begin
          if Redirect_Cqex and (Local.SoftwareID = softCQEX) then
            RedirectUser(GetLangT(LNG_USERSLIMIT, Local_Users))
          else
            LoginError(GetLangT(LNG_USERSLIMIT, Local_Users));
          Inc(Num_Rejects);
          Exit;
        end;
      Tmp_Pos := 197;
      if DB_Bans.Banned(Rec.UserName, Decode_Ip(Rec.Ip), Ban) then
      begin
        if BanMail <> '' then
          LoginError(GetLangT(LNG_BANNED3, BanMail, Ban^.Reason))
        else
          LoginError(GetLangT(LNG_BANNED2, Ban^.Reason));
        if Copy(Ban^.Admin, 1, 7) = 'server:' then
          Wallop(MSG_SERVER_NOSUCH, wallopSBanConn, Format(RS_Handler_BanConn,
            [Bandata, JoinBan(Ban^.User, Ban^.Ip), Ban^.Reason]), True)
        else
          Wallop(MSG_SERVER_NOSUCH, wallopMBanConn, Format(RS_Handler_BanConn,
            [Bandata, JoinBan(Ban^.User, Ban^.Ip), Ban^.Reason]), True);
        WriteAllServers(MSG_SRV_UPDATEBAN, '', Rec.UserName + ' ' +
          Decode_Ip(Rec.Ip));
        Inc(Ban^.Tries);
        Ban^.LastAttempt := GetTickCountT;
        if Ban^.Ip = '*' then
          Ban^.Using := Decode_Ip(Rec.Ip)
        else
          Ban^.Using := Rec.UserName;
        Exit;
      end;
      if Registered_Only then
        if DB_Registered.FindUser(Rec.UserName) = nil then
        begin
          LoginError(GetLangT(LNG_REGISTEREDONLY));
          Exit;
        end;
      Tmp_Pos := 198;
      Str := LowerCase(Rec.Software);
      B := True;
      for I := 0 to Max_Custom_Block do
        if Blocked_Custom[I] <> '' then
          if Copy(Str, 1, Length(Blocked_Custom[I])) = Blocked_Custom[I] then
          begin
            B := False;
            Break;
          end;
      Tmp_Pos := 199;
      if Blocked_Clients[Local.SoftwareID] then
        B := False;
      if not B then
      begin
        case Blocked_Messagetype of
          blckNone:
            begin
              AddReconnector(Decode_Ip(Local.Ip));
              DisconnectUser(Local, '', '', 'LoginError', False);
            end;
          blckCustom: LoginError(FormatString(@Rec, Blocked_Message, False))
        else
          LoginError(GetLangT(LNG_CLIENTBLOCK, HList.Strings[3]));
        end;
        Exit;
      end;
      if ((Local.SoftwareID = softWinMXNormal) or (Local.SoftwareID =
        softWinMXJap)) and Limit_Mx then
        if (Mx_Users * 100 div Max_Users) >= Quota_Mx then
        begin
          loginError(GetLangT(LNG_TOOMUCHMX, Quota_Mx));
          Exit;
        end;
      Tmp_Pos := 200;
      if Max_Clones > 0 then
        if DB_Online.CountClones(Rec.Ip, False) >= Max_Clones then
        begin
          LoginError(GetLangT(LNG_MAXCLONES));
          Exit;
        end;
    end;
  Tmp_Pos := 201;
  Rec.Password := Encode(HList.Strings[1]);
  if not DB_Registered.PasswordOk(Rec.UserName, Rec.Password) then
  begin
    LoginError(GetLangT(LNG_INVALIDPASS));
    Exit;
  end;
  if TCPSocket_GetRemoteSin(Local.Socket).Sin_Addr.S_Addr <>
    TCPSocket_GetLocalSin(Local.Socket).Sin_Addr.S_Addr then
    AddSoftware(Rec.Software);
  Tmp_Pos := 202;
  Rec.Speed := TNapSpeed(StrToIntDef(HList.Strings[4], 0));
  if Reg <> nil then
  begin
    Rec.Total_Up := Reg^.Uploads;
    Rec.Total_Down := Reg^.Downloads;
    Rec.Level := Reg^.Level;
    Rec.State := Reg^.State;
    if userChatting in Rec.State then
      Rec.State := Rec.State - [userChatting];
    if userCloaked in Rec.State then
      if Rec.Level < napUserModerator then
        Rec.State := Rec.State - [userCloaked];
    if userMuzzled in Rec.State then
      if Rec.Level > napUserUser then
        Rec.State := Rec.State - [userMuzzled];
  end;
  if Network_Hub and (Rec.Level < napUserAdmin) then
  begin
    LoginError(GetLangT(LNG_ACCESS));
    Exit;
  end;
  Tmp_Pos := 203;
  Rec.Local := Local;
  Local.Data := DB_Online.AddUser(Rec);
  User := Local.Data;
  Local.Exec(MSG_SERVER_EMAIL, 'anon@' + ServerAlias);
  Inc(Num_Login);
  Tmp_Pos := 204;
  Handler_Motd;
  UserOnline(Local);
  Tmp_Pos := 12263;
  if Enable_Loginim then
  begin
    if Pos('WinMX', Local.Software) <> 0 then
    begin
      for I := 0 to Max_Loginim do
        if LoginIm[I] <> '' then
          Loginims := Loginims + Loginim[I] + #13#10;
      Local.Exec(MSG_CLIENT_PRIVMSG, LoginIMBot + ' ' + LoginIMs);
    end
    else
      for I := 0 to Max_Loginim do
        if LoginIm[I] <> '' then
          Local.Exec(MSG_CLIENT_PRIVMSG, LoginIMBot + ' ' + LoginIM[I]);
  end;
  Tmp_Pos := 12264;
  if Enable_Msgserv then
  begin
    GCmd.Cmd := 'Msgserv read';
    Handler_MsgServ;
  end;
  Tmp_Pos := 12265;
  if Force_Enter then
  begin
    Local.Join_Delay := INITIAL_JOIN_DELAY;
    DB_Login.Add(Local);
  end;
  Tmp_Pos := 205;
  Local.Flush;
end;

procedure Handler_LoginRegister;
var
  Rec: TOnlineUser;
  User2: POnlineUser;
  Str, Bandata: string;
  I: Integer;
  Ban: PBan;
  B: Boolean;
begin
  Tmp_Pos := 210;
  if Local = nil then Exit;
  if (User <> nil) or (Local.Socket = INVALID_SOCKET) then
  begin
    LoginError(GetLangT(LNG_LOGGED));
    Exit;
  end;
  if Query <> queryNormal then
  begin
    LoginError(GetLangT(LNG_ACCESS));
    Exit;
  end;
  if Registered_Only then
    if not Allow_Register then
    begin
      LoginError(GetLangT(LNG_REGISTEREDONLY));
      Exit;
    end;
  Tmp_Pos := 211;
  SplitString(GCmd.Cmd, HList);
  if HList.Count < 5 then
  begin
    LoginError(GetLangT(LNG_INVALIDARGS));
    Exit;
  end;
  if not CheckRange(HList.Strings[4], 0, 10) then
  begin
    LoginError(GetLangT(LNG_INVSPEED));
    Exit;
  end;
  if not Check_Name(HList.Strings[0]) then
  begin
    LoginError(GetLangT(LNG_INVALIDNICK2, HList.Strings[0]));
    Exit;
  end;
  if not Check_Software(HList.Strings[3]) then
  begin
    LoginError(GetLangT(LNG_INVALIDSOFTWARE));
    Exit;
  end;
  Tmp_Pos := 212;
  Local.SoftwareId := GetSoftware(HList.Strings[3]);
  ResetOnlineRec(Rec);
  Rec.UserName := HList.Strings[0];
  Rec.NameCrc := StringCRC(Rec.UserName, True);
  // Sin := TCPSocket_GetRemoteSin(Local.Socket);
  // Rec.Ip := Sin.Sin_Addr.S_Addr;
  Rec.Ip := CheckIP(Local.Socket);
  Rec.Password := Encode(HList.Strings[1]);
  Rec.DataPort := StrToIntDef(HList.Strings[2], 0);
  Rec.Software := Copy(Trim(HList.Strings[3]), 1, Max_Software);
  Rec.Speed := TNapSpeed(StrToIntDef(HList.Strings[4], 0));
  Tmp_Pos := 213;
  Bandata := JoinBan(Rec.UserName, Decode_Ip(Rec.Ip));
  User2 := DB_Online.FindUser(Rec.UserName);
  if User2 <> nil then
  begin
    Exec(User2, MSG_SERVER_GHOST, '');
    LoginError(GetLangT(LNG_GHOST));
    Exit;
  end;
  if Reconnect_Delay then
    if GetIndex(DB_Reconnect, Decode_Ip(Rec.Ip), False) <> -1 then
    begin // Reconnector
      RedirectUser(GetLangT(LNG_RECONNECT));
      Local.LocalState := Local.LocalState + [locWriteOnly];
      Exit;
    end;
  if DB_Registered.FindUser(Rec.UserName) <> nil then
  begin
    LoginError(GetLangT(LNG_REGISTERED));
    Exit;
  end;
  Tmp_Pos := 214;
  if Network_Hub then
  begin
    if Redirect_Cqex then
      RedirectUser(GetLangT(LNG_ACCESS))
    else
      LoginError(GetLangT(LNG_ACCESS));
    Exit;
  end;
  if Linking then
  begin
    if Redirect_Cqex then
      RedirectUser(GetLangT(LNG_LINKING))
    else
      LoginError(GetLangT(LNG_LINKING));
    Exit;
  end;
  if Bandwidth_Limited then
  begin
    if Redirect_Cqex and (Local.SoftwareID = softCQEX) then
      RedirectUser(GetLangT(LNG_BANDWIDTHLIMIT))
    else
      LoginError(GetLangT(LNG_BANDWIDTHLIMIT));
    Inc(Num_Rejects);
    Exit;
  end;
  if Local_Users >= Max_Users then
  begin
    if Redirect_Cqex and (Local.SoftwareID = softCQEX) then
      RedirectUser(GetLangT(LNG_USERSLIMIT, Local_Users))
    else
      LoginError(GetLangT(LNG_USERSLIMIT, Local_Users));
    Inc(Num_Rejects);
    Exit;
  end;
  if (Memory_Limit > 0) and Win98 then
    if AllocMemSize > Memory_Limit then
    begin
      if Redirect_Cqex and (Local.SoftwareID = softCQEX) then
        RedirectUser(GetLangT(LNG_MEMORYLIMIT))
      else
        LoginError(GetLangT(LNG_MEMORYLIMIT));
      Inc(Num_Rejects);
      Exit;
    end;
  if Max_Users_Per_Minute > 0 then
    if Users_Per_Minute >= Max_Users_Per_Minute then
    begin
      if Redirect_Cqex and (Local.SoftwareID = softCQEX) then
        RedirectUser(GetLangT(LNG_USERSLIMIT, Local_Users))
      else
        LoginError(GetLangT(LNG_USERSLIMIT, Local_Users));
      Inc(Num_Rejects);
      Exit;
    end;
  Tmp_Pos := 215;
  if DB_Bans.Banned(Rec.UserName, Decode_Ip(Rec.Ip), Ban) then
  begin
    if BanMail <> '' then
      LoginError(GetLangT(LNG_BANNED3, BanMail, Ban^.Reason))
    else
      LoginError(GetLangT(LNG_BANNED2, Ban^.Reason));
    if Copy(Ban^.Admin, 1, 7) = 'server:' then
      Wallop(MSG_SERVER_NOSUCH, wallopSBanConn, Format(RS_Handler_BanConn,
        [Bandata, JoinBan(Ban^.User, Ban^.Ip), Ban^.Reason]), True)
    else
      Wallop(MSG_SERVER_NOSUCH, wallopMBanConn, Format(RS_Handler_BanConn,
        [Bandata, JoinBan(Ban^.User, Ban^.Ip), Ban^.Reason]), True);
    WriteAllServers(MSG_SRV_UPDATEBAN, '', Rec.UserName + ' ' +
      Decode_Ip(Rec.Ip));
    Inc(Ban^.Tries);
    Ban^.LastAttempt := GetTickCountT;
    if Ban^.Ip = '*' then
      Ban^.Using := Decode_Ip(Rec.Ip)
    else
      Ban^.Using := Rec.UserName;
    Exit;
  end;
  if Max_Clones > 0 then
    if DB_Online.CountClones(Rec.Ip, False) >= Max_Clones then
    begin
      LoginError(GetLangT(LNG_MAXCLONES));
      Exit;
    end;
  Tmp_Pos := 216;
  Str := LowerCase(Rec.Software);
  B := True;
  for I := 0 to Max_Custom_Block do
    if Blocked_Custom[I] <> '' then
      if Copy(Str, 1, Length(Blocked_Custom[I])) = Blocked_Custom[I] then
      begin
        B := False;
        Break;
      end;
  Tmp_Pos := 199;
  if Blocked_Clients[Local.SoftwareID] then
    B := False;
  if not B then
  begin
    case Blocked_Messagetype of
      blckNone:
        begin
          AddReconnector(Decode_Ip(Local.Ip));
          DisconnectUser(Local, '', '', 'LoginError', False);
        end;
      blckCustom: LoginError(FormatString(@Rec, Blocked_Message, False))
    else
      LoginError(GetLangT(LNG_CLIENTBLOCK, HList.Strings[3]));
    end;
    Exit;
  end;
  if ((Local.SoftwareID = softWinMXNormal) or (Local.SoftwareID = softWinMXJap))
    and Limit_Mx then
    if (Mx_Users * 100 div Max_Users) >= Quota_Mx then
    begin
      loginError(GetLangT(LNG_TOOMUCHMX, Quota_Mx));
      Exit;
    end;
  Tmp_Pos := 217;
  if TCPSocket_GetRemoteSin(Local.Socket).Sin_Addr.S_Addr <>
    TCPSocket_GetLocalSin(Local.Socket).Sin_Addr.S_Addr then
    AddSoftware(Rec.Software);
  Rec.Local := Local;
  Local.Data := DB_Online.AddUser(Rec);
  User := Local.Data;
  Local.Exec(MSG_SERVER_EMAIL, 'anon@' + ServerAlias);
  Inc(Num_Login);
  Tmp_Pos := 218;
  Handler_Motd;
  UserOnline(Local);
  if Force_Enter then
  begin
    Local.Join_Delay := INITIAL_JOIN_DELAY;
    DB_Login.Add(Local);
  end;
  Tmp_Pos := 219;
  Local.Flush;
end;

procedure Handler_CheckNick;
begin
  Tmp_Pos := 220;
  if Local = nil then Exit;
  if Query <> queryNormal then Exit;
  if User <> nil then Exit;
  if (User <> nil) or (Local.Socket = INVALID_SOCKET) then
  begin
    Local.Exec(MSG_SERVER_REGISTER_FAIL, '');
    Error(GetLangT(LNG_LOGGED), True);
    Exit;
  end;
  if not Check_Name(GCmd.Cmd) then
  begin
    Local.Exec(MSG_SERVER_BAD_NICK, '');
    Exit;
  end;
  Tmp_Pos := 221;
  if DB_Registered.FindUser(GCmd.Cmd) <> nil then
  begin
    Local.Exec(MSG_SERVER_REGISTER_FAIL, '');
    Exit;
  end;
  if DB_Online.FindUser(GCmd.Cmd) <> nil then
  begin
    Local.Exec(MSG_SERVER_REGISTER_FAIL, '');
    Exit;
  end;
  Tmp_Pos := 222;
  Local.Exec(MSG_SERVER_REGISTER_OK, '');
end;

proceduRe Handler_PassCheck;
begin
  Tmp_Pos := 230;
  if not IsLocal then Exit;
  if Query <> queryNormal then Exit;
  if (User <> nil) or (Local.Socket = INVALID_SOCKET) then
  begin
    Error(GetLangT(LNG_LOGGED), True);
    Exit;
  end;
  if not CheckParams(2) then Exit;
  if not Check_Name(HList.Strings[0]) then
  begin
    LoginError(GetLangT(LNG_INVALIDPASS));
    Exit;
  end;
  Tmp_Pos := 231;
  if DB_Online.FindUser(HList.Strings[0]) <> nil then
  begin
    LoginError(GetLangT(LNG_INVALIDPASS));
    Exit;
  end;
  if DB_Registered.FindUser(HList.Strings[0]) = nil then
  begin
    Local.Exec(MSG_SERVER_PASS_OK, '');
    Exit;
  end;
  Tmp_Pos := 232;
  if DB_Registered.PasswordOk(HList.Strings[0], Encode(HList.Strings[1])) then
    Local.Exec(MSG_SERVER_PASS_OK, '')
  else
    LoginError(GetLangT(LNG_INVALIDPASS));
end;

procedure Handler_Echo;
begin
  Exec(User, GCmd.Id, GCmd.Cmd);
end;

{procedure Handler_ServerLinks;
var
  I: Integer;
  Str: string;
  Srv: TServer;
  Moderator: Boolean;
begin
  Tmp_Pos := 240;
  Moderator := User^.Level > napUserUser;
  if Query <> queryNormal then
  begin
    if Num_Servers = 0 then
    begin
      Error(RS_Handler_NoLinkedServers);
      Exit;
    end;
    if not Moderator then
      Str := DispSrvName
    else
      Str := ServerName_T;
    Error(Format(RS_Handler_LinkedServers, [Str]));
  end;
  Tmp_Pos := 241;
  for I := 0 to DB_Servers.Count - 1 do
  begin
    Srv := DB_Servers.Items[I];
    if Srv.Logged then
    begin
      if not Moderator then
      begin
        Str := Srv.Alias;
        if Srv.Hub <> nil then
          Str := Str + ' (->' + Srv.Hub.Alias + ')';
        Str := Str + #9'   Id=' + IntToStr(Srv.Server_Handle);
        Str := Str + ', Users=' + IntToStr(Srv.Num_Users) + '/' + IntToStr(Srv.Max_Users);
        Error(Str);
      end
      else if Query <> queryNormal then
      begin
        Str := Srv.Host + ':' + Srv.Port;
        if Srv.Hub <> nil then
          Str := Str + ' (->' + Srv.Hub.Host + ')';
        Str := Str + #9'   Id=' + IntToStr(Srv.Server_Handle);
        Str := Str + ', Users=' + IntToStr(Srv.Num_Users) + '/' + IntToStr(Srv.Max_Users);
        Error(Str);
      end
      else
        Exec(User, GCmd.Id, GetServerName(Srv.Hub) + ' 0 ' + GetServerName(Srv) + ' ' + IntToStr(Srv.Port) + ' 1');
    end;
  end;
  Tmp_Pos := 242;
  if (Query <> queryNormal) or (not Moderator) then
    Error(Format(RS_Handler_TotalLinked, [Num_Servers]))
  else
    Exec(User, GCmd.Id, '');
end;
}

procedure Handler_ServerLinks;
var
  I: Integer;
  Str: string;
  Srv: TServer;
  Moderator: Boolean;
begin
  Tmp_Pos := 240;
  Moderator := User^.Level > napUserUser;
  if Query <> queryNormal then
  begin
    if Num_Servers = 0 then
    begin
      Error(RS_Handler_NoLinkedServers);
      Exit;
    end;
    if not Moderator then
      Str := ServerAlias
    else
      Str := ServerName_T;
    Error(Format(RS_Handler_LinkedServers, [Str]));
  end
  else if Num_Servers = 0 then
  begin
    Exec(User, GCmd.Id, '');
    Exit;
  end;
  Tmp_Pos := 241;
  for I := 0 to DB_Servers.Count - 1 do
  begin
    Srv := DB_Servers.Items[I];
    if Srv.Logged then
    begin
      if Query <> queryNormal then
      begin
        if not Moderator then
        begin
          Str := GetServerAlias(Srv);
          if Srv.Hub <> nil then
            Str := Str + ' (->' + GetServerAlias(Srv.Hub) + ')';
          Str := Str + #9'   Users=' + IntToStr(Srv.Num_Users) + '/' +
            IntToStr(Srv.Max_Users);
          Error(Str);
        end
        else
        begin
          Str := Srv.Host + ':' + IntToStr(Srv.Port);
          if Srv.Hub <> nil then
            Str := Str + ' (->' + Srv.Hub.Host + ')';
          Str := Str + #9'   Id=' + IntToStr(Srv.Server_Handle);
          Str := Str + ', Users=' + IntToStr(Srv.Num_Users) + '/' +
            IntToStr(Srv.Max_Users);
          Error(Str);
        end
      end
      else
      begin
        if (Current_Time - Local.Last_Seen) > 10000 then
          // if user requested stats 10 seconds after login it is probably
          // auto-request by client (like aG) to avoid connecting to linked servers
        begin
          if Srv.Hub = nil then
            Exec(User, GCmd.Id, AddStr(ServerAlias) + ' 0 ' + GetServerAlias(Srv)
              + ' ' + IntToStr(Srv.Port) + ' 1')
          else
            Exec(User, GCmd.Id, GetServerAlias(Srv.Hub) + ' 0 ' +
              GetServerAlias(Srv) + ' ' + IntToStr(Srv.Port) + ' 1')
        end
        else
          Exec(User, GCmd.Id, GetServerName(Srv.Hub) + ' 0 ' + GetServerName(Srv)
            + ' ' + IntToStr(Srv.Port) + ' 1');
      end;
    end;
  end;
  Tmp_Pos := 242;
  if Query <> queryNormal then
  begin
    if Moderator then
      Error(Format(RS_Handler_TotalLinked, [Num_Servers]))
    else
      Error('.')
  end
  else
    Exec(User, GCmd.Id, '');
end;

procedure Handler_HotList;
var
  User2: POnlineUser;
begin
  Tmp_Pos := 250;
  if not IsLocal then Exit;
  if Query <> queryNormal then Exit;
  if not IsLogged then Exit;
  if not CheckParams(1) then Exit;
  if GCmd.Id = MSG_CLIENT_ADD_HOTLIST_SEQ then
    if GetTickCount - Local.Last_Seen < 30000 then
      Local.Detector := Local.Detector + [loc208];
  Tmp_Pos := 251;
  case GCmd.Id of
    MSG_CLIENT_ADD_HOTLIST,
      MSG_CLIENT_ADD_HOTLIST_SEQ:
      begin
        if not Check_Name(GCmd.Cmd) then
        begin
          if GCmd.Id = MSG_CLIENT_ADD_HOTLIST then
            Local.Exec(MSG_SERVER_HOTLIST_ERROR, GCmd.Cmd);
          Exit;
        end;
        User2 := DB_Online.FindUser(GCmd.Cmd);
        Tmp_Pos := 252;
        if Max_Hotlist > 0 then
          if Local.Level < napUserModerator then
            if Local.Hotlist.Count >= Max_Hotlist then Exit;
        if StrHash_FindString(Local.Hotlist, GCmd.Cmd, True) then Exit;
        Tmp_Pos := 253;
        StrHash_Add(Local.Hotlist, GCmd.Cmd);
        if GCmd.Id = MSG_CLIENT_ADD_HOTLIST then
          Local.Exec(MSG_SERVER_HOTLIST_ACK, GCmd.Cmd);
        if User2 <> nil then
          Local.Exec(MSG_SERVER_USER_SIGNON, GCmd.Cmd + ' ' +
            IntToStr(Ord(User2^.Speed)));
        Tmp_Pos := 254;
      end;
    MSG_CLIENT_REMOVE_HOTLIST: StrHash_Delete(Local.Hotlist, GCmd.Cmd, True);
  end;
end;

procedure Handler_IgnoreList;
var
  I: Integer;
  Item: PStringHashItem;
begin
  Tmp_Pos := 260;
  if not IsLocal then Exit;
  if Query <> queryNormal then Exit;
  if not IsLogged then Exit;
  Tmp_Pos := 261;
  case GCmd.Id of
    MSG_CLIENT_IGNORE_LIST:
      begin
        Tmp_Pos := 262;
        Item := Local.Ignored.First;
        while Item <> nil do
        begin
          Local.Exec(MSG_SERVER_IGNORE_ENTRY, Item^.Data);
          Item := Item^.Next;
        end;
        Local.Exec(MSG_CLIENT_IGNORE_LIST, IntToStr(Local.Ignored.Count));
      end;
    MSG_CLIENT_IGNORE_USER:
      begin
        if not CheckParams(1) then Exit;
        if StrHash_FindString(Local.Ignored, AnsiLowerCase(GCmd.Cmd), False)
          then
        begin
          Local.Exec(MSG_SERVER_ALREADY_IGNORED, GCmd.Cmd);
          Exit;
        end;
        Tmp_Pos := 263;
        if Max_Ignorelist > 0 then
          if Local.Level < napUserModerator then
            if Local.Ignored.Count >= Max_Ignorelist then
            begin
              Local.Exec(MSG_SERVER_NOT_IGNORED, GCmd.Cmd);
              Exit;
            end;
        StrHash_Add(Local.Ignored, AnsiLowerCase(GCmd.Cmd));
        Local.Exec(GCmd.Id, GCmd.Cmd);
      end;
    MSG_CLIENT_UNIGNORE_USER:
      begin
        if not CheckParams(1) then Exit;
        if StrHash_Delete(Local.Ignored, AnsiLowerCase(GCmd.Cmd), False) then
          Local.Exec(GCmd.Id, GCmd.Cmd)
        else
          Local.Exec(MSG_SERVER_NOT_IGNORED, GCmd.Cmd);
      end;
    MSG_CLIENT_CLEAR_IGNORE:
      begin
        if GetTickCount - Local.Last_Seen < 30000 then
          Local.Detector := Local.Detector + [loc326];
        I := Local.Ignored.Count;
        StrHash_Clear(Local.Ignored);
        Local.Exec(GCmd.Id, IntToStr(I));
      end;
  end;
end;

procedure Handler_OperServ;
var
  Action: string;
begin
  Query := queryOperServ;
  if not IsLogged then Exit;
  if not IsLocal then Exit;
  if HList.Count < 2 then
    HList.Add('help');
  Action := LowerCase(HList.Strings[1]);
  GCmd.Cmd := NextParamEx(GCmd.Cmd, 2);
  GCmd.Id := 0;
  if Action = 'links' then
    GCmd.Id := MSG_CLIENT_LINKS
  else if Action = 'block' then
    GCmd.Id := MSG_CLIENT_BLOCK
  else if Action = 'blocklist' then
    GCmd.Id := MSG_CLIENT_BLOCKLIST
  else if Action = 'unblock' then
    GCmd.Id := MSG_CLIENT_UNBLOCK
  else if Action = 'stats' then
    GCmd.Id := MSG_CLIENT_USAGE_STATS
  else if Action = 'nuke' then
    GCmd.Id := MSG_CLIENT_NUKE
  else if Action = 'register' then
    GCmd.Id := MSG_CLIENT_REGISTER_USER
  else if Action = 'chanlevel' then
    GCmd.Id := MSG_CLIENT_SET_CHAN_LEVEL
  else if Action = 'chanlimit' then
    GCmd.Id := MSG_CLIENT_CHANNEL_LIMIT
  else if Action = 'kick' then
    GCmd.Id := MSG_CLIENT_KICK
  else if Action = 'config' then
    GCmd.Id := MSG_CLIENT_SERVER_CONFIG
  else if Action = 'reconfig' then
    GCmd.Id := MSG_CLIENT_SERVER_RECONFIG
  else if Action = 'cban' then
    GCmd.Id := MSG_CLIENT_CHANNEL_BAN
  else if Action = 'cunban' then
    GCmd.Id := MSG_CLIENT_CHANNEL_UNBAN
  else if Action = 'cbanlist' then
    GCmd.Id := MSG_CLIENT_CHANNEL_BAN_LIST
  else if Action = 'cbanclear' then
    GCmd.Id := MSG_CLIENT_CHANNEL_CLEAR_BANS
  else if Action = 'clearchan' then
    GCmd.Id := MSG_CLIENT_CLEAR_CHANNEL
  else if Action = 'cloak' then
    GCmd.Id := MSG_CLIENT_CLOAK
  else if Action = 'op' then
    GCmd.Id := MSG_CLIENT_OP
  else if Action = 'deop' then
    GCmd.Id := MSG_CLIENT_DEOP
  else if Action = 'redirect' then
    GCmd.Id := MSG_CLIENT_REDIRECT
  else if Action = 'cycle' then
    GCmd.Id := MSG_CLIENT_CYCLE
  else if Action = 'whowas' then
    GCmd.Id := MSG_CLIENT_WHO_WAS
  else if Action = 'restart' then
    GCmd.Id := MSG_CLIENT_RESTART
  else if Action = 'setvar' then
    GCmd.Id := MSG_CLIENT_SETVAR
  else if Action = 'userlist' then
    GCmd.Id := MSG_CLIENT_USERLIST
  else if Action = 'connect' then
    GCmd.Id := MSG_CLIENT_CONNECT
  else if Action = 'disconnect' then
    GCmd.Id := MSG_CLIENT_DISCONNECT
  else if Action = 'killserver' then
    GCmd.Id := MSG_CLIENT_KILL_SERVER
  else if Action = 'removeserver' then
    GCmd.Id := MSG_CLIENT_REMOVE_SERVER
  else if (Action = 'usermode') or (Action = 'um') then
    GCmd.Id := MSG_CLIENT_USER_MODE
  else if Action = 'muzzle' then
    GCmd.Id := MSG_CLIENT_MUZZLE
  else if Action = 'unmuzzle' then
    GCmd.Id := MSG_CLIENT_UNMUZZLE
  else if Action = 'help' then
  begin
    Local.Exec(MSG_CLIENT_PRIVMSG, 'OperServ ' + RS_OperServ_Help_Title);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'OperServ ' + RS_OperServ_Help_begin);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'OperServ ' + RS_OperServ_Help_Connect);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'OperServ ' + RS_OperServ_Help_Block);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'OperServ ' + RS_OperServ_Help_BlockList);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'OperServ ' + RS_OperServ_Help_ChanLevel);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'OperServ ' + RS_OperServ_Help_ChanLimit);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'OperServ ' + RS_OperServ_Help_ClearChan);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'OperServ ' + RS_OperServ_Help_Cloak);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'OperServ ' + RS_OperServ_Help_Cycle);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'OperServ ' + RS_OperServ_Help_Deop);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'OperServ ' + RS_OperServ_Help_Disconnect);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'OperServ ' + RS_OperServ_Help_Help);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'OperServ ' + RS_OperServ_Help_Kick);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'OperServ ' + RS_OperServ_Help_KillServer);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'OperServ ' + RS_OperServ_Help_Muzzle);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'OperServ ' + RS_OperServ_Help_Nuke);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'OperServ ' + RS_OperServ_Help_Op);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'OperServ ' + RS_OperServ_Help_Redirect);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'OperServ ' + RS_OperServ_Help_RemoveServer);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'OperServ ' + RS_OperServ_Help_Restart);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'OperServ ' + RS_OperServ_Help_SetVar);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'OperServ ' + RS_OperServ_Help_Stats);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'OperServ ' + RS_OperServ_Help_Unblock);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'OperServ ' + RS_OperServ_Help_UnMuzzle);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'OperServ ' + RS_OperServ_Help_UserList);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'OperServ ' + RS_OperServ_Help_UserMode);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'OperServ ' + RS_OperServ_Help_Whowas);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'OperServ ' + RS_OperServ_Help_end);
    Exit;
  end
  else
  begin
    Local.Exec(MSG_CLIENT_PRIVMSG, 'OperServ ' + GetLangT(LNG_INVALIDCOMMAND,
      Action));
    Exit;
  end;
  if GCmd.Id <> 0 then
    ProcessCommand(Local, queryOperServ);
end;

procedure Handler_MsgServ_ReadAll;
var
  T: PStringHashItem;
begin
  if not IsLogged then Exit;
  T := DB_MsgServ.First; // receiver (new|old) date time <sender> "msg"
  while T <> nil do
  try
    if FirstParam(T^.Data) = AnsiLowerCase(Local.Nick) then
    begin
      Local.Exec(MSG_CLIENT_PRIVMSG, 'MsgServ ' + NextParam(T^.Data, 2));
      if FirstParam(NextParam(T^.Data)) = 'new' then
        T^.Data := FirstParam(T^.Data) + ' old ' + NextParam(T^.Data, 2);
    end;
    T := T^.Next;
  except
  end;
end;

procedure Handler_MsgServ_ReadNew; // 伝言を送信者からのIMみたいに表示
var
  T: PStringHashItem;
begin
  if not IsLogged then Exit;
  T := DB_MsgServ.First; // receiver (new|old) date time <sender> "msg"
  while T <> nil do
  try
    if FirstParam(T^.Data) = AnsiLowerCase(Local.Nick) then
      if FirstParam(NextParam(T^.Data)) = 'new' then
      begin
        Local.Exec(MSG_CLIENT_PRIVMSG, FirstParam(NextParam(T^.Data, 4)) + ' ' +
          RS_Handler_Dengon + ' ' + FirstParam(NextParam(T^.Data, 2)) + ' ' +
          FirstParam(NextParam(T^.Data, 3)) + ' ' + NextParam(T^.Data, 5));
        T^.Data := FirstParam(T^.Data) + ' old ' + NextParam(T^.Data, 2);
      end;
    T := T^.Next;
  except
  end;
end;

procedure Handler_MsgServ_write; // write Sender Msg(空白含む)
begin
  if not IsLogged then Exit;
  if not CheckParams(2) then Exit;
  StrHash_AddEx(DB_MsgServ, AnsiLowerCase(FirstParam(GCmd.Cmd)) + ' new ' +
    DateTimeToStr(Now)
    + ' ' + AnsiLowerCase(User^.UserName) + ' "' + NextParam(GCmd.Cmd) + '"');
  // T^.Data := 'Receiver yyyy/Mm/dd Hh:mm:Ss Sender "Msg"';
  if User^.Server = nil then
    WriteAllServers(MSG_CLIENT_MSGSERV_WRITE, User^.UserName, GCmd.Cmd);
  if Local <> nil then
    Local.Exec(MSG_SERVER_NOSUCH, '<MsgServ> ' + RS_Handler_MsgServ_write);
end;

procedure Handler_MsgServ_Delete; // Delete date Time <sender> "Msg"
var
  B: Boolean;
begin
  if not IsLogged then Exit;
  if not CheckParams(4) then Exit;
  B := StrHash_Delete(DB_MsgServ, AnsiLowerCase(User^.UserName) + ' old ' +
    GCmd.Cmd, False);
  if User^.Server = nil then
    WriteAllServers(MSG_CLIENT_MSGSERV_DELETE, User^.UserName, GCmd.Cmd);
  if Local <> nil then
    if B then
      Local.Exec(MSG_CLIENT_PRIVMSG, 'MsgServ ' + RS_Handler_MsgServ_Delete)
    else
      Local.Exec(MSG_CLIENT_PRIVMSG, 'MsgServ ' + RS_Handler_MsgServ_NoSuch);
end;

procedure Handler_MsgServ_Clear; // Clear
var
  T, Next_T: PStringHashItem;
begin
  if not IsLogged then Exit;
  T := DB_MsgServ.First;
  while T <> nil do
  try
    Next_T := T^.Next;
    if FirstParam(T^.Data) = AnsiLowerCase(User^.UserName) then
      StrHash_Delete(DB_MsgServ, T^.Data, False);
    T := Next_T;
  except
  end;
  if User^.Server = nil then
    WriteAllServers(MSG_CLIENT_MSGSERV_CLEAR, User^.UserName, GCmd.Cmd);
  if Local <> nil then
    Local.Exec(MSG_CLIENT_PRIVMSG, 'MsgServ ' + RS_Handler_MsgServ_Clear);
end;

procedure Handler_MsgServ;
var
  Action: string;
begin
  Query := queryMsgServ;
  if not IsLogged then Exit;
  if not IsLocal then Exit;
  if not Enable_Msgserv then
  begin
    Local.Exec(MSG_CLIENT_PRIVMSG, 'MsgServ ' + RS_Handler_MsgServ_Offline);
    Exit;
  end;
  SplitString(GCmd.Cmd, HList);
  if HList.Count < 2 then
    HList.Add('help');
  Action := LowerCase(HList.Strings[1]);
  GCmd.Cmd := NextParamEx(GCmd.Cmd, 2);
  GCmd.Id := 0;
  if (Action = 'readall') or (Action = 'a') then
    GCmd.Id := MSG_CLIENT_MSGSERV_READALL
  else if (Action = 'read') or (Action = 'r') then
    GCmd.Id := MSG_CLIENT_MSGSERV_READNEW
  else if (Action = 'write') or (Action = 'w') then
    GCmd.Id := MSG_CLIENT_MSGSERV_WRITE
  else if (Action = 'delete') or (Action = 'd') then
    GCmd.Id := MSG_CLIENT_MSGSERV_DELETE
  else if Action = 'clear' then
    GCmd.Id := MSG_CLIENT_MSGSERV_CLEAR
  else if Action = 'help' then
  begin
    Local.Exec(MSG_CLIENT_PRIVMSG, 'MsgServ ' + RS_MsgServ_Help_Title);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'MsgServ ' + RS_MsgServ_Help_begin);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'MsgServ ' + RS_MsgServ_Help_ReadAll);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'MsgServ ' + RS_MsgServ_Help_read);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'MsgServ ' + RS_MsgServ_Help_write);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'MsgServ ' + RS_MsgServ_Help_Delete);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'MsgServ ' + RS_MsgServ_Help_Clear);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'MsgServ ' + RS_MsgServ_Help_Help);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'MsgServ ' + RS_MsgServ_Help_end);
    Exit;
  end
  else
  begin
    Local.Exec(MSG_CLIENT_PRIVMSG, 'MsgServ ' + GetLangT(LNG_INVALIDCOMMAND,
      Action));
    Exit;
  end;
  case GCmd.Id of
    MSG_CLIENT_MSGSERV_READNEW: Handler_MsgServ_ReadNew;
    MSG_CLIENT_MSGSERV_READALL: Handler_MsgServ_ReadAll;
    MSG_CLIENT_MSGSERV_WRITE: Handler_MsgServ_write;
    MSG_CLIENT_MSGSERV_DELETE: Handler_MsgServ_Delete;
    MSG_CLIENT_MSGSERV_CLEAR: Handler_MsgServ_Clear;
  end;
end;

procedure Handler_NickServ;
var
  Action: string;
begin
  Query := queryNickServ;
  if not IsLogged then Exit;
  if not IsLocal then Exit;
  if HList.Count < 2 then
    HList.Add('help');
  Action := LowerCase(HList.Strings[1]);
  GCmd.Cmd := NextParamEx(GCmd.Cmd, 2);
  GCmd.Id := 0;
  if Action = 'ghost' then
    GCmd.Id := MSG_CLIENT_GHOST
  else if Action = 'pass' then
    GCmd.Id := MSG_CLIENT_CHANGE_PASS
  else if (Action = 'usermode') or (Action = 'um') then
    GCmd.Id := MSG_CLIENT_USER_MODE
  else if Action = 'help' then
  begin
    Local.Exec(MSG_CLIENT_PRIVMSG, 'NickServ ' + RS_NickServ_Help_Title);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'NickServ ' + RS_NickServ_Help_begin);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'NickServ ' + RS_NickServ_Help_Ghost);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'NickServ ' + RS_NickServ_Help_Help);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'NickServ ' + RS_NickServ_Help_Pass);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'NickServ ' + RS_NickServ_Help_Usermode);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'NickServ ' + RS_NickServ_Help_end);
    Exit;
  end
  else
  begin
    Local.Exec(MSG_CLIENT_PRIVMSG, 'NickServ ' + GetLangT(LNG_INVALIDCOMMAND,
      Action));
    Exit;
  end;
  if GCmd.Id <> 0 then
    ProcessCommand(Local, queryNickServ);
end;

procedure Handler_ChanServ;
var
  Action: string;
begin
  Query := queryChanServ;
  if not IsLogged then Exit;
  if not IsLocal then Exit;
  if HList.Count < 2 then
    HList.Add('help');
  Action := LowerCase(HList.Strings[1]);
  GCmd.Cmd := NextParamEx(GCmd.Cmd, 2);
  GCmd.Id := 0;
  if Action = 'ban' then
    GCmd.Id := MSG_CLIENT_CHANNEL_BAN
  else if Action = 'unban' then
    GCmd.Id := MSG_CLIENT_CHANNEL_UNBAN
  else if Action = 'banclear' then
    GCmd.Id := MSG_CLIENT_CHANNEL_CLEAR_BANS
  else if Action = 'banlist' then
    GCmd.Id := MSG_CLIENT_CHANNEL_BAN_LIST
  else if Action = 'clear' then
    GCmd.Id := MSG_CLIENT_CLEAR_CHANNEL
  else if Action = 'kick' then
    GCmd.Id := MSG_CLIENT_KICK
  else if Action = 'topic' then
    GCmd.Id := MSG_SERVER_TOPIC
  else if Action = 'limit' then
    GCmd.Id := MSG_CLIENT_CHANNEL_LIMIT
  else if Action = 'drop' then
    GCmd.Id := MSG_CLIENT_DROP_CHANNEL
  else if Action = 'op' then
    GCmd.Id := MSG_CLIENT_OP
  else if Action = 'deop' then
    GCmd.Id := MSG_CLIENT_DEOP
  else if Action = 'wallop' then
    GCmd.Id := MSG_CLIENT_CHANNEL_WALLOP
  else if Action = 'invite' then
    GCmd.Id := MSG_CLIENT_CHANNEL_INVITE_OPENNAP
  else if Action = 'mode' then
    GCmd.Id := MSG_CLIENT_CHANNEL_MODE
  else if Action = 'muzzle' then
    GCmd.Id := MSG_CLIENT_CHANNEL_MUZZLE
  else if Action = 'unmuzzle' then
    GCmd.Id := MSG_CLIENT_CHANNEL_UNMUZZLE
  else if Action = 'unvoice' then
    GCmd.Id := MSG_CLIENT_CHANNEL_UNVOICE
  else if Action = 'voice' then
    GCmd.Id := MSG_CLIENT_CHANNEL_VOICE
  else if Action = 'level' then
    GCmd.Id := MSG_CLIENT_SET_CHAN_LEVEL
  else if Action = 'help' then
  begin
    Local.Exec(MSG_CLIENT_PRIVMSG, 'ChanServ ' + RS_ChanServ_Help_Title);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'ChanServ ' + RS_ChanServ_Help_begin);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'ChanServ ' + RS_ChanServ_Help_Ban);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'ChanServ ' + RS_ChanServ_Help_BanClear);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'ChanServ ' + RS_ChanServ_Help_BanList);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'ChanServ ' + RS_ChanServ_Help_Clear);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'ChanServ ' + RS_ChanServ_Help_Deop);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'ChanServ ' + RS_ChanServ_Help_Drop);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'ChanServ ' + RS_ChanServ_Help_Help);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'ChanServ ' + RS_ChanServ_Help_Invite);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'ChanServ ' + RS_ChanServ_Help_Kick);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'ChanServ ' + RS_ChanServ_Help_Level);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'ChanServ ' + RS_ChanServ_Help_Limit);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'ChanServ ' + RS_ChanServ_Help_Mode);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'ChanServ ' + RS_ChanServ_Help_Muzzle);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'ChanServ ' + RS_ChanServ_Help_Op);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'ChanServ ' + RS_ChanServ_Help_Topic);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'ChanServ ' + RS_ChanServ_Help_Unban);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'ChanServ ' + RS_ChanServ_Help_UnMuzzle);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'ChanServ ' + RS_ChanServ_Help_UnVoice);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'ChanServ ' + RS_ChanServ_Help_Voice);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'ChanServ ' + RS_ChanServ_Help_Wallop);
    Local.Exec(MSG_CLIENT_PRIVMSG, 'ChanServ ' + RS_ChanServ_Help_end);
    Exit;
  end
  else
  begin
    Local.Exec(MSG_CLIENT_PRIVMSG, 'ChanServ ' + GetLangT(LNG_INVALIDCOMMAND,
      Action));
    Exit;
  end;
  if GCmd.Id <> 0 then
    ProcessCommand(Local, queryChanServ);
end;

procedure Handler_PrivateMessage;
var
  User2: POnlineUser;
  L2: TLocalUser;
  Str, Msg: string;
begin
  Tmp_Pos := 270;
  if not IsLogged then Exit;
  if not CheckParams(1) then Exit;
  Str := AnsiLowerCase(HList.Strings[0]);
  Tmp_Pos := 271;
  if Str = 'operserv' then
  begin
    if User^.Level > napUserUser then
      Handler_OperServ
    else
      Exec(User, MSG_SERVER_PRIVMSG, 'OperServ ' + GetLangT(LNG_ACCESS));
    Exit;
  end;
  if Str = 'nickserv' then
  begin
    Handler_NickServ;
    Exit;
  end;
  if Str = 'chanserv' then
  begin
    Handler_ChanServ;
    Exit;
  end;
  if Str = 'msgserv' then
  begin
    Handler_MsgServ;
    Exit;
  end;
  Tmp_Pos := 272;
  Msg := Copy(NextParamEx(GCmd.Cmd), 1, Max_Privmsg_Len);
  if Trim(Msg) = '' then Exit;
  if not Allow_Rtf_Code and (Pos('{\rtf', Msg) > 0) then Exit;
  if (Msg = '// WantQueue') then
  begin
    Tmp_Pos := 122621;
    if User^.Level = napUserLeech then
    begin
      Exec(User, MSG_SERVER_UPLOAD_FAILED, GCmd.Cmd);
      Exec(User, MSG_SERVER_NOSUCH, Format(RS_Handler_LeechQueue, [Levels[0],
        Levels[0]]));
      Exit;
    end;
    Tmp_Pos := 122622;
    if (Local <> nil) then
    begin
      if not CheckWinMX(Local) then Exit;
      // -----------------------------------Wantqueueカウント.Mods+, Friendもやる
      Tmp_Pos := 122623;
      User2 := DB_Online.FindUser(Str);
      Tmp_Pos := 122624;
      if User2 = nil then
      begin
        UserIsOffline(Str, True);
        Exit;
      end;
      Tmp_Pos := 122625;
      if User2^.Server = nil then
        L2 := FindLocalUser(User2^.UserName)
      else
        L2 := nil;
      Tmp_Pos := 122626;
      Inc(Local.WantQueuep3m); // WQ元がローカルならとりあえずカウント
      Tmp_Pos := 122627;
      if (L2 <> nil) and StrHash_FindString(L2.Hotlist,
        AnsiLowerCase(Local.Nick), True) then
        Dec(Local.WantQueuep3m);
          // WQ元とWQ先がともにローカルでホットリストにWQ元がいるときは戻す
      // -----------------------------------連キューブロック
      Tmp_Pos := 122628;
      if Local.Level < napUserModerator then
        if not StrHash_FindString(DB_Friends, Local.Nick, True) then
          if (WQFloodBlock_Count <> 0) and (Local.WantQueuep3m >
            WQFloodBlock_Count) then
          begin
            // Local.Exec(MSG_SERVER_NOSUCH, RS_Handler_TooMuchQueue);
            case WQFloodBlock_Method of
              // Block: Local.Exec(MSG_SERVER_NOSUCH,
              // Format(RS_Handler_BlockTooMuchQueue, [Local.WantQueuep3m, WQFloodBlock_Count]));
              Leech:
                begin
                  Local.Exec(MSG_SERVER_NOSUCH, Format(RS_Handler_SetLeech,
                    [Levels[0]]));
                  Local.Data^.Level := napUserLeech;
                  RegisterUser(User, ServerName_T);
                  Wallop(MSG_SERVER_NOSUCH, wallopLevel, GetLangT(LNG_LEVEL1,
                    ServerName_T,
                    Local.Nick, Level2Str(napUserLeech),
                      IntToStr(Ord(napUserLeech))), False);
                end;
              Ban:
                begin
                  BanUser(Local.Nick, ServerName_T, WQFloodBlock_Bantime,
                    Format(RS_Handler_BanTooMuchQueue, [Local.Nick]), True);
                  AddReconnector(Decode_Ip(Local.Ip));
                  DisconnectUser(Local, '', GetLangT(LNG_KILLED, Local.Nick,
                    Local.Software,
                    'Server ' + ServerName_T) + RS_Handler_KillTooMuchQueue,
                      'Handler_PrivateMessage', False);
                end;
            end;
            Exit;
          end;
    end;
  end;
  Tmp_Pos := 273;
  User2 := DB_Online.FindUser(Str);
  if User2 = nil then
  begin
    UserIsOffline(Str, True);
    if Enable_Msgserv then
    begin
      GCmd.Cmd := 'Msgserv write ' + Str + ' ' + Msg;
      SplitString(GCmd.Cmd, HList);
      Handler_MsgServ;
    end;
    Exit;
  end;
  if userHidePM in User2^.State then Exit;
  if (User^.Server = nil) and (userMuzzled in User^.State) then
    if User2^.Level < napUserModerator then
    begin
      PermissionDenied('', True);
      Exit;
    end;
  if User2^.Level > napUserUser then
    if userCloaked in User2^.State then
      if User^.Level < napUserModerator then
      begin
        UserIsOffline(Str, True);
        Exit;
      end;
  Tmp_Pos := 274;
  if User2^.Server <> nil then
  begin
    if Local = nil then Exit;
    User2^.Server.Exec(GCmd.Id, User^.UserName + ' ' + GCmd.Cmd);
    Exit;
  end;
  if User^.Level < napUserModerator then
  begin
    L2 := User2^.Local;
    if L2 <> nil then
      if StrHash_FindString(L2.Ignored, AnsiLowerCase(User^.UserName), False)
        then
      begin
        UserIsOffline(HList.Strings[0], True);
        Exit;
      end;
  end;
  Tmp_Pos := 275;
  Exec(User2, GCmd.Id, User^.UserName + ' ' + Msg);
end;

procedure Handler_PrivateMessageAnonymous;
var
  User2: POnlineUser;
  Str, Msg: string;
begin
  Tmp_Pos := 7270;
  if User^.Level < napUserElite then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  if not IsLogged then Exit;
  if not CheckParams(1) then Exit;
  Str := AnsiLowerCase(HList.Strings[0]);
  Tmp_Pos := 271;
  Msg := Copy(NextParamEx(GCmd.Cmd), 1, Max_Privmsg_Len);
  if Trim(Msg) = '' then Exit;
  Tmp_Pos := 7271;
  User2 := DB_Online.FindUser(Str);
  if User2 = nil then
  begin
    UserIsOffline(Str, True);
    Exit;
  end;
  if User2^.Level > napUserUser then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  Tmp_Pos := 7272;
  GCmd.Id := MSG_CLIENT_PRIVMSG;
  if User2^.Server <> nil then
  begin
    if Local = nil then Exit;
    User2^.Server.Exec(GCmd.Id, 'Server' + ' ' + GCmd.Cmd);
    Exit;
  end;
  Exec(User2, GCmd.Id, 'Server' + ' ' + Msg);
end;

procedure Handler_Resume;
begin
  if not IsLocal then Exit;
  Local.Exec(MSG_SERVER_RESUME_MATCH_END, '');
end;

procedure Handler_GetData;
var
  User2: POnlineUser;
begin
  if not IsLogged then Exit;
  case GCmd.Id of
    MSG_CLIENT_USERSPEED:
      begin
        if not CheckParams(1) then Exit;
        User2 := DB_Online.FindUser(GCmd.Cmd);
        if User2 = nil then
          UserIsOffline(GCmd.Cmd, True)
        else
          Exec(User, MSG_SERVER_USER_SPEED, User2^.UserName + ' ' +
            IntToStr(Ord(User2^.Speed)));
      end;
  end;
end;

function ShareFile(FileName, Folder: string; Rec: TShare;
  Mime: Integer): Boolean;
var
  Item: PShare;
  Id, Index: Integer;
  Sharing: Boolean;
begin
  Tmp_Pos := 280;
  Result := True;
  if not Allow_Share then Exit;
  if Mime = TYPE_INVALID then
  begin
    Result := False;
    Exit;
  end;
{$I CheckSync.pas}
  Tmp_Pos := 281;
  if Share_Nomodem then
    if (User^.Speed < napSpeed64ISDN) and (User^.Speed <> napSpeedUnknown) then Exit; // Modem sharing Disabled
  case Mime of
    TYPE_AUDIO: Id := SHARED_AUDIO;
    TYPE_VIDEO: Id := SHARED_VIDEO;
    TYPE_TEXT: Id := SHARED_TEXT;
    TYPE_IMAGE: Id := SHARED_IMAGE;
    TYPE_APP: Id := SHARED_APP;
    TYPE_CD: Id := SHARED_CD;
  else // MP3/WMA/OGG
    case opBitrate(Rec.Options) of
      320: if not Share_320 then
          Id := SHARED_INVALID
        else
          Id := SHARED_320;
      256: if not Share_256 then
          Id := SHARED_INVALID
        else
          Id := SHARED_256;
      224: if not Share_224 then
          Id := SHARED_INVALID
        else
          Id := SHARED_224;
      192: if not Share_192 then
          Id := SHARED_INVALID
        else
          Id := SHARED_192;
      160: if not Share_160 then
          Id := SHARED_INVALID
        else
          Id := SHARED_160;
      128: if not Share_128 then
          Id := SHARED_INVALID
        else
          Id := SHARED_128;
      112: if not Share_112 then
          Id := SHARED_INVALID
        else
          Id := SHARED_112;
      96: if not Share_96 then
          Id := SHARED_INVALID
        else
          Id := SHARED_OTHER;
      80: if not Share_80 then
          Id := SHARED_INVALID
        else
          Id := SHARED_OTHER;
      64: if not Share_64 then
          Id := SHARED_INVALID
        else
          Id := SHARED_64;
      56: if not Share_56 then
          Id := SHARED_INVALID
        else
          Id := SHARED_OTHER;
      48: if not Share_48 then
          Id := SHARED_INVALID
        else
          Id := SHARED_OTHER;
      40: if not Share_40 then
          Id := SHARED_INVALID
        else
          Id := SHARED_OTHER;
      32: if not Share_32 then
          Id := SHARED_INVALID
        else
          Id := SHARED_OTHER;
      24: if not Share_24 then
          Id := SHARED_INVALID
        else
          Id := SHARED_OTHER;
    else if not Share_Unknown then
      Id := SHARED_INVALID
    else
      Id := SHARED_OTHER;
    end;
    if Id = SHARED_INVALID then
    begin
      if ShareInform and CanSendError(User, Query) then
        Local.Exec(MSG_SERVER_NOSUCH, GetLangT(LNG_NOSHARE_BITRATE,
          opBitrate(Rec.Options)));
      Exit;
    end;
  end;
  if User^.DataPort = 0 then
    Inc(Id, SHARED_FIREWALL);
  Tmp_Pos := 282;
  try
    // Checking duplicate
    if Local.Shared = nil then
      Local.Shared := TShareList.Create;
    Index := Local.Shared.AddFolder(Folder);
    if Index > 32767 then
    begin // Too many Directories
      Local.Shared.Dirs.Delete(Index);
      Result := False;
      Exit;
    end;
    Rec.Index := Index;
    if Share_Checkdup then
      if Local.Shared.FindFile(Rec.Index, Rec.Name) <> -1 then
      begin
        Result := False;
        Exit;
      end;
    Tmp_Pos := 283;
    // Extracting keywords
    Item := CreateShareItem;
    Item^.Name := Rec.Name;
    Item^.Crc := StringCRC(Folder + Rec.Name, False);
    Item^.Size := Rec.Size;
    Item^.Options := Rec.Options;
    Item^.Index := Rec.Index;
    Item^.User := Local;
    Tmp_Pos := 1262;
    if not GetKeywords(FileName, Id, Local, Item) then
    begin // Error extracting Keywords
      FreeShareItem(Item);
      Exit;
    end;
    Tmp_Pos := 1263;
    // Adding item
    Local.Shared.AddEx(Item);
    Sharing := opIsShared(Item^.Options);
    Tmp_Pos := 284;
    if Sharing then
    begin
      Inc(User^.Shared);
      case Mime of
        TYPE_MP3: Inc(Local.Shared_Mp3);
        TYPE_AUDIO: Inc(Local.Shared_Audio);
        TYPE_VIDEO: Inc(Local.Shared_Video);
        TYPE_IMAGE: Inc(Local.Shared_Images);
        TYPE_APP: Inc(Local.Shared_Apps);
        TYPE_CD: Inc(Local.Shared_Cd);
        TYPE_TEXT:
          begin
            if Nocount_Text then
              Dec(User^.Shared);
            Inc(Local.Shared_Text);
          end;
      end;
      if Auto_FriendAdd then
        if User^.Shared = Share_Auto_FriendAdd_Minimum then
          if not StrHash_FindString(DB_Friends, User^.UserName, True) then
            StrHash_Add(DB_Friends, User^.UserName);
      if Force_Enter then
        Local.Join_Delay := INITIAL_JOIN_DELAY;
      Inc(Total_Files);
      Inc(Local_Files);
      Inc(Total_Bytes, Rec.Size);
      Inc(Local_Bytes, Rec.Size);
      Inc(Local.Shared_Size, Rec.Size);
      Local.LocalState := Local.LocalState + [locNeedsUpdate];
      if Local_Files_Max < Local_Files then
        Local_Files_Max := Local_Files;
      if Total_Files_Max < Total_Files then
        Total_Files_Max := Total_Files;
      if Local_Bytes_Max < Local_Bytes then
        Local_Bytes_Max := Local_Bytes;
      if Total_Bytes_Max < Total_Bytes then
        Total_Bytes_Max := Total_Bytes;
    end
    else
      Inc(Local.Shared_Blocked);
    Tmp_Pos := 285;
  except
    on E: Exception do
      DebugLog('Exception in ShareFile (Pos=' + IntToStr(Tmp_Pos) + ') : ' +
        E.Message);
  end;
end;

procedure Handler_Share(Dir: string; Valid_Dir: Boolean);
var
  Rec: TShare;
  Folder_Name, Str, FileName, File_Ext, Ext_Fake: string;
  I, J, Mime: Integer;
  K, L, Fsize: Int64;
  Op_Bitrate, Op_Freq, Op_Time: Integer;
begin
  Tmp_Pos := 290;
  // Valid_Dir = False if this Proc is Called from Handler_ShareDir
  if not Allow_Share then Exit;
  if Network_Hub then Exit;
  if not IsLocal then Exit;
  if not IsLogged then Exit;
  if Query <> queryNormal then Exit;
  if not Valid_Dir then
    SplitString(GCmd.Cmd, HList);
  if HList.Count < 6 then
  begin
    Error(GetLangT(LNG_INVALIDARGS), True);
    Exit;
  end;
  if (Dir = '') and not Valid_Dir then
    Local.Detector := Local.Detector + [loc100];
  if HList.Strings[1] = '0' then
    Local.Detector := Local.Detector + [locMD5Zero]
  else if HList.Strings[1] = '00000000000000000000000000000000' then
    Local.Detector := Local.Detector + [locMD5Zeros]
  else
    Local.Detector := Local.Detector + [locMD5NonZero];
  Tmp_Pos := 12262;
  if Autoban_Incomplete and (Pos('__INCOMPLETE___', HList.Strings[0]) <> 0) then
  begin
    Inc(Local.Blocked_Incomplete);
    // if ShareInform and CanSendError(User, Query) then
      // Local.Exec(MSG_SERVER_NOSUCH, Format(RS_Handler_BlockIncomplete, [HList.Strings[0]]));
    Exit;
  end;
  Ext_Fake := ExtractFileExt(ChangeFileExt(HList.Strings[0], '')) +
    ExtractFileExt(HList.Strings[0]);
  if Disable_FakeExt and StrHash_FindString(Fakeext_List, Ext_Fake, True) then
  begin
    Inc(Local.Blocked_Fakeext);
    // if ShareInform and CanSendError(User, Query) then
      // Local.Exec(MSG_SERVER_NOSUCH, Format(RS_Handler_BlockFakeExt, [HList.Strings[0]]));
    Exit;
  end;
  Tmp_Pos := 291;
  FileName := Dir + HList.Strings[0];
  SplitFileName(FileName, Folder_Name, Rec.Name);
  // Checking FileNavigator
  if Copy(FileName, 1, 3) = '|||' then
  begin
    Str := ExtractFileName(Copy(FileName, 4, Length(FileName)));
    I := Length(ExtractFileExt(Str));
    Str := Copy(Str, 1, Length(Str) - I);
    I := Pos('|', Str);
    while I > 0 do
    begin
      Str[I] := '.';
      I := Pos('|', Str);
    end;
    FileName := Str;
  end;
  Tmp_Pos := 292;
  if MaxShare > 0 then
    if (User^.Shared >= MaxShare) or
      (Nocount_Text and (User^.Shared + Local.Shared_Text >= MaxShare)) then
    begin
      Inc(Local.Blocked_Toomany);
      // if ShareInform and CanSendError(User, Query) then
      // Local.Exec(MSG_SERVER_NOSUCH, GetLangT(LNG_SHARE_LIMIT, FileName, MaxShare));
      Exit;
    end;
  Tmp_Pos := 293;
  if MaxShare_Total > 0 then
    if Local_Files >= MaxShare_Total then
    begin
      if ShareInform and CanSendError(User, Query) then
        Local.Exec(MSG_SERVER_NOSUCH, GetLangT(LNG_SHARE_TOTAL_LIMIT,
          FileName));
      Exit;
    end;
  if (Memory_Limit > 0) and Win98 then
    if AllocMemSize >= Memory_Limit then
    begin
      if ShareInform and CanSendError(User, Query) then
        Local.Exec(MSG_SERVER_NOSUCH, GetLangT(LNG_SHARE_MEMORY_LIMIT,
          FileName));
      Exit;
    end;
  Tmp_Pos := 294;
  if Length(FileName) < MinFileName then
  begin
    Inc(Local.Blocked_Tooshort);
    // if ShareInform and CanSendError(User, Query) then
    // Local.Exec(MSG_SERVER_NOSUCH, GetLangT(LNG_SHARE_SHORT, FileName));
    Exit;
  end;
  Fsize := StrToInt64Def(HList.Strings[2], 0);
  Rec.Size := StrToInt64Def(HList.Strings[2], 0);
  Op_Bitrate := StrToIntDef(HList.Strings[3], WINMX_Bitrate);
  Op_Freq := StrToIntDef(HList.Strings[4], WINMX_FREQ);
  Op_Time := StrToIntDef(HList.Strings[5], WINMX_TIME);
  Tmp_Pos := 295;
  if (Op_Bitrate = 0) or (Op_Freq = 0) then // Fixing Bug in SunshineUE
  begin
    Op_Bitrate := WINMX_Bitrate;
    Op_Freq := WINMX_FREQ;
    Op_Time := WINMX_TIME;
  end;
  File_Ext := LowerCase(ExtractFileExt(FileName));
  if Length(File_Ext) > 0 then
    if File_Ext[1] = '.' then
      File_Ext := Copy(File_Ext, 2, Length(File_Ext));
  Tmp_Pos := 296;
  Mime := GetType(File_Ext);
  if Mime = TYPE_INVALID then
  begin
    Inc(Local.Blocked_Other);
    if ShareInform and CanSendError(User, Query) then
      Local.Exec(MSG_SERVER_NOSUCH, GetLangT(LNG_SHARE_ERROR, FileName));
    Exit;
  end;
  if Mime = TYPE_MP3 then
    if Op_Time < MinDuration then
    begin
      Inc(Local.Blocked_Tooshortmp3);
      // if ShareInform and CanSendError(User, Query) then
      // Local.Exec(MSG_SERVER_NOSUCH, GetLangT(LNG_SHARE_TIME, FileName));
      Exit;
    end;
  Tmp_Pos := 297;
  case Mime of
    TYPE_AUDIO:
      begin
        I := MaxShare_Audio;
        J := Local.Shared_Audio;
      end;
    TYPE_VIDEO:
      begin
        I := MaxShare_Video;
        J := Local.Shared_Video;
      end;
    TYPE_IMAGE:
      begin
        I := MaxShare_Image;
        J := Local.Shared_Images;
      end;
    TYPE_APP:
      begin
        I := MaxShare_App;
        J := Local.Shared_Apps;
      end;
    TYPE_CD:
      begin
        I := MaxShare_Cd;
        J := Local.Shared_Cd;
      end;
    TYPE_TEXT:
      begin
        I := MaxShare_Text;
        J := Local.Shared_Text;
      end;
  else
    I := MaxShare_Mp3;
    J := Local.Shared_Mp3;
  end;
  if J >= I then
  begin
    Inc(Local.Blocked_Toomany);
    // if ShareInform and CanSendError(User, Query) then
    // Local.Exec(MSG_SERVER_NOSUCH, GetLangT(LNG_SHARE_LIMIT, FileName, I));
    Exit;
  end;
  Tmp_Pos := 298;
  case Mime of
    TYPE_AUDIO:
      begin
        K := MinFileSize_Audio;
        L := MaxFileSize_Audio;
      end;
    TYPE_VIDEO:
      begin
        K := MinFileSize_Video;
        L := MaxFileSize_Video;
      end;
    TYPE_IMAGE:
      begin
        K := MinFileSize_Image;
        L := MaxFileSize_Image;
      end;
    TYPE_APP:
      begin
        K := MinFileSize_App;
        L := MaxFileSize_App;
      end;
    TYPE_CD:
      begin
        K := MinFileSize_Cd;
        L := MaxFileSize_Cd;
      end;
    TYPE_TEXT:
      begin
        K := MinFileSize_Text;
        L := MaxFileSize_Text;
      end;
  else
    K := MinFileSize_Mp3;
    L := MaxFileSize_Mp3;
  end;
  if K > 0 then
    if FSize < K then
    begin
      Inc(Local.Blocked_Toosmall);
      // if ShareInform and CanSendError(User, Query) then
      // Local.Exec(MSG_SERVER_NOSUCH, GetLangT(LNG_SHARE_SMALL, FileName));
      Exit;
    end;
  if L > 0 then
    if FSize > L then
    begin
      Inc(Local.Blocked_Toolarge);
      // if ShareInform and CanSendError(User, Query) then
      // Local.Exec(MSG_SERVER_NOSUCH, GetLangT(LNG_SHARE_LARGE, FileName));
      Exit;
    end;
  Rec.Options := SetOption(Op_Bitrate, Op_Freq, Op_Time, 0, True);
  Tmp_Pos := 299;
  if not ShareFile(FileName, Folder_Name, Rec, Mime) then
  begin
    Inc(Local.Blocked_Other);
    if ShareInform and CanSendError(User, Query) then
      Local.Exec(MSG_SERVER_NOSUCH, GetLangT(LNG_SHARE_ERROR, FileName));
  end;
end;

procedure Handler_ShareDir;
var
  I: Integer;
  Dir: string;
begin
  Tmp_Pos := 300;
  if not Allow_Share then Exit;
  if Network_Hub then Exit;
  if not IsLocal then Exit;
  if not IsLogged then Exit;
  // if User^.Level = napUserLeech then Exit;
  if Query <> queryNormal then Exit;
  Tmp_Pos := 301;
  if not CheckParams(7, False) then
  begin
    if HList.Count = 1 then // Possible winmx
      CheckWinMX(Local);
    Exit;
  end;
  Local.Detector := Local.Detector + [loc870];
  Tmp_Pos := 302;
  Dir := HList.Strings[0];
  if not (locMacDir in Local.Detector) then
  begin
    if AnsiLowerCase(Dir)[1] in ['a'..'z'] then
    begin
      if Copy(Dir, 2, 2) <> ':\' then
        Local.Detector := Local.Detector + [locMacDir];
    end
    else if not ((AnsiLowerCase(Dir)[1] in ['\']) and (AnsiLowerCase(Dir)[2] in
      ['\'])) then
      if not (AnsiLowerCase(Dir)[1] in ['/']) then
        Local.Detector := Local.Detector + [locMacDir];
  end;
  HList.Delete(0);
  Tmp_Pos := 303;
  while HList.Count > 5 do
  begin
    if not Local.Logged then Exit;
    if not Running then Exit;
    if MaxShare > 0 then
      if User^.Shared >= MaxShare then Exit;
    Tmp_Pos := 304;
    Handler_Share(Dir, True);
    Tmp_Pos := 305;
    for I := 0 to 5 do
      HList.Delete(0);
{$I CheckSync.pas}
  end;
end;

procedure Handler_ShareFile;
var
  Rec: TShare;
  I, J, Mime: Integer;
  K, L, Fsize: Int64;
  Folder, File_Ext, Ext_Fake: string;
begin
  Tmp_Pos := 310;
  if not Allow_Share then Exit;
  if Network_Hub then Exit;
  if not IsLocal then Exit;
  if not IsLogged then Exit;
  if Query <> queryNormal then Exit;
  // if User^.Level = napUserLeech then Exit;
  if not CheckParams(4) then Exit;
  Local.Detector := Local.Detector + [loc10300];
  if HList.Strings[1] = '0' then
    Local.Detector := Local.Detector + [locMD5Zero]
  else if HList.Strings[1] = '00000000000000000000000000000000' then
    Local.Detector := Local.Detector + [locMD5Zeros]
  else
    Local.Detector := Local.Detector + [locMD5NonZero];
  Tmp_Pos := 12262;
  if Autoban_Incomplete and (Pos('__INCOMPLETE___', HList.Strings[0]) <> 0) then
  begin
    Inc(Local.Blocked_Incomplete);
    Exit;
  end;
  Ext_Fake := ExtractFileExt(ChangeFileExt(HList.Strings[0], '')) +
    ExtractFileExt(HList.Strings[0]);
  if Disable_FakeExt and StrHash_FindString(Fakeext_List, Ext_Fake, True) then
  begin
    Inc(Local.Blocked_Fakeext);
    Exit;
  end;
  Tmp_Pos := 292;
  if MaxShare > 0 then
    if (User^.Shared >= MaxShare) or
      (Nocount_Text and (User^.Shared + Local.Shared_Text >= MaxShare)) then
    begin
      Inc(Local.Blocked_Toomany);
      Exit;
    end;
  Tmp_Pos := 293;
  if MaxShare_Total > 0 then
    if Local_Files >= MaxShare_Total then
    begin
      if ShareInform and CanSendError(User, Query) then
        Local.Exec(MSG_SERVER_NOSUCH, GetLangT(LNG_SHARE_TOTAL_LIMIT,
          HList.Strings[0]));
      Exit;
    end;
  if (Memory_Limit > 0) and Win98 then
    if AllocMemSize >= Memory_Limit then
    begin
      if ShareInform and CanSendError(User, Query) then
        Local.Exec(MSG_SERVER_NOSUCH, GetLangT(LNG_SHARE_MEMORY_LIMIT,
          HList.Strings[0]));
      Exit;
    end;
  Tmp_Pos := 294;
  if Length(HList.Strings[0]) < MinFileName then
  begin
    Inc(Local.Blocked_Tooshort);
    Exit;
  end;
  Tmp_Pos := 311;
  Mime := StrToType(HList.Strings[3]);
  if Mime = TYPE_INVALID then Exit;
  if Mime = TYPE_MP3 then
  begin
    if ShareInform and CanSendError(User, Query) then
      Local.Exec(MSG_SERVER_NOSUCH, GetLangT(LNG_SHARE_10300));
    Exit;
  end;
  case Mime of
    TYPE_AUDIO:
      begin
        I := MaxShare_Audio;
        J := Local.Shared_Audio;
      end;
    TYPE_VIDEO:
      begin
        I := MaxShare_Video;
        J := Local.Shared_Video;
      end;
    TYPE_IMAGE:
      begin
        I := MaxShare_Image;
        J := Local.Shared_Images;
      end;
    TYPE_APP:
      begin
        I := MaxShare_App;
        J := Local.Shared_Apps;
      end;
    TYPE_CD:
      begin
        I := MaxShare_Cd;
        J := Local.Shared_Cd;
      end;
    TYPE_TEXT:
      begin
        I := MaxShare_Text;
        J := Local.Shared_Text;
      end;
  else
    I := MaxShare_Mp3;
    J := Local.Shared_Mp3;
  end;
  Tmp_Pos := 312;
  if J >= I then
  begin
    Inc(Local.Blocked_Toomany);
    // if ShareInform and CanSendError(User, Query) then
    // Local.Exec(MSG_SERVER_NOSUCH, GetLangT(LNG_SHARE_LIMIT, HList.Strings[0], I));
    Exit;
  end;
  case Mime of
    TYPE_AUDIO:
      begin
        K := MinFileSize_Audio;
        L := MaxFileSize_Audio;
      end;
    TYPE_VIDEO:
      begin
        K := MinFileSize_Video;
        L := MaxFileSize_Video;
      end;
    TYPE_IMAGE:
      begin
        K := MinFileSize_Image;
        L := MaxFileSize_Image;
      end;
    TYPE_APP:
      begin
        K := MinFileSize_App;
        L := MaxFileSize_App;
      end;
    TYPE_CD:
      begin
        K := MinFileSize_Cd;
        L := MaxFileSize_Cd;
      end;
    TYPE_TEXT:
      begin
        K := MinFileSize_Text;
        L := MaxFileSize_Text;
      end;
  else
    K := MinFileSize_Mp3;
    L := MaxFileSize_Mp3;
  end;
  Tmp_Pos := 313;
  Rec.Size := StrToInt64Def(HList.Strings[1], 0);
  Fsize := StrToInt64Def(HList.Strings[1], 0);
  if K > 0 then
    if FSize < K then
    begin
      Inc(Local.Blocked_Toosmall);
      // if ShareInform and CanSendError(User, Query) then
      // Local.Exec(MSG_SERVER_NOSUCH, GetLangT(LNG_SHARE_SMALL, HList.Strings[0]));
      Exit;
    end;
  if L > 0 then
    if FSize > L then
    begin
      Inc(Local.Blocked_Toolarge);
      // if ShareInform and CanSendError(User, Query) then
      // Local.Exec(MSG_SERVER_NOSUCH, GetLangT(LNG_SHARE_LARGE, HList.Strings[0]));
      Exit;
    end;
  Tmp_Pos := 314;
  // Let's Make these Values compatible with WinMX and some Other clients
  Rec.Options := SetOption(WINMX_BITRATE, WINMX_FREQ, WINMX_TIME, 0, True);
  // if Length(HList.Strings[0]) < MinFileName then
  // begin
  //   if ShareInform and CanSendError(User, Query) then
  //    Local.Exec(MSG_SERVER_NOSUCH, GetLangT(LNG_SHARE_SHORT, HList.Strings[0]));
  //   Exit;
  // end;
  Tmp_Pos := 315;
  File_Ext := LowerCase(ExtractFileExt(HList.Strings[0]));
  if Length(File_Ext) > 0 then
    if File_Ext[1] = '.' then
      File_Ext := Copy(File_Ext, 2, Length(File_Ext));
  Tmp_Pos := 316;
  SplitFileName(HList.Strings[0], Folder, Rec.Name);
  ShareFile(HList.Strings[0], Folder, Rec, Mime);
end;

procedure Handler_Unshare(FileName: string = '');
var
  I, J: Integer;
  N: Int64;
  P: PShare;
  Kw: PKeyword;
  Shared: Boolean;
begin
  Tmp_Pos := 320;
  if not IsLocal then Exit;
  if not IsLogged then Exit;
  if GetTickCount - Local.Last_Seen < 30000 then
    Local.Detector := Local.Detector + [loc110];
  if Local.Shared = nil then Exit;
  if User^.Shared = 0 then
    if not Nocount_Text then
      Exit
    else if Local.Shared_Text = 0 then Exit;
  Tmp_Pos := 321;
  if FileName = '' then
  begin
    if Local.Shared <> nil then
    begin
      Dec(Total_Bytes, Local.Shared_Size);
      Dec(Local_Bytes, Local.Shared_Size);
      Dec(Total_Files, Local.Shared.Count);
      Dec(Local_Files, Local.Shared.Count);
      if Nocount_Text then
      begin
        Dec(Total_Files, Local.Shared_Text);
        Dec(Local_Files, Local.Shared_Text);
      end;
      Local.Shared.Clear;
      Local.LocalState := Local.LocalState + [locNeedsUpdate];
    end;
    Tmp_Pos := 322;
    User^.Shared := 0;
    Local.Shared_Mp3 := 0;
    Local.Shared_Audio := 0;
    Local.Shared_Video := 0;
    Local.Shared_Cd := 0;
    Local.Shared_Images := 0;
    Local.Shared_Text := 0;
    Local.Shared_Apps := 0;
    Local.Shared_Size := 0;
    Local.Shared_Blocked := 0;
    Exit;
  end;
  Tmp_Pos := 323;
  if Length(FileName) > 2 then
    if FileName[1] = '"' then
      FileName := Copy(FileName, 2, Length(FileName) - 2);
  if Local.Shared = nil then Exit;
  J := Local.Shared.FindFile(FileName);
  Tmp_Pos := 324;
  if J <> -1 then
  begin
    N := PShare(Local.Shared.Items[J])^.Size;
    P := Local.Shared.Items[J];
    Shared := opIsShared(P^.Options);
    Kw := P^.Keywords^[0];
    I := ID2Mime(Kw^.Id);
    Local.Shared.Delete(J);
    Local.Shared.ReIndex := True;
    if Shared then
    begin
      Dec(Total_Bytes, N);
      Dec(Local_Bytes, N);
      Dec(Local.Shared_Size, N);
      Dec(Total_Files);
      Dec(Local_Files);
      Dec(User^.Shared);
      Local.LocalState := Local.LocalState + [locNeedsUpdate];
      case I of
        TYPE_AUDIO: Dec(Local.Shared_Audio);
        TYPE_VIDEO: Dec(Local.Shared_Video);
        TYPE_TEXT: Dec(Local.Shared_Text);
        TYPE_IMAGE: Dec(Local.Shared_Images);
        TYPE_APP: Dec(Local.Shared_Apps);
        TYPE_CD: Dec(Local.Shared_Cd);
      else
        Dec(Local.Shared_Mp3);
      end;
    end
    else
      Dec(Local.Shared_Blocked);
  end;
end;

function Search(Inc_List, Exc_List: TNapCmdList): Integer;
var
  I, J, Mime, Sync: Integer;
  Loop_B, Loop_Item: Integer;
  B: array[0..SHARED_ARRAY - 1] of Boolean;
  Match, Match2, Complete: Boolean;
  Str: string;
  Sh: PShare;
  User2: TLocalUser;
  Inc_List2, Exc_List2: TMyList;
  KwIndex, KwLength: Integer;
  Kw, Kw_Min: PKeyword;
  P: PKeywordItem;
  Sh_Bitrate, Sh_Time, Sh_Numwords: Word;
  Sh_Freq: LongWord;
  Sh_Shared: Boolean;
  Matchcount_Per_User: Integer; // 1ユーザーが連続してヒットした数をカウント
begin
  Tmp_Pos := 330;
  Result := 0;
{$I CheckSync.pas}
  if Local_Users < 1 then Exit;
  for I := 0 to SHARED_MAX do
    B[I] := False;
  // Checking file Types
  Tmp_Pos := 1235;
  case Search_Data.Mime of
    TYPE_AUDIO: B[SHARED_AUDIO] := True;
    TYPE_VIDEO: B[SHARED_VIDEO] := True;
    TYPE_TEXT: B[SHARED_TEXT] := True;
    TYPE_IMAGE: B[SHARED_IMAGE] := True;
    TYPE_APP: B[SHARED_APP] := True;
  else
    if Search_Data.Nonmp3 = False then
    begin
      Tmp_Pos := 1236;
      if Search_Data.Mime = TYPE_INVALID then
      begin
        B[SHARED_AUDIO] := True;
        B[SHARED_VIDEO] := True;
        B[SHARED_TEXT] := True;
        B[SHARED_IMAGE] := True;
        B[SHARED_APP] := True;
      end;
      B[SHARED_320] := Compare(Search_Data.Bitrate_Cmp, Search_Data.Bitrate,
        320);
      B[SHARED_256] := Compare(Search_Data.Bitrate_Cmp, Search_Data.Bitrate,
        256);
      B[SHARED_224] := Compare(Search_Data.Bitrate_Cmp, Search_Data.Bitrate,
        224);
      B[SHARED_192] := Compare(Search_Data.Bitrate_Cmp, Search_Data.Bitrate,
        192);
      B[SHARED_160] := Compare(Search_Data.Bitrate_Cmp, Search_Data.Bitrate,
        160);
      B[SHARED_128] := Compare(Search_Data.Bitrate_Cmp, Search_Data.Bitrate,
        128);
      B[SHARED_112] := Compare(Search_Data.Bitrate_Cmp, Search_Data.Bitrate,
        112);
      B[SHARED_64] := Compare(Search_Data.Bitrate_Cmp, Search_Data.Bitrate, 64);
      Tmp_Pos := 1237;
      if Search_Data.Bitrate_Cmp <> napEqual then
        B[SHARED_OTHER] := True
      else if Search_Data.Bitrate <> 320 then
        if Search_Data.Bitrate <> 256 then
          if Search_Data.Bitrate <> 224 then
            if Search_Data.Bitrate <> 192 then
              if Search_Data.Bitrate <> 160 then
                if Search_Data.Bitrate <> 128 then
                  if Search_Data.Bitrate <> 112 then
                    if Search_Data.Bitrate <> 64 then
                      B[SHARED_OTHER] := True;
      Tmp_Pos := 1238;
      for I := Inc_List.Count - 1 downto 0 do
      begin // Allow user to type File extension in search string
        J := GetType(PNapCmd(Inc_List.Items[I])^.Cmd);
        Tmp_Pos := 1239;
        case J of
          TYPE_AUDIO: B[SHARED_AUDIO] := True;
          TYPE_VIDEO: B[SHARED_VIDEO] := True;
          TYPE_IMAGE: B[SHARED_IMAGE] := True;
          TYPE_APP: B[SHARED_APP] := True;
          TYPE_CD: B[SHARED_CD] := True;
          TYPE_TEXT: B[SHARED_TEXT] := True;
        else
          J := -1;
        end;
        if J <> -1 then
        begin
          Inc_List.Delete(I);
          if Inc_List.Count < 1 then Exit;
        end;
      end;
    end;
  end;
  Tmp_Pos := 1240;
  if Search_Data.Nonmp3 then // if searching All non-mp3s
  begin
    for I := 0 to SHARED_MAX do
      if I < SHARED_MP3_MIN then
        B[I] := True
      else
        B[I] := False;
  end;
  for I := 0 to SHARED_MAX do
  begin
    if User^.DataPort > 0 then // Can search Firewalled users
      B[I + SHARED_FIREWALL] := B[I]
    else
      B[I + SHARED_FIREWALL] := False;
  end;
  Tmp_Pos := 332;
{$I CheckSync.pas}
  Inc_List2 := TMyList.Create;
  Exc_List2 := TMyList.Create;
  Sync := 0;
  Matchcount_Per_User := 0;
  for Loop_B := 0 to SHARED_ARRAY - 1 do
    if B[Loop_B] then
    begin
      // Clearing variables
      Inc_List2.Clear;
      Exc_List2.Clear;
      Kw_Min := nil;
      Match := True;
      Mime := ID2Mime(Loop_B);
      // Checking if All keywords Are in Database
      for I := 0 to Inc_List.Count - 1 do
        if Match then
        begin
          KwIndex := GetKeywordIndex(PNapCmd(Inc_List.Items[I])^.Cmd);
          KwLength := Length(PNapCmd(Inc_List.Items[I])^.Cmd);
          if KwIndex = KEYWORDS_NOINDEX then
            Kw := nil
          else
            Kw := KWList_FindItem(DB_Keywords[Loop_B, KwIndex, KwLength],
              PNapCmd(Inc_List.Items[I])^.Cmd);
          if Kw = nil then
            Match := False
          else
          begin
            if (Kw_Min = nil) or (Kw^.Count < Kw_Min^.Count) then
            begin // Found new Minimum item
              if Kw_Min <> nil then
                Inc_List2.Add(Kw_Min);
              Kw_Min := Kw;
            end
            else
              Inc_List2.Add(Kw);
          end;
        end;
      if Kw_Min = nil then
        Match := False; // Possible error???
      // Checking all Excluded keywords
      if Match then
        for I := 0 to Exc_List.Count - 1 do
        begin
          KwIndex := GetKeywordIndex(PNapCmd(Exc_List.Items[I])^.Cmd);
          KwLength := Length(PNapCmd(Exc_List.Items[I])^.Cmd);
          if KwIndex = KEYWORDS_NOINDEX then
            Kw := nil
          else
            Kw := KWList_FindItem(DB_Keywords[Loop_B, KwIndex, KwLength],
              PNapCmd(Exc_List.Items[I])^.Cmd);
          if Kw <> nil then
            Exc_List2.Add(Kw);
        end;
{$I CheckSync.pas}
      if Match then
      begin // All keywords Found. Checking each Item in Smallest list for all Keywords
        Complete := True;
        P := Kw_Min^.Complete;
        if P = nil then
        begin
          P := Kw_Min^.Incomplete;
          Complete := False;
        end;
        while P <> nil do
        begin
          for Loop_Item := 0 to ITEMS_PER_KEYWORD - 1 do
            if P^.Share[Loop_Item] <> nil then
            begin
              Inc(Sync);
              Sh := P^.Share[Loop_Item];
              SplitOption(Sh^.Options, Sh_Bitrate, Sh_Freq, Sh_Time,
                Sh_Numwords, Sh_Shared);
              // Checking item 'Sh'
              Match := Sh_Shared or (User^.Level > napUserModerator);
              if (Sh^.User = Local) or (Sh^.User = nil) then
                Match := False;
              for I := 0 to Inc_List2.Count - 1 do
                if Match then // Checking if All keywords Are persent
                begin
                  Match := False;
                  for J := 0 to Sh_Numwords - 1 do
                    if Sh^.Keywords^[J * 2] = Inc_List2.Items[I] then
                      Match := True;
                end;
              if Match then // Checking excluded Keywords
                for I := 0 to Exc_List2.Count - 1 do
                  if Match then
                    for J := 0 to Sh_Numwords - 1 do
                      if Sh^.Keywords^[J * 2] = Exc_List2.Items[I] then
                        Match := False;
              if (Sync mod 100) = 30 then
              begin
{$I CheckSync.pas}
              end;
              if Match then
              begin
                // All keywords match - Checking Bitrate/Freq/Size, Etc...
                if not Compare(Search_Data.Size_Cmp, Search_Data.Size, Sh^.Size)
                  then
                  Match := False
                else if (Mime = TYPE_AUDIO) or (Mime = TYPE_VIDEO) or (Mime =
                  TYPE_MP3) then
                begin
                  if not Compare(Search_Data.Bitrate_Cmp, Search_Data.Bitrate,
                    Sh_Bitrate) then
                    Match := False
                  else if not compare(Search_Data.Frequency_Cmp,
                    Search_Data.Frequency, Sh_Freq) then
                    Match := False
                  else if not compare(Search_Data.Time_Cmp, Search_Data.Time,
                    Sh_Time) then
                    Match := False;
                end;
                User2 := TLocalUser(Sh^.User);
                if Match then
                  if not Compare(Search_Data.Speed_Cmp, Ord(Search_Data.Speed),
                    Ord(User2.Data^.Speed)) then
                    Match := False;
                if Match then
                begin // Found a Match !!!
                  if Restrict_Hitperuser then
                    if (Loop_Item = 0) or
                      ((Loop_Item > 0) and (P^.Share[Loop_Item - 1] <> nil) and
                        ((P^.Share[Loop_Item - 1])^.User = Sh^.User)) then
                    begin
                      Inc(Matchcount_Per_User);
                      if Matchcount_Per_User > MaxHitPerUser then Continue;
                    end
                    else
                      Matchcount_Per_User := 1;
                  Tmp_Pos := 1247;
                  I := Sh^.Index;
                  if I <> -1 then
                    Str := PNapCmd(User2.Shared.Dirs.Items[I])^.Cmd
                  else
                    Str := '';
                  if Disable_Nullip or (User.Level >= NapUserModerator) then
                    Str := '"' + Str + Sh^.Name + '" ' + Null_Md5 + ' ' +
                      IntToStr(Sh^.Size) + ' ' +
                      IntToStr(Sh_Bitrate) + ' ' + IntToStr(Sh_Freq) + ' ' +
                        IntToStr(Sh_Time) +
                      ' ' + User2.Nick + ' ' + IntToStr(User2.Ip) + ' ' +
                        IntToStr(Ord(User2.Data^.Speed))
                  else
                    Str := '"' + Str + Sh^.Name + '" ' + Null_Md5 + ' ' +
                      IntToStr(Sh^.Size) + ' ' +
                      IntToStr(Sh_Bitrate) + ' ' + IntToStr(Sh_Freq) + ' ' +
                        IntToStr(Sh_Time) +
                      ' ' + User2.Nick + ' ' + Null_Ip + ' ' +
                        IntToStr(Ord(User2.Data^.Speed));
                  Tmp_Pos := 1248;
                  if Search_Data.Ext_Queue then
                  begin
                    if User2.Data^.Queue = -1 then
                      Str := Str + ' n/a'
                    else
                      Str := Str + ' ' + IntToStr(User2.Data^.Uploads +
                        User2.Data^.Queue - User2.Data^.Max_Up);
                  end;
                  Tmp_Pos := 340;
                  if Search_Data.Ext_Soft then
                    Str := Str + ' ' + AddStr(User2.Data^.Software);
                  Tmp_Pos := 1249;
                  if Local = nil then
                    User^.Server.Exec(MSG_SRV_SEARCH_RESULT, User^.UserName + ' '
                      + Str)
                  else
                    Exec(User, MSG_SERVER_SEARCH_RESULT, Str);
                  Inc(Result);
                  Tmp_Pos := 341;
                  if Result >= Search_Data.Max then
                  begin
                    Inc_List2.Free;
                    Exc_List2.Free;
                    Exit;
                  end;
                end;
              end;
            end;
          P := P^.Next;
          if (P = nil) and Complete then
          begin
            P := Kw_Min^.Incomplete;
            Complete := False;
          end;
        end;
      end;
    end;
  Inc_List2.Free;
  Exc_List2.Free;
end;

procedure Handler_Search;
var
  I, J, K, ShareCount: Integer;
  Dec: Integer;
  Err: Boolean;
  Srv: TServer;
  Inc_List, Exc_List: TNapCmdList;
begin
  {
  Exec(User, MSG_SERVER_SEARCH_END, '');
  Exec(User, MSG_SERVER_NOSUCH, RS_Handler_SearchDisabled);
  Exit;
  }
  if not IsLogged then Exit;
  Inc(User^.SearchReqs);
  // Block searches By leeches
  if User^.Level = napUserLeech then
  begin
    if User^.Server = nil then
      Local.Exec(MSG_SERVER_SEARCH_END, '')
    else
      User^.Server.Exec(MSG_SRV_SEARCH_END, User^.UserName);
    Exit;
  end;
  Tmp_Pos := 360;
  if Local <> nil then
  begin // Local User
    Tmp_Pos := 1232;
    if not Allow_Share then
    begin
      Local.Exec(MSG_SERVER_SEARCH_END, '');
      Exit;
    end;
    if Network_Hub then
      if not IPisLocal(Decode_Ip(User^.Ip)) then
      begin
        Local.Exec(MSG_SERVER_SEARCH_END, '');
        Exit;
      end;
    // Block search By Local Ppl not Meeting Min_Share
    if User^.Level = napUserUser then
    begin
      if Blocked_Clients[softWinMXHidden] and (not
        StrHash_FindString(DB_Friends, User^.UserName, True))
        and (Local.SoftwareID = softWinMXHidden) and (not (locPingable in
          Local.LocalState)) then
      begin // Possible winmx (Don't Block it Here, Because other Clients can Execute Query Too
        Local.Exec(MSG_SERVER_SEARCH_END, '');
        Exit;
      end;
      if not MinShare_NoBlockAct then
      begin
        if Local.Shared_Size < MinShare_Size then
        begin
          if not StrHash_FindString(DB_Friends, User^.UserName, True) then
          begin
            // Local.Exec(MSG_SERVER_NOSUCH, GetLangT(LNG_SHAREMEGSFIRST,
            //   IntToStr(MinShare_Size div MegaByte), Server_Alias));
            Local.Exec(MSG_SERVER_SEARCH_END, '');
            Exit;
          end;
        end
        else if User^.Shared < MinShare then
          if not StrHash_FindString(DB_Friends, User^.UserName, True) then
          begin
            // Local.Exec(MSG_SERVER_NOSUCH, GetLangT(LNG_SHAREFILESFIRST,
            //   IntToStr(MinShare), Server_Alias));
            Local.Exec(MSG_SERVER_SEARCH_END, '');
            Exit;
          end;
      end;
    end;
    Tmp_Pos := 361;
    if not CheckParams(3) then
    begin
      Local.Exec(MSG_SERVER_SEARCH_END, '');
      Exit;
    end;
    Tmp_Pos := 1233;
    if not Local.BufferEmpty then
    begin
      Local.Exec(MSG_SERVER_NOSUCH, RS_Handler_BlockTooMuchSearch);
      Local.Exec(MSG_SERVER_SEARCH_END, '');
      Local.Searches_Count := 0;
      Exit;
    end;
    Tmp_Pos := 362;
    if Local.Searchespm >= Flood_Max_Searches then
    begin
      // Local.Exec(MSG_SERVER_NOSUCH, RS_Handler_BlockTooMuchSearch);
      Local.Exec(MSG_SERVER_SEARCH_END, '');
      Inc(Local.Searchespm);
      Exit;
    end;
    Tmp_Pos := 1234;
    if Local.Searches_Count > 0 then
    begin
      Local.Exec(MSG_SERVER_NOSUCH, RS_Handler_BlockTooMuchSearch);
      Local.Exec(MSG_SERVER_SEARCH_END, '');
      Inc(Local.Searchespm);
      Local.Searches_Count := 0;
      Exit;
    end;
    Tmp_Pos := 363;
    if Pos('FILENAME CONTAINS', UpperCase(GCmd.Cmd)) = 0 then
    begin
      Local.Exec(MSG_SERVER_NOSUCH, RS_Handler_BlockInvalidSearch);
      Local.Exec(MSG_SERVER_SEARCH_END, '');
      Exit;
    end;
    Tmp_Pos := 122621;
    if Local.Level < napUserModerator then
      if not StrHash_FindString(DB_Friends, Local.Nick, True) then
      begin
        if Local.Shared <> nil then
          ShareCount := Local.Shared.Count
        else
          ShareCount := 0;
        if SearchBlock_ChkFiles and SearchBlock_ChkSize then
        begin
          if SearchBlock_Hard then
          begin
            if ShareCount < SearchBlock_Files then
            begin
              Local.Exec(MSG_SERVER_NOSUCH,
                Format(RS_Handler_BlockPoorFilesSearch,
                [ShareCount, SearchBlock_Files]));
              Local.Exec(MSG_SERVER_SEARCH_END, '');
              Exit;
            end;
            if Local.Shared_Size < SearchBlock_Size then
            begin
              Local.Exec(MSG_SERVER_NOSUCH,
                Format(RS_Handler_BlockPoorSizeSearch,
                [Local.Shared_Size div MegaByte, SearchBlock_Size div
                  MegaByte]));
              Local.Exec(MSG_SERVER_SEARCH_END, '');
              Exit;
            end;
          end
          else if (ShareCount < SearchBlock_Files) and
            (Local.Shared_Size < SearchBlock_Size) then
          begin
            Local.Exec(MSG_SERVER_NOSUCH,
              Format(RS_Handler_BlockPoorShareSearch,
              [ShareCount, SearchBlock_Files, Local.Shared_Size div MegaByte,
              SearchBlock_Size div MegaByte]));
            Local.Exec(MSG_SERVER_SEARCH_END, '');
            Exit;
          end;
        end
        else if SearchBlock_ChkFiles and (ShareCount < SearchBlock_Files) then
        begin
          Local.Exec(MSG_SERVER_NOSUCH, Format(RS_Handler_BlockPoorFilesSearch,
            [ShareCount, SearchBlock_Files]));
          Local.Exec(MSG_SERVER_SEARCH_END, '');
          Exit;
        end
        else if SearchBlock_ChkSize and (Local.Shared_Size < SearchBlock_Size)
          then
        begin
          Local.Exec(MSG_SERVER_NOSUCH, Format(RS_Handler_BlockPoorSizeSearch,
            [Local.Shared_Size div MegaByte, SearchBlock_Size div MegaByte]));
          Local.Exec(MSG_SERVER_SEARCH_END, '');
          Exit;
        end;
      end;
    Inc(Local.Searchespm);
  end
  else
  begin
    Tmp_Pos := 364;
    if (not Allow_Share) or DisableRemoteSearch or (Local_Files < 1) then
    begin
      User^.Server.Exec(MSG_SRV_SEARCH_END, User^.UserName);
      Exit;
    end;
    if Search_Noforward_Results and CheckLag(User^.Server) then
    begin
      User^.Server.Exec(MSG_SRV_SEARCH_END, User^.UserName);
      Exit;
    end;
    Tmp_Pos := 122622;
    if SearchBlock_ChkFiles then
      if User^.Shared < SearchBlock_Files then
        if User^.Level < napUserModerator then
          if not StrHash_FindString(DB_Friends, User^.UserName, True) then
          begin
            Exec(User, MSG_SERVER_NOSUCH,
              Format(RS_Handler_BlockPoorFilesRemoteSearch,
              [ServerName_T, User^.Shared, SearchBlock_Files]));
            User^.Server.Exec(MSG_SERVER_SEARCH_END, User^.UserName);
            Exit;
          end;
    Tmp_Pos := 365;
    SplitString(GCmd.Cmd, HList);
  end;
  Tmp_Pos := 366;
  with Search_Data do
  begin
    Include := '';
    Exclude := '';
    Max := DefSearchResults;
    Mime := TYPE_MP3;
    Speed_Cmp := napNoCompare;
    Bitrate_Cmp := napNoCompare;
    Frequency_Cmp := napNoCompare;
    Size_Cmp := napNoCompare;
    Time_Cmp := napNoCompare;
    Local := False;
    Nonmp3 := False;
    Ext_Soft := False;
    Ext_Queue := False;
  end;
  Err := False;
  Tmp_Pos := 367;
  while HList.Count > 0 do
  begin
    HList.Strings[0] := UpperCase(HList.Strings[0]);
    Dec := 0;
    if HList.Strings[0] = 'FILENAME' then
    begin
      if HList.Count < 3 then
        Err := True
      else
      begin
        for I := 1 to Length(HList.Strings[2]) do
          if HList.Strings[2][I] in [Chr(0)..Chr(31), Chr(127)] then
            Err := True;
        if not Err then
        begin
          Dec := 3;
          if UpperCase(HList.Strings[1]) = 'CONTAINS' then
            Search_Data.Include := Search_Data.Include +
              AnsiLowerCase(HList.Strings[2]) + ' '
          else if UpperCase(HList.Strings[1]) = 'EXCLUDES' then
            Search_Data.Exclude := Search_Data.Exclude +
              AnsiLowerCase(HList.Strings[2]) + ' '
          else
            Err := True;
        end;
      end;
    end; // end FILENAME
    if HList.Strings[0] = 'MAX_RESULTS' then
    begin
      if HList.Count < 2 then
        Err := True
      else
        Search_Data.Max := StrToIntDef(HList.Strings[1], Search_Data.Max);
      Dec := 2;
    end; // end MAX_RESULTS
    if HList.Strings[0] = 'TYPE' then
    begin
      if HList.Count < 2 then
        Err := True
      else
        Search_Data.Mime := StrToType(HList.Strings[1]);
      Dec := 2;
    end; // end TYPE
    if HList.Strings[0] = 'LINESPEED' then
    begin
      if HList.Count < 3 then
        Err := True
      else
      begiN
        Search_Data.Speed_Cmp := Str2Compare(HList.Strings[1]);
        Search_Data.Speed := TNapSpeed(StrToIntDef(HList.Strings[2], 0));
        Dec := 3;
      end;
    end; // end LINESPEED
    if HList.Strings[0] = 'BITRATE' then
    begin
      if HList.Count < 3 then
        Err := True
      else
      begin
        Search_Data.Bitrate_Cmp := Str2Compare(HList.Strings[1]);
        Search_Data.Bitrate := StrToIntDef(HList.Strings[2], 0);
        if Search_Data.Bitrate = 0 then
          Search_Data.Bitrate := 24;
        Dec := 3;
      end;
    end; // end BITRATE
    if HList.Strings[0] = 'FREQ' then
    begin
      if HList.Count < 3 then
        Err := True
      else
      begin
        Search_Data.Frequency_Cmp := Str2Compare(HList.Strings[1]);
        Search_Data.Frequency := StrToIntDef(HList.Strings[2], WINMX_FREQ);
        Dec := 3;
      end;
    end; // end FREQ
    if (HList.Strings[0] = 'SIZE') or (HList.Strings[0] = 'FILESIZE') then
    begin
      if HList.Count < 3 then
        Err := True
      else
      begin
        Search_Data.Size_Cmp := Str2Compare(HList.Strings[1]);
        Search_Data.Size := StrToInt64Def(HList.Strings[2], 0);
        Dec := 3;
      end;
    end; // end SIZE
    if HList.Strings[0] = 'DURATION' then
    begin
      if HList.Count < 3 then
        Err := True
      else
      begin
        Search_Data.Time_Cmp := Str2Compare(HList.Strings[1]);
        Search_Data.Time := StrToIntDef(HList.Strings[2], 0);
        Dec := 3;
      end;
    end; // end DURATION
    if (HList.Strings[0] = 'LOCAL') or (HList.Strings[0] = 'LOCAL_ONLY') then
    begin // Local Only
      if Local <> nil then
        Search_Data.Local := True;
      Dec := 1;
    end;
    if HList.Strings[0] = 'SHOW_SOFTWARE' then
    begin
      Search_Data.Ext_Soft := True;
      Dec := 1;
    end;
    if HList.Strings[0] = 'SHOW_QUEUE' then
    begin
      Search_Data.Ext_Queue := True;
      Dec := 1;
    end;
    if (HList.Strings[0] = 'WMA-FILE') or (HList.Strings[0] = 'WMA_FILE') then
    begin // WMA-FILE
      // Search_Data.Mime := napTypeAudio;
      Dec := 1;
    end;
    if Dec = 0 then
      Err := True;
    if Err then
    begin
      Tmp_Pos := 368;
      if Local = nil then
        User^.Server.Exec(MSG_SRV_SEARCH_END, User^.UserName)
      else
        Exec(User, MSG_SERVER_SEARCH_END, '');
      Exit;
    end;
    for I := 1 to Dec do
      if HList.Count > 0 then
        HList.Delete(0);
  end;
  Tmp_Pos := 369;
  // Checking parsed Parameters
  Inc_List := TNapCmdList.Create;
  Exc_List := TNapCmdList.Create;
  SplitToKeywordsEx(Search_Data.Include, Search_Data.Exclude, Inc_List,
    Exc_List, KEYWORD_MAX_SEARCH);
  if Inc_List.Count < 1 then
  begin // Empty keywords List
    Inc_List.Free;
    Exc_List.Free;
    if User^.Server <> nil then
      User^.Server.Exec(MSG_SRV_SEARCH_END, User^.UserName)
    else
    begin
      Local.Exec(MSG_SERVER_NOSUCH, RS_Handler_BlockInvalidSearch);
      Local.Exec(MSG_SERVER_SEARCH_END, '');
    end;
    Exit;
  end;
  if IsSearchBlocked(User, GCmd.Cmd, Search_Data.Include, Inc_List) then
  begin
    if User^.Server <> nil then
      User^.Server.Exec(MSG_SRV_SEARCH_END, User^.UserName)
    else
      Local.Exec(MSG_SERVER_SEARCH_END, '');
    Exit;
  end;
  Tmp_Pos := 370;
  if User^.Server <> nil then
  begin
    if MaxRemoteSearchResults > 0 then
      if Search_Data.Max > MaxRemoteSearchResults then
        Search_Data.Max := MaxRemoteSearchResults;
  end
  else if MaxSearchResults > 0 then
    if Search_Data.Max > MaxSearchResults then
      Search_Data.Max := MaxSearchResults;
  Tmp_Pos := 372;
  with Search_Data do // Checking for WinMX/FileNavigator Command
  begin
    Nonmp3 := False;
    if Bitrate_Cmp = napEqual then
      if Bitrate = WINMX_BITRATE then
        if Frequency_Cmp = napEqual then
          if Frequency = WINMX_FREQ then
          begin
            Bitrate_Cmp := napNoCompare;
            Frequency_Cmp := napNoCompare;
            Time_Cmp := napNoCompare;
            Nonmp3 := True;
          end;
  end;
  Inc(Num_Searches);
{$I CheckSync.pas}
  Search_Data_Old := Search_Data;
    // Saving old Data, Cos "Search" Changes .Include Strings
  if Network_Hub or (not Allow_Share) then
    I := 0
  else
    I := Search(Inc_List, Exc_List);
  Inc_List.Free;
  Exc_List.Free;
  Search_Data := Search_Data_Old;
  if I > 0 then
  begin
    Inc(Successful_Searches);
    Total_Search_Results := Total_Search_Results + I;
  end;
  if Log_Search then
  begin
    if Local = nil then
      Log(slSearch, GetLangT(LNG_SEARCHLOG4, User^.UserName, User^.Software,
        GCmd.Cmd, IntToStr(I)))
    else
      Log(slSearch, GetLangT(LNG_SEARCHLOG3, Local.Nick, Local.Software,
        GCmd.Cmd, IntToStr(I)));
  end;
  Tmp_Pos := 373;
  if Local = nil then
  begin // That was Remote search
    User^.Server.Exec(MSG_SRV_SEARCH_END, User^.UserName);
    Exit;
  end;
  Local.Last_Search_Time := GetTickCount;
  Local.Searches_Count := 0;
  Tmp_Pos := 374;
  K := 0;
  if Num_Servers > 0 then
    for J := 0 to DB_Servers.Count - 1 do
    begin
      Srv := DB_Servers.Items[J];
      if Srv.Logged then
        if Srv.Hub = nil then
          if not Srv.Lag then
            Inc(K);
    end;
  if (I < Search_Data.Max) and (Num_Servers > 0) and (Search_Data.Local = False)
    and (K > 0) then
  begin
    WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
    Local.Searches_Count := Num_Servers;
  end
  else
    Local.Exec(MSG_SERVER_SEARCH_END, '');
end;

procedure Handler_RemoteSearchResult;
var
  User: POnlineUser;
  Loc: TLocalUser;
begin
  Tmp_Pos := 380;
  User := DB_Online.FindUser(FirstParam(GCmd.Cmd));
  if User = nil then Exit;
  if User^.Server <> nil then Exit;
  Tmp_Pos := 381;
  Loc := User^.Local;
  if Loc = nil then Exit;
  if Loc.Searches_Count < 1 then Exit;
  Tmp_Pos := 382;
  Loc.Exec(MSG_SERVER_SEARCH_RESULT, NextParamEx(GCmd.Cmd));
end;

procedure Handler_RemoteSearchEnd;
var
  User: POnlineUser;
  Loc: TLocalUser;
begin
  Tmp_Pos := 390;
  User := DB_Online.FindUser(GCmd.Cmd);
  if User = nil then Exit;
  if User^.Server <> nil then Exit;
  Tmp_Pos := 391;
  Loc := User^.Local;
  if Loc = nil then Exit;
  if Loc.Searches_Count < 1 then Exit;
  Dec(Loc.Searches_Count);
  Tmp_Pos := 392;
  if Loc.Searches_Count = 0 then
    Loc.Exec(MSG_SERVER_SEARCH_END, '');
end;

procedure Handler_Download;
var
  User2: POnlineUser;
  L2: TLocalUser;
begin
  Tmp_Pos := 400;
  if not IsLogged then Exit;
  if not CheckParams(2) then Exit;
  if (Query <> queryNormal) and (Query <> queryRemoteUser) then Exit;
  if User = nil then Exit;
  Tmp_Pos := 122621;
  if User^.Level = napUserLeech then
  begin
    Exec(User, MSG_SERVER_UPLOAD_FAILED, GCmd.Cmd);
    Exec(User, MSG_SERVER_NOSUCH, Format(RS_Handler_LeechDL, [Levels[0],
      Levels[0]]));
    Exit;
  end;
  Tmp_Pos := 122622;
  if Local <> nil then
  begin
    // -----------------------------------DLカウント.Mods+, Friendもやる
    Tmp_Pos := 122623;
    User2 := DB_Online.FindUser(HList.Strings[0]);
    Tmp_Pos := 122624;
    if User2 = nil then
    begin
      Exec(User, MSG_SERVER_UPLOAD_FAILED, GCmd.Cmd);
      UserIsOffline(HList.Strings[0], True);
      Exit;
    end;
    Tmp_Pos := 122625;
    if User2^.Server = nil then
      L2 := FindLocalUser(User2^.UserName)
    else
      L2 := nil;
    Tmp_Pos := 122626;
    Inc(Local.DLRequestsp3m); // DL元がローカルならとりあえずカウント
    Inc(User^.DLRequests);
    Inc(User2^.ULRequests);
    Tmp_Pos := 122627;
    if (L2 <> nil) and StrHash_FindString(L2.Hotlist, AnsiLowerCase(Local.Nick),
      True) then
    begin
      Dec(Local.DLRequestsp3m);
        // DL元とDL先がともにローカルでホットリストにDL元がいるときは戻す
      Dec(User^.DLRequests);
      Dec(User2^.ULRequests);
    end;
    // --------------------------------------------投網ブロック
    Tmp_Pos := 122628;
    if Local.Level < NapUserModerator then
      if not StrHash_FindString(DB_Friends, Local.Nick, True) then
        if (DLFloodBlock_Count <> 0) and (Local.DLRequestsp3m >
          DLFloodBlock_Count) then
        begin
          // Local.Exec(MSG_SERVER_NOSUCH, RS_Handler_TooMuchDL);
          case DLFloodBlock_metHod of
            // Block: Local.Exec(MSG_SERVER_NOSUCH, Format(RS_Handler_BlockTooMuchDL,
            //  [Local.DLRequestsp3m, DLFloodBlock_Count]));
            Leech:
              begin
                Local.Exec(MSG_SERVER_NOSUCH, Format(RS_Handler_SetLeech,
                  [Levels[0]]));
                Local.Data^.Level := napUserLeech;
                RegisterUser(User, ServerName_T);
                Wallop(MSG_SERVER_NOSUCH, wallopLevel, GetLangT(LNG_LEVEL1,
                  ServerName_T,
                  Local.Nick, Level2Str(napUserLeech),
                    IntToStr(Ord(napUserLeech))), False);
              end;
            Ban:
              begin
                BanUser(Local.Nick, ServerName_T, DLFloodBlock_Bantime,
                  Format(RS_Handler_BanTooMuchDL, [Local.Nick]), True);
                AddReconnector(Decode_Ip(Local.Ip));
                DisconnectUser(Local, '', GetLangT(LNG_KILLED, Local.Nick,
                  Local.Software,
                  'Server ' + ServerName_T) + RS_Handler_KillTooMuchDL,
                    'Handler_Download', False);
              end;
          end;
          Exit;
        end;
  end;
  Tmp_Pos := 122629;
  if User^.Level < NapUserModerator then
    if not StrHash_FindString(DB_Friends, User^.UserName, True) then
    begin
      if DomBlock_ChkFiles and DomBlock_ChkSize then
      begin
        if DomBlock_Hard then
        begin
          if User^.Shared < DomBlock_Files then
          begin
            Error(Format(RS_Handler_BlockPoorFilesDL, [User^.Shared,
              DomBlock_Files]), True);
            Exit;
          end;
          if (Local <> nil) and (Local.Shared_Size <= DomBlock_Size) then
          begin
            Local.Exec(MSG_SERVER_NOSUCH, Format(RS_Handler_BlockPoorSizeDL,
              [Local.Shared_Size div MegaByte, DomBlock_Size div MegaByte]));
            Exit;
          end;
        end
        else if (User^.Shared < DomBlock_Files) and
          (Local <> nil) and (Local.Shared_Size <= DomBlock_Size) then
        begin
          Local.Exec(MSG_SERVER_NOSUCH, Format(RS_Handler_BlockPoorShareDL,
            [User^.Shared, DomBlock_Files, Local.Shared_Size div MegaByte,
              DomBlock_Size div MegaByte]));
          Exit;
        end;
      end
      else if DomBlock_ChkFiles and (User^.Shared < DomBlock_Files) then
      begin
        Error(Format(RS_Handler_BlockPoorFilesDL, [User^.Shared,
          DomBlock_Files]), True);
        Exit;
      end
      else if DomBlock_ChkSize and (Local <> nil) and (Local.Shared_Size <=
        DomBlock_Size) then
      begin
        Local.Exec(MSG_SERVER_NOSUCH, Format(RS_Handler_BlockPoorSizeDL,
          [Local.Shared_Size div MegaByte, DomBlock_Size div MegaByte]));
        Exit;
      end;
    end;
  Tmp_Pos := 401;
  User2 := DB_Online.FindUser(HList.Strings[0]);
  if User2 = nil then
  begin
    Exec(User, MSG_SERVER_UPLOAD_FAILED, GCmd.Cmd);
    UserIsOffline(HList.Strings[0], True);
    Exit;
  end;
  if User2^.Server <> nil then
  begin
    if Local = nil then Exit;
    User2^.Server.Exec(GCmd.Id, User^.UserName + ' ' + GCmd.Cmd);
    Exit;
  end;
  Tmp_Pos := 402;
  L2 := User2^.Local;
  if L2 <> nil then
    if User^.Level < napUserModerator then
      if StrHash_FindString(L2.Ignored, AnsiLowerCase(User^.UserName), False)
        then
      begin
        Exec(User, MSG_SERVER_UPLOAD_FAILED, GCmd.Cmd);
        UserIsOffline(HList.Strings[0], True);
        Exit;
      end;
  Tmp_Pos := 403;
  if (User^.DataPort = 0) and (User2^.DataPort = 0) then
  begin
    Exec(User, MSG_SERVER_UPLOAD_FAILED, GCmd.Cmd);
    Error(GetLangT(LNG_FIREWALL), True);
    Exit;
  end;
  Tmp_Pos := 404;
  if L2 = nil then
    Exec(User2, MSG_SERVER_UPLOAD_REQUEST, User^.UserName + ' "' +
      HList.Strings[1] + '" ' + IntToStr(Ord(User^.Speed)))
  else
    L2.Exec(MSG_SERVER_UPLOAD_REQUEST, User^.UserName + ' "' + HList.Strings[1] +
      '" ' + IntToStr(Ord(User^.Speed)));
end;

procedure Handler_DownloadFirewall;
var
  Str: string;
  User2: POnlineUser;
  L2: TLocalUser;
begin
  Tmp_Pos := 410;
  if not IsLogged then Exit;
  if (Query <> queryNormal) and (Query <> queryRemoteUser) then Exit;
  if User = nil then Exit;
  if not CheckParams(2) then Exit;
  Tmp_Pos := 411;
  User2 := DB_Online.FindUser(HList.Strings[0]);
  if User2 = nil then
  begin
    Exec(User, MSG_SERVER_UPLOAD_FAILED, GCmd.Cmd);
    UserIsOffline(HList.Strings[0], True);
    Exit;
  end;
  if User2^.Server <> nil then
  begin
    if Local = nil then Exit;
    User2^.Server.Exec(GCmd.Id, User^.UserName + ' ' + GCmd.Cmd);
    Exit;
  end;
  Tmp_Pos := 412;
  L2 := User2^.Local;
  if L2 <> nil then
    if User^.Level < napUserModerator then
      if StrHash_FindString(L2.Ignored, AnsiLowerCase(User^.UserName), False)
        then
      begin
        Exec(User, MSG_SERVER_UPLOAD_FAILED, GCmd.Cmd);
        UserIsOffline(HList.Strings[0], True);
        Exit;
      end;
  if (User^.DataPort = 0) and (User2^.DataPort = 0) then
  begin
    Exec(User, MSG_SERVER_UPLOAD_FAILED, GCmd.Cmd);
    Error(GetLangT(LNG_FIREWALL), True);
    Exit;
  end;
  Tmp_Pos := 413;
  Str := User^.UserName + ' ' + IntToStr(User^.Ip) + ' ' +
    IntToStr(User^.DataPort) + ' "' + HList.Strings[1] +
    '" ' + Null_Md5 + ' ' + IntToStr(Ord(User^.Speed));
  Tmp_Pos := 414;
  if L2 = nil then
    Exec(User2, MSG_SERVER_UPLOAD_FIREWALL, Str)
  else
    L2.Exec(MSG_SERVER_UPLOAD_FIREWALL, Str)
end;

procedure Handler_Upload;
var
  Str: string;
  User2: POnlineUser;
  L2: TLocalUser;
begin
  Tmp_Pos := 420;
  if not IsLogged then Exit;
  if (Query <> queryNormal) and (Query <> queryRemoteUser) then Exit;
  if User = nil then Exit;
  if not CheckParams(2) then Exit;
  Tmp_Pos := 421;
  User2 := DB_Online.FindUser(HList.Strings[0]);
  if User2 = nil then
  begin
    UserIsOffline(HList.Strings[0], True);
    Exit;
  end;
  Tmp_Pos := 422;
  if User2^.Server <> nil then
  begin
    if Local = nil then Exit;
    if GCmd.Id <> MSG_SERVER_UPLOAD_FAILED then
    begin
      if Log_Transfers then
        Log(slTransfer, GetLangT(LNG_TRANSFERLOG5, HList.Strings[1],
          User2^.UserName, GetServerName(User2^.Server), User^.UserName));
      Inc(Num_Transfers);
    end;
    User2^.Server.Exec(GCmd.Id, User^.UserName + ' ' + GCmd.Cmd);
    Exit;
  end;
  Tmp_Pos := 423;
  L2 := User2^.Local;
  if L2 <> nil then
    if User^.Level < napUserModerator then
      if StrHash_FindString(L2.Ignored, AnsiLowerCase(User^.UserName), False)
        then
      begin
        Error(GetLangT(LNG_OFFLINE2, HList.Strings[0]), True);
        Exit;
      end;
  if (User^.DataPort = 0) and (User2^.DataPort = 0) then
  begin
    Error(GetLangT(LNG_FIREWALL), True);
    Exit;
  end;
  Tmp_Pos := 424;
  if GCmd.Id = MSG_SERVER_UPLOAD_FAILED then
  begin
    if L2 <> nil then
      L2.Exec(GCmd.Id, User^.UserName + ' "' + NextParamEx(GCmd.Cmd) + '"')
    else
      Exec(User2, GCmd.Id, User^.UserName + ' "' + NextParamEx(GCmd.Cmd) + '"');
    Exit;
  end;
  Tmp_Pos := 425;
  if Log_Transfers then
  begin
    if User^.Server <> nil then
      Log(slTransfer, GetLangT(LNG_TRANSFERLOG4, HList.Strings[1],
        User2^.UserName, User^.UserName, GetServerName(User^.Server)))
    else
      Log(slTransfer, GetLangT(LNG_TRANSFERLOG3, HList.Strings[1],
        User2^.UserName, User^.UserName));
  end;
  Inc(Num_Transfers);
  Tmp_Pos := 427;
  Str := User^.UserName + ' ' + IntToStr(User^.Ip) + ' ' +
    IntToStr(User^.DataPort) + ' "' + HList.Strings[1] + '" ' +
    Null_Md5 + ' ' + IntToStr(Ord(User^.Speed));
  if L2 = nil then
    Exec(User2, MSG_SERVER_FILE_READY, Str)
  else
    L2.Exec(MSG_SERVER_FILE_READY, Str);
end;

procedure Handler_Browse; // 211
var
  User2: POnlineUser;
  L2: TLocalUser;
  I, J, Limit: Integer;
  Str: string;
  Sh: PShare;
  Sh_Bitrate, Sh_Time, Sh_Numwords: Word;
  Sh_Freq: LongWord;
  Sh_Shared: Boolean;
begin
  Tmp_Pos := 430;
  if not IsLogged then Exit;
  if (Query <> queryNormal) and (Query <> queryRemoteUser) then Exit;
  if User = nil then Exit;
  Tmp_Pos := 431;
  if User^.Level = napUserLeech then
  begin
    Exec(User, MSG_SERVER_BROWSE_END, GCmd.Cmd);
    Exit;
  end;
  if Network_Hub then
    if not IPisLocal(Decode_Ip(User^.Ip)) then
    begin
      Exec(User, MSG_SERVER_BROWSE_END, GCmd.Cmd);
      Exit;
    end;
  if not MinShare_NoBlockAct then
    if User^.Level = napUserUser then
    begin
      if not StrHash_FindString(DB_Friends, User^.UserName, True) then
      begin
        if Local <> nil then
        begin
          if Local.Shared_Size < MinShare_Size then
          begin
            // Exec(User, MSG_SERVER_NOSUCH, GetLangT(LNG_SHAREMEGSFIRST,
            //   IntToStr(MinShare_Size div MegaByte), Server_Alias));
            Local.Exec(MSG_SERVER_BROWSE_END, GCmd.Cmd);
            Exit;
          end;
          if User^.Shared < MinShare then
          begin
            // Exec(User, MSG_SERVER_NOSUCH, GetLangT(LNG_SHAREFILESFIRST,
            //   IntToStr(MinShare), Server_Alias));
            Local.Exec(MSG_SERVER_BROWSE_END, GCmd.Cmd);
            Exit;
          end;
        end;
      end;
    end;
  if Local <> nil then
  begin
    if not Local.BufferEmpty then
    begin
      Local.Exec(MSG_SERVER_BROWSE_END, GCmd.Cmd);
      Exit;
    end;
    if Local.Searchespm >= Flood_Max_Searches then
    begin
      Local.Exec(MSG_SERVER_BROWSE_END, GCmd.Cmd);
      Inc(Local.Searchespm);
      Exit;
    end;
  end;
  Tmp_Pos := 12262;
  if User^.UserName <> GCmd.Cmd then
    if User^.Level < napUserModerator then
      if not StrHash_FindString(DB_Friends, User^.UserName, True) then
      begin
        if DomBlock_ChkFiles and DomBlock_ChkSize then
        begin
          if DomBlock_Hard then
          begin
            if (User^.Shared < DomBlock_Files) then
            begin
              Exec(User, MSG_SERVER_BROWSE_END, GCmd.Cmd);
              Exec(User, MSG_SERVER_NOSUCH,
                Format(RS_Handler_BlockPoorFilesBrowse,
                [ServerName_T, User^.Shared, DomBlock_Files]));
              Exit;
            end;
            if (Local <> nil) and (Local.Shared_Size <= DomBlock_Size) then
            begin
              Local.Exec(MSG_SERVER_BROWSE_END, GCmd.Cmd);
              Local.Exec(MSG_SERVER_NOSUCH,
                Format(RS_Handler_BlockPoorSizeBrowse,
                [Local.Shared_Size div MegaByte, DomBlock_Size div MegaByte]));
              Exit;
            end;
          end
          else if (User^.Shared < DomBlock_Files) and
            (Local <> nil) and (Local.Shared_Size <= DomBlock_Size) then
          begin
            Local.Exec(MSG_SERVER_BROWSE_END, GCmd.Cmd);
            Local.Exec(MSG_SERVER_NOSUCH,
              Format(RS_Handler_BlockPoorShareBrowse,
              [User^.Shared, DomBlock_Files, Local.Shared_Size div MegaByte,
                DomBlock_Size div MegaByte]));
            Exit;
          end;
        end
        else if DomBlock_ChkFiles and (User^.Shared < DomBlock_Files) then
        begin
          Exec(User, MSG_SERVER_BROWSE_END, GCmd.Cmd);
          Exec(User, MSG_SERVER_NOSUCH, Format(RS_Handler_BlockPoorFilesBrowse,
            [ServerName_T, User^.Shared, DomBlock_Files]));
          Exit;
        end
        else if DomBlock_ChkSize and (Local <> nil) and (Local.Shared_Size <=
          DomBlock_Size) then
        begin
          Local.Exec(MSG_SERVER_BROWSE_END, GCmd.Cmd);
          Local.Exec(MSG_SERVER_NOSUCH, Format(RS_Handler_BlockPoorSizeBrowse,
            [Local.Shared_Size div MegaByte, DomBlock_Size div MegaByte]));
          Exit;
        end;
      end;
  Tmp_Pos := 432;
  if not CheckParams(1) then Exit;
  if User^.Server <> nil then
    if Browse_Noforward_Results then
      if CheckLag(GetServerLink(User^.Server)) then
      begin // Block browse
        Exec(User, MSG_SERVER_BROWSE_END, GCmd.Cmd);
        Exit;
      end;
  Tmp_Pos := 433;
  User2 := DB_Online.FindUser(GCmd.Cmd);
  if User2 = nil then
  begin
    Error(GetLangT(LNG_BROWSEFAIL), True);
    Exec(User, MSG_SERVER_BROWSE_END, GCmd.Cmd);
    Exit;
  end;
  Tmp_Pos := 434;
  if Local <> nil then
    Inc(Local.Searchespm);
  if User2^.Server <> nil then
  begin
    if Local = nil then Exit;
    iF Browse_Noforward_Requests then
      if CheckLag(GetServerLink(User2^.Server)) then
      begin // Block browse
        Exec(User, MSG_SERVER_BROWSE_END, GCmd.Cmd);
        Exit;
      end;
    User2^.Server.Exec(MSG_CLIENT_BROWSE, User^.UserName + ' ' + GCmd.Cmd);
    Exit;
  end;
  Tmp_Pos := 435;
  L2 := User2^.Local;
  if L2 = nil then Exit; // Weird error
  if User^.Level < napUserModerator then
    if StrHash_FindString(L2.Ignored, AnsiLowerCase(User^.UserName), False) then
    begin
      Exec(User, MSG_SERVER_BROWSE_END, GCmd.Cmd);
      Exit;
    end;
  Tmp_Pos := 436;
  if User2^.Level > napUserUser then
    if User <> User2 then
      if not (userHideBrowse in User2^.State) then
        Exec(User2, MSG_SERVER_NOSUCH, GetLangT(LNG_BROWSEMOD,
          AddStr(Level2Str(User^.Level)), User^.UserName, Decode_Ip(User^.Ip)));
  J := 0;
  if User^.Level < napUserElite then
  begin
    Limit := MaxBrowseResults;
    if User^.Server <> nil then
      if MaxRemoteBrowse < Limit then
        Limit := MaxRemoteBrowse;
  end
  else
    Limit := 65535;
  Inc(Num_Browses);
  if Suggest_DBrowse then
    Exec(User, MSG_SERVER_PRIVMSG, User^.UserName + ' ' +
      RS_Handler_SuggestDirectBrowse);
  Tmp_Pos := 437;
  if L2.Shared <> nil then
    for I := 0 to L2.Shared.Count - 1 do
    begin
      Sh := L2.Shared.Items[I];
      SplitOption(Sh^.Options, Sh_Bitrate, Sh_Freq, Sh_Time, Sh_Numwords,
        Sh_Shared);
      if Sh_Shared or (User^.Level > napUserModerator) then
      begin
        Str := User2^.UserName + ' "' + L2.Shared.GetFileName(I) + '" ' +
          Null_Md5 + ' ' + IntToStr(Sh^.Size) + ' ' + IntToStr(Sh_Bitrate) + ' ' +
          IntToStr(Sh_Freq) + ' ' + IntToStr(Sh_Time);
        Exec(User, MSG_SERVER_BROWSE_RESPONSE, Str);
        Inc(J);
        if Limit > 0 then
          if J >= Limit then
          begin
            if Disable_Nullip or (User.Level >= NapUserModerator) then
              Exec(User, MSG_SERVER_BROWSE_END, GCmd.Cmd + ' ' +
                IntToStr(User2^.Ip))
            else
              Exec(User, MSG_SERVER_BROWSE_END, GCmd.Cmd + ' ' + Null_Ip);
            Total_Browse_Results := Total_Browse_Results + J;
            if Log_Browse then
            begin
              if Local = nil then
                Log(slBrowse, GetLangT(LNG_BROWSELOG2, User^.UserName,
                  User^.Software, GetServerName(User^.Server), L2.Nick,
                  IntToStr(J)))
              else
                Log(slBrowse, GetLangT(LNG_BROWSELOG1, Local.Nick,
                  Local.Software, L2.Nick, IntToStr(J)));
            end;
            Exit;
          end;
      end;
    end;
  if Disable_Nullip or (User.Level >= NapUserModerator) then
    Exec(User, MSG_SERVER_BROWSE_END, GCmd.Cmd + ' ' + IntToStr(User2^.Ip))
  else
    Exec(User, MSG_SERVER_BROWSE_END, GCmd.Cmd + ' ' + Null_Ip);
  Total_Browse_Results := Total_Browse_Results + J;
  if Log_Browse then
  begin
    if Local = nil then
      Log(slBrowse, GetLangT(LNG_BROWSELOG2, User^.UserName, User^.Software,
        GetServerName(User^.Server), L2.Nick, IntToStr(J)))
    else
      Log(slBrowse, GetLangT(LNG_BROWSELOG1, Local.Nick, Local.Software,
        L2.Nick, IntToStr(J)));
  end;
end;

procedure Handler_Queue;
var
  Rec: PShare;
  Str: string;
  Size: Integer;
  User2: POnlineUser;
  L2: TLocalUser;
begin
  Tmp_Pos := 440;
  if not IsLogged then Exit;
  if (Query <> queryNormal) and (Query <> queryRemoteUser) then Exit;
  if User = nil then Exit;
  if not CheckParams(2) then Exit;
  Tmp_Pos := 441;
  User2 := DB_Online.FindUser(HList.Strings[0]);
  if User2 = nil then
  begin
    UserIsOffline(HList.Strings[0], True);
    Exit;
  end;
  Tmp_Pos := 442;
  if User2^.Server <> nil then
  begin
    if Local = nil then Exit;
    User2^.Server.Exec(GCmd.Id, User^.UserName + ' ' + GCmd.Cmd);
    Exit;
  end;
  L2 := User2^.Local;
  Tmp_Pos := 443;
  if (L2 = nil) or (L2.Shared = nil) then
  begin
    Str := HList.Strings[1];
    Size := 0;
  end
  else
  begin
    Rec := L2.Shared.FindRec(HList.Strings[1]);
    iF Rec = nil then
    begin
      Str := HList.Strings[1];
      Size := 0;
    end
    else
    begin
      Str := HList.Strings[1];
      Size := Rec^.Size;
    end;
  end;
  Tmp_Pos := 444;
  Str := User^.UserName + ' "' + Str + '" ' + IntToStr(Size) + ' ';
  if HList.Count < 3 then
    Str := Str + '0'
  else
    Str := Str + HList.Strings[2];
  if L2 = nil then
    Exec(User2, MSG_SERVER_LIMIT, Str)
  else
    L2.Exec(MSG_SERVER_LIMIT, Str)
end;

procedure Handler_Friends;
var
  Str: string;
  P: PStringHashItem;
  User2: POnlineUser;
begin
  Tmp_Pos := 450;
  if not IsLogged then Exit;
  if not CheckLevel('', napUserAdmin) then Exit;
  if not CheckParams(1) then
  begin
    if Query = queryChannel then
      Error(STR_CHSTR_A_FRIENDS);
    Exit;
  end;
  Tmp_Pos := 451;
  Str := LowerCase(FirstParam(GCmd.Cmd));
  if Str = 'add' then
  begin
    Str := Trim(AnsiLowerCase(NextParamEx(GCmd.Cmd)));
    if Str <> '' then
      if not StrHash_FindString(DB_Friends, Str, False) then
      begin
        StrHash_Add(DB_Friends, Str);
        Wallop(MSG_SERVER_NOSUCH, wallopFriends, GetLangT(LNG_ADDFRIEND,
          User^.UserName, Str), True);
      end;
  end
  else if Str = 'remove' then
  begin
    Str := Trim(AnsiLowerCase(NextParamEx(GCmd.Cmd)));
    if StrHash_Delete(DB_Friends, Str, False) then
      Wallop(MSG_SERVER_NOSUCH, wallopFriends, GetLangT(LNG_REMOVEFRIEND,
        User^.UserName, Str), True);
  end
  else if Str = 'list' then
  begin
    P := DB_Friends.First;
    while P <> nil do
    begin
      Error(RS_Handler_Friends_List);
      P := DB_Friends.First;
      while P <> nil do
      begin
        Str := P^.Data;
        User2 := DB_Online.FindUser(P^.Data);
        if User2 <> nil then
          Str := Str + '    '#9#9 + (Time2Str(Current_Time_T -
            User2^.Last_Seen_T));
        Error(Str);
        P := P^.Next;
      end;
      Error('.');
    end;
  end
  else
  begin
    if Query = queryChannel then
      Error(STR_CHSTR_A_FRIENDS)
    else
      Error(GetLangT(LNG_INVALIDARGS), True);
  end;
end;

procedure Handler_JoinChannel;
var
  I, J: Integer;
  Ch: TChannel;
  Str: string;
  S: PNapCmdEx;
begin
  Tmp_Pos := 460;
  if DB_Channels = nil then Exit;
  if not IsLogged then Exit;
  if User = nil then Exit;
  if Local <> nil then
  begin
    Tmp_Pos := 461;
    Str := GCmd.Cmd;
    if IsDigit(Str) then // Fixing Napster 9.6+ Bug
      for I := DB_Invitations.Count - 1 downto 0 do
      begin
        S := DB_Invitations.Items[I];
        if S^.Data = Local.Nick then
        begin
          Str := S^.Cmd;
          DB_Invitations.Delete(I);
        end
        else if (GetTickCount - S^.Id) > EXPIRE_INVITATION then
          DB_Invitations.Delete(I);
      end;
    Str := ChannelName(Str);
    Tmp_Pos := 462;
    if (Str = '') or (Str = '#') then // Invalid channel
    begin
      Error(GetLangT(LNG_INVALIDCHANNEL), True);
      Exit;
    end;
    if userMuzzled in User^.State then
    begin
      Error(GetLangT(LNG_CANTTALK), True);
      Exit;
    end;
    Tmp_Pos := 463;
    Ch := FindChannel(Str);
    if Ch = nil then
    begin
      Tmp_Pos := 464;
      if (User^.Level < napUserModerator) and (Allow_Create_Channels = False)
        then
      begin
        Error(GetLangT(LNG_NOCREATE), True);
        Exit;
      end;
      if DB_Channels.Count >= Max_Channels_Total then
      begin
        Error(GetLangT(LNG_CHANNELLIMIT), True);
        Exit;
      end;
      Tmp_Pos := 465;
      if User^.Level < napUserModerator then
      begin
        J := 0;
        for I := 0 to DB_Channels.Count - 1 do
        begin
          Ch := DB_Channels.Items[I];
          if Ch.FindUser(User) <> -1 then
            Inc(J);
        end;
        if J >= Max_Channels then
        begin
          Error(GetLangT(LNG_CHANNELLIMIT), True);
          Exit;
        end;
      end;
      Tmp_Pos := 466;
      Ch := TChannel.Create(Str);
      DB_Channels.Add(Ch);
    end
    else
    begin
      Tmp_Pos := 467;
      if Ch.Level > User^.Level then
      begin
        PermissionDenied('', True);
        Exit;
      end;
      Tmp_Pos := 468;
      if User^.Level < napUserModerator then
        if Ch.Banned(User^.UserName, Decode_Ip(User^.Ip)) then
        begin
          PermissionDenied('', True);
          Exit;
        end;
      Tmp_Pos := 469;
      if not Ch.Operator(User) then
      begin
        if Ch.Users.Count >= Ch.Limit then
        begin
          Error(GetLangT(LNG_CHANNELLIMIT2), True);
          Exit;
        end;
        J := 0;
        Tmp_Pos := 470;
        for I := 0 to DB_Channels.Count - 1 do
          if TChannel(DB_Channels.Items[I]).FindUser(User) <> -1 then
            Inc(J);
        if J >= Max_Channels then
        begin
          Error(GetLangT(LNG_CHANNELLIMIT), True);
          Exit;
        end;
      end;
    end;
    Tmp_Pos := 471;
  end
  else
  begin
    Tmp_Pos := 472;
    Ch := FindChannel(GCmd.Cmd);
    if Ch = nil then
    begin
      Ch := TChannel.Create(GCmd.Cmd);
      DB_Channels.Add(Ch);
    end;
  end;
  Tmp_Pos := 473;
  Ch.Join(User);
end;

procedure Handler_PartChannel;
var
  Ch: TChannel;
  I: Integer;
begin
  Tmp_Pos := 480;
  Ch := FindChannel(GCmd.Cmd);
  if Ch <> nil then
  begin
    Tmp_Pos := 481;
    Ch.Part(User);
    Tmp_Pos := 482;
    if Ch.Users.Count = 0 then
      if not (chRegistered in Ch.State) then
        for I := DB_Channels.Count - 1 downto 0 do
          if DB_Channels.Items[I] = Ch then
          begin
            DB_Channels.Delete(I);
            Ch.Free;
          end;
    Tmp_Pos := 483;
  end;
end;

procedure Handler_ChannelServerCommand(Args: string; Count: Integer);
var
  Moderator, Admin, Elite (*, Console*): Boolean;
  List: TMyStringList;
  Action, Str, Str2, Last_Bw_Up, Last_Bw_Down: string;
  I, J: Integer;
  User2: POnlineUser;
  Srv: TServer;
begin
  Moderator := User^.Level > napUserUser;
  if not Moderator then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  Admin := User^.Level > napUserModerator;
  Elite := User^.Level > napUserAdmin;
  // Console := User^.Level = napUserConsole;
  List := CreateStringList;
  SplitString(Args, List);
  if List.Count < 1 then
    List.Add('help');
  Count := List.Count - 1;
  Action := LowerCase(List.Strings[0]);
  Args := NextParamEx(Args);
  GCmd.Cmd := Args;
  FreeStringList(List);
  if Action = 'version' then
  begin
    if Num_Servers = 0 then
      Error(SLAVANAP_FULL)
    else
    begin
      Error(ServerAlias + '      '#9 + SLAVANAP_FULL);
      for I := 0 to DB_Servers.Count - 1 do
      begin
        Srv := DB_Servers.Items[I];
        if Srv.Logged then
          Error(Srv.Host + '      '#9 + Srv.Version);
      end;
      Error('.');
    end;
  end
  else if Action = 'alias' then
    GCmd.Id := MSG_CLIENT_ALIAS
  else if Action = 'lag' then
  begin
    for I := 0 to DB_Servers.Count - 1 do
    begin
      Srv := DB_Servers.Items[I];
      if Srv.Logged then
        if Srv.Hub = nil then
          Error(Srv.Host + '      '#9 + GetLangT(LNG_SLIST_LAGSEC,
            Srv.CountLag));
    end;
    Error('.');
  end
  else if Action = 'allowed' then
    GCmd.Id := MSG_CLIENT_ALLOWEDLIST
  else if Action = 'list' then
  begin
    if not Elite then
    begin
      PermissionDenied('', True);
      Exit;
    end;
    for I := 0 to DB_Servers.Count - 1 do
    begin
      Srv := DB_Servers.Items[I];
      Str := Srv.Host + ':' + IntToStr(Srv.Port);
      if Srv.Authentication = authResolve then
        Str2 := GetLangI(LNG_SRV_RESOLVE)
      else
        Str2 := GetLangI(LNG_SRV_PASSWORD);
      Error(Str + '           '#9 + Str2 + '       '#9 + Srv.Alias);
    end;
  end
  else if (Action = 'connect') or (Action = 'link') then
  begin
    if Count < 1 then
      Error(STR_CHSTR_S_CONNECT)
    else
      GCmd.Id := MSG_CLIENT_CONNECT;
  end
  else if (Action = 'remove') or (Action = 'kill') then
  begin
    if Count < 1 then
      Error(STR_CHSTR_S_REMOVE)
    else
      GCmd.Id := MSG_CLIENT_REMOVE_SERVER;
  end
  else if Action = 'setpasswords' then
    GCmd.Id := MSG_CLIENT_SETSERVERPASSWORDS
  else if Action = 'restart' then
    GCmd.Id := MSG_CLIENT_RESTART
  else if Action = 'reboot' then
  begin
    if Count < 1 then Exit;
    GCmd.Id := MSG_CLIENT_REBOOT;
  end
  else if (Action = 'disconnect') or (Action = 'delink') then
  begin
    if Count < 1 then
      Error(STR_CHSTR_S_DISCONNECT)
    else
      GCmd.Id := MSG_CLIENT_DISCONNECT;
  end
  else if Action = 'links' then
    GCmd.Id := MSG_CLIENT_LINKS
  else if Action = 'console' then
    GCmd.Id := MSG_CLIENT_GETCONSOLE
  else if Action = 'reconnectors' then
  begin
    Error('DB_Reconnect:');
    for I := 0 to DB_Reconnect.Count - 1 do
      Error('  ' + DB_Reconnect.Strings[I]);
    Error('.');
  end
  else if Action = 'stats' then
  begin
    if not Moderator then
      PermissionDenied('', True)
    else
    begin
      Error(Format(RS_Stats_Title, [ServerName_T, SLAVANAP_VERSION_SHORT,
        SLAVANAP_BUILD]));
      Error(Format(RS_Stats_Local, [Local_Users, Max_Users,
        IntToStrDot(Local_Files), IntToStrDot(Local_Bytes div GigaByte)]));
      Error(Format(RS_Stats_LocalAvg, [Local_Files div Local_Users,
        IntToStrDot((Local_Bytes div GigaByte) div Local_Users)]));
      Error(Format(RS_Stats_Network, [Total_Users, Total_Users_Limit,
        IntToStrDot(Total_Files), IntToStrDot(Total_Bytes div GigaByte)]));
      Error(Format(RS_Stats_NetworkAvg, [Total_Files div Total_Users,
        IntToStrDot((Total_Bytes div GigaByte) div Total_Users)]));
      Error(Format(RS_Stats_Users, [Local_Users_Max, Total_Users_Max]));
      Error(Format(RS_Stats_Files, [IntToStrDot(Local_Files_Max),
        IntToStrDot(Total_Files_Max)]));
      Error(Format(RS_Stats_Size, [IntToStrDot(Local_Bytes_Max div GigaByte),
        IntToStrDot(Total_Bytes_Max div GigaByte)]));
      Error(Format(RS_Stats_Connection, [IntToStrDot(Total_Connections),
        IntToStrDot(Total_Transfers + Num_Transfers)]));
      Error(Format(RS_Stats_Search, [IntToStrDot(Total_Searches + Num_Searches),
        IntToStrDot(Successful_Searches), IntToStrDot(Total_Search_Results)]));
      Error(Format(RS_Stats_Browse, [IntToStrDot(Total_Browses),
        IntToStrDot(Total_Browse_Results)]));
      Error(Format(RS_Stats_Sockets, [Sockets_Count]));
      Error(Format(RS_Stats_Servers, [Num_Servers]));
      if Win98 then
        Error(Format(RS_Stats_Memory, [IntToStrDot(AllocMemSize)]));
      J := (GetTickCount - Start_Time) div 60000;
      Str := IntToStr(J mod 60) + RS_Stats_Minutes;
      if J > 59 then
      begin
        J := J div 60;
        Str := IntToStr(J mod 24) + RS_Stats_Hours + Str;
        if J > 23 then
          Str := IntToStr(J div 24) + RS_Stats_Days + Str;
      end;
      Error(RS_Stats_Uptime + Str);
      Error(Format(RS_Stats_BandWidth, [IntToStrDot(Total_Bytes_in + Bytes_in),
        IntToStrDot(Total_Bytes_Out + Bytes_Out)]));
      Last_Bw_Up := IntToStr((Last_Bytes_Out div KiloByte) div 60) + '.' +
        IntToStr(((Last_Bytes_Out div KiloByte) div 6) mod 10);
      Last_Bw_Down := IntToStr((Last_Bytes_in div KiloByte) div 60) + '.' +
        IntToStr(((Last_Bytes_in div KiloByte) div 6) mod 10);
      Error(Format(RS_Stats_LastBandWidth, [Last_Bw_Down, Last_Bw_Up]));
      Error('.');
    end;
  end
  else if Action = 'ts' then
  begin
    if not Admin then
      PermIssionDenied('', True)
    else
    begin
      if True_Stats = True then
        Str := 'yes'
      else
        Str := 'no';
      Error(ServerName_T + '    ' + Str);
      for I := 0 to DB_Servers.Count - 1 do
      begin
        Srv := DB_Servers.Items[I];
        if Srv.TrueStats = True then
          Str := 'yes'
        else
          Str := 'no';
        Error(Srv.Host + '    ' + Str);
      end;
    end;
  end
  else if Action = 'pingall' then
  begin
    if not Moderator then
      PermissionDenied('', True)
    else
    begin
      Str := User^.UserName + ' pingall ' + Query_Channel + ' ' +
        IntToStr(GetTickCount);
      Cons.Exec(MSG_SERVER_PING, Str);
      Error(Format(RS_Handler_Ping, [ServerName_T]));
      Str := IntToStr(MyServerHandle) + ' ' + IntToStr(GetTickCount) +
        ' pingall ' + User^.UserName + ' ' + Query_Channel;
      for I := 0 to DB_Servers.Count - 1 do
      begin
        Srv := DB_Servers.Items[I];
        if Srv.Logged then
        begin
          Srv.Exec(MSG_CLIENT_PING_ALL_SERVERS, Str);
          Error(Format(RS_Handler_Ping, [Srv.Host]));
        end;
      end;
      {for I := 0 to DB_Servers.Count - 1 do
      begin
        Srv := DB_Servers.Items[I];
        if Srv.Logged then
        begin
          User2 := DB_Online.FindUser(Srv.Console);
          if User2 <> nil then
          begin
            Exec(User2, MSG_SERVER_PING, Str);
            Error(Format(RS_Handler_Ping, [Srv.Host]));
          end;
        end;
      end;}
    end;
  end
  else if Action = 'ping' then
  begin
    if not Moderator then
      PermissionDenied('', True)
    else if Count < 1 then
      Error(STR_CHSTR_S_PING)
    else
    begin
      SplitString(LowerCase(Args), HList);
      Str := User^.UserName + ' pingall ' + Query_Channel + ' ' +
        IntToStr(GetTickCount);
      for J := 0 to HList.Count - 1 do
      begin
        if MatchesMaskEx(ServerName_T, HList.Strings[J]) or
          MatchesMaskEx(ServerAlias, HList.Strings[J]) then
        begin
          Cons.Exec(MSG_SERVER_PING, Str);
          Error(Format(RS_Handler_Ping, [ServerName_T]));
        end;
        for I := 0 to DB_Servers.Count - 1 do
        begin
          Srv := DB_Servers.Items[I];
          if Srv.Logged then
            if MatchesMaskEx(Srv.Host, HList.Strings[J]) or
              MatchesMaskEx(Srv.Alias, HList.Strings[J]) then
            begin
              User2 := DB_Online.FindUser(Srv.Console);
              if User2 <> nil then
              begin
                Exec(User2, MSG_SERVER_PING, Str);
                Error(Format(RS_Handler_Ping, [Srv.Host]));
              end;
            end;
        end;
      end;
    end;
  end
  else if Action = 'help' then
  begin
    if Local <> Cons then
      Error(STR_CHHLP_MAIN);
    Error(STR_CHHLP_HELPLISTSERVER);
    if Admin then
      Error(STR_CHHLP_S_ALLOWED);
    if Admin then
      Error(STR_CHHLP_S_CONNECT);
    Error(STR_CHHLP_S_CONSOLE);
    if Admin then
      Error(STR_CHHLP_S_DISCONNECT);
    Error(STR_CHHLP_S_LAG); // They must Be mod Now
    Error(STR_CHHLP_S_LINKS);
    if Elite then
      Error(STR_CHHLP_S_LIST);
    Error(STR_CHHLP_S_PING);
    Error(STR_CHHLP_S_PINGALL);
    if Elite then
      Error(STR_CHHLP_S_REBOOT);
    if Elite then
      Error(STR_CHHLP_S_REMOVE);
    if Elite then
      Error(STR_CHHLP_S_RESTART);
    if Local = Cons then
      Error(STR_CHHLP_S_SETPASSWORDS);
    Error(STR_CHHLP_S_STATS);
    Error(STR_CHHLP_S_VERSION);
    Error('.');
  end
  else
    Error(Format(STR_CHSTR_UNKNOWNSERVER, [Action]));
end;

procedure Handler_ChannelAdmin(Args: string; Count: Integer);
var
  Moderator, Admin, Elite (*, Console*): Boolean;
  List: TMyStringList;
  Action, Str: string;
  I, J: Integer;
  User2: POnlineUser;
  L1: TLocalUser;
  Reg: PRegisteredUser;
begin
  Moderator := User^.Level > napUserUser;
  Admin := User^.Level > napUserModerator;
  Elite := User^.Level > napUserAdmin;
  // Console := User^.Level = napUserConsole;
  List := CreateStringList;
  SplitString(Args, List);
  if List.Count < 1 then
    List.Add('help');
  Count := List.Count - 1;
  Action := LowerCase(List.Strings[0]);
  Args := NextParamEx(Args);
  GCmd.Cmd := Args;
  FreeStringList(List);
  if not Moderator then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  if Action = 'debug' then
  begin
    // if not Moderator then
    //  PermissionDenied('', True)
    // else
    // begin
    // L1 := User^.Local;
    Error('DB_Online Items = ' + IntToStr(DB_Online.CountItems));
    Error('DB_Local.Count = ' + IntToStr(DB_Local.Count));
    Error('DB_Local.Capacity = ' + IntToStr(DB_Local.Capacity));
    Error('DB_Servers.Count = ' + IntToStr(DB_Servers.Count));
    Error('DB_Registered Items = ' + IntToStr(DB_Registered.CountUsers));
    Error('DB_Bans.Count = ' + IntToStr(DB_Bans.Count));
    Error('Cmd_List.Count = ' + IntToStr(Cmd_List.Count));
    Error('Sync_Reply_List.Count = ' + IntToStr(Sync_Reply_List.Count));
    Error('Num_Servers = ' + IntToStr(Num_Servers));
    Error('Direct_Links = ' + IntToStr(Direct_Links));
    Str := Decode_Ip(User^.Ip);
    Error(Str);
    if IPisLocal(Str) then
      Error('yep')
    else
      Error('nope');
    Error(':-b');
    // end;
  end
  else if Action = 'register' then
  begin
    if Count < 1 then
      Error(STR_CHSTR_A_REGISTER)
    else
      GCmd.Id := MSG_CLIENT_REGISTER_USER;
  end
  else if Action = 'wallop' then
    GCmd.Id := MSG_CLIENT_WALLOP
  else if Action = 'announce' then
    GCmd.Id := MSG_CLIENT_ANNOUNCE
  else if (Action = 'clientblocks') or (Action = 'cb') then
    GCmd.Id := MSG_CLIENT_CLIENTBLOCKS
  else if Action = 'ban' then
  begin
    if Count < 1 then
      Error(STR_CHSTR_A_BAN)
    else
      GCmd.Id := MSG_CLIENT_BAN;
  end
  else if Action = 'unban' then
  begin
    if Count < 1 then
      Error(STR_CHSTR_A_UNBAN)
    else
      GCmd.Id := MSG_CLIENT_UNBAN;
  end
  else if Action = 'kill' then
  begin
    if Count < 1 then
      Error(STR_CHSTR_A_KILL)
    else
      GCmd.Id := MSG_CLIENT_KILL;
  end
  else if (Action = 'unregister') or (Action = 'unreg') then
  begin
    if Count < 1 then
      Error(STR_CHSTR_A_UNREGISTER)
    else
      GCmd.Id := MSG_CLIENT_UNREGISTER;
  end
  else if Action = 'nuke' then
  begin
    if Count < 1 then
      Error(STR_CHSTR_A_NUKE)
    else
      GCmd.Id := MSG_CLIENT_NUKE;
  end
  else if Action = 'level' then
  begin
    if Count < 2 then
      Error(STR_CHSTR_A_LEVEL)
    else
      GCmd.Id := MSG_CLIENT_SETUSERLEVEL;
  end
  else if (Action = 'reglist') or (Action = 'rl') then
  begin
    if not Admin then
      PermissionDenied('', True)
    else
      for I := 0 to USERS_NAME_ARRAY - 1 do
        for J := 0 to DB_Registered.List[I].Count - 1 do
        begin
          Reg := DB_Registered.List[I].Items[J];
          // Error(Reg^.Nick + '          '#9 + (Level2Str(Reg^.Level)));
          Str := Reg^.Nick + '          '#9 + (Level2Str(Reg^.Level));
          User2 := DB_Online.FindUser(Reg^.Nick);
          if User2 <> nil then
            Str := Str + '    '#9#9 + GetServerName(User2^.Server);
          Error(Str);
        end;
    Error('.');
  end
  else if Action = 'muzzle' then
  begin
    if Count < 1 then
      Error(STR_CHSTR_A_MUZZLE)
    else
      GCmd.Id := MSG_CLIENT_MUZZLE;
  end
  else if Action = 'unmuzzle' then
  begin
    if Count < 1 then
      Error(STR_CHSTR_A_UNMUZZLE)
    else
      GCmd.Id := MSG_CLIENT_UNMUZZLE;
  end
  else if Action = 'cloak' then
    GCmd.Id := MSG_CLIENT_CLOAK
  else if Action = 'banlist' then
    GCmd.Id := MSG_CLIENT_BANLIST
  else if Action = 'software' then
    GCmd.Id := MSG_CLIENT_VERSION_STATS
  else if Action = 'set' then
    GCmd.Id := MSG_CLIENT_SERVER_CONFIG
  else if Action = 'setremote' then
    GCmd.Id := MSG_CLIENT_SHOWSETTING
  else if Action = 'setallremote' then
    GCmd.Id := MSG_CLIENT_SHOWALLSETTINGS
  else if Action = 'redirect' then
  begin
    if not Elite then
      PermissionDenied('', True)
    else if Count < 2 then
      Error(STR_CHSTR_A_REDIRECT)
    else
      for J := 0 to USERS_NAME_ARRAY - 1 do
        for I := 0 to DB_Online.Names[J].Count - 1 do
        begin
          User2 := DB_Online.Names[J].Items[I];
          if User2^.Server = nil then
            if User2^.Level <> napUserConsole then
              Exec(User2, MSG_CLIENT_REDIRECT, GCmd.Cmd);
        end;
  end
  else if Action = 'cycle' then
  begin
    if not Elite then
      PermissionDenied('', True)
    else if Count < 1 then
      Error(STR_CHSTR_A_CYCLE)
    else
      for J := 0 to USERS_NAME_ARRAY - 1 do
        for I := 0 to DB_Online.Names[J].Count - 1 do
        begin
          User2 := DB_Online.Names[J].Items[I];
          if User2^.Server = nil then
            if User2^.Level <> napUserConsole then
              Exec(User2, MSG_CLIENT_CYCLE, GCmd.Cmd);
        end;
  end
  else if Action = 'ip' then
  begin
    // User2 := DB_Online.FindUser(GCmd.Cmd);
    // if User2^.Level > napUserUser then
    //   Exec(User2, MSG_SERVER_NOSUCH, GetLangT(LNG_IPREQMOD,
    //     AddStr(Level2Str(User^.Level)), User^.Nick, Decode_Ip(User^.Ip)));
    if not Moderator then
      PermissionDenied('', True)
    else if Count < 1 then
      Error(STR_CHSTR_A_IP)
    else
    begin
      User2 := DB_Online.FindUser(GCmd.Cmd);
      if User2 = nil then
        UserIsOffline(GCmd.Cmd)
      else
        Error(Format(RS_Handler_IP, [User2^.UserName, Decode_Ip(User2^.Ip)]));
    end;
  end
  else if Action = 'servermsg' then
    GCmd.Id := MSG_CLIENT_PRIVMSG_ANON
  else if Action = 'friends' then
  begin
    if not Admin then
      PermissionDenied('', True)
    else if Count < 1 then
      Error(STR_CHSTR_A_FRIENDS)
    else
      GCmd.Id := MSG_CLIENT_FRIENDS;
  end
  else if Action = 'block' then
    GCmd.Id := MSG_CLIENT_BLOCK
  else if Action = 'blocks' then
    GCmd.Id := MSG_CLIENT_BLOCKLIST
  else if Action = 'unblock' then
    GCmd.Id := MSG_CLIENT_UNBLOCK
  else if Action = 'blocked' then
  begin
    if User^.Level < napUserAdmin then
      PermissionDenied('')
    else
    begin
      if not Block_AllowShare then
        Error(RS_Handler_BlockUserFile);
      if not Block_AllowShare_Admins then
        Error(RS_Handler_BlockModsFile);
      J := 0;
      for I := 0 to DB_Local.Count - 1 do
      begin
        L1 := DB_Local.Items[I];
        if L1.Logged then
          if L1.Shared_Blocked > 0 then
          begin
            Str := Format(RS_Handler_BlockFiles, [Level2Str(L1.Level), L1.Nick,
              Decode_Ip(L1.Ip), L1.Shared_Blocked]);
            Inc(J, L1.Shared_Blocked);
            Error(Str);
          end;
      end;
      Error(GetLangT(LNG_ITEMSLISTED, IntToStr(J)));
    end;
  end
  else if Action = 'help' then
  begin
    if Local <> Cons then
      Error(STR_CHHLP_MAIN);
    Error(STR_CHHLP_HELPLISTADMIN);
    if Admin then
      Error(STR_CHHLP_A_ANNOUNCE);
    if Moderator then
      Error(STR_CHHLP_A_BAN);
    if Moderator then
      Error(STR_CHHLP_A_BANLIST);
    if Admin then
      Error(STR_CHHLP_A_BLOCK);
    if Admin then
      Error(STR_CHHLP_A_BLOCKED);
    if Admin then
      Error(STR_CHHLP_A_BLOCKS);
    if Moderator then
      Error(STR_CHHLP_A_CLIENTBLOCKS);
    if Moderator then
      Error(STR_CHHLP_A_CLOAK);
    if Elite then
      Error(STR_CHHLP_A_CYCLE);
    if Admin then
      Error(STR_CHHLP_A_FRIENDS);
    if Moderator then
      Error(STR_CHHLP_A_IP);
    if Moderator then
      Error(STR_CHHLP_A_KILL);
    if Moderator then
      Error(STR_CHHLP_A_LEVEL);
    if Moderator then
      Error(STR_CHHLP_A_MUZZLE);
    if Moderator then
      Error(STR_CHHLP_A_NUKE);
    if Elite then
      Error(STR_CHHLP_A_REDIRECT);
    if Moderator then
      Error(STR_CHHLP_A_REGISTER);
    if Admin then
      Error(STR_CHHLP_A_REGLIST);
    if Elite then
      Error(STR_CHHLP_A_SERVERMSG);
    if Elite then
      Error(STR_CHHLP_A_VARIABLE)
    else if Admin then
      Error(STR_CHHLP_A_VARIABLE2);
    if Elite then
      Error(STR_CHHLP_A_REMOTEVARIABLE)
    else if Admin then
      Error(STR_CHHLP_A_REMOTEVARIABLE2);
    if Elite then
      Error(STR_CHHLP_A_ALLREMOTEVARIABLE)
    else if Admin then
      Error(STR_CHHLP_A_ALLREMOTEVARIABLE2);
    if Moderator then
      Error(STR_CHHLP_A_SOFTWARE);
    if Moderator then
      Error(STR_CHHLP_A_UNBAN);
    if Admin then
      Error(STR_CHHLP_A_UNBLOCK);
    if Moderator then
      Error(STR_CHHLP_A_UNMUZZLE);
    if Moderator then
      Error(STR_CHHLP_A_UNREGISTER);
    if Moderator then
      Error(STR_CHHLP_A_WALLOP);
    Error('.');
  end
  else
    Error(Format(STR_CHSTR_UNKNOWNADMIN, [Action]));
end;

procedure Handler_ChannelCommand(Command: string);
var
  Action, Args, Str: string;
  I, J, K, Count: Integer;
  Admin, Moderator, Operator, Add_Ch: Boolean;
  Ch: TChannel;
  P: PStringHashItem;
  User2: POnlineUser;
  Kw: PKeyword;
  Kwl: PKeywordList;
begin
  Ch := Findchannel(Query_Channel);
  if Ch = nil then Exit;
  Operator := Ch.Operator(User);
  Moderator := User^.Level > napUserUser;
  Admin := User^.Level > napUserModerator;
  SplitString(Command, HList);
  if HList.Count < 1 then
    Action := '>help'
  else
    Action := LowerCase(HList.Strings[0]);
  if Length(Action) < 2 then
    Action := '>help';
  if HList.Count > 1 then
    Args := NextParamEx(Command)
  else
    Args := '';
  Count := HList.Count - 1;
  if Count < 0 then
    Count := 0;
  GCmd.Id := 0;
  GCmd.Cmd := Args;
  Add_Ch := True;
  Action := Copy(Action, 2, Length(Action));
  if IsDigit(Action) then
  begin
    Args := Action + ' ' + Args;
    Inc(Count);
    Action := 'raw';
  end;
  if Action = 'raw' then
  begin
    SplitString(Args, HList);
    if HList.Count > 0 then
    begin
      GCmd.Id := StrToIntDef(HList.Strings[0], 0);
      if GCmd.Id < 1 then Exit;
      GCmd.Cmd := NextParamEx(Args);
      ProcessCommand(Local, queryChannel);
      Exit;
    end;
    Action := 'help';
  end;
  if (Action = 'admin') or (Action = 'a') then
  begin
    Handler_ChannelAdmin(Args, Count);
    Add_Ch := False;
  end
  else if (Action = 'server') or (Action = 's') then
  begin
    Handler_ChannelServerCommand(Args, Count);
    Add_Ch := False;
  end
  else if Action = 'kw' then // Keywords debug
  begin
    if User^.Level < napUserModerator then Exit;
    if Length(Args) < 2 then Exit;
    J := GetKeywordIndex(Args);
    K := Length(Args);
    if J = -1 then Exit;
    for I := 0 to SHARED_ARRAY - 1 do
    begin
      Kwl := DB_Keywords[I, J, K];
      Kw := Kwl^.First;
      while Kw <> nil do
      begin
        if Kw^.Keyword = Args then
          Error('I=' + IntToStr(I) + ' Keyword=' + Kw^.Keyword + ' Count=' +
            IntToStr(Kw^.Count) + ' Total=' + IntToStr(Kw^.Total) + ' Available='
            + IntToStr(Kw^.Available));
        Kw := Kw^.Next;
      end;
    end;
    Error('.');
  end
  else if (Action = 'opsay') or (Action = 'o') then
    GCmd.Id := MSG_CLIENT_CHANNEL_WALLOP
  else if (Action = 'opme') or (Action = 'opemote') then
    GCmd.Id := MSG_CLIENT_CHANNEL_OPEMOTE
  else if (Action = 'me') or (Action = 'emote') then
    GCmd.Id := MSG_CLIENT_EMOTE
  else if Action = 'say' then
    GCmd.Id := 0 // do nothing
  else if Action = 'wallop' then
  begin
    GCmd.Id := MSG_CLIENT_WALLOP;
    Add_Ch := False;
  end
  else if (Action = 'listfriends') or (Action = 'lf') then
  begin
    if User^.Level < napUserAdmin then
    begin
      PermissionDenied('', True);
      Exit;
    end;
    Error(RS_Handler_Friends_List);
    P := DB_Friends.First;
    while P <> nil do
    begin
      Str := P^.Data;
      User2 := DB_Online.FindUser(P^.Data);
      if User2 <> nil then
        Str := Str + '    '#9#9 + (Time2Str(Current_Time_T -
          User2^.Last_Seen_T));
      Error(Str);
      P := P^.Next;
    end;
    Error('.');
  end
  else if Action = 'announce' then
  begin
    GCmd.Id := MSG_CLIENT_ANNOUNCE;
    Add_Ch := False;
  end
  else if (Action = 'usermode') or (Action = 'um') then
  begin
    GCmd.Id := MSG_CLIENT_USER_MODE;
    Add_Ch := False;
  end
  else if (Action = 'showserver') or (Action = 'ss') then
  begin
    if Count < 1 then
      Error(STR_CHSTR_SHOWSERVER)
    else
      GCmd.Id := MSG_CLIENT_WHICH_SERVER;
    Add_Ch := False;
  end
  else if Action = 'banlist' then
    GCmd.Id := MSG_CLIENT_CHANNEL_BAN_LIST
  else if Action = 'ban' then
  begin
    if Count < 1 then
      Error(STR_CHSTR_BAN)
    else
      GCmd.Id := MSG_CLIENT_CHANNEL_BAN;
  end
  else if Action = 'banip' then
  begin
    if Count < 1 then
      Error(STR_CHSTR_BANIP)
    else
      GCmd.Id := MSG_CLIENT_CHANNEL_BANIP;
  end
  else if Action = 'unban' then
  begin
    if Count < 1 then
      Error(STR_CHSTR_UNBAN)
    else
      GCmd.Id := MSG_CLIENT_CHANNEL_UNBAN;
  end
  else if Action = 'banclear' then
    GCmd.Id := MSG_CLIENT_CHANNEL_CLEAR_BANS
  else if Action = 'banlist' then
    GCmd.Id := MSG_CLIENT_CHANNEL_BAN_LIST
  else if Action = 'op' then
  begin
    if Count < 1 then
      Error(STR_CHSTR_OP)
    else
      GCmd.Id := MSG_CLIENT_OP;
  end
  else if Action = 'deop' then
  begin
    if Count < 1 then
      Error(STR_CHSTR_DEOP)
    else
      GCmd.Id := MSG_CLIENT_DEOP;
  end
  else if Action = 'oplist' then
  begin
    if not Moderator then
      PermissionDenied('', True)
    else
    begin
      P := Ch.Ops.First;
      while P <> nil do
      begin
        Error(P^.Data);
        P := P^.Next;
      end;
      Error('.');
    end;
  end
  else if Action = 'voice' then
  begin
    if Count < 1 then
      Error(STR_CHSTR_VOICE)
    else
      GCmd.Id := MSG_CLIENT_CHANNEL_VOICE;
  end
  else if Action = 'unvoice' then
  begin
    if Count < 1 then
      Error(STR_CHSTR_UNVOICE)
    else
      GCmd.Id := MSG_CLIENT_CHANNEL_UNVOICE;
  end
  else if Action = 'kick' then
  begin
    if Count < 1 then
      Error(STR_CHSTR_KICK)
    else
      GCmd.Id := MSG_CLIENT_KICK;
  end
  else if Action = 'mode' then
    GCmd.Id := MSG_CLIENT_CHANNEL_MODE
  else if (Action = 'whois') or (Action = 'w') then
  begin
    GCmd.Id := MSG_CLIENT_WHOIS;
    Add_Ch := False;
  end
  else if Action = 'topic' then
    GCmd.Id := MSG_SERVER_TOPIC
  else if Action = 'drop' then
    GCmd.Id := MSG_CLIENT_DROP_CHANNEL
  else if Action = 'remove' then
  begin
    if Count < 1 then
      Error(STR_CHSTR_REMOVE)
    else
      GCmd.Id := MSG_CLIENT_DROP_CHANNEL;
    Add_Ch := False;
  end
  else if Action = 'clear' then
    GCmd.Id := MSG_CLIENT_CLEAR_CHANNEL
  else if Action = 'part' then
    GCmd.Id := MSG_CLIENT_PART
  else if Action = 'msg' then
  begin
    GCmd.Id := MSG_CLIENT_PRIVMSG;
    Add_Ch := False;
  end
  else if Action = 'limit' then
  begin
    if Count < 1 then
      Error(GetLangT(LNG_CHLIMIT, Ch.Limit))
    else
      GCmd.Id := MSG_CLIENT_CHANNEL_LIMIT;
  end
  else if Action = 'level' then
  begin
    if Count < 1 then
      Error(GetLangT(LNG_CHLEVEL2, Level2Str(Ch.Level)))
    else
      GCmd.Id := MSG_CLIENT_SET_CHAN_LEVEL
  end
  else if Action = 'invite' then
  begin
    if Count < 1 then
      Error(STR_CHSTR_INVITE)
    else
      GCmd.Id := MSG_CLIENT_CHANNEL_INVITE_OPENNAP;
  end
  else if Action = 'motd' then
  begin
    P := Ch.Motd.First;
    while P <> nil do
    begin
      if Length(P^.Data) < 1 then
        Exec(User, MSG_SERVER_EMOTE, Ch.Channel + ' Server ""')
      else if P^.Data[1] <> ';' then
        Exec(User, MSG_SERVER_EMOTE, Ch.Channel + ' Server "' +
          FormatString(User, P^.Data, True) + '"');
      P := P^.Next;
    end;
  end
  else if Action = 'console' then
    Error(Cons.Data^.UserName)
  else if Action = 'ghost' then
  begin
    GCmd.Id := MSG_CLIENT_GHOST;
    Add_Ch := False;
  end
  else if Action = 'version' then
    Error(SLAVANAP_FULL)
  else if Action = 'help' then
  begin
    if Local <> Cons then
      Error(STR_CHHLP_MAIN);
    Error(STR_CHHLP_HELPLIST);
    // if Admin then Error(STR_CHHLP_ADDFRIENDS);
    if Moderator then
      Error(STR_CHHLP_ADMIN);
    if Operator then
      Error(STR_CHHLP_BAN);
    if Operator then
      Error(STR_CHHLP_BANCLEAR);
    if Operator then
      Error(STR_CHHLP_BANIP);
    if Operator then
      Error(STR_CHHLP_BANLIST);
    if Moderator then
      Error(STR_CHHLP_CLEAR);
    if Local = Cons then
      Error(STR_CHHLP_CLS);
    Error(STR_CHHLP_CONSOLE);
    if Moderator then
      Error(STR_CHHLP_DEOP);
    if Moderator then
      Error(STR_CHHLP_DROP);
    Error(STR_CHHLP_EMOTE);
    Error(STR_CHHLP_GHOST);
    Error(STR_CHHLP_HELP);
    Error(STR_CHHLP_INVITE);
    if Operator then
      Error(STR_CHHLP_KICK);
    if Moderator then
      Error(STR_CHHLP_LEVEL);
    if Admin then
      Error(STR_CHHLP_LISTFRIENDS);
    if Moderator then
      Error(STR_CHHLP_LIMIT);
    if Moderator then
      Error(STR_CHHLP_MODE);
    Error(STR_CHHLP_MSG);
    if Moderator then
      Error(STR_CHHLP_OP);
    if Moderator then
      Error(STR_CHHLP_OPLIST);
    if Operator then
      Error(STR_CHHLP_OPEMOTE);
    if Operator then
      Error(STR_CHHLP_OPSAY);
    Error(STR_CHHLP_PART);
    if Moderator then
      Error(STR_CHHLP_RAW);
    // if Admin then Error(STR_CHHLP_REMFRIENDS);
    if Moderator then
      Error(STR_CHHLP_REMOVE);
    if Moderator then
      Error(STR_CHHLP_SERVER);
    if Moderator then
      Error(STR_CHHLP_SHOWSERVER);
    if (chTopic in Ch.State) or Operator then
      Error(STR_CHHLP_TOPIC);
    if Operator then
      Error(STR_CHHLP_UNBAN);
    if Operator then
      Error(STR_CHHLP_UNVOICE);
    Error(STR_CHHLP_USERMODE);
    Error(STR_CHHLP_VERSION);
    if Operator then
      Error(STR_CHHLP_VOICE);
    Error(STR_CHHLP_WHOIS);
    Error('.');
  end
  else
    Error(Format(STR_CHSTR_UNKNOWN, [Action]));
  if GCmd.Id <> 0 then
  begin
    if Add_Ch then
      GCmd.Cmd := Trim(Ch.Channel + ' ' + GCmd.Cmd);
    ProcessCommand(Local, queryChannel);
  end;
end;

function CheckFlood(Ch: TChannel; Text: string): Boolean;
var
  I, User_Message, Same_Message: Integer;
  T: Cardinal;
  Str: string;
  Data: TNapCmdEx;
  Usr: POnlineUser;
begin
  T := GetTickCount;
  Result := True;
  if not Flood_Enable then Exit;
  if Ch = nil then Exit;
  if Local = nil then Exit;
  if User^.Level > napUserUser then Exit; // Mods+ Can bypass Flood control
  User_Message := 0;
  Same_Message := 0;
  Ch.History.AddCmd(T, Local.Nick, Text);
  for I := Ch.History.Count - 1 downto 0 do
  begin
    Data := Ch.History.CmdEx(I);
    if (T - Data.Id) > 10000 then
      Ch.History.Delete(I)
    else if Data.Cmd = Local.Nick then
    begin
      Inc(User_Message);
      if Data.Data = Text then
        Inc(Same_Message);
    end;
  end;
  if Same_Message > Flood_Max_Same_Message then
    Result := False
  else if User_Message > Flood_Max_User_Message then
  begin
    if Flood_Warning then
      if not (locFloodWarning in Local.LocalState) then
      begin
        Local.LocalState := Local.LocalState + [locFloodWarning];
        Local.Exec(MSG_SERVER_PUBLIC, Ch.Channel + ' Server ' +
          GetLangT(LNG_FLOODWARN));
        for I := Ch.History.Count - 1 downto 0 do
          if Ch.History.CmdEx(I).Cmd = Local.Nick then
            Ch.History.Delete(I);
        Exit;
      end;
    Result := False;
  end;
  if Result = False then
  begin
    for I := Ch.History.Count - 1 downto 0 do
      if Ch.History.CmdEx(I).Cmd = Local.Nick then
        Ch.History.Delete(I);
    Local.LocalState := Local.LocalState - [locFloodWarning];
    Local.Exec(MSG_SERVER_PUBLIC, Ch.Channel + ' Server ' +
      GetLangT(LNG_FLOODMUZZLE1));
    Str := Ch.Channel + ' Server ' + GetLangT(LNG_FLOODMUZZLE2, User^.UserName);
    for I := 0 to Ch.Users.Count - 1 do
    begin
      Usr := Ch.Users.Items[I];
      if (Usr^.Server = nil) and (Usr <> User) then
        Exec(Usr, MSG_SERVER_PUBLIC, Str);
    end;
    User^.State := User^.State + [userMuzzled];
    Wallop(MSG_SERVER_NOSUCH, wallopFlood, GetLangT(LNG_FLOODMUZZLE3,
      User^.UserName, ServerAlias, Ch.Channel), True);
    WriteAllServers(MSG_SRV_FLOODER, '', Ch.Channel + ' ' + User^.UserName);
    CompleteSyncUser(nil, User);
  end;
end;

procedure Handler_Flooder;
var
  Usr: POnlineUser;
  Ch: TChannel;
  Str: string;
  I: Integer;
begin
  if not CheckParams(2) then Exit;
  Ch := FindChannel(HList.Strings[0]);
  if Ch = nil then Exit;
  Str := Ch.Channel + ' Server ' + GetLangT(LNG_FLOODMUZZLE2, HList.Strings[1]);
  for I := 0 to Ch.Users.Count - 1 do
  begin
    Usr := Ch.Users.Items[I];
    if Usr^.Server = nil then
      Exec(Usr, MSG_SERVER_PUBLIC, Str);
  end;
end;

procedure Handler_public;
var
  Ch: TChannel;
  Usr: POnlineUser;
  Str: string;
  Vis: Boolean;
  I: Integer;
begin
  Tmp_Pos := 490;
  if not IsLogged then Exit;
  if not CheckParams(1) then Exit;
  if Query = queryChannel then Exit;
  Tmp_Pos := 491;
  Ch := FindChannel(HList.Strings[0]);
  if Ch = nil then Exit;
  if Ch.FindUser(User) = -1 then Exit;
  Str := NextParamEx(GCmd.Cmd);
  if not Allow_Rtf_Code and (Pos('{\rtf', Str) > 0) then Exit;
  Tmp_Pos := 492;
  if Trim(Str) = '' then Exit;
  if Prevent_Shouting and (User^.Level < napUserModerator) then
    if AnsiUpperCase(Str) = Str then
    begin
      Str := AnsiLowerCase(Str);
      GCmd.Cmd := Ch.Channel + ' ' + Str;
    end;
  if Length(Str) > 0 then
    if (Str[1] = '>') or (Str[1] = '~') then
      if Query = queryNormal then
      begin
        Query_Channel := Ch.Channel;
        Query := queryChannel;
        Handler_ChannelCommand(Str);
        Exit;
      end;
  Tmp_Pos := 493;
  if (User^.Server = nil) and (userMuzzled in User^.State) then
  begin
    Error(GetLangT(LNG_CANTTALK), True);
    Exit;
  end;
  if User^.Server = nil then
    if not Ch.Operator(User) then
      if chModerated in Ch.State then
        if not StrHash_FindString(Ch.Voices, User^.UserName, True) then
        begin
          Error(GetLangT(LNG_CANTTALK), True);
          Exit;
        end;
  Tmp_Pos := 494;
  if Block_Cqex_Chat then
    BlockCQEXChat(Str);
  if Length(Str) > Max_Channelmsg_Len then
    Str := Copy(Str, 1, Max_Channelmsg_Len);
  if Local <> nil then
    if not CheckFlood(Ch, Str) then Exit;
  Tmp_Pos := 495;
  Vis := Ch.Visible(User);
  for I := 0 to Ch.Users.Count - 1 do
  begin
    Usr := Ch.Users.Items[I];
    if Usr^.Server = nil then
    begin
      {if Vis or (Usr^.Level > napUserUser) then
        Exec(Usr, MSG_SERVER_PUBLIC, Ch.Channel + ' ' + User^.UserName + ' ' + Str)
      else
        Exec(Usr, MSG_SERVER_PUBLIC, Ch.Channel + ' Operator ' + Str);}
      if Vis then
        Exec(Usr, MSG_SERVER_PUBLIC, Ch.Channel + ' ' + User^.UserName + ' ' +
          Str)
      else if Usr^.Level < napUserModerator then
        Exec(Usr, MSG_SERVER_PUBLIC, Ch.Channel + ' Operator ' + Str)
      else
        Exec(Usr, MSG_SERVER_PUBLIC, Ch.Channel + ' Cloaked/' + User^.UserName +
          ' ' + Str);
    end;
  end;
  Tmp_Pos := 496;
  if User^.Server = nil then
    Ch.WriteChannelServers(GCmd.Id, User^.UserName + ' ' + GCmd.Cmd);
end;

procedure Handler_Emote;
var
  Ch: TChannel;
  Usr: POnlineUser;
  Str: string;
  Vis: Boolean;
  I: Integer;
begin
  Tmp_Pos := 500;
  if not IsLogged then Exit;
  if not CheckParams(1) then Exit;
  if (User^.Server = nil) and (userMuzzled in User^.State) then
  begin
    Error(GetLangT(LNG_CANTTALK), True);
    Exit;
  end;
  Tmp_Pos := 501;
  Ch := FindChannel(HList.Strings[0]);
  if Ch = nil then Exit;
  if Ch.FindUser(User) = -1 then Exit;
  if HList.Count = 1 then
    Str := '""'
  else if HList.Count = 2 then
    Str := '"' + HList.Strings[1] + '"'
  else
    Str := '"' + NextParamEx(GCmd.Cmd) + '"';
  if Length(Str) = 2 then Exit;
  // if AnsiUpperCase(Str) = AnsiLowerCase(Str) then Exit;
  Tmp_Pos := 502;
  if Block_Cqex_Chat then
    BlockCQEXChat(Str);
  if not Allow_Rtf_Code and (Pos('{\rtf', Str) > 0) then Exit;
  if Length(Str) > Max_Channelmsg_Len then
    Str := Copy(Str, 1, Max_Channelmsg_Len);
  if Prevent_Shouting and (User^.Level < napUserModerator) then
    if AnsiUpperCase(Str) = Str then
    begin
      Str := AnsiLowerCase(Str);
      GCmd.Cmd := Ch.Channel + ' ' + Str;
    end;
  if Local <> nil then
    if not CheckFlood(Ch, Str) then Exit;
  Vis := Ch.Visible(User);
  Tmp_Pos := 503;
  for I := 0 to Ch.Users.Count - 1 do
  begin
    Usr := Ch.Users.Items[I];
    if Usr^.Server = nil then
    begin
      {if Vis or (Usr^.Level > napUserUser) then
        Exec(Usr, MSG_SERVER_EMOTE, Ch.Channel + ' ' + User^.UserName + ' ' + Str)
      else
        Exec(Usr, MSG_SERVER_EMOTE, Ch.Channel + ' Operator ' + Str);}
      if Vis then
        Exec(Usr, MSG_SERVER_EMOTE, Ch.Channel + ' ' + User^.UserName + ' ' +
          Str)
      else if Usr^.Level < napUserModerator then
        Exec(Usr, MSG_SERVER_EMOTE, Ch.Channel + ' Operator ' + Str)
      else
        Exec(Usr, MSG_SERVER_EMOTE, Ch.Channel + ' Cloaked/' + User^.UserName +
          ' ' + Str);
    end;
  end;
  Tmp_Pos := 504;
  if User^.Server = nil then
    Ch.WriteChannelServers(GCmd.Id, User^.UserName + ' ' + GCmd.Cmd);
end;

procedure Handler_ListChannels;
var
  Ch: TChannel;
  I: Integer;
begin
  Tmp_Pos := 510;
  if not IsLogged then Exit;
  if User = nil then Exit;
  Tmp_Pos := 511;
  for I := 0 to DB_Channels.Count - 1 do
  begin
    Ch := DB_Channels.Items[I];
    if (not (chPrivate in Ch.State)) or (User^.Level > napUserUser) then
      if Ch.Level <= User^.Level then
        case Query of
          queryChannel,
            queryOperServ,
            queryNickServ,
            queryMsgServ,
            queryChanServ: Error('[' + IntToStr(Ch.Users.Count) + '] ' +
              Ch.Channel);
        else
          Exec(User, MSG_SERVER_CHANNEL_LIST, Ch.Channel + ' ' +
            IntToStr(Ch.Users.Count) + ' ' + Ch.Topic);
        end;
  end;
  Tmp_Pos := 512;
  case Query of
    queryChannel,
      queryOperServ,
      queryNickServ,
      queryMsgServ,
      queryChanServ: Error('.');
  else
    Exec(User, MSG_SERVER_CHANNEL_LIST_END, '');
  end;
  Tmp_Pos := 513;
end;

procedure Handler_ListAllChannels;
var
  Ch: TChannel;
  I: Integer;
begin
  Tmp_Pos := 520;
  if not IsLogged then Exit;
  if User = nil then Exit;
  if not Listall_for_Users and (User.Level < napUserModerator) then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  Tmp_Pos := 521;
  for I := 0 to DB_Channels.Count - 1 do
  begin
    Ch := DB_Channels.Items[I];
    case Query of
      queryChannel,
        queryOperServ,
        queryNickServ,
        queryMsgServ,
        queryChanServ: Error('[' + IntToStr(Ch.Users.Count) + '] ' +
          Ch.Channel);
    else
      Exec(User, MSG_SERVER_FULL_CHANNEL_INFO, Ch.Channel + ' ' +
        IntToStr(Ch.Users.Count) + ' ' + IntToStr(Ord(chPrivate in Ch.State)) + ' '
        + IntToStr(Ord(Ch.Level)) + ' ' + IntToStr(Ch.Limit) + ' ' +
        AddStr(Ch.Topic));
    end;
  end;
  Tmp_Pos := 522;
  case Query of
    queryChannel,
      queryOperServ,
      queryNickServ,
      queryMsgServ,
      queryChanServ: Error('.');
  else
    Exec(User, MSG_CLIENT_FULL_CHANNEL_LIST, '');
  end;
end;

procedure Handler_ChannelTopic;
var
  Ch: TChannel;
begin
  Tmp_Pos := 530;
  if Query = queryServer then
  begin
    if not CheckParams(2) then Exit;
    Ch := FindChannel(HList.Strings[0]);
    if Ch = nil then Exit;
    Ch.SetTopic(NextParamEx(GCmd.Cmd));
    Exit;
  end;
  Tmp_Pos := 531;
  if not IsLogged then Exit;
  if not CheckParams(1) then Exit;
  Tmp_Pos := 532;
  Ch := FindChannel(HList.Strings[0]);
  if Ch = nil then Exit;
  if Ch.FindUser(User) = -1 then
  begin
    if User.Level > napUserUser then
      if HList.Count > 1 then
      begin
        Ch.SetTopic(NextParamEx(GCmd.Cmd));
        if User^.Server = nil then
          WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
      end;
    Exit;
  end;
  Tmp_Pos := 533;
  if HList.Count = 1 then
  begin
    Exec(User, MSG_SERVER_TOPIC, Ch.Channel + ' ' + Ch.Topic);
    Exit;
  end;
  if not Ch.Operator(User) then
    if not (chTopic in Ch.State) then
    begin
      PermissionDenied('', True);
      Exit;
    end;
  Tmp_Pos := 534;
  Ch.SetTopic(NextParamEx(GCmd.Cmd));
  Wallop(MSG_SERVER_NOSUCH, wallopChannel, GetLangT(LNG_CHTOPIC,
    Level2Str(User^.Level), User^.UserName, Ch.Channel, Ch.Topic), True);
  if User^.Server = nil then
    WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
end;

procedure Handler_ChanBanList;
var
  Ch: TChannel;
  P: PStringHashItem;
begin
  Tmp_Pos := 540;
  if not IsLogged then Exit;
  if User = nil then Exit;
  if not CheckParams(1) then Exit;
  Ch := FindChannel(HList.Strings[0]);
  if Ch = nil then
  begin
    Error(GetLangT(LNG_NOCHANNEL), True);
    Exit;
  end;
  Tmp_Pos := 541;
  if not Ch.Operator(User) then
    if User^.Level < napUserModerator then
    begin
      PermissionDenied('', True);
      Exit;
    end;
  Tmp_Pos := 542;
  P := Ch.Bans.First;
  while P <> nil do
  begin
    case Query of
      queryOperServ,
        queryNickServ,
        queryChanServ,
        queryMsgServ,
        queryChannel: Error(P^.Data);
    else
      Exec(User, MSG_SERVER_CHANNEL_BAN_LIST, Ch.Channel + ' ' + P^.Data);
    end;
    P := P^.Next;
  end;
  Tmp_Pos := 543;
  case Query of
    queryOperServ,
      queryNickServ,
      queryChanServ,
      queryMsgServ,
      queryChannel: Error('.');
  else
    Exec(User, MSG_CLIENT_CHANNEL_BAN_LIST, Ch.Channel);
  end;
  Tmp_Pos := 544;
end;

procedure Handler_ChanBan; // 422
var
  Ch: TChannel;
  User2: POnlineUser;
  Ban, Name, Ip: string;
begin
  Tmp_Pos := 550;
  if Query <> queryServer then
    if not IsLogged then Exit;
  if not CheckParams(2) then Exit;
  Ch := FindChannel(HList.Strings[0]);
  Tmp_Pos := 551;
  if Ch = nil then
  begin
    Error(RS_Handler_ChanBanFailed, True);
    Exit;
  end;
  Tmp_Pos := 552;
  if Query <> queryServer then
    if not Ch.Operator(User) then
      if User^.Level < napUserModerator then
      begin
        PermissionDenied('', True);
        Exit;
      end;
  Tmp_Pos := 553;
  SplitBan(HList.Strings[1], Name, Ip);
  Ban := JoinBan(Name, Ip);
  if GCmd.Id = MSG_CLIENT_CHANNEL_BANIP then
  begin
    User2 := DB_Online.FindUser(Name);
    if User2 = nil then
    begin
      UserIsOffline(HList.Strings[1], True);
      Exit;
    end;
    Ip := Decode_Ip(User2^.Ip);
    Ban := JoinBan(Name, Ip);
  end;
  Tmp_Pos := 554;
  if StrHash_FindString(Ch.Bans, Ban, True) then
  begin
    Error(GetLangT(LNG_CHALREADYBANNED), True);
    Exit;
  end;
  StrHash_Add(Ch.Bans, Ban);
  Tmp_Pos := 555;
  if Query <> queryServer then
  begin
    Wallop(MSG_SERVER_NOSUCH, wallopChannel, GetLangT(LNG_CHBANNED,
      User^.UserName, Ban, Ch.Channel), True);
    if User^.Server = nil then
      WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
  end;
end;

procedure Handler_ChanUnban; // 423
var
  Ch: TChannel;
begin
  Tmp_Pos := 560;
  if Query <> queryServer then
    if not IsLogged then Exit;
  if not CheckParams(2) then Exit;
  Ch := FindChannel(HList.Strings[0]);
  if Ch = nil then
  begin
    Error(RS_Handler_ChanUnBanNoSuchChannel, True);
    Exit;
  end;
  Tmp_Pos := 561;
  if Query <> queryServer then
    if not Ch.Operator(User) then
      if User^.Level < napUserModerator then
      begin
        PermissionDenied('', True);
        Exit;
      end;
  Tmp_Pos := 562;
  if not StrHash_Delete(Ch.Bans, HList.Strings[1], True) then
  begin
    Error(RS_Handler_ChanUnBanNoSuchBan, True);
    Exit;
  end;
  Tmp_Pos := 563;
  if Query <> queryServer then
  begin
    Wallop(MSG_SERVER_NOSUCH, wallopChannel, GetLangT(LNG_CHUNBANNED,
      User^.UserName, HList.Strings[1], Ch.Channel), True);
    if User^.Server = nil then
      WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
  end;
end;

procedure Handler_ChanBanClear;
var
  Ch: TChannel;
begin
  Tmp_Pos := 564;
  if Query <> queryServer then
    if not IsLogged then Exit;
  if not CheckParams(1) then Exit;
  Ch := FindChannel(HList.Strings[0]);
  if Ch = nil then
  begin
    Error(GetLangT(LNG_NOCHANNEL), True);
    Exit;
  end;
  Tmp_Pos := 565;
  if Query <> queryServer then
    if not Ch.Operator(User) then
      if User^.Level < napUserModerator then
      begin
        PermissionDenied('', True);
        Exit;
      end;
  if Ch.Bans.Count < 1 then
  begin
    Error(RS_Handler_ChanNoBan, True);
    Exit;
  end;
  Tmp_Pos := 566;
  StrHash_Clear(Ch.Bans);
  if Query <> queryServer then
  begin
    Wallop(MSG_SERVER_NOSUCH, wallopChannel, Format(RS_Handler_ChanBanClear,
      [User^.UserName, Ch.Channel]), True);
    if User^.Server = nil then
      WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
  end;
end;

procedure Handler_Whois;
var
  User2: POnlineUser;
  Rec: PRegisteredUser;
  L2: TLocalUser;
  I, J: Integer;
  State, Str, Display_Str: string;
  Ch: TChannel;
  Cloaked: Boolean;
  P: PNapCmdEx;
begin
  Tmp_Pos := 570;
  if not IsLogged then Exit;
  if not CheckParams(1) then Exit;
  User2 := DB_Online.FindUser(GCmd.Cmd);
  Tmp_Pos := 571;
  Cloaked := False;
  if (User2 <> nil) and (User2^.Level > napUserUser) and
    (userCloaked in User2^.State) and (User^.Level < napUserModerator) then
    Cloaked := True;
  Tmp_Pos := 1224;
  if (User2 = nil) or cloaked then
  begin
    Rec := DB_Registered.FindUser(GCmd.Cmd);
    Tmp_Pos := 1225;
    if Rec = nil then
    begin
      I := DB_Whowas.FindByCmd(AnsiLowerCase(GCmd.Cmd));
      Str := GCmd.Cmd + ' "User" 0';
      Tmp_Pos := 1226;
      if (I <> -1) and (not cloaked) then
      begin
        P := DB_Whowas.Items[I];
        SplitString(P.Data, HLst);
        Tmp_Pos := 1227;
        if HLst.Count > 2 then
          Str := GCmd.Cmd + ' "' + HLst.Strings[1] + '" ' + HLst.Strings[2];
      end;
      Tmp_Pos := 1228;
      // Exec(User, MSG_SERVER_WHOWAS, Str);
      Error(Format(RS_Handler_Unregistered, [GCmd.Cmd]), True);
      Error(RS_Handler_UserOffline, True);
      Exit;
    end;
    Tmp_Pos := 572;
    Str := GCmd.Cmd + ' "' + Level2Str(Rec^.Level) + '" ' +
      IntToStr(Rec^.Last_Seen);
    Exec(User, MSG_SERVER_WHOWAS, Str);
    Error(RS_Handler_UserOffline, True);
    Exit;
  end;
  Tmp_Pos := 1229;
  J := Current_Time_T - User2^.Last_Seen_T;
  if J < 0 then
    J := 0;
  Tmp_Pos := 573;
  Str := GCmd.Cmd + ' "' + Level2Str(User2^.Level) + '" ' + IntToStr(J) + ' "';
  for I := 0 to DB_Channels.Count - 1 do
  begin
    Ch := DB_Channels.Items[I];
    if Ch.FindUser(User2) <> -1 then
      Str := Str + Ch.Channel + ' ';
  end;
  Tmp_Pos := 574;
  Str := Str + '" ';
  if User2^.Server = nil then
    State := 'Active'
  else
    State := 'Remote';
  if (User^.Level > napUserUser) and (userCloaked in User2^.State) then
    State := State + ' (Cloaked)';
  // -----------------------------------Stateの拡張
  User2 := DB_Online.FindUser(HList.Strings[0]);
  if User2^.Server = nil then
    L2 := FindLocalUser(User2^.UserName)
  else
    L2 := nil;
  if (L2 <> nil) and (Local <> nil) and StrHash_FindString(L2.Hotlist,
    AnsiLowerCase(Local.Nick), True) then
    State := State + ' Friend';
      // Whois先がローカルでホットリストにWhois元がいるときは情報追加
  Str := Str + AddStr(State) + ' ';
  Str := Str + IntToStr(User2^.Shared) + ' ' + IntToStr(User2^.Downloads) + ' '
    +
    IntToStr(User2^.Uploads) + ' ' + IntToStr(Ord(User2^.Speed)) + ' "' +
    User2^.Software;
  L2 := User2^.Local;
  if L2 <> nil then
    case L2.SoftwareID of
      softNapchan: Str := Str + #13#10' Size      : ' + FormatFloat('0,',
        L2.Shared_Size shr 10) + ' KB"';
    else
      Str := Str + #13#10'Size'#9#9': ' + FormatFloat('0,', L2.Shared_Size shr
        10) + ' KB"';
    end
  else
    Str := Str + '"';
  I := 0;
  Tmp_Pos := 1230;
  if User^.Level > napUserUser then
    I := 1;
  if User = User2 then
    I := 1;
  Tmp_Pos := 575;
  if I = 1 then
  begin // Complete whois Reply
    Str := Str + ' ' + IntToStr(User2^.Total_Down) + ' ' +
      IntToStr(User2^.Total_Up) + ' ' + Decode_Ip(User2^.Ip) + '(' +
      GetHostByIP(Decode_Ip(User2^.Ip)) + ') ';
    L2 := User2^.Local;
    Tmp_Pos := 1231;
    if (L2 <> nil) and (L2.Socket <> INVALID_SOCKET) then
      Str := Str + IntToStr(TCPSocket_GetRemoteSin(L2.Socket).Sin_Port) + ' '
    else
      Str := Str + '0 ';
    Str := Str + IntToStr(User2^.DataPort) + ' anon@' +
      GetServerAlias(User2^.Server);
    if User^.Level = napUserUser then
      Display_Str := GetServerAlias(User2^.Server)
    else
      Display_Str := GetServerName(User2^.Server);
    Str := Str + ' ' + Display_Str;
  end;
  Tmp_Pos := 576;
  Exec(User, MSG_SERVER_WHOIS_RESPONSE, Str);
  if User2^.Level > napUserUser then
    if User <> User2 then // do not Inform self
      if not (userHideWhois in User2^.State) then
        Exec(User2, MSG_SERVER_NOSUCH, GetLangT(LNG_WHOISMOD,
          AddStr(Level2Str(User^.Level)), User^.UserName, Decode_Ip(User^.Ip)));
  Tmp_Pos := 577;
  if (User2 = User) and (Query = queryNormal) then
    // Probably Napster Asking info About itself. for 10.3+ It would Be better to receive Stats.
    if User^.Server = nil then
      Handler_Stats;
end;

procedure BanUser(Ban, Server: string; Time: Integer; Reason: string;
  Write_All: Boolean);
begin
  Tmp_Pos := 580;
  if Time < 1 then Exit;
  Ban := AnsiLowerCase(Ban);
  Tmp_Pos := 581;
  if DB_Bans.FindRec(Ban) <> -1 then Exit;
  DB_Bans.Ban('Server: ' + Server, Ban, Reason, Time);
  Tmp_Pos := 582;
  if Wallop_Serverban then
  begin
    if Time = 0 then
      Wallop(MSG_SERVER_NOSUCH, wallopSBan, Format(RS_Handler_ServerBan,
        [Server, Ban, Reason]), True)
    else
      Wallop(MSG_SERVER_NOSUCH, wallopSBan, Format(RS_Handler_ServerBan2,
        [Server, Ban, BanTime2Str(Time), Reason]), True);
  end;
  if Write_All then
    WriteAllServers(MSG_SRV_SERVERBAN, '', AddStr(Ban) + ' ' + AddStr(Server) +
      ' ' + IntToStr(Time) + ' ' + AddStr(Reason));
end;

procedure Ban(Ban, Reason: string; TimeOut: Time_T; Write_All: Boolean);
begin
  Tmp_Pos := 583;
  Ban := AnsiLowerCase(Ban);
  if Ban = '' then Exit;
  if Ban = '*' then Exit;
  if Ban = '*!*' then Exit;
  Tmp_Pos := 584;
  if DB_Bans.FindRec(Ban) <> -1 then
  begin
    if User^.Server = nil then
      Exec(User, MSG_SERVER_NOSUCH, RS_Handler_AlreadyBanned);
    Exit;
  end;
  Tmp_Pos := 585;
  DB_Bans.Ban(User^.UserName, Ban, Reason, TimeOut);
  if TimeOut = 0 then
    Wallop(MSG_SERVER_NOSUCH, wallopMBan, Format(RS_Handler_Ban,
      [User^.UserName, Ban, Reason]), True)
  else
    Wallop(MSG_SERVER_NOSUCH, wallopMBan, Format(RS_Handler_Ban2,
      [User^.UserName, Ban, BanTime2Str(TimeOut), Reason]), True);
  Tmp_Pos := 586;
  if Write_All then
    WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
end;

procedure Handler_BanEx; // 8116
var
  TimeOut: Time_T;
  Reason: string;
begin
  Tmp_Pos := 600;
  if not IsLogged then Exit;
  if not CheckLevel('', napUserModerator) then Exit;
  if not CheckParams(1) then Exit;
  Tmp_Pos := 601;
  if HList.Count < 2 then
    HList.Add(IntToStr(Def_Ban_Timeout));
  Tmp_Pos := 602;
  TimeOut := StrToIntDef(HList.Strings[1], Def_Ban_Timeout);
  if (TimeOut < 60) and (TimeOut <> 0) then Exit;
  if HList.Count = 3 then
    Reason := HList.Strings[2]
  else
    Reason := NextParamEx(GCmd.Cmd, 2);
  Ban(HList.Strings[0], Reason, TimeOut, User^.Server = nil);
end;

procedure Handler_Ban; // 612
var
  Reason: string;
begin
  Tmp_Pos := 590;
  if not IsLogged then Exit;
  if not CheckLevel('', napUserModerator) then Exit;
  if not CheckParams(1) then Exit;
  if HList.Count = 3 then
    if IsDigit(HList.Strings[2]) then
    begin
      GCmd.Id := MSG_CLIENT_BANEX;
      GCmd.Cmd := AddStr(HList.Strings[0]) + ' ' + AddStr(HList.Strings[2]) + ' '
        + AddStr(HList.Strings[1]);
      Handler_BanEx;
    end;
  Tmp_Pos := 591;
  if HList.Count = 2 then
    Reason := HList.Strings[1]
  else
    Reason := NextParamEx(GCmd.Cmd);
  Ban(HList.Strings[0], Reason, Def_Ban_Timeout, User^.Server = nil);
end;

procedure Handler_Unban; // 614
var
  Ban: string;
  I: Integer;
  B: PBan;
begin
  Tmp_Pos := 610;
  if not IsLogged then Exit;
  if not CheckLevel('', napUserModerator) then Exit;
  if not CheckParams(1) then Exit;
  Ban := AnsiLowerCase(HList.Strings[0]);
  Tmp_Pos := 612;
  I := DB_Bans.FindRec(Ban);
  while I <> -1 do
  begin
    B := DB_Bans.Items[I];
    Wallop(MSG_SERVER_NOSUCH, wallopMBan, Format(RS_Handler_UnBan,
      [User^.UserName, Ban, B^.Reason, NextParamEx(GCmd.Cmd)]), True);
    DB_Bans.Delete(I);
    I := DB_Bans.FindRec(Ban);
  end;
  Tmp_Pos := 613;
  if User^.Server = nil then
    WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
end;

procedure Handler_Banlist; // 615
var
  Str: string;
  I: Integer;
  B: PBan;
begin
  Tmp_Pos := 620;
  if not IsLogged then Exit;
  if not CheckLevel('', napUserModerator) then Exit;
  if Length(GCmd.Cmd) > 2 then
  begin // ????? TekNap Sends 615 to ban User ???
    GCmd.Id := MSG_CLIENT_BAN;
    Handler_Ban;
  end;
  Tmp_Pos := 621;
  for I := 0 to DB_Bans.Count - 1 do
  begin
    B := DB_Bans.Items[I];
    case Query of
      queryChannel,
        queryOperServ,
        queryNickServ,
        queryMsgServ,
        queryChanServ:
        begin
          Str := B^.User + '!' + B^.Ip + ' ' + B^.Admin + ' ' +
            UnixTimeToStr(B^.Time) + '-';
          if B^.Expires > 0 then
            Str := Str + UnixTimeToStr(B^.Expires)
          else
            Str := Str + RS_BantimeForever;
          Str := Str + ': ' + B^.Reason;
          Error(Str);
        end;
    else
      Str := B^.User + ' ' + B^.Ip + ' "' + B^.Reason + '" ' + IntToStr(B^.Time)
        + ' ';
      if B^.Expires > 0 then
        Str := Str + IntToStr(B^.Expires - B^.Time)
      else
        Str := Str + '0';
      Exec(User, MSG_SERVER_IP_BANLIST, Str);
    end;
  end;
  Tmp_Pos := 622;
  case Query of
    queryChannel,
      queryOperServ,
      queryNickServ,
      queryMsgServ,
      queryChanServ: Error(GetLangT(LNG_ITEMSLISTED, IntToStr(DB_Bans.Count)));
  else
    Exec(User, MSG_CLIENT_BANLIST, '');
  end;
end;

procedure Handler_ChangeBan;
var
  B: PBan;
  Exp: Time_T;
  Str, Reason: string;
  I: Integer;
  Name, Ip: string;
  A: Boolean;
begin
  Tmp_Pos := 623;
  if not IsLogged then Exit;
  if not CheckParams(2) then Exit;
  if not CheckLevel('', napUserModerator) then Exit;
  if HList.Count = 3 then
    HList.Add('');
  Tmp_Pos := 624;
  SplitBan(HList.Strings[0], Name, Ip);
  for I := 0 to DB_Bans.Count - 1 do
  begin
    B := DB_Bans.Items[I];
    if (B^.User = Name) and (B^.Ip = Ip) then
    begin
      Exp := StrToIntDef(HList.Strings[1], B^.Expires);
      Reason := HList.Strings[2];
      A := False;
      if Exp <> B^.Expires then
      begin
        B^.Expires := Exp;
        if B^.Expires > 0 then
          Str := UnixTimeToStr(B^.Expires)
        else
          Str := GetLangT(LNG_BANS_NEVER_EXPIRE);
        Wallop(MSG_SERVER_NOSUCH, wallopMBan, GetLangT(LNG_BANEXPCHANGE,
          User^.UserName, HList.Strings[0], Str), True);
        A := True;
      end;
      Tmp_Pos := 625;
      if (Reason <> '') and (B^.Reason <> Reason) then
      begin
        B^.Reason := Reason;
        Wallop(MSG_SERVER_NOSUCH, wallopMBan, GetLangT(LNG_BANREASONCHANGE,
          User^.UserName, HList.Strings[0], Reason), True);
        A := True;
      end;
      if A then
        if User^.Server = nil then
          WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
      Exit;
    end;
  end;
end;

procedure Handler_ChannelDeclineInvite;
var
  I: Integer;
  S: PNapCmdEx;
begin
  if Local = nil then Exit;
  for I := DB_Invitations.Count - 1 downto 0 do
  begin
    S := DB_Invitations.Items[I];
    if S^.Data = Local.Nick then
      DB_Invitations.Delete(I)
    else if (GetTickCount - S^.Id) > EXPIRE_INVITATION then
      DB_Invitations.Delete(I);
  end;
end;

procedure Handler_ChannelInvite;
var
  Ch: TChannel;
  Str: string;
  User2: POnlineUser;
  L2: TLocalUser;
  S: PNapCmdEx;
  I: Integer;
begin
  Tmp_Pos := 630;
  if not IsLogged then Exit;
  if not CheckParams(2) then Exit;
  if GCmd.Id = MSG_CLIENT_CHANNEL_INVITE_OPENNAP then
  begin // Swap arguments
    Str := HList.Strings[1];
    HList.Strings[1] := HList.Strings[0];
    HList.Strings[0] := Str;
  end;
  Tmp_Pos := 631;
  Ch := FindChannel(HList.Strings[1]);
  if Ch = nil then
  begin
    Error(GetLangT(LNG_NOCHANNEL), True);
    Exit;
  end;
  if (User^.Level < napUserModerator) and (Ch.FindUser(User) = -1) then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  Tmp_Pos := 632;
  if User^.Level < Ch.Level then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  User2 := DB_Online.FindUser(HList.Strings[0]);
  if User2 = nil then
  begin
    NoSuchUser(True);
    Exit;
  end;
  Tmp_Pos := 633;
  if Ch.FindUser(User2) <> -1 then
  begin
    Error(GetLangT(LNG_CHJOINED, User2^.UserName, Ch.Channel), True);
    Exit;
  end;
  if User2^.Server <> nil then
  begin
    if Local = nil then Exit;
    User2^.Server.Exec(GCmd.Id, User^.UserName + ' ' + GCmd.Cmd);
    Exit;
  end;
  Tmp_Pos := 634;
  if User2^.Server <> nil then
  begin
    User2^.Server.Exec(MSG_SRV_SETLASTINV, User2^.UserName + ' ' +
      HList.Strings[1]);
  end
  else
  begin
    L2 := User2^.Local;
    if L2 = nil then Exit; // Weird error
    for I := DB_Invitations.Count - 1 downto 0 do
    begin
      S := DB_Invitations.Items[I];
      if S^.Data = L2.Nick then
        DB_Invitations.Delete(I)
      else if (GetTickCount - S^.Id) > EXPIRE_INVITATION then
        DB_Invitations.Delete(I);
    end;
    DB_Invitations.AddCmd(GetTickCount, HList.Strings[1], L2.Nick);
  end;
  Tmp_Pos := 635;
  Exec(User2, MSG_SERVER_NOSUCH, GetLangT(LNG_INVITE, Ch.Channel,
    User^.UserName));
  Exec(User2, MSG_CLIENT_CHANNEL_INVITE, User^.UserName + ' ' + Ch.Channel + ' "'
    + Ch.Topic + '" 0 0');
end;

procedure Handler_Muzzle;
var
  User2: POnlineUser;
  Reg: PRegisteredUser;
  Str: string;
  // L: TNapUserLevel;
  B: Boolean;
begin
  Tmp_Pos := 640;
  if not IsLogged then Exit;
  if not CheckLevel('', napUserModerator) then Exit;
  if not CheckParams(1) then Exit;
  User2 := DB_Online.FindUser(HList.Strings[0]);
  if User2^.Level >= User^.Level then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  Reg := DB_Registered.FindUser(HList.Strings[0]);
  Tmp_Pos := 641;
  if (User2 = nil) and (Reg = nil) then
  begin
    NoSuchUser(True);
    Exit;
  end;
  if User2 <> nil then
  begin
    // L := User2^.Level;
    B := userMuzzled in User2^.State;
  end
  else
  begin
    // L := Reg^.Level;
    B := userMuzzled in Reg^.State;
  end;
  Tmp_Pos := 642;
  // if L > napUserUser then // old routine- no muzzling any staff
  // begin
  //   PermissionDenied('', True);
  //   Exit;
  // end;
  if B then
  begin
    Error(GetLangT(LNG_MUZZLE1), True);
    Exit;
  end;
  Tmp_Pos := 643;
  if User2 <> nil then
    User2^.State := User2^.State + [userMuzzled];
  if Reg <> nil then
    Reg^.State := Reg^.State + [userMuzzled];
  case HList.Count of
    1: Str := '';
    2: Str := ' : ' + HList.Strings[1];
  else
    Str := ' : ' + Trim(NextParamEx(GCmd.Cmd));
  end;
  Wallop(MSG_SERVER_NOSUCH, wallopMuzzle, GetLangT(LNG_MUZZLE, HList.Strings[0],
    User^.UserName) + Str, True);
  Tmp_Pos := 644;
  if User^.Server = nil then
    WriteAllServers(MSG_CLIENT_MUZZLE, User^.UserName, GCmd.Cmd);
  if User2 <> nil then
    if User2^.Server = nil then
      Exec(User2, MSG_SERVER_NOSUCH, GetLangT(LNG_MUZZLE3,
        NextParamEx(GCmd.Cmd)));
end;

procedure Handler_Unmuzzle;
var
  User2: POnlineUser;
  Reg: PRegisteredUser;
  Str: string;
  // L: TNapUserLevel;
  B: Boolean;
begin
  Tmp_Pos := 645;
  if not IsLogged then Exit;
  if not CheckLevel('', napUserModerator) then Exit;
  if not CheckParams(1) then Exit;
  User2 := DB_Online.FindUser(HList.Strings[0]);
  Reg := DB_Registered.FindUser(HList.Strings[0]);
  if (User2 = nil) and (Reg = nil) then
  begin
    NoSuchUser(True);
    Exit;
  end;
  Tmp_Pos := 646;
  if User2 <> nil then
  begin
    // L := User2^.Level;
    B := userMuzzled in User2^.State;
  end
  else
  begin
    // L := Reg^.Level;
    B := userMuzzled in Reg^.State;
  end;
  {if L > User^.Level then
    begin
      PermissionDenied(True);
      Exit;
    end;}// mods+ can unmuzzle all other mods+ if accedently got muzzled
  Tmp_Pos := 647;
  if B = False then
  begin
    Error(GetLangT(LNG_UNMUZZLE1), True);
    Exit;
  end;
  if User <> nil then
    User2^.State := User2^.State - [userMuzzled];
  if Reg <> nil then
    Reg^.State := Reg^.State - [userMuzzled];
  case HList.Count of
    1: Str := '';
    2: Str := ' : ' + HList.Strings[1];
  else
    Str := ' : ' + Trim(NextParamEx(GCmd.Cmd));
  end;
  Tmp_Pos := 648;
  Wallop(MSG_SERVER_NOSUCH, wallopMuzzle, GetLangT(LNG_UNMUZZLE,
    HList.Strings[0], User^.UserName) + Str, True);
  if User^.Server = nil then
    WriteAllServers(MSG_CLIENT_UNMUZZLE, User^.UserName, GCmd.Cmd);
  if User2 <> nil then
    if User2^.Server = nil then
      Exec(User2, MSG_SERVER_NOSUCH, GetLangT(LNG_UNMUZZLE3));
end;

procedure Handler_MuzzleInv;
var
  User2: POnlineUser;
  Reg: PRegisteredUser;
  B: Boolean;
  // L: TNapUserLevel;
begin
  Tmp_Pos := 650;
  if not IsLogged then Exit;
  if not CheckLevel('', napUserModerator) then Exit;
  if not CheckParams(1) then Exit;
  User2 := DB_Online.FindUser(HList.Strings[0]);
  Reg := DB_Registered.FindUser(HList.Strings[0]);
  Tmp_Pos := 651;
  if User2 = nil then
  begin
    NoSuchUser(True);
    Exit;
  end;
  if User2 <> nil then
  begin
    B := userMuzzled in User2^.State;
    // L := User2^.Level;
  end
  else
  begin
    B := userMuzzled in Reg^.State;
    // L := Reg^.Level;
  end;
  Tmp_Pos := 652;
  { if L >= User^.Level then
   begin
     PermissionDenied(True);
     Exit;
   end;}
  if B then
  begin
    GCmd.Id := MSG_CLIENT_UNMUZZLE;
    Handler_Unmuzzle;
  end
  else
  begin
    GCmd.Id := MSG_CLIENT_MUZZLE;
    Handler_Muzzle;
  end;
end;

procedure Handler_AlterPort; // 613
var
  User2: POnlineUser;
  I: Integer;
begin
  if not IsLogged then Exit;
  if not CheckLevel('Alter_Port()', napUserAdmin) then Exit;
  if not CheckParams(2) then Exit;
  User2 := DB_Online.FindUser(HList.Strings[0]);
  if User2 = nil then
  begin
    UserIsOffline(HList.Strings[0], True);
    Exit;
  end;
  if User2^.Level >= User^.Level then
  begin
    PermissionDenied(RS_Handler_AlterPortFailed, True);
    Exit;
  end;
  I := StrToIntDef(HList.Strings[1], -1);
  if (I < 0) or (I > 65535) then
  begin
    Error(Format(RS_Handler_InvalidPort, [HList.Strings[1]]), True);
    Exit;
  end;
  if User2^.DataPort <> I then
  begin
    Wallop(MSG_SERVER_NOSUCH, wallopChange, Format(RS_Handler_AlterPort,
      [User^.UserName, User2^.UserName, I, NextParamEx(GCmd.Cmd, 2)]), True);
    User2^.DataPort := I;
  end;
  if User2^.Server = nil then
    Exec(User2, GCmd.Id, HList.Strings[1]);
  if User^.Server = nil then
    WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
end;

procedure Handler_AlterSpeed; // 625
var
  User2: POnlineUser;
  I: Integer;
begin
  if not IsLogged then Exit;
  if not CheckLevel('', napUserModerator) then Exit;
  if not CheckParams(2) then Exit;
  I := StrToIntDef(HList.Strings[1], -1);
  if (I < 0) or (I > 10) then
  begin
    Error(RS_Handler_InvalidSpeed, True);
    Exit;
  end;
  User2 := DB_Online.FindUser(HList.Strings[0]);
  if User2 = nil then
  begin
    UserIsOffline(HList.Strings[0], True);
    Exit;
  end;
  if User2^.Level >= User^.Level then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  if Ord(User2^.Speed) = I then
  begin
    if User^.Server = nil then
      Error(Format(RS_Handler_AlterSpeedFailed, [HList.Strings[0],
        HList.Strings[1]]), True);
    Exit;
  end;
  User2^.Speed := TNapSpeed(I);
  Wallop(MSG_SERVER_NOSUCH, wallopChange, GetLangT(LNG_ALTERSPEED,
    User^.UserName, User2^.UserName, HList.Strings[1]), True);
  if User^.Server = nil then
    WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
end;

procedure Handler_Relay;
var
  User2: POnlineUser;
begin
  Tmp_Pos := 660;
  if not IsLogged then Exit;
  if not CheckParams(1) then Exit;
  User2 := DB_Online.FindUser(HList.Strings[0]);
  if User2 = nil then
  begin
    if AnsiLowerCase(HList.Strings[0]) <> 'server' then
      UserIsOffline(HList.Strings[0], True);
    Exit;
  end;
  Tmp_Pos := 661;
  if HList.Count > 1 then
    Exec(User2, GCmd.Id, User^.UserName + ' ' + NextParamEx(GCmd.Cmd))
  else
    Exec(User2, GCmd.Id, User^.UserName);
end;

procedure Handler_Relay2;
var
  User2: POnlineUser;
  L2: TLocalUser;
begin
  Tmp_Pos := 662;
  if not IsLogged then Exit;
  if not CheckParams(1) then Exit;
  User2 := DB_Online.FindUser(HList.Strings[0]);
  if User2 = nil then
  begin
    if AnsiLowerCase(HList.Strings[0]) <> 'server' then
      UserIsOffline(HList.Strings[0], True);
    Exit;
  end;
  Tmp_Pos := 663;
  L2 := User2^.Local;
  if L2 <> nil then
    if User^.Level < napUserModerator then
      if StrHash_FindString(L2.Ignored, AnsiLowerCase(User^.UserName), False)
        then
      begin
        UserIsOffline(HList.Strings[0], True);
        Exit;
      end;
  Tmp_Pos := 664;
  if HList.Count > 1 then
    Exec(User2, GCmd.Id, User^.UserName + ' ' + NextParamEx(GCmd.Cmd))
  else
    Exec(User2, GCmd.Id, User^.UserName);
end;

procedure Handler_Pong;
begin
  if User^.Server = nil then
    if (not (locPingable in Local.LocalState)) and (AnsiLowerCase(GCmd.Cmd) =
      'server') then
    begin
      Local.LocalState := Local.LocalState + [locPingable];
      Exit;
    end;
  Handler_Relay;
end;

procedure Handler_UserMode;
var
  St: TUserState;
  Str: string;
begin
  if not IsLogged then Exit;
  SplitString(UpperCase(GCmd.Cmd), HList);
  if HList.Count = 0 then
  begin
    Str := '';
    if Query <> queryNormal then
    begin
      if userHideErrors in User^.State then
        Str := '-ERROR '
      else
        Str := '+ERROR ';
      if userHideAnnouncements in User^.State then
        Str := Str + '-ANNOUNCE'
      else
        Str := Str + '+ANNOUNCE';
      if User^.Level > napUserUser then
      begin
        // if userHideBans in User^.State then Str := Str + ' -BAN '
        // else Str := Str + ' +BAN ';
        if userHideMBans in User^.State then
          Str := Str + ' -MBAN '
        else
          Str := Str + ' +MBAN ';
        if userHideMBanConn in User^.State then
          Str := Str + '-MBANCONN '
        else
          Str := Str + '+MBANCONN ';
        if userHideSBans in User^.State then
          Str := Str + '-SBAN '
        else
          Str := Str + '+SBAN ';
        if userHideSBanConn in User^.State then
          Str := Str + '-SBANCONN '
        else
          Str := Str + '+SBANCONN ';
        if userHideChange in User^.State then
          Str := Str + '-CHANGE '
        else
          Str := Str + '+CHANGE ';
        if userHideKill in User^.State then
          Str := Str + '-KILL '
        else
          Str := Str + '+KILL ';
        if userHideLevel in User^.State then
          Str := Str + '-LEVEL '
        else
          Str := Str + '+LEVEL ';
        if userHideServer in User^.State then
          Str := Str + '-SERVER '
        else
          Str := Str + '+SERVER ';
        if userHideMuzzle in User^.State then
          Str := Str + '-MUZZLE '
        else
          Str := Str + '+MUZZLE ';
        if userHidePort in User^.State then
          Str := Str + '-PORT '
        else
          Str := Str + '+PORT ';
        if userHideWallop in User^.State then
          Str := Str + '-WALLOP '
        else
          Str := Str + '+WALLOP ';
        if userHideCloak in User^.State then
          Str := Str + '-CLOAK '
        else
          Str := Str + '+CLOAK ';
        if userHideFlood in User^.State then
          Str := Str + '-FLOOD '
        else
          Str := Str + '+FLOOD ';
        if userHidePM in User^.State then
          Str := Str + '-MSG '
        else
          Str := Str + '+MSG ';
        if userHideWhois in User^.State then
          Str := Str + '-WHOIS '
        else
          Str := Str + '+WHOIS ';
        if userHideBrowse in User^.State then
          Str := Str + '-BROWSE '
        else
          Str := Str + '+BROWSE ';
        if userHideMotd in User^.State then
          Str := Str + '-MOTD '
        else
          Str := Str + '+MOTD ';
        if userHideFriends in User^.State then
          Str := Str + '-FRIEND '
        else
          Str := Str + '+FRIEND ';
        if userHideChannel in User^.State then
          Str := Str + '-CHANNEL '
        else
          Str := Str + '+CHANNEL ';
        if userHideRegister in User^.State then
          Str := Str + '-REGISTER '
        else
          Str := Str + '+REGISTER ';
        if userHideVar in User^.State then
          Str := Str + '-VAR '
        else
          Str := Str + '+VAR ';
        if userHidePing in User^.State then
          Str := Str + '-PING '
        else
          Str := Str + '+PING ';
        if userHideLeech in User^.State then
          Str := Str + '-LEECH '
        else
          Str := Str + '+LEECH ';
        if userHideSameNic in User^.State then
          Str := Str + '-SAMENIC '
        else
          Str := Str + '+SAMENIC ';
      end;
      Error(Str);
    end
    else
    begin
      if not (userHideErrors in User^.State) then
        Str := 'ERROR ';
      if not (userHideAnnouncements in User^.State) then
        Str := Str + 'ANNOUNCE ';
      if User^.Level > napUserUser then
      begin
        // if not (userHideBans in User^.State) then Str := Str + 'BAN ';
        if not (userHideMBans in User^.State) then
          Str := Str + 'MBAN ';
        if not (userHideMBanConn in User^.State) then
          Str := Str + 'MBANCONN ';
        if not (userHideSBans in User^.State) then
          Str := Str + 'SBAN ';
        if not (userHideSBanConn in User^.State) then
          Str := Str + 'SBANCONN ';
        if not (userHideChange in User^.State) then
          Str := Str + 'CHANGE ';
        if not (userHideKill in User^.State) then
          Str := Str + 'KILL ';
        if not (userHideLevel in User^.State) then
          Str := Str + 'LEVEL ';
        if not (userHideServer in User^.State) then
          Str := Str + 'SERVER ';
        if not (userHideMuzzle in User^.State) then
          Str := Str + 'MUZZLE ';
        if not (userHidePort in User^.State) then
          Str := Str + 'PORT ';
        if not (userHideWallop in User^.State) then
          Str := Str + 'WALLOP ';
        if not (userHideCloak in User^.State) then
          Str := Str + 'CLOAK ';
        if not (userHideFlood in User^.State) then
          Str := Str + 'FLOOD ';
        if not (userHidePM in User^.State) then
          Str := Str + 'MSG ';
        if not (userHideWhois in User^.State) then
          Str := Str + 'WHOIS ';
        if not (userHideBrowse in User^.State) then
          Str := Str + 'BROWSE ';
        if not (userHideMotd in User^.State) then
          Str := Str + 'MOTD ';
        if not (userHideFriends in User^.State) then
          Str := Str + 'FRIEND ';
        if not (userHideChannel in User^.State) then
          Str := Str + 'CHANNEL ';
        if not (userHideRegister in User^.State) then
          Str := Str + 'REGISTER ';
        if not (userHideVar in User^.State) then
          Str := Str + 'VAR ';
        if not (userHidePing in User^.State) then
          Str := Str + 'PING ';
        if not (userHideLeech in User^.State) then
          Str := Str + 'LEECH ';
        if not (userHideSameNic in User^.State) then
          Str := Str + 'SAMENIC ';
      end;
      Exec(User, GCmd.Id, Trim(Str));
    end;
    Exit;
  end;
  St := User^.State;
  if UpperCase(GCmd.Cmd) = 'HELP' then
  begin
    Error(RS_Usermode_Title);
    Error(RS_Usermode_Error);
    Error(RS_Usermode_Announce);
    if User^.Level < napUserModerator then
      Error(RS_Usermode_Other)
    else
    begin
      Error(RS_Usermode_MBan);
      Error(RS_Usermode_MBanConn);
      Error(RS_Usermode_SBan);
      Error(RS_Usermode_SBanConn);
      Error(RS_Usermode_Change);
      Error(RS_Usermode_Kill);
      Error(RS_Usermode_Level);
      Error(RS_Usermode_Server);
      Error(RS_Usermode_Muzzle);
      Error(RS_Usermode_Port);
      Error(RS_Usermode_Wallop);
      Error(RS_Usermode_Cloak);
      Error(RS_Usermode_Flood);
      Error(RS_Usermode_Msg);
      Error(RS_Usermode_Whois);
      Error(RS_Usermode_Browse);
      Error(RS_Usermode_Motd);
      Error(RS_Usermode_Friend);
      Error(RS_Usermode_Channel);
      Error(RS_Usermode_Register);
      Error(RS_Usermode_var);
      Error(RS_Usermode_Ping);
      Error(RS_Usermode_Leech);
      Error(RS_Usermode_Samenic);
    end;
    Error('.');
  end;
  while HList.Count > 0 do
  begin
    Str := HList.Strings[0];
    HList.Delete(0);
    // if Str = 'NONE' then St := St + [userHideErrors, userHideAnnouncements, userHideBans,
    if Str = 'NONE' then
      St := St + [userHideErrors, userHideAnnouncements, userHideMBans,
        userHideMBanConn, userHideSBans, userHideSBanConn,
        userHideChange, userHideKill, userHideLevel, userHideServer,
          userHideMuzzle,
        userHidePort, userHideWallop, userHideCloak, userHideFlood, userHidePM,
        userHideWhois, userHideBrowse, userHideMotd, userHideFriends,
          userHideChannel,
        userHideRegister, userHideVar, userHidePing, userHideLeech,
          userHideSameNic]
        // else if Str = 'ALL' then St := St - [userHideErrors, userHideAnnouncements, userHideBans,
    else if Str = 'ALL' then
      St := St - [userHideErrors, userHideAnnouncements, userHideMBans,
        userHideMBanConn, userHideSBans, userHideSBanConn,
        userHideChange, userHideKill, userHideLevel, userHideServer,
          userHideMuzzle,
        userHidePort, userHideWallop, userHideCloak, userHideFlood, userHidePM,
        userHideWhois, userHideBrowse, userHideMotd, userHideFriends,
          userHideChannel,
        userHideRegister, userHideVar, userHidePing, userHideLeech,
          userHideSameNic]
    else if Str = '-ERROR' then
      St := St + [userHideErrors]
    else if Str = '+ERROR' then
      St := St - [userHideErrors]
    else if Str = '-ANNOUNCE' then
      St := St + [userHideAnnouncements]
    else if Str = '+ANNOUNCE' then
      St := St - [userHideAnnouncements]
    else if User^.Level > napUserUser then
    begin
      // if Str = '-BAN' then St := St + [userHideBans]
      // else if Str = '+BAN' then St := St - [userHideBans]
      if Str = '-MBAN' then
        St := St + [userHideMBans]
      else if Str = '+MBAN' then
        St := St - [userHideMBans]
      else if Str = '-MBANCONN' then
        St := St + [userHideMBanConn]
      else if Str = '+MBANCONN' then
        St := St - [userHideMBanConn]
      else if Str = '-SBAN' then
        St := St + [userHideSBans]
      else if Str = '+SBAN' then
        St := St - [userHideSBans]
      else if Str = '-SBANCONN' then
        St := St + [userHideSBanConn]
      else if Str = '+SBANCONN' then
        St := St - [userHideSBanConn]
      else if Str = '-CHANGE' then
        St := St + [userHideChange]
      else if Str = '+CHANGE' then
        St := St - [userHideChange]
      else if Str = '-KILL' then
        St := St + [userHideKill]
      else if Str = '+KILL' then
        St := St - [userHideKill]
      else if Str = '-LEVEL' then
        St := St + [userHideLevel]
      else if Str = '+LEVEL' then
        St := St - [userHideLevel]
      else if Str = '-SERVER' then
        St := St + [userHideServer]
      else if Str = '+SERVER' then
        St := St - [userHideServer]
      else if Str = '-MUZZLE' then
        St := St + [userHideMuzzle]
      else if Str = '+MUZZLE' then
        St := St - [userHideMuzzle]
      else if Str = '-PORT' then
        St := St + [userHidePort]
      else if Str = '+PORT' then
        St := St - [userHidePort]
      else if Str = '-WALLOP' then
        St := St + [userHideWallop]
      else if Str = '+WALLOP' then
        St := St - [userHideWallop]
      else if Str = '-CLOAK' then
        St := St + [userHideCloak]
      else if Str = '+CLOAK' then
        St := St - [userHideCloak]
      else if Str = '-FLOOD' then
        St := St + [userHideFlood]
      else if Str = '+FLOOD' then
        St := St - [userHideFlood]
      else if Str = '-MSG' then
        St := St + [userHidePM]
      else if Str = '+MSG' then
        St := St - [userHidePM]
      else if Str = '-WHOIS' then
        St := St + [userHideWhois]
      else if Str = '+WHOIS' then
        St := St - [userHideWhois]
      else if Str = '-BROWSE' then
        St := St + [userHideBrowse]
      else if Str = '+BROWSE' then
        St := St - [userHideBrowse]
      else if Str = '-MOTD' then
        St := St + [userHideMotd]
      else if Str = '+MOTD' then
        St := St - [userHideMotd]
      else if Str = '-FRIEND' then
        St := St + [userHideFriends]
      else if Str = '+FRIEND' then
        St := St - [userHideFriends]
      else if Str = '-CHANNEL' then
        St := St + [userHideChannel]
      else if Str = '+CHANNEL' then
        St := St - [userHideChannel]
      else if Str = '-REGISTER' then
        St := St + [userHideRegister]
      else if Str = '+REGISTER' then
        St := St - [userHideRegister]
      else if Str = '-VAR' then
        St := St + [userHideVar]
      else if Str = '+VAR' then
        St := St - [userHideVar]
      else if Str = '-PING' then
        St := St + [userHidePing]
      else if Str = '+PING' then
        St := St - [userHidePing]
      else if Str = '-LEECH' then
        St := St + [userHideLeech]
      else if Str = '+LEECH' then
        St := St - [userHideLeech]
      else if Str = '-SAMENIC' then
        St := St + [userHideSameNic]
      else if Str = '+SAMENIC' then
        St := St - [userHideSameNic];

    end;
  end;
  if User^.State = St then Exit;
  User^.State := St;
  if User^.Server = nil then
    WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
end;

procedure Handler_Wallop; // 627
begin
  if not IsLogged then Exit;
  if not CheckLevel('', napUserModerator) then Exit;
  if GCmd.Cmd = '' then Exit;
  Tmp_Pos := 665;
  Wallop(MSG_SERVER_WALLOP, wallopWallop, User^.UserName + ' ' + GCmd.Cmd,
    True);
  if User^.Server = nil then
    WriteAllServers(MSG_CLIENT_WALLOP, User^.UserName, GCmd.Cmd);
end;

procedure Handler_Announce; // 627
var
  I: Integer;
  L: TLocalUser;
begin
  if not IsLogged then Exit;
  if not CheckLevel('', napUserAdmin) then Exit;
  if GCmd.Cmd = '' then Exit;
  Tmp_Pos := 666;
  for I := 0 to DB_Local.Count - 1 do
  begin
    L := DB_Local.Items[I];
    if L.Logged then
      if not (userHideWallop in L.Data^.State) then
        L.Exec(MSG_SERVER_ANNOUNCE, User^.UserName + ' ' + GCmd.Cmd);
  end;
  if User^.Server = nil then
    WriteAllServers(MSG_CLIENT_ANNOUNCE, User^.UserName, GCmd.Cmd);
end;

procedure Handler_DirectBrowse;
var
  Str: string;
  User2: POnlineUser;
  L2: TLocalUser;
begin
  Tmp_Pos := 670;
  if not IsLogged then Exit;
  if not CheckParams(1) then Exit;
  if IsLocal and (GetTickCount - Local.Last_Seen < 30000) then
    Local.Detector := Local.Detector + [loc640];
  if User^.Level = napUserLeech then
  begin
    Exec(User, MSG_SERVER_BROWSE_DIRECT_ERR, HList.Strings[0] + ' "' +
      GetLangT(LNG_BROWSELEECH) + '"');
    Exit;
  end;
  // Block browse By ppl not meeting Min_Share
  if not MinShare_NoBlockAct then
    if User^.Level = napUserUser then
    begin
      if not StrHash_FindString(DB_Friends, User^.UserName, True) then
      begin
        if Local <> nil then
        begin
          if Local.Shared_Size < MinShare_Size then
          begin
            // Exec(User, MSG_SERVER_NOSUCH, GetLangT(LNG_SHAREMEGSFIRST,
            //   IntToStr(MinShare_Size div MegaByte), Server_Alias));
            Exec(User, MSG_SERVER_BROWSE_DIRECT_ERR, HList.Strings[0] + ' "' +
              GetLangT(LNG_BROWSELEECH) + '"');
            Exit;
          end;
          if User^.Shared < MinShare then
          begin
            // Exec(User, MSG_SERVER_NOSUCH, GetLangT(LNG_SHAREFILESFIRST,
            //   IntToStr(MinShare), Server_Alias));
            Exec(User, MSG_SERVER_BROWSE_DIRECT_ERR, HList.Strings[0] + ' "' +
              GetLangT(LNG_BROWSELEECH) + '"');
            Exit;
          end;
        end;
      end;
    end;
  User2 := DB_Online.FindUser(HList.Strings[0]);
  if User2 = nil then
  begin
    Exec(User, MSG_SERVER_BROWSE_DIRECT_ERR, HList.Strings[0] + ' "' +
      GetLangT(LNG_OFFLINE2, HList.Strings[0]) + '"');
    Exit;
  end;
  Tmp_Pos := 671;
  if User2^.Server <> nil then
  begin
    if Local = nil then Exit;
    User2^.Server.Exec(GCmd.Id, User^.UserName + ' ' + GCmd.Cmd);
    Exit;
  end;
  Tmp_Pos := 672;
  L2 := User2^.Local;
  if L2 <> nil then
    if User^.Level < napUserModerator then
      if StrHash_FindString(L2.Ignored, AnsiLowerCase(User^.UserName), False)
        then
      begin
        Exec(User, MSG_SERVER_BROWSE_DIRECT_ERR, HList.Strings[0] + ' "' +
          GetLangT(LNG_OFFLINE2, HList.Strings[0]) + '"');
        Exit;
      end;
  Tmp_Pos := 673;
  if (User^.DataPort = 0) and (User2^.DataPort = 0) then
  begin
    Exec(User, MSG_SERVER_BROWSE_DIRECT_ERR, HList.Strings[0] + ' "' +
      GetLangT(LNG_BROWSEFIREWALL, User2^.UserName) + '"');
    Exit;
  end;
  Str := User^.UserName;
  if User2^.DataPort = 0 then
    Str := Str + ' ' + IntToStr(User^.Ip) + ' ' + IntToStr(User^.DataPort);
  Exec(User2, GCmd.Id, Str);
end;

procedure Handler_DirectBrowseOK;
var
  Str: string;
  User2: POnlineUser;
  L2: TLocalUser;
begin
  Tmp_Pos := 674;
  if not IsLogged then Exit;
  if not CheckParams(1) then Exit;
  User2 := DB_Online.FindUser(HList.Strings[0]);
  if User2 = nil then
  begin
    Exec(User, MSG_SERVER_BROWSE_DIRECT_ERR, HList.Strings[0] + ' "' +
      GetLangT(LNG_OFFLINE2, HList.Strings[0]) + '"');
    Exit;
  end;
  Tmp_Pos := 675;
  if User2^.Server <> nil then
  begin
    if Local = nil then Exit;
    User2^.Server.Exec(GCmd.Id, User^.UserName + ' ' + GCmd.Cmd);
    Exit;
  end;
  Tmp_Pos := 676;
  L2 := User2^.Local;
  if L2 <> nil then
    if User^.Level < napUserModerator then
      if StrHash_FindString(L2.Ignored, AnsiLowerCase(User^.UserName), False)
        then
      begin
        Exec(User, MSG_SERVER_BROWSE_DIRECT_ERR, HList.Strings[0] + ' "' +
          GetLangT(LNG_OFFLINE2, HList.Strings[0]) + '"');
        Exit;
      end;
  Tmp_Pos := 677;
  if (User^.DataPort = 0) and (User2^.DataPort = 0) then
  begin
    Exec(User, MSG_SERVER_BROWSE_DIRECT_ERR, HList.Strings[0] + ' "' +
      GetLangT(LNG_BROWSEFIREWALL) + '"');
    Exit;
  end;
  Str := User^.UserName + ' ' + IntToStr(User^.Ip) + ' ' +
    IntToStr(User^.DataPort);
  Exec(User2, GCmd.Id, Str);
end;

procedure Handler_Cloak; // 652
var
  I, J: Integer;
  B: Boolean;
  Ch: TChannel;
  User2: POnlineUser;
  Loc: TLocalUser;
  Str: string;
begin
  Tmp_Pos := 680;
  if not IsLogged then Exit;
  if not CheckLevel('[' + ServerAlias + '] ' + RS_Handler_CloakFailed,
    napUserModerator) then Exit;
  B := UserCloaked in User.State;
  if GCmd.Cmd = '1' then
  begin
    if B then
    begin
      Error(GetLangT(LNG_ERRCLOAKED), True);
      Exit;
    end;
    B := True;
  end
  else if GCmd.Cmd = '0' then
  begin
    if not B then
    begin
      Error(GetLangT(LNG_ERRUNCLOAK), True);
      Exit;
    end;
    B := False;
  end
  else
    B := not B;
  Tmp_Pos := 681;
  if B then
  begin
    User^.State := User^.State + [userCloaked];
    Wallop(MSG_SERVER_NOSUCH, wallopCloak, GetLangT(LNG_CLOAKED,
      User^.UserName), True);
    if User^.Server = nil then
    begin
      Local.Exec(MSG_SERVER_NOSUCH, RS_Handler_Cloak);
      WriteAllServers(MSG_CLIENT_CLOAK, User^.UserName, '1');
    end;
  end
  else
  begin
    User^.State := User^.State - [userCloaked];
    Wallop(MSG_SERVER_NOSUCH, wallopCloak, GetLangT(LNG_UNCLOAKED,
      User^.UserName), True);
    if User^.Server = nil then
    begin
      Local.Exec(MSG_SERVER_NOSUCH, RS_Handler_DeCloak);
      WriteAllServers(MSG_CLIENT_CLOAK, User^.UserName, '0');
    end;
  end;
  Tmp_Pos := 682;
  for I := 0 to DB_Channels.Count - 1 do
  begin
    Ch := DB_Channels.Items[I];
    J := Ch.FindUser(User);
    if J <> -1 then
      for J := 0 to Ch.Users.Count - 1 do
      try
        User2 := Ch.Users.Items[J];
        if User2 <> User then
          if User2^.Server = nil then
            if User2^.Level < napUserModerator then
            begin
              if not B then
                Exec(User2, MSG_SERVER_JOIN, Ch.Channel + ' ' + User^.UserName +
                  ' ' + IntToStr(User^.Shared) + ' ' + IntToStr(Ord(User^.Speed)))
              else
                Exec(User2, MSG_SERVER_PART, Ch.Channel + ' ' + User^.UserName +
                  ' ' + IntToStr(User^.Shared) + ' ' +
                  IntToStr(Ord(User^.Speed)));
            end;
      except
        on E: Exception do
          DebugLog('Handler_Cloak : Exception ' + E.Message);
      end;
  end;
  for I := 0 to DB_Local.Count - 1 do
  begin
    Loc := DB_Local.Items[I];
    if Loc.Logged then
      if Loc <> Local then
        if Loc.Level < napUserModerator then
        begin
          Str := StrHash_FindStringEx(Loc.Hotlist, User^.UserName, True);
          if Str <> '' then
          begin
            if not B then
              Loc.Exec(MSG_SERVER_USER_SIGNON, Str + ' ' +
                IntToStr(Ord(User^.Speed)))
            else
              Loc.Exec(MSG_SERVER_USER_SIGNOFF, Str);
          end;
        end;
  end;
end;

procedure Handler_SetSpeed; // 700
var
  I: Integer;
begin
  Tmp_Pos := 690;
  if not IsLogged then Exit;
  I := StrToIntdef(GCmd.Cmd, -1);
  if (I < 0) or (I > 10) then
  begin
    Error(RS_Handler_InvalidSpeed);
    Exit;
  end;
  if (Ord(User^.Speed) = I) and (Query = queryNormal) and (Local <> nil) then
    if not CheckWinMX(Local) then Exit;
  Tmp_Pos := 691;
  User^.Speed := TNapSpeed(I);
  if User^.Server = nil then
  begin
    if Local = nil then
      WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd)
    else
      Local.LocalState := Local.LocalState + [locNeedsUpdate];
  end;
end;

procedure Handler_SetPass; // 701
var
  Str: string;
  Reg: PRegisteredUser;
begin
  if not IsLogged then Exit;
  if not CheckParams(1) then Exit;
  Str := Encode(HList.Strings[0]);
  if User^.Password = Str then Exit;
  User^.Password := Str;
  Reg := DB_Registered.FindUser(User^.UserName);
  if Reg <> nil then
    Reg^.Password := Str;
  if User^.Server = nil then
  begin
    Error(RS_Handler_SetPass, True);
    WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
  end;
end;

procedure Handler_SetDataPort; // 703
var
  I: Integer;
begin
  if not IsLogged then Exit;
  if not CheckRangeEx(GCmd.Cmd, 0, 65535) then Exit;
  I := StrToIntDef(GCmd.Cmd, -1);
  if (I < 0) or (I > 65535) then
  begin
    Error(RS_Handler_InvalidPort2, True);
    Exit;
  end;
  if I = User^.DataPort then Exit;
  User^.DataPort := I;
  if User^.Server = nil then
    WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
end;

procedure Handler_AlterPass; // 753
var
  User2: POnlineUser;
  Reg: PRegisteredUser;
  Str: string;
  L: TNapUserLevel;
  I: Integer;
  Srv: TServer;
begin
  if not IsLogged then Exit;
  if not CheckLevel('', napUserAdmin) then Exit;
  if not CheckParams(2) then Exit;
  Str := AnsiLowerCase(HList.Strings[0]);
  if Num_Servers > 0 then
    for I := 0 to DB_Servers.Count - 1 do
    begin
      Srv := DB_Servers.Items[I];
      if Srv.Logged then
        if AnsiLowerCase(Srv.Reg_User) = Str then
        begin
          PermissionDenied('', True);
          Exit;
        end;
    end;
  User2 := DB_Online.FindUser(HList.Strings[0]);
  Reg := DB_Registered.FindUser(HList.Strings[0]);
  if (User2 = nil) and (Reg = nil) then
  begin
    UserIsOffline(HList.Strings[0], True);
    Exit;
  end;
  if User2 <> nil then
    L := User2^.Level
  else
    L := Reg^.Level;
  if L >= User^.Level then
  begin
    Error(RS_Handler_PassChangeFailed, True);
    Exit;
  end;
  Str := Encode(HList.Strings[1]);
  if User2 <> nil then
    User2^.Password := Str;
  if Reg <> nil then
    Reg^.Password := Str;
  Wallop(MSG_SERVER_NOSUCH, wallopChange, GetLangT(LNG_CHPASS2, User^.UserName,
    HList.Strings[0]), True);
  if User^.Server = nil then
    WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
end;

procedure Handler_Restart;
begin
  if not IsLogged then Exit;
  if not CheckLevel('', napUserElite) then Exit;
  Restart_User := User^.UserName;
  Sync_Reply_List.AddDoubleCmd(MSG_SR_RESTART, 0, '', '');
end;

procedure Handler_ServerVersion;
var
  Srv: TServer;
  I: Integer;
  Display_Name: string;
  Moderator: Boolean;
begin
  Moderator := User^.Level > napUserUser;
  if GCmd.Cmd <> '' then
  begin
    GCmd.Cmd := LowerCase(GCmd.Cmd);
    for I := 0 to DB_Servers.Count - 1 do
    begin
      Srv := DB_Servers.Items[I];
      if Srv.Logged then
        if (Srv.Host = GCmd.Cmd) or (Srv.Alias = GCmd.Cmd) then
        begin
          Error('--');
          if not Moderator then
            Display_Name := GetServerAlias(Srv)
          else
            Display_Name := Srv.Host;
          Error('Server ' + Display_Name);
          Error(Srv.Version);
          Error('--');
          Exit;
        end;
    end;
  end;
  Error('--');
  Error(SLAVANAP_FULL);
  Error('--');
end;

procedure Handler_ClearChannel;
var
  Ch: TChannel;
  I: Integer;
begin
  Tmp_Pos := 700;
  if not IsLogged then Exit;
  if not CheckLevel('', napUserModerator) then Exit;
  if not CheckParams(1) then Exit;
  Ch := FindChannel(HList.Strings[0]);
  Tmp_Pos := 701;
  if Ch = nil then
  begin
    Error(GetLangT(LNG_NOCHANNEL), True);
    Exit;
  end;
  if Ch.Level >= User^.Level then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  Tmp_Pos := 702;
  while Ch.Users.Count > 0 do
    Ch.Part(Ch.Users.Items[0]);
  Wallop(MSG_SERVER_NOSUCH, wallopChannel, GetLangT(LNG_CLEARCHANNEL,
    User^.UserName, Ch.Channel, NextParamEx(GCmd.Cmd)), True);
  Tmp_Pos := 703;
  if not (chRegistered in Ch.State) then
    for I := DB_Channels.Count - 1 downto 0 do
      if DB_Channels.Items[I] = Ch then
      begin
        DB_Channels.Delete(I);
        Ch.Free;
      end;
  if User^.Server = nil then
    WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
end;

procedure Handler_DropChannel;
var
  Ch: TChannel;
  I: Integer;
  Motd_FileName: string;
begin
  Tmp_Pos := 710;
  if not IsLogged then Exit;
  if not CheckLevel('', napUserModerator) then Exit;
  if not CheckParams(1) theN
    Exit;
  Ch := FindChannel(HList.Strings[0]);
  if Ch = nil then
  begin
    Error(GetLangT(LNG_NOCHANNEL), True);
    Exit;
  end;
  Tmp_Pos := 711;
  if Ch.Level >= User^.Level then
    if Ch.Level <> napUserConsole then
    begin
      PermissionDenied('', True);
      Exit;
    end;
  while Ch.Users.Count > 0 do
    Ch.Part(Ch.Users.Items[0]);
  Wallop(MSG_SERVER_NOSUCH, wallopChannel, GetLangT(LNG_DROPCHANNEL,
    User^.UserName, Ch.Channel, NextParamEx(GCmd.Cmd)), True);
  Motd_FileName := (ApplicationDir + 'chmotd.' + Ch.Channel);
  if FileExists(Motd_FileName) then
    DeleteFile(PChar(Motd_FileName));
  Tmp_Pos := 712;
  for I := DB_Channels.Count - 1 downto 0 do
    if DB_Channels.Items[I] = Ch then
    begin
      DB_Channels.Delete(I);
      Ch.Free;
    end;
  Tmp_Pos := 713;
  if User^.Server = nil then
    WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
end;

procedure Handler_Redirect;
var
  User2: POnlineUser;
begin
  Tmp_Pos := 720;
  if not IsLogged then Exit;
  if not CheckLevel('', napUserElite) then Exit;
  case GCmd.Id of
    MSG_CLIENT_REDIRECT: if not CheckParams(3) then Exit;
  else if not CheckParams(2) then Exit;
  end;
  Tmp_Pos := 721;
  User2 := DB_Online.FindUser(HList.Strings[0]);
  if User2 = nil then
  begin
    UserIsOffline(HList.Strings[0], True);
    Exit;
  end;
  Tmp_Pos := 722;
  if User2^.Level >= User^.Level then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  Exec(User2, GCmd.Id, NextParamEx(GCmd.Cmd));
end;

procedure Handler_ChannelLevel;
var
  Ch: TChannel;
  L: TNapUserLevel;
  I: Integer;
  User2: POnlineUser;
  B: Boolean;
begin
  Tmp_Pos := 730;
  if not IsLogged then Exit;
  if not CheckLevel('', napUserModerator) then Exit;
  if not CheckParams(2) then Exit;
  Ch := FindChannel(HList.Strings[0]);
  if Ch = nil then
  begin
    Error(GetLangT(LNG_NOCHANNEL), True);
    Exit;
  end;
  Tmp_Pos := 731;
  if Ch.Level > User^.Level then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  if not IsLevel(HList.Strings[1]) then
  begin
    Error(GetLangT(LNG_INVALIDLEVEL, HList.Strings[1]), True);
    Exit;
  end;
  L := Str2Level(HList.Strings[1]);
  if Ch.Level = L then Exit;
  if L > User^.Level then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  Tmp_Pos := 732;
  Ch.Level := L;
  if User^.Server = nil then
    WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
  B := userCloaked in User^.State;
  Tmp_Pos := 733;
  for I := 0 to Ch.Users.Count - 1 do
  begin
    User2 := Ch.Users.Items[I];
    if User2^.Server = nil then
    begin
      if (User2^.Level > napUserUser) or (B = False) then
        Exec(User2, MSG_SERVER_PUBLIC, Ch.Channel + ' Server ' +
          GetLangT(LNG_CHLEVEL, User^.UserName, Ch.Channel, Level2Str(Ch.Level)))
      else
        Exec(User2, MSG_SERVER_PUBLIC, Ch.Channel + ' Server ' +
          GetLangT(LNG_CHLEVEL, 'Operator', Ch.Channel, Level2Str(Ch.Level)))
    end;
  end;
end;

procedure Handler_ChannelLimit;
var
  Ch: TChannel;
  I: Integer;
  User2: POnlineUser;
  B: Boolean;
begin
  Tmp_Pos := 734;
  if not IsLogged then Exit;
  if not CheckLevel('', napUserModerator) then Exit;
  if not CheckParams(2) then Exit;
  Ch := FindChannel(HList.Strings[0]);
  if Ch = nil then
  begin
    Error(GetLangT(LNG_NOCHANNEL), True);
    Exit;
  end;
  Tmp_Pos := 735;
  if Ch.Level > User^.Level then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  if not IsDigit(HList.Strings[1]) then
  begin
    Error(GetLangT(LNG_INVALIDARGS), True);
    Exit;
  end;
  Tmp_Pos := 736;
  I := StrToIntDef(HList.Strings[1], -1);
  if (I < 2) or (I > 65535) then
  begin
    Error(GetLangT(LNG_INVALIDARGS), True);
    Exit;
  end;
  if Ch.Limit = I then Exit;
  Ch.Limit := I;
  Tmp_Pos := 737;
  if User^.Server = nil then
    WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
  B := userCloaked in User^.State;
  Tmp_Pos := 738;
  for I := 0 to Ch.Users.Count - 1 do
  begin
    User2 := Ch.Users.Items[I];
    if User2^.Server = nil then
    begin
      if (User2^.Level > napUserUser) or (B = False) then
        Exec(User2, MSG_SERVER_PUBLIC, Ch.Channel + ' Server ' +
          GetLangT(LNG_LIMIT, User^.UserName, Ch.Channel, IntToStr(Ch.Limit)))
      else
        Exec(User2, MSG_SERVER_PUBLIC, Ch.Channel + ' Server ' +
          GetLangT(LNG_CHLEVEL, 'Operator', Ch.Channel, IntToStr(Ch.Limit)))
    end;
  end;
end;

procedure Handler_Nuke; // 611
var
  User2: POnlineUser;
  User3: TLocalUser;
  B: Boolean;
  Preg: PRegisteredUser;
  L: TNapUserLevel;
  I: Integer;
  Srv: TServer;
  Str: string;
begin
  Tmp_Pos := 740;
  if not IsLogged then Exit;
  if not CheckLevel('[' + ServerAlias + '] ' + RS_Handler_NukeFailed,
    napUserModerator) then Exit;
  if not CheckParams(1) then Exit;
  B := False;
  User2 := DB_Online.FindUser(HList.Strings[0]);
  if User2 = User then
    User2 := nil;
  Tmp_Pos := 741;
  Str := AnsiLowerCase(HList.Strings[0]);
  if User2 <> nil then
  begin
    if User2^.Level >= User^.Level then
    begin
      PermissionDenied('[' + ServerAlias + '] ' + RS_Handler_NukeFailed, True);
      Exit;
    end;
    if User^.Server = nil then
      if User2^.Level > napUserModerator then
        for I := 0 to DB_Servers.Count - 1 do
        begin
          Srv := DB_Servers.Items[I];
          if Srv.Logged then
            if AnsiLowerCase(Srv.Reg_User) = Str then
            begin
              Error(GetLangT(LNG_ACCESSREG, Level2Str(User2^.Level),
                User2^.UserName, Srv.Host, Srv.Console), True);
              Exit;
            end;
        end;
    Tmp_Pos := 742;
    if User^.Server = nil then
      WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
    User3 := User2^.Local;
    if User3 <> nil then
      if User3 <> Cons then
      begin
        User3.Exec(MSG_SERVER_NOSUCH, GetLangT(LNG_NUKEMSG));
        DisconnectUser(User3, '', GetLangT(LNG_NUKED, User2^.UserName,
          User2^.Software, User^.UserName), 'Handler_Nuke', False);
      end;
    Tmp_Pos := 743;
    B := True;
  end;
  Tmp_Pos := 744;
  Preg := DB_Registered.FindUser(HList.Strings[0]);
  if Preg <> nil then
  begin
    L := Preg^.Level;
    if L >= User^.Level then
    begin
      PermissionDenied('', True);
      Exit;
    end;
    if User^.Server = nil then
      if L > napUserModerator then
        for I := 0 to DB_Servers.Count - 1 do
        begin
          Srv := DB_Servers.Items[I];
          if Srv.Logged then
            if Srv.Reg_User = Str then
            begin
              Error(GetLangT(LNG_ACCESSREG, Level2Str(L), Preg^.Nick, Srv.Host,
                Srv.Console), True);
              Exit;
            end;
        end;
    Tmp_Pos := 745;
    if not B then
      if User^.Server = nil then
        WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
    DB_Registered.Delete(HList.Strings[0]);
  end
  else if not B then
  begin
    Tmp_Pos := 746;
    if User^.Server <> nil then
      NoSuchUser(True);
    Exit;
  end;
  Tmp_Pos := 747;
  Wallop(MSG_SERVER_NOSUCH, wallopKill, GetLangT(LNG_NUKE, User^.UserName,
    HList.Strings[0], NextParamEx(GCmd.Cmd)), True);
end;

procedure Handler_UnRegister; // 8124
var
  User2: POnlineUser;
  B: Boolean;
  Preg: PRegisteredUser;
  L: TNapUserLevel;
  I: Integer;
  Srv: TServer;
  Str: string;
begin
  Tmp_Pos := 7658;
  if not IsLogged then Exit;
  if not CheckLevel('[' + ServerAlias + '] ' + RS_Handler_UnregisterFailed,
    napUserModerator) then Exit;
  if not CheckParams(1) then Exit;
  B := False;
  User2 := DB_Online.FindUser(HList.Strings[0]);
  if User2 = User then
    User2 := nil;
  Tmp_Pos := 7659;
  Str := AnsiLowerCase(HList.Strings[0]);
  if User2 <> nil then
  begin
    if User2^.Level >= User^.Level then
    begin
      PermissionDenied('[' + ServerAlias + '] ' + RS_Handler_UnregisterFailed,
        True);
      Exit;
    end;
    if User^.Server = nil then
      if User2^.Level > napUserModerator then
        for I := 0 to DB_Servers.Count - 1 do
        begin
          Srv := DB_Servers.Items[I];
          if Srv.Logged then
            if AnsiLowerCase(Srv.Reg_User) = Str then
            begin
              Error(GetLangT(LNG_ACCESSREG, Level2Str(User2^.Level),
                User2^.UserName, Srv.Host, Srv.Console), True);
              Exit;
            end;
        end;
    Tmp_Pos := 7660;
    if User^.Server = nil then
      WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
    Tmp_Pos := 7661;
    B := True;
  end;
  Tmp_Pos := 7662;
  Preg := DB_Registered.FindUser(HList.Strings[0]);
  if Preg <> nil then
  begin
    L := Preg^.Level;
    if L >= User^.Level then
    begin
      PermissionDenied('', True);
      Exit;
    end;
    if User^.Server = nil then
      if L > napUserModerator then
        for I := 0 to DB_Servers.Count - 1 do
        begin
          Srv := DB_Servers.Items[I];
          if Srv.Logged then
            if Srv.Reg_User = Str then
            begin
              Error(GetLangT(LNG_ACCESSREG, Level2Str(L), Preg^.Nick, Srv.Host,
                Srv.Console), True);
              Exit;
            end;
        end;
    Tmp_Pos := 7663;
    if not B then
      if User^.Server = nil then
        WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
    DB_Registered.Delete(HList.Strings[0]);
  end
  else if not B then
  begin
    Tmp_Pos := 7664;
    if User^.Server <> nil then
      NoSuchUser(True);
    Exit;
  end;
  Tmp_Pos := 7665;
  if User2 <> nil then
    if User2^.Level <> napUserUser then
      User2^.Level := napUserUser;
  Wallop(MSG_SERVER_NOSUCH, wallopLevel, GetLangT(LNG_UNREGISTER,
    User^.UserName, HList.Strings[0], NextParamEx(GCmd.Cmd)), True);
end;

procedure Handler_SetUserLevel;
var
  L, L1: TNapUserLevel;
  B: Boolean;
  User2, User3: POnlineUser;
  Preg: PRegisteredUser;
  Ch: TChannel;
  Srv: TServer;
  I, J, K: Integer;
  Str: string;
begin
  Tmp_Pos := 750;
  if not IsLogged then Exit;
  if not CheckLevel('', napUserModerator) then Exit;
  if not CheckParams(2) then Exit;
  if not IsLevel(HList.Strings[1]) then
  begin
    Error(GetLangT(LNG_INVALIDLEVEL, HList.Strings[1]), True);
    Exit;
  end;
  Tmp_Pos := 751;
  L := Str2Level(HList.Strings[1]);
  if L >= User^.Level then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  B := False;
  Str := AnsiLowerCase(HList.Strings[0]);
  User2 := DB_Online.FindUser(HList.Strings[0]);
  Tmp_Pos := 752;
  if User2 <> nil then
  begin
    B := True;
    if User2^.Level >= User^.Level then
    begin
      PermissionDenied('', True);
      Exit;
    end;
    if User^.Server = nil then
      if User2^.Level > napUserModerator then
        if L <> napUserElite then
          for I := 0 to DB_Servers.Count - 1 do
          begin
            Srv := DB_Servers.Items[I];
            if Srv.Logged then
              if AnsiLowerCase(Srv.Reg_User) = Str then
              begin
                Error(GetLangT(LNG_ACCESSREG, Level2Str(User2^.Level),
                  User2^.UserName, Srv.Host, Srv.Console), True);
                Exit;
              end;
          end;
    Tmp_Pos := 753;
    L1 := User2^.Level;
    J := 0;
    if L > napUserUser then
      J := 1;
    if L1 > napUserUser then
      Inc(J, 2);
    Tmp_Pos := 754;
    if (L < napUserModerator) or (L1 < napUserModerator) then
      if userCloaked in User2^.State then
        if (J = 1) or (J = 2) then
          for I := 0 to DB_Channels.Count - 1 do
          begin
            Ch := DB_Channels.Items[I];
            Tmp_Pos := 755;
            if Ch.FindUser(User2) <> -1 then
            begin
              case J of
                1: // Hide user
                  for K := 0 to Ch.Users.Count - 1 do
                  begin
                    User3 := Ch.Users.Items[K];
                    if not Ch.Operator(User3) then
                      Exec(User3, MSG_SERVER_PART, Ch.Channel + ' ' +
                        User2^.UserName +
                        ' ' + IntToStr(User2^.Shared) + ' ' +
                          IntToStr(Ord(User2^.Speed)));
                  end;
                2: // Show user
                  for K := 0 to Ch.Users.Count - 1 do
                  begin
                    User3 := Ch.Users.Items[K];
                    if not Ch.Operator(User3) then
                      Exec(User3, MSG_SERVER_JOIN, Ch.Channel + ' ' +
                        User2^.UserName +
                        ' ' + IntToStr(User2^.Shared) + ' ' +
                          IntToStr(Ord(User2^.Speed)));
                  end;
              end;
            end;
          end;
    Tmp_Pos := 756;
    User2^.Level := L;
    if (L < napUserModerator) and (userCloaked in User2^.State) then
      User2^.State := User2^.State - [userCloaked];
    RegisterUser(User2, User^.UserName);
  end;
  Tmp_Pos := 757;
  Preg := DB_Registered.FindUser(HList.Strings[0]);
  if Preg <> nil then
  begin
    if B = False then
      if Preg^.Level >= User^.Level then
      begin
        PermissionDenied('', True);
        Exit;
      end;
    if User^.Server = nil then
      if Preg^.Level > napUserModerator then
        if L <> napUserElite then
          for I := 0 to DB_Servers.Count - 1 do
          begin
            Srv := DB_Servers.Items[I];
            if Srv.Logged then
              if AnsiLowerCase(Srv.Reg_User) = Str then
              begin
                Error(GetLangT(LNG_ACCESSREG, Level2Str(Preg^.Level),
                  Preg^.Nick, Srv.Host, Srv.Console), True);
                Exit;
              end;
          end;
    Preg^.Level := L;
    B := True;
    if User2 = nil then
      Preg^.LastSetBy := User^.UserName;
  end;
  Tmp_Pos := 758;
  if B then
  begin
    if User^.Server = nil then
      WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
    Wallop(MSG_SERVER_NOSUCH, wallopLevel, GetLangT(LNG_LEVEL2, User^.UserName,
      HList.Strings[0], Level2Str(L), IntToStr(Ord(L))), True);
    if Preg = nil then
      if User2 <> nil then
        RegisterUser(User2, User^.UserName);
  end
  else
    NoSuchUser(True);
end;

procedure Handler_Kill;
var
  User2: POnlineUser;
  User3: TLocalUser;
  Srv: TServer;
  I: Integer;
begin
  Tmp_Pos := 760;
  if not IsLogged then Exit;
  if not CheckLevel('', napUserModerator) then Exit;
  if not CheckParams(1) then Exit;
  User2 := DB_Online.FindUser(HList.Strings[0]);
  Tmp_Pos := 761;
  if User2 = nil then
  begin
    UserIsOffline(HList.Strings[0], True);
    Exit;
  end;
  Tmp_Pos := 762;
  if User2^.Level >= User^.Level then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  if Num_Servers > 0 then
    if User2^.Level = napUserElite then
      for I := 0 to DB_Servers.Count - 1 do
      begin
        Srv := DB_Servers.Items[I];
        if Srv.Logged then
          if AnsiLowerCase(Srv.Reg_User) = AnsiLowerCase(User2^.UserName) then
          begin
            PermissionDenied('', True);
            Exit;
          end;
      end;
  Tmp_Pos := 763;
  Wallop(MSG_SERVER_NOSUCH, wallopKill, GetLangT(LNG_KICK, User^.UserName,
    User2^.UserName, NextParamEx(GCmd.Cmd)), True);
  if User^.Server = nil then
    WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
  User3 := User2^.Local;
  Tmp_Pos := 764;
  if User3 <> nil then
    if User3 <> Cons then
    begin
      User3.Exec(MSG_SERVER_NOSUCH, GetLangT(LNG_KICKMSG));
      if User2^.Level < napUserModerator then
        AddReconnector(Decode_Ip(User3.Ip));
      DisconnectUser(User3, '', GetLangT(LNG_KILLED, User2^.UserName,
        User2^.Software, User^.UserName), 'Handler_Kill', False);
    end;
end;

procedure Handler_Kick;
var
  Ch: TChannel;
  User2: POnlineUser;
  Str, Str1: string;
  I: Integer;
begin
  Tmp_Pos := 770;
  if not IsLogged then Exit;
  if not CheckParams(2) then Exit;
  Ch := FindChannel(HList.Strings[0]);
  if Ch = nil then
  begin
    Error(GetLangT(LNG_NOCHANNEL), True);
    Exit;
  end;
  Tmp_Pos := 771;
  if not Ch.Operator(User) then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  User2 := DB_Online.FindUser(HList.Strings[1]);
  if User2 = nil then
  begin
    NoSuchUser(True);
    Exit;
  end;
  Tmp_Pos := 772;
  if Ch.FindUser(User2) = -1 then
  begin
    NoSuchUser(True);
    Exit;
  end;
  if User2^.Level >= User^.Level then
  begin
    if User^.Level <> napUserUser then
    begin
      PermissionDenied('', True);
      Exit;
    end;
    Tmp_Pos := 773;
    if Ch.Operator(User2) then
    begin
      PermissionDenied('', True);
      Exit;
    end;
  end;
  Ch.Part(User2);
  case HList.Count of
    2: Str1 := '';
    3: Str1 := HList.Strings[2];
  else
    Str1 := NextParamEx(GCmd.Cmd, 2);
  end;
  Tmp_Pos := 774;
  Str := GetLangT(LNG_CHKICK, User^.UserName, User2^.UserName, Ch.Channel,
    Str1);
  Str1 := GetLangT(LNG_CHKICK, 'Operator', User2^.UserName, Ch.Channel, Str1);
  if not (userCloaked in User^.State) then
    Str1 := Str;
  for I := 0 to Ch.Users.Count - 1 do
  begin
    User2 := Ch.Users.Items[I];
    if User2^.Server = nil then
    begin
      if User2^.Level > napUserUser then
        Exec(User2, MSG_SERVER_PUBLIC, Ch.Channel + ' Server ' + Str)
      else
        Exec(User2, MSG_SERVER_PUBLIC, Ch.Channel + ' Server ' + Str1);
    end;
  end;
  Tmp_Pos := 775;
  if Ch.Users.Count = 0 then
    if not (chRegistered in Ch.State) then
      for I := DB_Channels.Count - 1 downto 0 do
        if DB_Channels.Items[I] = Ch then
        begin
          DB_Channels.Delete(I);
          Ch.Free;
        end;
  if User^.Server = nil then
    WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
end;

procedure Handler_ChannelUsersList;
var
  I: Integer;
  User2: POnlineUser;
  Ch: TChannel;
begin
  Tmp_Pos := 780;
  if not IsLogged then Exit;
  if not CheckParams(1) then Exit;
  Ch := FindChannel(HList.Strings[0]);
  if Ch = nil then
  begin
    Error(GetLangT(LNG_NOCHANNEL), True);
    Exit;
  end;
  Tmp_Pos := 781;
  if User^.Level < napUserModerator then
    if Ch.FindUser(User) = -1 then
    begin
      PermissionDenied('', True);
      Exit;
    end;
  if User^.Level < Ch.Level then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  Tmp_Pos := 782;
  for I := 0 to Ch.Users.Count - 1 do
  begin
    User2 := Ch.Users.Items[I];
    if (not (userCloaked in User2^.State)) or (User^.Level > napUserUser) then
      Exec(User, MSG_SERVER_NAMES_LIST, Ch.Channel + ' ' + User2^.UserName + ' '
        + IntToStr(User2^.Shared) + ' ' + IntToStr(Ord(User2^.Speed)));
  end;
  Exec(User, GCmd.Id, '');
end;

procedure Handler_UserList;
var
  Str, Ip_Mask: string;
  Server_Name: string;
  Soft_Mask: string;
  User2: POnlineUser;
  I, J, K: Integer;
  B, B_Console, B_Elite, B_Admin, B_Mod, B_User, B_Leech,
  B_All, B_Mask, B_Server_Mask, B_Cloaked, B_NotShare,
  B_SoftMask, B_ShowSoft, B_ShowShared, B_ShowServer: Boolean;
begin
  Tmp_Pos := 790;
  if not IsLogged then Exit;
  if not CheckLevel('', napUserModerator) then Exit;
  B_All := True;
  Ip_Mask := '*';
  Soft_Mask := '';
  B_Console := False;
  B_Elite := False;
  B_Admin := False;
  B_Mod := False;
  B_User := False;
  B_Leech := False;
  B_Cloaked := False;
  B_NotShare := False;
  B_ShowSoft := False;
  B_ShowShared := False;
  B_SoftMask := False;
  B_ShowServer := False;
  SplitString(LowerCase(GCmd.Cmd), HList);
  J := 0;
  if HList.Count > 0 then
    Server_Name := LowerCase(HList.Strings[0])
  else
    Server_Name := '*';
  Tmp_Pos := 791;
  if HList.Count > 1 then
    while J < HList.Count do
    begin
      if HList.Strings[J] = 'c' then
        B_Console := True
      else if HList.Strings[J] = 'e' then
        B_Elite := True
      else if HList.Strings[J] = 'a' then
        B_Admin := True
      else if HList.Strings[J] = 'm' then
        B_Mod := True
      else if HList.Strings[J] = 'u' then
        B_User := True
      else if HList.Strings[J] = 'l' then
        B_Leech := True
      else if HList.Strings[J] = '-c' then
        B_Console := True
      else if HList.Strings[J] = '-e' then
        B_Elite := True
      else if HList.Strings[J] = '-a' then
        B_Admin := True
      else if HList.Strings[J] = '-m' then
        B_Mod := True
      else if HList.Strings[J] = '-u' then
        B_User := True
      else if HList.Strings[J] = '-l' then
        B_Leech := True
      else if HList.Strings[J] = '-cl' then
        B_Cloaked := True
      else if HList.Strings[J] = '-cloak' then
        B_Cloaked := True
      else if HList.Strings[J] = '-console' then
        B_Console := True
      else if HList.Strings[J] = '-elite' then
        B_Elite := True
      else if HList.Strings[J] = '-admin' then
        B_Admin := True
      else if HList.Strings[J] = '-administrator' then
        B_Admin := True
      else if HList.Strings[J] = '-mod' then
        B_Mod := True
      else if HList.Strings[J] = '-moderator' then
        B_Mod := True
      else if HList.Strings[J] = '-user' then
        B_User := True
      else if HList.Strings[J] = '-leech' then
        B_Leech := True
      else if HList.Strings[J] = '-notshare' then
        B_NotShare := True
      else if HList.Strings[J] = '-showshared' then
        B_ShowShared := True
      else if HList.Strings[J] = '-showsoftware' then
        B_ShowSoft := True
      else if HList.Strings[J] = '-showclient' then
        B_ShowSoft := True
      else if HList.Strings[J] = '-showserver' then
        B_ShowServer := True
      else if HList.Strings[J] = '-mods' then
      begin
        B_Mod := True;
        B_Admin := True;
        B_Elite := True;
        B_Console := True;
      end
      else if (HList.Strings[J] = 'i') or (HList.Strings[J] = '-ip') then
      begin
        if HList.Count > (J + 1) then
          Ip_Mask := HList.Strings[J + 1];
        Inc(J);
      end
      else if (HList.Strings[J] = '-client') or (HList.Strings[J] = '-software')
        then
      begin
        if HList.Count > (J + 1) then
          Soft_Mask := HList.Strings[J + 1];
        Inc(J);
      end;
      Inc(J);
    end;
  Tmp_Pos := 792;
  if B_Console or B_Elite or B_Admin or B_Mod or B_User or B_Leech then
    B_All := False;
  B_Mask := Ip_Mask <> '*';
  B_Server_Mask := Server_Name <> '*';
  if Soft_Mask <> '' then
    B_SoftMask := True;
  if Soft_Mask = '*' then
    B_SoftMask := False;
  if B_SoftMask then
    Soft_Mask := AnsiLowerCase(Soft_Mask);
  for K := 0 to USERS_NAME_ARRAY - 1 do
    for I := 0 to DB_Online.Names[K].Count - 1 do
    begin
      Tmp_Pos := 793;
      if ((I mod 30) = 5) then
      begin
{$I CheckSync.pas}
      end;
      User2 := DB_Online.Names[K].Items[I];
      B := True;
      if not B_All then
        case User2^.Level of
          napUserLeech: B := B_Leech;
          napUserUser: B := B_User;
          napUserModerator: B := B_Mod;
          napUserAdmin: B := B_Admin;
          napUserElite: B := B_Elite;
          napUserConsole: B := B_Console;
        end;
      if (userCloaked in User2^.State) then
        B := B_Cloaked;
      if B and B_NotShare then
        if User2^.Shared > 0 then
          B := False;
      Tmp_Pos := 794;
      if B and B_Mask then
        B := MatchesMaskEx(Decode_Ip(User2^.Ip), Ip_Mask);
      if B and B_Server_Mask then
        B := MatchesMaskEx(LowerCase(GetServerName(User2^.Server)),
          Server_Name);
      if B and B_SoftMask then
        B := MatchesMaskEx(AnsiLowerCase(User2^.Software), Soft_Mask);
      if B then
      begin
        Str := User2^.UserName + ' ' + Decode_Ip(User2^.Ip);
        if B_ShowShared then
          Str := Str + ' ' + IntToStr(User2^.Shared);
        if B_ShowSoft then
          Str := Str + ' ' + AddStr(User2^.Software);
        if B_ShowServer then
          Str := Str + ' ' + AddStr(GetServerName(User2^.Server));
        if Query = queryNormal then
          Exec(User, MSG_SERVER_GLOBAL_USER_LIST, Str)
        else
          Error(Str);
      end;
    end;
  if Query = queryNormal then
    Exec(User, MSG_CLIENT_GLOBAL_USER_LIST, '')
  else
    Error('.');
  Tmp_Pos := 795;
{$I CheckSync.pas}
end;

procedure Handler_SetConfig; //
begin
  if not IsLogged then Exit;
  if not CheckLevel('', napUserAdmin) then Exit;
  SetConfig(User, GCmd.Cmd);
end;

procedure Handler_RelayMessage;
var
  User2: POnlineUser;
  I: Integer;
begin
  Tmp_Pos := 800;
  if Query = queryServer then
    SplitString(GCmd.Cmd, HList)
  else
  begin
    if not IsLogged then Exit;
    if not checkParams(2) then Exit;
    if not CheckLevel('', napUserAdmin) then Exit;
  end;
  Tmp_Pos := 801;
  if not IsDigit(HList.Strings[0]) then
  begin
    Error(GetLangT(LNG_INVALIDARGS), True);
    Exit;
  end;
  User2 := DB_Online.FindUser(HList.Strings[1]);
  Tmp_Pos := 802;
  if User2 = nil then
  begin
    NoSuchUser(True);
    Exit;
  end;
  I := StrToIntDef(HList.Strings[0], MSG_SERVER_NOSUCH);
  if Query <> queryServer then
    if User2^.Level > napUserUser then
    begin
      PermissionDenied('', True);
      Exit;
    end;
  Tmp_Pos := 803;
  Exec(User2, I, NextParamEx(GCmd.Cmd, 2));
end;

procedure Handler_RelayAll;
var
  I, J, K: Integer;
  Str: string;
  User2: POnlineUser;
begin
  Tmp_Pos := 804;
  if Query <> queryServer then Exit;
  if not CheckParams(1) then Exit;
  if not IsDigit(HList.Strings[0]) then
  begin
    Error(GetLangT(LNG_INVALIDARGS), True);
    Exit;
  end;
  Tmp_Pos := 805;
  I := StrToIntDef(HList.Strings[0], 404);
  Str := NextParamEx(GCmd.Cmd);
  for K := 0 to USERS_NAME_ARRAY - 1 do
    for J := 0 to DB_Online.Names[K].Count - 1 do
    begin
      if ((J mod 50) = 5) then
      begin
{$I CheckSync.pas}
      end;
      User2 := DB_Online.Names[K].Items[J];
      if (User2 <> User) and (User2^.Server = nil) then
        Exec(User2, I, Str);
    end;
  Tmp_Pos := 806;
  if User^.Server = nil then
    WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
end;

procedure Handler_UsageStats;
var
  Str: string;
  I, J: Integer;
  Srv2: TServer;
begin
  if not IsLogged then Exit;
  if not CheckLevel('', napUserModerator) then Exit;
  J := 0;
  for I := 0 to DB_Servers.Count - 1 do
  begin
    Srv2 := DB_Servers.Items[I];
    if Srv2.Logged and (Srv2.Hub = nil) then
      Inc(J);
  end;
  Str := Format('%d %d %d %d %d %d %d %d %d %d %.2f %.2f %.2f %d %d 0', [
    Local_Users,
      J,
      Total_Users,
      Total_Files,
      Total_Bytes div 1073741824,
      DB_Channels.Count,
      Start_Time_T,
      DateTimeToUnixTime(Now) - Start_Time_T,
      AllocMemCount,
      DB_Registered.CountUsers,
      Last_Bytes_in / 60.0 / 1024.0,
      Last_Bytes_Out / 60 / 1024,
      Last_Searches / 60,
      Total_Bytes_in,
      Total_Bytes_Out
      ]);
  Exec(User, GCmd.Id, Str);
  case Query of
    queryOperServ, queryNickServ, queryChanServ, queryMsgServ, queryChannel:
      Error(RS_Handler_UsageStats + Str);
  end;
end;

procedure Handler_SoftwareStats;
var
  I: Integer;
  A: PNapCmd2;
begin
  if not IsLogged then Exit;
  Tmp_Pos := 820;
  if DB_Software <> nil then
  begin
    DB_Software.SortByID1;
    Tmp_Pos := 821;
    case Query of
      queryOperServ, queryNickServ, queryMsgServ, queryChanServ, queryChannel:
        begin
          Error(RS_Handler_SoftStats_Title);
          Error(RS_Handler_SoftStats_Format);
        end;
    end;
    for I := 0 to DB_Software.Count - 1 do
    begin
      if (I mod 100) = 50 then
      begin
{$I CheckSync.pas}
      end;
      A := DB_Software.Items[I];
      case Query of
        queryOperServ, queryNickServ, queryMsgServ, queryChanServ,
          queryChannel: Error(IntToStr(A^.Id1) + '        ' + #9 + ' ' +
            A^.Cmd);
      else
        Exec(User, MSG_CLIENT_VERSION_STATS, AddStr(A^.Cmd) + ' ' +
          IntToStr(A^.Id1));
      end;
    end;
  end;
  Tmp_Pos := 822;
  case Query of
    queryOperServ, queryNickServ, queryMsgServ, queryChanServ,
      queryChannel: Error(GetLangT(LNG_ITEMSLISTED,
        IntToStr(DB_Software.Count)));
  else
    Exec(User, MSG_CLIENT_VERSION_STATS, '');
  end;
end;

procedure Handler_WhichServer;
var
  User2: POnlineUser;
  L2: TLocalUser;
begin
  if not IsLogged then Exit;
  // if (User^.Level = napUserLeech) or (userMuzzled in User^.State) then
  if User^.Level < napUserModerator then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  Tmp_Pos := 823;
  User2 := DB_Online.FindUser(GCmd.Cmd);
  if User2 = nil then
  begin
    UserIsOffline(GCmd.Cmd, True);
    Exit;
  end;
  if userCloaked in User2.State then
    if User^.Level < napUserModerator then
    begin
      UserIsOffline(GCmd.Cmd, True);
      Exit;
    end;
  L2 := User2^.Local;
  Tmp_Pos := 824;
  if L2 <> nil then
  begin
    if User^.Level < napUserModerator then
      if StrHash_FindString(L2.Ignored, AnsiLowerCase(User^.UserName), False)
        then
      begin
        UserIsOffline(GCmd.Cmd, True);
        Exit;
      end;
  end;
  Tmp_Pos := 825;
  Error(GetLangT(LNG_USERSERVER, User2^.UserName,
    GetServerName(User2^.Server)));
end;

procedure Handler_WhoWas; // 10121
var
  User2: POnlineUser;
  Reg: PRegisteredUser;
  L2: TLocalUser;
  I: Integer;
  P: PNapCmdEx;
  Str: string;
begin
  Tmp_Pos := 830;
  if not IsLogged then Exit;
  if not CheckParams(1) then Exit;
  User2 := DB_Online.FindUser(GCmd.Cmd);
  if User2 <> nil then
  begin
    L2 := User2^.Local;
    if L2 <> nil then
      if User^.Level < napUserModerator then
        if StrHash_FindString(L2.Ignored, AnsiLowerCase(User^.UserName), False)
          then
          User2 := nil;
  end;
  Tmp_Pos := 831;
  if User = nil then
  begin
    Reg := DB_Registered.FindUser(GCmd.Cmd);
    if Reg <> nil then
    begin
      if User^.Level > napUserUser then
        Exec(User, GCmd.Id, Reg^.Nick + ' ' + IntToStr(Reg^.Last_Ip) + ' ' +
          ServerName_T + ' ' + IntToStr(Reg^.Last_Seen) + ' "n/a"')
      else
        Exec(User, GCmd.Id, Reg^.Nick + ' 0 ' + ServerAlias + ' ' +
          IntToStr(Reg^.Last_Seen) + ' "n/a"');
      Exit;
    end
    else
    begin
      I := DB_Whowas.FindByCmd(AnsiLowerCase(GCmd.Cmd));
      if I <> -1 then
      begin
        P := DB_Whowas.Items[I];
        SplitString(P^.Data, HLst);
        if HLst.Count > 5 then
        begin
          Str := GCmd.Cmd + ' ';
          if User^.Level > napUserUser then
            Str := Str + HLst.Strings[3] + ' '
          else
            Str := Str + '0 ';
          Str := Str + AddStr(HLst.Strings[4]) + ' ' + HLst.Strings[2] + ' ' +
            AddStr(HLst.Strings[5]);
          Exec(User, GCmd.Id, Str);
        end;
      end;
    end;
    Tmp_Pos := 832;
    UserIsOffline(GCmd.Cmd, True);
    Exit;
  end;
  Tmp_Pos := 833;
  if User^.Level > napUserUser then
    Exec(User, GCmd.Id, User2^.UserName + ' ' + IntToStr(User2^.Ip) + ' ' +
      GetServerName(User2^.Server) + ' ' + IntToStr(User2^.Last_Seen_T) + ' "' +
      User2^.Software + '"')
  else
    Exec(User, GCmd.Id, User2^.UserName + ' 0 ' + GetServerAlias(User2^.Server) +
      ' ' + IntToStr(User2^.Last_Seen_T) + ' "' + User2^.Software + '"');
end;

procedure Handler_AdminRegister;
var
  L: TNapUserLevel;
  Reg: TRegisteredUser;
  Preg: PRegisteredUser;
  Usr: POnlineUser;
begin
  if not IsLogged then Exit;
  if not CheckParams(1) then Exit;
  if HList.Count = 1 then
  begin
    Usr := DB_Online.FindUser(HList.Strings[0]);
    if Usr = nil then
      UserIsOffline(HList.Strings[0], True)
    else
    begin
      Preg := DB_Registered.FindUser(HList.Strings[0]);
      if Preg <> nil then
        Error(GetLangT(LNG_ALREADYREGISTERED, Level2Str(Preg^.Level),
          Preg^.Nick), True)
      else
      begin
        RegisterUser(Usr, User^.UserName);
        Wallop(MSG_SERVER_NOSUCH, wallopRegister, GetLangT(LNG_REGOK2,
          User^.UserName, Usr^.UserName, Level2Str(Usr^.Level)), True);
        if User^.Server = nil then
          WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
      end;
    end;
    Exit;
  end;
  if HList.Count = 2 then
    HList.Add('anon@' + ServerAlias);
  if HList.Count = 3 then
    HList.Add('1');
  Tmp_Pos := 840;
  if not IsLevel(HList.Strings[3]) then
  begin
    Error(GetLangT(LNG_INVALIDLEVEL, HList.Strings[3]), True);
    Exit;
  end;
  L := Str2Level(HList.Strings[3]);
  Tmp_Pos := 841;
  if (L >= User^.Level) or (L < napUserUser) then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  if not Check_Name(HList.Strings[0]) then
  begin
    Error(GetLangT(LNG_INVALIDNICK2, HList.Strings[0]), True);
    Exit;
  end;
  Tmp_Pos := 842;
  if DB_Registered.FindUser(HList.Strings[0]) <> nil then
  begin
    Error(GetLangT(LNG_REGEXISTS, HList.Strings[0]), True);
    Exit;
  end;
  Tmp_Pos := 843;
  ResetRegistered(Reg);
  Reg.Nick := HList.Strings[0];
  Reg.Password := Encode(HList.Strings[1]);
  Reg.Level := L;
  Reg.Last_Seen := 946702800;
  Reg.Created := Current_Time_T;
  Reg.CreatedBy := User^.UserName;
  DB_Registered.Add(Reg);
  Tmp_Pos := 844;
  Wallop(MSG_SERVER_NOSUCH, wallopRegister, GetLangT(LNG_REGOK2, User^.UserName,
    Reg.Nick, Level2Str(Reg.Level)), True);
  if User^.Server = nil then
    WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
end;

procedure Handler_ChannelOp;
var
  Ch: TChannel;
  User2: POnlineUser;
  Str: string;
begin
  Tmp_Pos := 850;
  if not IsLogged then Exit;
  if not CheckLevel('', napUserModerator) then Exit;
  if not CheckParams(2) then Exit;
  Ch := FindChannel(HList.Strings[0]);
  if Ch = nil then
  begin
    Error(GetLangT(LNG_NOCHANNEL), True);
    Exit;
  end;
  Tmp_Pos := 851;
  if Ch.Level > napUserUser then
  begin
    Error(GetLangT(LNG_CHNOOPS), True);
    Exit;
  end;
  HList.Delete(0);
  Tmp_Pos := 852;
  while HList.Count > 0 do
  begin
    Str := HList.Strings[0];
    HList.Delete(0);
    Tmp_Pos := 853;
    if not Check_Name(Str) then
    begin
      if User^.Server = nil then
        Error(GetLangT(LNG_INVALIDNICK2, Str), True);
    end
    else if StrHash_FindString(Ch.Ops, Str, True) then
    begin
      if User^.Server = nil then
        Error(GetLangT(LNG_CHANOP3, Str, Ch.Channel), True);
    end
    else
    begin
      User2 := DB_Online.FindUser(Str);
      Tmp_Pos := 854;
      if (User2 <> nil) and (User2^.Level > napUserUser) then
      begin
        if User^.Server = nil then
          Error(GetLangT(LNG_CHANOP3, Str, Ch.Channel), True);
      end
      else
      begin
        Tmp_Pos := 855;
        StrHash_AddEx(Ch.Ops, Str);
        Wallop(MSG_SERVER_NOSUCH, wallopChannel, GetLangT(LNG_CHANOP,
          User^.UserName, Str, Ch.Channel), True);
        if User2 <> nil then
          if User2^.Server = nil then
          begin
            if Ch.FindUser(User2) <> -1 then
              Exec(User2, MSG_SERVER_PUBLIC, Ch.Channel + ' Server ' +
                GetLangT(LNG_CHANOP2, Ch.Channel))
            else if User^.Server = nil then
              Error(GetLangT(LNG_CHANOP2, Ch.Channel), True);
          end;
        Tmp_Pos := 856;
      end;
    end;
  end;
  Tmp_Pos := 857;
  if User^.Server = nil then
    WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
end;

procedure Handler_ChannelDeop;
var
  Ch: TChannel;
  User2: POnlineUser;
  Str: string;
begin
  Tmp_Pos := 860;
  if not IsLogged then Exit;
  if not CheckLevel('', napUserModerator) then Exit;
  if not CheckParams(2) then Exit;
  Ch := FindChannel(HList.Strings[0]);
  if Ch = nil then
  begin
    if User^.Server = nil then
      Error(GetLangT(LNG_NOCHANNEL), True);
    Exit;
  end;
  Tmp_Pos := 861;
  if Ch.Level > napUserUser then
  begin
    Error(GetLangT(LNG_CHNOOPS), True);
    StrHash_Clear(Ch.Ops);
    Exit;
  end;
  HList.Delete(0);
  Tmp_Pos := 862;
  while HList.Count > 0 do
  begin
    Str := HList.Strings[0];
    HList.Delete(0);
    Tmp_Pos := 863;
    if not Check_Name(Str) then
    begin
      if User^.Server = nil then
        Error(GetLangT(LNG_INVALIDNICK2, Str), True);
    end
    else
    begin
      Tmp_Pos := 864;
      if not StrHash_FindString(Ch.Ops, Str, True) then
      begin
        if User^.Server = nil then
          Error(GetLangT(LNG_CHANDEOP3, Str, Ch.Channel), True);
      end
      else
      begin
        User2 := DB_Online.FindUser(Str);
        if (User2 <> nil) and (User2^.Level > napUserUser) then
        begin
          if User^.Server = nil then
            Error(GetLangT(LNG_CHANDEOP4, Str), True);
        end
        else
        begin
          Tmp_Pos := 865;
          StrHash_Delete(Ch.Ops, Str, True);
          Wallop(MSG_SERVER_NOSUCH, wallopChannel, GetLangT(LNG_CHANDEOP,
            User^.UserName, Str, Ch.Channel), True);
          if User2 <> nil then
            if User2^.Server = nil then
            begin
              Tmp_Pos := 866;
              if Ch.FindUser(User2) <> -1 then
                Exec(User2, MSG_SERVER_PUBLIC, Ch.Channel + ' Server ' +
                  GetLangT(LNG_CHANDEOP2, Ch.Channel))
              else
                Exec(User2, MSG_SERVER_NOSUCH, GetLangT(LNG_CHANDEOP2,
                  Ch.Channel));
            end;
        end;
      end;
    end;
  end;
  Tmp_Pos := 867;
  if User^.Server = nil then
    WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
end;

procedure Handler_ChannelOpEmote;
var
  Ch: TChannel;
  Str, Tempnic: string;
  I: Integer;
  User2: POnlineUser;
begin
  Tmp_Pos := 870;
  if not IsLogged then Exit;
  if not CheckParams(2) then Exit;
  Ch := FindChannel(HList.Strings[0]);
  if Ch = nil then
  begin
    Error(GetLangT(LNG_NOCHANNEL), True);
    Exit;
  end;
  Tmp_Pos := 871;
  if not Ch.Operator(User) then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  Str := NextParamEx(GCmd.Cmd);
  if AnsiUpperCase(Str) = AnsiLowerCase(Str) then Exit;
  // if Length(Str) > 0 then
  if Str[1] <> '"' then
    Str := '"' + Str + '"';
  // if Prevent_Shouting then
  // if AnsiUpperCase(Str) = Str then
  // begin
  //   Str := AnsiLowerCase(Str);
  //   GCmd.Cmd := Ch.Channel + ' ' + Str;
  // end;
  if userCloaked in User^.State then
    Tempnic := 'Cloaked/' + User^.UserName
  else
    Tempnic := User^.UserName;
  Str := Ch.Channel + ' Ops/' + Tempnic + ' ' + Str;
  Tmp_Pos := 872;
  for I := 0 to Ch.Users.Count - 1 do
  begin
    User2 := Ch.Users.Items[I];
    if User2^.Server = nil then
      if Ch.Operator(User2) then
      begin
        if Old_Opsay then
          Exec(User2, MSG_SERVER_NOSUCH, Str)
        else
          Exec(User2, MSG_SERVER_EMOTE, Str);
      end;
  end;
  Tmp_Pos := 873;
  if User^.Server = nil then
    WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
end;

procedure Handler_ChannelWallop; // 10208
var
  Ch: TChannel;
  Str, Tempnic: string;
  I: Integer;
  User2: POnlineUser;
begin
  Tmp_Pos := 870;
  if not IsLogged then Exit;
  if not CheckParams(2) then Exit;
  Ch := FindChannel(HList.Strings[0]);
  if Ch = nil then
  begin
    Error(GetLangT(LNG_NOCHANNEL), True);
    Exit;
  end;
  Tmp_Pos := 871;
  if not Ch.Operator(User) then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  Str := NextParamEx(GCmd.Cmd);
  // if Prevent_Shouting then
  //   if AnsiUpperCase(Str) = Str then
  //   begin
  //     Str := AnsiLowerCase(Str);
  //     GCmd.Cmd := Ch.Channel + ' ' + Str;
  //   end;
  if userCloaked in User^.State then
    Tempnic := 'Cloaked/' + User^.UserName
  else
    Tempnic := User^.UserName;
  if Old_Opsay then
    Str := Tempnic + ' [Ops/' + Ch.Channel + ']: ' + Str
  else
    Str := Ch.Channel + ' Ops/' + Tempnic + ' ' + Str;
  Tmp_Pos := 872;
  for I := 0 to Ch.Users.Count - 1 do
  begin
    User2 := Ch.Users.Items[I];
    if User2^.Server = nil then
      if Ch.Operator(User2) then
      begin
        if Old_Opsay then
          Exec(User2, MSG_SERVER_NOSUCH, Str)
        else
          Exec(User2, MSG_SERVER_PUBLIC, Str);
      end;
  end;
  Tmp_Pos := 873;
  if User^.Server = nil then
    WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
end;

procedure Handler_ChannelMode; // 10209
var
  Ch: TChannel;
  St, Old_St: TChannelState;
  Str: string;
begin
  Tmp_Pos := 880;
  if not IsLogged then Exit;
  if not CheckLevel('', napUserModerator) then Exit;
  if not CheckParams(1) then Exit;
  Ch := FindChannel(HList.Strings[0]);
  if Ch = nil then
  begin
    Error(GetLangT(LNG_NOCHANNEL), True);
    Exit;
  end;
  Tmp_Pos := 881;
  if Ch.Level > User^.Level then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  HList.Delete(0);
  Tmp_Pos := 882;
  Old_St := Ch.State;
  St := Ch.State;
  while HList.Count > 0 do
  begin
    Str := LowerCase(HList.Strings[0]);
    HList.Delete(0);
    if (Str = '+private') or (Str = '+p') then
      St := St + [chPrivate]
    else if (Str = '-private') or (Str = '-p') then
      St := St - [chPrivate]
    else if (Str = '+moderated') or (Str = '+m') then
      St := St + [chModerated]
    else if (Str = '-moderated') or (Str = '-m') then
      St := St - [chModerated]
    else if (Str = '+topic') or (Str = '+t') then
      St := St + [chTopic]
    else if (Str = '-topic') or (Str = '-t') theN
      St := St - [chTopic]
    else if (Str = '+registered') or (Str = '+r') then
      St := St + [chRegistered]
    else if (Str = '-registered') or (Str = '-r') then
      St := St - [chRegistered]
    else
      Error(GetLangT(LNG_UNKNOWNCHMODE, Str));
  end;
  Tmp_Pos := 883;
  Ch.State := St;
  if St <> Old_St then
  begin
    Str := '';
    if (chPrivate in St) and (not (chPrivate in Old_St)) then
      Str := Str + ' +PRIVATE';
    if (chPrivate in Old_St) and (not (chPrivate in St)) then
      Str := Str + ' -PRIVATE';
    if (chModerated in St) and (not (chModerated in Old_St)) then
      Str := Str + ' +MODERATED';
    if (chModerated in Old_St) and (not (chModerated in St)) then
      Str := Str + ' -MODERATED';
    if (chTopic in St) and (not (chTopic in Old_St)) then
      Str := Str + ' +TOPIC';
    if (chTopic in Old_St) and (not (chTopic in St)) then
      Str := Str + ' -TOPIC';
    if (chRegistered in St) and (not (chRegistered in Old_St)) then
      Str := Str + ' +REGISTERED';
    if (chRegistered in Old_St) and (not (chRegistered in St)) then
      Str := Str + ' -REGISTERED';
    Wallop(MSG_SERVER_NOSUCH, wallopChannel, Format(RS_Handler_ChanModeChange,
      [User^.UserName, Ch.Channel, Str]), True);
    if User^.Server = nil then
      WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
  end;
  if User^.Server <> nil then Exit;
  Tmp_Pos := 884;
  Str := '';
  if chRegistered in St then
    Str := '+REGISTERED';
  if chPrivate in St then
  begin
    if Str <> '' then
      Str := Str + ' ';
    Str := Str + '+PRIVATE';
  end;
  if chModerated in St then
  begin
    if Str <> '' then
      Str := Str + ' ';
    Str := Str + '+MODERATED';
  end;
  if chTopic in St then
  begin
    if Str <> '' then
      Str := Str + ' ';
    Str := Str + '+TOPIC';
  end;
  if Str = '' then
    Str := RS_Handler_ChanModeEmpty;
  Tmp_Pos := 885;
  if (Local <> Cons) or (Query <> queryNormal) then
    Error(Format(RS_Handler_ChanMode, [Ch.Channel, Str]));
end;

procedure Handler_ChannelVoice;
var
  Ch: TChannel;
  User2: POnlineUser;
  Str, Str1: string;
begin
  Tmp_Pos := 890;
  if not IsLogged then Exit;
  if not CheckParams(2) then Exit;
  Ch := FindChannel(HList.Strings[0]);
  if Ch = nil then
  begin
    Error(GetLangT(LNG_NOCHANNEL), True);
    Exit;
  end;
  Tmp_Pos := 891;
  if not Ch.Operator(User) then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  if (not (chModerated in Ch.State)) or (Ch.Level > napUserUser) then
  begin
    Error(GetLangT(LNG_NOMODERATE, Ch.Channel), True);
    Exit;
  end;
  HList.Delete(0);
  Tmp_Pos := 892;
  Str1 := User^.UserName;
  if userCloaked in User^.State then
    Str1 := 'Operator';
  while HList.Count > 0 do
  begin
    Str := HList.Strings[0];
    HList.Delete(0);
    Tmp_Pos := 893;
    if not Check_Name(Str) then
      Error(GetLangT(LNG_INVALIDNICK2, Str), True)
    else if StrHash_FindString(Ch.Voices, Str, True) then
      Error(GetLangT(LNG_VOICE3, Str, Ch.Channel), True)
    else
    begin
      Tmp_Pos := 894;
      User2 := DB_Online.FindUser(Str);
      if (User2 <> nil) and (User2^.Level > napUserUser) then
        Error(GetLangT(LNG_VOICE3, Str, Ch.Channel), True)
      else
      begin
        Tmp_Pos := 895;
        StrHash_AddEx(Ch.Voices, Str);
        if User^.Server = nil then
          Ch.Wallop(GetLangT(LNG_VOICE4, User^.UserName, Str));
        if User2 <> nil then
          if User2^.Server = nil then
          begin
            if Ch.FindUser(User2) <> -1 then
              Exec(User2, MSG_SERVER_PUBLIC, Ch.Channel + ' Server ' +
                GetLangT(LNG_VOICE, Str1, Ch.Channel))
            else
              Exec(User2, MSG_SERVER_NOSUCH, GetLangT(LNG_VOICE, Str1,
                Ch.Channel));
          end;
      end;
    end;
  end;
  Tmp_Pos := 896;
  if User^.Server = nil then
    WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
end;

procedure Handler_ChannelUnvoice;
var
  Ch: TChannel;
  User2: POnlineUser;
  Str: string;
begin
  Tmp_Pos := 900;
  if not IsLogged then Exit;
  if not CheckLevel('', napUserModerator) then Exit;
  if not CheckParams(2) then Exit;
  Ch := FindChannel(HList.Strings[0]);
  if Ch = nil then
  begin
    Error(GetLangT(LNG_NOCHANNEL), True);
    Exit;
  end;
  Tmp_Pos := 901;
  if not Ch.Operator(User) then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  if (not (chModerated in Ch.State)) or (Ch.Level > napUserUser) then
  begin
    Error(GetLangT(LNG_NOMODERATE, Ch.Channel), True);
    StrHash_Clear(Ch.Voices);
    Exit;
  end;
  HList.Delete(0);
  Tmp_Pos := 902;
  while HList.Count > 0 do
  begin
    Str := HList.Strings[0];
    HList.Delete(0);
    Tmp_Pos := 903;
    if not Check_Name(Str) then
      Error(GetLangT(LNG_INVALIDNICK2, Str), True)
    else
    begin
      if not StrHash_FindString(Ch.Voices, Str, True) then
        Error(GetLangT(LNG_DEVOICE3, Str, Ch.Channel), True)
      else
      begin
        Tmp_Pos := 904;
        User2 := DB_Online.FindUser(Str);
        if (User2 <> nil) and (User2^.Level > napUserUser) then
          Error(GetLangT(LNG_DEVOICE4, Str), True)
        else
        begin
          Tmp_Pos := 905;
          StrHash_Delete(Ch.Voices, Str, True);
          if User^.Server = nil then
            Ch.Wallop(GetLangT(LNG_DEVOICE5, User^.UserName, Str));
          if User2 <> nil then
            if User2^.Server = nil then
            begin
              Tmp_Pos := 906;
              if Ch.FindUser(User2) <> -1 then
                Exec(User2, MSG_SERVER_PUBLIC, Ch.Channel + ' Server ' +
                  GetLangT(LNG_DEVOICE, Ch.Channel))
              else
                Exec(User2, MSG_SERVER_NOSUCH, GetLangT(LNG_DEVOICE,
                  Ch.Channel));
            end;
        end;
      end;
    end;
  end;
  Tmp_Pos := 907;
  if User^.Server = nil then
    WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
end;

procedure Handler_GetConsole;
var
  I, Num: Integer;
  Srv: TServer;
  // Display_Name: string;
  // Moderator: Boolean;
begin
  if User^.Level < napUserModerator then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  if Query = queryNormal then
    Error(ServerName_T + ' ' + Cons.Data^.UserName)
  else
    Error(Cons.Data^.UserName + '  '#9 + ServerName_T);
  if Num_Servers = 0 then
  begin
    if Query = queryNormal then
      Local.Exec(GCmd.Id, GCmd.Cmd)
    else
      Error(GetLangT(LNG_ITEMSLISTED, '1'));
    Exit;
  end;
  Num := 1;
  for I := 0 to DB_Servers.Count - 1 do
  begin
    Srv := DB_Servers.Items[I];
    if Srv.Logged then
    begin
      if Query = queryNormal then
        Error(Srv.Host + ' ' + Srv.Console)
      else
        Error(Srv.Console + '  '#9 + Srv.Host);
      Inc(Num);
    end;
  end;
  if Query = queryNormal then
    Local.Exec(GCmd.Id, GCmd.Cmd)
  else
    Error(GetLangT(LNG_ITEMSLISTED, IntToStr(Num)));
end;

procedure Handler_Transfer;
begin
  if not IsLogged then Exit;
  if User^.Level = napUserLeech then Exit;
  if (Query <> queryNormal) and (Query <> queryRemoteUser) then Exit;
  case GCmd.Id of
    MSG_CLIENT_DOWNLOAD_START:
      begin
        Inc(User^.Downloads);
        Inc(User^.Total_Down);
      end;
    MSG_CLIENT_DOWNLOAD_END:
      begin
        Dec(User^.Downloads);
        if User^.Downloads < 0 then
          User^.Downloads := 0;
      end;
    MSG_CLIENT_UPLOAD_START:
      begin
        Inc(User^.Uploads);
        Inc(User^.Total_Up);
      end;
    MSG_CLIENT_UPLOAD_END:
      begin
        Dec(User^.Uploads);
        if User^.Uploads < 0 then
          User^.Uploads := 0;
      end;
  end;
  if Local <> nil then
    Local.LocalState := Local.LocalState + [locNeedsUpdate];
end;

procedure Handler_SetTransfers;
begin
  if not IsLogged then Exit;
  if not CheckParams(4) then Exit;
  if User = nil then Exit;
  User^.Downloads := StrToIntDef(HList.Strings[0], 0);
  User^.Uploads := StrToIntDef(HList.Strings[1], 0);
  User^.Max_Up := StrToIntDef(HList.Strings[2], -1);
  User^.Queue := StrToIntDef(HList.Strings[3], -1);
  if User^.Server = nil then
  begin
    if Local = nil then
      WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd)
    else
      Local.LocalState := Local.LocalState + [locNeedsUpdate];
  end;
end;

procedure Handler_GetTransfers;
var
  User2: POnlineUser;
begin
  if not IsLogged then Exit;
  User2 := DB_Online.FindUser(GCmd.Cmd);
  if User2 = nil then
  begin
    UserIsOffline(GCmd.Cmd, True);
    Exit;
  end;
  Exec(User, GCmd.Id, User2^.UserName + ' ' + IntToStr(User2^.Downloads) + ' ' +
    IntToStr(User2^.Uploads) + ' ' + IntToStr(User2^.Max_Up) + ' ' +
    IntToStr(User2^.Queue));
end;

procedure Handler_GhostKiller;
var
  I: Integer;
  User2: POnlineUser;
  Users: TMyList;
begin
  if not IsLogged then Exit;
  if User^.Server <> nil then Exit;
  if GCmd.Cmd = '' then
  begin // List users with that IP
    Users := TMyList.Create;
    try
      DB_Online.GetClones(User^.Ip, Users);
      for I := 0 to Users.Count - 1 do
        if Users.Items[I] <> User then
        begin
          User2 := Users.Items[I];
          if Query = queryNormal then
            Error(User2^.UserName + ' ' + GetServerAlias(User2^.Server))
          else
            Error(Format(RS_Handler_GhostList,
              [User2^.UserName, GetServerAlias(User2^.Server)]));
        end;
      if Query = queryNormal then
        Local.Exec(GCmd.Id, GCmd.Cmd)
      else
        Error(GetLangT(LNG_ITEMSLISTED, IntToStr(Users.Count - 1)));
      Exit;
    finally
      Users.Free;
    end;
  end;
  if not CheckParams(2) then Exit;
  User2 := DB_Online.FindUser(HList.Strings[0]);
  if User2 = nil then
  begin
    UserIsOffline(HList.Strings[0], True);
    Exit;
  end;
  if User2 = User then
  begin
    PermissionDenied('');
    Exit;
  end;
  if User2^.Level = napUserConsole then
  begin
    PermissionDenied('');
    Exit;
  end;
  if User^.Ip <> User2^.Ip then
  begin
    PermissionDenied('');
    Exit;
  end;
  if User2^.Password <> Encode(HList.Strings[1]) then
  begin
    PermissionDenied('');
    Exit;
  end;
  Wallop(MSG_SERVER_NOSUCH, wallopKill, GetLangT(LNG_KICK, User^.UserName,
    User2^.UserName, RS_Handler_WallopKillGhost), False);
  WriteAllServers(MSG_SRV_USEROFFLINE, User2^.UserName, '');
  KickUser(User2, Format(RS_Handler_KillGhost, [User^.UserName]));
end;

procedure Handler_AddChannel;
var
  Ch: TChannel;
  Str: string;
begin
  Tmp_Pos := 910;
  if not CheckLevel('', napUserModerator) then Exit;
  Str := ChannelName(GCmd.Cmd);
  if Str = '' then
  begin
    Error(GetLangT(LNG_INVALIDARGS), True);
    Exit;
  end;
  Ch := Findchannel(Str);
  Tmp_Pos := 911;
  if Ch <> nil then Exit;
  if DB_Channels.Count >= Max_Channels_Total then
    if User^.Server = nil then
    begin
      Error(GetLangT(LNG_CHANNELLIMIT), True);
      Exit;
    end;
  Tmp_Pos := 912;
  Ch := TChannel.Create(Str);
  Ch.State := [chRegistered];
  DB_Channels.Add(Ch);
  Tmp_Pos := 913;
  Wallop(MSG_SERVER_NOSUCH, wallopChannel, GetLangT(LNG_CHCREATED,
    Level2Str(User^.Level), User^.UserName, Str), True);
  if User^.Server = nil then
    WriteAllServers(GCmd.Id, User^.UserName, GCmd.Cmd);
  Tmp_Pos := 914;
  if Local = Cons then
    Cmd_List.AddDoubleCmd(MSG_CMD_LISTCHANNELS, 0, '', '');
end;

procedure Handler_ServerConnect(Srv: TServer; Timer: Boolean);
var
  Host, Port: string;
  I: Integer;
begin
  Tmp_Pos := 915;
  if not Timer then
  begin
    if not IsLogged then Exit;
    if not CheckLevel('', napUserAdmin) then Exit;
    if not CheckParams(1) then Exit;
    if Local = nil then Exit;
    HList.Strings[0] := LowerCase(HList.Strings[0]);
    I := Pos(':', HList.Strings[0]);
    if I = 0 then
    begin
      Host := HList.Strings[0];
      Port := '8888';
    end
    else
    begin
      Host := Copy(HList.Strings[0], 1, I - 1);
      Port := Copy(HList.Strings[0], I + 1, 5);
    end;
    Host := LowerCase(Host);
    Srv := FindServer(Host, True);
    if Local <> Cons then
      if AnsiLowerCase(Local.Nick) <> AnsiLowerCase(Cons_Reg_User) then
        if Restrict_Outgoing then
          if (Srv = nil) or (Srv.Relink = 0) then
          begin
            SplitString(Outgoing_List, HLst);
            if GetIndex(HLst, Host, False) = -1 then
            begin
              Error(GetLangT(LNG_OUTGOINGRESTRICTED, Host));
              Exit;
            end;
          end;
    Tmp_Pos := 916;
    if Srv = nil then
    begin
      Srv := TServer.Create;
      Srv.Host := Host;
      Srv.Port := StrToIntDef(Port, 8888);
      DB_Servers.Add(Srv);
    end;
  end
  else if Srv = nil then Exit;
  Tmp_Pos := 917;
  if Srv.Connected <> conNotConnected then
  begin
    if not Timer then
      Error(GetLangT(LNG_LINKED), True);
    Exit;
  end;
  Tmp_Pos := 918;
  if Timer then
    Wallop(MSG_SERVER_NOSUCH, wallopServer, GetLangT(LNG_LINKREQ2, Srv.Host),
      True)
  else
    Wallop(MSG_SERVER_NOSUCH, wallopServer, GetLangT(LNG_LINKREQ,
      User^.UserName, Host), True);
  Srv.Login_Start := GetTickCount;
  Linking := True;
  Srv.Connect;
end;

procedure Handler_ServerDisconnect;
var
  Srv: TServer;
begin
  Tmp_Pos := 920;
  if not IsLogged then Exit;
  if not CheckLevel('', napUserAdmin) then Exit;
  if not CheckParams(1) then Exit;
  Srv := FindServer(GCmd.Cmd, True);
  Tmp_Pos := 921;
  if Srv = nil then
  begin
    Error(GetLangT(LNG_NOSUCHSERVER), True);
    Exit;
  end;
  if (Srv.Connected <> conConnected) or (Srv.Hub <> nil) then
  begin
    Error(GetLangT(LNG_CANTDELINK, Srv.Host), True);
    Exit;
  end;
  Tmp_Pos := 922;
  if not CheckLag(Srv) then
  begin // Sending disconnect Message
    WriteAllServersEx(Srv, MSG_SRV_SHUTDOWN, '', User^.UserName);
    Srv.Flush;
  end;
  DisconnectServer(Srv, True, False, 'Handler_ServerDisconnect');
  Wallop(MSG_SERVER_NOSUCH, wallopServer, GetLangT(LNG_DELINKREQ, Srv.Host,
    User^.UserName), True);
end;

procedure Handler_KillServer;
var
  Srv: TServer;
begin
  Tmp_Pos := 1259;
  if not IsLogged then Exit;
  if not CheckLevel('', napUserElite) then Exit;
  if not CheckParams(1) then Exit;
  Srv := FindServer(GCmd.Cmd, True);
  if Srv = nil then
  begin
    Error(GetLangT(LNG_NOSUCHSERVER), True);
    Exit;
  end;
  Tmp_Pos := 1260;
  if (Srv.Connected <> conConnected) or (Srv.Hub <> nil) then
  begin
    Error(GetLangT(LNG_CANTKILL, Srv.Host), True);
    Exit;
  end;
  Tmp_Pos := 1261;
  DisconnectServer(Srv, True, False, 'Handler_KillServer');
  Wallop(MSG_SERVER_NOSUCH, wallopServer, GetLangT(LNG_KILLSERVER,
    User^.UserName, Srv.Host), True);
end;

procedure Handler_RemoveServer;
var
  Srv: TServer;
  I: Integer;
begin
  Tmp_Pos := 930;
  if not IsLogged then Exit;
  if not CheckLevel('', napUserElite) then Exit;
  if not CheckParams(1) then Exit;
  Srv := FindServer(GCmd.Cmd, True);
  if Srv = nil then
  begin
    Error(GetLangT(LNG_NOSUCHSERVER), True);
    Exit;
  end;
  Tmp_Pos := 931;
  if Srv.Connected <> conNotConnected then
  begin
    Error(GetLangT(LNG_CANTREMOVE, Srv.Host), True);
    Exit;
  end;
  for I := DB_Servers.Count - 1 downto 0 do
    if DB_Servers.Items[I] = Srv then
    begin
      Wallop(MSG_SERVER_NOSUCH, wallopServer, GetLangT(LNG_REMOVESERVER,
        User^.UserName, Srv.Host), True);
      DB_Servers.Delete(I);
      Srv.Free;
      Exit;
    end;
  Tmp_Pos := 932;
  Error(GetLangT(LNG_NOSUCHSERVER), True);
end;

procedure DisconnectServer(Srv: TServer; Inform_Servers, Do_Wallop: Boolean;
  Sender: string);
var
  I, J: Integer;
  Srv2: TServer;
  User2: POnlineUser;
begin
  Tmp_Pos := 933;
  if Srv = nil then Exit;
  if Srv.Connected = conNotConnected then
  begin
    Srv.ResetData;
    Exit;
  end;
  Tmp_Pos := 934;
  if Srv.Logged then
    if Srv.Hub = nil then
      WriteAllServers(MSG_SRV_DISCONNECTED, '', Srv.Host, GetServerLink(Srv));
  if DB_Servers <> nil then
    for I := 0 to DB_Servers.Count - 1 do
    begin
      Srv2 := DB_Servers.Items[I];
      if Srv2.Hub = Srv then
      begin
        DisconnectServer(Srv2, False, False, 'req-' + Sender);
        Wallop(MSG_SERVER_NOSUCH, wallopServer, GetLangT(LNG_DELINKHUB,
          Srv2.Host, Srv.Host), True);
      end;
    end;
  Tmp_Pos := 935;
  Srv.ResetData;
  Srv.Login_Start := GetTickCount;
  if Srv.Thread <> nil then
  try
    Srv.Thread.Terminate;
    Srv.Thread := nil;
  except
  end;
  Tmp_Pos := 936;
  if Srv.Socket <> INVALID_SOCKET then
  begin
    DoCloseSocket(Srv.Socket);
    Srv.Socket := INVALID_SOCKET;
  end;
  if Srv.Out_List <> nil then
  try
    FreeCmdList(Srv.Out_List);
    Srv.Out_List := nil;
  except
  end;
  Tmp_Pos := 937;
  if Srv.Out_Buf <> nil then
  try
    FreeCmdList(Srv.Out_Buf);
    Srv.Out_Buf := nil;
  except
  end;
  Tmp_Pos := 938;
  Sleep(5);
  for J := 0 to USERS_NAME_ARRAY - 1 do
    for I := DB_Online.Names[J].Count - 1 downto 0 do
    try
      User2 := DB_Online.Names[J].Items[I];
      if User2^.Server = Srv then
        KickUser(User2, '');
      if ((I mod 50) = 0) then
      begin
{$I CheckSync.pas}
      end;
    except
    end;
  Tmp_Pos := 939;
  CountStats;
  if Do_Wallop then
    Wallop(MSG_SERVER_NOSUCH, wallopServer, GetLangT(LNG_DELINK, Srv.Host,
      Sender), True);
end;

procedure Handler_SetLastInv;
var
  S: PNapCmdEx;
  I: Integer;
begin
  if not CheckParams(1) then Exit;
  Local := FindLocalUser(HList.Strings[0]);
  if Local = nil then Exit;
  for I := DB_Invitations.Count - 1 downto 0 do
  begin
    S := DB_Invitations.Items[I];
    if S^.Data = Local.Nick then
      DB_Invitations.Delete(I)
    else if (GetTickCount - S^.Id) > EXPIRE_INVITATION then
      DB_Invitations.Delete(I);
  end;
  DB_Invitations.AddCmd(GetTickCount, NextParamEx(GCmd.Cmd), Local.Nick);
end;

procedure Handler_ForwardAllServers;
var
  Srv, Srv2, Srv3: TServer;
  I, J: Integer;
  Str: string;
  User: POnlineUser;
begin
  Tmp_Pos := 940;
  if not CheckParams(2) then Exit;
  Srv := FindServer(StrToIntDef(HList.Strings[1], 0));
  if Srv = nil then Exit; // Error
  if not Srv.Logged then Exit; // Error
  Tmp_Pos := 941;
  Srv2 := GetServerLink(Srv);
  if Srv2 <> Server then Exit; // Error
  if (Srv2 <> nil) and (Srv.Logged = False) then Exit; // Error
  Tmp_Pos := 942;
  GCmd.Id := StrToIntDef(HList.Strings[0], 0);
  if GCmd.Id = 0 then Exit;
  if Search_Noforward_Requests then
    if GCmd.Id = MSG_CLIENT_SEARCH then
    try
      Tmp_Pos := 943;
      if CheckLag(Server) then // Won't Be able to return Search results
        if HList.Count > 2 then
        begin // Close search
          Tmp_Pos := 944;
          Str := HList.Strings[2]; // User
          User := DB_Online.FindUser(Str);
          Tmp_Pos := 945;
          if User <> nil then
            if User^.Server <> nil then
            begin
              J := CountLinkedServers(Server);
              for I := 1 to J do
                User^.Server.Exec(MSG_SRV_SEARCH_END, User^.UserName);
            end;
          Tmp_Pos := 946;
          Exit;
        end;
      Tmp_Pos := 947;
    except
      on E: Exception do
        DebugLog('Exception in Handler_ForwardAllServers (Pos=' +
          IntToStr(Tmp_Pos) + ') : ' + E.Message);
    end;
  Tmp_Pos := 948;
  Srv3 := GetServerLink(Server);
  Tmp_Pos := 949;
  if Srv3 <> nil then
    for I := 0 to DB_Servers.Count - 1 do
    begin
      Srv2 := DB_Servers.Items[I];
      if Srv2.Logged and (Srv2.Hub = nil) then
        if Srv2 <> Srv3 then
          Srv2.Exec(MSG_SRV_FORWARDALL, GCmd.Cmd);
    end;
  GCmd.Cmd := NextParamEx(GCmd.Cmd, 2);
  ProcessServerCommand(Srv);
end;

procedure CountStats;
var
  I: Integer;
  Srv: TServer;
  Num_Users, Num_Files, Srv_Num, Srv_Direct, Num_Max: Integer;
  Num_Bytes: Int64;
begin
  Num_Users := Local_Users;
  Num_Files := Local_Files;
  Num_Bytes := Local_Bytes;
  Num_Max := Max_Users;
  Srv_Num := 0;
  Srv_Direct := 0;
  Tmp_Pos := 950;
  if DB_Servers <> nil then
    for I := 0 to DB_Servers.Count - 1 do
    begin
      Tmp_Pos := 951;
      Srv := DB_Servers.Items[I];
      Tmp_Pos := 952;
      if Srv.Logged then
      begin
        Inc(Num_Users, Srv.Num_Users);
        Inc(Num_Files, Srv.Num_Files);
        Inc(Num_Bytes, Srv.Num_Bytes);
        Inc(Num_Max, Srv.Max_Users);
        Inc(Srv_Num);
        if Srv.Hub = nil then
          Inc(Srv_Direct);
      end;
    end;
  Tmp_Pos := 953;
  Total_Users := Num_Users;
  Total_Files := Num_Files;
  Total_Bytes := Num_Bytes;
  Total_Users_Limit := Num_Max;
  Num_Servers := Srv_Num;
  Direct_Links := Srv_Direct;
  if Total_Users_Max < Total_Users then
    Total_Users_Max := Total_Users;
  if Total_Files_Max < Total_Files then
    Total_Files_Max := Total_Files;
  if Total_Bytes_Max < Total_Bytes then
    Total_Bytes_Max := Total_Bytes;
end;

procedure Handler_ServerStats;
var
  A, B, C, D: Int64;
begin
  Tmp_Pos := 960;
  if not CheckParams(4) then Exit;
  if Server = nil then Exit;
  A := StrToInt64Def(HList.Strings[0], Server.Num_Users);
  B := StrToInt64Def(HList.Strings[1], Server.Num_Files);
  C := StrToInt64Def(HList.Strings[2], Server.Num_Bytes);
  D := StrToInt64Def(HList.Strings[3], Server.Max_Users);
  Tmp_Pos := 961;
  if (A < 0) or (B < 0) or (C < 0) or (D < 0) then Exit; // Something weird
  Server.Num_Users := A;
  Server.Num_Files := B;
  Server.Num_Bytes := C;
  Server.Max_Users := D;
  Tmp_Pos := 962;
  CountStats;
end;

procedure Handler_SrvRelay;
var
  I, Id, Src, Dst: Integer;
  Srv, Srv2: TServer;
begin
  Tmp_Pos := 970;
  SplitString(GCmd.Cmd, HList);
  if HList.Count < 3 then Exit; // Internal error
  Dst := StrToIntDef(HList.Strings[0], 0);
  Src := StrToIntDef(HList.Strings[1], 0);
  Tmp_Pos := 971;
  if (Dst = 0) or (Src = 0) then Exit; // Internal error
  Id := StrToIntDef(HList.Strings[2], 0);
  GCmd.Cmd := NextParamEx(GCmd.Cmd, 3);
  Tmp_Pos := 972;
  if Dst = MyServerHandle then
  begin
    // Message for This server
    try
      Srv := nil;
      Tmp_Pos := 973;
      for I := 0 to DB_Servers.Count - 1 do
      begin
        Srv2 := DB_Servers.Items[I];
        if Srv2.Server_Handle = Src then
          Srv := Srv2;
      end;
      Tmp_Pos := 974;
      if Srv = nil then Exit;
      if Srv.Connected <> conConnected then Exit;
      Tmp_Pos := 975;
      GCmd.Id := Id;
      ProcessServerCommand(Srv);
    except
      on E: Exception do
        DebugLog('Exception in Handler_SrvRelay (Pos=' + IntToStr(Tmp_Pos) +
          ') : ' + E.Message);
    end;
    Exit;
  end;
  Tmp_Pos := 976;
  for I := 0 to DB_Servers.Count - 1 do
  begin
    Srv2 := DB_Servers.Items[I];
    Tmp_Pos := 977;
    if Srv2.Logged then
      if Srv2.Server_Handle = Dst then
      begin
        Tmp_Pos := 978;
        if Search_Noforward_Results then
          if Id = MSG_SRV_SEARCH_RESULT then // to avoid Too much Traffic
            if CheckLag(GetServerLink(Srv2)) then Exit;
        Tmp_Pos := 979;
        if Browse_Noforward_Results then
          if Id = MSG_CLIENT_RELAY then // do not Return browse Result
            if FirstParam(GCmd.Cmd) = '212' then
              if CheckLag(GetServerLink(Srv2)) then Exit;
        Tmp_Pos := 980;
        Srv2.Relay(Id, GCmd.Cmd, Dst, Src);
        Exit;
      end;
  end;
end;

procedure Handler_SrvRemoteDisconnect;
var
  Srv: TServer;
begin
  Tmp_Pos := 981;
  Srv := FindServer(GCmd.Cmd, True);
  if Srv = nil then Exit;
  Tmp_Pos := 982;
  if Srv.Connected = conConnected then
    DisconnectServer(Srv, False, True, 'Handler_SrvRemoteDisconnect From ' +
      Server.Host);
end;

procedure Handler_ServerSyncChannel;
var
  Ch: TChannel;
  Overwrite: Boolean;
  St: TChannelState;
  I: Integer;
begin
  Tmp_Pos := 990;
  SplitString(GCmd.Cmd, HList);
  if HList.Count < 6 then Exit;
  Overwrite := HList.Strings[1] = '1';
  St := [];
  I := StrToIntDef(HList.Strings[5], 0);
  if (I and 1) > 0 then
    St := St + [chRegistered];
  if (I and 2) > 0 then
    St := St + [chPrivate];
  if (I and 4) > 0 then
    St := St + [chModerated];
  if (I and 8) > 0 then
    St := St + [chTopic];
  Ch := FindChannel(HList.Strings[0]);
  Tmp_Pos := 991;
  if Ch = nil then
  begin // New channel
    Tmp_Pos := 992;
    Ch := TChannel.Create(HList.Strings[0]);
    Ch.Topic := HList.Strings[2];
    Ch.Limit := StrToIntDef(HList.Strings[3], Ch.Limit);
    Ch.Level := Str2Level(HList.Strings[4]);
    Ch.State := St;
    Tmp_Pos := 993;
    DB_Channels.Add(Ch);
    WriteAllServers(GCmd.Id, '', GCmd.Cmd, Server);
    Exit;
  end;
  Tmp_Pos := 994;
  if not overwritE then Exit; // Remote server Should change Its channel Props, not this One
  Ch.SetTopic(HList.Strings[2]);
  Ch.Limit := StrToIntDef(HList.Strings[3], Ch.Limit);
  Ch.Level := Str2Level(HList.Strings[4]);
  Ch.State := St;
end;

procedure Handler_ServerChannelVoice;
var
  User: POnlineUser;
  Ch: TChannel;
  Forward_All: Boolean;
  Voice, Voice2: Boolean;
  Srv: TServer;
begin
  Tmp_Pos := 995;
  SplitString(GCmd.Cmd, HList);
  if HList.Count < 5 then Exit;
  Voice := HList.Strings[3] = '1';
  Forward_All := HList.Strings[4] = '1';
  Ch := FindChannel(HList.Strings[0]);
  Tmp_Pos := 996;
  if Ch = nil then Exit;
  if not (chModerated in Ch.State) then Exit;
  User := DB_Online.FindUser(HList.Strings[1]);
  Tmp_Pos := 997;
  if User = nil then Exit;
  if Ch.FindUser(User) = -1 then Exit;
  Voice2 := StrHash_FindString(Ch.Voices, User^.UserName, True);
  Tmp_Pos := 998;
  if Voice = Voice2 then Exit;
  if not Voice then
  begin // Can speak here, but can't on linked server
    if Forward_All then
      WriteAllServersEx(Server, MSG_SRV_VOICE, '', Ch.Channel + ' ' +
        User^.UserName + ' ' + IntToStr(MyServerHandle) + ' 1 1');
    Exit;
  end;
  Tmp_Pos := 999;
  // Can speak on linked server, but can't speak here
  StrHash_AddEx(Ch.Voices, User^.UserName);
  Tmp_Pos := 1000;
  if User^.Server = nil then
  begin
    Srv := FindServer(StrToIntDef(HList.Strings[2], 0));
    Exec(User, MSG_SERVER_PUBLIC, Ch.Channel + ' Server ' + GetLangT(LNG_VOICE6,
      GetServerAlias(Srv), Ch.Channel));
    Ch.Wallop(GetLangT(LNG_VOICE5, GetServerName(Srv), User^.UserName));
  end;
end;

procedure Handler_ServerChannelOp;
var
  User, User2: POnlineUser;
  Ch: TChannel;
  Forward_All: Boolean;
  Op: Boolean;
  Srv: TServer;
  I: Integer;
begin
  Tmp_Pos := 1001;
  SplitString(GCmd.Cmd, HList);
  if HList.Count < 5 then Exit;
  Op := HList.Strings[3] = '1';
  Forward_All := HList.Strings[4] = '1';
  Ch := FindChannel(HList.Strings[0]);
  Tmp_Pos := 1002;
  if Ch = nil then Exit;
  if not (chModerated in Ch.State) then Exit;
  User := DB_Online.FindUser(HList.Strings[1]);
  if User = nil then Exit;
  Tmp_Pos := 1003;
  if Ch.FindUser(User) = -1 then Exit;
  if Ch.Operator(User) = Op then Exit;
  Tmp_Pos := 1004;
  if not Op then
  begin // Can moderate here, but can't on linked server
    if Forward_All then
      WriteAllServers(MSG_SRV_OP, '', Ch.Channel + ' ' + User^.UserName + ' ' +
        IntToStr(MyServerHandle) + ' 1 0');
    Exit;
  end;
  Tmp_Pos := 1005;
  // Can speak on linked server, but can't speak here
  StrHash_AddEx(Ch.Ops, AnsiLowerCase(User^.UserName));
  Srv := FindServer(StrToIntDef(HList.Strings[2], 0));
  if User^.Server = nil then
    Exec(User, MSG_SERVER_PUBLIC, Ch.Channel + ' Server ' +
      GetLangT(LNG_CHANNELOPERATOR, GetServerAlias(Srv), Ch.Channel));
  Tmp_Pos := 1006;
  for I := 0 to Ch.Users.Count - 1 do
  begin
    User2 := Ch.Users.Items[I];
    if (User2^.Server = nil) and (User2 <> User) then
      Exec(User2, MSG_SERVER_PUBLIC, Ch.Channel + ' Server ' +
        GetLangT(LNG_CHANNELOPERATOR2, GetServerAlias(Srv), User^.UserName,
        Ch.Channel));
  end;
end;

procedure Handler_ServerSyncServer;
var
  Srv, Srv2: TServer;
  I: Integer;
begin
  Tmp_Pos := 1010;
  SplitString(GCmd.Cmd, HList);
  if HList.Count < 8 then Exit;
  HList.Strings[0] := LowerCase(HList.Strings[0]);
  // <server> <port> <version> <handle> <console> <parent_handle> <incoming:1|0>
  Tmp_Pos := 1011;
  Srv := FindServer(HList.Strings[0], False);
  Srv2 := FindServer(StrToIntDef(HList.Strings[5], 0));
  Tmp_Pos := 1012;
  if Srv2 = nil then
  begin
    Wallop(MSG_SERVER_NOSUCH, wallopServer, GetLangT(LNG_INVLINKHANDLE,
      HList.Strings[0], HList.Strings[5]), True);
    Exit;
  end;
  if Srv = nil then
  begin
    Srv := TServer.Create;
    Srv.Host := HList.Strings[0];
    DB_Servers.Add(Srv);
  end;
  Tmp_Pos := 1013;
  I := StrToIntDef(HList.Strings[3], 0);
  Srv.Server_Handle := 0;
  if (FindServer(I) <> nil) or (I = 0) then
  begin
    Wallop(MSG_SERVER_NOSUCH, wallopServer, GetLangT(LNG_LINKINVHANDLE4,
      HList.Strings[0]), True);
    Exit;
  end;
  Tmp_Pos := 1014;
  Srv.ResetData;
  Srv.Port := StrToIntDef(HList.Strings[1], Srv.Port);
  Srv.Version := HList.Strings[2];
  Srv.Server_Handle := I;
  Srv.Console := HList.Strings[4];
  Srv.Hub := Srv2;
  Srv.Incoming := HList.Strings[6] = '1';
  Srv.Reg_User := AnsiLowerCase(HList.Strings[7]);
  Srv.Logged := True;
  Srv.Connected := conConnected;
  Tmp_Pos := 1015;
  Wallop(MSG_SERVER_NOSUCH, wallopServer, GetLangT(LNG_LINKSRVLOGGED3, Srv.Host,
    Srv2.Host), True);
end;

procedure SyncRegisteredUser(Srv: TServer; Reg: PRegisteredUser;
  Write_All: Boolean);
var
  Str: string;
  List: TMyStringList;
begin
  Tmp_Pos := 1020;
  if Reg = nil then Exit;
  if (not Write_All) and (Srv = nil) then Exit; // Possible internal Error
  List := CreateStringList;
  List.Add(Reg^.Nick);
  List.Add(Reg^.Password);
  List.Add(IntToStr(Ord(Reg^.Level)));
  List.Add(IntToStr(Reg^.Downloads));
  List.Add(IntToStr(Reg^.Uploads));
  List.Add(IntToStr(Reg^.Last_Ip));
  List.Add(IntToStr(Reg^.Last_Seen));
  List.Add(IntToStr(UserState2Int(Reg^.State)));
  Tmp_Pos := 1021;
  Str := JoinString(List);
  FreeStringList(List);
  Tmp_Pos := 1022;
  if Write_All then
  begin
    if Srv = nil then
      WriteAllServers(MSG_SRV_SYNCREGISTERED, '', Str) // Send to Everyone
    else
      WriteAllServersEx(Srv, MSG_SRV_SYNCREGISTERED, '', Str);
        // Send to Srv and all Linked to It
  end
  else
    Srv.Exec(MSG_SRV_SYNCREGISTERED, Str); // Send to Srv Only
end;

procedure Handler_ClearServerRegistrations;
var
  I: Integer;
begin
  for I := 0 to USERS_NAME_ARRAY - 1 do
    DB_Registered.List[I].Clear;
end;

procedure Handler_SyncServerRegistrations;
var
  Reg: TRegisteredUser;
begin
  SplitString(GCmd.Cmd, HList);
  if HList.Count < 8 then Exit;
  Reg.Nick := HList.Strings[0];
  if DB_Registered.FindUser(Reg.Nick) <> nil then
    DB_Registered.Delete(Reg.Nick);
  Reg.Password := HList.Strings[1];
  Reg.Level := Str2Level(HList.Strings[2]);
  Reg.Downloads := StrToIntDef(HList.Strings[3], 0);
  Reg.Uploads := StrToIntDef(HList.Strings[4], 0);
  Reg.Last_Ip := StrToInt64Def(HList.Strings[5], 0);
  Reg.Last_Seen := StrToIntDef(HList.Strings[6], 946702800);
  Reg.State := Int2UserState(StrToIntDef(HList.Strings[7], 0), False);
  Reg.Created := StrToIntDef(HList.Strings[8], 946702800);
  Reg.CreatedBy := HList.Strings[9];
  Reg.LastSetBy := HList.Strings[10];
  DB_Registered.Add(Reg);
end;

procedure Handler_SyncRegistered;
var
  Reg: PRegisteredUser;
  R: TRegisteredUser;
  User: POnlineUser;
  Loc: TLocalUser;
begin
  Tmp_Pos := 1025;
  SplitString(GCmd.Cmd, HList);
  if HList.Count < 8 then Exit;
  R.Nick := HList.Strings[0];
  R.Password := HList.Strings[1];
  R.Level := Str2Level(HList.Strings[2]);
  R.Downloads := StrToIntDef(HList.Strings[3], 0);
  R.Uploads := StrToIntDef(HList.Strings[4], 0);
  R.Last_Ip := StrToInt64Def(HList.Strings[5], 0);
  R.Last_Seen := StrToIntDef(HList.Strings[6], 0);
  R.State := Int2UserState(StrToIntDef(HList.Strings[7], 0), False);
  Tmp_Pos := 1026;
  Reg := DB_Registered.FindUser(R.Nick);
  if Reg = nil then
  begin
    Tmp_Pos := 1027;
    if (R.Level <> napUserUser) or (userMuzzled in R.State) or
      Accept_Remote_Users then
      DB_Registered.Add(R);
  end
  else
  begin
    Tmp_Pos := 1028;
    if Reg^.Password <> R.Password then
    begin // Accounts don't Match - Kick oldest One
      Tmp_Pos := 1029;
      if R.Last_Seen < Reg^.Last_Seen then
      begin // Kick remote One
        Tmp_Pos := 1030;
        SyncRegisteredUser(nil, Reg, True);
        Exit;
      end
      else if R.Last_Seen > Reg^.Last_Seen then
      begin // Kick local
        Tmp_Pos := 1031;
        DB_Registered.Delete(R.Nick);
        DB_Registered.Add(R);
        Tmp_Pos := 1032;
        Reg := DB_Registered.FindUser(R.Nick);
        User := DB_Online.FindUser(R.Nick);
        Tmp_Pos := 1033;
        if (User <> nil) and (User^.Server = nil) then
          if User^.Level <> napUserConsole then
          begin
            Tmp_Pos := 1034;
            Loc := User^.Local;
            if Loc <> nil then
            begin
              Tmp_Pos := 1035;
              Loc.Exec(MSG_SERVER_NOSUCH, GetLangT(LNG_KICKMSG2, Server.Host));
              //         if User^.Level < napUserModerator then
              //          AddReconnector(Decode_Ip(Loc.Ip));
              DisconnectUser(Loc, '', '', 'Handler_SyncRegistered', False);
            end;
            Tmp_Pos := 1036;
            if Reg <> nil then
              SyncRegisteredUser(nil, Reg, True);
          end;
      end;
      Exit;
    end
    else
    begin // Passwords match. Same user
      Tmp_Pos := 1037;
      if R.Last_Seen > Reg^.Last_Seen then
      begin // Update record
        Tmp_Pos := 1038;
        DB_Registered.Delete(R.Nick);
        DB_Registered.Add(R);
        // Delete next Line? Yann zzz
              // Reg := DB_Registered.FindUser(R.Nick);
      end
      else if R.Last_Seen < Reg^.Last_Seen then
      begin // Update remote record
        Tmp_Pos := 1039;
        SyncRegisteredUser(nil, Reg, True);
        Exit;
      end;
    end;
  end;
  Tmp_Pos := 1040;
  User := DB_Online.FindUser(R.Nick);
  if User = nil then Exit;
  if User^.Server <> nil then Exit;
  Loc := User^.Local;
  Tmp_Pos := 1041;
  if Loc = nil then Exit;
  if Loc.Data^.Password <> R.Password then
  begin
    if Loc <> Cons then
    begin
      Tmp_Pos := 1042;
      Loc.Exec(MSG_SERVER_NOSUCH, GetLangT(LNG_KICKMSG2,
        GetServerAlias(Server)));
      if User^.Level < napUserModerator then
        AddReconnector(Decode_Ip(Loc.Ip));
      DisconnectUser(Loc, '', '', 'Handler_SyncRegistered2', False);
    end;
    Exit;
  end;
  Tmp_Pos := 1043;
  if Loc.Level < R.Level then
  begin // Updating level
    Tmp_Pos := 1044;
    Loc.Data^.Level := napUserUser;
    Loc.Exec(MSG_SERVER_NOSUCH, GetLangT(LNG_LEVEL, GetServerName(Server),
      Level2Str(R.Level), IntToStr(Ord(R.Level))));
    Wallop(MSG_SERVER_NOSUCH, wallopLevel, GetLangT(LNG_LEVEL1,
      GetServerName(Server), Loc.Nick, Level2Str(R.Level),
      IntToStr(Ord(R.Level))), False);
    Loc.Data^.Level := R.Level;
  end;
  Tmp_Pos := 1045;
  if (userMuzzled in R.State) <> (userMuzzled in Loc.Data^.State) then
  begin
    if userMuzzled in R.State then
      Loc.Data^.State := Loc.Data^.State + [userMuzzled]
    else
      Loc.Data^.State := Loc.Data^.State - [userMuzzled];
  end;
  Tmp_Pos := 1046;
  CompleteSyncUser(nil, Loc.Data);
end;

procedure Handler_SyncUser(public_Message: Boolean);
var
  User2: POnlineUser;
  Usr: TOnlineUser;
  Srv2: TServer;
  St: TUserState;
  I: Integer;
  L: TLocalUser;
  Reg: PRegisteredUser;
  Str1, Tempalias: string;
  B: PBan;
begin
  Tmp_Pos := 1050; // Extensive debug in this Handler
  SplitString(GCmd.Cmd, HList);
  Tmp_Pos := 1051;
  if HList.Count < 17 then Exit; // Invalid arguments
  Tmp_Pos := 1052;
  Srv2 := FindServer(StrToIntDef(HList.Strings[14], 0));
  if Srv2 = nil then Exit;
  Tmp_Pos := 1053;
  User2 := DB_Online.FindUser(HList.Strings[0]);
  St := [];
  I := StrToIntDef(HList.Strings[15], 0);
  Tmp_Pos := 1054;
  if (I and 1) <> 0 then
    St := St + [userMuzzled];
  if (I and 2) <> 0 then
    St := St + [userCloaked];
  Tmp_Pos := 1055;
  if User2 <> nil then
  begin
    Tmp_Pos := 1056;
    if User2^.Server <> Srv2 then
    begin // User already Exists
      Tmp_Pos := 1057;
      if User2^.Server = nil then
      begin
        Tmp_Pos := 1058;
        L := User2^.Local;
        if L = nil then Exit; // Weird error
        Tmp_Pos := 1059;
        Tempalias := GetServerAlias(Srv2);
        L.Exec(MSG_SERVER_NOSUCH, GetLangT(LNG_KICKMSG2, Tempalias));
        //       if User2^.Level < napUserModerator then
        //        AddReconnector(Decode_Ip(L.Ip));
        DisconnectUser(L, '', GetLangT(LNG_SERVERGHOST, User2^.UserName,
          User2^.Software), 'Handler_SyncUser', False);
      end
      else
      begin
        Tmp_Pos := 1060;
        WriteAllServers(MSG_SRV_USEROFFLINE, User2^.UserName, '');
          // Kick user on all servers
        KickUser(User2, 'SyncUser');
      end;
      Exit;
    end;
    Tmp_Pos := 1061;
    with User2^ do
    begin
      Tmp_Pos := 1062;
      Password := HList.Strings[1];
      Software := HList.Strings[2];
      Level := Str2Level(HList.Strings[3]);
      Ip := StrToInt64Def(HList.Strings[4], 0);
      DataPort := StrToIntDef(HList.Strings[5], 0);
      Total_Up := StrToIntDef(HList.Strings[6], 0);
      Total_Down := StrToIntDef(HList.Strings[7], 0);
      Uploads := StrToIntDef(HList.Strings[8], 0);
      Downloads := StrToIntDef(HList.Strings[9], 0);
      Max_Up := StrToIntDef(HList.Strings[10], -1);
      Queue := StrToIntDef(HList.Strings[11], -1);
      Speed := Str2Speed(HList.Strings[12]);
      Shared := StrToIntDef(HList.Strings[13], 0);
      Server := Srv2;
      State := State - [userMuzzled, userCloaked] + St;
      Last_Seen_T := StrToInt64Def(HList.Strings[16], 0);
    end;
    Tmp_Pos := 1063;
  end
  else
  begin
    Tmp_Pos := 1064;
    with Usr do
    begin
      UserName := HList.Strings[0];
      NameCrc := StringCRC(UserName, True);
      Password := HList.Strings[1];
      Software := HList.Strings[2];
      Level := Str2Level(HList.Strings[3]);
      Ip := StrToInt64Def(HList.Strings[4], 0);
      DataPort := StrToIntDef(HList.Strings[5], 0);
      Total_Up := StrToIntDef(HList.Strings[6], 0);
      Total_Down := StrToIntDef(HList.Strings[7], 0);
      Uploads := StrToIntDef(HList.Strings[8], 0);
      Downloads := StrToIntDef(HList.Strings[9], 0);
      Max_Up := StrToIntDef(HList.Strings[10], -1);
      Queue := StrToIntDef(HList.Strings[11], -1);
      Speed := Str2Speed(HList.Strings[12]);
      Shared := StrToIntDef(HList.Strings[13], 0);
      Server := Srv2;
      State := St;
      Last_Seen_T := StrToInt64Def(HList.Strings[16], 0);
      Local := nil;
    end;
    Tmp_Pos := 1065;
    if Usr.Level < napUserModerator then
      if DB_Bans.Banned(Usr.UserName, Decode_Ip(Usr.Ip), B) then
        if B <> nil then
        begin // Banned user
          Tmp_Pos := 1066;
          WriteAllServers(MSG_SRV_USEROFFLINE, Usr.UserName, '');
            // Kick user on all servers
          WriteAllServers(MSG_SRV_REMOTEBANKICK, '', JoinBan(B^.User, B^.Ip) +
            ' ' + AddStr(B^.Admin) + ' ' + IntToStr(B^.Time) + ' ' +
            IntToStr(B^.Expires) + ' ' + B^.Reason);
          Exit;
        end;
    Tmp_Pos := 1067;
    DB_Online.AddUser(Usr);
    Tmp_Pos := 1068;
    for I := 0 to DB_Local.Count - 1 do
    begin
      L := DB_Local.Items[I];
      Tmp_Pos := 1069;
      Str1 := StrHash_FindStringEx(L.Hotlist, Usr.UserName, True);
      Tmp_Pos := 1071;
      if Str1 <> '' then
        L.Exec(MSG_SERVER_USER_SIGNON, Str1 + ' ' + IntToStr(Ord(Usr.Speed)));
    end;
    Tmp_Pos := 1072;
    // Checking registered Users
    Reg := DB_Registered.FindUser(Usr.UserName);
    Tmp_Pos := 1073;
    if Reg <> nil then
      if (Usr.Password <> Reg^.Password) or (Usr.Level <> Reg^.Level) then
        SyncRegisteredUser(Usr.Server, Reg, False);
  end;
  Tmp_Pos := 1074;
  if not public_Message then
    WriteAllServers(GCmd.Id, '', GCmd.Cmd, Server);
end;

procedure Handler_UpdateUser;
var
  User: POnlineUser;
begin
  Tmp_Pos := 1080;
  SplitString(GCmd.Cmd, HList);
  if HList.Count < 9 then Exit;
  Tmp_Pos := 1081;
  User := DB_Online.FindUser(HList.Strings[0]);
  if User = nil then Exit;
  Tmp_Pos := 1082;
  User^.Total_Up := StrToIntDef(HList.Strings[1], User^.Total_Up);
  User^.Total_Down := StrToIntDef(HList.Strings[2], User^.Total_Down);
  User^.Uploads := StrToIntDef(HList.Strings[3], User^.Uploads);
  User^.Downloads := StrToIntDef(HList.Strings[4], User^.Downloads);
  User^.Max_Up := StrToIntDef(HList.Strings[5], User^.Max_Up);
  User^.Queue := StrToIntDef(HList.Strings[6], User^.Queue);
  User^.Speed := Str2Speed(HList.Strings[7]);
  User^.Shared := StrToIntDef(HList.Strings[8], User^.Shared);
end;

procedure Handler_RemoteBanEx;
begin
  Tmp_Pos := 1083;
  SplitString(GCmd.Cmd, HList);
  Tmp_Pos := 1084;
  if HList.Count < 5 then Exit;
  BanUser(HList.Strings[0], HList.Strings[1], StrToIntDef(HList.Strings[2], 0),
    HList.Strings[3], False);
end;

procedure Handler_RemoteBan;
var
  B: TBan;
begin
  Tmp_Pos := 1085;
  SplitString(GCmd.Cmd, HList);
  Tmp_Pos := 1086;
  if HList.Count < 4 then Exit;
  SplitBan(HList.Strings[0], B.User, B.Ip);
  if DB_Bans.FindRec(B.User, B.Ip) <> -1 then Exit;
  B.Admin := HList.Strings[1];
  B.Time := StrToIntDef(HList.Strings[2], 0);
  Tmp_Pos := 1087;
  if B.Time = 0 then Exit;
  B.Expires := StrToIntDef(HList.Strings[3], 0);
  if B.Expires <> 0 then
    if (B.Expires < GetTickCountT) then Exit; // Expired
  Tmp_Pos := 1088;
  B.Reason := NextParamEx(GCmd.Cmd, 4);
  Tmp_Pos := 1089;
  DB_Bans.Add(B);
end;

procedure Handler_ClearServerBans;
begin
  DB_Bans.Clear;
end;

procedure Handler_ClearBlockList;
begin
  FreeBlocks;
  DB_Blocks := TMyList.Create;
end;

procedure Handler_SyncServerBan;
var
  B: TBan;
begin
  SplitString(GCmd.Cmd, HList);
  SplitBan(HList.Strings[0], B.User, B.Ip);
  if DB_Bans.FindRec(B.User, B.Ip) <> -1 then Exit;
  B.Admin := HList.Strings[1];
  B.Time := StrToIntDef(HList.Strings[2], 0);
  B.Expires := StrToIntDef(HList.Strings[3], 0);
  B.LastAttempt := StrToIntDef(HList.Strings[4], 0);
  B.Using := HList.Strings[5];
  B.Tries := StrToIntDef(HList.Strings[6], 0);
  B.Reason := NextParamEx(GCmd.Cmd, 7);
  DB_Bans.Add(B);
end;

procedure Handler_SyncServerBlockList;
var
  Hash, H2: TNapCmdList;
  I, J, K: Integer;
begin
  Tmp_Pos := 8813;
  Hash := TNapCmdList.Create;
  if SplitToKeywords(AnsiLowerCase(GCmd.Cmd) + ' ', Hash, MAX_FILE_KEYWORDS) = 0
    then
  begin
    Hash.Free;
    Exit;
  end;
  Tmp_Pos := 8814;
  if Hash.Count > 0 then
  begin
    for I := 0 to DB_Blocks.Count - 1 do
    begin
      H2 := DB_Blocks.Items[I];
      if H2.Count = Hash.Count then
      begin
        K := 0;
          // comparing crc of every keyword (much faster than to compare all keywords)
        for J := 0 to Hash.Count - 1 do
          if K = 0 then
            if H2.FindItem(PNapCmd(Hash.Items[J]).Id, PNapCmd(Hash.Items[J]).Cmd)
              = -1 then
              K := 1;
        if K = 0 then
          // all keywords from 'Hash' are present in 'H2' (it doesn't guarantee
          // that all keywords from 'H2' are present in 'Hash'
          // because there might be duplicate keywords)
        begin
          Hash.Free;
          Exit;
        end;
      end;
    end;
    DB_Blocks.Add(Hash);
  end
  else
    Hash.Free;
  Tmp_Pos := 8815;
end;

procedure Handler_RemoteUserKick;
var
  User2: POnlineUser;
  L: TLocalUser;
begin
  Tmp_Pos := 1090;
  User2 := DB_Online.FindUser(GCmd.Cmd);
  if User2 = nil then Exit;
  Tmp_Pos := 1091;
  if User2^.Server = nil then
  begin
    Tmp_Pos := 1092;
    L := User2^.Local;
    if L = nil then Exit;
    Tmp_Pos := 1093;
    L.Exec(MSG_SERVER_NOSUCH, GetLangT(LNG_KICKMSG2, GetServerAlias(Server)));
    if User2^.Level < napUserModerator then
      AddReconnector(Decode_Ip(L.Ip));
    DisconnectUser(L, '', GetLangT(LNG_SERVERGHOST, User2^.UserName,
      User2^.Software), 'Handler_SyncUser', False)
  end
  else
    KickUser(User2, 'RemoteUserKick');
end;

procedure SyncServers(Srv, Srv2: TServer);
var
  I: Integer;
  Srv3: TServer;
  Str: string;
begin
  Tmp_Pos := 1100;
  if Srv2.TrueStats = True then
    Str := '1'
  else
    Str := '0';
  WriteAllServersEx(Srv, MSG_SRV_SYNCSRV, '', Srv2.Host + ' ' +
    IntToStr(Srv2.Port) + ' ' + AddStr(Srv2.Version) + ' ' +
    IntToStr(Srv2.Server_Handle) + ' ' + AddStr(Srv2.Console) + ' ' +
    IntToStr(GetServerHandle(Srv2.Hub)) + ' ' + IntToStr(Ord(Srv2.Incoming)) + ' '
    + AddStr(Srv2.Reg_User));
  WriteAllServersEx(Srv, MSG_SRV_ALIAS, '', Srv2.Host + ' ' + AddStr(Srv2.Alias)
    + ' ' + AddStr(Str));
  Tmp_Pos := 1101;
  for I := 0 to DB_Servers.Count - 1 do
  begin
    Srv3 := DB_Servers.Items[I];
    if Srv3.Logged then
      if Srv3.Hub = Srv2 then
        SyncServers(Srv, Srv3);
  end;
end;

procedure CompleteSyncUser(Srv: TServer; User: POnlineUser);
var
  Str: string;
  List: TMyStringList;
  I: Integer;
begin
  Tmp_Pos := 1102;
  List := CreateStringList;
  List.Add(User^.UserName);
  List.Add(User^.Password);
  List.Add(User^.Software);
  List.Add(IntToStr(Ord(User^.Level)));
  List.Add(IntToStr(User^.Ip));
  List.Add(IntToStr(User^.DataPort));
  List.Add(IntToStr(User^.Total_Up));
  List.Add(IntToStr(User^.Total_Down));
  List.Add(IntToStr(User^.Uploads));
  List.Add(IntToStr(User^.Downloads));
  List.Add(IntToStr(User^.Max_Up));
  List.Add(IntToStr(User^.Queue));
  List.Add(IntToStr(Ord(User^.Speed)));
  List.Add(IntToStr(User^.Shared));
  List.Add(IntToStr(GetServerHandle(User^.Server)));
  I := 0;
  if userMuzzled in User^.State then
    Inc(I, 1);
  if userCloaked in User^.State then
    Inc(I, 2);
  List.Add(IntToStr(I));
  List.Add(IntToStr(User^.Last_Seen_T));
  Tmp_Pos := 1103;
  Str := JoinString(List);
  if Srv <> nil then
    Srv.Exec(MSG_SRV_SYNCUSER, Str)
  else
    WriteAllServers(MSG_SRV_PUBLICSYNCUSER, '', Str);
  Tmp_Pos := 1104;
  FreeStringList(List);
end;

procedure UpdateUser(User: POnlineUser; Ignored: TServer = nil);
var
  Str: string;
  List: TMyStringList;
begin
  Tmp_Pos := 1105;
  List := CreateStringList;
  List.Add(User^.UserName);
  List.Add(IntToStr(User^.Total_Up));
  List.Add(IntToStr(User^.Total_Down));
  List.Add(IntToStr(User^.Uploads));
  List.Add(IntToStr(User^.Downloads));
  List.Add(IntToStr(User^.Max_Up));
  List.Add(IntToStr(User^.Queue));
  List.Add(IntToStr(Ord(User^.Speed)));
  List.Add(IntToStr(User^.Shared));
  Tmp_Pos := 1106;
  Str := JoinString(List);
  FreeStringList(List);
  Tmp_Pos := 1107;
  WriteAllServers(MSG_SRV_UPDATEUSER, '', Str, Ignored);
end;

procedure CompleteSyncServer(Srv: TServer; N: Integer);
var
  Ch: TChannel;
  I, J, K, Num: Integer;
  Str: string;
  Srv2: TServer;
  User2: POnlineUser;
  Local2: TLocalUser;
  Ban: PBan;
  Reg: PRegisteredUser;
  List: TMyStringList;
  Hash: TNapCmdList;
begin
  Tmp_Pos := 1110;
  Linking := True;
  // Sync all Linked servers
  if True_Stats = True then
    Str := '1'
  else
    Str := '0';
  WriteAllServers(MSG_SRV_SYNCSRV, '', Srv.Host + ' ' + IntToStr(Srv.Port) + ' '
    + AddStr(Srv.Version) + ' ' + IntToStr(Srv.Server_Handle) + ' ' +
    AddStr(Srv.Console) + ' ' + IntToStr(MyServerHandle) + ' ' +
    IntToStr(Ord(Srv.Incoming)) + ' ' + AddStr(Srv.Reg_User), Srv);
  WriteAllServers(MSG_SRV_ALIAS, '', ServerName_T + ' ' + AddStr(ServerAlias) +
    ' ' + AddStr(Str));
  Srv.Lag := True;
  Srv.Login_Start := GetTickCount;
  for I := 0 to DB_Servers.Count - 1 do
  begin
    Tmp_Pos := 1111;
    Srv2 := DB_Servers.Items[I];
    if Srv2 <> Srv then
      if Srv2.Logged then
        if Srv2.Hub = nil then
          SyncServers(Srv, Srv2);
  end;
  Tmp_Pos := 1112;
  K := 0;
{$I CheckSync.pas}
  Srv.Flush;
  Tmp_Pos := 1113;
  if Srv.Connected <> conConnected then Exit;
  // Sync all Bans
  if Hub_Syncban then
    if Srv.Incoming then
    begin
      WriteAllServersEx(Srv, MSG_SRV_SERVERBANCLEAR, '', '');
      for I := 0 to DB_Bans.Count - 1 do
      begin
        Ban := DB_Bans.Items[I];
        Str := '';
        // Str := JoinBan(Ban^.User, Ban^.Ip) + ' ' + AddStr(Ban^.Admin) +
        //   ' ' + IntToStr(Ban^.Time) + ' ' + IntToStr(Ban^.Expires) + ' ' +
        //   AddStr(Ban^.Reason) + ' ' + IntToStr(Ban^.LastAttempt) + ' ' +
        //   AddStr(Ban^.Using) + ' ' + IntToStr(Ban^.Tries);
        Str := JoinBan(Ban^.User, Ban^.Ip) + ' ' + AddStr(Ban^.Admin) + ' ' +
          IntToStr(Ban^.Time) + ' ' + IntToStr(Ban^.Expires) + ' ' +
          IntToStr(Ban^.LastAttempt) + ' ' + AddStr(Ban^.Using) + ' ' +
          IntToStr(Ban^.Tries) + ' ' + AddStr(Ban^.Reason);
        WriteAllServersEx(Srv, MSG_SRV_SERVERBANSYNC, '', Str);
        if ((I mod 30) = 20) then
        begin
{$I CheckSync.pas}
        end;
      end;
    end;
  if Hub_SyncBlock then
    if Srv.Incoming then
    begin
      WriteAllServersEx(Srv, MSG_SRV_BLOCKLISTCLEAR, '', '');
      for I := 0 to DB_Blocks.Count - 1 do
      begin
        Hash := DB_Blocks.Items[I];
        Str := '';
        for J := 0 to Hash.Count - 1 do
          Str := Str + PNapCmd(Hash.Items[J])^.Cmd + ' ';
        WriteAllServersEx(Srv, MSG_SRV_BLOCKLISTSYNC, '', Str);
        if ((I mod 30) = 20) then
        begin
{$I CheckSync.pas}
        end;
      end;
    end;
  if Hub_Syncreg then
    if Srv.Incoming then
    begin
      WriteAllServersEx(Srv, MSG_SRV_REGISTEREDCLEAR, '', '');
      List := CreateStringList;
      for I := 0 to USERS_NAME_ARRAY - 1 do
        for J := 0 to DB_Registered.List[I].Count - 1 do
        begin
          Reg := DB_Registered.List[I].Items[J];
          List.Clear;
          List.Add(Reg^.Nick);
          List.Add(Reg^.Password);
          List.Add(IntToStr(Ord(Reg^.Level)));
          List.Add(IntToStr(Reg^.Downloads));
          List.Add(IntToStr(Reg^.Uploads));
          List.Add(IntToStr(Reg^.Last_Ip));
          List.Add(IntToStr(Reg^.Last_Seen));
          List.Add(IntToStr(UserState2Int(Reg^.State)));
          List.Add(IntToStr(Reg^.Created));
          List.Add(Reg^.CreatedBy);
          List.Add(Reg^.LastSetBy);
          Str := JoinString(List);
          WriteAllServersEx(Srv, MSG_SRV_REGISTEREDSYNC, '', Str);
          if ((J mod 20) = 0) then
          begin
{$I CheckSync.pas}
          end;
        end;
      FreeStringList(List);
    end;
  // Sync all Users
  Num := 0;
  Sleep(5);
  for J := 0 to USERS_NAME_ARRAY - 1 do
    for I := 0 to DB_Online.Names[J].Count - 1 do
    begin
      if ((I mod 50) = 1) then
      begin
{$I CheckSync.pas}
      end;
      Tmp_Pos := 1114;
      User2 := DB_Online.Names[J].Items[I];
      CompleteSyncUser(Srv, User2);
      Tmp_Pos := 1115;
      if User2^.Server = nil then
      begin
        Local2 := User2^.Local;
        Tmp_Pos := 1116;
        if Local2 <> nil then
          if locNeedsUpdate in Local2.LocalState then
          begin
            UpdateUser(User2, Srv);
            Local2.LocalState := Local2.LocalState - [locNeedsUpdate];
          end;
      end;
      Tmp_Pos := 1117;
      Inc(Num);
      if ((Num div 50) = 30) then
      begin
{$I CheckSync.pas}
      end;
      if (Num div 100) = 0 then
      begin
        Tmp_Pos := 1118;
        Srv.Flush;
        if Srv.Connected <> conConnected then Exit;
      end;
    end;
  Tmp_Pos := 1119;
{$I CheckSync.pas}
  Srv.Flush;
  if Srv.Connected <> conConnected then Exit;
  // Sync all Channels
  Tmp_Pos := 1120;
  if DB_Channels <> nil then
    for I := 0 to DB_Channels.Count - 1 do
    begin
      if ((I mod 10) = 0) then
      begin
{$I CheckSync.pas}
      end;
      Tmp_Pos := 1121;
      Ch := DB_Channels.Items[I];
      J := 0;
      if chRegistered in Ch.State then
        Inc(J, 1);
      if chPrivate in Ch.State then
        Inc(J, 2);
      if chModerated in Ch.State then
        Inc(J, 4);
      if chTopic in Ch.State then
        Inc(J, 8);
      Str := Ch.Channel + ' ' + IntToStr(N) + ' ' + AddStr(Ch.Topic) + ' ' +
        IntToStr(Ch.Limit) + ' ' +
        IntToStr(Ord(Ch.Level)) + ' ' + IntToStr(J);
      Tmp_Pos := 1122;
      Srv.Exec(MSG_SRV_SYNCCH, Str);
      for J := 0 to Ch.Users.Count - 1 do
      begin
        Tmp_Pos := 1123;
        User2 := Ch.Users.Items[J];
        WriteAllServersEx(Srv, MSG_CLIENT_JOIN, User2^.UserName, Ch.Channel);
        Tmp_Pos := 1124;
        if User2^.Level < napUserModerator then
        begin
          if Ch.Operator(User2) then
            WriteAllServersEx(Srv, MSG_SRV_OP, '', Ch.Channel + ' ' +
              User2^.UserName + ' ' + IntToStr(MyServerHandle) + ' 1 1')
          else if (chModerated in Ch.State) and StrHash_FindString(Ch.Voices,
            User2^.UserName, True) then
            Srv.Exec(MSG_SRV_VOICE, Ch.Channel + ' ' + User2^.UserName + ' ' +
              IntToStr(MyServerHandle) + ' 1 1');
        end;
        Tmp_Pos := 1125;
        Inc(K);
        if ((K div 10) = 0) then
        begin
{$I CheckSync.pas}
        end;
      end;
    end;
  Tmp_Pos := 1126;
  Srv.Flush;
  if Srv.Connected <> conConnected then Exit;
  Tmp_Pos := 1127;
  WriteAllServers(MSG_SERVER_STATS, '', IntToStr(Local_Users) + ' ' +
    IntToStr(Local_Files) + ' ' + IntToStr(Local_Bytes) + ' ' +
    IntToStr(Max_Users));
  Srv.Lag := True;
end;

procedure Handler_LinkServer;
var
  R_Reg, R_Ip, R_Host, R_Version, R_Console, R_Port: string;
  R_Handle, I: Integer;
  Srv: TServer;
  S: PNapCmdEx;
  Sin: TSockAddrIn;
begin // Appears after Server authentication
  Tmp_Pos := 1130;
  if Query <> queryNormal then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  if (Local = nil) or (Local.Data <> nil) then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  if not (locSwapBytes in Local.LocalState) then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  if Local.Searchespm <> 999 then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  Tmp_Pos := 1131;
  Sin := TCPSocket_GetRemoteSin(Local.Socket);
  R_Ip := Decode_Ip(Sin.Sin_Addr.S_Addr);
  HList.Clear;
  for I := DB_Invitations.Count - 1 downto 0 do
  begin
    S := DB_Invitations.Items[I];
    if S^.Data = R_Ip then
    begin
      if HList.Count = 0 then
        SplitString(S^.Cmd, HList);
    end;
  end;
  if HList.Count < 9 then
  begin
    DebugLog('Possible Error in Handler_LinkServer: HList.Count = ' +
      IntToStr(HList.Count));
    Exit; // Internal error or command not found in database
  end;
  Tmp_Pos := 1132;
  // HList: <host> <port> <version> <net_build> <handle> <console>
  R_Host := LowerCase(HList.Strings[0]);
  R_Port := HList.Strings[1];
  R_Version := HList.Strings[2];
  R_Handle := StrToIntDef(HList.Strings[4], 0);
  R_Console := HList.Strings[5];
  R_Reg := HList.Strings[6];
  Srv := FindServer(R_Host, False);
  Tmp_Pos := 1133;
  if Srv = nil then
  begin
    Tmp_Pos := 1134;
    Srv := TServer.Create;
    DB_Servers.Add(Srv);
    Srv.Host := R_Host;
  end
  else
  begin
    Tmp_Pos := 1135;
    if Srv.Connected <> conNotConnected then
    begin
      DisconnectUser(Local, '', '', 'Handler_LinkServer', False);
      Exit;
    end;
  end;
  Tmp_Pos := 1136;
  Srv.Port := StrToIntDef(R_Port, 8888);
  Srv.Hub := nil;
  Srv.Connected := conConnected;
  Srv.Socket := Local.Socket;
  Srv.Login_Start := Current_Time;
  if not Sockets_Servers_default then
  begin
    TCPSocket_SetSizeRecvBuffer(Srv.Socket, Sockets_Servers_Recv);
    TCPSocket_SetSizeSendBuffer(Srv.Socket, Sockets_Servers_Send);
  end;
  Tmp_Pos := 1137;
  Local.Socket := INVALID_SOCKET;
  DisconnectUser(Local, '', '', 'Handler_LinkServer', True);
  Srv.Logged := True;
  Srv.Incoming := True;
  Srv.Version := R_Version;
  Srv.Console := R_Console;
  Srv.Num_Users := 0;
  Srv.Num_Files := 0;
  Srv.Max_Users := 0;
  Srv.Num_Bytes := 0;
  Srv.Server_Handle := R_Handle;
  Srv.Reg_User := AnsiLowerCase(R_Reg);
  Tmp_Pos := 1138;
  Srv.Exec(MSG_SRV_LOGIN_ACK, AddStr(SLAVANAP_FULL) + ' ' +
    IntToStr(MyServerHandle) + ' ' + Cons.Nick + ' ' + AddStr(Cons_Reg_User));
  // Srv.Exec(MSG_SRV_ALIAS, AddStr(ServerName_T) + ' ' + AddStr(ServerAlias));
  Srv.Compile;
  Srv.Flush;
  Tmp_Pos := 1139;
  if Srv.Connected <> conConnected then Exit;
  Srv.Logged := True;
  CompleteSyncServer(Srv, 1);
end;

procedure Handler_ServerCheckPass;
begin
  Server.Exec(MSG_SRV_CHECKPASS2, StrMD5(GCmd.Cmd + Server.MyPassword));
end;

procedure Handler_ServerLoginError;
begin
  Wallop(MSG_SERVER_NOSUCH, wallopServer, GetLangT(LNG_LINKLOGINERR,
    Server.Host, GCmd.Cmd), True);
  DisconnectServer(Server, False, False, 'Handler_ServerLoginError');
end;

procedure Handler_ServerLoginAck;
var
  User2: POnlineUser;
begin
  Tmp_Pos := 1140;
  SplitString(GCmd.Cmd, HList);
  if HList.Count < 4 then Exit;
  Tmp_Pos := 1141;
  Server.Logged := True;
  Server.Version := HList.Strings[0];
  Server.Server_Handle := StrToIntDef(HList.Strings[1], 0);
  Server.Console := HList.Strings[2];
  Server.Reg_User := AnsiLowerCase(HList.Strings[3]);
  User2 := DB_Online.FindUser(Server.Console);
  Tmp_Pos := 1142;
  if User2 <> nil then
  begin
    if User2^.Level = napUserConsole then
    begin
      Tmp_Pos := 1143;
      DisconnectServer(Server, False, False, 'Handler_ServerLoginAck');
      Wallop(MSG_SERVER_NOSUCH, wallopServer, GetLangT(LNG_LINKINVCONSOLE,
        Server.Host, User2^.UserName), True);
      Exit;
    end
    else
      KickUser(User2, 'ServerLoginAck');
  end;
  Tmp_pOs := 1144;
  // Wallop(MSG_SERVER_NOSUCH, wallopServer, GetLangT(LNG_LINKSRVLOGGED, Server.Host), True);
  Wallop(MSG_SERVER_NOSUCH, wallopServer, GetLangT(LNG_LINKCONNECTED,
    Server.Host), True);
  CompleteSyncServer(Server, 0);
end;

procedure Handler_ServerCheckPass2;
var
  R_Ip, Str: string;
  Srv: TServer;
  S: PNapCmdEx;
  Sin: TSockAddrIn;
  I: Integer;
begin
  Tmp_Pos := 1150;
  if Query <> queryNormal then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  if (Local = nil) or (Local.Data <> nil) then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  if not (locSwapBytes in Local.LocalState) then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  if Local.Searchespm <> 999 then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  Tmp_Pos := 1151;
  Sin := TCPSocket_GetRemoteSin(Local.Socket);
  R_Ip := Decode_Ip(Sin.Sin_Addr.S_Addr);
  HList.Clear;
  for I := DB_Invitations.Count - 1 downto 0 do
  begin
    S := DB_Invitations.Items[I];
    if S^.Data = R_Ip then
    begin
      if HList.Count = 0 then
        SplitString(S^.Cmd, HList);
    end;
  end;
  if HList.Count < 6 then
  begin
    DebugLog('Possible Error in Handler_ServerCheckPass2: HList.Count = ' +
      IntToStr(HList.Count));
    Exit; // Internal error or command not found in database
  end;
  Str := LowerCase(HList.Strings[0]);
  Srv := FindServer(Str, False);
  if Srv = nil then Exit; // Check pass Cannot be Executed with Unknown servers
  Tmp_Pos := 1152;
  Str := StrMD5(IntToStr(Local.Last_Search_Time) + Srv.RemotePassword);
  if Str <> GCmd.Cmd then
  begin
    Tmp_Pos := 1153;
    LoginError(GetLangT(LNG_LINKINVPASS));
    Wallop(MSG_SERVER_ERROR, wallopServer, GetLangT(LNG_LINKINVPASS2, Srv.Host),
      True);
    Exit;
  end;
  Wallop(MSG_SERVER_NOSUCH, wallopServer, GetLangT(LNG_LINKSRVLOGGED2,
    Srv.Host), True);
  Server := Srv;
  Tmp_Pos := 1154;
  Handler_LinkServer;
end;

procedure Handler_ServerLogin;
var
  R_Host, R_Version, R_Console, R_Port, R_Net, R_AllHandles, R_AllConsole,
    R_Reg: string;
  R_Handle, I, J, K: Integer;
  Srv, Srv2: TServer;
  B: Boolean;
  R_Ip: string;
  Sin: TSockAddrIn;
  S: PNapCmdEx;
  Usr: POnlineUser;
begin
  Tmp_Pos := 1160; // Extensive debug
  R_Host := RS_Handler_Unknown;
  R_Version := RS_Handler_Unknown;
  R_Port := '8888';
  R_Net := '0';
  R_Ip := Decode_Ip(TCPSocket_GetRemoteSin(Local.Socket).Sin_Addr.S_Addr);
  Tmp_Pos := 1161;
  // Format: <host> <port> <version> <net_build> <handle> <console>
  if Query <> queryNormal then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  Tmp_Pos := 1162;
  if (Local = nil) or (Local.Data <> nil) then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  Tmp_Pos := 1163;
  if not (locSwapBytes in Local.LocalState) then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  Tmp_Pos := 1164;
  SplitString(GCmd.Cmd, HList);
  if HList.Count > 5 then
    R_Net := HList.Strings[3];
  Tmp_Pos := 1165;
  if R_Net <> NET_BUILD then
  begin
    if HList.Count > 0 then
      R_Host := LowerCase(HList.Strings[0]);
    if HList.Count > 1 then
      R_Port := HList.Strings[1];
    if HList.Count > 2 then
      R_Version := HList.Strings[2];
    Tmp_Pos := 1166;
    LoginError(GetLangT(LNG_LINKBADVERSION, SLAVANAP_FULL));
    Wallop(MSG_SERVER_NOSUCH, wallopServer, GetLangT(LNG_LINKBADVERSION2,
      R_Host, R_Ip, R_Version), True);
    Exit;
  end;
  Tmp_Pos := 1167;
  // Format: <host> <port> <version> <net_build> <handle> <console>
  R_Host := LowerCase(HList.Strings[0]);
  R_Port := HList.Strings[1];
  R_Version := HList.Strings[2];
  R_Net := HList.Strings[3];
  R_Handle := StrToIntDef(HList.Strings[4], 0);
  R_Console := HList.Strings[5];
  R_Reg := HList.Strings[6];
  R_AllHandles := HList.Strings[7];
  R_AllConsole := HList.Strings[8];
  Tmp_Pos := 1168;
  if Max_Servers_Enabled then // Limit Total number of server Links
    if Direct_Links >= Max_Servers then
    begin
      LoginError(GetLangT(LNG_LINKMAXSERVERS));
      Wallop(MSG_SERVER_NOSUCH, wallopServer, GetLangT(LNG_LINKMAXSERVERS2,
        R_Host, R_Ip, Max_Servers), True);
      Exit;
    end;
  if Allow_Link = linkNone then // Linking disabled
  begin
    LoginError(GetLangT(LNG_LINKNOLINK));
    Wallop(MSG_SERVER_NOSUCH, wallopServer, GetLangT(LNG_LINKNOLINK2, R_Host,
      R_Ip), True);
    Exit;
  end;
  if Allow_Link = linkCustom then
  begin
    SplitString(Allowed_Servers, HLst);
    if GetIndex(HLst, R_Host, True) = -1 then
    begin
      LoginError(GetLangT(LNG_LINKNOLIST1));
      Wallop(MSG_SERVER_NOSUCH, wallopServer, GetLangT(LNG_LINKNOLIST3, R_Host,
        R_Ip), True);
      Exit;
    end;
  end;
  Tmp_Pos := 1169;
  Srv := FindServer(R_Host, False);
  Tmp_Pos := 1170;
  if Srv = nil then
    if Allow_Link = linkList then // Unknown server
    begin
      Tmp_Pos := 1171;
      LoginError(GetLangT(LNG_LINKNOLIST));
      Wallop(MSG_SERVER_NOSUCH, wallopServer, GetLangT(LNG_LINKNOLIST2, R_Host,
        R_Ip), True);
      Exit;
    end;
  Tmp_Pos := 1172;
  if (Srv <> nil) and Srv.Logged then // Already Logged
  begin
    Tmp_Pos := 1173;
    LoginError(GetLangT(LNG_LINKLOGGED));
    Wallop(MSG_SERVER_NOSUCH, wallopServer, GetLangT(LNG_LINKLOGGED2, R_Host,
      R_Ip), True);
    Exit;
  end;
  Tmp_Pos := 1174;
  if R_Handle < 1 then // Invalid handle
  begin
    Tmp_Pos := 1175;
    LoginError(GetLangT(LNG_LINKINVHANDLE));
    Wallop(MSG_SERVER_NOSUCH, wallopServer, GetLangT(LNG_LINKINVHANDLE2, R_Host,
      R_Ip), True);
    Exit;
  end;
  SplitString(R_AllHandles, HLst);
  for I := 0 to HLst.Count - 1 do
  begin
    J := StrToIntDef(HLst.Strings[I], 0);
    if J = MyServerHandle then
    begin
      LoginError(GetLangT(LNG_LINKINVHANDLE3, HLst.Strings[I], ServerName_T));
      Wallop(MSG_SERVER_NOSUCH, wallopServer, GetLangT(LNG_LINKINVHANDLE2,
        R_Host, R_Ip), True);
      Exit;
    end;
    for K := 0 to DB_Servers.Count - 1 do
    begin
      Srv2 := DB_Servers.Items[K];
      if Srv2.Logged then
        if Srv2.Server_Handle = J then
        begin
          LoginError(GetLangT(LNG_LINKINVHANDLE3, HLst.Strings[I], Srv2.Host));
          Wallop(MSG_SERVER_NOSUCH, wallopServer, GetLangT(LNG_LINKINVHANDLE2,
            R_Host, R_Ip), True);
          Exit;
        end;
    end;
  end;
  SplitString(R_AllConsole, HLst);
  for I := 0 to HLst.Count - 1 do
  begin
    Usr := DB_Online.FindUser(HLst.Strings[I]);
    if Usr <> nil then
    begin
      if Usr^.Level = napUserConsole then
      begin
        LoginError(GetLangT(LNG_LINKINVCONSOLE2, Usr^.UserName));
        Wallop(MSG_SERVER_NOSUCH, wallopServer, GetLangT(LNG_LINKINVCONSOLE3,
          R_Host, R_Ip), True);
        Exit;
      end;
      KickUser(Usr, 'ServerLogin');
    end;
  end;
  Tmp_Pos := 1176;
  Srv2 := FindServer(R_Handle);
  Tmp_Pos := 1177;
  if (Srv2 <> nil) and (Srv2.Connected <> conNotConnected) then
    // Handle already Used
  begin
    Tmp_Pos := 1178;
    LoginError(GetLangT(LNG_LINKINVHANDLE3, R_Handle, Srv2.Host));
    Wallop(MSG_SERVER_NOSUCH, wallopServer, GetLangT(LNG_LINKINVHANDLE2, R_Host,
      R_Ip), True);
    Exit;
  end;
  Tmp_Pos := 1179;
  if Srv <> nil then
    if (Srv.Connected = conConnecting) or ((Srv.Connected = conConnected) and
      (Srv.Logged = False)) then
    try // Already connecting
      LoginError(GetLangT(LNG_LINKLOGGING));
      Wallop(MSG_SERVER_NOSUCH, wallopServer, GetLangT(LNG_LINKLOGGING2, R_Host,
        R_Ip), True);
      DisconnectServer(Srv, False, False, 'Handler_ServerLogin');
      Exit;
    except
      Exit;
    end;
  Tmp_Pos := 1180;
  Sin := TCPSocket_GetRemoteSin(Local.Socket);
  R_Ip := Decode_Ip(Sin.Sin_Addr.S_Addr);
  for I := DB_Invitations.Count - 1 downto 0 do
  begin
    S := DB_Invitations.Items[I];
    if S^.Data = R_Ip then
      DB_Invitations.Delete(I)
    else if (GetTickCount - S^.Id) > EXPIRE_INVITATION then
      DB_Invitations.Delete(I);
  end;
  DB_Invitations.AddCmd(GetTickCount, GCmd.Cmd, R_Ip);
  Local.Searchespm := 999;
  Tmp_Pos := 1181;
  if (Srv = nil) or (Srv.Authentication = authResolve) then
  begin // Authentication via Resolving Host Name
    Tmp_Pos := 1182;
    HList.Clear;
    ResolveNameToIP(R_Host, HList);
    B := False;
    Tmp_Pos := 1183;
    for I := 0 to HList.Count - 1 do
      if HList.Strings[I] = R_Ip then
        B := True;
    Tmp_Pos := 1184;
    if not B then
    begin // Invalid IP
      Tmp_Pos := 1185;
      LoginError(GetLangT(LNG_LINKINVIP));
      Wallop(MSG_SERVER_NOSUCH, wallopServer, GetLangT(LNG_LINKINVIP2, R_Host,
        R_Ip), True);
      Exit;
    end;
    Tmp_Pos := 1186;
    Local.Last_Search_Time := 0;
  end
  else
  begin // Authentication via Password
    Tmp_Pos := 1187;
    Local.Last_Search_Time := Random(65535);
    Local.Exec(MSG_SRV_CHECKPASS, IntToStr(Local.Last_Search_Time));
    Exit;
  end;
  Tmp_Pos := 1188;
  Wallop(MSG_SERVER_NOSUCH, wallopServer, GetLangT(LNG_LINKSRVLOGGED2, R_Host),
    True);
  Tmp_Pos := 1189;
  Server := Srv;
  Handler_LinkServer;
end;

procedure Handler_RemoteWallop;
var
  I, J: Integer;
begin
  SplitString(GCmd.Cmd, HList);
  if HList.Count < 3 then Exit;
  I := StrToIntDef(HList.Strings[0], 0);
  if I < 1 then Exit;
  J := StrToIntDef(HList.Strings[1], -1);
  if J = -1 then Exit;
  Wallop(I, TWallopType(J), NextParamEx(GCmd.Cmd, 2), True);
end;

procedure Handler_ServerShutDown;
begin
  if Server.Logged then
  begin
    if GCmd.Cmd = '' then
      Wallop(MSG_SERVER_NOSUCH, wallopServer, GetLangT(LNG_DELINKREQ,
        Server.Host, Server.Console), True)
    else
      Wallop(MSG_SERVER_NOSUCH, wallopServer, GetLangT(LNG_DELINKREQ,
        Server.Host, GCmd.Cmd), True);
    DisconnectServer(Server, True, False, 'Handler_ServerShutDown');
  end;
end;

procedure Handler_PingAll;
var
  Srv2: TServer;
  I: Integer;
  Str: string;
begin
  if Query = queryServer then
  begin
    if not CheckParams(2) then Exit;
    I := StrToIntDef(HList.Strings[0], 0);
    if I = MyServerHandle then
    begin
      I := StrToIntDef(HList.Strings[1], 0);
      if I = 0 then Exit;
      I := GetTickCount - I;
      if I < 0 then Exit;
      if HList.Count = 5 then
        if HList.Strings[2] = 'pingall' then
        begin
          // <handle> <time> "pingall" "user" "channel"
          User := DB_Online.FindUser(HList.Strings[3]);
          if User = nil then Exit;
          if User^.Server <> nil then Exit;
          if HList.Strings[4] <> '' then
          begin
            Str := Format(RS_Handler_SPong, [HList.Strings[4], Server.Host,
              IntToStrDot(I)]);
            Exec(User, MSG_SERVER_PUBLIC, Str);
          end
          else
          begin
            Str := Format(RS_Handler_SPong2, [Server.Host, IntToStrDot(I)]);
            Exec(User, MSG_SERVER_NOSUCH, Str);
          end;
          Exit;
        end;
      Wallop(MSG_SERVER_NOSUCH, wallopPing, Format(RS_Handler_WallopSPong,
        [Server.Host, I]), True);
      Exit;
    end;
    if I = Server.Server_Handle then
      Server.Exec(GCmd.Id, GCmd.Cmd);
    Exit;
  end;
  if not CheckLevel('', napUserModerator) then Exit;
  if Num_Servers < 1 then
  begin
    Error(RS_Handler_PingNoLink);
    Exit;
  end;
  Wallop(MSG_SERVER_NOSUCH, wallopPing, RS_Handler_WallopPing, True);
  Str := IntToStr(MyServerHandle) + ' ' + IntToStr(GetTickCount);
  for I := 0 to DB_Servers.Count - 1 do
  begin
    Srv2 := DB_Servers.Items[I];
    if Srv2.Logged then
      Srv2.Exec(GCmd.Id, Str);
  end;
end;

procedure Handler_ServerPing;
var
  Srv: TServer;
begin
  if Query = queryRemoteUser then
    Exec(User, GCmd.Id, GCmd.Cmd)
  else
  begin
    if not CheckParams(1) then Exit;
    if (LowerCase(HList.Strings[0]) = LowerCase(ServerName_T)) or
      (LowerCase(HList.Strings[0]) = LowerCase(ServerAlias)) then
    begin
      Exec(User, GCmd.Id, GCmd.Cmd);
      Exit;
    end;
    Srv := FindServer(HList.Strings[0], True);
    if Srv = nil then
    begin
      if IsDigit(HList.Strings[0]) then
        Exec(User, GCmd.Id, GCmd.Cmd)
      else
        Error(RS_Handler_PingNoSuch, True);
    end
    else
      Srv.Exec(GCmd.Id, User^.UserName + ' ' + GCmd.Cmd);
  end;
end;

procedure Handler_Decompress;
var
  I: Integer;
  Buf: string;
  Srv: TServer;
begin
  Tmp_Pos := 1190;
  Srv := Server;
  I := -1;
  try
    Buf := ZDecompressStr(GCmd.Cmd);
{$I CheckSync.pas}
  except
    on E: Exception do
    begin
      DebugLog('Exception in decompressing Data : ' + E.Message);
      Exit;
    end;
  end;
  Tmp_Pos := 1191;
  try
    while Length(Buf) > 3 do
    begin
      Tmp_Pos := 1192;
      I := Ord(Buf[1]) * 256 + Ord(Buf[2]);
      GCmd.Id := Ord(Buf[3]) * 256 + Ord(Buf[4]);
      Tmp_Pos := 1193;
      if I > (Length(Buf) - 4) then
      begin
        Tmp_Pos := 1194;
        DebugLog('Handler_Decompress : Invalid command Length');
        Compressed := False;
        Tmp_Pos := 1195;
        SetLength(Buf, 0);
        Exit;
      end;
      Tmp_Pos := 1196;
      SetLength(GCmd.Cmd, I);
      Tmp_Pos := 1197;
      if I > 0 then
        Move(Buf[5], GCmd.Cmd[1], I);
      Move(Buf[I + 5], Buf[1], Length(Buf) - I - 4);
      Tmp_Pos := 1198;
      SetLength(Buf, Length(Buf) - I - 4);
      try
        Compressed := True;
        Tmp_Pos := 1199;
        ProcessServerCommand(Srv);
        Tmp_Pos := 1203;
        Compressed := False;
      except
        on E: Exception do
          DebugLog('Exception in Handler_Decompress (Pos=' + IntToStr(Tmp_Pos) +
            ') : ' + E.Message);
      end;
      Tmp_Pos := 1200;
    end;
    Tmp_Pos := 1201;
    Compressed := False;
    SetLength(Buf, 0);
    Tmp_Pos := 1202;
  except
    on E: Exception do
      DebugLog('Exception in Handler_Decompress (2) Posm=' + IntToStr(Tmp_Pos) +
        ' I=' + IntToStr(I) + ' Bufsize=' + IntToStr(Length(Buf)) + ' : ' +
        E.Message);
  end;
end;

procedure Handler_AliasList;
var
  I, Num: Integer;
  Srv: TServer;
begin
  if not CheckLevel('', napUserModerator) then Exit;
  Error(Servername_T + '  '#9 + ServerAlias);
  if Num_Servers = 0 then
  begin
    Error(GetLangT(LNG_ITEMSLISTED, '1'));
    Exit;
  end;
  Num := 1;
  for I := 0 to DB_Servers.Count - 1 do
  begin
    Srv := DB_Servers.Items[I];
    if Srv.Logged then
    begin
      Error(Srv.Host + '  '#9 + Srv.Alias);
      Inc(Num);
    end;
  end;
  Error(GetLangT(LNG_ITEMSLISTED, IntToStr(Num)));
end;

procedure Handler_ServerAlias;
var
  Server: TServer;
begin
  Tmp_Pos := 7654;
  SplitString(GCmd.Cmd, HList);
  if HList.Count < 1 then Exit;
  Server := FindServer(HList.Strings[0], False);
  if Server = nil then Exit;
  if HList.Count < 2 then
    HList.Add(HList.Strings[0]);
  Server.Alias := HList.Strings[1];
  Server.TrueStats := False;
  if HList.Count > 2 then
    if HList.Strings[2] = '1' then
      Server.TrueStats := True;
end;

procedure Handler_UpdateBan;
var
  Ban: PBan;
begin
  Tmp_Pos := 7655;
  SplitString(GCmd.Cmd, HList);
  if HList.Count < 1 then Exit;
  if DB_Bans.Banned(HList.Strings[0], HList.Strings[1], Ban) then
  begin
    Inc(Ban^.Tries);
    Ban^.LastAttempt := GetTickCountT;
    if Ban^.Ip = '*' then
      Ban^.Using := HList.Strings[1]
    else
      Ban^.Using := HList.Strings[0];
  end;
end;

procedure Handler_AllowedList;
var
  Str: string;
  Num: Integer;
begin
  Tmp_Pos := 7656;
  if User^.Level < napUserAdmin then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  Num := 0;
  SplitString(Outgoing_List, HList);
  while HList.Count > 0 do
  begin
    Str := HList.Strings[0];
    HList.Delete(0);
    Inc(Num);
    Error(Str);
  end;
  if Query = queryNormal then
    Local.Exec(GCmd.Id, GCmd.Cmd)
  else if Num = 0 then
    Error('.')
  else
    Error(GetLangT(LNG_ITEMSLISTED, IntToStr(Num)));
end;

procedure Handler_ListBlockedClients;
var
  Num, I: Integer;
begin
  Tmp_Pos := 7657;
  if not CheckLevel('', napUserModerator) then Exit;
  Num := 0;
  for I := 0 to softUnknown do
    if Blocked_Clients[I] then
    begin
      Error(Blocked_Clients_Desc[I]);
      Inc(Num);
    end;
  for I := 0 to Max_Custom_Block do
    if Blocked_Custom[I] <> '' then
    begin
      Error(Blocked_Custom[I]);
      Inc(Num);
    end;
  if Query = queryNormal then
    Local.Exec(GCmd.Id, GCmd.Cmd)
  else if Num = 0 then
    Error('.')
  else
    Error(GetLangT(LNG_ITEMSLISTED, IntToStr(Num)));
end;

procedure Handler_SetAllServerPasswords;
var
  I: Integer;
  Srv: TServer;
begin
  Tmp_Pos := 7666;
  if not IsLogged then Exit;
  if not CheckLevel('', napUserConsole) then Exit;
  if not CheckParams(1) then Exit;
  if Local = nil then Exit;
  for I := 0 to DB_Servers.Count - 1 do
  begin
    Srv := DB_Servers.Items[I];
    Srv.MyPassword := HList.Strings[0];
  end;
  Error(GetLangT(LNG_SRV_PASSWORDSRESET, HList.Strings[0]));
end;

procedure Handler_Reboot;
begin
  if not Remote_Admin_Ok then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  if not CheckLevel('', napUserElite) then
  begin
    PermissionDenied('', True);
    Exit;
  end;
  SplitString(GCmd.Cmd, HList);
  if HList.Count < 1 then Exit;
  if HList.Strings[0] <> Remote_Adminpass then Exit;
  ExitWindowsEx(EWX_FORCE + EWX_REBOOT, 0)
end;

procedure Handler_AskRemoteConfig;
var
  Server: TServer;
  Str: string;
  I: Integer;
begin
  Tmp_Pos := 8655;
  if not IsLogged then Exit;
  if not CheckLevel('', napUserAdmin) then Exit;
  SplitString(GCmd.Cmd, HList);
  if HList.Count < 1 then Exit;
  Server := FindServer(HList.Strings[0], False);
  if (Server = nil) or (Server.Connected <> conConnected) then
  begin
    Error(GetLangT(LNG_OFFLINE3, HList.Strings[0]));
    Exit;
  end;
  Str := User.UserName + ' ';
  if Query = queryNormal then
    Str := Str + '1 '
  else
    Str := Str + Query_Channel + ' ';
  for I := 1 to HList.Count - 1 do
    Str := Str + HList.Strings[I] + ' ';
  Server.Exec(MSG_SRV_REMOTE_VAR_REQUEST, Str);
end;

procedure Handler_AskAllConfig;
var
  // Server: TServer;
  Str: string;
  I: Integer;
begin
  Tmp_Pos := 8656;
  if not IsLogged then Exit;
  if not CheckLevel('', napUserAdmin) then Exit;
  SplitString(GCmd.Cmd, HList);
  if HList.Count < 1 then Exit;
  Str := User.UserName + ' ';
  if Query = queryNormal then
    Str := Str + '1 '
  else
    Str := Str + Query_Channel + ' ';
  for I := 0 to HList.Count - 1 do
    Str := Str + HList.Strings[I] + ' ';
  WriteAllServers(MSG_SRV_REMOTE_VAR_REQUEST, '', Str);
  Tmp_Pos := 8656;
end;

procedure Handler_SetRemoteConfig;
begin
  Tmp_Pos := 8654;
  SplitString(GCmd.Cmd, HList);
  if HList.Count < 2 then Exit;
  User := DB_Online.FindUser(HList.Strings[0]);
  if User = nil then Exit;
  GCmd.Cmd := NextParamEx(GCmd.Cmd, 1);
  SetConfigRemote(User, GCmd.Cmd);
end;

procedure Handler_ShowRemoteConfig;
begin
  Tmp_Pos := 8657;
  SplitString(GCmd.Cmd, HList);
  if HList.Count < 1 then Exit;
  User := DB_Online.FindUser(HList.Strings[0]);
  if User = nil then Exit;
  GCmd.Cmd := NextParamEx(GCmd.Cmd);
  if HList.Strings[1] = '1' then
  begin
    GCmd.Cmd := NextParamEx(GCmd.Cmd);
    Exec(User, MSG_SERVER_NOSUCH, GCmd.Cmd);
  end
  else
    Exec(User, MSG_SERVER_PUBLIC, GCmd.Cmd);
end;

function ProcessCommand(Usr: TLocalUser; Q: TQuery = queryNormal): Boolean;
var
  Str1: string;
begin
  Result := True;
  Tmp_Pos := 1;
  if not Running then Exit;
  if Q <> queryRemoteUser then
  begin
    if Usr = nil then Exit;
    Local := Usr;
    if locWriteOnly in Local.LocalState then
    begin
      Result := False;
      Exit;
    end;
    if Local.Last_Seen = 0 then
    begin
      Result := False;
      Exit;
    end;
    if GCmd.Id <> 214 then
      Local.Last_Command_Time := GetTickCount;
    User := Usr.Data;
  end;
  Tmp_Pos := 2;
  Query := Q;
  try
    if Log_Commands then
      if Query = queryNormal then
      begin
        Str1 := GCmd.Cmd;
        if (GCmd.Id = 2) or (GCmd.Id = 6) then
        begin
          SplitString(Str1, HList);
          if HList.Count > 1 then
          begin
            HList.Strings[1] := '*****';
            Str1 := JoinString(HList);
          end;
        end;
        if GCmd.Id = 205 then
        begin
          SplitString(Str1, HList);
          if HList.Count > 2 then
            if AnsiLowerCase(HList.Strings[0]) = 'nickserv' then
              if LowerCase(HList.Strings[1]) = 'pass' then
              begin
                HList.Strings[2] := '*****';
                Str1 := JoinString(HList);
              end;
        end;
        Str1 := RS_Handler_ReceiveCommand + ' [' + IntToStr(GCmd.Id) + '] "' +
          Str1 + '" (';
        if User <> nil then
          Str1 := Str1 + User^.UserName + ', ' + User^.Software + ', ' +
            Decode_Ip(Local.Ip) + ')'
        else if Local <> nil then
          Str1 := Str1 + Decode_Ip(Local.Ip) + ')'
        else
          Str1 := Str1 + RS_Handler_IPUnknown + ')';
        Log(0, Str1, True);
      end;
    Tmp_Pos := 3;
    case GCmd.Id of
      MSG_CLIENT_LOGIN: Handler_Login; // 2 - Login attempt
      MSG_CLIENT_VERSION_CHECK: Handler_Echo; // 4 - Version check
      MSG_CLIENT_LOGIN_REGISTER: Handler_LoginRegister; // 6 - Register attempt
      MSG_CLIENT_REGISTER: Handler_CheckNick; // 7 - Check nick
      MSG_CLIENT_CHECK_PASS: Handler_PassCheck; // 11 - Check password
      MSG_CLIENT_ADD_FILE: Handler_Share('', False); // 100 - Share file
      MSG_CLIENT_REMOVE_FILE: Handler_Unshare(GCmd.Cmd); // 102 - Unshare file
      MSG_CLIENT_UNSHARE_ALL: Handler_Unshare; // 110 - Unshare all
      MSG_CLIENT_SEARCH: Handler_Search; // 200 - Search request
      MSG_CLIENT_DOWNLOAD: Handler_Download; // 203 - Download request
      MSG_CLIENT_PRIVMSG: Handler_PrivateMessage; // 205 - private message
      MSG_CLIENT_ADD_HOTLIST,
        MSG_CLIENT_ADD_HOTLIST_SEQ: Handler_HotList; // 207, 208 - Add to Hotlist
      MSG_CLIENT_BROWSE: Handler_Browse; // 211 - Browse files
      MSG_SERVER_STATS: Handler_Stats; // 214 - Server stats
      MSG_CLIENT_RESUME_REQUEST: Handler_Resume; // 215 - Resume search
      MSG_CLIENT_DOWNLOAD_START..MSG_CLIENT_UPLOAD_END: Handler_Transfer;
        // 218..221 - Start/end Download/upload
      MSG_CLIENT_REMOVE_HOTLIST: Handler_HotList; // 303 - Remove from Hot list
      MSG_CLIENT_IGNORE_LIST: Handler_IgnoreList; // 320 - Ignore list
      MSG_CLIENT_IGNORE_USER: Handler_IgnoreList; // 322 - Ignore user
      MSG_CLIENT_UNIGNORE_USER: Handler_IgnoreList; // 323 - Unignore user
      MSG_CLIENT_CLEAR_IGNORE: Handler_IgnoreList; // 326 - Clear ignore List
      MSG_CLIENT_JOIN: Handler_JoinChannel; // 400 - Join channel
      MSG_CLIENT_PART: Handler_PartChannel; // 401 - Part channel
      MSG_CLIENT_PUBLIC, MSG_SERVER_PUBLIC: Handler_public;
        // 402, 403 - public message
      MSG_SERVER_TOPIC: Handler_ChannelTopic;
        // 410 - Change/request Channel topic
      MSG_CLIENT_CHANNEL_BAN_LIST: Handler_ChanBanList;
        // 420 - List bans for channel
      MSG_CLIENT_CHANNEL_BAN: Handler_ChanBan; // 422 - Ban user in channel
      MSG_CLIENT_CHANNEL_UNBAN: Handler_ChanUnban; // 423 - Unban user in channel
      MSG_CLIENT_CHANNEL_CLEAR_BANS: Handler_ChanBanClear;
        // 424 - Clear channel Bans
      MSG_CLIENT_CHANNEL_INVITE: Handler_ChannelInvite;
        // 430 - Invite user to channel
      MSG_CLIENT_CHANNEL_INVITE_DECLINE: Handler_ChannelDeclineInvite;
        // 432 - Invitation declined
      MSG_CLIENT_DOWNLOAD_FIREWALL: Handler_DownloadFirewall;
        // 500 - Download thru Firewall
      MSG_CLIENT_USERSPEED: Handler_GetData; // 600 - Get user's Speed
      MSG_CLIENT_WHOIS: Handler_Whois; // 603 - Whois request
      MSG_CLIENT_SETUSERLEVEL: Handler_SetUserLevel; // 606 - Change user Level
      MSG_CLIENT_UPLOAD_OK: Handler_Upload; // 608 - Upload accepted
      MSG_SERVER_UPLOAD_FAILED: Handler_Upload;
        // 609 - Upload failed (Used by AudioGnome)
      MSG_CLIENT_KILL: Handler_Kill; // 610 - Kill user
      MSG_CLIENT_NUKE: Handler_Nuke; // 611 - Nuke user
      MSG_CLIENT_BAN: Handler_Ban; // 612 - Ban user
      MSG_CLIENT_ALTER_PORT: Handler_AlterPort; // 613 - Alter port
      MSG_CLIENT_UNBAN: Handler_Unban; // 614 - Unban user
      MSG_CLIENT_BANLIST: Handler_Banlist; // 615 - List bans
      MSG_CLIENT_LIST_CHANNELS: Handler_ListChannels; // 617 - List channels
      MSG_CLIENT_LIMIT: Handler_Queue; // 619 - Queue limit
      MSG_CLIENT_MOTD: if (GetTickCount - Usr.Last_Seen) > 30000 then
          Handler_Motd; // 621 - Show motd
      MSG_CLIENT_MUZZLE: Handler_Muzzle; // 622 - Muzzle user
      MSG_CLIENT_UNMUZZLE: Handler_Unmuzzle; // 623 - Unmuzzle user
      MSG_CLIENT_ALTER_SPEED: Handler_AlterSpeed; // 625 - Alter speed
      MSG_CLIENT_DATA_PORT_ERROR: Handler_Relay; // 626 - Data port Error
      MSG_CLIENT_WALLOP: Handler_Wallop; // 627 - Wallop
      MSG_CLIENT_ANNOUNCE: Handler_Announce; // 628 - Global message
      MSG_CLIENT_BROWSE_DIRECT: Handler_DirectBrowse;
        // 640 - Direct browse Request
      MSG_SERVER_BROWSE_DIRECT_OK: Handler_DirectBrowseOK;
        // 641 - Directo browse Accepted
      MSG_CLIENT_CLOAK: Handler_Cloak; // 652 - Cloak
      MSG_CLIENT_CHANGE_SPEED: Handler_SetSpeed; // 700 - Change speed
      MSG_CLIENT_CHANGE_PASS: Handler_SetPass; // 701 - Change password
      MSG_CLIENT_CHANGE_DATA_PORT: Handler_SetDataPort; // 703 - Change data Port
      MSG_CLIENT_PING_SERVER: Handler_ServerPing; // 750 - Ping server
      MSG_CLIENT_PING: Handler_Relay2; // 751 - User ping
      MSG_CLIENT_PONG: Handler_Pong; // 752 - User pong
      MSG_CLIENT_ALTER_PASS: Handler_AlterPass; // 753 - Change password
      MSG_CLIENT_SERVER_RECONFIG: Handler_Restart; // 800 - Reconfig
      MSG_CLIENT_SERVER_VERSION: Handler_ServerVersion; // 801 - Server version
      MSG_CLIENT_SERVER_CONFIG: Handler_SetConfig; // 810 - Change server Config
      MSG_CLIENT_CLEAR_CHANNEL: Handler_ClearChannel; // 820 - Clear channel
      MSG_CLIENT_REDIRECT: Handler_Redirect;
        // 821 - Redirect user to another Server
      MSG_CLIENT_CYCLE: Handler_Redirect; // 822 - Redirect user to metaserver
      MSG_CLIENT_SET_CHAN_LEVEL: Handler_ChannelLevel;
        // 823 - set channels Level
      MSG_CLIENT_EMOTE: Handler_Emote; // 824 - Emote
      MSG_CLIENT_CHANNEL_LIMIT: Handler_ChannelLimit; // 826 - Channel limit
      MSG_CLIENT_FULL_CHANNEL_LIST: Handler_ListAllChannels;
        // 827 - List all Channels
      MSG_CLIENT_KICK: Handler_Kick; // 829 - Kick user From channel
      MSG_CLIENT_NAMES_LIST: Handler_ChannelUsersList;
        // 830 - List users in channel
      MSG_CLIENT_GLOBAL_USER_LIST: Handler_UserList; // 831 - List all Users
      MSG_CLIENT_ADD_DIRECTORY: Handler_ShareDir; // 870 - Share lots of files
      MSG_SRV_LOGIN: Handler_ServerLogin; // 8000 - Server login
      MSG_SRV_CHECKPASS2: Handler_ServerCheckPass2;
        // 8006 - Server authentication Reply
      MSG_SRV_ALIAS: Handler_ServerAlias; // 8029 Server alias
      //   MSG_CLIENT_RELAY                   : Handler_RelayMessage; // 8100 - Relay message to another User
      //   MSG_CLIENT_PRELAY                  : Handler_RelayAll; // 8101 - Relay message to all Users
      MSG_CLIENT_RESTART: Handler_Restart; // 8104 - Restart server
      MSG_CLIENT_BLOCK: Handler_Block; // 8105 - Block files
      MSG_CLIENT_UNBLOCK: Handler_Block; // 8106 - Unblock files
      MSG_CLIENT_BLOCKLIST: Handler_Block; // 8107 - List blocks
      MSG_CLIENT_MUZZLEINV: Handler_MuzzleInv; // 8108 - Invert muzzle
      MSG_CLIENT_GETCONSOLE: Handler_GetConsole; // 8109 - Get console Users
      MSG_CLIENT_CHANNEL_BANIP: Handler_ChanBan; // 8111 - Ban IP in channel
      MSG_CLIENT_SETTRANSFERS: Handler_SetTransfers; // 8112 - Set transfers
      MSG_CLIENT_GETTRANSFERS: Handler_GetTransfers; // 8113 - Get transfers
      MSG_CLIENT_ADDCHANNEL: Handler_AddChannel;
        // 8114 - Add new registered channel
      MSG_CLIENT_FRIENDS: Handler_Friends; // 8115 - Add/remove/list friends
      MSG_CLIENT_BANEX: Handler_BanEx; // 8116 - Ban user
      MSG_CLIENT_CHANGEBAN: Handler_ChangeBan; // 8117 - Change ban
      MSG_CLIENT_CHANNEL_OPEMOTE: Handler_ChannelOpEmote; // 8118 - Op emote
      MSG_CLIENT_ALIAS: Handler_AliasList; // 8119 - List server aliases
      MSG_CLIENT_GHOST: Handler_GhostKiller; // 8120 - Kill ghost
      MSG_CLIENT_PRIVMSG_ANON: Handler_PrivateMessageAnonymous;
        // 8121 - Send pm from user 'Server'
      MSG_CLIENT_ALLOWEDLIST: Handler_AllowedList;
        // 8122 - List admin+ allowed outgoing server connections
      MSG_CLIENT_CLIENTBLOCKS: Handler_ListBlockedClients;
        // 8123 List blocked clients
      MSG_CLIENT_UNREGISTER: Handler_UnRegister;
        // 8124 - Same as nuke without kill
      MSG_CLIENT_SETSERVERPASSWORDS: Handler_SetAllServerPasswords;
        // set 'My password' for all servers
      MSG_CLIENT_SHOWSETTING: Handler_AskRemoteConfig;
        // 8126 - Show value of server variable on all linked aervers
      MSG_CLIENT_SHOWALLSETTINGS: Handler_AskAllConfig;
        // 8127 Show settings on all linked servers
      MSG_CLIENT_REBOOT: Handler_Reboot; // 8128 Reboot server pc
      MSG_CLIENT_CONNECT: Handler_ServerConnect(nil, False);
        // 10100 - Connect server
      MSG_CLIENT_DISCONNECT: Handler_ServerDisconnect;
        // 10101 - Disconnect server
      MSG_CLIENT_KILL_SERVER: Handler_KillServer; // 10110 - Kill server
      MSG_CLIENT_REMOVE_SERVER: Handler_RemoveServer; // 10111 - Remove server
      MSG_CLIENT_LINKS: Handler_ServerLinks; // 10112 - Show linked servers
      MSG_CLIENT_USAGE_STATS: Handler_UsageStats; // 10115 - Server stats
      MSG_CLIENT_VERSION_STATS: Handler_SoftwareStats; // 10118 - Version stats
      MSG_CLIENT_WHICH_SERVER: Handler_WhichServer;
        // 10119 - Show which server client is on
      MSG_CLIENT_PING_ALL_SERVERS: Handler_PingAll; // 10120 - Ping all servers
      MSG_CLIENT_WHO_WAS: Handler_WhoWas; // 10121 - Who was
      MSG_CLIENT_HISTOGRAM: Exec(User, MSG_SERVER_HISTOGRAM, '0 0 0 0 0');
        // 10123
      MSG_CLIENT_REGISTER_USER: Handler_AdminRegister; // 10200 - Register user
      MSG_CLIENT_USER_MODE: Handler_UserMode; // 10203 - Set user mode
      MSG_CLIENT_OP: Handler_ChannelOp; // 10204 - Set channel Operator
      MSG_CLIENT_DEOP: Handler_ChannelDeop; // 10205 - Remove channel operator
      MSG_CLIENT_DROP_CHANNEL: Handler_DropChannel; // 10206 - Drop channel
      MSG_CLIENT_CHANNEL_WALLOP: Handler_ChannelWallop;
        // 10208 - Send message to all channel ops+
      MSG_CLIENT_CHANNEL_MODE: Handler_ChannelMode;
        // 10209 - Change channel mode
      MSG_CLIENT_CHANNEL_INVITE_OPENNAP: Handler_ChannelInvite;
        // 10210 - Invite user to channel
      MSG_CLIENT_CHANNEL_VOICE: Handler_ChannelVoice;
        // 10211 - Give voice to speak in channel
      MSG_CLIENT_CHANNEL_UNVOICE: Handler_ChannelUnvoice; // 10212 - Remove voice
      MSG_CLIENT_SHARE_FILE: Handler_ShareFile; // 10300 - Share non-mp3 file
      MSG_CLIENT_MSGSERV_READALL: Handler_MsgServ_ReadAll;
        // 10500 - read all messages
      MSG_CLIENT_MSGSERV_READNEW: Handler_MsgServ_ReadNew;
        // 10501 - read new messages
      MSG_CLIENT_MSGSERV_WRITE: Handler_MsgServ_write; // 10502 - Leave a message
      MSG_CLIENT_MSGSERV_DELETE: Handler_MsgServ_Delete;
        // 10503 - Delete a message
      MSG_CLIENT_MSGSERV_CLEAR: Handler_MsgServ_Clear;
        // 10504 - Delete all messages

      //     MSG_CLIENT_OPENNAPSERVER      : Handler_OpenNapLink; // 10010 - Opennap server tries to link

      14, 15, 300, 431, 624, 702, 920, 931, 932, 19999:
        begin
        end; // Ignore command
    end;
    Tmp_Pos := 4;
    if Local <> nil then
      Local.Flush;
{$I CheckSync.pas}
  except
    on E: Exception do
    begin
      DebugLog('Exception in ProcessCommand (Id=' + IntToStr(GCmd.Id) + ', Cmd="'
        + GCmd.Cmd + '", Query=' + IntToStr(Ord(Q)) + ', Pos=' + IntToStr(Tmp_Pos) +
        ') : ' + E.Message);
      Result := False;
    end;
  end;
  // Tmp_Local := nil; // to avoid possible access violation
end;

procedure ProcessRemoteUserCommand;
begin
  Tmp_Pos := 5;
  if not CheckParams(1) then Exit;
  User := DB_Online.FindUser(HList.Strings[0]);
  Tmp_Pos := 6;
  if User = nil then Exit;
  Local := User^.Local;
  Tmp_Pos := 7;
  GCmd.Cmd := NextParamEx(GCmd.Cmd);
  ProcessCommand(Local, queryRemoteUser);
end;

procedure ProcessServerCommand(Srv: TServer);
var
  Str1: string;
begin
  Tmp_Pos := 8;
  if Srv = nil then Exit;
  if not Running then Exit;
  Server := Srv;
  User := nil;
  Local := nil;
  // Tmp_Local := nil;
  Query := queryServer;
  Tmp_Pos := 9;
  try
    if Log_Servercommands then
    begin
      if GCmd.Id <> MSG_SRV_COMPRESSED then
      begin
        if Compressed then
          Str1 := RS_Handler_ReceiveCompressedSCommand +
            Format(' [%d] "%s" (%s)', [GCmd.Id, GCmd.Cmd, Server.Host])
        else
          Str1 := RS_Handler_ReceiveSCommand +
            Format(' [%d] "%s" (%s)', [GCmd.Id, GCmd.Cmd, Server.Host]);
        Log(0, Str1, True);
      end
      else
        Log(0, RS_Handler_ReceiveCompressedData + ' (' + Server.Host + ')',
          True);
    end;
    Tmp_Pos := 10;
    if GCmd.Id <> MSG_SRV_COMPRESSED then
      Inc(Num_Processed);
{$I CheckSync.pas}
    case GCmd.Id of
      MSG_SERVER_ERROR: Handler_ServerLoginError; // 0 - Server login Error
      MSG_SERVER_STATS: Handler_ServerStats; // 214 - Server stats
      MSG_SRV_LOGIN_ACK: Handler_ServerLoginAck; // 8001 - Login ack
      MSG_SRV_SETLASTINV: Handler_SetLastInv; // 8002 - set "Last_Inv" Variable
      MSG_SRV_FORWARDALL: Handler_ForwardAllServers;
        // 8003 - Forward message to all Linked servers
      MSG_SRV_DISCONNECTED: Handler_SrvRemoteDisconnect;
        // 8004 - Remote server Disconnected
      MSG_SRV_CHECKPASS: Handler_ServerCheckPass; // 8005 - Server authentication
      MSG_SRV_RELAY: Handler_SrvRelay;
        // 8007 - Relay message to other Server thru Hub
      MSG_SRV_SYNCCH: Handler_ServerSyncChannel; // 8008 - Channel sync
      MSG_SRV_VOICE: Handler_ServerChannelVoice; // 8009 - Voice user in channel
      MSG_SRV_OP: Handler_ServerChannelOp;
        // 8010 - Op user in channel (Done by Server)
      MSG_SRV_SYNCSRV: Handler_ServerSyncServer; // 8011 - Sync linked Server
      MSG_SRV_SYNCUSER: Handler_SyncUser(False); // 8012 - Sync user
      MSG_SRV_PUBLICSYNCUSER: Handler_SyncUser(True);
        // 8013 - Sync user (Sent to All servers)
      MSG_SRV_USEROFFLINE: Handler_RemoteUserKick; // 8014 - Kick remote User
      MSG_SRV_WALLOP: Handler_RemoteWallop; // 8015 - do wallop
      MSG_SRV_COMPRESSED: Handler_Decompress; // 8016 - Compressed Data
      MSG_SRV_UPDATEUSER: Handler_UpdateUser; // 8017 - Update user Data
      MSG_SRV_SEARCH_RESULT: Handler_RemoteSearchResult;
        // 8018 - Remote search Result
      MSG_SRV_SEARCH_END: Handler_RemoteSearchEnd; // 8019 - end remote Search
      MSG_SRV_SYNCREGISTERED: Handler_SyncRegistered;
        // 8020 - Sync registered User
      MSG_SRV_REMOTEBANKICK: Handler_RemoteBan; // 8021 - Kicking banned User
      MSG_SRV_SERVERBAN: Handler_RemoteBanEx; // 8022 - Ban user
      MSG_SRV_SERVERBANCLEAR: Handler_ClearServerBans; // 8023 - Sync bans
      MSG_SRV_SERVERBANSYNC: Handler_SyncServerBan; // 8024 - Sync bans
      MSG_SRV_REGISTEREDCLEAR: Handler_ClearServerRegistrations;
        // 8025 - Sync registered
      MSG_SRV_REGISTEREDSYNC: Handler_SyncServerRegistrations;
        // 8026 - Sync registered
      MSG_SRV_SHUTDOWN: Handler_ServerShutDown; // 8027 - Server shut Down
      MSG_SRV_FLOODER: Handler_Flooder; // 8028 - Informing about Flooder
      MSG_SRV_ALIAS: Handler_ServerAlias; // 8029 Server alias
      MSG_SRV_UPDATEBAN: Handler_UpdateBan;
        // 8030 Update ban with attempted Connection info
      MSG_SRV_BLOCKLISTCLEAR: Handler_ClearBlockList;
        // 8031 Sync blocked Files list
      MSG_SRV_BLOCKLISTSYNC: Handler_SyncServerBlockList;
        // 8032 Sync blocked Files list
      MSG_SRV_REMOTE_VAR_REQUEST: Handler_SetRemoteConfig;
        // 8033 Request setting Value from Remote server
      MSG_SRV_REMOTE_VAR_REPLY: Handler_ShowRemoteConfig; // 8034 Settings Reply
      MSG_CLIENT_RELAY: Handler_RelayMessage;
        // 8100 - Relay message to another User
      MSG_CLIENT_PING_ALL_SERVERS: Handler_PingAll; // 10120 - Ping all Servers
    else
      ProcessRemoteUserCommand;
    end;
    Tmp_Pos := 11;
{$I CheckSync.pas}
  except
    on E: Exception do
      if GCmd.Id <> MSG_SRV_COMPRESSED then
        DebugLog('Exception in ProcessServerCommand (Id=' + IntToStr(GCmd.Id) +
          ', Cmd="' + GCmd.Cmd + '", Pos=' + IntToStr(Tmp_Pos) + ') : ' + E.Message)
      else
        DebugLog('Exception in ProcessServerCommand (Id=' + IntToStr(GCmd.Id) +
          ', Pos=' + IntToStr(Tmp_Pos) + ' - Compressed Data) : ' + E.Message)
  end;
end;

initialization
  begin
    HList := TMyStringList.Create;
    HLst := TMyStringList.Create;
    Compressed := False;
    Tmp_Pos := 0; // Last = 1612
  end;

finalization
  begin
    HList.Free;
    HLst.Free;
  end;
end.
