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

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

 Class for main thread

*********************************************************}
unit thread;

interface

{$I defines.pas}

uses
 SysUtils, Classes, Classes2, graphics, zlibex, winsock, windows, constants,
 stypes, lang, blcksock, synsock, users, registered, keywords, localusers,
 servers, slavastrings, share, class_doublecmdlist, blocks, class_cmd2list;

type
 TMainThread = class(TThread)
  last_sync: Cardinal;
  last_stats: Cardinal;
  last_period: Cardinal;
  last_period30: Cardinal;
  last_period180: Cardinal;
  cycle_start, cycle_delay: Cardinal;
  constructor Create;
  destructor Destroy; override;
  procedure Execute; override;
  procedure ShutDown;
  procedure SyncClose;
//  procedure DoSync(t: Integer);
  procedure SyncData;
  procedure Check60;
  procedure Check30;
  procedure Check180;
  procedure CheckMinShare;
  procedure CheckBots;
  procedure SyncLog;
  procedure CreateSockets;
  function  Listen(var socket: HSocket; port: Integer): Boolean;
  procedure Accept; overload;
  procedure Accept(server: HSocket); overload;
  procedure AcceptOldReport;
  procedure AcceptNewReport;
  procedure AcceptMeta(server: HSocket);
  procedure CheckTimeouts;
  procedure CheckServers;
  procedure ReceiveData; overload;
  procedure ReceiveData(user: TLocalUser; counter, recurse: Integer); overload;
  procedure ProcessCmd(dcmd: TNapDoubleCmd);
  procedure SendWebPage(user: TLocalUser);
  procedure ResetSearchControl;
  procedure CloseSockets;
  procedure SynchronizeConsole;
  procedure ResetWantQueueControl;
  procedure ResetDLRequestControl;
  procedure CheckBlockedShare;
  procedure CheckForceEnter;
  procedure KillIdleUser;
 end;

var
 MainThread: TMainThread;
 quiet_listen : boolean;

implementation

uses
 vars, mainform, handler, napigator, dagsta, console, channels, chanattr,
 modeform, memory_manager, ips;

constructor TMainThread.Create;
begin
 try
  if (not log_to_file) and (log_file<>nil) then
   log_file.Free;
  except
   log_file:=nil;
   LogStartup('thread::create: cannot close "server.log" !!!');
   DebugLog('Error: cannot close file "server.log"');
 end;
 LogStartup('thread::create: resetting debug.log');
 try
  debug_file:=TFileStream.Create(ApplicationDir+'debug.log',fmCreate);
  debug_file.Free;
  except
 end;
 debug_file:=nil;
 try
//  if log_to_file then
   debug_file:=TFileStream.Create(ApplicationDir+'debug.log',fmOpenWrite or fmShareDenyWrite);
  except
   debug_file:=nil;
   DebugLog('Error: cannot open file "debug.log"');
   LogStartup('thread::create: cannot open "debug.log" !!!"');
 end;
 LogStartup('thread::create: opening "serverstats"');
 if clear_serverstats or (not FileExists(ApplicationDir+'serverstats')) then
 try
  DeleteFile(PChar(ApplicationDir+'serverstats'));
  except
 end;
 LogStartup('thread::create: loading blocked files');
 LoadBlocks;
 LogStartup('thread::create: loading ips');
 LoadIPs;
 LogStartup('thread::create: loading blocked directories');
 LoadDirs;
 LogStartup('thread::create: inherited Create(false);');
 inherited Create(false);
end;

destructor TMainThread.Destroy;
begin
 inherited Destroy;
end;

procedure TMainThread.ShutDown;
var
 i: Integer;
 user: TLocalUser;
 srv: TServer;
 ch: TChannel;
begin
SlavaNapWindow.Visible := False;
DebugLog('ShutDown - start debug',true);
 try
  DisconnectNapigator(0);
  DisconnectDagsta(0);
  except
   on E:exception do
    DebugLog('Exception on ShutDown - 0: '+E.Message);
 end;
 {$I checksync.pas}
 DebugLog('ShutDown - 1',true);
 try
  StrHash_SaveToFile(cons.hotlist,ApplicationDir+'hotlist');
  StrHash_SaveToFile(cons.ignored,ApplicationDir+'ignored');
  except
   on E:exception do
    DebugLog('Exception on ShutDown - 1: '+E.Message);
 end;
 {$I checksync.pas}
 DebugLog('ShutDown - 2',true);
 try
  for i:=db_local.count-1 downto 0 do
  begin
    user:=db_local.Items[i];
    if user.logged then DisconnectUser(user,'','','ShutDown',true);
    if (i mod 10)=0 then
    begin
     {$I checksync.pas}
    end;
  end;
  except
   on E:exception do
    DebugLog('Exception on ShutDown - 2: '+E.Message);
 end;
DebugLog('ShutDown - 3',true);
 try
  for i:=0 to db_local.count-1 do
  begin
   user:=db_local.Items[i];
   user.Free;
   if (i mod 10)=0 then
   begin
    {$I checksync.pas}
   end;
  end;
  except
   on E:exception do
    DebugLog('Exception on ShutDown - 3: '+E.Message);
 end;
DebugLog('ShutDown - 4',true);
 try
  db_local.Free;
  db_local:=nil;
  except
   on E:exception do
    DebugLog('Exception on ShutDown - db_local.free: '+E.Message);
 end;
 {$I checksync.pas}
 try
  for i:=0 to MAX_LISTEN_SOCKET do
  if server_socket[i]<>INVALID_SOCKET then
  begin
   synsock.shutdown(server_socket[i],SD_BOTH);
   synsock.closesocket(server_socket[i]);
   server_socket[i]:=INVALID_SOCKET;
   dec(sockets_count);
  end;
  for i:=0 to MAX_LISTEN_TRAPSOCKET do
  if trap_socket[i]<>INVALID_SOCKET then
  begin
   synsock.shutdown(trap_socket[i],SD_BOTH);
   synsock.closesocket(trap_socket[i]);
   trap_socket[i]:=INVALID_SOCKET;
   dec(sockets_count);
  end;
 DebugLog('ShutDown - 5',true);
  if old_report_socket<>INVALID_SOCKET then
  begin
   synsock.shutdown(old_report_socket,SD_BOTH);
   synsock.closesocket(old_report_socket);
   old_report_socket:=INVALID_SOCKET;
   dec(sockets_count);
  end;
  if new_report_socket<>INVALID_SOCKET then
  begin
   synsock.shutdown(new_report_socket,SD_BOTH);
   synsock.closesocket(new_report_socket);
   new_report_socket:=INVALID_SOCKET;
   dec(sockets_count);
  end;
  {if meta_socket<>INVALID_SOCKET then
  begin
   synsock.shutdown(meta_socket,SD_BOTH);
   synsock.closesocket(meta_socket);
   meta_socket:=INVALID_SOCKET;
   dec(sockets_count);
  end;}
  SetLength(metasocket_list, Length(metaport_list));
  for i := 0 to Length(metasocket_list) - 1 do
    if metasocket_list[i] <> INVALID_SOCKET then
    begin
      synsock.shutdown(metasocket_list[i],SD_BOTH);
      synsock.closesocket(metasocket_list[i]);
      metasocket_list[i]:=INVALID_SOCKET;
      dec(sockets_count);
    end;
 DebugLog('ShutDown - 6',true);
  {$I checksync.pas}
  if napigator_socket<>INVALID_SOCKET then
  begin
   synsock.shutdown(napigator_socket,SD_BOTH);
   synsock.closesocket(napigator_socket);
   napigator_socket:=INVALID_SOCKET;
   dec(sockets_count);
  end;
  if dagsta_socket<>INVALID_SOCKET then
  begin
   synsock.shutdown(dagsta_socket,SD_BOTH);
   synsock.closesocket(dagsta_socket);
   dagsta_socket:=INVALID_SOCKET;
   dec(sockets_count);
  end;
  except
   on E:exception do
    DebugLog('Exception on ShutDown - closing sockets: '+E.Message);
 end;
 try
  SaveServers;
  except
   on E:exception do
    DebugLog('Exception on ShutDown - SaveServers: '+E.Message);
 end;
DebugLog('ShutDown - 7',true);
 {$I checksync.pas}
 try
  for i:=0 to db_servers.Count-1 do
  begin
   srv:=db_servers.Items[i];
   srv.Free;
  end;
  except
   on E:exception do
    DebugLog('Exception on ShutDown - srv.free: '+E.Message);
 end;
 {$I checksync.pas}
 try
  SaveChannels(ApplicationDir+'channels');
  except
 end;
DebugLog('ShutDown - 8',true);
 {$I checksync.pas}
 try
  for i:=0 to db_channels.Count-1 do
  begin
   ch:=db_channels.Items[i];
   ch.Free;
  end;
  except
   on E:exception do
    DebugLog('Exception on ShutDown - ch.free: '+E.Message);
 end;
 {$I checksync.pas}
 try
  db_registered.SaveToFile(ApplicationDir+'users');
  except
 end;
