//Advanced Delphi Systems Code: ads_ApUpdate
unit ads_ApUpdate;


{Copyright(c)2016 Advanced Delphi Systems

 Richard Maley
 Advanced Delphi Systems
 12613 Maidens Bower Drive
 Potomac, MD 20854 USA
 phone 301-840-1554
 dickmaley@advdelphisys.com

 The code herein can be used or modified by anyone.  Please retain references
 to Richard Maley at Advanced Delphi Systems.  If you make improvements to the
 code please send your improvements to dickmaley@advdelphisys.com so that the
 entire Delphi community can benefit.  All comments are welcome.
}

(*
Things to do:
- Recompile Update.exe as ESAUpdateAp.exe
- need a public login other than olmsadm
- need to get informix role information
- need to modify the versions table to accommodate informix role
- need to implement roles in ESA_Public database
*)

(*
UnitIndex Master Index Implementation Section Download Units
Description: ads_ApUpdate.pas
This unit contains the following routines.

TESAApUpdateCustom.ChkEsqlAuthorization   TESAApUpdateCustom.ConvertIntegerToBinaryString  TESAApUpdateCustom.ConvertWordToBinaryString   TESAApUpdateCustom.Execute   TESAApUpdateCustom.FileDate   TESAApUpdateCustom.FileDatesSame   TESAApUpdateCustom.FilesInDirDetail   TESAApUpdateCustom.GetIniVar   TESAApUpdateCustom.GetInstallPath   TESAApUpdateCustom.GetKillAppOnUserStop   TESAApUpdateCustom.GetLogUserUpdates   TESAApUpdateCustom.GetMustInstallShield   TESAApUpdateCustom.GetUserCanStopVersionUpdate   TESAApUpdateCustom.GetUserRole   TESAApUpdateCustom.GetVersionBuildNumberin   TESAApUpdateCustom.GetVersionNumbersg   TESAApUpdateCustom.GetVersionUpdateType   TESAApUpdateCustom.IniGetStringValue   TESAApUpdateCustom.IniSetStringValue   TESAApUpdateCustom.IsUpdateRequired   TESAApUpdateCustom.IsUpdateRequiredDateTime   TESAApUpdateCustom.LogUserVersion   TESAApUpdateCustom.SetESAProgram   TESAApUpdateCustom.SetInformixRole   TESAApUpdateCustom.SetUpdateExecutable   TESAApUpdateCustom.SetUpdateLocatorAliasName   TESAApUpdateCustom.SetUpdateLocatorDatabaseName  TESAApUpdateCustom.SetUpdateLocatorTableName   TESAApUpdateCustom.SetUpdateLocatorUserName   TESAApUpdateCustom.SetUpdateLocatorUserPw   TESAApUpdateCustom.SetUserName   TESAApUpdateCustom.SetUserVersionLogTableName   TESAApUpdateCustom.UpdateThisApplication   TESAApUpdateCustom.UserIDFromWindows   UpdateApplication  

*)
interface
Uses
  DsgnIntf, dbtables, SysUtils, Dialogs, IniFiles, Classes, Forms,
  Wintypes, FileCtrl, Controls;

type
  TESAApUpdateCustom = class(TComponent)
  private
    FESAProgram               : String;
    FInformixRole             : String;
    FUpdateExecutable         : String;
    FUpdateLocatorAliasName   : String;
    FUpdateLocatorDatabaseName: String;
    FUpdateLocatorTableName   : String;
    FUpdateLocatorUserName    : String;
    FUpdateLocatorUserPw      : String;
    FUserName                 : String;
    FUserVersionLogTableName  : String;
    Function  ConvertIntegerToBinaryString(Int, Length : Integer) : ShortString;
    Function  ConvertWordToBinaryString(InputWord : Word; Length : Integer) : ShortString;
    Function  FileDate(FileString: String): TDateTime;
    Function  FileDatesSame(FileString1,FileString2: String): Boolean;
    Function  FilesInDirDetail(FileList: TStrings;Directory,Mask: String;Intersection,IsReadOnly,IsHidden,IsSystem,IsVolumeID,IsDirectory,IsArchive,IsNormal,InclDotFiles: Boolean): Boolean;
    Function  GetInstallPath(sgOrganization,ApName: String): String;
    Function  GetKillAppOnUserStop(fnInstallPathAndIniFileName, sgIniSection: String): Boolean;
    Function  GetLogUserUpdates(fnInstallPathAndIniFileName, sgIniSection: String): Boolean;
    Function  GetMustInstallShield(fnInstallPathAndIniFileName, sgIniSection : String): Boolean;
    Function  GetUserCanStopVersionUpdate(fnInstallPathAndIniFileName, sgIniSection: String): Boolean;
    Function  GetUserRole(sgOrganization,sgApName: String): String;
    Function  GetVersionBuildNumberin(sgApName : String): Integer;
    Function  GetVersionNumbersg(sgApName : String): String;
    Function  GetVersionUpdateType(fnInstallPathAndIniFileName, sgIniSection: String): String;
    Function  IniGetStringValue(TheIniFile,IniSection,StringName,DefaultString: String): String;
    Function  IniSetStringValue(TheIniFile,IniSection,StringName,StringValue: String): Boolean;
    Function  IsUpdateRequired(sgVersionUpdateType, sgApName, sgInstallPath: String): Boolean;
    Function  IsUpdateRequiredDateTime(sgInstallPath : String): Boolean;
    Function  LogUserVersion(sgOrganization, sgApName : String): Boolean;
    Function  UpdateThisApplication(sgOrganization : String): Boolean;
    Function  UserIDFromWindows: string;
    procedure ChkEsqlAuthorization;
    Procedure GetIniVar(Var IniVariable : String;IniFile,IniSection,IniVarName,IniVarDefault   : String);
    procedure SetESAProgram(const Value: String);
    procedure SetInformixRole(const Value: String);
    procedure SetUpdateExecutable(const Value: String);
    procedure SetUpdateLocatorAliasName(const Value: String);
    procedure SetUpdateLocatorDatabaseName(const Value: String);
    procedure SetUpdateLocatorTableName(const Value: String);
    procedure SetUpdateLocatorUserName(const Value: String);
    procedure SetUpdateLocatorUserPw(const Value: String);
    procedure SetUserName(const Value: String);
    procedure SetUserVersionLogTableName(const Value: String);
  protected
    Property UpdateExecutable            : String read FUpdateExecutable          write SetUpdateExecutable;
    Property UpdateLocatorAliasName      : String read FUpdateLocatorAliasName    write SetUpdateLocatorAliasName;
    Property UpdateLocatorDatabaseName   : String read FUpdateLocatorDatabaseName write SetUpdateLocatorDatabaseName;
    Property UpdateLocatorTableName      : String read FUpdateLocatorTableName    write SetUpdateLocatorTableName;
    Property UpdateLocatorUserName       : String read FUpdateLocatorUserName     write SetUpdateLocatorUserName;
    Property UpdateLocatorUserPw         : String read FUpdateLocatorUserPw       write SetUpdateLocatorUserPw;
    Property UserVersionLogTableName     : String read FUserVersionLogTableName   write SetUserVersionLogTableName;
  public
    {Standard Public declarations }
    constructor Create(AOwner: TComponent); override;
    destructor  Destroy                   ; override;
    function    Execute: Boolean;
    Property    UserName                 : String read FUserName                  write SetUserName;
    Property    InformixRole             : String read FInformixRole              write SetInformixRole;
    property    ESAProgram               : String read FESAProgram                write SetESAProgram;
  end;

  TESAApUpdate = class(TESAApUpdateCustom)
  Published
    property    ESAProgram;
  End;

  Function UpdateApplication(sgOrganization : String): Boolean;

  procedure Register;

