unit _NetRemote;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  IdTCPServer,Dialogs, StdCtrls,_NetRemote_const;

{$WARN UNIT_PLATFORM OFF}
{$WARN SYMBOL_PLATFORM OFF}

type
  TCnt = class
  private
    { Private 宣言 }
    str:string;
  public
    { Public 宣言 }
    procedure Init;
    function GetSendStr:string;
    procedure SetChar(i:char);
    procedure SetByte(i:Byte);
    procedure SetWord(i:word);
    procedure SetDWord(i:dword);
    procedure SetInteger(i:integer);
    procedure SetBool(i:bool);
    procedure SetMString(i:string);
  end;

type
  TNetRemote = class
  private
    { Private 宣言 }
    ClientAddress:string;
    Authed:boolean;
    LocalOnly:boolean;
    procedure WriteLog(msg:string);
  public
    { Public 宣言 }
    Connected:boolean;
    resCode:integer;
    resText:string;
    LoginPassword:string;
    StatusInfo:TStatusInfo;
    procedure MemInit(Port:integer;_LoginPassword:string;_LocalOnly:boolean);
    procedure MemFree;
    procedure onConnect(var AThread: TIdPeerThread);
    procedure onDisconnect;
    function CmdHnd_GETVERSION(item:string):string;
    function CmdHnd_LOGIN(Password:string):string;
    function CmdHnd_LOGOUT:string;
    function CmdHnd_GETSTATUS(item:string):string;
    function CmdHnd_CHDRV(drive:string):string;
    function CmdHnd_CHDIR(path:string):string;
    function CmdHnd_EXECUTE(mode:string):string;
    function CmdHnd_GETDIR:string;
    function CmdHnd_SETSEEK(ClockStr:string):string;
    function CmdHnd_PLAY(filename:string):string;
    function CmdHnd_SETTOTALVOLUME(TotalVolumeStr:string):string;
    function CmdHnd_SETTRANSTEMPO(TransTempoStr:string):string;
    function CmdHnd_SETPLAYMODE(PlayModeStr:string):string;
  end;

implementation

uses MainWin,
     _MDXWin_const,_MainFileCtl,_MDXWinINI,_SndDrv,_CurrentFiles,_KeyDef_const,_KeyDef;

procedure TCnt.Init;
begin
  str:='';
end;

function TCnt.GetSendStr:string;
var
  size:dword;
  s:string;
begin
  size:=length(str);
  s:=char(size and $ff)+char((size shr 8) and $ff)+char((size shr 16) and $ff)+char((size shr 24) and $ff);
  Result:=s+str;
end;

procedure TCnt.SetChar(i:char);
begin
  str:=str+i;
end;

procedure TCnt.SetByte(i:Byte);
begin
  str:=str+char(i);
end;

procedure TCnt.SetWord(i:word);
begin
  str:=str+char(i and $ff)+char((i shl 8) and $ff);
end;

procedure TCnt.SetDWord(i:dword);
begin
  str:=str+char(i and $ff)+char((i shr 8) and $ff)+char((i shr 16) and $ff)+char((i shr 24) and $ff);
end;

procedure TCnt.SetInteger(i:integer);
begin
  SetDWord(dword(i));
end;

procedure TCnt.SetBool(i:bool);
begin
  if i=True then begin
    str:=str+char($00);
    end else begin
    str:=str+char($ff);
  end;
end;

procedure TCnt.SetMString(i:string);
begin
  str:=str+char(length(i))+i;
end;

// ----------------------------------------------------------------------
// ----------------------------------------------------------------------
// ----------------------------------------------------------------------

procedure TNetRemote.WriteLog(msg:string);
var
  tf:TextFile;
begin
  AssignFile(tf,'NetRemote.log');
  if FileExists('NetRemote.log')=False then begin
    Rewrite(tf);
    end else begin
    Append(tf);
  end;
  Writeln(tf,msg);
  CloseFile(tf);
end;

procedure TNetRemote.MemInit(Port:integer;_LoginPassword:string;_LocalOnly:boolean);
begin
  LoginPassword:=_LoginPassword;
  LocalOnly:=_LocalOnly;

  Connected:=False;
  ClientAddress:='';
  Authed:=False;

  WriteLog('StartNetRemoteServer.');
  WriteLog('ListenPort:'+inttostr(Port));

  StatusInfo.MemSync:=TMultiReadExclusiveWriteSynchronizer.Create;
  ClearStatus(StatusInfo);
end;