DebugLog('ShutDown - 9',true);
 try
  db_bans.SaveToFile(ApplicationDir+'bans');
  except
 end;
 try
  StrHash_SaveToFile(db_motd,ApplicationDir+'motd');
  except
 end;
 {$I checksync.pas}
 DebugLog('ShutDown - 10',true);
 try
  StrHash_SaveToFileSorted(db_friends,ApplicationDir+'friends');
  except
 end;
 {$I checksync.pas}
 DebugLog('ShutDown - saving dengon', true);
 try
  // StrHash_SaveToFile(db_msgserv,ApplicationDir+'dengon');
  MataParaEncrypt(db_msgserv, DefaultCryptPass, ApplicationDir+'dengon');
  except
 end;
 try
  SaveBlocks;
  except
 end;
 {$I checksync.pas}
 DebugLog('ShutDown - saving IPs', true);
 try
  SaveIPs;
  except
 end;
 {$I checksync.pas}
 DebugLog('ShutDown - saving dirs', true);
 try
  SaveDirs;
  FreeDirs;
  except
   on E:exception do
    DebugLog('Exception on ShutDown - saving dirs: '+E.Message);
 end;
 {$I checksync.pas}
 DebugLog('ShutDown - saving clientstats', true);
 try
  db_software.SortByID1;
  db_software.SaveToFile(ApplicationDir+'clientstats',cmd2ID2CRCLC);
  except
   on E:exception do
    DebugLog('Exception on ShutDown - db_software.save: '+E.Message);
 end;
DebugLog('ShutDown - 11',true);
 {$I checksync.pas}
 try
  db_channels.Free;
  db_channels:=nil;
 DebugLog('ShutDown - 11.0',true);
  db_servers.Free;
  db_servers:=nil;
 DebugLog('ShutDown - 11.1',true);
  db_online.Free;
  db_online:=nil;
 DebugLog('ShutDown - 11.2',true);
  db_registered.Free;
  db_registered:=nil;
 DebugLog('ShutDown - 11.3(FreeKeywords)',true);
  {$I checksync.pas}
  FreeKeywords;
  {$I checksync.pas}
  DebugLog('ShutDown - 12',true);
  db_bans.Free;
  db_bans:=nil;
  DebugLog('ShutDown - 12.0',true);
  StrHash_Clear(db_motd);
  sleep(5);
  StrHash_Clear(db_friends);
  sleep(5);
  StrHash_Clear(db_msgserv);
  DebugLog('ShutDown - 12.1',true);
  {$I checksync.pas}
  sync_reply_list.Free;
  sync_reply_list:=nil;
 DebugLog('ShutDown - 13',true);
  {$I checksync.pas}
  StrHash_Clear(ext_mp3_list);
  StrHash_Clear(ext_audio_list);
  StrHash_Clear(ext_video_list);
  StrHash_Clear(ext_text_list);
  StrHash_Clear(ext_image_list);
  StrHash_Clear(ext_app_list);
  StrHash_Clear(ext_cd_list);
  StrHash_Clear(fakeext_list);
  StrHash_Clear(force_enter_channel_list);
  {$I checksync.pas}
  FreeBlocks;
 DebugLog('ShutDown - 14',true);
  {$I checksync.pas}
  db_software.Free;
  db_software:=nil;
  db_invitations.Free;
  db_invitations:=nil;
  db_whowas.Free;
  db_whowas:=nil;
  db_reconnect.Free;
  db_reconnect:=nil;
  redirected_servers.Free;
  redirected_servers := nil;
 DebugLog('ShutDown - 15',true);
  {$I checksync.pas}
  cmd_list.Free;
  cmd_list:=nil;
 DebugLog('ShutDown - ok',true);
  except
   on E:exception do
    DebugLog('Exception on ShutDown end: '+E.Message);
 end;
 if log_file<>nil then
 try
  log_file.Free;
  log_file:=nil;
  except
 end;
 if debug_file<>nil then
 try
  debug_file.Free;
  debug_file:=nil;
  except
 end;
 try
  for i:=0 to db_closed.count-1 do
  synsock.closesocket(PNapCmd2(db_closed.Items[i])^.id1);
  db_closed.Free;
  except
 end;
end;

procedure TMainThread.Execute;
begin
  current_time:=GetTickCount;
  current_time_t:=GetTickCountT;
  LogStartup('thread::execute: starting main thread');
  FreeOnTerminate:=true;
  Priority:=tpHigher;
  last_sync:=current_time;
//  last_sync_check:=current_time;
  last_stats:=current_time;
  cycle_start:=current_time;
  last_period:=current_time;
  last_period30:=current_time;
  last_period180:=current_time;
  last_announcement:=current_time;
  last_force_enter:=current_time;
  bandwidth_lastcheck:=current_time;
  bandwidth_up:=0;
  bandwidth_down:=0;
  users_per_minute:=0;
  LogStartup('thread::execute: first Synchronize(SyncLog);');
  try
   Synchronize(SyncLog);
   except
    on E:exception do
     DebugLog('Exception on Synchronize(synclog1) pos='+IntToStr(tmp_pos)+' : '+E.Message);
  end;
  LogStartup('thread::execute: creating sockets');
  CreateSockets;
 {$I checksync.pas}
  LogStartup('thread::execute: InitKeywords');
  InitKeywords;
  LogStartup('thread::execute: InitNapigator');
  InitNapigator;
  LogStartup('thread::execute: InitDagsta');
  InitDagsta;
  LogStartup('thread::execute: SyncLog');
  try
   Synchronize(SyncLog);
   except
    on E:exception do
     DebugLog('Exception on Synchronize(synclog2) pos='+IntToStr(tmp_pos)+' : '+E.Message);
  end;
  LogStartup('thread::execute: CountStats');
  CountStats;
  LogStartup('thread::execute: CheckBandwidthTime');
  CheckBandwidthTime;
  linking:=false;
  if GetLangT(LNG_LNGFILE_VERSION)<>SLAVANAP_BUILD then
   SlavaNapWindow.LogMain(slError,GetLangT(LNG_INVALIDLNGFILE,language+'.lng'));
  if GetLangT(LNG_MSGFILE_VERSION)<>SLAVANAP_BUILD then
   SlavaNapWindow.LogMain(slError,GetLangT(LNG_INVALIDLNGFILE,'slavanap.msg'));
  if cons_autojoin3<>'' then
   cmd_list.AddDoubleCmd(MSG_CLIENT_JOIN,0,cons_autojoin3,'');
  if cons_autojoin2<>'' then 
   cmd_list.AddDoubleCmd(MSG_CLIENT_JOIN,0,cons_autojoin2,'');
  if cons_autojoin1<>'' then 
   cmd_list.AddDoubleCmd(MSG_CLIENT_JOIN,0,cons_autojoin1,'');
  LogStartup('thread::execute: starting loop');
  while not Terminated do
  try
    tmp_pos:=1479;
    current_time:=GetTickCount;
    current_time_t:=GetTickCountT;
    if (current_time-bandwidth_lastcheck)>BANDWIDTH_TIMEOUT then CheckBandwidthTime;
    sleep(5);
    tmp_pos:=1480;
    if running then SynchronizeConsole;
    tmp_pos:=1603;
    if running then Accept;
    if running then CheckServers;
    if running then ReceiveData;
    sleep(10);
    tmp_pos:=1481;
    if running then Accept;
    if running then Check30;
    if running then CheckServers;
    if running then Accept;
    tmp_pos:=1482;
    current_time:=GetTickCount;
    current_time_t:=GetTickCountT;
    tmp_pos:=1483;
    if running then CheckNapigator;
    if running then CheckDagsta;
    if running then CheckTimeouts;
    sleep(5);
    tmp_pos:=1484;
    if running then CheckBots;
    if running then CheckForceEnter;
    if running then CheckServers;
    tmp_pos:=1485;
    if running then Check60;
    if running then CloseSockets;
    tmp_pos:=1486;
    if not running then Terminate;
   except
    DebugLog('Exception in TMainThread.Execute()  pos='+IntToStr(tmp_pos));
  end;
  try
   WriteAllServers(MSG_SRV_SHUTDOWN,'',restart_user);
   if num_servers>0 then CheckServers;
   LogStartup('thread::execute: ShutDown');
   ShutDown;
   LogStartup('thread::execute: SyncClose');
   try
    Synchronize(SyncClose);
    except
    on E:exception do
     DebugLog('Exception on Synchronize(syncclose) pos='+IntToStr(tmp_pos)+' : '+E.Message);
   end;
   LogStartup('thread::execute: end;');
   except
    DebugLog('Exception on shutdown in TMainThread.Execute. pos='+IntToStr(tmp_pos),true);
  end;
end;

procedure TMainThread.SyncClose;
begin
 try
  SlavaNapWindow.Close;
  except
 end;
end;

procedure TMainThread.SyncLog;
var
 dcmd: TNapDoubleCmd;
begin
 if not running then exit;
 try
   tmp_pos:=1487;
   while sync_reply_list.Count>0 do
   begin
     tmp_pos:=1488;
     dcmd:=sync_reply_list.Cmd(0);
     sync_reply_list.Delete(0);
     tmp_pos:=1489;
     case dcmd.id1 of
       MSG_SR_LOG:             SlavaNapWindow.LogMain(dcmd.id2,dcmd.cmd2,false);
       MSG_SR_CONSOLELOG:      SlavaNapWindow.LogConsole(dcmd.id2,dcmd.cmd2,false);
       MSG_SR_RESTART:         SlavaNapWindow.mnu_tray_restartClick(nil);
       MSG_SR_CONSOLEREPLY:    ConsoleReply(dcmd.id2,dcmd.cmd1);
     end;
     tmp_pos:=1490;
   end;
  except
   try
    SlavaNapWindow.LogMain(slDebugData,'Exception in SyncLog. pos='+IntToStr(tmp_pos));
    except
   end;
 end;
 tmp_pos:=1491;