implementation


constructor TESAApUpdateCustom.Create(AOwner: TComponent);
begin
  inherited Create (AOwner);
  //Put code below this line
  FInformixRole                 := '';
  FUserName                     := UserIDFromWindows;
  FESAProgram                   := '';
  FUpdateLocatorAliasName       := 'std_odbc_central';
  FUpdateLocatorDatabaseName    := 'ESA_Public';
  FUpdateLocatorUserName        := 'olmsadm';
  FUpdateLocatorUserPw          := 'olmsnew';
  FUpdateLocatorTableName       := 'ApInstallDir';
  FUserVersionLogTableName      := 'ApVersions';
  FUpdateExecutable             := 'ESAUpdateAp.exe';
  //Put code above this line
end;

destructor TESAApUpdateCustom.Destroy;
begin
  //Put code below this line

  //Put code above this line
  inherited         Destroy;
end;

//
Unit Description UnitIndex Master Index
function TESAApUpdateCustom.Execute: Boolean;
begin
  Result := UpdateThisApplication(ESAProgram);
end;

{!~ Returns the Windows User ID.}
//
Unit Description UnitIndex Master Index
Function TESAApUpdateCustom.UserIDFromWindows: string;
Var
  sgUserName    : string;
  UserNameLen : Dword;
Begin
  UserNameLen := 255;
  SetLength(sgUserName, UserNameLen);
  If GetUserName(PChar(sgUserName), UserNameLen) Then
    Result := Copy(sgUserName,1,UserNameLen - 1)
  Else
    Result := 'Unknown';
End;

{!~ Returns The Files Date Time Stamp as TDateTime.
Returns 0 if there is an error}
//
Unit Description UnitIndex Master Index
Function TESAApUpdateCustom.FileDate(FileString: String): TDateTime;
Begin
  Result := 0;
  Try
    If Not FileExists(FileString) Then Exit;
    Result := FileDateToDateTime(FileAge(FileString));
  Except
    Result := 0;
  End;
End;

{!~ Returns True is the filoe dates are the same, False otherwise.}
//
Unit Description UnitIndex Master Index
Function TESAApUpdateCustom.FileDatesSame(FileString1,FileString2: String): Boolean;
Begin
  Try
    If FileDate(FileString1)=FileDate(FileString2) Then
    Begin
      Result := True;
    End
    Else
    Begin
      Result := False;
    End;
  Except
    Result := True;
  End;
End;

//
Unit Description UnitIndex Master Index
Function TESAApUpdateCustom.ConvertWordToBinaryString(InputWord : Word; Length : Integer) : ShortString;
var
  Counter, Number : Cardinal;
  D : Array[0..1] of Char;
Begin
  D[0] := '0';
  D[1] := '1';
  Number := 1;
  Result[0] := #16;
  For Counter := 15 Downto 0 Do
  Begin
    Result[Number] :=
      D[Ord(InputWord and (1 shl Counter) <> 0)]; 
    Inc(Number);
  End;
  If Length > 16 Then Length := 16;
  If Length <  1 Then Length :=  1;
  Result := Copy(Result,16-Length,Length);
End;

{!~ Converts an integer value to its binary equivalent
as a ShortString }
//
Unit Description UnitIndex Master Index
Function TESAApUpdateCustom.ConvertIntegerToBinaryString(Int, Length : Integer) : ShortString;
Begin
  Result := ConvertWordToBinaryString(Word(Int),Length);
End;

{!~ Populates a TStrings FileList with the files meeting selected
file attribute criteria in a directory.  The mask argument is a
standard DOS file argument like '*.*.  The InclDotFiles argument
allows the user to exclude the system files "." and ".." by
setting the value to False.  If the Intersection argument is set
to true then the result will reflect only those files that satisfy
all attribute criteria.  If Intersection is set to false then the
result will be a union of files that meet any of the criteria.}
//
Unit Description UnitIndex Master Index
Function TESAApUpdateCustom.FilesInDirDetail(
  FileList    : TStrings;
  Directory   : String;
  Mask        : String;
  Intersection: Boolean;
  IsReadOnly  : Boolean;
  IsHidden    : Boolean;
  IsSystem    : Boolean;
  IsVolumeID  : Boolean;
  IsDirectory : Boolean;
  IsArchive   : Boolean;
  IsNormal    : Boolean;
  InclDotFiles: Boolean): Boolean;
var
  j          : Integer;
  MaskPtr    : PChar;
  Ptr        : PChar;
  FileInfo   : TSearchRec;
  CurDir     : String;
  FileType   : TFileType;
  FileTypeI  : Integer;
  FileTypeB  : ShortString;
  TSList     : TStringList;
  BinaryAttr : ShortString;
  ShouldAdd  : Boolean;
