# This script does the following:
#
# 1. Installs alloy-windows-amd64.exe, alloy-service-amd64.exe, and logo.ico.
# 2. Creates a Start Menu shortcut.
# 3. Builds an uninstaller.
# 4. Adds uninstall information to the registry for Add/Remove Programs.
# 5. Initializes the registry with appropriate settings.

Unicode true

!include nsDialogs.nsh
!include FileFunc.nsh
!include .\macros.nsis

!define APPNAME     "Alloy"
!define PUBLISHER   "Grafana Labs"
!define HELPURL     "https://grafana.com/docs/alloy/latest"
!define UPDATEURL   "https://github.com/grafana/alloy/releases"
!define ABOUTURL    "https://github.com/grafana/alloy"

# Because we modify the registry and install a service that runs as
# LocalSystem, we require admin permissions.
RequestExecutionLevel admin

Name        "${APPNAME} ${VERSION}" # Shown in title bar for installer/uninstaller
Icon        "logo.ico"
InstallDir  "$PROGRAMFILES64\GrafanaLabs\${APPNAME}"
LicenseData ..\..\LICENSE
OutFile     "${OUT}"

# Everything must be global Vars
Var PassedInParameters
Var Config
Var ConfigFlag
Var Environment
Var DisableReporting
Var DisableReportingFlag
Var DisableProfiling
Var DisableProfilingFlag
Var RuntimePriority
Var RuntimePriorityFlag
Var Stability
Var StabilityFlag
Var User
Var Password
Var AuthFlag

# Pages during the installer.
Page license
Page directory
Page instfiles

# Automatically called when installing.
Function .onInit
  SetShellVarContext all
  !insertmacro VerifyUserIsAdmin
FunctionEnd

Section "install"
  ${GetParameters} $PassedInParameters
  ${GetOptions} $PassedInParameters "/DISABLEPROFILING=" $DisableProfiling
  ${GetOptions} $PassedInParameters "/DISABLEREPORTING=" $DisableReporting
  ${GetOptions} $PassedInParameters "/RUNTIMEPRIORITY=" $RuntimePriority
  ${GetOptions} $PassedInParameters "/STABILITY=" $Stability
  ${GetOptions} $PassedInParameters "/ENVIRONMENT=" $Environment
  ${GetOptions} $PassedInParameters "/CONFIG=" $Config
  ${GetOptions} $PassedInParameters "/USERNAME=" $User
  ${GetOptions} $PassedInParameters "/PASSWORD=" $Password

  # Calls to functions like nsExec::ExecToLog below push the exit code to the
  # stack, and must be popped after calling.

  # Preemptively stop the existing service if it's running.
  nsExec::ExecToLog 'sc stop "Alloy"'
  Pop $0

  # Configure the out path and copy files to it.
  IfFileExists "$INSTDIR" Exists NotExists
  NotExists:
    SetOutPath "$INSTDIR"
    Call SetFolderPermissions
  Exists:
    SetOutPath "$INSTDIR"

  File "..\..\dist\alloy-windows-amd64.exe"
  File "..\..\dist.temp\alloy-service-windows-amd64.exe"
  File "logo.ico"

  # Create an uninstaller at the same pathFunctionEnd
  WriteUninstaller "$INSTDIR\uninstall.exe"

  # Registry information for Add/Remote Programs. It's OK for this to
  # overwriting existing registry entries since we want it to be relevant to
  # the current installed version.
  !define UNINSTALLKEY "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}"
  WriteRegStr   HKLM "${UNINSTALLKEY}" "DisplayName"          '${APPNAME} ${VERSION}'
  WriteRegStr   HKLM "${UNINSTALLKEY}" "DisplayVersion"       '${VERSION}'
  WriteRegStr   HKLM "${UNINSTALLKEY}" "UninstallString"      '"$INSTDIR\uninstall.exe"'
  WriteRegStr   HKLM "${UNINSTALLKEY}" "QuietUninstallString" '"$INSTDIR\uninstall.exe" /S'
  WriteRegStr   HKLM "${UNINSTALLKEY}" "InstallLocation"      '"$INSTDIR"'
  WriteRegStr   HKLM "${UNINSTALLKEY}" "DisplayIcon"          '"$INSTDIR\logo.ico"'
  WriteRegStr   HKLM "${UNINSTALLKEY}" "Publisher"            '${PUBLISHER}'
  WriteRegStr   HKLM "${UNINSTALLKEY}" "HelpLink"             '${HELPURL}'
  WriteRegStr   HKLM "${UNINSTALLKEY}" "URLUpdateInfo"        '${UPDATEURL}'
  WriteRegStr   HKLM "${UNINSTALLKEY}" "URLInfoAbout"         '${ABOUTURL}'
  WriteRegDWORD HKLM "${UNINSTALLKEY}" "NoModify" 1
  WriteRegDWORD HKLM "${UNINSTALLKEY}" "NoRepair" 1

  Call CreateConfig
  Call CreateDataDirectory
  Call InitializeRegistry

  ${If} $User != ""
    StrCpy $AuthFlag "obj= $\"$User$\""
    ${If} $Password != ""
      StrCpy $AuthFlag "$AuthFlag password= $\"$Password$\""
    ${EndIf}  
  ${EndIf}

  # Create the service.
  nsExec::ExecToLog 'sc create "Alloy" start= delayed-auto $AuthFlag binpath= "\"$INSTDIR\alloy-service-windows-amd64.exe\""'
  Pop $0

  # Start the service.
  nsExec::ExecToLog 'sc start "Alloy"'
  Pop $0

  # Auto-restart Alloy on failure. Reset failure counter after 60 seconds without failure
  nsExec::ExecToLog `sc failure "Alloy" reset= 60 actions= restart/5000 reboot= "Alloy has failed. Restarting in 5 seconds"`
  Pop $0