end;

procedure TMainThread.SyncData;
var
 dcmd: TNapDoubleCmd;
begin
 try
   tmp_pos:=1472;
   if not running then exit;
   if not SlavaNapWindow.Timer1.enabled then
     SlavaNapWindow.Timer1Timer(nil);
   tmp_pos:=1473;
   SyncLog;
   tmp_pos:=1474;
   while cmd_list.Count>0 do
   begin
     dcmd:=cmd_list.Cmd(0);
     cmd_list.Delete(0);
     tmp_pos:=1475;
     try
      ProcessCmd(dcmd);
      except
       on E:Exception do
        DebugLog('Exception in SyncData (id1='+IntToStr(dcmd.id1)+', pos='+IntToStr(tmp_pos)+') : '+E.Message);
     end;
     tmp_pos:=1476;
   end;
   tmp_pos:=1477;
  except
   try
    SlavaNapWindow.LogMain(slDebugData,'Exception in SyncData. pos='+IntToStr(tmp_pos));
    except
   end;
 end;
 tmp_pos:=1478;
end;

procedure TMainThread.ProcessCmd(dcmd: TNapDoubleCmd);
var
 ch: TChannel;
 logfilename: String;
begin
 tmp_pos:=1492;
 case dcmd.id1 of
   MSG_CMD_SAVEDATA: begin
                       if not running then exit;
                       tmp_pos:=1493;
                       SlavaNapWindow.mnu_save.Enabled:=true;
                       Log(slDebugData,GetLangI(LNG_SAVINGDATA));
                       Synchronize(SyncLog);
                       tmp_pos:=1494;
                       db_registered.SaveToFile(ApplicationDir+'users');
                       db_bans.SaveToFile(ApplicationDir+'bans');
                       db_software.SortByID1;
                       db_software.SaveToFile(ApplicationDir+'clientstats',cmd2ID2CRCLC);
                       tmp_pos:=1495;
                       StrHash_SaveToFileSorted(db_friends,ApplicationDir+'friends');
                       SaveChannels(ApplicationDir+'channels');
                       tmp_pos:=1496;
                       StrHash_SaveToFile(db_motd,ApplicationDir+'motd');
                       // StrHash_SaveToFile(db_msgserv,ApplicationDir+'dengon');
                       MataParaEncrypt(db_msgserv, 'himitsu', ApplicationDir+'dengon');
                       StrHash_SaveToFile(cons.hotlist,ApplicationDir+'hotlist');
                       tmp_pos:=1497;
                       StrHash_SaveToFile(cons.ignored,ApplicationDir+'ignored');
                       SaveBlocks;
                       SaveServers;
                       tmp_pos:=1498;
                     end;
   MSG_CMD_RESETLOG: begin
                       tmp_pos:=1499;
                       ShortDateFormat := 'yyyymmdd';
                       //logfilename:=ApplicationDir+'server-'+DateToStr(now)+'.log';
                       logfilename:=log_folder+'server-'+DateToStr(now)+'.log';
                       ShortDateFormat := 'yyyy/mm/dd';
                       try
                         if log_file<>nil then
                          log_file.Free;
                         log_file:=nil;
                        except
                       end;
                       try
                        log_file:=TFileStream.Create(logfilename,fmCreate);
                        log_file.Free;
                        except
                       end;
                       log_file:=nil;
                       tmp_pos:=1500;
                       try
                        if log_to_file then
                         log_file:=TFileStream.Create(logfilename,fmOpenWrite or fmShareDenyWrite);
                        except
                         log_file:=nil;
                         DebugLog('Error: cannot reset file '+logfilename);
                       end;
                       // resetting debug file
                       try
                         if debug_file<>nil then
                          debug_file.Free;
                         debug_file:=nil;
                        except
                       end;
                       try
                        debug_file:=TFileStream.Create(ApplicationDir+'debug.log',fmCreate);
                        debug_file.Free;
                        except
                       end;
                       debug_file:=nil;
                       try
                        // if log_to_file then
                         debug_file:=TFileStream.Create(ApplicationDir+'debug.log',fmOpenWrite or fmShareDenyWrite);
                        except
                         debug_file:=nil;
                         DebugLog('Error: cannot reset file "debug.log"');
                       end;
                       tmp_pos:=1501;
                     end;
   MSG_CMD_RELOADCHMOTD: begin
                       tmp_pos:=1502;
                       ch:=FindChannel(dcmd.cmd1);
                       tmp_pos:=1503;
                       if ch<>nil then
                       begin
                        if not StrHash_LoadFromFile(ch.motd,ApplicationDir+'chmotd.'+ch.channel) then
                         ch.DefaultMotd;
                       end;
                       tmp_pos:=1504;
                     end;
   MSG_CMD_REFRESHTYPES: begin
                       tmp_pos:=1505;
                       SplitString(lowercase(ext_mp3),ext_mp3_list);
                       SplitString(lowercase(ext_audio),ext_audio_list);
                       SplitString(lowercase(ext_video),ext_video_list);
                       SplitString(lowercase(ext_text),ext_text_list);
                       SplitString(lowercase(ext_image),ext_image_list);
                       SplitString(lowercase(ext_app),ext_app_list);
                       SplitString(lowercase(ext_cd),ext_cd_list);
                       SplitString(AnsiLowerCase(fakeext),fakeext_list);
                       tmp_pos:=1506;
                     end;
   MSG_CMD_GETMODE:  if running then
                     begin
                       tmp_pos:=1507;
                       SlavaNapWindow.btn_log_mode.enabled:=true;
                       SlavaNapMode.ShowForm;
                       tmp_pos:=1508;
                     end;
   MSG_CMD_MOTDCHANGE: StrHash_LoadFromFile(db_motd,ApplicationDir+'motd');
   MSG_CMD_LISTREGISTERED:  ConsoleListRegistered;
   MSG_CMD_LISTUSERS: ConsoleListUsers;
   MSG_CMD_LISTSERVERS: ConsoleListServers;
   MSG_CMD_LISTCHANNELS: ConsoleListChannels;
   MSG_CMD_LISTBANS: ConsoleListBans;
   MSG_CMD_LISTHOTLIST: ConsoleListHotlist;
   MSG_CMD_REFRESHLISTS: ConsoleRefreshLists(dcmd.cmd1='');
   MSG_CMD_SETNAPIGATORPASS: napigator_password:=dcmd.cmd1;
   MSG_CMD_SETDAGSTAPASS: dagsta_password:=dcmd.cmd1;
   MSG_CMD_CHANNELPROPS: SlavaNapChannelAttr.ShowChannel(dcmd.cmd1);
   MSG_CMD_WALLOP: Wallop(MSG_SERVER_NOSUCH,wallopServer,dcmd.cmd1,true);
   MSG_CMD_ADDSERVER: ConsoleAddServer(dcmd.cmd1);
   MSG_CMD_SERVERPROPS: ConsoleServerProps(dcmd.cmd1);
   MSG_CMD_UPDATESERVERPROPS: ConsoleUpdateServerProps(dcmd.cmd1);
   MSG_CLIENT_PRIVMSG: begin
                         tmp_pos:=1509;
                         gcmd.id:=dcmd.id1;
                         gcmd.cmd:=dcmd.cmd1;
                         ProcessCommand(cons,queryNormal);
                         SendPrivateMyMessage(dcmd.cmd1);
                         tmp_pos:=1510;
                       end;
   MSG_CMD_SETCOMPRESS: ConsoleSetCompress(dcmd.cmd1);
   MSG_CMD_SETSTARTUP: ConsoleSetStartup(dcmd.cmd1);
   MSG_CMD_SAVESHARED: ConsoleSaveShared;
   else  gcmd.id:=dcmd.id1;
         gcmd.cmd:=dcmd.cmd1;
         tmp_pos:=1511;
         ProcessCommand(cons,queryNormal);
 end;
 tmp_pos:=1512;
end;

procedure TMainThread.CreateSockets;
var
 i: Integer;
