Updated setup-windows-x86.iss

Updated setup-windows-x86.iss

fatal: remote error: upload-pack: not our ref 2bd5f1e658654b4d9a1bef15bd2666b2caec4294
fatal: the remote end hung up unexpectedly


added WinPcap installer to setup

added WinPcap installer to setup

git-svn-id: 3e9aee9f2516f2549516d29f9787a8c058f60743@321 83beac0b-88d4-de11-98f5-001125a849b0

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/kholia/Loki.git/'


Setup always overwrites package xml.

Setup always overwrites package xml.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/oozcitak/XCOM.git/'


Installer script update

Installer script update

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/komarVadim/colt-build.git/'


Installer-builder script for Inno Setup, contributed by Tim Peters.

fatal: unable to access 'https://github.com/cgwalters/expat-git-mirror.git/': The requested URL returned error: 403


added innosetup script

added innosetup script

; Script generated by the Inno Setup Script Wizard.
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!

[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{AD3A87C6-2DBA-4459-8F88-DFD022C2B5E2}
AppName=Stop, Ctop
AppVerName=Stop, Crop 1.0
AppPublisher=VisionMap
AppPublisherURL=http://www.visionmap.com/
AppSupportURL=http://www.visionmap.com/
AppUpdatesURL=http://www.visionmap.com/
DefaultDirName={pf}\StopCtop
DefaultGroupName=Stop, Ctop
OutputBaseFilename=StopCrop_v1_0_0_0
Compression=lzma
SolidCompression=yes
OutputDir=bin

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked
Name: "quicklaunchicon"; Description: "{cm:CreateQuickLaunchIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked

[Files]
Source: "bin\Release\StopCrop.exe"; DestDir: "{app}"; Flags: ignoreversion
; NOTE: Don't use "Flags: ignoreversion" on any shared system files

[Icons]
Name: "{group}\Stop, Ctop"; Filename: "{app}\StopCrop.exe"
Name: "{group}\{cm:UninstallProgram,Stop, Ctop}"; Filename: "{uninstallexe}"
Name: "{commondesktop}\Stop, Ctop"; Filename: "{app}\StopCrop.exe"; Tasks: desktopicon
Name: "{userappdata}\Microsoft\Internet Explorer\Quick Launch\Stop, Ctop"; Filename: "{app}\StopCrop.exe"; Tasks: quicklaunchicon

error: pathspec 'projects/StopCrop/innosetup.iss' did not match any file(s) known to git


Updated Windows installer

Updated Windows installer

fatal: remote error: upload-pack: not our ref 6dba67caffd8aaaecc4c6f7dfbb0c1c56f98c864
fatal: the remote end hung up unexpectedly


initial setup script

initial setup script

fatal: unable to access 'https://github.com/vguzmanp/cloud-in-one.git/': The requested URL returned error: 403


Tidy default directory references

Tidy default directory references

; Running this script will compile composer-setup with the following default settings:
; Output filename: /Output/setup.exe, exe version info: 0.0.0.0
;
; Use the command-line compiler to change this (see Inno Help), for example:
; iscc /o"My\Output\Folder" /f"MyProgram" /d"SetupVersion=n.n" "path\to\composer.iss"

#ifndef SetupVersion
  ; do not change this
  #define SetupVersion
#endif

#define CmdPhp "php.exe"
#define CmdBat "composer.bat"
#define CmdShell "composer"
#define DllData "userdata.dll"

#define AppDescription "Composer - Php Dependency Manager"
#define AppUrl "getcomposer.org"

#define CS_SETUP_GUID "3ECDC245-751A-4962-B580-B8A250EDD1CF"
#define GUID_LEN Len(CS_SETUP_GUID)

[Setup]
; app name and version, must both be Composer
AppName=Composer
AppVerName=Composer
AppPublisher={#AppUrl}

; compile directives
Compression=lzma
SolidCompression=yes

; runtime  directives
MinVersion=5.1
PrivilegesRequired=none
AllowCancelDuringInstall=false

; directory stuff
DefaultDirName={code:GetBaseDir}\Composer
DisableDirPage=yes
AppendDefaultDirName=no
DirExistsWarning=no
AlwaysShowDirOnReadyPage=yes

; uninstall
Uninstallable=yes
UninstallDisplayName={#AppDescription}
UninstallFilesDir={app}\bin

; exe version info
VersionInfoVersion={#SetupVersion}
VersionInfoProductVersion=0
VersionInfoProductName={#AppDescription}

; cosmetic
WizardImageFile=wiz.bmp
WizardSmallImageFile=wizsmall.bmp

[Files]
Source: "setup.php"; Flags: dontcopy
Source: "setup.class.php"; Flags: dontcopy
Source: "userdata.dll"; DestDir: "{app}\bin"; Flags: ignoreversion
Source: "shims\{#CmdShell}"; Flags: dontcopy
Source: "shims\{#CmdBat}"; DestDir: "{app}\bin"; Flags: ignoreversion
Source: "{tmp}\{#CmdShell}"; DestDir: "{app}\bin"; Flags: external ignoreversion
Source: "{tmp}\composer.phar"; DestDir: "{app}\bin"; Flags: external ignoreversion

[Dirs]
; we need to make all-users directory writeable so composer.phar can update
Name: {app}; Permissions: users-modify; Check: IsAdminLoggedOn;

[Run]
Filename: "http://{#AppUrl}"; Description: "View online documentation"; Flags: postinstall shellexec unchecked;

[UninstallDelete]
; to force deletion of \Composer
Type: filesandordirs; Name: "{app}"

[Messages]
SetupWindowTitle=%1 Setup
WelcomeLabel1=[name] Setup
ReadyLabel1=Setup is now ready to download [name] and install it on your computer.
ReadyLabel2a=Please review these settings. Click Install to continue with the installation.
FinishedHeadingLabel=Completing [name] Setup
FinishedLabelNoIcons=Setup has installed [name] on your computer.%nUsage: Open a command window and type "composer"
FinishedLabel=Setup has installed [name] on your computer.%nUsage: Open a command window and type "composer"

[Code]

type
  TPhpRec = record
    Exe     : String;
    Version : String;
    Error   : String;
  end;

type
  TSearchRec = record
    System  : String;
    User    : String;
    Cmd     : String;
    Path    : String;
  end;

type
  TPathList = record
    Safe    : Boolean;
    Items   : TArrayOfString;
  end;

type
  TPathInfo = record
    Php       : TSearchRec;
    Bat       : TSearchRec;
    Shell     : TSearchRec;
    PathList  : TPathList;
  end;

type
  TTmpFile = record
    Setup     : String;
    Composer  : String;
    Result    : String;
  end;

type
  TGetRec = record
    Error   : Integer;
    Next    : Integer;
    Force   : Boolean;
    Text    : String;
  end;

type
  TPathRec = record
    Path    : String;
    Hive    : Integer;
    Name    : String;
  end;

type
  TFlagsRec = record
    AddPhp      : TPathRec;
    AddComposer : TPathRec;
    PathChanged : Boolean;
    Installed   : Boolean;
    Completed   : Boolean;
  end;

var
  Completed: Boolean;
  TmpFile: TTmpFile;
  PhpRec: TPhpRec;
  CmdExe: String;
  ComposerPath: String;
  PathError: String;
  GetRec: TGetRec;
  Flags: TFlagsRec;
  TmpDir: String;
  Test: String;
  SettingsPage: TInputFileWizardPage;
  ProgressPage: TOutputProgressWizardPage;
  ErrorPage: TWizardPage;
  DownloadInfoPage: TWizardPage;
  FinishedInfoPage: TOutputMsgWizardPage;

const
  CSIDL_PROFILE = $0028;
  SEP_PATH = ';';
  LF = #13#10;
  TEST_FLAG = '?';

  ERR_NONE = 0;
  ERR_INSTALL = 1;
  ERR_UNKNOWN = 10;
  ERR_CMD = 11;
  ERR_CMD_EX = 12;
  ERR_CMD_PHP = 13;
  ERR_PHP = 20;
  ERR_STATUS = 21;
  ERR_RESULT = 22;
  ERR_INVALID = 23;
  ERR_LOGIC = 24;
  ERR_CONN = 30;
  ERR_DOWNLOAD = 31;

  NEXT_NONE = 0;
  NEXT_RETRY = 1;
  NEXT_OK = 2;

procedure Debug(const Message: String); forward;

#include "paths.iss"
#include "userdata.iss"

function GetBaseDir(Param: String): String;
begin

  if IsAdminLoggedOn then
    Result := ExpandConstant('{commonappdata}')
  else
    Result := ExpandConstant('{userpf}');

end;

procedure Debug(const Message: String);
begin
  Log('DEBUG:: ' + Message);
end;

function ResultIdLine(const Line: String; var S: String): Boolean;
begin

  Result := False;
  S := '';

  if Pos('{#CS_SETUP_GUID}', Line) <> 0 then
  begin
    S := Copy(Line, {#GUID_LEN} + 1, MaxInt);
    Result := True;
  end;

end;

function DebugPhp(const Line: String): Boolean;
var
  S: String;

begin

  Result := False;
  S := '';

  if ResultIdLine(Line, S) then
  begin
    Log('PHPDBG:: ' + S);
    Result := True;
  end;

end;

procedure ResetGetRec(Full: Boolean);
begin

  GetRec.Error := ERR_NONE;
  GetRec.Next := NEXT_NONE;

  if Full then
    GetRec.Force := False;

  GetRec.Text := '';

end;

procedure ResetPhp;
begin

  PhpRec.Exe := '';
  PhpRec.Version := '';
  PhpRec.Error := '';

  ResetGetRec(True);

end;

procedure AddLine(var Existing: String; const Value: String);
begin

  if Existing <> '' then
    Existing := Existing + LF;

  Existing := Existing + Value;

end;

procedure AddSwitch(var Switches: String; const Name, Value: String);
begin

  if Switches = '' then
    Switches := '--';

  Switches := Switches + ' --' + Name;

  if Value <> '' then
  begin
    // we don't want to send default test ? value
    if (Name <> 'test') or (Test <> '?') then
      Switches := Switches + ' "' + Value + '"';
  end;

end;

procedure GetCmdResults(Results: TArrayOfString; var Output: String);
var
  Count: Integer;
  I: Integer;
  Line: String;

begin

  Count := GetArrayLength(Results);

  for I := 0 to Count - 1 do
  begin

    Line := Results[I];

    // filter any initial empty output
    if (Output = '') and (Trim(Line) = '') then
      Continue;

    // filter any shebang
    if Pos('#!', TrimLeft(Line)) = 1 then
      Continue;

    if not DebugPhp(Line) then
      AddLine(Output, Line);

  end;

end;

procedure SetSearchRec(var Rec: TSearchRec);
begin

  if Rec.System <> '' then
  begin
    Rec.Cmd := Rec.System;
    Rec.Path := ExtractFileDir(Rec.System);
    Rec.User := '';
  end
  else if Rec.User <> '' then
  begin
    Rec.Cmd := Rec.User;
    Rec.Path := ExtractFileDir(Rec.User);
  end
  else
  begin
    Rec.Cmd := '';
    Rec.Path := '';
  end;

end;

function GetPathInfo: TPathInfo;
var
  List1: TPathList;
  List2: TPathList;
  C1: Integer;
  C2: Integer;
  I: Integer;

begin

  Debug('Getting path info from registry');

  List1 := GetSafePathList(HKEY_LOCAL_MACHINE);
  Result.Php.System := SearchPath(List1, '{#CmdPhp}');
  Result.Bat.System := SearchPath(List1, '{#CmdBat}');
  Result.Shell.System := SearchPath(List1, '{#CmdShell}');

  List2 := GetSafePathList(HKEY_CURRENT_USER);
  Result.Php.User := SearchPath(List2, '{#CmdPhp}');
  Result.Bat.User := SearchPath(List2, '{#CmdBat}');
  Result.Shell.User := SearchPath(List2, '{#CmdShell}');

  SetSearchRec(Result.Php);
  SetSearchRec(Result.Bat);
  SetSearchRec(Result.Shell);

  Result.PathList.Safe := True;
  C1 := GetArrayLength(List1.Items);
  C2 := GetArrayLength(List2.Items);
  SetArrayLength(Result.PathList.Items, C1 + C2);

  for I := 0 to C1 - 1 do
    Result.PathList.Items[I] := List1.Items[I];

  for I := 0 to C2 - 1 do
    Result.PathList.Items[C1 + I] := List2.Items[I];

end;

procedure SetPathRec(var Rec: TPathRec; const Path: String);
begin

  Rec.Path := Path;
  Rec.Hive := HKEY_CURRENT_USER;
  Rec.Name := 'User';

  if (Rec.Path <> '') and IsAdminLoggedOn then
  begin
    Rec.Hive := HKEY_LOCAL_MACHINE;
    Rec.Name := 'System';
  end;

end;

procedure InitRecordsFromPath;
var
  Info: TPathInfo;

begin

  Info := GetPathInfo;
  PhpRec.Exe := Info.Php.Cmd;

end;

function CheckPhpPath(PathList: TPathList; Rec: TSearchRec): String;
var
  S: String;
  Env: String;
  PhpPath: String;

begin

  Result := '';

  Debug('Checking for php path');

  if Rec.Path = '' then
  begin

    PhpPath := ExtractFileDir(PhpRec.Exe);

    if not DirectoryInPath(PhpPath, PathList) then
      SetPathRec(Flags.AddPhp, PhpPath);

    Exit;

  end;

  if CompareText(Rec.Cmd, PhpRec.Exe) = 0 then
    Exit;

  S := 'The php exe you selected does not match the one found in your path.' + LF;
  S := S + LF;
  S := S + 'Selected: ' + PhpRec.Exe + LF;
  S := S + 'In Path: ' + Rec.Cmd + LF;
  S := S + LF;

  if Rec.System <> '' then
    Env := 'System'
  else
    Env := 'User';

  S := S + 'You can either select the one in your path, or remove its entry from your ';
  S := S + Env + ' Path Environment variable:' + LF;
  S := S + '   ' + Rec.Path + LF;
  S := S + LF;

  S := S + 'Note: Only change your path if you are sure that it will not affect anything else.' + LF;
  S := S + LF;

  S := S + 'If neither of these options are suitable, you will have to install Composer manually.';

  Result := S;

end;

function CheckShim(Rec: TSearchRec; Cmd: String; var Installed: Boolean): String;
var
  S: String;

begin

  S := '';

  if Rec.Path <> '' then
  begin

    if CompareText(Rec.Cmd, Cmd) <> 0 then
    begin
      S := 'Composer is already installed in the following directory:' + LF;
      S := S + Rec.Path + LF;
      S := S + LF;
      S := S + 'You must remove it first, if you want to continue this installation.' + LF;
    end;

    // we only set Installed to true
    Installed := True;

  end;

  Result := S;

end;

function CheckComposerPath(Info: TPathInfo): String;
var
  BinPath: String;
  Cmd: String;

begin

  Result := '';

  Debug('Checking for composer path');

  BinPath := AddBackslash(WizardDirValue) + 'bin';

  if (Info.Bat.Path = '') and (Info.Shell.Path = '') then
  begin

    if not DirectoryInPath(BinPath, Info.PathList) then
      SetPathRec(Flags.AddComposer, BinPath);

    Exit;

  end;

  Cmd := AddBackslash(BinPath) + '{#CmdBat}';

  Result := CheckShim(Info.Bat, Cmd, Flags.Installed)

  if Result = '' then
  begin
    Cmd := AddBackslash(BinPath) + '{#CmdShell}';
    Result := CheckShim(Info.Shell, Cmd, Flags.Installed);
  end;

end;

function CheckPathExt: String;
var
  Hive: Integer;
  Key: String;
  Value: String;
  PathExt: String;
  Missing: String;
  Space: String;

begin

  Result := '';

  Debug('Checking PathExt values');

  PathExt := '';

  Hive := HKEY_LOCAL_MACHINE;
  Key := GetPathKeyForHive(Hive);
  Value := '';

  if RegQueryStringValue(Hive, Key, 'PathExt', Value) then
    PathExt := Value;

  Hive := HKEY_CURRENT_USER;
  Key := GetPathKeyForHive(Hive);
  Value := '';

  if RegQueryStringValue(Hive, Key, 'PathExt', Value) then
    PathExt := PathExt + ';' + Value;

  PathExt := Uppercase(PathExt  + ';');

  Missing := '';
  Space := '    ';

  if Pos('.EXE;', PathExt) = 0 then
    Missing := LF + Space + '.EXE';

  if Pos('.BAT;', PathExt) = 0 then
    Missing := Missing + LF + Space + '.BAT';

  if Missing <> '' then
    Result := 'Your PathExt Environment variable is missing required values:' + Missing;

end;

procedure CheckPath;
var
  Info: TPathInfo;

begin

  Debug('Checking paths');

  SetPathRec(Flags.AddPhp, '');
  SetPathRec(Flags.AddComposer, '');
  Flags.Installed := False;
  Flags.PathChanged := False;

  Info := GetPathInfo;

  PathError := CheckPhpPath(Info.PathList, Info.Php);

  if PathError = '' then
    PathError := CheckComposerPath(Info);

  if PathError = '' then
    PathError := CheckPathExt;

  ComposerPath := '';

  if Info.Bat.Path <> '' then
  begin

    if FileExists(Info.Bat.Path + '\composer.phar') then
      ComposerPath := Info.Bat.Path;

  end;

  if (ComposerPath = '') and (Info.Shell.Path <> '') then
  begin

    if FileExists(Info.Shell.Path + '\composer.phar') then
      ComposerPath := Info.Shell.Path;

  end;

end;

procedure PathsAdd(var Error: String);
begin

  Error := '';

  if Flags.AddPhp.Path <> '' then
  begin

    if not AddToPath(Flags.AddPhp.Hive, Flags.AddPhp.Path) then
    begin
      Error := 'Error setting ' + Flags.AddPhp.Name + ' Path variable';
      Exit;
    end;

    Flags.PathChanged := True;

  end;

  if Flags.AddComposer.Path <> '' then
  begin

    if not AddToPath(Flags.AddComposer.Hive, Flags.AddComposer.Path) then
    begin

      Error := 'Error setting ' + Flags.AddComposer.Name + ' Path variable';

      // remove php path in the unlikely event we have just added it
      if Flags.PathChanged then
      begin
        RemoveFromPath(Flags.AddPhp.Hive, Flags.AddPhp.Path);
        Flags.PathChanged := False;
        NotifyPathChange;
      end;

      Exit;

    end;

    Flags.PathChanged := True;

  end;

  if Flags.PathChanged then
    NotifyPathChange;

end;

procedure PathsRemove;
begin

  if Flags.PathChanged then
  begin

    if Flags.AddPhp.Path <> '' then
      RemoveFromPath(Flags.AddPhp.Hive, Flags.AddPhp.Path);

    if Flags.AddComposer.Path <> '' then
      RemoveFromPath(Flags.AddComposer.Hive, Flags.AddComposer.Path);

    Flags.PathChanged := False;
    NotifyPathChange;

  end;

end;

function StatusCodeText(Status: Integer): String;
begin

 case Status of

  ERR_NONE: Result := 'ERR_NONE';
  ERR_INSTALL: Result := 'ERR_INSTALL';
  ERR_UNKNOWN: Result := 'ERR_UNKNOWN';
  ERR_CMD: Result := 'ERR_CMD';
  ERR_CMD_EX: Result := 'ERR_CMD_EX';
  ERR_CMD_PHP: Result := 'ERR_CMD_PHP';
  ERR_PHP: Result := 'ERR_PHP';
  ERR_STATUS: Result := 'ERR_STATUS';
  ERR_RESULT: Result := 'ERR_RESULT';
  ERR_INVALID: Result := 'ERR_INVALID';
  ERR_LOGIC: Result := 'ERR_LOGIC';
  ERR_CONN: Result := 'ERR_CONN';
  ERR_DOWNLOAD: Result := 'ERR_DOWNLOAD';

 end;

 Result := Format('[%s]', [Result]);

end;

function ExecCmd(const PhpExe, Switches: String; Show: Integer; var ExitCode: Integer): Boolean;
var
  Args: String;
  Params: String;

begin

  if FileExists(TmpFile.Result) then
    DeleteFile(TmpFile.Result);

  Args := Switches;

  if Test <> '' then
  begin
    AddSwitch(Args, 'test', Test);
    Debug('Running test: ' + Test);
  end;

  if Pos('/LOG', GetCmdTail) <> 0 then
    AddSwitch(Args, 'debug', '');

  // we must not quote Args since they are quoted individually
  Params := Format('/c "%s %s %s > %s"', [AddQuotes(PhpExe), AddQuotes(TmpFile.Setup), Args, AddQuotes(TmpFile.Result)]);
  Debug('Calling cmd.exe with params: ' + Params);
  Result := Exec(CmdExe, Params, TmpDir, Show, ewWaitUntilTerminated, ExitCode);

end;

function GetSysError(ErrorCode: Integer; const Filename: String; var Error: String): Integer;
begin

  Error := SysErrorMessage(ErrorCode);
  Result := StringChangeEx(Error, '%1', '%s', True);

  if Result = 1 then
    Error := Format(Error, [Filename]);

end;

function GetCommonCmdError(StatusCode, ExitCode: Integer): String;
var
  Error: String;

begin

  Result := '';

  if StatusCode = ERR_CMD then
  begin
    GetSysError(ExitCode, CmdExe, Error);
    Result := 'Internal Error [ERR_CMD]: ' + Error;
  end
  else if StatusCode = ERR_CMD_EX then
    Result := 'Internal Error [ERR_CMDEX]: A command did not run correctly';

end;

procedure SetPhpError(ErrorCode, ExitCode: Integer; const Filename: String);
var
  Text: String;
  Error: String;
  Name: String;

begin

  Text := '';
  Name := StatusCodeText(ErrorCode);

  case ErrorCode of

    ERR_CMD_PHP:
    begin

      if GetSysError(ExitCode, Filename, Error) = 0 then
        Text := 'The PHP exe file you specified did not execute correctly: ' + Filename + LF
      else
        Text := Error;

    end;

    ERR_CMD, ERR_CMD_EX:
      Text := GetCommonCmdError(ErrorCode, ExitCode);

    ERR_STATUS, ERR_RESULT, ERR_INVALID:
    begin

      Error := Format('The PHP exe file you specified did not execute correctly: %s%s%s', [LF, Filename, LF]);
      Error := Error + LF + 'Running it from the command line might highlight the problem.'

      if ErrorCode = ERR_STATUS then
      begin

        if ExitCode = 255 then
          Error := Error + LF + 'Use the -v switch to show the PHP version - it must be at least 5.3.2';

      end;

      Text := Format('Internal Error %s, exit code %d', [Name, ExitCode]);
      Text := Error + LF + Text;

    end;

    ERR_LOGIC:
    begin
      Text := Format('An internal script did not run correctly (exit code %d)', [ExitCode]);
      Text := Format('Internal Error %s: %s', [Name, Text]);
    end;

  else

    begin
      ErrorCode := ERR_UNKNOWN;
      Text := Format('Internal Error %s: An unspecified error occurred', [Name]);
    end;

  end;

  PhpRec.Error := Text;
  Debug(Format('Checking php: error %s', [Name]));

end;

function CheckPhpExe(const Filename: String): Boolean;
var
  Show: Integer;
  ExitCode: Integer;

begin

  Result := False;

  {
   * Possible errors:
   * Php.exe error - did not run [ERR_CMD_PHP]
  }

  if Test <> '' then
    Show := SW_SHOW
  else
    Show := SW_HIDE;

  Result := Exec(Filename, '-v', TmpDir, Show, ewWaitUntilTerminated, ExitCode);

  if not Result then
    SetPhpError(ERR_CMD_PHP, ExitCode, Filename);

end;

function CheckPhp(const Filename: String): Boolean;
var
  Switches: String;
  Show: Integer;
  ExitCode: Integer;
  Results: TArrayOfString;

begin

  Result := False;

  {
   * Possible errors:
   * Internal error - cmd did not run [ERR_CMD]
   * Internal error - cmd did not create output file run [ERR_CMD_EX]
   * ExitCode: 0 - Php check passed
   * ExitCode: 1 - Php check failed
   * ExitCode: ? - Php program error [ERR_STATUS] (Test=p1, Test=p2)
   * Results file, empty: [ERR_RESULT] (Test=p3)
   * Results file, non-matching guid: [ERR_INVALID] (Test=p4)
   * Results file, ExitCode 0, multiline [ERR_LOGIC] (test=p5)
   * Results file, ExitCode 1, guid only [ERR_LOGIC] (test=p6)
  }

  ResetPhp;

  Debug('Checking php: ' + Filename);

  if not CheckPhpExe(Filename) then
    Exit;

  AddSwitch(Switches, 'php', '');
  Show := Integer(Test <> '');

  if not ExecCmd(Filename, Switches, Show, ExitCode) then
  begin
    SetPhpError(ERR_CMD, ExitCode, Filename);
    Exit;
  end;

  if not LoadStringsFromFile(TmpFile.Result, Results) then
  begin
    SetPhpError(ERR_CMD_EX, ExitCode, Filename);
    Exit;
  end;

  if (ExitCode <> 0) and (ExitCode <> 1) then
  begin
    SetPhpError(ERR_STATUS, ExitCode, Filename);
    Exit;
  end;

  if GetArrayLength(Results) = 0 then
  begin
    SetPhpError(ERR_RESULT, ExitCode, Filename);
    Exit;
  end;

  // get php version
  if not ResultIdLine(Results[0], PhpRec.Version) then
  begin
    SetPhpError(ERR_INVALID, ExitCode, Filename);
    Exit;
  end;

  GetCmdResults(Results, PhpRec.Error);

  if (ExitCode = 0) and (PhpRec.Error <> '') then
  begin
    SetPhpError(ERR_LOGIC, ExitCode, Filename);
    Exit;
  end;

  if (ExitCode = 1) and (PhpRec.Error = '') then
  begin
    SetPhpError(ERR_LOGIC, ExitCode, Filename);
    Exit;
  end;

  PhpRec.Exe := Filename;

  Result := PhpRec.Error = '';

end;

procedure SetDownloadStatus(StatusCode, ExitCode: Integer);
var
  Text: String;

begin

  Text := '';
  ResetGetRec(True);

  case StatusCode of

    ERR_NONE: GetRec.Next := NEXT_OK;

    ERR_INSTALL: GetRec.Next := NEXT_NONE;

    ERR_CMD, ERR_CMD_EX:
    begin
      GetRec.Next := NEXT_RETRY;
      Text := GetCommonCmdError(StatusCode, ExitCode);
    end;

    ERR_PHP:
    begin
      GetRec.Next := NEXT_RETRY;
      Text := 'Internal Error [ERR_PHP]: An internal script did not run correctly';
    end;

    ERR_STATUS:
    begin
      GetRec.Next := NEXT_RETRY;
      GetRec.Force := True;
      Text := Format('Composer Error [ERR_STATUS]: Unexpected exit code from Composer (%d)', [ExitCode]);
     end;

    ERR_DOWNLOAD:
    begin
      GetRec.Next := NEXT_RETRY;
      GetRec.Force := True;
      Text := 'Composer Error [ERR_DOWNLOAD]: Composer was not downloaded';
    end;

    ERR_INVALID:
    begin
      GetRec.Next := NEXT_RETRY;
      GetRec.Force := True;
      Text := 'Composer Error [ERR_INVALID]: The installer script did not run correctly';
    end;

    ERR_CONN:
    begin
      GetRec.Next := NEXT_RETRY;
      GetRec.Force := True;
      Text := 'Connection Error [ERR_CONNECTION]: Unable to connect to {#AppUrl}';
    end;

  else

    begin
      StatusCode := ERR_UNKNOWN;
      GetRec.Next := NEXT_RETRY;
      Text := 'Internal Error [ERR_UNKNOWN]: An unspecified error occurred';
    end;

  end;

  GetRec.Error := StatusCode;
  GetRec.Text := Text;

end;

procedure SetDownloadCmdError(ExitCode: Integer);
var
  Error: String;
  Text: String;

begin

  SetDownloadStatus(ERR_CMD, ExitCode);

  if GetSysError(ExitCode, CmdExe, Error) = 0 then
    Text := Error + CmdExe
  else
    Text := Error;

  GetRec.Text := GetRec.Text + Text;

end;

procedure DownloadWork;
var
  Switches: String;
  ExitCode: Integer;
  Results: TArrayOfString;

begin

  {
   * Possible errors:
   * Internal error - cmd did not run [ERR_CMD]
   * Internal error - cmd did not create output file [ERR_CMD_EX]
   * ExitCode: 0 - Installed, no warnings [ERR_NONE]
   * ExitCode: 0 - Installed, warnings [ERR_NONE] (Test=d1)
   * ExitCode: 1 - Not Installed, errors [ERR_INSTALL] (Test=d2)
   * ExitCode: 2 - Php script did not run properly [ERR_PHP] (Test=d3)
   * ExitCode: 3 - Connection error of some sort [ERR_CONNECTION] (Test=d4) (Test=d5)
   * ExitCode: ? - Unexpected exit code from Composer, didn't return 0 or 1 [ERR_STATUS] (Test=d6)
   * ExitCode: 0 - No composer.phar downloaded [ERR_DOWNLOAD] (Test=d7)
   * ExitCode: 1 - No errors reported by Composer [ERR_INVALID] (Test=d8)
  }

  Debug('Downloading from {#AppUrl}');

  AddSwitch(Switches, 'download', '');

  if GetRec.Force then
    AddSwitch(Switches, 'force', '');

  if not ExecCmd(PhpRec.Exe, Switches, SW_HIDE, ExitCode) then
  begin
    SetDownloadCmdError(ExitCode);
    Exit;
  end;

  if not LoadStringsFromFile(TmpFile.Result, Results) then
  begin
    SetDownloadStatus(ERR_CMD_EX, ExitCode);
    Exit;
  end;

  // the following checks all exit
  if ExitCode = 0 then
  begin

    if not FileExists(TmpDir + '\composer.phar') then
    begin
      SetDownloadStatus(ERR_DOWNLOAD, ExitCode);
      Exit;
    end;

  end
  else if ExitCode = 2 then
  begin
    SetDownloadStatus(ERR_PHP, ExitCode);
    Exit;
  end
  else if ExitCode = 3 then
  begin
    SetDownloadStatus(ERR_CONN, ExitCode);
    AddLine(GetRec.Text, '');
    GetCmdResults(Results, GetRec.Text);
    Exit;
  end
  else if ExitCode <> 1 then
  begin
    SetDownloadStatus(ERR_STATUS, ExitCode);
    Exit;
  end;

  // must set status now
  if ExitCode = 0 then
    SetDownloadStatus(ERR_NONE, ExitCode)
  else
    SetDownloadStatus(ERR_INSTALL, ExitCode);

  if GetArrayLength(Results) = 0 then
  begin

    // no output, check that we are not expecting errors
    if ExitCode = 1 then
      SetDownloadStatus(ERR_INVALID, ExitCode);

    Exit;

  end;

  GetCmdResults(Results, GetRec.Text);
  GetRec.Text := Trim(GetRec.Text);

  // final check
  if (ExitCode = 1) and (GetRec.Text = '') then
    SetDownloadStatus(ERR_INVALID, ExitCode);

  if GetRec.Text <> '' then
    AddLine(GetRec.Text, '');

end;

function CreateMessagePage(Id: Integer; Caption, Description, Text: String): TWizardPage;
var
  StaticText: TNewStaticText;
  Memo: TNewMemo;
  Top: Integer;

begin

  Result := CreateCustomPage(Id, Caption, Description);

  StaticText := TNewStaticText.Create(Result);
  StaticText.Name := 'Static';
  StaticText.Caption := Text;
  StaticText.AutoSize := True;
  StaticText.Parent := Result.Surface;

  Top := StaticText.Top + StaticText.Height;

  Memo := TNewMemo.Create(Result);
  Memo.Name := 'Memo';
  Memo.Top := Top + ScaleY(8);
  Memo.Height := Result.SurfaceHeight - (Top + ScaleY(8) + ScaleY(15));
  Memo.Width := Result.SurfaceWidth;
  Memo.ScrollBars := ssVertical;
  Memo.ReadOnly := True;
  Memo.Parent := Result.Surface;
  Memo.Text := '';

end;

procedure UpdateErrorPage();
var
  Memo: TNewMemo;

begin

  Memo := TNewMemo(ErrorPage.FindComponent('Memo'));

  if PhpRec.Error <> '' then
  begin
    ErrorPage.Caption := 'PHP Settings Error';
    ErrorPage.Description := 'Composer will not work with your current settings'
    Memo.Text := PhpRec.Error;
  end
  else if PathError <> '' then
  begin
    ErrorPage.Caption := 'Path Settings Error';
    ErrorPage.Description := 'Setup cannot continue with your current settings'
    Memo.Text := PathError;
  end

end;

procedure UpdateDownloadMsgPage();
var
  PageStatic: TNewStaticText;
  PageMemo: TNewMemo;

begin

  PageStatic := TNewStaticText(DownloadInfoPage.FindComponent('Static'));
  PageMemo := TNewMemo(DownloadInfoPage.FindComponent('Memo'));

  if GetRec.Error <> ERR_NONE then
  begin

    DownloadInfoPage.Caption := 'Composer Download Error';
    DownloadInfoPage.Description := 'Unable to continue with installation';

    if GetRec.Error = ERR_INSTALL then
      PageStatic.Caption := 'Please review and fix the issues listed below then try again.'
    else
      PageStatic.Caption := 'An error occurred. Clicking Retry may resolve this issue.'

  end
  else
  begin
    DownloadInfoPage.Caption := 'Composer Warning';
    DownloadInfoPage.Description := 'Please read the following information before continuing.';
    PageStatic.Caption := 'Review the issues listed below then click Next to continue';
  end;

  PageMemo.Text := GetRec.Text;

end;

procedure ShowCheckPage;
begin

  ProgressPage.Caption := 'Checking your settings';
  ProgressPage.Description := 'Please wait';
  ProgressPage.SetText('Checking:', SettingsPage.Values[0]);
  ProgressPage.SetProgress(25, 100);
  ProgressPage.Show;

  try

    ProgressPage.SetProgress(50, 100);
    CheckPhp(SettingsPage.Values[0]);

    if PhpRec.Error <> '' then
    begin
      UpdateErrorPage();
      ProgressPage.SetProgress(100, 100);
      Exit;
    end;

    ProgressPage.SetProgress(80, 100);
    ProgressPage.SetText('Checking:', 'Environment paths');
    CheckPath;

    ProgressPage.SetProgress(100, 100)

    if PathError <> '' then
      UpdateErrorPage();

  finally
    ProgressPage.Hide;
  end;

end;

function ShowDownloadPage(CurPageID: Integer): Boolean;
begin

  Result := True;

  if GetRec.Next = NEXT_OK then
    Exit;

  ProgressPage.Caption := 'Downloading Composer';
  ProgressPage.Description := 'Please wait';
  ProgressPage.SetText('Downloading from {#AppUrl}...', 'composer.phar');
  ProgressPage.SetProgress(25, 100);
  ProgressPage.Show;

  try
    ProgressPage.SetProgress(50, 100);
    DownloadWork;
  finally
    ProgressPage.Hide;
  end;

  if GetRec.Text <> '' then
  begin
    UpdateDownloadMsgPage;
    Result := CurPageID = wpReady;
  end;

end;

procedure TestUpdateCaption();
var
  Id: String;
  Caption: String;
  Index: Integer;
  Value: String;
  ClearBtn: TNewButton;

begin

  Id := ' /test: ';
  Caption := WizardForm.Caption;
  Index := Pos(Id, WizardForm.Caption);
  Value := '';

  if Test <> TEST_FLAG then
    Value := Id + Test;

  if Index <> 0 then
    Caption := Copy(WizardForm.Caption, 1, Index - 1);

  WizardForm.Caption := Caption + Value;
  ClearBtn := TNewButton(WizardForm.FindComponent('BtnClear'));
  ClearBtn.Enabled := Value <> '';

end;

function CheckAlreadyInstalled: Boolean;
var
  Uninstaller: String;
  S: String;

begin

  Result := False;

  if IsAdminLoggedOn then
    Exit;

  Uninstaller := ExpandConstant('{userappdata}') + '\Composer\bin\unins000.exe';

  if FileExists(Uninstaller) then
  begin
    S := 'Composer is already installed for user ' + GetUserNameString() + '.' + LF + LF;
    S := S + 'Please uninstall it if you wish to continue.';

    MsgBox(S, mbCriticalError, mb_Ok);
    Result := True;
    Exit;
  end;

  Uninstaller := ExpandConstant('{commonappdata}') + '\Composer\bin\unins000.exe';

  if FileExists(Uninstaller) then
  begin
    S := 'Composer is already installed on this computer for All Users.' + LF + LF;
    S := S + 'Please uninstall it if you wish to continue.';

    MsgBox(S, mbCriticalError, mb_Ok);
    Result := True;
    Exit;
  end;

end;

procedure TestButtonClick(Sender: TObject);
var
  Form: TSetupForm;
  Edit: TNewEdit;
  Btn: TNewButton;

begin

  Form := CreateCustomForm();

  try

    Form.ClientWidth := ScaleX(256);
    Form.ClientHeight := ScaleY(128);
    Form.Caption := 'Enter Test Identifier';
    Form.CenterInsideControl(WizardForm, False);

    Edit := TNewEdit.Create(Form);
    Edit.Top := ScaleY(10);
    Edit.Left := ScaleX(10);
    Edit.Width := Form.ClientWidth - ScaleX(2 * 10);
    Edit.Height := ScaleY(23);

    if Test <> TEST_FLAG then
      Edit.Text := Test;

    Edit.Parent := Form;

    Btn := TNewButton.Create(Form);
    Btn.Parent := Form;
    Btn.Width := ScaleX(75);
    Btn.Height := ScaleY(23);
    Btn.Left := Form.ClientWidth - ScaleX(75 + 6 + 75 + 10);
    Btn.Top := Form.ClientHeight - ScaleY(23 + 10);
    Btn.Caption := 'OK';
    Btn.ModalResult := mrOk;
    Btn.Default := True;

    Btn := TNewButton.Create(Form);
    Btn.Parent := Form;
    Btn.Width := ScaleX(75);
    Btn.Height := ScaleY(23);
    Btn.Left := Form.ClientWidth - ScaleX(75 + 10);
    Btn.Top := Form.ClientHeight - ScaleY(23 + 10);
    Btn.Caption := 'Cancel';
    Btn.ModalResult := mrCancel;
    Btn.Cancel := True;

    Form.ActiveControl := Edit;

    if Form.ShowModal() = mrOk then
    begin

      if Edit.Text <> '' then
        Test := Edit.Text
      else
        Test := TEST_FLAG;

      TestUpdateCaption();

    end;

  finally
    Form.Free();
  end;

end;

procedure TestClearButtonClick(Sender: TObject);
begin
  Test := TEST_FLAG;
  TestUpdateCaption();
end;

procedure TestCreateButtons(ParentForm: TSetupForm; CancelButton: TNewButton);
var
  BtnTest: TNewButton;
  BtnClear: TNewButton;

begin

  BtnTest := TNewButton.Create(ParentForm);
  BtnTest.Left := ParentForm.ClientWidth - CancelButton.Left - CancelButton.Width;
  BtnTest.Top := CancelButton.Top;
  BtnTest.Width := CancelButton.Width;
  BtnTest.Height := CancelButton.Height;
  BtnTest.Caption := '&Enter Test';
  BtnTest.OnClick := @TestButtonClick;
  BtnTest.Parent := ParentForm;

  BtnClear := TNewButton.Create(ParentForm);
  BtnClear.Name := 'BtnClear';
  BtnClear.Left := ParentForm.ClientWidth - CancelButton.Left - CancelButton.Width;
  BtnClear.Left := BtnTest.Left + BtnTest.Width + ScaleX(10);
  BtnClear.Top := CancelButton.Top;
  BtnClear.Width := CancelButton.Width;
  BtnClear.Height := CancelButton.Height;
  BtnClear.Caption := '&Clear Test';
  BtnClear.OnClick := @TestClearButtonClick;
  BtnClear.Parent := ParentForm;
  BtnClear.Enabled := False;

end;

function InitializeSetup(): Boolean;
begin

  Completed := False;
  CmdExe := ExpandConstant('{cmd}');
  TmpDir := ExpandConstant('{tmp}');

  ExtractTemporaryFile('setup.php');
  TmpFile.Setup := AddBackslash(TmpDir) + 'setup.php';
  ExtractTemporaryFile('setup.class.php');

  ExtractTemporaryFile('composer');
  TmpFile.Composer := AddBackslash(TmpDir) +'composer';

  TmpFile.Result := AddBackslash(TmpDir) + 'result.txt';

  ResetPhp();
  InitRecordsFromPath();

  if CheckAlreadyInstalled() then
    Exit;

  if Pos('/TEST', GetCmdTail) <> 0 then
    Test := TEST_FLAG;

  Result := True;

end;

procedure DeinitializeSetup();
begin

  if not Completed then
  begin
    Debug('Setup cancelled or aborted');
    PathsRemove();
  end;

end;

procedure InitializeWizard;
begin

  ProgressPage := CreateOutputProgressPage('', '');
  ProgressPage.ProgressBar.Style := npbstMarquee;

  SettingsPage := CreateInputFilePage(wpWelcome,
    'Settings Check',
    'We need to check your PHP and path settings.',
    'Select where php.exe is located, then click Next.');

  if Test = '' then
    SettingsPage.Add('', 'php.exe|php.exe', '.exe')
  else
    SettingsPage.Add('', 'All files|*.*', '');

  ErrorPage := CreateMessagePage(SettingsPage.ID,
    '', '', 'Please review and fix the issues listed below, then click Back and try again');

  DownloadInfoPage := CreateMessagePage(wpReady, '', '', '');

  FinishedInfoPage := CreateOutputMsgPage(wpInstalling,
  'Information',
  'Please read the following important information before continuing.',
  'Setup has changed your path variable, but not all running programs will be aware of this. ' +
  'To use Composer for the first time, you may have to open a NEW command window.');

  if Test = TEST_FLAG then
    TestCreateButtons(WizardForm, WizardForm.CancelButton);

end;

procedure CurPageChanged(CurPageID: Integer);
begin

  if CurPageID = SettingsPage.ID then
  begin

    if FileExists(PhpRec.Exe) then
      SettingsPage.Values[0] := PhpRec.Exe;

    WizardForm.ActiveControl := nil;

  end
  else if CurPageID = ErrorPage.ID then
  begin

    WizardForm.ActiveControl := nil;
    WizardForm.NextButton.Enabled := False;

  end
  else if CurPageID = wpPreparing then
  begin

    // only shown for a major error
    WizardForm.BackButton.Enabled := False;

  end
  else if CurPageID = DownloadInfoPage.ID then
  begin

    WizardForm.ActiveControl := nil;
    WizardForm.NextButton.Enabled := GetRec.Next <> NEXT_NONE;

    if GetRec.Next = NEXT_RETRY then
      WizardForm.NextButton.Caption := 'Retry';

  end;

end;

function ShouldSkipPage(PageID: Integer): Boolean;
begin

  Result := False;

  if PageID = ErrorPage.ID then
    Result := (PhpRec.Error = '') and (PathError = '')
  else if PageID = DownloadInfoPage.ID then
    Result := GetRec.Text = ''
  else if PageId = FinishedInfoPage.ID then
    Result := not Flags.PathChanged;

end;

function NextButtonClick(CurPageID: Integer): Boolean;
begin

  Result := True;

  if CurPageID = SettingsPage.ID then
  begin

    if not FileExists(SettingsPage.Values[0]) then
    begin
      MsgBox('The file you specified does not exist.', mbCriticalError, MB_OK);
      Result := False;
    end
    else
      ShowCheckPage();

  end
  else if CurPageID = wpReady then
    Result := ShowDownloadPage(CurPageID)
  else if CurPageID = DownloadInfoPage.ID then
    Result := ShowDownloadPage(CurPageID);

end;

function BackButtonClick(CurPageID: Integer): Boolean;
begin

  Result := True;

  if CurPageID = DownloadInfoPage.ID then
    ResetGetRec(False);

end;

procedure CancelButtonClick(CurPageID: Integer; var Cancel, Confirm: Boolean);
begin

  // remove cancel confirmation where it is not necessary
  case CurPageID of
    wpWelcome: Confirm := False;
    ErrorPage.ID: Confirm := False;
    DownloadInfoPage.ID: Confirm := False;
   end;

end;

function UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo,
  MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: String): String;
var
  S: String;
  Env: String;

begin

  S := MemoDirInfo;

  // this line left over from Start Menu days. Do not remove yet
  if (MemoGroupInfo <> '') and not Flags.Installed then
    S := S + NewLine + NewLine + MemoGroupInfo;

  S := S + NewLine + NewLine + 'PHP version ' + PhpRec.Version;
  S := S + NewLine + Space + PhpRec.Exe;

  Env := ' Path environment variable:';

  if Flags.AddPhp.Path <> '' then
  begin
    S := S + NewLine + NewLine + 'Add to ' + Flags.AddPhp.Name + Env;
    S := S + NewLine + Space + Flags.AddPhp.Path;
  end;

  if Flags.AddComposer.Path <> '' then
  begin
    S := S + NewLine + NewLine + 'Add to ' + Flags.AddComposer.Name + Env;
    S := S + NewLine + Space + Flags.AddComposer.Path;
  end;

  Result := S;

end;

function PrepareToInstall(var NeedsRestart: Boolean): String;
var
  Lines: TArrayOfString;
  S: AnsiString;
  I: Integer;

begin

  Result := '';

  Debug('Running PrepareToInstall tasks');

  PathsAdd(Result);

  if Result <> '' then
    Exit;

  if LoadStringsFromFile(TmpFile.Composer, Lines) then
  begin

    S := '';
    for I := 0 to GetArrayLength(Lines) - 1 do
      S := S + Lines[I] + #10;

    SaveStringToFile(TmpDir + '\composer', S, False);

  end;

end;

procedure CurStepChanged(CurStep: TSetupStep);
begin

  if CurStep = ssPostInstall then
    Completed := True;

end;

procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
var
  Rec: TPathRec;

begin

  if CurUninstallStep = usUninstall then
  begin

    // we must call this here, or the dll and app dir will not be deleted
    UserDataDelete();

    SetPathRec(Rec, ExpandConstant('{app}\bin'));
    RemoveFromPath(Rec.Hive, Rec.Path);
    NotifyPathChange();

  end;

end;

; Running this script will compile composer-setup with the following default settings:
; Output filename: /Output/setup.exe, exe version info: 0.0.0.0
;
; Use the command-line compiler to change this (see Inno Help), for example:
; iscc /o"My\Output\Folder" /f"MyProgram" /d"SetupVersion=n.n" "path\to\composer.iss"

#ifndef SetupVersion
  ; do not change this
  #define SetupVersion
#endif

#define CmdPhp "php.exe"
#define CmdBat "composer.bat"
#define CmdShell "composer"
#define DllData "userdata.dll"

#define AppDescription "Composer - Php Dependency Manager"
#define AppUrl "getcomposer.org"

#define CS_SETUP_GUID "3ECDC245-751A-4962-B580-B8A250EDD1CF"
#define GUID_LEN Len(CS_SETUP_GUID)

[Setup]
; app name and version, must both be Composer
AppName=Composer
AppVerName=Composer
AppPublisher={#AppUrl}

; compile directives
Compression=lzma
SolidCompression=yes

; runtime  directives
MinVersion=5.1
PrivilegesRequired=none
AllowCancelDuringInstall=false

; directory stuff
DefaultDirName={commonappdata}\Composer
DisableDirPage=yes
AppendDefaultDirName=no
DirExistsWarning=no
AlwaysShowDirOnReadyPage=yes

; uninstall
Uninstallable=yes
UninstallDisplayName={#AppDescription}
UninstallFilesDir={app}\bin

; exe version info
VersionInfoVersion={#SetupVersion}
VersionInfoProductVersion=0
VersionInfoProductName={#AppDescription}

; cosmetic
WizardImageFile=wiz.bmp
WizardSmallImageFile=wizsmall.bmp

[Files]
Source: "setup.php"; Flags: dontcopy
Source: "setup.class.php"; Flags: dontcopy
Source: "userdata.dll"; DestDir: "{app}\bin"; Flags: ignoreversion
Source: "shims\{#CmdShell}"; Flags: dontcopy
Source: "shims\{#CmdBat}"; DestDir: "{app}\bin"; Flags: ignoreversion
Source: "{tmp}\{#CmdShell}"; DestDir: "{app}\bin"; Flags: external ignoreversion
Source: "{tmp}\composer.phar"; DestDir: "{app}\bin"; Flags: external ignoreversion

[Dirs]
; we need to make all-users directory writeable so composer.phar can update
Name: {app}; Permissions: users-modify; Check: IsAdminLoggedOn;

[Run]
Filename: "http://{#AppUrl}"; Description: "View online documentation"; Flags: postinstall shellexec unchecked;

[UninstallDelete]
; to force deletion of \Composer
Type: filesandordirs; Name: "{app}"

[Messages]
SetupWindowTitle=%1 Setup
WelcomeLabel1=[name] Setup
ReadyLabel1=Setup is now ready to download [name] and install it on your computer.
ReadyLabel2a=Please review these settings. Click Install to continue with the installation.
FinishedHeadingLabel=Completing [name] Setup
FinishedLabelNoIcons=Setup has installed [name] on your computer.%nUsage: Open a command window and type "composer"
FinishedLabel=Setup has installed [name] on your computer.%nUsage: Open a command window and type "composer"

[Code]

type
  TPhpRec = record
    Exe     : String;
    Version : String;
    Error   : String;
  end;

type
  TSearchRec = record
    System  : String;
    User    : String;
    Cmd     : String;
    Path    : String;
  end;

type
  TPathList = record
    Safe    : Boolean;
    Items   : TArrayOfString;
  end;

type
  TPathInfo = record
    Php       : TSearchRec;
    Bat       : TSearchRec;
    Shell     : TSearchRec;
    PathList  : TPathList;
  end;

type
  TTmpFile = record
    Setup     : String;
    Composer  : String;
    Result    : String;
  end;

type
  TGetRec = record
    Error   : Integer;
    Next    : Integer;
    Force   : Boolean;
    Text    : String;
  end;

type
  TPathRec = record
    Path    : String;
    Hive    : Integer;
    Name    : String;
  end;

type
  TFlagsRec = record
    AddPhp      : TPathRec;
    AddComposer : TPathRec;
    PathChanged : Boolean;
    Installed   : Boolean;
    Completed   : Boolean;
  end;

var
  Completed: Boolean;
  TmpFile: TTmpFile;
  PhpRec: TPhpRec;
  CmdExe: String;
  ComposerPath: String;
  PathError: String;
  GetRec: TGetRec;
  Flags: TFlagsRec;
  TmpDir: String;
  Test: String;
  SettingsPage: TInputFileWizardPage;
  ProgressPage: TOutputProgressWizardPage;
  ErrorPage: TWizardPage;
  DownloadInfoPage: TWizardPage;
  FinishedInfoPage: TOutputMsgWizardPage;

const
  CSIDL_PROFILE = $0028;
  SEP_PATH = ';';
  LF = #13#10;
  TEST_FLAG = '?';

  ERR_NONE = 0;
  ERR_INSTALL = 1;
  ERR_UNKNOWN = 10;
  ERR_CMD = 11;
  ERR_CMD_EX = 12;
  ERR_CMD_PHP = 13;
  ERR_PHP = 20;
  ERR_STATUS = 21;
  ERR_RESULT = 22;
  ERR_INVALID = 23;
  ERR_LOGIC = 24;
  ERR_CONN = 30;
  ERR_DOWNLOAD = 31;

  NEXT_NONE = 0;
  NEXT_RETRY = 1;
  NEXT_OK = 2;

procedure Debug(const Message: String); forward;

#include "paths.iss"
#include "userdata.iss"

procedure Debug(const Message: String);
begin
  Log('DEBUG:: ' + Message);
end;

function ResultIdLine(const Line: String; var S: String): Boolean;
begin

  Result := False;
  S := '';

  if Pos('{#CS_SETUP_GUID}', Line) <> 0 then
  begin
    S := Copy(Line, {#GUID_LEN} + 1, MaxInt);
    Result := True;
  end;

end;

function DebugPhp(const Line: String): Boolean;
var
  S: String;

begin

  Result := False;
  S := '';

  if ResultIdLine(Line, S) then
  begin
    Log('PHPDBG:: ' + S);
    Result := True;
  end;

end;

procedure ResetGetRec(Full: Boolean);
begin

  GetRec.Error := ERR_NONE;
  GetRec.Next := NEXT_NONE;

  if Full then
    GetRec.Force := False;

  GetRec.Text := '';

end;

procedure ResetPhp;
begin

  PhpRec.Exe := '';
  PhpRec.Version := '';
  PhpRec.Error := '';

  ResetGetRec(True);

end;

procedure AddLine(var Existing: String; const Value: String);
begin

  if Existing <> '' then
    Existing := Existing + LF;

  Existing := Existing + Value;

end;

procedure AddSwitch(var Switches: String; const Name, Value: String);
begin

  if Switches = '' then
    Switches := '--';

  Switches := Switches + ' --' + Name;

  if Value <> '' then
  begin
    // we don't want to send default test ? value
    if (Name <> 'test') or (Test <> '?') then
      Switches := Switches + ' "' + Value + '"';
  end;

end;

procedure GetCmdResults(Results: TArrayOfString; var Output: String);
var
  Count: Integer;
  I: Integer;
  Line: String;

begin

  Count := GetArrayLength(Results);

  for I := 0 to Count - 1 do
  begin

    Line := Results[I];

    // filter any initial empty output
    if (Output = '') and (Trim(Line) = '') then
      Continue;

    // filter any shebang
    if Pos('#!', TrimLeft(Line)) = 1 then
      Continue;

    if not DebugPhp(Line) then
      AddLine(Output, Line);

  end;

end;

procedure SetSearchRec(var Rec: TSearchRec);
begin

  if Rec.System <> '' then
  begin
    Rec.Cmd := Rec.System;
    Rec.Path := ExtractFileDir(Rec.System);
    Rec.User := '';
  end
  else if Rec.User <> '' then
  begin
    Rec.Cmd := Rec.User;
    Rec.Path := ExtractFileDir(Rec.User);
  end
  else
  begin
    Rec.Cmd := '';
    Rec.Path := '';
  end;

end;

function GetPathInfo: TPathInfo;
var
  List1: TPathList;
  List2: TPathList;
  C1: Integer;
  C2: Integer;
  I: Integer;

begin

  Debug('Getting path info from registry');

  List1 := GetSafePathList(HKEY_LOCAL_MACHINE);
  Result.Php.System := SearchPath(List1, '{#CmdPhp}');
  Result.Bat.System := SearchPath(List1, '{#CmdBat}');
  Result.Shell.System := SearchPath(List1, '{#CmdShell}');

  List2 := GetSafePathList(HKEY_CURRENT_USER);
  Result.Php.User := SearchPath(List2, '{#CmdPhp}');
  Result.Bat.User := SearchPath(List2, '{#CmdBat}');
  Result.Shell.User := SearchPath(List2, '{#CmdShell}');

  SetSearchRec(Result.Php);
  SetSearchRec(Result.Bat);
  SetSearchRec(Result.Shell);

  Result.PathList.Safe := True;
  C1 := GetArrayLength(List1.Items);
  C2 := GetArrayLength(List2.Items);
  SetArrayLength(Result.PathList.Items, C1 + C2);

  for I := 0 to C1 - 1 do
    Result.PathList.Items[I] := List1.Items[I];

  for I := 0 to C2 - 1 do
    Result.PathList.Items[C1 + I] := List2.Items[I];

end;

procedure SetPathRec(var Rec: TPathRec; const Path: String);
begin

  Rec.Path := Path;
  Rec.Hive := HKEY_CURRENT_USER;
  Rec.Name := 'User';

  if (Rec.Path <> '') and IsAdminLoggedOn then
  begin
    Rec.Hive := HKEY_LOCAL_MACHINE;
    Rec.Name := 'System';
  end;

end;

function GetAppDir(): String;
begin

  if IsAdminLoggedOn then
    Result := ExpandConstant('{commonappdata}\Composer')
  else
    Result := ExpandConstant('{userpf}\Composer');

end;

function GetInstallDir(const AppDir: String): String;
begin
  Result := AddBackslash(AppDir) + 'bin';
end;

procedure InitRecordsFromPath;
var
  Info: TPathInfo;

begin

  Info := GetPathInfo;
  PhpRec.Exe := Info.Php.Cmd;

end;

function CheckPhpPath(PathList: TPathList; Rec: TSearchRec): String;
var
  S: String;
  Env: String;
  PhpPath: String;

begin

  Result := '';

  Debug('Checking for php path');

  if Rec.Path = '' then
  begin

    PhpPath := ExtractFileDir(PhpRec.Exe);

    if not DirectoryInPath(PhpPath, PathList) then
      SetPathRec(Flags.AddPhp, PhpPath);

    Exit;

  end;

  if CompareText(Rec.Cmd, PhpRec.Exe) = 0 then
    Exit;

  S := 'The php exe you selected does not match the one found in your path.' + LF;
  S := S + LF;
  S := S + 'Selected: ' + PhpRec.Exe + LF;
  S := S + 'In Path: ' + Rec.Cmd + LF;
  S := S + LF;

  if Rec.System <> '' then
    Env := 'System'
  else
    Env := 'User';

  S := S + 'You can either select the one in your path, or remove its entry from your ';
  S := S + Env + ' Path Environment variable:' + LF;
  S := S + '   ' + Rec.Path + LF;
  S := S + LF;

  S := S + 'Note: Only change your path if you are sure that it will not affect anything else.' + LF;
  S := S + LF;

  S := S + 'If neither of these options are suitable, you will have to install Composer manually.';

  Result := S;

end;

function CheckShim(Rec: TSearchRec; Cmd: String; var Installed: Boolean): String;
var
  S: String;

begin

  S := '';

  if Rec.Path <> '' then
  begin

    if CompareText(Rec.Cmd, Cmd) <> 0 then
    begin
      S := 'Composer is already installed in the following directory:' + LF;
      S := S + Rec.Path + LF;
      S := S + LF;
      S := S + 'You must remove it first, if you want to continue this installation.' + LF;
    end;

    // we only set Installed to true
    Installed := True;

  end;

  Result := S;

end;

function CheckComposerPath(Info: TPathInfo): String;
var
  BinPath: String;
  Cmd: String;

begin

  Result := '';

  Debug('Checking for composer path');

  BinPath := GetInstallDir(WizardDirValue);

  if (Info.Bat.Path = '') and (Info.Shell.Path = '') then
  begin

    if not DirectoryInPath(BinPath, Info.PathList) then
      SetPathRec(Flags.AddComposer, BinPath);

    Exit;

  end;

  Cmd := AddBackslash(BinPath) + '{#CmdBat}';

  Result := CheckShim(Info.Bat, Cmd, Flags.Installed)

  if Result = '' then
  begin
    Cmd := AddBackslash(BinPath) + '{#CmdShell}';
    Result := CheckShim(Info.Shell, Cmd, Flags.Installed);
  end;

end;

function CheckPathExt: String;
var
  Hive: Integer;
  Key: String;
  Value: String;
  PathExt: String;
  Missing: String;
  Space: String;

begin

  Result := '';

  Debug('Checking PathExt values');

  PathExt := '';

  Hive := HKEY_LOCAL_MACHINE;
  Key := GetPathKeyForHive(Hive);
  Value := '';

  if RegQueryStringValue(Hive, Key, 'PathExt', Value) then
    PathExt := Value;

  Hive := HKEY_CURRENT_USER;
  Key := GetPathKeyForHive(Hive);
  Value := '';

  if RegQueryStringValue(Hive, Key, 'PathExt', Value) then
    PathExt := PathExt + ';' + Value;

  PathExt := Uppercase(PathExt  + ';');

  Missing := '';
  Space := '    ';

  if Pos('.EXE;', PathExt) = 0 then
    Missing := LF + Space + '.EXE';

  if Pos('.BAT;', PathExt) = 0 then
    Missing := Missing + LF + Space + '.BAT';

  if Missing <> '' then
    Result := 'Your PathExt Environment variable is missing required values:' + Missing;

end;

procedure CheckPath;
var
  Info: TPathInfo;

begin

  Debug('Checking paths');

  SetPathRec(Flags.AddPhp, '');
  SetPathRec(Flags.AddComposer, '');
  Flags.Installed := False;
  Flags.PathChanged := False;

  Info := GetPathInfo;

  PathError := CheckPhpPath(Info.PathList, Info.Php);

  if PathError = '' then
    PathError := CheckComposerPath(Info);

  if PathError = '' then
    PathError := CheckPathExt;

  ComposerPath := '';

  if Info.Bat.Path <> '' then
  begin

    if FileExists(Info.Bat.Path + '\composer.phar') then
      ComposerPath := Info.Bat.Path;

  end;

  if (ComposerPath = '') and (Info.Shell.Path <> '') then
  begin

    if FileExists(Info.Shell.Path + '\composer.phar') then
      ComposerPath := Info.Shell.Path;

  end;

end;

procedure PathsAdd(var Error: String);
begin

  Error := '';

  if Flags.AddPhp.Path <> '' then
  begin

    if not AddToPath(Flags.AddPhp.Hive, Flags.AddPhp.Path) then
    begin
      Error := 'Error setting ' + Flags.AddPhp.Name + ' Path variable';
      Exit;
    end;

    Flags.PathChanged := True;

  end;

  if Flags.AddComposer.Path <> '' then
  begin

    if not AddToPath(Flags.AddComposer.Hive, Flags.AddComposer.Path) then
    begin

      Error := 'Error setting ' + Flags.AddComposer.Name + ' Path variable';

      // remove php path in the unlikely event we have just added it
      if Flags.PathChanged then
      begin
        RemoveFromPath(Flags.AddPhp.Hive, Flags.AddPhp.Path);
        Flags.PathChanged := False;
        NotifyPathChange;
      end;

      Exit;

    end;

    Flags.PathChanged := True;

  end;

  if Flags.PathChanged then
    NotifyPathChange;

end;

procedure PathsRemove;
begin

  if Flags.PathChanged then
  begin

    if Flags.AddPhp.Path <> '' then
      RemoveFromPath(Flags.AddPhp.Hive, Flags.AddPhp.Path);

    if Flags.AddComposer.Path <> '' then
      RemoveFromPath(Flags.AddComposer.Hive, Flags.AddComposer.Path);

    Flags.PathChanged := False;
    NotifyPathChange;

  end;

end;

function StatusCodeText(Status: Integer): String;
begin

 case Status of

  ERR_NONE: Result := 'ERR_NONE';
  ERR_INSTALL: Result := 'ERR_INSTALL';
  ERR_UNKNOWN: Result := 'ERR_UNKNOWN';
  ERR_CMD: Result := 'ERR_CMD';
  ERR_CMD_EX: Result := 'ERR_CMD_EX';
  ERR_CMD_PHP: Result := 'ERR_CMD_PHP';
  ERR_PHP: Result := 'ERR_PHP';
  ERR_STATUS: Result := 'ERR_STATUS';
  ERR_RESULT: Result := 'ERR_RESULT';
  ERR_INVALID: Result := 'ERR_INVALID';
  ERR_LOGIC: Result := 'ERR_LOGIC';
  ERR_CONN: Result := 'ERR_CONN';
  ERR_DOWNLOAD: Result := 'ERR_DOWNLOAD';

 end;

 Result := Format('[%s]', [Result]);

end;

function ExecCmd(const PhpExe, Switches: String; Show: Integer; var ExitCode: Integer): Boolean;
var
  Args: String;
  Params: String;

begin

  if FileExists(TmpFile.Result) then
    DeleteFile(TmpFile.Result);

  Args := Switches;

  if Test <> '' then
  begin
    AddSwitch(Args, 'test', Test);
    Debug('Running test: ' + Test);
  end;

  if Pos('/LOG', GetCmdTail) <> 0 then
    AddSwitch(Args, 'debug', '');

  // we must not quote Args since they are quoted individually
  Params := Format('/c "%s %s %s > %s"', [AddQuotes(PhpExe), AddQuotes(TmpFile.Setup), Args, AddQuotes(TmpFile.Result)]);
  Debug('Calling cmd.exe with params: ' + Params);
  Result := Exec(CmdExe, Params, TmpDir, Show, ewWaitUntilTerminated, ExitCode);

end;

function GetSysError(ErrorCode: Integer; const Filename: String; var Error: String): Integer;
begin

  Error := SysErrorMessage(ErrorCode);
  Result := StringChangeEx(Error, '%1', '%s', True);

  if Result = 1 then
    Error := Format(Error, [Filename]);

end;

function GetCommonCmdError(StatusCode, ExitCode: Integer): String;
var
  Error: String;

begin

  Result := '';

  if StatusCode = ERR_CMD then
  begin
    GetSysError(ExitCode, CmdExe, Error);
    Result := 'Internal Error [ERR_CMD]: ' + Error;
  end
  else if StatusCode = ERR_CMD_EX then
    Result := 'Internal Error [ERR_CMDEX]: A command did not run correctly';

end;

procedure SetPhpError(ErrorCode, ExitCode: Integer; const Filename: String);
var
  Text: String;
  Error: String;
  Name: String;

begin

  Text := '';
  Name := StatusCodeText(ErrorCode);

  case ErrorCode of

    ERR_CMD_PHP:
    begin

      if GetSysError(ExitCode, Filename, Error) = 0 then
        Text := 'The PHP exe file you specified did not execute correctly: ' + Filename + LF
      else
        Text := Error;

    end;

    ERR_CMD, ERR_CMD_EX:
      Text := GetCommonCmdError(ErrorCode, ExitCode);

    ERR_STATUS, ERR_RESULT, ERR_INVALID:
    begin

      Error := Format('The PHP exe file you specified did not execute correctly: %s%s%s', [LF, Filename, LF]);
      Error := Error + LF + 'Running it from the command line might highlight the problem.'

      if ErrorCode = ERR_STATUS then
      begin

        if ExitCode = 255 then
          Error := Error + LF + 'Use the -v switch to show the PHP version - it must be at least 5.3.2';

      end;

      Text := Format('Internal Error %s, exit code %d', [Name, ExitCode]);
      Text := Error + LF + Text;

    end;

    ERR_LOGIC:
    begin
      Text := Format('An internal script did not run correctly (exit code %d)', [ExitCode]);
      Text := Format('Internal Error %s: %s', [Name, Text]);
    end;

  else

    begin
      ErrorCode := ERR_UNKNOWN;
      Text := Format('Internal Error %s: An unspecified error occurred', [Name]);
    end;

  end;

  PhpRec.Error := Text;
  Debug(Format('Checking php: error %s', [Name]));

end;

function CheckPhpExe(const Filename: String): Boolean;
var
  Show: Integer;
  ExitCode: Integer;

begin

  Result := False;

  {
   * Possible errors:
   * Php.exe error - did not run [ERR_CMD_PHP]
  }

  if Test <> '' then
    Show := SW_SHOW
  else
    Show := SW_HIDE;

  Result := Exec(Filename, '-v', TmpDir, Show, ewWaitUntilTerminated, ExitCode);

  if not Result then
    SetPhpError(ERR_CMD_PHP, ExitCode, Filename);

end;

function CheckPhp(const Filename: String): Boolean;
var
  Switches: String;
  Show: Integer;
  ExitCode: Integer;
  Results: TArrayOfString;

begin

  Result := False;

  {
   * Possible errors:
   * Internal error - cmd did not run [ERR_CMD]
   * Internal error - cmd did not create output file run [ERR_CMD_EX]
   * ExitCode: 0 - Php check passed
   * ExitCode: 1 - Php check failed
   * ExitCode: ? - Php program error [ERR_STATUS] (Test=p1, Test=p2)
   * Results file, empty: [ERR_RESULT] (Test=p3)
   * Results file, non-matching guid: [ERR_INVALID] (Test=p4)
   * Results file, ExitCode 0, multiline [ERR_LOGIC] (test=p5)
   * Results file, ExitCode 1, guid only [ERR_LOGIC] (test=p6)
  }

  ResetPhp;

  Debug('Checking php: ' + Filename);

  if not CheckPhpExe(Filename) then
    Exit;

  AddSwitch(Switches, 'php', '');
  Show := Integer(Test <> '');

  if not ExecCmd(Filename, Switches, Show, ExitCode) then
  begin
    SetPhpError(ERR_CMD, ExitCode, Filename);
    Exit;
  end;

  if not LoadStringsFromFile(TmpFile.Result, Results) then
  begin
    SetPhpError(ERR_CMD_EX, ExitCode, Filename);
    Exit;
  end;

  if (ExitCode <> 0) and (ExitCode <> 1) then
  begin
    SetPhpError(ERR_STATUS, ExitCode, Filename);
    Exit;
  end;

  if GetArrayLength(Results) = 0 then
  begin
    SetPhpError(ERR_RESULT, ExitCode, Filename);
    Exit;
  end;

  // get php version
  if not ResultIdLine(Results[0], PhpRec.Version) then
  begin
    SetPhpError(ERR_INVALID, ExitCode, Filename);
    Exit;
  end;

  GetCmdResults(Results, PhpRec.Error);

  if (ExitCode = 0) and (PhpRec.Error <> '') then
  begin
    SetPhpError(ERR_LOGIC, ExitCode, Filename);
    Exit;
  end;

  if (ExitCode = 1) and (PhpRec.Error = '') then
  begin
    SetPhpError(ERR_LOGIC, ExitCode, Filename);
    Exit;
  end;

  PhpRec.Exe := Filename;

  Result := PhpRec.Error = '';

end;

procedure SetDownloadStatus(StatusCode, ExitCode: Integer);
var
  Text: String;

begin

  Text := '';
  ResetGetRec(True);

  case StatusCode of

    ERR_NONE: GetRec.Next := NEXT_OK;

    ERR_INSTALL: GetRec.Next := NEXT_NONE;

    ERR_CMD, ERR_CMD_EX:
    begin
      GetRec.Next := NEXT_RETRY;
      Text := GetCommonCmdError(StatusCode, ExitCode);
    end;

    ERR_PHP:
    begin
      GetRec.Next := NEXT_RETRY;
      Text := 'Internal Error [ERR_PHP]: An internal script did not run correctly';
    end;

    ERR_STATUS:
    begin
      GetRec.Next := NEXT_RETRY;
      GetRec.Force := True;
      Text := Format('Composer Error [ERR_STATUS]: Unexpected exit code from Composer (%d)', [ExitCode]);
     end;

    ERR_DOWNLOAD:
    begin
      GetRec.Next := NEXT_RETRY;
      GetRec.Force := True;
      Text := 'Composer Error [ERR_DOWNLOAD]: Composer was not downloaded';
    end;

    ERR_INVALID:
    begin
      GetRec.Next := NEXT_RETRY;
      GetRec.Force := True;
      Text := 'Composer Error [ERR_INVALID]: The installer script did not run correctly';
    end;

    ERR_CONN:
    begin
      GetRec.Next := NEXT_RETRY;
      GetRec.Force := True;
      Text := 'Connection Error [ERR_CONNECTION]: Unable to connect to {#AppUrl}';
    end;

  else

    begin
      StatusCode := ERR_UNKNOWN;
      GetRec.Next := NEXT_RETRY;
      Text := 'Internal Error [ERR_UNKNOWN]: An unspecified error occurred';
    end;

  end;

  GetRec.Error := StatusCode;
  GetRec.Text := Text;

end;

procedure SetDownloadCmdError(ExitCode: Integer);
var
  Error: String;
  Text: String;

begin

  SetDownloadStatus(ERR_CMD, ExitCode);

  if GetSysError(ExitCode, CmdExe, Error) = 0 then
    Text := Error + CmdExe
  else
    Text := Error;

  GetRec.Text := GetRec.Text + Text;

end;

procedure DownloadWork;
var
  Switches: String;
  ExitCode: Integer;
  Results: TArrayOfString;

begin

  {
   * Possible errors:
   * Internal error - cmd did not run [ERR_CMD]
   * Internal error - cmd did not create output file [ERR_CMD_EX]
   * ExitCode: 0 - Installed, no warnings [ERR_NONE]
   * ExitCode: 0 - Installed, warnings [ERR_NONE] (Test=d1)
   * ExitCode: 1 - Not Installed, errors [ERR_INSTALL] (Test=d2)
   * ExitCode: 2 - Php script did not run properly [ERR_PHP] (Test=d3)
   * ExitCode: 3 - Connection error of some sort [ERR_CONNECTION] (Test=d4) (Test=d5)
   * ExitCode: ? - Unexpected exit code from Composer, didn't return 0 or 1 [ERR_STATUS] (Test=d6)
   * ExitCode: 0 - No composer.phar downloaded [ERR_DOWNLOAD] (Test=d7)
   * ExitCode: 1 - No errors reported by Composer [ERR_INVALID] (Test=d8)
  }

  Debug('Downloading from {#AppUrl}');

  AddSwitch(Switches, 'download', '');

  if GetRec.Force then
    AddSwitch(Switches, 'force', '');

  if not ExecCmd(PhpRec.Exe, Switches, SW_HIDE, ExitCode) then
  begin
    SetDownloadCmdError(ExitCode);
    Exit;
  end;

  if not LoadStringsFromFile(TmpFile.Result, Results) then
  begin
    SetDownloadStatus(ERR_CMD_EX, ExitCode);
    Exit;
  end;

  // the following checks all exit
  if ExitCode = 0 then
  begin

    if not FileExists(TmpDir + '\composer.phar') then
    begin
      SetDownloadStatus(ERR_DOWNLOAD, ExitCode);
      Exit;
    end;

  end
  else if ExitCode = 2 then
  begin
    SetDownloadStatus(ERR_PHP, ExitCode);
    Exit;
  end
  else if ExitCode = 3 then
  begin
    SetDownloadStatus(ERR_CONN, ExitCode);
    AddLine(GetRec.Text, '');
    GetCmdResults(Results, GetRec.Text);
    Exit;
  end
  else if ExitCode <> 1 then
  begin
    SetDownloadStatus(ERR_STATUS, ExitCode);
    Exit;
  end;

  // must set status now
  if ExitCode = 0 then
    SetDownloadStatus(ERR_NONE, ExitCode)
  else
    SetDownloadStatus(ERR_INSTALL, ExitCode);

  if GetArrayLength(Results) = 0 then
  begin

    // no output, check that we are not expecting errors
    if ExitCode = 1 then
      SetDownloadStatus(ERR_INVALID, ExitCode);

    Exit;

  end;

  GetCmdResults(Results, GetRec.Text);
  GetRec.Text := Trim(GetRec.Text);

  // final check
  if (ExitCode = 1) and (GetRec.Text = '') then
    SetDownloadStatus(ERR_INVALID, ExitCode);

  if GetRec.Text <> '' then
    AddLine(GetRec.Text, '');

end;

function CreateMessagePage(Id: Integer; Caption, Description, Text: String): TWizardPage;
var
  StaticText: TNewStaticText;
  Memo: TNewMemo;
  Top: Integer;

begin

  Result := CreateCustomPage(Id, Caption, Description);

  StaticText := TNewStaticText.Create(Result);
  StaticText.Name := 'Static';
  StaticText.Caption := Text;
  StaticText.AutoSize := True;
  StaticText.Parent := Result.Surface;

  Top := StaticText.Top + StaticText.Height;

  Memo := TNewMemo.Create(Result);
  Memo.Name := 'Memo';
  Memo.Top := Top + ScaleY(8);
  Memo.Height := Result.SurfaceHeight - (Top + ScaleY(8) + ScaleY(15));
  Memo.Width := Result.SurfaceWidth;
  Memo.ScrollBars := ssVertical;
  Memo.ReadOnly := True;
  Memo.Parent := Result.Surface;
  Memo.Text := '';

end;

procedure UpdateErrorPage();
var
  Memo: TNewMemo;

begin

  Memo := TNewMemo(ErrorPage.FindComponent('Memo'));

  if PhpRec.Error <> '' then
  begin
    ErrorPage.Caption := 'PHP Settings Error';
    ErrorPage.Description := 'Composer will not work with your current settings'
    Memo.Text := PhpRec.Error;
  end
  else if PathError <> '' then
  begin
    ErrorPage.Caption := 'Path Settings Error';
    ErrorPage.Description := 'Setup cannot continue with your current settings'
    Memo.Text := PathError;
  end

end;

procedure UpdateDownloadMsgPage();
var
  PageStatic: TNewStaticText;
  PageMemo: TNewMemo;

begin

  PageStatic := TNewStaticText(DownloadInfoPage.FindComponent('Static'));
  PageMemo := TNewMemo(DownloadInfoPage.FindComponent('Memo'));

  if GetRec.Error <> ERR_NONE then
  begin

    DownloadInfoPage.Caption := 'Composer Download Error';
    DownloadInfoPage.Description := 'Unable to continue with installation';

    if GetRec.Error = ERR_INSTALL then
      PageStatic.Caption := 'Please review and fix the issues listed below then try again.'
    else
      PageStatic.Caption := 'An error occurred. Clicking Retry may resolve this issue.'

  end
  else
  begin
    DownloadInfoPage.Caption := 'Composer Warning';
    DownloadInfoPage.Description := 'Please read the following information before continuing.';
    PageStatic.Caption := 'Review the issues listed below then click Next to continue';
  end;

  PageMemo.Text := GetRec.Text;

end;

procedure ShowCheckPage;
begin

  ProgressPage.Caption := 'Checking your settings';
  ProgressPage.Description := 'Please wait';
  ProgressPage.SetText('Checking:', SettingsPage.Values[0]);
  ProgressPage.SetProgress(25, 100);
  ProgressPage.Show;

  try

    ProgressPage.SetProgress(50, 100);
    CheckPhp(SettingsPage.Values[0]);

    if PhpRec.Error <> '' then
    begin
      UpdateErrorPage();
      ProgressPage.SetProgress(100, 100);
      Exit;
    end;

    ProgressPage.SetProgress(80, 100);
    ProgressPage.SetText('Checking:', 'Environment paths');
    CheckPath;

    ProgressPage.SetProgress(100, 100)

    if PathError <> '' then
      UpdateErrorPage();

  finally
    ProgressPage.Hide;
  end;

end;

function ShowDownloadPage(CurPageID: Integer): Boolean;
begin

  Result := True;

  if GetRec.Next = NEXT_OK then
    Exit;

  ProgressPage.Caption := 'Downloading Composer';
  ProgressPage.Description := 'Please wait';
  ProgressPage.SetText('Downloading from {#AppUrl}...', 'composer.phar');
  ProgressPage.SetProgress(25, 100);
  ProgressPage.Show;

  try
    ProgressPage.SetProgress(50, 100);
    DownloadWork;
  finally
    ProgressPage.Hide;
  end;

  if GetRec.Text <> '' then
  begin
    UpdateDownloadMsgPage;
    Result := CurPageID = wpReady;
  end;

end;

procedure TestUpdateCaption();
var
  Id: String;
  Caption: String;
  Index: Integer;
  Value: String;
  ClearBtn: TNewButton;

begin

  Id := ' /test: ';
  Caption := WizardForm.Caption;
  Index := Pos(Id, WizardForm.Caption);
  Value := '';

  if Test <> TEST_FLAG then
    Value := Id + Test;

  if Index <> 0 then
    Caption := Copy(WizardForm.Caption, 1, Index - 1);

  WizardForm.Caption := Caption + Value;
  ClearBtn := TNewButton(WizardForm.FindComponent('BtnClear'));
  ClearBtn.Enabled := Value <> '';

end;

function CheckAlreadyInstalled: Boolean;
var
  Uninstaller: String;
  S: String;

begin

  Result := False;

  if IsAdminLoggedOn then
    Exit;

  Uninstaller := ExpandConstant('{userappdata}') + '\Composer\bin\unins000.exe';

  if FileExists(Uninstaller) then
  begin
    S := 'Composer is already installed for user ' + GetUserNameString() + '.' + LF + LF;
    S := S + 'Please uninstall it if you wish to continue.';

    MsgBox(S, mbCriticalError, mb_Ok);
    Result := True;
    Exit;
  end;

  Uninstaller := ExpandConstant('{commonappdata}') + '\Composer\bin\unins000.exe';

  if FileExists(Uninstaller) then
  begin
    S := 'Composer is already installed on this computer for All Users.' + LF + LF;
    S := S + 'Please uninstall it if you wish to continue.';

    MsgBox(S, mbCriticalError, mb_Ok);
    Result := True;
    Exit;
  end;

end;

procedure TestButtonClick(Sender: TObject);
var
  Form: TSetupForm;
  Edit: TNewEdit;
  Btn: TNewButton;

begin

  Form := CreateCustomForm();

  try

    Form.ClientWidth := ScaleX(256);
    Form.ClientHeight := ScaleY(128);
    Form.Caption := 'Enter Test Identifier';
    Form.CenterInsideControl(WizardForm, False);

    Edit := TNewEdit.Create(Form);
    Edit.Top := ScaleY(10);
    Edit.Left := ScaleX(10);
    Edit.Width := Form.ClientWidth - ScaleX(2 * 10);
    Edit.Height := ScaleY(23);

    if Test <> TEST_FLAG then
      Edit.Text := Test;

    Edit.Parent := Form;

    Btn := TNewButton.Create(Form);
    Btn.Parent := Form;
    Btn.Width := ScaleX(75);
    Btn.Height := ScaleY(23);
    Btn.Left := Form.ClientWidth - ScaleX(75 + 6 + 75 + 10);
    Btn.Top := Form.ClientHeight - ScaleY(23 + 10);
    Btn.Caption := 'OK';
    Btn.ModalResult := mrOk;
    Btn.Default := True;

    Btn := TNewButton.Create(Form);
    Btn.Parent := Form;
    Btn.Width := ScaleX(75);
    Btn.Height := ScaleY(23);
    Btn.Left := Form.ClientWidth - ScaleX(75 + 10);
    Btn.Top := Form.ClientHeight - ScaleY(23 + 10);
    Btn.Caption := 'Cancel';
    Btn.ModalResult := mrCancel;
    Btn.Cancel := True;

    Form.ActiveControl := Edit;

    if Form.ShowModal() = mrOk then
    begin

      if Edit.Text <> '' then
        Test := Edit.Text
      else
        Test := TEST_FLAG;

      TestUpdateCaption();

    end;

  finally
    Form.Free();
  end;

end;

procedure TestClearButtonClick(Sender: TObject);
begin
  Test := TEST_FLAG;
  TestUpdateCaption();
end;

procedure TestCreateButtons(ParentForm: TSetupForm; CancelButton: TNewButton);
var
  BtnTest: TNewButton;
  BtnClear: TNewButton;

begin

  BtnTest := TNewButton.Create(ParentForm);
  BtnTest.Left := ParentForm.ClientWidth - CancelButton.Left - CancelButton.Width;
  BtnTest.Top := CancelButton.Top;
  BtnTest.Width := CancelButton.Width;
  BtnTest.Height := CancelButton.Height;
  BtnTest.Caption := '&Enter Test';
  BtnTest.OnClick := @TestButtonClick;
  BtnTest.Parent := ParentForm;

  BtnClear := TNewButton.Create(ParentForm);
  BtnClear.Name := 'BtnClear';
  BtnClear.Left := ParentForm.ClientWidth - CancelButton.Left - CancelButton.Width;
  BtnClear.Left := BtnTest.Left + BtnTest.Width + ScaleX(10);
  BtnClear.Top := CancelButton.Top;
  BtnClear.Width := CancelButton.Width;
  BtnClear.Height := CancelButton.Height;
  BtnClear.Caption := '&Clear Test';
  BtnClear.OnClick := @TestClearButtonClick;
  BtnClear.Parent := ParentForm;
  BtnClear.Enabled := False;

end;

function InitializeSetup(): Boolean;
begin

  Completed := False;
  CmdExe := ExpandConstant('{cmd}');
  TmpDir := ExpandConstant('{tmp}');

  ExtractTemporaryFile('setup.php');
  TmpFile.Setup := AddBackslash(TmpDir) + 'setup.php';
  ExtractTemporaryFile('setup.class.php');

  ExtractTemporaryFile('composer');
  TmpFile.Composer := AddBackslash(TmpDir) +'composer';

  TmpFile.Result := AddBackslash(TmpDir) + 'result.txt';

  ResetPhp();
  InitRecordsFromPath();

  if CheckAlreadyInstalled() then
    Exit;

  if Pos('/TEST', GetCmdTail) <> 0 then
    Test := TEST_FLAG;

  Result := True;

end;

procedure DeinitializeSetup();
begin

  if not Completed then
  begin
    Debug('Setup cancelled or aborted');
    PathsRemove();
  end;

end;

procedure InitializeWizard;
begin

  WizardForm.DirEdit.Text := GetAppDir();

  ProgressPage := CreateOutputProgressPage('', '');
  ProgressPage.ProgressBar.Style := npbstMarquee;

  SettingsPage := CreateInputFilePage(wpWelcome,
    'Settings Check',
    'We need to check your PHP and path settings.',
    'Select where php.exe is located, then click Next.');

  if Test = '' then
    SettingsPage.Add('', 'php.exe|php.exe', '.exe')
  else
    SettingsPage.Add('', 'All files|*.*', '');

  ErrorPage := CreateMessagePage(SettingsPage.ID,
    '', '', 'Please review and fix the issues listed below, then click Back and try again');

  DownloadInfoPage := CreateMessagePage(wpReady, '', '', '');

  FinishedInfoPage := CreateOutputMsgPage(wpInstalling,
  'Information',
  'Please read the following important information before continuing.',
  'Setup has changed your path variable, but not all running programs will be aware of this. ' +
  'To use Composer for the first time, you may have to open a NEW command window.');

  if Test = TEST_FLAG then
    TestCreateButtons(WizardForm, WizardForm.CancelButton);

end;

procedure CurPageChanged(CurPageID: Integer);
begin

  if CurPageID = SettingsPage.ID then
  begin

    if FileExists(PhpRec.Exe) then
      SettingsPage.Values[0] := PhpRec.Exe;

    WizardForm.ActiveControl := nil;

  end
  else if CurPageID = ErrorPage.ID then
  begin

    WizardForm.ActiveControl := nil;
    WizardForm.NextButton.Enabled := False;

  end
  else if CurPageID = wpPreparing then
  begin

    // only shown for a major error
    WizardForm.BackButton.Enabled := False;

  end
  else if CurPageID = DownloadInfoPage.ID then
  begin

    WizardForm.ActiveControl := nil;
    WizardForm.NextButton.Enabled := GetRec.Next <> NEXT_NONE;

    if GetRec.Next = NEXT_RETRY then
      WizardForm.NextButton.Caption := 'Retry';

  end;

end;

function ShouldSkipPage(PageID: Integer): Boolean;
begin

  Result := False;

  if PageID = ErrorPage.ID then
    Result := (PhpRec.Error = '') and (PathError = '')
  else if PageID = DownloadInfoPage.ID then
    Result := GetRec.Text = ''
  else if PageId = FinishedInfoPage.ID then
    Result := not Flags.PathChanged;

end;

function NextButtonClick(CurPageID: Integer): Boolean;
begin

  Result := True;

  if CurPageID = SettingsPage.ID then
  begin

    if not FileExists(SettingsPage.Values[0]) then
    begin
      MsgBox('The file you specified does not exist.', mbCriticalError, MB_OK);
      Result := False;
    end
    else
      ShowCheckPage();

  end
  else if CurPageID = wpReady then
    Result := ShowDownloadPage(CurPageID)
  else if CurPageID = DownloadInfoPage.ID then
    Result := ShowDownloadPage(CurPageID);

end;

function BackButtonClick(CurPageID: Integer): Boolean;
begin

  Result := True;

  if CurPageID = DownloadInfoPage.ID then
    ResetGetRec(False);

end;

procedure CancelButtonClick(CurPageID: Integer; var Cancel, Confirm: Boolean);
begin

  // remove cancel confirmation where it is not necessary
  case CurPageID of
    wpWelcome: Confirm := False;
    ErrorPage.ID: Confirm := False;
    DownloadInfoPage.ID: Confirm := False;
   end;

end;

function UpdateReadyMemo(Space, NewLine, MemoUserInfoInfo, MemoDirInfo, MemoTypeInfo,
  MemoComponentsInfo, MemoGroupInfo, MemoTasksInfo: String): String;
var
  S: String;
  Env: String;

begin

  S := MemoDirInfo;

  // this line left over from Start Menu days. Do not remove yet
  if (MemoGroupInfo <> '') and not Flags.Installed then
    S := S + NewLine + NewLine + MemoGroupInfo;

  S := S + NewLine + NewLine + 'PHP version ' + PhpRec.Version;
  S := S + NewLine + Space + PhpRec.Exe;

  Env := ' Path environment variable:';

  if Flags.AddPhp.Path <> '' then
  begin
    S := S + NewLine + NewLine + 'Add to ' + Flags.AddPhp.Name + Env;
    S := S + NewLine + Space + Flags.AddPhp.Path;
  end;

  if Flags.AddComposer.Path <> '' then
  begin
    S := S + NewLine + NewLine + 'Add to ' + Flags.AddComposer.Name + Env;
    S := S + NewLine + Space + Flags.AddComposer.Path;
  end;

  Result := S;

end;

function PrepareToInstall(var NeedsRestart: Boolean): String;
var
  Lines: TArrayOfString;
  S: AnsiString;
  I: Integer;

begin

  Result := '';

  Debug('Running PrepareToInstall tasks');

  PathsAdd(Result);

  if Result <> '' then
    Exit;

  if LoadStringsFromFile(TmpFile.Composer, Lines) then
  begin

    S := '';
    for I := 0 to GetArrayLength(Lines) - 1 do
      S := S + Lines[I] + #10;

    SaveStringToFile(TmpDir + '\composer', S, False);

  end;

end;

procedure CurStepChanged(CurStep: TSetupStep);
begin

  if CurStep = ssPostInstall then
    Completed := True;

end;

procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);
var
  Rec: TPathRec;

begin

  if CurUninstallStep = usUninstall then
  begin

    // we must call this here, or the dll and app dir will not be deleted
    UserDataDelete();

    SetPathRec(Rec, ExpandConstant('{app}\bin'));
    RemoveFromPath(Rec.Hive, Rec.Path);
    NotifyPathChange();

  end;

end;


*** empty log message ***

*** empty log message ***

fatal: unable to access 'https://github.com/axbannaz/RemoteFunctionCall.git/': The requested URL returned error: 403


cleaned up install script

cleaned up install script

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/AugurProject/augur-prototype.git/'


Use wildcard in setup file selection

Use wildcard in setup file selection

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/R3turnz/OpenWeather.git/'


add setup

add setup

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/anotherdeveloper/CCT.git/'


default install location 64bit

default install location 64bit

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/kakysha/byteball.git/'


fixed vcredist parameters

fixed vcredist parameters

git-svn-id: 3e9aee9f2516f2549516d29f9787a8c058f60743@435 83beac0b-88d4-de11-98f5-001125a849b0

fatal: unable to access 'https://github.com/kholia/Loki.git/': The requested URL returned error: 403


first cut at inno setup (not working yet)

fatal: unable to access 'https://github.com/braams/shtoom.git/': The requested URL returned error: 403


added 32-bit packaging script

added 32-bit packaging script

; -- Example1.iss --
; Demonstrates copying 3 files and creating an icon.

; SEE THE DOCUMENTATION FOR DETAILS ON CREATING .ISS SCRIPT FILES!

#define MyAppName "CMN_binViewer"

[Setup]
AppName=CMN_binViewer
AppVersion=2.57
AppPublisher=Croatian Meteor Network
AppPublisherURL=http://cmn.rgn.hr/
DefaultDirName={pf}\CMN_binViewer
DefaultGroupName=CMN_binViewer
UninstallDisplayIcon={app}\CMN_binViewer.exe
Compression=lzma2
SolidCompression=yes
OutputDir="."

[Files]
Source: ".\build\exe.win32-3.8\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs

[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}";

[Icons]
Name: "{group}\CMN_binViewer"; Filename: "{app}\CMN_binViewer.exe"
Name: "{group}\{cm:UninstallProgram,{#MyAppName}}"; Filename: "{uninstallexe}"
Name: "{commondesktop}\{#MyAppName}"; Filename: "{app}\CMN_binViewer.exe"; Tasks: desktopicon; WorkingDir: {app}

error: pathspec 'CMN_binViewer_setup_win32.iss' did not match any file(s) known to git


Don't show quick launch checkbox for newer versions of windows.

remote: Support for password authentication was removed on August 13, 2021.
remote: Please see https://docs.github.com/en/get-started/getting-started-with-git/about-remote-repositories#cloning-with-https-urls for information on currently recommended modes of authentication.
fatal: Authentication failed for 'https://github.com/Xangis/ProximaController.git/'


Upload New File

Upload New File

fatal: remote error: upload-pack: not our ref e37d1a272926033c5d3ff7ff98affc5c5a1a4bdb
fatal: the remote end hung up unexpectedly


chore: add ISS script for GitHub Actions

#define Version GetEnv("BUILD_VERSION")

[Setup]
; NOTE: The value of AppId uniquely identifies this application.
; Do not use the same AppId value in installers for other applications.
; (To generate a new GUID, click Tools | Generate GUID inside the IDE.)
AppId={{44576435-E098-4581-A6D7-FD7FADC5B063}
AppName=Notes
AppVersion={#Version}
AppPublisher=Awesomeness
AppPublisherURL=https://www.get-notes.com/
AppSupportURL=https://www.get-notes.com/
AppUpdatesURL=https://www.get-notes.com/
UninstallDisplayIcon={app}\Notes.exe
DefaultDirName={autopf}\Notes
DefaultGroupName=Notes
UninstallDisplayName=Notes
OutputBaseFilename=NotesSetup_{#Version}
Compression=lzma
SolidCompression=yes
ArchitecturesInstallIn64BitMode=x64

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[Tasks]
Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked

[Files]
;x-64
Source: "{#SourcePath}\Notes64\Notes.exe"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "{#SourcePath}\Notes64\msvcp140.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "{#SourcePath}\Notes64\msvcp140_1.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "{#SourcePath}\Notes64\vcruntime140.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "{#SourcePath}\Notes64\d3dcompiler_47.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "{#SourcePath}\Notes64\libEGL.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "{#SourcePath}\Notes64\libGLESV2.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "{#SourcePath}\Notes64\opengl32sw.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "{#SourcePath}\Notes64\Qt5Core.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "{#SourcePath}\Notes64\Qt5Gui.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "{#SourcePath}\Notes64\Qt5Network.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "{#SourcePath}\Notes64\Qt5Sql.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "{#SourcePath}\Notes64\Qt5Svg.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "{#SourcePath}\Notes64\Qt5Widgets.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: Is64BitInstallMode
Source: "{#SourcePath}\Notes64\bearer\*"; DestDir: "{app}\bearer"; Flags: ignoreversion recursesubdirs createallsubdirs; Check: Is64BitInstallMode
Source: "{#SourcePath}\Notes64\iconengines\*"; DestDir: "{app}\iconengines"; Flags: ignoreversion recursesubdirs createallsubdirs; Check: Is64BitInstallMode
Source: "{#SourcePath}\Notes64\imageformats\*"; DestDir: "{app}\imageformats"; Flags: ignoreversion recursesubdirs createallsubdirs; Check: Is64BitInstallMode
Source: "{#SourcePath}\Notes64\platforms\*"; DestDir: "{app}\platforms"; Flags: ignoreversion recursesubdirs createallsubdirs; Check: Is64BitInstallMode
Source: "{#SourcePath}\Notes64\sqldrivers\*"; DestDir: "{app}\sqldrivers"; Flags: ignoreversion recursesubdirs createallsubdirs; Check: Is64BitInstallMode
Source: "{#SourcePath}\Notes64\styles\*"; DestDir: "{app}\translations"; Flags: ignoreversion recursesubdirs createallsubdirs; Check: Is64BitInstallMode
Source: "{#SourcePath}\Notes64\translations\*"; DestDir: "{app}\translations"; Flags: ignoreversion recursesubdirs createallsubdirs; Check: Is64BitInstallMode
;x-86
Source: "{#SourcePath}\Notes32\Notes.exe"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
Source: "{#SourcePath}\Notes32\msvcp140.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
Source: "{#SourcePath}\Notes32\msvcp140_1.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
Source: "{#SourcePath}\Notes32\vcruntime140.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
Source: "{#SourcePath}\Notes32\d3dcompiler_47.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
Source: "{#SourcePath}\Notes32\libEGL.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
Source: "{#SourcePath}\Notes32\libGLESV2.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
Source: "{#SourcePath}\Notes32\opengl32sw.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
Source: "{#SourcePath}\Notes32\Qt5Core.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
Source: "{#SourcePath}\Notes32\Qt5Gui.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
Source: "{#SourcePath}\Notes32\Qt5Network.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
Source: "{#SourcePath}\Notes32\Qt5Sql.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
Source: "{#SourcePath}\Notes32\Qt5Svg.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
Source: "{#SourcePath}\Notes32\Qt5Widgets.dll"; DestDir: "{app}"; Flags: ignoreversion; Check: not Is64BitInstallMode
Source: "{#SourcePath}\Notes32\bearer\*"; DestDir: "{app}\bearer"; Flags: ignoreversion recursesubdirs createallsubdirs; Check: not Is64BitInstallMode
Source: "{#SourcePath}\Notes32\iconengines\*"; DestDir: "{app}\iconengines"; Flags: ignoreversion recursesubdirs createallsubdirs; Check: not Is64BitInstallMode
Source: "{#SourcePath}\Notes32\imageformats\*"; DestDir: "{app}\imageformats"; Flags: ignoreversion recursesubdirs createallsubdirs; Check: not Is64BitInstallMode
Source: "{#SourcePath}\Notes32\platforms\*"; DestDir: "{app}\platforms"; Flags: ignoreversion recursesubdirs createallsubdirs; Check: not Is64BitInstallMode
Source: "{#SourcePath}\Notes32\sqldrivers\*"; DestDir: "{app}\sqldrivers"; Flags: ignoreversion recursesubdirs createallsubdirs; Check: not Is64BitInstallMode
Source: "{#SourcePath}\Notes32\styles\*"; DestDir: "{app}\translations"; Flags: ignoreversion recursesubdirs createallsubdirs; Check: not Is64BitInstallMode
Source: "{#SourcePath}\Notes32\translations\*"; DestDir: "{app}\translations"; Flags: ignoreversion recursesubdirs createallsubdirs; Check: not Is64BitInstallMode

[Icons]
Name: "{group}\Notes"; Filename: "{app}\Notes.exe"
Name: "{group}\{cm:UninstallProgram,Notes}"; Filename: "{uninstallexe}"
Name: "{commondesktop}\Notes"; Filename: "{app}\Notes.exe"; Tasks: desktopicon

[Run]
Filename: "{app}\Notes.exe"; Description: "{cm:LaunchProgram,Notes}"; Flags: nowait postinstall skipifsilent

error: pathspec 'packaging/windows/Notes_Inno_Script_Github.iss' did not match any file(s) known to git


Задача #19: Сделать понятное имя файла-установщика

#include <ISPPBuiltins.iss>
#define AppName "OneScript execution engine"
#define FSFriendlyName "OneScript execution engine"
#define MainExe "TestApp.exe"

#define VerMajor
#define VerMinor
#define VerRelease
#define Build
#expr ParseVersion("built\ScriptEngine.dll",VerMajor,VerMinor,VerRelease,Build)

[Setup]
AppName={#AppName}
AppVersion={#VerMajor}.{#VerMinor}.{#VerRelease}
DefaultDirName="{pf}\{#FSFriendlyName}"
DefaultGroupName="{#FSFriendlyName}"
OutputBaseFilename="OneScript-{#VerMajor}.{#VerMinor}.{#VerRelease}-setup"
DisableProgramGroupPage=yes
UninstallDisplayIcon="{app}\{#MainExe}"
Compression=lzma2
SolidCompression=yes

[Files]
Source: "built\*"; DestDir: "{app}"
Source: "dotNetFx40_Full_setup.exe"; DestDir: {tmp}; Flags: deleteafterinstall; Check: not IsRequiredDotNetDetected

[Icons]
Name: "{group}\{#FSFriendlyName}"; Filename: "{app}\{#MainExe}"

[Run]
Filename: {tmp}\dotNetFx40_Full_setup.exe; Parameters: "/q:a /c:""install /l /q"""; Check: not IsRequiredDotNetDetected; StatusMsg: Microsoft .NET Framework 4.0 is being installed. Please wait..
Filename: "{app}\{#MainExe}"; Description: "Launch application"; Flags: postinstall nowait skipifsilent unchecked

[Code]

function IsDotNetDetected(version: string; service: cardinal): boolean;
// Indicates whether the specified version and service pack of the .NET Framework is installed.
//
// version -- Specify one of these strings for the required .NET Framework version:
//    'v1.1.4322'     .NET Framework 1.1
//    'v2.0.50727'    .NET Framework 2.0
//    'v3.0'          .NET Framework 3.0
//    'v3.5'          .NET Framework 3.5
//    'v4\Client'     .NET Framework 4.0 Client Profile
//    'v4\Full'       .NET Framework 4.0 Full Installation
//    'v4.5'          .NET Framework 4.5
//
// service -- Specify any non-negative integer for the required service pack level:
//    0               No service packs required
//    1, 2, etc.      Service pack 1, 2, etc. required
var
    key: string;
    install, release, serviceCount: cardinal;
    check45, success: boolean;
//var reqNetVer : string;
begin
    // .NET 4.5 installs as update to .NET 4.0 Full
    if version = 'v4.5' then begin
        version := 'v4\Full';
        check45 := true;
    end else
        check45 := false;

    // installation key group for all .NET versions
    key := 'SOFTWARE\Microsoft\NET Framework Setup\NDP\' + version;

    // .NET 3.0 uses value InstallSuccess in subkey Setup
    if Pos('v3.0', version) = 1 then begin
        success := RegQueryDWordValue(HKLM, key + '\Setup', 'InstallSuccess', install);
    end else begin
        success := RegQueryDWordValue(HKLM, key, 'Install', install);
    end;

    // .NET 4.0/4.5 uses value Servicing instead of SP
    if Pos('v4', version) = 1 then begin
        success := success and RegQueryDWordValue(HKLM, key, 'Servicing', serviceCount);
    end else begin
        success := success and RegQueryDWordValue(HKLM, key, 'SP', serviceCount);
    end;

    // .NET 4.5 uses additional value Release
    if check45 then begin
        success := success and RegQueryDWordValue(HKLM, key, 'Release', release);
        success := success and (release >= 378389);
    end;

    result := success and (install = 1) and (serviceCount >= service);
end;

function IsRequiredDotNetDetected(): Boolean;
begin
    result := IsDotNetDetected('v4\Full', 0);
end;

function InitializeSetup(): Boolean;
begin
    if not IsDotNetDetected('v4\Full', 0) then begin
        MsgBox('{#AppName} requires Microsoft .NET Framework 4.0 Client Profile.'#13#13
          'The installer will attempt to install it', mbInformation, MB_OK);
    end;

    result := true;
end;

#include <ISPPBuiltins.iss>
#define AppName "1Script execution engine"
#define FSFriendlyName "1Script execution engine"
#define MainExe "TestApp.exe"

#define VerMajor
#define VerMinor
#define VerRelease
#define Build
#expr ParseVersion("built\ScriptEngine.dll",VerMajor,VerMinor,VerRelease,Build)

[Setup]
AppName={#AppName}
AppVersion={#VerMajor}.{#VerMinor}.{#VerRelease}
DefaultDirName="{pf}\{#FSFriendlyName}"
DefaultGroupName="{#FSFriendlyName}"
DisableProgramGroupPage=yes
UninstallDisplayIcon="{app}\{#MainExe}"
Compression=lzma2
SolidCompression=yes

[Files]
Source: "built\*"; DestDir: "{app}"
Source: "dotNetFx40_Full_setup.exe"; DestDir: {tmp}; Flags: deleteafterinstall; Check: not IsRequiredDotNetDetected

[Icons]
Name: "{group}\{#FSFriendlyName}"; Filename: "{app}\{#MainExe}"

[Run]
Filename: {tmp}\dotNetFx40_Full_setup.exe; Parameters: "/q:a /c:""install /l /q"""; Check: not IsRequiredDotNetDetected; StatusMsg: Microsoft .NET Framework 4.0 is being installed. Please wait..
Filename: "{app}\{#MainExe}"; Description: "Launch application"; Flags: postinstall nowait skipifsilent unchecked

[Code]

function IsDotNetDetected(version: string; service: cardinal): boolean;
// Indicates whether the specified version and service pack of the .NET Framework is installed.
//
// version -- Specify one of these strings for the required .NET Framework version:
//    'v1.1.4322'     .NET Framework 1.1
//    'v2.0.50727'    .NET Framework 2.0
//    'v3.0'          .NET Framework 3.0
//    'v3.5'          .NET Framework 3.5
//    'v4\Client'     .NET Framework 4.0 Client Profile
//    'v4\Full'       .NET Framework 4.0 Full Installation
//    'v4.5'          .NET Framework 4.5
//
// service -- Specify any non-negative integer for the required service pack level:
//    0               No service packs required
//    1, 2, etc.      Service pack 1, 2, etc. required
var
    key: string;
    install, release, serviceCount: cardinal;
    check45, success: boolean;
//var reqNetVer : string;
begin
    // .NET 4.5 installs as update to .NET 4.0 Full
    if version = 'v4.5' then begin
        version := 'v4\Full';
        check45 := true;
    end else
        check45 := false;

    // installation key group for all .NET versions
    key := 'SOFTWARE\Microsoft\NET Framework Setup\NDP\' + version;

    // .NET 3.0 uses value InstallSuccess in subkey Setup
    if Pos('v3.0', version) = 1 then begin
        success := RegQueryDWordValue(HKLM, key + '\Setup', 'InstallSuccess', install);
    end else begin
        success := RegQueryDWordValue(HKLM, key, 'Install', install);
    end;

    // .NET 4.0/4.5 uses value Servicing instead of SP
    if Pos('v4', version) = 1 then begin
        success := success and RegQueryDWordValue(HKLM, key, 'Servicing', serviceCount);
    end else begin
        success := success and RegQueryDWordValue(HKLM, key, 'SP', serviceCount);
    end;

    // .NET 4.5 uses additional value Release
    if check45 then begin
        success := success and RegQueryDWordValue(HKLM, key, 'Release', release);
        success := success and (release >= 378389);
    end;

    result := success and (install = 1) and (serviceCount >= service);
end;

function IsRequiredDotNetDetected(): Boolean;
begin
    result := IsDotNetDetected('v4\Full', 0);
end;

function InitializeSetup(): Boolean;
begin
    if not IsDotNetDetected('v4\Full', 0) then begin
        MsgBox('{#AppName} requires Microsoft .NET Framework 4.0 Client Profile.'#13#13
          'The installer will attempt to install it', mbInformation, MB_OK);
    end;

    result := true;
end;


Try to fix win icon issue

Try to fix win icon issue

;This file will be executed next to the application bundle image
;I.e. current directory will contain folder Bisq with application files
[Setup]
AppId={{bisq}}
AppName=Bisq
AppVersion=0.5.1
AppVerName=Bisq
AppPublisher=Bisq
AppComments=Bisq
AppCopyright=Copyright (C) 2016
AppPublisherURL=https://bisq.io
AppSupportURL=https://bisq.io
;AppUpdatesURL=http://java.com/
DefaultDirName={localappdata}\Bisq
DisableStartupPrompt=Yes
DisableDirPage=Yes
DisableProgramGroupPage=Yes
DisableReadyPage=Yes
DisableFinishedPage=Yes
DisableWelcomePage=Yes
DefaultGroupName=Bisq
;Optional License
LicenseFile=
;WinXP or above
MinVersion=0,5.1
OutputBaseFilename=Bisq
Compression=lzma
SolidCompression=yes
PrivilegesRequired=lowest
SetupIconFile=Bisq.ico
UninstallDisplayIcon=Bisq.ico
UninstallDisplayName=Bisq
WizardImageStretch=No
WizardSmallImageFile=Bisq-setup-icon.bmp
ArchitecturesInstallIn64BitMode=x64
ChangesAssociations=Yes

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[Files]
Source: "Bisq\Bisq.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "Bisq\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs

[Icons]
Name: "{group}\Bisq"; Filename: "{app}\Bisq.exe"; IconFilename: "{app}\Bisq.ico"; Check: returnTrue()
Name: "{commondesktop}\Bisq"; Filename: "{app}\Bisq.exe";  IconFilename: "{app}\Bisq.ico"; Check: returnFalse()

[Run]
Filename: "{app}\Bisq.exe"; Description: "{cm:LaunchProgram,Bisq}"; Flags: nowait postinstall skipifsilent; Check: returnTrue()
Filename: "{app}\Bisq.exe"; Parameters: "-install -svcName ""Bisq"" -svcDesc ""Bisq"" -mainExe ""Bisq.exe""  "; Check: returnFalse()

[UninstallRun]
Filename: "{app}\Bisq.exe "; Parameters: "-uninstall -svcName Bisq -stopOnUninstall"; Check: returnFalse()

[Code]
function returnTrue(): Boolean;
begin
  Result := True;
end;

function returnFalse(): Boolean;
begin
  Result := False;
end;

function InitializeSetup(): Boolean;
begin
// Possible future improvements:
//   if version less or same => just launch app
//   if upgrade => check if same app is running and wait for it to exit
//   Add pack200/unpack200 support?
  Result := True;
end;

;This file will be executed next to the application bundle image
;I.e. current directory will contain folder Bisq with application files
[Setup]
AppId={{bisq}}
AppName=Bisq
AppVersion=0.5.1
AppVerName=Bisq
AppPublisher=Bisq
AppComments=Bisq
AppCopyright=Copyright (C) 2016
AppPublisherURL=https://bisq.io
AppSupportURL=https://bisq.io
;AppUpdatesURL=http://java.com/
DefaultDirName={localappdata}\Bisq
DisableStartupPrompt=Yes
DisableDirPage=Yes
DisableProgramGroupPage=Yes
DisableReadyPage=Yes
DisableFinishedPage=Yes
DisableWelcomePage=Yes
DefaultGroupName=Bisq
;Optional License
LicenseFile=
;WinXP or above
MinVersion=0,5.1
OutputBaseFilename=Bisq
Compression=lzma
SolidCompression=yes
PrivilegesRequired=lowest
SetupIconFile={app}\Bisq.ico
UninstallDisplayIcon={app}\Bisq.ico
UninstallDisplayName=Bisq
WizardImageStretch=No
WizardSmallImageFile={app}\Bisq-setup-icon.bmp
ArchitecturesInstallIn64BitMode=x64
ChangesAssociations=Yes

[Languages]
Name: "english"; MessagesFile: "compiler:Default.isl"

[Files]
Source: "Bisq\Bisq.exe"; DestDir: "{app}"; Flags: ignoreversion
Source: "Bisq\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs

[Icons]
Name: "{group}\Bisq"; Filename: "{app}\Bisq.exe"; IconFilename: "{app}\Bisq.ico"; Check: returnTrue()
Name: "{commondesktop}\Bisq"; Filename: "{app}\Bisq.exe";  IconFilename: "{app}\Bisq.ico"; Check: returnFalse()

[Run]
Filename: "{app}\Bisq.exe"; Description: "{cm:LaunchProgram,Bisq}"; Flags: nowait postinstall skipifsilent; Check: returnTrue()
Filename: "{app}\Bisq.exe"; Parameters: "-install -svcName ""Bisq"" -svcDesc ""Bisq"" -mainExe ""Bisq.exe""  "; Check: returnFalse()

[UninstallRun]
Filename: "{app}\Bisq.exe "; Parameters: "-uninstall -svcName Bisq -stopOnUninstall"; Check: returnFalse()

[Code]
function returnTrue(): Boolean;
begin
  Result := True;
end;

function returnFalse(): Boolean;
begin
  Result := False;
end;

function InitializeSetup(): Boolean;
begin
// Possible future improvements:
//   if version less or same => just launch app
//   if upgrade => check if same app is running and wait for it to exit
//   Add pack200/unpack200 support?
  Result := True;
end;