procedure TNetRemote.MemFree;
begin
  Connected:=False;
  ClientAddress:='';
  Authed:=False;
  StatusInfo.MemSync.Free;

  WriteLog('CloseNetRemoteServer.');
end;

procedure TNetRemote.onConnect(var AThread: TIdPeerThread);
var
  IP:string;
begin
  IP:='';
  if AThread<>nil then begin
    if AThread.Connection<>nil then begin
      if AThread.Connection.Socket<>nil then begin
        if AThread.Connection.Socket.Binding<>nil then begin
          IP:=AThread.Connection.Socket.Binding.PeerIP;
        end;
      end;
    end;
  end;

  if IP='' then begin
    AThread.Connection.Disconnect;
    exit;
  end;

  WriteLog('ConnectRequest from '+IP);

  if LocalOnly=True then begin
    if (copy(IP,1,8)<>'192.168.') and (IP<>'127.0.0.1') then begin
      WriteLog('ローカルアドレスからの接続のみ許可されています。要求は破棄されます。');
      AThread.Connection.Disconnect;
      exit;
    end;
  end;

  Connected:=True;
  ClientAddress:=IP;
  Authed:=False;
end;

procedure TNetRemote.onDisconnect;
begin
  Connected:=False;
  ClientAddress:='';
  Authed:=False;
end;

function TNetRemote.CmdHnd_GETVERSION(item:string):string;
begin
  Result:='';
  resCode:=400;
  resText:='Bad Request (NotfoundVersionItem)';

  if item=gvInterface then begin
    Result:=InterfaceVersion;
    resCode:=200;
    resText:='OK';
  end;

  if item=gvServer then begin
    Result:=MDXWinCurrent;
    resCode:=200;
    resText:='OK';
  end;
end;

function TNetRemote.CmdHnd_LOGIN(Password:string):string;
begin
  Result:='';
  resCode:=0;

  if (resCode=0) and (LoginPassword='') then begin
    Authed:=False;
    resCode:=503;
    resText:='Service Unavailable (NotfoundServerPassword)';
  end;

  if resCode=0 then begin
    if LoginPassword<>Password then begin
      Authed:=False;
      resCode:=401;
      resText:='Unauthorized';
      end else begin
      Authed:=True;
      resCode:=200;
      resText:='OK';
    end;
  end;
end;

function TNetRemote.CmdHnd_LOGOUT:string;
begin
  Result:='';
  Authed:=False;
  resCode:=200;
  resText:='OK';
end;

function TNetRemote.CmdHnd_GETSTATUS(item:string):string;
var
  Cnt:TCnt;
  mode:integer;
begin
  Result:='';
  if Authed=False then begin
    resCode:=401;
    resText:='Unauthorized';
    exit;
  end;

  mode:=0;
  if item='High' then mode:=1;
  if item='Middle' then mode:=2;
  if item='Low' then mode:=3;

  if mode=0 then begin
    resCode:=400;
    resText:='Bad Request';
    exit;
  end;

  Cnt:=TCnt.Create;
  Cnt.Init;
  with StatusInfo do begin
    MemSync.BeginRead;
    case mode of
      1: begin
        Cnt.SetDWord(PlayTime);
        Cnt.SetDWord(DispClock);
        Cnt.SetDWord(TotalClock);
        Cnt.SetDWord(MesureClock);
        Cnt.SetInteger(TotalVolume);
        Cnt.SetInteger(TransTempo);
      end;
      2: begin
        Cnt.SetMString(CurrentPath);
        Cnt.SetInteger(LoopCount);
        Cnt.SetByte(TimerBCount);
        Cnt.SetBool(fPlay);
        Cnt.SetBool(fPause);
        Cnt.SetBool(fStop);
        Cnt.SetInteger(PlayMode);
      end;
      3: begin
        Cnt.SetMString(PlayFilename);
        Cnt.SetMString(PlayTitle);
        Cnt.SetInteger(PlayIndex);
      end;
    end;
    MemSync.EndRead;
  end;
  Result:=Cnt.GetSendStr;
  Cnt.Free;

  resCode:=200;
  resText:='OK';
end;

function TNetRemote.CmdHnd_CHDRV(drive:string):string;
begin
  Result:='';
  if Authed=False then begin
    resCode:=401;
    resText:='Unauthorized';
    exit;
  end;

  MainFileCtl.ChangeDrive(drive);

  resCode:=200;
  resText:='OK';
end;

