NSIS file downloader - download

I need NSIS script witch download file from the internet and execute it.
I've read many examples but I still don't understand how to do it. For example
NSISdl::download http://www.domain.com/file localfile.exe
Pop $R0 ;Get the return value
StrCmp $R0 "success" +3
MessageBox MB_OK "Download Failed: $R0"
Quit
$R0 contains information about installation process ("cancel" or "success"). But I don't understand what the "localfile.exe" is?
In what part of program I need write this code(section or function)?

localfile.exe is the path on the local system where you want to save the content you are downloading:
!include LogicLib.nsh
Section
NSISdl::download "http://cdn.sstatic.net/stackoverflow/img/sprites.png" "$pluginsdir\image.png"
Pop $0
${If} $0 == "success"
ExecShell "" '"$pluginsdir\image.png"' ;Open image in default application
${Else}
MessageBox mb_iconstop "Error: $0" ;Show cancel/error message
${EndIf}
SectionEnd

Related

Is there any alternative of InvokeShellVerb in NSIS Script?

I want to pin shortcut to taskbar in windows 10. Script works fine but my utily is detected as malware trojan. Microsoft has removed the verb and this could be the reason for the same. So is there any alternative way to pin/unpin any program programatically.
OutFile "C:\PinUnpinExe\PinUnpinShortcut.exe"
!include 'StdUtils.nsh'
!include FileFunc.nsh
SilentInstall silent
RequestExecutionLevel user ;no elevation needed
ShowInstDetails hide
var inputParam
Section
${GetParameters} $inputParam
System::Call "kernel32::GetCurrentDirectory(i ${NSIS_MAX_STRLEN}, t .r0)"
${StdUtils.InvokeShellVerb} $0 "$0" "abc.exe" $inputParam
SectionEnd
This line is detected as virus.
${StdUtils.InvokeShellVerb} $0 "$0" "abc.exe" $inputParam
I'm ok with the other language solutions too.
Don't programmatically pin shortcuts, the taskbar is a place for users to put their favorite icons!
To unpin you can do this (NSIS 3+):
!include "LogicLib.nsh"
!include "Win\COM.nsh"
!macro UnpinShortcut lnkpath
Push $0
Push $1
!insertmacro ComHlpr_CreateInProcInstance ${CLSID_StartMenuPin} ${IID_IStartMenuPinnedList} r0 ""
${If} $0 P<> 0
System::Call 'SHELL32::SHCreateItemFromParsingName(ws, p0, g "${IID_IShellItem}", *p0r1)' "${lnkpath}"
${If} $1 P<> 0
${IStartMenuPinnedList::RemoveFromList} $0 '(r1)'
${IUnknown::Release} $1 ""
${EndIf}
${IUnknown::Release} $0 ""
${EndIf}
Pop $1
Pop $0
!macroend
Section Uninstall
!insertmacro UnpinShortcut "$SMPrograms\MyApp.lnk"
SectionEnd

NSIS - Disable "Uninstall" button if no component is selected

I have a NSIS script with two components to uninstall. If the user unchecks both components in the "Choose components" page, the Uninstall button is still active, and the uninstall process can continue without actually uninstalling anything. Is there any easy way to disable the "Uninstall" button if no component is selected?
The code for selecting the uninstaller section is the following:
# Macro for selecting uninstaller sections
!macro SELECT_UNSECTION SECTION_NAME UNSECTION_ID
Push $R0
ReadRegStr $R0 HKLM "${REGKEY}\Components" "${SECTION_NAME}"
StrCmp $R0 1 0 next${UNSECTION_ID}
!insertmacro SelectSection "${UNSECTION_ID}"
GoTo done${UNSECTION_ID}
next${UNSECTION_ID}:
!insertmacro UnselectSection "${UNSECTION_ID}"
done${UNSECTION_ID}:
Pop $R0
!macroend
Thanks in advance!
UninstPage Components un.InitComponents
UninstPage InstFiles
Section "un.Foo" SID_FOO
SectionEnd
Section /o "un.Bar" SID_BAR
SectionEnd
!include LogicLib.nsh
Function un.InitComponents
;!insertmacro SELECT_UNSECTION SECTION_NAME ...
Call un.onSelChange ; Make sure the initial button state is correct
FunctionEnd
Function un.onSelChange
GetDlgItem $1 $hwndParent 1
${If} ${SectionIsSelected} ${SID_FOO}
${OrIf} ${SectionIsSelected} ${SID_BAR}
EnableWindow $1 1
${Else}
EnableWindow $1 0
${EndIf}
FunctionEnd

How can I switch between requiring and not requiring admin permission in NSIS?

