#ifndef Payload #error Payload is required #endif #ifndef Output #error Output is required #endif [Setup] AppId={{65B3E62C-937C-4FAA-B1F0-2F1C3D2B9955} AppName=Monitor AppVersion=1.003.1 DefaultDirName={autopf}\Monitor DefaultGroupName=Monitor OutputDir={#Output} OutputBaseFilename=Monitor-Setup-x64 ArchitecturesAllowed=x64compatible ArchitecturesInstallIn64BitMode=x64compatible MinVersion=10.0 PrivilegesRequired=admin Compression=zip/7 SolidCompression=no WizardStyle=modern UninstallDisplayIcon={app}\Monitor.exe CloseApplications=no RestartApplications=no [Files] Source: "{#Payload}\*"; DestDir: "{app}"; Flags: ignoreversion recursesubdirs createallsubdirs [Dirs] Name: "{commonappdata}\Monitor" [Icons] Name: "{autodesktop}\Monitor"; Filename: "{app}\Monitor.exe"; WorkingDir: "{app}" Name: "{group}\Monitor"; Filename: "{app}\Monitor.exe"; WorkingDir: "{app}" [Run] Filename: "{app}\Monitor.exe"; Description: "Start Monitor"; Flags: nowait postinstall skipifsilent runasoriginaluser [Code] var OwnerPage: TInputQueryWizardPage; procedure InitializeWizard(); begin OwnerPage := CreateInputQueryPage(wpSelectDir, 'Monitor Windows account', 'Choose the Windows account that will run Monitor', 'Only this account, Administrators and SYSTEM can access the database and keys. Use DOMAIN\username or COMPUTER\username.'); OwnerPage.Add('Runtime account:', False); OwnerPage.Values[0] := GetPreviousData('MonitorOwner', GetEnv('USERDOMAIN') + '\' + GetUserNameString); end; procedure RegisterPreviousData(PreviousDataKey: Integer); begin SetPreviousData(PreviousDataKey, 'MonitorOwner', OwnerPage.Values[0]); end; procedure ConfigureDataAcl(); var Code: Integer; UserName: String; begin UserName := Trim(OwnerPage.Values[0]); if UserName = '' then RaiseException('A runtime Windows account is required'); if Pos('"', UserName) > 0 then RaiseException('Unsupported Windows user name'); if not Exec(ExpandConstant('{sys}\icacls.exe'), '"' + ExpandConstant('{commonappdata}\Monitor') + '" /inheritance:r /grant:r "' + UserName + ':(OI)(CI)M" "*S-1-5-18:(OI)(CI)F" "*S-1-5-32-544:(OI)(CI)F"', '', SW_HIDE, ewWaitUntilTerminated, Code) or (Code <> 0) then RaiseException('Unable to restrict Monitor data directory permissions'); end; procedure AddFirewallRule(Arguments: String); var Code: Integer; begin if not Exec(ExpandConstant('{sys}\netsh.exe'), Arguments, '', SW_HIDE, ewWaitUntilTerminated, Code) or (Code <> 0) then RaiseException('Unable to configure Monitor firewall rules. Check Windows Firewall and the installation log.'); end; procedure Firewall(AddRules: Boolean); var Code: Integer; ExePath: String; begin Exec(ExpandConstant('{sys}\netsh.exe'),'advfirewall firewall delete rule name="Monitor Web SIP"','',SW_HIDE,ewWaitUntilTerminated,Code); Exec(ExpandConstant('{sys}\netsh.exe'),'advfirewall firewall delete rule name="Monitor ZLM TCP"','',SW_HIDE,ewWaitUntilTerminated,Code); Exec(ExpandConstant('{sys}\netsh.exe'),'advfirewall firewall delete rule name="Monitor ZLM UDP"','',SW_HIDE,ewWaitUntilTerminated,Code); if not AddRules then Exit; ExePath := ExpandConstant('{app}\Monitor.exe'); AddFirewallRule( 'advfirewall firewall add rule name="Monitor Web SIP" dir=in action=allow program="' + ExePath + '" protocol=TCP localport=10001,15060 profile=private,domain remoteip=localsubnet'); AddFirewallRule( 'advfirewall firewall add rule name="Monitor Web SIP" dir=in action=allow program="' + ExePath + '" protocol=UDP localport=15060 profile=private,domain remoteip=localsubnet'); ExePath := ExpandConstant('{app}\_internal\zlm\monitor_zlm.exe'); AddFirewallRule( 'advfirewall firewall add rule name="Monitor ZLM TCP" dir=in action=allow program="' + ExePath + '" protocol=TCP localport=10002,10554,10935,20002-40000 profile=private,domain remoteip=localsubnet'); AddFirewallRule( 'advfirewall firewall add rule name="Monitor ZLM UDP" dir=in action=allow program="' + ExePath + '" protocol=UDP localport=20002-40000 profile=private,domain remoteip=localsubnet'); end; function BackupRuntimeData(): String; var Root, Backup, Base: String; Files: TArrayOfString; I, Counter: Integer; begin Result := ''; Root := ExpandConstant('{commonappdata}\Monitor'); if not DirExists(Root) then Exit; if not FileExists(Root + '\monitor.sqlite3') then Exit; Base := Root + '\backups\installer-' + GetDateTimeString('yyyymmdd-hhnnss', '-', ':'); Backup := Base; Counter := 0; while DirExists(Backup) do begin Counter := Counter + 1; Backup := Base + '-' + IntToStr(Counter); end; if not ForceDirectories(Backup) then begin Result := 'Cannot create the pre-upgrade data backup directory.'; Exit; end; SetArrayLength(Files, 11); Files[0] := 'monitor.sqlite3'; Files[1] := 'monitor.sqlite3-wal'; Files[2] := 'monitor.sqlite3-shm'; Files[3] := 'config.json'; Files[4] := 'settings.json'; Files[5] := 'network.json'; Files[6] := '.runtime-secrets.json'; Files[7] := '.trusted-models.json'; Files[8] := 'license.json'; Files[9] := 'license-clock.json'; Files[10] := 'zlm-template.ini'; for I := 0 to GetArrayLength(Files)-1 do if FileExists(Root + '\' + Files[I]) then if not CopyFile(Root + '\' + Files[I], Backup + '\' + Files[I], True) then begin Result := 'Pre-upgrade backup failed for ' + Files[I] + '. Installation stopped to protect existing data.'; Exit; end; end; function PrepareToInstall(var NeedsRestart: Boolean): String; var Code: Integer; begin Result := ''; if FileExists(ExpandConstant('{app}\Monitor.exe')) then begin if not Exec(ExpandConstant('{app}\Monitor.exe'),'--stop','',SW_HIDE,ewWaitUntilTerminated,Code) or (Code <> 0) then begin Result := 'Please exit Monitor before upgrading.'; Exit; end; end; Result := BackupRuntimeData(); end; procedure CurStepChanged(CurStep: TSetupStep); begin if CurStep = ssPostInstall then begin ConfigureDataAcl(); Firewall(True); end; end; function InitializeUninstall(): Boolean; var Code: Integer; begin Result := Exec(ExpandConstant('{app}\Monitor.exe'),'--stop','',SW_HIDE,ewWaitUntilTerminated,Code) and (Code = 0); end; procedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep); begin if CurUninstallStep = usUninstall then Firewall(False); end;