function TNetRemote.CmdHnd_CHDIR(path:string):string;
begin
  Result:='';
  if Authed=False then begin
    resCode:=401;
    resText:='Unauthorized';
    exit;
  end;

  if path='..' then begin
    MainFileCtl.MoveDirToUpper;
    end else begin
    MainFileCtl.ChangeDir(path);
  end;

  resCode:=200;
  resText:='OK';
end;

function TNetRemote.CmdHnd_EXECUTE(mode:string):string;
var
  unknown:boolean;
begin
  Result:='';
  if Authed=False then begin
    resCode:=401;
    resText:='Unauthorized';
    exit;
  end;

  unknown:=True;

  if mode='PushPlay' then begin
    KeyDef.ExecuteCommand(DCT_sdPlay);
    unknown:=False;
  end;
  if mode='PushStop' then begin
    KeyDef.ExecuteCommand(DCT_sdStop);
    unknown:=False;
  end;

  if unknown=True then begin
    resCode:=400;
    resText:='Bad Request';
    end else begin
    resCode:=200;
    resText:='OK';
  end;
end;

function TNetRemote.CmdHnd_GETDIR:string;
var
  loop:boolean;
  Cnt:TCnt;
  y:integer;
  Item:TFilesItem;
begin
  Result:='';
  if Authed=False then begin
    resCode:=401;
    resText:='Unauthorized';
    exit;
  end;

  loop:=True;
  while (loop=True) do begin
    loop:=False;
    if Main.FileGridTimer.Enabled=True then loop:=True;
    if Main.FilereadBusyFlag=True then loop:=True;
    if loop=True then sleep(100);
  end;

  Cnt:=TCnt.Create;
  Cnt.Init;
  Cnt.SetInteger(CurrentFiles.GetItemsCount);
  for y:=0 to CurrentFiles.GetItemsCount-1 do begin
    CurrentFiles.GetItem(y,Item);
    Cnt.SetByte(Item.Attr);
    Cnt.SetMString(Item.Name);
    Cnt.SetMString(Item.Value);
  end;
  Cnt.SetInteger(CurrentFiles.GetCursorIndex);
  Result:=Cnt.GetSendStr;
  Cnt.Free;

  resCode:=200;
  resText:='OK';
end;

function TNetRemote.CmdHnd_SETSEEK(ClockStr:string):string;
var
  Clock:integer;
begin
  Result:='';
  if Authed=False then begin
    resCode:=401;
    resText:='Unauthorized';
    exit;
  end;

  Clock:=StrToIntDef(ClockStr,-1);
  if Clock<>-1 then sdSeek(Clock);

  resCode:=200;
  resText:='OK';
end;

function TNetRemote.CmdHnd_PLAY(filename:string):string;
begin
  Result:='';
  if Authed=False then begin
    resCode:=401;
    resText:='Unauthorized';
    exit;
  end;

  MainFileCtl.ExecutePlayFile(filename,True);

  resCode:=200;
  resText:='OK';
end;

function TNetRemote.CmdHnd_SETTOTALVOLUME(TotalVolumeStr:string):string;
var
  TotalVolume:integer;
begin
  Result:='';
  if Authed=False then begin
    resCode:=401;
    resText:='Unauthorized';
    exit;
  end;

  TotalVolume:=StrToIntDef(TotalVolumeStr,-1);
  if TotalVolume>=0 then begin
    MainINI.TotalVolume:=TotalVolume;
    sdSetTotalVolume(MainINI.TotalVolume);
    Main.VolumeBarImgRefresh;
    Main.FormPaintVolumeTempo;
  end;

  resCode:=200;
  resText:='OK';
end;

function TNetRemote.CmdHnd_SETTRANSTEMPO(TransTempoStr:string):string;
var
  TransTempo:integer;
begin
  Result:='';
  if Authed=False then begin
    resCode:=401;
    resText:='Unauthorized';
    exit;
  end;

  TransTempo:=StrToIntDef(TransTempoStr,0);
  Main.TempoBarImgRefresh(TransTempo);

  resCode:=200;
  resText:='OK';
end;

function TNetRemote.CmdHnd_SETPLAYMODE(PlayModeStr:string):string;
var
  pm:integer;
begin
  Result:='';
  if Authed=False then begin
    resCode:=401;
    resText:='Unauthorized';
    exit;
  end;

  pm:=StrToIntDef(PlayModeStr,PlayMode_None);

  if (0<=pm) and (pm<PlayModeCount) then begin
    MainINI.PlayMode:=pm;
    Main.PlayButtonsCallback;
    resCode:=200;
    resText:='OK';
    end else begin
    resCode:=400;
    resText:='Bad Request';
  end;
end;

end.
