
{******************************************}
{                                          }
{             FastReport v4.0              }
{        Report Server Configurator        }
{                                          }
{         Copyright (c) 1998-2011          }
{          by Alexander Fediachov,         }
{             Fast Reports Inc.            }
{                                          }
{******************************************}

unit frxServerConfig;

{$I frx.inc}

interface

{$R frxServerConfig.res}

uses Windows, Classes, SysUtils, SyncObjs, frxXML, frxUtils,
     frxServerUtils, inifiles, frxServerLog;

type
  TfrxConfig = class (TObject)
  private
    FXML: TfrxXMLDocument;
    FErrors: TStrings;
    FLines: TStringList;
    FCS: TCriticalSection;
    FFileName: String;
    FConfigFolder: String;
    procedure UpdateLines;
    procedure AddLine(const Name: String; const Value: String; XMLItem: TfrxXMLItem);
    function GetCount: Integer;
  public
    ServerMessage: String;
    constructor Create;
    destructor Destroy; override;
    function LoadFromStream(Stream: TStream): HRESULT;
    function SaveToStream(Stream: TStream): HRESULT;
    function LoadFromFile(const FileName: String): HRESULT;
    function SaveToFile(const FileName: String): HRESULT;
    function GetValue(const Name: String): String;
    function GetNumber(const Name: String): Integer;
    function GetBool(const Name: String): Boolean;
    function CheckValue(const Name: String; const Current: String): String;
    procedure SetValue(const Name: String; const Value: String);
    procedure SetBool(const Name: String; const Value: Boolean);
    procedure Clear;
    procedure ConfigListToFile(const FileName: String);
    procedure Reload;

    property Lines: TStringList read FLines;
    property XML: TfrxXMLDocument read FXML;
    property Count: Integer read GetCount;
    property ConfigFolder: String read FConfigFolder write FConfigFolder;
  end;

  TfrxConfigItem = class (TObject)
  private
    FValue: String;
    FXMLItem: TfrxXMLItem;
  public
    property Value: String read FValue write FValue;
    property XMLItem: TfrxXMLItem read FXMLItem write FXMLItem;
  end;

  TfrxServerConfig = class(TPersistent)
  private
    FGzip: Boolean;
    FIndexFileName: String;
    FLogging: Boolean;
    FLogin: String;
    FLogPath: String;
    FMIC: Boolean;
    FNoCacheHeader: Boolean;
    FOutputFormats: TfrxServerOutputFormats;
    FPassword: String;
    FPort: Integer;
    FReportPath: String;
    FRootPath: String;
    FSessionTimeOut: Integer;
    FSocketTimeOut: Integer;
    FReportCachePath: String;
    FDefaultCacheLatency: Integer;
    FReportCaching: Boolean;
    FMaxLogFiles: Integer;
    FMaxLogSize: Integer;
    FDatabase: String;
    FDatabaseLogin: String;
    FDatabasePassword: String;
    FReportsList: Boolean;
    FConfigFolder: String;
  public
    procedure LoadFromFile(const FileName: String);
    procedure SaveToFile(const FileName: String);
  published
    property Compression: Boolean read FGzip write FGzip;
    property IndexFileName: String read FIndexFileName write FIndexFileName;
    property Logging: Boolean read FLogging write FLogging;
    property MaxLogSize: Integer read FMaxLogSize write FMaxLogSize;
    property MaxLogFiles: Integer read FMaxLogFiles write FMaxLogFiles;
    property Login: String read FLogin write FLogin;
    property LogPath: String read FLogPath write FLogPath;
    property MIC: Boolean read FMIC write FMIC;
    property NoCacheHeader: Boolean read FNoCacheHeader write FNoCacheHeader;
    property OutputFormats: TfrxServerOutputFormats read FOutputFormats
      write FOutputFormats;
    property Password: String read FPassword write FPassword;
    property Port: Integer read FPort write FPort;
    property ReportPath: String read FReportPath write FReportPath;
    property ReportCachePath: String read FReportCachePath write FReportCachePath;
    property ReportCaching: Boolean read FReportCaching write FReportCaching;
    property DefaultCacheLatency: Integer read FDefaultCacheLatency write FDefaultCacheLatency;
    property RootPath: String read FRootPath write FRootPath;
    property SessionTimeOut: Integer read FSessionTimeOut write FSessionTimeOut;
    property SocketTimeOut: Integer read FSocketTimeOut write FSocketTimeOut;
    property Database: String read FDatabase write FDatabase;
    property DatabaseLogin: String read FDatabaseLogin write FDatabaseLogin;
    property DatabasePassword: String read FDatabasePassword write FDatabasePassword;
    property ReportsList: Boolean read FReportsList write FReportsList;
    property ConfigFolder: String read FConfigFolder write FConfigFolder;
  end;

