/*

MultiUser.nsh

Installer configuration for multi-user Windows environments

Copyright 2008-2026 Joost Verburg

*/

!ifndef MULTIUSER_INCLUDED
!define MULTIUSER_INCLUDED 20210216
!verbose push 3

;Standard NSIS header files

!ifdef MULTIUSER_MUI
  !include MUI2.nsh
!endif
!include LogicLib.nsh
!include WinVer.nsh
!include FileFunc.nsh

!if ${NSIS_PTR_SIZE} > 4
!define /IfNDef MULTIUSER_MINWIN 0x501
!else
!define /IfNDef MULTIUSER_MINWIN 0x400
!endif

;Variables

Var MultiUser.Privileges
Var MultiUser.InstallMode

;Command line installation mode setting

!ifdef MULTIUSER_INSTALLMODE_COMMANDLINE
  !include StrFunc.nsh
  ${Using:StrFunc} StrStr
  !ifndef MULTIUSER_NOUNINSTALL
    ${Using:StrFunc} UnStrStr
  !endif

  Var MultiUser.Parameters
  Var MultiUser.Result
!endif

;Installation folder stored in registry

!ifdef MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_KEY & MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_VALUENAME
  Var MultiUser.InstDir
!endif

!ifdef MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_KEY & MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_VALUENAME
  Var MultiUser.DefaultKeyValue
!endif

;Windows Vista UAC setting

!if "${MULTIUSER_EXECUTIONLEVEL}" == Admin
  RequestExecutionLevel admin
  !define MULTIUSER_EXECUTIONLEVEL_ALLUSERS
!else if "${MULTIUSER_EXECUTIONLEVEL}" == Power
  RequestExecutionLevel admin
  !define MULTIUSER_EXECUTIONLEVEL_ALLUSERS
!else if "${MULTIUSER_EXECUTIONLEVEL}" == Highest
  RequestExecutionLevel highest
  !define MULTIUSER_EXECUTIONLEVEL_ALLUSERS
!else
  RequestExecutionLevel user
  !ifndef MULTIUSER_EXECUTIONLEVEL
    !warning "MULTIUSER_EXECUTIONLEVEL not set!"
  !endif
!endif

/*

Install modes

*/

!macro MULTIUSER_INSTALLMODE_ALLUSERS UNINSTALLER_PREFIX UNINSTALLER_FUNCPREFIX

  ;Install mode initialization - per-machine

  ${ifnot} ${IsNT}
  ${orif} $MultiUser.Privileges == "Admin"
  ${orif} $MultiUser.Privileges == "Power"

    StrCpy $MultiUser.InstallMode AllUsers
  
    SetShellVarContext all
  
    !if "${UNINSTALLER_PREFIX}" != UN
      ;Set default installation location for installer
      !ifdef MULTIUSER_INSTALLMODE_INSTDIR
        !ifdef MULTIUSER_USE_PROGRAMFILES64
          StrCpy $INSTDIR "$PROGRAMFILES64\${MULTIUSER_INSTALLMODE_INSTDIR}"
        !else
          StrCpy $INSTDIR "$PROGRAMFILES\${MULTIUSER_INSTALLMODE_INSTDIR}"
        !endif
      !endif
    !endif
  
    !ifdef MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_KEY & MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_VALUENAME

      ReadRegStr $MultiUser.InstDir HKLM "${MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_KEY}" "${MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_VALUENAME}"
  
      ${if} $MultiUser.InstDir != ""
        StrCpy $INSTDIR $MultiUser.InstDir
      ${endif}

    !endif

    !ifdef MULTIUSER_INSTALLMODE_${UNINSTALLER_PREFIX}FUNCTION
      Call "${MULTIUSER_INSTALLMODE_${UNINSTALLER_PREFIX}FUNCTION}"
    !endif

  ${endif}

!macroend