I want to create a dual installer for an application, which either installs it as portable or normal version.
For the portable version, I don't want to require admin rights. For the normal version, I need admin rights for adding the application to the start menu and other things.
Is there a way to prompt the user for admin rights when starting the actual installation? Maybe with a plug-in? I'm looking for something like RequestExecutionLevel admin inside a section.
RequestExecutionLevel highest will force members of the administrator group to elevate while normal users can run it with no UAC interaction. This example does not elevate for you because doing that is tricky, UAC is broken in certain scenarios and it would require more code to do it correctly...
RequestExecutionLevel highest
Var InstMode
!include nsDialogs.nsh
!include Sections.nsh
!include LogicLib.nsh
Page Custom InstallModePageInit InstallModePageLeave
Page InstFiles
Section "StartMenu shortcuts" SEC_SM
; CreateShortcut ...
SectionEnd
Section "" SEC_UNINST
; WriteUninstaller & registry
SectionEnd
Function InstallModePageInit
nsDialogs::Create 1018
Pop $0
${NSD_CreateRadioButton} 20u 30u 100% 12u "Normal install"
Pop $1
${NSD_CreateRadioButton} 20u 50u 100% 12u "Portable install"
Pop $2
${If} $InstMode = 0
${NSD_Check} $1
${Else}
${NSD_Check} $2
${EndIf}
nsDialogs::Show
FunctionEnd
Function InstallModePageLeave
${NSD_GetState} $2 $InstMode
${If} $InstMode = 0
!insertmacro SelectSection ${SEC_SM}
!insertmacro SelectSection ${SEC_UNINST}
UserInfo::GetAccountType
Pop $0
${If} $0 != "Admin"
MessageBox mb_iconstop "Administrator privileges required, please restart installer to continue..."
Abort
${EndIf}
${Else}
!insertmacro UnselectSection ${SEC_SM}
!insertmacro UnselectSection ${SEC_UNINST}
${EndIf}
FunctionEnd

How to get around NSIS download error "connecting to host"

So I'm trying to download and install PySVN as part of my program's stack. I'm downloading it rather than including, but the download is failing for unknown reasons. Here's the code:
!define PYSVN_FILE "py25-pysvn-svn161-1.7.0-1177.exe"
!define PYSVN_DOWNLOAD_LOC "http://pysvn.tigris.org/files/documents/1233/45661/py25-pysvn-svn161-1.7.0-1177.exe"
${If} $pythonVersion == "2.5"
NSISdl::download "${PYSVN_DOWNLOAD_LOC}" ${PYSVN_FILE}
${Else}
NSISdl::download "${PYSVN2_DOWNLOAD_LOC}" ${PYSVN_FILE}
${EndIf}
Where ${PYSVN_DOWNLOAD_LOC} = http://pysvn.tigris.org/files/documents/1233/45661/py25-pysvn-svn161-1.7.0-1177.exe is what I'm trying to download.
The file obviously downloads fine from a web browser, but NSIS throws a "connecting to host" error when it tries to connect. I've tried giving it a big timeout value. Is this a proxy issue? How can I get around this without including the file in my installer?
Edit:
Thanks to Anders I've edited my code as follows:
${If} $pythonVersion == "2.5"
inetc::get "${PYSVN_DOWNLOAD_LOC}" "${PYSVN_FILE}" /END
${Else}
inetc::get "${PYSVN2_DOWNLOAD_LOC}" "${PYSVN_FILE}" /END
${EndIf}
Which now gives me a "302 (redirection)" error. The file and download location have not changed.
NSISdl uses plain sockets and will fail if the server does redirect and cookie tricks/tracking, try INetC, it uses the higher level WinInet API
inetc::get /NOCANCEL /RESUME "" "http://file.blah" "$TEMP\Setup.exe"
Pop $0
StrCmp $0 "OK" dlok
MessageBox MB_OK|MB_ICONEXCLAMATION "Error downloading. Click OK to abort installation." /SD IDOK
dlok:
!insertmacro closeAllBrowsers
; install plugin
ExecWait `$TEMP\Setup.exe`

How can I check free space during a NullSoft silent install?

