diff options
-rw-r--r-- | Repository.mk | 1 | ||||
-rw-r--r-- | scp2/source/ooo/windowscustomaction_ooo.scp | 41 | ||||
-rw-r--r-- | scp2/source/spsupp/module_spsupp.scp | 18 | ||||
-rw-r--r-- | setup_native/Library_reg_dlls.mk | 40 | ||||
-rw-r--r-- | setup_native/Module_setup_native.mk | 1 | ||||
-rw-r--r-- | setup_native/source/win32/customactions/reg_dlls/reg_dlls.cxx | 309 | ||||
-rw-r--r-- | setup_native/source/win32/customactions/reg_dlls/reg_dlls.def | 5 | ||||
-rw-r--r-- | solenv/bin/modules/installer/windows/feature.pm | 23 |
8 files changed, 432 insertions, 6 deletions
diff --git a/Repository.mk b/Repository.mk index 88b88d73808f..03628e958f72 100644 --- a/Repository.mk +++ b/Repository.mk @@ -676,6 +676,7 @@ $(eval $(call gb_Helper_register_libraries_for_install,PLAINLIBS_OOO,ooobinaryta instooofiltmsi \ inst_msu_msi \ qslnkmsi \ + reg_dlls \ reg4allmsdoc \ sdqsmsi \ sellangmsi \ diff --git a/scp2/source/ooo/windowscustomaction_ooo.scp b/scp2/source/ooo/windowscustomaction_ooo.scp index bc7201b9b2b4..116b437b1ed3 100644 --- a/scp2/source/ooo/windowscustomaction_ooo.scp +++ b/scp2/source/ooo/windowscustomaction_ooo.scp @@ -221,3 +221,44 @@ WindowsCustomAction gid_Customaction_RegisterSomeExtensions End #endif /* HAVE_WINDOWS_SDK */ + +/* Deferred not-impersonated actions that will call regsvr32 to (un)register DLLs. + * Custom action type 1 (msidbCustomActionTypeDll + msidbCustomActionTypeBinaryData) + * + 64 (msidbCustomActionTypeContinue) + 1024 (msidbCustomActionTypeInScript) + * + 2048 (msidbCustomActionTypeNoImpersonate). + * Since deferred actions don't have access to current DB, the actions depend on + * immediate-executed action prep_reg_unreg_dlls (see below) that precedes it, and + * sets this action's CustomActionData property. + */ + +WindowsCustomAction gid_Customaction_reg_dlls + Name = "reg_dlls"; + Typ = "3137"; + Source = "reg_dlls.dll"; + Target = "RegDLLs"; + Inbinarytable = 1; + Assignment1 = ("InstallExecuteSequence", "reg_dlls", "InstallFinalize"); +End + +WindowsCustomAction gid_Customaction_unreg_dlls + Name = "unreg_dlls"; + Typ = "3137"; + Source = "reg_dlls.dll"; + Target = "UnregDLLs"; + Inbinarytable = 1; + Assignment1 = ("InstallExecuteSequence", "unreg_dlls", "UnpublishComponents"); +End + +/* Immediately-executed action that adds registration command lines for spsupp_x*.dll + * to "[un]reg_dlls" properties. + * Custom action type 1 (msidbCustomActionTypeDll + msidbCustomActionTypeBinaryData) + * + 64 (msidbCustomActionTypeContinue). + */ +WindowsCustomAction gid_Customaction_prep_reg_dlls + Name = "prep_reg_unreg_dlls"; + Typ = "65"; + Source = "reg_dlls.dll"; + Target = "PrepRegUnregDLLs"; + Inbinarytable = 1; + Assignment1 = ("InstallExecuteSequence", "", "behind_CostFinalize"); +End diff --git a/scp2/source/spsupp/module_spsupp.scp b/scp2/source/spsupp/module_spsupp.scp index 39c547a5fa5c..b97a84a699d5 100644 --- a/scp2/source/spsupp/module_spsupp.scp +++ b/scp2/source/spsupp/module_spsupp.scp @@ -22,5 +22,23 @@ Module gid_Module_Optional_SharePointSupport Files = (auto_spsuppfiles_ALL); End +/* + * This is an *empty* feature disabled by default, that controls custom action registering + * SharePoint.OpenDocuments class, which replaces registration of that class from MSO. + * It is disabled to allow co-existing with MS Office. To install, a transform must be + * used that would set its level to non-0 value, or a command line like + * + * msiexec.exe /i path-to-msi ADDLOCAL=gm_SharePointSupport_SubstMSO + */ +Module gid_Module_SharePointSupport_SubstMSO + ParentID = gid_Module_Optional_SharePointSupport; + Name = "gid_Module_SharePointSupport_SubstMSO"; + Description = "Registration of SharePoint.OpenDocuments class"; + Sortkey = "1305"; + Default = NO; + Independent = YES; + Styles = (HIDDEN_ROOT); +End + #endif diff --git a/setup_native/Library_reg_dlls.mk b/setup_native/Library_reg_dlls.mk new file mode 100644 index 000000000000..f0ac9a875c60 --- /dev/null +++ b/setup_native/Library_reg_dlls.mk @@ -0,0 +1,40 @@ +# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*- +# +# This file is part of the LibreOffice project. +# +# This Source Code Form is subject to the terms of the Mozilla Public +# License, v. 2.0. If a copy of the MPL was not distributed with this +# file, You can obtain one at http://mozilla.org/MPL/2.0/. +# + +$(eval $(call gb_Library_Library,reg_dlls)) + +$(eval $(call gb_Library_add_defs,reg_dlls,\ + -U_DLL \ +)) + +$(eval $(call gb_Library_add_cxxflags,reg_dlls,\ + $(if $(MSVC_USE_DEBUG_RUNTIME),/MTd,/MT) \ +)) + +$(eval $(call gb_Library_add_ldflags,reg_dlls,\ + /DEF:$(SRCDIR)/setup_native/source/win32/customactions/reg_dlls/reg_dlls.def \ + /NODEFAULTLIB \ +)) + +$(eval $(call gb_Library_add_exception_objects,reg_dlls,\ + setup_native/source/win32/customactions/reg_dlls/reg_dlls \ +)) + +$(eval $(call gb_Library_use_system_win32_libs,reg_dlls,\ + libcmt \ + libcpmt \ + libucrt \ + libvcruntime \ + kernel32 \ + Ole32 \ + Shell32 \ + Msi \ +)) + +# vim: set noet sw=4 ts=4: diff --git a/setup_native/Module_setup_native.mk b/setup_native/Module_setup_native.mk index 1009c53dcb2d..42fa1421786d 100644 --- a/setup_native/Module_setup_native.mk +++ b/setup_native/Module_setup_native.mk @@ -25,6 +25,7 @@ $(eval $(call gb_Module_add_targets,setup_native,\ Library_instooofiltmsi \ Library_inst_msu_msi \ Library_qslnkmsi \ + Library_reg_dlls \ Library_reg4allmsdoc \ Library_regactivex \ Library_sdqsmsi \ diff --git a/setup_native/source/win32/customactions/reg_dlls/reg_dlls.cxx b/setup_native/source/win32/customactions/reg_dlls/reg_dlls.cxx new file mode 100644 index 000000000000..250a81bd0fa0 --- /dev/null +++ b/setup_native/source/win32/customactions/reg_dlls/reg_dlls.cxx @@ -0,0 +1,309 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include <iomanip> +#include <memory> +#include <string> +#include <sstream> + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <Shlobj.h> +#include <msiquery.h> + +namespace +{ +template <typename IntType> std::string Num2Hex(IntType n) +{ + std::stringstream sMsg; + sMsg << "0x" << std::uppercase << std::setfill('0') << std::setw(sizeof(n) * 2) << std::hex + << n; + return sMsg.str(); +} + +template <typename IntType> std::string Num2Dec(IntType n) +{ + std::stringstream sMsg; + sMsg << n; + return sMsg.str(); +} + +std::string Win32ErrorMessage(const char* sFunc, DWORD nWin32Error) +{ + std::stringstream sMsg; + sMsg << sFunc << " failed with Win32 error code " << Num2Hex(nWin32Error) << "!"; + + return sMsg.str(); +} + +void ThrowHResult(const char* sFunc, HRESULT hr) +{ + std::stringstream sMsg; + sMsg << sFunc << " failed (HRESULT = " << Num2Hex(hr) << ")!"; + + throw std::exception(sMsg.str().c_str()); +} + +void CheckHResult(const char* sFunc, HRESULT hr) +{ + if (FAILED(hr)) + ThrowHResult(sFunc, hr); +} + +void ThrowWin32Error(const char* sFunc, DWORD nWin32Error) +{ + throw std::exception(Win32ErrorMessage(sFunc, nWin32Error).c_str()); +} + +void ThrowLastError(const char* sFunc) { ThrowWin32Error(sFunc, GetLastError()); } + +void CheckWin32Error(const char* sFunc, DWORD nWin32Error) +{ + if (nWin32Error != ERROR_SUCCESS) + ThrowWin32Error(sFunc, nWin32Error); +} + +std::wstring GetKnownFolder(const KNOWNFOLDERID& rfid) +{ + PWSTR sPath = nullptr; + HRESULT hr = SHGetKnownFolderPath(rfid, KF_FLAG_DEFAULT, nullptr, &sPath); + CheckHResult("SHGetKnownFolderPath", hr); + std::wstring sResult(sPath); + CoTaskMemFree(sPath); + return sResult; +} + +void WriteLogElem(MSIHANDLE hInst, MSIHANDLE hRecord, std::ostringstream& sTmpl, UINT) +{ + MsiRecordSetStringA(hRecord, 0, sTmpl.str().c_str()); + MsiProcessMessage(hInst, INSTALLMESSAGE_INFO, hRecord); +} + +void RecSetString(MSIHANDLE hRec, UINT nField, LPCSTR sVal) +{ + MsiRecordSetStringA(hRec, nField, sVal); +} + +void RecSetString(MSIHANDLE hRec, UINT nField, LPCWSTR sVal) +{ + MsiRecordSetStringW(hRec, nField, sVal); +} + +template <class Ch, class... SOther> +void WriteLogElem(MSIHANDLE hInst, MSIHANDLE hRec, std::ostringstream& sTmpl, UINT nField, + const Ch* elem, const SOther&... others) +{ + sTmpl << " [" << nField << "]"; + RecSetString(hRec, nField, elem); + WriteLogElem(hInst, hRec, sTmpl, nField + 1, others...); +} + +template <class S1, class... SOther> +void WriteLogElem(MSIHANDLE hInst, MSIHANDLE hRec, std::ostringstream& sTmpl, UINT nField, + const S1& elem, const SOther&... others) +{ + WriteLogElem(hInst, hRec, sTmpl, nField, elem.c_str(), others...); +} + +static std::string sLogPrefix; + +template <class... StrType> void WriteLog(MSIHANDLE hInst, const StrType&... elements) +{ + PMSIHANDLE hRec = MsiCreateRecord(sizeof...(elements)); + if (!hRec) + return; + + std::ostringstream sTemplate; + sTemplate << sLogPrefix; + WriteLogElem(hInst, hRec, sTemplate, 1, elements...); +} + +std::wstring MsiGetPropertyW(MSIHANDLE hInst, LPCWSTR szName) +{ + std::wstring sResult; + DWORD nSz = 0; + UINT nRet = ::MsiGetPropertyW(hInst, szName, L"", &nSz); + if (nRet == ERROR_MORE_DATA) + { + ++nSz; + auto buf = std::make_unique<wchar_t[]>(nSz); + CheckWin32Error("MsiGetPropertyW", ::MsiGetPropertyW(hInst, szName, buf.get(), &nSz)); + sResult = buf.get(); + WriteLog(hInst, "Property", szName, "=", sResult); + } + else + CheckWin32Error("MsiGetPropertyW", nRet); + + return sResult; +} + +typedef std::unique_ptr<void, decltype(&CloseHandle)> CloseHandleGuard; +CloseHandleGuard Guard(HANDLE h) { return CloseHandleGuard(h, CloseHandle); } + +void RegDLL(MSIHANDLE hInst, const std::wstring& sArgs, bool bUnreg) +{ + static std::wstring sRegSvr32 = GetKnownFolder(FOLDERID_System) + L"\\regsvr32.exe"; + + try + { + std::wstring sCmd = L"\"" + sRegSvr32 + L"\" /s "; + if (bUnreg) + sCmd += L"/u "; + sCmd += sArgs; + WriteLog(hInst, "Prepared regsvr32 command:", sCmd); + + STARTUPINFOW si{}; + si.cb = sizeof(si); + PROCESS_INFORMATION pi{}; + if (!CreateProcessW(sRegSvr32.c_str(), const_cast<LPWSTR>(sCmd.c_str()), nullptr, nullptr, + FALSE, CREATE_NO_WINDOW, nullptr, nullptr, &si, &pi)) + ThrowLastError("CreateProcessW"); + auto aCloseProcHandleGuard(Guard(pi.hProcess)); + WriteLog(hInst, "CreateProcessW succeeded"); + + DWORD nWaitResult = WaitForSingleObject(pi.hProcess, INFINITE); + if (nWaitResult != WAIT_OBJECT_0) + ThrowWin32Error("WaitForSingleObject", nWaitResult); + + DWORD nExitCode = 0; + if (!GetExitCodeProcess(pi.hProcess, &nExitCode)) + ThrowLastError("GetExitCodeProcess"); + + WriteLog(hInst, "regsvr32 returned:", Num2Dec(nExitCode)); + } + catch (std::exception& e) + { + WriteLog(hInst, e.what()); + } +} + +void ProcessCustomActionData(MSIHANDLE hInst, bool bUnreg) +{ + WriteLog(hInst, "Checking value of CustomActionData"); + std::wstring sCustomActionData = MsiGetPropertyW(hInst, L"CustomActionData"); + WriteLog(hInst, "Got CustomActionData value:", sCustomActionData); + std::wstringstream ss(sCustomActionData); + std::wstring sToken; + while (std::getline(ss, sToken, L'|')) + { + if (!sToken.empty()) + { + RegDLL(hInst, sToken, bUnreg); + } + } +} +} // namespace + +// Deferred action "reg_dlls" that must be run from system account. Receives a list of regsvr32 +// arguments: DLLs which need registering, and possibly /i argument with its parameter. +extern "C" __declspec(dllexport) UINT __stdcall RegDLLs(MSIHANDLE hInstall) +{ + sLogPrefix = "RegDLLs:"; + WriteLog(hInstall, "started"); + + ProcessCustomActionData(hInstall, false); + return ERROR_SUCCESS; +} + +// Deferred action "unreg_dlls" that must be run from system account. Receives a list of regsvr32 +// arguments: DLLs which need registering, and possibly /i argument with its parameter. +extern "C" __declspec(dllexport) UINT __stdcall UnregDLLs(MSIHANDLE hInstall) +{ + sLogPrefix = "UnregDLLs:"; + WriteLog(hInstall, "started"); + + ProcessCustomActionData(hInstall, true); + return ERROR_SUCCESS; +} + +// Immediate action "prep_reg_unreg_dlls". Checks states of the features to prepare custom action data +// for reg_dlls and unreg_dlls deferred actions. +extern "C" __declspec(dllexport) UINT __stdcall PrepRegUnregDLLs(MSIHANDLE hInstall) +{ + sLogPrefix = "PrepRegUnregDLLs:"; + WriteLog(hInstall, "started"); + + try + { + INSTALLSTATE current_state_SubstMSO; + INSTALLSTATE future_state_SubstMSO; + CheckWin32Error("MsiGetFeatureStateW", + MsiGetFeatureStateW(hInstall, L"gm_SharePointSupport_SubstMSO", + ¤t_state_SubstMSO, &future_state_SubstMSO)); + INSTALLSTATE current_state_Main; + INSTALLSTATE future_state_Main; + CheckWin32Error("MsiGetFeatureStateW", + MsiGetFeatureStateW(hInstall, L"gm_o_SharePointSupport", + ¤t_state_Main, &future_state_Main)); + + const bool bUnregSubstMSO = current_state_SubstMSO == INSTALLSTATE_LOCAL + && future_state_SubstMSO == INSTALLSTATE_ABSENT; + const bool bUnregMain + = current_state_Main == INSTALLSTATE_LOCAL && future_state_Main == INSTALLSTATE_ABSENT; + const bool bRegSubstMSO = current_state_SubstMSO == INSTALLSTATE_ABSENT + && future_state_SubstMSO == INSTALLSTATE_LOCAL; + // basic registration is needed when either: + // 1. gm_o_SharePointSupport is installed; + // 2. gm_SharePointSupport_SubstMSO is uninstalled (and unregisters everything), but + // gm_o_SharePointSupport is not, so needs to be re-registered + const bool bRegMain + = (current_state_Main == INSTALLSTATE_ABSENT && future_state_Main == INSTALLSTATE_LOCAL) + || (bUnregSubstMSO && !bUnregMain); + + std::wstring sUnregStr; + if (bUnregSubstMSO) + { + sUnregStr = L"/i:Substitute_OWSSUPP \"[#spsupp_x86.dll]\"|" + L"/i:Substitute_OWSSUPP \"[#spsupp_x64.dll]\""; + } + else if (bUnregMain) + { + sUnregStr = L"\"[#spsupp_x86.dll]\"|\"[#spsupp_x64.dll]\""; + } + + std::wstring sRegStr; + if (bRegSubstMSO) + { + sRegStr = L"/i:Substitute_OWSSUPP \"[#spsupp_x86.dll]\"|" + L"/i:Substitute_OWSSUPP \"[#spsupp_x64.dll]\""; + } + else if (bRegMain) + { + sRegStr = L"\"[#spsupp_x86.dll]\"|\"[#spsupp_x64.dll]\""; + } + + auto SetFormattedPropW = [&](LPCWSTR sProp, const std::wstring& sVal) { + PMSIHANDLE hRec = MsiCreateRecord(0); + if (!hRec) + throw std::exception("MsiCreateRecord failed!"); + MsiRecordSetStringW(hRec, 0, sVal.c_str()); + DWORD nSz = 0; + if (MsiFormatRecordW(hInstall, hRec, L"", &nSz) == ERROR_MORE_DATA) + { + ++nSz; + auto buf = std::make_unique<wchar_t[]>(nSz); + CheckWin32Error("MsiFormatRecordW", + MsiFormatRecordW(hInstall, hRec, buf.get(), &nSz)); + CheckWin32Error("MsiSetPropertyW", MsiSetPropertyW(hInstall, sProp, buf.get())); + } + }; + if (!sRegStr.empty()) + SetFormattedPropW(L"reg_dlls", sRegStr); + if (!sUnregStr.empty()) + SetFormattedPropW(L"unreg_dlls", sUnregStr); + } + catch (std::exception& e) + { + WriteLog(hInstall, e.what()); + } + + return ERROR_SUCCESS; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/setup_native/source/win32/customactions/reg_dlls/reg_dlls.def b/setup_native/source/win32/customactions/reg_dlls/reg_dlls.def new file mode 100644 index 000000000000..d0f31c491b1c --- /dev/null +++ b/setup_native/source/win32/customactions/reg_dlls/reg_dlls.def @@ -0,0 +1,5 @@ +LIBRARY "reg_dlls.dll" +EXPORTS + RegDLLs + UnregDLLs + PrepRegUnregDLLs diff --git a/solenv/bin/modules/installer/windows/feature.pm b/solenv/bin/modules/installer/windows/feature.pm index 84d4e992d37f..c97be4a9c98e 100644 --- a/solenv/bin/modules/installer/windows/feature.pm +++ b/solenv/bin/modules/installer/windows/feature.pm @@ -125,13 +125,19 @@ sub get_feature_level my $level = "20"; # the default - my $localdefault = ""; - - if ( $onefeature->{'Default'} ) { $localdefault = $onefeature->{'Default'}; } - - if ( $localdefault eq "NO" ) # explicitly set Default = "NO" + if ( $onefeature->{'Disabled'} ) { - $level = "200"; # deselected in default installation, base is 100 + if ( $onefeature->{'Disabled'} eq "YES" ) # Disabled = "YES" + { + $level = "0"; # disabled for installation at any INSTALLLEVEL + } + } + elsif ( $onefeature->{'Default'} ) + { + if ( $onefeature->{'Default'} eq "NO" ) # explicitly set Default = "NO" + { + $level = "200"; # deselected in default installation, base is 100 + } } return $level @@ -162,6 +168,10 @@ sub get_feature_attributes my $attributes; + # 2 = msidbFeatureAttributesFollowParent + # 8 = msidbFeatureAttributesDisallowAdvertise + # 16 = msidbFeatureAttributesUIDisallowAbsent + # No advertising of features and no leaving on network. # Feature without parent must not have the "2" @@ -169,6 +179,7 @@ sub get_feature_attributes if ( $onefeature->{'ParentID'} ) { $parentgid = $onefeature->{'ParentID'}; } if (( $parentgid eq "" ) || ( $parentgid eq $installer::globals::rootmodulegid )) { $attributes = "8"; } + elsif ( $onefeature->{'Independent'} && ($onefeature->{'Independent'} eq "YES") ) { $attributes = "8"; } elsif ( get_feature_display($onefeature) eq "0" ) { $attributes = "26"; } # fdo#33798 else { $attributes = "10"; } |