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

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

 Napigator handling

*********************************************************}
unit napigator;

interface

uses
 SysUtils, Classes, Classes2, winsock, windows, blcksock, synsock;

procedure InitNapigator;
procedure CheckNapigator;
procedure DisconnectNapigator(socket_error: Integer);
procedure ConnectNapigator;

implementation

uses
 vars, thread, stypes, constants, lang, md5, memory_manager, handler;

type
 TNapigatorThread = class(TThread)
   ip: String;
   socket: HSocket;
   last_error: Integer;
   constructor Create;
   destructor Destroy; override;
   procedure Execute; override;
   procedure SyncIP;
   procedure SyncNoConnect;
   procedure SyncConnected;
 end;

var
 napi_sync,napi_sync_stats: Cardinal;
 napi_thread: TNapigatorThread;

constructor TNapigatorThread.Create;
begin
 socket:=napigator_socket;
 napigator_salt:='';
 napi_sync:=GetTickCount;
 napi_sync_stats:=napi_sync;
 last_error:=0;
 inherited Create(false);
end;

destructor TNapigatorThread.Destroy;
begin
 if napi_thread=nil then
 try
  if napigator_socket<>INVALID_SOCKET then
  begin
    DoCloseSocket(napigator_socket);
    napigator_socket:=INVALID_SOCKET;
  end;
  except
 end;
 napi_thread:=nil;
 inherited Destroy;
end;

procedure TNapigatorThread.Execute;
var
 list: TMyStringList;
begin
 FreeOnTerminate:=true;
 list:=TMyStringList.Create;
 ResolveNameToIp(napigator_host,list);
 if list.count>0 then ip:=list.Strings[0]
 else ip:='';
 list.Free;
 if Terminated then exit;
 Synchronize(SyncIP);
 if ip='' then
 begin
  if not Terminated then
  try
    socket:=INVALID_SOCKET;
    DoCloseSocket(napigator_socket);
    napigator_socket:=INVALID_SOCKET;
   except
  end;
  exit;
 end;
 TCPSocket_Connect(socket,ip,napigator_port,last_error);
 if not running then exit;
 napi_sync:=GetTickCount;
 napi_sync_stats:=napi_sync;
 if last_error<>0 then
 begin
   Synchronize(SyncNoConnect);
   if not Terminated then
   try
     socket:=INVALID_SOCKET;
     DoCloseSocket(napigator_socket);
     napigator_socket:=INVALID_SOCKET;
    except
   end;
   exit;
 end;
 TCPSocket_Block(socket,false);
 Synchronize(SyncConnected);
end;

procedure TNapigatorThread.SyncConnected;
begin
 if not running then exit;
 if not log_napigator then exit;
 Log(slNapigator,GetLangE(LNG_NAPIGATOR_CONNECTED));
end;

procedure TNapigatorThread.SyncIP;
begin
 if not running then exit;
 if not log_napigator then exit;
 if ip='' then Log(slNapigator,GetLangE(LNG_NAPIGATOR_NORESOLVE,napigator_host))
 else Log(slNapigator,GetLangE(LNG_NAPIGATOR_RESOLVED,napigator_host,ip));
end;

procedure TNapigatorThread.SyncNoConnect;
begin
 if not running then exit;
 if not log_napigator then exit;
 Log(slNapigator,GetLangE(LNG_NAPIGATOR_NOCONNECT,GetErrorDesc(last_error)));
end;

procedure DisconnectNapigator(socket_error: Integer);
begin
 if napi_thread<>nil then
 try
   napi_thread.Terminate;
   napi_thread:=nil;
   napigator_socket:=INVALID_SOCKET;
  except
 end;
 if napigator_socket<>INVALID_SOCKET then
 begin
   if log_napigator then
    log(slNapigator,GetLangT(LNG_NAPIGATOR_DISCONNECT));
   DoCloseSocket(napigator_socket);
   napigator_socket:=INVALID_SOCKET;
 end;
 napigator_salt:='';
end;

procedure ConnectNapigator;
begin
 if napigator_user='' then napigator_enabled:=false;
 if napigator_password='' then napigator_enabled:=false;
 if napigator_host='' then napigator_enabled:=false;
 if napigator_port='' then napigator_enabled:=false;
 if not napigator_enabled then exit;
 if napi_thread<>nil then exit;
 if napigator_socket<>INVALID_SOCKET then
   DoCloseSocket(napigator_socket);
 napigator_socket:=synsock.socket(PF_INET,integer(SOCK_STREAM),IPPROTO_TCP);
 inc(sockets_count);
 if log_napigator then log(slNapigator,GetLangT(LNG_NAPIGATOR_CONNECTING));
 napi_thread:=TNapigatorThread.Create;
end;

procedure InitNapigator;
begin
 napi_sync:=GetTickCount;
 napi_sync_stats:=napi_sync;
 napigator_salt:='';
 napi_thread:=nil;
 if napigator_user='' then napigator_enabled:=false;
 if napigator_password='' then napigator_enabled:=false;
 if napigator_host='' then napigator_enabled:=false;
 if napigator_port='' then napigator_enabled:=false;
 if not napigator_enabled then exit;
 ConnectNapigator;
end;

function WriteNapigator(str: String; show_log: Boolean=true): Boolean;
var
 last_error: Integer;
