How do I replace strings in files with output from a script in NSIS? - nsis

I have an installer working with NSIS that I'm updating at the moment. At several points, the installer needs to configure packages by replacing paths or values inside configuration files. Those configuration files have placeholders in them that are replaced by whichever deployment tool I'm using (NSIS for this specific case).
Scripts are mostly PHP scripts written to do some simple tasks that would have been excruciatingly complex in NSIS. For some reason though I keep going back to making my PHP scripts replace the placeholders by themselves instead of doing it in the NSIS script, which just isn't right.
My code looks like:
nsExec::ExecToStack '"$INSTDIR\Php\php.exe" "$INSTDIR\Apache\tools\findport.php"'
pop $1 ; return code
pop $2 ; port number
!insertmacro _ReplaceInFile "Apache\conf\httpd.conf" "APACHE_PORT" "$2"
The _ReplaceInFile macro comes from http://nsis.sourceforge.net/ReplaceInFile and works just fine if I use $INSTDIR instead of $2 in the above example. Showing $2 in a MessageBox shows the port number just fine.
I guess I'm doing something wrong but I can't figure out what it is, and debugging is a pain with NSIS.
Thanks,

I guess the lesson is to always verify paths before blaming the utility functions (Using Process Monitor is a good idea so you can tell if file-system redirection is getting in the way)
I would also like to add that using $instdir to hold anything other than a path is not a good idea since it will strip away invalid path characters behind your back. Use a normal register or a custom variable...

Related

Can I push/pop the value of the SetOverwrite flag in NSIS?

Inside a function or macro, I frequently need specific SetOverwrite behavior. However, I prefer to have these subprograms clean up after themselves (not leaving non-return values on the stack, keeping the OutPath the same, not altering global states, etc.) so that I don't have to copy a stream of settings commands every time I want to do something—in order to keep the code more readable.
Is there a way to accomplish this with SetOverwrite? Can I "detect" it, save it, and restore it somehow?
There's no GetOverwrite function that I can see. The SetOverwrite docs refer to the "overwrite flag," though I can't find concrete references to what exactly that is. Is there a way I can get the value of this (or an arbitrary) flag?
No there is no flag you can interact with. There is a set of flags that can be controlled by plug-ins but the overwrite mode is not one of them.
I think the overwrite mode interacts with the File instruction and therefore has some compile-time restrictions.
The only thing you can do is
${If} $something = "whatever"
SetOverwrite on
File "foo.txt"
${Else}
SetOverwrite ifnewer
File "foo.txt"
${EndIf}
SetOverwrite applies to all other File instructions later in the script so it is a shame there is no way to push/pop its state.
From the documentation:
4.8.2 Compiler Flags
The following commands affect how the compiler generates code and compresses data. Unless otherwise noted, these commands are valid anywhere in the script and affect every line below where each one is placed (until overridden by another command). They cannot be jumped over using flow control instructions.

Can NSIS's $TEMP value be over-ridden?

I have a customer using Host Intrusion Protection and has set every User's temp folder not to allow execution (C:\users\\AppData\Local\Temp). NSIS extracts all plugin dlls and its own dlls into a folder below %TEMP%. The problem is nothing is allow to execute from temp so the entire install fails. What I need to know is how to tell NSIS to use a different folder. The only work around I can find is to edit the TEMP and TMP values under the registry key HCU\Environment from "%USERPROFILE%\AppData\Local\Temp" to something like C:\NSISTEMP. However even though this works changing the registry and then putting it back is not really an option. I also cannot just redirect InitPluginDir as that only effects plug-ins and not the rest of what NSIS extracts (icons xml files etc...). Any ideas?s
You can set %TMP% in a terminal/console window before running the installer, there is no need to edit the registry.
In NSIS v3+ you can use UnsafeStrCpy:
Function .onInit
UnsafeStrCpy $Temp "c:\foo\bar"
CreateDirectory "$Temp"
FunctionEnd
The real problem is the security "solution", why would preventing execution from %Temp% but not from other directories really provide any protection after the bad guys find out about this restriction?
If the installer is started with the special _?=$InstDir parameter then it is not copied to %Temp%.
Try this (look for more info in documentation) maybe it is safer than overriding $Temp folder and so on.

InstallShield Reponse File missing a response

