NSIS/Source/exehead/Main.c
anders_k f69251d87e (C) 2021
git-svn-id: https://svn.code.sf.net/p/nsis/code/NSIS/trunk@7245 212acab6-be3b-0410-9dea-997c60f758d6
2021-01-01 20:27:52 +00:00

454 lines
15 KiB
C

/*
* main.c: executable header main code
*
* This file is a part of NSIS.
*
* Copyright (C) 1999-2021 Nullsoft and Contributors
*
* Licensed under the zlib/libpng license (the "License");
* you may not use this file except in compliance with the License.
*
* Licence details can be found in the file COPYING.
*
* This software is provided 'as-is', without any express or implied
* warranty.
*/
#include "../Platform.h"
#include <shlobj.h>
#include "resource.h"
#include "util.h"
#include "fileform.h"
#include "state.h"
#include "ui.h"
#include "lang.h"
#include "state.h"
#include "exec.h"
#include "plugin.h"
#ifndef LOAD_LIBRARY_SEARCH_USER_DIRS
#define LOAD_LIBRARY_SEARCH_USER_DIRS 0x00000400
#define LOAD_LIBRARY_SEARCH_SYSTEM32 0x00000800
#endif
#ifndef SHTDN_REASON_FLAG_PLANNED
#define SHTDN_REASON_FLAG_PLANNED 0x80000000
#endif
#ifndef SHTDN_REASON_MAJOR_APPLICATION
#define SHTDN_REASON_MAJOR_APPLICATION 0x00040000
#endif
#ifndef SHTDN_REASON_MINOR_INSTALLATION
#define SHTDN_REASON_MINOR_INSTALLATION 0x0002
#endif
#ifndef SHUTDOWN_RESTART
#define SHUTDOWN_RESTART 0x00000004
#endif
#ifndef SHUTDOWN_FORCE_OTHERS
#define SHUTDOWN_FORCE_OTHERS 0x00000001
#endif
#ifndef SHUTDOWN_GRACE_OVERRIDE
#define SHUTDOWN_GRACE_OVERRIDE 0x00000020
#endif
#if defined(_MSC_VER) && _MSC_VER >= 1200
EXTERN_C IMAGE_DOS_HEADER __ImageBase;
#define HINST_THISCOMPONENT ( (HINSTANCE) &__ImageBase )
#define HINST_APPLICATION HINST_THISCOMPONENT
#else
#define HINST_APPLICATION ( (HINSTANCE) GetModuleHandle(NULL) )
#endif
#if !defined(NSIS_CONFIG_VISIBLE_SUPPORT) && !defined(NSIS_CONFIG_SILENT_SUPPORT)
#error One of NSIS_CONFIG_SILENT_SUPPORT or NSIS_CONFIG_VISIBLE_SUPPORT must be defined.
#endif
#ifdef NSIS_COMPRESS_WHOLE
extern HANDLE dbd_hFile;
#endif
TCHAR g_caption[NSIS_MAX_STRLEN*2]; // Why does this have to be NSIS_MAX_STRLEN*2?
#ifdef NSIS_CONFIG_VISIBLE_SUPPORT
HWND g_hwnd;
HANDLE g_hInstance;
#endif
void *g_SHGetFolderPath;
DWORD g_WinVer;
void NSISCALL CleanUp();
TCHAR *ValidateTempDir()
{
validate_filename(state_temp_dir);
if (!validpathspec(state_temp_dir))
return NULL;
addtrailingslash(state_temp_dir);
CreateNormalDirectory(state_temp_dir);
// state_language is used as a temp var here
return my_GetTempFileName(state_language, state_temp_dir);
}
NSIS_ENTRYPOINT_GUINOCRT
EXTERN_C void NSISWinMainNOCRT()
{
int ret = 0;
const TCHAR *m_Err = _LANG_ERRORWRITINGTEMP;
int cl_flags = 0;
TCHAR *realcmds;
TCHAR seekchar=_T(' ');
TCHAR *cmdline;
SetErrorMode(SEM_NOOPENFILEERRORBOX | SEM_FAILCRITICALERRORS);
g_WinVer = GetVersion() & ~(NSIS_WINVER_WOW64FLAG); // We store a private flag in the build number bits
{
// bug #1125: Don't load modules from the application nor current directory.
// SetDefaultDllDirectories() allows us to restrict implicitly loaded and
// dynamically loaded modules to just %windir%\System32 and directories
// added with AddDllDirectory(). This prevents DLL search order attacks (CAPEC-471).
DWORD winver = g_WinVer;
// CoCreateInstance(CLSID_ShellLink, ...) fails on Vista if SetDefaultDllDirectories is called
BOOL avoidwinbug = LOWORD(winver) == MAKEWORD(6, 0);
if (!avoidwinbug)
{
FARPROC fp = myGetProcAddress(MGA_SetDefaultDllDirectories);
if (fp) ((BOOL(WINAPI*)(DWORD))fp)(LOAD_LIBRARY_SEARCH_SYSTEM32|LOAD_LIBRARY_SEARCH_USER_DIRS);
}
// SetDefaultDllDirectories might not be available so we try to preload various libraries as
// best we can before Windows gets a chance to mess things up by loading from the wrong directory.
{
static const char preload[] =
"UXTHEME\0" // Vista: OleInitialize calls NtUserCreateWindowEx and that pulls in UXTheme.dll
"USERENV\0" // Vista: SHGetFileInfo ends up in SHELL32.kfapi::GetUserProfileDir and that pulls in UserEnv.dll
"SETUPAPI\0" // XP: SHGetFileInfo ends up in CMountPoint::_InitLocalDriveHelper and that pulls in SetupAPI.dll
"APPHELP\0" // Vista: SHGetFileInfo ... SHELL32.SHILAliasTranslate ... SHELL32.ApphelpCheckShellObject
"PROPSYS\0" // Vista: SHGetFileInfo ... SHELL32.SHILAliasTranslate ... SHLWAPI.#187 ... SHLWAPI.#505/SHPropertyBag_ReadGUID
"DWMAPI\0" // Win7 without KB2533623: UXTheme pulls in DWMAPI.dll
"CRYPTBASE\0" // Win7 without KB2533623: OleInitialize ... RPCRT4.UuidCreate ... RPCRT4.GenerateRandomNumber
"OLEACC\0" // Vista: SHFileOperation ... SHELL32.CProgressDialogUI::_Setup ... SHELL32.GetRoleTextW
"CLBCATQ\0" // XP.SP2&SP3: SHAutoComplete ... OLE32!InitializeCatalogIfNecessary ... OLE32!CComCatalog::TryToLoadCLB
"NTMARTA\0" // Win7 without KB2533623 (Bug #1204): SHGetFileInfo ... SetEntriesInAcl ... ADVAPI32!AccProvpLoadMartaFunctions
#ifndef NSIS_SUPPORT_GETDLLVERSION
"VERSION\0"
#endif
;
const char *dll;
for (dll = preload; dll[0]; dll += lstrlenA(dll) + 1)
LoadSystemLibrary(dll);
}
}
// Because myGetProcAddress now loads dlls with a full path
// under GetSystemDirectory() the previous issues in <= v3.0b2 with
// 'SetOutPath' and/or 'File "shfolder.dll"' no longer apply.
// All MGA dlls still need to be loaded early here because installers
// running under WoW64 might disable WoW64 FS redirection in .onInit and
// because GetSystemDirectory() can return the native system32 path we need
// the redirection to be turned off so LoadLibrary uses the correct folder.
// Note: We also import directly from KERNEL32, ADVAPI32 and SHELL32 so they
// are exempt from this requirement and SHELL32 imports from SHLWAPI on
// WoW64 systems and it is also on the KnownDLLs list so
// SHLWAPI also gets a pass and that just leaves
#ifdef NSIS_SUPPORT_GETDLLVERSION
myGetProcAddress(MGA_GetFileVersionInfo); // VERSION
#endif
g_SHGetFolderPath = myGetProcAddress(MGA_SHGetFolderPath); // and SHFOLDER
#ifndef _WIN64
{
// KEY_WOW64_xxKEY flags causes registry functions to fail on WinNT4 & Win2000.
// We don't filter them out because all registry instructions are supposed to fail when
// accessing a unsupported view and RegKey* takes care of that by looking at the WOW64 flag.
FARPROC fp = myGetProcAddress(MGA_IsOS);
enum { os_wow6432 = 30 };
if (fp && ((BOOL(WINAPI*)(UINT))fp)(os_wow6432)) g_WinVer |= NSIS_WINVER_WOW64FLAG;
}
#endif
InitCommonControls();
#if defined(NSIS_SUPPORT_ACTIVEXREG) || defined(NSIS_SUPPORT_CREATESHORTCUT)
{
extern HRESULT g_hres;
g_hres=OleInitialize(NULL);
}
#endif
{
// workaround for bug #1008632
// http://sourceforge.net/tracker/index.php?func=detail&aid=1008632&group_id=22049&atid=373085
//
// without this, SHGetSpecialFolderLocation doesn't always recognize
// some special folders, like the desktop folder for all users, on
// Windows 9x. unlike SHGetSpecialFolderPath, which is not available
// on all versions of Windows, SHGetSpecialFolderLocation doesn't try
// too hard to make sure the caller gets what he asked for. so we give
// it a little push in the right direction by doing part of the work
// for it.
//
// part of what SHGetFileInfo does, is to convert a path into an idl.
// to do this conversion, it first needs to initialize the list of
// special idls, which are exactly the idls we use to get the paths
// of special folders (CSIDL_*).
SHFILEINFO shfi;
SHGetFileInfo(_T(""), 0, &shfi, sizeof(SHFILEINFO), 0);
}
mystrcpy(g_caption,_LANG_GENERIC_ERROR);
mystrcpy(state_command_line, GetCommandLine());
#ifdef NSIS_CONFIG_VISIBLE_SUPPORT
g_hInstance = HINST_APPLICATION;
#endif//NSIS_CONFIG_VISIBLE_SUPPORT
cmdline = state_command_line;
if (*cmdline == _T('\"')) seekchar = *cmdline++;
cmdline=findchar(cmdline, seekchar);
cmdline=CharNext(cmdline);
realcmds=cmdline;
while (*cmdline)
{
// skip over any spaces
while (*cmdline == _T(' ')) cmdline++;
// get char we should look for to get the next parm
seekchar = _T(' ');
if (cmdline[0] == _T('\"'))
{
cmdline++;
seekchar = _T('\"');
}
// is it a switch?
if (cmdline[0] == _T('/'))
{
cmdline++;
#define END_OF_ARG(c) (c == _T(' ') || c == _T('\0'))
#if defined(NSIS_CONFIG_VISIBLE_SUPPORT) && defined(NSIS_CONFIG_SILENT_SUPPORT)
if (cmdline[0] == _T('S') && END_OF_ARG(cmdline[1]))
g_exec_flags.silent = 1; // bug #1076 - just set the silent flag. the user really wants it silent.
// loadHeaders() will not reset this as it uses |= to apply the script flags.
// there is also no option to force non-silent like `CRCCheck force`
#endif//NSIS_CONFIG_SILENT_SUPPORT && NSIS_CONFIG_VISIBLE_SUPPORT
#ifdef NSIS_CONFIG_CRC_SUPPORT
if (CMP4CHAR(cmdline, _T("NCRC")) && END_OF_ARG(cmdline[4]))
cl_flags |= FH_FLAGS_NO_CRC;
#endif//NSIS_CONFIG_CRC_SUPPORT
if (CMP4CHAR(cmdline-2, _T(" /D=")))
{
*(cmdline-2)=_T('\0'); // keep this from being passed to uninstaller if necessary
mystrcpy(state_install_directory,cmdline+2);
break; // /D= must always be last
}
}
// skip over our parm
cmdline = findchar(cmdline, seekchar);
// skip the quote
if (*cmdline == _T('\"'))
cmdline++;
}
GetTempPath(NSIS_MAX_STRLEN, state_temp_dir);
if (!ValidateTempDir())
{
GetWindowsDirectory(state_temp_dir, NSIS_MAX_STRLEN - 5); // leave space for \Temp
mystrcat(state_temp_dir, _T("\\Temp"));
if (!ValidateTempDir())
{
// Bug #2909242:
// When running at <= Low IL we cannot write to %Temp% but we can try the temp folder used by IE.
// There does not seem to be a API to get the low temp dir directly, so we build the path on our own
GetTempPath(NSIS_MAX_STRLEN - 4, state_temp_dir); // leave space for \Low
mystrcat(state_temp_dir, _T("Low"));
// If we don't call SetEnvironmentVariable
// child processes will use %temp% and not %temp%\Low
// and some apps probably can't handle a read only %temp%
// Do it before ValidateTempDir() because it appends a backslash.
// TODO: Should this be moved to ValidateTempDir() so it also updates for %windir%\Temp?
SetEnvironmentVariable(_T("TEMP"), state_temp_dir);
SetEnvironmentVariable(_T("TMP"), state_temp_dir);
if (!ValidateTempDir())
{
goto end;
}
}
}
DeleteFile(state_language);
m_Err = loadHeaders(cl_flags);
if (m_Err) goto end;
#ifdef NSIS_CONFIG_UNINSTALL_SUPPORT
if (g_is_uninstaller)
{
TCHAR *p = findchar(state_command_line, 0);
// state_command_line has state_install_directory right after it in memory, so reading
// a bit over state_command_line won't do any harm
while (p >= state_command_line && !CMP4CHAR(p, _T(" _?="))) p--;
m_Err = _LANG_UNINSTINITERROR;
if (p >= state_command_line)
{
*p=0; // terminate before "_?="
p+=4; // skip over " _?="
if (is_valid_instpath(p))
{
mystrcpy(state_install_directory, p);
mystrcpy(state_output_directory, p);
m_Err = 0;
}
else
{
goto end;
}
}
else
{
int x, admin = UserIsAdminGrpMember();
mystrcat(state_temp_dir,_T("~nsu"));
if (admin) mystrcat(state_temp_dir,_T("A")); // Don't lock down the directory used by non-admins
mystrcat(state_temp_dir,_T(".tmp"));
// check if already running from uninstaller temp dir
// this prevents recursive uninstaller calls
if (!lstrcmpi(state_temp_dir,state_exe_directory))
goto end;
admin ? CreateRestrictedDirectory(state_temp_dir) : CreateNormalDirectory(state_temp_dir);
SetCurrentDirectory(state_temp_dir);
if (!(*state_install_directory))
mystrcpy(state_install_directory,state_exe_directory);
mystrcpy(g_usrvars[0], realcmds);
SET2CHAR(g_usrvars[1], _T("A\0"));
for (x = 0; x < 26; x ++)
{
static TCHAR buf2[NSIS_MAX_STRLEN];
GetNSISString(buf2,g_header->str_uninstchild); // $TEMP\Un_$1.exe
DeleteFile(buf2); // clean up after all the other ones if they are there
if (m_Err) // not done yet
{
// copy file
if (CopyFile(state_exe_path,buf2,TRUE))
{
HANDLE hProc;
#ifdef NSIS_SUPPORT_MOVEONREBOOT
MoveFileOnReboot(buf2,NULL);
#endif
GetNSISString(buf2,g_header->str_uninstcmd); // '"$TEMP\Un_$1.exe" $0 _?=$INSTDIR\'
hProc=myCreateProcess(buf2);
if (hProc)
{
CloseHandle(hProc);
// success
m_Err = 0;
}
}
}
(*(((NSIS_STRING *)g_usrvars)[1]))++;
}
#ifdef NSIS_SUPPORT_MOVEONREBOOT
MoveFileOnReboot(state_temp_dir,NULL);
#endif
goto end;
}
}
#endif//NSIS_CONFIG_UNINSTALL_SUPPORT
g_exec_flags.errlvl = -1;
ret = ui_doinstall();
#ifdef NSIS_CONFIG_LOG
#if !defined(NSIS_CONFIG_LOG_ODS) && !defined(NSIS_CONFIG_LOG_STDOUT)
log_write(1);
#endif//!NSIS_CONFIG_LOG_ODS && !NSIS_CONFIG_LOG_STDOUT
#endif//NSIS_CONFIG_LOG
end:
CleanUp();
#if defined(NSIS_SUPPORT_ACTIVEXREG) || defined(NSIS_SUPPORT_CREATESHORTCUT)
OleUninitialize();
#endif
if (m_Err)
{
my_MessageBox(m_Err, MB_OK | MB_ICONSTOP | (IDOK << 21));
ExitProcess(2);
}
#ifdef NSIS_SUPPORT_REBOOT
if (g_exec_flags.reboot_called)
{
const DWORD reason = SHTDN_REASON_FLAG_PLANNED | SHTDN_REASON_MAJOR_APPLICATION | SHTDN_REASON_MINOR_INSTALLATION;
BOOL (WINAPI *IS)(LPTSTR,LPTSTR,DWORD,DWORD,DWORD);
HANDLE hToken;
TOKEN_PRIVILEGES tkp;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken))
{
LookupPrivilegeValue(NULL, SE_SHUTDOWN_NAME, &tkp.Privileges[0].Luid);
tkp.PrivilegeCount = 1;
tkp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
AdjustTokenPrivileges(hToken, FALSE, &tkp, 0, (PTOKEN_PRIVILEGES)NULL, 0);
}
IS=myGetProcAddress(MGA_InitiateShutdown);
if ( (IS && !IS(NULL, NULL, 0, SHUTDOWN_RESTART | SHUTDOWN_FORCE_OTHERS | SHUTDOWN_GRACE_OVERRIDE, reason))
|| (!ExitWindowsEx(EWX_REBOOT, reason))
)
ExecuteCallbackFunction(CB_ONREBOOTFAILED);
}
#endif//NSIS_SUPPORT_REBOOT
if (g_exec_flags.errlvl != -1)
ret = g_exec_flags.errlvl;
ExitProcess(ret);
}
void NSISCALL CleanUp()
{
if (g_db_hFile != INVALID_HANDLE_VALUE)
{
CloseHandle(g_db_hFile);
g_db_hFile = INVALID_HANDLE_VALUE;
}
#ifdef NSIS_COMPRESS_WHOLE
if (dbd_hFile != INVALID_HANDLE_VALUE)
{
CloseHandle(dbd_hFile);
dbd_hFile = INVALID_HANDLE_VALUE;
}
#endif
#ifdef NSIS_CONFIG_PLUGIN_SUPPORT
// Notify plugins that we are about to unload
Plugins_UnloadAll();
// Clean up after plug-ins
myDelete(state_plugins_dir, DEL_DIR | DEL_RECURSE | DEL_REBOOT);
#endif // NSIS_CONFIG_PLUGIN_SUPPORT
#ifdef DEBUG
// GlobalFree(g_header); ?
#endif
}