!macro MULTIUSER_INSTALLMODE_CURRENTUSER UNINSTALLER_PREFIX UNINSTALLER_FUNCPREFIX

  ;Install mode initialization - per-user

  !if ${MULTIUSER_MINWIN} < 0x500
  ${if} ${IsNT}
  !endif

    StrCpy $MultiUser.InstallMode CurrentUser
    SetShellVarContext current

    !if "${UNINSTALLER_PREFIX}" != UN
      ;Set default installation location for installer
      !ifdef MULTIUSER_INSTALLMODE_INSTDIR
        !if ${MULTIUSER_MINWIN} < 0x490
        ${if} ${AtLeastWin2000}
        !endif
          GetKnownFolderPath $INSTDIR {5CD7AEE2-2219-4A67-B85D-6C9CE15660CB} ; FOLDERID_UserProgramFiles
          StrCmp $INSTDIR "" 0 +2
          StrCpy $INSTDIR "$LocalAppData\Programs" ; Fallback directory
          StrCpy $INSTDIR "$INSTDIR\${MULTIUSER_INSTALLMODE_INSTDIR}"
        !if ${MULTIUSER_MINWIN} < 0x490
        ${else}
          StrCpy $INSTDIR "$PROGRAMFILES\${MULTIUSER_INSTALLMODE_INSTDIR}"
        ${endif}
        !endif
      !endif
    !endif

    !ifdef MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_KEY & MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_VALUENAME
      ReadRegStr $MultiUser.InstDir HKCU "${MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_KEY}" "${MULTIUSER_INSTALLMODE_INSTDIR_REGISTRY_VALUENAME}"
      ${if} $MultiUser.InstDir != ""
        StrCpy $INSTDIR $MultiUser.InstDir
      ${endif}
    !endif

    !ifdef MULTIUSER_INSTALLMODE_${UNINSTALLER_PREFIX}FUNCTION
      Call "${MULTIUSER_INSTALLMODE_${UNINSTALLER_PREFIX}FUNCTION}"
    !endif

  !if ${MULTIUSER_MINWIN} < 0x500
  ${endif}
  !endif

!macroend

Function MultiUser.InstallMode.AllUsers
  !insertmacro MULTIUSER_INSTALLMODE_ALLUSERS "" ""
FunctionEnd

Function MultiUser.InstallMode.CurrentUser
  !insertmacro MULTIUSER_INSTALLMODE_CURRENTUSER "" ""
FunctionEnd

!ifndef MULTIUSER_NOUNINSTALL

Function un.MultiUser.InstallMode.AllUsers
  !insertmacro MULTIUSER_INSTALLMODE_ALLUSERS UN .un
FunctionEnd

Function un.MultiUser.InstallMode.CurrentUser
  !insertmacro MULTIUSER_INSTALLMODE_CURRENTUSER UN .un
FunctionEnd

!endif

/*

Installer/uninstaller initialization

*/

!macro MULTIUSER_INIT_QUIT UNINSTALLER_FUNCPREFIX

  !ifdef MULTIUSER_INIT_${UNINSTALLER_FUNCPREFIX}FUNCTIONQUIT
    Call "${MULTIUSER_INIT_${UNINSTALLER_FUNCPREFIX}FUNCTIONQUIT}"
  !else
    Quit
  !endif

!macroend

!macro MULTIUSER_INIT_TEXTS UNINSTALLER_PREFIX

  !if "${UNINSTALLER_PREFIX}" == ""
    !define /ReDef MULTIUSER_TMPSTR_CAPTION "$(^SetupCaption)"
  !else
    !define /ReDef MULTIUSER_TMPSTR_CAPTION "$(^Name)"
  !endif

  !define /IfNDef MULTIUSER_INIT_TEXT_ADMINREQUIRED "${MULTIUSER_TMPSTR_CAPTION} requires administrator privileges."
  !define /IfNDef MULTIUSER_INIT_TEXT_POWERREQUIRED "${MULTIUSER_TMPSTR_CAPTION} requires at least Power User privileges."
  !define /IfNDef MULTIUSER_INIT_TEXT_ALLUSERSNOTPOSSIBLE "Your user account does not have sufficient privileges to install $(^Name) for all users of this computer."

  !undef MULTIUSER_TMPSTR_CAPTION

