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

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

 Dagsta handling

*********************************************************}
unit dagsta;

interface

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

procedure InitDagsta;
procedure CheckDagsta;
procedure DisconnectDagsta(socket_error: Integer);
procedure ConnectDagsta;

implementation

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

type
 TDagstaThread = 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: TDagstaThread;

constructor TDagstaThread.Create;
begin
 socket:=dagsta_socket;
 dagsta_salt:='';
 napi_sync:=GetTickCount;
 napi_sync_stats:=napi_sync;
 last_error:=0;
 inherited Create(false);
end;

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

procedure TDagstaThread.Execute;
var
 list: TMyStringList;
begin
 FreeOnTerminate:=true;
 list:=TMyStringList.Create;
 ResolveNameToIp(dagsta_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(dagsta_socket);
    dagsta_socket:=INVALID_SOCKET;
   except
  end;
  exit;
 end;
 TCPSocket_Connect(socket,ip,dagsta_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(dagsta_socket);
     dagsta_socket:=INVALID_SOCKET;
    except
   end;
   exit;
 end;
 TCPSocket_Block(socket,false);
 Synchronize(SyncConnected);
end;

procedure TDagstaThread.SyncConnected;
begin
 if not running then exit;
 if not log_dagsta then exit;
 Log(slDagsta,GetLangE(LNG_DAGSTA_CONNECTED));
end;

procedure TDagstaThread.SyncIP;
begin
 if not running then exit;
 if not log_dagsta then exit;
 if ip='' then Log(slDagsta,GetLangE(LNG_DAGSTA_NORESOLVE,dagsta_host))
 else Log(slDagsta,GetLangE(LNG_DAGSTA_RESOLVED,dagsta_host,ip));
end;

procedure TDagstaThread.SyncNoConnect;
begin
 if not running then exit;
 if not log_dagsta then exit;
 Log(slDagsta,GetLangE(LNG_DAGSTA_NOCONNECT,GetErrorDesc(last_error)));
end;

procedure DisconnectDagsta(socket_error: Integer);
begin
 if napi_thread<>nil then
 try
   napi_thread.Terminate;
   napi_thread:=nil;
   dagsta_socket:=INVALID_SOCKET;
  except
 end;
 if dagsta_socket<>INVALID_SOCKET then
 begin
   if log_dagsta then
    log(slDagsta,GetLangT(LNG_DAGSTA_DISCONNECT));
   DoCloseSocket(dagsta_socket);
   dagsta_socket:=INVALID_SOCKET;
 end;
 dagsta_salt:='';
end;

procedure ConnectDagsta;
begin
 if dagsta_user='' then dagsta_enabled:=false;
 if dagsta_password='' then dagsta_enabled:=false;
 if dagsta_host='' then dagsta_enabled:=false;
 if dagsta_port='' then dagsta_enabled:=false;
 if not dagsta_enabled then exit;
 if napi_thread<>nil then exit;
 if dagsta_socket<>INVALID_SOCKET then
   DoCloseSocket(dagsta_socket);
 dagsta_socket:=synsock.socket(PF_INET,integer(SOCK_STREAM),IPPROTO_TCP);
 inc(sockets_count);
 if log_dagsta then log(slDagsta,GetLangT(LNG_DAGSTA_CONNECTING));
 napi_thread:=TDagstaThread.Create;
end;

procedure InitDagsta;
begin
 napi_sync:=GetTickCount;
 napi_sync_stats:=napi_sync;
 dagsta_salt:='';
 napi_thread:=nil;
 if dagsta_user='' then dagsta_enabled:=false;
 if dagsta_password='' then dagsta_enabled:=false;
 if dagsta_host='' then dagsta_enabled:=false;
 if dagsta_port='' then dagsta_enabled:=false;
 if not dagsta_enabled then exit;
 ConnectDagsta;
end;

function WriteDagsta(str: String; show_log: Boolean=true): Boolean;
var
 last_error: Integer;
begin
 Result:=false;
 if dagsta_socket=INVALID_SOCKET then exit;
 TCPSocket_SendString(dagsta_socket,str+#13#10,last_error);
 if last_error=WSAEWOULDBLOCK then exit;
 if last_error<>0 then
 begin
   DisconnectDagsta(last_error);
   exit;
 end;
 inc(bytes_out,Length(str)+2);
 if log_dagsta and show_log then log(slDagsta,GetLangT(LNG_DAGSTA_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';
 WriteDagsta(str);
end;

procedure DagstaReceive;
var
 str: String;
 buf: Array[0..1024] of Char;
 i: Integer;
 list: TMyStringList;
 addr: String;
 last_error: Integer;
begin
 if dagsta_socket=INVALID_SOCKET then exit;
 last_error:=0;
 i:=TCPSocket_RecvBuffer(dagsta_socket,PChar(@buf[0]),1024,last_error);
 if last_error=WSAEWOULDBLOCK then exit;
 if last_error<>0 then
 begin
   DisconnectDagsta(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_dagsta then
     log(slDagsta,GetLangT(LNG_DAGSTA_ERROR,str));
   DisconnectDagsta(0);
   exit;
 end;
 case i of
    220:  begin
            if log_dagsta then
              log(slDagsta,GetLangT(LNG_DAGSTA_REPLY,str));
            if list.Count>1 then
               dagsta_salt:=list.Strings[1]
            else
               dagsta_salt:=' ';
            str:='USER '+dagsta_user;
            WriteDagsta(str);
          end;
    221:  begin
            if log_dagsta then
              log(slDagsta,GetLangT(LNG_DAGSTA_REPLY,str));
            DisconnectDagsta(0);
          end;
    300:  begin
            if log_dagsta then
              log(slDagsta,GetLangT(LNG_DAGSTA_REPLY,str));
            str:='PASS '+uppercase(StrMD5(Trim(dagsta_salt)+Trim(dagsta_password)));
            if WriteDagsta(str,false) then
             if log_dagsta then
              log(slDagsta,GetLangT(LNG_DAGSTA_SEND,'PASS *****'));
          end;
    201:  begin
            if log_dagsta then
              log(slDagsta,GetLangT(LNG_DAGSTA_REPLY,str));
            list.Clear;
            if Trim(dagsta_myip)='' then
             ResolveNameToIP(servername_t,list)
            else
             list.Add(dagsta_myip); 
            if list.count=0 then
             addr:=''
            else
             addr:=list.Strings[0];
            if addr='' then
            begin
             if log_dagsta then
              log(slDagsta,GetLangT(LNG_DAGSTA_NORESOLVE,servername_t));
             addr:='127.0.0.1';
            end;
            str:=servername_t+' '+addr;
            str:='IPPORT '+str+' '+IntToStr(dagsta_myport);
            if WriteDagsta(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;
              WriteDagsta(str);
            end;
          end;
    200:  begin
            if log_dagsta then
              log(slDagsta,GetLangT(LNG_DAGSTA_REPLY,'200 stats updated.'));
          end;
    400..1000: begin
            if log_dagsta then
              log(slDagsta,GetLangT(LNG_DAGSTA_REPLY,str));
            WriteDagsta('QUIT');
          end;
    else
          if (i<0) or (i>1000) then
           DisconnectDagsta(0);
 end;
 FreeStringList(list);
end;

procedure CheckDagsta;
var
 last_error: Integer;
begin
 try
   if napi_thread<>nil then exit;
   if dagsta_socket<>INVALID_SOCKET then
   begin
    if TCPSocket_CanRead(dagsta_socket,0,last_error) then
      DagstaReceive
    else if last_error<>0 then
      DisconnectDagsta(last_error);
   end;
   if (GetTickCount-napi_sync)>60000 then
   begin
    if dagsta_socket<>INVALID_SOCKET then
    begin
      if dagsta_salt='' then
      begin
        if log_dagsta then
         log(slDagsta,GetLangT(LNG_DAGSTA_NOWELCOME));
        DisconnectDagsta(0);
      end
      else if dagsta_autodisconnect and (local_users>=max_users) then
      begin
        if log_dagsta then
         log(slDagsta,GetLangT(LNG_DAGSTA_AUTODISCONNECT));
        DisconnectDagsta(0);
      end;
    end;
    if dagsta_socket=INVALID_SOCKET then
    begin
     if (not dagsta_autodisconnect) or (local_users<(max_users-20)) then
      if not network_hub then
       ConnectDagsta;
    end
    else
     napi_sync:=GetTickCount;
   end;
   if (GetTickCount-napi_sync_stats)>dagsta_delay then
    WriteStats;
  except
   on E:exception do
   DebugLog('Exception in CheckDagsta : '+E.Message);
 end;
end;

end.