var
   ServerConfig: TfrxConfig;

const
  FR_SERVER_CONFIG_VERSION = '2.3.0';

implementation

{ TfrxConfig }

procedure TfrxConfig.AddLine(const Name: String; const Value: String; XMLItem: TfrxXMLItem);
var
  FValue: TfrxConfigItem;
  i: Integer;
begin
  i := FLines.IndexOf(LowerCase(Name));
  if i <> -1 then
  begin
    TfrxConfigItem(FLines.Objects[i]).Value := Value;
    TfrxConfigItem(FLines.Objects[i]).XMLItem := XMLItem;
  end
  else
  begin
    FValue := TfrxConfigItem.Create;
    FValue.Value := Value;
    FValue.XMLItem := XMLItem;
    FLines.AddObject(LowerCase(Name), FValue);
  end;
end;

function TfrxConfig.CheckValue(const Name, Current: String): String;
begin
  Result := GetValue(Name);
  if Result = '' then
    Result := Current;
end;

procedure TfrxConfig.Clear;
var
  i: Integer;
begin
  for i:= 0 to FLines.Count - 1 do
    TfrxConfigItem(FLines.Objects[i]).Free;
  FLines.Clear;
end;

procedure TfrxConfig.ConfigListToFile(const FileName: String);
var
  f: TFileStream;
  s: String;
  i: Integer;
begin
  f := TFileStream.Create(FileName, fmCreate);
  try
    for i := 0 to FLines.Count - 1 do
    begin
      s := FLines[i] + ' = ' + TfrxConfigItem(FLines.Objects[i]).Value + #13#10;
      f.Write(s[1], Length(s));
    end;
  finally
    f.Free;
  end;
end;

constructor TfrxConfig.Create;
begin
  FCS := TCriticalSection.Create;
  FXML := TfrxXMLDocument.Create;
  FXML.AutoIndent := True;
  FErrors := TStringList.Create;
  FLines := TStringList.Create;
  FLines.Sorted := True;
  FConfigFolder := GetAppPath;
end;

destructor TfrxConfig.Destroy;
begin
  Clear;
  FLines.Free;
  FErrors.Free;
  FXML.Free;
  FCS.Free;
  inherited;
end;

function TfrxConfig.GetBool(const Name: String): Boolean;
begin
  Result := GetValue(Name) = 'yes';
end;

function TfrxConfig.GetCount: Integer;
begin
  Result := FLines.Count;
end;

function TfrxConfig.GetNumber(const Name: String): Integer;
var
  s: String;
begin
  s := GetValue(Name);
  if s = '' then
    Result := 0
  else
    Result := StrToInt(s);
end;

function TfrxConfig.GetValue(const Name: String): String;
var
  i: Integer;
begin
  FCS.Enter;
  try
    i := FLines.IndexOf(LowerCase(Name));
    if i <> -1 then
      Result := TfrxConfigItem(FLines.Objects[i]).Value
    else
      Result := '';
  finally
    FCS.Leave;
  end;
end;

function TfrxConfig.LoadFromFile(const FileName: String): HRESULT;
var
  f: TFileStream;
  frc: TResourceStream;