I am trying to automate the install of a few setup files (.exe). I managed to get one working without any issue but am having difficulty with the second.
I created response files by using the following in command prompt:
MyProgram.exe -r
This generated a "setup.exe" file in C:\Windows as I would expect it to. Here is an example of what the file looks like in notepad:
[{PRODUCT_GUID}-DlgOrder]
Dlg0={PRODUCT_GUID}-SdWelcome-0
Count=5
Dlg1={PRODUCT_GUID}-SdLicense-0
Dlg2={PRODUCT_GUID}-SdAskDestPath-0
Dlg3={PRODUCT_GUID}-SdSelectFolder-0
Dlg4={PRODUCT_GUID}-SdStartCopy-0
[{PRODUCT_GUID}-SdWelcome-0]
Result=1
[{PRODUCT_GUID}-SdLicense-0]
Result=1
[{PRODUCT_GUID}-SdAskDestPath-0]
szDir=C:\Example\
Result=1
[{PRODUCT_GUID}-SdSelectFolder-0]
szFolder=Example\Folder
Result=1
[{PRODUCT_GUID}-SdStartCopy-0]
Result=1
I run the install with the setup.iss (response file) using the command:
program.exe /S /f1.\setup.iss
All response files seem to work except for one. The program opens a dialog asking me to select from a pair of radio buttons to select what language manual I want it to install. I want it to default to hit the "Next" button but there's obviously nothing recorded in the .iss file to do so.
What do I have to manually add to the .iss file in order to complete this prompt?
Why doesn't my recording put this in?
Additional information:
If I manually hit "Next" at this step, the program completes install as expected.
The program successfully installs when I install everything manually.
It sounds like this installation includes a custom dialog that doesn't properly handle either MODE SILENTMODE or RECORDMODE. For silent installations to work properly, it needs to call SilentWriteData and SilentReadData when appropriate.
If you are the author of this installation (whether original or inherited), you should handle this case. If you are not the author and are trying to install this installation silently, you should contact the vendor, or (as Glytzhkof suggests) ask on a more relevant site for workarounds.
I think the response file will only contain the actual answers that were input during the original response file creation session. Did this missing dialog show up during the original setup run? Reboot dialogs and rare to display dialogs are often missing from the response file.
It could also be that the missing dialog is a custom made dialog and not a built-in Installshield dialog. I suppose this could mean it doesn't behave in the standard way.
How complex is this setup? How many systems are you deploying to? To reliably deploy files like these it is common to use "setup capture" and repackage as MSI files - so called application repackaging.
Depending on how many setups you have, how important they are and how many machines they need to reliably work on it might be worth capturing them. This is a highly complex task at times, but yield more reliable deployment once done right. Personally I find the biggest benefit of repackaging is the availability of a reliable uninstall - provided you have cleaned up the capture properly. Otherwise you have to create response files for the uninstall too. Very clunky and error prone - even when done well.
You might want to take this discussion to serverfault.com - the system administrator equivalent to stackoverflow.com. You can also have a look here: http://unattended.sourceforge.net/installers.php

how to host an exe with a dynamic commandline parameter

I have an installer exe which takes a channel_id param as a command line parameter and uses it.
The channel_id may be different for different downloads and installs.
I want to host my installer exe on web in such a way that when it's downloaded and executed (by double clicking) the channel_id is passed to it in someway ,which should be equivalent to running the installer exe in cmd with channel_id as below.
cmd> myinstaller.exe channel_id.
How is it possible to do so ?
You can append data to the end of the .exe file.
You can include your param in the name of the file. For example, instead of setup.exe, call it setup_XXXX.exe. Then from NSIS you can read and parse $EXEPATH and extract your param from the filename.
Probably not the most reliable way to do this (if there is any), but you could probably check for the Zone.Identifier. I'm not aware of a way to this natively in NSIS, but you might be able to achieve by parsing the result through the commandline.
Try
nsExec::ExecToLog 'more < "$EXEPATH:Zone.Identifier"'
or
nsExec::ExecToLog 'dir /r "$EXEPATH"'
I've also found several Powershell (and VisualBasic) scripts that allow interacting with Alternate Data Streams, but personally I'm not a big fan of using third party scripting languages.

NSIS: How to check whether *.dll from my installation is in $SYSDIR?

I wanted to write an NSIS script, let's call it for now setup.nsi, and check
if several required dll files already exists in $SYSDIR
Let me emphasize on the word "several"
What I understand from nsis IfFileExists documentation is that if I type in:
IfFileExists $SYSDIR\blabla.dll +2 +1
then it checks if blabla.dll is in $SYSDIR .. but what if I want to know if *.dll from where setup.nsi copies the file (i.e. the *.dll's that I am interested in installing in.. and they are a lot of them.. so I can't just go around checking for all the names) exists in $SYSDIR
During uninstallation I want to then be able to delete them from $SYSDIR (using some uninstall.log to see if I really copied them in $SYSDIR.. and again the wildcard question).
Please be patient with me as I am really new to NSIS scripts.
Is it REALLY necessary to write and delete in $SYSDIR ? Unless yours is a system file, there's no reason for it to be in $__SYS__DIR. If you need to use a specific version of a library, consider DLL redirection (put your DLL in your app dir and use the .local feature) - see the MSDN article on DLL redirection and Side-by-side assemblies.
Plus, you are one typo away from wrecking the user's computer ("Deleted: C:\Windows\System32\user32.dll").
As Piskvor mentions, I don't think you should be worrying about deleting system DLLs in the uninstaller. In case you want to overwrite system DLLs with an updated version, you may want to look at the SetOverwrite command. It lets you overwrite files if what you've got is newer.
Windows XP (SP2?) and up has file protection for system32, so you can't overwrite system critical files in there.
Do try to stay away from that.
Also, to check for your file specifically, see if there's a plugin for NSIS that can calculate checksums and compare that on uninstall. That's probably the safest, IF you really need to do it.
I'd suggest install files somewhere else and add that to PATH.

Resources