!macroend

!macro MULTIUSER_INIT_CHECKS UNINSTALLER_PREFIX UNINSTALLER_FUNCPREFIX

  ;Installer initialization - check privileges and set install mode

  !insertmacro MULTIUSER_INIT_TEXTS "${UNINSTALLER_PREFIX}"

  UserInfo::GetAccountType
  Pop $MultiUser.Privileges

  !if ${MULTIUSER_MINWIN} < 0x500
  ${if} ${IsNT}
  !endif

    ;Check privileges

    !if "${MULTIUSER_EXECUTIONLEVEL}" == Admin

      ${if} $MultiUser.Privileges != "Admin"
        MessageBox MB_OK|MB_ICONSTOP "${MULTIUSER_INIT_TEXT_ADMINREQUIRED}"
        !insertmacro MULTIUSER_INIT_QUIT "${UNINSTALLER_FUNCPREFIX}"
      ${endif}

    !else if "${MULTIUSER_EXECUTIONLEVEL}" == Power

      ${if} $MultiUser.Privileges != "Power"
      ${andif} $MultiUser.Privileges != "Admin"
        ${if} ${AtMostWinXP}
           MessageBox MB_OK|MB_ICONSTOP "${MULTIUSER_INIT_TEXT_POWERREQUIRED}"
        ${else}
           MessageBox MB_OK|MB_ICONSTOP "${MULTIUSER_INIT_TEXT_ADMINREQUIRED}"
        ${endif}        
        !insertmacro MULTIUSER_INIT_QUIT "${UNINSTALLER_FUNCPREFIX}"
      ${endif}

    !endif

    !ifdef MULTIUSER_EXECUTIONLEVEL_ALLUSERS

      ;Default to per-machine installation if possible

      ${if} $MultiUser.Privileges == "Admin"
      ${orif} $MultiUser.Privileges == "Power"
        !ifndef MULTIUSER_INSTALLMODE_DEFAULT_CURRENTUSER
          Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.AllUsers
        !else
          Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.CurrentUser
        !endif

        !ifdef MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_KEY & MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_VALUENAME

          ;Set installation mode to setting from a previous installation

          !ifndef MULTIUSER_INSTALLMODE_DEFAULT_CURRENTUSER
            ReadRegStr $MultiUser.DefaultKeyValue HKLM "${MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_KEY}" "${MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_VALUENAME}"
            ${if} $MultiUser.DefaultKeyValue == ""
              ReadRegStr $MultiUser.DefaultKeyValue HKCU "${MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_KEY}" "${MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_VALUENAME}"
              ${if} $MultiUser.DefaultKeyValue != ""
                Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.CurrentUser
              ${endif}
            ${endif}
          !else
            ReadRegStr $MultiUser.DefaultKeyValue HKCU "${MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_KEY}" "${MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_VALUENAME}"
            ${if} $MultiUser.DefaultKeyValue == ""
              ReadRegStr $MultiUser.DefaultKeyValue HKLM "${MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_KEY}" "${MULTIUSER_INSTALLMODE_DEFAULT_REGISTRY_VALUENAME}"
              ${if} $MultiUser.DefaultKeyValue != ""
                Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.AllUsers
              ${endif}
            ${endif}
          !endif

        !endif

      ${else}
        Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.CurrentUser
      ${endif}

    !else

      Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.CurrentUser

    !endif

    !ifdef MULTIUSER_INSTALLMODE_COMMANDLINE

      ;Check for install mode setting on command line

      ${${UNINSTALLER_FUNCPREFIX}GetParameters} $MultiUser.Parameters

      ${${UNINSTALLER_PREFIX}StrStr} $MultiUser.Result $MultiUser.Parameters "/CurrentUser"

      ${if} $MultiUser.Result != ""
        Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.CurrentUser
      ${endif}

      ${${UNINSTALLER_PREFIX}StrStr} $MultiUser.Result $MultiUser.Parameters "/AllUsers"

      ${if} $MultiUser.Result != ""
        ${if} $MultiUser.Privileges == "Admin"
        ${orif} $MultiUser.Privileges == "Power"
          Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.AllUsers
        ${else}
          MessageBox MB_OK|MB_ICONSTOP "${MULTIUSER_INIT_TEXT_ALLUSERSNOTPOSSIBLE}"
          !insertmacro MULTIUSER_INIT_QUIT "${UNINSTALLER_FUNCPREFIX}"
        ${endif}
      ${endif}

    !endif

  !if ${MULTIUSER_MINWIN} < 0x500
  ${else}

    ;Not running Windows NT, per-user installation not supported
    Call ${UNINSTALLER_FUNCPREFIX}MultiUser.InstallMode.AllUsers

  ${endif}
  !endif