begin
  try
    frc := TResourceStream.Create(HInstance, 'TfrxConfig', RT_RCDATA);
    try
      Result := LoadFromStream(frc);
    finally
      frc.Free;
    end;
  except
    Result := E_FAIL;
  end;
  if FileExists(FileName) then
  begin
    try
      f := TFileStream.Create(FileName, fmOpenRead + fmShareDenyWrite);
      try
        Result := LoadFromStream(f);
        FFileName := FileName;
      finally
        f.Free;
      end;
    except
      Result := E_FAIL;
    end;
  end;
end;

function TfrxConfig.LoadFromStream(Stream: TStream): HRESULT;
begin
  Result := S_OK;
  FCS.Enter;
  try
    try
      FXML.LoadFromStream(Stream);
      UpdateLines;
    except
      Result := E_FAIL;
    end;
  finally
    FCS.Leave;
  end;
end;

procedure TfrxConfig.Reload;
begin
  LoadFromFile(FFileName);
end;

function TfrxConfig.SaveToFile(const FileName: String): HRESULT;
var
  f: TFileStream;
begin
  try
    f := TFileStream.Create(FileName, fmCreate);
    try
      Result := SaveToStream(f);
      FFileName := FileName;
    finally
      f.Free;
    end;
  except
    Result := E_FAIL;
  end;
end;

function TfrxConfig.SaveToStream(Stream: TStream): HRESULT;
begin
  Result := S_OK;
  FCS.Enter;
  try
    try
      FXML.Root.Prop['configversion'] := FR_SERVER_CONFIG_VERSION;
      FXML.SaveToStream(Stream);
      UpdateLines;
    except
      Result := E_FAIL;
    end;
  finally
    FCS.Leave;
  end;
end;

procedure TfrxConfig.SetBool(const Name: String; const Value: Boolean);
begin
  if Value then
    SetValue(Name, 'yes')
  else
    SetValue(Name, 'no');
end;

procedure TfrxConfig.SetValue(const Name, Value: String);
var
  i: Integer;
begin
  i := FLines.IndexOf(Name);
  if (i <> -1) and (FLines.Objects[i] <> nil) then
  begin
    TfrxConfigItem(FLines.Objects[i]).Value := Value;
    if TfrxConfigItem(FLines.Objects[i]).XMLItem.Name = 'set' then
      TfrxConfigItem(FLines.Objects[i]).XMLItem.Prop['value'] := Value;
  end;
end;

procedure TfrxConfig.UpdateLines;

  procedure AddItemToLines(const Prefix: String; Item: TfrxXMLItem);
  var
    s: String;
    i: Integer;
  begin
    if Item <> nil then
    begin
      s := Prefix;
      if s = '' then
        s := Item.Name
      else if Item.PropExists('desc') and (Item.Name <> 'set') then
        s :=  s + '.' + Item.Name;
      if Item.Name = 'set' then
        AddLine(s + '.' + Item.Prop['name'], Item.Prop['value'], Item)
      else
      begin
        if Item.PropExists('name') then
          AddLine(s + '.name', Item.Prop['name'], Item);
        for i := 0 to Item.Count - 1 do
          AddItemToLines(s, Item.Items[i]);
      end;
    end;
  end;

begin
  //Clear;
  AddLine('configversion', FXML.Root.Prop['configversion'], nil);
  AddItemToLines('', FXML.Root.FindItem('server'));
end;

{ TfrxServerConfig }

procedure TfrxServerConfig.LoadFromFile(const FileName: String);
var
  Ini: TIniFile;