In silent install mode the user is not asked about the installation target with the PageEx directory, and therefore the functions DirVerify and GetInstDirError are never called.
This is also applicable to installs that hardcode the installation target (a bad idea) for the same reason as above: the PageEx directory is never invoked.
Your sample code is ok, but calling ${DriveSpace} on Win9x could fail. I also removed the need to specify the section id's
!define APPNAME "CalcEnoughSpace"
name "${APPNAME}"
outfile "$%temp%\${APPNAME}.exe"
ShowInstDetails show
RequestExecutionLevel user
installdir "$Temp"
AllowRootDirInstall true
!include Sections.nsh
!include LogicLib.nsh
Function .onInit
push $instdir
call VerifyFreeSpace
pop $0
${If} $0 < 1
MessageBox mb_iconstop "Not enough free space!"
${EndIf}
FunctionEnd
page instfiles
Section !a
AddSize 10000
SectionEnd
Section /o b
AddSize 10000
SectionEnd
SectionGroup grp
Section c
AddSize 10000
SectionEnd
SectionGroupEnd
Function VerifyFreeSpace
System::Store s
pop $0 ;path to check
Push 0 ;default to no
System::Call 'kernel32::GetDiskFreeSpaceEx(tr0,*l.r1,*l,*l)i.r2'
${If} $2 < 1
StrCpy $0 $0 3
System::Call 'kernel32::GetDiskFreeSpace(tr0,*i.r1,*i.r2,*i.r3,*i)i.r4'
IntCmpU $4 0 ret
IntOp $1 $1 * $2
System::Int64Op $1 * $3
pop $1
${EndIf}
System::Int64Op $1 / 1024 ;to kb
pop $1
StrCpy $4 0 ;size
StrCpy $2 0 ;section idx
loop:
ClearErrors
SectionGetFlags $2 $3
IfErrors testspace
IntOp $3 $3 & ${SF_SELECTED}
${If} $3 <> 0
SectionGetSize $2 $3
IntOp $4 $4 + $3
${EndIf}
IntOp $2 $2 + 1
goto loop
testspace:
pop $2 ;throw away default return value
System::Int64Op $1 > $4
ret:
System::Store l
FunctionEnd
I only did limited testing, hopefully there are no bugs :)
I wrote a function called CheckFreeSpace in NSIS to do this. Unfortunately it has the following limitations:
To compute the size of all the sections in your installation, you have to modify CheckFreeSpace to add each section, by knowing each variable that each section id was written into. I can't find a way of iterating over all sections that will be installed using NSIS.
Installation drive must be computed because ${DriveSpace} requires a drive letter, not a path to an arbitrary directory. The drive letter string is computed with StrCpy $instdrive $INSTDIR 3. If the $INSTDIR variable is a relative path or does not begin with a string such as C:\, this will fail.
If the installation cannot continue it produces a MessageBox. You can suppress the MessageBox by adding /SD IDOK at the end of the statement, but then the user is not informed of the installation failure: I can't find a way to emit to stdout from NSIS. Perhaps the return code from the installer is enough?
If the disk free space is really low (like 10kb), the installer won't run at all; it doesn't have space to unpack its temporary DLL's into the \tmp directory.
Also, in my implementation below, CheckFreeSpace has a hardcoded value for the space free after installation. Obviously that can be parameterized.
Here it is within a sample installer:
!include FileFunc.nsh
!insertmacro DriveSpace
Name "CheckFreeSpace"
OutFile "C:\CheckFreeSpace.exe"
InstallDir C:\tmp\checkfreespace
Page instfiles
Section "install_section" install_section_id
Call CheckFreeSpace
CreateDirectory $INSTDIR
SetOutPath $INSTDIR
File "C:\installme.bat"
WriteUninstaller "$INSTDIR\Uninstall.exe"
DetailPrint "Installation Successful."
SectionEnd
Section "Uninstall"
RMDIR /r "$INSTDIR"
SectionEnd
Function CheckFreeSpace
var /GLOBAL installsize
var /GLOBAL adjustedinstallsize
var /GLOBAL freespace
var /GLOBAL instdrive
; Verify that we have sufficient space for the install
; SectionGetSize returns the size of each section in kilobyte.
SectionGetSize ${install_section_id} $installsize
; Adjust the required install size by 10mb, as a minimum amount
; of free space left after installation.
IntOp $adjustedinstallsize $installsize + 10240
; Compute the drive that is the installation target; the
; ${DriveSpace} macro will not accept a path, it must be a drive.
StrCpy $instdrive $INSTDIR 3
; Compute drive space free in kilobyte
${DriveSpace} $instdrive "/D=F /S=K" $freespace
DetailPrint "Determined installer needs $adjustedinstallsize kb ($installsize kb) while $freespace kb is free"
IntCmp $adjustedinstallsize $freespace spaceok spaceok
MessageBox MB_OK|MB_ICONSTOP "Insufficient space for installation. Please free space for installation directory $INSTDIR and try again."
DetailPrint "Insufficient space for installation. Installer needs $adjustedinstallsize kb, but freespace is only $freespace kb."
Abort "Insufficient space for installation."
spaceok:
DetailPrint "Installation target space is sufficient"
FunctionEnd
Do you have a sample script for silent install?

Resources