!macroend

!macro MULTIUSER_INIT
  !verbose push 3
  !ifdef __UNINSTALL__
    !insertmacro MULTIUSER_UNINIT
  !else
    !insertmacro MULTIUSER_INIT_CHECKS "" ""
  !endif
  !verbose pop
!macroend

!ifndef MULTIUSER_NOUNINSTALL
!macro MULTIUSER_UNINIT
  !verbose push 3
  !insertmacro MULTIUSER_INIT_CHECKS Un un.
  !verbose pop
!macroend
!endif

/*

Mode selection page

*/

!macro MULTIUSER_PAGE_FUNCTION_CUSTOM TYPE
  !ifmacrodef MUI_PAGE_FUNCTION_CUSTOM
    !insertmacro MUI_PAGE_FUNCTION_CUSTOM "${TYPE}"
  !endif
  !ifdef MULTIUSER_PAGE_CUSTOMFUNCTION_${TYPE}
    Call "${MULTIUSER_PAGE_CUSTOMFUNCTION_${TYPE}}"
    !undef MULTIUSER_PAGE_CUSTOMFUNCTION_${TYPE}
  !endif
!macroend

!macro MULTIUSER_INSTALLMODEPAGE_INTERFACE

  !ifndef MULTIUSER_INSTALLMODEPAGE_INTERFACE
    !define MULTIUSER_INSTALLMODEPAGE_INTERFACE
    Var MultiUser.InstallModePage

    Var MultiUser.InstallModePage.Text

    Var MultiUser.InstallModePage.AllUsers
    Var MultiUser.InstallModePage.CurrentUser

  !endif

!macroend

!macro MULTIUSER_PAGEDECLARATION_INSTALLMODE UNPREFIX UNIQUEID

  !define /ReDef MULTIUSER_${UNPREFIX}INSTALLMODEPAGE "" ; Unlock strings in the language file(s)
  !insertmacro MULTIUSER_INSTALLMODEPAGE_INTERFACE

  !define /IfNDef MULTIUSER_INSTALLMODEPAGE_TEXT_TOP "$(MULTIUSER_INNERTEXT_INSTALLMODE_TOP)"
  !define /IfNDef MULTIUSER_INSTALLMODEPAGE_TEXT_ALLUSERS "$(MULTIUSER_INNERTEXT_INSTALLMODE_ALLUSERS)"
  !define /IfNDef MULTIUSER_INSTALLMODEPAGE_TEXT_CURRENTUSER "$(MULTIUSER_INNERTEXT_INSTALLMODE_CURRENTUSER)"

  PageEx custom

    PageCallbacks MultiUser.InstallModePre_${UNIQUEID} MultiUser.InstallModeLeave_${UNIQUEID}

    !define /IfNDef MULTIUSER_INSTALLMODEPAGE_CAPTION " "
    Caption "${MULTIUSER_INSTALLMODEPAGE_CAPTION}"

  PageExEnd

  !insertmacro MULTIUSER_FUNCTION_INSTALLMODEPAGE MultiUser.InstallModePre_${UNIQUEID} MultiUser.InstallModeLeave_${UNIQUEID}

  !undef MULTIUSER_INSTALLMODEPAGE_TEXT_TOP
  !undef MULTIUSER_INSTALLMODEPAGE_TEXT_ALLUSERS
  !undef MULTIUSER_INSTALLMODEPAGE_TEXT_CURRENTUSER