SectionEnd

Function CreateConfig
  IfFileExists "$INSTDIR\config.alloy" Noop CreateNewConfig
  Noop:
    Return
  CreateNewConfig:
    File "config.alloy"

    # Set permissions on the config file
    AccessControl::DisableFileInheritance "$INSTDIR\config.alloy"
    AccessControl::SetFileOwner "$INSTDIR\config.alloy" "Administrators"
    AccessControl::ClearOnFile  "$INSTDIR\config.alloy" "Administrators" "FullAccess"
    AccessControl::SetOnFile    "$INSTDIR\config.alloy" "SYSTEM" "FullAccess"
    AccessControl::GrantOnFile  "$INSTDIR\config.alloy" "Everyone" "ListDirectory"
    AccessControl::GrantOnFile  "$INSTDIR\config.alloy" "Everyone" "ReadAttributes"
    ${If} $User != ""
      AccessControl::SetOnFile    "$INSTDIR\config.alloy" "$User" "FullAccess"
    ${EndIf}

    Return
FunctionEnd

Function CreateDataDirectory
  # IfFileExists checks for directories by adding \*.* to the path; this handles
  # empty directories as well as directories with files.
  IfFileExists "$APPDATA\GrafanaLabs\${APPNAME}\data\*.*" Noop CreateDataDirectory
  Noop:
    Return
  CreateDataDirectory:
    CreateDirectory "$APPDATA\GrafanaLabs\${APPNAME}\data"

    # Set permissions on the data directory
    AccessControl::DisableFileInheritance "$APPDATA\GrafanaLabs\${APPNAME}\data"
    AccessControl::SetFileOwner "$APPDATA\GrafanaLabs\${APPNAME}\data" "Administrators"
    AccessControl::ClearOnFile  "$APPDATA\GrafanaLabs\${APPNAME}\data" "Administrators" "FullAccess"
    AccessControl::SetOnFile    "$APPDATA\GrafanaLabs\${APPNAME}\data" "SYSTEM" "FullAccess"
    AccessControl::GrantOnFile  "$APPDATA\GrafanaLabs\${APPNAME}\data" "Everyone" "ListDirectory"
    AccessControl::GrantOnFile  "$APPDATA\GrafanaLabs\${APPNAME}\data" "Everyone" "ReadAttributes"
    ${If} $User != ""
      AccessControl::SetOnFile    "$APPDATA\GrafanaLabs\${APPNAME}\data" "$User" "FullAccess"
    ${EndIf}

    Return
FunctionEnd