begin
  TSList := TStringList.Create();
  Try
    Try
      FileType := [];
      If IsReadOnly  Then FileType := (FileType + [ftReadOnly]);
      If IsHidden    Then FileType := (FileType + [ftHidden]);
      If IsSystem    Then FileType := (FileType + [ftSystem]);
      If IsVolumeID  Then FileType := (FileType + [ftVolumeID]);
      If IsDirectory Then FileType := (FileType + [ftDirectory]);
      If IsArchive   Then FileType := (FileType + [ftArchive]);
      If IsNormal    Then FileType := (FileType + [ftNormal]);
      FileTypeI := 0;
      If IsReadOnly  Then FileTypeI := (FileTypeI +   1);
      If IsHidden    Then FileTypeI := (FileTypeI +   2);
      If IsSystem    Then FileTypeI := (FileTypeI +   4);
      If IsVolumeID  Then FileTypeI := (FileTypeI +   8);
      If IsDirectory Then FileTypeI := (FileTypeI +  16);
      If IsArchive   Then FileTypeI := (FileTypeI +  32);
      If IsNormal    Then FileTypeI := (FileTypeI + 128);
      FileTypeB := ConvertIntegerToBinaryString(FileTypeI,8);
      TSList.Clear;
      GetDir(0,CurDir);
      ChDir(Directory);  { go to the directory we want }
      FileList.Clear;    { clear the list }

      MaskPtr := PChar(Mask);
      while MaskPtr <> nil do
      begin
        Ptr := StrScan (MaskPtr, ';');
        If Ptr <> nil Then Ptr^ := #0;
        If FindFirst(MaskPtr, 191, FileInfo) = 0 Then
        Begin
          Repeat            { exclude normal files if ftNormal not set }
          Begin
            If ftNormal in FileType Then
            Begin
              TSList.Add(FileInfo.Name);
            End
            Else
            Begin
              BinaryAttr := ConvertIntegerToBinaryString(FileInfo.Attr,8);
              If Intersection Then
              Begin
                ShouldAdd := True;
                For j := 1 To 8 Do
                Begin
                  If (FileTypeB[j]='1') And (BinaryAttr[j]<>'1') Then
                  Begin
                    ShouldAdd := False;
                    Break;
                  End;
                End;
                If ShouldAdd Then
                  TSList.Add(FileInfo.Name);
              End
              Else
              Begin
                For j := 1 To 8 Do
                Begin
                  If (FileTypeB[j]='1') And (BinaryAttr[j]='1') Then
                  Begin
                    TSList.Add(FileInfo.Name);
                    Break;
                  End;
                End;
              End;
            End;
          End;
          Until FindNext(FileInfo) <> 0;
          FindClose(FileInfo.FindHandle);
        End;
        If Ptr <> nil then
        begin
          Ptr^ := ';';
          Inc (Ptr);
        end;
        MaskPtr := Ptr;
      end;
      ChDir(CurDir);
      TSList.Sorted := False;
      If Not InclDotFiles Then
      Begin
        If TSList.IndexOf('.') > -1 Then
          TSLIst.Delete(TSList.IndexOf('.'));
        If TSList.IndexOf('..') > -1 Then
          TSLIst.Delete(TSList.IndexOf('..'));
      End;
      TSList.Sorted := True;
      TSList.Sorted := False;
      FileList.Assign(TSList);
      Result := True;
    Except
      Result := False;
    End;
  Finally
    TSList.Free;
  End;
end;

{!~ Returns the ini value for a variable (StringName)
in the ini section (IniSection) of the ini file (TheIniFile).}
//
Unit Description UnitIndex Master Index
Function TESAApUpdateCustom.IniGetStringValue(
  TheIniFile            : String;
  IniSection            : String;
  StringName            : String;
  DefaultString         : String): String;
Var
  TheIni                : TIniFile;
Begin
  TheIni                  := TIniFile.Create(TheIniFile);
  Try
    Result :=
      TheIni.ReadString(
        IniSection,
        StringName,
        DefaultString);
    If Result = '' Then
    Begin
      Result := DefaultString;
    End;
  Finally
    TheIni.Free;
  End;
End;

{!~ Sets a variable (StringName) in the ini section (IniSection)
of the ini file (TheIniFile) with the value (StringValue).
If an exception is thrown the function returns False,
True otherwise.}
//
Unit Description UnitIndex Master Index
Function TESAApUpdateCustom.IniSetStringValue(
  TheIniFile            : String;
  IniSection            : String;
  StringName            : String;
  StringValue           : String): Boolean;
Var
  TheIni                : TIniFile;
Begin
{  Result := False;}{zzz}
  TheIni                := TIniFile.Create(TheIniFile);
  Try
    Try
      TheIni.WriteString(
        IniSection,
        StringName,
        StringValue);
      Result := True;
    Except
      Result := False;
    End;
  Finally
    TheIni.Free;
  End;
End;