begin
 Result:=false;
 if napigator_socket=INVALID_SOCKET then exit;
 TCPSocket_SendString(napigator_socket,str+#13#10,last_error);
 if last_error=WSAEWOULDBLOCK then exit;
 if last_error<>0 then
 begin
   DisconnectNapigator(last_error);
   exit;
 end;
 inc(bytes_out,Length(str)+2);
 if log_napigator and show_log then log(slNapigator,GetLangT(LNG_NAPIGATOR_SEND,str));
 Result:=true;
end;

procedure WriteStats;
var
 str: String;
begin
 napi_sync_stats:=GetTickCount;
 str:='STATS '+servername_t+' '+IntToStr(total_users)+' '+IntToStr(total_files)+' 0 '+IntToStr(total_bytes)+' 0';
 WriteNapigator(str);
end;

procedure NapigatorReceive;
var
 str: String;
 buf: Array[0..1024] of Char;
 i: Integer;
 list: TMyStringList;
 addr: String;
 last_error: Integer;
begin
 if napigator_socket=INVALID_SOCKET then exit;
 last_error:=0;
 i:=TCPSocket_RecvBuffer(napigator_socket,PChar(@buf[0]),1024,last_error);
 if last_error=WSAEWOULDBLOCK then exit;
 if last_error<>0 then
 begin
   DisconnectNapigator(last_error);
   exit;
 end;
 if i=0 then exit;
 inc(bytes_in,i);
 SetLength(str,i);
 Move(buf[0],str[1],i);
 for i:=1 to Length(str) do
  if str[i]<#32 then str[i]:=#32;
 list:=CreateStringList;
 SplitString(Trim(str),list);
 if list.Count<1 then
 begin // weird error
   FreeStringList(list);
   exit;
 end;
 i:=StrToIntDef(list.Strings[0],-1);
 if i=-1 then
 begin // weird error - disconnecting
   FreeStringList(list);
   if log_napigator then
     log(slNapigator,GetLangT(LNG_NAPIGATOR_ERROR,str));
   DisconnectNapigator(0);
   exit;
 end;
 case i of
    220:  begin
            if log_napigator then
              log(slNapigator,GetLangT(LNG_NAPIGATOR_REPLY,str));
            if list.Count>1 then
               napigator_salt:=list.Strings[1]
            else
               napigator_salt:=' ';
            str:='USER '+napigator_user;
            WriteNapigator(str);
          end;
    221:  begin
            if log_napigator then
              log(slNapigator,GetLangT(LNG_NAPIGATOR_REPLY,str));
            DisconnectNapigator(0);
          end;
    300:  begin
            if log_napigator then
              log(slNapigator,GetLangT(LNG_NAPIGATOR_REPLY,str));
            str:='PASS '+uppercase(StrMD5(Trim(napigator_salt)+Trim(napigator_password)));
            if WriteNapigator(str,false) then
             if log_napigator then
              log(slNapigator,GetLangT(LNG_NAPIGATOR_SEND,'PASS *****'));
          end;
    201:  begin
            if log_napigator then
              log(slNapigator,GetLangT(LNG_NAPIGATOR_REPLY,str));
            list.Clear;
            if Trim(napigator_myip)='' then
             ResolveNameToIP(servername_t,list)
            else
             list.Add(napigator_myip); 
            if list.count=0 then
             addr:=''
            else
             addr:=list.Strings[0];
            if addr='' then
            begin
             if log_napigator then
              log(slNapigator,GetLangT(LNG_NAPIGATOR_NORESOLVE,servername_t));
             addr:='127.0.0.1';
            end;
            if napigator_fake_ip then addr:=encode_ip_rev(addr);
            str:=servername_t+' '+addr;
            str:='IPPORT '+str+' '+IntToStr(napigator_myport);
            if WriteNapigator(str) then
            begin
              str:='STATS '+servername_t+' '+IntToStr(total_users)+' '+IntToStr(total_files)+' 0 '+IntToStr(total_bytes)+' 0';
              napi_sync:=GetTickCount;
              napi_sync_stats:=napi_sync;
              WriteNapigator(str);
            end;
          end;
    200:  begin
            if log_napigator then
              log(slNapigator,GetLangT(LNG_NAPIGATOR_REPLY,'200 stats updated.'));
          end;
    400..1000: begin
            if log_napigator then
              log(slNapigator,GetLangT(LNG_NAPIGATOR_REPLY,str));
            WriteNapigator('QUIT');
          end;
    else
          if (i<0) or (i>1000) then
           DisconnectNapigator(0);
 end;
 FreeStringList(list);
end;

procedure CheckNapigator;
var
 last_error: Integer;
begin
 try
   if napi_thread<>nil then exit;
   if napigator_socket<>INVALID_SOCKET then
   begin
    if TCPSocket_CanRead(napigator_socket,0,last_error) then
      NapigatorReceive
    else if last_error<>0 then
      DisconnectNapigator(last_error);
   end;
   if (GetTickCount-napi_sync)>60000 then
   begin
    if napigator_socket<>INVALID_SOCKET then
    begin
      if napigator_salt='' then
      begin
        if log_napigator then
         log(slNapigator,GetLangT(LNG_NAPIGATOR_NOWELCOME));
        DisconnectNapigator(0);
      end
      else if napigator_autodisconnect and (local_users>=max_users) then
      begin
        if log_napigator then
         log(slNapigator,GetLangT(LNG_NAPIGATOR_AUTODISCONNECT));
        DisconnectNapigator(0);
      end;
    end;
    if napigator_socket=INVALID_SOCKET then
    begin
     if (not napigator_autodisconnect) or (local_users<(max_users-20)) then
      if not network_hub then
       ConnectNapigator;
    end
    else
     napi_sync:=GetTickCount;
   end;
   if (GetTickCount-napi_sync_stats)>napigator_delay then
    WriteStats;
  except
   on E:exception do
   DebugLog('Exception in CheckNapigator : '+E.Message);
 end;
end;

end.