begin
 db_closed:=TNapCmd2List.Create;
 quiet_listen:=false;
 tmp_pos:=1513;
 for i:=0 to MAX_LISTEN_SOCKET do
  if server_port[i]<>0 then
  try
    tmp_pos:=1514;
    server_socket[i]:=synsock.socket(PF_INET,integer(SOCK_STREAM),IPPROTO_TCP);
    inc(sockets_count);
    tmp_pos:=1515;
    if not Listen(server_socket[i],server_port[i]) then
     if server_socket[i]<>INVALID_SOCKET then
     begin
      tmp_pos:=1516;
      synsock.shutdown(server_socket[i],SD_BOTH);
      synsock.closesocket(server_socket[i]);
      server_socket[i]:=INVALID_SOCKET;
      tmp_pos:=1517;
      dec(sockets_count);
     end;
    tmp_pos:=1518;
    Synchronize(SyncLog);
   except
   on E:Exception do
    DebugLog('Exception in TMainThread::CreateSockets (pos='+IntToStr(tmp_pos)+') : '+E.Message);
  end;
 tmp_pos:=12262;
 if enable_trapport then
 for i:=0 to MAX_LISTEN_TRAPSOCKET do
  if trap_port[i]<>0 then
  try
    trap_socket[i]:=synsock.socket(PF_INET,integer(SOCK_STREAM),IPPROTO_TCP);
    inc(sockets_count);
    if not Listen(trap_socket[i],trap_port[i]) then
     if trap_socket[i]<>INVALID_SOCKET then
     begin
      synsock.shutdown(trap_socket[i],SD_BOTH);
      synsock.closesocket(trap_socket[i]);
      trap_socket[i]:=INVALID_SOCKET;
      dec(sockets_count);
     end;
    Synchronize(SyncLog);
   except
   on E:Exception do
    DebugLog('Exception in TMainThread::CreateSockets (pos='+IntToStr(tmp_pos)+') : '+E.Message);
  end;
  if old_report_enabled then
  try
   tmp_pos:=1519;
   old_report_socket:=synsock.socket(PF_INET,integer(SOCK_STREAM),IPPROTO_TCP);
   inc(sockets_count);
   tmp_pos:=1520;
   if not Listen(old_report_socket,old_report_port) then
    if old_report_socket<>INVALID_SOCKET then
    begin
      tmp_pos:=1521;
      synsock.shutdown(old_report_socket,SD_BOTH);
      synsock.closesocket(old_report_socket);
      old_report_socket:=INVALID_SOCKET;
      dec(sockets_count);
    end;
    tmp_pos:=1522;
    Synchronize(SyncLog);
   except
   on E:Exception do
    DebugLog('Exception in TMainThread::CreateSockets (2) (pos='+IntToStr(tmp_pos)+') : '+E.Message);
  end;
  if new_report_enabled then
  try
   tmp_pos:=1523;
   new_report_socket:=synsock.socket(PF_INET,integer(SOCK_STREAM),IPPROTO_TCP);
   inc(sockets_count);
   tmp_pos:=1524;
   if not Listen(new_report_socket,new_report_port) then
    if new_report_socket<>INVALID_SOCKET then
    begin
      tmp_pos:=1525;
      synsock.shutdown(new_report_socket,SD_BOTH);
      synsock.closesocket(new_report_socket);
      new_report_socket:=INVALID_SOCKET;
      dec(sockets_count);
    end;
    tmp_pos:=1526;
    Synchronize(SyncLog);
   except
   on E:Exception do
    DebugLog('Exception in TMainThread::CreateSockets (3) (pos='+IntToStr(tmp_pos)+') : '+E.Message);
  end;
  {if meta_enabled then
  try
   tmp_pos:=1527;
   meta_socket:=synsock.socket(PF_INET,integer(SOCK_STREAM),IPPROTO_TCP);
   inc(sockets_count);
   if not Listen(meta_socket,meta_port) then
    if meta_socket<>INVALID_SOCKET then
    begin
      tmp_pos:=1528;
      synsock.shutdown(meta_socket,SD_BOTH);
      synsock.closesocket(meta_socket);
      meta_socket:=INVALID_SOCKET;
      dec(sockets_count);
    end;
    tmp_pos:=1529;
    Synchronize(SyncLog);
   except
   on E:Exception do
    DebugLog('Exception in TMainThread::CreateSockets (4) (pos='+IntToStr(tmp_pos)+') : '+E.Message);
  end;}
  if enable_metaserver then
  try
    tmp_pos:=1527;
    SetLength(metasocket_list, Length(metaport_list));
    for i := 0 to Length(metasocket_list) - 1 do
    begin
      metasocket_list[i] := synsock.socket(PF_INET, integer(SOCK_STREAM), IPPROTO_TCP);
      Inc(sockets_count);
      if not Listen(metasocket_list[i], metaport_list[i]) then
      if metasocket_list[i]<>INVALID_SOCKET then
      begin
        tmp_pos:=1528;
        synsock.shutdown(metasocket_list[i],SD_BOTH);
        synsock.closesocket(metasocket_list[i]);
        metasocket_list[i]:=INVALID_SOCKET;
        dec(sockets_count);
      end;
      tmp_pos:=1529;
      Synchronize(SyncLog);
    end;
  except
    on E:Exception do
      DebugLog('Exception in TMainThread::CreateSockets (4) (pos='+IntToStr(tmp_pos)+') : '+E.Message);
  end;
  tmp_pos:=1530;
  Synchronize(SyncLog);
  quiet_listen:=true;
  tmp_pos:=1531;
end;

function TMainThread.Listen(var socket: HSocket; port: Integer): Boolean;
var
 last_error: Integer;
begin
 tmp_pos:=1531;
 if socket=INVALID_SOCKET then
 begin
  socket:=synsock.socket(PF_INET,integer(SOCK_STREAM),IPPROTO_TCP);
  inc(sockets_count);
 end;
// TCPSocket_SetLinger(socket,true,0);
 TCPSocket_Bind(socket,listen_interface,IntToStr(port));
 last_error:=TCPSocket_SockCheck(synsock.listen(socket,SOMAXCONN));
 tmp_pos:=1532;
 if last_error=0 then
 begin
  if not (quiet_listen) then
  if (socket=new_report_socket) or (socket=old_report_socket) then
   log(slText,GetLangT(LNG_SERVER_LISTENSTATS,port))
  else
   log(slText,GetLangT(LNG_SERVER_LISTEN,port));
  TCPSocket_Block(socket,false);
  Result:=true;
 end
 else
 begin
  log(slError,GetLangT(LNG_SERVER_LISTENERR,port));
  Result:=false;
 end;
 tmp_pos:=1533;
end;

procedure TMainThread.Accept;
var
 i,last_error: Integer;
begin
 try
  tmp_pos:=1534;
  if old_report_socket<>INVALID_SOCKET then
   while TCPSocket_CanRead(old_report_socket,0,last_error) do
    AcceptOldReport;
  if new_report_socket<>INVALID_SOCKET then
   while TCPSocket_CanRead(new_report_socket,0,last_error) do
    AcceptNewReport;
  {if meta_socket<>INVALID_SOCKET then
   while TCPSocket_CanRead(meta_socket,0,last_error) do
    AcceptMeta;}
  for i := 0 to Length(metasocket_list) - 1 do
    if metasocket_list[i] <> INVALID_SOCKET then
    while TCPSocket_CanRead(metasocket_list[i], 0, last_error) do
      AcceptMeta(metasocket_list[i]);
  {$I checksync.pas}
  tmp_pos:=1535;
  for i:=0 to MAX_LISTEN_SOCKET do
   if server_socket[i]<>INVALID_SOCKET then
     while TCPSocket_CanRead(server_socket[i],0,last_error) do
     begin
      Accept(server_socket[i]);
      {$I checksync.pas}
     end;
  if enable_trapport then
  for i:=0 to MAX_LISTEN_TRAPSOCKET do
   if trap_socket[i]<>INVALID_SOCKET then
     while TCPSocket_CanRead(trap_socket[i],0,last_error) do
     begin
      Accept(trap_socket[i]);
      {$I checksync.pas}
     end;
   except
   DebugLog('Exception in Accept 1. pos='+IntToStr(tmp_pos));
 end;
 tmp_pos:=1536;
end;

procedure TMainThread.Accept(server: HSocket);
var
 user: TLocalUser;
 h: HSocket;
 i: Integer;
 local_ip: Cardinal;
 sin: TSockAddrIn;
begin
 try
   i:=0;
   h:=synsock.Accept(server,nil,nil);
   i:=1;
   if h=SOCKET_ERROR then exit;
   if h=INVALID_SOCKET then exit;
   inc(sockets_count);
   i:=2;
//   sin:=TCPSocket_GetRemoteSin(h);
//   remote_ip:=decode_ip(sin.sin_addr.S_addr);
   sin:=TCPSocket_GetLocalSin(h);
   local_ip:=sin.sin_addr.S_addr;
   i:=3;
   user:=CreateLocalUser;
   user.socket:=h;
   if not sockets_users_default then
   begin
     TCPSocket_SetSizeRecvBuffer(h,sockets_users_recv);
     TCPSocket_SetSizeSendBuffer(h,sockets_users_send);
   end;
   i:=4;
   TCPSocket_Block(h,false);
   TCPSocket_KeepAlive(h,true);
   i:=6;
   if cons.data^.ip=0 then
   begin
    i:=8;
    cons.data^.ip:=local_ip;
    i:=9;
   end;
   i:=10;
   db_local.Add(user);
   i:=11;
   {$I checksync.pas}
  except
   DebugLog('Exception in Accept 2.  i='+IntToStr(i));
 end;
end;

procedure TMainThread.AcceptOldReport;
var
 user: TLocalUser;
 h: TSocket;
 i: Integer;
 str: String;
 sin: TSockAddrIn;
begin
 try
   i:=0;
   h:=synsock.accept(old_report_socket,nil,nil);
   i:=1;
   if h=SOCKET_ERROR then exit;
   if h=INVALID_SOCKET then exit;
   inc(sockets_count);
   i:=2;
   user:=CreateLocalUser;
   i:=3;
   user.socket:=h;
   i:=5;
   TCPSocket_Block(h,false);
   i:=6;
   sin:=TCPSocket_GetLocalSin(h);
   i:=7;
{   if cons.data^.ip=0 then
   begin
    i:=8;
    cons.data^.ip:=sin.sin_addr.S_addr;
    i:=9;
   end;}
   i:=10;
   db_local.Add(user);
   i:=11;
   user.localstate:=user.localstate+[locWriteOnly];
   i:=12;
   str:=IntToStr(total_users)+' '+IntToStr(total_files)+' 0.00 '+IntToStr(total_bytes)+' 0';
   i:=13;
   user.WriteData(str);  
  except
   DebugLog('Exception in AcceptOldReport.  i='+IntToStr(i));
 end;