!macroend

!macro MULTIUSER_PAGE_INSTALLMODE

  !verbose push 3

  !ifndef MULTIUSER_EXECUTIONLEVEL_ALLUSERS
    !error "A mixed-mode installation requires MULTIUSER_EXECUTIONLEVEL to be set to Admin, Power or Highest."
  !endif

  !ifmacrodef MUI_PAGE_INIT
    !insertmacro MUI_PAGE_INIT
  !endif
  !insertmacro MULTIUSER_PAGEDECLARATION_INSTALLMODE "" ${__COUNTER__}

  !verbose pop

!macroend

!macro MULTIUSER_FUNCTION_INSTALLMODEPAGE PRE LEAVE

  !include nsDialogs.nsh

  Function "${PRE}"

    !if ${MULTIUSER_MINWIN} < 0x500
    ${ifnot} ${IsNT}
      Abort
    ${endif}
    !endif

    ${if} $MultiUser.Privileges != "Power"
    ${andif} $MultiUser.Privileges != "Admin"
      Abort
    ${endif}

    !insertmacro MULTIUSER_PAGE_FUNCTION_CUSTOM PRE
    !ifmacrodef MUI_HEADER_TEXT_PAGE
      !insertmacro MUI_HEADER_TEXT_PAGE $(MULTIUSER_TEXT_INSTALLMODE_TITLE) $(MULTIUSER_TEXT_INSTALLMODE_SUBTITLE)
    !endif

    nsDialogs::Create 1018
    Pop $MultiUser.InstallModePage

    ${NSD_CreateLabel} 0 2u 100% 42u "${MULTIUSER_INSTALLMODEPAGE_TEXT_TOP}"
    Pop $MultiUser.InstallModePage.Text

    ${NSD_CreateRadioButton} 15u 50u -15u 10u "${MULTIUSER_INSTALLMODEPAGE_TEXT_ALLUSERS}"
    Pop $MultiUser.InstallModePage.AllUsers

    !ifdef MULTIUSER_INSTALLMODEPAGE_SHOWUSERNAME
    !ifdef NOSYSTEMCALLS
    ReadEnvStr $0 USERNAME
    !else
    System::Call 'ADVAPI32::GetUserName(t""r0,*i${NSIS_MAX_STRLEN})'
    !endif
    StrCmp $0 "" +2
      StrCpy $0 " ($0)"
    ${NSD_CreateRadioButton} 15u 70u -15u 10u "${MULTIUSER_INSTALLMODEPAGE_TEXT_CURRENTUSER}$0"
    !else
    ${NSD_CreateRadioButton} 15u 70u -15u 10u "${MULTIUSER_INSTALLMODEPAGE_TEXT_CURRENTUSER}"
    !endif
    Pop $MultiUser.InstallModePage.CurrentUser

    ${if} $MultiUser.InstallMode == "AllUsers"
      SendMessage $MultiUser.InstallModePage.AllUsers ${BM_SETCHECK} ${BST_CHECKED} 0
    ${else}
      SendMessage $MultiUser.InstallModePage.CurrentUser ${BM_SETCHECK} ${BST_CHECKED} 0
    ${endif}

    !insertmacro MULTIUSER_PAGE_FUNCTION_CUSTOM SHOW
    nsDialogs::Show
    !insertmacro MULTIUSER_PAGE_FUNCTION_CUSTOM DESTROYED

  FunctionEnd

  Function "${LEAVE}"
     SendMessage $MultiUser.InstallModePage.AllUsers ${BM_GETCHECK} 0 0 $0

     ${if} $0 = ${BST_CHECKED}
        Call MultiUser.InstallMode.AllUsers
     ${else}
        Call MultiUser.InstallMode.CurrentUser
     ${endif}

    !insertmacro MULTIUSER_PAGE_FUNCTION_CUSTOM LEAVE
  FunctionEnd

!macroend


!verbose pop
!endif