# InitializeRegistry initializes the keys in the registry that the service
# runner uses. If the registry values already exist, they are not overwritten.
Function InitializeRegistry
  !define REGKEY "HKLM\Software\GrafanaLabs\Alloy"

  # Define the default key, which points to the service.
  nsExec::ExecToLog 'Reg.exe query "${REGKEY}" /reg:64 /ve'
  Pop $0
  ${If} $0 == 1
    nsExec::ExecToLog 'Reg.exe add "${REGKEY}" /reg:64 /ve /d  "$INSTDIR\alloy-windows-amd64.exe"'
    Pop $0 # Ignore return result
  ${EndIf}

  ${If} $Config != ""
    StrCpy $ConfigFlag "$Config"
  ${Else}
    StrCpy $ConfigFlag "$INSTDIR\config.alloy"
  ${EndIf}

  ${If} $DisableReporting == "yes"
    StrCpy $DisableReportingFlag "--disable-reporting\0"
  ${Else}
    StrCpy $DisableReportingFlag ""
  ${EndIf}

  ${If} $RuntimePriority != ""
    StrCpy $RuntimePriorityFlag "--windows.priority=$\"$RuntimePriority$\"\0"
  ${Else}
    StrCpy $RuntimePriorityFlag ""
  ${EndIf}

  ${If} $Stability != ""
    StrCpy $StabilityFlag "--stability.level=$\"$Stability$\"\0"
  ${Else}
    StrCpy $StabilityFlag ""
  ${EndIf}

  ${If} $DisableProfiling == "yes"
    StrCpy $DisableProfilingFlag "--server.http.enable-pprof=false\0"
  ${Else}
    StrCpy $DisableProfilingFlag ""
  ${EndIf}

  # Define the arguments key, which holds arguments to pass to the
  # service.
  nsExec::ExecToLog 'Reg.exe query "${REGKEY}" /reg:64 /v Arguments'
  Pop $0
  ${If} $0 == 1
    nsExec::ExecToLog 'Reg.exe add "${REGKEY}" /reg:64 /v Arguments /t REG_MULTI_SZ /d "run"\0"$ConfigFlag"\0"--storage.path=$APPDATA\GrafanaLabs\${APPNAME}\data"\0"$DisableReportingFlag$DisableProfilingFlag$RuntimePriorityFlag$StabilityFlag"'
    Pop $0 # Ignore return result
  ${EndIf}

  nsExec::ExecToLog 'Reg.exe query "${REGKEY}" /reg:64 /v Environment'
  Pop $0
  ${If} $0 == 1
    # Define the environment key, which holds environment variables to pass to the
    # service.
    nsExec::ExecToLog 'Reg.exe add "${REGKEY}" /reg:64 /v Environment /t REG_MULTI_SZ /d "$Environment"'
    Pop $0 # Ignore return result
  ${EndIf}

  Return
FunctionEnd

Function SetFolderPermissions
    # Set permissions on the install directory
    AccessControl::DisableFileInheritance $INSTDIR
    AccessControl::SetFileOwner $INSTDIR "Administrators"
    AccessControl::ClearOnFile  $INSTDIR "Administrators" "FullAccess"
    AccessControl::SetOnFile    $INSTDIR "SYSTEM" "FullAccess"
    AccessControl::GrantOnFile  $INSTDIR "Everyone" "ListDirectory"
    AccessControl::GrantOnFile  $INSTDIR "Everyone" "GenericExecute"
    AccessControl::GrantOnFile  $INSTDIR "Everyone" "GenericRead"
    AccessControl::GrantOnFile  $INSTDIR "Everyone" "ReadAttributes"
    ${If} $User != ""
      AccessControl::SetOnFile    $INSTDIR "$User" "FullAccess"
    ${EndIf}
FunctionEnd

# Automatically called when uninstalling.
Function un.onInit
  SetShellVarContext all

  IfSilent Noop WarnUser
  Noop:
    Return
  WarnUser:
    MessageBox MB_OKCANCEL "Permanently remove ${APPNAME}? This will remove all data for the application." IDOK Continue
      Abort
    Continue:
      !insertmacro VerifyUserIsAdmin
      Return
FunctionEnd

Section "uninstall"
  DetailPrint "Starting uninstaller."

  # Stop and remove service.
  nsExec::ExecToLog 'sc stop "Alloy"'
  Pop $0
  nsExec::ExecToLog 'sc delete "Alloy"'
  Pop $0

  RMDir /r "$SMPROGRAMS\${APPNAME}"  # Start Menu folder.
  RMDir /r "$INSTDIR"                # Install directory.
  RMDir /r "$APPDATA\${APPNAME}"     # Application data.

  # Remove service and uninstaller information from the registry. Note that the
  # service settings are stored in the 64-bit registry (so we use /reg:64),
  # while the uninstaller information is stored in the 32-bit registry.
  nsExec::ExecToLog 'Reg.exe delete "HKLM\SOFTWARE\GrafanaLabs\Alloy"  /reg:64 /f'
  Pop $0
  DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APPNAME}"
SectionEnd