begin
// compatibility code
  if FileExists(FileName) then
  begin
    LogWriter.Write(SERVER_LEVEL, 'Borrow old-style config. You should delete the file ' + FileName + ' after this launch.');
    Ini := TIniFile.Create(FileName);
    try
      Port := Ini.ReadInteger('Server', 'Port', 80);
      ServerConfig.SetValue('server.http.port', IntToStr(Port));

      SessionTimeOut := Ini.ReadInteger('Server', 'SessionTimeOut', 300);
      ServerConfig.SetValue('server.http.sessiontimeout', IntToStr(SessionTimeOut));

      SocketTimeOut := Ini.ReadInteger('Server', 'SocketTimeOut', 300);
      ServerConfig.SetValue('server.http.sockettimeout', IntToStr(SocketTimeOut));

      IndexFileName := Ini.ReadString('Server', 'IndexFileName', 'index.html');
      ServerConfig.SetValue('server.http.indexfile', IndexFileName);

      LogPath := frxGetAbsPath(Ini.ReadString('Server', 'LogPath', 'logs'));
      ServerConfig.SetValue('server.logs.path', frxGetRelPath(LogPath));

      ReportPath := frxGetAbsPath(Ini.ReadString('Server', 'ReportPath', 'reports'));
      ServerConfig.SetValue('server.reports.path', frxGetRelPath(ReportPath));

      RootPath := frxGetAbsPath(Ini.ReadString('Server', 'RootPath', 'htdocs'));
      ServerConfig.SetValue('server.http.rootpath', frxGetRelPath(RootPath));

      NoCacheHeader := Ini.ReadBool('Server', 'NoCacheHeader', True);
      ServerConfig.SetBool('server.http.nocacheheader', NoCacheHeader);

      Compression := Ini.ReadBool('Server', 'Compression', True);
      ServerConfig.SetBool('server.http.compression', Compression);

      Login := Ini.ReadString('Server', 'Login', '');
      ServerConfig.SetValue('server.security.login', Login);

      Password := Ini.ReadString('Server', 'Password', '');
      ServerConfig.SetValue('server.security.password', Password);

      MIC := Ini.ReadBool('Server', 'MessageIntegrityCheck', True);
      ServerConfig.SetBool('server.http.mic', MIC);

      ReportsList := Ini.ReadBool('Server', 'ReportsList', True);
      ServerConfig.SetBool('server.security.reportslist', ReportsList);

      Logging := Ini.ReadBool('Server', 'WriteLogs', False);
      ServerConfig.SetBool('server.logs.active', Logging);

      MaxLogSize := Ini.ReadInteger('Server', 'MaxLogSize', 1024);
      ServerConfig.SetValue('server.logs.rotatesize', IntToStr(MaxLogSize));

      MaxLogFiles := Ini.ReadInteger('Server', 'MaxLogFiles', 5);
      ServerConfig.SetValue('server.logs.rotatefiles', IntToStr(MaxLogFiles));

      ReportCaching := Ini.ReadBool('ReportsCache', 'Enabled', False);
      ServerConfig.SetBool('server.cache.active', ReportCaching);

      ReportCachePath := frxGetAbsPath(Ini.ReadString('ReportsCache', 'CachePath', 'cache'));
      ServerConfig.SetValue('server.cache.path', frxGetRelPath(ReportCachePath));

      DefaultCacheLatency := Ini.ReadInteger('ReportsCache', 'DefaultLatency', 300);
      ServerConfig.SetValue('server.cache.defaultlatency', IntToStr(DefaultCacheLatency));

  {    Ini.ReadSection('ReportsLatency', ReportCache.LatencyReports);
      Ini.ReadSectionValues('ReportsLatency', ReportCache.LatencyValues);
      for i := 0 to ReportCache.LatencyValues.Count - 1 do
      begin
        j := Pos('=', ReportCache.LatencyValues[i]);
        ReportCache.LatencyValues[i] := Copy(ReportCache.LatencyValues[i], j + 1,
          Length(ReportCache.LatencyValues[i]) - j + 1);
      end; }

      Database :=  frxGetAbsPath(Ini.ReadString('Database', 'Connection', ''));
      ServerConfig.SetValue('server.database.pathtodatabase', frxGetRelPath(Database));
      ServerConfig.SaveToFile(frxGetAbsPath('config.xml'));
      ServerConfig.ServerMessage := 'Please restart server!';
    finally
      Ini.Free;
    end;
  end;
end;

procedure TfrxServerConfig.SaveToFile(const FileName: String);
begin
// compatibility code
  ServerConfig.SaveToFile(FileName);
end;

initialization
  ServerConfig := TfrxConfig.Create;

finalization
  ServerConfig.Free;

end.