{Return the Path to this application's installation directory}
//
Unit Description UnitIndex Master Index
Function TESAApUpdateCustom.GetInstallPath(
  sgOrganization : String;
  ApName          : String): String;
Var
  Database : TDatabase;
  Query    : TQuery;
Begin
  Result := '';
  Database := TDatabase.Create(nil);
  Query := TQuery.Create(nil);
  Try
    Try
      Database.Connected      := False;
      Database.AliasName      := UpdateLocatorAliasName;
      Database.DatabaseName   := 'GetInstallPathDBName';
      Database.KeepConnection := False;
      Database.LoginPrompt    := False;
      Database.Params.Clear;
      Database.Params.Add('DATABASE NAME='+UpdateLocatorDatabaseName);
      Database.Params.Add('USER NAME='+UpdateLocatorUserName);
      Database.Params.Add('ODBC DSN='+UpdateLocatorAliasName);
      Database.Params.Add('OPEN MODE=READ/WRITE');
      Database.Params.Add('BATCH COUNT=200');
      Database.Params.Add('LANGDRIVER=');
      Database.Params.Add('MAX ROWS=-1');
      Database.Params.Add('SCHEMA CACHE DIR=');
      Database.Params.Add('SCHEMA CACHE SIZE=8');
      Database.Params.Add('SCHEMA CACHE TIME=-1');
      Database.Params.Add('SQLPASSTHRU MODE=SHARED AUTOCOMMIT');
      Database.Params.Add('SQLQRYMODE=');
      Database.Params.Add('ENABLE SCHEMA CACHE=FALSE');
      Database.Params.Add('ENABLE BCD=FALSE');
      Database.Params.Add('ROWSET SIZE=20');
      Database.Params.Add('BLOBS TO CACHE=64');
      Database.Params.Add('BLOB SIZE=32');
      Database.Params.Add('PASSWORD='+UpdateLocatorUserPw);

      ChkEsqlAuthorization;
      try
        Database.Connected := True;
      except
        on EDBEngineError do Exit;
      end;

      Query.Active := False;
      Query.DatabaseName := Database.DatabaseName;
      Query.Sql.Clear;
      Query.Sql.Add('Select');
      Query.Sql.Add('Install_Path');
      Query.Sql.Add('From');
      If Pos('.DB',UpperCase(UpdateLocatorTableName)) > 0 Then
      Begin
        Query.Sql.Add('"'+UpdateLocatorTableName+'"');
      End
      Else
      Begin
        Query.Sql.Add(UpdateLocatorTableName);
      End;
      Query.Sql.Add('Where');
      Query.Sql.Add('Organization Like "'+sgOrganization+'%"');
      Query.Sql.Add('And');
      Query.Sql.Add('Length(Organization) = '+IntToStr(Length(sgOrganization)));
      Query.Sql.Add('And');
      Query.Sql.Add('Application_Name Like "'+ApName+'%"');
      Query.Sql.Add('And');
      Query.Sql.Add('Length(Application_Name) = '+IntToStr(Length(ApName)));
      Query.Active := True;
      Query.First;
      If Query.BOF And Query.EOF Then
      Begin
        Result := '';
      End
      Else
      Begin
        Result := Query.FieldByName('Install_Path').AsString;
      End;
    Except
      Result := '';
    End;
  Finally
    Query.Active       := False;
    Database.Connected := False;
    Query.Free;
    Database.Free;
  End;
End;

//
Unit Description UnitIndex Master Index
Procedure TESAApUpdateCustom.GetIniVar(
  Var IniVariable : String;
  IniFile         : String;
  IniSection      : String;
  IniVarName      : String;
  IniVarDefault   : String);
Begin
  IniVariable :=
    IniGetStringValue(
      IniFile,                         //TheIniFile            : String;
      IniSection,                      //IniSection            : String;
      IniVarName,                      //StringName            : String;
      '');                             //DefaultString         : String): String;

  {Try to update ini if possible}
  If IniVariable = '' Then
  Begin
    If IniVarDefault <> '' Then
    Begin
      IniVariable := IniVarDefault;
      Try
        IniSetStringValue(
            IniFile,                   //TheIniFile            : String;
            IniSection,                //IniSection            : String;
            IniVarName,                //StringName            : String;
            IniVariable);              //StringValue           : String): Boolean;
      Except
      End;
    End;
  End;
End;

//
Unit Description UnitIndex Master Index
Function TESAApUpdateCustom.GetVersionUpdateType(
    fnInstallPathAndIniFileName : String;  //IniFile         : String;
    sgIniSection                : String)  //IniSection      : String;
    : String;
Var
  sgVersionUpdateTypeOptions : String;
  sgVersionUpdateType         : String;
Begin
  GetIniVar(
    sgVersionUpdateTypeOptions,//Var IniVariable : String;
    fnInstallPathAndIniFileName,//IniFile         : String;
    sgIniSection,               //IniSection      : String;
    'VersionUpdateTypeOptions', //IniVarName      : String;
    'DateTime,BuildAll,Version,NoUpdate');//IniVarDefault   : String);

  GetIniVar(
    sgVersionUpdateType,        //Var IniVariable : String;
    fnInstallPathAndIniFileName,//IniFile         : String;
    sgIniSection,               //IniSection      : String;
    'VersionUpdateType',         //IniVarName      : String;
    'DateTime');                 //IniVarDefault   : String);
  Result := sgVersionUpdateType;
End;

//
Unit Description UnitIndex Master Index
Function TESAApUpdateCustom.GetUserCanStopVersionUpdate(
    fnInstallPathAndIniFileName : String;  //IniFile         : String;
    sgIniSection                : String)  //IniSection      : String;
    : Boolean;
Var
  sgUserCanStopVersionUpdateOptions : String;
  sgUserCanStopVersionUpdate         : String;
Begin
  GetIniVar(
    sgUserCanStopVersionUpdateOptions,//Var IniVariable : String;
    fnInstallPathAndIniFileName,      //IniFile         : String;
    sgIniSection,                     //IniSection      : String;
    'UserCanStopVersionUpdateOptions',//IniVarName      : String;
    'True,False');                     //IniVarDefault   : String);

  GetIniVar(
    sgUserCanStopVersionUpdate,       //Var IniVariable : String;
    fnInstallPathAndIniFileName,      //IniFile         : String;
    sgIniSection,                     //IniSection      : String;
    'UserCanStopVersionUpdate',        //IniVarName      : String;
    'True');                          //IniVarDefault   : String);
  If UpperCase(sgUserCanStopVersionUpdate) = 'TRUE' Then
  Begin
    Result := True;
  End
  Else
  Begin
    Result := False;
  End;
End;

//
Unit Description UnitIndex Master Index
Function TESAApUpdateCustom.GetKillAppOnUserStop(
    fnInstallPathAndIniFileName : String;  //IniFile         : String;
    sgIniSection                : String)  //IniSection      : String;
    : Boolean;
Var
  sgKillAppOnUserStopOptions : String;
  sgKillAppOnUserStop         : String;
Begin
  GetIniVar(
    sgKillAppOnUserStopOptions,      //Var IniVariable : String;
    fnInstallPathAndIniFileName,      //IniFile         : String;
    sgIniSection,                     //IniSection      : String;
    'KillAppOnUserStopOptions',       //IniVarName      : String;
    'True,False');                     //IniVarDefault   : String);

  GetIniVar(
    sgKillAppOnUserStop,              //Var IniVariable : String;
    fnInstallPathAndIniFileName,      //IniFile         : String;
    sgIniSection,                     //IniSection      : String;
    'KillAppOnUserStop',               //IniVarName      : String;
    'False');                          //IniVarDefault   : String);
  If UpperCase(sgKillAppOnUserStop) = 'TRUE' Then
  Begin
    Result := True;
  End
  Else
  Begin
    Result := False;
  End;
End;

//
Unit Description UnitIndex Master Index
Function TESAApUpdateCustom.GetMustInstallShield(
    fnInstallPathAndIniFileName : String;  //IniFile         : String;
    sgIniSection                : String)  //IniSection      : String;
    : Boolean;
Var
  sgMustInstallShieldOptions : String;
  sgMustInstallShield         : String;
Begin
  GetIniVar(
    sgMustInstallShieldOptions,      //Var IniVariable : String;
    fnInstallPathAndIniFileName,      //IniFile         : String;
    sgIniSection,                     //IniSection      : String;
    'MustInstallShieldOptions',       //IniVarName      : String;
    'True,False');                     //IniVarDefault   : String);

  GetIniVar(
    sgMustInstallShield,              //Var IniVariable : String;
    fnInstallPathAndIniFileName,      //IniFile         : String;
    sgIniSection,                     //IniSection      : String;
    'MustInstallShield',               //IniVarName      : String;
    'False');                          //IniVarDefault   : String);
  If UpperCase(sgMustInstallShield) = 'TRUE' Then
  Begin
    Result := True;
  End
  Else
  Begin
    Result := False;
  End;
End;

//
Unit Description UnitIndex Master Index
Function TESAApUpdateCustom.GetLogUserUpdates(
    fnInstallPathAndIniFileName : String;  //IniFile         : String;
    sgIniSection                : String)  //IniSection      : String;
    : Boolean;
Var
  sgLogUserUpdatesOptions : String;
  sgLogUserUpdates         : String;
Begin
  GetIniVar(
    sgLogUserUpdatesOptions,         //Var IniVariable : String;
    fnInstallPathAndIniFileName,      //IniFile         : String;
    sgIniSection,                     //IniSection      : String;
    'LogUserUpdatesOptions',          //IniVarName      : String;
    'True,False');                     //IniVarDefault   : String);

  GetIniVar(
    sgLogUserUpdates,                 //Var IniVariable : String;
    fnInstallPathAndIniFileName,      //IniFile         : String;
    sgIniSection,                     //IniSection      : String;
    'LogUserUpdates',                  //IniVarName      : String;
    'False');                          //IniVarDefault   : String);
  If UpperCase(sgLogUserUpdates) = 'TRUE' Then
  Begin
    Result := True;
  End
  Else
  Begin
    Result := False;
  End;
End;

{!~ Returns the Version number of an executable.}
//
Unit Description UnitIndex Master Index
Function TESAApUpdateCustom.GetVersionNumbersg(
  sgApName : String): String;
const
  kInfoNum = 11;
  InfoStr : array [1..kInfoNum] of String =
    ('CompanyName', 'FileDescription', 'FileVersion', 'InternalName',
     'LegalCopyright', 'LegalTradeMarks', 'OriginalFilename',
     'ProductName', 'ProductVersion', 'Comments', 'Author');
  LabelStr : array [1..kInfoNum] of String =
    ('Company Name', 'Description', 'File Version', 'Internal Name',
     'Copyright', 'TradeMarks', 'Original File Name',
     'Product Name', 'Product Version', 'Comments', 'Author');
var
  sgVersion         : String;
  {$IFDEF VER100}
  inn, inLen       : Integer;
  {$ELSE}
  inn, inLen       : Cardinal;
  {$ENDIF}
  pcBuf             : PChar;
  pcValue           : PChar;
begin
  Try
    sgVersion := '';
    inn := GetFileVersionInfoSize(PChar(sgApName),inn);
    If inn > 0 Then
    Begin
      pcBuf := AllocMem(inn);
      GetFileVersionInfo(PChar(sgApName),0,inn,pcBuf);
      If VerQueryValue(pcBuf,PChar('StringFileInfo\040904E4\'+
                                 InfoStr[3]),Pointer(pcValue),inLen) Then
      Begin
        If Length(pcValue) > 0 Then
        Begin
          sgVersion := pcValue;
        End;
      End;
      FreeMem(pcBuf,inn);
    End
    Else
    Begin
      sgVersion := '';
    End;
    Result := sgVersion;
  Except
    Result := sgVersion;
  End;
End;

{!~ Returns the Build number from the version information of an executable.}
//
Unit Description UnitIndex Master Index
Function TESAApUpdateCustom.GetVersionBuildNumberin(
  sgApName : String): Integer;
Var
  sgVersion  : String;
  sgBuildNum : String;
  inBuildNum : Integer;
  ini        : Integer;
  inLen      : Integer;
Begin
  sgVersion := GetVersionNumbersg(sgApName);
  sgBuildNum := '0';
  If sgVersion = '' Then
  Begin
    //
  End
  Else
  Begin
    inLen := Length(sgVersion);
    For ini := inLen DownTo 1 Do
    Begin
      If Copy(sgVersion,ini,1) = '.' Then
      Begin
        sgBuildNum := Copy(sgVersion,ini+1,inLen-(ini+1)+1);
        Break;
      End;
    End;
  End;
  Try
    inBuildNum := StrToInt(sgBuildNum);
  Except
    inBuildNum := 0;
  End;
  Result := inBuildNum;
End;

//
Unit Description UnitIndex Master Index
Function TESAApUpdateCustom.IsUpdateRequiredDateTime(
  sgInstallPath      : String
  ): Boolean;
Var
  lstFileList   : TStringList;
  fnSource      : String;
  fnDestination : String;
  sgApNameFull  : String;
  sgApName      : String;
  i,j            : Integer;
  FileName       : String;
Begin
  sgApNameFull     := ExtractFileName(Application.ExeName);
  sgApName         := Copy(sgApNameFull,1,Length(sgApNameFull)-4);
  fnDestination    := ExtractFilePath(Application.ExeName);
  fnSource         := sgInstallPath;
  lstFileList      := TStringList.Create();
  Try
    If FilesInDirDetail(
         lstFileList, //FileList    : TStrings;
         fnSource,    //Directory   : String;
         '*.*',        //Mask        : String;
         True,         //Intersection: Boolean;
         False,        //IsReadOnly  : Boolean;
         False,        //IsHidden    : Boolean;
         False,        //IsSystem    : Boolean;
         False,        //IsVolumeID  : Boolean;
         False,        //IsDirectory : Boolean;
         False,        //IsArchive   : Boolean;
         True,         //IsNormal    : Boolean;
         False)        //InclDotFiles: Boolean)
    Then
    Begin
      //Eliminate the InstallShield Setup.exe file from the list
      i := lstFileList.IndexOf('Setup.exe');
      If i <> -1 Then lstFileList.Delete(i);
      //Eliminate the "ExecutableName.ini" file from the list
      i := lstFileList.IndexOf(sgApName+'.ini');
      If i <> -1 Then lstFileList.Delete(i);
      //Eliminate this Update executable from the list
      i := lstFileList.IndexOf(UpdateExecutable);
      If i <> -1 Then lstFileList.Delete(i);
      //If there are other files that should be eliminated they should be eliminated here
      {}
      //Create Final List of files to be copied
      //eliminate files that have the same date time stamp
      For j := (lstFileList.Count - 1) DownTo 0 Do
      Begin
        FileName := lstFileList[j];
        If FileExists(fnDestination+FileName) Then
        Begin
          If FileDatesSame(
               fnSource      + FileName,
               fnDestination + FileName)
          Then
          Begin
            i := lstFileList.IndexOf(FileName);
            If i <> -1 Then lstFileList.Delete(i);
          End;
        End;
      End;
    End;
  Finally
    Result := (lstFileList.Count > 0);
    lstFileList.Free;
  End;
End;

//
Unit Description UnitIndex Master Index
Function TESAApUpdateCustom.IsUpdateRequired(
  sgVersionUpdateType : String;
  sgApName            : String;
  sgInstallPath      : String
  ): Boolean;
Begin
  sgVersionUpdateType := UpperCase(sgVersionUpdateType);
  Result := False;
  If sgVersionUpdateType = 'NOUPDATE' Then
  Begin
    //Updates are turned off
    Exit;
  End;
  If sgVersionUpdateType = 'DATETIME' Then
  Begin
    //Compare file Date Time stamps
    Result := IsUpdateRequiredDateTime(sgInstallPath);
  End
  Else
  Begin
    If sgVersionUpdateType = 'BUILDALL' Then
    Begin
      //Compare BuildAll Parameters in Version numbers
      If GetVersionBuildNumberin(Application.ExeName) =
         GetVersionBuildNumberin(sgInstallPath+sgApName)
      Then
      Begin
        Result := False;
      End
      Else
      Begin
        Result := True;
      End;
    End
    Else
    Begin
      //Compare Version numbers
      If GetVersionNumbersg(Application.ExeName) =
         GetVersionNumbersg(sgInstallPath+sgApName)
      Then
      Begin
        Result := False;
      End
      Else
      Begin
        Result := True;
      End;
    End;
  End;
End;

{Log the user's version.}
//
Unit Description UnitIndex Master Index
Function TESAApUpdateCustom.LogUserVersion(
  sgOrganization : String;
  sgApName       : String): Boolean;
Var
  Database   : TDatabase;
  Query      : TQuery;
  sgUserID : String;
  sgVersion : String;
  dtExeDate : TDateTime;
Begin
  Result := False;
  Database := TDatabase.Create(nil);
  Query := TQuery.Create(nil);
  Try
    Try
      Database.Connected      := False;
      Database.AliasName      := UpdateLocatorAliasName;
      Database.DatabaseName   := 'GetInstallPathDBName';
      Database.KeepConnection := False;
      Database.LoginPrompt    := False;
      Database.Params.Clear;
      Database.Params.Add('DATABASE NAME='+UpdateLocatorDatabaseName);
      Database.Params.Add('USER NAME='+UpdateLocatorUserName);
      Database.Params.Add('ODBC DSN='+UpdateLocatorAliasName);
      Database.Params.Add('OPEN MODE=READ/WRITE');
      Database.Params.Add('BATCH COUNT=200');
      Database.Params.Add('LANGDRIVER=');
      Database.Params.Add('MAX ROWS=-1');
      Database.Params.Add('SCHEMA CACHE DIR=');
      Database.Params.Add('SCHEMA CACHE SIZE=8');
      Database.Params.Add('SCHEMA CACHE TIME=-1');
      Database.Params.Add('SQLPASSTHRU MODE=SHARED AUTOCOMMIT');
      Database.Params.Add('SQLQRYMODE=');
      Database.Params.Add('ENABLE SCHEMA CACHE=FALSE');
      Database.Params.Add('ENABLE BCD=FALSE');
      Database.Params.Add('ROWSET SIZE=20');
      Database.Params.Add('BLOBS TO CACHE=64');
      Database.Params.Add('BLOB SIZE=32');
      Database.Params.Add('PASSWORD='+UpdateLocatorUserPw);

      ChkEsqlAuthorization;
      try
        Database.Connected := True;
      except
        on EDBEngineError do Exit;
      end;

      sgUserID           := UserIDFromWindows;
      sgVersion          := GetVersionNumbersg(ParamStr(0));
      dtExeDate          := FileDate(ParamStr(0));
      Query.Active       := False;
      Query.DatabaseName := Database.DatabaseName;
      Query.RequestLive  := True;
      Query.Sql.Clear;
      Query.Sql.Add('Select *');
      Query.Sql.Add('From');
      If Pos('.DB',UpperCase(UserVersionLogTableName)) > 0 Then
      Begin
        Query.Sql.Add('"'+UserVersionLogTableName+'"');
      End
      Else
      Begin
        Query.Sql.Add(UserVersionLogTableName);
      End;
      Query.Sql.Add('Where');
      Query.Sql.Add('Organization Like "'+sgOrganization+'%"');
      Query.Sql.Add('And');
      Query.Sql.Add('Length(Organization) = '+IntToStr(Length(sgOrganization)));
      Query.Sql.Add('And');

      Query.Sql.Add('Application_Name Like "'+sgApName+'%"');
      Query.Sql.Add('And');
      Query.Sql.Add('Length(Application_Name) = '+IntToStr(Length(sgApName)));
      Query.Sql.Add('And');

      Query.Sql.Add('User_Name Like "'+sgUserID+'%"');
      Query.Sql.Add('And');
      Query.Sql.Add('Length(User_Name) = '+IntToStr(Length(sgUserID)));

      Query.Active := True;
      Query.First;
      If Query.BOF And Query.EOF Then
      Begin
        //No record exists
        Query.Insert;
      End
      Else
      Begin
        //Record exists
        If
        (Query.FieldByName('Executable_Version').AsString = sgVersion)
        And
        (Query.FieldByName('Executable_Date').AsDateTime  = dtExeDate)
        Then
        Begin
          Exit;
        End
        Else
        Begin
          Query.Edit;
        End;
      End;
      Query.FieldByName('Organization').AsString       := sgOrganization;
      Query.FieldByName('Application_Name').AsString   := sgApName;
      Query.FieldByName('Executable_Date').AsDateTime  := dtExeDate;
      Query.FieldByName('Executable_Version').AsString := sgVersion;
      Query.FieldByName('User_Name').AsString          := sgUserID;
      Query.Post;
      Result := True;
    Except
      Result := False;
    End;
  Finally
    Query.Active       := False;
    Database.Connected := False;
    Query.Free;
    Database.Free;
  End;
End;

{!~ Test whether the application needs to be updated.  If it does then
this procedure controls the process.}
//
Unit Description UnitIndex Master Index
Function TESAApUpdateCustom.UpdateThisApplication(sgOrganization : String): Boolean;
Var
  sgApName                        : String;
  sgInstallPath                   : String;
  boUpdateRequired                : Boolean;
  boUpdateNow                     : Boolean;
  boIniFound                      : Boolean;
  sgIniFileName                   : String;
  sgIniSection                    : String;
  sgVersionUpdateType             : String;
  boUserCanStopVersionUpdate      : Boolean;
  boKillAppOnUserStop             : Boolean;
  boMustInstallShield             : Boolean;
  boLogUserUpdates                : Boolean;
  sgUpdateExeOnly                 : String;
  MutexHandle                     : THandle;
Begin
  Result            := True;
  sgApName          := ExtractFileName(ParamStr(0));
  sgIniFileName     := Copy(sgApName,1,Length(sgApName)-4)+'.ini';
  sgIniSection      := 'ApplicationUpdateParameters';
  InformixRole      := GetUserRole(sgOrganization,sgApName);
  sgInstallPath     := GetInstallPath(sgOrganization, sgApName);
  If sgInstallPath = '' Then
  Begin
    ShowMessage('Report that the Application could not be found in the Locator table');
    Result := False;
    Exit;
  End;
  If Copy(sgInstallPath,Length(sgInstallPath),1) <> '\' Then
  Begin
    sgInstallPath := sgInstallPath + '\';
  End;

  sgVersionUpdateType        := GetVersionUpdateType(sgInstallPath+sgIniFileName,sgIniSection);
  boUserCanStopVersionUpdate := GetUserCanStopVersionUpdate(sgInstallPath+sgIniFileName,sgIniSection);
  boKillAppOnUserStop        := GetKillAppOnUserStop(sgInstallPath+sgIniFileName,sgIniSection);
  boMustInstallShield        := GetMustInstallShield(sgInstallPath+sgIniFileName,sgIniSection);
  boLogUserUpdates           := GetLogUserUpdates(sgInstallPath+sgIniFileName,sgIniSection);

  If boLogUserUpdates Then LogUserVersion(sgOrganization, sgApName);

  boIniFound := FileExists(sgInstallPath+sgIniFileName);
  If Not boIniFound Then
  Begin
    ShowMessage('Application Update Error - '+sgIniFileName+' file not found!');
    Result := False;
    Exit;
  End;
  boUpdateRequired :=
    IsUpdateRequired(
      sgVersionUpdateType,
      sgApName,
      sgInstallPath);

  If boUpdateRequired Then
  Begin
    boUpdateNow := False;
    //An Update is Required
    //Test whether the user can stop the update
    If boUserCanStopVersionUpdate Then
    Begin
      //User can choose to abort the update
      If MessageDlg(
           'The application needs to be updated.  Update now?',
           mtConfirmation,
           [mbYes, mbNo], 0) = mrYes
      Then
      Begin
        //User chose to update now
        boUpdateNow := True;
      End
      Else
      Begin
        //User chose not to update now
        //If boKillAppOnUserStop=True Then the application needs to shut down
        //The user will be given one more chance
        If boKillAppOnUserStop Then
        Begin
          If MessageDlg(
               'The application will close if not updated.  Update now?',
               mtConfirmation,
               [mbYes, mbNo], 0) = mrYes
          Then
          Begin
            //User changed mind.  Update now!
            boUpdateNow := True;
          End
          Else
          Begin
            Application.Terminate;
          End;
        End
        Else
        Begin
          //User chose not to update now and application can continue
          Result := False;
          Exit;
        End;
      End;
    End
    Else
    Begin
      //User has not control over the update process
      boUpdateNow := True;
    End;
    If boUpdateNow Then
    Begin
      If boMustInstallShield Then
      Begin
        //Start InstallShield Setup.exe
        If FileExists(sgInstallPath+'Setup.exe') Then
        Begin
          MutexHandle := CreateMutex(nil, False, PChar('Update'+ParamStr(0)));
          If MutexHandle > 0 Then
          Begin
            WinExec(PChar(sgInstallPath+'Setup.exe'), SW_SHOW);
            Application.Terminate;
          End
          Else
          Begin
            ShowMessage('Update Aborted! More than one instance of application is running.');
            Halt;
          End;
        End
        Else
        Begin
          ShowMessage('Update Aborted! '+sgInstallPath+'Setup.exe not found');
        End;
      End
      Else
      Begin
        //Start UpdateExecutable
        If FileExists(sgInstallPath+UpdateExecutable) Then
        Begin
          If
          (UpperCase(sgVersionUpdateType) = 'BUILDALL') Or
          (UpperCase(sgVersionUpdateType) = 'VERSION')
          Then
          Begin
            sgUpdateExeOnly := 'TRUE';
          End
          Else
          Begin
            sgUpdateExeOnly := 'FALSE';
          End;
          MutexHandle := CreateMutex(nil, False, PChar('Update'+ParamStr(0)));
          If MutexHandle > 0 Then
          Begin
            WinExec(
              PChar(
                sgInstallPath   +
                UpdateExecutable  +
                ' "'              +
                sgInstallPath   +
                '" "'             +
                ParamStr(0)       +
                '" '              +
                sgUpdateExeOnly),
              SW_SHOW);
            Application.Terminate;
          End
          Else
          Begin
            ShowMessage('Update Aborted! More than one instance of application is running.');
            Halt;
          End;
        End
        Else
        Begin
          ShowMessage('Update Aborted! '+sgInstallPath+UpdateExecutable+' not found');
        End;
      End;
    End;
  End;
End;

//
Unit Description UnitIndex Master Index
procedure TESAApUpdateCustom.SetUpdateExecutable(const Value: String);
begin
  FUpdateExecutable := Value;
end;

//
Unit Description UnitIndex Master Index
procedure TESAApUpdateCustom.SetUpdateLocatorAliasName(const Value: String);
begin
  FUpdateLocatorAliasName := Value;
end;

//
Unit Description UnitIndex Master Index
procedure TESAApUpdateCustom.SetUpdateLocatorDatabaseName(const Value: String);
begin
  FUpdateLocatorDatabaseName := Value;
end;

//
Unit Description UnitIndex Master Index
procedure TESAApUpdateCustom.SetUpdateLocatorTableName(const Value: String);
begin
  FUpdateLocatorTableName := Value;
end;

//
Unit Description UnitIndex Master Index
procedure TESAApUpdateCustom.SetUpdateLocatorUserName(const Value: String);
begin
  FUpdateLocatorUserName := Value;
end;

//
Unit Description UnitIndex Master Index
procedure TESAApUpdateCustom.SetUpdateLocatorUserPw(const Value: String);
begin
  FUpdateLocatorUserPw := Value;
end;

//
Unit Description UnitIndex Master Index
procedure TESAApUpdateCustom.SetUserVersionLogTableName(const Value: String);
begin
  FUserVersionLogTableName := Value;
end;

//
Unit Description UnitIndex Master Index
procedure TESAApUpdateCustom.SetESAProgram(const Value: String);
begin
  FESAProgram := Value;
end;

//
Unit Description UnitIndex Master Index
procedure TESAApUpdateCustom.SetUserName(const Value: String);
begin
  FUserName := Value;
end;

//
Unit Description UnitIndex Master Index
procedure TESAApUpdateCustom.SetInformixRole(const Value: String);
begin
  FInformixRole := Value;
end;

//
Unit Description UnitIndex Master Index
Function UpdateApplication(sgOrganization : String): Boolean;
Var
  ESAApUpdate : TESAApUpdate;
Begin
  ESAApUpdate := TESAApUpdate.Create(nil);
  Try
    ESAApUpdate.ESAProgram := sgOrganization;
    Result := ESAApUpdate.Execute;
  Finally
    ESAApUpdate.Free;
  End;
End;

{Get the user's role.}
//
Unit Description UnitIndex Master Index
Function TESAApUpdateCustom.GetUserRole(
  sgOrganization : String;
  sgApName       : String): String;
Var
  Database   : TDatabase;
  Query      : TQuery;
  sgUserID   : String;
  sgVersion  : String;
Begin
  Result := '';
  Database := TDatabase.Create(nil);
  Query := TQuery.Create(nil);
  Try
    Try
      Database.Connected      := False;
      Database.AliasName      := UpdateLocatorAliasName;
      Database.DatabaseName   := 'GetInstallPathDBName';
      Database.KeepConnection := False;
      Database.LoginPrompt    := False;
      Database.Params.Clear;
      Database.Params.Add('DATABASE NAME='+UpdateLocatorDatabaseName);
      Database.Params.Add('USER NAME='+UpdateLocatorUserName);
      Database.Params.Add('ODBC DSN='+UpdateLocatorAliasName);
      Database.Params.Add('OPEN MODE=READ/WRITE');
      Database.Params.Add('BATCH COUNT=200');
      Database.Params.Add('LANGDRIVER=');
      Database.Params.Add('MAX ROWS=-1');
      Database.Params.Add('SCHEMA CACHE DIR=');
      Database.Params.Add('SCHEMA CACHE SIZE=8');
      Database.Params.Add('SCHEMA CACHE TIME=-1');
      Database.Params.Add('SQLPASSTHRU MODE=SHARED AUTOCOMMIT');
      Database.Params.Add('SQLQRYMODE=');
      Database.Params.Add('ENABLE SCHEMA CACHE=FALSE');
      Database.Params.Add('ENABLE BCD=FALSE');
      Database.Params.Add('ROWSET SIZE=20');
      Database.Params.Add('BLOBS TO CACHE=64');
      Database.Params.Add('BLOB SIZE=32');
      Database.Params.Add('PASSWORD='+UpdateLocatorUserPw);

      ChkEsqlAuthorization;
      try
        Database.Connected := True;
      except
        on EDBEngineError do Exit;
      end;

      sgUserID           := UserIDFromWindows;
      sgVersion          := GetVersionNumbersg(ParamStr(0));
      Query.Active       := False;
      Query.DatabaseName := Database.DatabaseName;
      Query.RequestLive  := True;
      Query.Sql.Clear;
      Query.Sql.Add('Select *');
      Query.Sql.Add('From');
      If Pos('.DB',UpperCase(UserVersionLogTableName)) > 0 Then
      Begin
        Query.Sql.Add('"'+UserVersionLogTableName+'"');
      End
      Else
      Begin
        Query.Sql.Add(UserVersionLogTableName);
      End;
      Query.Sql.Add('Where');
      Query.Sql.Add('Organization Like "'+sgOrganization+'%"');
      Query.Sql.Add('And');
      Query.Sql.Add('Length(Organization) = '+IntToStr(Length(sgOrganization)));
      Query.Sql.Add('And');

      Query.Sql.Add('Application_Name Like "'+sgApName+'%"');
      Query.Sql.Add('And');
      Query.Sql.Add('Length(Application_Name) = '+IntToStr(Length(sgApName)));
      Query.Sql.Add('And');

      Query.Sql.Add('User_Name Like "'+sgUserID+'%"');
      Query.Sql.Add('And');
      Query.Sql.Add('Length(User_Name) = '+IntToStr(Length(sgUserID)));

      Query.Active := True;
      Query.First;
      If Query.BOF And Query.EOF Then
      Begin
        //No record exists
      End
      Else
      Begin
        //Record exists
        Result := Query.FieldByName('User_Role').AsString;
      End;
    Except
    End;
  Finally
    Query.Active       := False;
    Database.Connected := False;
    Query.Free;
    Database.Free;
  End;
End;

//
Unit Description UnitIndex Master Index
procedure TESAApUpdateCustom.ChkEsqlAuthorization; external 'ESQLCHECK.DLL' name 'ChkEsqlAuthorization';procedure Register;
begin
  RegisterComponents('DOL-ESA', [TESAApUpdate]);
end;

end.
//