end;

procedure TMainThread.AcceptNewReport;
var
 user: TLocalUser;
 h: TSocket;
 i,j: Integer;
 str, str2: String;
 srv: TServer;
 list: TMyStringList;
 sin: TSockAddrIn;
begin
 try
   i:=0;
   h:=synsock.accept(new_report_socket,nil,nil);
   i:=1;
   if h=SOCKET_ERROR then exit;
   if h=INVALID_SOCKET then exit;
   inc(sockets_count);
   i:=2;
   user:=CreateLocalUser;
   i:=4;
   user.socket:=h;
   i:=5;
   TCPSocket_Block(h,false);
   i:=6;
   sin:=TCPSocket_GetLocalSin(h);
   i:=7;
{   if cons.data^.ip=0 then
   begin
    i:=8;
    cons.data^.ip:=sin.sin_addr.S_addr;
    i:=9;
   end;}
   i:=10;
   db_local.Add(user);
   i:=11;
   user.localstate:=user.localstate+[locWriteOnly];
   i:=12;
   list:=CreateStringList;
   if (max_users>9) or (not new_report_hide_hub) then
   begin
    if true_stats=true then str2:=servername_t else str2:=serveralias;
    str:=str2+' '+IntToStr(napigator_myport)+' '+IntToStr(local_users)+' '+IntToStr(max_users)+' '+IntToStr(local_files)+' '+IntToStr(local_bytes div GigaByte)+' '+cons.nick;
    list.Add(str);
   end;
   for j:=0 to db_servers.Count-1 do
   begin
     str2:='';
     srv:=db_servers.Items[j];
     if srv.logged then
     if (srv.max_users>9) or (not new_report_hide_hub) then
     begin
       if srv.truestats then str2:=GetServerName(srv) else str2:=GetServerAlias(srv);
       str:=str2+' '+IntToStr(srv.port)+' '+IntToStr(srv.num_users)+' '+IntToStr(srv.max_users)+' '+IntToStr(srv.num_files)+' '+IntToStr(srv.num_bytes div GigaByte)+' '+srv.console;
       list.Add(str);
     end;
   end;
   list.Sort;
   str:=IntToStr(list.count)+' '+IntToStr(total_users)+' '+IntToStr(total_users_limit)+' '+IntToStr(total_files)+' '+IntToStr(total_bytes div GigaByte);
   list.Insert(0,str);
   i:=13;
   user.WriteData(list.Text);
   i:=14;
   FreeStringList(list);
  except
   DebugLog('Exception in AcceptNewReport.  i='+IntToStr(i));
 end;
end;

procedure TMainThread.AcceptMeta(Server: HSocket);
var
 h: TSocket;
 j: Integer;
 str: String;
 srv,srv1: TServer;
 list: TMyStringList;
 diff: Integer;
 hostport: String;
begin
 try
   tmp_pos:=1537;
   // h:=synsock.accept(meta_socket,nil,nil);
   h := synsock.accept(Server, nil, nil);
   if (h=SOCKET_ERROR) or (h=INVALID_SOCKET) then exit;
   inc(sockets_count);
   diff:=0;
   if max_users>local_users then diff:=max_users-local_users;
   srv1:=nil;
   tmp_pos:=1538;
   for j:=0 to db_servers.Count-1 do
   begin
     srv:=db_servers.Items[j];
     if srv.logged and
      (srv.max_users>srv.num_users) and
      ((srv.max_users - srv.num_users) > diff) then
     begin
       hostport := LowerCase(srv.host) + ':' + IntToStr(srv.port);
       if redirected_servers.FindItem(Integer(False), hostport) <> -1 then Continue;
       diff:=srv.max_users - srv.num_users;
       srv1:=srv;
     end;
   end;
   tmp_pos:=1539;
   str:='';
   if srv1<>nil then
   begin
     inc(srv1.redirects);
     list:=TMyStringList.Create;
     ResolveNameToIp(srv1.host,list);
     if list.count>0 then str:=list.Strings[0]+':'+IntToStr(srv1.port);
     list.Free;
   end;
   tmp_pos:=1540;
   if str='' then
   begin
     list:=TMyStringList.Create;
     ResolveNameToIp(servername_t,list);
     if list.count>0 then str:=list.Strings[0]+':'+IntToStr(server_port[0])
     else str:='127.0.0.1:8888';
     list.Free;
   end;
   tmp_pos:=1541;
   str:=str+#13;
   synsock.Send(h, str[1], Length(str), 0);
   synsock.shutdown(h,SD_BOTH);
   synsock.closesocket(h);
   dec(sockets_count);
   tmp_pos:=1542;
  except
   DebugLog('Exception in AcceptMeta. pos='+IntToStr(tmp_pos));
 end;
end;

