diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:51:28 +0000 |
commit | 940b4d1848e8c70ab7642901a68594e8016caffc (patch) | |
tree | eb72f344ee6c3d9b80a7ecc079ea79e9fba8676d /setup_native/source/win32/customactions | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 1:7.0.4.upstream/1%7.0.4upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'setup_native/source/win32/customactions')
32 files changed, 3745 insertions, 0 deletions
diff --git a/setup_native/source/win32/customactions/indexingfilter/instooofiltmsi.def b/setup_native/source/win32/customactions/indexingfilter/instooofiltmsi.def new file mode 100644 index 000000000..0126556a4 --- /dev/null +++ b/setup_native/source/win32/customactions/indexingfilter/instooofiltmsi.def @@ -0,0 +1,3 @@ +LIBRARY "instooofiltmsi.dll" +EXPORTS + RestartIndexingService
\ No newline at end of file diff --git a/setup_native/source/win32/customactions/indexingfilter/restartindexingservice.cxx b/setup_native/source/win32/customactions/indexingfilter/restartindexingservice.cxx new file mode 100644 index 000000000..e1c26501b --- /dev/null +++ b/setup_native/source/win32/customactions/indexingfilter/restartindexingservice.cxx @@ -0,0 +1,157 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +/* + After installation of the OOo filter for the indexing service + it is necessary to restart the indexing service in order to + activate the filter. This is the most reliable way to get the + indexing service working. We only restart the service if it is + already running. If we have insufficient privileges to restart + the service we do nothing. +*/ + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <msiquery.h> + +const wchar_t * const INDEXING_SERVICE_NAME = L"cisvc"; + +static bool StopIndexingService(SC_HANDLE hService) +{ + SERVICE_STATUS status; + + if (ControlService(hService, SERVICE_CONTROL_STOP, &status)) + { + // Check the status until the service is no longer stop pending. + if (QueryServiceStatus(hService, &status)) + { + DWORD startTime = GetTickCount(); + DWORD oldCheckPoint = status.dwCheckPoint; + + while (status.dwCurrentState == SERVICE_STOP_PENDING) + { + // Do not wait longer than the wait hint. A good interval is + // one tenth the wait hint, but no less than 1 second and no + // more than 10 seconds. + DWORD waitTime = status.dwWaitHint / 10; + + if (waitTime < 1000) + waitTime = 1000; + else if (waitTime > 10000) + waitTime = 10000; + + Sleep(waitTime); + + // Check the status again. + if (!QueryServiceStatus(hService, &status) || + (status.dwCurrentState == SERVICE_STOPPED)) + break; + + if (status.dwCheckPoint > oldCheckPoint) + { + startTime = GetTickCount(); + oldCheckPoint = status.dwCheckPoint; + } + else if ((GetTickCount() - startTime) > status.dwWaitHint) + { + break; // service doesn't react anymore + } + } + } + } + return (status.dwCurrentState == SERVICE_STOPPED); +} + +static void StartIndexingService(SC_HANDLE hService) +{ + if (StartServiceW(hService, 0, nullptr)) + { + SERVICE_STATUS status; + + // Check the status until the service is no longer stop pending. + if (QueryServiceStatus(hService, &status)) + { + DWORD startTime = GetTickCount(); + DWORD oldCheckPoint = status.dwCheckPoint; + + while (status.dwCurrentState == SERVICE_START_PENDING) + { + // Do not wait longer than the wait hint. A good interval is + // one tenth the wait hint, but no less than 1 second and no + // more than 10 seconds. + DWORD waitTime = status.dwWaitHint / 10; + + if (waitTime < 1000) + waitTime = 1000; + else if (waitTime > 10000) + waitTime = 10000; + + Sleep(waitTime); + + // Check the status again. + if (!QueryServiceStatus(hService, &status) || + (status.dwCurrentState == SERVICE_STOPPED)) + break; + + if (status.dwCheckPoint > oldCheckPoint) + { + startTime = GetTickCount(); + oldCheckPoint = status.dwCheckPoint; + } + else if ((GetTickCount() - startTime) > status.dwWaitHint) + { + // service doesn't react anymore + break; + } + } + } + } +} + +extern "C" __declspec(dllexport) UINT __stdcall RestartIndexingService(MSIHANDLE) +{ + SC_HANDLE hSCManager = OpenSCManagerW( + nullptr, // local machine + nullptr, // ServicesActive database + SC_MANAGER_ALL_ACCESS); + + if (hSCManager != nullptr) + { + SC_HANDLE hIndexingService = OpenServiceW( + hSCManager, INDEXING_SERVICE_NAME, SERVICE_QUERY_STATUS | SERVICE_START | SERVICE_STOP); + + if (hIndexingService) + { + SERVICE_STATUS status; + ZeroMemory(&status, sizeof(status)); + + if (QueryServiceStatus(hIndexingService, &status) && + (status.dwCurrentState == SERVICE_RUNNING)) + { + if (StopIndexingService(hIndexingService)) + StartIndexingService(hIndexingService); + } + CloseServiceHandle(hIndexingService); + } + CloseServiceHandle(hSCManager); + } + return ERROR_SUCCESS; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/setup_native/source/win32/customactions/inst_msu/inst_msu.cxx b/setup_native/source/win32/customactions/inst_msu/inst_msu.cxx new file mode 100644 index 000000000..26f7668ff --- /dev/null +++ b/setup_native/source/win32/customactions/inst_msu/inst_msu.cxx @@ -0,0 +1,674 @@ +/* -*- 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 <memory> +#include <string> +#include <sstream> +#include <iomanip> + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <Shlobj.h> +#include <Wuerror.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...); +} + +// Show a warning message box. This will be automatically suppressed in unattended installation. +void ShowWarning(MSIHANDLE hInst, const std::wstring& sKBNo, const char* sMessage) +{ + // Error table's message #25000: "Installing a pre-requisite [2] failed. + // You might need to manually install it from Microsoft site to be able to run the product.[3]" + PMSIHANDLE hRec = MsiCreateRecord(3); + // To show a message from Error table, record's Field 0 must be null + MsiRecordSetInteger(hRec, 1, 25000); + MsiRecordSetStringW(hRec, 2, sKBNo.c_str()); + std::string s("\n"); + s += sMessage; + MsiRecordSetStringA(hRec, 3, s.c_str()); + MsiProcessMessage(hInst, INSTALLMESSAGE_WARNING, hRec); +} + +// Set custom action description visible in progress dialog +void SetStatusText(MSIHANDLE hInst, const std::wstring& actName, const std::wstring& actDesc) +{ + PMSIHANDLE hRec = MsiCreateRecord(3); + // For INSTALLMESSAGE_ACTIONSTART, record's Field 0 must be null + // Field 1: Action name - must be non-null + MsiRecordSetStringW(hRec, 1, actName.c_str()); + // Field 2: Action description - displayed in dialog + MsiRecordSetStringW(hRec, 2, actDesc.c_str()); + // Let Field 3 stay null - no action template + MsiProcessMessage(hInst, INSTALLMESSAGE_ACTIONSTART, hRec); +} + +typedef std::unique_ptr<void, decltype(&CloseHandle)> CloseHandleGuard; +CloseHandleGuard Guard(HANDLE h) { return CloseHandleGuard(h, CloseHandle); } + +typedef std::unique_ptr<const wchar_t, decltype(&DeleteFileW)> DeleteFileGuard; +DeleteFileGuard Guard(const wchar_t* sFileName) { return DeleteFileGuard(sFileName, DeleteFileW); } + +typedef std::unique_ptr<SC_HANDLE__, decltype(&CloseServiceHandle)> CloseServiceHandleGuard; +CloseServiceHandleGuard Guard(SC_HANDLE h) +{ + return CloseServiceHandleGuard(h, CloseServiceHandle); +} + +std::wstring GetTempFile() +{ + wchar_t sPath[MAX_PATH + 1]; + DWORD nResult = GetTempPathW(sizeof(sPath) / sizeof(*sPath), sPath); + if (!nResult) + ThrowLastError("GetTempPathW"); + + wchar_t sFile[MAX_PATH + 1]; + nResult = GetTempFileNameW(sPath, L"TMP", 0, sFile); + if (!nResult) + ThrowLastError("GetTempFileNameW"); + return sFile; +} + +bool IsWow64Process() +{ +#if !defined _WIN64 + BOOL bResult = FALSE; + typedef BOOL(WINAPI * LPFN_ISWOW64PROCESS)(HANDLE, PBOOL); + LPFN_ISWOW64PROCESS fnIsWow64Process = reinterpret_cast<LPFN_ISWOW64PROCESS>( + GetProcAddress(GetModuleHandleW(L"kernel32"), "IsWow64Process")); + + if (fnIsWow64Process) + { + if (!fnIsWow64Process(GetCurrentProcess(), &bResult)) + ThrowLastError("IsWow64Process"); + } + + return bResult; +#else + return false; +#endif +} + +// This class uses MsiProcessMessage to check for user input: it returns IDCANCEL when user cancels +// installation. It throws a special exception, to be intercepted in main action function to return +// corresponding exit code. +class UserInputChecker +{ +public: + class eUserCancelled + { + }; + + UserInputChecker(MSIHANDLE hInstall) + : m_hInstall(hInstall) + , m_hProgressRec(MsiCreateRecord(3)) + { + // Use explicit progress messages + MsiRecordSetInteger(m_hProgressRec, 1, 1); + MsiRecordSetInteger(m_hProgressRec, 2, 1); + MsiRecordSetInteger(m_hProgressRec, 3, 0); + int nResult = MsiProcessMessage(m_hInstall, INSTALLMESSAGE_PROGRESS, m_hProgressRec); + if (nResult == IDCANCEL) + throw eUserCancelled(); + // Prepare the record to following progress update calls + MsiRecordSetInteger(m_hProgressRec, 1, 2); + MsiRecordSetInteger(m_hProgressRec, 2, 0); // step by 0 - don't move progress + MsiRecordSetInteger(m_hProgressRec, 3, 0); + } + + void ThrowIfUserCancelled() + { + // Check if user has cancelled + int nResult = MsiProcessMessage(m_hInstall, INSTALLMESSAGE_PROGRESS, m_hProgressRec); + if (nResult == IDCANCEL) + throw eUserCancelled(); + } + +private: + MSIHANDLE m_hInstall; + PMSIHANDLE m_hProgressRec; +}; + +// Checks if Windows Update service is disabled, and if it is, enables it temporarily. +// Also stops the service if it's currently running, because it seems that wusa.exe +// does not freeze when it starts the service itself. +class WUServiceEnabler +{ +public: + WUServiceEnabler(MSIHANDLE hInstall) + : mhInstall(hInstall) + , mhService(EnableWUService(hInstall)) + { + } + + ~WUServiceEnabler() + { + try + { + if (mhService) + { + EnsureServiceEnabled(mhInstall, mhService.get(), false); + StopService(mhInstall, mhService.get(), false); + } + } + catch (std::exception& e) + { + WriteLog(mhInstall, e.what()); + } + } + +private: + static CloseServiceHandleGuard EnableWUService(MSIHANDLE hInstall) + { + auto hSCM = Guard(OpenSCManagerW(nullptr, nullptr, SC_MANAGER_ALL_ACCESS)); + if (!hSCM) + ThrowLastError("OpenSCManagerW"); + WriteLog(hInstall, "Opened service control manager"); + + auto hService = Guard(OpenServiceW(hSCM.get(), L"wuauserv", + SERVICE_QUERY_CONFIG | SERVICE_CHANGE_CONFIG + | SERVICE_QUERY_STATUS | SERVICE_STOP)); + if (!hService) + ThrowLastError("OpenServiceW"); + WriteLog(hInstall, "Obtained WU service handle"); + + const DWORD nCurrentStatus = ServiceStatus(hInstall, hService.get()); + // Stop currently running service to prevent wusa.exe from hanging trying to detect if the + // update is applicable (sometimes this freezes it ~indefinitely; it seems that it doesn't + // happen if wusa.exe starts the service itself: https://superuser.com/questions/1044528/). + // tdf#124794: Wait for service to stop. + if (nCurrentStatus == SERVICE_RUNNING) + StopService(hInstall, hService.get(), true); + + if (nCurrentStatus == SERVICE_RUNNING + || !EnsureServiceEnabled(hInstall, hService.get(), true)) + { + // No need to restore anything back, since we didn't change config + hService.reset(); + WriteLog(hInstall, "Service configuration is unchanged"); + } + + return hService; + } + + // Returns if the service configuration was actually changed + static bool EnsureServiceEnabled(MSIHANDLE hInstall, SC_HANDLE hService, bool bEnabled) + { + bool bConfigChanged = false; + + DWORD nCbRequired = 0; + if (!QueryServiceConfigW(hService, nullptr, 0, &nCbRequired)) + { + if (DWORD nError = GetLastError(); nError != ERROR_INSUFFICIENT_BUFFER) + ThrowWin32Error("QueryServiceConfigW", nError); + } + std::unique_ptr<char[]> pBuf(new char[nCbRequired]); + LPQUERY_SERVICE_CONFIGW pConfig = reinterpret_cast<LPQUERY_SERVICE_CONFIGW>(pBuf.get()); + if (!QueryServiceConfigW(hService, pConfig, nCbRequired, &nCbRequired)) + ThrowLastError("QueryServiceConfigW"); + WriteLog(hInstall, "Obtained service config"); + + DWORD eNewStartType = 0; + if (bEnabled && pConfig->dwStartType == SERVICE_DISABLED) + { + bConfigChanged = true; + eNewStartType = SERVICE_DEMAND_START; + WriteLog(hInstall, "Service is disabled, and requested to enable"); + } + else if (!bEnabled && pConfig->dwStartType != SERVICE_DISABLED) + { + bConfigChanged = true; + eNewStartType = SERVICE_DISABLED; + WriteLog(hInstall, "Service is enabled, and requested to disable"); + } + + if (bConfigChanged) + { + if (!ChangeServiceConfigW(hService, SERVICE_NO_CHANGE, eNewStartType, SERVICE_NO_CHANGE, + nullptr, nullptr, nullptr, nullptr, nullptr, nullptr, + nullptr)) + ThrowLastError("ChangeServiceConfigW"); + WriteLog(hInstall, "WU service config successfully changed"); + } + else + WriteLog(hInstall, "No need to modify service config"); + + return bConfigChanged; + } + + static DWORD ServiceStatus(MSIHANDLE hInstall, SC_HANDLE hService) + { + SERVICE_STATUS aServiceStatus{}; + if (!QueryServiceStatus(hService, &aServiceStatus)) + ThrowLastError("QueryServiceStatus"); + + std::string sStatus; + switch (aServiceStatus.dwCurrentState) + { + case SERVICE_STOPPED: + sStatus = "SERVICE_STOPPED"; + break; + case SERVICE_START_PENDING: + sStatus = "SERVICE_START_PENDING"; + break; + case SERVICE_STOP_PENDING: + sStatus = "SERVICE_STOP_PENDING"; + break; + case SERVICE_RUNNING: + sStatus = "SERVICE_RUNNING"; + break; + case SERVICE_CONTINUE_PENDING: + sStatus = "SERVICE_CONTINUE_PENDING"; + break; + case SERVICE_PAUSE_PENDING: + sStatus = "SERVICE_PAUSE_PENDING"; + break; + case SERVICE_PAUSED: + sStatus = "SERVICE_PAUSED"; + break; + default: + sStatus = Num2Hex(aServiceStatus.dwCurrentState); + } + WriteLog(hInstall, "Service status is", sStatus); + + return aServiceStatus.dwCurrentState; + } + + static void StopService(MSIHANDLE hInstall, SC_HANDLE hService, bool bWait) + { + try + { + if (ServiceStatus(hInstall, hService) != SERVICE_STOPPED) + { + SERVICE_STATUS aServiceStatus{}; + if (!ControlService(hService, SERVICE_CONTROL_STOP, &aServiceStatus)) + ThrowLastError("ControlService"); + WriteLog(hInstall, + "Successfully sent SERVICE_CONTROL_STOP code to Windows Update service"); + if (aServiceStatus.dwCurrentState != SERVICE_STOPPED && bWait) + { + // Let user cancel too long wait + UserInputChecker aInputChecker(hInstall); + // aServiceStatus.dwWaitHint is unreasonably high for Windows Update (30000), + // so don't use it, but simply poll service status each second + for (int nWait = 0; nWait < 30; ++nWait) // arbitrary limit of 30 s + { + for (int i = 0; i < 2; ++i) // check user input twice a second + { + Sleep(500); + aInputChecker.ThrowIfUserCancelled(); + } + + if (!QueryServiceStatus(hService, &aServiceStatus)) + ThrowLastError("QueryServiceStatus"); + + if (aServiceStatus.dwCurrentState == SERVICE_STOPPED) + break; + } + } + if (aServiceStatus.dwCurrentState == SERVICE_STOPPED) + WriteLog(hInstall, "Successfully stopped Windows Update service"); + else if (bWait) + WriteLog(hInstall, "Wait for Windows Update stop timed out - proceeding"); + } + else + WriteLog(hInstall, "Windows Update service is not running"); + } + catch (std::exception& e) + { + WriteLog(hInstall, e.what()); + } + } + + MSIHANDLE mhInstall; + CloseServiceHandleGuard mhService; +}; +} + +// Immediate action "unpack_msu" that has access to installation database and properties; checks +// "InstMSUBinary" property and unpacks the binary with that name to a temporary file; sets +// "cleanup_msu" and "inst_msu" properties to the full name of the extracted temporary file. These +// properties will become "CustomActionData" property inside relevant deferred actions. +extern "C" __declspec(dllexport) UINT __stdcall UnpackMSUForInstall(MSIHANDLE hInstall) +{ + try + { + sLogPrefix = "UnpackMSUForInstall:"; + WriteLog(hInstall, "started"); + + WriteLog(hInstall, "Checking value of InstMSUBinary"); + wchar_t sInstMSUBinary[MAX_PATH + 10]; + DWORD nCCh = sizeof(sInstMSUBinary) / sizeof(*sInstMSUBinary); + CheckWin32Error("MsiGetPropertyW", + MsiGetPropertyW(hInstall, L"InstMSUBinary", sInstMSUBinary, &nCCh)); + WriteLog(hInstall, "Got InstMSUBinary value:", + sInstMSUBinary); // KB2999226|Windows61-KB2999226-x64msu + const wchar_t* sBinaryName = wcschr(sInstMSUBinary, L'|'); + if (!sBinaryName) + throw std::exception("No KB number in InstMSUBinary!"); + // "KB2999226" + const std::wstring sKBNo(sInstMSUBinary, sBinaryName - sInstMSUBinary); + ++sBinaryName; + + PMSIHANDLE hDatabase = MsiGetActiveDatabase(hInstall); + if (!hDatabase) + ThrowLastError("MsiGetActiveDatabase"); + WriteLog(hInstall, "MsiGetActiveDatabase succeeded"); + + std::wstringstream sQuery; + sQuery << "SELECT `Data` FROM `Binary` WHERE `Name`='" << sBinaryName << "'"; + + PMSIHANDLE hBinaryView; + CheckWin32Error("MsiDatabaseOpenViewW", + MsiDatabaseOpenViewW(hDatabase, sQuery.str().c_str(), &hBinaryView)); + WriteLog(hInstall, "MsiDatabaseOpenViewW succeeded"); + + CheckWin32Error("MsiViewExecute", MsiViewExecute(hBinaryView, 0)); + WriteLog(hInstall, "MsiViewExecute succeeded"); + + PMSIHANDLE hBinaryRecord; + CheckWin32Error("MsiViewFetch", MsiViewFetch(hBinaryView, &hBinaryRecord)); + WriteLog(hInstall, "MsiViewFetch succeeded"); + + const std::wstring sBinary = GetTempFile(); + auto aDeleteFileGuard(Guard(sBinary.c_str())); + WriteLog(hInstall, "Temp file path:", sBinary.c_str()); + + CheckWin32Error("MsiSetPropertyW", + MsiSetPropertyW(hInstall, L"cleanup_msu", sBinary.c_str())); + + { + HANDLE hFile = CreateFileW(sBinary.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, + FILE_ATTRIBUTE_NORMAL, nullptr); + if (hFile == INVALID_HANDLE_VALUE) + ThrowLastError("CreateFileW"); + auto aFileHandleGuard(Guard(hFile)); + + const DWORD nBufSize = 1024 * 1024; + std::unique_ptr<char[]> buf(new char[nBufSize]); + DWORD nTotal = 0; + DWORD nRead; + do + { + nRead = nBufSize; + CheckWin32Error("MsiRecordReadStream", + MsiRecordReadStream(hBinaryRecord, 1, buf.get(), &nRead)); + + if (nRead > 0) + { + DWORD nWritten; + if (!WriteFile(hFile, buf.get(), nRead, &nWritten, nullptr)) + ThrowLastError("WriteFile"); + nTotal += nWritten; + } + } while (nRead == nBufSize); + + WriteLog(hInstall, "Successfully wrote", Num2Dec(nTotal), "bytes"); + } + const std::wstring s_inst_msu = sKBNo + L"|" + sBinary; + CheckWin32Error("MsiSetPropertyW", + MsiSetPropertyW(hInstall, L"inst_msu", s_inst_msu.c_str())); + + // Don't delete the file: it will be done by following actions (inst_msu or cleanup_msu) + (void)aDeleteFileGuard.release(); + return ERROR_SUCCESS; + } + catch (std::exception& e) + { + WriteLog(hInstall, e.what()); + } + return ERROR_INSTALL_FAILURE; +} + +// Deferred action "inst_msu" that must be run from system account. Receives the tempfile name from +// "CustomActionData" property, and runs wusa.exe to install it. Waits for it and checks exit code. +extern "C" __declspec(dllexport) UINT __stdcall InstallMSU(MSIHANDLE hInstall) +{ + std::wstring sKBNo; // "KB2999226" + try + { + sLogPrefix = "InstallMSU:"; + WriteLog(hInstall, "started"); + + WriteLog(hInstall, "Checking value of CustomActionData"); + wchar_t sCustomActionData[MAX_PATH + 10]; // "KB2999226|C:\Temp\binary.tmp" + DWORD nCCh = sizeof(sCustomActionData) / sizeof(*sCustomActionData); + CheckWin32Error("MsiGetPropertyW", + MsiGetPropertyW(hInstall, L"CustomActionData", sCustomActionData, &nCCh)); + WriteLog(hInstall, "Got CustomActionData value:", sCustomActionData); + const wchar_t* sBinaryName = wcschr(sCustomActionData, L'|'); + if (!sBinaryName) + throw std::exception("No KB number in CustomActionData!"); + sKBNo = std::wstring(sCustomActionData, sBinaryName - sCustomActionData); + ++sBinaryName; + auto aDeleteFileGuard(Guard(sBinaryName)); + + SetStatusText(hInstall, L"WU service state check", + L"Checking Windows Update service state"); + + // In case the Windows Update service is disabled, we temporarily enable it here. We also + // stop running WU service, to avoid wusa.exe freeze (see comment in EnableWUService). + WUServiceEnabler aWUServiceEnabler(hInstall); + + SetStatusText(hInstall, sKBNo + L" installation", L"Installing " + sKBNo); + + const bool bWow64Process = IsWow64Process(); + WriteLog(hInstall, "Is Wow64 Process:", bWow64Process ? "YES" : "NO"); + std::wstring sWUSAPath = bWow64Process ? GetKnownFolder(FOLDERID_Windows) + L"\\SysNative" + : GetKnownFolder(FOLDERID_System); + sWUSAPath += L"\\wusa.exe"; + WriteLog(hInstall, "Prepared wusa path:", sWUSAPath); + + std::wstring sWUSACmd + = L"\"" + sWUSAPath + L"\" \"" + sBinaryName + L"\" /quiet /norestart"; + WriteLog(hInstall, "Prepared wusa command:", sWUSACmd); + + STARTUPINFOW si{}; + si.cb = sizeof(si); + PROCESS_INFORMATION pi{}; + if (!CreateProcessW(sWUSAPath.c_str(), const_cast<LPWSTR>(sWUSACmd.c_str()), nullptr, + nullptr, FALSE, CREATE_NO_WINDOW, nullptr, nullptr, &si, &pi)) + ThrowLastError("CreateProcessW"); + CloseHandle(pi.hThread); + auto aCloseProcHandleGuard(Guard(pi.hProcess)); + WriteLog(hInstall, "CreateProcessW succeeded"); + + { + // This block waits when the started wusa.exe process finishes. Since it's possible + // for wusa.exe in some circumstances to wait really long (indefinitely?), we check + // for user input here. + UserInputChecker aInputChecker(hInstall); + for (;;) + { + DWORD nWaitResult = WaitForSingleObject(pi.hProcess, 500); + if (nWaitResult == WAIT_OBJECT_0) + break; // wusa.exe finished + else if (nWaitResult == WAIT_TIMEOUT) + aInputChecker.ThrowIfUserCancelled(); + else + ThrowWin32Error("WaitForSingleObject", nWaitResult); + } + } + + DWORD nExitCode = 0; + if (!GetExitCodeProcess(pi.hProcess, &nExitCode)) + ThrowLastError("GetExitCodeProcess"); + + HRESULT hr = static_cast<HRESULT>(nExitCode); + + // HRESULT_FROM_WIN32 is defined as an inline function in SDK 8.1 without the constexpr + // And it won't work to place it inside the switch statement. + if (HRESULT_FROM_WIN32(ERROR_SUCCESS_REBOOT_REQUIRED) == hr) + { + hr = ERROR_SUCCESS_REBOOT_REQUIRED; + } + + switch (hr) + { + case S_OK: + case WU_S_ALREADY_INSTALLED: + case WU_E_NOT_APPLICABLE: // Windows could lie us about its version, etc. + case ERROR_SUCCESS_REBOOT_REQUIRED: + case WU_S_REBOOT_REQUIRED: + WriteLog(hInstall, "wusa.exe succeeded with exit code", Num2Hex(nExitCode)); + return ERROR_SUCCESS; + + default: + ThrowHResult("Execution of wusa.exe", hr); + } + } + catch (const UserInputChecker::eUserCancelled&) + { + return ERROR_INSTALL_USEREXIT; + } + catch (std::exception& e) + { + WriteLog(hInstall, e.what()); + ShowWarning(hInstall, sKBNo, e.what()); + } + return ERROR_SUCCESS; // Do not break on MSU installation errors +} + +// Rollback deferred action "cleanup_msu" that is executed on error or cancel. +// It removes the temporary file created by UnpackMSUForInstall action. +// MUST be placed IMMEDIATELY AFTER "unpack_msu" in execute sequence. +extern "C" __declspec(dllexport) UINT __stdcall CleanupMSU(MSIHANDLE hInstall) +{ + try + { + sLogPrefix = "CleanupMSU:"; + WriteLog(hInstall, "started"); + + WriteLog(hInstall, "Checking value of CustomActionData"); + wchar_t sBinaryName[MAX_PATH + 1]; + DWORD nCCh = sizeof(sBinaryName) / sizeof(*sBinaryName); + CheckWin32Error("MsiGetPropertyW", + MsiGetPropertyW(hInstall, L"CustomActionData", sBinaryName, &nCCh)); + WriteLog(hInstall, "Got CustomActionData value:", sBinaryName); + + if (!DeleteFileW(sBinaryName)) + { + if (DWORD nError = GetLastError(); nError != ERROR_FILE_NOT_FOUND) + ThrowWin32Error("DeleteFileW", nError); + } + WriteLog(hInstall, "File successfully removed"); + } + catch (std::exception& e) + { + WriteLog(hInstall, e.what()); + } + // Always return success - we don't want rollback to fail. + return ERROR_SUCCESS; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/setup_native/source/win32/customactions/inst_msu/inst_msu_msi.def b/setup_native/source/win32/customactions/inst_msu/inst_msu_msi.def new file mode 100644 index 000000000..49ade9c01 --- /dev/null +++ b/setup_native/source/win32/customactions/inst_msu/inst_msu_msi.def @@ -0,0 +1,5 @@ +LIBRARY "inst_msu_msi.dll" +EXPORTS + UnpackMSUForInstall + InstallMSU + CleanupMSU
\ No newline at end of file diff --git a/setup_native/source/win32/customactions/quickstarter/qslnkmsi.def b/setup_native/source/win32/customactions/quickstarter/qslnkmsi.def new file mode 100644 index 000000000..f50a3a396 --- /dev/null +++ b/setup_native/source/win32/customactions/quickstarter/qslnkmsi.def @@ -0,0 +1,3 @@ +LIBRARY "qslnkmsi.dll" +EXPORTS + RemoveQuickstarterLink
\ No newline at end of file diff --git a/setup_native/source/win32/customactions/quickstarter/quickstarter.cxx b/setup_native/source/win32/customactions/quickstarter/quickstarter.cxx new file mode 100644 index 000000000..960274f39 --- /dev/null +++ b/setup_native/source/win32/customactions/quickstarter/quickstarter.cxx @@ -0,0 +1,131 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "quickstarter.hxx" + +#include <psapi.h> + +#include <malloc.h> + +std::wstring GetOfficeInstallationPathW(MSIHANDLE handle) +{ + std::wstring progpath; + DWORD sz = 0; + PWSTR dummy = const_cast<PWSTR>(L""); + + if (MsiGetPropertyW(handle, L"INSTALLLOCATION", dummy, &sz) == ERROR_MORE_DATA) + { + sz++; // space for the final '\0' + DWORD nbytes = sz * sizeof(WCHAR); + PWSTR buff = static_cast<PWSTR>(_alloca(nbytes)); + ZeroMemory(buff, nbytes); + MsiGetPropertyW(handle, L"INSTALLLOCATION", buff, &sz); + progpath = buff; + } + return progpath; +} + +std::wstring GetOfficeProductNameW(MSIHANDLE handle) +{ + std::wstring productname; + DWORD sz = 0; + PWSTR dummy = const_cast<PWSTR>(L""); + + if (MsiGetPropertyW(handle, L"ProductName", dummy, &sz) == ERROR_MORE_DATA) + { + sz++; // space for the final '\0' + DWORD nbytes = sz * sizeof(WCHAR); + PWSTR buff = static_cast<PWSTR>(_alloca(nbytes)); + ZeroMemory(buff, nbytes); + MsiGetPropertyW(handle, L"ProductName", buff, &sz); + productname = buff; + } + return productname; +} + +std::wstring GetQuickstarterLinkNameW(MSIHANDLE handle) +{ + std::wstring quickstarterlinkname; + DWORD sz = 0; + PWSTR dummy = const_cast<PWSTR>(L""); + + if (MsiGetPropertyW(handle, L"Quickstarterlinkname", dummy, &sz) == ERROR_MORE_DATA) + { + sz++; // space for the final '\0' + DWORD nbytes = sz * sizeof(WCHAR); + PWSTR buff = static_cast<PWSTR>(_alloca(nbytes)); + ZeroMemory(buff, nbytes); + MsiGetPropertyW(handle, L"Quickstarterlinkname", buff, &sz); + quickstarterlinkname = buff; + } + else if (MsiGetPropertyW(handle, L"ProductName", dummy, &sz) == ERROR_MORE_DATA) + { + sz++; // space for the final '\0' + DWORD nbytes = sz * sizeof(WCHAR); + PWSTR buff = static_cast<PWSTR>(_alloca(nbytes)); + ZeroMemory(buff, nbytes); + MsiGetPropertyW(handle, L"ProductName", buff, &sz); + quickstarterlinkname = buff; + } + return quickstarterlinkname; +} + +static bool IsValidHandle( HANDLE handle ) +{ + return nullptr != handle && INVALID_HANDLE_VALUE != handle; +} + +static DWORD WINAPI GetModuleFileNameExW_( HANDLE hProcess, HMODULE hModule, PWSTR lpFileName, DWORD nSize ) +{ + static auto lpProc = []() { + HMODULE hLibrary = LoadLibraryW(L"PSAPI.DLL"); + decltype(GetModuleFileNameExW)* pRet = nullptr; + if (hLibrary) + pRet = reinterpret_cast<decltype(GetModuleFileNameExW)*>( + GetProcAddress(hLibrary, "GetModuleFileNameExW")); + return pRet; + }(); + + if ( lpProc ) + return lpProc( hProcess, hModule, lpFileName, nSize ); + + return 0; + +} + +std::wstring GetProcessImagePathW( DWORD dwProcessId ) +{ + std::wstring sImagePath; + + HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, dwProcessId ); + + if ( IsValidHandle( hProcess ) ) + { + WCHAR szPathBuffer[MAX_PATH] = L""; + + if ( GetModuleFileNameExW_( hProcess, nullptr, szPathBuffer, sizeof(szPathBuffer)/sizeof(szPathBuffer[0]) ) ) + sImagePath = szPathBuffer; + + CloseHandle( hProcess ); + } + + return sImagePath; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/setup_native/source/win32/customactions/quickstarter/quickstarter.hxx b/setup_native/source/win32/customactions/quickstarter/quickstarter.hxx new file mode 100644 index 000000000..2370f4691 --- /dev/null +++ b/setup_native/source/win32/customactions/quickstarter/quickstarter.hxx @@ -0,0 +1,36 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_SETUP_NATIVE_SOURCE_WIN32_CUSTOMACTIONS_QUICKSTARTER_QUICKSTARTER_HXX +#define INCLUDED_SETUP_NATIVE_SOURCE_WIN32_CUSTOMACTIONS_QUICKSTARTER_QUICKSTARTER_HXX + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <msiquery.h> + +#include <string> + +std::wstring GetOfficeInstallationPathW(MSIHANDLE handle); +std::wstring GetOfficeProductNameW(MSIHANDLE handle); +std::wstring GetQuickstarterLinkNameW(MSIHANDLE handle); +std::wstring GetProcessImagePathW(DWORD dwProcessId); + +#endif // INCLUDED_SETUP_NATIVE_SOURCE_WIN32_CUSTOMACTIONS_QUICKSTARTER_QUICKSTARTER_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/setup_native/source/win32/customactions/quickstarter/remove_quickstart_link.cxx b/setup_native/source/win32/customactions/quickstarter/remove_quickstart_link.cxx new file mode 100644 index 000000000..68faed45a --- /dev/null +++ b/setup_native/source/win32/customactions/quickstarter/remove_quickstart_link.cxx @@ -0,0 +1,42 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "quickstarter.hxx" + +#include <shlobj.h> + +extern "C" __declspec(dllexport) UINT __stdcall RemoveQuickstarterLink( MSIHANDLE hMSI ) +{ + WCHAR szStartupPath[MAX_PATH]; + + if ( SHGetSpecialFolderPathW( nullptr, szStartupPath, CSIDL_STARTUP, FALSE ) ) + { + std::wstring sQuickstartLinkPath = szStartupPath; + + sQuickstartLinkPath += L"\\"; + sQuickstartLinkPath += GetQuickstarterLinkNameW( hMSI ); + sQuickstartLinkPath += L".lnk"; + + DeleteFileW( sQuickstartLinkPath.c_str() ); + } + + return ERROR_SUCCESS; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/setup_native/source/win32/customactions/quickstarter/sdqsmsi.def b/setup_native/source/win32/customactions/quickstarter/sdqsmsi.def new file mode 100644 index 000000000..c8df896e5 --- /dev/null +++ b/setup_native/source/win32/customactions/quickstarter/sdqsmsi.def @@ -0,0 +1,3 @@ +LIBRARY "sdqsmsi.dll" +EXPORTS + ShutDownQuickstarter
\ No newline at end of file diff --git a/setup_native/source/win32/customactions/quickstarter/shutdown_quickstart.cxx b/setup_native/source/win32/customactions/quickstarter/shutdown_quickstart.cxx new file mode 100644 index 000000000..c93b000e4 --- /dev/null +++ b/setup_native/source/win32/customactions/quickstarter/shutdown_quickstart.cxx @@ -0,0 +1,73 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "quickstarter.hxx" + +#include <systools/win32/qswin32.h> + +static BOOL CALLBACK EnumWindowsProc( HWND hWnd, LPARAM lParam ) +{ + MSIHANDLE hMSI = static_cast< MSIHANDLE >( lParam ); + WCHAR szClassName[sizeof(QUICKSTART_CLASSNAME)/sizeof(WCHAR) + 1]; + + int nCharsCopied = GetClassNameW( hWnd, szClassName, sizeof(szClassName)/sizeof(szClassName[0]) ); + + if ( nCharsCopied && !_wcsicmp( QUICKSTART_CLASSNAME, szClassName ) ) + { + DWORD dwProcessId; + + if ( GetWindowThreadProcessId( hWnd, &dwProcessId ) ) + { + std::wstring sImagePath = GetProcessImagePathW( dwProcessId ); + std::wstring sOfficeImageDir = GetOfficeInstallationPathW( hMSI ) + L"program\\"; + + if ( !_wcsnicmp( sImagePath.c_str(), sOfficeImageDir.c_str(), sOfficeImageDir.length() ) ) + { + UINT uMsgShutdownQuickstart = RegisterWindowMessageW( SHUTDOWN_QUICKSTART_MESSAGE ); + + if ( uMsgShutdownQuickstart ) + SendMessageW( hWnd, uMsgShutdownQuickstart, 0, 0 ); + + + HANDLE hProcess = OpenProcess( SYNCHRONIZE, FALSE, dwProcessId ); + + if ( hProcess ) + { + WaitForSingleObject( hProcess, 30000 ); // Wait at most 30 seconds for process to terminate + CloseHandle( hProcess ); + } + + return FALSE; + } + + } + } + + return TRUE; +} + + +extern "C" __declspec(dllexport) UINT __stdcall ShutDownQuickstarter( MSIHANDLE hMSI ) +{ + EnumWindows( EnumWindowsProc, hMSI ); + + return ERROR_SUCCESS; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/setup_native/source/win32/customactions/reg4allmsdoc/reg4allmsdoc.def b/setup_native/source/win32/customactions/reg4allmsdoc/reg4allmsdoc.def new file mode 100644 index 000000000..cea8b4699 --- /dev/null +++ b/setup_native/source/win32/customactions/reg4allmsdoc/reg4allmsdoc.def @@ -0,0 +1,6 @@ +LIBRARY "reg4allmsdoc.dll" +EXPORTS + FindRegisteredExtensions + LookForRegisteredExtensions + RegisterSomeExtensions + RestoreRegAllMSDoc
\ No newline at end of file diff --git a/setup_native/source/win32/customactions/reg4allmsdoc/reg4allmsi.cxx b/setup_native/source/win32/customactions/reg4allmsdoc/reg4allmsi.cxx new file mode 100644 index 000000000..1a2e82eb7 --- /dev/null +++ b/setup_native/source/win32/customactions/reg4allmsdoc/reg4allmsi.cxx @@ -0,0 +1,519 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <msiquery.h> + +#include <malloc.h> +#include <string> +#include <strsafe.h> + +static const WCHAR* g_Extensions[] = +{ + L".doc", // Microsoft Word Text [0] + L".dot", // Microsoft Word Template + L".wps", // Kingsoft Writer Document + L".wpt", // Kingsoft Writer Template + L".rtf", // rtf text + L".docx", // Office Word 2007 XML document + L".docm", // Office Word 2007 XML macro-enabled document + L".dotx", // Office Word 2007 XML template + L".dotm", // Office Word 2007 XML macro-enabled template + L".xlw", // Microsoft Excel + L".xls", // Microsoft Excel + L".xlt", // Microsoft Excel Template + L".xlsx", // Office Excel 2007 XML workbook + L".xlsm", // Office Excel 2007 XML macro-enabled workbook + L".xltx", // Office Excel 2007 XML template + L".xltm", // Office Excel 2007 XML macro-enabled template + L".xlsb", // Office Excel 2007 binary workbook (BIFF12) + L".iqy", // Microsoft Excel Web Query File + L".et", // Kingsoft Spreadsheet + L".ett", // Kingsoft SpreadSheet Template + L".ppt", // Microsoft Powerpoint + L".pps", // Microsoft Powerpoint + L".pot", // Microsoft Powerpoint Template + L".pptx", // Office PowerPoint 2007 XML presentation + L".pptm", // Office PowerPoint 2007 macro-enabled XML presentation + L".potx", // Office PowerPoint 2007 XML template + L".potm", // Office PowerPoint 2007 macro-enabled XML template + L".ppsx", // Office PowerPoint 2007 XML show + L".dps", // Kingsoft Presentation + L".dpt", // Kingsoft Presentation Template + L".vsd", // Visio 2000/XP/2003 document + L".vst", // Visio 2000/XP/2003 template + nullptr +}; + +static const int WORD_START = 0; +static const int EXCEL_START = 9; +static const int POWERPOINT_START = 20; +static const int VISIO_START = 30; +static const int VISIO_END = 32; + +// ".xlam", // Office Excel 2007 XML macro-enabled add-in +// ".ppam", // Office PowerPoint 2007 macro-enabled XML add-in +// ".ppsm", // Office PowerPoint 2007 macro-enabled XML show + +#ifdef DEBUG +inline void OutputDebugStringFormatW( LPCWSTR pFormat, ... ) +{ + WCHAR buffer[1024]; + va_list args; + + va_start( args, pFormat ); + StringCchVPrintfW( buffer, sizeof(buffer)/sizeof(*buffer), pFormat, args ); + OutputDebugStringW( buffer ); + va_end(args); +} +#else +static void OutputDebugStringFormatW( LPCWSTR, ... ) +{ +} +#endif + +static bool CheckExtensionInRegistry( LPCWSTR lpSubKey ) +{ + bool bRet = false; + HKEY hKey = nullptr; + LONG lResult = RegOpenKeyExW( HKEY_CLASSES_ROOT, lpSubKey, 0, KEY_QUERY_VALUE, &hKey ); + + if ( ERROR_SUCCESS == lResult ) + { + WCHAR szBuffer[1024]; + DWORD nSize = sizeof( szBuffer ); + + lResult = RegQueryValueExW( hKey, L"", nullptr, nullptr, reinterpret_cast<LPBYTE>(szBuffer), &nSize ); + if ( ERROR_SUCCESS == lResult && nSize > 0 ) + { + szBuffer[nSize/sizeof(*szBuffer)] = L'\0'; + OutputDebugStringFormatW( L"Found value [%s] for key [%s].\n", szBuffer, lpSubKey ); + + if ( wcsncmp( szBuffer, L"WordPad.Document.1", 18 ) == 0 ) + { // We will replace registration for WordPad (alas, on XP only) FIXME + bRet = true; + } + else if ( wcsncmp( szBuffer, L"LibreOffice.", 12 ) == 0 ) + { // We will replace registration for our own types, too + bRet = true; + } + else if ( wcsncmp( szBuffer, L"lostub.", 7 ) == 0 ) + { // We will replace registration for lostub, too + bRet = true; + } + else // we have a default value -> do not register, see fdo#39791 + bRet = false; + } + else // no default value found -> return TRUE to register for that key + bRet = true; + + RegCloseKey( hKey ); + } + else // no key found -> return TRUE to register for that key + bRet = true; + + return bRet; +} + +static bool GetMsiPropW( MSIHANDLE handle, LPCWSTR name, /*out*/std::wstring& value ) +{ + DWORD sz = 0; + LPWSTR dummy = const_cast<LPWSTR>(L""); + if (MsiGetPropertyW(handle, name, dummy, &sz) == ERROR_MORE_DATA) + { + sz++; + DWORD nbytes = sz * sizeof(WCHAR); + LPWSTR buff = static_cast<LPWSTR>(_alloca(nbytes)); + ZeroMemory(buff, nbytes); + MsiGetPropertyW(handle, name, buff, &sz); + value = buff; + return true; + } + return false; +} + +static bool IsSetMsiPropW( MSIHANDLE handle, LPCWSTR name ) +{ + std::wstring val; + GetMsiPropW( handle, name, val ); + return (val == L"1"); +} + +static void registerForExtension( MSIHANDLE handle, const int nIndex, bool bRegister ) +{ + WCHAR sPropName[256]; + StringCchCopyW( sPropName, 256, L"REGISTER_" ); + StringCchCatW( sPropName, 256, (g_Extensions[nIndex])+1 ); + CharUpperBuffW( sPropName+9, 4 ); + + if ( bRegister ) { + MsiSetPropertyW( handle, sPropName, L"1" ); + OutputDebugStringFormatW( L"Set MSI property %s.\n", sPropName ); + } else { + MsiSetPropertyW( handle, sPropName, L"0" ); + OutputDebugStringFormatW( L"Unset MSI property %s.\n", sPropName ); + } +} + +static void saveOldRegistration( LPCWSTR lpSubKey ) +{ + HKEY hKey = nullptr; + LONG lResult = RegOpenKeyExW( HKEY_CLASSES_ROOT, lpSubKey, 0, + KEY_QUERY_VALUE|KEY_SET_VALUE, &hKey ); + + if ( ERROR_SUCCESS == lResult ) + { + WCHAR szBuffer[1024]; + DWORD nSize = sizeof( szBuffer ); + + lResult = RegQueryValueExW( hKey, L"", nullptr, nullptr, reinterpret_cast<LPBYTE>(szBuffer), &nSize ); + if ( ERROR_SUCCESS == lResult ) + { + szBuffer[nSize/sizeof(*szBuffer)] = L'\0'; + + // No need to save associations for our own types + if ( wcsncmp( szBuffer, L"LibreOffice.", 12 ) != 0 ) + { + // Save the old association + RegSetValueExW( hKey, L"LOBackupAssociation", 0, + REG_SZ, reinterpret_cast<LPBYTE>(szBuffer), nSize ); + // Also save what the old association means, just so we can try to verify + // if/when restoring it that the old application still exists + HKEY hKey2 = nullptr; + lResult = RegOpenKeyExW( HKEY_CLASSES_ROOT, szBuffer, 0, + KEY_QUERY_VALUE, &hKey2 ); + if ( ERROR_SUCCESS == lResult ) + { + nSize = sizeof( szBuffer ); + lResult = RegQueryValueExW( hKey2, L"", nullptr, nullptr, reinterpret_cast<LPBYTE>(szBuffer), &nSize ); + if ( ERROR_SUCCESS == lResult ) + { + RegSetValueExW( hKey, L"LOBackupAssociationDeref", 0, + REG_SZ, reinterpret_cast<LPBYTE>(szBuffer), nSize ); + } + RegCloseKey( hKey2 ); + } + } + } + RegCloseKey( hKey ); + } +} + +static void registerForExtensions( MSIHANDLE handle, bool bRegisterAll ) +{ // Check all file extensions + int nIndex = 0; + while ( g_Extensions[nIndex] != nullptr ) + { + saveOldRegistration( g_Extensions[nIndex] ); + + bool bRegister = bRegisterAll || CheckExtensionInRegistry( g_Extensions[nIndex] ); + if ( bRegister ) + registerForExtension( handle, nIndex, true ); + ++nIndex; + } +} + +static bool checkSomeExtensionInRegistry( const int nStart, const int nEnd ) +{ // Check all file extensions + int nIndex = nStart; + bool bFound = false; + + while ( !bFound && (nIndex < nEnd) && (g_Extensions[nIndex] != nullptr) ) + { + bFound = ! CheckExtensionInRegistry( g_Extensions[nIndex] ); + + if ( bFound ) + OutputDebugStringFormatW( L"Found registration for [%s].\n", g_Extensions[nIndex] ); + + ++nIndex; + } + return bFound; +} + +static void registerSomeExtensions( MSIHANDLE handle, const int nStart, const int nEnd, bool bRegister ) +{ // Check all file extensions + int nIndex = nStart; + + while ( (nIndex < nEnd) && (g_Extensions[nIndex] != nullptr) ) + { + registerForExtension( handle, nIndex++, bRegister ); + } +} + +extern "C" __declspec(dllexport) UINT __stdcall LookForRegisteredExtensions( MSIHANDLE handle ) +{ + OutputDebugStringFormatW( L"LookForRegisteredExtensions: " ); + + INSTALLSTATE current_state; + INSTALLSTATE future_state; + + bool bWriterEnabled = false; + bool bCalcEnabled = false; + bool bImpressEnabled = false; + bool bDrawEnabled = false; + bool bRegisterNone = IsSetMsiPropW( handle, L"REGISTER_NO_MSO_TYPES" ); + + if ( ( ERROR_SUCCESS == MsiGetFeatureStateW( handle, L"gm_p_Wrt", ¤t_state, &future_state ) ) && + ( (future_state == INSTALLSTATE_LOCAL) || ((current_state == INSTALLSTATE_LOCAL) && (future_state == INSTALLSTATE_UNKNOWN) ) ) ) + bWriterEnabled = true; + + OutputDebugStringFormatW( L"LookForRegisteredExtensions: Install state Writer is [%d], will be [%d]", current_state, future_state ); + if ( bWriterEnabled ) + OutputDebugStringFormatW( L"LookForRegisteredExtensions: Writer is enabled" ); + else + OutputDebugStringFormatW( L"LookForRegisteredExtensions: Writer is NOT enabled" ); + + if ( ( ERROR_SUCCESS == MsiGetFeatureStateW( handle, L"gm_p_Calc", ¤t_state, &future_state ) ) && + ( (future_state == INSTALLSTATE_LOCAL) || ((current_state == INSTALLSTATE_LOCAL) && (future_state == INSTALLSTATE_UNKNOWN) ) ) ) + bCalcEnabled = true; + + OutputDebugStringFormatW( L"LookForRegisteredExtensions: Install state Calc is [%d], will be [%d]", current_state, future_state ); + if ( bCalcEnabled ) + OutputDebugStringFormatW( L"LookForRegisteredExtensions: Calc is enabled" ); + else + OutputDebugStringFormatW( L"LookForRegisteredExtensions: Calc is NOT enabled" ); + + if ( ( ERROR_SUCCESS == MsiGetFeatureStateW( handle, L"gm_p_Impress", ¤t_state, &future_state ) ) && + ( (future_state == INSTALLSTATE_LOCAL) || ((current_state == INSTALLSTATE_LOCAL) && (future_state == INSTALLSTATE_UNKNOWN) ) ) ) + bImpressEnabled = true; + + OutputDebugStringFormatW( L"LookForRegisteredExtensions: Install state Impress is [%d], will be [%d]", current_state, future_state ); + if ( bImpressEnabled ) + OutputDebugStringFormatW( L"LookForRegisteredExtensions: Impress is enabled" ); + else + OutputDebugStringFormatW( L"LookForRegisteredExtensions: Impress is NOT enabled" ); + + if ( ( ERROR_SUCCESS == MsiGetFeatureStateW( handle, L"gm_p_Draw", ¤t_state, &future_state ) ) && + ( (future_state == INSTALLSTATE_LOCAL) || ((current_state == INSTALLSTATE_LOCAL) && (future_state == INSTALLSTATE_UNKNOWN) ) ) ) + bDrawEnabled = true; + + OutputDebugStringFormatW( L"LookForRegisteredExtensions: Install state Draw is [%d], will be [%d]", current_state, future_state ); + if ( bImpressEnabled ) + OutputDebugStringFormatW( L"LookForRegisteredExtensions: Draw is enabled" ); + else + OutputDebugStringFormatW( L"LookForRegisteredExtensions: Draw is NOT enabled" ); + + MsiSetPropertyW( handle, L"SELECT_WORD", L"" ); + MsiSetPropertyW( handle, L"SELECT_EXCEL", L"" ); + MsiSetPropertyW( handle, L"SELECT_POWERPOINT", L"" ); + MsiSetPropertyW( handle, L"SELECT_VISIO", L"" ); + + if ( ! bRegisterNone ) + { + if ( IsSetMsiPropW( handle, L"REGISTER_ALL_MSO_TYPES" ) ) + { + if ( bWriterEnabled ) + MsiSetPropertyW( handle, L"SELECT_WORD", L"1" ); + if ( bCalcEnabled ) + MsiSetPropertyW( handle, L"SELECT_EXCEL", L"1" ); + if ( bImpressEnabled ) + MsiSetPropertyW( handle, L"SELECT_POWERPOINT", L"1" ); + if ( bDrawEnabled ) + MsiSetPropertyW( handle, L"SELECT_VISIO", L"1" ); + } + else + { + if ( bWriterEnabled && ! checkSomeExtensionInRegistry( WORD_START, EXCEL_START ) ) + { + MsiSetPropertyW( handle, L"SELECT_WORD", L"1" ); + OutputDebugStringFormatW( L"LookForRegisteredExtensions: Register for Microsoft Word" ); + } + if ( bCalcEnabled && ! checkSomeExtensionInRegistry( EXCEL_START, POWERPOINT_START ) ) + { + MsiSetPropertyW( handle, L"SELECT_EXCEL", L"1" ); + OutputDebugStringFormatW( L"LookForRegisteredExtensions: Register for Microsoft Excel" ); + } + if ( bImpressEnabled && ! checkSomeExtensionInRegistry( POWERPOINT_START, VISIO_START ) ) + { + MsiSetPropertyW( handle, L"SELECT_POWERPOINT", L"1" ); + OutputDebugStringFormatW( L"LookForRegisteredExtensions: Register for Microsoft PowerPoint" ); + } + if ( bImpressEnabled && ! checkSomeExtensionInRegistry( VISIO_START, VISIO_END ) ) + { + MsiSetPropertyW( handle, L"SELECT_VISIO", L"1" ); + OutputDebugStringFormatW( L"LookForRegisteredExtensions: Register for Microsoft Visio" ); + } + } + } + + MsiSetPropertyW( handle, L"FILETYPEDIALOGUSED", L"1" ); + + return ERROR_SUCCESS; +} + +extern "C" __declspec(dllexport) UINT __stdcall RegisterSomeExtensions( MSIHANDLE handle ) +{ + OutputDebugStringFormatW( L"RegisterSomeExtensions: " ); + + if ( IsSetMsiPropW( handle, L"SELECT_WORD" ) ) + { + registerSomeExtensions( handle, WORD_START, EXCEL_START, true ); + MsiSetFeatureStateW( handle, L"gm_p_Wrt_MSO_Reg", INSTALLSTATE_LOCAL ); + OutputDebugStringFormatW( L"RegisterSomeExtensions: Register for Microsoft Word" ); + } + else + { + registerSomeExtensions( handle, WORD_START, EXCEL_START, false ); + MsiSetFeatureStateW( handle, L"gm_p_Wrt_MSO_Reg", INSTALLSTATE_ABSENT ); + } + + if ( IsSetMsiPropW( handle, L"SELECT_EXCEL" ) ) + { + registerSomeExtensions( handle, EXCEL_START, POWERPOINT_START, true ); + MsiSetFeatureStateW( handle, L"gm_p_Calc_MSO_Reg", INSTALLSTATE_LOCAL ); + OutputDebugStringFormatW( L"RegisterSomeExtensions: Register for Microsoft Excel" ); + } + else + { + registerSomeExtensions( handle, EXCEL_START, POWERPOINT_START, false ); + MsiSetFeatureStateW( handle, L"gm_p_Calc_MSO_Reg", INSTALLSTATE_ABSENT ); + } + + if ( IsSetMsiPropW( handle, L"SELECT_POWERPOINT" ) ) + { + registerSomeExtensions( handle, POWERPOINT_START, VISIO_START, true ); + MsiSetFeatureStateW( handle, L"gm_p_Impress_MSO_Reg", INSTALLSTATE_LOCAL ); + OutputDebugStringFormatW( L"RegisterSomeExtensions: Register for Microsoft PowerPoint" ); + } + else + { + registerSomeExtensions( handle, POWERPOINT_START, VISIO_START, false ); + MsiSetFeatureStateW( handle, L"gm_p_Impress_MSO_Reg", INSTALLSTATE_ABSENT ); + } + + if ( IsSetMsiPropW( handle, L"SELECT_VISIO" ) ) + { + registerSomeExtensions( handle, VISIO_START, VISIO_END, true ); + MsiSetFeatureStateW( handle, L"gm_p_Draw_MSO_Reg", INSTALLSTATE_LOCAL ); + OutputDebugStringFormatW( L"RegisterSomeExtensions: Register for Microsoft Visio" ); + } + else + { + registerSomeExtensions( handle, VISIO_START, VISIO_END, false ); + MsiSetFeatureStateW( handle, L"gm_p_Draw_MSO_Reg", INSTALLSTATE_ABSENT ); + } + return ERROR_SUCCESS; +} + +extern "C" __declspec(dllexport) UINT __stdcall FindRegisteredExtensions( MSIHANDLE handle ) +{ + if ( IsSetMsiPropW( handle, L"FILETYPEDIALOGUSED" ) ) + { + OutputDebugStringFormatW( L"FindRegisteredExtensions: FILETYPEDIALOGUSED!" ); + return ERROR_SUCCESS; + } + + OutputDebugStringFormatW( L"FindRegisteredExtensions:" ); + + bool bRegisterAll = IsSetMsiPropW( handle, L"REGISTER_ALL_MSO_TYPES" ); + + if ( IsSetMsiPropW( handle, L"REGISTER_NO_MSO_TYPES" ) ) + { + OutputDebugStringFormatW( L"FindRegisteredExtensions: Register none!" ); + return ERROR_SUCCESS; + } + else if ( bRegisterAll ) + OutputDebugStringFormatW( L"FindRegisteredExtensions: Force all on" ); + else + OutputDebugStringFormatW( L"FindRegisteredExtensions: " ); + + // setting the msi properties SELECT_* will force registering for all corresponding + // file types + if ( IsSetMsiPropW( handle, L"SELECT_WORD" ) ) + registerSomeExtensions( handle, WORD_START, EXCEL_START, true ); + if ( IsSetMsiPropW( handle, L"SELECT_EXCEL" ) ) + registerSomeExtensions( handle, EXCEL_START, POWERPOINT_START, true ); + if ( IsSetMsiPropW( handle, L"SELECT_POWERPOINT" ) ) + registerSomeExtensions( handle, POWERPOINT_START, VISIO_START, true ); + if ( IsSetMsiPropW( handle, L"SELECT_VISIO" ) ) + registerSomeExtensions( handle, VISIO_START, VISIO_END, true ); + + registerForExtensions( handle, bRegisterAll ); + + return ERROR_SUCCESS; +} + +static void restoreOldRegistration( LPCWSTR lpSubKey ) +{ + HKEY hKey = nullptr; + LONG lResult = RegOpenKeyExW( HKEY_CLASSES_ROOT, lpSubKey, 0, + KEY_QUERY_VALUE|KEY_SET_VALUE, &hKey ); + + if ( ERROR_SUCCESS == lResult ) + { + WCHAR szBuffer[1024]; + DWORD nSize = sizeof( szBuffer ); + + lResult = RegQueryValueExW( hKey, L"LOBackupAssociation", nullptr, nullptr, + reinterpret_cast<LPBYTE>(szBuffer), &nSize ); + if ( ERROR_SUCCESS == lResult ) + { + szBuffer[nSize/sizeof(*szBuffer)] = L'\0'; + HKEY hKey2 = nullptr; + lResult = RegOpenKeyExW( HKEY_CLASSES_ROOT, szBuffer, 0, + KEY_QUERY_VALUE, &hKey2 ); + if ( ERROR_SUCCESS == lResult ) + { + WCHAR szBuffer2[1024]; + DWORD nSize2 = sizeof( szBuffer2 ); + + lResult = RegQueryValueExW( hKey2, L"", nullptr, nullptr, reinterpret_cast<LPBYTE>(szBuffer2), &nSize2 ); + if ( ERROR_SUCCESS == lResult ) + { + WCHAR szBuffer3[1024]; + DWORD nSize3 = sizeof( szBuffer3 ); + + // Try to verify that the old association is OK to restore + lResult = RegQueryValueExW( hKey, L"LOBackupAssociationDeref", nullptr, nullptr, + reinterpret_cast<LPBYTE>(szBuffer3), &nSize3 ); + if ( ERROR_SUCCESS == lResult ) + { + if ( nSize2 == nSize3 && wcsncmp (szBuffer2, szBuffer3, nSize2/sizeof(*szBuffer2)) == 0) + { + // Yep. So restore it + RegSetValueExW( hKey, L"", 0, REG_SZ, reinterpret_cast<LPBYTE>(szBuffer), nSize ); + } + } + } + RegCloseKey( hKey2 ); + } + RegDeleteValueW( hKey, L"LOBackupAssociation" ); + } + RegDeleteValueW( hKey, L"LOBackupAssociationDeref" ); + RegCloseKey( hKey ); + } +} + +extern "C" __declspec(dllexport) UINT __stdcall RestoreRegAllMSDoc( MSIHANDLE /*handle*/ ) +{ + OutputDebugStringFormatW( L"RestoreRegAllMSDoc\n" ); + + int nIndex = 0; + while ( g_Extensions[nIndex] != nullptr ) + { + restoreOldRegistration( g_Extensions[nIndex] ); + ++nIndex; + } + + + return ERROR_SUCCESS; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ 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 000000000..c7b1cc742 --- /dev/null +++ b/setup_native/source/win32/customactions/reg_dlls/reg_dlls.cxx @@ -0,0 +1,319 @@ +/* -*- 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, const_cast<wchar_t*>(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)); + + WriteLog(hInstall, "gm_SharePointSupport_SubstMSO state:", // + "current", std::to_string(current_state_SubstMSO), // + "future", std::to_string(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)); + + WriteLog(hInstall, "gm_o_SharePointSupport state:", // + "current", std::to_string(current_state_Main), // + "future", std::to_string(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, const_cast<wchar_t*>(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 000000000..d0f31c491 --- /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/setup_native/source/win32/customactions/regactivex/regactivex.cxx b/setup_native/source/win32/customactions/regactivex/regactivex.cxx new file mode 100644 index 000000000..48b11bd4f --- /dev/null +++ b/setup_native/source/win32/customactions/regactivex/regactivex.cxx @@ -0,0 +1,344 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#if !defined WIN32_LEAN_AND_MEAN +# define WIN32_LEAN_AND_MEAN +#endif +#include <windows.h> +#include <msiquery.h> + +#include <cassert> +#include <string.h> +#include <malloc.h> + +#define CHART_COMPONENT 1 +#define DRAW_COMPONENT 2 +#define IMPRESS_COMPONENT 4 +#define CALC_COMPONENT 8 +#define WRITER_COMPONENT 16 +#define MATH_COMPONENT 32 + +typedef int ( __stdcall * DllNativeRegProc ) ( int, BOOL, BOOL, const wchar_t* ); +typedef int ( __stdcall * DllNativeUnregProc ) ( int, BOOL, BOOL ); + +static bool UnicodeEquals( wchar_t const * pStr1, wchar_t const * pStr2 ) +{ + if ( pStr1 == nullptr && pStr2 == nullptr ) + return true; + else if ( pStr1 == nullptr || pStr2 == nullptr ) + return false; + + while( *pStr1 == *pStr2 && *pStr1 && *pStr2 ) + { + pStr1++; + pStr2++; + } + + return ( *pStr1 == 0 && *pStr2 == 0 ); +} + + +static void RegisterActiveXNative( const wchar_t* pActiveXPath, int nMode, bool InstallForAllUser, bool InstallFor64Bit ) +{ + HINSTANCE hModule = LoadLibraryExW( pActiveXPath, nullptr, LOAD_WITH_ALTERED_SEARCH_PATH ); + if( hModule ) + { + DllNativeRegProc pNativeProc = reinterpret_cast<DllNativeRegProc>(GetProcAddress( hModule, "DllRegisterServerNative" )); + if( pNativeProc!=nullptr ) + { + int nLen = wcslen( pActiveXPath ); + int nRemoveLen = strlen( "\\so_activex.dll" ); + if ( nLen > nRemoveLen ) + { + wchar_t* pProgramPath = static_cast<wchar_t*>( malloc( (nLen - nRemoveLen + 1) * sizeof(wchar_t) ) ); + assert(pProgramPath); // Don't handle OOM conditions + wcsncpy( pProgramPath, pActiveXPath, nLen - nRemoveLen ); + pProgramPath[ nLen - nRemoveLen ] = 0; + + ( *pNativeProc )( nMode, InstallForAllUser, InstallFor64Bit, pProgramPath ); + + free( pProgramPath ); + } + } + + FreeLibrary( hModule ); + } +} + + +static void UnregisterActiveXNative( const wchar_t* pActiveXPath, int nMode, bool InstallForAllUser, bool InstallFor64Bit ) +{ + HINSTANCE hModule = LoadLibraryExW( pActiveXPath, nullptr, LOAD_WITH_ALTERED_SEARCH_PATH ); + if( hModule ) + { + DllNativeUnregProc pNativeProc = reinterpret_cast<DllNativeUnregProc>(GetProcAddress( hModule, "DllUnregisterServerNative" )); + if( pNativeProc!=nullptr ) + ( *pNativeProc )( nMode, InstallForAllUser, InstallFor64Bit ); + + FreeLibrary( hModule ); + } +} + + +static bool GetMsiPropW( MSIHANDLE hMSI, const wchar_t* pPropName, wchar_t** ppValue ) +{ + DWORD sz = 0; + if ( MsiGetPropertyW( hMSI, pPropName, const_cast<wchar_t *>(L""), &sz ) == ERROR_MORE_DATA ) + { + sz++; + DWORD nbytes = sz * sizeof( wchar_t ); + wchar_t* buff = static_cast<wchar_t*>( malloc( nbytes ) ); + assert(buff); // Don't handle OOM conditions + ZeroMemory( buff, nbytes ); + MsiGetPropertyW( hMSI, pPropName, buff, &sz ); + *ppValue = buff; + + return true; + } + + return false; +} + + +static bool GetActiveXControlPath( MSIHANDLE hMSI, wchar_t** ppActiveXPath ) +{ + wchar_t* pProgPath = nullptr; + if ( GetMsiPropW( hMSI, L"INSTALLLOCATION", &pProgPath ) && pProgPath ) + { + int nLen = wcslen( pProgPath ); + *ppActiveXPath = static_cast<wchar_t*>( malloc( (nLen + 23) * sizeof(wchar_t) ) ); + wcsncpy( *ppActiveXPath, pProgPath, nLen ); + wcsncpy( (*ppActiveXPath) + nLen, L"program\\so_activex.dll", 22 ); + (*ppActiveXPath)[nLen+22] = 0; + + free(pProgPath); + + return true; + } + + return false; +} + + +static bool GetDelta( MSIHANDLE hMSI, int& nOldInstallMode, int& nInstallMode, int& nDeinstallMode ) +{ + // for now the chart is always installed + nOldInstallMode = CHART_COMPONENT; + nInstallMode = CHART_COMPONENT; + nDeinstallMode = 0; + + INSTALLSTATE current_state; + INSTALLSTATE future_state; + + if ( ERROR_SUCCESS == MsiGetFeatureStateW( hMSI, L"gm_p_Wrt_Bin", ¤t_state, &future_state ) ) + { + // analyze writer installation mode + if ( current_state == INSTALLSTATE_LOCAL ) + nOldInstallMode |= WRITER_COMPONENT; + + if ( future_state == INSTALLSTATE_LOCAL + || ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_UNKNOWN ) ) + nInstallMode |= WRITER_COMPONENT; + else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT ) + nDeinstallMode |= WRITER_COMPONENT; + } + else + { + // assert( FALSE ); + } + + if ( ERROR_SUCCESS == MsiGetFeatureStateW( hMSI, L"gm_p_Calc_Bin", ¤t_state, &future_state ) ) + { + // analyze calc installation mode + if ( current_state == INSTALLSTATE_LOCAL ) + nOldInstallMode |= CALC_COMPONENT; + + if ( future_state == INSTALLSTATE_LOCAL + || ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_UNKNOWN ) ) + nInstallMode |= CALC_COMPONENT; + else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT ) + nDeinstallMode |= CALC_COMPONENT; + } + else + { + // assert( FALSE ); + } + + if ( ERROR_SUCCESS == MsiGetFeatureStateW( hMSI, L"gm_p_Draw_Bin", ¤t_state, &future_state ) ) + { + // analyze draw installation mode + if ( current_state == INSTALLSTATE_LOCAL ) + nOldInstallMode |= DRAW_COMPONENT; + + if ( future_state == INSTALLSTATE_LOCAL + || ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_UNKNOWN ) ) + nInstallMode |= DRAW_COMPONENT; + else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT ) + nDeinstallMode |= DRAW_COMPONENT; + } + else + { + // assert( FALSE ); + } + + if ( ERROR_SUCCESS == MsiGetFeatureStateW( hMSI, L"gm_p_Impress_Bin", ¤t_state, &future_state ) ) + { + // analyze impress installation mode + if ( current_state == INSTALLSTATE_LOCAL ) + nOldInstallMode |= IMPRESS_COMPONENT; + + if ( future_state == INSTALLSTATE_LOCAL + || ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_UNKNOWN ) ) + nInstallMode |= IMPRESS_COMPONENT; + else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT ) + nDeinstallMode |= IMPRESS_COMPONENT; + } + else + { + // assert( FALSE ); + } + + if ( ERROR_SUCCESS == MsiGetFeatureStateW( hMSI, L"gm_p_Math_Bin", ¤t_state, &future_state ) ) + { + // analyze math installation mode + if ( current_state == INSTALLSTATE_LOCAL ) + nOldInstallMode |= MATH_COMPONENT; + + if ( future_state == INSTALLSTATE_LOCAL + || ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_UNKNOWN ) ) + nInstallMode |= MATH_COMPONENT; + else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT ) + nDeinstallMode |= MATH_COMPONENT; + } + else + { + // assert( FALSE ); + } + + return true; +} + + +static bool MakeInstallForAllUsers( MSIHANDLE hMSI ) +{ + bool bResult = false; + wchar_t* pVal = nullptr; + if ( GetMsiPropW( hMSI, L"ALLUSERS", &pVal ) && pVal ) + { + bResult = UnicodeEquals( pVal , L"1" ); + free( pVal ); + } + + return bResult; +} + + +static bool MakeInstallFor64Bit( MSIHANDLE hMSI ) +{ + bool bResult = false; + wchar_t* pVal = nullptr; + if ( GetMsiPropW( hMSI, L"VersionNT64", &pVal ) && pVal ) + { + bResult = true; + free( pVal ); + } + + return bResult; +} + +extern "C" __declspec(dllexport) UINT __stdcall InstallActiveXControl( MSIHANDLE hMSI ) +{ + INSTALLSTATE current_state; + INSTALLSTATE future_state; + + if ( ERROR_SUCCESS == MsiGetFeatureStateW( hMSI, L"gm_o_Activexcontrol", ¤t_state, &future_state ) ) + { + int nOldInstallMode = 0; + int nInstallMode = 0; + int nDeinstallMode = 0; + bool bInstallForAllUser = MakeInstallForAllUsers( hMSI ); + bool bInstallFor64Bit = MakeInstallFor64Bit( hMSI ); + + wchar_t* pActiveXPath = nullptr; + if ( GetActiveXControlPath( hMSI, &pActiveXPath ) && pActiveXPath + && GetDelta( hMSI, nOldInstallMode, nInstallMode, nDeinstallMode ) ) + { + if ( future_state == INSTALLSTATE_LOCAL + || ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_UNKNOWN ) ) + { + // the control is installed in the new selected configuration + + if ( current_state == INSTALLSTATE_LOCAL && nDeinstallMode ) + UnregisterActiveXNative( pActiveXPath, nDeinstallMode, bInstallForAllUser, bInstallFor64Bit ); + + if ( nInstallMode ) + RegisterActiveXNative( pActiveXPath, nInstallMode, bInstallForAllUser, bInstallFor64Bit ); + } + else if ( current_state == INSTALLSTATE_LOCAL && future_state == INSTALLSTATE_ABSENT ) + { + if ( nOldInstallMode ) + UnregisterActiveXNative( pActiveXPath, nOldInstallMode, bInstallForAllUser, bInstallFor64Bit ); + } + } + + if ( pActiveXPath ) + free( pActiveXPath ); + } + else + { + // assert( FALSE ); + } + + return ERROR_SUCCESS; +} + + +extern "C" __declspec(dllexport) UINT __stdcall DeinstallActiveXControl( MSIHANDLE hMSI ) +{ + INSTALLSTATE current_state; + INSTALLSTATE future_state; + + if ( ERROR_SUCCESS == MsiGetFeatureStateW( hMSI, L"gm_o_Activexcontrol", ¤t_state, &future_state ) ) + { + wchar_t* pActiveXPath = nullptr; + if ( current_state == INSTALLSTATE_LOCAL && GetActiveXControlPath( hMSI, &pActiveXPath ) && pActiveXPath ) + { + bool bInstallForAllUser = MakeInstallForAllUsers( hMSI ); + bool bInstallFor64Bit = MakeInstallFor64Bit( hMSI ); + + { + UnregisterActiveXNative( pActiveXPath, + CHART_COMPONENT + | DRAW_COMPONENT + | IMPRESS_COMPONENT + | CALC_COMPONENT + | WRITER_COMPONENT + | MATH_COMPONENT, + bInstallForAllUser, + bInstallFor64Bit ); + } + + free( pActiveXPath ); + } + } + + return ERROR_SUCCESS; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/setup_native/source/win32/customactions/regactivex/regactivex.def b/setup_native/source/win32/customactions/regactivex/regactivex.def new file mode 100644 index 000000000..8a4c744d7 --- /dev/null +++ b/setup_native/source/win32/customactions/regactivex/regactivex.def @@ -0,0 +1,4 @@ +LIBRARY "regactivex.dll" +EXPORTS + InstallActiveXControl + DeinstallActiveXControl
\ No newline at end of file diff --git a/setup_native/source/win32/customactions/sellang/sellang.cxx b/setup_native/source/win32/customactions/sellang/sellang.cxx new file mode 100644 index 000000000..3154e1190 --- /dev/null +++ b/setup_native/source/win32/customactions/sellang/sellang.cxx @@ -0,0 +1,387 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +/* Currently the "all" installer has a bit over 100 UI languages, and + * I doubt it will grow a lot over that. + */ +#define MAX_LANGUAGES 200 + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <msiquery.h> +#include <malloc.h> + +#include <cassert> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <sal/macros.h> +#include <systools/win32/uwinapi.h> +#include <algorithm> + +#include <spellchecker_selection.hxx> + +static bool GetMsiPropA( MSIHANDLE hMSI, const char* pPropName, char** ppValue ) +{ + DWORD sz = 0; + if ( MsiGetPropertyA( hMSI, pPropName, const_cast<char *>(""), &sz ) == ERROR_MORE_DATA ) { + sz++; + DWORD nbytes = sz * sizeof( char ); + char* buff = static_cast<char*>( malloc( nbytes ) ); + assert(buff); // Don't handle OOM conditions + ZeroMemory( buff, nbytes ); + MsiGetPropertyA( hMSI, pPropName, buff, &sz ); + *ppValue = buff; + return ( strlen(buff) > 0 ); + } + return false; +} + +static const char * +langid_to_string( LANGID langid ) +{ + /* Map from LANGID to string. The languages below are now in + * alphabetical order of codes as in + * l10ntools/source/ulfconv/msi-encodinglist.txt. Only the + * language part is returned in the string. + */ + switch (PRIMARYLANGID (langid)) { + case LANG_AFRIKAANS: return "af"; + case LANG_AMHARIC: return "am"; + case LANG_ARABIC: return "ar"; + case LANG_ASSAMESE: return "as"; + case LANG_BELARUSIAN: return "be"; + case LANG_BULGARIAN: return "bg"; + case LANG_BENGALI: return "bn"; + case LANG_BRETON: return "br"; + case LANG_CATALAN: return "ca"; + case LANG_CZECH: return "cs"; + case LANG_WELSH: return "cy"; + case LANG_DANISH: return "da"; + case LANG_GERMAN: return "de"; + case LANG_GREEK: return "el"; + case LANG_ENGLISH: return "en"; + case LANG_SPANISH: return "es"; + case LANG_ESTONIAN: return "et"; + case LANG_BASQUE: return "eu"; + case LANG_FARSI: return "fa"; + case LANG_FINNISH: return "fi"; + case LANG_FAEROESE: return "fo"; + case LANG_FRENCH: return "fr"; + case LANG_IRISH: return "ga"; + case LANG_GALICIAN: return "gl"; + case LANG_GUJARATI: return "gu"; + case LANG_HEBREW: return "he"; + case LANG_HINDI: return "hi"; + case LANG_HUNGARIAN: return "hu"; + case LANG_ARMENIAN: return "hy"; + case LANG_INDONESIAN: return "id"; + case LANG_ICELANDIC: return "is"; + case LANG_ITALIAN: return "it"; + case LANG_JAPANESE: return "ja"; + case LANG_GEORGIAN: return "ka"; + case LANG_KAZAK: return "kk"; + case LANG_KHMER: return "km"; + case LANG_KANNADA: return "kn"; + case LANG_KOREAN: return "ko"; + case LANG_KASHMIRI: return "ks"; + case LANG_LAO: return "lo"; + case LANG_LITHUANIAN: return "lt"; + case LANG_LATVIAN: return "lv"; + case LANG_MACEDONIAN: return "mk"; + case LANG_MALAYALAM: return "ml"; + case LANG_MONGOLIAN: return "mn"; + case LANG_MARATHI: return "mr"; + case LANG_MALAY: return "ms"; + case LANG_MALTESE: return "mt"; + case LANG_NEPALI: return "ne"; + case LANG_DUTCH: return "nl"; + case LANG_SOTHO: return "ns"; + case LANG_ORIYA: return "or"; + case LANG_PUNJABI: return "pa"; + case LANG_POLISH: return "pl"; + case LANG_PORTUGUESE: return "pt"; + case LANG_ROMANSH: return "rm"; + case LANG_ROMANIAN: return "ro"; + case LANG_RUSSIAN: return "ru"; + case LANG_KINYARWANDA: return "rw"; + case LANG_SANSKRIT: return "sa"; + case LANG_UPPER_SORBIAN: return "sb"; + case LANG_SINDHI: return "sd"; + case LANG_SLOVAK: return "sk"; + case LANG_SLOVENIAN: return "sl"; + case LANG_ALBANIAN: return "sq"; + case LANG_SWEDISH: return "sv"; + case LANG_SWAHILI: return "sw"; + case LANG_TAMIL: return "ta"; + case LANG_TELUGU: return "te"; + case LANG_TAJIK: return "tg"; + case LANG_THAI: return "th"; + case LANG_TIGRIGNA: return "ti"; + case LANG_TSWANA: return "tn"; + case LANG_TURKISH: return "tr"; + case LANG_TATAR: return "tt"; + case LANG_UKRAINIAN: return "uk"; + case LANG_URDU: return "ur"; + case LANG_UZBEK: return "uz"; + case LANG_VIETNAMESE: return "vi"; + case LANG_XHOSA: return "xh"; + case LANG_CHINESE: return "zh"; + case LANG_ZULU: return "zu"; + /* Special cases */ + default: + switch (langid) { + case MAKELANGID(LANG_SERBIAN, 0x05): return "bs"; + case MAKELANGID(LANG_SERBIAN, SUBLANG_DEFAULT): return "hr"; + case MAKELANGID(LANG_NORWEGIAN, SUBLANG_NORWEGIAN_BOKMAL): return "nb"; + case MAKELANGID(LANG_NORWEGIAN, SUBLANG_NORWEGIAN_NYNORSK): return "nn"; + case MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_LATIN): return "sh"; + case MAKELANGID(LANG_SERBIAN, SUBLANG_SERBIAN_CYRILLIC): return "sr"; + default: return nullptr; + } + } +} + +/* Here we collect the UI languages present on the system; + * MAX_LANGUAGES is certainly enough for that + */ +static const char *ui_langs[MAX_LANGUAGES]; +static int num_ui_langs = 0; + +static void add_ui_lang(char const * lang) +{ + if (lang != nullptr && num_ui_langs != SAL_N_ELEMENTS(ui_langs)) { + ui_langs[num_ui_langs++] = lang; + } +} + +static BOOL CALLBACK +enum_ui_lang_proc (LPTSTR language, LONG_PTR /* unused_lParam */) +{ + long langid = strtol(language, nullptr, 16); + if (langid > 0xFFFF) + return TRUE; + add_ui_lang(langid_to_string(static_cast<LANGID>(langid))); + if (num_ui_langs == SAL_N_ELEMENTS(ui_langs) ) + return FALSE; + return TRUE; +} + +static bool +present_in_ui_langs(const char *lang) +{ + for (int i = 0; i < num_ui_langs; i++) + { + if (strchr (lang, '_') != nullptr) + if (memcmp (ui_langs[i], lang, std::min(strlen(ui_langs[i]), strlen(lang))) == 0) + return true; + if (strcmp (ui_langs[i], lang) == 0) + return true; + } + return false; +} + +namespace { + +/* TODO-BCP47: unlimit this, and if possible change from '_' to '-' separator + * to ease things. */ +// XXX NOTE: the sizeof needs to follow what is accepted in +// setup_native/source/packinfo/spellchecker_selection.pl +struct InstallLocalized { + char lang[sizeof("lll_Ssss_CC_vvvvvvvv")]; + bool install; +}; + +void addMatchingDictionaries( + char const * lang, InstallLocalized * dicts, int ndicts) +{ + for (int i = 0; i != SAL_N_ELEMENTS(setup_native::languageDictionaries); + ++i) + { + if (strcmp(lang, setup_native::languageDictionaries[i].language) == 0) { + for (char const * const * p = setup_native::languageDictionaries[i]. + dictionaries; + *p != nullptr; ++p) + { + for (int j = 0; j != ndicts; ++j) { + if (_stricmp(*p, dicts[j].lang) == 0) { + dicts[j].install = true; + break; + } + } + } + break; + } + } +} + +} + +extern "C" __declspec(dllexport) UINT __stdcall SelectLanguage( MSIHANDLE handle ) +{ + char feature[100]; + MSIHANDLE database, view, record; + DWORD length; + int nlangs = 0; + InstallLocalized langs[MAX_LANGUAGES]; + int ndicts = 0; + InstallLocalized dicts[MAX_LANGUAGES]; + + database = MsiGetActiveDatabase(handle); + + if (MsiDatabaseOpenViewA(database, "SELECT Feature from Feature WHERE Feature_Parent = 'gm_Langpack_Languageroot'", &view) != ERROR_SUCCESS) { + MsiCloseHandle(database); + return ERROR_SUCCESS; + } + + if (MsiViewExecute(view, NULL) != ERROR_SUCCESS) { + MsiCloseHandle(view); + MsiCloseHandle(database); + return ERROR_SUCCESS; + } + + while (nlangs < MAX_LANGUAGES && + MsiViewFetch(view, &record) == ERROR_SUCCESS) { + length = sizeof(feature); + if (MsiRecordGetStringA(record, 1, feature, &length) != ERROR_SUCCESS) { + MsiCloseHandle(record); + MsiCloseHandle(view); + MsiCloseHandle(database); + return ERROR_SUCCESS; + } + + /* Keep track of what langpacks are included in this installer. + */ + strcpy(langs[nlangs].lang, feature + strlen("gm_Langpack_r_")); + langs[nlangs].install = false; + ++nlangs; + + MsiCloseHandle(record); + } + + MsiCloseHandle(view); + + /* Keep track of what dictionaries are included in this installer: + */ + if (MsiDatabaseOpenViewA( + database, + ("SELECT Feature from Feature WHERE" + " Feature_Parent = 'gm_Dictionaries'"), + &view) + == ERROR_SUCCESS) + { + if (MsiViewExecute(view, NULL) == ERROR_SUCCESS) { + while (ndicts < MAX_LANGUAGES && + MsiViewFetch(view, &record) == ERROR_SUCCESS) + { + length = sizeof(feature); + if (MsiRecordGetStringA(record, 1, feature, &length) + == ERROR_SUCCESS) + { + if (strncmp( + feature, "gm_r_ex_Dictionary_", + strlen("gm_r_ex_Dictionary_")) + == 0) + { + strcpy( + dicts[ndicts].lang, + feature + strlen("gm_r_ex_Dictionary_")); + dicts[ndicts].install = false; + ++ndicts; + } + } + MsiCloseHandle(record); + } + } + MsiCloseHandle(view); + } + + /* Keep track of what UI languages are relevant, either the ones explicitly + * requested with the UI_LANGS property, or all available on the system: + */ + char* pVal = nullptr; + if ( (GetMsiPropA( handle, "UI_LANGS", &pVal )) && pVal ) { + char *str_ptr; + str_ptr = strtok(pVal, ","); + for(; str_ptr != nullptr ;) { + add_ui_lang(str_ptr); + str_ptr = strtok(nullptr, ","); + } + } else { + add_ui_lang(langid_to_string(GetSystemDefaultUILanguage())); + add_ui_lang(langid_to_string(LANGIDFROMLCID(GetThreadLocale()))); + //TODO: are the above two explicit additions necessary, or will + // those values always be included in the below EnumUILanguages + // anyway? + EnumUILanguagesA(enum_ui_lang_proc, 0, 0); + } + + // If the set of langpacks that match any of the relevant UI languages is + // non-empty, select just those matching langpacks; otherwise, if an en_US + // langpack is included, select just that langpack (this happens if, e.g., + // a multi-language en-US,de,es,fr,it,pt-BR installation set is installed on + // a Finnish Windows); otherwise, select all langpacks (this happens if, + // e.g., a single-language de installation set is installed on a Finnish + // Windows): + bool matches = false; + for (int i = 0; i < nlangs; i++) { + if (present_in_ui_langs(langs[i].lang)) { + langs[i].install = true; + matches = true; + } + } + if (!matches) { + for (int i = 0; i < nlangs; i++) { + if (strcmp(langs[i].lang, "en_US") == 0) { + langs[i].install = true; + matches = true; + break; + } + } + if (!matches) { + for (int i = 0; i < nlangs; i++) { + langs[i].install = true; + } + } + } + + for (int i = 0; i < nlangs; i++) { + if (langs[i].install) { + addMatchingDictionaries(langs[i].lang, dicts, ndicts); + } else { + sprintf(feature, "gm_Langpack_r_%s", langs[i].lang); + MsiSetFeatureStateA(handle, feature, INSTALLSTATE_ABSENT); + } + } + + // Select just those dictionaries that match any of the selected langpacks: + for (int i = 0; i != ndicts; ++i) { + if (!dicts[i].install) { + sprintf(feature, "gm_r_ex_Dictionary_%s", dicts[i].lang); + MsiSetFeatureStateA(handle, feature, INSTALLSTATE_ABSENT); + } + } + + MsiCloseHandle(database); + + return ERROR_SUCCESS; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/setup_native/source/win32/customactions/sellang/sellang.def b/setup_native/source/win32/customactions/sellang/sellang.def new file mode 100644 index 000000000..8d3c4d44c --- /dev/null +++ b/setup_native/source/win32/customactions/sellang/sellang.def @@ -0,0 +1,4 @@ +LIBRARY "sellangmsi.dll" +EXPORTS + SelectLanguage + SortTree
\ No newline at end of file diff --git a/setup_native/source/win32/customactions/sellang/sorttree.cxx b/setup_native/source/win32/customactions/sellang/sorttree.cxx new file mode 100644 index 000000000..cb44675b9 --- /dev/null +++ b/setup_native/source/win32/customactions/sellang/sorttree.cxx @@ -0,0 +1,55 @@ +/* -*- 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/. + */ + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <msi.h> +#include <commctrl.h> + +extern "C" __declspec(dllexport) UINT __stdcall SortTree(MSIHANDLE) +{ + // Sort items (languages) in SelectionTree control, fdo#46355 + + HWND hwndMSI = FindWindowW(L"MsiDialogCloseClass", nullptr); + if (hwndMSI == nullptr) + { + OutputDebugStringW(L"SortTree: MsiDialogCloseClass not found\n"); + return ERROR_SUCCESS; + } + HWND hwndTV = FindWindowExW(hwndMSI, nullptr, L"SysTreeView32", nullptr); + if (hwndTV == nullptr) + { + OutputDebugStringW(L"SortTree: SysTreeView32 not found\n"); + return ERROR_SUCCESS; + } + HTREEITEM optional = TreeView_GetRoot(hwndTV); + if (optional == nullptr) + { + OutputDebugStringW(L"SortTree: Optional Components branch not found\n"); + return ERROR_SUCCESS; + } + HTREEITEM dicts = TreeView_GetChild(hwndTV, optional); + if (dicts == nullptr) + { + OutputDebugStringW(L"SortTree: Dictionaries branch not found\n"); + return ERROR_SUCCESS; + } + TreeView_SortChildren(hwndTV, dicts, TRUE); + HTREEITEM langs = TreeView_GetNextSibling(hwndTV, optional); + if (langs == nullptr) + { + OutputDebugStringW(L"SortTree: Additional UI Languages branch not found\n"); + return ERROR_SUCCESS; + } + TreeView_SortChildren(hwndTV, langs, TRUE); + + return ERROR_SUCCESS; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/setup_native/source/win32/customactions/shellextensions/checkdirectory.cxx b/setup_native/source/win32/customactions/shellextensions/checkdirectory.cxx new file mode 100644 index 000000000..5c6865995 --- /dev/null +++ b/setup_native/source/win32/customactions/shellextensions/checkdirectory.cxx @@ -0,0 +1,66 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "shlxtmsi.hxx" + +#include <malloc.h> +#include <assert.h> + +#include <queue> +#include <stdio.h> + +#include <systools/win32/uwinapi.h> +#include "../tools/seterror.hxx" + +extern "C" __declspec(dllexport) UINT __stdcall CheckInstallDirectory(MSIHANDLE handle) +{ + std::wstring sInstallPath = GetMsiPropertyW(handle, L"INSTALLLOCATION"); + std::wstring sOfficeHostnamePath = GetMsiPropertyW(handle, L"OFFICEDIRHOSTNAME"); + + // MessageBoxW(NULL, sInstallPath.c_str(), L"DEBUG", MB_OK); + + // unsetting all properties + + UnsetMsiPropertyW( handle, L"DIRECTORY_NOT_EMPTY" ); + + // 1. Searching for file setup.ini + + std::wstring sSetupIniPath = sInstallPath + sOfficeHostnamePath + L"\\program\\setup.ini"; + + WIN32_FIND_DATAW data; + HANDLE hdl = FindFirstFileW(sSetupIniPath.c_str(), &data); + + // std::wstring mystr = L"Searching for " + sSetupIniPath; + // MessageBoxW(NULL, mystr.c_str(), L"DEBUG", MB_OK); + + if ( IsValidHandle(hdl) ) + { + // setup.ini found -> directory cannot be used for installation. + SetMsiPropertyW( handle, L"DIRECTORY_NOT_EMPTY", L"1" ); + SetMsiErrorCode( MSI_ERROR_DIRECTORY_NOT_EMPTY ); + // std::wstring notEmptyStr = L"Directory is not empty. Please choose another installation directory."; + // std::wstring notEmptyTitle = L"Directory not empty"; + // MessageBoxW(NULL, notEmptyStr.c_str(), notEmptyTitle.c_str(), MB_OK); + FindClose(hdl); + } + + return ERROR_SUCCESS; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/setup_native/source/win32/customactions/shellextensions/checkpatches.cxx b/setup_native/source/win32/customactions/shellextensions/checkpatches.cxx new file mode 100644 index 000000000..0a879dc1b --- /dev/null +++ b/setup_native/source/win32/customactions/shellextensions/checkpatches.cxx @@ -0,0 +1,59 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "shlxtmsi.hxx" +#include <strsafe.h> +#include <systools/win32/uwinapi.h> + +#ifdef DEBUG +inline void OutputDebugStringFormatW( PCWSTR pFormat, ... ) +{ + WCHAR buffer[1024]; + va_list args; + + va_start( args, pFormat ); + StringCchVPrintfW( buffer, SAL_N_ELEMENTS(buffer), pFormat, args ); + OutputDebugStringW( buffer ); + va_end(args); +} +#else +static void OutputDebugStringFormatW( PCWSTR, ... ) +{ +} +#endif + +extern "C" __declspec(dllexport) UINT __stdcall CheckPatchList( MSIHANDLE handle ) +{ + std::wstring sPatchList = GetMsiPropertyW( handle, L"PATCH" ); + std::wstring sRequiredPatch = GetMsiPropertyW( handle, L"PREREQUIREDPATCH" ); + + OutputDebugStringFormatW( L"CheckPatchList called with PATCH=%s and PRQ=%s\n", sPatchList.c_str(), sRequiredPatch.c_str() ); + + if ( ( sPatchList.length() != 0 ) && ( sRequiredPatch.length() != 0 ) ) + { + if ( wcsstr( sPatchList.c_str(), sRequiredPatch.c_str() ) ) + { + SetMsiPropertyW( handle, L"IGNOREPREREQUIREDPATCH", L"1" ); + OutputDebugStringFormatW( L"Set Property IgnorePrerequiredPatch!\n" ); + } + } + return ERROR_SUCCESS; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/setup_native/source/win32/customactions/shellextensions/completeinstallpath.cxx b/setup_native/source/win32/customactions/shellextensions/completeinstallpath.cxx new file mode 100644 index 000000000..787041ed8 --- /dev/null +++ b/setup_native/source/win32/customactions/shellextensions/completeinstallpath.cxx @@ -0,0 +1,116 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "shlxtmsi.hxx" + +#include <malloc.h> + +extern "C" __declspec(dllexport) UINT __stdcall CompleteInstallPath( MSIHANDLE handle ) +{ + // This CustomAction is necessary for updates from OOo 3.0, OOo 3.1 and OOo 3.2 to versions + // OOo 3.3 or later. This is caused by a change of INSTALLLOCATION, that starting with OOo 3.3 + // contains the name of the product again (instead of only "c:\program files"). Unfortunately + // this causes in an update installation, that INSTALLLOCATION is set to "c:\program files", + // so that in an OOo 3.3 or later, the directory "program" or "share" are directly created + // below "c:\program files". + + HKEY hKey; + + // Reading property OFFICEDIRHOSTNAME_, that contains the part of the path behind + // the program files folder. + + std::wstring sInstallLocation = GetMsiPropertyW( handle, L"INSTALLLOCATION" ); + std::wstring sOfficeDirHostname = GetMsiPropertyW( handle, L"OFFICEDIRHOSTNAME_" ); + + // If sInstallLocation ends with (contains) the string sOfficeDirHostname, + // INSTALLLOCATION is good and nothing has to be done here. + + bool pathCompletionRequired = true; + + if ( wcsstr( sInstallLocation.c_str(), sOfficeDirHostname.c_str() ) ) + { + pathCompletionRequired = false; // nothing to do + } + + // If the path INSTALLLOCATION does not end with this string, INSTALLLOCATION is maybe + // transferred from an OOo 3.0, OOo 3.1 and OOo 3.2 and need to be changed therefore. + + if ( pathCompletionRequired ) + { + std::wstring sManufacturer = GetMsiPropertyW( handle, L"Manufacturer" ); + std::wstring sDefinedName = GetMsiPropertyW( handle, L"DEFINEDPRODUCT" ); + std::wstring sUpgradeCode = GetMsiPropertyW( handle, L"UpgradeCode" ); + + // sUpdateVersion can be "3.0", "3.1" or "3.2" + + std::wstring sProductKey30 = L"Software\\" + sManufacturer + L"\\" + sDefinedName + + L"\\" L"3.0" L"\\" + sUpgradeCode; + + std::wstring sProductKey31 = L"Software\\" + sManufacturer + L"\\" + sDefinedName + + L"\\" L"3.1" L"\\" + sUpgradeCode; + + std::wstring sProductKey32 = L"Software\\" + sManufacturer + L"\\" + sDefinedName + + L"\\" L"3.2" L"\\" + sUpgradeCode; + + bool oldVersionExists = false; + + if ( ERROR_SUCCESS == RegOpenKeyW( HKEY_CURRENT_USER, sProductKey30.c_str(), &hKey ) ) + { + oldVersionExists = true; + RegCloseKey( hKey ); + } + else if ( ERROR_SUCCESS == RegOpenKeyW( HKEY_CURRENT_USER, sProductKey31.c_str(), &hKey ) ) + { + oldVersionExists = true; + RegCloseKey( hKey ); + } + else if ( ERROR_SUCCESS == RegOpenKeyW( HKEY_CURRENT_USER, sProductKey32.c_str(), &hKey ) ) + { + oldVersionExists = true; + RegCloseKey( hKey ); + } + else if ( ERROR_SUCCESS == RegOpenKeyW( HKEY_LOCAL_MACHINE, sProductKey30.c_str(), &hKey ) ) + { + oldVersionExists = true; + RegCloseKey( hKey ); + } + else if ( ERROR_SUCCESS == RegOpenKeyW( HKEY_LOCAL_MACHINE, sProductKey31.c_str(), &hKey ) ) + { + oldVersionExists = true; + RegCloseKey( hKey ); + } + else if ( ERROR_SUCCESS == RegOpenKeyW( HKEY_LOCAL_MACHINE, sProductKey32.c_str(), &hKey ) ) + { + oldVersionExists = true; + RegCloseKey( hKey ); + } + + if ( oldVersionExists ) + { + // Adding the new path content sOfficeDirHostname + sInstallLocation += sOfficeDirHostname; + // Setting the new property value + MsiSetPropertyW(handle, L"INSTALLLOCATION", sInstallLocation.c_str()); + } + } + + return ERROR_SUCCESS; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/setup_native/source/win32/customactions/shellextensions/migrateinstallpath.cxx b/setup_native/source/win32/customactions/shellextensions/migrateinstallpath.cxx new file mode 100644 index 000000000..10c817169 --- /dev/null +++ b/setup_native/source/win32/customactions/shellextensions/migrateinstallpath.cxx @@ -0,0 +1,96 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "shlxtmsi.hxx" +#include <algorithm> +#include <sstream> +#include <systools/win32/uwinapi.h> + +extern "C" __declspec(dllexport) UINT __stdcall MigrateInstallPath(MSIHANDLE handle) +{ + std::wstring sInstDir = GetMsiPropertyW(handle, L"INSTALLLOCATION"); + if (!sInstDir.empty()) + return ERROR_SUCCESS; // Don't overwrite explicitly set value + + auto RegValue = [](HKEY hRoot, const WCHAR* sKey, const WCHAR* sVal) { + std::wstring sResult; + WCHAR buf[32767]; // max longpath + DWORD bufsize = sizeof(buf); // yes, it is the number of bytes + if (RegGetValueW(hRoot, sKey, sVal, RRF_RT_REG_SZ, nullptr, buf, &bufsize) == ERROR_SUCCESS) + sResult = buf; // RegGetValueW null-terminates strings + + return sResult; + }; + + const std::wstring sManufacturer = GetMsiPropertyW( handle, L"Manufacturer" ); + const std::wstring sDefinedName = GetMsiPropertyW( handle, L"DEFINEDPRODUCT" ); + const std::wstring sUpdateVersion = GetMsiPropertyW( handle, L"DEFINEDVERSION" ); + const std::wstring sUpgradeCode = GetMsiPropertyW( handle, L"UpgradeCode" ); + const std::wstring sBrandPackageVersion = GetMsiPropertyW(handle, L"BRANDPACKAGEVERSION"); + + std::wstring sKey = L"Software\\" + sManufacturer + L"\\" + sDefinedName + + L"\\" + sUpdateVersion + L"\\" + sUpgradeCode; + + sInstDir = RegValue(HKEY_CURRENT_USER, sKey.c_str(), L"INSTALLLOCATION"); + if (sInstDir.empty()) + sInstDir = RegValue(HKEY_LOCAL_MACHINE, sKey.c_str(), L"INSTALLLOCATION"); + // See #i93032# for layers description + if (sInstDir.empty()) + { + sKey = L"Software\\LibreOffice\\Layers\\" + sDefinedName + L"\\" + sBrandPackageVersion; + sInstDir = RegValue(HKEY_CURRENT_USER, sKey.c_str(), L"INSTALLLOCATION"); + } + if (sInstDir.empty()) + { + sKey = L"Software\\LibreOffice\\Layers_\\" + sDefinedName + L"\\" + sBrandPackageVersion; + sInstDir = RegValue(HKEY_CURRENT_USER, sKey.c_str(), L"INSTALLLOCATION"); + } + if (sInstDir.empty()) + { + sKey = L"Software\\LibreOffice\\Layers\\" + sDefinedName + L"\\" + sBrandPackageVersion; + sInstDir = RegValue(HKEY_LOCAL_MACHINE, sKey.c_str(), L"INSTALLLOCATION"); + } + if (sInstDir.empty()) + { + sKey = L"Software\\LibreOffice\\Layers_\\" + sDefinedName + L"\\" + sBrandPackageVersion; + sInstDir = RegValue(HKEY_LOCAL_MACHINE, sKey.c_str(), L"INSTALLLOCATION"); + } + if (sInstDir.empty()) + { + std::wistringstream sOlds{ GetMsiPropertyW(handle, L"OLDPRODUCTS") }; + std::wstring sOld; + while (std::getline(sOlds, sOld, L';')) + { + if (sOld.empty()) + continue; + sKey = L"Software\\Microsoft\\Windows\\CurrentVersion\\Uninstall\\" + sOld; + sInstDir = RegValue(HKEY_LOCAL_MACHINE, sKey.c_str(), L"InstallLocation"); + if (!sInstDir.empty()) + break; + } + } + + if (!sInstDir.empty()) + MsiSetPropertyW(handle, L"INSTALLLOCATION", sInstDir.c_str()); + + return ERROR_SUCCESS; + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/setup_native/source/win32/customactions/shellextensions/shlxtmsi.def b/setup_native/source/win32/customactions/shellextensions/shlxtmsi.def new file mode 100644 index 000000000..e0e667953 --- /dev/null +++ b/setup_native/source/win32/customactions/shellextensions/shlxtmsi.def @@ -0,0 +1,11 @@ +LIBRARY "shlxtmsi.dll" +EXPORTS + CheckInstallDirectory + CheckPatchList + CompleteInstallPath + MigrateInstallPath + InstallStartmenuFolderIcon + DeinstallStartmenuFolderIcon + SetProductInstallMode + RenamePrgFolder + RemovePrgFolder diff --git a/setup_native/source/win32/customactions/shellextensions/shlxtmsi.hxx b/setup_native/source/win32/customactions/shellextensions/shlxtmsi.hxx new file mode 100644 index 000000000..3a08060be --- /dev/null +++ b/setup_native/source/win32/customactions/shellextensions/shlxtmsi.hxx @@ -0,0 +1,59 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_SETUP_NATIVE_SOURCE_WIN32_CUSTOMACTIONS_SHELLEXTENSIONS_SHLXTMSI_HXX +#define INCLUDED_SETUP_NATIVE_SOURCE_WIN32_CUSTOMACTIONS_SHELLEXTENSIONS_SHLXTMSI_HXX + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <msiquery.h> + +#include <string> + +static inline std::wstring GetMsiPropertyW( MSIHANDLE handle, const std::wstring& sProperty ) +{ + std::wstring result; + WCHAR szDummy[1] = L""; + DWORD nChars = 0; + + if ( MsiGetPropertyW( handle, sProperty.c_str(), szDummy, &nChars ) == ERROR_MORE_DATA ) + { + DWORD nBytes = ++nChars * sizeof(WCHAR); + PWSTR buffer = static_cast<PWSTR>(_alloca(nBytes)); + ZeroMemory( buffer, nBytes ); + MsiGetPropertyW( handle, sProperty.c_str(), buffer, &nChars ); + result = buffer; + } + + return result; +} + +static inline void SetMsiPropertyW( MSIHANDLE handle, const std::wstring& sProperty, const std::wstring& sValue ) +{ + MsiSetPropertyW( handle, sProperty.c_str(), sValue.c_str() ); +} + +static inline void UnsetMsiPropertyW( MSIHANDLE handle, const std::wstring& sProperty ) +{ + MsiSetPropertyW( handle, sProperty.c_str(), nullptr ); +} + +#endif // INCLUDED_SETUP_NATIVE_SOURCE_WIN32_CUSTOMACTIONS_SHELLEXTENSIONS_SHLXTMSI_HXX + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/setup_native/source/win32/customactions/shellextensions/startmenuicon.cxx b/setup_native/source/win32/customactions/shellextensions/startmenuicon.cxx new file mode 100644 index 000000000..6e95c6e1e --- /dev/null +++ b/setup_native/source/win32/customactions/shellextensions/startmenuicon.cxx @@ -0,0 +1,63 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "shlxtmsi.hxx" + +#include <malloc.h> + +/* + Called during installation to customize the start menu folder icon. + See: http://msdn.microsoft.com/library/en-us/shellcc/platform/shell/programmersguide/shell_basics/shell_basics_extending/custom.asp +*/ +extern "C" __declspec(dllexport) UINT __stdcall InstallStartmenuFolderIcon( MSIHANDLE handle ) +{ + std::wstring sOfficeMenuFolder = GetMsiPropertyW( handle, L"OfficeMenuFolder" ); + std::wstring sDesktopFile = sOfficeMenuFolder + L"Desktop.ini"; + + // at the moment there exists no Vista Icon, so we use the default folder icon. + // add the icon into desktop/util/verinfo.rc + + // The value '0' is to avoid a message like "You Are Deleting a System Folder" warning when deleting or moving the folder. + WritePrivateProfileStringW( + L".ShellClassInfo", + L"ConfirmFileOp", + L"0", + sDesktopFile.c_str() ); + + SetFileAttributesW( sDesktopFile.c_str(), FILE_ATTRIBUTE_HIDDEN ); + SetFileAttributesW( sOfficeMenuFolder.c_str(), FILE_ATTRIBUTE_SYSTEM ); + + + return ERROR_SUCCESS; +} + +extern "C" __declspec(dllexport) UINT __stdcall DeinstallStartmenuFolderIcon(MSIHANDLE handle) +{ + std::wstring sOfficeMenuFolder = GetMsiPropertyW( handle, L"OfficeMenuFolder" ); + std::wstring sDesktopFile = sOfficeMenuFolder + L"Desktop.ini"; + + SetFileAttributesW( sDesktopFile.c_str(), FILE_ATTRIBUTE_NORMAL ); + DeleteFileW( sDesktopFile.c_str() ); + + SetFileAttributesW( sOfficeMenuFolder.c_str(), FILE_ATTRIBUTE_NORMAL ); + + return ERROR_SUCCESS; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/setup_native/source/win32/customactions/shellextensions/upgrade.cxx b/setup_native/source/win32/customactions/shellextensions/upgrade.cxx new file mode 100644 index 000000000..494cfaeda --- /dev/null +++ b/setup_native/source/win32/customactions/shellextensions/upgrade.cxx @@ -0,0 +1,158 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "shlxtmsi.hxx" + +#include <malloc.h> +#include <assert.h> + +namespace +{ + // The provided GUID must be without surrounding '{}' + std::wstring GetGuidPart(const std::wstring& guid, int index) + { + assert((guid.length() == 36) && "No GUID or wrong format!"); + assert(((index > -1) && (index < 5)) && "Out of range!"); + + if (index == 0) return std::wstring(guid.c_str(), 8); + if (index == 1) return std::wstring(guid.c_str() + 9, 4); + if (index == 2) return std::wstring(guid.c_str() + 14, 4); + if (index == 3) return std::wstring(guid.c_str() + 19, 4); + if (index == 4) return std::wstring(guid.c_str() + 24, 12); + + return std::wstring(); + } + + void Swap(wchar_t* p1, wchar_t* p2) + { + wchar_t tmp = *p1; + *p1 = *p2; + *p2 = tmp; + } + + std::wstring Invert(const std::wstring& str) + { + wchar_t* buff = static_cast<wchar_t*>(_alloca(str.length()*sizeof(wchar_t))); + wcsncpy(buff, str.c_str(), str.length()); + + wchar_t* front = buff; + wchar_t* back = buff + str.length() - 1; + + while (front < back) + Swap(front++, back--); + + return std::wstring(buff, str.length()); + } + + // Convert the upgrade code (which is a GUID) according + // to the way the windows installer does when writing it + // to the registry + // The first 8 bytes will be inverted, from the last + // 8 bytes always the nibbles will be inverted for further + // details look in the MSDN under compressed registry keys + std::wstring ConvertGuid(const std::wstring& guid) + { + std::wstring convertedGuid; + + std::wstring part = GetGuidPart(guid, 0); + convertedGuid = Invert(part); + + part = GetGuidPart(guid, 1); + convertedGuid += Invert(part); + + part = GetGuidPart(guid, 2); + convertedGuid += Invert(part); + + part = GetGuidPart(guid, 3); + convertedGuid += Invert(std::wstring(part.c_str(), 2)); + convertedGuid += Invert(std::wstring(part.c_str() + 2, 2)); + + part = GetGuidPart(guid, 4); + int pos = 0; + for (int i = 0; i < 6; i++) + { + convertedGuid += Invert(std::wstring(part.c_str() + pos, 2)); + pos += 2; + } + return convertedGuid; + } + + bool IsSetMsiPropertyW(MSIHANDLE handle, const std::wstring& sProperty) + { + return (GetMsiPropertyW(handle, sProperty).length() > 0); + } + + void SetMsiPropertyW(MSIHANDLE handle, const std::wstring& sProperty) + { + MsiSetPropertyW(handle, sProperty.c_str(), L"1"); + } + + bool RegistryKeyHasUpgradeSubKey( + HKEY hRootKey, const std::wstring& regKey, const std::wstring& upgradeKey) + { + HKEY hKey; + if (RegOpenKeyW(hRootKey, regKey.c_str(), &hKey) == ERROR_SUCCESS) + { + DWORD nSubKeys; + DWORD lLongestSubKey; + + if (RegQueryInfoKeyW( + hKey, nullptr, nullptr, nullptr, &nSubKeys, &lLongestSubKey, nullptr, nullptr, nullptr, nullptr, nullptr, nullptr) == ERROR_SUCCESS) + { + LPWSTR buffer = static_cast<LPWSTR>(_alloca((lLongestSubKey + 1)*sizeof(WCHAR))); + + for (DWORD i = 0; i < nSubKeys; i++) + { + LONG ret = RegEnumKeyW(hKey, i, buffer, lLongestSubKey + 1); + if ((ret == ERROR_SUCCESS) && (buffer == upgradeKey)) + return true; + } + } + } + return false; + } +} // namespace + +extern "C" __declspec(dllexport) UINT __stdcall SetProductInstallMode(MSIHANDLE handle) +{ + std::wstring upgradeCode = GetMsiPropertyW(handle, L"UpgradeCode"); + upgradeCode = ConvertGuid(std::wstring(upgradeCode.c_str() + 1, upgradeCode.length() - 2)); + + // MessageBoxW(NULL, upgradeCode.c_str(), "Debug", MB_OK); + + if (RegistryKeyHasUpgradeSubKey( + HKEY_CURRENT_USER, + L"Software\\Microsoft\\Installer\\UpgradeCodes", + upgradeCode) && IsSetMsiPropertyW(handle, L"ALLUSERS")) + { + UnsetMsiPropertyW(handle, L"ALLUSERS"); + // MessageBoxW(NULL, L"ALLUSERS removed", L"DEBUG", MB_OK); + } + else if (RegistryKeyHasUpgradeSubKey( + HKEY_LOCAL_MACHINE, + L"Software\\Classes\\Installer\\UpgradeCodes", + upgradeCode) && !IsSetMsiPropertyW(handle, L"ALLUSERS")) + { + SetMsiPropertyW(handle, L"ALLUSERS"); + // MessageBoxW(NULL, L"ALLUSERS set", L"DEBUG", MB_OK); + } + return ERROR_SUCCESS; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/setup_native/source/win32/customactions/shellextensions/vistaspecial.cxx b/setup_native/source/win32/customactions/shellextensions/vistaspecial.cxx new file mode 100644 index 000000000..e61f4a43f --- /dev/null +++ b/setup_native/source/win32/customactions/shellextensions/vistaspecial.cxx @@ -0,0 +1,126 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include "shlxtmsi.hxx" + +#include <strsafe.h> + +#include <systools/win32/uwinapi.h> +#include "../tools/seterror.hxx" + +static bool RemoveCompleteDirectoryW(const std::wstring& rPath) +{ + bool bDirectoryRemoved = true; + + std::wstring sPattern = rPath + L"\\" + L"*.*"; + WIN32_FIND_DATAW aFindData; + + // Finding all content in rPath + + HANDLE hFindContent = FindFirstFileW( sPattern.c_str(), &aFindData ); + + if ( hFindContent != INVALID_HANDLE_VALUE ) + { + bool fNextFile = false; + std::wstring sCurrentDir = L"."; + std::wstring sParentDir = L".."; + + do + { + std::wstring sFileName = aFindData.cFileName; + + if (( wcscmp(sFileName.c_str(),sCurrentDir.c_str()) != 0 ) && + ( wcscmp(sFileName.c_str(),sParentDir.c_str()) != 0 )) + { + std::wstring sCompleteFileName = rPath + L"\\" + sFileName; + + if ( aFindData.dwFileAttributes == FILE_ATTRIBUTE_DIRECTORY ) + { + RemoveCompleteDirectoryW(sCompleteFileName); + } + else + { + DeleteFileW( sCompleteFileName.c_str() ); + } + } + + fNextFile = FindNextFileW( hFindContent, &aFindData ); + + } while ( fNextFile ); + + FindClose( hFindContent ); + + // empty directory can be removed now + // RemoveDirectory is only successful, if the last handle to the directory is closed + // -> first removing content -> closing handle -> remove empty directory + + + if( !( RemoveDirectoryW(rPath.c_str()) ) ) + { + bDirectoryRemoved = false; + } + } + + return bDirectoryRemoved; +} + +extern "C" __declspec(dllexport) UINT __stdcall RenamePrgFolder( MSIHANDLE handle ) +{ + std::wstring sOfficeInstallPath = GetMsiPropertyW(handle, L"INSTALLLOCATION"); + + std::wstring sRenameSrc = sOfficeInstallPath + L"program"; + std::wstring sRenameDst = sOfficeInstallPath + L"program_old"; + + bool bSuccess = MoveFileW( sRenameSrc.c_str(), sRenameDst.c_str() ); + if ( !bSuccess ) + { + WCHAR sAppend[2] = L"0"; + for ( int i = 0; i < 10; i++ ) + { + sRenameDst = sOfficeInstallPath + L"program_old" + sAppend; + bSuccess = MoveFileW( sRenameSrc.c_str(), sRenameDst.c_str() ); + if ( bSuccess ) + break; + sAppend[0] += 1; + } + } + + // ? This succeeds unconditionally, even if bSuccess is false! + return ERROR_SUCCESS; +} + +extern "C" __declspec(dllexport) UINT __stdcall RemovePrgFolder( MSIHANDLE handle ) +{ + std::wstring sOfficeInstallPath = GetMsiPropertyW(handle, L"INSTALLLOCATION"); + std::wstring sRemoveDir = sOfficeInstallPath + L"program_old"; + + RemoveCompleteDirectoryW( sRemoveDir ); + + WCHAR sAppend[2] = L"0"; + for ( int i = 0; i < 10; i++ ) + { + sRemoveDir = sOfficeInstallPath + L"program_old" + sAppend; + RemoveCompleteDirectoryW( sRemoveDir ); + sAppend[0] += 1; + } + + return ERROR_SUCCESS; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/setup_native/source/win32/customactions/tools/checkversion.cxx b/setup_native/source/win32/customactions/tools/checkversion.cxx new file mode 100644 index 000000000..3f0b30f3c --- /dev/null +++ b/setup_native/source/win32/customactions/tools/checkversion.cxx @@ -0,0 +1,98 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> +#include <msiquery.h> + +#include <cassert> +#include <string.h> +#include <malloc.h> +#include <stdio.h> +#include <strsafe.h> + +#include "seterror.hxx" + + +static bool GetMsiPropW( MSIHANDLE hMSI, const wchar_t* pPropName, wchar_t** ppValue ) +{ + DWORD sz = 0; + if ( MsiGetPropertyW( hMSI, pPropName, const_cast<wchar_t *>(L""), &sz ) == ERROR_MORE_DATA ) + { + sz++; + DWORD nbytes = sz * sizeof( wchar_t ); + wchar_t* buff = static_cast<wchar_t*>( malloc( nbytes ) ); + assert(buff); // Don't handle OOM conditions + ZeroMemory( buff, nbytes ); + MsiGetPropertyW( hMSI, pPropName, buff, &sz ); + *ppValue = buff; + + return true; + } + + return false; +} + + +#ifdef DEBUG +inline void OutputDebugStringFormatW( PCWSTR pFormat, ... ) +{ + WCHAR buffer[1024]; + va_list args; + + va_start( args, pFormat ); + StringCchVPrintfW( buffer, sizeof(buffer)/sizeof(buffer[0]), pFormat, args ); + OutputDebugStringW( buffer ); + va_end(args); +} +#else +static void OutputDebugStringFormatW( PCWSTR, ... ) +{ +} +#endif + + +extern "C" __declspec(dllexport) UINT __stdcall CheckVersions( MSIHANDLE hMSI ) +{ + // MessageBoxW(NULL, L"CheckVersions", L"Information", MB_OK | MB_ICONINFORMATION); + + wchar_t* pVal = nullptr; + + if ( GetMsiPropW( hMSI, L"NEWPRODUCTS", &pVal ) && pVal ) + { + OutputDebugStringFormatW( L"DEBUG: NEWPRODUCTS found [%s]", pVal ); + if ( *pVal != 0 ) + SetMsiErrorCode( MSI_ERROR_NEW_VERSION_FOUND ); + free( pVal ); + } + pVal = nullptr; + if ( GetMsiPropW( hMSI, L"OLDPRODUCTS", &pVal ) && pVal ) + { + OutputDebugStringFormatW( L"DEBUG: OLDPRODUCTS found [%s]", pVal ); + if ( *pVal != 0 ) + SetMsiErrorCode( MSI_ERROR_OLD_VERSION_FOUND ); + free( pVal ); + } + pVal = nullptr; + + return ERROR_SUCCESS; +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/setup_native/source/win32/customactions/tools/seterror.cxx b/setup_native/source/win32/customactions/tools/seterror.cxx new file mode 100644 index 000000000..f1800ffb7 --- /dev/null +++ b/setup_native/source/win32/customactions/tools/seterror.cxx @@ -0,0 +1,84 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#define WIN32_LEAN_AND_MEAN +#include <windows.h> + +#include <string.h> +#include <malloc.h> +#include <stdio.h> +#include <strsafe.h> + +#include "seterror.hxx" + + +#ifdef DEBUG +inline void OutputDebugStringFormatW( PCWSTR pFormat, ... ) +{ + WCHAR buffer[1024]; + va_list args; + + va_start( args, pFormat ); + StringCchVPrintfW( buffer, sizeof(buffer)/sizeof(buffer[0]), pFormat, args ); + OutputDebugStringW( buffer ); + va_end(args); +} +#else +static void OutputDebugStringFormatW( PCWSTR, ... ) +{ +} +#endif + + +void SetMsiErrorCode( int nErrorCode ) +{ + const WCHAR sMemMapName[] = L"Global\\MsiErrorObject"; + + HANDLE hMapFile; + int *pBuf; + + hMapFile = OpenFileMappingW( + FILE_MAP_ALL_ACCESS, // read/write access + FALSE, // do not inherit the name + sMemMapName ); // name of mapping object + + if ( hMapFile == nullptr ) // can not set error code + { + OutputDebugStringFormatW( L"Could not open map file (%d).\n", GetLastError() ); + return; + } + + pBuf = static_cast<int*>(MapViewOfFile( hMapFile, // handle to map object + FILE_MAP_ALL_ACCESS, // read/write permission + 0, + 0, + sizeof( int ) )); + if ( pBuf ) + { + *pBuf = nErrorCode; + UnmapViewOfFile( pBuf ); + } + else + OutputDebugStringFormatW( L"Could not map view of file (%d).\n", GetLastError() ); + + CloseHandle( hMapFile ); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/setup_native/source/win32/customactions/tools/seterror.hxx b/setup_native/source/win32/customactions/tools/seterror.hxx new file mode 100644 index 000000000..010af4a8c --- /dev/null +++ b/setup_native/source/win32/customactions/tools/seterror.hxx @@ -0,0 +1,36 @@ +/* -*- 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/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#ifndef INCLUDED_SETUP_NATIVE_SOURCE_WIN32_CUSTOMACTIONS_TOOLS_SETERROR_HXX +#define INCLUDED_SETUP_NATIVE_SOURCE_WIN32_CUSTOMACTIONS_TOOLS_SETERROR_HXX + + +// list of own error codes + +#define MSI_ERROR_NEW_VERSION_FOUND 9010 +#define MSI_ERROR_OLD_VERSION_FOUND 9012 + +#define MSI_ERROR_DIRECTORY_NOT_EMPTY 9030 + + +void SetMsiErrorCode( int nErrorCode ); + +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/setup_native/source/win32/customactions/tools/sn_tools.def b/setup_native/source/win32/customactions/tools/sn_tools.def new file mode 100644 index 000000000..3c881861b --- /dev/null +++ b/setup_native/source/win32/customactions/tools/sn_tools.def @@ -0,0 +1,3 @@ +LIBRARY "sn_tools.dll" +EXPORTS + CheckVersions
\ No newline at end of file |