procedure TMainThread.SendWebPage(user: TLocalUser);
begin
 tmp_pos:=1543;
 user.localstate:=user.localstate+[locWriteOnly];
 if redirect_url='' then
 begin
   tmp_pos:=1544;
   user.WriteData('HTTP/1.0 200 OK'#13#10'Server: Apache/1.3.22'#13#10'Connection: close'#13#10);
   user.WriteData('Content-Type: text/html'#13#10#13#10);
   user.WriteData('<HTML><HEAD>'#13#10+
         '<TITLE>403 Forbidden</TITLE>'#13#10+
         '</HEAD><BODY>'#13#10+
         '<H1>Forbidden</H1>'#13#10+
         'You don''t have permission to access this server.<P>'#13#10+
         '<HR>'#13#10+
         '<ADDRESS>Apache/1.3.22 Server at localhost Port '+IntToStr(server_port[0])+'</ADDRESS>'#13#10+
         '</BODY></HTML>');
 end
 else
 begin
   tmp_pos:=1545;
   user.WriteData('HTTP/1.0 301 Moved Permanently'#13#10'Server: Apache/1.3.22'#13#10'Connection: close'#13#10);
   user.WriteData('Location: '+redirect_url+#13#10#13#10);
   {user.WriteData('Content-Type: text/html'#13#10#13#10);
   user.WriteData('<HTML><HEAD>'#13#10+
         '<TITLE>Moved</TITLE>'#13#10+
         '</HEAD><BODY>'#13#10+
         'This site moved to <a href="'+redirect_url+'">'+redirect_url+'</a>.'+
         '</BODY></HTML>');}
 end;
 tmp_pos:=1546;
end;

procedure TMainThread.CheckBots;
var
 str: String;
 loc: TLocalUser;
 i,j,k: Integer;
begin
 tmp_pos:=1547;
 if (current_time-last_announcement)>(ann_delay*1000) then
 begin // announcements bot
   last_announcement:=current_time;
   if ann_enabled then
   begin
     tmp_pos:=1548;
     j:=0;
     for i:=0 to max_announcement do
      if ann_messages[i]<>'' then inc(j);
     if j>0 then
     begin
      tmp_pos:=1549;
      repeat
         k:=random(j);
         if k=j then k:=0;
      until (ann_messages[k]<>'') and ((ann_messages[k])[1]<>'#');
      str:=ann_messages[k];
      tmp_pos:=1550;
      for i:=0 to db_local.count-1 do
      begin
        loc:=db_local.Items[i];
        if ((i mod 20)=0) then
        begin
         {$I checksync.pas}
        end;
        if loc.logged then
         if not (userHideAnnouncements in loc.data^.state) then
          loc.Exec(MSG_SERVER_ANNOUNCE,ann_user+' '+str);
      end;
     end;
   end;
 end;
 tmp_pos:=1551;
end;

procedure TMainThread.CheckMinShare;
var
 i,j,num: Integer;
 loc: TLocalUser;
 t: Cardinal;
begin
 tmp_pos:=1552;
 if minshare_fullonly and (local_users<(max_users-5)) then exit;
 if (minshare<1) and (minshare_size<1) then exit;
 t:=current_time-minshare_delay;
 num:=0;
 tmp_pos:=1553;
 for i:=0 to db_local.count-1 do
 begin
   loc:=db_local.Items[i];
   if ((i mod 30)=0) then
   begin
    {$I checksync.pas}
   end;
   tmp_pos:=1554;
   if loc.logged then
    if loc.last_seen<t then
     if loc.level<napUserModerator then
     if minshare_kickchat or (not (userChatting in loc.data^.state)) then
     begin
       j:=0;
       if (minshare_size>0) and (loc.shared_size<minshare_size) then j:=1;
       if (minshare>0) and (loc.data^.shared<minshare) then j:=2;
       if j<>0 then
       if not StrHash_FindString(db_friends,loc.nick,true) then
//       if not StrHash_FindString(db_friends,decode_ip(loc.ip),false) then
       begin
         tmp_pos:=1555;
         if j=1 then
         begin // minshare by size
           loc.Exec(MSG_SERVER_NOSUCH,GetLangT(LNG_MINSHARE3,minshare_size div MegaByte,loc.shared_size div MegaByte));
           if minshare_ban then
           begin
             if minshare_banip then
              BanUser(decode_ip(loc.ip),serveralias,minshare_bantime,GetLangT(LNG_MINSHARE_REASON2,loc.shared_size div MegaByte,serveralias)+': '+loc.nick,true)
             else
              BanUser(loc.nick,serveralias,minshare_bantime,GetLangT(LNG_MINSHARE_REASON2,loc.shared_size div MegaByte,serveralias),true);
           end;
           tmp_pos:=1556;
           AddReconnector(decode_ip(loc.ip));
           DisconnectUser(loc,'',GetLangT(LNG_MINSHARE4,loc.nick,loc.software,loc.shared_size div MegaByte),'CheckMinShare',false);
         end
         else
         begin // minshare by number of files
           tmp_pos:=1557;
           loc.Exec(MSG_SERVER_NOSUCH,GetLangT(LNG_MINSHARE,minshare,loc.data^.shared));
           if minshare_ban then
           begin
            if minshare_banip then
             BanUser(decode_ip(loc.ip),serveralias,minshare_bantime,GetLangT(LNG_MINSHARE_REASON,loc.data^.shared,serveralias)+': '+loc.nick,true)
            else
             BanUser(loc.nick,serveralias,minshare_bantime,GetLangT(LNG_MINSHARE_REASON,loc.data^.shared,serveralias),true);
           end;
           tmp_pos:=1558;
           AddReconnector(decode_ip(loc.ip));
           DisconnectUser(loc,'',GetLangT(LNG_MINSHARE2,loc.nick,loc.software,loc.data^.shared),'CheckMinShare',false);
         end;
         inc(num);
         if minshare_fullonly and minshare_only10 then
          if num>=10 then exit;
         tmp_pos:=1559;
       end;
     end;
 end;
 tmp_pos:=1560;
end;

procedure TMainThread.Check180;
var
 loc: TLocalUser;
 i: Integer;
begin
 tmp_pos:=1561;
 if (current_time-last_period180)<180000 then exit;
 inc(last_period180,180000);
 db_whowas.Clear;
 db_reconnect.Clear;
 ResetWantQueueControl;
 ResetDLRequestControl;
 tmp_pos:=1562;
 if num_servers>0 then
  for i:=0 to db_local.count-1 do
  begin
    loc:=db_local.Items[i];
    if ((i mod 50)=0) then
    begin
     {$I checksync.pas}
    end;
    tmp_pos:=1563;
    if loc.logged then
    begin
      if locNeedsUpdate in loc.localstate then
       if loc.data<>nil then
        UpdateUser(loc.data);
      loc.localstate:=loc.localstate-[locNeedsUpdate];
    end;
  end;
 tmp_pos:=1564;
 ExpireLists;
 tmp_pos:=1565;
end;

procedure TMainThread.Check30;
var
 i: Integer;
 srv: TServer;
 linking: Boolean;
begin
 tmp_pos:=1566;
 if (current_time-last_period30)<30000 then exit;
 inc(last_period30,30000);
 CheckMinShare;
 CheckBlockedShare;
 CountStats;
 tmp_pos:=1567;
 if (not autolink_only1) or (num_servers=0) then
 begin
  linking:=false;
  for i:=0 to db_servers.count-1 do
  begin
   srv:=db_servers.Items[i];
   //if TServer(db_servers.Items[i]).thread<>nil then
   if srv.thread<>nil then
    linking:=true
   else if (current_time-srv.login_start)<30000 then
    linking:=true;
  end;
  for i:=0 to db_servers.count-1 do
  if not linking then
  begin
   srv:=db_servers.Items[i];
   if srv.connected=conNotConnected then
    if srv.relink>0 then
     if (current_time-srv.login_start)>srv.relink then
     begin
       tmp_pos:=1568;
       Handler_ServerConnect(srv,true);
       tmp_pos:=1569;
       linking:=true;
     end;
  end;
 end;
 tmp_pos:=1571;
 Check180;
 tmp_pos:=1572;
end;

procedure TMainThread.Check60;
var
 str: String;
 list: TMyStringList;
 i: Integer;
 f: TFileStream;
 srv: TServer;
 loc: TLocalUser;
begin
 tmp_pos:=1573;
 if (current_time-last_period)<60000 then exit;
 inc(last_period,60000);
 users_per_minute:=0;
 db_bans.Expire;
 CountStats;
 tmp_pos:=1574;
 WriteAllServers(MSG_SERVER_STATS,'',IntToStr(local_users)+' '+IntToStr(local_files)+' '+IntToStr(local_bytes)+' '+IntToStr(max_users));
 {$I checksync.pas}
 ResetSearchControl;
 tmp_pos:=1575;
 //if mx_useping then
 if blocked_clients[softWinMXHidden] then
 for i:=0 to db_local.count-1 do
 begin
   loc:=db_local.Items[i];
   if (i mod 20)=0 then
   begin
    {$I checksync.pas}
   end;
   if (loc.shared<>nil) and
     (loc.shared.reindex=true) then
      loc.shared.DoReindex;
   if loc.logged and (not (locPingable in loc.localstate))
    and (loc.softwareId<>softWinMXNormal) and (loc.softwareId<>softWinMXJap) then
     if (current_time-WINMX_DETECT_TIME)>loc.last_seen then
      CheckWinMX(loc);
 end;
 KillIdleUser;
 tmp_pos:=1576;
 linking:=false;
 for i:=0 to db_servers.count-1 do
 begin
   srv:=db_servers.Items[i];
   if srv.connected=conConnecting then linking:=true
   else if (srv.connected=conConnected) and (srv.logged=false) then linking:=true;
 end;
 tmp_pos:=1577;
 inc(total_bytes_in,bytes_in);
 last_bytes_in:=bytes_in;
 bytes_in:=0;
 inc(total_bytes_out,bytes_out);
 last_bytes_out:=bytes_out;
 bytes_out:=0;
 inc(total_searches,num_searches);
 last_searches:=num_searches;
 num_searches:=0;
 inc(total_browses,num_browses);
 last_browses:=num_browses;
 num_browses:=0;
 inc(total_transfers,num_transfers);
 last_transfers:=num_transfers;
 num_transfers:=0;
 last_login:=num_login;
 num_login:=0;
 last_rejects:=num_rejects;
 num_rejects:=0;
 if not save_stats then exit;
 try
  if not FileExists(ApplicationDir+'serverstats') then
   f:=TFileStream.Create(ApplicationDir+'serverstats',fmCreate)
  else
   f:=TFileStream.Create(ApplicationDir+'serverstats',fmOpenWrite or fmShareDenyWrite);
  f.Seek(0,soFromEnd);
  except
   DebugLog('Error: cannot open file "serverstats"');
   exit;
 end;
 tmp_pos:=1578;
 list:=CreateStringList;
 list.Add(DateToStr(now));  // date
 list.Add(TimeToStr(now));  // time
 list.Add(IntToStr(current_time_t-start_time_t)); // time online in seconds
 list.Add(IntToStr(local_users)); // local users
 list.Add(IntToStr(local_files)); // local files
 list.Add(IntToStr(local_bytes)); // local size
 list.Add(IntToStr(total_users)); // total users
 list.Add(IntToStr(total_files)); // total files
 list.Add(IntToStr(total_bytes)); // total bytes
 list.Add(IntToStr(last_login)); // logins for the last minute
 list.Add(IntToStr(last_rejects)); // rejects for the last minute
 list.Add(IntToStr(last_searches)); // searches for the last minute
 list.Add(IntToStr(last_transfers)); // transfers for the last minute
 list.Add(IntToStr(AllocMemSize)); // memory used in bytes
 list.Add(IntToStr(sockets_count)); // used sockets
 list.Add(IntToStr(last_bytes_in)); // bytes in
 list.Add(IntToStr(last_bytes_out)); // bytes out
 list.Add(IntToStr(num_servers)); // linked servers
 str:=list.Strings[0];
 for i:=1 to list.count-1 do str:=str+','+Trim(list.Strings[i]);
 str:=str+#10;
 tmp_pos:=1579;
 FreeStringList(list);
 try
  f.Write(str[1],Length(str));
  f.Free;
  except
 end;
 tmp_pos:=1580;
end;

procedure TMainThread.CheckServers;
var
 i: Integer;
 srv: TServer;
begin
 tmp_pos:=1581;
 for i:=0 to db_servers.Count-1 do
 if TServer(db_servers.ITems[i]).connected=conConnected then
 begin
   tmp_pos:=1582;
   {$I checksync.pas}
   srv:=db_servers.Items[i];
   num_processed:=0;
   // send
   srv.Compile;
   srv.Flush;
   // receive
   srv.Receive(0);
   // send
   srv.Compile;
   srv.Flush;
   tmp_pos:=1583;
   if (srv.connected=conConnected) and (srv.logged=false) then
    if (current_time-srv.login_start)>MAX_LOGIN_TIMEOUT then
    begin
      DisconnectServer(srv,false,false,'CheckServers');
      Wallop(MSG_SERVER_NOSUCH,wallopServer,GetLangT(LNG_LINKERRLOGIN,srv.host),true);
    end;
 end;
 tmp_pos:=1584;
end;

procedure TMainThread.CheckTimeouts;
var
 i: Integer;
 user: TLocalUser;
begin
 try
   tmp_pos:=1585;
   for i:=db_local.count-1 downto 0 do
   begin
     tmp_pos:=1586;
     user:=db_local.Items[i];
     if ((i mod 30)=0) then
     begin
      {$I checksync.pas}
     end;
     tmp_pos:=1587;
     if user<>cons then
     begin
       if user.socket=INVALID_SOCKET then
       try
        FreeLocalUser(user);
        db_local.Delete(i);
        except
         on E:Exception do
         DebugLog('Exception in TMainThread::CheckData (1) : '+E.Message);
       end
       else
        if not user.logged then
         if (current_time-user.last_seen)>timeout_login then
         try
          FreeLocalUser(user);
          db_local.Delete(i);
         except
          on E:Exception do
           DebugLog('Exception in TMainThread::CheckData (2) : '+E.Message);
         end;
     end;
   end;
  except
   DebugLog('Exception in CheckTimeouts. pos='+IntToStr(tmp_pos));
 end;
 tmp_pos:=1588;
end;

procedure TMainThread.ReceiveData;
var
 i: Integer;
 user: TLocalUser;
 str: String;
begin
 try
   for i:=0 to db_local.count-1 do
   begin
     if (i mod 20)=10 then
     begin
      {$I checksync.pas}
     end;
     if (i mod 100)=30 then CheckServers;
     user:=db_local.Items[i];
     if user<>cons then
       begin
         if user.last_seen<>0 then
           ReceiveData(user,0,0);
         if not running then exit;
       end;
   end;
   if (current_time-last_stats)>stats_delay then
   begin
    last_stats:=current_time;
    CountStats;
    str:=IntToStr(total_users)+' '+IntToStr(total_files)+' '+IntToStr(total_bytes div 1073741824);
    for i:=0 to db_local.Count-1 do
    begin
     if (i mod 50)=10 then
     begin
      {$I checksync.pas}
     end;
     user:=db_local.Items[i];
     if user<>cons then
      if user.logged then
       user.Exec(MSG_SERVER_STATS,str);
    end;
   end;
  except
   DebugLog('Exception in ReceiveData 1');
 end;
end;

procedure TMainThread.ReceiveData(user: TLocalUser; counter, recurse: Integer);
var
 i,num,len,recv_max,rev,tag,old_mem,new_mem: Integer;
 last_error: Integer;
begin
 num:=0; // debug variable
 try
   if user=nil then exit;
   num:=1;
   if user.socket=INVALID_SOCKET then exit;
   if user.last_seen=0 then exit;
   if user.logged then
    if user.searches_count>0 then
     if (current_time-user.last_search_time)>timeout_remote_search then
     begin
       num:=2;
       user.searches_count:=0;
       user.Exec(MSG_SERVER_SEARCH_END,'');
       num:=3;
     end;
   num:=4;
   user.Flush;
   num:=5;
   if (user.logged=true) and (CanReceive(false)=false) then exit;
   if user.last_seen=0 then exit;
   if locWriteOnly in user.localstate then exit;
   num:=6;
   if Length(user.recv_buf)=0 then SetLength(user.recv_buf,RECV_BUF_SIZE_DEF);
   num:=7;
   while true do
   begin
     num:=8;
     if not running then exit;
     if user.last_seen=0 then exit;
     if user.socket=INVALID_SOCKET then exit;
     if (user.logged=true) and (CanReceive(false)=false) then exit;
     num:=9;
     if counter>=MAX_COMMANDS_PER_CYCLE then exit;
     if linking then if counter>=MAX_COMMANDS_PER_CYCLE2 then exit;
     if TCPSocket_CanRead(user.socket,0,last_error) then
     begin
       num:=10;
       recv_max:=Length(user.recv_buf)-user.recv_len;
       num:=11;
       if recv_max=0 then
       begin // increase buffer
         num:=12;
         if user.recv_len>(RECV_BUF_SIZE_MAX-4) then
         begin
           num:=13;
           DisconnectUser(user,'',GetLangT(LNG_DISCONNECT_INVCMD,user.nick,user.software+', '+decode_ip(user.ip)),'ReceiveData (1)',true);
           exit;
         end;
         num:=14;
         SetLength(user.recv_buf,user.recv_len+RECV_BUF_SIZE_MIN); // increase buffer length if needed
         inc(recv_max,RECV_BUF_SIZE_MIN);
         num:=15;
       end;
       num:=16;
       len:=TCPSocket_RecvBuffer(user.socket,PChar(@user.recv_buf[user.recv_len+1]),recv_max,last_error);
       num:=17;
       if last_error=WSAEWOULDBLOCK then exit;
       num:=19;
       if last_error<>0 then
       begin
         num:=20;
         DisconnectUser(user,'',GetLangT(LNG_DISCONNECT_SOCKETERR,user.nick,user.software,IntToStr(last_error),GetErrorDesc(last_error)),'ReceiveData (2)',true);
         exit;
       end;
       num:=21;
       inc(user.recv_len,len);
       inc(bytes_in,len);
       inc(bandwidth_down,len);
     end
     else if last_error<>0 then
      if last_error<>WSAEWOULDBLOCK then
      begin
        DisconnectUser(user,'',GetLangT(LNG_DISCONNECT_SOCKETERR,user.nick,user.software,IntToStr(last_error),GetErrorDesc(last_error)),'ReceiveData (2a)',true);
        exit;
      end;
     num:=22;
     if user.recv_len<4 then exit;
     if locSwapBytes in user.localstate then
      len:=Ord(user.recv_buf[2])+256*Ord(user.recv_buf[1])
     else
     begin
      num:=23;
      len:=Ord(user.recv_buf[1])+256*Ord(user.recv_buf[2]);
      rev:=Ord(user.recv_buf[4])+256*Ord(user.recv_buf[3]);
      num:=24;
      if not user.logged then
      begin
        if (rev=2) or (rev=4) or (rev=6) or (rev=7) or (rev=11) or (rev=920) then
        begin // checking for big-endian (possible senders: Napster 10.3+, linked server, some buggy client)
          num:=25;
          user.localstate:=user.localstate+[locSwapBytes];
          len:=Ord(user.recv_buf[2])+256*Ord(user.recv_buf[1]);
          num:=26;
        end
        else
        begin // checking HTTP client
         if (Copy(user.recv_buf,1,3)='GET') or (Copy(user.recv_buf,1,4)='POST') then
         begin // web server
           SendWebPage(user);
           exit;
         end;
        end;
      end;
      num:=27;
     end;
     num:=28;
     if len>(RECV_BUF_SIZE_MAX-4) then
     begin
       num:=29;
       DisconnectUser(user,'',GetLangT(LNG_DISCONNECT_INVCMD,user.nick,user.software+', '+decode_ip(user.ip)),'ReceiveData (3)',true);
       exit;
     end;
     num:=30;
     if (len+4)>Length(user.recv_buf) then
     begin // increase buffer
       num:=31;
       tag:=((len+4) div RECV_BUF_SIZE_MIN);
       num:=32;
       SetLength(user.recv_buf,tag*RECV_BUF_SIZE_MIN);
       num:=33;
       if recurse>=MAX_RECURSE then exit;
       num:=34;
       ReceiveData(user,counter,recurse+1);
       exit;
     end;
     num:=35;
     if user.recv_len<(len+4) then exit;
     num:=36;
     while len<(user.recv_len-3) do // processing received buffer
     begin
       num:=37;
       if user.recv_len<4 then exit;
       if (user.recv_len-4)<len then exit;
       num:=38;
       if locSwapBytes in user.localstate then
        tag:=Ord(user.recv_buf[4])+256*Ord(user.recv_buf[3])
       else
        tag:=Ord(user.recv_buf[3])+256*Ord(user.recv_buf[4]);
       num:=39;
       SetLength(gcmd.cmd,len);
       num:=40;
       if len>0 then Move(user.recv_buf[5],gcmd.cmd[1],len);
       num:=41;
       Move(user.recv_buf[len+5],user.recv_buf[1],user.recv_len-len-4);
       num:=42;
       dec(user.recv_len,len+4);
       num:=43;
       try
        {$IFDEF CHECK_LEAK}
        old_mem:=AllocMemSize;
        {$ENDIF}
        gcmd.id:=tag;
        if gcmd.id=870 then inc(counter,3)
        else if gcmd.id=200 then
        begin
         if num_servers>0 then
          inc(counter,5)
         else
          inc(counter,1);
        end;
        if not ProcessCommand(user,queryNormal) then
        begin
          {$IFDEF CHECK_LEAK}
          new_mem:=AllocMemSize;
          if (new_mem-old_mem)>POSSIBLE_LEAK then
           DebugLog('Possible leak in ProcessCommand(1,'+IntToStr(tag)+','+gcmd.cmd+'): '+IntToStr(new_mem-old_mem)+' bytes allocated');
          {$ENDIF}
          exit;
        end;
        {$IFDEF CHECK_LEAK}
        new_mem:=AllocMemSize;
        if (new_mem-old_mem)>POSSIBLE_LEAK then
         DebugLog('Possible leak in ProcessCommand(2,'+IntToStr(tag)+','+gcmd.cmd+'): '+IntToStr(new_mem-old_mem)+' bytes allocated');
        {$ENDIF}
        except
         on E:Exception do
         begin
          DebugLog('Exception in TMainThread::ReceiveData (tag='+IntToStr(tag)+') : '+E.Message);
          {$IFDEF CHECK_LEAK}
          new_mem:=AllocMemSize;
          if (new_mem-old_mem)>POSSIBLE_LEAK then
           DebugLog('Possible leak in ProcessCommand(3,'+IntToStr(tag)+','+gcmd.cmd+'): '+IntToStr(new_mem-old_mem)+' bytes allocated');
          {$ENDIF} 
          exit;
         end;
       end;
       num:=44;
       {$I checksync.pas}
       num:=45;
       if user.last_seen=0 then exit;
       inc(counter);
       num:=46;
       if (counter>=MAX_COMMANDS_PER_CYCLE) or (linking and (counter>=MAX_COMMANDS_PER_CYCLE2)) then
       begin
        num:=47;
        exit;
       end;
       num:=48;
       if user.recv_len>3 then
       begin
         num:=49;
         if locSwapBytes in user.localstate then
          len:=Ord(user.recv_buf[2])+256*Ord(user.recv_buf[1])
         else
          len:=Ord(user.recv_buf[1])+256*Ord(user.recv_buf[2]);
         num:=50;
         if (len+4)>Length(user.recv_buf) then
         begin
           num:=51;
           if len>(RECV_BUF_SIZE_MAX-4) then
           begin
             num:=52;
             DisconnectUser(user,'',GetLangT(LNG_DISCONNECT_INVCMD,user.nick,user.software+', '+decode_ip(user.ip)),'ReceiveData (4)',true);
             exit;
           end;
           num:=53;
           tag:=((len+4) div RECV_BUF_SIZE_MIN);
           SetLength(user.recv_buf,tag*RECV_BUF_SIZE_MIN); // increase buffer length if needed
           num:=54;
           if recurse>=MAX_RECURSE then exit;
           ReceiveData(user,counter,recurse+1);
           num:=55;
           exit;
         end;
         num:=56;
       end
       else
        len:=user.recv_len+1;
       num:=57;
     end; // loop
     num:=58;
   end;
   num:=59;
  except
   DebugLog('Exception in ReceiveData 2  num='+IntToStr(num)+' i='+IntToStr(i)+' len='+IntToStr(len)+' recv_max='+IntToStr(recv_max)+' rev='+IntToStr(rev)+' tag='+IntToStr(tag));
 end;
end;

procedure TMainThread.ResetSearchControl;
var
 i: Integer;
 loc: TLocalUser;
begin
 for i:=db_local.count-1 downto 0 do
 try
   loc:=db_local.Items[i];
   if (i mod 50)=0 then
   begin
    {$I checksync.pas}
   end;
   if loc.logged then
    if loc.searchespm<>999 then
    begin
      if loc.searchespm>=flood_max_searches then
      if loc.level<NapUserModerator then
        loc.Exec(MSG_SERVER_NOSUCH,'1分間に行う検索が多すぎます: '+IntToStr(loc.searchespm));
     loc.searchespm:=0;
    end;
  except
 end;
end;

procedure TMainThread.CloseSockets;
var
 i, k: Integer;
 item: PNapCmd2;
begin
 for i:=db_closed.count-1 downto 0 do
 begin
   item:=db_closed.Items[i];
   if (current_time-item^.id1)>5000 then
   begin
//DebugLog('Closing socket');   
    synsock.shutdown(item^.id2,SD_BOTH);
    synsock.closesocket(item^.id2);
    dec(sockets_count);
    db_closed.Delete(i);
   end;
 end;
end;

procedure TMainThread.SynchronizeConsole;
begin
 tmp_pos:=1604;
 if (current_time-last_sync)<CONSOLE_SYNC then exit;
 last_sync:=current_time;
 try
  tmp_pos:=1605;
  Synchronize(SyncData);
  tmp_pos:=1606;
  if sync_reply_list.Count>0 then
   Synchronize(SyncLog);
  except
 end;
 tmp_pos:=1607;
end;

procedure TMainThread.ResetWantQueueControl;
var
 i: Integer;
 loc: TLocalUser;
begin
 for i:=db_local.count-1 downto 0 do
 try
   loc:=db_local.Items[i];
   if (i mod 50)=0 then
   begin
    {$I checksync.pas}
   end;
   if loc.logged then
    if loc.wantqueuep3m<>999 then
    begin
     if loc.level<NapUserModerator then
     if (wqfloodblock_count<>0) and (loc.wantqueuep3m>wqfloodblock_count) then
       loc.Exec(MSG_SERVER_NOSUCH,'一度に連キューしすぎです:WantQueue/上限(3分間で)='
         +IntToStr(loc.wantqueuep3m)+'/'+IntToStr(wqfloodblock_count));
     loc.wantqueuep3m:=0;
    end;
  except
 end;
end;

procedure TMainThread.ResetDLRequestControl;
var
 i: Integer;
 loc: TLocalUser;
begin
 for i:=db_local.count-1 downto 0 do
 try
   loc:=db_local.Items[i];
   if (i mod 50)=0 then
   begin
    {$I checksync.pas}
   end;
   if loc.logged then
    if loc.dlrequestsp3m<>999 then
    begin
     if loc.level<NapUserModerator then
     if (dlfloodblock_count<>0) and (loc.dlrequestsp3m>dlfloodblock_count) then
       loc.Exec(MSG_SERVER_NOSUCH,'一度にDL要求しすぎです:DL要求/上限(3分間で)='
         +IntToStr(loc.dlrequestsp3m)+'/'+IntToStr(dlfloodblock_count));
     loc.dlrequestsp3m:=0;
    end;
  except
 end;
end;

procedure TMainThread.CheckBlockedShare;
var
 i: Integer;
 loc: TLocalUser;
 t: Cardinal;
begin
 tmp_pos:=12262;
 if not shareinform then exit;
 t:=current_time-minshare_delay;
 tmp_pos:=12263;
 for i:=0 to db_local.count-1 do
 begin
   loc:=db_local.Items[i];
   if ((i mod 30)=0) then
   begin
    {$I checksync.pas}
   end;
   tmp_pos:=12264;
   if loc.logged then
   if (loc.last_seen<t) and (loc.last_seen>t-30000) then
   if not (userHideErrors in loc.data^.state) then
   begin
     if loc.blocked_incomplete<>0 then
       loc.Exec(MSG_SERVER_NOSUCH,'INCOMPLETEファイルは共有されません: '+IntToStr(loc.blocked_incomplete));
     if loc.blocked_fakeext<>0 then
       loc.Exec(MSG_SERVER_NOSUCH,'拡張子偽装ファイルは共有されません: '+IntToStr(loc.blocked_fakeext));
     if loc.blocked_toomany<>0 then
       loc.Exec(MSG_SERVER_NOSUCH,'ファイル数制限に引っかかりました: '+IntToStr(loc.blocked_toomany));
     if loc.blocked_tooshort<>0 then
       loc.Exec(MSG_SERVER_NOSUCH,'ファイル名が短すぎると共有されません: '+IntToStr(loc.blocked_tooshort));
     if loc.blocked_tooshortmp3<>0 then
       loc.Exec(MSG_SERVER_NOSUCH,'再生時間が短すぎると共有されません: '+IntToStr(loc.blocked_tooshortmp3));
     if loc.blocked_toosmall<>0 then
       loc.Exec(MSG_SERVER_NOSUCH,'サイズが小さすぎると共有されません: '+IntToStr(loc.blocked_toosmall));
     if loc.blocked_toolarge<>0 then
       loc.Exec(MSG_SERVER_NOSUCH,'サイズが大きすぎると共有されません: '+IntToStr(loc.blocked_toolarge));
     tmp_pos:=12265;
   end;
 end;
 tmp_pos:=12266;
end;

procedure TMainThread.CheckForceEnter;
const
  CHECK_DELAY = 1000; // この関数のチェック間隔(ミリ秒)。
var
 i: Integer;
 loc: TLocalUser;
 p: PStringHashItem;
begin
  tmp_pos:=12267;
  if last_force_enter > current_time - CHECK_DELAY then Exit;
  Inc(last_force_enter, CHECK_DELAY);
  if not force_enter then exit;
  tmp_pos:=12268;
  i := 0;
  while i < db_login.Count do
  begin
    loc := db_login.Items[i];
    if ((i mod 30)=0) then
    begin
     {$I checksync.pas}
    end;
    tmp_pos:=12269;
    if not loc.logged then
      db_login.Delete(i)
    else if loc.join_delay < 0 then
    begin
      gcmd.id:=MSG_CLIENT_JOIN;
      if (Pos('WinMX',loc.software)<>0) and
         (Pos('3.',loc.software)<>0) then
      begin
        gcmd.cmd:='#WinMX_v3.x'; //WinMX v3.x専用部屋
        ProcessCommand(loc, queryNormal);
      end
      else
      begin
        p:=force_enter_channel_list.first;
        while p<>nil do
        begin
          gcmd.cmd:=p^.data;
          ProcessCommand(loc, queryNormal);
          p:=p^.next;
        end;
      end;
      db_login.Delete(i);
    end
    else
    begin
      Dec(loc.join_delay, CHECK_DELAY);
      Inc(i);
    end;
  end;
  tmp_pos:=12270;
end;

procedure TMainThread.KillIdleUser;
var
 i: Integer;
 loc: TLocalUser;
begin
 if not kill_idleuser then exit;
 for i:=db_local.count-1 downto 0 do
 try
   loc:=db_local.Items[i];
   if (i mod 20)=0 then
   begin
    {$I checksync.pas}
   end;
   if loc.logged then
   if GetTickCount-loc.last_command_time>kill_idleuser_time then
   if loc.level<NapUserModerator then
   if not StrHash_FindString(db_friends,loc.nick,true) then
   begin
     AddReconnector(decode_ip(loc.ip));
     DisconnectUser(loc,'','','KillIdleUser',false);
   end;
  except
 end;
end;

end.
